@willwade/aac-processors 0.0.30 → 0.1.1

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 (94) hide show
  1. package/README.md +52 -852
  2. package/dist/browser/core/baseProcessor.js +241 -0
  3. package/dist/browser/core/stringCasing.js +179 -0
  4. package/dist/browser/core/treeStructure.js +255 -0
  5. package/dist/browser/index.browser.js +73 -0
  6. package/dist/browser/processors/applePanelsProcessor.js +582 -0
  7. package/dist/browser/processors/astericsGridProcessor.js +1509 -0
  8. package/dist/browser/processors/dotProcessor.js +221 -0
  9. package/dist/browser/processors/gridset/commands.js +962 -0
  10. package/dist/browser/processors/gridset/crypto.js +53 -0
  11. package/dist/browser/processors/gridset/password.js +49 -0
  12. package/dist/browser/processors/gridset/pluginTypes.js +277 -0
  13. package/dist/browser/processors/gridset/resolver.js +137 -0
  14. package/dist/browser/processors/gridset/symbolAlignment.js +276 -0
  15. package/dist/browser/processors/gridset/symbols.js +464 -0
  16. package/dist/browser/processors/gridsetProcessor.js +2002 -0
  17. package/dist/browser/processors/obfProcessor.js +705 -0
  18. package/dist/browser/processors/opmlProcessor.js +274 -0
  19. package/dist/browser/types/aac.js +38 -0
  20. package/dist/browser/utilities/analytics/utils/idGenerator.js +89 -0
  21. package/dist/browser/utilities/translation/translationProcessor.js +200 -0
  22. package/dist/browser/utils/io.js +95 -0
  23. package/dist/browser/validation/baseValidator.js +156 -0
  24. package/dist/browser/validation/gridsetValidator.js +356 -0
  25. package/dist/browser/validation/obfValidator.js +500 -0
  26. package/dist/browser/validation/validationTypes.js +46 -0
  27. package/dist/cli/index.js +5 -5
  28. package/dist/core/analyze.d.ts +2 -2
  29. package/dist/core/analyze.js +2 -2
  30. package/dist/core/baseProcessor.d.ts +5 -4
  31. package/dist/core/baseProcessor.js +22 -27
  32. package/dist/core/treeStructure.d.ts +5 -5
  33. package/dist/core/treeStructure.js +1 -4
  34. package/dist/index.browser.d.ts +37 -0
  35. package/dist/index.browser.js +99 -0
  36. package/dist/index.d.ts +1 -48
  37. package/dist/index.js +1 -136
  38. package/dist/index.node.d.ts +48 -0
  39. package/dist/index.node.js +152 -0
  40. package/dist/processors/applePanelsProcessor.d.ts +5 -4
  41. package/dist/processors/applePanelsProcessor.js +58 -62
  42. package/dist/processors/astericsGridProcessor.d.ts +7 -6
  43. package/dist/processors/astericsGridProcessor.js +31 -42
  44. package/dist/processors/dotProcessor.d.ts +5 -4
  45. package/dist/processors/dotProcessor.js +25 -33
  46. package/dist/processors/excelProcessor.d.ts +4 -3
  47. package/dist/processors/excelProcessor.js +6 -3
  48. package/dist/processors/gridset/crypto.d.ts +18 -0
  49. package/dist/processors/gridset/crypto.js +57 -0
  50. package/dist/processors/gridset/helpers.d.ts +1 -1
  51. package/dist/processors/gridset/helpers.js +18 -8
  52. package/dist/processors/gridset/password.d.ts +20 -3
  53. package/dist/processors/gridset/password.js +29 -12
  54. package/dist/processors/gridset/symbols.js +63 -46
  55. package/dist/processors/gridset/wordlistHelpers.d.ts +3 -3
  56. package/dist/processors/gridset/wordlistHelpers.js +21 -20
  57. package/dist/processors/gridsetProcessor.d.ts +7 -12
  58. package/dist/processors/gridsetProcessor.js +116 -77
  59. package/dist/processors/obfProcessor.d.ts +9 -7
  60. package/dist/processors/obfProcessor.js +131 -56
  61. package/dist/processors/obfsetProcessor.d.ts +5 -4
  62. package/dist/processors/obfsetProcessor.js +10 -16
  63. package/dist/processors/opmlProcessor.d.ts +5 -4
  64. package/dist/processors/opmlProcessor.js +27 -34
  65. package/dist/processors/snapProcessor.d.ts +8 -7
  66. package/dist/processors/snapProcessor.js +15 -12
  67. package/dist/processors/touchchatProcessor.d.ts +8 -7
  68. package/dist/processors/touchchatProcessor.js +22 -17
  69. package/dist/types/aac.d.ts +0 -2
  70. package/dist/types/aac.js +2 -0
  71. package/dist/utils/io.d.ts +12 -0
  72. package/dist/utils/io.js +107 -0
  73. package/dist/validation/gridsetValidator.js +10 -9
  74. package/dist/validation/snapValidator.js +28 -35
  75. package/docs/BROWSER_USAGE.md +618 -0
  76. package/docs/PAGESET_GETTING_STARTED.md +185 -0
  77. package/examples/README.md +77 -0
  78. package/examples/browser-test-server.js +81 -0
  79. package/examples/browser-test.html +331 -0
  80. package/examples/vitedemo/QUICKSTART.md +75 -0
  81. package/examples/vitedemo/README.md +157 -0
  82. package/examples/vitedemo/index.html +531 -0
  83. package/examples/vitedemo/package-lock.json +1221 -0
  84. package/examples/vitedemo/package.json +18 -0
  85. package/examples/vitedemo/src/main.ts +871 -0
  86. package/examples/vitedemo/test-files/example.dot +14 -0
  87. package/examples/vitedemo/test-files/example.grd +1 -0
  88. package/examples/vitedemo/test-files/example.gridset +0 -0
  89. package/examples/vitedemo/test-files/example.obz +0 -0
  90. package/examples/vitedemo/test-files/example.opml +18 -0
  91. package/examples/vitedemo/test-files/simple.obf +53 -0
  92. package/examples/vitedemo/tsconfig.json +24 -0
  93. package/examples/vitedemo/vite.config.ts +34 -0
  94. package/package.json +21 -4
@@ -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
- zip = new adm_zip_1.default(gridsetBuffer);
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
- entries.forEach((entry) => {
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().toString('utf8');
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
- return;
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
- return;
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
- zip = new adm_zip_1.default(gridsetBuffer);
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
- entries.forEach((entry) => {
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().toString('utf8');
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
- return;
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.updateFile(entry, Buffer.from(updatedXml, 'utf8'));
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.toBuffer();
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: string | Buffer): string[];
29
- loadIntoTree(filePathOrBuffer: string | Buffer): AACTree;
30
- processTexts(filePathOrBuffer: string | Buffer, translations: Map<string, string>, outputPath: string): Buffer;
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
- }): Buffer;
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;
@@ -1,19 +1,36 @@
1
1
  "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
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 __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
4
24
  };
5
25
  Object.defineProperty(exports, "__esModule", { value: true });
6
26
  exports.GridsetProcessor = void 0;
7
27
  const baseProcessor_1 = require("../core/baseProcessor");
8
28
  const treeStructure_1 = require("../core/treeStructure");
9
- const adm_zip_1 = __importDefault(require("adm-zip"));
10
- const fs_1 = __importDefault(require("fs"));
11
29
  const fast_xml_parser_1 = require("fast-xml-parser");
12
30
  const resolver_1 = require("./gridset/resolver");
13
31
  const translationProcessor_1 = require("../utilities/translation/translationProcessor");
14
32
  const password_1 = require("./gridset/password");
15
- const crypto_1 = __importDefault(require("crypto"));
16
- const zlib_1 = __importDefault(require("zlib"));
33
+ const crypto_1 = require("./gridset/crypto");
17
34
  const gridsetValidator_1 = require("../validation/gridsetValidator");
18
35
  // New imports for enhanced Grid 3 support
19
36
  const pluginTypes_1 = require("./gridset/pluginTypes");
@@ -22,33 +39,35 @@ const symbols_1 = require("./gridset/symbols");
22
39
  const resolver_2 = require("./gridset/resolver");
23
40
  const idGenerator_1 = require("../utilities/analytics/utils/idGenerator");
24
41
  const symbolAlignment_1 = require("./gridset/symbolAlignment");
25
- class GridsetProcessor extends baseProcessor_1.BaseProcessor {
26
- constructor(options) {
27
- super(options);
28
- }
29
- /**
30
- * Decrypt and inflate a Grid3 encrypted payload (DesktopContentEncrypter).
31
- * Uses AES-256-CBC with key/IV derived from the password padded with spaces
32
- * and then Deflate decompression.
33
- */
34
- decryptGridsetEntry(buffer, password) {
35
- const pwd = (password || 'Chocolate').padEnd(32, ' ');
36
- const key = Buffer.from(pwd.slice(0, 32), 'utf8');
37
- const iv = Buffer.from(pwd.slice(0, 16), 'utf8');
42
+ const io_1 = require("../utils/io");
43
+ let JSZipModule;
44
+ async function getJSZip() {
45
+ if (!JSZipModule) {
38
46
  try {
39
- const decipher = crypto_1.default.createDecipheriv('aes-256-cbc', key, iv);
40
- const decrypted = Buffer.concat([decipher.update(buffer), decipher.final()]);
47
+ // Try ES module import first (browser/Vite)
48
+ const module = await Promise.resolve().then(() => __importStar(require('jszip')));
49
+ JSZipModule = module.default || module;
50
+ }
51
+ catch (error) {
52
+ // Fall back to CommonJS require (Node.js)
41
53
  try {
42
- return zlib_1.default.inflateSync(decrypted);
54
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
55
+ const module = require('jszip');
56
+ JSZipModule = module.default || module;
43
57
  }
44
- catch {
45
- // If data isn't deflated, return raw decrypted bytes
46
- return decrypted;
58
+ catch (err2) {
59
+ throw new Error('Zip handling requires JSZip in this environment.');
47
60
  }
48
61
  }
49
- catch {
50
- return buffer;
51
- }
62
+ }
63
+ if (!JSZipModule) {
64
+ throw new Error('Zip handling requires JSZip in this environment.');
65
+ }
66
+ return JSZipModule;
67
+ }
68
+ class GridsetProcessor extends baseProcessor_1.BaseProcessor {
69
+ constructor(options) {
70
+ super(options);
52
71
  }
53
72
  // Determine password to use when opening encrypted gridset archives (.gridsetx)
54
73
  getGridsetPassword(source) {
@@ -394,8 +413,8 @@ class GridsetProcessor extends baseProcessor_1.BaseProcessor {
394
413
  }
395
414
  return undefined;
396
415
  }
397
- extractTexts(filePathOrBuffer) {
398
- const tree = this.loadIntoTree(filePathOrBuffer);
416
+ async extractTexts(filePathOrBuffer) {
417
+ const tree = await this.loadIntoTree(filePathOrBuffer);
399
418
  const texts = [];
400
419
  for (const pageId in tree.pages) {
401
420
  const page = tree.pages[pageId];
@@ -410,11 +429,13 @@ class GridsetProcessor extends baseProcessor_1.BaseProcessor {
410
429
  }
411
430
  return texts;
412
431
  }
413
- loadIntoTree(filePathOrBuffer) {
432
+ async loadIntoTree(filePathOrBuffer) {
414
433
  const tree = new treeStructure_1.AACTree();
415
434
  let zip;
416
435
  try {
417
- zip = new adm_zip_1.default(filePathOrBuffer);
436
+ const JSZip = await getJSZip();
437
+ const zipInput = (0, io_1.readBinaryFromInput)(filePathOrBuffer);
438
+ zip = await JSZip.loadAsync(zipInput);
418
439
  }
419
440
  catch (error) {
420
441
  throw new Error(`Invalid ZIP file format: ${error.message}`);
@@ -430,18 +451,22 @@ class GridsetProcessor extends baseProcessor_1.BaseProcessor {
430
451
  isSmartBox: isEncryptedArchive, // SmartBox files are .gridsetx encrypted archives
431
452
  passwordProtected: !!password,
432
453
  };
433
- const readEntryBuffer = (entry) => {
434
- const raw = entry.getData();
435
- if (!isEncryptedArchive)
454
+ const readEntryBuffer = async (entry) => {
455
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-argument
456
+ const raw = await entry.getData();
457
+ if (!isEncryptedArchive) {
458
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-return
436
459
  return raw;
437
- return this.decryptGridsetEntry(raw, encryptedContentPassword);
460
+ }
461
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-return
462
+ return (0, crypto_1.decryptGridsetEntry)(raw, encryptedContentPassword);
438
463
  };
439
464
  // Parse FileMap.xml if present to index dynamic files per grid
440
465
  const fileMapIndex = new Map();
441
466
  try {
442
467
  const fmEntry = entries.find((e) => e.entryName.endsWith('FileMap.xml'));
443
468
  if (fmEntry) {
444
- const fmXml = readEntryBuffer(fmEntry).toString('utf8');
469
+ const fmXml = (0, io_1.decodeText)(await readEntryBuffer(fmEntry));
445
470
  const fmData = parser.parse(fmXml);
446
471
  const entries = fmData?.FileMap?.Entries?.Entry || fmData?.fileMap?.entries?.entry;
447
472
  if (entries) {
@@ -476,7 +501,7 @@ class GridsetProcessor extends baseProcessor_1.BaseProcessor {
476
501
  const styleEntry = entries.find((entry) => entry.entryName.endsWith('styles.xml') || entry.entryName.endsWith('style.xml'));
477
502
  if (styleEntry) {
478
503
  try {
479
- const styleXmlContent = readEntryBuffer(styleEntry).toString('utf8');
504
+ const styleXmlContent = (0, io_1.decodeText)(await readEntryBuffer(styleEntry));
480
505
  const styleData = parser.parse(styleXmlContent);
481
506
  // Parse styles and store them in the map
482
507
  // Grid3 uses StyleData.Styles.Style with Key attribute
@@ -507,18 +532,23 @@ class GridsetProcessor extends baseProcessor_1.BaseProcessor {
507
532
  }
508
533
  }
509
534
  // Debug: log all entry names
510
- // console.log('Gridset zip entries:', entries.map(e => e.entryName));
535
+ console.log('[Gridset] Total zip entries:', entries.length);
536
+ const gridEntries = entries.filter((e) => e.entryName.startsWith('Grids/') && e.entryName.endsWith('grid.xml'));
537
+ console.log('[Gridset] Grid XML entries found:', gridEntries.length);
538
+ if (gridEntries.length > 0) {
539
+ console.log('[Gridset] First few grid entries:', gridEntries.slice(0, 3).map((e) => e.entryName));
540
+ }
511
541
  // First pass: collect all grid names and IDs for navigation resolution
512
542
  const gridNameToIdMap = new Map();
513
543
  const gridIdToNameMap = new Map();
514
- entries.forEach((entry) => {
544
+ for (const entry of entries) {
515
545
  if (entry.entryName.startsWith('Grids/') && entry.entryName.endsWith('grid.xml')) {
516
546
  try {
517
- const xmlContent = readEntryBuffer(entry).toString('utf8');
547
+ const xmlContent = (0, io_1.decodeText)(await readEntryBuffer(entry));
518
548
  const data = parser.parse(xmlContent);
519
549
  const grid = data.Grid || data.grid;
520
550
  if (!grid)
521
- return;
551
+ continue;
522
552
  const gridId = this.textOf(grid.GridGuid || grid.gridGuid || grid.id);
523
553
  const gridName = this.textOf(grid.Name) || this.textOf(grid.name) || this.textOf(grid['@_Name']);
524
554
  const folderMatch = entry.entryName.match(/^Grids\/([^/]+)\//);
@@ -541,32 +571,36 @@ class GridsetProcessor extends baseProcessor_1.BaseProcessor {
541
571
  // Skip errors in first pass
542
572
  }
543
573
  }
544
- });
574
+ }
545
575
  // Second pass: process each grid file in the gridset
546
- entries.forEach((entry) => {
576
+ for (const entry of entries) {
547
577
  // Only process files named grid.xml under Grids/ (any subdir)
548
578
  if (entry.entryName.startsWith('Grids/') && entry.entryName.endsWith('grid.xml')) {
549
579
  let xmlContent;
550
580
  try {
551
- xmlContent = readEntryBuffer(entry).toString('utf8');
581
+ const buffer = await readEntryBuffer(entry);
582
+ xmlContent = (0, io_1.decodeText)(buffer);
583
+ console.log(`[Gridset] Raw XML content (first 200 chars) for ${entry.entryName}:`, xmlContent.substring(0, 200));
552
584
  }
553
585
  catch (e) {
554
586
  // Skip unreadable files
555
- return;
587
+ continue;
556
588
  }
557
589
  let data;
558
590
  try {
559
591
  data = parser.parse(xmlContent);
592
+ console.log(`[Gridset] Parsed ${entry.entryName}, root keys:`, Object.keys(data));
560
593
  }
561
594
  catch (error) {
562
595
  // Skip malformed XML but log the specific error
563
596
  console.warn(`Malformed XML in ${entry.entryName}: ${error.message}`);
564
- return;
597
+ continue;
565
598
  }
566
599
  // Grid3 XML: <Grid> root
567
600
  const grid = data.Grid || data.grid;
568
601
  if (!grid) {
569
- return;
602
+ console.warn(`[Gridset] No Grid/grid found in ${entry.entryName}`);
603
+ continue;
570
604
  }
571
605
  // Defensive: GridGuid and Name required
572
606
  const gridId = this.textOf(grid.GridGuid || grid.gridGuid || grid.id);
@@ -578,7 +612,7 @@ class GridsetProcessor extends baseProcessor_1.BaseProcessor {
578
612
  gridName = match[1];
579
613
  }
580
614
  if (!gridId || !gridName) {
581
- return;
615
+ continue;
582
616
  }
583
617
  const page = new treeStructure_1.AACPage({
584
618
  id: String(gridId),
@@ -1334,7 +1368,7 @@ class GridsetProcessor extends baseProcessor_1.BaseProcessor {
1334
1368
  }
1335
1369
  tree.addPage(page);
1336
1370
  }
1337
- });
1371
+ }
1338
1372
  // After all pages are loaded, set parentId for navigation targets
1339
1373
  for (const pageId in tree.pages) {
1340
1374
  const page = tree.pages[pageId];
@@ -1351,7 +1385,7 @@ class GridsetProcessor extends baseProcessor_1.BaseProcessor {
1351
1385
  try {
1352
1386
  const settingsEntry = entries.find((e) => e.entryName.endsWith('settings.xml'));
1353
1387
  if (settingsEntry) {
1354
- const settingsXml = readEntryBuffer(settingsEntry).toString('utf8');
1388
+ const settingsXml = (0, io_1.decodeText)(await readEntryBuffer(settingsEntry));
1355
1389
  const settingsData = parser.parse(settingsXml);
1356
1390
  const gsName = settingsData?.GridSetSettings?.Name ||
1357
1391
  settingsData?.gridSetSettings?.name ||
@@ -1445,9 +1479,9 @@ class GridsetProcessor extends baseProcessor_1.BaseProcessor {
1445
1479
  tree.metadata = metadata;
1446
1480
  return tree;
1447
1481
  }
1448
- processTexts(filePathOrBuffer, translations, outputPath) {
1482
+ async processTexts(filePathOrBuffer, translations, outputPath) {
1449
1483
  // Load the tree, apply translations, and save to new file
1450
- const tree = this.loadIntoTree(filePathOrBuffer);
1484
+ const tree = await this.loadIntoTree(filePathOrBuffer);
1451
1485
  // Apply translations to all text content
1452
1486
  Object.values(tree.pages).forEach((page) => {
1453
1487
  // Translate page names
@@ -1505,8 +1539,8 @@ class GridsetProcessor extends baseProcessor_1.BaseProcessor {
1505
1539
  });
1506
1540
  });
1507
1541
  // Save the translated tree and return its content
1508
- this.saveFromTree(tree, outputPath);
1509
- return fs_1.default.readFileSync(outputPath);
1542
+ await this.saveFromTree(tree, outputPath);
1543
+ return (0, io_1.readBinaryFromInput)(outputPath);
1510
1544
  }
1511
1545
  /**
1512
1546
  * Extract symbol information from a gridset for LLM-based translation.
@@ -1517,8 +1551,8 @@ class GridsetProcessor extends baseProcessor_1.BaseProcessor {
1517
1551
  * @param filePathOrBuffer - Path to gridset file or buffer
1518
1552
  * @returns Array of symbol information for LLM processing
1519
1553
  */
1520
- extractSymbolsForLLM(filePathOrBuffer) {
1521
- const tree = this.loadIntoTree(filePathOrBuffer);
1554
+ async extractSymbolsForLLM(filePathOrBuffer) {
1555
+ const tree = await this.loadIntoTree(filePathOrBuffer);
1522
1556
  // Collect all buttons from all pages
1523
1557
  const allButtons = [];
1524
1558
  Object.values(tree.pages).forEach((page) => {
@@ -1547,8 +1581,8 @@ class GridsetProcessor extends baseProcessor_1.BaseProcessor {
1547
1581
  * @param options - Translation options (e.g., allowPartial for testing)
1548
1582
  * @returns Buffer of the translated gridset
1549
1583
  */
1550
- processLLMTranslations(filePathOrBuffer, llmTranslations, outputPath, options) {
1551
- const tree = this.loadIntoTree(filePathOrBuffer);
1584
+ async processLLMTranslations(filePathOrBuffer, llmTranslations, outputPath, options) {
1585
+ const tree = await this.loadIntoTree(filePathOrBuffer);
1552
1586
  // Validate translations using shared utility
1553
1587
  const buttonIds = Object.values(tree.pages).flatMap((page) => page.buttons.map((b) => b.id));
1554
1588
  (0, translationProcessor_1.validateTranslationResults)(llmTranslations, buttonIds, options);
@@ -1585,14 +1619,17 @@ class GridsetProcessor extends baseProcessor_1.BaseProcessor {
1585
1619
  });
1586
1620
  });
1587
1621
  // Save and return
1588
- this.saveFromTree(tree, outputPath);
1589
- return fs_1.default.readFileSync(outputPath);
1622
+ await this.saveFromTree(tree, outputPath);
1623
+ return (0, io_1.readBinaryFromInput)(outputPath);
1590
1624
  }
1591
- saveFromTree(tree, outputPath) {
1592
- const zip = new adm_zip_1.default();
1625
+ async saveFromTree(tree, outputPath) {
1626
+ const JSZip = await getJSZip();
1627
+ const zip = new JSZip();
1593
1628
  if (Object.keys(tree.pages).length === 0) {
1594
1629
  // Create empty zip for empty tree
1595
- zip.writeZip(outputPath);
1630
+ const zipBuffer = await zip.generateAsync({ type: 'uint8array' });
1631
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
1632
+ require('fs').writeFileSync(outputPath, zipBuffer);
1596
1633
  return;
1597
1634
  }
1598
1635
  // Collect all unique styles from pages and buttons
@@ -1664,7 +1701,7 @@ class GridsetProcessor extends baseProcessor_1.BaseProcessor {
1664
1701
  suppressEmptyNode: true,
1665
1702
  });
1666
1703
  const settingsXmlContent = settingsBuilder.build(settingsData);
1667
- zip.addFile('Settings0/settings.xml', Buffer.from(settingsXmlContent, 'utf8'));
1704
+ zip.file('Settings0/settings.xml', settingsXmlContent, { binary: false });
1668
1705
  // Create Settings0/Styles/style.xml if there are styles
1669
1706
  if (uniqueStyles.size > 0) {
1670
1707
  const stylesArray = Array.from(uniqueStyles.values()).map(({ id, style }) => {
@@ -1697,7 +1734,7 @@ class GridsetProcessor extends baseProcessor_1.BaseProcessor {
1697
1734
  indentBy: ' ',
1698
1735
  });
1699
1736
  const styleXmlContent = styleBuilder.build(styleData);
1700
- zip.addFile('Settings0/Styles/styles.xml', Buffer.from(styleXmlContent, 'utf8'));
1737
+ zip.file('Settings0/Styles/styles.xml', styleXmlContent, { binary: false });
1701
1738
  }
1702
1739
  // Collect grid file paths for FileMap.xml
1703
1740
  const gridFilePaths = [];
@@ -1780,8 +1817,8 @@ class GridsetProcessor extends baseProcessor_1.BaseProcessor {
1780
1817
  }
1781
1818
  }
1782
1819
  const cellData = {
1783
- '@_X': position.x, // Grid3 uses 0-based X coordinates (defaults to 0 when omitted)
1784
- '@_Y': position.y + yOffset, // Grid3 uses 0-based Y coordinates with workspace offset
1820
+ '@_X': position.x + 1, // Grid3 uses 1-based X coordinates
1821
+ '@_Y': position.y + yOffset + 1, // Grid3 uses 1-based Y coordinates with workspace offset
1785
1822
  '@_ColumnSpan': position.columnSpan,
1786
1823
  '@_RowSpan': position.rowSpan,
1787
1824
  Content: {
@@ -1836,16 +1873,16 @@ class GridsetProcessor extends baseProcessor_1.BaseProcessor {
1836
1873
  });
1837
1874
  const xmlContent = builder.build(gridData);
1838
1875
  // Add to zip in Grids folder with proper Grid3 naming
1839
- const gridPath = `Grids\\${page.name || page.id}\\grid.xml`;
1876
+ const gridPath = `Grids/${page.name || page.id}/grid.xml`;
1840
1877
  gridFilePaths.push(gridPath);
1841
- zip.addFile(gridPath, Buffer.from(xmlContent, 'utf8'));
1878
+ zip.file(gridPath, xmlContent, { binary: false });
1842
1879
  });
1843
1880
  // Write image files to ZIP
1844
1881
  buttonImages.forEach((imgData) => {
1845
1882
  if (imgData.imageData && imgData.imageData.length > 0) {
1846
1883
  // Create image path in the grid's directory
1847
- const imagePath = `Grids\\${imgData.pageName}\\${imgData.x}-${imgData.y}-0-text-0.${imgData.ext}`;
1848
- zip.addFile(imagePath, imgData.imageData);
1884
+ const imagePath = `Grids/${imgData.pageName}/${imgData.x}-${imgData.y}-0-text-0.${imgData.ext}`;
1885
+ zip.file(imagePath, imgData.imageData);
1849
1886
  }
1850
1887
  });
1851
1888
  // Create FileMap.xml to map all grid files with their dynamic image files
@@ -1856,13 +1893,13 @@ class GridsetProcessor extends baseProcessor_1.BaseProcessor {
1856
1893
  Entries: {
1857
1894
  Entry: gridFilePaths.map((gridPath) => {
1858
1895
  // Find all image files for this grid
1859
- const gridName = gridPath.match(/Grids\\([^\\]+)\\grid\.xml$/)?.[1] || '';
1896
+ const gridName = gridPath.match(/Grids\/([^/]+)\/grid\.xml$/)?.[1] || '';
1860
1897
  const imageFiles = [];
1861
1898
  // Collect image filenames for buttons on this page
1862
- // IMPORTANT: FileMap.xml requires full paths like "Grids\PageName\1-5-0-text-0.png"
1899
+ // IMPORTANT: FileMap.xml requires full paths like "Grids/PageName/1-5-0-text-0.png"
1863
1900
  buttonImages.forEach((imgData) => {
1864
1901
  if (imgData.pageName === gridName && imgData.imageData.length > 0) {
1865
- const imagePath = `Grids\\${gridName}\\${imgData.x}-${imgData.y}-0-text-0.${imgData.ext}`;
1902
+ const imagePath = `Grids/${gridName}/${imgData.x}-${imgData.y}-0-text-0.${imgData.ext}`;
1866
1903
  imageFiles.push(imagePath);
1867
1904
  }
1868
1905
  });
@@ -1884,9 +1921,11 @@ class GridsetProcessor extends baseProcessor_1.BaseProcessor {
1884
1921
  indentBy: ' ',
1885
1922
  });
1886
1923
  const fileMapXmlContent = fileMapBuilder.build(fileMapData);
1887
- zip.addFile('FileMap.xml', Buffer.from(fileMapXmlContent, 'utf8'));
1924
+ zip.file('FileMap.xml', fileMapXmlContent, { binary: false });
1888
1925
  // Write the zip file
1889
- zip.writeZip(outputPath);
1926
+ const zipBuffer = await zip.generateAsync({ type: 'uint8array' });
1927
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
1928
+ require('fs').writeFileSync(outputPath, zipBuffer);
1890
1929
  }
1891
1930
  // Helper method to calculate column definitions based on page layout
1892
1931
  calculateColumnDefinitions(page) {
@@ -2,6 +2,7 @@ import { BaseProcessor, ProcessorOptions, ExtractStringsResult, TranslatedString
2
2
  import { AACTree } from '../core/treeStructure';
3
3
  import { ValidationResult } from '../validation/validationTypes';
4
4
  import { type ButtonForTranslation, type LLMLTranslationResult } from '../utilities/translation/translationProcessor';
5
+ import { ProcessorInput } from '../utils/io';
5
6
  declare class ObfProcessor extends BaseProcessor {
6
7
  private zipFile?;
7
8
  private imageCache;
@@ -16,12 +17,12 @@ declare class ObfProcessor extends BaseProcessor {
16
17
  private extractImageAsDataUrl;
17
18
  private getMimeTypeFromFilename;
18
19
  private processBoard;
19
- extractTexts(filePathOrBuffer: string | Buffer): string[];
20
- loadIntoTree(filePathOrBuffer: string | Buffer): AACTree;
20
+ extractTexts(filePathOrBuffer: ProcessorInput): Promise<string[]>;
21
+ loadIntoTree(filePathOrBuffer: ProcessorInput): Promise<AACTree>;
21
22
  private buildGridMetadata;
22
23
  private createObfBoardFromPage;
23
- processTexts(filePathOrBuffer: string | Buffer, translations: Map<string, string>, outputPath: string): Buffer;
24
- saveFromTree(tree: AACTree, outputPath: string): void;
24
+ processTexts(filePathOrBuffer: ProcessorInput, translations: Map<string, string>, outputPath: string): Promise<Uint8Array>;
25
+ saveFromTree(tree: AACTree, outputPath: string): Promise<void>;
25
26
  /**
26
27
  * Extract strings with metadata for aac-tools-platform compatibility
27
28
  * Uses the generic implementation from BaseProcessor
@@ -47,7 +48,7 @@ declare class ObfProcessor extends BaseProcessor {
47
48
  * @param filePathOrBuffer - Path to OBF/OBZ file or buffer
48
49
  * @returns Array of symbol information for LLM processing
49
50
  */
50
- extractSymbolsForLLM(filePathOrBuffer: string | Buffer): ButtonForTranslation[];
51
+ extractSymbolsForLLM(filePathOrBuffer: ProcessorInput): Promise<ButtonForTranslation[]>;
51
52
  /**
52
53
  * Apply LLM translations with symbol information.
53
54
  * The LLM should provide translations with symbol attachments in the correct positions.
@@ -60,8 +61,9 @@ declare class ObfProcessor extends BaseProcessor {
60
61
  * @param options - Translation options (e.g., allowPartial for testing)
61
62
  * @returns Buffer of the translated OBF/OBZ file
62
63
  */
63
- processLLMTranslations(filePathOrBuffer: string | Buffer, llmTranslations: LLMLTranslationResult[], outputPath: string, options?: {
64
+ processLLMTranslations(filePathOrBuffer: ProcessorInput, llmTranslations: LLMLTranslationResult[], outputPath: string, options?: {
64
65
  allowPartial?: boolean;
65
- }): Buffer;
66
+ }): Promise<Uint8Array>;
67
+ private getObfValidator;
66
68
  }
67
69
  export { ObfProcessor };