@willwade/aac-processors 0.0.21 → 0.0.23
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 +19 -27
- package/dist/core/treeStructure.d.ts +2 -2
- package/dist/core/treeStructure.js +4 -1
- package/dist/processors/applePanelsProcessor.js +166 -123
- package/dist/processors/astericsGridProcessor.js +121 -105
- package/dist/processors/dotProcessor.js +83 -65
- package/dist/processors/gridsetProcessor.js +2 -0
- package/dist/processors/obfProcessor.js +11 -4
- package/dist/processors/opmlProcessor.js +82 -44
- package/dist/processors/snapProcessor.js +19 -9
- package/dist/processors/touchchatProcessor.js +72 -21
- package/dist/utilities/analytics/metrics/core.d.ts +1 -1
- package/dist/utilities/analytics/metrics/core.js +191 -212
- package/dist/validation/applePanelsValidator.d.ts +10 -0
- package/dist/validation/applePanelsValidator.js +124 -0
- package/dist/validation/astericsValidator.d.ts +16 -0
- package/dist/validation/astericsValidator.js +115 -0
- package/dist/validation/dotValidator.d.ts +10 -0
- package/dist/validation/dotValidator.js +113 -0
- package/dist/validation/excelValidator.d.ts +10 -0
- package/dist/validation/excelValidator.js +89 -0
- package/dist/validation/index.d.ts +14 -1
- package/dist/validation/index.js +104 -1
- package/dist/validation/obfsetValidator.d.ts +10 -0
- package/dist/validation/obfsetValidator.js +103 -0
- package/dist/validation/opmlValidator.d.ts +10 -0
- package/dist/validation/opmlValidator.js +107 -0
- package/dist/validation/validationTypes.d.ts +22 -0
- package/dist/validation/validationTypes.js +38 -1
- package/dist/validation.d.ts +8 -2
- package/dist/validation.js +16 -1
- package/package.json +1 -1
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.AstericsGridValidator = void 0;
|
|
27
|
+
/* eslint-disable @typescript-eslint/require-await */
|
|
28
|
+
const fs = __importStar(require("fs"));
|
|
29
|
+
const path = __importStar(require("path"));
|
|
30
|
+
const baseValidator_1 = require("./baseValidator");
|
|
31
|
+
/**
|
|
32
|
+
* Validator for Asterics Grid (.grd) JSON files
|
|
33
|
+
*/
|
|
34
|
+
class AstericsGridValidator extends baseValidator_1.BaseValidator {
|
|
35
|
+
/**
|
|
36
|
+
* Validate from disk
|
|
37
|
+
*/
|
|
38
|
+
static async validateFile(filePath) {
|
|
39
|
+
const validator = new AstericsGridValidator();
|
|
40
|
+
const content = fs.readFileSync(filePath);
|
|
41
|
+
const stats = fs.statSync(filePath);
|
|
42
|
+
return validator.validate(content, path.basename(filePath), stats.size);
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Identify whether the content appears to be an Asterics .grd file
|
|
46
|
+
*/
|
|
47
|
+
static async identifyFormat(content, filename) {
|
|
48
|
+
const name = filename.toLowerCase();
|
|
49
|
+
if (name.endsWith('.grd')) {
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
try {
|
|
53
|
+
const str = Buffer.isBuffer(content) ? content.toString('utf-8') : String(content);
|
|
54
|
+
const json = JSON.parse(str);
|
|
55
|
+
return Array.isArray(json?.grids);
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
async validate(content, filename, filesize) {
|
|
62
|
+
this.reset();
|
|
63
|
+
await this.add_check('filename', 'file extension', async () => {
|
|
64
|
+
if (!filename.toLowerCase().endsWith('.grd')) {
|
|
65
|
+
this.warn('filename should end with .grd');
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
let json = null;
|
|
69
|
+
await this.add_check('json_parse', 'valid JSON', async () => {
|
|
70
|
+
try {
|
|
71
|
+
let str = Buffer.isBuffer(content) ? content.toString('utf-8') : String(content);
|
|
72
|
+
if (str.charCodeAt(0) === 0xfeff) {
|
|
73
|
+
str = str.slice(1);
|
|
74
|
+
}
|
|
75
|
+
json = JSON.parse(str);
|
|
76
|
+
}
|
|
77
|
+
catch (e) {
|
|
78
|
+
this.err(`Failed to parse JSON: ${e.message}`, true);
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
if (!json) {
|
|
82
|
+
return this.buildResult(filename, filesize, 'asterics');
|
|
83
|
+
}
|
|
84
|
+
await this.add_check('grids', 'grids array', async () => {
|
|
85
|
+
if (!Array.isArray(json.grids) || json.grids.length === 0) {
|
|
86
|
+
this.err('missing grids array in file', true);
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
const grids = Array.isArray(json.grids) ? json.grids.slice(0, 5) : [];
|
|
90
|
+
grids.forEach((grid, idx) => {
|
|
91
|
+
const prefix = `grid[${idx}]`;
|
|
92
|
+
this.add_check_sync(`${prefix}_id`, `${prefix} id`, () => {
|
|
93
|
+
if (!grid?.id || typeof grid.id !== 'string') {
|
|
94
|
+
this.err('grid is missing an id');
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
this.add_check_sync(`${prefix}_rows`, `${prefix} rowCount`, () => {
|
|
98
|
+
if (typeof grid?.rowCount !== 'number' || grid.rowCount <= 0) {
|
|
99
|
+
this.err('rowCount must be a positive number');
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
this.add_check_sync(`${prefix}_elements`, `${prefix} elements`, () => {
|
|
103
|
+
if (!Array.isArray(grid?.gridElements)) {
|
|
104
|
+
this.err('gridElements must be an array');
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
if (grid.gridElements.length === 0) {
|
|
108
|
+
this.warn('grid has no elements');
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
return this.buildResult(filename, filesize, 'asterics');
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
exports.AstericsGridValidator = AstericsGridValidator;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { BaseValidator } from './baseValidator';
|
|
2
|
+
import { ValidationResult } from './validationTypes';
|
|
3
|
+
/**
|
|
4
|
+
* Validator for Graphviz DOT files
|
|
5
|
+
*/
|
|
6
|
+
export declare class DotValidator extends BaseValidator {
|
|
7
|
+
static validateFile(filePath: string): Promise<ValidationResult>;
|
|
8
|
+
static identifyFormat(content: any, filename: string): Promise<boolean>;
|
|
9
|
+
validate(content: Buffer | Uint8Array, filename: string, filesize: number): Promise<ValidationResult>;
|
|
10
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.DotValidator = void 0;
|
|
27
|
+
/* eslint-disable @typescript-eslint/require-await */
|
|
28
|
+
const fs = __importStar(require("fs"));
|
|
29
|
+
const path = __importStar(require("path"));
|
|
30
|
+
const baseValidator_1 = require("./baseValidator");
|
|
31
|
+
/**
|
|
32
|
+
* Validator for Graphviz DOT files
|
|
33
|
+
*/
|
|
34
|
+
class DotValidator extends baseValidator_1.BaseValidator {
|
|
35
|
+
static async validateFile(filePath) {
|
|
36
|
+
const validator = new DotValidator();
|
|
37
|
+
const content = fs.readFileSync(filePath);
|
|
38
|
+
const stats = fs.statSync(filePath);
|
|
39
|
+
return validator.validate(content, path.basename(filePath), stats.size);
|
|
40
|
+
}
|
|
41
|
+
static async identifyFormat(content, filename) {
|
|
42
|
+
const name = filename.toLowerCase();
|
|
43
|
+
if (name.endsWith('.dot'))
|
|
44
|
+
return true;
|
|
45
|
+
try {
|
|
46
|
+
const str = Buffer.isBuffer(content) ? content.toString('utf-8') : String(content);
|
|
47
|
+
return str.includes('digraph') || str.includes('->');
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
async validate(content, filename, filesize) {
|
|
54
|
+
this.reset();
|
|
55
|
+
await this.add_check('filename', 'file extension', async () => {
|
|
56
|
+
if (!filename.toLowerCase().endsWith('.dot')) {
|
|
57
|
+
this.warn('filename should end with .dot');
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
let text = '';
|
|
61
|
+
await this.add_check('text', 'text content', async () => {
|
|
62
|
+
text = Buffer.isBuffer(content) ? content.toString('utf-8') : String(content);
|
|
63
|
+
if (!text.trim()) {
|
|
64
|
+
this.err('DOT file is empty', true);
|
|
65
|
+
}
|
|
66
|
+
// Basic control character check
|
|
67
|
+
const head = text.substring(0, 200);
|
|
68
|
+
for (let i = 0; i < head.length; i++) {
|
|
69
|
+
const code = head.charCodeAt(i);
|
|
70
|
+
if (code === 0) {
|
|
71
|
+
this.err('DOT appears to be binary data', true);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
if (!text) {
|
|
76
|
+
return this.buildResult(filename, filesize, 'dot');
|
|
77
|
+
}
|
|
78
|
+
let nodes = [];
|
|
79
|
+
let edges = [];
|
|
80
|
+
await this.add_check('structure', 'graph structure', async () => {
|
|
81
|
+
const edgeRegex = /"?([^"\s]+)"?\s*->\s*"?([^"\s]+)"?(?:\s*\[label="([^"]+)"\])?/g;
|
|
82
|
+
let maskedContent = text;
|
|
83
|
+
let edgeMatch;
|
|
84
|
+
edges = [];
|
|
85
|
+
const nodeMap = new Map();
|
|
86
|
+
while ((edgeMatch = edgeRegex.exec(text)) !== null) {
|
|
87
|
+
const [fullMatch, from, to, label] = edgeMatch;
|
|
88
|
+
edges.push({ from, to, label });
|
|
89
|
+
nodeMap.set(from, { id: from, label: from });
|
|
90
|
+
nodeMap.set(to, { id: to, label: to });
|
|
91
|
+
maskedContent = maskedContent.replace(fullMatch, ' '.repeat(fullMatch.length));
|
|
92
|
+
}
|
|
93
|
+
const nodeRegex = /"?([^"\s]+)"?\s*\[label="((?:[^"\\]|\\.)*)"\]/g;
|
|
94
|
+
let nodeMatch;
|
|
95
|
+
while ((nodeMatch = nodeRegex.exec(maskedContent)) !== null) {
|
|
96
|
+
const [, id, rawLabel] = nodeMatch;
|
|
97
|
+
const label = rawLabel.replace(/\\"/g, '"').replace(/\\\\/g, '\\');
|
|
98
|
+
nodeMap.set(id, { id, label });
|
|
99
|
+
}
|
|
100
|
+
nodes = Array.from(nodeMap.values());
|
|
101
|
+
if (nodes.length === 0 && edges.length === 0) {
|
|
102
|
+
this.err('no nodes or edges found in DOT content', true);
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
await this.add_check('connections', 'navigation edges', async () => {
|
|
106
|
+
if (edges.length === 0) {
|
|
107
|
+
this.warn('graph contains no edges; navigation buttons may be missing');
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
return this.buildResult(filename, filesize, 'dot');
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
exports.DotValidator = DotValidator;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { BaseValidator } from './baseValidator';
|
|
2
|
+
import { ValidationResult } from './validationTypes';
|
|
3
|
+
/**
|
|
4
|
+
* Validator for Excel imports (.xlsx/.xls)
|
|
5
|
+
*/
|
|
6
|
+
export declare class ExcelValidator extends BaseValidator {
|
|
7
|
+
static validateFile(filePath: string): Promise<ValidationResult>;
|
|
8
|
+
static identifyFormat(_content: any, filename: string): Promise<boolean>;
|
|
9
|
+
validate(content: Buffer | Uint8Array, filename: string, filesize: number): Promise<ValidationResult>;
|
|
10
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.ExcelValidator = void 0;
|
|
27
|
+
/* eslint-disable @typescript-eslint/require-await */
|
|
28
|
+
const fs = __importStar(require("fs"));
|
|
29
|
+
const path = __importStar(require("path"));
|
|
30
|
+
const ExcelJS = __importStar(require("exceljs"));
|
|
31
|
+
const baseValidator_1 = require("./baseValidator");
|
|
32
|
+
/**
|
|
33
|
+
* Validator for Excel imports (.xlsx/.xls)
|
|
34
|
+
*/
|
|
35
|
+
class ExcelValidator extends baseValidator_1.BaseValidator {
|
|
36
|
+
static async validateFile(filePath) {
|
|
37
|
+
const validator = new ExcelValidator();
|
|
38
|
+
const content = fs.readFileSync(filePath);
|
|
39
|
+
const stats = fs.statSync(filePath);
|
|
40
|
+
return validator.validate(content, path.basename(filePath), stats.size);
|
|
41
|
+
}
|
|
42
|
+
static async identifyFormat(_content, filename) {
|
|
43
|
+
const name = filename.toLowerCase();
|
|
44
|
+
return name.endsWith('.xlsx') || name.endsWith('.xls');
|
|
45
|
+
}
|
|
46
|
+
async validate(content, filename, filesize) {
|
|
47
|
+
this.reset();
|
|
48
|
+
const ext = filename.toLowerCase().split('.').pop() || '';
|
|
49
|
+
await this.add_check('filename', 'file extension', async () => {
|
|
50
|
+
if (!['xlsx', 'xls'].includes(ext)) {
|
|
51
|
+
this.warn('filename should end with .xlsx or .xls');
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
if (ext === 'xls') {
|
|
55
|
+
// exceljs cannot parse legacy .xls files
|
|
56
|
+
await this.add_check('xls_support', 'legacy Excel format', async () => {
|
|
57
|
+
this.err('legacy .xls files are not supported; please provide .xlsx', true);
|
|
58
|
+
});
|
|
59
|
+
return this.buildResult(filename, filesize, 'excel');
|
|
60
|
+
}
|
|
61
|
+
const buffer = Buffer.isBuffer(content) ? content : Buffer.from(content);
|
|
62
|
+
const workbook = new ExcelJS.Workbook();
|
|
63
|
+
await this.add_check('open', 'open workbook', async () => {
|
|
64
|
+
try {
|
|
65
|
+
await workbook.xlsx.load(buffer);
|
|
66
|
+
}
|
|
67
|
+
catch (e) {
|
|
68
|
+
this.err(`Failed to read Excel workbook: ${e.message}`, true);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
await this.add_check('worksheets', 'worksheets exist', async () => {
|
|
72
|
+
if (workbook.worksheets.length === 0) {
|
|
73
|
+
this.err('Excel workbook has no worksheets', true);
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
const firstSheet = workbook.worksheets[0];
|
|
77
|
+
if (firstSheet) {
|
|
78
|
+
await this.add_check('content', 'worksheet has content', async () => {
|
|
79
|
+
const rows = firstSheet.actualRowCount || firstSheet.rowCount;
|
|
80
|
+
const cols = firstSheet.columnCount;
|
|
81
|
+
if (!rows || !cols) {
|
|
82
|
+
this.err('first worksheet is empty', true);
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
return this.buildResult(filename, filesize, 'excel');
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
exports.ExcelValidator = ExcelValidator;
|
|
@@ -2,12 +2,25 @@
|
|
|
2
2
|
* Validation system for AAC processors
|
|
3
3
|
* Provides consistent validation across all supported formats
|
|
4
4
|
*/
|
|
5
|
-
export { ValidationError, ValidationCheck, ValidationResult, ValidationOptions, ValidationRule, } from './validationTypes';
|
|
5
|
+
export { ValidationError, ValidationCheck, ValidationResult, ValidationOptions, ValidationRule, ValidationFailureError, buildValidationResultFromMessage, } from './validationTypes';
|
|
6
6
|
export { BaseValidator } from './baseValidator';
|
|
7
7
|
export { ObfValidator } from './obfValidator';
|
|
8
8
|
export { GridsetValidator } from './gridsetValidator';
|
|
9
9
|
export { SnapValidator } from './snapValidator';
|
|
10
10
|
export { TouchChatValidator } from './touchChatValidator';
|
|
11
|
+
export { AstericsGridValidator } from './astericsValidator';
|
|
12
|
+
export { ExcelValidator } from './excelValidator';
|
|
13
|
+
export { OpmlValidator } from './opmlValidator';
|
|
14
|
+
export { DotValidator } from './dotValidator';
|
|
15
|
+
export { ApplePanelsValidator } from './applePanelsValidator';
|
|
16
|
+
export { ObfsetValidator } from './obfsetValidator';
|
|
11
17
|
import { BaseValidator } from './baseValidator';
|
|
18
|
+
import { ValidationResult } from './validationTypes';
|
|
12
19
|
export declare function getValidatorForFormat(format: string): BaseValidator | null;
|
|
13
20
|
export declare function getValidatorForFile(filename: string): BaseValidator | null;
|
|
21
|
+
/**
|
|
22
|
+
* Convenience helper to validate either a file path or a Buffer/Uint8Array.
|
|
23
|
+
* When a file path is provided, any validator-specific validateFile() helper
|
|
24
|
+
* will be used if available to access nested resources.
|
|
25
|
+
*/
|
|
26
|
+
export declare function validateFileOrBuffer(filePathOrBuffer: string | Buffer, filenameHint?: string): Promise<ValidationResult>;
|
package/dist/validation/index.js
CHANGED
|
@@ -3,12 +3,38 @@
|
|
|
3
3
|
* Validation system for AAC processors
|
|
4
4
|
* Provides consistent validation across all supported formats
|
|
5
5
|
*/
|
|
6
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
7
|
+
if (k2 === undefined) k2 = k;
|
|
8
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
9
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
10
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
11
|
+
}
|
|
12
|
+
Object.defineProperty(o, k2, desc);
|
|
13
|
+
}) : (function(o, m, k, k2) {
|
|
14
|
+
if (k2 === undefined) k2 = k;
|
|
15
|
+
o[k2] = m[k];
|
|
16
|
+
}));
|
|
17
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
18
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
19
|
+
}) : function(o, v) {
|
|
20
|
+
o["default"] = v;
|
|
21
|
+
});
|
|
22
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
23
|
+
if (mod && mod.__esModule) return mod;
|
|
24
|
+
var result = {};
|
|
25
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
26
|
+
__setModuleDefault(result, mod);
|
|
27
|
+
return result;
|
|
28
|
+
};
|
|
6
29
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
exports.TouchChatValidator = exports.SnapValidator = exports.GridsetValidator = exports.ObfValidator = exports.BaseValidator = exports.ValidationError = void 0;
|
|
30
|
+
exports.ObfsetValidator = exports.ApplePanelsValidator = exports.DotValidator = exports.OpmlValidator = exports.ExcelValidator = exports.AstericsGridValidator = exports.TouchChatValidator = exports.SnapValidator = exports.GridsetValidator = exports.ObfValidator = exports.BaseValidator = exports.buildValidationResultFromMessage = exports.ValidationFailureError = exports.ValidationError = void 0;
|
|
8
31
|
exports.getValidatorForFormat = getValidatorForFormat;
|
|
9
32
|
exports.getValidatorForFile = getValidatorForFile;
|
|
33
|
+
exports.validateFileOrBuffer = validateFileOrBuffer;
|
|
10
34
|
var validationTypes_1 = require("./validationTypes");
|
|
11
35
|
Object.defineProperty(exports, "ValidationError", { enumerable: true, get: function () { return validationTypes_1.ValidationError; } });
|
|
36
|
+
Object.defineProperty(exports, "ValidationFailureError", { enumerable: true, get: function () { return validationTypes_1.ValidationFailureError; } });
|
|
37
|
+
Object.defineProperty(exports, "buildValidationResultFromMessage", { enumerable: true, get: function () { return validationTypes_1.buildValidationResultFromMessage; } });
|
|
12
38
|
var baseValidator_1 = require("./baseValidator");
|
|
13
39
|
Object.defineProperty(exports, "BaseValidator", { enumerable: true, get: function () { return baseValidator_1.BaseValidator; } });
|
|
14
40
|
// Individual format validators
|
|
@@ -20,6 +46,18 @@ var snapValidator_1 = require("./snapValidator");
|
|
|
20
46
|
Object.defineProperty(exports, "SnapValidator", { enumerable: true, get: function () { return snapValidator_1.SnapValidator; } });
|
|
21
47
|
var touchChatValidator_1 = require("./touchChatValidator");
|
|
22
48
|
Object.defineProperty(exports, "TouchChatValidator", { enumerable: true, get: function () { return touchChatValidator_1.TouchChatValidator; } });
|
|
49
|
+
var astericsValidator_1 = require("./astericsValidator");
|
|
50
|
+
Object.defineProperty(exports, "AstericsGridValidator", { enumerable: true, get: function () { return astericsValidator_1.AstericsGridValidator; } });
|
|
51
|
+
var excelValidator_1 = require("./excelValidator");
|
|
52
|
+
Object.defineProperty(exports, "ExcelValidator", { enumerable: true, get: function () { return excelValidator_1.ExcelValidator; } });
|
|
53
|
+
var opmlValidator_1 = require("./opmlValidator");
|
|
54
|
+
Object.defineProperty(exports, "OpmlValidator", { enumerable: true, get: function () { return opmlValidator_1.OpmlValidator; } });
|
|
55
|
+
var dotValidator_1 = require("./dotValidator");
|
|
56
|
+
Object.defineProperty(exports, "DotValidator", { enumerable: true, get: function () { return dotValidator_1.DotValidator; } });
|
|
57
|
+
var applePanelsValidator_1 = require("./applePanelsValidator");
|
|
58
|
+
Object.defineProperty(exports, "ApplePanelsValidator", { enumerable: true, get: function () { return applePanelsValidator_1.ApplePanelsValidator; } });
|
|
59
|
+
var obfsetValidator_1 = require("./obfsetValidator");
|
|
60
|
+
Object.defineProperty(exports, "ObfsetValidator", { enumerable: true, get: function () { return obfsetValidator_1.ObfsetValidator; } });
|
|
23
61
|
/**
|
|
24
62
|
* Main validator factory
|
|
25
63
|
* Returns the appropriate validator for a given format
|
|
@@ -28,6 +66,14 @@ const obfValidator_2 = require("./obfValidator");
|
|
|
28
66
|
const gridsetValidator_2 = require("./gridsetValidator");
|
|
29
67
|
const snapValidator_2 = require("./snapValidator");
|
|
30
68
|
const touchChatValidator_2 = require("./touchChatValidator");
|
|
69
|
+
const astericsValidator_2 = require("./astericsValidator");
|
|
70
|
+
const excelValidator_2 = require("./excelValidator");
|
|
71
|
+
const opmlValidator_2 = require("./opmlValidator");
|
|
72
|
+
const dotValidator_2 = require("./dotValidator");
|
|
73
|
+
const applePanelsValidator_2 = require("./applePanelsValidator");
|
|
74
|
+
const obfsetValidator_2 = require("./obfsetValidator");
|
|
75
|
+
const fs = __importStar(require("fs"));
|
|
76
|
+
const path = __importStar(require("path"));
|
|
31
77
|
function getValidatorForFormat(format) {
|
|
32
78
|
switch (format.toLowerCase()) {
|
|
33
79
|
case 'obf':
|
|
@@ -43,6 +89,23 @@ function getValidatorForFormat(format) {
|
|
|
43
89
|
case 'touchchat':
|
|
44
90
|
case 'ce':
|
|
45
91
|
return new touchChatValidator_2.TouchChatValidator();
|
|
92
|
+
case 'asterics':
|
|
93
|
+
case 'grd':
|
|
94
|
+
return new astericsValidator_2.AstericsGridValidator();
|
|
95
|
+
case 'excel':
|
|
96
|
+
case 'xlsx':
|
|
97
|
+
case 'xls':
|
|
98
|
+
return new excelValidator_2.ExcelValidator();
|
|
99
|
+
case 'opml':
|
|
100
|
+
return new opmlValidator_2.OpmlValidator();
|
|
101
|
+
case 'dot':
|
|
102
|
+
return new dotValidator_2.DotValidator();
|
|
103
|
+
case 'applepanels':
|
|
104
|
+
case 'plist':
|
|
105
|
+
case 'ascconfig':
|
|
106
|
+
return new applePanelsValidator_2.ApplePanelsValidator();
|
|
107
|
+
case 'obfset':
|
|
108
|
+
return new obfsetValidator_2.ObfsetValidator();
|
|
46
109
|
default:
|
|
47
110
|
return null;
|
|
48
111
|
}
|
|
@@ -63,7 +126,47 @@ function getValidatorForFile(filename) {
|
|
|
63
126
|
return new snapValidator_2.SnapValidator();
|
|
64
127
|
case 'ce':
|
|
65
128
|
return new touchChatValidator_2.TouchChatValidator();
|
|
129
|
+
case 'grd':
|
|
130
|
+
return new astericsValidator_2.AstericsGridValidator();
|
|
131
|
+
case 'xlsx':
|
|
132
|
+
case 'xls':
|
|
133
|
+
return new excelValidator_2.ExcelValidator();
|
|
134
|
+
case 'opml':
|
|
135
|
+
return new opmlValidator_2.OpmlValidator();
|
|
136
|
+
case 'dot':
|
|
137
|
+
return new dotValidator_2.DotValidator();
|
|
138
|
+
case 'plist':
|
|
139
|
+
case 'ascconfig':
|
|
140
|
+
return new applePanelsValidator_2.ApplePanelsValidator();
|
|
141
|
+
case 'obfset':
|
|
142
|
+
return new obfsetValidator_2.ObfsetValidator();
|
|
66
143
|
default:
|
|
67
144
|
return null;
|
|
68
145
|
}
|
|
69
146
|
}
|
|
147
|
+
/**
|
|
148
|
+
* Convenience helper to validate either a file path or a Buffer/Uint8Array.
|
|
149
|
+
* When a file path is provided, any validator-specific validateFile() helper
|
|
150
|
+
* will be used if available to access nested resources.
|
|
151
|
+
*/
|
|
152
|
+
async function validateFileOrBuffer(filePathOrBuffer, filenameHint) {
|
|
153
|
+
const isPath = typeof filePathOrBuffer === 'string';
|
|
154
|
+
const name = filenameHint || (isPath ? path.basename(filePathOrBuffer) : 'upload');
|
|
155
|
+
const validator = getValidatorForFile(name) || getValidatorForFormat(name);
|
|
156
|
+
if (!validator) {
|
|
157
|
+
throw new Error(`No validator registered for ${name}`);
|
|
158
|
+
}
|
|
159
|
+
if (isPath) {
|
|
160
|
+
const ctor = validator.constructor;
|
|
161
|
+
if (typeof ctor.validateFile === 'function') {
|
|
162
|
+
return ctor.validateFile(filePathOrBuffer);
|
|
163
|
+
}
|
|
164
|
+
const buf = fs.readFileSync(filePathOrBuffer);
|
|
165
|
+
const stats = fs.statSync(filePathOrBuffer);
|
|
166
|
+
return validator.validate(buf, path.basename(filePathOrBuffer), stats.size);
|
|
167
|
+
}
|
|
168
|
+
const buffer = Buffer.isBuffer(filePathOrBuffer)
|
|
169
|
+
? filePathOrBuffer
|
|
170
|
+
: Buffer.from(filePathOrBuffer);
|
|
171
|
+
return validator.validate(buffer, name, buffer.byteLength);
|
|
172
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { BaseValidator } from './baseValidator';
|
|
2
|
+
import { ValidationResult } from './validationTypes';
|
|
3
|
+
/**
|
|
4
|
+
* Validator for OBF set bundles (.obfset) - JSON arrays of boards
|
|
5
|
+
*/
|
|
6
|
+
export declare class ObfsetValidator extends BaseValidator {
|
|
7
|
+
static validateFile(filePath: string): Promise<ValidationResult>;
|
|
8
|
+
static identifyFormat(content: any, filename: string): Promise<boolean>;
|
|
9
|
+
validate(content: Buffer | Uint8Array, filename: string, filesize: number): Promise<ValidationResult>;
|
|
10
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.ObfsetValidator = void 0;
|
|
27
|
+
/* eslint-disable @typescript-eslint/require-await */
|
|
28
|
+
const fs = __importStar(require("fs"));
|
|
29
|
+
const path = __importStar(require("path"));
|
|
30
|
+
const baseValidator_1 = require("./baseValidator");
|
|
31
|
+
/**
|
|
32
|
+
* Validator for OBF set bundles (.obfset) - JSON arrays of boards
|
|
33
|
+
*/
|
|
34
|
+
class ObfsetValidator extends baseValidator_1.BaseValidator {
|
|
35
|
+
static async validateFile(filePath) {
|
|
36
|
+
const validator = new ObfsetValidator();
|
|
37
|
+
const content = fs.readFileSync(filePath);
|
|
38
|
+
const stats = fs.statSync(filePath);
|
|
39
|
+
return validator.validate(content, path.basename(filePath), stats.size);
|
|
40
|
+
}
|
|
41
|
+
static async identifyFormat(content, filename) {
|
|
42
|
+
const name = filename.toLowerCase();
|
|
43
|
+
if (name.endsWith('.obfset'))
|
|
44
|
+
return true;
|
|
45
|
+
try {
|
|
46
|
+
const str = Buffer.isBuffer(content) ? content.toString('utf-8') : String(content);
|
|
47
|
+
const parsed = JSON.parse(str);
|
|
48
|
+
return Array.isArray(parsed);
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
async validate(content, filename, filesize) {
|
|
55
|
+
this.reset();
|
|
56
|
+
await this.add_check('filename', 'file extension', async () => {
|
|
57
|
+
if (!filename.toLowerCase().endsWith('.obfset')) {
|
|
58
|
+
this.warn('filename should end with .obfset');
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
let boards = null;
|
|
62
|
+
await this.add_check('json_parse', 'valid JSON array', async () => {
|
|
63
|
+
try {
|
|
64
|
+
const str = Buffer.isBuffer(content) ? content.toString('utf-8') : String(content);
|
|
65
|
+
const parsed = JSON.parse(str);
|
|
66
|
+
if (!Array.isArray(parsed)) {
|
|
67
|
+
this.err('root must be a JSON array of boards', true);
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
boards = parsed;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
catch (e) {
|
|
74
|
+
this.err(`Failed to parse JSON: ${e.message}`, true);
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
if (!boards) {
|
|
78
|
+
return this.buildResult(filename, filesize, 'obfset');
|
|
79
|
+
}
|
|
80
|
+
const safeBoards = boards;
|
|
81
|
+
safeBoards.slice(0, 5).forEach((board, idx) => {
|
|
82
|
+
const prefix = `board[${idx}]`;
|
|
83
|
+
this.add_check_sync(`${prefix}_id`, `${prefix} id`, () => {
|
|
84
|
+
if (!board?.id) {
|
|
85
|
+
this.err('board is missing id');
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
this.add_check_sync(`${prefix}_buttons`, `${prefix} buttons`, () => {
|
|
89
|
+
if (!Array.isArray(board?.buttons)) {
|
|
90
|
+
this.warn('board has no buttons array');
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
this.add_check_sync(`${prefix}_grid`, `${prefix} grid definition`, () => {
|
|
94
|
+
const grid = board?.grid;
|
|
95
|
+
if (!grid || typeof grid.rows !== 'number' || typeof grid.columns !== 'number') {
|
|
96
|
+
this.warn('grid rows/columns missing; layout may be invalid');
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
return this.buildResult(filename, filesize, 'obfset');
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
exports.ObfsetValidator = ObfsetValidator;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { BaseValidator } from './baseValidator';
|
|
2
|
+
import { ValidationResult } from './validationTypes';
|
|
3
|
+
/**
|
|
4
|
+
* Validator for OPML files
|
|
5
|
+
*/
|
|
6
|
+
export declare class OpmlValidator extends BaseValidator {
|
|
7
|
+
static validateFile(filePath: string): Promise<ValidationResult>;
|
|
8
|
+
static identifyFormat(content: any, filename: string): Promise<boolean>;
|
|
9
|
+
validate(content: Buffer | Uint8Array, filename: string, filesize: number): Promise<ValidationResult>;
|
|
10
|
+
}
|