@legalplace/prerenderx 3.8.24 → 3.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/CHANGELOG.md +20 -0
- package/dist/libs/DocumentRender.d.ts +3 -2
- package/dist/libs/DocumentRender.js +13 -4
- package/dist/libs/FillPdfForm.d.ts +1 -1
- package/dist/libs/FillPdfForm.js +25 -15
- package/dist/libs/OptionRender.d.ts +4 -0
- package/dist/libs/OptionRender.js +125 -29
- package/dist/libs/OutputParser.d.ts +1 -1
- package/dist/libs/OutputParser.js +18 -6
- package/dist/libs/Prerender.d.ts +5 -2
- package/dist/libs/Prerender.js +100 -27
- package/dist/libs/SectionRender.d.ts +3 -0
- package/dist/libs/SectionRender.js +64 -22
- package/dist/libs/misc/FillPdfFormBase.d.ts +1 -1
- package/dist/libs/misc/FillPdfFormBase.js +11 -37
- package/dist/libs/tableOutputExpansion.d.ts +20 -0
- package/dist/libs/tableOutputExpansion.js +68 -0
- package/dist/types/types.d.ts +13 -0
- package/package.json +8 -8
- package/src/libs/DocumentRender.ts +33 -5
- package/src/libs/FillPdfForm.ts +39 -22
- package/src/libs/OptionRender.ts +218 -63
- package/src/libs/OutputParser.ts +40 -9
- package/src/libs/Prerender.ts +157 -38
- package/src/libs/SectionRender.ts +111 -28
- package/src/libs/misc/FillPdfFormBase.ts +34 -67
- package/src/libs/tableOutputExpansion.ts +145 -0
- package/src/types/types.ts +27 -0
- package/tests/Prerender.documentMultiple.test.ts +278 -0
- package/tests/Prerender.repeated.test.ts +115 -0
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
import type { ModelV3 } from "@legalplace/models-v3-types";
|
|
2
|
+
import Prerender from "../src/libs/Prerender";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Builds a Model V3 with one document `main` configured as multiple
|
|
6
|
+
* (`params.multiple.enabled = true`, source = option #2). The output
|
|
7
|
+
* `[var:3]` interpolates the partner name at each occurrence.
|
|
8
|
+
*/
|
|
9
|
+
const buildMultipleDocumentModel = (
|
|
10
|
+
filenameTemplate?: string
|
|
11
|
+
): ModelV3 => ({
|
|
12
|
+
documents: {
|
|
13
|
+
main: {
|
|
14
|
+
name: "Contract",
|
|
15
|
+
sections: [
|
|
16
|
+
{
|
|
17
|
+
id: 1,
|
|
18
|
+
label: "Section",
|
|
19
|
+
options: [2, 5],
|
|
20
|
+
},
|
|
21
|
+
],
|
|
22
|
+
params: {
|
|
23
|
+
multiple: {
|
|
24
|
+
enabled: true,
|
|
25
|
+
repeatOption: 2,
|
|
26
|
+
...(filenameTemplate ? { filenameTemplate } : {}),
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
options: {
|
|
32
|
+
"2": {
|
|
33
|
+
meta: {
|
|
34
|
+
id: 2,
|
|
35
|
+
type: "static",
|
|
36
|
+
label: "Partner",
|
|
37
|
+
step: "*",
|
|
38
|
+
multiple: {
|
|
39
|
+
enabled: true,
|
|
40
|
+
incrementationStart: 1,
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
options: [],
|
|
44
|
+
variables: [3],
|
|
45
|
+
},
|
|
46
|
+
"5": {
|
|
47
|
+
meta: {
|
|
48
|
+
id: 5,
|
|
49
|
+
type: "hidden",
|
|
50
|
+
label: "Body",
|
|
51
|
+
step: "*",
|
|
52
|
+
output: "<p>Hello [var:3]</p>",
|
|
53
|
+
},
|
|
54
|
+
options: [],
|
|
55
|
+
variables: [3],
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
variables: {
|
|
59
|
+
"3": {
|
|
60
|
+
id: 3,
|
|
61
|
+
type: "text",
|
|
62
|
+
label: "Partner name",
|
|
63
|
+
step: "*",
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
customization: {},
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
const ovcWithThreePartners = {
|
|
70
|
+
options: {
|
|
71
|
+
"2": [true, true, true],
|
|
72
|
+
},
|
|
73
|
+
variables: {
|
|
74
|
+
"3": ["Alice", "Bob", "Charlie"],
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
describe("Prerender — document multiple", () => {
|
|
79
|
+
it("emits one entry per occurrence with distinct hashes", () => {
|
|
80
|
+
const prerender = new Prerender(
|
|
81
|
+
buildMultipleDocumentModel(),
|
|
82
|
+
ovcWithThreePartners
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
const index = prerender.getDocumentsIndex();
|
|
86
|
+
const entries = Object.entries(index);
|
|
87
|
+
|
|
88
|
+
expect(entries).toHaveLength(3);
|
|
89
|
+
const slugs = entries.map(([, e]) => e.slug);
|
|
90
|
+
expect(slugs).toEqual(["main", "main", "main"]);
|
|
91
|
+
|
|
92
|
+
const occurrenceIndexes = entries.map(([, e]) => e.occurrenceIndex);
|
|
93
|
+
expect(occurrenceIndexes.sort()).toEqual([0, 1, 2]);
|
|
94
|
+
|
|
95
|
+
// Hashes must be distinct across occurrences
|
|
96
|
+
const hashes = entries.map(([h]) => h);
|
|
97
|
+
expect(new Set(hashes).size).toBe(hashes.length);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it("uses the document title for each occurrence when no template is set", () => {
|
|
101
|
+
const prerender = new Prerender(
|
|
102
|
+
buildMultipleDocumentModel(),
|
|
103
|
+
ovcWithThreePartners
|
|
104
|
+
);
|
|
105
|
+
const index = prerender.getDocumentsIndex();
|
|
106
|
+
const names = Object.values(index)
|
|
107
|
+
.map((e) => e.name)
|
|
108
|
+
.sort();
|
|
109
|
+
|
|
110
|
+
expect(names).toEqual(["Contract", "Contract", "Contract"]);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it("interpolates document name with variables at each occurrence", () => {
|
|
114
|
+
const model = buildMultipleDocumentModel();
|
|
115
|
+
model.documents.main.name = "Contract - [var:3]";
|
|
116
|
+
if (model.documents.main.params?.multiple) {
|
|
117
|
+
delete model.documents.main.params.multiple.filenameTemplate;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const prerender = new Prerender(model, ovcWithThreePartners);
|
|
121
|
+
const index = prerender.getDocumentsIndex();
|
|
122
|
+
const names = Object.values(index)
|
|
123
|
+
.map((e) => e.name)
|
|
124
|
+
.sort();
|
|
125
|
+
|
|
126
|
+
expect(names).toEqual([
|
|
127
|
+
"Contract - Alice",
|
|
128
|
+
"Contract - Bob",
|
|
129
|
+
"Contract - Charlie",
|
|
130
|
+
]);
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it("interpolates filenameTemplate with the variable value at each occurrence", () => {
|
|
134
|
+
const prerender = new Prerender(
|
|
135
|
+
buildMultipleDocumentModel("Contract - [var:3]"),
|
|
136
|
+
ovcWithThreePartners
|
|
137
|
+
);
|
|
138
|
+
const index = prerender.getDocumentsIndex();
|
|
139
|
+
const names = Object.values(index)
|
|
140
|
+
.map((e) => e.name)
|
|
141
|
+
.sort();
|
|
142
|
+
|
|
143
|
+
expect(names).toEqual([
|
|
144
|
+
"Contract - Alice",
|
|
145
|
+
"Contract - Bob",
|
|
146
|
+
"Contract - Charlie",
|
|
147
|
+
]);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it("renders the body with the right variable for each occurrence", () => {
|
|
151
|
+
const prerender = new Prerender(
|
|
152
|
+
buildMultipleDocumentModel(),
|
|
153
|
+
ovcWithThreePartners
|
|
154
|
+
);
|
|
155
|
+
const index = prerender.getDocumentsIndex();
|
|
156
|
+
|
|
157
|
+
const entries = Object.entries(index).sort(
|
|
158
|
+
(a, b) =>
|
|
159
|
+
((a[1].occurrenceIndex ?? 0) as number) -
|
|
160
|
+
((b[1].occurrenceIndex ?? 0) as number)
|
|
161
|
+
);
|
|
162
|
+
|
|
163
|
+
const rendered = entries.map(([hash]) =>
|
|
164
|
+
prerender.getDocument(hash) as string
|
|
165
|
+
);
|
|
166
|
+
|
|
167
|
+
expect(rendered[0]).toContain("Hello Alice");
|
|
168
|
+
expect(rendered[1]).toContain("Hello Bob");
|
|
169
|
+
expect(rendered[2]).toContain("Hello Charlie");
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
it("returns a single entry when params.multiple.enabled is false", () => {
|
|
173
|
+
const model = buildMultipleDocumentModel();
|
|
174
|
+
if (model.documents.main.params?.multiple) {
|
|
175
|
+
model.documents.main.params.multiple.enabled = false;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const prerender = new Prerender(model, ovcWithThreePartners);
|
|
179
|
+
const index = prerender.getDocumentsIndex();
|
|
180
|
+
|
|
181
|
+
expect(Object.keys(index)).toHaveLength(1);
|
|
182
|
+
const [entry] = Object.values(index);
|
|
183
|
+
expect(entry.slug).toBe("main");
|
|
184
|
+
// No occurrenceIndex when not multiple
|
|
185
|
+
expect(entry.occurrenceIndex).toBeUndefined();
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
it("emits only related occurrences that pass a selected document condition", () => {
|
|
189
|
+
const model: ModelV3 = {
|
|
190
|
+
documents: {
|
|
191
|
+
main: {
|
|
192
|
+
name: "Main",
|
|
193
|
+
sections: [
|
|
194
|
+
{
|
|
195
|
+
id: 1,
|
|
196
|
+
label: "Section",
|
|
197
|
+
options: [2],
|
|
198
|
+
},
|
|
199
|
+
],
|
|
200
|
+
},
|
|
201
|
+
annex: {
|
|
202
|
+
name: "Annex",
|
|
203
|
+
sections: [
|
|
204
|
+
{
|
|
205
|
+
id: 2,
|
|
206
|
+
label: "Annex section",
|
|
207
|
+
options: [],
|
|
208
|
+
},
|
|
209
|
+
],
|
|
210
|
+
params: {
|
|
211
|
+
multiple: {
|
|
212
|
+
enabled: true,
|
|
213
|
+
repeatOption: 2,
|
|
214
|
+
},
|
|
215
|
+
conditions: {
|
|
216
|
+
selected: [{ var: "o.3" }],
|
|
217
|
+
scope: "related",
|
|
218
|
+
},
|
|
219
|
+
},
|
|
220
|
+
},
|
|
221
|
+
},
|
|
222
|
+
options: {
|
|
223
|
+
"2": {
|
|
224
|
+
meta: {
|
|
225
|
+
id: 2,
|
|
226
|
+
type: "static",
|
|
227
|
+
label: "Partner",
|
|
228
|
+
step: "*",
|
|
229
|
+
multiple: {
|
|
230
|
+
enabled: true,
|
|
231
|
+
incrementationStart: 1,
|
|
232
|
+
},
|
|
233
|
+
},
|
|
234
|
+
options: [3, 4],
|
|
235
|
+
variables: [],
|
|
236
|
+
},
|
|
237
|
+
"3": {
|
|
238
|
+
meta: {
|
|
239
|
+
id: 3,
|
|
240
|
+
type: "radio",
|
|
241
|
+
label: "Yes",
|
|
242
|
+
step: "*",
|
|
243
|
+
},
|
|
244
|
+
options: [],
|
|
245
|
+
variables: [],
|
|
246
|
+
},
|
|
247
|
+
"4": {
|
|
248
|
+
meta: {
|
|
249
|
+
id: 4,
|
|
250
|
+
type: "radio",
|
|
251
|
+
label: "No",
|
|
252
|
+
step: "*",
|
|
253
|
+
},
|
|
254
|
+
options: [],
|
|
255
|
+
variables: [],
|
|
256
|
+
},
|
|
257
|
+
},
|
|
258
|
+
variables: {},
|
|
259
|
+
customization: {},
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
const prerender = new Prerender(model, {
|
|
263
|
+
options: {
|
|
264
|
+
"2": [true, true],
|
|
265
|
+
"3": [true, false],
|
|
266
|
+
"4": [false, true],
|
|
267
|
+
},
|
|
268
|
+
variables: {},
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
const annexEntries = Object.values(prerender.getDocumentsIndex()).filter(
|
|
272
|
+
(entry) => entry.slug === "annex"
|
|
273
|
+
);
|
|
274
|
+
|
|
275
|
+
expect(annexEntries).toHaveLength(1);
|
|
276
|
+
expect(annexEntries[0].occurrenceIndex).toBe(0);
|
|
277
|
+
});
|
|
278
|
+
});
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import type { ModelV3 } from "@legalplace/models-v3-types";
|
|
2
|
+
import Prerender from "../src/libs/Prerender";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Builds a Model V3 whose body is a `repeated` block linked to multiple
|
|
6
|
+
* option #2. The hidden child interpolates `[var:3]` at each occurrence.
|
|
7
|
+
*
|
|
8
|
+
* @param multipleType Type of the linked multiple source option
|
|
9
|
+
*/
|
|
10
|
+
const buildRepeatedContentModel = (
|
|
11
|
+
multipleType: "static" | "checkbox" = "static"
|
|
12
|
+
): ModelV3 => ({
|
|
13
|
+
documents: {
|
|
14
|
+
main: {
|
|
15
|
+
name: "Contract",
|
|
16
|
+
sections: [
|
|
17
|
+
{
|
|
18
|
+
id: 1,
|
|
19
|
+
label: "Section",
|
|
20
|
+
options: [10],
|
|
21
|
+
},
|
|
22
|
+
],
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
options: {
|
|
26
|
+
"2": {
|
|
27
|
+
meta: {
|
|
28
|
+
id: 2,
|
|
29
|
+
type: multipleType,
|
|
30
|
+
label: "Partner",
|
|
31
|
+
step: "*",
|
|
32
|
+
multiple: {
|
|
33
|
+
enabled: true,
|
|
34
|
+
incrementationStart: 1,
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
options: [],
|
|
38
|
+
variables: [3],
|
|
39
|
+
},
|
|
40
|
+
"10": {
|
|
41
|
+
meta: {
|
|
42
|
+
id: 10,
|
|
43
|
+
type: "repeated",
|
|
44
|
+
label: "Repeated partners",
|
|
45
|
+
step: "*",
|
|
46
|
+
repeatOption: 2,
|
|
47
|
+
},
|
|
48
|
+
options: [11],
|
|
49
|
+
variables: [],
|
|
50
|
+
},
|
|
51
|
+
"11": {
|
|
52
|
+
meta: {
|
|
53
|
+
id: 11,
|
|
54
|
+
type: "hidden",
|
|
55
|
+
label: "Partner line",
|
|
56
|
+
step: "*",
|
|
57
|
+
output: "<p>Hello [var:3]</p>",
|
|
58
|
+
},
|
|
59
|
+
options: [],
|
|
60
|
+
variables: [3],
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
variables: {
|
|
64
|
+
"3": {
|
|
65
|
+
id: 3,
|
|
66
|
+
type: "text",
|
|
67
|
+
label: "Partner name",
|
|
68
|
+
step: "*",
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
customization: {},
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
const ovcWithThreePartners = {
|
|
75
|
+
options: {
|
|
76
|
+
"2": [true, true, true],
|
|
77
|
+
},
|
|
78
|
+
variables: {
|
|
79
|
+
"3": ["Alice", "Bob", "Charlie"],
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
describe("Prerender — repeated content", () => {
|
|
84
|
+
it("renders every occurrence of a repeated block", () => {
|
|
85
|
+
const prerender = new Prerender(
|
|
86
|
+
buildRepeatedContentModel(),
|
|
87
|
+
ovcWithThreePartners
|
|
88
|
+
);
|
|
89
|
+
const index = prerender.getDocumentsIndex();
|
|
90
|
+
const [hash] = Object.keys(index);
|
|
91
|
+
const rendered = prerender.getDocument(hash) as string;
|
|
92
|
+
|
|
93
|
+
expect(rendered).toContain("Hello Alice");
|
|
94
|
+
expect(rendered).toContain("Hello Bob");
|
|
95
|
+
expect(rendered).toContain("Hello Charlie");
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it("skips inactive occurrences of the linked multiple", () => {
|
|
99
|
+
const prerender = new Prerender(buildRepeatedContentModel("checkbox"), {
|
|
100
|
+
options: {
|
|
101
|
+
"2": [true, false, true],
|
|
102
|
+
},
|
|
103
|
+
variables: {
|
|
104
|
+
"3": ["Alice", "Bob", "Charlie"],
|
|
105
|
+
},
|
|
106
|
+
});
|
|
107
|
+
const index = prerender.getDocumentsIndex();
|
|
108
|
+
const [hash] = Object.keys(index);
|
|
109
|
+
const rendered = prerender.getDocument(hash) as string;
|
|
110
|
+
|
|
111
|
+
expect(rendered).toContain("Hello Alice");
|
|
112
|
+
expect(rendered).not.toContain("Hello Bob");
|
|
113
|
+
expect(rendered).toContain("Hello Charlie");
|
|
114
|
+
});
|
|
115
|
+
});
|