@btst/stack 1.2.1 → 1.2.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.
- package/dist/packages/better-stack/src/plugins/blog/api/plugin.cjs +34 -29
- package/dist/packages/better-stack/src/plugins/blog/api/plugin.mjs +34 -29
- package/dist/packages/better-stack/src/plugins/blog/client/plugin.cjs +10 -0
- package/dist/packages/better-stack/src/plugins/blog/client/plugin.mjs +10 -0
- package/package.json +1 -1
- package/src/plugins/blog/api/plugin.ts +36 -56
- package/src/plugins/blog/client/plugin.tsx +12 -0
|
@@ -482,12 +482,15 @@ const blogBackendPlugin = (hooks) => api.defineBackendPlugin({
|
|
|
482
482
|
}
|
|
483
483
|
}
|
|
484
484
|
const date = query.date;
|
|
485
|
-
const
|
|
486
|
-
const WINDOW_SIZE = 100;
|
|
487
|
-
const allPosts = await adapter.findMany({
|
|
485
|
+
const previousPost = await adapter.findMany({
|
|
488
486
|
model: "post",
|
|
489
|
-
limit:
|
|
487
|
+
limit: 1,
|
|
490
488
|
where: [
|
|
489
|
+
{
|
|
490
|
+
field: "createdAt",
|
|
491
|
+
value: date,
|
|
492
|
+
operator: "lt"
|
|
493
|
+
},
|
|
491
494
|
{
|
|
492
495
|
field: "published",
|
|
493
496
|
value: true,
|
|
@@ -499,27 +502,29 @@ const blogBackendPlugin = (hooks) => api.defineBackendPlugin({
|
|
|
499
502
|
direction: "desc"
|
|
500
503
|
}
|
|
501
504
|
});
|
|
502
|
-
const
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
505
|
+
const nextPost = await adapter.findMany({
|
|
506
|
+
model: "post",
|
|
507
|
+
limit: 1,
|
|
508
|
+
where: [
|
|
509
|
+
{
|
|
510
|
+
field: "createdAt",
|
|
511
|
+
value: date,
|
|
512
|
+
operator: "gt"
|
|
513
|
+
},
|
|
514
|
+
{
|
|
515
|
+
field: "published",
|
|
516
|
+
value: true,
|
|
517
|
+
operator: "eq"
|
|
518
|
+
}
|
|
519
|
+
],
|
|
520
|
+
sortBy: {
|
|
521
|
+
field: "createdAt",
|
|
522
|
+
direction: "asc"
|
|
518
523
|
}
|
|
519
|
-
}
|
|
524
|
+
});
|
|
520
525
|
const postIds = [
|
|
521
|
-
...previousPost ? [previousPost.id] : [],
|
|
522
|
-
...nextPost ? [nextPost.id] : []
|
|
526
|
+
...previousPost?.[0] ? [previousPost[0].id] : [],
|
|
527
|
+
...nextPost?.[0] ? [nextPost[0].id] : []
|
|
523
528
|
];
|
|
524
529
|
const postTagsMap = await loadTagsForPosts(
|
|
525
530
|
postIds,
|
|
@@ -527,13 +532,13 @@ const blogBackendPlugin = (hooks) => api.defineBackendPlugin({
|
|
|
527
532
|
postTagCache
|
|
528
533
|
);
|
|
529
534
|
return {
|
|
530
|
-
previous: previousPost ? {
|
|
531
|
-
...previousPost,
|
|
532
|
-
tags: postTagsMap.get(previousPost.id) || []
|
|
535
|
+
previous: previousPost?.[0] ? {
|
|
536
|
+
...previousPost[0],
|
|
537
|
+
tags: postTagsMap.get(previousPost[0].id) || []
|
|
533
538
|
} : null,
|
|
534
|
-
next: nextPost ? {
|
|
535
|
-
...nextPost,
|
|
536
|
-
tags: postTagsMap.get(nextPost.id) || []
|
|
539
|
+
next: nextPost?.[0] ? {
|
|
540
|
+
...nextPost[0],
|
|
541
|
+
tags: postTagsMap.get(nextPost[0].id) || []
|
|
537
542
|
} : null
|
|
538
543
|
};
|
|
539
544
|
} catch (error) {
|
|
@@ -480,12 +480,15 @@ const blogBackendPlugin = (hooks) => defineBackendPlugin({
|
|
|
480
480
|
}
|
|
481
481
|
}
|
|
482
482
|
const date = query.date;
|
|
483
|
-
const
|
|
484
|
-
const WINDOW_SIZE = 100;
|
|
485
|
-
const allPosts = await adapter.findMany({
|
|
483
|
+
const previousPost = await adapter.findMany({
|
|
486
484
|
model: "post",
|
|
487
|
-
limit:
|
|
485
|
+
limit: 1,
|
|
488
486
|
where: [
|
|
487
|
+
{
|
|
488
|
+
field: "createdAt",
|
|
489
|
+
value: date,
|
|
490
|
+
operator: "lt"
|
|
491
|
+
},
|
|
489
492
|
{
|
|
490
493
|
field: "published",
|
|
491
494
|
value: true,
|
|
@@ -497,27 +500,29 @@ const blogBackendPlugin = (hooks) => defineBackendPlugin({
|
|
|
497
500
|
direction: "desc"
|
|
498
501
|
}
|
|
499
502
|
});
|
|
500
|
-
const
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
503
|
+
const nextPost = await adapter.findMany({
|
|
504
|
+
model: "post",
|
|
505
|
+
limit: 1,
|
|
506
|
+
where: [
|
|
507
|
+
{
|
|
508
|
+
field: "createdAt",
|
|
509
|
+
value: date,
|
|
510
|
+
operator: "gt"
|
|
511
|
+
},
|
|
512
|
+
{
|
|
513
|
+
field: "published",
|
|
514
|
+
value: true,
|
|
515
|
+
operator: "eq"
|
|
516
|
+
}
|
|
517
|
+
],
|
|
518
|
+
sortBy: {
|
|
519
|
+
field: "createdAt",
|
|
520
|
+
direction: "asc"
|
|
516
521
|
}
|
|
517
|
-
}
|
|
522
|
+
});
|
|
518
523
|
const postIds = [
|
|
519
|
-
...previousPost ? [previousPost.id] : [],
|
|
520
|
-
...nextPost ? [nextPost.id] : []
|
|
524
|
+
...previousPost?.[0] ? [previousPost[0].id] : [],
|
|
525
|
+
...nextPost?.[0] ? [nextPost[0].id] : []
|
|
521
526
|
];
|
|
522
527
|
const postTagsMap = await loadTagsForPosts(
|
|
523
528
|
postIds,
|
|
@@ -525,13 +530,13 @@ const blogBackendPlugin = (hooks) => defineBackendPlugin({
|
|
|
525
530
|
postTagCache
|
|
526
531
|
);
|
|
527
532
|
return {
|
|
528
|
-
previous: previousPost ? {
|
|
529
|
-
...previousPost,
|
|
530
|
-
tags: postTagsMap.get(previousPost.id) || []
|
|
533
|
+
previous: previousPost?.[0] ? {
|
|
534
|
+
...previousPost[0],
|
|
535
|
+
tags: postTagsMap.get(previousPost[0].id) || []
|
|
531
536
|
} : null,
|
|
532
|
-
next: nextPost ? {
|
|
533
|
-
...nextPost,
|
|
534
|
-
tags: postTagsMap.get(nextPost.id) || []
|
|
537
|
+
next: nextPost?.[0] ? {
|
|
538
|
+
...nextPost[0],
|
|
539
|
+
tags: postTagsMap.get(nextPost[0].id) || []
|
|
535
540
|
} : null
|
|
536
541
|
};
|
|
537
542
|
} catch (error) {
|
|
@@ -477,6 +477,10 @@ const blogClientPlugin = (config) => client.defineClientPlugin({
|
|
|
477
477
|
if (page.length < limit) break;
|
|
478
478
|
offset += limit;
|
|
479
479
|
}
|
|
480
|
+
const tagsRes = await client$1("/tags", {
|
|
481
|
+
method: "GET"
|
|
482
|
+
});
|
|
483
|
+
const tags = tagsRes.data ?? [];
|
|
480
484
|
const getLastModified = (p) => {
|
|
481
485
|
const dates = [p.updatedAt, p.publishedAt, p.createdAt].filter(
|
|
482
486
|
Boolean
|
|
@@ -499,6 +503,12 @@ const blogClientPlugin = (config) => client.defineClientPlugin({
|
|
|
499
503
|
lastModified: getLastModified(p),
|
|
500
504
|
changeFrequency: "monthly",
|
|
501
505
|
priority: 0.6
|
|
506
|
+
})),
|
|
507
|
+
...tags.map((t) => ({
|
|
508
|
+
url: `${origin}/blog/tag/${t.slug}`,
|
|
509
|
+
lastModified: t.updatedAt ? new Date(t.updatedAt) : void 0,
|
|
510
|
+
changeFrequency: "weekly",
|
|
511
|
+
priority: 0.5
|
|
502
512
|
}))
|
|
503
513
|
];
|
|
504
514
|
return entries;
|
|
@@ -475,6 +475,10 @@ const blogClientPlugin = (config) => defineClientPlugin({
|
|
|
475
475
|
if (page.length < limit) break;
|
|
476
476
|
offset += limit;
|
|
477
477
|
}
|
|
478
|
+
const tagsRes = await client("/tags", {
|
|
479
|
+
method: "GET"
|
|
480
|
+
});
|
|
481
|
+
const tags = tagsRes.data ?? [];
|
|
478
482
|
const getLastModified = (p) => {
|
|
479
483
|
const dates = [p.updatedAt, p.publishedAt, p.createdAt].filter(
|
|
480
484
|
Boolean
|
|
@@ -497,6 +501,12 @@ const blogClientPlugin = (config) => defineClientPlugin({
|
|
|
497
501
|
lastModified: getLastModified(p),
|
|
498
502
|
changeFrequency: "monthly",
|
|
499
503
|
priority: 0.6
|
|
504
|
+
})),
|
|
505
|
+
...tags.map((t) => ({
|
|
506
|
+
url: `${origin}/blog/tag/${t.slug}`,
|
|
507
|
+
lastModified: t.updatedAt ? new Date(t.updatedAt) : void 0,
|
|
508
|
+
changeFrequency: "weekly",
|
|
509
|
+
priority: 0.5
|
|
500
510
|
}))
|
|
501
511
|
];
|
|
502
512
|
return entries;
|
package/package.json
CHANGED
|
@@ -720,27 +720,17 @@ export const blogBackendPlugin = (hooks?: BlogBackendHooks) =>
|
|
|
720
720
|
}
|
|
721
721
|
|
|
722
722
|
const date = query.date;
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
// This avoids relying on comparison operators (lt/gt) which may not be
|
|
727
|
-
// consistently implemented across all database adapters (e.g., Drizzle).
|
|
728
|
-
//
|
|
729
|
-
// Strategy:
|
|
730
|
-
// 1. Fetch a window of recent published posts (sorted by date DESC)
|
|
731
|
-
// 2. Filter in memory to find posts immediately before/after target date
|
|
732
|
-
//
|
|
733
|
-
// Trade-offs:
|
|
734
|
-
// - Works reliably across all adapters (only uses eq operator and sorting)
|
|
735
|
-
// - Efficient for typical blog sizes (100 most recent posts)
|
|
736
|
-
// - Limitation: If target post is outside the window, we may not find neighbors
|
|
737
|
-
// (acceptable for typical blog navigation where users browse recent content)
|
|
738
|
-
const WINDOW_SIZE = 100;
|
|
739
|
-
|
|
740
|
-
const allPosts = await adapter.findMany<Post>({
|
|
723
|
+
|
|
724
|
+
// Get previous post (createdAt < date, newest first)
|
|
725
|
+
const previousPost = await adapter.findMany<Post>({
|
|
741
726
|
model: "post",
|
|
742
|
-
limit:
|
|
727
|
+
limit: 1,
|
|
743
728
|
where: [
|
|
729
|
+
{
|
|
730
|
+
field: "createdAt",
|
|
731
|
+
value: date,
|
|
732
|
+
operator: "lt" as const,
|
|
733
|
+
},
|
|
744
734
|
{
|
|
745
735
|
field: "published",
|
|
746
736
|
value: true,
|
|
@@ -753,40 +743,30 @@ export const blogBackendPlugin = (hooks?: BlogBackendHooks) =>
|
|
|
753
743
|
},
|
|
754
744
|
});
|
|
755
745
|
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
746
|
+
const nextPost = await adapter.findMany<Post>({
|
|
747
|
+
model: "post",
|
|
748
|
+
limit: 1,
|
|
749
|
+
where: [
|
|
750
|
+
{
|
|
751
|
+
field: "createdAt",
|
|
752
|
+
value: date,
|
|
753
|
+
operator: "gt" as const,
|
|
754
|
+
},
|
|
755
|
+
{
|
|
756
|
+
field: "published",
|
|
757
|
+
value: true,
|
|
758
|
+
operator: "eq" as const,
|
|
759
|
+
},
|
|
760
|
+
],
|
|
761
|
+
sortBy: {
|
|
762
|
+
field: "createdAt",
|
|
763
|
+
direction: "asc",
|
|
764
|
+
},
|
|
761
765
|
});
|
|
762
766
|
|
|
763
|
-
// Find posts immediately before and after the target date
|
|
764
|
-
// In DESC sorted array: newer posts come before, older posts come after
|
|
765
|
-
let previousPost: Post | null = null;
|
|
766
|
-
let nextPost: Post | null = null;
|
|
767
|
-
|
|
768
|
-
for (let i = 0; i < sortedPosts.length; i++) {
|
|
769
|
-
const post = sortedPosts[i];
|
|
770
|
-
if (!post) continue;
|
|
771
|
-
|
|
772
|
-
const postTime = new Date(post.createdAt).getTime();
|
|
773
|
-
|
|
774
|
-
if (postTime > targetTime) {
|
|
775
|
-
// This post is newer than target - it's a next post candidate
|
|
776
|
-
// Keep the last one we find (closest to target date)
|
|
777
|
-
nextPost = post;
|
|
778
|
-
} else if (postTime < targetTime) {
|
|
779
|
-
// This is the first post older than target - this is the previous post
|
|
780
|
-
previousPost = post;
|
|
781
|
-
// We've found both neighbors, no need to continue
|
|
782
|
-
break;
|
|
783
|
-
}
|
|
784
|
-
// Skip posts with exactly the same timestamp (the current post itself)
|
|
785
|
-
}
|
|
786
|
-
|
|
787
767
|
const postIds = [
|
|
788
|
-
...(previousPost ? [previousPost.id] : []),
|
|
789
|
-
...(nextPost ? [nextPost.id] : []),
|
|
768
|
+
...(previousPost?.[0] ? [previousPost[0].id] : []),
|
|
769
|
+
...(nextPost?.[0] ? [nextPost[0].id] : []),
|
|
790
770
|
];
|
|
791
771
|
const postTagsMap = await loadTagsForPosts(
|
|
792
772
|
postIds,
|
|
@@ -795,16 +775,16 @@ export const blogBackendPlugin = (hooks?: BlogBackendHooks) =>
|
|
|
795
775
|
);
|
|
796
776
|
|
|
797
777
|
return {
|
|
798
|
-
previous: previousPost
|
|
778
|
+
previous: previousPost?.[0]
|
|
799
779
|
? {
|
|
800
|
-
...previousPost,
|
|
801
|
-
tags: postTagsMap.get(previousPost.id) || [],
|
|
780
|
+
...previousPost[0],
|
|
781
|
+
tags: postTagsMap.get(previousPost[0].id) || [],
|
|
802
782
|
}
|
|
803
783
|
: null,
|
|
804
|
-
next: nextPost
|
|
784
|
+
next: nextPost?.[0]
|
|
805
785
|
? {
|
|
806
|
-
...nextPost,
|
|
807
|
-
tags: postTagsMap.get(nextPost.id) || [],
|
|
786
|
+
...nextPost[0],
|
|
787
|
+
tags: postTagsMap.get(nextPost[0].id) || [],
|
|
808
788
|
}
|
|
809
789
|
: null,
|
|
810
790
|
};
|
|
@@ -755,6 +755,12 @@ export const blogClientPlugin = (config: BlogClientConfig) =>
|
|
|
755
755
|
offset += limit;
|
|
756
756
|
}
|
|
757
757
|
|
|
758
|
+
// Fetch all tags
|
|
759
|
+
const tagsRes = await client("/tags", {
|
|
760
|
+
method: "GET",
|
|
761
|
+
});
|
|
762
|
+
const tags = (tagsRes.data ?? []) as unknown as SerializedTag[];
|
|
763
|
+
|
|
758
764
|
const getLastModified = (p: SerializedPost): Date | undefined => {
|
|
759
765
|
const dates = [p.updatedAt, p.publishedAt, p.createdAt].filter(
|
|
760
766
|
Boolean,
|
|
@@ -784,6 +790,12 @@ export const blogClientPlugin = (config: BlogClientConfig) =>
|
|
|
784
790
|
changeFrequency: "monthly" as const,
|
|
785
791
|
priority: 0.6,
|
|
786
792
|
})),
|
|
793
|
+
...tags.map((t) => ({
|
|
794
|
+
url: `${origin}/blog/tag/${t.slug}`,
|
|
795
|
+
lastModified: t.updatedAt ? new Date(t.updatedAt) : undefined,
|
|
796
|
+
changeFrequency: "weekly" as const,
|
|
797
|
+
priority: 0.5,
|
|
798
|
+
})),
|
|
787
799
|
];
|
|
788
800
|
|
|
789
801
|
return entries;
|