@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
@@ -0,0 +1,21 @@
1
+ import { buildSignedProxyUrl } from "./sign.js";
2
+ export function buildProxyUrl(path, query, secret) {
3
+ if (secret)
4
+ return buildSignedProxyUrl(path, query, secret);
5
+ const parts = [];
6
+ for (const [key, value] of Object.entries(query)) {
7
+ if (value === void 0 || value === null)
8
+ continue;
9
+ const encodedKey = encodeURIComponent(key);
10
+ if (Array.isArray(value)) {
11
+ for (const item of value) {
12
+ if (item === void 0 || item === null)
13
+ continue;
14
+ parts.push(`${encodedKey}=${encodeURIComponent(String(item))}`);
15
+ }
16
+ } else {
17
+ parts.push(`${encodedKey}=${encodeURIComponent(String(value))}`);
18
+ }
19
+ }
20
+ return parts.length ? `${path}?${parts.join("&")}` : path;
21
+ }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Signing constants shared between server (HMAC) and client (page-token) code.
3
+ *
4
+ * Kept in a crypto-free module so client bundles can import the param names
5
+ * without pulling in `node:crypto`.
6
+ */
7
+ /** Query param name for the HMAC signature. */
8
+ export declare const SIG_PARAM = "sig";
9
+ /** Length of the hex signature (16 chars = 64 bits). */
10
+ export declare const SIG_LENGTH = 16;
11
+ /** Query param name for the page token. */
12
+ export declare const PAGE_TOKEN_PARAM = "_pt";
13
+ /** Query param name for the page token timestamp. */
14
+ export declare const PAGE_TOKEN_TS_PARAM = "_ts";
15
+ /** Default max age for page tokens in seconds (1 hour). */
16
+ export declare const PAGE_TOKEN_MAX_AGE = 3600;
@@ -0,0 +1,5 @@
1
+ export const SIG_PARAM = "sig";
2
+ export const SIG_LENGTH = 16;
3
+ export const PAGE_TOKEN_PARAM = "_pt";
4
+ export const PAGE_TOKEN_TS_PARAM = "_ts";
5
+ export const PAGE_TOKEN_MAX_AGE = 3600;
@@ -0,0 +1,101 @@
1
+ /**
2
+ * HMAC URL signing for proxy endpoints.
3
+ *
4
+ * ## Why
5
+ *
6
+ * Proxy endpoints like `/_scripts/proxy/google-static-maps` inject a server-side
7
+ * API key and forward requests to third-party services. Without signing, anyone
8
+ * can call these endpoints with arbitrary parameters and burn the site owner's
9
+ * API quota. Signing ensures only URLs generated server-side (during SSR/prerender
10
+ * or via the `/_scripts/sign` endpoint) are accepted.
11
+ *
12
+ * ## How
13
+ *
14
+ * 1. The module stores a deterministic secret in `runtimeConfig.nuxt-scripts.proxySecret`
15
+ * (env: `NUXT_SCRIPTS_PROXY_SECRET`).
16
+ * 2. URLs are canonicalized (sort query keys, strip `sig`) and signed with HMAC-SHA256.
17
+ * 3. The first 16 hex chars (64 bits) of the digest is appended as `?sig=<hex>`.
18
+ * 4. Endpoints wrapped with `withSigning()` verify the sig against the current request.
19
+ *
20
+ * A 64-bit signature is enough to defeat brute force for this threat model
21
+ * (a billion guesses gives a ~5% hit rate at 2^64). Longer signatures bloat
22
+ * prerendered HTML for no practical gain.
23
+ */
24
+ import type { H3Event } from 'h3';
25
+ import { PAGE_TOKEN_MAX_AGE, PAGE_TOKEN_PARAM, PAGE_TOKEN_TS_PARAM, SIG_LENGTH, SIG_PARAM } from './sign-constants.js';
26
+ export { PAGE_TOKEN_MAX_AGE, PAGE_TOKEN_PARAM, PAGE_TOKEN_TS_PARAM, SIG_LENGTH, SIG_PARAM };
27
+ /**
28
+ * Canonicalize a query object into a deterministic string suitable for HMAC input.
29
+ *
30
+ * Rules:
31
+ * - The `sig` param is stripped (it can't sign itself).
32
+ * - `undefined` and `null` values are skipped (mirrors `ufo.withQuery`).
33
+ * - Keys are sorted alphabetically so order-independent reconstruction works.
34
+ * - Arrays expand to repeated keys (e.g. `markers=a&markers=b`), matching how
35
+ * `ufo.withQuery` serializes array-valued params.
36
+ * - Objects are JSON-stringified (rare, but consistent with `ufo.withQuery`).
37
+ * - Encoding uses `encodeURIComponent` for both keys and values so the canonical
38
+ * form matches what shows up on the wire.
39
+ *
40
+ * The resulting string is stable across server/client and different JS runtimes
41
+ * because it does not depend on `URLSearchParams` insertion order.
42
+ */
43
+ export declare function canonicalizeQuery(query: Record<string, unknown>): string;
44
+ /**
45
+ * Sign a path + query using HMAC-SHA256 and return the 16-char hex digest.
46
+ *
47
+ * The HMAC input is `${path}?${canonicalQuery}` so that the same query signed
48
+ * against a different endpoint yields a different signature (prevents cross-
49
+ * endpoint signature reuse).
50
+ *
51
+ * `path` should be the URL path without query string (e.g. `/_scripts/proxy/google-static-maps`).
52
+ * Callers should not include origin / host since the signing contract is path-relative.
53
+ */
54
+ export declare function signProxyUrl(path: string, query: Record<string, unknown>, secret: string): string;
55
+ /**
56
+ * Build a fully-formed signed URL (path + query + sig).
57
+ *
58
+ * This is the primary helper for code paths that need to emit a proxy URL
59
+ * (SSR components, server-side URL rewriters like instagram-embed).
60
+ */
61
+ export declare function buildSignedProxyUrl(path: string, query: Record<string, unknown>, secret: string): string;
62
+ /**
63
+ * Generate a page token that authorizes client-side proxy requests.
64
+ *
65
+ * Embedded in the SSR payload so the browser can attach it to reactive proxy
66
+ * URL updates without needing a `/sign` round-trip. The token is scoped to
67
+ * a timestamp and expires after `PAGE_TOKEN_MAX_AGE` seconds.
68
+ *
69
+ * Construction: first 16 hex chars of `HMAC(secret, "proxy-access:<timestamp>")`.
70
+ */
71
+ export declare function generateProxyToken(secret: string, timestamp: number): string;
72
+ /**
73
+ * Verify a page token against the current time.
74
+ *
75
+ * Returns `true` if the token matches the HMAC for the given timestamp AND
76
+ * the timestamp is within `maxAge` seconds of `now`.
77
+ */
78
+ export declare function verifyProxyToken(token: string, timestamp: number, secret: string, maxAge?: number, now?: number): boolean;
79
+ /**
80
+ * Verify a request against either a URL signature or a page token.
81
+ *
82
+ * Two verification modes, checked in order:
83
+ *
84
+ * 1. **URL signature** (`sig` param): the exact URL was signed server-side
85
+ * during SSR/prerender. Locked to the specific path + query params.
86
+ *
87
+ * 2. **Page token** (`_pt` + `_ts` params): the client received a short-lived
88
+ * token during SSR and is making a reactive proxy request with new params.
89
+ * Valid for any params on the target path, but expires after `maxAge`.
90
+ *
91
+ * Returns `false` if neither mode validates.
92
+ */
93
+ export declare function verifyProxyRequest(event: H3Event, secret: string, maxAge?: number): boolean;
94
+ /**
95
+ * Constant-time string comparison.
96
+ *
97
+ * Both inputs are expected to be equal-length hex strings. The loop runs over
98
+ * the longer length so an early-exit on length mismatch doesn't leak the
99
+ * expected length (though both are fixed at `SIG_LENGTH` in practice).
100
+ */
101
+ export declare function constantTimeEqual(a: string, b: string): boolean;
@@ -0,0 +1,91 @@
1
+ import { createHmac } from "node:crypto";
2
+ import { getQuery } from "h3";
3
+ import {
4
+ PAGE_TOKEN_MAX_AGE,
5
+ PAGE_TOKEN_PARAM,
6
+ PAGE_TOKEN_TS_PARAM,
7
+ SIG_LENGTH,
8
+ SIG_PARAM
9
+ } from "./sign-constants.js";
10
+ export { PAGE_TOKEN_MAX_AGE, PAGE_TOKEN_PARAM, PAGE_TOKEN_TS_PARAM, SIG_LENGTH, SIG_PARAM };
11
+ export function canonicalizeQuery(query) {
12
+ const keys = Object.keys(query).filter((k) => k !== SIG_PARAM && query[k] !== void 0 && query[k] !== null).sort();
13
+ const parts = [];
14
+ for (const key of keys) {
15
+ const value = query[key];
16
+ const encodedKey = encodeURIComponent(key);
17
+ if (Array.isArray(value)) {
18
+ for (const item of value) {
19
+ if (item === void 0 || item === null)
20
+ continue;
21
+ parts.push(`${encodedKey}=${encodeURIComponent(serializeValue(item))}`);
22
+ }
23
+ } else {
24
+ parts.push(`${encodedKey}=${encodeURIComponent(serializeValue(value))}`);
25
+ }
26
+ }
27
+ return parts.join("&");
28
+ }
29
+ function serializeValue(value) {
30
+ if (typeof value === "string")
31
+ return value;
32
+ if (typeof value === "object")
33
+ return JSON.stringify(value);
34
+ return String(value);
35
+ }
36
+ export function signProxyUrl(path, query, secret) {
37
+ const canonical = canonicalizeQuery(query);
38
+ const input = canonical ? `${path}?${canonical}` : path;
39
+ return createHmac("sha256", secret).update(input).digest("hex").slice(0, SIG_LENGTH);
40
+ }
41
+ export function buildSignedProxyUrl(path, query, secret) {
42
+ const sig = signProxyUrl(path, query, secret);
43
+ const canonical = canonicalizeQuery(query);
44
+ const queryString = canonical ? `${canonical}&${SIG_PARAM}=${sig}` : `${SIG_PARAM}=${sig}`;
45
+ return `${path}?${queryString}`;
46
+ }
47
+ export function generateProxyToken(secret, timestamp) {
48
+ return createHmac("sha256", secret).update(`proxy-access:${timestamp}`).digest("hex").slice(0, SIG_LENGTH);
49
+ }
50
+ export function verifyProxyToken(token, timestamp, secret, maxAge = PAGE_TOKEN_MAX_AGE, now = Math.floor(Date.now() / 1e3)) {
51
+ if (!token || !secret || typeof timestamp !== "number")
52
+ return false;
53
+ if (token.length !== SIG_LENGTH)
54
+ return false;
55
+ const age = now - timestamp;
56
+ if (age > maxAge || age < -60)
57
+ return false;
58
+ const expected = generateProxyToken(secret, timestamp);
59
+ return constantTimeEqual(expected, token);
60
+ }
61
+ export function verifyProxyRequest(event, secret, maxAge) {
62
+ if (!secret)
63
+ return false;
64
+ const query = getQuery(event);
65
+ const rawSig = query[SIG_PARAM];
66
+ const sig = Array.isArray(rawSig) ? rawSig[0] : rawSig;
67
+ if (typeof sig === "string" && sig.length === SIG_LENGTH) {
68
+ const path = (event.path || "").split("?")[0] || "";
69
+ const expected = signProxyUrl(path, query, secret);
70
+ if (constantTimeEqual(expected, sig))
71
+ return true;
72
+ }
73
+ const rawToken = query[PAGE_TOKEN_PARAM];
74
+ const rawTs = query[PAGE_TOKEN_TS_PARAM];
75
+ const token = Array.isArray(rawToken) ? rawToken[0] : rawToken;
76
+ const ts = Array.isArray(rawTs) ? rawTs[0] : rawTs;
77
+ if (typeof token === "string" && ts !== void 0) {
78
+ const timestamp = Number(ts);
79
+ if (!Number.isNaN(timestamp))
80
+ return verifyProxyToken(token, timestamp, secret, maxAge);
81
+ }
82
+ return false;
83
+ }
84
+ export function constantTimeEqual(a, b) {
85
+ if (a.length !== b.length)
86
+ return false;
87
+ let diff = 0;
88
+ for (let i = 0; i < a.length; i++)
89
+ diff |= a.charCodeAt(i) ^ b.charCodeAt(i);
90
+ return diff === 0;
91
+ }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Middleware wrapper that enforces HMAC signature verification on a proxy handler.
3
+ *
4
+ * Usage:
5
+ * ```ts
6
+ * export default withSigning(defineEventHandler(async (event) => {
7
+ * // ... handler logic
8
+ * }))
9
+ * ```
10
+ *
11
+ * Behavior:
12
+ * - Reads `runtimeConfig.nuxt-scripts.proxySecret` (server-only).
13
+ * - If no secret is configured: passes through (signing not yet enabled).
14
+ * This allows shipping handler wiring before components emit signed URLs.
15
+ * Once `NUXT_SCRIPTS_PROXY_SECRET` is set, verification is enforced.
16
+ * - If a secret IS configured and the request's signature is invalid: 403.
17
+ * - Otherwise, delegates to the wrapped handler.
18
+ *
19
+ * The outer wrapper runs before any handler logic, so unauthorized requests
20
+ * never reach the upstream fetch and cannot consume API quota.
21
+ */
22
+ import type { EventHandler, EventHandlerRequest, EventHandlerResponse } from 'h3';
23
+ export declare function withSigning<Req extends EventHandlerRequest = EventHandlerRequest, Res extends EventHandlerResponse = EventHandlerResponse>(handler: EventHandler<Req, Res>): EventHandler<Req, Res>;
@@ -0,0 +1,19 @@
1
+ import { createError, defineEventHandler } from "h3";
2
+ import { useRuntimeConfig } from "nitropack/runtime";
3
+ import { verifyProxyRequest } from "./sign.js";
4
+ export function withSigning(handler) {
5
+ return defineEventHandler(async (event) => {
6
+ const runtimeConfig = useRuntimeConfig(event);
7
+ const scriptsConfig = runtimeConfig["nuxt-scripts"];
8
+ const secret = scriptsConfig?.proxySecret;
9
+ if (!secret)
10
+ return handler(event);
11
+ if (!verifyProxyRequest(event, secret, scriptsConfig?.pageTokenMaxAge)) {
12
+ throw createError({
13
+ statusCode: 403,
14
+ statusMessage: "Invalid signature"
15
+ });
16
+ }
17
+ return handler(event);
18
+ });
19
+ }
@@ -1,2 +1,2 @@
1
- declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<any>>;
1
+ declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<Buffer<ArrayBufferLike>>>;
2
2
  export default _default;
@@ -1,7 +1,20 @@
1
1
  import { createError, defineEventHandler, getQuery, setHeader } from "h3";
2
- import { $fetch } from "ofetch";
2
+ import { useRuntimeConfig } from "nitropack/runtime";
3
+ import { createCachedJsonFetch } from "./utils/cached-upstream.js";
4
+ import { rewriteTweetImages } from "./utils/embed-rewriters.js";
5
+ import { withSigning } from "./utils/withSigning.js";
3
6
  const TWEET_ID_RE = /^\d+$/;
4
- export default defineEventHandler(async (event) => {
7
+ const EMBED_X_SUFFIX_RE = /\/embed\/x$/;
8
+ const TWEET_ID_FROM_URL_RE = /[?&]id=(\d+)/;
9
+ const cachedTweetFetch = createCachedJsonFetch(
10
+ "nuxt-scripts-x-tweet",
11
+ 600,
12
+ (url) => {
13
+ const match = url.match(TWEET_ID_FROM_URL_RE);
14
+ return match?.[1] || url;
15
+ }
16
+ );
17
+ export default withSigning(defineEventHandler(async (event) => {
5
18
  const query = getQuery(event);
6
19
  const tweetId = query.id;
7
20
  if (!tweetId || !TWEET_ID_RE.test(tweetId)) {
@@ -12,7 +25,7 @@ export default defineEventHandler(async (event) => {
12
25
  }
13
26
  const randomToken = Array.from(Array.from({ length: 11 }), () => (Math.random() * 36).toString(36)[2]).join("");
14
27
  const params = new URLSearchParams({ id: tweetId, token: randomToken });
15
- const tweetData = await $fetch(
28
+ const tweetRaw = await cachedTweetFetch(
16
29
  `https://cdn.syndication.twimg.com/tweet-result?${params.toString()}`,
17
30
  {
18
31
  headers: {
@@ -26,7 +39,13 @@ export default defineEventHandler(async (event) => {
26
39
  statusMessage: error.statusMessage || "Failed to fetch tweet"
27
40
  });
28
41
  });
42
+ const tweetData = structuredClone(tweetRaw);
43
+ const handlerPath = event.path?.split("?")[0] || "";
44
+ const prefix = handlerPath.replace(EMBED_X_SUFFIX_RE, "") || "/_scripts";
45
+ const imagePath = `${prefix}/embed/x-image`;
46
+ const secret = useRuntimeConfig(event)["nuxt-scripts"]?.proxySecret;
47
+ rewriteTweetImages(tweetData, imagePath, secret);
29
48
  setHeader(event, "Content-Type", "application/json");
30
49
  setHeader(event, "Cache-Control", "public, max-age=600, s-maxage=600");
31
50
  return tweetData;
32
- });
51
+ }));
@@ -41,13 +41,38 @@ import type { XEmbedInput } from './registry/x-embed.js';
41
41
  import type { XPixelInput } from './registry/x-pixel.js';
42
42
  import type { YouTubePlayerInput } from './registry/youtube-player.js';
43
43
  import type { ProxyPrivacyInput } from './server/utils/privacy.js';
44
+ export type { Cluster, ClusterStats, MarkerClustererContext, MarkerClustererInstance, MarkerClustererOptions } from './components/GoogleMaps/types.js';
45
+ export { MARKER_CLUSTERER_INJECTION_KEY } from './components/GoogleMaps/types.js';
44
46
  export type WarmupStrategy = false | 'preload' | 'preconnect' | 'dns-prefetch';
45
- export type UseScriptContext<T extends Record<symbol | string, any>> = VueScriptInstance<T> & {
47
+ /**
48
+ * GCMv2 consent category value.
49
+ * @see https://developers.google.com/tag-platform/security/guides/consent
50
+ */
51
+ export type ConsentCategoryValue = 'granted' | 'denied';
52
+ /**
53
+ * Canonical GCMv2 consent state shape used by vendors that natively consume
54
+ * Consent Mode v2 (Google Analytics, Google Tag Manager, Bing UET).
55
+ */
56
+ export interface ConsentState {
57
+ ad_storage?: ConsentCategoryValue;
58
+ ad_user_data?: ConsentCategoryValue;
59
+ ad_personalization?: ConsentCategoryValue;
60
+ analytics_storage?: ConsentCategoryValue;
61
+ functionality_storage?: ConsentCategoryValue;
62
+ personalization_storage?: ConsentCategoryValue;
63
+ security_storage?: ConsentCategoryValue;
64
+ }
65
+ export type UseScriptContext<T extends Record<symbol | string, any>, C = unknown> = VueScriptInstance<T> & {
46
66
  /**
47
67
  * Remove and reload the script. Useful for scripts that need to re-execute
48
68
  * after SPA navigation (e.g., DOM-scanning scripts like iubenda).
49
69
  */
50
70
  reload: () => Promise<T>;
71
+ /**
72
+ * Vendor-native consent controls attached by registry scripts.
73
+ * Shape depends on the vendor (GCMv2 update, binary grant/revoke, three-state, etc.).
74
+ */
75
+ consent?: C;
51
76
  };
52
77
  export type NuxtUseScriptOptions<T extends Record<symbol | string, any> = {}> = Omit<UseScriptOptions<T>, 'trigger'> & {
53
78
  /**
@@ -233,7 +258,7 @@ export type BuiltInRegistryScriptKey = 'bingUet' | 'blueskyEmbed' | 'carbonAds'
233
258
  * Includes both built-in and augmented keys.
234
259
  */
235
260
  export type RegistryScriptKey = Exclude<keyof ScriptRegistry, `${string}-npm`>;
236
- type RegistryConfigInput<T> = [T] extends [true] ? Record<string, never> : T;
261
+ type RegistryConfigInput<T> = 0 extends 1 & T ? Record<string, any> : [T] extends [true] ? Record<string, never> : T;
237
262
  export type NuxtConfigScriptRegistryEntry<T> = true | false | 'mock' | (RegistryConfigInput<T> & {
238
263
  trigger?: NuxtUseScriptOptionsSerializable['trigger'] | false;
239
264
  proxy?: boolean;
@@ -241,9 +266,12 @@ export type NuxtConfigScriptRegistryEntry<T> = true | false | 'mock' | (Registry
241
266
  partytown?: boolean;
242
267
  privacy?: ProxyPrivacyInput;
243
268
  });
244
- export type NuxtConfigScriptRegistry<T extends keyof ScriptRegistry = keyof ScriptRegistry> = Partial<{
245
- [key in T]: NuxtConfigScriptRegistryEntry<ScriptRegistry[key]>;
246
- }> & Record<string & {}, NuxtConfigScriptRegistryEntry<any>>;
269
+ type _NuxtConfigScriptRegistryEntries = {
270
+ [K in keyof ScriptRegistry as K extends `${string}-npm` ? never : K]?: NuxtConfigScriptRegistryEntry<ScriptRegistry[K]>;
271
+ };
272
+ export interface NuxtConfigScriptRegistry extends _NuxtConfigScriptRegistryEntries {
273
+ [key: string]: any;
274
+ }
247
275
  export type UseFunctionType<T, U> = T extends {
248
276
  use: infer V;
249
277
  } ? V extends (...args: any) => any ? ReturnType<V> : U : U;
@@ -263,6 +291,14 @@ export interface RegistryScriptServerHandler {
263
291
  route: string;
264
292
  handler: string;
265
293
  middleware?: boolean;
294
+ /**
295
+ * Whether this handler verifies HMAC signatures via `withSigning()`.
296
+ *
297
+ * When any enabled script registers a handler with `requiresSigning: true`,
298
+ * the module enforces that `NUXT_SCRIPTS_PROXY_SECRET` is set in production,
299
+ * and the `/_scripts/sign` endpoint will accept this route as a signable path.
300
+ */
301
+ requiresSigning?: boolean;
266
302
  }
267
303
  /**
268
304
  * Declares what optimization modes a script supports and what's active by default.
@@ -453,4 +489,3 @@ export interface ProxyConfig {
453
489
  /** AST-level SDK patches applied during URL rewriting. */
454
490
  sdkPatches?: SdkPatch[];
455
491
  }
456
- export {};
@@ -0,0 +1 @@
1
+ export { MARKER_CLUSTERER_INJECTION_KEY } from "./components/GoogleMaps/types.js";