@toolpath/tool-scraper 2.0.0 → 2.2.0
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/dist/columns.d.ts +5 -3
- package/dist/columns.js +11 -5
- package/dist/conventions.d.ts +54 -0
- package/dist/conventions.js +59 -0
- package/dist/errors.d.ts +30 -0
- package/dist/errors.js +30 -0
- package/dist/families/emuge.d.ts +3 -1
- package/dist/families/emuge.js +8 -4
- package/dist/family.d.ts +16 -2
- package/dist/holding.d.ts +396 -0
- package/dist/holding.js +360 -0
- package/dist/index.d.ts +23 -13
- package/dist/index.js +23 -13
- package/dist/node/cad-mirror.d.ts +58 -1
- package/dist/node/cad-mirror.js +56 -8
- package/dist/node/cli.d.ts +4 -1
- package/dist/node/cli.js +198 -19
- package/dist/node/holder-import.d.ts +223 -0
- package/dist/node/holder-import.js +379 -0
- package/dist/node/index.d.ts +1 -0
- package/dist/node/index.js +1 -0
- package/dist/node/paths.d.ts +16 -0
- package/dist/node/paths.js +20 -0
- package/dist/profiles.d.ts +265 -0
- package/dist/profiles.js +295 -0
- package/dist/records.d.ts +57 -12
- package/dist/records.js +37 -13
- package/dist/registry.d.ts +86 -5
- package/dist/registry.js +149 -8
- package/dist/vendors/destinytool/records.js +8 -0
- package/dist/vendors/emuge/records.d.ts +83 -3
- package/dist/vendors/emuge/records.js +184 -16
- package/dist/vendors/kennametal/family.d.ts +119 -0
- package/dist/vendors/kennametal/family.js +155 -0
- package/dist/vendors/kennametal/holding.d.ts +35 -0
- package/dist/vendors/kennametal/holding.js +112 -0
- package/dist/vendors/kennametal/index.d.ts +2 -0
- package/dist/vendors/kennametal/index.js +2 -0
- package/dist/vendors/kennametal/records.d.ts +16 -0
- package/dist/vendors/kennametal/records.js +32 -0
- package/dist/vendors/kennametal/scrape.d.ts +25 -2
- package/dist/vendors/kennametal/scrape.js +28 -3
- package/dist/vendors/maritool/holding.d.ts +79 -0
- package/dist/vendors/maritool/holding.js +164 -0
- package/dist/vendors/maritool/index.d.ts +1 -0
- package/dist/vendors/maritool/index.js +1 -0
- package/dist/vendors/maritool/scrape.d.ts +37 -13
- package/dist/vendors/maritool/scrape.js +53 -14
- package/dist/vendors/regofix/holding.d.ts +35 -0
- package/dist/vendors/regofix/holding.js +108 -0
- package/dist/vendors/regofix/index.d.ts +1 -0
- package/dist/vendors/regofix/index.js +1 -0
- package/dist/vendors/regofix/scrape.js +2 -2
- package/package.json +1 -1
|
@@ -26,11 +26,16 @@
|
|
|
26
26
|
*
|
|
27
27
|
* ## What the vendor gets wrong
|
|
28
28
|
*
|
|
29
|
-
* Four faults found on 2026-08-29
|
|
29
|
+
* Four faults found on 2026-08-29. Three are reported as warnings rather than
|
|
30
30
|
* fixed — two disagreeing vendor cells cannot say which one is wrong, and a
|
|
31
31
|
* scraper that corrects one becomes a place tool data is authored by hand.
|
|
32
32
|
* This is the same call `vendors/regofix/scrape.ts` made on its three.
|
|
33
33
|
*
|
|
34
|
+
* The fourth is resolved rather than reported, and the difference is where the
|
|
35
|
+
* answer came from: `docs/ADDING-A-VENDOR.md` says that when a vendor label is
|
|
36
|
+
* unclear you **ask**, and record the answer and its date. That is what
|
|
37
|
+
* happened — see fault 2.
|
|
38
|
+
*
|
|
34
39
|
* 1. **`BT40-ER32-60` publishes no `Taper` row at all**, alone among the 529
|
|
35
40
|
* parts in scope. Its row is kept with `taper` and `contact` empty rather
|
|
36
41
|
* than dropped or filled in from its part number: the CSV is a receipt, and
|
|
@@ -38,10 +43,21 @@
|
|
|
38
43
|
* 2. **`Collet Size` carries a collet *nut* designation on two parts.**
|
|
39
44
|
* `CAT40-ER25-3.0MD` and `BT30-ER25-60M` both state `ER25M`, and `ER25M` is
|
|
40
45
|
* not a collet series — `HSK40E-ER16-3.0M` puts exactly that shape of value
|
|
41
|
-
* in its own `Collet Nut` cell, which is the column it belongs in.
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
46
|
+
* in its own `Collet Nut` cell, which is the column it belongs in.
|
|
47
|
+
*
|
|
48
|
+
* **`ER25M` is the mini collet nut series, and the collet it closes is a
|
|
49
|
+
* plain ER25** (JG 2026-09-02). So {@link colletSeries} resolves the cell to
|
|
50
|
+
* the collet the holder actually takes, and the two parts join to the ER25
|
|
51
|
+
* collets they fit. Until that answer existed the string was written through
|
|
52
|
+
* as designated and warned about, because widening a series on a guess is
|
|
53
|
+
* how a machinist is offered a collet that does not seat — and the cost of
|
|
54
|
+
* being wrong the other way was one option, not a purchase.
|
|
55
|
+
*
|
|
56
|
+
* **Nothing is lost by resolving it.** `Collet Size` is one of the vendor's
|
|
57
|
+
* own labels and is carried into the CSV verbatim, so the receipt still says
|
|
58
|
+
* `ER25M` and still says which of the two parts has a mini nut. What changes
|
|
59
|
+
* is only `CST`, which is a derived join key rather than a record of what the
|
|
60
|
+
* vendor published — the same thing the spacing rule below does to it.
|
|
45
61
|
* 3. **`Collet Size` is spaced inconsistently within one style** — `ER 11`
|
|
46
62
|
* and `ER11` are both published. {@link colletSeries} closes the space,
|
|
47
63
|
* because `CST` is a join key and two spellings of one series join to
|
|
@@ -444,21 +460,44 @@ export function parseGageLength(cell) {
|
|
|
444
460
|
const unit = (parsed?.[2] ?? '').toLowerCase() === 'mm' ? 'millimeters' : 'inches';
|
|
445
461
|
return { value, unit };
|
|
446
462
|
}
|
|
463
|
+
/**
|
|
464
|
+
* A `Collet Size` cell that names the **nut** rather than the collet.
|
|
465
|
+
*
|
|
466
|
+
* `M` is MariTool's mini collet nut, and the collet a mini nut closes is the
|
|
467
|
+
* plain one of that series (JG 2026-09-02). The vendor's own `Collet Nut`
|
|
468
|
+
* column carries `ER11M`, `ER16M`, `ER20M` and `ER25M` beside a `Collet Size`
|
|
469
|
+
* of plain `ER 11`, `ER 16`, `ER 20` and `ER 25` on 57 parts, which is the
|
|
470
|
+
* column those values belong in.
|
|
471
|
+
*
|
|
472
|
+
* Anchored on `ER\d+` rather than stripping a trailing letter, so a suffix that
|
|
473
|
+
* turns out to designate something else still reaches {@link holderRow}'s
|
|
474
|
+
* warning instead of being quietly shortened into a series that fits nothing.
|
|
475
|
+
*/
|
|
476
|
+
const NUT_DESIGNATION = /^(ER\d+)M$/;
|
|
447
477
|
/**
|
|
448
478
|
* A `Collet Size` cell as the series `CST` joins a collet family on.
|
|
449
479
|
*
|
|
450
|
-
*
|
|
451
|
-
*
|
|
452
|
-
*
|
|
453
|
-
*
|
|
480
|
+
* **Derived, not a receipt.** `CST` is the key `families/kennametal.ts` states
|
|
481
|
+
* the holder-to-collet join against, so what belongs in it is the series of the
|
|
482
|
+
* collet the holder takes. The vendor's own cell is carried into the CSV under
|
|
483
|
+
* its own label and untouched, which is where the two normalisations below are
|
|
484
|
+
* still visible.
|
|
485
|
+
*
|
|
486
|
+
* Two things are closed, and both exist because two spellings of one series
|
|
487
|
+
* join to nothing:
|
|
488
|
+
*
|
|
489
|
+
* - **Spacing.** MariTool publishes `ER 11` and `ER11` within one style, and
|
|
490
|
+
* the collet side of the join spells the series without a space.
|
|
491
|
+
* - **A nut designation.** `ER25M` names a mini nut, not a collet series — see
|
|
492
|
+
* {@link NUT_DESIGNATION} and this module's docstring.
|
|
454
493
|
*
|
|
455
|
-
* Nothing else is
|
|
456
|
-
*
|
|
457
|
-
*
|
|
458
|
-
* carries elsewhere — see this module's docstring. {@link holderRow} warns.
|
|
494
|
+
* Nothing else is. A cell this leaves as neither `ER<n>` nor a nut designation
|
|
495
|
+
* is written through as the vendor designated it, and {@link holderRow} warns
|
|
496
|
+
* that it joins to no collet.
|
|
459
497
|
*/
|
|
460
498
|
export function colletSeries(cell) {
|
|
461
|
-
|
|
499
|
+
const closed = cell.replace(/\s+/g, '');
|
|
500
|
+
return NUT_DESIGNATION.exec(closed)?.[1] ?? closed;
|
|
462
501
|
}
|
|
463
502
|
/**
|
|
464
503
|
* One listing row plus its spec table -> one CSV row.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* REGO-FIX's toolholding column vocabulary, and nothing else.
|
|
3
|
+
*
|
|
4
|
+
* Two things about this vendor decide the shape of what is below, and both are
|
|
5
|
+
* the reverse of Kennametal's:
|
|
6
|
+
*
|
|
7
|
+
* - **`contact` is a column, not a family fact.** REGO-FIX publishes plain and
|
|
8
|
+
* dual-contact powRgrip in one product group — `BT 30 / PG 25 x 080 H` and
|
|
9
|
+
* `BT+ 30 / PG 25 x 080 H` are two rows of one table — so `scrape.holderRow`
|
|
10
|
+
* resolves it from the vendor's own `form_name` and writes
|
|
11
|
+
* `conventions.CONTACT_COLUMN`. `families/regofix.ts` deliberately declares no
|
|
12
|
+
* `contact` fact, and reading one here would silently mask a scrape that lost
|
|
13
|
+
* the column.
|
|
14
|
+
* - **`unit` is a column on a collet.** Every one of the twelve PG groups holds
|
|
15
|
+
* metric and fractional-inch collets side by side, so there is no
|
|
16
|
+
* family-level answer to declare, and none of the collet families declares
|
|
17
|
+
* one.
|
|
18
|
+
*
|
|
19
|
+
* ## What a powRgrip holder does not publish
|
|
20
|
+
*
|
|
21
|
+
* No `D1`. A powRgrip holder clamps through a PG collet, and a collet-clamping
|
|
22
|
+
* holder that also carried a bore would be claiming two ways of gripping one
|
|
23
|
+
* tool — `scrape.holderRow` says so, and `holding.checkHolder` refuses it.
|
|
24
|
+
*
|
|
25
|
+
* No `L2`, `L9`, `V` or `D11` either: the DIN 4000 documents publish `A2`, `B1`
|
|
26
|
+
* and `B2`, whose meaning is not stated anywhere this package has been able to
|
|
27
|
+
* check, and they stay behind `conventions.DIN_PREFIX` rather than being guessed
|
|
28
|
+
* at. `B3`, the projection length, *is* pinned and understood — it is the
|
|
29
|
+
* `x 050` of the designation — and it is still not on the record, under
|
|
30
|
+
* `records.ToolRecord`'s standing rule that a field arrives when something
|
|
31
|
+
* displays it.
|
|
32
|
+
*/
|
|
33
|
+
import { type HoldingMappers } from '../../holding.js';
|
|
34
|
+
/** The toolholding half of the adapter contract `registry` looks up by brand. */
|
|
35
|
+
export declare const HOLDING_MAPPERS: HoldingMappers;
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* REGO-FIX's toolholding column vocabulary, and nothing else.
|
|
3
|
+
*
|
|
4
|
+
* Two things about this vendor decide the shape of what is below, and both are
|
|
5
|
+
* the reverse of Kennametal's:
|
|
6
|
+
*
|
|
7
|
+
* - **`contact` is a column, not a family fact.** REGO-FIX publishes plain and
|
|
8
|
+
* dual-contact powRgrip in one product group — `BT 30 / PG 25 x 080 H` and
|
|
9
|
+
* `BT+ 30 / PG 25 x 080 H` are two rows of one table — so `scrape.holderRow`
|
|
10
|
+
* resolves it from the vendor's own `form_name` and writes
|
|
11
|
+
* `conventions.CONTACT_COLUMN`. `families/regofix.ts` deliberately declares no
|
|
12
|
+
* `contact` fact, and reading one here would silently mask a scrape that lost
|
|
13
|
+
* the column.
|
|
14
|
+
* - **`unit` is a column on a collet.** Every one of the twelve PG groups holds
|
|
15
|
+
* metric and fractional-inch collets side by side, so there is no
|
|
16
|
+
* family-level answer to declare, and none of the collet families declares
|
|
17
|
+
* one.
|
|
18
|
+
*
|
|
19
|
+
* ## What a powRgrip holder does not publish
|
|
20
|
+
*
|
|
21
|
+
* No `D1`. A powRgrip holder clamps through a PG collet, and a collet-clamping
|
|
22
|
+
* holder that also carried a bore would be claiming two ways of gripping one
|
|
23
|
+
* tool — `scrape.holderRow` says so, and `holding.checkHolder` refuses it.
|
|
24
|
+
*
|
|
25
|
+
* No `L2`, `L9`, `V` or `D11` either: the DIN 4000 documents publish `A2`, `B1`
|
|
26
|
+
* and `B2`, whose meaning is not stated anywhere this package has been able to
|
|
27
|
+
* check, and they stay behind `conventions.DIN_PREFIX` rather than being guessed
|
|
28
|
+
* at. `B3`, the projection length, *is* pinned and understood — it is the
|
|
29
|
+
* `x 050` of the designation — and it is still not on the record, under
|
|
30
|
+
* `records.ToolRecord`'s standing rule that a field arrives when something
|
|
31
|
+
* displays it.
|
|
32
|
+
*/
|
|
33
|
+
import { CAD_COLUMN, COLLET_DESIGNATION_COLUMN, COLLET_SERIES_COLUMN, CONTACT_COLUMN, } from '../../conventions.js';
|
|
34
|
+
import { familyBrand } from '../../family.js';
|
|
35
|
+
import { checkUnitAgreement, clampingMode, colletRecord, contactMode, dim, holderRecord, holdingFact, published, unitSystem, } from '../../holding.js';
|
|
36
|
+
import { consoleWarn } from '../../scrape.js';
|
|
37
|
+
/** The identity columns this vendor adopted from Kennametal, being second. */
|
|
38
|
+
const MATERIAL_NUMBER = 'Material Number';
|
|
39
|
+
const CATALOG_NUMBER = 'ISO Catalog Number';
|
|
40
|
+
/**
|
|
41
|
+
* The column `scrape.colletRow` writes the system it read off a designation to.
|
|
42
|
+
*
|
|
43
|
+
* REGO-FIX's own, and not a convention: it is the only vendor here that states
|
|
44
|
+
* a unit per row, so there is no second writer for the name to be shared with.
|
|
45
|
+
*/
|
|
46
|
+
const UNIT_COLUMN = 'unit';
|
|
47
|
+
/** How a part names itself in a warning or a refusal. */
|
|
48
|
+
function subject(row) {
|
|
49
|
+
return `${row[CATALOG_NUMBER] ?? ''} (${row[MATERIAL_NUMBER] ?? ''})`;
|
|
50
|
+
}
|
|
51
|
+
/** One powRgrip holder row -> one {@link HolderRecord}. */
|
|
52
|
+
function holder(row, family) {
|
|
53
|
+
const what = subject(row);
|
|
54
|
+
const unit = holdingFact(family, 'unit', family.unit);
|
|
55
|
+
return holderRecord({
|
|
56
|
+
brand: familyBrand(family),
|
|
57
|
+
materialNumber: published(row[MATERIAL_NUMBER], what, 'material number'),
|
|
58
|
+
// The designation REGO-FIX titles the part with — `BT 30 / PG 25 x 080 H`.
|
|
59
|
+
// It is the catalog number and not a description: the vendor publishes no
|
|
60
|
+
// prose about a holder anywhere, so `description` is `''`.
|
|
61
|
+
catalogNumber: published(row[CATALOG_NUMBER], what, 'catalog number'),
|
|
62
|
+
description: '',
|
|
63
|
+
unit,
|
|
64
|
+
taper: holdingFact(family, 'taper', family.taper),
|
|
65
|
+
contact: contactMode(published(row[CONTACT_COLUMN], what, 'contact mode'), what),
|
|
66
|
+
clamping: clampingMode(holdingFact(family, 'clamping', family.clamping), what),
|
|
67
|
+
style: holdingFact(family, 'style', family.style),
|
|
68
|
+
colletSeries: row[COLLET_SERIES_COLUMN] || null,
|
|
69
|
+
gaugeLength: published(dim(row, 'L1', unit), what, 'L1 gage length'),
|
|
70
|
+
bodyDiameter: dim(row, 'D2', unit),
|
|
71
|
+
cadModelUrl: row[CAD_COLUMN] || null,
|
|
72
|
+
cadDxfUrl: null,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
/** One PG or PGST collet row -> one {@link ColletRecord}. */
|
|
76
|
+
function collet(row, family, options = {}) {
|
|
77
|
+
const warn = options.warn ?? consoleWarn;
|
|
78
|
+
const what = subject(row);
|
|
79
|
+
const unit = unitSystem(published(row[UNIT_COLUMN], what, 'unit system'), what);
|
|
80
|
+
// An inch collet carries both columns, because `scrape.colletRow` projects the
|
|
81
|
+
// exact fraction from the designation into millimetres. Cross-checking them is
|
|
82
|
+
// therefore checking this package's own arithmetic as much as the vendor's,
|
|
83
|
+
// which is worth doing once per row and costs nothing when they agree.
|
|
84
|
+
for (const label of ['D1', 'CCCN', 'CCCX'])
|
|
85
|
+
checkUnitAgreement(row, label, what, warn);
|
|
86
|
+
return colletRecord({
|
|
87
|
+
brand: familyBrand(family),
|
|
88
|
+
materialNumber: published(row[MATERIAL_NUMBER], what, 'material number'),
|
|
89
|
+
catalogNumber: published(row[CATALOG_NUMBER], what, 'catalog number'),
|
|
90
|
+
description: '',
|
|
91
|
+
unit,
|
|
92
|
+
// Written exactly as the vendor designates it, so a `PGST15` collet matches
|
|
93
|
+
// no `PG25` holder. `families/regofix.ts` records why that is the
|
|
94
|
+
// conservative direction and how to resolve it — by asking REGO-FIX, not by
|
|
95
|
+
// widening the string.
|
|
96
|
+
series: published(row[COLLET_DESIGNATION_COLUMN], what, 'collet series'),
|
|
97
|
+
style: holdingFact(family, 'style', family.style),
|
|
98
|
+
nominal: dim(row, 'D1', unit),
|
|
99
|
+
// A powRgrip collet clamps one size to h6 rather than closing over a range,
|
|
100
|
+
// so its capacity is its nominal diameter at both ends — the vendor's own
|
|
101
|
+
// `Clamping range or tolerance` row. A zero-width range is still a range,
|
|
102
|
+
// and `holding.checkCollet` refuses only an inverted one.
|
|
103
|
+
clampMin: published(dim(row, 'CCCN', unit), what, 'CCCN clamping minimum'),
|
|
104
|
+
clampMax: published(dim(row, 'CCCX', unit), what, 'CCCX clamping maximum'),
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
/** The toolholding half of the adapter contract `registry` looks up by brand. */
|
|
108
|
+
export const HOLDING_MAPPERS = { holder, collet };
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
* (`1715.08215` and `1725.08215`). This is why the nominal size is parsed
|
|
54
54
|
* from the vendor's own designation and `o_mm` is only ever a cross-check.
|
|
55
55
|
*/
|
|
56
|
-
import { CAD_COLUMN, COLLET_SERIES_COLUMN, CONTACT_COLUMN, DIN_PREFIX, GAGE_COLUMNS, } from '../../conventions.js';
|
|
56
|
+
import { CAD_COLUMN, COLLET_DESIGNATION_COLUMN, COLLET_SERIES_COLUMN, CONTACT_COLUMN, DIN_PREFIX, GAGE_COLUMNS, } from '../../conventions.js';
|
|
57
57
|
import { VendorResponseError } from '../../errors.js';
|
|
58
58
|
import { statusOf } from '../../fetch.js';
|
|
59
59
|
import { convertLength, fractionValue } from '../../measure.js';
|
|
@@ -417,7 +417,7 @@ export function colletRow(source, warn = consoleWarn) {
|
|
|
417
417
|
const row = {
|
|
418
418
|
'Material Number': sku,
|
|
419
419
|
'ISO Catalog Number': title,
|
|
420
|
-
|
|
420
|
+
[COLLET_DESIGNATION_COLUMN]: (parsed['series'] ?? '').replaceAll(' ', ''),
|
|
421
421
|
unit,
|
|
422
422
|
o_mm: plain(one(source, 'o_mm')),
|
|
423
423
|
Square_mm: '',
|