nirs4all 0.2.6 → 0.2.8

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,12 +2,16 @@
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.
8
12
 
9
13
  The portable execution API delegates Kennard-Stone, SNV, Savitzky-Golay, and
10
- PLS component sweeps to `@nirs4all/methods-wasm`:
14
+ PLS component sweeps to `@nirs4all/methods`:
11
15
 
12
16
  - `runPortablePipeline(source, dataset)` parses the shared nirs4all JSON/YAML
13
17
  syntax, executes the portable subset, and returns parity-checkable split,
@@ -20,3 +24,10 @@ PLS component sweeps to `@nirs4all/methods-wasm`:
20
24
  Savitzky-Golay defaults to `mode: "interp"` for full nirs4all parity and
21
25
  preserves explicit methods-backed modes (`mirror`, `constant`, `nearest`,
22
26
  `wrap`, `interp`) plus `cval` in the serialized preprocessing chain.
27
+
28
+ For a browser-only custom host, pair this package with `nirs4all-ui`: keep
29
+ runtime loading and portable execution in `nirs4all`, and consume shared React
30
+ components / view-model helpers / brand assets from `nirs4all-ui`. The
31
+ reference composition lives in `../../nirs4all-web/studio-lite`, whose contract
32
+ tests exercise `runPortablePipeline()` / `predictPortablePipeline()` together
33
+ with the shared UI package in a no-backend environment.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nirs4all",
3
- "version": "0.2.6",
3
+ "version": "0.2.8",
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",
@@ -43,7 +47,7 @@
43
47
  "@nirs4all/datasets-wasm": "*",
44
48
  "@nirs4all/formats-wasm": "*",
45
49
  "@nirs4all/io-wasm": "*",
46
- "@nirs4all/methods-wasm": "*",
50
+ "@nirs4all/methods": "*",
47
51
  "@nirs4all/nirs4all-datasets-wasm": "*",
48
52
  "dag-ml-data-wasm": "*",
49
53
  "dag-ml-wasm": "*",
@@ -60,7 +64,7 @@
60
64
  "@nirs4all/io-wasm": {
61
65
  "optional": true
62
66
  },
63
- "@nirs4all/methods-wasm": {
67
+ "@nirs4all/methods": {
64
68
  "optional": true
65
69
  },
66
70
  "@nirs4all/nirs4all-datasets-wasm": {
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) {
package/src/index.js CHANGED
@@ -28,7 +28,7 @@ export const upstreams = Object.freeze([
28
28
  },
29
29
  {
30
30
  key: 'methods',
31
- candidates: ['@nirs4all/methods-wasm'],
31
+ candidates: ['@nirs4all/methods'],
32
32
  role: 'Portable C ABI PLS/NIRS numerical engine',
33
33
  },
34
34
  ]);
@@ -213,8 +213,8 @@ function importUpstreamCandidate(candidate) {
213
213
  return import('@nirs4all/datasets-wasm');
214
214
  case '@nirs4all/nirs4all-datasets-wasm':
215
215
  return import('@nirs4all/nirs4all-datasets-wasm');
216
- case '@nirs4all/methods-wasm':
217
- return import('@nirs4all/methods-wasm');
216
+ case '@nirs4all/methods':
217
+ return import('@nirs4all/methods');
218
218
  default:
219
219
  return import(candidate);
220
220
  }