@willwade/aac-processors 0.1.6 → 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.
@@ -72,8 +72,22 @@ export function isNodeRuntime() {
72
72
  return typeof process !== 'undefined' && !!process.versions?.node;
73
73
  }
74
74
  export function getBasename(filePath) {
75
- const parts = filePath.split(/[/\\]/);
76
- return parts[parts.length - 1] || filePath;
75
+ const trimmed = filePath.replace(/[/\\]+$/, '') || filePath;
76
+ const parts = trimmed.split(/[/\\]/);
77
+ return parts[parts.length - 1] || trimmed;
78
+ }
79
+ export function toUint8Array(input) {
80
+ if (input instanceof Uint8Array) {
81
+ return input;
82
+ }
83
+ return new Uint8Array(input);
84
+ }
85
+ export function toArrayBuffer(input) {
86
+ if (input instanceof ArrayBuffer) {
87
+ return input;
88
+ }
89
+ const view = input instanceof Uint8Array ? input : new Uint8Array(input);
90
+ return view.buffer.slice(view.byteOffset, view.byteOffset + view.byteLength);
77
91
  }
78
92
  export function decodeText(input) {
79
93
  if (typeof Buffer !== 'undefined' && Buffer.isBuffer(input)) {
@@ -2,24 +2,9 @@
2
2
  /* eslint-disable @typescript-eslint/no-unsafe-argument */
3
3
  /* eslint-disable @typescript-eslint/no-unsafe-return */
4
4
  import JSZip from 'jszip';
5
+ import * as xml2js from 'xml2js';
5
6
  import { BaseValidator } from './baseValidator';
6
- import { getFs, getNodeRequire, getPath } from '../utils/io';
7
- let cachedXml2js = null;
8
- function getXml2js() {
9
- if (cachedXml2js)
10
- return cachedXml2js;
11
- try {
12
- const nodeRequire = getNodeRequire();
13
- // eslint-disable-next-line @typescript-eslint/no-var-requires
14
- const module = nodeRequire('xml2js');
15
- const resolved = module.default || module;
16
- cachedXml2js = resolved;
17
- return resolved;
18
- }
19
- catch {
20
- throw new Error('Validator requires Xml2js in this environment.');
21
- }
22
- }
7
+ import { decodeText, getBasename, getFs, toUint8Array } from '../utils/io';
23
8
  /**
24
9
  * Validator for Grid3/Smartbox Gridset files (.gridset, .gridsetx)
25
10
  */
@@ -33,10 +18,9 @@ export class GridsetValidator extends BaseValidator {
33
18
  static async validateFile(filePath) {
34
19
  const validator = new GridsetValidator();
35
20
  const fs = getFs();
36
- const path = getPath();
37
21
  const content = fs.readFileSync(filePath);
38
22
  const stats = fs.statSync(filePath);
39
- return validator.validate(content, path.basename(filePath), stats.size);
23
+ return validator.validate(content, getBasename(filePath), stats.size);
40
24
  }
41
25
  /**
42
26
  * Check if content is Gridset format
@@ -48,8 +32,7 @@ export class GridsetValidator extends BaseValidator {
48
32
  }
49
33
  // Try to parse as XML and check for gridset structure
50
34
  try {
51
- const contentStr = Buffer.isBuffer(content) ? content.toString('utf-8') : content;
52
- const xml2js = getXml2js();
35
+ const contentStr = typeof content === 'string' ? content : decodeText(toUint8Array(content));
53
36
  const parser = new xml2js.Parser();
54
37
  const result = await parser.parseStringPromise(contentStr);
55
38
  return result && (result.gridset || result.Gridset);
@@ -102,9 +85,8 @@ export class GridsetValidator extends BaseValidator {
102
85
  let xmlObj = null;
103
86
  await this.add_check('xml_parse', 'valid XML', async () => {
104
87
  try {
105
- const xml2js = getXml2js();
106
88
  const parser = new xml2js.Parser();
107
- const contentStr = content.toString('utf-8');
89
+ const contentStr = decodeText(content);
108
90
  xmlObj = await parser.parseStringPromise(contentStr);
109
91
  }
110
92
  catch (e) {
@@ -129,7 +111,7 @@ export class GridsetValidator extends BaseValidator {
129
111
  async validateZipArchive(content, filename, _filesize) {
130
112
  let zip;
131
113
  try {
132
- zip = await JSZip.loadAsync(Buffer.from(content));
114
+ zip = await JSZip.loadAsync(toUint8Array(content));
133
115
  }
134
116
  catch (e) {
135
117
  this.err(`Failed to open ZIP archive: ${e.message}`, true);
@@ -145,7 +127,6 @@ export class GridsetValidator extends BaseValidator {
145
127
  else {
146
128
  try {
147
129
  const gridsetXml = await gridsetEntry.async('string');
148
- const xml2js = getXml2js();
149
130
  const parser = new xml2js.Parser();
150
131
  const xmlObj = await parser.parseStringPromise(gridsetXml);
151
132
  const gridset = xmlObj.gridset || xmlObj.Gridset;
@@ -153,7 +134,7 @@ export class GridsetValidator extends BaseValidator {
153
134
  this.err('Invalid gridset.xml structure', true);
154
135
  }
155
136
  else {
156
- await this.validateGridsetStructure(gridset, filename, Buffer.from(gridsetXml));
137
+ await this.validateGridsetStructure(gridset, filename, new Uint8Array());
157
138
  }
158
139
  }
159
140
  catch (e) {
@@ -170,7 +151,6 @@ export class GridsetValidator extends BaseValidator {
170
151
  else {
171
152
  try {
172
153
  const settingsXml = await settingsEntry.async('string');
173
- const xml2js = getXml2js();
174
154
  const parser = new xml2js.Parser();
175
155
  const xmlObj = await parser.parseStringPromise(settingsXml);
176
156
  const settings = xmlObj.GridSetSettings || xmlObj.gridSetSettings || xmlObj.GridsetSettings;
@@ -5,7 +5,7 @@
5
5
  /* eslint-disable @typescript-eslint/restrict-template-expressions */
6
6
  import JSZip from 'jszip';
7
7
  import { BaseValidator } from './baseValidator';
8
- import { getFs, getPath, readBinaryFromInput } from '../utils/io';
8
+ import { decodeText, getBasename, getFs, readBinaryFromInput, toUint8Array } from '../utils/io';
9
9
  const OBF_FORMAT = 'open-board-0.1';
10
10
  const OBF_FORMAT_CURRENT_VERSION = 0.1;
11
11
  /**
@@ -22,7 +22,7 @@ export class ObfValidator extends BaseValidator {
22
22
  const validator = new ObfValidator();
23
23
  const content = readBinaryFromInput(filePath);
24
24
  const stats = getFs().statSync(filePath);
25
- return validator.validate(content, getPath().basename(filePath), stats.size);
25
+ return validator.validate(content, getBasename(filePath), stats.size);
26
26
  }
27
27
  /**
28
28
  * Check if content is OBF format
@@ -34,7 +34,12 @@ export class ObfValidator extends BaseValidator {
34
34
  }
35
35
  // Try to parse as JSON and check format
36
36
  try {
37
- const contentStr = Buffer.isBuffer(content) ? content.toString() : content;
37
+ if (typeof content !== 'string' &&
38
+ !(content instanceof ArrayBuffer) &&
39
+ !(content instanceof Uint8Array)) {
40
+ return false;
41
+ }
42
+ const contentStr = typeof content === 'string' ? content : decodeText(toUint8Array(content));
38
43
  const json = JSON.parse(contentStr);
39
44
  return json && json.format && json.format.startsWith('open-board-');
40
45
  }
@@ -68,7 +73,7 @@ export class ObfValidator extends BaseValidator {
68
73
  let json = null;
69
74
  await this.add_check('valid_json', 'JSON file', async () => {
70
75
  try {
71
- json = JSON.parse(content.toString());
76
+ json = JSON.parse(decodeText(content));
72
77
  }
73
78
  catch {
74
79
  this.err("Couldn't parse as JSON", true);
@@ -1,10 +1,9 @@
1
1
  /* eslint-disable @typescript-eslint/require-await */
2
2
  /* eslint-disable @typescript-eslint/no-unsafe-argument */
3
- import * as fs from 'fs';
4
- import * as path from 'path';
5
3
  import * as xml2js from 'xml2js';
6
4
  import JSZip from 'jszip';
7
5
  import { BaseValidator } from './baseValidator';
6
+ import { getBasename, getFs, readBinaryFromInput, toUint8Array } from '../utils/io';
8
7
  /**
9
8
  * Validator for Snap files (.spb, .sps)
10
9
  * Snap files are zipped packages containing XML configuration
@@ -18,9 +17,9 @@ export class SnapValidator extends BaseValidator {
18
17
  */
19
18
  static async validateFile(filePath) {
20
19
  const validator = new SnapValidator();
21
- const content = fs.readFileSync(filePath);
22
- const stats = fs.statSync(filePath);
23
- return validator.validate(content, path.basename(filePath), stats.size);
20
+ const content = readBinaryFromInput(filePath);
21
+ const stats = getFs().statSync(filePath);
22
+ return validator.validate(content, getBasename(filePath), stats.size);
24
23
  }
25
24
  /**
26
25
  * Check if content is Snap format
@@ -33,8 +32,7 @@ export class SnapValidator extends BaseValidator {
33
32
  }
34
33
  // Try to parse as ZIP and check for Snap structure
35
34
  try {
36
- const buffer = Buffer.isBuffer(content) ? content : Buffer.from(content);
37
- const zip = await JSZip.loadAsync(buffer);
35
+ const zip = await JSZip.loadAsync(toUint8Array(content));
38
36
  const entries = Object.values(zip.files).filter((entry) => !entry.dir);
39
37
  return entries.some((entry) => entry.name.includes('settings') || entry.name.includes('.xml'));
40
38
  }
@@ -56,8 +54,7 @@ export class SnapValidator extends BaseValidator {
56
54
  let validZip = false;
57
55
  await this.add_check('zip', 'valid zip package', async () => {
58
56
  try {
59
- const buffer = Buffer.isBuffer(content) ? content : Buffer.from(content);
60
- zip = await JSZip.loadAsync(buffer);
57
+ zip = await JSZip.loadAsync(toUint8Array(content));
61
58
  const entries = Object.values(zip.files);
62
59
  validZip = entries.length > 0;
63
60
  }
@@ -1,10 +1,9 @@
1
1
  /* eslint-disable @typescript-eslint/require-await */
2
2
  /* eslint-disable @typescript-eslint/no-unsafe-argument */
3
3
  /* eslint-disable @typescript-eslint/no-unsafe-return */
4
- import * as fs from 'fs';
5
- import * as path from 'path';
6
4
  import * as xml2js from 'xml2js';
7
5
  import { BaseValidator } from './baseValidator';
6
+ import { decodeText, getBasename, getFs, readBinaryFromInput, toUint8Array } from '../utils/io';
8
7
  /**
9
8
  * Validator for TouchChat files (.ce)
10
9
  * TouchChat files are XML-based
@@ -18,9 +17,9 @@ export class TouchChatValidator extends BaseValidator {
18
17
  */
19
18
  static async validateFile(filePath) {
20
19
  const validator = new TouchChatValidator();
21
- const content = fs.readFileSync(filePath);
22
- const stats = fs.statSync(filePath);
23
- return validator.validate(content, path.basename(filePath), stats.size);
20
+ const content = readBinaryFromInput(filePath);
21
+ const stats = getFs().statSync(filePath);
22
+ return validator.validate(content, getBasename(filePath), stats.size);
24
23
  }
25
24
  /**
26
25
  * Check if content is TouchChat format
@@ -32,7 +31,7 @@ export class TouchChatValidator extends BaseValidator {
32
31
  }
33
32
  // Try to parse as XML and check for TouchChat structure
34
33
  try {
35
- const contentStr = Buffer.isBuffer(content) ? content.toString('utf-8') : content;
34
+ const contentStr = typeof content === 'string' ? content : decodeText(toUint8Array(content));
36
35
  const parser = new xml2js.Parser();
37
36
  const result = await parser.parseStringPromise(contentStr);
38
37
  // TouchChat files typically have specific structure
@@ -56,7 +55,7 @@ export class TouchChatValidator extends BaseValidator {
56
55
  await this.add_check('xml_parse', 'valid XML', async () => {
57
56
  try {
58
57
  const parser = new xml2js.Parser();
59
- const contentStr = content.toString('utf-8');
58
+ const contentStr = decodeText(content);
60
59
  xmlObj = await parser.parseStringPromise(contentStr);
61
60
  }
62
61
  catch (e) {
@@ -7,6 +7,8 @@ export declare function getPath(): typeof import('path');
7
7
  export declare function getOs(): typeof import('os');
8
8
  export declare function isNodeRuntime(): boolean;
9
9
  export declare function getBasename(filePath: string): string;
10
+ export declare function toUint8Array(input: Uint8Array | ArrayBuffer | Buffer): Uint8Array;
11
+ export declare function toArrayBuffer(input: Uint8Array | ArrayBuffer | Buffer): ArrayBuffer;
10
12
  export declare function decodeText(input: Uint8Array): string;
11
13
  export declare function encodeBase64(input: Uint8Array): string;
12
14
  export declare function encodeText(text: string): BinaryOutput;
package/dist/utils/io.js CHANGED
@@ -6,6 +6,8 @@ exports.getPath = getPath;
6
6
  exports.getOs = getOs;
7
7
  exports.isNodeRuntime = isNodeRuntime;
8
8
  exports.getBasename = getBasename;
9
+ exports.toUint8Array = toUint8Array;
10
+ exports.toArrayBuffer = toArrayBuffer;
9
11
  exports.decodeText = decodeText;
10
12
  exports.encodeBase64 = encodeBase64;
11
13
  exports.encodeText = encodeText;
@@ -87,8 +89,22 @@ function isNodeRuntime() {
87
89
  return typeof process !== 'undefined' && !!process.versions?.node;
88
90
  }
89
91
  function getBasename(filePath) {
90
- const parts = filePath.split(/[/\\]/);
91
- return parts[parts.length - 1] || filePath;
92
+ const trimmed = filePath.replace(/[/\\]+$/, '') || filePath;
93
+ const parts = trimmed.split(/[/\\]/);
94
+ return parts[parts.length - 1] || trimmed;
95
+ }
96
+ function toUint8Array(input) {
97
+ if (input instanceof Uint8Array) {
98
+ return input;
99
+ }
100
+ return new Uint8Array(input);
101
+ }
102
+ function toArrayBuffer(input) {
103
+ if (input instanceof ArrayBuffer) {
104
+ return input;
105
+ }
106
+ const view = input instanceof Uint8Array ? input : new Uint8Array(input);
107
+ return view.buffer.slice(view.byteOffset, view.byteOffset + view.byteLength);
92
108
  }
93
109
  function decodeText(input) {
94
110
  if (typeof Buffer !== 'undefined' && Buffer.isBuffer(input)) {
@@ -1,45 +1,23 @@
1
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
2
  var __importDefault = (this && this.__importDefault) || function (mod) {
26
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
27
4
  };
28
5
  Object.defineProperty(exports, "__esModule", { value: true });
29
6
  exports.ApplePanelsValidator = void 0;
30
7
  /* eslint-disable @typescript-eslint/require-await */
31
- const fs = __importStar(require("fs"));
32
- const path = __importStar(require("path"));
33
8
  const plist_1 = __importDefault(require("plist"));
34
9
  const baseValidator_1 = require("./baseValidator");
10
+ const io_1 = require("../utils/io");
35
11
  /**
36
12
  * Validator for Apple Panels (.plist or .ascconfig directory)
37
13
  */
38
14
  class ApplePanelsValidator extends baseValidator_1.BaseValidator {
39
15
  static async validateFile(filePath) {
40
16
  const validator = new ApplePanelsValidator();
17
+ const fs = (0, io_1.getFs)();
18
+ const path = (0, io_1.getPath)();
41
19
  let content;
42
- const filename = path.basename(filePath);
20
+ const filename = (0, io_1.getBasename)(filePath);
43
21
  let size = 0;
44
22
  const stats = fs.existsSync(filePath) ? fs.statSync(filePath) : null;
45
23
  if (stats?.isDirectory() && filename.toLowerCase().endsWith('.ascconfig')) {
@@ -62,7 +40,12 @@ class ApplePanelsValidator extends baseValidator_1.BaseValidator {
62
40
  return true;
63
41
  }
64
42
  try {
65
- const str = Buffer.isBuffer(content) ? content.toString('utf-8') : String(content);
43
+ if (typeof content !== 'string' &&
44
+ !(content instanceof ArrayBuffer) &&
45
+ !(content instanceof Uint8Array)) {
46
+ return false;
47
+ }
48
+ const str = typeof content === 'string' ? content : (0, io_1.decodeText)((0, io_1.toUint8Array)(content));
66
49
  const parsed = plist_1.default.parse(str);
67
50
  return Boolean(parsed.panels || parsed.Panels);
68
51
  }
@@ -80,7 +63,7 @@ class ApplePanelsValidator extends baseValidator_1.BaseValidator {
80
63
  let parsed = null;
81
64
  await this.add_check('plist_parse', 'valid plist/XML', async () => {
82
65
  try {
83
- const str = Buffer.isBuffer(content) ? content.toString('utf-8') : String(content);
66
+ const str = (0, io_1.decodeText)(content);
84
67
  parsed = plist_1.default.parse(str);
85
68
  }
86
69
  catch (e) {
@@ -1,33 +1,9 @@
1
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
2
  Object.defineProperty(exports, "__esModule", { value: true });
26
3
  exports.AstericsGridValidator = void 0;
27
4
  /* eslint-disable @typescript-eslint/require-await */
28
- const fs = __importStar(require("fs"));
29
- const path = __importStar(require("path"));
30
5
  const baseValidator_1 = require("./baseValidator");
6
+ const io_1 = require("../utils/io");
31
7
  /**
32
8
  * Validator for Asterics Grid (.grd) JSON files
33
9
  */
@@ -37,9 +13,9 @@ class AstericsGridValidator extends baseValidator_1.BaseValidator {
37
13
  */
38
14
  static async validateFile(filePath) {
39
15
  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);
16
+ const content = (0, io_1.readBinaryFromInput)(filePath);
17
+ const stats = (0, io_1.getFs)().statSync(filePath);
18
+ return validator.validate(content, (0, io_1.getBasename)(filePath), stats.size);
43
19
  }
44
20
  /**
45
21
  * Identify whether the content appears to be an Asterics .grd file
@@ -50,7 +26,12 @@ class AstericsGridValidator extends baseValidator_1.BaseValidator {
50
26
  return true;
51
27
  }
52
28
  try {
53
- const str = Buffer.isBuffer(content) ? content.toString('utf-8') : String(content);
29
+ if (typeof content !== 'string' &&
30
+ !(content instanceof ArrayBuffer) &&
31
+ !(content instanceof Uint8Array)) {
32
+ return false;
33
+ }
34
+ const str = typeof content === 'string' ? content : (0, io_1.decodeText)((0, io_1.toUint8Array)(content));
54
35
  const json = JSON.parse(str);
55
36
  return Array.isArray(json?.grids);
56
37
  }
@@ -68,7 +49,7 @@ class AstericsGridValidator extends baseValidator_1.BaseValidator {
68
49
  let json = null;
69
50
  await this.add_check('json_parse', 'valid JSON', async () => {
70
51
  try {
71
- let str = Buffer.isBuffer(content) ? content.toString('utf-8') : String(content);
52
+ let str = (0, io_1.decodeText)(content);
72
53
  if (str.charCodeAt(0) === 0xfeff) {
73
54
  str = str.slice(1);
74
55
  }
@@ -1,49 +1,30 @@
1
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
2
  Object.defineProperty(exports, "__esModule", { value: true });
26
3
  exports.DotValidator = void 0;
27
4
  /* eslint-disable @typescript-eslint/require-await */
28
- const fs = __importStar(require("fs"));
29
- const path = __importStar(require("path"));
30
5
  const baseValidator_1 = require("./baseValidator");
6
+ const io_1 = require("../utils/io");
31
7
  /**
32
8
  * Validator for Graphviz DOT files
33
9
  */
34
10
  class DotValidator extends baseValidator_1.BaseValidator {
35
11
  static async validateFile(filePath) {
36
12
  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);
13
+ const content = (0, io_1.readBinaryFromInput)(filePath);
14
+ const stats = (0, io_1.getFs)().statSync(filePath);
15
+ return validator.validate(content, (0, io_1.getBasename)(filePath), stats.size);
40
16
  }
41
17
  static async identifyFormat(content, filename) {
42
18
  const name = filename.toLowerCase();
43
19
  if (name.endsWith('.dot'))
44
20
  return true;
45
21
  try {
46
- const str = Buffer.isBuffer(content) ? content.toString('utf-8') : String(content);
22
+ if (typeof content !== 'string' &&
23
+ !(content instanceof ArrayBuffer) &&
24
+ !(content instanceof Uint8Array)) {
25
+ return false;
26
+ }
27
+ const str = typeof content === 'string' ? content : (0, io_1.decodeText)((0, io_1.toUint8Array)(content));
47
28
  return str.includes('digraph') || str.includes('->');
48
29
  }
49
30
  catch {
@@ -59,7 +40,7 @@ class DotValidator extends baseValidator_1.BaseValidator {
59
40
  });
60
41
  let text = '';
61
42
  await this.add_check('text', 'text content', async () => {
62
- text = Buffer.isBuffer(content) ? content.toString('utf-8') : String(content);
43
+ text = (0, io_1.decodeText)(content);
63
44
  if (!text.trim()) {
64
45
  this.err('DOT file is empty', true);
65
46
  }
@@ -25,19 +25,18 @@ var __importStar = (this && this.__importStar) || function (mod) {
25
25
  Object.defineProperty(exports, "__esModule", { value: true });
26
26
  exports.ExcelValidator = void 0;
27
27
  /* eslint-disable @typescript-eslint/require-await */
28
- const fs = __importStar(require("fs"));
29
- const path = __importStar(require("path"));
30
28
  const ExcelJS = __importStar(require("exceljs"));
31
29
  const baseValidator_1 = require("./baseValidator");
30
+ const io_1 = require("../utils/io");
32
31
  /**
33
32
  * Validator for Excel imports (.xlsx/.xls)
34
33
  */
35
34
  class ExcelValidator extends baseValidator_1.BaseValidator {
36
35
  static async validateFile(filePath) {
37
36
  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);
37
+ const content = (0, io_1.readBinaryFromInput)(filePath);
38
+ const stats = (0, io_1.getFs)().statSync(filePath);
39
+ return validator.validate(content, (0, io_1.getBasename)(filePath), stats.size);
41
40
  }
42
41
  static async identifyFormat(_content, filename) {
43
42
  const name = filename.toLowerCase();
@@ -58,7 +57,7 @@ class ExcelValidator extends baseValidator_1.BaseValidator {
58
57
  });
59
58
  return this.buildResult(filename, filesize, 'excel');
60
59
  }
61
- const buffer = Buffer.isBuffer(content) ? content : Buffer.from(content);
60
+ const buffer = typeof Buffer !== 'undefined' && Buffer.isBuffer(content) ? content : (0, io_1.toArrayBuffer)(content);
62
61
  const workbook = new ExcelJS.Workbook();
63
62
  await this.add_check('open', 'open workbook', async () => {
64
63
  try {
@@ -1,4 +1,27 @@
1
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
+ };
2
25
  var __importDefault = (this && this.__importDefault) || function (mod) {
3
26
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
27
  };
@@ -8,24 +31,9 @@ exports.GridsetValidator = void 0;
8
31
  /* eslint-disable @typescript-eslint/no-unsafe-argument */
9
32
  /* eslint-disable @typescript-eslint/no-unsafe-return */
10
33
  const jszip_1 = __importDefault(require("jszip"));
34
+ const xml2js = __importStar(require("xml2js"));
11
35
  const baseValidator_1 = require("./baseValidator");
12
36
  const io_1 = require("../utils/io");
13
- let cachedXml2js = null;
14
- function getXml2js() {
15
- if (cachedXml2js)
16
- return cachedXml2js;
17
- try {
18
- const nodeRequire = (0, io_1.getNodeRequire)();
19
- // eslint-disable-next-line @typescript-eslint/no-var-requires
20
- const module = nodeRequire('xml2js');
21
- const resolved = module.default || module;
22
- cachedXml2js = resolved;
23
- return resolved;
24
- }
25
- catch {
26
- throw new Error('Validator requires Xml2js in this environment.');
27
- }
28
- }
29
37
  /**
30
38
  * Validator for Grid3/Smartbox Gridset files (.gridset, .gridsetx)
31
39
  */
@@ -39,10 +47,9 @@ class GridsetValidator extends baseValidator_1.BaseValidator {
39
47
  static async validateFile(filePath) {
40
48
  const validator = new GridsetValidator();
41
49
  const fs = (0, io_1.getFs)();
42
- const path = (0, io_1.getPath)();
43
50
  const content = fs.readFileSync(filePath);
44
51
  const stats = fs.statSync(filePath);
45
- return validator.validate(content, path.basename(filePath), stats.size);
52
+ return validator.validate(content, (0, io_1.getBasename)(filePath), stats.size);
46
53
  }
47
54
  /**
48
55
  * Check if content is Gridset format
@@ -54,8 +61,7 @@ class GridsetValidator extends baseValidator_1.BaseValidator {
54
61
  }
55
62
  // Try to parse as XML and check for gridset structure
56
63
  try {
57
- const contentStr = Buffer.isBuffer(content) ? content.toString('utf-8') : content;
58
- const xml2js = getXml2js();
64
+ const contentStr = typeof content === 'string' ? content : (0, io_1.decodeText)((0, io_1.toUint8Array)(content));
59
65
  const parser = new xml2js.Parser();
60
66
  const result = await parser.parseStringPromise(contentStr);
61
67
  return result && (result.gridset || result.Gridset);
@@ -108,9 +114,8 @@ class GridsetValidator extends baseValidator_1.BaseValidator {
108
114
  let xmlObj = null;
109
115
  await this.add_check('xml_parse', 'valid XML', async () => {
110
116
  try {
111
- const xml2js = getXml2js();
112
117
  const parser = new xml2js.Parser();
113
- const contentStr = content.toString('utf-8');
118
+ const contentStr = (0, io_1.decodeText)(content);
114
119
  xmlObj = await parser.parseStringPromise(contentStr);
115
120
  }
116
121
  catch (e) {
@@ -135,7 +140,7 @@ class GridsetValidator extends baseValidator_1.BaseValidator {
135
140
  async validateZipArchive(content, filename, _filesize) {
136
141
  let zip;
137
142
  try {
138
- zip = await jszip_1.default.loadAsync(Buffer.from(content));
143
+ zip = await jszip_1.default.loadAsync((0, io_1.toUint8Array)(content));
139
144
  }
140
145
  catch (e) {
141
146
  this.err(`Failed to open ZIP archive: ${e.message}`, true);
@@ -151,7 +156,6 @@ class GridsetValidator extends baseValidator_1.BaseValidator {
151
156
  else {
152
157
  try {
153
158
  const gridsetXml = await gridsetEntry.async('string');
154
- const xml2js = getXml2js();
155
159
  const parser = new xml2js.Parser();
156
160
  const xmlObj = await parser.parseStringPromise(gridsetXml);
157
161
  const gridset = xmlObj.gridset || xmlObj.Gridset;
@@ -159,7 +163,7 @@ class GridsetValidator extends baseValidator_1.BaseValidator {
159
163
  this.err('Invalid gridset.xml structure', true);
160
164
  }
161
165
  else {
162
- await this.validateGridsetStructure(gridset, filename, Buffer.from(gridsetXml));
166
+ await this.validateGridsetStructure(gridset, filename, new Uint8Array());
163
167
  }
164
168
  }
165
169
  catch (e) {
@@ -176,7 +180,6 @@ class GridsetValidator extends baseValidator_1.BaseValidator {
176
180
  else {
177
181
  try {
178
182
  const settingsXml = await settingsEntry.async('string');
179
- const xml2js = getXml2js();
180
183
  const parser = new xml2js.Parser();
181
184
  const xmlObj = await parser.parseStringPromise(settingsXml);
182
185
  const settings = xmlObj.GridSetSettings || xmlObj.gridSetSettings || xmlObj.GridsetSettings;
@@ -16,6 +16,7 @@ export { ApplePanelsValidator } from './applePanelsValidator';
16
16
  export { ObfsetValidator } from './obfsetValidator';
17
17
  import { BaseValidator } from './baseValidator';
18
18
  import { ValidationResult } from './validationTypes';
19
+ import { type ProcessorInput } from '../utils/io';
19
20
  export declare function getValidatorForFormat(format: string): BaseValidator | null;
20
21
  export declare function getValidatorForFile(filename: string): BaseValidator | null;
21
22
  /**
@@ -23,4 +24,4 @@ export declare function getValidatorForFile(filename: string): BaseValidator | n
23
24
  * When a file path is provided, any validator-specific validateFile() helper
24
25
  * will be used if available to access nested resources.
25
26
  */
26
- export declare function validateFileOrBuffer(filePathOrBuffer: string | Buffer, filenameHint?: string): Promise<ValidationResult>;
27
+ export declare function validateFileOrBuffer(filePathOrBuffer: ProcessorInput, filenameHint?: string): Promise<ValidationResult>;
@@ -3,29 +3,6 @@
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
- };
29
6
  Object.defineProperty(exports, "__esModule", { value: true });
30
7
  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;
31
8
  exports.getValidatorForFormat = getValidatorForFormat;
@@ -72,8 +49,7 @@ const opmlValidator_2 = require("./opmlValidator");
72
49
  const dotValidator_2 = require("./dotValidator");
73
50
  const applePanelsValidator_2 = require("./applePanelsValidator");
74
51
  const obfsetValidator_2 = require("./obfsetValidator");
75
- const fs = __importStar(require("fs"));
76
- const path = __importStar(require("path"));
52
+ const io_1 = require("../utils/io");
77
53
  function getValidatorForFormat(format) {
78
54
  switch (format.toLowerCase()) {
79
55
  case 'obf':
@@ -151,22 +127,23 @@ function getValidatorForFile(filename) {
151
127
  */
152
128
  async function validateFileOrBuffer(filePathOrBuffer, filenameHint) {
153
129
  const isPath = typeof filePathOrBuffer === 'string';
154
- const name = filenameHint || (isPath ? path.basename(filePathOrBuffer) : 'upload');
130
+ const name = filenameHint || (isPath ? (0, io_1.getBasename)(filePathOrBuffer) : 'upload');
155
131
  const validator = getValidatorForFile(name) || getValidatorForFormat(name);
156
132
  if (!validator) {
157
133
  throw new Error(`No validator registered for ${name}`);
158
134
  }
159
135
  if (isPath) {
136
+ if (!(0, io_1.isNodeRuntime)()) {
137
+ throw new Error('File path validation is only supported in Node.js environments.');
138
+ }
160
139
  const ctor = validator.constructor;
161
140
  if (typeof ctor.validateFile === 'function') {
162
141
  return ctor.validateFile(filePathOrBuffer);
163
142
  }
164
- const buf = fs.readFileSync(filePathOrBuffer);
165
- const stats = fs.statSync(filePathOrBuffer);
166
- return validator.validate(buf, path.basename(filePathOrBuffer), stats.size);
143
+ const buf = (0, io_1.readBinaryFromInput)(filePathOrBuffer);
144
+ const stats = (0, io_1.getFs)().statSync(filePathOrBuffer);
145
+ return validator.validate(buf, (0, io_1.getBasename)(filePathOrBuffer), stats.size);
167
146
  }
168
- const buffer = Buffer.isBuffer(filePathOrBuffer)
169
- ? filePathOrBuffer
170
- : Buffer.from(filePathOrBuffer);
147
+ const buffer = (0, io_1.toUint8Array)(filePathOrBuffer);
171
148
  return validator.validate(buffer, name, buffer.byteLength);
172
149
  }
@@ -28,7 +28,7 @@ class ObfValidator extends baseValidator_1.BaseValidator {
28
28
  const validator = new ObfValidator();
29
29
  const content = (0, io_1.readBinaryFromInput)(filePath);
30
30
  const stats = (0, io_1.getFs)().statSync(filePath);
31
- return validator.validate(content, (0, io_1.getPath)().basename(filePath), stats.size);
31
+ return validator.validate(content, (0, io_1.getBasename)(filePath), stats.size);
32
32
  }
33
33
  /**
34
34
  * Check if content is OBF format
@@ -40,7 +40,12 @@ class ObfValidator extends baseValidator_1.BaseValidator {
40
40
  }
41
41
  // Try to parse as JSON and check format
42
42
  try {
43
- const contentStr = Buffer.isBuffer(content) ? content.toString() : content;
43
+ if (typeof content !== 'string' &&
44
+ !(content instanceof ArrayBuffer) &&
45
+ !(content instanceof Uint8Array)) {
46
+ return false;
47
+ }
48
+ const contentStr = typeof content === 'string' ? content : (0, io_1.decodeText)((0, io_1.toUint8Array)(content));
44
49
  const json = JSON.parse(contentStr);
45
50
  return json && json.format && json.format.startsWith('open-board-');
46
51
  }
@@ -74,7 +79,7 @@ class ObfValidator extends baseValidator_1.BaseValidator {
74
79
  let json = null;
75
80
  await this.add_check('valid_json', 'JSON file', async () => {
76
81
  try {
77
- json = JSON.parse(content.toString());
82
+ json = JSON.parse((0, io_1.decodeText)(content));
78
83
  }
79
84
  catch {
80
85
  this.err("Couldn't parse as JSON", true);
@@ -1,49 +1,30 @@
1
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
2
  Object.defineProperty(exports, "__esModule", { value: true });
26
3
  exports.ObfsetValidator = void 0;
27
4
  /* eslint-disable @typescript-eslint/require-await */
28
- const fs = __importStar(require("fs"));
29
- const path = __importStar(require("path"));
30
5
  const baseValidator_1 = require("./baseValidator");
6
+ const io_1 = require("../utils/io");
31
7
  /**
32
8
  * Validator for OBF set bundles (.obfset) - JSON arrays of boards
33
9
  */
34
10
  class ObfsetValidator extends baseValidator_1.BaseValidator {
35
11
  static async validateFile(filePath) {
36
12
  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);
13
+ const content = (0, io_1.readBinaryFromInput)(filePath);
14
+ const stats = (0, io_1.getFs)().statSync(filePath);
15
+ return validator.validate(content, (0, io_1.getBasename)(filePath), stats.size);
40
16
  }
41
17
  static async identifyFormat(content, filename) {
42
18
  const name = filename.toLowerCase();
43
19
  if (name.endsWith('.obfset'))
44
20
  return true;
45
21
  try {
46
- const str = Buffer.isBuffer(content) ? content.toString('utf-8') : String(content);
22
+ if (typeof content !== 'string' &&
23
+ !(content instanceof ArrayBuffer) &&
24
+ !(content instanceof Uint8Array)) {
25
+ return false;
26
+ }
27
+ const str = typeof content === 'string' ? content : (0, io_1.decodeText)((0, io_1.toUint8Array)(content));
47
28
  const parsed = JSON.parse(str);
48
29
  return Array.isArray(parsed);
49
30
  }
@@ -61,7 +42,7 @@ class ObfsetValidator extends baseValidator_1.BaseValidator {
61
42
  let boards = null;
62
43
  await this.add_check('json_parse', 'valid JSON array', async () => {
63
44
  try {
64
- const str = Buffer.isBuffer(content) ? content.toString('utf-8') : String(content);
45
+ const str = (0, io_1.decodeText)(content);
65
46
  const parsed = JSON.parse(str);
66
47
  if (!Array.isArray(parsed)) {
67
48
  this.err('root must be a JSON array of boards', true);
@@ -1,43 +1,19 @@
1
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
2
  Object.defineProperty(exports, "__esModule", { value: true });
26
3
  exports.OpmlValidator = void 0;
27
4
  /* eslint-disable @typescript-eslint/require-await */
28
- const fs = __importStar(require("fs"));
29
- const path = __importStar(require("path"));
30
5
  const fast_xml_parser_1 = require("fast-xml-parser");
31
6
  const baseValidator_1 = require("./baseValidator");
7
+ const io_1 = require("../utils/io");
32
8
  /**
33
9
  * Validator for OPML files
34
10
  */
35
11
  class OpmlValidator extends baseValidator_1.BaseValidator {
36
12
  static async validateFile(filePath) {
37
13
  const validator = new OpmlValidator();
38
- const content = fs.readFileSync(filePath);
39
- const stats = fs.statSync(filePath);
40
- return validator.validate(content, path.basename(filePath), stats.size);
14
+ const content = (0, io_1.readBinaryFromInput)(filePath);
15
+ const stats = (0, io_1.getFs)().statSync(filePath);
16
+ return validator.validate(content, (0, io_1.getBasename)(filePath), stats.size);
41
17
  }
42
18
  static async identifyFormat(content, filename) {
43
19
  const name = filename.toLowerCase();
@@ -45,7 +21,12 @@ class OpmlValidator extends baseValidator_1.BaseValidator {
45
21
  return true;
46
22
  }
47
23
  try {
48
- const str = Buffer.isBuffer(content) ? content.toString('utf-8') : String(content);
24
+ if (typeof content !== 'string' &&
25
+ !(content instanceof ArrayBuffer) &&
26
+ !(content instanceof Uint8Array)) {
27
+ return false;
28
+ }
29
+ const str = typeof content === 'string' ? content : (0, io_1.decodeText)((0, io_1.toUint8Array)(content));
49
30
  const validation = fast_xml_parser_1.XMLValidator.validate(str);
50
31
  if (validation !== true) {
51
32
  return false;
@@ -68,7 +49,7 @@ class OpmlValidator extends baseValidator_1.BaseValidator {
68
49
  });
69
50
  let text = '';
70
51
  await this.add_check('content', 'non-empty content', async () => {
71
- text = Buffer.isBuffer(content) ? content.toString('utf-8') : String(content);
52
+ text = (0, io_1.decodeText)(content);
72
53
  if (!text.trim()) {
73
54
  this.err('OPML file is empty', true);
74
55
  }
@@ -29,11 +29,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
29
29
  exports.SnapValidator = void 0;
30
30
  /* eslint-disable @typescript-eslint/require-await */
31
31
  /* eslint-disable @typescript-eslint/no-unsafe-argument */
32
- const fs = __importStar(require("fs"));
33
- const path = __importStar(require("path"));
34
32
  const xml2js = __importStar(require("xml2js"));
35
33
  const jszip_1 = __importDefault(require("jszip"));
36
34
  const baseValidator_1 = require("./baseValidator");
35
+ const io_1 = require("../utils/io");
37
36
  /**
38
37
  * Validator for Snap files (.spb, .sps)
39
38
  * Snap files are zipped packages containing XML configuration
@@ -47,9 +46,9 @@ class SnapValidator extends baseValidator_1.BaseValidator {
47
46
  */
48
47
  static async validateFile(filePath) {
49
48
  const validator = new SnapValidator();
50
- const content = fs.readFileSync(filePath);
51
- const stats = fs.statSync(filePath);
52
- return validator.validate(content, path.basename(filePath), stats.size);
49
+ const content = (0, io_1.readBinaryFromInput)(filePath);
50
+ const stats = (0, io_1.getFs)().statSync(filePath);
51
+ return validator.validate(content, (0, io_1.getBasename)(filePath), stats.size);
53
52
  }
54
53
  /**
55
54
  * Check if content is Snap format
@@ -62,8 +61,7 @@ class SnapValidator extends baseValidator_1.BaseValidator {
62
61
  }
63
62
  // Try to parse as ZIP and check for Snap structure
64
63
  try {
65
- const buffer = Buffer.isBuffer(content) ? content : Buffer.from(content);
66
- const zip = await jszip_1.default.loadAsync(buffer);
64
+ const zip = await jszip_1.default.loadAsync((0, io_1.toUint8Array)(content));
67
65
  const entries = Object.values(zip.files).filter((entry) => !entry.dir);
68
66
  return entries.some((entry) => entry.name.includes('settings') || entry.name.includes('.xml'));
69
67
  }
@@ -85,8 +83,7 @@ class SnapValidator extends baseValidator_1.BaseValidator {
85
83
  let validZip = false;
86
84
  await this.add_check('zip', 'valid zip package', async () => {
87
85
  try {
88
- const buffer = Buffer.isBuffer(content) ? content : Buffer.from(content);
89
- zip = await jszip_1.default.loadAsync(buffer);
86
+ zip = await jszip_1.default.loadAsync((0, io_1.toUint8Array)(content));
90
87
  const entries = Object.values(zip.files);
91
88
  validZip = entries.length > 0;
92
89
  }
@@ -27,10 +27,9 @@ exports.TouchChatValidator = void 0;
27
27
  /* eslint-disable @typescript-eslint/require-await */
28
28
  /* eslint-disable @typescript-eslint/no-unsafe-argument */
29
29
  /* eslint-disable @typescript-eslint/no-unsafe-return */
30
- const fs = __importStar(require("fs"));
31
- const path = __importStar(require("path"));
32
30
  const xml2js = __importStar(require("xml2js"));
33
31
  const baseValidator_1 = require("./baseValidator");
32
+ const io_1 = require("../utils/io");
34
33
  /**
35
34
  * Validator for TouchChat files (.ce)
36
35
  * TouchChat files are XML-based
@@ -44,9 +43,9 @@ class TouchChatValidator extends baseValidator_1.BaseValidator {
44
43
  */
45
44
  static async validateFile(filePath) {
46
45
  const validator = new TouchChatValidator();
47
- const content = fs.readFileSync(filePath);
48
- const stats = fs.statSync(filePath);
49
- return validator.validate(content, path.basename(filePath), stats.size);
46
+ const content = (0, io_1.readBinaryFromInput)(filePath);
47
+ const stats = (0, io_1.getFs)().statSync(filePath);
48
+ return validator.validate(content, (0, io_1.getBasename)(filePath), stats.size);
50
49
  }
51
50
  /**
52
51
  * Check if content is TouchChat format
@@ -58,7 +57,7 @@ class TouchChatValidator extends baseValidator_1.BaseValidator {
58
57
  }
59
58
  // Try to parse as XML and check for TouchChat structure
60
59
  try {
61
- const contentStr = Buffer.isBuffer(content) ? content.toString('utf-8') : content;
60
+ const contentStr = typeof content === 'string' ? content : (0, io_1.decodeText)((0, io_1.toUint8Array)(content));
62
61
  const parser = new xml2js.Parser();
63
62
  const result = await parser.parseStringPromise(contentStr);
64
63
  // TouchChat files typically have specific structure
@@ -82,7 +81,7 @@ class TouchChatValidator extends baseValidator_1.BaseValidator {
82
81
  await this.add_check('xml_parse', 'valid XML', async () => {
83
82
  try {
84
83
  const parser = new xml2js.Parser();
85
- const contentStr = content.toString('utf-8');
84
+ const contentStr = (0, io_1.decodeText)(content);
86
85
  xmlObj = await parser.parseStringPromise(contentStr);
87
86
  }
88
87
  catch (e) {
@@ -191,6 +191,48 @@
191
191
  font-size: 13px;
192
192
  }
193
193
 
194
+ .validation-panel {
195
+ margin-top: 15px;
196
+ }
197
+
198
+ .validation-summary {
199
+ background: #f8f9fa;
200
+ border-radius: 8px;
201
+ padding: 10px 12px;
202
+ font-size: 13px;
203
+ font-weight: 600;
204
+ margin-bottom: 10px;
205
+ }
206
+
207
+ .validation-summary.success {
208
+ border-left: 4px solid #2ecc71;
209
+ color: #2d7a4f;
210
+ }
211
+
212
+ .validation-summary.error {
213
+ border-left: 4px solid #e74c3c;
214
+ color: #b03a2e;
215
+ }
216
+
217
+ .validation-list {
218
+ max-height: 180px;
219
+ overflow-y: auto;
220
+ font-size: 12px;
221
+ }
222
+
223
+ .validation-item {
224
+ padding: 6px 0;
225
+ border-bottom: 1px solid #ececec;
226
+ }
227
+
228
+ .validation-item.warn {
229
+ color: #c18401;
230
+ }
231
+
232
+ .validation-item.error {
233
+ color: #b03a2e;
234
+ }
235
+
194
236
  .processor-name {
195
237
  font-weight: 600;
196
238
  color: #333;
@@ -437,6 +479,7 @@
437
479
  </div>
438
480
 
439
481
  <button class="btn" id="processBtn" disabled>🚀 Process File</button>
482
+ <button class="btn btn-secondary" id="validateBtn" disabled>✅ Validate File</button>
440
483
  <button class="btn btn-secondary" id="runTestsBtn">🧪 Run Compatibility Tests</button>
441
484
  <button class="btn btn-secondary" id="clearBtn">🗑️ Clear Results</button>
442
485
 
@@ -467,6 +510,12 @@
467
510
  <div class="panel-title">Test Results</div>
468
511
  <div id="testList"></div>
469
512
  </div>
513
+
514
+ <div class="validation-panel" id="validationPanel" style="display: none;">
515
+ <div class="panel-title">Validation</div>
516
+ <div class="validation-summary" id="validationSummary"></div>
517
+ <div class="validation-list" id="validationList"></div>
518
+ </div>
470
519
  </div>
471
520
 
472
521
  <!-- Right Panel: Results -->
@@ -81,6 +81,7 @@ import {
81
81
  AACPage,
82
82
  AACButton
83
83
  } from 'aac-processors';
84
+ import { validateFileOrBuffer, type ValidationResult } from 'aac-processors/validation';
84
85
 
85
86
  import sqlWasmUrl from 'sql.js/dist/sql-wasm.wasm?url';
86
87
 
@@ -92,6 +93,7 @@ configureSqlJs({
92
93
  const dropArea = document.getElementById('dropArea') as HTMLElement;
93
94
  const fileInput = document.getElementById('fileInput') as HTMLInputElement;
94
95
  const processBtn = document.getElementById('processBtn') as HTMLButtonElement;
96
+ const validateBtn = document.getElementById('validateBtn') as HTMLButtonElement;
95
97
  const runTestsBtn = document.getElementById('runTestsBtn') as HTMLButtonElement;
96
98
  const clearBtn = document.getElementById('clearBtn') as HTMLButtonElement;
97
99
  const fileInfo = document.getElementById('fileInfo') as HTMLElement;
@@ -102,6 +104,9 @@ const results = document.getElementById('results') as HTMLElement;
102
104
  const logPanel = document.getElementById('logPanel') as HTMLElement;
103
105
  const testResults = document.getElementById('testResults') as HTMLElement;
104
106
  const testList = document.getElementById('testList') as HTMLElement;
107
+ const validationPanel = document.getElementById('validationPanel') as HTMLElement;
108
+ const validationSummary = document.getElementById('validationSummary') as HTMLElement;
109
+ const validationList = document.getElementById('validationList') as HTMLElement;
105
110
  const tabButtons = document.querySelectorAll('.tab-btn') as NodeListOf<HTMLButtonElement>;
106
111
  const inspectTab = document.getElementById('inspectTab') as HTMLElement;
107
112
  const pagesetTab = document.getElementById('pagesetTab') as HTMLElement;
@@ -218,6 +223,7 @@ function handleFile(file: File) {
218
223
  fileDetails.textContent = extension;
219
224
  fileInfo.style.display = 'block';
220
225
  processBtn.disabled = true;
226
+ validateBtn.disabled = true;
221
227
  return;
222
228
  }
223
229
 
@@ -228,12 +234,14 @@ function handleFile(file: File) {
228
234
  fileDetails.textContent = `${file.name} • ${formatFileSize(file.size)}`;
229
235
  fileInfo.style.display = 'block';
230
236
  processBtn.disabled = false;
237
+ validateBtn.disabled = false;
231
238
  currentSourceLabel = file.name;
232
239
 
233
240
  log(`Using processor: ${currentProcessor.constructor.name}`, 'success');
234
241
  } catch (error) {
235
242
  log(`Error getting processor: ${(error as Error).message}`, 'error');
236
243
  processBtn.disabled = true;
244
+ validateBtn.disabled = true;
237
245
  }
238
246
  }
239
247
 
@@ -314,6 +322,79 @@ processBtn.addEventListener('click', async () => {
314
322
  }
315
323
  });
316
324
 
325
+ function collectValidationMessages(
326
+ result: ValidationResult,
327
+ prefix = ''
328
+ ): Array<{ type: 'error' | 'warn'; message: string }> {
329
+ const messages: Array<{ type: 'error' | 'warn'; message: string }> = [];
330
+ const label = prefix ? `${prefix}: ` : '';
331
+ result.results.forEach((check) => {
332
+ if (!check.valid && check.error) {
333
+ messages.push({ type: 'error', message: `${label}${check.description}: ${check.error}` });
334
+ }
335
+ if (check.warnings?.length) {
336
+ check.warnings.forEach((warning) => {
337
+ messages.push({ type: 'warn', message: `${label}${check.description}: ${warning}` });
338
+ });
339
+ }
340
+ });
341
+ result.sub_results?.forEach((sub) => {
342
+ const nextPrefix = `${label}${sub.filename || sub.format}`;
343
+ messages.push(...collectValidationMessages(sub, nextPrefix));
344
+ });
345
+ return messages;
346
+ }
347
+
348
+ function renderValidationResult(result: ValidationResult) {
349
+ validationPanel.style.display = 'block';
350
+ validationSummary.classList.remove('success', 'error');
351
+ validationSummary.classList.add(result.valid ? 'success' : 'error');
352
+ validationSummary.textContent = `${result.valid ? '✅ Valid' : '❌ Invalid'} • ${result.format.toUpperCase()} • ${result.errors} errors, ${result.warnings} warnings`;
353
+
354
+ validationList.innerHTML = '';
355
+ const messages = collectValidationMessages(result).slice(0, 30);
356
+ if (messages.length === 0) {
357
+ const empty = document.createElement('div');
358
+ empty.className = 'validation-item';
359
+ empty.textContent = 'No issues reported.';
360
+ validationList.appendChild(empty);
361
+ return;
362
+ }
363
+
364
+ messages.forEach((entry) => {
365
+ const item = document.createElement('div');
366
+ item.className = `validation-item ${entry.type}`;
367
+ item.textContent = entry.message;
368
+ validationList.appendChild(item);
369
+ });
370
+ }
371
+
372
+ validateBtn.addEventListener('click', async () => {
373
+ if (!currentFile) return;
374
+ log('Validating file...', 'info');
375
+
376
+ try {
377
+ validateBtn.disabled = true;
378
+ const arrayBuffer = await currentFile.arrayBuffer();
379
+ const result = await validateFileOrBuffer(new Uint8Array(arrayBuffer), currentFile.name);
380
+ renderValidationResult(result);
381
+ log(
382
+ `${result.valid ? '✅' : '❌'} Validation complete: ${result.errors} errors, ${result.warnings} warnings`,
383
+ result.valid ? 'success' : 'warn'
384
+ );
385
+ } catch (error) {
386
+ const errorMsg = (error as Error).message;
387
+ validationPanel.style.display = 'block';
388
+ validationSummary.classList.remove('success');
389
+ validationSummary.classList.add('error');
390
+ validationSummary.textContent = `❌ Validation failed: ${errorMsg}`;
391
+ validationList.innerHTML = '';
392
+ log(`❌ Validation failed: ${errorMsg}`, 'error');
393
+ } finally {
394
+ validateBtn.disabled = !currentFile;
395
+ }
396
+ });
397
+
317
398
  // Display results
318
399
  function displayResults(tree: AACTree) {
319
400
  results.innerHTML = '';
@@ -423,6 +504,9 @@ clearBtn.addEventListener('click', () => {
423
504
  stats.style.display = 'none';
424
505
  results.innerHTML = '<p style="color: #999; text-align: center; padding: 40px;">Load a file to see its contents here</p>';
425
506
  testResults.style.display = 'none';
507
+ validationPanel.style.display = 'none';
508
+ validationSummary.textContent = '';
509
+ validationList.innerHTML = '';
426
510
  logPanel.innerHTML = '<div class="log-entry log-info">Cleared. Ready to process files...</div>';
427
511
  pagesetOutput.textContent = 'Generate or convert a pageset to preview the output JSON.';
428
512
  updateConvertButtons();
@@ -3,13 +3,32 @@ import path from 'path';
3
3
 
4
4
  export default defineConfig({
5
5
  resolve: {
6
- alias: {
7
- 'aac-processors': path.resolve(__dirname, '../../src/index.browser.ts'),
8
- stream: path.resolve(__dirname, 'node_modules/stream-browserify'),
9
- events: path.resolve(__dirname, 'node_modules/events'),
10
- timers: path.resolve(__dirname, 'node_modules/timers-browserify'),
11
- util: path.resolve(__dirname, 'node_modules/util')
12
- }
6
+ alias: [
7
+ {
8
+ find: /^aac-processors\/validation$/,
9
+ replacement: path.resolve(__dirname, '../../src/validation.ts'),
10
+ },
11
+ {
12
+ find: /^aac-processors$/,
13
+ replacement: path.resolve(__dirname, '../../src/index.browser.ts'),
14
+ },
15
+ {
16
+ find: /^stream$/,
17
+ replacement: path.resolve(__dirname, 'node_modules/stream-browserify'),
18
+ },
19
+ {
20
+ find: /^events$/,
21
+ replacement: path.resolve(__dirname, 'node_modules/events'),
22
+ },
23
+ {
24
+ find: /^timers$/,
25
+ replacement: path.resolve(__dirname, 'node_modules/timers-browserify'),
26
+ },
27
+ {
28
+ find: /^util$/,
29
+ replacement: path.resolve(__dirname, 'node_modules/util'),
30
+ },
31
+ ],
13
32
  },
14
33
  optimizeDeps: {
15
34
  exclude: ['aac-processors'],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@willwade/aac-processors",
3
- "version": "0.1.6",
3
+ "version": "0.1.7",
4
4
  "description": "A comprehensive TypeScript library for processing AAC (Augmentative and Alternative Communication) file formats with translation support",
5
5
  "main": "dist/index.js",
6
6
  "browser": "dist/browser/index.browser.js",