@willwade/aac-processors 0.0.29 → 0.1.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/README.md +52 -852
- package/dist/browser/core/baseProcessor.js +241 -0
- package/dist/browser/core/stringCasing.js +179 -0
- package/dist/browser/core/treeStructure.js +255 -0
- package/dist/browser/index.browser.js +73 -0
- package/dist/browser/processors/applePanelsProcessor.js +582 -0
- package/dist/browser/processors/astericsGridProcessor.js +1509 -0
- package/dist/browser/processors/dotProcessor.js +221 -0
- package/dist/browser/processors/gridset/commands.js +962 -0
- package/dist/browser/processors/gridset/crypto.js +53 -0
- package/dist/browser/processors/gridset/password.js +43 -0
- package/dist/browser/processors/gridset/pluginTypes.js +277 -0
- package/dist/browser/processors/gridset/resolver.js +137 -0
- package/dist/browser/processors/gridset/symbolAlignment.js +276 -0
- package/dist/browser/processors/gridset/symbols.js +421 -0
- package/dist/browser/processors/gridsetProcessor.js +2002 -0
- package/dist/browser/processors/obfProcessor.js +705 -0
- package/dist/browser/processors/opmlProcessor.js +274 -0
- package/dist/browser/types/aac.js +38 -0
- package/dist/browser/utilities/analytics/utils/idGenerator.js +89 -0
- package/dist/browser/utilities/translation/translationProcessor.js +200 -0
- package/dist/browser/utils/io.js +95 -0
- package/dist/browser/validation/baseValidator.js +156 -0
- package/dist/browser/validation/gridsetValidator.js +355 -0
- package/dist/browser/validation/obfValidator.js +500 -0
- package/dist/browser/validation/validationTypes.js +46 -0
- package/dist/cli/index.js +5 -5
- package/dist/core/analyze.d.ts +2 -2
- package/dist/core/analyze.js +2 -2
- package/dist/core/baseProcessor.d.ts +5 -4
- package/dist/core/baseProcessor.js +22 -27
- package/dist/core/treeStructure.d.ts +5 -5
- package/dist/core/treeStructure.js +1 -4
- package/dist/index.browser.d.ts +37 -0
- package/dist/index.browser.js +99 -0
- package/dist/index.d.ts +1 -48
- package/dist/index.js +1 -136
- package/dist/index.node.d.ts +48 -0
- package/dist/index.node.js +152 -0
- package/dist/processors/applePanelsProcessor.d.ts +5 -4
- package/dist/processors/applePanelsProcessor.js +58 -62
- package/dist/processors/astericsGridProcessor.d.ts +7 -6
- package/dist/processors/astericsGridProcessor.js +31 -42
- package/dist/processors/dotProcessor.d.ts +5 -4
- package/dist/processors/dotProcessor.js +25 -33
- package/dist/processors/excelProcessor.d.ts +4 -3
- package/dist/processors/excelProcessor.js +6 -3
- package/dist/processors/gridset/crypto.d.ts +18 -0
- package/dist/processors/gridset/crypto.js +57 -0
- package/dist/processors/gridset/helpers.d.ts +1 -1
- package/dist/processors/gridset/helpers.js +18 -8
- package/dist/processors/gridset/password.d.ts +20 -3
- package/dist/processors/gridset/password.js +17 -3
- package/dist/processors/gridset/wordlistHelpers.d.ts +3 -3
- package/dist/processors/gridset/wordlistHelpers.js +21 -20
- package/dist/processors/gridsetProcessor.d.ts +7 -12
- package/dist/processors/gridsetProcessor.js +118 -77
- package/dist/processors/obfProcessor.d.ts +9 -7
- package/dist/processors/obfProcessor.js +131 -56
- package/dist/processors/obfsetProcessor.d.ts +5 -4
- package/dist/processors/obfsetProcessor.js +10 -16
- package/dist/processors/opmlProcessor.d.ts +5 -4
- package/dist/processors/opmlProcessor.js +27 -34
- package/dist/processors/snapProcessor.d.ts +8 -7
- package/dist/processors/snapProcessor.js +15 -12
- package/dist/processors/touchchatProcessor.d.ts +8 -7
- package/dist/processors/touchchatProcessor.js +22 -17
- package/dist/types/aac.d.ts +0 -2
- package/dist/types/aac.js +2 -0
- package/dist/utils/io.d.ts +12 -0
- package/dist/utils/io.js +107 -0
- package/dist/validation/gridsetValidator.js +7 -7
- package/dist/validation/snapValidator.js +28 -35
- package/docs/BROWSER_USAGE.md +618 -0
- package/examples/README.md +77 -0
- package/examples/browser-test-server.js +81 -0
- package/examples/browser-test.html +331 -0
- package/examples/vitedemo/QUICKSTART.md +74 -0
- package/examples/vitedemo/README.md +157 -0
- package/examples/vitedemo/index.html +376 -0
- package/examples/vitedemo/package-lock.json +1221 -0
- package/examples/vitedemo/package.json +18 -0
- package/examples/vitedemo/src/main.ts +519 -0
- package/examples/vitedemo/test-files/example.dot +14 -0
- package/examples/vitedemo/test-files/example.grd +1 -0
- package/examples/vitedemo/test-files/example.gridset +0 -0
- package/examples/vitedemo/test-files/example.obz +0 -0
- package/examples/vitedemo/test-files/example.opml +18 -0
- package/examples/vitedemo/test-files/simple.obf +53 -0
- package/examples/vitedemo/tsconfig.json +24 -0
- package/examples/vitedemo/vite.config.ts +34 -0
- package/package.json +20 -4
|
@@ -1,16 +1,59 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var
|
|
3
|
-
|
|
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;
|
|
4
24
|
};
|
|
5
25
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
26
|
exports.ObfProcessor = void 0;
|
|
7
27
|
const baseProcessor_1 = require("../core/baseProcessor");
|
|
8
28
|
const treeStructure_1 = require("../core/treeStructure");
|
|
9
29
|
const idGenerator_1 = require("../utilities/analytics/utils/idGenerator");
|
|
10
|
-
const adm_zip_1 = __importDefault(require("adm-zip"));
|
|
11
|
-
const fs_1 = __importDefault(require("fs"));
|
|
12
|
-
const obfValidator_1 = require("../validation/obfValidator");
|
|
13
30
|
const translationProcessor_1 = require("../utilities/translation/translationProcessor");
|
|
31
|
+
const io_1 = require("../utils/io");
|
|
32
|
+
let JSZipModuleObf;
|
|
33
|
+
async function getJSZipObf() {
|
|
34
|
+
if (!JSZipModuleObf) {
|
|
35
|
+
try {
|
|
36
|
+
// Try ES module import first (browser/Vite)
|
|
37
|
+
const module = await Promise.resolve().then(() => __importStar(require('jszip')));
|
|
38
|
+
JSZipModuleObf = module.default || module;
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
// Fall back to CommonJS require (Node.js)
|
|
42
|
+
try {
|
|
43
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
44
|
+
const module = require('jszip');
|
|
45
|
+
JSZipModuleObf = module.default || module;
|
|
46
|
+
}
|
|
47
|
+
catch (err2) {
|
|
48
|
+
throw new Error('Zip handling requires JSZip in this environment.');
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
if (!JSZipModuleObf) {
|
|
53
|
+
throw new Error('Zip handling requires JSZip in this environment.');
|
|
54
|
+
}
|
|
55
|
+
return JSZipModuleObf;
|
|
56
|
+
}
|
|
14
57
|
const OBF_FORMAT_VERSION = 'open-board-0.1';
|
|
15
58
|
/**
|
|
16
59
|
* Map OBF hidden value to AAC standard visibility
|
|
@@ -31,7 +74,7 @@ class ObfProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
31
74
|
/**
|
|
32
75
|
* Extract an image from the ZIP file as a Buffer
|
|
33
76
|
*/
|
|
34
|
-
extractImageAsBuffer(imageId, images) {
|
|
77
|
+
async extractImageAsBuffer(imageId, images) {
|
|
35
78
|
if (!this.zipFile || !images) {
|
|
36
79
|
return null;
|
|
37
80
|
}
|
|
@@ -48,9 +91,10 @@ class ObfProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
48
91
|
].filter(Boolean);
|
|
49
92
|
for (const imagePath of possiblePaths) {
|
|
50
93
|
try {
|
|
51
|
-
const
|
|
52
|
-
if (
|
|
53
|
-
|
|
94
|
+
const file = this.zipFile.file(imagePath);
|
|
95
|
+
if (file) {
|
|
96
|
+
const buffer = await file.async('nodebuffer');
|
|
97
|
+
return buffer;
|
|
54
98
|
}
|
|
55
99
|
}
|
|
56
100
|
catch (err) {
|
|
@@ -62,7 +106,7 @@ class ObfProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
62
106
|
/**
|
|
63
107
|
* Extract an image from the ZIP file and convert to data URL
|
|
64
108
|
*/
|
|
65
|
-
extractImageAsDataUrl(imageId, images) {
|
|
109
|
+
async extractImageAsDataUrl(imageId, images) {
|
|
66
110
|
// Check cache first
|
|
67
111
|
if (this.imageCache.has(imageId)) {
|
|
68
112
|
return this.imageCache.get(imageId) ?? null;
|
|
@@ -84,12 +128,12 @@ class ObfProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
84
128
|
].filter(Boolean);
|
|
85
129
|
for (const imagePath of possiblePaths) {
|
|
86
130
|
try {
|
|
87
|
-
const
|
|
88
|
-
if (
|
|
89
|
-
const buffer =
|
|
131
|
+
const file = this.zipFile.file(imagePath);
|
|
132
|
+
if (file) {
|
|
133
|
+
const buffer = await file.async('uint8array');
|
|
90
134
|
const contentType = imageData.content_type ||
|
|
91
135
|
this.getMimeTypeFromFilename(imagePath);
|
|
92
|
-
const dataUrl = `data:${contentType};base64,${
|
|
136
|
+
const dataUrl = `data:${contentType};base64,${(0, io_1.encodeBase64)(buffer)}`;
|
|
93
137
|
this.imageCache.set(imageId, dataUrl);
|
|
94
138
|
return dataUrl;
|
|
95
139
|
}
|
|
@@ -125,7 +169,7 @@ class ObfProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
125
169
|
return 'image/png';
|
|
126
170
|
}
|
|
127
171
|
}
|
|
128
|
-
processBoard(boardData, _boardPath) {
|
|
172
|
+
async processBoard(boardData, _boardPath) {
|
|
129
173
|
const sourceButtons = boardData.buttons || [];
|
|
130
174
|
// Calculate page ID first (used to make button IDs unique)
|
|
131
175
|
const pageId = _boardPath && _boardPath.endsWith('.obf') && !_boardPath.includes('/')
|
|
@@ -133,7 +177,7 @@ class ObfProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
133
177
|
: boardData?.id
|
|
134
178
|
? String(boardData.id)
|
|
135
179
|
: _boardPath?.split('/').pop() || '';
|
|
136
|
-
const buttons = sourceButtons.map((btn) => {
|
|
180
|
+
const buttons = await Promise.all(sourceButtons.map(async (btn) => {
|
|
137
181
|
const semanticAction = btn.load_board
|
|
138
182
|
? {
|
|
139
183
|
category: treeStructure_1.AACSemanticCategory.NAVIGATION,
|
|
@@ -157,8 +201,10 @@ class ObfProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
157
201
|
let resolvedImage;
|
|
158
202
|
let imageBuffer;
|
|
159
203
|
if (btn.image_id && boardData.images) {
|
|
160
|
-
resolvedImage =
|
|
161
|
-
|
|
204
|
+
resolvedImage =
|
|
205
|
+
(await this.extractImageAsDataUrl(btn.image_id, boardData.images)) || undefined;
|
|
206
|
+
imageBuffer =
|
|
207
|
+
(await this.extractImageAsBuffer(btn.image_id, boardData.images)) || undefined;
|
|
162
208
|
}
|
|
163
209
|
// Build parameters object for Grid3 export compatibility
|
|
164
210
|
const buttonParameters = {};
|
|
@@ -186,7 +232,7 @@ class ObfProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
186
232
|
targetPageId: btn.load_board?.path,
|
|
187
233
|
semantic_id: btn.semantic_id, // Extract semantic_id if present
|
|
188
234
|
});
|
|
189
|
-
});
|
|
235
|
+
}));
|
|
190
236
|
const buttonMap = new Map(buttons.map((btn) => [btn.id, btn]));
|
|
191
237
|
const page = new treeStructure_1.AACPage({
|
|
192
238
|
id: pageId, // Use the page ID we calculated earlier
|
|
@@ -269,8 +315,8 @@ class ObfProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
269
315
|
}
|
|
270
316
|
return page;
|
|
271
317
|
}
|
|
272
|
-
extractTexts(filePathOrBuffer) {
|
|
273
|
-
const tree = this.loadIntoTree(filePathOrBuffer);
|
|
318
|
+
async extractTexts(filePathOrBuffer) {
|
|
319
|
+
const tree = await this.loadIntoTree(filePathOrBuffer);
|
|
274
320
|
const texts = [];
|
|
275
321
|
for (const pageId in tree.pages) {
|
|
276
322
|
const page = tree.pages[pageId];
|
|
@@ -285,20 +331,23 @@ class ObfProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
285
331
|
}
|
|
286
332
|
return texts;
|
|
287
333
|
}
|
|
288
|
-
loadIntoTree(filePathOrBuffer) {
|
|
334
|
+
async loadIntoTree(filePathOrBuffer) {
|
|
289
335
|
// Detailed logging for debugging input
|
|
336
|
+
const bufferLength = typeof filePathOrBuffer === 'string'
|
|
337
|
+
? null
|
|
338
|
+
: (0, io_1.readBinaryFromInput)(filePathOrBuffer).byteLength;
|
|
290
339
|
console.log('[OBF] loadIntoTree called with:', {
|
|
291
340
|
type: typeof filePathOrBuffer,
|
|
292
|
-
isBuffer: Buffer.isBuffer(filePathOrBuffer),
|
|
341
|
+
isBuffer: typeof Buffer !== 'undefined' && Buffer.isBuffer(filePathOrBuffer),
|
|
293
342
|
value: typeof filePathOrBuffer === 'string'
|
|
294
343
|
? filePathOrBuffer
|
|
295
|
-
:
|
|
344
|
+
: `[Buffer of length ${bufferLength ?? 0}]`,
|
|
296
345
|
});
|
|
297
346
|
const tree = new treeStructure_1.AACTree();
|
|
298
347
|
// Helper: try to parse JSON OBF
|
|
299
348
|
function tryParseObfJson(data) {
|
|
300
349
|
try {
|
|
301
|
-
const str = typeof data === 'string' ? data :
|
|
350
|
+
const str = typeof data === 'string' ? data : (0, io_1.readTextFromInput)(data);
|
|
302
351
|
// Check for empty or whitespace-only content
|
|
303
352
|
if (!str.trim()) {
|
|
304
353
|
return null;
|
|
@@ -320,11 +369,11 @@ class ObfProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
320
369
|
// If input is a string path and ends with .obf, treat as JSON
|
|
321
370
|
if (typeof filePathOrBuffer === 'string' && filePathOrBuffer.endsWith('.obf')) {
|
|
322
371
|
try {
|
|
323
|
-
const content =
|
|
372
|
+
const content = (0, io_1.readTextFromInput)(filePathOrBuffer);
|
|
324
373
|
const boardData = tryParseObfJson(content);
|
|
325
374
|
if (boardData) {
|
|
326
375
|
console.log('[OBF] Detected .obf file, parsed as JSON');
|
|
327
|
-
const page = this.processBoard(boardData, filePathOrBuffer);
|
|
376
|
+
const page = await this.processBoard(boardData, filePathOrBuffer);
|
|
328
377
|
tree.addPage(page);
|
|
329
378
|
// Set metadata from root board
|
|
330
379
|
tree.metadata.format = 'obf';
|
|
@@ -352,7 +401,7 @@ class ObfProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
352
401
|
const asJson = tryParseObfJson(filePathOrBuffer);
|
|
353
402
|
if (asJson) {
|
|
354
403
|
console.log('[OBF] Detected buffer/string as OBF JSON');
|
|
355
|
-
const page = this.processBoard(asJson, '[bufferOrString]');
|
|
404
|
+
const page = await this.processBoard(asJson, '[bufferOrString]');
|
|
356
405
|
tree.addPage(page);
|
|
357
406
|
// Set metadata from root board
|
|
358
407
|
tree.metadata.format = 'obf';
|
|
@@ -372,32 +421,42 @@ class ObfProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
372
421
|
function isLikelyZip(input) {
|
|
373
422
|
if (typeof input === 'string')
|
|
374
423
|
return input.endsWith('.zip') || input.endsWith('.obz');
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
}
|
|
378
|
-
return false;
|
|
424
|
+
const bytes = (0, io_1.readBinaryFromInput)(input);
|
|
425
|
+
return bytes.length >= 2 && bytes[0] === 0x50 && bytes[1] === 0x4b;
|
|
379
426
|
}
|
|
380
427
|
if (!isLikelyZip(filePathOrBuffer)) {
|
|
381
428
|
throw new Error('Invalid OBF content: not JSON and not ZIP');
|
|
382
429
|
}
|
|
430
|
+
const JSZip = await getJSZipObf();
|
|
383
431
|
let zip;
|
|
384
432
|
try {
|
|
385
|
-
|
|
433
|
+
const zipInput = (0, io_1.readBinaryFromInput)(filePathOrBuffer);
|
|
434
|
+
zip = await JSZip.loadAsync(zipInput);
|
|
386
435
|
}
|
|
387
436
|
catch (err) {
|
|
388
|
-
console.error('[OBF] Error
|
|
437
|
+
console.error('[OBF] Error loading ZIP with JSZip:', err);
|
|
389
438
|
throw err;
|
|
390
439
|
}
|
|
391
440
|
// Store the ZIP file reference for image extraction
|
|
392
441
|
this.zipFile = zip;
|
|
393
442
|
this.imageCache.clear(); // Clear cache for new file
|
|
394
443
|
console.log('[OBF] Detected zip archive, extracting .obf files');
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
444
|
+
// Collect all .obf entries
|
|
445
|
+
const obfEntries = [];
|
|
446
|
+
zip.forEach((relativePath, file) => {
|
|
447
|
+
if (file.dir)
|
|
448
|
+
return;
|
|
449
|
+
if (relativePath.endsWith('.obf')) {
|
|
450
|
+
obfEntries.push({ name: relativePath, file });
|
|
451
|
+
}
|
|
452
|
+
});
|
|
453
|
+
// Process each .obf entry
|
|
454
|
+
for (const entry of obfEntries) {
|
|
455
|
+
try {
|
|
456
|
+
const content = await entry.file.async('string');
|
|
398
457
|
const boardData = tryParseObfJson(content);
|
|
399
458
|
if (boardData) {
|
|
400
|
-
const page = this.processBoard(boardData, entry.
|
|
459
|
+
const page = await this.processBoard(boardData, entry.name);
|
|
401
460
|
tree.addPage(page);
|
|
402
461
|
// Set metadata if not already set (use first board as reference)
|
|
403
462
|
if (!tree.metadata.format) {
|
|
@@ -414,10 +473,13 @@ class ObfProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
414
473
|
}
|
|
415
474
|
}
|
|
416
475
|
else {
|
|
417
|
-
console.warn('[OBF] Skipped entry (not valid OBF JSON):', entry.
|
|
476
|
+
console.warn('[OBF] Skipped entry (not valid OBF JSON):', entry.name);
|
|
418
477
|
}
|
|
419
478
|
}
|
|
420
|
-
|
|
479
|
+
catch (err) {
|
|
480
|
+
console.warn('[OBF] Error processing entry:', entry.name, err);
|
|
481
|
+
}
|
|
482
|
+
}
|
|
421
483
|
return tree;
|
|
422
484
|
}
|
|
423
485
|
buildGridMetadata(page) {
|
|
@@ -497,9 +559,9 @@ class ObfProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
497
559
|
sounds: Array.isArray(page.sounds) ? page.sounds : [],
|
|
498
560
|
};
|
|
499
561
|
}
|
|
500
|
-
processTexts(filePathOrBuffer, translations, outputPath) {
|
|
562
|
+
async processTexts(filePathOrBuffer, translations, outputPath) {
|
|
501
563
|
// Load the tree, apply translations, and save to new file
|
|
502
|
-
const tree = this.loadIntoTree(filePathOrBuffer);
|
|
564
|
+
const tree = await this.loadIntoTree(filePathOrBuffer);
|
|
503
565
|
// Apply translations to all text content
|
|
504
566
|
Object.values(tree.pages).forEach((page) => {
|
|
505
567
|
// Translate page names
|
|
@@ -526,10 +588,10 @@ class ObfProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
526
588
|
});
|
|
527
589
|
});
|
|
528
590
|
// Save the translated tree and return its content
|
|
529
|
-
this.saveFromTree(tree, outputPath);
|
|
530
|
-
return
|
|
591
|
+
await this.saveFromTree(tree, outputPath);
|
|
592
|
+
return (0, io_1.readBinaryFromInput)(outputPath);
|
|
531
593
|
}
|
|
532
|
-
saveFromTree(tree, outputPath) {
|
|
594
|
+
async saveFromTree(tree, outputPath) {
|
|
533
595
|
if (outputPath.endsWith('.obf')) {
|
|
534
596
|
// Save as single OBF JSON file
|
|
535
597
|
const rootPage = tree.rootId ? tree.getPage(tree.rootId) : Object.values(tree.pages)[0];
|
|
@@ -537,17 +599,20 @@ class ObfProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
537
599
|
throw new Error('No pages to save');
|
|
538
600
|
}
|
|
539
601
|
const obfBoard = this.createObfBoardFromPage(rootPage, 'Exported Board', tree.metadata);
|
|
540
|
-
|
|
602
|
+
(0, io_1.writeTextToPath)(outputPath, JSON.stringify(obfBoard, null, 2));
|
|
541
603
|
}
|
|
542
604
|
else {
|
|
543
605
|
// Save as OBZ (zip with multiple OBF files)
|
|
544
|
-
const
|
|
606
|
+
const JSZip = await getJSZipObf();
|
|
607
|
+
const zip = new JSZip();
|
|
545
608
|
Object.values(tree.pages).forEach((page) => {
|
|
546
609
|
const obfBoard = this.createObfBoardFromPage(page, 'Board', tree.metadata);
|
|
547
610
|
const obfContent = JSON.stringify(obfBoard, null, 2);
|
|
548
|
-
zip.
|
|
611
|
+
zip.file(`${page.id}.obf`, obfContent);
|
|
549
612
|
});
|
|
550
|
-
zip.
|
|
613
|
+
const zipBuffer = await zip.generateAsync({ type: 'uint8array' });
|
|
614
|
+
const { writeBinaryToPath } = await Promise.resolve().then(() => __importStar(require('../utils/io')));
|
|
615
|
+
writeBinaryToPath(outputPath, zipBuffer);
|
|
551
616
|
}
|
|
552
617
|
}
|
|
553
618
|
/**
|
|
@@ -570,7 +635,8 @@ class ObfProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
570
635
|
* @returns Promise with validation result
|
|
571
636
|
*/
|
|
572
637
|
async validate(filePath) {
|
|
573
|
-
|
|
638
|
+
const ObfValidator = this.getObfValidator();
|
|
639
|
+
return ObfValidator.validateFile(filePath);
|
|
574
640
|
}
|
|
575
641
|
/**
|
|
576
642
|
* Extract symbol information from an OBF/OBZ file for LLM-based translation.
|
|
@@ -581,8 +647,8 @@ class ObfProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
581
647
|
* @param filePathOrBuffer - Path to OBF/OBZ file or buffer
|
|
582
648
|
* @returns Array of symbol information for LLM processing
|
|
583
649
|
*/
|
|
584
|
-
extractSymbolsForLLM(filePathOrBuffer) {
|
|
585
|
-
const tree = this.loadIntoTree(filePathOrBuffer);
|
|
650
|
+
async extractSymbolsForLLM(filePathOrBuffer) {
|
|
651
|
+
const tree = await this.loadIntoTree(filePathOrBuffer);
|
|
586
652
|
// Collect all buttons from all pages
|
|
587
653
|
const allButtons = [];
|
|
588
654
|
Object.values(tree.pages).forEach((page) => {
|
|
@@ -611,8 +677,8 @@ class ObfProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
611
677
|
* @param options - Translation options (e.g., allowPartial for testing)
|
|
612
678
|
* @returns Buffer of the translated OBF/OBZ file
|
|
613
679
|
*/
|
|
614
|
-
processLLMTranslations(filePathOrBuffer, llmTranslations, outputPath, options) {
|
|
615
|
-
const tree = this.loadIntoTree(filePathOrBuffer);
|
|
680
|
+
async processLLMTranslations(filePathOrBuffer, llmTranslations, outputPath, options) {
|
|
681
|
+
const tree = await this.loadIntoTree(filePathOrBuffer);
|
|
616
682
|
// Validate translations using shared utility
|
|
617
683
|
const buttonIds = Object.values(tree.pages).flatMap((page) => page.buttons.map((b) => b.id));
|
|
618
684
|
(0, translationProcessor_1.validateTranslationResults)(llmTranslations, buttonIds, options);
|
|
@@ -649,8 +715,17 @@ class ObfProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
649
715
|
});
|
|
650
716
|
});
|
|
651
717
|
// Save and return
|
|
652
|
-
this.saveFromTree(tree, outputPath);
|
|
653
|
-
return
|
|
718
|
+
await this.saveFromTree(tree, outputPath);
|
|
719
|
+
return (0, io_1.readBinaryFromInput)(outputPath);
|
|
720
|
+
}
|
|
721
|
+
getObfValidator() {
|
|
722
|
+
try {
|
|
723
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires, @typescript-eslint/no-unsafe-return
|
|
724
|
+
return require('../validation/obfValidator').ObfValidator;
|
|
725
|
+
}
|
|
726
|
+
catch (error) {
|
|
727
|
+
throw new Error('Validation utilities are not available in this environment.');
|
|
728
|
+
}
|
|
654
729
|
}
|
|
655
730
|
}
|
|
656
731
|
exports.ObfProcessor = ObfProcessor;
|
|
@@ -4,23 +4,24 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { AACTree } from '../core/treeStructure';
|
|
6
6
|
import { BaseProcessor, ProcessorOptions } from '../core/baseProcessor';
|
|
7
|
+
import { ProcessorInput } from '../utils/io';
|
|
7
8
|
export declare class ObfsetProcessor extends BaseProcessor {
|
|
8
9
|
constructor(options?: ProcessorOptions);
|
|
9
10
|
/**
|
|
10
11
|
* Extract all text content
|
|
11
12
|
*/
|
|
12
|
-
extractTexts(filePathOrBuffer:
|
|
13
|
+
extractTexts(filePathOrBuffer: ProcessorInput): Promise<string[]>;
|
|
13
14
|
/**
|
|
14
15
|
* Load an .obfset file (JSON array of boards)
|
|
15
16
|
*/
|
|
16
|
-
loadIntoTree(filePathOrBuffer:
|
|
17
|
+
loadIntoTree(filePathOrBuffer: ProcessorInput): Promise<AACTree>;
|
|
17
18
|
/**
|
|
18
19
|
* Process texts (not supported for .obfset currently)
|
|
19
20
|
*/
|
|
20
|
-
processTexts(_filePathOrBuffer:
|
|
21
|
+
processTexts(_filePathOrBuffer: ProcessorInput, _translations: Map<string, string>, _outputPath: string): Promise<Uint8Array>;
|
|
21
22
|
/**
|
|
22
23
|
* Save tree structure back to file
|
|
23
24
|
*/
|
|
24
|
-
saveFromTree(_tree: AACTree, _outputPath: string): void
|
|
25
|
+
saveFromTree(_tree: AACTree, _outputPath: string): Promise<void>;
|
|
25
26
|
supportsExtension(extension: string): boolean;
|
|
26
27
|
}
|
|
@@ -3,15 +3,12 @@
|
|
|
3
3
|
* OBF Set Processor - Handles JSON-formatted .obfset files
|
|
4
4
|
* These are pre-extracted board sets in JSON array format
|
|
5
5
|
*/
|
|
6
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
7
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
8
|
-
};
|
|
9
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
7
|
exports.ObfsetProcessor = void 0;
|
|
11
8
|
const treeStructure_1 = require("../core/treeStructure");
|
|
12
9
|
const treeStructure_2 = require("../core/treeStructure");
|
|
13
|
-
const fs_1 = __importDefault(require("fs"));
|
|
14
10
|
const baseProcessor_1 = require("../core/baseProcessor");
|
|
11
|
+
const io_1 = require("../utils/io");
|
|
15
12
|
class ObfsetProcessor extends baseProcessor_1.BaseProcessor {
|
|
16
13
|
constructor(options = {}) {
|
|
17
14
|
super(options);
|
|
@@ -19,8 +16,8 @@ class ObfsetProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
19
16
|
/**
|
|
20
17
|
* Extract all text content
|
|
21
18
|
*/
|
|
22
|
-
extractTexts(filePathOrBuffer) {
|
|
23
|
-
const tree = this.loadIntoTree(filePathOrBuffer);
|
|
19
|
+
async extractTexts(filePathOrBuffer) {
|
|
20
|
+
const tree = await this.loadIntoTree(filePathOrBuffer);
|
|
24
21
|
const texts = new Set();
|
|
25
22
|
Object.values(tree.pages).forEach((page) => {
|
|
26
23
|
if (page.name)
|
|
@@ -35,16 +32,11 @@ class ObfsetProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
35
32
|
/**
|
|
36
33
|
* Load an .obfset file (JSON array of boards)
|
|
37
34
|
*/
|
|
38
|
-
loadIntoTree(filePathOrBuffer) {
|
|
35
|
+
async loadIntoTree(filePathOrBuffer) {
|
|
36
|
+
await Promise.resolve();
|
|
39
37
|
const tree = new treeStructure_1.AACTree();
|
|
40
38
|
tree.metadata.format = 'obfset';
|
|
41
|
-
|
|
42
|
-
if (Buffer.isBuffer(filePathOrBuffer)) {
|
|
43
|
-
content = filePathOrBuffer.toString('utf-8');
|
|
44
|
-
}
|
|
45
|
-
else {
|
|
46
|
-
content = fs_1.default.readFileSync(filePathOrBuffer, 'utf-8');
|
|
47
|
-
}
|
|
39
|
+
const content = (0, io_1.readTextFromInput)(filePathOrBuffer);
|
|
48
40
|
const boards = JSON.parse(content);
|
|
49
41
|
// Track board ID mappings
|
|
50
42
|
const boardMap = new Map();
|
|
@@ -164,13 +156,15 @@ class ObfsetProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
164
156
|
/**
|
|
165
157
|
* Process texts (not supported for .obfset currently)
|
|
166
158
|
*/
|
|
167
|
-
processTexts(_filePathOrBuffer, _translations, _outputPath) {
|
|
159
|
+
async processTexts(_filePathOrBuffer, _translations, _outputPath) {
|
|
160
|
+
await Promise.resolve();
|
|
168
161
|
throw new Error('processTexts is not supported for .obfset currently');
|
|
169
162
|
}
|
|
170
163
|
/**
|
|
171
164
|
* Save tree structure back to file
|
|
172
165
|
*/
|
|
173
|
-
saveFromTree(_tree, _outputPath) {
|
|
166
|
+
async saveFromTree(_tree, _outputPath) {
|
|
167
|
+
await Promise.resolve();
|
|
174
168
|
throw new Error('saveFromTree is not supported for .obfset currently');
|
|
175
169
|
}
|
|
176
170
|
supportsExtension(extension) {
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { BaseProcessor, ProcessorOptions, ExtractStringsResult, TranslatedString, SourceString } from '../core/baseProcessor';
|
|
2
2
|
import { AACTree } from '../core/treeStructure';
|
|
3
|
+
import { ProcessorInput } from '../utils/io';
|
|
3
4
|
declare class OpmlProcessor extends BaseProcessor {
|
|
4
5
|
constructor(options?: ProcessorOptions);
|
|
5
6
|
private processOutline;
|
|
6
|
-
extractTexts(filePathOrBuffer:
|
|
7
|
-
loadIntoTree(filePathOrBuffer:
|
|
8
|
-
processTexts(filePathOrBuffer:
|
|
9
|
-
saveFromTree(tree: AACTree, outputPath: string): void
|
|
7
|
+
extractTexts(filePathOrBuffer: ProcessorInput): Promise<string[]>;
|
|
8
|
+
loadIntoTree(filePathOrBuffer: ProcessorInput): Promise<AACTree>;
|
|
9
|
+
processTexts(filePathOrBuffer: ProcessorInput, translations: Map<string, string>, outputPath: string): Promise<Uint8Array>;
|
|
10
|
+
saveFromTree(tree: AACTree, outputPath: string): Promise<void>;
|
|
10
11
|
/**
|
|
11
12
|
* Extract strings with metadata for aac-tools-platform compatibility
|
|
12
13
|
* Uses the generic implementation from BaseProcessor
|
|
@@ -1,16 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
3
|
exports.OpmlProcessor = void 0;
|
|
7
4
|
const baseProcessor_1 = require("../core/baseProcessor");
|
|
8
5
|
const treeStructure_1 = require("../core/treeStructure");
|
|
9
6
|
// Removed unused import: FileProcessor
|
|
10
7
|
const fast_xml_parser_1 = require("fast-xml-parser");
|
|
11
|
-
const
|
|
12
|
-
const
|
|
13
|
-
const validation_1 = require("../validation");
|
|
8
|
+
const validationTypes_1 = require("../validation/validationTypes");
|
|
9
|
+
const io_1 = require("../utils/io");
|
|
14
10
|
class OpmlProcessor extends baseProcessor_1.BaseProcessor {
|
|
15
11
|
constructor(options) {
|
|
16
12
|
super(options);
|
|
@@ -56,10 +52,9 @@ class OpmlProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
56
52
|
return { page: null, childPages: [] };
|
|
57
53
|
return { page, childPages };
|
|
58
54
|
}
|
|
59
|
-
extractTexts(filePathOrBuffer) {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
: filePathOrBuffer.toString('utf8');
|
|
55
|
+
async extractTexts(filePathOrBuffer) {
|
|
56
|
+
await Promise.resolve();
|
|
57
|
+
const content = (0, io_1.readTextFromInput)(filePathOrBuffer);
|
|
63
58
|
const parser = new fast_xml_parser_1.XMLParser({ ignoreAttributes: false });
|
|
64
59
|
const data = parser.parse(content);
|
|
65
60
|
const texts = [];
|
|
@@ -89,15 +84,14 @@ class OpmlProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
89
84
|
outlines.forEach(processNode);
|
|
90
85
|
return texts;
|
|
91
86
|
}
|
|
92
|
-
loadIntoTree(filePathOrBuffer) {
|
|
93
|
-
|
|
94
|
-
const
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
const content = buffer.toString('utf8');
|
|
87
|
+
async loadIntoTree(filePathOrBuffer) {
|
|
88
|
+
await Promise.resolve();
|
|
89
|
+
const filename = typeof filePathOrBuffer === 'string' ? (0, io_1.getBasename)(filePathOrBuffer) : 'upload.opml';
|
|
90
|
+
const buffer = (0, io_1.readBinaryFromInput)(filePathOrBuffer);
|
|
91
|
+
const content = (0, io_1.readTextFromInput)(buffer);
|
|
98
92
|
try {
|
|
99
93
|
if (!content || !content.trim()) {
|
|
100
|
-
const validationResult = (0,
|
|
94
|
+
const validationResult = (0, validationTypes_1.buildValidationResultFromMessage)({
|
|
101
95
|
filename,
|
|
102
96
|
filesize: buffer.byteLength,
|
|
103
97
|
format: 'opml',
|
|
@@ -105,13 +99,13 @@ class OpmlProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
105
99
|
type: 'content',
|
|
106
100
|
description: 'OPML content is empty',
|
|
107
101
|
});
|
|
108
|
-
throw new
|
|
102
|
+
throw new validationTypes_1.ValidationFailureError('Empty OPML content', validationResult);
|
|
109
103
|
}
|
|
110
104
|
// Validate XML before parsing, fast-xml-parser is permissive by default
|
|
111
105
|
const validationResult = fast_xml_parser_1.XMLValidator.validate(content);
|
|
112
106
|
if (validationResult !== true) {
|
|
113
107
|
const reason = validationResult?.err?.msg || JSON.stringify(validationResult);
|
|
114
|
-
const structured = (0,
|
|
108
|
+
const structured = (0, validationTypes_1.buildValidationResultFromMessage)({
|
|
115
109
|
filename,
|
|
116
110
|
filesize: buffer.byteLength,
|
|
117
111
|
format: 'opml',
|
|
@@ -119,7 +113,7 @@ class OpmlProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
119
113
|
type: 'xml',
|
|
120
114
|
description: 'OPML XML validation',
|
|
121
115
|
});
|
|
122
|
-
throw new
|
|
116
|
+
throw new validationTypes_1.ValidationFailureError('Invalid OPML XML', structured);
|
|
123
117
|
}
|
|
124
118
|
const parser = new fast_xml_parser_1.XMLParser({ ignoreAttributes: false });
|
|
125
119
|
const data = parser.parse(content);
|
|
@@ -128,7 +122,7 @@ class OpmlProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
128
122
|
// Handle case where body.outline might not exist or be in different formats
|
|
129
123
|
const bodyOutline = data.opml?.body?.outline;
|
|
130
124
|
if (!bodyOutline) {
|
|
131
|
-
const structured = (0,
|
|
125
|
+
const structured = (0, validationTypes_1.buildValidationResultFromMessage)({
|
|
132
126
|
filename,
|
|
133
127
|
filesize: buffer.byteLength,
|
|
134
128
|
format: 'opml',
|
|
@@ -136,7 +130,7 @@ class OpmlProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
136
130
|
type: 'structure',
|
|
137
131
|
description: 'OPML outline root',
|
|
138
132
|
});
|
|
139
|
-
throw new
|
|
133
|
+
throw new validationTypes_1.ValidationFailureError('Invalid OPML structure', structured);
|
|
140
134
|
}
|
|
141
135
|
const outlines = Array.isArray(bodyOutline) ? bodyOutline : [bodyOutline];
|
|
142
136
|
let firstRootId = null;
|
|
@@ -162,10 +156,10 @@ class OpmlProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
162
156
|
return tree;
|
|
163
157
|
}
|
|
164
158
|
catch (err) {
|
|
165
|
-
if (err instanceof
|
|
159
|
+
if (err instanceof validationTypes_1.ValidationFailureError) {
|
|
166
160
|
throw err;
|
|
167
161
|
}
|
|
168
|
-
const validationResult = (0,
|
|
162
|
+
const validationResult = (0, validationTypes_1.buildValidationResultFromMessage)({
|
|
169
163
|
filename,
|
|
170
164
|
filesize: buffer.byteLength,
|
|
171
165
|
format: 'opml',
|
|
@@ -173,13 +167,12 @@ class OpmlProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
173
167
|
type: 'parse',
|
|
174
168
|
description: 'Parse OPML XML',
|
|
175
169
|
});
|
|
176
|
-
throw new
|
|
170
|
+
throw new validationTypes_1.ValidationFailureError('Failed to load OPML file', validationResult, err);
|
|
177
171
|
}
|
|
178
172
|
}
|
|
179
|
-
processTexts(filePathOrBuffer, translations, outputPath) {
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
: filePathOrBuffer.toString('utf8');
|
|
173
|
+
async processTexts(filePathOrBuffer, translations, outputPath) {
|
|
174
|
+
await Promise.resolve();
|
|
175
|
+
const content = (0, io_1.readTextFromInput)(filePathOrBuffer);
|
|
183
176
|
let translatedContent = content;
|
|
184
177
|
// Apply translations to text attributes in OPML outline elements
|
|
185
178
|
translations.forEach((translation, originalText) => {
|
|
@@ -189,12 +182,12 @@ class OpmlProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
189
182
|
translatedContent = translatedContent.replace(textAttrRegex, `text="${translation}"`);
|
|
190
183
|
}
|
|
191
184
|
});
|
|
192
|
-
const resultBuffer =
|
|
193
|
-
|
|
194
|
-
fs_1.default.writeFileSync(outputPath, resultBuffer);
|
|
185
|
+
const resultBuffer = (0, io_1.encodeText)(translatedContent);
|
|
186
|
+
(0, io_1.writeBinaryToPath)(outputPath, resultBuffer);
|
|
195
187
|
return resultBuffer;
|
|
196
188
|
}
|
|
197
|
-
saveFromTree(tree, outputPath) {
|
|
189
|
+
async saveFromTree(tree, outputPath) {
|
|
190
|
+
await Promise.resolve();
|
|
198
191
|
// Helper to recursively build outline nodes with cycle detection
|
|
199
192
|
function buildOutline(page, visited = new Set()) {
|
|
200
193
|
// Prevent infinite recursion by tracking visited pages
|
|
@@ -264,7 +257,7 @@ class OpmlProcessor extends baseProcessor_1.BaseProcessor {
|
|
|
264
257
|
attributeNamePrefix: '@_',
|
|
265
258
|
});
|
|
266
259
|
const xml = '<?xml version="1.0" encoding="UTF-8"?>\n' + builder.build(opmlObj);
|
|
267
|
-
|
|
260
|
+
(0, io_1.writeTextToPath)(outputPath, xml);
|
|
268
261
|
}
|
|
269
262
|
/**
|
|
270
263
|
* Extract strings with metadata for aac-tools-platform compatibility
|