@formio/uag 1.13.2 → 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 (115) hide show
  1. package/README.md +237 -535
  2. package/bin/uag-eval.js +99 -0
  3. package/bin/uag.js +116 -0
  4. package/lib/UAGFormInterface.d.ts +72 -2
  5. package/lib/UAGFormInterface.js +226 -57
  6. package/lib/UAGProjectInterface.d.ts +45 -5
  7. package/lib/UAGProjectInterface.js +187 -36
  8. package/lib/actionLogs.d.ts +24 -0
  9. package/lib/actionLogs.js +149 -0
  10. package/lib/actions/runTask.d.ts +43 -0
  11. package/lib/actions/runTask.js +324 -0
  12. package/lib/agent/anonymous.d.ts +34 -0
  13. package/lib/agent/anonymous.js +99 -0
  14. package/lib/agent/chat.d.ts +114 -0
  15. package/lib/agent/chat.js +475 -0
  16. package/lib/agent/engines.d.ts +17 -0
  17. package/lib/agent/engines.js +46 -0
  18. package/lib/agent/errors.d.ts +25 -0
  19. package/lib/agent/errors.js +132 -0
  20. package/lib/agent/index.d.ts +4 -0
  21. package/lib/agent/index.js +19 -0
  22. package/lib/agent/remote.d.ts +71 -0
  23. package/lib/agent/remote.js +252 -0
  24. package/lib/agent/requestId.d.ts +4 -0
  25. package/lib/agent/requestId.js +19 -0
  26. package/lib/agent/router.d.ts +61 -0
  27. package/lib/agent/router.js +307 -0
  28. package/lib/agent/stubs.d.ts +37 -0
  29. package/lib/agent/stubs.js +39 -0
  30. package/lib/agent/tools.d.ts +31 -0
  31. package/lib/agent/tools.js +76 -0
  32. package/lib/config.d.ts +97 -3
  33. package/lib/config.js +19 -0
  34. package/lib/database.d.ts +70 -0
  35. package/lib/database.js +212 -0
  36. package/lib/documents/convert.d.ts +38 -0
  37. package/lib/documents/convert.js +151 -0
  38. package/lib/documents/converters.d.ts +28 -0
  39. package/lib/documents/converters.js +149 -0
  40. package/lib/documents/detect.d.ts +31 -0
  41. package/lib/documents/detect.js +169 -0
  42. package/lib/documents/index.d.ts +9 -0
  43. package/lib/documents/index.js +43 -0
  44. package/lib/documents/mime.d.ts +39 -0
  45. package/lib/documents/mime.js +100 -0
  46. package/lib/documents/plugins.d.ts +18 -0
  47. package/lib/documents/plugins.js +65 -0
  48. package/lib/documents/readers.d.ts +19 -0
  49. package/lib/documents/readers.js +64 -0
  50. package/lib/documents/routes.d.ts +52 -0
  51. package/lib/documents/routes.js +99 -0
  52. package/lib/documents/types.d.ts +126 -0
  53. package/lib/documents/types.js +2 -0
  54. package/lib/documents/uagJs.d.ts +17 -0
  55. package/lib/documents/uagJs.js +157 -0
  56. package/lib/formflow.d.ts +47 -0
  57. package/lib/formflow.js +28 -0
  58. package/lib/identity.d.ts +53 -0
  59. package/lib/identity.js +107 -0
  60. package/lib/index.d.ts +98 -1
  61. package/lib/index.js +382 -8
  62. package/lib/markdown.d.ts +1 -0
  63. package/lib/markdown.js +166 -0
  64. package/lib/modules/api.d.ts +25 -0
  65. package/lib/modules/api.js +98 -0
  66. package/lib/modules/index.d.ts +4 -0
  67. package/lib/modules/index.js +24 -0
  68. package/lib/modules/routes.d.ts +14 -0
  69. package/lib/modules/routes.js +21 -0
  70. package/lib/modules/types.d.ts +68 -0
  71. package/lib/modules/types.js +5 -0
  72. package/lib/pluginFiles.d.ts +28 -0
  73. package/lib/pluginFiles.js +101 -0
  74. package/lib/plugins.d.ts +17 -0
  75. package/lib/plugins.js +93 -0
  76. package/lib/richText.d.ts +20 -0
  77. package/lib/richText.js +99 -0
  78. package/lib/router.d.ts +9 -0
  79. package/lib/router.js +34 -25
  80. package/lib/runner.d.ts +149 -0
  81. package/lib/runner.js +353 -0
  82. package/lib/runs.d.ts +27 -0
  83. package/lib/runs.js +13 -0
  84. package/lib/template.js +7 -9
  85. package/lib/templates/uagComponentNotFound.md +5 -5
  86. package/lib/testing.d.ts +74 -0
  87. package/lib/testing.js +180 -0
  88. package/lib/tools/SchemaBuilder.d.ts +3 -3
  89. package/lib/tools/SchemaBuilder.js +4 -4
  90. package/lib/tools/agentDecide.d.ts +8 -0
  91. package/lib/tools/agentDecide.js +57 -0
  92. package/lib/tools/agentProvide.d.ts +8 -0
  93. package/lib/tools/agentProvide.js +97 -0
  94. package/lib/tools/collectData.js +3 -2
  95. package/lib/tools/confirmSubmission.js +1 -1
  96. package/lib/tools/fetchExternalData.js +8 -17
  97. package/lib/tools/findSubmission.js +62 -56
  98. package/lib/tools/getFieldInfo.js +1 -1
  99. package/lib/tools/getFormFields.js +1 -1
  100. package/lib/tools/getForms.js +1 -1
  101. package/lib/tools/index.d.ts +4 -1
  102. package/lib/tools/index.js +21 -14
  103. package/lib/tools/readDocument.d.ts +17 -0
  104. package/lib/tools/readDocument.js +99 -0
  105. package/lib/tools/submissionUpdate.js +4 -3
  106. package/lib/tools/submitForm.js +1 -1
  107. package/lib/tools/utils.d.ts +40 -0
  108. package/lib/tools/utils.js +16 -1
  109. package/lib/version.d.ts +2 -0
  110. package/lib/version.js +5 -0
  111. package/package.json +49 -19
  112. package/testing.d.ts +1 -0
  113. package/testing.js +2 -0
  114. package/lib/tools/agentProvideData.d.ts +0 -3
  115. package/lib/tools/agentProvideData.js +0 -62
@@ -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
+ }
@@ -0,0 +1,19 @@
1
+ import type { PluginRegistry } from '@formio/appserver';
2
+ import type { FormflowFileReader } from '../formflow';
3
+ /** Read a file as text through the converters: its detected type, its text, and its page count when known. */
4
+ export declare function readAsText(registry: PluginRegistry, buffer: Buffer, meta: {
5
+ name: string;
6
+ type?: string;
7
+ }, options?: {
8
+ signal?: AbortSignal;
9
+ }): Promise<{
10
+ type: string;
11
+ text: string;
12
+ pages?: number;
13
+ }>;
14
+ /**
15
+ * A reader for task file inputs, built from the converters: it accepts a file
16
+ * whose type has a route to text, and reads it through that route. Task file
17
+ * inputs therefore read every type the installed converters handle.
18
+ */
19
+ export declare function converterFileReader(registry: PluginRegistry): FormflowFileReader;
@@ -0,0 +1,64 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.readAsText = readAsText;
4
+ exports.converterFileReader = converterFileReader;
5
+ const detect_1 = require("./detect");
6
+ const convert_1 = require("./convert");
7
+ const mime_1 = require("./mime");
8
+ const plugins_1 = require("./plugins");
9
+ /** What a ZIP file is, from its extension: the first bytes cannot tell a Word file from any other ZIP. */
10
+ const ZIP_BY_EXTENSION = {
11
+ '.docx': mime_1.MIME.docx, '.xlsx': mime_1.MIME.xlsx, '.pptx': mime_1.MIME.pptx,
12
+ '.odt': 'application/vnd.oasis.opendocument.text', '.ods': 'application/vnd.oasis.opendocument.spreadsheet', '.odp': 'application/vnd.oasis.opendocument.presentation'
13
+ };
14
+ const log = {
15
+ info: () => undefined,
16
+ warn: (msg) => console.warn(`[formio-uag] ${msg}`),
17
+ error: (msg) => console.error(`[formio-uag] ${msg}`)
18
+ };
19
+ /** Read a file as text through the converters: its detected type, its text, and its page count when known. */
20
+ async function readAsText(registry, buffer, meta, options = {}) {
21
+ const converters = registry.list('converter');
22
+ const type = (0, detect_1.identify)(buffer, { name: meta.name, type: meta.type }, converters);
23
+ const steps = (0, plugins_1.documentsOf)(registry).routes.toText(type);
24
+ if (!steps) {
25
+ throw new Error(`"${meta.name}" (${type}) cannot be read as text: no installed converter reads it.`);
26
+ }
27
+ const output = await (0, convert_1.runRoute)(steps, { buffer, type, name: meta.name }, 'text', {
28
+ log,
29
+ signal: options.signal,
30
+ settingsOf: (converter) => registry.settings('converter', converter.name)
31
+ });
32
+ // No text, with the converters saying why (an image, a scan): an error, never an empty document.
33
+ if (!String(output.text).trim() && Array.isArray(output.warnings) && output.warnings.length) {
34
+ throw new convert_1.ConversionError(`"${meta.name}" has no text to read: ${output.warnings.join(' ')}`);
35
+ }
36
+ return { type, text: output.text, ...(Array.isArray(output.pages) && { pages: output.pages.length }) };
37
+ }
38
+ /**
39
+ * A reader for task file inputs, built from the converters: it accepts a file
40
+ * whose type has a route to text, and reads it through that route. Task file
41
+ * inputs therefore read every type the installed converters handle.
42
+ */
43
+ function converterFileReader(registry) {
44
+ const converters = () => registry.list('converter');
45
+ return {
46
+ name: 'converters',
47
+ accepts(meta, head) {
48
+ let type;
49
+ try {
50
+ type = (0, detect_1.identify)(head, { name: meta.name, type: meta.type }, converters());
51
+ }
52
+ catch {
53
+ // Too few bytes to be sure, or a name that contradicts them: read() decides on the whole file.
54
+ return true;
55
+ }
56
+ if (type === mime_1.MIME.zip)
57
+ type = ZIP_BY_EXTENSION[(0, mime_1.extensionOf)(meta.name)] || type;
58
+ return !!(0, plugins_1.documentsOf)(registry).routes.toText(type);
59
+ },
60
+ async read(buffer, meta, options) {
61
+ return { text: (await readAsText(registry, buffer, meta, { signal: options?.signal })).text };
62
+ }
63
+ };
64
+ }
@@ -0,0 +1,52 @@
1
+ import type { Converter, Representation } from './types';
2
+ /** How one file reaches the representations of one of a plugin's input sets. */
3
+ export type FileRoute = {
4
+ inputs: Representation[];
5
+ /** The converters that make the PDF ([] for a PDF: identity). */
6
+ pdf?: Converter[];
7
+ /** The converters that make the text; with a PDF in the set, the PDF's own steps then PDF to text. */
8
+ text?: Converter[];
9
+ };
10
+ /** Converter pins by source type: a component's `uag.converters`. */
11
+ export type Pins = Record<string, string>;
12
+ /** A source type a document plugin can take, as its manifest's `accept` lists it. */
13
+ export type AcceptEntry = {
14
+ type: string;
15
+ ext: string[];
16
+ /** The input set it reaches, and the converter names of each representation's route. */
17
+ routes: {
18
+ inputs: Representation[];
19
+ pdf?: string[];
20
+ text?: string[];
21
+ };
22
+ /** The converters that read this type: a pin chooses among them when there are several. */
23
+ converters: string[];
24
+ };
25
+ /** A converter as `name@version`, as results record routes. */
26
+ export declare const stepName: (converter: Converter) => string;
27
+ /**
28
+ * Routes from source types to representations over a set of converters.
29
+ * When several converters can do a step, the component's pin wins, then the
30
+ * operator's `documents.prefer`, then the highest rank, then the name.
31
+ */
32
+ export declare class ConverterRoutes {
33
+ readonly converters: Converter[];
34
+ private readonly prefer;
35
+ constructor(converters: Converter[], prefer?: Record<string, string>);
36
+ /** The converter for one step from `source` to `target`, or undefined when none can do it. */
37
+ choose(source: string, target: Representation, pins?: Pins): Converter | undefined;
38
+ /** The steps from `source` to a PDF: [] for a PDF, one converter otherwise, or null. */
39
+ toPdf(source: string, pins?: Pins): Converter[] | null;
40
+ /** The steps from `source` to text: one converter, or to a PDF and then its text; null when none. */
41
+ toText(source: string, pins?: Pins): Converter[] | null;
42
+ /**
43
+ * The first of a plugin's input sets every representation of which the
44
+ * file can reach. With both PDF and text, the text is always that same
45
+ * PDF's text, so content made from the PDF is checked against it.
46
+ */
47
+ route(source: string, inputs: Representation[][], pins?: Pins): FileRoute | null;
48
+ /** Every source type a converter reads, and PDF. */
49
+ sourceTypes(): string[];
50
+ /** The source types a plugin with these inputs can take, with their routes: its manifest's `accept`. */
51
+ accept(inputs: Representation[][]): AcceptEntry[];
52
+ }