@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.
package/lib/Client/Client.d.ts
CHANGED
|
@@ -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>;
|
package/lib/Client/Client.js
CHANGED
|
@@ -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, '&')
|
|
173
|
+
.replace(/</g, '<')
|
|
174
|
+
.replace(/>/g, '>')
|
|
175
|
+
.replace(/"/g, '"')
|
|
176
|
+
.replace(/'/g, ''');
|
|
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(),
|