@xingwangzhe/stalux 1.51.0 → 1.52.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/.release/cli.mjs +16 -20
- package/package.json +47 -45
- package/src/components/stalux/footer/FooterBadges.astro +2 -2
- package/src/components/stalux/layout/head.astro +1 -1
- package/src/components/stalux/posts/postContent.astro +1 -1
- package/src/index.ts +57 -51
- package/src/internal/cjk-search-index.ts +4 -7
- package/src/internal/components-plugin.ts +1 -3
- package/src/internal/font-slices.ts +36 -29
- package/src/internal/html-images.ts +1 -1
- package/src/internal/html-styles.ts +1 -1
- package/src/internal/image-dimensions.ts +3 -8
- package/src/internal/injected-routes.ts +1 -3
- package/src/internal/page-output-cache.ts +11 -15
- package/src/internal/post-cover.ts +22 -26
- package/src/internal/project-mode.ts +3 -2
- package/src/internal/public-assets.ts +42 -23
- package/src/internal/runtime-cache-key.ts +42 -27
- package/src/internal/satteri-config.ts +2 -2
- package/src/internal/search-style.ts +6 -7
- package/src/internal/social-card-cover.ts +12 -57
- package/src/internal/social-card-data.ts +6 -4
- package/src/internal/social-card-layout.ts +69 -63
- package/src/internal/social-card-renderer.ts +48 -140
- package/src/internal/webmcp-mutations.ts +11 -5
- package/src/layouts/Stalux.astro +4 -4
- package/src/pages/openapi.json.ts +1 -1
- package/src/plugins/feature-flags.ts +2 -2
- package/src/schemas/collections.ts +1 -1
- package/src/schemas/config.ts +4 -1
- package/src/scripts/list-reveal.ts +7 -5
- package/src/scripts/swup-router.ts +1 -1
- package/src/scripts/weather.ts +2 -2
- package/src/scripts/webmcp.ts +4 -4
- package/src/utils/accent-color.ts +1 -1
- package/src/utils/ai-discovery.ts +6 -6
- package/src/utils/badge-generator.ts +1 -2
- package/src/utils/cc.ts +2 -2
- package/src/utils/dayjs.ts +5 -5
- package/src/utils/diagnostics.ts +3 -3
- package/src/utils/public-routes.ts +3 -3
- package/src/utils/seo-document.ts +2 -2
- package/src/utils/site-path.ts +1 -1
- package/src/utils/social-image.ts +6 -14
- package/src/internal/sharp-loader.ts +0 -45
|
@@ -274,9 +274,11 @@ declare global {
|
|
|
274
274
|
__staluxListRevealPrime?: boolean;
|
|
275
275
|
}
|
|
276
276
|
}
|
|
277
|
-
if (
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
277
|
+
if (
|
|
278
|
+
typeof document !== "undefined" &&
|
|
279
|
+
typeof window !== "undefined" &&
|
|
280
|
+
!window.__staluxListRevealPrime
|
|
281
|
+
) {
|
|
282
|
+
window.__staluxListRevealPrime = true;
|
|
283
|
+
document.addEventListener("astro:after-swap", primeListReveal);
|
|
282
284
|
}
|
|
@@ -111,7 +111,7 @@ export function installSwupRouter(): Swup {
|
|
|
111
111
|
document.querySelector("base[target]")?.getAttribute("target");
|
|
112
112
|
return (
|
|
113
113
|
getRouteKind(url.pathname) === "other" ||
|
|
114
|
-
/\.(?:md|json|xml|txt|html?|pdf|png|webp|avif|svg|jpe?g)$/
|
|
114
|
+
/\.(?:md|json|xml|txt|html?|pdf|png|webp|avif|svg|jpe?g)$/iu.test(url.pathname) ||
|
|
115
115
|
Boolean(el?.closest("[data-no-swup],[data-astro-reload],[rel~=external]")) ||
|
|
116
116
|
Boolean(target && target !== "_self")
|
|
117
117
|
);
|
package/src/scripts/weather.ts
CHANGED
|
@@ -57,7 +57,7 @@ function parseLocation(value: unknown): ApproximateLocation {
|
|
|
57
57
|
"ip" in value &&
|
|
58
58
|
typeof value.ip === "string" &&
|
|
59
59
|
value.ip.length <= 45 &&
|
|
60
|
-
/^[0-9a-f:.]+$/
|
|
60
|
+
/^[0-9a-f:.]+$/iu.test(value.ip);
|
|
61
61
|
return { city: value.city.trim(), lat, lon, ...(usableIp ? { ip: value.ip as string } : {}) };
|
|
62
62
|
}
|
|
63
63
|
|
|
@@ -97,7 +97,7 @@ function parseWeatherData(value: unknown): WeatherData | undefined {
|
|
|
97
97
|
if (
|
|
98
98
|
typeof value.ip !== "string" ||
|
|
99
99
|
value.ip.length > 45 ||
|
|
100
|
-
!/^[0-9a-f:.]+$/
|
|
100
|
+
!/^[0-9a-f:.]+$/iu.test(value.ip)
|
|
101
101
|
)
|
|
102
102
|
return undefined;
|
|
103
103
|
ip = value.ip;
|
package/src/scripts/webmcp.ts
CHANGED
|
@@ -17,8 +17,8 @@
|
|
|
17
17
|
*/
|
|
18
18
|
// 浏览器无原生 document.modelContext 时由 polyfill 兜底安装;
|
|
19
19
|
// 有原生实现或已存在 WebMCP-aware 扩展时,polyfill 检测到后自动 no-op。
|
|
20
|
-
// ESM entry exports
|
|
21
|
-
import {
|
|
20
|
+
// ESM entry exports installWebMCP; importing it alone does not install the API.
|
|
21
|
+
import { installWebMCP } from "@mcp-b/webmcp-polyfill";
|
|
22
22
|
import { createPostLookup } from "../utils/post-lookup";
|
|
23
23
|
import { sitePath } from "../utils/site-path";
|
|
24
24
|
import { createClientLogger } from "./logger";
|
|
@@ -211,7 +211,7 @@ function postIdFromUrl(value: string): string | null {
|
|
|
211
211
|
const prefix = sitePath("posts/");
|
|
212
212
|
if (!url.pathname.startsWith(prefix)) return null;
|
|
213
213
|
const suffix = url.pathname.slice(prefix.length);
|
|
214
|
-
const path = suffix.endsWith("/") ? suffix.slice(0, -1) : suffix.replace(/\.md
|
|
214
|
+
const path = suffix.endsWith("/") ? suffix.slice(0, -1) : suffix.replace(/\.md$/u, "");
|
|
215
215
|
if (!path || path.includes("/")) return null;
|
|
216
216
|
return decodeURIComponent(path);
|
|
217
217
|
} catch {
|
|
@@ -841,7 +841,7 @@ async function registerTools(
|
|
|
841
841
|
registerPageLifecycle("webmcp", () => {
|
|
842
842
|
// Astro can hoist processed scripts from conditional template branches.
|
|
843
843
|
if (document.body?.dataset.staluxWebmcpEnabled === "false") return;
|
|
844
|
-
|
|
844
|
+
installWebMCP();
|
|
845
845
|
const controller = new AbortController();
|
|
846
846
|
void registerTools(controller.signal)
|
|
847
847
|
.then((r) => {
|
|
@@ -61,22 +61,22 @@ function getSiteConfig(config: ConfigMap): Record<string, unknown> {
|
|
|
61
61
|
export function getSite(config: ConfigMap, contextSite?: string): string {
|
|
62
62
|
const siteData = getSiteConfig(config);
|
|
63
63
|
const configUrl = siteData.url as string | undefined;
|
|
64
|
-
return (configUrl || contextSite || "").replace(
|
|
64
|
+
return (configUrl || contextSite || "").replace(/\/$/u, "");
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
/** 将任意空白压缩为单行 */
|
|
68
68
|
function inlineText(text: string | undefined): string {
|
|
69
|
-
return (text || "").replace(/\s+/
|
|
69
|
+
return (text || "").replace(/\s+/gu, " ").trim();
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
/** 转义 markdown 文本中的特殊字符(用于描述) */
|
|
73
73
|
function escapeMd(text: string | undefined): string {
|
|
74
|
-
return inlineText(text).replace(/([\\[*_`#])/
|
|
74
|
+
return inlineText(text).replace(/([\\[*_`#])/gu, "\\$1");
|
|
75
75
|
}
|
|
76
76
|
|
|
77
77
|
/** 转义 markdown 链接标题中的特殊字符 */
|
|
78
78
|
function escapeMdLinkTitle(text: string | undefined): string {
|
|
79
|
-
return inlineText(text).replace(/([\\[\]])/
|
|
79
|
+
return inlineText(text).replace(/([\\[\]])/gu, "\\$1");
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
/** 构建 markdown 列表项 */
|
|
@@ -125,7 +125,7 @@ function getMediaLinks(
|
|
|
125
125
|
try {
|
|
126
126
|
const hostname = new URL(url.startsWith("http") ? url : `https://example.com${url}`)
|
|
127
127
|
.hostname;
|
|
128
|
-
const domain = hostname.replace(/^www
|
|
128
|
+
const domain = hostname.replace(/^www\./u, "").split(".")[0];
|
|
129
129
|
name = domain ? domain.charAt(0).toUpperCase() + domain.slice(1) : "Link";
|
|
130
130
|
} catch {
|
|
131
131
|
logDetail(
|
|
@@ -147,7 +147,7 @@ function getEmail(config: ConfigMap): string | undefined {
|
|
|
147
147
|
| undefined;
|
|
148
148
|
if (!items) return undefined;
|
|
149
149
|
const mail = items.find((l) => l.link?.startsWith("mailto:"))?.link;
|
|
150
|
-
if (mail) return mail.replace(/^mailto
|
|
150
|
+
if (mail) return mail.replace(/^mailto:/u, "");
|
|
151
151
|
return undefined;
|
|
152
152
|
}
|
|
153
153
|
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { createHash } from "node:crypto";
|
|
2
1
|
import type { AstroRuntimeLogger } from "astro";
|
|
3
2
|
/**
|
|
4
3
|
* 徽章生成器工具
|
|
@@ -59,7 +58,7 @@ export function generateBadge(options: BadgeOptions, logger?: AstroRuntimeLogger
|
|
|
59
58
|
|
|
60
59
|
/** Content-addressed assets are shared by all pages and remain lazy inside details. */
|
|
61
60
|
export function badgeAssetId(svg: string): string {
|
|
62
|
-
return
|
|
61
|
+
return new Bun.CryptoHasher("sha256").update(svg).digest("hex").slice(0, 24);
|
|
63
62
|
}
|
|
64
63
|
|
|
65
64
|
export function normalizeBadgeStyle(style: string | undefined): BadgeOptions["style"] {
|
package/src/utils/cc.ts
CHANGED
|
@@ -11,7 +11,7 @@ export function buildCCLink(licenseCode: string): string {
|
|
|
11
11
|
return "https://creativecommons.org/publicdomain/zero/1.0/";
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
const match = licenseCode.match(/^CC-?(.+)-(\d+(?:\.\d+))$/);
|
|
14
|
+
const match = licenseCode.match(/^CC-?(.+)-(\d+(?:\.\d+))$/u);
|
|
15
15
|
if (!match) {
|
|
16
16
|
return "https://creativecommons.org/licenses/by-nc-sa/4.0/";
|
|
17
17
|
}
|
|
@@ -36,7 +36,7 @@ export function buildCCName(licenseCode: string, t: Translator["t"]): string {
|
|
|
36
36
|
return t("cc.zero");
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
const match = licenseCode.match(/^CC-?(.+)-(\d+(?:\.\d+))$/);
|
|
39
|
+
const match = licenseCode.match(/^CC-?(.+)-(\d+(?:\.\d+))$/u);
|
|
40
40
|
if (!match) {
|
|
41
41
|
// 无法解析时回退为默认 CC-BY-NC-SA-4.0
|
|
42
42
|
const elements = ["BY", "NC", "SA"].map((code) => t(`cc.${code}`)).join("-");
|
package/src/utils/dayjs.ts
CHANGED
|
@@ -33,7 +33,7 @@ export const toTimestamp = (v: unknown, timeZone?: string): number =>
|
|
|
33
33
|
* RSS pubDate、页面 datetime 全体平移 8 小时(2026-10-24 事故)。
|
|
34
34
|
*/
|
|
35
35
|
const NAIVE_DATE_PATTERN =
|
|
36
|
-
/^(\d{4})-(\d{2})-(\d{2})(?:[ T](\d{2}):(\d{2})(?::(\d{2})(?:\.(\d{1,3}))?)?)
|
|
36
|
+
/^(\d{4})-(\d{2})-(\d{2})(?:[ T](\d{2}):(\d{2})(?::(\d{2})(?:\.(\d{1,3}))?)?)?$/u;
|
|
37
37
|
|
|
38
38
|
/** 用共享的 formatter 计算目标时区在给定时刻的 UTC 偏移(毫秒,东八区为 +28800000) */
|
|
39
39
|
function zoneOffsetMs(date: Date, timeZone: string): number {
|
|
@@ -128,7 +128,7 @@ export function format(date: Date | string | number, formatStr: string): string
|
|
|
128
128
|
mm: pad2(d.getMinutes()),
|
|
129
129
|
ss: pad2(d.getSeconds()),
|
|
130
130
|
};
|
|
131
|
-
return formatStr.replace(/yyyy|MM|dd|HH|mm|ss/
|
|
131
|
+
return formatStr.replace(/yyyy|MM|dd|HH|mm|ss/gu, (t) => tokens[t] ?? t);
|
|
132
132
|
}
|
|
133
133
|
|
|
134
134
|
/**
|
|
@@ -159,11 +159,11 @@ export function formatInTimeZone(
|
|
|
159
159
|
if (offset === "GMT" || offset === "UTC") {
|
|
160
160
|
offset = "+00:00";
|
|
161
161
|
} else {
|
|
162
|
-
offset = offset.replace(/^GMT
|
|
162
|
+
offset = offset.replace(/^GMT/u, "").replace(/^UTC/u, "");
|
|
163
163
|
if (!(offset.startsWith("+") || offset.startsWith("-"))) {
|
|
164
164
|
offset = `+${offset}`;
|
|
165
165
|
}
|
|
166
|
-
const m = offset.match(/^([+-])(\d{1,2})(?::?(\d{2}))?$/);
|
|
166
|
+
const m = offset.match(/^([+-])(\d{1,2})(?::?(\d{2}))?$/u);
|
|
167
167
|
const sign = m?.[1];
|
|
168
168
|
const hours = m?.[2];
|
|
169
169
|
if (sign && hours) {
|
|
@@ -186,5 +186,5 @@ export function formatInTimeZone(
|
|
|
186
186
|
ss: second,
|
|
187
187
|
XXX: offset,
|
|
188
188
|
};
|
|
189
|
-
return formatStr.replace(/yyyy|MM|dd|HH|mm|ss|XXX/
|
|
189
|
+
return formatStr.replace(/yyyy|MM|dd|HH|mm|ss|XXX/gu, (t) => tokens[t] ?? t);
|
|
190
190
|
}
|
package/src/utils/diagnostics.ts
CHANGED
|
@@ -19,9 +19,9 @@ export function describeError(error: unknown): string {
|
|
|
19
19
|
}
|
|
20
20
|
const text = parts.join("\nCaused by: ");
|
|
21
21
|
return text
|
|
22
|
-
.replace(/(https?:\/\/[^\s?#]+)[?#][^\s)]+/
|
|
23
|
-
.replace(/(Bearer\s+)[^\s]+/
|
|
24
|
-
.replace(/((?:token|password|secret|api[_-]?key)\s*[=:]\s*)[^\s,;]+/
|
|
22
|
+
.replace(/(https?:\/\/[^\s?#]+)[?#][^\s)]+/gu, "$1?[redacted]")
|
|
23
|
+
.replace(/(Bearer\s+)[^\s]+/giu, "$1[redacted]")
|
|
24
|
+
.replace(/((?:token|password|secret|api[_-]?key)\s*[=:]\s*)[^\s,;]+/giu, "$1[redacted]");
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
/** Runtime loggers have no debug method. Detail is opt-in, then sent through official info. */
|
|
@@ -13,7 +13,7 @@ export type RouteKind =
|
|
|
13
13
|
| "other";
|
|
14
14
|
|
|
15
15
|
export function normalizePublicPath(pathname: string): string {
|
|
16
|
-
const path = pathname.split(/[?#]
|
|
16
|
+
const path = pathname.split(/[?#]/u, 1)[0]?.replace(/\/+$/u, "") ?? "";
|
|
17
17
|
return path || "/";
|
|
18
18
|
}
|
|
19
19
|
|
|
@@ -41,12 +41,12 @@ export function getMarkdownPath(pathname: string): string | undefined {
|
|
|
41
41
|
if (path === "/") return "/index.md";
|
|
42
42
|
if (path === "/tags" || path === "/categories") return `${path}/index.md`;
|
|
43
43
|
if (["/about", "/archives", "/links", "/words"].includes(path)) return `${path}.md`;
|
|
44
|
-
if (/^\/(?:posts|tags|categories)\/[^/]
|
|
44
|
+
if (/^\/(?:posts|tags|categories)\/[^/]+$/u.test(path)) return `${path}.md`;
|
|
45
45
|
return undefined;
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
export function toPublicUrl(site: string, pathname: string, markdown = false): string {
|
|
49
|
-
const base = site.replace(
|
|
49
|
+
const base = site.replace(/\/+$/u, "");
|
|
50
50
|
const normalized = normalizePublicPath(pathname);
|
|
51
51
|
const path = markdown ? getMarkdownPath(normalized) : normalized;
|
|
52
52
|
if (!path) return `${base}${normalized === "/" ? "/" : `${normalized}/`}`;
|
|
@@ -46,7 +46,7 @@ export interface SeoDocument {
|
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
function normalizeSiteUrl(value: string): string {
|
|
49
|
-
return new URL(value).href.replace(
|
|
49
|
+
return new URL(value).href.replace(/\/+$/u, "");
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
function absoluteUrl(value: string, base: string, protocol: string): string {
|
|
@@ -155,7 +155,7 @@ export function buildSeoDocument(input: SeoDocumentInput): SeoDocument {
|
|
|
155
155
|
]),
|
|
156
156
|
];
|
|
157
157
|
|
|
158
|
-
const sameAs = input.mediaLinks.filter((link) => /^https?:\/\//
|
|
158
|
+
const sameAs = input.mediaLinks.filter((link) => /^https?:\/\//iu.test(link));
|
|
159
159
|
const authorImage = absoluteUrl(author.avatar, siteUrl, input.protocol);
|
|
160
160
|
const authorEntity = {
|
|
161
161
|
"@type": "Person",
|
package/src/utils/site-path.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
/** Resolve a known static resource under Astro's configured deployment base. */
|
|
2
2
|
export function sitePath(pathname: string, base = import.meta.env.BASE_URL): string {
|
|
3
|
-
return `${(base || "/").replace(
|
|
3
|
+
return `${(base || "/").replace(/\/+$/u, "")}/${pathname.replace(/^\/+/u, "")}`;
|
|
4
4
|
}
|
|
@@ -1,13 +1,10 @@
|
|
|
1
|
-
import { createHash } from "node:crypto";
|
|
2
|
-
import { loadSharp } from "../internal/sharp-loader";
|
|
3
|
-
|
|
4
1
|
export interface SocialCardData {
|
|
5
2
|
title: string;
|
|
6
|
-
description: string;
|
|
7
3
|
siteTitle: string;
|
|
8
4
|
author: string;
|
|
9
5
|
host: string;
|
|
10
|
-
|
|
6
|
+
postUrl: string;
|
|
7
|
+
tags: string[];
|
|
11
8
|
date: string;
|
|
12
9
|
category: string;
|
|
13
10
|
cover?: string;
|
|
@@ -37,20 +34,15 @@ export function isSocialImageUrl(value: string): boolean {
|
|
|
37
34
|
}
|
|
38
35
|
}
|
|
39
36
|
|
|
40
|
-
/**
|
|
41
|
-
* Cards with artwork are photographic, so JPEG saves most of the bytes and is
|
|
42
|
-
* accepted by every crawler. Plain cards stay PNG: flat type compresses far
|
|
43
|
-
* better there. Without `sharp` neither palette PNG nor JPEG is available, so
|
|
44
|
-
* the card falls back to a plain PNG.
|
|
45
|
-
*/
|
|
37
|
+
/** Artwork cards are photographic, so JPEG saves bytes; plain cards stay PNG. */
|
|
46
38
|
export function socialCardFormat(data: Pick<SocialCardData, "cover">): SocialCardFormat {
|
|
47
|
-
return data.cover
|
|
39
|
+
return data.cover ? "jpg" : "png";
|
|
48
40
|
}
|
|
49
41
|
|
|
50
42
|
export function socialCardPath(data: SocialCardData, base = "/"): string {
|
|
51
43
|
// Bump the template revision when layout changes. Metadata changes get a fresh URL.
|
|
52
|
-
const hash =
|
|
53
|
-
.update(JSON.stringify(["stalux-mono-
|
|
44
|
+
const hash = new Bun.CryptoHasher("sha256")
|
|
45
|
+
.update(JSON.stringify(["stalux-mono-v2", data]))
|
|
54
46
|
.digest("hex")
|
|
55
47
|
.slice(0, 20);
|
|
56
48
|
return `${base.replace(/\/$/u, "")}/og/${hash}.${socialCardFormat(data)}`;
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import { createRequire } from "node:module";
|
|
2
|
-
|
|
3
|
-
export interface SharpRawBuffer {
|
|
4
|
-
data: Buffer;
|
|
5
|
-
info: { width: number; height: number; channels: number };
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
export interface SharpPipeline {
|
|
9
|
-
rotate: () => SharpPipeline;
|
|
10
|
-
resize: (options: Record<string, unknown>) => SharpPipeline;
|
|
11
|
-
flatten: (options: Record<string, unknown>) => SharpPipeline;
|
|
12
|
-
removeAlpha: () => SharpPipeline;
|
|
13
|
-
composite: (images: Record<string, unknown>[]) => SharpPipeline;
|
|
14
|
-
greyscale: () => SharpPipeline;
|
|
15
|
-
toColourspace: (colourspace: string) => SharpPipeline;
|
|
16
|
-
raw: () => SharpPipeline;
|
|
17
|
-
jpeg: (options: Record<string, unknown>) => SharpPipeline;
|
|
18
|
-
png: (options: Record<string, unknown>) => SharpPipeline;
|
|
19
|
-
toBuffer(): Promise<Buffer>;
|
|
20
|
-
toBuffer(options: { resolveWithObject: true }): Promise<SharpRawBuffer>;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export type SharpFactory = (input: Buffer) => SharpPipeline;
|
|
24
|
-
|
|
25
|
-
let cached: { factory: SharpFactory | undefined } | undefined;
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* `sharp` is optional: it ships with Astro and is a dev dependency of the theme,
|
|
29
|
-
* but a project using another image service must still be able to build. It is
|
|
30
|
-
* loaded with `require` because the Vite module runner may already be closed and
|
|
31
|
-
* because a bare dynamic import would make the bundler resolve it eagerly.
|
|
32
|
-
*/
|
|
33
|
-
export function loadSharp(): SharpFactory | undefined {
|
|
34
|
-
if (cached) return cached.factory;
|
|
35
|
-
let factory: SharpFactory | undefined;
|
|
36
|
-
try {
|
|
37
|
-
const loaded = createRequire(import.meta.url)("sharp") as { default?: unknown };
|
|
38
|
-
const candidate = (loaded.default ?? loaded) as unknown;
|
|
39
|
-
if (typeof candidate === "function") factory = candidate as SharpFactory;
|
|
40
|
-
} catch {
|
|
41
|
-
factory = undefined;
|
|
42
|
-
}
|
|
43
|
-
cached = { factory };
|
|
44
|
-
return factory;
|
|
45
|
-
}
|