@willwade/aac-processors 0.1.20 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (113) hide show
  1. package/dist/browser/core/baseProcessor.js +4 -0
  2. package/dist/browser/processors/applePanelsProcessor.js +33 -40
  3. package/dist/browser/processors/astericsGridProcessor.js +31 -26
  4. package/dist/browser/processors/dotProcessor.js +11 -12
  5. package/dist/browser/processors/gridset/colorUtils.js +354 -0
  6. package/dist/browser/processors/gridset/helpers.js +60 -53
  7. package/dist/browser/processors/gridset/index.js +61 -0
  8. package/dist/browser/processors/gridset/styleHelpers.js +205 -0
  9. package/dist/browser/processors/gridset/symbolExtractor.js +331 -0
  10. package/dist/browser/processors/gridset/symbolSearch.js +248 -0
  11. package/dist/browser/processors/gridset/symbols.js +39 -72
  12. package/dist/browser/processors/gridsetProcessor.js +39 -48
  13. package/dist/browser/processors/obfProcessor.js +39 -53
  14. package/dist/browser/processors/opmlProcessor.js +11 -12
  15. package/dist/browser/processors/snap/helpers.js +57 -49
  16. package/dist/browser/processors/snapProcessor.js +48 -51
  17. package/dist/browser/processors/touchchatProcessor.js +60 -52
  18. package/dist/browser/utilities/analytics/history.js +24 -18
  19. package/dist/browser/utilities/analytics/metrics/comparison.js +16 -16
  20. package/dist/browser/utilities/analytics/metrics/vocabulary.js +2 -2
  21. package/dist/browser/utilities/analytics/reference/browser.js +16 -16
  22. package/dist/browser/utilities/analytics/reference/index.js +44 -35
  23. package/dist/browser/utils/io.js +78 -21
  24. package/dist/browser/utils/sqlite.js +8 -10
  25. package/dist/browser/utils/zip.js +43 -43
  26. package/dist/browser/validation/baseValidator.js +5 -0
  27. package/dist/browser/validation/gridsetValidator.js +12 -20
  28. package/dist/browser/validation/obfValidator.js +6 -5
  29. package/dist/browser/validation/snapValidator.js +11 -7
  30. package/dist/browser/validation/touchChatValidator.js +23 -13
  31. package/dist/cli/index.js +22 -24
  32. package/dist/core/baseProcessor.d.ts +7 -7
  33. package/dist/core/baseProcessor.js +4 -0
  34. package/dist/processors/applePanelsProcessor.js +32 -39
  35. package/dist/processors/astericsGridProcessor.d.ts +4 -4
  36. package/dist/processors/astericsGridProcessor.js +30 -25
  37. package/dist/processors/dotProcessor.js +10 -11
  38. package/dist/processors/excelProcessor.d.ts +3 -3
  39. package/dist/processors/excelProcessor.js +14 -20
  40. package/dist/processors/gridset/helpers.d.ts +12 -14
  41. package/dist/processors/gridset/helpers.js +60 -79
  42. package/dist/processors/gridset/imageDebug.d.ts +3 -5
  43. package/dist/processors/gridset/imageDebug.js +4 -4
  44. package/dist/processors/gridset/password.d.ts +1 -1
  45. package/dist/processors/gridset/symbolExtractor.d.ts +5 -3
  46. package/dist/processors/gridset/symbolExtractor.js +15 -38
  47. package/dist/processors/gridset/symbolSearch.d.ts +11 -10
  48. package/dist/processors/gridset/symbolSearch.js +29 -51
  49. package/dist/processors/gridset/symbols.d.ts +8 -6
  50. package/dist/processors/gridset/symbols.js +38 -71
  51. package/dist/processors/gridset/wordlistHelpers.d.ts +4 -6
  52. package/dist/processors/gridset/wordlistHelpers.js +15 -74
  53. package/dist/processors/gridsetProcessor.d.ts +2 -2
  54. package/dist/processors/gridsetProcessor.js +38 -70
  55. package/dist/processors/obfProcessor.d.ts +2 -2
  56. package/dist/processors/obfProcessor.js +38 -75
  57. package/dist/processors/obfsetProcessor.js +2 -3
  58. package/dist/processors/opmlProcessor.js +10 -11
  59. package/dist/processors/snap/helpers.d.ts +9 -9
  60. package/dist/processors/snap/helpers.js +58 -76
  61. package/dist/processors/snapProcessor.d.ts +2 -2
  62. package/dist/processors/snapProcessor.js +47 -50
  63. package/dist/processors/touchchatProcessor.d.ts +2 -2
  64. package/dist/processors/touchchatProcessor.js +59 -51
  65. package/dist/types/aac.d.ts +2 -2
  66. package/dist/utilities/analytics/history.d.ts +8 -8
  67. package/dist/utilities/analytics/history.js +24 -18
  68. package/dist/utilities/analytics/index.d.ts +3 -2
  69. package/dist/utilities/analytics/index.js +9 -10
  70. package/dist/utilities/analytics/metrics/comparison.d.ts +1 -1
  71. package/dist/utilities/analytics/metrics/comparison.js +16 -16
  72. package/dist/utilities/analytics/metrics/vocabulary.d.ts +1 -1
  73. package/dist/utilities/analytics/metrics/vocabulary.js +2 -2
  74. package/dist/utilities/analytics/reference/browser.d.ts +9 -9
  75. package/dist/utilities/analytics/reference/browser.js +16 -16
  76. package/dist/utilities/analytics/reference/index.d.ts +25 -23
  77. package/dist/utilities/analytics/reference/index.js +43 -34
  78. package/dist/utilities/symbolTools.d.ts +8 -6
  79. package/dist/utilities/symbolTools.js +21 -18
  80. package/dist/utils/io.d.ts +24 -6
  81. package/dist/utils/io.js +79 -25
  82. package/dist/utils/sqlite.d.ts +3 -1
  83. package/dist/utils/sqlite.js +7 -9
  84. package/dist/utils/zip.d.ts +7 -3
  85. package/dist/utils/zip.js +43 -43
  86. package/dist/validation/applePanelsValidator.d.ts +2 -1
  87. package/dist/validation/applePanelsValidator.js +10 -11
  88. package/dist/validation/astericsValidator.d.ts +2 -1
  89. package/dist/validation/astericsValidator.js +5 -4
  90. package/dist/validation/baseValidator.d.ts +2 -2
  91. package/dist/validation/baseValidator.js +5 -0
  92. package/dist/validation/dotValidator.d.ts +2 -1
  93. package/dist/validation/dotValidator.js +5 -4
  94. package/dist/validation/excelValidator.d.ts +2 -1
  95. package/dist/validation/excelValidator.js +5 -4
  96. package/dist/validation/gridsetValidator.d.ts +2 -1
  97. package/dist/validation/gridsetValidator.js +11 -22
  98. package/dist/validation/index.d.ts +2 -2
  99. package/dist/validation/index.js +5 -4
  100. package/dist/validation/obfValidator.d.ts +2 -1
  101. package/dist/validation/obfValidator.js +5 -4
  102. package/dist/validation/obfsetValidator.d.ts +2 -1
  103. package/dist/validation/obfsetValidator.js +5 -4
  104. package/dist/validation/opmlValidator.d.ts +2 -1
  105. package/dist/validation/opmlValidator.js +5 -4
  106. package/dist/validation/snapValidator.d.ts +2 -1
  107. package/dist/validation/snapValidator.js +10 -6
  108. package/dist/validation/touchChatValidator.d.ts +4 -6
  109. package/dist/validation/touchChatValidator.js +22 -12
  110. package/dist/validation/validationTypes.d.ts +8 -1
  111. package/package.json +1 -1
  112. package/dist/core/fileProcessor.d.ts +0 -7
  113. package/dist/core/fileProcessor.js +0 -57
@@ -0,0 +1,248 @@
1
+ /**
2
+ * Grid 3 Symbol Search Implementation
3
+ *
4
+ * The .pix files are simple text mappings:
5
+ * searchTerm=symbolFilename=searchTerm
6
+ *
7
+ * Example:
8
+ * above bw=above bw.png=above bw
9
+ * active family=active family.png=active family
10
+ */
11
+ import { defaultFileAdapter } from '../../utils/io';
12
+ /**
13
+ * Parse a .pix file into search index
14
+ * @param pixFilePath - Path to .pix file
15
+ * @returns Search index
16
+ */
17
+ export async function parsePixFile(pixFilePath, fileAdapter = defaultFileAdapter) {
18
+ const { readTextFromInput, basename } = fileAdapter;
19
+ const content = await readTextFromInput(pixFilePath);
20
+ const library = basename(pixFilePath, '.pix');
21
+ const searchTerms = new Map();
22
+ const filenames = new Map();
23
+ const lines = content.split('\n');
24
+ for (const line of lines) {
25
+ const trimmed = line.trim();
26
+ if (!trimmed || trimmed.startsWith('encoding=')) {
27
+ continue;
28
+ }
29
+ // Format: searchTerm=symbolFilename=searchTerm
30
+ const parts = trimmed.split('=');
31
+ if (parts.length >= 3) {
32
+ const searchTerm = parts[0];
33
+ const symbolFilename = parts[1];
34
+ const displayName = parts[2];
35
+ searchTerms.set(searchTerm.toLowerCase(), symbolFilename);
36
+ filenames.set(symbolFilename, displayName || searchTerm);
37
+ }
38
+ }
39
+ return { library, searchTerms, filenames };
40
+ }
41
+ /**
42
+ * Load search indexes for all available libraries
43
+ * @param options - Search options
44
+ * @returns Map of library name to search index
45
+ */
46
+ export async function loadSearchIndexes(options = {}, fileAdapter = defaultFileAdapter) {
47
+ const { listDir, pathExists, join, basename } = fileAdapter;
48
+ const { grid3Path, locale = 'en-GB', libraries: specifiedLibs } = options;
49
+ if (!grid3Path) {
50
+ throw new Error('grid3Path is required for symbol search');
51
+ }
52
+ const searchIndexesDir = join(grid3Path, 'Locale', locale, 'symbolsearch');
53
+ if (!(await pathExists(searchIndexesDir))) {
54
+ throw new Error(`Symbol search directory not found: ${searchIndexesDir}`);
55
+ }
56
+ const indexes = new Map();
57
+ const files = await listDir(searchIndexesDir);
58
+ for (const file of files) {
59
+ if (!file.endsWith('.pix')) {
60
+ continue;
61
+ }
62
+ const libraryName = basename(file, '.pix');
63
+ // Filter libraries if specified
64
+ if (specifiedLibs && specifiedLibs.length > 0) {
65
+ if (!specifiedLibs.some((lib) => lib.toLowerCase() === libraryName.toLowerCase())) {
66
+ continue;
67
+ }
68
+ }
69
+ try {
70
+ const pixFilePath = join(searchIndexesDir, file);
71
+ const index = await parsePixFile(pixFilePath);
72
+ indexes.set(libraryName, index);
73
+ }
74
+ catch (error) {
75
+ console.warn(`Failed to load index for ${libraryName}:`, error);
76
+ }
77
+ }
78
+ return indexes;
79
+ }
80
+ /**
81
+ * Search for symbols by term
82
+ * @param searchTerm - Term to search for
83
+ * @param options - Search options
84
+ * @returns Array of search results
85
+ */
86
+ export async function searchSymbols(searchTerm, options = {}) {
87
+ const indexes = await loadSearchIndexes(options);
88
+ const results = [];
89
+ const lowerSearchTerm = searchTerm.toLowerCase().trim();
90
+ const limit = options.limit || 100;
91
+ for (const [libraryName, index] of indexes.entries()) {
92
+ // Exact match first
93
+ if (index.searchTerms.has(lowerSearchTerm)) {
94
+ const symbolFilename = index.searchTerms.get(lowerSearchTerm);
95
+ if (symbolFilename) {
96
+ results.push({
97
+ searchTerm: lowerSearchTerm,
98
+ symbolFilename,
99
+ displayName: index.filenames.get(symbolFilename) || lowerSearchTerm,
100
+ library: libraryName,
101
+ exactMatch: true,
102
+ });
103
+ }
104
+ }
105
+ // Fuzzy match if enabled
106
+ if (options.fuzzyMatch !== false) {
107
+ for (const [term, symbolFilename] of index.searchTerms.entries()) {
108
+ if (term.includes(lowerSearchTerm) || lowerSearchTerm.includes(term)) {
109
+ // Skip if already added as exact match
110
+ if (results.some((r) => r.library === libraryName && r.symbolFilename === symbolFilename)) {
111
+ continue;
112
+ }
113
+ results.push({
114
+ searchTerm: lowerSearchTerm,
115
+ symbolFilename,
116
+ displayName: index.filenames.get(symbolFilename) || term,
117
+ library: libraryName,
118
+ exactMatch: false,
119
+ });
120
+ }
121
+ }
122
+ }
123
+ }
124
+ // Sort by exact match first, then by library
125
+ results.sort((a, b) => {
126
+ if (a.exactMatch !== b.exactMatch) {
127
+ return a.exactMatch ? -1 : 1;
128
+ }
129
+ return a.library.localeCompare(b.library);
130
+ });
131
+ return results.slice(0, limit);
132
+ }
133
+ /**
134
+ * Get symbol filename for a specific search term
135
+ * @param searchTerm - Search term to look up
136
+ * @param library - Library name
137
+ * @param options - Search options
138
+ * @returns Symbol filename or undefined
139
+ */
140
+ export async function getSymbolFilename(searchTerm, library, options = {}) {
141
+ const indexes = await loadSearchIndexes({
142
+ ...options,
143
+ libraries: [library],
144
+ });
145
+ const index = indexes.get(library.toLowerCase());
146
+ if (!index) {
147
+ return undefined;
148
+ }
149
+ return index.searchTerms.get(searchTerm.toLowerCase());
150
+ }
151
+ /**
152
+ * Get display name for a symbol filename
153
+ * @param symbolFilename - Symbol filename (e.g., "above bw.png")
154
+ * @param library - Library name
155
+ * @param options - Search options
156
+ * @returns Display name or undefined
157
+ */
158
+ export async function getSymbolDisplayName(symbolFilename, library, options = {}) {
159
+ const indexes = await loadSearchIndexes({
160
+ ...options,
161
+ libraries: [library],
162
+ });
163
+ const index = indexes.get(library.toLowerCase());
164
+ if (!index) {
165
+ return undefined;
166
+ }
167
+ return index.filenames.get(symbolFilename);
168
+ }
169
+ /**
170
+ * Get all search terms for a library
171
+ * @param library - Library name
172
+ * @param options - Search options
173
+ * @returns Array of search terms
174
+ */
175
+ export async function getAllSearchTerms(library, options = {}) {
176
+ const indexes = await loadSearchIndexes({
177
+ ...options,
178
+ libraries: [library],
179
+ });
180
+ const index = indexes.get(library.toLowerCase());
181
+ if (!index) {
182
+ return [];
183
+ }
184
+ return Array.from(index.searchTerms.keys());
185
+ }
186
+ /**
187
+ * Search suggestions (autocomplete)
188
+ * @param partialTerm - Partial search term
189
+ * @param options - Search options
190
+ * @returns Array of suggested terms
191
+ */
192
+ export async function getSearchSuggestions(partialTerm, options = {}) {
193
+ const indexes = await loadSearchIndexes(options);
194
+ const suggestions = new Set();
195
+ const lowerPartial = partialTerm.toLowerCase().trim();
196
+ for (const index of indexes.values()) {
197
+ for (const term of index.searchTerms.keys()) {
198
+ if (term.startsWith(lowerPartial)) {
199
+ suggestions.add(term);
200
+ }
201
+ }
202
+ }
203
+ return Array.from(suggestions).sort().slice(0, 20);
204
+ }
205
+ /**
206
+ * Search for symbols and return results with library references
207
+ * @param searchTerm - Term to search for
208
+ * @param options - Search options
209
+ * @returns Array of full symbol references
210
+ */
211
+ export async function searchSymbolsWithReferences(searchTerm, options = {}) {
212
+ const results = await searchSymbols(searchTerm, options);
213
+ return results.map((r) => `[${r.library}]${r.symbolFilename}`);
214
+ }
215
+ /**
216
+ * Count symbols in each library
217
+ * @param options - Search options
218
+ * @returns Map of library name to symbol count
219
+ */
220
+ export async function countLibrarySymbols(options = {}) {
221
+ const indexes = await loadSearchIndexes(options);
222
+ const counts = new Map();
223
+ for (const [libraryName, index] of indexes.entries()) {
224
+ counts.set(libraryName, index.searchTerms.size);
225
+ }
226
+ return counts;
227
+ }
228
+ /**
229
+ * Get symbol search statistics
230
+ * @param options - Search options
231
+ * @returns Statistics about available symbols
232
+ */
233
+ export async function getSymbolSearchStats(options = {}) {
234
+ const indexes = await loadSearchIndexes(options);
235
+ const stats = {
236
+ totalLibraries: indexes.size,
237
+ totalSymbols: 0,
238
+ libraries: {},
239
+ };
240
+ for (const [libraryName, index] of indexes.entries()) {
241
+ stats.totalSymbols += index.searchTerms.size;
242
+ stats.libraries[libraryName] = {
243
+ symbolCount: index.searchTerms.size,
244
+ exampleTerms: Array.from(index.searchTerms.keys()).slice(0, 10),
245
+ };
246
+ }
247
+ return stats;
248
+ }
@@ -12,7 +12,8 @@
12
12
  *
13
13
  * This module provides symbol resolution and metadata extraction.
14
14
  */
15
- import { getFs, getNodeRequire, getPath } from '../../utils/io';
15
+ import { defaultFileAdapter } from '../../utils/io';
16
+ import { getZipAdapter } from '../../utils/zip';
16
17
  /**
17
18
  * Default Grid 3 installation paths by platform
18
19
  */
@@ -55,38 +56,6 @@ export const SYMBOL_LIBRARIES = {
55
56
  * Default locale to use
56
57
  */
57
58
  export const DEFAULT_LOCALE = 'en-GB';
58
- function getNodeFs() {
59
- try {
60
- return getFs();
61
- }
62
- catch {
63
- throw new Error('Symbol library access is not available in this environment.');
64
- }
65
- }
66
- function getNodePath() {
67
- try {
68
- return getPath();
69
- }
70
- catch {
71
- throw new Error('Path utilities are not available in this environment.');
72
- }
73
- }
74
- let cachedAdmZip = null;
75
- function getAdmZip() {
76
- if (cachedAdmZip)
77
- return cachedAdmZip;
78
- try {
79
- const nodeRequire = getNodeRequire();
80
- // eslint-disable-next-line @typescript-eslint/no-var-requires
81
- const module = nodeRequire('adm-zip');
82
- const resolved = module.default || module;
83
- cachedAdmZip = resolved;
84
- return resolved;
85
- }
86
- catch {
87
- throw new Error('Symbol library access requires AdmZip in this environment.');
88
- }
89
- }
90
59
  /**
91
60
  * Parse a symbol reference string
92
61
  * @param reference - Symbol reference like "[widgit]/food/apple.png"
@@ -124,12 +93,12 @@ export function isSymbolReference(reference) {
124
93
  * Get the default Grid 3 installation path for the current platform
125
94
  * @returns Default Grid 3 path or empty string if not found
126
95
  */
127
- export function getDefaultGrid3Path() {
96
+ export async function getDefaultGrid3Path(fileAdapter) {
97
+ const { pathExists } = fileAdapter ?? defaultFileAdapter;
128
98
  const platform = (typeof process !== 'undefined' && process.platform ? process.platform : 'unknown');
129
99
  const defaultPath = DEFAULT_GRID3_PATHS[platform] || '';
130
100
  try {
131
- const fs = getNodeFs();
132
- if (defaultPath && fs.existsSync(defaultPath)) {
101
+ if (defaultPath && (await pathExists(defaultPath))) {
133
102
  return defaultPath;
134
103
  }
135
104
  // Try to find Grid 3 in common locations
@@ -141,7 +110,7 @@ export function getDefaultGrid3Path() {
141
110
  '/opt/smartbox/grid3',
142
111
  ];
143
112
  for (const testPath of commonPaths) {
144
- if (fs.existsSync(testPath)) {
113
+ if (await pathExists(testPath)) {
145
114
  return testPath;
146
115
  }
147
116
  }
@@ -157,9 +126,9 @@ export function getDefaultGrid3Path() {
157
126
  * @param grid3Path - Grid 3 installation path
158
127
  * @returns Path to Symbol Libraries directory (e.g., "C:\...\Grid 3\Resources\Symbols")
159
128
  */
160
- export function getSymbolLibrariesDir(grid3Path) {
161
- const path = getNodePath();
162
- return path.join(grid3Path, SYMBOLS_SUBDIR);
129
+ export function getSymbolLibrariesDir(grid3Path, fileAdapter = defaultFileAdapter) {
130
+ const { join } = fileAdapter;
131
+ return join(grid3Path, SYMBOLS_SUBDIR);
163
132
  }
164
133
  /**
165
134
  * Get the symbol search indexes directory path for a given locale
@@ -168,38 +137,37 @@ export function getSymbolLibrariesDir(grid3Path) {
168
137
  * @param locale - Locale code (e.g., 'en-GB')
169
138
  * @returns Path to symbol search indexes directory (e.g., "C:\...\Grid 3\Locale\en-GB\symbolsearch")
170
139
  */
171
- export function getSymbolSearchIndexesDir(grid3Path, locale = DEFAULT_LOCALE) {
172
- const path = getNodePath();
173
- return path.join(grid3Path, SYMBOLSEARCH_SUBDIR, locale, 'symbolsearch');
140
+ export function getSymbolSearchIndexesDir(grid3Path, locale = DEFAULT_LOCALE, fileAdapter = defaultFileAdapter) {
141
+ const { join } = fileAdapter;
142
+ return join(grid3Path, SYMBOLSEARCH_SUBDIR, locale, 'symbolsearch');
174
143
  }
175
144
  /**
176
145
  * Get all available symbol libraries in the Grid 3 installation
177
146
  * @param options - Resolution options
178
147
  * @returns Array of symbol library information
179
148
  */
180
- export function getAvailableSymbolLibraries(options = {}) {
181
- const grid3Path = options.grid3Path || options.symbolDir || getDefaultGrid3Path();
149
+ export async function getAvailableSymbolLibraries(options = {}, fileAdapter) {
150
+ const { pathExists, getFileSize, listDir, join, basename } = fileAdapter ?? defaultFileAdapter;
151
+ const grid3Path = options.grid3Path || options.symbolDir || (await getDefaultGrid3Path());
182
152
  if (!grid3Path) {
183
153
  return [];
184
154
  }
185
- const symbolsDir = getSymbolLibrariesDir(grid3Path);
186
- const fs = getNodeFs();
187
- if (!fs.existsSync(symbolsDir)) {
155
+ const symbolsDir = getSymbolLibrariesDir(grid3Path, fileAdapter);
156
+ if (!(await pathExists(symbolsDir))) {
188
157
  return [];
189
158
  }
190
159
  const libraries = [];
191
- const files = fs.readdirSync(symbolsDir);
160
+ const files = await listDir(symbolsDir);
192
161
  for (const file of files) {
193
162
  if (file.endsWith('.symbols')) {
194
- const path = getNodePath();
195
- const fullPath = path.join(symbolsDir, file);
196
- const stats = fs.statSync(fullPath);
197
- const libraryName = path.basename(file, '.symbols');
163
+ const fullPath = join(symbolsDir, file);
164
+ const size = await getFileSize(fullPath);
165
+ const libraryName = basename(file, '.symbols');
198
166
  libraries.push({
199
167
  name: libraryName,
200
168
  pixFile: fullPath, // Reuse this field for the .symbols file path
201
169
  exists: true,
202
- size: stats.size,
170
+ size,
203
171
  locale: 'global', // .symbols files are not locale-specific
204
172
  });
205
173
  }
@@ -212,12 +180,13 @@ export function getAvailableSymbolLibraries(options = {}) {
212
180
  * @param options - Resolution options
213
181
  * @returns Symbol library info or undefined if not found
214
182
  */
215
- export function getSymbolLibraryInfo(libraryName, options = {}) {
216
- const grid3Path = options.grid3Path || options.symbolDir || getDefaultGrid3Path();
183
+ export async function getSymbolLibraryInfo(libraryName, options = {}, fileAdapter) {
184
+ const { pathExists, getFileSize, join } = fileAdapter ?? defaultFileAdapter;
185
+ const grid3Path = options.grid3Path || options.symbolDir || (await getDefaultGrid3Path());
217
186
  if (!grid3Path) {
218
187
  return undefined;
219
188
  }
220
- const symbolsDir = getSymbolLibrariesDir(grid3Path);
189
+ const symbolsDir = getSymbolLibrariesDir(grid3Path, fileAdapter);
221
190
  const normalizedLibName = libraryName.toLowerCase();
222
191
  // Try different case variations
223
192
  const variations = [
@@ -226,16 +195,14 @@ export function getSymbolLibraryInfo(libraryName, options = {}) {
226
195
  libraryName + '.symbols',
227
196
  ];
228
197
  for (const file of variations) {
229
- const path = getNodePath();
230
- const fullPath = path.join(symbolsDir, file);
231
- const fs = getNodeFs();
232
- if (fs.existsSync(fullPath)) {
233
- const stats = fs.statSync(fullPath);
198
+ const fullPath = join(symbolsDir, file);
199
+ if (await pathExists(fullPath)) {
200
+ const size = await getFileSize(fullPath);
234
201
  return {
235
202
  name: libraryName,
236
203
  pixFile: fullPath,
237
204
  exists: true,
238
- size: stats.size,
205
+ size,
239
206
  locale: 'global',
240
207
  };
241
208
  }
@@ -248,7 +215,7 @@ export function getSymbolLibraryInfo(libraryName, options = {}) {
248
215
  * @param options - Resolution options
249
216
  * @returns Resolution result with image data if found
250
217
  */
251
- export function resolveSymbolReference(reference, options = {}) {
218
+ export async function resolveSymbolReference(reference, options = {}, fileAdapter = defaultFileAdapter, zipAdapter) {
252
219
  const parsed = parseSymbolReference(reference);
253
220
  if (!parsed.isValid) {
254
221
  return {
@@ -257,7 +224,7 @@ export function resolveSymbolReference(reference, options = {}) {
257
224
  error: 'Invalid symbol reference format',
258
225
  };
259
226
  }
260
- const grid3Path = options.grid3Path || getDefaultGrid3Path();
227
+ const grid3Path = options.grid3Path || (await getDefaultGrid3Path());
261
228
  if (!grid3Path) {
262
229
  return {
263
230
  reference: parsed,
@@ -265,7 +232,7 @@ export function resolveSymbolReference(reference, options = {}) {
265
232
  error: 'Grid 3 installation not found. Please specify grid3Path.',
266
233
  };
267
234
  }
268
- const libraryInfo = getSymbolLibraryInfo(parsed.library, { grid3Path });
235
+ const libraryInfo = await getSymbolLibraryInfo(parsed.library, { grid3Path });
269
236
  if (!libraryInfo || !libraryInfo.exists) {
270
237
  return {
271
238
  reference: parsed,
@@ -275,16 +242,16 @@ export function resolveSymbolReference(reference, options = {}) {
275
242
  }
276
243
  try {
277
244
  // .symbols files are ZIP archives
278
- const AdmZip = getAdmZip();
279
- const zip = new AdmZip(libraryInfo.pixFile);
245
+ const zipFile = libraryInfo.pixFile;
246
+ const zip = zipAdapter ? await zipAdapter(zipFile) : await getZipAdapter(zipFile, fileAdapter);
280
247
  // The path in the symbol reference becomes the path within the symbols/ folder
281
248
  // e.g., [tawasl]/above bw.png becomes symbols/above bw.png
282
249
  const symbolPath = `symbols/${parsed.path}`;
283
- const entry = zip.getEntry(symbolPath);
250
+ const entry = await zip.readFile(symbolPath);
284
251
  if (!entry) {
285
252
  // Try without the symbols/ prefix (in case reference already includes it)
286
253
  const altPath = parsed.path.startsWith('symbols/') ? parsed.path : `symbols/${parsed.path}`;
287
- const altEntry = zip.getEntry(altPath);
254
+ const altEntry = await zip.readFile(altPath);
288
255
  if (!altEntry) {
289
256
  return {
290
257
  reference: parsed,
@@ -295,7 +262,7 @@ export function resolveSymbolReference(reference, options = {}) {
295
262
  };
296
263
  }
297
264
  // Found with alternate path
298
- const data = altEntry.getData();
265
+ const data = Buffer.from(altEntry);
299
266
  return {
300
267
  reference: parsed,
301
268
  found: true,
@@ -305,7 +272,7 @@ export function resolveSymbolReference(reference, options = {}) {
305
272
  };
306
273
  }
307
274
  // Found the symbol!
308
- const data = entry.getData();
275
+ const data = Buffer.from(entry);
309
276
  return {
310
277
  reference: parsed,
311
278
  found: true,
@@ -13,8 +13,7 @@ import { parseSymbolReference } from './gridset/symbols';
13
13
  import { isSymbolLibraryReference } from './gridset/resolver';
14
14
  import { generateCloneId } from '../utilities/analytics/utils/idGenerator';
15
15
  import { translateWithSymbols, extractSymbolsFromButton } from './gridset/symbolAlignment';
16
- import { readBinaryFromInput, decodeText, writeBinaryToPath, getNodeRequire, isNodeRuntime, } from '../utils/io';
17
- import { openZipFromInput } from '../utils/zip';
16
+ import { decodeText } from '../utils/io';
18
17
  class GridsetProcessor extends BaseProcessor {
19
18
  constructor(options) {
20
19
  super(options);
@@ -395,19 +394,18 @@ class GridsetProcessor extends BaseProcessor {
395
394
  return texts;
396
395
  }
397
396
  async loadIntoTree(filePathOrBuffer) {
397
+ const { readBinaryFromInput } = this.options.fileAdapter;
398
398
  const tree = new AACTree();
399
399
  let zipResult;
400
400
  try {
401
- const zipInput = readBinaryFromInput(filePathOrBuffer);
402
- zipResult = this.options.zipAdapter
403
- ? await this.options.zipAdapter(zipInput)
404
- : await openZipFromInput(zipInput);
401
+ const zipInput = await readBinaryFromInput(filePathOrBuffer);
402
+ zipResult = await this.options.zipAdapter(zipInput);
405
403
  }
406
404
  catch (error) {
407
405
  throw new Error(`Invalid ZIP file format: ${error.message}`);
408
406
  }
409
407
  const password = this.getGridsetPassword(filePathOrBuffer);
410
- const entries = getZipEntriesFromAdapter(zipResult.zip, password);
408
+ const entries = getZipEntriesFromAdapter(zipResult, password);
411
409
  const options = {
412
410
  ignoreAttributes: false,
413
411
  ignoreDeclaration: true,
@@ -1683,6 +1681,7 @@ class GridsetProcessor extends BaseProcessor {
1683
1681
  return tree;
1684
1682
  }
1685
1683
  async processTexts(filePathOrBuffer, translations, outputPath) {
1684
+ const { readBinaryFromInput } = this.options.fileAdapter;
1686
1685
  // Load the tree, apply translations, and save to new file
1687
1686
  const tree = await this.loadIntoTree(filePathOrBuffer);
1688
1687
  // Apply translations to all text content
@@ -1743,7 +1742,7 @@ class GridsetProcessor extends BaseProcessor {
1743
1742
  });
1744
1743
  // Save the translated tree and return its content
1745
1744
  await this.saveFromTree(tree, outputPath);
1746
- return readBinaryFromInput(outputPath);
1745
+ return await readBinaryFromInput(outputPath);
1747
1746
  }
1748
1747
  /**
1749
1748
  * Extract symbol information from a gridset for LLM-based translation.
@@ -1752,7 +1751,7 @@ class GridsetProcessor extends BaseProcessor {
1752
1751
  * This method uses shared translation utilities that work across all AAC formats.
1753
1752
  *
1754
1753
  * @param filePathOrBuffer - Path to gridset file or buffer
1755
- * @returns Array of symbol information for LLM processing
1754
+ * @returns Promise resolving to symbol information for LLM processing
1756
1755
  */
1757
1756
  async extractSymbolsForLLM(filePathOrBuffer) {
1758
1757
  const tree = await this.loadIntoTree(filePathOrBuffer);
@@ -1782,9 +1781,10 @@ class GridsetProcessor extends BaseProcessor {
1782
1781
  * @param llmTranslations - Array of LLM translations with symbol info
1783
1782
  * @param outputPath - Where to save the translated gridset
1784
1783
  * @param options - Translation options (e.g., allowPartial for testing)
1785
- * @returns Buffer of the translated gridset
1784
+ * @returns Promise resolving to a buffer of the translated gridset
1786
1785
  */
1787
1786
  async processLLMTranslations(filePathOrBuffer, llmTranslations, outputPath, options) {
1787
+ const { readBinaryFromInput } = this.options.fileAdapter;
1788
1788
  const tree = await this.loadIntoTree(filePathOrBuffer);
1789
1789
  // Validate translations using shared utility
1790
1790
  const buttonIds = Object.values(tree.pages).flatMap((page) => page.buttons.map((b) => b.id));
@@ -1823,40 +1823,16 @@ class GridsetProcessor extends BaseProcessor {
1823
1823
  });
1824
1824
  // Save and return
1825
1825
  await this.saveFromTree(tree, outputPath);
1826
- return readBinaryFromInput(outputPath);
1826
+ return await readBinaryFromInput(outputPath);
1827
1827
  }
1828
1828
  async saveFromTree(tree, outputPath) {
1829
- const useNodeZip = isNodeRuntime();
1830
- let addText;
1831
- let addBinary;
1832
- let finalizeZip;
1833
- if (useNodeZip) {
1834
- const AdmZip = getNodeRequire()('adm-zip');
1835
- const zip = new AdmZip();
1836
- addText = (entryPath, content) => {
1837
- zip.addFile(entryPath, Buffer.from(content, 'utf8'));
1838
- };
1839
- addBinary = (entryPath, content) => {
1840
- zip.addFile(entryPath, Buffer.from(content));
1841
- };
1842
- finalizeZip = () => Promise.resolve(zip.toBuffer());
1843
- }
1844
- else {
1845
- const module = await import('jszip');
1846
- const JSZip = module.default || module;
1847
- const zip = new JSZip();
1848
- addText = (entryPath, content) => {
1849
- zip.file(entryPath, content, { binary: false });
1850
- };
1851
- addBinary = (entryPath, content) => {
1852
- zip.file(entryPath, content);
1853
- };
1854
- finalizeZip = async () => zip.generateAsync({ type: 'uint8array' });
1855
- }
1829
+ const files = [];
1830
+ const { writeBinaryToPath } = this.options.fileAdapter;
1831
+ const zip = await this.options.zipAdapter();
1856
1832
  if (Object.keys(tree.pages).length === 0) {
1857
1833
  // Create empty zip for empty tree
1858
- const zipBuffer = await finalizeZip();
1859
- writeBinaryToPath(outputPath, zipBuffer);
1834
+ const zipBuffer = await zip.writeFiles([]);
1835
+ await writeBinaryToPath(outputPath, zipBuffer);
1860
1836
  return;
1861
1837
  }
1862
1838
  // Collect all unique styles from pages and buttons
@@ -1928,7 +1904,10 @@ class GridsetProcessor extends BaseProcessor {
1928
1904
  suppressEmptyNode: true,
1929
1905
  });
1930
1906
  const settingsXmlContent = settingsBuilder.build(settingsData);
1931
- addText('Settings0/settings.xml', settingsXmlContent);
1907
+ files.push({
1908
+ name: 'Settings0/settings.xml',
1909
+ data: settingsXmlContent,
1910
+ });
1932
1911
  // Create Settings0/Styles/style.xml if there are styles
1933
1912
  if (uniqueStyles.size > 0) {
1934
1913
  const stylesArray = Array.from(uniqueStyles.values()).map(({ id, style }) => {
@@ -1961,7 +1940,10 @@ class GridsetProcessor extends BaseProcessor {
1961
1940
  indentBy: ' ',
1962
1941
  });
1963
1942
  const styleXmlContent = styleBuilder.build(styleData);
1964
- addText('Settings0/Styles/styles.xml', styleXmlContent);
1943
+ files.push({
1944
+ name: 'Settings0/Styles/styles.xml',
1945
+ data: styleXmlContent,
1946
+ });
1965
1947
  }
1966
1948
  // Collect grid file paths for FileMap.xml
1967
1949
  const gridFilePaths = [];
@@ -2102,14 +2084,20 @@ class GridsetProcessor extends BaseProcessor {
2102
2084
  // Add to zip in Grids folder with proper Grid3 naming
2103
2085
  const gridPath = `Grids/${page.name || page.id}/grid.xml`;
2104
2086
  gridFilePaths.push(gridPath);
2105
- addText(gridPath, xmlContent);
2087
+ files.push({
2088
+ name: gridPath,
2089
+ data: xmlContent,
2090
+ });
2106
2091
  });
2107
2092
  // Write image files to ZIP
2108
2093
  buttonImages.forEach((imgData) => {
2109
2094
  if (imgData.imageData && imgData.imageData.length > 0) {
2110
2095
  // Create image path in the grid's directory
2111
2096
  const imagePath = `Grids/${imgData.pageName}/${imgData.x}-${imgData.y}-0-text-0.${imgData.ext}`;
2112
- addBinary(imagePath, imgData.imageData);
2097
+ files.push({
2098
+ name: imagePath,
2099
+ data: imgData.imageData,
2100
+ });
2113
2101
  }
2114
2102
  });
2115
2103
  // Create FileMap.xml to map all grid files with their dynamic image files
@@ -2148,10 +2136,13 @@ class GridsetProcessor extends BaseProcessor {
2148
2136
  indentBy: ' ',
2149
2137
  });
2150
2138
  const fileMapXmlContent = fileMapBuilder.build(fileMapData);
2151
- addText('FileMap.xml', fileMapXmlContent);
2139
+ files.push({
2140
+ name: 'FileMap.xml',
2141
+ data: fileMapXmlContent,
2142
+ });
2152
2143
  // Write the zip file
2153
- const zipBuffer = await finalizeZip();
2154
- writeBinaryToPath(outputPath, zipBuffer);
2144
+ const zipBuffer = await zip.writeFiles(files);
2145
+ await writeBinaryToPath(outputPath, zipBuffer);
2155
2146
  }
2156
2147
  // Helper method to calculate column definitions based on page layout
2157
2148
  calculateColumnDefinitions(page) {
@@ -2248,7 +2239,7 @@ class GridsetProcessor extends BaseProcessor {
2248
2239
  * @returns Promise with validation result
2249
2240
  */
2250
2241
  async validate(filePath) {
2251
- return GridsetValidator.validateFile(filePath);
2242
+ return GridsetValidator.validateFile(filePath, this.options.fileAdapter);
2252
2243
  }
2253
2244
  }
2254
2245
  export { GridsetProcessor };