@pdfme/common 5.5.10-dev.5 → 6.0.0-dev.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/package.json +30 -55
- package/set-version.js +9 -4
- package/src/dynamicTemplate.ts +3 -1
- package/src/expression.ts +14 -5
- package/src/helper.ts +9 -3
- package/src/types.ts +20 -15
- package/src/version.ts +1 -1
- package/tsconfig.build.json +14 -0
- package/tsconfig.json +14 -4
- package/vite.config.mts +33 -0
- package/dist/cjs/__tests__/dynamicTemplate.test.js +0 -337
- package/dist/cjs/__tests__/dynamicTemplate.test.js.map +0 -1
- package/dist/cjs/__tests__/expression.test.js +0 -474
- package/dist/cjs/__tests__/expression.test.js.map +0 -1
- package/dist/cjs/__tests__/helper.test.js +0 -647
- package/dist/cjs/__tests__/helper.test.js.map +0 -1
- package/dist/cjs/__tests__/pluginRegistry.test.js +0 -83
- package/dist/cjs/__tests__/pluginRegistry.test.js.map +0 -1
- package/dist/cjs/src/constants.js +0 -27
- package/dist/cjs/src/constants.js.map +0 -1
- package/dist/cjs/src/dynamicTemplate.js +0 -228
- package/dist/cjs/src/dynamicTemplate.js.map +0 -1
- package/dist/cjs/src/expression.js +0 -463
- package/dist/cjs/src/expression.js.map +0 -1
- package/dist/cjs/src/helper.js +0 -277
- package/dist/cjs/src/helper.js.map +0 -1
- package/dist/cjs/src/index.js +0 -43
- package/dist/cjs/src/index.js.map +0 -1
- package/dist/cjs/src/pluginRegistry.js +0 -33
- package/dist/cjs/src/pluginRegistry.js.map +0 -1
- package/dist/cjs/src/schema.js +0 -190
- package/dist/cjs/src/schema.js.map +0 -1
- package/dist/cjs/src/types.js +0 -3
- package/dist/cjs/src/types.js.map +0 -1
- package/dist/cjs/src/version.js +0 -5
- package/dist/cjs/src/version.js.map +0 -1
- package/dist/esm/__tests__/dynamicTemplate.test.js +0 -302
- package/dist/esm/__tests__/dynamicTemplate.test.js.map +0 -1
- package/dist/esm/__tests__/expression.test.js +0 -472
- package/dist/esm/__tests__/expression.test.js.map +0 -1
- package/dist/esm/__tests__/helper.test.js +0 -612
- package/dist/esm/__tests__/helper.test.js.map +0 -1
- package/dist/esm/__tests__/pluginRegistry.test.js +0 -81
- package/dist/esm/__tests__/pluginRegistry.test.js.map +0 -1
- package/dist/esm/src/constants.js +0 -24
- package/dist/esm/src/constants.js.map +0 -1
- package/dist/esm/src/dynamicTemplate.js +0 -224
- package/dist/esm/src/dynamicTemplate.js.map +0 -1
- package/dist/esm/src/expression.js +0 -426
- package/dist/esm/src/expression.js.map +0 -1
- package/dist/esm/src/helper.js +0 -252
- package/dist/esm/src/helper.js.map +0 -1
- package/dist/esm/src/index.js +0 -8
- package/dist/esm/src/index.js.map +0 -1
- package/dist/esm/src/pluginRegistry.js +0 -29
- package/dist/esm/src/pluginRegistry.js.map +0 -1
- package/dist/esm/src/schema.js +0 -187
- package/dist/esm/src/schema.js.map +0 -1
- package/dist/esm/src/types.js +0 -2
- package/dist/esm/src/types.js.map +0 -1
- package/dist/esm/src/version.js +0 -2
- package/dist/esm/src/version.js.map +0 -1
- package/dist/node/__tests__/dynamicTemplate.test.js +0 -337
- package/dist/node/__tests__/dynamicTemplate.test.js.map +0 -1
- package/dist/node/__tests__/expression.test.js +0 -474
- package/dist/node/__tests__/expression.test.js.map +0 -1
- package/dist/node/__tests__/helper.test.js +0 -647
- package/dist/node/__tests__/helper.test.js.map +0 -1
- package/dist/node/__tests__/pluginRegistry.test.js +0 -83
- package/dist/node/__tests__/pluginRegistry.test.js.map +0 -1
- package/dist/node/src/constants.js +0 -27
- package/dist/node/src/constants.js.map +0 -1
- package/dist/node/src/dynamicTemplate.js +0 -228
- package/dist/node/src/dynamicTemplate.js.map +0 -1
- package/dist/node/src/expression.js +0 -463
- package/dist/node/src/expression.js.map +0 -1
- package/dist/node/src/helper.js +0 -277
- package/dist/node/src/helper.js.map +0 -1
- package/dist/node/src/index.js +0 -43
- package/dist/node/src/index.js.map +0 -1
- package/dist/node/src/pluginRegistry.js +0 -33
- package/dist/node/src/pluginRegistry.js.map +0 -1
- package/dist/node/src/schema.js +0 -190
- package/dist/node/src/schema.js.map +0 -1
- package/dist/node/src/types.js +0 -3
- package/dist/node/src/types.js.map +0 -1
- package/dist/node/src/version.js +0 -5
- package/dist/node/src/version.js.map +0 -1
- package/dist/types/__tests__/dynamicTemplate.test.d.ts +0 -1
- package/dist/types/__tests__/expression.test.d.ts +0 -1
- package/dist/types/__tests__/helper.test.d.ts +0 -1
- package/dist/types/__tests__/pluginRegistry.test.d.ts +0 -1
- package/dist/types/src/constants.d.ts +0 -20
- package/dist/types/src/dynamicTemplate.d.ts +0 -27
- package/dist/types/src/expression.d.ts +0 -6
- package/dist/types/src/helper.d.ts +0 -36
- package/dist/types/src/index.d.ts +0 -9
- package/dist/types/src/pluginRegistry.d.ts +0 -5
- package/dist/types/src/schema.d.ts +0 -820
- package/dist/types/src/types.d.ts +0 -181
- package/dist/types/src/version.d.ts +0 -1
- package/eslint.config.mjs +0 -22
- package/tsconfig.cjs.json +0 -10
- package/tsconfig.esm.json +0 -11
- package/tsconfig.node.json +0 -11
|
@@ -1,224 +0,0 @@
|
|
|
1
|
-
import { cloneDeep, isBlankPdf } from './helper.js';
|
|
2
|
-
/** Floating point tolerance for comparisons */
|
|
3
|
-
const EPSILON = 0.01;
|
|
4
|
-
/** Calculate the content height of a page (drawable area excluding padding) */
|
|
5
|
-
const getContentHeight = (basePdf) => basePdf.height - basePdf.padding[0] - basePdf.padding[2];
|
|
6
|
-
/** Get the input value for a schema */
|
|
7
|
-
const getSchemaValue = (schema, input) => (schema.readOnly ? schema.content : input?.[schema.name]) || '';
|
|
8
|
-
/**
|
|
9
|
-
* Normalize schemas within a single page into layout items.
|
|
10
|
-
* Returns items sorted by Y coordinate with their order preserved.
|
|
11
|
-
*/
|
|
12
|
-
function normalizePageSchemas(pageSchemas, paddingTop) {
|
|
13
|
-
const items = [];
|
|
14
|
-
const orderMap = new Map();
|
|
15
|
-
pageSchemas.forEach((schema, index) => {
|
|
16
|
-
const localY = schema.position.y - paddingTop;
|
|
17
|
-
items.push({
|
|
18
|
-
schema: cloneDeep(schema),
|
|
19
|
-
baseY: localY,
|
|
20
|
-
height: schema.height,
|
|
21
|
-
dynamicHeights: [schema.height], // Will be updated later
|
|
22
|
-
});
|
|
23
|
-
orderMap.set(schema.name, index);
|
|
24
|
-
});
|
|
25
|
-
// Sort by Y coordinate (preserve original order for same position)
|
|
26
|
-
items.sort((a, b) => {
|
|
27
|
-
if (Math.abs(a.baseY - b.baseY) > EPSILON) {
|
|
28
|
-
return a.baseY - b.baseY;
|
|
29
|
-
}
|
|
30
|
-
return (orderMap.get(a.schema.name) ?? 0) - (orderMap.get(b.schema.name) ?? 0);
|
|
31
|
-
});
|
|
32
|
-
return { items, orderMap };
|
|
33
|
-
}
|
|
34
|
-
/**
|
|
35
|
-
* Place rows on pages, splitting across pages as needed.
|
|
36
|
-
* @returns The final global Y coordinate after placement
|
|
37
|
-
*/
|
|
38
|
-
function placeRowsOnPages(schema, dynamicHeights, startGlobalY, contentHeight, paddingTop, pages) {
|
|
39
|
-
let currentRowIndex = 0;
|
|
40
|
-
let currentPageIndex = Math.floor(startGlobalY / contentHeight);
|
|
41
|
-
let currentYInPage = startGlobalY % contentHeight;
|
|
42
|
-
if (currentYInPage < 0)
|
|
43
|
-
currentYInPage = 0;
|
|
44
|
-
let actualGlobalEndY = 0;
|
|
45
|
-
const isSplittable = dynamicHeights.length > 1;
|
|
46
|
-
while (currentRowIndex < dynamicHeights.length) {
|
|
47
|
-
// Ensure page exists
|
|
48
|
-
while (pages.length <= currentPageIndex)
|
|
49
|
-
pages.push([]);
|
|
50
|
-
const spaceLeft = contentHeight - currentYInPage;
|
|
51
|
-
const rowHeight = dynamicHeights[currentRowIndex];
|
|
52
|
-
// If row doesn't fit, move to next page
|
|
53
|
-
if (rowHeight > spaceLeft + EPSILON) {
|
|
54
|
-
const isAtPageStart = Math.abs(spaceLeft - contentHeight) <= EPSILON;
|
|
55
|
-
if (!isAtPageStart) {
|
|
56
|
-
currentPageIndex++;
|
|
57
|
-
currentYInPage = 0;
|
|
58
|
-
continue;
|
|
59
|
-
}
|
|
60
|
-
// Force placement for oversized rows that don't fit even on a fresh page
|
|
61
|
-
}
|
|
62
|
-
// Pack as many rows as possible on this page
|
|
63
|
-
let chunkHeight = 0;
|
|
64
|
-
const startRowIndex = currentRowIndex;
|
|
65
|
-
while (currentRowIndex < dynamicHeights.length) {
|
|
66
|
-
const h = dynamicHeights[currentRowIndex];
|
|
67
|
-
if (currentYInPage + chunkHeight + h <= contentHeight + EPSILON) {
|
|
68
|
-
chunkHeight += h;
|
|
69
|
-
currentRowIndex++;
|
|
70
|
-
}
|
|
71
|
-
else {
|
|
72
|
-
break;
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
// Don't leave header alone on a page without any data rows
|
|
76
|
-
// If only header fits and there are data rows remaining, move everything to next page
|
|
77
|
-
// BUT: if already at page top, don't move (prevents infinite loop when data row is too large)
|
|
78
|
-
const isAtPageTop = currentYInPage <= EPSILON;
|
|
79
|
-
if (isSplittable &&
|
|
80
|
-
startRowIndex === 0 &&
|
|
81
|
-
currentRowIndex === 1 &&
|
|
82
|
-
dynamicHeights.length > 1 &&
|
|
83
|
-
!isAtPageTop) {
|
|
84
|
-
currentRowIndex = 0;
|
|
85
|
-
currentPageIndex++;
|
|
86
|
-
currentYInPage = 0;
|
|
87
|
-
continue;
|
|
88
|
-
}
|
|
89
|
-
// Force at least one row to prevent infinite loop
|
|
90
|
-
if (currentRowIndex === startRowIndex) {
|
|
91
|
-
chunkHeight += dynamicHeights[currentRowIndex];
|
|
92
|
-
currentRowIndex++;
|
|
93
|
-
}
|
|
94
|
-
// Create schema for this chunk
|
|
95
|
-
const newSchema = {
|
|
96
|
-
...schema,
|
|
97
|
-
height: chunkHeight,
|
|
98
|
-
position: { ...schema.position, y: currentYInPage + paddingTop },
|
|
99
|
-
};
|
|
100
|
-
// Set bodyRange for splittable elements
|
|
101
|
-
// dynamicHeights[0] = header row, dynamicHeights[1] = body[0]
|
|
102
|
-
// So subtract 1 to convert to body index
|
|
103
|
-
if (isSplittable) {
|
|
104
|
-
newSchema.__bodyRange = {
|
|
105
|
-
start: startRowIndex === 0 ? 0 : startRowIndex - 1,
|
|
106
|
-
end: currentRowIndex - 1,
|
|
107
|
-
};
|
|
108
|
-
newSchema.__isSplit = startRowIndex > 0;
|
|
109
|
-
}
|
|
110
|
-
pages[currentPageIndex].push(newSchema);
|
|
111
|
-
// Update position
|
|
112
|
-
currentYInPage += chunkHeight;
|
|
113
|
-
if (currentYInPage >= contentHeight - EPSILON) {
|
|
114
|
-
currentPageIndex++;
|
|
115
|
-
currentYInPage = 0;
|
|
116
|
-
}
|
|
117
|
-
actualGlobalEndY = currentPageIndex * contentHeight + currentYInPage;
|
|
118
|
-
}
|
|
119
|
-
return actualGlobalEndY;
|
|
120
|
-
}
|
|
121
|
-
/** Sort elements within each page by their original order */
|
|
122
|
-
function sortPagesByOrder(pages, orderMap) {
|
|
123
|
-
pages.forEach((page) => {
|
|
124
|
-
page.sort((a, b) => (orderMap.get(a.name) ?? 0) - (orderMap.get(b.name) ?? 0));
|
|
125
|
-
});
|
|
126
|
-
}
|
|
127
|
-
/** Remove trailing empty pages */
|
|
128
|
-
function removeTrailingEmptyPages(pages) {
|
|
129
|
-
while (pages.length > 1 && pages[pages.length - 1].length === 0) {
|
|
130
|
-
pages.pop();
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
/**
|
|
134
|
-
* Process a single template page that has dynamic content.
|
|
135
|
-
* Uses the same layout algorithm as the original implementation,
|
|
136
|
-
* but scoped to a single page's schemas.
|
|
137
|
-
*/
|
|
138
|
-
function processDynamicPage(items, orderMap, contentHeight, paddingTop) {
|
|
139
|
-
const pages = [];
|
|
140
|
-
let totalYOffset = 0;
|
|
141
|
-
for (const item of items) {
|
|
142
|
-
const currentGlobalStartY = item.baseY + totalYOffset;
|
|
143
|
-
const actualGlobalEndY = placeRowsOnPages(item.schema, item.dynamicHeights, currentGlobalStartY, contentHeight, paddingTop, pages);
|
|
144
|
-
// Update offset: difference between actual and original end position
|
|
145
|
-
const originalGlobalEndY = item.baseY + item.height;
|
|
146
|
-
totalYOffset = actualGlobalEndY - originalGlobalEndY;
|
|
147
|
-
}
|
|
148
|
-
sortPagesByOrder(pages, orderMap);
|
|
149
|
-
removeTrailingEmptyPages(pages);
|
|
150
|
-
return pages;
|
|
151
|
-
}
|
|
152
|
-
/**
|
|
153
|
-
* Process a template containing tables with dynamic heights
|
|
154
|
-
* and generate a new template with proper page breaks.
|
|
155
|
-
*
|
|
156
|
-
* Processing is done page-by-page:
|
|
157
|
-
* - Pages with height changes are processed with full layout calculations
|
|
158
|
-
* - Pages without height changes are copied as-is (no offset propagation between pages)
|
|
159
|
-
*
|
|
160
|
-
* This reduces computation cost by:
|
|
161
|
-
* 1. Limiting layout calculations to pages that need them
|
|
162
|
-
* 2. Avoiding cross-page offset propagation for static pages
|
|
163
|
-
*/
|
|
164
|
-
export const getDynamicTemplate = async (arg) => {
|
|
165
|
-
const { template, input, options, _cache, getDynamicHeights } = arg;
|
|
166
|
-
const basePdf = template.basePdf;
|
|
167
|
-
if (!isBlankPdf(basePdf)) {
|
|
168
|
-
return template;
|
|
169
|
-
}
|
|
170
|
-
const contentHeight = getContentHeight(basePdf);
|
|
171
|
-
const paddingTop = basePdf.padding[0];
|
|
172
|
-
const resultPages = [];
|
|
173
|
-
const PARALLEL_LIMIT = 10;
|
|
174
|
-
// Process each template page independently
|
|
175
|
-
for (let pageIndex = 0; pageIndex < template.schemas.length; pageIndex++) {
|
|
176
|
-
const pageSchemas = template.schemas[pageIndex];
|
|
177
|
-
// Normalize this page's schemas
|
|
178
|
-
const { items, orderMap } = normalizePageSchemas(pageSchemas, paddingTop);
|
|
179
|
-
// Calculate dynamic heights for this page's schemas with concurrency limit
|
|
180
|
-
for (let i = 0; i < items.length; i += PARALLEL_LIMIT) {
|
|
181
|
-
const chunk = items.slice(i, i + PARALLEL_LIMIT);
|
|
182
|
-
const chunkResults = await Promise.all(chunk.map((item) => {
|
|
183
|
-
const value = getSchemaValue(item.schema, input);
|
|
184
|
-
return getDynamicHeights(value, {
|
|
185
|
-
schema: item.schema,
|
|
186
|
-
basePdf,
|
|
187
|
-
options,
|
|
188
|
-
_cache,
|
|
189
|
-
}).then((heights) => (heights.length === 0 ? [0] : heights));
|
|
190
|
-
}));
|
|
191
|
-
// Update items with calculated heights
|
|
192
|
-
for (let j = 0; j < chunkResults.length; j++) {
|
|
193
|
-
items[i + j].dynamicHeights = chunkResults[j];
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
// Process all pages independently (no cross-page offset propagation)
|
|
197
|
-
const processedPages = processDynamicPage(items, orderMap, contentHeight, paddingTop);
|
|
198
|
-
resultPages.push(...processedPages);
|
|
199
|
-
}
|
|
200
|
-
removeTrailingEmptyPages(resultPages);
|
|
201
|
-
// Check if anything changed - return original template if not
|
|
202
|
-
if (resultPages.length === template.schemas.length) {
|
|
203
|
-
let unchanged = true;
|
|
204
|
-
for (let i = 0; i < resultPages.length && unchanged; i++) {
|
|
205
|
-
if (resultPages[i].length !== template.schemas[i].length) {
|
|
206
|
-
unchanged = false;
|
|
207
|
-
break;
|
|
208
|
-
}
|
|
209
|
-
for (let j = 0; j < resultPages[i].length && unchanged; j++) {
|
|
210
|
-
const orig = template.schemas[i][j];
|
|
211
|
-
const result = resultPages[i][j];
|
|
212
|
-
if (Math.abs(orig.height - result.height) > EPSILON ||
|
|
213
|
-
Math.abs(orig.position.y - result.position.y) > EPSILON) {
|
|
214
|
-
unchanged = false;
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
if (unchanged) {
|
|
219
|
-
return template;
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
return { basePdf, schemas: resultPages };
|
|
223
|
-
};
|
|
224
|
-
//# sourceMappingURL=dynamicTemplate.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"dynamicTemplate.js","sourceRoot":"","sources":["../../../src/dynamicTemplate.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,426 +0,0 @@
|
|
|
1
|
-
import * as acorn from 'acorn';
|
|
2
|
-
const expressionCache = new Map();
|
|
3
|
-
const parseDataCache = new Map();
|
|
4
|
-
const parseData = (data) => {
|
|
5
|
-
const key = JSON.stringify(data);
|
|
6
|
-
if (parseDataCache.has(key)) {
|
|
7
|
-
return parseDataCache.get(key);
|
|
8
|
-
}
|
|
9
|
-
const parsed = Object.fromEntries(Object.entries(data).map(([key, value]) => {
|
|
10
|
-
if (typeof value === 'string') {
|
|
11
|
-
try {
|
|
12
|
-
const parsedValue = JSON.parse(value);
|
|
13
|
-
return [key, parsedValue];
|
|
14
|
-
}
|
|
15
|
-
catch {
|
|
16
|
-
return [key, value];
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
return [key, value];
|
|
20
|
-
}));
|
|
21
|
-
parseDataCache.set(key, parsed);
|
|
22
|
-
return parsed;
|
|
23
|
-
};
|
|
24
|
-
const padZero = (num) => String(num).padStart(2, '0');
|
|
25
|
-
const formatDate = (date) => `${date.getFullYear()}/${padZero(date.getMonth() + 1)}/${padZero(date.getDate())}`;
|
|
26
|
-
const formatDateTime = (date) => `${formatDate(date)} ${padZero(date.getHours())}:${padZero(date.getMinutes())}`;
|
|
27
|
-
// Safe assign function that prevents prototype pollution
|
|
28
|
-
const safeAssign = (target, ...sources) => {
|
|
29
|
-
if (target == null) {
|
|
30
|
-
throw new TypeError('Cannot convert undefined or null to object');
|
|
31
|
-
}
|
|
32
|
-
const to = { ...target };
|
|
33
|
-
for (const source of sources) {
|
|
34
|
-
if (source != null) {
|
|
35
|
-
for (const key in source) {
|
|
36
|
-
// Skip prototype pollution keys
|
|
37
|
-
if (key === '__proto__' || key === 'constructor' || key === 'prototype') {
|
|
38
|
-
continue;
|
|
39
|
-
}
|
|
40
|
-
// Only copy own properties
|
|
41
|
-
if (Object.prototype.hasOwnProperty.call(source, key)) {
|
|
42
|
-
to[key] = source[key];
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
return to;
|
|
48
|
-
};
|
|
49
|
-
// Create a safe copy of Object with dangerous methods excluded
|
|
50
|
-
const safeObject = {
|
|
51
|
-
keys: Object.keys,
|
|
52
|
-
values: Object.values,
|
|
53
|
-
entries: Object.entries,
|
|
54
|
-
fromEntries: Object.fromEntries,
|
|
55
|
-
is: Object.is,
|
|
56
|
-
hasOwnProperty: Object.hasOwnProperty,
|
|
57
|
-
assign: safeAssign, // Safe version of Object.assign
|
|
58
|
-
// The following methods are excluded due to security concerns:
|
|
59
|
-
// - Side effects: create, freeze, seal (can still be used for attacks)
|
|
60
|
-
// - Prototype access: getOwnPropertyDescriptor, getPrototypeOf, setPrototypeOf,
|
|
61
|
-
// defineProperty, defineProperties, getOwnPropertyNames, getOwnPropertySymbols
|
|
62
|
-
};
|
|
63
|
-
const allowedGlobals = {
|
|
64
|
-
Math,
|
|
65
|
-
String,
|
|
66
|
-
Number,
|
|
67
|
-
Boolean,
|
|
68
|
-
Array,
|
|
69
|
-
Object: safeObject,
|
|
70
|
-
Date,
|
|
71
|
-
JSON,
|
|
72
|
-
isNaN,
|
|
73
|
-
parseFloat,
|
|
74
|
-
parseInt,
|
|
75
|
-
decodeURI,
|
|
76
|
-
decodeURIComponent,
|
|
77
|
-
encodeURI,
|
|
78
|
-
encodeURIComponent,
|
|
79
|
-
};
|
|
80
|
-
const validateAST = (node) => {
|
|
81
|
-
switch (node.type) {
|
|
82
|
-
case 'Literal':
|
|
83
|
-
case 'Identifier':
|
|
84
|
-
break;
|
|
85
|
-
case 'BinaryExpression':
|
|
86
|
-
case 'LogicalExpression': {
|
|
87
|
-
const binaryNode = node;
|
|
88
|
-
validateAST(binaryNode.left);
|
|
89
|
-
validateAST(binaryNode.right);
|
|
90
|
-
break;
|
|
91
|
-
}
|
|
92
|
-
case 'UnaryExpression': {
|
|
93
|
-
const unaryNode = node;
|
|
94
|
-
validateAST(unaryNode.argument);
|
|
95
|
-
break;
|
|
96
|
-
}
|
|
97
|
-
case 'ConditionalExpression': {
|
|
98
|
-
const condNode = node;
|
|
99
|
-
validateAST(condNode.test);
|
|
100
|
-
validateAST(condNode.consequent);
|
|
101
|
-
validateAST(condNode.alternate);
|
|
102
|
-
break;
|
|
103
|
-
}
|
|
104
|
-
case 'MemberExpression': {
|
|
105
|
-
const memberNode = node;
|
|
106
|
-
validateAST(memberNode.object);
|
|
107
|
-
if (memberNode.computed) {
|
|
108
|
-
validateAST(memberNode.property);
|
|
109
|
-
}
|
|
110
|
-
else {
|
|
111
|
-
const propName = memberNode.property.name;
|
|
112
|
-
if (['constructor', '__proto__', 'prototype'].includes(propName)) {
|
|
113
|
-
throw new Error('Access to prohibited property');
|
|
114
|
-
}
|
|
115
|
-
// Block prototype pollution methods
|
|
116
|
-
if (['__defineGetter__', '__defineSetter__', '__lookupGetter__', '__lookupSetter__'].includes(propName)) {
|
|
117
|
-
throw new Error(`Access to prohibited method: ${propName}`);
|
|
118
|
-
}
|
|
119
|
-
const prohibitedMethods = ['toLocaleString', 'valueOf'];
|
|
120
|
-
if (typeof propName === 'string' && prohibitedMethods.includes(propName)) {
|
|
121
|
-
throw new Error(`Access to prohibited method: ${propName}`);
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
break;
|
|
125
|
-
}
|
|
126
|
-
case 'CallExpression': {
|
|
127
|
-
const callNode = node;
|
|
128
|
-
validateAST(callNode.callee);
|
|
129
|
-
callNode.arguments.forEach(validateAST);
|
|
130
|
-
break;
|
|
131
|
-
}
|
|
132
|
-
case 'ArrayExpression': {
|
|
133
|
-
const arrayNode = node;
|
|
134
|
-
arrayNode.elements.forEach((elem) => {
|
|
135
|
-
if (elem)
|
|
136
|
-
validateAST(elem);
|
|
137
|
-
});
|
|
138
|
-
break;
|
|
139
|
-
}
|
|
140
|
-
case 'ObjectExpression': {
|
|
141
|
-
const objectNode = node;
|
|
142
|
-
objectNode.properties.forEach((prop) => {
|
|
143
|
-
const propNode = prop;
|
|
144
|
-
validateAST(propNode.key);
|
|
145
|
-
validateAST(propNode.value);
|
|
146
|
-
});
|
|
147
|
-
break;
|
|
148
|
-
}
|
|
149
|
-
case 'ArrowFunctionExpression': {
|
|
150
|
-
const arrowFuncNode = node;
|
|
151
|
-
arrowFuncNode.params.forEach((param) => {
|
|
152
|
-
if (param.type !== 'Identifier') {
|
|
153
|
-
throw new Error('Only identifier parameters are supported in arrow functions');
|
|
154
|
-
}
|
|
155
|
-
validateAST(param);
|
|
156
|
-
});
|
|
157
|
-
validateAST(arrowFuncNode.body);
|
|
158
|
-
break;
|
|
159
|
-
}
|
|
160
|
-
default:
|
|
161
|
-
throw new Error(`Unsupported syntax in placeholder: ${node.type}`);
|
|
162
|
-
}
|
|
163
|
-
};
|
|
164
|
-
const evaluateAST = (node, context) => {
|
|
165
|
-
switch (node.type) {
|
|
166
|
-
case 'Literal': {
|
|
167
|
-
const literalNode = node;
|
|
168
|
-
return literalNode.value;
|
|
169
|
-
}
|
|
170
|
-
case 'Identifier': {
|
|
171
|
-
const idNode = node;
|
|
172
|
-
if (Object.prototype.hasOwnProperty.call(context, idNode.name)) {
|
|
173
|
-
return context[idNode.name];
|
|
174
|
-
}
|
|
175
|
-
else if (Object.prototype.hasOwnProperty.call(allowedGlobals, idNode.name)) {
|
|
176
|
-
return allowedGlobals[idNode.name];
|
|
177
|
-
}
|
|
178
|
-
else {
|
|
179
|
-
throw new Error(`Undefined variable: ${idNode.name}`);
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
case 'BinaryExpression': {
|
|
183
|
-
const binaryNode = node;
|
|
184
|
-
const left = evaluateAST(binaryNode.left, context);
|
|
185
|
-
const right = evaluateAST(binaryNode.right, context);
|
|
186
|
-
switch (binaryNode.operator) {
|
|
187
|
-
case '+':
|
|
188
|
-
return left + right;
|
|
189
|
-
case '-':
|
|
190
|
-
return left - right;
|
|
191
|
-
case '*':
|
|
192
|
-
return left * right;
|
|
193
|
-
case '/':
|
|
194
|
-
return left / right;
|
|
195
|
-
case '%':
|
|
196
|
-
return left % right;
|
|
197
|
-
case '**':
|
|
198
|
-
return left ** right;
|
|
199
|
-
case '==':
|
|
200
|
-
return left == right;
|
|
201
|
-
case '!=':
|
|
202
|
-
return left != right;
|
|
203
|
-
case '===':
|
|
204
|
-
return left === right;
|
|
205
|
-
case '!==':
|
|
206
|
-
return left !== right;
|
|
207
|
-
case '<':
|
|
208
|
-
return left < right;
|
|
209
|
-
case '>':
|
|
210
|
-
return left > right;
|
|
211
|
-
case '<=':
|
|
212
|
-
return left <= right;
|
|
213
|
-
case '>=':
|
|
214
|
-
return left >= right;
|
|
215
|
-
default:
|
|
216
|
-
throw new Error(`Unsupported operator: ${binaryNode.operator}`);
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
case 'LogicalExpression': {
|
|
220
|
-
const logicalNode = node;
|
|
221
|
-
const leftLogical = evaluateAST(logicalNode.left, context);
|
|
222
|
-
const rightLogical = evaluateAST(logicalNode.right, context);
|
|
223
|
-
switch (logicalNode.operator) {
|
|
224
|
-
case '&&':
|
|
225
|
-
return leftLogical && rightLogical;
|
|
226
|
-
case '||':
|
|
227
|
-
return leftLogical || rightLogical;
|
|
228
|
-
default:
|
|
229
|
-
throw new Error(`Unsupported operator: ${logicalNode.operator}`);
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
case 'UnaryExpression': {
|
|
233
|
-
const unaryNode = node;
|
|
234
|
-
const arg = evaluateAST(unaryNode.argument, context);
|
|
235
|
-
switch (unaryNode.operator) {
|
|
236
|
-
case '+':
|
|
237
|
-
return +arg;
|
|
238
|
-
case '-':
|
|
239
|
-
return -arg;
|
|
240
|
-
case '!':
|
|
241
|
-
return !arg;
|
|
242
|
-
default:
|
|
243
|
-
throw new Error(`Unsupported operator: ${unaryNode.operator}`);
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
case 'ConditionalExpression': {
|
|
247
|
-
const condNode = node;
|
|
248
|
-
const test = evaluateAST(condNode.test, context);
|
|
249
|
-
return test
|
|
250
|
-
? evaluateAST(condNode.consequent, context)
|
|
251
|
-
: evaluateAST(condNode.alternate, context);
|
|
252
|
-
}
|
|
253
|
-
case 'MemberExpression': {
|
|
254
|
-
const memberNode = node;
|
|
255
|
-
const obj = evaluateAST(memberNode.object, context);
|
|
256
|
-
let prop;
|
|
257
|
-
if (memberNode.computed) {
|
|
258
|
-
prop = evaluateAST(memberNode.property, context);
|
|
259
|
-
}
|
|
260
|
-
else {
|
|
261
|
-
prop = memberNode.property.name;
|
|
262
|
-
}
|
|
263
|
-
if (typeof prop === 'string' || typeof prop === 'number') {
|
|
264
|
-
if (typeof prop === 'string' && ['constructor', '__proto__', 'prototype'].includes(prop)) {
|
|
265
|
-
throw new Error('Access to prohibited property');
|
|
266
|
-
}
|
|
267
|
-
// Block prototype pollution methods
|
|
268
|
-
if (typeof prop === 'string' && ['__defineGetter__', '__defineSetter__', '__lookupGetter__', '__lookupSetter__'].includes(prop)) {
|
|
269
|
-
throw new Error(`Access to prohibited method: ${prop}`);
|
|
270
|
-
}
|
|
271
|
-
return obj[prop];
|
|
272
|
-
}
|
|
273
|
-
else {
|
|
274
|
-
throw new Error('Invalid property access');
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
case 'CallExpression': {
|
|
278
|
-
const callNode = node;
|
|
279
|
-
const callee = evaluateAST(callNode.callee, context);
|
|
280
|
-
const args = callNode.arguments.map((argNode) => evaluateAST(argNode, context));
|
|
281
|
-
if (typeof callee === 'function') {
|
|
282
|
-
if (callNode.callee.type === 'MemberExpression') {
|
|
283
|
-
const memberExpr = callNode.callee;
|
|
284
|
-
const obj = evaluateAST(memberExpr.object, context);
|
|
285
|
-
if (obj !== null &&
|
|
286
|
-
(typeof obj === 'object' ||
|
|
287
|
-
typeof obj === 'number' ||
|
|
288
|
-
typeof obj === 'string' ||
|
|
289
|
-
typeof obj === 'boolean')) {
|
|
290
|
-
return callee.call(obj, ...args);
|
|
291
|
-
}
|
|
292
|
-
else {
|
|
293
|
-
throw new Error('Invalid object in member function call');
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
else {
|
|
297
|
-
// Use a type assertion to tell TypeScript this is a safe function call
|
|
298
|
-
return callee(...args);
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
else {
|
|
302
|
-
throw new Error('Attempted to call a non-function');
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
case 'ArrowFunctionExpression': {
|
|
306
|
-
const arrowFuncNode = node;
|
|
307
|
-
const params = arrowFuncNode.params.map((param) => param.name);
|
|
308
|
-
const body = arrowFuncNode.body;
|
|
309
|
-
return (...args) => {
|
|
310
|
-
const newContext = { ...context };
|
|
311
|
-
params.forEach((param, index) => {
|
|
312
|
-
newContext[param] = args[index];
|
|
313
|
-
});
|
|
314
|
-
return evaluateAST(body, newContext);
|
|
315
|
-
};
|
|
316
|
-
}
|
|
317
|
-
case 'ArrayExpression': {
|
|
318
|
-
const arrayNode = node;
|
|
319
|
-
return arrayNode.elements.map((elem) => (elem ? evaluateAST(elem, context) : null));
|
|
320
|
-
}
|
|
321
|
-
case 'ObjectExpression': {
|
|
322
|
-
const objectNode = node;
|
|
323
|
-
const objResult = {};
|
|
324
|
-
objectNode.properties.forEach((prop) => {
|
|
325
|
-
const propNode = prop;
|
|
326
|
-
let key;
|
|
327
|
-
if (propNode.key.type === 'Identifier') {
|
|
328
|
-
key = propNode.key.name;
|
|
329
|
-
}
|
|
330
|
-
else {
|
|
331
|
-
const evaluatedKey = evaluateAST(propNode.key, context);
|
|
332
|
-
if (typeof evaluatedKey !== 'string' && typeof evaluatedKey !== 'number') {
|
|
333
|
-
throw new Error('Object property keys must be strings or numbers');
|
|
334
|
-
}
|
|
335
|
-
key = String(evaluatedKey);
|
|
336
|
-
}
|
|
337
|
-
const value = evaluateAST(propNode.value, context);
|
|
338
|
-
objResult[key] = value;
|
|
339
|
-
});
|
|
340
|
-
return objResult;
|
|
341
|
-
}
|
|
342
|
-
default:
|
|
343
|
-
throw new Error(`Unsupported syntax in placeholder: ${node.type}`);
|
|
344
|
-
}
|
|
345
|
-
};
|
|
346
|
-
const evaluatePlaceholders = (arg) => {
|
|
347
|
-
const { content, context } = arg;
|
|
348
|
-
let resultContent = '';
|
|
349
|
-
let index = 0;
|
|
350
|
-
while (index < content.length) {
|
|
351
|
-
const startIndex = content.indexOf('{', index);
|
|
352
|
-
if (startIndex === -1) {
|
|
353
|
-
resultContent += content.slice(index);
|
|
354
|
-
break;
|
|
355
|
-
}
|
|
356
|
-
resultContent += content.slice(index, startIndex);
|
|
357
|
-
let braceCount = 1;
|
|
358
|
-
let endIndex = startIndex + 1;
|
|
359
|
-
while (endIndex < content.length && braceCount > 0) {
|
|
360
|
-
if (content[endIndex] === '{') {
|
|
361
|
-
braceCount++;
|
|
362
|
-
}
|
|
363
|
-
else if (content[endIndex] === '}') {
|
|
364
|
-
braceCount--;
|
|
365
|
-
}
|
|
366
|
-
endIndex++;
|
|
367
|
-
}
|
|
368
|
-
if (braceCount === 0) {
|
|
369
|
-
const code = content.slice(startIndex + 1, endIndex - 1).trim();
|
|
370
|
-
if (expressionCache.has(code)) {
|
|
371
|
-
const evalFunc = expressionCache.get(code);
|
|
372
|
-
try {
|
|
373
|
-
const value = evalFunc(context);
|
|
374
|
-
resultContent += String(value);
|
|
375
|
-
}
|
|
376
|
-
catch {
|
|
377
|
-
resultContent += content.slice(startIndex, endIndex);
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
else {
|
|
381
|
-
try {
|
|
382
|
-
const ast = acorn.parseExpressionAt(code, 0, { ecmaVersion: 'latest' });
|
|
383
|
-
validateAST(ast);
|
|
384
|
-
const evalFunc = (ctx) => evaluateAST(ast, ctx);
|
|
385
|
-
expressionCache.set(code, evalFunc);
|
|
386
|
-
const value = evalFunc(context);
|
|
387
|
-
resultContent += String(value);
|
|
388
|
-
}
|
|
389
|
-
catch {
|
|
390
|
-
resultContent += content.slice(startIndex, endIndex);
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
index = endIndex;
|
|
394
|
-
}
|
|
395
|
-
else {
|
|
396
|
-
throw new Error('Invalid placeholder');
|
|
397
|
-
}
|
|
398
|
-
}
|
|
399
|
-
return resultContent;
|
|
400
|
-
};
|
|
401
|
-
export const replacePlaceholders = (arg) => {
|
|
402
|
-
const { content, variables, schemas } = arg;
|
|
403
|
-
if (!content || typeof content !== 'string' || !content.includes('{') || !content.includes('}')) {
|
|
404
|
-
return content;
|
|
405
|
-
}
|
|
406
|
-
const date = new Date();
|
|
407
|
-
const formattedDate = formatDate(date);
|
|
408
|
-
const formattedDateTime = formatDateTime(date);
|
|
409
|
-
const data = {
|
|
410
|
-
...Object.fromEntries(schemas.flat().map((schema) => [schema.name, schema.readOnly ? schema.content || '' : ''])),
|
|
411
|
-
...variables,
|
|
412
|
-
};
|
|
413
|
-
const parsedInput = parseData(data);
|
|
414
|
-
const context = {
|
|
415
|
-
date: formattedDate,
|
|
416
|
-
dateTime: formattedDateTime,
|
|
417
|
-
...parsedInput,
|
|
418
|
-
};
|
|
419
|
-
Object.entries(context).forEach(([key, value]) => {
|
|
420
|
-
if (typeof value === 'string' && value.includes('{') && value.includes('}')) {
|
|
421
|
-
context[key] = evaluatePlaceholders({ content: value, context });
|
|
422
|
-
}
|
|
423
|
-
});
|
|
424
|
-
return evaluatePlaceholders({ content, context });
|
|
425
|
-
};
|
|
426
|
-
//# sourceMappingURL=expression.js.map
|