@nuxt/scripts 1.0.0-beta.1 → 1.0.0-beta.2

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 (68) hide show
  1. package/README.md +6 -0
  2. package/dist/client/200.html +1 -1
  3. package/dist/client/404.html +1 -1
  4. package/dist/client/_nuxt/B66N9HCo.js +1 -0
  5. package/dist/client/_nuxt/B8XOar-X.js +162 -0
  6. package/dist/client/_nuxt/{DTDyDxvR.js → DfLgoB--.js} +1 -1
  7. package/dist/client/_nuxt/DvH517bE.js +1 -0
  8. package/dist/client/_nuxt/builds/latest.json +1 -1
  9. package/dist/client/_nuxt/builds/meta/133a46c5-a5c1-4a63-87d1-037947a5bcdb.json +1 -0
  10. package/dist/client/_nuxt/entry.D45OuV0w.css +1 -0
  11. package/dist/client/_nuxt/error-404.B57D-jUQ.css +1 -0
  12. package/dist/client/_nuxt/error-500.DTHUW7BI.css +1 -0
  13. package/dist/client/index.html +1 -1
  14. package/dist/module.d.mts +80 -2
  15. package/dist/module.json +1 -1
  16. package/dist/module.mjs +630 -142
  17. package/dist/registry.mjs +10 -0
  18. package/dist/runtime/components/ScriptInstagramEmbed.d.vue.ts +53 -0
  19. package/dist/runtime/components/ScriptInstagramEmbed.vue +38 -0
  20. package/dist/runtime/components/ScriptInstagramEmbed.vue.d.ts +53 -0
  21. package/dist/runtime/components/ScriptXEmbed.d.vue.ts +82 -0
  22. package/dist/runtime/components/ScriptXEmbed.vue +76 -0
  23. package/dist/runtime/components/ScriptXEmbed.vue.d.ts +82 -0
  24. package/dist/runtime/composables/useScript.js +24 -3
  25. package/dist/runtime/composables/useScriptTriggerServiceWorker.d.ts +7 -0
  26. package/dist/runtime/composables/useScriptTriggerServiceWorker.js +39 -0
  27. package/dist/runtime/plugins/sw-register.client.d.ts +2 -0
  28. package/dist/runtime/plugins/sw-register.client.js +12 -0
  29. package/dist/runtime/registry/instagram-embed.d.ts +23 -0
  30. package/dist/runtime/registry/instagram-embed.js +22 -0
  31. package/dist/runtime/registry/lemon-squeezy.d.ts +0 -1
  32. package/dist/runtime/registry/plausible-analytics.js +2 -2
  33. package/dist/runtime/registry/tiktok-pixel.d.ts +1 -0
  34. package/dist/runtime/registry/tiktok-pixel.js +1 -0
  35. package/dist/runtime/registry/x-embed.d.ts +77 -0
  36. package/dist/runtime/registry/x-embed.js +41 -0
  37. package/dist/runtime/server/instagram-embed-asset.d.ts +2 -0
  38. package/dist/runtime/server/instagram-embed-asset.js +42 -0
  39. package/dist/runtime/server/instagram-embed-image.d.ts +2 -0
  40. package/dist/runtime/server/instagram-embed-image.js +54 -0
  41. package/dist/runtime/server/instagram-embed.d.ts +2 -0
  42. package/dist/runtime/server/instagram-embed.js +91 -0
  43. package/dist/runtime/server/proxy-handler.d.ts +6 -0
  44. package/dist/runtime/server/proxy-handler.js +230 -0
  45. package/dist/runtime/server/sw-handler.d.ts +2 -0
  46. package/dist/runtime/server/sw-handler.js +25 -0
  47. package/dist/runtime/server/utils/privacy.d.ts +97 -0
  48. package/dist/runtime/server/utils/privacy.js +268 -0
  49. package/dist/runtime/server/x-embed-image.d.ts +2 -0
  50. package/dist/runtime/server/x-embed-image.js +53 -0
  51. package/dist/runtime/server/x-embed.d.ts +49 -0
  52. package/dist/runtime/server/x-embed.js +31 -0
  53. package/dist/runtime/sw/proxy-sw.template.d.ts +1 -0
  54. package/dist/runtime/sw/proxy-sw.template.js +54 -0
  55. package/dist/runtime/types.d.ts +29 -0
  56. package/dist/runtime/utils/pure.d.ts +13 -0
  57. package/dist/runtime/utils/pure.js +67 -0
  58. package/dist/runtime/utils.d.ts +1 -1
  59. package/dist/runtime/utils.js +2 -1
  60. package/dist/types.d.mts +1 -1
  61. package/package.json +27 -26
  62. package/dist/client/_nuxt/Bdf7Qtwg.js +0 -1
  63. package/dist/client/_nuxt/CoyZWCgl.js +0 -162
  64. package/dist/client/_nuxt/Ds1k3yKJ.js +0 -1
  65. package/dist/client/_nuxt/builds/meta/62574f80-71d4-4f9e-8b96-145c85230d99.json +0 -1
  66. package/dist/client/_nuxt/entry.BjfcJo5q.css +0 -1
  67. package/dist/client/_nuxt/error-404.D45Vtjcx.css +0 -1
  68. package/dist/client/_nuxt/error-500.BOm1rWQf.css +0 -1
package/dist/module.mjs CHANGED
@@ -1,12 +1,13 @@
1
- import { useNuxt, extendViteConfig, useLogger, addDevServerHandler, tryUseNuxt, logger as logger$1, addTypeTemplate, defineNuxtModule, createResolver, addImports, addComponentsDir, addTemplate, addPluginTemplate, addBuildPlugin, addServerHandler, hasNuxtModule } from '@nuxt/kit';
1
+ import { useNuxt, extendViteConfig, useLogger, addDevServerHandler, extendRouteRules, tryUseNuxt, logger as logger$1, addTypeTemplate, defineNuxtModule, createResolver, hasNuxtModule, addImports, addComponentsDir, addTemplate, addServerHandler, addPluginTemplate, addBuildPlugin } from '@nuxt/kit';
2
+ import { existsSync, readFileSync } from 'node:fs';
2
3
  import { defu } from 'defu';
3
4
  import { resolvePackageJSON, readPackageJSON } from 'pkg-types';
4
- import { existsSync } from 'node:fs';
5
+ import { addCustomTab } from '@nuxt/devtools-kit';
5
6
  import { createHash } from 'node:crypto';
6
7
  import fsp from 'node:fs/promises';
7
8
  import { createUnplugin } from 'unplugin';
8
9
  import MagicString from 'magic-string';
9
- import { asyncWalk, walk } from 'estree-walker';
10
+ import { parseAndWalk } from 'oxc-walker';
10
11
  import { joinURL, parseURL, parseQuery, hasProtocol } from 'ufo';
11
12
  import { hash } from 'ohash';
12
13
  import { join, resolve, relative } from 'pathe';
@@ -15,6 +16,7 @@ import { fetch, $fetch } from 'ofetch';
15
16
  import { lazyEventHandler, eventHandler, createError } from 'h3';
16
17
  import { createStorage } from 'unstorage';
17
18
  import fsDriver from 'unstorage/drivers/fs-lite';
19
+ import { rewriteScriptUrls } from '../dist/runtime/utils/pure.js';
18
20
  import { pathToFileURL } from 'node:url';
19
21
  import { isCI, provider } from 'std-env';
20
22
  import { registry } from './registry.mjs';
@@ -45,20 +47,18 @@ async function setupDevToolsUI(options, resolve, nuxt = useNuxt()) {
45
47
  };
46
48
  });
47
49
  }
48
- nuxt.hook("devtools:customTabs", (tabs) => {
49
- tabs.push({
50
- // unique identifier
51
- name: "nuxt-scripts",
52
- // title to display in the tab
53
- title: "Scripts",
54
- // any icon from Iconify, or a URL to an image
55
- icon: "carbon:script",
56
- // iframe view
57
- view: {
58
- type: "iframe",
59
- src: DEVTOOLS_UI_ROUTE
60
- }
61
- });
50
+ addCustomTab({
51
+ // unique identifier
52
+ name: "nuxt-scripts",
53
+ // title to display in the tab
54
+ title: "Scripts",
55
+ // any icon from Iconify, or a URL to an image
56
+ icon: "carbon:script",
57
+ // iframe view
58
+ view: {
59
+ type: "iframe",
60
+ src: DEVTOOLS_UI_ROUTE
61
+ }
62
62
  });
63
63
  }
64
64
 
@@ -86,6 +86,9 @@ function setupPublicAssetStrategy(options = {}) {
86
86
  const scriptDescriptor = renderedScript.get(join(assetsBaseURL, event.path.slice(1)));
87
87
  if (!scriptDescriptor || scriptDescriptor instanceof Error)
88
88
  throw createError({ statusCode: 404 });
89
+ if (scriptDescriptor.content) {
90
+ return scriptDescriptor.content;
91
+ }
89
92
  const key = `bundle:${filename}`;
90
93
  let res = await storage.getItemRaw(key);
91
94
  if (!res) {
@@ -97,31 +100,204 @@ function setupPublicAssetStrategy(options = {}) {
97
100
  })
98
101
  });
99
102
  if (nuxt.options.dev) {
100
- nuxt.options.routeRules ||= {};
101
- nuxt.options.routeRules[joinURL(assetsBaseURL, "**")] = {
103
+ extendRouteRules(joinURL(assetsBaseURL, "**"), {
102
104
  cache: {
103
105
  maxAge: ONE_YEAR_IN_SECONDS
104
106
  }
105
- };
107
+ });
106
108
  }
107
- nuxt.options.nitro.publicAssets ||= [];
108
109
  const cacheDir = join(nuxt.options.buildDir, "cache", "scripts");
109
- nuxt.options.nitro.publicAssets.push();
110
- nuxt.options.nitro = defu(nuxt.options.nitro, {
111
- publicAssets: [{
110
+ nuxt.hook("nitro:config", (nitroConfig) => {
111
+ nitroConfig.publicAssets ||= [];
112
+ nitroConfig.publicAssets.push({
112
113
  dir: cacheDir,
113
114
  maxAge: ONE_YEAR_IN_SECONDS,
114
115
  baseURL: assetsBaseURL
115
- }],
116
- prerender: {
117
- ignore: [assetsBaseURL]
118
- }
116
+ });
117
+ nitroConfig.prerender ||= {};
118
+ nitroConfig.prerender.ignore ||= [];
119
+ nitroConfig.prerender.ignore.push(assetsBaseURL);
119
120
  });
120
121
  return {
121
122
  renderedScript
122
123
  };
123
124
  }
124
125
 
126
+ function buildProxyConfig(collectPrefix) {
127
+ return {
128
+ googleAnalytics: {
129
+ rewrite: [
130
+ // Modern gtag.js uses www.google.com/g/collect
131
+ { from: "www.google.com/g/collect", to: `${collectPrefix}/ga/g/collect` },
132
+ // Older gtag.js constructs URLs dynamically: "https://"+(subdomain)+".google-analytics.com/g/collect"
133
+ // We need to catch the suffix pattern with leading dot
134
+ { from: ".google-analytics.com/g/collect", to: `${collectPrefix}/ga/g/collect` },
135
+ { from: ".analytics.google.com/g/collect", to: `${collectPrefix}/ga/g/collect` },
136
+ // Full domain patterns for static URLs
137
+ { from: "www.google-analytics.com/g/collect", to: `${collectPrefix}/ga/g/collect` },
138
+ { from: "analytics.google.com/g/collect", to: `${collectPrefix}/ga/g/collect` },
139
+ // Legacy endpoints still used by some scripts
140
+ { from: "www.google-analytics.com", to: `${collectPrefix}/ga` },
141
+ { from: "analytics.google.com", to: `${collectPrefix}/ga` },
142
+ // DoubleClick tracking (used by GA4 for ads/conversions)
143
+ { from: "stats.g.doubleclick.net/g/collect", to: `${collectPrefix}/ga/g/collect` },
144
+ { from: "stats.g.doubleclick.net", to: `${collectPrefix}/ga-dc` },
145
+ // Google Ads/Syndication tracking
146
+ { from: "pagead2.googlesyndication.com", to: `${collectPrefix}/ga-syn` },
147
+ { from: "www.googleadservices.com", to: `${collectPrefix}/ga-ads` },
148
+ { from: "googleads.g.doubleclick.net", to: `${collectPrefix}/ga-gads` }
149
+ ],
150
+ routes: {
151
+ [`${collectPrefix}/ga/**`]: { proxy: "https://www.google-analytics.com/**" },
152
+ [`${collectPrefix}/ga-dc/**`]: { proxy: "https://stats.g.doubleclick.net/**" },
153
+ [`${collectPrefix}/ga-syn/**`]: { proxy: "https://pagead2.googlesyndication.com/**" },
154
+ [`${collectPrefix}/ga-ads/**`]: { proxy: "https://www.googleadservices.com/**" },
155
+ [`${collectPrefix}/ga-gads/**`]: { proxy: "https://googleads.g.doubleclick.net/**" }
156
+ }
157
+ },
158
+ googleTagManager: {
159
+ rewrite: [
160
+ { from: "www.googletagmanager.com", to: `${collectPrefix}/gtm` }
161
+ ],
162
+ routes: {
163
+ [`${collectPrefix}/gtm/**`]: { proxy: "https://www.googletagmanager.com/**" }
164
+ }
165
+ },
166
+ metaPixel: {
167
+ rewrite: [
168
+ // SDK script loading
169
+ { from: "connect.facebook.net", to: `${collectPrefix}/meta` },
170
+ // Tracking pixel endpoint (www and non-www)
171
+ { from: "www.facebook.com/tr", to: `${collectPrefix}/meta-tr` },
172
+ { from: "facebook.com/tr", to: `${collectPrefix}/meta-tr` },
173
+ // Additional Meta tracking domains
174
+ { from: "pixel.facebook.com", to: `${collectPrefix}/meta-px` },
175
+ { from: "www.facebook.com/plugins", to: `${collectPrefix}/meta-plugins` }
176
+ ],
177
+ routes: {
178
+ [`${collectPrefix}/meta/**`]: { proxy: "https://connect.facebook.net/**" },
179
+ [`${collectPrefix}/meta-tr/**`]: { proxy: "https://www.facebook.com/tr/**" },
180
+ [`${collectPrefix}/meta-px/**`]: { proxy: "https://pixel.facebook.com/**" },
181
+ [`${collectPrefix}/meta-plugins/**`]: { proxy: "https://www.facebook.com/plugins/**" }
182
+ }
183
+ },
184
+ tiktokPixel: {
185
+ rewrite: [
186
+ { from: "analytics.tiktok.com", to: `${collectPrefix}/tiktok` }
187
+ ],
188
+ routes: {
189
+ [`${collectPrefix}/tiktok/**`]: { proxy: "https://analytics.tiktok.com/**" }
190
+ }
191
+ },
192
+ segment: {
193
+ rewrite: [
194
+ { from: "api.segment.io", to: `${collectPrefix}/segment` },
195
+ { from: "cdn.segment.com", to: `${collectPrefix}/segment-cdn` }
196
+ ],
197
+ routes: {
198
+ [`${collectPrefix}/segment/**`]: { proxy: "https://api.segment.io/**" },
199
+ [`${collectPrefix}/segment-cdn/**`]: { proxy: "https://cdn.segment.com/**" }
200
+ }
201
+ },
202
+ xPixel: {
203
+ rewrite: [
204
+ { from: "analytics.twitter.com", to: `${collectPrefix}/x` },
205
+ { from: "t.co", to: `${collectPrefix}/x-t` }
206
+ ],
207
+ routes: {
208
+ [`${collectPrefix}/x/**`]: { proxy: "https://analytics.twitter.com/**" },
209
+ [`${collectPrefix}/x-t/**`]: { proxy: "https://t.co/**" }
210
+ }
211
+ },
212
+ snapchatPixel: {
213
+ rewrite: [
214
+ { from: "tr.snapchat.com", to: `${collectPrefix}/snap` }
215
+ ],
216
+ routes: {
217
+ [`${collectPrefix}/snap/**`]: { proxy: "https://tr.snapchat.com/**" }
218
+ }
219
+ },
220
+ redditPixel: {
221
+ rewrite: [
222
+ { from: "alb.reddit.com", to: `${collectPrefix}/reddit` }
223
+ ],
224
+ routes: {
225
+ [`${collectPrefix}/reddit/**`]: { proxy: "https://alb.reddit.com/**" }
226
+ }
227
+ },
228
+ clarity: {
229
+ rewrite: [
230
+ // Main clarity domain
231
+ { from: "www.clarity.ms", to: `${collectPrefix}/clarity` },
232
+ // Script loader (the actual SDK is loaded from here)
233
+ { from: "scripts.clarity.ms", to: `${collectPrefix}/clarity-scripts` },
234
+ // Data collection endpoint
235
+ { from: "d.clarity.ms", to: `${collectPrefix}/clarity-data` },
236
+ // Event collection endpoint
237
+ { from: "e.clarity.ms", to: `${collectPrefix}/clarity-events` }
238
+ ],
239
+ routes: {
240
+ [`${collectPrefix}/clarity/**`]: { proxy: "https://www.clarity.ms/**" },
241
+ [`${collectPrefix}/clarity-scripts/**`]: { proxy: "https://scripts.clarity.ms/**" },
242
+ [`${collectPrefix}/clarity-data/**`]: { proxy: "https://d.clarity.ms/**" },
243
+ [`${collectPrefix}/clarity-events/**`]: { proxy: "https://e.clarity.ms/**" }
244
+ }
245
+ },
246
+ hotjar: {
247
+ rewrite: [
248
+ // Static assets
249
+ { from: "static.hotjar.com", to: `${collectPrefix}/hotjar` },
250
+ // Script loader (bootstrap script loads the main SDK from here)
251
+ { from: "script.hotjar.com", to: `${collectPrefix}/hotjar-script` },
252
+ // Configuration/variables
253
+ { from: "vars.hotjar.com", to: `${collectPrefix}/hotjar-vars` },
254
+ // Data ingestion endpoint
255
+ { from: "in.hotjar.com", to: `${collectPrefix}/hotjar-in` },
256
+ // Video capture
257
+ { from: "vc.hotjar.com", to: `${collectPrefix}/hotjar-vc` },
258
+ // Metrics/telemetry
259
+ { from: "metrics.hotjar.io", to: `${collectPrefix}/hotjar-metrics` },
260
+ // Insights (ContentSquare integration)
261
+ { from: "insights.hotjar.com", to: `${collectPrefix}/hotjar-insights` }
262
+ ],
263
+ routes: {
264
+ [`${collectPrefix}/hotjar/**`]: { proxy: "https://static.hotjar.com/**" },
265
+ [`${collectPrefix}/hotjar-script/**`]: { proxy: "https://script.hotjar.com/**" },
266
+ [`${collectPrefix}/hotjar-vars/**`]: { proxy: "https://vars.hotjar.com/**" },
267
+ [`${collectPrefix}/hotjar-in/**`]: { proxy: "https://in.hotjar.com/**" },
268
+ [`${collectPrefix}/hotjar-vc/**`]: { proxy: "https://vc.hotjar.com/**" },
269
+ [`${collectPrefix}/hotjar-metrics/**`]: { proxy: "https://metrics.hotjar.io/**" },
270
+ [`${collectPrefix}/hotjar-insights/**`]: { proxy: "https://insights.hotjar.com/**" }
271
+ }
272
+ }
273
+ };
274
+ }
275
+ function getProxyConfig(key, collectPrefix) {
276
+ const configs = buildProxyConfig(collectPrefix);
277
+ return configs[key];
278
+ }
279
+ function getAllProxyConfigs(collectPrefix) {
280
+ return buildProxyConfig(collectPrefix);
281
+ }
282
+ function getSWInterceptRules(collectPrefix) {
283
+ const configs = buildProxyConfig(collectPrefix);
284
+ const rules = [];
285
+ for (const config of Object.values(configs)) {
286
+ if (!config.routes)
287
+ continue;
288
+ for (const [localPath, { proxy }] of Object.entries(config.routes)) {
289
+ const match = proxy.match(/^https?:\/\/([^/]+)(\/.*)?\/\*\*$/);
290
+ if (match?.[1]) {
291
+ const domain = match[1];
292
+ const pathPrefix = match[2] || "";
293
+ const target = localPath.replace(/\/\*\*$/, "");
294
+ rules.push({ pattern: domain, pathPrefix, target });
295
+ }
296
+ }
297
+ }
298
+ return rules;
299
+ }
300
+
125
301
  function isVue(id, opts = {}) {
126
302
  const { search } = parseURL(decodeURIComponent(pathToFileURL(id).href));
127
303
  if (id.endsWith(".vue") && !search) {
@@ -178,7 +354,7 @@ function normalizeScriptData(src, assetsBaseURL = "/_scripts") {
178
354
  return { url: src };
179
355
  }
180
356
  async function downloadScript(opts, renderedScript, fetchOptions, cacheMaxAge) {
181
- const { src, url, filename, forceDownload, integrity } = opts;
357
+ const { src, url, filename, forceDownload, integrity, proxyRewrites } = opts;
182
358
  if (src === url || !filename) {
183
359
  return;
184
360
  }
@@ -186,7 +362,8 @@ async function downloadScript(opts, renderedScript, fetchOptions, cacheMaxAge) {
186
362
  const scriptContent = renderedScript.get(src);
187
363
  let res = scriptContent instanceof Error ? void 0 : scriptContent?.content;
188
364
  if (!res) {
189
- const cacheKey = `bundle:${filename}`;
365
+ const proxyRewritesHash = proxyRewrites?.length ? `-${hash(proxyRewrites)}` : "";
366
+ const cacheKey = proxyRewrites?.length ? `bundle-proxy:${filename.replace(".js", `${proxyRewritesHash}.js`)}` : `bundle:${filename}`;
190
367
  const shouldUseCache = !forceDownload && await storage.hasItem(cacheKey) && !await isCacheExpired(storage, filename, cacheMaxAge);
191
368
  if (shouldUseCache) {
192
369
  const cachedContent = await storage.getItemRaw(cacheKey);
@@ -212,8 +389,15 @@ async function downloadScript(opts, renderedScript, fetchOptions, cacheMaxAge) {
212
389
  size = contentLength ? Number(contentLength) / 1024 : 0;
213
390
  return Buffer.from(r._data || await r.arrayBuffer());
214
391
  });
215
- const integrityHash = integrity && res ? calculateIntegrity(res, integrity === true ? "sha384" : integrity) : void 0;
216
392
  await storage.setItemRaw(`bundle:${filename}`, res);
393
+ if (proxyRewrites?.length && res) {
394
+ const content = res.toString("utf-8");
395
+ const rewritten = rewriteScriptUrls(content, proxyRewrites);
396
+ res = Buffer.from(rewritten, "utf-8");
397
+ logger.debug(`Rewrote ${proxyRewrites.length} URL patterns in ${filename}`);
398
+ }
399
+ const integrityHash = integrity && res ? calculateIntegrity(res, integrity === true ? "sha384" : integrity) : void 0;
400
+ await storage.setItemRaw(cacheKey, res);
217
401
  await storage.setItem(`bundle-meta:${filename}`, {
218
402
  timestamp: Date.now(),
219
403
  src,
@@ -262,16 +446,21 @@ function NuxtScriptBundleTransformer(options = {
262
446
  return createUnplugin(() => {
263
447
  return {
264
448
  name: "nuxt:scripts:bundler-transformer",
265
- transformInclude(id) {
266
- return isVue(id, { type: ["template", "script"] }) || isJS(id);
267
- },
268
- async transform(code, id) {
269
- if (!code.includes("useScript"))
270
- return;
271
- const ast = this.parse(code);
272
- const s = new MagicString(code);
273
- await asyncWalk(ast, {
274
- async enter(_node) {
449
+ transform: {
450
+ filter: {
451
+ id: {
452
+ include: [/\.vue/, /\.[cm]?[jt]sx?$/],
453
+ exclude: [/\.(?:test|spec)\./]
454
+ }
455
+ },
456
+ async handler(code, id) {
457
+ if (!isVue(id, { type: ["template", "script"] }) && !isJS(id))
458
+ return;
459
+ if (!code.includes("useScript"))
460
+ return;
461
+ const s = new MagicString(code);
462
+ const deferredOps = [];
463
+ parseAndWalk(code, id, function(_node) {
275
464
  const calleeName = _node.callee?.name;
276
465
  if (!calleeName)
277
466
  return;
@@ -281,6 +470,11 @@ function NuxtScriptBundleTransformer(options = {
281
470
  const node = _node;
282
471
  let scriptSrcNode;
283
472
  let src;
473
+ let registryKey;
474
+ if (fnName !== "useScript") {
475
+ const baseName = fnName.replace(/^useScript/, "");
476
+ registryKey = baseName.length > 0 ? baseName.charAt(0).toLowerCase() + baseName.slice(1) : void 0;
477
+ }
284
478
  if (fnName === "useScript") {
285
479
  if (node.arguments[0]?.type === "Literal") {
286
480
  scriptSrcNode = node.arguments[0];
@@ -297,9 +491,7 @@ function NuxtScriptBundleTransformer(options = {
297
491
  }
298
492
  if (!registryNode.scriptBundling && !registryNode.src)
299
493
  return;
300
- const baseName = fnName.replace(/^useScript/, "");
301
- const registryKey = baseName.length > 0 ? baseName.charAt(0).toLowerCase() + baseName.slice(1) : "";
302
- const registryConfig = options.registryConfig?.[registryKey] || {};
494
+ const registryConfig = options.registryConfig?.[registryKey || ""] || {};
303
495
  const fnArg0 = {};
304
496
  if (node.arguments[0]?.type === "ObjectExpression") {
305
497
  const optionsNode = node.arguments[0];
@@ -319,6 +511,8 @@ function NuxtScriptBundleTransformer(options = {
319
511
  src = registryNode.scriptBundling && registryNode.scriptBundling(mergedOptions);
320
512
  if (src === false)
321
513
  return;
514
+ if (!src && registryNode.src)
515
+ src = registryNode.src;
322
516
  }
323
517
  }
324
518
  if (!scriptSrcNode && !src) {
@@ -333,8 +527,7 @@ function NuxtScriptBundleTransformer(options = {
333
527
  if (bundleProperty && bundleProperty.value.type === "Literal") {
334
528
  const bundleValue = bundleProperty.value.value;
335
529
  if (bundleValue === true || bundleValue === "force" || String(bundleValue) === "true") {
336
- const valueNode = bundleProperty.value;
337
- s.overwrite(valueNode.start, valueNode.end, `'unsupported'`);
530
+ s.overwrite(bundleProperty.value.start, bundleProperty.value.end, `'unsupported'`);
338
531
  }
339
532
  }
340
533
  }
@@ -351,8 +544,7 @@ function NuxtScriptBundleTransformer(options = {
351
544
  (p) => (p.key?.name === "bundle" || p.key?.value === "bundle") && p.type === "Property"
352
545
  );
353
546
  if (bundleProperty && bundleProperty.value.type === "Literal") {
354
- const value = bundleProperty.value;
355
- const bundleValue = value.value;
547
+ const bundleValue = bundleProperty.value.value;
356
548
  if (bundleValue !== true && bundleValue !== "force" && String(bundleValue) !== "true") {
357
549
  canBundle = false;
358
550
  return;
@@ -380,81 +572,104 @@ function NuxtScriptBundleTransformer(options = {
380
572
  canBundle = bundleValue === true || bundleValue === "force" || String(bundleValue) === "true";
381
573
  forceDownload = bundleValue === "force";
382
574
  }
575
+ const firstPartyOption = scriptOptions?.value.properties?.find((prop) => {
576
+ return prop.type === "Property" && prop.key?.name === "firstParty" && prop.value.type === "Literal";
577
+ });
578
+ let firstPartyOptOut = firstPartyOption?.value.value === false;
579
+ if (!firstPartyOptOut && node.arguments[1]?.type === "ObjectExpression") {
580
+ const secondArgFirstPartyProp = node.arguments[1].properties.find(
581
+ (p) => p.type === "Property" && p.key?.name === "firstParty" && p.value.type === "Literal"
582
+ );
583
+ firstPartyOptOut = secondArgFirstPartyProp?.value.value === false;
584
+ }
585
+ if (!firstPartyOptOut && node.arguments[0]?.type === "ObjectExpression") {
586
+ const firstArgFirstPartyProp = node.arguments[0].properties.find(
587
+ (p) => p.type === "Property" && p.key?.name === "firstParty" && p.value.type === "Literal"
588
+ );
589
+ firstPartyOptOut = firstArgFirstPartyProp?.value.value === false;
590
+ }
383
591
  if (canBundle) {
384
592
  const { url: _url, filename } = normalizeScriptData(src, options.assetsBaseURL);
385
- let url = _url;
386
- try {
387
- await downloadScript({ src, url, filename, forceDownload, integrity: options.integrity }, renderedScript, options.fetchOptions, options.cacheMaxAge);
388
- } catch (e) {
389
- if (options.fallbackOnSrcOnBundleFail) {
390
- logger.warn(`[Nuxt Scripts: Bundle Transformer] Failed to bundle ${src}. Fallback to remote loading.`);
391
- url = src;
392
- } else {
393
- const errorMessage = e?.message || "Unknown error";
394
- if (errorMessage.includes("timeout") || errorMessage.includes("network") || errorMessage.includes("ENOTFOUND")) {
395
- logger.error(`[Nuxt Scripts: Bundle Transformer] Network issue while bundling ${src}: ${errorMessage}`);
396
- logger.error(`[Nuxt Scripts: Bundle Transformer] Tip: Set 'fallbackOnSrcOnBundleFail: true' in module options or disable bundling in Docker environments`);
593
+ const script = options.scripts?.find((s2) => s2.import.name === fnName);
594
+ const proxyConfigKey = script?.proxy !== false ? script?.proxy || registryKey : void 0;
595
+ const proxyRewrites = options.firstPartyEnabled && !firstPartyOptOut && proxyConfigKey && options.firstPartyCollectPrefix ? getProxyConfig(proxyConfigKey, options.firstPartyCollectPrefix)?.rewrite : void 0;
596
+ deferredOps.push(async () => {
597
+ let url = _url;
598
+ try {
599
+ await downloadScript({ src, url, filename, forceDownload, proxyRewrites, integrity: options.integrity }, renderedScript, options.fetchOptions, options.cacheMaxAge);
600
+ } catch (e) {
601
+ if (options.fallbackOnSrcOnBundleFail) {
602
+ logger.warn(`[Nuxt Scripts: Bundle Transformer] Failed to bundle ${src}. Fallback to remote loading.`);
603
+ url = src;
604
+ } else {
605
+ const errorMessage = e?.message || "Unknown error";
606
+ if (errorMessage.includes("timeout") || errorMessage.includes("network") || errorMessage.includes("ENOTFOUND") || errorMessage.includes("certificate")) {
607
+ logger.error(`[Nuxt Scripts: Bundle Transformer] Network issue while bundling ${src}: ${errorMessage}`);
608
+ logger.error(`[Nuxt Scripts: Bundle Transformer] Tip: Set 'fallbackOnSrcOnBundleFail: true' in module options or disable bundling in Docker environments`);
609
+ }
610
+ throw e;
397
611
  }
398
- throw e;
399
612
  }
400
- }
401
- if (src === url) {
402
- if (src && src.startsWith("/"))
403
- logger.warn(`[Nuxt Scripts: Bundle Transformer] Relative scripts are already bundled. Skipping bundling for \`${src}\`.`);
404
- else
405
- logger.warn(`[Nuxt Scripts: Bundle Transformer] Failed to bundle ${src}.`);
406
- }
407
- const scriptMeta = renderedScript.get(url);
408
- const integrityHash = scriptMeta instanceof Error ? void 0 : scriptMeta?.integrity;
409
- if (scriptSrcNode) {
410
- if (integrityHash && fnName === "useScript" && node.arguments[0]?.type === "Literal") {
411
- s.overwrite(scriptSrcNode.start, scriptSrcNode.end, `{ src: '${url}', integrity: '${integrityHash}', crossorigin: 'anonymous' }`);
412
- } else if (integrityHash && fnName === "useScript" && node.arguments[0]?.type === "ObjectExpression") {
413
- s.overwrite(scriptSrcNode.start, scriptSrcNode.end, `'${url}'`);
414
- const objArg = node.arguments[0];
415
- s.appendLeft(objArg.end - 1, `, integrity: '${integrityHash}', crossorigin: 'anonymous'`);
416
- } else {
417
- s.overwrite(scriptSrcNode.start, scriptSrcNode.end, `'${url}'`);
613
+ if (src === url) {
614
+ if (src && src.startsWith("/"))
615
+ logger.warn(`[Nuxt Scripts: Bundle Transformer] Relative scripts are already bundled. Skipping bundling for \`${src}\`.`);
616
+ else
617
+ logger.warn(`[Nuxt Scripts: Bundle Transformer] Failed to bundle ${src}.`);
418
618
  }
419
- } else {
420
- const integrityProps = integrityHash ? `, integrity: '${integrityHash}', crossorigin: 'anonymous'` : "";
421
- if (node.arguments[0]) {
422
- const optionsNode = node.arguments[0];
423
- const scriptInputProperty = optionsNode.properties.find(
424
- (p) => p.key?.name === "scriptInput" || p.key?.value === "scriptInput"
425
- );
426
- if (scriptInputProperty) {
427
- const scriptInput = scriptInputProperty.value;
428
- if (scriptInput.type === "ObjectExpression") {
429
- const srcProperty = scriptInput.properties.find(
430
- (p) => p.key?.name === "src" || p.key?.value === "src"
431
- );
432
- if (srcProperty) {
433
- s.overwrite(srcProperty.value.start, srcProperty.value.end, `'${url}'`);
434
- if (integrityHash)
435
- s.appendLeft(scriptInput.end - 1, integrityProps);
436
- } else {
437
- s.appendRight(scriptInput.end - 1, `, src: '${url}'${integrityProps}`);
619
+ const scriptMeta = renderedScript.get(url);
620
+ const integrityHash = scriptMeta instanceof Error ? void 0 : scriptMeta?.integrity;
621
+ if (scriptSrcNode) {
622
+ if (integrityHash && fnName === "useScript" && node.arguments[0]?.type === "Literal") {
623
+ s.overwrite(scriptSrcNode.start, scriptSrcNode.end, `{ src: '${url}', integrity: '${integrityHash}', crossorigin: 'anonymous' }`);
624
+ } else if (integrityHash && fnName === "useScript" && node.arguments[0]?.type === "ObjectExpression") {
625
+ s.overwrite(scriptSrcNode.start, scriptSrcNode.end, `'${url}'`);
626
+ s.appendLeft(node.arguments[0].end - 1, `, integrity: '${integrityHash}', crossorigin: 'anonymous'`);
627
+ } else {
628
+ s.overwrite(scriptSrcNode.start, scriptSrcNode.end, `'${url}'`);
629
+ }
630
+ } else {
631
+ const integrityProps = integrityHash ? `, integrity: '${integrityHash}', crossorigin: 'anonymous'` : "";
632
+ if (node.arguments[0]) {
633
+ const optionsNode = node.arguments[0];
634
+ const scriptInputProperty = optionsNode.properties.find(
635
+ (p) => p.key?.name === "scriptInput" || p.key?.value === "scriptInput"
636
+ );
637
+ if (scriptInputProperty) {
638
+ const scriptInput = scriptInputProperty.value;
639
+ if (scriptInput.type === "ObjectExpression") {
640
+ const srcProperty = scriptInput.properties.find(
641
+ (p) => p.key?.name === "src" || p.key?.value === "src"
642
+ );
643
+ if (srcProperty) {
644
+ s.overwrite(srcProperty.value.start, srcProperty.value.end, `'${url}'`);
645
+ if (integrityHash)
646
+ s.appendLeft(scriptInput.end - 1, integrityProps);
647
+ } else {
648
+ s.appendRight(scriptInput.end - 1, `, src: '${url}'${integrityProps}`);
649
+ }
438
650
  }
651
+ } else {
652
+ s.appendRight(node.arguments[0].start + 1, ` scriptInput: { src: '${url}'${integrityProps} }, `);
439
653
  }
440
654
  } else {
441
- s.appendRight(node.arguments[0].start + 1, ` scriptInput: { src: '${url}'${integrityProps} }, `);
655
+ s.appendRight(node.callee.end, `({ scriptInput: { src: '${url}'${integrityProps} } })`);
442
656
  }
443
- } else {
444
- s.appendRight(node.callee.end, `({ scriptInput: { src: '${url}'${integrityProps} } })`);
445
657
  }
446
- }
658
+ });
447
659
  }
448
660
  }
449
661
  }
450
662
  }
663
+ });
664
+ for (const op of deferredOps) {
665
+ await op();
666
+ }
667
+ if (s.hasChanged()) {
668
+ return {
669
+ code: s.toString(),
670
+ map: s.generateMap({ includeContent: true, source: id })
671
+ };
451
672
  }
452
- });
453
- if (s.hasChanged()) {
454
- return {
455
- code: s.toString(),
456
- map: s.generateMap({ includeContent: true, source: id })
457
- };
458
673
  }
459
674
  }
460
675
  };
@@ -505,17 +720,18 @@ function NuxtScriptsCheckScripts() {
505
720
  return createUnplugin(() => {
506
721
  return {
507
722
  name: "nuxt-scripts:check-scripts",
508
- transformInclude(id) {
509
- return isVue(id, { type: ["script"] });
510
- },
511
- async transform(code) {
512
- if (!code.includes("useScript"))
513
- return;
514
- const ast = this.parse(code);
515
- let nameNode;
516
- let errorNode;
517
- walk(ast, {
518
- enter(_node) {
723
+ transform: {
724
+ filter: {
725
+ id: /\.vue/
726
+ },
727
+ handler(code, id) {
728
+ if (!isVue(id, { type: ["script"] }))
729
+ return;
730
+ if (!code.includes("useScript"))
731
+ return;
732
+ let nameNode;
733
+ let errorNode;
734
+ parseAndWalk(code, id, function(_node) {
519
735
  if (_node.type === "VariableDeclaration" && _node.declarations?.[0]?.id?.type === "ObjectPattern") {
520
736
  const objPattern = _node.declarations[0]?.id;
521
737
  for (const property of objPattern.properties) {
@@ -545,10 +761,10 @@ function NuxtScriptsCheckScripts() {
545
761
  }
546
762
  }
547
763
  }
764
+ });
765
+ if (errorNode) {
766
+ return this.error(new Error("You can't use a top-level await on $script as it will never resolve."));
548
767
  }
549
- });
550
- if (errorNode) {
551
- return this.error(new Error("You can't use a top-level await on $script as it will never resolve."));
552
768
  }
553
769
  }
554
770
  };
@@ -596,6 +812,7 @@ export {}`;
596
812
  function templateTriggerResolver(defaultScriptOptions) {
597
813
  const needsIdleTimeout = defaultScriptOptions?.trigger && typeof defaultScriptOptions.trigger === "object" && "idleTimeout" in defaultScriptOptions.trigger;
598
814
  const needsInteraction = defaultScriptOptions?.trigger && typeof defaultScriptOptions.trigger === "object" && "interaction" in defaultScriptOptions.trigger;
815
+ const needsServiceWorker = defaultScriptOptions?.trigger && typeof defaultScriptOptions.trigger === "object" && "serviceWorker" in defaultScriptOptions.trigger;
599
816
  const imports = [];
600
817
  if (needsIdleTimeout) {
601
818
  imports.push(`import { useScriptTriggerIdleTimeout } from '#nuxt-scripts/composables/useScriptTriggerIdleTimeout'`);
@@ -603,11 +820,15 @@ function templateTriggerResolver(defaultScriptOptions) {
603
820
  if (needsInteraction) {
604
821
  imports.push(`import { useScriptTriggerInteraction } from '#nuxt-scripts/composables/useScriptTriggerInteraction'`);
605
822
  }
823
+ if (needsServiceWorker) {
824
+ imports.push(`import { useScriptTriggerServiceWorker } from '#nuxt-scripts/composables/useScriptTriggerServiceWorker'`);
825
+ }
606
826
  return [
607
827
  ...imports,
608
828
  `export function resolveTrigger(trigger) {`,
609
829
  needsIdleTimeout ? ` if ('idleTimeout' in trigger) return useScriptTriggerIdleTimeout({ timeout: trigger.idleTimeout })` : "",
610
830
  needsInteraction ? ` if ('interaction' in trigger) return useScriptTriggerInteraction({ events: trigger.interaction })` : "",
831
+ needsServiceWorker ? ` if ('serviceWorker' in trigger) return useScriptTriggerServiceWorker()` : "",
611
832
  ` return null`,
612
833
  `}`
613
834
  ].filter(Boolean).join("\n");
@@ -624,6 +845,9 @@ function resolveTriggerForTemplate(trigger) {
624
845
  if ("interaction" in trigger) {
625
846
  return `useScriptTriggerInteraction({ events: ${JSON.stringify(trigger.interaction)} })`;
626
847
  }
848
+ if ("serviceWorker" in trigger) {
849
+ return `useScriptTriggerServiceWorker()`;
850
+ }
627
851
  }
628
852
  return null;
629
853
  }
@@ -634,27 +858,34 @@ function templatePlugin(config, registry) {
634
858
  }
635
859
  const imports = [];
636
860
  const inits = [];
861
+ const resolvedRegistryKeys = [];
637
862
  let needsIdleTimeoutImport = false;
638
863
  let needsInteractionImport = false;
864
+ let needsServiceWorkerImport = false;
639
865
  for (const [k, c] of Object.entries(config.registry || {})) {
640
- const importDefinition = registry.find((i) => i.import.name === `useScript${k.substring(0, 1).toUpperCase() + k.substring(1)}`);
866
+ const importDefinition = registry.find((i) => i.proxy === k || i.import.name === `useScript${k.substring(0, 1).toUpperCase() + k.substring(1)}`);
641
867
  if (importDefinition) {
868
+ resolvedRegistryKeys.push(k);
642
869
  imports.unshift(`import { ${importDefinition.import.name} } from '${importDefinition.import.from}'`);
643
- const args = (typeof c !== "object" ? {} : c) || {};
644
870
  if (c === "mock") {
645
- args.scriptOptions = { trigger: "manual", skipValidation: true };
646
- } else if (Array.isArray(c) && c.length === 2 && c[1]?.trigger) {
647
- const triggerResolved = resolveTriggerForTemplate(c[1].trigger);
871
+ inits.push(`const ${k} = ${importDefinition.import.name}({ scriptOptions: { trigger: 'manual', skipValidation: true } })`);
872
+ } else if (Array.isArray(c) && c.length === 2) {
873
+ const input = c[0] || {};
874
+ const scriptOptions = { ...c[1] };
875
+ const triggerResolved = resolveTriggerForTemplate(scriptOptions?.trigger);
648
876
  if (triggerResolved) {
649
- args.scriptOptions = { ...c[1] };
650
- if (args.scriptOptions) {
651
- args.scriptOptions.trigger = `__TRIGGER_${triggerResolved}__`;
652
- }
877
+ scriptOptions.trigger = "__TRIGGER_PLACEHOLDER__";
653
878
  if (triggerResolved.includes("useScriptTriggerIdleTimeout")) needsIdleTimeoutImport = true;
654
879
  if (triggerResolved.includes("useScriptTriggerInteraction")) needsInteractionImport = true;
880
+ if (triggerResolved.includes("useScriptTriggerServiceWorker")) needsServiceWorkerImport = true;
655
881
  }
882
+ const args = { ...input, scriptOptions };
883
+ const argsJson = triggerResolved ? JSON.stringify(args).replace(/"__TRIGGER_PLACEHOLDER__"/g, triggerResolved) : JSON.stringify(args);
884
+ inits.push(`const ${k} = ${importDefinition.import.name}(${argsJson})`);
885
+ } else {
886
+ const args = (typeof c !== "object" ? {} : c) || {};
887
+ inits.push(`const ${k} = ${importDefinition.import.name}(${JSON.stringify(args)})`);
656
888
  }
657
- inits.push(`const ${k} = ${importDefinition.import.name}(${JSON.stringify(args).replace(/"__TRIGGER_(.*?)__"/g, "$1")})`);
658
889
  }
659
890
  }
660
891
  for (const [k, c] of Object.entries(config.globals || {})) {
@@ -666,8 +897,10 @@ function templatePlugin(config, registry) {
666
897
  if (triggerResolved) {
667
898
  if (triggerResolved.includes("useScriptTriggerIdleTimeout")) needsIdleTimeoutImport = true;
668
899
  if (triggerResolved.includes("useScriptTriggerInteraction")) needsInteractionImport = true;
669
- const resolvedOptions = { ...options, trigger: `__TRIGGER_${triggerResolved}__` };
670
- inits.push(`const ${k} = useScript(${JSON.stringify({ key: k, ...typeof c[0] === "string" ? { src: c[0] } : c[0] })}, { ...${JSON.stringify(resolvedOptions).replace(/"__TRIGGER_(.*?)__"/g, "$1")}, use: () => ({ ${k}: window.${k} }) })`);
900
+ if (triggerResolved.includes("useScriptTriggerServiceWorker")) needsServiceWorkerImport = true;
901
+ const resolvedOptions = { ...options, trigger: "__TRIGGER_PLACEHOLDER__" };
902
+ const optionsJson = JSON.stringify(resolvedOptions).replace(/"__TRIGGER_PLACEHOLDER__"/g, triggerResolved);
903
+ inits.push(`const ${k} = useScript(${JSON.stringify({ key: k, ...typeof c[0] === "string" ? { src: c[0] } : c[0] })}, { ...${optionsJson}, use: () => ({ ${k}: window.${k} }) })`);
671
904
  } else {
672
905
  inits.push(`const ${k} = useScript(${JSON.stringify({ key: k, ...typeof c[0] === "string" ? { src: c[0] } : c[0] })}, { ...${JSON.stringify(c[1])}, use: () => ({ ${k}: window.${k} }) })`);
673
906
  }
@@ -676,8 +909,10 @@ function templatePlugin(config, registry) {
676
909
  if (triggerResolved) {
677
910
  if (triggerResolved.includes("useScriptTriggerIdleTimeout")) needsIdleTimeoutImport = true;
678
911
  if (triggerResolved.includes("useScriptTriggerInteraction")) needsInteractionImport = true;
679
- const resolvedOptions = { ...c, trigger: `__TRIGGER_${triggerResolved}__` };
680
- inits.push(`const ${k} = useScript(${JSON.stringify({ key: k, ...resolvedOptions }).replace(/"__TRIGGER_(.*?)__"/g, "$1")}, { use: () => ({ ${k}: window.${k} }) })`);
912
+ if (triggerResolved.includes("useScriptTriggerServiceWorker")) needsServiceWorkerImport = true;
913
+ const resolvedOptions = { ...c, trigger: "__TRIGGER_PLACEHOLDER__" };
914
+ const argsJson = JSON.stringify({ key: k, ...resolvedOptions }).replace(/"__TRIGGER_PLACEHOLDER__"/g, triggerResolved);
915
+ inits.push(`const ${k} = useScript(${argsJson}, { use: () => ({ ${k}: window.${k} }) })`);
681
916
  } else {
682
917
  inits.push(`const ${k} = useScript(${JSON.stringify({ key: k, ...c })}, { use: () => ({ ${k}: window.${k} }) })`);
683
918
  }
@@ -690,6 +925,9 @@ function templatePlugin(config, registry) {
690
925
  if (needsInteractionImport) {
691
926
  triggerImports.push(`import { useScriptTriggerInteraction } from '#nuxt-scripts/composables/useScriptTriggerInteraction'`);
692
927
  }
928
+ if (needsServiceWorkerImport) {
929
+ triggerImports.push(`import { useScriptTriggerServiceWorker } from '#nuxt-scripts/composables/useScriptTriggerServiceWorker'`);
930
+ }
693
931
  return [
694
932
  `import { useScript } from '#nuxt-scripts/composables/useScript'`,
695
933
  `import { defineNuxtPlugin } from 'nuxt/app'`,
@@ -702,12 +940,26 @@ function templatePlugin(config, registry) {
702
940
  ` parallel: true,`,
703
941
  ` setup() {`,
704
942
  ...inits.map((i) => ` ${i}`),
705
- ` return { provide: { $scripts: { ${[...Object.keys(config.globals || {}), ...Object.keys(config.registry || {})].join(", ")} } } }`,
943
+ ` return { provide: { $scripts: { ${[...Object.keys(config.globals || {}), ...resolvedRegistryKeys].join(", ")} } } }`,
706
944
  ` }`,
707
945
  `})`
708
946
  ].join("\n");
709
947
  }
710
948
 
949
+ const PARTYTOWN_FORWARDS = {
950
+ googleAnalytics: ["dataLayer.push", "gtag"],
951
+ plausible: ["plausible"],
952
+ fathom: ["fathom", "fathom.trackEvent", "fathom.trackPageview"],
953
+ umami: ["umami", "umami.track"],
954
+ matomo: ["_paq.push"],
955
+ segment: ["analytics", "analytics.track", "analytics.page", "analytics.identify"],
956
+ metaPixel: ["fbq"],
957
+ xPixel: ["twq"],
958
+ tiktokPixel: ["ttq.track", "ttq.page", "ttq.identify"],
959
+ snapchatPixel: ["snaptr"],
960
+ redditPixel: ["rdt"],
961
+ cloudflareWebAnalytics: ["__cfBeacon"]
962
+ };
711
963
  const module$1 = defineNuxtModule({
712
964
  meta: {
713
965
  name: "@nuxt/scripts",
@@ -717,6 +969,7 @@ const module$1 = defineNuxtModule({
717
969
  }
718
970
  },
719
971
  defaults: {
972
+ firstParty: true,
720
973
  defaultScriptOptions: {
721
974
  trigger: "onNuxtReady"
722
975
  },
@@ -756,7 +1009,8 @@ const module$1 = defineNuxtModule({
756
1009
  nuxt.options.runtimeConfig["nuxt-scripts"] = {
757
1010
  version,
758
1011
  // Private proxy config with API key (server-side only)
759
- googleStaticMapsProxy: config.googleStaticMapsProxy?.enabled ? { apiKey: nuxt.options.runtimeConfig.public.scripts?.googleMaps?.apiKey } : void 0
1012
+ googleStaticMapsProxy: config.googleStaticMapsProxy?.enabled ? { apiKey: nuxt.options.runtimeConfig.public.scripts?.googleMaps?.apiKey } : void 0,
1013
+ swTemplate: readFileSync(await resolvePath("./runtime/sw/proxy-sw.template.js"), "utf-8")
760
1014
  };
761
1015
  nuxt.options.runtimeConfig.public["nuxt-scripts"] = {
762
1016
  // expose for devtools
@@ -772,13 +1026,57 @@ const module$1 = defineNuxtModule({
772
1026
  config.registry
773
1027
  );
774
1028
  }
1029
+ if (config.defaultScriptOptions?.bundle !== void 0) {
1030
+ logger.warn(
1031
+ "`scripts.defaultScriptOptions.bundle` is deprecated. Use `scripts.firstParty: true` instead. First-party mode is now enabled by default."
1032
+ );
1033
+ }
1034
+ const staticPresets = ["static", "github-pages", "cloudflare-pages-static"];
1035
+ const preset = process.env.NITRO_PRESET || "";
1036
+ const isStaticPreset = staticPresets.includes(preset);
1037
+ const firstPartyEnabled = !!config.firstParty;
1038
+ const firstPartyPrefix = typeof config.firstParty === "object" ? config.firstParty.prefix : void 0;
1039
+ const firstPartyCollectPrefix = typeof config.firstParty === "object" ? config.firstParty.collectPrefix || "/_proxy" : "/_proxy";
1040
+ const firstPartyPrivacy = typeof config.firstParty === "object" ? config.firstParty.privacy ?? "anonymize" : "anonymize";
1041
+ const assetsPrefix = firstPartyPrefix || config.assets?.prefix || "/_scripts";
1042
+ if (config.partytown?.length) {
1043
+ config.registry = config.registry || {};
1044
+ const requiredForwards = [];
1045
+ for (const scriptKey of config.partytown) {
1046
+ const forwards = PARTYTOWN_FORWARDS[scriptKey];
1047
+ if (forwards) {
1048
+ requiredForwards.push(...forwards);
1049
+ } else if (import.meta.dev) {
1050
+ logger.warn(`[partytown] "${scriptKey}" has no known Partytown forwards configured. It may not work correctly or may require manual forward configuration.`);
1051
+ }
1052
+ const reg = config.registry;
1053
+ const existing = reg[scriptKey];
1054
+ if (Array.isArray(existing)) {
1055
+ existing[1] = { ...existing[1], partytown: true };
1056
+ } else if (existing && typeof existing === "object" && existing !== true && existing !== "mock") {
1057
+ reg[scriptKey] = [existing, { partytown: true }];
1058
+ } else if (existing === true || existing === "mock") {
1059
+ reg[scriptKey] = [{}, { partytown: true }];
1060
+ } else {
1061
+ reg[scriptKey] = [{}, { partytown: true }];
1062
+ }
1063
+ }
1064
+ if (requiredForwards.length && hasNuxtModule("@nuxtjs/partytown")) {
1065
+ const partytownConfig = nuxt.options.partytown || {};
1066
+ const existingForwards = partytownConfig.forward || [];
1067
+ const newForwards = [.../* @__PURE__ */ new Set([...existingForwards, ...requiredForwards])];
1068
+ nuxt.options.partytown = { ...partytownConfig, forward: newForwards };
1069
+ logger.info(`[partytown] Auto-configured forwards: ${requiredForwards.join(", ")}`);
1070
+ }
1071
+ }
775
1072
  const composables = [
776
1073
  "useScript",
777
1074
  "useScriptEventPage",
778
1075
  "useScriptTriggerConsent",
779
1076
  "useScriptTriggerElement",
780
1077
  "useScriptTriggerIdleTimeout",
781
- "useScriptTriggerInteraction"
1078
+ "useScriptTriggerInteraction",
1079
+ "useScriptTriggerServiceWorker"
782
1080
  ];
783
1081
  for (const composable of composables) {
784
1082
  addImports({
@@ -798,6 +1096,104 @@ const module$1 = defineNuxtModule({
798
1096
  return templateTriggerResolver(config.defaultScriptOptions);
799
1097
  }
800
1098
  });
1099
+ const swHandlerPath = await resolvePath("./runtime/server/sw-handler");
1100
+ logger.debug("[nuxt-scripts] First-party config:", { firstPartyEnabled, firstPartyPrivacy, firstPartyCollectPrefix });
1101
+ if (firstPartyEnabled && !nuxt.options.dev) {
1102
+ const swPath = "/_nuxt-scripts-sw.js";
1103
+ const swRules = getSWInterceptRules(firstPartyCollectPrefix);
1104
+ addServerHandler({
1105
+ route: swPath,
1106
+ handler: swHandlerPath
1107
+ });
1108
+ addPluginTemplate({
1109
+ filename: "nuxt-scripts-sw-register.client.mjs",
1110
+ getContents() {
1111
+ return `import { defineNuxtPlugin } from 'nuxt/app'
1112
+
1113
+ export default defineNuxtPlugin({
1114
+ name: 'nuxt-scripts:sw-register',
1115
+ enforce: 'pre',
1116
+ async setup() {
1117
+ if (!('serviceWorker' in navigator)) return;
1118
+
1119
+ try {
1120
+ const reg = await navigator.serviceWorker.register('${swPath}', { scope: '/' });
1121
+
1122
+ // Wait for SW to be active and controlling this page
1123
+ if (!navigator.serviceWorker.controller) {
1124
+ await new Promise((resolve) => {
1125
+ const onControllerChange = () => {
1126
+ navigator.serviceWorker.removeEventListener('controllerchange', onControllerChange);
1127
+ resolve();
1128
+ };
1129
+ navigator.serviceWorker.addEventListener('controllerchange', onControllerChange);
1130
+
1131
+ // Fallback timeout
1132
+ setTimeout(resolve, 2000);
1133
+ });
1134
+ }
1135
+ } catch (err) {
1136
+ console.warn('[nuxt-scripts] SW registration failed:', err);
1137
+ }
1138
+ },
1139
+ })
1140
+ `;
1141
+ }
1142
+ });
1143
+ addPluginTemplate({
1144
+ filename: "nuxt-scripts-beacon-intercept.client.mjs",
1145
+ getContents() {
1146
+ const rulesJson = JSON.stringify(swRules);
1147
+ return `export default defineNuxtPlugin({
1148
+ name: 'nuxt-scripts:beacon-intercept',
1149
+ enforce: 'pre',
1150
+ setup() {
1151
+ if (typeof navigator === 'undefined' || !navigator.sendBeacon) return;
1152
+
1153
+ const rules = ${rulesJson};
1154
+ const originalBeacon = navigator.sendBeacon.bind(navigator);
1155
+
1156
+ navigator.sendBeacon = (url, data) => {
1157
+ try {
1158
+ const parsed = new URL(url, window.location.origin);
1159
+
1160
+ // Check if this URL matches any of our proxy rules
1161
+ for (const rule of rules) {
1162
+ if (parsed.hostname === rule.pattern || parsed.hostname.endsWith('.' + rule.pattern)) {
1163
+ // Check path prefix if specified
1164
+ if (rule.pathPrefix && !parsed.pathname.startsWith(rule.pathPrefix)) {
1165
+ continue;
1166
+ }
1167
+
1168
+ // Rewrite to proxy: strip pathPrefix from original, prepend target
1169
+ const pathWithoutPrefix = rule.pathPrefix
1170
+ ? parsed.pathname.slice(rule.pathPrefix.length)
1171
+ : parsed.pathname;
1172
+ const separator = pathWithoutPrefix.startsWith('/') ? '' : '/';
1173
+ const proxyUrl = rule.target + separator + pathWithoutPrefix + parsed.search;
1174
+
1175
+ return originalBeacon(proxyUrl, data);
1176
+ }
1177
+ }
1178
+ } catch (e) {
1179
+ // URL parsing failed, pass through
1180
+ }
1181
+
1182
+ return originalBeacon(url, data);
1183
+ };
1184
+ },
1185
+ })
1186
+ `;
1187
+ }
1188
+ });
1189
+ nuxt.options.runtimeConfig.public["nuxt-scripts-sw"] = { path: swPath };
1190
+ const proxyHandlerPath = await resolvePath("./runtime/server/proxy-handler");
1191
+ logger.debug("[nuxt-scripts] Registering proxy handler:", `${firstPartyCollectPrefix}/**`, "->", proxyHandlerPath);
1192
+ addServerHandler({
1193
+ route: `${firstPartyCollectPrefix}/**`,
1194
+ handler: proxyHandlerPath
1195
+ });
1196
+ }
801
1197
  const scripts = await registry(resolvePath);
802
1198
  for (const script of scripts) {
803
1199
  if (script.import?.name) {
@@ -825,6 +1221,75 @@ const module$1 = defineNuxtModule({
825
1221
  });
826
1222
  }
827
1223
  const { renderedScript } = setupPublicAssetStrategy(config.assets);
1224
+ if (firstPartyEnabled) {
1225
+ const proxyConfigs = getAllProxyConfigs(firstPartyCollectPrefix);
1226
+ const registryKeys = Object.keys(config.registry || {});
1227
+ const neededRoutes = {};
1228
+ const unsupportedScripts = [];
1229
+ for (const key of registryKeys) {
1230
+ const script = registryScriptsWithImport.find((s) => s.import.name.toLowerCase() === `usescript${key.toLowerCase()}`);
1231
+ const proxyKey = script?.proxy || void 0;
1232
+ if (proxyKey) {
1233
+ const proxyConfig = proxyConfigs[proxyKey];
1234
+ if (proxyConfig?.routes) {
1235
+ Object.assign(neededRoutes, proxyConfig.routes);
1236
+ } else {
1237
+ unsupportedScripts.push(key);
1238
+ }
1239
+ }
1240
+ }
1241
+ if (unsupportedScripts.length && nuxt.options.dev) {
1242
+ logger.warn(
1243
+ `First-party mode is enabled but these scripts don't support it yet: ${unsupportedScripts.join(", ")}.
1244
+ They will load directly from third-party servers. Request support at https://github.com/nuxt/scripts/issues`
1245
+ );
1246
+ }
1247
+ const flatRoutes = {};
1248
+ for (const [path, config2] of Object.entries(neededRoutes)) {
1249
+ flatRoutes[path] = config2.proxy;
1250
+ }
1251
+ const allRewrites = [];
1252
+ for (const key of registryKeys) {
1253
+ const script = registryScriptsWithImport.find((s) => s.import.name.toLowerCase() === `usescript${key.toLowerCase()}`);
1254
+ const proxyKey = script?.proxy !== false ? script?.proxy || key : void 0;
1255
+ if (proxyKey) {
1256
+ const proxyConfig = proxyConfigs[proxyKey];
1257
+ if (proxyConfig?.rewrite) {
1258
+ allRewrites.push(...proxyConfig.rewrite);
1259
+ }
1260
+ }
1261
+ }
1262
+ nuxt.options.runtimeConfig["nuxt-scripts-proxy"] = {
1263
+ routes: flatRoutes,
1264
+ privacy: firstPartyPrivacy,
1265
+ rewrites: allRewrites
1266
+ };
1267
+ if (Object.keys(neededRoutes).length) {
1268
+ if (nuxt.options.dev) {
1269
+ const routeCount = Object.keys(neededRoutes).length;
1270
+ const scriptsCount = registryKeys.length;
1271
+ logger.success(`First-party mode enabled for ${scriptsCount} script(s), ${routeCount} proxy route(s) configured (privacy: ${firstPartyPrivacy})`);
1272
+ if (logger.level >= 4) {
1273
+ for (const [path, config2] of Object.entries(neededRoutes)) {
1274
+ logger.debug(` ${path} \u2192 ${config2.proxy}`);
1275
+ }
1276
+ }
1277
+ }
1278
+ }
1279
+ if (isStaticPreset) {
1280
+ logger.warn(
1281
+ `First-party collection endpoints require a server runtime (detected: ${preset || "static"}).
1282
+ Scripts will be bundled, but collection requests will not be proxied.
1283
+
1284
+ Options:
1285
+ 1. Configure platform rewrites (Vercel, Netlify, Cloudflare)
1286
+ 2. Switch to server-rendered mode (ssr: true)
1287
+ 3. Disable with firstParty: false
1288
+
1289
+ See: https://scripts.nuxt.com/docs/guides/first-party#static-hosting`
1290
+ );
1291
+ }
1292
+ }
828
1293
  const moduleInstallPromises = /* @__PURE__ */ new Map();
829
1294
  addBuildPlugin(NuxtScriptsCheckScripts(), {
830
1295
  dev: true
@@ -832,12 +1297,14 @@ const module$1 = defineNuxtModule({
832
1297
  addBuildPlugin(NuxtScriptBundleTransformer({
833
1298
  scripts: registryScriptsWithImport,
834
1299
  registryConfig: nuxt.options.runtimeConfig.public.scripts,
835
- defaultBundle: config.defaultScriptOptions?.bundle,
1300
+ defaultBundle: firstPartyEnabled || config.defaultScriptOptions?.bundle,
1301
+ firstPartyEnabled,
1302
+ firstPartyCollectPrefix,
836
1303
  moduleDetected(module) {
837
1304
  if (nuxt.options.dev && module !== "@nuxt/scripts" && !moduleInstallPromises.has(module) && !hasNuxtModule(module))
838
1305
  moduleInstallPromises.set(module, () => installNuxtModule(module));
839
1306
  },
840
- assetsBaseURL: config.assets?.prefix,
1307
+ assetsBaseURL: assetsPrefix,
841
1308
  fallbackOnSrcOnBundleFail: config.assets?.fallbackOnSrcOnBundleFail,
842
1309
  fetchOptions: config.assets?.fetchOptions,
843
1310
  cacheMaxAge: config.assets?.cacheMaxAge,
@@ -856,8 +1323,29 @@ const module$1 = defineNuxtModule({
856
1323
  handler: await resolvePath("./runtime/server/google-static-maps-proxy")
857
1324
  });
858
1325
  }
859
- if (nuxt.options.dev)
1326
+ addServerHandler({
1327
+ route: "/api/_scripts/x-embed",
1328
+ handler: await resolvePath("./runtime/server/x-embed")
1329
+ });
1330
+ addServerHandler({
1331
+ route: "/api/_scripts/x-embed-image",
1332
+ handler: await resolvePath("./runtime/server/x-embed-image")
1333
+ });
1334
+ addServerHandler({
1335
+ route: "/api/_scripts/instagram-embed",
1336
+ handler: await resolvePath("./runtime/server/instagram-embed")
1337
+ });
1338
+ addServerHandler({
1339
+ route: "/api/_scripts/instagram-embed-image",
1340
+ handler: await resolvePath("./runtime/server/instagram-embed-image")
1341
+ });
1342
+ addServerHandler({
1343
+ route: "/api/_scripts/instagram-embed-asset",
1344
+ handler: await resolvePath("./runtime/server/instagram-embed-asset")
1345
+ });
1346
+ if (nuxt.options.dev) {
860
1347
  setupDevToolsUI(config, resolvePath);
1348
+ }
861
1349
  }
862
1350
  });
863
1351