@cntrl-site/sdk 1.26.5 → 1.27.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.
@@ -11,6 +11,7 @@ export declare class Client {
11
11
  private static getPageMeta;
12
12
  getPageData(pageSlug: string, buildMode?: 'default' | 'self-hosted'): Promise<CntrlPageData>;
13
13
  getProjectPagesPaths(): Promise<string[]>;
14
+ getProject(buildMode?: 'default' | 'self-hosted'): Promise<Project>;
14
15
  getLayouts(): Promise<Layout[]>;
15
16
  fetchCustomComponents(buildMode?: 'default' | 'self-hosted'): Promise<CustomComponentMeta[]>;
16
17
  fetchCustomComponentBundle(componentId: string, buildMode?: 'default' | 'self-hosted'): Promise<string>;
@@ -70,6 +70,11 @@ class Client {
70
70
  }
71
71
  });
72
72
  }
73
+ getProject() {
74
+ return __awaiter(this, arguments, void 0, function* (buildMode = 'default') {
75
+ return this.fetchProject(buildMode);
76
+ });
77
+ }
73
78
  getLayouts() {
74
79
  return __awaiter(this, void 0, void 0, function* () {
75
80
  try {
package/lib/cli.js CHANGED
@@ -46,6 +46,135 @@ commander_1.program
46
46
  process.exit(1);
47
47
  }
48
48
  }));
49
+ commander_1.program
50
+ .command('generate-sitemap')
51
+ .description('Generate sitemap.xml and robots.txt for the published site')
52
+ .option('-o, --output <outputDir>', 'Output directory', 'public')
53
+ .option('-e, --env <envFilename>', 'Name of the .env file', '.env.local')
54
+ .action((options) => __awaiter(void 0, void 0, void 0, function* () {
55
+ try {
56
+ (0, dotenv_1.config)({ path: options.env });
57
+ const apiUrl = process.env.CNTRL_API_URL;
58
+ if (!apiUrl) {
59
+ console.warn('[sitemap] CNTRL_API_URL is not set — skipping sitemap/robots generation.');
60
+ return;
61
+ }
62
+ const client = new Client_1.Client(apiUrl);
63
+ const project = yield client.getProject();
64
+ const siteUrl = resolveSiteUrl(project.primaryDomain, process.env.SITE_URL);
65
+ if (!siteUrl) {
66
+ console.warn('[sitemap] Could not resolve a canonical site URL — skipping sitemap/robots generation.\n' +
67
+ ' The project has no primary domain in the API response and SITE_URL is not set.\n' +
68
+ ' Either publish the site (so a primary domain exists) or set SITE_URL.');
69
+ return;
70
+ }
71
+ const indexable = project.pages.filter(isIndexablePage);
72
+ const fallbackLastmod = formatLastmod(new Date().toISOString());
73
+ const outputDir = path_1.default.resolve(process.cwd(), options.output);
74
+ if (!fs_1.default.existsSync(outputDir)) {
75
+ fs_1.default.mkdirSync(outputDir, { recursive: true });
76
+ }
77
+ const sitemapPath = path_1.default.resolve(outputDir, 'sitemap.xml');
78
+ const robotsPath = path_1.default.resolve(outputDir, 'robots.txt');
79
+ fs_1.default.writeFileSync(sitemapPath, renderSitemap(indexable, siteUrl, fallbackLastmod));
80
+ fs_1.default.writeFileSync(robotsPath, renderRobots(siteUrl));
81
+ console.log(`Generated sitemap.xml at ${sitemapPath} (${indexable.length} of ${project.pages.length} pages)`);
82
+ console.log(`Generated robots.txt at ${robotsPath}`);
83
+ }
84
+ catch (error) {
85
+ console.warn(`[sitemap] Failed to generate sitemap/robots: ${(error === null || error === void 0 ? void 0 : error.message) || error}`);
86
+ console.warn('[sitemap] Continuing without updated sitemap.');
87
+ }
88
+ }));
89
+ function resolveSiteUrl(primaryDomain, override) {
90
+ const host = override || primaryDomain;
91
+ if (!host)
92
+ return null;
93
+ return parseAsUrl(host);
94
+ }
95
+ function isIndexablePage(page) {
96
+ if (typeof page.slug !== 'string')
97
+ return false;
98
+ if (page.isPublished === false)
99
+ return false;
100
+ if (page.isAuthProtected === true)
101
+ return false;
102
+ return true;
103
+ }
104
+ function renderSitemap(pages, siteUrl, fallbackLastmod) {
105
+ const entries = pages.map(page => {
106
+ const loc = escapeXml(buildPageUrl(siteUrl, page.slug));
107
+ const lastmod = page.lastModified ? formatLastmod(page.lastModified) : fallbackLastmod;
108
+ const priority = page.slug === '' ? '1.00' : '0.80';
109
+ return [
110
+ ' <url>',
111
+ ` <loc>${loc}</loc>`,
112
+ ` <lastmod>${lastmod}</lastmod>`,
113
+ ` <priority>${priority}</priority>`,
114
+ ' </url>'
115
+ ].join('\n');
116
+ });
117
+ return [
118
+ '<?xml version="1.0" encoding="UTF-8"?>',
119
+ '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">',
120
+ ...entries,
121
+ '</urlset>',
122
+ ''
123
+ ].join('\n');
124
+ }
125
+ function formatLastmod(iso) {
126
+ // YYYY-MM-DD is sitemap-spec valid and avoids hour-of-rebuild noise.
127
+ return iso.slice(0, 10);
128
+ }
129
+ function renderRobots(siteUrl) {
130
+ const sitemapUrl = new URL('/sitemap.xml', siteUrl.origin).toString();
131
+ return [
132
+ 'User-agent: *',
133
+ 'Allow: /',
134
+ '',
135
+ `Sitemap: ${sitemapUrl}`,
136
+ ''
137
+ ].join('\n');
138
+ }
139
+ function buildPageUrl(siteUrl, slug) {
140
+ const base = new URL(siteUrl.href);
141
+ if (!base.pathname.endsWith('/'))
142
+ base.pathname += '/';
143
+ const relative = stripSlashes(slug);
144
+ const target = relative ? new URL(`${relative}/`, base) : base;
145
+ target.search = '';
146
+ target.hash = '';
147
+ return target.toString();
148
+ }
149
+ function parseAsUrl(input) {
150
+ var _a;
151
+ return (_a = tryParseUrl(input)) !== null && _a !== void 0 ? _a : tryParseUrl(`https://${input}`);
152
+ }
153
+ function tryParseUrl(input) {
154
+ try {
155
+ return new URL(input);
156
+ }
157
+ catch (_a) {
158
+ return null;
159
+ }
160
+ }
161
+ function stripSlashes(value) {
162
+ let start = 0;
163
+ let end = value.length;
164
+ while (start < end && value[start] === '/')
165
+ start += 1;
166
+ while (end > start && value[end - 1] === '/')
167
+ end -= 1;
168
+ return value.slice(start, end);
169
+ }
170
+ function escapeXml(value) {
171
+ return value
172
+ .replace(/&/g, '&amp;')
173
+ .replace(/</g, '&lt;')
174
+ .replace(/>/g, '&gt;')
175
+ .replace(/"/g, '&quot;')
176
+ .replace(/'/g, '&apos;');
177
+ }
49
178
  function convertLayouts(layouts, maxLayoutWidth = Number.MAX_SAFE_INTEGER) {
50
179
  const sorted = layouts.slice().sort((la, lb) => la.startsWith - lb.startsWith);
51
180
  const mapped = sorted.map((layout, i, ls) => {
@@ -74,6 +74,9 @@ export declare const ProjectSchema: z.ZodObject<{
74
74
  keywords?: string | undefined;
75
75
  }>>;
76
76
  id: z.ZodString;
77
+ isPublished: z.ZodOptional<z.ZodBoolean>;
78
+ isAuthProtected: z.ZodOptional<z.ZodBoolean>;
79
+ lastModified: z.ZodOptional<z.ZodString>;
77
80
  }, "strip", z.ZodTypeAny, {
78
81
  id: string;
79
82
  title: string;
@@ -86,6 +89,9 @@ export declare const ProjectSchema: z.ZodObject<{
86
89
  opengraphThumbnail?: string | undefined;
87
90
  keywords?: string | undefined;
88
91
  } | undefined;
92
+ isPublished?: boolean | undefined;
93
+ isAuthProtected?: boolean | undefined;
94
+ lastModified?: string | undefined;
89
95
  }, {
90
96
  id: string;
91
97
  title: string;
@@ -98,6 +104,9 @@ export declare const ProjectSchema: z.ZodObject<{
98
104
  opengraphThumbnail?: string | undefined;
99
105
  keywords?: string | undefined;
100
106
  } | undefined;
107
+ isPublished?: boolean | undefined;
108
+ isAuthProtected?: boolean | undefined;
109
+ lastModified?: string | undefined;
101
110
  }>, "many">;
102
111
  fonts: z.ZodObject<{
103
112
  google: z.ZodString;
@@ -191,6 +200,9 @@ export declare const ProjectSchema: z.ZodObject<{
191
200
  opengraphThumbnail?: string | undefined;
192
201
  keywords?: string | undefined;
193
202
  } | undefined;
203
+ isPublished?: boolean | undefined;
204
+ isAuthProtected?: boolean | undefined;
205
+ lastModified?: string | undefined;
194
206
  }[];
195
207
  fonts: {
196
208
  custom: {
@@ -238,6 +250,9 @@ export declare const ProjectSchema: z.ZodObject<{
238
250
  opengraphThumbnail?: string | undefined;
239
251
  keywords?: string | undefined;
240
252
  } | undefined;
253
+ isPublished?: boolean | undefined;
254
+ isAuthProtected?: boolean | undefined;
255
+ lastModified?: string | undefined;
241
256
  }[];
242
257
  fonts: {
243
258
  custom: {
@@ -30,7 +30,10 @@ exports.ProjectSchema = zod_1.z.object({
30
30
  keywords: zod_1.z.string().optional(),
31
31
  enabled: zod_1.z.boolean()
32
32
  }).optional(),
33
- id: zod_1.z.string().min(1)
33
+ id: zod_1.z.string().min(1),
34
+ isPublished: zod_1.z.boolean().optional(),
35
+ isAuthProtected: zod_1.z.boolean().optional(),
36
+ lastModified: zod_1.z.string().optional()
34
37
  })),
35
38
  fonts: zod_1.z.object({
36
39
  google: zod_1.z.string(),
@@ -8,4 +8,7 @@ export interface Page {
8
8
  articleId: string;
9
9
  slug: string;
10
10
  meta?: PageMeta;
11
+ isPublished?: boolean;
12
+ isAuthProtected?: boolean;
13
+ lastModified?: string;
11
14
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cntrl-site/sdk",
3
- "version": "1.26.5",
3
+ "version": "1.27.1",
4
4
  "description": "Generic SDK for use in public websites.",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",