@agile-team/mach-report 2.5.0 → 2.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +91 -14
- package/dist/ReportPreview-Dk-28QAi.js +2946 -0
- package/dist/ReportPreview-FSvcBLjH.cjs +6 -0
- package/dist/_plugin-vue_export-helper-BHFhmbuH.cjs +1 -0
- package/dist/_plugin-vue_export-helper-CHgC5LLL.js +9 -0
- package/dist/analytics.cjs +1 -0
- package/dist/analytics.d.cts +276 -0
- package/dist/analytics.d.ts +276 -0
- package/dist/analytics.js +223 -0
- package/dist/assets-66UBlhES.cjs +1 -0
- package/dist/assets-NVA_sftu.js +163 -0
- package/dist/capabilities-CNjIirfo.js +36 -0
- package/dist/capabilities-D4xeVYSR.cjs +1 -0
- package/dist/designer.cjs +1 -0
- package/dist/designer.d.cts +753 -0
- package/dist/designer.d.ts +753 -0
- package/dist/designer.js +372 -0
- package/dist/grid-geometry-BNptEcMm.js +65 -0
- package/dist/grid-geometry-CZ_cYB6O.cjs +1 -0
- package/dist/index.cjs +8 -5
- package/dist/index.d.cts +413 -8
- package/dist/index.d.ts +413 -8
- package/dist/index.js +1016 -1501
- package/dist/param-validation-BWNjLcN0.js +116 -0
- package/dist/param-validation-CiTxFeZ8.cjs +1 -0
- package/dist/pdf.cjs +48 -48
- package/dist/pdf.d.cts +111 -1
- package/dist/pdf.d.ts +111 -1
- package/dist/pdf.js +3931 -3868
- package/dist/{plugin-DMPLDpBY.cjs → plugin-BqBkn_kI.cjs} +1 -1
- package/dist/{plugin-DDLcFWlk.js → plugin-Ds10VnQO.js} +2 -2
- package/dist/render-plan-CckPEQyf.cjs +1 -0
- package/dist/{render-plan-CXBWRog4.js → render-plan-uzZYYSy1.js} +11 -10
- package/dist/report-params-B_VCihAh.js +12 -0
- package/dist/report-params-CjWrjPl6.cjs +1 -0
- package/dist/report-source-B9bENO0F.js +750 -0
- package/dist/report-source-BbNkkBDy.cjs +1 -0
- package/dist/server/node.d.cts +297 -0
- package/dist/server/node.d.ts +297 -0
- package/dist/server-node.cjs +1 -0
- package/dist/server-node.js +270 -0
- package/dist/server.cjs +1 -0
- package/dist/server.d.cts +643 -0
- package/dist/server.d.ts +643 -0
- package/dist/server.js +237 -0
- package/dist/style-CpwiOPyB.cjs +1 -0
- package/dist/style-Dv5o_x6b.js +67 -0
- package/dist/style.css +1 -1
- package/dist/template-registry-DWExx0gU.cjs +1 -0
- package/dist/template-registry-NpOakidD.js +354 -0
- package/dist/textwrap-CnGsEum6.cjs +4 -0
- package/dist/textwrap-DOl04hCQ.js +110 -0
- package/dist/validate-DfHPD8wX.cjs +1 -0
- package/dist/validate-S6CgIm5d.js +148 -0
- package/dist/vue/async.d.cts +602 -57
- package/dist/vue/async.d.ts +602 -57
- package/dist/vue/index.d.cts +1074 -111
- package/dist/vue/index.d.ts +1074 -111
- package/dist/vue-async.cjs +1 -1
- package/dist/vue-async.js +3 -3
- package/dist/vue.cjs +1 -1
- package/dist/vue.js +167 -82
- package/dist/worker.cjs +1 -0
- package/dist/worker.d.cts +302 -0
- package/dist/worker.d.ts +302 -0
- package/dist/worker.js +133 -0
- package/dist/xlsx/node.d.cts +47 -0
- package/dist/xlsx/node.d.ts +47 -0
- package/dist/xlsx-node.cjs +221 -0
- package/dist/xlsx-node.js +36409 -0
- package/dist/xlsx.cjs +20 -20
- package/dist/xlsx.d.cts +78 -1
- package/dist/xlsx.d.ts +78 -1
- package/dist/xlsx.js +5974 -5831
- package/package.json +62 -2
- package/dist/ReportPreview-CBKLI--k.js +0 -2188
- package/dist/ReportPreview-ClM8h0pM.cjs +0 -6
- package/dist/grid-geometry-BNIvw52m.cjs +0 -1
- package/dist/grid-geometry-C8hGoe9o.js +0 -130
- package/dist/render-plan-CCTL4F9v.cjs +0 -1
- package/dist/report-params-B3Fia6JO.js +0 -10
- package/dist/report-params-D1gZ1DME.cjs +0 -1
- package/dist/textwrap-nH0UYU6D.js +0 -108
- package/dist/textwrap-oD-t4b4B.cjs +0 -4
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";const k=require("./render-plan-CckPEQyf.cjs"),F=require("./textwrap-CnGsEum6.cjs"),v=require("./style-CpwiOPyB.cjs"),oe=require("./validate-DfHPD8wX.cjs"),ae=require("./report-params-CjWrjPl6.cjs"),B=require("./template-registry-DWExx0gU.cjs"),j=require("./param-validation-CiTxFeZ8.cjs");function K(e){if(!e||e.kind==="date")return;const t=e.digits;if(!(t!==void 0&&(!Number.isInteger(t)||t<0||t>100))){if(e.kind==="percent"){const n=t??2;return`0${n?`.${"0".repeat(n)}`:""}%`}return t===void 0?e.thousands?"#,##0.###############":void 0:`${e.thousands?"#,##0":"0"}${t?`.${"0".repeat(t)}`:""}`}}function G(e){const t=/^(\d{4})[-/](\d{1,2})[-/](\d{1,2})$/.exec(e.trim());if(t){const r=new Date(Number(t[1]),Number(t[2])-1,Number(t[3]));return Number.isNaN(r.getTime())?null:r}const n=new Date(e);return Number.isNaN(n.getTime())?null:n}function C(e,t){return e.toLocaleString("en-US",{minimumFractionDigits:t,maximumFractionDigits:t})}function ie(e,t){const n=r=>String(r).padStart(2,"0");return t.replace(/YYYY/g,String(e.getFullYear())).replace(/MM/g,n(e.getMonth()+1)).replace(/DD/g,n(e.getDate())).replace(/hh/g,n(e.getHours())).replace(/mm/g,n(e.getMinutes())).replace(/ss/g,n(e.getSeconds()))}function se(e,t,n){return n!=null?t?C(e,n):e.toFixed(n):t?C(e):String(e)}function N(e){const t=typeof e=="number"?e:Number(String(e).replace(/,/g,""));return Number.isFinite(t)?t:null}function _(e,t){if(e==null||e===""||!t)return String(e??"");switch(t.kind){case"number":{const n=N(e);return n==null?String(e):se(n,t.thousands===!0,t.digits)}case"percent":{const n=N(e);return n==null?String(e):`${C(n*100,t.digits??2)}%`}case"date":{const n=t.pattern??"YYYY-MM-DD",r=e instanceof Date?e:G(String(e));return r?ie(r,n):String(e)}default:return String(e)}}function Y(e,t){const n=e[t.field];if(n==null||n==="")return!1;if(typeof t.value=="number"){const a=N(n);if(a==null)return!1;switch(t.op){case">":return a>t.value;case">=":return a>=t.value;case"<":return a<t.value;case"<=":return a<=t.value;case"==":return a===t.value;case"!=":return a!==t.value}}const r=String(n),o=String(t.value);switch(t.op){case"==":return r===o;case"!=":return r!==o;case">":return r>o;case">=":return r>=o;case"<":return r<o;case"<=":return r<=o}return!1}const me=["text","number","boolean","date","decimal"],ce=new Set(["__proto__","prototype","constructor"]);function x(e){throw new Error(`数据契约无效:${e}`)}function V(e){(!e||!Array.isArray(e.fields)||!e.fields.length||e.fields.length>2048)&&x("fields 应包含 1–2048 个字段");const t=new Set;for(const n of e.fields)(!n||typeof n.field!="string"||!n.field.trim()||ce.has(n.field)||t.has(n.field))&&x("字段名应安全、非空且唯一"),t.add(n.field),me.includes(n.type)||x(`未知字段类型 ${n.type}`),ge(n),de(n),ue(n);le(e,t)}function ue(e){e.enum!==void 0&&(!Array.isArray(e.enum)||!e.enum.length||e.enum.some(t=>!J(t,e.type)))&&x("enum 应为匹配字段类型的非空标量数组")}function le(e,t){e.rowKey!==void 0&&((typeof e.rowKey!="string"||!e.rowKey.trim()||!t.has(e.rowKey))&&x("rowKey 应为引用已声明字段的非空字符串"),e.fields.find(n=>n.field===e.rowKey)?.type==="boolean"&&x("布尔字段不能作为业务主键"))}function de(e){for(const t of["label","unit"])e[t]!==void 0&&typeof e[t]!="string"&&x(`${t} 应为字符串`);for(const t of["required","nullable"])e[t]!==void 0&&typeof e[t]!="boolean"&&x(`${t} 应为布尔值`);e.semanticRole!==void 0&&!["identifier","dimension","measure","date","text"].includes(e.semanticRole)&&x("未知字段语义")}function ge(e){for(const t of["min","max","minLength","maxLength"]){const n=e[t];n!==void 0&&(!Number.isFinite(n)||t.includes("Length")&&(!Number.isInteger(n)||n<0))&&x(`${t} 无效`)}e.min!==void 0&&e.max!==void 0&&e.min>e.max&&x("min 不能超过 max"),e.minLength!==void 0&&e.maxLength!==void 0&&e.minLength>e.maxLength&&x("minLength 不能超过 maxLength"),e.type!=="number"&&(e.min!==void 0||e.max!==void 0)&&x("min/max 仅适用于 number,精确小数文本需业务校验"),["number","boolean"].includes(e.type)&&(e.minLength!==void 0||e.maxLength!==void 0)&&x("长度约束仅适用于文本类型")}function fe(e){const t=/^(\d{4})-(\d{2})-(\d{2})$/.exec(e);if(!t)return!1;const n=Number(t[1]),r=Number(t[2]),o=Number(t[3]),a=n%4===0&&(n%100!==0||n%400===0);return n>0&&r>=1&&r<=12&&o>=1&&o<=[31,a?29:28,31,30,31,30,31,31,30,31,30,31][r-1]}function J(e,t){return t==="number"?typeof e=="number"&&Number.isFinite(e):t==="boolean"?typeof e=="boolean":typeof e!="string"?!1:t==="date"?fe(e):t==="decimal"?e.length<=1e4&&/^-?(?:0|[1-9]\d*)(?:\.\d+)?$/.test(e):!0}function he(e,t){if(e==null)return t.required||e===null&&t.nullable===!1?{code:"required",message:"缺少必需字段值"}:void 0;if(!J(e,t.type))return{code:"type",message:`值应为 ${t.type},不会自动转换原始值`};if(t.enum&&!t.enum.includes(e))return{code:"enum",message:"值不在允许的枚举中"};if(typeof e=="number"&&(t.min!==void 0&&e<t.min||t.max!==void 0&&e>t.max))return{code:"range",message:"数值超出范围"};if(typeof e=="string"&&!pe(e,t))return{code:"range",message:"文本长度超出范围"}}function pe(e,t){if(t.minLength===void 0&&t.maxLength===void 0)return!0;let n=0;for(const r of e)n++;return(t.minLength===void 0||n>=t.minLength)&&(t.maxLength===void 0||n<=t.maxLength)}function H(e,t){const n=e??t;if(!Number.isInteger(n)||n<1)throw new Error("数据校验预算应为正整数");return n}function ye(e,t,n){const r=Object.prototype.hasOwnProperty.call(e,t)?e[t]:void 0;if((typeof r!="string"||!r.trim())&&!(typeof r=="number"&&Number.isFinite(r)))return"业务主键应为非空字符串或有限数字";const o=JSON.stringify([typeof r,r]);if(n.has(o))return"业务主键重复";n.add(o)}function U(e,t,n={}){V(t);const r=n.path??"$.rows",o=[],a=H(n.maxIssues,100);if(!Array.isArray(e))return{ok:!1,issues:[{code:"row",path:r,message:"数据集应为对象数组"}]};if(e.length>H(n.maxRows,1e5)||e.length*t.fields.length>H(n.maxCells,2e6))return{ok:!1,issues:[{code:"budget",path:r,message:"数据校验超出行数或单元格预算"}]};const i=new Set;for(let s=0;s<e.length&&o.length<a;s++){const l=e[s];if(!l||typeof l!="object"||Array.isArray(l)){o.push({code:"row",path:`${r}[${s}]`,rowIndex:s,message:"行应为对象"});continue}we(l,t,s,r,i,o,a)}return{ok:o.length===0,issues:o}}function we(e,t,n,r,o,a,i){for(const s of t.fields){if(a.length>=i)return;const l=he(Object.prototype.hasOwnProperty.call(e,s.field)?e[s.field]:void 0,s);l&&a.push({...l,rowIndex:n,field:s.field,path:`${r}[${n}].${s.field}`})}if(t.rowKey&&a.length<i){const s=ye(e,t.rowKey,o);s&&a.push({code:"row-key",path:`${r}[${n}].${t.rowKey}`,message:s,rowIndex:n,field:t.rowKey})}}function be(e){return V(e),{$schema:"https://json-schema.org/draft/2020-12/schema",type:"array",items:{type:"object",properties:Object.fromEntries(e.fields.map(n=>[n.field,Me(n,n.field===e.rowKey)])),required:e.fields.filter(n=>n.required||n.field===e.rowKey).map(n=>n.field),additionalProperties:!0},...e.rowKey?{"x-report-rowKey":e.rowKey}:{}}}function Me(e,t){const n=!t&&e.nullable!==!1&&!e.required;return{type:n?[q(e.type),"null"]:q(e.type),...e.label?{title:e.label}:{},...e.type==="date"?{format:"date"}:{},...e.type==="decimal"?{pattern:"^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?$"}:{},...e.enum?{enum:n?[...e.enum,null]:[...e.enum]}:{},...e.min===void 0?{}:{minimum:e.min},...e.max===void 0?{}:{maximum:e.max},...Se(e,t),...t&&e.type==="text"?{pattern:"\\S"}:{},...e.unit?{"x-report-unit":e.unit}:{},...e.semanticRole?{"x-report-semanticRole":e.semanticRole}:{}}}function Se(e,t){const n={};return(e.minLength!==void 0||t&&e.type!=="number")&&(n.minLength=Math.max(e.minLength??0,t?1:0)),e.type==="decimal"?n.maxLength=Math.min(e.maxLength??1e4,1e4):e.maxLength!==void 0&&(n.maxLength=e.maxLength),n}function q(e){return e==="text"||e==="date"||e==="decimal"?"string":e}class L extends Error{constructor(t,n){super(`报表页数超出上限 ${t}(至少 ${n} 页)`),this.limit=t,this.actual=n,this.code="page-limit",this.name="PaginatePageLimitError"}}function xe(e){return{widthMm:e.widthMm,heightMm:e.heightMm,marginTopMm:e.marginTopMm??0,marginBottomMm:e.marginBottomMm??0,marginLeftMm:e.marginLeftMm??0,marginRightMm:e.marginRightMm??0,components:[]}}function ve(e,t){return t.heightMm-(e.layout==="wide"?t.marginBottomMm:e.marginBottomMm??t.marginBottomMm)}function Q(e){if(e==null)return"";if(typeof e=="string")return e;if(typeof e=="number")return String(e);if(typeof e=="boolean")return e?"是":"否";if(e instanceof Date)return e.toISOString().slice(0,10);try{return JSON.stringify(e)}catch{return String(e)}}function X(e,t){let n=e.textMetrics.get(t);if(!n){const r=v.resolveFontSize(t).fontSizePt;n={measureOptions:{fontSizePt:r,fontFamily:t.fontFamily??k.DEFAULT_FONT_FAMILY,italic:t.italic,bold:t.bold,letterSpacingPt:t.letterSpacing},lineMm:r*(t.lineHeight??k.LINE_HEIGHT)*25.4/72},e.textMetrics.set(t,n)}return n}function Z(e,t,n){return n==="identifier"||n==="dimension"||n==="date"?"center":n==="measure"?"right":n==="text"?"left":t?.kind==="number"||t?.kind==="percent"?"right":t?.kind==="date"||e instanceof Date||typeof e=="boolean"?"center":typeof e=="number"&&Number.isFinite(e)?"right":"left"}function Pe(e){if(e===null||typeof e=="string"||typeof e=="boolean"||typeof e=="number"&&Number.isFinite(e))return e}function $e(e,t){const n=e.fontSizePt??k.DEFAULT_FONT_PT,r=e.paddingMm??1,o=e.columns.map(m=>m.align??m.style?.align??e.align??"left"),a=e.columns.map((m,c)=>({...v.mergeComponentStyles({fontSize:n,verticalAlign:"middle"},m.style),align:o[c]==="auto"?"left":o[c]})),i=a.map((m,c)=>o[c]==="auto"?{left:m,center:{...m,align:"center"},right:{...m,align:"right"}}:void 0),s=e.columns.map((m,c)=>m.rules?.map(M=>{const h=i[c];if(h)return{left:v.mergeComponentStyles(h.left,M.style),center:v.mergeComponentStyles(h.center,M.style),right:v.mergeComponentStyles(h.right,M.style)};const S=v.mergeComponentStyles(a[c],M.style);return{left:S,center:S,right:S}})),l=e.columns.map(m=>{let c=v.mergeComponentStyles({fontSize:n,bold:e.headerBold??!0,align:"center",verticalAlign:"middle",backgroundColor:e.headerBackgroundColor},m.style);return e.headerAlign!==void 0&&(c.align=e.headerAlign),c=v.mergeComponentStyles(c,e.headerStyle),m.headerAlign!==void 0&&(c.align=m.headerAlign),v.mergeComponentStyles(c,m.headerStyle)}),d=e.columns.map(m=>m.header);let u=e.rowHeightMm??0;for(let m=0;m<d.length;m++){const c=l[m],M=v.resolveFontSize(c).fontSizePt,h=F.wrapText(d[m],e.columns[m].widthMm-r*2,{fontSizePt:M,fontFamily:c.fontFamily??k.DEFAULT_FONT_FAMILY,italic:c.italic,bold:c.bold,letterSpacingPt:c.letterSpacing},t),S=M*(c.lineHeight??k.LINE_HEIGHT)*25.4/72;u=Math.max(u,Math.max(h.length,1)*S+r*2)}const g=d.map((m,c)=>({text:m,style:l[c]})),b=e.border===!1?{borderTop:!1,borderRight:!1,borderBottom:!1,borderLeft:!1}:{borderBottom:!0,borderLeft:!0,borderRight:!0,borderTop:!0},y=e.columns.map(m=>m.widthMm),f=ke(e.headerGrid,y,{cells:[g],colWidthsMm:y,rowHeightsMm:[u],paddingMm:r});return{fontSizePt:n,paddingMm:r,cellStyles:a,autoStyles:i,ruleStyles:s,textMetrics:new WeakMap,numberFormats:e.columns.map(m=>K(m.format)),borderStyle:b,headerHeightMm:f.rowHeightsMm.reduce((m,c)=>m+c,0),headerGrid:f,colWidthsMm:y,columns:e.columns}}function ke(e,t,n){if(!e)return n;if(!e.cells.length)throw new Error("自定义列表表头至少需要一行");const r=e.rowHeightsMm??e.cells.map(()=>n.rowHeightsMm[0]);if(r.length!==e.cells.length||r.some(o=>!Number.isFinite(o)||o<=0))throw new Error("自定义列表表头行高无效");return{...e,colWidthsMm:t,rowHeightsMm:r}}function Re(e,t,n){const r=t.format?.kind==="number"||t.format?.kind==="percent";t.valueType!==void 0?e.valueType=t.valueType:r&&(e.valueType="number"),t.editable!==void 0&&(e.editable=t.editable),(typeof e.value=="number"||e.value==null)&&n!==void 0&&(e.numberFormat=n)}function W(e,t,n){const r=e.columns;let o=e.rowHeightFloorMm;const a=new Array(r.length);for(let i=0;i<r.length;i++){const s=r[i],l=t[s.field],d=e.autoStyles[i]?Z(l,s.format,s.semanticRole):"left";let u=e.autoStyles[i]?.[d]??e.cellStyles[i];if(s.rules)for(let c=0;c<s.rules.length;c++){const M=s.rules[c];if(Y(t,M.when)){u=e.ruleStyles[i][c][d];break}}const g=s.format?_(l,s.format):Q(l),b=X(e,u),y=F.wrapText(g,s.widthMm-e.paddingMm*2,b.measureOptions,n);o=Math.max(o,y.length*b.lineMm+e.paddingMm*2);const f=Pe(l),m={text:g,style:u};f!==void 0&&(m.value=f),Re(m,s,e.numberFormats[i]),a[i]=m}return{grid:{cells:[a],colWidthsMm:e.colWidthsMm,rowHeightsMm:[o],paddingMm:e.paddingMm},heightMm:o}}function*Te(e,t){if(!t){for(const r of e)yield{kind:"row",row:r};return}const n=new Map;for(const r of e){const o=Q(r[t.field]),a=n.get(o);a?a.push(r):n.set(o,[r])}for(const[r,o]of n){yield{kind:"groupHeader",label:(t.headerTemplate??"{value}").replace(/\{value\}/g,r),firstRow:o[0]};for(const a of o)yield{kind:"row",row:a};t.subtotal?.length&&(yield{kind:"subtotal",label:t.subtotalLabel??"小计",rows:o})}}function Le(e,t){let n=0;for(const r of e){const o=typeof r[t]=="number"?r[t]:Number(r[t]);Number.isFinite(o)&&(n+=o)}return n}function ee(e,t,n){return new Map(n.map(r=>{const o=e.find(i=>i.field===r)?.format??{kind:"number",thousands:!0},a=Le(t,r);return[r,{text:_(a,o),value:a,numberFormat:K(o)}]}))}function te(e,t){const n=e.findIndex(r=>t.includes(r.field));return n>0?n:1}function I(e,t,n,r,o,a){const i=[],s=Math.max(1,Math.min(a,e.columns.length)),l=v.mergeComponentStyles({fontSize:e.fontSizePt},o);i.push({text:n,colSpan:s,style:{bold:!0,...l}});for(let g=s;g<e.columns.length;g++){const b=e.columns[g],y=r.get(b.field),f=e.autoStyles[g]?.[Z(y?.value,b.format)]??e.cellStyles[g];i.push({...y,text:y?.text??"",style:v.mergeComponentStyles(f,o)})}let d=e.rowHeightFloorMm,u=0;for(const g of i){let b=0;const y=u+(g.colSpan??1);for(;u<y;u++)b+=e.colWidthsMm[u]??0;const f=X(e,g.style),m=F.wrapText(g.text??"",b-e.paddingMm*2,f.measureOptions,t);d=Math.max(d,m.length*f.lineMm+e.paddingMm*2)}return{grid:{cells:[i],colWidthsMm:e.colWidthsMm,rowHeightsMm:[d],paddingMm:e.paddingMm},heightMm:d}}function Ee(e,t,n){const{templatePage:r,current:o,warnings:a,nextId:i,pushCurrent:s}=n,l=()=>ve(r,o),d=o.marginTopMm,u={id:n.tableId,columnKeys:e.columns.map(p=>p.field),...e.layout===void 0?{}:{layout:e.layout},...e.title===void 0?{}:{title:e.title}};let g=0;const b={kind:"rect",leftMm:e.leftMm,topMm:0,widthMm:e.widthMm,heightMm:t.headerHeightMm,grid:t.headerGrid,nid:i("listh"),table:{...u,role:"header",rowIndex:-1},style:t.borderStyle},y=p=>o.components.push({...b,topMm:p});let f=e.topMm;y(f),f+=t.headerHeightMm+n.mmPerRow;function m(){const p=r.layout==="wide"?0:e.topMm;return e.headerEveryPage!==!1?p+t.headerHeightMm:r.layout==="wide"?0:d}function c(){const p=r.layout==="wide"?0:o.marginBottomMm;return r.heightMm-p-m()}function M(p){if(f+p<=l())return;s(),o.components=[],o.heightMm=r.heightMm,r.layout==="wide"&&(o.marginTopMm=0,o.marginBottomMm=0);const $=r.layout==="wide"?0:e.topMm;e.headerEveryPage!==!1?(y($),f=$+t.headerHeightMm):f=r.layout==="wide"?0:d}function h(p,$,R,E){const T=c();p.heightMm>T&&a.push(`列表 ${e.id??e.dataset} 行高 ${p.heightMm.toFixed(2)}mm 超出单页可用高度 ${T.toFixed(2)}mm,预览与打印可能截断内容`),M(p.heightMm),o.components.push({kind:"rect",leftMm:e.leftMm,topMm:f,widthMm:e.widthMm,heightMm:p.heightMm,grid:p.grid,nid:i($),table:{...u,role:R,rowIndex:g++,...E?{source:E}:{}},style:t.borderStyle}),f+=p.heightMm}function S(p){p<=c()&&M(p)}return{keepTogether:S,placeGridRow:h}}function Ne(e,t){if(!e.rowKey)return;const n=t[e.rowKey];if((typeof n!="string"||!n.trim())&&!(typeof n=="number"&&Number.isFinite(n)))throw new Error(`数据集 ${e.dataset} 的 ${e.rowKey} 应为稳定的字符串或有限数字主键`);const r=e.revisionField?t[e.revisionField]:void 0,o=e.sourceTokenField?t[e.sourceTokenField]:void 0;if(r!==void 0&&typeof r!="string"&&!(typeof r=="number"&&Number.isFinite(r)))throw new Error("业务修订号应为字符串或有限数字");if(o!==void 0&&typeof o!="string")throw new Error("来源令牌应为字符串");return{datasetId:e.dataset,rowKey:n,...r===void 0?{}:{revision:r},...o===void 0?{}:{sourceToken:o}}}function Fe(e,t){const n=new Map;if(!e.rowKey)return n;const r=new Set;for(const o of t){const a=Ne(e,o),i=JSON.stringify(a.rowKey);if(r.has(i))throw new Error(`数据集 ${e.dataset} 的业务主键重复:${String(a.rowKey)}`);r.add(i),n.set(o,a)}return n}function Ae(e,t,n,r,o){if(!e.grandTotal)return;const a=typeof e.grandTotal=="object"?e.grandTotal:void 0,i=a?.fields??e.groupBy?.subtotal??[];if(i.length===0){r.warnings.push(`列表 ${e.id??""} 配置了 grandTotal 但未提供合计字段(groupBy.subtotal 或 grandTotal.fields)`);return}const s=I(n,r.measurer,a?.label??"合计",ee(e.columns,t,i),a?.style??{},te(e.columns,i));o.placeGridRow(s,"listgt","grand-total")}function De(e,t,n){const r=Fe(e,t),o=e.columns.reduce((u,g)=>u+g.widthMm,0),a=o>e.widthMm?e.widthMm/o:1,i=a===1?e:{...e,columns:e.columns.map(u=>({...u,widthMm:u.widthMm*a}))},s={...$e(i,n.measurer),rowHeightFloorMm:i.rowHeightMm??0},l=Ee(i,s,n);let d;for(const u of Te(t,i.groupBy))if(u.kind==="row")l.placeGridRow(d??W(s,u.row,n.measurer),"listr","body",r.get(u.row)),d=void 0;else if(u.kind==="groupHeader"){const g=I(s,n.measurer,u.label,new Map,i.groupBy?.headerStyle??{},i.columns.length);d=W(s,u.firstRow,n.measurer),i.groupBy?.keepWithNext!==!1&&l.keepTogether(g.heightMm+d.heightMm),l.placeGridRow(g,"listg","group-header")}else{const g=i.groupBy?.subtotal??[],b=I(s,n.measurer,u.label,ee(i.columns,u.rows,g),i.groupBy?.subtotalStyle??{},te(i.columns,g));l.placeGridRow(b,"listst","subtotal")}Ae(i,t,s,n,l)}function z(e){return e.kind==="text"&&typeof e.text=="string"&&(e.text.includes("{page}")||e.text.includes("{totalPages}"))}function He(e,t,n,r,o,a){const i={pageWidthMm:t.widthMm,pageHeightMm:t.heightMm,...e.layout===void 0?{}:{layout:e.layout},marginTopMm:t.marginTopMm,marginBottomMm:t.marginBottomMm,marginLeftMm:t.marginLeftMm,marginRightMm:t.marginRightMm,components:t.components};r.push(i);for(const s of n){const l={...s,text:String(s.text??"").replace(/\{page\}/g,String(r.length))};i.components.push(l),l.text.includes("{totalPages}")&&o.push(l)}if(e.layout==="wide"){const s=i.components.reduce((l,d)=>Math.max(l,d.topMm+d.heightMm),0);i.marginBottomMm=a?e.marginBottomMm??0:0,i.pageHeightMm=Math.max(1,s+i.marginBottomMm)}}function ne(e,t,n={}){const r=typeof n=="number"?{mmPerRow:n}:n;if(r.maxPages!==void 0&&(!Number.isSafeInteger(r.maxPages)||r.maxPages<1))throw new RangeError("maxPages 应为正安全整数");const o=[],a=[],i=[];let s=0;const l=d=>`${d}_${(++s).toString(36)}`;for(const[d,u]of e.pages.entries()){const g=xe(u),b=u.components.filter(z),y=(f=!1)=>{if(r.maxPages!==void 0&&a.length>=r.maxPages)throw new L(r.maxPages,a.length+1);He(u,g,b,a,i,f)};for(const[f,m]of u.components.entries()){if(z(m))continue;if(m.kind!=="list"){g.components.push(m);continue}const c=t[m.dataset];Array.isArray(c)||o.push(`数据集 ${m.dataset} 未提供数据,列表 ${m.id??""} 输出为空`),De(m,Array.isArray(c)?c:[],{templatePage:u,current:g,warnings:o,nextId:l,pushCurrent:y,measurer:r.measurer??F.heuristicMeasurer,mmPerRow:r.mmPerRow??0,tableId:`table:${d}:${f}`})}y(!0)}for(const d of i)d.text=d.text.replace(/\{totalPages\}/g,String(a.length));return{plan:{schemaVersion:k.RENDER_PLAN_SCHEMA_VERSION,pages:a},warnings:o}}function Ce(e){if(!("mapData"in e))return e;const{load:t,mapData:n,...r}=e;return{...r,load:async o=>{const a=await t(o);P(o.signal);try{return n(a,o)}catch(i){throw new w("validate",`报表 ${o.reportId} 数据映射失败`,i)}}}}class w extends Error{constructor(t,n,r){super(n),this.name="ReportSourceError",this.code=t,this.cause=r}}function A(e){const t=new Error("报表加载已取消");return t.name="AbortError",Object.defineProperty(t,"cause",{value:e.reason}),t}function P(e){if(e?.aborted)throw A(e)}function O(e,t){return t?new Promise((n,r)=>{const o=()=>t.removeEventListener("abort",a),a=()=>{o(),r(A(t))};t.addEventListener("abort",a,{once:!0}),t.aborted&&a(),Promise.resolve(e).then(i=>{o(),n(i)},i=>{o(),r(i)})}):Promise.resolve(e)}function Ie(e,t){if(!e||typeof e!="object"||Array.isArray(e))throw new w("validate",`报表 ${t} 的数据集应为对象`);for(const[n,r]of Object.entries(e))if(!Array.isArray(r)||r.some(o=>!o||typeof o!="object"||Array.isArray(o)))throw new w("validate",`报表 ${t} 的数据集 ${n} 应为对象数组`)}async function Oe(e,t,n){const{reportId:r,signal:o}=t;try{const a=await O(e(t),o);P(o);const i=B.validateReportTemplate(a);if(!i.ok)throw new w("validate",`报表 ${r} 模板校验失败:${i.errors.slice(0,3).map(s=>`${s.path}: ${s.message}`).join("; ")}`);return n.push(...i.warnings.map(s=>`${r}: ${s.path}: ${s.message}`)),a}catch(a){throw o?.aborted?A(o):a instanceof w?a:a instanceof B.ReportTemplateError?new w("validate",`报表 ${r} 模板校验失败:${a.message}`,a):new w("fetch",`报表 ${r} 模板加载失败:${a instanceof Error?a.message:String(a)}`,a)}}async function Ke(e,t,n,r,o){const a=t.signal;let i;try{if(e.load)i=await O(e.load(t),a);else if(e.provider){const s=await O(e.provider.query(t),a);P(a),_e(s,n),i=s.datasets,r?.(n,s)}else i=e.datasets??{}}catch(s){throw a?.aborted?A(a):s instanceof w?s:new w("fetch",`报表 ${n} 取数失败:${s instanceof Error?s.message:String(s)}`,s)}return P(a),Ie(i,n),o?.(n,i),Be(e,i,n),i}function _e(e,t){if(!e||typeof e!="object"||Array.isArray(e))throw new w("validate",`报表 ${t} Provider 应返回 datasets 结果对象`);for(const n of["revision","sourceToken"])if(e[n]!==void 0&&typeof e[n]!="string")throw new w("validate",`报表 ${t} Provider ${n} 应为字符串`);if(e.diagnostics!==void 0&&(!Array.isArray(e.diagnostics)||e.diagnostics.some(n=>!n||typeof n.code!="string"||typeof n.message!="string"||!["info","warning","error"].includes(n.severity))))throw new w("validate",`报表 ${t} Provider diagnostics 不符合结构化诊断契约`)}function Be(e,t,n){for(const[r,o]of Object.entries(e.dataSchema??{})){let a;try{a=U(Object.prototype.hasOwnProperty.call(t,r)?t[r]:void 0,o,{...e.dataValidation,path:`$.datasets.${r}`})}catch(i){throw new w("config",`报表 ${n} 数据集 ${r} 字段契约配置无效`,i)}if(!a.ok)throw new w("validate",`报表 ${n} 数据集 ${r} 不符合字段契约:${a.issues[0]?.message}`,{issues:a.issues})}}function je(e,t,n,r,o){const a=o?o.maxPages-o.usedPages:void 0;try{if(a!==void 0&&a<1&&e.pages.length)throw new L(0,1);const i=a===void 0?n.pagination:{...n.pagination,maxPages:Math.max(1,Math.min(a,n.pagination?.maxPages??a))};return ne(e,t,i)}catch(i){const s=i instanceof L&&o&&i.limit===a?new L(o.maxPages,o.usedPages+i.actual):i;throw new w("validate",`报表 ${r} 排版失败:${s instanceof Error?s.message:String(s)}`,s)}}function qe(e,t,n,r){const o=new Map,a=i=>{let s=o.get(i);return s===void 0&&(s=JSON.stringify([n,r,i]),o.set(i,s)),s};for(const i of t.pages)e.push({...i,components:i.components.map(s=>s.table?{...s,table:{...s.table,id:a(s.table.id)}}:s)})}function We(e,t={}){if(t.maxPages!==void 0&&(!Number.isSafeInteger(t.maxPages)||t.maxPages<1))throw new w("config","maxPages 应为正安全整数");function n(o){const a=Object.prototype.hasOwnProperty.call(e,o)?e[o]:void 0;if(!a)throw new w("config",`未注册报表 ${o}`);return a}return Object.assign(async({reportIds:o,params:a,signal:i,onWarning:s,onQueryResult:l})=>{P(i);const d=[...o],u={...a};if(d.length===0)throw new w("config","缺少报表 ID");const g=d.map(n),b=g.map(h=>j.assertReportParams(h.params??(typeof h.template=="function"?[]:h.template.params)??[],u)),y=[],f=[];for(const[h,S]of g.entries()){P(i);const p=d[h],$={reportId:p,params:b[h],signal:i},R=typeof S.template=="function"?await Oe(S.template,$,f):S.template;$.params=j.assertReportParams(S.params??R.params??[],$.params);const E=await Ke(S,$,p,l,t.onDatasets?(D,re)=>t.onDatasets(D,re,R):void 0);P(i);const T=je(R,E,S,p,t.maxPages===void 0?void 0:{maxPages:t.maxPages,usedPages:y.length});qe(y,T.plan,h,p),f.push(...T.warnings.map(D=>`${p}: ${D}`))}P(i);const m={schemaVersion:k.RENDER_PLAN_SCHEMA_VERSION,pages:y},c=oe.validateRenderPlan(m);if(!c.ok)throw new w("validate",`渲染计划校验失败:${c.errors.slice(0,3).map(h=>`${h.path}: ${h.message}`).join("; ")}`);f.push(...c.warnings.map(h=>`${h.path}: ${h.message}`));const M=s??t.onWarning??(h=>console.warn("[mach-report]",h));for(const h of f)P(i),M(h);return P(i),m},{getParamDefs:o=>ae.getReportParamDefs(e,o)})}exports.PaginatePageLimitError=L;exports.ReportSourceError=w;exports.createReportSource=We;exports.datasetJsonSchema=be;exports.defineReport=Ce;exports.evalStyleRule=Y;exports.formatValue=_;exports.paginateTemplate=ne;exports.parseDateLocal=G;exports.toExcelNumberFormat=K;exports.toNumber=N;exports.validateDatasetRows=U;
|
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
|
|
3
|
+
import { WorkerOptions as WorkerOptions_2 } from 'node:worker_threads';
|
|
4
|
+
|
|
5
|
+
declare type ComponentKind = "text" | "image" | "barcode" | "qrcode" | "chart" | "line" | "rect" | "ellipse";
|
|
6
|
+
|
|
7
|
+
declare interface ComponentStyle {
|
|
8
|
+
fontSizePx?: number;
|
|
9
|
+
fontSize?: number;
|
|
10
|
+
fontFamily?: string;
|
|
11
|
+
bold?: boolean;
|
|
12
|
+
italic?: boolean;
|
|
13
|
+
underline?: boolean;
|
|
14
|
+
color?: string;
|
|
15
|
+
backgroundColor?: string;
|
|
16
|
+
align?: "left" | "center" | "right" | "justify";
|
|
17
|
+
verticalAlign?: "top" | "middle" | "bottom";
|
|
18
|
+
lineColor?: string;
|
|
19
|
+
borderColor?: string;
|
|
20
|
+
lineWidthPt?: number;
|
|
21
|
+
borderTop?: boolean;
|
|
22
|
+
borderRight?: boolean;
|
|
23
|
+
borderBottom?: boolean;
|
|
24
|
+
borderLeft?: boolean;
|
|
25
|
+
lineHeight?: number;
|
|
26
|
+
letterSpacing?: number;
|
|
27
|
+
opacity?: number;
|
|
28
|
+
rotateDeg?: number;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** 独立 worker_threads 适配器;worker 入口注册自己的模板/Provider,不把函数跨线程序列化。 */
|
|
32
|
+
export declare function createNodeReportExecutor(options: NodeReportExecutorOptions): ReportRuntimeExecutor;
|
|
33
|
+
|
|
34
|
+
declare interface GridCell {
|
|
35
|
+
text?: string;
|
|
36
|
+
rawText?: string;
|
|
37
|
+
/** 原始值,仅供 Excel raw 模式消费;不参与预览、分页与 PDF 绘制。 */
|
|
38
|
+
value?: string | number | boolean | null;
|
|
39
|
+
/** Excel 数字格式(例如 #,##0.00 / 0.00%),不改变 text。 */
|
|
40
|
+
numberFormat?: string;
|
|
41
|
+
/** 编辑输入类型;省略时只按原始值推断,数字字符串不会被转换为数值。 */
|
|
42
|
+
valueType?: "text" | "number" | "boolean";
|
|
43
|
+
/** 单元格级编辑控制;预览仍需显式开启 editing。 */
|
|
44
|
+
editable?: boolean;
|
|
45
|
+
source?: ReportCellSource;
|
|
46
|
+
/** Excel 公式保留元数据;当前引擎不计算公式,公式格始终只读。 */
|
|
47
|
+
formula?: string;
|
|
48
|
+
children?: PlanComponent[];
|
|
49
|
+
colSpan?: number;
|
|
50
|
+
rowSpan?: number;
|
|
51
|
+
style?: ComponentStyle;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export declare interface NodeReportExecutorOptions extends ReportWorkerOptions {
|
|
55
|
+
filename?: string | URL;
|
|
56
|
+
workerUrl?: string | URL;
|
|
57
|
+
workerOptions?: WorkerOptions_2;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
declare interface PlanComponent {
|
|
61
|
+
kind: ComponentKind;
|
|
62
|
+
leftMm: number;
|
|
63
|
+
topMm: number;
|
|
64
|
+
widthMm: number;
|
|
65
|
+
heightMm: number;
|
|
66
|
+
nid?: string;
|
|
67
|
+
rkey?: string;
|
|
68
|
+
text?: string;
|
|
69
|
+
rawText?: string;
|
|
70
|
+
lines?: string[];
|
|
71
|
+
richParagraphs?: RichParagraph[];
|
|
72
|
+
imageData?: string;
|
|
73
|
+
/** 明确指定线段方向;省略保持历史水平线行为。 */
|
|
74
|
+
lineDirection?: "down" | "up";
|
|
75
|
+
grid?: PlanGrid;
|
|
76
|
+
table?: PlanTableFragment;
|
|
77
|
+
style?: ComponentStyle;
|
|
78
|
+
meta?: Record<string, unknown>;
|
|
79
|
+
/** MachReport 扩展字段:未知 kind 的兜底渲染标记 */
|
|
80
|
+
fallbackText?: string;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
declare interface PlanGrid {
|
|
84
|
+
/** 行内允许 null 空位;渲染器按空单元格处理 */
|
|
85
|
+
cells: (GridCell | null)[][];
|
|
86
|
+
colWidthsMm?: number[];
|
|
87
|
+
rowHeightsMm?: number[];
|
|
88
|
+
gapMm?: number;
|
|
89
|
+
/** 单元格四边内距;旧静态网格默认 0.5mm,分页列表使用其真实测量内距。 */
|
|
90
|
+
paddingMm?: number;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
declare interface PlanPage {
|
|
94
|
+
/** wide 表示完整自然列宽;页面仅为预览的垂直窗口分段。 */
|
|
95
|
+
layout?: "page" | "wide";
|
|
96
|
+
pageWidthMm: number;
|
|
97
|
+
pageHeightMm: number;
|
|
98
|
+
marginTopMm?: number;
|
|
99
|
+
marginBottomMm?: number;
|
|
100
|
+
marginLeftMm?: number;
|
|
101
|
+
marginRightMm?: number;
|
|
102
|
+
components: PlanComponent[];
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/** 列表的逻辑行来源;用于连续 Excel 重组,避免按文本猜测重复表头。 */
|
|
106
|
+
declare interface PlanTableFragment {
|
|
107
|
+
/** 在整个计划中唯一的逻辑表身份;同一表的分页片段共享身份。 */
|
|
108
|
+
id: string;
|
|
109
|
+
/** 表头固定 -1;其余行(包括组头/小计/总计)从 0 连续递增。 */
|
|
110
|
+
rowIndex: number;
|
|
111
|
+
role: "header" | "body" | "group-header" | "subtotal" | "grand-total";
|
|
112
|
+
/** 按物理列顺序保存字段身份;不以表头显示文本判断列结构。 */
|
|
113
|
+
columnKeys: string[];
|
|
114
|
+
layout?: "page" | "wide";
|
|
115
|
+
title?: string;
|
|
116
|
+
/** 只有宿主声明稳定 rowKey 时生成,不能用显示顺序替代业务主键。 */
|
|
117
|
+
source?: Omit<ReportCellSource, "field" | "rowKey"> & {
|
|
118
|
+
rowKey?: string | number;
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
declare interface PreparedReport {
|
|
123
|
+
plan: RenderPlan;
|
|
124
|
+
diagnostics: ReportDiagnostic[];
|
|
125
|
+
stats: ReportRuntimeStats;
|
|
126
|
+
revisions: Record<string, {
|
|
127
|
+
revision?: string;
|
|
128
|
+
sourceToken?: string;
|
|
129
|
+
}>;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
declare interface RenderPlan {
|
|
133
|
+
schemaVersion: string;
|
|
134
|
+
pages: PlanPage[];
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/** 可持久化的业务身份与计划坐标独立;权限及并发校验仍由业务服务器执行。 */
|
|
138
|
+
declare interface ReportCellSource {
|
|
139
|
+
datasetId: string;
|
|
140
|
+
rowKey: string | number;
|
|
141
|
+
field: string;
|
|
142
|
+
revision?: string | number;
|
|
143
|
+
sourceToken?: string;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
declare interface ReportDescription {
|
|
147
|
+
id: string;
|
|
148
|
+
params: ReportParamDef[];
|
|
149
|
+
paramsSchema: Record<string, unknown>;
|
|
150
|
+
datasetsSchema: Record<string, Record<string, unknown>>;
|
|
151
|
+
/** Provider 的可选远程能力;runtime 自身始终提供 PDF/XLSX 导出。 */
|
|
152
|
+
capabilities: {
|
|
153
|
+
save: boolean;
|
|
154
|
+
export: boolean;
|
|
155
|
+
templates: boolean;
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
declare interface ReportDiagnostic {
|
|
160
|
+
code: string;
|
|
161
|
+
message: string;
|
|
162
|
+
path?: string;
|
|
163
|
+
severity: "warning" | "error" | "info";
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/** 报表参数定义(声明式,模板自描述参数面:渲染组件据此生成查询面板) */
|
|
167
|
+
declare interface ReportParamDef {
|
|
168
|
+
/** 参数名(传入 params,与数据集 SQL #{field} 绑定对应) */
|
|
169
|
+
field: string;
|
|
170
|
+
/** 显示名(缺省用 field) */
|
|
171
|
+
label?: string;
|
|
172
|
+
type?: "text" | "number" | "date" | "select";
|
|
173
|
+
/** select 选项 */
|
|
174
|
+
options?: Array<{
|
|
175
|
+
label: string;
|
|
176
|
+
value: string;
|
|
177
|
+
}>;
|
|
178
|
+
defaultValue?: string;
|
|
179
|
+
required?: boolean;
|
|
180
|
+
placeholder?: string;
|
|
181
|
+
/** 数字上下界;date 参数使用 YYYY-MM-DD 字符串(含边界)。 */
|
|
182
|
+
min?: number | string;
|
|
183
|
+
max?: number | string;
|
|
184
|
+
minLength?: number;
|
|
185
|
+
maxLength?: number;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
declare interface ReportParamIssue {
|
|
189
|
+
code: ReportParamIssueCode;
|
|
190
|
+
path: string;
|
|
191
|
+
field: string;
|
|
192
|
+
message: string;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
declare type ReportParamIssueCode = "definition" | "required" | "type" | "option" | "minimum" | "maximum" | "minLength" | "maxLength";
|
|
196
|
+
|
|
197
|
+
declare interface ReportParamValidation {
|
|
198
|
+
ok: boolean;
|
|
199
|
+
/** 保留历史取数契约的字符串形式;未知参数保留,不能代替接口权限白名单。 */
|
|
200
|
+
values: Record<string, string>;
|
|
201
|
+
/** JSON Schema 对应的规范值:number 参数转换为有限数字。 */
|
|
202
|
+
typedValues: Record<string, string | number>;
|
|
203
|
+
issues: ReportParamIssue[];
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
declare interface ReportRuntime {
|
|
207
|
+
describe(reportIds?: string[]): ReportDescription[];
|
|
208
|
+
validate(input: Omit<ReportRuntimeInput, "signal">): ReportParamValidation;
|
|
209
|
+
prepare(input: ReportRuntimeInput): Promise<PreparedReport>;
|
|
210
|
+
export(input: ReportRuntimeExportInput): Promise<ReportRuntimeExportResult>;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/** 可用 worker_threads/进程/RPC 实现;接收可序列化请求,signal 由桥接层映射为取消消息。 */
|
|
214
|
+
declare interface ReportRuntimeExecutor {
|
|
215
|
+
prepare(input: Omit<ReportRuntimeInput, "signal">, signal?: AbortSignal): Promise<PreparedReport>;
|
|
216
|
+
export(input: Omit<ReportRuntimeExportInput, "signal">, signal?: AbortSignal): Promise<ReportRuntimeExportResult>;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
declare interface ReportRuntimeExportInput extends ReportRuntimeInput {
|
|
220
|
+
format: "pdf" | "xlsx";
|
|
221
|
+
/** PDF 中文需宿主显式提供有授权的字体;不隐式下载字体或资源。 */
|
|
222
|
+
pdf?: {
|
|
223
|
+
customFontBytes?: Uint8Array | ArrayBuffer;
|
|
224
|
+
customFontName?: string;
|
|
225
|
+
customFontFaces?: {
|
|
226
|
+
regular: Uint8Array | ArrayBuffer;
|
|
227
|
+
bold?: Uint8Array | ArrayBuffer;
|
|
228
|
+
italic?: Uint8Array | ArrayBuffer;
|
|
229
|
+
boldItalic?: Uint8Array | ArrayBuffer;
|
|
230
|
+
};
|
|
231
|
+
};
|
|
232
|
+
xlsx?: {
|
|
233
|
+
sheetMode?: "auto" | "pages" | "continuous";
|
|
234
|
+
valueMode?: "text" | "raw";
|
|
235
|
+
paperSize?: "auto" | "a3" | "a4" | "a5";
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
declare interface ReportRuntimeExportResult extends Omit<PreparedReport, "plan"> {
|
|
240
|
+
bytes: Uint8Array;
|
|
241
|
+
format: "pdf" | "xlsx";
|
|
242
|
+
contentType: string;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
declare interface ReportRuntimeInput {
|
|
246
|
+
reportIds: string[];
|
|
247
|
+
params?: Record<string, string>;
|
|
248
|
+
signal?: AbortSignal;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
declare interface ReportRuntimeStats {
|
|
252
|
+
rows: number;
|
|
253
|
+
pages: number;
|
|
254
|
+
cells: number;
|
|
255
|
+
durationMs: number;
|
|
256
|
+
outputBytes?: number;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
export declare class ReportWorkerError extends Error {
|
|
260
|
+
readonly code?: string;
|
|
261
|
+
readonly path?: string;
|
|
262
|
+
readonly issues?: unknown;
|
|
263
|
+
constructor(error: SerializedReportError);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
declare interface ReportWorkerOptions {
|
|
267
|
+
timeoutMs?: number;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
declare interface RichParagraph {
|
|
271
|
+
segments: RichSegment[];
|
|
272
|
+
align?: ComponentStyle["align"];
|
|
273
|
+
lineHeight?: number;
|
|
274
|
+
spaceBeforeMm?: number;
|
|
275
|
+
spaceAfterMm?: number;
|
|
276
|
+
indentMm?: number;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
declare interface RichSegment {
|
|
280
|
+
kind?: "text" | "field";
|
|
281
|
+
text?: string;
|
|
282
|
+
field?: string;
|
|
283
|
+
style?: ComponentStyle;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
declare interface SerializedReportError {
|
|
287
|
+
name: string;
|
|
288
|
+
message: string;
|
|
289
|
+
code?: string;
|
|
290
|
+
path?: string;
|
|
291
|
+
issues?: unknown;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/** Node Worker 入口调用;主线程调用会明确报错。 */
|
|
295
|
+
export declare function serveNodeReportWorker(runtime: ReportRuntime): () => void;
|
|
296
|
+
|
|
297
|
+
export { }
|
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
|
|
3
|
+
import { WorkerOptions as WorkerOptions_2 } from 'node:worker_threads';
|
|
4
|
+
|
|
5
|
+
declare type ComponentKind = "text" | "image" | "barcode" | "qrcode" | "chart" | "line" | "rect" | "ellipse";
|
|
6
|
+
|
|
7
|
+
declare interface ComponentStyle {
|
|
8
|
+
fontSizePx?: number;
|
|
9
|
+
fontSize?: number;
|
|
10
|
+
fontFamily?: string;
|
|
11
|
+
bold?: boolean;
|
|
12
|
+
italic?: boolean;
|
|
13
|
+
underline?: boolean;
|
|
14
|
+
color?: string;
|
|
15
|
+
backgroundColor?: string;
|
|
16
|
+
align?: "left" | "center" | "right" | "justify";
|
|
17
|
+
verticalAlign?: "top" | "middle" | "bottom";
|
|
18
|
+
lineColor?: string;
|
|
19
|
+
borderColor?: string;
|
|
20
|
+
lineWidthPt?: number;
|
|
21
|
+
borderTop?: boolean;
|
|
22
|
+
borderRight?: boolean;
|
|
23
|
+
borderBottom?: boolean;
|
|
24
|
+
borderLeft?: boolean;
|
|
25
|
+
lineHeight?: number;
|
|
26
|
+
letterSpacing?: number;
|
|
27
|
+
opacity?: number;
|
|
28
|
+
rotateDeg?: number;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** 独立 worker_threads 适配器;worker 入口注册自己的模板/Provider,不把函数跨线程序列化。 */
|
|
32
|
+
export declare function createNodeReportExecutor(options: NodeReportExecutorOptions): ReportRuntimeExecutor;
|
|
33
|
+
|
|
34
|
+
declare interface GridCell {
|
|
35
|
+
text?: string;
|
|
36
|
+
rawText?: string;
|
|
37
|
+
/** 原始值,仅供 Excel raw 模式消费;不参与预览、分页与 PDF 绘制。 */
|
|
38
|
+
value?: string | number | boolean | null;
|
|
39
|
+
/** Excel 数字格式(例如 #,##0.00 / 0.00%),不改变 text。 */
|
|
40
|
+
numberFormat?: string;
|
|
41
|
+
/** 编辑输入类型;省略时只按原始值推断,数字字符串不会被转换为数值。 */
|
|
42
|
+
valueType?: "text" | "number" | "boolean";
|
|
43
|
+
/** 单元格级编辑控制;预览仍需显式开启 editing。 */
|
|
44
|
+
editable?: boolean;
|
|
45
|
+
source?: ReportCellSource;
|
|
46
|
+
/** Excel 公式保留元数据;当前引擎不计算公式,公式格始终只读。 */
|
|
47
|
+
formula?: string;
|
|
48
|
+
children?: PlanComponent[];
|
|
49
|
+
colSpan?: number;
|
|
50
|
+
rowSpan?: number;
|
|
51
|
+
style?: ComponentStyle;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export declare interface NodeReportExecutorOptions extends ReportWorkerOptions {
|
|
55
|
+
filename?: string | URL;
|
|
56
|
+
workerUrl?: string | URL;
|
|
57
|
+
workerOptions?: WorkerOptions_2;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
declare interface PlanComponent {
|
|
61
|
+
kind: ComponentKind;
|
|
62
|
+
leftMm: number;
|
|
63
|
+
topMm: number;
|
|
64
|
+
widthMm: number;
|
|
65
|
+
heightMm: number;
|
|
66
|
+
nid?: string;
|
|
67
|
+
rkey?: string;
|
|
68
|
+
text?: string;
|
|
69
|
+
rawText?: string;
|
|
70
|
+
lines?: string[];
|
|
71
|
+
richParagraphs?: RichParagraph[];
|
|
72
|
+
imageData?: string;
|
|
73
|
+
/** 明确指定线段方向;省略保持历史水平线行为。 */
|
|
74
|
+
lineDirection?: "down" | "up";
|
|
75
|
+
grid?: PlanGrid;
|
|
76
|
+
table?: PlanTableFragment;
|
|
77
|
+
style?: ComponentStyle;
|
|
78
|
+
meta?: Record<string, unknown>;
|
|
79
|
+
/** MachReport 扩展字段:未知 kind 的兜底渲染标记 */
|
|
80
|
+
fallbackText?: string;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
declare interface PlanGrid {
|
|
84
|
+
/** 行内允许 null 空位;渲染器按空单元格处理 */
|
|
85
|
+
cells: (GridCell | null)[][];
|
|
86
|
+
colWidthsMm?: number[];
|
|
87
|
+
rowHeightsMm?: number[];
|
|
88
|
+
gapMm?: number;
|
|
89
|
+
/** 单元格四边内距;旧静态网格默认 0.5mm,分页列表使用其真实测量内距。 */
|
|
90
|
+
paddingMm?: number;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
declare interface PlanPage {
|
|
94
|
+
/** wide 表示完整自然列宽;页面仅为预览的垂直窗口分段。 */
|
|
95
|
+
layout?: "page" | "wide";
|
|
96
|
+
pageWidthMm: number;
|
|
97
|
+
pageHeightMm: number;
|
|
98
|
+
marginTopMm?: number;
|
|
99
|
+
marginBottomMm?: number;
|
|
100
|
+
marginLeftMm?: number;
|
|
101
|
+
marginRightMm?: number;
|
|
102
|
+
components: PlanComponent[];
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/** 列表的逻辑行来源;用于连续 Excel 重组,避免按文本猜测重复表头。 */
|
|
106
|
+
declare interface PlanTableFragment {
|
|
107
|
+
/** 在整个计划中唯一的逻辑表身份;同一表的分页片段共享身份。 */
|
|
108
|
+
id: string;
|
|
109
|
+
/** 表头固定 -1;其余行(包括组头/小计/总计)从 0 连续递增。 */
|
|
110
|
+
rowIndex: number;
|
|
111
|
+
role: "header" | "body" | "group-header" | "subtotal" | "grand-total";
|
|
112
|
+
/** 按物理列顺序保存字段身份;不以表头显示文本判断列结构。 */
|
|
113
|
+
columnKeys: string[];
|
|
114
|
+
layout?: "page" | "wide";
|
|
115
|
+
title?: string;
|
|
116
|
+
/** 只有宿主声明稳定 rowKey 时生成,不能用显示顺序替代业务主键。 */
|
|
117
|
+
source?: Omit<ReportCellSource, "field" | "rowKey"> & {
|
|
118
|
+
rowKey?: string | number;
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
declare interface PreparedReport {
|
|
123
|
+
plan: RenderPlan;
|
|
124
|
+
diagnostics: ReportDiagnostic[];
|
|
125
|
+
stats: ReportRuntimeStats;
|
|
126
|
+
revisions: Record<string, {
|
|
127
|
+
revision?: string;
|
|
128
|
+
sourceToken?: string;
|
|
129
|
+
}>;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
declare interface RenderPlan {
|
|
133
|
+
schemaVersion: string;
|
|
134
|
+
pages: PlanPage[];
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/** 可持久化的业务身份与计划坐标独立;权限及并发校验仍由业务服务器执行。 */
|
|
138
|
+
declare interface ReportCellSource {
|
|
139
|
+
datasetId: string;
|
|
140
|
+
rowKey: string | number;
|
|
141
|
+
field: string;
|
|
142
|
+
revision?: string | number;
|
|
143
|
+
sourceToken?: string;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
declare interface ReportDescription {
|
|
147
|
+
id: string;
|
|
148
|
+
params: ReportParamDef[];
|
|
149
|
+
paramsSchema: Record<string, unknown>;
|
|
150
|
+
datasetsSchema: Record<string, Record<string, unknown>>;
|
|
151
|
+
/** Provider 的可选远程能力;runtime 自身始终提供 PDF/XLSX 导出。 */
|
|
152
|
+
capabilities: {
|
|
153
|
+
save: boolean;
|
|
154
|
+
export: boolean;
|
|
155
|
+
templates: boolean;
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
declare interface ReportDiagnostic {
|
|
160
|
+
code: string;
|
|
161
|
+
message: string;
|
|
162
|
+
path?: string;
|
|
163
|
+
severity: "warning" | "error" | "info";
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/** 报表参数定义(声明式,模板自描述参数面:渲染组件据此生成查询面板) */
|
|
167
|
+
declare interface ReportParamDef {
|
|
168
|
+
/** 参数名(传入 params,与数据集 SQL #{field} 绑定对应) */
|
|
169
|
+
field: string;
|
|
170
|
+
/** 显示名(缺省用 field) */
|
|
171
|
+
label?: string;
|
|
172
|
+
type?: "text" | "number" | "date" | "select";
|
|
173
|
+
/** select 选项 */
|
|
174
|
+
options?: Array<{
|
|
175
|
+
label: string;
|
|
176
|
+
value: string;
|
|
177
|
+
}>;
|
|
178
|
+
defaultValue?: string;
|
|
179
|
+
required?: boolean;
|
|
180
|
+
placeholder?: string;
|
|
181
|
+
/** 数字上下界;date 参数使用 YYYY-MM-DD 字符串(含边界)。 */
|
|
182
|
+
min?: number | string;
|
|
183
|
+
max?: number | string;
|
|
184
|
+
minLength?: number;
|
|
185
|
+
maxLength?: number;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
declare interface ReportParamIssue {
|
|
189
|
+
code: ReportParamIssueCode;
|
|
190
|
+
path: string;
|
|
191
|
+
field: string;
|
|
192
|
+
message: string;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
declare type ReportParamIssueCode = "definition" | "required" | "type" | "option" | "minimum" | "maximum" | "minLength" | "maxLength";
|
|
196
|
+
|
|
197
|
+
declare interface ReportParamValidation {
|
|
198
|
+
ok: boolean;
|
|
199
|
+
/** 保留历史取数契约的字符串形式;未知参数保留,不能代替接口权限白名单。 */
|
|
200
|
+
values: Record<string, string>;
|
|
201
|
+
/** JSON Schema 对应的规范值:number 参数转换为有限数字。 */
|
|
202
|
+
typedValues: Record<string, string | number>;
|
|
203
|
+
issues: ReportParamIssue[];
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
declare interface ReportRuntime {
|
|
207
|
+
describe(reportIds?: string[]): ReportDescription[];
|
|
208
|
+
validate(input: Omit<ReportRuntimeInput, "signal">): ReportParamValidation;
|
|
209
|
+
prepare(input: ReportRuntimeInput): Promise<PreparedReport>;
|
|
210
|
+
export(input: ReportRuntimeExportInput): Promise<ReportRuntimeExportResult>;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/** 可用 worker_threads/进程/RPC 实现;接收可序列化请求,signal 由桥接层映射为取消消息。 */
|
|
214
|
+
declare interface ReportRuntimeExecutor {
|
|
215
|
+
prepare(input: Omit<ReportRuntimeInput, "signal">, signal?: AbortSignal): Promise<PreparedReport>;
|
|
216
|
+
export(input: Omit<ReportRuntimeExportInput, "signal">, signal?: AbortSignal): Promise<ReportRuntimeExportResult>;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
declare interface ReportRuntimeExportInput extends ReportRuntimeInput {
|
|
220
|
+
format: "pdf" | "xlsx";
|
|
221
|
+
/** PDF 中文需宿主显式提供有授权的字体;不隐式下载字体或资源。 */
|
|
222
|
+
pdf?: {
|
|
223
|
+
customFontBytes?: Uint8Array | ArrayBuffer;
|
|
224
|
+
customFontName?: string;
|
|
225
|
+
customFontFaces?: {
|
|
226
|
+
regular: Uint8Array | ArrayBuffer;
|
|
227
|
+
bold?: Uint8Array | ArrayBuffer;
|
|
228
|
+
italic?: Uint8Array | ArrayBuffer;
|
|
229
|
+
boldItalic?: Uint8Array | ArrayBuffer;
|
|
230
|
+
};
|
|
231
|
+
};
|
|
232
|
+
xlsx?: {
|
|
233
|
+
sheetMode?: "auto" | "pages" | "continuous";
|
|
234
|
+
valueMode?: "text" | "raw";
|
|
235
|
+
paperSize?: "auto" | "a3" | "a4" | "a5";
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
declare interface ReportRuntimeExportResult extends Omit<PreparedReport, "plan"> {
|
|
240
|
+
bytes: Uint8Array;
|
|
241
|
+
format: "pdf" | "xlsx";
|
|
242
|
+
contentType: string;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
declare interface ReportRuntimeInput {
|
|
246
|
+
reportIds: string[];
|
|
247
|
+
params?: Record<string, string>;
|
|
248
|
+
signal?: AbortSignal;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
declare interface ReportRuntimeStats {
|
|
252
|
+
rows: number;
|
|
253
|
+
pages: number;
|
|
254
|
+
cells: number;
|
|
255
|
+
durationMs: number;
|
|
256
|
+
outputBytes?: number;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
export declare class ReportWorkerError extends Error {
|
|
260
|
+
readonly code?: string;
|
|
261
|
+
readonly path?: string;
|
|
262
|
+
readonly issues?: unknown;
|
|
263
|
+
constructor(error: SerializedReportError);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
declare interface ReportWorkerOptions {
|
|
267
|
+
timeoutMs?: number;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
declare interface RichParagraph {
|
|
271
|
+
segments: RichSegment[];
|
|
272
|
+
align?: ComponentStyle["align"];
|
|
273
|
+
lineHeight?: number;
|
|
274
|
+
spaceBeforeMm?: number;
|
|
275
|
+
spaceAfterMm?: number;
|
|
276
|
+
indentMm?: number;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
declare interface RichSegment {
|
|
280
|
+
kind?: "text" | "field";
|
|
281
|
+
text?: string;
|
|
282
|
+
field?: string;
|
|
283
|
+
style?: ComponentStyle;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
declare interface SerializedReportError {
|
|
287
|
+
name: string;
|
|
288
|
+
message: string;
|
|
289
|
+
code?: string;
|
|
290
|
+
path?: string;
|
|
291
|
+
issues?: unknown;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/** Node Worker 入口调用;主线程调用会明确报错。 */
|
|
295
|
+
export declare function serveNodeReportWorker(runtime: ReportRuntime): () => void;
|
|
296
|
+
|
|
297
|
+
export { }
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const E=require("node:worker_threads");function N(e,t,r){e.editable!==void 0&&typeof e.editable!="boolean"&&r.push({path:`${t}.editable`,message:"编辑控制应为布尔值"}),e.valueType!==void 0&&!["text","number","boolean"].includes(e.valueType)&&r.push({path:`${t}.valueType`,message:"编辑类型应为 text、number 或 boolean"})}function W(e,t,r){N(e,t,r),M(e.source,`${t}.source`,r,!0),e.formula!==void 0&&typeof e.formula!="string"&&r.push({path:`${t}.formula`,message:"单元格公式应为字符串"})}function M(e,t,r,o=!1){if(e===void 0)return;if(!e||typeof e!="object"||Array.isArray(e)){r.push({path:t,message:"业务来源应为对象"});return}const s=e;for(const n of["datasetId",...o?["field"]:[]])(typeof s[n]!="string"||!s[n].trim())&&r.push({path:`${t}.${n}`,message:"业务标识应为非空字符串"});for(const n of["rowKey","revision"]){const i=s[n];i===void 0&&!(o&&n==="rowKey")||typeof i!="string"&&!(typeof i=="number"&&Number.isFinite(i))&&r.push({path:`${t}.${n}`,message:"业务键及修订号应为字符串或有限数字"})}s.sourceToken!==void 0&&typeof s.sourceToken!="string"&&r.push({path:`${t}.sourceToken`,message:"来源令牌应为字符串"})}const T=new Set(["text","image","barcode","qrcode","chart","line","rect","ellipse"]);function p(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function O(e,t,r,o){const s=e[t];(typeof s!="number"||!Number.isFinite(s))&&o.push({path:`${r}.${t}`,message:`应为有限数字,实际 ${JSON.stringify(s)}`})}function P(e,t,r){if(e!==void 0){const o=`${t}.table`;if(!p(e))r.push({path:o,message:"逻辑表来源应为对象"});else{M(e.source,`${o}.source`,r),(typeof e.id!="string"||!e.id.trim())&&r.push({path:`${o}.id`,message:"逻辑表 ID 应为非空字符串"});const s=e.role;["header","body","group-header","subtotal","grand-total"].includes(s)||r.push({path:`${o}.role`,message:"未知逻辑表行角色"});const n=e.rowIndex;(typeof n!="number"||!Number.isInteger(n)||(s==="header"?n!==-1:n<0))&&r.push({path:`${o}.rowIndex`,message:"表头索引应为 -1,内容行索引应为非负整数"}),(!Array.isArray(e.columnKeys)||e.columnKeys.some(i=>typeof i!="string"))&&r.push({path:`${o}.columnKeys`,message:"逻辑列字段应为字符串数组"}),e.layout!==void 0&&e.layout!=="page"&&e.layout!=="wide"&&r.push({path:`${o}.layout`,message:"布局应为 page 或 wide"}),e.title!==void 0&&typeof e.title!="string"&&r.push({path:`${o}.title`,message:"逻辑表名称应为字符串"})}}}function S(e,t,r,o){if(e!==void 0)if(!p(e)||!Array.isArray(e.cells))r.push({path:`${t}.grid.cells`,message:"grid.cells 应为二维数组"});else{const s=e.paddingMm;s!==void 0&&(typeof s!="number"||!Number.isFinite(s)||s<0)&&r.push({path:`${t}.grid.paddingMm`,message:"单元格内距应为有限非负数"}),e.cells.forEach((n,i)=>{if(!Array.isArray(n)){r.push({path:`${t}.grid.cells.${i}`,message:"行应为数组"});return}n.forEach((u,c)=>{if(u==null)return;const m=`${t}.grid.cells.${i}.${c}`;if(!p(u)){r.push({path:m,message:"网格单元格应为对象或 null"});return}W(u,m,r),Array.isArray(u.children)&&u.children.forEach((l,g)=>{R(l,`${t}.grid.cells.${i}.${c}.children.${g}`,r,o)})})})}}function j(e,t,r){e!==void 0&&(Array.isArray(e)?e.forEach((o,s)=>{(!p(o)||!Array.isArray(o.segments))&&r.push({path:`${t}.richParagraphs.${s}`,message:"段落缺少 segments 数组"})}):r.push({path:`${t}.richParagraphs`,message:"richParagraphs 应为数组"}))}function R(e,t,r,o=[]){if(!p(e)){r.push({path:t,message:"组件应为对象"});return}const s=e.kind;typeof s!="string"?r.push({path:`${t}.kind`,message:"组件缺少 kind 字符串"}):T.has(s)||o.push({path:`${t}.kind`,message:`未知组件类型 ${JSON.stringify(s)},将以兜底方式渲染`});for(const n of["leftMm","topMm","widthMm","heightMm"])O(e,n,t,r);typeof e.leftMm=="number"&&e.leftMm<-100&&r.push({path:`${t}.leftMm`,message:"几何数值异常(leftMm<-100)"});for(const n of["widthMm","heightMm"])typeof e[n]=="number"&&e[n]<0&&r.push({path:`${t}.${n}`,message:"组件尺寸不能为负数"});P(e.table,t,r),e.lineDirection!==void 0&&!["down","up"].includes(e.lineDirection)&&r.push({path:`${t}.lineDirection`,message:"线段方向应为 down 或 up"}),S(e.grid,t,r,o),j(e.richParagraphs,t,r)}function I(e,t,r,o=[]){if(!p(e)){r.push({path:t,message:"页应为对象"});return}const s=e.pageWidthMm,n=e.pageHeightMm;(typeof s!="number"||!Number.isFinite(s)||s<=0)&&r.push({path:`${t}.pageWidthMm`,message:`页宽非法: ${JSON.stringify(s)}`}),(typeof n!="number"||!(n>0&&n<5e3))&&r.push({path:`${t}.pageHeightMm`,message:`页高非法: ${JSON.stringify(n)}`}),e.layout!==void 0&&e.layout!=="page"&&e.layout!=="wide"&&r.push({path:`${t}.layout`,message:"布局应为 page 或 wide"});const i=e.components;Array.isArray(i)?Array.isArray(i)&&i.forEach((u,c)=>R(u,`${t}.components.${c}`,r,o)):r.push({path:`${t}.components`,message:"components 应为数组"})}function K(e){const t=[],r=[],o=p(e)?e.pages:void 0;if(!Array.isArray(o))t.push({path:"$.pages",message:e==null?"渲染计划为空":"渲染计划缺少 pages 数组"});else{o.forEach((n,i)=>I(n,`$.pages.${i}`,t,r)),o.length>2e3&&r.push({path:"$.pages",message:`页数异常大(${o.length} 页),渲染与打印耗时可能显著增加`});const s=o.reduce((n,i)=>n+(p(i)&&Array.isArray(i.components)?i.components.length:0),0);s>5e4&&r.push({path:"$.pages",message:`组件总数异常大(${s} 个),可能触发浏览器性能限制`})}return{ok:t.length===0,errors:t,warnings:r}}const h="mach-report-worker/1";class k extends Error{constructor(t){super(t.message),this.name=t.name,this.code=t.code,this.path=t.path,this.issues=t.issues}}function y(e){return!!e&&typeof e=="object"&&!Array.isArray(e)}function D(e){return y(e)?["rows","pages","cells"].every(t=>typeof e[t]=="number"&&Number.isSafeInteger(e[t])&&e[t]>=0)&&typeof e.durationMs=="number"&&Number.isFinite(e.durationMs)&&e.durationMs>=0:!1}function F(e){return Array.isArray(e)&&e.every(t=>y(t)&&typeof t.code=="string"&&typeof t.message=="string"&&["info","warning","error"].includes(t.severity))}function _(e){return y(e)&&Object.values(e).every(t=>y(t)&&(t.revision===void 0||typeof t.revision=="string")&&(t.sourceToken===void 0||typeof t.sourceToken=="string"))}function C(e,t){if(!y(e)||!D(e.stats)||!F(e.diagnostics)||!_(e.revisions))return!1;if(t==="prepare")return K(e.plan).ok;const r=e.format==="pdf"?"application/pdf":"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";return e.bytes instanceof Uint8Array&&(e.format==="pdf"||e.format==="xlsx")&&e.contentType===r}function J(e){if(!e||typeof e!="object")return{message:String(e)};const t={};for(const r of["code","path","field","message"]){const o=e[r];typeof o=="string"&&(t[r]=o)}return t}function L(e){if(!(e instanceof Error))return{name:"Error",message:String(e)};const t=e;return{name:e.name,message:e.message,...typeof t.code=="string"?{code:t.code}:{},...typeof t.path=="string"?{path:t.path}:{},...Array.isArray(t.issues)?{issues:t.issues.map(J)}:{}}}function U(e){if(!e||typeof e!="object")return!1;const t=e;return t.protocol===h&&typeof t.id=="string"&&(t.operation==="prepare"||t.operation==="export")&&!!t.input&&typeof t.input=="object"}async function q(e,t){try{const r=t.operation==="prepare"?await e.prepare(t.input):await e.export(t.input);return{protocol:h,id:t.id,result:r}}catch(r){return{protocol:h,id:t.id,error:L(r)}}}function z(e){const t=e.result;return!t||!("bytes"in t)||!(t.bytes.buffer instanceof ArrayBuffer)?[]:[t.bytes.buffer]}let B=0;function A(e){const t=new Error("报表 Worker 任务已取消");return t.name="AbortError",Object.defineProperty(t,"cause",{value:e}),t}function G(e,t){if(!e||typeof e!="object")return;const r=e;return r.protocol===h&&r.id===t?r:void 0}function H(e,t={}){const r=t.timeoutMs??12e4;if(!Number.isFinite(r)||r<=0||r>2147483647)throw new RangeError("Worker timeoutMs 应为正有限数字,且不能超过 2147483647");const o=(s,n,i)=>i?.aborted?Promise.reject(A(i.reason)):new Promise((u,c)=>{let m;try{m=e()}catch(a){c(a);return}const l=`mach-report-${Date.now()}-${++B}`;let g=!1,$=()=>{},b;const d=(a,f)=>{g||(g=!0,b&&clearTimeout(b),$(),i?.removeEventListener("abort",w),Promise.resolve().then(()=>m.terminate()).then(()=>a===void 0?u(f):c(a),x=>c(a??x)))},w=()=>d(A(i?.reason)),v=a=>{const f=G(a,l);f&&(f.error&&typeof f.error.name=="string"&&typeof f.error.message=="string"?d(new k(f.error)):C(f.result,s)&&(s!=="export"||"format"in f.result&&"format"in n&&f.result.format===n.format)?d(void 0,f.result):d(new Error("报表 Worker 返回无效响应")))};try{if($=m.subscribe(v,a=>d(a??new Error("报表 Worker 执行失败"))),g){$();return}i?.addEventListener("abort",w,{once:!0}),b=setTimeout(()=>d(new k({name:"ReportRuntimeError",code:"timeout",message:`报表 Worker 执行超过 ${r}ms`})),r),i?.aborted?w():m.postMessage({protocol:h,id:l,operation:s,input:n})}catch(a){d(a)}});return{prepare:(s,n)=>o("prepare",s,n),export:(s,n)=>o("export",s,n)}}function Q(e){const t=e.filename??e.workerUrl;if(!t||e.filename&&e.workerUrl)throw new TypeError("请且仅请指定 filename 或 workerUrl");return H(()=>{const r=new E.Worker(t,e.workerOptions);return{postMessage:o=>r.postMessage(o),terminate:()=>r.terminate(),subscribe:(o,s)=>{const n=i=>s(new Error(`报表 Worker 在响应前退出(code ${i})`));return r.on("message",o),r.on("error",s),r.on("messageerror",s),r.on("exit",n),()=>{r.off("message",o),r.off("error",s),r.off("messageerror",s),r.off("exit",n)}}}},e)}function V(e){const t=E.parentPort;if(!t)throw new Error("serveNodeReportWorker 只能在 worker_threads 中调用");const r=o=>{U(o)&&q(e,o).then(s=>t.postMessage(s,z(s)))};return t.on("message",r),()=>t.off("message",r)}exports.ReportWorkerError=k;exports.createNodeReportExecutor=Q;exports.serveNodeReportWorker=V;
|