@agile-team/mach-report 2.8.4 → 2.9.0
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 +27 -9
- package/dist/ExcelBindingWizard-DiaxvWpQ.cjs +1 -0
- package/dist/ExcelBindingWizard-RLJIXKKG.js +816 -0
- package/dist/{ReportPreview-DTWiCH5a.js → ReportPreview-B5ZNEasW.js} +22 -21
- package/dist/ReportPreview-DRtVPUYo.cjs +6 -0
- package/dist/RuleEditor-N4qnMZ-Z.cjs +1 -0
- package/dist/RuleEditor-ynnUEK0s.js +1575 -0
- package/dist/TemplateReviewPanel-C_Lmzis6.cjs +1 -0
- package/dist/TemplateReviewPanel-Cn32OY4_.js +173 -0
- package/dist/analytics.cjs +1 -1
- package/dist/analytics.d.cts +188 -5
- package/dist/analytics.d.ts +188 -5
- package/dist/analytics.js +68 -202
- package/dist/{assets-B9qJku3V.js → assets-DDsfEi8R.js} +3 -3
- package/dist/{assets-RZan7EyL.cjs → assets-ahTRUXRk.cjs} +1 -1
- package/dist/binding-preview-B5834rml.cjs +2 -0
- package/dist/binding-preview-ChXjN5L5.js +649 -0
- package/dist/{capabilities-joSutDF8.js → capabilities-Cm9hZ3uV.js} +5 -5
- package/dist/{capabilities-xGTpnPjJ.cjs → capabilities-DYzWL7KS.cjs} +1 -1
- package/dist/dataset-BkRmwgeJ.cjs +1 -0
- package/dist/dataset-H5pFuQGU.js +121 -0
- package/dist/defaults-DXhiM1ZD.js +10 -0
- package/dist/defaults-xhmcLLrf.cjs +1 -0
- package/dist/designer.cjs +2 -1
- package/dist/designer.d.cts +1373 -6
- package/dist/designer.d.ts +1373 -6
- package/dist/designer.js +2495 -767
- package/dist/grid-geometry-BNptEcMm.js +65 -0
- package/dist/grid-geometry-CZ_cYB6O.cjs +1 -0
- package/dist/import-template-CNMLJSQw.js +23030 -0
- package/dist/import-template-CpxunKVp.cjs +29 -0
- package/dist/index.cjs +7 -7
- package/dist/index.d.cts +202 -3
- package/dist/index.d.ts +202 -3
- package/dist/index.js +554 -535
- package/dist/matrix-CNPDJxED.js +745 -0
- package/dist/matrix-Div9Ilh3.cjs +1 -0
- package/dist/pdf.cjs +46 -46
- package/dist/pdf.d.cts +2 -0
- package/dist/pdf.d.ts +2 -0
- package/dist/pdf.js +17 -16
- package/dist/{plugin-CQmJXNcA.js → plugin-BWwIgZXV.js} +1 -1
- package/dist/{plugin-DSFu51Ua.cjs → plugin-Dg-J1Rbm.cjs} +1 -1
- package/dist/progress-7OSZbFok.js +39 -0
- package/dist/progress-BnLiS5eH.cjs +1 -0
- package/dist/render-plan-7xpGrCEl.cjs +1 -0
- package/dist/render-plan-Ck6koX9L.js +13 -0
- package/dist/server/node.d.cts +62 -0
- package/dist/server/node.d.ts +62 -0
- package/dist/server-node.cjs +1 -1
- package/dist/server-node.js +373 -145
- package/dist/server.cjs +1 -1
- package/dist/server.d.cts +311 -0
- package/dist/server.d.ts +311 -0
- package/dist/server.js +590 -186
- package/dist/{style-Dv5o_x6b.js → style-CQ1PK8ZB.js} +5 -5
- package/dist/style-UgIJSA1F.cjs +1 -0
- package/dist/style.css +1 -1
- package/dist/template-registry-CHMYZ7SD.cjs +1 -0
- package/dist/template-registry-R61AMCr0.js +433 -0
- package/dist/template-review-hvWWQFsi.js +1027 -0
- package/dist/template-review-jEcOfmGr.cjs +1 -0
- package/dist/textwrap-CWHyzkBH.cjs +4 -0
- package/dist/{textwrap-DOl04hCQ.js → textwrap-DE1kgcJB.js} +1 -1
- package/dist/{validate-DfHPD8wX.cjs → validate-BDiMqkJC.cjs} +1 -1
- package/dist/{validate-S6CgIm5d.js → validate-DWPF7icl.js} +13 -13
- package/dist/vue/async.d.cts +186 -0
- package/dist/vue/async.d.ts +186 -0
- package/dist/vue/index.d.cts +271 -3
- package/dist/vue/index.d.ts +271 -3
- package/dist/vue-async.cjs +1 -1
- package/dist/vue-async.js +3 -3
- package/dist/vue.cjs +1 -1
- package/dist/vue.js +4 -4
- package/dist/worker.cjs +1 -1
- package/dist/worker.d.cts +2 -0
- package/dist/worker.d.ts +2 -0
- package/dist/worker.js +1 -1
- package/dist/xlsx.cjs +2 -30
- package/dist/xlsx.d.cts +268 -0
- package/dist/xlsx.d.ts +268 -0
- package/dist/xlsx.js +220 -23440
- package/package.json +1 -1
- package/dist/ReportPreview-DjGeWHY-.cjs +0 -6
- package/dist/progress-DD964VAB.js +0 -102
- package/dist/progress-KvhwXZ1Q.cjs +0 -1
- package/dist/render-plan-CckPEQyf.cjs +0 -1
- package/dist/render-plan-uzZYYSy1.js +0 -19
- package/dist/report-source-BXHNy_AD.js +0 -755
- package/dist/report-source-CCAvFE3t.cjs +0 -1
- package/dist/style-CpwiOPyB.cjs +0 -1
- package/dist/template-registry-DWExx0gU.cjs +0 -1
- package/dist/template-registry-NpOakidD.js +0 -354
- package/dist/textwrap-CnGsEum6.cjs +0 -4
package/dist/server.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var Q=Object.create;var B=Object.defineProperty;var _=Object.getOwnPropertyDescriptor;var W=Object.getOwnPropertyNames;var G=Object.getPrototypeOf,U=Object.prototype.hasOwnProperty;var X=(r,e,n,a)=>{if(e&&typeof e=="object"||typeof e=="function")for(let t of W(e))!U.call(r,t)&&t!==n&&B(r,t,{get:()=>e[t],enumerable:!(a=_(e,t))||a.enumerable});return r};var N=(r,e,n)=>(n=r!=null?Q(G(r)):{},X(e||!r||!r.__esModule?B(n,"default",{value:r,enumerable:!0}):n,r));Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const P=require("./report-source-CCAvFE3t.cjs"),C=require("./report-params-CjWrjPl6.cjs"),T=require("./param-validation-CiTxFeZ8.cjs"),H=require("./validate-DfHPD8wX.cjs"),$=require("./task-events-DQ276fn2.cjs");class b extends Error{constructor(e,n,a){super(n),this.code=e,this.path=a,this.name="ReportRuntimeError"}}function x(r){if(!r?.aborted)return;if(r.reason instanceof b)throw r.reason;const e=new Error("报表执行已取消");throw e.name="AbortError",Object.defineProperty(e,"cause",{value:r.reason}),e}function y(r,e,n){if(!Number.isFinite(n)||n<0)throw new b("budget",`${e} 的执行统计无效`,`$.budget.${e}`);const a=r[e];if(a!==void 0&&n>a)throw new b("budget",`${e} 超出预算 ${a}(实际 ${n})`,`$.budget.${e}`)}function K(r){for(const[e,n]of Object.entries(r)){if(n!==void 0&&(!Number.isFinite(n)||n<=0))throw new b("budget",`${e} 应为正有限数字`,`$.budget.${e}`);if(e!=="maxDurationMs"&&n!==void 0&&!Number.isSafeInteger(n))throw new b("budget",`${e} 应为正安全整数`,`$.budget.${e}`);if(e==="maxDurationMs"&&n!==void 0&&n>2147483647)throw new b("budget","maxDurationMs 不能超过 2147483647",`$.budget.${e}`)}}async function F(r,e,n){const a=new AbortController,t=()=>a.abort(r?.reason);r?.addEventListener("abort",t,{once:!0}),r?.aborted&&t();const s=Date.now(),d=e===void 0?void 0:setTimeout(()=>a.abort(new b("timeout",`报表执行超过 ${e}ms`)),e);let u;try{x(a.signal);const p=new Promise((w,i)=>{u=()=>{try{x(a.signal)}catch(g){i(g)}},a.signal.addEventListener("abort",u,{once:!0})}),c=await Promise.race([n(a.signal),p]);if(x(a.signal),e!==void 0&&Date.now()-s>e)throw new b("timeout",`报表执行超过 ${e}ms`);return c}finally{d!==void 0&&clearTimeout(d),u&&a.signal.removeEventListener("abort",u),r?.removeEventListener("abort",t)}}function V(r,e){if(e.length===0)throw new P.ReportSourceError("config","缺少报表 ID");return e.map(n=>{if(!Object.prototype.hasOwnProperty.call(r,n))throw new P.ReportSourceError("config",`未注册报表 ${n}`);return r[n]})}function D(r){return r.grid?.cells.reduce((e,n)=>e+n.reduce((a,t)=>a+(t?1+(t.children?.reduce((s,d)=>s+D(d),0)??0):0),0),0)??0}function Y(r,e){return r.pages.reduce((n,a)=>n+a.components.reduce((t,s)=>{if(s.kind!=="list")return t+D(s);const d=e[s.dataset]?.length??0;return t+s.columns.length*(d+(s.headerGrid?.cells.length??1))},0),0)}function Z(r,e=Object.keys(r)){return V(r,e).map((n,a)=>{const t=e[a],s=C.getReportParamDefs(r,[t]);return{id:t,params:JSON.parse(JSON.stringify(s)),paramsSchema:T.reportParamsJsonSchema(s),datasetsSchema:Object.fromEntries(Object.entries(n.dataSchema??{}).map(([d,u])=>[d,P.datasetJsonSchema(u)])),capabilities:{save:!!n.provider?.save,export:!!n.provider?.export,templates:!!n.provider?.templates}}})}function z(r,e){V(r,e.reportIds);const n=T.validateReportParams(C.getReportParamDefs(r,e.reportIds),e.params),a=e.reportIds.flatMap(s=>T.validateReportParams(C.getReportParamDefs(r,[s]),e.params).issues),t=new Map(a.map(s=>[JSON.stringify(s),s]));return{...n,ok:t.size===0,issues:[...t.values()]}}function ee(r,e){const n=H.validateRenderPlan(r.plan);if(!n.ok)throw new P.ReportSourceError("validate",`执行器返回无效计划:${n.errors[0]?.path}`);const a=r.plan.pages.length,t=r.plan.pages.reduce((s,d)=>s+d.components.reduce((u,p)=>u+D(p),0),0);return y(e,"maxRows",r.stats.rows),y(e,"maxPages",a),y(e,"maxCells",t),{...r,stats:{...r.stats,pages:a,cells:t}}}function M(r,e){const n=z(r.reports,e);if(!n.ok)throw new T.ReportParamValidationError(n.issues)}function A(r=[]){return r.map(({code:e,message:n,severity:a,pageIndex:t,componentIndex:s})=>({code:e,message:n,severity:a,path:`$.pages.${t}.components.${s}`}))}function te(r){const e={maxRows:1e5,maxPages:1e4,maxCells:2e6,maxOutputBytes:1e8,maxDurationMs:12e4,...r.budget};K(e);const n=async(t,s)=>{const d=Date.now();let u=0,p=0;const c=[],w={},i=Object.fromEntries(Object.entries(r.reports).map(([l,o])=>[l,{...o,pagination:{...r.pagination,...o.pagination,...s?{measurer:s}:{}},dataValidation:{...o.dataValidation,maxRows:Math.min(e.maxRows??1e5,o.dataValidation?.maxRows??1/0),maxCells:Math.min(e.maxCells??2e6,o.dataValidation?.maxCells??1/0)}}])),m=await P.createReportSource(i,{maxPages:e.maxPages,onDatasets:(l,o,h)=>{u+=Object.values(o).reduce((R,O)=>R+O.length,0),y(e,"maxRows",u),p+=Y(h,o),y(e,"maxCells",p)}})({...t,params:t.params??{},onWarning:l=>c.push({code:"layout",message:l,severity:"warning"}),onQueryResult:(l,o)=>{Object.defineProperty(w,l,{value:{revision:o.revision,sourceToken:o.sourceToken},enumerable:!0,writable:!0}),c.push(...o.diagnostics??[])}}).catch(l=>{throw l instanceof P.ReportSourceError&&l.cause instanceof P.PaginatePageLimitError?new b("budget",l.cause.message,"$.budget.maxPages"):l});x(t.signal);const f=m.pages.reduce((l,o)=>l+o.components.reduce((h,R)=>h+D(R),0),0);return y(e,"maxPages",m.pages.length),y(e,"maxCells",f),{plan:m,diagnostics:c,revisions:w,stats:{rows:u,pages:m.pages.length,cells:f,durationMs:Date.now()-d}}},a=async t=>{const s=Date.now();if(t.format!=="pdf"&&t.format!=="xlsx")throw new P.ReportSourceError("config","不支持的报表导出格式");const d=t.format==="pdf"?await import("@agile-team/mach-report/pdf"):void 0;x(t.signal);const u=t.pdf?.customFontFaces??(t.pdf?.customFontBytes?{regular:t.pdf.customFontBytes}:void 0),p=d&&u?d.createFontBytesMeasurer(u):void 0,c=await n(t,p);x(t.signal);const w=$.createReportTaskReporter(t),i=await w.run("encode",async()=>{let f;if(t.format==="pdf"){x(t.signal);const l=await d.renderPlanToPdf(c.plan,{...t.pdf,...p?{measurer:p}:{},fidelity:t.fidelity,signal:t.signal,onProgress:(o,h)=>w.progress("encode",o,h)});f=l.bytes,c.diagnostics.push(...A(l.diagnostics)),c.diagnostics.push(...l.fidelityWarnings.map(o=>({code:"pdf-fidelity",message:o,severity:"warning"})),...l.pageErrors.map(o=>({code:"pdf-page",message:o,severity:"error"}))),l.unsupportedTextCount&&c.diagnostics.push({code:"pdf-font",message:`${l.unsupportedTextCount} 段文本包含当前字体不支持的字符`,severity:"warning"})}else{const{renderPlanToXlsx:l}=await import("@agile-team/mach-report/xlsx");x(t.signal);const o=await l(c.plan,{...t.xlsx,fidelity:t.fidelity,signal:t.signal,onProgress:(h,R)=>w.progress("encode",h,R)});f=new Uint8Array(o.buffer),c.diagnostics.push(...A(o.diagnostics)),c.diagnostics.push(...o.warnings.map(h=>({code:"xlsx-fidelity",message:h,severity:"warning"})))}return x(t.signal),f});y(e,"maxOutputBytes",i.byteLength);const{plan:g,...m}=c;return{...m,bytes:i,format:t.format,contentType:t.format==="pdf"?"application/pdf":"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",stats:{...m.stats,durationMs:Date.now()-s,outputBytes:i.byteLength}}};return{describe:t=>Z(r.reports,t),validate:t=>z(r.reports,t),prepare:t=>F(t.signal,e.maxDurationMs,async s=>{const{signal:d,onEvent:u,...p}=t,c={...p,taskId:p.taskId??$.reportTaskId()},w=u?m=>$.notifyReportTaskEvent(u,m,s):void 0,i={...c,signal:s,onEvent:w};$.createReportTaskReporter(i).runSync("validate",()=>M(r,c));const g=r.executor?await r.executor.prepare(c,s,w):await n(i);return ee(g,e)}),export:t=>F(t.signal,e.maxDurationMs,async s=>{const{signal:d,onEvent:u,...p}=t,c={...p,taskId:p.taskId??$.reportTaskId()},w=u?m=>$.notifyReportTaskEvent(u,m,s):void 0,i={...c,signal:s,onEvent:w};if($.createReportTaskReporter(i).runSync("validate",()=>M(r,c)),c.format!=="pdf"&&c.format!=="xlsx")throw new P.ReportSourceError("config","不支持的报表导出格式");const g=r.executor?await r.executor.export(c,s,w):await a(i);return y(e,"maxRows",g.stats.rows),y(e,"maxPages",g.stats.pages),y(e,"maxCells",g.stats.cells),y(e,"maxOutputBytes",g.bytes.byteLength),{...g,stats:{...g.stats,outputBytes:g.bytes.byteLength}}})}}function re(r={}){const e=r.concurrency??1,n=r.maxQueued??20;if(!Number.isSafeInteger(e)||e<1||!Number.isSafeInteger(n)||n<0)throw new b("budget","队列并发应为正整数,排队上限应为非负整数");const a=[],t=new Set,s=new Set;let d=!1,u=0;const p=()=>{for(;!d&&t.size<e&&a.length;)a.shift().start()};return{get active(){return t.size},get pending(){return a.length},enqueue:(w,i={})=>{if(d)throw new b("closed","任务队列已关闭");if(x(i.signal),t.size>=e&&a.length>=n)throw new b("queue-full","报表任务队列已满");const g=i.id??`report-${++u}`;if(s.has(g))throw new b("queue-full","任务 ID 正在执行或排队");const m=new AbortController;let f="queued",l,o,h,R;const O=new Promise((v,E)=>{h=v,R=E}),j=()=>{i.signal?.removeEventListener("abort",q),s.delete(g)},k=v=>{if(f!=="queued"&&f!=="running")return;const E=f==="queued";f="cancelled",m.abort(v);try{x(m.signal)}catch(I){R(I)}if(E){const I=a.indexOf(S);I>=0&&a.splice(I,1),j(),p()}},q=()=>k(i.signal?.reason),L=(v,E)=>{f==="running"&&(!Number.isFinite(v)||v<0||E!==void 0&&(!Number.isFinite(E)||E<v)||(l={completed:v,...E===void 0?{}:{total:E}}))},J=v=>{f!=="running"||m.signal.aborted||(o={...v,taskId:g},v.completed!==void 0&&L(v.completed,v.total),$.notifyReportTaskEvent(i.onEvent,o,m.signal))},S={cancel:k,start:()=>{f==="queued"&&(f="running",t.add(S),Promise.resolve().then(()=>(x(m.signal),w({taskId:g,signal:m.signal,reportProgress:L,onEvent:J}))).then(v=>{f==="running"&&(f="completed",h(v))},v=>{f==="running"&&(f="failed",R(v))}).finally(()=>{t.delete(S),j(),p()}))}};return s.add(g),i.signal?.addEventListener("abort",q,{once:!0}),i.signal?.aborted?k(i.signal.reason):t.size<e?S.start():a.push(S),{id:g,get status(){return f},get progress(){return l},get event(){return o},result:O,cancel:k}},close:(w={})=>{d=!0;for(const i of[...a])i.cancel(new b("closed","任务队列已关闭"));if(w.cancelRunning)for(const i of t)i.cancel(new b("closed","任务队列已关闭"))}}}exports.ReportRuntimeError=b;exports.createReportRuntime=te;exports.createReportTaskQueue=re;
|
|
1
|
+
"use strict";var re=Object.create;var z=Object.defineProperty;var ne=Object.getOwnPropertyDescriptor;var se=Object.getOwnPropertyNames;var ae=Object.getPrototypeOf,oe=Object.prototype.hasOwnProperty;var ie=(e,t,r,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let s of se(t))!oe.call(e,s)&&s!==r&&z(e,s,{get:()=>t[s],enumerable:!(n=ne(t,s))||n.enumerable});return e};var V=(e,t,r)=>(r=e!=null?re(ae(e)):{},ie(t||!e||!e.__esModule?z(r,"default",{value:e,enumerable:!0}):r,e));Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const P=require("./template-review-jEcOfmGr.cjs"),M=require("./report-params-CjWrjPl6.cjs"),$=require("./param-validation-CiTxFeZ8.cjs"),ce=require("./validate-BDiMqkJC.cjs"),ue=require("./dataset-BkRmwgeJ.cjs"),A=require("./task-events-DQ276fn2.cjs"),le=require("./template-registry-CHMYZ7SD.cjs");class v extends Error{constructor(t,r,n){super(r),this.code=t,this.path=n,this.name="ReportRuntimeError"}}function x(e){if(!e?.aborted)return;if(e.reason instanceof v)throw e.reason;const t=new Error("报表执行已取消");throw t.name="AbortError",Object.defineProperty(t,"cause",{value:e.reason}),t}function S(e,t,r){if(!Number.isFinite(r)||r<0)throw new v("budget",`${t} 的执行统计无效`,`$.budget.${t}`);const n=e[t];if(n!==void 0&&r>n)throw new v("budget",`${t} 超出预算 ${n}(实际 ${r})`,`$.budget.${t}`)}function de(e){for(const[t,r]of Object.entries(e)){if(r!==void 0&&(!Number.isFinite(r)||r<=0))throw new v("budget",`${t} 应为正有限数字`,`$.budget.${t}`);if(t!=="maxDurationMs"&&r!==void 0&&!Number.isSafeInteger(r))throw new v("budget",`${t} 应为正安全整数`,`$.budget.${t}`);if(t==="maxDurationMs"&&r!==void 0&&r>2147483647)throw new v("budget","maxDurationMs 不能超过 2147483647",`$.budget.${t}`)}}async function L(e,t,r){const n=new AbortController,s=()=>n.abort(e?.reason);e?.addEventListener("abort",s,{once:!0}),e?.aborted&&s();const a=Date.now(),c=t===void 0?void 0:setTimeout(()=>n.abort(new v("timeout",`报表执行超过 ${t}ms`)),t);let p;try{x(n.signal);const w=new Promise((u,i)=>{p=()=>{try{x(n.signal)}catch(f){i(f)}},n.signal.addEventListener("abort",p,{once:!0})}),o=await Promise.race([r(n.signal),w]);if(x(n.signal),t!==void 0&&Date.now()-a>t)throw new v("timeout",`报表执行超过 ${t}ms`);return o}finally{c!==void 0&&clearTimeout(c),p&&n.signal.removeEventListener("abort",p),e?.removeEventListener("abort",s)}}function H(e,t){if(t.length===0)throw new P.ReportSourceError("config","缺少报表 ID");return t.map(r=>{if(!Object.prototype.hasOwnProperty.call(e,r))throw new P.ReportSourceError("config",`未注册报表 ${r}`);return e[r]})}function C(e){return e.grid?.cells.reduce((t,r)=>t+r.reduce((n,s)=>n+(s?1+(s.children?.reduce((a,c)=>a+C(c),0)??0):0),0),0)??0}function fe(e,t,r){return e.pages.reduce((n,s)=>n+s.components.reduce((a,c)=>c.kind!=="list"?a+C(c):a+P.estimateListLayoutCells(c,t,r),0),0)}function pe(e,t=Object.keys(e)){return H(e,t).map((r,n)=>{const s=t[n],a=M.getReportParamDefs(e,[s]);return{id:s,params:JSON.parse(JSON.stringify(a)),paramsSchema:$.reportParamsJsonSchema(a),datasetsSchema:Object.fromEntries(Object.entries(r.dataSchema??{}).map(([c,p])=>[c,ue.datasetJsonSchema(p)])),capabilities:{save:!!r.provider?.save,export:!!r.provider?.export,templates:!!r.provider?.templates}}})}function K(e,t){H(e,t.reportIds);const r=$.validateReportParams(M.getReportParamDefs(e,t.reportIds),t.params),n=t.reportIds.flatMap(a=>$.validateReportParams(M.getReportParamDefs(e,[a]),t.params).issues),s=new Map(n.map(a=>[JSON.stringify(a),a]));return{...r,ok:s.size===0,issues:[...s.values()]}}function ge(e,t){const r=ce.validateRenderPlan(e.plan);if(!r.ok)throw new P.ReportSourceError("validate",`执行器返回无效计划:${r.errors[0]?.path}`);const n=e.plan.pages.length,s=e.plan.pages.reduce((a,c)=>a+c.components.reduce((p,w)=>p+C(w),0),0);return S(t,"maxRows",e.stats.rows),S(t,"maxPages",n),S(t,"maxCells",s),{...e,stats:{...e.stats,pages:n,cells:s}}}function Q(e,t){const r=K(e.reports,t);if(!r.ok)throw new $.ReportParamValidationError(r.issues)}function X(e=[]){return e.map(({code:t,message:r,severity:n,pageIndex:s,componentIndex:a})=>({code:t,message:r,severity:n,path:`$.pages.${s}.components.${a}`}))}function U(e){const t={maxRows:1e5,maxPages:1e4,maxCells:2e6,maxOutputBytes:1e8,maxDurationMs:12e4,...e.budget};de(t);const r=async(s,a)=>{const c=Date.now();let p=0,w=0;const o=[],u={},i=Object.fromEntries(Object.entries(e.reports).map(([d,l])=>[d,{...l,pagination:{...e.pagination,...l.pagination,...a?{measurer:a}:{}},dataValidation:{...l.dataValidation,maxRows:Math.min(t.maxRows??1e5,l.dataValidation?.maxRows??1/0),maxCells:Math.min(t.maxCells??2e6,l.dataValidation?.maxCells??1/0)}}])),m=await P.createReportSource(i,{maxPages:t.maxPages,maxCells:t.maxCells,onDatasets:(d,l,y)=>{p+=Object.values(l).reduce((R,T)=>R+T.length,0),S(t,"maxRows",p),w+=fe(y,l,i[d]?.pagination),S(t,"maxCells",w)}})({...s,params:s.params??{},onWarning:d=>o.push({code:"layout",message:d,severity:"warning"}),onQueryResult:(d,l)=>{Object.defineProperty(u,d,{value:{revision:l.revision,sourceToken:l.sourceToken},enumerable:!0,writable:!0}),o.push(...l.diagnostics??[])}}).catch(d=>{throw d instanceof P.ReportSourceError&&d.cause instanceof P.PaginatePageLimitError?new v("budget",d.cause.message,"$.budget.maxPages"):d instanceof P.ReportSourceError&&d.cause instanceof P.PaginateCellLimitError?new v("budget",d.cause.message,"$.budget.maxCells"):d});x(s.signal);const h=m.pages.reduce((d,l)=>d+l.components.reduce((y,R)=>y+C(R),0),0);return S(t,"maxPages",m.pages.length),S(t,"maxCells",h),{plan:m,diagnostics:o,revisions:u,stats:{rows:p,pages:m.pages.length,cells:h,durationMs:Date.now()-c}}},n=async s=>{const a=Date.now();if(s.format!=="pdf"&&s.format!=="xlsx")throw new P.ReportSourceError("config","不支持的报表导出格式");const c=s.format==="pdf"?await import("@agile-team/mach-report/pdf"):void 0;x(s.signal);const p=s.pdf?.customFontFaces??(s.pdf?.customFontBytes?{regular:s.pdf.customFontBytes}:void 0),w=c&&p?c.createFontBytesMeasurer(p):void 0,o=await r(s,w);x(s.signal);const u=A.createReportTaskReporter(s),i=await u.run("encode",async()=>{let h;if(s.format==="pdf"){x(s.signal);const d=await c.renderPlanToPdf(o.plan,{...s.pdf,...w?{measurer:w}:{},fidelity:s.fidelity,signal:s.signal,onProgress:(l,y)=>u.progress("encode",l,y)});h=d.bytes,o.diagnostics.push(...X(d.diagnostics)),o.diagnostics.push(...d.fidelityWarnings.map(l=>({code:"pdf-fidelity",message:l,severity:"warning"})),...d.pageErrors.map(l=>({code:"pdf-page",message:l,severity:"error"}))),d.unsupportedTextCount&&o.diagnostics.push({code:"pdf-font",message:`${d.unsupportedTextCount} 段文本包含当前字体不支持的字符`,severity:"warning"})}else{const{renderPlanToXlsx:d}=await import("@agile-team/mach-report/xlsx");x(s.signal);const l=await d(o.plan,{...s.xlsx,fidelity:s.fidelity,signal:s.signal,onProgress:(y,R)=>u.progress("encode",y,R)});h=new Uint8Array(l.buffer),o.diagnostics.push(...X(l.diagnostics)),o.diagnostics.push(...l.warnings.map(y=>({code:"xlsx-fidelity",message:y,severity:"warning"})))}return x(s.signal),h});S(t,"maxOutputBytes",i.byteLength);const{plan:f,...m}=o;return{...m,bytes:i,format:s.format,contentType:s.format==="pdf"?"application/pdf":"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",stats:{...m.stats,durationMs:Date.now()-a,outputBytes:i.byteLength}}};return{describe:s=>pe(e.reports,s),validate:s=>K(e.reports,s),prepare:s=>L(s.signal,t.maxDurationMs,async a=>{const{signal:c,onEvent:p,...w}=s,o={...w,taskId:w.taskId??A.reportTaskId()},u=p?m=>A.notifyReportTaskEvent(p,m,a):void 0,i={...o,signal:a,onEvent:u};A.createReportTaskReporter(i).runSync("validate",()=>Q(e,o));const f=e.executor?await e.executor.prepare(o,a,u):await r(i);return ge(f,t)}),export:s=>L(s.signal,t.maxDurationMs,async a=>{const{signal:c,onEvent:p,...w}=s,o={...w,taskId:w.taskId??A.reportTaskId()},u=p?m=>A.notifyReportTaskEvent(p,m,a):void 0,i={...o,signal:a,onEvent:u};if(A.createReportTaskReporter(i).runSync("validate",()=>Q(e,o)),o.format!=="pdf"&&o.format!=="xlsx")throw new P.ReportSourceError("config","不支持的报表导出格式");const f=e.executor?await e.executor.export(o,a,u):await n(i);return S(t,"maxRows",f.stats.rows),S(t,"maxPages",f.stats.pages),S(t,"maxCells",f.stats.cells),S(t,"maxOutputBytes",f.bytes.byteLength),{...f,stats:{...f.stats,outputBytes:f.bytes.byteLength}}})}}function we(e={}){const t=e.concurrency??1,r=e.maxQueued??20;if(!Number.isSafeInteger(t)||t<1||!Number.isSafeInteger(r)||r<0)throw new v("budget","队列并发应为正整数,排队上限应为非负整数");const n=[],s=new Set,a=new Set;let c=!1,p=0;const w=()=>{for(;!c&&s.size<t&&n.length;)n.shift().start()};return{get active(){return s.size},get pending(){return n.length},enqueue:(u,i={})=>{if(c)throw new v("closed","任务队列已关闭");if(x(i.signal),s.size>=t&&n.length>=r)throw new v("queue-full","报表任务队列已满");const f=i.id??`report-${++p}`;if(a.has(f))throw new v("queue-full","任务 ID 正在执行或排队");const m=new AbortController;let h="queued",d,l,y,R;const T=new Promise((b,E)=>{y=b,R=E}),F=()=>{i.signal?.removeEventListener("abort",B),a.delete(f)},N=b=>{if(h!=="queued"&&h!=="running")return;const E=h==="queued";h="cancelled",m.abort(b);try{x(m.signal)}catch(I){R(I)}if(E){const I=n.indexOf(q);I>=0&&n.splice(I,1),F(),w()}},B=()=>N(i.signal?.reason),_=(b,E)=>{h==="running"&&(!Number.isFinite(b)||b<0||E!==void 0&&(!Number.isFinite(E)||E<b)||(d={completed:b,...E===void 0?{}:{total:E}}))},te=b=>{h!=="running"||m.signal.aborted||(l={...b,taskId:f},b.completed!==void 0&&_(b.completed,b.total),A.notifyReportTaskEvent(i.onEvent,l,m.signal))},q={cancel:N,start:()=>{h==="queued"&&(h="running",s.add(q),Promise.resolve().then(()=>(x(m.signal),u({taskId:f,signal:m.signal,reportProgress:_,onEvent:te}))).then(b=>{h==="running"&&(h="completed",y(b))},b=>{h==="running"&&(h="failed",R(b))}).finally(()=>{s.delete(q),F(),w()}))}};return a.add(f),i.signal?.addEventListener("abort",B,{once:!0}),i.signal?.aborted?N(i.signal.reason):s.size<t?q.start():n.push(q),{id:f,get status(){return h},get progress(){return d},get event(){return l},result:T,cancel:N}},close:(u={})=>{c=!0;for(const i of[...n])i.cancel(new v("closed","任务队列已关闭"));if(u.cancelRunning)for(const i of s)i.cancel(new v("closed","任务队列已关闭"))}}}class g extends Error{constructor(t,r){super(r),this.code=t,this.name="ReportJobError"}}function J(e,t){if(typeof e!="string"||!e.trim()||e.length>256||typeof t!="string"||!/^[a-zA-Z0-9][a-zA-Z0-9_.:-]{0,127}$/.test(t))throw new g("input","任务范围应非空,ID应为1–128位安全标识")}function me(e){const t=Array.isArray(e),r=Object.getPrototypeOf(e),n=Object.keys(e);if(r!==(t?Array.prototype:Object.prototype)&&!(r===null&&!t))throw new g("input","任务对象应为普通 JSON 对象");if(Object.getOwnPropertySymbols(e).length)throw new g("input","任务不能包含Symbol");if(Object.getOwnPropertyNames(e).filter(a=>!(t&&a==="length")).length!==n.length)throw new g("input","任务不能包含不可枚举属性");if(t&&(n.length!==e.length||n.some(a=>!/^(0|[1-9]\d*)$/.test(a)||Number(a)>=e.length)))throw new g("input","任务数组不能稀疏或包含额外属性");return n.sort()}function he(e,t){const r=Object.getOwnPropertyDescriptor(e,t);if(["__proto__","prototype","constructor"].includes(t)||r.get||r.set)throw new g("input","任务不能包含危险键或访问器");return r.value}function O(e,t={}){const r=t.maxNodes??2e4,n=t.maxCharacters??1e6;if(!Number.isSafeInteger(r)||r<1||r>2e4||!Number.isSafeInteger(n)||n<1||n>1e6)throw new g("input","任务JSON预算无效");let s=0,a=0;const c=new Set;function p(o,u){if(++s>r||u>32)throw new g("input","任务数据超过节点或深度预算");if(typeof o=="string"){if(a+=o.length,a>n)throw new g("input","任务文本超过预算");return o}if(o===null||typeof o=="boolean"||typeof o=="number"&&Number.isFinite(o))return o;if(!o||typeof o!="object"||c.has(o))throw new g("input","任务只允许无循环的有限 JSON 值");const i=Array.isArray(o),f=me(o);c.add(o);const m=Object.create(null);for(const h of f){if(a+=h.length,a>n)throw new g("input","任务文本超过预算");m[h]=p(he(o,h),u+1)}return c.delete(o),i?Array.from({length:o.length},(h,d)=>m[String(d)]):m}const w=JSON.stringify(p(e,0));if(w.length>n)throw new g("input","任务序列化数据超过字符预算");return w}function k(e){return JSON.parse(JSON.stringify(e))}function G(e,t,r,n){if(J(t,r),e.scope!==t||e.id!==r||e.revision!==(n??0)+1||!Number.isSafeInteger(e.revision))throw new g("input","任务身份或修订不匹配");if(![e.createdAt,e.updatedAt,e.attempt].every(s=>Number.isSafeInteger(s)&&s>=0)||!["queued","running","completed","failed","cancelled"].includes(e.status))throw new g("input","任务状态无效");if(e.lease&&(!e.lease.owner||!Number.isSafeInteger(e.lease.expiresAt)))throw new g("input","任务租约无效");if(!e.request||typeof e.request.kind!="string"||!e.request.kind.trim()||e.request.kind.length>128)throw new g("input","任务类型无效");O(e.request.payload,{maxNodes:19e3,maxCharacters:75e4}),O(e)}function Y(e,t){if(e&&(e.createdAt!==t.createdAt||O(e.request)!==O(t.request)))throw new g("input","任务创建时间与请求不可变更,请使用新任务ID")}function ee(e,t){return e.status==="queued"||e.status==="running"&&(e.lease?.expiresAt??0)<=t}function ye(){const e=new Map,t=(r,n)=>JSON.stringify([r,n]);return{read:async(r,n)=>{const s=e.get(t(r,n));return s?k(s):void 0},candidates:async(r,n)=>Array.from(e.values()).filter(s=>ee(s,r)).sort((s,a)=>s.createdAt-a.createdAt).slice(0,n).map(k),compareAndSwap:async(r,n,s,a)=>{G(a,r,n,s);const c=e.get(t(r,n));return(c?.revision??null)!==s?!1:(Y(c,a),e.set(t(r,n),k(a)),!0)}}}function be(e){let t,r;const n=new Promise((s,a)=>{t=s,r=a});return n.catch(()=>{}),{progress:e,promise:n,resolve:t,reject:r}}function W(e){const t=Promise.reject(e);return t.catch(()=>{}),t}function ve(e,t){let r,n,s=!0,a;function c(o){a??=o??new Error("任务心跳更新失败"),n?.reject(a),n=void 0,t(a)}function p(o){const u=Promise.resolve().then(()=>e(o?.progress)).then(()=>o?.resolve(),i=>{o?.reject(i),c(i)}).finally(()=>{r=void 0;const i=n;n=void 0,i&&a===void 0&&p(i)});r=u,u.catch(i=>{c(i)})}function w(o){if(!s)return W(new g("closed","任务进度上报已结束"));if(a!==void 0)return W(a);n?n.progress=o:n=be(o);const u=n;return r||(n=void 0,p(u)),u.promise}return{progress:w,pulse(){s&&!r&&a===void 0&&p()},async finish(){for(s=!1;r;)await r;if(a!==void 0)throw a}}}function xe(e){const t=e.concurrency??1,r=e.leaseMs??3e4,n=e.heartbeatMs??1e4;if(![t,r,n].every(a=>Number.isSafeInteger(a)&&a>0)||n*2>r)throw new g("input","并发、租约、心跳应为正整数,租约至少为心跳的两倍");if(n>2147483647)throw new g("input","心跳间隔超过平台定时器上限(2147483647毫秒)");return{concurrency:t,leaseMs:r,heartbeatMs:n,now:()=>{const a=(e.now??Date.now)();if(!Number.isSafeInteger(a)||a<0)throw new g("input","任务时钟应返回非负整数毫秒");return a}}}async function j(e,t,r){J(t,r);const n=await e.read(t,r);if(!n)throw new g("missing","报表任务不存在");return n}async function D(e,t,r,n=[]){const s={...t,...r,revision:t.revision+1};for(const a of n)delete s[a];if(!await e.compareAndSwap(t.scope,t.id,t.revision,s))throw new g("conflict","报表任务已被其他请求更新");return k(s)}async function Re(e,t,r,n){if(!t.trim()||t.length>128)throw new g("input","workerId应为1–128位标识");for(const s of await e.store.candidates(n,100)){const a=s.cancelRequested?{...s,revision:s.revision+1,status:"cancelled",updatedAt:n}:{...s,revision:s.revision+1,status:"running",attempt:s.attempt+1,updatedAt:n,lease:{owner:t,expiresAt:n+r}};if(a.status==="cancelled"&&delete a.lease,await e.store.compareAndSwap(s.scope,s.id,s.revision,a))return a}}function Z(e,t,r,n){return e.status==="running"&&e.attempt===n&&e.lease?.owner===t&&e.lease.expiresAt>r}async function Se(e,t,r,n){const{scope:s,id:a}=r,c=r.lease.owner;let p;const w=ve(async l=>{const y=await j(e.store,s,a),R=t.now();if(!Z(y,c,R,r.attempt))throw new g("lease","报表任务租约已失效");if(y.cancelRequested){n.abort(new g("closed","任务已请求取消"));return}await D(e.store,y,{updatedAt:R,lease:{owner:c,expiresAt:R+t.leaseMs},...l?{progress:l}:{}})},l=>{p=l,n.abort(l)}),o=(l,y)=>!Number.isFinite(l)||l<0||y!==void 0&&(!Number.isFinite(y)||y<l)?Promise.reject(new g("input","任务进度无效")):w.progress({completed:l,...y===void 0?{}:{total:y}}),u=setInterval(()=>{w.pulse()},t.heartbeatMs);let i,f;try{const l=Object.prototype.hasOwnProperty.call(e.handlers,r.request.kind)?e.handlers[r.request.kind]:void 0;if(!l)throw new g("input","报表任务处理器未注册");if(!n.signal.aborted){const y=await l(k(r.request.payload),{scope:s,taskId:a,signal:n.signal,reportProgress:o});i=JSON.parse(O(y))}}catch(l){f=l??new Error("报表任务执行失败")}finally{clearInterval(u),await w.finish().catch(()=>{})}const m=await j(e.store,s,a),h=t.now();if(!Z(m,c,h,r.attempt))throw new g("lease","任务租约已失效,迟到结果不会写回");const d={...m,updatedAt:h};delete d.lease,f??=p,m.cancelRequested||n.signal.aborted&&p===void 0?d.status="cancelled":f!==void 0?(d.status="failed",d.error={code:f instanceof g?f.code:"execution",message:f instanceof Error?f.message.slice(0,500):"报表任务执行失败"}):(d.status="completed",d.result=i);try{O(d)}catch{delete d.result,d.status="failed",d.error={code:"result-budget",message:"任务结果与请求的组合超过存储预算,请保存文件引用而非文件内容"}}return D(e.store,m,d,["lease"])}function Pe(e){e={...e,handlers:{...e.handlers}};const t=xe(e),r=new Set,n=new Set;let s=!1,a=0;const c=(o,u)=>(J(o,u),e.store.read(o,u));return{submit:async o=>{if(o={...o},J(o.scope,o.id),!Object.prototype.hasOwnProperty.call(e.handlers,o.kind)||typeof e.handlers[o.kind]!="function")throw new g("input","报表任务处理器未注册");const u={kind:o.kind,payload:JSON.parse(O(o.payload,{maxNodes:19e3,maxCharacters:75e4}))},i=await c(o.scope,o.id);if(i){if(O(i.request)!==O(u))throw new g("conflict","相同任务ID不能提交不同内容");return i}const f=t.now(),m={scope:o.scope,id:o.id,revision:1,request:u,status:"queued",createdAt:f,updatedAt:f,attempt:0};if(!await e.store.compareAndSwap(o.scope,o.id,null,m)){const h=await c(o.scope,o.id);if(h&&O(h.request)===O(u))return h;throw new g("conflict","报表任务提交发生冲突")}return k(m)},get:c,runNext:o=>{if(s)return Promise.reject(new g("closed","报表任务服务已关闭"));if(a>=t.concurrency)return Promise.reject(new g("capacity","报表任务执行槽位已满"));a++;const u=new AbortController;r.add(u);const i=(async()=>{try{const f=await Re(e,o,t.leaseMs,t.now());return f?.status==="running"?await Se(e,t,f,u):f}finally{a--,r.delete(u)}})();return n.add(i),i.then(()=>n.delete(i),()=>n.delete(i)),i},get active(){return a},cancel:async(o,u)=>{const i=await j(e.store,o,u);return i.status!=="queued"&&i.status!=="running"?i:D(e.store,i,{updatedAt:t.now(),...i.status==="queued"?{status:"cancelled"}:{cancelRequested:!0}})},retry:async(o,u,i)=>{const f=await j(e.store,o,u);if(f.revision!==i||f.status!=="failed")throw new g("conflict","仅可重试当前修订的失败任务");const m={...f,status:"queued",updatedAt:t.now()};return delete m.error,delete m.progress,D(e.store,f,m,["error","progress"])},close:async()=>{s=!0;for(const o of r)o.abort(new g("closed","任务服务正在关闭"));await Promise.allSettled([...n])}}}async function Oe(e,t,r,n,s){const a=U({reports:{sample:{template:r,datasets:e.datasets}},budget:{maxRows:n.maxSampleRows??1e3,maxPages:n.maxPages??100,maxCells:n.maxCells??25e4,maxOutputBytes:n.maxOutputBytes??2e7,maxDurationMs:n.maxDurationMs??3e4}});try{const{result:c,digest:p}=await L(n.signal,n.maxDurationMs??3e4,async w=>{const o=await a.export({reportIds:["sample"],params:e.params,format:t,signal:w,fidelity:n.strictOutputs?"strict":"warn",pdf:n.pdf,xlsx:n.xlsx}),u=await n.digest?.(o.bytes);return x(w),await n.onArtifact?.(e.name,o),x(w),{result:o,digest:u}});s.artifacts.push({fixture:e.name,format:t,bytes:c.bytes.byteLength,pages:c.stats.pages,...p?{digest:p}:{}}),s.issues.push(...c.diagnostics.map(w=>({code:"output",severity:n.strictOutputs||w.severity==="error"?"error":"warning",path:"$.template",fixture:e.name,message:`${t}: ${w.message}`})))}catch(c){x(n.signal),s.issues.push({code:"output",severity:"error",path:"$.template",fixture:e.name,message:`${t}: ${c instanceof Error?c.message:String(c)}`})}}async function Ee(e,t={}){x(t.signal);const r={...P.reviewReportTemplate(e,t),artifacts:[]};if(!r.ok)return r;const n=le.parseReportTemplate(e).template,s=[...new Set(t.formats??["pdf","xlsx"])];if(!s.length||s.some(a=>a!=="pdf"&&a!=="xlsx"))return r.issues.push({code:"output",severity:"error",path:"$.formats",message:"真实文件验收应至少选择PDF/XLSX之一"}),r.ok=!1,r;for(const a of t.fixtures??[])for(const c of s)x(t.signal),!(c!=="pdf"&&c!=="xlsx")&&await Oe(a,c,n,t,r);return r.ok=r.issues.every(a=>a.severity!=="error"),r}exports.ReportJobError=g;exports.ReportRuntimeError=v;exports.checkJobIdentity=J;exports.cloneJob=k;exports.createMemoryReportJobStore=ye;exports.createReportJobService=Pe;exports.createReportRuntime=U;exports.createReportTaskQueue=we;exports.reviewReportPublication=Ee;exports.runnableJob=ee;exports.serializeJobJson=O;exports.validateJobRecord=G;exports.validateJobTransition=Y;
|
package/dist/server.d.cts
CHANGED
|
@@ -1,3 +1,19 @@
|
|
|
1
|
+
declare interface AggregateDefinition {
|
|
2
|
+
/** Output column; valueField defaults to this field. */
|
|
3
|
+
field: string;
|
|
4
|
+
op: AggregateOperation;
|
|
5
|
+
valueField?: string;
|
|
6
|
+
weightField?: string;
|
|
7
|
+
numeric?: "number" | "decimal";
|
|
8
|
+
digits?: number;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
declare type AggregateOperation = "sum" | "count" | "avg" | "min" | "max" | "weightedAvg";
|
|
12
|
+
|
|
13
|
+
export declare function checkJobIdentity(scope: string, id: string): void;
|
|
14
|
+
|
|
15
|
+
export declare function cloneJob<T>(value: T): T;
|
|
16
|
+
|
|
1
17
|
/**
|
|
2
18
|
* 列值格式化(声明式、JSON 可序列化)。
|
|
3
19
|
* 在 paginate 的 cellText 出口统一消费:格式化发生在进 RenderPlan 之前,
|
|
@@ -47,6 +63,22 @@ declare interface ComponentStyle {
|
|
|
47
63
|
rotateDeg?: number;
|
|
48
64
|
}
|
|
49
65
|
|
|
66
|
+
declare interface ComputedField extends ExpressionOptions {
|
|
67
|
+
field: string;
|
|
68
|
+
expression: ReportExpression | string;
|
|
69
|
+
/** Scope controls aggregate inputs; row expressions may reference the current row in any scope. */
|
|
70
|
+
scope?: "row" | "group" | "dataset";
|
|
71
|
+
groupBy?: readonly string[];
|
|
72
|
+
/** String adapter expressions cannot be inspected; declare dependencies explicitly. */
|
|
73
|
+
dependsOn?: readonly string[];
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** Reference store for tests; use a database CAS or the single-process durable Node adapter in production. */
|
|
77
|
+
export declare function createMemoryReportJobStore(): ReportJobStore;
|
|
78
|
+
|
|
79
|
+
/** Durable at-least-once dispatch; export/artifact handlers must be idempotent by scope+taskId. */
|
|
80
|
+
export declare function createReportJobService(options: ReportJobServiceOptions): ReportJobService;
|
|
81
|
+
|
|
50
82
|
/** 无 Vue、无 HTTP 框架的可选服务端外观;PDF/Excel 仅在对应 export 调用时加载。 */
|
|
51
83
|
export declare function createReportRuntime(options: ReportRuntimeOptions): ReportRuntime;
|
|
52
84
|
|
|
@@ -70,6 +102,22 @@ declare interface DatasetValidationOptions {
|
|
|
70
102
|
maxIssues?: number;
|
|
71
103
|
}
|
|
72
104
|
|
|
105
|
+
declare interface ExpressionOptions {
|
|
106
|
+
numeric?: "number" | "decimal";
|
|
107
|
+
digits?: number;
|
|
108
|
+
nulls?: "propagate" | "error";
|
|
109
|
+
divisionByZero?: "null" | "error";
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
declare interface FormulaAdapter {
|
|
113
|
+
/** Explicit trusted engine; core never evaluates source code/macros/external workbook references. */
|
|
114
|
+
evaluate(expression: string, context: {
|
|
115
|
+
row: Readonly<Record<string, unknown>>;
|
|
116
|
+
rowIndex: number;
|
|
117
|
+
rows: readonly Record<string, unknown>[];
|
|
118
|
+
}): unknown;
|
|
119
|
+
}
|
|
120
|
+
|
|
73
121
|
declare interface GridCell {
|
|
74
122
|
text?: string;
|
|
75
123
|
rawText?: string;
|
|
@@ -114,6 +162,9 @@ declare interface ListColumn {
|
|
|
114
162
|
valueType?: GridCell["valueType"];
|
|
115
163
|
/** 字段语义用于 auto 对齐;明确声明编号,避免把数字编号当成度量。 */
|
|
116
164
|
semanticRole?: "identifier" | "dimension" | "measure" | "date" | "text";
|
|
165
|
+
hidden?: boolean;
|
|
166
|
+
/** Condition hides cell content, preserving its physical layout in preview and exports. */
|
|
167
|
+
visibleWhen?: StyleRule["when"];
|
|
117
168
|
}
|
|
118
169
|
|
|
119
170
|
declare interface ListComponent {
|
|
@@ -148,15 +199,38 @@ declare interface ListComponent {
|
|
|
148
199
|
border?: boolean;
|
|
149
200
|
/** 分组小计(组头/组尾小计/组头防孤行) */
|
|
150
201
|
groupBy?: ListGroupBy;
|
|
202
|
+
/** Ordered outer-to-inner grouping; groupBy remains the single-level shorthand. */
|
|
203
|
+
groups?: ListGroupBy[];
|
|
204
|
+
computed?: ComputedField[];
|
|
205
|
+
/** Dynamic pivot of the current source dataset, with read-only derived identities. */
|
|
206
|
+
matrix?: MatrixTableOptions;
|
|
207
|
+
/** Explicit row visibility; hidden rows do not participate in summaries. */
|
|
208
|
+
rowVisibleWhen?: StyleRule["when"];
|
|
209
|
+
/** Master/detail relation is keyed by business identity, never by display row index. */
|
|
210
|
+
detail?: ListDetail;
|
|
151
211
|
/** 末尾总合计行(依赖 groupBy.subtotal 或显式 fields) */
|
|
152
212
|
grandTotal?: ListGrandTotal;
|
|
153
213
|
}
|
|
154
214
|
|
|
215
|
+
declare interface ListDetail {
|
|
216
|
+
dataset: string;
|
|
217
|
+
parentKey: string;
|
|
218
|
+
foreignKey: string;
|
|
219
|
+
rowKey?: string;
|
|
220
|
+
columns: ListColumn[];
|
|
221
|
+
rowHeightMm?: number;
|
|
222
|
+
header?: boolean;
|
|
223
|
+
label?: string;
|
|
224
|
+
/** Unmatched children fail by default; opt-in ignore is explicit. */
|
|
225
|
+
unmatched?: "error" | "ignore";
|
|
226
|
+
}
|
|
227
|
+
|
|
155
228
|
declare type ListGrandTotal = boolean | {
|
|
156
229
|
label?: string;
|
|
157
230
|
style?: ComponentStyle;
|
|
158
231
|
/** 合计列(缺省沿用 groupBy.subtotal) */
|
|
159
232
|
fields?: string[];
|
|
233
|
+
aggregates?: AggregateDefinition[];
|
|
160
234
|
};
|
|
161
235
|
|
|
162
236
|
/** 分组小计配置(声明式,见 ListComponent.groupBy) */
|
|
@@ -172,8 +246,42 @@ declare interface ListGroupBy {
|
|
|
172
246
|
subtotalLabel?: string;
|
|
173
247
|
/** 组头防孤行(默认 true:页尾放不下"组头+首行"时整体换页) */
|
|
174
248
|
keepWithNext?: boolean;
|
|
249
|
+
aggregates?: AggregateDefinition[];
|
|
250
|
+
/** Repeat this group heading after page breaks within the group. */
|
|
251
|
+
repeatHeader?: boolean;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
declare interface MatrixTableOptions {
|
|
255
|
+
rowFields: readonly string[];
|
|
256
|
+
columnFields: readonly string[];
|
|
257
|
+
measures: readonly (AggregateDefinition & {
|
|
258
|
+
label?: string;
|
|
259
|
+
})[];
|
|
260
|
+
totals?: boolean;
|
|
261
|
+
rowHeader?: string;
|
|
262
|
+
columnWidthMm?: number;
|
|
263
|
+
rowWidthMm?: number;
|
|
264
|
+
maxRows?: number;
|
|
265
|
+
maxColumns?: number;
|
|
266
|
+
maxInputRows?: number;
|
|
267
|
+
maxCells?: number;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
declare interface OutputDiagnostic {
|
|
271
|
+
code: "missing-image" | "unsupported-component" | "unsupported-image-format" | "unsupported-style" | "rich-text-flattened" | "nested-components-omitted" | "async-image-required" | "image-decode-failed" | "asset-render-failed" | "asset-budget-exceeded";
|
|
272
|
+
severity: "warning";
|
|
273
|
+
format: OutputFormat;
|
|
274
|
+
pageIndex: number;
|
|
275
|
+
componentIndex: number;
|
|
276
|
+
/** Nested grid child location as repeating [rowIndex, columnIndex, childIndex] triples. */
|
|
277
|
+
componentPath?: readonly number[];
|
|
278
|
+
kind: string;
|
|
279
|
+
nid?: string;
|
|
280
|
+
message: string;
|
|
175
281
|
}
|
|
176
282
|
|
|
283
|
+
declare type OutputFormat = "dom" | "canvas" | "pdf" | "xlsx";
|
|
284
|
+
|
|
177
285
|
declare interface PaginateOptions {
|
|
178
286
|
/** 每行额外占高(mm),压测专用钩子 */
|
|
179
287
|
mmPerRow?: number;
|
|
@@ -181,9 +289,19 @@ declare interface PaginateOptions {
|
|
|
181
289
|
measurer?: TextMeasurer;
|
|
182
290
|
/** 可选页数上限;在追加超限页前终止,避免先分配全部页面再检查。 */
|
|
183
291
|
maxPages?: number;
|
|
292
|
+
/** Actual grid cells, including repeated furniture and nested children; checked before each page is appended. */
|
|
293
|
+
maxCells?: number;
|
|
294
|
+
signal?: AbortSignal;
|
|
295
|
+
/** Streaming total-page anchors can skip the bounded counting pass when known. */
|
|
296
|
+
knownTotalPages?: number;
|
|
297
|
+
onWarning?: (warning: string) => void;
|
|
298
|
+
/** Trusted formula adapter is passed at execution time; it is never serialized in templates. */
|
|
299
|
+
formula?: FormulaAdapter;
|
|
184
300
|
}
|
|
185
301
|
|
|
186
302
|
declare interface PlanComponent {
|
|
303
|
+
/** Template page furniture repeats per page and reserves its vertical band. */
|
|
304
|
+
pageAnchor?: "header" | "footer";
|
|
187
305
|
kind: ComponentKind;
|
|
188
306
|
leftMm: number;
|
|
189
307
|
topMm: number;
|
|
@@ -333,6 +451,19 @@ declare interface ReportDiagnostic {
|
|
|
333
451
|
severity: "warning" | "error" | "info";
|
|
334
452
|
}
|
|
335
453
|
|
|
454
|
+
declare type ReportExpression = {
|
|
455
|
+
field: string;
|
|
456
|
+
} | {
|
|
457
|
+
value: string | number | boolean | null;
|
|
458
|
+
} | {
|
|
459
|
+
aggregate: AggregateOperation;
|
|
460
|
+
field?: string;
|
|
461
|
+
weightField?: string;
|
|
462
|
+
} | {
|
|
463
|
+
op: "add" | "subtract" | "multiply" | "divide" | "coalesce";
|
|
464
|
+
args: readonly ReportExpression[];
|
|
465
|
+
};
|
|
466
|
+
|
|
336
467
|
declare interface ReportFieldContract {
|
|
337
468
|
field: string;
|
|
338
469
|
label?: string;
|
|
@@ -348,6 +479,87 @@ declare interface ReportFieldContract {
|
|
|
348
479
|
unit?: string;
|
|
349
480
|
}
|
|
350
481
|
|
|
482
|
+
export declare class ReportJobError extends Error {
|
|
483
|
+
readonly code: "input" | "conflict" | "missing" | "lease" | "closed" | "capacity";
|
|
484
|
+
constructor(code: "input" | "conflict" | "missing" | "lease" | "closed" | "capacity", message: string);
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
export declare interface ReportJobExecutionContext {
|
|
488
|
+
scope: string;
|
|
489
|
+
taskId: string;
|
|
490
|
+
signal: AbortSignal;
|
|
491
|
+
reportProgress(completed: number, total?: number): Promise<void>;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
export declare type ReportJobHandler = (payload: ReportJobJson, context: ReportJobExecutionContext) => ReportJobJson | Promise<ReportJobJson>;
|
|
495
|
+
|
|
496
|
+
export declare type ReportJobJson = null | boolean | number | string | ReportJobJson[] | {
|
|
497
|
+
[key: string]: ReportJobJson;
|
|
498
|
+
};
|
|
499
|
+
|
|
500
|
+
export declare interface ReportJobRecord {
|
|
501
|
+
scope: string;
|
|
502
|
+
id: string;
|
|
503
|
+
revision: number;
|
|
504
|
+
request: ReportJobRequest;
|
|
505
|
+
status: ReportJobStatus;
|
|
506
|
+
createdAt: number;
|
|
507
|
+
updatedAt: number;
|
|
508
|
+
attempt: number;
|
|
509
|
+
lease?: {
|
|
510
|
+
owner: string;
|
|
511
|
+
expiresAt: number;
|
|
512
|
+
};
|
|
513
|
+
cancelRequested?: boolean;
|
|
514
|
+
progress?: {
|
|
515
|
+
completed: number;
|
|
516
|
+
total?: number;
|
|
517
|
+
};
|
|
518
|
+
result?: ReportJobJson;
|
|
519
|
+
error?: {
|
|
520
|
+
code: string;
|
|
521
|
+
message: string;
|
|
522
|
+
};
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
export declare interface ReportJobRequest {
|
|
526
|
+
kind: string;
|
|
527
|
+
payload: ReportJobJson;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
export declare interface ReportJobService {
|
|
531
|
+
submit(input: ReportJobRequest & {
|
|
532
|
+
scope: string;
|
|
533
|
+
id: string;
|
|
534
|
+
}): Promise<ReportJobRecord>;
|
|
535
|
+
get(scope: string, id: string): Promise<ReportJobRecord | undefined>;
|
|
536
|
+
cancel(scope: string, id: string): Promise<ReportJobRecord>;
|
|
537
|
+
retry(scope: string, id: string, expectedRevision: number): Promise<ReportJobRecord>;
|
|
538
|
+
/** Dispatch one queued/expired job. A scheduler controls when to call this method. */
|
|
539
|
+
runNext(workerId: string): Promise<ReportJobRecord | undefined>;
|
|
540
|
+
/** Stops new dispatch, signals active handlers and waits until their actual completion. */
|
|
541
|
+
close(): Promise<void>;
|
|
542
|
+
readonly active: number;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
export declare interface ReportJobServiceOptions {
|
|
546
|
+
store: ReportJobStore;
|
|
547
|
+
handlers: Readonly<Record<string, ReportJobHandler>>;
|
|
548
|
+
concurrency?: number;
|
|
549
|
+
leaseMs?: number;
|
|
550
|
+
heartbeatMs?: number;
|
|
551
|
+
now?: () => number;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
export declare type ReportJobStatus = "queued" | "running" | "completed" | "failed" | "cancelled";
|
|
555
|
+
|
|
556
|
+
/** CAS must be atomic across all worker instances; candidate enumeration is advisory only. */
|
|
557
|
+
export declare interface ReportJobStore {
|
|
558
|
+
read(scope: string, id: string): Promise<ReportJobRecord | undefined>;
|
|
559
|
+
candidates(now: number, limit: number): Promise<ReportJobRecord[]>;
|
|
560
|
+
compareAndSwap(scope: string, id: string, expectedRevision: number | null, next: ReportJobRecord): Promise<boolean>;
|
|
561
|
+
}
|
|
562
|
+
|
|
351
563
|
/** 取数与排版共同使用的请求上下文;取消只影响本次调用。 */
|
|
352
564
|
declare interface ReportLoadContext {
|
|
353
565
|
reportId: string;
|
|
@@ -414,6 +626,31 @@ declare interface ReportProviderExportResult {
|
|
|
414
626
|
diagnostics?: ReportDiagnostic[];
|
|
415
627
|
}
|
|
416
628
|
|
|
629
|
+
export declare interface ReportPublicationArtifact {
|
|
630
|
+
fixture: string;
|
|
631
|
+
format: "pdf" | "xlsx";
|
|
632
|
+
bytes: number;
|
|
633
|
+
pages: number;
|
|
634
|
+
/** Optional host digest. No Node or crypto dependency is pulled into /server. */
|
|
635
|
+
digest?: string;
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
export declare interface ReportPublicationOptions extends ReportTemplateReviewOptions {
|
|
639
|
+
formats?: readonly ("pdf" | "xlsx")[];
|
|
640
|
+
signal?: AbortSignal;
|
|
641
|
+
pdf?: ReportRuntimeExportInput["pdf"];
|
|
642
|
+
xlsx?: ReportRuntimeExportInput["xlsx"];
|
|
643
|
+
maxOutputBytes?: number;
|
|
644
|
+
maxDurationMs?: number;
|
|
645
|
+
/** Explicit fixtures only. Host may archive these bytes; the result retains metadata only. */
|
|
646
|
+
onArtifact?: (fixture: string, result: ReportRuntimeExportResult) => void | Promise<void>;
|
|
647
|
+
digest?: (bytes: Uint8Array) => string | Promise<string>;
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
export declare interface ReportPublicationReview extends ReportTemplateReview {
|
|
651
|
+
artifacts: ReportPublicationArtifact[];
|
|
652
|
+
}
|
|
653
|
+
|
|
417
654
|
declare interface ReportQueryResult {
|
|
418
655
|
datasets: DatasetRows;
|
|
419
656
|
revision?: string;
|
|
@@ -580,6 +817,22 @@ declare interface ReportTemplate {
|
|
|
580
817
|
pages: TemplatePage[];
|
|
581
818
|
}
|
|
582
819
|
|
|
820
|
+
declare interface ReportTemplateChange {
|
|
821
|
+
kind: "added" | "removed" | "modified" | "moved";
|
|
822
|
+
category: "metadata" | "structure" | "binding" | "calculation" | "format";
|
|
823
|
+
path: string;
|
|
824
|
+
before?: string;
|
|
825
|
+
after?: string;
|
|
826
|
+
breaking: boolean;
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
declare interface ReportTemplateDiff {
|
|
830
|
+
changes: ReportTemplateChange[];
|
|
831
|
+
totalChanges: number;
|
|
832
|
+
truncated: boolean;
|
|
833
|
+
breakingChanges: number;
|
|
834
|
+
}
|
|
835
|
+
|
|
583
836
|
/** 可存入数据库/文件的纯 JSON 模板;数据、接口与鉴权仍由宿主提供。 */
|
|
584
837
|
declare interface ReportTemplateDocument {
|
|
585
838
|
schemaVersion: "1.0";
|
|
@@ -591,6 +844,12 @@ declare interface ReportTemplateDocument {
|
|
|
591
844
|
template: ReportTemplate;
|
|
592
845
|
}
|
|
593
846
|
|
|
847
|
+
declare interface ReportTemplateFixture {
|
|
848
|
+
name: string;
|
|
849
|
+
datasets: DatasetRows;
|
|
850
|
+
params?: Record<string, string>;
|
|
851
|
+
}
|
|
852
|
+
|
|
594
853
|
declare type ReportTemplateLoader = (context: ReportLoadContext) => ReportTemplate | Promise<ReportTemplate>;
|
|
595
854
|
|
|
596
855
|
declare interface ReportTemplateProvider {
|
|
@@ -602,8 +861,46 @@ declare interface ReportTemplateProvider {
|
|
|
602
861
|
}): Promise<ReportTemplateDocument | undefined>;
|
|
603
862
|
}
|
|
604
863
|
|
|
864
|
+
declare interface ReportTemplateReview {
|
|
865
|
+
ok: boolean;
|
|
866
|
+
changes?: ReportTemplateDiff;
|
|
867
|
+
issues: ReportTemplateReviewIssue[];
|
|
868
|
+
samples: Array<{
|
|
869
|
+
name: string;
|
|
870
|
+
pages: number;
|
|
871
|
+
rows: number;
|
|
872
|
+
diagnostics: OutputDiagnostic[];
|
|
873
|
+
}>;
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
declare interface ReportTemplateReviewIssue {
|
|
877
|
+
code: "template" | "compatibility" | "dataset" | "field" | "sample" | "layout" | "output";
|
|
878
|
+
severity: "error" | "warning";
|
|
879
|
+
path: string;
|
|
880
|
+
message: string;
|
|
881
|
+
fixture?: string;
|
|
882
|
+
pageIndex?: number;
|
|
883
|
+
componentIndex?: number;
|
|
884
|
+
field?: string;
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
declare interface ReportTemplateReviewOptions {
|
|
888
|
+
previous?: ReportTemplateDocument;
|
|
889
|
+
dataSchemas?: Readonly<Record<string, DatasetContract>>;
|
|
890
|
+
fixtures?: readonly ReportTemplateFixture[];
|
|
891
|
+
formats?: readonly OutputFormat[];
|
|
892
|
+
maxPages?: number;
|
|
893
|
+
maxCells?: number;
|
|
894
|
+
maxSampleRows?: number;
|
|
895
|
+
allowBreakingChanges?: boolean;
|
|
896
|
+
strictOutputs?: boolean;
|
|
897
|
+
}
|
|
898
|
+
|
|
605
899
|
declare type ReportTemplateSummary = Omit<ReportTemplateDocument, "template">;
|
|
606
900
|
|
|
901
|
+
/** Publication gate with actual sequential PDF/XLSX encoding, no business query or implicit save. */
|
|
902
|
+
export declare function reviewReportPublication(input: unknown, options?: ReportPublicationOptions): Promise<ReportPublicationReview>;
|
|
903
|
+
|
|
607
904
|
declare interface RichParagraph {
|
|
608
905
|
segments: RichSegment[];
|
|
609
906
|
align?: ComponentStyle["align"];
|
|
@@ -620,12 +917,22 @@ declare interface RichSegment {
|
|
|
620
917
|
style?: ComponentStyle;
|
|
621
918
|
}
|
|
622
919
|
|
|
920
|
+
export declare function runnableJob(record: ReportJobRecord, now: number): boolean;
|
|
921
|
+
|
|
922
|
+
/** Canonical plain JSON rejects getters/scripts and gives stable idempotency without coercion. */
|
|
923
|
+
export declare function serializeJobJson(value: unknown, budget?: {
|
|
924
|
+
maxNodes?: number;
|
|
925
|
+
maxCharacters?: number;
|
|
926
|
+
}): string;
|
|
927
|
+
|
|
623
928
|
/** 条件格式规则求值(声明式,见 ListColumn.rules) */
|
|
624
929
|
declare interface StyleRule {
|
|
625
930
|
when: {
|
|
626
931
|
field: string;
|
|
627
932
|
op: ">" | ">=" | "<" | "<=" | "==" | "!=";
|
|
628
933
|
value: number | string;
|
|
934
|
+
/** Explicit decimal comparisons retain decimal thresholds as text. */
|
|
935
|
+
numeric?: "number" | "decimal";
|
|
629
936
|
};
|
|
630
937
|
/** 命中时合并进单元格样式(color/bold/backgroundColor 等) */
|
|
631
938
|
style: Record<string, unknown>;
|
|
@@ -665,4 +972,8 @@ declare interface TextMeasurer {
|
|
|
665
972
|
measureMm(text: string, options: TextMeasureOptions): number;
|
|
666
973
|
}
|
|
667
974
|
|
|
975
|
+
export declare function validateJobRecord(record: ReportJobRecord, scope: string, id: string, expectedRevision: number | null): void;
|
|
976
|
+
|
|
977
|
+
export declare function validateJobTransition(previous: ReportJobRecord | undefined, next: ReportJobRecord): void;
|
|
978
|
+
|
|
668
979
|
export { }
|