@nuxt/scripts 1.0.0-beta.30 → 1.0.0-beta.32

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.
Files changed (37) hide show
  1. package/dist/client/200.html +1 -1
  2. package/dist/client/404.html +1 -1
  3. package/dist/client/_nuxt/{CYlYSSNW.js → CxpRPAAJ.js} +1 -1
  4. package/dist/client/_nuxt/{D5FIkDae.js → D0d_xOOu.js} +1 -1
  5. package/dist/client/_nuxt/{AwAKM0sG.js → DxzaVa0B.js} +1 -1
  6. package/dist/client/_nuxt/builds/latest.json +1 -1
  7. package/dist/client/_nuxt/builds/meta/d7ecb215-eee2-4720-b2bc-f3ad271b9c30.json +1 -0
  8. package/dist/client/_nuxt/entry.esAfLJmC.css +1 -0
  9. package/dist/client/_nuxt/ojT6Btul.js +162 -0
  10. package/dist/client/index.html +1 -1
  11. package/dist/module.d.mts +23 -45
  12. package/dist/module.d.ts +23 -45
  13. package/dist/module.json +1 -1
  14. package/dist/module.mjs +241 -227
  15. package/dist/registry.d.mts +6 -1
  16. package/dist/registry.d.ts +6 -1
  17. package/dist/registry.mjs +150 -11
  18. package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsMarkerClusterer.d.vue.ts +5 -2
  19. package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsMarkerClusterer.vue +11 -2
  20. package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsMarkerClusterer.vue.d.ts +5 -2
  21. package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsOverlayView.d.vue.ts +14 -0
  22. package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsOverlayView.vue +50 -1
  23. package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsOverlayView.vue.d.ts +14 -0
  24. package/dist/runtime/registry/gravatar.d.ts +1 -1
  25. package/dist/runtime/server/proxy-handler.js +25 -36
  26. package/dist/runtime/server/utils/privacy.d.ts +0 -8
  27. package/dist/runtime/server/utils/privacy.js +7 -7
  28. package/dist/runtime/types.d.ts +71 -13
  29. package/dist/shared/scripts.T4Z99VT8.mjs +37 -0
  30. package/dist/stats.mjs +7 -15
  31. package/dist/types-source.mjs +16 -2
  32. package/dist/types.d.mts +1 -1
  33. package/package.json +2 -2
  34. package/dist/client/_nuxt/Bl23o3st.js +0 -162
  35. package/dist/client/_nuxt/builds/meta/f0b4dd20-8496-4003-b7a3-05cbae515923.json +0 -1
  36. package/dist/client/_nuxt/entry.C5SUNdim.css +0 -1
  37. package/dist/shared/scripts.ViOoYQXH.mjs +0 -381
package/dist/module.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { existsSync, readdirSync, readFileSync } from 'node:fs';
2
- import { useLogger, addServerHandler, addPluginTemplate, useNuxt, addDevServerHandler, extendRouteRules, tryUseNuxt, extendViteConfig, logger as logger$1, addTypeTemplate, defineNuxtModule, createResolver, hasNuxtModule, addImports, addComponentsDir, addTemplate, addBuildPlugin } from '@nuxt/kit';
2
+ import { useNuxt, addDevServerHandler, extendRouteRules, tryUseNuxt, extendViteConfig, useLogger, addServerHandler, addPluginTemplate, logger as logger$1, addTypeTemplate, defineNuxtModule, createResolver, addImports, addComponentsDir, addTemplate, hasNuxtModule, addBuildPlugin } from '@nuxt/kit';
3
3
  import { defu } from 'defu';
4
4
  import { join, resolve, relative } from 'pathe';
5
5
  import { resolvePackageJSON, readPackageJSON } from 'pkg-types';
@@ -19,48 +19,161 @@ import { colors } from 'consola/utils';
19
19
  import MagicString from 'magic-string';
20
20
  import { hash } from 'ohash';
21
21
  import { registry } from './registry.mjs';
22
- import { g as getAllProxyConfigs, r as routesToInterceptRules } from './shared/scripts.ViOoYQXH.mjs';
22
+ import { b as buildProxyConfigsFromRegistry } from './shared/scripts.T4Z99VT8.mjs';
23
23
 
24
- function generatePartytownResolveUrl(interceptRules) {
25
- const rulesJson = JSON.stringify(interceptRules);
26
- return `function(url, location, type) {
27
- var rules = ${rulesJson};
28
- for (var i = 0; i < rules.length; i++) {
29
- var rule = rules[i];
30
- if (url.hostname === rule.pattern || url.hostname.endsWith('.' + rule.pattern)) {
31
- if (rule.pathPrefix && !url.pathname.startsWith(rule.pathPrefix)) continue;
32
- var path = rule.pathPrefix ? url.pathname.slice(rule.pathPrefix.length) : url.pathname;
33
- var newPath = rule.target + (path.startsWith('/') ? '' : '/') + path + url.search;
34
- return new URL(newPath, location.origin);
24
+ const renderedScript = /* @__PURE__ */ new Map();
25
+ const ONE_YEAR_IN_SECONDS = 60 * 60 * 24 * 365;
26
+ function bundleStorage() {
27
+ const nuxt = tryUseNuxt();
28
+ return createStorage({
29
+ driver: fsDriver({
30
+ base: resolve(nuxt?.options.rootDir || "", "node_modules/.cache/nuxt/scripts")
31
+ })
32
+ });
33
+ }
34
+ function setupPublicAssetStrategy(options = {}) {
35
+ const assetsBaseURL = options.prefix || "/_scripts/assets";
36
+ const nuxt = useNuxt();
37
+ const storage = bundleStorage();
38
+ addDevServerHandler({
39
+ route: assetsBaseURL,
40
+ handler: lazyEventHandler(async () => {
41
+ return eventHandler(async (event) => {
42
+ const cleanPath = (event.path || "").split("?")[0]?.slice(1) || "";
43
+ const filename = cleanPath;
44
+ const scriptDescriptor = renderedScript.get(join(assetsBaseURL, cleanPath));
45
+ if (!scriptDescriptor || scriptDescriptor instanceof Error)
46
+ throw createError({ statusCode: 404 });
47
+ if (scriptDescriptor.content) {
48
+ return scriptDescriptor.content;
49
+ }
50
+ const key = `bundle:${filename}`;
51
+ let res = await storage.getItemRaw(key);
52
+ if (!res) {
53
+ res = await fetch(scriptDescriptor.src).then((r) => r.arrayBuffer()).then((r) => Buffer.from(r));
54
+ await storage.setItemRaw(key, res);
55
+ }
56
+ return res;
57
+ });
58
+ })
59
+ });
60
+ if (nuxt.options.dev) {
61
+ extendRouteRules(joinURL(assetsBaseURL, "**"), {
62
+ cache: {
63
+ maxAge: ONE_YEAR_IN_SECONDS
64
+ }
65
+ });
66
+ }
67
+ const cacheDir = join(nuxt.options.buildDir, "cache", "scripts");
68
+ nuxt.hook("nitro:config", (nitroConfig) => {
69
+ nitroConfig.publicAssets ||= [];
70
+ nitroConfig.publicAssets.push({
71
+ dir: cacheDir,
72
+ maxAge: ONE_YEAR_IN_SECONDS,
73
+ baseURL: assetsBaseURL
74
+ });
75
+ nitroConfig.prerender ||= {};
76
+ nitroConfig.prerender.ignore ||= [];
77
+ nitroConfig.prerender.ignore.push(assetsBaseURL);
78
+ });
79
+ return {
80
+ renderedScript
81
+ };
82
+ }
83
+
84
+ const DEVTOOLS_UI_ROUTE = "/__nuxt-scripts";
85
+ const DEVTOOLS_UI_LOCAL_PORT = 3300;
86
+
87
+ async function setupDevToolsUI(options, resolve, nuxt = useNuxt()) {
88
+ const clientPath = await resolve("./client");
89
+ const isProductionBuild = existsSync(clientPath);
90
+ if (isProductionBuild) {
91
+ nuxt.hook("vite:serverCreated", async (server) => {
92
+ const sirv = await import('sirv').then((r) => r.default || r);
93
+ server.middlewares.use(
94
+ DEVTOOLS_UI_ROUTE,
95
+ sirv(clientPath, { dev: true, single: true })
96
+ );
97
+ });
98
+ } else {
99
+ extendViteConfig((config) => {
100
+ config.server = config.server || {};
101
+ config.server.proxy = config.server.proxy || {};
102
+ config.server.proxy[DEVTOOLS_UI_ROUTE] = {
103
+ target: `http://localhost:${DEVTOOLS_UI_LOCAL_PORT}${DEVTOOLS_UI_ROUTE}`,
104
+ changeOrigin: true,
105
+ followRedirects: true,
106
+ rewrite: (path) => path.replace(DEVTOOLS_UI_ROUTE, "")
107
+ };
108
+ });
109
+ }
110
+ addCustomTab({
111
+ // unique identifier
112
+ name: "nuxt-scripts",
113
+ // title to display in the tab
114
+ title: "Scripts",
115
+ // any icon from Iconify, or a URL to an image
116
+ icon: "carbon:script",
117
+ // iframe view
118
+ view: {
119
+ type: "iframe",
120
+ src: DEVTOOLS_UI_ROUTE
35
121
  }
122
+ });
123
+ }
124
+
125
+ function generatePartytownResolveUrl(proxyPrefix) {
126
+ return `function(url, location, type) {
127
+ if (url.origin !== location.origin) {
128
+ return new URL(${JSON.stringify(proxyPrefix)} + '/' + url.host + url.pathname + url.search, location.origin);
36
129
  }
37
130
  }`;
38
131
  }
39
132
 
40
133
  const logger = useLogger("@nuxt/scripts");
41
134
 
42
- function generateInterceptPluginContents(interceptRules) {
43
- const rulesJson = JSON.stringify(interceptRules);
135
+ function resolveCapabilities(script, scriptOptions) {
136
+ const defaults = script.defaultCapability ?? {};
137
+ const ceiling = script.capabilities ?? {};
138
+ const resolved = { ...defaults };
139
+ if (!scriptOptions)
140
+ return resolved;
141
+ const overrideKeys = ["reverseProxyIntercept", "bundle", "partytown"];
142
+ for (const key of overrideKeys) {
143
+ if (key in scriptOptions) {
144
+ const userValue = scriptOptions[key];
145
+ if (typeof userValue !== "boolean")
146
+ continue;
147
+ if (userValue && !ceiling[key]) {
148
+ if (import.meta.dev) {
149
+ logger.warn(
150
+ `[nuxt-scripts] Script "${script.registryKey}" does not support capability "${key}". This override will be ignored. Supported capabilities: ${JSON.stringify(ceiling)}`
151
+ );
152
+ }
153
+ continue;
154
+ }
155
+ resolved[key] = userValue;
156
+ }
157
+ }
158
+ return resolved;
159
+ }
160
+
161
+ function generateInterceptPluginContents(proxyPrefix) {
44
162
  return `export default defineNuxtPlugin({
45
163
  name: 'nuxt-scripts:intercept',
46
164
  enforce: 'pre',
47
165
  setup() {
48
- const rules = ${rulesJson};
166
+ const proxyPrefix = ${JSON.stringify(proxyPrefix)};
49
167
  const origBeacon = typeof navigator !== 'undefined' && navigator.sendBeacon
50
168
  ? navigator.sendBeacon.bind(navigator)
51
169
  : () => false;
52
170
  const origFetch = globalThis.fetch.bind(globalThis);
53
171
 
54
- function rewriteUrl(url) {
172
+ function proxyUrl(url) {
55
173
  try {
56
174
  const parsed = new URL(url, location.origin);
57
- for (const rule of rules) {
58
- if (parsed.hostname === rule.pattern || parsed.hostname.endsWith('.' + rule.pattern)) {
59
- if (rule.pathPrefix && !parsed.pathname.startsWith(rule.pathPrefix)) continue;
60
- const path = rule.pathPrefix ? parsed.pathname.slice(rule.pathPrefix.length) : parsed.pathname;
61
- return location.origin + rule.target + (path.startsWith('/') ? '' : '/') + path + parsed.search;
62
- }
63
- }
175
+ if (parsed.origin !== location.origin)
176
+ return location.origin + proxyPrefix + '/' + parsed.host + parsed.pathname + parsed.search;
64
177
  } catch {}
65
178
  return url;
66
179
  }
@@ -70,7 +183,7 @@ function generateInterceptPluginContents(interceptRules) {
70
183
  class ProxiedXHR extends OrigXHR {
71
184
  open() {
72
185
  const args = Array.from(arguments);
73
- if (typeof args[1] === 'string') args[1] = rewriteUrl(args[1]);
186
+ if (typeof args[1] === 'string') args[1] = proxyUrl(args[1]);
74
187
  return super.open.apply(this, args);
75
188
  }
76
189
  }
@@ -83,7 +196,7 @@ function generateInterceptPluginContents(interceptRules) {
83
196
  if (origSrcDesc && origSrcDesc.set) {
84
197
  Object.defineProperty(img, 'src', {
85
198
  get() { return origSrcDesc.get.call(this); },
86
- set(v) { origSrcDesc.set.call(this, typeof v === 'string' ? rewriteUrl(v) : v); },
199
+ set(v) { origSrcDesc.set.call(this, typeof v === 'string' ? proxyUrl(v) : v); },
87
200
  configurable: true,
88
201
  });
89
202
  }
@@ -91,8 +204,12 @@ function generateInterceptPluginContents(interceptRules) {
91
204
  }
92
205
 
93
206
  globalThis.__nuxtScripts = {
94
- sendBeacon: (url, data) => origBeacon(rewriteUrl(url), data),
95
- fetch: (url, opts) => origFetch(typeof url === 'string' ? rewriteUrl(url) : url, opts),
207
+ sendBeacon: (url, data) => origBeacon(proxyUrl(url), data),
208
+ fetch: (url, opts) => {
209
+ if (typeof url === 'string') return origFetch(proxyUrl(url), opts);
210
+ if (url instanceof Request) return origFetch(new Request(proxyUrl(url.url), url), opts);
211
+ return origFetch(url, opts);
212
+ },
96
213
  XMLHttpRequest: ProxiedXHR,
97
214
  Image: ProxiedImage,
98
215
  };
@@ -102,13 +219,12 @@ function generateInterceptPluginContents(interceptRules) {
102
219
  }
103
220
 
104
221
  async function setupFirstParty(config, resolvePath) {
105
- const enabled = !!config.firstParty;
106
- const proxyPrefix = typeof config.firstParty === "object" ? config.firstParty.proxyPrefix || "/_scripts/p" : "/_scripts/p";
107
- const privacy = typeof config.firstParty === "object" ? config.firstParty.privacy : void 0;
222
+ const proxyDisabled = config.proxy === false;
223
+ const proxyPrefix = typeof config.proxy === "object" ? config.proxy.prefix || "/_scripts/p" : "/_scripts/p";
224
+ const privacy = typeof config.proxy === "object" ? config.proxy.privacy : void 0;
108
225
  const assetsPrefix = config.assets?.prefix || "/_scripts/assets";
109
- const proxyConfigs = enabled ? getAllProxyConfigs(proxyPrefix) : {};
110
- const firstParty = { enabled, proxyPrefix, privacy, assetsPrefix, proxyConfigs };
111
- if (enabled) {
226
+ const firstParty = { enabled: !proxyDisabled, proxyPrefix, privacy, assetsPrefix, proxyConfigs: {} };
227
+ if (!proxyDisabled) {
112
228
  const proxyHandlerPath = await resolvePath("./runtime/server/proxy-handler");
113
229
  logger.debug("[nuxt-scripts] Registering proxy handler:", `${proxyPrefix}/**`, "->", proxyHandlerPath);
114
230
  addServerHandler({
@@ -123,6 +239,9 @@ function applyAutoInject(registry, runtimeConfig, proxyPrefix, registryKey, auto
123
239
  if (!entry)
124
240
  return;
125
241
  const input = entry[0];
242
+ const scriptOptions = entry[1];
243
+ if (input?.reverseProxyIntercept === false || scriptOptions?.reverseProxyIntercept === false)
244
+ return;
126
245
  const rtScripts = runtimeConfig.public?.scripts;
127
246
  const rtEntry = rtScripts?.[registryKey];
128
247
  const config = rtEntry && typeof rtEntry === "object" ? rtEntry : input;
@@ -133,16 +252,6 @@ function applyAutoInject(registry, runtimeConfig, proxyPrefix, registryKey, auto
133
252
  if (rtEntry && typeof rtEntry === "object" && rtEntry !== input)
134
253
  rtEntry[autoInject.configField] = value;
135
254
  }
136
- const DOMAIN_RE = /^https?:\/\/([^/]+)/;
137
- function extractDomains(routes) {
138
- const domains = /* @__PURE__ */ new Set();
139
- for (const { proxy } of Object.values(routes)) {
140
- const match = proxy.match(DOMAIN_RE);
141
- if (match?.[1])
142
- domains.add(match[1]);
143
- }
144
- return [...domains].sort();
145
- }
146
255
  function computePrivacyLevel(privacy) {
147
256
  const flags = Object.values(privacy);
148
257
  if (flags.every(Boolean))
@@ -153,17 +262,19 @@ function computePrivacyLevel(privacy) {
153
262
  }
154
263
  function finalizeFirstParty(opts) {
155
264
  const { firstParty, registryScripts, nuxtOptions } = opts;
156
- const { proxyConfigs, proxyPrefix } = firstParty;
265
+ const { proxyPrefix } = firstParty;
266
+ const proxyConfigs = buildProxyConfigsFromRegistry(registryScripts);
267
+ firstParty.proxyConfigs = proxyConfigs;
157
268
  const registryKeys = Object.keys(opts.registry || {});
158
269
  const scriptByKey = /* @__PURE__ */ new Map();
159
270
  for (const script of registryScripts) {
160
271
  if (script.registryKey)
161
272
  scriptByKey.set(script.registryKey, script);
162
273
  }
163
- const neededRoutes = {};
164
- const routePrivacyOverrides = {};
274
+ const domainPrivacy = {};
165
275
  const unsupportedScripts = [];
166
276
  const unmatchedScripts = [];
277
+ let totalDomains = 0;
167
278
  const devtoolsScripts = [];
168
279
  for (const key of registryKeys) {
169
280
  const script = scriptByKey.get(key);
@@ -171,20 +282,22 @@ function finalizeFirstParty(opts) {
171
282
  unmatchedScripts.push(key);
172
283
  continue;
173
284
  }
174
- if (script.proxy === false)
285
+ if (!script.capabilities?.reverseProxyIntercept)
286
+ continue;
287
+ const registryEntry = opts.registry?.[key];
288
+ const entryScriptOptions = registryEntry?.[1];
289
+ const entryInput = registryEntry?.[0];
290
+ if (entryScriptOptions?.reverseProxyIntercept === false || entryInput?.reverseProxyIntercept === false)
175
291
  continue;
176
- const configKey = script.proxy || key;
292
+ const configKey = script.proxyConfig || key;
177
293
  const proxyConfig = proxyConfigs[configKey];
178
294
  if (!proxyConfig) {
179
- if (script.scriptBundling !== false)
180
- unsupportedScripts.push(key);
295
+ unsupportedScripts.push(key);
181
296
  continue;
182
297
  }
183
- const scriptRoutes = proxyConfig.routes || {};
184
- if (proxyConfig.routes) {
185
- Object.assign(neededRoutes, proxyConfig.routes);
186
- for (const routePath of Object.keys(proxyConfig.routes))
187
- routePrivacyOverrides[routePath] = proxyConfig.privacy;
298
+ for (const domain of proxyConfig.domains) {
299
+ domainPrivacy[domain] = proxyConfig.privacy;
300
+ totalDomains++;
188
301
  }
189
302
  if (proxyConfig.autoInject && opts.registry)
190
303
  applyAutoInject(opts.registry, nuxtOptions.runtimeConfig, proxyPrefix, key, proxyConfig.autoInject);
@@ -200,7 +313,6 @@ function finalizeFirstParty(opts) {
200
313
  };
201
314
  const logo = script.logo;
202
315
  const logoStr = typeof logo === "object" ? logo.dark || logo.light : logo || "";
203
- const interceptRules2 = routesToInterceptRules(scriptRoutes);
204
316
  devtoolsScripts.push({
205
317
  registryKey: key,
206
318
  label: script.label || key,
@@ -213,9 +325,7 @@ function finalizeFirstParty(opts) {
213
325
  hasPostProcess: !!proxyConfig.postProcess,
214
326
  privacy: normalizedPrivacy,
215
327
  privacyLevel: computePrivacyLevel(normalizedPrivacy),
216
- domains: extractDomains(scriptRoutes),
217
- routes: Object.entries(scriptRoutes).map(([local, { proxy }]) => ({ local, target: proxy })),
218
- interceptRules: interceptRules2
328
+ domains: [...proxyConfig.domains]
219
329
  });
220
330
  }
221
331
  }
@@ -231,30 +341,21 @@ These scripts will not have proxy routes registered. Check that the registry key
231
341
  They will load directly from third-party servers. Request support at https://github.com/nuxt/scripts/issues`
232
342
  );
233
343
  }
234
- const interceptRules = routesToInterceptRules(neededRoutes);
235
344
  addPluginTemplate({
236
345
  filename: "nuxt-scripts-intercept.client.mjs",
237
346
  getContents() {
238
- return generateInterceptPluginContents(interceptRules);
347
+ return generateInterceptPluginContents(proxyPrefix);
239
348
  }
240
349
  });
241
- const flatRoutes = {};
242
- for (const [path, config] of Object.entries(neededRoutes))
243
- flatRoutes[path] = config.proxy;
244
350
  nuxtOptions.runtimeConfig["nuxt-scripts-proxy"] = {
245
- routes: flatRoutes,
246
- privacy: firstParty.privacy,
247
- routePrivacy: routePrivacyOverrides
351
+ proxyPrefix,
352
+ domainPrivacy,
353
+ privacy: firstParty.privacy
248
354
  };
249
355
  const privacyLabel = firstParty.privacy === void 0 ? "per-script" : typeof firstParty.privacy === "boolean" ? firstParty.privacy ? "anonymize" : "passthrough" : "custom";
250
- if (Object.keys(neededRoutes).length && nuxtOptions.dev) {
251
- const routeCount = Object.keys(neededRoutes).length;
356
+ if (totalDomains > 0 && nuxtOptions.dev) {
252
357
  const scriptsCount = registryKeys.length;
253
- logger.success(`First-party mode enabled for ${scriptsCount} script(s), ${routeCount} proxy route(s) configured (privacy: ${privacyLabel})`);
254
- if (logger.level >= 4) {
255
- for (const [path, config] of Object.entries(neededRoutes))
256
- logger.debug(` ${path} \u2192 ${config.proxy}`);
257
- }
358
+ logger.success(`First-party mode enabled for ${scriptsCount} script(s), ${totalDomains} domain(s) proxied (privacy: ${privacyLabel})`);
258
359
  }
259
360
  const staticPresets = ["static", "github-pages", "cloudflare-pages-static", "netlify-static", "azure-static", "firebase-static"];
260
361
  const preset = process.env.NITRO_PRESET || "";
@@ -266,7 +367,7 @@ Scripts will be bundled, but collection requests will not be proxied.
266
367
  Options:
267
368
  1. Configure platform rewrites (Vercel, Netlify, Cloudflare)
268
369
  2. Switch to server-rendered mode (ssr: true)
269
- 3. Disable with firstParty: false
370
+ 3. Disable with proxy: false
270
371
 
271
372
  See: https://scripts.nuxt.com/docs/guides/first-party#static-hosting`
272
373
  );
@@ -283,112 +384,10 @@ See: https://scripts.nuxt.com/docs/guides/first-party#static-hosting`
283
384
  proxyPrefix,
284
385
  privacyMode: privacyLabel,
285
386
  scripts: devtoolsScripts,
286
- totalRoutes: Object.keys(neededRoutes).length,
287
387
  totalDomains: allDomains.size
288
388
  };
289
389
  }
290
- return { interceptRules, devtools };
291
- }
292
-
293
- const renderedScript = /* @__PURE__ */ new Map();
294
- const ONE_YEAR_IN_SECONDS = 60 * 60 * 24 * 365;
295
- function bundleStorage() {
296
- const nuxt = tryUseNuxt();
297
- return createStorage({
298
- driver: fsDriver({
299
- base: resolve(nuxt?.options.rootDir || "", "node_modules/.cache/nuxt/scripts")
300
- })
301
- });
302
- }
303
- function setupPublicAssetStrategy(options = {}) {
304
- const assetsBaseURL = options.prefix || "/_scripts/assets";
305
- const nuxt = useNuxt();
306
- const storage = bundleStorage();
307
- addDevServerHandler({
308
- route: assetsBaseURL,
309
- handler: lazyEventHandler(async () => {
310
- return eventHandler(async (event) => {
311
- const cleanPath = (event.path || "").split("?")[0]?.slice(1) || "";
312
- const filename = cleanPath;
313
- const scriptDescriptor = renderedScript.get(join(assetsBaseURL, cleanPath));
314
- if (!scriptDescriptor || scriptDescriptor instanceof Error)
315
- throw createError({ statusCode: 404 });
316
- if (scriptDescriptor.content) {
317
- return scriptDescriptor.content;
318
- }
319
- const key = `bundle:${filename}`;
320
- let res = await storage.getItemRaw(key);
321
- if (!res) {
322
- res = await fetch(scriptDescriptor.src).then((r) => r.arrayBuffer()).then((r) => Buffer.from(r));
323
- await storage.setItemRaw(key, res);
324
- }
325
- return res;
326
- });
327
- })
328
- });
329
- if (nuxt.options.dev) {
330
- extendRouteRules(joinURL(assetsBaseURL, "**"), {
331
- cache: {
332
- maxAge: ONE_YEAR_IN_SECONDS
333
- }
334
- });
335
- }
336
- const cacheDir = join(nuxt.options.buildDir, "cache", "scripts");
337
- nuxt.hook("nitro:config", (nitroConfig) => {
338
- nitroConfig.publicAssets ||= [];
339
- nitroConfig.publicAssets.push({
340
- dir: cacheDir,
341
- maxAge: ONE_YEAR_IN_SECONDS,
342
- baseURL: assetsBaseURL
343
- });
344
- nitroConfig.prerender ||= {};
345
- nitroConfig.prerender.ignore ||= [];
346
- nitroConfig.prerender.ignore.push(assetsBaseURL);
347
- });
348
- return {
349
- renderedScript
350
- };
351
- }
352
-
353
- const DEVTOOLS_UI_ROUTE = "/__nuxt-scripts";
354
- const DEVTOOLS_UI_LOCAL_PORT = 3300;
355
-
356
- async function setupDevToolsUI(options, resolve, nuxt = useNuxt()) {
357
- const clientPath = await resolve("./client");
358
- const isProductionBuild = existsSync(clientPath);
359
- if (isProductionBuild) {
360
- nuxt.hook("vite:serverCreated", async (server) => {
361
- const sirv = await import('sirv').then((r) => r.default || r);
362
- server.middlewares.use(
363
- DEVTOOLS_UI_ROUTE,
364
- sirv(clientPath, { dev: true, single: true })
365
- );
366
- });
367
- } else {
368
- extendViteConfig((config) => {
369
- config.server = config.server || {};
370
- config.server.proxy = config.server.proxy || {};
371
- config.server.proxy[DEVTOOLS_UI_ROUTE] = {
372
- target: `http://localhost:${DEVTOOLS_UI_LOCAL_PORT}${DEVTOOLS_UI_ROUTE}`,
373
- changeOrigin: true,
374
- followRedirects: true,
375
- rewrite: (path) => path.replace(DEVTOOLS_UI_ROUTE, "")
376
- };
377
- });
378
- }
379
- addCustomTab({
380
- // unique identifier
381
- name: "nuxt-scripts",
382
- // title to display in the tab
383
- title: "Scripts",
384
- // any icon from Iconify, or a URL to an image
385
- icon: "carbon:script",
386
- // iframe view
387
- view: {
388
- type: "iframe",
389
- src: DEVTOOLS_UI_ROUTE
390
- }
391
- });
390
+ return { proxyPrefix, devtools };
392
391
  }
393
392
 
394
393
  const isStackblitz = provider === "stackblitz";
@@ -710,7 +709,8 @@ function rewriteScriptUrlsAST(content, filename, rewrites, postProcess, options)
710
709
  }
711
710
  if (node.type === "CallExpression" && !options?.skipApiRewrites) {
712
711
  const callee = node.callee;
713
- const canvasPropName = callee?.type === "MemberExpression" ? callee.computed ? callee.property?.type === "Literal" && typeof callee.property.value === "string" ? callee.property.value : null : callee.property?.name : null;
712
+ const shouldNeutralizeCanvas = options?.neutralizeCanvas !== false;
713
+ const canvasPropName = shouldNeutralizeCanvas && callee?.type === "MemberExpression" ? callee.computed ? callee.property?.type === "Literal" && typeof callee.property.value === "string" ? callee.property.value : null : callee.property?.name : null;
714
714
  if (canvasPropName === "toDataURL" && callee.object) {
715
715
  const blankCanvas = `"${BLANK_CANVAS_DATA_URL}"`;
716
716
  if (callee.object.type === "Identifier") {
@@ -817,7 +817,7 @@ function normalizeScriptData(src, assetsBaseURL = "/_scripts/assets") {
817
817
  return { url: src };
818
818
  }
819
819
  async function downloadScript(opts, renderedScript, fetchOptions, cacheMaxAge) {
820
- const { src, url, filename, forceDownload, integrity, proxyRewrites, postProcess, skipApiRewrites } = opts;
820
+ const { src, url, filename, forceDownload, integrity, proxyRewrites, postProcess, skipApiRewrites, neutralizeCanvas } = opts;
821
821
  if (src === url || !filename) {
822
822
  return;
823
823
  }
@@ -855,7 +855,7 @@ async function downloadScript(opts, renderedScript, fetchOptions, cacheMaxAge) {
855
855
  await storage.setItemRaw(`bundle:${filename}`, res);
856
856
  if (proxyRewrites?.length && res) {
857
857
  const content = res.toString("utf-8");
858
- const rewritten = rewriteScriptUrlsAST(content, filename || "script.js", proxyRewrites, postProcess, { skipApiRewrites });
858
+ const rewritten = rewriteScriptUrlsAST(content, filename || "script.js", proxyRewrites, postProcess, { skipApiRewrites, neutralizeCanvas });
859
859
  res = Buffer.from(rewritten, "utf-8");
860
860
  logger.debug(`Rewrote ${proxyRewrites.length} URL patterns in ${filename}`);
861
861
  }
@@ -1035,34 +1035,39 @@ function NuxtScriptBundleTransformer(options = {
1035
1035
  canBundle = bundleValue === true || bundleValue === "force" || String(bundleValue) === "true";
1036
1036
  forceDownload = bundleValue === "force";
1037
1037
  }
1038
- const firstPartyOption = scriptOptions?.value.properties?.find((prop) => {
1039
- return prop.type === "Property" && prop.key?.name === "firstParty" && prop.value.type === "Literal";
1038
+ const rpiOption = scriptOptions?.value.properties?.find((prop) => {
1039
+ return prop.type === "Property" && prop.key?.name === "reverseProxyIntercept" && prop.value.type === "Literal";
1040
1040
  });
1041
- let firstPartyOptOut = firstPartyOption?.value.value === false;
1041
+ let firstPartyOptOut = rpiOption?.value.value === false;
1042
1042
  if (!firstPartyOptOut && node.arguments[1]?.type === "ObjectExpression") {
1043
- const secondArgFirstPartyProp = node.arguments[1].properties.find(
1044
- (p) => p.type === "Property" && p.key?.name === "firstParty" && p.value.type === "Literal"
1043
+ const secondArgProp = node.arguments[1].properties.find(
1044
+ (p) => p.type === "Property" && p.key?.name === "reverseProxyIntercept" && p.value.type === "Literal"
1045
1045
  );
1046
- firstPartyOptOut = secondArgFirstPartyProp?.value.value === false;
1046
+ firstPartyOptOut = secondArgProp?.value.value === false;
1047
1047
  }
1048
1048
  if (!firstPartyOptOut && node.arguments[0]?.type === "ObjectExpression") {
1049
- const firstArgFirstPartyProp = node.arguments[0].properties.find(
1050
- (p) => p.type === "Property" && p.key?.name === "firstParty" && p.value.type === "Literal"
1049
+ const firstArgProp = node.arguments[0].properties.find(
1050
+ (p) => p.type === "Property" && p.key?.name === "reverseProxyIntercept" && p.value.type === "Literal"
1051
1051
  );
1052
- firstPartyOptOut = firstArgFirstPartyProp?.value.value === false;
1052
+ firstPartyOptOut = firstArgProp?.value.value === false;
1053
1053
  }
1054
1054
  if (canBundle) {
1055
1055
  const { url: _url, filename } = normalizeScriptData(src, options.assetsBaseURL);
1056
1056
  const script = options.scripts?.find((s2) => s2.import.name === fnName);
1057
- const proxyConfigKey = script?.proxy !== false ? script?.proxy || registryKey : void 0;
1057
+ const hasReverseProxy = script?.capabilities?.reverseProxyIntercept;
1058
+ const proxyConfigKey = hasReverseProxy ? script?.proxyConfig || registryKey : void 0;
1058
1059
  const proxyConfig = !firstPartyOptOut && proxyConfigKey ? options.proxyConfigs?.[proxyConfigKey] : void 0;
1059
- const proxyRewrites = proxyConfig?.rewrite;
1060
+ const proxyRewrites = proxyConfig?.domains?.map((domain) => ({
1061
+ from: domain,
1062
+ to: `${options.proxyPrefix}/${domain}`
1063
+ }));
1060
1064
  const postProcess = proxyConfig?.postProcess;
1061
1065
  const skipApiRewrites = !!(registryKey && options.partytownScripts?.has(registryKey));
1066
+ const neutralizeCanvas = proxyConfig?.privacy !== void 0 && typeof proxyConfig.privacy === "object" ? proxyConfig.privacy.hardware ?? true : true;
1062
1067
  deferredOps.push(async () => {
1063
1068
  let url = _url;
1064
1069
  try {
1065
- await downloadScript({ src, url, filename, forceDownload, proxyRewrites, postProcess, integrity: options.integrity, skipApiRewrites }, renderedScript, options.fetchOptions, options.cacheMaxAge);
1070
+ await downloadScript({ src, url, filename, forceDownload, proxyRewrites, postProcess, integrity: options.integrity, skipApiRewrites, neutralizeCanvas }, renderedScript, options.fetchOptions, options.cacheMaxAge);
1066
1071
  } catch (e) {
1067
1072
  if (options.fallbackOnSrcOnBundleFail) {
1068
1073
  logger.warn(`[Nuxt Scripts: Bundle Transformer] Failed to bundle ${src}. Fallback to remote loading.`);
@@ -1436,7 +1441,6 @@ const module$1 = defineNuxtModule({
1436
1441
  }
1437
1442
  },
1438
1443
  defaults: {
1439
- firstParty: true,
1440
1444
  defaultScriptOptions: {
1441
1445
  trigger: "onNuxtReady"
1442
1446
  },
@@ -1513,37 +1517,11 @@ const module$1 = defineNuxtModule({
1513
1517
  };
1514
1518
  if (config.defaultScriptOptions?.bundle !== void 0) {
1515
1519
  logger.warn(
1516
- "`scripts.defaultScriptOptions.bundle` is deprecated. Use `scripts.firstParty: true` instead. First-party mode is now enabled by default."
1520
+ "`scripts.defaultScriptOptions.bundle` is deprecated. Bundling is now auto-enabled per-script via capabilities. Set `bundle: false` per-script to disable."
1517
1521
  );
1518
1522
  }
1519
1523
  const firstParty = await setupFirstParty(config, resolvePath);
1520
1524
  const assetsPrefix = firstParty.assetsPrefix;
1521
- if (config.partytown?.length) {
1522
- config.registry = config.registry || {};
1523
- const requiredForwards = [];
1524
- for (const scriptKey of config.partytown) {
1525
- const forwards = PARTYTOWN_FORWARDS[scriptKey];
1526
- if (forwards) {
1527
- requiredForwards.push(...forwards);
1528
- } else if (import.meta.dev) {
1529
- logger.warn(`[partytown] "${scriptKey}" has no known Partytown forwards configured. It may not work correctly or may require manual forward configuration.`);
1530
- }
1531
- const reg = config.registry;
1532
- const existing = reg[scriptKey];
1533
- if (existing) {
1534
- existing[1] = { ...existing[1], partytown: true };
1535
- } else {
1536
- reg[scriptKey] = [{}, { partytown: true }];
1537
- }
1538
- }
1539
- if (requiredForwards.length && hasNuxtModule("@nuxtjs/partytown")) {
1540
- const partytownConfig = nuxt.options.partytown || {};
1541
- const existingForwards = partytownConfig.forward || [];
1542
- const newForwards = [.../* @__PURE__ */ new Set([...existingForwards, ...requiredForwards])];
1543
- nuxt.options.partytown = { ...partytownConfig, forward: newForwards };
1544
- logger.info(`[partytown] Auto-configured forwards: ${requiredForwards.join(", ")}`);
1545
- }
1546
- }
1547
1525
  const composables = [
1548
1526
  "useScript",
1549
1527
  "useScriptEventPage",
@@ -1600,8 +1578,43 @@ const module$1 = defineNuxtModule({
1600
1578
  });
1601
1579
  }
1602
1580
  const { renderedScript } = setupPublicAssetStrategy(config.assets);
1581
+ const partytownScripts = /* @__PURE__ */ new Set();
1582
+ const scriptByKey = /* @__PURE__ */ new Map();
1583
+ for (const script of registryScripts) {
1584
+ if (script.registryKey)
1585
+ scriptByKey.set(script.registryKey, script);
1586
+ }
1587
+ let anyNeedsProxy = false;
1588
+ const registryKeys = Object.keys(config.registry || {});
1589
+ for (const key of registryKeys) {
1590
+ const script = scriptByKey.get(key);
1591
+ if (!script)
1592
+ continue;
1593
+ const entry = config.registry?.[key];
1594
+ const scriptOptions = entry?.[1] || {};
1595
+ const inputOptions = entry?.[0] || {};
1596
+ const mergedOverrides = { ...inputOptions, ...scriptOptions };
1597
+ const resolved = resolveCapabilities(script, mergedOverrides);
1598
+ if (resolved.reverseProxyIntercept)
1599
+ anyNeedsProxy = true;
1600
+ if (resolved.partytown) {
1601
+ partytownScripts.add(key);
1602
+ const forwards = PARTYTOWN_FORWARDS[key];
1603
+ if (forwards?.length && hasNuxtModule("@nuxtjs/partytown")) {
1604
+ const partytownConfig = nuxt.options.partytown || {};
1605
+ const existingForwards = partytownConfig.forward || [];
1606
+ const newForwards = [.../* @__PURE__ */ new Set([...existingForwards, ...forwards])];
1607
+ nuxt.options.partytown = { ...partytownConfig, forward: newForwards };
1608
+ } else if (!forwards && import.meta.dev) {
1609
+ logger.warn(`[partytown] "${key}" has no known Partytown forwards configured. It may not work correctly or may require manual forward configuration.`);
1610
+ }
1611
+ }
1612
+ }
1613
+ if (firstParty.enabled && !anyNeedsProxy) {
1614
+ firstParty.enabled = false;
1615
+ }
1603
1616
  if (firstParty.enabled) {
1604
- const { interceptRules, devtools: devtoolsData } = finalizeFirstParty({
1617
+ const { proxyPrefix, devtools: devtoolsData } = finalizeFirstParty({
1605
1618
  firstParty,
1606
1619
  registry: config.registry,
1607
1620
  registryScripts,
@@ -1610,14 +1623,14 @@ const module$1 = defineNuxtModule({
1610
1623
  if (devtoolsData) {
1611
1624
  nuxt.options.runtimeConfig.public["nuxt-scripts-devtools"] = devtoolsData;
1612
1625
  }
1613
- if (config.partytown?.length && hasNuxtModule("@nuxtjs/partytown") && interceptRules.length) {
1626
+ if (partytownScripts.size && hasNuxtModule("@nuxtjs/partytown")) {
1614
1627
  const partytownConfig = nuxt.options.partytown || {};
1615
1628
  if (!partytownConfig.resolveUrl) {
1616
- partytownConfig.resolveUrl = generatePartytownResolveUrl(interceptRules);
1629
+ partytownConfig.resolveUrl = generatePartytownResolveUrl(proxyPrefix);
1617
1630
  nuxt.options.partytown = partytownConfig;
1618
1631
  logger.info("[partytown] Auto-configured resolveUrl for first-party proxy");
1619
1632
  } else {
1620
- logger.warn("[partytown] Custom resolveUrl already set \u2014 first-party proxy URLs will not be auto-rewritten in Partytown worker. Add first-party proxy rules to your resolveUrl manually.");
1633
+ logger.warn("[partytown] Custom resolveUrl already set. Add first-party proxy rules to your resolveUrl manually.");
1621
1634
  }
1622
1635
  }
1623
1636
  }
@@ -1630,7 +1643,8 @@ const module$1 = defineNuxtModule({
1630
1643
  registryConfig: nuxt.options.runtimeConfig.public.scripts,
1631
1644
  defaultBundle: firstParty.enabled || config.defaultScriptOptions?.bundle,
1632
1645
  proxyConfigs: firstParty.proxyConfigs,
1633
- partytownScripts: new Set(config.partytown || []),
1646
+ proxyPrefix: firstParty.proxyPrefix,
1647
+ partytownScripts,
1634
1648
  moduleDetected(module) {
1635
1649
  if (nuxt.options.dev && module !== "@nuxt/scripts" && !moduleInstallPromises.has(module) && !hasNuxtModule(module))
1636
1650
  moduleInstallPromises.set(module, () => installNuxtModule(module));