conversion to type script
This commit is contained in:
@@ -1,9 +1,27 @@
|
||||
import { findProvider, loadProviders } from "./providers.js";
|
||||
import { findProvider, loadProviders, type Provider } from "./providers.js";
|
||||
|
||||
const DEFAULT_TIMEOUT_MS = 8000;
|
||||
const DEFAULT_MAX_WIDTH = 500;
|
||||
|
||||
function normalizeMaxWidth(endpoint, maxWidth) {
|
||||
type HttpError = Error & { status?: number };
|
||||
|
||||
type FetchImpl = typeof fetch;
|
||||
|
||||
type FetchOembedOptions = {
|
||||
fetchImpl?: FetchImpl;
|
||||
providers?: Provider[];
|
||||
timeoutMs?: number;
|
||||
maxWidth?: number;
|
||||
maxHeight?: number;
|
||||
};
|
||||
|
||||
function httpError(message: string, status: number): HttpError {
|
||||
const error: HttpError = new Error(message);
|
||||
error.status = status;
|
||||
return error;
|
||||
}
|
||||
|
||||
function normalizeMaxWidth(_endpoint: string, maxWidth?: number) {
|
||||
if (!maxWidth) {
|
||||
return maxWidth;
|
||||
}
|
||||
@@ -11,7 +29,7 @@ function normalizeMaxWidth(endpoint, maxWidth) {
|
||||
return Math.min(maxWidth, DEFAULT_MAX_WIDTH);
|
||||
}
|
||||
|
||||
export function buildOembedUrl(endpoint, targetUrl, maxWidth, maxHeight) {
|
||||
export function buildOembedUrl(endpoint: string, targetUrl: string, maxWidth?: number, maxHeight?: number) {
|
||||
const requestUrl = new URL(endpoint);
|
||||
const normalizedMaxWidth = normalizeMaxWidth(endpoint, maxWidth);
|
||||
requestUrl.searchParams.set("url", targetUrl);
|
||||
@@ -28,36 +46,30 @@ export function buildOembedUrl(endpoint, targetUrl, maxWidth, maxHeight) {
|
||||
return requestUrl;
|
||||
}
|
||||
|
||||
export async function fetchOembed(targetUrl, {
|
||||
export async function fetchOembed(targetUrl: string, {
|
||||
fetchImpl = fetch,
|
||||
providers,
|
||||
timeoutMs = Number(process.env.OEMBED_TIMEOUT_MS || DEFAULT_TIMEOUT_MS),
|
||||
maxWidth,
|
||||
maxHeight,
|
||||
} = {}) {
|
||||
}: FetchOembedOptions = {}) {
|
||||
let parsed;
|
||||
|
||||
try {
|
||||
parsed = new URL(targetUrl);
|
||||
} catch {
|
||||
const error = new Error("The url parameter must be an absolute URL.");
|
||||
error.status = 400;
|
||||
throw error;
|
||||
throw httpError("The url parameter must be an absolute URL.", 400);
|
||||
}
|
||||
|
||||
if (!["http:", "https:"].includes(parsed.protocol)) {
|
||||
const error = new Error("Only http and https URLs are supported.");
|
||||
error.status = 400;
|
||||
throw error;
|
||||
throw httpError("Only http and https URLs are supported.", 400);
|
||||
}
|
||||
|
||||
const availableProviders = providers || await loadProviders({ fetchImpl });
|
||||
const provider = findProvider(targetUrl, availableProviders);
|
||||
|
||||
if (!provider) {
|
||||
const error = new Error("No oEmbed provider matched this URL.");
|
||||
error.status = 404;
|
||||
throw error;
|
||||
throw httpError("No oEmbed provider matched this URL.", 404);
|
||||
}
|
||||
|
||||
const controller = new AbortController();
|
||||
@@ -73,9 +85,7 @@ export async function fetchOembed(targetUrl, {
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const error = new Error(`oEmbed provider returned ${response.status}.`);
|
||||
error.status = response.status;
|
||||
throw error;
|
||||
throw httpError(`oEmbed provider returned ${response.status}.`, response.status);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
@@ -1,22 +1,39 @@
|
||||
const DEFAULT_PROVIDERS_URL = "https://oembed.com/providers.json";
|
||||
const DEFAULT_TTL_MS = 12 * 60 * 60 * 1000;
|
||||
|
||||
let providerCache = {
|
||||
expiresAt: 0,
|
||||
providers: [],
|
||||
export type RawProvider = {
|
||||
provider_name: string;
|
||||
provider_url: string;
|
||||
endpoints?: Array<{
|
||||
schemes?: string[];
|
||||
url: string;
|
||||
}>;
|
||||
};
|
||||
|
||||
export function wildcardToRegExp(pattern) {
|
||||
export type Provider = {
|
||||
providerName: string;
|
||||
providerUrl: string;
|
||||
endpoint: string;
|
||||
scheme: string;
|
||||
regex: RegExp;
|
||||
};
|
||||
|
||||
let providerCache = {
|
||||
expiresAt: 0,
|
||||
providers: [] as Provider[],
|
||||
};
|
||||
|
||||
export function wildcardToRegExp(pattern: string) {
|
||||
const escaped = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
|
||||
return new RegExp(`^${escaped}$`, "i");
|
||||
}
|
||||
|
||||
export function normalizeEndpoint(endpoint) {
|
||||
export function normalizeEndpoint(endpoint: string) {
|
||||
return endpoint.replace("{format}", "json");
|
||||
}
|
||||
|
||||
export function flattenProviders(rawProviders) {
|
||||
const providers = [];
|
||||
export function flattenProviders(rawProviders: RawProvider[]) {
|
||||
const providers: Provider[] = [];
|
||||
|
||||
for (const provider of rawProviders) {
|
||||
for (const endpoint of provider.endpoints || []) {
|
||||
@@ -37,7 +54,7 @@ export function flattenProviders(rawProviders) {
|
||||
return providers;
|
||||
}
|
||||
|
||||
export function findProvider(url, providers) {
|
||||
export function findProvider(url: string, providers: Provider[]) {
|
||||
return providers.find((provider) => provider.regex.test(url));
|
||||
}
|
||||
|
||||
@@ -46,6 +63,11 @@ export async function loadProviders({
|
||||
ttlMs = Number(process.env.PROVIDERS_TTL_MS || DEFAULT_TTL_MS),
|
||||
fetchImpl = fetch,
|
||||
force = false,
|
||||
}: {
|
||||
providersUrl?: string;
|
||||
ttlMs?: number;
|
||||
fetchImpl?: typeof fetch;
|
||||
force?: boolean;
|
||||
} = {}) {
|
||||
const now = Date.now();
|
||||
|
||||
@@ -64,7 +86,7 @@ export async function loadProviders({
|
||||
throw new Error(`Could not load providers: ${response.status} ${response.statusText}`);
|
||||
}
|
||||
|
||||
const rawProviders = await response.json();
|
||||
const rawProviders = await response.json() as RawProvider[];
|
||||
const providers = flattenProviders(rawProviders);
|
||||
|
||||
providerCache = {
|
||||
@@ -1,4 +1,5 @@
|
||||
import { createServer } from "node:http";
|
||||
import { existsSync } from "node:fs";
|
||||
import { readFile } from "node:fs/promises";
|
||||
import { extname, join } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
@@ -9,7 +10,8 @@ import { casparTemplatePage, collagePage, errorPage, graphicPage, homePage } fro
|
||||
|
||||
const __dirname = fileURLToPath(new URL(".", import.meta.url));
|
||||
const rootDir = join(__dirname, "..");
|
||||
const publicDir = join(rootDir, "public");
|
||||
const compiledPublicDir = join(rootDir, "dist", "public");
|
||||
const publicDir = existsSync(compiledPublicDir) ? compiledPublicDir : join(rootDir, "public");
|
||||
|
||||
const port = Number(process.env.PORT || 3000);
|
||||
const host = process.env.HOST || "0.0.0.0";
|
||||
Reference in New Issue
Block a user