@agile-team/mach-report 0.5.0 → 0.6.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.
Files changed (71) hide show
  1. package/dist/ReportPreview-DWLjYi_p.js +527 -0
  2. package/dist/ReportPreview-rBtmbu3W.cjs +6 -0
  3. package/dist/_commonjsHelpers-DKOUU3wS.cjs +1 -0
  4. package/dist/_commonjsHelpers-DaMA6jEr.js +8 -0
  5. package/dist/builder/template-builder.d.ts +53 -0
  6. package/dist/compat/jh4j-template.d.ts +12 -0
  7. package/dist/defaults.d.ts +27 -0
  8. package/dist/index.cjs +6 -1663
  9. package/dist/index.d.cts +13 -344
  10. package/dist/index.d.ts +13 -344
  11. package/dist/index.js +727 -1419
  12. package/dist/layout/paginate.d.ts +51 -0
  13. package/dist/layout/textwrap.d.ts +36 -0
  14. package/dist/manager/api.d.ts +45 -0
  15. package/dist/manager/index.d.ts +2 -0
  16. package/dist/manager/types.d.ts +50 -0
  17. package/dist/manager.cjs +1 -122
  18. package/dist/manager.d.cts +5 -97
  19. package/dist/manager.d.ts +5 -97
  20. package/dist/manager.js +66 -81
  21. package/dist/pdf/font-loader.d.ts +5 -0
  22. package/dist/pdf/index.d.ts +2 -0
  23. package/dist/pdf/render-pdf.d.ts +25 -0
  24. package/dist/pdf.cjs +84 -52530
  25. package/dist/pdf.d.cts +4 -31
  26. package/dist/pdf.d.ts +4 -31
  27. package/dist/pdf.js +22843 -39218
  28. package/dist/plugin-BIJxGHZX.cjs +1 -0
  29. package/dist/plugin-DrV67_VW.js +89 -0
  30. package/dist/render/canvas.d.ts +61 -0
  31. package/dist/render/dom.d.ts +10 -0
  32. package/dist/render/grid-geometry.d.ts +41 -0
  33. package/dist/render/style.d.ts +33 -0
  34. package/dist/render/window.d.ts +23 -0
  35. package/dist/schema/render-plan.d.ts +96 -0
  36. package/dist/schema/validate.d.ts +15 -0
  37. package/dist/sql/ast-check.d.ts +14 -0
  38. package/dist/sql/compile.d.ts +31 -0
  39. package/dist/sql/expr.d.ts +21 -0
  40. package/dist/sql/index.d.ts +4 -0
  41. package/dist/sql/tokenizer.d.ts +25 -0
  42. package/dist/sql.cjs +68 -45207
  43. package/dist/sql.d.cts +3 -93
  44. package/dist/sql.d.ts +3 -93
  45. package/dist/sql.js +37213 -41613
  46. package/dist/style-B3vxGDZd.cjs +3 -0
  47. package/dist/style-B7Xfo5I-.js +255 -0
  48. package/dist/style.css +1 -0
  49. package/dist/units.d.ts +11 -0
  50. package/dist/vue/ReportPreview.d.ts +141 -0
  51. package/dist/vue/ReportToolbar.d.ts +25 -0
  52. package/dist/vue/adapters.d.ts +37 -0
  53. package/dist/vue/async.d.cts +146 -0
  54. package/dist/vue/async.d.ts +146 -0
  55. package/dist/vue/composables/usePageWindow.d.ts +28 -0
  56. package/dist/vue/composables/usePrintExport.d.ts +19 -0
  57. package/dist/vue/composables/useZoom.d.ts +15 -0
  58. package/dist/vue/config.d.ts +79 -0
  59. package/dist/vue/index.d.cts +11 -0
  60. package/dist/vue/index.d.ts +11 -0
  61. package/dist/vue/injection-keys.d.ts +12 -0
  62. package/dist/vue/local-adapter.d.ts +12 -0
  63. package/dist/vue/pdf-exporter.d.ts +13 -0
  64. package/dist/vue/plugin.d.ts +34 -0
  65. package/dist/vue-async.cjs +1 -0
  66. package/dist/vue-async.js +17 -0
  67. package/dist/vue.cjs +1 -0
  68. package/dist/vue.js +44 -0
  69. package/package.json +44 -6
  70. package/dist/textwrap-DWzM7pt1.d.cts +0 -135
  71. package/dist/textwrap-DWzM7pt1.d.ts +0 -135
@@ -0,0 +1 @@
1
+ "use strict";var g=Object.create;var d=Object.defineProperty;var T=Object.getOwnPropertyDescriptor;var F=Object.getOwnPropertyNames;var U=Object.getPrototypeOf,w=Object.prototype.hasOwnProperty;var y=(r,e,n,o)=>{if(e&&typeof e=="object"||typeof e=="function")for(let t of F(e))!w.call(r,t)&&t!==n&&d(r,t,{get:()=>e[t],enumerable:!(o=T(e,t))||o.enumerable});return r};var j=(r,e,n)=>(n=r!=null?g(U(r)):{},y(e||!r||!r.__esModule?d(n,"default",{value:r,enumerable:!0}):n,r));const C=require("vue");function h(r){return r==null?[]:Array.isArray(r)?r.map(String).filter(e=>e!==""):String(r).split(",").map(e=>e.trim()).filter(e=>e!=="")}function O(r){return h(r).join(",")}class l extends Error{constructor(e){super(e),this.name="PlanLoadError"}}function E(r={}){const{baseUrl:e="",headers:n,credentials:o}=r;return async({url:t,method:f,params:a})=>{const c=a&&Object.keys(a).length>0?`?${new URLSearchParams(a)}`:"";let s;try{s=await fetch(`${e}${t}${c}`,{method:f.toUpperCase(),headers:{Accept:"application/json",...n},credentials:o})}catch(i){throw new l(`请求失败(${t}): ${i instanceof Error?i.message:String(i)}`)}if(!s.ok)throw new l(`HTTP ${s.status} ${t}`);return s.json()}}function P(r){const{request:e,baseUrl:n=""}=r;return async({tempIds:o,furnitureTempId:t,params:f})=>{if(o.length===0)throw new l("缺少报表模板 ID");const a={tempId:o.join(",")};t&&(a.furnitureTempId=t);for(const[i,u]of Object.entries(f||{}))i&&u!=null&&u!==""&&(a[i]=String(u));const c=await e({url:`${n}/report/codePrintReport/gridPlan`,method:"get",params:a});if(c&&c.code!=null&&c.code!==200)throw new l(String(c.message||"预览加载失败"));const s=c?.data?.pages;return{schemaVersion:"jh4j-compatible",pages:Array.isArray(s)?s:[]}}}function R(r={fontUrl:"/simhei.ttf"}){let e=null;return async n=>{e||(e=await import("@agile-team/mach-report/pdf"));const o=r.fontUrl?await e.loadFontWithCache(r.fontUrl):null,{bytes:t}=await e.renderPlanToPdf(n,o?{customFontBytes:o}:{});return t}}const m="mach-report/fetcher",p="mach-report/pdf-exporter",_="mach-report/config",b={install(r,e={}){const n=e.fetcher??P({request:e.request??E({baseUrl:e.baseUrl}),baseUrl:e.baseUrl});r.provide(m,n);const o=e.pdfFontUrl??e.config?.defaults?.pdfFontUrl??"/simhei.ttf";r.provide(p,e.pdfExporter??R({fontUrl:o})),r.provide(_,C.computed(()=>e.config??null))}};exports.MACH_REPORT_CONFIG_KEY=_;exports.MACH_REPORT_FETCHER_KEY=m;exports.MACH_REPORT_PDF_EXPORTER_KEY=p;exports.PlanLoadError=l;exports.createDefaultPdfExporter=R;exports.createFetchRequest=E;exports.createJh4jGridPlanFetcher=P;exports.joinTempIds=O;exports.machReportPlugin=b;exports.normalizeTempIds=h;
@@ -0,0 +1,89 @@
1
+ import { computed as d } from "vue";
2
+ function h(e) {
3
+ return e == null ? [] : Array.isArray(e) ? e.map(String).filter((r) => r !== "") : String(e).split(",").map((r) => r.trim()).filter((r) => r !== "");
4
+ }
5
+ function y(e) {
6
+ return h(e).join(",");
7
+ }
8
+ class l extends Error {
9
+ constructor(r) {
10
+ super(r), this.name = "PlanLoadError";
11
+ }
12
+ }
13
+ function m(e = {}) {
14
+ const { baseUrl: r = "", headers: a, credentials: t } = e;
15
+ return async ({ url: n, method: f, params: o }) => {
16
+ const s = o && Object.keys(o).length > 0 ? `?${new URLSearchParams(o)}` : "";
17
+ let c;
18
+ try {
19
+ c = await fetch(`${r}${n}${s}`, {
20
+ method: f.toUpperCase(),
21
+ headers: { Accept: "application/json", ...a },
22
+ credentials: t
23
+ });
24
+ } catch (i) {
25
+ throw new l(
26
+ `请求失败(${n}): ${i instanceof Error ? i.message : String(i)}`
27
+ );
28
+ }
29
+ if (!c.ok)
30
+ throw new l(`HTTP ${c.status} ${n}`);
31
+ return c.json();
32
+ };
33
+ }
34
+ function p(e) {
35
+ const { request: r, baseUrl: a = "" } = e;
36
+ return async ({ tempIds: t, furnitureTempId: n, params: f }) => {
37
+ if (t.length === 0) throw new l("缺少报表模板 ID");
38
+ const o = { tempId: t.join(",") };
39
+ n && (o.furnitureTempId = n);
40
+ for (const [i, u] of Object.entries(f || {}))
41
+ i && u != null && u !== "" && (o[i] = String(u));
42
+ const s = await r({
43
+ url: `${a}/report/codePrintReport/gridPlan`,
44
+ method: "get",
45
+ params: o
46
+ });
47
+ if (s && s.code != null && s.code !== 200)
48
+ throw new l(String(s.message || "预览加载失败"));
49
+ const c = s?.data?.pages;
50
+ return {
51
+ schemaVersion: "jh4j-compatible",
52
+ pages: Array.isArray(c) ? c : []
53
+ };
54
+ };
55
+ }
56
+ function g(e = { fontUrl: "/simhei.ttf" }) {
57
+ let r = null;
58
+ return async (a) => {
59
+ r || (r = await import("@agile-team/mach-report/pdf"));
60
+ const t = e.fontUrl ? await r.loadFontWithCache(e.fontUrl) : null, { bytes: n } = await r.renderPlanToPdf(a, t ? { customFontBytes: t } : {});
61
+ return n;
62
+ };
63
+ }
64
+ const P = "mach-report/fetcher", E = "mach-report/pdf-exporter", U = "mach-report/config", R = {
65
+ install(e, r = {}) {
66
+ const a = r.fetcher ?? p({
67
+ request: r.request ?? m({ baseUrl: r.baseUrl }),
68
+ baseUrl: r.baseUrl
69
+ });
70
+ e.provide(P, a);
71
+ const t = r.pdfFontUrl ?? r.config?.defaults?.pdfFontUrl ?? "/simhei.ttf";
72
+ e.provide(
73
+ E,
74
+ r.pdfExporter ?? g({ fontUrl: t })
75
+ ), e.provide(U, d(() => r.config ?? null));
76
+ }
77
+ };
78
+ export {
79
+ U as M,
80
+ l as P,
81
+ P as a,
82
+ E as b,
83
+ g as c,
84
+ m as d,
85
+ p as e,
86
+ y as j,
87
+ R as m,
88
+ h as n
89
+ };
@@ -0,0 +1,61 @@
1
+ import { PlanPage, RenderPlan } from '../schema/render-plan';
2
+ import { TextMeasurer } from '../layout/textwrap';
3
+ export interface CanvasRenderOptions {
4
+ /** 设备像素比,默认取运行环境 devicePixelRatio */
5
+ dpr?: number;
6
+ dpi?: number;
7
+ /** 文本默认字体族 */
8
+ fontFamily?: string;
9
+ /** 文本测量器(默认启发式) */
10
+ measurer?: TextMeasurer;
11
+ /** 只渲染页区间(闭区间索引;缺省全渲染——仅小报表使用,大报表请用 createCanvasPager) */
12
+ start?: number;
13
+ end?: number;
14
+ }
15
+ export declare function measurePageSizePx(page: Pick<PlanPage, "pageWidthMm" | "pageHeightMm">, dpi?: number): {
16
+ widthPx: number;
17
+ heightPx: number;
18
+ };
19
+ /** pt→px 换算(与 DOM 后端 units.ptToPx 同一真相源),供单测直接断言 */
20
+ export declare function planFontSizePx(style: {
21
+ fontSizePx?: number;
22
+ fontSize?: number;
23
+ } | undefined, dpi?: number): number;
24
+ export interface CanvasRenderResult {
25
+ canvases: HTMLCanvasElement[];
26
+ }
27
+ /**
28
+ * RenderPlan → Canvas 位图页(顶点原点坐标系,与 DOM/PDF 一致)。
29
+ *
30
+ * ⚠️ 内存模型:A4 @dpr2 ≈ 14MB/页,全量渲染 100 页 ≈ 1.4GB。
31
+ * 大报表必须使用 createCanvasPager(窗口化 + 画布池 + 分帧渲染),
32
+ * 本函数适合页数少或已指定 start/end 区间的场景。
33
+ */
34
+ export declare function renderPlanToCanvas(plan: RenderPlan, options?: CanvasRenderOptions): CanvasRenderResult;
35
+ export interface CanvasPagerOptions extends CanvasRenderOptions {
36
+ /** 预渲染页数缓冲(窗口两侧各加 N 页),默认 1 */
37
+ overscan?: number;
38
+ /** 页间距 px,默认 18 */
39
+ gapPx?: number;
40
+ /** 每帧渲染页数上限(分帧,防长任务),默认 1 */
41
+ pagesPerFrame?: number;
42
+ }
43
+ export interface CanvasPager {
44
+ /** 挂载到滚动容器(viewport,overflow:auto),接管其内容 */
45
+ attach(viewport: HTMLElement): void;
46
+ destroy(): void;
47
+ /** 当前实际渲染的页区间 */
48
+ window(): {
49
+ start: number;
50
+ end: number;
51
+ };
52
+ }
53
+ /**
54
+ * Canvas 虚拟化分页器(大报表专用):
55
+ * - 视口窗口化:只绘制窗口内页(computePageWindow 与 DOM 后端共用)
56
+ * - 画布池:固定数量 canvas 复用重绘,滚动零新增位图
57
+ * - 分帧:每帧最多 pagesPerFrame 页,滚动期间不产生长任务
58
+ *
59
+ * 内存从 O(总页数) 降到 O(窗口+2×overscan)。
60
+ */
61
+ export declare function createCanvasPager(plan: RenderPlan, options?: CanvasPagerOptions): CanvasPager;
@@ -0,0 +1,10 @@
1
+ import { PlanComponent, PlanPage, RenderPlan } from '../schema/render-plan';
2
+ export interface RenderOptions {
3
+ dpi?: number;
4
+ classNames?: Partial<Record<ClassKey, string>>;
5
+ }
6
+ export type ClassKey = "root" | "page" | "comp" | "text" | "lines" | "line" | "rich" | "paragraph" | "segment" | "image" | "shape" | "grid" | "row" | "cell" | "unknown";
7
+ export declare function classOf(key: ClassKey, options?: RenderOptions): string;
8
+ export declare function renderComponent(comp: PlanComponent, dpi: number, options: RenderOptions, doc: Document): HTMLElement;
9
+ export declare function renderPage(page: PlanPage, dpi: number, options: RenderOptions, doc: Document): HTMLElement;
10
+ export declare function renderPlan(plan: RenderPlan, doc: Document, options?: RenderOptions): HTMLElement;
@@ -0,0 +1,41 @@
1
+ import { GridCell, PlanGrid } from '../schema/render-plan';
2
+ export interface GridCellBox {
3
+ ri: number;
4
+ ci: number;
5
+ cell: GridCell;
6
+ /** 相对组件左上角(mm,顶点原点) */
7
+ xMm: number;
8
+ yMm: number;
9
+ widthMm: number;
10
+ heightMm: number;
11
+ }
12
+ /** 网格线段:kind="h" 为水平线(pos=y,from/to=x 区间);"v" 为垂直线(pos=x,from/to=y 区间) */
13
+ export interface GridEdge {
14
+ kind: "h" | "v";
15
+ pos: number;
16
+ from: number;
17
+ to: number;
18
+ }
19
+ export interface GridLayout {
20
+ columns: {
21
+ pos: number;
22
+ size: number;
23
+ }[];
24
+ rows: {
25
+ pos: number;
26
+ size: number;
27
+ }[];
28
+ cells: GridCellBox[];
29
+ /** 内部网格线段(不含外框;已跳过被 colSpan/rowSpan 跨越的边界,相邻段已合并) */
30
+ edges: GridEdge[];
31
+ }
32
+ /**
33
+ * 网格几何统一解析(DOM/Canvas/PDF 三后端共用,单位真相源):
34
+ * - 列宽:优先 colWidthsMm(等比缩放至组件宽),否则按列数均分
35
+ * - 行高:优先 rowHeightsMm(等比缩放至组件高),否则按行数均分
36
+ * - colSpan/rowSpan:占位矩阵展开,跨度取起止行列差;越界收敛到网格内
37
+ * - 行内 null/undefined 单元格视为占 1 列的空单元格(与 DOM 表格行为一致)
38
+ * - edges:内部网格线段,跳过被 span 跨越的边界(合并单元格中间无线)
39
+ * 产出为组件左上角顶点原点的 mm 坐标,各后端自行换算 px/pt。
40
+ */
41
+ export declare function resolveGridLayout(grid: PlanGrid, compWidthMm: number, compHeightMm: number): GridLayout;
@@ -0,0 +1,33 @@
1
+ import { ComponentStyle } from '../schema/render-plan';
2
+ /** 解析后的字号(px 值已按 dpi 换算;pt 值供 PDF 使用) */
3
+ export interface ResolvedFontSize {
4
+ fontSizePt: number;
5
+ fontSizePx: number;
6
+ }
7
+ /** 字号解析:fontSizePx 直通;否则 pt→px(与 units.ptToPx 同源) */
8
+ export declare function resolveFontSize(style: Pick<ComponentStyle, "fontSizePx" | "fontSize"> | undefined, dpi?: number, defaultPt?: number): ResolvedFontSize;
9
+ /** 描边解析:颜色 + 线宽(pt) */
10
+ export declare function resolveStroke(style: Pick<ComponentStyle, "borderColor" | "lineColor" | "lineWidthPt"> | undefined, fallbackColor?: string): {
11
+ color: string;
12
+ widthPt: number;
13
+ };
14
+ /** 文本颜色解析 */
15
+ export declare function resolveTextColor(style: Pick<ComponentStyle, "color"> | undefined, fallback?: string): string;
16
+ /** 行高解析(倍数,基线距 = 字号 × lineHeight) */
17
+ export declare function resolveLineHeight(style: Pick<ComponentStyle, "lineHeight"> | undefined): number;
18
+ /** 盒子边框语义:四边开关 + 内部网格线(四边全关则内线不画) */
19
+ export interface BoxBorders {
20
+ top: boolean;
21
+ right: boolean;
22
+ bottom: boolean;
23
+ left: boolean;
24
+ /** 网格内线(仅当任意外边开启) */
25
+ inner: boolean;
26
+ }
27
+ /**
28
+ * 边框开关解析:
29
+ * - 未声明(undefined)→ 四边全开(与既有 rect 总有边框的行为兼容)
30
+ * - 显式 false → 对应边关闭
31
+ * - 四边全关(如 list border:false)→ 内线也不画
32
+ */
33
+ export declare function resolveBoxBorders(style: Pick<ComponentStyle, "borderTop" | "borderRight" | "borderBottom" | "borderLeft"> | undefined): BoxBorders;
@@ -0,0 +1,23 @@
1
+ export interface PageWindowInput {
2
+ pageHeightsPx: number[];
3
+ viewportHeightPx: number;
4
+ scrollTopPx: number;
5
+ overscan?: number;
6
+ /** 页间距(px)。占位高度按“每页高度+间距”累计,与 margin-bottom 布局一致;默认 0 */
7
+ gapPx?: number;
8
+ }
9
+ export interface PageWindow {
10
+ start: number;
11
+ end: number;
12
+ padTopPx: number;
13
+ padBottomPx: number;
14
+ }
15
+ /**
16
+ * 页级视口虚拟化:给定每页高度与滚动位置,返回需要真实渲染的页区间
17
+ * 与上下占位高度。start/end 为闭区间索引。
18
+ *
19
+ * 布局模型:每页占据 height + gap(gap 为页间距,默认 0),
20
+ * padTop/padBottom 同口径累计,保证 占位+渲染页+间距 还原总滚动高度。
21
+ * 内部用前缀和 + 二分定位覆盖页与可见终点。
22
+ */
23
+ export declare function computePageWindow(input: PageWindowInput): PageWindow;
@@ -0,0 +1,96 @@
1
+ export type ComponentKind = "text" | "image" | "barcode" | "qrcode" | "chart" | "line" | "rect" | "ellipse";
2
+ export interface ComponentStyle {
3
+ fontSizePx?: number;
4
+ fontSize?: number;
5
+ fontFamily?: string;
6
+ bold?: boolean;
7
+ italic?: boolean;
8
+ underline?: boolean;
9
+ color?: string;
10
+ backgroundColor?: string;
11
+ align?: "left" | "center" | "right" | "justify";
12
+ verticalAlign?: "top" | "middle" | "bottom";
13
+ lineColor?: string;
14
+ borderColor?: string;
15
+ lineWidthPt?: number;
16
+ borderTop?: boolean;
17
+ borderRight?: boolean;
18
+ borderBottom?: boolean;
19
+ borderLeft?: boolean;
20
+ lineHeight?: number;
21
+ letterSpacing?: number;
22
+ opacity?: number;
23
+ rotateDeg?: number;
24
+ }
25
+ export interface RichParagraph {
26
+ segments: RichSegment[];
27
+ align?: ComponentStyle["align"];
28
+ lineHeight?: number;
29
+ spaceBeforeMm?: number;
30
+ spaceAfterMm?: number;
31
+ indentMm?: number;
32
+ }
33
+ export interface RichSegment {
34
+ kind?: "text" | "field";
35
+ text?: string;
36
+ field?: string;
37
+ style?: ComponentStyle;
38
+ }
39
+ export interface GridCell {
40
+ text?: string;
41
+ rawText?: string;
42
+ children?: PlanComponent[];
43
+ colSpan?: number;
44
+ rowSpan?: number;
45
+ style?: ComponentStyle;
46
+ }
47
+ export interface PlanGrid {
48
+ /** 行内允许 null 空位(jh4j 契约数据存在稀疏行;渲染器按空单元格处理) */
49
+ cells: (GridCell | null)[][];
50
+ colWidthsMm?: number[];
51
+ rowHeightsMm?: number[];
52
+ gapMm?: number;
53
+ }
54
+ export interface PlanComponent {
55
+ kind: ComponentKind;
56
+ leftMm: number;
57
+ topMm: number;
58
+ widthMm: number;
59
+ heightMm: number;
60
+ nid?: string;
61
+ rkey?: string;
62
+ text?: string;
63
+ rawText?: string;
64
+ lines?: string[];
65
+ richParagraphs?: RichParagraph[];
66
+ imageData?: string;
67
+ grid?: PlanGrid;
68
+ style?: ComponentStyle;
69
+ meta?: Record<string, unknown>;
70
+ /** MachReport 扩展字段:未知 kind 的兜底渲染标记 */
71
+ fallbackText?: string;
72
+ }
73
+ export interface PlanPage {
74
+ pageWidthMm: number;
75
+ pageHeightMm: number;
76
+ marginTopMm?: number;
77
+ marginBottomMm?: number;
78
+ marginLeftMm?: number;
79
+ marginRightMm?: number;
80
+ components: PlanComponent[];
81
+ }
82
+ export interface GridPlanResponse {
83
+ code?: number;
84
+ message?: string;
85
+ data?: {
86
+ pages?: PlanPage[];
87
+ } | null;
88
+ }
89
+ export interface RenderPlan {
90
+ schemaVersion: string;
91
+ pages: PlanPage[];
92
+ }
93
+ export declare const RENDER_PLAN_SCHEMA_VERSION = "1.0.0-mach";
94
+ export declare function toRenderPlan(response: GridPlanResponse): RenderPlan;
95
+ export declare function isImageBackedComponent(comp: PlanComponent): boolean;
96
+ export declare function isRichText(comp: PlanComponent): boolean;
@@ -0,0 +1,15 @@
1
+ import { PlanComponent, PlanPage } from './render-plan';
2
+ export interface ValidationError {
3
+ path: string;
4
+ message: string;
5
+ }
6
+ export interface ValidationResult {
7
+ ok: boolean;
8
+ errors: ValidationError[];
9
+ warnings: ValidationError[];
10
+ }
11
+ export declare function validateComponent(comp: unknown, path: string, errors: ValidationError[], warnings?: ValidationError[]): void;
12
+ export declare function validatePage(page: unknown, path: string, errors: ValidationError[], warnings?: ValidationError[]): void;
13
+ export declare function validateRenderPlan(input: unknown): ValidationResult;
14
+ export declare function assertRenderPlan(input: unknown): PlanPage[];
15
+ export type { PlanComponent, PlanPage };
@@ -0,0 +1,14 @@
1
+ export interface AstCheckResult {
2
+ ok: boolean;
3
+ /** ast 深度校验是否执行(方言不支持时降级词法) */
4
+ degraded: boolean;
5
+ message?: string;
6
+ }
7
+ /**
8
+ * AST 深度校验(双保险策略):
9
+ * 1. node-sql-parser(MySQL 方言,支持 ? 占位符)AST 顶层必须为 SELECT/集合运算
10
+ * 2. AST 通过后再叠加词法校验(MySQL 方言把 SELECT INTO 解析为别名,AST 层不可见,
11
+ * 由词法层兜底拦截)
12
+ * AST 解析失败(方言不兼容)自动降级为纯词法校验,返回 degraded=true。
13
+ */
14
+ export declare function assertSingleSelectAst(sql: string): AstCheckResult;
@@ -0,0 +1,31 @@
1
+ export { SqlSyntaxError } from './tokenizer';
2
+ export interface RenderedSql {
3
+ sql: string;
4
+ binds: unknown[];
5
+ warnings: string[];
6
+ }
7
+ export interface CompiledSql {
8
+ original: string;
9
+ /** 参数名集合(bind 与 subst 并集) */
10
+ paramNames: string[];
11
+ render(params: Record<string, unknown>): RenderedSql;
12
+ }
13
+ export declare class SqlValidationError extends Error {
14
+ readonly sql: string;
15
+ constructor(message: string, sql: string);
16
+ }
17
+ export declare function compileDynamicSql(source: string): CompiledSql;
18
+ /**
19
+ * 去注释:-- 行注释、# 行注释(仅行首/空白后,且排除 #{ 绑定占位)、/* 块注释。
20
+ * 注意:此函数用于校验口径,输出会折叠空白。
21
+ */
22
+ export declare function stripSqlComments(sql: string): string;
23
+ /**
24
+ * 单 SELECT 结构校验(词法层;AST 级校验见 ast-check.ts 双保险):
25
+ * - 去注释后必须以 select 或 with 开头
26
+ * - 语句内除末尾外不允许出现分号
27
+ * - 顶层禁止 DML/DDL/调用关键字(字符串字面量内容不参与匹配)
28
+ */
29
+ export declare function assertSingleSelect(sql: string): void;
30
+ /** 便捷入口:编译 + 渲染 + 校验一体 */
31
+ export declare function renderDynamicSql(source: string, params: Record<string, unknown>): RenderedSql;
@@ -0,0 +1,21 @@
1
+ export interface ExprContext {
2
+ params: Record<string, unknown>;
3
+ }
4
+ export declare class ExprError extends Error {
5
+ constructor(message: string);
6
+ }
7
+ /**
8
+ * 求值 jh4j 公式表达式(用于 {if(cond, a, b)} 的 cond 与操作数):
9
+ * - #param 引用(渲染时被替换为参数值;此实现按值求值)
10
+ * - "..." 字符串字面量(仅双引号,与 jh4j 一致)
11
+ * - isEmpty(x)、isNotEmpty(x)
12
+ * - == / != 比较
13
+ * - + 拼接
14
+ */
15
+ export declare function evalExpr(input: string, ctx: ExprContext): unknown;
16
+ /** {if(cond, whenTrue, whenFalse)} 的参数拆分(2 或 3 段,兼容 jh4j 三段式) */
17
+ export declare function parseIfArgs(inner: string): {
18
+ cond: string;
19
+ whenTrue: string;
20
+ whenFalse: string;
21
+ };
@@ -0,0 +1,4 @@
1
+ export * from './tokenizer';
2
+ export * from './expr';
3
+ export * from './compile';
4
+ export * from './ast-check';
@@ -0,0 +1,25 @@
1
+ export type TokenType = "literal" | "bind" | "subst" | "if" | "escape";
2
+ export interface Token {
3
+ type: TokenType;
4
+ text: string;
5
+ /** bind/subst 参数名;if 表达式源码 */
6
+ value?: string;
7
+ start: number;
8
+ end: number;
9
+ }
10
+ export declare class SqlSyntaxError extends Error {
11
+ readonly position: number;
12
+ readonly source: string;
13
+ constructor(message: string, position: number, source: string);
14
+ }
15
+ /**
16
+ * 扫描动态 SQL,产出 token 流:
17
+ * - literal: 纯 SQL 文本(已反转义 \{ \})
18
+ * - bind: #{name}
19
+ * - subst: ${name}
20
+ * - if: {if(expr, a, b)} —— value 为去壳后的参数串
21
+ * - escape: \{ 或 \}
22
+ */
23
+ export declare function tokenize(sql: string): Token[];
24
+ /** 按顶层逗号拆分(忽略双引号内与括号/花括号内) */
25
+ export declare function splitTopLevel(input: string): string[];