feedparser-rs 0.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 (4) hide show
  1. package/README.md +305 -0
  2. package/index.d.ts +277 -0
  3. package/index.js +577 -0
  4. package/package.json +57 -0
package/README.md ADDED
@@ -0,0 +1,305 @@
1
+ # feedparser-rs
2
+
3
+ High-performance RSS/Atom/JSON Feed parser written in Rust with Node.js bindings.
4
+
5
+ Drop-in replacement for Python's `feedparser` library, offering 10-100x performance improvement.
6
+
7
+ ## Features
8
+
9
+ - **Fast**: Written in Rust, 10-100x faster than Python feedparser
10
+ - **Tolerant**: Handles malformed feeds with bozo flag (like feedparser)
11
+ - **Multi-format**: Supports RSS 0.9x/1.0/2.0, Atom 0.3/1.0, JSON Feed 1.0/1.1
12
+ - **Zero-copy**: Efficient parsing with minimal allocations
13
+ - **TypeScript**: Full TypeScript definitions included
14
+
15
+ ## Installation
16
+
17
+ ```bash
18
+ npm install feedparser-rs
19
+ # or
20
+ yarn add feedparser-rs
21
+ # or
22
+ pnpm add feedparser-rs
23
+ ```
24
+
25
+ ## Quick Start
26
+
27
+ ```javascript
28
+ import { parse } from 'feedparser-rs';
29
+
30
+ // Parse from string
31
+ const feed = parse(`
32
+ <?xml version="1.0"?>
33
+ <rss version="2.0">
34
+ <channel>
35
+ <title>My Blog</title>
36
+ <item>
37
+ <title>Hello World</title>
38
+ <link>https://example.com/1</link>
39
+ </item>
40
+ </channel>
41
+ </rss>
42
+ `);
43
+
44
+ console.log(feed.feed.title); // "My Blog"
45
+ console.log(feed.entries[0].title); // "Hello World"
46
+ console.log(feed.version); // "rss20"
47
+
48
+ // Parse from Buffer (e.g., HTTP response)
49
+ const response = await fetch('https://example.com/feed.xml');
50
+ const buffer = Buffer.from(await response.arrayBuffer());
51
+ const feed = parse(buffer);
52
+ ```
53
+
54
+ ## API
55
+
56
+ ### `parse(source: Buffer | string | Uint8Array): ParsedFeed`
57
+
58
+ Parse a feed from bytes or string.
59
+
60
+ **Parameters:**
61
+ - `source` - Feed content as Buffer, string, or Uint8Array
62
+
63
+ **Returns:**
64
+ - `ParsedFeed` object with feed metadata and entries
65
+
66
+ **Throws:**
67
+ - `Error` if parsing fails catastrophically
68
+
69
+ **Example:**
70
+ ```javascript
71
+ const feed = parse('<rss version="2.0">...</rss>');
72
+ console.log(feed.feed.title);
73
+ console.log(feed.entries.length);
74
+ ```
75
+
76
+ ### `detectFormat(source: Buffer | string | Uint8Array): string`
77
+
78
+ Detect feed format without full parsing.
79
+
80
+ **Parameters:**
81
+ - `source` - Feed content as Buffer, string, or Uint8Array
82
+
83
+ **Returns:**
84
+ - Format string: `"rss20"`, `"atom10"`, `"json11"`, etc.
85
+
86
+ **Example:**
87
+ ```javascript
88
+ const format = detectFormat('<feed xmlns="http://www.w3.org/2005/Atom">...</feed>');
89
+ console.log(format); // "atom10"
90
+ ```
91
+
92
+ ## Types
93
+
94
+ ### ParsedFeed
95
+
96
+ ```typescript
97
+ interface ParsedFeed {
98
+ feed: FeedMeta;
99
+ entries: Entry[];
100
+ bozo: boolean;
101
+ bozo_exception?: string;
102
+ encoding: string;
103
+ version: string;
104
+ namespaces: Record<string, string>;
105
+ }
106
+ ```
107
+
108
+ ### FeedMeta
109
+
110
+ ```typescript
111
+ interface FeedMeta {
112
+ title?: string;
113
+ title_detail?: TextConstruct;
114
+ link?: string;
115
+ links: Link[];
116
+ subtitle?: string;
117
+ subtitle_detail?: TextConstruct;
118
+ updated?: number; // Milliseconds since epoch
119
+ author?: string;
120
+ author_detail?: Person;
121
+ authors: Person[];
122
+ contributors: Person[];
123
+ publisher?: string;
124
+ publisher_detail?: Person;
125
+ language?: string;
126
+ rights?: string;
127
+ rights_detail?: TextConstruct;
128
+ generator?: string;
129
+ generator_detail?: Generator;
130
+ image?: Image;
131
+ icon?: string;
132
+ logo?: string;
133
+ tags: Tag[];
134
+ id?: string;
135
+ ttl?: number;
136
+ }
137
+ ```
138
+
139
+ ### Entry
140
+
141
+ ```typescript
142
+ interface Entry {
143
+ id?: string;
144
+ title?: string;
145
+ title_detail?: TextConstruct;
146
+ link?: string;
147
+ links: Link[];
148
+ summary?: string;
149
+ summary_detail?: TextConstruct;
150
+ content: Content[];
151
+ published?: number; // Milliseconds since epoch
152
+ updated?: number;
153
+ created?: number;
154
+ expired?: number;
155
+ author?: string;
156
+ author_detail?: Person;
157
+ authors: Person[];
158
+ contributors: Person[];
159
+ publisher?: string;
160
+ publisher_detail?: Person;
161
+ tags: Tag[];
162
+ enclosures: Enclosure[];
163
+ comments?: string;
164
+ source?: Source;
165
+ }
166
+ ```
167
+
168
+ ### Other Types
169
+
170
+ ```typescript
171
+ interface TextConstruct {
172
+ value: string;
173
+ type: "text" | "html" | "xhtml";
174
+ language?: string;
175
+ base?: string;
176
+ }
177
+
178
+ interface Link {
179
+ href: string;
180
+ rel?: string;
181
+ type?: string;
182
+ title?: string;
183
+ length?: number;
184
+ hreflang?: string;
185
+ }
186
+
187
+ interface Person {
188
+ name?: string;
189
+ email?: string;
190
+ uri?: string;
191
+ }
192
+
193
+ interface Tag {
194
+ term: string;
195
+ scheme?: string;
196
+ label?: string;
197
+ }
198
+
199
+ interface Image {
200
+ url: string;
201
+ title?: string;
202
+ link?: string;
203
+ width?: number;
204
+ height?: number;
205
+ description?: string;
206
+ }
207
+
208
+ interface Enclosure {
209
+ url: string;
210
+ length?: number;
211
+ type?: string;
212
+ }
213
+
214
+ interface Content {
215
+ value: string;
216
+ type?: string;
217
+ language?: string;
218
+ base?: string;
219
+ }
220
+
221
+ interface Generator {
222
+ value: string;
223
+ uri?: string;
224
+ version?: string;
225
+ }
226
+
227
+ interface Source {
228
+ title?: string;
229
+ link?: string;
230
+ id?: string;
231
+ }
232
+ ```
233
+
234
+ ## Error Handling
235
+
236
+ The library uses a "bozo" flag (like feedparser) to indicate parsing errors while still returning partial results:
237
+
238
+ ```javascript
239
+ const feed = parse('<rss><channel><title>Broken</title></rss>');
240
+
241
+ if (feed.bozo) {
242
+ console.warn('Feed has errors:', feed.bozo_exception);
243
+ }
244
+
245
+ // Still can access parsed data
246
+ console.log(feed.feed.title); // "Broken"
247
+ ```
248
+
249
+ ## Dates
250
+
251
+ All date fields are returned as milliseconds since Unix epoch (number type). Convert to JavaScript Date:
252
+
253
+ ```javascript
254
+ const feed = parse(xmlWithDates);
255
+
256
+ const entry = feed.entries[0];
257
+ if (entry.published) {
258
+ const date = new Date(entry.published);
259
+ console.log(date.toISOString());
260
+ }
261
+ ```
262
+
263
+ ## Performance
264
+
265
+ Benchmarks vs Python feedparser (parsing 100KB RSS feed):
266
+
267
+ | Library | Time | Speedup |
268
+ |---------|------|---------|
269
+ | feedparser-rs | 0.5ms | 100x |
270
+ | feedparser (Python) | 50ms | 1x |
271
+
272
+ See [benchmarks/](../../benchmarks/) for detailed results and methodology.
273
+
274
+ ## Platform Support
275
+
276
+ Pre-built binaries available for:
277
+ - macOS (Intel & Apple Silicon)
278
+ - Linux (x64, ARM64)
279
+ - Windows (x64)
280
+
281
+ Supported Node.js versions: 18, 20, 22
282
+
283
+ ## Development
284
+
285
+ ```bash
286
+ # Install dependencies
287
+ npm install
288
+
289
+ # Build native module
290
+ npm run build
291
+
292
+ # Run tests
293
+ npm test
294
+ ```
295
+
296
+ ## License
297
+
298
+ MIT OR Apache-2.0
299
+
300
+ ## Links
301
+
302
+ - [GitHub](https://github.com/bug-ops/feedparser-rs)
303
+ - [npm](https://www.npmjs.com/package/feedparser-rs)
304
+ - [Documentation](https://docs.rs/feedparser-rs-core)
305
+ - [Changelog](../../CHANGELOG.md)
package/index.d.ts ADDED
@@ -0,0 +1,277 @@
1
+ /* auto-generated by NAPI-RS */
2
+ /* eslint-disable */
3
+ /** Content block */
4
+ export interface Content {
5
+ /** Content body */
6
+ value: string
7
+ /** Content MIME type */
8
+ type?: string
9
+ /** Content language */
10
+ language?: string
11
+ /** Base URL for relative links */
12
+ base?: string
13
+ }
14
+
15
+ /**
16
+ * Detect feed format without full parsing
17
+ *
18
+ * # Arguments
19
+ *
20
+ * * `source` - Feed content as Buffer, string, or Uint8Array
21
+ *
22
+ * # Returns
23
+ *
24
+ * Feed version string (e.g., "rss20", "atom10")
25
+ */
26
+ export declare function detectFormat(source: Buffer | string): string
27
+
28
+ /** Enclosure (attached media file) */
29
+ export interface Enclosure {
30
+ /** Enclosure URL */
31
+ url: string
32
+ /** File size in bytes */
33
+ length?: number
34
+ /** MIME type */
35
+ type?: string
36
+ }
37
+
38
+ /** Feed entry/item */
39
+ export interface Entry {
40
+ /** Unique entry identifier */
41
+ id?: string
42
+ /** Entry title */
43
+ title?: string
44
+ /** Detailed title with metadata */
45
+ titleDetail?: TextConstruct
46
+ /** Primary link */
47
+ link?: string
48
+ /** All links associated with this entry */
49
+ links: Array<Link>
50
+ /** Short description/summary */
51
+ summary?: string
52
+ /** Detailed summary with metadata */
53
+ summaryDetail?: TextConstruct
54
+ /** Full content blocks */
55
+ content: Array<Content>
56
+ /** Publication date (milliseconds since epoch) */
57
+ published?: number
58
+ /** Last update date (milliseconds since epoch) */
59
+ updated?: number
60
+ /** Creation date (milliseconds since epoch) */
61
+ created?: number
62
+ /** Expiration date (milliseconds since epoch) */
63
+ expired?: number
64
+ /** Primary author name */
65
+ author?: string
66
+ /** Detailed author information */
67
+ authorDetail?: Person
68
+ /** All authors */
69
+ authors: Array<Person>
70
+ /** Contributors */
71
+ contributors: Array<Person>
72
+ /** Publisher name */
73
+ publisher?: string
74
+ /** Detailed publisher information */
75
+ publisherDetail?: Person
76
+ /** Tags/categories */
77
+ tags: Array<Tag>
78
+ /** Media enclosures (audio, video, etc.) */
79
+ enclosures: Array<Enclosure>
80
+ /** Comments URL or text */
81
+ comments?: string
82
+ /** Source feed reference */
83
+ source?: Source
84
+ }
85
+
86
+ /** Feed metadata */
87
+ export interface FeedMeta {
88
+ /** Feed title */
89
+ title?: string
90
+ /** Detailed title with metadata */
91
+ titleDetail?: TextConstruct
92
+ /** Primary feed link */
93
+ link?: string
94
+ /** All links associated with this feed */
95
+ links: Array<Link>
96
+ /** Feed subtitle/description */
97
+ subtitle?: string
98
+ /** Detailed subtitle with metadata */
99
+ subtitleDetail?: TextConstruct
100
+ /** Last update date (milliseconds since epoch) */
101
+ updated?: number
102
+ /** Primary author name */
103
+ author?: string
104
+ /** Detailed author information */
105
+ authorDetail?: Person
106
+ /** All authors */
107
+ authors: Array<Person>
108
+ /** Contributors */
109
+ contributors: Array<Person>
110
+ /** Publisher name */
111
+ publisher?: string
112
+ /** Detailed publisher information */
113
+ publisherDetail?: Person
114
+ /** Feed language (e.g., "en-us") */
115
+ language?: string
116
+ /** Copyright/rights statement */
117
+ rights?: string
118
+ /** Detailed rights with metadata */
119
+ rightsDetail?: TextConstruct
120
+ /** Generator name */
121
+ generator?: string
122
+ /** Detailed generator information */
123
+ generatorDetail?: Generator
124
+ /** Feed image */
125
+ image?: Image
126
+ /** Icon URL (small image) */
127
+ icon?: string
128
+ /** Logo URL (larger image) */
129
+ logo?: string
130
+ /** Feed-level tags/categories */
131
+ tags: Array<Tag>
132
+ /** Unique feed identifier */
133
+ id?: string
134
+ /** Time-to-live (update frequency hint) in minutes */
135
+ ttl?: number
136
+ }
137
+
138
+ /** Generator metadata */
139
+ export interface Generator {
140
+ /** Generator name */
141
+ value: string
142
+ /** Generator URI */
143
+ uri?: string
144
+ /** Generator version */
145
+ version?: string
146
+ }
147
+
148
+ /** Image metadata */
149
+ export interface Image {
150
+ /** Image URL */
151
+ url: string
152
+ /** Image title */
153
+ title?: string
154
+ /** Link associated with the image */
155
+ link?: string
156
+ /** Image width in pixels */
157
+ width?: number
158
+ /** Image height in pixels */
159
+ height?: number
160
+ /** Image description */
161
+ description?: string
162
+ }
163
+
164
+ /** Link in feed or entry */
165
+ export interface Link {
166
+ /** Link URL */
167
+ href: string
168
+ /** Link relationship type (e.g., "alternate", "enclosure", "self") */
169
+ rel?: string
170
+ /** MIME type of the linked resource */
171
+ type?: string
172
+ /** Human-readable link title */
173
+ title?: string
174
+ /** Length of the linked resource in bytes */
175
+ length?: number
176
+ /** Language of the linked resource */
177
+ hreflang?: string
178
+ }
179
+
180
+ /**
181
+ * Parse an RSS/Atom/JSON Feed from bytes or string
182
+ *
183
+ * # Arguments
184
+ *
185
+ * * `source` - Feed content as Buffer, string, or Uint8Array
186
+ *
187
+ * # Returns
188
+ *
189
+ * Parsed feed result with metadata and entries
190
+ *
191
+ * # Errors
192
+ *
193
+ * Returns error if input exceeds size limit or parsing fails catastrophically
194
+ */
195
+ export declare function parse(source: Buffer | string): ParsedFeed
196
+
197
+ /**
198
+ * Parsed feed result
199
+ *
200
+ * This is analogous to Python feedparser's `FeedParserDict`.
201
+ */
202
+ export interface ParsedFeed {
203
+ /** Feed metadata */
204
+ feed: FeedMeta
205
+ /** Feed entries/items */
206
+ entries: Array<Entry>
207
+ /** True if parsing encountered errors */
208
+ bozo: boolean
209
+ /** Description of parsing error (if bozo is true) */
210
+ bozoException?: string
211
+ /** Detected or declared encoding */
212
+ encoding: string
213
+ /** Detected feed format version */
214
+ version: string
215
+ /** XML namespaces (prefix -> URI) */
216
+ namespaces: Record<string, string>
217
+ }
218
+
219
+ /**
220
+ * Parse an RSS/Atom/JSON Feed with custom size limit
221
+ *
222
+ * # Arguments
223
+ *
224
+ * * `source` - Feed content as Buffer, string, or Uint8Array
225
+ * * `max_size` - Optional maximum feed size in bytes (default: 100MB)
226
+ *
227
+ * # Returns
228
+ *
229
+ * Parsed feed result with metadata and entries
230
+ *
231
+ * # Errors
232
+ *
233
+ * Returns error if input exceeds size limit or parsing fails catastrophically
234
+ */
235
+ export declare function parseWithOptions(source: Buffer | string, maxSize?: number | undefined | null): ParsedFeed
236
+
237
+ /** Person (author, contributor, etc.) */
238
+ export interface Person {
239
+ /** Person's name */
240
+ name?: string
241
+ /** Person's email address */
242
+ email?: string
243
+ /** Person's URI/website */
244
+ uri?: string
245
+ }
246
+
247
+ /** Source reference (for entries) */
248
+ export interface Source {
249
+ /** Source title */
250
+ title?: string
251
+ /** Source link */
252
+ link?: string
253
+ /** Source ID */
254
+ id?: string
255
+ }
256
+
257
+ /** Tag/category */
258
+ export interface Tag {
259
+ /** Tag term/label */
260
+ term: string
261
+ /** Tag scheme/domain */
262
+ scheme?: string
263
+ /** Human-readable tag label */
264
+ label?: string
265
+ }
266
+
267
+ /** Text construct with metadata */
268
+ export interface TextConstruct {
269
+ /** Text content */
270
+ value: string
271
+ /** Content type ("text", "html", "xhtml") */
272
+ type: string
273
+ /** Content language */
274
+ language?: string
275
+ /** Base URL for relative links */
276
+ base?: string
277
+ }
package/index.js ADDED
@@ -0,0 +1,577 @@
1
+ // prettier-ignore
2
+ /* eslint-disable */
3
+ // @ts-nocheck
4
+ /* auto-generated by NAPI-RS */
5
+
6
+ const { readFileSync } = require('node:fs')
7
+ let nativeBinding = null
8
+ const loadErrors = []
9
+
10
+ const isMusl = () => {
11
+ let musl = false
12
+ if (process.platform === 'linux') {
13
+ musl = isMuslFromFilesystem()
14
+ if (musl === null) {
15
+ musl = isMuslFromReport()
16
+ }
17
+ if (musl === null) {
18
+ musl = isMuslFromChildProcess()
19
+ }
20
+ }
21
+ return musl
22
+ }
23
+
24
+ const isFileMusl = (f) => f.includes('libc.musl-') || f.includes('ld-musl-')
25
+
26
+ const isMuslFromFilesystem = () => {
27
+ try {
28
+ return readFileSync('/usr/bin/ldd', 'utf-8').includes('musl')
29
+ } catch {
30
+ return null
31
+ }
32
+ }
33
+
34
+ const isMuslFromReport = () => {
35
+ let report = null
36
+ if (typeof process.report?.getReport === 'function') {
37
+ process.report.excludeNetwork = true
38
+ report = process.report.getReport()
39
+ }
40
+ if (!report) {
41
+ return null
42
+ }
43
+ if (report.header && report.header.glibcVersionRuntime) {
44
+ return false
45
+ }
46
+ if (Array.isArray(report.sharedObjects)) {
47
+ if (report.sharedObjects.some(isFileMusl)) {
48
+ return true
49
+ }
50
+ }
51
+ return false
52
+ }
53
+
54
+ const isMuslFromChildProcess = () => {
55
+ try {
56
+ return require('child_process').execSync('ldd --version', { encoding: 'utf8' }).includes('musl')
57
+ } catch (e) {
58
+ // If we reach this case, we don't know if the system is musl or not, so is better to just fallback to false
59
+ return false
60
+ }
61
+ }
62
+
63
+ function requireNative() {
64
+ if (process.env.NAPI_RS_NATIVE_LIBRARY_PATH) {
65
+ try {
66
+ return require(process.env.NAPI_RS_NATIVE_LIBRARY_PATH);
67
+ } catch (err) {
68
+ loadErrors.push(err)
69
+ }
70
+ } else if (process.platform === 'android') {
71
+ if (process.arch === 'arm64') {
72
+ try {
73
+ return require('./feedparser-rs.android-arm64.node')
74
+ } catch (e) {
75
+ loadErrors.push(e)
76
+ }
77
+ try {
78
+ const binding = require('feedparser-rs-android-arm64')
79
+ const bindingPackageVersion = require('feedparser-rs-android-arm64/package.json').version
80
+ if (bindingPackageVersion !== '0.1.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
81
+ throw new Error(`Native binding package version mismatch, expected 0.1.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
82
+ }
83
+ return binding
84
+ } catch (e) {
85
+ loadErrors.push(e)
86
+ }
87
+ } else if (process.arch === 'arm') {
88
+ try {
89
+ return require('./feedparser-rs.android-arm-eabi.node')
90
+ } catch (e) {
91
+ loadErrors.push(e)
92
+ }
93
+ try {
94
+ const binding = require('feedparser-rs-android-arm-eabi')
95
+ const bindingPackageVersion = require('feedparser-rs-android-arm-eabi/package.json').version
96
+ if (bindingPackageVersion !== '0.1.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
97
+ throw new Error(`Native binding package version mismatch, expected 0.1.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
98
+ }
99
+ return binding
100
+ } catch (e) {
101
+ loadErrors.push(e)
102
+ }
103
+ } else {
104
+ loadErrors.push(new Error(`Unsupported architecture on Android ${process.arch}`))
105
+ }
106
+ } else if (process.platform === 'win32') {
107
+ if (process.arch === 'x64') {
108
+ if (process.config?.variables?.shlib_suffix === 'dll.a' || process.config?.variables?.node_target_type === 'shared_library') {
109
+ try {
110
+ return require('./feedparser-rs.win32-x64-gnu.node')
111
+ } catch (e) {
112
+ loadErrors.push(e)
113
+ }
114
+ try {
115
+ const binding = require('feedparser-rs-win32-x64-gnu')
116
+ const bindingPackageVersion = require('feedparser-rs-win32-x64-gnu/package.json').version
117
+ if (bindingPackageVersion !== '0.1.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
118
+ throw new Error(`Native binding package version mismatch, expected 0.1.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
119
+ }
120
+ return binding
121
+ } catch (e) {
122
+ loadErrors.push(e)
123
+ }
124
+ } else {
125
+ try {
126
+ return require('./feedparser-rs.win32-x64-msvc.node')
127
+ } catch (e) {
128
+ loadErrors.push(e)
129
+ }
130
+ try {
131
+ const binding = require('feedparser-rs-win32-x64-msvc')
132
+ const bindingPackageVersion = require('feedparser-rs-win32-x64-msvc/package.json').version
133
+ if (bindingPackageVersion !== '0.1.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
134
+ throw new Error(`Native binding package version mismatch, expected 0.1.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
135
+ }
136
+ return binding
137
+ } catch (e) {
138
+ loadErrors.push(e)
139
+ }
140
+ }
141
+ } else if (process.arch === 'ia32') {
142
+ try {
143
+ return require('./feedparser-rs.win32-ia32-msvc.node')
144
+ } catch (e) {
145
+ loadErrors.push(e)
146
+ }
147
+ try {
148
+ const binding = require('feedparser-rs-win32-ia32-msvc')
149
+ const bindingPackageVersion = require('feedparser-rs-win32-ia32-msvc/package.json').version
150
+ if (bindingPackageVersion !== '0.1.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
151
+ throw new Error(`Native binding package version mismatch, expected 0.1.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
152
+ }
153
+ return binding
154
+ } catch (e) {
155
+ loadErrors.push(e)
156
+ }
157
+ } else if (process.arch === 'arm64') {
158
+ try {
159
+ return require('./feedparser-rs.win32-arm64-msvc.node')
160
+ } catch (e) {
161
+ loadErrors.push(e)
162
+ }
163
+ try {
164
+ const binding = require('feedparser-rs-win32-arm64-msvc')
165
+ const bindingPackageVersion = require('feedparser-rs-win32-arm64-msvc/package.json').version
166
+ if (bindingPackageVersion !== '0.1.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
167
+ throw new Error(`Native binding package version mismatch, expected 0.1.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
168
+ }
169
+ return binding
170
+ } catch (e) {
171
+ loadErrors.push(e)
172
+ }
173
+ } else {
174
+ loadErrors.push(new Error(`Unsupported architecture on Windows: ${process.arch}`))
175
+ }
176
+ } else if (process.platform === 'darwin') {
177
+ try {
178
+ return require('./feedparser-rs.darwin-universal.node')
179
+ } catch (e) {
180
+ loadErrors.push(e)
181
+ }
182
+ try {
183
+ const binding = require('feedparser-rs-darwin-universal')
184
+ const bindingPackageVersion = require('feedparser-rs-darwin-universal/package.json').version
185
+ if (bindingPackageVersion !== '0.1.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
186
+ throw new Error(`Native binding package version mismatch, expected 0.1.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
187
+ }
188
+ return binding
189
+ } catch (e) {
190
+ loadErrors.push(e)
191
+ }
192
+ if (process.arch === 'x64') {
193
+ try {
194
+ return require('./feedparser-rs.darwin-x64.node')
195
+ } catch (e) {
196
+ loadErrors.push(e)
197
+ }
198
+ try {
199
+ const binding = require('feedparser-rs-darwin-x64')
200
+ const bindingPackageVersion = require('feedparser-rs-darwin-x64/package.json').version
201
+ if (bindingPackageVersion !== '0.1.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
202
+ throw new Error(`Native binding package version mismatch, expected 0.1.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
203
+ }
204
+ return binding
205
+ } catch (e) {
206
+ loadErrors.push(e)
207
+ }
208
+ } else if (process.arch === 'arm64') {
209
+ try {
210
+ return require('./feedparser-rs.darwin-arm64.node')
211
+ } catch (e) {
212
+ loadErrors.push(e)
213
+ }
214
+ try {
215
+ const binding = require('feedparser-rs-darwin-arm64')
216
+ const bindingPackageVersion = require('feedparser-rs-darwin-arm64/package.json').version
217
+ if (bindingPackageVersion !== '0.1.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
218
+ throw new Error(`Native binding package version mismatch, expected 0.1.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
219
+ }
220
+ return binding
221
+ } catch (e) {
222
+ loadErrors.push(e)
223
+ }
224
+ } else {
225
+ loadErrors.push(new Error(`Unsupported architecture on macOS: ${process.arch}`))
226
+ }
227
+ } else if (process.platform === 'freebsd') {
228
+ if (process.arch === 'x64') {
229
+ try {
230
+ return require('./feedparser-rs.freebsd-x64.node')
231
+ } catch (e) {
232
+ loadErrors.push(e)
233
+ }
234
+ try {
235
+ const binding = require('feedparser-rs-freebsd-x64')
236
+ const bindingPackageVersion = require('feedparser-rs-freebsd-x64/package.json').version
237
+ if (bindingPackageVersion !== '0.1.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
238
+ throw new Error(`Native binding package version mismatch, expected 0.1.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
239
+ }
240
+ return binding
241
+ } catch (e) {
242
+ loadErrors.push(e)
243
+ }
244
+ } else if (process.arch === 'arm64') {
245
+ try {
246
+ return require('./feedparser-rs.freebsd-arm64.node')
247
+ } catch (e) {
248
+ loadErrors.push(e)
249
+ }
250
+ try {
251
+ const binding = require('feedparser-rs-freebsd-arm64')
252
+ const bindingPackageVersion = require('feedparser-rs-freebsd-arm64/package.json').version
253
+ if (bindingPackageVersion !== '0.1.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
254
+ throw new Error(`Native binding package version mismatch, expected 0.1.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
255
+ }
256
+ return binding
257
+ } catch (e) {
258
+ loadErrors.push(e)
259
+ }
260
+ } else {
261
+ loadErrors.push(new Error(`Unsupported architecture on FreeBSD: ${process.arch}`))
262
+ }
263
+ } else if (process.platform === 'linux') {
264
+ if (process.arch === 'x64') {
265
+ if (isMusl()) {
266
+ try {
267
+ return require('./feedparser-rs.linux-x64-musl.node')
268
+ } catch (e) {
269
+ loadErrors.push(e)
270
+ }
271
+ try {
272
+ const binding = require('feedparser-rs-linux-x64-musl')
273
+ const bindingPackageVersion = require('feedparser-rs-linux-x64-musl/package.json').version
274
+ if (bindingPackageVersion !== '0.1.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
275
+ throw new Error(`Native binding package version mismatch, expected 0.1.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
276
+ }
277
+ return binding
278
+ } catch (e) {
279
+ loadErrors.push(e)
280
+ }
281
+ } else {
282
+ try {
283
+ return require('./feedparser-rs.linux-x64-gnu.node')
284
+ } catch (e) {
285
+ loadErrors.push(e)
286
+ }
287
+ try {
288
+ const binding = require('feedparser-rs-linux-x64-gnu')
289
+ const bindingPackageVersion = require('feedparser-rs-linux-x64-gnu/package.json').version
290
+ if (bindingPackageVersion !== '0.1.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
291
+ throw new Error(`Native binding package version mismatch, expected 0.1.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
292
+ }
293
+ return binding
294
+ } catch (e) {
295
+ loadErrors.push(e)
296
+ }
297
+ }
298
+ } else if (process.arch === 'arm64') {
299
+ if (isMusl()) {
300
+ try {
301
+ return require('./feedparser-rs.linux-arm64-musl.node')
302
+ } catch (e) {
303
+ loadErrors.push(e)
304
+ }
305
+ try {
306
+ const binding = require('feedparser-rs-linux-arm64-musl')
307
+ const bindingPackageVersion = require('feedparser-rs-linux-arm64-musl/package.json').version
308
+ if (bindingPackageVersion !== '0.1.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
309
+ throw new Error(`Native binding package version mismatch, expected 0.1.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
310
+ }
311
+ return binding
312
+ } catch (e) {
313
+ loadErrors.push(e)
314
+ }
315
+ } else {
316
+ try {
317
+ return require('./feedparser-rs.linux-arm64-gnu.node')
318
+ } catch (e) {
319
+ loadErrors.push(e)
320
+ }
321
+ try {
322
+ const binding = require('feedparser-rs-linux-arm64-gnu')
323
+ const bindingPackageVersion = require('feedparser-rs-linux-arm64-gnu/package.json').version
324
+ if (bindingPackageVersion !== '0.1.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
325
+ throw new Error(`Native binding package version mismatch, expected 0.1.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
326
+ }
327
+ return binding
328
+ } catch (e) {
329
+ loadErrors.push(e)
330
+ }
331
+ }
332
+ } else if (process.arch === 'arm') {
333
+ if (isMusl()) {
334
+ try {
335
+ return require('./feedparser-rs.linux-arm-musleabihf.node')
336
+ } catch (e) {
337
+ loadErrors.push(e)
338
+ }
339
+ try {
340
+ const binding = require('feedparser-rs-linux-arm-musleabihf')
341
+ const bindingPackageVersion = require('feedparser-rs-linux-arm-musleabihf/package.json').version
342
+ if (bindingPackageVersion !== '0.1.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
343
+ throw new Error(`Native binding package version mismatch, expected 0.1.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
344
+ }
345
+ return binding
346
+ } catch (e) {
347
+ loadErrors.push(e)
348
+ }
349
+ } else {
350
+ try {
351
+ return require('./feedparser-rs.linux-arm-gnueabihf.node')
352
+ } catch (e) {
353
+ loadErrors.push(e)
354
+ }
355
+ try {
356
+ const binding = require('feedparser-rs-linux-arm-gnueabihf')
357
+ const bindingPackageVersion = require('feedparser-rs-linux-arm-gnueabihf/package.json').version
358
+ if (bindingPackageVersion !== '0.1.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
359
+ throw new Error(`Native binding package version mismatch, expected 0.1.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
360
+ }
361
+ return binding
362
+ } catch (e) {
363
+ loadErrors.push(e)
364
+ }
365
+ }
366
+ } else if (process.arch === 'loong64') {
367
+ if (isMusl()) {
368
+ try {
369
+ return require('./feedparser-rs.linux-loong64-musl.node')
370
+ } catch (e) {
371
+ loadErrors.push(e)
372
+ }
373
+ try {
374
+ const binding = require('feedparser-rs-linux-loong64-musl')
375
+ const bindingPackageVersion = require('feedparser-rs-linux-loong64-musl/package.json').version
376
+ if (bindingPackageVersion !== '0.1.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
377
+ throw new Error(`Native binding package version mismatch, expected 0.1.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
378
+ }
379
+ return binding
380
+ } catch (e) {
381
+ loadErrors.push(e)
382
+ }
383
+ } else {
384
+ try {
385
+ return require('./feedparser-rs.linux-loong64-gnu.node')
386
+ } catch (e) {
387
+ loadErrors.push(e)
388
+ }
389
+ try {
390
+ const binding = require('feedparser-rs-linux-loong64-gnu')
391
+ const bindingPackageVersion = require('feedparser-rs-linux-loong64-gnu/package.json').version
392
+ if (bindingPackageVersion !== '0.1.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
393
+ throw new Error(`Native binding package version mismatch, expected 0.1.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
394
+ }
395
+ return binding
396
+ } catch (e) {
397
+ loadErrors.push(e)
398
+ }
399
+ }
400
+ } else if (process.arch === 'riscv64') {
401
+ if (isMusl()) {
402
+ try {
403
+ return require('./feedparser-rs.linux-riscv64-musl.node')
404
+ } catch (e) {
405
+ loadErrors.push(e)
406
+ }
407
+ try {
408
+ const binding = require('feedparser-rs-linux-riscv64-musl')
409
+ const bindingPackageVersion = require('feedparser-rs-linux-riscv64-musl/package.json').version
410
+ if (bindingPackageVersion !== '0.1.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
411
+ throw new Error(`Native binding package version mismatch, expected 0.1.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
412
+ }
413
+ return binding
414
+ } catch (e) {
415
+ loadErrors.push(e)
416
+ }
417
+ } else {
418
+ try {
419
+ return require('./feedparser-rs.linux-riscv64-gnu.node')
420
+ } catch (e) {
421
+ loadErrors.push(e)
422
+ }
423
+ try {
424
+ const binding = require('feedparser-rs-linux-riscv64-gnu')
425
+ const bindingPackageVersion = require('feedparser-rs-linux-riscv64-gnu/package.json').version
426
+ if (bindingPackageVersion !== '0.1.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
427
+ throw new Error(`Native binding package version mismatch, expected 0.1.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
428
+ }
429
+ return binding
430
+ } catch (e) {
431
+ loadErrors.push(e)
432
+ }
433
+ }
434
+ } else if (process.arch === 'ppc64') {
435
+ try {
436
+ return require('./feedparser-rs.linux-ppc64-gnu.node')
437
+ } catch (e) {
438
+ loadErrors.push(e)
439
+ }
440
+ try {
441
+ const binding = require('feedparser-rs-linux-ppc64-gnu')
442
+ const bindingPackageVersion = require('feedparser-rs-linux-ppc64-gnu/package.json').version
443
+ if (bindingPackageVersion !== '0.1.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
444
+ throw new Error(`Native binding package version mismatch, expected 0.1.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
445
+ }
446
+ return binding
447
+ } catch (e) {
448
+ loadErrors.push(e)
449
+ }
450
+ } else if (process.arch === 's390x') {
451
+ try {
452
+ return require('./feedparser-rs.linux-s390x-gnu.node')
453
+ } catch (e) {
454
+ loadErrors.push(e)
455
+ }
456
+ try {
457
+ const binding = require('feedparser-rs-linux-s390x-gnu')
458
+ const bindingPackageVersion = require('feedparser-rs-linux-s390x-gnu/package.json').version
459
+ if (bindingPackageVersion !== '0.1.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
460
+ throw new Error(`Native binding package version mismatch, expected 0.1.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
461
+ }
462
+ return binding
463
+ } catch (e) {
464
+ loadErrors.push(e)
465
+ }
466
+ } else {
467
+ loadErrors.push(new Error(`Unsupported architecture on Linux: ${process.arch}`))
468
+ }
469
+ } else if (process.platform === 'openharmony') {
470
+ if (process.arch === 'arm64') {
471
+ try {
472
+ return require('./feedparser-rs.openharmony-arm64.node')
473
+ } catch (e) {
474
+ loadErrors.push(e)
475
+ }
476
+ try {
477
+ const binding = require('feedparser-rs-openharmony-arm64')
478
+ const bindingPackageVersion = require('feedparser-rs-openharmony-arm64/package.json').version
479
+ if (bindingPackageVersion !== '0.1.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
480
+ throw new Error(`Native binding package version mismatch, expected 0.1.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
481
+ }
482
+ return binding
483
+ } catch (e) {
484
+ loadErrors.push(e)
485
+ }
486
+ } else if (process.arch === 'x64') {
487
+ try {
488
+ return require('./feedparser-rs.openharmony-x64.node')
489
+ } catch (e) {
490
+ loadErrors.push(e)
491
+ }
492
+ try {
493
+ const binding = require('feedparser-rs-openharmony-x64')
494
+ const bindingPackageVersion = require('feedparser-rs-openharmony-x64/package.json').version
495
+ if (bindingPackageVersion !== '0.1.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
496
+ throw new Error(`Native binding package version mismatch, expected 0.1.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
497
+ }
498
+ return binding
499
+ } catch (e) {
500
+ loadErrors.push(e)
501
+ }
502
+ } else if (process.arch === 'arm') {
503
+ try {
504
+ return require('./feedparser-rs.openharmony-arm.node')
505
+ } catch (e) {
506
+ loadErrors.push(e)
507
+ }
508
+ try {
509
+ const binding = require('feedparser-rs-openharmony-arm')
510
+ const bindingPackageVersion = require('feedparser-rs-openharmony-arm/package.json').version
511
+ if (bindingPackageVersion !== '0.1.0' && process.env.NAPI_RS_ENFORCE_VERSION_CHECK && process.env.NAPI_RS_ENFORCE_VERSION_CHECK !== '0') {
512
+ throw new Error(`Native binding package version mismatch, expected 0.1.0 but got ${bindingPackageVersion}. You can reinstall dependencies to fix this issue.`)
513
+ }
514
+ return binding
515
+ } catch (e) {
516
+ loadErrors.push(e)
517
+ }
518
+ } else {
519
+ loadErrors.push(new Error(`Unsupported architecture on OpenHarmony: ${process.arch}`))
520
+ }
521
+ } else {
522
+ loadErrors.push(new Error(`Unsupported OS: ${process.platform}, architecture: ${process.arch}`))
523
+ }
524
+ }
525
+
526
+ nativeBinding = requireNative()
527
+
528
+ if (!nativeBinding || process.env.NAPI_RS_FORCE_WASI) {
529
+ let wasiBinding = null
530
+ let wasiBindingError = null
531
+ try {
532
+ wasiBinding = require('./feedparser-rs.wasi.cjs')
533
+ nativeBinding = wasiBinding
534
+ } catch (err) {
535
+ if (process.env.NAPI_RS_FORCE_WASI) {
536
+ wasiBindingError = err
537
+ }
538
+ }
539
+ if (!nativeBinding) {
540
+ try {
541
+ wasiBinding = require('feedparser-rs-wasm32-wasi')
542
+ nativeBinding = wasiBinding
543
+ } catch (err) {
544
+ if (process.env.NAPI_RS_FORCE_WASI) {
545
+ wasiBindingError.cause = err
546
+ loadErrors.push(err)
547
+ }
548
+ }
549
+ }
550
+ if (process.env.NAPI_RS_FORCE_WASI === 'error' && !wasiBinding) {
551
+ const error = new Error('WASI binding not found and NAPI_RS_FORCE_WASI is set to error')
552
+ error.cause = wasiBindingError
553
+ throw error
554
+ }
555
+ }
556
+
557
+ if (!nativeBinding) {
558
+ if (loadErrors.length > 0) {
559
+ throw new Error(
560
+ `Cannot find native binding. ` +
561
+ `npm has a bug related to optional dependencies (https://github.com/npm/cli/issues/4828). ` +
562
+ 'Please try `npm i` again after removing both package-lock.json and node_modules directory.',
563
+ {
564
+ cause: loadErrors.reduce((err, cur) => {
565
+ cur.cause = err
566
+ return cur
567
+ }),
568
+ },
569
+ )
570
+ }
571
+ throw new Error(`Failed to load native binding`)
572
+ }
573
+
574
+ module.exports = nativeBinding
575
+ module.exports.detectFormat = nativeBinding.detectFormat
576
+ module.exports.parse = nativeBinding.parse
577
+ module.exports.parseWithOptions = nativeBinding.parseWithOptions
package/package.json ADDED
@@ -0,0 +1,57 @@
1
+ {
2
+ "name": "feedparser-rs",
3
+ "version": "0.1.0",
4
+ "description": "High-performance RSS/Atom/JSON Feed parser (drop-in feedparser replacement)",
5
+ "main": "index.js",
6
+ "types": "index.d.ts",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/bug-ops/feedparser-rs.git"
10
+ },
11
+ "license": "MIT OR Apache-2.0",
12
+ "keywords": [
13
+ "rss",
14
+ "atom",
15
+ "feed",
16
+ "parser",
17
+ "feedparser",
18
+ "json-feed",
19
+ "rust",
20
+ "napi-rs"
21
+ ],
22
+ "files": [
23
+ "index.js",
24
+ "index.d.ts"
25
+ ],
26
+ "napi": {
27
+ "binaryName": "feedparser-rs",
28
+ "targets": [
29
+ "x86_64-apple-darwin",
30
+ "aarch64-apple-darwin",
31
+ "x86_64-unknown-linux-gnu",
32
+ "aarch64-unknown-linux-gnu",
33
+ "x86_64-pc-windows-msvc"
34
+ ]
35
+ },
36
+ "engines": {
37
+ "node": ">= 18"
38
+ },
39
+ "scripts": {
40
+ "build": "napi build --platform --release",
41
+ "build:debug": "napi build --platform",
42
+ "test": "node --test __test__/index.spec.mjs",
43
+ "test:coverage": "c8 --reporter=lcov --reporter=text --reports-dir=coverage node --test __test__/index.spec.mjs",
44
+ "prepublishOnly": "napi prepublish -t npm"
45
+ },
46
+ "devDependencies": {
47
+ "@napi-rs/cli": "^3.5",
48
+ "c8": "^10.1.3"
49
+ },
50
+ "optionalDependencies": {
51
+ "feedparser-rs-darwin-x64": "0.1.0",
52
+ "feedparser-rs-darwin-arm64": "0.1.0",
53
+ "feedparser-rs-linux-x64-gnu": "0.1.0",
54
+ "feedparser-rs-linux-arm64-gnu": "0.1.0",
55
+ "feedparser-rs-win32-x64-msvc": "0.1.0"
56
+ }
57
+ }