@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
@@ -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,7 +58,7 @@ class TouchChatProcessor extends baseProcessor_1.BaseProcessor {
58
58
  return texts;
59
59
  }
60
60
  async loadIntoTree(filePathOrBuffer) {
61
- await Promise.resolve();
61
+ const { readBinaryFromInput } = this.options.fileAdapter;
62
62
  // Unzip .ce file, extract the .c4v SQLite DB, and parse pages/buttons
63
63
  let db = null;
64
64
  let cleanup;
@@ -66,16 +66,17 @@ class TouchChatProcessor extends baseProcessor_1.BaseProcessor {
66
66
  // Store source file path or buffer
67
67
  this.sourceFile = filePathOrBuffer;
68
68
  // 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);
69
+ const zipInput = await readBinaryFromInput(filePathOrBuffer);
70
+ const zip = await this.options.zipAdapter(zipInput);
73
71
  const vocabEntry = zip.listFiles().find((name) => name.endsWith('.c4v'));
74
72
  if (!vocabEntry) {
75
73
  throw new Error('No .c4v vocab DB found in TouchChat export');
76
74
  }
77
75
  const dbBuffer = await zip.readFile(vocabEntry);
78
- const dbResult = await (0, sqlite_1.openSqliteDatabase)(dbBuffer, { readonly: true });
76
+ const dbResult = await (0, sqlite_1.openSqliteDatabase)(dbBuffer, {
77
+ readonly: true,
78
+ fileAdapter: this.options.fileAdapter,
79
+ });
79
80
  db = dbResult.db;
80
81
  cleanup = dbResult.cleanup;
81
82
  // Step 3: Create tree and load pages
@@ -470,7 +471,7 @@ class TouchChatProcessor extends baseProcessor_1.BaseProcessor {
470
471
  finally {
471
472
  // Clean up
472
473
  if (cleanup) {
473
- cleanup();
474
+ await cleanup();
474
475
  }
475
476
  else if (db) {
476
477
  db.close();
@@ -478,6 +479,7 @@ class TouchChatProcessor extends baseProcessor_1.BaseProcessor {
478
479
  }
479
480
  }
480
481
  async processTexts(filePathOrBuffer, translations, outputPath) {
482
+ const { pathExists, mkDir, removePath, mkTempDir, writeBinaryToPath, readBinaryFromInput, dirname, join, } = this.options.fileAdapter;
481
483
  if (!(0, io_1.isNodeRuntime)()) {
482
484
  throw new Error('processTexts is only supported in Node.js environments for TouchChat files.');
483
485
  }
@@ -490,28 +492,25 @@ class TouchChatProcessor extends baseProcessor_1.BaseProcessor {
490
492
  * within the embedded SQLite database, ensuring assets and metadata remain intact.
491
493
  */
492
494
  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
495
  const inputPath = filePathOrBuffer;
498
- const outputDir = path.dirname(outputPath);
499
- if (!fs.existsSync(outputDir)) {
500
- fs.mkdirSync(outputDir, { recursive: true });
496
+ const outputDir = dirname(outputPath);
497
+ const dirExists = await pathExists(outputDir);
498
+ if (!dirExists) {
499
+ await mkDir(outputDir, { recursive: true });
501
500
  }
502
- if (fs.existsSync(outputPath)) {
503
- fs.unlinkSync(outputPath);
501
+ if (await pathExists(outputPath)) {
502
+ await 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 = await mkTempDir('touchchat-translate-');
511
+ const dbPath = join(tempDir, 'vocab.c4v');
513
512
  try {
514
- fs.writeFileSync(dbPath, vocabEntry.getData());
513
+ await 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: await readBinaryFromInput(dbPath),
574
581
  });
575
- outputZip.addFile(vocabEntry.entryName, fs.readFileSync(dbPath));
576
- outputZip.writeZip(outputPath);
582
+ const zipData = await outputZip.writeFiles(files);
583
+ await writeBinaryToPath(outputPath, zipData);
577
584
  }
578
585
  finally {
579
586
  try {
580
- fs.rmSync(tempDir, { recursive: true, force: true });
587
+ await 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 await 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,16 @@ 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 await readBinaryFromInput(outputPath);
615
621
  }
616
622
  async saveFromTree(tree, outputPath) {
617
- await Promise.resolve();
623
+ const { writeBinaryToPath, mkTempDir, readBinaryFromInput, pathExists, removePath, join } = this.options.fileAdapter;
618
624
  if (!(0, io_1.isNodeRuntime)()) {
619
625
  throw new Error('saveFromTree is only supported in Node.js environments for TouchChat files.');
620
626
  }
621
- const fs = (0, io_1.getFs)();
622
- const path = (0, io_1.getPath)();
623
- const os = (0, io_1.getOs)();
624
627
  // 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');
628
+ const tmpDir = await mkTempDir('touchchat-export-');
629
+ const dbPath = join(tmpDir, 'vocab.c4v');
627
630
  try {
628
631
  const Database = (0, sqlite_1.requireBetterSqlite3)();
629
632
  const db = new Database(dbPath);
@@ -916,15 +919,20 @@ class TouchChatProcessor extends baseProcessor_1.BaseProcessor {
916
919
  }
917
920
  db.close();
918
921
  // 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);
922
+ const zip = await this.options.zipAdapter();
923
+ const data = await readBinaryFromInput(dbPath);
924
+ const zipData = await zip.writeFiles([
925
+ {
926
+ name: 'vocab.c4v',
927
+ data,
928
+ },
929
+ ]);
930
+ await writeBinaryToPath(outputPath, zipData);
923
931
  }
924
932
  finally {
925
933
  // Clean up
926
- if (fs.existsSync(tmpDir)) {
927
- fs.rmSync(tmpDir, { recursive: true, force: true });
934
+ if (await pathExists(tmpDir)) {
935
+ await removePath(tmpDir, { recursive: true, force: true });
928
936
  }
929
937
  }
930
938
  }
@@ -1007,7 +1015,7 @@ class TouchChatProcessor extends baseProcessor_1.BaseProcessor {
1007
1015
  const outputPath = filePath.replace(/\.ce$/, '_translated.ce');
1008
1016
  // Use existing processTexts method
1009
1017
  await this.processTexts(filePath, translations, outputPath);
1010
- return Promise.resolve(outputPath);
1018
+ return outputPath;
1011
1019
  }
1012
1020
  catch (error) {
1013
1021
  return Promise.reject(new Error(`Failed to generate translated download: ${error instanceof Error ? error.message : 'Unknown error'}`));
@@ -1019,7 +1027,7 @@ class TouchChatProcessor extends baseProcessor_1.BaseProcessor {
1019
1027
  * @returns Promise with validation result
1020
1028
  */
1021
1029
  async validate(filePath) {
1022
- return touchChatValidator_1.TouchChatValidator.validateFile(filePath);
1030
+ return await touchChatValidator_1.TouchChatValidator.validateFile(filePath, this.options.fileAdapter);
1023
1031
  }
1024
1032
  /**
1025
1033
  * Extract symbol information from a TouchChat file for LLM-based translation.
@@ -1028,7 +1036,7 @@ class TouchChatProcessor extends baseProcessor_1.BaseProcessor {
1028
1036
  * This method uses shared translation utilities that work across all AAC formats.
1029
1037
  *
1030
1038
  * @param filePathOrBuffer - Path to TouchChat .ce file or buffer
1031
- * @returns Array of symbol information for LLM processing
1039
+ * @returns Promise resolving to symbol information for LLM processing
1032
1040
  */
1033
1041
  async extractSymbolsForLLM(filePathOrBuffer) {
1034
1042
  const tree = await this.loadIntoTree(filePathOrBuffer);
@@ -1058,9 +1066,10 @@ class TouchChatProcessor extends baseProcessor_1.BaseProcessor {
1058
1066
  * @param llmTranslations - Array of LLM translations with symbol info
1059
1067
  * @param outputPath - Where to save the translated TouchChat file
1060
1068
  * @param options - Translation options (e.g., allowPartial for testing)
1061
- * @returns Buffer of the translated TouchChat file
1069
+ * @returns Promise resolving to a buffer of the translated TouchChat file
1062
1070
  */
1063
1071
  async processLLMTranslations(filePathOrBuffer, llmTranslations, outputPath, options) {
1072
+ const { readBinaryFromInput } = this.options.fileAdapter;
1064
1073
  if (!(0, io_1.isNodeRuntime)()) {
1065
1074
  throw new Error('processLLMTranslations is only supported in Node.js environments for TouchChat files.');
1066
1075
  }
@@ -1102,8 +1111,7 @@ class TouchChatProcessor extends baseProcessor_1.BaseProcessor {
1102
1111
  });
1103
1112
  // Save and return
1104
1113
  await this.saveFromTree(tree, outputPath);
1105
- const fs = (0, io_1.getFs)();
1106
- return fs.readFileSync(outputPath);
1114
+ return await readBinaryFromInput(outputPath);
1107
1115
  }
1108
1116
  }
1109
1117
  exports.TouchChatProcessor = TouchChatProcessor;
@@ -203,6 +203,6 @@ export interface AACTree {
203
203
  getPage(id: string): AACPage | undefined;
204
204
  }
205
205
  export interface AACProcessor {
206
- extractTexts(filePath: string | Buffer): string[];
207
- loadIntoTree(filePath: string | Buffer): AACTree;
206
+ extractTexts(filePath: string | Buffer): Promise<string[]>;
207
+ loadIntoTree(filePath: string | Buffer): Promise<AACTree>;
208
208
  }
@@ -64,30 +64,30 @@ export declare function exportHistoryToBaton(entries: HistoryEntry[], options?:
64
64
  /**
65
65
  * Read Grid 3 phrase history from a history.sqlite database and tag entries with their source.
66
66
  */
67
- export declare function readGrid3History(historyDbPath: string): HistoryEntry[];
67
+ export declare function readGrid3History(historyDbPath: string): Promise<HistoryEntry[]>;
68
68
  /**
69
69
  * Read Grid 3 history for a specific user/language combination.
70
70
  */
71
- export declare function readGrid3HistoryForUser(userName: string, langCode?: string): HistoryEntry[];
71
+ export declare function readGrid3HistoryForUser(userName: string, langCode?: string): Promise<HistoryEntry[]>;
72
72
  /**
73
73
  * Read every available Grid 3 history database on the machine.
74
74
  */
75
- export declare function readAllGrid3History(): HistoryEntry[];
75
+ export declare function readAllGrid3History(): Promise<HistoryEntry[]>;
76
76
  /**
77
77
  * Read Snap button usage from a pageset database and tag entries with source.
78
78
  */
79
- export declare function readSnapUsage(pagesetPath: string): HistoryEntry[];
79
+ export declare function readSnapUsage(pagesetPath: string): Promise<HistoryEntry[]>;
80
80
  /**
81
81
  * Read Snap usage for a specific user across all discovered pagesets.
82
82
  */
83
- export declare function readSnapUsageForUser(userId?: string, packageNamePattern?: string): HistoryEntry[];
84
- export declare function listSnapUsers(): SnapUserInfo[];
83
+ export declare function readSnapUsageForUser(userId?: string, packageNamePattern?: string): Promise<HistoryEntry[]>;
84
+ export declare function listSnapUsers(): Promise<SnapUserInfo[]>;
85
85
  /**
86
86
  * List Grid 3 users on the current machine.
87
87
  */
88
- export declare function listGrid3Users(): Grid3UserPath[];
88
+ export declare function listGrid3Users(): Promise<Grid3UserPath[]>;
89
89
  /**
90
90
  * Convenience helper to gather all available history across Grid 3 and Snap.
91
91
  * Returns an empty array if no history files are present.
92
92
  */
93
- export declare function collectUnifiedHistory(): HistoryEntry[];
93
+ export declare function collectUnifiedHistory(): Promise<HistoryEntry[]>;
@@ -72,8 +72,9 @@ function exportHistoryToBaton(entries, options) {
72
72
  /**
73
73
  * Read Grid 3 phrase history from a history.sqlite database and tag entries with their source.
74
74
  */
75
- function readGrid3History(historyDbPath) {
76
- return (0, helpers_1.readGrid3History)(historyDbPath).map((e) => ({
75
+ async function readGrid3History(historyDbPath) {
76
+ const history = await (0, helpers_1.readGrid3History)(historyDbPath);
77
+ return history.map((e) => ({
77
78
  ...e,
78
79
  source: 'Grid',
79
80
  }));
@@ -81,8 +82,9 @@ function readGrid3History(historyDbPath) {
81
82
  /**
82
83
  * Read Grid 3 history for a specific user/language combination.
83
84
  */
84
- function readGrid3HistoryForUser(userName, langCode) {
85
- return (0, helpers_1.readGrid3HistoryForUser)(userName, langCode).map((e) => ({
85
+ async function readGrid3HistoryForUser(userName, langCode) {
86
+ const history = await (0, helpers_1.readGrid3HistoryForUser)(userName, langCode);
87
+ return history.map((e) => ({
86
88
  ...e,
87
89
  source: 'Grid',
88
90
  }));
@@ -90,39 +92,43 @@ function readGrid3HistoryForUser(userName, langCode) {
90
92
  /**
91
93
  * Read every available Grid 3 history database on the machine.
92
94
  */
93
- function readAllGrid3History() {
94
- return (0, helpers_1.readAllGrid3History)().map((e) => ({ ...e, source: 'Grid' }));
95
+ async function readAllGrid3History() {
96
+ const history = await (0, helpers_1.readAllGrid3History)();
97
+ return history.map((e) => ({ ...e, source: 'Grid' }));
95
98
  }
96
99
  /**
97
100
  * Read Snap button usage from a pageset database and tag entries with source.
98
101
  */
99
- function readSnapUsage(pagesetPath) {
100
- return (0, helpers_2.readSnapUsage)(pagesetPath).map((e) => ({ ...e, source: 'Snap' }));
102
+ async function readSnapUsage(pagesetPath) {
103
+ const usage = await (0, helpers_2.readSnapUsage)(pagesetPath);
104
+ return usage.map((e) => ({ ...e, source: 'Snap' }));
101
105
  }
102
106
  /**
103
107
  * Read Snap usage for a specific user across all discovered pagesets.
104
108
  */
105
- function readSnapUsageForUser(userId, packageNamePattern = 'TobiiDynavox') {
106
- return (0, helpers_2.readSnapUsageForUser)(userId, packageNamePattern).map((e) => ({
109
+ async function readSnapUsageForUser(userId, packageNamePattern = 'TobiiDynavox') {
110
+ const usage = await (0, helpers_2.readSnapUsageForUser)(userId, packageNamePattern);
111
+ return usage.map((e) => ({
107
112
  ...e,
108
113
  source: 'Snap',
109
114
  }));
110
115
  }
111
- function listSnapUsers() {
112
- return (0, helpers_2.findSnapUsers)();
116
+ async function listSnapUsers() {
117
+ return await (0, helpers_2.findSnapUsers)();
113
118
  }
114
119
  /**
115
120
  * List Grid 3 users on the current machine.
116
121
  */
117
- function listGrid3Users() {
118
- return (0, helpers_1.findGrid3Users)();
122
+ async function listGrid3Users() {
123
+ return await (0, helpers_1.findGrid3Users)();
119
124
  }
120
125
  /**
121
126
  * Convenience helper to gather all available history across Grid 3 and Snap.
122
127
  * Returns an empty array if no history files are present.
123
128
  */
124
- function collectUnifiedHistory() {
125
- const gridHistory = readAllGrid3History();
126
- const snapHistory = (0, helpers_2.findSnapUsers)().flatMap((u) => readSnapUsageForUser(u.userId));
127
- return [...gridHistory, ...snapHistory];
129
+ async function collectUnifiedHistory() {
130
+ const gridHistory = await readAllGrid3History();
131
+ const users = await (0, helpers_2.findSnapUsers)();
132
+ const snapHistory = await Promise.all(users.map(async (u) => await readSnapUsageForUser(u.userId)));
133
+ return [...gridHistory, ...snapHistory.flat()];
128
134
  }
@@ -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): Promise<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
+ async 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,6 @@ 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
+ const existingPaths = await Promise.all(requiredFiles.map(async (file) => await pathExists(join(dataPath, file))));
76
+ return existingPaths.every((exists) => exists);
78
77
  }
@@ -19,7 +19,7 @@ export declare class ComparisonAnalyzer {
19
19
  compare(targetResult: MetricsResult, compareResult: MetricsResult, options?: {
20
20
  includeSentences?: boolean;
21
21
  locale?: string;
22
- } & Partial<MetricsOptions>): ComparisonResult;
22
+ } & Partial<MetricsOptions>): Promise<ComparisonResult>;
23
23
  /**
24
24
  * Calculate CARE component scores
25
25
  */
@@ -26,7 +26,7 @@ class ComparisonAnalyzer {
26
26
  /**
27
27
  * Compare two board sets
28
28
  */
29
- compare(targetResult, compareResult, options) {
29
+ async compare(targetResult, compareResult, options) {
30
30
  // Create base result from target
31
31
  const baseResult = { ...targetResult };
32
32
  // Create word maps with normalized keys
@@ -80,7 +80,7 @@ class ComparisonAnalyzer {
80
80
  };
81
81
  });
82
82
  // Calculate CARE components
83
- const careComponents = this.calculateCareComponents(targetResult, compareResult, overlappingWords, options);
83
+ const careComponents = await this.calculateCareComponents(targetResult, compareResult, overlappingWords, options);
84
84
  // Analyze high/low effort words
85
85
  const highEffortWords = [];
86
86
  const lowEffortWords = [];
@@ -113,7 +113,7 @@ class ComparisonAnalyzer {
113
113
  // Sentence analysis
114
114
  let sentences = [];
115
115
  if (options?.includeSentences) {
116
- const testSentences = this.referenceLoader.loadSentences();
116
+ const testSentences = await this.referenceLoader.loadSentences();
117
117
  const targetSentences = this.sentenceAnalyzer.analyzeSentences(targetResult, testSentences);
118
118
  const compareSentences = this.sentenceAnalyzer.analyzeSentences(compareResult, testSentences);
119
119
  sentences = targetSentences.map((ts, idx) => ({
@@ -126,7 +126,7 @@ class ComparisonAnalyzer {
126
126
  }));
127
127
  }
128
128
  // Core vocabulary analysis
129
- const coreLists = this.referenceLoader.loadCoreLists();
129
+ const coreLists = await this.referenceLoader.loadCoreLists();
130
130
  const cores = {};
131
131
  coreLists.forEach((list) => {
132
132
  let targetTotal = 0;
@@ -174,8 +174,8 @@ class ComparisonAnalyzer {
174
174
  }
175
175
  });
176
176
  // Fringe vocabulary analysis
177
- const fringeWords = this.analyzeFringe(targetWords, compareWords);
178
- const commonFringeWords = this.analyzeCommonFringe(targetWords, compareWords);
177
+ const fringeWords = await this.analyzeFringe(targetWords, compareWords);
178
+ const commonFringeWords = await this.analyzeCommonFringe(targetWords, compareWords);
179
179
  return {
180
180
  ...baseResult,
181
181
  buttons: enrichedButtons,
@@ -217,9 +217,9 @@ class ComparisonAnalyzer {
217
217
  /**
218
218
  * Calculate CARE component scores
219
219
  */
220
- calculateCareComponents(targetResult, compareResult, _overlappingWords, options) {
220
+ async calculateCareComponents(targetResult, compareResult, _overlappingWords, options) {
221
221
  // Load common words with baseline efforts (matching Ruby line 527-534)
222
- const commonWordsData = this.referenceLoader.loadCommonWords();
222
+ const commonWordsData = await this.referenceLoader.loadCommonWords();
223
223
  const commonWords = new Map();
224
224
  commonWordsData.words.forEach((word) => {
225
225
  commonWords.set(word.toLowerCase(), commonWordsData.efforts[word] || 0);
@@ -271,10 +271,10 @@ class ComparisonAnalyzer {
271
271
  }
272
272
  });
273
273
  // Load reference data
274
- const coreLists = this.referenceLoader.loadCoreLists();
275
- const fringe = this.referenceLoader.loadFringe();
276
- const commonFringe = this.referenceLoader.loadCommonFringe();
277
- const sentences = this.referenceLoader.loadSentences();
274
+ const coreLists = await this.referenceLoader.loadCoreLists();
275
+ const fringe = await this.referenceLoader.loadFringe();
276
+ const commonFringe = await this.referenceLoader.loadCommonFringe();
277
+ const sentences = await this.referenceLoader.loadSentences();
278
278
  // Calculate core coverage and effort (matching Ruby lines 609-647)
279
279
  let coreCount = 0;
280
280
  let compCoreCount = 0;
@@ -426,8 +426,8 @@ class ComparisonAnalyzer {
426
426
  /**
427
427
  * Analyze fringe vocabulary
428
428
  */
429
- analyzeFringe(targetWords, compareWords) {
430
- const fringe = this.referenceLoader.loadFringe();
429
+ async analyzeFringe(targetWords, compareWords) {
430
+ const fringe = await this.referenceLoader.loadFringe();
431
431
  const result = [];
432
432
  fringe.forEach((word) => {
433
433
  const key = this.normalize(word);
@@ -447,8 +447,8 @@ class ComparisonAnalyzer {
447
447
  /**
448
448
  * Analyze common fringe vocabulary
449
449
  */
450
- analyzeCommonFringe(targetWords, compareWords) {
451
- const fringe = this.referenceLoader.loadFringe();
450
+ async analyzeCommonFringe(targetWords, compareWords) {
451
+ const fringe = await this.referenceLoader.loadFringe();
452
452
  const result = [];
453
453
  fringe.forEach((word) => {
454
454
  const key = this.normalize(word);
@@ -41,7 +41,7 @@ export declare class VocabularyAnalyzer {
41
41
  locale?: string;
42
42
  highEffortThreshold?: number;
43
43
  lowEffortThreshold?: number;
44
- }): VocabularyAnalysis;
44
+ }): Promise<VocabularyAnalysis>;
45
45
  /**
46
46
  * Analyze coverage for a single core list
47
47
  */
@@ -16,12 +16,12 @@ class VocabularyAnalyzer {
16
16
  /**
17
17
  * Analyze vocabulary coverage against core lists
18
18
  */
19
- analyze(metrics, options) {
19
+ async analyze(metrics, options) {
20
20
  // const locale = options?.locale || metrics.locale || 'en';
21
21
  const highEffortThreshold = options?.highEffortThreshold || 5.0;
22
22
  const lowEffortThreshold = options?.lowEffortThreshold || 2.0;
23
23
  // Load reference data
24
- const coreLists = this.referenceLoader.loadCoreLists();
24
+ const coreLists = await this.referenceLoader.loadCoreLists();
25
25
  // Create word to effort map (using lowercase keys for matching)
26
26
  const wordEffortMap = new Map();
27
27
  metrics.buttons.forEach((btn) => {
@@ -16,16 +16,16 @@ export interface ReferenceData {
16
16
  export declare class InMemoryReferenceLoader implements ReferenceDataProvider {
17
17
  private data;
18
18
  constructor(data: ReferenceData);
19
- loadCoreLists(): CoreList[];
20
- loadCommonWords(): CommonWordsData;
21
- loadSynonyms(): SynonymsData;
22
- loadSentences(): string[][];
23
- loadFringe(): string[];
24
- loadBaseWords(): {
19
+ loadCoreLists(): Promise<CoreList[]>;
20
+ loadCommonWords(): Promise<CommonWordsData>;
21
+ loadSynonyms(): Promise<SynonymsData>;
22
+ loadSentences(): Promise<string[][]>;
23
+ loadFringe(): Promise<string[]>;
24
+ loadBaseWords(): Promise<{
25
25
  [word: string]: boolean;
26
- };
27
- loadCommonFringe(): string[];
28
- loadAll(): ReferenceData;
26
+ }>;
27
+ loadCommonFringe(): Promise<string[]>;
28
+ loadAll(): Promise<ReferenceData>;
29
29
  }
30
30
  export declare function loadReferenceDataFromUrl(baseUrl: string, locale?: string): Promise<ReferenceData>;
31
31
  export declare function createBrowserReferenceLoader(baseUrl: string, locale?: string): Promise<InMemoryReferenceLoader>;
@@ -10,34 +10,34 @@ class InMemoryReferenceLoader {
10
10
  constructor(data) {
11
11
  this.data = data;
12
12
  }
13
- loadCoreLists() {
14
- return this.data.coreLists;
13
+ async loadCoreLists() {
14
+ return Promise.resolve(this.data.coreLists);
15
15
  }
16
- loadCommonWords() {
17
- return this.data.commonWords;
16
+ async loadCommonWords() {
17
+ return Promise.resolve(this.data.commonWords);
18
18
  }
19
- loadSynonyms() {
20
- return this.data.synonyms;
19
+ async loadSynonyms() {
20
+ return Promise.resolve(this.data.synonyms);
21
21
  }
22
- loadSentences() {
23
- return this.data.sentences;
22
+ async loadSentences() {
23
+ return Promise.resolve(this.data.sentences);
24
24
  }
25
- loadFringe() {
26
- return this.data.fringe;
25
+ async loadFringe() {
26
+ return Promise.resolve(this.data.fringe);
27
27
  }
28
- loadBaseWords() {
29
- return this.data.baseWords;
28
+ async loadBaseWords() {
29
+ return Promise.resolve(this.data.baseWords);
30
30
  }
31
- loadCommonFringe() {
31
+ async loadCommonFringe() {
32
32
  const commonWords = new Set(this.data.commonWords.words.map((w) => w.toLowerCase()));
33
33
  const coreWords = new Set();
34
34
  this.data.coreLists.forEach((list) => {
35
35
  list.words.forEach((word) => coreWords.add(word.toLowerCase()));
36
36
  });
37
- return Array.from(commonWords).filter((word) => !coreWords.has(word));
37
+ return Promise.resolve(Array.from(commonWords).filter((word) => !coreWords.has(word)));
38
38
  }
39
- loadAll() {
40
- return this.data;
39
+ async loadAll() {
40
+ return Promise.resolve(this.data);
41
41
  }
42
42
  }
43
43
  exports.InMemoryReferenceLoader = InMemoryReferenceLoader;