@willwade/aac-processors 0.1.21 → 0.2.1
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/README.md +3 -2
- package/dist/browser/processors/applePanelsProcessor.js +24 -24
- package/dist/browser/processors/astericsGridProcessor.js +22 -24
- package/dist/browser/processors/dotProcessor.js +6 -10
- package/dist/browser/processors/gridset/helpers.js +33 -30
- package/dist/browser/processors/gridset/symbolExtractor.js +2 -2
- package/dist/browser/processors/gridset/symbolSearch.js +22 -22
- package/dist/browser/processors/gridset/symbols.js +14 -14
- package/dist/browser/processors/gridsetProcessor.js +7 -7
- package/dist/browser/processors/obfProcessor.js +54 -47
- package/dist/browser/processors/opmlProcessor.js +6 -10
- package/dist/browser/processors/snap/helpers.js +34 -30
- package/dist/browser/processors/snapProcessor.js +28 -28
- package/dist/browser/processors/touchchatProcessor.js +24 -25
- 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 +25 -24
- package/dist/browser/utils/io.js +29 -25
- package/dist/browser/utils/sqlite.js +5 -5
- package/dist/browser/utils/zip.js +2 -4
- package/dist/browser/validation/gridsetValidator.js +2 -2
- package/dist/browser/validation/obfValidator.js +2 -2
- package/dist/browser/validation/snapValidator.js +3 -3
- package/dist/browser/validation/touchChatValidator.js +3 -3
- package/dist/cli/index.js +19 -16
- package/dist/core/baseProcessor.d.ts +1 -1
- package/dist/processors/applePanelsProcessor.js +24 -24
- package/dist/processors/astericsGridProcessor.d.ts +4 -4
- package/dist/processors/astericsGridProcessor.js +22 -24
- package/dist/processors/dotProcessor.js +6 -10
- package/dist/processors/excelProcessor.d.ts +3 -3
- package/dist/processors/excelProcessor.js +10 -13
- package/dist/processors/gridset/helpers.d.ts +9 -9
- package/dist/processors/gridset/helpers.js +33 -30
- package/dist/processors/gridset/symbolExtractor.d.ts +1 -1
- package/dist/processors/gridset/symbolExtractor.js +2 -2
- package/dist/processors/gridset/symbolSearch.d.ts +10 -10
- package/dist/processors/gridset/symbolSearch.js +22 -22
- package/dist/processors/gridset/symbols.d.ts +3 -3
- package/dist/processors/gridset/symbols.js +14 -14
- package/dist/processors/gridsetProcessor.d.ts +2 -2
- package/dist/processors/gridsetProcessor.js +7 -7
- package/dist/processors/obfProcessor.d.ts +2 -2
- package/dist/processors/obfProcessor.js +54 -47
- package/dist/processors/obfsetProcessor.js +1 -2
- package/dist/processors/opmlProcessor.js +6 -10
- package/dist/processors/snap/helpers.d.ts +8 -8
- package/dist/processors/snap/helpers.js +34 -30
- package/dist/processors/snapProcessor.d.ts +2 -2
- package/dist/processors/snapProcessor.js +28 -28
- package/dist/processors/touchchatProcessor.d.ts +2 -2
- package/dist/processors/touchchatProcessor.js +24 -25
- 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 +1 -1
- package/dist/utilities/analytics/index.js +3 -2
- 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 +21 -21
- package/dist/utilities/analytics/reference/index.js +25 -24
- package/dist/utilities/symbolTools.d.ts +5 -5
- package/dist/utilities/symbolTools.js +10 -8
- package/dist/utils/io.d.ts +11 -11
- package/dist/utils/io.js +29 -25
- package/dist/utils/sqlite.d.ts +1 -1
- package/dist/utils/sqlite.js +5 -5
- package/dist/utils/zip.js +2 -4
- package/dist/validation/applePanelsValidator.js +7 -6
- package/dist/validation/astericsValidator.js +2 -2
- package/dist/validation/dotValidator.js +2 -2
- package/dist/validation/excelValidator.js +2 -2
- package/dist/validation/gridsetValidator.js +2 -2
- package/dist/validation/index.js +2 -2
- package/dist/validation/obfValidator.js +2 -2
- package/dist/validation/obfsetValidator.js +2 -2
- package/dist/validation/opmlValidator.js +2 -2
- package/dist/validation/snapValidator.js +3 -3
- package/dist/validation/touchChatValidator.js +3 -3
- package/docs/BROWSER_USAGE.md +0 -40
- package/package.json +1 -1
|
@@ -26,9 +26,9 @@ const io_1 = require("../../utils/io");
|
|
|
26
26
|
* @param pixFilePath - Path to .pix file
|
|
27
27
|
* @returns Search index
|
|
28
28
|
*/
|
|
29
|
-
function parsePixFile(pixFilePath, fileAdapter = io_1.defaultFileAdapter) {
|
|
29
|
+
async function parsePixFile(pixFilePath, fileAdapter = io_1.defaultFileAdapter) {
|
|
30
30
|
const { readTextFromInput, basename } = fileAdapter;
|
|
31
|
-
const content = readTextFromInput(pixFilePath);
|
|
31
|
+
const content = await readTextFromInput(pixFilePath);
|
|
32
32
|
const library = basename(pixFilePath, '.pix');
|
|
33
33
|
const searchTerms = new Map();
|
|
34
34
|
const filenames = new Map();
|
|
@@ -55,18 +55,18 @@ function parsePixFile(pixFilePath, fileAdapter = io_1.defaultFileAdapter) {
|
|
|
55
55
|
* @param options - Search options
|
|
56
56
|
* @returns Map of library name to search index
|
|
57
57
|
*/
|
|
58
|
-
function loadSearchIndexes(options = {}, fileAdapter = io_1.defaultFileAdapter) {
|
|
58
|
+
async function loadSearchIndexes(options = {}, fileAdapter = io_1.defaultFileAdapter) {
|
|
59
59
|
const { listDir, pathExists, join, basename } = fileAdapter;
|
|
60
60
|
const { grid3Path, locale = 'en-GB', libraries: specifiedLibs } = options;
|
|
61
61
|
if (!grid3Path) {
|
|
62
62
|
throw new Error('grid3Path is required for symbol search');
|
|
63
63
|
}
|
|
64
64
|
const searchIndexesDir = join(grid3Path, 'Locale', locale, 'symbolsearch');
|
|
65
|
-
if (!pathExists(searchIndexesDir)) {
|
|
65
|
+
if (!(await pathExists(searchIndexesDir))) {
|
|
66
66
|
throw new Error(`Symbol search directory not found: ${searchIndexesDir}`);
|
|
67
67
|
}
|
|
68
68
|
const indexes = new Map();
|
|
69
|
-
const files = listDir(searchIndexesDir);
|
|
69
|
+
const files = await listDir(searchIndexesDir);
|
|
70
70
|
for (const file of files) {
|
|
71
71
|
if (!file.endsWith('.pix')) {
|
|
72
72
|
continue;
|
|
@@ -80,7 +80,7 @@ function loadSearchIndexes(options = {}, fileAdapter = io_1.defaultFileAdapter)
|
|
|
80
80
|
}
|
|
81
81
|
try {
|
|
82
82
|
const pixFilePath = join(searchIndexesDir, file);
|
|
83
|
-
const index = parsePixFile(pixFilePath);
|
|
83
|
+
const index = await parsePixFile(pixFilePath);
|
|
84
84
|
indexes.set(libraryName, index);
|
|
85
85
|
}
|
|
86
86
|
catch (error) {
|
|
@@ -95,8 +95,8 @@ function loadSearchIndexes(options = {}, fileAdapter = io_1.defaultFileAdapter)
|
|
|
95
95
|
* @param options - Search options
|
|
96
96
|
* @returns Array of search results
|
|
97
97
|
*/
|
|
98
|
-
function searchSymbols(searchTerm, options = {}) {
|
|
99
|
-
const indexes = loadSearchIndexes(options);
|
|
98
|
+
async function searchSymbols(searchTerm, options = {}) {
|
|
99
|
+
const indexes = await loadSearchIndexes(options);
|
|
100
100
|
const results = [];
|
|
101
101
|
const lowerSearchTerm = searchTerm.toLowerCase().trim();
|
|
102
102
|
const limit = options.limit || 100;
|
|
@@ -149,8 +149,8 @@ function searchSymbols(searchTerm, options = {}) {
|
|
|
149
149
|
* @param options - Search options
|
|
150
150
|
* @returns Symbol filename or undefined
|
|
151
151
|
*/
|
|
152
|
-
function getSymbolFilename(searchTerm, library, options = {}) {
|
|
153
|
-
const indexes = loadSearchIndexes({
|
|
152
|
+
async function getSymbolFilename(searchTerm, library, options = {}) {
|
|
153
|
+
const indexes = await loadSearchIndexes({
|
|
154
154
|
...options,
|
|
155
155
|
libraries: [library],
|
|
156
156
|
});
|
|
@@ -167,8 +167,8 @@ function getSymbolFilename(searchTerm, library, options = {}) {
|
|
|
167
167
|
* @param options - Search options
|
|
168
168
|
* @returns Display name or undefined
|
|
169
169
|
*/
|
|
170
|
-
function getSymbolDisplayName(symbolFilename, library, options = {}) {
|
|
171
|
-
const indexes = loadSearchIndexes({
|
|
170
|
+
async function getSymbolDisplayName(symbolFilename, library, options = {}) {
|
|
171
|
+
const indexes = await loadSearchIndexes({
|
|
172
172
|
...options,
|
|
173
173
|
libraries: [library],
|
|
174
174
|
});
|
|
@@ -184,8 +184,8 @@ function getSymbolDisplayName(symbolFilename, library, options = {}) {
|
|
|
184
184
|
* @param options - Search options
|
|
185
185
|
* @returns Array of search terms
|
|
186
186
|
*/
|
|
187
|
-
function getAllSearchTerms(library, options = {}) {
|
|
188
|
-
const indexes = loadSearchIndexes({
|
|
187
|
+
async function getAllSearchTerms(library, options = {}) {
|
|
188
|
+
const indexes = await loadSearchIndexes({
|
|
189
189
|
...options,
|
|
190
190
|
libraries: [library],
|
|
191
191
|
});
|
|
@@ -201,8 +201,8 @@ function getAllSearchTerms(library, options = {}) {
|
|
|
201
201
|
* @param options - Search options
|
|
202
202
|
* @returns Array of suggested terms
|
|
203
203
|
*/
|
|
204
|
-
function getSearchSuggestions(partialTerm, options = {}) {
|
|
205
|
-
const indexes = loadSearchIndexes(options);
|
|
204
|
+
async function getSearchSuggestions(partialTerm, options = {}) {
|
|
205
|
+
const indexes = await loadSearchIndexes(options);
|
|
206
206
|
const suggestions = new Set();
|
|
207
207
|
const lowerPartial = partialTerm.toLowerCase().trim();
|
|
208
208
|
for (const index of indexes.values()) {
|
|
@@ -220,8 +220,8 @@ function getSearchSuggestions(partialTerm, options = {}) {
|
|
|
220
220
|
* @param options - Search options
|
|
221
221
|
* @returns Array of full symbol references
|
|
222
222
|
*/
|
|
223
|
-
function searchSymbolsWithReferences(searchTerm, options = {}) {
|
|
224
|
-
const results = searchSymbols(searchTerm, options);
|
|
223
|
+
async function searchSymbolsWithReferences(searchTerm, options = {}) {
|
|
224
|
+
const results = await searchSymbols(searchTerm, options);
|
|
225
225
|
return results.map((r) => `[${r.library}]${r.symbolFilename}`);
|
|
226
226
|
}
|
|
227
227
|
/**
|
|
@@ -229,8 +229,8 @@ function searchSymbolsWithReferences(searchTerm, options = {}) {
|
|
|
229
229
|
* @param options - Search options
|
|
230
230
|
* @returns Map of library name to symbol count
|
|
231
231
|
*/
|
|
232
|
-
function countLibrarySymbols(options = {}) {
|
|
233
|
-
const indexes = loadSearchIndexes(options);
|
|
232
|
+
async function countLibrarySymbols(options = {}) {
|
|
233
|
+
const indexes = await loadSearchIndexes(options);
|
|
234
234
|
const counts = new Map();
|
|
235
235
|
for (const [libraryName, index] of indexes.entries()) {
|
|
236
236
|
counts.set(libraryName, index.searchTerms.size);
|
|
@@ -242,8 +242,8 @@ function countLibrarySymbols(options = {}) {
|
|
|
242
242
|
* @param options - Search options
|
|
243
243
|
* @returns Statistics about available symbols
|
|
244
244
|
*/
|
|
245
|
-
function getSymbolSearchStats(options = {}) {
|
|
246
|
-
const indexes = loadSearchIndexes(options);
|
|
245
|
+
async function getSymbolSearchStats(options = {}) {
|
|
246
|
+
const indexes = await loadSearchIndexes(options);
|
|
247
247
|
const stats = {
|
|
248
248
|
totalLibraries: indexes.size,
|
|
249
249
|
totalSymbols: 0,
|
|
@@ -94,7 +94,7 @@ export declare function isSymbolReference(reference: string): boolean;
|
|
|
94
94
|
* Get the default Grid 3 installation path for the current platform
|
|
95
95
|
* @returns Default Grid 3 path or empty string if not found
|
|
96
96
|
*/
|
|
97
|
-
export declare function getDefaultGrid3Path(fileAdapter?: FileAdapter): string
|
|
97
|
+
export declare function getDefaultGrid3Path(fileAdapter?: FileAdapter): Promise<string>;
|
|
98
98
|
/**
|
|
99
99
|
* Get the Symbol Libraries directory path
|
|
100
100
|
* Contains .symbols ZIP archives with actual image files
|
|
@@ -115,14 +115,14 @@ export declare function getSymbolSearchIndexesDir(grid3Path: string, locale?: st
|
|
|
115
115
|
* @param options - Resolution options
|
|
116
116
|
* @returns Array of symbol library information
|
|
117
117
|
*/
|
|
118
|
-
export declare function getAvailableSymbolLibraries(options?: SymbolResolutionOptions, fileAdapter?: FileAdapter): SymbolLibraryInfo[]
|
|
118
|
+
export declare function getAvailableSymbolLibraries(options?: SymbolResolutionOptions, fileAdapter?: FileAdapter): Promise<SymbolLibraryInfo[]>;
|
|
119
119
|
/**
|
|
120
120
|
* Check if a symbol library exists
|
|
121
121
|
* @param libraryName - Name of the library (e.g., 'widgit', 'tawasl')
|
|
122
122
|
* @param options - Resolution options
|
|
123
123
|
* @returns Symbol library info or undefined if not found
|
|
124
124
|
*/
|
|
125
|
-
export declare function getSymbolLibraryInfo(libraryName: string, options?: SymbolResolutionOptions, fileAdapter?: FileAdapter): SymbolLibraryInfo | undefined
|
|
125
|
+
export declare function getSymbolLibraryInfo(libraryName: string, options?: SymbolResolutionOptions, fileAdapter?: FileAdapter): Promise<SymbolLibraryInfo | undefined>;
|
|
126
126
|
/**
|
|
127
127
|
* Resolve a symbol reference to extract the actual image data
|
|
128
128
|
* @param reference - Symbol reference like "[tawasl]/above bw.png"
|
|
@@ -114,12 +114,12 @@ function isSymbolReference(reference) {
|
|
|
114
114
|
* Get the default Grid 3 installation path for the current platform
|
|
115
115
|
* @returns Default Grid 3 path or empty string if not found
|
|
116
116
|
*/
|
|
117
|
-
function getDefaultGrid3Path(fileAdapter) {
|
|
117
|
+
async function getDefaultGrid3Path(fileAdapter) {
|
|
118
118
|
const { pathExists } = fileAdapter ?? io_1.defaultFileAdapter;
|
|
119
119
|
const platform = (typeof process !== 'undefined' && process.platform ? process.platform : 'unknown');
|
|
120
120
|
const defaultPath = DEFAULT_GRID3_PATHS[platform] || '';
|
|
121
121
|
try {
|
|
122
|
-
if (defaultPath && pathExists(defaultPath)) {
|
|
122
|
+
if (defaultPath && (await pathExists(defaultPath))) {
|
|
123
123
|
return defaultPath;
|
|
124
124
|
}
|
|
125
125
|
// Try to find Grid 3 in common locations
|
|
@@ -131,7 +131,7 @@ function getDefaultGrid3Path(fileAdapter) {
|
|
|
131
131
|
'/opt/smartbox/grid3',
|
|
132
132
|
];
|
|
133
133
|
for (const testPath of commonPaths) {
|
|
134
|
-
if (pathExists(testPath)) {
|
|
134
|
+
if (await pathExists(testPath)) {
|
|
135
135
|
return testPath;
|
|
136
136
|
}
|
|
137
137
|
}
|
|
@@ -167,22 +167,22 @@ function getSymbolSearchIndexesDir(grid3Path, locale = exports.DEFAULT_LOCALE, f
|
|
|
167
167
|
* @param options - Resolution options
|
|
168
168
|
* @returns Array of symbol library information
|
|
169
169
|
*/
|
|
170
|
-
function getAvailableSymbolLibraries(options = {}, fileAdapter) {
|
|
170
|
+
async function getAvailableSymbolLibraries(options = {}, fileAdapter) {
|
|
171
171
|
const { pathExists, getFileSize, listDir, join, basename } = fileAdapter ?? io_1.defaultFileAdapter;
|
|
172
|
-
const grid3Path = options.grid3Path || options.symbolDir || getDefaultGrid3Path();
|
|
172
|
+
const grid3Path = options.grid3Path || options.symbolDir || (await getDefaultGrid3Path());
|
|
173
173
|
if (!grid3Path) {
|
|
174
174
|
return [];
|
|
175
175
|
}
|
|
176
176
|
const symbolsDir = getSymbolLibrariesDir(grid3Path, fileAdapter);
|
|
177
|
-
if (!pathExists(symbolsDir)) {
|
|
177
|
+
if (!(await pathExists(symbolsDir))) {
|
|
178
178
|
return [];
|
|
179
179
|
}
|
|
180
180
|
const libraries = [];
|
|
181
|
-
const files = listDir(symbolsDir);
|
|
181
|
+
const files = await listDir(symbolsDir);
|
|
182
182
|
for (const file of files) {
|
|
183
183
|
if (file.endsWith('.symbols')) {
|
|
184
184
|
const fullPath = join(symbolsDir, file);
|
|
185
|
-
const size = getFileSize(fullPath);
|
|
185
|
+
const size = await getFileSize(fullPath);
|
|
186
186
|
const libraryName = basename(file, '.symbols');
|
|
187
187
|
libraries.push({
|
|
188
188
|
name: libraryName,
|
|
@@ -201,9 +201,9 @@ function getAvailableSymbolLibraries(options = {}, fileAdapter) {
|
|
|
201
201
|
* @param options - Resolution options
|
|
202
202
|
* @returns Symbol library info or undefined if not found
|
|
203
203
|
*/
|
|
204
|
-
function getSymbolLibraryInfo(libraryName, options = {}, fileAdapter) {
|
|
204
|
+
async function getSymbolLibraryInfo(libraryName, options = {}, fileAdapter) {
|
|
205
205
|
const { pathExists, getFileSize, join } = fileAdapter ?? io_1.defaultFileAdapter;
|
|
206
|
-
const grid3Path = options.grid3Path || options.symbolDir || getDefaultGrid3Path();
|
|
206
|
+
const grid3Path = options.grid3Path || options.symbolDir || (await getDefaultGrid3Path());
|
|
207
207
|
if (!grid3Path) {
|
|
208
208
|
return undefined;
|
|
209
209
|
}
|
|
@@ -217,8 +217,8 @@ function getSymbolLibraryInfo(libraryName, options = {}, fileAdapter) {
|
|
|
217
217
|
];
|
|
218
218
|
for (const file of variations) {
|
|
219
219
|
const fullPath = join(symbolsDir, file);
|
|
220
|
-
if (pathExists(fullPath)) {
|
|
221
|
-
const size = getFileSize(fullPath);
|
|
220
|
+
if (await pathExists(fullPath)) {
|
|
221
|
+
const size = await getFileSize(fullPath);
|
|
222
222
|
return {
|
|
223
223
|
name: libraryName,
|
|
224
224
|
pixFile: fullPath,
|
|
@@ -245,7 +245,7 @@ async function resolveSymbolReference(reference, options = {}, fileAdapter = io_
|
|
|
245
245
|
error: 'Invalid symbol reference format',
|
|
246
246
|
};
|
|
247
247
|
}
|
|
248
|
-
const grid3Path = options.grid3Path || getDefaultGrid3Path();
|
|
248
|
+
const grid3Path = options.grid3Path || (await getDefaultGrid3Path());
|
|
249
249
|
if (!grid3Path) {
|
|
250
250
|
return {
|
|
251
251
|
reference: parsed,
|
|
@@ -253,7 +253,7 @@ async function resolveSymbolReference(reference, options = {}, fileAdapter = io_
|
|
|
253
253
|
error: 'Grid 3 installation not found. Please specify grid3Path.',
|
|
254
254
|
};
|
|
255
255
|
}
|
|
256
|
-
const libraryInfo = getSymbolLibraryInfo(parsed.library, { grid3Path });
|
|
256
|
+
const libraryInfo = await getSymbolLibraryInfo(parsed.library, { grid3Path });
|
|
257
257
|
if (!libraryInfo || !libraryInfo.exists) {
|
|
258
258
|
return {
|
|
259
259
|
reference: parsed,
|
|
@@ -30,7 +30,7 @@ declare class GridsetProcessor extends BaseProcessor {
|
|
|
30
30
|
* This method uses shared translation utilities that work across all AAC formats.
|
|
31
31
|
*
|
|
32
32
|
* @param filePathOrBuffer - Path to gridset file or buffer
|
|
33
|
-
* @returns
|
|
33
|
+
* @returns Promise resolving to symbol information for LLM processing
|
|
34
34
|
*/
|
|
35
35
|
extractSymbolsForLLM(filePathOrBuffer: string | Buffer): Promise<ButtonForTranslation[]>;
|
|
36
36
|
/**
|
|
@@ -43,7 +43,7 @@ declare class GridsetProcessor extends BaseProcessor {
|
|
|
43
43
|
* @param llmTranslations - Array of LLM translations with symbol info
|
|
44
44
|
* @param outputPath - Where to save the translated gridset
|
|
45
45
|
* @param options - Translation options (e.g., allowPartial for testing)
|
|
46
|
-
* @returns
|
|
46
|
+
* @returns Promise resolving to a buffer of the translated gridset
|
|
47
47
|
*/
|
|
48
48
|
processLLMTranslations(filePathOrBuffer: string | Buffer, llmTranslations: LLMLTranslationResult[], outputPath: string, options?: {
|
|
49
49
|
allowPartial?: boolean;
|
|
@@ -401,7 +401,7 @@ class GridsetProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
401
401
|
const tree = new treeStructure_1.AACTree();
|
|
402
402
|
let zipResult;
|
|
403
403
|
try {
|
|
404
|
-
const zipInput = readBinaryFromInput(filePathOrBuffer);
|
|
404
|
+
const zipInput = await readBinaryFromInput(filePathOrBuffer);
|
|
405
405
|
zipResult = await this.options.zipAdapter(zipInput);
|
|
406
406
|
}
|
|
407
407
|
catch (error) {
|
|
@@ -1745,7 +1745,7 @@ class GridsetProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
1745
1745
|
});
|
|
1746
1746
|
// Save the translated tree and return its content
|
|
1747
1747
|
await this.saveFromTree(tree, outputPath);
|
|
1748
|
-
return readBinaryFromInput(outputPath);
|
|
1748
|
+
return await readBinaryFromInput(outputPath);
|
|
1749
1749
|
}
|
|
1750
1750
|
/**
|
|
1751
1751
|
* Extract symbol information from a gridset for LLM-based translation.
|
|
@@ -1754,7 +1754,7 @@ class GridsetProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
1754
1754
|
* This method uses shared translation utilities that work across all AAC formats.
|
|
1755
1755
|
*
|
|
1756
1756
|
* @param filePathOrBuffer - Path to gridset file or buffer
|
|
1757
|
-
* @returns
|
|
1757
|
+
* @returns Promise resolving to symbol information for LLM processing
|
|
1758
1758
|
*/
|
|
1759
1759
|
async extractSymbolsForLLM(filePathOrBuffer) {
|
|
1760
1760
|
const tree = await this.loadIntoTree(filePathOrBuffer);
|
|
@@ -1784,7 +1784,7 @@ class GridsetProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
1784
1784
|
* @param llmTranslations - Array of LLM translations with symbol info
|
|
1785
1785
|
* @param outputPath - Where to save the translated gridset
|
|
1786
1786
|
* @param options - Translation options (e.g., allowPartial for testing)
|
|
1787
|
-
* @returns
|
|
1787
|
+
* @returns Promise resolving to a buffer of the translated gridset
|
|
1788
1788
|
*/
|
|
1789
1789
|
async processLLMTranslations(filePathOrBuffer, llmTranslations, outputPath, options) {
|
|
1790
1790
|
const { readBinaryFromInput } = this.options.fileAdapter;
|
|
@@ -1826,7 +1826,7 @@ class GridsetProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
1826
1826
|
});
|
|
1827
1827
|
// Save and return
|
|
1828
1828
|
await this.saveFromTree(tree, outputPath);
|
|
1829
|
-
return readBinaryFromInput(outputPath);
|
|
1829
|
+
return await readBinaryFromInput(outputPath);
|
|
1830
1830
|
}
|
|
1831
1831
|
async saveFromTree(tree, outputPath) {
|
|
1832
1832
|
const files = [];
|
|
@@ -1835,7 +1835,7 @@ class GridsetProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
1835
1835
|
if (Object.keys(tree.pages).length === 0) {
|
|
1836
1836
|
// Create empty zip for empty tree
|
|
1837
1837
|
const zipBuffer = await zip.writeFiles([]);
|
|
1838
|
-
writeBinaryToPath(outputPath, zipBuffer);
|
|
1838
|
+
await writeBinaryToPath(outputPath, zipBuffer);
|
|
1839
1839
|
return;
|
|
1840
1840
|
}
|
|
1841
1841
|
// Collect all unique styles from pages and buttons
|
|
@@ -2145,7 +2145,7 @@ class GridsetProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
2145
2145
|
});
|
|
2146
2146
|
// Write the zip file
|
|
2147
2147
|
const zipBuffer = await zip.writeFiles(files);
|
|
2148
|
-
writeBinaryToPath(outputPath, zipBuffer);
|
|
2148
|
+
await writeBinaryToPath(outputPath, zipBuffer);
|
|
2149
2149
|
}
|
|
2150
2150
|
// Helper method to calculate column definitions based on page layout
|
|
2151
2151
|
calculateColumnDefinitions(page) {
|
|
@@ -46,7 +46,7 @@ declare class ObfProcessor extends BaseProcessor {
|
|
|
46
46
|
* This method uses shared translation utilities that work across all AAC formats.
|
|
47
47
|
*
|
|
48
48
|
* @param filePathOrBuffer - Path to OBF/OBZ file or buffer
|
|
49
|
-
* @returns
|
|
49
|
+
* @returns Promise resolving to symbol information for LLM processing
|
|
50
50
|
*/
|
|
51
51
|
extractSymbolsForLLM(filePathOrBuffer: ProcessorInput): Promise<ButtonForTranslation[]>;
|
|
52
52
|
/**
|
|
@@ -59,7 +59,7 @@ declare class ObfProcessor extends BaseProcessor {
|
|
|
59
59
|
* @param llmTranslations - Array of LLM translations with symbol info
|
|
60
60
|
* @param outputPath - Where to save the translated OBF/OBZ file
|
|
61
61
|
* @param options - Translation options (e.g., allowPartial for testing)
|
|
62
|
-
* @returns
|
|
62
|
+
* @returns Promise resolving to a buffer of the translated OBF/OBZ file
|
|
63
63
|
*/
|
|
64
64
|
processLLMTranslations(filePathOrBuffer: ProcessorInput, llmTranslations: LLMLTranslationResult[], outputPath: string, options?: {
|
|
65
65
|
allowPartial?: boolean;
|
|
@@ -6,7 +6,6 @@ const treeStructure_1 = require("../core/treeStructure");
|
|
|
6
6
|
const idGenerator_1 = require("../utilities/analytics/utils/idGenerator");
|
|
7
7
|
const translationProcessor_1 = require("../utilities/translation/translationProcessor");
|
|
8
8
|
const io_1 = require("../utils/io");
|
|
9
|
-
const zip_1 = require("../utils/zip");
|
|
10
9
|
const OBF_FORMAT_VERSION = 'open-board-0.1';
|
|
11
10
|
/**
|
|
12
11
|
* Map OBF hidden value to AAC standard visibility
|
|
@@ -66,35 +65,42 @@ class ObfProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
66
65
|
if (this.imageCache.has(imageId)) {
|
|
67
66
|
return this.imageCache.get(imageId) ?? null;
|
|
68
67
|
}
|
|
69
|
-
if (!
|
|
68
|
+
if (!images)
|
|
70
69
|
return null;
|
|
71
|
-
}
|
|
72
70
|
// Find the image metadata
|
|
73
71
|
const imageData = images.find((img) => img.id === imageId);
|
|
74
72
|
if (!imageData) {
|
|
75
73
|
return null;
|
|
76
74
|
}
|
|
77
|
-
//
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
if
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
75
|
+
// If image has data property, use that
|
|
76
|
+
if (imageData.data) {
|
|
77
|
+
const dataUrl = imageData.data;
|
|
78
|
+
this.imageCache.set(imageId, dataUrl);
|
|
79
|
+
return dataUrl;
|
|
80
|
+
}
|
|
81
|
+
if (this.zipFile) {
|
|
82
|
+
// Try to get the image file from the ZIP
|
|
83
|
+
// Images are typically stored in an 'images' folder or root
|
|
84
|
+
const possiblePaths = [
|
|
85
|
+
imageData.path, // Explicit path if provided
|
|
86
|
+
`images/${imageData.filename || imageId}`, // Standard images folder
|
|
87
|
+
imageData.id, // Just the ID
|
|
88
|
+
].filter(Boolean);
|
|
89
|
+
for (const imagePath of possiblePaths) {
|
|
90
|
+
try {
|
|
91
|
+
const buffer = await this.zipFile.readFile(imagePath);
|
|
92
|
+
if (buffer) {
|
|
93
|
+
const contentType = imageData.content_type ||
|
|
94
|
+
this.getMimeTypeFromFilename(imagePath);
|
|
95
|
+
const dataUrl = `data:${contentType};base64,${(0, io_1.encodeBase64)(buffer)}`;
|
|
96
|
+
this.imageCache.set(imageId, dataUrl);
|
|
97
|
+
return dataUrl;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
catch (err) {
|
|
101
|
+
// Continue to next path
|
|
102
|
+
continue;
|
|
93
103
|
}
|
|
94
|
-
}
|
|
95
|
-
catch (err) {
|
|
96
|
-
// Continue to next path
|
|
97
|
-
continue;
|
|
98
104
|
}
|
|
99
105
|
}
|
|
100
106
|
// If image has a URL, use that as fallback
|
|
@@ -170,8 +176,7 @@ class ObfProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
170
176
|
buttonParameters.image_id = btn.image_id;
|
|
171
177
|
}
|
|
172
178
|
return new treeStructure_1.AACButton({
|
|
173
|
-
|
|
174
|
-
id: `${pageId}::${btn?.id || ''}`,
|
|
179
|
+
id: String(btn.id),
|
|
175
180
|
label: String(btn?.label || ''),
|
|
176
181
|
message: String(btn?.vocalization || btn?.label || ''),
|
|
177
182
|
visibility: mapObfVisibility(btn.hidden),
|
|
@@ -220,7 +225,7 @@ class ObfProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
220
225
|
return;
|
|
221
226
|
if (rowIndex >= rows || colIndex >= cols)
|
|
222
227
|
return;
|
|
223
|
-
const aacBtn = buttonMap.get(
|
|
228
|
+
const aacBtn = buttonMap.get(String(cellId));
|
|
224
229
|
if (aacBtn) {
|
|
225
230
|
grid[rowIndex][colIndex] = aacBtn;
|
|
226
231
|
}
|
|
@@ -233,7 +238,7 @@ class ObfProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
233
238
|
const row = Math.floor(btn.box_id / cols);
|
|
234
239
|
const col = btn.box_id % cols;
|
|
235
240
|
if (row < rows && col < cols) {
|
|
236
|
-
const aacBtn = buttonMap.get(
|
|
241
|
+
const aacBtn = buttonMap.get(String(btn.id));
|
|
237
242
|
if (aacBtn) {
|
|
238
243
|
grid[row][col] = aacBtn;
|
|
239
244
|
}
|
|
@@ -290,7 +295,7 @@ class ObfProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
290
295
|
// Detailed logging for debugging input
|
|
291
296
|
const bufferLength = typeof filePathOrBuffer === 'string'
|
|
292
297
|
? null
|
|
293
|
-
: readBinaryFromInput(filePathOrBuffer).byteLength;
|
|
298
|
+
: (await readBinaryFromInput(filePathOrBuffer)).byteLength;
|
|
294
299
|
console.log('[OBF] loadIntoTree called with:', {
|
|
295
300
|
type: typeof filePathOrBuffer,
|
|
296
301
|
isBuffer: typeof Buffer !== 'undefined' && Buffer.isBuffer(filePathOrBuffer),
|
|
@@ -300,9 +305,9 @@ class ObfProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
300
305
|
});
|
|
301
306
|
const tree = new treeStructure_1.AACTree();
|
|
302
307
|
// Helper: try to parse JSON OBF
|
|
303
|
-
function tryParseObfJson(data) {
|
|
308
|
+
async function tryParseObfJson(data) {
|
|
304
309
|
try {
|
|
305
|
-
const str = typeof data === 'string' ? data : readTextFromInput(data);
|
|
310
|
+
const str = typeof data === 'string' ? data : await readTextFromInput(data);
|
|
306
311
|
// Check for empty or whitespace-only content
|
|
307
312
|
if (!str.trim()) {
|
|
308
313
|
return null;
|
|
@@ -324,8 +329,8 @@ class ObfProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
324
329
|
// If input is a string path and ends with .obf, treat as JSON
|
|
325
330
|
if (typeof filePathOrBuffer === 'string' && filePathOrBuffer.toLowerCase().endsWith('.obf')) {
|
|
326
331
|
try {
|
|
327
|
-
const content = readTextFromInput(filePathOrBuffer);
|
|
328
|
-
const boardData = tryParseObfJson(content);
|
|
332
|
+
const content = await readTextFromInput(filePathOrBuffer);
|
|
333
|
+
const boardData = await tryParseObfJson(content);
|
|
329
334
|
if (boardData) {
|
|
330
335
|
console.log('[OBF] Detected .obf file, parsed as JSON');
|
|
331
336
|
const page = await this.processBoard(boardData, filePathOrBuffer, false);
|
|
@@ -353,17 +358,17 @@ class ObfProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
353
358
|
}
|
|
354
359
|
}
|
|
355
360
|
// Detect likely zip signature first
|
|
356
|
-
function isLikelyZip(input) {
|
|
361
|
+
async function isLikelyZip(input) {
|
|
357
362
|
if (typeof input === 'string') {
|
|
358
363
|
const lowered = input.toLowerCase();
|
|
359
364
|
return lowered.endsWith('.zip') || lowered.endsWith('.obz');
|
|
360
365
|
}
|
|
361
|
-
const bytes = readBinaryFromInput(input);
|
|
366
|
+
const bytes = await readBinaryFromInput(input);
|
|
362
367
|
return bytes.length >= 2 && bytes[0] === 0x50 && bytes[1] === 0x4b;
|
|
363
368
|
}
|
|
364
369
|
// Check if input is a buffer or string that parses as OBF JSON; throw if neither JSON nor ZIP
|
|
365
|
-
if (!isLikelyZip(filePathOrBuffer)) {
|
|
366
|
-
const asJson = tryParseObfJson(filePathOrBuffer);
|
|
370
|
+
if (!(await isLikelyZip(filePathOrBuffer))) {
|
|
371
|
+
const asJson = await tryParseObfJson(filePathOrBuffer);
|
|
367
372
|
if (!asJson)
|
|
368
373
|
throw new Error('Invalid OBF content: not JSON and not ZIP');
|
|
369
374
|
console.log('[OBF] Detected buffer/string as OBF JSON');
|
|
@@ -402,7 +407,7 @@ class ObfProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
402
407
|
try {
|
|
403
408
|
const content = await this.zipFile.readFile(manifestFile[0]);
|
|
404
409
|
const data = (0, io_1.decodeText)(content);
|
|
405
|
-
const str = typeof data === 'string' ? data : readTextFromInput(data);
|
|
410
|
+
const str = typeof data === 'string' ? data : await readTextFromInput(data);
|
|
406
411
|
if (!str.trim())
|
|
407
412
|
throw new Error('Manifest object missing');
|
|
408
413
|
const manifestObject = JSON.parse(str);
|
|
@@ -426,7 +431,7 @@ class ObfProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
426
431
|
for (const entryName of obfEntries) {
|
|
427
432
|
try {
|
|
428
433
|
const content = await this.zipFile.readFile(entryName);
|
|
429
|
-
const boardData = tryParseObfJson((0, io_1.decodeText)(content));
|
|
434
|
+
const boardData = await tryParseObfJson((0, io_1.decodeText)(content));
|
|
430
435
|
if (boardData) {
|
|
431
436
|
const page = await this.processBoard(boardData, entryName, true);
|
|
432
437
|
tree.addPage(page);
|
|
@@ -533,6 +538,7 @@ class ObfProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
533
538
|
border_color: button.style?.borderColor,
|
|
534
539
|
box_id: buttonPositions.get(String(button.id ?? '')),
|
|
535
540
|
image_id: imageId,
|
|
541
|
+
hidden: button.visibility === 'Hidden' || false,
|
|
536
542
|
};
|
|
537
543
|
}),
|
|
538
544
|
images: Array.isArray(page.images) ? page.images : [],
|
|
@@ -570,10 +576,10 @@ class ObfProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
570
576
|
});
|
|
571
577
|
// Save the translated tree and return its content
|
|
572
578
|
await this.saveFromTree(tree, outputPath);
|
|
573
|
-
return readBinaryFromInput(outputPath);
|
|
579
|
+
return await readBinaryFromInput(outputPath);
|
|
574
580
|
}
|
|
575
581
|
async saveFromTree(tree, outputPath) {
|
|
576
|
-
const { writeTextToPath, writeBinaryToPath } = this.options.fileAdapter;
|
|
582
|
+
const { writeTextToPath, writeBinaryToPath, pathExists } = this.options.fileAdapter;
|
|
577
583
|
if (outputPath.endsWith('.obf')) {
|
|
578
584
|
// Save as single OBF JSON file
|
|
579
585
|
const rootPage = tree.rootId ? tree.getPage(tree.rootId) : Object.values(tree.pages)[0];
|
|
@@ -581,7 +587,7 @@ class ObfProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
581
587
|
throw new Error('No pages to save');
|
|
582
588
|
}
|
|
583
589
|
const obfBoard = this.createObfBoardFromPage(rootPage, 'Exported Board', tree.metadata);
|
|
584
|
-
writeTextToPath(outputPath, JSON.stringify(obfBoard, null, 2));
|
|
590
|
+
await writeTextToPath(outputPath, JSON.stringify(obfBoard, null, 2));
|
|
585
591
|
}
|
|
586
592
|
else {
|
|
587
593
|
const files = Object.values(tree.pages).map((page) => {
|
|
@@ -593,9 +599,10 @@ class ObfProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
593
599
|
data: new TextEncoder().encode(obfContent),
|
|
594
600
|
};
|
|
595
601
|
});
|
|
596
|
-
const
|
|
597
|
-
|
|
598
|
-
|
|
602
|
+
const fileExists = await pathExists(outputPath);
|
|
603
|
+
this.zipFile = await this.options.zipAdapter(fileExists ? outputPath : undefined, this.options.fileAdapter);
|
|
604
|
+
const zipData = await this.zipFile.writeFiles(files);
|
|
605
|
+
await writeBinaryToPath(outputPath, zipData);
|
|
599
606
|
}
|
|
600
607
|
}
|
|
601
608
|
/**
|
|
@@ -628,7 +635,7 @@ class ObfProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
628
635
|
* This method uses shared translation utilities that work across all AAC formats.
|
|
629
636
|
*
|
|
630
637
|
* @param filePathOrBuffer - Path to OBF/OBZ file or buffer
|
|
631
|
-
* @returns
|
|
638
|
+
* @returns Promise resolving to symbol information for LLM processing
|
|
632
639
|
*/
|
|
633
640
|
async extractSymbolsForLLM(filePathOrBuffer) {
|
|
634
641
|
const tree = await this.loadIntoTree(filePathOrBuffer);
|
|
@@ -658,7 +665,7 @@ class ObfProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
658
665
|
* @param llmTranslations - Array of LLM translations with symbol info
|
|
659
666
|
* @param outputPath - Where to save the translated OBF/OBZ file
|
|
660
667
|
* @param options - Translation options (e.g., allowPartial for testing)
|
|
661
|
-
* @returns
|
|
668
|
+
* @returns Promise resolving to a buffer of the translated OBF/OBZ file
|
|
662
669
|
*/
|
|
663
670
|
async processLLMTranslations(filePathOrBuffer, llmTranslations, outputPath, options) {
|
|
664
671
|
const { readBinaryFromInput } = this.options.fileAdapter;
|
|
@@ -700,7 +707,7 @@ class ObfProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
700
707
|
});
|
|
701
708
|
// Save and return
|
|
702
709
|
await this.saveFromTree(tree, outputPath);
|
|
703
|
-
return readBinaryFromInput(outputPath);
|
|
710
|
+
return await readBinaryFromInput(outputPath);
|
|
704
711
|
}
|
|
705
712
|
getObfValidator() {
|
|
706
713
|
try {
|
|
@@ -33,10 +33,9 @@ class ObfsetProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
33
33
|
*/
|
|
34
34
|
async loadIntoTree(filePathOrBuffer) {
|
|
35
35
|
const { readTextFromInput } = this.options.fileAdapter;
|
|
36
|
-
await Promise.resolve();
|
|
37
36
|
const tree = new treeStructure_1.AACTree();
|
|
38
37
|
tree.metadata.format = 'obfset';
|
|
39
|
-
const content = readTextFromInput(filePathOrBuffer);
|
|
38
|
+
const content = await readTextFromInput(filePathOrBuffer);
|
|
40
39
|
const boards = JSON.parse(content);
|
|
41
40
|
// Track board ID mappings
|
|
42
41
|
const boardMap = new Map();
|
|
@@ -53,8 +53,7 @@ class OpmlProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
53
53
|
}
|
|
54
54
|
async extractTexts(filePathOrBuffer) {
|
|
55
55
|
const { readTextFromInput } = this.options.fileAdapter;
|
|
56
|
-
await
|
|
57
|
-
const content = readTextFromInput(filePathOrBuffer);
|
|
56
|
+
const content = await readTextFromInput(filePathOrBuffer);
|
|
58
57
|
const parser = new fast_xml_parser_1.XMLParser({ ignoreAttributes: false });
|
|
59
58
|
const data = parser.parse(content);
|
|
60
59
|
const texts = [];
|
|
@@ -86,10 +85,9 @@ class OpmlProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
86
85
|
}
|
|
87
86
|
async loadIntoTree(filePathOrBuffer) {
|
|
88
87
|
const { readBinaryFromInput, readTextFromInput } = this.options.fileAdapter;
|
|
89
|
-
await Promise.resolve();
|
|
90
88
|
const filename = typeof filePathOrBuffer === 'string' ? (0, io_1.getBasename)(filePathOrBuffer) : 'upload.opml';
|
|
91
|
-
const buffer = readBinaryFromInput(filePathOrBuffer);
|
|
92
|
-
const content = readTextFromInput(buffer);
|
|
89
|
+
const buffer = await readBinaryFromInput(filePathOrBuffer);
|
|
90
|
+
const content = await readTextFromInput(buffer);
|
|
93
91
|
try {
|
|
94
92
|
if (!content || !content.trim()) {
|
|
95
93
|
const validationResult = (0, validationTypes_1.buildValidationResultFromMessage)({
|
|
@@ -173,8 +171,7 @@ class OpmlProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
173
171
|
}
|
|
174
172
|
async processTexts(filePathOrBuffer, translations, outputPath) {
|
|
175
173
|
const { writeBinaryToPath, readTextFromInput } = this.options.fileAdapter;
|
|
176
|
-
await
|
|
177
|
-
const content = readTextFromInput(filePathOrBuffer);
|
|
174
|
+
const content = await readTextFromInput(filePathOrBuffer);
|
|
178
175
|
let translatedContent = content;
|
|
179
176
|
// Apply translations to text attributes in OPML outline elements
|
|
180
177
|
translations.forEach((translation, originalText) => {
|
|
@@ -185,12 +182,11 @@ class OpmlProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
185
182
|
}
|
|
186
183
|
});
|
|
187
184
|
const resultBuffer = (0, io_1.encodeText)(translatedContent);
|
|
188
|
-
writeBinaryToPath(outputPath, resultBuffer);
|
|
185
|
+
await writeBinaryToPath(outputPath, resultBuffer);
|
|
189
186
|
return resultBuffer;
|
|
190
187
|
}
|
|
191
188
|
async saveFromTree(tree, outputPath) {
|
|
192
189
|
const { writeTextToPath } = this.options.fileAdapter;
|
|
193
|
-
await Promise.resolve();
|
|
194
190
|
// Helper to recursively build outline nodes with cycle detection
|
|
195
191
|
function buildOutline(page, visited = new Set()) {
|
|
196
192
|
// Prevent infinite recursion by tracking visited pages
|
|
@@ -260,7 +256,7 @@ class OpmlProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
260
256
|
attributeNamePrefix: '@_',
|
|
261
257
|
});
|
|
262
258
|
const xml = '<?xml version="1.0" encoding="UTF-8"?>\n' + builder.build(opmlObj);
|
|
263
|
-
writeTextToPath(outputPath, xml);
|
|
259
|
+
await writeTextToPath(outputPath, xml);
|
|
264
260
|
}
|
|
265
261
|
/**
|
|
266
262
|
* Extract strings with metadata for aac-tools-platform compatibility
|