@cntrl-site/sdk 1.26.4 → 1.27.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.
@@ -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,130 @@ 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 lastmod = 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, lastmod));
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, lastmod) {
105
+ const entries = pages.map(page => {
106
+ const loc = escapeXml(buildPageUrl(siteUrl, page.slug));
107
+ const priority = page.slug === '' ? '1.00' : '0.80';
108
+ return [
109
+ ' <url>',
110
+ ` <loc>${loc}</loc>`,
111
+ ` <lastmod>${lastmod}</lastmod>`,
112
+ ` <priority>${priority}</priority>`,
113
+ ' </url>'
114
+ ].join('\n');
115
+ });
116
+ return [
117
+ '<?xml version="1.0" encoding="UTF-8"?>',
118
+ '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">',
119
+ ...entries,
120
+ '</urlset>',
121
+ ''
122
+ ].join('\n');
123
+ }
124
+ function renderRobots(siteUrl) {
125
+ const sitemapUrl = new URL('/sitemap.xml', siteUrl.origin).toString();
126
+ return [
127
+ 'User-agent: *',
128
+ 'Allow: /',
129
+ '',
130
+ `Sitemap: ${sitemapUrl}`,
131
+ ''
132
+ ].join('\n');
133
+ }
134
+ function buildPageUrl(siteUrl, slug) {
135
+ const base = new URL(siteUrl.href);
136
+ if (!base.pathname.endsWith('/'))
137
+ base.pathname += '/';
138
+ const relative = stripSlashes(slug);
139
+ const target = relative ? new URL(`${relative}/`, base) : base;
140
+ target.search = '';
141
+ target.hash = '';
142
+ return target.toString();
143
+ }
144
+ function parseAsUrl(input) {
145
+ var _a;
146
+ return (_a = tryParseUrl(input)) !== null && _a !== void 0 ? _a : tryParseUrl(`https://${input}`);
147
+ }
148
+ function tryParseUrl(input) {
149
+ try {
150
+ return new URL(input);
151
+ }
152
+ catch (_a) {
153
+ return null;
154
+ }
155
+ }
156
+ function stripSlashes(value) {
157
+ let start = 0;
158
+ let end = value.length;
159
+ while (start < end && value[start] === '/')
160
+ start += 1;
161
+ while (end > start && value[end - 1] === '/')
162
+ end -= 1;
163
+ return value.slice(start, end);
164
+ }
165
+ function escapeXml(value) {
166
+ return value
167
+ .replace(/&/g, '&amp;')
168
+ .replace(/</g, '&lt;')
169
+ .replace(/>/g, '&gt;')
170
+ .replace(/"/g, '&quot;')
171
+ .replace(/'/g, '&apos;');
172
+ }
49
173
  function convertLayouts(layouts, maxLayoutWidth = Number.MAX_SAFE_INTEGER) {
50
174
  const sorted = layouts.slice().sort((la, lb) => la.startsWith - lb.startsWith);
51
175
  const mapped = sorted.map((layout, i, ls) => {
@@ -74,6 +74,8 @@ 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>;
77
79
  }, "strip", z.ZodTypeAny, {
78
80
  id: string;
79
81
  title: string;
@@ -86,6 +88,8 @@ export declare const ProjectSchema: z.ZodObject<{
86
88
  opengraphThumbnail?: string | undefined;
87
89
  keywords?: string | undefined;
88
90
  } | undefined;
91
+ isPublished?: boolean | undefined;
92
+ isAuthProtected?: boolean | undefined;
89
93
  }, {
90
94
  id: string;
91
95
  title: string;
@@ -98,6 +102,8 @@ export declare const ProjectSchema: z.ZodObject<{
98
102
  opengraphThumbnail?: string | undefined;
99
103
  keywords?: string | undefined;
100
104
  } | undefined;
105
+ isPublished?: boolean | undefined;
106
+ isAuthProtected?: boolean | undefined;
101
107
  }>, "many">;
102
108
  fonts: z.ZodObject<{
103
109
  google: z.ZodString;
@@ -158,6 +164,7 @@ export declare const ProjectSchema: z.ZodObject<{
158
164
  google: string;
159
165
  adobe: string;
160
166
  }>;
167
+ primaryDomain: z.ZodOptional<z.ZodNullable<z.ZodString>>;
161
168
  }, "strip", z.ZodTypeAny, {
162
169
  html: {
163
170
  head: string;
@@ -190,6 +197,8 @@ export declare const ProjectSchema: z.ZodObject<{
190
197
  opengraphThumbnail?: string | undefined;
191
198
  keywords?: string | undefined;
192
199
  } | undefined;
200
+ isPublished?: boolean | undefined;
201
+ isAuthProtected?: boolean | undefined;
193
202
  }[];
194
203
  fonts: {
195
204
  custom: {
@@ -204,6 +213,7 @@ export declare const ProjectSchema: z.ZodObject<{
204
213
  google: string;
205
214
  adobe: string;
206
215
  };
216
+ primaryDomain?: string | null | undefined;
207
217
  }, {
208
218
  html: {
209
219
  head: string;
@@ -236,6 +246,8 @@ export declare const ProjectSchema: z.ZodObject<{
236
246
  opengraphThumbnail?: string | undefined;
237
247
  keywords?: string | undefined;
238
248
  } | undefined;
249
+ isPublished?: boolean | undefined;
250
+ isAuthProtected?: boolean | undefined;
239
251
  }[];
240
252
  fonts: {
241
253
  custom: {
@@ -250,4 +262,5 @@ export declare const ProjectSchema: z.ZodObject<{
250
262
  google: string;
251
263
  adobe: string;
252
264
  };
265
+ primaryDomain?: string | null | undefined;
253
266
  }>;
@@ -30,7 +30,9 @@ 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()
34
36
  })),
35
37
  fonts: zod_1.z.object({
36
38
  google: zod_1.z.string(),
@@ -44,5 +46,6 @@ exports.ProjectSchema = zod_1.z.object({
44
46
  url: zod_1.z.string()
45
47
  }))
46
48
  }))
47
- })
49
+ }),
50
+ primaryDomain: zod_1.z.string().nullable().optional()
48
51
  });
@@ -8,4 +8,6 @@ export interface Page {
8
8
  articleId: string;
9
9
  slug: string;
10
10
  meta?: PageMeta;
11
+ isPublished?: boolean;
12
+ isAuthProtected?: boolean;
11
13
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cntrl-site/sdk",
3
- "version": "1.26.4",
3
+ "version": "1.27.0",
4
4
  "description": "Generic SDK for use in public websites.",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",