nirs4all 0.2.6 → 0.2.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.
package/README.md CHANGED
@@ -2,6 +2,10 @@
2
2
 
3
3
  npm package name: `nirs4all`
4
4
 
5
+ The canonical source repository is `nirs4all-core`; the npm publication keeps
6
+ the bare `nirs4all` name because the lite->core rename applies only to the
7
+ Python distribution.
8
+
5
9
  This package is the runtime surface that `nirs4all-web` should consume. The web
6
10
  application lives in `nirs4all-web`; this directory is for the reusable
7
11
  JavaScript/WASM binding and package metadata.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nirs4all",
3
- "version": "0.2.6",
3
+ "version": "0.2.7",
4
4
  "description": "Portable nirs4all aggregate binding for JavaScript and WASM",
5
5
  "type": "module",
6
6
  "exports": {
@@ -22,6 +22,10 @@
22
22
  "typecheck": "tsc --project tsconfig.typecheck.json"
23
23
  },
24
24
  "license": "(CECILL-2.1 OR AGPL-3.0-or-later)",
25
+ "homepage": "https://nirs4all-core.readthedocs.io/en/latest/",
26
+ "bugs": {
27
+ "url": "https://github.com/GBeurier/nirs4all-core/issues"
28
+ },
25
29
  "repository": {
26
30
  "type": "git",
27
31
  "url": "git+https://github.com/GBeurier/nirs4all-core.git",
@@ -78,9 +82,5 @@
78
82
  "dag-ml-data-wasm": {
79
83
  "optional": true
80
84
  }
81
- },
82
- "publishConfig": {
83
- "access": "public",
84
- "provenance": true
85
85
  }
86
86
  }
package/src/execution.js CHANGED
@@ -238,7 +238,7 @@ function computeSplit(methods, splitter, input) {
238
238
  const indices = Array.from({ length: input.rows }, (_, i) => i);
239
239
  return { kind: 'all', trainIndices: indices, testIndices: indices };
240
240
  }
241
- const splitOptions = { testSize: numberParam(splitter.params.test_size, 0.25) };
241
+ const splitOptions = { testSize: numberParam(splitter.params.test_size, 0.25, 'test_size') };
242
242
  if (typeof methods.computeSplitIndices === 'function') {
243
243
  const split = methods.computeSplitIndices(
244
244
  'KennardStone',
@@ -280,17 +280,17 @@ function selectRows(data, rows, cols, indices) {
280
280
  }
281
281
 
282
282
  function savgolParams(params) {
283
- const delta = numberParam(params.delta, 1);
284
- if (delta !== 1) {
283
+ const delta = numberParam(params.delta, 1, 'delta');
284
+ if (Math.abs(delta - 1) > Number.EPSILON) {
285
285
  throw new Error('Portable Savitzky-Golay execution currently supports delta=1 only.');
286
286
  }
287
287
  return [
288
- numberParam(params.window_length ?? params.window, 11),
289
- numberParam(params.polyorder, 3),
290
- numberParam(params.deriv, 0),
288
+ integerParam(params.window_length ?? params.window, 11, 'window_length', { min: 1 }),
289
+ integerParam(params.polyorder, 3, 'polyorder', { min: 0 }),
290
+ integerParam(params.deriv, 0, 'deriv', { min: 0 }),
291
291
  // scipy.signal.savgol_filter, and therefore nirs4all Python, default to interp.
292
292
  savgolMode(params.mode ?? 'interp'),
293
- numberParam(params.cval, 0),
293
+ numberParam(params.cval, 0, 'cval'),
294
294
  ];
295
295
  }
296
296
 
@@ -322,23 +322,52 @@ function componentValues(step) {
322
322
  if (step.param !== 'n_components') {
323
323
  throw new Error("Portable execution only supports _range_ sweeps over 'n_components'.");
324
324
  }
325
- const [start, stop, stride] = step._range_.map(Number);
326
- if (![start, stop, stride].every(Number.isFinite) || stride <= 0 || start > stop) {
325
+ if (step._range_.length !== 3) {
327
326
  throw new Error('Invalid n_components _range_; expected [start, stop, positive_step].');
328
327
  }
328
+ const start = integerParam(step._range_[0], undefined, 'n_components range start', { min: 1 });
329
+ const stop = integerParam(step._range_[1], undefined, 'n_components range stop', { min: 1 });
330
+ const stride = integerParam(step._range_[2], undefined, 'n_components range step', { min: 1 });
331
+ if (start > stop) {
332
+ throw new Error('Invalid n_components _range_; start must be <= stop.');
333
+ }
329
334
  const values = [];
330
335
  for (let value = start; value <= stop; value += stride) {
331
- values.push(Math.round(value));
336
+ values.push(value);
332
337
  }
333
338
  return values;
334
339
  }
335
340
  const params = step.model?.params ?? {};
336
- return [Math.max(1, Math.round(numberParam(params.n_components, 2)))];
341
+ return [integerParam(params.n_components, 2, 'n_components', { min: 1 })];
342
+ }
343
+
344
+ function numberParam(value, fallback, label) {
345
+ if (value == null) {
346
+ return fallback;
347
+ }
348
+ let n;
349
+ if (typeof value === 'number') {
350
+ n = value;
351
+ } else if (typeof value === 'string' && value.trim() !== '') {
352
+ n = Number(value);
353
+ } else {
354
+ throw new TypeError(`${label} must be numeric.`);
355
+ }
356
+ if (!Number.isFinite(n)) {
357
+ throw new RangeError(`${label} must be finite.`);
358
+ }
359
+ return n;
337
360
  }
338
361
 
339
- function numberParam(value, fallback) {
340
- const n = Number(value);
341
- return Number.isFinite(n) ? n : fallback;
362
+ function integerParam(value, fallback, label, options = {}) {
363
+ const n = numberParam(value, fallback, label);
364
+ if (!Number.isInteger(n)) {
365
+ throw new RangeError(`${label} must be an integer.`);
366
+ }
367
+ if (options.min != null && n < options.min) {
368
+ throw new RangeError(`${label} must be >= ${options.min}.`);
369
+ }
370
+ return n;
342
371
  }
343
372
 
344
373
  function rmse(predictions, targets) {