hazo_scrape 1.6.0 → 1.6.2
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/CHANGE_LOG.md +27 -0
- package/README.md +69 -11
- package/dist/parse/extract_table.d.ts +6 -3
- package/dist/parse/extract_table.d.ts.map +1 -1
- package/dist/parse/extract_table.js +63 -8
- package/package.json +2 -2
package/CHANGE_LOG.md
CHANGED
|
@@ -1,5 +1,32 @@
|
|
|
1
1
|
# hazo_scrape — Change Log
|
|
2
2
|
|
|
3
|
+
## 1.6.1 — 2026-07-29
|
|
4
|
+
|
|
5
|
+
Four `extractTable` correctness fixes. Kept as a **separate version** rather than folded into the (unpublished) 1.6.0: three of the four regress behaviour that shipped in **1.3.0** and is live on npm, so they deserve their own visible `Fixed` entry attributable to a version consumers can upgrade *to*, instead of being buried inside 1.6.0's `Added` list.
|
|
6
|
+
|
|
7
|
+
### Fixed — grid mode duplicated the header row when `headerRow` matched nothing
|
|
8
|
+
- `extractTable(html, { grid: { …, headerRow } })` with a typo'd/stale `headerRow` selector emitted the header as **data row 0**. The fallback correctly re-read row 0 as the header, but the "skip row 0" guard was gated on `grid.headerRow == null`, so it could never fire once `headerRow` was supplied. A `headerRow` that matches nothing now degrades to exactly the same behaviour as omitting it.
|
|
9
|
+
|
|
10
|
+
### Fixed — the repeated-in-cell-header strip truncated legitimate values
|
|
11
|
+
- The responsive-label strip (1.3.0) only tested `text.startsWith(header)`, which butchered real values: header `2025` + cell `2025-06-30` → `-06-30` (then `parseDate` → `null`); header `Total` + cell `Total dividend` → `dividend`. The strip now requires a genuine **DOM-concatenation seam**, matching the pattern it exists for (`<div>Payment date</div><div>2 July 2026</div>` → `"Payment date2 July 2026"`):
|
|
12
|
+
- the header must **not** be followed by whitespace — whitespace means the cell reads as one natural phrase, not two glued nodes; and
|
|
13
|
+
- the remainder must not continue the header's own token — if the header ends in a digit and the remainder opens with a digit or a numeric joiner (`-` `/` `.` `:`), the "header" is really a prefix of one longer number/date.
|
|
14
|
+
- Everything else (letter → digit, letter → letter with no space) is still stripped, so the NAB responsive case is unchanged.
|
|
15
|
+
|
|
16
|
+
### Fixed — row-header alignment when the header row has NO corner cell
|
|
17
|
+
- 1.3.0's `td` → `th, td` change fixed tables that pair a `<th scope="row">` row label with a corner cell in the header row, but broke the equally common **cornerless** variant: header `<th>Ex date</th><th>Amount</th>` with row `<th scope="row">FY25 Final</th><td>1 Jan 2026</td><td>45c</td>` shifted every column right by one (`Ex date` read as `"FY25 Final"`). Data rows are now reconciled against the header width: when a row is wider than the header and the excess is fully explained by leading `<th>` cells, exactly that many leading row-header cells are dropped. **Both** shapes now map correctly; a merely ragged row (extra `<td>`s, or a leading `<td>`) is untouched.
|
|
18
|
+
|
|
19
|
+
### Fixed — combine mode leaked unrelated tables past a non-table sibling
|
|
20
|
+
- `combine` documented "stopping at the first sibling that doesn't match", but the walk used `nextAll('table')`, which **filters out** non-table siblings rather than stopping at them — so an intervening `<h2>`/`<p>` never triggered the `break` and a later, unrelated same-width table was absorbed as data. The walk now visits every following sibling and breaks on the first that is not a `<table>` (as well as on the first width mismatch), matching the documented contract.
|
|
21
|
+
|
|
22
|
+
### Docs
|
|
23
|
+
- `README.md`'s `extractTable` entry documented only `opts.select`. It now documents `grid`, `combine`, `cellIgnoreSelectors`, precedence between the modes, and row-header reconciliation; `mapRows`/`parseDate` gained the `dateYearPivot`/`yearPivot` options added in 1.6.0.
|
|
24
|
+
|
|
25
|
+
### Known remaining (out of scope, pre-existing)
|
|
26
|
+
- `parseNumber('1.234,56')` (European locale) is misread — only comma-as-thousands is handled.
|
|
27
|
+
- Accounting-style negatives (`(1.23)`) are not recognised as `-1.23`.
|
|
28
|
+
- `extractTableFromPdf` keeps a multi-page PDF's per-page repeated header as data rows.
|
|
29
|
+
|
|
3
30
|
## 1.6.0 — 2026-07-25
|
|
4
31
|
|
|
5
32
|
### Added — `parseNumber` tokenizes a trailing bare cent marker
|
package/README.md
CHANGED
|
@@ -61,13 +61,54 @@ console.log(rows[0].exDate[0].value); // "2026-03-05"
|
|
|
61
61
|
|
|
62
62
|
## API
|
|
63
63
|
|
|
64
|
-
### `extractTable(html
|
|
64
|
+
### `extractTable(html, opts?): { headers: string[]; rows: string[][]; warning?: string }`
|
|
65
|
+
|
|
66
|
+
```ts
|
|
67
|
+
extractTable(html: string, opts?: {
|
|
68
|
+
select?: string;
|
|
69
|
+
grid?: { container?: string; row: string; cell: string; headerRow?: string };
|
|
70
|
+
combine?: { headerSelector: string; dropLabelRows?: boolean };
|
|
71
|
+
cellIgnoreSelectors?: string[];
|
|
72
|
+
}): ExtractTableResult
|
|
73
|
+
```
|
|
65
74
|
|
|
66
75
|
Parses network-free HTML and returns the best-guess data table as raw header
|
|
67
|
-
and row strings.
|
|
68
|
-
`<table>`
|
|
69
|
-
|
|
70
|
-
|
|
76
|
+
and row strings. With no options a scoring heuristic picks the most
|
|
77
|
+
data-table-like `<table>` on the page. A `warning` is returned (never thrown)
|
|
78
|
+
when no table-like element is found, or when a selector matches nothing.
|
|
79
|
+
|
|
80
|
+
Precedence: `grid` > `combine` > `select`.
|
|
81
|
+
|
|
82
|
+
- **`select`** — a CSS selector targeting a specific `<table>` instead of
|
|
83
|
+
letting the scoring heuristic choose.
|
|
84
|
+
|
|
85
|
+
- **`grid`** — read a "table" built from styled `<div>`s (CSS grid/flex)
|
|
86
|
+
rather than a real `<table>`; common on IR pages whose real table is
|
|
87
|
+
JS-rendered. `row`/`cell` are required; `container` scopes the search (when
|
|
88
|
+
it matches several grids, the one with the most data rows wins); `headerRow`
|
|
89
|
+
identifies the header among the `row` matches (rows matching it are never
|
|
90
|
+
data). Omit `headerRow` — or give one that matches nothing — and the first
|
|
91
|
+
row is used as the header and excluded from the data.
|
|
92
|
+
|
|
93
|
+
- **`combine`** — reconstruct one logical table from a header-only `<table>`
|
|
94
|
+
followed by one `<table>` per section (e.g. per year), a common
|
|
95
|
+
Computershare-style layout. Headers come from `headerSelector`'s first row;
|
|
96
|
+
data rows are gathered from that table plus each **immediately-following
|
|
97
|
+
sibling**, stopping at the first sibling that is not a `<table>` or whose
|
|
98
|
+
column count differs — so an unrelated later section can't leak in. Set
|
|
99
|
+
`dropLabelRows: true` to drop bare section-divider rows (only the first cell
|
|
100
|
+
non-empty, e.g. a lone `2025`).
|
|
101
|
+
|
|
102
|
+
- **`cellIgnoreSelectors`** — CSS selectors whose matching descendants are
|
|
103
|
+
removed from every cell **and header** before its text is read, e.g.
|
|
104
|
+
`['sup']` so `100<sup>4</sup>` reads as `100`, not `1004`. Off by default
|
|
105
|
+
(cells are read verbatim); applies to all three modes; the removal happens
|
|
106
|
+
on a per-cell clone and never mutates the document.
|
|
107
|
+
|
|
108
|
+
**Row-header columns.** Data cells are read as `th, td`, so an accessible
|
|
109
|
+
table whose first column is a `<th scope="row">` label stays column-aligned.
|
|
110
|
+
When the header row omits the matching corner cell, the leading row-header
|
|
111
|
+
cell(s) are dropped so the row still lines up with the headers.
|
|
71
112
|
|
|
72
113
|
### `mapColumns(headers: string[], keywords: ColumnKeywords, opts?: { dateGuard?: boolean; required?: string[] }): ColumnMap`
|
|
73
114
|
|
|
@@ -77,14 +118,24 @@ the first. `opts.dateGuard` drops date-looking headers from any key whose
|
|
|
77
118
|
`type !== 'date'`. `opts.required` lists keys that must have at least one
|
|
78
119
|
candidate for `map.ok` to be `true`.
|
|
79
120
|
|
|
80
|
-
### `mapRows(table
|
|
121
|
+
### `mapRows(table, map, keywords, opts?): MappedRow[]`
|
|
122
|
+
|
|
123
|
+
```ts
|
|
124
|
+
mapRows(
|
|
125
|
+
table: { headers: string[]; rows: string[][] },
|
|
126
|
+
map: ColumnMap,
|
|
127
|
+
keywords: ColumnKeywords,
|
|
128
|
+
opts?: { dateFormats?: string[]; dateExtractLeading?: boolean; dateYearPivot?: number },
|
|
129
|
+
): MappedRow[]
|
|
130
|
+
```
|
|
81
131
|
|
|
82
132
|
Joins a `ColumnMap` back onto `table.rows`, producing one `MappedRow` per data
|
|
83
133
|
row. Each matched key holds an array of `Cell`s (one per candidate header),
|
|
84
|
-
each parsed according to that key's declared `type`. `opts.dateFormats
|
|
85
|
-
`opts.dateExtractLeading` are forwarded verbatim to
|
|
86
|
-
|
|
87
|
-
omitting `opts` preserves the
|
|
134
|
+
each parsed according to that key's declared `type`. `opts.dateFormats`,
|
|
135
|
+
`opts.dateExtractLeading` and `opts.dateYearPivot` are forwarded verbatim to
|
|
136
|
+
`parseDate`'s `opts.formats`, `opts.extractLeading` and `opts.yearPivot` for
|
|
137
|
+
every date-typed cell; all three default off, so omitting `opts` preserves the
|
|
138
|
+
exact prior behaviour.
|
|
88
139
|
|
|
89
140
|
### `parseNumber(raw: string): number | null`
|
|
90
141
|
|
|
@@ -93,7 +144,7 @@ and `%`, then parses what remains as a number. Returns `null` when nothing
|
|
|
93
144
|
parseable is left. Does **not** convert cents to dollars — domain scaling is
|
|
94
145
|
the caller's job.
|
|
95
146
|
|
|
96
|
-
### `parseDate(raw: string, opts?: { formats?: string[]; extractLeading?: boolean }): string | null`
|
|
147
|
+
### `parseDate(raw: string, opts?: { formats?: string[]; extractLeading?: boolean; yearPivot?: number }): string | null`
|
|
97
148
|
|
|
98
149
|
Parses a handful of common date text formats (ISO, `D Mon YYYY`, `Mon D,
|
|
99
150
|
YYYY`, and `D/M/Y` slash dates) into an ISO `yyyy-mm-dd` string. Returns
|
|
@@ -108,6 +159,13 @@ junk is still rejected (the date must be the first token) and the date itself is
|
|
|
108
159
|
still matched exactly and disambiguated by the same rules — so the "never guess"
|
|
109
160
|
contract holds. For real IR pages that staple a label onto a date cell.
|
|
110
161
|
|
|
162
|
+
`opts.yearPivot` (default off) enables 2-digit years, which are otherwise
|
|
163
|
+
**rejected** (`null`) rather than guessed. It is the base of a sliding 100-year
|
|
164
|
+
window: `parseDate('10 Mar 26', { yearPivot: 2000 })` → `'2026-03-10'`, while
|
|
165
|
+
`yearPivot: 1950` reads `26`→2026 and `99`→1999. Applies to the named-month
|
|
166
|
+
formats only (`D Mon YY`, `Mon D, YY`); slash dates stay 4-digit-only, since a
|
|
167
|
+
2-digit slash year compounds day/month **and** century ambiguity.
|
|
168
|
+
|
|
111
169
|
### Types
|
|
112
170
|
|
|
113
171
|
```ts
|
|
@@ -25,7 +25,9 @@ export type GridSelector = {
|
|
|
25
25
|
/**
|
|
26
26
|
* Selector identifying the header row among the `row` matches. Rows that
|
|
27
27
|
* match it are treated as headers (never data); the first such row supplies
|
|
28
|
-
* the column headers. Omit to treat the first row as the header
|
|
28
|
+
* the column headers. Omit to treat the first row as the header — as does a
|
|
29
|
+
* selector that matches NOTHING (a typo'd/stale selector degrades to the
|
|
30
|
+
* omitted behaviour rather than emitting the header as data row 0).
|
|
29
31
|
*/
|
|
30
32
|
headerRow?: string;
|
|
31
33
|
};
|
|
@@ -36,8 +38,9 @@ export type GridSelector = {
|
|
|
36
38
|
* Computershare-style IR pages (e.g. JB Hi-Fi's dividend history). The header
|
|
37
39
|
* comes from `headerSelector`'s first row; data rows are gathered from that
|
|
38
40
|
* table plus its immediately-following sibling <table>s that share its column
|
|
39
|
-
* count
|
|
40
|
-
*
|
|
41
|
+
* count. The walk stops at the first following sibling that is not a <table>
|
|
42
|
+
* (e.g. an <h2> introducing an unrelated section) or whose column count
|
|
43
|
+
* differs, so an unrelated later table can't leak in.
|
|
41
44
|
*/
|
|
42
45
|
export type CombineSelector = {
|
|
43
46
|
/** Selector matching the ONE table whose first row supplies the shared headers. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extract_table.d.ts","sourceRoot":"","sources":["../../src/parse/extract_table.ts"],"names":[],"mappings":"AAsBA,MAAM,MAAM,kBAAkB,GAAG;IAC/B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,IAAI,EAAE,MAAM,EAAE,EAAE,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB;;;;;;OAMG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,0FAA0F;IAC1F,GAAG,EAAE,MAAM,CAAC;IACZ,0DAA0D;IAC1D,IAAI,EAAE,MAAM,CAAC;IACb
|
|
1
|
+
{"version":3,"file":"extract_table.d.ts","sourceRoot":"","sources":["../../src/parse/extract_table.ts"],"names":[],"mappings":"AAsBA,MAAM,MAAM,kBAAkB,GAAG;IAC/B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,IAAI,EAAE,MAAM,EAAE,EAAE,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB;;;;;;OAMG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,0FAA0F;IAC1F,GAAG,EAAE,MAAM,CAAC;IACZ,0DAA0D;IAC1D,IAAI,EAAE,MAAM,CAAC;IACb;;;;;;OAMG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF;;;;;;;;;;GAUG;AACH,MAAM,MAAM,eAAe,GAAG;IAC5B,mFAAmF;IACnF,cAAc,EAAE,MAAM,CAAC;IACvB;;;;;OAKG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,iGAAiG;IACjG,IAAI,CAAC,EAAE,YAAY,CAAC;IACpB;;;;OAIG;IACH,OAAO,CAAC,EAAE,eAAe,CAAC;IAC1B;;;;;;OAMG;IACH,mBAAmB,CAAC,EAAE,MAAM,EAAE,CAAC;CAChC,CAAC;AAkTF,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,mBAAmB,GAAG,kBAAkB,CAkCzF"}
|
|
@@ -80,6 +80,34 @@ function scoreTable(tableSel) {
|
|
|
80
80
|
const consistencyRatio = numDataRows > 0 ? numConsistent / numDataRows : 0;
|
|
81
81
|
return numHeaderCells * 2 + (hasTh ? 5 : 0) + numDataRows * 3 + consistencyRatio * 10;
|
|
82
82
|
}
|
|
83
|
+
// Decide whether a leading copy of `header` inside `text` is a REPEATED DOM
|
|
84
|
+
// LABEL (which must be stripped) or genuinely part of the value (which must
|
|
85
|
+
// not). The responsive-table pattern that motivates the strip glues two sibling
|
|
86
|
+
// nodes together with no separator — `<div>Payment date</div><div>2 July
|
|
87
|
+
// 2026</div>` reads as `"Payment date2 July 2026"` — so the seam is the tell:
|
|
88
|
+
//
|
|
89
|
+
// 1. Whitespace after the header => prose, one natural phrase, not a seam.
|
|
90
|
+
// Header "Total" + "Total dividend" is a value that happens to start with
|
|
91
|
+
// the header word; stripping it would leave the bare word "dividend".
|
|
92
|
+
// 2. The remainder must not continue the header's OWN token. If the header
|
|
93
|
+
// ends in a digit and the remainder opens with a digit or a numeric
|
|
94
|
+
// joiner (`-`, `/`, `.`, `:`), the "header" is really a prefix of one
|
|
95
|
+
// longer number/date — header "2025" + "2025-06-30" would be butchered
|
|
96
|
+
// into "-06-30".
|
|
97
|
+
//
|
|
98
|
+
// Everything else (letter -> digit, letter -> letter with no space) is the
|
|
99
|
+
// concatenation signature the strip exists for and is stripped as before.
|
|
100
|
+
function isRepeatedHeaderLabel(text, header) {
|
|
101
|
+
if (header === '' || text.length <= header.length || !text.startsWith(header))
|
|
102
|
+
return false;
|
|
103
|
+
const rest = text.slice(header.length);
|
|
104
|
+
if (/^\s/.test(rest))
|
|
105
|
+
return false; // (1) prose, not a DOM seam
|
|
106
|
+
const headerEndsDigit = /\d$/.test(header);
|
|
107
|
+
if (headerEndsDigit && /^[\d\-/.:]/.test(rest))
|
|
108
|
+
return false; // (2) same token cut in half
|
|
109
|
+
return rest.trim() !== '';
|
|
110
|
+
}
|
|
83
111
|
// Read the data rows (everything after the header row) of one real <table>,
|
|
84
112
|
// aligning each cell against `headers` (for the repeated-in-cell-label strip)
|
|
85
113
|
// and removing `ignore` descendants first. Shared by readTable and combine
|
|
@@ -97,18 +125,35 @@ function readTableDataRows(rows, headers, ignore, startIndex) {
|
|
|
97
125
|
if (rowSel.children('td').length === 0)
|
|
98
126
|
continue;
|
|
99
127
|
const cellEls = rowSel.children('th, td');
|
|
128
|
+
// Row-header reconciliation. Two real shapes exist:
|
|
129
|
+
// (a) the header row carries a (usually blank) CORNER cell for the
|
|
130
|
+
// row-label column — widths already agree, keep every cell;
|
|
131
|
+
// (b) the header row OMITS it — the row is then wider than the header
|
|
132
|
+
// and reading the <th> would shift every column right by one.
|
|
133
|
+
// Detect (b) by an excess that is fully explained by leading <th> cells,
|
|
134
|
+
// and drop exactly that many. A row that is merely ragged (excess <td>s,
|
|
135
|
+
// or a leading <td>) is untouched.
|
|
136
|
+
let offset = 0;
|
|
137
|
+
if (headers.length > 0 && cellEls.length > headers.length) {
|
|
138
|
+
let leadingTh = 0;
|
|
139
|
+
while (leadingTh < cellEls.length && cellEls.eq(leadingTh).is('th'))
|
|
140
|
+
leadingTh += 1;
|
|
141
|
+
const excess = cellEls.length - headers.length;
|
|
142
|
+
if (excess <= leadingTh)
|
|
143
|
+
offset = excess;
|
|
144
|
+
}
|
|
100
145
|
const cells = [];
|
|
101
|
-
for (let j =
|
|
146
|
+
for (let j = offset; j < cellEls.length; j++) {
|
|
102
147
|
let text = readCellText(cellEls.eq(j), ignore);
|
|
103
148
|
// Responsive-table pattern: some IR pages repeat the column header
|
|
104
149
|
// INSIDE each cell as a (visually-hidden on desktop) label, e.g. NAB's
|
|
105
150
|
// "<div>Payment date</div><div>2 July 2026</div>", whose .text()
|
|
106
151
|
// collapses to "Payment date2 July 2026". Strip a leading copy of this
|
|
107
152
|
// cell's own column header so downstream number/date parsing sees just
|
|
108
|
-
// the value
|
|
109
|
-
//
|
|
110
|
-
const header = headers[j];
|
|
111
|
-
if (header != null &&
|
|
153
|
+
// the value — but only at a genuine DOM seam (see
|
|
154
|
+
// isRepeatedHeaderLabel), so a legitimate value is never truncated.
|
|
155
|
+
const header = headers[j - offset];
|
|
156
|
+
if (header != null && isRepeatedHeaderLabel(text, header)) {
|
|
112
157
|
text = text.slice(header.length).trim();
|
|
113
158
|
}
|
|
114
159
|
cells.push(text);
|
|
@@ -158,9 +203,15 @@ function readCombined($, combine, ignore) {
|
|
|
158
203
|
// Following-sibling tables that share the header's column count; stop at the
|
|
159
204
|
// first sibling that doesn't (or a non-table sibling) so an unrelated later
|
|
160
205
|
// table with a coincidentally-equal width can't leak in.
|
|
161
|
-
|
|
206
|
+
// Walk EVERY following sibling, not just the <table> ones: filtering to
|
|
207
|
+
// tables first would silently skip over an intervening <h2>/<p>, so the
|
|
208
|
+
// documented "stop at the first sibling that doesn't match" could never fire
|
|
209
|
+
// and an unrelated later section's table could leak in.
|
|
210
|
+
const siblings = headerTable.nextAll();
|
|
162
211
|
for (let i = 0; i < siblings.length; i++) {
|
|
163
212
|
const sib = siblings.eq(i);
|
|
213
|
+
if (!sib.is('table'))
|
|
214
|
+
break;
|
|
164
215
|
const sibRows = tableRows(sib);
|
|
165
216
|
if (sibRows.length === 0)
|
|
166
217
|
break;
|
|
@@ -208,8 +259,12 @@ function readGridContainer(container, grid, ignore) {
|
|
|
208
259
|
const rowSel = allRows.eq(i);
|
|
209
260
|
if (grid.headerRow != null && rowSel.is(grid.headerRow))
|
|
210
261
|
continue; // never treat a header row as data
|
|
211
|
-
|
|
212
|
-
|
|
262
|
+
// First row already consumed as header — either because no headerRow
|
|
263
|
+
// selector was given, OR because one was given and matched nothing (a
|
|
264
|
+
// typo'd/stale selector must degrade to "first row is the header", never
|
|
265
|
+
// silently emit the header as data row 0).
|
|
266
|
+
if (firstRowIsHeader && i === 0)
|
|
267
|
+
continue;
|
|
213
268
|
const cells = gridRowCells(rowSel, grid.cell, ignore);
|
|
214
269
|
if (cells.length === 0)
|
|
215
270
|
continue;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hazo_scrape",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.2",
|
|
4
4
|
"description": "Generic source-agnostic web scraping engine with a network-free parse core.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"module": "./dist/index.js",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"build:test-app": "npm run build && cd test-app && npm run build"
|
|
36
36
|
},
|
|
37
37
|
"peerDependencies": {
|
|
38
|
-
"hazo_core": "^
|
|
38
|
+
"hazo_core": "^2.0.0",
|
|
39
39
|
"react": "^18.0.0 || ^19.0.0",
|
|
40
40
|
"react-dom": "^18.0.0 || ^19.0.0"
|
|
41
41
|
},
|