@willwade/aac-processors 0.0.30 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +52 -852
- package/dist/browser/core/baseProcessor.js +241 -0
- package/dist/browser/core/stringCasing.js +179 -0
- package/dist/browser/core/treeStructure.js +255 -0
- package/dist/browser/index.browser.js +73 -0
- package/dist/browser/processors/applePanelsProcessor.js +582 -0
- package/dist/browser/processors/astericsGridProcessor.js +1509 -0
- package/dist/browser/processors/dotProcessor.js +221 -0
- package/dist/browser/processors/gridset/commands.js +962 -0
- package/dist/browser/processors/gridset/crypto.js +53 -0
- package/dist/browser/processors/gridset/password.js +43 -0
- package/dist/browser/processors/gridset/pluginTypes.js +277 -0
- package/dist/browser/processors/gridset/resolver.js +137 -0
- package/dist/browser/processors/gridset/symbolAlignment.js +276 -0
- package/dist/browser/processors/gridset/symbols.js +421 -0
- package/dist/browser/processors/gridsetProcessor.js +2002 -0
- package/dist/browser/processors/obfProcessor.js +705 -0
- package/dist/browser/processors/opmlProcessor.js +274 -0
- package/dist/browser/types/aac.js +38 -0
- package/dist/browser/utilities/analytics/utils/idGenerator.js +89 -0
- package/dist/browser/utilities/translation/translationProcessor.js +200 -0
- package/dist/browser/utils/io.js +95 -0
- package/dist/browser/validation/baseValidator.js +156 -0
- package/dist/browser/validation/gridsetValidator.js +355 -0
- package/dist/browser/validation/obfValidator.js +500 -0
- package/dist/browser/validation/validationTypes.js +46 -0
- package/dist/cli/index.js +5 -5
- package/dist/core/analyze.d.ts +2 -2
- package/dist/core/analyze.js +2 -2
- package/dist/core/baseProcessor.d.ts +5 -4
- package/dist/core/baseProcessor.js +22 -27
- package/dist/core/treeStructure.d.ts +5 -5
- package/dist/core/treeStructure.js +1 -4
- package/dist/index.browser.d.ts +37 -0
- package/dist/index.browser.js +99 -0
- package/dist/index.d.ts +1 -48
- package/dist/index.js +1 -136
- package/dist/index.node.d.ts +48 -0
- package/dist/index.node.js +152 -0
- package/dist/processors/applePanelsProcessor.d.ts +5 -4
- package/dist/processors/applePanelsProcessor.js +58 -62
- package/dist/processors/astericsGridProcessor.d.ts +7 -6
- package/dist/processors/astericsGridProcessor.js +31 -42
- package/dist/processors/dotProcessor.d.ts +5 -4
- package/dist/processors/dotProcessor.js +25 -33
- package/dist/processors/excelProcessor.d.ts +4 -3
- package/dist/processors/excelProcessor.js +6 -3
- package/dist/processors/gridset/crypto.d.ts +18 -0
- package/dist/processors/gridset/crypto.js +57 -0
- package/dist/processors/gridset/helpers.d.ts +1 -1
- package/dist/processors/gridset/helpers.js +18 -8
- package/dist/processors/gridset/password.d.ts +20 -3
- package/dist/processors/gridset/password.js +17 -3
- package/dist/processors/gridset/wordlistHelpers.d.ts +3 -3
- package/dist/processors/gridset/wordlistHelpers.js +21 -20
- package/dist/processors/gridsetProcessor.d.ts +7 -12
- package/dist/processors/gridsetProcessor.js +116 -77
- package/dist/processors/obfProcessor.d.ts +9 -7
- package/dist/processors/obfProcessor.js +131 -56
- package/dist/processors/obfsetProcessor.d.ts +5 -4
- package/dist/processors/obfsetProcessor.js +10 -16
- package/dist/processors/opmlProcessor.d.ts +5 -4
- package/dist/processors/opmlProcessor.js +27 -34
- package/dist/processors/snapProcessor.d.ts +8 -7
- package/dist/processors/snapProcessor.js +15 -12
- package/dist/processors/touchchatProcessor.d.ts +8 -7
- package/dist/processors/touchchatProcessor.js +22 -17
- package/dist/types/aac.d.ts +0 -2
- package/dist/types/aac.js +2 -0
- package/dist/utils/io.d.ts +12 -0
- package/dist/utils/io.js +107 -0
- package/dist/validation/gridsetValidator.js +7 -7
- package/dist/validation/snapValidator.js +28 -35
- package/docs/BROWSER_USAGE.md +618 -0
- package/examples/README.md +77 -0
- package/examples/browser-test-server.js +81 -0
- package/examples/browser-test.html +331 -0
- package/examples/vitedemo/QUICKSTART.md +74 -0
- package/examples/vitedemo/README.md +157 -0
- package/examples/vitedemo/index.html +376 -0
- package/examples/vitedemo/package-lock.json +1221 -0
- package/examples/vitedemo/package.json +18 -0
- package/examples/vitedemo/src/main.ts +519 -0
- package/examples/vitedemo/test-files/example.dot +14 -0
- package/examples/vitedemo/test-files/example.grd +1 -0
- package/examples/vitedemo/test-files/example.gridset +0 -0
- package/examples/vitedemo/test-files/example.obz +0 -0
- package/examples/vitedemo/test-files/example.opml +18 -0
- package/examples/vitedemo/test-files/simple.obf +53 -0
- package/examples/vitedemo/tsconfig.json +24 -0
- package/examples/vitedemo/vite.config.ts +34 -0
- package/package.json +20 -4
|
@@ -1,15 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
3
|
exports.DotProcessor = void 0;
|
|
7
4
|
const baseProcessor_1 = require("../core/baseProcessor");
|
|
8
5
|
const treeStructure_1 = require("../core/treeStructure");
|
|
9
6
|
// Removed unused import: FileProcessor
|
|
10
|
-
const
|
|
11
|
-
const
|
|
12
|
-
const validation_1 = require("../validation");
|
|
7
|
+
const validationTypes_1 = require("../validation/validationTypes");
|
|
8
|
+
const io_1 = require("../utils/io");
|
|
13
9
|
class DotProcessor extends baseProcessor_1.BaseProcessor {
|
|
14
10
|
constructor(options) {
|
|
15
11
|
super(options);
|
|
@@ -55,10 +51,9 @@ class DotProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
55
51
|
}
|
|
56
52
|
return { nodes: Array.from(nodes.values()), edges };
|
|
57
53
|
}
|
|
58
|
-
extractTexts(filePathOrBuffer) {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
: filePathOrBuffer.toString('utf8');
|
|
54
|
+
async extractTexts(filePathOrBuffer) {
|
|
55
|
+
await Promise.resolve();
|
|
56
|
+
const content = (0, io_1.readTextFromInput)(filePathOrBuffer);
|
|
62
57
|
const { nodes, edges } = this.parseDotFile(content);
|
|
63
58
|
const texts = [];
|
|
64
59
|
// Collect node labels
|
|
@@ -73,16 +68,15 @@ class DotProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
73
68
|
}
|
|
74
69
|
return texts;
|
|
75
70
|
}
|
|
76
|
-
loadIntoTree(filePathOrBuffer) {
|
|
77
|
-
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
: fs_1.default.readFileSync(filePathOrBuffer);
|
|
71
|
+
async loadIntoTree(filePathOrBuffer) {
|
|
72
|
+
await Promise.resolve();
|
|
73
|
+
const filename = typeof filePathOrBuffer === 'string' ? (0, io_1.getBasename)(filePathOrBuffer) : 'upload.dot';
|
|
74
|
+
const buffer = (0, io_1.readBinaryFromInput)(filePathOrBuffer);
|
|
81
75
|
const filesize = buffer.byteLength;
|
|
82
76
|
try {
|
|
83
|
-
const content =
|
|
77
|
+
const content = (0, io_1.readTextFromInput)(buffer);
|
|
84
78
|
if (!content || content.trim().length === 0) {
|
|
85
|
-
const validation = (0,
|
|
79
|
+
const validation = (0, validationTypes_1.buildValidationResultFromMessage)({
|
|
86
80
|
filename,
|
|
87
81
|
filesize,
|
|
88
82
|
format: 'dot',
|
|
@@ -90,14 +84,14 @@ class DotProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
90
84
|
type: 'content',
|
|
91
85
|
description: 'DOT file content',
|
|
92
86
|
});
|
|
93
|
-
throw new
|
|
87
|
+
throw new validationTypes_1.ValidationFailureError('Empty DOT content', validation);
|
|
94
88
|
}
|
|
95
89
|
// Check for binary data (contains null bytes or non-printable characters)
|
|
96
90
|
const head = content.substring(0, 100);
|
|
97
91
|
for (let i = 0; i < head.length; i++) {
|
|
98
92
|
const code = head.charCodeAt(i);
|
|
99
93
|
if (code === 0 || (code >= 0 && code <= 8) || (code >= 14 && code <= 31)) {
|
|
100
|
-
const validation = (0,
|
|
94
|
+
const validation = (0, validationTypes_1.buildValidationResultFromMessage)({
|
|
101
95
|
filename,
|
|
102
96
|
filesize,
|
|
103
97
|
format: 'dot',
|
|
@@ -105,7 +99,7 @@ class DotProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
105
99
|
type: 'content',
|
|
106
100
|
description: 'DOT file content',
|
|
107
101
|
});
|
|
108
|
-
throw new
|
|
102
|
+
throw new validationTypes_1.ValidationFailureError('Invalid DOT content', validation);
|
|
109
103
|
}
|
|
110
104
|
}
|
|
111
105
|
const { nodes, edges } = this.parseDotFile(content);
|
|
@@ -149,10 +143,10 @@ class DotProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
149
143
|
return tree;
|
|
150
144
|
}
|
|
151
145
|
catch (error) {
|
|
152
|
-
if (error instanceof
|
|
146
|
+
if (error instanceof validationTypes_1.ValidationFailureError) {
|
|
153
147
|
throw error;
|
|
154
148
|
}
|
|
155
|
-
const validation = (0,
|
|
149
|
+
const validation = (0, validationTypes_1.buildValidationResultFromMessage)({
|
|
156
150
|
filename,
|
|
157
151
|
filesize,
|
|
158
152
|
format: 'dot',
|
|
@@ -160,14 +154,12 @@ class DotProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
160
154
|
type: 'parse',
|
|
161
155
|
description: 'Parse DOT graph',
|
|
162
156
|
});
|
|
163
|
-
throw new
|
|
157
|
+
throw new validationTypes_1.ValidationFailureError('Failed to load DOT file', validation, error);
|
|
164
158
|
}
|
|
165
159
|
}
|
|
166
|
-
processTexts(filePathOrBuffer, translations, outputPath) {
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
: fs_1.default.readFileSync(filePathOrBuffer);
|
|
170
|
-
const content = safeBuffer.toString('utf8');
|
|
160
|
+
async processTexts(filePathOrBuffer, translations, outputPath) {
|
|
161
|
+
await Promise.resolve();
|
|
162
|
+
const content = (0, io_1.readTextFromInput)(filePathOrBuffer);
|
|
171
163
|
let translatedContent = content;
|
|
172
164
|
translations.forEach((translation, text) => {
|
|
173
165
|
if (typeof text === 'string' && typeof translation === 'string') {
|
|
@@ -177,12 +169,12 @@ class DotProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
177
169
|
translatedContent = translatedContent.replace(new RegExp(`label="${escapedText}"`, 'g'), `label="${escapedTranslation}"`);
|
|
178
170
|
}
|
|
179
171
|
});
|
|
180
|
-
const resultBuffer =
|
|
181
|
-
|
|
182
|
-
fs_1.default.writeFileSync(outputPath, resultBuffer);
|
|
172
|
+
const resultBuffer = (0, io_1.encodeText)(translatedContent || '');
|
|
173
|
+
(0, io_1.writeBinaryToPath)(outputPath, resultBuffer);
|
|
183
174
|
return resultBuffer;
|
|
184
175
|
}
|
|
185
|
-
saveFromTree(tree, _outputPath) {
|
|
176
|
+
async saveFromTree(tree, _outputPath) {
|
|
177
|
+
await Promise.resolve();
|
|
186
178
|
let dotContent = `digraph "${tree.metadata?.name || 'AACBoard'}" {\n`;
|
|
187
179
|
// Helper to escape DOT string
|
|
188
180
|
const escapeDotString = (str) => {
|
|
@@ -212,7 +204,7 @@ class DotProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
212
204
|
});
|
|
213
205
|
}
|
|
214
206
|
dotContent += '}\n';
|
|
215
|
-
|
|
207
|
+
(0, io_1.writeTextToPath)(_outputPath, dotContent);
|
|
216
208
|
}
|
|
217
209
|
/**
|
|
218
210
|
* Extract strings with metadata for aac-tools-platform compatibility
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ProcessorInput } from '../utils/io';
|
|
1
2
|
import { BaseProcessor, ExtractStringsResult, TranslatedString, SourceString } from '../core/baseProcessor';
|
|
2
3
|
import { AACTree } from '../core/treeStructure';
|
|
3
4
|
/**
|
|
@@ -12,13 +13,13 @@ export declare class ExcelProcessor extends BaseProcessor {
|
|
|
12
13
|
* @param filePathOrBuffer - Path to Excel file or Buffer containing Excel data
|
|
13
14
|
* @returns Array of all text content found in the Excel file
|
|
14
15
|
*/
|
|
15
|
-
extractTexts(_filePathOrBuffer:
|
|
16
|
+
extractTexts(_filePathOrBuffer: ProcessorInput): Promise<string[]>;
|
|
16
17
|
/**
|
|
17
18
|
* Load Excel file into AACTree structure
|
|
18
19
|
* @param filePathOrBuffer - Path to Excel file or Buffer containing Excel data
|
|
19
20
|
* @returns AACTree representation of the Excel file
|
|
20
21
|
*/
|
|
21
|
-
loadIntoTree(_filePathOrBuffer:
|
|
22
|
+
loadIntoTree(_filePathOrBuffer: ProcessorInput): Promise<AACTree>;
|
|
22
23
|
/**
|
|
23
24
|
* Process texts in Excel file (apply translations)
|
|
24
25
|
* @param filePathOrBuffer - Path to Excel file or Buffer containing Excel data
|
|
@@ -26,7 +27,7 @@ export declare class ExcelProcessor extends BaseProcessor {
|
|
|
26
27
|
* @param outputPath - Path where translated Excel file should be saved
|
|
27
28
|
* @returns Buffer containing the translated Excel file
|
|
28
29
|
*/
|
|
29
|
-
processTexts(_filePathOrBuffer:
|
|
30
|
+
processTexts(_filePathOrBuffer: ProcessorInput, _translations: Map<string, string>, outputPath: string): Promise<Uint8Array>;
|
|
30
31
|
/**
|
|
31
32
|
* Convert an AAC page to an Excel worksheet
|
|
32
33
|
* @param workbook - Excel workbook to add worksheet to
|
|
@@ -43,7 +43,8 @@ class ExcelProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
43
43
|
* @param filePathOrBuffer - Path to Excel file or Buffer containing Excel data
|
|
44
44
|
* @returns Array of all text content found in the Excel file
|
|
45
45
|
*/
|
|
46
|
-
extractTexts(_filePathOrBuffer) {
|
|
46
|
+
async extractTexts(_filePathOrBuffer) {
|
|
47
|
+
await Promise.resolve();
|
|
47
48
|
console.warn('ExcelProcessor.extractTexts is not implemented yet.');
|
|
48
49
|
return [];
|
|
49
50
|
}
|
|
@@ -52,7 +53,8 @@ class ExcelProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
52
53
|
* @param filePathOrBuffer - Path to Excel file or Buffer containing Excel data
|
|
53
54
|
* @returns AACTree representation of the Excel file
|
|
54
55
|
*/
|
|
55
|
-
loadIntoTree(_filePathOrBuffer) {
|
|
56
|
+
async loadIntoTree(_filePathOrBuffer) {
|
|
57
|
+
await Promise.resolve();
|
|
56
58
|
console.warn('ExcelProcessor.loadIntoTree is not implemented yet.');
|
|
57
59
|
const tree = new treeStructure_1.AACTree();
|
|
58
60
|
tree.metadata.format = 'excel';
|
|
@@ -65,7 +67,8 @@ class ExcelProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
65
67
|
* @param outputPath - Path where translated Excel file should be saved
|
|
66
68
|
* @returns Buffer containing the translated Excel file
|
|
67
69
|
*/
|
|
68
|
-
processTexts(_filePathOrBuffer, _translations, outputPath) {
|
|
70
|
+
async processTexts(_filePathOrBuffer, _translations, outputPath) {
|
|
71
|
+
await Promise.resolve();
|
|
69
72
|
console.warn('ExcelProcessor.processTexts is not implemented yet.');
|
|
70
73
|
const outputDir = path_1.default.dirname(outputPath);
|
|
71
74
|
if (!fs_1.default.existsSync(outputDir)) {
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Crypto utilities for Gridsetx (encrypted Grid3 files)
|
|
3
|
+
* This module is only needed for .gridsetx files and uses Node-only crypto/zlib
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Decrypt and inflate a Grid3 encrypted payload (DesktopContentEncrypter).
|
|
7
|
+
* Uses AES-256-CBC with key/IV derived from the password padded with spaces
|
|
8
|
+
* and then Deflate decompression.
|
|
9
|
+
*
|
|
10
|
+
* @param buffer - Encrypted buffer
|
|
11
|
+
* @param password - Password (defaults to 'Chocolate')
|
|
12
|
+
* @returns Decrypted and inflated buffer
|
|
13
|
+
*/
|
|
14
|
+
export declare function decryptGridsetEntry(buffer: Buffer, password?: string): Buffer;
|
|
15
|
+
/**
|
|
16
|
+
* Check if crypto operations are available in the current environment
|
|
17
|
+
*/
|
|
18
|
+
export declare function isCryptoAvailable(): boolean;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Crypto utilities for Gridsetx (encrypted Grid3 files)
|
|
4
|
+
* This module is only needed for .gridsetx files and uses Node-only crypto/zlib
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.decryptGridsetEntry = decryptGridsetEntry;
|
|
8
|
+
exports.isCryptoAvailable = isCryptoAvailable;
|
|
9
|
+
/**
|
|
10
|
+
* Decrypt and inflate a Grid3 encrypted payload (DesktopContentEncrypter).
|
|
11
|
+
* Uses AES-256-CBC with key/IV derived from the password padded with spaces
|
|
12
|
+
* and then Deflate decompression.
|
|
13
|
+
*
|
|
14
|
+
* @param buffer - Encrypted buffer
|
|
15
|
+
* @param password - Password (defaults to 'Chocolate')
|
|
16
|
+
* @returns Decrypted and inflated buffer
|
|
17
|
+
*/
|
|
18
|
+
function decryptGridsetEntry(buffer, password) {
|
|
19
|
+
// Dynamic require to avoid breaking in browser environments
|
|
20
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires, @typescript-eslint/no-unsafe-return
|
|
21
|
+
const crypto = require('crypto');
|
|
22
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires, @typescript-eslint/no-unsafe-return
|
|
23
|
+
const zlib = require('zlib');
|
|
24
|
+
const pwd = (password || 'Chocolate').padEnd(32, ' ');
|
|
25
|
+
const key = Buffer.from(pwd.slice(0, 32), 'utf8');
|
|
26
|
+
const iv = Buffer.from(pwd.slice(0, 16), 'utf8');
|
|
27
|
+
try {
|
|
28
|
+
const decipher = crypto.createDecipheriv('aes-256-cbc', key, iv);
|
|
29
|
+
const decrypted = Buffer.concat([decipher.update(buffer), decipher.final()]);
|
|
30
|
+
try {
|
|
31
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
32
|
+
return zlib.inflateSync(decrypted);
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
// If data isn't deflated, return raw decrypted bytes
|
|
36
|
+
return decrypted;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
return buffer;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Check if crypto operations are available in the current environment
|
|
45
|
+
*/
|
|
46
|
+
function isCryptoAvailable() {
|
|
47
|
+
try {
|
|
48
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
49
|
+
require('crypto');
|
|
50
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
51
|
+
require('zlib');
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -15,7 +15,7 @@ export declare function getAllowedImageEntries(tree: AACTree): Set<string>;
|
|
|
15
15
|
* @param entryPath Entry name inside the zip
|
|
16
16
|
* @returns Image data buffer or null if not found
|
|
17
17
|
*/
|
|
18
|
-
export declare function openImage(gridsetBuffer:
|
|
18
|
+
export declare function openImage(gridsetBuffer: Uint8Array, entryPath: string, password?: string | undefined): Promise<Uint8Array | null>;
|
|
19
19
|
/**
|
|
20
20
|
* Generate a random GUID for Grid3 elements
|
|
21
21
|
* Grid3 uses GUIDs for grid identification
|
|
@@ -42,7 +42,6 @@ exports.isGrid3Installed = isGrid3Installed;
|
|
|
42
42
|
exports.readGrid3History = readGrid3History;
|
|
43
43
|
exports.readGrid3HistoryForUser = readGrid3HistoryForUser;
|
|
44
44
|
exports.readAllGrid3History = readAllGrid3History;
|
|
45
|
-
const adm_zip_1 = __importDefault(require("adm-zip"));
|
|
46
45
|
const fast_xml_parser_1 = require("fast-xml-parser");
|
|
47
46
|
const treeStructure_1 = require("../../core/treeStructure");
|
|
48
47
|
const fs = __importStar(require("fs"));
|
|
@@ -96,14 +95,25 @@ function getAllowedImageEntries(tree) {
|
|
|
96
95
|
* @param entryPath Entry name inside the zip
|
|
97
96
|
* @returns Image data buffer or null if not found
|
|
98
97
|
*/
|
|
99
|
-
function openImage(gridsetBuffer, entryPath, password = (0, password_1.resolveGridsetPasswordFromEnv)()) {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
98
|
+
async function openImage(gridsetBuffer, entryPath, password = (0, password_1.resolveGridsetPasswordFromEnv)()) {
|
|
99
|
+
try {
|
|
100
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
101
|
+
const JSZip = require('jszip');
|
|
102
|
+
const zip = await JSZip.loadAsync(gridsetBuffer);
|
|
103
|
+
const entries = (0, password_1.getZipEntriesWithPassword)(zip, password);
|
|
104
|
+
const want = normalizeZipPath(entryPath);
|
|
105
|
+
const entry = entries.find((e) => normalizeZipPath(e.entryName) === want);
|
|
106
|
+
if (!entry)
|
|
107
|
+
return null;
|
|
108
|
+
const data = await entry.getData();
|
|
109
|
+
if (typeof Buffer !== 'undefined' && typeof Buffer.from === 'function') {
|
|
110
|
+
return Buffer.from(data);
|
|
111
|
+
}
|
|
112
|
+
return data;
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
105
115
|
return null;
|
|
106
|
-
|
|
116
|
+
}
|
|
107
117
|
}
|
|
108
118
|
/**
|
|
109
119
|
* Generate a random GUID for Grid3 elements
|
|
@@ -1,11 +1,28 @@
|
|
|
1
|
+
import type JSZip from 'jszip';
|
|
1
2
|
import { ProcessorOptions } from '../../core/baseProcessor';
|
|
2
|
-
import
|
|
3
|
+
import { ProcessorInput } from '../../utils/io';
|
|
3
4
|
/**
|
|
4
5
|
* Resolve the password to use for Grid3 archives.
|
|
5
6
|
* Preference order:
|
|
6
7
|
* 1. Explicit processor option
|
|
7
8
|
* 2. GRIDSET_PASSWORD env var
|
|
8
9
|
*/
|
|
9
|
-
export declare function resolveGridsetPassword(options?: ProcessorOptions, source?:
|
|
10
|
+
export declare function resolveGridsetPassword(options?: ProcessorOptions, source?: ProcessorInput): string | undefined;
|
|
10
11
|
export declare function resolveGridsetPasswordFromEnv(): string | undefined;
|
|
11
|
-
|
|
12
|
+
/**
|
|
13
|
+
* Get zip entries as an array from JSZip instance.
|
|
14
|
+
* JSZip doesn't have password protection at the entry level like AdmZip.
|
|
15
|
+
* Password protection for .gridsetx is handled at the archive level in crypto.ts.
|
|
16
|
+
*
|
|
17
|
+
* @param zip - JSZip instance
|
|
18
|
+
* @param password - Optional password (kept for API compatibility, not used with JSZip)
|
|
19
|
+
* @returns Array of entry objects with name and data
|
|
20
|
+
*/
|
|
21
|
+
type ZipEntry = {
|
|
22
|
+
name: string;
|
|
23
|
+
entryName: string;
|
|
24
|
+
dir: boolean;
|
|
25
|
+
getData: () => Promise<Uint8Array>;
|
|
26
|
+
};
|
|
27
|
+
export declare function getZipEntriesWithPassword(zip: JSZip, password?: string): ZipEntry[];
|
|
28
|
+
export {};
|
|
@@ -28,10 +28,24 @@ function resolveGridsetPassword(options, source) {
|
|
|
28
28
|
function resolveGridsetPasswordFromEnv() {
|
|
29
29
|
return process.env.GRIDSET_PASSWORD;
|
|
30
30
|
}
|
|
31
|
-
// Wrapper to set the password before reading entries (typed getEntries lacks the optional arg)
|
|
32
31
|
function getZipEntriesWithPassword(zip, password) {
|
|
32
|
+
const entries = [];
|
|
33
|
+
// Note: JSZip doesn't support zip-level password protection like AdmZip
|
|
34
|
+
// Password protection for .gridsetx files is handled at the encrypted archive level
|
|
35
|
+
// in crypto.ts before the zip is loaded
|
|
33
36
|
if (password) {
|
|
34
|
-
|
|
37
|
+
console.warn('JSZip does not support zip-level password protection. For .gridsetx encrypted files, password is handled at the archive level.');
|
|
35
38
|
}
|
|
36
|
-
|
|
39
|
+
zip.forEach((relativePath, file) => {
|
|
40
|
+
entries.push({
|
|
41
|
+
name: relativePath,
|
|
42
|
+
entryName: relativePath,
|
|
43
|
+
dir: file.dir || false,
|
|
44
|
+
getData: async () => {
|
|
45
|
+
// Use 'uint8array' which is supported everywhere
|
|
46
|
+
return await file.async('uint8array');
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
return entries;
|
|
37
51
|
}
|
|
@@ -59,12 +59,12 @@ export declare function wordlistToXml(wordlist: WordList): string;
|
|
|
59
59
|
* @returns Map of grid names to their wordlists (if they have any)
|
|
60
60
|
*
|
|
61
61
|
* @example
|
|
62
|
-
* const wordlists = extractWordlists(gridsetBuffer);
|
|
62
|
+
* const wordlists = await extractWordlists(gridsetBuffer);
|
|
63
63
|
* wordlists.forEach((wordlist, gridName) => {
|
|
64
64
|
* console.log(`Grid "${gridName}" has ${wordlist.items.length} items`);
|
|
65
65
|
* });
|
|
66
66
|
*/
|
|
67
|
-
export declare function extractWordlists(gridsetBuffer:
|
|
67
|
+
export declare function extractWordlists(gridsetBuffer: Uint8Array, password?: string | undefined): Promise<Map<string, WordList>>;
|
|
68
68
|
/**
|
|
69
69
|
* Updates or adds a wordlist to a specific grid in a gridset
|
|
70
70
|
*
|
|
@@ -79,4 +79,4 @@ export declare function extractWordlists(gridsetBuffer: Buffer, password?: strin
|
|
|
79
79
|
* const updatedGridset = updateWordlist(gridsetBuffer, 'Greetings', newWordlist);
|
|
80
80
|
* fs.writeFileSync('updated-gridset.gridset', updatedGridset);
|
|
81
81
|
*/
|
|
82
|
-
export declare function updateWordlist(gridsetBuffer:
|
|
82
|
+
export declare function updateWordlist(gridsetBuffer: Uint8Array, gridName: string, wordlist: WordList, password?: string | undefined): Promise<Uint8Array>;
|
|
@@ -9,17 +9,14 @@
|
|
|
9
9
|
* Note: Wordlists are only supported in Grid3 format. Other AAC formats
|
|
10
10
|
* do not have equivalent wordlist functionality.
|
|
11
11
|
*/
|
|
12
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
13
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
14
|
-
};
|
|
15
12
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
13
|
exports.createWordlist = createWordlist;
|
|
17
14
|
exports.wordlistToXml = wordlistToXml;
|
|
18
15
|
exports.extractWordlists = extractWordlists;
|
|
19
16
|
exports.updateWordlist = updateWordlist;
|
|
20
|
-
const adm_zip_1 = __importDefault(require("adm-zip"));
|
|
21
17
|
const fast_xml_parser_1 = require("fast-xml-parser");
|
|
22
18
|
const password_1 = require("./password");
|
|
19
|
+
const io_1 = require("../../utils/io");
|
|
23
20
|
/**
|
|
24
21
|
* Creates a WordList object from an array of words/phrases or a dictionary
|
|
25
22
|
*
|
|
@@ -100,31 +97,33 @@ function wordlistToXml(wordlist) {
|
|
|
100
97
|
* @returns Map of grid names to their wordlists (if they have any)
|
|
101
98
|
*
|
|
102
99
|
* @example
|
|
103
|
-
* const wordlists = extractWordlists(gridsetBuffer);
|
|
100
|
+
* const wordlists = await extractWordlists(gridsetBuffer);
|
|
104
101
|
* wordlists.forEach((wordlist, gridName) => {
|
|
105
102
|
* console.log(`Grid "${gridName}" has ${wordlist.items.length} items`);
|
|
106
103
|
* });
|
|
107
104
|
*/
|
|
108
|
-
function extractWordlists(gridsetBuffer, password = (0, password_1.resolveGridsetPasswordFromEnv)()) {
|
|
105
|
+
async function extractWordlists(gridsetBuffer, password = (0, password_1.resolveGridsetPasswordFromEnv)()) {
|
|
109
106
|
const wordlists = new Map();
|
|
110
107
|
const parser = new fast_xml_parser_1.XMLParser();
|
|
111
108
|
let zip;
|
|
112
109
|
try {
|
|
113
|
-
|
|
110
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
111
|
+
const JSZip = require('jszip');
|
|
112
|
+
zip = await JSZip.loadAsync(gridsetBuffer);
|
|
114
113
|
}
|
|
115
114
|
catch (error) {
|
|
116
115
|
throw new Error(`Invalid gridset buffer: ${error.message}`);
|
|
117
116
|
}
|
|
118
117
|
const entries = (0, password_1.getZipEntriesWithPassword)(zip, password);
|
|
119
118
|
// Process each grid file
|
|
120
|
-
|
|
119
|
+
for (const entry of entries) {
|
|
121
120
|
if (entry.entryName.startsWith('Grids/') && entry.entryName.endsWith('grid.xml')) {
|
|
122
121
|
try {
|
|
123
|
-
const xmlContent = entry.getData()
|
|
122
|
+
const xmlContent = (0, io_1.decodeText)(await entry.getData());
|
|
124
123
|
const data = parser.parse(xmlContent);
|
|
125
124
|
const grid = data.Grid || data.grid;
|
|
126
125
|
if (!grid || !grid.WordList) {
|
|
127
|
-
|
|
126
|
+
continue;
|
|
128
127
|
}
|
|
129
128
|
// Extract grid name from path (e.g., "Grids/MyGrid/grid.xml" -> "MyGrid")
|
|
130
129
|
const match = entry.entryName.match(/^Grids\/([^/]+)\//);
|
|
@@ -133,7 +132,7 @@ function extractWordlists(gridsetBuffer, password = (0, password_1.resolveGridse
|
|
|
133
132
|
const wordlistData = grid.WordList;
|
|
134
133
|
const itemsContainer = wordlistData.Items || wordlistData.items;
|
|
135
134
|
if (!itemsContainer) {
|
|
136
|
-
|
|
135
|
+
continue;
|
|
137
136
|
}
|
|
138
137
|
const itemArray = Array.isArray(itemsContainer.WordListItem)
|
|
139
138
|
? itemsContainer.WordListItem
|
|
@@ -154,7 +153,7 @@ function extractWordlists(gridsetBuffer, password = (0, password_1.resolveGridse
|
|
|
154
153
|
console.warn(`Failed to extract wordlist from ${entry.entryName}:`, error);
|
|
155
154
|
}
|
|
156
155
|
}
|
|
157
|
-
}
|
|
156
|
+
}
|
|
158
157
|
return wordlists;
|
|
159
158
|
}
|
|
160
159
|
/**
|
|
@@ -171,7 +170,7 @@ function extractWordlists(gridsetBuffer, password = (0, password_1.resolveGridse
|
|
|
171
170
|
* const updatedGridset = updateWordlist(gridsetBuffer, 'Greetings', newWordlist);
|
|
172
171
|
* fs.writeFileSync('updated-gridset.gridset', updatedGridset);
|
|
173
172
|
*/
|
|
174
|
-
function updateWordlist(gridsetBuffer, gridName, wordlist, password = (0, password_1.resolveGridsetPasswordFromEnv)()) {
|
|
173
|
+
async function updateWordlist(gridsetBuffer, gridName, wordlist, password = (0, password_1.resolveGridsetPasswordFromEnv)()) {
|
|
175
174
|
const parser = new fast_xml_parser_1.XMLParser();
|
|
176
175
|
const builder = new fast_xml_parser_1.XMLBuilder({
|
|
177
176
|
ignoreAttributes: false,
|
|
@@ -181,7 +180,9 @@ function updateWordlist(gridsetBuffer, gridName, wordlist, password = (0, passwo
|
|
|
181
180
|
});
|
|
182
181
|
let zip;
|
|
183
182
|
try {
|
|
184
|
-
|
|
183
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
184
|
+
const JSZip = require('jszip');
|
|
185
|
+
zip = await JSZip.loadAsync(gridsetBuffer);
|
|
185
186
|
}
|
|
186
187
|
catch (error) {
|
|
187
188
|
throw new Error(`Invalid gridset buffer: ${error.message}`);
|
|
@@ -189,17 +190,17 @@ function updateWordlist(gridsetBuffer, gridName, wordlist, password = (0, passwo
|
|
|
189
190
|
const entries = (0, password_1.getZipEntriesWithPassword)(zip, password);
|
|
190
191
|
let found = false;
|
|
191
192
|
// Find and update the grid
|
|
192
|
-
|
|
193
|
+
for (const entry of entries) {
|
|
193
194
|
if (entry.entryName.startsWith('Grids/') && entry.entryName.endsWith('grid.xml')) {
|
|
194
195
|
const match = entry.entryName.match(/^Grids\/([^/]+)\//);
|
|
195
196
|
const currentGridName = match ? match[1] : null;
|
|
196
197
|
if (currentGridName === gridName) {
|
|
197
198
|
try {
|
|
198
|
-
const xmlContent = entry.getData()
|
|
199
|
+
const xmlContent = (0, io_1.decodeText)(await entry.getData());
|
|
199
200
|
const data = parser.parse(xmlContent);
|
|
200
201
|
const grid = data.Grid || data.grid;
|
|
201
202
|
if (!grid) {
|
|
202
|
-
|
|
203
|
+
continue;
|
|
203
204
|
}
|
|
204
205
|
// Build the new wordlist XML structure
|
|
205
206
|
const items = wordlist.items.map((item) => ({
|
|
@@ -221,7 +222,7 @@ function updateWordlist(gridsetBuffer, gridName, wordlist, password = (0, passwo
|
|
|
221
222
|
};
|
|
222
223
|
// Rebuild the XML
|
|
223
224
|
const updatedXml = builder.build(data);
|
|
224
|
-
zip.
|
|
225
|
+
zip.file(entry.entryName, updatedXml, { binary: false });
|
|
225
226
|
found = true;
|
|
226
227
|
}
|
|
227
228
|
catch (error) {
|
|
@@ -230,9 +231,9 @@ function updateWordlist(gridsetBuffer, gridName, wordlist, password = (0, passwo
|
|
|
230
231
|
}
|
|
231
232
|
}
|
|
232
233
|
}
|
|
233
|
-
}
|
|
234
|
+
}
|
|
234
235
|
if (!found) {
|
|
235
236
|
throw new Error(`Grid "${gridName}" not found in gridset`);
|
|
236
237
|
}
|
|
237
|
-
return zip.
|
|
238
|
+
return await zip.generateAsync({ type: 'uint8array' });
|
|
238
239
|
}
|
|
@@ -2,14 +2,9 @@ import { BaseProcessor, ProcessorOptions, ExtractStringsResult, TranslatedString
|
|
|
2
2
|
import { AACTree } from '../core/treeStructure';
|
|
3
3
|
import { type ButtonForTranslation, type LLMLTranslationResult } from '../utilities/translation/translationProcessor';
|
|
4
4
|
import { ValidationResult } from '../validation/validationTypes';
|
|
5
|
+
import { ProcessorInput } from '../utils/io';
|
|
5
6
|
declare class GridsetProcessor extends BaseProcessor {
|
|
6
7
|
constructor(options?: ProcessorOptions);
|
|
7
|
-
/**
|
|
8
|
-
* Decrypt and inflate a Grid3 encrypted payload (DesktopContentEncrypter).
|
|
9
|
-
* Uses AES-256-CBC with key/IV derived from the password padded with spaces
|
|
10
|
-
* and then Deflate decompression.
|
|
11
|
-
*/
|
|
12
|
-
private decryptGridsetEntry;
|
|
13
8
|
private getGridsetPassword;
|
|
14
9
|
private ensureAlphaChannel;
|
|
15
10
|
/**
|
|
@@ -25,9 +20,9 @@ declare class GridsetProcessor extends BaseProcessor {
|
|
|
25
20
|
private convertGrid3StyleToAACStyle;
|
|
26
21
|
private getStyleById;
|
|
27
22
|
private textOf;
|
|
28
|
-
extractTexts(filePathOrBuffer:
|
|
29
|
-
loadIntoTree(filePathOrBuffer:
|
|
30
|
-
processTexts(filePathOrBuffer:
|
|
23
|
+
extractTexts(filePathOrBuffer: ProcessorInput): Promise<string[]>;
|
|
24
|
+
loadIntoTree(filePathOrBuffer: ProcessorInput): Promise<AACTree>;
|
|
25
|
+
processTexts(filePathOrBuffer: ProcessorInput, translations: Map<string, string>, outputPath: string): Promise<Uint8Array>;
|
|
31
26
|
/**
|
|
32
27
|
* Extract symbol information from a gridset for LLM-based translation.
|
|
33
28
|
* Returns a structured format showing which buttons have symbols and their context.
|
|
@@ -37,7 +32,7 @@ declare class GridsetProcessor extends BaseProcessor {
|
|
|
37
32
|
* @param filePathOrBuffer - Path to gridset file or buffer
|
|
38
33
|
* @returns Array of symbol information for LLM processing
|
|
39
34
|
*/
|
|
40
|
-
extractSymbolsForLLM(filePathOrBuffer: string | Buffer): ButtonForTranslation[]
|
|
35
|
+
extractSymbolsForLLM(filePathOrBuffer: string | Buffer): Promise<ButtonForTranslation[]>;
|
|
41
36
|
/**
|
|
42
37
|
* Apply LLM translations with symbol information.
|
|
43
38
|
* The LLM should provide translations with symbol attachments in the correct positions.
|
|
@@ -52,8 +47,8 @@ declare class GridsetProcessor extends BaseProcessor {
|
|
|
52
47
|
*/
|
|
53
48
|
processLLMTranslations(filePathOrBuffer: string | Buffer, llmTranslations: LLMLTranslationResult[], outputPath: string, options?: {
|
|
54
49
|
allowPartial?: boolean;
|
|
55
|
-
}):
|
|
56
|
-
saveFromTree(tree: AACTree, outputPath: string): void
|
|
50
|
+
}): Promise<Uint8Array>;
|
|
51
|
+
saveFromTree(tree: AACTree, outputPath: string): Promise<void>;
|
|
57
52
|
private calculateColumnDefinitions;
|
|
58
53
|
private calculateRowDefinitions;
|
|
59
54
|
private findButtonPosition;
|