@willwade/aac-processors 0.1.19 → 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.
- package/dist/browser/core/baseProcessor.js +4 -0
- package/dist/browser/processors/applePanelsProcessor.js +24 -31
- package/dist/browser/processors/astericsGridProcessor.js +10 -3
- package/dist/browser/processors/dotProcessor.js +5 -2
- package/dist/browser/processors/gridset/colorUtils.js +354 -0
- package/dist/browser/processors/gridset/helpers.js +49 -45
- package/dist/browser/processors/gridset/index.js +61 -0
- package/dist/browser/processors/gridset/styleHelpers.js +205 -0
- package/dist/browser/processors/gridset/symbolExtractor.js +331 -0
- package/dist/browser/processors/gridset/symbolSearch.js +248 -0
- package/dist/browser/processors/gridset/symbols.js +35 -68
- package/dist/browser/processors/gridsetProcessor.js +32 -41
- package/dist/browser/processors/obfProcessor.js +53 -45
- package/dist/browser/processors/opmlProcessor.js +5 -2
- package/dist/browser/processors/snap/helpers.js +49 -45
- package/dist/browser/processors/snapProcessor.js +67 -31
- package/dist/browser/processors/touchchatProcessor.js +54 -45
- package/dist/browser/utilities/analytics/reference/index.js +27 -19
- package/dist/browser/utils/io.js +67 -14
- package/dist/browser/utils/sqlite.js +6 -8
- package/dist/browser/utils/zip.js +45 -43
- package/dist/browser/validation/baseValidator.js +5 -0
- package/dist/browser/validation/gridsetValidator.js +12 -20
- package/dist/browser/validation/obfValidator.js +5 -4
- package/dist/browser/validation/snapValidator.js +9 -5
- package/dist/browser/validation/touchChatValidator.js +21 -11
- package/dist/cli/index.js +10 -15
- package/dist/core/baseProcessor.d.ts +7 -7
- package/dist/core/baseProcessor.js +4 -0
- package/dist/processors/applePanelsProcessor.js +29 -36
- package/dist/processors/astericsGridProcessor.js +20 -13
- package/dist/processors/dotProcessor.js +10 -7
- package/dist/processors/excelProcessor.js +9 -12
- package/dist/processors/gridset/helpers.d.ts +9 -11
- package/dist/processors/gridset/helpers.js +49 -71
- package/dist/processors/gridset/imageDebug.d.ts +3 -5
- package/dist/processors/gridset/imageDebug.js +4 -4
- package/dist/processors/gridset/password.d.ts +1 -1
- package/dist/processors/gridset/symbolExtractor.d.ts +5 -3
- package/dist/processors/gridset/symbolExtractor.js +15 -38
- package/dist/processors/gridset/symbolSearch.d.ts +3 -2
- package/dist/processors/gridset/symbolSearch.js +12 -34
- package/dist/processors/gridset/symbols.d.ts +8 -6
- package/dist/processors/gridset/symbols.js +34 -67
- package/dist/processors/gridset/wordlistHelpers.d.ts +4 -6
- package/dist/processors/gridset/wordlistHelpers.js +15 -74
- package/dist/processors/gridsetProcessor.js +36 -68
- package/dist/processors/obfProcessor.js +58 -73
- package/dist/processors/obfsetProcessor.js +2 -2
- package/dist/processors/opmlProcessor.js +10 -7
- package/dist/processors/snap/helpers.d.ts +8 -8
- package/dist/processors/snap/helpers.js +50 -72
- package/dist/processors/snapProcessor.js +66 -30
- package/dist/processors/touchchatProcessor.js +54 -45
- package/dist/utilities/analytics/index.d.ts +3 -2
- package/dist/utilities/analytics/index.js +8 -10
- package/dist/utilities/analytics/reference/index.d.ts +5 -3
- package/dist/utilities/analytics/reference/index.js +26 -18
- package/dist/utilities/symbolTools.d.ts +4 -2
- package/dist/utilities/symbolTools.js +16 -15
- package/dist/utils/io.d.ts +24 -6
- package/dist/utils/io.js +64 -14
- package/dist/utils/sqlite.d.ts +2 -0
- package/dist/utils/sqlite.js +6 -8
- package/dist/utils/zip.d.ts +7 -3
- package/dist/utils/zip.js +45 -43
- package/dist/validation/applePanelsValidator.d.ts +2 -1
- package/dist/validation/applePanelsValidator.js +9 -11
- package/dist/validation/astericsValidator.d.ts +2 -1
- package/dist/validation/astericsValidator.js +5 -4
- package/dist/validation/baseValidator.d.ts +2 -2
- package/dist/validation/baseValidator.js +5 -0
- package/dist/validation/dotValidator.d.ts +2 -1
- package/dist/validation/dotValidator.js +5 -4
- package/dist/validation/excelValidator.d.ts +2 -1
- package/dist/validation/excelValidator.js +5 -4
- package/dist/validation/gridsetValidator.d.ts +2 -1
- package/dist/validation/gridsetValidator.js +11 -22
- package/dist/validation/index.d.ts +2 -2
- package/dist/validation/index.js +5 -4
- package/dist/validation/obfValidator.d.ts +2 -1
- package/dist/validation/obfValidator.js +5 -4
- package/dist/validation/obfsetValidator.d.ts +2 -1
- package/dist/validation/obfsetValidator.js +5 -4
- package/dist/validation/opmlValidator.d.ts +2 -1
- package/dist/validation/opmlValidator.js +5 -4
- package/dist/validation/snapValidator.d.ts +2 -1
- package/dist/validation/snapValidator.js +9 -5
- package/dist/validation/touchChatValidator.d.ts +4 -6
- package/dist/validation/touchChatValidator.js +21 -11
- package/dist/validation/validationTypes.d.ts +8 -1
- package/package.json +1 -1
- package/dist/core/fileProcessor.d.ts +0 -7
- package/dist/core/fileProcessor.js +0 -52
|
@@ -38,15 +38,15 @@ function mapSnapVisibility(visible) {
|
|
|
38
38
|
return visible === 0 ? 'Hidden' : 'Visible';
|
|
39
39
|
}
|
|
40
40
|
class SnapProcessor extends baseProcessor_1.BaseProcessor {
|
|
41
|
-
constructor(symbolResolver = null, options
|
|
41
|
+
constructor(symbolResolver = null, options) {
|
|
42
42
|
super(options);
|
|
43
43
|
this.symbolResolver = null;
|
|
44
44
|
this.loadAudio = false;
|
|
45
45
|
this.pageLayoutPreference = 'scanning'; // Default to scanning for metrics
|
|
46
46
|
this.symbolResolver = symbolResolver;
|
|
47
|
-
this.loadAudio = options
|
|
47
|
+
this.loadAudio = options?.loadAudio !== undefined ? options.loadAudio : true;
|
|
48
48
|
this.pageLayoutPreference =
|
|
49
|
-
options
|
|
49
|
+
options?.pageLayoutPreference !== undefined ? options.pageLayoutPreference : 'scanning'; // Default to scanning
|
|
50
50
|
}
|
|
51
51
|
async extractTexts(filePathOrBuffer) {
|
|
52
52
|
const tree = await this.loadIntoTree(filePathOrBuffer);
|
|
@@ -67,11 +67,41 @@ class SnapProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
67
67
|
return texts;
|
|
68
68
|
}
|
|
69
69
|
async loadIntoTree(filePathOrBuffer) {
|
|
70
|
+
const { writeBinaryToPath, removePath, mkTempDir, basename, join } = this.options.fileAdapter;
|
|
70
71
|
await Promise.resolve();
|
|
71
72
|
const tree = new treeStructure_1.AACTree();
|
|
72
73
|
let dbResult = null;
|
|
74
|
+
let cleanupTempZip = null;
|
|
73
75
|
try {
|
|
74
|
-
|
|
76
|
+
// Handle .sub.zip files (Snap pageset backups containing .sps files)
|
|
77
|
+
let inputFile = filePathOrBuffer;
|
|
78
|
+
if (typeof filePathOrBuffer === 'string') {
|
|
79
|
+
const fileName = basename(filePathOrBuffer).toLowerCase();
|
|
80
|
+
if (fileName.endsWith('.sub.zip') || filePathOrBuffer.endsWith('.sub')) {
|
|
81
|
+
// Extract .sub.zip to find the embedded .sps file
|
|
82
|
+
const tempDir = mkTempDir('snap-sub-');
|
|
83
|
+
const zip = await this.options.zipAdapter(filePathOrBuffer);
|
|
84
|
+
// Find the .sps file in the archive
|
|
85
|
+
const files = zip.listFiles();
|
|
86
|
+
const spsFile = files.find((f) => f.endsWith('.sps'));
|
|
87
|
+
if (!spsFile) {
|
|
88
|
+
removePath(tempDir, { recursive: true, force: true });
|
|
89
|
+
throw new Error('No .sps file found in .sub.zip archive');
|
|
90
|
+
}
|
|
91
|
+
// Extract the .sps file
|
|
92
|
+
const spsData = await zip.readFile(spsFile);
|
|
93
|
+
const extractedSpsPath = join(tempDir, basename(spsFile));
|
|
94
|
+
writeBinaryToPath(extractedSpsPath, Buffer.from(spsData));
|
|
95
|
+
inputFile = extractedSpsPath;
|
|
96
|
+
cleanupTempZip = () => {
|
|
97
|
+
removePath(tempDir, { recursive: true, force: true });
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
dbResult = await (0, sqlite_1.openSqliteDatabase)(inputFile, {
|
|
102
|
+
readonly: true,
|
|
103
|
+
fileAdapter: this.options.fileAdapter,
|
|
104
|
+
});
|
|
75
105
|
const db = dbResult.db;
|
|
76
106
|
const getTableColumns = (tableName) => {
|
|
77
107
|
try {
|
|
@@ -661,24 +691,32 @@ class SnapProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
661
691
|
else if (dbResult?.db) {
|
|
662
692
|
dbResult.db.close();
|
|
663
693
|
}
|
|
694
|
+
// Clean up temporary extracted .sps file from .sub.zip
|
|
695
|
+
if (cleanupTempZip) {
|
|
696
|
+
try {
|
|
697
|
+
cleanupTempZip();
|
|
698
|
+
}
|
|
699
|
+
catch (e) {
|
|
700
|
+
console.warn('[SnapProcessor] Failed to clean up temporary .sps file:', e);
|
|
701
|
+
}
|
|
702
|
+
}
|
|
664
703
|
}
|
|
665
704
|
}
|
|
666
705
|
async processTexts(filePathOrBuffer, translations, outputPath) {
|
|
706
|
+
const { pathExists, mkDir, writeBinaryToPath, readBinaryFromInput, removePath, dirname } = this.options.fileAdapter;
|
|
667
707
|
if (!(0, io_1.isNodeRuntime)()) {
|
|
668
708
|
throw new Error('processTexts is only supported in Node.js environments for Snap files.');
|
|
669
709
|
}
|
|
670
|
-
const fs = (0, io_1.getFs)();
|
|
671
|
-
const path = (0, io_1.getPath)();
|
|
672
710
|
if (typeof filePathOrBuffer === 'string') {
|
|
673
711
|
const inputPath = filePathOrBuffer;
|
|
674
|
-
const outputDir =
|
|
675
|
-
if (!
|
|
676
|
-
|
|
712
|
+
const outputDir = dirname(outputPath);
|
|
713
|
+
if (!pathExists(outputDir)) {
|
|
714
|
+
mkDir(outputDir, { recursive: true });
|
|
677
715
|
}
|
|
678
|
-
if (
|
|
679
|
-
|
|
716
|
+
if (pathExists(outputPath)) {
|
|
717
|
+
removePath(outputPath);
|
|
680
718
|
}
|
|
681
|
-
|
|
719
|
+
writeBinaryToPath(outputPath, readBinaryFromInput(inputPath));
|
|
682
720
|
const Database = (0, sqlite_1.requireBetterSqlite3)();
|
|
683
721
|
const db = new Database(outputPath, { readonly: false });
|
|
684
722
|
try {
|
|
@@ -740,7 +778,7 @@ class SnapProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
740
778
|
finally {
|
|
741
779
|
db.close();
|
|
742
780
|
}
|
|
743
|
-
return
|
|
781
|
+
return readBinaryFromInput(outputPath);
|
|
744
782
|
}
|
|
745
783
|
// Fallback for buffer inputs: rebuild from tree (may drop Snap assets)
|
|
746
784
|
const tree = await this.loadIntoTree(filePathOrBuffer);
|
|
@@ -767,21 +805,20 @@ class SnapProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
767
805
|
});
|
|
768
806
|
});
|
|
769
807
|
await this.saveFromTree(tree, outputPath);
|
|
770
|
-
return
|
|
808
|
+
return readBinaryFromInput(outputPath);
|
|
771
809
|
}
|
|
772
810
|
async saveFromTree(tree, outputPath) {
|
|
811
|
+
const { pathExists, mkDir, removePath, dirname } = this.options.fileAdapter;
|
|
773
812
|
if (!(0, io_1.isNodeRuntime)()) {
|
|
774
813
|
throw new Error('saveFromTree is only supported in Node.js environments for Snap files.');
|
|
775
814
|
}
|
|
776
815
|
await Promise.resolve();
|
|
777
|
-
const
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
if (!fs.existsSync(outputDir)) {
|
|
781
|
-
fs.mkdirSync(outputDir, { recursive: true });
|
|
816
|
+
const outputDir = dirname(outputPath);
|
|
817
|
+
if (!pathExists(outputDir)) {
|
|
818
|
+
mkDir(outputDir, { recursive: true });
|
|
782
819
|
}
|
|
783
|
-
if (
|
|
784
|
-
|
|
820
|
+
if (pathExists(outputPath)) {
|
|
821
|
+
removePath(outputPath);
|
|
785
822
|
}
|
|
786
823
|
// Create a new SQLite database for Snap format
|
|
787
824
|
const Database = (0, sqlite_1.requireBetterSqlite3)();
|
|
@@ -1064,12 +1101,12 @@ class SnapProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
1064
1101
|
* Create a copy of the pageset with audio recordings added
|
|
1065
1102
|
*/
|
|
1066
1103
|
async createAudioEnhancedPageset(sourceDbPath, targetDbPath, audioMappings) {
|
|
1104
|
+
const { writeBinaryToPath, readBinaryFromInput } = this.options.fileAdapter;
|
|
1067
1105
|
if (!(0, io_1.isNodeRuntime)()) {
|
|
1068
1106
|
throw new Error('createAudioEnhancedPageset is only supported in Node.js environments.');
|
|
1069
1107
|
}
|
|
1070
|
-
const fs = (0, io_1.getFs)();
|
|
1071
1108
|
// Copy the source database to target
|
|
1072
|
-
|
|
1109
|
+
writeBinaryToPath(targetDbPath, readBinaryFromInput(sourceDbPath));
|
|
1073
1110
|
// Add audio recordings to the copy
|
|
1074
1111
|
for (const [buttonId, audioInfo] of audioMappings.entries()) {
|
|
1075
1112
|
await this.addAudioToButton(targetDbPath, buttonId, audioInfo.audioData, audioInfo.metadata);
|
|
@@ -1131,7 +1168,7 @@ class SnapProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
1131
1168
|
* @returns Promise with validation result
|
|
1132
1169
|
*/
|
|
1133
1170
|
async validate(filePath) {
|
|
1134
|
-
return snapValidator_1.SnapValidator.validateFile(filePath);
|
|
1171
|
+
return snapValidator_1.SnapValidator.validateFile(filePath, this.options.fileAdapter);
|
|
1135
1172
|
}
|
|
1136
1173
|
/**
|
|
1137
1174
|
* Get available PageLayouts for a Snap file
|
|
@@ -1140,14 +1177,13 @@ class SnapProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
1140
1177
|
* @returns Array of available PageLayouts with their dimensions
|
|
1141
1178
|
*/
|
|
1142
1179
|
getAvailablePageLayouts(filePath) {
|
|
1180
|
+
const { writeBinaryToPath, removePath, pathExists, join } = this.options.fileAdapter;
|
|
1143
1181
|
if (!(0, io_1.isNodeRuntime)()) {
|
|
1144
1182
|
throw new Error('getAvailablePageLayouts is only supported in Node.js environments.');
|
|
1145
1183
|
}
|
|
1146
|
-
const
|
|
1147
|
-
const path = (0, io_1.getPath)();
|
|
1148
|
-
const dbPath = typeof filePath === 'string' ? filePath : path.join(process.cwd(), 'temp.spb');
|
|
1184
|
+
const dbPath = typeof filePath === 'string' ? filePath : join(process.cwd(), 'temp.spb');
|
|
1149
1185
|
if (Buffer.isBuffer(filePath)) {
|
|
1150
|
-
|
|
1186
|
+
writeBinaryToPath(dbPath, filePath);
|
|
1151
1187
|
}
|
|
1152
1188
|
let db = null;
|
|
1153
1189
|
try {
|
|
@@ -1198,9 +1234,9 @@ class SnapProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
1198
1234
|
db.close();
|
|
1199
1235
|
}
|
|
1200
1236
|
// Clean up temporary file if created from buffer
|
|
1201
|
-
if (Buffer.isBuffer(filePath) &&
|
|
1237
|
+
if (Buffer.isBuffer(filePath) && pathExists(dbPath)) {
|
|
1202
1238
|
try {
|
|
1203
|
-
|
|
1239
|
+
removePath(dbPath);
|
|
1204
1240
|
}
|
|
1205
1241
|
catch (e) {
|
|
1206
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
|
|
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 =
|
|
70
|
-
const
|
|
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, {
|
|
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 =
|
|
499
|
-
if (!
|
|
500
|
-
|
|
497
|
+
const outputDir = dirname(outputPath);
|
|
498
|
+
if (!pathExists(outputDir)) {
|
|
499
|
+
mkDir(outputDir, { recursive: true });
|
|
501
500
|
}
|
|
502
|
-
if (
|
|
503
|
-
|
|
501
|
+
if (pathExists(outputPath)) {
|
|
502
|
+
removePath(outputPath);
|
|
504
503
|
}
|
|
505
|
-
const zip =
|
|
506
|
-
const entries =
|
|
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 =
|
|
512
|
-
const dbPath =
|
|
510
|
+
const tempDir = mkTempDir('touchchat-translate-');
|
|
511
|
+
const dbPath = join(tempDir, 'vocab.c4v');
|
|
513
512
|
try {
|
|
514
|
-
|
|
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 =
|
|
568
|
-
|
|
566
|
+
const outputZip = await this.options.zipAdapter();
|
|
567
|
+
const files = [];
|
|
568
|
+
for (const entry of entries) {
|
|
569
569
|
if (entry.entryName === vocabEntry.entryName) {
|
|
570
|
-
|
|
570
|
+
continue;
|
|
571
571
|
}
|
|
572
|
-
const data =
|
|
573
|
-
|
|
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.
|
|
576
|
-
|
|
582
|
+
const zipData = await outputZip.writeFiles(files);
|
|
583
|
+
writeBinaryToPath(outputPath, zipData);
|
|
577
584
|
}
|
|
578
585
|
finally {
|
|
579
586
|
try {
|
|
580
|
-
|
|
587
|
+
removePath(tempDir, { recursive: true, force: true });
|
|
581
588
|
}
|
|
582
589
|
catch {
|
|
583
590
|
// Best-effort cleanup
|
|
584
591
|
}
|
|
585
592
|
}
|
|
586
|
-
return
|
|
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
|
-
|
|
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 =
|
|
626
|
-
const dbPath =
|
|
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
|
|
920
|
-
const
|
|
921
|
-
zip.
|
|
922
|
-
|
|
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 (
|
|
927
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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) =>
|
|
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
|
-
|
|
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 =
|
|
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
|
|
30
|
-
const
|
|
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
|
|
38
|
-
const
|
|
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
|
|
46
|
-
const
|
|
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
|
|
54
|
-
const
|
|
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
|
|
62
|
-
const
|
|
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
|
|
81
|
-
const
|
|
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(
|
|
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) => (
|
|
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
|
-
|
|
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;
|