@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
@@ -0,0 +1,221 @@
1
+ import { BaseProcessor, } from '../core/baseProcessor';
2
+ import { AACTree, AACPage, AACButton, AACSemanticIntent } from '../core/treeStructure';
3
+ // Removed unused import: FileProcessor
4
+ import { ValidationFailureError, buildValidationResultFromMessage, } from '../validation/validationTypes';
5
+ import { getBasename, readBinaryFromInput, readTextFromInput, writeBinaryToPath, writeTextToPath, encodeText, } from '../utils/io';
6
+ class DotProcessor extends BaseProcessor {
7
+ constructor(options) {
8
+ super(options);
9
+ }
10
+ parseDotFile(content) {
11
+ const nodes = new Map();
12
+ const edges = [];
13
+ // Extract all edge statements using regex to handle single-line DOT files
14
+ const edgeRegex = /"?([^"\s]+)"?\s*->\s*"?([^"\s]+)"?(?:\s*\[label="([^"]+)"\])?/g;
15
+ // We need to find nodes, but avoid matching the target of an edge which might look like a node definition
16
+ // e.g. A -> B [label="L"] -- "B [label="L"]" looks like a node def
17
+ // Strategy: Find all edges, record them, and then "mask" them in the content to avoid false positives for nodes
18
+ let maskedContent = content;
19
+ let edgeMatch;
20
+ // Find all edge definitions
21
+ while ((edgeMatch = edgeRegex.exec(content)) !== null) {
22
+ const [fullMatch, from, to, label] = edgeMatch;
23
+ edges.push({ from, to, label });
24
+ // Add nodes if they don't exist (implicit definition)
25
+ if (!nodes.has(from)) {
26
+ nodes.set(from, { id: from, label: from });
27
+ }
28
+ if (!nodes.has(to)) {
29
+ nodes.set(to, { id: to, label: to });
30
+ }
31
+ // Mask this edge in the content so we don't match it as a node
32
+ // We replace it with spaces to preserve indices if needed, but simple replacement is enough here
33
+ maskedContent = maskedContent.replace(fullMatch, ' '.repeat(fullMatch.length));
34
+ }
35
+ // Now find explicit node definitions in the masked content
36
+ // This regex matches: ID [label="LABEL"]
37
+ // We use a non-greedy match for the label content to handle escaped quotes if possible,
38
+ // but the previous regex `[^"]+` was too simple.
39
+ // Better regex for quoted string content: (?:[^"\\]|\\.)*
40
+ const nodeRegex = /"?([^"\s]+)"?\s*\[label="((?:[^"\\]|\\.)*)"\]/g;
41
+ let nodeMatch;
42
+ while ((nodeMatch = nodeRegex.exec(maskedContent)) !== null) {
43
+ const [, id, rawLabel] = nodeMatch;
44
+ // Unescape the label: replace \" with " and \\ with \
45
+ const label = rawLabel.replace(/\\"/g, '"').replace(/\\\\/g, '\\');
46
+ // Only update if not already defined or if we want to override the implicit label
47
+ nodes.set(id, { id, label });
48
+ }
49
+ return { nodes: Array.from(nodes.values()), edges };
50
+ }
51
+ async extractTexts(filePathOrBuffer) {
52
+ await Promise.resolve();
53
+ const content = readTextFromInput(filePathOrBuffer);
54
+ const { nodes, edges } = this.parseDotFile(content);
55
+ const texts = [];
56
+ // Collect node labels
57
+ for (const node of nodes) {
58
+ texts.push(node.label);
59
+ }
60
+ // Collect edge labels
61
+ for (const edge of edges) {
62
+ if (edge.label) {
63
+ texts.push(edge.label);
64
+ }
65
+ }
66
+ return texts;
67
+ }
68
+ async loadIntoTree(filePathOrBuffer) {
69
+ await Promise.resolve();
70
+ const filename = typeof filePathOrBuffer === 'string' ? getBasename(filePathOrBuffer) : 'upload.dot';
71
+ const buffer = readBinaryFromInput(filePathOrBuffer);
72
+ const filesize = buffer.byteLength;
73
+ try {
74
+ const content = readTextFromInput(buffer);
75
+ if (!content || content.trim().length === 0) {
76
+ const validation = buildValidationResultFromMessage({
77
+ filename,
78
+ filesize,
79
+ format: 'dot',
80
+ message: 'DOT file is empty',
81
+ type: 'content',
82
+ description: 'DOT file content',
83
+ });
84
+ throw new ValidationFailureError('Empty DOT content', validation);
85
+ }
86
+ // Check for binary data (contains null bytes or non-printable characters)
87
+ const head = content.substring(0, 100);
88
+ for (let i = 0; i < head.length; i++) {
89
+ const code = head.charCodeAt(i);
90
+ if (code === 0 || (code >= 0 && code <= 8) || (code >= 14 && code <= 31)) {
91
+ const validation = buildValidationResultFromMessage({
92
+ filename,
93
+ filesize,
94
+ format: 'dot',
95
+ message: 'DOT appears to be binary data',
96
+ type: 'content',
97
+ description: 'DOT file content',
98
+ });
99
+ throw new ValidationFailureError('Invalid DOT content', validation);
100
+ }
101
+ }
102
+ const { nodes, edges } = this.parseDotFile(content);
103
+ const tree = new AACTree();
104
+ tree.metadata.format = 'dot';
105
+ // Create pages for each node and add a self button representing the node label
106
+ for (const node of nodes) {
107
+ const page = new AACPage({
108
+ id: node.id,
109
+ name: node.label,
110
+ grid: [],
111
+ buttons: [],
112
+ parentId: null,
113
+ });
114
+ tree.addPage(page);
115
+ // Add a self button so single-node graphs yield one button
116
+ page.addButton(new AACButton({
117
+ id: `${node.id}_self`,
118
+ label: node.label,
119
+ message: node.label,
120
+ semanticAction: {
121
+ intent: AACSemanticIntent.SPEAK_TEXT,
122
+ text: node.label,
123
+ fallback: { type: 'SPEAK', message: node.label },
124
+ },
125
+ }));
126
+ }
127
+ // Create navigation buttons based on edges
128
+ for (const edge of edges) {
129
+ const fromPage = tree.getPage(edge.from);
130
+ if (fromPage) {
131
+ const button = new AACButton({
132
+ id: `nav_${edge.from}_${edge.to}`,
133
+ label: edge.label || edge.to,
134
+ message: '',
135
+ targetPageId: edge.to,
136
+ });
137
+ fromPage.addButton(button);
138
+ }
139
+ }
140
+ return tree;
141
+ }
142
+ catch (error) {
143
+ if (error instanceof ValidationFailureError) {
144
+ throw error;
145
+ }
146
+ const validation = buildValidationResultFromMessage({
147
+ filename,
148
+ filesize,
149
+ format: 'dot',
150
+ message: error?.message || 'Failed to parse DOT file',
151
+ type: 'parse',
152
+ description: 'Parse DOT graph',
153
+ });
154
+ throw new ValidationFailureError('Failed to load DOT file', validation, error);
155
+ }
156
+ }
157
+ async processTexts(filePathOrBuffer, translations, outputPath) {
158
+ await Promise.resolve();
159
+ const content = readTextFromInput(filePathOrBuffer);
160
+ let translatedContent = content;
161
+ translations.forEach((translation, text) => {
162
+ if (typeof text === 'string' && typeof translation === 'string') {
163
+ // Escape special regex characters in the text
164
+ const escapedText = text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
165
+ const escapedTranslation = translation.replace(/\$/g, '$$$$'); // Escape $ in replacement
166
+ translatedContent = translatedContent.replace(new RegExp(`label="${escapedText}"`, 'g'), `label="${escapedTranslation}"`);
167
+ }
168
+ });
169
+ const resultBuffer = encodeText(translatedContent || '');
170
+ writeBinaryToPath(outputPath, resultBuffer);
171
+ return resultBuffer;
172
+ }
173
+ async saveFromTree(tree, _outputPath) {
174
+ await Promise.resolve();
175
+ let dotContent = `digraph "${tree.metadata?.name || 'AACBoard'}" {\n`;
176
+ // Helper to escape DOT string
177
+ const escapeDotString = (str) => {
178
+ return str.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
179
+ };
180
+ if (tree.metadata?.name) {
181
+ dotContent += ` label="${escapeDotString(tree.metadata.name)}";\n`;
182
+ }
183
+ // Add nodes
184
+ for (const pageId in tree.pages) {
185
+ const page = tree.pages[pageId];
186
+ dotContent += ` "${page.id}" [label="${escapeDotString(page.name)}"]\n`;
187
+ }
188
+ // Add edges from navigation buttons (semantic intent or legacy targetPageId)
189
+ for (const pageId in tree.pages) {
190
+ const page = tree.pages[pageId];
191
+ page.buttons
192
+ .filter((btn) => {
193
+ const intentStr = String(btn.semanticAction?.intent);
194
+ return (intentStr === 'NAVIGATE_TO' || !!btn.targetPageId || !!btn.semanticAction?.targetId);
195
+ })
196
+ .forEach((btn) => {
197
+ const target = btn.semanticAction?.targetId || btn.targetPageId;
198
+ if (target) {
199
+ dotContent += ` "${page.id}" -> "${target}" [label="${escapeDotString(btn.label)}"]\n`;
200
+ }
201
+ });
202
+ }
203
+ dotContent += '}\n';
204
+ writeTextToPath(_outputPath, dotContent);
205
+ }
206
+ /**
207
+ * Extract strings with metadata for aac-tools-platform compatibility
208
+ * Uses the generic implementation from BaseProcessor
209
+ */
210
+ async extractStringsWithMetadata(filePath) {
211
+ return this.extractStringsWithMetadataGeneric(filePath);
212
+ }
213
+ /**
214
+ * Generate translated download for aac-tools-platform compatibility
215
+ * Uses the generic implementation from BaseProcessor
216
+ */
217
+ async generateTranslatedDownload(filePath, translatedStrings, sourceStrings) {
218
+ return this.generateTranslatedDownloadGeneric(filePath, translatedStrings, sourceStrings);
219
+ }
220
+ }
221
+ export { DotProcessor };