@pdfme/common 4.5.2-dev.2 → 4.5.2-dev.3
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 +82 -11
- package/dist/cjs/__tests__/helper.test.js +213 -62
- package/dist/cjs/__tests__/helper.test.js.map +1 -1
- package/dist/cjs/src/helper.js +84 -53
- package/dist/cjs/src/helper.js.map +1 -1
- package/dist/cjs/src/index.js.map +1 -1
- package/dist/cjs/src/schema.js +7 -6
- package/dist/cjs/src/schema.js.map +1 -1
- package/dist/esm/__tests__/helper.test.js +214 -63
- package/dist/esm/__tests__/helper.test.js.map +1 -1
- package/dist/esm/src/helper.js +80 -50
- package/dist/esm/src/helper.js.map +1 -1
- package/dist/esm/src/index.js.map +1 -1
- package/dist/esm/src/schema.js +6 -5
- package/dist/esm/src/schema.js.map +1 -1
- package/dist/types/src/helper.d.ts +7 -2
- package/dist/types/src/index.d.ts +2 -2
- package/dist/types/src/schema.d.ts +293 -49
- package/dist/types/src/types.d.ts +3 -4
- package/package.json +1 -1
- package/src/helper.ts +96 -50
- package/src/index.ts +2 -0
- package/src/schema.ts +7 -5
- package/src/types.ts +4 -3
@@ -4,7 +4,7 @@ import type { PDFPage, PDFDocument } from '@pdfme/pdf-lib';
|
|
4
4
|
import type { ThemeConfig, GlobalToken } from 'antd';
|
5
5
|
/** @ts-ignore -- optional interface, will gracefully degrade to `any` if `form-render` isn't installed*/
|
6
6
|
import type { WidgetProps as _PropPanelWidgetProps, Schema as _PropPanelSchema } from 'form-render';
|
7
|
-
import { Lang, Dict, Mode, Size, Schema, Font, SchemaForUI, BasePdf, BlankPdf, CommonOptions, Template, GeneratorOptions, GenerateProps, UIOptions, UIProps, PreviewProps, DesignerProps, ColorType } from './schema.js';
|
7
|
+
import { Lang, Dict, Mode, Size, Schema, Font, SchemaForUI, BasePdf, BlankPdf, CommonOptions, Template, GeneratorOptions, GenerateProps, UIOptions, UIProps, PreviewProps, DesignerProps, ColorType, LegacySchemaPageArray, SchemaPageArray } from './schema.js';
|
8
8
|
export type PropPanelSchema = _PropPanelSchema;
|
9
9
|
export type ChangeSchemaItem = {
|
10
10
|
key: string;
|
@@ -15,7 +15,6 @@ export type ChangeSchemas = (objs: ChangeSchemaItem[]) => void;
|
|
15
15
|
/**
|
16
16
|
* Properties used for PDF rendering.
|
17
17
|
* @template T Type of the extended Schema object.
|
18
|
-
* @property {string} key The key of the schema object.
|
19
18
|
* @property {string} value The string used for PDF rendering.
|
20
19
|
* @property {T} schema Extended Schema object for rendering.
|
21
20
|
* @property {BasePdf} basePdf Base PDF object for rendering.
|
@@ -26,7 +25,6 @@ export type ChangeSchemas = (objs: ChangeSchemaItem[]) => void;
|
|
26
25
|
* @property {Map<any, any>} _cache Cache shared only during the execution of the generate function (useful for caching images, etc. if needed).
|
27
26
|
*/
|
28
27
|
export interface PDFRenderProps<T extends Schema> {
|
29
|
-
key: string;
|
30
28
|
value: string;
|
31
29
|
schema: T;
|
32
30
|
basePdf: BasePdf;
|
@@ -63,7 +61,6 @@ export type UIRenderProps<T extends Schema> = {
|
|
63
61
|
tabIndex?: number;
|
64
62
|
placeholder?: string;
|
65
63
|
stopEditing?: () => void;
|
66
|
-
key: string;
|
67
64
|
value: string;
|
68
65
|
onChange?: (arg: {
|
69
66
|
key: string;
|
@@ -173,4 +170,6 @@ export type PreviewProps = z.infer<typeof PreviewProps> & {
|
|
173
170
|
export type DesignerProps = z.infer<typeof DesignerProps> & {
|
174
171
|
plugins?: Plugins;
|
175
172
|
};
|
173
|
+
export type SchemaPageArray = z.infer<typeof SchemaPageArray>;
|
174
|
+
export type LegacySchemaPageArray = z.infer<typeof LegacySchemaPageArray>;
|
176
175
|
export {};
|
package/package.json
CHANGED
package/src/helper.ts
CHANGED
@@ -1,6 +1,16 @@
|
|
1
1
|
import { z } from 'zod';
|
2
2
|
import { Buffer } from 'buffer';
|
3
|
-
import {
|
3
|
+
import {
|
4
|
+
Schema,
|
5
|
+
Template,
|
6
|
+
Font,
|
7
|
+
BasePdf,
|
8
|
+
Plugins,
|
9
|
+
BlankPdf,
|
10
|
+
CommonOptions,
|
11
|
+
LegacySchemaPageArray,
|
12
|
+
SchemaPageArray
|
13
|
+
} from './types';
|
4
14
|
import {
|
5
15
|
Inputs as InputsSchema,
|
6
16
|
UIOptions as UIOptionsSchema,
|
@@ -79,12 +89,34 @@ export const isHexValid = (hex: string): boolean => {
|
|
79
89
|
return /^#(?:[A-Fa-f0-9]{3,4}|[A-Fa-f0-9]{6}|[A-Fa-f0-9]{8})$/i.test(hex);
|
80
90
|
};
|
81
91
|
|
92
|
+
/**
|
93
|
+
* Migrate from legacy keyed object format to array format
|
94
|
+
* @param template Template
|
95
|
+
*/
|
96
|
+
export const migrateTemplate = (template: Template) => {
|
97
|
+
if (!template.schemas) {
|
98
|
+
return;
|
99
|
+
}
|
100
|
+
|
101
|
+
if (Array.isArray(template.schemas) && template.schemas.length > 0 && !Array.isArray(template.schemas[0])) {
|
102
|
+
template.schemas = (template.schemas as unknown as LegacySchemaPageArray).map(
|
103
|
+
(page: Record<string, Schema>) =>
|
104
|
+
Object.entries(page).map(([key, value]) => ({
|
105
|
+
...value,
|
106
|
+
name: key,
|
107
|
+
}))
|
108
|
+
);
|
109
|
+
}
|
110
|
+
};
|
111
|
+
|
82
112
|
export const getInputFromTemplate = (template: Template): { [key: string]: string }[] => {
|
113
|
+
migrateTemplate(template);
|
114
|
+
|
83
115
|
const input: { [key: string]: string } = {};
|
84
|
-
template.schemas.forEach(
|
85
|
-
|
86
|
-
if (!
|
87
|
-
input[
|
116
|
+
template.schemas.forEach(page => {
|
117
|
+
page.forEach(schema => {
|
118
|
+
if (!schema.readOnly) {
|
119
|
+
input[schema.name] = schema.content || '';
|
88
120
|
}
|
89
121
|
});
|
90
122
|
});
|
@@ -124,10 +156,10 @@ export const b64toUint8Array = (base64: string) => {
|
|
124
156
|
return unit8arr;
|
125
157
|
};
|
126
158
|
|
127
|
-
const getFontNamesInSchemas = (schemas:
|
159
|
+
const getFontNamesInSchemas = (schemas: SchemaPageArray) =>
|
128
160
|
uniq(
|
129
161
|
schemas
|
130
|
-
.map((
|
162
|
+
.map((p) => p.map((v) => (v as any).fontName ?? ''))
|
131
163
|
.reduce((acc, cur) => acc.concat(cur), [] as (string | undefined)[])
|
132
164
|
.filter(Boolean) as string[]
|
133
165
|
);
|
@@ -169,7 +201,7 @@ export const checkPlugins = (arg: { plugins: Plugins; template: Template }) => {
|
|
169
201
|
plugins,
|
170
202
|
template: { schemas },
|
171
203
|
} = arg;
|
172
|
-
const allSchemaTypes = uniq(schemas.map((
|
204
|
+
const allSchemaTypes = uniq(schemas.map((p) => p.map((v) => v.type)).flat());
|
173
205
|
|
174
206
|
const pluginsSchemaTypes = Object.values(plugins).map((p) => p?.propPanel.defaultSchema.type);
|
175
207
|
|
@@ -219,11 +251,24 @@ ${message}`);
|
|
219
251
|
|
220
252
|
export const checkInputs = (data: unknown) => checkProps(data, InputsSchema);
|
221
253
|
export const checkUIOptions = (data: unknown) => checkProps(data, UIOptionsSchema);
|
222
|
-
export const checkTemplate = (data: unknown) => checkProps(data, TemplateSchema);
|
223
|
-
export const checkUIProps = (data: unknown) => checkProps(data, UIPropsSchema);
|
224
254
|
export const checkPreviewProps = (data: unknown) => checkProps(data, PreviewPropsSchema);
|
225
255
|
export const checkDesignerProps = (data: unknown) => checkProps(data, DesignerPropsSchema);
|
226
|
-
export const
|
256
|
+
export const checkUIProps = (data: unknown) => {
|
257
|
+
if (typeof data === 'object' && data !== null && 'template' in data) {
|
258
|
+
migrateTemplate(data.template as Template);
|
259
|
+
}
|
260
|
+
checkProps(data, UIPropsSchema);
|
261
|
+
}
|
262
|
+
export const checkTemplate = (template: unknown) => {
|
263
|
+
migrateTemplate(template as Template);
|
264
|
+
checkProps(template, TemplateSchema);
|
265
|
+
}
|
266
|
+
export const checkGenerateProps = (data: unknown) => {
|
267
|
+
if (typeof data === 'object' && data !== null && 'template' in data) {
|
268
|
+
migrateTemplate(data.template as Template);
|
269
|
+
}
|
270
|
+
checkProps(data, GeneratePropsSchema);
|
271
|
+
}
|
227
272
|
|
228
273
|
interface ModifyTemplateForDynamicTableArg {
|
229
274
|
template: Template;
|
@@ -239,7 +284,6 @@ interface ModifyTemplateForDynamicTableArg {
|
|
239
284
|
class Node {
|
240
285
|
index = 0;
|
241
286
|
|
242
|
-
key?: string;
|
243
287
|
schema?: Schema;
|
244
288
|
|
245
289
|
children: Node[] = [];
|
@@ -258,8 +302,7 @@ class Node {
|
|
258
302
|
this.index = index;
|
259
303
|
}
|
260
304
|
|
261
|
-
|
262
|
-
this.key = key;
|
305
|
+
setSchema(schema: Schema): void {
|
263
306
|
this.schema = schema;
|
264
307
|
}
|
265
308
|
|
@@ -301,24 +344,23 @@ function createPage(basePdf: BlankPdf) {
|
|
301
344
|
}
|
302
345
|
|
303
346
|
function createNode(arg: {
|
304
|
-
key: string;
|
305
347
|
schema: Schema;
|
306
348
|
position: { x: number; y: number };
|
307
349
|
width: number;
|
308
350
|
height: number;
|
309
351
|
}) {
|
310
|
-
const { position, width, height,
|
352
|
+
const { position, width, height, schema } = arg;
|
311
353
|
const node = new Node({ width, height });
|
312
354
|
node.setPosition(position);
|
313
|
-
node.
|
355
|
+
node.setSchema(schema);
|
314
356
|
return node;
|
315
357
|
}
|
316
358
|
|
317
359
|
function resortChildren(page: Node, orderMap: Map<string, number>): void {
|
318
360
|
page.children = page.children
|
319
361
|
.sort((a, b) => {
|
320
|
-
const orderA = orderMap.get(a.
|
321
|
-
const orderB = orderMap.get(b.
|
362
|
+
const orderA = orderMap.get(a.schema?.name!);
|
363
|
+
const orderB = orderMap.get(b.schema?.name!);
|
322
364
|
if (orderA === undefined || orderB === undefined) {
|
323
365
|
throw new Error('[@pdfme/common] order is not defined');
|
324
366
|
}
|
@@ -333,23 +375,21 @@ function resortChildren(page: Node, orderMap: Map<string, number>): void {
|
|
333
375
|
async function createOnePage(
|
334
376
|
arg: {
|
335
377
|
basePdf: BlankPdf;
|
336
|
-
|
378
|
+
schemaPage: Schema[];
|
337
379
|
orderMap: Map<string, number>;
|
338
380
|
} & Omit<ModifyTemplateForDynamicTableArg, 'template'>
|
339
381
|
): Promise<Node> {
|
340
|
-
const { basePdf,
|
382
|
+
const { basePdf, schemaPage, orderMap, input, options, _cache, getDynamicHeights } = arg;
|
341
383
|
const page = createPage(basePdf);
|
342
384
|
|
343
385
|
const schemaPositions: number[] = [];
|
344
|
-
const sortedSchemaEntries =
|
345
|
-
(a, b) => a[1].position.y - b[1].position.y
|
346
|
-
);
|
386
|
+
const sortedSchemaEntries = cloneDeep(schemaPage).sort((a, b) => a.position.y - b.position.y);
|
347
387
|
const diffMap = new Map();
|
348
|
-
for (const
|
388
|
+
for (const schema of sortedSchemaEntries) {
|
349
389
|
const { position, width } = schema;
|
350
390
|
|
351
391
|
const opt = { schema, basePdf, options, _cache };
|
352
|
-
const value = (schema.readOnly ? schema.content : input?.[
|
392
|
+
const value = (schema.readOnly ? schema.content : input?.[schema.name]) || '';
|
353
393
|
const heights = await getDynamicHeights(value, opt);
|
354
394
|
|
355
395
|
const heightsSum = heights.reduce((acc, cur) => acc + cur, 0);
|
@@ -364,7 +404,7 @@ async function createOnePage(
|
|
364
404
|
y += diff;
|
365
405
|
}
|
366
406
|
}
|
367
|
-
const node = createNode({
|
407
|
+
const node = createNode({ schema, position: { ...position, y }, width, height });
|
368
408
|
|
369
409
|
schemaPositions.push(y + height + basePdf.padding[2]);
|
370
410
|
page.insertChild(node);
|
@@ -406,7 +446,7 @@ function breakIntoPages(arg: {
|
|
406
446
|
|
407
447
|
const children = longPage.children.sort((a, b) => a.position.y - b.position.y);
|
408
448
|
for (let i = 0; i < children.length; i++) {
|
409
|
-
const {
|
449
|
+
const { schema, position, height, width } = children[i];
|
410
450
|
const { y, x } = position;
|
411
451
|
|
412
452
|
let targetPageIndex = Math.floor(y / getPageHeight(pages.length - 1));
|
@@ -417,9 +457,9 @@ function breakIntoPages(arg: {
|
|
417
457
|
newY = calculateNewY(y, targetPageIndex);
|
418
458
|
}
|
419
459
|
|
420
|
-
if (!
|
460
|
+
if (!schema) throw new Error('[@pdfme/common] schema is undefined');
|
421
461
|
|
422
|
-
const clonedElement = createNode({
|
462
|
+
const clonedElement = createNode({ schema, position: { x, y: newY }, width, height });
|
423
463
|
pages[targetPageIndex].insertChild(clonedElement);
|
424
464
|
}
|
425
465
|
|
@@ -430,46 +470,53 @@ function breakIntoPages(arg: {
|
|
430
470
|
|
431
471
|
function createNewTemplate(pages: Node[], basePdf: BlankPdf): Template {
|
432
472
|
const newTemplate: Template = {
|
433
|
-
schemas: Array.from({ length: pages.length }, () => (
|
473
|
+
schemas: Array.from({ length: pages.length }, () => ([] as Schema[])),
|
434
474
|
basePdf: basePdf,
|
435
475
|
};
|
436
476
|
|
437
|
-
const
|
477
|
+
const nameToSchemas = new Map<string, Node[]>();
|
438
478
|
|
439
479
|
cloneDeep(pages).forEach((page, pageIndex) => {
|
440
480
|
page.children.forEach((child) => {
|
441
|
-
const {
|
442
|
-
if (!
|
481
|
+
const { schema } = child;
|
482
|
+
if (!schema) throw new Error('[@pdfme/common] schema is undefined');
|
443
483
|
|
444
|
-
|
445
|
-
|
484
|
+
const name = schema.name
|
485
|
+
if (!nameToSchemas.has(name)) {
|
486
|
+
nameToSchemas.set(name, []);
|
446
487
|
}
|
447
|
-
|
488
|
+
nameToSchemas.get(name)!.push(child);
|
448
489
|
|
449
|
-
const
|
450
|
-
const start =
|
490
|
+
const sameNameSchemas = page.children.filter((c) => c.schema?.name === name);
|
491
|
+
const start = nameToSchemas.get(name)!.length - sameNameSchemas.length;
|
451
492
|
|
452
|
-
if (
|
453
|
-
if (!
|
493
|
+
if (sameNameSchemas.length > 0) {
|
494
|
+
if (!sameNameSchemas[0].schema) {
|
454
495
|
throw new Error('[@pdfme/common] schema is undefined');
|
455
496
|
}
|
456
497
|
|
457
498
|
// Use the first schema to get the schema and position
|
458
|
-
const schema =
|
459
|
-
const height =
|
460
|
-
const position =
|
499
|
+
const schema = sameNameSchemas[0].schema;
|
500
|
+
const height = sameNameSchemas.reduce((acc, cur) => acc + cur.height, 0);
|
501
|
+
const position = sameNameSchemas[0].position;
|
461
502
|
|
462
503
|
// Currently, __bodyRange exists for table schemas, but if we make it more abstract,
|
463
504
|
// it could be used for other schemas as well to render schemas that have been split by page breaks, starting from the middle.
|
464
505
|
schema.__bodyRange = {
|
465
506
|
start: Math.max(start - 1, 0),
|
466
|
-
end: start +
|
507
|
+
end: start + sameNameSchemas.length - 1,
|
467
508
|
};
|
468
509
|
|
469
510
|
// Currently, this is used to determine whether to display the header when a table is split.
|
470
511
|
schema.__isSplit = start > 0;
|
471
512
|
|
472
|
-
|
513
|
+
const newSchema = Object.assign({}, schema, { position, height });
|
514
|
+
const index = newTemplate.schemas[pageIndex].findIndex((s) => s.name === name);
|
515
|
+
if (index !== -1) {
|
516
|
+
newTemplate.schemas[pageIndex][index] = newSchema;
|
517
|
+
} else {
|
518
|
+
newTemplate.schemas[pageIndex].push(newSchema);
|
519
|
+
}
|
473
520
|
}
|
474
521
|
});
|
475
522
|
});
|
@@ -488,10 +535,9 @@ export const getDynamicTemplate = async (
|
|
488
535
|
const basePdf = template.basePdf as BlankPdf;
|
489
536
|
const pages: Node[] = [];
|
490
537
|
|
491
|
-
for (const
|
492
|
-
const orderMap = new Map(
|
493
|
-
|
494
|
-
const longPage = await createOnePage({ basePdf, schemaObj, orderMap, ...arg });
|
538
|
+
for (const schemaPage of template.schemas) {
|
539
|
+
const orderMap = new Map(schemaPage.map((schema, index) => [schema.name, index]));
|
540
|
+
const longPage = await createOnePage({ basePdf, schemaPage, orderMap, ...arg });
|
495
541
|
const brokenPages = breakIntoPages({ longPage, basePdf, orderMap });
|
496
542
|
pages.push(...brokenPages);
|
497
543
|
}
|
package/src/index.ts
CHANGED
@@ -10,6 +10,7 @@ import {
|
|
10
10
|
import type {
|
11
11
|
ChangeSchemaItem,
|
12
12
|
ChangeSchemas,
|
13
|
+
SchemaPageArray,
|
13
14
|
PropPanel,
|
14
15
|
PropPanelSchema,
|
15
16
|
PropPanelWidgetProps,
|
@@ -112,6 +113,7 @@ export type {
|
|
112
113
|
DesignerProps,
|
113
114
|
ChangeSchemaItem,
|
114
115
|
ChangeSchemas,
|
116
|
+
SchemaPageArray,
|
115
117
|
PropPanel,
|
116
118
|
PropPanelSchema,
|
117
119
|
PropPanelWidgetProps,
|
package/src/schema.ts
CHANGED
@@ -84,6 +84,7 @@ export const Size = z.object({ height: z.number(), width: z.number() });
|
|
84
84
|
|
85
85
|
export const Schema = z
|
86
86
|
.object({
|
87
|
+
name: z.string(),
|
87
88
|
type: z.string(),
|
88
89
|
content: z.string().optional(),
|
89
90
|
position: z.object({ x: z.number(), y: z.number() }),
|
@@ -98,10 +99,7 @@ export const Schema = z
|
|
98
99
|
})
|
99
100
|
.passthrough();
|
100
101
|
|
101
|
-
const SchemaForUIAdditionalInfo = z.object({
|
102
|
-
id: z.string(),
|
103
|
-
key: z.string(),
|
104
|
-
});
|
102
|
+
const SchemaForUIAdditionalInfo = z.object({ id: z.string() });
|
105
103
|
export const SchemaForUI = Schema.merge(SchemaForUIAdditionalInfo);
|
106
104
|
|
107
105
|
const ArrayBufferSchema: z.ZodSchema<ArrayBuffer> = z.any().refine((v) => v instanceof ArrayBuffer);
|
@@ -117,9 +115,13 @@ const CustomPdf = z.union([z.string(), ArrayBufferSchema, Uint8ArraySchema]);
|
|
117
115
|
|
118
116
|
export const BasePdf = z.union([CustomPdf, BlankPdf]);
|
119
117
|
|
118
|
+
// Legacy keyed structure for BC - we convert to SchemaPageArray on import
|
119
|
+
export const LegacySchemaPageArray = z.array(z.record(Schema));
|
120
|
+
export const SchemaPageArray = z.array(z.array(Schema));
|
121
|
+
|
120
122
|
export const Template = z
|
121
123
|
.object({
|
122
|
-
schemas:
|
124
|
+
schemas: SchemaPageArray,
|
123
125
|
basePdf: BasePdf,
|
124
126
|
pdfmeVersion: z.string().optional(),
|
125
127
|
})
|
package/src/types.ts
CHANGED
@@ -23,6 +23,8 @@ import {
|
|
23
23
|
PreviewProps,
|
24
24
|
DesignerProps,
|
25
25
|
ColorType,
|
26
|
+
LegacySchemaPageArray,
|
27
|
+
SchemaPageArray,
|
26
28
|
} from './schema.js';
|
27
29
|
|
28
30
|
export type PropPanelSchema = _PropPanelSchema;
|
@@ -36,7 +38,6 @@ export type ChangeSchemas = (objs: ChangeSchemaItem[]) => void;
|
|
36
38
|
/**
|
37
39
|
* Properties used for PDF rendering.
|
38
40
|
* @template T Type of the extended Schema object.
|
39
|
-
* @property {string} key The key of the schema object.
|
40
41
|
* @property {string} value The string used for PDF rendering.
|
41
42
|
* @property {T} schema Extended Schema object for rendering.
|
42
43
|
* @property {BasePdf} basePdf Base PDF object for rendering.
|
@@ -47,7 +48,6 @@ export type ChangeSchemas = (objs: ChangeSchemaItem[]) => void;
|
|
47
48
|
* @property {Map<any, any>} _cache Cache shared only during the execution of the generate function (useful for caching images, etc. if needed).
|
48
49
|
*/
|
49
50
|
export interface PDFRenderProps<T extends Schema> {
|
50
|
-
key: string;
|
51
51
|
value: string;
|
52
52
|
schema: T;
|
53
53
|
basePdf: BasePdf;
|
@@ -86,7 +86,6 @@ export type UIRenderProps<T extends Schema> = {
|
|
86
86
|
tabIndex?: number;
|
87
87
|
placeholder?: string;
|
88
88
|
stopEditing?: () => void;
|
89
|
-
key: string;
|
90
89
|
value: string;
|
91
90
|
onChange?: (arg: { key: string; value: any } | { key: string; value: any }[]) => void;
|
92
91
|
rootElement: HTMLDivElement;
|
@@ -186,3 +185,5 @@ export type UIOptions = z.infer<typeof UIOptions> & { theme?: ThemeConfig };
|
|
186
185
|
export type UIProps = z.infer<typeof UIProps> & { plugins?: Plugins };
|
187
186
|
export type PreviewProps = z.infer<typeof PreviewProps> & { plugins?: Plugins };
|
188
187
|
export type DesignerProps = z.infer<typeof DesignerProps> & { plugins?: Plugins };
|
188
|
+
export type SchemaPageArray = z.infer<typeof SchemaPageArray>;
|
189
|
+
export type LegacySchemaPageArray = z.infer<typeof LegacySchemaPageArray>;
|