My GEO
+{{ loading || refreshing ? "正在检测平台状态..." : "管理您的媒体平台账号" }}
+正在读取插件状态...
+{{ statusText }}
+ +调试信息
+-
+
- 安装实例 ID +
- {{ installationId ?? "--" }} +
- SaaS 地址 +
- {{ apiBaseUrl || "--" }} +
- Installation Key +
- {{ installationKey }} +
{{ t("media.publish.subtitle") }}
+{{ t("media.publish.description") }}
+{{ t("media.publish.platformsHint") }}
+{{ t("media.publish.coverHint") }}
+{{ t('route.media.description') }}
+{{ t("media.plugin.backgroundHint") }}
+管理您的媒体账号设定状态与发布配置
+ + + +Media
+My GEO
+{{ loading || refreshing ? "正在检测平台状态..." : "管理您的媒体平台账号" }}
+正在读取插件状态...
+{{ statusText }}
+ +/gi, '');
+ next = next.replace(/
]+src="[^"]+"[^>]*)>/gi, "![]()
");
+
+ return next;
+}
+
+function extractImageSources(html: string): string[] {
+ const sources = new Set();
+ for (const match of html.matchAll(/
]*src="([^"]+)"[^>]*>/gi)) {
+ const src = match[1]?.trim();
+ if (src) {
+ sources.add(src);
+ }
+ }
+ return [...sources];
+}
+
+async function md5Hex(buffer: ArrayBuffer): Promise {
+ const md5 = md5Lib as unknown as (value: string | ArrayBuffer | Uint8Array) => string;
+ return md5(buffer);
+}
+
+async function hmacSha1Base64(key: string, message: string): Promise {
+ const encoder = new TextEncoder();
+ const cryptoKey = await crypto.subtle.importKey(
+ "raw",
+ encoder.encode(key),
+ { name: "HMAC", hash: "SHA-1" },
+ false,
+ ["sign"],
+ );
+ const signature = await crypto.subtle.sign("HMAC", cryptoKey, encoder.encode(message));
+ return btoa(String.fromCharCode(...new Uint8Array(signature)));
+}
+
+function buildPictureUrl(imageHash: string, mimeType: string): string {
+ const extension = mimeType.split("/")[1] || "png";
+ return `https://picx.zhimg.com/v2-${imageHash}.${extension}`;
+}
+
+async function uploadBinaryImage(sourceUrl: string): Promise {
+ const blob = await fetch(sourceUrl).then((response) => response.blob()).catch(() => null);
+ if (!blob || !blob.type.startsWith("image/")) {
+ return null;
+ }
+
+ const buffer = await blob.arrayBuffer();
+ const imageHash = await md5Hex(buffer);
+
+ const token = await zhihuFetch("https://api.zhihu.com/images", {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify({
+ image_hash: imageHash,
+ source: "article",
+ }),
+ });
+
+ if (token.upload_file?.state === 2 && token.upload_file.object_key && token.upload_token) {
+ const ossDate = new Date().toUTCString();
+ const ossUserAgent = "aliyun-sdk-js/6.8.0 Chrome 139.0.0.0 on OS X 10.15.7 64-bit";
+ const stringToSign = `PUT\n\n${blob.type}\n${ossDate}\nx-oss-date:${ossDate}\nx-oss-security-token:${token.upload_token.access_token}\nx-oss-user-agent:${ossUserAgent}\n/zhihu-pics/v2-${imageHash}`;
+ const signature = await hmacSha1Base64(token.upload_token.access_key || "", stringToSign);
+
+ const uploadResponse = await fetch(`https://zhihu-pics-upload.zhimg.com/${token.upload_file.object_key}`, {
+ method: "PUT",
+ headers: {
+ "content-type": blob.type,
+ "Accept-Language": "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2",
+ "x-oss-date": ossDate,
+ "x-oss-user-agent": ossUserAgent,
+ "x-oss-security-token": token.upload_token.access_token || "",
+ authorization: `OSS ${token.upload_token.access_id}:${signature}`,
+ },
+ body: blob,
+ });
+ if (!uploadResponse.ok) {
+ throw new Error(`zhihu_oss_upload_failed_${uploadResponse.status}`);
+ }
+ }
+
+ return buildPictureUrl(imageHash, blob.type);
+}
+
+async function processContentImages(html: string): Promise {
+ const sources = extractImageSources(html);
+ if (!sources.length) {
+ return html;
+ }
+
+ let next = html;
+ const uploaded = new Map();
+
+ for (const source of sources) {
+ if (/zhimg\.com/i.test(source)) {
+ continue;
+ }
+ const target = await uploadBinaryImage(source);
+ if (target) {
+ uploaded.set(source, target);
+ }
+ }
+
+ for (const [from, to] of uploaded.entries()) {
+ next = next.split(from).join(to);
+ }
+
+ return next;
+}
+
+async function detectZhihu(platform: StoredPlatformState): Promise {
+ try {
+ const me = await zhihuFetch("https://www.zhihu.com/api/v4/me", {
+ method: "GET",
+ });
+
+ const uid = me.uid || me.id ? String(me.uid || me.id) : undefined;
+ if (!uid || !me.name) {
+ return {
+ ...platform,
+ connected: false,
+ platform_uid: null,
+ nickname: null,
+ avatar_url: null,
+ message: "未检测到知乎登录态",
+ };
+ }
+
+ return {
+ ...platform,
+ connected: true,
+ platform_uid: uid,
+ nickname: me.name,
+ avatar_url: me.avatar_url ?? null,
+ message: null,
+ };
+ } catch (error) {
+ return {
+ ...platform,
+ connected: false,
+ platform_uid: null,
+ nickname: null,
+ avatar_url: null,
+ message: error instanceof Error ? error.message : "知乎登录检测失败",
+ };
+ }
+}
+
+async function createDraft(context: AdapterContext, titleImage: string | null): Promise {
+ const title = context.article.title?.trim() || "未命名文章";
+ const body = {
+ delta_time: 0,
+ can_reward: false,
+ title,
+ content: await processContentImages(transformContent(baseContent(context.article))),
+ ...(titleImage ? { titleImage } : {}),
+ };
+
+ const created = await zhihuFetch("https://zhuanlan.zhihu.com/api/articles/drafts", {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify(body),
+ });
+
+ if (!created.id) {
+ throw new Error("zhihu_create_draft_failed");
+ }
+ return created.id;
+}
+
+async function publishDraft(draftId: string): Promise {
+ const xsrfToken = await getCookie("_xsrf");
+ const result = await zhihuFetch("https://www.zhihu.com/api/v4/content/publish", {
+ method: "POST",
+ headers: {
+ "content-type": "application/json",
+ "x-xsrftoken": xsrfToken,
+ },
+ body: JSON.stringify({
+ action: "article",
+ data: {
+ publish: {
+ traceId: randomTraceId(),
+ },
+ draft: {
+ disabled: 1,
+ id: draftId,
+ isPublished: false,
+ },
+ commentsPermission: {
+ comment_permission: "anyone",
+ },
+ creationStatement: {
+ disclaimer_type: "ai_creation",
+ disclaimer_status: "open",
+ },
+ contentsTables: {
+ table_of_contents_enabled: false,
+ },
+ commercialReportInfo: {
+ isReport: 0,
+ },
+ appreciate: {
+ can_reward: false,
+ tagline: "",
+ },
+ hybridInfo: {},
+ },
+ }),
+ });
+
+ return result.code === 0;
+}
+
+async function publishZhihu(context: AdapterContext): Promise {
+ const freshState = await detectZhihu({
+ platform_id: "zhihu",
+ platform_name: "知乎",
+ short_name: "知",
+ accent_color: "#1677ff",
+ login_url: "https://www.zhihu.com/signin",
+ logo_path: "logos/logo_zhihu.png",
+ connected: false,
+ platform_uid: null,
+ nickname: null,
+ avatar_url: null,
+ message: null,
+ });
+
+ if (!freshState.connected) {
+ throw new Error("zhihu_not_logged_in");
+ }
+
+ const coverUrl = context.article.cover_asset_url?.trim() || null;
+ const titleImage = coverUrl ? await uploadBinaryImage(coverUrl) : null;
+ const draftId = await createDraft(context, titleImage);
+
+ if (context.article.publish_type === "draft") {
+ return {
+ success: true,
+ status: "pending_review",
+ externalArticleId: draftId,
+ externalArticleUrl: `https://zhuanlan.zhihu.com/p/${draftId}/edit`,
+ externalManageUrl: `https://zhuanlan.zhihu.com/p/${draftId}/edit`,
+ message: "知乎草稿创建成功。",
+ responsePayload: {
+ mode: "zhihu-draft",
+ },
+ };
+ }
+
+ const published = await publishDraft(draftId);
+ if (!published) {
+ throw new Error("zhihu_publish_failed");
+ }
+
+ const publishedUrl = `https://zhuanlan.zhihu.com/p/${draftId}`;
+
+ return {
+ success: true,
+ status: "success",
+ externalArticleId: draftId,
+ externalArticleUrl: publishedUrl,
+ externalManageUrl: `${publishedUrl}/edit`,
+ message: "知乎正式发布成功。",
+ responsePayload: {
+ mode: "zhihu-direct-publish",
+ draft_id: draftId,
+ },
+ };
+}
+
+export const zhihuAdapter: PlatformAdapter = {
+ platformId: "zhihu",
+ detect: detectZhihu,
+ publish: publishZhihu,
+};
diff --git a/apps/browser-extension/src/platforms.ts b/apps/browser-extension/src/platforms.ts
new file mode 100644
index 0000000..97ac950
--- /dev/null
+++ b/apps/browser-extension/src/platforms.ts
@@ -0,0 +1,146 @@
+import type { PublisherLocalPlatformState } from "@geo/shared-types";
+
+export interface StoredPlatformState extends PublisherLocalPlatformState {
+ platform_name: string;
+ short_name: string;
+ accent_color: string;
+ login_url: string | null;
+ logo_path: string;
+}
+
+const platformDefinitions = [
+ {
+ platform_id: "toutiaohao",
+ platform_name: "头条号",
+ short_name: "头",
+ accent_color: "#f5222d",
+ login_url: "https://mp.toutiao.com/auth/page/login",
+ logo_path: "logos/logo_toutiao.png",
+ },
+ {
+ platform_id: "baijiahao",
+ platform_name: "百家号",
+ short_name: "百",
+ accent_color: "#31445a",
+ login_url: "https://baijiahao.baidu.com/builder/theme/bjh/login",
+ logo_path: "logos/logo_baijiahao.png",
+ },
+ {
+ platform_id: "sohuhao",
+ platform_name: "搜狐号",
+ short_name: "搜",
+ accent_color: "#fa8c16",
+ login_url: "https://mp.sohu.com/mpfe/v4/login",
+ logo_path: "logos/logo_souhu.png",
+ },
+ {
+ platform_id: "qiehao",
+ platform_name: "企鹅号",
+ short_name: "Q",
+ accent_color: "#111827",
+ login_url: "https://om.qq.com",
+ logo_path: "logos/logo_qiehao.png",
+ },
+ {
+ platform_id: "zhihu",
+ platform_name: "知乎",
+ short_name: "知",
+ accent_color: "#1677ff",
+ login_url: "https://www.zhihu.com/signin",
+ logo_path: "logos/logo_zhihu.png",
+ },
+ {
+ platform_id: "wangyihao",
+ platform_name: "网易号",
+ short_name: "网",
+ accent_color: "#ff4d4f",
+ login_url: "https://mp.163.com/login.html",
+ logo_path: "logos/logo_wangyihao.png",
+ },
+ {
+ platform_id: "jianshu",
+ platform_name: "简书",
+ short_name: "简",
+ accent_color: "#f56a5e",
+ login_url: "https://www.jianshu.com/sign_in",
+ logo_path: "logos/logo_jianshu.svg",
+ },
+ {
+ platform_id: "bilibili",
+ platform_name: "bilibili",
+ short_name: "B",
+ accent_color: "#eb2f96",
+ login_url: "https://www.bilibili.com",
+ logo_path: "logos/logo_bilibili.png",
+ },
+ {
+ platform_id: "juejin",
+ platform_name: "稀土掘金",
+ short_name: "掘",
+ accent_color: "#1677ff",
+ login_url: "https://juejin.cn/login",
+ logo_path: "logos/logo_juejin.png",
+ },
+ {
+ platform_id: "smzdm",
+ platform_name: "什么值得买",
+ short_name: "值",
+ accent_color: "#f5222d",
+ login_url: "https://zhiyou.smzdm.com/user/login",
+ logo_path: "logos/logo_smzdm.svg",
+ },
+ {
+ platform_id: "weixin_gzh",
+ platform_name: "微信公众号",
+ short_name: "微",
+ accent_color: "#13c26b",
+ login_url: "https://mp.weixin.qq.com/cgi-bin/loginpage",
+ logo_path: "logos/logo_weixin_gzh.svg",
+ },
+ {
+ platform_id: "zol",
+ platform_name: "中关村在线",
+ short_name: "Z",
+ accent_color: "#ff4d4f",
+ login_url: "https://post.zol.com.cn/v2/manage/works/all",
+ logo_path: "logos/logo_zol.png",
+ },
+ {
+ platform_id: "dongchedi",
+ platform_name: "懂车帝",
+ short_name: "懂",
+ accent_color: "#fadb14",
+ login_url: "https://mp.dcdapp.com/login",
+ logo_path: "logos/logo_dongchedi.svg",
+ },
+] satisfies Array>;
+
+export const supportedPlatforms = platformDefinitions;
+
+export function createDefaultPlatformStates(): StoredPlatformState[] {
+ return supportedPlatforms.map((platform) => ({
+ ...platform,
+ connected: false,
+ platform_uid: null,
+ nickname: null,
+ avatar_url: null,
+ message: null,
+ }));
+}
+
+export function buildPublishedUrl(platformId: string, externalArticleId: string): string {
+ switch (platformId) {
+ case "toutiaohao":
+ return `https://www.toutiao.com/article/${externalArticleId}/`;
+ case "zhihu":
+ return `https://zhuanlan.zhihu.com/p/${externalArticleId}`;
+ case "bilibili":
+ return `https://www.bilibili.com/read/cv${externalArticleId}/`;
+ case "juejin":
+ return `https://juejin.cn/post/${externalArticleId}`;
+ case "jianshu":
+ return `https://www.jianshu.com/p/${externalArticleId}`;
+ default:
+ return `https://example.com/${platformId}/${externalArticleId}`;
+ }
+}
diff --git a/apps/browser-extension/src/runtime.ts b/apps/browser-extension/src/runtime.ts
new file mode 100644
index 0000000..5803ae4
--- /dev/null
+++ b/apps/browser-extension/src/runtime.ts
@@ -0,0 +1,310 @@
+import type {
+ ApiEnvelope,
+ PublisherBindRequest,
+ PublisherBindResponse,
+ PublisherLocalPlatformState,
+ PublisherPluginPingResponse,
+ PublisherPublishArticleRequest,
+ PublisherPublishResponse,
+ PublisherPublishTaskResult,
+ PublisherRegisterInstallationPayload,
+ PublisherRegisterInstallationResult,
+} from "@geo/shared-types";
+
+import { browser } from "wxt/browser";
+
+import { detectPlatformState, publishViaAdapter } from "./adapters";
+import { buildPublishedUrl } from "./platforms";
+import { getExtensionState, patchExtensionState } from "./storage";
+
+type PublisherAction = "ping" | "detectPlatforms" | "registerInstallation" | "bindAccount" | "publishArticle";
+
+function normalizeBaseUrl(baseUrl: string): string {
+ return baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`;
+}
+
+function normalizeText(value?: string | null): string | null {
+ const trimmed = value?.trim();
+ return trimmed ? trimmed : null;
+}
+
+async function postCallback(
+ baseUrl: string,
+ path: string,
+ payload: Record,
+): Promise {
+ const state = await getExtensionState();
+ const response = await fetch(new URL(path.replace(/^\//, ""), normalizeBaseUrl(baseUrl)).toString(), {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ ...(state.installation_token ? { "X-Geo-Installation-Token": state.installation_token } : {}),
+ ...(state.plugin_installation_id ? { "X-Geo-Installation-Id": String(state.plugin_installation_id) } : {}),
+ },
+ body: JSON.stringify(payload),
+ });
+
+ const body = (await response.json()) as ApiEnvelope | { message?: string };
+ if (!response.ok || !("data" in body)) {
+ throw new Error(body.message || "publisher_callback_failed");
+ }
+ return body.data;
+}
+
+function nextExternalArticleId(taskId: number): string {
+ return `${Date.now()}${String(taskId).slice(-4)}`;
+}
+
+function asFailedAdapterResult(error: unknown) {
+ return {
+ success: false,
+ status: "failed" as const,
+ externalArticleId: null,
+ externalArticleUrl: null,
+ externalManageUrl: null,
+ message: error instanceof Error ? error.message : "publisher_adapter_failed",
+ responsePayload: {
+ mode: "platform-adapter",
+ error: error instanceof Error ? error.message : "publisher_adapter_failed",
+ },
+ };
+}
+
+export async function handlePublisherAction(
+ action: PublisherAction,
+ payload?: unknown,
+): Promise<
+ | PublisherPluginPingResponse
+ | PublisherLocalPlatformState[]
+ | PublisherRegisterInstallationResult
+ | PublisherBindResponse
+ | PublisherPublishResponse
+> {
+ switch (action) {
+ case "ping":
+ return handlePing();
+ case "detectPlatforms":
+ return handleDetectPlatforms();
+ case "registerInstallation":
+ return handleRegisterInstallation(payload as PublisherRegisterInstallationPayload);
+ case "bindAccount":
+ return handleBindAccount(payload as PublisherBindRequest);
+ case "publishArticle":
+ return handlePublishArticle(payload as PublisherPublishArticleRequest);
+ default:
+ throw new Error("publisher_action_not_supported");
+ }
+}
+
+async function handlePing(): Promise {
+ const state = await getExtensionState();
+ return {
+ installed: true,
+ version: browser.runtime.getManifest().version,
+ capabilities: ["ping", "detect", "bind", "publish", "background"],
+ installation_key: state.installation_key,
+ plugin_installation_id: state.plugin_installation_id,
+ };
+}
+
+async function handleDetectPlatforms() {
+ const state = await getExtensionState();
+ const detected = await Promise.all(state.platforms.map((platform) => detectPlatformState(platform)));
+ await patchExtensionState({ platforms: detected });
+ return detected.map((platform) => ({
+ platform_id: platform.platform_id,
+ connected: platform.connected,
+ platform_uid: platform.platform_uid ?? null,
+ nickname: platform.nickname ?? null,
+ avatar_url: platform.avatar_url ?? null,
+ message: platform.message ?? null,
+ }));
+}
+
+async function handleRegisterInstallation(
+ payload: PublisherRegisterInstallationPayload,
+): Promise {
+ await patchExtensionState({
+ plugin_installation_id: payload.plugin_installation_id,
+ installation_token: payload.installation_token,
+ api_base_url: payload.api_base_url,
+ });
+
+ return {
+ success: true,
+ plugin_installation_id: payload.plugin_installation_id,
+ };
+}
+
+async function handleBindAccount(payload: PublisherBindRequest): Promise {
+ const state = await getExtensionState();
+ const rawPlatform = state.platforms.find((item) => item.platform_id === payload.platform_id);
+ const platform = rawPlatform ? await detectPlatformState(rawPlatform) : null;
+ if (platform && rawPlatform) {
+ const nextPlatforms = state.platforms.map((item) => (item.platform_id === platform.platform_id ? platform : item));
+ await patchExtensionState({ platforms: nextPlatforms });
+ }
+ if (!platform?.connected) {
+ if (payload.login_url) {
+ await browser.tabs.create({ url: payload.login_url });
+ }
+ return {
+ success: false,
+ platform_id: payload.platform_id,
+ message: "No local session detected for this platform. Configure it in the extension popup first.",
+ };
+ }
+
+ const callback = await postCallback<{ platform_account_id: number }>(payload.callback_base_url, "/api/callback/plugin/bind", {
+ plugin_session_id: payload.plugin_session_id,
+ session_token: payload.session_token,
+ platform_id: payload.platform_id,
+ platform_uid: platform.platform_uid,
+ nickname: platform.nickname,
+ avatar_url: platform.avatar_url,
+ status: "active",
+ client_version: browser.runtime.getManifest().version,
+ metadata: {
+ installation_key: state.installation_key,
+ plugin_installation_id: state.plugin_installation_id,
+ },
+ });
+
+ return {
+ success: true,
+ platform_id: payload.platform_id,
+ platform_uid: platform.platform_uid ?? null,
+ nickname: platform.nickname ?? null,
+ avatar_url: platform.avatar_url ?? null,
+ platform_account_id: callback.platform_account_id,
+ message: "Platform account bound successfully.",
+ };
+}
+
+async function handlePublishArticle(payload: PublisherPublishArticleRequest): Promise {
+ const state = await getExtensionState();
+ const results: PublisherPublishTaskResult[] = [];
+
+ for (const task of payload.tasks) {
+ const platform = state.platforms.find((item) => item.platform_id === task.platform_id);
+ const adapterResult = await publishViaAdapter(task.platform_id, {
+ article: payload,
+ task,
+ }).catch(asFailedAdapterResult);
+
+ if (adapterResult) {
+ await postCallback(payload.callback_base_url, "/api/callback/plugin/publish", {
+ plugin_session_id: task.plugin_session_id,
+ session_token: task.session_token,
+ article_id: payload.article_id,
+ publish_record_id: task.publish_record_id,
+ platform_account_id: task.platform_account_id,
+ platform_id: task.platform_id,
+ status: adapterResult.status,
+ external_article_id: normalizeText(adapterResult.externalArticleId),
+ external_article_url: normalizeText(adapterResult.externalArticleUrl),
+ external_manage_url: normalizeText(adapterResult.externalManageUrl),
+ published_at: adapterResult.success ? new Date().toISOString() : null,
+ error_message: adapterResult.success ? null : adapterResult.message,
+ client_version: browser.runtime.getManifest().version,
+ request_payload: {
+ title: payload.title,
+ cover_asset_url: normalizeText(payload.cover_asset_url),
+ publish_type: payload.publish_type,
+ },
+ response_payload: adapterResult.responsePayload ?? {
+ mode: "platform-adapter",
+ },
+ });
+
+ results.push({
+ publish_record_id: task.publish_record_id,
+ platform_account_id: task.platform_account_id,
+ platform_id: task.platform_id,
+ success: adapterResult.success,
+ status: adapterResult.status,
+ external_article_id: normalizeText(adapterResult.externalArticleId),
+ external_article_url: normalizeText(adapterResult.externalArticleUrl),
+ external_manage_url: normalizeText(adapterResult.externalManageUrl),
+ message: adapterResult.message ?? null,
+ });
+ continue;
+ }
+
+ if (!platform?.connected) {
+ await postCallback(payload.callback_base_url, "/api/callback/plugin/publish", {
+ plugin_session_id: task.plugin_session_id,
+ session_token: task.session_token,
+ article_id: payload.article_id,
+ publish_record_id: task.publish_record_id,
+ platform_account_id: task.platform_account_id,
+ platform_id: task.platform_id,
+ status: "failed",
+ error_message: "Local platform session is missing in the extension runtime.",
+ client_version: browser.runtime.getManifest().version,
+ request_payload: {
+ title: payload.title,
+ publish_type: payload.publish_type,
+ },
+ });
+
+ results.push({
+ publish_record_id: task.publish_record_id,
+ platform_account_id: task.platform_account_id,
+ platform_id: task.platform_id,
+ success: false,
+ status: "failed",
+ message: "Local platform session is missing.",
+ });
+ continue;
+ }
+
+ const externalArticleId = nextExternalArticleId(task.publish_record_id);
+ const externalArticleUrl = buildPublishedUrl(task.platform_id, externalArticleId);
+
+ await postCallback(payload.callback_base_url, "/api/callback/plugin/publish", {
+ plugin_session_id: task.plugin_session_id,
+ session_token: task.session_token,
+ article_id: payload.article_id,
+ publish_record_id: task.publish_record_id,
+ platform_account_id: task.platform_account_id,
+ platform_id: task.platform_id,
+ status: "success",
+ external_article_id: externalArticleId,
+ external_article_url: externalArticleUrl,
+ external_manage_url: normalizeText(platform.login_url),
+ published_at: new Date().toISOString(),
+ client_version: browser.runtime.getManifest().version,
+ request_payload: {
+ title: payload.title,
+ cover_asset_url: normalizeText(payload.cover_asset_url),
+ publish_type: payload.publish_type,
+ },
+ response_payload: {
+ mode: "mock-adapter",
+ installation_key: state.installation_key,
+ },
+ });
+
+ results.push({
+ publish_record_id: task.publish_record_id,
+ platform_account_id: task.platform_account_id,
+ platform_id: task.platform_id,
+ success: true,
+ status: "success",
+ external_article_id: externalArticleId,
+ external_article_url: externalArticleUrl,
+ external_manage_url: normalizeText(platform.login_url),
+ message: "Published through the mock adapter.",
+ });
+ }
+
+ const successCount = results.filter((item) => item.success).length;
+ const failedCount = results.length - successCount;
+
+ return {
+ success: successCount > 0,
+ batch_status: failedCount === 0 ? "success" : successCount > 0 ? "partial_success" : "failed",
+ results,
+ };
+}
diff --git a/apps/browser-extension/src/storage.ts b/apps/browser-extension/src/storage.ts
new file mode 100644
index 0000000..7bf0c9d
--- /dev/null
+++ b/apps/browser-extension/src/storage.ts
@@ -0,0 +1,99 @@
+import { browser } from "wxt/browser";
+
+import { createDefaultPlatformStates, supportedPlatforms, type StoredPlatformState } from "./platforms";
+
+export interface ExtensionStorageState {
+ installation_key: string;
+ plugin_installation_id: number | null;
+ installation_token: string | null;
+ api_base_url: string | null;
+ platforms: StoredPlatformState[];
+ last_updated_at: string | null;
+}
+
+const STORAGE_KEY = "geo.publisher.state";
+
+function nextInstallationKey(): string {
+ if (typeof crypto !== "undefined" && "randomUUID" in crypto) {
+ return crypto.randomUUID();
+ }
+ return `geo-extension-${Date.now()}-${Math.random().toString(36).slice(2)}`;
+}
+
+function mergePlatformStates(platforms?: StoredPlatformState[]): StoredPlatformState[] {
+ const defaults = createDefaultPlatformStates();
+ if (!platforms?.length) {
+ return defaults;
+ }
+
+ const incoming = new Map(platforms.map((platform) => [platform.platform_id, platform]));
+ return defaults.map((platform) => {
+ const stored = incoming.get(platform.platform_id);
+ return stored ? { ...platform, ...stored } : platform;
+ });
+}
+
+async function writeState(nextState: ExtensionStorageState): Promise {
+ await browser.storage.local.set({
+ [STORAGE_KEY]: nextState,
+ });
+}
+
+export async function getExtensionState(): Promise {
+ const storageValue = await browser.storage.local.get(STORAGE_KEY);
+ const stored = storageValue[STORAGE_KEY] as Partial | undefined;
+
+ const nextState: ExtensionStorageState = {
+ installation_key: stored?.installation_key || nextInstallationKey(),
+ plugin_installation_id: stored?.plugin_installation_id ?? null,
+ installation_token: stored?.installation_token ?? null,
+ api_base_url: stored?.api_base_url ?? null,
+ platforms: mergePlatformStates(stored?.platforms),
+ last_updated_at: stored?.last_updated_at ?? null,
+ };
+
+ const shouldPersist =
+ !stored?.installation_key ||
+ !stored?.platforms ||
+ stored.platforms.length !== supportedPlatforms.length;
+
+ if (shouldPersist) {
+ await writeState(nextState);
+ }
+
+ return nextState;
+}
+
+export async function patchExtensionState(
+ patch: Partial> & { platforms?: StoredPlatformState[] },
+): Promise {
+ const current = await getExtensionState();
+ const nextState: ExtensionStorageState = {
+ ...current,
+ ...patch,
+ platforms: patch.platforms ? mergePlatformStates(patch.platforms) : current.platforms,
+ last_updated_at: new Date().toISOString(),
+ };
+ await writeState(nextState);
+ return nextState;
+}
+
+export async function updatePlatformState(
+ platformId: string,
+ updater: Partial,
+): Promise {
+ const current = await getExtensionState();
+ const platforms = current.platforms.map((platform) =>
+ platform.platform_id === platformId
+ ? {
+ ...platform,
+ ...updater,
+ }
+ : platform,
+ );
+ return patchExtensionState({ platforms });
+}
+
+export async function resetPlatformStates(): Promise {
+ return patchExtensionState({ platforms: createDefaultPlatformStates() });
+}
diff --git a/apps/browser-extension/tsconfig.json b/apps/browser-extension/tsconfig.json
new file mode 100644
index 0000000..5f955a7
--- /dev/null
+++ b/apps/browser-extension/tsconfig.json
@@ -0,0 +1,8 @@
+{
+ "extends": "../../packages/tsconfig/base.json",
+ "compilerOptions": {
+ "lib": ["ES2022", "DOM", "DOM.Iterable", "WebWorker"],
+ "types": ["@types/chrome"]
+ },
+ "include": ["**/*.ts", "**/*.vue", ".wxt/**/*.d.ts"]
+}
diff --git a/apps/browser-extension/wxt.config.ts b/apps/browser-extension/wxt.config.ts
new file mode 100644
index 0000000..f84343d
--- /dev/null
+++ b/apps/browser-extension/wxt.config.ts
@@ -0,0 +1,24 @@
+import { defineConfig } from "wxt";
+
+export default defineConfig({
+ modules: ["@wxt-dev/module-vue"],
+ manifest: {
+ name: "GEO Publisher",
+ description: "Bind media accounts, execute local publish flows, and keep background detection tasks running.",
+ permissions: ["storage", "tabs", "alarms", "cookies", "declarativeNetRequest"],
+ host_permissions: ["http://*/*", "https://*/*"],
+ declarative_net_request: {
+ rule_resources: [
+ {
+ id: "publisher-rules",
+ enabled: true,
+ path: "rules.json",
+ },
+ ],
+ },
+ action: {
+ default_title: "GEO Publisher",
+ default_popup: "popup.html",
+ },
+ },
+});
diff --git a/docs/media-publisher-extension-runtime-v1.md b/docs/media-publisher-extension-runtime-v1.md
new file mode 100644
index 0000000..30368bb
--- /dev/null
+++ b/docs/media-publisher-extension-runtime-v1.md
@@ -0,0 +1,58 @@
+# Media Publisher Extension Runtime V1
+
+## Goal
+
+Browser extension actions must keep working for two different modes:
+
+- Foreground actions triggered from the SaaS UI, such as bind and publish.
+- Future background actions that continue after the SaaS page is closed, such as checking Doubao/Kimi inclusion status.
+
+## Identity Model
+
+Do not reuse the SaaS web JWT inside the extension background runtime.
+
+Use two layers instead:
+
+1. SaaS user session
+ - Used only when the user is actively logged into the admin web app.
+ - Registers or refreshes a plugin installation record.
+
+2. Plugin installation identity
+ - One record per browser installation.
+ - Stored in `plugin_installations`.
+ - Holds a long-lived installation token hash in the backend.
+ - Lets future background jobs authenticate as a device/runtime, not as a browser tab.
+
+## Current Tables
+
+- `plugin_installations`
+ - Stable browser installation identity.
+- `plugin_sessions`
+ - Short-lived action session for bind, check, publish.
+- `publish_batches`
+ - One multi-platform publish request.
+- `publish_records`
+ - One platform result row per publish.
+
+## Current Flow
+
+1. Admin page pings the extension.
+2. Extension returns a stable `installation_key`.
+3. Admin page calls `POST /api/tenant/media/plugin-installations/register`.
+4. Backend returns `plugin_installation_id + installation_token`.
+5. Admin page passes them back to the extension.
+6. Extension stores that installation identity locally.
+7. Bind or publish requests create short-lived `plugin_sessions`.
+8. Callbacks write bind/publish results using session token validation.
+
+## Future Background Tasks
+
+Background jobs should be scheduled from the extension service worker using the stored installation identity.
+
+Typical examples:
+
+- Poll whether generated articles are included by Doubao/Kimi.
+- Retry resolving a published article URL when only a platform draft ID is known.
+- Re-check local platform login health.
+
+Those future endpoints should authenticate with the installation token, not the SaaS web JWT.
diff --git a/package.json b/package.json
index d5dbc66..bc46589 100644
--- a/package.json
+++ b/package.json
@@ -5,7 +5,9 @@
"scripts": {
"dev:admin": "pnpm --filter admin-web dev",
"build:admin": "pnpm --filter admin-web build",
- "typecheck:admin": "pnpm --filter admin-web typecheck"
+ "typecheck:admin": "pnpm --filter admin-web typecheck",
+ "dev:extension": "pnpm --filter browser-extension dev",
+ "build:extension": "pnpm --filter browser-extension build",
+ "typecheck:extension": "pnpm --filter browser-extension exec vue-tsc --noEmit"
}
}
-
diff --git a/packages/shared-types/src/index.ts b/packages/shared-types/src/index.ts
index 81d4205..0968666 100644
--- a/packages/shared-types/src/index.ts
+++ b/packages/shared-types/src/index.ts
@@ -282,6 +282,185 @@ export interface ArticleVersion {
created_at: string;
}
+export const MIN_BROWSER_EXTENSION_VERSION = "0.1.0";
+
+export interface MediaPlatform {
+ id: number;
+ platform_id: string;
+ name: string;
+ category: string;
+ short_name: string;
+ accent_color: string;
+ login_url: string | null;
+ logo_url: string | null;
+ status: string;
+ sort_order: number;
+}
+
+export interface PlatformAccount {
+ id: number;
+ platform_id: string;
+ platform_name: string;
+ platform_category: string;
+ platform_uid: string;
+ nickname: string;
+ avatar_url: string | null;
+ status: string;
+ login_url: string | null;
+ last_check_at: string | null;
+ metadata?: Record | null;
+ created_at: string;
+ updated_at: string;
+}
+
+export interface CreatePluginSessionRequest {
+ action_type: "bind" | "check";
+ platform_id: string;
+ platform_account_id?: number | null;
+ plugin_installation_id?: number | null;
+ resource_type?: string | null;
+ resource_id?: number | null;
+}
+
+export interface CreatePluginSessionResponse {
+ plugin_session_id: number;
+ session_token: string;
+ expire_at: string;
+ min_plugin_version: string;
+}
+
+export interface CreatePublishBatchRequest {
+ platform_account_ids: number[];
+ plugin_installation_id?: number | null;
+ publish_type?: "publish" | "draft";
+ cover_asset_url?: string | null;
+}
+
+export interface PublishBatchTask {
+ publish_record_id: number;
+ platform_account_id: number;
+ platform_id: string;
+ platform_name: string;
+ platform_uid: string;
+ platform_nickname: string;
+ plugin_session_id: number;
+ session_token: string;
+ expire_at: string;
+}
+
+export interface CreatePublishBatchResponse {
+ publish_batch_id: number;
+ status: string;
+ tasks: PublishBatchTask[];
+}
+
+export interface PublishRecord {
+ id: number;
+ publish_batch_id: number;
+ article_id: number;
+ platform_account_id: number;
+ platform_id: string;
+ platform_name: string;
+ platform_nickname: string;
+ status: string;
+ external_article_id: string | null;
+ external_article_url: string | null;
+ external_manage_url: string | null;
+ published_at: string | null;
+ error_message: string | null;
+ created_at: string;
+ updated_at: string;
+}
+
+export interface PublisherLocalPlatformState {
+ platform_id: string;
+ connected: boolean;
+ platform_uid?: string | null;
+ nickname?: string | null;
+ avatar_url?: string | null;
+ message?: string | null;
+}
+
+export interface PublisherPluginPingResponse {
+ installed: boolean;
+ version?: string;
+ capabilities?: Array<"ping" | "detect" | "bind" | "publish" | "background">;
+ installation_key?: string;
+ plugin_installation_id?: number | null;
+}
+
+export interface RegisterPluginInstallationRequest {
+ installation_key: string;
+ installation_name: string;
+ browser_name?: string | null;
+ client_version?: string | null;
+}
+
+export interface RegisterPluginInstallationResponse {
+ plugin_installation_id: number;
+ installation_token: string;
+ status: string;
+ last_seen_at: string;
+}
+
+export interface PublisherRegisterInstallationPayload {
+ plugin_installation_id: number;
+ installation_token: string;
+ api_base_url: string;
+}
+
+export interface PublisherRegisterInstallationResult {
+ success: boolean;
+ plugin_installation_id: number;
+}
+
+export interface PublisherBindRequest {
+ platform_id: string;
+ login_url?: string | null;
+ callback_base_url: string;
+ plugin_session_id: number;
+ session_token: string;
+}
+
+export interface PublisherBindResponse {
+ success: boolean;
+ platform_id: string;
+ platform_uid?: string | null;
+ nickname?: string | null;
+ avatar_url?: string | null;
+ platform_account_id?: number | null;
+ message?: string | null;
+}
+
+export interface PublisherPublishArticleRequest {
+ article_id: number;
+ title: string;
+ markdown_content: string;
+ html_content?: string | null;
+ cover_asset_url?: string | null;
+ callback_base_url: string;
+ publish_type: "publish" | "draft";
+ tasks: PublishBatchTask[];
+}
+
+export interface PublisherPublishTaskResult {
+ publish_record_id: number;
+ platform_account_id: number;
+ platform_id: string;
+ success: boolean;
+ status: "success" | "failed" | "pending_review";
+ external_article_id?: string | null;
+ external_article_url?: string | null;
+ external_manage_url?: string | null;
+ message?: string | null;
+}
+
+export interface PublisherPublishResponse {
+ success: boolean;
+ batch_status: string;
+ results: PublisherPublishTaskResult[];
+}
+
export interface BrandRequest {
name: string;
website?: string | null;
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index b6e1349..6327156 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -64,17 +64,48 @@ importers:
version: 24.12.0
'@vitejs/plugin-vue':
specifier: ^6.0.5
- version: 6.0.5(vite@8.0.3(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@24.12.0))(vue@3.5.31(typescript@5.9.3))
+ version: 6.0.5(vite@8.0.3(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@24.12.0)(esbuild@0.27.5)(jiti@2.6.1))(vue@3.5.31(typescript@5.9.3))
typescript:
specifier: ^5.9.3
version: 5.9.3
vite:
specifier: ^8.0.3
- version: 8.0.3(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@24.12.0)
+ version: 8.0.3(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@24.12.0)(esbuild@0.27.5)(jiti@2.6.1)
vue-tsc:
specifier: ^3.2.6
version: 3.2.6(typescript@5.9.3)
+ apps/browser-extension:
+ dependencies:
+ '@geo/shared-types':
+ specifier: workspace:*
+ version: link:../../packages/shared-types
+ js-md5:
+ specifier: ^0.8.3
+ version: 0.8.3
+ marked:
+ specifier: ^17.0.5
+ version: 17.0.5
+ vue:
+ specifier: ^3.5.31
+ version: 3.5.31(typescript@5.9.3)
+ devDependencies:
+ '@types/chrome':
+ specifier: ^0.1.38
+ version: 0.1.38
+ '@wxt-dev/module-vue':
+ specifier: ^1.0.3
+ version: 1.0.3(vite@8.0.3(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@24.12.0)(esbuild@0.27.5)(jiti@2.6.1))(vue@3.5.31(typescript@5.9.3))(wxt@0.20.20(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@24.12.0)(jiti@2.6.1))
+ typescript:
+ specifier: ^5.9.3
+ version: 5.9.3
+ vue-tsc:
+ specifier: ^3.2.6
+ version: 3.2.6(typescript@5.9.3)
+ wxt:
+ specifier: ^0.20.20
+ version: 0.20.20(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@24.12.0)(jiti@2.6.1)
+
packages/http-client:
dependencies:
'@geo/shared-types':
@@ -88,6 +119,19 @@ importers:
packages:
+ '@1natsu/wait-element@4.1.2':
+ resolution: {integrity: sha512-qWxSJD+Q5b8bKOvESFifvfZ92DuMsY+03SBNjTO34ipJLP6mZ9yK4bQz/vlh48aEQXoJfaZBqUwKL5BdI5iiWw==}
+
+ '@aklinker1/rollup-plugin-visualizer@5.12.0':
+ resolution: {integrity: sha512-X24LvEGw6UFmy0lpGJDmXsMyBD58XmX1bbwsaMLhNoM+UMQfQ3b2RtC+nz4b/NoRK5r6QJSKJHBNVeUdwqybaQ==}
+ engines: {node: '>=14'}
+ hasBin: true
+ peerDependencies:
+ rollup: 2.x || 3.x || 4.x
+ peerDependenciesMeta:
+ rollup:
+ optional: true
+
'@ant-design/colors@6.0.0':
resolution: {integrity: sha512-qAZRvPzfdWHtfameEGP2Qvuf838NhergR35o+EuVyB5XvSA98xod5r4utvi4TJ3ywmevm290g9nsCG5MryrdWQ==}
@@ -99,6 +143,10 @@ packages:
peerDependencies:
vue: '>=3.0.3'
+ '@babel/code-frame@7.29.0':
+ resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==}
+ engines: {node: '>=6.9.0'}
+
'@babel/helper-string-parser@7.27.1':
resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==}
engines: {node: '>=6.9.0'}
@@ -112,6 +160,10 @@ packages:
engines: {node: '>=6.0.0'}
hasBin: true
+ '@babel/runtime@7.28.2':
+ resolution: {integrity: sha512-KHp2IflsnGywDjBWDkR9iEqiWSpc8GIi0lgTT3mOElT0PP1tG26P4tmFI2YvAdzgq9RGyoHZQEIEdZy6Ec5xCA==}
+ engines: {node: '>=6.9.0'}
+
'@babel/runtime@7.29.2':
resolution: {integrity: sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==}
engines: {node: '>=6.9.0'}
@@ -217,6 +269,19 @@ packages:
resolution: {integrity: sha512-SITSV6aIXsuVNV3f3O0f2n/cgyEDWoSqtZMYiAmcsYHydcKrOz3gUxB/iXd/Qf08+IZX4KpgNbvUdMBmWz+kcA==}
engines: {node: '>=10'}
+ '@devicefarmer/adbkit-logcat@2.1.3':
+ resolution: {integrity: sha512-yeaGFjNBc/6+svbDeul1tNHtNChw6h8pSHAt5D+JsedUrMTN7tla7B15WLDyekxsuS2XlZHRxpuC6m92wiwCNw==}
+ engines: {node: '>= 4'}
+
+ '@devicefarmer/adbkit-monkey@1.2.1':
+ resolution: {integrity: sha512-ZzZY/b66W2Jd6NHbAhLyDWOEIBWC11VizGFk7Wx7M61JZRz7HR9Cq5P+65RKWUU7u6wgsE8Lmh9nE4Mz+U2eTg==}
+ engines: {node: '>= 0.10.4'}
+
+ '@devicefarmer/adbkit@3.3.8':
+ resolution: {integrity: sha512-7rBLLzWQnBwutH2WZ0EWUkQdihqrnLYCUMaB44hSol9e0/cdIhuNFcqZO0xNheAU6qqHVA8sMiLofkYTgb+lmw==}
+ engines: {node: '>= 0.10.4'}
+ hasBin: true
+
'@emnapi/core@1.9.1':
resolution: {integrity: sha512-mukuNALVsoix/w1BJwFzwXBN/dHeejQtuVzcDsfOEsdpCumXb/E9j8w11h5S54tT1xhifGfbbSm/ICrObRb3KA==}
@@ -232,6 +297,162 @@ packages:
'@emotion/unitless@0.8.1':
resolution: {integrity: sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==}
+ '@esbuild/aix-ppc64@0.27.5':
+ resolution: {integrity: sha512-nGsF/4C7uzUj+Nj/4J+Zt0bYQ6bz33Phz8Lb2N80Mti1HjGclTJdXZ+9APC4kLvONbjxN1zfvYNd8FEcbBK/MQ==}
+ engines: {node: '>=18'}
+ cpu: [ppc64]
+ os: [aix]
+
+ '@esbuild/android-arm64@0.27.5':
+ resolution: {integrity: sha512-Oeghq+XFgh1pUGd1YKs4DDoxzxkoUkvko+T/IVKwlghKLvvjbGFB3ek8VEDBmNvqhwuL0CQS3cExdzpmUyIrgA==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [android]
+
+ '@esbuild/android-arm@0.27.5':
+ resolution: {integrity: sha512-Cv781jd0Rfj/paoNrul1/r4G0HLvuFKYh7C9uHZ2Pl8YXstzvCyyeWENTFR9qFnRzNMCjXmsulZuvosDg10Mog==}
+ engines: {node: '>=18'}
+ cpu: [arm]
+ os: [android]
+
+ '@esbuild/android-x64@0.27.5':
+ resolution: {integrity: sha512-nQD7lspbzerlmtNOxYMFAGmhxgzn8Z7m9jgFkh6kpkjsAhZee1w8tJW3ZlW+N9iRePz0oPUDrYrXidCPSImD0Q==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [android]
+
+ '@esbuild/darwin-arm64@0.27.5':
+ resolution: {integrity: sha512-I+Ya/MgC6rr8oRWGRDF3BXDfP8K1BVUggHqN6VI2lUZLdDi1IM1v2cy0e3lCPbP+pVcK3Tv8cgUhHse1kaNZZw==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@esbuild/darwin-x64@0.27.5':
+ resolution: {integrity: sha512-MCjQUtC8wWJn/pIPM7vQaO69BFgwPD1jriEdqwTCKzWjGgkMbcg+M5HzrOhPhuYe1AJjXlHmD142KQf+jnYj8A==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [darwin]
+
+ '@esbuild/freebsd-arm64@0.27.5':
+ resolution: {integrity: sha512-X6xVS+goSH0UelYXnuf4GHLwpOdc8rgK/zai+dKzBMnncw7BTQIwquOodE7EKvY2UVUetSqyAfyZC1D+oqLQtg==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [freebsd]
+
+ '@esbuild/freebsd-x64@0.27.5':
+ resolution: {integrity: sha512-233X1FGo3a8x1ekLB6XT69LfZ83vqz+9z3TSEQCTYfMNY880A97nr81KbPcAMl9rmOFp11wO0dP+eB18KU/Ucg==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@esbuild/linux-arm64@0.27.5':
+ resolution: {integrity: sha512-euKkilsNOv7x/M1NKsx5znyprbpsRFIzTV6lWziqJch7yWYayfLtZzDxDTl+LSQDJYAjd9TVb/Kt5UKIrj2e4A==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@esbuild/linux-arm@0.27.5':
+ resolution: {integrity: sha512-0wkVrYHG4sdCCN/bcwQ7yYMXACkaHc3UFeaEOwSVW6e5RycMageYAFv+JS2bKLwHyeKVUvtoVH+5/RHq0fgeFw==}
+ engines: {node: '>=18'}
+ cpu: [arm]
+ os: [linux]
+
+ '@esbuild/linux-ia32@0.27.5':
+ resolution: {integrity: sha512-hVRQX4+P3MS36NxOy24v/Cdsimy/5HYePw+tmPqnNN1fxV0bPrFWR6TMqwXPwoTM2VzbkA+4lbHWUKDd5ZDA/w==}
+ engines: {node: '>=18'}
+ cpu: [ia32]
+ os: [linux]
+
+ '@esbuild/linux-loong64@0.27.5':
+ resolution: {integrity: sha512-mKqqRuOPALI8nDzhOBmIS0INvZOOFGGg5n1osGIXAx8oersceEbKd4t1ACNTHM3sJBXGFAlEgqM+svzjPot+ZQ==}
+ engines: {node: '>=18'}
+ cpu: [loong64]
+ os: [linux]
+
+ '@esbuild/linux-mips64el@0.27.5':
+ resolution: {integrity: sha512-EE/QXH9IyaAj1qeuIV5+/GZkBTipgGO782Ff7Um3vPS9cvLhJJeATy4Ggxikz2inZ46KByamMn6GqtqyVjhenA==}
+ engines: {node: '>=18'}
+ cpu: [mips64el]
+ os: [linux]
+
+ '@esbuild/linux-ppc64@0.27.5':
+ resolution: {integrity: sha512-0V2iF1RGxBf1b7/BjurA5jfkl7PtySjom1r6xOK2q9KWw/XCpAdtB6KNMO+9xx69yYfSCRR9FE0TyKfHA2eQMw==}
+ engines: {node: '>=18'}
+ cpu: [ppc64]
+ os: [linux]
+
+ '@esbuild/linux-riscv64@0.27.5':
+ resolution: {integrity: sha512-rYxThBx6G9HN6tFNuvB/vykeLi4VDsm5hE5pVwzqbAjZEARQrWu3noZSfbEnPZ/CRXP3271GyFk/49up2W190g==}
+ engines: {node: '>=18'}
+ cpu: [riscv64]
+ os: [linux]
+
+ '@esbuild/linux-s390x@0.27.5':
+ resolution: {integrity: sha512-uEP2q/4qgd8goEUc4QIdU/1P2NmEtZ/zX5u3OpLlCGhJIuBIv0s0wr7TB2nBrd3/A5XIdEkkS5ZLF0ULuvaaYQ==}
+ engines: {node: '>=18'}
+ cpu: [s390x]
+ os: [linux]
+
+ '@esbuild/linux-x64@0.27.5':
+ resolution: {integrity: sha512-+Gq47Wqq6PLOOZuBzVSII2//9yyHNKZLuwfzCemqexqOQCSz0zy0O26kIzyp9EMNMK+nZ0tFHBZrCeVUuMs/ew==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [linux]
+
+ '@esbuild/netbsd-arm64@0.27.5':
+ resolution: {integrity: sha512-3F/5EG8VHfN/I+W5cO1/SV2H9Q/5r7vcHabMnBqhHK2lTWOh3F8vixNzo8lqxrlmBtZVFpW8pmITHnq54+Tq4g==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [netbsd]
+
+ '@esbuild/netbsd-x64@0.27.5':
+ resolution: {integrity: sha512-28t+Sj3CPN8vkMOlZotOmDgilQwVvxWZl7b8rxpn73Tt/gCnvrHxQUMng4uu3itdFvrtba/1nHejvxqz8xgEMA==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [netbsd]
+
+ '@esbuild/openbsd-arm64@0.27.5':
+ resolution: {integrity: sha512-Doz/hKtiuVAi9hMsBMpwBANhIZc8l238U2Onko3t2xUp8xtM0ZKdDYHMnm/qPFVthY8KtxkXaocwmMh6VolzMA==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [openbsd]
+
+ '@esbuild/openbsd-x64@0.27.5':
+ resolution: {integrity: sha512-WfGVaa1oz5A7+ZFPkERIbIhKT4olvGl1tyzTRaB5yoZRLqC0KwaO95FeZtOdQj/oKkjW57KcVF944m62/0GYtA==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [openbsd]
+
+ '@esbuild/openharmony-arm64@0.27.5':
+ resolution: {integrity: sha512-Xh+VRuh6OMh3uJ0JkCjI57l+DVe7VRGBYymen8rFPnTVgATBwA6nmToxM2OwTlSvrnWpPKkrQUj93+K9huYC6A==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [openharmony]
+
+ '@esbuild/sunos-x64@0.27.5':
+ resolution: {integrity: sha512-aC1gpJkkaUADHuAdQfuVTnqVUTLqqUNhAvEwHwVWcnVVZvNlDPGA0UveZsfXJJ9T6k9Po4eHi3c02gbdwO3g6w==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [sunos]
+
+ '@esbuild/win32-arm64@0.27.5':
+ resolution: {integrity: sha512-0UNx2aavV0fk6UpZcwXFLztA2r/k9jTUa7OW7SAea1VYUhkug99MW1uZeXEnPn5+cHOd0n8myQay6TlFnBR07w==}
+ engines: {node: '>=18'}
+ cpu: [arm64]
+ os: [win32]
+
+ '@esbuild/win32-ia32@0.27.5':
+ resolution: {integrity: sha512-5nlJ3AeJWCTSzR7AEqVjT/faWyqKU86kCi1lLmxVqmNR+j4HrYdns+eTGjS/vmrzCIe8inGQckUadvS0+JkKdQ==}
+ engines: {node: '>=18'}
+ cpu: [ia32]
+ os: [win32]
+
+ '@esbuild/win32-x64@0.27.5':
+ resolution: {integrity: sha512-PWypQR+d4FLfkhBIV+/kHsUELAnMpx1bRvvsn3p+/sAERbnCzFrtDRG2Xw5n+2zPxBK2+iaP+vetsRl4Ti7WgA==}
+ engines: {node: '>=18'}
+ cpu: [x64]
+ os: [win32]
+
'@floating-ui/core@1.7.5':
resolution: {integrity: sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==}
@@ -253,9 +474,22 @@ packages:
resolution: {integrity: sha512-BcmHpb5bQyeVNrptC3UhzpBZB/YHHDoEREOUERrmF2BRxsyOEuRrq+Z96C/D4+2KJb8kuHiouzAei7BXlG0YYw==}
engines: {node: '>= 16'}
+ '@jridgewell/gen-mapping@0.3.13':
+ resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==}
+
+ '@jridgewell/remapping@2.3.5':
+ resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==}
+
+ '@jridgewell/resolve-uri@3.1.2':
+ resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==}
+ engines: {node: '>=6.0.0'}
+
'@jridgewell/sourcemap-codec@1.5.5':
resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==}
+ '@jridgewell/trace-mapping@0.3.31':
+ resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==}
+
'@lezer/common@1.5.1':
resolution: {integrity: sha512-6YRVG9vBkaY7p1IVxL4s44n5nUnaNnGM2/AckNgYOnxTG2kWh1vR8BMxPseWPjRNpb5VtXnMpeYAEAADoRV1Iw==}
@@ -397,6 +631,18 @@ packages:
'@oxc-project/types@0.122.0':
resolution: {integrity: sha512-oLAl5kBpV4w69UtFZ9xqcmTi+GENWOcPF7FCrczTiBbmC0ibXxCwyvZGbO39rCVEuLGAZM84DH0pUIyyv/YJzA==}
+ '@pnpm/config.env-replace@1.1.0':
+ resolution: {integrity: sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w==}
+ engines: {node: '>=12.22.0'}
+
+ '@pnpm/network.ca-file@1.0.2':
+ resolution: {integrity: sha512-YcPQ8a0jwYU9bTdJDpXjMi7Brhkr1mXsXrUJvjqM2mQDgkRiz8jFaQGOdaLxgjtUfQgZhKy/O3cG/YwmgKaxLA==}
+ engines: {node: '>=12.22.0'}
+
+ '@pnpm/npm-conf@3.0.2':
+ resolution: {integrity: sha512-h104Kh26rR8tm+a3Qkc5S4VLYint3FE48as7+/5oCEcKR2idC/pF1G6AhIXKI+eHPJa/3J9i5z0Al47IeGHPkA==}
+ engines: {node: '>=12'}
+
'@rolldown/binding-android-arm64@1.0.0-rc.12':
resolution: {integrity: sha512-pv1y2Fv0JybcykuiiD3qBOBdz6RteYojRFY1d+b95WVuzx211CRh+ytI/+9iVyWQ6koTh5dawe4S/yRfOFjgaA==}
engines: {node: ^20.19.0 || >=22.12.0}
@@ -520,9 +766,24 @@ packages:
'@tybys/wasm-util@0.10.1':
resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==}
+ '@types/chrome@0.1.38':
+ resolution: {integrity: sha512-5aK4m9wZqoWAoB98aElESLm/5pXpqJnFWMNoiCs/XdPsXR6wNdVkJFSdQ9Wr4PnTuUrxD0SuNuDHh3EG5QeBzA==}
+
'@types/debug@4.1.13':
resolution: {integrity: sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==}
+ '@types/estree@1.0.8':
+ resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==}
+
+ '@types/filesystem@0.0.36':
+ resolution: {integrity: sha512-vPDXOZuannb9FZdxgHnqSwAG/jvdGM8Wq+6N4D/d80z+D4HWH+bItqsZaVRQykAn6WEVeEkLm2oQigyHtgb0RA==}
+
+ '@types/filewriter@0.0.33':
+ resolution: {integrity: sha512-xFU8ZXTw4gd358lb2jw25nxY9QAgqn2+bKKjKOYfNCzN4DKCFetK7sPtrlpg66Ywe3vWY9FNxprZawAh9wfJ3g==}
+
+ '@types/har-format@1.2.16':
+ resolution: {integrity: sha512-fluxdy7ryD3MV6h8pTfTYpy/xQzCFC7m89nOH9y94cNqJ1mDIDPut7MnRHI3F6qRmh/cT2fUjG1MLdCNb4hE9A==}
+
'@types/hast@3.0.4':
resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==}
@@ -538,6 +799,9 @@ packages:
'@types/mdast@4.0.4':
resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==}
+ '@types/minimatch@3.0.5':
+ resolution: {integrity: sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==}
+
'@types/ms@2.1.0':
resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==}
@@ -626,43 +890,206 @@ packages:
peerDependencies:
vue: ^3.5.0
+ '@webext-core/fake-browser@1.3.4':
+ resolution: {integrity: sha512-nZcVWr3JpwpS5E6hKpbAwAMBM/AXZShnfW0F76udW8oLd6Kv0nbW6vFS07md4Na/0ntQonk3hFnlQYGYBAlTrA==}
+
+ '@webext-core/isolated-element@1.1.5':
+ resolution: {integrity: sha512-4m6oP8Vzm/68YO1QmkUOZqqUcmyBtA53tji2g00/nYXE3E3IceYgeub7eIqvXDV2Z7xU6cm6qO1IMt4XFVwtvQ==}
+
+ '@webext-core/match-patterns@1.0.3':
+ resolution: {integrity: sha512-NY39ACqCxdKBmHgw361M9pfJma8e4AZo20w9AY+5ZjIj1W2dvXC8J31G5fjfOGbulW9w4WKpT8fPooi0mLkn9A==}
+
+ '@wxt-dev/browser@0.1.38':
+ resolution: {integrity: sha512-Y9nUfNOMqgsoO8KQ1BssrwzHEmrSr/2pUowAG4Wcr9EyKyhOK7mC7Vdyj2kXAmp5NOUXHjhghzJ6qIb5h+RbCA==}
+
+ '@wxt-dev/module-vue@1.0.3':
+ resolution: {integrity: sha512-xUNygcj4w0b1MzQGOQIp5V2ZwFE4y+5AwKPi/IPKyo2aNeFVJTfItIxzWI5vTj7031WCLzHgQrrBRS5C63D5iw==}
+ peerDependencies:
+ wxt: '>=0.19.16'
+
+ '@wxt-dev/storage@1.2.8':
+ resolution: {integrity: sha512-GWCFKgF5+d7eslOxUDFC70ypA9njupmJb1nQM8uZoX0J3sWT2BO5xJLzb1sYahWAfID9p2BMtnUBN1lkWxPsbQ==}
+
+ acorn@8.16.0:
+ resolution: {integrity: sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==}
+ engines: {node: '>=0.4.0'}
+ hasBin: true
+
+ adm-zip@0.5.17:
+ resolution: {integrity: sha512-+Ut8d9LLqwEvHHJl1+PIHqoyDxFgVN847JTVM3Izi3xHDWPE4UtzzXysMZQs64DMcrJfBeS/uoEP4AD3HQHnQQ==}
+ engines: {node: '>=12.0'}
+
alien-signals@3.1.2:
resolution: {integrity: sha512-d9dYqZTS90WLiU0I5c6DHj/HcKkF8ZyGN3G5x8wSbslulz70KOxaqCT0hQCo9KOyhVqzqGojvNdJXoTumZOtcw==}
+ ansi-align@3.0.1:
+ resolution: {integrity: sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==}
+
+ ansi-escapes@7.3.0:
+ resolution: {integrity: sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg==}
+ engines: {node: '>=18'}
+
+ ansi-regex@5.0.1:
+ resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
+ engines: {node: '>=8'}
+
+ ansi-regex@6.2.2:
+ resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==}
+ engines: {node: '>=12'}
+
+ ansi-styles@4.3.0:
+ resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
+ engines: {node: '>=8'}
+
+ ansi-styles@6.2.3:
+ resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==}
+ engines: {node: '>=12'}
+
ant-design-vue@4.2.6:
resolution: {integrity: sha512-t7eX13Yj3i9+i5g9lqFyYneoIb3OzTvQjq9Tts1i+eiOd3Eva/6GagxBSXM1fOCjqemIu0FYVE1ByZ/38epR3Q==}
engines: {node: '>=12.22.0'}
peerDependencies:
vue: '>=3.2.0'
+ array-differ@4.0.0:
+ resolution: {integrity: sha512-Q6VPTLMsmXZ47ENG3V+wQyZS1ZxXMxFyYzA+Z/GMrJ6yIutAIEf9wTyroTzmGjNfox9/h3GdGBCVh43GVFx4Uw==}
+ engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+
array-tree-filter@2.1.0:
resolution: {integrity: sha512-4ROwICNlNw/Hqa9v+rk5h22KjmzB1JGTMVKP2AKJBOCgb0yL0ASf0+YvCcLNNwquOHNX48jkeZIJ3a+oOQqKcw==}
+ array-union@3.0.1:
+ resolution: {integrity: sha512-1OvF9IbWwaeiM9VhzYXVQacMibxpXOMYVNIvMtKRyX9SImBXpKcFr8XvFDeEslCyuH/t6KRt7HEO94AlP8Iatw==}
+ engines: {node: '>=12'}
+
+ async-mutex@0.5.0:
+ resolution: {integrity: sha512-1A94B18jkJ3DYq284ohPxoXbfTA5HsQ7/Mf4DEhcyLx3Bz27Rh59iScbB6EPiP+B+joue6YCxcMXSbFC1tZKwA==}
+
async-validator@4.2.5:
resolution: {integrity: sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg==}
+ async@3.2.6:
+ resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==}
+
asynckit@0.4.0:
resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
+ atomic-sleep@1.0.0:
+ resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==}
+ engines: {node: '>=8.0.0'}
+
+ atomically@2.1.1:
+ resolution: {integrity: sha512-P4w9o2dqARji6P7MHprklbfiArZAWvo07yW7qs3pdljb3BWr12FIB7W+p0zJiuiVsUpRO0iZn1kFFcpPegg0tQ==}
+
axios@1.14.0:
resolution: {integrity: sha512-3Y8yrqLSwjuzpXuZ0oIYZ/XGgLwUIBU3uLvbcpb0pidD9ctpShJd43KSlEEkVQg6DS0G9NKyzOvBfUtDKEyHvQ==}
bail@2.0.2:
resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==}
+ balanced-match@1.0.2:
+ resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
+
+ balanced-match@4.0.4:
+ resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==}
+ engines: {node: 18 || 20 || >=22}
+
birpc@2.9.0:
resolution: {integrity: sha512-KrayHS5pBi69Xi9JmvoqrIgYGDkD6mcSe/i6YKi3w5kekCLzrX4+nawcXqrj2tIp50Kw/mT/s3p+GVK0A0sKxw==}
+ bluebird@3.7.2:
+ resolution: {integrity: sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==}
+
+ boolbase@1.0.0:
+ resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==}
+
+ boxen@8.0.1:
+ resolution: {integrity: sha512-F3PH5k5juxom4xktynS7MoFY+NUWH5LC4CnH11YB8NPew+HLpmBLCybSAEyb2F+4pRXhuhWqFesoQd6DAyc2hw==}
+ engines: {node: '>=18'}
+
+ brace-expansion@1.1.13:
+ resolution: {integrity: sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==}
+
+ brace-expansion@5.0.5:
+ resolution: {integrity: sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==}
+ engines: {node: 18 || 20 || >=22}
+
+ buffer-equal-constant-time@1.0.1:
+ resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==}
+
+ buffer-from@1.1.2:
+ resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
+
+ bundle-name@4.1.0:
+ resolution: {integrity: sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==}
+ engines: {node: '>=18'}
+
+ c12@3.3.4:
+ resolution: {integrity: sha512-cM0ApFQSBXuourJejzwv/AuPRvAxordTyParRVcHjjtXirtkzM0uK2L9TTn9s0cXZbG7E55jCivRQzoxYmRAlA==}
+ peerDependencies:
+ magicast: '*'
+ peerDependenciesMeta:
+ magicast:
+ optional: true
+
+ cac@6.7.14:
+ resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==}
+ engines: {node: '>=8'}
+
+ cac@7.0.0:
+ resolution: {integrity: sha512-tixWYgm5ZoOD+3g6UTea91eow5z6AAHaho3g0V9CNSNb45gM8SmflpAc+GRd1InC4AqN/07Unrgp56Y94N9hJQ==}
+ engines: {node: '>=20.19.0'}
+
call-bind-apply-helpers@1.0.2:
resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==}
engines: {node: '>= 0.4'}
+ camelcase@8.0.0:
+ resolution: {integrity: sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==}
+ engines: {node: '>=16'}
+
ccount@2.0.1:
resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==}
+ chalk@5.6.2:
+ resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==}
+ engines: {node: ^12.17.0 || ^14.13 || >=16.0.0}
+
character-entities@2.0.2:
resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==}
+ chokidar@5.0.0:
+ resolution: {integrity: sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==}
+ engines: {node: '>= 20.19.0'}
+
+ chrome-launcher@1.2.0:
+ resolution: {integrity: sha512-JbuGuBNss258bvGil7FT4HKdC3SC2K7UAEUqiPy3ACS3Yxo3hAW6bvFpCu2HsIJLgTqxgEX6BkujvzZfLpUD0Q==}
+ engines: {node: '>=12.13.0'}
+ hasBin: true
+
+ ci-info@4.4.0:
+ resolution: {integrity: sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==}
+ engines: {node: '>=8'}
+
+ citty@0.2.2:
+ resolution: {integrity: sha512-+6vJA3L98yv+IdfKGZHBNiGW5KHn22e/JwID0Strsz8h4S/csAu/OuICwxrg44k5MRiZHWIo8XXuJgQTriRP4w==}
+
+ cli-boxes@3.0.0:
+ resolution: {integrity: sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==}
+ engines: {node: '>=10'}
+
+ cli-cursor@5.0.0:
+ resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==}
+ engines: {node: '>=18'}
+
+ cli-truncate@5.2.0:
+ resolution: {integrity: sha512-xRwvIOMGrfOAnM1JYtqQImuaNtDEv9v6oIYAs4LIHwTiKee8uwvIi363igssOC0O5U04i4AlENs79LQLu9tEMw==}
+ engines: {node: '>=20'}
+
+ cliui@8.0.1:
+ resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==}
+ engines: {node: '>=12'}
+
clsx@2.1.1:
resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==}
engines: {node: '>=6'}
@@ -670,17 +1097,56 @@ packages:
codemirror@6.0.2:
resolution: {integrity: sha512-VhydHotNW5w1UGK0Qj96BwSk/Zqbp9WbnyK2W/eVMv4QyF41INRGpjUhFJY7/uDNuudSc33a/PKr4iDqRduvHw==}
+ color-convert@2.0.1:
+ resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
+ engines: {node: '>=7.0.0'}
+
+ color-name@1.1.4:
+ resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
+
combined-stream@1.0.8:
resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
engines: {node: '>= 0.8'}
+ commander@2.9.0:
+ resolution: {integrity: sha512-bmkUukX8wAOjHdN26xj5c4ctEV22TQ7dQYhSmuckKhToXrkUn0iIaolHdIxYYqD55nhpSPA9zPQ1yP57GdXP2A==}
+ engines: {node: '>= 0.6.x'}
+
commander@8.3.0:
resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==}
engines: {node: '>= 12'}
+ commander@9.5.0:
+ resolution: {integrity: sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==}
+ engines: {node: ^12.20.0 || >=14}
+
compute-scroll-into-view@1.0.20:
resolution: {integrity: sha512-UCB0ioiyj8CRjtrvaceBLqqhZCVP+1B8+NWQhmdsm0VXOJtobBCf1dBQmebCCo34qZmUwZfIH2MZLqNHazrfjg==}
+ concat-map@0.0.1:
+ resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
+
+ concat-stream@1.6.2:
+ resolution: {integrity: sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==}
+ engines: {'0': node >= 0.8}
+
+ confbox@0.1.8:
+ resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==}
+
+ confbox@0.2.4:
+ resolution: {integrity: sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ==}
+
+ config-chain@1.1.13:
+ resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==}
+
+ configstore@7.1.0:
+ resolution: {integrity: sha512-N4oog6YJWbR9kGyXvS7jEykLDXIE2C0ILYqNBZBp9iwiJpoCBWYsuAdW6PPFn6w06jjnC+3JstVvWHO4cZqvRg==}
+ engines: {node: '>=18'}
+
+ consola@3.4.2:
+ resolution: {integrity: sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==}
+ engines: {node: ^14.18.0 || >=16.10.0}
+
copy-anything@4.0.5:
resolution: {integrity: sha512-7Vv6asjS4gMOuILabD3l739tsaxFQmC+a7pLZm02zyvs8p977bL3zEgq3yDk5rn9B0PbYgIv++jmHcuUab4RhA==}
engines: {node: '>=18'}
@@ -688,15 +1154,40 @@ packages:
core-js@3.49.0:
resolution: {integrity: sha512-es1U2+YTtzpwkxVLwAFdSpaIMyQaq0PBgm3YD1W3Qpsn1NAmO3KSgZfu+oGSWVu6NvLHoHCV/aYcsE5wiB7ALg==}
+ core-util-is@1.0.3:
+ resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==}
+
crelt@1.0.6:
resolution: {integrity: sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==}
+ css-select@5.2.2:
+ resolution: {integrity: sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==}
+
+ css-what@6.2.2:
+ resolution: {integrity: sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==}
+ engines: {node: '>= 6'}
+
+ cssom@0.5.0:
+ resolution: {integrity: sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==}
+
csstype@3.2.3:
resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==}
dayjs@1.11.20:
resolution: {integrity: sha512-YbwwqR/uYpeoP4pu043q+LTDLFBLApUP6VxRihdfNTqu4ubqMlGDLd6ErXhEgsyvY0K6nCs7nggYumAN+9uEuQ==}
+ debounce@1.2.1:
+ resolution: {integrity: sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==}
+
+ debug@4.3.7:
+ resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==}
+ engines: {node: '>=6.0'}
+ peerDependencies:
+ supports-color: '*'
+ peerDependenciesMeta:
+ supports-color:
+ optional: true
+
debug@4.4.3:
resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==}
engines: {node: '>=6.0'}
@@ -709,6 +1200,29 @@ packages:
decode-named-character-reference@1.3.0:
resolution: {integrity: sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==}
+ deep-extend@0.6.0:
+ resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==}
+ engines: {node: '>=4.0.0'}
+
+ default-browser-id@5.0.1:
+ resolution: {integrity: sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==}
+ engines: {node: '>=18'}
+
+ default-browser@5.5.0:
+ resolution: {integrity: sha512-H9LMLr5zwIbSxrmvikGuI/5KGhZ8E2zH3stkMgM5LpOWDutGM2JZaj460Udnf1a+946zc7YBgrqEWwbk7zHvGw==}
+ engines: {node: '>=18'}
+
+ define-lazy-prop@2.0.0:
+ resolution: {integrity: sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==}
+ engines: {node: '>=8'}
+
+ define-lazy-prop@3.0.0:
+ resolution: {integrity: sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==}
+ engines: {node: '>=12'}
+
+ defu@6.1.6:
+ resolution: {integrity: sha512-f8mefEW4WIVg4LckePx3mALjQSPQgFlg9U8yaPdlsbdYcHQyj9n2zL2LJEA52smeYxOvmd/nB7TpMtHGMTHcug==}
+
delayed-stream@1.0.0:
resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
engines: {node: '>=0.4.0'}
@@ -717,6 +1231,9 @@ packages:
resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==}
engines: {node: '>=6'}
+ destr@2.0.5:
+ resolution: {integrity: sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==}
+
detect-libc@2.1.2:
resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==}
engines: {node: '>=8'}
@@ -730,17 +1247,66 @@ packages:
dom-scroll-into-view@2.0.1:
resolution: {integrity: sha512-bvVTQe1lfaUr1oFzZX80ce9KLDlZ3iU+XGNE/bz9HnGdklTieqsbmsLHe+rT2XWqopvL0PckkYqN7ksmm5pe3w==}
+ dom-serializer@2.0.0:
+ resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==}
+
+ domelementtype@2.3.0:
+ resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==}
+
+ domhandler@5.0.3:
+ resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==}
+ engines: {node: '>= 4'}
+
dompurify@3.3.3:
resolution: {integrity: sha512-Oj6pzI2+RqBfFG+qOaOLbFXLQ90ARpcGG6UePL82bJLtdsa6CYJD7nmiU8MW9nQNOtCHV3lZ/Bzq1X0QYbBZCA==}
+ domutils@3.2.2:
+ resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==}
+
+ dot-prop@9.0.0:
+ resolution: {integrity: sha512-1gxPBJpI/pcjQhKgIU91II6Wkay+dLcN3M6rf2uwP8hRur3HtQXjVrdAK3sjC0piaEuxzMwjXChcETiJl47lAQ==}
+ engines: {node: '>=18'}
+
+ dotenv-expand@12.0.3:
+ resolution: {integrity: sha512-uc47g4b+4k/M/SeaW1y4OApx+mtLWl92l5LMPP0GNXctZqELk+YGgOPIIC5elYmUH4OuoK3JLhuRUYegeySiFA==}
+ engines: {node: '>=12'}
+
+ dotenv@16.6.1:
+ resolution: {integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==}
+ engines: {node: '>=12'}
+
+ dotenv@17.4.0:
+ resolution: {integrity: sha512-kCKF62fwtzwYm0IGBNjRUjtJgMfGapII+FslMHIjMR5KTnwEmBmWLDRSnc3XSNP8bNy34tekgQyDT0hr7pERRQ==}
+ engines: {node: '>=12'}
+
dunder-proto@1.0.1:
resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==}
engines: {node: '>= 0.4'}
+ ecdsa-sig-formatter@1.0.11:
+ resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==}
+
+ emoji-regex@10.6.0:
+ resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==}
+
+ emoji-regex@8.0.0:
+ resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
+
+ entities@4.5.0:
+ resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
+ engines: {node: '>=0.12'}
+
entities@7.0.1:
resolution: {integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==}
engines: {node: '>=0.12'}
+ environment@1.1.0:
+ resolution: {integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==}
+ engines: {node: '>=18'}
+
+ error-ex@1.3.4:
+ resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==}
+
es-define-property@1.0.1:
resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==}
engines: {node: '>= 0.4'}
@@ -749,6 +1315,9 @@ packages:
resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==}
engines: {node: '>= 0.4'}
+ es-module-lexer@2.0.0:
+ resolution: {integrity: sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==}
+
es-object-atoms@1.1.1:
resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==}
engines: {node: '>= 0.4'}
@@ -757,6 +1326,26 @@ packages:
resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==}
engines: {node: '>= 0.4'}
+ es6-error@4.1.1:
+ resolution: {integrity: sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==}
+
+ esbuild@0.27.5:
+ resolution: {integrity: sha512-zdQoHBjuDqKsvV5OPaWansOwfSQ0Js+Uj9J85TBvj3bFW1JjWTSULMRwdQAc8qMeIScbClxeMK0jlrtB9linhA==}
+ engines: {node: '>=18'}
+ hasBin: true
+
+ escalade@3.2.0:
+ resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
+ engines: {node: '>=6'}
+
+ escape-goat@4.0.0:
+ resolution: {integrity: sha512-2Sd4ShcWxbx6OY1IHyla/CVNwvg7XwZVoXZHcSu9w9SReNP1EzzD5T8NWKIR38fIqEns9kDWKUQTXXAmlDrdPg==}
+ engines: {node: '>=12'}
+
+ escape-string-regexp@4.0.0:
+ resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==}
+ engines: {node: '>=10'}
+
escape-string-regexp@5.0.0:
resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==}
engines: {node: '>=12'}
@@ -764,9 +1353,22 @@ packages:
estree-walker@2.0.2:
resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
+ estree-walker@3.0.3:
+ resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==}
+
+ eventemitter3@5.0.4:
+ resolution: {integrity: sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==}
+
+ exsolve@1.0.8:
+ resolution: {integrity: sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA==}
+
extend@3.0.2:
resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==}
+ fast-redact@3.5.0:
+ resolution: {integrity: sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==}
+ engines: {node: '>=6'}
+
fdir@6.5.0:
resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==}
engines: {node: '>=12.0.0'}
@@ -776,6 +1378,15 @@ packages:
picomatch:
optional: true
+ filesize@11.0.15:
+ resolution: {integrity: sha512-30TpbYxQxCpi4XdVjkwXYQ37CzZltV38+P7MYroQ+4NK/Dmx9mxixFNrolzcmEIBsjT/uowC9T7kiy2+C12r1A==}
+ engines: {node: '>= 10.8.0'}
+
+ firefox-profile@4.7.0:
+ resolution: {integrity: sha512-aGApEu5bfCNbA4PGUZiRJAIU6jKmghV2UVdklXAofnNtiDjqYw0czLS46W7IfFqVKgKhFB8Ao2YoNGHY4BoIMQ==}
+ engines: {node: '>=18'}
+ hasBin: true
+
follow-redirects@1.15.11:
resolution: {integrity: sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==}
engines: {node: '>=4.0'}
@@ -785,10 +1396,22 @@ packages:
debug:
optional: true
+ form-data-encoder@4.1.0:
+ resolution: {integrity: sha512-G6NsmEW15s0Uw9XnCg+33H3ViYRyiM0hMrMhhqQOR8NFc5GhYrI+6I3u7OTw7b91J2g8rtvMBZJDbcGb2YUniw==}
+ engines: {node: '>= 18'}
+
form-data@4.0.5:
resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==}
engines: {node: '>= 6'}
+ formdata-node@6.0.3:
+ resolution: {integrity: sha512-8e1++BCiTzUno9v5IZ2J6bv4RU+3UKDmqWUQD0MIMVCd9AdhWkO1gw57oo1mNEX1dMq2EGI+FbWz4B92pscSQg==}
+ engines: {node: '>= 18'}
+
+ fs-extra@11.3.4:
+ resolution: {integrity: sha512-CTXd6rk/M3/ULNQj8FBqBWHYBVYybQ3VPBw0xGKFe3tuH7ytT6ACnvzpIQ3UZtB8yvUKC2cXn1a+x+5EVQLovA==}
+ engines: {node: '>=14.14'}
+
fsevents@2.3.3:
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
@@ -797,18 +1420,56 @@ packages:
function-bind@1.1.2:
resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
+ fx-runner@1.4.0:
+ resolution: {integrity: sha512-rci1g6U0rdTg6bAaBboP7XdRu01dzTAaKXxFf+PUqGuCv6Xu7o8NZdY1D5MvKGIjb6EdS1g3VlXOgksir1uGkg==}
+ hasBin: true
+
+ get-caller-file@2.0.5:
+ resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==}
+ engines: {node: 6.* || 8.* || >= 10.*}
+
+ get-east-asian-width@1.5.0:
+ resolution: {integrity: sha512-CQ+bEO+Tva/qlmw24dCejulK5pMzVnUOFOijVogd3KQs07HnRIgp8TGipvCCRT06xeYEbpbgwaCxglFyiuIcmA==}
+ engines: {node: '>=18'}
+
get-intrinsic@1.3.0:
resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==}
engines: {node: '>= 0.4'}
+ get-port-please@3.2.0:
+ resolution: {integrity: sha512-I9QVvBw5U/hw3RmWpYKRumUeaDgxTPd401x364rLmWBJcOQ753eov1eTgzDqRG9bqFIfDc7gfzcQEWrUri3o1A==}
+
get-proto@1.0.1:
resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==}
engines: {node: '>= 0.4'}
+ giget@3.2.0:
+ resolution: {integrity: sha512-GvHTWcykIR/fP8cj8dMpuMMkvaeJfPvYnhq0oW+chSeIr+ldX21ifU2Ms6KBoyKZQZmVaUAAhQ2EZ68KJF8a7A==}
+ hasBin: true
+
+ glob-to-regexp@0.4.1:
+ resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==}
+
+ global-directory@4.0.1:
+ resolution: {integrity: sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==}
+ engines: {node: '>=18'}
+
gopd@1.2.0:
resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==}
engines: {node: '>= 0.4'}
+ graceful-fs@4.2.10:
+ resolution: {integrity: sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==}
+
+ graceful-fs@4.2.11:
+ resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
+
+ graceful-readlink@1.0.1:
+ resolution: {integrity: sha512-8tLu60LgxF6XpdbK8OW3FA+IfTNBn1ZHGHKF4KQbEeSkajYw5PlYJcKluntgegDPTg8UkHjpet1T82vk6TQ68w==}
+
+ growly@1.3.0:
+ resolution: {integrity: sha512-+xGQY0YyAWCnqy7Cd++hc2JqMYzlm0dG30Jd0beaA64sROr8C4nt8Yc9V5Ro3avlSUDTN0ulqP/VBKi1/lLygw==}
+
has-symbols@1.1.0:
resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==}
engines: {node: '>= 0.4'}
@@ -824,25 +1485,194 @@ packages:
hookable@5.5.3:
resolution: {integrity: sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==}
+ hookable@6.1.0:
+ resolution: {integrity: sha512-ZoKZSJgu8voGK2geJS+6YtYjvIzu9AOM/KZXsBxr83uhLL++e9pEv/dlgwgy3dvHg06kTz6JOh1hk3C8Ceiymw==}
+
+ html-escaper@3.0.3:
+ resolution: {integrity: sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ==}
+
+ htmlparser2@10.1.0:
+ resolution: {integrity: sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ==}
+
+ immediate@3.0.6:
+ resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==}
+
+ import-meta-resolve@4.2.0:
+ resolution: {integrity: sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==}
+
+ inherits@2.0.4:
+ resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
+
+ ini@1.3.8:
+ resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==}
+
+ ini@4.1.1:
+ resolution: {integrity: sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==}
+ engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+
+ ini@4.1.3:
+ resolution: {integrity: sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg==}
+ engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+
+ is-absolute@0.1.7:
+ resolution: {integrity: sha512-Xi9/ZSn4NFapG8RP98iNPMOeaV3mXPisxKxzKtHVqr3g56j/fBn+yZmnxSVAA8lmZbl2J9b/a4kJvfU3hqQYgA==}
+ engines: {node: '>=0.10.0'}
+
+ is-arrayish@0.2.1:
+ resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==}
+
+ is-docker@2.2.1:
+ resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==}
+ engines: {node: '>=8'}
+ hasBin: true
+
+ is-docker@3.0.0:
+ resolution: {integrity: sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==}
+ engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+ hasBin: true
+
+ is-fullwidth-code-point@3.0.0:
+ resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
+ engines: {node: '>=8'}
+
+ is-fullwidth-code-point@5.1.0:
+ resolution: {integrity: sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==}
+ engines: {node: '>=18'}
+
+ is-in-ci@1.0.0:
+ resolution: {integrity: sha512-eUuAjybVTHMYWm/U+vBO1sY/JOCgoPCXRxzdju0K+K0BiGW0SChEL1MLC0PoCIR1OlPo5YAp8HuQoUlsWEICwg==}
+ engines: {node: '>=18'}
+ hasBin: true
+
+ is-in-ssh@1.0.0:
+ resolution: {integrity: sha512-jYa6Q9rH90kR1vKB6NM7qqd1mge3Fx4Dhw5TVlK1MUBqhEOuCagrEHMevNuCcbECmXZ0ThXkRm+Ymr51HwEPAw==}
+ engines: {node: '>=20'}
+
+ is-inside-container@1.0.0:
+ resolution: {integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==}
+ engines: {node: '>=14.16'}
+ hasBin: true
+
+ is-installed-globally@1.0.0:
+ resolution: {integrity: sha512-K55T22lfpQ63N4KEN57jZUAaAYqYHEe8veb/TycJRk9DdSCLLcovXz/mL6mOnhQaZsQGwPhuFopdQIlqGSEjiQ==}
+ engines: {node: '>=18'}
+
+ is-npm@6.1.0:
+ resolution: {integrity: sha512-O2z4/kNgyjhQwVR1Wpkbfc19JIhggF97NZNCpWTnjH7kVcZMUrnut9XSN7txI7VdyIYk5ZatOq3zvSuWpU8hoA==}
+ engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+
+ is-path-inside@4.0.0:
+ resolution: {integrity: sha512-lJJV/5dYS+RcL8uQdBDW9c9uWFLLBNRyFhnAKXw5tVqLlKZ4RMGZKv+YQ/IA3OhD+RpbJa1LLFM1FQPGyIXvOA==}
+ engines: {node: '>=12'}
+
is-plain-obj@4.1.0:
resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==}
engines: {node: '>=12'}
+ is-plain-object@2.0.4:
+ resolution: {integrity: sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==}
+ engines: {node: '>=0.10.0'}
+
is-plain-object@3.0.1:
resolution: {integrity: sha512-Xnpx182SBMrr/aBik8y+GuR4U1L9FqMSojwDQwPMmxyC6bvEqly9UBCxhauBF5vNh2gwWJNX6oDV7O+OM4z34g==}
engines: {node: '>=0.10.0'}
+ is-potential-custom-element-name@1.0.1:
+ resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==}
+
+ is-primitive@3.0.1:
+ resolution: {integrity: sha512-GljRxhWvlCNRfZyORiH77FwdFwGcMO620o37EOYC0ORWdq+WYNVqW0w2Juzew4M+L81l6/QS3t5gkkihyRqv9w==}
+ engines: {node: '>=0.10.0'}
+
+ is-relative@0.1.3:
+ resolution: {integrity: sha512-wBOr+rNM4gkAZqoLRJI4myw5WzzIdQosFAAbnvfXP5z1LyzgAI3ivOKehC5KfqlQJZoihVhirgtCBj378Eg8GA==}
+ engines: {node: '>=0.10.0'}
+
is-what@5.5.0:
resolution: {integrity: sha512-oG7cgbmg5kLYae2N5IVd3jm2s+vldjxJzK1pcu9LfpGuQ93MQSzo0okvRna+7y5ifrD+20FE8FvjusyGaz14fw==}
engines: {node: '>=18'}
+ is-wsl@2.2.0:
+ resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==}
+ engines: {node: '>=8'}
+
+ is-wsl@3.1.1:
+ resolution: {integrity: sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==}
+ engines: {node: '>=16'}
+
+ isarray@1.0.0:
+ resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==}
+
+ isexe@1.1.2:
+ resolution: {integrity: sha512-d2eJzK691yZwPHcv1LbeAOa91yMJ9QmfTgSO1oXB65ezVhXQsxBac2vEB4bMVms9cGzaA99n6V2viHMq82VLDw==}
+
+ isexe@2.0.0:
+ resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
+
+ isobject@3.0.1:
+ resolution: {integrity: sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==}
+ engines: {node: '>=0.10.0'}
+
+ jiti@2.6.1:
+ resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==}
+ hasBin: true
+
+ js-md5@0.8.3:
+ resolution: {integrity: sha512-qR0HB5uP6wCuRMrWPTrkMaev7MJZwJuuw4fnwAzRgP4J4/F8RwtodOKpGp4XpqsLBFzzgqIO42efFAyz2Et6KQ==}
+
js-tokens@4.0.0:
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
+ js-tokens@9.0.1:
+ resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==}
+
+ json-parse-even-better-errors@3.0.2:
+ resolution: {integrity: sha512-fi0NG4bPjCHunUJffmLd0gxssIgkNmArMvis4iNah6Owg1MCJjWhEcDLmsK6iGkJq3tHwbDkTlce70/tmXN4cQ==}
+ engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+
+ json5@2.2.3:
+ resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==}
+ engines: {node: '>=6'}
+ hasBin: true
+
+ jsonfile@6.2.0:
+ resolution: {integrity: sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==}
+
+ jsonwebtoken@9.0.3:
+ resolution: {integrity: sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g==}
+ engines: {node: '>=12', npm: '>=6'}
+
+ jszip@3.10.1:
+ resolution: {integrity: sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==}
+
+ jwa@2.0.1:
+ resolution: {integrity: sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==}
+
+ jws@4.0.1:
+ resolution: {integrity: sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==}
+
katex@0.16.44:
resolution: {integrity: sha512-EkxoDTk8ufHqHlf9QxGwcxeLkWRR3iOuYfRpfORgYfqc8s13bgb+YtRY59NK5ZpRaCwq1kqA6a5lpX8C/eLphQ==}
hasBin: true
+ kleur@3.0.3:
+ resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==}
+ engines: {node: '>=6'}
+
+ ky@1.14.3:
+ resolution: {integrity: sha512-9zy9lkjac+TR1c2tG+mkNSVlyOpInnWdSMiue4F+kq8TwJSgv6o8jhLRg8Ho6SnZ9wOYUq/yozts9qQCfk7bIw==}
+ engines: {node: '>=18'}
+
+ latest-version@9.0.0:
+ resolution: {integrity: sha512-7W0vV3rqv5tokqkBAFV1LbR7HPOWzXQDpDgEuib/aJ1jsZZx6x3c2mBI+TJhJzOhkGeaLbCKEHXEXLfirtG2JA==}
+ engines: {node: '>=18'}
+
+ lie@3.3.0:
+ resolution: {integrity: sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==}
+
+ lighthouse-logger@2.0.2:
+ resolution: {integrity: sha512-vWl2+u5jgOQuZR55Z1WM0XDdrJT6mzMP8zHUct7xTlWhuQs+eV0g+QL0RQdFjT54zVmbhLCP8vIVpy1wGn/gCg==}
+
lightningcss-android-arm64@1.32.0:
resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==}
engines: {node: '>= 12.0.0'}
@@ -917,12 +1747,61 @@ packages:
resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==}
engines: {node: '>= 12.0.0'}
+ lines-and-columns@2.0.4:
+ resolution: {integrity: sha512-wM1+Z03eypVAVUCE7QdSqpVIvelbOakn1M0bPDoA4SGWPx3sNDVUiMo3L6To6WWGClB7VyXnhQ4Sn7gxiJbE6A==}
+ engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+
+ linkedom@0.18.12:
+ resolution: {integrity: sha512-jalJsOwIKuQJSeTvsgzPe9iJzyfVaEJiEXl+25EkKevsULHvMJzpNqwvj1jOESWdmgKDiXObyjOYwlUqG7wo1Q==}
+ engines: {node: '>=16'}
+ peerDependencies:
+ canvas: '>= 2'
+ peerDependenciesMeta:
+ canvas:
+ optional: true
+
+ listr2@10.2.1:
+ resolution: {integrity: sha512-7I5knELsJKTUjXG+A6BkKAiGkW1i25fNa/xlUl9hFtk15WbE9jndA89xu5FzQKrY5llajE1hfZZFMILXkDHk/Q==}
+ engines: {node: '>=22.13.0'}
+
+ local-pkg@1.1.2:
+ resolution: {integrity: sha512-arhlxbFRmoQHl33a0Zkle/YWlmNwoyt6QNZEIJcqNbdrsix5Lvc4HyyI3EnwxTYlZYc32EbYrQ8SzEZ7dqgg9A==}
+ engines: {node: '>=14'}
+
lodash-es@4.17.23:
resolution: {integrity: sha512-kVI48u3PZr38HdYz98UmfPnXl2DXrpdctLrFLCd3kOx1xUkOmpFPx7gCWWM5MPkL/fD8zb+Ph0QzjGFs4+hHWg==}
+ lodash.includes@4.3.0:
+ resolution: {integrity: sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==}
+
+ lodash.isboolean@3.0.3:
+ resolution: {integrity: sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==}
+
+ lodash.isinteger@4.0.4:
+ resolution: {integrity: sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==}
+
+ lodash.isnumber@3.0.3:
+ resolution: {integrity: sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==}
+
+ lodash.isplainobject@4.0.6:
+ resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==}
+
+ lodash.isstring@4.0.1:
+ resolution: {integrity: sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==}
+
+ lodash.merge@4.6.2:
+ resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
+
+ lodash.once@4.1.1:
+ resolution: {integrity: sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==}
+
lodash@4.17.23:
resolution: {integrity: sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==}
+ log-update@6.1.0:
+ resolution: {integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==}
+ engines: {node: '>=18'}
+
longest-streak@3.1.0:
resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==}
@@ -933,9 +1812,26 @@ packages:
magic-string@0.30.21:
resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==}
+ magicast@0.5.2:
+ resolution: {integrity: sha512-E3ZJh4J3S9KfwdjZhe2afj6R9lGIN5Pher1pF39UGrXRqq/VDaGVIGN13BjHd2u8B61hArAGOnso7nBOouW3TQ==}
+
+ make-error@1.3.6:
+ resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==}
+
+ many-keys-map@2.0.1:
+ resolution: {integrity: sha512-DHnZAD4phTbZ+qnJdjoNEVU1NecYoSdbOOoVmTDH46AuxDkEVh3MxTVpXq10GtcTC6mndN9dkv1rNfpjRcLnOw==}
+
markdown-table@3.0.4:
resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==}
+ marked@17.0.5:
+ resolution: {integrity: sha512-6hLvc0/JEbRjRgzI6wnT2P1XuM1/RrrDEX0kPt0N7jGm1133g6X7DlxFasUIx+72aKAr904GTxhSLDrd5DIlZg==}
+ engines: {node: '>= 20'}
+ hasBin: true
+
+ marky@1.3.0:
+ resolution: {integrity: sha512-ocnPZQLNpvbedwTy9kNrQEsknEfgvcLMvOtz3sFeWApDq1MXH1TqkCIx58xlpESsfwQOnuBO9beyQuNGzVvuhQ==}
+
math-intrinsics@1.1.0:
resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
engines: {node: '>= 0.4'}
@@ -1074,15 +1970,40 @@ packages:
resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==}
engines: {node: '>= 0.6'}
+ mimic-function@5.0.1:
+ resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==}
+ engines: {node: '>=18'}
+
+ minimatch@10.2.5:
+ resolution: {integrity: sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==}
+ engines: {node: 18 || 20 || >=22}
+
+ minimatch@3.1.5:
+ resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==}
+
+ minimist@1.2.8:
+ resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==}
+
mitt@3.0.1:
resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==}
+ mlly@1.8.2:
+ resolution: {integrity: sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA==}
+
ms@2.1.3:
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
muggle-string@0.4.1:
resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==}
+ multimatch@6.0.0:
+ resolution: {integrity: sha512-I7tSVxHGPlmPN/enE3mS1aOSo6bWBfls+3HmuEeCUBCE7gWnm3cBXCBkpurzFjVRwC6Kld8lLaZ1Iv5vOcjvcQ==}
+ engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+
+ nano-spawn@2.1.0:
+ resolution: {integrity: sha512-yTW+2okrElHiH4fsiz/+/zc0EDo9BDDoC3iKk8dpv1GeRc9nUWzUZHx6TofMWErchhUQR8hY9/Eu1Uja9x1nqA==}
+ engines: {node: '>=20.17'}
+
nanoid@3.3.11:
resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==}
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
@@ -1096,18 +2017,93 @@ packages:
nanopop@2.4.2:
resolution: {integrity: sha512-NzOgmMQ+elxxHeIha+OG/Pv3Oc3p4RU2aBhwWwAqDpXrdTbtRylbRLQztLy8dMMwfl6pclznBdfUhccEn9ZIzw==}
+ nanospinner@1.2.2:
+ resolution: {integrity: sha512-Zt/AmG6qRU3e+WnzGGLuMCEAO/dAu45stNbHY223tUxldaDAeE+FxSPsd9Q+j+paejmm0ZbrNVs5Sraqy3dRxA==}
+
+ node-fetch-native@1.6.7:
+ resolution: {integrity: sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==}
+
+ node-forge@1.4.0:
+ resolution: {integrity: sha512-LarFH0+6VfriEhqMMcLX2F7SwSXeWwnEAJEsYm5QKWchiVYVvJyV9v7UDvUv+w5HO23ZpQTXDv/GxdDdMyOuoQ==}
+ engines: {node: '>= 6.13.0'}
+
+ node-notifier@10.0.1:
+ resolution: {integrity: sha512-YX7TSyDukOZ0g+gmzjB6abKu+hTGvO8+8+gIFDsRCU2t8fLV/P2unmt+LGFaIa4y64aX98Qksa97rgz4vMNeLQ==}
+
+ normalize-path@3.0.0:
+ resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
+ engines: {node: '>=0.10.0'}
+
+ nth-check@2.1.1:
+ resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==}
+
+ nypm@0.6.5:
+ resolution: {integrity: sha512-K6AJy1GMVyfyMXRVB88700BJqNUkByijGJM8kEHpLdcAt+vSQAVfkWWHYzuRXHSY6xA2sNc5RjTj0p9rE2izVQ==}
+ engines: {node: '>=18'}
+ hasBin: true
+
+ obug@2.1.1:
+ resolution: {integrity: sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==}
+
+ ofetch@1.5.1:
+ resolution: {integrity: sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA==}
+
+ ohash@2.0.11:
+ resolution: {integrity: sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==}
+
+ on-exit-leak-free@2.1.2:
+ resolution: {integrity: sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==}
+ engines: {node: '>=14.0.0'}
+
+ onetime@7.0.0:
+ resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==}
+ engines: {node: '>=18'}
+
+ open@11.0.0:
+ resolution: {integrity: sha512-smsWv2LzFjP03xmvFoJ331ss6h+jixfA4UUV/Bsiyuu4YJPfN+FIQGOIiv4w9/+MoHkfkJ22UIaQWRVFRfH6Vw==}
+ engines: {node: '>=20'}
+
+ open@8.4.2:
+ resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==}
+ engines: {node: '>=12'}
+
orderedmap@2.1.1:
resolution: {integrity: sha512-TvAWxi0nDe1j/rtMcWcIj94+Ffe6n7zhow33h40SKxmsmozs6dz/e+EajymfoFcHd7sxNn8yHM8839uixMOV6g==}
+ os-shim@0.1.3:
+ resolution: {integrity: sha512-jd0cvB8qQ5uVt0lvCIexBaROw1KyKm5sbulg2fWOHjETisuCzWyt+eTZKEMs8v6HwzoGs8xik26jg7eCM6pS+A==}
+ engines: {node: '>= 0.4.0'}
+
+ package-json@10.0.1:
+ resolution: {integrity: sha512-ua1L4OgXSBdsu1FPb7F3tYH0F48a6kxvod4pLUlGY9COeJAJQNX/sNH2IiEmsxw7lqYiAwrdHMjz1FctOsyDQg==}
+ engines: {node: '>=18'}
+
+ pako@1.0.11:
+ resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==}
+
+ parse-json@7.1.1:
+ resolution: {integrity: sha512-SgOTCX/EZXtZxBE5eJ97P4yGM5n37BwRU+YMsH4vNzFqJV/oWFXXCmwFlgWUM4PrakybVOueJJ6pwHqSVhTFDw==}
+ engines: {node: '>=16'}
+
path-browserify@1.0.1:
resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==}
+ pathe@2.0.3:
+ resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==}
+
perfect-debounce@1.0.0:
resolution: {integrity: sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==}
+ perfect-debounce@2.1.0:
+ resolution: {integrity: sha512-LjgdTytVFXeUgtHZr9WYViYSM/g8MkcTPYDlPa3cDqMirHjKiSZPYd6DoL7pK8AJQr+uWkQvCjHNdiMqsrJs+g==}
+
picocolors@1.1.1:
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
+ picomatch@2.3.2:
+ resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==}
+ engines: {node: '>=8.6'}
+
picomatch@4.0.4:
resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==}
engines: {node: '>=12'}
@@ -1121,10 +2117,44 @@ packages:
typescript:
optional: true
+ pino-abstract-transport@2.0.0:
+ resolution: {integrity: sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==}
+
+ pino-std-serializers@7.1.0:
+ resolution: {integrity: sha512-BndPH67/JxGExRgiX1dX0w1FvZck5Wa4aal9198SrRhZjH3GxKQUKIBnYJTdj2HDN3UQAS06HlfcSbQj2OHmaw==}
+
+ pino@9.7.0:
+ resolution: {integrity: sha512-vnMCM6xZTb1WDmLvtG2lE/2p+t9hDEIvTWJsu6FejkE62vB7gDhvzrpFR4Cw2to+9JNQxVnkAKVPA1KPB98vWg==}
+ hasBin: true
+
+ pkg-types@1.3.1:
+ resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==}
+
+ pkg-types@2.3.0:
+ resolution: {integrity: sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==}
+
postcss@8.5.8:
resolution: {integrity: sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==}
engines: {node: ^10 || ^12 || >=14}
+ powershell-utils@0.1.0:
+ resolution: {integrity: sha512-dM0jVuXJPsDN6DvRpea484tCUaMiXWjuCn++HGTqUWzGDjv5tZkEZldAJ/UMlqRYGFrD/etByo4/xOuC/snX2A==}
+ engines: {node: '>=20'}
+
+ process-nextick-args@2.0.1:
+ resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==}
+
+ process-warning@5.0.0:
+ resolution: {integrity: sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==}
+
+ promise-toolbox@0.21.0:
+ resolution: {integrity: sha512-NV8aTmpwrZv+Iys54sSFOBx3tuVaOBvvrft5PNppnxy9xpU/akHbaWIril22AB22zaPgrgwKdD0KsrM0ptUtpg==}
+ engines: {node: '>=6'}
+
+ prompts@2.4.2:
+ resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==}
+ engines: {node: '>= 6'}
+
prosemirror-changeset@2.4.0:
resolution: {integrity: sha512-LvqH2v7Q2SF6yxatuPP2e8vSUKS/L+xAU7dPDC4RMyHMhZoGDfBC74mYuyYF4gLqOEG758wajtyhNnsTkuhvng==}
@@ -1184,10 +2214,54 @@ packages:
prosemirror-view:
optional: true
+ proto-list@1.2.4:
+ resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==}
+
proxy-from-env@2.1.0:
resolution: {integrity: sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==}
engines: {node: '>=10'}
+ publish-browser-extension@4.0.4:
+ resolution: {integrity: sha512-QMQbWL0FWgBfnkJ6w8HOJoIPaWLE7vTpewM4ae2vLs7SrD4eKdAk+SxOzqAICwbhEPuaLAOA+XkT9sZS5R0PmA==}
+ engines: {node: '>=18.0.0'}
+ hasBin: true
+
+ pupa@3.3.0:
+ resolution: {integrity: sha512-LjgDO2zPtoXP2wJpDjZrGdojii1uqO0cnwKoIoUzkfS98HDmbeiGmYiXo3lXeFlq2xvne1QFQhwYXSUCLKtEuA==}
+ engines: {node: '>=12.20'}
+
+ quansync@0.2.11:
+ resolution: {integrity: sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==}
+
+ quick-format-unescaped@4.0.4:
+ resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==}
+
+ rc9@3.0.1:
+ resolution: {integrity: sha512-gMDyleLWVE+i6Sgtc0QbbY6pEKqYs97NGi6isHQPqYlLemPoO8dxQ3uGi0f4NiP98c+jMW6cG1Kx9dDwfvqARQ==}
+
+ rc@1.2.8:
+ resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==}
+ hasBin: true
+
+ readable-stream@2.3.8:
+ resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==}
+
+ readdirp@5.0.0:
+ resolution: {integrity: sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==}
+ engines: {node: '>= 20.19.0'}
+
+ real-require@0.2.0:
+ resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==}
+ engines: {node: '>= 12.13.0'}
+
+ registry-auth-token@5.1.1:
+ resolution: {integrity: sha512-P7B4+jq8DeD2nMsAcdfaqHbssgHtZ7Z5+++a5ask90fvmJ8p5je4mOa+wzu+DB4vQ5tdJV/xywY+UnVFeQLV5Q==}
+ engines: {node: '>=14'}
+
+ registry-url@6.0.1:
+ resolution: {integrity: sha512-+crtS5QjFRqFCoQmvGduwYWEBng99ZvmFvF+cUJkGYF1L1BfU8C6Zp9T7f5vPAwyLkUExpvK+ANVZmGU49qi4Q==}
+ engines: {node: '>=12'}
+
remark-gfm@4.0.1:
resolution: {integrity: sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==}
@@ -1209,9 +2283,17 @@ packages:
remove-accents@0.5.0:
resolution: {integrity: sha512-8g3/Otx1eJaVD12e31UbJj1YzdtVvzH85HV7t+9MJYk/u3XmkOUJ5Ys9wQrf9PCPK8+xn4ymzqYCiZl6QWKn+A==}
+ require-directory@2.1.1:
+ resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
+ engines: {node: '>=0.10.0'}
+
resize-observer-polyfill@1.5.1:
resolution: {integrity: sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==}
+ restore-cursor@5.1.0:
+ resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==}
+ engines: {node: '>=18'}
+
rfdc@1.4.1:
resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==}
@@ -1223,20 +2305,142 @@ packages:
rope-sequence@1.3.4:
resolution: {integrity: sha512-UT5EDe2cu2E/6O4igUr5PSFs23nvvukicWHx6GnOPlHAiiYbzNuCRQCuiUdHJQcqKalLKlrYJnjY0ySGsXNQXQ==}
+ run-applescript@7.1.0:
+ resolution: {integrity: sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==}
+ engines: {node: '>=18'}
+
+ safe-buffer@5.1.2:
+ resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==}
+
+ safe-buffer@5.2.1:
+ resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
+
+ safe-stable-stringify@2.5.0:
+ resolution: {integrity: sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==}
+ engines: {node: '>=10'}
+
+ sax@1.6.0:
+ resolution: {integrity: sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==}
+ engines: {node: '>=11.0.0'}
+
scroll-into-view-if-needed@2.2.31:
resolution: {integrity: sha512-dGCXy99wZQivjmjIqihaBQNjryrz5rueJY7eHfTdyWEiR4ttYpsajb14rn9s5d4DY4EcY6+4+U/maARBXJedkA==}
+ scule@1.3.0:
+ resolution: {integrity: sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g==}
+
+ semver@7.7.4:
+ resolution: {integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==}
+ engines: {node: '>=10'}
+ hasBin: true
+
+ set-value@4.1.0:
+ resolution: {integrity: sha512-zTEg4HL0RwVrqcWs3ztF+x1vkxfm0lP+MQQFPiMJTKVceBwEV0A569Ou8l9IYQG8jOZdMVI1hGsc0tmeD2o/Lw==}
+ engines: {node: '>=11.0'}
+
+ setimmediate@1.0.5:
+ resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==}
+
shallow-equal@1.2.1:
resolution: {integrity: sha512-S4vJDjHHMBaiZuT9NPb616CSmLf618jawtv3sufLl6ivK8WocjAo58cXwbRV1cgqxH0Qbv+iUt6m05eqEa2IRA==}
+ shell-quote@1.7.3:
+ resolution: {integrity: sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw==}
+
+ shellwords@0.1.1:
+ resolution: {integrity: sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==}
+
+ signal-exit@4.1.0:
+ resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
+ engines: {node: '>=14'}
+
+ sisteransi@1.0.5:
+ resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==}
+
+ slice-ansi@7.1.2:
+ resolution: {integrity: sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==}
+ engines: {node: '>=18'}
+
+ slice-ansi@8.0.0:
+ resolution: {integrity: sha512-stxByr12oeeOyY2BlviTNQlYV5xOj47GirPr4yA1hE9JCtxfQN0+tVbkxwCtYDQWhEKWFHsEK48ORg5jrouCAg==}
+ engines: {node: '>=20'}
+
+ sonic-boom@4.2.1:
+ resolution: {integrity: sha512-w6AxtubXa2wTXAUsZMMWERrsIRAdrK0Sc+FUytWvYAhBJLyuI4llrMIC1DtlNSdI99EI86KZum2MMq3EAZlF9Q==}
+
source-map-js@1.2.1:
resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
engines: {node: '>=0.10.0'}
+ source-map-support@0.5.21:
+ resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==}
+
+ source-map@0.6.1:
+ resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
+ engines: {node: '>=0.10.0'}
+
+ source-map@0.7.6:
+ resolution: {integrity: sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==}
+ engines: {node: '>= 12'}
+
+ spawn-sync@1.0.15:
+ resolution: {integrity: sha512-9DWBgrgYZzNghseho0JOuh+5fg9u6QWhAWa51QC7+U5rCheZ/j1DrEZnyE0RBBRqZ9uEXGPgSSM0nky6burpVw==}
+
speakingurl@14.0.1:
resolution: {integrity: sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==}
engines: {node: '>=0.10.0'}
+ split2@4.2.0:
+ resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==}
+ engines: {node: '>= 10.x'}
+
+ split@1.0.1:
+ resolution: {integrity: sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==}
+
+ string-width@4.2.3:
+ resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
+ engines: {node: '>=8'}
+
+ string-width@7.2.0:
+ resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==}
+ engines: {node: '>=18'}
+
+ string-width@8.2.0:
+ resolution: {integrity: sha512-6hJPQ8N0V0P3SNmP6h2J99RLuzrWz2gvT7VnK5tKvrNqJoyS9W4/Fb8mo31UiPvy00z7DQXkP2hnKBVav76thw==}
+ engines: {node: '>=20'}
+
+ string_decoder@1.1.1:
+ resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==}
+
+ strip-ansi@6.0.1:
+ resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
+ engines: {node: '>=8'}
+
+ strip-ansi@7.2.0:
+ resolution: {integrity: sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==}
+ engines: {node: '>=12'}
+
+ strip-bom@5.0.0:
+ resolution: {integrity: sha512-p+byADHF7SzEcVnLvc/r3uognM1hUhObuHXxJcgLCfD194XAkaLbjq3Wzb0N5G2tgIjH0dgT708Z51QxMeu60A==}
+ engines: {node: '>=12'}
+
+ strip-json-comments@2.0.1:
+ resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==}
+ engines: {node: '>=0.10.0'}
+
+ strip-json-comments@5.0.2:
+ resolution: {integrity: sha512-4X2FR3UwhNUE9G49aIsJW5hRRR3GXGTBTZRMfv568O60ojM8HcWjV/VxAxCDW3SUND33O6ZY66ZuRcdkj73q2g==}
+ engines: {node: '>=14.16'}
+
+ strip-literal@3.1.0:
+ resolution: {integrity: sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==}
+
+ stubborn-fs@2.0.0:
+ resolution: {integrity: sha512-Y0AvSwDw8y+nlSNFXMm2g6L51rBGdAQT20J3YSOqxC53Lo3bjWRtr2BKcfYoAf352WYpsZSTURrA0tqhfgudPA==}
+
+ stubborn-utils@1.0.2:
+ resolution: {integrity: sha512-zOh9jPYI+xrNOyisSelgym4tolKTJCQd5GBhK0+0xJvcYDcwlOoxF/rnFKQ2KRZknXSG9jWAp66fwP6AxN9STg==}
+
style-mod@4.1.3:
resolution: {integrity: sha512-i/n8VsZydrugj3Iuzll8+x/00GH2vnYsk1eomD8QiRrSAeW6ItbCQDtfXCeJHd0iwiNagqjQkvpvREEPtW3IoQ==}
@@ -1247,31 +2451,66 @@ packages:
resolution: {integrity: sha512-H+ue8Zo4vJmV2nRjpx86P35lzwDT3nItnIsocgumgr0hHMQ+ZGq5vrERg9kJBo5AWGmxZDhzDo+WVIJqkB0cGA==}
engines: {node: '>=16'}
+ thread-stream@3.1.0:
+ resolution: {integrity: sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==}
+
throttle-debounce@5.0.2:
resolution: {integrity: sha512-B71/4oyj61iNH0KeCamLuE2rmKuTO5byTOSVwECM5FA7TiAiAW+UqTKZ9ERueC4qvgSttUhdmq1mXC3kJqGX7A==}
engines: {node: '>=12.22'}
+ through@2.3.8:
+ resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==}
+
+ tinyexec@1.0.4:
+ resolution: {integrity: sha512-u9r3uZC0bdpGOXtlxUIdwf9pkmvhqJdrVCH9fapQtgy/OeTTMZ1nqH7agtvEfmGui6e1XxjcdrlxvxJvc3sMqw==}
+ engines: {node: '>=18'}
+
tinyglobby@0.2.15:
resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==}
engines: {node: '>=12.0.0'}
+ tmp@0.2.5:
+ resolution: {integrity: sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==}
+ engines: {node: '>=14.14'}
+
trough@2.2.0:
resolution: {integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==}
tslib@2.8.1:
resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
+ type-fest@3.13.1:
+ resolution: {integrity: sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==}
+ engines: {node: '>=14.16'}
+
+ type-fest@4.41.0:
+ resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==}
+ engines: {node: '>=16'}
+
+ typedarray@0.0.6:
+ resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==}
+
typescript@5.9.3:
resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==}
engines: {node: '>=14.17'}
hasBin: true
+ ufo@1.6.3:
+ resolution: {integrity: sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q==}
+
+ uhyphen@0.2.0:
+ resolution: {integrity: sha512-qz3o9CHXmJJPGBdqzab7qAYuW8kQGKNEuoHFYrBwV6hWIMcpAmxDLXojcHfFr9US1Pe6zUswEIJIbLI610fuqA==}
+
undici-types@7.16.0:
resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==}
unified@11.0.5:
resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==}
+ unimport@6.0.2:
+ resolution: {integrity: sha512-ZSOkrDw380w+KIPniY3smyXh2h7H9v2MNr9zejDuh239o5sdea44DRAYrv+rfUi2QGT186P2h0GPGKvy8avQ5g==}
+ engines: {node: '>=18.12.0'}
+
unist-util-is@6.0.1:
resolution: {integrity: sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==}
@@ -1287,12 +2526,40 @@ packages:
unist-util-visit@5.1.0:
resolution: {integrity: sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==}
+ universalify@2.0.1:
+ resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==}
+ engines: {node: '>= 10.0.0'}
+
+ unplugin-utils@0.3.1:
+ resolution: {integrity: sha512-5lWVjgi6vuHhJ526bI4nlCOmkCIF3nnfXkCMDeMJrtdvxTs6ZFCM8oNufGTsDbKv/tJ/xj8RpvXjRuPBZJuJog==}
+ engines: {node: '>=20.19.0'}
+
+ unplugin@3.0.0:
+ resolution: {integrity: sha512-0Mqk3AT2TZCXWKdcoaufeXNukv2mTrEZExeXlHIOZXdqYoHHr4n51pymnwV8x2BOVxwXbK2HLlI7usrqMpycdg==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+
+ update-notifier@7.3.1:
+ resolution: {integrity: sha512-+dwUY4L35XFYEzE+OAL3sarJdUioVovq+8f7lcIJ7wnmnYQV5UD1Y/lcwaMSyaQ6Bj3JMj1XSTjZbNLHn/19yA==}
+ engines: {node: '>=18'}
+
+ util-deprecate@1.0.2:
+ resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
+
+ uuid@8.3.2:
+ resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==}
+ hasBin: true
+
vfile-message@4.0.3:
resolution: {integrity: sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==}
vfile@6.0.3:
resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==}
+ vite-node@6.0.0:
+ resolution: {integrity: sha512-oj4PVrT+pDh6GYf5wfUXkcZyekYS8kKPfLPXVl8qe324Ec6l4K2DUKNadRbZ3LQl0qGcDz+PyOo7ZAh00Y+JjQ==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ hasBin: true
+
vite@8.0.3:
resolution: {integrity: sha512-B9ifbFudT1TFhfltfaIPgjo9Z3mDynBTJSUYxTjOQruf/zHH+ezCQKcoqO+h7a9Pw9Nm/OtlXAiGT1axBgwqrQ==}
engines: {node: ^20.19.0 || >=22.12.0}
@@ -1388,6 +2655,91 @@ packages:
warning@4.0.3:
resolution: {integrity: sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==}
+ watchpack@2.4.4:
+ resolution: {integrity: sha512-c5EGNOiyxxV5qmTtAB7rbiXxi1ooX1pQKMLX/MIabJjRA0SJBQOjKF+KSVfHkr9U1cADPon0mRiVe/riyaiDUA==}
+ engines: {node: '>=10.13.0'}
+
+ web-ext-run@0.2.4:
+ resolution: {integrity: sha512-rQicL7OwuqWdQWI33JkSXKcp7cuv1mJG8u3jRQwx/8aDsmhbTHs9ZRmNYOL+LX0wX8edIEQX8jj4bB60GoXtKA==}
+ engines: {node: '>=18.0.0', npm: '>=8.0.0'}
+
+ webpack-virtual-modules@0.6.2:
+ resolution: {integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==}
+
+ when-exit@2.1.5:
+ resolution: {integrity: sha512-VGkKJ564kzt6Ms1dbgPP/yuIoQCrsFAnRbptpC5wOEsDaNsbCB2bnfnaA8i/vRs5tjUSEOtIuvl9/MyVsvQZCg==}
+
+ when@3.7.7:
+ resolution: {integrity: sha512-9lFZp/KHoqH6bPKjbWqa+3Dg/K/r2v0X/3/G2x4DBGchVS2QX2VXL3cZV994WQVnTM1/PD71Az25nAzryEUugw==}
+
+ which@1.2.4:
+ resolution: {integrity: sha512-zDRAqDSBudazdfM9zpiI30Fu9ve47htYXcGi3ln0wfKu2a7SmrT6F3VDoYONu//48V8Vz4TdCRNPjtvyRO3yBA==}
+ hasBin: true
+
+ which@2.0.2:
+ resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
+ engines: {node: '>= 8'}
+ hasBin: true
+
+ widest-line@5.0.0:
+ resolution: {integrity: sha512-c9bZp7b5YtRj2wOe6dlj32MK+Bx/M/d+9VB2SHM1OtsUHR0aV0tdP6DWh/iMt0kWi1t5g1Iudu6hQRNd1A4PVA==}
+ engines: {node: '>=18'}
+
+ winreg@0.0.12:
+ resolution: {integrity: sha512-typ/+JRmi7RqP1NanzFULK36vczznSNN8kWVA9vIqXyv8GhghUlwhGp1Xj3Nms1FsPcNnsQrJOR10N58/nQ9hQ==}
+
+ wrap-ansi@10.0.0:
+ resolution: {integrity: sha512-SGcvg80f0wUy2/fXES19feHMz8E0JoXv2uNgHOu4Dgi2OrCy1lqwFYEJz1BLbDI0exjPMe/ZdzZ/YpGECBG/aQ==}
+ engines: {node: '>=20'}
+
+ wrap-ansi@7.0.0:
+ resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
+ engines: {node: '>=10'}
+
+ wrap-ansi@9.0.2:
+ resolution: {integrity: sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==}
+ engines: {node: '>=18'}
+
+ wsl-utils@0.3.1:
+ resolution: {integrity: sha512-g/eziiSUNBSsdDJtCLB8bdYEUMj4jR7AGeUo96p/3dTafgjHhpF4RiCFPiRILwjQoDXx5MqkBr4fwWtR3Ky4Wg==}
+ engines: {node: '>=20'}
+
+ wxt@0.20.20:
+ resolution: {integrity: sha512-OGvOD1YEXwasjlOmfYzCGlIa88Jm9mxjM+hqx7zw+Xctg+TKjhF1bIt7vVJ1oT1t4RqvczTAcD2mUduiDltZaw==}
+ hasBin: true
+ peerDependencies:
+ eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
+ peerDependenciesMeta:
+ eslint:
+ optional: true
+
+ xdg-basedir@5.1.0:
+ resolution: {integrity: sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ==}
+ engines: {node: '>=12'}
+
+ xml2js@0.6.2:
+ resolution: {integrity: sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==}
+ engines: {node: '>=4.0.0'}
+
+ xmlbuilder@11.0.1:
+ resolution: {integrity: sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==}
+ engines: {node: '>=4.0'}
+
+ y18n@5.0.8:
+ resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
+ engines: {node: '>=10'}
+
+ yargs-parser@21.1.1:
+ resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==}
+ engines: {node: '>=12'}
+
+ yargs@17.7.2:
+ resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==}
+ engines: {node: '>=12'}
+
+ zip-dir@2.0.0:
+ resolution: {integrity: sha512-uhlsJZWz26FLYXOD6WVuq+fIcZ3aBPGo/cFdiLlv3KNwpa52IF3ISV8fLhQLiqVu5No3VhlqlgthN6gehil1Dg==}
+
zod@4.3.6:
resolution: {integrity: sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==}
@@ -1396,6 +2748,18 @@ packages:
snapshots:
+ '@1natsu/wait-element@4.1.2':
+ dependencies:
+ defu: 6.1.6
+ many-keys-map: 2.0.1
+
+ '@aklinker1/rollup-plugin-visualizer@5.12.0':
+ dependencies:
+ open: 8.4.2
+ picomatch: 2.3.2
+ source-map: 0.7.6
+ yargs: 17.7.2
+
'@ant-design/colors@6.0.0':
dependencies:
'@ctrl/tinycolor': 3.6.1
@@ -1408,6 +2772,12 @@ snapshots:
'@ant-design/icons-svg': 4.4.2
vue: 3.5.31(typescript@5.9.3)
+ '@babel/code-frame@7.29.0':
+ dependencies:
+ '@babel/helper-validator-identifier': 7.28.5
+ js-tokens: 4.0.0
+ picocolors: 1.1.1
+
'@babel/helper-string-parser@7.27.1': {}
'@babel/helper-validator-identifier@7.28.5': {}
@@ -1416,6 +2786,8 @@ snapshots:
dependencies:
'@babel/types': 7.29.0
+ '@babel/runtime@7.28.2': {}
+
'@babel/runtime@7.29.2': {}
'@babel/types@7.29.0':
@@ -1680,6 +3052,22 @@ snapshots:
'@ctrl/tinycolor@3.6.1': {}
+ '@devicefarmer/adbkit-logcat@2.1.3': {}
+
+ '@devicefarmer/adbkit-monkey@1.2.1': {}
+
+ '@devicefarmer/adbkit@3.3.8':
+ dependencies:
+ '@devicefarmer/adbkit-logcat': 2.1.3
+ '@devicefarmer/adbkit-monkey': 1.2.1
+ bluebird: 3.7.2
+ commander: 9.5.0
+ debug: 4.3.7
+ node-forge: 1.4.0
+ split: 1.0.1
+ transitivePeerDependencies:
+ - supports-color
+
'@emnapi/core@1.9.1':
dependencies:
'@emnapi/wasi-threads': 1.2.0
@@ -1700,6 +3088,84 @@ snapshots:
'@emotion/unitless@0.8.1': {}
+ '@esbuild/aix-ppc64@0.27.5':
+ optional: true
+
+ '@esbuild/android-arm64@0.27.5':
+ optional: true
+
+ '@esbuild/android-arm@0.27.5':
+ optional: true
+
+ '@esbuild/android-x64@0.27.5':
+ optional: true
+
+ '@esbuild/darwin-arm64@0.27.5':
+ optional: true
+
+ '@esbuild/darwin-x64@0.27.5':
+ optional: true
+
+ '@esbuild/freebsd-arm64@0.27.5':
+ optional: true
+
+ '@esbuild/freebsd-x64@0.27.5':
+ optional: true
+
+ '@esbuild/linux-arm64@0.27.5':
+ optional: true
+
+ '@esbuild/linux-arm@0.27.5':
+ optional: true
+
+ '@esbuild/linux-ia32@0.27.5':
+ optional: true
+
+ '@esbuild/linux-loong64@0.27.5':
+ optional: true
+
+ '@esbuild/linux-mips64el@0.27.5':
+ optional: true
+
+ '@esbuild/linux-ppc64@0.27.5':
+ optional: true
+
+ '@esbuild/linux-riscv64@0.27.5':
+ optional: true
+
+ '@esbuild/linux-s390x@0.27.5':
+ optional: true
+
+ '@esbuild/linux-x64@0.27.5':
+ optional: true
+
+ '@esbuild/netbsd-arm64@0.27.5':
+ optional: true
+
+ '@esbuild/netbsd-x64@0.27.5':
+ optional: true
+
+ '@esbuild/openbsd-arm64@0.27.5':
+ optional: true
+
+ '@esbuild/openbsd-x64@0.27.5':
+ optional: true
+
+ '@esbuild/openharmony-arm64@0.27.5':
+ optional: true
+
+ '@esbuild/sunos-x64@0.27.5':
+ optional: true
+
+ '@esbuild/win32-arm64@0.27.5':
+ optional: true
+
+ '@esbuild/win32-ia32@0.27.5':
+ optional: true
+
+ '@esbuild/win32-x64@0.27.5':
+ optional: true
+
'@floating-ui/core@1.7.5':
dependencies:
'@floating-ui/utils': 0.2.11
@@ -1723,8 +3189,25 @@ snapshots:
'@intlify/shared@10.0.8': {}
+ '@jridgewell/gen-mapping@0.3.13':
+ dependencies:
+ '@jridgewell/sourcemap-codec': 1.5.5
+ '@jridgewell/trace-mapping': 0.3.31
+
+ '@jridgewell/remapping@2.3.5':
+ dependencies:
+ '@jridgewell/gen-mapping': 0.3.13
+ '@jridgewell/trace-mapping': 0.3.31
+
+ '@jridgewell/resolve-uri@3.1.2': {}
+
'@jridgewell/sourcemap-codec@1.5.5': {}
+ '@jridgewell/trace-mapping@0.3.31':
+ dependencies:
+ '@jridgewell/resolve-uri': 3.1.2
+ '@jridgewell/sourcemap-codec': 1.5.5
+
'@lezer/common@1.5.1': {}
'@lezer/cpp@1.1.5':
@@ -2113,6 +3596,18 @@ snapshots:
'@oxc-project/types@0.122.0': {}
+ '@pnpm/config.env-replace@1.1.0': {}
+
+ '@pnpm/network.ca-file@1.0.2':
+ dependencies:
+ graceful-fs: 4.2.10
+
+ '@pnpm/npm-conf@3.0.2':
+ dependencies:
+ '@pnpm/config.env-replace': 1.1.0
+ '@pnpm/network.ca-file': 1.0.2
+ config-chain: 1.1.13
+
'@rolldown/binding-android-arm64@1.0.0-rc.12':
optional: true
@@ -2191,10 +3686,25 @@ snapshots:
tslib: 2.8.1
optional: true
+ '@types/chrome@0.1.38':
+ dependencies:
+ '@types/filesystem': 0.0.36
+ '@types/har-format': 1.2.16
+
'@types/debug@4.1.13':
dependencies:
'@types/ms': 2.1.0
+ '@types/estree@1.0.8': {}
+
+ '@types/filesystem@0.0.36':
+ dependencies:
+ '@types/filewriter': 0.0.33
+
+ '@types/filewriter@0.0.33': {}
+
+ '@types/har-format@1.2.16': {}
+
'@types/hast@3.0.4':
dependencies:
'@types/unist': 3.0.3
@@ -2211,6 +3721,8 @@ snapshots:
dependencies:
'@types/unist': 3.0.3
+ '@types/minimatch@3.0.5': {}
+
'@types/ms@2.1.0': {}
'@types/node@24.12.0':
@@ -2224,10 +3736,10 @@ snapshots:
'@types/web-bluetooth@0.0.21': {}
- '@vitejs/plugin-vue@6.0.5(vite@8.0.3(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@24.12.0))(vue@3.5.31(typescript@5.9.3))':
+ '@vitejs/plugin-vue@6.0.5(vite@8.0.3(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@24.12.0)(esbuild@0.27.5)(jiti@2.6.1))(vue@3.5.31(typescript@5.9.3))':
dependencies:
'@rolldown/pluginutils': 1.0.0-rc.2
- vite: 8.0.3(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@24.12.0)
+ vite: 8.0.3(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@24.12.0)(esbuild@0.27.5)(jiti@2.6.1)
vue: 3.5.31(typescript@5.9.3)
'@volar/language-core@2.4.28':
@@ -2339,8 +3851,59 @@ snapshots:
dependencies:
vue: 3.5.31(typescript@5.9.3)
+ '@webext-core/fake-browser@1.3.4':
+ dependencies:
+ lodash.merge: 4.6.2
+
+ '@webext-core/isolated-element@1.1.5':
+ dependencies:
+ is-potential-custom-element-name: 1.0.1
+
+ '@webext-core/match-patterns@1.0.3': {}
+
+ '@wxt-dev/browser@0.1.38':
+ dependencies:
+ '@types/filesystem': 0.0.36
+ '@types/har-format': 1.2.16
+
+ '@wxt-dev/module-vue@1.0.3(vite@8.0.3(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@24.12.0)(esbuild@0.27.5)(jiti@2.6.1))(vue@3.5.31(typescript@5.9.3))(wxt@0.20.20(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@24.12.0)(jiti@2.6.1))':
+ dependencies:
+ '@vitejs/plugin-vue': 6.0.5(vite@8.0.3(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@24.12.0)(esbuild@0.27.5)(jiti@2.6.1))(vue@3.5.31(typescript@5.9.3))
+ wxt: 0.20.20(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@24.12.0)(jiti@2.6.1)
+ transitivePeerDependencies:
+ - vite
+ - vue
+
+ '@wxt-dev/storage@1.2.8':
+ dependencies:
+ '@wxt-dev/browser': 0.1.38
+ async-mutex: 0.5.0
+ dequal: 2.0.3
+
+ acorn@8.16.0: {}
+
+ adm-zip@0.5.17: {}
+
alien-signals@3.1.2: {}
+ ansi-align@3.0.1:
+ dependencies:
+ string-width: 4.2.3
+
+ ansi-escapes@7.3.0:
+ dependencies:
+ environment: 1.1.0
+
+ ansi-regex@5.0.1: {}
+
+ ansi-regex@6.2.2: {}
+
+ ansi-styles@4.3.0:
+ dependencies:
+ color-convert: 2.0.1
+
+ ansi-styles@6.2.3: {}
+
ant-design-vue@4.2.6(vue@3.5.31(typescript@5.9.3)):
dependencies:
'@ant-design/colors': 6.0.0
@@ -2367,12 +3930,29 @@ snapshots:
vue-types: 3.0.2(vue@3.5.31(typescript@5.9.3))
warning: 4.0.3
+ array-differ@4.0.0: {}
+
array-tree-filter@2.1.0: {}
+ array-union@3.0.1: {}
+
+ async-mutex@0.5.0:
+ dependencies:
+ tslib: 2.8.1
+
async-validator@4.2.5: {}
+ async@3.2.6: {}
+
asynckit@0.4.0: {}
+ atomic-sleep@1.0.0: {}
+
+ atomically@2.1.1:
+ dependencies:
+ stubborn-fs: 2.0.0
+ when-exit: 2.1.5
+
axios@1.14.0:
dependencies:
follow-redirects: 1.15.11
@@ -2383,17 +3963,112 @@ snapshots:
bail@2.0.2: {}
+ balanced-match@1.0.2: {}
+
+ balanced-match@4.0.4: {}
+
birpc@2.9.0: {}
+ bluebird@3.7.2: {}
+
+ boolbase@1.0.0: {}
+
+ boxen@8.0.1:
+ dependencies:
+ ansi-align: 3.0.1
+ camelcase: 8.0.0
+ chalk: 5.6.2
+ cli-boxes: 3.0.0
+ string-width: 7.2.0
+ type-fest: 4.41.0
+ widest-line: 5.0.0
+ wrap-ansi: 9.0.2
+
+ brace-expansion@1.1.13:
+ dependencies:
+ balanced-match: 1.0.2
+ concat-map: 0.0.1
+
+ brace-expansion@5.0.5:
+ dependencies:
+ balanced-match: 4.0.4
+
+ buffer-equal-constant-time@1.0.1: {}
+
+ buffer-from@1.1.2: {}
+
+ bundle-name@4.1.0:
+ dependencies:
+ run-applescript: 7.1.0
+
+ c12@3.3.4(magicast@0.5.2):
+ dependencies:
+ chokidar: 5.0.0
+ confbox: 0.2.4
+ defu: 6.1.6
+ dotenv: 17.4.0
+ exsolve: 1.0.8
+ giget: 3.2.0
+ jiti: 2.6.1
+ ohash: 2.0.11
+ pathe: 2.0.3
+ perfect-debounce: 2.1.0
+ pkg-types: 2.3.0
+ rc9: 3.0.1
+ optionalDependencies:
+ magicast: 0.5.2
+
+ cac@6.7.14: {}
+
+ cac@7.0.0: {}
+
call-bind-apply-helpers@1.0.2:
dependencies:
es-errors: 1.3.0
function-bind: 1.1.2
+ camelcase@8.0.0: {}
+
ccount@2.0.1: {}
+ chalk@5.6.2: {}
+
character-entities@2.0.2: {}
+ chokidar@5.0.0:
+ dependencies:
+ readdirp: 5.0.0
+
+ chrome-launcher@1.2.0:
+ dependencies:
+ '@types/node': 24.12.0
+ escape-string-regexp: 4.0.0
+ is-wsl: 2.2.0
+ lighthouse-logger: 2.0.2
+ transitivePeerDependencies:
+ - supports-color
+
+ ci-info@4.4.0: {}
+
+ citty@0.2.2: {}
+
+ cli-boxes@3.0.0: {}
+
+ cli-cursor@5.0.0:
+ dependencies:
+ restore-cursor: 5.1.0
+
+ cli-truncate@5.2.0:
+ dependencies:
+ slice-ansi: 8.0.0
+ string-width: 8.2.0
+
+ cliui@8.0.1:
+ dependencies:
+ string-width: 4.2.3
+ strip-ansi: 6.0.1
+ wrap-ansi: 7.0.0
+
clsx@2.1.1: {}
codemirror@6.0.2:
@@ -2406,26 +4081,85 @@ snapshots:
'@codemirror/state': 6.6.0
'@codemirror/view': 6.40.0
+ color-convert@2.0.1:
+ dependencies:
+ color-name: 1.1.4
+
+ color-name@1.1.4: {}
+
combined-stream@1.0.8:
dependencies:
delayed-stream: 1.0.0
+ commander@2.9.0:
+ dependencies:
+ graceful-readlink: 1.0.1
+
commander@8.3.0: {}
+ commander@9.5.0: {}
+
compute-scroll-into-view@1.0.20: {}
+ concat-map@0.0.1: {}
+
+ concat-stream@1.6.2:
+ dependencies:
+ buffer-from: 1.1.2
+ inherits: 2.0.4
+ readable-stream: 2.3.8
+ typedarray: 0.0.6
+
+ confbox@0.1.8: {}
+
+ confbox@0.2.4: {}
+
+ config-chain@1.1.13:
+ dependencies:
+ ini: 1.3.8
+ proto-list: 1.2.4
+
+ configstore@7.1.0:
+ dependencies:
+ atomically: 2.1.1
+ dot-prop: 9.0.0
+ graceful-fs: 4.2.11
+ xdg-basedir: 5.1.0
+
+ consola@3.4.2: {}
+
copy-anything@4.0.5:
dependencies:
is-what: 5.5.0
core-js@3.49.0: {}
+ core-util-is@1.0.3: {}
+
crelt@1.0.6: {}
+ css-select@5.2.2:
+ dependencies:
+ boolbase: 1.0.0
+ css-what: 6.2.2
+ domhandler: 5.0.3
+ domutils: 3.2.2
+ nth-check: 2.1.1
+
+ css-what@6.2.2: {}
+
+ cssom@0.5.0: {}
+
csstype@3.2.3: {}
dayjs@1.11.20: {}
+ debounce@1.2.1: {}
+
+ debug@4.3.7:
+ dependencies:
+ ms: 2.1.3
+
debug@4.4.3:
dependencies:
ms: 2.1.3
@@ -2434,10 +4168,27 @@ snapshots:
dependencies:
character-entities: 2.0.2
+ deep-extend@0.6.0: {}
+
+ default-browser-id@5.0.1: {}
+
+ default-browser@5.5.0:
+ dependencies:
+ bundle-name: 4.1.0
+ default-browser-id: 5.0.1
+
+ define-lazy-prop@2.0.0: {}
+
+ define-lazy-prop@3.0.0: {}
+
+ defu@6.1.6: {}
+
delayed-stream@1.0.0: {}
dequal@2.0.3: {}
+ destr@2.0.5: {}
+
detect-libc@2.1.2: {}
devlop@1.1.0:
@@ -2448,22 +4199,70 @@ snapshots:
dom-scroll-into-view@2.0.1: {}
+ dom-serializer@2.0.0:
+ dependencies:
+ domelementtype: 2.3.0
+ domhandler: 5.0.3
+ entities: 4.5.0
+
+ domelementtype@2.3.0: {}
+
+ domhandler@5.0.3:
+ dependencies:
+ domelementtype: 2.3.0
+
dompurify@3.3.3:
optionalDependencies:
'@types/trusted-types': 2.0.7
+ domutils@3.2.2:
+ dependencies:
+ dom-serializer: 2.0.0
+ domelementtype: 2.3.0
+ domhandler: 5.0.3
+
+ dot-prop@9.0.0:
+ dependencies:
+ type-fest: 4.41.0
+
+ dotenv-expand@12.0.3:
+ dependencies:
+ dotenv: 16.6.1
+
+ dotenv@16.6.1: {}
+
+ dotenv@17.4.0: {}
+
dunder-proto@1.0.1:
dependencies:
call-bind-apply-helpers: 1.0.2
es-errors: 1.3.0
gopd: 1.2.0
+ ecdsa-sig-formatter@1.0.11:
+ dependencies:
+ safe-buffer: 5.2.1
+
+ emoji-regex@10.6.0: {}
+
+ emoji-regex@8.0.0: {}
+
+ entities@4.5.0: {}
+
entities@7.0.1: {}
+ environment@1.1.0: {}
+
+ error-ex@1.3.4:
+ dependencies:
+ is-arrayish: 0.2.1
+
es-define-property@1.0.1: {}
es-errors@1.3.0: {}
+ es-module-lexer@2.0.0: {}
+
es-object-atoms@1.1.1:
dependencies:
es-errors: 1.3.0
@@ -2475,18 +4274,77 @@ snapshots:
has-tostringtag: 1.0.2
hasown: 2.0.2
+ es6-error@4.1.1: {}
+
+ esbuild@0.27.5:
+ optionalDependencies:
+ '@esbuild/aix-ppc64': 0.27.5
+ '@esbuild/android-arm': 0.27.5
+ '@esbuild/android-arm64': 0.27.5
+ '@esbuild/android-x64': 0.27.5
+ '@esbuild/darwin-arm64': 0.27.5
+ '@esbuild/darwin-x64': 0.27.5
+ '@esbuild/freebsd-arm64': 0.27.5
+ '@esbuild/freebsd-x64': 0.27.5
+ '@esbuild/linux-arm': 0.27.5
+ '@esbuild/linux-arm64': 0.27.5
+ '@esbuild/linux-ia32': 0.27.5
+ '@esbuild/linux-loong64': 0.27.5
+ '@esbuild/linux-mips64el': 0.27.5
+ '@esbuild/linux-ppc64': 0.27.5
+ '@esbuild/linux-riscv64': 0.27.5
+ '@esbuild/linux-s390x': 0.27.5
+ '@esbuild/linux-x64': 0.27.5
+ '@esbuild/netbsd-arm64': 0.27.5
+ '@esbuild/netbsd-x64': 0.27.5
+ '@esbuild/openbsd-arm64': 0.27.5
+ '@esbuild/openbsd-x64': 0.27.5
+ '@esbuild/openharmony-arm64': 0.27.5
+ '@esbuild/sunos-x64': 0.27.5
+ '@esbuild/win32-arm64': 0.27.5
+ '@esbuild/win32-ia32': 0.27.5
+ '@esbuild/win32-x64': 0.27.5
+
+ escalade@3.2.0: {}
+
+ escape-goat@4.0.0: {}
+
+ escape-string-regexp@4.0.0: {}
+
escape-string-regexp@5.0.0: {}
estree-walker@2.0.2: {}
+ estree-walker@3.0.3:
+ dependencies:
+ '@types/estree': 1.0.8
+
+ eventemitter3@5.0.4: {}
+
+ exsolve@1.0.8: {}
+
extend@3.0.2: {}
+ fast-redact@3.5.0: {}
+
fdir@6.5.0(picomatch@4.0.4):
optionalDependencies:
picomatch: 4.0.4
+ filesize@11.0.15: {}
+
+ firefox-profile@4.7.0:
+ dependencies:
+ adm-zip: 0.5.17
+ fs-extra: 11.3.4
+ ini: 4.1.3
+ minimist: 1.2.8
+ xml2js: 0.6.2
+
follow-redirects@1.15.11: {}
+ form-data-encoder@4.1.0: {}
+
form-data@4.0.5:
dependencies:
asynckit: 0.4.0
@@ -2495,11 +4353,32 @@ snapshots:
hasown: 2.0.2
mime-types: 2.1.35
+ formdata-node@6.0.3: {}
+
+ fs-extra@11.3.4:
+ dependencies:
+ graceful-fs: 4.2.11
+ jsonfile: 6.2.0
+ universalify: 2.0.1
+
fsevents@2.3.3:
optional: true
function-bind@1.1.2: {}
+ fx-runner@1.4.0:
+ dependencies:
+ commander: 2.9.0
+ shell-quote: 1.7.3
+ spawn-sync: 1.0.15
+ when: 3.7.7
+ which: 1.2.4
+ winreg: 0.0.12
+
+ get-caller-file@2.0.5: {}
+
+ get-east-asian-width@1.5.0: {}
+
get-intrinsic@1.3.0:
dependencies:
call-bind-apply-helpers: 1.0.2
@@ -2513,13 +4392,31 @@ snapshots:
hasown: 2.0.2
math-intrinsics: 1.1.0
+ get-port-please@3.2.0: {}
+
get-proto@1.0.1:
dependencies:
dunder-proto: 1.0.1
es-object-atoms: 1.1.1
+ giget@3.2.0: {}
+
+ glob-to-regexp@0.4.1: {}
+
+ global-directory@4.0.1:
+ dependencies:
+ ini: 4.1.1
+
gopd@1.2.0: {}
+ graceful-fs@4.2.10: {}
+
+ graceful-fs@4.2.11: {}
+
+ graceful-readlink@1.0.1: {}
+
+ growly@1.3.0: {}
+
has-symbols@1.1.0: {}
has-tostringtag@1.0.2:
@@ -2532,18 +4429,166 @@ snapshots:
hookable@5.5.3: {}
+ hookable@6.1.0: {}
+
+ html-escaper@3.0.3: {}
+
+ htmlparser2@10.1.0:
+ dependencies:
+ domelementtype: 2.3.0
+ domhandler: 5.0.3
+ domutils: 3.2.2
+ entities: 7.0.1
+
+ immediate@3.0.6: {}
+
+ import-meta-resolve@4.2.0: {}
+
+ inherits@2.0.4: {}
+
+ ini@1.3.8: {}
+
+ ini@4.1.1: {}
+
+ ini@4.1.3: {}
+
+ is-absolute@0.1.7:
+ dependencies:
+ is-relative: 0.1.3
+
+ is-arrayish@0.2.1: {}
+
+ is-docker@2.2.1: {}
+
+ is-docker@3.0.0: {}
+
+ is-fullwidth-code-point@3.0.0: {}
+
+ is-fullwidth-code-point@5.1.0:
+ dependencies:
+ get-east-asian-width: 1.5.0
+
+ is-in-ci@1.0.0: {}
+
+ is-in-ssh@1.0.0: {}
+
+ is-inside-container@1.0.0:
+ dependencies:
+ is-docker: 3.0.0
+
+ is-installed-globally@1.0.0:
+ dependencies:
+ global-directory: 4.0.1
+ is-path-inside: 4.0.0
+
+ is-npm@6.1.0: {}
+
+ is-path-inside@4.0.0: {}
+
is-plain-obj@4.1.0: {}
+ is-plain-object@2.0.4:
+ dependencies:
+ isobject: 3.0.1
+
is-plain-object@3.0.1: {}
+ is-potential-custom-element-name@1.0.1: {}
+
+ is-primitive@3.0.1: {}
+
+ is-relative@0.1.3: {}
+
is-what@5.5.0: {}
+ is-wsl@2.2.0:
+ dependencies:
+ is-docker: 2.2.1
+
+ is-wsl@3.1.1:
+ dependencies:
+ is-inside-container: 1.0.0
+
+ isarray@1.0.0: {}
+
+ isexe@1.1.2: {}
+
+ isexe@2.0.0: {}
+
+ isobject@3.0.1: {}
+
+ jiti@2.6.1: {}
+
+ js-md5@0.8.3: {}
+
js-tokens@4.0.0: {}
+ js-tokens@9.0.1: {}
+
+ json-parse-even-better-errors@3.0.2: {}
+
+ json5@2.2.3: {}
+
+ jsonfile@6.2.0:
+ dependencies:
+ universalify: 2.0.1
+ optionalDependencies:
+ graceful-fs: 4.2.11
+
+ jsonwebtoken@9.0.3:
+ dependencies:
+ jws: 4.0.1
+ lodash.includes: 4.3.0
+ lodash.isboolean: 3.0.3
+ lodash.isinteger: 4.0.4
+ lodash.isnumber: 3.0.3
+ lodash.isplainobject: 4.0.6
+ lodash.isstring: 4.0.1
+ lodash.once: 4.1.1
+ ms: 2.1.3
+ semver: 7.7.4
+
+ jszip@3.10.1:
+ dependencies:
+ lie: 3.3.0
+ pako: 1.0.11
+ readable-stream: 2.3.8
+ setimmediate: 1.0.5
+
+ jwa@2.0.1:
+ dependencies:
+ buffer-equal-constant-time: 1.0.1
+ ecdsa-sig-formatter: 1.0.11
+ safe-buffer: 5.2.1
+
+ jws@4.0.1:
+ dependencies:
+ jwa: 2.0.1
+ safe-buffer: 5.2.1
+
katex@0.16.44:
dependencies:
commander: 8.3.0
+ kleur@3.0.3: {}
+
+ ky@1.14.3: {}
+
+ latest-version@9.0.0:
+ dependencies:
+ package-json: 10.0.1
+
+ lie@3.3.0:
+ dependencies:
+ immediate: 3.0.6
+
+ lighthouse-logger@2.0.2:
+ dependencies:
+ debug: 4.4.3
+ marky: 1.3.0
+ transitivePeerDependencies:
+ - supports-color
+
lightningcss-android-arm64@1.32.0:
optional: true
@@ -2593,10 +4638,58 @@ snapshots:
lightningcss-win32-arm64-msvc: 1.32.0
lightningcss-win32-x64-msvc: 1.32.0
+ lines-and-columns@2.0.4: {}
+
+ linkedom@0.18.12:
+ dependencies:
+ css-select: 5.2.2
+ cssom: 0.5.0
+ html-escaper: 3.0.3
+ htmlparser2: 10.1.0
+ uhyphen: 0.2.0
+
+ listr2@10.2.1:
+ dependencies:
+ cli-truncate: 5.2.0
+ eventemitter3: 5.0.4
+ log-update: 6.1.0
+ rfdc: 1.4.1
+ wrap-ansi: 10.0.0
+
+ local-pkg@1.1.2:
+ dependencies:
+ mlly: 1.8.2
+ pkg-types: 2.3.0
+ quansync: 0.2.11
+
lodash-es@4.17.23: {}
+ lodash.includes@4.3.0: {}
+
+ lodash.isboolean@3.0.3: {}
+
+ lodash.isinteger@4.0.4: {}
+
+ lodash.isnumber@3.0.3: {}
+
+ lodash.isplainobject@4.0.6: {}
+
+ lodash.isstring@4.0.1: {}
+
+ lodash.merge@4.6.2: {}
+
+ lodash.once@4.1.1: {}
+
lodash@4.17.23: {}
+ log-update@6.1.0:
+ dependencies:
+ ansi-escapes: 7.3.0
+ cli-cursor: 5.0.0
+ slice-ansi: 7.1.2
+ strip-ansi: 7.2.0
+ wrap-ansi: 9.0.2
+
longest-streak@3.1.0: {}
loose-envify@1.4.0:
@@ -2607,8 +4700,22 @@ snapshots:
dependencies:
'@jridgewell/sourcemap-codec': 1.5.5
+ magicast@0.5.2:
+ dependencies:
+ '@babel/parser': 7.29.2
+ '@babel/types': 7.29.0
+ source-map-js: 1.2.1
+
+ make-error@1.3.6: {}
+
+ many-keys-map@2.0.1: {}
+
markdown-table@3.0.4: {}
+ marked@17.0.5: {}
+
+ marky@1.3.0: {}
+
math-intrinsics@1.1.0: {}
mdast-util-definitions@6.0.0:
@@ -2938,26 +5045,139 @@ snapshots:
dependencies:
mime-db: 1.52.0
+ mimic-function@5.0.1: {}
+
+ minimatch@10.2.5:
+ dependencies:
+ brace-expansion: 5.0.5
+
+ minimatch@3.1.5:
+ dependencies:
+ brace-expansion: 1.1.13
+
+ minimist@1.2.8: {}
+
mitt@3.0.1: {}
+ mlly@1.8.2:
+ dependencies:
+ acorn: 8.16.0
+ pathe: 2.0.3
+ pkg-types: 1.3.1
+ ufo: 1.6.3
+
ms@2.1.3: {}
muggle-string@0.4.1: {}
+ multimatch@6.0.0:
+ dependencies:
+ '@types/minimatch': 3.0.5
+ array-differ: 4.0.0
+ array-union: 3.0.1
+ minimatch: 3.1.5
+
+ nano-spawn@2.1.0: {}
+
nanoid@3.3.11: {}
nanoid@5.1.7: {}
nanopop@2.4.2: {}
+ nanospinner@1.2.2:
+ dependencies:
+ picocolors: 1.1.1
+
+ node-fetch-native@1.6.7: {}
+
+ node-forge@1.4.0: {}
+
+ node-notifier@10.0.1:
+ dependencies:
+ growly: 1.3.0
+ is-wsl: 2.2.0
+ semver: 7.7.4
+ shellwords: 0.1.1
+ uuid: 8.3.2
+ which: 2.0.2
+
+ normalize-path@3.0.0: {}
+
+ nth-check@2.1.1:
+ dependencies:
+ boolbase: 1.0.0
+
+ nypm@0.6.5:
+ dependencies:
+ citty: 0.2.2
+ pathe: 2.0.3
+ tinyexec: 1.0.4
+
+ obug@2.1.1: {}
+
+ ofetch@1.5.1:
+ dependencies:
+ destr: 2.0.5
+ node-fetch-native: 1.6.7
+ ufo: 1.6.3
+
+ ohash@2.0.11: {}
+
+ on-exit-leak-free@2.1.2: {}
+
+ onetime@7.0.0:
+ dependencies:
+ mimic-function: 5.0.1
+
+ open@11.0.0:
+ dependencies:
+ default-browser: 5.5.0
+ define-lazy-prop: 3.0.0
+ is-in-ssh: 1.0.0
+ is-inside-container: 1.0.0
+ powershell-utils: 0.1.0
+ wsl-utils: 0.3.1
+
+ open@8.4.2:
+ dependencies:
+ define-lazy-prop: 2.0.0
+ is-docker: 2.2.1
+ is-wsl: 2.2.0
+
orderedmap@2.1.1: {}
+ os-shim@0.1.3: {}
+
+ package-json@10.0.1:
+ dependencies:
+ ky: 1.14.3
+ registry-auth-token: 5.1.1
+ registry-url: 6.0.1
+ semver: 7.7.4
+
+ pako@1.0.11: {}
+
+ parse-json@7.1.1:
+ dependencies:
+ '@babel/code-frame': 7.29.0
+ error-ex: 1.3.4
+ json-parse-even-better-errors: 3.0.2
+ lines-and-columns: 2.0.4
+ type-fest: 3.13.1
+
path-browserify@1.0.1: {}
+ pathe@2.0.3: {}
+
perfect-debounce@1.0.0: {}
+ perfect-debounce@2.1.0: {}
+
picocolors@1.1.1: {}
+ picomatch@2.3.2: {}
+
picomatch@4.0.4: {}
pinia@3.0.4(typescript@5.9.3)(vue@3.5.31(typescript@5.9.3)):
@@ -2967,12 +5187,59 @@ snapshots:
optionalDependencies:
typescript: 5.9.3
+ pino-abstract-transport@2.0.0:
+ dependencies:
+ split2: 4.2.0
+
+ pino-std-serializers@7.1.0: {}
+
+ pino@9.7.0:
+ dependencies:
+ atomic-sleep: 1.0.0
+ fast-redact: 3.5.0
+ on-exit-leak-free: 2.1.2
+ pino-abstract-transport: 2.0.0
+ pino-std-serializers: 7.1.0
+ process-warning: 5.0.0
+ quick-format-unescaped: 4.0.4
+ real-require: 0.2.0
+ safe-stable-stringify: 2.5.0
+ sonic-boom: 4.2.1
+ thread-stream: 3.1.0
+
+ pkg-types@1.3.1:
+ dependencies:
+ confbox: 0.1.8
+ mlly: 1.8.2
+ pathe: 2.0.3
+
+ pkg-types@2.3.0:
+ dependencies:
+ confbox: 0.2.4
+ exsolve: 1.0.8
+ pathe: 2.0.3
+
postcss@8.5.8:
dependencies:
nanoid: 3.3.11
picocolors: 1.1.1
source-map-js: 1.2.1
+ powershell-utils@0.1.0: {}
+
+ process-nextick-args@2.0.1: {}
+
+ process-warning@5.0.0: {}
+
+ promise-toolbox@0.21.0:
+ dependencies:
+ make-error: 1.3.6
+
+ prompts@2.4.2:
+ dependencies:
+ kleur: 3.0.3
+ sisteransi: 1.0.5
+
prosemirror-changeset@2.4.0:
dependencies:
prosemirror-transform: 1.12.0
@@ -3065,8 +5332,64 @@ snapshots:
prosemirror-state: 1.4.4
prosemirror-view: 1.41.7
+ proto-list@1.2.4: {}
+
proxy-from-env@2.1.0: {}
+ publish-browser-extension@4.0.4:
+ dependencies:
+ cac: 6.7.14
+ consola: 3.4.2
+ dotenv: 17.4.0
+ form-data-encoder: 4.1.0
+ formdata-node: 6.0.3
+ jsonwebtoken: 9.0.3
+ listr2: 10.2.1
+ ofetch: 1.5.1
+ zod: 4.3.6
+
+ pupa@3.3.0:
+ dependencies:
+ escape-goat: 4.0.0
+
+ quansync@0.2.11: {}
+
+ quick-format-unescaped@4.0.4: {}
+
+ rc9@3.0.1:
+ dependencies:
+ defu: 6.1.6
+ destr: 2.0.5
+
+ rc@1.2.8:
+ dependencies:
+ deep-extend: 0.6.0
+ ini: 1.3.8
+ minimist: 1.2.8
+ strip-json-comments: 2.0.1
+
+ readable-stream@2.3.8:
+ dependencies:
+ core-util-is: 1.0.3
+ inherits: 2.0.4
+ isarray: 1.0.0
+ process-nextick-args: 2.0.1
+ safe-buffer: 5.1.2
+ string_decoder: 1.1.1
+ util-deprecate: 1.0.2
+
+ readdirp@5.0.0: {}
+
+ real-require@0.2.0: {}
+
+ registry-auth-token@5.1.1:
+ dependencies:
+ '@pnpm/npm-conf': 3.0.2
+
+ registry-url@6.0.1:
+ dependencies:
+ rc: 1.2.8
+
remark-gfm@4.0.1:
dependencies:
'@types/mdast': 4.0.4
@@ -3119,8 +5442,15 @@ snapshots:
remove-accents@0.5.0: {}
+ require-directory@2.1.1: {}
+
resize-observer-polyfill@1.5.1: {}
+ restore-cursor@5.1.0:
+ dependencies:
+ onetime: 7.0.0
+ signal-exit: 4.1.0
+
rfdc@1.4.1: {}
rolldown@1.0.0-rc.12(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1):
@@ -3149,16 +5479,124 @@ snapshots:
rope-sequence@1.3.4: {}
+ run-applescript@7.1.0: {}
+
+ safe-buffer@5.1.2: {}
+
+ safe-buffer@5.2.1: {}
+
+ safe-stable-stringify@2.5.0: {}
+
+ sax@1.6.0: {}
+
scroll-into-view-if-needed@2.2.31:
dependencies:
compute-scroll-into-view: 1.0.20
+ scule@1.3.0: {}
+
+ semver@7.7.4: {}
+
+ set-value@4.1.0:
+ dependencies:
+ is-plain-object: 2.0.4
+ is-primitive: 3.0.1
+
+ setimmediate@1.0.5: {}
+
shallow-equal@1.2.1: {}
+ shell-quote@1.7.3: {}
+
+ shellwords@0.1.1: {}
+
+ signal-exit@4.1.0: {}
+
+ sisteransi@1.0.5: {}
+
+ slice-ansi@7.1.2:
+ dependencies:
+ ansi-styles: 6.2.3
+ is-fullwidth-code-point: 5.1.0
+
+ slice-ansi@8.0.0:
+ dependencies:
+ ansi-styles: 6.2.3
+ is-fullwidth-code-point: 5.1.0
+
+ sonic-boom@4.2.1:
+ dependencies:
+ atomic-sleep: 1.0.0
+
source-map-js@1.2.1: {}
+ source-map-support@0.5.21:
+ dependencies:
+ buffer-from: 1.1.2
+ source-map: 0.6.1
+
+ source-map@0.6.1: {}
+
+ source-map@0.7.6: {}
+
+ spawn-sync@1.0.15:
+ dependencies:
+ concat-stream: 1.6.2
+ os-shim: 0.1.3
+
speakingurl@14.0.1: {}
+ split2@4.2.0: {}
+
+ split@1.0.1:
+ dependencies:
+ through: 2.3.8
+
+ string-width@4.2.3:
+ dependencies:
+ emoji-regex: 8.0.0
+ is-fullwidth-code-point: 3.0.0
+ strip-ansi: 6.0.1
+
+ string-width@7.2.0:
+ dependencies:
+ emoji-regex: 10.6.0
+ get-east-asian-width: 1.5.0
+ strip-ansi: 7.2.0
+
+ string-width@8.2.0:
+ dependencies:
+ get-east-asian-width: 1.5.0
+ strip-ansi: 7.2.0
+
+ string_decoder@1.1.1:
+ dependencies:
+ safe-buffer: 5.1.2
+
+ strip-ansi@6.0.1:
+ dependencies:
+ ansi-regex: 5.0.1
+
+ strip-ansi@7.2.0:
+ dependencies:
+ ansi-regex: 6.2.2
+
+ strip-bom@5.0.0: {}
+
+ strip-json-comments@2.0.1: {}
+
+ strip-json-comments@5.0.2: {}
+
+ strip-literal@3.1.0:
+ dependencies:
+ js-tokens: 9.0.1
+
+ stubborn-fs@2.0.0:
+ dependencies:
+ stubborn-utils: 1.0.2
+
+ stubborn-utils@1.0.2: {}
+
style-mod@4.1.3: {}
stylis@4.3.6: {}
@@ -3167,20 +5605,39 @@ snapshots:
dependencies:
copy-anything: 4.0.5
+ thread-stream@3.1.0:
+ dependencies:
+ real-require: 0.2.0
+
throttle-debounce@5.0.2: {}
+ through@2.3.8: {}
+
+ tinyexec@1.0.4: {}
+
tinyglobby@0.2.15:
dependencies:
fdir: 6.5.0(picomatch@4.0.4)
picomatch: 4.0.4
+ tmp@0.2.5: {}
+
trough@2.2.0: {}
- tslib@2.8.1:
- optional: true
+ tslib@2.8.1: {}
+
+ type-fest@3.13.1: {}
+
+ type-fest@4.41.0: {}
+
+ typedarray@0.0.6: {}
typescript@5.9.3: {}
+ ufo@1.6.3: {}
+
+ uhyphen@0.2.0: {}
+
undici-types@7.16.0: {}
unified@11.0.5:
@@ -3193,6 +5650,23 @@ snapshots:
trough: 2.2.0
vfile: 6.0.3
+ unimport@6.0.2:
+ dependencies:
+ acorn: 8.16.0
+ escape-string-regexp: 5.0.0
+ estree-walker: 3.0.3
+ local-pkg: 1.1.2
+ magic-string: 0.30.21
+ mlly: 1.8.2
+ pathe: 2.0.3
+ picomatch: 4.0.4
+ pkg-types: 2.3.0
+ scule: 1.3.0
+ strip-literal: 3.1.0
+ tinyglobby: 0.2.15
+ unplugin: 3.0.0
+ unplugin-utils: 0.3.1
+
unist-util-is@6.0.1:
dependencies:
'@types/unist': 3.0.3
@@ -3217,6 +5691,36 @@ snapshots:
unist-util-is: 6.0.1
unist-util-visit-parents: 6.0.2
+ universalify@2.0.1: {}
+
+ unplugin-utils@0.3.1:
+ dependencies:
+ pathe: 2.0.3
+ picomatch: 4.0.4
+
+ unplugin@3.0.0:
+ dependencies:
+ '@jridgewell/remapping': 2.3.5
+ picomatch: 4.0.4
+ webpack-virtual-modules: 0.6.2
+
+ update-notifier@7.3.1:
+ dependencies:
+ boxen: 8.0.1
+ chalk: 5.6.2
+ configstore: 7.1.0
+ is-in-ci: 1.0.0
+ is-installed-globally: 1.0.0
+ is-npm: 6.1.0
+ latest-version: 9.0.0
+ pupa: 3.3.0
+ semver: 7.7.4
+ xdg-basedir: 5.1.0
+
+ util-deprecate@1.0.2: {}
+
+ uuid@8.3.2: {}
+
vfile-message@4.0.3:
dependencies:
'@types/unist': 3.0.3
@@ -3227,7 +5731,30 @@ snapshots:
'@types/unist': 3.0.3
vfile-message: 4.0.3
- vite@8.0.3(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@24.12.0):
+ vite-node@6.0.0(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@24.12.0)(esbuild@0.27.5)(jiti@2.6.1):
+ dependencies:
+ cac: 7.0.0
+ es-module-lexer: 2.0.0
+ obug: 2.1.1
+ pathe: 2.0.3
+ vite: 8.0.3(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@24.12.0)(esbuild@0.27.5)(jiti@2.6.1)
+ transitivePeerDependencies:
+ - '@emnapi/core'
+ - '@emnapi/runtime'
+ - '@types/node'
+ - '@vitejs/devtools'
+ - esbuild
+ - jiti
+ - less
+ - sass
+ - sass-embedded
+ - stylus
+ - sugarss
+ - terser
+ - tsx
+ - yaml
+
+ vite@8.0.3(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@24.12.0)(esbuild@0.27.5)(jiti@2.6.1):
dependencies:
lightningcss: 1.32.0
picomatch: 4.0.4
@@ -3236,7 +5763,9 @@ snapshots:
tinyglobby: 0.2.15
optionalDependencies:
'@types/node': 24.12.0
+ esbuild: 0.27.5
fsevents: 2.3.3
+ jiti: 2.6.1
transitivePeerDependencies:
- '@emnapi/core'
- '@emnapi/runtime'
@@ -3286,6 +5815,172 @@ snapshots:
dependencies:
loose-envify: 1.4.0
+ watchpack@2.4.4:
+ dependencies:
+ glob-to-regexp: 0.4.1
+ graceful-fs: 4.2.11
+
+ web-ext-run@0.2.4:
+ dependencies:
+ '@babel/runtime': 7.28.2
+ '@devicefarmer/adbkit': 3.3.8
+ chrome-launcher: 1.2.0
+ debounce: 1.2.1
+ es6-error: 4.1.1
+ firefox-profile: 4.7.0
+ fx-runner: 1.4.0
+ multimatch: 6.0.0
+ node-notifier: 10.0.1
+ parse-json: 7.1.1
+ pino: 9.7.0
+ promise-toolbox: 0.21.0
+ set-value: 4.1.0
+ source-map-support: 0.5.21
+ strip-bom: 5.0.0
+ strip-json-comments: 5.0.2
+ tmp: 0.2.5
+ update-notifier: 7.3.1
+ watchpack: 2.4.4
+ zip-dir: 2.0.0
+ transitivePeerDependencies:
+ - supports-color
+
+ webpack-virtual-modules@0.6.2: {}
+
+ when-exit@2.1.5: {}
+
+ when@3.7.7: {}
+
+ which@1.2.4:
+ dependencies:
+ is-absolute: 0.1.7
+ isexe: 1.1.2
+
+ which@2.0.2:
+ dependencies:
+ isexe: 2.0.0
+
+ widest-line@5.0.0:
+ dependencies:
+ string-width: 7.2.0
+
+ winreg@0.0.12: {}
+
+ wrap-ansi@10.0.0:
+ dependencies:
+ ansi-styles: 6.2.3
+ string-width: 8.2.0
+ strip-ansi: 7.2.0
+
+ wrap-ansi@7.0.0:
+ dependencies:
+ ansi-styles: 4.3.0
+ string-width: 4.2.3
+ strip-ansi: 6.0.1
+
+ wrap-ansi@9.0.2:
+ dependencies:
+ ansi-styles: 6.2.3
+ string-width: 7.2.0
+ strip-ansi: 7.2.0
+
+ wsl-utils@0.3.1:
+ dependencies:
+ is-wsl: 3.1.1
+ powershell-utils: 0.1.0
+
+ wxt@0.20.20(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@24.12.0)(jiti@2.6.1):
+ dependencies:
+ '@1natsu/wait-element': 4.1.2
+ '@aklinker1/rollup-plugin-visualizer': 5.12.0
+ '@webext-core/fake-browser': 1.3.4
+ '@webext-core/isolated-element': 1.1.5
+ '@webext-core/match-patterns': 1.0.3
+ '@wxt-dev/browser': 0.1.38
+ '@wxt-dev/storage': 1.2.8
+ async-mutex: 0.5.0
+ c12: 3.3.4(magicast@0.5.2)
+ cac: 7.0.0
+ chokidar: 5.0.0
+ ci-info: 4.4.0
+ consola: 3.4.2
+ defu: 6.1.6
+ dotenv: 17.4.0
+ dotenv-expand: 12.0.3
+ esbuild: 0.27.5
+ filesize: 11.0.15
+ get-port-please: 3.2.0
+ giget: 3.2.0
+ hookable: 6.1.0
+ import-meta-resolve: 4.2.0
+ is-wsl: 3.1.1
+ json5: 2.2.3
+ jszip: 3.10.1
+ linkedom: 0.18.12
+ magicast: 0.5.2
+ minimatch: 10.2.5
+ nano-spawn: 2.1.0
+ nanospinner: 1.2.2
+ normalize-path: 3.0.0
+ nypm: 0.6.5
+ ohash: 2.0.11
+ open: 11.0.0
+ perfect-debounce: 2.1.0
+ picocolors: 1.1.1
+ prompts: 2.4.2
+ publish-browser-extension: 4.0.4
+ scule: 1.3.0
+ tinyglobby: 0.2.15
+ unimport: 6.0.2
+ vite: 8.0.3(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@24.12.0)(esbuild@0.27.5)(jiti@2.6.1)
+ vite-node: 6.0.0(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@24.12.0)(esbuild@0.27.5)(jiti@2.6.1)
+ web-ext-run: 0.2.4
+ transitivePeerDependencies:
+ - '@emnapi/core'
+ - '@emnapi/runtime'
+ - '@types/node'
+ - '@vitejs/devtools'
+ - canvas
+ - jiti
+ - less
+ - rollup
+ - sass
+ - sass-embedded
+ - stylus
+ - sugarss
+ - supports-color
+ - terser
+ - tsx
+ - yaml
+
+ xdg-basedir@5.1.0: {}
+
+ xml2js@0.6.2:
+ dependencies:
+ sax: 1.6.0
+ xmlbuilder: 11.0.1
+
+ xmlbuilder@11.0.1: {}
+
+ y18n@5.0.8: {}
+
+ yargs-parser@21.1.1: {}
+
+ yargs@17.7.2:
+ dependencies:
+ cliui: 8.0.1
+ escalade: 3.2.0
+ get-caller-file: 2.0.5
+ require-directory: 2.1.1
+ string-width: 4.2.3
+ y18n: 5.0.8
+ yargs-parser: 21.1.1
+
+ zip-dir@2.0.0:
+ dependencies:
+ async: 3.2.6
+ jszip: 3.10.1
+
zod@4.3.6: {}
zwitch@2.0.4: {}
diff --git a/server/internal/tenant/app/media_service.go b/server/internal/tenant/app/media_service.go
new file mode 100644
index 0000000..24c8250
--- /dev/null
+++ b/server/internal/tenant/app/media_service.go
@@ -0,0 +1,1018 @@
+package app
+
+import (
+ "context"
+ "crypto/rand"
+ "encoding/hex"
+ "encoding/json"
+ "errors"
+ "strings"
+ "time"
+
+ "github.com/jackc/pgx/v5"
+ "github.com/jackc/pgx/v5/pgxpool"
+
+ "github.com/geo-platform/tenant-api/internal/shared/auth"
+ "github.com/geo-platform/tenant-api/internal/shared/response"
+)
+
+const minBrowserExtensionVersion = "0.1.0"
+
+type MediaService struct {
+ pool *pgxpool.Pool
+}
+
+func NewMediaService(pool *pgxpool.Pool) *MediaService {
+ return &MediaService{pool: pool}
+}
+
+type MediaPlatformResponse struct {
+ ID int64 `json:"id"`
+ PlatformID string `json:"platform_id"`
+ Name string `json:"name"`
+ Category string `json:"category"`
+ ShortName string `json:"short_name"`
+ AccentColor string `json:"accent_color"`
+ LoginURL *string `json:"login_url"`
+ LogoURL *string `json:"logo_url"`
+ Status string `json:"status"`
+ SortOrder int `json:"sort_order"`
+}
+
+type PlatformAccountResponse struct {
+ ID int64 `json:"id"`
+ PlatformID string `json:"platform_id"`
+ PlatformName string `json:"platform_name"`
+ PlatformCategory string `json:"platform_category"`
+ PlatformUID string `json:"platform_uid"`
+ Nickname string `json:"nickname"`
+ AvatarURL *string `json:"avatar_url"`
+ Status string `json:"status"`
+ LoginURL *string `json:"login_url"`
+ LastCheckAt *time.Time `json:"last_check_at"`
+ Metadata map[string]interface{} `json:"metadata,omitempty"`
+ CreatedAt time.Time `json:"created_at"`
+ UpdatedAt time.Time `json:"updated_at"`
+}
+
+type CreatePluginSessionRequest struct {
+ ActionType string `json:"action_type" binding:"required"`
+ PlatformID string `json:"platform_id" binding:"required"`
+ PlatformAccountID *int64 `json:"platform_account_id"`
+ PluginInstallationID *int64 `json:"plugin_installation_id"`
+ ResourceType string `json:"resource_type"`
+ ResourceID *int64 `json:"resource_id"`
+}
+
+type CreatePluginSessionResponse struct {
+ PluginSessionID int64 `json:"plugin_session_id"`
+ SessionToken string `json:"session_token"`
+ ExpireAt time.Time `json:"expire_at"`
+ MinPluginVersion string `json:"min_plugin_version"`
+}
+
+type CreatePublishBatchRequest struct {
+ PlatformAccountIDs []int64 `json:"platform_account_ids" binding:"required"`
+ PluginInstallationID *int64 `json:"plugin_installation_id"`
+ PublishType string `json:"publish_type"`
+ CoverAssetURL *string `json:"cover_asset_url"`
+}
+
+type PublishBatchTask struct {
+ PublishRecordID int64 `json:"publish_record_id"`
+ PlatformAccountID int64 `json:"platform_account_id"`
+ PlatformID string `json:"platform_id"`
+ PlatformName string `json:"platform_name"`
+ PlatformUID string `json:"platform_uid"`
+ PlatformNickname string `json:"platform_nickname"`
+ PluginSessionID int64 `json:"plugin_session_id"`
+ SessionToken string `json:"session_token"`
+ ExpireAt time.Time `json:"expire_at"`
+}
+
+type CreatePublishBatchResponse struct {
+ PublishBatchID int64 `json:"publish_batch_id"`
+ Status string `json:"status"`
+ Tasks []PublishBatchTask `json:"tasks"`
+}
+
+type PublishRecordResponse struct {
+ ID int64 `json:"id"`
+ PublishBatchID int64 `json:"publish_batch_id"`
+ ArticleID int64 `json:"article_id"`
+ PlatformAccountID int64 `json:"platform_account_id"`
+ PlatformID string `json:"platform_id"`
+ PlatformName string `json:"platform_name"`
+ PlatformNickname string `json:"platform_nickname"`
+ Status string `json:"status"`
+ ExternalArticleID *string `json:"external_article_id"`
+ ExternalArticleURL *string `json:"external_article_url"`
+ ExternalManageURL *string `json:"external_manage_url"`
+ PublishedAt *time.Time `json:"published_at"`
+ ErrorMessage *string `json:"error_message"`
+ CreatedAt time.Time `json:"created_at"`
+ UpdatedAt time.Time `json:"updated_at"`
+}
+
+type PluginBindCallbackRequest struct {
+ PluginSessionID int64 `json:"plugin_session_id" binding:"required"`
+ SessionToken string `json:"session_token" binding:"required"`
+ PlatformID string `json:"platform_id" binding:"required"`
+ PlatformUID string `json:"platform_uid" binding:"required"`
+ Nickname string `json:"nickname" binding:"required"`
+ AvatarURL *string `json:"avatar_url"`
+ ClientVersion *string `json:"client_version"`
+ Metadata map[string]interface{} `json:"metadata"`
+ Status string `json:"status"`
+}
+
+type PluginBindCallbackResponse struct {
+ PlatformAccountID int64 `json:"platform_account_id"`
+ Status string `json:"status"`
+}
+
+type PluginPublishCallbackRequest struct {
+ PluginSessionID int64 `json:"plugin_session_id" binding:"required"`
+ SessionToken string `json:"session_token" binding:"required"`
+ ArticleID int64 `json:"article_id" binding:"required"`
+ PublishRecordID int64 `json:"publish_record_id" binding:"required"`
+ PlatformAccountID int64 `json:"platform_account_id" binding:"required"`
+ PlatformID string `json:"platform_id" binding:"required"`
+ Status string `json:"status" binding:"required"`
+ ExternalArticleID *string `json:"external_article_id"`
+ ExternalArticleURL *string `json:"external_article_url"`
+ ExternalManageURL *string `json:"external_manage_url"`
+ PublishedAt *time.Time `json:"published_at"`
+ ClientVersion *string `json:"client_version"`
+ RequestPayload map[string]interface{} `json:"request_payload"`
+ ResponsePayload map[string]interface{} `json:"response_payload"`
+ ErrorMessage *string `json:"error_message"`
+}
+
+type PluginPublishCallbackResponse struct {
+ PublishRecordID int64 `json:"publish_record_id"`
+ BatchStatus string `json:"batch_status"`
+ ArticleStatus string `json:"article_status"`
+}
+
+type RegisterPluginInstallationRequest struct {
+ InstallationKey string `json:"installation_key" binding:"required"`
+ InstallationName string `json:"installation_name" binding:"required"`
+ BrowserName string `json:"browser_name"`
+ ClientVersion string `json:"client_version"`
+}
+
+type RegisterPluginInstallationResponse struct {
+ PluginInstallationID int64 `json:"plugin_installation_id"`
+ InstallationToken string `json:"installation_token"`
+ Status string `json:"status"`
+ LastSeenAt time.Time `json:"last_seen_at"`
+}
+
+type platformAccountSeed struct {
+ ID int64
+ PlatformID string
+ PlatformName string
+ PlatformUID string
+ Nickname string
+}
+
+type pluginSessionRow struct {
+ ID int64
+ TenantID int64
+ UserID int64
+ PluginInstallationID *int64
+ ActionType string
+ ResourceType *string
+ ResourceID *int64
+ PlatformAccountID *int64
+ PlatformID string
+ ExpireAt time.Time
+}
+
+func (s *MediaService) ListPlatforms(ctx context.Context) ([]MediaPlatformResponse, error) {
+ rows, err := s.pool.Query(ctx, `
+ SELECT id, platform_id, name, category, short_name, accent_color, login_url, logo_url, status, sort_order
+ FROM media_platforms
+ WHERE status = 'active'
+ ORDER BY sort_order ASC, id ASC
+ `)
+ if err != nil {
+ return nil, response.ErrInternal(50030, "media_platform_query_failed", "failed to list media platforms")
+ }
+ defer rows.Close()
+
+ items := make([]MediaPlatformResponse, 0)
+ for rows.Next() {
+ var item MediaPlatformResponse
+ if err := rows.Scan(
+ &item.ID,
+ &item.PlatformID,
+ &item.Name,
+ &item.Category,
+ &item.ShortName,
+ &item.AccentColor,
+ &item.LoginURL,
+ &item.LogoURL,
+ &item.Status,
+ &item.SortOrder,
+ ); err != nil {
+ return nil, response.ErrInternal(50030, "media_platform_scan_failed", err.Error())
+ }
+ items = append(items, item)
+ }
+ return items, nil
+}
+
+func (s *MediaService) RegisterPluginInstallation(ctx context.Context, req RegisterPluginInstallationRequest) (*RegisterPluginInstallationResponse, error) {
+ actor := auth.MustActor(ctx)
+ installationKey := strings.TrimSpace(req.InstallationKey)
+ installationName := strings.TrimSpace(req.InstallationName)
+ if installationKey == "" || installationName == "" {
+ return nil, response.ErrBadRequest(40038, "invalid_plugin_installation", "installation_key and installation_name are required")
+ }
+
+ installationToken, err := newSessionToken()
+ if err != nil {
+ return nil, response.ErrInternal(50060, "installation_token_failed", "failed to generate installation token")
+ }
+ tokenHash := auth.HashToken(installationToken)
+ lastSeenAt := time.Now().UTC()
+ browserName := limitStringPointer(nilIfBlank(req.BrowserName), 64)
+ clientVersion := limitStringPointer(nilIfBlank(req.ClientVersion), 32)
+
+ tx, err := s.pool.Begin(ctx)
+ if err != nil {
+ return nil, response.ErrInternal(50061, "installation_begin_failed", "failed to start installation registration")
+ }
+ defer tx.Rollback(ctx)
+
+ var installationID int64
+ err = tx.QueryRow(ctx, `
+ SELECT id
+ FROM plugin_installations
+ WHERE tenant_id = $1 AND installation_key = $2 AND deleted_at IS NULL
+ LIMIT 1
+ `, actor.TenantID, installationKey).Scan(&installationID)
+
+ switch {
+ case errors.Is(err, pgx.ErrNoRows):
+ if err := tx.QueryRow(ctx, `
+ INSERT INTO plugin_installations (
+ tenant_id, user_id, installation_key, installation_name, browser_name,
+ client_version, installation_token_hash, status, last_seen_at
+ )
+ VALUES ($1, $2, $3, $4, $5, $6, $7, 'active', $8)
+ RETURNING id
+ `, actor.TenantID, actor.UserID, installationKey, installationName, browserName, clientVersion, tokenHash, lastSeenAt).Scan(&installationID); err != nil {
+ return nil, response.ErrInternal(50062, "installation_insert_failed", "failed to register plugin installation")
+ }
+ case err != nil:
+ return nil, response.ErrInternal(50063, "installation_lookup_failed", "failed to lookup plugin installation")
+ default:
+ if _, err := tx.Exec(ctx, `
+ UPDATE plugin_installations
+ SET user_id = $1,
+ installation_name = $2,
+ browser_name = $3,
+ client_version = $4,
+ installation_token_hash = $5,
+ status = 'active',
+ last_seen_at = $6,
+ updated_at = NOW()
+ WHERE id = $7
+ `, actor.UserID, installationName, browserName, clientVersion, tokenHash, lastSeenAt, installationID); err != nil {
+ return nil, response.ErrInternal(50064, "installation_update_failed", "failed to refresh plugin installation")
+ }
+ }
+
+ if err := tx.Commit(ctx); err != nil {
+ return nil, response.ErrInternal(50065, "installation_commit_failed", "failed to commit plugin installation")
+ }
+
+ return &RegisterPluginInstallationResponse{
+ PluginInstallationID: installationID,
+ InstallationToken: installationToken,
+ Status: "active",
+ LastSeenAt: lastSeenAt,
+ }, nil
+}
+
+func (s *MediaService) ListPlatformAccounts(ctx context.Context) ([]PlatformAccountResponse, error) {
+ actor := auth.MustActor(ctx)
+ rows, err := s.pool.Query(ctx, `
+ SELECT pa.id, pa.platform_id, mp.name, mp.category, pa.platform_uid, pa.nickname,
+ pa.avatar_url, pa.status, mp.login_url, pa.last_check_at, pa.metadata_json,
+ pa.created_at, pa.updated_at
+ FROM platform_accounts pa
+ JOIN media_platforms mp ON mp.platform_id = pa.platform_id
+ WHERE pa.tenant_id = $1 AND pa.deleted_at IS NULL
+ ORDER BY mp.sort_order ASC, pa.created_at DESC
+ `, actor.TenantID)
+ if err != nil {
+ return nil, response.ErrInternal(50031, "platform_account_query_failed", "failed to list platform accounts")
+ }
+ defer rows.Close()
+
+ items := make([]PlatformAccountResponse, 0)
+ for rows.Next() {
+ var item PlatformAccountResponse
+ var metadataJSON []byte
+ if err := rows.Scan(
+ &item.ID,
+ &item.PlatformID,
+ &item.PlatformName,
+ &item.PlatformCategory,
+ &item.PlatformUID,
+ &item.Nickname,
+ &item.AvatarURL,
+ &item.Status,
+ &item.LoginURL,
+ &item.LastCheckAt,
+ &metadataJSON,
+ &item.CreatedAt,
+ &item.UpdatedAt,
+ ); err != nil {
+ return nil, response.ErrInternal(50031, "platform_account_scan_failed", err.Error())
+ }
+ if len(metadataJSON) > 0 {
+ item.Metadata = map[string]interface{}{}
+ _ = json.Unmarshal(metadataJSON, &item.Metadata)
+ }
+ items = append(items, item)
+ }
+ return items, nil
+}
+
+func (s *MediaService) DeletePlatformAccount(ctx context.Context, id int64) error {
+ actor := auth.MustActor(ctx)
+ tag, err := s.pool.Exec(ctx, `
+ UPDATE platform_accounts
+ SET deleted_at = NOW(), updated_at = NOW(), status = 'invalid'
+ WHERE id = $1 AND tenant_id = $2 AND deleted_at IS NULL
+ `, id, actor.TenantID)
+ if err != nil {
+ return response.ErrInternal(50032, "platform_account_delete_failed", "failed to unbind platform account")
+ }
+ if tag.RowsAffected() == 0 {
+ return response.ErrNotFound(40431, "platform_account_not_found", "platform account not found")
+ }
+ return nil
+}
+
+func (s *MediaService) CreatePluginSession(ctx context.Context, req CreatePluginSessionRequest) (*CreatePluginSessionResponse, error) {
+ actor := auth.MustActor(ctx)
+ actionType := strings.TrimSpace(req.ActionType)
+ platformID := strings.TrimSpace(req.PlatformID)
+ if actionType == "" || platformID == "" {
+ return nil, response.ErrBadRequest(40031, "invalid_plugin_session_request", "action_type and platform_id are required")
+ }
+ if actionType != "bind" && actionType != "check" {
+ return nil, response.ErrBadRequest(40032, "invalid_plugin_action", "only bind and check are supported on this endpoint")
+ }
+ if err := s.validatePluginInstallationOwnership(ctx, actor.TenantID, req.PluginInstallationID); err != nil {
+ return nil, err
+ }
+
+ if req.PlatformAccountID != nil {
+ var exists bool
+ if err := s.pool.QueryRow(ctx, `
+ SELECT EXISTS(
+ SELECT 1 FROM platform_accounts
+ WHERE id = $1 AND tenant_id = $2 AND deleted_at IS NULL
+ )
+ `, *req.PlatformAccountID, actor.TenantID).Scan(&exists); err != nil {
+ return nil, response.ErrInternal(50033, "platform_account_check_failed", "failed to validate platform account")
+ }
+ if !exists {
+ return nil, response.ErrNotFound(40431, "platform_account_not_found", "platform account not found")
+ }
+ }
+
+ token, err := newSessionToken()
+ if err != nil {
+ return nil, response.ErrInternal(50034, "session_token_failed", "failed to generate plugin session token")
+ }
+ expireAt := time.Now().Add(10 * time.Minute).UTC()
+
+ var id int64
+ if err := s.pool.QueryRow(ctx, `
+ INSERT INTO plugin_sessions (
+ tenant_id, user_id, plugin_installation_id, action_type, resource_type, resource_id, platform_account_id,
+ platform_id, session_token, expire_at, status
+ )
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, 'pending')
+ RETURNING id
+ `, actor.TenantID, actor.UserID, req.PluginInstallationID, actionType, nilIfBlank(req.ResourceType), req.ResourceID, req.PlatformAccountID, platformID, token, expireAt).Scan(&id); err != nil {
+ return nil, response.ErrInternal(50035, "plugin_session_create_failed", "failed to create plugin session")
+ }
+
+ return &CreatePluginSessionResponse{
+ PluginSessionID: id,
+ SessionToken: token,
+ ExpireAt: expireAt,
+ MinPluginVersion: minBrowserExtensionVersion,
+ }, nil
+}
+
+func (s *MediaService) CreatePublishBatch(ctx context.Context, articleID int64, req CreatePublishBatchRequest) (*CreatePublishBatchResponse, error) {
+ actor := auth.MustActor(ctx)
+ if len(req.PlatformAccountIDs) == 0 {
+ return nil, response.ErrBadRequest(40033, "platform_account_ids_required", "select at least one platform account")
+ }
+ if err := s.validatePluginInstallationOwnership(ctx, actor.TenantID, req.PluginInstallationID); err != nil {
+ return nil, err
+ }
+
+ publishType := strings.TrimSpace(req.PublishType)
+ if publishType == "" {
+ publishType = "publish"
+ }
+
+ tx, err := s.pool.Begin(ctx)
+ if err != nil {
+ return nil, response.ErrInternal(50036, "publish_batch_begin_failed", "failed to start publish batch")
+ }
+ defer tx.Rollback(ctx)
+
+ var articleExists bool
+ var generateStatus string
+ if err := tx.QueryRow(ctx, `
+ SELECT EXISTS(SELECT 1 FROM articles WHERE id = $1 AND tenant_id = $2 AND deleted_at IS NULL),
+ COALESCE((SELECT generate_status FROM articles WHERE id = $1 AND tenant_id = $2 AND deleted_at IS NULL), '')
+ `, articleID, actor.TenantID).Scan(&articleExists, &generateStatus); err != nil {
+ return nil, response.ErrInternal(50037, "article_check_failed", "failed to validate article")
+ }
+ if !articleExists {
+ return nil, response.ErrNotFound(40411, "article_not_found", "article not found")
+ }
+ if generateStatus != "completed" {
+ return nil, response.ErrConflict(40913, "article_not_publishable", "only completed articles can be published")
+ }
+
+ accountSeeds, err := loadPlatformAccountsForPublish(ctx, tx, actor.TenantID, req.PlatformAccountIDs)
+ if err != nil {
+ return nil, err
+ }
+
+ var publishBatchID int64
+ if err := tx.QueryRow(ctx, `
+ INSERT INTO publish_batches (tenant_id, article_id, initiator_user_id, status, publish_type, cover_asset_url)
+ VALUES ($1, $2, $3, 'publishing', $4, $5)
+ RETURNING id
+ `, actor.TenantID, articleID, actor.UserID, publishType, normalizeStringPointer(req.CoverAssetURL)).Scan(&publishBatchID); err != nil {
+ return nil, response.ErrInternal(50038, "publish_batch_create_failed", "failed to create publish batch")
+ }
+
+ tasks := make([]PublishBatchTask, 0, len(accountSeeds))
+ for _, account := range accountSeeds {
+ var publishRecordID int64
+ if err := tx.QueryRow(ctx, `
+ INSERT INTO publish_records (
+ tenant_id, publish_batch_id, article_id, platform_account_id, platform_id, status
+ )
+ VALUES ($1, $2, $3, $4, $5, 'queued')
+ RETURNING id
+ `, actor.TenantID, publishBatchID, articleID, account.ID, account.PlatformID).Scan(&publishRecordID); err != nil {
+ return nil, response.ErrInternal(50039, "publish_record_create_failed", "failed to create publish record")
+ }
+
+ token, tokenErr := newSessionToken()
+ if tokenErr != nil {
+ return nil, response.ErrInternal(50034, "session_token_failed", "failed to generate plugin session token")
+ }
+ expireAt := time.Now().Add(15 * time.Minute).UTC()
+
+ var pluginSessionID int64
+ if err := tx.QueryRow(ctx, `
+ INSERT INTO plugin_sessions (
+ tenant_id, user_id, plugin_installation_id, action_type, resource_type, resource_id, platform_account_id,
+ platform_id, session_token, expire_at, status
+ )
+ VALUES ($1, $2, $3, 'publish', 'publish_record', $4, $5, $6, $7, $8, 'pending')
+ RETURNING id
+ `, actor.TenantID, actor.UserID, req.PluginInstallationID, publishRecordID, account.ID, account.PlatformID, token, expireAt).Scan(&pluginSessionID); err != nil {
+ return nil, response.ErrInternal(50040, "publish_session_create_failed", "failed to create publish session")
+ }
+
+ tasks = append(tasks, PublishBatchTask{
+ PublishRecordID: publishRecordID,
+ PlatformAccountID: account.ID,
+ PlatformID: account.PlatformID,
+ PlatformName: account.PlatformName,
+ PlatformUID: account.PlatformUID,
+ PlatformNickname: account.Nickname,
+ PluginSessionID: pluginSessionID,
+ SessionToken: token,
+ ExpireAt: expireAt,
+ })
+ }
+
+ if _, err := tx.Exec(ctx, `
+ UPDATE articles
+ SET publish_status = 'publishing', updated_at = NOW()
+ WHERE id = $1 AND tenant_id = $2 AND deleted_at IS NULL
+ `, articleID, actor.TenantID); err != nil {
+ return nil, response.ErrInternal(50041, "article_publish_status_failed", "failed to update article publish status")
+ }
+
+ if err := tx.Commit(ctx); err != nil {
+ return nil, response.ErrInternal(50042, "publish_batch_commit_failed", "failed to commit publish batch")
+ }
+
+ return &CreatePublishBatchResponse{
+ PublishBatchID: publishBatchID,
+ Status: "publishing",
+ Tasks: tasks,
+ }, nil
+}
+
+func (s *MediaService) ListArticlePublishRecords(ctx context.Context, articleID int64) ([]PublishRecordResponse, error) {
+ actor := auth.MustActor(ctx)
+ return s.listArticlePublishRecords(ctx, actor.TenantID, articleID)
+}
+
+func (s *MediaService) listArticlePublishRecords(ctx context.Context, tenantID, articleID int64) ([]PublishRecordResponse, error) {
+ rows, err := s.pool.Query(ctx, `
+ SELECT pr.id, pr.publish_batch_id, pr.article_id, pr.platform_account_id, pr.platform_id,
+ mp.name, pa.nickname, pr.status, pr.external_article_id, pr.external_article_url,
+ pr.external_manage_url, pr.published_at, pr.error_message, pr.created_at, pr.updated_at
+ FROM publish_records pr
+ JOIN media_platforms mp ON mp.platform_id = pr.platform_id
+ JOIN platform_accounts pa ON pa.id = pr.platform_account_id
+ WHERE pr.tenant_id = $1 AND pr.article_id = $2
+ ORDER BY pr.created_at DESC, pr.id DESC
+ `, tenantID, articleID)
+ if err != nil {
+ return nil, response.ErrInternal(50043, "publish_record_query_failed", "failed to list publish records")
+ }
+ defer rows.Close()
+
+ items := make([]PublishRecordResponse, 0)
+ for rows.Next() {
+ var item PublishRecordResponse
+ if err := rows.Scan(
+ &item.ID,
+ &item.PublishBatchID,
+ &item.ArticleID,
+ &item.PlatformAccountID,
+ &item.PlatformID,
+ &item.PlatformName,
+ &item.PlatformNickname,
+ &item.Status,
+ &item.ExternalArticleID,
+ &item.ExternalArticleURL,
+ &item.ExternalManageURL,
+ &item.PublishedAt,
+ &item.ErrorMessage,
+ &item.CreatedAt,
+ &item.UpdatedAt,
+ ); err != nil {
+ return nil, response.ErrInternal(50043, "publish_record_scan_failed", err.Error())
+ }
+ items = append(items, item)
+ }
+ return items, nil
+}
+
+func (s *MediaService) HandleBindCallback(ctx context.Context, req PluginBindCallbackRequest) (*PluginBindCallbackResponse, error) {
+ session, err := s.validatePluginSession(ctx, req.PluginSessionID, req.SessionToken, "bind")
+ if err != nil {
+ return nil, err
+ }
+ if session.PlatformID != req.PlatformID {
+ return nil, response.ErrConflict(40936, "plugin_session_platform_mismatch", "platform does not match plugin session")
+ }
+
+ metadataJSON, err := marshalJSON(req.Metadata)
+ if err != nil {
+ return nil, response.ErrBadRequest(40034, "invalid_bind_metadata", "metadata must be serializable")
+ }
+
+ status := strings.TrimSpace(req.Status)
+ if status == "" {
+ status = "active"
+ }
+
+ tx, err := s.pool.Begin(ctx)
+ if err != nil {
+ return nil, response.ErrInternal(50044, "bind_callback_begin_failed", "failed to start bind callback transaction")
+ }
+ defer tx.Rollback(ctx)
+
+ var platformAccountID int64
+ err = tx.QueryRow(ctx, `
+ SELECT id
+ FROM platform_accounts
+ WHERE tenant_id = $1 AND platform_id = $2 AND platform_uid = $3 AND deleted_at IS NULL
+ LIMIT 1
+ `, session.TenantID, req.PlatformID, req.PlatformUID).Scan(&platformAccountID)
+
+ switch {
+ case errors.Is(err, pgx.ErrNoRows):
+ if err := tx.QueryRow(ctx, `
+ INSERT INTO platform_accounts (
+ tenant_id, user_id, platform_id, platform_uid, nickname, avatar_url, status,
+ metadata_json, last_check_at
+ )
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8, NOW())
+ RETURNING id
+ `, session.TenantID, session.UserID, req.PlatformID, req.PlatformUID, req.Nickname, req.AvatarURL, status, metadataJSON).Scan(&platformAccountID); err != nil {
+ return nil, response.ErrInternal(50045, "platform_account_insert_failed", "failed to create platform account")
+ }
+ case err != nil:
+ return nil, response.ErrInternal(50046, "platform_account_lookup_failed", "failed to lookup platform account")
+ default:
+ if _, err := tx.Exec(ctx, `
+ UPDATE platform_accounts
+ SET user_id = $1,
+ nickname = $2,
+ avatar_url = $3,
+ status = $4,
+ metadata_json = $5,
+ last_check_at = NOW(),
+ updated_at = NOW()
+ WHERE id = $6
+ `, session.UserID, req.Nickname, req.AvatarURL, status, metadataJSON, platformAccountID); err != nil {
+ return nil, response.ErrInternal(50047, "platform_account_update_failed", "failed to update platform account")
+ }
+ }
+
+ if _, err := tx.Exec(ctx, `
+ UPDATE plugin_sessions
+ SET client_version = COALESCE($1, client_version), status = 'completed', updated_at = NOW()
+ WHERE id = $2
+ `, req.ClientVersion, session.ID); err != nil {
+ return nil, response.ErrInternal(50048, "plugin_session_update_failed", "failed to finalize bind session")
+ }
+ if err := touchPluginInstallation(ctx, tx, session.PluginInstallationID); err != nil {
+ return nil, err
+ }
+
+ if err := tx.Commit(ctx); err != nil {
+ return nil, response.ErrInternal(50049, "bind_callback_commit_failed", "failed to commit bind callback")
+ }
+
+ return &PluginBindCallbackResponse{
+ PlatformAccountID: platformAccountID,
+ Status: status,
+ }, nil
+}
+
+func (s *MediaService) HandlePublishCallback(ctx context.Context, req PluginPublishCallbackRequest) (*PluginPublishCallbackResponse, error) {
+ session, err := s.validatePluginSession(ctx, req.PluginSessionID, req.SessionToken, "publish")
+ if err != nil {
+ return nil, err
+ }
+ if session.ResourceType == nil || *session.ResourceType != "publish_record" || session.ResourceID == nil || *session.ResourceID != req.PublishRecordID {
+ return nil, response.ErrConflict(40931, "plugin_session_resource_mismatch", "publish record does not match plugin session")
+ }
+ if session.PlatformAccountID == nil || *session.PlatformAccountID != req.PlatformAccountID {
+ return nil, response.ErrConflict(40932, "plugin_session_account_mismatch", "platform account does not match plugin session")
+ }
+ if session.PlatformID != req.PlatformID {
+ return nil, response.ErrConflict(40933, "plugin_session_platform_mismatch", "platform does not match plugin session")
+ }
+
+ reqStatus := normalizePublishStatus(req.Status)
+ requestPayloadJSON, err := marshalJSON(req.RequestPayload)
+ if err != nil {
+ return nil, response.ErrBadRequest(40035, "invalid_publish_request_payload", "request_payload must be serializable")
+ }
+ responsePayloadJSON, err := marshalJSON(req.ResponsePayload)
+ if err != nil {
+ return nil, response.ErrBadRequest(40036, "invalid_publish_response_payload", "response_payload must be serializable")
+ }
+
+ tx, err := s.pool.Begin(ctx)
+ if err != nil {
+ return nil, response.ErrInternal(50050, "publish_callback_begin_failed", "failed to start publish callback transaction")
+ }
+ defer tx.Rollback(ctx)
+
+ var publishBatchID int64
+ var articleID int64
+ if err := tx.QueryRow(ctx, `
+ SELECT publish_batch_id, article_id
+ FROM publish_records
+ WHERE id = $1 AND tenant_id = $2 AND platform_account_id = $3
+ `, req.PublishRecordID, session.TenantID, req.PlatformAccountID).Scan(&publishBatchID, &articleID); err != nil {
+ if errors.Is(err, pgx.ErrNoRows) {
+ return nil, response.ErrNotFound(40432, "publish_record_not_found", "publish record not found")
+ }
+ return nil, response.ErrInternal(50051, "publish_record_lookup_failed", "failed to lookup publish record")
+ }
+ if req.ArticleID != articleID {
+ return nil, response.ErrConflict(40934, "publish_article_mismatch", "article does not match publish record")
+ }
+
+ publishedAt := req.PublishedAt
+ if reqStatus == "success" && publishedAt == nil {
+ now := time.Now().UTC()
+ publishedAt = &now
+ }
+
+ if _, err := tx.Exec(ctx, `
+ UPDATE publish_records
+ SET status = $1,
+ external_article_id = $2,
+ external_article_url = $3,
+ external_manage_url = $4,
+ published_at = $5,
+ request_payload_json = $6,
+ response_payload_json = $7,
+ error_message = $8,
+ updated_at = NOW()
+ WHERE id = $9
+ `, reqStatus, normalizeStringPointer(req.ExternalArticleID), normalizeStringPointer(req.ExternalArticleURL), normalizeStringPointer(req.ExternalManageURL), publishedAt, requestPayloadJSON, responsePayloadJSON, normalizeStringPointer(req.ErrorMessage), req.PublishRecordID); err != nil {
+ return nil, response.ErrInternal(50052, "publish_record_update_failed", "failed to update publish record")
+ }
+
+ pluginStatus := "completed"
+ if reqStatus == "failed" {
+ pluginStatus = "failed"
+ }
+ if _, err := tx.Exec(ctx, `
+ UPDATE plugin_sessions
+ SET client_version = COALESCE($1, client_version), status = $2, updated_at = NOW()
+ WHERE id = $3
+ `, req.ClientVersion, pluginStatus, session.ID); err != nil {
+ return nil, response.ErrInternal(50053, "plugin_session_finalize_failed", "failed to finalize publish session")
+ }
+ if err := touchPluginInstallation(ctx, tx, session.PluginInstallationID); err != nil {
+ return nil, err
+ }
+
+ batchStatus, err := recalculatePublishBatchStatus(ctx, tx, publishBatchID)
+ if err != nil {
+ return nil, err
+ }
+ if _, err := tx.Exec(ctx, `
+ UPDATE publish_batches
+ SET status = $1, updated_at = NOW()
+ WHERE id = $2
+ `, batchStatus, publishBatchID); err != nil {
+ return nil, response.ErrInternal(50054, "publish_batch_update_failed", "failed to update publish batch")
+ }
+
+ articleStatus := batchStatus
+ if batchStatus == "success" {
+ articleStatus = "success"
+ }
+ if batchStatus == "failed" {
+ articleStatus = "failed"
+ }
+ if _, err := tx.Exec(ctx, `
+ UPDATE articles
+ SET publish_status = $1, updated_at = NOW()
+ WHERE id = $2 AND tenant_id = $3 AND deleted_at IS NULL
+ `, articleStatus, articleID, session.TenantID); err != nil {
+ return nil, response.ErrInternal(50055, "article_publish_update_failed", "failed to update article publish status")
+ }
+
+ if err := tx.Commit(ctx); err != nil {
+ return nil, response.ErrInternal(50056, "publish_callback_commit_failed", "failed to commit publish callback")
+ }
+
+ return &PluginPublishCallbackResponse{
+ PublishRecordID: req.PublishRecordID,
+ BatchStatus: batchStatus,
+ ArticleStatus: articleStatus,
+ }, nil
+}
+
+func (s *MediaService) validatePluginSession(ctx context.Context, pluginSessionID int64, sessionToken, expectedAction string) (*pluginSessionRow, error) {
+ sessionToken = strings.TrimSpace(sessionToken)
+ if sessionToken == "" {
+ return nil, response.ErrUnauthorized(40131, "plugin_session_invalid", "plugin session token is required")
+ }
+
+ row := &pluginSessionRow{}
+ err := s.pool.QueryRow(ctx, `
+ SELECT id, tenant_id, user_id, plugin_installation_id, action_type, resource_type, resource_id, platform_account_id, platform_id, expire_at
+ FROM plugin_sessions
+ WHERE id = $1 AND session_token = $2
+ LIMIT 1
+ `, pluginSessionID, sessionToken).Scan(
+ &row.ID,
+ &row.TenantID,
+ &row.UserID,
+ &row.PluginInstallationID,
+ &row.ActionType,
+ &row.ResourceType,
+ &row.ResourceID,
+ &row.PlatformAccountID,
+ &row.PlatformID,
+ &row.ExpireAt,
+ )
+ if err != nil {
+ if errors.Is(err, pgx.ErrNoRows) {
+ return nil, response.ErrUnauthorized(40132, "plugin_session_not_found", "plugin session not found")
+ }
+ return nil, response.ErrInternal(50057, "plugin_session_lookup_failed", "failed to lookup plugin session")
+ }
+ if row.ActionType != expectedAction {
+ return nil, response.ErrConflict(40935, "plugin_session_action_mismatch", "plugin session action mismatch")
+ }
+ if time.Now().UTC().After(row.ExpireAt) {
+ return nil, response.ErrUnauthorized(40133, "plugin_session_expired", "plugin session has expired")
+ }
+ return row, nil
+}
+
+func (s *MediaService) validatePluginInstallationOwnership(ctx context.Context, tenantID int64, pluginInstallationID *int64) error {
+ if pluginInstallationID == nil {
+ return nil
+ }
+
+ var exists bool
+ if err := s.pool.QueryRow(ctx, `
+ SELECT EXISTS(
+ SELECT 1
+ FROM plugin_installations
+ WHERE id = $1 AND tenant_id = $2 AND status = 'active' AND deleted_at IS NULL
+ )
+ `, *pluginInstallationID, tenantID).Scan(&exists); err != nil {
+ return response.ErrInternal(50066, "plugin_installation_lookup_failed", "failed to validate plugin installation")
+ }
+ if !exists {
+ return response.ErrNotFound(40433, "plugin_installation_not_found", "plugin installation not found")
+ }
+ return nil
+}
+
+func touchPluginInstallation(ctx context.Context, tx pgx.Tx, pluginInstallationID *int64) error {
+ if pluginInstallationID == nil {
+ return nil
+ }
+ if _, err := tx.Exec(ctx, `
+ UPDATE plugin_installations
+ SET last_seen_at = NOW(), updated_at = NOW()
+ WHERE id = $1
+ `, *pluginInstallationID); err != nil {
+ return response.ErrInternal(50067, "plugin_installation_touch_failed", "failed to update plugin installation activity")
+ }
+ return nil
+}
+
+func loadPlatformAccountsForPublish(ctx context.Context, tx pgx.Tx, tenantID int64, ids []int64) ([]platformAccountSeed, error) {
+ rows, err := tx.Query(ctx, `
+ SELECT pa.id, pa.platform_id, mp.name, pa.platform_uid, pa.nickname
+ FROM platform_accounts pa
+ JOIN media_platforms mp ON mp.platform_id = pa.platform_id
+ WHERE pa.tenant_id = $1
+ AND pa.deleted_at IS NULL
+ AND pa.status = 'active'
+ AND pa.id = ANY($2)
+ ORDER BY mp.sort_order ASC, pa.id ASC
+ `, tenantID, ids)
+ if err != nil {
+ return nil, response.ErrInternal(50058, "platform_account_publish_query_failed", "failed to load platform accounts")
+ }
+ defer rows.Close()
+
+ items := make([]platformAccountSeed, 0, len(ids))
+ for rows.Next() {
+ var item platformAccountSeed
+ if err := rows.Scan(&item.ID, &item.PlatformID, &item.PlatformName, &item.PlatformUID, &item.Nickname); err != nil {
+ return nil, response.ErrInternal(50058, "platform_account_publish_scan_failed", err.Error())
+ }
+ items = append(items, item)
+ }
+ if len(items) != len(uniqueInt64(ids)) {
+ return nil, response.ErrBadRequest(40037, "invalid_platform_accounts", "one or more selected platform accounts are unavailable")
+ }
+ return items, nil
+}
+
+func recalculatePublishBatchStatus(ctx context.Context, tx pgx.Tx, publishBatchID int64) (string, error) {
+ rows, err := tx.Query(ctx, `
+ SELECT status
+ FROM publish_records
+ WHERE publish_batch_id = $1
+ `, publishBatchID)
+ if err != nil {
+ return "", response.ErrInternal(50059, "publish_batch_status_query_failed", "failed to aggregate publish batch status")
+ }
+ defer rows.Close()
+
+ statuses := make([]string, 0)
+ for rows.Next() {
+ var status string
+ if err := rows.Scan(&status); err != nil {
+ return "", response.ErrInternal(50059, "publish_batch_status_scan_failed", err.Error())
+ }
+ statuses = append(statuses, normalizePublishStatus(status))
+ }
+ return aggregatePublishStatuses(statuses), nil
+}
+
+func aggregatePublishStatuses(statuses []string) string {
+ if len(statuses) == 0 {
+ return "pending"
+ }
+
+ counts := map[string]int{}
+ for _, status := range statuses {
+ counts[normalizePublishStatus(status)]++
+ }
+
+ if counts["queued"] > 0 || counts["publishing"] > 0 {
+ return "publishing"
+ }
+ if counts["failed"] == len(statuses) {
+ return "failed"
+ }
+ if counts["success"] == len(statuses) {
+ return "success"
+ }
+ if counts["pending_review"] == len(statuses) {
+ return "pending_review"
+ }
+ if counts["failed"] > 0 && (counts["success"] > 0 || counts["pending_review"] > 0) {
+ return "partial_success"
+ }
+ if counts["pending_review"] > 0 {
+ return "pending_review"
+ }
+ if counts["success"] > 0 {
+ return "success"
+ }
+ return "publishing"
+}
+
+func normalizePublishStatus(status string) string {
+ switch strings.TrimSpace(status) {
+ case "queued":
+ return "queued"
+ case "publishing", "running":
+ return "publishing"
+ case "success", "published", "publish_success":
+ return "success"
+ case "failed", "publish_failed":
+ return "failed"
+ case "pending_review":
+ return "pending_review"
+ default:
+ return "failed"
+ }
+}
+
+func normalizeStringPointer(value *string) *string {
+ if value == nil {
+ return nil
+ }
+ trimmed := strings.TrimSpace(*value)
+ if trimmed == "" {
+ return nil
+ }
+ return &trimmed
+}
+
+func nilIfBlank(value string) *string {
+ value = strings.TrimSpace(value)
+ if value == "" {
+ return nil
+ }
+ return &value
+}
+
+func limitStringPointer(value *string, maxLen int) *string {
+ if value == nil || maxLen <= 0 {
+ return value
+ }
+ runes := []rune(*value)
+ if len(runes) <= maxLen {
+ return value
+ }
+ trimmed := string(runes[:maxLen])
+ return &trimmed
+}
+
+func marshalJSON(value interface{}) ([]byte, error) {
+ if value == nil {
+ return nil, nil
+ }
+ return json.Marshal(value)
+}
+
+func newSessionToken() (string, error) {
+ buf := make([]byte, 24)
+ if _, err := rand.Read(buf); err != nil {
+ return "", err
+ }
+ return hex.EncodeToString(buf), nil
+}
+
+func uniqueInt64(values []int64) []int64 {
+ seen := make(map[int64]struct{}, len(values))
+ result := make([]int64, 0, len(values))
+ for _, value := range values {
+ if _, ok := seen[value]; ok {
+ continue
+ }
+ seen[value] = struct{}{}
+ result = append(result, value)
+ }
+ return result
+}
diff --git a/server/internal/tenant/transport/media_handler.go b/server/internal/tenant/transport/media_handler.go
new file mode 100644
index 0000000..3e8daa2
--- /dev/null
+++ b/server/internal/tenant/transport/media_handler.go
@@ -0,0 +1,139 @@
+package transport
+
+import (
+ "strconv"
+
+ "github.com/gin-gonic/gin"
+
+ "github.com/geo-platform/tenant-api/internal/bootstrap"
+ "github.com/geo-platform/tenant-api/internal/shared/response"
+ "github.com/geo-platform/tenant-api/internal/tenant/app"
+)
+
+type MediaHandler struct {
+ svc *app.MediaService
+}
+
+func NewMediaHandler(a *bootstrap.App) *MediaHandler {
+ return &MediaHandler{svc: app.NewMediaService(a.DB)}
+}
+
+func (h *MediaHandler) ListPlatforms(c *gin.Context) {
+ data, err := h.svc.ListPlatforms(c.Request.Context())
+ if err != nil {
+ response.Error(c, err)
+ return
+ }
+ response.Success(c, data)
+}
+
+func (h *MediaHandler) ListPlatformAccounts(c *gin.Context) {
+ data, err := h.svc.ListPlatformAccounts(c.Request.Context())
+ if err != nil {
+ response.Error(c, err)
+ return
+ }
+ response.Success(c, data)
+}
+
+func (h *MediaHandler) DeletePlatformAccount(c *gin.Context) {
+ id, err := strconv.ParseInt(c.Param("id"), 10, 64)
+ if err != nil {
+ response.Error(c, response.ErrBadRequest(40001, "invalid_id", "platform account id must be a number"))
+ return
+ }
+ if err := h.svc.DeletePlatformAccount(c.Request.Context(), id); err != nil {
+ response.Error(c, err)
+ return
+ }
+ response.Success(c, nil)
+}
+
+func (h *MediaHandler) CreatePluginSession(c *gin.Context) {
+ var req app.CreatePluginSessionRequest
+ if err := c.ShouldBindJSON(&req); err != nil {
+ response.Error(c, response.ErrBadRequest(40001, "invalid_params", err.Error()))
+ return
+ }
+ data, err := h.svc.CreatePluginSession(c.Request.Context(), req)
+ if err != nil {
+ response.Error(c, err)
+ return
+ }
+ response.Success(c, data)
+}
+
+func (h *MediaHandler) RegisterPluginInstallation(c *gin.Context) {
+ var req app.RegisterPluginInstallationRequest
+ if err := c.ShouldBindJSON(&req); err != nil {
+ response.Error(c, response.ErrBadRequest(40001, "invalid_params", err.Error()))
+ return
+ }
+ data, err := h.svc.RegisterPluginInstallation(c.Request.Context(), req)
+ if err != nil {
+ response.Error(c, err)
+ return
+ }
+ response.Success(c, data)
+}
+
+func (h *MediaHandler) CreatePublishBatch(c *gin.Context) {
+ articleID, err := strconv.ParseInt(c.Param("id"), 10, 64)
+ if err != nil {
+ response.Error(c, response.ErrBadRequest(40001, "invalid_id", "article id must be a number"))
+ return
+ }
+ var req app.CreatePublishBatchRequest
+ if err := c.ShouldBindJSON(&req); err != nil {
+ response.Error(c, response.ErrBadRequest(40001, "invalid_params", err.Error()))
+ return
+ }
+ data, err := h.svc.CreatePublishBatch(c.Request.Context(), articleID, req)
+ if err != nil {
+ response.Error(c, err)
+ return
+ }
+ response.SuccessWithStatus(c, 201, data)
+}
+
+func (h *MediaHandler) ListPublishRecords(c *gin.Context) {
+ articleID, err := strconv.ParseInt(c.Param("id"), 10, 64)
+ if err != nil {
+ response.Error(c, response.ErrBadRequest(40001, "invalid_id", "article id must be a number"))
+ return
+ }
+ data, err := h.svc.ListArticlePublishRecords(c.Request.Context(), articleID)
+ if err != nil {
+ response.Error(c, err)
+ return
+ }
+ response.Success(c, data)
+}
+
+func (h *MediaHandler) PluginBindCallback(c *gin.Context) {
+ var req app.PluginBindCallbackRequest
+ if err := c.ShouldBindJSON(&req); err != nil {
+ response.Error(c, response.ErrBadRequest(40001, "invalid_params", err.Error()))
+ return
+ }
+ data, err := h.svc.HandleBindCallback(c.Request.Context(), req)
+ if err != nil {
+ response.Error(c, err)
+ return
+ }
+ response.Success(c, data)
+}
+
+func (h *MediaHandler) PluginPublishCallback(c *gin.Context) {
+ var req app.PluginPublishCallbackRequest
+ if err := c.ShouldBindJSON(&req); err != nil {
+ response.Error(c, response.ErrBadRequest(40001, "invalid_params", err.Error()))
+ return
+ }
+ data, err := h.svc.HandlePublishCallback(c.Request.Context(), req)
+ if err != nil {
+ response.Error(c, err)
+ return
+ }
+ response.Success(c, data)
+}
diff --git a/server/internal/tenant/transport/router.go b/server/internal/tenant/transport/router.go
index d9c5e0c..2a6dd87 100644
--- a/server/internal/tenant/transport/router.go
+++ b/server/internal/tenant/transport/router.go
@@ -12,6 +12,11 @@ func RegisterRoutes(app *bootstrap.App) {
authAPI.POST("/login", authHandler.Login)
authAPI.POST("/refresh", authHandler.Refresh)
+ pluginHandler := NewMediaHandler(app)
+ callbacks := app.Engine.Group("/api/callback/plugin")
+ callbacks.POST("/bind", pluginHandler.PluginBindCallback)
+ callbacks.POST("/publish", pluginHandler.PluginPublishCallback)
+
protected := app.Engine.Group("/api")
protected.Use(auth.Middleware(app.JWT, app.Sessions))
protected.Use(middleware.TenantScope())
@@ -47,8 +52,17 @@ func RegisterRoutes(app *bootstrap.App) {
articles.PUT("/:id", artHandler.Update)
articles.GET("/:id/stream", artHandler.Stream)
articles.GET("/:id/versions", artHandler.Versions)
+ articles.GET("/:id/publish-records", pluginHandler.ListPublishRecords)
+ articles.POST("/:id/publish-batches", pluginHandler.CreatePublishBatch)
articles.DELETE("/:id", artHandler.Delete)
+ media := protected.Group("/tenant/media")
+ media.GET("/platforms", pluginHandler.ListPlatforms)
+ media.GET("/platform-accounts", pluginHandler.ListPlatformAccounts)
+ media.POST("/plugin-installations/register", pluginHandler.RegisterPluginInstallation)
+ media.POST("/plugin-sessions", pluginHandler.CreatePluginSession)
+ media.DELETE("/platform-accounts/:id", pluginHandler.DeletePlatformAccount)
+
brands := protected.Group("/tenant/brands")
brandHandler := NewBrandHandler(app)
brands.GET("", brandHandler.List)
diff --git a/server/migrations/20260402140000_create_media_publisher_tables.down.sql b/server/migrations/20260402140000_create_media_publisher_tables.down.sql
new file mode 100644
index 0000000..587bcaa
--- /dev/null
+++ b/server/migrations/20260402140000_create_media_publisher_tables.down.sql
@@ -0,0 +1,6 @@
+DROP TABLE IF EXISTS publish_records;
+DROP TABLE IF EXISTS publish_batches;
+DROP TABLE IF EXISTS plugin_sessions;
+DROP TABLE IF EXISTS plugin_installations;
+DROP TABLE IF EXISTS platform_accounts;
+DROP TABLE IF EXISTS media_platforms;
diff --git a/server/migrations/20260402140000_create_media_publisher_tables.up.sql b/server/migrations/20260402140000_create_media_publisher_tables.up.sql
new file mode 100644
index 0000000..82cf3b3
--- /dev/null
+++ b/server/migrations/20260402140000_create_media_publisher_tables.up.sql
@@ -0,0 +1,137 @@
+CREATE TABLE media_platforms (
+ id BIGSERIAL PRIMARY KEY,
+ platform_id VARCHAR(64) NOT NULL,
+ name VARCHAR(100) NOT NULL,
+ category VARCHAR(32) NOT NULL DEFAULT 'ugc',
+ short_name VARCHAR(16) NOT NULL,
+ accent_color VARCHAR(16) NOT NULL DEFAULT '#1677ff',
+ login_url TEXT,
+ logo_url TEXT,
+ status VARCHAR(20) NOT NULL DEFAULT 'active',
+ sort_order INT NOT NULL DEFAULT 0,
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
+);
+
+CREATE UNIQUE INDEX uk_media_platforms_platform_id ON media_platforms(platform_id);
+CREATE INDEX idx_media_platforms_status_sort ON media_platforms(status, sort_order, id);
+
+INSERT INTO media_platforms (platform_id, name, category, short_name, accent_color, login_url, sort_order)
+VALUES
+ ('toutiaohao', '头条号', 'news', '头', '#f5222d', 'https://mp.toutiao.com/auth/page/login', 10),
+ ('baijiahao', '百家号', 'news', '百', '#31445a', 'https://baijiahao.baidu.com/builder/theme/bjh/login', 20),
+ ('sohuhao', '搜狐号', 'news', '搜', '#fa8c16', 'https://mp.sohu.com/mpfe/v4/login', 30),
+ ('qiehao', '企鹅号', 'social', 'Q', '#111827', 'https://om.qq.com', 40),
+ ('zhihu', '知乎', 'social', '知', '#1677ff', 'https://www.zhihu.com/signin', 50),
+ ('wangyihao', '网易号', 'news', '网', '#ff4d4f', 'https://mp.163.com/login.html', 60),
+ ('jianshu', '简书', 'social', '简', '#f56a5e', 'https://www.jianshu.com/sign_in', 70),
+ ('bilibili', 'bilibili', 'video', 'B', '#eb2f96', 'https://www.bilibili.com', 80),
+ ('juejin', '稀土掘金', 'social', '掘', '#1677ff', 'https://juejin.cn/login', 90),
+ ('smzdm', '什么值得买', 'social', '值', '#f5222d', 'https://zhiyou.smzdm.com/user/login', 100),
+ ('weixin_gzh', '微信公众号', 'social', '微', '#13c26b', 'https://mp.weixin.qq.com/cgi-bin/loginpage', 110),
+ ('zol', '中关村在线', 'news', 'Z', '#ff4d4f', 'https://post.zol.com.cn/v2/manage/works/all', 120),
+ ('dongchedi', '懂车帝', 'news', '懂', '#fadb14', 'https://mp.dcdapp.com/login', 130);
+
+CREATE TABLE platform_accounts (
+ id BIGSERIAL PRIMARY KEY,
+ tenant_id BIGINT NOT NULL REFERENCES tenants(id),
+ user_id BIGINT NOT NULL REFERENCES users(id),
+ platform_id VARCHAR(64) NOT NULL REFERENCES media_platforms(platform_id),
+ platform_uid VARCHAR(128) NOT NULL,
+ nickname VARCHAR(255) NOT NULL,
+ avatar_url TEXT,
+ status VARCHAR(20) NOT NULL DEFAULT 'active',
+ metadata_json JSONB,
+ last_check_at TIMESTAMPTZ,
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
+ deleted_at TIMESTAMPTZ
+);
+
+CREATE UNIQUE INDEX uk_platform_accounts_identity_active
+ ON platform_accounts(tenant_id, platform_id, platform_uid)
+ WHERE deleted_at IS NULL;
+CREATE INDEX idx_platform_accounts_tenant_platform ON platform_accounts(tenant_id, platform_id, status, created_at DESC);
+
+CREATE TABLE plugin_installations (
+ id BIGSERIAL PRIMARY KEY,
+ tenant_id BIGINT NOT NULL REFERENCES tenants(id),
+ user_id BIGINT NOT NULL REFERENCES users(id),
+ installation_key VARCHAR(128) NOT NULL,
+ installation_name VARCHAR(255) NOT NULL,
+ browser_name VARCHAR(64),
+ client_version VARCHAR(32),
+ installation_token_hash VARCHAR(128) NOT NULL,
+ status VARCHAR(20) NOT NULL DEFAULT 'active',
+ last_seen_at TIMESTAMPTZ,
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
+ deleted_at TIMESTAMPTZ
+);
+
+CREATE UNIQUE INDEX uk_plugin_installations_key_active
+ ON plugin_installations(tenant_id, installation_key)
+ WHERE deleted_at IS NULL;
+CREATE INDEX idx_plugin_installations_tenant_status
+ ON plugin_installations(tenant_id, status, created_at DESC);
+
+CREATE TABLE plugin_sessions (
+ id BIGSERIAL PRIMARY KEY,
+ tenant_id BIGINT NOT NULL REFERENCES tenants(id),
+ user_id BIGINT NOT NULL REFERENCES users(id),
+ plugin_installation_id BIGINT REFERENCES plugin_installations(id),
+ action_type VARCHAR(20) NOT NULL,
+ resource_type VARCHAR(30),
+ resource_id BIGINT,
+ platform_account_id BIGINT REFERENCES platform_accounts(id),
+ platform_id VARCHAR(64) NOT NULL REFERENCES media_platforms(platform_id),
+ session_token VARCHAR(128) NOT NULL,
+ client_version VARCHAR(32),
+ status VARCHAR(20) NOT NULL DEFAULT 'pending',
+ expire_at TIMESTAMPTZ NOT NULL,
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
+);
+
+CREATE UNIQUE INDEX uk_plugin_sessions_token ON plugin_sessions(session_token);
+CREATE INDEX idx_plugin_sessions_status_expire ON plugin_sessions(status, expire_at);
+CREATE INDEX idx_plugin_sessions_tenant_action ON plugin_sessions(tenant_id, action_type, created_at DESC);
+
+CREATE TABLE publish_batches (
+ id BIGSERIAL PRIMARY KEY,
+ tenant_id BIGINT NOT NULL REFERENCES tenants(id),
+ article_id BIGINT NOT NULL REFERENCES articles(id),
+ initiator_user_id BIGINT NOT NULL REFERENCES users(id),
+ status VARCHAR(20) NOT NULL DEFAULT 'pending',
+ publish_type VARCHAR(20) NOT NULL DEFAULT 'publish',
+ cover_asset_url TEXT,
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
+);
+
+CREATE INDEX idx_publish_batches_article_created ON publish_batches(article_id, created_at DESC);
+CREATE INDEX idx_publish_batches_tenant_status ON publish_batches(tenant_id, status, created_at DESC);
+
+CREATE TABLE publish_records (
+ id BIGSERIAL PRIMARY KEY,
+ tenant_id BIGINT NOT NULL REFERENCES tenants(id),
+ publish_batch_id BIGINT NOT NULL REFERENCES publish_batches(id) ON DELETE CASCADE,
+ article_id BIGINT NOT NULL REFERENCES articles(id),
+ platform_account_id BIGINT NOT NULL REFERENCES platform_accounts(id),
+ platform_id VARCHAR(64) NOT NULL REFERENCES media_platforms(platform_id),
+ status VARCHAR(20) NOT NULL DEFAULT 'queued',
+ external_article_id VARCHAR(128),
+ external_article_url TEXT,
+ external_manage_url TEXT,
+ published_at TIMESTAMPTZ,
+ request_payload_json JSONB,
+ response_payload_json JSONB,
+ error_message TEXT,
+ retry_count INT NOT NULL DEFAULT 0,
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
+);
+
+CREATE UNIQUE INDEX uk_publish_records_batch_account ON publish_records(publish_batch_id, platform_account_id);
+CREATE INDEX idx_publish_records_article_created ON publish_records(article_id, created_at DESC);
+CREATE INDEX idx_publish_records_status ON publish_records(status, created_at DESC);
diff --git a/server/migrations/20260402153000_repair_plugin_installations_schema.down.sql b/server/migrations/20260402153000_repair_plugin_installations_schema.down.sql
new file mode 100644
index 0000000..72e5efb
--- /dev/null
+++ b/server/migrations/20260402153000_repair_plugin_installations_schema.down.sql
@@ -0,0 +1,4 @@
+-- No-op rollback.
+-- This migration repairs databases that already applied an older variant of
+-- 20260402140000 without plugin_installations. Dropping these objects on
+-- rollback would break databases created from the current baseline migration.
diff --git a/server/migrations/20260402153000_repair_plugin_installations_schema.up.sql b/server/migrations/20260402153000_repair_plugin_installations_schema.up.sql
new file mode 100644
index 0000000..7fc3954
--- /dev/null
+++ b/server/migrations/20260402153000_repair_plugin_installations_schema.up.sql
@@ -0,0 +1,39 @@
+CREATE TABLE IF NOT EXISTS plugin_installations (
+ id BIGSERIAL PRIMARY KEY,
+ tenant_id BIGINT NOT NULL REFERENCES tenants(id),
+ user_id BIGINT NOT NULL REFERENCES users(id),
+ installation_key VARCHAR(128) NOT NULL,
+ installation_name VARCHAR(255) NOT NULL,
+ browser_name VARCHAR(64),
+ client_version VARCHAR(32),
+ installation_token_hash VARCHAR(128) NOT NULL,
+ status VARCHAR(20) NOT NULL DEFAULT 'active',
+ last_seen_at TIMESTAMPTZ,
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
+ deleted_at TIMESTAMPTZ
+);
+
+CREATE UNIQUE INDEX IF NOT EXISTS uk_plugin_installations_key_active
+ ON plugin_installations(tenant_id, installation_key)
+ WHERE deleted_at IS NULL;
+
+CREATE INDEX IF NOT EXISTS idx_plugin_installations_tenant_status
+ ON plugin_installations(tenant_id, status, created_at DESC);
+
+ALTER TABLE plugin_sessions
+ ADD COLUMN IF NOT EXISTS plugin_installation_id BIGINT;
+
+DO $$
+BEGIN
+ IF NOT EXISTS (
+ SELECT 1
+ FROM pg_constraint
+ WHERE conname = 'plugin_sessions_plugin_installation_id_fkey'
+ ) THEN
+ ALTER TABLE plugin_sessions
+ ADD CONSTRAINT plugin_sessions_plugin_installation_id_fkey
+ FOREIGN KEY (plugin_installation_id)
+ REFERENCES plugin_installations(id);
+ END IF;
+END $$;