@btst/stack 2.11.0 → 2.11.2

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 (71) hide show
  1. package/dist/packages/stack/src/plugins/blog/api/mutations.cjs +170 -0
  2. package/dist/packages/stack/src/plugins/blog/api/mutations.mjs +166 -0
  3. package/dist/packages/stack/src/plugins/blog/api/plugin.cjs +34 -157
  4. package/dist/packages/stack/src/plugins/blog/api/plugin.mjs +40 -163
  5. package/dist/packages/stack/src/plugins/media/api/plugin.cjs +4 -1
  6. package/dist/packages/stack/src/plugins/media/api/plugin.mjs +5 -2
  7. package/dist/plugins/blog/api/index.cjs +4 -0
  8. package/dist/plugins/blog/api/index.d.cts +2 -2
  9. package/dist/plugins/blog/api/index.d.mts +2 -2
  10. package/dist/plugins/blog/api/index.d.ts +2 -2
  11. package/dist/plugins/blog/api/index.mjs +1 -0
  12. package/dist/plugins/blog/client/hooks/index.d.cts +2 -2
  13. package/dist/plugins/blog/client/hooks/index.d.mts +2 -2
  14. package/dist/plugins/blog/client/hooks/index.d.ts +2 -2
  15. package/dist/plugins/blog/client/index.d.cts +2 -2
  16. package/dist/plugins/blog/client/index.d.mts +2 -2
  17. package/dist/plugins/blog/client/index.d.ts +2 -2
  18. package/dist/plugins/blog/query-keys.d.cts +2 -2
  19. package/dist/plugins/blog/query-keys.d.mts +2 -2
  20. package/dist/plugins/blog/query-keys.d.ts +2 -2
  21. package/dist/plugins/kanban/api/index.d.cts +1 -1
  22. package/dist/plugins/kanban/api/index.d.mts +1 -1
  23. package/dist/plugins/kanban/api/index.d.ts +1 -1
  24. package/dist/plugins/kanban/query-keys.d.cts +1 -1
  25. package/dist/plugins/kanban/query-keys.d.mts +1 -1
  26. package/dist/plugins/kanban/query-keys.d.ts +1 -1
  27. package/dist/{packages/stack/src/plugins → plugins}/media/api/adapters/local.cjs +7 -1
  28. package/dist/plugins/media/api/adapters/local.d.cts +30 -0
  29. package/dist/plugins/media/api/adapters/local.d.mts +30 -0
  30. package/dist/plugins/media/api/adapters/local.d.ts +30 -0
  31. package/dist/{packages/stack/src/plugins → plugins}/media/api/adapters/local.mjs +7 -1
  32. package/dist/plugins/media/api/adapters/s3.d.cts +1 -1
  33. package/dist/plugins/media/api/adapters/s3.d.mts +1 -1
  34. package/dist/plugins/media/api/adapters/s3.d.ts +1 -1
  35. package/dist/plugins/media/api/adapters/vercel-blob.d.cts +1 -1
  36. package/dist/plugins/media/api/adapters/vercel-blob.d.mts +1 -1
  37. package/dist/plugins/media/api/adapters/vercel-blob.d.ts +1 -1
  38. package/dist/plugins/media/api/index.cjs +0 -2
  39. package/dist/plugins/media/api/index.d.cts +5 -102
  40. package/dist/plugins/media/api/index.d.mts +5 -102
  41. package/dist/plugins/media/api/index.d.ts +5 -102
  42. package/dist/plugins/media/api/index.mjs +0 -1
  43. package/dist/plugins/media/query-keys.d.cts +2 -2
  44. package/dist/plugins/media/query-keys.d.mts +2 -2
  45. package/dist/plugins/media/query-keys.d.ts +2 -2
  46. package/dist/shared/{stack.Bci0-plK.d.ts → stack.C5ucdatf.d.ts} +76 -3
  47. package/dist/shared/{stack.D7HSzZdG.d.ts → stack.D0p6oNme.d.ts} +90 -7
  48. package/dist/shared/{stack.6mEHS2WH.d.mts → stack.DOZ1EXjM.d.mts} +3 -3
  49. package/dist/shared/{stack.IUeyQKrm.d.mts → stack.DWipT53I.d.cts} +90 -7
  50. package/dist/shared/{stack.AJTXI7kw.d.cts → stack.DX-tQ93o.d.cts} +3 -3
  51. package/dist/shared/{stack.C_MUwwgR.d.mts → stack.DpZoZd98.d.mts} +76 -3
  52. package/dist/shared/{stack.DjgpFWq3.d.cts → stack.E17kSK1W.d.mts} +90 -7
  53. package/dist/shared/{stack.QYn-Px94.d.ts → stack.VF6FhyZw.d.ts} +3 -3
  54. package/dist/shared/{stack.DO6vOGQG.d.cts → stack.lkebw2nj.d.cts} +1 -1
  55. package/dist/shared/{stack.DO6vOGQG.d.mts → stack.lkebw2nj.d.mts} +1 -1
  56. package/dist/shared/{stack.DO6vOGQG.d.ts → stack.lkebw2nj.d.ts} +1 -1
  57. package/dist/shared/{stack.D6zyQnMo.d.cts → stack.vVFh38aS.d.cts} +76 -3
  58. package/package.json +14 -1
  59. package/src/plugins/blog/api/index.ts +7 -0
  60. package/src/plugins/blog/api/mutations.ts +287 -0
  61. package/src/plugins/blog/api/plugin.ts +43 -184
  62. package/src/plugins/media/__tests__/storage-adapters.test.ts +11 -0
  63. package/src/plugins/media/api/adapters/local.ts +10 -1
  64. package/src/plugins/media/api/index.ts +0 -5
  65. package/src/plugins/media/api/plugin.ts +6 -0
  66. package/dist/shared/{stack.eq5eg1yt.d.cts → stack.B6S3cgwN.d.cts} +6 -6
  67. package/dist/shared/{stack.BQmuNl5p.d.ts → stack.BWp0hcm9.d.cts} +3 -3
  68. package/dist/shared/{stack.BQmuNl5p.d.cts → stack.BWp0hcm9.d.mts} +3 -3
  69. package/dist/shared/{stack.BQmuNl5p.d.mts → stack.BWp0hcm9.d.ts} +3 -3
  70. package/dist/shared/{stack.Dj04W2c3.d.mts → stack.Bzfx-_lq.d.mts} +6 -6
  71. package/dist/shared/{stack.CMbX8Q5C.d.ts → stack.j5SFLC1d.d.ts} +6 -6
@@ -4,6 +4,7 @@ import { blogSchema } from '../db.mjs';
4
4
  import { slugify } from '../utils.mjs';
5
5
  import { createPostSchema, updatePostSchema } from '../schemas.mjs';
6
6
  import { getAllTags, getPostBySlug, getAllPosts } from './getters.mjs';
7
+ import { deletePost, updatePost, createPost } from './mutations.mjs';
7
8
  import { BLOG_QUERY_KEYS } from './query-key-defs.mjs';
8
9
  import { serializePost, serializeTag } from './serializers.mjs';
9
10
  import { runHookWithShim } from '../../utils.mjs';
@@ -79,70 +80,13 @@ const blogBackendPlugin = (hooks) => defineBackendPlugin({
79
80
  getAllPosts: (params) => getAllPosts(adapter, params),
80
81
  getPostBySlug: (slug) => getPostBySlug(adapter, slug),
81
82
  getAllTags: () => getAllTags(adapter),
82
- prefetchForRoute: createBlogPrefetchForRoute(adapter)
83
+ prefetchForRoute: createBlogPrefetchForRoute(adapter),
84
+ // Mutations
85
+ createPost: (input) => createPost(adapter, input),
86
+ updatePost: (id, input) => updatePost(adapter, id, input),
87
+ deletePost: (id) => deletePost(adapter, id)
83
88
  }),
84
89
  routes: (adapter) => {
85
- const findOrCreateTags = async (tagInputs) => {
86
- if (tagInputs.length === 0) return [];
87
- const normalizeTagName = (name) => {
88
- return name.trim();
89
- };
90
- const tagsWithIds = [];
91
- const tagsToFindOrCreate = [];
92
- for (const tagInput of tagInputs) {
93
- if ("id" in tagInput && tagInput.id) {
94
- tagsWithIds.push({
95
- id: tagInput.id,
96
- name: normalizeTagName(tagInput.name),
97
- slug: tagInput.slug,
98
- createdAt: /* @__PURE__ */ new Date(),
99
- updatedAt: /* @__PURE__ */ new Date()
100
- });
101
- } else {
102
- tagsToFindOrCreate.push({ name: normalizeTagName(tagInput.name) });
103
- }
104
- }
105
- if (tagsToFindOrCreate.length === 0) {
106
- return tagsWithIds;
107
- }
108
- const allTags = await adapter.findMany({
109
- model: "tag"
110
- });
111
- const tagMapBySlug = /* @__PURE__ */ new Map();
112
- for (const tag of allTags) {
113
- tagMapBySlug.set(tag.slug, tag);
114
- }
115
- const tagSlugs = tagsToFindOrCreate.map((tag) => slugify(tag.name));
116
- const foundTags = [];
117
- for (const slug of tagSlugs) {
118
- const tag = tagMapBySlug.get(slug);
119
- if (tag) {
120
- foundTags.push(tag);
121
- }
122
- }
123
- const existingSlugs = /* @__PURE__ */ new Set([
124
- ...tagsWithIds.map((tag) => tag.slug),
125
- ...foundTags.map((tag) => tag.slug)
126
- ]);
127
- const tagsToCreate = tagsToFindOrCreate.filter(
128
- (tag) => !existingSlugs.has(slugify(tag.name))
129
- );
130
- const createdTags = [];
131
- for (const tag of tagsToCreate) {
132
- const normalizedName = normalizeTagName(tag.name);
133
- const newTag = await adapter.create({
134
- model: "tag",
135
- data: {
136
- name: normalizedName,
137
- slug: slugify(normalizedName),
138
- createdAt: /* @__PURE__ */ new Date(),
139
- updatedAt: /* @__PURE__ */ new Date()
140
- }
141
- });
142
- createdTags.push(newTag);
143
- }
144
- return [...tagsWithIds, ...foundTags, ...createdTags];
145
- };
146
90
  const listPosts = createEndpoint(
147
91
  "/posts",
148
92
  {
@@ -173,7 +117,7 @@ const blogBackendPlugin = (hooks) => defineBackendPlugin({
173
117
  }
174
118
  }
175
119
  );
176
- const createPost = createEndpoint(
120
+ const createPost$1 = createEndpoint(
177
121
  "/posts",
178
122
  {
179
123
  method: "POST",
@@ -192,41 +136,26 @@ const blogBackendPlugin = (hooks) => defineBackendPlugin({
192
136
  "Unauthorized: Cannot create post"
193
137
  );
194
138
  }
195
- const { tags, ...postData } = ctx.body;
196
- const tagNames = tags || [];
197
- const slug = slugify(postData.slug || postData.title);
139
+ const {
140
+ tags,
141
+ slug: rawSlug,
142
+ createdAt: _ca,
143
+ updatedAt: _ua,
144
+ ...postData
145
+ } = ctx.body;
146
+ const slug = slugify(rawSlug || postData.title);
198
147
  if (!slug) {
199
148
  throw ctx.error(400, {
200
149
  message: "Invalid slug: must contain at least one alphanumeric character"
201
150
  });
202
151
  }
203
- const newPost = await adapter.create({
204
- model: "post",
205
- data: {
206
- ...postData,
207
- slug,
208
- tags: [],
209
- createdAt: /* @__PURE__ */ new Date(),
210
- updatedAt: /* @__PURE__ */ new Date()
211
- }
152
+ const newPost = await createPost(adapter, {
153
+ ...postData,
154
+ slug,
155
+ tags: tags ?? [],
156
+ createdAt: /* @__PURE__ */ new Date(),
157
+ updatedAt: /* @__PURE__ */ new Date()
212
158
  });
213
- if (tagNames.length > 0) {
214
- const createdTags = await findOrCreateTags(tagNames);
215
- await adapter.transaction(async (tx) => {
216
- for (const tag of createdTags) {
217
- await tx.create({
218
- model: "postTag",
219
- data: {
220
- postId: newPost.id,
221
- tagId: tag.id
222
- }
223
- });
224
- }
225
- });
226
- newPost.tags = createdTags.map((tag) => ({ ...tag }));
227
- } else {
228
- newPost.tags = [];
229
- }
230
159
  if (hooks?.onPostCreated) {
231
160
  await hooks.onPostCreated(newPost, context);
232
161
  }
@@ -239,7 +168,7 @@ const blogBackendPlugin = (hooks) => defineBackendPlugin({
239
168
  }
240
169
  }
241
170
  );
242
- const updatePost = createEndpoint(
171
+ const updatePost$1 = createEndpoint(
243
172
  "/posts/:id",
244
173
  {
245
174
  method: "PUT",
@@ -259,76 +188,27 @@ const blogBackendPlugin = (hooks) => defineBackendPlugin({
259
188
  "Unauthorized: Cannot update post"
260
189
  );
261
190
  }
262
- const { tags, slug: rawSlug, ...restPostData } = ctx.body;
263
- const tagNames = tags || [];
191
+ const {
192
+ tags,
193
+ slug: rawSlug,
194
+ createdAt: _ca,
195
+ updatedAt: _ua,
196
+ ...restPostData
197
+ } = ctx.body;
264
198
  const slugified = rawSlug ? slugify(rawSlug) : void 0;
265
199
  if (rawSlug && !slugified) {
266
200
  throw ctx.error(400, {
267
201
  message: "Invalid slug: must contain at least one alphanumeric character"
268
202
  });
269
203
  }
270
- const postData = {
204
+ const updated = await updatePost(adapter, ctx.params.id, {
271
205
  ...restPostData,
272
- ...slugified ? { slug: slugified } : {}
273
- };
274
- const updated = await adapter.transaction(async (tx) => {
275
- const existingPostTags = await tx.findMany({
276
- model: "postTag",
277
- where: [
278
- {
279
- field: "postId",
280
- value: ctx.params.id,
281
- operator: "eq"
282
- }
283
- ]
284
- });
285
- const updatedPost = await tx.update({
286
- model: "post",
287
- where: [{ field: "id", value: ctx.params.id }],
288
- update: {
289
- ...postData,
290
- updatedAt: /* @__PURE__ */ new Date()
291
- }
292
- });
293
- if (!updatedPost) {
294
- throw ctx.error(404, {
295
- message: "Post not found"
296
- });
297
- }
298
- for (const postTag of existingPostTags) {
299
- await tx.delete({
300
- model: "postTag",
301
- where: [
302
- {
303
- field: "postId",
304
- value: postTag.postId,
305
- operator: "eq"
306
- },
307
- {
308
- field: "tagId",
309
- value: postTag.tagId,
310
- operator: "eq"
311
- }
312
- ]
313
- });
314
- }
315
- if (tagNames.length > 0) {
316
- const createdTags = await findOrCreateTags(tagNames);
317
- for (const tag of createdTags) {
318
- await tx.create({
319
- model: "postTag",
320
- data: {
321
- postId: ctx.params.id,
322
- tagId: tag.id
323
- }
324
- });
325
- }
326
- updatedPost.tags = createdTags.map((tag) => ({ ...tag }));
327
- } else {
328
- updatedPost.tags = [];
329
- }
330
- return updatedPost;
206
+ ...slugified ? { slug: slugified } : {},
207
+ tags: tags ?? []
331
208
  });
209
+ if (!updated) {
210
+ throw ctx.error(404, { message: "Post not found" });
211
+ }
332
212
  if (hooks?.onPostUpdated) {
333
213
  await hooks.onPostUpdated(updated, context);
334
214
  }
@@ -341,7 +221,7 @@ const blogBackendPlugin = (hooks) => defineBackendPlugin({
341
221
  }
342
222
  }
343
223
  );
344
- const deletePost = createEndpoint(
224
+ const deletePost$1 = createEndpoint(
345
225
  "/posts/:id",
346
226
  {
347
227
  method: "DELETE"
@@ -359,10 +239,7 @@ const blogBackendPlugin = (hooks) => defineBackendPlugin({
359
239
  "Unauthorized: Cannot delete post"
360
240
  );
361
241
  }
362
- await adapter.delete({
363
- model: "post",
364
- where: [{ field: "id", value: ctx.params.id }]
365
- });
242
+ await deletePost(adapter, ctx.params.id);
366
243
  if (hooks?.onPostDeleted) {
367
244
  await hooks.onPostDeleted(ctx.params.id, context);
368
245
  }
@@ -493,9 +370,9 @@ const blogBackendPlugin = (hooks) => defineBackendPlugin({
493
370
  );
494
371
  return {
495
372
  listPosts,
496
- createPost,
497
- updatePost,
498
- deletePost,
373
+ createPost: createPost$1,
374
+ updatePost: updatePost$1,
375
+ deletePost: deletePost$1,
499
376
  getNextPreviousPosts,
500
377
  listTags
501
378
  };
@@ -22,9 +22,12 @@ const mediaBackendPlugin = (config) => api.defineBackendPlugin({
22
22
  api: (adapter) => ({
23
23
  listAssets: (params) => getters.listAssets(adapter, params),
24
24
  getAssetById: (id) => getters.getAssetById(adapter, id),
25
+ createAsset: (input) => mutations.createAsset(adapter, input),
26
+ updateAsset: (id, input) => mutations.updateAsset(adapter, id, input),
25
27
  listFolders: (params) => getters.listFolders(adapter, params),
26
28
  getFolderById: (id) => getters.getFolderById(adapter, id),
27
- getFolderByName: (name, parentId, tenantId) => getters.getFolderByName(adapter, name, parentId, tenantId)
29
+ getFolderByName: (name, parentId, tenantId) => getters.getFolderByName(adapter, name, parentId, tenantId),
30
+ createFolder: (input) => mutations.createFolder(adapter, input)
28
31
  }),
29
32
  routes: (adapter) => {
30
33
  const {
@@ -3,7 +3,7 @@ import { z } from 'zod';
3
3
  import { mediaSchema } from '../db.mjs';
4
4
  import { AssetListQuerySchema, createAssetSchema, updateAssetSchema, createFolderSchema, uploadTokenRequestSchema } from '../schemas.mjs';
5
5
  import { getFolderByName, getFolderById, listFolders, getAssetById, listAssets } from './getters.mjs';
6
- import { createAsset, updateAsset, deleteAsset, createFolder, deleteFolder } from './mutations.mjs';
6
+ import { createFolder, updateAsset, createAsset, deleteAsset, deleteFolder } from './mutations.mjs';
7
7
  import { isDirectAdapter, isS3Adapter, isVercelBlobAdapter } from './storage-adapter.mjs';
8
8
  import { runHookWithShim } from '../../utils.mjs';
9
9
 
@@ -20,9 +20,12 @@ const mediaBackendPlugin = (config) => defineBackendPlugin({
20
20
  api: (adapter) => ({
21
21
  listAssets: (params) => listAssets(adapter, params),
22
22
  getAssetById: (id) => getAssetById(adapter, id),
23
+ createAsset: (input) => createAsset(adapter, input),
24
+ updateAsset: (id, input) => updateAsset(adapter, id, input),
23
25
  listFolders: (params) => listFolders(adapter, params),
24
26
  getFolderById: (id) => getFolderById(adapter, id),
25
- getFolderByName: (name, parentId, tenantId) => getFolderByName(adapter, name, parentId, tenantId)
27
+ getFolderByName: (name, parentId, tenantId) => getFolderByName(adapter, name, parentId, tenantId),
28
+ createFolder: (input) => createFolder(adapter, input)
26
29
  }),
27
30
  routes: (adapter) => {
28
31
  const {
@@ -2,6 +2,7 @@
2
2
 
3
3
  const plugin = require('../../../packages/stack/src/plugins/blog/api/plugin.cjs');
4
4
  const getters = require('../../../packages/stack/src/plugins/blog/api/getters.cjs');
5
+ const mutations = require('../../../packages/stack/src/plugins/blog/api/mutations.cjs');
5
6
  const serializers = require('../../../packages/stack/src/plugins/blog/api/serializers.cjs');
6
7
  const queryKeyDefs = require('../../../packages/stack/src/plugins/blog/api/query-key-defs.cjs');
7
8
  const plugins_blog_queryKeys = require('../query-keys.cjs');
@@ -14,6 +15,9 @@ exports.blogBackendPlugin = plugin.blogBackendPlugin;
14
15
  exports.getAllPosts = getters.getAllPosts;
15
16
  exports.getAllTags = getters.getAllTags;
16
17
  exports.getPostBySlug = getters.getPostBySlug;
18
+ exports.createPost = mutations.createPost;
19
+ exports.deletePost = mutations.deletePost;
20
+ exports.updatePost = mutations.updatePost;
17
21
  exports.serializePost = serializers.serializePost;
18
22
  exports.serializeTag = serializers.serializeTag;
19
23
  exports.BLOG_QUERY_KEYS = queryKeyDefs.BLOG_QUERY_KEYS;
@@ -1,5 +1,5 @@
1
- export { B as BLOG_QUERY_KEYS, h as BlogApiContext, k as BlogApiRouter, i as BlogBackendHooks, e as BlogRouteKey, N as NextPreviousPostsQuerySchema, P as PostListParams, f as PostListQuerySchema, c as PostListResult, j as blogBackendPlugin, d as createBlogQueryKeys, g as getAllPosts, b as getAllTags, a as getPostBySlug } from '../../../shared/stack.DjgpFWq3.cjs';
2
- import { P as Post, T as Tag, S as SerializedPost, a as SerializedTag } from '../../../shared/stack.BQmuNl5p.cjs';
1
+ export { B as BLOG_QUERY_KEYS, j as BlogApiContext, m as BlogApiRouter, k as BlogBackendHooks, h as BlogRouteKey, C as CreatePostInput, N as NextPreviousPostsQuerySchema, P as PostListParams, i as PostListQuerySchema, c as PostListResult, U as UpdatePostInput, l as blogBackendPlugin, f as createBlogQueryKeys, d as createPost, e as deletePost, g as getAllPosts, b as getAllTags, a as getPostBySlug, u as updatePost } from '../../../shared/stack.DWipT53I.cjs';
2
+ import { P as Post, T as Tag, S as SerializedPost, a as SerializedTag } from '../../../shared/stack.BWp0hcm9.cjs';
3
3
  import '@tanstack/react-query';
4
4
  import '@btst/stack/plugins/client';
5
5
  import '@btst/stack/plugins/api';
@@ -1,5 +1,5 @@
1
- export { B as BLOG_QUERY_KEYS, h as BlogApiContext, k as BlogApiRouter, i as BlogBackendHooks, e as BlogRouteKey, N as NextPreviousPostsQuerySchema, P as PostListParams, f as PostListQuerySchema, c as PostListResult, j as blogBackendPlugin, d as createBlogQueryKeys, g as getAllPosts, b as getAllTags, a as getPostBySlug } from '../../../shared/stack.IUeyQKrm.mjs';
2
- import { P as Post, T as Tag, S as SerializedPost, a as SerializedTag } from '../../../shared/stack.BQmuNl5p.mjs';
1
+ export { B as BLOG_QUERY_KEYS, j as BlogApiContext, m as BlogApiRouter, k as BlogBackendHooks, h as BlogRouteKey, C as CreatePostInput, N as NextPreviousPostsQuerySchema, P as PostListParams, i as PostListQuerySchema, c as PostListResult, U as UpdatePostInput, l as blogBackendPlugin, f as createBlogQueryKeys, d as createPost, e as deletePost, g as getAllPosts, b as getAllTags, a as getPostBySlug, u as updatePost } from '../../../shared/stack.E17kSK1W.mjs';
2
+ import { P as Post, T as Tag, S as SerializedPost, a as SerializedTag } from '../../../shared/stack.BWp0hcm9.mjs';
3
3
  import '@tanstack/react-query';
4
4
  import '@btst/stack/plugins/client';
5
5
  import '@btst/stack/plugins/api';
@@ -1,5 +1,5 @@
1
- export { B as BLOG_QUERY_KEYS, h as BlogApiContext, k as BlogApiRouter, i as BlogBackendHooks, e as BlogRouteKey, N as NextPreviousPostsQuerySchema, P as PostListParams, f as PostListQuerySchema, c as PostListResult, j as blogBackendPlugin, d as createBlogQueryKeys, g as getAllPosts, b as getAllTags, a as getPostBySlug } from '../../../shared/stack.D7HSzZdG.js';
2
- import { P as Post, T as Tag, S as SerializedPost, a as SerializedTag } from '../../../shared/stack.BQmuNl5p.js';
1
+ export { B as BLOG_QUERY_KEYS, j as BlogApiContext, m as BlogApiRouter, k as BlogBackendHooks, h as BlogRouteKey, C as CreatePostInput, N as NextPreviousPostsQuerySchema, P as PostListParams, i as PostListQuerySchema, c as PostListResult, U as UpdatePostInput, l as blogBackendPlugin, f as createBlogQueryKeys, d as createPost, e as deletePost, g as getAllPosts, b as getAllTags, a as getPostBySlug, u as updatePost } from '../../../shared/stack.D0p6oNme.js';
2
+ import { P as Post, T as Tag, S as SerializedPost, a as SerializedTag } from '../../../shared/stack.BWp0hcm9.js';
3
3
  import '@tanstack/react-query';
4
4
  import '@btst/stack/plugins/client';
5
5
  import '@btst/stack/plugins/api';
@@ -1,5 +1,6 @@
1
1
  export { NextPreviousPostsQuerySchema, PostListQuerySchema, blogBackendPlugin } from '../../../packages/stack/src/plugins/blog/api/plugin.mjs';
2
2
  export { getAllPosts, getAllTags, getPostBySlug } from '../../../packages/stack/src/plugins/blog/api/getters.mjs';
3
+ export { createPost, deletePost, updatePost } from '../../../packages/stack/src/plugins/blog/api/mutations.mjs';
3
4
  export { serializePost, serializeTag } from '../../../packages/stack/src/plugins/blog/api/serializers.mjs';
4
5
  export { BLOG_QUERY_KEYS } from '../../../packages/stack/src/plugins/blog/api/query-key-defs.mjs';
5
6
  export { createBlogQueryKeys } from '../query-keys.mjs';
@@ -1,6 +1,6 @@
1
- export { P as PostCreateInput, e as PostUpdateInput, o as UseNextPreviousPostsOptions, p as UseNextPreviousPostsResult, d as UsePostResult, b as UsePostSearchOptions, c as UsePostSearchResult, U as UsePostsOptions, a as UsePostsResult, r as UseRecentPostsOptions, s as UseRecentPostsResult, k as useCreatePost, m as useDeletePost, q as useNextPreviousPosts, g as usePost, n as usePostSearch, u as usePosts, t as useRecentPosts, h as useSuspensePost, f as useSuspensePosts, j as useSuspenseTags, i as useTags, l as useUpdatePost } from '../../../../shared/stack.AJTXI7kw.cjs';
1
+ export { P as PostCreateInput, e as PostUpdateInput, o as UseNextPreviousPostsOptions, p as UseNextPreviousPostsResult, d as UsePostResult, b as UsePostSearchOptions, c as UsePostSearchResult, U as UsePostsOptions, a as UsePostsResult, r as UseRecentPostsOptions, s as UseRecentPostsResult, k as useCreatePost, m as useDeletePost, q as useNextPreviousPosts, g as usePost, n as usePostSearch, u as usePosts, t as useRecentPosts, h as useSuspensePost, f as useSuspensePosts, j as useSuspenseTags, i as useTags, l as useUpdatePost } from '../../../../shared/stack.DX-tQ93o.cjs';
2
2
  import '@tanstack/react-query';
3
- import '../../../../shared/stack.BQmuNl5p.cjs';
3
+ import '../../../../shared/stack.BWp0hcm9.cjs';
4
4
  import 'zod';
5
5
 
6
6
  declare function useDebounce<T>(value: T, delay?: number): T;
@@ -1,6 +1,6 @@
1
- export { P as PostCreateInput, e as PostUpdateInput, o as UseNextPreviousPostsOptions, p as UseNextPreviousPostsResult, d as UsePostResult, b as UsePostSearchOptions, c as UsePostSearchResult, U as UsePostsOptions, a as UsePostsResult, r as UseRecentPostsOptions, s as UseRecentPostsResult, k as useCreatePost, m as useDeletePost, q as useNextPreviousPosts, g as usePost, n as usePostSearch, u as usePosts, t as useRecentPosts, h as useSuspensePost, f as useSuspensePosts, j as useSuspenseTags, i as useTags, l as useUpdatePost } from '../../../../shared/stack.6mEHS2WH.mjs';
1
+ export { P as PostCreateInput, e as PostUpdateInput, o as UseNextPreviousPostsOptions, p as UseNextPreviousPostsResult, d as UsePostResult, b as UsePostSearchOptions, c as UsePostSearchResult, U as UsePostsOptions, a as UsePostsResult, r as UseRecentPostsOptions, s as UseRecentPostsResult, k as useCreatePost, m as useDeletePost, q as useNextPreviousPosts, g as usePost, n as usePostSearch, u as usePosts, t as useRecentPosts, h as useSuspensePost, f as useSuspensePosts, j as useSuspenseTags, i as useTags, l as useUpdatePost } from '../../../../shared/stack.DOZ1EXjM.mjs';
2
2
  import '@tanstack/react-query';
3
- import '../../../../shared/stack.BQmuNl5p.mjs';
3
+ import '../../../../shared/stack.BWp0hcm9.mjs';
4
4
  import 'zod';
5
5
 
6
6
  declare function useDebounce<T>(value: T, delay?: number): T;
@@ -1,6 +1,6 @@
1
- export { P as PostCreateInput, e as PostUpdateInput, o as UseNextPreviousPostsOptions, p as UseNextPreviousPostsResult, d as UsePostResult, b as UsePostSearchOptions, c as UsePostSearchResult, U as UsePostsOptions, a as UsePostsResult, r as UseRecentPostsOptions, s as UseRecentPostsResult, k as useCreatePost, m as useDeletePost, q as useNextPreviousPosts, g as usePost, n as usePostSearch, u as usePosts, t as useRecentPosts, h as useSuspensePost, f as useSuspensePosts, j as useSuspenseTags, i as useTags, l as useUpdatePost } from '../../../../shared/stack.QYn-Px94.js';
1
+ export { P as PostCreateInput, e as PostUpdateInput, o as UseNextPreviousPostsOptions, p as UseNextPreviousPostsResult, d as UsePostResult, b as UsePostSearchOptions, c as UsePostSearchResult, U as UsePostsOptions, a as UsePostsResult, r as UseRecentPostsOptions, s as UseRecentPostsResult, k as useCreatePost, m as useDeletePost, q as useNextPreviousPosts, g as usePost, n as usePostSearch, u as usePosts, t as useRecentPosts, h as useSuspensePost, f as useSuspensePosts, j as useSuspenseTags, i as useTags, l as useUpdatePost } from '../../../../shared/stack.VF6FhyZw.js';
2
2
  import '@tanstack/react-query';
3
- import '../../../../shared/stack.BQmuNl5p.js';
3
+ import '../../../../shared/stack.BWp0hcm9.js';
4
4
  import 'zod';
5
5
 
6
6
  declare function useDebounce<T>(value: T, delay?: number): T;
@@ -2,8 +2,8 @@ import * as _btst_stack_plugins_client from '@btst/stack/plugins/client';
2
2
  import * as _btst_yar from '@btst/yar';
3
3
  import { ComponentType, ReactNode } from 'react';
4
4
  import { QueryClient } from '@tanstack/react-query';
5
- import { P as Post, S as SerializedPost } from '../../../shared/stack.BQmuNl5p.cjs';
6
- export { U as UsePostsOptions, a as UsePostsResult } from '../../../shared/stack.AJTXI7kw.cjs';
5
+ import { P as Post, S as SerializedPost } from '../../../shared/stack.BWp0hcm9.cjs';
6
+ export { U as UsePostsOptions, a as UsePostsResult } from '../../../shared/stack.DX-tQ93o.cjs';
7
7
  import 'zod';
8
8
 
9
9
  /**
@@ -2,8 +2,8 @@ import * as _btst_stack_plugins_client from '@btst/stack/plugins/client';
2
2
  import * as _btst_yar from '@btst/yar';
3
3
  import { ComponentType, ReactNode } from 'react';
4
4
  import { QueryClient } from '@tanstack/react-query';
5
- import { P as Post, S as SerializedPost } from '../../../shared/stack.BQmuNl5p.mjs';
6
- export { U as UsePostsOptions, a as UsePostsResult } from '../../../shared/stack.6mEHS2WH.mjs';
5
+ import { P as Post, S as SerializedPost } from '../../../shared/stack.BWp0hcm9.mjs';
6
+ export { U as UsePostsOptions, a as UsePostsResult } from '../../../shared/stack.DOZ1EXjM.mjs';
7
7
  import 'zod';
8
8
 
9
9
  /**
@@ -2,8 +2,8 @@ import * as _btst_stack_plugins_client from '@btst/stack/plugins/client';
2
2
  import * as _btst_yar from '@btst/yar';
3
3
  import { ComponentType, ReactNode } from 'react';
4
4
  import { QueryClient } from '@tanstack/react-query';
5
- import { P as Post, S as SerializedPost } from '../../../shared/stack.BQmuNl5p.js';
6
- export { U as UsePostsOptions, a as UsePostsResult } from '../../../shared/stack.QYn-Px94.js';
5
+ import { P as Post, S as SerializedPost } from '../../../shared/stack.BWp0hcm9.js';
6
+ export { U as UsePostsOptions, a as UsePostsResult } from '../../../shared/stack.VF6FhyZw.js';
7
7
  import 'zod';
8
8
 
9
9
  /**
@@ -1,7 +1,7 @@
1
1
  import '@tanstack/react-query';
2
- export { d as createBlogQueryKeys } from '../../shared/stack.DjgpFWq3.cjs';
2
+ export { f as createBlogQueryKeys } from '../../shared/stack.DWipT53I.cjs';
3
3
  import '@btst/stack/plugins/client';
4
- import '../../shared/stack.BQmuNl5p.cjs';
4
+ import '../../shared/stack.BWp0hcm9.cjs';
5
5
  import '@btst/stack/plugins/api';
6
6
  import '@btst/db';
7
7
  import 'better-call';
@@ -1,7 +1,7 @@
1
1
  import '@tanstack/react-query';
2
- export { d as createBlogQueryKeys } from '../../shared/stack.IUeyQKrm.mjs';
2
+ export { f as createBlogQueryKeys } from '../../shared/stack.E17kSK1W.mjs';
3
3
  import '@btst/stack/plugins/client';
4
- import '../../shared/stack.BQmuNl5p.mjs';
4
+ import '../../shared/stack.BWp0hcm9.mjs';
5
5
  import '@btst/stack/plugins/api';
6
6
  import '@btst/db';
7
7
  import 'better-call';
@@ -1,7 +1,7 @@
1
1
  import '@tanstack/react-query';
2
- export { d as createBlogQueryKeys } from '../../shared/stack.D7HSzZdG.js';
2
+ export { f as createBlogQueryKeys } from '../../shared/stack.D0p6oNme.js';
3
3
  import '@btst/stack/plugins/client';
4
- import '../../shared/stack.BQmuNl5p.js';
4
+ import '../../shared/stack.BWp0hcm9.js';
5
5
  import '@btst/stack/plugins/api';
6
6
  import '@btst/db';
7
7
  import 'better-call';
@@ -1,4 +1,4 @@
1
- export { B as BoardListResult, C as CreateKanbanTaskInput, i as KANBAN_QUERY_KEYS, b as KanbanApiContext, K as KanbanApiRouter, c as KanbanBackendHooks, a as KanbanRouteKey, e as createKanbanTask, f as findOrCreateKanbanBoard, g as getAllBoards, d as getBoardById, h as getKanbanColumnsByBoardId, k as kanbanBackendPlugin } from '../../../shared/stack.eq5eg1yt.cjs';
1
+ export { B as BoardListResult, C as CreateKanbanTaskInput, i as KANBAN_QUERY_KEYS, b as KanbanApiContext, K as KanbanApiRouter, c as KanbanBackendHooks, a as KanbanRouteKey, e as createKanbanTask, f as findOrCreateKanbanBoard, g as getAllBoards, d as getBoardById, h as getKanbanColumnsByBoardId, k as kanbanBackendPlugin } from '../../../shared/stack.B6S3cgwN.cjs';
2
2
  import { B as BoardWithColumns, S as SerializedBoardWithColumns, C as ColumnWithTasks, a as SerializedColumn, T as Task, b as SerializedTask } from '../../../shared/stack.DJaKVY7v.cjs';
3
3
  import '@btst/stack/plugins/api';
4
4
  import '@btst/db';
@@ -1,4 +1,4 @@
1
- export { B as BoardListResult, C as CreateKanbanTaskInput, i as KANBAN_QUERY_KEYS, b as KanbanApiContext, K as KanbanApiRouter, c as KanbanBackendHooks, a as KanbanRouteKey, e as createKanbanTask, f as findOrCreateKanbanBoard, g as getAllBoards, d as getBoardById, h as getKanbanColumnsByBoardId, k as kanbanBackendPlugin } from '../../../shared/stack.Dj04W2c3.mjs';
1
+ export { B as BoardListResult, C as CreateKanbanTaskInput, i as KANBAN_QUERY_KEYS, b as KanbanApiContext, K as KanbanApiRouter, c as KanbanBackendHooks, a as KanbanRouteKey, e as createKanbanTask, f as findOrCreateKanbanBoard, g as getAllBoards, d as getBoardById, h as getKanbanColumnsByBoardId, k as kanbanBackendPlugin } from '../../../shared/stack.Bzfx-_lq.mjs';
2
2
  import { B as BoardWithColumns, S as SerializedBoardWithColumns, C as ColumnWithTasks, a as SerializedColumn, T as Task, b as SerializedTask } from '../../../shared/stack.DJaKVY7v.mjs';
3
3
  import '@btst/stack/plugins/api';
4
4
  import '@btst/db';
@@ -1,4 +1,4 @@
1
- export { B as BoardListResult, C as CreateKanbanTaskInput, i as KANBAN_QUERY_KEYS, b as KanbanApiContext, K as KanbanApiRouter, c as KanbanBackendHooks, a as KanbanRouteKey, e as createKanbanTask, f as findOrCreateKanbanBoard, g as getAllBoards, d as getBoardById, h as getKanbanColumnsByBoardId, k as kanbanBackendPlugin } from '../../../shared/stack.CMbX8Q5C.js';
1
+ export { B as BoardListResult, C as CreateKanbanTaskInput, i as KANBAN_QUERY_KEYS, b as KanbanApiContext, K as KanbanApiRouter, c as KanbanBackendHooks, a as KanbanRouteKey, e as createKanbanTask, f as findOrCreateKanbanBoard, g as getAllBoards, d as getBoardById, h as getKanbanColumnsByBoardId, k as kanbanBackendPlugin } from '../../../shared/stack.j5SFLC1d.js';
2
2
  import { B as BoardWithColumns, S as SerializedBoardWithColumns, C as ColumnWithTasks, a as SerializedColumn, T as Task, b as SerializedTask } from '../../../shared/stack.DJaKVY7v.js';
3
3
  import '@btst/stack/plugins/api';
4
4
  import '@btst/db';
@@ -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.eq5eg1yt.cjs';
2
+ import { K as KanbanApiRouter, j as BoardsListDiscriminator } from '../../shared/stack.B6S3cgwN.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.Dj04W2c3.mjs';
2
+ import { K as KanbanApiRouter, j as BoardsListDiscriminator } from '../../shared/stack.Bzfx-_lq.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.CMbX8Q5C.js';
2
+ import { K as KanbanApiRouter, j as BoardsListDiscriminator } from '../../shared/stack.j5SFLC1d.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';
@@ -40,7 +40,13 @@ function localAdapter(options = {}) {
40
40
  const encodedFilename = url.split("/").pop();
41
41
  if (!encodedFilename) return;
42
42
  const filename = decodeURIComponent(encodedFilename);
43
- const filePath = path__namespace.join(uploadDir, filename);
43
+ const resolvedUploadDir = path__namespace.resolve(uploadDir);
44
+ const filePath = path__namespace.join(resolvedUploadDir, filename);
45
+ if (!filePath.startsWith(resolvedUploadDir + path__namespace.sep)) {
46
+ throw new Error(
47
+ `Refusing to delete file outside upload directory: ${filePath}`
48
+ );
49
+ }
44
50
  try {
45
51
  await fs__namespace.unlink(filePath);
46
52
  } catch (err) {
@@ -0,0 +1,30 @@
1
+ import { D as DirectStorageAdapter } from '../../../../shared/stack.lkebw2nj.cjs';
2
+
3
+ interface LocalStorageAdapterOptions {
4
+ /**
5
+ * Absolute path to the directory where uploaded files are stored.
6
+ * @default "./public/uploads"
7
+ */
8
+ uploadDir?: string;
9
+ /**
10
+ * URL prefix used to build the public URL for uploaded files.
11
+ * @default "/uploads"
12
+ */
13
+ publicPath?: string;
14
+ }
15
+ /**
16
+ * Create a local filesystem storage adapter.
17
+ * Files are written to `uploadDir` and served at `publicPath`.
18
+ * Suitable for development and self-hosted deployments.
19
+ *
20
+ * @example
21
+ * ```ts
22
+ * mediaBackendPlugin({
23
+ * storageAdapter: localAdapter({ uploadDir: "./public/uploads", publicPath: "/uploads" })
24
+ * })
25
+ * ```
26
+ */
27
+ declare function localAdapter(options?: LocalStorageAdapterOptions): DirectStorageAdapter;
28
+
29
+ export { localAdapter };
30
+ export type { LocalStorageAdapterOptions };
@@ -0,0 +1,30 @@
1
+ import { D as DirectStorageAdapter } from '../../../../shared/stack.lkebw2nj.mjs';
2
+
3
+ interface LocalStorageAdapterOptions {
4
+ /**
5
+ * Absolute path to the directory where uploaded files are stored.
6
+ * @default "./public/uploads"
7
+ */
8
+ uploadDir?: string;
9
+ /**
10
+ * URL prefix used to build the public URL for uploaded files.
11
+ * @default "/uploads"
12
+ */
13
+ publicPath?: string;
14
+ }
15
+ /**
16
+ * Create a local filesystem storage adapter.
17
+ * Files are written to `uploadDir` and served at `publicPath`.
18
+ * Suitable for development and self-hosted deployments.
19
+ *
20
+ * @example
21
+ * ```ts
22
+ * mediaBackendPlugin({
23
+ * storageAdapter: localAdapter({ uploadDir: "./public/uploads", publicPath: "/uploads" })
24
+ * })
25
+ * ```
26
+ */
27
+ declare function localAdapter(options?: LocalStorageAdapterOptions): DirectStorageAdapter;
28
+
29
+ export { localAdapter };
30
+ export type { LocalStorageAdapterOptions };
@@ -0,0 +1,30 @@
1
+ import { D as DirectStorageAdapter } from '../../../../shared/stack.lkebw2nj.js';
2
+
3
+ interface LocalStorageAdapterOptions {
4
+ /**
5
+ * Absolute path to the directory where uploaded files are stored.
6
+ * @default "./public/uploads"
7
+ */
8
+ uploadDir?: string;
9
+ /**
10
+ * URL prefix used to build the public URL for uploaded files.
11
+ * @default "/uploads"
12
+ */
13
+ publicPath?: string;
14
+ }
15
+ /**
16
+ * Create a local filesystem storage adapter.
17
+ * Files are written to `uploadDir` and served at `publicPath`.
18
+ * Suitable for development and self-hosted deployments.
19
+ *
20
+ * @example
21
+ * ```ts
22
+ * mediaBackendPlugin({
23
+ * storageAdapter: localAdapter({ uploadDir: "./public/uploads", publicPath: "/uploads" })
24
+ * })
25
+ * ```
26
+ */
27
+ declare function localAdapter(options?: LocalStorageAdapterOptions): DirectStorageAdapter;
28
+
29
+ export { localAdapter };
30
+ export type { LocalStorageAdapterOptions };