@toolpath/tool-scraper 0.1.0 → 2.1.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.
Files changed (76) hide show
  1. package/README.md +41 -11
  2. package/dist/columns.d.ts +64 -0
  3. package/dist/columns.js +68 -0
  4. package/dist/conventions.d.ts +107 -15
  5. package/dist/conventions.js +126 -15
  6. package/dist/errors.d.ts +30 -0
  7. package/dist/errors.js +30 -0
  8. package/dist/families/emuge.d.ts +185 -0
  9. package/dist/families/emuge.js +163 -0
  10. package/dist/families/harvey.d.ts +1782 -0
  11. package/dist/families/harvey.js +1328 -0
  12. package/dist/families/index.js +5 -2
  13. package/dist/families/kennametal.d.ts +21 -0
  14. package/dist/families/kennametal.js +10 -0
  15. package/dist/families/maritool.d.ts +120 -0
  16. package/dist/families/maritool.js +175 -0
  17. package/dist/family.d.ts +35 -1
  18. package/dist/family.js +28 -0
  19. package/dist/identity.d.ts +18 -0
  20. package/dist/identity.js +46 -0
  21. package/dist/index.d.ts +16 -0
  22. package/dist/index.js +16 -0
  23. package/dist/measure.d.ts +112 -0
  24. package/dist/measure.js +130 -0
  25. package/dist/node/cli.d.ts +3 -0
  26. package/dist/node/cli.js +158 -3
  27. package/dist/node/main.js +0 -0
  28. package/dist/records.d.ts +214 -12
  29. package/dist/records.js +140 -5
  30. package/dist/registry.d.ts +56 -1
  31. package/dist/registry.js +79 -2
  32. package/dist/scrape.d.ts +15 -0
  33. package/dist/scrape.js +24 -0
  34. package/dist/vendors/destinytool/records.d.ts +13 -3
  35. package/dist/vendors/destinytool/records.js +49 -37
  36. package/dist/vendors/emuge/index.d.ts +17 -0
  37. package/dist/vendors/emuge/index.js +17 -0
  38. package/dist/vendors/emuge/records.d.ts +230 -0
  39. package/dist/vendors/emuge/records.js +543 -0
  40. package/dist/vendors/emuge/scrape.d.ts +227 -0
  41. package/dist/vendors/emuge/scrape.js +358 -0
  42. package/dist/vendors/emuge/value.d.ts +75 -0
  43. package/dist/vendors/emuge/value.js +116 -0
  44. package/dist/vendors/harvey/catalog.d.ts +53 -0
  45. package/dist/vendors/harvey/catalog.js +120 -0
  46. package/dist/vendors/harvey/header.d.ts +89 -0
  47. package/dist/vendors/harvey/header.js +185 -0
  48. package/dist/vendors/harvey/index.d.ts +21 -0
  49. package/dist/vendors/harvey/index.js +21 -0
  50. package/dist/vendors/harvey/lexicon.d.ts +73 -0
  51. package/dist/vendors/harvey/lexicon.js +126 -0
  52. package/dist/vendors/harvey/literal.d.ts +68 -0
  53. package/dist/vendors/harvey/literal.js +214 -0
  54. package/dist/vendors/harvey/records.d.ts +79 -0
  55. package/dist/vendors/harvey/records.js +163 -0
  56. package/dist/vendors/harvey/scrape.d.ts +187 -0
  57. package/dist/vendors/harvey/scrape.js +483 -0
  58. package/dist/vendors/harvey/value.d.ts +74 -0
  59. package/dist/vendors/harvey/value.js +119 -0
  60. package/dist/vendors/kennametal/family.d.ts +119 -0
  61. package/dist/vendors/kennametal/family.js +155 -0
  62. package/dist/vendors/kennametal/index.d.ts +1 -0
  63. package/dist/vendors/kennametal/index.js +1 -0
  64. package/dist/vendors/kennametal/records.d.ts +29 -4
  65. package/dist/vendors/kennametal/records.js +93 -26
  66. package/dist/vendors/kennametal/scrape.d.ts +25 -2
  67. package/dist/vendors/kennametal/scrape.js +28 -3
  68. package/dist/vendors/maritool/catalog.d.ts +81 -0
  69. package/dist/vendors/maritool/catalog.js +132 -0
  70. package/dist/vendors/maritool/index.d.ts +10 -0
  71. package/dist/vendors/maritool/index.js +10 -0
  72. package/dist/vendors/maritool/scrape.d.ts +297 -0
  73. package/dist/vendors/maritool/scrape.js +593 -0
  74. package/dist/vendors/regofix/scrape.d.ts +8 -11
  75. package/dist/vendors/regofix/scrape.js +21 -36
  76. package/package.json +31 -7
@@ -0,0 +1,185 @@
1
+ /**
2
+ * EMUGE-FRANKEN's families.
3
+ *
4
+ * Four, one per catalog category crossed with the unit system the category is
5
+ * published in. That is coarser than the vendor's own marketing, which splits
6
+ * end mills fifteen ways by product line — TOP-Cut, Hard-Cut, Alu-Cut — and it
7
+ * is deliberate: EMUGE states the product line, the cutting material, the
8
+ * coating and the coolant supply **per part**, in columns, and a scraped column
9
+ * beats a family constant. Splitting by product line would turn four counted
10
+ * row totals into thirty and buy nothing a `product line` column does not
11
+ * already carry — and it is carried: `vendors/emuge/records.ts`'s
12
+ * `PRODUCT_LINE_COLUMNS` reads one per part onto `ToolRecord.productLine`, from
13
+ * a column every scrape already writes.
14
+ *
15
+ * So the only fact three of these four state is `unit`, and the fourth adds the
16
+ * two a drill record cannot be built without.
17
+ *
18
+ * ## `rows`
19
+ *
20
+ * The vendor's own result count for exactly the query the family scrapes, read
21
+ * on 2026-09-01. It is the second number `node/receipts.checkRows` exists to
22
+ * compare against, and the point is that nothing computes it from the file it
23
+ * is checking — so when EMUGE adds a size, the scrape and this table disagree
24
+ * and somebody looks.
25
+ *
26
+ * ## The unit split, and where it does not apply
27
+ *
28
+ * Milling is the one category EMUGE publishes in both systems: `AMM_EINHS`
29
+ * indexes every milling variant as inch or metric, and an inch part states its
30
+ * dimensions in fractional inches (`1 1/2 "`) where a metric one states
31
+ * millimetres. Drilling and tapping have no such facet and no such split —
32
+ * every drill and every tap, including a `#4-40 UNC` one, is published in
33
+ * millimetres. That is why `emuge_taps.csv` declares a `unit` where
34
+ * `families/kennametal.ts`'s taps declare none: there is no per-row thread
35
+ * system to read, because the vendor states one system for all of them.
36
+ */
37
+ /**
38
+ * What each family scrapes, keyed by the same CSV names as {@link FAMILIES}.
39
+ *
40
+ * A sibling table rather than more keys on `FamilyDefinition`, which has no
41
+ * word for "a category narrowed by one facet" and should not grow one for a
42
+ * single vendor — the call `families/harvey.ts` makes with `PRODUCT_PAGES` and
43
+ * `families/maritool.ts` with `LEAVES`.
44
+ *
45
+ * `Target` is declared here rather than imported from
46
+ * `vendors/emuge/scrape.ts`, which is structurally identical, because
47
+ * `families/` importing an adapter is what `tests/vendor-boundary.test.ts`
48
+ * refuses: this table is read by every test, and none of them should drag a
49
+ * vendor's scraper in behind it.
50
+ */
51
+ export interface Target {
52
+ /** The vendor's category code. */
53
+ readonly category: string;
54
+ /** A facet code and value, both the vendor's own. */
55
+ readonly facet?: {
56
+ readonly code: string;
57
+ readonly value: string;
58
+ };
59
+ }
60
+ export declare const SCRAPE_TARGETS: {
61
+ readonly 'emuge_end_mills_inch.csv': {
62
+ readonly category: "FF01";
63
+ readonly facet: {
64
+ readonly code: "feature-HYBCL_PRODUKTMERKMALE-AMM_EINHS";
65
+ readonly value: "AMM_EINHS_Z";
66
+ };
67
+ };
68
+ readonly 'emuge_end_mills_mm.csv': {
69
+ readonly category: "FF01";
70
+ readonly facet: {
71
+ readonly code: "feature-HYBCL_PRODUKTMERKMALE-AMM_EINHS";
72
+ readonly value: "AMM_EINHS_M";
73
+ };
74
+ };
75
+ readonly 'emuge_drills.csv': {
76
+ readonly category: "FB01";
77
+ };
78
+ readonly 'emuge_taps.csv': {
79
+ readonly category: "FG01";
80
+ };
81
+ };
82
+ export declare const FAMILIES: {
83
+ readonly 'emuge_end_mills_inch.csv': {
84
+ readonly id: "end-mills-inch";
85
+ readonly brand: "emuge";
86
+ readonly kind: "endmill";
87
+ readonly familyCode: "FF01";
88
+ readonly rows: 1832;
89
+ readonly columns: {
90
+ readonly DC: "cutting diameter Ød₁";
91
+ readonly SFDM: "shank diameter Ød₂";
92
+ readonly OAL: "overall length l₁";
93
+ readonly LCF: "cutting length l₂";
94
+ readonly RE: "radius r₁";
95
+ readonly 'shoulder-length': "neck length l₃";
96
+ readonly 'shoulder-diameter': "neck diameter Ød₃";
97
+ readonly NOF: "number of flutes Z";
98
+ };
99
+ readonly facts: {
100
+ readonly unit: {
101
+ readonly value: "inches";
102
+ readonly source: "vendor-stated";
103
+ readonly cite: "the vendor's own `AMM_EINHS` facet, whose two values are `AMM_EINHS_Z` (inch, 1,832 variants) and `AMM_EINHS_M` (metric, 5,189); this family scrapes the first, and every dimensional value in it states `\"` rather than `mm`";
104
+ };
105
+ };
106
+ };
107
+ readonly 'emuge_end_mills_mm.csv': {
108
+ readonly id: "end-mills-mm";
109
+ readonly brand: "emuge";
110
+ readonly kind: "endmill";
111
+ readonly familyCode: "FF01";
112
+ readonly rows: 5189;
113
+ readonly columns: {
114
+ readonly DC: "cutting diameter Ød₁";
115
+ readonly SFDM: "shank diameter Ød₂";
116
+ readonly OAL: "overall length l₁";
117
+ readonly LCF: "cutting length l₂";
118
+ readonly RE: "radius r₁";
119
+ readonly 'shoulder-length': "neck length l₃";
120
+ readonly 'shoulder-diameter': "neck diameter Ød₃";
121
+ readonly NOF: "number of flutes Z";
122
+ };
123
+ readonly facts: {
124
+ readonly unit: {
125
+ readonly value: "millimeters";
126
+ readonly source: "vendor-stated";
127
+ readonly cite: "the same `AMM_EINHS` facet, `AMM_EINHS_M`; every dimensional value in this family states `mm`";
128
+ };
129
+ };
130
+ };
131
+ readonly 'emuge_drills.csv': {
132
+ readonly id: "drills";
133
+ readonly brand: "emuge";
134
+ readonly kind: "drill";
135
+ readonly familyCode: "FB01";
136
+ readonly columns: {
137
+ readonly DC: "nominal diameter d₁";
138
+ readonly SFDM: "Shank diameter d₂";
139
+ readonly OAL: "Overall length l₁";
140
+ readonly LCF: "Flute length l₂";
141
+ readonly SIG: "point angle";
142
+ };
143
+ readonly rows: 2670;
144
+ readonly facts: {
145
+ readonly unit: {
146
+ readonly value: "millimeters";
147
+ readonly source: "vendor-stated";
148
+ readonly cite: "every dimensional value on every drill variant states `mm`; the inch column the detail record also publishes (`nominal diameter d₁ [in]`) is a second index on the same part rather than a second family";
149
+ };
150
+ readonly flutes: {
151
+ readonly value: 2;
152
+ readonly source: "assumed";
153
+ readonly note: "EMUGE publishes no flute count for a drill anywhere a scrape can reach. All 17 grouped products in FB01 state `Specification: Twist drill`, which is a two-flute geometry, and the vendor publishes `Number of margins` (2 or 4) as a separate column — so the 4 that appears there is a margin count and not a flute count, and that column is in the CSV as the evidence";
154
+ readonly checked: "2026-09-01";
155
+ readonly by: "JG";
156
+ };
157
+ readonly nonFerrous: {
158
+ readonly value: false;
159
+ readonly source: "vendor-stated";
160
+ readonly cite: "the vendor's own `applicationMaterials` index rates these drills for P, M, K, N, S and H — the ferrous groups included — on every part sampled across all 17 grouped products";
161
+ };
162
+ };
163
+ };
164
+ readonly 'emuge_taps.csv': {
165
+ readonly id: "taps";
166
+ readonly brand: "emuge";
167
+ readonly kind: "tap";
168
+ readonly familyCode: "FG01";
169
+ readonly columns: {
170
+ readonly DC: "nominal diameter d₁";
171
+ readonly SFDM: "Shank diameter d₂";
172
+ readonly OAL: "Overall length l₁";
173
+ readonly LCF: "length of cutting edge l₂";
174
+ readonly TP: "pitch";
175
+ };
176
+ readonly rows: 11566;
177
+ readonly facts: {
178
+ readonly unit: {
179
+ readonly value: "millimeters";
180
+ readonly source: "vendor-stated";
181
+ readonly cite: "every tap dimension is published in millimetres whatever the thread standard — a `#4-40 UNC` tap states `nominal diameter d₁ [mm]` as `2.845 mm`, `pitch [mm]` as `0.635 mm`, and its shank and lengths in `mm` — with `thread symbol`, `nominal size` and `threads per inch` carrying the inch designation beside them";
182
+ };
183
+ };
184
+ };
185
+ };
@@ -0,0 +1,163 @@
1
+ /**
2
+ * EMUGE-FRANKEN's families.
3
+ *
4
+ * Four, one per catalog category crossed with the unit system the category is
5
+ * published in. That is coarser than the vendor's own marketing, which splits
6
+ * end mills fifteen ways by product line — TOP-Cut, Hard-Cut, Alu-Cut — and it
7
+ * is deliberate: EMUGE states the product line, the cutting material, the
8
+ * coating and the coolant supply **per part**, in columns, and a scraped column
9
+ * beats a family constant. Splitting by product line would turn four counted
10
+ * row totals into thirty and buy nothing a `product line` column does not
11
+ * already carry — and it is carried: `vendors/emuge/records.ts`'s
12
+ * `PRODUCT_LINE_COLUMNS` reads one per part onto `ToolRecord.productLine`, from
13
+ * a column every scrape already writes.
14
+ *
15
+ * So the only fact three of these four state is `unit`, and the fourth adds the
16
+ * two a drill record cannot be built without.
17
+ *
18
+ * ## `rows`
19
+ *
20
+ * The vendor's own result count for exactly the query the family scrapes, read
21
+ * on 2026-09-01. It is the second number `node/receipts.checkRows` exists to
22
+ * compare against, and the point is that nothing computes it from the file it
23
+ * is checking — so when EMUGE adds a size, the scrape and this table disagree
24
+ * and somebody looks.
25
+ *
26
+ * ## The unit split, and where it does not apply
27
+ *
28
+ * Milling is the one category EMUGE publishes in both systems: `AMM_EINHS`
29
+ * indexes every milling variant as inch or metric, and an inch part states its
30
+ * dimensions in fractional inches (`1 1/2 "`) where a metric one states
31
+ * millimetres. Drilling and tapping have no such facet and no such split —
32
+ * every drill and every tap, including a `#4-40 UNC` one, is published in
33
+ * millimetres. That is why `emuge_taps.csv` declares a `unit` where
34
+ * `families/kennametal.ts`'s taps declare none: there is no per-row thread
35
+ * system to read, because the vendor states one system for all of them.
36
+ */
37
+ /**
38
+ * The facet EMUGE indexes milling variants by unit system under.
39
+ *
40
+ * The vendor's own code, verbatim, including the German class name in the
41
+ * middle of it — it is a key in their search index rather than a label, and
42
+ * shortening it would be inventing a query.
43
+ */
44
+ const UNIT_FACET = 'feature-HYBCL_PRODUKTMERKMALE-AMM_EINHS';
45
+ export const SCRAPE_TARGETS = {
46
+ 'emuge_end_mills_inch.csv': {
47
+ category: 'FF01',
48
+ facet: { code: UNIT_FACET, value: 'AMM_EINHS_Z' },
49
+ },
50
+ 'emuge_end_mills_mm.csv': {
51
+ category: 'FF01',
52
+ facet: { code: UNIT_FACET, value: 'AMM_EINHS_M' },
53
+ },
54
+ 'emuge_drills.csv': { category: 'FB01' },
55
+ 'emuge_taps.csv': { category: 'FG01' },
56
+ };
57
+ /** Milling geometry, identical either side of the unit split. */
58
+ const MILLING_COLUMNS = {
59
+ DC: 'cutting diameter Ød₁',
60
+ SFDM: 'shank diameter Ød₂',
61
+ OAL: 'overall length l₁',
62
+ LCF: 'cutting length l₂',
63
+ RE: 'radius r₁',
64
+ 'shoulder-length': 'neck length l₃',
65
+ 'shoulder-diameter': 'neck diameter Ød₃',
66
+ NOF: 'number of flutes Z',
67
+ };
68
+ export const FAMILIES = {
69
+ 'emuge_end_mills_inch.csv': {
70
+ id: 'end-mills-inch',
71
+ brand: 'emuge',
72
+ kind: 'endmill',
73
+ familyCode: 'FF01',
74
+ rows: 1832,
75
+ columns: MILLING_COLUMNS,
76
+ facts: {
77
+ unit: {
78
+ value: 'inches',
79
+ source: 'vendor-stated',
80
+ cite: 'the vendor\'s own `AMM_EINHS` facet, whose two values are `AMM_EINHS_Z` (inch, 1,832 variants) and `AMM_EINHS_M` (metric, 5,189); this family scrapes the first, and every dimensional value in it states `"` rather than `mm`',
81
+ },
82
+ },
83
+ },
84
+ 'emuge_end_mills_mm.csv': {
85
+ id: 'end-mills-mm',
86
+ brand: 'emuge',
87
+ kind: 'endmill',
88
+ familyCode: 'FF01',
89
+ rows: 5189,
90
+ columns: MILLING_COLUMNS,
91
+ facts: {
92
+ unit: {
93
+ value: 'millimeters',
94
+ source: 'vendor-stated',
95
+ cite: 'the same `AMM_EINHS` facet, `AMM_EINHS_M`; every dimensional value in this family states `mm`',
96
+ },
97
+ },
98
+ },
99
+ 'emuge_drills.csv': {
100
+ id: 'drills',
101
+ brand: 'emuge',
102
+ kind: 'drill',
103
+ familyCode: 'FB01',
104
+ // `SIG` is a mapped column and not a fact, which no other drill family in
105
+ // this package manages: EMUGE states a point angle on the detail record of
106
+ // 2,669 of these 2,670 parts, and leaves the cell empty on the last, so the
107
+ // record may carry no `SIG` — see `vendors/emuge/records.ts`'s `angle`.
108
+ // Kennametal's two drill lines assume theirs or derive them from a point
109
+ // length, and both say so at length in `families/kennametal.ts`.
110
+ columns: {
111
+ DC: 'nominal diameter d₁',
112
+ SFDM: 'Shank diameter d₂',
113
+ OAL: 'Overall length l₁',
114
+ LCF: 'Flute length l₂',
115
+ SIG: 'point angle',
116
+ },
117
+ rows: 2670,
118
+ facts: {
119
+ unit: {
120
+ value: 'millimeters',
121
+ source: 'vendor-stated',
122
+ cite: 'every dimensional value on every drill variant states `mm`; the inch column the detail record also publishes (`nominal diameter d₁ [in]`) is a second index on the same part rather than a second family',
123
+ },
124
+ flutes: {
125
+ value: 2,
126
+ source: 'assumed',
127
+ note: 'EMUGE publishes no flute count for a drill anywhere a scrape can reach. All 17 grouped products in FB01 state `Specification: Twist drill`, which is a two-flute geometry, and the vendor publishes `Number of margins` (2 or 4) as a separate column — so the 4 that appears there is a margin count and not a flute count, and that column is in the CSV as the evidence',
128
+ checked: '2026-09-01',
129
+ by: 'JG',
130
+ },
131
+ nonFerrous: {
132
+ value: false,
133
+ source: 'vendor-stated',
134
+ cite: "the vendor's own `applicationMaterials` index rates these drills for P, M, K, N, S and H — the ferrous groups included — on every part sampled across all 17 grouped products",
135
+ },
136
+ },
137
+ },
138
+ 'emuge_taps.csv': {
139
+ id: 'taps',
140
+ brand: 'emuge',
141
+ kind: 'tap',
142
+ familyCode: 'FG01',
143
+ // `TP` reads a column with no unit suffix, which is what
144
+ // `records.DIMENSIONAL_COLUMNS` excluding it means: the vendor publishes
145
+ // `pitch [mm]` and this family is millimetres, so the column is already in
146
+ // the record's native unit.
147
+ columns: {
148
+ DC: 'nominal diameter d₁',
149
+ SFDM: 'Shank diameter d₂',
150
+ OAL: 'Overall length l₁',
151
+ LCF: 'length of cutting edge l₂',
152
+ TP: 'pitch',
153
+ },
154
+ rows: 11566,
155
+ facts: {
156
+ unit: {
157
+ value: 'millimeters',
158
+ source: 'vendor-stated',
159
+ cite: 'every tap dimension is published in millimetres whatever the thread standard — a `#4-40 UNC` tap states `nominal diameter d₁ [mm]` as `2.845 mm`, `pitch [mm]` as `0.635 mm`, and its shank and lengths in `mm` — with `thread symbol`, `nominal size` and `threads per inch` carrying the inch designation beside them',
160
+ },
161
+ },
162
+ },
163
+ };