@nuxt/scripts 1.0.0-beta.20 → 1.0.0-beta.22
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/dist/client/200.html +1 -1
- package/dist/client/404.html +1 -1
- package/dist/client/_nuxt/{DMlY-BNa.js → B7aPLMNo.js} +1 -1
- package/dist/client/_nuxt/BNNMZFwZ.js +162 -0
- package/dist/client/_nuxt/{__ZZTkMj.js → Bh9fd9qr.js} +1 -1
- package/dist/client/_nuxt/{9LJPrOyI.js → UTi7FhVv.js} +1 -1
- package/dist/client/_nuxt/builds/latest.json +1 -1
- package/dist/client/_nuxt/builds/meta/7b372941-1db0-4ea4-80d2-a41f53088a98.json +1 -0
- package/dist/client/_nuxt/error-404.DMdWw4vT.css +1 -0
- package/dist/client/_nuxt/error-500.CROTF27X.css +1 -0
- package/dist/client/index.html +1 -1
- package/dist/module.d.mts +2 -2
- package/dist/module.d.ts +2 -2
- package/dist/module.json +1 -1
- package/dist/module.mjs +75 -51
- package/dist/registry.mjs +65 -4
- package/dist/runtime/components/GoogleMaps/ScriptGoogleMaps.d.vue.ts +3 -3
- package/dist/runtime/components/GoogleMaps/ScriptGoogleMaps.vue +14 -1
- package/dist/runtime/components/GoogleMaps/ScriptGoogleMaps.vue.d.ts +3 -3
- package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsAdvancedMarkerElement.d.vue.ts +2 -2
- package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsAdvancedMarkerElement.vue.d.ts +2 -2
- package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsMarker.d.vue.ts +2 -2
- package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsMarker.vue.d.ts +2 -2
- package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsPolygon.d.vue.ts +2 -2
- package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsPolygon.vue.d.ts +2 -2
- package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsPolyline.d.vue.ts +2 -2
- package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsPolyline.vue.d.ts +2 -2
- package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsRectangle.d.vue.ts +2 -2
- package/dist/runtime/components/GoogleMaps/ScriptGoogleMapsRectangle.vue.d.ts +2 -2
- package/dist/runtime/components/ScriptBlueskyEmbed.d.vue.ts +87 -0
- package/dist/runtime/components/ScriptBlueskyEmbed.vue +85 -0
- package/dist/runtime/components/ScriptBlueskyEmbed.vue.d.ts +87 -0
- package/dist/runtime/components/ScriptInstagramEmbed.d.vue.ts +2 -2
- package/dist/runtime/components/ScriptInstagramEmbed.vue +4 -1
- package/dist/runtime/components/ScriptInstagramEmbed.vue.d.ts +2 -2
- package/dist/runtime/components/ScriptXEmbed.d.vue.ts +2 -2
- package/dist/runtime/components/ScriptXEmbed.vue +5 -2
- package/dist/runtime/components/ScriptXEmbed.vue.d.ts +2 -2
- package/dist/runtime/registry/bluesky-embed.d.ts +116 -0
- package/dist/runtime/registry/bluesky-embed.js +72 -0
- package/dist/runtime/registry/gravatar.js +2 -2
- package/dist/runtime/registry/schemas.d.ts +20 -3
- package/dist/runtime/registry/schemas.js +20 -3
- package/dist/runtime/registry/x-embed.js +1 -1
- package/dist/runtime/server/bluesky-embed-image.d.ts +2 -0
- package/dist/runtime/server/bluesky-embed-image.js +7 -0
- package/dist/runtime/server/bluesky-embed.d.ts +16 -0
- package/dist/runtime/server/bluesky-embed.js +59 -0
- package/dist/runtime/server/google-maps-geocode-proxy.d.ts +2 -0
- package/dist/runtime/server/google-maps-geocode-proxy.js +34 -0
- package/dist/runtime/server/google-static-maps-proxy.js +1 -12
- package/dist/runtime/server/gravatar-proxy.js +1 -16
- package/dist/runtime/server/instagram-embed-asset.js +8 -42
- package/dist/runtime/server/instagram-embed-image.js +6 -54
- package/dist/runtime/server/instagram-embed.d.ts +16 -0
- package/dist/runtime/server/instagram-embed.js +171 -42
- package/dist/runtime/server/utils/image-proxy.d.ts +12 -0
- package/dist/runtime/server/utils/image-proxy.js +70 -0
- package/dist/runtime/server/x-embed-image.js +5 -49
- package/dist/runtime/types.d.ts +16 -0
- package/dist/runtime/utils.d.ts +1 -0
- package/dist/runtime/utils.js +11 -2
- package/dist/stats.mjs +1 -1
- package/dist/types-source.mjs +18 -6
- package/package.json +10 -9
- package/dist/client/_nuxt/DFEfk2pB.js +0 -162
- package/dist/client/_nuxt/builds/meta/8212d4fa-7985-421b-815a-03a886e667d4.json +0 -1
- package/dist/client/_nuxt/error-404.CHeaW3dp.css +0 -1
- package/dist/client/_nuxt/error-500.DvOvWme_.css +0 -1
|
@@ -7,12 +7,12 @@ type __VLS_Props = {
|
|
|
7
7
|
tweetId: string;
|
|
8
8
|
/**
|
|
9
9
|
* Custom API endpoint for fetching tweet data
|
|
10
|
-
* @default '/
|
|
10
|
+
* @default '/_scripts/embed/x'
|
|
11
11
|
*/
|
|
12
12
|
apiEndpoint?: string;
|
|
13
13
|
/**
|
|
14
14
|
* Custom image proxy endpoint
|
|
15
|
-
* @default '/_scripts/x-
|
|
15
|
+
* @default '/_scripts/embed/x-image'
|
|
16
16
|
*/
|
|
17
17
|
imageProxyEndpoint?: string;
|
|
18
18
|
/**
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import type { RegistryScriptInput } from '#nuxt-scripts/types';
|
|
2
|
+
import { BlueskyEmbedOptions } from './schemas.js';
|
|
3
|
+
export { BlueskyEmbedOptions };
|
|
4
|
+
export interface BlueskyEmbedPostData {
|
|
5
|
+
uri: string;
|
|
6
|
+
cid: string;
|
|
7
|
+
author: {
|
|
8
|
+
did: string;
|
|
9
|
+
handle: string;
|
|
10
|
+
displayName: string;
|
|
11
|
+
avatar: string;
|
|
12
|
+
labels: Array<{
|
|
13
|
+
val: string;
|
|
14
|
+
}>;
|
|
15
|
+
verification?: {
|
|
16
|
+
verifiedStatus: string;
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
record: {
|
|
20
|
+
$type: string;
|
|
21
|
+
createdAt: string;
|
|
22
|
+
text: string;
|
|
23
|
+
langs?: string[];
|
|
24
|
+
facets?: Array<{
|
|
25
|
+
features: Array<{
|
|
26
|
+
$type: string;
|
|
27
|
+
uri?: string;
|
|
28
|
+
did?: string;
|
|
29
|
+
tag?: string;
|
|
30
|
+
}>;
|
|
31
|
+
index: {
|
|
32
|
+
byteStart: number;
|
|
33
|
+
byteEnd: number;
|
|
34
|
+
};
|
|
35
|
+
}>;
|
|
36
|
+
embed?: {
|
|
37
|
+
$type: string;
|
|
38
|
+
images?: Array<{
|
|
39
|
+
alt: string;
|
|
40
|
+
image: {
|
|
41
|
+
ref: {
|
|
42
|
+
$link: string;
|
|
43
|
+
};
|
|
44
|
+
mimeType: string;
|
|
45
|
+
size: number;
|
|
46
|
+
};
|
|
47
|
+
aspectRatio?: {
|
|
48
|
+
width: number;
|
|
49
|
+
height: number;
|
|
50
|
+
};
|
|
51
|
+
}>;
|
|
52
|
+
external?: {
|
|
53
|
+
uri: string;
|
|
54
|
+
title: string;
|
|
55
|
+
description: string;
|
|
56
|
+
thumb?: {
|
|
57
|
+
ref: {
|
|
58
|
+
$link: string;
|
|
59
|
+
};
|
|
60
|
+
mimeType: string;
|
|
61
|
+
size: number;
|
|
62
|
+
};
|
|
63
|
+
};
|
|
64
|
+
};
|
|
65
|
+
};
|
|
66
|
+
embed?: {
|
|
67
|
+
$type: string;
|
|
68
|
+
images?: Array<{
|
|
69
|
+
thumb: string;
|
|
70
|
+
fullsize: string;
|
|
71
|
+
alt: string;
|
|
72
|
+
aspectRatio?: {
|
|
73
|
+
width: number;
|
|
74
|
+
height: number;
|
|
75
|
+
};
|
|
76
|
+
}>;
|
|
77
|
+
external?: {
|
|
78
|
+
uri: string;
|
|
79
|
+
title: string;
|
|
80
|
+
description: string;
|
|
81
|
+
thumb?: string;
|
|
82
|
+
};
|
|
83
|
+
};
|
|
84
|
+
likeCount: number;
|
|
85
|
+
repostCount: number;
|
|
86
|
+
replyCount: number;
|
|
87
|
+
quoteCount: number;
|
|
88
|
+
indexedAt: string;
|
|
89
|
+
labels: Array<{
|
|
90
|
+
val: string;
|
|
91
|
+
}>;
|
|
92
|
+
}
|
|
93
|
+
export type BlueskyEmbedInput = RegistryScriptInput<typeof BlueskyEmbedOptions, false, false, false>;
|
|
94
|
+
/**
|
|
95
|
+
* Extract the handle/DID and post rkey from a Bluesky post URL
|
|
96
|
+
*/
|
|
97
|
+
export declare function extractBlueskyPostId(url: string): {
|
|
98
|
+
actor: string;
|
|
99
|
+
rkey: string;
|
|
100
|
+
} | undefined;
|
|
101
|
+
/**
|
|
102
|
+
* Proxy a Bluesky image URL through the server
|
|
103
|
+
*/
|
|
104
|
+
export declare function proxyBlueskyImageUrl(url: string, proxyEndpoint?: string): string;
|
|
105
|
+
/**
|
|
106
|
+
* Format a Bluesky post date for display
|
|
107
|
+
*/
|
|
108
|
+
export declare function formatBlueskyDate(dateString: string): string;
|
|
109
|
+
/**
|
|
110
|
+
* Format a number for display (e.g., 1234 -> 1.2K)
|
|
111
|
+
*/
|
|
112
|
+
export declare function formatCount(count: number): string;
|
|
113
|
+
/**
|
|
114
|
+
* Convert Bluesky facets (byte-range rich text annotations) to HTML
|
|
115
|
+
*/
|
|
116
|
+
export declare function facetsToHtml(text: string, facets?: BlueskyEmbedPostData['record']['facets']): string;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { BlueskyEmbedOptions } from "./schemas.js";
|
|
2
|
+
export { BlueskyEmbedOptions };
|
|
3
|
+
const BSKY_POST_URL_RE = /bsky\.app\/profile\/([^/]+)\/post\/([^/?]+)/;
|
|
4
|
+
export function extractBlueskyPostId(url) {
|
|
5
|
+
const match = url.match(BSKY_POST_URL_RE);
|
|
6
|
+
if (!match)
|
|
7
|
+
return void 0;
|
|
8
|
+
return { actor: match[1], rkey: match[2] };
|
|
9
|
+
}
|
|
10
|
+
export function proxyBlueskyImageUrl(url, proxyEndpoint = "/_scripts/embed/bluesky-image") {
|
|
11
|
+
const separator = proxyEndpoint.includes("?") ? "&" : "?";
|
|
12
|
+
return `${proxyEndpoint}${separator}url=${encodeURIComponent(url)}`;
|
|
13
|
+
}
|
|
14
|
+
export function formatBlueskyDate(dateString) {
|
|
15
|
+
const date = new Date(dateString);
|
|
16
|
+
const time = date.toLocaleString("en-US", {
|
|
17
|
+
hour: "numeric",
|
|
18
|
+
minute: "numeric",
|
|
19
|
+
hour12: true,
|
|
20
|
+
timeZone: "UTC"
|
|
21
|
+
});
|
|
22
|
+
const month = date.toLocaleString("en-US", { month: "short", timeZone: "UTC" });
|
|
23
|
+
return `${time} \xB7 ${month} ${date.getUTCDate()}, ${date.getUTCFullYear()}`;
|
|
24
|
+
}
|
|
25
|
+
export function formatCount(count) {
|
|
26
|
+
if (count >= 1e6) {
|
|
27
|
+
return `${(count / 1e6).toFixed(1)}M`;
|
|
28
|
+
}
|
|
29
|
+
if (count >= 1e3) {
|
|
30
|
+
return `${(count / 1e3).toFixed(1)}K`;
|
|
31
|
+
}
|
|
32
|
+
return count.toString();
|
|
33
|
+
}
|
|
34
|
+
export function facetsToHtml(text, facets) {
|
|
35
|
+
if (!facets?.length)
|
|
36
|
+
return escapeHtml(text);
|
|
37
|
+
const encoder = new TextEncoder();
|
|
38
|
+
const decoder = new TextDecoder();
|
|
39
|
+
const bytes = encoder.encode(text);
|
|
40
|
+
const sorted = facets.toSorted((a, b) => a.index.byteStart - b.index.byteStart);
|
|
41
|
+
const parts = [];
|
|
42
|
+
let lastEnd = 0;
|
|
43
|
+
for (const facet of sorted) {
|
|
44
|
+
if (facet.index.byteStart > lastEnd) {
|
|
45
|
+
parts.push(escapeHtml(decoder.decode(bytes.slice(lastEnd, facet.index.byteStart))));
|
|
46
|
+
}
|
|
47
|
+
const facetText = escapeHtml(decoder.decode(bytes.slice(facet.index.byteStart, facet.index.byteEnd)));
|
|
48
|
+
for (const feature of facet.features) {
|
|
49
|
+
if (feature.$type === "app.bsky.richtext.facet#link" && feature.uri) {
|
|
50
|
+
parts.push(`<a href="${escapeHtml(feature.uri)}" target="_blank" rel="noopener noreferrer">${facetText}</a>`);
|
|
51
|
+
} else if (feature.$type === "app.bsky.richtext.facet#mention" && feature.did) {
|
|
52
|
+
parts.push(`<a href="https://bsky.app/profile/${escapeHtml(feature.did)}" target="_blank" rel="noopener noreferrer">${facetText}</a>`);
|
|
53
|
+
} else if (feature.$type === "app.bsky.richtext.facet#tag" && feature.tag) {
|
|
54
|
+
parts.push(`<a href="https://bsky.app/hashtag/${escapeHtml(feature.tag)}" target="_blank" rel="noopener noreferrer">${facetText}</a>`);
|
|
55
|
+
} else {
|
|
56
|
+
parts.push(facetText);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
lastEnd = facet.index.byteEnd;
|
|
60
|
+
}
|
|
61
|
+
if (lastEnd < bytes.length) {
|
|
62
|
+
parts.push(escapeHtml(decoder.decode(bytes.slice(lastEnd))));
|
|
63
|
+
}
|
|
64
|
+
return parts.join("");
|
|
65
|
+
}
|
|
66
|
+
const AMP_RE = /&/g;
|
|
67
|
+
const LT_RE = /</g;
|
|
68
|
+
const GT_RE = />/g;
|
|
69
|
+
const QUOT_RE = /"/g;
|
|
70
|
+
function escapeHtml(str) {
|
|
71
|
+
return str.replace(AMP_RE, "&").replace(LT_RE, "<").replace(GT_RE, ">").replace(QUOT_RE, """);
|
|
72
|
+
}
|
|
@@ -20,10 +20,10 @@ export function useScriptGravatar(_options) {
|
|
|
20
20
|
scriptOptions: {
|
|
21
21
|
use: () => ({
|
|
22
22
|
getAvatarUrl: (hash, overrides) => {
|
|
23
|
-
return `/_scripts/gravatar
|
|
23
|
+
return `/_scripts/proxy/gravatar?hash=${encodeURIComponent(hash)}&${buildQuery(overrides)}`;
|
|
24
24
|
},
|
|
25
25
|
getAvatarUrlFromEmail: (email, overrides) => {
|
|
26
|
-
return `/_scripts/gravatar
|
|
26
|
+
return `/_scripts/proxy/gravatar?email=${encodeURIComponent(email)}&${buildQuery(overrides)}`;
|
|
27
27
|
}
|
|
28
28
|
})
|
|
29
29
|
}
|
|
@@ -1,3 +1,20 @@
|
|
|
1
|
+
export declare const BlueskyEmbedOptions: import("valibot").ObjectSchema<{
|
|
2
|
+
/**
|
|
3
|
+
* The Bluesky post URL to embed.
|
|
4
|
+
* @example 'https://bsky.app/profile/bsky.app/post/3mgnwwvj3u22a'
|
|
5
|
+
*/
|
|
6
|
+
readonly postUrl: import("valibot").StringSchema<undefined>;
|
|
7
|
+
/**
|
|
8
|
+
* Custom API endpoint for fetching post data.
|
|
9
|
+
* @default '/_scripts/embed/bluesky'
|
|
10
|
+
*/
|
|
11
|
+
readonly apiEndpoint: import("valibot").OptionalSchema<import("valibot").StringSchema<undefined>, undefined>;
|
|
12
|
+
/**
|
|
13
|
+
* Custom image proxy endpoint.
|
|
14
|
+
* @default '/_scripts/embed/bluesky-image'
|
|
15
|
+
*/
|
|
16
|
+
readonly imageProxyEndpoint: import("valibot").OptionalSchema<import("valibot").StringSchema<undefined>, undefined>;
|
|
17
|
+
}, undefined>;
|
|
1
18
|
export declare const ClarityOptions: import("valibot").ObjectSchema<{
|
|
2
19
|
/**
|
|
3
20
|
* The Clarity token.
|
|
@@ -433,7 +450,7 @@ export declare const InstagramEmbedOptions: import("valibot").ObjectSchema<{
|
|
|
433
450
|
readonly captions: import("valibot").OptionalSchema<import("valibot").BooleanSchema<undefined>, undefined>;
|
|
434
451
|
/**
|
|
435
452
|
* Custom API endpoint for fetching embed HTML.
|
|
436
|
-
* @default '/
|
|
453
|
+
* @default '/_scripts/embed/instagram'
|
|
437
454
|
*/
|
|
438
455
|
readonly apiEndpoint: import("valibot").OptionalSchema<import("valibot").StringSchema<undefined>, undefined>;
|
|
439
456
|
}, undefined>;
|
|
@@ -871,12 +888,12 @@ export declare const XEmbedOptions: import("valibot").ObjectSchema<{
|
|
|
871
888
|
readonly tweetId: import("valibot").StringSchema<undefined>;
|
|
872
889
|
/**
|
|
873
890
|
* Optional: Custom API endpoint for fetching tweet data.
|
|
874
|
-
* @default '/
|
|
891
|
+
* @default '/_scripts/embed/x'
|
|
875
892
|
*/
|
|
876
893
|
readonly apiEndpoint: import("valibot").OptionalSchema<import("valibot").StringSchema<undefined>, undefined>;
|
|
877
894
|
/**
|
|
878
895
|
* Optional: Custom image proxy endpoint.
|
|
879
|
-
* @default '/
|
|
896
|
+
* @default '/_scripts/embed/x-image'
|
|
880
897
|
*/
|
|
881
898
|
readonly imageProxyEndpoint: import("valibot").OptionalSchema<import("valibot").StringSchema<undefined>, undefined>;
|
|
882
899
|
}, undefined>;
|
|
@@ -1,4 +1,21 @@
|
|
|
1
1
|
import { any, array, boolean, custom, literal, minLength, number, object, optional, pipe, record, string, union } from "#nuxt-scripts-validator";
|
|
2
|
+
export const BlueskyEmbedOptions = object({
|
|
3
|
+
/**
|
|
4
|
+
* The Bluesky post URL to embed.
|
|
5
|
+
* @example 'https://bsky.app/profile/bsky.app/post/3mgnwwvj3u22a'
|
|
6
|
+
*/
|
|
7
|
+
postUrl: string(),
|
|
8
|
+
/**
|
|
9
|
+
* Custom API endpoint for fetching post data.
|
|
10
|
+
* @default '/_scripts/embed/bluesky'
|
|
11
|
+
*/
|
|
12
|
+
apiEndpoint: optional(string()),
|
|
13
|
+
/**
|
|
14
|
+
* Custom image proxy endpoint.
|
|
15
|
+
* @default '/_scripts/embed/bluesky-image'
|
|
16
|
+
*/
|
|
17
|
+
imageProxyEndpoint: optional(string())
|
|
18
|
+
});
|
|
2
19
|
export const ClarityOptions = object({
|
|
3
20
|
/**
|
|
4
21
|
* The Clarity token.
|
|
@@ -428,7 +445,7 @@ export const InstagramEmbedOptions = object({
|
|
|
428
445
|
captions: optional(boolean()),
|
|
429
446
|
/**
|
|
430
447
|
* Custom API endpoint for fetching embed HTML.
|
|
431
|
-
* @default '/
|
|
448
|
+
* @default '/_scripts/embed/instagram'
|
|
432
449
|
*/
|
|
433
450
|
apiEndpoint: optional(string())
|
|
434
451
|
});
|
|
@@ -826,12 +843,12 @@ export const XEmbedOptions = object({
|
|
|
826
843
|
tweetId: string(),
|
|
827
844
|
/**
|
|
828
845
|
* Optional: Custom API endpoint for fetching tweet data.
|
|
829
|
-
* @default '/
|
|
846
|
+
* @default '/_scripts/embed/x'
|
|
830
847
|
*/
|
|
831
848
|
apiEndpoint: optional(string()),
|
|
832
849
|
/**
|
|
833
850
|
* Optional: Custom image proxy endpoint.
|
|
834
|
-
* @default '/
|
|
851
|
+
* @default '/_scripts/embed/x-image'
|
|
835
852
|
*/
|
|
836
853
|
imageProxyEndpoint: optional(string())
|
|
837
854
|
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { XEmbedOptions } from "./schemas.js";
|
|
2
2
|
export { XEmbedOptions };
|
|
3
|
-
export function proxyXImageUrl(url, proxyEndpoint = "/
|
|
3
|
+
export function proxyXImageUrl(url, proxyEndpoint = "/_scripts/embed/x-image") {
|
|
4
4
|
const separator = proxyEndpoint.includes("?") ? "&" : "?";
|
|
5
5
|
return `${proxyEndpoint}${separator}url=${encodeURIComponent(url)}`;
|
|
6
6
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<{
|
|
2
|
+
uri: string;
|
|
3
|
+
cid: string;
|
|
4
|
+
author: Record<string, any>;
|
|
5
|
+
record: Record<string, any>;
|
|
6
|
+
embed?: Record<string, any>;
|
|
7
|
+
likeCount: number;
|
|
8
|
+
repostCount: number;
|
|
9
|
+
replyCount: number;
|
|
10
|
+
quoteCount: number;
|
|
11
|
+
indexedAt: string;
|
|
12
|
+
labels: Array<{
|
|
13
|
+
val: string;
|
|
14
|
+
}>;
|
|
15
|
+
}>>;
|
|
16
|
+
export default _default;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { createError, defineEventHandler, getQuery, setHeader } from "h3";
|
|
2
|
+
import { $fetch } from "ofetch";
|
|
3
|
+
const BSKY_POST_URL_RE = /^https:\/\/bsky\.app\/profile\/([^/]+)\/post\/([^/?]+)$/;
|
|
4
|
+
export default defineEventHandler(async (event) => {
|
|
5
|
+
const query = getQuery(event);
|
|
6
|
+
const postUrl = query.url;
|
|
7
|
+
if (!postUrl) {
|
|
8
|
+
throw createError({
|
|
9
|
+
statusCode: 400,
|
|
10
|
+
statusMessage: "Post URL is required"
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
const match = postUrl.match(BSKY_POST_URL_RE);
|
|
14
|
+
if (!match) {
|
|
15
|
+
throw createError({
|
|
16
|
+
statusCode: 400,
|
|
17
|
+
statusMessage: "Invalid Bluesky post URL"
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
const [, actor, rkey] = match;
|
|
21
|
+
let did = actor;
|
|
22
|
+
if (!actor.startsWith("did:")) {
|
|
23
|
+
const profile = await $fetch(
|
|
24
|
+
`https://public.api.bsky.app/xrpc/app.bsky.actor.getProfile?actor=${encodeURIComponent(actor)}`
|
|
25
|
+
).catch((error) => {
|
|
26
|
+
throw createError({
|
|
27
|
+
statusCode: error.statusCode || 500,
|
|
28
|
+
statusMessage: "Failed to resolve Bluesky handle"
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
did = profile.did;
|
|
32
|
+
}
|
|
33
|
+
const uri = `at://${did}/app.bsky.feed.post/${rkey}`;
|
|
34
|
+
const response = await $fetch(
|
|
35
|
+
`https://public.api.bsky.app/xrpc/app.bsky.feed.getPostThread?uri=${encodeURIComponent(uri)}&depth=0&parentHeight=0`
|
|
36
|
+
).catch((error) => {
|
|
37
|
+
throw createError({
|
|
38
|
+
statusCode: error.statusCode || 500,
|
|
39
|
+
statusMessage: error.statusMessage || "Failed to fetch Bluesky post"
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
if (response.thread.$type !== "app.bsky.feed.defs#threadViewPost") {
|
|
43
|
+
throw createError({
|
|
44
|
+
statusCode: 404,
|
|
45
|
+
statusMessage: "Post not found"
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
const post = response.thread.post;
|
|
49
|
+
const hasOptOut = post.labels?.some((l) => l.val === "!no-unauthenticated") || post.author?.labels?.some((l) => l.val === "!no-unauthenticated");
|
|
50
|
+
if (hasOptOut) {
|
|
51
|
+
throw createError({
|
|
52
|
+
statusCode: 403,
|
|
53
|
+
statusMessage: "Author has opted out of external embedding"
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
setHeader(event, "Content-Type", "application/json");
|
|
57
|
+
setHeader(event, "Cache-Control", "public, max-age=600, s-maxage=600");
|
|
58
|
+
return post;
|
|
59
|
+
});
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { useRuntimeConfig } from "#imports";
|
|
2
|
+
import { createError, defineEventHandler, getQuery, setHeader } from "h3";
|
|
3
|
+
import { $fetch } from "ofetch";
|
|
4
|
+
import { withQuery } from "ufo";
|
|
5
|
+
export default defineEventHandler(async (event) => {
|
|
6
|
+
const runtimeConfig = useRuntimeConfig();
|
|
7
|
+
const privateConfig = runtimeConfig["nuxt-scripts"]?.googleMapsGeocodeProxy;
|
|
8
|
+
const apiKey = privateConfig?.apiKey;
|
|
9
|
+
if (!apiKey) {
|
|
10
|
+
throw createError({
|
|
11
|
+
statusCode: 500,
|
|
12
|
+
statusMessage: "Google Maps API key not configured for geocode proxy"
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
const query = getQuery(event);
|
|
16
|
+
const { key: _clientKey, ...safeQuery } = query;
|
|
17
|
+
const geocodeUrl = withQuery("https://maps.googleapis.com/maps/api/geocode/json", {
|
|
18
|
+
...safeQuery,
|
|
19
|
+
key: apiKey
|
|
20
|
+
});
|
|
21
|
+
const data = await $fetch(geocodeUrl, {
|
|
22
|
+
headers: {
|
|
23
|
+
"User-Agent": "Nuxt Scripts Google Geocode Proxy"
|
|
24
|
+
}
|
|
25
|
+
}).catch((error) => {
|
|
26
|
+
throw createError({
|
|
27
|
+
statusCode: error.statusCode || 500,
|
|
28
|
+
statusMessage: error.statusMessage || "Failed to geocode"
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
setHeader(event, "Content-Type", "application/json");
|
|
32
|
+
setHeader(event, "Cache-Control", "public, max-age=86400, s-maxage=86400");
|
|
33
|
+
return data;
|
|
34
|
+
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useRuntimeConfig } from "#imports";
|
|
2
|
-
import { createError, defineEventHandler,
|
|
2
|
+
import { createError, defineEventHandler, getQuery, setHeader } from "h3";
|
|
3
3
|
import { $fetch } from "ofetch";
|
|
4
4
|
import { withQuery } from "ufo";
|
|
5
5
|
export default defineEventHandler(async (event) => {
|
|
@@ -19,17 +19,6 @@ export default defineEventHandler(async (event) => {
|
|
|
19
19
|
statusMessage: "Google Maps API key not configured for proxy"
|
|
20
20
|
});
|
|
21
21
|
}
|
|
22
|
-
const referer = getHeader(event, "referer");
|
|
23
|
-
const host = getHeader(event, "host");
|
|
24
|
-
if (referer && host) {
|
|
25
|
-
const refererUrl = new URL(referer).host;
|
|
26
|
-
if (refererUrl !== host) {
|
|
27
|
-
throw createError({
|
|
28
|
-
statusCode: 403,
|
|
29
|
-
statusMessage: "Invalid referer"
|
|
30
|
-
});
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
22
|
const query = getQuery(event);
|
|
34
23
|
const { key: _clientKey, ...safeQuery } = query;
|
|
35
24
|
const googleMapsUrl = withQuery("https://maps.googleapis.com/maps/api/staticmap", {
|
|
@@ -1,25 +1,10 @@
|
|
|
1
1
|
import { useRuntimeConfig } from "#imports";
|
|
2
|
-
import { createError, defineEventHandler,
|
|
2
|
+
import { createError, defineEventHandler, getQuery, setHeader } from "h3";
|
|
3
3
|
import { $fetch } from "ofetch";
|
|
4
4
|
import { withQuery } from "ufo";
|
|
5
5
|
export default defineEventHandler(async (event) => {
|
|
6
6
|
const runtimeConfig = useRuntimeConfig();
|
|
7
7
|
const proxyConfig = runtimeConfig.public["nuxt-scripts"]?.gravatarProxy;
|
|
8
|
-
const referer = getHeader(event, "referer");
|
|
9
|
-
const host = getHeader(event, "host");
|
|
10
|
-
if (referer && host) {
|
|
11
|
-
let refererHost;
|
|
12
|
-
try {
|
|
13
|
-
refererHost = new URL(referer).host;
|
|
14
|
-
} catch {
|
|
15
|
-
}
|
|
16
|
-
if (refererHost && refererHost !== host) {
|
|
17
|
-
throw createError({
|
|
18
|
-
statusCode: 403,
|
|
19
|
-
statusMessage: "Invalid referer"
|
|
20
|
-
});
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
8
|
const query = getQuery(event);
|
|
24
9
|
let hash = query.hash;
|
|
25
10
|
const email = query.email;
|
|
@@ -1,43 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
statusCode: 400,
|
|
10
|
-
statusMessage: "Asset URL is required"
|
|
11
|
-
});
|
|
12
|
-
}
|
|
13
|
-
let parsedUrl;
|
|
14
|
-
try {
|
|
15
|
-
parsedUrl = new URL(url);
|
|
16
|
-
} catch {
|
|
17
|
-
throw createError({
|
|
18
|
-
statusCode: 400,
|
|
19
|
-
statusMessage: "Invalid asset URL"
|
|
20
|
-
});
|
|
21
|
-
}
|
|
22
|
-
if (parsedUrl.hostname !== "static.cdninstagram.com") {
|
|
23
|
-
throw createError({
|
|
24
|
-
statusCode: 403,
|
|
25
|
-
statusMessage: "Domain not allowed"
|
|
26
|
-
});
|
|
27
|
-
}
|
|
28
|
-
const response = await $fetch.raw(url, {
|
|
29
|
-
headers: {
|
|
30
|
-
"Accept": "*/*",
|
|
31
|
-
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36"
|
|
32
|
-
}
|
|
33
|
-
}).catch((error) => {
|
|
34
|
-
throw createError({
|
|
35
|
-
statusCode: error.statusCode || 500,
|
|
36
|
-
statusMessage: error.statusMessage || "Failed to fetch asset"
|
|
37
|
-
});
|
|
38
|
-
});
|
|
39
|
-
const contentType = response.headers.get("content-type") || "application/octet-stream";
|
|
40
|
-
setHeader(event, "Content-Type", contentType);
|
|
41
|
-
setHeader(event, "Cache-Control", "public, max-age=86400, s-maxage=86400");
|
|
42
|
-
return response._data;
|
|
1
|
+
import { createImageProxyHandler } from "./utils/image-proxy.js";
|
|
2
|
+
export default createImageProxyHandler({
|
|
3
|
+
allowedDomains: ["static.cdninstagram.com"],
|
|
4
|
+
accept: "*/*",
|
|
5
|
+
userAgent: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36",
|
|
6
|
+
cacheMaxAge: 86400,
|
|
7
|
+
contentType: "application/octet-stream",
|
|
8
|
+
decodeAmpersands: true
|
|
43
9
|
});
|
|
@@ -1,55 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
if (!url) {
|
|
8
|
-
throw createError({
|
|
9
|
-
statusCode: 400,
|
|
10
|
-
statusMessage: "Image URL is required"
|
|
11
|
-
});
|
|
12
|
-
}
|
|
13
|
-
let parsedUrl;
|
|
14
|
-
try {
|
|
15
|
-
parsedUrl = new URL(url);
|
|
16
|
-
} catch {
|
|
17
|
-
throw createError({
|
|
18
|
-
statusCode: 400,
|
|
19
|
-
statusMessage: "Invalid image URL"
|
|
20
|
-
});
|
|
21
|
-
}
|
|
22
|
-
if (parsedUrl.protocol !== "http:" && parsedUrl.protocol !== "https:") {
|
|
23
|
-
throw createError({
|
|
24
|
-
statusCode: 400,
|
|
25
|
-
statusMessage: "Invalid URL scheme"
|
|
26
|
-
});
|
|
27
|
-
}
|
|
28
|
-
if (!parsedUrl.hostname.endsWith(".cdninstagram.com") && parsedUrl.hostname !== "scontent.cdninstagram.com") {
|
|
29
|
-
throw createError({
|
|
30
|
-
statusCode: 403,
|
|
31
|
-
statusMessage: "Domain not allowed"
|
|
32
|
-
});
|
|
33
|
-
}
|
|
34
|
-
const response = await $fetch.raw(url, {
|
|
35
|
-
redirect: "manual",
|
|
36
|
-
headers: {
|
|
37
|
-
"Accept": "image/webp,image/jpeg,image/png,image/*,*/*;q=0.8",
|
|
38
|
-
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36"
|
|
39
|
-
}
|
|
40
|
-
}).catch((error) => {
|
|
41
|
-
throw createError({
|
|
42
|
-
statusCode: error.statusCode || 500,
|
|
43
|
-
statusMessage: error.statusMessage || "Failed to fetch image"
|
|
44
|
-
});
|
|
45
|
-
});
|
|
46
|
-
if (response.status >= 300 && response.status < 400) {
|
|
47
|
-
throw createError({
|
|
48
|
-
statusCode: 403,
|
|
49
|
-
statusMessage: "Redirects not allowed"
|
|
50
|
-
});
|
|
51
|
-
}
|
|
52
|
-
setHeader(event, "Content-Type", response.headers.get("content-type") || "image/jpeg");
|
|
53
|
-
setHeader(event, "Cache-Control", "public, max-age=3600, s-maxage=3600");
|
|
54
|
-
return response._data;
|
|
1
|
+
import { createImageProxyHandler } from "./utils/image-proxy.js";
|
|
2
|
+
export default createImageProxyHandler({
|
|
3
|
+
allowedDomains: (hostname) => hostname.endsWith(".cdninstagram.com") || hostname === "scontent.cdninstagram.com",
|
|
4
|
+
userAgent: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36",
|
|
5
|
+
followRedirects: false,
|
|
6
|
+
decodeAmpersands: true
|
|
55
7
|
});
|
|
@@ -1,2 +1,18 @@
|
|
|
1
|
+
export declare const RSRC_RE: RegExp;
|
|
2
|
+
export declare const AMP_RE: RegExp;
|
|
3
|
+
export declare const SCONTENT_RE: RegExp;
|
|
4
|
+
export declare const STATIC_CDN_RE: RegExp;
|
|
5
|
+
export declare const LOOKASIDE_RE: RegExp;
|
|
6
|
+
export declare const INSTAGRAM_IMAGE_HOSTS: string[];
|
|
7
|
+
export declare const INSTAGRAM_ASSET_HOST = "static.cdninstagram.com";
|
|
8
|
+
export declare function proxyImageUrl(url: string): string;
|
|
9
|
+
export declare function proxyAssetUrl(url: string): string;
|
|
10
|
+
export declare function rewriteUrl(url: string): string;
|
|
11
|
+
export declare function rewriteUrlsInText(text: string): string;
|
|
12
|
+
/**
|
|
13
|
+
* Scope CSS rules under a parent selector and strip global/page-level rules.
|
|
14
|
+
* Removes :root, html, body selectors and @charset/@import at-rules.
|
|
15
|
+
*/
|
|
16
|
+
export declare function scopeCss(css: string, scopeSelector: string): string;
|
|
1
17
|
declare const _default: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<string>>;
|
|
2
18
|
export default _default;
|