@nuxt/scripts 1.3.5 → 2.0.0-beta.1
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.
- package/THIRD_PARTY_LICENSES.md +124 -0
- package/dist/devtools-client/200.html +1 -1
- package/dist/devtools-client/404.html +1 -1
- package/dist/devtools-client/_nuxt/{B551Pnuh.js → 2p1_IuX2.js} +1 -1
- package/dist/devtools-client/_nuxt/{KxC4Q3ev.js → 9elIQxwW.js} +1 -1
- package/dist/devtools-client/_nuxt/{DTP8MRtW.js → BNPn63Jz.js} +1 -1
- package/dist/devtools-client/_nuxt/BjRLaDIG.js +152 -0
- package/dist/devtools-client/_nuxt/BtNUYBXT.js +8 -0
- package/dist/devtools-client/_nuxt/CDDBpkXG.js +3 -0
- package/dist/devtools-client/_nuxt/Cpb0XAYP.js +1 -0
- package/dist/devtools-client/_nuxt/DAsiyhPm.js +6 -0
- package/dist/devtools-client/_nuxt/{VICyrUSM.js → DabLKy7M.js} +1 -1
- package/dist/devtools-client/_nuxt/Dat651Oh.js +1 -0
- package/dist/devtools-client/_nuxt/DcG2ZXvY.js +1 -0
- package/dist/devtools-client/_nuxt/DmlGWZ2X.js +1 -0
- package/dist/devtools-client/_nuxt/KsX6Az5B.js +1 -0
- package/dist/devtools-client/_nuxt/RL1np866.js +28 -0
- package/dist/devtools-client/_nuxt/UmWNrBO5.js +2 -0
- package/dist/devtools-client/_nuxt/builds/latest.json +1 -1
- package/dist/devtools-client/_nuxt/builds/meta/e28b96f1-78e8-41a4-ad6f-904cd078f85e.json +1 -0
- package/dist/devtools-client/_nuxt/{entry.CYUdJFoE.css → entry.9HJPJ6Hp.css} +1 -1
- package/dist/devtools-client/_nuxt/error-404.DnwL1N6v.css +1 -0
- package/dist/devtools-client/_nuxt/error-500.DEbXRLdm.css +1 -0
- package/dist/devtools-client/docs/index.html +1 -1
- package/dist/devtools-client/first-party/index.html +1 -1
- package/dist/devtools-client/index.html +1 -1
- package/dist/devtools-client/registry/index.html +1 -1
- package/dist/module.d.mts +2 -86
- package/dist/module.d.ts +2 -86
- package/dist/module.json +3 -3
- package/dist/module.mjs +71 -328
- package/dist/registry.mjs +54 -22
- package/dist/runtime/components/GoogleMaps/ScriptGoogleMaps.d.vue.ts +1 -28
- package/dist/runtime/components/GoogleMaps/ScriptGoogleMaps.vue +7 -50
- package/dist/runtime/components/GoogleMaps/ScriptGoogleMaps.vue.d.ts +1 -28
- package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsOverlayView.d.vue.ts +0 -8
- package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsOverlayView.vue +1 -13
- package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsOverlayView.vue.d.ts +0 -8
- package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsStaticMap.d.vue.ts +4 -4
- package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsStaticMap.vue +9 -15
- package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsStaticMap.vue.d.ts +4 -4
- package/dist/runtime/components/GoogleMaps/useGoogleMapsResource.d.ts +0 -22
- package/dist/runtime/components/GoogleMaps/useGoogleMapsResource.js +0 -31
- package/dist/runtime/components/Leaflet/ScriptLeafletGeoJson.d.vue.ts +31 -0
- package/dist/runtime/components/Leaflet/ScriptLeafletGeoJson.vue +43 -0
- package/dist/runtime/components/Leaflet/ScriptLeafletGeoJson.vue.d.ts +31 -0
- package/dist/runtime/components/Leaflet/ScriptLeafletMap.d.vue.ts +90 -0
- package/dist/runtime/components/Leaflet/ScriptLeafletMap.vue +171 -0
- package/dist/runtime/components/Leaflet/ScriptLeafletMap.vue.d.ts +90 -0
- package/dist/runtime/components/Leaflet/ScriptLeafletMarker.d.vue.ts +47 -0
- package/dist/runtime/components/Leaflet/ScriptLeafletMarker.vue +70 -0
- package/dist/runtime/components/Leaflet/ScriptLeafletMarker.vue.d.ts +47 -0
- package/dist/runtime/components/Leaflet/ScriptLeafletPopup.d.vue.ts +31 -0
- package/dist/runtime/components/Leaflet/ScriptLeafletPopup.vue +81 -0
- package/dist/runtime/components/Leaflet/ScriptLeafletPopup.vue.d.ts +31 -0
- package/dist/runtime/components/Leaflet/ScriptLeafletTileLayer.d.vue.ts +26 -0
- package/dist/runtime/components/Leaflet/ScriptLeafletTileLayer.vue +38 -0
- package/dist/runtime/components/Leaflet/ScriptLeafletTileLayer.vue.d.ts +26 -0
- package/dist/runtime/components/Leaflet/useLeafletResource.d.ts +30 -0
- package/dist/runtime/components/Leaflet/useLeafletResource.js +46 -0
- package/dist/runtime/components/MapLibre/ScriptMapLibreGeoJson.d.vue.ts +39 -0
- package/dist/runtime/components/MapLibre/ScriptMapLibreGeoJson.vue +84 -0
- package/dist/runtime/components/MapLibre/ScriptMapLibreGeoJson.vue.d.ts +39 -0
- package/dist/runtime/components/MapLibre/ScriptMapLibreMap.d.vue.ts +122 -0
- package/dist/runtime/components/MapLibre/ScriptMapLibreMap.vue +214 -0
- package/dist/runtime/components/MapLibre/ScriptMapLibreMap.vue.d.ts +122 -0
- package/dist/runtime/components/MapLibre/ScriptMapLibreMarker.d.vue.ts +35 -0
- package/dist/runtime/components/MapLibre/ScriptMapLibreMarker.vue +98 -0
- package/dist/runtime/components/MapLibre/ScriptMapLibreMarker.vue.d.ts +35 -0
- package/dist/runtime/components/MapLibre/ScriptMapLibreNavigationControl.d.vue.ts +12 -0
- package/dist/runtime/components/MapLibre/ScriptMapLibreNavigationControl.vue +23 -0
- package/dist/runtime/components/MapLibre/ScriptMapLibreNavigationControl.vue.d.ts +12 -0
- package/dist/runtime/components/MapLibre/ScriptMapLibrePopup.d.vue.ts +31 -0
- package/dist/runtime/components/MapLibre/ScriptMapLibrePopup.vue +99 -0
- package/dist/runtime/components/MapLibre/ScriptMapLibrePopup.vue.d.ts +31 -0
- package/dist/runtime/components/MapLibre/useMapLibreResource.d.ts +23 -0
- package/dist/runtime/components/MapLibre/useMapLibreResource.js +41 -0
- package/dist/runtime/components/ScriptCrisp.vue +20 -18
- package/dist/runtime/components/ScriptDeskCrew.d.vue.ts +40 -0
- package/dist/runtime/components/ScriptDeskCrew.vue +82 -0
- package/dist/runtime/components/ScriptDeskCrew.vue.d.ts +40 -0
- package/dist/runtime/components/ScriptIntercom.vue +21 -19
- package/dist/runtime/components/ScriptLoadingIndicator.d.vue.ts +1 -1
- package/dist/runtime/components/ScriptLoadingIndicator.vue.d.ts +1 -1
- package/dist/runtime/components/ScriptPayPalButtons.vue +7 -3
- package/dist/runtime/components/ScriptPayPalMessages.vue +8 -4
- package/dist/runtime/composables/useScript.js +96 -49
- package/dist/runtime/composables/useScriptProxyUrl.d.ts +4 -8
- package/dist/runtime/composables/useScriptProxyUrl.js +0 -7
- package/dist/runtime/composables/useScriptTriggerIdleTimeout.d.ts +2 -1
- package/dist/runtime/composables/useScriptTriggerIdleTimeout.js +3 -34
- package/dist/runtime/composables/useScriptTriggerInteraction.d.ts +2 -1
- package/dist/runtime/composables/useScriptTriggerInteraction.js +13 -46
- package/dist/runtime/composables/useScriptTriggerServiceWorker.d.ts +2 -1
- package/dist/runtime/composables/useScriptTriggerServiceWorker.js +5 -38
- package/dist/runtime/leaflet-styles.d.ts +13 -0
- package/dist/runtime/leaflet-styles.js +684 -0
- package/dist/runtime/maplibre-styles.d.ts +7 -0
- package/dist/runtime/maplibre-styles.js +25 -0
- package/dist/runtime/npm-script-api-state.d.ts +4 -0
- package/dist/runtime/npm-script-api-state.js +13 -0
- package/dist/runtime/registry/crisp.d.ts +1 -1
- package/dist/runtime/registry/crisp.js +30 -18
- package/dist/runtime/registry/deskcrew.d.ts +52 -0
- package/dist/runtime/registry/deskcrew.js +31 -0
- package/dist/runtime/registry/google-analytics.d.ts +0 -2
- package/dist/runtime/registry/google-maps.d.ts +2 -11
- package/dist/runtime/registry/google-maps.js +31 -10
- package/dist/runtime/registry/google-tag-manager.d.ts +9 -3
- package/dist/runtime/registry/leaflet.d.ts +13 -0
- package/dist/runtime/registry/leaflet.js +29 -0
- package/dist/runtime/registry/maplibre.d.ts +13 -0
- package/dist/runtime/registry/maplibre.js +29 -0
- package/dist/runtime/registry/matomo-analytics.js +1 -8
- package/dist/runtime/registry/plausible-analytics.d.ts +4 -16
- package/dist/runtime/registry/plausible-analytics.js +15 -58
- package/dist/runtime/registry/rybbit-analytics.d.ts +0 -4
- package/dist/runtime/registry/rybbit-analytics.js +1 -4
- package/dist/runtime/registry/schemas.d.ts +61 -5
- package/dist/runtime/registry/schemas.js +64 -5
- package/dist/runtime/registry/tiktok-pixel.d.ts +2 -12
- package/dist/runtime/registry/tiktok-pixel.js +1 -1
- package/dist/runtime/registry/usercentrics.js +46 -6
- package/dist/runtime/registry/youtube-player.d.ts +3 -4
- package/dist/runtime/registry/youtube-player.js +30 -23
- package/dist/runtime/server/bluesky-embed-image.d.ts +1 -1
- package/dist/runtime/server/bluesky-embed.d.ts +1 -1
- package/dist/runtime/server/bluesky-embed.js +3 -6
- package/dist/runtime/server/gravatar-proxy.d.ts +1 -1
- package/dist/runtime/server/gravatar-proxy.js +2 -3
- package/dist/runtime/server/instagram-embed-asset.d.ts +1 -1
- package/dist/runtime/server/instagram-embed-image.d.ts +1 -1
- package/dist/runtime/server/instagram-embed.d.ts +1 -1
- package/dist/runtime/server/instagram-embed.js +4 -7
- package/dist/runtime/server/utils/cached-upstream.d.ts +6 -11
- package/dist/runtime/server/utils/cached-upstream.js +94 -23
- package/dist/runtime/server/utils/embed-rewriters.d.ts +3 -5
- package/dist/runtime/server/utils/embed-rewriters.js +8 -8
- package/dist/runtime/server/utils/image-proxy.d.ts +1 -1
- package/dist/runtime/server/utils/image-proxy.js +2 -3
- package/dist/runtime/server/utils/instagram-embed.d.ts +6 -6
- package/dist/runtime/server/utils/instagram-embed.js +18 -19
- package/dist/runtime/server/utils/proxy-url.d.ts +5 -6
- package/dist/runtime/server/utils/proxy-url.js +1 -4
- package/dist/runtime/server/x-embed-image.d.ts +1 -1
- package/dist/runtime/server/x-embed.d.ts +48 -1
- package/dist/runtime/server/x-embed.js +3 -6
- package/dist/runtime/types.d.ts +13 -13
- package/dist/runtime/utils/abortable-promise.js +26 -5
- package/dist/runtime/utils/nuxt-ready-script-trigger.d.ts +3 -0
- package/dist/runtime/utils/nuxt-ready-script-trigger.js +16 -0
- package/dist/runtime/utils.js +21 -32
- package/dist/stats.mjs +13 -0
- package/dist/types-source.mjs +1173 -100
- package/dist/types.d.mts +2 -2
- package/package.json +34 -21
- package/LICENSE.md +0 -9
- package/bin/cli.mjs +0 -2
- package/dist/cli.d.mts +0 -2
- package/dist/cli.d.ts +0 -2
- package/dist/cli.mjs +0 -50
- package/dist/devtools-client/_nuxt/8ltZ0o6d.js +0 -1
- package/dist/devtools-client/_nuxt/B8_EBdhg.js +0 -152
- package/dist/devtools-client/_nuxt/BKdLzFmX.js +0 -3
- package/dist/devtools-client/_nuxt/Bf2Kt1P0.js +0 -28
- package/dist/devtools-client/_nuxt/CC0GKROJ.js +0 -1
- package/dist/devtools-client/_nuxt/CH5oUfNi.js +0 -1
- package/dist/devtools-client/_nuxt/CMXzQxJZ.js +0 -1
- package/dist/devtools-client/_nuxt/CP7c-Js6.js +0 -8
- package/dist/devtools-client/_nuxt/C_v3_O0R.js +0 -6
- package/dist/devtools-client/_nuxt/CxN78VhB.js +0 -1
- package/dist/devtools-client/_nuxt/Dkc7iTdR.js +0 -1
- package/dist/devtools-client/_nuxt/builds/meta/173480db-0944-415b-962d-d619682f0042.json +0 -1
- package/dist/devtools-client/_nuxt/error-404.Bq2JQbJs.css +0 -1
- package/dist/devtools-client/_nuxt/error-500.5E_FOzlc.css +0 -1
- package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsAdvancedMarkerElement.d.vue.ts +0 -13
- package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsAdvancedMarkerElement.vue +0 -15
- package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsAdvancedMarkerElement.vue.d.ts +0 -13
- package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsHeatmapLayer.d.vue.ts +0 -27
- package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsHeatmapLayer.vue +0 -34
- package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsHeatmapLayer.vue.d.ts +0 -27
- package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsPinElement.d.vue.ts +0 -13
- package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsPinElement.vue +0 -11
- package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsPinElement.vue.d.ts +0 -13
- package/dist/runtime/components/GoogleMaps/bindGoogleMapsEvents.d.ts +0 -13
- package/dist/runtime/components/GoogleMaps/bindGoogleMapsEvents.js +0 -8
- package/dist/runtime/components/GoogleMaps/injectionKeys.d.ts +0 -10
- package/dist/runtime/components/GoogleMaps/injectionKeys.js +0 -2
- package/dist/runtime/composables/useScriptProxyToken.d.ts +0 -12
- package/dist/runtime/composables/useScriptProxyToken.js +0 -4
- package/dist/runtime/npm-script-stub.d.ts +0 -20
- package/dist/runtime/npm-script-stub.js +0 -72
- package/dist/runtime/plugins/proxy-token.server.d.ts +0 -10
- package/dist/runtime/plugins/proxy-token.server.js +0 -17
- package/dist/runtime/server/google-maps-geocode-proxy.d.ts +0 -2
- package/dist/runtime/server/google-maps-geocode-proxy.js +0 -43
- package/dist/runtime/server/google-static-maps-proxy.d.ts +0 -2
- package/dist/runtime/server/google-static-maps-proxy.js +0 -56
- package/dist/runtime/server/utils/proxy-query.d.ts +0 -2
- package/dist/runtime/server/utils/proxy-query.js +0 -10
- package/dist/runtime/server/utils/sign-constants.d.ts +0 -16
- package/dist/runtime/server/utils/sign-constants.js +0 -5
- package/dist/runtime/server/utils/sign.d.ts +0 -101
- package/dist/runtime/server/utils/sign.js +0 -91
- package/dist/runtime/server/utils/withSigning.d.ts +0 -23
- package/dist/runtime/server/utils/withSigning.js +0 -19
- package/dist/runtime/unhead-features.d.ts +0 -1
- package/dist/runtime/unhead-features.js +0 -3
|
@@ -6,6 +6,10 @@ import { NUXT_SCRIPTS_CACHE_BASE } from "./cache-config.js";
|
|
|
6
6
|
import { closePublicNetworkDispatcher, createPublicNetworkDispatcher, isPublicNetworkHostname } from "./network-host.js";
|
|
7
7
|
const DEFAULT_BINARY_MAX_RESPONSE_BYTES = 10 * 1024 * 1024;
|
|
8
8
|
const DEFAULT_JSON_MAX_RESPONSE_BYTES = 2 * 1024 * 1024;
|
|
9
|
+
const TIMEOUT_ERROR_NAMES = /* @__PURE__ */ new Set(["AbortError", "BodyTimeoutError", "HeadersTimeoutError", "TimeoutError"]);
|
|
10
|
+
const DEFAULT_UPSTREAM_TIMEOUT_MS = 1e4;
|
|
11
|
+
const MAX_TRACKED_FAILURES = 512;
|
|
12
|
+
export const UPSTREAM_FAILURE_MAX_AGE = 60;
|
|
9
13
|
export function isSafeHttpsUrl(url) {
|
|
10
14
|
return url.protocol === "https:" && !url.username && !url.password && (!url.port || url.port === "443") && isPublicNetworkHostname(url.hostname);
|
|
11
15
|
}
|
|
@@ -15,6 +19,51 @@ function upstreamError(message, statusCode, statusMessage, cause) {
|
|
|
15
19
|
statusMessage
|
|
16
20
|
});
|
|
17
21
|
}
|
|
22
|
+
function asUpstreamError(error) {
|
|
23
|
+
if (typeof error?.statusCode === "number")
|
|
24
|
+
return error;
|
|
25
|
+
const timedOut = isTimeoutError(error);
|
|
26
|
+
return upstreamError(
|
|
27
|
+
`Upstream request failed: ${error?.message || "unknown error"}`,
|
|
28
|
+
timedOut ? 504 : 502,
|
|
29
|
+
timedOut ? "Gateway Timeout" : "Upstream request failed",
|
|
30
|
+
error
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
function isTimeoutError(error) {
|
|
34
|
+
const candidate = error;
|
|
35
|
+
const code = candidate?.cause?.code;
|
|
36
|
+
return TIMEOUT_ERROR_NAMES.has(candidate?.name || "") || TIMEOUT_ERROR_NAMES.has(candidate?.cause?.name || "") || typeof code === "string" && code.includes("TIMEOUT");
|
|
37
|
+
}
|
|
38
|
+
function createFailureGate(maxAge) {
|
|
39
|
+
const failures = /* @__PURE__ */ new Map();
|
|
40
|
+
const failureWindow = Math.min(UPSTREAM_FAILURE_MAX_AGE, maxAge) * 1e3;
|
|
41
|
+
return {
|
|
42
|
+
replay(key) {
|
|
43
|
+
const failure = failures.get(key);
|
|
44
|
+
if (!failure)
|
|
45
|
+
return;
|
|
46
|
+
if (Date.now() >= failure.until) {
|
|
47
|
+
failures.delete(key);
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
throw upstreamError(failure.error.message, failure.error.statusCode, failure.error.statusMessage);
|
|
51
|
+
},
|
|
52
|
+
record(key, error) {
|
|
53
|
+
const failure = error;
|
|
54
|
+
if (failures.size >= MAX_TRACKED_FAILURES)
|
|
55
|
+
failures.delete(failures.keys().next().value);
|
|
56
|
+
failures.set(key, {
|
|
57
|
+
until: Date.now() + failureWindow,
|
|
58
|
+
error: {
|
|
59
|
+
message: failure?.message || "Upstream request failed",
|
|
60
|
+
statusCode: failure?.statusCode ?? 502,
|
|
61
|
+
statusMessage: failure?.statusMessage || "Upstream request failed"
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
}
|
|
18
67
|
function resolveMaxResponseBytes(value, fallback) {
|
|
19
68
|
const maxBytes = value ?? fallback;
|
|
20
69
|
if (!Number.isSafeInteger(maxBytes) || maxBytes < 0)
|
|
@@ -138,10 +187,11 @@ async function fetchBoundedUpstream(url, options) {
|
|
|
138
187
|
}
|
|
139
188
|
}
|
|
140
189
|
if (!options.ignoreResponseError && response.status >= 400 && response.status < 600) {
|
|
190
|
+
const upstreamFault = response.status >= 500;
|
|
141
191
|
await rejectResponse(response, upstreamError(
|
|
142
192
|
`Upstream request failed with status ${response.status}`,
|
|
143
|
-
response.status,
|
|
144
|
-
response.statusText || "Upstream request failed"
|
|
193
|
+
upstreamFault ? 502 : response.status,
|
|
194
|
+
upstreamFault ? "Upstream request failed" : response.statusText || "Upstream request failed"
|
|
145
195
|
));
|
|
146
196
|
}
|
|
147
197
|
const contentType = response.headers.get("content-type");
|
|
@@ -168,16 +218,35 @@ async function fetchBoundedUpstream(url, options) {
|
|
|
168
218
|
return { contentType, data, status: response.status };
|
|
169
219
|
}
|
|
170
220
|
} catch (error) {
|
|
171
|
-
primaryError = error;
|
|
172
|
-
throw
|
|
221
|
+
primaryError = asUpstreamError(error);
|
|
222
|
+
throw primaryError;
|
|
173
223
|
} finally {
|
|
174
224
|
await closePublicNetworkDispatcher(network, primaryError);
|
|
175
225
|
}
|
|
176
226
|
}
|
|
177
227
|
export function createCachedBinaryFetch(name, maxAge, config = {}) {
|
|
178
228
|
const maxResponseBytes = resolveMaxResponseBytes(config.maxResponseBytes, DEFAULT_BINARY_MAX_RESPONSE_BYTES);
|
|
229
|
+
const failureGate = createFailureGate(maxAge);
|
|
230
|
+
const cacheKey = (url, opts) => {
|
|
231
|
+
if (!opts)
|
|
232
|
+
return hash(url);
|
|
233
|
+
const parts = [url];
|
|
234
|
+
if (opts.headers) {
|
|
235
|
+
const entries = Object.entries(opts.headers).sort(([a], [b]) => a.localeCompare(b));
|
|
236
|
+
for (const [k, v] of entries)
|
|
237
|
+
parts.push(`${k}=${v}`);
|
|
238
|
+
}
|
|
239
|
+
if (opts.redirect)
|
|
240
|
+
parts.push(`redirect=${opts.redirect}`);
|
|
241
|
+
if (opts.ignoreResponseError !== void 0)
|
|
242
|
+
parts.push(`ignoreResponseError=${opts.ignoreResponseError}`);
|
|
243
|
+
return hash(parts);
|
|
244
|
+
};
|
|
245
|
+
const gateKey = (url, opts) => `${cacheKey(url, opts)}:${opts?.timeout ?? DEFAULT_UPSTREAM_TIMEOUT_MS}`;
|
|
179
246
|
const cached = defineCachedFunction(
|
|
180
247
|
async (url, opts) => {
|
|
248
|
+
const key = gateKey(url, opts);
|
|
249
|
+
failureGate.replay(key);
|
|
181
250
|
const response = await fetchBoundedUpstream(url, {
|
|
182
251
|
allowContentType: config.allowContentType,
|
|
183
252
|
allowUrl: config.allowUrl,
|
|
@@ -186,7 +255,10 @@ export function createCachedBinaryFetch(name, maxAge, config = {}) {
|
|
|
186
255
|
maxRedirects: config.maxRedirects,
|
|
187
256
|
maxResponseBytes,
|
|
188
257
|
redirect: opts?.redirect ?? (config.allowUrl ? "follow" : "manual"),
|
|
189
|
-
timeoutMs: opts?.timeout ??
|
|
258
|
+
timeoutMs: opts?.timeout ?? DEFAULT_UPSTREAM_TIMEOUT_MS
|
|
259
|
+
}).catch((error) => {
|
|
260
|
+
failureGate.record(key, error);
|
|
261
|
+
throw error;
|
|
190
262
|
});
|
|
191
263
|
return {
|
|
192
264
|
base64: response.data.byteLength ? Buffer.from(response.data).toString("base64") : "",
|
|
@@ -200,21 +272,7 @@ export function createCachedBinaryFetch(name, maxAge, config = {}) {
|
|
|
200
272
|
maxAge,
|
|
201
273
|
swr: true,
|
|
202
274
|
staleMaxAge: maxAge,
|
|
203
|
-
getKey:
|
|
204
|
-
if (!opts)
|
|
205
|
-
return hash(url);
|
|
206
|
-
const parts = [url];
|
|
207
|
-
if (opts.headers) {
|
|
208
|
-
const entries = Object.entries(opts.headers).sort(([a], [b]) => a.localeCompare(b));
|
|
209
|
-
for (const [k, v] of entries)
|
|
210
|
-
parts.push(`${k}=${v}`);
|
|
211
|
-
}
|
|
212
|
-
if (opts.redirect)
|
|
213
|
-
parts.push(`redirect=${opts.redirect}`);
|
|
214
|
-
if (opts.ignoreResponseError !== void 0)
|
|
215
|
-
parts.push(`ignoreResponseError=${opts.ignoreResponseError}`);
|
|
216
|
-
return hash(parts);
|
|
217
|
-
}
|
|
275
|
+
getKey: cacheKey
|
|
218
276
|
}
|
|
219
277
|
);
|
|
220
278
|
return async (url, opts) => {
|
|
@@ -227,8 +285,13 @@ export function createCachedBinaryFetch(name, maxAge, config = {}) {
|
|
|
227
285
|
}
|
|
228
286
|
export function createCachedJsonFetch(name, maxAge, getKey, config) {
|
|
229
287
|
const maxResponseBytes = resolveMaxResponseBytes(config.maxResponseBytes, DEFAULT_JSON_MAX_RESPONSE_BYTES);
|
|
288
|
+
const failureGate = createFailureGate(maxAge);
|
|
289
|
+
const cacheKey = (url, opts) => hash(getKey(url, opts));
|
|
290
|
+
const gateKey = (url, opts) => `${cacheKey(url, opts)}:${opts?.timeout ?? DEFAULT_UPSTREAM_TIMEOUT_MS}`;
|
|
230
291
|
return defineCachedFunction(
|
|
231
292
|
async (url, opts) => {
|
|
293
|
+
const key = gateKey(url, opts);
|
|
294
|
+
failureGate.replay(key);
|
|
232
295
|
const response = await fetchBoundedUpstream(url, {
|
|
233
296
|
allowUrl: config.allowUrl,
|
|
234
297
|
contentTypePrefixes: config.contentTypePrefixes,
|
|
@@ -237,7 +300,10 @@ export function createCachedJsonFetch(name, maxAge, getKey, config) {
|
|
|
237
300
|
maxRedirects: config.maxRedirects,
|
|
238
301
|
maxResponseBytes,
|
|
239
302
|
redirect: "follow",
|
|
240
|
-
timeoutMs: opts?.timeout ??
|
|
303
|
+
timeoutMs: opts?.timeout ?? DEFAULT_UPSTREAM_TIMEOUT_MS
|
|
304
|
+
}).catch((error) => {
|
|
305
|
+
failureGate.record(key, error);
|
|
306
|
+
throw error;
|
|
241
307
|
});
|
|
242
308
|
const text = new TextDecoder().decode(response.data);
|
|
243
309
|
let data;
|
|
@@ -250,7 +316,12 @@ export function createCachedJsonFetch(name, maxAge, getKey, config) {
|
|
|
250
316
|
throw upstreamError("Upstream response is not valid JSON", 502, "Invalid upstream response", cause);
|
|
251
317
|
}
|
|
252
318
|
}
|
|
253
|
-
|
|
319
|
+
try {
|
|
320
|
+
config.validateResponse?.(data);
|
|
321
|
+
} catch (error) {
|
|
322
|
+
failureGate.record(key, error);
|
|
323
|
+
throw error;
|
|
324
|
+
}
|
|
254
325
|
return data;
|
|
255
326
|
},
|
|
256
327
|
{
|
|
@@ -259,7 +330,7 @@ export function createCachedJsonFetch(name, maxAge, getKey, config) {
|
|
|
259
330
|
maxAge,
|
|
260
331
|
swr: true,
|
|
261
332
|
staleMaxAge: maxAge,
|
|
262
|
-
getKey:
|
|
333
|
+
getKey: cacheKey
|
|
263
334
|
}
|
|
264
335
|
);
|
|
265
336
|
}
|
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Mutate a tweet (and any quoted tweet) in place so every raw CDN image URL
|
|
3
|
-
* is rewritten to route through the site's `/embed/x-image` proxy.
|
|
4
|
-
* `secret` is provided, URLs are HMAC-signed and pass `withSigning` without a
|
|
5
|
-
* page token.
|
|
3
|
+
* is rewritten to route through the site's `/embed/x-image` proxy.
|
|
6
4
|
*
|
|
7
5
|
* Clone the input first if it came from a shared cache — this function does
|
|
8
6
|
* not copy.
|
|
9
7
|
*/
|
|
10
|
-
export declare function rewriteTweetImages(tweet: any, imagePath: string
|
|
8
|
+
export declare function rewriteTweetImages(tweet: any, imagePath: string): void;
|
|
11
9
|
/**
|
|
12
10
|
* Mutate a Bluesky post in place so every CDN image URL routes through the
|
|
13
11
|
* site's `/embed/bluesky-image` proxy. Covers author avatar, embedded images
|
|
@@ -16,4 +14,4 @@ export declare function rewriteTweetImages(tweet: any, imagePath: string, secret
|
|
|
16
14
|
* Clone the input first if it came from a shared cache — this function does
|
|
17
15
|
* not copy.
|
|
18
16
|
*/
|
|
19
|
-
export declare function rewriteBlueskyPostImages(post: any, imagePath: string
|
|
17
|
+
export declare function rewriteBlueskyPostImages(post: any, imagePath: string): void;
|
|
@@ -1,30 +1,30 @@
|
|
|
1
1
|
import { buildProxyUrl } from "./proxy-url.js";
|
|
2
|
-
export function rewriteTweetImages(tweet, imagePath
|
|
2
|
+
export function rewriteTweetImages(tweet, imagePath) {
|
|
3
3
|
if (!tweet)
|
|
4
4
|
return;
|
|
5
5
|
if (tweet.user?.profile_image_url_https)
|
|
6
|
-
tweet.user.profile_image_url_https = buildProxyUrl(imagePath, { url: tweet.user.profile_image_url_https }
|
|
6
|
+
tweet.user.profile_image_url_https = buildProxyUrl(imagePath, { url: tweet.user.profile_image_url_https });
|
|
7
7
|
if (tweet.photos) {
|
|
8
8
|
for (const photo of tweet.photos) {
|
|
9
9
|
if (photo.url)
|
|
10
|
-
photo.url = buildProxyUrl(imagePath, { url: photo.url }
|
|
10
|
+
photo.url = buildProxyUrl(imagePath, { url: photo.url });
|
|
11
11
|
}
|
|
12
12
|
}
|
|
13
13
|
if (tweet.entities?.media) {
|
|
14
14
|
for (const media of tweet.entities.media) {
|
|
15
15
|
if (media.media_url_https)
|
|
16
|
-
media.media_url_https = buildProxyUrl(imagePath, { url: media.media_url_https }
|
|
16
|
+
media.media_url_https = buildProxyUrl(imagePath, { url: media.media_url_https });
|
|
17
17
|
}
|
|
18
18
|
}
|
|
19
19
|
if (tweet.video?.poster)
|
|
20
|
-
tweet.video.poster = buildProxyUrl(imagePath, { url: tweet.video.poster }
|
|
20
|
+
tweet.video.poster = buildProxyUrl(imagePath, { url: tweet.video.poster });
|
|
21
21
|
if (tweet.quoted_tweet)
|
|
22
|
-
rewriteTweetImages(tweet.quoted_tweet, imagePath
|
|
22
|
+
rewriteTweetImages(tweet.quoted_tweet, imagePath);
|
|
23
23
|
}
|
|
24
|
-
export function rewriteBlueskyPostImages(post, imagePath
|
|
24
|
+
export function rewriteBlueskyPostImages(post, imagePath) {
|
|
25
25
|
if (!post)
|
|
26
26
|
return;
|
|
27
|
-
const proxy = (url) => url ? buildProxyUrl(imagePath, { url }
|
|
27
|
+
const proxy = (url) => url ? buildProxyUrl(imagePath, { url }) : url;
|
|
28
28
|
if (post.author?.avatar)
|
|
29
29
|
post.author.avatar = proxy(post.author.avatar);
|
|
30
30
|
const embed = post.embed;
|
|
@@ -12,4 +12,4 @@ export interface ImageProxyConfig {
|
|
|
12
12
|
/** Unique name for the nitro cache group (defaults to derived from allowedDomains). */
|
|
13
13
|
cacheName?: string;
|
|
14
14
|
}
|
|
15
|
-
export declare function createImageProxyHandler(config: ImageProxyConfig): import("h3").EventHandler<import("h3").EventHandlerRequest,
|
|
15
|
+
export declare function createImageProxyHandler(config: ImageProxyConfig): import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<Buffer<ArrayBufferLike>>>;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { createError, defineEventHandler, getQuery, setHeader } from "#nuxt-scripts/h3";
|
|
2
2
|
import { createCachedBinaryFetch } from "./cached-upstream.js";
|
|
3
3
|
import { isPublicNetworkHostname } from "./network-host.js";
|
|
4
|
-
import { withSigning } from "./withSigning.js";
|
|
5
4
|
const AMP_RE = /&/g;
|
|
6
5
|
export function createImageProxyHandler(config) {
|
|
7
6
|
const {
|
|
@@ -21,7 +20,7 @@ export function createImageProxyHandler(config) {
|
|
|
21
20
|
allowContentType: contentTypeAllowed,
|
|
22
21
|
allowUrl: urlAllowed
|
|
23
22
|
});
|
|
24
|
-
return
|
|
23
|
+
return defineEventHandler(async (event) => {
|
|
25
24
|
const query = getQuery(event);
|
|
26
25
|
let url = query.url;
|
|
27
26
|
if (decodeAmpersands && url)
|
|
@@ -79,5 +78,5 @@ export function createImageProxyHandler(config) {
|
|
|
79
78
|
setHeader(event, "Content-Security-Policy", "sandbox; default-src 'none'");
|
|
80
79
|
setHeader(event, "X-Content-Type-Options", "nosniff");
|
|
81
80
|
return result.body;
|
|
82
|
-
})
|
|
81
|
+
});
|
|
83
82
|
}
|
|
@@ -9,15 +9,15 @@ export declare const INSTAGRAM_ASSET_HOST = "static.cdninstagram.com";
|
|
|
9
9
|
export declare function isSafeInstagramPostUrl(url: URL): boolean;
|
|
10
10
|
export declare function isSafeInstagramEmbedUrl(url: URL): boolean;
|
|
11
11
|
export declare function isSafeInstagramCssUrl(url: URL): boolean;
|
|
12
|
-
export declare function sanitizeInstagramEmbedHtml(html: string, prefix: string
|
|
12
|
+
export declare function sanitizeInstagramEmbedHtml(html: string, prefix: string): {
|
|
13
13
|
bodyHtml: string;
|
|
14
14
|
cssUrls: string[];
|
|
15
15
|
};
|
|
16
|
-
export declare function sanitizeInstagramEmbedCss(css: string, scopeSelector: string, prefix: string
|
|
17
|
-
export declare function proxyImageUrl(url: string, prefix?: string
|
|
18
|
-
export declare function proxyAssetUrl(url: string, prefix?: string
|
|
19
|
-
export declare function rewriteUrl(url: string, prefix?: string
|
|
20
|
-
export declare function rewriteUrlsInText(text: string, prefix?: string
|
|
16
|
+
export declare function sanitizeInstagramEmbedCss(css: string, scopeSelector: string, prefix: string): string;
|
|
17
|
+
export declare function proxyImageUrl(url: string, prefix?: string): string;
|
|
18
|
+
export declare function proxyAssetUrl(url: string, prefix?: string): string;
|
|
19
|
+
export declare function rewriteUrl(url: string, prefix?: string): string;
|
|
20
|
+
export declare function rewriteUrlsInText(text: string, prefix?: string): string;
|
|
21
21
|
/**
|
|
22
22
|
* Scope CSS rules under a parent selector and strip global/page-level rules.
|
|
23
23
|
* Removes :root, html, body selectors and @charset/@import at-rules.
|
|
@@ -88,11 +88,11 @@ function sanitizeCssUrls(css, proxyPrefix) {
|
|
|
88
88
|
return value.startsWith(allowedPrefix) ? match : 'url("")';
|
|
89
89
|
});
|
|
90
90
|
}
|
|
91
|
-
function rewriteMediaUrl(url, prefix
|
|
92
|
-
const rewritten = rewriteUrl(url, prefix
|
|
91
|
+
function rewriteMediaUrl(url, prefix) {
|
|
92
|
+
const rewritten = rewriteUrl(url, prefix);
|
|
93
93
|
return rewritten === url ? void 0 : rewritten;
|
|
94
94
|
}
|
|
95
|
-
export function sanitizeInstagramEmbedHtml(html, prefix
|
|
95
|
+
export function sanitizeInstagramEmbedHtml(html, prefix) {
|
|
96
96
|
const ast = parse(html);
|
|
97
97
|
const cssUrls = /* @__PURE__ */ new Set();
|
|
98
98
|
walkSync(ast, (node) => {
|
|
@@ -122,7 +122,7 @@ export function sanitizeInstagramEmbedHtml(html, prefix, secret) {
|
|
|
122
122
|
continue;
|
|
123
123
|
}
|
|
124
124
|
if (lowerName === "src" || lowerName === "poster") {
|
|
125
|
-
const rewritten = rewriteMediaUrl(value, prefix
|
|
125
|
+
const rewritten = rewriteMediaUrl(value, prefix);
|
|
126
126
|
if (rewritten)
|
|
127
127
|
node.attributes[name] = rewritten;
|
|
128
128
|
else
|
|
@@ -132,7 +132,7 @@ export function sanitizeInstagramEmbedHtml(html, prefix, secret) {
|
|
|
132
132
|
if (lowerName === "srcset") {
|
|
133
133
|
const entries = value.split(",").flatMap((entry) => {
|
|
134
134
|
const [url, descriptor, ...rest] = entry.trim().split(SRCSET_SPLIT_RE);
|
|
135
|
-
const rewritten = url ? rewriteMediaUrl(url, prefix
|
|
135
|
+
const rewritten = url ? rewriteMediaUrl(url, prefix) : void 0;
|
|
136
136
|
const safeDescriptor = !descriptor || /^\d+(?:\.\d+)?[wx]$/.test(descriptor);
|
|
137
137
|
return rewritten && safeDescriptor && rest.length === 0 ? [`${rewritten}${descriptor ? ` ${descriptor}` : ""}`] : [];
|
|
138
138
|
});
|
|
@@ -143,7 +143,7 @@ export function sanitizeInstagramEmbedHtml(html, prefix, secret) {
|
|
|
143
143
|
continue;
|
|
144
144
|
}
|
|
145
145
|
if (lowerName === "style") {
|
|
146
|
-
const rewritten = rewriteUrlsInText(value, prefix
|
|
146
|
+
const rewritten = rewriteUrlsInText(value, prefix);
|
|
147
147
|
if (DANGEROUS_STYLE_RE.test(rewritten))
|
|
148
148
|
delete node.attributes[name];
|
|
149
149
|
else
|
|
@@ -163,33 +163,32 @@ export function sanitizeInstagramEmbedHtml(html, prefix, secret) {
|
|
|
163
163
|
cssUrls: [...cssUrls]
|
|
164
164
|
};
|
|
165
165
|
}
|
|
166
|
-
export function sanitizeInstagramEmbedCss(css, scopeSelector, prefix
|
|
166
|
+
export function sanitizeInstagramEmbedCss(css, scopeSelector, prefix) {
|
|
167
167
|
const rewritten = rewriteUrlsInText(
|
|
168
|
-
css.replace(RSRC_RE, (_match, path) => `url(${proxyAssetUrl(`https://${INSTAGRAM_ASSET_HOST}/rsrc.php${path}`, prefix
|
|
169
|
-
prefix
|
|
170
|
-
secret
|
|
168
|
+
css.replace(RSRC_RE, (_match, path) => `url(${proxyAssetUrl(`https://${INSTAGRAM_ASSET_HOST}/rsrc.php${path}`, prefix)})`),
|
|
169
|
+
prefix
|
|
171
170
|
);
|
|
172
171
|
return sanitizeCssUrls(scopeCss(rewritten, scopeSelector), prefix).replace(/<\/style/gi, "<\\/style");
|
|
173
172
|
}
|
|
174
|
-
export function proxyImageUrl(url, prefix = "/_scripts"
|
|
175
|
-
return buildProxyUrl(`${prefix}/embed/instagram-image`, { url: url.replace(AMP_RE, "&") }
|
|
173
|
+
export function proxyImageUrl(url, prefix = "/_scripts") {
|
|
174
|
+
return buildProxyUrl(`${prefix}/embed/instagram-image`, { url: url.replace(AMP_RE, "&") });
|
|
176
175
|
}
|
|
177
|
-
export function proxyAssetUrl(url, prefix = "/_scripts"
|
|
178
|
-
return buildProxyUrl(`${prefix}/embed/instagram-asset`, { url: url.replace(AMP_RE, "&") }
|
|
176
|
+
export function proxyAssetUrl(url, prefix = "/_scripts") {
|
|
177
|
+
return buildProxyUrl(`${prefix}/embed/instagram-asset`, { url: url.replace(AMP_RE, "&") });
|
|
179
178
|
}
|
|
180
|
-
export function rewriteUrl(url, prefix = "/_scripts"
|
|
179
|
+
export function rewriteUrl(url, prefix = "/_scripts") {
|
|
181
180
|
try {
|
|
182
181
|
const parsed = new URL(url);
|
|
183
182
|
if (parsed.hostname === INSTAGRAM_ASSET_HOST)
|
|
184
|
-
return proxyAssetUrl(url, prefix
|
|
183
|
+
return proxyAssetUrl(url, prefix);
|
|
185
184
|
if (INSTAGRAM_IMAGE_HOSTS.some((h) => parsed.hostname === h || parsed.hostname.endsWith(`.cdninstagram.com`)))
|
|
186
|
-
return proxyImageUrl(url, prefix
|
|
185
|
+
return proxyImageUrl(url, prefix);
|
|
187
186
|
} catch {
|
|
188
187
|
}
|
|
189
188
|
return url;
|
|
190
189
|
}
|
|
191
|
-
export function rewriteUrlsInText(text, prefix = "/_scripts"
|
|
192
|
-
return text.replace(SCONTENT_RE, (m) => proxyImageUrl(m, prefix
|
|
190
|
+
export function rewriteUrlsInText(text, prefix = "/_scripts") {
|
|
191
|
+
return text.replace(SCONTENT_RE, (m) => proxyImageUrl(m, prefix)).replace(STATIC_CDN_RE, (m) => proxyAssetUrl(m, prefix)).replace(LOOKASIDE_RE, (m) => proxyImageUrl(m, prefix));
|
|
193
192
|
}
|
|
194
193
|
export function scopeCss(css, scopeSelector) {
|
|
195
194
|
let result = css.replace(CHARSET_RE, "");
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Build a proxy URL with query params
|
|
2
|
+
* Build a proxy URL with query params.
|
|
3
3
|
*
|
|
4
|
-
* Used by embed handlers that inject proxy URLs into HTML/JSON responses
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* (which is only safe when the `withSigning` middleware has no secret either).
|
|
4
|
+
* Used by embed handlers that inject proxy URLs into HTML/JSON responses so
|
|
5
|
+
* the client loads upstream assets through the site origin. The proxy
|
|
6
|
+
* endpoints are restricted to an allowlist of upstream domains.
|
|
8
7
|
*/
|
|
9
|
-
export declare function buildProxyUrl(path: string, query: Record<string, unknown
|
|
8
|
+
export declare function buildProxyUrl(path: string, query: Record<string, unknown>): string;
|
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
export function buildProxyUrl(path, query, secret) {
|
|
3
|
-
if (secret)
|
|
4
|
-
return buildSignedProxyUrl(path, query, secret);
|
|
1
|
+
export function buildProxyUrl(path, query) {
|
|
5
2
|
const parts = [];
|
|
6
3
|
for (const [key, value] of Object.entries(query)) {
|
|
7
4
|
if (value === void 0 || value === null)
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest,
|
|
1
|
+
declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<Buffer<ArrayBufferLike>>>;
|
|
2
2
|
export default _default;
|
|
@@ -1,2 +1,49 @@
|
|
|
1
|
-
|
|
1
|
+
interface TweetData {
|
|
2
|
+
id_str: string;
|
|
3
|
+
text: string;
|
|
4
|
+
created_at: string;
|
|
5
|
+
favorite_count: number;
|
|
6
|
+
conversation_count: number;
|
|
7
|
+
user: {
|
|
8
|
+
name: string;
|
|
9
|
+
screen_name: string;
|
|
10
|
+
profile_image_url_https: string;
|
|
11
|
+
verified?: boolean;
|
|
12
|
+
is_blue_verified?: boolean;
|
|
13
|
+
};
|
|
14
|
+
entities?: {
|
|
15
|
+
media?: Array<{
|
|
16
|
+
media_url_https: string;
|
|
17
|
+
type: string;
|
|
18
|
+
sizes: Record<string, {
|
|
19
|
+
w: number;
|
|
20
|
+
h: number;
|
|
21
|
+
}>;
|
|
22
|
+
}>;
|
|
23
|
+
urls?: Array<{
|
|
24
|
+
url: string;
|
|
25
|
+
expanded_url: string;
|
|
26
|
+
display_url: string;
|
|
27
|
+
}>;
|
|
28
|
+
};
|
|
29
|
+
photos?: Array<{
|
|
30
|
+
url: string;
|
|
31
|
+
width: number;
|
|
32
|
+
height: number;
|
|
33
|
+
}>;
|
|
34
|
+
video?: {
|
|
35
|
+
poster: string;
|
|
36
|
+
variants: Array<{
|
|
37
|
+
type: string;
|
|
38
|
+
src: string;
|
|
39
|
+
}>;
|
|
40
|
+
};
|
|
41
|
+
quoted_tweet?: TweetData;
|
|
42
|
+
parent?: {
|
|
43
|
+
user: {
|
|
44
|
+
screen_name: string;
|
|
45
|
+
};
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<TweetData>>;
|
|
2
49
|
export default _default;
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import { createError, defineEventHandler, getQuery, setHeader } from "#nuxt-scripts/h3";
|
|
2
|
-
import { useRuntimeConfig } from "#nuxt-scripts/nitro";
|
|
3
2
|
import { createCachedJsonFetch, isSafeHttpsUrl } from "./utils/cached-upstream.js";
|
|
4
3
|
import { rewriteTweetImages } from "./utils/embed-rewriters.js";
|
|
5
|
-
import { withSigning } from "./utils/withSigning.js";
|
|
6
4
|
const TWEET_ID_RE = /^\d+$/;
|
|
7
5
|
const EMBED_X_SUFFIX_RE = /\/embed\/x$/;
|
|
8
6
|
const TWEET_ID_FROM_URL_RE = /[?&]id=(\d+)/;
|
|
@@ -18,7 +16,7 @@ const cachedTweetFetch = createCachedJsonFetch(
|
|
|
18
16
|
contentTypePrefixes: ["application/json"]
|
|
19
17
|
}
|
|
20
18
|
);
|
|
21
|
-
export default
|
|
19
|
+
export default defineEventHandler(async (event) => {
|
|
22
20
|
const query = getQuery(event);
|
|
23
21
|
const tweetId = query.id;
|
|
24
22
|
if (!tweetId || !TWEET_ID_RE.test(tweetId)) {
|
|
@@ -47,9 +45,8 @@ export default withSigning(defineEventHandler(async (event) => {
|
|
|
47
45
|
const handlerPath = event.path?.split("?")[0] || "";
|
|
48
46
|
const prefix = handlerPath.replace(EMBED_X_SUFFIX_RE, "") || "/_scripts";
|
|
49
47
|
const imagePath = `${prefix}/embed/x-image`;
|
|
50
|
-
|
|
51
|
-
rewriteTweetImages(tweetData, imagePath, secret);
|
|
48
|
+
rewriteTweetImages(tweetData, imagePath);
|
|
52
49
|
setHeader(event, "Content-Type", "application/json");
|
|
53
50
|
setHeader(event, "Cache-Control", "public, max-age=600, s-maxage=600");
|
|
54
51
|
return tweetData;
|
|
55
|
-
})
|
|
52
|
+
});
|
package/dist/runtime/types.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { UseScriptInput, UseScriptOptions, VueScriptInstance } from '@unhead/vue/scripts';
|
|
1
|
+
import type { UseScriptInput, UseScriptOptions, VueScriptInstance, VueScriptScope } from '@unhead/vue/scripts';
|
|
2
2
|
import type { Script } from '@unhead/vue/types';
|
|
3
3
|
import type { Import } from 'unimport';
|
|
4
4
|
import type { InferInput, ObjectEntries, ObjectSchema, UnionSchema, ValiError } from 'valibot';
|
|
@@ -11,6 +11,7 @@ import type { ClarityInput } from './registry/clarity.js';
|
|
|
11
11
|
import type { CloudflareWebAnalyticsInput } from './registry/cloudflare-web-analytics.js';
|
|
12
12
|
import type { CrispInput } from './registry/crisp.js';
|
|
13
13
|
import type { DatabuddyAnalyticsInput } from './registry/databuddy-analytics.js';
|
|
14
|
+
import type { DeskCrewInput } from './registry/deskcrew.js';
|
|
14
15
|
import type { FathomAnalyticsInput } from './registry/fathom-analytics.js';
|
|
15
16
|
import type { GoogleAdsenseInput } from './registry/google-adsense.js';
|
|
16
17
|
import type { GoogleAnalyticsInput } from './registry/google-analytics.js';
|
|
@@ -22,8 +23,10 @@ import type { GravatarInput } from './registry/gravatar.js';
|
|
|
22
23
|
import type { HotjarInput } from './registry/hotjar.js';
|
|
23
24
|
import type { InstagramEmbedInput } from './registry/instagram-embed.js';
|
|
24
25
|
import type { IntercomInput } from './registry/intercom.js';
|
|
26
|
+
import type { LeafletInput } from './registry/leaflet.js';
|
|
25
27
|
import type { LemonSqueezyInput } from './registry/lemon-squeezy.js';
|
|
26
28
|
import type { LinkedInInsightInput } from './registry/linkedin-insight.js';
|
|
29
|
+
import type { MapLibreInput } from './registry/maplibre.js';
|
|
27
30
|
import type { MatomoAnalyticsInput } from './registry/matomo-analytics.js';
|
|
28
31
|
import type { MetaPixelInput } from './registry/meta-pixel.js';
|
|
29
32
|
import type { MixpanelAnalyticsInput } from './registry/mixpanel-analytics.js';
|
|
@@ -81,7 +84,7 @@ export interface GcmConsentApi {
|
|
|
81
84
|
/** Push `['consent','update', state]` (or equivalent gtag call) with GCMv2 partial state. */
|
|
82
85
|
update: (state: ConsentState) => void;
|
|
83
86
|
}
|
|
84
|
-
export type UseScriptContext<T extends Record<symbol | string, any>, C = unknown> =
|
|
87
|
+
export type UseScriptContext<T extends Record<symbol | string, any>, C = unknown> = VueScriptScope<T> & {
|
|
85
88
|
/**
|
|
86
89
|
* Remove and reload the script. Useful for scripts that need to re-execute
|
|
87
90
|
* after SPA navigation (e.g., DOM-scanning scripts like iubenda).
|
|
@@ -240,6 +243,7 @@ export interface ScriptRegistry {
|
|
|
240
243
|
carbonAds?: true;
|
|
241
244
|
calendly?: CalendlyInput;
|
|
242
245
|
crisp?: CrispInput;
|
|
246
|
+
deskcrew?: DeskCrewInput;
|
|
243
247
|
clarity?: ClarityInput;
|
|
244
248
|
cloudflareWebAnalytics?: CloudflareWebAnalyticsInput;
|
|
245
249
|
databuddyAnalytics?: DatabuddyAnalyticsInput;
|
|
@@ -250,6 +254,8 @@ export interface ScriptRegistry {
|
|
|
250
254
|
googleAdsense?: GoogleAdsenseInput;
|
|
251
255
|
googleAnalytics?: GoogleAnalyticsInput;
|
|
252
256
|
googleMaps?: GoogleMapsInput;
|
|
257
|
+
leaflet?: LeafletInput;
|
|
258
|
+
maplibre?: MapLibreInput;
|
|
253
259
|
googleRecaptcha?: GoogleRecaptchaInput;
|
|
254
260
|
googleSignIn?: GoogleSignInInput;
|
|
255
261
|
lemonSqueezy?: LemonSqueezyInput;
|
|
@@ -283,14 +289,14 @@ export interface ScriptRegistry {
|
|
|
283
289
|
* Built-in registry script keys — not affected by module augmentation.
|
|
284
290
|
* Use this to type-check records that must enumerate all built-in scripts (logos, meta, etc.).
|
|
285
291
|
*/
|
|
286
|
-
export type BuiltInRegistryScriptKey = 'ahrefsAnalytics' | 'bingUet' | 'blueskyEmbed' | 'calendly' | 'carbonAds' | 'crisp' | 'clarity' | 'cloudflareWebAnalytics' | 'databuddyAnalytics' | 'metaPixel' | 'fathomAnalytics' | 'instagramEmbed' | 'plausibleAnalytics' | 'googleAdsense' | 'googleAnalytics' | 'googleMaps' | 'googleRecaptcha' | 'googleSignIn' | 'lemonSqueezy' | 'googleTagManager' | 'hotjar' | 'intercom' | 'linkedinInsight' | 'paypal' | 'posthog' | 'matomoAnalytics' | 'mixpanelAnalytics' | 'rybbitAnalytics' | 'redditPixel' | 'segment' | 'stripe' | 'tiktokPixel' | 'xEmbed' | 'xPixel' | 'snapchatPixel' | 'speedcurve' | 'youtubePlayer' | 'vercelAnalytics' | 'vimeoPlayer' | 'umamiAnalytics' | 'usercentrics' | 'gravatar' | 'npm';
|
|
292
|
+
export type BuiltInRegistryScriptKey = 'ahrefsAnalytics' | 'bingUet' | 'blueskyEmbed' | 'calendly' | 'carbonAds' | 'crisp' | 'clarity' | 'cloudflareWebAnalytics' | 'databuddyAnalytics' | 'deskcrew' | 'metaPixel' | 'fathomAnalytics' | 'instagramEmbed' | 'plausibleAnalytics' | 'googleAdsense' | 'googleAnalytics' | 'googleMaps' | 'leaflet' | 'maplibre' | 'googleRecaptcha' | 'googleSignIn' | 'lemonSqueezy' | 'googleTagManager' | 'hotjar' | 'intercom' | 'linkedinInsight' | 'paypal' | 'posthog' | 'matomoAnalytics' | 'mixpanelAnalytics' | 'rybbitAnalytics' | 'redditPixel' | 'segment' | 'stripe' | 'tiktokPixel' | 'xEmbed' | 'xPixel' | 'snapchatPixel' | 'speedcurve' | 'youtubePlayer' | 'vercelAnalytics' | 'vimeoPlayer' | 'umamiAnalytics' | 'usercentrics' | 'gravatar' | 'npm';
|
|
287
293
|
/**
|
|
288
294
|
* Union of all explicit registry script keys (excludes npm pattern).
|
|
289
295
|
* Includes both built-in and augmented keys.
|
|
290
296
|
*/
|
|
291
297
|
export type RegistryScriptKey = Exclude<keyof ScriptRegistry, `${string}-npm`>;
|
|
292
298
|
type RegistryConfigInput<T> = 0 extends 1 & T ? Record<string, any> : [T] extends [true] ? Record<string, never> : T;
|
|
293
|
-
export type NuxtConfigScriptRegistryEntry<T> =
|
|
299
|
+
export type NuxtConfigScriptRegistryEntry<T> = false | 'mock' | (RegistryConfigInput<T> & {
|
|
294
300
|
trigger?: NuxtUseScriptOptionsSerializable['trigger'] | false;
|
|
295
301
|
proxy?: boolean;
|
|
296
302
|
bundle?: boolean;
|
|
@@ -304,8 +310,10 @@ export interface NuxtConfigScriptRegistry extends _NuxtConfigScriptRegistryEntri
|
|
|
304
310
|
[key: string]: any;
|
|
305
311
|
}
|
|
306
312
|
export type UseFunctionType<T, U> = T extends {
|
|
313
|
+
resolve: infer V;
|
|
314
|
+
} ? V extends (...args: any) => any ? NonNullable<Awaited<ReturnType<V>>> : U : T extends {
|
|
307
315
|
use: infer V;
|
|
308
|
-
} ? V extends (...args: any) => any ? ReturnType<V
|
|
316
|
+
} ? V extends (...args: any) => any ? NonNullable<Awaited<ReturnType<V>>> : U : U;
|
|
309
317
|
export type EmptyOptionsSchema = ObjectSchema<ObjectEntries, undefined>;
|
|
310
318
|
type ScriptInput = Script;
|
|
311
319
|
export type InferIfSchema<T> = T extends ObjectSchema<any, any> | UnionSchema<any, any> ? InferInput<T> : T;
|
|
@@ -322,14 +330,6 @@ export interface RegistryScriptServerHandler {
|
|
|
322
330
|
route: string;
|
|
323
331
|
handler: string;
|
|
324
332
|
middleware?: boolean;
|
|
325
|
-
/**
|
|
326
|
-
* Whether this handler verifies HMAC signatures via `withSigning()`.
|
|
327
|
-
*
|
|
328
|
-
* When any enabled script registers a handler with `requiresSigning: true`,
|
|
329
|
-
* the module enforces that `NUXT_SCRIPTS_PROXY_SECRET` is set in production,
|
|
330
|
-
* and the `/_scripts/sign` endpoint will accept this route as a signable path.
|
|
331
|
-
*/
|
|
332
|
-
requiresSigning?: boolean;
|
|
333
333
|
}
|
|
334
334
|
/**
|
|
335
335
|
* Declares what optimization modes a script supports and what's active by default.
|