File size: 7,111 Bytes
9bd422a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
/**
 * Validators - Input and data validation utilities
 * Provides functions for validating ONNX files, SafeTensors files, model data, and user inputs.
 * Requirements: 3.3, 9.3, 36.1, 36.3, 36.5
 */

const Validators = (function () {

  // ─── ONNX File Validation ─────────────────────────────────────────────────

  /**
   * ONNX protobuf magic bytes (first 2 bytes of a valid serialised protobuf
   * message with field 1 = irVersion).  We accept any non-empty ArrayBuffer
   * whose first byte is 0x08 (field 1, wire type 0 = varint) as a minimal
   * heuristic, since ONNX models always start with the irVersion field.
   *
   * A stricter check would require a full protobuf parse; this is intentionally
   * lightweight for a browser-side static app.
   */
  const ONNX_MAGIC_BYTE = 0x08;

  /**
   * Validate that a File object is a valid ONNX file.
   * Checks:
   *  1. The file has a .onnx extension (case-insensitive).
   *  2. The file is not empty.
   *
   * @param {File} file - Browser File object
   * @returns {{ valid: boolean, error?: string }}
   */
  function isValidOnnxFile(file) {
    if (!file) {
      return { valid: false, error: 'No file provided.' };
    }

    // Extension check
    const name = file.name || '';
    if (!name.toLowerCase().endsWith('.onnx')) {
      return {
        valid: false,
        error: `Invalid file type "${name}". Only .onnx files are accepted.`,
      };
    }

    // Empty file check
    if (file.size === 0) {
      return { valid: false, error: 'The file is empty.' };
    }

    return { valid: true };
  }

  /**
   * Validate the raw bytes of an ONNX file (ArrayBuffer).
   * Performs a lightweight magic-byte check.
   *
   * @param {ArrayBuffer} buffer
   * @returns {{ valid: boolean, error?: string }}
   */
  function isValidOnnxBuffer(buffer) {
    if (!(buffer instanceof ArrayBuffer) || buffer.byteLength === 0) {
      return { valid: false, error: 'Invalid or empty buffer.' };
    }
    const view = new Uint8Array(buffer, 0, Math.min(4, buffer.byteLength));
    if (view[0] !== ONNX_MAGIC_BYTE) {
      return {
        valid: false,
        error: 'File does not appear to be a valid ONNX model (unexpected header bytes).',
      };
    }
    return { valid: true };
  }

  // ─── SafeTensors File Validation ────────────────────────────────────────────

  /**
   * Validate that a File object is a valid SafeTensors file.
   * Checks:
   *  1. The file has a .safetensors extension (case-insensitive).
   *  2. The file is not empty.
   *
   * @param {File} file - Browser File object
   * @returns {{ valid: boolean, error?: string }}
   */
  function isValidSafeTensorsFile(file) {
    if (!file) {
      return { valid: false, error: 'No file provided.' };
    }

    // Extension check
    const name = file.name || '';
    if (!name.toLowerCase().endsWith('.safetensors')) {
      return {
        valid: false,
        error: `Invalid file type "${name}". Only .safetensors files are accepted.`,
      };
    }

    // Empty file check
    if (file.size === 0) {
      return { valid: false, error: 'The file is empty.' };
    }

    return { valid: true };
  }

  // ─── Model Data Validation ────────────────────────────────────────────────

  /**
   * Validate a parsed model data object returned by ONNXParser.
   * Ensures the required top-level fields are present and non-null.
   *
   * @param {Object} data - ParsedModel object
   * @returns {{ valid: boolean, error?: string }}
   */
  function isValidModelData(data) {
    if (!data || typeof data !== 'object') {
      return { valid: false, error: 'Model data is null or not an object.' };
    }

    const required = ['metadata', 'graph', 'inputs', 'outputs'];
    for (const field of required) {
      if (!(field in data) || data[field] == null) {
        return { valid: false, error: `Model data is missing required field: "${field}".` };
      }
    }

    if (!Array.isArray(data.inputs)) {
      return { valid: false, error: 'Model data "inputs" must be an array.' };
    }
    if (!Array.isArray(data.outputs)) {
      return { valid: false, error: 'Model data "outputs" must be an array.' };
    }

    return { valid: true };
  }

  // ─── Input / Search Validation ────────────────────────────────────────────

  /**
   * Validate a generic user input string.
   * Returns false for null/undefined; trims and checks length.
   *
   * @param {*} input
   * @param {{ minLength?: number, maxLength?: number, required?: boolean }} [opts]
   * @returns {{ valid: boolean, error?: string }}
   */
  function isValidInput(input, opts = {}) {
    const { minLength = 0, maxLength = 1000, required = false } = opts;

    if (input == null || input === '') {
      if (required) {
        return { valid: false, error: 'This field is required.' };
      }
      return { valid: true }; // empty is OK when not required
    }

    const str = String(input).trim();

    if (required && str.length === 0) {
      return { valid: false, error: 'This field cannot be blank.' };
    }
    if (str.length < minLength) {
      return { valid: false, error: `Input must be at least ${minLength} characters.` };
    }
    if (str.length > maxLength) {
      return { valid: false, error: `Input must not exceed ${maxLength} characters.` };
    }

    return { valid: true };
  }

  /**
   * Validate a search query string.
   * Allows empty strings (clears the filter); rejects excessively long ones.
   *
   * @param {string} query
   * @returns {{ valid: boolean, error?: string }}
   */
  function isValidSearchQuery(query) {
    return isValidInput(query, { maxLength: 200 });
  }

  /**
   * Validate a model path string (must be non-empty and end with .onnx or .safetensors).
   * @param {string} path
   * @returns {{ valid: boolean, error?: string }}
   */
  function isValidModelPath(path) {
    if (!path || typeof path !== 'string' || path.trim() === '') {
      return { valid: false, error: 'Model path must be a non-empty string.' };
    }
    const lower = path.toLowerCase();
    if (!lower.endsWith('.onnx') && !lower.endsWith('.safetensors')) {
      return { valid: false, error: 'Model path must point to an .onnx or .safetensors file.' };
    }
    return { valid: true };
  }

  // ─── Public API ───────────────────────────────────────────────────────────

  return {
    isValidOnnxFile,
    isValidOnnxBuffer,
    isValidSafeTensorsFile,
    isValidModelData,
    isValidInput,
    isValidSearchQuery,
    isValidModelPath,
  };
})();

// Export for global access in vanilla JS context
window.Validators = Validators;