@btst/stack 2.11.1 → 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 (40) 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/plugins/blog/api/index.cjs +4 -0
  6. package/dist/plugins/blog/api/index.d.cts +2 -2
  7. package/dist/plugins/blog/api/index.d.mts +2 -2
  8. package/dist/plugins/blog/api/index.d.ts +2 -2
  9. package/dist/plugins/blog/api/index.mjs +1 -0
  10. package/dist/plugins/blog/client/hooks/index.d.cts +2 -2
  11. package/dist/plugins/blog/client/hooks/index.d.mts +2 -2
  12. package/dist/plugins/blog/client/hooks/index.d.ts +2 -2
  13. package/dist/plugins/blog/client/index.d.cts +2 -2
  14. package/dist/plugins/blog/client/index.d.mts +2 -2
  15. package/dist/plugins/blog/client/index.d.ts +2 -2
  16. package/dist/plugins/blog/query-keys.d.cts +2 -2
  17. package/dist/plugins/blog/query-keys.d.mts +2 -2
  18. package/dist/plugins/blog/query-keys.d.ts +2 -2
  19. package/dist/plugins/kanban/api/index.d.cts +1 -1
  20. package/dist/plugins/kanban/api/index.d.mts +1 -1
  21. package/dist/plugins/kanban/api/index.d.ts +1 -1
  22. package/dist/plugins/kanban/query-keys.d.cts +1 -1
  23. package/dist/plugins/kanban/query-keys.d.mts +1 -1
  24. package/dist/plugins/kanban/query-keys.d.ts +1 -1
  25. package/dist/shared/{stack.DvMUCTTl.d.mts → stack.D0p6oNme.d.ts} +96 -13
  26. package/dist/shared/{stack.DGsFF5qb.d.cts → stack.DOZ1EXjM.d.mts} +5 -5
  27. package/dist/shared/{stack.qta0-CPq.d.ts → stack.DWipT53I.d.cts} +96 -13
  28. package/dist/shared/{stack.NtflNnnH.d.mts → stack.DX-tQ93o.d.cts} +5 -5
  29. package/dist/shared/{stack.DEW8EtFu.d.cts → stack.E17kSK1W.d.mts} +96 -13
  30. package/dist/shared/{stack.QrBE0Bc8.d.ts → stack.VF6FhyZw.d.ts} +5 -5
  31. package/package.json +1 -1
  32. package/src/plugins/blog/api/index.ts +7 -0
  33. package/src/plugins/blog/api/mutations.ts +287 -0
  34. package/src/plugins/blog/api/plugin.ts +43 -184
  35. package/dist/shared/{stack.BOokfhZD.d.cts → stack.B6S3cgwN.d.cts} +16 -16
  36. package/dist/shared/{stack.Sod_PZhB.d.cts → stack.BWp0hcm9.d.cts} +7 -7
  37. package/dist/shared/{stack.Sod_PZhB.d.mts → stack.BWp0hcm9.d.mts} +7 -7
  38. package/dist/shared/{stack.Sod_PZhB.d.ts → stack.BWp0hcm9.d.ts} +7 -7
  39. package/dist/shared/{stack.CWxAl9K3.d.mts → stack.Bzfx-_lq.d.mts} +16 -16
  40. package/dist/shared/{stack.BvCR4-9H.d.ts → stack.j5SFLC1d.d.ts} +16 -16
@@ -0,0 +1,287 @@
1
+ import type { DBAdapter as Adapter } from "@btst/db";
2
+ import type { Post, Tag } from "../types";
3
+ import { slugify } from "../utils";
4
+
5
+ type TagInput = { name: string } | { id: string; name: string; slug: string };
6
+
7
+ /**
8
+ * Find existing tags by slug or create missing ones, then return the resolved Tag records.
9
+ * Tags that already carry an `id` are returned as-is (after name normalisation).
10
+ */
11
+ async function findOrCreateTags(
12
+ adapter: Adapter,
13
+ tagInputs: TagInput[],
14
+ ): Promise<Tag[]> {
15
+ if (tagInputs.length === 0) return [];
16
+
17
+ const normalizeTagName = (name: string): string => name.trim();
18
+
19
+ const tagsWithIds: Tag[] = [];
20
+ const tagsToFindOrCreate: Array<{ name: string }> = [];
21
+
22
+ for (const tagInput of tagInputs) {
23
+ if ("id" in tagInput && tagInput.id) {
24
+ tagsWithIds.push({
25
+ id: tagInput.id,
26
+ name: normalizeTagName(tagInput.name),
27
+ slug: tagInput.slug,
28
+ createdAt: new Date(),
29
+ updatedAt: new Date(),
30
+ } as Tag);
31
+ } else {
32
+ tagsToFindOrCreate.push({ name: normalizeTagName(tagInput.name) });
33
+ }
34
+ }
35
+
36
+ if (tagsToFindOrCreate.length === 0) {
37
+ return tagsWithIds;
38
+ }
39
+
40
+ const allTags = await adapter.findMany<Tag>({ model: "tag" });
41
+ const tagMapBySlug = new Map<string, Tag>();
42
+ for (const tag of allTags) {
43
+ tagMapBySlug.set(tag.slug, tag);
44
+ }
45
+
46
+ const tagSlugs = tagsToFindOrCreate.map((tag) => slugify(tag.name));
47
+ const foundTags: Tag[] = [];
48
+ for (const slug of tagSlugs) {
49
+ const tag = tagMapBySlug.get(slug);
50
+ if (tag) {
51
+ foundTags.push(tag);
52
+ }
53
+ }
54
+
55
+ const existingSlugs = new Set([
56
+ ...tagsWithIds.map((tag) => tag.slug),
57
+ ...foundTags.map((tag) => tag.slug),
58
+ ]);
59
+ const tagsToCreate = tagsToFindOrCreate.filter(
60
+ (tag) => !existingSlugs.has(slugify(tag.name)),
61
+ );
62
+
63
+ const createdTags: Tag[] = [];
64
+ for (const tag of tagsToCreate) {
65
+ const normalizedName = normalizeTagName(tag.name);
66
+ const newTag = await adapter.create<Tag>({
67
+ model: "tag",
68
+ data: {
69
+ name: normalizedName,
70
+ slug: slugify(normalizedName),
71
+ createdAt: new Date(),
72
+ updatedAt: new Date(),
73
+ },
74
+ });
75
+ createdTags.push(newTag);
76
+ }
77
+
78
+ return [...tagsWithIds, ...foundTags, ...createdTags];
79
+ }
80
+
81
+ /**
82
+ * Input for creating a new blog post.
83
+ * `slug` must already be slugified by the caller.
84
+ */
85
+ export interface CreatePostInput {
86
+ title: string;
87
+ content: string;
88
+ excerpt: string;
89
+ /** Pre-slugified URL slug — use {@link slugify} before passing. */
90
+ slug: string;
91
+ image?: string;
92
+ published?: boolean;
93
+ publishedAt?: Date;
94
+ createdAt?: Date;
95
+ updatedAt?: Date;
96
+ tags?: TagInput[];
97
+ }
98
+
99
+ /**
100
+ * Input for updating an existing blog post.
101
+ * If `slug` is provided it must already be slugified by the caller.
102
+ */
103
+ export interface UpdatePostInput {
104
+ title?: string;
105
+ content?: string;
106
+ excerpt?: string;
107
+ /** Pre-slugified URL slug — use {@link slugify} before passing. */
108
+ slug?: string;
109
+ image?: string;
110
+ published?: boolean;
111
+ publishedAt?: Date;
112
+ createdAt?: Date;
113
+ updatedAt?: Date;
114
+ tags?: TagInput[];
115
+ }
116
+
117
+ /**
118
+ * Create a new blog post with optional tag associations.
119
+ * Pure DB function — no hooks, no HTTP context. Safe for server-side and SSG use.
120
+ *
121
+ * @remarks **Security:** Authorization hooks (e.g. `onBeforeCreatePost`) are NOT
122
+ * called. The caller is responsible for any access-control checks before
123
+ * invoking this function.
124
+ *
125
+ * @param adapter - The database adapter
126
+ * @param input - Post data; `slug` must be pre-slugified
127
+ */
128
+ export async function createPost(
129
+ adapter: Adapter,
130
+ input: CreatePostInput,
131
+ ): Promise<Post> {
132
+ const { tags: tagInputs, ...postData } = input;
133
+ const tagList = tagInputs ?? [];
134
+
135
+ const newPost = await adapter.create<Post>({
136
+ model: "post",
137
+ data: {
138
+ ...postData,
139
+ published: postData.published ?? false,
140
+ tags: [] as Tag[],
141
+ createdAt: postData.createdAt ?? new Date(),
142
+ updatedAt: postData.updatedAt ?? new Date(),
143
+ },
144
+ });
145
+
146
+ if (tagList.length > 0) {
147
+ const resolvedTags = await findOrCreateTags(adapter, tagList);
148
+
149
+ await adapter.transaction(async (tx) => {
150
+ for (const tag of resolvedTags) {
151
+ await tx.create<{ postId: string; tagId: string }>({
152
+ model: "postTag",
153
+ data: {
154
+ postId: newPost.id,
155
+ tagId: tag.id,
156
+ },
157
+ });
158
+ }
159
+ });
160
+
161
+ newPost.tags = resolvedTags.map((tag) => ({ ...tag }));
162
+ } else {
163
+ newPost.tags = [];
164
+ }
165
+
166
+ return newPost;
167
+ }
168
+
169
+ /**
170
+ * Update an existing blog post and reconcile its tag associations.
171
+ * Returns `null` if no post with the given `id` exists.
172
+ * Pure DB function — no hooks, no HTTP context. Safe for server-side use.
173
+ *
174
+ * @remarks **Security:** Authorization hooks (e.g. `onBeforeUpdatePost`) are NOT
175
+ * called. The caller is responsible for any access-control checks before
176
+ * invoking this function.
177
+ *
178
+ * @param adapter - The database adapter
179
+ * @param id - The post ID to update
180
+ * @param input - Partial post data to apply; `slug` must be pre-slugified if provided
181
+ */
182
+ export async function updatePost(
183
+ adapter: Adapter,
184
+ id: string,
185
+ input: UpdatePostInput,
186
+ ): Promise<Post | null> {
187
+ const { tags: tagInputs, ...postData } = input;
188
+
189
+ return adapter.transaction(async (tx) => {
190
+ const updatedPost = await tx.update<Post>({
191
+ model: "post",
192
+ where: [{ field: "id", value: id }],
193
+ update: {
194
+ ...postData,
195
+ updatedAt: new Date(),
196
+ },
197
+ });
198
+
199
+ if (!updatedPost) return null;
200
+
201
+ if (tagInputs !== undefined) {
202
+ const existingPostTags = await tx.findMany<{
203
+ postId: string;
204
+ tagId: string;
205
+ }>({
206
+ model: "postTag",
207
+ where: [{ field: "postId", value: id, operator: "eq" as const }],
208
+ });
209
+
210
+ for (const postTag of existingPostTags) {
211
+ await tx.delete<{ postId: string; tagId: string }>({
212
+ model: "postTag",
213
+ where: [
214
+ {
215
+ field: "postId",
216
+ value: postTag.postId,
217
+ operator: "eq" as const,
218
+ },
219
+ {
220
+ field: "tagId",
221
+ value: postTag.tagId,
222
+ operator: "eq" as const,
223
+ },
224
+ ],
225
+ });
226
+ }
227
+
228
+ if (tagInputs.length > 0) {
229
+ const resolvedTags = await findOrCreateTags(adapter, tagInputs);
230
+
231
+ for (const tag of resolvedTags) {
232
+ await tx.create<{ postId: string; tagId: string }>({
233
+ model: "postTag",
234
+ data: {
235
+ postId: id,
236
+ tagId: tag.id,
237
+ },
238
+ });
239
+ }
240
+
241
+ updatedPost.tags = resolvedTags.map((tag) => ({ ...tag }));
242
+ } else {
243
+ updatedPost.tags = [];
244
+ }
245
+ } else {
246
+ const existingPostTags = await tx.findMany<{
247
+ postId: string;
248
+ tagId: string;
249
+ }>({
250
+ model: "postTag",
251
+ where: [{ field: "postId", value: id, operator: "eq" as const }],
252
+ });
253
+
254
+ if (existingPostTags.length > 0) {
255
+ const tagIds = existingPostTags.map((pt) => pt.tagId);
256
+ const tags = await tx.findMany<Tag>({
257
+ model: "tag",
258
+ });
259
+ updatedPost.tags = tags
260
+ .filter((tag) => tagIds.includes(tag.id))
261
+ .map((tag) => ({ ...tag }));
262
+ } else {
263
+ updatedPost.tags = [];
264
+ }
265
+ }
266
+
267
+ return updatedPost;
268
+ });
269
+ }
270
+
271
+ /**
272
+ * Delete a blog post by ID.
273
+ * Pure DB function — no hooks, no HTTP context. Safe for server-side use.
274
+ *
275
+ * @remarks **Security:** Authorization hooks (e.g. `onBeforeDeletePost`) are NOT
276
+ * called. The caller is responsible for any access-control checks before
277
+ * invoking this function.
278
+ *
279
+ * @param adapter - The database adapter
280
+ * @param id - The post ID to delete
281
+ */
282
+ export async function deletePost(adapter: Adapter, id: string): Promise<void> {
283
+ await adapter.delete<Post>({
284
+ model: "post",
285
+ where: [{ field: "id", value: id }],
286
+ });
287
+ }
@@ -7,6 +7,13 @@ import type { Post, PostWithPostTag, Tag } from "../types";
7
7
  import { slugify } from "../utils";
8
8
  import { createPostSchema, updatePostSchema } from "../schemas";
9
9
  import { getAllPosts, getPostBySlug, getAllTags } from "./getters";
10
+ import {
11
+ createPost as createPostMutation,
12
+ updatePost as updatePostMutation,
13
+ deletePost as deletePostMutation,
14
+ type CreatePostInput,
15
+ type UpdatePostInput,
16
+ } from "./mutations";
10
17
  import { BLOG_QUERY_KEYS } from "./query-key-defs";
11
18
  import { serializePost, serializeTag } from "./serializers";
12
19
  import type { QueryClient } from "@tanstack/react-query";
@@ -260,85 +267,15 @@ export const blogBackendPlugin = (hooks?: BlogBackendHooks) =>
260
267
  getPostBySlug: (slug: string) => getPostBySlug(adapter, slug),
261
268
  getAllTags: () => getAllTags(adapter),
262
269
  prefetchForRoute: createBlogPrefetchForRoute(adapter),
270
+ // Mutations
271
+ createPost: (input: CreatePostInput) =>
272
+ createPostMutation(adapter, input),
273
+ updatePost: (id: string, input: UpdatePostInput) =>
274
+ updatePostMutation(adapter, id, input),
275
+ deletePost: (id: string) => deletePostMutation(adapter, id),
263
276
  }),
264
277
 
265
278
  routes: (adapter: Adapter) => {
266
- const findOrCreateTags = async (
267
- tagInputs: Array<
268
- { name: string } | { id: string; name: string; slug: string }
269
- >,
270
- ): Promise<Tag[]> => {
271
- if (tagInputs.length === 0) return [];
272
-
273
- const normalizeTagName = (name: string): string => {
274
- return name.trim();
275
- };
276
-
277
- const tagsWithIds: Tag[] = [];
278
- const tagsToFindOrCreate: Array<{ name: string }> = [];
279
-
280
- for (const tagInput of tagInputs) {
281
- if ("id" in tagInput && tagInput.id) {
282
- tagsWithIds.push({
283
- id: tagInput.id,
284
- name: normalizeTagName(tagInput.name),
285
- slug: tagInput.slug,
286
- createdAt: new Date(),
287
- updatedAt: new Date(),
288
- } as Tag);
289
- } else {
290
- tagsToFindOrCreate.push({ name: normalizeTagName(tagInput.name) });
291
- }
292
- }
293
-
294
- if (tagsToFindOrCreate.length === 0) {
295
- return tagsWithIds;
296
- }
297
-
298
- const allTags = await adapter.findMany<Tag>({
299
- model: "tag",
300
- });
301
- const tagMapBySlug = new Map<string, Tag>();
302
- for (const tag of allTags) {
303
- tagMapBySlug.set(tag.slug, tag);
304
- }
305
-
306
- const tagSlugs = tagsToFindOrCreate.map((tag) => slugify(tag.name));
307
- const foundTags: Tag[] = [];
308
-
309
- for (const slug of tagSlugs) {
310
- const tag = tagMapBySlug.get(slug);
311
- if (tag) {
312
- foundTags.push(tag);
313
- }
314
- }
315
-
316
- const existingSlugs = new Set([
317
- ...tagsWithIds.map((tag) => tag.slug),
318
- ...foundTags.map((tag) => tag.slug),
319
- ]);
320
- const tagsToCreate = tagsToFindOrCreate.filter(
321
- (tag) => !existingSlugs.has(slugify(tag.name)),
322
- );
323
-
324
- const createdTags: Tag[] = [];
325
- for (const tag of tagsToCreate) {
326
- const normalizedName = normalizeTagName(tag.name);
327
- const newTag = await adapter.create<Tag>({
328
- model: "tag",
329
- data: {
330
- name: normalizedName,
331
- slug: slugify(normalizedName),
332
- createdAt: new Date(),
333
- updatedAt: new Date(),
334
- },
335
- });
336
- createdTags.push(newTag);
337
- }
338
-
339
- return [...tagsWithIds, ...foundTags, ...createdTags];
340
- };
341
-
342
279
  const listPosts = createEndpoint(
343
280
  "/posts",
344
281
  {
@@ -394,11 +331,17 @@ export const blogBackendPlugin = (hooks?: BlogBackendHooks) =>
394
331
  );
395
332
  }
396
333
 
397
- const { tags, ...postData } = ctx.body;
398
- const tagNames = tags || [];
334
+ // Destructure and discard createdAt/updatedAt timestamps are always server-generated
335
+ const {
336
+ tags,
337
+ slug: rawSlug,
338
+ createdAt: _ca,
339
+ updatedAt: _ua,
340
+ ...postData
341
+ } = ctx.body;
399
342
 
400
343
  // Always slugify to ensure URL-safe slug, whether provided or generated from title
401
- const slug = slugify(postData.slug || postData.title);
344
+ const slug = slugify(rawSlug || postData.title);
402
345
 
403
346
  // Validate that slugification produced a non-empty result
404
347
  if (!slug) {
@@ -408,37 +351,14 @@ export const blogBackendPlugin = (hooks?: BlogBackendHooks) =>
408
351
  });
409
352
  }
410
353
 
411
- const newPost = await adapter.create<Post>({
412
- model: "post",
413
- data: {
414
- ...postData,
415
- slug,
416
- tags: [],
417
- createdAt: new Date(),
418
- updatedAt: new Date(),
419
- },
354
+ const newPost = await createPostMutation(adapter, {
355
+ ...postData,
356
+ slug,
357
+ tags: tags ?? [],
358
+ createdAt: new Date(),
359
+ updatedAt: new Date(),
420
360
  });
421
361
 
422
- if (tagNames.length > 0) {
423
- const createdTags = await findOrCreateTags(tagNames);
424
-
425
- await adapter.transaction(async (tx) => {
426
- for (const tag of createdTags) {
427
- await tx.create<{ postId: string; tagId: string }>({
428
- model: "postTag",
429
- data: {
430
- postId: newPost.id,
431
- tagId: tag.id,
432
- },
433
- });
434
- }
435
- });
436
-
437
- newPost.tags = createdTags.map((tag) => ({ ...tag }));
438
- } else {
439
- newPost.tags = [];
440
- }
441
-
442
362
  if (hooks?.onPostCreated) {
443
363
  await hooks.onPostCreated(newPost, context);
444
364
  }
@@ -475,8 +395,14 @@ export const blogBackendPlugin = (hooks?: BlogBackendHooks) =>
475
395
  );
476
396
  }
477
397
 
478
- const { tags, slug: rawSlug, ...restPostData } = ctx.body;
479
- const tagNames = tags || [];
398
+ // Destructure and discard createdAt/updatedAt timestamps are always server-generated
399
+ const {
400
+ tags,
401
+ slug: rawSlug,
402
+ createdAt: _ca,
403
+ updatedAt: _ua,
404
+ ...restPostData
405
+ } = ctx.body;
480
406
 
481
407
  // Sanitize slug if provided to ensure it's URL-safe
482
408
  const slugified = rawSlug ? slugify(rawSlug) : undefined;
@@ -489,80 +415,16 @@ export const blogBackendPlugin = (hooks?: BlogBackendHooks) =>
489
415
  });
490
416
  }
491
417
 
492
- const postData = {
418
+ const updated = await updatePostMutation(adapter, ctx.params.id, {
493
419
  ...restPostData,
494
420
  ...(slugified ? { slug: slugified } : {}),
495
- };
496
-
497
- const updated = await adapter.transaction(async (tx) => {
498
- const existingPostTags = await tx.findMany<{
499
- postId: string;
500
- tagId: string;
501
- }>({
502
- model: "postTag",
503
- where: [
504
- {
505
- field: "postId",
506
- value: ctx.params.id,
507
- operator: "eq" as const,
508
- },
509
- ],
510
- });
511
-
512
- const updatedPost = await tx.update<Post>({
513
- model: "post",
514
- where: [{ field: "id", value: ctx.params.id }],
515
- update: {
516
- ...postData,
517
- updatedAt: new Date(),
518
- },
519
- });
520
-
521
- if (!updatedPost) {
522
- throw ctx.error(404, {
523
- message: "Post not found",
524
- });
525
- }
526
-
527
- for (const postTag of existingPostTags) {
528
- await tx.delete<{ postId: string; tagId: string }>({
529
- model: "postTag",
530
- where: [
531
- {
532
- field: "postId",
533
- value: postTag.postId,
534
- operator: "eq" as const,
535
- },
536
- {
537
- field: "tagId",
538
- value: postTag.tagId,
539
- operator: "eq" as const,
540
- },
541
- ],
542
- });
543
- }
544
-
545
- if (tagNames.length > 0) {
546
- const createdTags = await findOrCreateTags(tagNames);
547
-
548
- for (const tag of createdTags) {
549
- await tx.create<{ postId: string; tagId: string }>({
550
- model: "postTag",
551
- data: {
552
- postId: ctx.params.id,
553
- tagId: tag.id,
554
- },
555
- });
556
- }
557
-
558
- updatedPost.tags = createdTags.map((tag) => ({ ...tag }));
559
- } else {
560
- updatedPost.tags = [];
561
- }
562
-
563
- return updatedPost;
421
+ tags: tags ?? [],
564
422
  });
565
423
 
424
+ if (!updated) {
425
+ throw ctx.error(404, { message: "Post not found" });
426
+ }
427
+
566
428
  if (hooks?.onPostUpdated) {
567
429
  await hooks.onPostUpdated(updated, context);
568
430
  }
@@ -597,10 +459,7 @@ export const blogBackendPlugin = (hooks?: BlogBackendHooks) =>
597
459
  );
598
460
  }
599
461
 
600
- await adapter.delete<Post>({
601
- model: "post",
602
- where: [{ field: "id", value: ctx.params.id }],
603
- });
462
+ await deletePostMutation(adapter, ctx.params.id);
604
463
 
605
464
  // Lifecycle hook
606
465
  if (hooks?.onPostDeleted) {
@@ -7,12 +7,12 @@ import { QueryClient } from '@tanstack/react-query';
7
7
 
8
8
  declare const createBoardSchema: z.ZodObject<{
9
9
  description: z.ZodOptional<z.ZodString>;
10
- name: z.ZodString;
11
- createdAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
12
- updatedAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
13
10
  slug: z.ZodOptional<z.ZodString>;
14
11
  ownerId: z.ZodOptional<z.ZodString>;
15
12
  organizationId: z.ZodOptional<z.ZodString>;
13
+ createdAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
14
+ updatedAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
15
+ name: z.ZodString;
16
16
  }, z.core.$strip>;
17
17
  declare const updateBoardSchema: z.ZodObject<{
18
18
  createdAt: z.ZodOptional<z.ZodOptional<z.ZodCoercedDate<unknown>>>;
@@ -28,8 +28,8 @@ declare const createColumnSchema: z.ZodObject<{
28
28
  title: z.ZodString;
29
29
  createdAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
30
30
  updatedAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
31
- boardId: z.ZodString;
32
31
  order: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
32
+ boardId: z.ZodString;
33
33
  }, z.core.$strip>;
34
34
  declare const updateColumnSchema: z.ZodObject<{
35
35
  createdAt: z.ZodOptional<z.ZodOptional<z.ZodCoercedDate<unknown>>>;
@@ -331,19 +331,19 @@ declare const kanbanBackendPlugin: (hooks?: KanbanBackendHooks) => _btst_stack_p
331
331
  body: better_call.StandardSchemaV1<{
332
332
  name: string;
333
333
  description?: string | undefined;
334
- createdAt?: unknown;
335
- updatedAt?: unknown;
336
334
  slug?: string | undefined;
337
335
  ownerId?: string | undefined;
338
336
  organizationId?: string | undefined;
337
+ createdAt?: unknown;
338
+ updatedAt?: unknown;
339
339
  }, {
340
340
  name: string;
341
341
  description?: string | undefined;
342
- createdAt?: unknown;
343
- updatedAt?: unknown;
344
342
  slug?: string | undefined;
345
343
  ownerId?: string | undefined;
346
344
  organizationId?: string | undefined;
345
+ createdAt?: unknown;
346
+ updatedAt?: unknown;
347
347
  }>;
348
348
  }, {
349
349
  columns: ColumnWithTasks[];
@@ -360,20 +360,20 @@ declare const kanbanBackendPlugin: (hooks?: KanbanBackendHooks) => _btst_stack_p
360
360
  method: "PUT";
361
361
  body: better_call.StandardSchemaV1<{
362
362
  description?: string | undefined;
363
- name?: string | undefined;
364
- createdAt?: unknown;
365
- updatedAt?: unknown;
366
363
  slug?: string | undefined;
367
364
  ownerId?: string | undefined;
368
365
  organizationId?: string | undefined;
369
- }, {
370
- description?: string | undefined;
371
- name?: string | undefined;
372
366
  createdAt?: unknown;
373
367
  updatedAt?: unknown;
368
+ name?: string | undefined;
369
+ }, {
370
+ description?: string | undefined;
374
371
  slug?: string | undefined;
375
372
  ownerId?: string | undefined;
376
373
  organizationId?: string | undefined;
374
+ createdAt?: unknown;
375
+ updatedAt?: unknown;
376
+ name?: string | undefined;
377
377
  }>;
378
378
  }, Board>;
379
379
  readonly deleteBoard: better_call.StrictEndpoint<"/boards/:id", {} & {
@@ -404,14 +404,14 @@ declare const kanbanBackendPlugin: (hooks?: KanbanBackendHooks) => _btst_stack_p
404
404
  title?: string | undefined;
405
405
  createdAt?: unknown;
406
406
  updatedAt?: unknown;
407
- boardId?: string | undefined;
408
407
  order?: number | undefined;
408
+ boardId?: string | undefined;
409
409
  }, {
410
410
  title?: string | undefined;
411
411
  createdAt?: unknown;
412
412
  updatedAt?: unknown;
413
- boardId?: string | undefined;
414
413
  order?: number | undefined;
414
+ boardId?: string | undefined;
415
415
  }>;
416
416
  }, Column>;
417
417
  readonly deleteColumn: better_call.StrictEndpoint<"/columns/:id", {} & {
@@ -35,6 +35,13 @@ interface SerializedTag extends Omit<Tag, "createdAt" | "updatedAt"> {
35
35
  }
36
36
 
37
37
  declare const createPostSchema: z.ZodObject<{
38
+ tags: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodUnion<readonly [z.ZodObject<{
39
+ name: z.ZodString;
40
+ }, z.core.$strip>, z.ZodObject<{
41
+ id: z.ZodString;
42
+ name: z.ZodString;
43
+ slug: z.ZodString;
44
+ }, z.core.$strip>]>>>>;
38
45
  slug: z.ZodOptional<z.ZodString>;
39
46
  published: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
40
47
  publishedAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
@@ -44,13 +51,6 @@ declare const createPostSchema: z.ZodObject<{
44
51
  content: z.ZodString;
45
52
  excerpt: z.ZodString;
46
53
  image: z.ZodOptional<z.ZodString>;
47
- tags: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodUnion<readonly [z.ZodObject<{
48
- name: z.ZodString;
49
- }, z.core.$strip>, z.ZodObject<{
50
- id: z.ZodString;
51
- name: z.ZodString;
52
- slug: z.ZodString;
53
- }, z.core.$strip>]>>>>;
54
54
  }, z.core.$strip>;
55
55
  declare const updatePostSchema: z.ZodObject<{
56
56
  publishedAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;