@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.
package/CHANGELOG.md ADDED
@@ -0,0 +1,9 @@
1
+ # Changelog
2
+
3
+ ## 0.0.1
4
+
5
+ - Established the framework-neutral typed CMS delivery client.
6
+ - Added article, author, category, search, site configuration, redirect, preview,
7
+ and sitemap contracts.
8
+ - Added configurable request headers and error normalization without embedding a
9
+ product or platform protocol.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024-present yhy
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,62 @@
1
+ # @stackonward/cms-client
2
+
3
+ Framework-agnostic typed client for a CMS delivery API. It provides article,
4
+ author, category, comment, sitemap, redirect, series, site-configuration, and
5
+ webhook-signature contracts while leaving backend-specific headers and error
6
+ envelopes to adapters.
7
+
8
+ ## Install
9
+
10
+ ```bash
11
+ pnpm add @stackonward/cms-client
12
+ ```
13
+
14
+ ## Usage
15
+
16
+ ```typescript
17
+ import { createCmsClient } from '@stackonward/cms-client'
18
+
19
+ const cms = createCmsClient({
20
+ baseUrl: 'https://cms.example.com/api',
21
+ defaultLocale: 'en',
22
+ timeout: 10_000,
23
+ headers: { 'X-Customer-Scope': 'customer_alpha' },
24
+ })
25
+
26
+ const article = await cms.getArticle('getting-started')
27
+ const sitemap = await cms.getSitemap('en')
28
+ ```
29
+
30
+ ## Configuration
31
+
32
+ | Option | Required | Description |
33
+ | ----------------- | -------- | ---------------------------------------------------- |
34
+ | `baseUrl` | Yes | CMS delivery API base URL |
35
+ | `defaultLocale` | No | Locale added to eligible reads; defaults to `en` |
36
+ | `fetch` | No | Fetch-compatible transport implementation |
37
+ | `timeout` | No | Request timeout in milliseconds; defaults to `10000` |
38
+ | `headers` | No | Static or request-aware header provider |
39
+ | `errorNormalizer` | No | Backend error-envelope adapter |
40
+
41
+ The generic client does not know product codes, preview-header ownership, or a
42
+ specific backend error envelope. OneX consumers use
43
+ `@stackonward/onex-cms-client` to bind those contracts explicitly.
44
+
45
+ ## Entry points
46
+
47
+ | Entry point | Content |
48
+ | --------------------------------- | ---------------------------------------- |
49
+ | `@stackonward/cms-client` | Client, routes, errors, and public types |
50
+ | `@stackonward/cms-client/types` | CMS request and response types |
51
+ | `@stackonward/cms-client/routes` | Resource route policy helpers |
52
+ | `@stackonward/cms-client/webhook` | HMAC-SHA256 webhook verification |
53
+
54
+ ## Compatibility
55
+
56
+ - Fetch-compatible ESM runtime
57
+ - Node.js 18+ for webhook verification
58
+ - TypeScript declarations included
59
+
60
+ ## License
61
+
62
+ [MIT](./LICENSE)
@@ -0,0 +1,86 @@
1
+ import { ArticleListParams, Paginated, ArticleListItem, ArticleDetail, ArticleSearchParams, ArticleSearchResultItem, PaginationParams, Author, CategoryNode, Comment, CreateCommentPayload, SitemapEntry, RedirectRule, SiteConfigQuery, SiteConfig, Series } from './types.mjs';
2
+ export { AuthorProfile, AuthorProfileCta, AuthorProfileQuestion, AuthorProfileSection, AuthorProfileStat, AuthorSocialLink, HreflangEntry, ListResponse, LocalizedOgMetaMap, LocalizedSeoMetaMap, LocalizedString, LocalizedTwitterMetaMap, OgMeta, PaginationMeta, RedirectMatchType, RevalidatePayload, SeoMeta, SiteDeliveryMode, SiteDomain, SiteDomainType, SitemapImageEntry, SitemapVideoEntry, TwitterMeta, WebhookEventType } from './types.mjs';
3
+ import { HeaderProvider, ErrorNormalizer, NormalizedApiError } from '@stackonward/api-client';
4
+ export { BuildRoutePathOptions, BuildRouteUrlOptions, DEFAULT_ROUTE_RESOURCES, ResolvedRouteLocalePolicy, ResolvedRoutePolicy, RouteLocalePolicy, RouteParams, RoutePolicy, RoutePolicyConfig, RouteResourceDefaults, RouteResources, buildRoutePath, buildRouteUrl, extractRouteParams, extractSourceRouteParams, formatRoutePolicy, hasCustomArticleRoute, isRoutePolicy, mergeRouteParams, resolveRoutePolicy } from './routes.mjs';
5
+
6
+ /** CMS 客户端配置 */
7
+ interface CmsClientConfig {
8
+ /** CMS API 基础 URL */
9
+ baseUrl: string;
10
+ /** 默认语言 */
11
+ defaultLocale?: string;
12
+ /** 自定义 fetch 实现 */
13
+ fetch?: typeof globalThis.fetch;
14
+ /** 请求超时(毫秒),默认 10000 */
15
+ timeout?: number;
16
+ /** 请求头提供器 */
17
+ headers?: HeaderProvider;
18
+ /** 后端错误格式适配器 */
19
+ errorNormalizer?: ErrorNormalizer;
20
+ }
21
+
22
+ /** CMS Public API 客户端 */
23
+ declare class CmsClient {
24
+ private readonly config;
25
+ private readonly api;
26
+ constructor(config: CmsClientConfig);
27
+ /** 获取文章分页列表 */
28
+ getArticles(params?: ArticleListParams): Promise<Paginated<ArticleListItem>>;
29
+ /** 获取文章详情 */
30
+ getArticle(slug: string, locale?: string): Promise<ArticleDetail>;
31
+ /** 获取相关文章 */
32
+ getRelatedArticles(slug: string, options?: {
33
+ limit?: number;
34
+ locale?: string;
35
+ }): Promise<ArticleListItem[]>;
36
+ /** 搜索已发布文章 */
37
+ searchArticles(params: ArticleSearchParams): Promise<Paginated<ArticleSearchResultItem>>;
38
+ /** 获取作者分页列表 */
39
+ getAuthorsPage(options?: {
40
+ locale?: string;
41
+ } & PaginationParams): Promise<Paginated<Author>>;
42
+ /** 获取所有作者 */
43
+ getAuthors(options?: {
44
+ locale?: string;
45
+ } & PaginationParams): Promise<Author[]>;
46
+ /** 获取作者详情 */
47
+ getAuthor(slug: string, locale?: string): Promise<Author>;
48
+ /** 获取作者的文章分页列表 */
49
+ getAuthorArticlesPage(slug: string, options?: {
50
+ is_pinned?: boolean;
51
+ locale?: string;
52
+ } & PaginationParams): Promise<Paginated<ArticleListItem>>;
53
+ /** 获取作者的文章列表 */
54
+ getAuthorArticles(slug: string, options?: {
55
+ is_pinned?: boolean;
56
+ locale?: string;
57
+ } & PaginationParams): Promise<ArticleListItem[]>;
58
+ /** 获取分类树 */
59
+ getCategories(): Promise<CategoryNode[]>;
60
+ /** 获取文章评论树 */
61
+ getComments(articleSlug: string): Promise<Comment[]>;
62
+ /** 发表评论 */
63
+ createComment(articleSlug: string, payload: CreateCommentPayload): Promise<void>;
64
+ /** 获取 Sitemap 条目 */
65
+ getSitemap(locale?: string): Promise<SitemapEntry[]>;
66
+ /** 获取重定向规则 */
67
+ getRedirects(): Promise<RedirectRule[]>;
68
+ /** 获取站点运行时配置 */
69
+ getSiteConfig(options?: SiteConfigQuery): Promise<SiteConfig>;
70
+ /** 获取文章系列 */
71
+ getSeries(slug: string, locale?: string): Promise<Series>;
72
+ private request;
73
+ }
74
+ /** 创建 CMS 客户端实例 */
75
+ declare function createCmsClient(config: CmsClientConfig): CmsClient;
76
+
77
+ /** CMS API 错误(NormalizedApiError 子类,可被 api-client 的 normalizer 直接抛出) */
78
+ declare class CmsApiError extends NormalizedApiError {
79
+ readonly errorCode: string;
80
+ constructor(statusCode: number, errorCode: string, message: string, body?: unknown);
81
+ get isNotFound(): boolean;
82
+ get isUnauthorized(): boolean;
83
+ }
84
+
85
+ export { ArticleDetail, ArticleListItem, ArticleListParams, ArticleSearchParams, ArticleSearchResultItem, Author, CategoryNode, CmsApiError, CmsClient, Comment, CreateCommentPayload, Paginated, PaginationParams, RedirectRule, Series, SiteConfig, SiteConfigQuery, SitemapEntry, createCmsClient };
86
+ export type { CmsClientConfig };
@@ -0,0 +1,86 @@
1
+ import { ArticleListParams, Paginated, ArticleListItem, ArticleDetail, ArticleSearchParams, ArticleSearchResultItem, PaginationParams, Author, CategoryNode, Comment, CreateCommentPayload, SitemapEntry, RedirectRule, SiteConfigQuery, SiteConfig, Series } from './types.js';
2
+ export { AuthorProfile, AuthorProfileCta, AuthorProfileQuestion, AuthorProfileSection, AuthorProfileStat, AuthorSocialLink, HreflangEntry, ListResponse, LocalizedOgMetaMap, LocalizedSeoMetaMap, LocalizedString, LocalizedTwitterMetaMap, OgMeta, PaginationMeta, RedirectMatchType, RevalidatePayload, SeoMeta, SiteDeliveryMode, SiteDomain, SiteDomainType, SitemapImageEntry, SitemapVideoEntry, TwitterMeta, WebhookEventType } from './types.js';
3
+ import { HeaderProvider, ErrorNormalizer, NormalizedApiError } from '@stackonward/api-client';
4
+ export { BuildRoutePathOptions, BuildRouteUrlOptions, DEFAULT_ROUTE_RESOURCES, ResolvedRouteLocalePolicy, ResolvedRoutePolicy, RouteLocalePolicy, RouteParams, RoutePolicy, RoutePolicyConfig, RouteResourceDefaults, RouteResources, buildRoutePath, buildRouteUrl, extractRouteParams, extractSourceRouteParams, formatRoutePolicy, hasCustomArticleRoute, isRoutePolicy, mergeRouteParams, resolveRoutePolicy } from './routes.js';
5
+
6
+ /** CMS 客户端配置 */
7
+ interface CmsClientConfig {
8
+ /** CMS API 基础 URL */
9
+ baseUrl: string;
10
+ /** 默认语言 */
11
+ defaultLocale?: string;
12
+ /** 自定义 fetch 实现 */
13
+ fetch?: typeof globalThis.fetch;
14
+ /** 请求超时(毫秒),默认 10000 */
15
+ timeout?: number;
16
+ /** 请求头提供器 */
17
+ headers?: HeaderProvider;
18
+ /** 后端错误格式适配器 */
19
+ errorNormalizer?: ErrorNormalizer;
20
+ }
21
+
22
+ /** CMS Public API 客户端 */
23
+ declare class CmsClient {
24
+ private readonly config;
25
+ private readonly api;
26
+ constructor(config: CmsClientConfig);
27
+ /** 获取文章分页列表 */
28
+ getArticles(params?: ArticleListParams): Promise<Paginated<ArticleListItem>>;
29
+ /** 获取文章详情 */
30
+ getArticle(slug: string, locale?: string): Promise<ArticleDetail>;
31
+ /** 获取相关文章 */
32
+ getRelatedArticles(slug: string, options?: {
33
+ limit?: number;
34
+ locale?: string;
35
+ }): Promise<ArticleListItem[]>;
36
+ /** 搜索已发布文章 */
37
+ searchArticles(params: ArticleSearchParams): Promise<Paginated<ArticleSearchResultItem>>;
38
+ /** 获取作者分页列表 */
39
+ getAuthorsPage(options?: {
40
+ locale?: string;
41
+ } & PaginationParams): Promise<Paginated<Author>>;
42
+ /** 获取所有作者 */
43
+ getAuthors(options?: {
44
+ locale?: string;
45
+ } & PaginationParams): Promise<Author[]>;
46
+ /** 获取作者详情 */
47
+ getAuthor(slug: string, locale?: string): Promise<Author>;
48
+ /** 获取作者的文章分页列表 */
49
+ getAuthorArticlesPage(slug: string, options?: {
50
+ is_pinned?: boolean;
51
+ locale?: string;
52
+ } & PaginationParams): Promise<Paginated<ArticleListItem>>;
53
+ /** 获取作者的文章列表 */
54
+ getAuthorArticles(slug: string, options?: {
55
+ is_pinned?: boolean;
56
+ locale?: string;
57
+ } & PaginationParams): Promise<ArticleListItem[]>;
58
+ /** 获取分类树 */
59
+ getCategories(): Promise<CategoryNode[]>;
60
+ /** 获取文章评论树 */
61
+ getComments(articleSlug: string): Promise<Comment[]>;
62
+ /** 发表评论 */
63
+ createComment(articleSlug: string, payload: CreateCommentPayload): Promise<void>;
64
+ /** 获取 Sitemap 条目 */
65
+ getSitemap(locale?: string): Promise<SitemapEntry[]>;
66
+ /** 获取重定向规则 */
67
+ getRedirects(): Promise<RedirectRule[]>;
68
+ /** 获取站点运行时配置 */
69
+ getSiteConfig(options?: SiteConfigQuery): Promise<SiteConfig>;
70
+ /** 获取文章系列 */
71
+ getSeries(slug: string, locale?: string): Promise<Series>;
72
+ private request;
73
+ }
74
+ /** 创建 CMS 客户端实例 */
75
+ declare function createCmsClient(config: CmsClientConfig): CmsClient;
76
+
77
+ /** CMS API 错误(NormalizedApiError 子类,可被 api-client 的 normalizer 直接抛出) */
78
+ declare class CmsApiError extends NormalizedApiError {
79
+ readonly errorCode: string;
80
+ constructor(statusCode: number, errorCode: string, message: string, body?: unknown);
81
+ get isNotFound(): boolean;
82
+ get isUnauthorized(): boolean;
83
+ }
84
+
85
+ export { ArticleDetail, ArticleListItem, ArticleListParams, ArticleSearchParams, ArticleSearchResultItem, Author, CategoryNode, CmsApiError, CmsClient, Comment, CreateCommentPayload, Paginated, PaginationParams, RedirectRule, Series, SiteConfig, SiteConfigQuery, SitemapEntry, createCmsClient };
86
+ export type { CmsClientConfig };
package/dist/index.mjs ADDED
@@ -0,0 +1,221 @@
1
+ import { NormalizedApiError, createApiClient, ApiHttpError } from '@stackonward/api-client';
2
+ export { DEFAULT_ROUTE_RESOURCES, buildRoutePath, buildRouteUrl, extractRouteParams, extractSourceRouteParams, formatRoutePolicy, hasCustomArticleRoute, isRoutePolicy, mergeRouteParams, resolveRoutePolicy } from './routes.mjs';
3
+
4
+ const DEFAULT_CONFIG = {
5
+ defaultLocale: "en",
6
+ timeout: 1e4
7
+ };
8
+
9
+ class CmsApiError extends NormalizedApiError {
10
+ errorCode;
11
+ constructor(statusCode, errorCode, message, body) {
12
+ super({ kind: "http", statusCode, code: errorCode, message, body });
13
+ this.name = "CmsApiError";
14
+ this.errorCode = errorCode;
15
+ }
16
+ get isNotFound() {
17
+ return this.statusCode === 404;
18
+ }
19
+ get isUnauthorized() {
20
+ return this.statusCode === 401;
21
+ }
22
+ }
23
+
24
+ async function resolveHeaders(provider, context) {
25
+ if (!provider) {
26
+ return { Accept: "application/json" };
27
+ }
28
+ const configured = typeof provider === "function" ? await provider(context) : provider;
29
+ const headers = new Headers(configured);
30
+ if (!headers.has("Accept")) {
31
+ headers.set("Accept", "application/json");
32
+ }
33
+ return headers;
34
+ }
35
+ function normalizeListResponse(body, endpoint) {
36
+ if (Array.isArray(body)) {
37
+ return body;
38
+ }
39
+ if (typeof body === "object" && body !== null && "data" in body) {
40
+ const data = body.data;
41
+ if (Array.isArray(data)) {
42
+ return data;
43
+ }
44
+ }
45
+ throw new CmsApiError(
46
+ 500,
47
+ "INVALID_LIST_RESPONSE",
48
+ `CMS API returned invalid list response for ${endpoint}`,
49
+ body
50
+ );
51
+ }
52
+ const cmsErrorNormalizer = {
53
+ normalize(raw) {
54
+ if (raw instanceof CmsApiError) return raw;
55
+ if (raw instanceof ApiHttpError) {
56
+ return new CmsApiError(
57
+ raw.status,
58
+ "HTTP_ERROR",
59
+ `CMS request failed with status ${raw.status}`,
60
+ raw.data
61
+ );
62
+ }
63
+ if (raw instanceof NormalizedApiError) {
64
+ return new CmsApiError(
65
+ raw.statusCode ?? 0,
66
+ String(raw.code ?? raw.kind).toUpperCase(),
67
+ raw.message,
68
+ raw.body
69
+ );
70
+ }
71
+ return new CmsApiError(
72
+ 0,
73
+ "NETWORK_ERROR",
74
+ raw instanceof Error ? raw.message : "CMS request failed",
75
+ raw
76
+ );
77
+ }
78
+ };
79
+ class CmsClient {
80
+ config;
81
+ api;
82
+ constructor(config) {
83
+ this.config = { ...DEFAULT_CONFIG, ...config };
84
+ this.api = createApiClient({
85
+ baseUrl: this.config.baseUrl,
86
+ timeout: this.config.timeout,
87
+ fetch: config.fetch,
88
+ headers: (context) => resolveHeaders(config.headers, context),
89
+ errorNormalizer: config.errorNormalizer ?? cmsErrorNormalizer
90
+ });
91
+ }
92
+ // ─── 文章 ───────────────────────────────────────────
93
+ /** 获取文章分页列表 */
94
+ async getArticles(params) {
95
+ return this.request(
96
+ "GET",
97
+ "/articles",
98
+ params
99
+ );
100
+ }
101
+ /** 获取文章详情 */
102
+ async getArticle(slug, locale) {
103
+ return this.request("GET", `/articles/${encodeURIComponent(slug)}`, { locale });
104
+ }
105
+ /** 获取相关文章 */
106
+ async getRelatedArticles(slug, options) {
107
+ const endpoint = `/articles/${encodeURIComponent(slug)}/related`;
108
+ const result = await this.request(
109
+ "GET",
110
+ endpoint,
111
+ options
112
+ );
113
+ return normalizeListResponse(result, endpoint);
114
+ }
115
+ /** 搜索已发布文章 */
116
+ async searchArticles(params) {
117
+ return this.request(
118
+ "GET",
119
+ "/search",
120
+ params
121
+ );
122
+ }
123
+ // ─── 作者 ───────────────────────────────────────────
124
+ /** 获取作者分页列表 */
125
+ async getAuthorsPage(options) {
126
+ return this.request("GET", "/authors", options);
127
+ }
128
+ /** 获取所有作者 */
129
+ async getAuthors(options) {
130
+ const result = await this.getAuthorsPage(options);
131
+ return normalizeListResponse(result, "/authors");
132
+ }
133
+ /** 获取作者详情 */
134
+ async getAuthor(slug, locale) {
135
+ return this.request("GET", `/authors/${encodeURIComponent(slug)}`, { locale });
136
+ }
137
+ /** 获取作者的文章分页列表 */
138
+ async getAuthorArticlesPage(slug, options) {
139
+ return this.request(
140
+ "GET",
141
+ `/authors/${encodeURIComponent(slug)}/articles`,
142
+ options
143
+ );
144
+ }
145
+ /** 获取作者的文章列表 */
146
+ async getAuthorArticles(slug, options) {
147
+ const result = await this.getAuthorArticlesPage(slug, options);
148
+ return normalizeListResponse(
149
+ result,
150
+ `/authors/${encodeURIComponent(slug)}/articles`
151
+ );
152
+ }
153
+ // ─── 分类 ───────────────────────────────────────────
154
+ /** 获取分类树 */
155
+ async getCategories() {
156
+ const result = await this.request(
157
+ "GET",
158
+ "/categories"
159
+ );
160
+ return normalizeListResponse(result, "/categories");
161
+ }
162
+ // ─── 评论 ───────────────────────────────────────────
163
+ /** 获取文章评论树 */
164
+ async getComments(articleSlug) {
165
+ return this.request("GET", `/articles/${encodeURIComponent(articleSlug)}/comments`);
166
+ }
167
+ /** 发表评论 */
168
+ async createComment(articleSlug, payload) {
169
+ return this.request(
170
+ "POST",
171
+ `/articles/${encodeURIComponent(articleSlug)}/comments`,
172
+ void 0,
173
+ payload
174
+ );
175
+ }
176
+ // ─── Sitemap / 重定向 / 系列 ──────────────────────
177
+ /** 获取 Sitemap 条目 */
178
+ async getSitemap(locale) {
179
+ const result = await this.request(
180
+ "GET",
181
+ "/sitemap",
182
+ { locale }
183
+ );
184
+ return normalizeListResponse(result, "/sitemap");
185
+ }
186
+ /** 获取重定向规则 */
187
+ async getRedirects() {
188
+ const result = await this.request(
189
+ "GET",
190
+ "/redirects"
191
+ );
192
+ return normalizeListResponse(result, "/redirects");
193
+ }
194
+ /** 获取站点运行时配置 */
195
+ async getSiteConfig(options) {
196
+ return this.request("GET", "/site", options, void 0, {
197
+ defaultLocale: false
198
+ });
199
+ }
200
+ /** 获取文章系列 */
201
+ async getSeries(slug, locale) {
202
+ return this.request("GET", `/series/${encodeURIComponent(slug)}`, { locale });
203
+ }
204
+ // ─── 内部请求方法 ──────────────────────────────────
205
+ async request(method, path, query, body, options) {
206
+ let finalQuery = query;
207
+ if (method === "GET" && options?.defaultLocale !== false && this.config.defaultLocale) {
208
+ const q = { ...query ?? {} };
209
+ if (q.locale === void 0 || q.locale === null) {
210
+ q.locale = this.config.defaultLocale;
211
+ }
212
+ finalQuery = q;
213
+ }
214
+ return this.api.request(method, path, { query: finalQuery, body });
215
+ }
216
+ }
217
+ function createCmsClient(config) {
218
+ return new CmsClient(config);
219
+ }
220
+
221
+ export { CmsApiError, CmsClient, createCmsClient };
@@ -0,0 +1,54 @@
1
+ type RouteParams = Record<string, string | number | boolean | null | undefined>;
2
+ interface RouteResources {
3
+ index?: string;
4
+ category?: string;
5
+ article?: string;
6
+ author?: string;
7
+ [resource: string]: string | undefined;
8
+ }
9
+ interface RouteLocalePolicy {
10
+ defaultLocale?: string;
11
+ prefixes?: Record<string, string>;
12
+ includeDefaultLocale?: boolean;
13
+ }
14
+ interface RoutePolicy {
15
+ resources?: RouteResources;
16
+ sourceResources?: RouteResources;
17
+ locale?: RouteLocalePolicy;
18
+ }
19
+ interface RoutePolicyConfig {
20
+ defaultLocale?: string;
21
+ localePrefixes?: Record<string, string>;
22
+ routes?: RoutePolicy;
23
+ }
24
+ interface BuildRoutePathOptions {
25
+ includeLocale?: boolean;
26
+ }
27
+ interface BuildRouteUrlOptions extends BuildRoutePathOptions {
28
+ }
29
+ interface ResolvedRouteLocalePolicy {
30
+ defaultLocale: string;
31
+ prefixes: Record<string, string>;
32
+ includeDefaultLocale: boolean;
33
+ }
34
+ type RouteResourceDefaults = RouteResources & Required<Pick<RouteResources, 'article' | 'author' | 'category' | 'index'>>;
35
+ interface ResolvedRoutePolicy {
36
+ resources: RouteResourceDefaults;
37
+ sourceResources: RouteResourceDefaults;
38
+ locale: ResolvedRouteLocalePolicy;
39
+ }
40
+ declare const DEFAULT_ROUTE_RESOURCES: RouteResourceDefaults;
41
+ declare function resolveRoutePolicy(config?: RoutePolicyConfig): ResolvedRoutePolicy;
42
+ declare function buildRoutePath(policy: RoutePolicy, resource: string, params?: RouteParams, options?: BuildRoutePathOptions): string;
43
+ declare function buildRouteUrl(baseUrl: string, policy: null | RoutePolicy | undefined, resource: string, params?: RouteParams, options?: BuildRouteUrlOptions): string;
44
+ declare function isRoutePolicy(value: unknown): value is RoutePolicy;
45
+ declare function hasCustomArticleRoute(policy?: null | RoutePolicy): boolean;
46
+ declare function formatRoutePolicy(policy?: null | RoutePolicy): string;
47
+ declare function extractRouteParams(pattern: string, path: string): RouteParams | null;
48
+ declare function extractSourceRouteParams(policy: RoutePolicy, resource: string, path: string): RouteParams | null;
49
+ declare function mergeRouteParams(left: RouteParams | null | undefined, right: RouteParams | null | undefined): {
50
+ [x: string]: string | number | boolean | null | undefined;
51
+ };
52
+
53
+ export { DEFAULT_ROUTE_RESOURCES, buildRoutePath, buildRouteUrl, extractRouteParams, extractSourceRouteParams, formatRoutePolicy, hasCustomArticleRoute, isRoutePolicy, mergeRouteParams, resolveRoutePolicy };
54
+ export type { BuildRoutePathOptions, BuildRouteUrlOptions, ResolvedRouteLocalePolicy, ResolvedRoutePolicy, RouteLocalePolicy, RouteParams, RoutePolicy, RoutePolicyConfig, RouteResourceDefaults, RouteResources };
@@ -0,0 +1,54 @@
1
+ type RouteParams = Record<string, string | number | boolean | null | undefined>;
2
+ interface RouteResources {
3
+ index?: string;
4
+ category?: string;
5
+ article?: string;
6
+ author?: string;
7
+ [resource: string]: string | undefined;
8
+ }
9
+ interface RouteLocalePolicy {
10
+ defaultLocale?: string;
11
+ prefixes?: Record<string, string>;
12
+ includeDefaultLocale?: boolean;
13
+ }
14
+ interface RoutePolicy {
15
+ resources?: RouteResources;
16
+ sourceResources?: RouteResources;
17
+ locale?: RouteLocalePolicy;
18
+ }
19
+ interface RoutePolicyConfig {
20
+ defaultLocale?: string;
21
+ localePrefixes?: Record<string, string>;
22
+ routes?: RoutePolicy;
23
+ }
24
+ interface BuildRoutePathOptions {
25
+ includeLocale?: boolean;
26
+ }
27
+ interface BuildRouteUrlOptions extends BuildRoutePathOptions {
28
+ }
29
+ interface ResolvedRouteLocalePolicy {
30
+ defaultLocale: string;
31
+ prefixes: Record<string, string>;
32
+ includeDefaultLocale: boolean;
33
+ }
34
+ type RouteResourceDefaults = RouteResources & Required<Pick<RouteResources, 'article' | 'author' | 'category' | 'index'>>;
35
+ interface ResolvedRoutePolicy {
36
+ resources: RouteResourceDefaults;
37
+ sourceResources: RouteResourceDefaults;
38
+ locale: ResolvedRouteLocalePolicy;
39
+ }
40
+ declare const DEFAULT_ROUTE_RESOURCES: RouteResourceDefaults;
41
+ declare function resolveRoutePolicy(config?: RoutePolicyConfig): ResolvedRoutePolicy;
42
+ declare function buildRoutePath(policy: RoutePolicy, resource: string, params?: RouteParams, options?: BuildRoutePathOptions): string;
43
+ declare function buildRouteUrl(baseUrl: string, policy: null | RoutePolicy | undefined, resource: string, params?: RouteParams, options?: BuildRouteUrlOptions): string;
44
+ declare function isRoutePolicy(value: unknown): value is RoutePolicy;
45
+ declare function hasCustomArticleRoute(policy?: null | RoutePolicy): boolean;
46
+ declare function formatRoutePolicy(policy?: null | RoutePolicy): string;
47
+ declare function extractRouteParams(pattern: string, path: string): RouteParams | null;
48
+ declare function extractSourceRouteParams(policy: RoutePolicy, resource: string, path: string): RouteParams | null;
49
+ declare function mergeRouteParams(left: RouteParams | null | undefined, right: RouteParams | null | undefined): {
50
+ [x: string]: string | number | boolean | null | undefined;
51
+ };
52
+
53
+ export { DEFAULT_ROUTE_RESOURCES, buildRoutePath, buildRouteUrl, extractRouteParams, extractSourceRouteParams, formatRoutePolicy, hasCustomArticleRoute, isRoutePolicy, mergeRouteParams, resolveRoutePolicy };
54
+ export type { BuildRoutePathOptions, BuildRouteUrlOptions, ResolvedRouteLocalePolicy, ResolvedRoutePolicy, RouteLocalePolicy, RouteParams, RoutePolicy, RoutePolicyConfig, RouteResourceDefaults, RouteResources };