@cntrl-site/sdk 1.26.5 → 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.
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,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, '&')
|
|
168
|
+
.replace(/</g, '<')
|
|
169
|
+
.replace(/>/g, '>')
|
|
170
|
+
.replace(/"/g, '"')
|
|
171
|
+
.replace(/'/g, ''');
|
|
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;
|
|
@@ -191,6 +197,8 @@ export declare const ProjectSchema: z.ZodObject<{
|
|
|
191
197
|
opengraphThumbnail?: string | undefined;
|
|
192
198
|
keywords?: string | undefined;
|
|
193
199
|
} | undefined;
|
|
200
|
+
isPublished?: boolean | undefined;
|
|
201
|
+
isAuthProtected?: boolean | undefined;
|
|
194
202
|
}[];
|
|
195
203
|
fonts: {
|
|
196
204
|
custom: {
|
|
@@ -238,6 +246,8 @@ export declare const ProjectSchema: z.ZodObject<{
|
|
|
238
246
|
opengraphThumbnail?: string | undefined;
|
|
239
247
|
keywords?: string | undefined;
|
|
240
248
|
} | undefined;
|
|
249
|
+
isPublished?: boolean | undefined;
|
|
250
|
+
isAuthProtected?: boolean | undefined;
|
|
241
251
|
}[];
|
|
242
252
|
fonts: {
|
|
243
253
|
custom: {
|
|
@@ -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(),
|