@nuxt/scripts 1.0.0-rc.1 → 1.0.0-rc.10

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 (164) hide show
  1. package/bin/cli.mjs +2 -0
  2. package/dist/cli.d.mts +2 -0
  3. package/dist/cli.d.ts +2 -0
  4. package/dist/cli.mjs +50 -0
  5. package/dist/devtools-client/200.html +1 -1
  6. package/dist/devtools-client/404.html +1 -1
  7. package/dist/devtools-client/_nuxt/{ajngqPCs.js → BgPDxVUn.js} +1 -1
  8. package/dist/devtools-client/_nuxt/{DKL6PHO3.js → BmlapxLP.js} +1 -1
  9. package/dist/devtools-client/_nuxt/CM2vefXI.js +188 -0
  10. package/dist/devtools-client/_nuxt/{CfOsp0mU.js → DAF5Qk9P.js} +1 -1
  11. package/dist/devtools-client/_nuxt/{B3kN3DAy.js → Dx6HhVmj.js} +1 -1
  12. package/dist/devtools-client/_nuxt/{dlaR8P-P.js → S8LiR9M1.js} +1 -1
  13. package/dist/devtools-client/_nuxt/builds/latest.json +1 -1
  14. package/dist/devtools-client/_nuxt/builds/meta/5458a3f2-af35-479c-8852-bf6f92fed611.json +1 -0
  15. package/dist/devtools-client/_nuxt/{entry.BwpOBArY.css → entry.BKkVrcJj.css} +1 -1
  16. package/dist/devtools-client/_nuxt/error-404.d44aGwWI.css +1 -0
  17. package/dist/devtools-client/_nuxt/error-500.NthMfIEt.css +1 -0
  18. package/dist/devtools-client/_nuxt/index.DZD1lwyI.css +1 -0
  19. package/dist/devtools-client/_nuxt/vBkR1GJq.js +1 -0
  20. package/dist/devtools-client/docs/index.html +1 -1
  21. package/dist/devtools-client/first-party/index.html +1 -1
  22. package/dist/devtools-client/index.html +1 -1
  23. package/dist/devtools-client/registry/index.html +1 -1
  24. package/dist/module.d.mts +66 -2
  25. package/dist/module.d.ts +66 -2
  26. package/dist/module.json +1 -1
  27. package/dist/module.mjs +144 -28
  28. package/dist/registry.d.mts +1 -0
  29. package/dist/registry.d.ts +1 -0
  30. package/dist/registry.mjs +14 -14
  31. package/dist/runtime/components/GoogleMaps/ScriptGoogleMaps.d.vue.ts +73 -97
  32. package/dist/runtime/components/GoogleMaps/ScriptGoogleMaps.vue +81 -58
  33. package/dist/runtime/components/GoogleMaps/ScriptGoogleMaps.vue.d.ts +73 -97
  34. package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsInfoWindow.d.vue.ts +2 -3
  35. package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsInfoWindow.vue +1 -0
  36. package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsInfoWindow.vue.d.ts +2 -3
  37. package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsMarker.d.vue.ts +2 -3
  38. package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsMarker.vue +2 -1
  39. package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsMarker.vue.d.ts +2 -3
  40. package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsMarkerClusterer.d.vue.ts +10 -43
  41. package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsMarkerClusterer.vue +3 -1
  42. package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsMarkerClusterer.vue.d.ts +10 -43
  43. package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsOverlayView.d.vue.ts +50 -30
  44. package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsOverlayView.vue +145 -104
  45. package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsOverlayView.vue.d.ts +50 -30
  46. package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsStaticMap.vue +7 -2
  47. package/dist/runtime/components/GoogleMaps/types.d.ts +42 -0
  48. package/dist/runtime/components/GoogleMaps/types.js +1 -0
  49. package/dist/runtime/components/GoogleMaps/useGoogleMapsResource.d.ts +50 -0
  50. package/dist/runtime/components/GoogleMaps/useGoogleMapsResource.js +76 -1
  51. package/dist/runtime/components/ScriptBlueskyEmbed.d.vue.ts +10 -12
  52. package/dist/runtime/components/ScriptBlueskyEmbed.vue +13 -10
  53. package/dist/runtime/components/ScriptBlueskyEmbed.vue.d.ts +10 -12
  54. package/dist/runtime/components/ScriptCarbonAds.d.vue.ts +4 -7
  55. package/dist/runtime/components/ScriptCarbonAds.vue +1 -0
  56. package/dist/runtime/components/ScriptCarbonAds.vue.d.ts +4 -7
  57. package/dist/runtime/components/ScriptCrisp.d.vue.ts +7 -11
  58. package/dist/runtime/components/ScriptCrisp.vue +1 -0
  59. package/dist/runtime/components/ScriptCrisp.vue.d.ts +7 -11
  60. package/dist/runtime/components/ScriptGoogleAdsense.d.vue.ts +4 -7
  61. package/dist/runtime/components/ScriptGoogleAdsense.vue +1 -0
  62. package/dist/runtime/components/ScriptGoogleAdsense.vue.d.ts +4 -7
  63. package/dist/runtime/components/ScriptInstagramEmbed.d.vue.ts +11 -13
  64. package/dist/runtime/components/ScriptInstagramEmbed.vue +4 -1
  65. package/dist/runtime/components/ScriptInstagramEmbed.vue.d.ts +11 -13
  66. package/dist/runtime/components/ScriptIntercom.d.vue.ts +7 -11
  67. package/dist/runtime/components/ScriptIntercom.vue +1 -0
  68. package/dist/runtime/components/ScriptIntercom.vue.d.ts +7 -11
  69. package/dist/runtime/components/ScriptLemonSqueezy.d.vue.ts +2 -3
  70. package/dist/runtime/components/ScriptLemonSqueezy.vue +1 -0
  71. package/dist/runtime/components/ScriptLemonSqueezy.vue.d.ts +2 -3
  72. package/dist/runtime/components/ScriptPayPalButtons.d.vue.ts +8 -13
  73. package/dist/runtime/components/ScriptPayPalButtons.vue +1 -0
  74. package/dist/runtime/components/ScriptPayPalButtons.vue.d.ts +8 -13
  75. package/dist/runtime/components/ScriptPayPalMessages.d.vue.ts +8 -13
  76. package/dist/runtime/components/ScriptPayPalMessages.vue +1 -0
  77. package/dist/runtime/components/ScriptPayPalMessages.vue.d.ts +8 -13
  78. package/dist/runtime/components/ScriptStripePricingTable.d.vue.ts +5 -9
  79. package/dist/runtime/components/ScriptStripePricingTable.vue +1 -0
  80. package/dist/runtime/components/ScriptStripePricingTable.vue.d.ts +5 -9
  81. package/dist/runtime/components/ScriptVimeoPlayer.d.vue.ts +8 -11
  82. package/dist/runtime/components/ScriptVimeoPlayer.vue +1 -0
  83. package/dist/runtime/components/ScriptVimeoPlayer.vue.d.ts +8 -11
  84. package/dist/runtime/components/ScriptXEmbed.d.vue.ts +10 -12
  85. package/dist/runtime/components/ScriptXEmbed.vue +12 -9
  86. package/dist/runtime/components/ScriptXEmbed.vue.d.ts +10 -12
  87. package/dist/runtime/components/ScriptYouTubePlayer.d.vue.ts +8 -13
  88. package/dist/runtime/components/ScriptYouTubePlayer.vue +1 -0
  89. package/dist/runtime/components/ScriptYouTubePlayer.vue.d.ts +8 -13
  90. package/dist/runtime/composables/useScript.js +17 -6
  91. package/dist/runtime/composables/useScriptProxyToken.d.ts +12 -0
  92. package/dist/runtime/composables/useScriptProxyToken.js +4 -0
  93. package/dist/runtime/composables/useScriptProxyUrl.d.ts +12 -0
  94. package/dist/runtime/composables/useScriptProxyUrl.js +27 -0
  95. package/dist/runtime/plugins/proxy-token.server.d.ts +10 -0
  96. package/dist/runtime/plugins/proxy-token.server.js +17 -0
  97. package/dist/runtime/registry/bing-uet.d.ts +189 -11
  98. package/dist/runtime/registry/bing-uet.js +16 -2
  99. package/dist/runtime/registry/bluesky-embed.d.ts +0 -4
  100. package/dist/runtime/registry/bluesky-embed.js +0 -4
  101. package/dist/runtime/registry/clarity.d.ts +6 -2
  102. package/dist/runtime/registry/clarity.js +12 -1
  103. package/dist/runtime/registry/google-analytics.d.ts +6 -2
  104. package/dist/runtime/registry/google-analytics.js +12 -1
  105. package/dist/runtime/registry/google-tag-manager.d.ts +6 -2
  106. package/dist/runtime/registry/google-tag-manager.js +10 -1
  107. package/dist/runtime/registry/gravatar.js +10 -13
  108. package/dist/runtime/registry/matomo-analytics.d.ts +9 -3
  109. package/dist/runtime/registry/matomo-analytics.js +28 -1
  110. package/dist/runtime/registry/meta-pixel.d.ts +8 -2
  111. package/dist/runtime/registry/meta-pixel.js +10 -1
  112. package/dist/runtime/registry/mixpanel-analytics.d.ts +12 -2
  113. package/dist/runtime/registry/mixpanel-analytics.js +16 -4
  114. package/dist/runtime/registry/posthog.d.ts +8 -2
  115. package/dist/runtime/registry/posthog.js +15 -4
  116. package/dist/runtime/registry/schemas.d.ts +65 -0
  117. package/dist/runtime/registry/schemas.js +75 -8
  118. package/dist/runtime/registry/tiktok-pixel.d.ts +16 -2
  119. package/dist/runtime/registry/tiktok-pixel.js +22 -1
  120. package/dist/runtime/registry/x-embed.d.ts +0 -4
  121. package/dist/runtime/registry/x-embed.js +0 -4
  122. package/dist/runtime/server/bluesky-embed-image.d.ts +1 -1
  123. package/dist/runtime/server/bluesky-embed.d.ts +1 -15
  124. package/dist/runtime/server/bluesky-embed.js +25 -6
  125. package/dist/runtime/server/google-maps-geocode-proxy.js +12 -8
  126. package/dist/runtime/server/google-static-maps-proxy.d.ts +1 -1
  127. package/dist/runtime/server/google-static-maps-proxy.js +17 -11
  128. package/dist/runtime/server/gravatar-proxy.d.ts +1 -1
  129. package/dist/runtime/server/gravatar-proxy.js +10 -10
  130. package/dist/runtime/server/instagram-embed-asset.d.ts +1 -1
  131. package/dist/runtime/server/instagram-embed-image.d.ts +1 -1
  132. package/dist/runtime/server/instagram-embed.d.ts +1 -16
  133. package/dist/runtime/server/instagram-embed.js +26 -125
  134. package/dist/runtime/server/proxy-handler.js +1 -2
  135. package/dist/runtime/server/utils/cached-upstream.d.ts +55 -0
  136. package/dist/runtime/server/utils/cached-upstream.js +65 -0
  137. package/dist/runtime/server/utils/embed-rewriters.d.ts +19 -0
  138. package/dist/runtime/server/utils/embed-rewriters.js +41 -0
  139. package/dist/runtime/server/utils/image-proxy.d.ts +3 -1
  140. package/dist/runtime/server/utils/image-proxy.js +11 -8
  141. package/dist/runtime/server/utils/instagram-embed.d.ts +16 -0
  142. package/dist/runtime/server/utils/instagram-embed.js +153 -0
  143. package/dist/runtime/server/utils/proxy-url.d.ts +9 -0
  144. package/dist/runtime/server/utils/proxy-url.js +21 -0
  145. package/dist/runtime/server/utils/sign-constants.d.ts +16 -0
  146. package/dist/runtime/server/utils/sign-constants.js +5 -0
  147. package/dist/runtime/server/utils/sign.d.ts +101 -0
  148. package/dist/runtime/server/utils/sign.js +91 -0
  149. package/dist/runtime/server/utils/withSigning.d.ts +23 -0
  150. package/dist/runtime/server/utils/withSigning.js +19 -0
  151. package/dist/runtime/server/x-embed-image.d.ts +1 -1
  152. package/dist/runtime/server/x-embed.js +23 -4
  153. package/dist/runtime/types.d.ts +41 -6
  154. package/dist/runtime/types.js +1 -0
  155. package/dist/stats.mjs +298 -338
  156. package/dist/types-source.mjs +537 -164
  157. package/dist/types.d.mts +2 -2
  158. package/package.json +10 -6
  159. package/dist/devtools-client/_nuxt/C8jhSQ8l.js +0 -1
  160. package/dist/devtools-client/_nuxt/CJD6wrkT.js +0 -188
  161. package/dist/devtools-client/_nuxt/builds/meta/b800a0be-5cab-4ea6-89e3-dd3a85690a73.json +0 -1
  162. package/dist/devtools-client/_nuxt/error-404.CvOVjXeC.css +0 -1
  163. package/dist/devtools-client/_nuxt/error-500.BIm53nmx.css +0 -1
  164. package/dist/devtools-client/_nuxt/index.CA-OpSj0.css +0 -1
package/dist/module.mjs CHANGED
@@ -1,5 +1,6 @@
1
- import { existsSync, readdirSync, readFileSync } from 'node:fs';
2
- import { useNuxt, addDevServerHandler, extendRouteRules, tryUseNuxt, createResolver, extendViteConfig, logger as logger$1, useLogger, addTypeTemplate, defineNuxtModule, addPluginTemplate, addServerHandler, addImports, addComponentsDir, addTemplate, hasNuxtModule, addBuildPlugin } from '@nuxt/kit';
1
+ import { createHash, randomBytes } from 'node:crypto';
2
+ import { existsSync, readFileSync, appendFileSync, writeFileSync, readdirSync } from 'node:fs';
3
+ import { useNuxt, addDevServerHandler, extendRouteRules, tryUseNuxt, createResolver, extendViteConfig, logger as logger$1, useLogger, addTypeTemplate, defineNuxtModule, addPluginTemplate, addServerHandler, addImports, addComponentsDir, addTemplate, hasNuxtModule, addBuildPlugin, addPlugin } from '@nuxt/kit';
3
4
  import { defu } from 'defu';
4
5
  import { join, resolve } from 'pathe';
5
6
  import { resolvePackageJSON, readPackageJSON } from 'pkg-types';
@@ -12,7 +13,6 @@ import { isCI, provider } from 'std-env';
12
13
  import { parseAndWalk, ScopeTracker, walk, ScopeTrackerFunction, ScopeTrackerIdentifier, ScopeTrackerFunctionParam, ScopeTrackerVariable } from 'oxc-walker';
13
14
  import { createUnplugin } from 'unplugin';
14
15
  import { pathToFileURL } from 'node:url';
15
- import { createHash } from 'node:crypto';
16
16
  import fsp from 'node:fs/promises';
17
17
  import { colors } from 'consola/utils';
18
18
  import MagicString from 'magic-string';
@@ -416,7 +416,8 @@ function NuxtScriptsCheckScripts() {
416
416
  });
417
417
  }
418
418
 
419
- function generateInterceptPluginContents(proxyPrefix) {
419
+ function generateInterceptPluginContents(proxyPrefix, options) {
420
+ const testMode = options?.testMode ?? false;
420
421
  return `export default defineNuxtPlugin({
421
422
  name: 'nuxt-scripts:intercept',
422
423
  enforce: 'pre',
@@ -461,8 +462,14 @@ function generateInterceptPluginContents(proxyPrefix) {
461
462
  return img;
462
463
  }
463
464
 
464
- globalThis.__nuxtScripts = {
465
- sendBeacon: (url, data) => origBeacon(proxyUrl(url), data),
465
+ globalThis.__nuxtScripts = {${testMode ? `
466
+ // Test mode: replace sendBeacon with fetch for immediate, observable requests
467
+ sendBeacon: (url, data) => {
468
+ const proxied = proxyUrl(url);
469
+ origFetch(proxied, { method: 'POST', body: data, keepalive: true }).catch(() => {});
470
+ return true;
471
+ },` : `
472
+ sendBeacon: (url, data) => origBeacon(proxyUrl(url), data),`}
466
473
  fetch: (url, opts) => {
467
474
  if (typeof url === 'string') return origFetch(proxyUrl(url), opts);
468
475
  if (url instanceof Request) return origFetch(new Request(proxyUrl(url.url), url), opts);
@@ -908,6 +915,7 @@ function NuxtScriptBundleTransformer(options = {
908
915
  const node = _node;
909
916
  let scriptSrcNode;
910
917
  let src;
918
+ let registryConfig = {};
911
919
  let registryKey;
912
920
  if (fnName !== "useScript") {
913
921
  const baseName = fnName.replace(USE_SCRIPT_RE, "");
@@ -930,7 +938,7 @@ function NuxtScriptBundleTransformer(options = {
930
938
  const bundleResolve = getBundleResolve(registryNode);
931
939
  if (!bundleResolve && !registryNode.src)
932
940
  return;
933
- const registryConfig = options.registryConfig?.[registryKey || ""] || {};
941
+ registryConfig = options.registryConfig?.[registryKey || ""] || {};
934
942
  const fnArg0 = {};
935
943
  if (node.arguments[0]?.type === "ObjectExpression") {
936
944
  const optionsNode = node.arguments[0];
@@ -978,12 +986,14 @@ function NuxtScriptBundleTransformer(options = {
978
986
  const registryScript = fnName !== "useScript" ? options.scripts?.find((s2) => s2.import.name === fnName) : void 0;
979
987
  let canBundle = !!registryScript?.bundle;
980
988
  let forceDownload = false;
989
+ let explicitBundleInCall = false;
981
990
  if (node.arguments[1]?.type === "ObjectExpression") {
982
991
  const scriptOptionsArg = node.arguments[1];
983
992
  const bundleProperty = scriptOptionsArg.properties.find(
984
993
  (p) => (p.key?.name === "bundle" || p.key?.value === "bundle") && p.type === "Property"
985
994
  );
986
995
  if (bundleProperty && bundleProperty.value.type === "Literal") {
996
+ explicitBundleInCall = true;
987
997
  const bundleValue = bundleProperty.value.value;
988
998
  if (bundleValue !== true && bundleValue !== "force" && String(bundleValue) !== "true") {
989
999
  canBundle = false;
@@ -1008,10 +1018,16 @@ function NuxtScriptBundleTransformer(options = {
1008
1018
  return prop.type === "Property" && prop.key?.name === "bundle" && prop.value.type === "Literal";
1009
1019
  });
1010
1020
  if (bundleOption) {
1021
+ explicitBundleInCall = true;
1011
1022
  const bundleValue = bundleOption.value.value;
1012
1023
  canBundle = bundleValue === true || bundleValue === "force" || String(bundleValue) === "true";
1013
1024
  forceDownload = bundleValue === "force";
1014
1025
  }
1026
+ if (!explicitBundleInCall && registryConfig?.scriptOptions?.bundle !== void 0) {
1027
+ const bundleValue = registryConfig.scriptOptions.bundle;
1028
+ canBundle = bundleValue === true || bundleValue === "force" || String(bundleValue) === "true";
1029
+ forceDownload = bundleValue === "force";
1030
+ }
1015
1031
  const rpiOption = scriptOptions?.value.properties?.find((prop) => {
1016
1032
  return prop.type === "Property" && prop.key?.name === "proxy" && prop.value.type === "Literal";
1017
1033
  });
@@ -1214,14 +1230,15 @@ function templatePlugin(config, registry) {
1214
1230
  for (const [k, c] of Object.entries(config.registry || {})) {
1215
1231
  if (c === false)
1216
1232
  continue;
1217
- const [, scriptOptions] = c;
1233
+ const entry = c;
1234
+ const [, scriptOptions] = entry;
1218
1235
  if (!scriptOptions?.trigger)
1219
1236
  continue;
1220
1237
  const importDefinition = registry.find((i) => i.import.name.toLowerCase() === `usescript${k.toLowerCase()}`);
1221
1238
  if (importDefinition) {
1222
1239
  resolvedRegistryKeys.push(k);
1223
1240
  imports.unshift(`import { ${importDefinition.import.name} } from '${importDefinition.import.from}'`);
1224
- const [input] = c;
1241
+ const [input] = entry;
1225
1242
  const opts = { ...scriptOptions };
1226
1243
  const triggerResolved = resolveTriggerForTemplate(opts.trigger);
1227
1244
  if (triggerResolved) {
@@ -1356,6 +1373,42 @@ function fixSelfClosingScriptComponents(nuxt) {
1356
1373
  }
1357
1374
  const UPPER_RE = /([A-Z])/g;
1358
1375
  const toScreamingSnake = (s) => s.replace(UPPER_RE, "_$1").toUpperCase();
1376
+ const PROXY_SECRET_ENV_KEY = "NUXT_SCRIPTS_PROXY_SECRET";
1377
+ const PROXY_SECRET_ENV_LINE_RE = /^NUXT_SCRIPTS_PROXY_SECRET=/m;
1378
+ const PROXY_SECRET_ENV_VALUE_RE = /^NUXT_SCRIPTS_PROXY_SECRET=(.+)$/m;
1379
+ function resolveProxySecret(rootDir, isDev, configSecret, autoGenerate = true) {
1380
+ if (configSecret)
1381
+ return { secret: configSecret, ephemeral: false, source: "config" };
1382
+ const envSecret = process.env[PROXY_SECRET_ENV_KEY];
1383
+ if (envSecret)
1384
+ return { secret: envSecret, ephemeral: false, source: "env" };
1385
+ if (!isDev || !autoGenerate)
1386
+ return void 0;
1387
+ const secret = randomBytes(32).toString("hex");
1388
+ const envPath = resolve(rootDir, ".env");
1389
+ const line = `${PROXY_SECRET_ENV_KEY}=${secret}
1390
+ `;
1391
+ try {
1392
+ if (existsSync(envPath)) {
1393
+ const contents = readFileSync(envPath, "utf-8");
1394
+ if (PROXY_SECRET_ENV_LINE_RE.test(contents)) {
1395
+ const match = contents.match(PROXY_SECRET_ENV_VALUE_RE);
1396
+ if (match?.[1])
1397
+ return { secret: match[1].trim(), ephemeral: false, source: "dotenv-generated" };
1398
+ }
1399
+ appendFileSync(envPath, contents.endsWith("\n") ? line : `
1400
+ ${line}`);
1401
+ } else {
1402
+ writeFileSync(envPath, `# Generated by @nuxt/scripts
1403
+ ${line}`);
1404
+ }
1405
+ process.env[PROXY_SECRET_ENV_KEY] = secret;
1406
+ return { secret, ephemeral: false, source: "dotenv-generated" };
1407
+ } catch {
1408
+ process.env[PROXY_SECRET_ENV_KEY] = secret;
1409
+ return { secret, ephemeral: true, source: "memory-generated" };
1410
+ }
1411
+ }
1359
1412
  function isProxyDisabled(registryKey, registry2, runtimeConfig) {
1360
1413
  const entry = registry2?.[registryKey];
1361
1414
  if (!entry)
@@ -1451,17 +1504,23 @@ const module$1 = defineNuxtModule({
1451
1504
  if (entry === false)
1452
1505
  continue;
1453
1506
  const input = entry[0];
1507
+ const scriptOptions = entry[1];
1454
1508
  const envDefaults = scripts.find((s) => s.registryKey === key)?.envDefaults;
1509
+ const base = {};
1455
1510
  if (!envDefaults || !Object.keys(envDefaults).length) {
1456
- registryWithDefaults[key] = input;
1457
- continue;
1511
+ Object.assign(base, input);
1512
+ } else {
1513
+ const envResolved = {};
1514
+ for (const [field, defaultValue] of Object.entries(envDefaults)) {
1515
+ const envKey = `NUXT_PUBLIC_SCRIPTS_${toScreamingSnake(key)}_${toScreamingSnake(field)}`;
1516
+ envResolved[field] = process.env[envKey] || defaultValue;
1517
+ }
1518
+ Object.assign(base, defu(input, envResolved));
1458
1519
  }
1459
- const envResolved = {};
1460
- for (const [field, defaultValue] of Object.entries(envDefaults)) {
1461
- const envKey = `NUXT_PUBLIC_SCRIPTS_${toScreamingSnake(key)}_${toScreamingSnake(field)}`;
1462
- envResolved[field] = process.env[envKey] || defaultValue;
1520
+ if (scriptOptions && Object.keys(scriptOptions).length > 0) {
1521
+ base.scriptOptions = scriptOptions;
1463
1522
  }
1464
- registryWithDefaults[key] = defu(input, envResolved);
1523
+ registryWithDefaults[key] = base;
1465
1524
  }
1466
1525
  nuxt.options.runtimeConfig.public.scripts = defu(
1467
1526
  nuxt.options.runtimeConfig.public.scripts || {},
@@ -1491,6 +1550,8 @@ const module$1 = defineNuxtModule({
1491
1550
  const composables = [
1492
1551
  "useScript",
1493
1552
  "useScriptEventPage",
1553
+ "useScriptProxyToken",
1554
+ "useScriptProxyUrl",
1494
1555
  "useScriptTriggerConsent",
1495
1556
  "useScriptTriggerElement",
1496
1557
  "useScriptTriggerIdleTimeout",
@@ -1533,10 +1594,23 @@ const module$1 = defineNuxtModule({
1533
1594
  const script = scripts.find((s) => s.registryKey === key);
1534
1595
  if (!script?.schema)
1535
1596
  continue;
1536
- const requiredFields = extractRequiredFields(script.schema);
1537
- const missing = requiredFields.filter((f) => !input[f]);
1538
- if (missing.length) {
1539
- logger.warn(`[nuxt-scripts] registry.${key}: missing required field${missing.length > 1 ? "s" : ""} ${missing.map((f) => `'${f}'`).join(", ")}. The script infrastructure is registered but will not function without ${missing.length > 1 ? "them" : "it"}.`);
1597
+ const isComponentOnly = !script.import;
1598
+ if (isComponentOnly) {
1599
+ if (scriptOptions && "trigger" in scriptOptions && scriptOptions.trigger !== false) {
1600
+ const pascal = key.charAt(0).toUpperCase() + key.slice(1);
1601
+ logger.warn(
1602
+ `[nuxt-scripts] registry.${key}: \`trigger\` has no effect on component-only scripts. Render <Script${pascal}> in your template to load the embed.`
1603
+ );
1604
+ }
1605
+ continue;
1606
+ }
1607
+ const willAutoLoad = scriptOptions && "trigger" in scriptOptions && scriptOptions.trigger !== false;
1608
+ if (willAutoLoad) {
1609
+ const requiredFields = extractRequiredFields(script.schema);
1610
+ const missing = requiredFields.filter((f) => !input[f]);
1611
+ if (missing.length) {
1612
+ logger.warn(`[nuxt-scripts] registry.${key}: missing required field${missing.length > 1 ? "s" : ""} ${missing.map((f) => `'${f}'`).join(", ")}. The script infrastructure is registered but will not function without ${missing.length > 1 ? "them" : "it"}.`);
1613
+ }
1540
1614
  }
1541
1615
  const envDefaultKeys = new Set(Object.keys(script.envDefaults || {}));
1542
1616
  const userProvidedFields = Object.keys(input).filter((f) => !envDefaultKeys.has(f));
@@ -1649,7 +1723,7 @@ They will load directly from third-party servers.`
1649
1723
  addPluginTemplate({
1650
1724
  filename: "nuxt-scripts-intercept.client.mjs",
1651
1725
  getContents() {
1652
- return generateInterceptPluginContents(proxyPrefix);
1726
+ return generateInterceptPluginContents(proxyPrefix, { testMode: !!nuxt.options.test });
1653
1727
  }
1654
1728
  });
1655
1729
  nuxt.options.runtimeConfig["nuxt-scripts-proxy"] = {
@@ -1661,12 +1735,12 @@ They will load directly from third-party servers.`
1661
1735
  if (totalDomains > 0 && nuxt.options.dev) {
1662
1736
  logger.success(`Proxy mode enabled for ${registryKeys.length} script(s), ${totalDomains} domain(s) proxied (privacy: ${privacyLabel})`);
1663
1737
  }
1664
- const staticPresets = ["static", "github-pages", "cloudflare-pages-static", "netlify-static", "azure-static", "firebase-static"];
1665
- const preset = process.env.NITRO_PRESET || "";
1666
- if (staticPresets.includes(preset)) {
1738
+ const proxyStaticPresets = ["static", "github-pages", "cloudflare-pages-static", "netlify-static", "azure-static", "firebase-static"];
1739
+ const proxyPreset = process.env.NITRO_PRESET || "";
1740
+ if (proxyStaticPresets.includes(proxyPreset)) {
1667
1741
  logger.warn(
1668
- `Proxy collection endpoints require a server runtime (detected: ${preset || "static"}).
1669
- Scripts will be bundled, but collection requests will not be proxied.
1742
+ `Proxy collection endpoints require a server runtime (detected: ${proxyPreset || "static"}).
1743
+ Scripts will be bundled, but collection requests will not be proxied and URL signing will be unavailable.
1670
1744
  Options: configure platform rewrites, switch to server-rendered mode, or disable with proxy: false.`
1671
1745
  );
1672
1746
  }
@@ -1713,6 +1787,7 @@ Options: configure platform rewrites, switch to server-rendered mode, or disable
1713
1787
  });
1714
1788
  const scriptsPrefix = config.prefix || "/_scripts";
1715
1789
  const enabledEndpoints = {};
1790
+ let anyHandlerRequiresSigning = false;
1716
1791
  for (const script of scripts) {
1717
1792
  if (!script.serverHandlers?.length || !script.registryKey)
1718
1793
  continue;
@@ -1721,11 +1796,14 @@ Options: configure platform rewrites, switch to server-rendered mode, or disable
1721
1796
  continue;
1722
1797
  enabledEndpoints[script.registryKey] = true;
1723
1798
  for (const handler of script.serverHandlers) {
1799
+ const resolvedRoute = handler.route.replace("/_scripts", scriptsPrefix);
1724
1800
  addServerHandler({
1725
- route: handler.route.replace("/_scripts", scriptsPrefix),
1801
+ route: resolvedRoute,
1726
1802
  handler: handler.handler,
1727
1803
  middleware: handler.middleware
1728
1804
  });
1805
+ if (handler.requiresSigning)
1806
+ anyHandlerRequiresSigning = true;
1729
1807
  }
1730
1808
  if (script.registryKey === "gravatar") {
1731
1809
  const gravatarConfig = config.registry?.gravatar?.[0] || {};
@@ -1745,7 +1823,45 @@ Options: configure platform rewrites, switch to server-rendered mode, or disable
1745
1823
  { endpoints: enabledEndpoints },
1746
1824
  nuxt.options.runtimeConfig.public["nuxt-scripts"]
1747
1825
  );
1826
+ const staticPresets = ["static", "github-pages", "cloudflare-pages-static", "netlify-static", "azure-static", "firebase-static"];
1827
+ const nitroPreset = process.env.NITRO_PRESET || "";
1828
+ const isStaticTarget = staticPresets.includes(nitroPreset);
1829
+ const isSpa = nuxt.options.ssr === false;
1830
+ if (anyHandlerRequiresSigning && (isSpa || isStaticTarget)) {
1831
+ logger.warn(
1832
+ `[security] URL signing requires a server runtime${isStaticTarget ? ` (detected preset: ${nitroPreset})` : " (ssr: false)"}.
1833
+ Proxy endpoints will work without signature verification.
1834
+ To enable signing, deploy with a server-rendered target or configure platform-level rewrites.`
1835
+ );
1836
+ } else if (anyHandlerRequiresSigning) {
1837
+ const proxySecretResolved = resolveProxySecret(
1838
+ nuxt.options.rootDir,
1839
+ !!nuxt.options.dev,
1840
+ config.security?.secret,
1841
+ config.security?.autoGenerateSecret !== false
1842
+ );
1843
+ if (proxySecretResolved?.source === "dotenv-generated")
1844
+ logger.info(`[security] Generated ${PROXY_SECRET_ENV_KEY} in .env for signed proxy URLs.`);
1845
+ else if (proxySecretResolved?.source === "memory-generated")
1846
+ logger.warn(`[security] Generated an in-memory ${PROXY_SECRET_ENV_KEY} (could not write .env). Signed URLs will break across restarts.`);
1847
+ if (proxySecretResolved?.secret) {
1848
+ const scriptsRuntime = nuxt.options.runtimeConfig["nuxt-scripts"];
1849
+ scriptsRuntime.proxySecret = proxySecretResolved.secret;
1850
+ if (config.security?.pageTokenMaxAge !== void 0)
1851
+ scriptsRuntime.pageTokenMaxAge = config.security.pageTokenMaxAge;
1852
+ addPlugin({
1853
+ src: await resolvePath("./runtime/plugins/proxy-token.server"),
1854
+ mode: "server"
1855
+ });
1856
+ } else if (!nuxt.options.dev) {
1857
+ logger.warn(
1858
+ `[security] ${PROXY_SECRET_ENV_KEY} is not set. Proxy endpoints will pass requests through without signature verification.
1859
+ Generate one with: npx @nuxt/scripts generate-secret
1860
+ Then set the env var: ${PROXY_SECRET_ENV_KEY}=<secret>`
1861
+ );
1862
+ }
1863
+ }
1748
1864
  }
1749
1865
  });
1750
1866
 
1751
- export { applyAutoInject, module$1 as default, isProxyDisabled };
1867
+ export { applyAutoInject, module$1 as default, isProxyDisabled, resolveProxySecret };
@@ -1,5 +1,6 @@
1
1
  import { ProxyPrivacyInput } from '../dist/runtime/server/utils/privacy.js';
2
2
  import { ScriptCapabilities, RegistryScript, RegistryScriptKey, ProxyConfig, ProxyCapability, ProxyAutoInject, ResolvedProxyAutoInject } from '../dist/runtime/types.js';
3
+ export { ScriptCapabilities } from '../dist/runtime/types.js';
3
4
 
4
5
  interface RegistryScriptMeta {
5
6
  /** Registry key (e.g. 'plausibleAnalytics') */
@@ -1,5 +1,6 @@
1
1
  import { ProxyPrivacyInput } from '../dist/runtime/server/utils/privacy.js';
2
2
  import { ScriptCapabilities, RegistryScript, RegistryScriptKey, ProxyConfig, ProxyCapability, ProxyAutoInject, ResolvedProxyAutoInject } from '../dist/runtime/types.js';
3
+ export { ScriptCapabilities } from '../dist/runtime/types.js';
3
4
 
4
5
  interface RegistryScriptMeta {
5
6
  /** Registry key (e.g. 'plausibleAnalytics') */
package/dist/registry.mjs CHANGED
@@ -126,7 +126,7 @@ const registryMeta = [
126
126
  m("intercom", "Intercom", "support", "useScriptIntercom", { bundle: true, proxy: true }, PRIVACY_IP_ONLY),
127
127
  m("crisp", "Crisp", "support", "useScriptCrisp", { bundle: true }, null),
128
128
  // cdn
129
- m("npm", "NPM", "cdn", "useScriptNpm", {}, null),
129
+ m("npm", "NPM", "cdn", "useScriptNpm", { bundle: true }, null),
130
130
  // utility
131
131
  m("googleRecaptcha", "Google reCAPTCHA", "utility", "useScriptGoogleRecaptcha", {}, null),
132
132
  m("googleSignIn", "Google Sign-In", "utility", "useScriptGoogleSignIn", {}, null),
@@ -346,7 +346,7 @@ async function registry(resolve) {
346
346
  return "https://cdn.mxpnl.com/libs/mixpanel-2-latest.min.js";
347
347
  }
348
348
  },
349
- partytown: { forwards: ["mixpanel", "mixpanel.init", "mixpanel.track", "mixpanel.identify", "mixpanel.people.set", "mixpanel.reset", "mixpanel.register"] }
349
+ partytown: { forwards: ["mixpanel", "mixpanel.init", "mixpanel.track", "mixpanel.identify", "mixpanel.people.set", "mixpanel.reset", "mixpanel.register", "mixpanel.opt_in_tracking", "mixpanel.opt_out_tracking"] }
350
350
  }),
351
351
  // ad
352
352
  def("bingUet", {
@@ -401,7 +401,7 @@ async function registry(resolve) {
401
401
  domains: ["analytics.tiktok.com", "mon.tiktok.com", "mcs.tiktok.com"],
402
402
  privacy: PRIVACY_FULL
403
403
  },
404
- partytown: { forwards: ["ttq.track", "ttq.page", "ttq.identify"] }
404
+ partytown: { forwards: ["ttq.track", "ttq.page", "ttq.identify", "ttq.grantConsent", "ttq.revokeConsent", "ttq.holdConsent"] }
405
405
  }),
406
406
  def("snapchatPixel", {
407
407
  schema: SnapTrPixelOptions,
@@ -503,7 +503,7 @@ async function registry(resolve) {
503
503
  domains: ["www.clarity.ms", "scripts.clarity.ms", "d.clarity.ms", "e.clarity.ms", "k.clarity.ms", "c.clarity.ms", "a.clarity.ms", "b.clarity.ms"],
504
504
  privacy: PRIVACY_HEATMAP
505
505
  },
506
- partytown: { forwards: [] }
506
+ partytown: { forwards: ["clarity"] }
507
507
  }),
508
508
  // payments
509
509
  def("stripe", {
@@ -554,8 +554,8 @@ async function registry(resolve) {
554
554
  envDefaults: { apiKey: "" },
555
555
  category: "content",
556
556
  serverHandlers: [
557
- { route: "/_scripts/proxy/google-static-maps", handler: "./runtime/server/google-static-maps-proxy" },
558
- { route: "/_scripts/proxy/google-maps-geocode", handler: "./runtime/server/google-maps-geocode-proxy" }
557
+ { route: "/_scripts/proxy/google-static-maps", handler: "./runtime/server/google-static-maps-proxy", requiresSigning: true },
558
+ { route: "/_scripts/proxy/google-maps-geocode", handler: "./runtime/server/google-maps-geocode-proxy", requiresSigning: true }
559
559
  ]
560
560
  }),
561
561
  def("blueskyEmbed", {
@@ -564,8 +564,8 @@ async function registry(resolve) {
564
564
  label: "Bluesky Embed",
565
565
  category: "content",
566
566
  serverHandlers: [
567
- { route: "/_scripts/embed/bluesky", handler: "./runtime/server/bluesky-embed" },
568
- { route: "/_scripts/embed/bluesky-image", handler: "./runtime/server/bluesky-embed-image" }
567
+ { route: "/_scripts/embed/bluesky", handler: "./runtime/server/bluesky-embed", requiresSigning: true },
568
+ { route: "/_scripts/embed/bluesky-image", handler: "./runtime/server/bluesky-embed-image", requiresSigning: true }
569
569
  ]
570
570
  }),
571
571
  def("instagramEmbed", {
@@ -574,9 +574,9 @@ async function registry(resolve) {
574
574
  label: "Instagram Embed",
575
575
  category: "content",
576
576
  serverHandlers: [
577
- { route: "/_scripts/embed/instagram", handler: "./runtime/server/instagram-embed" },
578
- { route: "/_scripts/embed/instagram-image", handler: "./runtime/server/instagram-embed-image" },
579
- { route: "/_scripts/embed/instagram-asset", handler: "./runtime/server/instagram-embed-asset" }
577
+ { route: "/_scripts/embed/instagram", handler: "./runtime/server/instagram-embed", requiresSigning: true },
578
+ { route: "/_scripts/embed/instagram-image", handler: "./runtime/server/instagram-embed-image", requiresSigning: true },
579
+ { route: "/_scripts/embed/instagram-asset", handler: "./runtime/server/instagram-embed-asset", requiresSigning: true }
580
580
  ]
581
581
  }),
582
582
  def("xEmbed", {
@@ -585,8 +585,8 @@ async function registry(resolve) {
585
585
  label: "X Embed",
586
586
  category: "content",
587
587
  serverHandlers: [
588
- { route: "/_scripts/embed/x", handler: "./runtime/server/x-embed" },
589
- { route: "/_scripts/embed/x-image", handler: "./runtime/server/x-embed-image" }
588
+ { route: "/_scripts/embed/x", handler: "./runtime/server/x-embed", requiresSigning: true },
589
+ { route: "/_scripts/embed/x-image", handler: "./runtime/server/x-embed-image", requiresSigning: true }
590
590
  ]
591
591
  }),
592
592
  // support
@@ -690,7 +690,7 @@ async function registry(resolve) {
690
690
  privacy: PRIVACY_IP_ONLY
691
691
  },
692
692
  serverHandlers: [
693
- { route: "/_scripts/proxy/gravatar", handler: "./runtime/server/gravatar-proxy" }
693
+ { route: "/_scripts/proxy/gravatar", handler: "./runtime/server/gravatar-proxy", requiresSigning: true }
694
694
  ]
695
695
  })
696
696
  ]);