@djangocfg/nextjs 2.1.480 → 2.1.482

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.
@@ -108,15 +108,43 @@ type CspVendor = keyof typeof CSP_VENDOR_PRESETS;
108
108
  /**
109
109
  * Vendors applied to every app unless explicitly disabled.
110
110
  *
111
- * Only for origins the DEPLOYMENT PLATFORM injects, which an app therefore
112
- * cannot discover by reading its own source. Cloudflare's Web Analytics beacon
113
- * is rewritten into the HTML at the edge, after Next.js has already sent the
114
- * CSP header — so an opt-in flag here reliably produces a production-only
115
- * console error that nobody notices until a user reports it. (Observed
116
- * 2026-07-22: all six DjangoCFG-served hosts were violating their own CSP.)
111
+ * The rule for this list: an origin belongs here when the app CANNOT DECLARE IT
112
+ * FROM `next.config.ts`, because whatever turns the script on lives somewhere
113
+ * the config file cannot read. Leaving such a vendor opt-in does not produce a
114
+ * build error or a dev warning — it produces a production-only console error
115
+ * that nobody sees until a user reports a broken page.
117
116
  *
118
- * Do NOT add ordinary third parties here; those belong in `csp.vendors` on the
119
- * app that actually uses them, so unrelated apps keep the tighter policy.
117
+ * - `cloudflare` — the Web Analytics beacon is rewritten into the HTML AT THE
118
+ * EDGE, after Next.js has already sent the CSP header. Nothing in the source
119
+ * tree reveals it. (Observed 2026-07-22: all six DjangoCFG-served hosts were
120
+ * violating their own CSP.)
121
+ *
122
+ * - `google-analytics` — gtag.js is injected AT RUNTIME by `@djangocfg/layouts`
123
+ * (`BaseApp analytics={{ googleTrackingId }}` → the lazy `ga4()` transport).
124
+ * That id is an ordinary TS value in the app's `settings.ts`, not an env var,
125
+ * so `next.config.ts` — a different module, evaluated in a different process,
126
+ * before any of that code runs — has no way to see whether GA is on. The app
127
+ * author enables analytics in one file and silently breaks CSP in another.
128
+ * (Observed 2026-07-23: cmdop `apps/web` blocked on
129
+ * `googletagmanager.com/gtag/js?id=G-YJ0F48S83F`.)
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.
136
+ *
137
+ * Note what these have in common: the script/frame is turned on by the RUNTIME
138
+ * (the edge, a package, or an end user), never by the app author editing the
139
+ * config. Stripe is the counter-example and deliberately NOT here — a developer
140
+ * decides to mount a payment form, knows they did, and can say `vendors:
141
+ * ['stripe']`; putting it in the baseline would hand `frame-src js.stripe.com`
142
+ * to marketing sites that never take a payment.
143
+ *
144
+ * Do NOT add ordinary third parties here — anything an app knowingly mounts and
145
+ * can name in its own config belongs in `csp.vendors`, so unrelated apps keep
146
+ * the tighter policy. The cost of an entry here is that every app permits these
147
+ * hosts; that is only worth paying when the alternative is silent breakage.
120
148
  */
121
149
  declare const CSP_BASELINE_VENDORS: CspVendor[];
122
150
  /**
@@ -208,18 +236,19 @@ interface BaseNextConfigOptions {
208
236
  * additive; omit a key to leave that directive untouched.
209
237
  * - omitted — tightest default (`frame-src 'self'`, etc.), same as today.
210
238
  *
211
- * **The deployment platform counts as a vendor.** Some origins are injected
212
- * into the response by the EDGE, after Next.js has emitted this header —
213
- * Cloudflare Web Analytics rewrites `beacon.min.js` into the HTML when the
214
- * feature is on for the zone. Nothing in the source tree reveals that, so
215
- * any app served through such a zone must name the platform in `vendors`
216
- * (e.g. `'cloudflare'`) or it will violate its own CSP in production only.
239
+ * **Scripts nothing in this file can see are already handled.** Cloudflare
240
+ * Web Analytics (injected at the edge) and Google Analytics (injected at
241
+ * runtime by `BaseApp analytics={{ googleTrackingId }}`) are both switched on
242
+ * outside `next.config.ts`, so their origins are in the baseline and you do
243
+ * NOT need to list them — see `CSP_BASELINE_VENDORS`. Turn them off with
244
+ * `baseline: false`.
217
245
  *
218
246
  * @example
219
247
  * ```ts
220
248
  * // Declare the third parties this app actually uses — origins come from
221
249
  * // the shared registry, so a vendor host change is fixed in one place.
222
- * createBaseNextConfig({ csp: { vendors: ['stripe', 'cloudflare'] } })
250
+ * // (Cloudflare + GA are already in the baseline; no need to list them.)
251
+ * createBaseNextConfig({ csp: { vendors: ['stripe'] } })
223
252
  *
224
253
  * // Escape hatch for something not in the registry yet
225
254
  * createBaseNextConfig({ csp: { frameSrc: ['https://www.youtube-nocookie.com'] } })
@@ -237,13 +266,18 @@ interface BaseNextConfigOptions {
237
266
  */
238
267
  vendors?: (CspVendor | (string & {}))[];
239
268
  /**
240
- * Cloudflare Web Analytics origins are in the baseline (see
241
- * `CSP_BASELINE_VENDORS`) because DjangoCFG apps are Cloudflare-served
242
- * and the beacon is injected at the edge, where no app can see it.
243
- * Set `false` only for an app served off Cloudflare entirely.
269
+ * Drop the baseline vendors (see `CSP_BASELINE_VENDORS`: Cloudflare Web
270
+ * Analytics + Google Analytics). They are on by default because each is
271
+ * switched on somewhere `next.config.ts` cannot read — the Cloudflare
272
+ * beacon is injected at the edge, and gtag.js is injected at runtime by
273
+ * `BaseApp analytics={{ googleTrackingId }}` — so an opt-in flag would
274
+ * only ever be discovered as a production console error.
275
+ *
276
+ * Set `false` for an app you know mounts neither, to get back the
277
+ * tightest `script-src`.
244
278
  * @default true
245
279
  */
246
- cloudflare?: boolean;
280
+ baseline?: boolean;
247
281
  /** Extra `frame-src` origins (iframes this app embeds). */
248
282
  frameSrc?: string[];
249
283
  /** Extra `script-src` origins (third-party SDK scripts). */
@@ -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.480",
17
+ version: "2.1.482",
18
18
  description: "Next.js server utilities: sitemap, health, OG images, contact forms, navigation, config",
19
19
  keywords: [
20
20
  "nextjs",
@@ -355,7 +355,12 @@ var CSP_VENDOR_PRESETS = {
355
355
  imgSrc: ["https://i.vimeocdn.com"]
356
356
  }
357
357
  };
358
- var CSP_BASELINE_VENDORS = ["cloudflare"];
358
+ var CSP_BASELINE_VENDORS = [
359
+ "cloudflare",
360
+ "google-analytics",
361
+ "youtube",
362
+ "vimeo"
363
+ ];
359
364
  function resolveCspVendors(vendors, warnUnknown = true) {
360
365
  const merged = {
361
366
  scriptSrc: [],
@@ -1500,7 +1505,7 @@ function createBaseNextConfig(options = {}) {
1500
1505
  const cspDisabled = options.csp === false;
1501
1506
  const cspExtra = typeof options.csp === "object" ? options.csp : void 0;
1502
1507
  const vendorNames = [
1503
- ...cspExtra?.cloudflare === false ? [] : CSP_BASELINE_VENDORS,
1508
+ ...cspExtra?.baseline === false ? [] : CSP_BASELINE_VENDORS,
1504
1509
  ...options.stripe ? ["stripe"] : [],
1505
1510
  ...cspExtra?.vendors ?? []
1506
1511
  ];