@willwade/aac-processors 0.1.20 → 0.2.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.
Files changed (113) hide show
  1. package/dist/browser/core/baseProcessor.js +4 -0
  2. package/dist/browser/processors/applePanelsProcessor.js +33 -40
  3. package/dist/browser/processors/astericsGridProcessor.js +31 -26
  4. package/dist/browser/processors/dotProcessor.js +11 -12
  5. package/dist/browser/processors/gridset/colorUtils.js +354 -0
  6. package/dist/browser/processors/gridset/helpers.js +60 -53
  7. package/dist/browser/processors/gridset/index.js +61 -0
  8. package/dist/browser/processors/gridset/styleHelpers.js +205 -0
  9. package/dist/browser/processors/gridset/symbolExtractor.js +331 -0
  10. package/dist/browser/processors/gridset/symbolSearch.js +248 -0
  11. package/dist/browser/processors/gridset/symbols.js +39 -72
  12. package/dist/browser/processors/gridsetProcessor.js +39 -48
  13. package/dist/browser/processors/obfProcessor.js +39 -53
  14. package/dist/browser/processors/opmlProcessor.js +11 -12
  15. package/dist/browser/processors/snap/helpers.js +57 -49
  16. package/dist/browser/processors/snapProcessor.js +48 -51
  17. package/dist/browser/processors/touchchatProcessor.js +60 -52
  18. package/dist/browser/utilities/analytics/history.js +24 -18
  19. package/dist/browser/utilities/analytics/metrics/comparison.js +16 -16
  20. package/dist/browser/utilities/analytics/metrics/vocabulary.js +2 -2
  21. package/dist/browser/utilities/analytics/reference/browser.js +16 -16
  22. package/dist/browser/utilities/analytics/reference/index.js +44 -35
  23. package/dist/browser/utils/io.js +78 -21
  24. package/dist/browser/utils/sqlite.js +8 -10
  25. package/dist/browser/utils/zip.js +43 -43
  26. package/dist/browser/validation/baseValidator.js +5 -0
  27. package/dist/browser/validation/gridsetValidator.js +12 -20
  28. package/dist/browser/validation/obfValidator.js +6 -5
  29. package/dist/browser/validation/snapValidator.js +11 -7
  30. package/dist/browser/validation/touchChatValidator.js +23 -13
  31. package/dist/cli/index.js +22 -24
  32. package/dist/core/baseProcessor.d.ts +7 -7
  33. package/dist/core/baseProcessor.js +4 -0
  34. package/dist/processors/applePanelsProcessor.js +32 -39
  35. package/dist/processors/astericsGridProcessor.d.ts +4 -4
  36. package/dist/processors/astericsGridProcessor.js +30 -25
  37. package/dist/processors/dotProcessor.js +10 -11
  38. package/dist/processors/excelProcessor.d.ts +3 -3
  39. package/dist/processors/excelProcessor.js +14 -20
  40. package/dist/processors/gridset/helpers.d.ts +12 -14
  41. package/dist/processors/gridset/helpers.js +60 -79
  42. package/dist/processors/gridset/imageDebug.d.ts +3 -5
  43. package/dist/processors/gridset/imageDebug.js +4 -4
  44. package/dist/processors/gridset/password.d.ts +1 -1
  45. package/dist/processors/gridset/symbolExtractor.d.ts +5 -3
  46. package/dist/processors/gridset/symbolExtractor.js +15 -38
  47. package/dist/processors/gridset/symbolSearch.d.ts +11 -10
  48. package/dist/processors/gridset/symbolSearch.js +29 -51
  49. package/dist/processors/gridset/symbols.d.ts +8 -6
  50. package/dist/processors/gridset/symbols.js +38 -71
  51. package/dist/processors/gridset/wordlistHelpers.d.ts +4 -6
  52. package/dist/processors/gridset/wordlistHelpers.js +15 -74
  53. package/dist/processors/gridsetProcessor.d.ts +2 -2
  54. package/dist/processors/gridsetProcessor.js +38 -70
  55. package/dist/processors/obfProcessor.d.ts +2 -2
  56. package/dist/processors/obfProcessor.js +38 -75
  57. package/dist/processors/obfsetProcessor.js +2 -3
  58. package/dist/processors/opmlProcessor.js +10 -11
  59. package/dist/processors/snap/helpers.d.ts +9 -9
  60. package/dist/processors/snap/helpers.js +58 -76
  61. package/dist/processors/snapProcessor.d.ts +2 -2
  62. package/dist/processors/snapProcessor.js +47 -50
  63. package/dist/processors/touchchatProcessor.d.ts +2 -2
  64. package/dist/processors/touchchatProcessor.js +59 -51
  65. package/dist/types/aac.d.ts +2 -2
  66. package/dist/utilities/analytics/history.d.ts +8 -8
  67. package/dist/utilities/analytics/history.js +24 -18
  68. package/dist/utilities/analytics/index.d.ts +3 -2
  69. package/dist/utilities/analytics/index.js +9 -10
  70. package/dist/utilities/analytics/metrics/comparison.d.ts +1 -1
  71. package/dist/utilities/analytics/metrics/comparison.js +16 -16
  72. package/dist/utilities/analytics/metrics/vocabulary.d.ts +1 -1
  73. package/dist/utilities/analytics/metrics/vocabulary.js +2 -2
  74. package/dist/utilities/analytics/reference/browser.d.ts +9 -9
  75. package/dist/utilities/analytics/reference/browser.js +16 -16
  76. package/dist/utilities/analytics/reference/index.d.ts +25 -23
  77. package/dist/utilities/analytics/reference/index.js +43 -34
  78. package/dist/utilities/symbolTools.d.ts +8 -6
  79. package/dist/utilities/symbolTools.js +21 -18
  80. package/dist/utils/io.d.ts +24 -6
  81. package/dist/utils/io.js +79 -25
  82. package/dist/utils/sqlite.d.ts +3 -1
  83. package/dist/utils/sqlite.js +7 -9
  84. package/dist/utils/zip.d.ts +7 -3
  85. package/dist/utils/zip.js +43 -43
  86. package/dist/validation/applePanelsValidator.d.ts +2 -1
  87. package/dist/validation/applePanelsValidator.js +10 -11
  88. package/dist/validation/astericsValidator.d.ts +2 -1
  89. package/dist/validation/astericsValidator.js +5 -4
  90. package/dist/validation/baseValidator.d.ts +2 -2
  91. package/dist/validation/baseValidator.js +5 -0
  92. package/dist/validation/dotValidator.d.ts +2 -1
  93. package/dist/validation/dotValidator.js +5 -4
  94. package/dist/validation/excelValidator.d.ts +2 -1
  95. package/dist/validation/excelValidator.js +5 -4
  96. package/dist/validation/gridsetValidator.d.ts +2 -1
  97. package/dist/validation/gridsetValidator.js +11 -22
  98. package/dist/validation/index.d.ts +2 -2
  99. package/dist/validation/index.js +5 -4
  100. package/dist/validation/obfValidator.d.ts +2 -1
  101. package/dist/validation/obfValidator.js +5 -4
  102. package/dist/validation/obfsetValidator.d.ts +2 -1
  103. package/dist/validation/obfsetValidator.js +5 -4
  104. package/dist/validation/opmlValidator.d.ts +2 -1
  105. package/dist/validation/opmlValidator.js +5 -4
  106. package/dist/validation/snapValidator.d.ts +2 -1
  107. package/dist/validation/snapValidator.js +10 -6
  108. package/dist/validation/touchChatValidator.d.ts +4 -6
  109. package/dist/validation/touchChatValidator.js +22 -12
  110. package/dist/validation/validationTypes.d.ts +8 -1
  111. package/package.json +1 -1
  112. package/dist/core/fileProcessor.d.ts +0 -7
  113. package/dist/core/fileProcessor.js +0 -57
@@ -1,30 +1,4 @@
1
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 __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;
24
- };
25
- var __importDefault = (this && this.__importDefault) || function (mod) {
26
- return (mod && mod.__esModule) ? mod : { "default": mod };
27
- };
28
2
  Object.defineProperty(exports, "__esModule", { value: true });
29
3
  exports.getPageTokenImageMap = getPageTokenImageMap;
30
4
  exports.getAllowedImageEntries = getAllowedImageEntries;
@@ -38,10 +12,9 @@ exports.isSnapInstalled = isSnapInstalled;
38
12
  exports.readSnapUsage = readSnapUsage;
39
13
  exports.readSnapUsageForUser = readSnapUsageForUser;
40
14
  const treeStructure_1 = require("../../core/treeStructure");
41
- const fs = __importStar(require("fs"));
42
- const path = __importStar(require("path"));
43
- const better_sqlite3_1 = __importDefault(require("better-sqlite3"));
44
15
  const dotnetTicks_1 = require("../../utils/dotnetTicks");
16
+ const io_1 = require("../../utils/io");
17
+ const sqlite_1 = require("../../utils/sqlite");
45
18
  // Minimal Snap helpers (stubs) to align with processors/<engine>/helpers pattern
46
19
  // NOTE: Snap files can store different types of image data in PageSetData:
47
20
  // - PNG/JPEG binaries: Actual images that can be displayed
@@ -50,7 +23,8 @@ const dotnetTicks_1 = require("../../utils/dotnetTicks");
50
23
  // We extract PNG/JPEG images but skip vector graphics (requires renderer).
51
24
  // NOTE: Snap buttons currently do not populate resolvedImageEntry; these helpers
52
25
  // therefore return empty collections until image resolution is implemented.
53
- function collectFiles(root, matcher, maxDepth = 3) {
26
+ async function collectFiles(root, matcher, maxDepth = 3, fileAdapter = io_1.defaultFileAdapter) {
27
+ const { listDir, join, isDirectory } = fileAdapter;
54
28
  const results = new Set();
55
29
  const stack = [{ dir: root, depth: 0 }];
56
30
  while (stack.length > 0) {
@@ -61,14 +35,14 @@ function collectFiles(root, matcher, maxDepth = 3) {
61
35
  continue;
62
36
  let entries;
63
37
  try {
64
- entries = fs.readdirSync(current.dir, { withFileTypes: true });
38
+ entries = await listDir(current.dir);
65
39
  }
66
40
  catch (error) {
67
41
  continue;
68
42
  }
69
43
  for (const entry of entries) {
70
- const fullPath = path.join(current.dir, entry.name);
71
- if (entry.isDirectory()) {
44
+ const fullPath = join(current.dir, entry);
45
+ if (await isDirectory(entry)) {
72
46
  stack.push({ dir: fullPath, depth: current.depth + 1 });
73
47
  }
74
48
  else if (matcher(fullPath)) {
@@ -122,17 +96,15 @@ function getAllowedImageEntries(tree) {
122
96
  * @param entryPath Symbol identifier (e.g., "SYM:12345")
123
97
  * @returns Image data buffer or null if not found
124
98
  */
125
- function openImage(dbOrFile, entryPath) {
99
+ async function openImage(dbOrFile, entryPath, fileAdapter = io_1.defaultFileAdapter) {
100
+ const { mkTempDir, join, writeBinaryToPath, removePath, dirname } = fileAdapter;
126
101
  let dbPath;
127
102
  let cleanupNeeded = false;
128
103
  // Handle Buffer input by writing to temp file
129
104
  if (Buffer.isBuffer(dbOrFile)) {
130
- if (typeof fs.mkdtempSync !== 'function') {
131
- return null; // Not in Node environment
132
- }
133
- const tempDir = fs.mkdtempSync(path.join(process.cwd(), 'snap-'));
134
- dbPath = path.join(tempDir, 'temp.sps');
135
- fs.writeFileSync(dbPath, dbOrFile);
105
+ const tempDir = await mkTempDir(join(process.cwd(), 'snap-'));
106
+ dbPath = join(tempDir, 'temp.sps');
107
+ await writeBinaryToPath(dbPath, dbOrFile);
136
108
  cleanupNeeded = true;
137
109
  }
138
110
  else if (typeof dbOrFile === 'string') {
@@ -141,9 +113,10 @@ function openImage(dbOrFile, entryPath) {
141
113
  else {
142
114
  return null;
143
115
  }
116
+ const better_sqlite3 = (0, io_1.getNodeRequire)()('better-sqlite3');
144
117
  let db = null;
145
118
  try {
146
- db = new better_sqlite3_1.default(dbPath, { readonly: true });
119
+ db = new better_sqlite3.Database(dbPath, { readonly: true });
147
120
  // Query PageSetData for the symbol
148
121
  const row = db
149
122
  .prepare('SELECT Id, Identifier, Data FROM PageSetData WHERE Identifier = ?')
@@ -166,9 +139,9 @@ function openImage(dbOrFile, entryPath) {
166
139
  }
167
140
  if (cleanupNeeded && dbPath) {
168
141
  try {
169
- fs.unlinkSync(dbPath);
170
- const dir = path.dirname(dbPath);
171
- fs.rmdirSync(dir);
142
+ await removePath(dbPath);
143
+ const dir = dirname(dbPath);
144
+ await removePath(dir);
172
145
  }
173
146
  catch (e) {
174
147
  // Ignore cleanup errors
@@ -182,7 +155,8 @@ function openImage(dbOrFile, entryPath) {
182
155
  * @param packageNamePattern Optional pattern to filter package names (default: 'TobiiDynavox')
183
156
  * @returns Array of Snap package path information
184
157
  */
185
- function findSnapPackages(packageNamePattern = 'TobiiDynavox') {
158
+ async function findSnapPackages(packageNamePattern = 'TobiiDynavox', fileAdapter = io_1.defaultFileAdapter) {
159
+ const { join, listDir, isDirectory, pathExists } = fileAdapter;
186
160
  const results = [];
187
161
  // Only works on Windows
188
162
  if (process.platform !== 'win32') {
@@ -193,22 +167,22 @@ function findSnapPackages(packageNamePattern = 'TobiiDynavox') {
193
167
  if (!localAppData) {
194
168
  return results;
195
169
  }
196
- const packagesPath = path.join(localAppData, 'Packages');
170
+ const packagesPath = join(localAppData, 'Packages');
197
171
  // Check if Packages directory exists
198
- if (!fs.existsSync(packagesPath)) {
172
+ if (!(await pathExists(packagesPath))) {
199
173
  return results;
200
174
  }
201
175
  // Enumerate packages
202
- const packages = fs.readdirSync(packagesPath, { withFileTypes: true });
176
+ const packages = await listDir(packagesPath);
203
177
  for (const packageDir of packages) {
204
- if (!packageDir.isDirectory())
178
+ if (!(await isDirectory(packageDir)))
205
179
  continue;
206
- const packageName = packageDir.name;
180
+ const packageName = packageDir;
207
181
  // Filter by pattern
208
182
  if (packageName.includes(packageNamePattern)) {
209
183
  results.push({
210
184
  packageName,
211
- packagePath: path.join(packagesPath, packageName),
185
+ packagePath: join(packagesPath, packageName),
212
186
  });
213
187
  }
214
188
  }
@@ -224,8 +198,8 @@ function findSnapPackages(packageNamePattern = 'TobiiDynavox') {
224
198
  * @param packageNamePattern Optional pattern to filter package names (default: 'TobiiDynavox')
225
199
  * @returns Path to the first matching Snap package, or null if not found
226
200
  */
227
- function findSnapPackagePath(packageNamePattern = 'TobiiDynavox') {
228
- const packages = findSnapPackages(packageNamePattern);
201
+ async function findSnapPackagePath(packageNamePattern = 'TobiiDynavox', fileAdapter) {
202
+ const packages = await findSnapPackages(packageNamePattern, fileAdapter);
229
203
  return packages.length > 0 ? packages[0].packagePath : null;
230
204
  }
231
205
  /**
@@ -235,32 +209,33 @@ function findSnapPackagePath(packageNamePattern = 'TobiiDynavox') {
235
209
  * @param packageNamePattern Optional package filter (default TobiiDynavox)
236
210
  * @returns Array of user info with vocab paths
237
211
  */
238
- function findSnapUsers(packageNamePattern = 'TobiiDynavox') {
212
+ async function findSnapUsers(packageNamePattern = 'TobiiDynavox', fileAdapter = io_1.defaultFileAdapter) {
213
+ const { join, listDir, isDirectory, pathExists } = fileAdapter;
239
214
  const results = [];
240
215
  if (process.platform !== 'win32') {
241
216
  return results;
242
217
  }
243
- const packagePath = findSnapPackagePath(packageNamePattern);
218
+ const packagePath = await findSnapPackagePath(packageNamePattern, fileAdapter);
244
219
  if (!packagePath) {
245
220
  return results;
246
221
  }
247
- const usersRoot = path.join(packagePath, 'LocalState', 'Users');
248
- if (!fs.existsSync(usersRoot)) {
222
+ const usersRoot = join(packagePath, 'LocalState', 'Users');
223
+ if (!(await pathExists(usersRoot))) {
249
224
  return results;
250
225
  }
251
- const entries = fs.readdirSync(usersRoot, { withFileTypes: true });
226
+ const entries = await listDir(usersRoot);
252
227
  for (const entry of entries) {
253
- if (!entry.isDirectory())
228
+ if (!(await isDirectory(entry)))
254
229
  continue;
255
- if (entry.name.toLowerCase().startsWith('swiftkey'))
230
+ if (entry.toLowerCase().startsWith('swiftkey'))
256
231
  continue;
257
- const userPath = path.join(usersRoot, entry.name);
258
- const vocabPaths = collectFiles(userPath, (full) => {
259
- const ext = path.extname(full).toLowerCase();
232
+ const userPath = join(usersRoot, entry);
233
+ const vocabPaths = await collectFiles(userPath, (full) => {
234
+ const ext = (0, io_1.extname)(full).toLowerCase();
260
235
  return ext === '.sps' || ext === '.spb';
261
- }, 2);
236
+ }, 2, fileAdapter);
262
237
  results.push({
263
- userId: entry.name,
238
+ userId: entry,
264
239
  userPath,
265
240
  vocabPaths,
266
241
  });
@@ -273,8 +248,9 @@ function findSnapUsers(packageNamePattern = 'TobiiDynavox') {
273
248
  * @param packageNamePattern Optional package filter
274
249
  * @returns Array of vocab file paths
275
250
  */
276
- function findSnapUserVocabularies(userId, packageNamePattern = 'TobiiDynavox') {
277
- const users = findSnapUsers(packageNamePattern).filter((u) => !userId || u.userId === userId);
251
+ async function findSnapUserVocabularies(userId, packageNamePattern = 'TobiiDynavox', fileAdapter) {
252
+ const allUsers = await findSnapUsers(packageNamePattern, fileAdapter);
253
+ const users = allUsers.filter((u) => !userId || u.userId === userId);
278
254
  return users.flatMap((u) => u.vocabPaths);
279
255
  }
280
256
  /**
@@ -284,11 +260,13 @@ function findSnapUserVocabularies(userId, packageNamePattern = 'TobiiDynavox') {
284
260
  * @param packageNamePattern Optional package filter
285
261
  * @returns Array of history file paths (may be empty if not found)
286
262
  */
287
- function findSnapUserHistory(userId, packageNamePattern = 'TobiiDynavox') {
288
- const user = findSnapUsers(packageNamePattern).find((u) => u.userId === userId);
263
+ async function findSnapUserHistory(userId, packageNamePattern = 'TobiiDynavox', fileAdapter = io_1.defaultFileAdapter) {
264
+ const { basename } = fileAdapter;
265
+ const allUsers = await findSnapUsers(packageNamePattern, fileAdapter);
266
+ const user = allUsers.find((u) => u.userId === userId);
289
267
  if (!user)
290
268
  return [];
291
- return collectFiles(user.userPath, (full) => path.basename(full).toLowerCase().includes('history'), 2);
269
+ return await collectFiles(user.userPath, (full) => basename(full).toLowerCase().includes('history'), 2, fileAdapter);
292
270
  }
293
271
  /**
294
272
  * Check whether TD Snap appears to be installed (Windows only)
@@ -301,10 +279,12 @@ function isSnapInstalled(packageNamePattern = 'TobiiDynavox') {
301
279
  /**
302
280
  * Read Snap usage history from a pageset file (.sps/.spb)
303
281
  */
304
- function readSnapUsage(pagesetPath) {
305
- if (!fs.existsSync(pagesetPath))
282
+ async function readSnapUsage(pagesetPath, fileAdapter = io_1.defaultFileAdapter) {
283
+ const { pathExists } = fileAdapter;
284
+ if (!(await pathExists(pagesetPath)))
306
285
  return [];
307
- const db = new better_sqlite3_1.default(pagesetPath, { readonly: true });
286
+ const Database = (0, sqlite_1.requireBetterSqlite3)();
287
+ const db = new Database(pagesetPath, { readonly: true });
308
288
  const tableCheck = db
309
289
  .prepare("SELECT name FROM sqlite_master WHERE type='table' AND name IN ('ButtonUsage','Button')")
310
290
  .all();
@@ -358,8 +338,10 @@ function readSnapUsage(pagesetPath) {
358
338
  /**
359
339
  * Read Snap usage history for a user (all pagesets)
360
340
  */
361
- function readSnapUsageForUser(userId, packageNamePattern = 'TobiiDynavox') {
362
- const users = findSnapUsers(packageNamePattern).filter((u) => !userId || u.userId === userId);
341
+ async function readSnapUsageForUser(userId, packageNamePattern = 'TobiiDynavox') {
342
+ const allUsers = await findSnapUsers(packageNamePattern);
343
+ const users = allUsers.filter((u) => !userId || u.userId === userId);
363
344
  const pagesets = users.flatMap((u) => u.vocabPaths);
364
- return pagesets.flatMap((p) => readSnapUsage(p));
345
+ const usage = await Promise.all(pagesets.map(async (p) => await readSnapUsage(p)));
346
+ return usage.flat();
365
347
  }
@@ -54,9 +54,9 @@ declare class SnapProcessor extends BaseProcessor {
54
54
  * Get available PageLayouts for a Snap file
55
55
  * Useful for UI components that want to let users select layout size
56
56
  * @param filePath - Path to the Snap file
57
- * @returns Array of available PageLayouts with their dimensions
57
+ * @returns Promise resolving to available PageLayouts with their dimensions
58
58
  */
59
- getAvailablePageLayouts(filePath: string): PageLayoutInfo[];
59
+ getAvailablePageLayouts(filePath: string): Promise<PageLayoutInfo[]>;
60
60
  }
61
61
  /**
62
62
  * Interface for PageLayout information returned by getAvailablePageLayouts
@@ -7,7 +7,6 @@ const idGenerator_1 = require("../utilities/analytics/utils/idGenerator");
7
7
  const snapValidator_1 = require("../validation/snapValidator");
8
8
  const io_1 = require("../utils/io");
9
9
  const sqlite_1 = require("../utils/sqlite");
10
- const zip_1 = require("../utils/zip");
11
10
  /**
12
11
  * Convert a Buffer or Uint8Array to base64 string (browser and Node compatible)
13
12
  * Node.js Buffers support toString('base64'), but Uint8Arrays in browser do not.
@@ -39,15 +38,15 @@ function mapSnapVisibility(visible) {
39
38
  return visible === 0 ? 'Hidden' : 'Visible';
40
39
  }
41
40
  class SnapProcessor extends baseProcessor_1.BaseProcessor {
42
- constructor(symbolResolver = null, options = {}) {
41
+ constructor(symbolResolver = null, options) {
43
42
  super(options);
44
43
  this.symbolResolver = null;
45
44
  this.loadAudio = false;
46
45
  this.pageLayoutPreference = 'scanning'; // Default to scanning for metrics
47
46
  this.symbolResolver = symbolResolver;
48
- this.loadAudio = options.loadAudio !== undefined ? options.loadAudio : true;
47
+ this.loadAudio = options?.loadAudio !== undefined ? options.loadAudio : true;
49
48
  this.pageLayoutPreference =
50
- options.pageLayoutPreference !== undefined ? options.pageLayoutPreference : 'scanning'; // Default to scanning
49
+ options?.pageLayoutPreference !== undefined ? options.pageLayoutPreference : 'scanning'; // Default to scanning
51
50
  }
52
51
  async extractTexts(filePathOrBuffer) {
53
52
  const tree = await this.loadIntoTree(filePathOrBuffer);
@@ -68,7 +67,7 @@ class SnapProcessor extends baseProcessor_1.BaseProcessor {
68
67
  return texts;
69
68
  }
70
69
  async loadIntoTree(filePathOrBuffer) {
71
- await Promise.resolve();
70
+ const { writeBinaryToPath, removePath, mkTempDir, basename, join } = this.options.fileAdapter;
72
71
  const tree = new treeStructure_1.AACTree();
73
72
  let dbResult = null;
74
73
  let cleanupTempZip = null;
@@ -76,32 +75,32 @@ class SnapProcessor extends baseProcessor_1.BaseProcessor {
76
75
  // Handle .sub.zip files (Snap pageset backups containing .sps files)
77
76
  let inputFile = filePathOrBuffer;
78
77
  if (typeof filePathOrBuffer === 'string') {
79
- const fileName = (0, io_1.getPath)().basename(filePathOrBuffer).toLowerCase();
78
+ const fileName = basename(filePathOrBuffer).toLowerCase();
80
79
  if (fileName.endsWith('.sub.zip') || filePathOrBuffer.endsWith('.sub')) {
81
- const fs = (0, io_1.getFs)();
82
- const path = (0, io_1.getPath)();
83
- const os = (0, io_1.getOs)();
84
80
  // Extract .sub.zip to find the embedded .sps file
85
- const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'snap-sub-'));
86
- const { zip } = await (0, zip_1.openZipFromInput)(filePathOrBuffer);
81
+ const tempDir = await mkTempDir('snap-sub-');
82
+ const zip = await this.options.zipAdapter(filePathOrBuffer);
87
83
  // Find the .sps file in the archive
88
84
  const files = zip.listFiles();
89
85
  const spsFile = files.find((f) => f.endsWith('.sps'));
90
86
  if (!spsFile) {
91
- fs.rmSync(tempDir, { recursive: true, force: true });
87
+ await removePath(tempDir, { recursive: true, force: true });
92
88
  throw new Error('No .sps file found in .sub.zip archive');
93
89
  }
94
90
  // Extract the .sps file
95
91
  const spsData = await zip.readFile(spsFile);
96
- const extractedSpsPath = path.join(tempDir, path.basename(spsFile));
97
- fs.writeFileSync(extractedSpsPath, Buffer.from(spsData));
92
+ const extractedSpsPath = join(tempDir, basename(spsFile));
93
+ await writeBinaryToPath(extractedSpsPath, Buffer.from(spsData));
98
94
  inputFile = extractedSpsPath;
99
- cleanupTempZip = () => {
100
- fs.rmSync(tempDir, { recursive: true, force: true });
95
+ cleanupTempZip = async () => {
96
+ await removePath(tempDir, { recursive: true, force: true });
101
97
  };
102
98
  }
103
99
  }
104
- dbResult = await (0, sqlite_1.openSqliteDatabase)(inputFile, { readonly: true });
100
+ dbResult = await (0, sqlite_1.openSqliteDatabase)(inputFile, {
101
+ readonly: true,
102
+ fileAdapter: this.options.fileAdapter,
103
+ });
105
104
  const db = dbResult.db;
106
105
  const getTableColumns = (tableName) => {
107
106
  try {
@@ -686,7 +685,7 @@ class SnapProcessor extends baseProcessor_1.BaseProcessor {
686
685
  }
687
686
  finally {
688
687
  if (dbResult?.cleanup) {
689
- dbResult.cleanup();
688
+ await dbResult.cleanup();
690
689
  }
691
690
  else if (dbResult?.db) {
692
691
  dbResult.db.close();
@@ -694,7 +693,7 @@ class SnapProcessor extends baseProcessor_1.BaseProcessor {
694
693
  // Clean up temporary extracted .sps file from .sub.zip
695
694
  if (cleanupTempZip) {
696
695
  try {
697
- cleanupTempZip();
696
+ await cleanupTempZip();
698
697
  }
699
698
  catch (e) {
700
699
  console.warn('[SnapProcessor] Failed to clean up temporary .sps file:', e);
@@ -703,21 +702,21 @@ class SnapProcessor extends baseProcessor_1.BaseProcessor {
703
702
  }
704
703
  }
705
704
  async processTexts(filePathOrBuffer, translations, outputPath) {
705
+ const { pathExists, mkDir, writeBinaryToPath, readBinaryFromInput, removePath, dirname } = this.options.fileAdapter;
706
706
  if (!(0, io_1.isNodeRuntime)()) {
707
707
  throw new Error('processTexts is only supported in Node.js environments for Snap files.');
708
708
  }
709
- const fs = (0, io_1.getFs)();
710
- const path = (0, io_1.getPath)();
711
709
  if (typeof filePathOrBuffer === 'string') {
712
710
  const inputPath = filePathOrBuffer;
713
- const outputDir = path.dirname(outputPath);
714
- if (!fs.existsSync(outputDir)) {
715
- fs.mkdirSync(outputDir, { recursive: true });
711
+ const outputDir = dirname(outputPath);
712
+ const dirExists = await pathExists(outputDir);
713
+ if (!dirExists) {
714
+ await mkDir(outputDir, { recursive: true });
716
715
  }
717
- if (fs.existsSync(outputPath)) {
718
- fs.unlinkSync(outputPath);
716
+ if (await pathExists(outputPath)) {
717
+ await removePath(outputPath);
719
718
  }
720
- fs.copyFileSync(inputPath, outputPath);
719
+ await writeBinaryToPath(outputPath, await readBinaryFromInput(inputPath));
721
720
  const Database = (0, sqlite_1.requireBetterSqlite3)();
722
721
  const db = new Database(outputPath, { readonly: false });
723
722
  try {
@@ -779,7 +778,7 @@ class SnapProcessor extends baseProcessor_1.BaseProcessor {
779
778
  finally {
780
779
  db.close();
781
780
  }
782
- return fs.readFileSync(outputPath);
781
+ return await readBinaryFromInput(outputPath);
783
782
  }
784
783
  // Fallback for buffer inputs: rebuild from tree (may drop Snap assets)
785
784
  const tree = await this.loadIntoTree(filePathOrBuffer);
@@ -806,21 +805,20 @@ class SnapProcessor extends baseProcessor_1.BaseProcessor {
806
805
  });
807
806
  });
808
807
  await this.saveFromTree(tree, outputPath);
809
- return fs.readFileSync(outputPath);
808
+ return await readBinaryFromInput(outputPath);
810
809
  }
811
810
  async saveFromTree(tree, outputPath) {
811
+ const { pathExists, mkDir, removePath, dirname } = this.options.fileAdapter;
812
812
  if (!(0, io_1.isNodeRuntime)()) {
813
813
  throw new Error('saveFromTree is only supported in Node.js environments for Snap files.');
814
814
  }
815
- await Promise.resolve();
816
- const fs = (0, io_1.getFs)();
817
- const path = (0, io_1.getPath)();
818
- const outputDir = path.dirname(outputPath);
819
- if (!fs.existsSync(outputDir)) {
820
- fs.mkdirSync(outputDir, { recursive: true });
815
+ const outputDir = dirname(outputPath);
816
+ const dirExists = await pathExists(outputDir);
817
+ if (!dirExists) {
818
+ await mkDir(outputDir, { recursive: true });
821
819
  }
822
- if (fs.existsSync(outputPath)) {
823
- fs.unlinkSync(outputPath);
820
+ if (await pathExists(outputPath)) {
821
+ await removePath(outputPath);
824
822
  }
825
823
  // Create a new SQLite database for Snap format
826
824
  const Database = (0, sqlite_1.requireBetterSqlite3)();
@@ -1055,7 +1053,6 @@ class SnapProcessor extends baseProcessor_1.BaseProcessor {
1055
1053
  * Add audio recording to a button in the database
1056
1054
  */
1057
1055
  async addAudioToButton(dbPath, buttonId, audioData, metadata) {
1058
- await Promise.resolve();
1059
1056
  if (!(0, io_1.isNodeRuntime)()) {
1060
1057
  throw new Error('addAudioToButton is only supported in Node.js environments.');
1061
1058
  }
@@ -1093,7 +1090,7 @@ class SnapProcessor extends baseProcessor_1.BaseProcessor {
1093
1090
  const updateButton = db.prepare('UPDATE Button SET MessageRecordingId = ?, UseMessageRecording = 1, SerializedMessageSoundMetadata = ? WHERE Id = ?');
1094
1091
  const metadataJson = metadata ? JSON.stringify({ FileName: metadata }) : null;
1095
1092
  updateButton.run(audioId, metadataJson, buttonId);
1096
- return audioId;
1093
+ return Promise.resolve(audioId);
1097
1094
  }
1098
1095
  finally {
1099
1096
  db.close();
@@ -1103,12 +1100,12 @@ class SnapProcessor extends baseProcessor_1.BaseProcessor {
1103
1100
  * Create a copy of the pageset with audio recordings added
1104
1101
  */
1105
1102
  async createAudioEnhancedPageset(sourceDbPath, targetDbPath, audioMappings) {
1103
+ const { writeBinaryToPath, readBinaryFromInput } = this.options.fileAdapter;
1106
1104
  if (!(0, io_1.isNodeRuntime)()) {
1107
1105
  throw new Error('createAudioEnhancedPageset is only supported in Node.js environments.');
1108
1106
  }
1109
- const fs = (0, io_1.getFs)();
1110
1107
  // Copy the source database to target
1111
- fs.copyFileSync(sourceDbPath, targetDbPath);
1108
+ await writeBinaryToPath(targetDbPath, await readBinaryFromInput(sourceDbPath));
1112
1109
  // Add audio recordings to the copy
1113
1110
  for (const [buttonId, audioInfo] of audioMappings.entries()) {
1114
1111
  await this.addAudioToButton(targetDbPath, buttonId, audioInfo.audioData, audioInfo.metadata);
@@ -1170,23 +1167,22 @@ class SnapProcessor extends baseProcessor_1.BaseProcessor {
1170
1167
  * @returns Promise with validation result
1171
1168
  */
1172
1169
  async validate(filePath) {
1173
- return snapValidator_1.SnapValidator.validateFile(filePath);
1170
+ return snapValidator_1.SnapValidator.validateFile(filePath, this.options.fileAdapter);
1174
1171
  }
1175
1172
  /**
1176
1173
  * Get available PageLayouts for a Snap file
1177
1174
  * Useful for UI components that want to let users select layout size
1178
1175
  * @param filePath - Path to the Snap file
1179
- * @returns Array of available PageLayouts with their dimensions
1176
+ * @returns Promise resolving to available PageLayouts with their dimensions
1180
1177
  */
1181
- getAvailablePageLayouts(filePath) {
1178
+ async getAvailablePageLayouts(filePath) {
1179
+ const { writeBinaryToPath, removePath, pathExists, join } = this.options.fileAdapter;
1182
1180
  if (!(0, io_1.isNodeRuntime)()) {
1183
1181
  throw new Error('getAvailablePageLayouts is only supported in Node.js environments.');
1184
1182
  }
1185
- const fs = (0, io_1.getFs)();
1186
- const path = (0, io_1.getPath)();
1187
- const dbPath = typeof filePath === 'string' ? filePath : path.join(process.cwd(), 'temp.spb');
1183
+ const dbPath = typeof filePath === 'string' ? filePath : join(process.cwd(), 'temp.spb');
1188
1184
  if (Buffer.isBuffer(filePath)) {
1189
- fs.writeFileSync(dbPath, filePath);
1185
+ await writeBinaryToPath(dbPath, filePath);
1190
1186
  }
1191
1187
  let db = null;
1192
1188
  try {
@@ -1237,9 +1233,10 @@ class SnapProcessor extends baseProcessor_1.BaseProcessor {
1237
1233
  db.close();
1238
1234
  }
1239
1235
  // Clean up temporary file if created from buffer
1240
- if (Buffer.isBuffer(filePath) && fs.existsSync(dbPath)) {
1236
+ const exists = await pathExists(dbPath);
1237
+ if (Buffer.isBuffer(filePath) && exists) {
1241
1238
  try {
1242
- fs.unlinkSync(dbPath);
1239
+ await removePath(dbPath);
1243
1240
  }
1244
1241
  catch (e) {
1245
1242
  console.warn('Failed to clean up temporary file:', e);
@@ -39,7 +39,7 @@ declare class TouchChatProcessor extends BaseProcessor {
39
39
  * This method uses shared translation utilities that work across all AAC formats.
40
40
  *
41
41
  * @param filePathOrBuffer - Path to TouchChat .ce file or buffer
42
- * @returns Array of symbol information for LLM processing
42
+ * @returns Promise resolving to symbol information for LLM processing
43
43
  */
44
44
  extractSymbolsForLLM(filePathOrBuffer: string | Buffer): Promise<ButtonForTranslation[]>;
45
45
  /**
@@ -52,7 +52,7 @@ declare class TouchChatProcessor extends BaseProcessor {
52
52
  * @param llmTranslations - Array of LLM translations with symbol info
53
53
  * @param outputPath - Where to save the translated TouchChat file
54
54
  * @param options - Translation options (e.g., allowPartial for testing)
55
- * @returns Buffer of the translated TouchChat file
55
+ * @returns Promise resolving to a buffer of the translated TouchChat file
56
56
  */
57
57
  processLLMTranslations(filePathOrBuffer: string | Uint8Array, llmTranslations: LLMLTranslationResult[], outputPath: string, options?: {
58
58
  allowPartial?: boolean;