@willwade/aac-processors 0.1.20 → 0.1.21

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/dist/browser/core/baseProcessor.js +4 -0
  2. package/dist/browser/processors/applePanelsProcessor.js +24 -31
  3. package/dist/browser/processors/astericsGridProcessor.js +10 -3
  4. package/dist/browser/processors/dotProcessor.js +5 -2
  5. package/dist/browser/processors/gridset/colorUtils.js +354 -0
  6. package/dist/browser/processors/gridset/helpers.js +49 -45
  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 +35 -68
  12. package/dist/browser/processors/gridsetProcessor.js +32 -41
  13. package/dist/browser/processors/obfProcessor.js +20 -33
  14. package/dist/browser/processors/opmlProcessor.js +5 -2
  15. package/dist/browser/processors/snap/helpers.js +49 -45
  16. package/dist/browser/processors/snapProcessor.js +39 -42
  17. package/dist/browser/processors/touchchatProcessor.js +54 -45
  18. package/dist/browser/utilities/analytics/reference/index.js +27 -19
  19. package/dist/browser/utils/io.js +67 -14
  20. package/dist/browser/utils/sqlite.js +6 -8
  21. package/dist/browser/utils/zip.js +45 -43
  22. package/dist/browser/validation/baseValidator.js +5 -0
  23. package/dist/browser/validation/gridsetValidator.js +12 -20
  24. package/dist/browser/validation/obfValidator.js +5 -4
  25. package/dist/browser/validation/snapValidator.js +9 -5
  26. package/dist/browser/validation/touchChatValidator.js +21 -11
  27. package/dist/cli/index.js +10 -15
  28. package/dist/core/baseProcessor.d.ts +7 -7
  29. package/dist/core/baseProcessor.js +4 -0
  30. package/dist/processors/applePanelsProcessor.js +29 -36
  31. package/dist/processors/astericsGridProcessor.js +20 -13
  32. package/dist/processors/dotProcessor.js +10 -7
  33. package/dist/processors/excelProcessor.js +9 -12
  34. package/dist/processors/gridset/helpers.d.ts +9 -11
  35. package/dist/processors/gridset/helpers.js +49 -71
  36. package/dist/processors/gridset/imageDebug.d.ts +3 -5
  37. package/dist/processors/gridset/imageDebug.js +4 -4
  38. package/dist/processors/gridset/password.d.ts +1 -1
  39. package/dist/processors/gridset/symbolExtractor.d.ts +5 -3
  40. package/dist/processors/gridset/symbolExtractor.js +15 -38
  41. package/dist/processors/gridset/symbolSearch.d.ts +3 -2
  42. package/dist/processors/gridset/symbolSearch.js +12 -34
  43. package/dist/processors/gridset/symbols.d.ts +8 -6
  44. package/dist/processors/gridset/symbols.js +34 -67
  45. package/dist/processors/gridset/wordlistHelpers.d.ts +4 -6
  46. package/dist/processors/gridset/wordlistHelpers.js +15 -74
  47. package/dist/processors/gridsetProcessor.js +36 -68
  48. package/dist/processors/obfProcessor.js +26 -62
  49. package/dist/processors/obfsetProcessor.js +2 -2
  50. package/dist/processors/opmlProcessor.js +10 -7
  51. package/dist/processors/snap/helpers.d.ts +8 -8
  52. package/dist/processors/snap/helpers.js +50 -72
  53. package/dist/processors/snapProcessor.js +38 -41
  54. package/dist/processors/touchchatProcessor.js +54 -45
  55. package/dist/utilities/analytics/index.d.ts +3 -2
  56. package/dist/utilities/analytics/index.js +8 -10
  57. package/dist/utilities/analytics/reference/index.d.ts +5 -3
  58. package/dist/utilities/analytics/reference/index.js +26 -18
  59. package/dist/utilities/symbolTools.d.ts +4 -2
  60. package/dist/utilities/symbolTools.js +16 -15
  61. package/dist/utils/io.d.ts +24 -6
  62. package/dist/utils/io.js +64 -14
  63. package/dist/utils/sqlite.d.ts +2 -0
  64. package/dist/utils/sqlite.js +6 -8
  65. package/dist/utils/zip.d.ts +7 -3
  66. package/dist/utils/zip.js +45 -43
  67. package/dist/validation/applePanelsValidator.d.ts +2 -1
  68. package/dist/validation/applePanelsValidator.js +9 -11
  69. package/dist/validation/astericsValidator.d.ts +2 -1
  70. package/dist/validation/astericsValidator.js +5 -4
  71. package/dist/validation/baseValidator.d.ts +2 -2
  72. package/dist/validation/baseValidator.js +5 -0
  73. package/dist/validation/dotValidator.d.ts +2 -1
  74. package/dist/validation/dotValidator.js +5 -4
  75. package/dist/validation/excelValidator.d.ts +2 -1
  76. package/dist/validation/excelValidator.js +5 -4
  77. package/dist/validation/gridsetValidator.d.ts +2 -1
  78. package/dist/validation/gridsetValidator.js +11 -22
  79. package/dist/validation/index.d.ts +2 -2
  80. package/dist/validation/index.js +5 -4
  81. package/dist/validation/obfValidator.d.ts +2 -1
  82. package/dist/validation/obfValidator.js +5 -4
  83. package/dist/validation/obfsetValidator.d.ts +2 -1
  84. package/dist/validation/obfsetValidator.js +5 -4
  85. package/dist/validation/opmlValidator.d.ts +2 -1
  86. package/dist/validation/opmlValidator.js +5 -4
  87. package/dist/validation/snapValidator.d.ts +2 -1
  88. package/dist/validation/snapValidator.js +9 -5
  89. package/dist/validation/touchChatValidator.d.ts +4 -6
  90. package/dist/validation/touchChatValidator.js +21 -11
  91. package/dist/validation/validationTypes.d.ts +8 -1
  92. package/package.json +1 -1
  93. package/dist/core/fileProcessor.d.ts +0 -7
  94. package/dist/core/fileProcessor.js +0 -57
@@ -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,6 +67,7 @@ class SnapProcessor extends baseProcessor_1.BaseProcessor {
68
67
  return texts;
69
68
  }
70
69
  async loadIntoTree(filePathOrBuffer) {
70
+ const { writeBinaryToPath, removePath, mkTempDir, basename, join } = this.options.fileAdapter;
71
71
  await Promise.resolve();
72
72
  const tree = new treeStructure_1.AACTree();
73
73
  let dbResult = null;
@@ -76,32 +76,32 @@ class SnapProcessor extends baseProcessor_1.BaseProcessor {
76
76
  // Handle .sub.zip files (Snap pageset backups containing .sps files)
77
77
  let inputFile = filePathOrBuffer;
78
78
  if (typeof filePathOrBuffer === 'string') {
79
- const fileName = (0, io_1.getPath)().basename(filePathOrBuffer).toLowerCase();
79
+ const fileName = basename(filePathOrBuffer).toLowerCase();
80
80
  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
81
  // 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);
82
+ const tempDir = mkTempDir('snap-sub-');
83
+ const zip = await this.options.zipAdapter(filePathOrBuffer);
87
84
  // Find the .sps file in the archive
88
85
  const files = zip.listFiles();
89
86
  const spsFile = files.find((f) => f.endsWith('.sps'));
90
87
  if (!spsFile) {
91
- fs.rmSync(tempDir, { recursive: true, force: true });
88
+ removePath(tempDir, { recursive: true, force: true });
92
89
  throw new Error('No .sps file found in .sub.zip archive');
93
90
  }
94
91
  // Extract the .sps file
95
92
  const spsData = await zip.readFile(spsFile);
96
- const extractedSpsPath = path.join(tempDir, path.basename(spsFile));
97
- fs.writeFileSync(extractedSpsPath, Buffer.from(spsData));
93
+ const extractedSpsPath = join(tempDir, basename(spsFile));
94
+ writeBinaryToPath(extractedSpsPath, Buffer.from(spsData));
98
95
  inputFile = extractedSpsPath;
99
96
  cleanupTempZip = () => {
100
- fs.rmSync(tempDir, { recursive: true, force: true });
97
+ removePath(tempDir, { recursive: true, force: true });
101
98
  };
102
99
  }
103
100
  }
104
- dbResult = await (0, sqlite_1.openSqliteDatabase)(inputFile, { readonly: true });
101
+ dbResult = await (0, sqlite_1.openSqliteDatabase)(inputFile, {
102
+ readonly: true,
103
+ fileAdapter: this.options.fileAdapter,
104
+ });
105
105
  const db = dbResult.db;
106
106
  const getTableColumns = (tableName) => {
107
107
  try {
@@ -703,21 +703,20 @@ class SnapProcessor extends baseProcessor_1.BaseProcessor {
703
703
  }
704
704
  }
705
705
  async processTexts(filePathOrBuffer, translations, outputPath) {
706
+ const { pathExists, mkDir, writeBinaryToPath, readBinaryFromInput, removePath, dirname } = this.options.fileAdapter;
706
707
  if (!(0, io_1.isNodeRuntime)()) {
707
708
  throw new Error('processTexts is only supported in Node.js environments for Snap files.');
708
709
  }
709
- const fs = (0, io_1.getFs)();
710
- const path = (0, io_1.getPath)();
711
710
  if (typeof filePathOrBuffer === 'string') {
712
711
  const inputPath = filePathOrBuffer;
713
- const outputDir = path.dirname(outputPath);
714
- if (!fs.existsSync(outputDir)) {
715
- fs.mkdirSync(outputDir, { recursive: true });
712
+ const outputDir = dirname(outputPath);
713
+ if (!pathExists(outputDir)) {
714
+ mkDir(outputDir, { recursive: true });
716
715
  }
717
- if (fs.existsSync(outputPath)) {
718
- fs.unlinkSync(outputPath);
716
+ if (pathExists(outputPath)) {
717
+ removePath(outputPath);
719
718
  }
720
- fs.copyFileSync(inputPath, outputPath);
719
+ writeBinaryToPath(outputPath, 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 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 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
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 });
816
+ const outputDir = dirname(outputPath);
817
+ if (!pathExists(outputDir)) {
818
+ mkDir(outputDir, { recursive: true });
821
819
  }
822
- if (fs.existsSync(outputPath)) {
823
- fs.unlinkSync(outputPath);
820
+ if (pathExists(outputPath)) {
821
+ removePath(outputPath);
824
822
  }
825
823
  // Create a new SQLite database for Snap format
826
824
  const Database = (0, sqlite_1.requireBetterSqlite3)();
@@ -1103,12 +1101,12 @@ class SnapProcessor extends baseProcessor_1.BaseProcessor {
1103
1101
  * Create a copy of the pageset with audio recordings added
1104
1102
  */
1105
1103
  async createAudioEnhancedPageset(sourceDbPath, targetDbPath, audioMappings) {
1104
+ const { writeBinaryToPath, readBinaryFromInput } = this.options.fileAdapter;
1106
1105
  if (!(0, io_1.isNodeRuntime)()) {
1107
1106
  throw new Error('createAudioEnhancedPageset is only supported in Node.js environments.');
1108
1107
  }
1109
- const fs = (0, io_1.getFs)();
1110
1108
  // Copy the source database to target
1111
- fs.copyFileSync(sourceDbPath, targetDbPath);
1109
+ writeBinaryToPath(targetDbPath, readBinaryFromInput(sourceDbPath));
1112
1110
  // Add audio recordings to the copy
1113
1111
  for (const [buttonId, audioInfo] of audioMappings.entries()) {
1114
1112
  await this.addAudioToButton(targetDbPath, buttonId, audioInfo.audioData, audioInfo.metadata);
@@ -1170,7 +1168,7 @@ class SnapProcessor extends baseProcessor_1.BaseProcessor {
1170
1168
  * @returns Promise with validation result
1171
1169
  */
1172
1170
  async validate(filePath) {
1173
- return snapValidator_1.SnapValidator.validateFile(filePath);
1171
+ return snapValidator_1.SnapValidator.validateFile(filePath, this.options.fileAdapter);
1174
1172
  }
1175
1173
  /**
1176
1174
  * Get available PageLayouts for a Snap file
@@ -1179,14 +1177,13 @@ class SnapProcessor extends baseProcessor_1.BaseProcessor {
1179
1177
  * @returns Array of available PageLayouts with their dimensions
1180
1178
  */
1181
1179
  getAvailablePageLayouts(filePath) {
1180
+ const { writeBinaryToPath, removePath, pathExists, join } = this.options.fileAdapter;
1182
1181
  if (!(0, io_1.isNodeRuntime)()) {
1183
1182
  throw new Error('getAvailablePageLayouts is only supported in Node.js environments.');
1184
1183
  }
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');
1184
+ const dbPath = typeof filePath === 'string' ? filePath : join(process.cwd(), 'temp.spb');
1188
1185
  if (Buffer.isBuffer(filePath)) {
1189
- fs.writeFileSync(dbPath, filePath);
1186
+ writeBinaryToPath(dbPath, filePath);
1190
1187
  }
1191
1188
  let db = null;
1192
1189
  try {
@@ -1237,9 +1234,9 @@ class SnapProcessor extends baseProcessor_1.BaseProcessor {
1237
1234
  db.close();
1238
1235
  }
1239
1236
  // Clean up temporary file if created from buffer
1240
- if (Buffer.isBuffer(filePath) && fs.existsSync(dbPath)) {
1237
+ if (Buffer.isBuffer(filePath) && pathExists(dbPath)) {
1241
1238
  try {
1242
- fs.unlinkSync(dbPath);
1239
+ removePath(dbPath);
1243
1240
  }
1244
1241
  catch (e) {
1245
1242
  console.warn('Failed to clean up temporary file:', e);
@@ -9,7 +9,7 @@ const touchChatValidator_1 = require("../validation/touchChatValidator");
9
9
  const io_1 = require("../utils/io");
10
10
  const translationProcessor_1 = require("../utilities/translation/translationProcessor");
11
11
  const sqlite_1 = require("../utils/sqlite");
12
- const zip_1 = require("../utils/zip");
12
+ const gridset_1 = require("./gridset");
13
13
  const toNumberOrUndefined = (value) => typeof value === 'number' ? value : undefined;
14
14
  const toStringOrUndefined = (value) => typeof value === 'string' && value.length > 0 ? value : undefined;
15
15
  const toBooleanOrUndefined = (value) => typeof value === 'number' ? value !== 0 : undefined;
@@ -58,6 +58,7 @@ class TouchChatProcessor extends baseProcessor_1.BaseProcessor {
58
58
  return texts;
59
59
  }
60
60
  async loadIntoTree(filePathOrBuffer) {
61
+ const { readBinaryFromInput } = this.options.fileAdapter;
61
62
  await Promise.resolve();
62
63
  // Unzip .ce file, extract the .c4v SQLite DB, and parse pages/buttons
63
64
  let db = null;
@@ -66,16 +67,17 @@ class TouchChatProcessor extends baseProcessor_1.BaseProcessor {
66
67
  // Store source file path or buffer
67
68
  this.sourceFile = filePathOrBuffer;
68
69
  // Step 1: Unzip
69
- const zipInput = (0, io_1.readBinaryFromInput)(filePathOrBuffer);
70
- const { zip } = this.options.zipAdapter
71
- ? await this.options.zipAdapter(zipInput)
72
- : await (0, zip_1.openZipFromInput)(zipInput);
70
+ const zipInput = readBinaryFromInput(filePathOrBuffer);
71
+ const zip = await this.options.zipAdapter(zipInput);
73
72
  const vocabEntry = zip.listFiles().find((name) => name.endsWith('.c4v'));
74
73
  if (!vocabEntry) {
75
74
  throw new Error('No .c4v vocab DB found in TouchChat export');
76
75
  }
77
76
  const dbBuffer = await zip.readFile(vocabEntry);
78
- const dbResult = await (0, sqlite_1.openSqliteDatabase)(dbBuffer, { readonly: true });
77
+ const dbResult = await (0, sqlite_1.openSqliteDatabase)(dbBuffer, {
78
+ readonly: true,
79
+ fileAdapter: this.options.fileAdapter,
80
+ });
79
81
  db = dbResult.db;
80
82
  cleanup = dbResult.cleanup;
81
83
  // Step 3: Create tree and load pages
@@ -478,6 +480,7 @@ class TouchChatProcessor extends baseProcessor_1.BaseProcessor {
478
480
  }
479
481
  }
480
482
  async processTexts(filePathOrBuffer, translations, outputPath) {
483
+ const { pathExists, mkDir, removePath, mkTempDir, writeBinaryToPath, readBinaryFromInput, dirname, join, } = this.options.fileAdapter;
481
484
  if (!(0, io_1.isNodeRuntime)()) {
482
485
  throw new Error('processTexts is only supported in Node.js environments for TouchChat files.');
483
486
  }
@@ -490,28 +493,24 @@ class TouchChatProcessor extends baseProcessor_1.BaseProcessor {
490
493
  * within the embedded SQLite database, ensuring assets and metadata remain intact.
491
494
  */
492
495
  if (typeof filePathOrBuffer === 'string') {
493
- const fs = (0, io_1.getFs)();
494
- const path = (0, io_1.getPath)();
495
- const os = (0, io_1.getOs)();
496
- const AdmZip = (0, io_1.getNodeRequire)()('adm-zip');
497
496
  const inputPath = filePathOrBuffer;
498
- const outputDir = path.dirname(outputPath);
499
- if (!fs.existsSync(outputDir)) {
500
- fs.mkdirSync(outputDir, { recursive: true });
497
+ const outputDir = dirname(outputPath);
498
+ if (!pathExists(outputDir)) {
499
+ mkDir(outputDir, { recursive: true });
501
500
  }
502
- if (fs.existsSync(outputPath)) {
503
- fs.unlinkSync(outputPath);
501
+ if (pathExists(outputPath)) {
502
+ removePath(outputPath);
504
503
  }
505
- const zip = new AdmZip(inputPath);
506
- const entries = zip.getEntries();
504
+ const zip = await this.options.zipAdapter(inputPath);
505
+ const entries = (0, gridset_1.getZipEntriesFromAdapter)(zip);
507
506
  const vocabEntry = entries.find((entry) => entry.entryName.endsWith('.c4v'));
508
507
  if (!vocabEntry) {
509
508
  throw new Error('No .c4v vocab DB found in TouchChat export');
510
509
  }
511
- const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'touchchat-translate-'));
512
- const dbPath = path.join(tempDir, 'vocab.c4v');
510
+ const tempDir = mkTempDir('touchchat-translate-');
511
+ const dbPath = join(tempDir, 'vocab.c4v');
513
512
  try {
514
- fs.writeFileSync(dbPath, vocabEntry.getData());
513
+ writeBinaryToPath(dbPath, await vocabEntry.getData());
515
514
  const Database = (0, sqlite_1.requireBetterSqlite3)();
516
515
  const db = new Database(dbPath, { readonly: false });
517
516
  try {
@@ -564,26 +563,34 @@ class TouchChatProcessor extends baseProcessor_1.BaseProcessor {
564
563
  finally {
565
564
  db.close();
566
565
  }
567
- const outputZip = new AdmZip();
568
- entries.forEach((entry) => {
566
+ const outputZip = await this.options.zipAdapter();
567
+ const files = [];
568
+ for (const entry of entries) {
569
569
  if (entry.entryName === vocabEntry.entryName) {
570
- return;
570
+ continue;
571
571
  }
572
- const data = entry.isDirectory ? Buffer.alloc(0) : entry.getData();
573
- outputZip.addFile(entry.entryName, data, entry.comment || '');
572
+ const data = await entry.getData();
573
+ files.push({
574
+ name: entry.entryName,
575
+ data,
576
+ });
577
+ }
578
+ files.push({
579
+ name: vocabEntry.entryName,
580
+ data: readBinaryFromInput(dbPath),
574
581
  });
575
- outputZip.addFile(vocabEntry.entryName, fs.readFileSync(dbPath));
576
- outputZip.writeZip(outputPath);
582
+ const zipData = await outputZip.writeFiles(files);
583
+ writeBinaryToPath(outputPath, zipData);
577
584
  }
578
585
  finally {
579
586
  try {
580
- fs.rmSync(tempDir, { recursive: true, force: true });
587
+ removePath(tempDir, { recursive: true, force: true });
581
588
  }
582
589
  catch {
583
590
  // Best-effort cleanup
584
591
  }
585
592
  }
586
- return fs.readFileSync(outputPath);
593
+ return readBinaryFromInput(outputPath);
587
594
  }
588
595
  // Fallback for buffer inputs: rebuild from tree (may drop TouchChat metadata)
589
596
  const tree = await this.loadIntoTree(filePathOrBuffer);
@@ -610,20 +617,17 @@ class TouchChatProcessor extends baseProcessor_1.BaseProcessor {
610
617
  });
611
618
  });
612
619
  await this.saveFromTree(tree, outputPath);
613
- const fs = (0, io_1.getFs)();
614
- return fs.readFileSync(outputPath);
620
+ return readBinaryFromInput(outputPath);
615
621
  }
616
622
  async saveFromTree(tree, outputPath) {
623
+ const { writeBinaryToPath, mkTempDir, readBinaryFromInput, pathExists, removePath, join } = this.options.fileAdapter;
617
624
  await Promise.resolve();
618
625
  if (!(0, io_1.isNodeRuntime)()) {
619
626
  throw new Error('saveFromTree is only supported in Node.js environments for TouchChat files.');
620
627
  }
621
- const fs = (0, io_1.getFs)();
622
- const path = (0, io_1.getPath)();
623
- const os = (0, io_1.getOs)();
624
628
  // Create a TouchChat database that matches the expected schema for loading
625
- const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'touchchat-export-'));
626
- const dbPath = path.join(tmpDir, 'vocab.c4v');
629
+ const tmpDir = mkTempDir('touchchat-export-');
630
+ const dbPath = join(tmpDir, 'vocab.c4v');
627
631
  try {
628
632
  const Database = (0, sqlite_1.requireBetterSqlite3)();
629
633
  const db = new Database(dbPath);
@@ -916,15 +920,20 @@ class TouchChatProcessor extends baseProcessor_1.BaseProcessor {
916
920
  }
917
921
  db.close();
918
922
  // Create zip file with the database
919
- const AdmZip = (0, io_1.getNodeRequire)()('adm-zip');
920
- const zip = new AdmZip();
921
- zip.addLocalFile(dbPath, '', 'vocab.c4v');
922
- zip.writeZip(outputPath);
923
+ const zip = await this.options.zipAdapter();
924
+ const data = readBinaryFromInput(dbPath);
925
+ const zipData = await zip.writeFiles([
926
+ {
927
+ name: 'vocab.c4v',
928
+ data,
929
+ },
930
+ ]);
931
+ writeBinaryToPath(outputPath, zipData);
923
932
  }
924
933
  finally {
925
934
  // Clean up
926
- if (fs.existsSync(tmpDir)) {
927
- fs.rmSync(tmpDir, { recursive: true, force: true });
935
+ if (pathExists(tmpDir)) {
936
+ removePath(tmpDir, { recursive: true, force: true });
928
937
  }
929
938
  }
930
939
  }
@@ -1019,7 +1028,7 @@ class TouchChatProcessor extends baseProcessor_1.BaseProcessor {
1019
1028
  * @returns Promise with validation result
1020
1029
  */
1021
1030
  async validate(filePath) {
1022
- return touchChatValidator_1.TouchChatValidator.validateFile(filePath);
1031
+ return touchChatValidator_1.TouchChatValidator.validateFile(filePath, this.options.fileAdapter);
1023
1032
  }
1024
1033
  /**
1025
1034
  * Extract symbol information from a TouchChat file for LLM-based translation.
@@ -1061,6 +1070,7 @@ class TouchChatProcessor extends baseProcessor_1.BaseProcessor {
1061
1070
  * @returns Buffer of the translated TouchChat file
1062
1071
  */
1063
1072
  async processLLMTranslations(filePathOrBuffer, llmTranslations, outputPath, options) {
1073
+ const { readBinaryFromInput } = this.options.fileAdapter;
1064
1074
  if (!(0, io_1.isNodeRuntime)()) {
1065
1075
  throw new Error('processLLMTranslations is only supported in Node.js environments for TouchChat files.');
1066
1076
  }
@@ -1102,8 +1112,7 @@ class TouchChatProcessor extends baseProcessor_1.BaseProcessor {
1102
1112
  });
1103
1113
  // Save and return
1104
1114
  await this.saveFromTree(tree, outputPath);
1105
- const fs = (0, io_1.getFs)();
1106
- return fs.readFileSync(outputPath);
1115
+ return readBinaryFromInput(outputPath);
1107
1116
  }
1108
1117
  }
1109
1118
  exports.TouchChatProcessor = TouchChatProcessor;
@@ -9,6 +9,7 @@
9
9
  *
10
10
  * @module
11
11
  */
12
+ import { FileAdapter } from '../../utils/io';
12
13
  export * from './metrics/types';
13
14
  export * from './metrics/effort';
14
15
  export * from './utils/idGenerator';
@@ -23,8 +24,8 @@ export { ReferenceLoader } from './reference';
23
24
  /**
24
25
  * Get the default reference data path
25
26
  */
26
- export declare function getReferenceDataPath(): string;
27
+ export declare function getReferenceDataPath(fileAdapter: FileAdapter): string;
27
28
  /**
28
29
  * Check if reference data files exist
29
30
  */
30
- export declare function hasReferenceData(): boolean;
31
+ export declare function hasReferenceData(fileAdapter?: FileAdapter): boolean;
@@ -24,15 +24,11 @@ var __createBinding = (this && this.__createBinding) || (Object.create ? (functi
24
24
  var __exportStar = (this && this.__exportStar) || function(m, exports) {
25
25
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
26
26
  };
27
- var __importDefault = (this && this.__importDefault) || function (mod) {
28
- return (mod && mod.__esModule) ? mod : { "default": mod };
29
- };
30
27
  Object.defineProperty(exports, "__esModule", { value: true });
31
28
  exports.ReferenceLoader = exports.ComparisonAnalyzer = exports.SentenceAnalyzer = exports.VocabularyAnalyzer = exports.MetricsCalculator = exports.OblAnonymizer = exports.OblUtil = void 0;
32
29
  exports.getReferenceDataPath = getReferenceDataPath;
33
30
  exports.hasReferenceData = hasReferenceData;
34
- const path_1 = __importDefault(require("path"));
35
- const fs_1 = __importDefault(require("fs"));
31
+ const io_1 = require("../../utils/io");
36
32
  // Always-available exports
37
33
  __exportStar(require("./metrics/types"), exports);
38
34
  __exportStar(require("./metrics/effort"), exports);
@@ -59,14 +55,16 @@ Object.defineProperty(exports, "ReferenceLoader", { enumerable: true, get: funct
59
55
  /**
60
56
  * Get the default reference data path
61
57
  */
62
- function getReferenceDataPath() {
63
- return path_1.default.join(__dirname, 'reference', 'data');
58
+ function getReferenceDataPath(fileAdapter) {
59
+ const { join } = fileAdapter;
60
+ return join(__dirname, 'reference', 'data');
64
61
  }
65
62
  /**
66
63
  * Check if reference data files exist
67
64
  */
68
- function hasReferenceData() {
69
- const dataPath = getReferenceDataPath();
65
+ function hasReferenceData(fileAdapter = io_1.defaultFileAdapter) {
66
+ const { pathExists, join } = fileAdapter;
67
+ const dataPath = getReferenceDataPath(fileAdapter);
70
68
  const requiredFiles = [
71
69
  'core_lists.en.json',
72
70
  'common_words.en.json',
@@ -74,5 +72,5 @@ function hasReferenceData() {
74
72
  'synonyms.en.json',
75
73
  'fringe.en.json',
76
74
  ];
77
- return requiredFiles.every((file) => fs_1.default.existsSync(path_1.default.join(dataPath, file)));
75
+ return requiredFiles.every((file) => pathExists(join(dataPath, file)));
78
76
  }
@@ -5,6 +5,7 @@
5
5
  * for AAC metrics analysis.
6
6
  */
7
7
  import { CoreList, CommonWordsData, SynonymsData } from '../metrics/types';
8
+ import { FileAdapter } from '../../../utils/io';
8
9
  export interface ReferenceDataProvider {
9
10
  loadCoreLists(): CoreList[];
10
11
  loadCommonWords(): CommonWordsData;
@@ -29,7 +30,8 @@ export interface ReferenceDataProvider {
29
30
  export declare class ReferenceLoader {
30
31
  private dataDir;
31
32
  private locale;
32
- constructor(dataDir?: string, locale?: string);
33
+ private fileAdapter;
34
+ constructor(dataDir?: string, locale?: string, fileAdapter?: FileAdapter);
33
35
  /**
34
36
  * Load core vocabulary lists
35
37
  */
@@ -79,8 +81,8 @@ export declare class ReferenceLoader {
79
81
  /**
80
82
  * Get the default reference data path
81
83
  */
82
- export declare function getReferenceDataPath(): string;
84
+ export declare function getReferenceDataPath(fileAdapter?: FileAdapter): string;
83
85
  /**
84
86
  * Check if reference data files exist
85
87
  */
86
- export declare function hasReferenceData(): boolean;
88
+ export declare function hasReferenceData(fileAdapter?: FileAdapter): boolean;
@@ -11,55 +11,61 @@ exports.getReferenceDataPath = getReferenceDataPath;
11
11
  exports.hasReferenceData = hasReferenceData;
12
12
  const io_1 = require("../../../utils/io");
13
13
  class ReferenceLoader {
14
- constructor(dataDir, locale = 'en') {
14
+ constructor(dataDir, locale = 'en', fileAdapter = io_1.defaultFileAdapter) {
15
15
  this.locale = locale;
16
+ this.fileAdapter = fileAdapter;
16
17
  if (dataDir) {
17
18
  this.dataDir = dataDir;
18
19
  }
19
20
  else {
20
21
  // Resolve the data directory relative to this file's location
21
22
  // Use __dirname which works correctly after compilation
22
- this.dataDir = (0, io_1.getPath)().join(__dirname, 'data');
23
+ this.dataDir = this.fileAdapter.join(__dirname, 'data');
23
24
  }
24
25
  }
25
26
  /**
26
27
  * Load core vocabulary lists
27
28
  */
28
29
  loadCoreLists() {
29
- const filePath = (0, io_1.getPath)().join(this.dataDir, `core_lists.${this.locale}.json`);
30
- const content = (0, io_1.getFs)().readFileSync(filePath, 'utf-8');
30
+ const { readTextFromInput } = this.fileAdapter;
31
+ const filePath = this.fileAdapter.join(this.dataDir, `core_lists.${this.locale}.json`);
32
+ const content = readTextFromInput(filePath);
31
33
  return JSON.parse(String(content));
32
34
  }
33
35
  /**
34
36
  * Load common words with baseline effort scores
35
37
  */
36
38
  loadCommonWords() {
37
- const filePath = (0, io_1.getPath)().join(this.dataDir, `common_words.${this.locale}.json`);
38
- const content = (0, io_1.getFs)().readFileSync(filePath, 'utf-8');
39
+ const { readTextFromInput, join } = this.fileAdapter;
40
+ const filePath = join(this.dataDir, `common_words.${this.locale}.json`);
41
+ const content = readTextFromInput(filePath);
39
42
  return JSON.parse(String(content));
40
43
  }
41
44
  /**
42
45
  * Load synonym mappings
43
46
  */
44
47
  loadSynonyms() {
45
- const filePath = (0, io_1.getPath)().join(this.dataDir, `synonyms.${this.locale}.json`);
46
- const content = (0, io_1.getFs)().readFileSync(filePath, 'utf-8');
48
+ const { readTextFromInput, join } = this.fileAdapter;
49
+ const filePath = join(this.dataDir, `synonyms.${this.locale}.json`);
50
+ const content = readTextFromInput(filePath);
47
51
  return JSON.parse(String(content));
48
52
  }
49
53
  /**
50
54
  * Load test sentences
51
55
  */
52
56
  loadSentences() {
53
- const filePath = (0, io_1.getPath)().join(this.dataDir, `sentences.${this.locale}.json`);
54
- const content = (0, io_1.getFs)().readFileSync(filePath, 'utf-8');
57
+ const { readTextFromInput, join } = this.fileAdapter;
58
+ const filePath = join(this.dataDir, `sentences.${this.locale}.json`);
59
+ const content = readTextFromInput(filePath);
55
60
  return JSON.parse(String(content));
56
61
  }
57
62
  /**
58
63
  * Load fringe vocabulary
59
64
  */
60
65
  loadFringe() {
61
- const filePath = (0, io_1.getPath)().join(this.dataDir, `fringe.${this.locale}.json`);
62
- const content = (0, io_1.getFs)().readFileSync(filePath, 'utf-8');
66
+ const { readTextFromInput, join } = this.fileAdapter;
67
+ const filePath = join(this.dataDir, `fringe.${this.locale}.json`);
68
+ const content = readTextFromInput(filePath);
63
69
  const data = JSON.parse(String(content));
64
70
  // Flatten nested category words if needed
65
71
  if (Array.isArray(data) && data.length > 0 && data[0].categories) {
@@ -77,8 +83,9 @@ class ReferenceLoader {
77
83
  * Load base words hash map
78
84
  */
79
85
  loadBaseWords() {
80
- const filePath = (0, io_1.getPath)().join(this.dataDir, `base_words.${this.locale}.json`);
81
- const content = (0, io_1.getFs)().readFileSync(filePath, 'utf-8');
86
+ const { readTextFromInput, join } = this.fileAdapter;
87
+ const filePath = join(this.dataDir, `base_words.${this.locale}.json`);
88
+ const content = readTextFromInput(filePath);
82
89
  return JSON.parse(String(content));
83
90
  }
84
91
  /**
@@ -116,13 +123,14 @@ exports.ReferenceLoader = ReferenceLoader;
116
123
  /**
117
124
  * Get the default reference data path
118
125
  */
119
- function getReferenceDataPath() {
120
- return String((0, io_1.getPath)().join(__dirname, 'data'));
126
+ function getReferenceDataPath(fileAdapter = io_1.defaultFileAdapter) {
127
+ return String(fileAdapter.join(__dirname, 'data'));
121
128
  }
122
129
  /**
123
130
  * Check if reference data files exist
124
131
  */
125
- function hasReferenceData() {
132
+ function hasReferenceData(fileAdapter = io_1.defaultFileAdapter) {
133
+ const { pathExists, join } = fileAdapter;
126
134
  const dataPath = getReferenceDataPath();
127
135
  const requiredFiles = [
128
136
  'core_lists.en.json',
@@ -131,5 +139,5 @@ function hasReferenceData() {
131
139
  'synonyms.en.json',
132
140
  'fringe.en.json',
133
141
  ];
134
- return requiredFiles.every((file) => (0, io_1.getFs)().existsSync((0, io_1.getPath)().join(dataPath, file)));
142
+ return requiredFiles.every((file) => pathExists(join(dataPath, file)));
135
143
  }
@@ -1,10 +1,12 @@
1
+ import { FileAdapter } from '../utils/io';
1
2
  export declare abstract class SymbolExtractor {
2
3
  abstract getSymbolReferences(filePath: string): string[];
3
4
  }
4
5
  export declare abstract class SymbolResolver {
5
6
  protected symbolPath: string;
6
7
  protected dbPath: string;
7
- constructor(symbolPath: string, dbPath: string);
8
+ protected fileAdapter: FileAdapter;
9
+ constructor(symbolPath: string, dbPath: string, fileAdapter?: FileAdapter);
8
10
  abstract resolveSymbol(symbolRef: string): string | null;
9
11
  }
10
12
  export declare class SnapSymbolExtractor extends SymbolExtractor {
@@ -25,4 +27,4 @@ export declare class TouchChatSymbolExtractor extends SymbolExtractor {
25
27
  export declare class TouchChatSymbolResolver extends SymbolResolver {
26
28
  resolveSymbol(symbolRef: string): string | null;
27
29
  }
28
- export declare function resolveSymbol(label: string, symbolDir: string): string | null;
30
+ export declare function resolveSymbol(label: string, symbolDir: string, fileAdapter?: FileAdapter): string | null;