@vigilkids/cms-nuxt 0.3.0 → 0.4.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.
Files changed (75) hide show
  1. package/CHANGELOG.md +99 -0
  2. package/README.md +239 -22
  3. package/dist/module.d.mts +37 -2
  4. package/dist/module.d.ts +5 -44
  5. package/dist/module.d.ts.map +1 -0
  6. package/dist/module.json +1 -1
  7. package/dist/module.mjs +91 -5
  8. package/dist/runtime/composables/useArticle.d.ts +16 -0
  9. package/dist/runtime/composables/useArticle.d.ts.map +1 -0
  10. package/dist/runtime/composables/useArticleList.d.ts +11 -0
  11. package/dist/runtime/composables/useArticleList.d.ts.map +1 -0
  12. package/dist/runtime/composables/useAuthor.d.ts +10 -0
  13. package/dist/runtime/composables/useAuthor.d.ts.map +1 -0
  14. package/dist/runtime/composables/useBlogSeo.d.ts +9 -0
  15. package/dist/runtime/composables/useBlogSeo.d.ts.map +1 -0
  16. package/dist/runtime/composables/useCategories.d.ts +10 -0
  17. package/dist/runtime/composables/useCategories.d.ts.map +1 -0
  18. package/dist/runtime/composables/useCmsArticleCollection.d.ts +14 -0
  19. package/dist/runtime/composables/useCmsArticleCollection.d.ts.map +1 -0
  20. package/dist/runtime/composables/useCmsArticleCollection.js +16 -0
  21. package/dist/runtime/composables/useCmsCategories.d.ts +78 -0
  22. package/dist/runtime/composables/useCmsCategories.d.ts.map +1 -0
  23. package/dist/runtime/composables/useCmsCategories.js +60 -0
  24. package/dist/runtime/composables/useCmsClient.d.ts +4 -0
  25. package/dist/runtime/composables/useCmsClient.d.ts.map +1 -0
  26. package/dist/runtime/composables/useCmsFetch.d.ts +19 -0
  27. package/dist/runtime/composables/useCmsFetch.d.ts.map +1 -0
  28. package/dist/runtime/composables/useCmsFetch.js +20 -0
  29. package/dist/runtime/composables/useCmsPreview.d.ts +16 -0
  30. package/dist/runtime/composables/useCmsPreview.d.ts.map +1 -0
  31. package/dist/runtime/composables/useCmsRoutes.d.ts +7 -0
  32. package/dist/runtime/composables/useCmsRoutes.d.ts.map +1 -0
  33. package/dist/runtime/composables/useCmsRoutes.js +10 -0
  34. package/dist/runtime/composables/useHreflangLinks.d.ts +34 -0
  35. package/dist/runtime/composables/useHreflangLinks.d.ts.map +1 -0
  36. package/dist/runtime/composables/useHreflangLinks.js +2 -1
  37. package/dist/runtime/plugins/preview-init.client.d.ts +4 -0
  38. package/dist/runtime/plugins/preview-init.client.d.ts.map +1 -0
  39. package/dist/runtime/server/api/_revalidate.post.d.ts +9 -0
  40. package/dist/runtime/server/api/_revalidate.post.d.ts.map +1 -0
  41. package/dist/runtime/server/api/_revalidate.post.js +30 -10
  42. package/dist/runtime/server/api/articles.get.d.ts +4 -0
  43. package/dist/runtime/server/api/articles.get.d.ts.map +1 -0
  44. package/dist/runtime/server/api/articles.get.js +92 -0
  45. package/dist/runtime/server/api/preview.get.d.ts +4 -0
  46. package/dist/runtime/server/api/preview.get.d.ts.map +1 -0
  47. package/dist/runtime/server/api/preview.get.js +47 -12
  48. package/dist/runtime/server/api/sitemap-urls.get.d.ts +16 -0
  49. package/dist/runtime/server/api/sitemap-urls.get.d.ts.map +1 -0
  50. package/dist/runtime/server/api/sitemap-urls.get.js +20 -0
  51. package/dist/runtime/server/middleware/preview-context.d.ts +14 -0
  52. package/dist/runtime/server/middleware/preview-context.d.ts.map +1 -0
  53. package/dist/runtime/server/middleware/redirects.d.ts +4 -0
  54. package/dist/runtime/server/middleware/redirects.d.ts.map +1 -0
  55. package/dist/runtime/server/middleware/redirects.js +27 -10
  56. package/dist/runtime/server/routes/cms-proxy.d.ts +3 -0
  57. package/dist/runtime/server/routes/cms-proxy.d.ts.map +1 -0
  58. package/dist/runtime/server/routes/cms-proxy.js +55 -0
  59. package/dist/runtime/server/utils/cms-client.d.ts +4 -0
  60. package/dist/runtime/server/utils/cms-client.d.ts.map +1 -0
  61. package/dist/runtime/server/utils/cms-client.js +14 -7
  62. package/dist/runtime/sitemap/provider.d.ts +17 -0
  63. package/dist/runtime/sitemap/provider.d.ts.map +1 -0
  64. package/dist/runtime/sitemap/provider.js +9 -11
  65. package/dist/runtime/utils/list.d.ts +2 -0
  66. package/dist/runtime/utils/list.d.ts.map +1 -0
  67. package/dist/runtime/utils/routes.d.ts +19 -0
  68. package/dist/runtime/utils/routes.d.ts.map +1 -0
  69. package/dist/runtime/utils/routes.js +119 -0
  70. package/dist/runtime/utils/sitemap.d.ts +20 -0
  71. package/dist/runtime/utils/sitemap.d.ts.map +1 -0
  72. package/dist/runtime/utils/sitemap.js +44 -0
  73. package/dist/types.d.ts +76 -7
  74. package/dist/types.d.ts.map +1 -0
  75. package/package.json +7 -6
@@ -1,16 +1,33 @@
1
1
  import { env } from "node:process";
2
2
  import { useRuntimeConfig } from "#imports";
3
3
  import { createError, defineEventHandler, getQuery, sendRedirect, setCookie } from "h3";
4
+ import { buildCmsPath, extractCmsSourceRouteParams, mergeCmsRouteParams, resolveCmsRoutePolicy } from "../../utils/routes.js";
5
+ import { createServerCmsClient } from "../utils/cms-client.js";
6
+ function stringValue(value) {
7
+ return typeof value === "string" ? value.trim() : "";
8
+ }
9
+ function queryRouteParams(query) {
10
+ const params = {};
11
+ for (const [key, value] of Object.entries(query)) {
12
+ if (typeof value === "string" && value.trim()) {
13
+ params[key] = value.trim();
14
+ }
15
+ }
16
+ return params;
17
+ }
4
18
  export default defineEventHandler(async (event) => {
5
19
  const query = getQuery(event);
6
- const token = query.token;
7
- const slug = query.slug;
8
- const locale = query.locale;
9
- if (!token || !slug) {
20
+ const token = stringValue(query.token);
21
+ const rawSlug = stringValue(query.slug);
22
+ const resource = stringValue(query.resource_type) || stringValue(query.resource) || "article";
23
+ if (!token || resource === "article" && !rawSlug) {
10
24
  throw createError({ statusCode: 400, message: "Missing token or slug parameter" });
11
25
  }
12
26
  const config = useRuntimeConfig();
13
27
  const cmsConfig = config.public.cms;
28
+ const routePolicy = resolveCmsRoutePolicy(cmsConfig);
29
+ const publicLocale = stringValue(query.locale) || routePolicy.locale.defaultLocale || "en";
30
+ const apiLocale = cmsConfig.localeMap?.[publicLocale] ?? publicLocale;
14
31
  const cookieName = cmsConfig.preview?.cookieName ?? "__preview_token";
15
32
  const cookieMaxAge = cmsConfig.preview?.cookieMaxAge ?? 1800;
16
33
  setCookie(event, cookieName, token, {
@@ -20,13 +37,31 @@ export default defineEventHandler(async (event) => {
20
37
  path: "/",
21
38
  maxAge: cookieMaxAge
22
39
  });
23
- const defaultLocale = cmsConfig.defaultLocale ?? "en";
24
- let prefix = "";
25
- if (locale && locale !== defaultLocale) {
26
- const prefixes = cmsConfig.localePrefixes ?? {};
27
- prefix = `/${prefixes[locale] ?? locale.toLowerCase()}`;
40
+ const sourceParams = rawSlug ? extractCmsSourceRouteParams(routePolicy, resource, rawSlug) : null;
41
+ let routeParams = mergeCmsRouteParams(sourceParams, queryRouteParams(query));
42
+ routeParams.locale = publicLocale;
43
+ if (rawSlug && !routeParams.slug) {
44
+ routeParams.slug = rawSlug;
45
+ }
46
+ if (resource === "article" && !routeParams.category_slug && routePolicy.resources.article?.includes("{category_slug}")) {
47
+ const client = createServerCmsClient(token);
48
+ try {
49
+ const article = await client.getArticle(String(routeParams.slug), apiLocale);
50
+ if (article.category) {
51
+ routeParams = { ...routeParams, category_slug: article.category };
52
+ }
53
+ } catch {
54
+ throw createError({ statusCode: 404, message: "Preview article not found" });
55
+ }
56
+ }
57
+ let path;
58
+ try {
59
+ path = buildCmsPath(routePolicy, resource, routeParams, { includeLocale: true });
60
+ } catch (error) {
61
+ throw createError({
62
+ statusCode: 400,
63
+ message: error instanceof Error ? error.message : "Invalid preview route parameters"
64
+ });
28
65
  }
29
- const articlePath = cmsConfig.preview?.articlePath ?? "/blog/{slug}";
30
- const path = articlePath.replace("{slug}", slug);
31
- return sendRedirect(event, `${prefix}${path}?_preview=1`, 302);
66
+ return sendRedirect(event, `${path}?_preview=1`, 302);
32
67
  });
@@ -0,0 +1,16 @@
1
+ declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<{
2
+ loc: string;
3
+ lastmod: string;
4
+ changefreq: string;
5
+ priority: number;
6
+ alternatives: {
7
+ hreflang: string;
8
+ href: string;
9
+ }[] | undefined;
10
+ images: {
11
+ loc: string;
12
+ title: string | undefined;
13
+ }[] | undefined;
14
+ }[]>>;
15
+ export default _default;
16
+ //# sourceMappingURL=sitemap-urls.get.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sitemap-urls.get.d.ts","sourceRoot":"","sources":["../../../../src/runtime/server/api/sitemap-urls.get.ts"],"names":[],"mappings":";;;;;;;;;;;;;;AASA,wBAqBE"}
@@ -0,0 +1,20 @@
1
+ import { useRuntimeConfig } from "#imports";
2
+ import { defineEventHandler, getQuery, setResponseHeader } from "h3";
3
+ import { toArray } from "../../utils/list.js";
4
+ import { resolveCmsRoutePolicy } from "../../utils/routes.js";
5
+ import { cmsLocale, toNuxtSitemapEntry } from "../../utils/sitemap.js";
6
+ import { useServerCmsClient } from "../utils/cms-client.js";
7
+ export default defineEventHandler(async (event) => {
8
+ setResponseHeader(event, "Cache-Control", "private, no-cache, no-store");
9
+ const { locale } = getQuery(event);
10
+ if (!locale || typeof locale !== "string")
11
+ return [];
12
+ const config = useRuntimeConfig();
13
+ const cmsConfig = config.public.cms;
14
+ const routePolicy = resolveCmsRoutePolicy(cmsConfig);
15
+ const client = useServerCmsClient();
16
+ const entries = await client.getSitemap(cmsLocale(cmsConfig.localeMap, locale));
17
+ return toArray(entries).map(
18
+ (entry) => toNuxtSitemapEntry(entry, locale, routePolicy, cmsConfig.localeMap)
19
+ );
20
+ });
@@ -0,0 +1,14 @@
1
+ /**
2
+ * 预览模式中间件:token 穿透 + SWR 缓存绕过。
3
+ *
4
+ * 1. Token 穿透:将预览 cookie 存入 event.context,穿越 SWR 合成事件边界。
5
+ * SWR cachedEventHandler 的合成事件会剥离 cookie,但 event.context
6
+ * 通过引用共享(synthetic.context = original.context)可以穿越。
7
+ *
8
+ * 2. 缓存绕过:Nitro SWR cache key 基于 hash(event.node.req.url)(含 query)。
9
+ * 对页面路由追加唯一时间戳参数,使每次预览请求产生不同 cache key,
10
+ * 确保编辑后刷新总是获取最新草稿内容。
11
+ */
12
+ declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, void>;
13
+ export default _default;
14
+ //# sourceMappingURL=preview-context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preview-context.d.ts","sourceRoot":"","sources":["../../../../src/runtime/server/middleware/preview-context.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;GAUG;;AACH,wBAsBE"}
@@ -0,0 +1,4 @@
1
+ /** CMS 重定向中间件 */
2
+ declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<void>>;
3
+ export default _default;
4
+ //# sourceMappingURL=redirects.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redirects.d.ts","sourceRoot":"","sources":["../../../../src/runtime/server/middleware/redirects.ts"],"names":[],"mappings":"AAsDA,iBAAiB;;AACjB,wBAiBE"}
@@ -3,6 +3,8 @@ import { defineEventHandler, getRequestURL, sendRedirect } from "h3";
3
3
  import { toArray } from "../../utils/list.js";
4
4
  let cachedRules = null;
5
5
  let cacheExpiry = 0;
6
+ const regexCache = /* @__PURE__ */ new Map();
7
+ const skippedPrefixes = ["/_", "/__", "/_nuxt", "/_ipx", "/.well-known", "/api"];
6
8
  async function loadRules() {
7
9
  const now = Date.now();
8
10
  if (cachedRules && now < cacheExpiry)
@@ -13,27 +15,42 @@ async function loadRules() {
13
15
  const { useServerCmsClient } = await import("../utils/cms-client.js");
14
16
  const client = useServerCmsClient();
15
17
  cachedRules = toArray(await client.getRedirects());
18
+ regexCache.clear();
16
19
  cacheExpiry = now + cacheTtl;
17
20
  return cachedRules;
18
- } catch (error) {
19
- console.error("[cms-nuxt] \u52A0\u8F7D\u91CD\u5B9A\u5411\u89C4\u5219\u5931\u8D25", error);
21
+ } catch {
20
22
  return cachedRules ?? [];
21
23
  }
22
24
  }
25
+ function matchesRule(rule, path) {
26
+ if (rule.match_type === "prefix")
27
+ return path.startsWith(rule.source_path);
28
+ if (rule.match_type === "regex") {
29
+ if (path.length > 2048)
30
+ return false;
31
+ let expression = regexCache.get(rule.source_path);
32
+ if (!expression) {
33
+ try {
34
+ expression = new RegExp(rule.source_path);
35
+ } catch {
36
+ return false;
37
+ }
38
+ regexCache.set(rule.source_path, expression);
39
+ }
40
+ return expression.test(path);
41
+ }
42
+ return path === rule.source_path;
43
+ }
23
44
  export default defineEventHandler(async (event) => {
24
45
  const path = getRequestURL(event).pathname;
25
- if (path.startsWith("/api/") || path.startsWith("/_nuxt/") || path.startsWith("/__nuxt")) {
46
+ const config = useRuntimeConfig();
47
+ const proxyPath = config.public.cms.content?.cmsProxyPath ?? "/cms-proxy";
48
+ if (path.includes(".") || [...skippedPrefixes, proxyPath].some((prefix) => path.startsWith(prefix))) {
26
49
  return;
27
50
  }
28
51
  const rules = await loadRules();
29
52
  for (const rule of rules) {
30
- let matched = false;
31
- if (rule.match_type === "exact" && path === rule.source_path) {
32
- matched = true;
33
- } else if (rule.match_type === "prefix" && path.startsWith(rule.source_path)) {
34
- matched = true;
35
- }
36
- if (matched) {
53
+ if (matchesRule(rule, path)) {
37
54
  return sendRedirect(event, rule.destination_path, rule.status_code);
38
55
  }
39
56
  }
@@ -0,0 +1,3 @@
1
+ declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<unknown>>;
2
+ export default _default;
3
+ //# sourceMappingURL=cms-proxy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cms-proxy.d.ts","sourceRoot":"","sources":["../../../../src/runtime/server/routes/cms-proxy.ts"],"names":[],"mappings":";AA0BA,wBA0CE"}
@@ -0,0 +1,55 @@
1
+ import { useRuntimeConfig } from "#imports";
2
+ import {
3
+ createError,
4
+ defineEventHandler,
5
+ getCookie,
6
+ getQuery,
7
+ getRequestHeader,
8
+ getRequestURL,
9
+ setResponseHeader
10
+ } from "h3";
11
+ function queryString(query) {
12
+ const params = new URLSearchParams();
13
+ for (const [key, value] of Object.entries(query)) {
14
+ if (value !== void 0 && value !== null) {
15
+ params.set(key, String(value));
16
+ }
17
+ }
18
+ return params.toString();
19
+ }
20
+ function proxyPathname(pathname, proxyPath) {
21
+ const prefix = proxyPath.replace(/\/+$/g, "");
22
+ return decodeURIComponent(pathname.slice(prefix.length).replace(/^\/+/g, ""));
23
+ }
24
+ export default defineEventHandler(async (event) => {
25
+ setResponseHeader(event, "Cache-Control", "private, no-cache, no-store");
26
+ const config = useRuntimeConfig();
27
+ const cmsConfig = config.public.cms;
28
+ if (!cmsConfig.apiUrl) {
29
+ throw createError({ statusCode: 500, message: "CMS API URL not configured" });
30
+ }
31
+ const url = getRequestURL(event);
32
+ const proxyPath = cmsConfig.content?.cmsProxyPath ?? "/cms-proxy";
33
+ const path = proxyPathname(url.pathname, proxyPath);
34
+ const search = queryString(getQuery(event));
35
+ const targetUrl = `${cmsConfig.apiUrl.replace(/\/+$/g, "")}/${path}${search ? `?${search}` : ""}`;
36
+ const previewCookieName = cmsConfig.preview?.cookieName ?? "__preview_token";
37
+ const previewToken = getRequestHeader(event, "x-preview-token") || getCookie(event, previewCookieName);
38
+ const headers = { Accept: "application/json" };
39
+ if (cmsConfig.productCode)
40
+ headers["X-Product-Code"] = cmsConfig.productCode;
41
+ if (previewToken)
42
+ headers["X-Preview-Token"] = decodeURIComponent(previewToken);
43
+ const upstreamResponse = await $fetch.raw(targetUrl, {
44
+ headers,
45
+ ignoreResponseError: true
46
+ });
47
+ if (!upstreamResponse.ok) {
48
+ throw createError({
49
+ statusCode: upstreamResponse.status,
50
+ statusMessage: upstreamResponse.statusText,
51
+ data: upstreamResponse._data
52
+ });
53
+ }
54
+ return upstreamResponse._data;
55
+ });
@@ -0,0 +1,4 @@
1
+ import { CmsClient } from '@vigilkids/cms-client';
2
+ export declare function createServerCmsClient(previewToken?: string): CmsClient;
3
+ export declare function useServerCmsClient(): CmsClient;
4
+ //# sourceMappingURL=cms-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cms-client.d.ts","sourceRoot":"","sources":["../../../../src/runtime/server/utils/cms-client.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAA;AAgBjD,wBAAgB,qBAAqB,CAAC,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAQtE;AAED,wBAAgB,kBAAkB,IAAI,SAAS,CAK9C"}
@@ -1,15 +1,22 @@
1
1
  import { useRuntimeConfig } from "#imports";
2
2
  import { CmsClient } from "@vigilkids/cms-client";
3
3
  let _client = null;
4
+ function publicCmsConfig() {
5
+ const config = useRuntimeConfig();
6
+ return config.public.cms;
7
+ }
8
+ export function createServerCmsClient(previewToken) {
9
+ const publicCms = publicCmsConfig();
10
+ return new CmsClient({
11
+ baseUrl: publicCms.apiUrl,
12
+ productCode: publicCms.productCode,
13
+ defaultLocale: publicCms.defaultLocale,
14
+ previewToken
15
+ });
16
+ }
4
17
  export function useServerCmsClient() {
5
18
  if (!_client) {
6
- const config = useRuntimeConfig();
7
- const publicCms = config.public.cms;
8
- _client = new CmsClient({
9
- baseUrl: publicCms.apiUrl,
10
- productCode: publicCms.productCode,
11
- defaultLocale: publicCms.defaultLocale
12
- });
19
+ _client = createServerCmsClient();
13
20
  }
14
21
  return _client;
15
22
  }
@@ -0,0 +1,17 @@
1
+ import type { CmsNuxtModuleOptions } from '../../types';
2
+ /** 为 @nuxtjs/sitemap 提供 CMS 内容 URL */
3
+ export declare function sitemapProvider(options: CmsNuxtModuleOptions): Promise<{
4
+ loc: string;
5
+ lastmod?: string;
6
+ changefreq?: string;
7
+ priority?: number;
8
+ alternatives?: Array<{
9
+ hreflang: string;
10
+ href: string;
11
+ }>;
12
+ images?: Array<{
13
+ loc: string;
14
+ title?: string;
15
+ }>;
16
+ }[]>;
17
+ //# sourceMappingURL=provider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../../../src/runtime/sitemap/provider.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAA;AAMvD,sCAAsC;AACtC,wBAAsB,eAAe,CAAC,OAAO,EAAE,oBAAoB;SAc1D,MAAM;cACD,MAAM;iBACH,MAAM;eACR,MAAM;mBACF,KAAK,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;aAC/C,KAAK,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;KAWlD"}
@@ -1,5 +1,7 @@
1
1
  import { CmsClient } from "@vigilkids/cms-client";
2
2
  import { toArray } from "../utils/list.js";
3
+ import { resolveCmsRoutePolicy } from "../utils/routes.js";
4
+ import { cmsLocale, toNuxtSitemapEntry } from "../utils/sitemap.js";
3
5
  export async function sitemapProvider(options) {
4
6
  const client = new CmsClient({
5
7
  baseUrl: options.apiUrl,
@@ -7,20 +9,16 @@ export async function sitemapProvider(options) {
7
9
  defaultLocale: options.defaultLocale
8
10
  });
9
11
  const locales = options.sitemap?.locales ?? [options.defaultLocale ?? "en"];
12
+ const routePolicy = resolveCmsRoutePolicy({
13
+ defaultLocale: options.defaultLocale,
14
+ localePrefixes: options.localePrefixes,
15
+ routes: options.routes
16
+ });
10
17
  const entries = [];
11
18
  for (const locale of locales) {
12
- const sitemapEntries = await client.getSitemap(locale);
19
+ const sitemapEntries = await client.getSitemap(cmsLocale(options.localeMap, locale));
13
20
  for (const entry of toArray(sitemapEntries)) {
14
- entries.push({
15
- loc: entry.loc,
16
- lastmod: entry.last_mod,
17
- changefreq: entry.change_freq,
18
- priority: entry.priority,
19
- alternatives: entry.hreflang?.map((h) => ({
20
- hreflang: h.lang,
21
- href: h.href
22
- }))
23
- });
21
+ entries.push(toNuxtSitemapEntry(entry, locale, routePolicy, options.localeMap));
24
22
  }
25
23
  }
26
24
  return entries;
@@ -0,0 +1,2 @@
1
+ export declare function toArray<T>(value: unknown): T[];
2
+ //# sourceMappingURL=list.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../../../src/runtime/utils/list.ts"],"names":[],"mappings":"AAAA,wBAAgB,OAAO,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,GAAG,CAAC,EAAE,CAE9C"}
@@ -0,0 +1,19 @@
1
+ import type { CmsRouteParams, CmsRoutePolicy, CmsRouteResources } from '../../types';
2
+ export declare const DEFAULT_CMS_ROUTE_RESOURCES: CmsRouteResources & Required<Pick<CmsRouteResources, 'article' | 'author' | 'category' | 'index'>>;
3
+ interface CmsRouteRuntimeConfig {
4
+ defaultLocale?: string;
5
+ localePrefixes?: Record<string, string>;
6
+ routes?: CmsRoutePolicy;
7
+ }
8
+ interface BuildCmsPathOptions {
9
+ includeLocale?: boolean;
10
+ }
11
+ export declare function resolveCmsRoutePolicy(config?: CmsRouteRuntimeConfig): Required<CmsRoutePolicy>;
12
+ export declare function buildCmsPath(policy: CmsRoutePolicy, resource: string, params?: CmsRouteParams, options?: BuildCmsPathOptions): string;
13
+ export declare function extractCmsRouteParams(pattern: string, path: string): CmsRouteParams | null;
14
+ export declare function extractCmsSourceRouteParams(policy: CmsRoutePolicy, resource: string, path: string): CmsRouteParams | null;
15
+ export declare function mergeCmsRouteParams(left: CmsRouteParams | null | undefined, right: CmsRouteParams | null | undefined): {
16
+ [x: string]: string | number | boolean | null | undefined;
17
+ };
18
+ export {};
19
+ //# sourceMappingURL=routes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../../src/runtime/utils/routes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAA;AAEpF,eAAO,MAAM,2BAA2B,EAAE,iBAAiB,GAAG,QAAQ,CAAC,IAAI,CACzE,iBAAiB,EACjB,SAAS,GAAG,QAAQ,GAAG,UAAU,GAAG,OAAO,CAC5C,CAKA,CAAA;AAED,UAAU,qBAAqB;IAC7B,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACvC,MAAM,CAAC,EAAE,cAAc,CAAA;CACxB;AAED,UAAU,mBAAmB;IAC3B,aAAa,CAAC,EAAE,OAAO,CAAA;CACxB;AAuCD,wBAAgB,qBAAqB,CAAC,MAAM,CAAC,EAAE,qBAAqB,GAAG,QAAQ,CAAC,cAAc,CAAC,CAiB9F;AAED,wBAAgB,YAAY,CAC1B,MAAM,EAAE,cAAc,EACtB,QAAQ,EAAE,MAAM,EAChB,MAAM,GAAE,cAAmB,EAC3B,OAAO,GAAE,mBAAwB,UAmBlC;AAED,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI,CAgC1F;AAED,wBAAgB,2BAA2B,CACzC,MAAM,EAAE,cAAc,EACtB,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,yBAeb;AAED,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,cAAc,GAAG,IAAI,GAAG,SAAS,EACvC,KAAK,EAAE,cAAc,GAAG,IAAI,GAAG,SAAS;;EAMzC"}
@@ -0,0 +1,119 @@
1
+ export const DEFAULT_CMS_ROUTE_RESOURCES = {
2
+ index: "/",
3
+ category: "/{category_slug}",
4
+ article: "/{category_slug}/{slug}",
5
+ author: "/authors/{slug}"
6
+ };
7
+ function trimSlashes(value) {
8
+ return value.replace(/^\/+|\/+$/g, "");
9
+ }
10
+ function normalizePath(path) {
11
+ const clean = path.split("?")[0]?.split("#")[0] ?? "";
12
+ const withSlash = clean.startsWith("/") ? clean : `/${clean}`;
13
+ const collapsed = withSlash.replace(/\/{2,}/g, "/");
14
+ if (collapsed.length > 1)
15
+ return collapsed.replace(/\/+$/g, "");
16
+ return collapsed;
17
+ }
18
+ function encodePathValue(value) {
19
+ return encodeURIComponent(String(value));
20
+ }
21
+ function localePrefix(locale, policy) {
22
+ if (!locale)
23
+ return "";
24
+ const defaultLocale = policy.defaultLocale ?? "en";
25
+ if (locale === defaultLocale && !policy.includeDefaultLocale)
26
+ return "";
27
+ const prefix = policy.prefixes?.[locale] ?? locale.toLowerCase();
28
+ return `/${trimSlashes(prefix)}`;
29
+ }
30
+ function mergeResources(resources) {
31
+ return {
32
+ ...DEFAULT_CMS_ROUTE_RESOURCES,
33
+ ...resources ?? {}
34
+ };
35
+ }
36
+ export function resolveCmsRoutePolicy(config) {
37
+ const routes = config?.routes ?? {};
38
+ const defaultLocale = routes.locale?.defaultLocale ?? config?.defaultLocale ?? "en";
39
+ const prefixes = {
40
+ ...config?.localePrefixes ?? {},
41
+ ...routes.locale?.prefixes ?? {}
42
+ };
43
+ return {
44
+ resources: mergeResources(routes.resources),
45
+ sourceResources: mergeResources(routes.sourceResources),
46
+ locale: {
47
+ defaultLocale,
48
+ prefixes,
49
+ includeDefaultLocale: routes.locale?.includeDefaultLocale ?? false
50
+ }
51
+ };
52
+ }
53
+ export function buildCmsPath(policy, resource, params = {}, options = {}) {
54
+ const resolvedPolicy = resolveCmsRoutePolicy({ routes: policy });
55
+ const pattern = resolvedPolicy.resources[resource];
56
+ if (!pattern) {
57
+ throw new Error(`CMS route pattern is not configured for resource '${resource}'`);
58
+ }
59
+ const path = normalizePath(pattern.replace(/\{(\w+)\}/g, (_, key) => {
60
+ const value = params[key];
61
+ if (value === void 0 || value === null || value === "") {
62
+ throw new Error(`CMS route parameter '${key}' is required for resource '${resource}'`);
63
+ }
64
+ return encodePathValue(value);
65
+ }));
66
+ if (!options.includeLocale)
67
+ return path;
68
+ return normalizePath(`${localePrefix(String(params.locale ?? ""), resolvedPolicy.locale)}${path}`);
69
+ }
70
+ export function extractCmsRouteParams(pattern, path) {
71
+ const keys = [];
72
+ const source = normalizePath(pattern);
73
+ let expression = "^";
74
+ for (let index = 0; index < source.length; index++) {
75
+ const char = source[index];
76
+ if (char === "{") {
77
+ const closeIndex = source.indexOf("}", index);
78
+ if (closeIndex > index) {
79
+ const key = source.slice(index + 1, closeIndex);
80
+ keys.push(key);
81
+ expression += "([^/]+)";
82
+ index = closeIndex;
83
+ continue;
84
+ }
85
+ }
86
+ expression += char.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
87
+ }
88
+ expression += "$";
89
+ const match = normalizePath(path).match(new RegExp(expression));
90
+ if (!match)
91
+ return null;
92
+ const params = {};
93
+ for (let index = 0; index < keys.length; index++) {
94
+ const raw = match[index + 1];
95
+ if (raw)
96
+ params[keys[index]] = decodeURIComponent(raw);
97
+ }
98
+ return params;
99
+ }
100
+ export function extractCmsSourceRouteParams(policy, resource, path) {
101
+ const resolvedPolicy = resolveCmsRoutePolicy({ routes: policy });
102
+ const candidates = [
103
+ resolvedPolicy.sourceResources[resource],
104
+ resolvedPolicy.resources[resource],
105
+ DEFAULT_CMS_ROUTE_RESOURCES[resource]
106
+ ].filter((pattern) => Boolean(pattern));
107
+ for (const pattern of candidates) {
108
+ const params = extractCmsRouteParams(pattern, path);
109
+ if (params)
110
+ return params;
111
+ }
112
+ return null;
113
+ }
114
+ export function mergeCmsRouteParams(left, right) {
115
+ return {
116
+ ...left ?? {},
117
+ ...right ?? {}
118
+ };
119
+ }
@@ -0,0 +1,20 @@
1
+ import type { SitemapEntry } from '@vigilkids/cms-client/types';
2
+ import type { CmsRoutePolicy } from '../../types';
3
+ export declare function cmsLocale(localeMap: Record<string, string> | undefined, locale: string): string;
4
+ export declare function publicLocale(localeMap: Record<string, string> | undefined, locale: string): string;
5
+ export declare function sitemapPath(entry: SitemapEntry, locale: string, routePolicy: CmsRoutePolicy): string;
6
+ export declare function toNuxtSitemapEntry(entry: SitemapEntry, locale: string, routePolicy: CmsRoutePolicy, localeMap?: Record<string, string>): {
7
+ loc: string;
8
+ lastmod: string;
9
+ changefreq: string;
10
+ priority: number;
11
+ alternatives: {
12
+ hreflang: string;
13
+ href: string;
14
+ }[] | undefined;
15
+ images: {
16
+ loc: string;
17
+ title: string | undefined;
18
+ }[] | undefined;
19
+ };
20
+ //# sourceMappingURL=sitemap.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sitemap.d.ts","sourceRoot":"","sources":["../../../src/runtime/utils/sitemap.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAA;AAC/D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAGjD,wBAAgB,SAAS,CAAC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,EAAE,MAAM,EAAE,MAAM,UAEtF;AAED,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,EAAE,MAAM,EAAE,MAAM,UAGzF;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,cAAc,UAY3F;AAED,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,YAAY,EACnB,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,cAAc,EAC3B,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;;;;;;;;;;;;;EAwBnC"}
@@ -0,0 +1,44 @@
1
+ import { buildCmsPath, extractCmsSourceRouteParams, mergeCmsRouteParams, resolveCmsRoutePolicy } from "./routes.js";
2
+ export function cmsLocale(localeMap, locale) {
3
+ return localeMap?.[locale] ?? locale;
4
+ }
5
+ export function publicLocale(localeMap, locale) {
6
+ const match = Object.entries(localeMap ?? {}).find(([, value]) => value === locale);
7
+ return match?.[0] ?? locale;
8
+ }
9
+ export function sitemapPath(entry, locale, routePolicy) {
10
+ const resource = entry.resource_type || "article";
11
+ const sourceParams = entry.route_params ?? extractCmsSourceRouteParams(routePolicy, resource, entry.loc);
12
+ if (!sourceParams)
13
+ return entry.loc;
14
+ return buildCmsPath(
15
+ resolveCmsRoutePolicy({ routes: routePolicy }),
16
+ resource,
17
+ mergeCmsRouteParams(sourceParams, { locale }),
18
+ { includeLocale: true }
19
+ );
20
+ }
21
+ export function toNuxtSitemapEntry(entry, locale, routePolicy, localeMap) {
22
+ return {
23
+ loc: sitemapPath(entry, locale, routePolicy),
24
+ lastmod: entry.last_mod,
25
+ changefreq: entry.change_freq,
26
+ priority: entry.priority,
27
+ alternatives: entry.hreflang?.map((h) => ({
28
+ hreflang: h.lang,
29
+ href: sitemapPath(
30
+ {
31
+ ...entry,
32
+ loc: h.href,
33
+ route_params: h.route_params
34
+ },
35
+ publicLocale(localeMap, h.lang),
36
+ routePolicy
37
+ )
38
+ })),
39
+ images: entry.images?.map((image) => ({
40
+ loc: image.loc,
41
+ title: image.title
42
+ }))
43
+ };
44
+ }