feat: 建立 DOCX 共享协议与准备服务
This commit is contained in:
@@ -0,0 +1,149 @@
|
||||
import { z } from "zod";
|
||||
import { exportConfigSchema } from "./export-config.js";
|
||||
|
||||
export const DOCX_PANDOC_VERSION = "3.9.0.2";
|
||||
export const DOCX_MIME_TYPE =
|
||||
"application/vnd.openxmlformats-officedocument.wordprocessingml.document";
|
||||
export const DOCX_FILE_EXTENSION = ".docx";
|
||||
export const MAXIMUM_DOCX_MARKDOWN_LENGTH = 1_500_000;
|
||||
export const MAXIMUM_DOCX_FILE_NAME_LENGTH = 500;
|
||||
export const MAXIMUM_DOCX_RESOURCE_COUNT = 50;
|
||||
|
||||
export const documentExportFormatSchema = z.enum(["pdf", "docx"]);
|
||||
export type DocumentExportFormat = z.infer<
|
||||
typeof documentExportFormatSchema
|
||||
>;
|
||||
|
||||
export const docxSourceResourceSchema = z.object({
|
||||
path: z.string().min(1).max(1_000),
|
||||
contentType: z.string().min(1).max(100).optional(),
|
||||
data: z.string().min(1)
|
||||
});
|
||||
|
||||
export type DocxSourceResource = z.infer<
|
||||
typeof docxSourceResourceSchema
|
||||
>;
|
||||
|
||||
export const docxExportRequestSchema = z.object({
|
||||
markdown: z.string().max(MAXIMUM_DOCX_MARKDOWN_LENGTH),
|
||||
fileName: z
|
||||
.string()
|
||||
.max(MAXIMUM_DOCX_FILE_NAME_LENGTH)
|
||||
.default("文档.md"),
|
||||
language: z.string().max(50).default("zh-CN"),
|
||||
resources: z
|
||||
.array(docxSourceResourceSchema)
|
||||
.max(MAXIMUM_DOCX_RESOURCE_COUNT)
|
||||
.default([]),
|
||||
exportConfig: exportConfigSchema
|
||||
});
|
||||
|
||||
export type DocxExportRequest = z.infer<
|
||||
typeof docxExportRequestSchema
|
||||
>;
|
||||
export type DocxExportRequestInput = z.input<
|
||||
typeof docxExportRequestSchema
|
||||
>;
|
||||
|
||||
export const docxRuntimeStatusSchema = z.enum([
|
||||
"available",
|
||||
"not-found",
|
||||
"version-mismatch",
|
||||
"not-executable",
|
||||
"probe-timeout"
|
||||
]);
|
||||
|
||||
export type DocxRuntimeStatus = z.infer<
|
||||
typeof docxRuntimeStatusSchema
|
||||
>;
|
||||
|
||||
const availableDocxCapabilitySchema = z.object({
|
||||
format: z.literal("docx"),
|
||||
status: z.literal("available"),
|
||||
expectedVersion: z.literal(DOCX_PANDOC_VERSION),
|
||||
detectedVersion: z.literal(DOCX_PANDOC_VERSION)
|
||||
});
|
||||
|
||||
const unavailableDocxCapabilitySchema = z.object({
|
||||
format: z.literal("docx"),
|
||||
status: docxRuntimeStatusSchema.exclude(["available"]),
|
||||
expectedVersion: z.literal(DOCX_PANDOC_VERSION),
|
||||
detectedVersion: z.string().max(100).optional(),
|
||||
message: z.string().max(500)
|
||||
});
|
||||
|
||||
export const docxCapabilitySchema = z.discriminatedUnion("status", [
|
||||
availableDocxCapabilitySchema,
|
||||
unavailableDocxCapabilitySchema
|
||||
]);
|
||||
|
||||
export type DocxCapability = z.infer<typeof docxCapabilitySchema>;
|
||||
|
||||
export const docxExportErrorCodeSchema = z.enum([
|
||||
"INVALID_DOCX_REQUEST",
|
||||
"INVALID_EXPORT_CONFIG",
|
||||
"INVALID_FILE_NAME",
|
||||
"INVALID_MARKDOWN",
|
||||
"MARKDOWN_TOO_LARGE",
|
||||
"INVALID_IMAGE_RESOURCES",
|
||||
"IMAGE_RESOURCES_TOO_LARGE",
|
||||
"THEME_NOT_FOUND",
|
||||
"DOCX_RUNTIME_NOT_FOUND",
|
||||
"DOCX_RUNTIME_VERSION_MISMATCH",
|
||||
"DOCX_RUNTIME_NOT_EXECUTABLE",
|
||||
"DOCX_RUNTIME_PROBE_TIMEOUT",
|
||||
"DOCX_QUEUE_FULL",
|
||||
"DOCX_RENDER_TIMEOUT",
|
||||
"DOCX_GENERATION_FAILED",
|
||||
"DOCX_OUTPUT_INVALID"
|
||||
]);
|
||||
|
||||
export type DocxExportErrorCode = z.infer<
|
||||
typeof docxExportErrorCodeSchema
|
||||
>;
|
||||
|
||||
export const docxExportErrorResponseSchema = z.object({
|
||||
error: docxExportErrorCodeSchema,
|
||||
message: z.string().min(1).max(1_000),
|
||||
retryable: z.boolean().default(false)
|
||||
});
|
||||
|
||||
export type DocxExportErrorResponse = z.infer<
|
||||
typeof docxExportErrorResponseSchema
|
||||
>;
|
||||
|
||||
export interface DocxGenerationTimings {
|
||||
probeMs: number;
|
||||
prepareMs: number;
|
||||
mediaMs: number;
|
||||
referenceMs: number;
|
||||
pandocMs: number;
|
||||
validationMs: number;
|
||||
totalMs: number;
|
||||
}
|
||||
|
||||
export interface DocxExportDiagnostics {
|
||||
warnings: string[];
|
||||
echartsErrors: string[];
|
||||
mermaidErrors: string[];
|
||||
}
|
||||
|
||||
export interface DocxExportResult {
|
||||
docx: Uint8Array;
|
||||
fileName: string;
|
||||
diagnostics: DocxExportDiagnostics;
|
||||
timings: DocxGenerationTimings;
|
||||
}
|
||||
|
||||
export function createDocxFileName(fileName: unknown) {
|
||||
const source =
|
||||
typeof fileName === "string"
|
||||
? fileName.split(/[\\/]/).at(-1) ?? ""
|
||||
: "";
|
||||
const stem = source
|
||||
.replace(/\.(?:md|markdown|docx)$/i, "")
|
||||
.replace(/[<>:"/\\|?*\u0000-\u001f]/g, "_")
|
||||
.replace(/[.\s]+$/g, "")
|
||||
.trim();
|
||||
return `${stem || "文档"}${DOCX_FILE_EXTENSION}`;
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
export * from "./document.js";
|
||||
export * from "./docx.js";
|
||||
export * from "./document-profile.js";
|
||||
export * from "./document-link.js";
|
||||
export * from "./export-config.js";
|
||||
|
||||
Reference in New Issue
Block a user