@willwade/aac-processors 0.0.30 → 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.
Files changed (92) hide show
  1. package/README.md +52 -852
  2. package/dist/browser/core/baseProcessor.js +241 -0
  3. package/dist/browser/core/stringCasing.js +179 -0
  4. package/dist/browser/core/treeStructure.js +255 -0
  5. package/dist/browser/index.browser.js +73 -0
  6. package/dist/browser/processors/applePanelsProcessor.js +582 -0
  7. package/dist/browser/processors/astericsGridProcessor.js +1509 -0
  8. package/dist/browser/processors/dotProcessor.js +221 -0
  9. package/dist/browser/processors/gridset/commands.js +962 -0
  10. package/dist/browser/processors/gridset/crypto.js +53 -0
  11. package/dist/browser/processors/gridset/password.js +43 -0
  12. package/dist/browser/processors/gridset/pluginTypes.js +277 -0
  13. package/dist/browser/processors/gridset/resolver.js +137 -0
  14. package/dist/browser/processors/gridset/symbolAlignment.js +276 -0
  15. package/dist/browser/processors/gridset/symbols.js +421 -0
  16. package/dist/browser/processors/gridsetProcessor.js +2002 -0
  17. package/dist/browser/processors/obfProcessor.js +705 -0
  18. package/dist/browser/processors/opmlProcessor.js +274 -0
  19. package/dist/browser/types/aac.js +38 -0
  20. package/dist/browser/utilities/analytics/utils/idGenerator.js +89 -0
  21. package/dist/browser/utilities/translation/translationProcessor.js +200 -0
  22. package/dist/browser/utils/io.js +95 -0
  23. package/dist/browser/validation/baseValidator.js +156 -0
  24. package/dist/browser/validation/gridsetValidator.js +355 -0
  25. package/dist/browser/validation/obfValidator.js +500 -0
  26. package/dist/browser/validation/validationTypes.js +46 -0
  27. package/dist/cli/index.js +5 -5
  28. package/dist/core/analyze.d.ts +2 -2
  29. package/dist/core/analyze.js +2 -2
  30. package/dist/core/baseProcessor.d.ts +5 -4
  31. package/dist/core/baseProcessor.js +22 -27
  32. package/dist/core/treeStructure.d.ts +5 -5
  33. package/dist/core/treeStructure.js +1 -4
  34. package/dist/index.browser.d.ts +37 -0
  35. package/dist/index.browser.js +99 -0
  36. package/dist/index.d.ts +1 -48
  37. package/dist/index.js +1 -136
  38. package/dist/index.node.d.ts +48 -0
  39. package/dist/index.node.js +152 -0
  40. package/dist/processors/applePanelsProcessor.d.ts +5 -4
  41. package/dist/processors/applePanelsProcessor.js +58 -62
  42. package/dist/processors/astericsGridProcessor.d.ts +7 -6
  43. package/dist/processors/astericsGridProcessor.js +31 -42
  44. package/dist/processors/dotProcessor.d.ts +5 -4
  45. package/dist/processors/dotProcessor.js +25 -33
  46. package/dist/processors/excelProcessor.d.ts +4 -3
  47. package/dist/processors/excelProcessor.js +6 -3
  48. package/dist/processors/gridset/crypto.d.ts +18 -0
  49. package/dist/processors/gridset/crypto.js +57 -0
  50. package/dist/processors/gridset/helpers.d.ts +1 -1
  51. package/dist/processors/gridset/helpers.js +18 -8
  52. package/dist/processors/gridset/password.d.ts +20 -3
  53. package/dist/processors/gridset/password.js +17 -3
  54. package/dist/processors/gridset/wordlistHelpers.d.ts +3 -3
  55. package/dist/processors/gridset/wordlistHelpers.js +21 -20
  56. package/dist/processors/gridsetProcessor.d.ts +7 -12
  57. package/dist/processors/gridsetProcessor.js +116 -77
  58. package/dist/processors/obfProcessor.d.ts +9 -7
  59. package/dist/processors/obfProcessor.js +131 -56
  60. package/dist/processors/obfsetProcessor.d.ts +5 -4
  61. package/dist/processors/obfsetProcessor.js +10 -16
  62. package/dist/processors/opmlProcessor.d.ts +5 -4
  63. package/dist/processors/opmlProcessor.js +27 -34
  64. package/dist/processors/snapProcessor.d.ts +8 -7
  65. package/dist/processors/snapProcessor.js +15 -12
  66. package/dist/processors/touchchatProcessor.d.ts +8 -7
  67. package/dist/processors/touchchatProcessor.js +22 -17
  68. package/dist/types/aac.d.ts +0 -2
  69. package/dist/types/aac.js +2 -0
  70. package/dist/utils/io.d.ts +12 -0
  71. package/dist/utils/io.js +107 -0
  72. package/dist/validation/gridsetValidator.js +7 -7
  73. package/dist/validation/snapValidator.js +28 -35
  74. package/docs/BROWSER_USAGE.md +618 -0
  75. package/examples/README.md +77 -0
  76. package/examples/browser-test-server.js +81 -0
  77. package/examples/browser-test.html +331 -0
  78. package/examples/vitedemo/QUICKSTART.md +74 -0
  79. package/examples/vitedemo/README.md +157 -0
  80. package/examples/vitedemo/index.html +376 -0
  81. package/examples/vitedemo/package-lock.json +1221 -0
  82. package/examples/vitedemo/package.json +18 -0
  83. package/examples/vitedemo/src/main.ts +519 -0
  84. package/examples/vitedemo/test-files/example.dot +14 -0
  85. package/examples/vitedemo/test-files/example.grd +1 -0
  86. package/examples/vitedemo/test-files/example.gridset +0 -0
  87. package/examples/vitedemo/test-files/example.obz +0 -0
  88. package/examples/vitedemo/test-files/example.opml +18 -0
  89. package/examples/vitedemo/test-files/simple.obf +53 -0
  90. package/examples/vitedemo/tsconfig.json +24 -0
  91. package/examples/vitedemo/vite.config.ts +34 -0
  92. package/package.json +20 -4
@@ -1,6 +1,7 @@
1
1
  import { BaseProcessor, ProcessorOptions, ExtractStringsResult, TranslatedString, SourceString } from '../core/baseProcessor';
2
2
  import { AACTree } from '../core/treeStructure';
3
3
  import { ValidationResult } from '../validation/validationTypes';
4
+ import { ProcessorInput } from '../utils/io';
4
5
  declare class SnapProcessor extends BaseProcessor {
5
6
  private symbolResolver;
6
7
  private loadAudio;
@@ -9,21 +10,21 @@ declare class SnapProcessor extends BaseProcessor {
9
10
  loadAudio?: boolean;
10
11
  pageLayoutPreference?: 'largest' | 'smallest' | 'scanning' | number;
11
12
  });
12
- extractTexts(filePathOrBuffer: string | Buffer): string[];
13
- loadIntoTree(filePathOrBuffer: string | Buffer): AACTree;
14
- processTexts(filePathOrBuffer: string | Buffer, translations: Map<string, string>, outputPath: string): Buffer;
15
- saveFromTree(tree: AACTree, outputPath: string): void;
13
+ extractTexts(filePathOrBuffer: ProcessorInput): Promise<string[]>;
14
+ loadIntoTree(filePathOrBuffer: ProcessorInput): Promise<AACTree>;
15
+ processTexts(filePathOrBuffer: ProcessorInput, translations: Map<string, string>, outputPath: string): Promise<Uint8Array>;
16
+ saveFromTree(tree: AACTree, outputPath: string): Promise<void>;
16
17
  /**
17
18
  * Add audio recording to a button in the database
18
19
  */
19
- addAudioToButton(dbPath: string, buttonId: number, audioData: Buffer, metadata?: string): number;
20
+ addAudioToButton(dbPath: string, buttonId: number, audioData: Uint8Array, metadata?: string): Promise<number>;
20
21
  /**
21
22
  * Create a copy of the pageset with audio recordings added
22
23
  */
23
24
  createAudioEnhancedPageset(sourceDbPath: string, targetDbPath: string, audioMappings: Map<number, {
24
- audioData: Buffer;
25
+ audioData: Uint8Array;
25
26
  metadata?: string;
26
- }>): void;
27
+ }>): Promise<void>;
27
28
  /**
28
29
  * Extract buttons from a specific page that need audio recordings
29
30
  */
@@ -34,8 +34,8 @@ class SnapProcessor extends baseProcessor_1.BaseProcessor {
34
34
  this.pageLayoutPreference =
35
35
  options.pageLayoutPreference !== undefined ? options.pageLayoutPreference : 'scanning'; // Default to scanning
36
36
  }
37
- extractTexts(filePathOrBuffer) {
38
- const tree = this.loadIntoTree(filePathOrBuffer);
37
+ async extractTexts(filePathOrBuffer) {
38
+ const tree = await this.loadIntoTree(filePathOrBuffer);
39
39
  const texts = [];
40
40
  for (const pageId in tree.pages) {
41
41
  const page = tree.pages[pageId];
@@ -52,7 +52,8 @@ class SnapProcessor extends baseProcessor_1.BaseProcessor {
52
52
  }
53
53
  return texts;
54
54
  }
55
- loadIntoTree(filePathOrBuffer) {
55
+ async loadIntoTree(filePathOrBuffer) {
56
+ await Promise.resolve();
56
57
  const tree = new treeStructure_1.AACTree();
57
58
  const filePath = typeof filePathOrBuffer === 'string'
58
59
  ? filePathOrBuffer
@@ -612,9 +613,9 @@ class SnapProcessor extends baseProcessor_1.BaseProcessor {
612
613
  }
613
614
  }
614
615
  }
615
- processTexts(filePathOrBuffer, translations, outputPath) {
616
+ async processTexts(filePathOrBuffer, translations, outputPath) {
616
617
  // Load the tree, apply translations, and save to new file
617
- const tree = this.loadIntoTree(filePathOrBuffer);
618
+ const tree = await this.loadIntoTree(filePathOrBuffer);
618
619
  // Apply translations to all text content
619
620
  Object.values(tree.pages).forEach((page) => {
620
621
  // Translate page names
@@ -641,10 +642,11 @@ class SnapProcessor extends baseProcessor_1.BaseProcessor {
641
642
  });
642
643
  });
643
644
  // Save the translated tree and return its content
644
- this.saveFromTree(tree, outputPath);
645
+ await this.saveFromTree(tree, outputPath);
645
646
  return fs_1.default.readFileSync(outputPath);
646
647
  }
647
- saveFromTree(tree, outputPath) {
648
+ async saveFromTree(tree, outputPath) {
649
+ await Promise.resolve();
648
650
  const outputDir = path_1.default.dirname(outputPath);
649
651
  if (!fs_1.default.existsSync(outputDir)) {
650
652
  fs_1.default.mkdirSync(outputDir, { recursive: true });
@@ -840,7 +842,8 @@ class SnapProcessor extends baseProcessor_1.BaseProcessor {
840
842
  /**
841
843
  * Add audio recording to a button in the database
842
844
  */
843
- addAudioToButton(dbPath, buttonId, audioData, metadata) {
845
+ async addAudioToButton(dbPath, buttonId, audioData, metadata) {
846
+ await Promise.resolve();
844
847
  const db = new better_sqlite3_1.default(dbPath, { fileMustExist: true });
845
848
  try {
846
849
  // Ensure PageSetData table exists
@@ -882,13 +885,13 @@ class SnapProcessor extends baseProcessor_1.BaseProcessor {
882
885
  /**
883
886
  * Create a copy of the pageset with audio recordings added
884
887
  */
885
- createAudioEnhancedPageset(sourceDbPath, targetDbPath, audioMappings) {
888
+ async createAudioEnhancedPageset(sourceDbPath, targetDbPath, audioMappings) {
886
889
  // Copy the source database to target
887
890
  fs_1.default.copyFileSync(sourceDbPath, targetDbPath);
888
891
  // Add audio recordings to the copy
889
- audioMappings.forEach((audioInfo, buttonId) => {
890
- this.addAudioToButton(targetDbPath, buttonId, audioInfo.audioData, audioInfo.metadata);
891
- });
892
+ for (const [buttonId, audioInfo] of audioMappings.entries()) {
893
+ await this.addAudioToButton(targetDbPath, buttonId, audioInfo.audioData, audioInfo.metadata);
894
+ }
892
895
  }
893
896
  /**
894
897
  * Extract buttons from a specific page that need audio recordings
@@ -1,15 +1,16 @@
1
1
  import { BaseProcessor, ProcessorOptions, ExtractStringsResult, TranslatedString, SourceString } from '../core/baseProcessor';
2
2
  import { AACTree } from '../core/treeStructure';
3
3
  import { ValidationResult } from '../validation/validationTypes';
4
+ import { ProcessorInput } from '../utils/io';
4
5
  import { type ButtonForTranslation, type LLMLTranslationResult } from '../utilities/translation/translationProcessor';
5
6
  declare class TouchChatProcessor extends BaseProcessor {
6
7
  private tree;
7
8
  private sourceFile;
8
9
  constructor(options?: ProcessorOptions);
9
- extractTexts(filePathOrBuffer: string | Buffer): string[];
10
- loadIntoTree(filePathOrBuffer: string | Buffer): AACTree;
11
- processTexts(filePathOrBuffer: string | Buffer, translations: Map<string, string>, outputPath: string): Buffer;
12
- saveFromTree(tree: AACTree, outputPath: string): void;
10
+ extractTexts(filePathOrBuffer: ProcessorInput): Promise<string[]>;
11
+ loadIntoTree(filePathOrBuffer: ProcessorInput): Promise<AACTree>;
12
+ processTexts(filePathOrBuffer: ProcessorInput, translations: Map<string, string>, outputPath: string): Promise<Uint8Array>;
13
+ saveFromTree(tree: AACTree, outputPath: string): Promise<void>;
13
14
  /**
14
15
  * Alias method for aac-tools-platform compatibility
15
16
  * Extracts strings with TouchChat-specific metadata required for database storage
@@ -40,7 +41,7 @@ declare class TouchChatProcessor extends BaseProcessor {
40
41
  * @param filePathOrBuffer - Path to TouchChat .ce file or buffer
41
42
  * @returns Array of symbol information for LLM processing
42
43
  */
43
- extractSymbolsForLLM(filePathOrBuffer: string | Buffer): ButtonForTranslation[];
44
+ extractSymbolsForLLM(filePathOrBuffer: string | Buffer): Promise<ButtonForTranslation[]>;
44
45
  /**
45
46
  * Apply LLM translations with symbol information.
46
47
  * The LLM should provide translations with symbol attachments in the correct positions.
@@ -53,8 +54,8 @@ declare class TouchChatProcessor extends BaseProcessor {
53
54
  * @param options - Translation options (e.g., allowPartial for testing)
54
55
  * @returns Buffer of the translated TouchChat file
55
56
  */
56
- processLLMTranslations(filePathOrBuffer: string | Buffer, llmTranslations: LLMLTranslationResult[], outputPath: string, options?: {
57
+ processLLMTranslations(filePathOrBuffer: string | Uint8Array, llmTranslations: LLMLTranslationResult[], outputPath: string, options?: {
57
58
  allowPartial?: boolean;
58
- }): Buffer;
59
+ }): Promise<Uint8Array>;
59
60
  }
60
61
  export { TouchChatProcessor };
@@ -14,6 +14,7 @@ const path_1 = __importDefault(require("path"));
14
14
  const fs_1 = __importDefault(require("fs"));
15
15
  const os_1 = __importDefault(require("os"));
16
16
  const touchChatValidator_1 = require("../validation/touchChatValidator");
17
+ const io_1 = require("../utils/io");
17
18
  const translationProcessor_1 = require("../utilities/translation/translationProcessor");
18
19
  const toNumberOrUndefined = (value) => typeof value === 'number' ? value : undefined;
19
20
  const toStringOrUndefined = (value) => typeof value === 'string' && value.length > 0 ? value : undefined;
@@ -42,10 +43,10 @@ class TouchChatProcessor extends baseProcessor_1.BaseProcessor {
42
43
  this.tree = null;
43
44
  this.sourceFile = null;
44
45
  }
45
- extractTexts(filePathOrBuffer) {
46
+ async extractTexts(filePathOrBuffer) {
46
47
  // Extracts all button labels/texts from TouchChat .ce file
47
48
  if (!this.tree && filePathOrBuffer) {
48
- this.tree = this.loadIntoTree(filePathOrBuffer);
49
+ this.tree = await this.loadIntoTree(filePathOrBuffer);
49
50
  }
50
51
  if (!this.tree) {
51
52
  throw new Error('No tree available - call loadIntoTree first');
@@ -62,7 +63,8 @@ class TouchChatProcessor extends baseProcessor_1.BaseProcessor {
62
63
  }
63
64
  return texts;
64
65
  }
65
- loadIntoTree(filePathOrBuffer) {
66
+ async loadIntoTree(filePathOrBuffer) {
67
+ await Promise.resolve();
66
68
  // Unzip .ce file, extract the .c4v SQLite DB, and parse pages/buttons
67
69
  let tmpDir = null;
68
70
  let db = null;
@@ -71,7 +73,9 @@ class TouchChatProcessor extends baseProcessor_1.BaseProcessor {
71
73
  this.sourceFile = filePathOrBuffer;
72
74
  // Step 1: Unzip
73
75
  tmpDir = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'touchchat-'));
74
- const zip = new adm_zip_1.default(typeof filePathOrBuffer === 'string' ? filePathOrBuffer : Buffer.from(filePathOrBuffer));
76
+ const zipInput = (0, io_1.readBinaryFromInput)(filePathOrBuffer);
77
+ const zipBuffer = Buffer.isBuffer(zipInput) ? zipInput : Buffer.from(zipInput);
78
+ const zip = new adm_zip_1.default(zipBuffer);
75
79
  zip.extractAllTo(tmpDir, true);
76
80
  // Step 2: Find and open SQLite DB
77
81
  const files = fs_1.default.readdirSync(tmpDir);
@@ -485,9 +489,9 @@ class TouchChatProcessor extends baseProcessor_1.BaseProcessor {
485
489
  }
486
490
  }
487
491
  }
488
- processTexts(filePathOrBuffer, translations, outputPath) {
492
+ async processTexts(filePathOrBuffer, translations, outputPath) {
489
493
  // Load the tree, apply translations, and save to new file
490
- const tree = this.loadIntoTree(filePathOrBuffer);
494
+ const tree = await this.loadIntoTree(filePathOrBuffer);
491
495
  // Apply translations to all text content
492
496
  Object.values(tree.pages).forEach((page) => {
493
497
  // Translate page names
@@ -514,10 +518,11 @@ class TouchChatProcessor extends baseProcessor_1.BaseProcessor {
514
518
  });
515
519
  });
516
520
  // Save the translated tree and return its content
517
- this.saveFromTree(tree, outputPath);
521
+ await this.saveFromTree(tree, outputPath);
518
522
  return fs_1.default.readFileSync(outputPath);
519
523
  }
520
- saveFromTree(tree, outputPath) {
524
+ async saveFromTree(tree, outputPath) {
525
+ await Promise.resolve();
521
526
  // Create a TouchChat database that matches the expected schema for loading
522
527
  const tmpDir = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'touchchat-export-'));
523
528
  const dbPath = path_1.default.join(tmpDir, 'vocab.c4v');
@@ -829,9 +834,9 @@ class TouchChatProcessor extends baseProcessor_1.BaseProcessor {
829
834
  * @param filePath - Path to the TouchChat .ce file
830
835
  * @returns Promise with extracted strings and any errors
831
836
  */
832
- extractStringsWithMetadata(filePath) {
837
+ async extractStringsWithMetadata(filePath) {
833
838
  try {
834
- const tree = this.loadIntoTree(filePath);
839
+ const tree = await this.loadIntoTree(filePath);
835
840
  const extractedMap = new Map();
836
841
  // Process all pages and buttons with TouchChat-specific logic
837
842
  Object.values(tree.pages).forEach((page) => {
@@ -885,7 +890,7 @@ class TouchChatProcessor extends baseProcessor_1.BaseProcessor {
885
890
  * @param sourceStrings - Array of source string data with metadata
886
891
  * @returns Promise with path to the generated translated file
887
892
  */
888
- generateTranslatedDownload(filePath, translatedStrings, sourceStrings) {
893
+ async generateTranslatedDownload(filePath, translatedStrings, sourceStrings) {
889
894
  try {
890
895
  // Build translation map from the provided data
891
896
  const translations = new Map();
@@ -901,7 +906,7 @@ class TouchChatProcessor extends baseProcessor_1.BaseProcessor {
901
906
  // Generate output path for TouchChat files
902
907
  const outputPath = filePath.replace(/\.ce$/, '_translated.ce');
903
908
  // Use existing processTexts method
904
- this.processTexts(filePath, translations, outputPath);
909
+ await this.processTexts(filePath, translations, outputPath);
905
910
  return Promise.resolve(outputPath);
906
911
  }
907
912
  catch (error) {
@@ -925,8 +930,8 @@ class TouchChatProcessor extends baseProcessor_1.BaseProcessor {
925
930
  * @param filePathOrBuffer - Path to TouchChat .ce file or buffer
926
931
  * @returns Array of symbol information for LLM processing
927
932
  */
928
- extractSymbolsForLLM(filePathOrBuffer) {
929
- const tree = this.loadIntoTree(filePathOrBuffer);
933
+ async extractSymbolsForLLM(filePathOrBuffer) {
934
+ const tree = await this.loadIntoTree(filePathOrBuffer);
930
935
  // Collect all buttons from all pages
931
936
  const allButtons = [];
932
937
  Object.values(tree.pages).forEach((page) => {
@@ -955,8 +960,8 @@ class TouchChatProcessor extends baseProcessor_1.BaseProcessor {
955
960
  * @param options - Translation options (e.g., allowPartial for testing)
956
961
  * @returns Buffer of the translated TouchChat file
957
962
  */
958
- processLLMTranslations(filePathOrBuffer, llmTranslations, outputPath, options) {
959
- const tree = this.loadIntoTree(filePathOrBuffer);
963
+ async processLLMTranslations(filePathOrBuffer, llmTranslations, outputPath, options) {
964
+ const tree = await this.loadIntoTree(filePathOrBuffer);
960
965
  // Validate translations using shared utility
961
966
  const buttonIds = Object.values(tree.pages).flatMap((page) => page.buttons.map((b) => b.id));
962
967
  (0, translationProcessor_1.validateTranslationResults)(llmTranslations, buttonIds, options);
@@ -993,7 +998,7 @@ class TouchChatProcessor extends baseProcessor_1.BaseProcessor {
993
998
  });
994
999
  });
995
1000
  // Save and return
996
- this.saveFromTree(tree, outputPath);
1001
+ await this.saveFromTree(tree, outputPath);
997
1002
  return fs_1.default.readFileSync(outputPath);
998
1003
  }
999
1004
  }
@@ -1,4 +1,3 @@
1
- import { AACSemanticAction } from '../core/treeStructure';
2
1
  /**
3
2
  * Scanning selection methods for switch access
4
3
  * Determines how the scanning advances through items
@@ -76,7 +75,6 @@ export interface AACButton {
76
75
  id: string;
77
76
  label: string;
78
77
  message: string;
79
- semanticAction?: AACSemanticAction;
80
78
  targetPageId?: string;
81
79
  style?: AACStyle;
82
80
  audioRecording?: {
package/dist/types/aac.js CHANGED
@@ -1,4 +1,6 @@
1
1
  "use strict";
2
+ // Note: AACSemanticAction is defined in core/treeStructure.ts to avoid circular dependency
3
+ // Import it directly if needed: import { AACSemanticAction } from '../core/treeStructure';
2
4
  Object.defineProperty(exports, "__esModule", { value: true });
3
5
  exports.CellScanningOrder = exports.ScanningSelectionMethod = void 0;
4
6
  /**
@@ -0,0 +1,12 @@
1
+ export type ProcessorInput = string | Buffer | ArrayBuffer | Uint8Array;
2
+ export type BinaryOutput = Buffer | Uint8Array;
3
+ export declare function getFs(): typeof import('fs');
4
+ export declare function getPath(): typeof import('path');
5
+ export declare function getBasename(filePath: string): string;
6
+ export declare function decodeText(input: Uint8Array): string;
7
+ export declare function encodeBase64(input: Uint8Array): string;
8
+ export declare function encodeText(text: string): BinaryOutput;
9
+ export declare function readBinaryFromInput(input: ProcessorInput): Uint8Array;
10
+ export declare function readTextFromInput(input: ProcessorInput, encoding?: BufferEncoding): string;
11
+ export declare function writeBinaryToPath(outputPath: string, data: BinaryOutput): void;
12
+ export declare function writeTextToPath(outputPath: string, text: string): void;
@@ -0,0 +1,107 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getFs = getFs;
4
+ exports.getPath = getPath;
5
+ exports.getBasename = getBasename;
6
+ exports.decodeText = decodeText;
7
+ exports.encodeBase64 = encodeBase64;
8
+ exports.encodeText = encodeText;
9
+ exports.readBinaryFromInput = readBinaryFromInput;
10
+ exports.readTextFromInput = readTextFromInput;
11
+ exports.writeBinaryToPath = writeBinaryToPath;
12
+ exports.writeTextToPath = writeTextToPath;
13
+ let cachedFs = null;
14
+ let cachedPath = null;
15
+ function getFs() {
16
+ if (!cachedFs) {
17
+ try {
18
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
19
+ cachedFs = require('fs');
20
+ }
21
+ catch {
22
+ throw new Error('File system access is not available in this environment.');
23
+ }
24
+ }
25
+ if (!cachedFs) {
26
+ throw new Error('File system access is not available in this environment.');
27
+ }
28
+ return cachedFs;
29
+ }
30
+ function getPath() {
31
+ if (!cachedPath) {
32
+ try {
33
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
34
+ cachedPath = require('path');
35
+ }
36
+ catch {
37
+ throw new Error('Path utilities are not available in this environment.');
38
+ }
39
+ }
40
+ if (!cachedPath) {
41
+ throw new Error('Path utilities are not available in this environment.');
42
+ }
43
+ return cachedPath;
44
+ }
45
+ function getBasename(filePath) {
46
+ const parts = filePath.split(/[/\\]/);
47
+ return parts[parts.length - 1] || filePath;
48
+ }
49
+ function decodeText(input) {
50
+ if (typeof Buffer !== 'undefined' && Buffer.isBuffer(input)) {
51
+ return input.toString('utf8');
52
+ }
53
+ const decoder = new TextDecoder('utf-8');
54
+ return decoder.decode(input);
55
+ }
56
+ function encodeBase64(input) {
57
+ if (typeof Buffer !== 'undefined' && Buffer.isBuffer(input)) {
58
+ return input.toString('base64');
59
+ }
60
+ // Browser fallback using btoa
61
+ let binary = '';
62
+ const len = input.byteLength;
63
+ for (let i = 0; i < len; i++) {
64
+ binary += String.fromCharCode(input[i]);
65
+ }
66
+ return btoa(binary);
67
+ }
68
+ function encodeText(text) {
69
+ if (typeof Buffer !== 'undefined') {
70
+ return Buffer.from(text, 'utf8');
71
+ }
72
+ return new TextEncoder().encode(text);
73
+ }
74
+ function readBinaryFromInput(input) {
75
+ if (typeof input === 'string') {
76
+ const fs = getFs();
77
+ return fs.readFileSync(input);
78
+ }
79
+ if (typeof Buffer !== 'undefined' && Buffer.isBuffer(input)) {
80
+ return input;
81
+ }
82
+ if (input instanceof ArrayBuffer) {
83
+ return new Uint8Array(input);
84
+ }
85
+ return input;
86
+ }
87
+ function readTextFromInput(input, encoding = 'utf8') {
88
+ if (typeof input === 'string') {
89
+ const fs = getFs();
90
+ return fs.readFileSync(input, encoding);
91
+ }
92
+ if (typeof Buffer !== 'undefined' && Buffer.isBuffer(input)) {
93
+ return input.toString(encoding);
94
+ }
95
+ if (input instanceof ArrayBuffer) {
96
+ return decodeText(new Uint8Array(input));
97
+ }
98
+ return decodeText(input);
99
+ }
100
+ function writeBinaryToPath(outputPath, data) {
101
+ const fs = getFs();
102
+ fs.writeFileSync(outputPath, data);
103
+ }
104
+ function writeTextToPath(outputPath, text) {
105
+ const fs = getFs();
106
+ fs.writeFileSync(outputPath, text, 'utf8');
107
+ }
@@ -33,7 +33,7 @@ exports.GridsetValidator = void 0;
33
33
  const fs = __importStar(require("fs"));
34
34
  const path = __importStar(require("path"));
35
35
  const xml2js = __importStar(require("xml2js"));
36
- const adm_zip_1 = __importDefault(require("adm-zip"));
36
+ const jszip_1 = __importDefault(require("jszip"));
37
37
  const baseValidator_1 = require("./baseValidator");
38
38
  /**
39
39
  * Validator for Grid3/Smartbox Gridset files (.gridset, .gridsetx)
@@ -140,22 +140,22 @@ class GridsetValidator extends baseValidator_1.BaseValidator {
140
140
  async validateZipArchive(content, filename, _filesize) {
141
141
  let zip;
142
142
  try {
143
- zip = new adm_zip_1.default(Buffer.from(content));
143
+ zip = await jszip_1.default.loadAsync(Buffer.from(content));
144
144
  }
145
145
  catch (e) {
146
146
  this.err(`Failed to open ZIP archive: ${e.message}`, true);
147
147
  return;
148
148
  }
149
- const entries = zip.getEntries();
149
+ const entries = Object.values(zip.files).filter((entry) => !entry.dir);
150
150
  // Check for gridset.xml (required)
151
151
  await this.add_check('gridset_xml_presence', 'gridset.xml presence', async () => {
152
- const gridsetEntry = entries.find((e) => e.entryName.toLowerCase() === 'gridset.xml');
152
+ const gridsetEntry = entries.find((e) => e.name.toLowerCase() === 'gridset.xml');
153
153
  if (!gridsetEntry) {
154
154
  this.err('Missing gridset.xml in archive', true);
155
155
  }
156
156
  else {
157
157
  try {
158
- const gridsetXml = gridsetEntry.getData().toString('utf-8');
158
+ const gridsetXml = await gridsetEntry.async('string');
159
159
  const parser = new xml2js.Parser();
160
160
  const xmlObj = await parser.parseStringPromise(gridsetXml);
161
161
  const gridset = xmlObj.gridset || xmlObj.Gridset;
@@ -173,13 +173,13 @@ class GridsetValidator extends baseValidator_1.BaseValidator {
173
173
  });
174
174
  // Check for settings.xml (highly recommended/required for metadata)
175
175
  await this.add_check('settings_xml_presence', 'settings.xml presence', async () => {
176
- const settingsEntry = entries.find((e) => e.entryName.toLowerCase() === 'settings.xml');
176
+ const settingsEntry = entries.find((e) => e.name.toLowerCase() === 'settings.xml');
177
177
  if (!settingsEntry) {
178
178
  this.warn('Missing settings.xml in archive (required for full metadata)');
179
179
  }
180
180
  else {
181
181
  try {
182
- const settingsXml = settingsEntry.getData().toString('utf-8');
182
+ const settingsXml = await settingsEntry.async('string');
183
183
  const parser = new xml2js.Parser();
184
184
  const xmlObj = await parser.parseStringPromise(settingsXml);
185
185
  const settings = xmlObj.GridSetSettings || xmlObj.gridSetSettings || xmlObj.GridsetSettings;
@@ -32,7 +32,7 @@ exports.SnapValidator = void 0;
32
32
  const fs = __importStar(require("fs"));
33
33
  const path = __importStar(require("path"));
34
34
  const xml2js = __importStar(require("xml2js"));
35
- const adm_zip_1 = __importDefault(require("adm-zip"));
35
+ const jszip_1 = __importDefault(require("jszip"));
36
36
  const baseValidator_1 = require("./baseValidator");
37
37
  /**
38
38
  * Validator for Snap files (.spb, .sps)
@@ -62,10 +62,10 @@ class SnapValidator extends baseValidator_1.BaseValidator {
62
62
  }
63
63
  // Try to parse as ZIP and check for Snap structure
64
64
  try {
65
- const zip = new adm_zip_1.default(content);
66
- const entries = zip.getEntries();
67
- // Snap packages typically have settings.xml or similar
68
- return entries.some((e) => e.entryName.includes('settings') || e.entryName.includes('.xml'));
65
+ const buffer = Buffer.isBuffer(content) ? content : Buffer.from(content);
66
+ const zip = await jszip_1.default.loadAsync(buffer);
67
+ const entries = Object.values(zip.files).filter((entry) => !entry.dir);
68
+ return entries.some((entry) => entry.name.includes('settings') || entry.name.includes('.xml'));
69
69
  }
70
70
  catch {
71
71
  return false;
@@ -85,10 +85,9 @@ class SnapValidator extends baseValidator_1.BaseValidator {
85
85
  let validZip = false;
86
86
  await this.add_check('zip', 'valid zip package', async () => {
87
87
  try {
88
- // Ensure content is a Buffer for AdmZip
89
88
  const buffer = Buffer.isBuffer(content) ? content : Buffer.from(content);
90
- zip = new adm_zip_1.default(buffer);
91
- const entries = zip.getEntries();
89
+ zip = await jszip_1.default.loadAsync(buffer);
90
+ const entries = Object.values(zip.files);
92
91
  validZip = entries.length > 0;
93
92
  }
94
93
  catch (e) {
@@ -107,8 +106,8 @@ class SnapValidator extends baseValidator_1.BaseValidator {
107
106
  async validateSnapStructure(zip, _filename) {
108
107
  // Check for required files
109
108
  await this.add_check('required_files', 'required package files', async () => {
110
- const entries = zip.getEntries();
111
- const entryNames = entries.map((e) => e.entryName);
109
+ const entries = Object.values(zip.files);
110
+ const entryNames = entries.map((e) => e.name);
112
111
  // Look for common Snap files
113
112
  const hasSettings = entryNames.some((n) => n.toLowerCase().includes('settings'));
114
113
  const hasXml = entryNames.some((n) => n.toLowerCase().endsWith('.xml'));
@@ -120,14 +119,12 @@ class SnapValidator extends baseValidator_1.BaseValidator {
120
119
  }
121
120
  });
122
121
  // Try to parse and validate the main settings file
123
- const settingsEntry = zip
124
- .getEntries()
125
- .find((e) => e.entryName.toLowerCase().includes('settings'));
122
+ const settingsEntry = Object.values(zip.files).find((entry) => !entry.dir && entry.name.toLowerCase().includes('settings'));
126
123
  if (settingsEntry) {
127
- await this.validateSettingsFile(zip, settingsEntry);
124
+ await this.validateSettingsFile(settingsEntry);
128
125
  }
129
126
  // Check for pages
130
- const pageEntries = zip.getEntries().filter((e) => e.entryName.toLowerCase().includes('page'));
127
+ const pageEntries = Object.values(zip.files).filter((entry) => !entry.dir && entry.name.toLowerCase().includes('page'));
131
128
  await this.add_check('pages', 'pages in package', async () => {
132
129
  if (pageEntries.length === 0) {
133
130
  this.warn('Snap package should contain at least one page file');
@@ -136,21 +133,17 @@ class SnapValidator extends baseValidator_1.BaseValidator {
136
133
  // Validate a sample of pages
137
134
  const samplePages = pageEntries.slice(0, 5); // Limit to first 5 pages
138
135
  for (let i = 0; i < samplePages.length; i++) {
139
- await this.validatePageFile(zip, samplePages[i], i);
136
+ await this.validatePageFile(samplePages[i], i);
140
137
  }
141
138
  // Check for images
142
- const imageEntries = zip
143
- .getEntries()
144
- .filter((e) => e.entryName.toLowerCase().match(/\.(png|jpg|jpeg|gif|bmp)$/i));
139
+ const imageEntries = Object.values(zip.files).filter((entry) => !entry.dir && entry.name.toLowerCase().match(/\.(png|jpg|jpeg|gif|bmp)$/i));
145
140
  await this.add_check('images', 'image files', async () => {
146
141
  if (imageEntries.length === 0) {
147
142
  this.warn('Snap package should contain image files for buttons');
148
143
  }
149
144
  });
150
145
  // Check for audio files
151
- const audioEntries = zip
152
- .getEntries()
153
- .filter((e) => e.entryName.toLowerCase().match(/\.(wav|mp3|m4a|ogg)$/i));
146
+ const audioEntries = Object.values(zip.files).filter((entry) => !entry.dir && entry.name.toLowerCase().match(/\.(wav|mp3|m4a|ogg)$/i));
154
147
  await this.add_check('audio', 'audio files', async () => {
155
148
  // Audio files are optional, so just warn if missing
156
149
  if (audioEntries.length === 0) {
@@ -159,9 +152,9 @@ class SnapValidator extends baseValidator_1.BaseValidator {
159
152
  });
160
153
  // Check for unexpected files
161
154
  await this.add_check('unexpected_files', 'unexpected file types', async () => {
162
- const entries = zip.getEntries();
163
- const unexpectedFiles = entries.filter((e) => {
164
- const name = e.entryName.toLowerCase();
155
+ const entries = Object.values(zip.files).filter((entry) => !entry.dir);
156
+ const unexpectedFiles = entries.filter((entry) => {
157
+ const name = entry.name.toLowerCase();
165
158
  // Skip common system files and directories
166
159
  if (name.startsWith('__macosx') || name.startsWith('.ds_store')) {
167
160
  return false;
@@ -170,7 +163,7 @@ class SnapValidator extends baseValidator_1.BaseValidator {
170
163
  return !name.match(/\.(xml|png|jpg|jpeg|gif|bmp|wav|mp3|m4a|ogg|json)$/i);
171
164
  });
172
165
  if (unexpectedFiles.length > 0) {
173
- const unexpectedNames = unexpectedFiles.map((f) => f.entryName).slice(0, 5);
166
+ const unexpectedNames = unexpectedFiles.map((f) => f.name).slice(0, 5);
174
167
  this.warn(`Package contains unexpected file types: ${unexpectedNames.join(', ')}`);
175
168
  }
176
169
  });
@@ -178,10 +171,10 @@ class SnapValidator extends baseValidator_1.BaseValidator {
178
171
  /**
179
172
  * Validate the main settings file
180
173
  */
181
- async validateSettingsFile(zip, entry) {
174
+ async validateSettingsFile(entry) {
182
175
  await this.add_check('settings_format', 'settings file format', async () => {
183
176
  try {
184
- const content = zip.readAsText(entry.entryName);
177
+ const content = await entry.async('string');
185
178
  const parser = new xml2js.Parser();
186
179
  const xml = await parser.parseStringPromise(content);
187
180
  // Check for expected root element
@@ -206,30 +199,30 @@ class SnapValidator extends baseValidator_1.BaseValidator {
206
199
  /**
207
200
  * Validate a page file
208
201
  */
209
- async validatePageFile(zip, entry, index) {
210
- await this.add_check(`page[${index}]`, `page file ${index}: ${entry.entryName}`, async () => {
202
+ async validatePageFile(entry, index) {
203
+ await this.add_check(`page[${index}]`, `page file ${index}: ${entry.name}`, async () => {
211
204
  try {
212
- const content = zip.readAsText(entry.entryName);
205
+ const content = await entry.async('string');
213
206
  const parser = new xml2js.Parser();
214
207
  const xml = await parser.parseStringPromise(content);
215
208
  const page = xml.page || xml.Page;
216
209
  if (!page) {
217
- this.err(`Page file ${entry.entryName} does not contain a page element`);
210
+ this.err(`Page file ${entry.name} does not contain a page element`);
218
211
  return;
219
212
  }
220
213
  // Check page attributes
221
214
  const pageId = page.$?.id || page.$?.Id;
222
215
  if (!pageId) {
223
- this.warn(`Page ${entry.entryName} is missing an id attribute`);
216
+ this.warn(`Page ${entry.name} is missing an id attribute`);
224
217
  }
225
218
  // Check for cells/buttons
226
219
  const cells = page.cells || page.Cells || page.button || page.Button;
227
220
  if (!cells || (Array.isArray(cells) && cells.length === 0)) {
228
- this.warn(`Page ${entry.entryName} has no cells or buttons`);
221
+ this.warn(`Page ${entry.name} has no cells or buttons`);
229
222
  }
230
223
  }
231
224
  catch (e) {
232
- this.err(`Failed to parse page file ${entry.entryName}: ${e.message}`);
225
+ this.err(`Failed to parse page file ${entry.name}: ${e.message}`);
233
226
  }
234
227
  });
235
228
  }