@formio/uag 2.0.0-rc.1 → 2.0.0-rc.10

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 (96) hide show
  1. package/README.md +17 -15
  2. package/bin/uag-eval.js +7 -3
  3. package/bin/uag.js +48 -5
  4. package/lib/UAGFormInterface.d.ts +12 -0
  5. package/lib/UAGFormInterface.js +55 -12
  6. package/lib/UAGProjectInterface.d.ts +9 -1
  7. package/lib/UAGProjectInterface.js +25 -7
  8. package/lib/actionLogs.d.ts +1 -10
  9. package/lib/actionLogs.js +5 -3
  10. package/lib/actions/runTask.js +4 -3
  11. package/lib/agent/anonymous.js +5 -4
  12. package/lib/agent/chat.d.ts +3 -2
  13. package/lib/agent/chat.js +35 -99
  14. package/lib/agent/engines.d.ts +10 -6
  15. package/lib/agent/engines.js +19 -10
  16. package/lib/agent/errors.d.ts +25 -0
  17. package/lib/agent/errors.js +132 -0
  18. package/lib/agent/index.d.ts +1 -1
  19. package/lib/agent/index.js +2 -1
  20. package/lib/agent/remote.d.ts +5 -1
  21. package/lib/agent/remote.js +25 -2
  22. package/lib/agent/router.d.ts +32 -2
  23. package/lib/agent/router.js +37 -25
  24. package/lib/agent/stubs.d.ts +13 -2
  25. package/lib/agent/stubs.js +18 -5
  26. package/lib/agent/tools.d.ts +24 -3
  27. package/lib/agent/tools.js +47 -4
  28. package/lib/config.d.ts +31 -4
  29. package/lib/database.d.ts +3 -8
  30. package/lib/database.js +2 -19
  31. package/lib/documents/convert.d.ts +38 -0
  32. package/lib/documents/convert.js +151 -0
  33. package/lib/documents/converters.d.ts +28 -0
  34. package/lib/documents/converters.js +149 -0
  35. package/lib/documents/detect.d.ts +31 -0
  36. package/lib/documents/detect.js +169 -0
  37. package/lib/documents/index.d.ts +9 -0
  38. package/lib/documents/index.js +43 -0
  39. package/lib/documents/mime.d.ts +39 -0
  40. package/lib/documents/mime.js +100 -0
  41. package/lib/documents/plugins.d.ts +18 -0
  42. package/lib/documents/plugins.js +65 -0
  43. package/lib/documents/readers.d.ts +19 -0
  44. package/lib/documents/readers.js +64 -0
  45. package/lib/documents/routes.d.ts +52 -0
  46. package/lib/documents/routes.js +99 -0
  47. package/lib/documents/types.d.ts +126 -0
  48. package/lib/documents/types.js +2 -0
  49. package/lib/documents/uagJs.d.ts +17 -0
  50. package/lib/documents/uagJs.js +157 -0
  51. package/lib/formflow.d.ts +11 -4
  52. package/lib/formflow.js +5 -5
  53. package/lib/index.d.ts +65 -9
  54. package/lib/index.js +228 -26
  55. package/lib/markdown.d.ts +1 -0
  56. package/lib/markdown.js +166 -0
  57. package/lib/modules/api.d.ts +25 -0
  58. package/lib/modules/api.js +98 -0
  59. package/lib/modules/index.d.ts +4 -0
  60. package/lib/modules/index.js +24 -0
  61. package/lib/modules/routes.d.ts +14 -0
  62. package/lib/modules/routes.js +21 -0
  63. package/lib/modules/types.d.ts +68 -0
  64. package/lib/modules/types.js +5 -0
  65. package/lib/pluginFiles.d.ts +28 -0
  66. package/lib/pluginFiles.js +101 -0
  67. package/lib/plugins.d.ts +17 -0
  68. package/lib/plugins.js +93 -0
  69. package/lib/richText.d.ts +20 -0
  70. package/lib/richText.js +99 -0
  71. package/lib/router.d.ts +9 -0
  72. package/lib/router.js +19 -0
  73. package/lib/runner.d.ts +2 -0
  74. package/lib/runner.js +11 -3
  75. package/lib/runs.d.ts +17 -79
  76. package/lib/runs.js +10 -71
  77. package/lib/testing.d.ts +74 -0
  78. package/lib/testing.js +180 -0
  79. package/lib/tools/SchemaBuilder.d.ts +0 -5
  80. package/lib/tools/SchemaBuilder.js +0 -8
  81. package/lib/tools/agentDecide.js +2 -2
  82. package/lib/tools/agentProvide.d.ts +0 -5
  83. package/lib/tools/agentProvide.js +7 -40
  84. package/lib/tools/collectData.js +2 -1
  85. package/lib/tools/index.d.ts +3 -2
  86. package/lib/tools/index.js +5 -5
  87. package/lib/tools/readDocument.d.ts +17 -0
  88. package/lib/tools/readDocument.js +99 -0
  89. package/lib/tools/submissionUpdate.js +3 -2
  90. package/lib/tools/utils.d.ts +40 -0
  91. package/lib/tools/utils.js +16 -1
  92. package/lib/version.d.ts +2 -0
  93. package/lib/version.js +5 -0
  94. package/package.json +17 -10
  95. package/testing.d.ts +1 -0
  96. package/testing.js +2 -0
@@ -0,0 +1,28 @@
1
+ import type { Converter } from './types';
2
+ /** unpdf, loaded on first use: it is an ES module, and carries a PDF.js build. */
3
+ export declare function loadUnpdf(): Promise<typeof import('unpdf')>;
4
+ /** pdf-text: a PDF's text layer, page by page, with unpdf. */
5
+ export declare const pdfTextConverter: Converter;
6
+ /** Decode text: UTF-16 with a byte order mark, else UTF-8 (without its BOM), else Windows-1252 (a superset of Latin-1). */
7
+ export declare function decodeText(buffer: Buffer): string;
8
+ /** text: plain text, Markdown, CSV, JSON, HTML (as Markdown) and XML. */
9
+ export declare const textConverter: Converter;
10
+ /**
11
+ * The most pixels of a PNG image-pdf takes (50 megapixels, about 200 MB once
12
+ * decoded). A PNG is decoded whole, in memory and synchronously, so its pixel
13
+ * count is checked from its header first: a small, highly compressed file can
14
+ * describe an image far larger than its size suggests. JPEGs are embedded
15
+ * without being decoded. The converter's `maxPixels` setting changes it
16
+ * (config.plugins.settings.converter['image-pdf'].maxPixels, or
17
+ * DOCUMENTS_MAX_IMAGE_PIXELS).
18
+ */
19
+ export declare const MAX_IMAGE_PIXELS = 50000000;
20
+ /** A PNG's width and height, from its IHDR header; undefined when it has none. */
21
+ export declare function pngSize(buffer: Buffer): {
22
+ width: number;
23
+ height: number;
24
+ } | undefined;
25
+ /** image-pdf: PNG and JPEG images into a PDF, one page per image, scaled to fit and never resampled. */
26
+ export declare const imagePdfConverter: Converter;
27
+ /** The converters every UAG has. None needs a system binary. */
28
+ export declare const BUILT_IN_CONVERTERS: Converter[];
@@ -0,0 +1,149 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BUILT_IN_CONVERTERS = exports.imagePdfConverter = exports.MAX_IMAGE_PIXELS = exports.textConverter = exports.pdfTextConverter = void 0;
4
+ exports.loadUnpdf = loadUnpdf;
5
+ exports.decodeText = decodeText;
6
+ exports.pngSize = pngSize;
7
+ const node_html_markdown_1 = require("node-html-markdown");
8
+ const mime_1 = require("./mime");
9
+ const convert_1 = require("./convert");
10
+ const version_1 = require("../version");
11
+ /**
12
+ * A real dynamic import: TypeScript compiles `import()` in a CommonJS build to
13
+ * `require()`, which cannot load an ES module before Node 22.12.
14
+ */
15
+ const esmImport = new Function('specifier', 'return import(specifier)');
16
+ /** unpdf, loaded on first use: it is an ES module, and carries a PDF.js build. */
17
+ function loadUnpdf() {
18
+ return esmImport('unpdf');
19
+ }
20
+ /** pdf-text: a PDF's text layer, page by page, with unpdf. */
21
+ exports.pdfTextConverter = {
22
+ type: 'converter',
23
+ name: 'pdf-text',
24
+ title: 'PDF text',
25
+ description: 'The text layer of a PDF, page by page. A PDF without one (a scan) yields empty text and a warning.',
26
+ version: version_1.UAG_VERSION,
27
+ apiVersion: 1,
28
+ accepts: [{ type: mime_1.MIME.pdf, ext: ['.pdf'] }],
29
+ produces: ['text'],
30
+ async convert(input, options) {
31
+ options.signal.throwIfAborted();
32
+ const { extractText, getDocumentProxy } = await loadUnpdf();
33
+ const document = await getDocumentProxy(new Uint8Array(input.buffer));
34
+ const { totalPages, text } = await extractText(document, { mergePages: false });
35
+ const pages = text.map((pageText, i) => ({ page: i + 1, text: String(pageText || '').trim() }));
36
+ const all = pages.map((page) => page.text).join('\n\n').trim();
37
+ return {
38
+ text: all,
39
+ textFormat: 'plain',
40
+ pages,
41
+ ...(!all && { warnings: [`"${input.name}" has no text layer (${totalPages} page${totalPages === 1 ? '' : 's'}): it may be a scan.`] })
42
+ };
43
+ }
44
+ };
45
+ /** Decode text: UTF-16 with a byte order mark, else UTF-8 (without its BOM), else Windows-1252 (a superset of Latin-1). */
46
+ function decodeText(buffer) {
47
+ if (buffer[0] === 0xff && buffer[1] === 0xfe)
48
+ return new TextDecoder('utf-16le').decode(buffer.subarray(2));
49
+ if (buffer[0] === 0xfe && buffer[1] === 0xff)
50
+ return new TextDecoder('utf-16be').decode(buffer.subarray(2));
51
+ try {
52
+ return new TextDecoder('utf-8', { fatal: true }).decode(buffer).replace(/^\uFEFF/, '');
53
+ }
54
+ catch {
55
+ return new TextDecoder('windows-1252').decode(buffer);
56
+ }
57
+ }
58
+ const TEXT_FORMATS = {
59
+ [mime_1.MIME.markdown]: 'markdown',
60
+ [mime_1.MIME.html]: 'markdown',
61
+ [mime_1.MIME.csv]: 'csv'
62
+ };
63
+ /** text: plain text, Markdown, CSV, JSON, HTML (as Markdown) and XML. */
64
+ exports.textConverter = {
65
+ type: 'converter',
66
+ name: 'text',
67
+ title: 'Text',
68
+ description: 'Text files as they are; HTML as Markdown.',
69
+ version: version_1.UAG_VERSION,
70
+ apiVersion: 1,
71
+ accepts: [
72
+ { type: mime_1.MIME.text, ext: ['.txt'] },
73
+ { type: mime_1.MIME.markdown, ext: ['.md', '.markdown'] },
74
+ { type: mime_1.MIME.csv, ext: ['.csv'] },
75
+ { type: mime_1.MIME.json, ext: ['.json'] },
76
+ { type: mime_1.MIME.html, ext: ['.html', '.htm'] },
77
+ { type: mime_1.MIME.xml, ext: ['.xml'] }
78
+ ],
79
+ produces: ['text'],
80
+ async convert(input, options) {
81
+ options.signal.throwIfAborted();
82
+ const decoded = decodeText(input.buffer);
83
+ const text = input.type === mime_1.MIME.html ? node_html_markdown_1.NodeHtmlMarkdown.translate(decoded) : decoded;
84
+ return { text, textFormat: TEXT_FORMATS[input.type] || 'plain' };
85
+ }
86
+ };
87
+ /** A4 portrait, in points. */
88
+ const PAGE = { width: 595.28, height: 841.89, margin: 36 };
89
+ /**
90
+ * The most pixels of a PNG image-pdf takes (50 megapixels, about 200 MB once
91
+ * decoded). A PNG is decoded whole, in memory and synchronously, so its pixel
92
+ * count is checked from its header first: a small, highly compressed file can
93
+ * describe an image far larger than its size suggests. JPEGs are embedded
94
+ * without being decoded. The converter's `maxPixels` setting changes it
95
+ * (config.plugins.settings.converter['image-pdf'].maxPixels, or
96
+ * DOCUMENTS_MAX_IMAGE_PIXELS).
97
+ */
98
+ exports.MAX_IMAGE_PIXELS = 50000000;
99
+ /** A PNG's width and height, from its IHDR header; undefined when it has none. */
100
+ function pngSize(buffer) {
101
+ if (buffer.length < 24 || buffer.toString('latin1', 12, 16) !== 'IHDR')
102
+ return undefined;
103
+ return { width: buffer.readUInt32BE(16), height: buffer.readUInt32BE(20) };
104
+ }
105
+ /** image-pdf: PNG and JPEG images into a PDF, one page per image, scaled to fit and never resampled. */
106
+ exports.imagePdfConverter = {
107
+ type: 'converter',
108
+ name: 'image-pdf',
109
+ title: 'Image to PDF',
110
+ description: 'A PNG or JPEG image as a one-page PDF, scaled to fit the page.',
111
+ version: version_1.UAG_VERSION,
112
+ apiVersion: 1,
113
+ accepts: [{ type: mime_1.MIME.png, ext: ['.png'] }, { type: mime_1.MIME.jpeg, ext: ['.jpg', '.jpeg'] }],
114
+ produces: ['pdf'],
115
+ settings: {
116
+ maxPixels: {
117
+ type: 'number',
118
+ env: 'DOCUMENTS_MAX_IMAGE_PIXELS',
119
+ default: exports.MAX_IMAGE_PIXELS,
120
+ description: 'The largest PNG, in pixels (width × height), converted; a larger one is refused before it is decoded.'
121
+ }
122
+ },
123
+ async convert(input, options) {
124
+ options.signal.throwIfAborted();
125
+ if (input.type === mime_1.MIME.png) {
126
+ const size = pngSize(input.buffer);
127
+ if (!size)
128
+ throw new convert_1.ConversionError(`"${input.name}" is not a PNG image the image-pdf converter can read.`);
129
+ const maxPixels = Number(options.settings?.maxPixels) > 0 ? Number(options.settings.maxPixels) : exports.MAX_IMAGE_PIXELS;
130
+ if (size.width * size.height > maxPixels) {
131
+ throw new convert_1.ConversionError(`"${input.name}" is ${size.width}×${size.height} pixels, more than the image-pdf converter takes (${maxPixels} pixels).`);
132
+ }
133
+ }
134
+ const { PDFDocument } = require('pdf-lib');
135
+ const pdf = await PDFDocument.create();
136
+ const image = input.type === mime_1.MIME.png ? await pdf.embedPng(input.buffer) : await pdf.embedJpg(input.buffer);
137
+ const landscape = image.width > image.height;
138
+ const width = landscape ? PAGE.height : PAGE.width;
139
+ const height = landscape ? PAGE.width : PAGE.height;
140
+ const scale = Math.min(1, (width - 2 * PAGE.margin) / image.width, (height - 2 * PAGE.margin) / image.height);
141
+ const page = pdf.addPage([width, height]);
142
+ const drawn = { width: image.width * scale, height: image.height * scale };
143
+ // The image is embedded as it is and only drawn smaller: its pixels are never resampled.
144
+ page.drawImage(image, { x: (width - drawn.width) / 2, y: (height - drawn.height) / 2, ...drawn });
145
+ return { pdf: Buffer.from(await pdf.save()), pages: 1 };
146
+ }
147
+ };
148
+ /** The converters every UAG has. None needs a system binary. */
149
+ exports.BUILT_IN_CONVERTERS = [exports.pdfTextConverter, exports.textConverter, exports.imagePdfConverter];
@@ -0,0 +1,31 @@
1
+ import type { Converter } from './types';
2
+ /** A file refused for what its bytes are. The message names the file, never its content. */
3
+ export declare class DetectionError extends Error {
4
+ constructor(message: string);
5
+ }
6
+ /**
7
+ * The names in a ZIP archive's central directory, read from the end of the
8
+ * buffer only: no entry is ever inflated. Undefined when it is not a readable
9
+ * ZIP archive.
10
+ */
11
+ export declare function zipEntryNames(buffer: Buffer): string[] | undefined;
12
+ /** Whether a buffer is valid UTF-8 (or UTF-16 with a byte order mark) without NUL bytes. */
13
+ export declare function isText(buffer: Buffer): boolean;
14
+ /**
15
+ * Whether a buffer is text in a legacy 8-bit encoding (Latin-1, Windows-1252):
16
+ * no NUL bytes and no control characters other than tab, line breaks, form feed
17
+ * and the DOS end-of-file mark. Spreadsheet and editor exports still write these.
18
+ */
19
+ export declare function isLegacyText(buffer: Buffer): boolean;
20
+ /**
21
+ * Identify a file's type from its bytes. The name and the client's MIME type
22
+ * are hints only: known signatures first, then each converter's `detect`, then
23
+ * text (typed by extension). A detected type that contradicts the family the
24
+ * extension promises is refused.
25
+ */
26
+ export declare function identify(buffer: Buffer, hint: {
27
+ name: string;
28
+ type?: string;
29
+ }, converters?: Converter[]): string;
30
+ /** The extensions known for a type, from the converters that accept it and the usual ones. */
31
+ export declare function extensionsOf(type: string, converters?: Converter[]): string[];
@@ -0,0 +1,169 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DetectionError = void 0;
4
+ exports.zipEntryNames = zipEntryNames;
5
+ exports.isText = isText;
6
+ exports.isLegacyText = isLegacyText;
7
+ exports.identify = identify;
8
+ exports.extensionsOf = extensionsOf;
9
+ const mime_1 = require("./mime");
10
+ /** A file refused for what its bytes are. The message names the file, never its content. */
11
+ class DetectionError extends Error {
12
+ constructor(message) {
13
+ super(message);
14
+ this.name = 'DetectionError';
15
+ }
16
+ }
17
+ exports.DetectionError = DetectionError;
18
+ const startsWith = (buffer, bytes, offset = 0) => {
19
+ const expected = typeof bytes === 'string' ? Buffer.from(bytes, 'latin1') : Buffer.from(bytes);
20
+ return buffer.length >= offset + expected.length && buffer.subarray(offset, offset + expected.length).equals(expected);
21
+ };
22
+ /**
23
+ * The names in a ZIP archive's central directory, read from the end of the
24
+ * buffer only: no entry is ever inflated. Undefined when it is not a readable
25
+ * ZIP archive.
26
+ */
27
+ function zipEntryNames(buffer) {
28
+ // End of central directory: 22 bytes, plus a comment of up to 65535.
29
+ const start = Math.max(0, buffer.length - 22 - 0xffff);
30
+ let end = -1;
31
+ for (let i = buffer.length - 22; i >= start; i--) {
32
+ if (buffer.readUInt32LE(i) === 0x06054b50) {
33
+ end = i;
34
+ break;
35
+ }
36
+ }
37
+ if (end < 0)
38
+ return undefined;
39
+ const count = buffer.readUInt16LE(end + 10);
40
+ const size = buffer.readUInt32LE(end + 12);
41
+ let offset = buffer.readUInt32LE(end + 16);
42
+ if (offset + size > end)
43
+ return undefined;
44
+ const names = [];
45
+ for (let i = 0; i < count && offset + 46 <= end; i++) {
46
+ if (buffer.readUInt32LE(offset) !== 0x02014b50)
47
+ return undefined;
48
+ const nameLength = buffer.readUInt16LE(offset + 28);
49
+ const extraLength = buffer.readUInt16LE(offset + 30);
50
+ const commentLength = buffer.readUInt16LE(offset + 32);
51
+ names.push(buffer.subarray(offset + 46, offset + 46 + nameLength).toString('utf8'));
52
+ offset += 46 + nameLength + extraLength + commentLength;
53
+ }
54
+ return names;
55
+ }
56
+ /** An ODF document's type, from its `mimetype` entry when stored uncompressed first, as ODF requires. */
57
+ function odfType(buffer) {
58
+ if (buffer.readUInt32LE(0) !== 0x04034b50)
59
+ return undefined;
60
+ const method = buffer.readUInt16LE(8);
61
+ const size = buffer.readUInt32LE(18);
62
+ const nameLength = buffer.readUInt16LE(26);
63
+ const extraLength = buffer.readUInt16LE(28);
64
+ const name = buffer.subarray(30, 30 + nameLength).toString('utf8');
65
+ if (method !== 0 || name !== 'mimetype' || size > 200)
66
+ return undefined;
67
+ const type = buffer.subarray(30 + nameLength + extraLength, 30 + nameLength + extraLength + size).toString('ascii').trim();
68
+ return /^application\/vnd\.oasis\.opendocument\.[a-z.-]+$/.test(type) ? type : undefined;
69
+ }
70
+ function zipType(buffer) {
71
+ const names = zipEntryNames(buffer);
72
+ if (!names)
73
+ return mime_1.MIME.zip;
74
+ const has = new Set(names);
75
+ if (has.has('word/document.xml'))
76
+ return mime_1.MIME.docx;
77
+ if (has.has('xl/workbook.xml'))
78
+ return mime_1.MIME.xlsx;
79
+ if (has.has('ppt/presentation.xml'))
80
+ return mime_1.MIME.pptx;
81
+ if (has.has('mimetype'))
82
+ return odfType(buffer) || mime_1.MIME.zip;
83
+ return mime_1.MIME.zip;
84
+ }
85
+ /** Whether a buffer is valid UTF-8 (or UTF-16 with a byte order mark) without NUL bytes. */
86
+ function isText(buffer) {
87
+ if (startsWith(buffer, [0xff, 0xfe]) || startsWith(buffer, [0xfe, 0xff])) {
88
+ return buffer.length % 2 === 0;
89
+ }
90
+ if (buffer.includes(0))
91
+ return false;
92
+ try {
93
+ new TextDecoder('utf-8', { fatal: true }).decode(buffer);
94
+ return true;
95
+ }
96
+ catch {
97
+ return false;
98
+ }
99
+ }
100
+ /**
101
+ * Whether a buffer is text in a legacy 8-bit encoding (Latin-1, Windows-1252):
102
+ * no NUL bytes and no control characters other than tab, line breaks, form feed
103
+ * and the DOS end-of-file mark. Spreadsheet and editor exports still write these.
104
+ */
105
+ function isLegacyText(buffer) {
106
+ for (const byte of buffer) {
107
+ if (byte < 0x20 && byte !== 0x09 && byte !== 0x0a && byte !== 0x0d && byte !== 0x0c && byte !== 0x1a)
108
+ return false;
109
+ }
110
+ return true;
111
+ }
112
+ /** The type the shared detection gives the bytes, or undefined when it does not know them. */
113
+ function signatureType(buffer, ext) {
114
+ if (startsWith(buffer, '%PDF-'))
115
+ return mime_1.MIME.pdf;
116
+ if (startsWith(buffer, [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]))
117
+ return mime_1.MIME.png;
118
+ if (startsWith(buffer, [0xff, 0xd8, 0xff]))
119
+ return mime_1.MIME.jpeg;
120
+ if (startsWith(buffer, 'GIF87a') || startsWith(buffer, 'GIF89a'))
121
+ return mime_1.MIME.gif;
122
+ if (startsWith(buffer, 'RIFF') && startsWith(buffer, 'WEBP', 8))
123
+ return mime_1.MIME.webp;
124
+ if (startsWith(buffer, [0x50, 0x4b, 0x03, 0x04]) || startsWith(buffer, [0x50, 0x4b, 0x05, 0x06]))
125
+ return zipType(buffer);
126
+ if (startsWith(buffer, [0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1])) {
127
+ return { '.doc': mime_1.MIME.doc, '.xls': mime_1.MIME.xls, '.ppt': mime_1.MIME.ppt }[ext] || mime_1.MIME.ole;
128
+ }
129
+ if (startsWith(buffer, '{\\rtf'))
130
+ return mime_1.MIME.rtf;
131
+ return undefined;
132
+ }
133
+ /**
134
+ * Identify a file's type from its bytes. The name and the client's MIME type
135
+ * are hints only: known signatures first, then each converter's `detect`, then
136
+ * text (typed by extension). A detected type that contradicts the family the
137
+ * extension promises is refused.
138
+ */
139
+ function identify(buffer, hint, converters = []) {
140
+ const ext = (0, mime_1.extensionOf)(hint.name);
141
+ let type = signatureType(buffer, ext);
142
+ if (!type) {
143
+ const head = buffer.subarray(0, 4096);
144
+ for (const converter of converters) {
145
+ if (typeof converter.detect !== 'function')
146
+ continue;
147
+ const detected = converter.detect(head, { name: hint.name, type: hint.type || '' });
148
+ if (typeof detected === 'string' && detected) {
149
+ type = detected;
150
+ break;
151
+ }
152
+ }
153
+ }
154
+ if (!type) {
155
+ // Text named as text may also be in a legacy 8-bit encoding; decodeText reads it as Windows-1252.
156
+ const text = isText(buffer) || (!!mime_1.TEXT_BY_EXTENSION[ext] && isLegacyText(buffer));
157
+ type = text ? (mime_1.TEXT_BY_EXTENSION[ext] || mime_1.MIME.text) : mime_1.MIME.binary;
158
+ }
159
+ const promised = mime_1.FAMILY_BY_EXTENSION[ext];
160
+ if (promised && promised !== (0, mime_1.familyOf)(type)) {
161
+ throw new DetectionError(`"${hint.name}" is named ${ext} but its content is not a ${ext.slice(1).toUpperCase()} file; it was refused.`);
162
+ }
163
+ return type;
164
+ }
165
+ /** The extensions known for a type, from the converters that accept it and the usual ones. */
166
+ function extensionsOf(type, converters = []) {
167
+ const fromConverters = converters.flatMap((converter) => converter.accepts.filter((accept) => accept.type === type).flatMap((accept) => accept.ext || []));
168
+ return [...new Set([...fromConverters, ...(mime_1.EXTENSIONS[type] || [])].map((ext) => ext.toLowerCase()))];
169
+ }
@@ -0,0 +1,9 @@
1
+ export * from './types';
2
+ export { MIME } from './mime';
3
+ export { identify, DetectionError, zipEntryNames } from './detect';
4
+ export { runConverter, ConversionError, CONVERTER_DEFAULTS } from './convert';
5
+ export { BUILT_IN_CONVERTERS, pdfTextConverter, textConverter, imagePdfConverter } from './converters';
6
+ export { ConverterRoutes, stepName } from './routes';
7
+ export type { FileRoute, AcceptEntry, Pins } from './routes';
8
+ export { converterPluginType, registerDocuments, documentsOf } from './plugins';
9
+ export { converterFileReader, readAsText } from './readers';
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.readAsText = exports.converterFileReader = exports.documentsOf = exports.registerDocuments = exports.converterPluginType = exports.stepName = exports.ConverterRoutes = exports.imagePdfConverter = exports.textConverter = exports.pdfTextConverter = exports.BUILT_IN_CONVERTERS = exports.CONVERTER_DEFAULTS = exports.ConversionError = exports.runConverter = exports.zipEntryNames = exports.DetectionError = exports.identify = exports.MIME = void 0;
18
+ __exportStar(require("./types"), exports);
19
+ var mime_1 = require("./mime");
20
+ Object.defineProperty(exports, "MIME", { enumerable: true, get: function () { return mime_1.MIME; } });
21
+ var detect_1 = require("./detect");
22
+ Object.defineProperty(exports, "identify", { enumerable: true, get: function () { return detect_1.identify; } });
23
+ Object.defineProperty(exports, "DetectionError", { enumerable: true, get: function () { return detect_1.DetectionError; } });
24
+ Object.defineProperty(exports, "zipEntryNames", { enumerable: true, get: function () { return detect_1.zipEntryNames; } });
25
+ var convert_1 = require("./convert");
26
+ Object.defineProperty(exports, "runConverter", { enumerable: true, get: function () { return convert_1.runConverter; } });
27
+ Object.defineProperty(exports, "ConversionError", { enumerable: true, get: function () { return convert_1.ConversionError; } });
28
+ Object.defineProperty(exports, "CONVERTER_DEFAULTS", { enumerable: true, get: function () { return convert_1.CONVERTER_DEFAULTS; } });
29
+ var converters_1 = require("./converters");
30
+ Object.defineProperty(exports, "BUILT_IN_CONVERTERS", { enumerable: true, get: function () { return converters_1.BUILT_IN_CONVERTERS; } });
31
+ Object.defineProperty(exports, "pdfTextConverter", { enumerable: true, get: function () { return converters_1.pdfTextConverter; } });
32
+ Object.defineProperty(exports, "textConverter", { enumerable: true, get: function () { return converters_1.textConverter; } });
33
+ Object.defineProperty(exports, "imagePdfConverter", { enumerable: true, get: function () { return converters_1.imagePdfConverter; } });
34
+ var routes_1 = require("./routes");
35
+ Object.defineProperty(exports, "ConverterRoutes", { enumerable: true, get: function () { return routes_1.ConverterRoutes; } });
36
+ Object.defineProperty(exports, "stepName", { enumerable: true, get: function () { return routes_1.stepName; } });
37
+ var plugins_1 = require("./plugins");
38
+ Object.defineProperty(exports, "converterPluginType", { enumerable: true, get: function () { return plugins_1.converterPluginType; } });
39
+ Object.defineProperty(exports, "registerDocuments", { enumerable: true, get: function () { return plugins_1.registerDocuments; } });
40
+ Object.defineProperty(exports, "documentsOf", { enumerable: true, get: function () { return plugins_1.documentsOf; } });
41
+ var readers_1 = require("./readers");
42
+ Object.defineProperty(exports, "converterFileReader", { enumerable: true, get: function () { return readers_1.converterFileReader; } });
43
+ Object.defineProperty(exports, "readAsText", { enumerable: true, get: function () { return readers_1.readAsText; } });
@@ -0,0 +1,39 @@
1
+ /** Canonical MIME types the UAG identifies, and the extensions that go with them. */
2
+ export declare const MIME: {
3
+ readonly pdf: 'application/pdf';
4
+ readonly png: 'image/png';
5
+ readonly jpeg: 'image/jpeg';
6
+ readonly gif: 'image/gif';
7
+ readonly webp: 'image/webp';
8
+ readonly docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document';
9
+ readonly xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
10
+ readonly pptx: 'application/vnd.openxmlformats-officedocument.presentationml.presentation';
11
+ readonly doc: 'application/msword';
12
+ readonly xls: 'application/vnd.ms-excel';
13
+ readonly ppt: 'application/vnd.ms-powerpoint';
14
+ readonly rtf: 'application/rtf';
15
+ readonly zip: 'application/zip';
16
+ readonly ole: 'application/x-ole-storage';
17
+ readonly text: 'text/plain';
18
+ readonly markdown: 'text/markdown';
19
+ readonly csv: 'text/csv';
20
+ readonly json: 'application/json';
21
+ readonly html: 'text/html';
22
+ readonly xml: 'application/xml';
23
+ readonly binary: 'application/octet-stream';
24
+ };
25
+ /** Text types, by extension. */
26
+ export declare const TEXT_BY_EXTENSION: Record<string, string>;
27
+ /** The usual extensions of each type, for the builder's file picker. */
28
+ export declare const EXTENSIONS: Record<string, string[]>;
29
+ /**
30
+ * The family of content an extension promises. A file whose detected content
31
+ * belongs to another family is refused: a disguised file.
32
+ */
33
+ export declare const FAMILY_BY_EXTENSION: Record<string, string>;
34
+ /** The family of a detected type. */
35
+ export declare function familyOf(type: string): string;
36
+ /** A file name's extension, lower-case with its dot ('' when it has none). */
37
+ export declare function extensionOf(name: string | undefined): string;
38
+ /** Whether a type is one the built-in text converter reads. */
39
+ export declare const TEXT_TYPES: string[];
@@ -0,0 +1,100 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TEXT_TYPES = exports.FAMILY_BY_EXTENSION = exports.EXTENSIONS = exports.TEXT_BY_EXTENSION = exports.MIME = void 0;
4
+ exports.familyOf = familyOf;
5
+ exports.extensionOf = extensionOf;
6
+ /** Canonical MIME types the UAG identifies, and the extensions that go with them. */
7
+ exports.MIME = {
8
+ pdf: 'application/pdf',
9
+ png: 'image/png',
10
+ jpeg: 'image/jpeg',
11
+ gif: 'image/gif',
12
+ webp: 'image/webp',
13
+ docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
14
+ xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
15
+ pptx: 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
16
+ doc: 'application/msword',
17
+ xls: 'application/vnd.ms-excel',
18
+ ppt: 'application/vnd.ms-powerpoint',
19
+ rtf: 'application/rtf',
20
+ zip: 'application/zip',
21
+ ole: 'application/x-ole-storage',
22
+ text: 'text/plain',
23
+ markdown: 'text/markdown',
24
+ csv: 'text/csv',
25
+ json: 'application/json',
26
+ html: 'text/html',
27
+ xml: 'application/xml',
28
+ binary: 'application/octet-stream'
29
+ };
30
+ /** Text types, by extension. */
31
+ exports.TEXT_BY_EXTENSION = {
32
+ '.txt': exports.MIME.text,
33
+ '.text': exports.MIME.text,
34
+ '.md': exports.MIME.markdown,
35
+ '.markdown': exports.MIME.markdown,
36
+ '.csv': exports.MIME.csv,
37
+ '.json': exports.MIME.json,
38
+ '.html': exports.MIME.html,
39
+ '.htm': exports.MIME.html,
40
+ '.xml': exports.MIME.xml
41
+ };
42
+ /** The usual extensions of each type, for the builder's file picker. */
43
+ exports.EXTENSIONS = {
44
+ [exports.MIME.pdf]: ['.pdf'],
45
+ [exports.MIME.png]: ['.png'],
46
+ [exports.MIME.jpeg]: ['.jpg', '.jpeg'],
47
+ [exports.MIME.gif]: ['.gif'],
48
+ [exports.MIME.webp]: ['.webp'],
49
+ [exports.MIME.docx]: ['.docx'],
50
+ [exports.MIME.xlsx]: ['.xlsx'],
51
+ [exports.MIME.pptx]: ['.pptx'],
52
+ [exports.MIME.doc]: ['.doc'],
53
+ [exports.MIME.xls]: ['.xls'],
54
+ [exports.MIME.ppt]: ['.ppt'],
55
+ [exports.MIME.rtf]: ['.rtf'],
56
+ [exports.MIME.text]: ['.txt'],
57
+ [exports.MIME.markdown]: ['.md', '.markdown'],
58
+ [exports.MIME.csv]: ['.csv'],
59
+ [exports.MIME.json]: ['.json'],
60
+ [exports.MIME.html]: ['.html', '.htm'],
61
+ [exports.MIME.xml]: ['.xml'],
62
+ 'application/vnd.oasis.opendocument.text': ['.odt'],
63
+ 'application/vnd.oasis.opendocument.spreadsheet': ['.ods'],
64
+ 'application/vnd.oasis.opendocument.presentation': ['.odp']
65
+ };
66
+ /**
67
+ * The family of content an extension promises. A file whose detected content
68
+ * belongs to another family is refused: a disguised file.
69
+ */
70
+ exports.FAMILY_BY_EXTENSION = {
71
+ '.pdf': 'pdf',
72
+ '.png': 'image', '.jpg': 'image', '.jpeg': 'image', '.gif': 'image', '.webp': 'image',
73
+ '.docx': 'zip', '.xlsx': 'zip', '.pptx': 'zip', '.odt': 'zip', '.ods': 'zip', '.odp': 'zip', '.zip': 'zip',
74
+ '.doc': 'ole', '.xls': 'ole', '.ppt': 'ole',
75
+ '.rtf': 'rtf',
76
+ ...Object.fromEntries(Object.keys(exports.TEXT_BY_EXTENSION).map((ext) => [ext, 'text']))
77
+ };
78
+ /** The family of a detected type. */
79
+ function familyOf(type) {
80
+ if (type === exports.MIME.pdf)
81
+ return 'pdf';
82
+ if (type.startsWith('image/'))
83
+ return 'image';
84
+ if ([exports.MIME.docx, exports.MIME.xlsx, exports.MIME.pptx, exports.MIME.zip].includes(type) || type.startsWith('application/vnd.oasis.opendocument.'))
85
+ return 'zip';
86
+ if ([exports.MIME.doc, exports.MIME.xls, exports.MIME.ppt, exports.MIME.ole].includes(type))
87
+ return 'ole';
88
+ if (type === exports.MIME.rtf)
89
+ return 'rtf';
90
+ if (type.startsWith('text/') || type === exports.MIME.json || type === exports.MIME.xml)
91
+ return 'text';
92
+ return 'other';
93
+ }
94
+ /** A file name's extension, lower-case with its dot ('' when it has none). */
95
+ function extensionOf(name) {
96
+ const match = /(\.[A-Za-z0-9]+)$/.exec(String(name || ''));
97
+ return match ? match[1].toLowerCase() : '';
98
+ }
99
+ /** Whether a type is one the built-in text converter reads. */
100
+ exports.TEXT_TYPES = [exports.MIME.text, exports.MIME.markdown, exports.MIME.csv, exports.MIME.json, exports.MIME.html, exports.MIME.xml];
@@ -0,0 +1,18 @@
1
+ import type { PluginRegistry, PluginType } from '@formio/appserver';
2
+ import { AcceptEntry, ConverterRoutes } from './routes';
3
+ /** Per registry: the converter routes (with `documents.prefer`), once the converters are set up. */
4
+ type DocumentsState = {
5
+ routes: ConverterRoutes;
6
+ accept: Map<string, AcceptEntry[]>;
7
+ };
8
+ /** The routes over the enabled converters, and each document plugin's `accept`, once the registry has started. */
9
+ export declare function documentsOf(registry: PluginRegistry): DocumentsState;
10
+ /** Plugin type `converter`: a file into a PDF or text. Chosen automatically by file type. Experimental in 2.0. */
11
+ export declare const converterPluginType: PluginType;
12
+ /**
13
+ * Register the converter type and the built-in converters, as the server's
14
+ * own. The `document` type comes with the AI Document module
15
+ * (@formio/uag-extras/modules/ai-document).
16
+ */
17
+ export declare function registerDocuments(registry: PluginRegistry): void;
18
+ export {};
@@ -0,0 +1,65 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.converterPluginType = void 0;
4
+ exports.documentsOf = documentsOf;
5
+ exports.registerDocuments = registerDocuments;
6
+ const converters_1 = require("./converters");
7
+ const routes_1 = require("./routes");
8
+ const REPRESENTATIONS = ['pdf', 'text'];
9
+ const isObject = (value) => !!value && typeof value === 'object' && !Array.isArray(value);
10
+ const states = new WeakMap();
11
+ /** The routes over the enabled converters, and each document plugin's `accept`, once the registry has started. */
12
+ function documentsOf(registry) {
13
+ let state = states.get(registry);
14
+ if (!state) {
15
+ state = { routes: new routes_1.ConverterRoutes(registry.list('converter')), accept: new Map() };
16
+ states.set(registry, state);
17
+ }
18
+ return state;
19
+ }
20
+ /** Plugin type `converter`: a file into a PDF or text. Chosen automatically by file type. Experimental in 2.0. */
21
+ exports.converterPluginType = {
22
+ type: 'converter',
23
+ title: 'Converter',
24
+ apiVersions: [1],
25
+ editForm: 'none',
26
+ unavailable: 'disable',
27
+ stability: 'experimental',
28
+ validate(plugin) {
29
+ const problems = [];
30
+ if (!Array.isArray(plugin.accepts) || !plugin.accepts.length || !plugin.accepts.every((accept) => isObject(accept) && typeof accept.type === 'string' && /^[a-z]+\/[a-z0-9.+-]+$/i.test(accept.type) &&
31
+ (accept.ext === undefined || (Array.isArray(accept.ext) && accept.ext.every((ext) => typeof ext === 'string' && /^\.[a-z0-9]+$/i.test(ext)))))) {
32
+ problems.push('"accepts" must list { type, ext? } entries: MIME types, and extensions such as ".docx"');
33
+ }
34
+ if (!Array.isArray(plugin.produces) || !plugin.produces.length || !plugin.produces.every((kind) => REPRESENTATIONS.includes(kind))) {
35
+ problems.push('"produces" must list "pdf", "text" or both');
36
+ }
37
+ if (typeof plugin.convert !== 'function')
38
+ problems.push('"convert" must be a function');
39
+ if (plugin.detect !== undefined && typeof plugin.detect !== 'function')
40
+ problems.push('"detect" must be a function');
41
+ if (plugin.rank !== undefined && !Number.isFinite(plugin.rank))
42
+ problems.push('"rank" must be a number');
43
+ if (plugin.limits !== undefined) {
44
+ if (!isObject(plugin.limits) || !Object.entries(plugin.limits).every(([key, value]) => ['maxBytes', 'timeoutMs', 'concurrency'].includes(key) && Number.isFinite(value) && value > 0)) {
45
+ problems.push('"limits" may hold positive maxBytes, timeoutMs and concurrency');
46
+ }
47
+ }
48
+ return problems;
49
+ },
50
+ /** Once every converter is set up: the routes between file types, with `documents.prefer`. */
51
+ initialize(registry, context) {
52
+ const prefer = context.server?.config?.documents?.prefer || {};
53
+ states.set(registry, { routes: new routes_1.ConverterRoutes(registry.list('converter'), prefer), accept: new Map() });
54
+ },
55
+ manifest: (plugin) => ({ accepts: plugin.accepts, produces: plugin.produces })
56
+ };
57
+ /**
58
+ * Register the converter type and the built-in converters, as the server's
59
+ * own. The `document` type comes with the AI Document module
60
+ * (@formio/uag-extras/modules/ai-document).
61
+ */
62
+ function registerDocuments(registry) {
63
+ registry.addType(exports.converterPluginType, '@formio/uag');
64
+ registry.add(converters_1.BUILT_IN_CONVERTERS, '@formio/uag', { server: true });
65
+ }