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