@stackonward/cms-client 0.0.1

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.
@@ -0,0 +1,311 @@
1
+ import { RoutePolicy } from './routes.js';
2
+ export { BuildRoutePathOptions, ResolvedRouteLocalePolicy, ResolvedRoutePolicy, RouteLocalePolicy, RouteParams, RoutePolicyConfig, RouteResourceDefaults, RouteResources } from './routes.js';
3
+
4
+ /** 作者社交链接 */
5
+ interface AuthorSocialLink {
6
+ kind?: string;
7
+ label: string;
8
+ href: string;
9
+ handle?: string;
10
+ }
11
+ /** 作者资料统计项 */
12
+ interface AuthorProfileStat {
13
+ label: string;
14
+ value: string;
15
+ description?: string;
16
+ }
17
+ /** 作者资料问答项 */
18
+ interface AuthorProfileQuestion {
19
+ label?: string;
20
+ text: string;
21
+ }
22
+ /** 作者资料内容分区 */
23
+ interface AuthorProfileSection {
24
+ key: string;
25
+ eyebrow: string;
26
+ description?: string;
27
+ paragraphs?: string[];
28
+ questions?: AuthorProfileQuestion[];
29
+ }
30
+ /** 作者资料行动入口 */
31
+ interface AuthorProfileCta {
32
+ label: string;
33
+ href: string;
34
+ title?: string;
35
+ description?: string;
36
+ }
37
+ /** 作者资料内容 */
38
+ interface AuthorProfile {
39
+ eyebrow?: string;
40
+ role?: string;
41
+ role_line?: string;
42
+ lede?: string;
43
+ socials?: AuthorSocialLink[];
44
+ stats?: AuthorProfileStat[];
45
+ sections?: AuthorProfileSection[];
46
+ tags?: string[];
47
+ cta?: AuthorProfileCta;
48
+ }
49
+ /** 作者信息 (Go: delivery.DeliveryAuthorResponse) */
50
+ interface Author {
51
+ id: number;
52
+ locale: string;
53
+ display_name: string;
54
+ slug: string;
55
+ avatar_url?: string;
56
+ bio?: string;
57
+ seo_meta?: Partial<SeoMeta>;
58
+ og_meta?: Partial<OgMeta>;
59
+ twitter_meta?: Partial<TwitterMeta>;
60
+ available_locales?: string[];
61
+ localized_slugs?: Record<string, string>;
62
+ profile?: AuthorProfile;
63
+ }
64
+
65
+ /** 多语言字符串 (Go: domain.LocalizedString = map[string]string) */
66
+ type LocalizedString = Record<string, string>;
67
+ /** 分页元信息 (Go: response.PaginationMeta) */
68
+ interface PaginationMeta {
69
+ page: number;
70
+ page_size: number;
71
+ total: number;
72
+ total_pages: number;
73
+ }
74
+ /** 分页响应 (Go: response.Paginated[T]) */
75
+ interface Paginated<T> {
76
+ data: T[];
77
+ pagination: PaginationMeta;
78
+ }
79
+ /** 列表响应 (Go: response.NewList[T]) */
80
+ type ListResponse<T> = Paginated<T>;
81
+ /** 分页查询参数 */
82
+ interface PaginationParams {
83
+ page?: number;
84
+ page_size?: number;
85
+ }
86
+
87
+ /** 文章详情 (Go: delivery.DeliveryArticleResponse) */
88
+ interface ArticleDetail {
89
+ slug: string;
90
+ title: string;
91
+ excerpt?: string;
92
+ content_html: string;
93
+ content_json?: unknown;
94
+ featured_image_url?: string;
95
+ featured_image_alt?: string;
96
+ category?: string;
97
+ tags: string[];
98
+ authors: Author[];
99
+ related_article_ids?: number[];
100
+ comment_count: number;
101
+ is_comment_enabled: boolean;
102
+ published_at: string | null;
103
+ last_published_at: string | null;
104
+ word_count: number;
105
+ reading_time_minutes: number;
106
+ seo_meta?: SeoMeta;
107
+ og_meta?: OgMeta;
108
+ twitter_meta?: TwitterMeta;
109
+ json_ld: unknown;
110
+ locale: string;
111
+ available_locales: string[];
112
+ localized_slugs?: Record<string, string>;
113
+ }
114
+ /** 文章列表项 (Go: delivery.DeliveryArticleListItem) */
115
+ interface ArticleListItem {
116
+ slug: string;
117
+ title: string;
118
+ excerpt?: string;
119
+ featured_image_url?: string;
120
+ featured_image_alt?: string;
121
+ published_at: string | null;
122
+ last_published_at?: string | null;
123
+ reading_time_minutes: number;
124
+ category_slug?: string;
125
+ is_pinned: boolean;
126
+ tags: string[];
127
+ authors: Author[];
128
+ }
129
+ /** 文章列表查询参数 */
130
+ interface ArticleListParams extends PaginationParams {
131
+ locale?: string;
132
+ category_id?: number;
133
+ category_slug?: string;
134
+ tag?: string;
135
+ is_pinned?: boolean;
136
+ sort_by?: string;
137
+ sort_order?: 'asc' | 'desc';
138
+ }
139
+ /** 文章搜索查询参数 */
140
+ interface ArticleSearchParams extends PaginationParams {
141
+ locale?: string;
142
+ q: string;
143
+ }
144
+ /** 文章搜索结果项 */
145
+ interface ArticleSearchResultItem {
146
+ article_id: number;
147
+ title: string;
148
+ excerpt?: string;
149
+ slug: string;
150
+ score: number;
151
+ category_slug?: string;
152
+ featured_image_url?: string;
153
+ featured_image_alt?: string;
154
+ published_at?: string | null;
155
+ last_published_at?: string | null;
156
+ reading_time_minutes?: number;
157
+ tags?: string[];
158
+ authors?: Author[];
159
+ }
160
+ /** SEO 元信息 */
161
+ interface SeoMeta {
162
+ title: string | null;
163
+ description: string | null;
164
+ keywords: string | null;
165
+ robots: string | null;
166
+ canonical_url: string | null;
167
+ }
168
+ /** Open Graph 元信息 */
169
+ interface OgMeta {
170
+ title: string | null;
171
+ description: string | null;
172
+ image: string | null;
173
+ url: string | null;
174
+ type: string | null;
175
+ }
176
+ /** Twitter Card 元信息 */
177
+ interface TwitterMeta {
178
+ card: string | null;
179
+ title: string | null;
180
+ description: string | null;
181
+ image: string | null;
182
+ }
183
+
184
+ /** 本地化 SEO 元信息 */
185
+ type LocalizedSeoMetaMap = Record<string, Partial<SeoMeta>>;
186
+ /** 本地化 Open Graph 元信息 */
187
+ type LocalizedOgMetaMap = Record<string, Partial<OgMeta>>;
188
+ /** 本地化 Twitter Card 元信息 */
189
+ type LocalizedTwitterMetaMap = Record<string, Partial<TwitterMeta>>;
190
+ /** 分类树节点 (Go: delivery.CategoryNodeResponse) */
191
+ interface CategoryNode {
192
+ id: number;
193
+ slug: string;
194
+ name: LocalizedString;
195
+ description: LocalizedString;
196
+ seo_meta?: LocalizedSeoMetaMap;
197
+ og_meta?: LocalizedOgMetaMap;
198
+ twitter_meta?: LocalizedTwitterMetaMap;
199
+ article_count: number;
200
+ article_counts?: Record<string, number>;
201
+ children?: CategoryNode[];
202
+ }
203
+
204
+ /** 评论树节点 (Go: delivery.CommentTreeResponse) */
205
+ interface Comment {
206
+ id: number;
207
+ author_name: string;
208
+ author_url?: string;
209
+ content_html: string;
210
+ is_pinned: boolean;
211
+ like_count: number;
212
+ created_at: string;
213
+ children?: Comment[];
214
+ }
215
+ /** 发表评论请求 */
216
+ interface CreateCommentPayload {
217
+ author_name: string;
218
+ author_email: string;
219
+ content: string;
220
+ parent_id?: number;
221
+ }
222
+
223
+ /** 重定向规则 (Go: delivery.RedirectRuleResponse) */
224
+ type RedirectMatchType = 'exact' | 'prefix' | 'regex';
225
+ interface RedirectRule {
226
+ source_path: string;
227
+ destination_path: string;
228
+ status_code: number;
229
+ match_type: RedirectMatchType;
230
+ priority: number;
231
+ }
232
+
233
+ /** 文章系列 (Go: delivery.DeliverySeriesResponse) */
234
+ interface Series {
235
+ title: string;
236
+ slug: string;
237
+ description: string | null;
238
+ cover_image_url: string | null;
239
+ articles: ArticleListItem[];
240
+ }
241
+
242
+ type SiteDeliveryMode = 'isr' | 'static';
243
+ type SiteDomainType = 'alias' | 'preview' | 'primary';
244
+ interface SiteDomain {
245
+ scheme: 'http' | 'https';
246
+ host: string;
247
+ port?: number;
248
+ base_path: string;
249
+ domain_type: SiteDomainType;
250
+ }
251
+ interface SiteConfig {
252
+ site_code: string;
253
+ product_code: string;
254
+ site_name: string;
255
+ delivery_mode: SiteDeliveryMode;
256
+ canonical_base_url: string;
257
+ domains: SiteDomain[];
258
+ sitemap_path: string;
259
+ route_policy: RoutePolicy;
260
+ updated_at: string;
261
+ }
262
+ interface SiteConfigQuery {
263
+ site_code?: string;
264
+ host?: string;
265
+ }
266
+
267
+ /** Hreflang 条目 (Go: delivery.HreflangEntry) */
268
+ interface HreflangEntry {
269
+ lang: string;
270
+ href: string;
271
+ route_params?: Record<string, string | number | boolean | null | undefined>;
272
+ }
273
+ /** Sitemap 图片条目 */
274
+ interface SitemapImageEntry {
275
+ loc: string;
276
+ title?: string;
277
+ }
278
+ /** Sitemap 视频条目(Google Video Sitemap 扩展) */
279
+ interface SitemapVideoEntry {
280
+ thumbnail_loc: string;
281
+ title: string;
282
+ description: string;
283
+ content_loc?: string;
284
+ player_loc?: string;
285
+ publication_date: string;
286
+ }
287
+ /** Sitemap 条目 (Go: delivery.SitemapEntryResponse) */
288
+ interface SitemapEntry {
289
+ loc: string;
290
+ resource_type?: string;
291
+ route_params?: Record<string, string | number | boolean | null | undefined>;
292
+ last_mod: string;
293
+ change_freq: string;
294
+ priority: number;
295
+ hreflang?: HreflangEntry[];
296
+ images?: SitemapImageEntry[];
297
+ videos?: SitemapVideoEntry[];
298
+ }
299
+
300
+ /** Webhook 事件类型 */
301
+ type WebhookEventType = 'article.published' | 'article.unpublished' | 'article.updated' | 'article.archived' | 'redirect.changed' | 'comment.approved';
302
+ /** ISR 重验证请求体 */
303
+ interface RevalidatePayload {
304
+ event: WebhookEventType;
305
+ slug: string;
306
+ locale: string;
307
+ category_slug?: string;
308
+ }
309
+
310
+ export { RoutePolicy };
311
+ export type { ArticleDetail, ArticleListItem, ArticleListParams, ArticleSearchParams, ArticleSearchResultItem, Author, AuthorProfile, AuthorProfileCta, AuthorProfileQuestion, AuthorProfileSection, AuthorProfileStat, AuthorSocialLink, CategoryNode, Comment, CreateCommentPayload, HreflangEntry, ListResponse, LocalizedOgMetaMap, LocalizedSeoMetaMap, LocalizedString, LocalizedTwitterMetaMap, OgMeta, Paginated, PaginationMeta, PaginationParams, RedirectMatchType, RedirectRule, RevalidatePayload, SeoMeta, Series, SiteConfig, SiteConfigQuery, SiteDeliveryMode, SiteDomain, SiteDomainType, SitemapEntry, SitemapImageEntry, SitemapVideoEntry, TwitterMeta, WebhookEventType };
package/dist/types.mjs ADDED
@@ -0,0 +1 @@
1
+
@@ -0,0 +1,10 @@
1
+ interface VerifyWebhookOptions {
2
+ rawBody: string;
3
+ signature: string;
4
+ secret: string;
5
+ }
6
+ /** 验证 Webhook 签名(HMAC-SHA256) */
7
+ declare function verifyWebhookSignature(options: VerifyWebhookOptions): boolean;
8
+
9
+ export { verifyWebhookSignature };
10
+ export type { VerifyWebhookOptions };
@@ -0,0 +1,10 @@
1
+ interface VerifyWebhookOptions {
2
+ rawBody: string;
3
+ signature: string;
4
+ secret: string;
5
+ }
6
+ /** 验证 Webhook 签名(HMAC-SHA256) */
7
+ declare function verifyWebhookSignature(options: VerifyWebhookOptions): boolean;
8
+
9
+ export { verifyWebhookSignature };
10
+ export type { VerifyWebhookOptions };
@@ -0,0 +1,14 @@
1
+ import { Buffer } from 'node:buffer';
2
+ import { createHmac, timingSafeEqual } from 'node:crypto';
3
+
4
+ function verifyWebhookSignature(options) {
5
+ const { rawBody, signature, secret } = options;
6
+ if (!signature || !rawBody || !secret) return false;
7
+ const expected = `sha256=${createHmac("sha256", secret).update(rawBody).digest("hex")}`;
8
+ const sigBuffer = Buffer.from(signature);
9
+ const expectedBuffer = Buffer.from(expected);
10
+ if (sigBuffer.length !== expectedBuffer.length) return false;
11
+ return timingSafeEqual(sigBuffer, expectedBuffer);
12
+ }
13
+
14
+ export { verifyWebhookSignature };
package/package.json ADDED
@@ -0,0 +1,74 @@
1
+ {
2
+ "name": "@stackonward/cms-client",
3
+ "version": "0.0.1",
4
+ "description": "Framework-agnostic client for structured CMS resources",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "main": "./dist/index.mjs",
8
+ "types": "./dist/index.d.ts",
9
+ "typesVersions": {
10
+ "*": {
11
+ "routes": [
12
+ "./dist/routes.d.ts"
13
+ ],
14
+ "types": [
15
+ "./dist/types.d.ts"
16
+ ],
17
+ "webhook": [
18
+ "./dist/webhook.d.ts"
19
+ ],
20
+ "*": [
21
+ "./dist/index.d.ts"
22
+ ]
23
+ }
24
+ },
25
+ "exports": {
26
+ ".": {
27
+ "types": "./dist/index.d.ts",
28
+ "import": "./dist/index.mjs"
29
+ },
30
+ "./types": {
31
+ "types": "./dist/types.d.ts",
32
+ "import": "./dist/types.mjs"
33
+ },
34
+ "./routes": {
35
+ "types": "./dist/routes.d.ts",
36
+ "import": "./dist/routes.mjs"
37
+ },
38
+ "./webhook": {
39
+ "types": "./dist/webhook.d.ts",
40
+ "import": "./dist/webhook.mjs"
41
+ }
42
+ },
43
+ "files": [
44
+ "dist",
45
+ "CHANGELOG.md"
46
+ ],
47
+ "sideEffects": false,
48
+ "publishConfig": {
49
+ "access": "public"
50
+ },
51
+ "repository": {
52
+ "type": "git",
53
+ "url": "git+https://github.com/1yhy/stackonward.git",
54
+ "directory": "packages/core/cms-client"
55
+ },
56
+ "dependencies": {
57
+ "@stackonward/api-client": "0.0.1"
58
+ },
59
+ "devDependencies": {
60
+ "@antfu/eslint-config": "^4.13.2",
61
+ "@types/node": "^22.0.0",
62
+ "eslint": "^9.22.0",
63
+ "eslint-config-prettier": "^10.1.1",
64
+ "prettier": "^3.5.3",
65
+ "unbuild": "^3.5.0"
66
+ },
67
+ "scripts": {
68
+ "build": "unbuild",
69
+ "typecheck": "tsc --noEmit",
70
+ "lint": "eslint .",
71
+ "lint:fix": "eslint . --fix",
72
+ "format": "prettier --write src/"
73
+ }
74
+ }