@danielsimonjr/mathts-wasm 0.1.2 → 0.1.3

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.
@@ -0,0 +1,4 @@
1
+ {
2
+ "mathts-debug.wasm": "sha384-DWpIT/Aj2P6lTUCvEX4wZi/wIBBILesWJ2DnuRw6fp0PMj6b8aOwNkLHtL657y4z",
3
+ "mathts.wasm": "sha384-q/fiU1qnpj7vLiYnprGBDdO7on0S/Gnd5Pyql20PZxGfl9Jx0jdWPUGQe0FM91wo"
4
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@danielsimonjr/mathts-wasm",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "MathTS WebAssembly module - high-performance numerical computing",
5
5
  "type": "module",
6
6
  "main": "build/release.js",
@@ -28,10 +28,11 @@
28
28
  "build:bindings": "tsc -p tsconfig.bindings.json",
29
29
  "build": "npm run asbuild && npm run build:bindings",
30
30
  "clean": "rm -rf build/",
31
- "test": "node --experimental-wasm-simd tests/run.js",
31
+ "test": "node tests/run.js && node tests/svd.test.mjs && node tests/special-numtheory.test.mjs && node tests/polynomial.test.mjs && node tests/signal.test.mjs && node tests/linalg.test.mjs && node tests/curvefit.test.mjs && node tests/optimization.test.mjs && node tests/approx.test.mjs && node tests/tensor.test.mjs",
32
32
  "benchmark": "node --experimental-wasm-simd benchmarks/run.js"
33
33
  },
34
34
  "devDependencies": {
35
+ "@assemblyscript/loader": "^0.28.17",
35
36
  "assemblyscript": "^0.27.27",
36
37
  "typescript": "^5.3.0"
37
38
  },
@@ -45,11 +46,11 @@
45
46
  "linear-algebra",
46
47
  "simd"
47
48
  ],
48
- "author": "MathTS Contributors",
49
+ "author": "Daniel Simon Jr.",
49
50
  "license": "MIT",
50
51
  "repository": {
51
52
  "type": "git",
52
- "url": "https://github.com/danielsimonjr/MathTS.git",
53
+ "url": "https://github.com/danielsimonjr/mathts",
53
54
  "directory": "assembly"
54
55
  },
55
56
  "bugs": {
@@ -3,8 +3,82 @@
3
3
  *
4
4
  * TypeScript bindings for loading and using the MathTS WASM module.
5
5
  * This provides a clean interface for JavaScript/TypeScript consumers.
6
+ *
7
+ * Security: optional SHA-384 integrity check via a sibling
8
+ * `wasm-manifest.json`. See verifyWasm() / loadManifest() below — kept
9
+ * inline rather than imported because the assembly package ships
10
+ * standalone (no functions/ dependency).
6
11
  */
7
12
 
13
+ interface WasmManifest {
14
+ [fileName: string]: string;
15
+ }
16
+
17
+ async function sha384Base64(buffer: ArrayBuffer | Uint8Array): Promise<string> {
18
+ const bytes = buffer instanceof Uint8Array ? buffer : new Uint8Array(buffer);
19
+ const isNode = typeof process !== 'undefined' && (process as any).versions?.node !== undefined;
20
+ let digest: Uint8Array;
21
+ if (isNode) {
22
+ const { createHash } = await import('crypto');
23
+ const h = createHash('sha384');
24
+ h.update(bytes);
25
+ digest = new Uint8Array(h.digest());
26
+ } else {
27
+ // Copy into a fresh ArrayBuffer so SubtleCrypto.digest accepts the
28
+ // bytes regardless of the source backing (ArrayBuffer or SharedArrayBuffer).
29
+ const copy = new ArrayBuffer(bytes.byteLength);
30
+ new Uint8Array(copy).set(bytes);
31
+ const out = await crypto.subtle.digest('SHA-384', copy);
32
+ digest = new Uint8Array(out);
33
+ }
34
+ let bin = '';
35
+ for (let i = 0; i < digest.length; i++) bin += String.fromCharCode(digest[i]);
36
+ const b64 =
37
+ typeof btoa === 'function'
38
+ ? btoa(bin)
39
+ : (globalThis as any).Buffer.from(digest).toString('base64');
40
+ return `sha384-${b64}`;
41
+ }
42
+
43
+ async function loadManifest(wasmPath: string): Promise<WasmManifest | null> {
44
+ const manifestPath = wasmPath.replace(/[^/\\]+$/, 'wasm-manifest.json');
45
+ const isNode = typeof process !== 'undefined' && (process as any).versions?.node !== undefined;
46
+ try {
47
+ if (isNode) {
48
+ const fs = await import('fs');
49
+ const text = fs.readFileSync(manifestPath, 'utf8');
50
+ return JSON.parse(text) as WasmManifest;
51
+ } else {
52
+ const res = await fetch(manifestPath);
53
+ if (!res.ok) return null;
54
+ return (await res.json()) as WasmManifest;
55
+ }
56
+ } catch {
57
+ return null;
58
+ }
59
+ }
60
+
61
+ async function verifyWasm(buffer: ArrayBuffer | Uint8Array, wasmPath: string): Promise<void> {
62
+ const manifest = await loadManifest(wasmPath);
63
+ if (!manifest) {
64
+ if (typeof console !== 'undefined') {
65
+ console.warn(
66
+ `[wasm-integrity] no manifest found beside "${wasmPath}"; skipping SHA-384 verification`
67
+ );
68
+ }
69
+ return;
70
+ }
71
+ const fileName = wasmPath.split(/[/\\]/).pop() || wasmPath;
72
+ const expected = manifest[fileName];
73
+ if (!expected) return;
74
+ const actual = await sha384Base64(buffer);
75
+ if (actual !== expected) {
76
+ throw new Error(
77
+ `WASM integrity check failed for "${fileName}": expected ${expected}, got ${actual}`
78
+ );
79
+ }
80
+ }
81
+
8
82
  // Type definitions for the WASM module exports
9
83
  export interface MathTSWasmExports {
10
84
  // Memory
@@ -30,11 +104,54 @@ export interface MathTSWasmExports {
30
104
  array_norm(dataPtr: number): number;
31
105
 
32
106
  // Matrix operations (pointers)
33
- matrix_multiply(aPtr: number, aRows: number, aCols: number, bPtr: number, bCols: number, resultPtr: number): void;
107
+ matrix_multiply(
108
+ aPtr: number,
109
+ aRows: number,
110
+ aCols: number,
111
+ bPtr: number,
112
+ bCols: number,
113
+ resultPtr: number
114
+ ): void;
34
115
  matrix_transpose(aPtr: number, rows: number, cols: number, resultPtr: number): void;
35
116
  matrix_trace(dataPtr: number, rows: number, cols: number): number;
36
117
  matrix_norm_frobenius(dataPtr: number): number;
37
118
 
119
+ // Dense matrix decompositions — AS exports added in tandem with the
120
+ // Rust crate's `decomposition.rs` so the AS backend can stop falling
121
+ // back to JS for these ops. AS calling convention: pass typed-array
122
+ // *header* references (the AS runtime carries length, no ptr+len args).
123
+ // See `assembly/src/algebra/decomposition.ts` for the kernels.
124
+ matrix_lu_decompose?(
125
+ a: Float64Array,
126
+ n: number,
127
+ l_out: Float64Array,
128
+ u_out: Float64Array,
129
+ perm_out: Int32Array
130
+ ): number;
131
+ matrix_qr_decompose?(
132
+ a: Float64Array,
133
+ m: number,
134
+ n: number,
135
+ q_out: Float64Array,
136
+ r_out: Float64Array
137
+ ): number;
138
+ matrix_cholesky?(a: Float64Array, n: number, l_out: Float64Array): number;
139
+ matrix_inverse?(a: Float64Array, n: number, result: Float64Array, work: Float64Array): number;
140
+ matrix_determinant?(a: Float64Array, n: number, work: Float64Array): number;
141
+
142
+ // Bitwise operations (Int32Array, elementwise) — AS-backend kernels.
143
+ // The Rust backend ships an equivalent set under the `*Array` /
144
+ // `*ArrayPerElement` naming (declared in
145
+ // functions/src/wasm/WasmLoader.ts's interface); the AS kernels keep
146
+ // the `_i32_array` suffix to match AS export-name discoverability.
147
+ bitAnd_i32_array?(a: Int32Array, b: Int32Array, result: Int32Array): void;
148
+ bitOr_i32_array?(a: Int32Array, b: Int32Array, result: Int32Array): void;
149
+ bitXor_i32_array?(a: Int32Array, b: Int32Array, result: Int32Array): void;
150
+ bitNot_i32_array?(a: Int32Array, result: Int32Array): void;
151
+ leftShift_i32_array?(a: Int32Array, b: Int32Array, result: Int32Array): void;
152
+ rightArithShift_i32_array?(a: Int32Array, b: Int32Array, result: Int32Array): void;
153
+ rightLogShift_i32_array?(a: Int32Array, b: Int32Array, result: Int32Array): void;
154
+
38
155
  // Memory management
39
156
  __new(size: number, id: number): number;
40
157
  __pin(ptr: number): number;
@@ -58,16 +175,20 @@ export async function loadWasm(source: string | BufferSource): Promise<MathTSWas
58
175
  if (typeof fetch !== 'undefined') {
59
176
  const response = await fetch(source);
60
177
  const buffer = await response.arrayBuffer();
178
+ await verifyWasm(buffer, source);
61
179
  wasmModule = await WebAssembly.compile(buffer);
62
180
  } else {
63
181
  // Node.js environment
64
182
  const fs = await import('fs');
65
183
  const path = await import('path');
66
- const buffer = fs.readFileSync(path.resolve(source));
184
+ const resolved = path.resolve(source);
185
+ const buffer = fs.readFileSync(resolved);
186
+ await verifyWasm(buffer, resolved);
67
187
  wasmModule = await WebAssembly.compile(buffer);
68
188
  }
69
189
  } else {
70
- // Load from buffer
190
+ // Load from buffer — caller is responsible for verifying integrity
191
+ // since we have no path to locate a sibling manifest.
71
192
  wasmModule = await WebAssembly.compile(source);
72
193
  }
73
194
 
@@ -214,8 +335,8 @@ export class MathTSWasm {
214
335
  this.exports.__pin(ptr);
215
336
 
216
337
  const view = new DataView(this.memory.buffer);
217
- view.setInt32(ptr, ptr + 16, true); // data pointer
218
- view.setInt32(ptr + 4, length, true); // length
338
+ view.setInt32(ptr, ptr + 16, true); // data pointer
339
+ view.setInt32(ptr + 4, length, true); // length
219
340
 
220
341
  const array = new Float64Array(this.memory.buffer, ptr + 16, length);
221
342
  return { ptr, array };