@willwade/aac-processors 0.1.5 → 0.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/README.md +14 -0
  2. package/dist/browser/index.browser.js +15 -1
  3. package/dist/browser/processors/gridset/password.js +11 -0
  4. package/dist/browser/processors/gridsetProcessor.js +42 -46
  5. package/dist/browser/processors/obfProcessor.js +47 -63
  6. package/dist/browser/processors/snapProcessor.js +1031 -0
  7. package/dist/browser/processors/touchchatProcessor.js +1004 -0
  8. package/dist/browser/utils/io.js +36 -2
  9. package/dist/browser/utils/sqlite.js +109 -0
  10. package/dist/browser/utils/zip.js +54 -0
  11. package/dist/browser/validation/gridsetValidator.js +7 -27
  12. package/dist/browser/validation/obfValidator.js +9 -4
  13. package/dist/browser/validation/snapValidator.js +197 -0
  14. package/dist/browser/validation/touchChatValidator.js +201 -0
  15. package/dist/index.browser.d.ts +7 -0
  16. package/dist/index.browser.js +19 -2
  17. package/dist/processors/gridset/helpers.js +3 -4
  18. package/dist/processors/gridset/index.d.ts +1 -1
  19. package/dist/processors/gridset/index.js +3 -2
  20. package/dist/processors/gridset/password.d.ts +3 -2
  21. package/dist/processors/gridset/password.js +12 -0
  22. package/dist/processors/gridset/wordlistHelpers.js +107 -51
  23. package/dist/processors/gridsetProcessor.js +40 -44
  24. package/dist/processors/obfProcessor.js +46 -62
  25. package/dist/processors/snapProcessor.js +60 -54
  26. package/dist/processors/touchchatProcessor.js +38 -36
  27. package/dist/utils/io.d.ts +4 -0
  28. package/dist/utils/io.js +40 -2
  29. package/dist/utils/sqlite.d.ts +21 -0
  30. package/dist/utils/sqlite.js +137 -0
  31. package/dist/utils/zip.d.ts +7 -0
  32. package/dist/utils/zip.js +80 -0
  33. package/dist/validation/applePanelsValidator.js +11 -28
  34. package/dist/validation/astericsValidator.js +11 -30
  35. package/dist/validation/dotValidator.js +11 -30
  36. package/dist/validation/excelValidator.js +5 -6
  37. package/dist/validation/gridsetValidator.js +29 -26
  38. package/dist/validation/index.d.ts +2 -1
  39. package/dist/validation/index.js +9 -32
  40. package/dist/validation/obfValidator.js +8 -3
  41. package/dist/validation/obfsetValidator.js +11 -30
  42. package/dist/validation/opmlValidator.js +11 -30
  43. package/dist/validation/snapValidator.js +6 -9
  44. package/dist/validation/touchChatValidator.js +6 -7
  45. package/docs/BROWSER_USAGE.md +2 -10
  46. package/examples/README.md +3 -75
  47. package/examples/vitedemo/README.md +13 -7
  48. package/examples/vitedemo/index.html +51 -2
  49. package/examples/vitedemo/package-lock.json +9 -0
  50. package/examples/vitedemo/package.json +1 -0
  51. package/examples/vitedemo/src/main.ts +132 -2
  52. package/examples/vitedemo/src/vite-env.d.ts +1 -0
  53. package/examples/vitedemo/vite.config.ts +26 -7
  54. package/package.json +3 -1
  55. package/examples/browser-test-server.js +0 -81
@@ -40,31 +40,7 @@ const resolver_2 = require("./gridset/resolver");
40
40
  const idGenerator_1 = require("../utilities/analytics/utils/idGenerator");
41
41
  const symbolAlignment_1 = require("./gridset/symbolAlignment");
42
42
  const io_1 = require("../utils/io");
43
- let JSZipModule;
44
- async function getJSZip() {
45
- if (!JSZipModule) {
46
- try {
47
- // Try ES module import first (browser/Vite)
48
- const module = await Promise.resolve().then(() => __importStar(require('jszip')));
49
- JSZipModule = module.default || module;
50
- }
51
- catch (error) {
52
- // Fall back to CommonJS require (Node.js)
53
- try {
54
- // eslint-disable-next-line @typescript-eslint/no-var-requires
55
- const module = require('jszip');
56
- JSZipModule = module.default || module;
57
- }
58
- catch (err2) {
59
- throw new Error('Zip handling requires JSZip in this environment.');
60
- }
61
- }
62
- }
63
- if (!JSZipModule) {
64
- throw new Error('Zip handling requires JSZip in this environment.');
65
- }
66
- return JSZipModule;
67
- }
43
+ const zip_1 = require("../utils/zip");
68
44
  class GridsetProcessor extends baseProcessor_1.BaseProcessor {
69
45
  constructor(options) {
70
46
  super(options);
@@ -437,17 +413,15 @@ class GridsetProcessor extends baseProcessor_1.BaseProcessor {
437
413
  }
438
414
  async loadIntoTree(filePathOrBuffer) {
439
415
  const tree = new treeStructure_1.AACTree();
440
- let zip;
416
+ let zipResult;
441
417
  try {
442
- const JSZip = await getJSZip();
443
- const zipInput = (0, io_1.readBinaryFromInput)(filePathOrBuffer);
444
- zip = await JSZip.loadAsync(zipInput);
418
+ zipResult = await (0, zip_1.openZipFromInput)((0, io_1.readBinaryFromInput)(filePathOrBuffer));
445
419
  }
446
420
  catch (error) {
447
421
  throw new Error(`Invalid ZIP file format: ${error.message}`);
448
422
  }
449
423
  const password = this.getGridsetPassword(filePathOrBuffer);
450
- const entries = (0, password_1.getZipEntriesWithPassword)(zip, password);
424
+ const entries = (0, password_1.getZipEntriesFromAdapter)(zipResult.zip, password);
451
425
  const parser = new fast_xml_parser_1.XMLParser({ ignoreAttributes: false });
452
426
  const isEncryptedArchive = typeof filePathOrBuffer === 'string' && filePathOrBuffer.toLowerCase().endsWith('.gridsetx');
453
427
  const encryptedContentPassword = this.getGridsetPassword(filePathOrBuffer);
@@ -458,14 +432,11 @@ class GridsetProcessor extends baseProcessor_1.BaseProcessor {
458
432
  passwordProtected: !!password,
459
433
  };
460
434
  const readEntryBuffer = async (entry) => {
461
- // eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-argument
462
435
  const raw = await entry.getData();
463
436
  if (!isEncryptedArchive) {
464
- // eslint-disable-next-line @typescript-eslint/no-unsafe-return
465
437
  return raw;
466
438
  }
467
- // eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-return
468
- return (0, crypto_1.decryptGridsetEntry)(raw, encryptedContentPassword);
439
+ return (0, crypto_1.decryptGridsetEntry)(Buffer.from(raw), encryptedContentPassword);
469
440
  };
470
441
  // Parse FileMap.xml if present to index dynamic files per grid
471
442
  const fileMapIndex = new Map();
@@ -785,7 +756,7 @@ class GridsetProcessor extends baseProcessor_1.BaseProcessor {
785
756
  const gridEntryPath = entry.entryName.replace(/\\/g, '/');
786
757
  const baseDir = gridEntryPath.replace(/\/grid\.xml$/, '/');
787
758
  const dynamicFiles = fileMapIndex.get(gridEntryPath) || [];
788
- const resolvedImageEntry = (0, resolver_1.resolveGrid3CellImage)(zip, {
759
+ const resolvedImageEntry = (0, resolver_1.resolveGrid3CellImage)(null, {
789
760
  baseDir,
790
761
  imageName: declaredImageName,
791
762
  x: cellX + 1,
@@ -1636,11 +1607,36 @@ class GridsetProcessor extends baseProcessor_1.BaseProcessor {
1636
1607
  return (0, io_1.readBinaryFromInput)(outputPath);
1637
1608
  }
1638
1609
  async saveFromTree(tree, outputPath) {
1639
- const JSZip = await getJSZip();
1640
- const zip = new JSZip();
1610
+ const useNodeZip = (0, io_1.isNodeRuntime)();
1611
+ let addText;
1612
+ let addBinary;
1613
+ let finalizeZip;
1614
+ if (useNodeZip) {
1615
+ const AdmZip = (0, io_1.getNodeRequire)()('adm-zip');
1616
+ const zip = new AdmZip();
1617
+ addText = (entryPath, content) => {
1618
+ zip.addFile(entryPath, Buffer.from(content, 'utf8'));
1619
+ };
1620
+ addBinary = (entryPath, content) => {
1621
+ zip.addFile(entryPath, Buffer.from(content));
1622
+ };
1623
+ finalizeZip = () => Promise.resolve(zip.toBuffer());
1624
+ }
1625
+ else {
1626
+ const module = await Promise.resolve().then(() => __importStar(require('jszip')));
1627
+ const JSZip = module.default || module;
1628
+ const zip = new JSZip();
1629
+ addText = (entryPath, content) => {
1630
+ zip.file(entryPath, content, { binary: false });
1631
+ };
1632
+ addBinary = (entryPath, content) => {
1633
+ zip.file(entryPath, content);
1634
+ };
1635
+ finalizeZip = async () => zip.generateAsync({ type: 'uint8array' });
1636
+ }
1641
1637
  if (Object.keys(tree.pages).length === 0) {
1642
1638
  // Create empty zip for empty tree
1643
- const zipBuffer = await zip.generateAsync({ type: 'uint8array' });
1639
+ const zipBuffer = await finalizeZip();
1644
1640
  (0, io_1.writeBinaryToPath)(outputPath, zipBuffer);
1645
1641
  return;
1646
1642
  }
@@ -1713,7 +1709,7 @@ class GridsetProcessor extends baseProcessor_1.BaseProcessor {
1713
1709
  suppressEmptyNode: true,
1714
1710
  });
1715
1711
  const settingsXmlContent = settingsBuilder.build(settingsData);
1716
- zip.file('Settings0/settings.xml', settingsXmlContent, { binary: false });
1712
+ addText('Settings0/settings.xml', settingsXmlContent);
1717
1713
  // Create Settings0/Styles/style.xml if there are styles
1718
1714
  if (uniqueStyles.size > 0) {
1719
1715
  const stylesArray = Array.from(uniqueStyles.values()).map(({ id, style }) => {
@@ -1746,7 +1742,7 @@ class GridsetProcessor extends baseProcessor_1.BaseProcessor {
1746
1742
  indentBy: ' ',
1747
1743
  });
1748
1744
  const styleXmlContent = styleBuilder.build(styleData);
1749
- zip.file('Settings0/Styles/styles.xml', styleXmlContent, { binary: false });
1745
+ addText('Settings0/Styles/styles.xml', styleXmlContent);
1750
1746
  }
1751
1747
  // Collect grid file paths for FileMap.xml
1752
1748
  const gridFilePaths = [];
@@ -1887,14 +1883,14 @@ class GridsetProcessor extends baseProcessor_1.BaseProcessor {
1887
1883
  // Add to zip in Grids folder with proper Grid3 naming
1888
1884
  const gridPath = `Grids/${page.name || page.id}/grid.xml`;
1889
1885
  gridFilePaths.push(gridPath);
1890
- zip.file(gridPath, xmlContent, { binary: false });
1886
+ addText(gridPath, xmlContent);
1891
1887
  });
1892
1888
  // Write image files to ZIP
1893
1889
  buttonImages.forEach((imgData) => {
1894
1890
  if (imgData.imageData && imgData.imageData.length > 0) {
1895
1891
  // Create image path in the grid's directory
1896
1892
  const imagePath = `Grids/${imgData.pageName}/${imgData.x}-${imgData.y}-0-text-0.${imgData.ext}`;
1897
- zip.file(imagePath, imgData.imageData);
1893
+ addBinary(imagePath, imgData.imageData);
1898
1894
  }
1899
1895
  });
1900
1896
  // Create FileMap.xml to map all grid files with their dynamic image files
@@ -1933,9 +1929,9 @@ class GridsetProcessor extends baseProcessor_1.BaseProcessor {
1933
1929
  indentBy: ' ',
1934
1930
  });
1935
1931
  const fileMapXmlContent = fileMapBuilder.build(fileMapData);
1936
- zip.file('FileMap.xml', fileMapXmlContent, { binary: false });
1932
+ addText('FileMap.xml', fileMapXmlContent);
1937
1933
  // Write the zip file
1938
- const zipBuffer = await zip.generateAsync({ type: 'uint8array' });
1934
+ const zipBuffer = await finalizeZip();
1939
1935
  (0, io_1.writeBinaryToPath)(outputPath, zipBuffer);
1940
1936
  }
1941
1937
  // Helper method to calculate column definitions based on page layout
@@ -29,31 +29,7 @@ const treeStructure_1 = require("../core/treeStructure");
29
29
  const idGenerator_1 = require("../utilities/analytics/utils/idGenerator");
30
30
  const translationProcessor_1 = require("../utilities/translation/translationProcessor");
31
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
- }
32
+ const zip_1 = require("../utils/zip");
57
33
  const OBF_FORMAT_VERSION = 'open-board-0.1';
58
34
  /**
59
35
  * Map OBF hidden value to AAC standard visibility
@@ -91,10 +67,12 @@ class ObfProcessor extends baseProcessor_1.BaseProcessor {
91
67
  ].filter(Boolean);
92
68
  for (const imagePath of possiblePaths) {
93
69
  try {
94
- const file = this.zipFile.file(imagePath);
95
- if (file) {
96
- const buffer = await file.async('nodebuffer');
97
- return buffer;
70
+ const buffer = await this.zipFile.readFile(imagePath);
71
+ if (buffer) {
72
+ if (typeof Buffer !== 'undefined') {
73
+ return Buffer.from(buffer);
74
+ }
75
+ return null;
98
76
  }
99
77
  }
100
78
  catch (err) {
@@ -128,9 +106,8 @@ class ObfProcessor extends baseProcessor_1.BaseProcessor {
128
106
  ].filter(Boolean);
129
107
  for (const imagePath of possiblePaths) {
130
108
  try {
131
- const file = this.zipFile.file(imagePath);
132
- if (file) {
133
- const buffer = await file.async('uint8array');
109
+ const buffer = await this.zipFile.readFile(imagePath);
110
+ if (buffer) {
134
111
  const contentType = imageData.content_type ||
135
112
  this.getMimeTypeFromFilename(imagePath);
136
113
  const dataUrl = `data:${contentType};base64,${(0, io_1.encodeBase64)(buffer)}`;
@@ -433,36 +410,28 @@ class ObfProcessor extends baseProcessor_1.BaseProcessor {
433
410
  if (!isLikelyZip(filePathOrBuffer)) {
434
411
  throw new Error('Invalid OBF content: not JSON and not ZIP');
435
412
  }
436
- const JSZip = await getJSZipObf();
437
- let zip;
438
413
  try {
439
- const zipInput = (0, io_1.readBinaryFromInput)(filePathOrBuffer);
440
- zip = await JSZip.loadAsync(zipInput);
414
+ const zipResult = await (0, zip_1.openZipFromInput)(filePathOrBuffer);
415
+ this.zipFile = zipResult.zip;
441
416
  }
442
417
  catch (err) {
443
- console.error('[OBF] Error loading ZIP with JSZip:', err);
418
+ console.error('[OBF] Error loading ZIP:', err);
444
419
  throw err;
445
420
  }
446
421
  // Store the ZIP file reference for image extraction
447
- this.zipFile = zip;
448
422
  this.imageCache.clear(); // Clear cache for new file
449
423
  console.log('[OBF] Detected zip archive, extracting .obf files');
450
424
  // Collect all .obf entries
451
- const obfEntries = [];
452
- zip.forEach((relativePath, file) => {
453
- if (file.dir)
454
- return;
455
- if (relativePath.toLowerCase().endsWith('.obf')) {
456
- obfEntries.push({ name: relativePath, file });
457
- }
458
- });
425
+ const obfEntries = this.zipFile
426
+ .listFiles()
427
+ .filter((name) => name.toLowerCase().endsWith('.obf'));
459
428
  // Process each .obf entry
460
- for (const entry of obfEntries) {
429
+ for (const entryName of obfEntries) {
461
430
  try {
462
- const content = await entry.file.async('string');
463
- const boardData = tryParseObfJson(content);
431
+ const content = await this.zipFile.readFile(entryName);
432
+ const boardData = tryParseObfJson((0, io_1.decodeText)(content));
464
433
  if (boardData) {
465
- const page = await this.processBoard(boardData, entry.name);
434
+ const page = await this.processBoard(boardData, entryName);
466
435
  tree.addPage(page);
467
436
  // Set metadata if not already set (use first board as reference)
468
437
  if (!tree.metadata.format) {
@@ -479,11 +448,11 @@ class ObfProcessor extends baseProcessor_1.BaseProcessor {
479
448
  }
480
449
  }
481
450
  else {
482
- console.warn('[OBF] Skipped entry (not valid OBF JSON):', entry.name);
451
+ console.warn('[OBF] Skipped entry (not valid OBF JSON):', entryName);
483
452
  }
484
453
  }
485
454
  catch (err) {
486
- console.warn('[OBF] Error processing entry:', entry.name, err);
455
+ console.warn('[OBF] Error processing entry:', entryName, err);
487
456
  }
488
457
  }
489
458
  return tree;
@@ -609,16 +578,31 @@ class ObfProcessor extends baseProcessor_1.BaseProcessor {
609
578
  }
610
579
  else {
611
580
  // Save as OBZ (zip with multiple OBF files)
612
- const JSZip = await getJSZipObf();
613
- const zip = new JSZip();
614
- Object.values(tree.pages).forEach((page) => {
615
- const obfBoard = this.createObfBoardFromPage(page, 'Board', tree.metadata);
616
- const obfContent = JSON.stringify(obfBoard, null, 2);
617
- zip.file(`${page.id}.obf`, obfContent);
618
- });
619
- const zipBuffer = await zip.generateAsync({ type: 'uint8array' });
620
- const { writeBinaryToPath } = await Promise.resolve().then(() => __importStar(require('../utils/io')));
621
- writeBinaryToPath(outputPath, zipBuffer);
581
+ if ((0, io_1.isNodeRuntime)()) {
582
+ const AdmZip = (0, io_1.getNodeRequire)()('adm-zip');
583
+ const zip = new AdmZip();
584
+ Object.values(tree.pages).forEach((page) => {
585
+ const obfBoard = this.createObfBoardFromPage(page, 'Board', tree.metadata);
586
+ const obfContent = JSON.stringify(obfBoard, null, 2);
587
+ zip.addFile(`${page.id}.obf`, Buffer.from(obfContent, 'utf8'));
588
+ });
589
+ const zipBuffer = zip.toBuffer();
590
+ const { writeBinaryToPath } = await Promise.resolve().then(() => __importStar(require('../utils/io')));
591
+ writeBinaryToPath(outputPath, zipBuffer);
592
+ }
593
+ else {
594
+ const module = await Promise.resolve().then(() => __importStar(require('jszip')));
595
+ const JSZip = module.default || module;
596
+ const zip = new JSZip();
597
+ Object.values(tree.pages).forEach((page) => {
598
+ const obfBoard = this.createObfBoardFromPage(page, 'Board', tree.metadata);
599
+ const obfContent = JSON.stringify(obfBoard, null, 2);
600
+ zip.file(`${page.id}.obf`, obfContent);
601
+ });
602
+ const zipBuffer = await zip.generateAsync({ type: 'uint8array' });
603
+ const { writeBinaryToPath } = await Promise.resolve().then(() => __importStar(require('../utils/io')));
604
+ writeBinaryToPath(outputPath, zipBuffer);
605
+ }
622
606
  }
623
607
  }
624
608
  /**
@@ -1,18 +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.SnapProcessor = void 0;
7
4
  const baseProcessor_1 = require("../core/baseProcessor");
8
5
  const treeStructure_1 = require("../core/treeStructure");
9
6
  const idGenerator_1 = require("../utilities/analytics/utils/idGenerator");
10
- const better_sqlite3_1 = __importDefault(require("better-sqlite3"));
11
- const path_1 = __importDefault(require("path"));
12
- const fs_1 = __importDefault(require("fs"));
13
- const crypto_1 = __importDefault(require("crypto"));
14
- const os_1 = __importDefault(require("os"));
15
7
  const snapValidator_1 = require("../validation/snapValidator");
8
+ const io_1 = require("../utils/io");
9
+ const sqlite_1 = require("../utils/sqlite");
16
10
  /**
17
11
  * Map Snap Visible value to AAC standard visibility
18
12
  * Snap: 0 = hidden, 1 (or non-zero) = visible
@@ -56,22 +50,10 @@ class SnapProcessor extends baseProcessor_1.BaseProcessor {
56
50
  async loadIntoTree(filePathOrBuffer) {
57
51
  await Promise.resolve();
58
52
  const tree = new treeStructure_1.AACTree();
59
- let tempDir = null;
60
- const filePath = typeof filePathOrBuffer !== 'string'
61
- ? (() => {
62
- tempDir = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'snap-'));
63
- return path_1.default.join(tempDir, 'input.spb');
64
- })()
65
- : filePathOrBuffer;
66
- if (typeof filePathOrBuffer !== 'string') {
67
- const buffer = Buffer.isBuffer(filePathOrBuffer)
68
- ? filePathOrBuffer
69
- : Buffer.from(filePathOrBuffer);
70
- fs_1.default.writeFileSync(filePath, buffer);
71
- }
72
- let db = null;
53
+ let dbResult = null;
73
54
  try {
74
- db = new better_sqlite3_1.default(filePath, { readonly: true });
55
+ dbResult = await (0, sqlite_1.openSqliteDatabase)(filePathOrBuffer, { readonly: true });
56
+ const db = dbResult.db;
75
57
  const getTableColumns = (tableName) => {
76
58
  try {
77
59
  const rows = db.prepare(`PRAGMA table_info(${tableName})`).all();
@@ -338,10 +320,12 @@ class SnapProcessor extends baseProcessor_1.BaseProcessor {
338
320
  ${hasCommandSequence ? 'LEFT JOIN CommandSequence cs ON b.Id = cs.ButtonId' : ''}
339
321
  WHERE er.PageId = ? ${selectedPageLayoutId ? 'AND ep.PageLayoutId = ?' : ''}
340
322
  `;
341
- const queryParams = selectedPageLayoutId
342
- ? [pageRow.Id, selectedPageLayoutId]
343
- : [pageRow.Id];
344
- buttons = db.prepare(buttonQuery).all(...queryParams);
323
+ if (selectedPageLayoutId) {
324
+ buttons = db.prepare(buttonQuery).all(pageRow.Id, selectedPageLayoutId);
325
+ }
326
+ else {
327
+ buttons = db.prepare(buttonQuery).all(pageRow.Id);
328
+ }
345
329
  }
346
330
  catch (err) {
347
331
  const errorMessage = err instanceof Error ? err.message : String(err);
@@ -606,22 +590,18 @@ class SnapProcessor extends baseProcessor_1.BaseProcessor {
606
590
  }
607
591
  }
608
592
  finally {
609
- // Ensure database is closed
610
- if (db) {
611
- db.close();
593
+ if (dbResult?.cleanup) {
594
+ dbResult.cleanup();
612
595
  }
613
- // Clean up temporary file if created from buffer
614
- if (tempDir && fs_1.default.existsSync(tempDir)) {
615
- try {
616
- fs_1.default.rmSync(tempDir, { recursive: true, force: true });
617
- }
618
- catch (e) {
619
- console.warn('Failed to clean up temporary files:', e);
620
- }
596
+ else if (dbResult?.db) {
597
+ dbResult.db.close();
621
598
  }
622
599
  }
623
600
  }
624
601
  async processTexts(filePathOrBuffer, translations, outputPath) {
602
+ if (!(0, io_1.isNodeRuntime)()) {
603
+ throw new Error('processTexts is only supported in Node.js environments for Snap files.');
604
+ }
625
605
  // Load the tree, apply translations, and save to new file
626
606
  const tree = await this.loadIntoTree(filePathOrBuffer);
627
607
  // Apply translations to all text content
@@ -651,19 +631,26 @@ class SnapProcessor extends baseProcessor_1.BaseProcessor {
651
631
  });
652
632
  // Save the translated tree and return its content
653
633
  await this.saveFromTree(tree, outputPath);
654
- return fs_1.default.readFileSync(outputPath);
634
+ const fs = (0, io_1.getFs)();
635
+ return fs.readFileSync(outputPath);
655
636
  }
656
637
  async saveFromTree(tree, outputPath) {
638
+ if (!(0, io_1.isNodeRuntime)()) {
639
+ throw new Error('saveFromTree is only supported in Node.js environments for Snap files.');
640
+ }
657
641
  await Promise.resolve();
658
- const outputDir = path_1.default.dirname(outputPath);
659
- if (!fs_1.default.existsSync(outputDir)) {
660
- fs_1.default.mkdirSync(outputDir, { recursive: true });
642
+ const fs = (0, io_1.getFs)();
643
+ const path = (0, io_1.getPath)();
644
+ const outputDir = path.dirname(outputPath);
645
+ if (!fs.existsSync(outputDir)) {
646
+ fs.mkdirSync(outputDir, { recursive: true });
661
647
  }
662
- if (fs_1.default.existsSync(outputPath)) {
663
- fs_1.default.unlinkSync(outputPath);
648
+ if (fs.existsSync(outputPath)) {
649
+ fs.unlinkSync(outputPath);
664
650
  }
665
651
  // Create a new SQLite database for Snap format
666
- const db = new better_sqlite3_1.default(outputPath, { readonly: false });
652
+ const Database = (0, sqlite_1.requireBetterSqlite3)();
653
+ const db = new Database(outputPath, { readonly: false });
667
654
  try {
668
655
  // Create basic Snap database schema (simplified)
669
656
  db.exec(`
@@ -852,7 +839,12 @@ class SnapProcessor extends baseProcessor_1.BaseProcessor {
852
839
  */
853
840
  async addAudioToButton(dbPath, buttonId, audioData, metadata) {
854
841
  await Promise.resolve();
855
- const db = new better_sqlite3_1.default(dbPath, { fileMustExist: true });
842
+ if (!(0, io_1.isNodeRuntime)()) {
843
+ throw new Error('addAudioToButton is only supported in Node.js environments.');
844
+ }
845
+ const Database = (0, sqlite_1.requireBetterSqlite3)();
846
+ const crypto = (0, io_1.getNodeRequire)()('crypto');
847
+ const db = new Database(dbPath, { fileMustExist: true });
856
848
  try {
857
849
  // Ensure PageSetData table exists
858
850
  db.exec(`
@@ -863,7 +855,7 @@ class SnapProcessor extends baseProcessor_1.BaseProcessor {
863
855
  );
864
856
  `);
865
857
  // Generate SHA1 hash for the identifier
866
- const sha1Hash = crypto_1.default.createHash('sha1').update(audioData).digest('hex');
858
+ const sha1Hash = crypto.createHash('sha1').update(audioData).digest('hex');
867
859
  const identifier = `SND:${sha1Hash}`;
868
860
  // Check if audio with this identifier already exists
869
861
  let audioId;
@@ -894,8 +886,12 @@ class SnapProcessor extends baseProcessor_1.BaseProcessor {
894
886
  * Create a copy of the pageset with audio recordings added
895
887
  */
896
888
  async createAudioEnhancedPageset(sourceDbPath, targetDbPath, audioMappings) {
889
+ if (!(0, io_1.isNodeRuntime)()) {
890
+ throw new Error('createAudioEnhancedPageset is only supported in Node.js environments.');
891
+ }
892
+ const fs = (0, io_1.getFs)();
897
893
  // Copy the source database to target
898
- fs_1.default.copyFileSync(sourceDbPath, targetDbPath);
894
+ fs.copyFileSync(sourceDbPath, targetDbPath);
899
895
  // Add audio recordings to the copy
900
896
  for (const [buttonId, audioInfo] of audioMappings.entries()) {
901
897
  await this.addAudioToButton(targetDbPath, buttonId, audioInfo.audioData, audioInfo.metadata);
@@ -905,7 +901,11 @@ class SnapProcessor extends baseProcessor_1.BaseProcessor {
905
901
  * Extract buttons from a specific page that need audio recordings
906
902
  */
907
903
  extractButtonsForAudio(dbPath, pageUniqueId) {
908
- const db = new better_sqlite3_1.default(dbPath, { readonly: true });
904
+ if (!(0, io_1.isNodeRuntime)()) {
905
+ throw new Error('extractButtonsForAudio is only supported in Node.js environments.');
906
+ }
907
+ const Database = (0, sqlite_1.requireBetterSqlite3)();
908
+ const db = new Database(dbPath, { readonly: true });
909
909
  try {
910
910
  // Find the page by UniqueId
911
911
  const page = db.prepare('SELECT * FROM Page WHERE UniqueId = ?').get(pageUniqueId);
@@ -962,13 +962,19 @@ class SnapProcessor extends baseProcessor_1.BaseProcessor {
962
962
  * @returns Array of available PageLayouts with their dimensions
963
963
  */
964
964
  getAvailablePageLayouts(filePath) {
965
- const dbPath = typeof filePath === 'string' ? filePath : path_1.default.join(process.cwd(), 'temp.spb');
965
+ if (!(0, io_1.isNodeRuntime)()) {
966
+ throw new Error('getAvailablePageLayouts is only supported in Node.js environments.');
967
+ }
968
+ const fs = (0, io_1.getFs)();
969
+ const path = (0, io_1.getPath)();
970
+ const dbPath = typeof filePath === 'string' ? filePath : path.join(process.cwd(), 'temp.spb');
966
971
  if (Buffer.isBuffer(filePath)) {
967
- fs_1.default.writeFileSync(dbPath, filePath);
972
+ fs.writeFileSync(dbPath, filePath);
968
973
  }
969
974
  let db = null;
970
975
  try {
971
- db = new better_sqlite3_1.default(dbPath, { readonly: true });
976
+ const Database = (0, sqlite_1.requireBetterSqlite3)();
977
+ db = new Database(dbPath, { readonly: true });
972
978
  // Get unique PageLayouts based on PageLayoutSetting (dimensions)
973
979
  const pageLayouts = db
974
980
  .prepare(`
@@ -1014,9 +1020,9 @@ class SnapProcessor extends baseProcessor_1.BaseProcessor {
1014
1020
  db.close();
1015
1021
  }
1016
1022
  // Clean up temporary file if created from buffer
1017
- if (Buffer.isBuffer(filePath) && fs_1.default.existsSync(dbPath)) {
1023
+ if (Buffer.isBuffer(filePath) && fs.existsSync(dbPath)) {
1018
1024
  try {
1019
- fs_1.default.unlinkSync(dbPath);
1025
+ fs.unlinkSync(dbPath);
1020
1026
  }
1021
1027
  catch (e) {
1022
1028
  console.warn('Failed to clean up temporary file:', e);