@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.
- package/README.md +52 -852
- package/dist/browser/core/baseProcessor.js +241 -0
- package/dist/browser/core/stringCasing.js +179 -0
- package/dist/browser/core/treeStructure.js +255 -0
- package/dist/browser/index.browser.js +73 -0
- package/dist/browser/processors/applePanelsProcessor.js +582 -0
- package/dist/browser/processors/astericsGridProcessor.js +1509 -0
- package/dist/browser/processors/dotProcessor.js +221 -0
- package/dist/browser/processors/gridset/commands.js +962 -0
- package/dist/browser/processors/gridset/crypto.js +53 -0
- package/dist/browser/processors/gridset/password.js +43 -0
- package/dist/browser/processors/gridset/pluginTypes.js +277 -0
- package/dist/browser/processors/gridset/resolver.js +137 -0
- package/dist/browser/processors/gridset/symbolAlignment.js +276 -0
- package/dist/browser/processors/gridset/symbols.js +421 -0
- package/dist/browser/processors/gridsetProcessor.js +2002 -0
- package/dist/browser/processors/obfProcessor.js +705 -0
- package/dist/browser/processors/opmlProcessor.js +274 -0
- package/dist/browser/types/aac.js +38 -0
- package/dist/browser/utilities/analytics/utils/idGenerator.js +89 -0
- package/dist/browser/utilities/translation/translationProcessor.js +200 -0
- package/dist/browser/utils/io.js +95 -0
- package/dist/browser/validation/baseValidator.js +156 -0
- package/dist/browser/validation/gridsetValidator.js +355 -0
- package/dist/browser/validation/obfValidator.js +500 -0
- package/dist/browser/validation/validationTypes.js +46 -0
- package/dist/cli/index.js +5 -5
- package/dist/core/analyze.d.ts +2 -2
- package/dist/core/analyze.js +2 -2
- package/dist/core/baseProcessor.d.ts +5 -4
- package/dist/core/baseProcessor.js +22 -27
- package/dist/core/treeStructure.d.ts +5 -5
- package/dist/core/treeStructure.js +1 -4
- package/dist/index.browser.d.ts +37 -0
- package/dist/index.browser.js +99 -0
- package/dist/index.d.ts +1 -48
- package/dist/index.js +1 -136
- package/dist/index.node.d.ts +48 -0
- package/dist/index.node.js +152 -0
- package/dist/processors/applePanelsProcessor.d.ts +5 -4
- package/dist/processors/applePanelsProcessor.js +58 -62
- package/dist/processors/astericsGridProcessor.d.ts +7 -6
- package/dist/processors/astericsGridProcessor.js +31 -42
- package/dist/processors/dotProcessor.d.ts +5 -4
- package/dist/processors/dotProcessor.js +25 -33
- package/dist/processors/excelProcessor.d.ts +4 -3
- package/dist/processors/excelProcessor.js +6 -3
- package/dist/processors/gridset/crypto.d.ts +18 -0
- package/dist/processors/gridset/crypto.js +57 -0
- package/dist/processors/gridset/helpers.d.ts +1 -1
- package/dist/processors/gridset/helpers.js +18 -8
- package/dist/processors/gridset/password.d.ts +20 -3
- package/dist/processors/gridset/password.js +17 -3
- package/dist/processors/gridset/wordlistHelpers.d.ts +3 -3
- package/dist/processors/gridset/wordlistHelpers.js +21 -20
- package/dist/processors/gridsetProcessor.d.ts +7 -12
- package/dist/processors/gridsetProcessor.js +116 -77
- package/dist/processors/obfProcessor.d.ts +9 -7
- package/dist/processors/obfProcessor.js +131 -56
- package/dist/processors/obfsetProcessor.d.ts +5 -4
- package/dist/processors/obfsetProcessor.js +10 -16
- package/dist/processors/opmlProcessor.d.ts +5 -4
- package/dist/processors/opmlProcessor.js +27 -34
- package/dist/processors/snapProcessor.d.ts +8 -7
- package/dist/processors/snapProcessor.js +15 -12
- package/dist/processors/touchchatProcessor.d.ts +8 -7
- package/dist/processors/touchchatProcessor.js +22 -17
- package/dist/types/aac.d.ts +0 -2
- package/dist/types/aac.js +2 -0
- package/dist/utils/io.d.ts +12 -0
- package/dist/utils/io.js +107 -0
- package/dist/validation/gridsetValidator.js +7 -7
- package/dist/validation/snapValidator.js +28 -35
- package/docs/BROWSER_USAGE.md +618 -0
- package/examples/README.md +77 -0
- package/examples/browser-test-server.js +81 -0
- package/examples/browser-test.html +331 -0
- package/examples/vitedemo/QUICKSTART.md +74 -0
- package/examples/vitedemo/README.md +157 -0
- package/examples/vitedemo/index.html +376 -0
- package/examples/vitedemo/package-lock.json +1221 -0
- package/examples/vitedemo/package.json +18 -0
- package/examples/vitedemo/src/main.ts +519 -0
- package/examples/vitedemo/test-files/example.dot +14 -0
- package/examples/vitedemo/test-files/example.grd +1 -0
- package/examples/vitedemo/test-files/example.gridset +0 -0
- package/examples/vitedemo/test-files/example.obz +0 -0
- package/examples/vitedemo/test-files/example.opml +18 -0
- package/examples/vitedemo/test-files/simple.obf +53 -0
- package/examples/vitedemo/tsconfig.json +24 -0
- package/examples/vitedemo/vite.config.ts +34 -0
- 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 };
|