@toolpath/tool-scraper 2.1.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/conventions.d.ts +36 -0
- package/dist/conventions.js +41 -0
- 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 +192 -18
- 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/registry.d.ts +61 -5
- package/dist/registry.js +109 -6
- package/dist/vendors/kennametal/holding.d.ts +35 -0
- package/dist/vendors/kennametal/holding.js +112 -0
- package/dist/vendors/kennametal/index.d.ts +1 -0
- package/dist/vendors/kennametal/index.js +1 -0
- 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
package/dist/conventions.d.ts
CHANGED
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
* | `Description` carries the vendor's own free text | Harvey, MariTool |
|
|
23
23
|
* | `contact` says how a holder seats | REGO-FIX, MariTool |
|
|
24
24
|
* | `CST` names the collet series a holder takes | REGO-FIX, MariTool |
|
|
25
|
+
* | `Collet Series` names the series a collet is | Kennametal, REGO-FIX |
|
|
25
26
|
* | `L1_in`/`L1_mm` carry a holder's gage length | REGO-FIX, MariTool |
|
|
26
27
|
* | Unmapped vendor codes keep a `DIN_` prefix | REGO-FIX; rule is general |
|
|
27
28
|
* | The identity columns | **broken** — see below |
|
|
@@ -134,6 +135,21 @@ export declare const CONTACT_COLUMN = "contact";
|
|
|
134
135
|
* the same reason.
|
|
135
136
|
*/
|
|
136
137
|
export declare const COLLET_SERIES_COLUMN = "CST";
|
|
138
|
+
/**
|
|
139
|
+
* The CSV column naming the series a collet **is** — `ER16`, `PG25`, `PGST15`.
|
|
140
|
+
*
|
|
141
|
+
* The other half of {@link COLLET_SERIES_COLUMN}, and deliberately a second
|
|
142
|
+
* column rather than the same one: `CST` is the series a *holder takes* and
|
|
143
|
+
* this is the series a *collet belongs to*, so the join between a holder family
|
|
144
|
+
* and a collet family is a comparison of the two. One column carrying both
|
|
145
|
+
* would make that join a row's comparison with itself.
|
|
146
|
+
*
|
|
147
|
+
* Vendor-neutral and here rather than in either adapter for the reason
|
|
148
|
+
* {@link CAD_COLUMN} is: Kennametal's ER collet tables and REGO-FIX's PG collet
|
|
149
|
+
* index both write it, and neither owns it. Both close the vendor's own spacing
|
|
150
|
+
* before writing, exactly as they do for `CST` — two spellings join to nothing.
|
|
151
|
+
*/
|
|
152
|
+
export declare const COLLET_DESIGNATION_COLUMN = "Collet Series";
|
|
137
153
|
/**
|
|
138
154
|
* The CSV columns carrying a holder's gage length, one per unit system.
|
|
139
155
|
*
|
|
@@ -206,6 +222,26 @@ export declare function dimensionalColumn(label: string, unit: string): string;
|
|
|
206
222
|
* indistinguishable from a scrape that lost it.
|
|
207
223
|
*/
|
|
208
224
|
export declare function identityColumns(brand: BrandName): readonly string[];
|
|
225
|
+
/**
|
|
226
|
+
* The identity column a human reads one part by.
|
|
227
|
+
*
|
|
228
|
+
* The catalog designation where the vendor publishes one, and the sole
|
|
229
|
+
* identifier where it does not — the **last** of {@link identityColumns},
|
|
230
|
+
* because that is the order the pair is written in and a deviation lists the
|
|
231
|
+
* one column its vendor has.
|
|
232
|
+
*
|
|
233
|
+
* A lookup rather than a read of the row, for the reason {@link identityColumns}
|
|
234
|
+
* gives: a header that is missing `ISO Catalog Number` is indistinguishable
|
|
235
|
+
* from a scrape that lost it, so a caller that fell back column by column would
|
|
236
|
+
* name half a family's files after part numbers and never say why.
|
|
237
|
+
*
|
|
238
|
+
* It exists because `node/cad-mirror.ts` names a mirrored file after the part
|
|
239
|
+
* it holds, and hardcoded `'ISO Catalog Number'` to do it. That is Kennametal's
|
|
240
|
+
* pair; MariTool publishes one number per part under `Material Number` and no
|
|
241
|
+
* catalog designation at all, so every one of its 357 STEP models was skipped
|
|
242
|
+
* with a warning that the row had no catalog number to name it.
|
|
243
|
+
*/
|
|
244
|
+
export declare function catalogColumn(brand: BrandName): string;
|
|
209
245
|
/**
|
|
210
246
|
* Every identity column `brand` claims is really in the CSV header.
|
|
211
247
|
*
|
package/dist/conventions.js
CHANGED
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
* | `Description` carries the vendor's own free text | Harvey, MariTool |
|
|
23
23
|
* | `contact` says how a holder seats | REGO-FIX, MariTool |
|
|
24
24
|
* | `CST` names the collet series a holder takes | REGO-FIX, MariTool |
|
|
25
|
+
* | `Collet Series` names the series a collet is | Kennametal, REGO-FIX |
|
|
25
26
|
* | `L1_in`/`L1_mm` carry a holder's gage length | REGO-FIX, MariTool |
|
|
26
27
|
* | Unmapped vendor codes keep a `DIN_` prefix | REGO-FIX; rule is general |
|
|
27
28
|
* | The identity columns | **broken** — see below |
|
|
@@ -135,6 +136,21 @@ export const CONTACT_COLUMN = 'contact';
|
|
|
135
136
|
* the same reason.
|
|
136
137
|
*/
|
|
137
138
|
export const COLLET_SERIES_COLUMN = 'CST';
|
|
139
|
+
/**
|
|
140
|
+
* The CSV column naming the series a collet **is** — `ER16`, `PG25`, `PGST15`.
|
|
141
|
+
*
|
|
142
|
+
* The other half of {@link COLLET_SERIES_COLUMN}, and deliberately a second
|
|
143
|
+
* column rather than the same one: `CST` is the series a *holder takes* and
|
|
144
|
+
* this is the series a *collet belongs to*, so the join between a holder family
|
|
145
|
+
* and a collet family is a comparison of the two. One column carrying both
|
|
146
|
+
* would make that join a row's comparison with itself.
|
|
147
|
+
*
|
|
148
|
+
* Vendor-neutral and here rather than in either adapter for the reason
|
|
149
|
+
* {@link CAD_COLUMN} is: Kennametal's ER collet tables and REGO-FIX's PG collet
|
|
150
|
+
* index both write it, and neither owns it. Both close the vendor's own spacing
|
|
151
|
+
* before writing, exactly as they do for `CST` — two spellings join to nothing.
|
|
152
|
+
*/
|
|
153
|
+
export const COLLET_DESIGNATION_COLUMN = 'Collet Series';
|
|
138
154
|
/**
|
|
139
155
|
* The CSV columns carrying a holder's gage length, one per unit system.
|
|
140
156
|
*
|
|
@@ -236,6 +252,31 @@ export function dimensionalColumn(label, unit) {
|
|
|
236
252
|
export function identityColumns(brand) {
|
|
237
253
|
return IDENTITY_DEVIATIONS[brand] ?? IDENTITY_COLUMNS;
|
|
238
254
|
}
|
|
255
|
+
/**
|
|
256
|
+
* The identity column a human reads one part by.
|
|
257
|
+
*
|
|
258
|
+
* The catalog designation where the vendor publishes one, and the sole
|
|
259
|
+
* identifier where it does not — the **last** of {@link identityColumns},
|
|
260
|
+
* because that is the order the pair is written in and a deviation lists the
|
|
261
|
+
* one column its vendor has.
|
|
262
|
+
*
|
|
263
|
+
* A lookup rather than a read of the row, for the reason {@link identityColumns}
|
|
264
|
+
* gives: a header that is missing `ISO Catalog Number` is indistinguishable
|
|
265
|
+
* from a scrape that lost it, so a caller that fell back column by column would
|
|
266
|
+
* name half a family's files after part numbers and never say why.
|
|
267
|
+
*
|
|
268
|
+
* It exists because `node/cad-mirror.ts` names a mirrored file after the part
|
|
269
|
+
* it holds, and hardcoded `'ISO Catalog Number'` to do it. That is Kennametal's
|
|
270
|
+
* pair; MariTool publishes one number per part under `Material Number` and no
|
|
271
|
+
* catalog designation at all, so every one of its 357 STEP models was skipped
|
|
272
|
+
* with a warning that the row had no catalog number to name it.
|
|
273
|
+
*/
|
|
274
|
+
export function catalogColumn(brand) {
|
|
275
|
+
const columns = identityColumns(brand);
|
|
276
|
+
// Non-null: `IDENTITY_COLUMNS` has two entries and every deviation has at
|
|
277
|
+
// least one, which `tests/conventions.test.ts` holds the table to.
|
|
278
|
+
return columns[columns.length - 1];
|
|
279
|
+
}
|
|
239
280
|
/**
|
|
240
281
|
* Every identity column `brand` claims is really in the CSV header.
|
|
241
282
|
*
|
package/dist/family.d.ts
CHANGED
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
*/
|
|
19
19
|
import type { UnitSystem } from './conventions.js';
|
|
20
20
|
import type { BrandName } from './identity.js';
|
|
21
|
+
import type { HoldingMapper, ToolholdingKind } from './holding.js';
|
|
21
22
|
import type { Fact } from './provenance.js';
|
|
22
23
|
import type { ColumnMap, ToolKind, ToolRecord } from './records.js';
|
|
23
24
|
import type { MapperOptions, ScrapedRow } from './scrape.js';
|
|
@@ -121,8 +122,21 @@ export type BoundFamily = Omit<FamilyDefinition, 'columns'> & FamilyFacts & {
|
|
|
121
122
|
/** The adapter that turns this family's rows into records. */
|
|
122
123
|
readonly records: RecordMapper;
|
|
123
124
|
};
|
|
124
|
-
/**
|
|
125
|
-
|
|
125
|
+
/**
|
|
126
|
+
* A toolholding family after its facts have been checked and projected.
|
|
127
|
+
*
|
|
128
|
+
* `kind` is projected by the registry from **which table declared the family**,
|
|
129
|
+
* rather than being a key on {@link ToolholdingDefinition}: the table is the
|
|
130
|
+
* fact, and a `kind` beside it would be a second copy free to disagree with it.
|
|
131
|
+
* `records` is the mapper that brand binds for that kind, and it is optional
|
|
132
|
+
* because a vendor's holders may be scraped long before anybody has read its
|
|
133
|
+
* columns — see `holding.HoldingMappers`.
|
|
134
|
+
*/
|
|
135
|
+
export type BoundToolholding = ToolholdingDefinition & FamilyFacts & {
|
|
136
|
+
readonly kind: ToolholdingKind;
|
|
137
|
+
/** The adapter that turns this family's rows into records, where there is one. */
|
|
138
|
+
readonly records?: HoldingMapper;
|
|
139
|
+
};
|
|
126
140
|
/**
|
|
127
141
|
* A family's id: `<brand>:<vendor-local id>`.
|
|
128
142
|
*
|
|
@@ -0,0 +1,396 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a holder and a collet become, and the two gates that refuse one.
|
|
3
|
+
*
|
|
4
|
+
* `records.ts` is the cutting-tool half of this package's output. This is the
|
|
5
|
+
* toolholding half, and it is a separate module rather than a third `ToolKind`
|
|
6
|
+
* because the three vocabularies genuinely do not overlap: a tool answers `DC`,
|
|
7
|
+
* a flute count and a workpiece material group; a holder answers a taper, a
|
|
8
|
+
* clamping mode and a gage length; a collet answers a series and a capacity
|
|
9
|
+
* band. `families/index.ts` states the same rule for the config tables —
|
|
10
|
+
*
|
|
11
|
+
* > Separate tables rather than a `kind` on one, because a holder and a collet
|
|
12
|
+
* > are not variants of a thing.
|
|
13
|
+
*
|
|
14
|
+
* — and the records follow it. What they *do* share is identity, the unit rule
|
|
15
|
+
* and the guid space, which is why both live here and not in two more files:
|
|
16
|
+
* `identity.recordGuid` mints a holder and a tool into one namespace per brand,
|
|
17
|
+
* so a consumer building a catalog from both can refuse a collision between
|
|
18
|
+
* them.
|
|
19
|
+
*
|
|
20
|
+
* ## Two copies of a dimension, and only where something compares them
|
|
21
|
+
*
|
|
22
|
+
* A fit-bearing dimension is kept twice — once in the family's native unit for
|
|
23
|
+
* display, once in canonical millimetres for arithmetic. The reason is that fit
|
|
24
|
+
* and filtering are different questions: a 3/8 in shank is 9.525 mm and
|
|
25
|
+
* genuinely seats in a metric 9-10 mm collet, so a fit comparison must convert,
|
|
26
|
+
* while a range *filter* must still refuse to, because "between 9 and 10 mm" is
|
|
27
|
+
* not a question an inch tool answers.
|
|
28
|
+
*
|
|
29
|
+
* Only {@link HolderRecord.bore}, {@link HolderRecord.gaugeLength},
|
|
30
|
+
* {@link ColletRecord.clampMin} and {@link ColletRecord.clampMax} get a twin.
|
|
31
|
+
* Everything else — usable length, body diameter, adjustment range — is
|
|
32
|
+
* displayed and never compared, and a second copy of a number nothing reads is
|
|
33
|
+
* a field to keep in sync for free.
|
|
34
|
+
*
|
|
35
|
+
* **The twin is derived here rather than read from the other unit column**, and
|
|
36
|
+
* that is a deliberate departure from the reference implementation, which read
|
|
37
|
+
* `D1_mm` directly whatever the family's unit was. Vendors publish pairs that
|
|
38
|
+
* disagree — see {@link checkUnitAgreement} — and a record whose `bore` and
|
|
39
|
+
* `boreMm` came from two contradicting cells is one record stating two sizes.
|
|
40
|
+
* Deriving makes `boreMm` exactly `bore` in millimetres by construction, so the
|
|
41
|
+
* pair cannot drift and a test can pin one to the other.
|
|
42
|
+
*
|
|
43
|
+
* ## Which refusal a vendor's row earns
|
|
44
|
+
*
|
|
45
|
+
* The split is **blank versus wrong**, and it decides whether one bad part ends
|
|
46
|
+
* a family:
|
|
47
|
+
*
|
|
48
|
+
* - A cell the vendor left **blank** that a part cannot exist without — no gage
|
|
49
|
+
* length, no bore on a bore-clamping holder, no capacity on a collet — is an
|
|
50
|
+
* `errors.IncompletePartError`. `registry.toHolding` warns and drops the row,
|
|
51
|
+
* the same call `registry.toRecords` makes for a cutting tool, and for the
|
|
52
|
+
* same reason: MariTool leaves `Shank Size` blank on four HSK holders out of
|
|
53
|
+
* 527, and losing five families over four rows is not a trade worth making.
|
|
54
|
+
* - A value that is **present and unreadable**, or two present values that
|
|
55
|
+
* **contradict** — a clamping mode this package has no word for, a holder
|
|
56
|
+
* that names both a bore and a collet series, a collet whose nominal size
|
|
57
|
+
* falls outside its own published capacity — is an `errors.VendorResponseError`
|
|
58
|
+
* and stops the family. Those say the vendor's vocabulary or this package's
|
|
59
|
+
* reading of it has moved, and skipping past one quietly is how a scraper
|
|
60
|
+
* starts publishing a catalog nobody checked.
|
|
61
|
+
*/
|
|
62
|
+
import { type UnitSystem } from './conventions.js';
|
|
63
|
+
import type { BoundToolholding } from './family.js';
|
|
64
|
+
import { type BrandName } from './identity.js';
|
|
65
|
+
import { type ScrapedRow, type Warn } from './scrape.js';
|
|
66
|
+
/**
|
|
67
|
+
* How a holder grips the thing it holds.
|
|
68
|
+
*
|
|
69
|
+
* Four values where the reference implementation had two. `bore` and `collet`
|
|
70
|
+
* are its own; `shrink` and `hydraulic` are here because MariTool's leaf
|
|
71
|
+
* categories already classify parts as those and the distinction is a real one
|
|
72
|
+
* a buyer makes — a shrink-fit holder needs an induction heater on the bench
|
|
73
|
+
* and a hydraulic chuck needs an actuation screw, where both are otherwise the
|
|
74
|
+
* same answer to "what fits in it".
|
|
75
|
+
*
|
|
76
|
+
* They are the same *fit* question, which is what {@link BORE_CLAMPINGS} says:
|
|
77
|
+
* all three grip a shank directly and are held to one rule. Kennametal's
|
|
78
|
+
* shrink-fit and hydraulic families declare `bore` and stay declaring it —
|
|
79
|
+
* the vendor states the mode as a bore and this package does not re-classify a
|
|
80
|
+
* family's own words. `style` is the finer axis and already carries
|
|
81
|
+
* `shrink-fit-gp` and `hydraulic-chuck`.
|
|
82
|
+
*/
|
|
83
|
+
export type ClampingMode = 'bore' | 'collet' | 'shrink' | 'hydraulic';
|
|
84
|
+
/** Every {@link ClampingMode}, for a message that can list what it knows. */
|
|
85
|
+
export declare const CLAMPING_MODES: readonly ClampingMode[];
|
|
86
|
+
/**
|
|
87
|
+
* The clamping modes that grip a shank directly, and therefore publish a bore.
|
|
88
|
+
*
|
|
89
|
+
* One rule over three values rather than three rules: the gate in
|
|
90
|
+
* {@link checkHolder} asks whether a holder takes a shank or a collet, and
|
|
91
|
+
* every mode but `collet` takes a shank.
|
|
92
|
+
*/
|
|
93
|
+
export declare const BORE_CLAMPINGS: readonly ClampingMode[];
|
|
94
|
+
/**
|
|
95
|
+
* Whether the flange face seats on the spindle face as well as the cone.
|
|
96
|
+
*
|
|
97
|
+
* Never defaulted. `taper` is the common case, and defaulting to it would
|
|
98
|
+
* record a dual-contact family as a plain cone on no evidence — the same
|
|
99
|
+
* silent-wrong-answer shape as a bore-clamping holder with no bore.
|
|
100
|
+
*/
|
|
101
|
+
export type ContactMode = 'taper' | 'face';
|
|
102
|
+
/** Every {@link ContactMode}, for the same reason {@link CLAMPING_MODES} is a list. */
|
|
103
|
+
export declare const CONTACT_MODES: readonly ContactMode[];
|
|
104
|
+
/** What every toolholding record shares with every `records.ToolRecord`. */
|
|
105
|
+
export interface HoldingIdentity {
|
|
106
|
+
/** `holder` or `collet` — which of the two record types this is. */
|
|
107
|
+
readonly kind: ToolholdingKind;
|
|
108
|
+
/**
|
|
109
|
+
* The brand key the record was minted under — `identity.BRANDS`'s own key.
|
|
110
|
+
*
|
|
111
|
+
* Here for the reason `records.ToolRecord.brand` is: {@link HoldingIdentity.guid}
|
|
112
|
+
* is minted in this brand's namespace, so without the key the guid is
|
|
113
|
+
* underivable from the record.
|
|
114
|
+
*/
|
|
115
|
+
readonly brand: BrandName;
|
|
116
|
+
/** What this brand's records call the vendor — `identity.BRANDS[brand].vendor`. */
|
|
117
|
+
readonly vendor: string;
|
|
118
|
+
/** `identity.recordGuid(brand, materialNumber)`, minted by the factories below. */
|
|
119
|
+
readonly guid: string;
|
|
120
|
+
readonly materialNumber: string;
|
|
121
|
+
readonly catalogNumber: string;
|
|
122
|
+
/**
|
|
123
|
+
* The vendor's own free text about this part, verbatim — `''` where the
|
|
124
|
+
* vendor publishes none.
|
|
125
|
+
*
|
|
126
|
+
* **Never a copy of another field**, the rule `records.ToolRecord.description`
|
|
127
|
+
* already states. The reference implementation set it to the catalog number
|
|
128
|
+
* on every holder and collet, which put one string in two fields and told a
|
|
129
|
+
* consumer nothing it did not already have. Kennametal and REGO-FIX publish
|
|
130
|
+
* no description column for toolholding, so `''` is the honest answer for
|
|
131
|
+
* both; MariTool publishes a product name and that is what its records carry.
|
|
132
|
+
*/
|
|
133
|
+
readonly description: string;
|
|
134
|
+
readonly productLink: string;
|
|
135
|
+
/**
|
|
136
|
+
* Which unit system this record's native dimensions are in.
|
|
137
|
+
*
|
|
138
|
+
* **Per record, not per family.** REGO-FIX publishes `PG 25 Ø 6.0 mm` and
|
|
139
|
+
* `PG 25 Ø 1/4"` as two rows of one product group, and MariTool gages two
|
|
140
|
+
* parts on one listing page in different systems. A family-level constant
|
|
141
|
+
* would be contradicted row by row, which is why `families/maritool.ts`
|
|
142
|
+
* declares no `unit` fact and the scraper promotes an `L1_in`/`L1_mm` pair
|
|
143
|
+
* with one cell filled instead.
|
|
144
|
+
*/
|
|
145
|
+
readonly unit: UnitSystem;
|
|
146
|
+
}
|
|
147
|
+
/** One holder — a spindle interface, a way of gripping, and a gage length. */
|
|
148
|
+
export interface HolderRecord extends HoldingIdentity {
|
|
149
|
+
readonly kind: 'holder';
|
|
150
|
+
/** The spindle interface, as the vendor designates it — `BT30`, `HSK63A`. */
|
|
151
|
+
readonly taper: string;
|
|
152
|
+
readonly contact: ContactMode;
|
|
153
|
+
readonly clamping: ClampingMode;
|
|
154
|
+
/** The vendor's own product style — `er-collet-chuck`, `shrink-fit-gp`. */
|
|
155
|
+
readonly style: string;
|
|
156
|
+
/**
|
|
157
|
+
* The collet series this holder takes, on a collet-clamping holder only.
|
|
158
|
+
*
|
|
159
|
+
* Joins to {@link ColletRecord.series}. Written exactly as the vendor
|
|
160
|
+
* designates it, so a `PGST15` collet matches no `PG25` holder — the
|
|
161
|
+
* conservative direction on purpose, because hiding a collet that would have
|
|
162
|
+
* fitted costs an option while offering one that does not fit costs a
|
|
163
|
+
* machinist a purchase.
|
|
164
|
+
*/
|
|
165
|
+
readonly colletSeries: string | null;
|
|
166
|
+
/** `D1` — the bore a shank seats in, on a bore-clamping holder only. */
|
|
167
|
+
readonly bore: number | null;
|
|
168
|
+
readonly boreMm: number | null;
|
|
169
|
+
/** `L1` — gage line to nose. Required: without it there is no stickout. */
|
|
170
|
+
readonly gaugeLength: number;
|
|
171
|
+
readonly gaugeLengthMm: number;
|
|
172
|
+
/** `L2` — usable length. */
|
|
173
|
+
readonly usableLength: number | null;
|
|
174
|
+
/** `L9` — clamping length. */
|
|
175
|
+
readonly clampingLength: number | null;
|
|
176
|
+
/** `V` — the adjustment range. */
|
|
177
|
+
readonly adjustmentRange: number | null;
|
|
178
|
+
/** `D2` — body diameter. */
|
|
179
|
+
readonly bodyDiameter: number | null;
|
|
180
|
+
/** `D11` — lock-nut diameter. */
|
|
181
|
+
readonly lockNutDiameter: number | null;
|
|
182
|
+
/** `conventions.CAD_COLUMN`, or null where the vendor publishes no model. */
|
|
183
|
+
readonly cadModelUrl: string | null;
|
|
184
|
+
/** `conventions.CAD_DXF_COLUMN`, or null where the vendor publishes no profile. */
|
|
185
|
+
readonly cadDxfUrl: string | null;
|
|
186
|
+
}
|
|
187
|
+
/** One collet — a series, a capacity band, and the sizes in between. */
|
|
188
|
+
export interface ColletRecord extends HoldingIdentity {
|
|
189
|
+
readonly kind: 'collet';
|
|
190
|
+
/** `ER16`, `PG25`, `PGST15` — joins to {@link HolderRecord.colletSeries}. */
|
|
191
|
+
readonly series: string;
|
|
192
|
+
/** The vendor's own product style — `er-standard`, `pg-coolant-flush`. */
|
|
193
|
+
readonly style: string;
|
|
194
|
+
/** `D1` — the nominal size the vendor designates the collet by. */
|
|
195
|
+
readonly nominal: number | null;
|
|
196
|
+
/**
|
|
197
|
+
* `CCCN`/`CCCX` — the vendor's published clamping capacity, never derived.
|
|
198
|
+
*
|
|
199
|
+
* DIN 6499 is usually summarised as a 1 mm band, which is wrong at the small
|
|
200
|
+
* end of every series (`16ER010M` clamps 1.0 down to 0.5) and wrong by a
|
|
201
|
+
* whole millimetre on a sealed coolant-through collet, where the two are
|
|
202
|
+
* equal and the collet takes one exact size.
|
|
203
|
+
*/
|
|
204
|
+
readonly clampMin: number;
|
|
205
|
+
readonly clampMax: number;
|
|
206
|
+
readonly clampMinMm: number;
|
|
207
|
+
readonly clampMaxMm: number;
|
|
208
|
+
/** `BDX` — body diameter. */
|
|
209
|
+
readonly bodyDiameter: number | null;
|
|
210
|
+
/** `LF` — functional length. */
|
|
211
|
+
readonly functionalLength: number | null;
|
|
212
|
+
/** `L` — overall length. */
|
|
213
|
+
readonly overallLength: number | null;
|
|
214
|
+
}
|
|
215
|
+
/** Either toolholding record. Narrow on {@link HoldingIdentity.kind}. */
|
|
216
|
+
export type HoldingRecord = HolderRecord | ColletRecord;
|
|
217
|
+
/**
|
|
218
|
+
* Which of the two toolholding tables a family came from.
|
|
219
|
+
*
|
|
220
|
+
* Not a key on `family.ToolholdingDefinition`: which table declares a family
|
|
221
|
+
* *is* the fact, and a `kind` beside it would be a second copy to disagree with
|
|
222
|
+
* it. `registry.boundToolholding` projects it onto the bound config, which is
|
|
223
|
+
* where the registry already knows the answer.
|
|
224
|
+
*/
|
|
225
|
+
export type ToolholdingKind = 'holder' | 'collet';
|
|
226
|
+
/** One toolholding row -> one record. A vendor adapter supplies these. */
|
|
227
|
+
export type HolderMapper = (row: ScrapedRow, family: BoundToolholding, options?: {
|
|
228
|
+
warn?: Warn;
|
|
229
|
+
}) => HolderRecord;
|
|
230
|
+
/** The collet half of the same contract. */
|
|
231
|
+
export type ColletMapper = (row: ScrapedRow, family: BoundToolholding, options?: {
|
|
232
|
+
warn?: Warn;
|
|
233
|
+
}) => ColletRecord;
|
|
234
|
+
/**
|
|
235
|
+
* A vendor adapter's toolholding mappers, both optional.
|
|
236
|
+
*
|
|
237
|
+
* **Partial on purpose.** REGO-FIX publishes holders and collets, MariTool
|
|
238
|
+
* holders only, and Harvey, EMUGE and Destiny Tool neither. A brand absent from
|
|
239
|
+
* `registry.HOLDING_ADAPTERS`, or present with no mapper for the kind, keeps
|
|
240
|
+
* today's behaviour exactly: the scrape ends at rows and a receipt. That is
|
|
241
|
+
* what makes minting records additive rather than a break, and it is the honest
|
|
242
|
+
* state for a vendor whose columns nobody has read yet.
|
|
243
|
+
*/
|
|
244
|
+
export interface HoldingMappers {
|
|
245
|
+
holder?: HolderMapper;
|
|
246
|
+
collet?: ColletMapper;
|
|
247
|
+
}
|
|
248
|
+
/** Either mapper, as the registry stores the one a family binds. */
|
|
249
|
+
export type HoldingMapper = HolderMapper | ColletMapper;
|
|
250
|
+
/**
|
|
251
|
+
* A per-family constant a toolholding mapper cannot proceed without.
|
|
252
|
+
*
|
|
253
|
+
* `family.fact`'s counterpart for a family with no `id` and no `ToolKind`. The
|
|
254
|
+
* two are separate rather than one widened function because the subject of the
|
|
255
|
+
* message differs: a cutting-tool family is named by its vendor-local id and a
|
|
256
|
+
* toolholding family by the catalog name a human reads.
|
|
257
|
+
*
|
|
258
|
+
* Refusing rather than defaulting, for the reason `family.fact` states: every
|
|
259
|
+
* default is a claim the family never made, and a missing `taper` becoming
|
|
260
|
+
* `''` ships a holder that fits no spindle and raises nothing.
|
|
261
|
+
*/
|
|
262
|
+
export declare function holdingFact<T>(family: BoundToolholding, key: string, value: T | undefined): T;
|
|
263
|
+
/**
|
|
264
|
+
* A value the vendor left blank on one part, refused as an incomplete part.
|
|
265
|
+
*
|
|
266
|
+
* The toolholding counterpart of `columns.required`, and it throws the same
|
|
267
|
+
* type for the same reason: this is the one refusal `registry.toHolding` skips
|
|
268
|
+
* past, because a single part with an unpublished cell must not end a family's
|
|
269
|
+
* conversion. See `errors.IncompletePartError` for why the others must not be
|
|
270
|
+
* skipped alike.
|
|
271
|
+
*/
|
|
272
|
+
export declare function published<T extends string | number>(value: T | null | undefined, what: string, label: string): T;
|
|
273
|
+
/**
|
|
274
|
+
* `value`, stated in `from`, as a number in `to` — a no-op when they agree.
|
|
275
|
+
*
|
|
276
|
+
* The one conversion every toolholding mapper makes, so that the rounding above
|
|
277
|
+
* is applied in one place rather than wherever somebody remembers to.
|
|
278
|
+
*/
|
|
279
|
+
export declare function asUnit(value: number, from: UnitSystem, to: UnitSystem): number;
|
|
280
|
+
/** `value`, stated in `unit`, as millimetres. */
|
|
281
|
+
export declare function millimeters(value: number, unit: UnitSystem): number;
|
|
282
|
+
export declare function millimeters(value: number | null, unit: UnitSystem): number | null;
|
|
283
|
+
/**
|
|
284
|
+
* One dimension in `unit`, converted from the other system where that is all
|
|
285
|
+
* the vendor published.
|
|
286
|
+
*
|
|
287
|
+
* **The fallback is load-bearing, not defensive.** Kennametal's `D1` is a unit
|
|
288
|
+
* pair on the BT30 hydraulic chucks and metric-only on the HSK63A HP line — an
|
|
289
|
+
* *inch* family with no `D1_in` column at all. A bare suffixed read is correct
|
|
290
|
+
* on the first and yields null on the second, producing a holder with no bore:
|
|
291
|
+
* it matches no tool, raises nothing, and looks like an empty result rather
|
|
292
|
+
* than a bug.
|
|
293
|
+
*
|
|
294
|
+
* The grammar is `measure.fractionValue`'s, which is the package's one reader
|
|
295
|
+
* for a machinist's number and refuses a range rather than summing it.
|
|
296
|
+
*/
|
|
297
|
+
export declare function dim(row: ScrapedRow, label: string, unit: UnitSystem): number | null;
|
|
298
|
+
/**
|
|
299
|
+
* Report where a vendor's own millimetre and inch cells disagree.
|
|
300
|
+
*
|
|
301
|
+
* **Vendors really do publish contradictory pairs.** Kennametal's `16ERSS0312`
|
|
302
|
+
* states `D1`'s metric cell as `0.3125` — the inch value sitting in the metric
|
|
303
|
+
* column, a factor of 25.4 out — and `25ER130M` publishes `CCCN` as both
|
|
304
|
+
* 12.0 mm and 0.437 in, which is 11.1 mm. Both are in the source HTML.
|
|
305
|
+
*
|
|
306
|
+
* **This reports; it does not gate.** Two disagreeing cells cannot say which
|
|
307
|
+
* one is wrong, so refusing the family would trade a knowable warning for an
|
|
308
|
+
* unusable pipeline, and correcting a cell here would make this package a place
|
|
309
|
+
* tool data is authored by hand. What protects the output instead is that
|
|
310
|
+
* {@link dim} reads the family's *native* column and never the other one, plus
|
|
311
|
+
* {@link checkCollet}'s native-unit test that a nominal size falls inside its
|
|
312
|
+
* own published capacity. Every disagreement found so far sits in the column
|
|
313
|
+
* {@link dim} ignores; escalate this to a gate if one ever lands in a native
|
|
314
|
+
* column.
|
|
315
|
+
*
|
|
316
|
+
* **The tolerance is the vendor's own rounding, not a percentage.** A relative
|
|
317
|
+
* tolerance cannot tell rounding from error at small sizes: `16ER010M`
|
|
318
|
+
* publishes 0.5 mm as `0.02` in, correct to the two decimals it states and
|
|
319
|
+
* 1.6 % off as a ratio. Half a unit in each column's last printed place is
|
|
320
|
+
* exactly the slack the printed precision allows, and it is nowhere near the
|
|
321
|
+
* 7.6 mm a value in the wrong column produces.
|
|
322
|
+
*
|
|
323
|
+
* Returns whether it warned, so a caller can count.
|
|
324
|
+
*/
|
|
325
|
+
export declare function checkUnitAgreement(row: ScrapedRow, label: string, what: string, warn?: Warn): boolean;
|
|
326
|
+
/**
|
|
327
|
+
* One cell as a {@link ContactMode}, refusing a word this package cannot read.
|
|
328
|
+
*
|
|
329
|
+
* A `VendorResponseError` and not an incomplete part: a *blank* contact is the
|
|
330
|
+
* caller's `published` call, and a contact the vendor states in a word nobody
|
|
331
|
+
* has mapped is the vocabulary having moved.
|
|
332
|
+
*/
|
|
333
|
+
export declare function contactMode(value: string, what: string): ContactMode;
|
|
334
|
+
/** One cell as a {@link ClampingMode}, on the same terms as {@link contactMode}. */
|
|
335
|
+
export declare function clampingMode(value: string, what: string): ClampingMode;
|
|
336
|
+
/**
|
|
337
|
+
* One cell as a {@link UnitSystem}, on the same terms as {@link contactMode}.
|
|
338
|
+
*
|
|
339
|
+
* For the vendor that states the unit per row rather than per family: REGO-FIX
|
|
340
|
+
* publishes `PG 25 Ø 6.0 mm` and `PG 25 Ø 1/4"` in one product group, so its
|
|
341
|
+
* collet scrape writes the system it read off each designation into a column.
|
|
342
|
+
*/
|
|
343
|
+
export declare function unitSystem(value: string, what: string): UnitSystem;
|
|
344
|
+
/**
|
|
345
|
+
* Vendor HTML is a system boundary, so this validates rather than guarding
|
|
346
|
+
* against a caller mistake.
|
|
347
|
+
*
|
|
348
|
+
* The `clamping` discriminant and the fields it implies must agree. **That is
|
|
349
|
+
* what turns Kennametal's missing-bore case into a failed conversion instead of
|
|
350
|
+
* a holder that quietly fits nothing** — a bore-clamping holder with no bore is
|
|
351
|
+
* the exact shape of that bug, and it raises nothing anywhere else.
|
|
352
|
+
*/
|
|
353
|
+
export declare function checkHolder(record: HolderRecord): void;
|
|
354
|
+
/**
|
|
355
|
+
* The same boundary rule for a collet.
|
|
356
|
+
*
|
|
357
|
+
* A collet with no capacity would match every shank or none depending on which
|
|
358
|
+
* way a comparison read a null, which is why {@link ColletRecord.clampMin} and
|
|
359
|
+
* {@link ColletRecord.clampMax} are not nullable and the mapper refuses the row
|
|
360
|
+
* before it gets here.
|
|
361
|
+
*/
|
|
362
|
+
export declare function checkCollet(record: ColletRecord): void;
|
|
363
|
+
/**
|
|
364
|
+
* The nullable holder dimensions a mapper may simply not mention.
|
|
365
|
+
*
|
|
366
|
+
* They stay **required on the type** so a consumer reading a record never
|
|
367
|
+
* handles `undefined`; only the construction is optional, which is the shape
|
|
368
|
+
* `records.toolRecord` already has. Kennametal publishes every one of them and
|
|
369
|
+
* REGO-FIX four fewer, and writing `usableLength: null` five times in an
|
|
370
|
+
* adapter is how a null becomes a default nobody notices.
|
|
371
|
+
*/
|
|
372
|
+
type OptionalHolderFields = 'colletSeries' | 'bore' | 'usableLength' | 'clampingLength' | 'adjustmentRange' | 'bodyDiameter' | 'lockNutDiameter' | 'cadModelUrl' | 'cadDxfUrl';
|
|
373
|
+
/** What a mapper supplies to build a holder; the rest is derived or minted. */
|
|
374
|
+
type HolderFields = Omit<HolderRecord, 'kind' | 'guid' | 'vendor' | 'productLink' | 'boreMm' | 'gaugeLengthMm' | OptionalHolderFields> & Partial<Pick<HolderRecord, OptionalHolderFields>>;
|
|
375
|
+
/**
|
|
376
|
+
* Build a {@link HolderRecord}: mint its guid, derive its millimetre twins, and
|
|
377
|
+
* refuse the states that cannot be true.
|
|
378
|
+
*
|
|
379
|
+
* `guid`, `vendor` and `productLink` are not inputs at all — every adapter
|
|
380
|
+
* minting them would be three copies of `recordGuid(brand, materialNumber)` to
|
|
381
|
+
* drift, on the value that is the join key for every downstream consumer. The
|
|
382
|
+
* nullable dimensions stay **required on the type** so a consumer reading a
|
|
383
|
+
* record never handles `undefined`; only the construction is optional, which is
|
|
384
|
+
* the shape `records.toolRecord` already has.
|
|
385
|
+
*
|
|
386
|
+
* The result is frozen: a record is an interchange value, and a mapper that
|
|
387
|
+
* mutated one would be reaching back across the seam this type exists to draw.
|
|
388
|
+
*/
|
|
389
|
+
export declare function holderRecord(fields: HolderFields): HolderRecord;
|
|
390
|
+
/** The same, for a collet. REGO-FIX publishes none of these four. */
|
|
391
|
+
type OptionalColletFields = 'nominal' | 'bodyDiameter' | 'functionalLength' | 'overallLength';
|
|
392
|
+
/** What a mapper supplies to build a collet. */
|
|
393
|
+
type ColletFields = Omit<ColletRecord, 'kind' | 'guid' | 'vendor' | 'productLink' | 'clampMinMm' | 'clampMaxMm' | OptionalColletFields> & Partial<Pick<ColletRecord, OptionalColletFields>>;
|
|
394
|
+
/** Build a {@link ColletRecord}, on the same terms as {@link holderRecord}. */
|
|
395
|
+
export declare function colletRecord(fields: ColletFields): ColletRecord;
|
|
396
|
+
export {};
|