@djangocfg/nextjs 2.1.541 → 2.1.543

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/README.md CHANGED
@@ -122,8 +122,9 @@ the box — no setup required:
122
122
  - `Content-Security-Policy` — a pragmatic baseline that blocks the high-value
123
123
  injection vectors (`object-src 'none'`, `base-uri 'self'`, `form-action 'self'`,
124
124
  `frame-ancestors`) while keeping the inline/eval scripts a Next.js app needs.
125
- **Dev-aware:** `connect-src`/`img-src` allow `http:`/`ws:` in development (local
126
- Django/Centrifugo) and tighten to `https:`/`wss:` only in production.
125
+ **Dev-aware:** `connect-src`/`img-src` allow `http:`/`ws:` in development (a
126
+ local Django, plus any WebSocket transport the app brings itself) and tighten
127
+ to `https:`/`wss:` only in production.
127
128
  - `X-Content-Type-Options: nosniff`, `Referrer-Policy: strict-origin-when-cross-origin`
128
129
  - `X-Frame-Options: DENY` (or `SAMEORIGIN` when `allowIframeFrom` is set)
129
130
 
@@ -128,11 +128,14 @@ type CspVendor = keyof typeof CSP_VENDOR_PRESETS;
128
128
  * (Observed 2026-07-23: cmdop `apps/web` blocked on
129
129
  * `googletagmanager.com/gtag/js?id=G-YJ0F48S83F`.)
130
130
  *
131
- * - `youtube` / `vimeo` — `VideoPlayer` (`@djangocfg/ui-tools`) mounts these
132
- * iframes AT RUNTIME from a URL inside a chat message (see
133
- * `chat/messages/blocks/renderers/VideoBlock.tsx`). The app author enables a
134
- * chat; whether any user ever pastes a YouTube link is not knowable at build
135
- * time, so no `next.config.ts` can declare it honestly.
131
+ * - `youtube` / `vimeo` — these existed for `VideoPlayer`
132
+ * (`@djangocfg/ui-tools`), which mounted the iframes AT RUNTIME from a URL
133
+ * inside a chat message. That player was retired with `ui-tools`;
134
+ * `@djangocfg/widget-player` embeds no third-party frames, so **nothing in
135
+ * this repo needs these presets today**. They are kept because the shape of
136
+ * the problem outlives the component: an app author enables a chat, and
137
+ * whether a user ever pastes a YouTube link is not knowable at build time,
138
+ * so no `next.config.ts` can declare it honestly.
136
139
  *
137
140
  * Note what these have in common: the script/frame is turned on by the RUNTIME
138
141
  * (the edge, a package, or an end user), never by the app author editing the
@@ -179,7 +182,17 @@ declare function resolveCspVendors(vendors: readonly string[] | undefined, warnU
179
182
  * ```
180
183
  */
181
184
 
182
- interface BaseNextConfigOptions {
185
+ /**
186
+ * Second argument Next.js passes to the `webpack` hook. Derived from Next's own
187
+ * signature rather than re-declared, so it tracks upstream.
188
+ */
189
+ type WebpackContext = Parameters<NonNullable<NextConfig['webpack']>>[1];
190
+ /**
191
+ * DjangoCFG-specific options. Every other Next.js option passes through via the
192
+ * `NextConfig` half of `BaseNextConfigOptions`; the keys redeclared here are
193
+ * omitted from it because this package narrows or re-purposes them.
194
+ */
195
+ interface DjangoCfgNextOptions {
183
196
  /** Static build path (used when NEXT_PUBLIC_STATIC_BUILD=true) */
184
197
  isDefaultCfgAdmin?: boolean;
185
198
  /** Additional transpile packages (merged with defaults) */
@@ -311,11 +324,7 @@ interface BaseNextConfigOptions {
311
324
  /** Turbopack configuration (Next.js 16+ default bundler) */
312
325
  turbopack?: NextConfig['turbopack'];
313
326
  /** Custom webpack configuration function (called after base webpack logic) */
314
- webpack?: (config: Configuration, options: {
315
- isServer: boolean;
316
- dev: boolean;
317
- [key: string]: any;
318
- }) => Configuration | void;
327
+ webpack?: (config: Configuration, options: WebpackContext) => Configuration | void;
319
328
  /** Custom experimental options (merged with defaults) */
320
329
  experimental?: NextConfig['experimental'];
321
330
  /** Custom env variables (merged with defaults) */
@@ -326,9 +335,11 @@ interface BaseNextConfigOptions {
326
335
  headers?: NextConfig['headers'];
327
336
  /** Additional rewrites */
328
337
  rewrites?: NextConfig['rewrites'];
329
- /** Override any Next.js config option */
330
- [key: string]: any;
331
338
  }
339
+ /**
340
+ * DjangoCFG options plus every remaining Next.js config option.
341
+ */
342
+ type BaseNextConfigOptions = DjangoCfgNextOptions & Omit<NextConfig, keyof DjangoCfgNextOptions>;
332
343
  /**
333
344
  * Create base Next.js configuration with standard DjangoCFG settings
334
345
  *
@@ -342,8 +353,8 @@ declare function createBaseNextConfig(options?: BaseNextConfigOptions): NextConf
342
353
  */
343
354
  declare const PACKAGE_NAME = "@djangocfg/nextjs";
344
355
  declare const DJANGO_CFG_BANNER = "\n\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557\n\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D \u2588\u2588\u2554\u2550\u2550\u2550\u2588\u2588\u2557 \u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\n\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2554\u2588\u2588\u2557 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2551 \u2588\u2588\u2588\u2557\n\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2551\u2588\u2588\u2551\u255A\u2588\u2588\u2557\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2554\u2550\u2550\u255D \u2588\u2588\u2551 \u2588\u2588\u2551\n\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u255A\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u255A\u2588\u2588\u2588\u2588\u2551\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D \u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2551 \u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\n\u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D\n";
345
- declare const DJANGOCFG_PACKAGES: readonly ["@djangocfg/ui-core", "@djangocfg/layouts", "@djangocfg/nextjs", "@djangocfg/api", "@djangocfg/centrifugo", "@djangocfg/eslint-config", "@djangocfg/typescript-config"];
346
- declare const DEFAULT_TRANSPILE_PACKAGES: readonly ["@djangocfg/i18n", "@djangocfg/ui-core", "@djangocfg/layouts", "@djangocfg/ui-tools", "@djangocfg/api", "@djangocfg/analytics", "@djangocfg/centrifugo", "@djangocfg/devtools", "@djangocfg/payments"];
356
+ declare const DJANGOCFG_PACKAGES: readonly ["@djangocfg/ui-core", "@djangocfg/layouts", "@djangocfg/nextjs", "@djangocfg/api", "@djangocfg/eslint-config", "@djangocfg/typescript-config"];
357
+ declare const DEFAULT_TRANSPILE_PACKAGES: readonly ["@djangocfg/i18n", "@djangocfg/ui-core", "@djangocfg/layouts", "@djangocfg/api", "@djangocfg/analytics", "@djangocfg/devtools", "@djangocfg/payments", "@djangocfg/widget-agent", "@djangocfg/widget-avatar", "@djangocfg/widget-charts", "@djangocfg/widget-chat", "@djangocfg/widget-code", "@djangocfg/widget-data", "@djangocfg/widget-diagram", "@djangocfg/widget-editor", "@djangocfg/widget-forms", "@djangocfg/widget-kit", "@djangocfg/widget-map", "@djangocfg/widget-media", "@djangocfg/widget-ogimage", "@djangocfg/widget-overlay", "@djangocfg/widget-player", "@djangocfg/widget-visual"];
347
358
  declare const DEFAULT_OPTIMIZE_PACKAGES: readonly ["@djangocfg/ui-core", "@djangocfg/layouts", "lucide-react", "recharts"];
348
359
 
349
360
  /**
@@ -351,7 +362,12 @@ declare const DEFAULT_OPTIMIZE_PACKAGES: readonly ["@djangocfg/ui-core", "@djang
351
362
  *
352
363
  * Recursively merges objects, replacing arrays instead of merging them.
353
364
  */
354
- declare function deepMerge<T extends Record<string, any>>(target: T, source: Partial<T>): T;
365
+ /**
366
+ * `T extends object` rather than `Record<string, unknown>`: call sites pass
367
+ * Next.js config interfaces, which have declared keys and no index signature —
368
+ * they are not assignable to `Record<string, unknown>`.
369
+ */
370
+ declare function deepMerge<T extends object>(target: T, source: Partial<T>): T;
355
371
 
356
372
  /**
357
373
  * Environment Variable Utilities
@@ -38,7 +38,6 @@ var DJANGOCFG_PACKAGES = [
38
38
  "@djangocfg/layouts",
39
39
  "@djangocfg/nextjs",
40
40
  "@djangocfg/api",
41
- "@djangocfg/centrifugo",
42
41
  "@djangocfg/eslint-config",
43
42
  "@djangocfg/typescript-config"
44
43
  ];
@@ -46,12 +45,26 @@ var DEFAULT_TRANSPILE_PACKAGES = [
46
45
  "@djangocfg/i18n",
47
46
  "@djangocfg/ui-core",
48
47
  "@djangocfg/layouts",
49
- "@djangocfg/ui-tools",
50
48
  "@djangocfg/api",
51
49
  "@djangocfg/analytics",
52
- "@djangocfg/centrifugo",
53
50
  "@djangocfg/devtools",
54
- "@djangocfg/payments"
51
+ "@djangocfg/payments",
52
+ "@djangocfg/widget-agent",
53
+ "@djangocfg/widget-avatar",
54
+ "@djangocfg/widget-charts",
55
+ "@djangocfg/widget-chat",
56
+ "@djangocfg/widget-code",
57
+ "@djangocfg/widget-data",
58
+ "@djangocfg/widget-diagram",
59
+ "@djangocfg/widget-editor",
60
+ "@djangocfg/widget-forms",
61
+ "@djangocfg/widget-kit",
62
+ "@djangocfg/widget-map",
63
+ "@djangocfg/widget-media",
64
+ "@djangocfg/widget-ogimage",
65
+ "@djangocfg/widget-overlay",
66
+ "@djangocfg/widget-player",
67
+ "@djangocfg/widget-visual"
55
68
  ];
56
69
  var DEFAULT_OPTIMIZE_PACKAGES = [
57
70
  "@djangocfg/ui-core",
@@ -336,7 +349,7 @@ function addCompressionPlugins(config, options = {}) {
336
349
  );
337
350
  }
338
351
  return true;
339
- } catch (error) {
352
+ } catch {
340
353
  return false;
341
354
  }
342
355
  }
@@ -544,7 +557,7 @@ async function installSinglePackage(pkg, pm, index, total) {
544
557
  const spinner = createSpinner(`${chalk.dim(progress)} Installing ${chalk.cyan(pkg.name)}...`);
545
558
  spinner.start();
546
559
  return new Promise((resolve) => {
547
- const [cmd, ...args] = command.split(" ");
560
+ const [cmd = pm, ...args] = command.split(" ");
548
561
  const proc = spawn(cmd, args, {
549
562
  shell: true,
550
563
  cwd: process.cwd(),
@@ -580,13 +593,13 @@ async function installPackagesWithProgress(packages) {
580
593
  console.log(chalk.bold(` Installing ${total} package${total > 1 ? "s" : ""}...`));
581
594
  console.log("");
582
595
  let successCount = 0;
583
- let failedPackages = [];
584
- for (let i = 0; i < packages.length; i++) {
585
- const success = await installSinglePackage(packages[i], pm, i, total);
596
+ const failedPackages = [];
597
+ for (const [i, pkg] of packages.entries()) {
598
+ const success = await installSinglePackage(pkg, pm, i, total);
586
599
  if (success) {
587
600
  successCount++;
588
601
  } else {
589
- failedPackages.push(packages[i].name);
602
+ failedPackages.push(pkg.name);
590
603
  }
591
604
  }
592
605
  console.log("");
@@ -951,12 +964,12 @@ async function updatePackagesWithProgress(packages) {
951
964
  console.log("");
952
965
  let successCount = 0;
953
966
  const failedPackages = [];
954
- for (let i = 0; i < packages.length; i++) {
955
- const success = await updateSinglePackage(packages[i], pm, i, total);
967
+ for (const [i, pkg] of packages.entries()) {
968
+ const success = await updateSinglePackage(pkg, pm, i, total);
956
969
  if (success) {
957
970
  successCount++;
958
971
  } else {
959
- failedPackages.push(packages[i].name);
972
+ failedPackages.push(pkg.name);
960
973
  }
961
974
  }
962
975
  console.log("");
@@ -1212,22 +1225,17 @@ function resetDevStartupState() {
1212
1225
  }
1213
1226
 
1214
1227
  // src/config/utils/deepMerge.ts
1228
+ function isPlainObject(value) {
1229
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1230
+ }
1215
1231
  function deepMerge(target, source) {
1216
1232
  const output = { ...target };
1217
- for (const key in source) {
1218
- if (source[key] === void 0) continue;
1219
- if (Array.isArray(source[key])) {
1220
- output[key] = source[key];
1221
- } else if (source[key] && typeof source[key] === "object" && !Array.isArray(source[key])) {
1222
- const targetValue = output[key];
1223
- if (targetValue && typeof targetValue === "object" && !Array.isArray(targetValue)) {
1224
- output[key] = deepMerge(targetValue, source[key]);
1225
- } else {
1226
- output[key] = source[key];
1227
- }
1228
- } else {
1229
- output[key] = source[key];
1230
- }
1233
+ for (const key of Object.keys(source)) {
1234
+ const sourceValue = source[key];
1235
+ if (sourceValue === void 0) continue;
1236
+ const targetValue = output[key];
1237
+ const merged = isPlainObject(sourceValue) && isPlainObject(targetValue) ? deepMerge(targetValue, sourceValue) : sourceValue;
1238
+ output[key] = merged;
1231
1239
  }
1232
1240
  return output;
1233
1241
  }
@@ -1316,7 +1324,7 @@ function createBaseNextConfig(options = {}) {
1316
1324
  "style-src 'self' 'unsafe-inline'",
1317
1325
  imgSrc,
1318
1326
  `font-src 'self' data:${extraFont}`,
1319
- // API/websocket calls go cross-origin to Django/Centrifugo.
1327
+ // API calls go cross-origin to Django.
1320
1328
  connectSrc,
1321
1329
  // Declared vendors (Stripe Elements/Checkout, video embeds…) and any
1322
1330
  // app-declared trusted origins mount iframes; add their hosts here.
@@ -1437,23 +1445,28 @@ function createBaseNextConfig(options = {}) {
1437
1445
  return config;
1438
1446
  }
1439
1447
  };
1440
- const {
1441
- i18n: i18nOptions,
1442
- pwa: pwaOptions,
1443
- transpilePackages,
1444
- optimizePackageImports,
1445
- isDefaultCfgAdmin,
1446
- checkUpdates,
1447
- autoUpdate,
1448
- forceCheckWorkspace,
1449
- checkPackages: checkPackages2,
1450
- autoInstall,
1451
- allowIframeFrom,
1452
- dpop,
1453
- stripe,
1454
- csp,
1455
- ...nextConfigOptions
1456
- } = options;
1448
+ const { i18n: i18nOptions, pwa: pwaOptions } = options;
1449
+ const DJANGOCFG_ONLY_KEYS = [
1450
+ "i18n",
1451
+ "pwa",
1452
+ "transpilePackages",
1453
+ "optimizePackageImports",
1454
+ "isDefaultCfgAdmin",
1455
+ "checkUpdates",
1456
+ "autoUpdate",
1457
+ "forceCheckWorkspace",
1458
+ "checkPackages",
1459
+ "autoInstall",
1460
+ "allowIframeFrom",
1461
+ "dpop",
1462
+ "stripe",
1463
+ "csp"
1464
+ ];
1465
+ const nextConfigOptions = Object.fromEntries(
1466
+ Object.entries(options).filter(
1467
+ ([key]) => !DJANGOCFG_ONLY_KEYS.includes(key)
1468
+ )
1469
+ );
1457
1470
  let finalConfig = deepMerge(baseConfig, nextConfigOptions);
1458
1471
  if (pwaOptions) {
1459
1472
  finalConfig = withPWA(finalConfig, pwaOptions);