import { isWindowsAbsolutePath } from "@t3tools/shared/path"; import { normalizeMarkdownLinkDestination, parseFileUrlHref, safeDecodeURIComponent, splitMarkdownLinkSearchAndHash, stripSlashPrefixedWindowsDrive,} from "./markdownLinks.ts"; const DIRECT_IMAGE_SOURCE_PATTERN = /^(?:https?:|data:|blob:|\/\/)/i;const URI_SCHEME_PATTERN = /^[A-Za-z][A-Za-z0-9+.-]*:/; export type MarkdownImageSource = | { readonly _tag: "Direct"; readonly uri: string } | { readonly _tag: "WorkspaceFile"; readonly path: string } | { readonly _tag: "Blocked" }; export function markdownImageSourceFragment(source: string): string { return splitMarkdownLinkSearchAndHash(normalizeMarkdownLinkDestination(source)).hash;} function joinWorkspacePath(workspaceRoot: string, relativePath: string): string { const separator = isWindowsAbsolutePath(workspaceRoot) ? "\\" : "/"; const root = workspaceRoot.replace(/[\\/]+$/, ""); const path = relativePath.replace(/[\\/]/g, separator).replace(/^[\\/]+/, ""); return `${root}${separator}${path}`;} /** * Classifies a markdown image or video source by where its bytes must be loaded from. * Filesystem paths belong to the environment host and must never reach a * browser or native image component without first becoming a signed asset URL. */export function classifyMarkdownImageSource( value: string | null | undefined, workspaceRoot?: string | null,): MarkdownImageSource { if (value === null || value === undefined) return { _tag: "Blocked" }; const source = normalizeMarkdownLinkDestination(value); if (source.length === 0 || source.startsWith("#") || source.startsWith("?")) { return { _tag: "Blocked" }; } if (DIRECT_IMAGE_SOURCE_PATTERN.test(source)) { return { _tag: "Direct", uri: source }; } if (/^file:/i.test(source)) { const target = parseFileUrlHref(source); return target === null ? { _tag: "Blocked" } : { _tag: "WorkspaceFile", path: stripSlashPrefixedWindowsDrive(safeDecodeURIComponent(target.path)), }; } const path = stripSlashPrefixedWindowsDrive( safeDecodeURIComponent(splitMarkdownLinkSearchAndHash(source).path), ); if (path.length === 0) return { _tag: "Blocked" }; if (path.startsWith("/") || isWindowsAbsolutePath(path)) { return { _tag: "WorkspaceFile", path }; } if (URI_SCHEME_PATTERN.test(path) || path.startsWith("~/") || path.startsWith("~\\")) { return { _tag: "Blocked" }; } if (!workspaceRoot) return { _tag: "Blocked" }; return { _tag: "WorkspaceFile", path: joinWorkspacePath(workspaceRoot, path) };}
import { isWindowsAbsolutePath } from "@t3tools/shared/path";
import {
normalizeMarkdownLinkDestination,
parseFileUrlHref,
safeDecodeURIComponent,
splitMarkdownLinkSearchAndHash,
stripSlashPrefixedWindowsDrive,
} from "./markdownLinks.ts";
const DIRECT_IMAGE_SOURCE_PATTERN = /^(?:https?:|data:|blob:|\/\/)/i;
const URI_SCHEME_PATTERN = /^[A-Za-z][A-Za-z0-9+.-]*:/;
export type MarkdownImageSource =
| { readonly _tag: "Direct"; readonly uri: string }
| { readonly _tag: "WorkspaceFile"; readonly path: string }
| { readonly _tag: "Blocked" };
export function markdownImageSourceFragment(source: string): string {
return splitMarkdownLinkSearchAndHash(normalizeMarkdownLinkDestination(source)).hash;
}
function joinWorkspacePath(workspaceRoot: string, relativePath: string): string {
const separator = isWindowsAbsolutePath(workspaceRoot) ? "\\" : "/";
const root = workspaceRoot.replace(/[\\/]+$/, "");
const path = relativePath.replace(/[\\/]/g, separator).replace(/^[\\/]+/, "");
return `${root}${separator}${path}`;
}
/**
* Classifies a markdown image or video source by where its bytes must be loaded from.
* Filesystem paths belong to the environment host and must never reach a
* browser or native image component without first becoming a signed asset URL.
*/
export function classifyMarkdownImageSource(
value: string | null | undefined,
workspaceRoot?: string | null,
): MarkdownImageSource {
if (value === null || value === undefined) return { _tag: "Blocked" };
const source = normalizeMarkdownLinkDestination(value);
if (source.length === 0 || source.startsWith("#") || source.startsWith("?")) {
return { _tag: "Blocked" };
}
if (DIRECT_IMAGE_SOURCE_PATTERN.test(source)) {
return { _tag: "Direct", uri: source };
}
if (/^file:/i.test(source)) {
const target = parseFileUrlHref(source);
return target === null
? { _tag: "Blocked" }
: {
_tag: "WorkspaceFile",
path: stripSlashPrefixedWindowsDrive(safeDecodeURIComponent(target.path)),
};
}
const path = stripSlashPrefixedWindowsDrive(
safeDecodeURIComponent(splitMarkdownLinkSearchAndHash(source).path),
);
if (path.length === 0) return { _tag: "Blocked" };
if (path.startsWith("/") || isWindowsAbsolutePath(path)) {
return { _tag: "WorkspaceFile", path };
}
if (URI_SCHEME_PATTERN.test(path) || path.startsWith("~/") || path.startsWith("~\\")) {
return { _tag: "Blocked" };
}
if (!workspaceRoot) return { _tag: "Blocked" };
return { _tag: "WorkspaceFile", path: joinWorkspacePath(workspaceRoot, path) };
}