@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
@@ -0,0 +1,201 @@
1
+ /* eslint-disable @typescript-eslint/require-await */
2
+ /* eslint-disable @typescript-eslint/no-unsafe-argument */
3
+ /* eslint-disable @typescript-eslint/no-unsafe-return */
4
+ import * as xml2js from 'xml2js';
5
+ import { BaseValidator } from './baseValidator';
6
+ import { decodeText, getBasename, getFs, readBinaryFromInput, toUint8Array } from '../utils/io';
7
+ /**
8
+ * Validator for TouchChat files (.ce)
9
+ * TouchChat files are XML-based
10
+ */
11
+ export class TouchChatValidator extends BaseValidator {
12
+ constructor() {
13
+ super();
14
+ }
15
+ /**
16
+ * Validate a TouchChat file from disk
17
+ */
18
+ static async validateFile(filePath) {
19
+ const validator = new TouchChatValidator();
20
+ const content = readBinaryFromInput(filePath);
21
+ const stats = getFs().statSync(filePath);
22
+ return validator.validate(content, getBasename(filePath), stats.size);
23
+ }
24
+ /**
25
+ * Check if content is TouchChat format
26
+ */
27
+ static async identifyFormat(content, filename) {
28
+ const name = filename.toLowerCase();
29
+ if (name.endsWith('.ce')) {
30
+ return true;
31
+ }
32
+ // Try to parse as XML and check for TouchChat structure
33
+ try {
34
+ const contentStr = typeof content === 'string' ? content : decodeText(toUint8Array(content));
35
+ const parser = new xml2js.Parser();
36
+ const result = await parser.parseStringPromise(contentStr);
37
+ // TouchChat files typically have specific structure
38
+ return result && (result.PageSet || result.Pageset || result.page || result.Page);
39
+ }
40
+ catch {
41
+ return false;
42
+ }
43
+ }
44
+ /**
45
+ * Main validation method
46
+ */
47
+ async validate(content, filename, filesize) {
48
+ this.reset();
49
+ await this.add_check('filename', 'file extension', async () => {
50
+ if (!filename.match(/\.ce$/i)) {
51
+ this.warn('filename should end with .ce');
52
+ }
53
+ });
54
+ let xmlObj = null;
55
+ await this.add_check('xml_parse', 'valid XML', async () => {
56
+ try {
57
+ const parser = new xml2js.Parser();
58
+ const contentStr = decodeText(content);
59
+ xmlObj = await parser.parseStringPromise(contentStr);
60
+ }
61
+ catch (e) {
62
+ this.err(`Failed to parse XML: ${e.message}`, true);
63
+ }
64
+ });
65
+ if (!xmlObj) {
66
+ return this.buildResult(filename, filesize, 'touchchat');
67
+ }
68
+ await this.add_check('xml_structure', 'TouchChat root element', async () => {
69
+ // TouchChat can have different root elements
70
+ const hasValidRoot = xmlObj.PageSet ||
71
+ xmlObj.Pageset ||
72
+ xmlObj.page ||
73
+ xmlObj.Page ||
74
+ xmlObj.pages ||
75
+ xmlObj.Pages;
76
+ if (!hasValidRoot) {
77
+ this.err('file does not contain a recognized TouchChat structure');
78
+ }
79
+ });
80
+ const root = xmlObj.PageSet ||
81
+ xmlObj.Pageset ||
82
+ xmlObj.page ||
83
+ xmlObj.Page ||
84
+ xmlObj.pages ||
85
+ xmlObj.Pages;
86
+ if (root) {
87
+ await this.validateTouchChatStructure(root);
88
+ }
89
+ return this.buildResult(filename, filesize, 'touchchat');
90
+ }
91
+ /**
92
+ * Validate TouchChat structure
93
+ */
94
+ async validateTouchChatStructure(root) {
95
+ // Check for ID
96
+ await this.add_check('root_id', 'root element ID', async () => {
97
+ const id = root.$?.id || root.$?.Id;
98
+ if (!id) {
99
+ this.warn('root element should have an id attribute');
100
+ }
101
+ });
102
+ // Check for name
103
+ await this.add_check('root_name', 'root element name', async () => {
104
+ const name = root.$?.name || root.$?.Name || root.name?.[0];
105
+ if (!name) {
106
+ this.warn('root element should have a name');
107
+ }
108
+ });
109
+ // Check for pages
110
+ await this.add_check('pages', 'pages collection', async () => {
111
+ const pages = root.page || root.Page || root.pages || root.Pages;
112
+ if (!pages) {
113
+ this.err('TouchChat file must contain pages');
114
+ }
115
+ else if (!Array.isArray(pages) || pages.length === 0) {
116
+ this.err('TouchChat file must contain at least one page');
117
+ }
118
+ });
119
+ // Validate individual pages
120
+ const pages = root.page || root.Page || root.pages || root.Pages;
121
+ if (pages && Array.isArray(pages)) {
122
+ await this.add_check('page_count', 'page count', async () => {
123
+ if (pages.length === 0) {
124
+ this.err('Must contain at least one page');
125
+ }
126
+ });
127
+ // Sample first few pages
128
+ const sampleSize = Math.min(pages.length, 5);
129
+ for (let i = 0; i < sampleSize; i++) {
130
+ await this.validatePage(pages[i], i);
131
+ }
132
+ }
133
+ }
134
+ /**
135
+ * Validate a single page
136
+ */
137
+ async validatePage(page, index) {
138
+ await this.add_check(`page[${index}]_id`, `page ${index} ID`, async () => {
139
+ const id = page.$?.id || page.$?.Id;
140
+ if (!id) {
141
+ this.warn(`page ${index} is missing an id attribute`);
142
+ }
143
+ });
144
+ await this.add_check(`page[${index}]_name`, `page ${index} name`, async () => {
145
+ const name = page.$?.name || page.$?.Name || page.name?.[0];
146
+ if (!name) {
147
+ this.warn(`page ${index} should have a name`);
148
+ }
149
+ });
150
+ // Check for buttons/items
151
+ await this.add_check(`page[${index}]_buttons`, `page ${index} buttons`, async () => {
152
+ const buttons = page.button || page.Button || page.item || page.Item;
153
+ if (!buttons) {
154
+ this.warn(`page ${index} has no buttons/items`);
155
+ }
156
+ else if (Array.isArray(buttons) && buttons.length === 0) {
157
+ this.warn(`page ${index} should contain at least one button`);
158
+ }
159
+ });
160
+ // Validate button references
161
+ const buttons = page.button || page.Button || page.item || page.Item;
162
+ if (buttons && Array.isArray(buttons)) {
163
+ const sampleSize = Math.min(buttons.length, 3);
164
+ for (let i = 0; i < sampleSize; i++) {
165
+ await this.validateButton(buttons[i], index, i);
166
+ }
167
+ }
168
+ }
169
+ /**
170
+ * Validate a single button
171
+ */
172
+ async validateButton(button, pageIdx, buttonIdx) {
173
+ await this.add_check(`page[${pageIdx}]_button[${buttonIdx}]_label`, `button label`, async () => {
174
+ const label = button.$?.label || button.$?.Label || button.label?.[0];
175
+ if (!label) {
176
+ this.warn(`button ${buttonIdx} on page ${pageIdx} should have a label`);
177
+ }
178
+ });
179
+ await this.add_check(`page[${pageIdx}]_button[${buttonIdx}]_vocalization`, `button vocalization`, async () => {
180
+ const vocalization = button.$?.vocalization || button.$?.Vocalization || button.vocalization?.[0];
181
+ if (!vocalization) {
182
+ // Vocalization is optional, so just info
183
+ }
184
+ });
185
+ // Check for image reference
186
+ await this.add_check(`page[${pageIdx}]_button[${buttonIdx}]_image`, `button image`, async () => {
187
+ const image = button.$?.image || button.$?.Image || button.img?.[0];
188
+ if (!image) {
189
+ this.warn(`button ${buttonIdx} on page ${pageIdx} should have an image reference`);
190
+ }
191
+ });
192
+ // Check for link/action
193
+ await this.add_check(`page[${pageIdx}]_button[${buttonIdx}]_action`, `button action`, async () => {
194
+ const link = button.$?.link || button.$?.Link;
195
+ const action = button.$?.action || button.$?.Action;
196
+ if (!link && !action) {
197
+ // Not all buttons need actions, they can just speak
198
+ }
199
+ });
200
+ }
201
+ }
@@ -6,6 +6,10 @@
6
6
  * **NOTE: Gridset .gridsetx files**
7
7
  * GridsetProcessor supports regular `.gridset` files in browser.
8
8
  * Encrypted `.gridsetx` files require Node.js for crypto operations and are not supported in browser.
9
+ *
10
+ * **NOTE: SQLite-backed formats**
11
+ * Snap (.sps/.spb) and TouchChat (.ce) require a WASM-backed SQLite engine.
12
+ * Configure `sql.js` in browser builds via `configureSqlJs()` before loading these formats.
9
13
  */
10
14
  export * from './core/treeStructure';
11
15
  export * from './core/baseProcessor';
@@ -14,9 +18,12 @@ export { DotProcessor } from './processors/dotProcessor';
14
18
  export { OpmlProcessor } from './processors/opmlProcessor';
15
19
  export { ObfProcessor } from './processors/obfProcessor';
16
20
  export { GridsetProcessor } from './processors/gridsetProcessor';
21
+ export { SnapProcessor } from './processors/snapProcessor';
22
+ export { TouchChatProcessor } from './processors/touchchatProcessor';
17
23
  export { ApplePanelsProcessor } from './processors/applePanelsProcessor';
18
24
  export { AstericsGridProcessor } from './processors/astericsGridProcessor';
19
25
  import { BaseProcessor } from './core/baseProcessor';
26
+ export { configureSqlJs } from './utils/sqlite';
20
27
  /**
21
28
  * Factory function to get the appropriate processor for a file extension
22
29
  * @param filePathOrExtension - File path or extension (e.g., '.dot', '/path/to/file.obf')
@@ -7,6 +7,10 @@
7
7
  * **NOTE: Gridset .gridsetx files**
8
8
  * GridsetProcessor supports regular `.gridset` files in browser.
9
9
  * Encrypted `.gridsetx` files require Node.js for crypto operations and are not supported in browser.
10
+ *
11
+ * **NOTE: SQLite-backed formats**
12
+ * Snap (.sps/.spb) and TouchChat (.ce) require a WASM-backed SQLite engine.
13
+ * Configure `sql.js` in browser builds via `configureSqlJs()` before loading these formats.
10
14
  */
11
15
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
12
16
  if (k2 === undefined) k2 = k;
@@ -23,7 +27,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
23
27
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
24
28
  };
25
29
  Object.defineProperty(exports, "__esModule", { value: true });
26
- exports.AstericsGridProcessor = exports.ApplePanelsProcessor = exports.GridsetProcessor = exports.ObfProcessor = exports.OpmlProcessor = exports.DotProcessor = void 0;
30
+ exports.configureSqlJs = exports.AstericsGridProcessor = exports.ApplePanelsProcessor = exports.TouchChatProcessor = exports.SnapProcessor = exports.GridsetProcessor = exports.ObfProcessor = exports.OpmlProcessor = exports.DotProcessor = void 0;
27
31
  exports.getProcessor = getProcessor;
28
32
  exports.getSupportedExtensions = getSupportedExtensions;
29
33
  exports.isExtensionSupported = isExtensionSupported;
@@ -44,6 +48,10 @@ var obfProcessor_1 = require("./processors/obfProcessor");
44
48
  Object.defineProperty(exports, "ObfProcessor", { enumerable: true, get: function () { return obfProcessor_1.ObfProcessor; } });
45
49
  var gridsetProcessor_1 = require("./processors/gridsetProcessor");
46
50
  Object.defineProperty(exports, "GridsetProcessor", { enumerable: true, get: function () { return gridsetProcessor_1.GridsetProcessor; } });
51
+ var snapProcessor_1 = require("./processors/snapProcessor");
52
+ Object.defineProperty(exports, "SnapProcessor", { enumerable: true, get: function () { return snapProcessor_1.SnapProcessor; } });
53
+ var touchchatProcessor_1 = require("./processors/touchchatProcessor");
54
+ Object.defineProperty(exports, "TouchChatProcessor", { enumerable: true, get: function () { return touchchatProcessor_1.TouchChatProcessor; } });
47
55
  var applePanelsProcessor_1 = require("./processors/applePanelsProcessor");
48
56
  Object.defineProperty(exports, "ApplePanelsProcessor", { enumerable: true, get: function () { return applePanelsProcessor_1.ApplePanelsProcessor; } });
49
57
  var astericsGridProcessor_1 = require("./processors/astericsGridProcessor");
@@ -52,8 +60,12 @@ const dotProcessor_2 = require("./processors/dotProcessor");
52
60
  const opmlProcessor_2 = require("./processors/opmlProcessor");
53
61
  const obfProcessor_2 = require("./processors/obfProcessor");
54
62
  const gridsetProcessor_2 = require("./processors/gridsetProcessor");
63
+ const snapProcessor_2 = require("./processors/snapProcessor");
64
+ const touchchatProcessor_2 = require("./processors/touchchatProcessor");
55
65
  const applePanelsProcessor_2 = require("./processors/applePanelsProcessor");
56
66
  const astericsGridProcessor_2 = require("./processors/astericsGridProcessor");
67
+ var sqlite_1 = require("./utils/sqlite");
68
+ Object.defineProperty(exports, "configureSqlJs", { enumerable: true, get: function () { return sqlite_1.configureSqlJs; } });
57
69
  /**
58
70
  * Factory function to get the appropriate processor for a file extension
59
71
  * @param filePathOrExtension - File path or extension (e.g., '.dot', '/path/to/file.obf')
@@ -74,6 +86,11 @@ function getProcessor(filePathOrExtension) {
74
86
  return new obfProcessor_2.ObfProcessor();
75
87
  case '.gridset':
76
88
  return new gridsetProcessor_2.GridsetProcessor();
89
+ case '.spb':
90
+ case '.sps':
91
+ return new snapProcessor_2.SnapProcessor();
92
+ case '.ce':
93
+ return new touchchatProcessor_2.TouchChatProcessor();
77
94
  case '.plist':
78
95
  return new applePanelsProcessor_2.ApplePanelsProcessor();
79
96
  case '.grd':
@@ -87,7 +104,7 @@ function getProcessor(filePathOrExtension) {
87
104
  * @returns Array of supported file extensions
88
105
  */
89
106
  function getSupportedExtensions() {
90
- return ['.dot', '.opml', '.obf', '.obz', '.gridset', '.plist', '.grd'];
107
+ return ['.dot', '.opml', '.obf', '.obz', '.gridset', '.spb', '.sps', '.ce', '.plist', '.grd'];
91
108
  }
92
109
  /**
93
110
  * Check if a file extension is supported
@@ -50,6 +50,7 @@ const child_process_1 = require("child_process");
50
50
  const better_sqlite3_1 = __importDefault(require("better-sqlite3"));
51
51
  const dotnetTicks_1 = require("../../utils/dotnetTicks");
52
52
  const password_1 = require("./password");
53
+ const zip_1 = require("../../utils/zip");
53
54
  function normalizeZipPath(p) {
54
55
  const unified = p.replace(/\\/g, '/');
55
56
  try {
@@ -97,10 +98,8 @@ function getAllowedImageEntries(tree) {
97
98
  */
98
99
  async function openImage(gridsetBuffer, entryPath, password = (0, password_1.resolveGridsetPasswordFromEnv)()) {
99
100
  try {
100
- // eslint-disable-next-line @typescript-eslint/no-var-requires
101
- const JSZip = require('jszip');
102
- const zip = await JSZip.loadAsync(gridsetBuffer);
103
- const entries = (0, password_1.getZipEntriesWithPassword)(zip, password);
101
+ const { zip } = await (0, zip_1.openZipFromInput)(gridsetBuffer);
102
+ const entries = (0, password_1.getZipEntriesFromAdapter)(zip, password);
104
103
  const want = normalizeZipPath(entryPath);
105
104
  const entry = entries.find((e) => normalizeZipPath(e.entryName) === want);
106
105
  if (!entry)
@@ -15,7 +15,7 @@ import { CellBackgroundShape } from './styleHelpers';
15
15
  import { Grid3CellType } from './pluginTypes';
16
16
  import { Grid3CommandCategory } from './commands';
17
17
  export { ensureAlphaChannel, darkenColor, lightenColor, hexToRgba, rgbaToHex } from './colorUtils';
18
- export { resolveGridsetPassword, getZipEntriesWithPassword, resolveGridsetPasswordFromEnv, } from './password';
18
+ export { resolveGridsetPassword, getZipEntriesWithPassword, getZipEntriesFromAdapter, resolveGridsetPasswordFromEnv, } from './password';
19
19
  export { getPageTokenImageMap, getAllowedImageEntries, openImage, generateGrid3Guid, createSettingsXml, createFileMapXml, getCommonDocumentsPath, findGrid3UserPaths, findGrid3HistoryDatabases, findGrid3Vocabularies, findGrid3UserHistory, findGrid3Users, isGrid3Installed, readGrid3History, readGrid3HistoryForUser, readAllGrid3History, type Grid3UserPath, type Grid3VocabularyPath, type Grid3HistoryEntry, } from './helpers';
20
20
  export { parseSymbolReference, isSymbolReference, resolveSymbolReference, getAvailableSymbolLibraries, getSymbolLibraryInfo, extractSymbolReferences, analyzeSymbolUsage, createSymbolReference, getSymbolLibraryName, getSymbolPath, isKnownSymbolLibrary, getSymbolLibraryDisplayName, getDefaultGrid3Path, getSymbolLibrariesDir, getSymbolSearchIndexesDir, symbolReferenceToFilename, SYMBOL_LIBRARIES, type SymbolReference, type SymbolLibraryInfo, type SymbolResolutionOptions, type SymbolResolutionResult, type SymbolUsageStats, type SymbolLibraryName, } from './symbols';
21
21
  export { getSymbolsDir, getSymbolSearchDir } from './symbols';
@@ -10,8 +10,8 @@
10
10
  * - Image resolution helpers
11
11
  */
12
12
  Object.defineProperty(exports, "__esModule", { value: true });
13
- exports.parseSymbolReference = exports.readAllGrid3History = exports.readGrid3HistoryForUser = exports.readGrid3History = exports.isGrid3Installed = exports.findGrid3Users = exports.findGrid3UserHistory = exports.findGrid3Vocabularies = exports.findGrid3HistoryDatabases = exports.findGrid3UserPaths = exports.getCommonDocumentsPath = exports.createFileMapXml = exports.createSettingsXml = exports.generateGrid3Guid = exports.openImage = exports.getAllowedImageEntries = exports.getPageTokenImageMap = exports.resolveGridsetPasswordFromEnv = exports.getZipEntriesWithPassword = exports.resolveGridsetPassword = exports.rgbaToHex = exports.hexToRgba = exports.lightenColor = exports.darkenColor = exports.ensureAlphaChannel = exports.Grid3CommandCategory = exports.GRID3_COMMANDS = exports.extractCommandParameters = exports.getAllPluginIds = exports.getAllCommandIds = exports.getCommandsByCategory = exports.getCommandsByPlugin = exports.getCommandDefinition = exports.detectCommand = exports.isRegularCell = exports.isAutoContentCell = exports.isLiveCell = exports.isWorkspaceCell = exports.getCellTypeDisplayName = exports.AUTOCONTENT_TYPES = exports.LIVECELL_TYPES = exports.WORKSPACE_TYPES = exports.Grid3CellType = exports.detectPluginCellType = exports.createCategoryStyle = exports.createDefaultStylesXml = exports.CATEGORY_STYLES = exports.DEFAULT_GRID3_STYLES = exports.SHAPE_NAMES = exports.CellBackgroundShape = void 0;
14
- exports.GRID3_COMMAND_CATEGORIES = exports.GRID3_CELL_TYPES = exports.GRID3_CELL_SHAPES = exports.GRID3_PLUGIN_IDS = exports.GRID3_COMMAND_IDS = exports.getSymbolSearchStats = exports.countLibrarySymbols = exports.getSearchSuggestions = exports.getAllSearchTerms = exports.getSymbolDisplayName = exports.getSymbolFilename = exports.searchSymbolsWithReferences = exports.searchSymbols = exports.loadSearchIndexes = exports.parsePixFile = exports.createSymbolManifest = exports.exportSymbolReferencesToCsv = exports.suggestExtractionStrategy = exports.analyzeSymbolExtraction = exports.convertToAstericsImage = exports.extractSymbolLibraryImage = exports.extractButtonImage = exports.parseImageSymbolReference = exports.isSymbolLibraryReference = exports.resolveGrid3CellImage = exports.getSymbolSearchDir = exports.getSymbolsDir = exports.SYMBOL_LIBRARIES = exports.symbolReferenceToFilename = exports.getSymbolSearchIndexesDir = exports.getSymbolLibrariesDir = exports.getDefaultGrid3Path = exports.getSymbolLibraryDisplayName = exports.isKnownSymbolLibrary = exports.getSymbolPath = exports.getSymbolLibraryName = exports.createSymbolReference = exports.analyzeSymbolUsage = exports.extractSymbolReferences = exports.getSymbolLibraryInfo = exports.getAvailableSymbolLibraries = exports.resolveSymbolReference = exports.isSymbolReference = void 0;
13
+ exports.readAllGrid3History = exports.readGrid3HistoryForUser = exports.readGrid3History = exports.isGrid3Installed = exports.findGrid3Users = exports.findGrid3UserHistory = exports.findGrid3Vocabularies = exports.findGrid3HistoryDatabases = exports.findGrid3UserPaths = exports.getCommonDocumentsPath = exports.createFileMapXml = exports.createSettingsXml = exports.generateGrid3Guid = exports.openImage = exports.getAllowedImageEntries = exports.getPageTokenImageMap = exports.resolveGridsetPasswordFromEnv = exports.getZipEntriesFromAdapter = exports.getZipEntriesWithPassword = exports.resolveGridsetPassword = exports.rgbaToHex = exports.hexToRgba = exports.lightenColor = exports.darkenColor = exports.ensureAlphaChannel = exports.Grid3CommandCategory = exports.GRID3_COMMANDS = exports.extractCommandParameters = exports.getAllPluginIds = exports.getAllCommandIds = exports.getCommandsByCategory = exports.getCommandsByPlugin = exports.getCommandDefinition = exports.detectCommand = exports.isRegularCell = exports.isAutoContentCell = exports.isLiveCell = exports.isWorkspaceCell = exports.getCellTypeDisplayName = exports.AUTOCONTENT_TYPES = exports.LIVECELL_TYPES = exports.WORKSPACE_TYPES = exports.Grid3CellType = exports.detectPluginCellType = exports.createCategoryStyle = exports.createDefaultStylesXml = exports.CATEGORY_STYLES = exports.DEFAULT_GRID3_STYLES = exports.SHAPE_NAMES = exports.CellBackgroundShape = void 0;
14
+ exports.GRID3_COMMAND_CATEGORIES = exports.GRID3_CELL_TYPES = exports.GRID3_CELL_SHAPES = exports.GRID3_PLUGIN_IDS = exports.GRID3_COMMAND_IDS = exports.getSymbolSearchStats = exports.countLibrarySymbols = exports.getSearchSuggestions = exports.getAllSearchTerms = exports.getSymbolDisplayName = exports.getSymbolFilename = exports.searchSymbolsWithReferences = exports.searchSymbols = exports.loadSearchIndexes = exports.parsePixFile = exports.createSymbolManifest = exports.exportSymbolReferencesToCsv = exports.suggestExtractionStrategy = exports.analyzeSymbolExtraction = exports.convertToAstericsImage = exports.extractSymbolLibraryImage = exports.extractButtonImage = exports.parseImageSymbolReference = exports.isSymbolLibraryReference = exports.resolveGrid3CellImage = exports.getSymbolSearchDir = exports.getSymbolsDir = exports.SYMBOL_LIBRARIES = exports.symbolReferenceToFilename = exports.getSymbolSearchIndexesDir = exports.getSymbolLibrariesDir = exports.getDefaultGrid3Path = exports.getSymbolLibraryDisplayName = exports.isKnownSymbolLibrary = exports.getSymbolPath = exports.getSymbolLibraryName = exports.createSymbolReference = exports.analyzeSymbolUsage = exports.extractSymbolReferences = exports.getSymbolLibraryInfo = exports.getAvailableSymbolLibraries = exports.resolveSymbolReference = exports.isSymbolReference = exports.parseSymbolReference = void 0;
15
15
  // Style helpers
16
16
  var styleHelpers_1 = require("./styleHelpers");
17
17
  Object.defineProperty(exports, "CellBackgroundShape", { enumerable: true, get: function () { return styleHelpers_1.CellBackgroundShape; } });
@@ -59,6 +59,7 @@ Object.defineProperty(exports, "rgbaToHex", { enumerable: true, get: function ()
59
59
  var password_1 = require("./password");
60
60
  Object.defineProperty(exports, "resolveGridsetPassword", { enumerable: true, get: function () { return password_1.resolveGridsetPassword; } });
61
61
  Object.defineProperty(exports, "getZipEntriesWithPassword", { enumerable: true, get: function () { return password_1.getZipEntriesWithPassword; } });
62
+ Object.defineProperty(exports, "getZipEntriesFromAdapter", { enumerable: true, get: function () { return password_1.getZipEntriesFromAdapter; } });
62
63
  Object.defineProperty(exports, "resolveGridsetPasswordFromEnv", { enumerable: true, get: function () { return password_1.resolveGridsetPasswordFromEnv; } });
63
64
  // Helper functions
64
65
  var helpers_1 = require("./helpers");
@@ -1,6 +1,7 @@
1
1
  import type JSZip from 'jszip';
2
2
  import { ProcessorOptions } from '../../core/baseProcessor';
3
3
  import { ProcessorInput } from '../../utils/io';
4
+ import { type ZipAdapter } from '../../utils/zip';
4
5
  /**
5
6
  * Resolve the password to use for Grid3 archives.
6
7
  * Preference order:
@@ -18,11 +19,11 @@ export declare function resolveGridsetPasswordFromEnv(): string | undefined;
18
19
  * @param password - Optional password (kept for API compatibility, not used with JSZip)
19
20
  * @returns Array of entry objects with name and data
20
21
  */
21
- type ZipEntry = {
22
+ export type ZipEntry = {
22
23
  name: string;
23
24
  entryName: string;
24
25
  dir: boolean;
25
26
  getData: () => Promise<Uint8Array>;
26
27
  };
27
28
  export declare function getZipEntriesWithPassword(zip: JSZip, password?: string): ZipEntry[];
28
- export {};
29
+ export declare function getZipEntriesFromAdapter(zip: ZipAdapter, password?: string): ZipEntry[];
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.resolveGridsetPassword = resolveGridsetPassword;
4
4
  exports.resolveGridsetPasswordFromEnv = resolveGridsetPasswordFromEnv;
5
5
  exports.getZipEntriesWithPassword = getZipEntriesWithPassword;
6
+ exports.getZipEntriesFromAdapter = getZipEntriesFromAdapter;
6
7
  function getExtension(source) {
7
8
  const index = source.lastIndexOf('.');
8
9
  if (index === -1)
@@ -52,3 +53,14 @@ function getZipEntriesWithPassword(zip, password) {
52
53
  });
53
54
  return entries;
54
55
  }
56
+ function getZipEntriesFromAdapter(zip, password) {
57
+ if (password) {
58
+ console.warn('Zip password support is handled at the archive level for .gridsetx files.');
59
+ }
60
+ return zip.listFiles().map((entryName) => ({
61
+ name: entryName,
62
+ entryName,
63
+ dir: false,
64
+ getData: () => zip.readFile(entryName),
65
+ }));
66
+ }
@@ -9,6 +9,29 @@
9
9
  * Note: Wordlists are only supported in Grid3 format. Other AAC formats
10
10
  * do not have equivalent wordlist functionality.
11
11
  */
12
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
13
+ if (k2 === undefined) k2 = k;
14
+ var desc = Object.getOwnPropertyDescriptor(m, k);
15
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
16
+ desc = { enumerable: true, get: function() { return m[k]; } };
17
+ }
18
+ Object.defineProperty(o, k2, desc);
19
+ }) : (function(o, m, k, k2) {
20
+ if (k2 === undefined) k2 = k;
21
+ o[k2] = m[k];
22
+ }));
23
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
24
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
25
+ }) : function(o, v) {
26
+ o["default"] = v;
27
+ });
28
+ var __importStar = (this && this.__importStar) || function (mod) {
29
+ if (mod && mod.__esModule) return mod;
30
+ var result = {};
31
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
32
+ __setModuleDefault(result, mod);
33
+ return result;
34
+ };
12
35
  Object.defineProperty(exports, "__esModule", { value: true });
13
36
  exports.createWordlist = createWordlist;
14
37
  exports.wordlistToXml = wordlistToXml;
@@ -16,7 +39,9 @@ exports.extractWordlists = extractWordlists;
16
39
  exports.updateWordlist = updateWordlist;
17
40
  const fast_xml_parser_1 = require("fast-xml-parser");
18
41
  const password_1 = require("./password");
42
+ const zip_1 = require("../../utils/zip");
19
43
  const io_1 = require("../../utils/io");
44
+ const io_2 = require("../../utils/io");
20
45
  /**
21
46
  * Creates a WordList object from an array of words/phrases or a dictionary
22
47
  *
@@ -105,55 +130,52 @@ function wordlistToXml(wordlist) {
105
130
  async function extractWordlists(gridsetBuffer, password = (0, password_1.resolveGridsetPasswordFromEnv)()) {
106
131
  const wordlists = new Map();
107
132
  const parser = new fast_xml_parser_1.XMLParser();
108
- let zip;
109
133
  try {
110
- // eslint-disable-next-line @typescript-eslint/no-var-requires
111
- const JSZip = require('jszip');
112
- zip = await JSZip.loadAsync(gridsetBuffer);
113
- }
114
- catch (error) {
115
- throw new Error(`Invalid gridset buffer: ${error.message}`);
116
- }
117
- const entries = (0, password_1.getZipEntriesWithPassword)(zip, password);
118
- // Process each grid file
119
- for (const entry of entries) {
120
- if (entry.entryName.startsWith('Grids/') && entry.entryName.endsWith('grid.xml')) {
121
- try {
122
- const xmlContent = (0, io_1.decodeText)(await entry.getData());
123
- const data = parser.parse(xmlContent);
124
- const grid = data.Grid || data.grid;
125
- if (!grid || !grid.WordList) {
126
- continue;
127
- }
128
- // Extract grid name from path (e.g., "Grids/MyGrid/grid.xml" -> "MyGrid")
129
- const match = entry.entryName.match(/^Grids\/([^/]+)\//);
130
- const gridName = match ? match[1] : entry.entryName;
131
- // Parse wordlist items
132
- const wordlistData = grid.WordList;
133
- const itemsContainer = wordlistData.Items || wordlistData.items;
134
- if (!itemsContainer) {
135
- continue;
134
+ const { zip } = await (0, zip_1.openZipFromInput)(gridsetBuffer);
135
+ const entries = (0, password_1.getZipEntriesFromAdapter)(zip, password);
136
+ // Process each grid file
137
+ for (const entry of entries) {
138
+ if (entry.entryName.startsWith('Grids/') && entry.entryName.endsWith('grid.xml')) {
139
+ try {
140
+ const xmlContent = (0, io_2.decodeText)(await entry.getData());
141
+ const data = parser.parse(xmlContent);
142
+ const grid = data.Grid || data.grid;
143
+ if (!grid || !grid.WordList) {
144
+ continue;
145
+ }
146
+ // Extract grid name from path (e.g., "Grids/MyGrid/grid.xml" -> "MyGrid")
147
+ const match = entry.entryName.match(/^Grids\/([^/]+)\//);
148
+ const gridName = match ? match[1] : entry.entryName;
149
+ // Parse wordlist items
150
+ const wordlistData = grid.WordList;
151
+ const itemsContainer = wordlistData.Items || wordlistData.items;
152
+ if (!itemsContainer) {
153
+ continue;
154
+ }
155
+ const itemArray = Array.isArray(itemsContainer.WordListItem)
156
+ ? itemsContainer.WordListItem
157
+ : itemsContainer.WordListItem
158
+ ? [itemsContainer.WordListItem]
159
+ : [];
160
+ const items = itemArray.map((item) => ({
161
+ text: item.Text?.s?.r || item.text?.s?.r || '',
162
+ image: item.Image || item.image || undefined,
163
+ partOfSpeech: item.PartOfSpeech || item.partOfSpeech || 'Unknown',
164
+ }));
165
+ if (items.length > 0) {
166
+ wordlists.set(gridName, { items });
167
+ }
136
168
  }
137
- const itemArray = Array.isArray(itemsContainer.WordListItem)
138
- ? itemsContainer.WordListItem
139
- : itemsContainer.WordListItem
140
- ? [itemsContainer.WordListItem]
141
- : [];
142
- const items = itemArray.map((item) => ({
143
- text: item.Text?.s?.r || item.text?.s?.r || '',
144
- image: item.Image || item.image || undefined,
145
- partOfSpeech: item.PartOfSpeech || item.partOfSpeech || 'Unknown',
146
- }));
147
- if (items.length > 0) {
148
- wordlists.set(gridName, { items });
169
+ catch (error) {
170
+ // Skip grids with parsing errors
171
+ console.warn(`Failed to extract wordlist from ${entry.entryName}:`, error);
149
172
  }
150
173
  }
151
- catch (error) {
152
- // Skip grids with parsing errors
153
- console.warn(`Failed to extract wordlist from ${entry.entryName}:`, error);
154
- }
155
174
  }
156
175
  }
176
+ catch (error) {
177
+ throw new Error(`Invalid gridset buffer: ${error.message}`);
178
+ }
157
179
  return wordlists;
158
180
  }
159
181
  /**
@@ -178,16 +200,45 @@ async function updateWordlist(gridsetBuffer, gridName, wordlist, password = (0,
178
200
  indentBy: ' ',
179
201
  suppressEmptyNode: false,
180
202
  });
181
- let zip;
203
+ let entries;
204
+ let saveZip = null;
205
+ let updateEntry = null;
182
206
  try {
183
- // eslint-disable-next-line @typescript-eslint/no-var-requires
184
- const JSZip = require('jszip');
185
- zip = await JSZip.loadAsync(gridsetBuffer);
207
+ if ((0, io_1.isNodeRuntime)()) {
208
+ const AdmZip = (0, io_1.getNodeRequire)()('adm-zip');
209
+ const zip = new AdmZip(Buffer.from(gridsetBuffer));
210
+ entries = zip.getEntries().map((entry) => ({
211
+ entryName: entry.entryName,
212
+ getData: () => Promise.resolve(entry.getData()),
213
+ }));
214
+ updateEntry = (entryName, xml) => {
215
+ zip.addFile(entryName, Buffer.from(xml, 'utf8'));
216
+ };
217
+ saveZip = () => Promise.resolve(zip.toBuffer());
218
+ }
219
+ else {
220
+ const module = await Promise.resolve().then(() => __importStar(require('jszip')));
221
+ const JSZip = module.default || module;
222
+ const zip = await JSZip.loadAsync(gridsetBuffer);
223
+ entries = (0, password_1.getZipEntriesFromAdapter)({
224
+ listFiles: () => Object.keys(zip.files),
225
+ readFile: async (name) => {
226
+ const file = zip.file(name);
227
+ if (!file) {
228
+ throw new Error(`Zip entry not found: ${name}`);
229
+ }
230
+ return file.async('uint8array');
231
+ },
232
+ }, password);
233
+ updateEntry = (entryName, xml) => {
234
+ zip.file(entryName, xml, { binary: false });
235
+ };
236
+ saveZip = async () => zip.generateAsync({ type: 'uint8array' });
237
+ }
186
238
  }
187
239
  catch (error) {
188
240
  throw new Error(`Invalid gridset buffer: ${error.message}`);
189
241
  }
190
- const entries = (0, password_1.getZipEntriesWithPassword)(zip, password);
191
242
  let found = false;
192
243
  // Find and update the grid
193
244
  for (const entry of entries) {
@@ -196,7 +247,7 @@ async function updateWordlist(gridsetBuffer, gridName, wordlist, password = (0,
196
247
  const currentGridName = match ? match[1] : null;
197
248
  if (currentGridName === gridName) {
198
249
  try {
199
- const xmlContent = (0, io_1.decodeText)(await entry.getData());
250
+ const xmlContent = (0, io_2.decodeText)(await entry.getData());
200
251
  const data = parser.parse(xmlContent);
201
252
  const grid = data.Grid || data.grid;
202
253
  if (!grid) {
@@ -222,7 +273,9 @@ async function updateWordlist(gridsetBuffer, gridName, wordlist, password = (0,
222
273
  };
223
274
  // Rebuild the XML
224
275
  const updatedXml = builder.build(data);
225
- zip.file(entry.entryName, updatedXml, { binary: false });
276
+ if (updateEntry) {
277
+ updateEntry(entry.entryName, updatedXml);
278
+ }
226
279
  found = true;
227
280
  }
228
281
  catch (error) {
@@ -235,5 +288,8 @@ async function updateWordlist(gridsetBuffer, gridName, wordlist, password = (0,
235
288
  if (!found) {
236
289
  throw new Error(`Grid "${gridName}" not found in gridset`);
237
290
  }
238
- return await zip.generateAsync({ type: 'uint8array' });
291
+ if (!saveZip) {
292
+ throw new Error('Failed to serialize updated gridset.');
293
+ }
294
+ return await saveZip();
239
295
  }