@btst/stack 2.8.0 → 2.9.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 (214) hide show
  1. package/README.md +3 -2
  2. package/dist/components/markdown/index.d.cts +15 -2
  3. package/dist/components/markdown/index.d.mts +15 -2
  4. package/dist/components/markdown/index.d.ts +15 -2
  5. package/dist/packages/stack/src/plugins/blog/client/components/forms/image-field.cjs +30 -1
  6. package/dist/packages/stack/src/plugins/blog/client/components/forms/image-field.mjs +30 -1
  7. package/dist/packages/stack/src/plugins/blog/client/components/forms/markdown-editor-with-overrides.cjs +49 -9
  8. package/dist/packages/stack/src/plugins/blog/client/components/forms/markdown-editor-with-overrides.mjs +50 -10
  9. package/dist/packages/stack/src/plugins/blog/client/components/forms/markdown-editor.cjs +77 -9
  10. package/dist/packages/stack/src/plugins/blog/client/components/forms/markdown-editor.mjs +77 -9
  11. package/dist/packages/stack/src/plugins/cms/client/components/forms/content-form.cjs +24 -5
  12. package/dist/packages/stack/src/plugins/cms/client/components/forms/content-form.mjs +24 -5
  13. package/dist/packages/stack/src/plugins/cms/client/components/forms/file-upload.cjs +47 -13
  14. package/dist/packages/stack/src/plugins/cms/client/components/forms/file-upload.mjs +47 -13
  15. package/dist/packages/stack/src/plugins/kanban/client/components/forms/board-form.cjs +1 -1
  16. package/dist/packages/stack/src/plugins/kanban/client/components/forms/board-form.mjs +1 -1
  17. package/dist/packages/stack/src/plugins/kanban/client/components/forms/task-form.cjs +6 -2
  18. package/dist/packages/stack/src/plugins/kanban/client/components/forms/task-form.mjs +6 -2
  19. package/dist/packages/stack/src/plugins/media/api/adapters/local.cjs +55 -0
  20. package/dist/packages/stack/src/plugins/media/api/adapters/local.mjs +37 -0
  21. package/dist/packages/stack/src/plugins/media/api/getters.cjs +83 -0
  22. package/dist/packages/stack/src/plugins/media/api/getters.mjs +78 -0
  23. package/dist/packages/stack/src/plugins/media/api/mutations.cjs +88 -0
  24. package/dist/packages/stack/src/plugins/media/api/mutations.mjs +82 -0
  25. package/dist/packages/stack/src/plugins/media/api/plugin.cjs +525 -0
  26. package/dist/packages/stack/src/plugins/media/api/plugin.mjs +523 -0
  27. package/dist/packages/stack/src/plugins/media/api/query-key-defs.cjs +19 -0
  28. package/dist/packages/stack/src/plugins/media/api/query-key-defs.mjs +16 -0
  29. package/dist/packages/stack/src/plugins/media/api/serializers.cjs +17 -0
  30. package/dist/packages/stack/src/plugins/media/api/serializers.mjs +14 -0
  31. package/dist/packages/stack/src/plugins/media/api/storage-adapter.cjs +15 -0
  32. package/dist/packages/stack/src/plugins/media/api/storage-adapter.mjs +11 -0
  33. package/dist/packages/stack/src/plugins/media/client/components/media-picker/asset-card.cjs +129 -0
  34. package/dist/packages/stack/src/plugins/media/client/components/media-picker/asset-card.mjs +127 -0
  35. package/dist/packages/stack/src/plugins/media/client/components/media-picker/asset-preview-button.cjs +58 -0
  36. package/dist/packages/stack/src/plugins/media/client/components/media-picker/asset-preview-button.mjs +56 -0
  37. package/dist/packages/stack/src/plugins/media/client/components/media-picker/browse-tab.cjs +94 -0
  38. package/dist/packages/stack/src/plugins/media/client/components/media-picker/browse-tab.mjs +92 -0
  39. package/dist/packages/stack/src/plugins/media/client/components/media-picker/folder-tree.cjs +171 -0
  40. package/dist/packages/stack/src/plugins/media/client/components/media-picker/folder-tree.mjs +168 -0
  41. package/dist/packages/stack/src/plugins/media/client/components/media-picker/index.cjs +308 -0
  42. package/dist/packages/stack/src/plugins/media/client/components/media-picker/index.mjs +305 -0
  43. package/dist/packages/stack/src/plugins/media/client/components/media-picker/upload-tab.cjs +104 -0
  44. package/dist/packages/stack/src/plugins/media/client/components/media-picker/upload-tab.mjs +102 -0
  45. package/dist/packages/stack/src/plugins/media/client/components/media-picker/url-tab.cjs +70 -0
  46. package/dist/packages/stack/src/plugins/media/client/components/media-picker/url-tab.mjs +68 -0
  47. package/dist/packages/stack/src/plugins/media/client/components/media-picker/utils.cjs +21 -0
  48. package/dist/packages/stack/src/plugins/media/client/components/media-picker/utils.mjs +17 -0
  49. package/dist/packages/stack/src/plugins/media/client/components/pages/library-page.cjs +35 -0
  50. package/dist/packages/stack/src/plugins/media/client/components/pages/library-page.internal.cjs +125 -0
  51. package/dist/packages/stack/src/plugins/media/client/components/pages/library-page.internal.mjs +123 -0
  52. package/dist/packages/stack/src/plugins/media/client/components/pages/library-page.mjs +33 -0
  53. package/dist/packages/stack/src/plugins/media/client/hooks/use-media.cjs +222 -0
  54. package/dist/packages/stack/src/plugins/media/client/hooks/use-media.mjs +214 -0
  55. package/dist/packages/stack/src/plugins/media/client/plugin.cjs +94 -0
  56. package/dist/packages/stack/src/plugins/media/client/plugin.mjs +92 -0
  57. package/dist/packages/stack/src/plugins/media/client/upload.cjs +121 -0
  58. package/dist/packages/stack/src/plugins/media/client/upload.mjs +119 -0
  59. package/dist/packages/stack/src/plugins/media/client/utils/image-compression.cjs +67 -0
  60. package/dist/packages/stack/src/plugins/media/client/utils/image-compression.mjs +65 -0
  61. package/dist/packages/stack/src/plugins/media/db.cjs +62 -0
  62. package/dist/packages/stack/src/plugins/media/db.mjs +60 -0
  63. package/dist/packages/stack/src/plugins/media/schemas.cjs +41 -0
  64. package/dist/packages/stack/src/plugins/media/schemas.mjs +35 -0
  65. package/dist/packages/ui/src/components/minimal-tiptap/components/image/image-edit-block.cjs +18 -1
  66. package/dist/packages/ui/src/components/minimal-tiptap/components/image/image-edit-block.mjs +19 -2
  67. package/dist/packages/ui/src/components/minimal-tiptap/components/image/image-edit-dialog.cjs +2 -2
  68. package/dist/packages/ui/src/components/minimal-tiptap/components/image/image-edit-dialog.mjs +2 -2
  69. package/dist/packages/ui/src/components/minimal-tiptap/components/section/five.cjs +3 -2
  70. package/dist/packages/ui/src/components/minimal-tiptap/components/section/five.mjs +3 -2
  71. package/dist/packages/ui/src/components/minimal-tiptap/minimal-tiptap.cjs +12 -5
  72. package/dist/packages/ui/src/components/minimal-tiptap/minimal-tiptap.mjs +12 -5
  73. package/dist/plugins/blog/api/index.d.cts +2 -2
  74. package/dist/plugins/blog/api/index.d.mts +2 -2
  75. package/dist/plugins/blog/api/index.d.ts +2 -2
  76. package/dist/plugins/blog/client/hooks/index.d.cts +2 -2
  77. package/dist/plugins/blog/client/hooks/index.d.mts +2 -2
  78. package/dist/plugins/blog/client/hooks/index.d.ts +2 -2
  79. package/dist/plugins/blog/client/index.d.cts +60 -3
  80. package/dist/plugins/blog/client/index.d.mts +60 -3
  81. package/dist/plugins/blog/client/index.d.ts +60 -3
  82. package/dist/plugins/blog/query-keys.d.cts +2 -2
  83. package/dist/plugins/blog/query-keys.d.mts +2 -2
  84. package/dist/plugins/blog/query-keys.d.ts +2 -2
  85. package/dist/plugins/cms/client/index.d.cts +73 -3
  86. package/dist/plugins/cms/client/index.d.mts +73 -3
  87. package/dist/plugins/cms/client/index.d.ts +73 -3
  88. package/dist/plugins/kanban/api/index.d.cts +1 -1
  89. package/dist/plugins/kanban/api/index.d.mts +1 -1
  90. package/dist/plugins/kanban/api/index.d.ts +1 -1
  91. package/dist/plugins/kanban/client/hooks/index.d.cts +1 -1
  92. package/dist/plugins/kanban/client/hooks/index.d.mts +1 -1
  93. package/dist/plugins/kanban/client/hooks/index.d.ts +1 -1
  94. package/dist/plugins/kanban/client/index.d.cts +1 -1
  95. package/dist/plugins/kanban/client/index.d.mts +1 -1
  96. package/dist/plugins/kanban/client/index.d.ts +1 -1
  97. package/dist/plugins/kanban/query-keys.d.cts +1 -1
  98. package/dist/plugins/kanban/query-keys.d.mts +1 -1
  99. package/dist/plugins/kanban/query-keys.d.ts +1 -1
  100. package/dist/plugins/media/api/adapters/s3.cjs +106 -0
  101. package/dist/plugins/media/api/adapters/s3.d.cts +60 -0
  102. package/dist/plugins/media/api/adapters/s3.d.mts +60 -0
  103. package/dist/plugins/media/api/adapters/s3.d.ts +60 -0
  104. package/dist/plugins/media/api/adapters/s3.mjs +104 -0
  105. package/dist/plugins/media/api/adapters/vercel-blob.cjs +54 -0
  106. package/dist/plugins/media/api/adapters/vercel-blob.d.cts +41 -0
  107. package/dist/plugins/media/api/adapters/vercel-blob.d.mts +41 -0
  108. package/dist/plugins/media/api/adapters/vercel-blob.d.ts +41 -0
  109. package/dist/plugins/media/api/adapters/vercel-blob.mjs +52 -0
  110. package/dist/plugins/media/api/index.cjs +26 -0
  111. package/dist/plugins/media/api/index.d.cts +116 -0
  112. package/dist/plugins/media/api/index.d.mts +116 -0
  113. package/dist/plugins/media/api/index.d.ts +116 -0
  114. package/dist/plugins/media/api/index.mjs +6 -0
  115. package/dist/plugins/media/client/components/index.cjs +10 -0
  116. package/dist/plugins/media/client/components/index.d.cts +55 -0
  117. package/dist/plugins/media/client/components/index.d.mts +55 -0
  118. package/dist/plugins/media/client/components/index.d.ts +55 -0
  119. package/dist/plugins/media/client/components/index.mjs +2 -0
  120. package/dist/plugins/media/client/hooks/index.cjs +13 -0
  121. package/dist/plugins/media/client/hooks/index.d.cts +53 -0
  122. package/dist/plugins/media/client/hooks/index.d.mts +53 -0
  123. package/dist/plugins/media/client/hooks/index.d.ts +53 -0
  124. package/dist/plugins/media/client/hooks/index.mjs +1 -0
  125. package/dist/plugins/media/client/index.cjs +9 -0
  126. package/dist/plugins/media/client/index.d.cts +242 -0
  127. package/dist/plugins/media/client/index.d.mts +242 -0
  128. package/dist/plugins/media/client/index.d.ts +242 -0
  129. package/dist/plugins/media/client/index.mjs +2 -0
  130. package/dist/plugins/media/client.css +1 -0
  131. package/dist/plugins/media/query-keys.cjs +72 -0
  132. package/dist/plugins/media/query-keys.d.cts +49 -0
  133. package/dist/plugins/media/query-keys.d.mts +49 -0
  134. package/dist/plugins/media/query-keys.d.ts +49 -0
  135. package/dist/plugins/media/query-keys.mjs +70 -0
  136. package/dist/plugins/media/style.css +1 -0
  137. package/dist/shared/{stack.DOZ1EXjM.d.mts → stack.6mEHS2WH.d.mts} +3 -3
  138. package/dist/shared/{stack.DX-tQ93o.d.cts → stack.AJTXI7kw.d.cts} +3 -3
  139. package/dist/shared/{stack.DRpeDS6X.d.ts → stack.BMx2QYOK.d.ts} +25 -0
  140. package/dist/shared/stack.BUTXWiG-.d.ts +286 -0
  141. package/dist/shared/stack.C7Y9sBDg.d.mts +286 -0
  142. package/dist/shared/stack.C7vfOBmO.d.mts +63 -0
  143. package/dist/shared/stack.CAni8dnD.d.cts +63 -0
  144. package/dist/shared/stack.CLcnSF_b.d.cts +25 -0
  145. package/dist/shared/stack.CLcnSF_b.d.mts +25 -0
  146. package/dist/shared/stack.CLcnSF_b.d.ts +25 -0
  147. package/dist/shared/stack.CYSwntXC.d.ts +63 -0
  148. package/dist/shared/{stack.Jb0kQDJC.d.mts → stack.Cd6McBu1.d.mts} +25 -0
  149. package/dist/shared/stack.CoBj86jf.d.cts +109 -0
  150. package/dist/shared/stack.CoBj86jf.d.mts +109 -0
  151. package/dist/shared/stack.CoBj86jf.d.ts +109 -0
  152. package/dist/shared/{stack.BXxrFL9R.d.ts → stack.D7HSzZdG.d.ts} +5 -5
  153. package/dist/shared/{stack.DzOhpIYM.d.mts → stack.DjgpFWq3.d.cts} +5 -5
  154. package/dist/shared/{stack.BxFl46lB.d.cts → stack.DxQl8Wa1.d.cts} +25 -0
  155. package/dist/shared/{stack.BSqJrCTM.d.cts → stack.IUeyQKrm.d.mts} +5 -5
  156. package/dist/shared/{stack.VF6FhyZw.d.ts → stack.QYn-Px94.d.ts} +3 -3
  157. package/dist/shared/stack.vxskCkim.d.cts +286 -0
  158. package/package.json +115 -6
  159. package/src/plugins/blog/client/components/forms/image-field.tsx +35 -4
  160. package/src/plugins/blog/client/components/forms/markdown-editor-with-overrides.tsx +67 -12
  161. package/src/plugins/blog/client/components/forms/markdown-editor.tsx +106 -10
  162. package/src/plugins/blog/client/overrides.ts +58 -1
  163. package/src/plugins/cms/client/components/forms/content-form.tsx +26 -7
  164. package/src/plugins/cms/client/components/forms/file-upload.tsx +73 -15
  165. package/src/plugins/cms/client/overrides.ts +57 -2
  166. package/src/plugins/kanban/client/components/forms/board-form.tsx +1 -1
  167. package/src/plugins/kanban/client/components/forms/task-form.tsx +7 -1
  168. package/src/plugins/kanban/client/overrides.ts +25 -0
  169. package/src/plugins/media/__tests__/__stubs__/vercel-blob-server.ts +9 -0
  170. package/src/plugins/media/__tests__/getters.test.ts +274 -0
  171. package/src/plugins/media/__tests__/mutations.test.ts +299 -0
  172. package/src/plugins/media/__tests__/plugin.test.ts +752 -0
  173. package/src/plugins/media/__tests__/query-key-defs.test.ts +54 -0
  174. package/src/plugins/media/__tests__/storage-adapters.test.ts +351 -0
  175. package/src/plugins/media/api/adapters/local.ts +79 -0
  176. package/src/plugins/media/api/adapters/s3.ts +198 -0
  177. package/src/plugins/media/api/adapters/vercel-blob.ts +132 -0
  178. package/src/plugins/media/api/getters.ts +174 -0
  179. package/src/plugins/media/api/index.ts +41 -0
  180. package/src/plugins/media/api/mutations.ts +179 -0
  181. package/src/plugins/media/api/plugin.ts +855 -0
  182. package/src/plugins/media/api/query-key-defs.ts +41 -0
  183. package/src/plugins/media/api/serializers.ts +28 -0
  184. package/src/plugins/media/api/storage-adapter.ts +139 -0
  185. package/src/plugins/media/client/components/index.tsx +6 -0
  186. package/src/plugins/media/client/components/media-picker/asset-card.tsx +150 -0
  187. package/src/plugins/media/client/components/media-picker/asset-preview-button.tsx +67 -0
  188. package/src/plugins/media/client/components/media-picker/browse-tab.tsx +116 -0
  189. package/src/plugins/media/client/components/media-picker/folder-tree.tsx +188 -0
  190. package/src/plugins/media/client/components/media-picker/index.tsx +347 -0
  191. package/src/plugins/media/client/components/media-picker/upload-tab.tsx +108 -0
  192. package/src/plugins/media/client/components/media-picker/url-tab.tsx +72 -0
  193. package/src/plugins/media/client/components/media-picker/utils.ts +17 -0
  194. package/src/plugins/media/client/components/pages/library-page.internal.tsx +134 -0
  195. package/src/plugins/media/client/components/pages/library-page.tsx +42 -0
  196. package/src/plugins/media/client/hooks/index.tsx +9 -0
  197. package/src/plugins/media/client/hooks/use-media.tsx +289 -0
  198. package/src/plugins/media/client/index.ts +4 -0
  199. package/src/plugins/media/client/overrides.ts +127 -0
  200. package/src/plugins/media/client/plugin.tsx +184 -0
  201. package/src/plugins/media/client/upload.ts +171 -0
  202. package/src/plugins/media/client/utils/image-compression.ts +131 -0
  203. package/src/plugins/media/client.css +1 -0
  204. package/src/plugins/media/db.ts +62 -0
  205. package/src/plugins/media/query-keys.ts +96 -0
  206. package/src/plugins/media/schemas.ts +37 -0
  207. package/src/plugins/media/style.css +1 -0
  208. package/src/plugins/media/types.ts +26 -0
  209. package/dist/shared/{stack.BWp0hcm9.d.ts → stack.BQmuNl5p.d.cts} +3 -3
  210. package/dist/shared/{stack.BWp0hcm9.d.cts → stack.BQmuNl5p.d.mts} +3 -3
  211. package/dist/shared/{stack.BWp0hcm9.d.mts → stack.BQmuNl5p.d.ts} +3 -3
  212. package/dist/shared/{stack.BvCR4-9H.d.ts → stack.D4Cea8II.d.ts} +3 -3
  213. package/dist/shared/{stack.CWxAl9K3.d.mts → stack.HE_IvqV5.d.mts} +3 -3
  214. package/dist/shared/{stack.BOokfhZD.d.cts → stack.Rtcvl8sS.d.cts} +3 -3
@@ -3,7 +3,7 @@ import * as _btst_yar from '@btst/yar';
3
3
  import { ComponentType } from 'react';
4
4
  import { QueryClient } from '@tanstack/react-query';
5
5
  import { S as SerializedBoardWithColumns } from '../../../shared/stack.DJaKVY7v.mjs';
6
- export { K as KanbanPluginOverrides, a as KanbanUser } from '../../../shared/stack.Jb0kQDJC.mjs';
6
+ export { K as KanbanPluginOverrides, a as KanbanUser } from '../../../shared/stack.Cd6McBu1.mjs';
7
7
 
8
8
  /**
9
9
  * Context passed to route hooks
@@ -3,7 +3,7 @@ import * as _btst_yar from '@btst/yar';
3
3
  import { ComponentType } from 'react';
4
4
  import { QueryClient } from '@tanstack/react-query';
5
5
  import { S as SerializedBoardWithColumns } from '../../../shared/stack.DJaKVY7v.js';
6
- export { K as KanbanPluginOverrides, a as KanbanUser } from '../../../shared/stack.DRpeDS6X.js';
6
+ export { K as KanbanPluginOverrides, a as KanbanUser } from '../../../shared/stack.BMx2QYOK.js';
7
7
 
8
8
  /**
9
9
  * Context passed to route hooks
@@ -1,5 +1,5 @@
1
1
  import * as _tanstack_react_query from '@tanstack/react-query';
2
- import { K as KanbanApiRouter, j as BoardsListDiscriminator } from '../../shared/stack.BOokfhZD.cjs';
2
+ import { K as KanbanApiRouter, j as BoardsListDiscriminator } from '../../shared/stack.Rtcvl8sS.cjs';
3
3
  import { createApiClient } from '@btst/stack/plugins/client';
4
4
  import { S as SerializedBoardWithColumns } from '../../shared/stack.DJaKVY7v.cjs';
5
5
  import '@btst/stack/plugins/api';
@@ -1,5 +1,5 @@
1
1
  import * as _tanstack_react_query from '@tanstack/react-query';
2
- import { K as KanbanApiRouter, j as BoardsListDiscriminator } from '../../shared/stack.CWxAl9K3.mjs';
2
+ import { K as KanbanApiRouter, j as BoardsListDiscriminator } from '../../shared/stack.HE_IvqV5.mjs';
3
3
  import { createApiClient } from '@btst/stack/plugins/client';
4
4
  import { S as SerializedBoardWithColumns } from '../../shared/stack.DJaKVY7v.mjs';
5
5
  import '@btst/stack/plugins/api';
@@ -1,5 +1,5 @@
1
1
  import * as _tanstack_react_query from '@tanstack/react-query';
2
- import { K as KanbanApiRouter, j as BoardsListDiscriminator } from '../../shared/stack.BvCR4-9H.js';
2
+ import { K as KanbanApiRouter, j as BoardsListDiscriminator } from '../../shared/stack.D4Cea8II.js';
3
3
  import { createApiClient } from '@btst/stack/plugins/client';
4
4
  import { S as SerializedBoardWithColumns } from '../../shared/stack.DJaKVY7v.js';
5
5
  import '@btst/stack/plugins/api';
@@ -0,0 +1,106 @@
1
+ 'use strict';
2
+
3
+ function s3Adapter(options) {
4
+ const {
5
+ bucket,
6
+ region,
7
+ accessKeyId,
8
+ secretAccessKey,
9
+ endpoint,
10
+ publicBaseUrl,
11
+ expiresIn = 300
12
+ } = options;
13
+ let s3ModulePromise = null;
14
+ let clientPromise = null;
15
+ function getS3Module() {
16
+ if (!s3ModulePromise) {
17
+ s3ModulePromise = import('@aws-sdk/client-s3').catch(() => {
18
+ s3ModulePromise = null;
19
+ throw new Error(
20
+ "[@btst/stack] S3 adapter requires '@aws-sdk/client-s3'. Run: npm install @aws-sdk/client-s3 @aws-sdk/s3-request-presigner"
21
+ );
22
+ });
23
+ }
24
+ return s3ModulePromise;
25
+ }
26
+ function getClient() {
27
+ if (!clientPromise) {
28
+ clientPromise = getS3Module().then(({ S3Client }) => {
29
+ return new S3Client({
30
+ region,
31
+ endpoint,
32
+ credentials: { accessKeyId, secretAccessKey },
33
+ forcePathStyle: !!endpoint
34
+ });
35
+ });
36
+ }
37
+ return clientPromise.catch((error) => {
38
+ if (clientPromise) {
39
+ clientPromise = null;
40
+ }
41
+ if (s3ModulePromise && error instanceof Error) {
42
+ throw error;
43
+ }
44
+ throw new Error(
45
+ "[@btst/stack] S3 adapter requires '@aws-sdk/client-s3'. Run: npm install @aws-sdk/client-s3 @aws-sdk/s3-request-presigner"
46
+ );
47
+ });
48
+ }
49
+ async function buildSignedUrl(client, command, opts) {
50
+ let getSignedUrl;
51
+ try {
52
+ ({ getSignedUrl } = await import('@aws-sdk/s3-request-presigner'));
53
+ } catch {
54
+ throw new Error(
55
+ "[@btst/stack] S3 adapter requires '@aws-sdk/s3-request-presigner'. Run: npm install @aws-sdk/client-s3 @aws-sdk/s3-request-presigner"
56
+ );
57
+ }
58
+ return getSignedUrl(
59
+ client,
60
+ command,
61
+ opts
62
+ );
63
+ }
64
+ return {
65
+ type: "s3",
66
+ urlPrefix: publicBaseUrl.replace(/\/$/, ""),
67
+ async generateUploadToken(uploadOptions) {
68
+ const [client, { PutObjectCommand }] = await Promise.all([
69
+ getClient(),
70
+ getS3Module()
71
+ ]);
72
+ const key = uploadOptions.folderId ? `${uploadOptions.folderId}/${uploadOptions.filename}` : uploadOptions.filename;
73
+ const command = new PutObjectCommand({
74
+ Bucket: bucket,
75
+ Key: key,
76
+ ContentType: uploadOptions.mimeType,
77
+ ContentLength: uploadOptions.size
78
+ });
79
+ const uploadUrl = await buildSignedUrl(client, command, { expiresIn });
80
+ const encodedKey = key.split("/").map(encodeURIComponent).join("/");
81
+ const publicUrl = `${publicBaseUrl.replace(/\/$/, "")}/${encodedKey}`;
82
+ return {
83
+ type: "presigned-url",
84
+ payload: {
85
+ uploadUrl,
86
+ publicUrl,
87
+ key,
88
+ method: "PUT",
89
+ headers: { "Content-Type": uploadOptions.mimeType }
90
+ }
91
+ };
92
+ },
93
+ async delete(url) {
94
+ const [client, { DeleteObjectCommand }] = await Promise.all([
95
+ getClient(),
96
+ getS3Module()
97
+ ]);
98
+ const base = publicBaseUrl.replace(/\/$/, "");
99
+ const encodedKey = url.startsWith(base) ? url.slice(base.length + 1) : url.split("/").pop() ?? url;
100
+ const key = decodeURIComponent(encodedKey);
101
+ await client.send(new DeleteObjectCommand({ Bucket: bucket, Key: key }));
102
+ }
103
+ };
104
+ }
105
+
106
+ exports.s3Adapter = s3Adapter;
@@ -0,0 +1,60 @@
1
+ import { S as S3StorageAdapter } from '../../../../shared/stack.CoBj86jf.cjs';
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.CoBj86jf.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.CoBj86jf.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,54 @@
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 vercelBlobServer = (
11
+ /* @vite-ignore */
12
+ // @ts-expect-error — @vercel/blob/server may not be exported in all installed versions
13
+ await import('@vercel/blob/server')
14
+ );
15
+ ({ handleUpload } = vercelBlobServer);
16
+ } catch {
17
+ throw new Error(
18
+ "[@btst/stack] Vercel Blob adapter requires '@vercel/blob' with 'handleUpload' exported from '@vercel/blob/server'. Run: npm install @vercel/blob"
19
+ );
20
+ }
21
+ const body = await request.json();
22
+ return handleUpload({
23
+ body,
24
+ request,
25
+ token: options.token,
26
+ onBeforeGenerateToken: async (pathname, clientPayload) => {
27
+ const tokenOptions = await callbacks.onBeforeGenerateToken?.(
28
+ pathname,
29
+ clientPayload ?? null
30
+ ) ?? {};
31
+ return {
32
+ addRandomSuffix: true,
33
+ ...tokenOptions
34
+ };
35
+ },
36
+ onUploadCompleted: async () => {
37
+ }
38
+ });
39
+ },
40
+ async delete(url) {
41
+ let del;
42
+ try {
43
+ ({ del } = await import('@vercel/blob'));
44
+ } catch {
45
+ throw new Error(
46
+ "[@btst/stack] Vercel Blob adapter requires '@vercel/blob'. Run: npm install @vercel/blob"
47
+ );
48
+ }
49
+ await del(url, options.token ? { token: options.token } : void 0);
50
+ }
51
+ };
52
+ }
53
+
54
+ exports.vercelBlobAdapter = vercelBlobAdapter;
@@ -0,0 +1,41 @@
1
+ import { V as VercelBlobStorageAdapter } from '../../../../shared/stack.CoBj86jf.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/server`
15
+ * in compatible versions).
16
+ *
17
+ * @remarks Requires `@vercel/blob` as an optional peer dependency (version
18
+ * with `handleUpload` exported from `@vercel/blob/server`).
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.CoBj86jf.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/server`
15
+ * in compatible versions).
16
+ *
17
+ * @remarks Requires `@vercel/blob` as an optional peer dependency (version
18
+ * with `handleUpload` exported from `@vercel/blob/server`).
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.CoBj86jf.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/server`
15
+ * in compatible versions).
16
+ *
17
+ * @remarks Requires `@vercel/blob` as an optional peer dependency (version
18
+ * with `handleUpload` exported from `@vercel/blob/server`).
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,52 @@
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 vercelBlobServer = (
9
+ /* @vite-ignore */
10
+ // @ts-expect-error — @vercel/blob/server may not be exported in all installed versions
11
+ await import('@vercel/blob/server')
12
+ );
13
+ ({ handleUpload } = vercelBlobServer);
14
+ } catch {
15
+ throw new Error(
16
+ "[@btst/stack] Vercel Blob adapter requires '@vercel/blob' with 'handleUpload' exported from '@vercel/blob/server'. Run: npm install @vercel/blob"
17
+ );
18
+ }
19
+ const body = await request.json();
20
+ return handleUpload({
21
+ body,
22
+ request,
23
+ token: options.token,
24
+ onBeforeGenerateToken: async (pathname, clientPayload) => {
25
+ const tokenOptions = await callbacks.onBeforeGenerateToken?.(
26
+ pathname,
27
+ clientPayload ?? null
28
+ ) ?? {};
29
+ return {
30
+ addRandomSuffix: true,
31
+ ...tokenOptions
32
+ };
33
+ },
34
+ onUploadCompleted: async () => {
35
+ }
36
+ });
37
+ },
38
+ async delete(url) {
39
+ let del;
40
+ try {
41
+ ({ del } = await import('@vercel/blob'));
42
+ } catch {
43
+ throw new Error(
44
+ "[@btst/stack] Vercel Blob adapter requires '@vercel/blob'. Run: npm install @vercel/blob"
45
+ );
46
+ }
47
+ await del(url, options.token ? { token: options.token } : void 0);
48
+ }
49
+ };
50
+ }
51
+
52
+ export { vercelBlobAdapter };