@djangocfg/nextjs 2.1.411 → 2.1.413

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/dist/index.mjs CHANGED
@@ -14,7 +14,7 @@ var require_package = __commonJS({
14
14
  "package.json"(exports, module) {
15
15
  module.exports = {
16
16
  name: "@djangocfg/nextjs",
17
- version: "2.1.411",
17
+ version: "2.1.413",
18
18
  description: "Next.js server utilities: sitemap, health, OG images, contact forms, navigation, config",
19
19
  keywords: [
20
20
  "nextjs",
@@ -204,155 +204,125 @@ var require_package = __commonJS({
204
204
  }
205
205
  });
206
206
 
207
- // src/sitemap/route.ts
208
- import { NextResponse } from "next/server";
209
-
210
- // src/sitemap/generator.ts
211
- function generateSitemapXml(urlsOrOptions) {
212
- const isOptionsObject = !Array.isArray(urlsOrOptions);
213
- const urls = isOptionsObject ? urlsOrOptions.urls : urlsOrOptions;
214
- const i18n = isOptionsObject ? urlsOrOptions.i18n : void 0;
215
- const siteUrl = isOptionsObject ? urlsOrOptions.siteUrl : "";
216
- const namespaces = i18n ? `xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
217
- xmlns:xhtml="http://www.w3.org/1999/xhtml"
218
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
219
- xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9
220
- http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd"` : `xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
221
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
222
- xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9
223
- http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd"`;
224
- return `<?xml version="1.0" encoding="UTF-8"?>
225
- <urlset ${namespaces}>
226
- ${urls.map(({ loc, lastmod, changefreq, priority }) => {
227
- const hreflangLinks = i18n ? generateHreflangLinks(loc, i18n, siteUrl) : "";
228
- return ` <url>
229
- <loc>${escapeXml(loc)}</loc>${hreflangLinks}
230
- ${lastmod ? `<lastmod>${formatDate(lastmod)}</lastmod>` : ""}
231
- ${changefreq ? `<changefreq>${changefreq}</changefreq>` : ""}
232
- ${priority !== void 0 ? `<priority>${priority.toFixed(1)}</priority>` : ""}
233
- </url>`;
234
- }).join("\n")}
235
- </urlset>`;
236
- }
237
- function generateHreflangLinks(loc, i18n, siteUrl) {
238
- const { locales, defaultLocale } = i18n;
239
- const baseSiteUrl = siteUrl.endsWith("/") ? siteUrl.slice(0, -1) : siteUrl;
240
- let path = loc.replace(baseSiteUrl, "");
241
- for (const locale of locales) {
242
- const localePrefix = `/${locale}`;
243
- if (path === localePrefix || path.startsWith(`${localePrefix}/`)) {
244
- path = path.slice(localePrefix.length) || "/";
245
- break;
207
+ // src/sitemap/fetch.ts
208
+ var EMPTY_INDEX = {
209
+ sources: [],
210
+ generated_at: (/* @__PURE__ */ new Date(0)).toISOString(),
211
+ ttl_seconds: 60
212
+ };
213
+ var emptyFeed = (source, cursor) => ({
214
+ source,
215
+ chunk_id: `${source}-empty`,
216
+ count: 0,
217
+ has_more: false,
218
+ next_cursor: cursor,
219
+ entries: []
220
+ });
221
+ async function fetchSitemapIndex(apiUrl, revalidate) {
222
+ try {
223
+ const r = await fetch(`${apiUrl}/cfg/sitemap/index/`, {
224
+ next: { revalidate }
225
+ });
226
+ if (!r.ok) {
227
+ console.warn(`[sitemap] index fetch returned ${r.status}; falling back to empty`);
228
+ return EMPTY_INDEX;
246
229
  }
230
+ return await r.json();
231
+ } catch (err) {
232
+ console.warn("[sitemap] index fetch failed:", err);
233
+ return EMPTY_INDEX;
247
234
  }
248
- const links = [];
249
- for (const locale of locales) {
250
- const localePath = locale === defaultLocale ? path : path === "/" ? `/${locale}` : `/${locale}${path}`;
251
- const fullUrl = `${baseSiteUrl}${localePath}`;
252
- links.push(
253
- ` <xhtml:link rel="alternate" hreflang="${locale}" href="${escapeXml(fullUrl)}"/>`
254
- );
255
- }
256
- const defaultUrl = `${baseSiteUrl}${path}`;
257
- links.push(
258
- ` <xhtml:link rel="alternate" hreflang="x-default" href="${escapeXml(defaultUrl)}"/>`
259
- );
260
- return "\n" + links.join("\n");
261
235
  }
262
- function formatDate(date) {
263
- if (typeof date === "string") {
264
- return date;
236
+ async function fetchSitemapFeed(apiUrl, source, cursor, revalidate) {
237
+ const params = new URLSearchParams({ source });
238
+ if (cursor) params.set("cursor", cursor);
239
+ try {
240
+ const r = await fetch(`${apiUrl}/cfg/sitemap/feed/?${params}`, {
241
+ next: { revalidate }
242
+ });
243
+ if (!r.ok) {
244
+ console.warn(`[sitemap] feed ${source} returned ${r.status}; falling back to empty`);
245
+ return emptyFeed(source, cursor);
246
+ }
247
+ return await r.json();
248
+ } catch (err) {
249
+ console.warn(`[sitemap] feed ${source} fetch failed:`, err);
250
+ return emptyFeed(source, cursor);
265
251
  }
266
- return date.toISOString().split("T")[0];
267
252
  }
268
- function escapeXml(unsafe) {
269
- return unsafe.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
253
+
254
+ // src/sitemap/ids.ts
255
+ function encodeChunkId(source, cursor) {
256
+ return `${source}--${cursor ?? ""}`;
270
257
  }
271
- function normalizeUrl(url, siteUrl) {
272
- if (url.startsWith("http://") || url.startsWith("https://")) {
273
- return url;
274
- }
275
- const baseUrl = siteUrl.endsWith("/") ? siteUrl.slice(0, -1) : siteUrl;
276
- const path = url.startsWith("/") ? url : `/${url}`;
277
- return `${baseUrl}${path}`;
258
+ function decodeChunkId(id) {
259
+ const sep = id.indexOf("--");
260
+ if (sep === -1) return { source: id, cursor: null };
261
+ const source = id.slice(0, sep);
262
+ const cursorRaw = id.slice(sep + 2);
263
+ return { source, cursor: cursorRaw === "" ? null : cursorRaw };
278
264
  }
279
265
 
280
- // src/sitemap/route.ts
281
- function createSitemapHandler(options) {
266
+ // src/sitemap/sitemap.ts
267
+ var STATIC_ID = "static";
268
+ var DEFAULT_INDEX_REVALIDATE = 600;
269
+ var DEFAULT_FEED_REVALIDATE = 3600;
270
+ function createDjangoSitemap(opts) {
282
271
  const {
283
- siteUrl,
284
- staticPages = [],
285
- dynamicPages = [],
286
- cacheControl = "public, s-maxage=86400, stale-while-revalidate",
287
- i18n
288
- } = options;
289
- return async function GET() {
290
- const urls = [...staticPages];
291
- if (dynamicPages) {
292
- if (typeof dynamicPages === "function") {
293
- const dynamicUrls = await dynamicPages();
294
- urls.push(...dynamicUrls);
295
- } else {
296
- urls.push(...dynamicPages);
272
+ host,
273
+ apiUrl,
274
+ staticRoutes = [],
275
+ indexRevalidate = DEFAULT_INDEX_REVALIDATE,
276
+ feedRevalidate = DEFAULT_FEED_REVALIDATE
277
+ } = opts;
278
+ return {
279
+ async generateSitemaps() {
280
+ const ids = [{ id: STATIC_ID }];
281
+ if (!apiUrl) return ids;
282
+ const index = await fetchSitemapIndex(apiUrl, indexRevalidate);
283
+ for (const s of index.sources) {
284
+ for (const c of s.chunks) {
285
+ ids.push({ id: encodeChunkId(s.name, c.cursor_to) });
286
+ }
297
287
  }
298
- }
299
- let expandedUrls;
300
- if (i18n) {
301
- expandedUrls = expandUrlsForLocales(urls, i18n.locales, siteUrl, i18n.defaultLocale);
302
- } else {
303
- expandedUrls = urls.map((url) => ({
304
- ...url,
305
- loc: normalizeUrl(url.loc, siteUrl)
288
+ return ids;
289
+ },
290
+ async sitemap({ id: idPromise }) {
291
+ const id = await idPromise;
292
+ if (id === STATIC_ID) {
293
+ return renderStatic(host, staticRoutes);
294
+ }
295
+ if (!apiUrl) return [];
296
+ const { source, cursor } = decodeChunkId(id);
297
+ const feed = await fetchSitemapFeed(apiUrl, source, cursor, feedRevalidate);
298
+ return feed.entries.map((e) => ({
299
+ url: `${host}${e.loc}`,
300
+ lastModified: e.lastmod ? new Date(e.lastmod) : void 0
306
301
  }));
307
302
  }
308
- const sitemap = generateSitemapXml({
309
- urls: expandedUrls,
310
- i18n,
311
- siteUrl
312
- });
313
- return new NextResponse(sitemap, {
314
- status: 200,
315
- headers: {
316
- "Content-Type": "application/xml",
317
- "Cache-Control": cacheControl
318
- }
319
- });
320
303
  };
321
304
  }
322
- function expandUrlsForLocales(urls, locales, siteUrl, defaultLocale) {
323
- const baseSiteUrl = siteUrl.endsWith("/") ? siteUrl.slice(0, -1) : siteUrl;
324
- const expandedUrls = [];
325
- for (const url of urls) {
326
- let path = url.loc;
327
- if (path.startsWith(baseSiteUrl)) {
328
- path = path.replace(baseSiteUrl, "");
329
- }
330
- if (!path.startsWith("/")) {
331
- path = "/" + path;
332
- }
333
- const hasLocalePrefix = locales.some(
334
- (locale) => path === `/${locale}` || path.startsWith(`/${locale}/`)
335
- );
336
- if (hasLocalePrefix) {
337
- expandedUrls.push({
338
- ...url,
339
- loc: normalizeUrl(path, siteUrl)
340
- });
341
- } else {
342
- for (const locale of locales) {
343
- const localePath = locale === defaultLocale ? path : path === "/" ? `/${locale}` : `/${locale}${path}`;
344
- expandedUrls.push({
345
- ...url,
346
- loc: `${baseSiteUrl}${localePath}`
347
- });
348
- }
349
- }
350
- }
351
- return expandedUrls;
305
+ function renderStatic(host, routes) {
306
+ return routes.map((r) => ({
307
+ url: `${host}${r.path}`,
308
+ changeFrequency: r.changeFrequency,
309
+ priority: r.priority
310
+ }));
311
+ }
312
+
313
+ // src/sitemap/robots.ts
314
+ var DEFAULT_DISALLOW = ["/account/", "/auth", "/api/"];
315
+ function createRobots(opts) {
316
+ const { host, disallow = DEFAULT_DISALLOW, sitemap } = opts;
317
+ return () => ({
318
+ rules: [{ userAgent: "*", allow: "/", disallow }],
319
+ sitemap: sitemap ?? `${host}/sitemap.xml`,
320
+ host
321
+ });
352
322
  }
353
323
 
354
324
  // src/health/route.ts
355
- import { NextResponse as NextResponse2 } from "next/server";
325
+ import { NextResponse } from "next/server";
356
326
  function createHealthHandler(config = {}) {
357
327
  const { version, checks = [], customData = {} } = config;
358
328
  return async function GET() {
@@ -382,7 +352,7 @@ function createHealthHandler(config = {}) {
382
352
  ...customData
383
353
  };
384
354
  const statusCode = status === "ok" ? 200 : 503;
385
- return NextResponse2.json(response, { status: statusCode });
355
+ return NextResponse.json(response, { status: statusCode });
386
356
  };
387
357
  }
388
358
 
@@ -1900,15 +1870,19 @@ export {
1900
1870
  checkForUpdates,
1901
1871
  checkPackages,
1902
1872
  createBaseNextConfig,
1873
+ createDjangoSitemap,
1903
1874
  createHealthHandler,
1904
- createSitemapHandler,
1875
+ createRobots,
1876
+ decodeChunkId,
1905
1877
  deepMerge,
1906
1878
  defineRoute,
1907
1879
  detectPackageManager,
1880
+ encodeChunkId,
1908
1881
  fetchLatestVersion2 as fetchLatestVersion,
1882
+ fetchSitemapFeed,
1883
+ fetchSitemapIndex,
1909
1884
  findRoute,
1910
1885
  findRouteByPattern,
1911
- generateSitemapXml,
1912
1886
  getApiUrl,
1913
1887
  getBasePath,
1914
1888
  getCurrentVersion,
@@ -1933,7 +1907,6 @@ export {
1933
1907
  isPackageInstalled,
1934
1908
  isProduction,
1935
1909
  isStaticBuild,
1936
- normalizeUrl,
1937
1910
  printVersionInfo,
1938
1911
  redirectToAuth,
1939
1912
  resetDevStartupState,