@btst/stack 2.1.0 → 2.3.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.
- package/dist/api/index.cjs +9 -1
- package/dist/api/index.d.cts +4 -4
- package/dist/api/index.d.mts +4 -4
- package/dist/api/index.d.ts +4 -4
- package/dist/api/index.mjs +9 -1
- package/dist/client/index.d.cts +2 -2
- package/dist/client/index.d.mts +2 -2
- package/dist/client/index.d.ts +2 -2
- package/dist/index.d.cts +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/packages/stack/src/plugins/ai-chat/api/getters.cjs +42 -0
- package/dist/packages/stack/src/plugins/ai-chat/api/getters.mjs +39 -0
- package/dist/packages/stack/src/plugins/ai-chat/api/plugin.cjs +5 -0
- package/dist/packages/stack/src/plugins/ai-chat/api/plugin.mjs +5 -0
- package/dist/packages/stack/src/plugins/blog/api/getters.cjs +131 -0
- package/dist/packages/stack/src/plugins/blog/api/getters.mjs +127 -0
- package/dist/packages/stack/src/plugins/blog/api/plugin.cjs +60 -107
- package/dist/packages/stack/src/plugins/blog/api/plugin.mjs +60 -107
- package/dist/packages/stack/src/plugins/blog/api/query-key-defs.cjs +18 -0
- package/dist/packages/stack/src/plugins/blog/api/query-key-defs.mjs +15 -0
- package/dist/packages/stack/src/plugins/blog/api/serializers.cjs +21 -0
- package/dist/packages/stack/src/plugins/blog/api/serializers.mjs +18 -0
- package/dist/packages/stack/src/plugins/blog/client/plugin.cjs +16 -1
- package/dist/packages/stack/src/plugins/blog/client/plugin.mjs +17 -2
- package/dist/packages/stack/src/plugins/cms/api/getters.cjs +156 -0
- package/dist/packages/stack/src/plugins/cms/api/getters.mjs +147 -0
- package/dist/packages/stack/src/plugins/cms/api/plugin.cjs +624 -617
- package/dist/packages/stack/src/plugins/cms/api/plugin.mjs +623 -616
- package/dist/packages/stack/src/plugins/cms/api/query-key-defs.cjs +29 -0
- package/dist/packages/stack/src/plugins/cms/api/query-key-defs.mjs +26 -0
- package/dist/packages/stack/src/plugins/cms/client/components/pages/content-editor-page.internal.cjs +1 -1
- package/dist/packages/stack/src/plugins/cms/client/components/pages/content-editor-page.internal.mjs +1 -1
- package/dist/packages/stack/src/plugins/cms/client/hooks/cms-hooks.cjs +6 -3
- package/dist/packages/stack/src/plugins/cms/client/hooks/cms-hooks.mjs +6 -3
- package/dist/packages/stack/src/plugins/cms/client/plugin.cjs +15 -0
- package/dist/packages/stack/src/plugins/cms/client/plugin.mjs +16 -1
- package/dist/packages/stack/src/plugins/form-builder/api/getters.cjs +120 -0
- package/dist/packages/stack/src/plugins/form-builder/api/getters.mjs +112 -0
- package/dist/packages/stack/src/plugins/form-builder/api/plugin.cjs +75 -86
- package/dist/packages/stack/src/plugins/form-builder/api/plugin.mjs +71 -82
- package/dist/packages/stack/src/plugins/form-builder/api/query-key-defs.cjs +37 -0
- package/dist/packages/stack/src/plugins/form-builder/api/query-key-defs.mjs +33 -0
- package/dist/packages/stack/src/plugins/form-builder/client/components/pages/submissions-page.internal.cjs +1 -1
- package/dist/packages/stack/src/plugins/form-builder/client/components/pages/submissions-page.internal.mjs +1 -1
- package/dist/packages/stack/src/plugins/form-builder/client/plugin.cjs +15 -0
- package/dist/packages/stack/src/plugins/form-builder/client/plugin.mjs +16 -1
- package/dist/packages/stack/src/plugins/kanban/api/getters.cjs +84 -0
- package/dist/packages/stack/src/plugins/kanban/api/getters.mjs +81 -0
- package/dist/packages/stack/src/plugins/kanban/api/plugin.cjs +37 -123
- package/dist/packages/stack/src/plugins/kanban/api/plugin.mjs +37 -123
- package/dist/packages/stack/src/plugins/kanban/api/query-key-defs.cjs +26 -0
- package/dist/packages/stack/src/plugins/kanban/api/query-key-defs.mjs +23 -0
- package/dist/packages/stack/src/plugins/kanban/api/serializers.cjs +30 -0
- package/dist/packages/stack/src/plugins/kanban/api/serializers.mjs +26 -0
- package/dist/packages/stack/src/plugins/kanban/client/plugin.cjs +11 -1
- package/dist/packages/stack/src/plugins/kanban/client/plugin.mjs +12 -2
- package/dist/packages/stack/src/plugins/utils.cjs +6 -0
- package/dist/packages/stack/src/plugins/utils.mjs +6 -1
- package/dist/plugins/ai-chat/api/index.cjs +3 -0
- package/dist/plugins/ai-chat/api/index.d.cts +27 -4
- package/dist/plugins/ai-chat/api/index.d.mts +27 -4
- package/dist/plugins/ai-chat/api/index.d.ts +27 -4
- package/dist/plugins/ai-chat/api/index.mjs +1 -0
- package/dist/plugins/ai-chat/client/hooks/index.d.cts +2 -2
- package/dist/plugins/ai-chat/client/hooks/index.d.mts +2 -2
- package/dist/plugins/ai-chat/client/hooks/index.d.ts +2 -2
- package/dist/plugins/ai-chat/query-keys.d.cts +9 -284
- package/dist/plugins/ai-chat/query-keys.d.mts +9 -284
- package/dist/plugins/ai-chat/query-keys.d.ts +9 -284
- package/dist/plugins/api/index.d.cts +4 -3
- package/dist/plugins/api/index.d.mts +4 -3
- package/dist/plugins/api/index.d.ts +4 -3
- package/dist/plugins/blog/api/index.cjs +9 -0
- package/dist/plugins/blog/api/index.d.cts +20 -4
- package/dist/plugins/blog/api/index.d.mts +20 -4
- package/dist/plugins/blog/api/index.d.ts +20 -4
- package/dist/plugins/blog/api/index.mjs +3 -0
- package/dist/plugins/blog/client/hooks/index.d.cts +5 -5
- package/dist/plugins/blog/client/hooks/index.d.mts +5 -5
- package/dist/plugins/blog/client/hooks/index.d.ts +5 -5
- package/dist/plugins/blog/client/index.d.cts +1 -1
- package/dist/plugins/blog/client/index.d.mts +1 -1
- package/dist/plugins/blog/client/index.d.ts +1 -1
- package/dist/plugins/blog/query-keys.cjs +13 -9
- package/dist/plugins/blog/query-keys.d.cts +8 -333
- package/dist/plugins/blog/query-keys.d.mts +8 -333
- package/dist/plugins/blog/query-keys.d.ts +8 -333
- package/dist/plugins/blog/query-keys.mjs +13 -9
- package/dist/plugins/client/index.cjs +1 -0
- package/dist/plugins/client/index.d.cts +10 -3
- package/dist/plugins/client/index.d.mts +10 -3
- package/dist/plugins/client/index.d.ts +10 -3
- package/dist/plugins/client/index.mjs +1 -1
- package/dist/plugins/cms/api/index.cjs +10 -0
- package/dist/plugins/cms/api/index.d.cts +7 -163
- package/dist/plugins/cms/api/index.d.mts +7 -163
- package/dist/plugins/cms/api/index.d.ts +7 -163
- package/dist/plugins/cms/api/index.mjs +2 -0
- package/dist/plugins/cms/client/hooks/index.d.cts +1 -1
- package/dist/plugins/cms/client/hooks/index.d.mts +1 -1
- package/dist/plugins/cms/client/hooks/index.d.ts +1 -1
- package/dist/plugins/cms/query-keys.cjs +2 -1
- package/dist/plugins/cms/query-keys.d.cts +6 -9
- package/dist/plugins/cms/query-keys.d.mts +6 -9
- package/dist/plugins/cms/query-keys.d.ts +6 -9
- package/dist/plugins/cms/query-keys.mjs +2 -1
- package/dist/plugins/form-builder/api/index.cjs +10 -0
- package/dist/plugins/form-builder/api/index.d.cts +7 -141
- package/dist/plugins/form-builder/api/index.d.mts +7 -141
- package/dist/plugins/form-builder/api/index.d.ts +7 -141
- package/dist/plugins/form-builder/api/index.mjs +2 -0
- package/dist/plugins/form-builder/client/components/index.d.cts +1 -1
- package/dist/plugins/form-builder/client/components/index.d.mts +1 -1
- package/dist/plugins/form-builder/client/components/index.d.ts +1 -1
- package/dist/plugins/form-builder/client/hooks/index.d.cts +1 -1
- package/dist/plugins/form-builder/client/hooks/index.d.mts +1 -1
- package/dist/plugins/form-builder/client/hooks/index.d.ts +1 -1
- package/dist/plugins/form-builder/query-keys.cjs +3 -2
- package/dist/plugins/form-builder/query-keys.d.cts +7 -6
- package/dist/plugins/form-builder/query-keys.d.mts +7 -6
- package/dist/plugins/form-builder/query-keys.d.ts +7 -6
- package/dist/plugins/form-builder/query-keys.mjs +3 -2
- package/dist/plugins/kanban/api/index.cjs +9 -0
- package/dist/plugins/kanban/api/index.d.cts +17 -395
- package/dist/plugins/kanban/api/index.d.mts +17 -395
- package/dist/plugins/kanban/api/index.d.ts +17 -395
- package/dist/plugins/kanban/api/index.mjs +3 -0
- package/dist/plugins/kanban/client/components/index.d.cts +1 -1
- package/dist/plugins/kanban/client/components/index.d.mts +1 -1
- package/dist/plugins/kanban/client/components/index.d.ts +1 -1
- package/dist/plugins/kanban/client/hooks/index.d.cts +1 -1
- package/dist/plugins/kanban/client/hooks/index.d.mts +1 -1
- package/dist/plugins/kanban/client/hooks/index.d.ts +1 -1
- package/dist/plugins/kanban/client/index.d.cts +1 -1
- package/dist/plugins/kanban/client/index.d.mts +1 -1
- package/dist/plugins/kanban/client/index.d.ts +1 -1
- package/dist/plugins/kanban/query-keys.cjs +6 -12
- package/dist/plugins/kanban/query-keys.d.cts +5 -16
- package/dist/plugins/kanban/query-keys.d.mts +5 -16
- package/dist/plugins/kanban/query-keys.d.ts +5 -16
- package/dist/plugins/kanban/query-keys.mjs +6 -12
- package/dist/plugins/open-api/api/index.d.cts +2 -2
- package/dist/plugins/open-api/api/index.d.mts +2 -2
- package/dist/plugins/open-api/api/index.d.ts +2 -2
- package/dist/plugins/route-docs/client/index.d.cts +1 -1
- package/dist/plugins/route-docs/client/index.d.mts +1 -1
- package/dist/plugins/route-docs/client/index.d.ts +1 -1
- package/dist/plugins/ui-builder/index.d.cts +1 -1
- package/dist/plugins/ui-builder/index.d.mts +1 -1
- package/dist/plugins/ui-builder/index.d.ts +1 -1
- package/dist/shared/{stack.BoA0xkJv.d.cts → stack.7n9Y_u7N.d.cts} +33 -7
- package/dist/shared/{stack.BoA0xkJv.d.mts → stack.7n9Y_u7N.d.mts} +33 -7
- package/dist/shared/{stack.BoA0xkJv.d.ts → stack.7n9Y_u7N.d.ts} +33 -7
- package/dist/shared/stack.B1EeBt1b.d.ts +297 -0
- package/dist/shared/stack.BIXEI6v_.d.mts +419 -0
- package/dist/shared/stack.BKfolAyK.d.ts +419 -0
- package/dist/shared/stack.BeSm90va.d.ts +289 -0
- package/dist/shared/stack.BpolpQpf.d.cts +445 -0
- package/dist/shared/stack.C5dtIncc.d.mts +293 -0
- package/dist/shared/stack.CIP6QS9l.d.ts +293 -0
- package/dist/shared/stack.CMh_EdxW.d.cts +289 -0
- package/dist/shared/stack.CP68pFEH.d.mts +297 -0
- package/dist/shared/{stack.BsXokfNh.d.mts → stack.CVDTkMoO.d.cts} +8 -2
- package/dist/shared/{stack.BsXokfNh.d.ts → stack.CVDTkMoO.d.mts} +8 -2
- package/dist/shared/{stack.BsXokfNh.d.cts → stack.CVDTkMoO.d.ts} +8 -2
- package/dist/shared/{stack.DKDMI-QO.d.mts → stack.DJaKVY7v.d.cts} +7 -1
- package/dist/shared/{stack.DKDMI-QO.d.ts → stack.DJaKVY7v.d.mts} +7 -1
- package/dist/shared/{stack.DKDMI-QO.d.cts → stack.DJaKVY7v.d.ts} +7 -1
- package/dist/shared/{stack.DzH_wcvr.d.mts → stack.DdI5W6MB.d.cts} +9 -3
- package/dist/shared/{stack.DzH_wcvr.d.ts → stack.DdI5W6MB.d.mts} +9 -3
- package/dist/shared/{stack.DzH_wcvr.d.cts → stack.DdI5W6MB.d.ts} +9 -3
- package/dist/shared/stack.Dg09R0oB.d.mts +289 -0
- package/dist/shared/stack.Dw0Ly2TM.d.cts +293 -0
- package/dist/shared/stack.IdtKDRka.d.cts +297 -0
- package/dist/shared/stack.TIBF2AOx.d.ts +445 -0
- package/dist/shared/stack.rTy7-wQU.d.mts +445 -0
- package/dist/shared/stack.snB1EDP7.d.cts +419 -0
- package/package.json +3 -3
- package/src/__tests__/stack-api.test.ts +118 -0
- package/src/api/index.ts +15 -1
- package/src/plugins/ai-chat/__tests__/getters.test.ts +109 -0
- package/src/plugins/ai-chat/api/getters.ts +71 -0
- package/src/plugins/ai-chat/api/index.ts +1 -0
- package/src/plugins/ai-chat/api/plugin.ts +8 -0
- package/src/plugins/api/index.ts +3 -1
- package/src/plugins/blog/__tests__/getters.test.ts +540 -0
- package/src/plugins/blog/api/getters.ts +243 -0
- package/src/plugins/blog/api/index.ts +9 -0
- package/src/plugins/blog/api/plugin.ts +98 -141
- package/src/plugins/blog/api/query-key-defs.ts +46 -0
- package/src/plugins/blog/api/serializers.ts +27 -0
- package/src/plugins/blog/client/plugin.tsx +21 -1
- package/src/plugins/blog/query-keys.ts +21 -20
- package/src/plugins/client/index.ts +1 -1
- package/src/plugins/cms/__tests__/getters.test.ts +206 -0
- package/src/plugins/cms/api/getters.ts +268 -0
- package/src/plugins/cms/api/index.ts +15 -1
- package/src/plugins/cms/api/plugin.ts +151 -150
- package/src/plugins/cms/api/query-key-defs.ts +53 -0
- package/src/plugins/cms/api/serializers.ts +12 -0
- package/src/plugins/cms/client/components/pages/content-editor-page.internal.tsx +1 -1
- package/src/plugins/cms/client/hooks/cms-hooks.tsx +3 -0
- package/src/plugins/cms/client/plugin.tsx +19 -0
- package/src/plugins/cms/query-keys.ts +2 -1
- package/src/plugins/cms/types.ts +1 -1
- package/src/plugins/form-builder/__tests__/getters.test.ts +159 -0
- package/src/plugins/form-builder/api/getters.ts +226 -0
- package/src/plugins/form-builder/api/index.ts +15 -1
- package/src/plugins/form-builder/api/plugin.ts +107 -109
- package/src/plugins/form-builder/api/query-key-defs.ts +79 -0
- package/src/plugins/form-builder/api/serializers.ts +12 -0
- package/src/plugins/form-builder/client/components/pages/submissions-page.internal.tsx +1 -1
- package/src/plugins/form-builder/client/plugin.tsx +19 -0
- package/src/plugins/form-builder/query-keys.ts +6 -2
- package/src/plugins/form-builder/types.ts +2 -2
- package/src/plugins/kanban/__tests__/getters.test.ts +172 -0
- package/src/plugins/kanban/api/getters.ts +149 -0
- package/src/plugins/kanban/api/index.ts +4 -0
- package/src/plugins/kanban/api/plugin.ts +65 -146
- package/src/plugins/kanban/api/query-key-defs.ts +54 -0
- package/src/plugins/kanban/api/serializers.ts +49 -0
- package/src/plugins/kanban/client/plugin.tsx +15 -1
- package/src/plugins/kanban/query-keys.ts +10 -14
- package/src/plugins/utils.ts +19 -0
- package/src/types.ts +44 -5
- package/dist/shared/{stack.CbuN2zVV.d.cts → stack.CBON0dWL.d.cts} +7 -7
- package/dist/shared/{stack.CbuN2zVV.d.mts → stack.CBON0dWL.d.mts} +7 -7
- package/dist/shared/{stack.CbuN2zVV.d.ts → stack.CBON0dWL.d.ts} +7 -7
|
@@ -0,0 +1,540 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach } from "vitest";
|
|
2
|
+
import { createMemoryAdapter } from "@btst/adapter-memory";
|
|
3
|
+
import { defineDb } from "@btst/db";
|
|
4
|
+
import type { Adapter } from "@btst/db";
|
|
5
|
+
import { blogSchema } from "../db";
|
|
6
|
+
import { getAllPosts, getPostBySlug, getAllTags } from "../api/getters";
|
|
7
|
+
|
|
8
|
+
const createTestAdapter = (): Adapter => {
|
|
9
|
+
const db = defineDb({}).use(blogSchema);
|
|
10
|
+
return createMemoryAdapter(db)({});
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
describe("blog getters", () => {
|
|
14
|
+
let adapter: Adapter;
|
|
15
|
+
|
|
16
|
+
beforeEach(() => {
|
|
17
|
+
adapter = createTestAdapter();
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
describe("getAllPosts", () => {
|
|
21
|
+
it("returns empty result when no posts exist", async () => {
|
|
22
|
+
const result = await getAllPosts(adapter);
|
|
23
|
+
expect(result.items).toEqual([]);
|
|
24
|
+
expect(result.total).toBe(0);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it("returns all posts with empty tags array", async () => {
|
|
28
|
+
await adapter.create({
|
|
29
|
+
model: "post",
|
|
30
|
+
data: {
|
|
31
|
+
title: "Hello World",
|
|
32
|
+
slug: "hello-world",
|
|
33
|
+
content: "Content here",
|
|
34
|
+
excerpt: "Excerpt",
|
|
35
|
+
published: true,
|
|
36
|
+
tags: [],
|
|
37
|
+
createdAt: new Date(),
|
|
38
|
+
updatedAt: new Date(),
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
const result = await getAllPosts(adapter);
|
|
43
|
+
expect(result.items).toHaveLength(1);
|
|
44
|
+
expect(result.total).toBe(1);
|
|
45
|
+
expect(result.items[0]!.slug).toBe("hello-world");
|
|
46
|
+
expect(result.items[0]!.tags).toEqual([]);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it("filters posts by published status", async () => {
|
|
50
|
+
await adapter.create({
|
|
51
|
+
model: "post",
|
|
52
|
+
data: {
|
|
53
|
+
title: "Published Post",
|
|
54
|
+
slug: "published",
|
|
55
|
+
content: "Content",
|
|
56
|
+
excerpt: "",
|
|
57
|
+
published: true,
|
|
58
|
+
tags: [],
|
|
59
|
+
createdAt: new Date(),
|
|
60
|
+
updatedAt: new Date(),
|
|
61
|
+
},
|
|
62
|
+
});
|
|
63
|
+
await adapter.create({
|
|
64
|
+
model: "post",
|
|
65
|
+
data: {
|
|
66
|
+
title: "Draft Post",
|
|
67
|
+
slug: "draft",
|
|
68
|
+
content: "Content",
|
|
69
|
+
excerpt: "",
|
|
70
|
+
published: false,
|
|
71
|
+
tags: [],
|
|
72
|
+
createdAt: new Date(),
|
|
73
|
+
updatedAt: new Date(),
|
|
74
|
+
},
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
const published = await getAllPosts(adapter, { published: true });
|
|
78
|
+
expect(published.items).toHaveLength(1);
|
|
79
|
+
expect(published.total).toBe(1);
|
|
80
|
+
expect(published.items[0]!.slug).toBe("published");
|
|
81
|
+
|
|
82
|
+
const drafts = await getAllPosts(adapter, { published: false });
|
|
83
|
+
expect(drafts.items).toHaveLength(1);
|
|
84
|
+
expect(drafts.total).toBe(1);
|
|
85
|
+
expect(drafts.items[0]!.slug).toBe("draft");
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it("filters posts by slug", async () => {
|
|
89
|
+
await adapter.create({
|
|
90
|
+
model: "post",
|
|
91
|
+
data: {
|
|
92
|
+
title: "Post A",
|
|
93
|
+
slug: "post-a",
|
|
94
|
+
content: "Content",
|
|
95
|
+
excerpt: "",
|
|
96
|
+
published: true,
|
|
97
|
+
tags: [],
|
|
98
|
+
createdAt: new Date(),
|
|
99
|
+
updatedAt: new Date(),
|
|
100
|
+
},
|
|
101
|
+
});
|
|
102
|
+
await adapter.create({
|
|
103
|
+
model: "post",
|
|
104
|
+
data: {
|
|
105
|
+
title: "Post B",
|
|
106
|
+
slug: "post-b",
|
|
107
|
+
content: "Content",
|
|
108
|
+
excerpt: "",
|
|
109
|
+
published: true,
|
|
110
|
+
tags: [],
|
|
111
|
+
createdAt: new Date(),
|
|
112
|
+
updatedAt: new Date(),
|
|
113
|
+
},
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
const result = await getAllPosts(adapter, { slug: "post-a" });
|
|
117
|
+
expect(result.items).toHaveLength(1);
|
|
118
|
+
expect(result.total).toBe(1);
|
|
119
|
+
expect(result.items[0]!.slug).toBe("post-a");
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it("searches posts by query string", async () => {
|
|
123
|
+
await adapter.create({
|
|
124
|
+
model: "post",
|
|
125
|
+
data: {
|
|
126
|
+
title: "TypeScript Tips",
|
|
127
|
+
slug: "ts-tips",
|
|
128
|
+
content: "Using generics",
|
|
129
|
+
excerpt: "",
|
|
130
|
+
published: true,
|
|
131
|
+
tags: [],
|
|
132
|
+
createdAt: new Date(),
|
|
133
|
+
updatedAt: new Date(),
|
|
134
|
+
},
|
|
135
|
+
});
|
|
136
|
+
await adapter.create({
|
|
137
|
+
model: "post",
|
|
138
|
+
data: {
|
|
139
|
+
title: "React Hooks",
|
|
140
|
+
slug: "react-hooks",
|
|
141
|
+
content: "Using hooks",
|
|
142
|
+
excerpt: "",
|
|
143
|
+
published: true,
|
|
144
|
+
tags: [],
|
|
145
|
+
createdAt: new Date(),
|
|
146
|
+
updatedAt: new Date(),
|
|
147
|
+
},
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
const result = await getAllPosts(adapter, { query: "typescript" });
|
|
151
|
+
expect(result.items).toHaveLength(1);
|
|
152
|
+
expect(result.total).toBe(1);
|
|
153
|
+
expect(result.items[0]!.slug).toBe("ts-tips");
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it("respects limit and offset", async () => {
|
|
157
|
+
for (let i = 1; i <= 5; i++) {
|
|
158
|
+
await adapter.create({
|
|
159
|
+
model: "post",
|
|
160
|
+
data: {
|
|
161
|
+
title: `Post ${i}`,
|
|
162
|
+
slug: `post-${i}`,
|
|
163
|
+
content: "Content",
|
|
164
|
+
excerpt: "",
|
|
165
|
+
published: true,
|
|
166
|
+
tags: [],
|
|
167
|
+
createdAt: new Date(Date.now() + i * 1000),
|
|
168
|
+
updatedAt: new Date(),
|
|
169
|
+
},
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const page1 = await getAllPosts(adapter, { limit: 2, offset: 0 });
|
|
174
|
+
expect(page1.items).toHaveLength(2);
|
|
175
|
+
expect(page1.total).toBe(5);
|
|
176
|
+
|
|
177
|
+
const page2 = await getAllPosts(adapter, { limit: 2, offset: 2 });
|
|
178
|
+
expect(page2.items).toHaveLength(2);
|
|
179
|
+
expect(page2.total).toBe(5);
|
|
180
|
+
|
|
181
|
+
// Pages should be different posts
|
|
182
|
+
expect(page1.items[0]!.slug).not.toBe(page2.items[0]!.slug);
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
it("attaches tags to posts", async () => {
|
|
186
|
+
const post = await adapter.create({
|
|
187
|
+
model: "post",
|
|
188
|
+
data: {
|
|
189
|
+
title: "Tagged Post",
|
|
190
|
+
slug: "tagged",
|
|
191
|
+
content: "Content",
|
|
192
|
+
excerpt: "",
|
|
193
|
+
published: true,
|
|
194
|
+
tags: [],
|
|
195
|
+
createdAt: new Date(),
|
|
196
|
+
updatedAt: new Date(),
|
|
197
|
+
},
|
|
198
|
+
});
|
|
199
|
+
const tag = await adapter.create({
|
|
200
|
+
model: "tag",
|
|
201
|
+
data: {
|
|
202
|
+
name: "JavaScript",
|
|
203
|
+
slug: "javascript",
|
|
204
|
+
createdAt: new Date(),
|
|
205
|
+
updatedAt: new Date(),
|
|
206
|
+
},
|
|
207
|
+
});
|
|
208
|
+
await adapter.create({
|
|
209
|
+
model: "postTag",
|
|
210
|
+
data: { postId: (post as any).id, tagId: (tag as any).id },
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
const result = await getAllPosts(adapter);
|
|
214
|
+
expect(result.items[0]!.tags).toHaveLength(1);
|
|
215
|
+
expect(result.items[0]!.tags[0]!.slug).toBe("javascript");
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
it("filters posts by tagSlug and returns empty for missing tag", async () => {
|
|
219
|
+
const result = await getAllPosts(adapter, { tagSlug: "nonexistent" });
|
|
220
|
+
expect(result.items).toEqual([]);
|
|
221
|
+
expect(result.total).toBe(0);
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
it("filters posts by tagSlug - returns only tagged posts", async () => {
|
|
225
|
+
const taggedPost = await adapter.create({
|
|
226
|
+
model: "post",
|
|
227
|
+
data: {
|
|
228
|
+
title: "Tagged Post",
|
|
229
|
+
slug: "tagged-post",
|
|
230
|
+
content: "Content",
|
|
231
|
+
excerpt: "",
|
|
232
|
+
published: true,
|
|
233
|
+
tags: [],
|
|
234
|
+
createdAt: new Date(),
|
|
235
|
+
updatedAt: new Date(),
|
|
236
|
+
},
|
|
237
|
+
});
|
|
238
|
+
for (let i = 1; i <= 3; i++) {
|
|
239
|
+
await adapter.create({
|
|
240
|
+
model: "post",
|
|
241
|
+
data: {
|
|
242
|
+
title: `Untagged Post ${i}`,
|
|
243
|
+
slug: `untagged-${i}`,
|
|
244
|
+
content: "Content",
|
|
245
|
+
excerpt: "",
|
|
246
|
+
published: true,
|
|
247
|
+
tags: [],
|
|
248
|
+
createdAt: new Date(),
|
|
249
|
+
updatedAt: new Date(),
|
|
250
|
+
},
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
const tag = await adapter.create({
|
|
254
|
+
model: "tag",
|
|
255
|
+
data: {
|
|
256
|
+
name: "TypeScript",
|
|
257
|
+
slug: "typescript",
|
|
258
|
+
createdAt: new Date(),
|
|
259
|
+
updatedAt: new Date(),
|
|
260
|
+
},
|
|
261
|
+
});
|
|
262
|
+
await adapter.create({
|
|
263
|
+
model: "postTag",
|
|
264
|
+
data: { postId: (taggedPost as any).id, tagId: (tag as any).id },
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
const result = await getAllPosts(adapter, { tagSlug: "typescript" });
|
|
268
|
+
expect(result.items).toHaveLength(1);
|
|
269
|
+
expect(result.total).toBe(1);
|
|
270
|
+
expect(result.items[0]!.slug).toBe("tagged-post");
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
it("paginates tagSlug results at the DB level - limit/offset respected", async () => {
|
|
274
|
+
// Create a tag and 5 posts tagged with it, plus 10 untagged posts.
|
|
275
|
+
// With DB-level filtering the findMany should only receive 5 rows,
|
|
276
|
+
// never loading the 10 untagged posts into memory.
|
|
277
|
+
const tag = await adapter.create({
|
|
278
|
+
model: "tag",
|
|
279
|
+
data: {
|
|
280
|
+
name: "JS",
|
|
281
|
+
slug: "js",
|
|
282
|
+
createdAt: new Date(),
|
|
283
|
+
updatedAt: new Date(),
|
|
284
|
+
},
|
|
285
|
+
});
|
|
286
|
+
for (let i = 1; i <= 5; i++) {
|
|
287
|
+
const post = await adapter.create({
|
|
288
|
+
model: "post",
|
|
289
|
+
data: {
|
|
290
|
+
title: `JS Post ${i}`,
|
|
291
|
+
slug: `js-post-${i}`,
|
|
292
|
+
content: "Content",
|
|
293
|
+
excerpt: "",
|
|
294
|
+
published: true,
|
|
295
|
+
tags: [],
|
|
296
|
+
createdAt: new Date(Date.now() + i * 1000),
|
|
297
|
+
updatedAt: new Date(),
|
|
298
|
+
},
|
|
299
|
+
});
|
|
300
|
+
await adapter.create({
|
|
301
|
+
model: "postTag",
|
|
302
|
+
data: { postId: (post as any).id, tagId: (tag as any).id },
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
for (let i = 1; i <= 10; i++) {
|
|
306
|
+
await adapter.create({
|
|
307
|
+
model: "post",
|
|
308
|
+
data: {
|
|
309
|
+
title: `Noise Post ${i}`,
|
|
310
|
+
slug: `noise-${i}`,
|
|
311
|
+
content: "Content",
|
|
312
|
+
excerpt: "",
|
|
313
|
+
published: true,
|
|
314
|
+
tags: [],
|
|
315
|
+
createdAt: new Date(),
|
|
316
|
+
updatedAt: new Date(),
|
|
317
|
+
},
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
const page1 = await getAllPosts(adapter, {
|
|
322
|
+
tagSlug: "js",
|
|
323
|
+
limit: 2,
|
|
324
|
+
offset: 0,
|
|
325
|
+
});
|
|
326
|
+
expect(page1.items).toHaveLength(2);
|
|
327
|
+
expect(page1.total).toBe(5);
|
|
328
|
+
expect(page1.items.every((p) => p.slug.startsWith("js-post"))).toBe(true);
|
|
329
|
+
|
|
330
|
+
const page2 = await getAllPosts(adapter, {
|
|
331
|
+
tagSlug: "js",
|
|
332
|
+
limit: 2,
|
|
333
|
+
offset: 2,
|
|
334
|
+
});
|
|
335
|
+
expect(page2.items).toHaveLength(2);
|
|
336
|
+
expect(page2.total).toBe(5);
|
|
337
|
+
|
|
338
|
+
const page3 = await getAllPosts(adapter, {
|
|
339
|
+
tagSlug: "js",
|
|
340
|
+
limit: 2,
|
|
341
|
+
offset: 4,
|
|
342
|
+
});
|
|
343
|
+
expect(page3.items).toHaveLength(1);
|
|
344
|
+
expect(page3.total).toBe(5);
|
|
345
|
+
|
|
346
|
+
// Pages must be disjoint
|
|
347
|
+
const allSlugs = [...page1.items, ...page2.items, ...page3.items].map(
|
|
348
|
+
(p) => p.slug,
|
|
349
|
+
);
|
|
350
|
+
expect(new Set(allSlugs).size).toBe(5);
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
it("tagSlug combined with published filter only returns published tagged posts", async () => {
|
|
354
|
+
const tag = await adapter.create({
|
|
355
|
+
model: "tag",
|
|
356
|
+
data: {
|
|
357
|
+
name: "CSS",
|
|
358
|
+
slug: "css",
|
|
359
|
+
createdAt: new Date(),
|
|
360
|
+
updatedAt: new Date(),
|
|
361
|
+
},
|
|
362
|
+
});
|
|
363
|
+
const published = await adapter.create({
|
|
364
|
+
model: "post",
|
|
365
|
+
data: {
|
|
366
|
+
title: "Published CSS Post",
|
|
367
|
+
slug: "pub-css",
|
|
368
|
+
content: "Content",
|
|
369
|
+
excerpt: "",
|
|
370
|
+
published: true,
|
|
371
|
+
tags: [],
|
|
372
|
+
createdAt: new Date(),
|
|
373
|
+
updatedAt: new Date(),
|
|
374
|
+
},
|
|
375
|
+
});
|
|
376
|
+
const draft = await adapter.create({
|
|
377
|
+
model: "post",
|
|
378
|
+
data: {
|
|
379
|
+
title: "Draft CSS Post",
|
|
380
|
+
slug: "draft-css",
|
|
381
|
+
content: "Content",
|
|
382
|
+
excerpt: "",
|
|
383
|
+
published: false,
|
|
384
|
+
tags: [],
|
|
385
|
+
createdAt: new Date(),
|
|
386
|
+
updatedAt: new Date(),
|
|
387
|
+
},
|
|
388
|
+
});
|
|
389
|
+
await adapter.create({
|
|
390
|
+
model: "postTag",
|
|
391
|
+
data: { postId: (published as any).id, tagId: (tag as any).id },
|
|
392
|
+
});
|
|
393
|
+
await adapter.create({
|
|
394
|
+
model: "postTag",
|
|
395
|
+
data: { postId: (draft as any).id, tagId: (tag as any).id },
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
const result = await getAllPosts(adapter, {
|
|
399
|
+
tagSlug: "css",
|
|
400
|
+
published: true,
|
|
401
|
+
});
|
|
402
|
+
expect(result.items).toHaveLength(1);
|
|
403
|
+
expect(result.total).toBe(1);
|
|
404
|
+
expect(result.items[0]!.slug).toBe("pub-css");
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
it("returns all posts when no limit is specified (DB path - more than 10)", async () => {
|
|
408
|
+
for (let i = 1; i <= 15; i++) {
|
|
409
|
+
await adapter.create({
|
|
410
|
+
model: "post",
|
|
411
|
+
data: {
|
|
412
|
+
title: `Post ${i}`,
|
|
413
|
+
slug: `post-${i}`,
|
|
414
|
+
content: "Content",
|
|
415
|
+
excerpt: "",
|
|
416
|
+
published: true,
|
|
417
|
+
tags: [],
|
|
418
|
+
createdAt: new Date(Date.now() + i * 1000),
|
|
419
|
+
updatedAt: new Date(),
|
|
420
|
+
},
|
|
421
|
+
});
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
const result = await getAllPosts(adapter);
|
|
425
|
+
expect(result.items).toHaveLength(15);
|
|
426
|
+
expect(result.total).toBe(15);
|
|
427
|
+
expect(result.limit).toBeUndefined();
|
|
428
|
+
});
|
|
429
|
+
|
|
430
|
+
it("returns all matching posts when no limit is specified (in-memory query path - more than 10)", async () => {
|
|
431
|
+
for (let i = 1; i <= 15; i++) {
|
|
432
|
+
await adapter.create({
|
|
433
|
+
model: "post",
|
|
434
|
+
data: {
|
|
435
|
+
title: `TypeScript Post ${i}`,
|
|
436
|
+
slug: `ts-post-${i}`,
|
|
437
|
+
content: "TypeScript content",
|
|
438
|
+
excerpt: "",
|
|
439
|
+
published: true,
|
|
440
|
+
tags: [],
|
|
441
|
+
createdAt: new Date(Date.now() + i * 1000),
|
|
442
|
+
updatedAt: new Date(),
|
|
443
|
+
},
|
|
444
|
+
});
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
const result = await getAllPosts(adapter, { query: "TypeScript" });
|
|
448
|
+
expect(result.items).toHaveLength(15);
|
|
449
|
+
expect(result.total).toBe(15);
|
|
450
|
+
expect(result.limit).toBeUndefined();
|
|
451
|
+
});
|
|
452
|
+
|
|
453
|
+
it("total reflects count before pagination slice for in-memory filters", async () => {
|
|
454
|
+
for (let i = 1; i <= 4; i++) {
|
|
455
|
+
await adapter.create({
|
|
456
|
+
model: "post",
|
|
457
|
+
data: {
|
|
458
|
+
title: `TypeScript Post ${i}`,
|
|
459
|
+
slug: `ts-post-${i}`,
|
|
460
|
+
content: "TypeScript content",
|
|
461
|
+
excerpt: "",
|
|
462
|
+
published: true,
|
|
463
|
+
tags: [],
|
|
464
|
+
createdAt: new Date(Date.now() + i * 1000),
|
|
465
|
+
updatedAt: new Date(),
|
|
466
|
+
},
|
|
467
|
+
});
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
const result = await getAllPosts(adapter, {
|
|
471
|
+
query: "TypeScript",
|
|
472
|
+
limit: 2,
|
|
473
|
+
offset: 0,
|
|
474
|
+
});
|
|
475
|
+
expect(result.items).toHaveLength(2);
|
|
476
|
+
expect(result.total).toBe(4);
|
|
477
|
+
});
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
describe("getPostBySlug", () => {
|
|
481
|
+
it("returns null when post does not exist", async () => {
|
|
482
|
+
const post = await getPostBySlug(adapter, "nonexistent");
|
|
483
|
+
expect(post).toBeNull();
|
|
484
|
+
});
|
|
485
|
+
|
|
486
|
+
it("returns the post when it exists", async () => {
|
|
487
|
+
await adapter.create({
|
|
488
|
+
model: "post",
|
|
489
|
+
data: {
|
|
490
|
+
title: "My Post",
|
|
491
|
+
slug: "my-post",
|
|
492
|
+
content: "Content",
|
|
493
|
+
excerpt: "",
|
|
494
|
+
published: true,
|
|
495
|
+
tags: [],
|
|
496
|
+
createdAt: new Date(),
|
|
497
|
+
updatedAt: new Date(),
|
|
498
|
+
},
|
|
499
|
+
});
|
|
500
|
+
|
|
501
|
+
const post = await getPostBySlug(adapter, "my-post");
|
|
502
|
+
expect(post).not.toBeNull();
|
|
503
|
+
expect(post!.slug).toBe("my-post");
|
|
504
|
+
expect(post!.title).toBe("My Post");
|
|
505
|
+
});
|
|
506
|
+
});
|
|
507
|
+
|
|
508
|
+
describe("getAllTags", () => {
|
|
509
|
+
it("returns empty array when no tags exist", async () => {
|
|
510
|
+
const tags = await getAllTags(adapter);
|
|
511
|
+
expect(tags).toEqual([]);
|
|
512
|
+
});
|
|
513
|
+
|
|
514
|
+
it("returns all tags sorted alphabetically by name", async () => {
|
|
515
|
+
await adapter.create({
|
|
516
|
+
model: "tag",
|
|
517
|
+
data: {
|
|
518
|
+
name: "TypeScript",
|
|
519
|
+
slug: "typescript",
|
|
520
|
+
createdAt: new Date(),
|
|
521
|
+
updatedAt: new Date(),
|
|
522
|
+
},
|
|
523
|
+
});
|
|
524
|
+
await adapter.create({
|
|
525
|
+
model: "tag",
|
|
526
|
+
data: {
|
|
527
|
+
name: "React",
|
|
528
|
+
slug: "react",
|
|
529
|
+
createdAt: new Date(),
|
|
530
|
+
updatedAt: new Date(),
|
|
531
|
+
},
|
|
532
|
+
});
|
|
533
|
+
|
|
534
|
+
const tags = await getAllTags(adapter);
|
|
535
|
+
expect(tags).toHaveLength(2);
|
|
536
|
+
expect(tags[0]!.name).toBe("React");
|
|
537
|
+
expect(tags[1]!.name).toBe("TypeScript");
|
|
538
|
+
});
|
|
539
|
+
});
|
|
540
|
+
});
|