wendang 0.2.4 → 0.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.CELL_BACKGROUNDS=void 0,exports.cellBackground=function cellBackground(e){return"string"==typeof e&&exports.CELL_BACKGROUNDS.some(a=>a.value===e)?e:null},exports.CELL_BACKGROUNDS=[{name:"默认",value:null},{name:"灰",value:"#f1f3f5"},{name:"黄",value:"#fff3bf"},{name:"绿",value:"#d3f9d8"},{name:"蓝",value:"#d0ebff"},{name:"紫",value:"#e5dbff"},{name:"红",value:"#ffe3e3"}];
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.CELL_BACKGROUNDS=void 0,exports.cellBackground=function cellBackground(e){return"string"==typeof e&&exports.CELL_BACKGROUNDS.some(o=>o.value===e)?e:null};const e=require("./document-colors.js");exports.CELL_BACKGROUNDS=e.HIGHLIGHT_COLORS;
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.ContentSchemaService=exports.SCHEMA_VERSION=exports.ALLOWED_NODES=void 0;const t=require("./error.js"),e="validation.failed",n=require("./doc-node-whitelist.js");Object.defineProperty(exports,"ALLOWED_NODES",{enumerable:!0,get:function(){return n.ALLOWED_NODES}});const r=require("./video-embed.js"),o=require("./schema-version.js"),i=require("./mind-map.js"),a=require("./callout.js"),s=require("./workbook.js");exports.SCHEMA_VERSION=o.CONTENT_SCHEMA_VERSION;const l={heading:new Set(["level","textAlign","indent"]),paragraph:new Set(["textAlign","indent"]),orderedList:new Set(["start"]),textStyle:new Set(["color"]),highlight:new Set(["color"]),image:new Set(["attachmentId","alt","width","align"]),fileAttachment:new Set(["attachmentId","filename","mimeType","size"]),videoEmbed:new Set(["provider","videoId","page","startSeconds"]),mindMap:new Set(["id","rootId","formatVersion","layout","theme","height","branchStyle"]),mindMapTopic:new Set(["id","parentId","order","shape","color","background","fontSize","align","icon","done","attachmentId","side"]),mindMapRelation:new Set(["id","source","target","kind","label"]),callout:new Set(["tone","icon","textColor","borderColor","backgroundColor"]),codeBlock:new Set(["language"]),link:new Set(["href","target","rel"]),pageLink:new Set(["pageId","label"]),mention:new Set(["userId","label"]),tableCell:new Set(["colspan","rowspan","align","colwidth","backgroundColor"]),tableHeader:new Set(["colspan","rowspan","align","colwidth","backgroundColor"]),taskItem:new Set(["checked"]),mathInline:new Set(["latex"]),mathBlock:new Set(["latex"])},c=new Set(["colspan","rowspan"]),d=new Set(["#f1f3f5","#fff3bf","#d3f9d8","#d0ebff","#e5dbff","#ffe3e3"]);function isTableCellType(t){return"tableCell"===t||"tableHeader"===t}function coercePositiveInt(t){const e="number"==typeof t?t:Number.parseInt(String(t??""),10);if(!Number.isFinite(e))return;const n=Math.floor(e);return n>=1?n:void 0}function coerceColwidth(t){if(null===t)return null;let e;if(Array.isArray(t))e=t;else{if("string"!=typeof t)return;{const n=t.trim();if(!n)return null;if(n.startsWith("["))try{const t=JSON.parse(n);e=Array.isArray(t)?t:[]}catch{return}else e=n.split(",")}}if(0===e.length)return null;const n=e.map(t=>{const e="number"==typeof t?t:Number.parseInt(String(t??""),10);return Number.isFinite(e)&&e>0?Math.floor(e):0});return n.some(t=>t>0)?n:null}exports.ContentSchemaService=class ContentSchemaService{validate(o,f){if(!Number.isSafeInteger(f)||f<1)throw t.ContentValidationError.validation("内容版本必须是正整数");if((0,s.isWorkbookDocument)(o)){if(10!==f)throw t.ContentValidationError.validation("工作表需要当前编辑器版本");try{return{doc:(0,s.validateWorkbook)(o),schemaVersion:f,linkTargetIds:[],attachmentIds:[],mentionUserIds:[]}}catch(e){throw t.ContentValidationError.validation(e instanceof Error?e.message:"工作表格式无效")}}if("object"!=typeof o||null===o||"doc"!==o.type)throw t.ContentValidationError.validation("正文格式非法:根节点必须是 doc");if(f>exports.SCHEMA_VERSION)throw new t.ContentValidationError(e,`正文版本 ${f} 高于服务端支持的 ${exports.SCHEMA_VERSION},请升级后再编辑`,422);const p=new Set,h=new Set,m=new Set;let u=0;const walk=(o,s,f="")=>{if("object"!=typeof o||null===o)throw t.ContentValidationError.validation("正文包含非法节点");if(s>100)throw t.ContentValidationError.validation("正文嵌套层级过深");const S=o,g=S.type;if(("mindMapTopic"===g||"mindMapRelation"===g)&&"mindMap"!==f)throw t.ContentValidationError.validation("导图主题只能放在思维导图内");if(["mindMapDescription","mindMapNote"].includes(String(g))&&"mindMapTopic"!==f)throw t.ContentValidationError.validation("导图描述只能放在主题内");if("mindMap"===g)try{(0,i.validateMindMap)(S)}catch(e){throw t.ContentValidationError.validation(e instanceof Error?e.message:"思维导图结构无效")}if("callout"===g&&!(0,a.validCalloutAttrs)(S.attrs??{}))throw t.ContentValidationError.validation("高亮块属性无效");if("string"==typeof g){if(!n.ALLOWED_NODES.has(g))throw new t.ContentValidationError(e,`正文包含服务端不支持的节点「${g}」,已拒绝保存以避免内容丢失`,422,{nodeType:g,schemaVersion:exports.SCHEMA_VERSION});if(u+=1,u>2e4)throw t.ContentValidationError.validation("正文节点数超出上限")}const y={};if("string"==typeof g&&(y.type=g),S.attrs&&"object"==typeof S.attrs){const t="string"==typeof g?l[g]:void 0,e={};for(const[n,r]of Object.entries(S.attrs))if(t&&t.has(n))if(isTableCellType(g)&&"backgroundColor"===n)"string"==typeof r&&d.has(r)&&(e[n]=r);else if("href"!==n||"string"!=typeof r||this.isSafeHref(r)){if(isTableCellType(g)&&c.has(n)){const t=coercePositiveInt(r);if(void 0===t)continue;e[n]=t;continue}if(isTableCellType(g)&&"colwidth"===n){const t=coerceColwidth(r);if(void 0===t)continue;e[n]=t;continue}e[n]=r}Object.keys(e).length>0&&(y.attrs=e)}if("videoEmbed"===g){const e=(0,r.normalizeVideoEmbed)(S.attrs);if(!e)throw t.ContentValidationError.validation("视频属性无效,请重新插入视频");y.attrs={...e}}if("string"==typeof S.text&&(y.text=S.text),Array.isArray(S.marks)){const t=S.marks.filter(t=>"string"==typeof t.type&&n.ALLOWED_MARKS.has(t.type)).map(t=>{const e={type:t.type};if(t.attrs&&"object"==typeof t.attrs){const n=l[t.type],r={};for(const[e,o]of Object.entries(t.attrs))n?.has(e)&&("href"!==e||"string"==typeof o&&this.isSafeHref(o))&&(r[e]=o);Object.keys(r).length>0&&(e.attrs=r)}return e});t.length>0&&(y.marks=t)}if("pageLink"===g&&S.attrs){const t=S.attrs.pageId;"string"==typeof t&&t.length>0&&p.add(t)}if(("image"===g||"mindMapTopic"===g)&&S.attrs){const t=S.attrs.attachmentId;"string"==typeof t&&t.length>0&&h.add(t)}if("fileAttachment"===g&&S.attrs){const t=S.attrs.attachmentId;"string"==typeof t&&t.length>0&&h.add(t)}if(("mathInline"===g||"mathBlock"===g)&&S.attrs){const e=S.attrs.latex;if("string"==typeof e&&e.length>5e3)throw t.ContentValidationError.validation("公式内容过长(上限 5000 字符)")}if("mention"===g&&S.attrs){const t=S.attrs.userId;"string"==typeof t&&t.length>0&&m.add(t)}return Array.isArray(S.content)&&(y.content=S.content.filter(t=>null!=t).map(t=>walk(t,s+1,"string"==typeof g?g:""))),y};return{doc:walk(o,0),schemaVersion:exports.SCHEMA_VERSION,linkTargetIds:[...p],attachmentIds:[...h],mentionUserIds:[...m]}}isSafeHref(t){const e=t.trim().toLowerCase();return!(!e.startsWith("/")&&!e.startsWith("#"))||e.startsWith("http://")||e.startsWith("https://")||e.startsWith("mailto:")}extractPlainText(t){if((0,s.isWorkbookDocument)(t))return(0,s.workbookText)(t);const e=[],walk=t=>{if("object"!=typeof t||null===t)return;const n=t;"string"==typeof n.text&&e.push(n.text),Array.isArray(n.content)&&n.content.forEach(walk)};return walk(t),e.join(" ").replace(/\s+/g," ").trim()}};
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.ContentSchemaService=exports.SCHEMA_VERSION=exports.ALLOWED_NODES=void 0;const t=require("./error.js"),e="validation.failed",n=require("./doc-node-whitelist.js");Object.defineProperty(exports,"ALLOWED_NODES",{enumerable:!0,get:function(){return n.ALLOWED_NODES}});const r=require("./video-embed.js"),o=require("./schema-version.js"),i=require("./mind-map.js"),a=require("./callout.js"),s=require("../shared/table-cell-format.js"),l=require("./workbook.js");exports.SCHEMA_VERSION=o.CONTENT_SCHEMA_VERSION;const c={heading:new Set(["level","textAlign","indent"]),paragraph:new Set(["textAlign","indent"]),orderedList:new Set(["start"]),textStyle:new Set(["color"]),highlight:new Set(["color"]),image:new Set(["attachmentId","alt","width","align"]),fileAttachment:new Set(["attachmentId","filename","mimeType","size"]),videoEmbed:new Set(["provider","videoId","page","startSeconds"]),mindMap:new Set(["id","rootId","formatVersion","layout","theme","height","branchStyle"]),mindMapTopic:new Set(["id","parentId","order","shape","color","background","fontSize","align","icon","done","attachmentId","side"]),mindMapRelation:new Set(["id","source","target","kind","label"]),callout:new Set(["tone","icon","textColor","borderColor","backgroundColor"]),codeBlock:new Set(["language"]),link:new Set(["href","target","rel"]),pageLink:new Set(["pageId","label"]),mention:new Set(["userId","label"]),tableCell:new Set(["colspan","rowspan","align","colwidth","backgroundColor"]),tableHeader:new Set(["colspan","rowspan","align","colwidth","backgroundColor"]),taskItem:new Set(["checked"]),mathInline:new Set(["latex"]),mathBlock:new Set(["latex"])},d=new Set(["colspan","rowspan"]),f=new Set(s.CELL_BACKGROUNDS.flatMap(t=>t.value?[t.value]:[]));function isTableCellType(t){return"tableCell"===t||"tableHeader"===t}function coercePositiveInt(t){const e="number"==typeof t?t:Number.parseInt(String(t??""),10);if(!Number.isFinite(e))return;const n=Math.floor(e);return n>=1?n:void 0}function coerceColwidth(t){if(null===t)return null;let e;if(Array.isArray(t))e=t;else{if("string"!=typeof t)return;{const n=t.trim();if(!n)return null;if(n.startsWith("["))try{const t=JSON.parse(n);e=Array.isArray(t)?t:[]}catch{return}else e=n.split(",")}}if(0===e.length)return null;const n=e.map(t=>{const e="number"==typeof t?t:Number.parseInt(String(t??""),10);return Number.isFinite(e)&&e>0?Math.floor(e):0});return n.some(t=>t>0)?n:null}exports.ContentSchemaService=class ContentSchemaService{validate(o,s){if(!Number.isSafeInteger(s)||s<1)throw t.ContentValidationError.validation("内容版本必须是正整数");if((0,l.isWorkbookDocument)(o)){if(10!==s)throw t.ContentValidationError.validation("工作表需要当前编辑器版本");try{return{doc:(0,l.validateWorkbook)(o),schemaVersion:s,linkTargetIds:[],attachmentIds:[],mentionUserIds:[]}}catch(e){throw t.ContentValidationError.validation(e instanceof Error?e.message:"工作表格式无效")}}if("object"!=typeof o||null===o||"doc"!==o.type)throw t.ContentValidationError.validation("正文格式非法:根节点必须是 doc");if(s>exports.SCHEMA_VERSION)throw new t.ContentValidationError(e,`正文版本 ${s} 高于服务端支持的 ${exports.SCHEMA_VERSION},请升级后再编辑`,422);const p=new Set,h=new Set,u=new Set;let m=0;const walk=(o,s,l="")=>{if("object"!=typeof o||null===o)throw t.ContentValidationError.validation("正文包含非法节点");if(s>100)throw t.ContentValidationError.validation("正文嵌套层级过深");const S=o,g=S.type;if(("mindMapTopic"===g||"mindMapRelation"===g)&&"mindMap"!==l)throw t.ContentValidationError.validation("导图主题只能放在思维导图内");if(["mindMapDescription","mindMapNote"].includes(String(g))&&"mindMapTopic"!==l)throw t.ContentValidationError.validation("导图描述只能放在主题内");if("mindMap"===g)try{(0,i.validateMindMap)(S)}catch(e){throw t.ContentValidationError.validation(e instanceof Error?e.message:"思维导图结构无效")}if("callout"===g&&!(0,a.validCalloutAttrs)(S.attrs??{}))throw t.ContentValidationError.validation("高亮块属性无效");if("string"==typeof g){if(!n.ALLOWED_NODES.has(g))throw new t.ContentValidationError(e,`正文包含服务端不支持的节点「${g}」,已拒绝保存以避免内容丢失`,422,{nodeType:g,schemaVersion:exports.SCHEMA_VERSION});if(m+=1,m>2e4)throw t.ContentValidationError.validation("正文节点数超出上限")}const y={};if("string"==typeof g&&(y.type=g),S.attrs&&"object"==typeof S.attrs){const t="string"==typeof g?c[g]:void 0,e={};for(const[n,r]of Object.entries(S.attrs))if(t&&t.has(n))if(isTableCellType(g)&&"backgroundColor"===n)"string"==typeof r&&f.has(r)&&(e[n]=r);else if("href"!==n||"string"!=typeof r||this.isSafeHref(r)){if(isTableCellType(g)&&d.has(n)){const t=coercePositiveInt(r);if(void 0===t)continue;e[n]=t;continue}if(isTableCellType(g)&&"colwidth"===n){const t=coerceColwidth(r);if(void 0===t)continue;e[n]=t;continue}e[n]=r}Object.keys(e).length>0&&(y.attrs=e)}if("videoEmbed"===g){const e=(0,r.normalizeVideoEmbed)(S.attrs);if(!e)throw t.ContentValidationError.validation("视频属性无效,请重新插入视频");y.attrs={...e}}if("string"==typeof S.text&&(y.text=S.text),Array.isArray(S.marks)){const t=S.marks.filter(t=>"string"==typeof t.type&&n.ALLOWED_MARKS.has(t.type)).map(t=>{const e={type:t.type};if(t.attrs&&"object"==typeof t.attrs){const n=c[t.type],r={};for(const[e,o]of Object.entries(t.attrs))n?.has(e)&&("href"!==e||"string"==typeof o&&this.isSafeHref(o))&&(r[e]=o);Object.keys(r).length>0&&(e.attrs=r)}return e});t.length>0&&(y.marks=t)}if("pageLink"===g&&S.attrs){const t=S.attrs.pageId;"string"==typeof t&&t.length>0&&p.add(t)}if(("image"===g||"mindMapTopic"===g)&&S.attrs){const t=S.attrs.attachmentId;"string"==typeof t&&t.length>0&&h.add(t)}if("fileAttachment"===g&&S.attrs){const t=S.attrs.attachmentId;"string"==typeof t&&t.length>0&&h.add(t)}if(("mathInline"===g||"mathBlock"===g)&&S.attrs){const e=S.attrs.latex;if("string"==typeof e&&e.length>5e3)throw t.ContentValidationError.validation("公式内容过长(上限 5000 字符)")}if("mention"===g&&S.attrs){const t=S.attrs.userId;"string"==typeof t&&t.length>0&&u.add(t)}return Array.isArray(S.content)&&(y.content=S.content.filter(t=>null!=t).map(t=>walk(t,s+1,"string"==typeof g?g:""))),y};return{doc:walk(o,0),schemaVersion:exports.SCHEMA_VERSION,linkTargetIds:[...p],attachmentIds:[...h],mentionUserIds:[...u]}}isSafeHref(t){const e=t.trim().toLowerCase();return!(!e.startsWith("/")&&!e.startsWith("#"))||e.startsWith("http://")||e.startsWith("https://")||e.startsWith("mailto:")}extractPlainText(t){if((0,l.isWorkbookDocument)(t))return(0,l.workbookText)(t);const e=[],walk=t=>{if("object"!=typeof t||null===t)return;const n=t;"string"==typeof n.text&&e.push(n.text),Array.isArray(n.content)&&n.content.forEach(walk)};return walk(t),e.join(" ").replace(/\s+/g," ").trim()}};
@@ -1 +1 @@
1
- export const CELL_BACKGROUNDS=[{name:"默认",value:null},{name:"灰",value:"#f1f3f5"},{name:"黄",value:"#fff3bf"},{name:"绿",value:"#d3f9d8"},{name:"蓝",value:"#d0ebff"},{name:"紫",value:"#e5dbff"},{name:"红",value:"#ffe3e3"}];export function cellBackground(e){return"string"==typeof e&&CELL_BACKGROUNDS.some(a=>a.value===e)?e:null}
1
+ import{HIGHLIGHT_COLORS as o}from"./document-colors.js";export const CELL_BACKGROUNDS=o;export function cellBackground(o){return"string"==typeof o&&CELL_BACKGROUNDS.some(r=>r.value===o)?o:null}
@@ -1 +1 @@
1
- import{ContentValidationError as t}from"./error.js";const e="validation.failed";import{ALLOWED_MARKS as n,ALLOWED_NODES as r}from"./doc-node-whitelist.js";import{normalizeVideoEmbed as o}from"./video-embed.js";import{CONTENT_SCHEMA_VERSION as i}from"./schema-version.js";import{validateMindMap as a}from"./mind-map.js";import{validCalloutAttrs as s}from"./callout.js";import{isWorkbookDocument as l,validateWorkbook as c,workbookText as f}from"./workbook.js";export{r as ALLOWED_NODES};export const SCHEMA_VERSION=i;const d={heading:new Set(["level","textAlign","indent"]),paragraph:new Set(["textAlign","indent"]),orderedList:new Set(["start"]),textStyle:new Set(["color"]),highlight:new Set(["color"]),image:new Set(["attachmentId","alt","width","align"]),fileAttachment:new Set(["attachmentId","filename","mimeType","size"]),videoEmbed:new Set(["provider","videoId","page","startSeconds"]),mindMap:new Set(["id","rootId","formatVersion","layout","theme","height","branchStyle"]),mindMapTopic:new Set(["id","parentId","order","shape","color","background","fontSize","align","icon","done","attachmentId","side"]),mindMapRelation:new Set(["id","source","target","kind","label"]),callout:new Set(["tone","icon","textColor","borderColor","backgroundColor"]),codeBlock:new Set(["language"]),link:new Set(["href","target","rel"]),pageLink:new Set(["pageId","label"]),mention:new Set(["userId","label"]),tableCell:new Set(["colspan","rowspan","align","colwidth","backgroundColor"]),tableHeader:new Set(["colspan","rowspan","align","colwidth","backgroundColor"]),taskItem:new Set(["checked"]),mathInline:new Set(["latex"]),mathBlock:new Set(["latex"])},p=new Set(["colspan","rowspan"]),h=new Set(["#f1f3f5","#fff3bf","#d3f9d8","#d0ebff","#e5dbff","#ffe3e3"]);function isTableCellType(t){return"tableCell"===t||"tableHeader"===t}function coercePositiveInt(t){const e="number"==typeof t?t:Number.parseInt(String(t??""),10);if(!Number.isFinite(e))return;const n=Math.floor(e);return n>=1?n:void 0}function coerceColwidth(t){if(null===t)return null;let e;if(Array.isArray(t))e=t;else{if("string"!=typeof t)return;{const n=t.trim();if(!n)return null;if(n.startsWith("["))try{const t=JSON.parse(n);e=Array.isArray(t)?t:[]}catch{return}else e=n.split(",")}}if(0===e.length)return null;const n=e.map(t=>{const e="number"==typeof t?t:Number.parseInt(String(t??""),10);return Number.isFinite(e)&&e>0?Math.floor(e):0});return n.some(t=>t>0)?n:null}export class ContentSchemaService{validate(i,f){if(!Number.isSafeInteger(f)||f<1)throw t.validation("内容版本必须是正整数");if(l(i)){if(10!==f)throw t.validation("工作表需要当前编辑器版本");try{return{doc:c(i),schemaVersion:f,linkTargetIds:[],attachmentIds:[],mentionUserIds:[]}}catch(e){throw t.validation(e instanceof Error?e.message:"工作表格式无效")}}if("object"!=typeof i||null===i||"doc"!==i.type)throw t.validation("正文格式非法:根节点必须是 doc");if(f>SCHEMA_VERSION)throw new t(e,`正文版本 ${f} 高于服务端支持的 ${SCHEMA_VERSION},请升级后再编辑`,422);const m=new Set,g=new Set,u=new Set;let w=0;const walk=(i,l,c="")=>{if("object"!=typeof i||null===i)throw t.validation("正文包含非法节点");if(l>100)throw t.validation("正文嵌套层级过深");const f=i,y=f.type;if(("mindMapTopic"===y||"mindMapRelation"===y)&&"mindMap"!==c)throw t.validation("导图主题只能放在思维导图内");if(["mindMapDescription","mindMapNote"].includes(String(y))&&"mindMapTopic"!==c)throw t.validation("导图描述只能放在主题内");if("mindMap"===y)try{a(f)}catch(e){throw t.validation(e instanceof Error?e.message:"思维导图结构无效")}if("callout"===y&&!s(f.attrs??{}))throw t.validation("高亮块属性无效");if("string"==typeof y){if(!r.has(y))throw new t(e,`正文包含服务端不支持的节点「${y}」,已拒绝保存以避免内容丢失`,422,{nodeType:y,schemaVersion:SCHEMA_VERSION});if(w+=1,w>2e4)throw t.validation("正文节点数超出上限")}const S={};if("string"==typeof y&&(S.type=y),f.attrs&&"object"==typeof f.attrs){const t="string"==typeof y?d[y]:void 0,e={};for(const[n,r]of Object.entries(f.attrs))if(t&&t.has(n))if(isTableCellType(y)&&"backgroundColor"===n)"string"==typeof r&&h.has(r)&&(e[n]=r);else if("href"!==n||"string"!=typeof r||this.isSafeHref(r)){if(isTableCellType(y)&&p.has(n)){const t=coercePositiveInt(r);if(void 0===t)continue;e[n]=t;continue}if(isTableCellType(y)&&"colwidth"===n){const t=coerceColwidth(r);if(void 0===t)continue;e[n]=t;continue}e[n]=r}Object.keys(e).length>0&&(S.attrs=e)}if("videoEmbed"===y){const e=o(f.attrs);if(!e)throw t.validation("视频属性无效,请重新插入视频");S.attrs={...e}}if("string"==typeof f.text&&(S.text=f.text),Array.isArray(f.marks)){const t=f.marks.filter(t=>"string"==typeof t.type&&n.has(t.type)).map(t=>{const e={type:t.type};if(t.attrs&&"object"==typeof t.attrs){const n=d[t.type],r={};for(const[e,o]of Object.entries(t.attrs))n?.has(e)&&("href"!==e||"string"==typeof o&&this.isSafeHref(o))&&(r[e]=o);Object.keys(r).length>0&&(e.attrs=r)}return e});t.length>0&&(S.marks=t)}if("pageLink"===y&&f.attrs){const t=f.attrs.pageId;"string"==typeof t&&t.length>0&&m.add(t)}if(("image"===y||"mindMapTopic"===y)&&f.attrs){const t=f.attrs.attachmentId;"string"==typeof t&&t.length>0&&g.add(t)}if("fileAttachment"===y&&f.attrs){const t=f.attrs.attachmentId;"string"==typeof t&&t.length>0&&g.add(t)}if(("mathInline"===y||"mathBlock"===y)&&f.attrs){const e=f.attrs.latex;if("string"==typeof e&&e.length>5e3)throw t.validation("公式内容过长(上限 5000 字符)")}if("mention"===y&&f.attrs){const t=f.attrs.userId;"string"==typeof t&&t.length>0&&u.add(t)}return Array.isArray(f.content)&&(S.content=f.content.filter(t=>null!=t).map(t=>walk(t,l+1,"string"==typeof y?y:""))),S};return{doc:walk(i,0),schemaVersion:SCHEMA_VERSION,linkTargetIds:[...m],attachmentIds:[...g],mentionUserIds:[...u]}}isSafeHref(t){const e=t.trim().toLowerCase();return!(!e.startsWith("/")&&!e.startsWith("#"))||e.startsWith("http://")||e.startsWith("https://")||e.startsWith("mailto:")}extractPlainText(t){if(l(t))return f(t);const e=[],walk=t=>{if("object"!=typeof t||null===t)return;const n=t;"string"==typeof n.text&&e.push(n.text),Array.isArray(n.content)&&n.content.forEach(walk)};return walk(t),e.join(" ").replace(/\s+/g," ").trim()}}
1
+ import{ContentValidationError as t}from"./error.js";const e="validation.failed";import{ALLOWED_MARKS as n,ALLOWED_NODES as r}from"./doc-node-whitelist.js";import{normalizeVideoEmbed as o}from"./video-embed.js";import{CONTENT_SCHEMA_VERSION as i}from"./schema-version.js";import{validateMindMap as a}from"./mind-map.js";import{validCalloutAttrs as s}from"./callout.js";import{CELL_BACKGROUNDS as l}from"../shared/table-cell-format.js";import{isWorkbookDocument as c,validateWorkbook as d,workbookText as f}from"./workbook.js";export{r as ALLOWED_NODES};export const SCHEMA_VERSION=i;const p={heading:new Set(["level","textAlign","indent"]),paragraph:new Set(["textAlign","indent"]),orderedList:new Set(["start"]),textStyle:new Set(["color"]),highlight:new Set(["color"]),image:new Set(["attachmentId","alt","width","align"]),fileAttachment:new Set(["attachmentId","filename","mimeType","size"]),videoEmbed:new Set(["provider","videoId","page","startSeconds"]),mindMap:new Set(["id","rootId","formatVersion","layout","theme","height","branchStyle"]),mindMapTopic:new Set(["id","parentId","order","shape","color","background","fontSize","align","icon","done","attachmentId","side"]),mindMapRelation:new Set(["id","source","target","kind","label"]),callout:new Set(["tone","icon","textColor","borderColor","backgroundColor"]),codeBlock:new Set(["language"]),link:new Set(["href","target","rel"]),pageLink:new Set(["pageId","label"]),mention:new Set(["userId","label"]),tableCell:new Set(["colspan","rowspan","align","colwidth","backgroundColor"]),tableHeader:new Set(["colspan","rowspan","align","colwidth","backgroundColor"]),taskItem:new Set(["checked"]),mathInline:new Set(["latex"]),mathBlock:new Set(["latex"])},h=new Set(["colspan","rowspan"]),m=new Set(l.flatMap(t=>t.value?[t.value]:[]));function isTableCellType(t){return"tableCell"===t||"tableHeader"===t}function coercePositiveInt(t){const e="number"==typeof t?t:Number.parseInt(String(t??""),10);if(!Number.isFinite(e))return;const n=Math.floor(e);return n>=1?n:void 0}function coerceColwidth(t){if(null===t)return null;let e;if(Array.isArray(t))e=t;else{if("string"!=typeof t)return;{const n=t.trim();if(!n)return null;if(n.startsWith("["))try{const t=JSON.parse(n);e=Array.isArray(t)?t:[]}catch{return}else e=n.split(",")}}if(0===e.length)return null;const n=e.map(t=>{const e="number"==typeof t?t:Number.parseInt(String(t??""),10);return Number.isFinite(e)&&e>0?Math.floor(e):0});return n.some(t=>t>0)?n:null}export class ContentSchemaService{validate(i,l){if(!Number.isSafeInteger(l)||l<1)throw t.validation("内容版本必须是正整数");if(c(i)){if(10!==l)throw t.validation("工作表需要当前编辑器版本");try{return{doc:d(i),schemaVersion:l,linkTargetIds:[],attachmentIds:[],mentionUserIds:[]}}catch(e){throw t.validation(e instanceof Error?e.message:"工作表格式无效")}}if("object"!=typeof i||null===i||"doc"!==i.type)throw t.validation("正文格式非法:根节点必须是 doc");if(l>SCHEMA_VERSION)throw new t(e,`正文版本 ${l} 高于服务端支持的 ${SCHEMA_VERSION},请升级后再编辑`,422);const f=new Set,g=new Set,u=new Set;let w=0;const walk=(i,l,c="")=>{if("object"!=typeof i||null===i)throw t.validation("正文包含非法节点");if(l>100)throw t.validation("正文嵌套层级过深");const d=i,y=d.type;if(("mindMapTopic"===y||"mindMapRelation"===y)&&"mindMap"!==c)throw t.validation("导图主题只能放在思维导图内");if(["mindMapDescription","mindMapNote"].includes(String(y))&&"mindMapTopic"!==c)throw t.validation("导图描述只能放在主题内");if("mindMap"===y)try{a(d)}catch(e){throw t.validation(e instanceof Error?e.message:"思维导图结构无效")}if("callout"===y&&!s(d.attrs??{}))throw t.validation("高亮块属性无效");if("string"==typeof y){if(!r.has(y))throw new t(e,`正文包含服务端不支持的节点「${y}」,已拒绝保存以避免内容丢失`,422,{nodeType:y,schemaVersion:SCHEMA_VERSION});if(w+=1,w>2e4)throw t.validation("正文节点数超出上限")}const S={};if("string"==typeof y&&(S.type=y),d.attrs&&"object"==typeof d.attrs){const t="string"==typeof y?p[y]:void 0,e={};for(const[n,r]of Object.entries(d.attrs))if(t&&t.has(n))if(isTableCellType(y)&&"backgroundColor"===n)"string"==typeof r&&m.has(r)&&(e[n]=r);else if("href"!==n||"string"!=typeof r||this.isSafeHref(r)){if(isTableCellType(y)&&h.has(n)){const t=coercePositiveInt(r);if(void 0===t)continue;e[n]=t;continue}if(isTableCellType(y)&&"colwidth"===n){const t=coerceColwidth(r);if(void 0===t)continue;e[n]=t;continue}e[n]=r}Object.keys(e).length>0&&(S.attrs=e)}if("videoEmbed"===y){const e=o(d.attrs);if(!e)throw t.validation("视频属性无效,请重新插入视频");S.attrs={...e}}if("string"==typeof d.text&&(S.text=d.text),Array.isArray(d.marks)){const t=d.marks.filter(t=>"string"==typeof t.type&&n.has(t.type)).map(t=>{const e={type:t.type};if(t.attrs&&"object"==typeof t.attrs){const n=p[t.type],r={};for(const[e,o]of Object.entries(t.attrs))n?.has(e)&&("href"!==e||"string"==typeof o&&this.isSafeHref(o))&&(r[e]=o);Object.keys(r).length>0&&(e.attrs=r)}return e});t.length>0&&(S.marks=t)}if("pageLink"===y&&d.attrs){const t=d.attrs.pageId;"string"==typeof t&&t.length>0&&f.add(t)}if(("image"===y||"mindMapTopic"===y)&&d.attrs){const t=d.attrs.attachmentId;"string"==typeof t&&t.length>0&&g.add(t)}if("fileAttachment"===y&&d.attrs){const t=d.attrs.attachmentId;"string"==typeof t&&t.length>0&&g.add(t)}if(("mathInline"===y||"mathBlock"===y)&&d.attrs){const e=d.attrs.latex;if("string"==typeof e&&e.length>5e3)throw t.validation("公式内容过长(上限 5000 字符)")}if("mention"===y&&d.attrs){const t=d.attrs.userId;"string"==typeof t&&t.length>0&&u.add(t)}return Array.isArray(d.content)&&(S.content=d.content.filter(t=>null!=t).map(t=>walk(t,l+1,"string"==typeof y?y:""))),S};return{doc:walk(i,0),schemaVersion:SCHEMA_VERSION,linkTargetIds:[...f],attachmentIds:[...g],mentionUserIds:[...u]}}isSafeHref(t){const e=t.trim().toLowerCase();return!(!e.startsWith("/")&&!e.startsWith("#"))||e.startsWith("http://")||e.startsWith("https://")||e.startsWith("mailto:")}extractPlainText(t){if(c(t))return f(t);const e=[],walk=t=>{if("object"!=typeof t||null===t)return;const n=t;"string"==typeof n.text&&e.push(n.text),Array.isArray(n.content)&&n.content.forEach(walk)};return walk(t),e.join(" ").replace(/\s+/g," ").trim()}}
@@ -1,23 +1,66 @@
1
1
  export declare const CELL_BACKGROUNDS: readonly [{
2
- readonly name: "默认";
2
+ readonly name: "无";
3
3
  readonly value: null;
4
+ readonly token: null;
4
5
  }, {
5
- readonly name: "灰";
6
+ readonly name: "浅灰";
6
7
  readonly value: "#f1f3f5";
8
+ readonly token: "gray-soft";
7
9
  }, {
8
- readonly name: "黄";
10
+ readonly name: "浅红";
11
+ readonly value: "#ffe3e3";
12
+ readonly token: "red";
13
+ }, {
14
+ readonly name: "浅橙";
15
+ readonly value: "#fff0db";
16
+ readonly token: "orange";
17
+ }, {
18
+ readonly name: "浅黄";
9
19
  readonly value: "#fff3bf";
20
+ readonly token: "yellow";
10
21
  }, {
11
- readonly name: "绿";
22
+ readonly name: "浅绿";
12
23
  readonly value: "#d3f9d8";
24
+ readonly token: "green";
13
25
  }, {
14
- readonly name: "蓝";
26
+ readonly name: "浅蓝";
15
27
  readonly value: "#d0ebff";
28
+ readonly token: "blue";
16
29
  }, {
17
- readonly name: "紫";
30
+ readonly name: "浅紫";
18
31
  readonly value: "#e5dbff";
32
+ readonly token: "purple";
33
+ }, {
34
+ readonly name: "灰";
35
+ readonly value: "#dee2e6";
36
+ readonly token: "gray";
37
+ }, {
38
+ readonly name: "深灰";
39
+ readonly value: "#ced4da";
40
+ readonly token: "gray-strong";
19
41
  }, {
20
42
  readonly name: "红";
21
- readonly value: "#ffe3e3";
43
+ readonly value: "#ffc9c9";
44
+ readonly token: "red-strong";
45
+ }, {
46
+ readonly name: "橙";
47
+ readonly value: "#ffd8a8";
48
+ readonly token: "orange-strong";
49
+ }, {
50
+ readonly name: "黄";
51
+ readonly value: "#ffec99";
52
+ readonly token: "yellow-strong";
53
+ }, {
54
+ readonly name: "绿";
55
+ readonly value: "#b2f2bb";
56
+ readonly token: "green-strong";
57
+ }, {
58
+ readonly name: "蓝";
59
+ readonly value: "#a5d8ff";
60
+ readonly token: "blue-strong";
61
+ }, {
62
+ readonly name: "紫";
63
+ readonly value: "#d0bfff";
64
+ readonly token: "purple-strong";
22
65
  }];
23
66
  export declare function cellBackground(value: unknown): string | null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wendang",
3
- "version": "0.2.4",
3
+ "version": "0.2.5",
4
4
  "description": "文档、工作表和思维导图的内容协议与校验核心。",
5
5
  "type": "module",
6
6
  "license": "MIT",