rss.today 2.0.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 (131) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +702 -0
  3. package/dist/atom1.d.ts +8 -0
  4. package/dist/atom1.d.ts.map +1 -0
  5. package/dist/atom1.js +195 -0
  6. package/dist/atom1.js.map +1 -0
  7. package/dist/config/index.d.ts +2 -0
  8. package/dist/config/index.d.ts.map +1 -0
  9. package/dist/config/index.js +2 -0
  10. package/dist/config/index.js.map +1 -0
  11. package/dist/feed.d.ts +54 -0
  12. package/dist/feed.d.ts.map +1 -0
  13. package/dist/feed.js +58 -0
  14. package/dist/feed.js.map +1 -0
  15. package/dist/index.d.ts +12 -0
  16. package/dist/index.d.ts.map +1 -0
  17. package/dist/index.js +7 -0
  18. package/dist/index.js.map +1 -0
  19. package/dist/json.d.ts +8 -0
  20. package/dist/json.d.ts.map +1 -0
  21. package/dist/json.js +97 -0
  22. package/dist/json.js.map +1 -0
  23. package/dist/parser/fields.d.ts +12 -0
  24. package/dist/parser/fields.d.ts.map +1 -0
  25. package/dist/parser/fields.js +72 -0
  26. package/dist/parser/fields.js.map +1 -0
  27. package/dist/parser/parser.d.ts +93 -0
  28. package/dist/parser/parser.d.ts.map +1 -0
  29. package/dist/parser/parser.js +411 -0
  30. package/dist/parser/parser.js.map +1 -0
  31. package/dist/parser/utils.d.ts +9 -0
  32. package/dist/parser/utils.d.ts.map +1 -0
  33. package/dist/parser/utils.js +81 -0
  34. package/dist/parser/utils.js.map +1 -0
  35. package/dist/parser.d.ts +106 -0
  36. package/dist/parser.d.ts.map +1 -0
  37. package/dist/parser.js +458 -0
  38. package/dist/parser.js.map +1 -0
  39. package/dist/rss0_9.d.ts +4 -0
  40. package/dist/rss0_9.d.ts.map +1 -0
  41. package/dist/rss0_9.js +77 -0
  42. package/dist/rss0_9.js.map +1 -0
  43. package/dist/rss1.d.ts +4 -0
  44. package/dist/rss1.d.ts.map +1 -0
  45. package/dist/rss1.js +92 -0
  46. package/dist/rss1.js.map +1 -0
  47. package/dist/rss2.d.ts +7 -0
  48. package/dist/rss2.d.ts.map +1 -0
  49. package/dist/rss2.js +267 -0
  50. package/dist/rss2.js.map +1 -0
  51. package/dist/test/feed-utils.test.d.ts +2 -0
  52. package/dist/test/feed-utils.test.d.ts.map +1 -0
  53. package/dist/test/feed-utils.test.js +279 -0
  54. package/dist/test/feed-utils.test.js.map +1 -0
  55. package/dist/test/feed.test.d.ts +2 -0
  56. package/dist/test/feed.test.d.ts.map +1 -0
  57. package/dist/test/feed.test.js +190 -0
  58. package/dist/test/feed.test.js.map +1 -0
  59. package/dist/test/opml.test.d.ts +2 -0
  60. package/dist/test/opml.test.d.ts.map +1 -0
  61. package/dist/test/opml.test.js +135 -0
  62. package/dist/test/opml.test.js.map +1 -0
  63. package/dist/test/parser-test.cjs +11 -0
  64. package/dist/test/parser-test.cjs.map +1 -0
  65. package/dist/test/parser-test.d.cts +2 -0
  66. package/dist/test/parser-test.d.cts.map +1 -0
  67. package/dist/test/parser-test01.d.ts +2 -0
  68. package/dist/test/parser-test01.d.ts.map +1 -0
  69. package/dist/test/parser-test01.js +19 -0
  70. package/dist/test/parser-test01.js.map +1 -0
  71. package/dist/test/parser.test.d.ts +2 -0
  72. package/dist/test/parser.test.d.ts.map +1 -0
  73. package/dist/test/parser.test.js +244 -0
  74. package/dist/test/parser.test.js.map +1 -0
  75. package/dist/test/test-01.d.ts +2 -0
  76. package/dist/test/test-01.d.ts.map +1 -0
  77. package/dist/test/test-01.js +7 -0
  78. package/dist/test/test-01.js.map +1 -0
  79. package/dist/test/test-xml2js.d.ts +2 -0
  80. package/dist/test/test-xml2js.d.ts.map +1 -0
  81. package/dist/test/test-xml2js.js +43 -0
  82. package/dist/test/test-xml2js.js.map +1 -0
  83. package/dist/test/test.d.ts +2 -0
  84. package/dist/test/test.d.ts.map +1 -0
  85. package/dist/test/test.js +3 -0
  86. package/dist/test/test.js.map +1 -0
  87. package/dist/test/utilities.test.d.ts +2 -0
  88. package/dist/test/utilities.test.d.ts.map +1 -0
  89. package/dist/test/utilities.test.js +258 -0
  90. package/dist/test/utilities.test.js.map +1 -0
  91. package/dist/types/atom-types.d.ts +100 -0
  92. package/dist/types/atom-types.d.ts.map +1 -0
  93. package/dist/types/atom-types.js +2 -0
  94. package/dist/types/atom-types.js.map +1 -0
  95. package/dist/types/index.d.ts +63 -0
  96. package/dist/types/index.d.ts.map +1 -0
  97. package/dist/types/index.js +2 -0
  98. package/dist/types/index.js.map +1 -0
  99. package/dist/types/rss-types.d.ts +504 -0
  100. package/dist/types/rss-types.d.ts.map +1 -0
  101. package/dist/types/rss-types.js +2 -0
  102. package/dist/types/rss-types.js.map +1 -0
  103. package/dist/uitls/format-check.d.ts +7 -0
  104. package/dist/uitls/format-check.d.ts.map +1 -0
  105. package/dist/uitls/format-check.js +28 -0
  106. package/dist/uitls/format-check.js.map +1 -0
  107. package/dist/uitls/util.d.ts +9 -0
  108. package/dist/uitls/util.d.ts.map +1 -0
  109. package/dist/uitls/util.js +81 -0
  110. package/dist/uitls/util.js.map +1 -0
  111. package/dist/util/converter.d.ts +5 -0
  112. package/dist/util/converter.d.ts.map +1 -0
  113. package/dist/util/converter.js +97 -0
  114. package/dist/util/converter.js.map +1 -0
  115. package/dist/util/feed-utils.d.ts +19 -0
  116. package/dist/util/feed-utils.d.ts.map +1 -0
  117. package/dist/util/feed-utils.js +157 -0
  118. package/dist/util/feed-utils.js.map +1 -0
  119. package/dist/util/opml.d.ts +29 -0
  120. package/dist/util/opml.d.ts.map +1 -0
  121. package/dist/util/opml.js +129 -0
  122. package/dist/util/opml.js.map +1 -0
  123. package/dist/util/validator.d.ts +10 -0
  124. package/dist/util/validator.d.ts.map +1 -0
  125. package/dist/util/validator.js +143 -0
  126. package/dist/util/validator.js.map +1 -0
  127. package/dist/util.d.ts +2 -0
  128. package/dist/util.d.ts.map +1 -0
  129. package/dist/util.js +7 -0
  130. package/dist/util.js.map +1 -0
  131. package/package.json +54 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 rssToday
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,702 @@
1
+ # rss.Today
2
+
3
+ A modern TypeScript library for parsing and generating RSS/Atom feeds. Supports multiple input sources, all common feed formats, and podcast extensions.
4
+
5
+ ## Features
6
+
7
+ - **Parse** RSS 0.9, RSS 1.0, RSS 2.0, Atom 1.0, and JSON Feed 1.0
8
+ - **Generate** RSS 0.9, RSS 1.0, RSS 2.0, Atom 1.0, and JSON Feed 1.0
9
+ - **Multiple input sources**: URL, string, file, buffer, or stream
10
+ - **Feed validation**: Validate feeds against RSS 2.0, Atom 1.0, and JSON Feed 1.0 specifications
11
+ - **Feed conversion**: Convert feeds between different formats
12
+ - **Podcast support**: iTunes namespace extensions
13
+ - **TypeScript**: Fully typed with comprehensive type definitions
14
+ - **ESM**: Modern ES modules support
15
+
16
+ ## Installation
17
+
18
+ ```bash
19
+ npm install rss.today
20
+ ```
21
+
22
+ ```bash
23
+ yarn add rss.today
24
+ ```
25
+
26
+ ```bash
27
+ pnpm add rss.today
28
+ ```
29
+
30
+ ## Quick Start
31
+
32
+ ### Parsing Feeds
33
+
34
+ ```typescript
35
+ import { Parser } from 'rss.today';
36
+
37
+ const parser = new Parser();
38
+
39
+ // Parse from URL
40
+ const feed = await parser.parseURL('https://example.com/feed.xml');
41
+ console.log(feed.title);
42
+ console.log(feed.items);
43
+
44
+ // Parse from XML string
45
+ const xml = '<?xml version="1.0"?><rss version="2.0">...</rss>';
46
+ const feed = await parser.parseString(xml);
47
+
48
+ // Parse from local file
49
+ const feed = await parser.parseFile('./feeds/my-feed.xml');
50
+
51
+ // Parse from Buffer
52
+ const buffer = Buffer.from(xmlContent);
53
+ const feed = await parser.parseBuffer(buffer);
54
+
55
+ // Parse from Stream
56
+ const stream = fs.createReadStream('./feed.xml');
57
+ const feed = await parser.parseStream(stream);
58
+ ```
59
+
60
+ ### Generating Feeds
61
+
62
+ ```typescript
63
+ import { Feed } from 'rss.today';
64
+
65
+ const feed = new Feed({
66
+ title: 'My Blog',
67
+ description: 'A blog about technology',
68
+ link: 'https://example.com',
69
+ id: 'https://example.com/feed',
70
+ language: 'en',
71
+ });
72
+
73
+ // Add items
74
+ feed.addItem({
75
+ title: 'First Post',
76
+ link: 'https://example.com/first-post',
77
+ description: 'This is my first post',
78
+ date: new Date(),
79
+ });
80
+
81
+ // Generate RSS 0.9
82
+ const rss09 = feed.rss0_9();
83
+
84
+ // Generate RSS 1.0
85
+ const rss1 = feed.rss1();
86
+
87
+ // Generate RSS 2.0
88
+ const rss = feed.rss2();
89
+
90
+ // Generate Atom 1.0
91
+ const atom = feed.atom1();
92
+
93
+ // Generate JSON Feed 1.0
94
+ const json = feed.json1();
95
+ ```
96
+
97
+ ## API Documentation
98
+
99
+ ### Parser Class
100
+
101
+ #### Constructor
102
+
103
+ ```typescript
104
+ constructor(options?: ParserOptions)
105
+ ```
106
+
107
+ **Options:**
108
+ ```typescript
109
+ interface ParserOptions {
110
+ headers?: Record<string, string>;
111
+ xml2js?: xml2js.OptionsV2;
112
+ customFields?: {
113
+ item?: string[];
114
+ feed?: string[];
115
+ };
116
+ defaultRSS?: 0.9 | 1 | 2;
117
+ }
118
+ ```
119
+
120
+ **Example with custom fields:**
121
+ ```typescript
122
+ const parser = new Parser({
123
+ customFields: {
124
+ feed: ['dc:creator', 'dc:date'],
125
+ item: ['dc:subject', 'media:thumbnail']
126
+ }
127
+ });
128
+ ```
129
+
130
+ #### Methods
131
+
132
+ ##### `parseString(xml: string): Promise<ParsedFeed>`
133
+
134
+ Parse feed from XML string.
135
+
136
+ ```typescript
137
+ const feed = await parser.parseString(xmlString);
138
+ ```
139
+
140
+ ##### `parseURL(url: string): Promise<ParsedFeed>`
141
+
142
+ Parse feed from URL.
143
+
144
+ ```typescript
145
+ const feed = await parser.parseURL('https://example.com/feed.xml');
146
+ ```
147
+
148
+ ##### `parseFile(filePath: string): Promise<ParsedFeed>`
149
+
150
+ Parse feed from local file.
151
+
152
+ ```typescript
153
+ const feed = await parser.parseFile('./feed.xml');
154
+ ```
155
+
156
+ ##### `parseBuffer(buffer: Buffer, encoding?: BufferEncoding): Promise<ParsedFeed>`
157
+
158
+ Parse feed from Buffer.
159
+
160
+ ```typescript
161
+ const feed = await parser.parseBuffer(buffer, 'utf-8');
162
+ ```
163
+
164
+ ##### `parseStream(stream: Readable, maxChunkSize?: number): Promise<ParsedFeed>`
165
+
166
+ Parse feed from Node.js Readable stream.
167
+
168
+ ```typescript
169
+ const feed = await parser.parseStream(stream, 10 * 1024 * 1024);
170
+ ```
171
+
172
+ ##### `parseJSON(json: string): Promise<ParsedFeed>`
173
+
174
+ Parse JSON Feed 1.0 from JSON string.
175
+
176
+ ```typescript
177
+ const feed = await parser.parseJSON('{"version": "https://jsonfeed.org/version/1", "title": "My Feed", ...}');
178
+ ```
179
+
180
+ #### ParsedFeed Interface
181
+
182
+ ```typescript
183
+ interface ParsedFeed {
184
+ title?: string;
185
+ description?: string;
186
+ link?: string;
187
+ feedUrl?: string;
188
+ language?: string;
189
+ lastBuildDate?: string;
190
+ image?: {
191
+ url?: string;
192
+ title?: string;
193
+ link?: string;
194
+ };
195
+ items: ParsedItem[];
196
+ itunes?: any; // Podcast metadata
197
+ [key: string]: any; // Custom fields
198
+ }
199
+ ```
200
+
201
+ #### ParsedItem Interface
202
+
203
+ ```typescript
204
+ interface ParsedItem {
205
+ title?: string;
206
+ link?: string;
207
+ pubDate?: string;
208
+ date?: string;
209
+ isoDate?: string;
210
+ author?: string;
211
+ content?: string;
212
+ contentSnippet?: string;
213
+ summary?: string;
214
+ guid?: string;
215
+ enclosure?: {
216
+ url?: string;
217
+ length?: string;
218
+ type?: string;
219
+ };
220
+ categories?: any[];
221
+ itunes?: any; // Podcast metadata
222
+ [key: string]: any; // Custom fields
223
+ }
224
+ ```
225
+
226
+ ### Feed Class
227
+
228
+ #### Constructor
229
+
230
+ ```typescript
231
+ constructor(options: FeedOptions)
232
+ ```
233
+
234
+ **Options:**
235
+ ```typescript
236
+ interface FeedOptions {
237
+ title: string;
238
+ description?: string;
239
+ link?: string;
240
+ id?: string;
241
+ language?: string;
242
+ copyright?: string;
243
+ updated?: Date;
244
+ author?: Author;
245
+ }
246
+ ```
247
+
248
+ #### Methods
249
+
250
+ ##### `addItem(item: Item): void`
251
+
252
+ Add an item to the feed.
253
+
254
+ ```typescript
255
+ feed.addItem({
256
+ title: 'Post Title',
257
+ link: 'https://example.com/post',
258
+ description: 'Post description',
259
+ content: 'Full HTML content',
260
+ author: 'Author Name',
261
+ date: new Date(),
262
+ });
263
+ ```
264
+
265
+ ##### `addCategory(category: string): void`
266
+
267
+ Add a category to the feed.
268
+
269
+ ```typescript
270
+ feed.addCategory('technology');
271
+ feed.addCategory('programming');
272
+ ```
273
+
274
+ ##### `addContributor(contributor: Author): void`
275
+
276
+ Add a contributor to the feed.
277
+
278
+ ```typescript
279
+ feed.addContributor({
280
+ name: 'Jane Doe',
281
+ email: 'jane@example.com',
282
+ link: 'https://example.com/jane',
283
+ });
284
+ ```
285
+
286
+ ##### `addExtension(extension: Extension): void`
287
+
288
+ Add a custom extension to the feed.
289
+
290
+ ```typescript
291
+ feed.addExtension({
292
+ name: 'itunes',
293
+ objects: { author: 'Podcast Author' },
294
+ });
295
+ ```
296
+
297
+ ##### `rss2(): string`
298
+
299
+ Generate RSS 2.0 feed.
300
+
301
+ ```typescript
302
+ const rss = feed.rss2();
303
+ ```
304
+
305
+ ##### `atom1(): string`
306
+
307
+ Generate Atom 1.0 feed.
308
+
309
+ ```typescript
310
+ const atom = feed.atom1();
311
+ ```
312
+
313
+ ##### `json1(): string`
314
+
315
+ Generate JSON Feed 1.0.
316
+
317
+ ```typescript
318
+ const json = feed.json1();
319
+ ```
320
+
321
+ ### Validation Utilities
322
+
323
+ The library includes feed validation utilities to help ensure feeds meet format specifications:
324
+
325
+ #### `validateRSS2(feed: ParsedFeed): ValidationResult`
326
+
327
+ Validate RSS 2.0 feeds for compliance.
328
+
329
+ ```typescript
330
+ import { validateRSS2 } from 'rss.today';
331
+
332
+ const result = validateRSS2(parsedFeed);
333
+
334
+ if (!result.valid) {
335
+ console.error('Validation errors:', result.errors);
336
+ }
337
+ if (result.warnings.length > 0) {
338
+ console.warn('Validation warnings:', result.warnings);
339
+ }
340
+ ```
341
+
342
+ #### `validateAtom(feed: ParsedFeed): ValidationResult`
343
+
344
+ Validate Atom 1.0 feeds for compliance.
345
+
346
+ ```typescript
347
+ import { validateAtom } from 'rss.today';
348
+
349
+ const result = validateAtom(parsedFeed);
350
+ ```
351
+
352
+ #### `validateJSONFeed(feed: any): ValidationResult`
353
+
354
+ Validate JSON Feed 1.0 for compliance.
355
+
356
+ ```typescript
357
+ import { validateJSONFeed } from 'rss.today';
358
+
359
+ const result = validateJSONFeed(jsonFeed);
360
+ ```
361
+
362
+ ### Conversion Utilities
363
+
364
+ Convert parsed feeds between different formats:
365
+
366
+ #### `convertToRSS2(feed: ParsedFeed): string`
367
+
368
+ Convert any parsed feed to RSS 2.0 format.
369
+
370
+ ```typescript
371
+ import { Parser, convertToRSS2 } from 'rss.today';
372
+
373
+ const parser = new Parser();
374
+ const atomFeed = await parser.parseString(atomXml);
375
+ const rss2Xml = convertToRSS2(atomFeed);
376
+ ```
377
+
378
+ #### `convertToAtom(feed: ParsedFeed): string`
379
+
380
+ Convert any parsed feed to Atom 1.0 format.
381
+
382
+ ```typescript
383
+ import { convertToAtom } from 'rss.today';
384
+
385
+ const rss2Feed = await parser.parseString(rssXml);
386
+ const atomXml = convertToAtom(rss2Feed);
387
+ ```
388
+
389
+ #### `convertToJSON(feed: ParsedFeed): string`
390
+
391
+ Convert any parsed feed to JSON Feed 1.0 format.
392
+
393
+ ```typescript
394
+ import { convertToJSON } from 'rss.today';
395
+
396
+ const atomFeed = await parser.parseString(atomXml);
397
+ const jsonFeed = convertToJSON(atomFeed);
398
+ ```
399
+
400
+ ### Feed Utilities
401
+
402
+ Utilities for manipulating and managing feeds:
403
+
404
+ #### `mergeFeeds(feeds: ParsedFeed[]): ParsedFeed`
405
+
406
+ Combine multiple feeds into one, automatically removing duplicates.
407
+
408
+ ```typescript
409
+ import { Parser, mergeFeeds } from 'rss.today';
410
+
411
+ const parser = new Parser();
412
+ const feeds = await Promise.all([
413
+ parser.parseURL('https://example.com/feed1.xml'),
414
+ parser.parseURL('https://example.com/feed2.xml')
415
+ ]);
416
+
417
+ const merged = mergeFeeds(feeds);
418
+ console.log(`Merged ${merged.items.length} items`);
419
+ ```
420
+
421
+ #### `diffFeeds(oldFeed: ParsedFeed, newFeed: ParsedFeed): FeedDiffResult`
422
+
423
+ Compare two feeds to find differences.
424
+
425
+ ```typescript
426
+ import { Parser, diffFeeds } from 'rss.today';
427
+
428
+ const oldFeed = await parser.parseFile('./old-feed.xml');
429
+ const newFeed = await parser.parseFile('./new-feed.xml');
430
+
431
+ const diff = diffFeeds(oldFeed, newFeed);
432
+
433
+ console.log(`Added: ${diff.added.length}`);
434
+ console.log(`Removed: ${diff.removed.length}`);
435
+ console.log(`Modified: ${diff.modified.length}`);
436
+ ```
437
+
438
+ #### `filterFeed(feed: ParsedFeed, predicate: (item, ParsedItem) => boolean): ParsedFeed`
439
+
440
+ Filter feed items using a custom predicate.
441
+
442
+ #### `filterByCategory(feed: ParsedFeed, category: string): ParsedFeed`
443
+
444
+ Filter items by category.
445
+
446
+ ```typescript
447
+ import { filterByCategory } from 'rss.today';
448
+
449
+ const techFeed = filterByCategory(feed, 'technology');
450
+ ```
451
+
452
+ #### `filterByDateRange(feed: ParsedFeed, startDate: Date, endDate?: Date): ParsedFeed`
453
+
454
+ Filter items by date range.
455
+
456
+ ```typescript
457
+ import { filterByDateRange } from 'rss.today';
458
+
459
+ const weekAgo = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000);
460
+ const recentFeed = filterByDateRange(feed, weekAgo);
461
+ ```
462
+
463
+ #### `sortByDate(feed: ParsedFeed, order?: 'asc' | 'desc'): ParsedFeed`
464
+
465
+ Sort feed items by date.
466
+
467
+ ```typescript
468
+ import { sortByDate } from 'rss.today';
469
+
470
+ const sorted = sortByDate(feed, 'desc'); // newest first
471
+ ```
472
+
473
+ #### `sortByTitle(feed: ParsedFeed, order?: 'asc' | 'desc'): ParsedFeed`
474
+
475
+ Sort feed items by title.
476
+
477
+ #### `limitFeed(feed: ParsedFeed, count: number): ParsedFeed`
478
+
479
+ Limit feed to a specific number of items.
480
+
481
+ ```typescript
482
+ import { limitFeed } from 'rss.today';
483
+
484
+ const latest10 = limitFeed(feed, 10);
485
+ ```
486
+
487
+ #### `deduplicateFeed(feed: ParsedFeed): ParsedFeed`
488
+
489
+ Remove duplicate items from a feed.
490
+
491
+ ```typescript
492
+ import { deduplicateFeed } from 'rss.today';
493
+
494
+ const unique = deduplicateFeed(feed);
495
+ ```
496
+
497
+ ### OPML Support
498
+
499
+ Parse and generate OPML (Outline Processor Markup Language) files for feed subscriptions:
500
+
501
+ #### `OPMLParser`
502
+
503
+ Parse OPML subscription lists.
504
+
505
+ ```typescript
506
+ import { OPMLParser } from 'rss.today';
507
+
508
+ const parser = new OPMLParser();
509
+ const opml = await parser.parseOPML(opmlXml);
510
+
511
+ console.log(opml.title);
512
+ console.log(opml.outlines.length);
513
+ ```
514
+
515
+ #### `OPMLGenerator`
516
+
517
+ Generate OPML subscription lists.
518
+
519
+ ```typescript
520
+ import { OPMLGenerator, type OPMLDocument } from 'rss.today';
521
+
522
+ const opml: OPMLDocument = {
523
+ title: 'My Subscriptions',
524
+ outlines: [
525
+ {
526
+ text: 'TechCrunch',
527
+ type: 'rss',
528
+ xmlUrl: 'https://techcrunch.com/feed/',
529
+ htmlUrl: 'https://techcrunch.com'
530
+ }
531
+ ]
532
+ };
533
+
534
+ const generator = new OPMLGenerator();
535
+ const opmlXml = generator.generateOPML(opml);
536
+ ```
537
+
538
+ ## Supported Formats
539
+
540
+ | Format | Parse | Generate |
541
+ |--------|-------|----------|
542
+ | RSS 0.9 | ✅ | ✅ |
543
+ | RSS 1.0 | ✅ | ✅ |
544
+ | RSS 2.0 | ✅ | ✅ |
545
+ | Atom 1.0 | ✅ | ✅ |
546
+ | JSON Feed 1.0 | ✅ | ✅ |
547
+
548
+ ## Podcast Support
549
+
550
+ The parser automatically detects and extracts iTunes podcast metadata:
551
+
552
+ ```typescript
553
+ const feed = await parser.parseURL('https://example.com/podcast.xml');
554
+
555
+ console.log(feed.itunes?.author);
556
+ console.log(feed.itunes?.summary);
557
+ console.log(feed.itunes?.image);
558
+
559
+ // Items also have iTunes metadata
560
+ feed.items.forEach(item => {
561
+ console.log(item.itunes?.duration);
562
+ console.log(item.itunes?.subtitle);
563
+ });
564
+ ```
565
+
566
+ ## Error Handling
567
+
568
+ ```typescript
569
+ import { Parser } from 'rss.today';
570
+
571
+ const parser = new Parser();
572
+
573
+ try {
574
+ const feed = await parser.parseURL('https://example.com/feed.xml');
575
+ console.log(feed.title);
576
+ } catch (error) {
577
+ if (error.message.includes('Feed not recognized')) {
578
+ console.error('Unsupported feed format');
579
+ } else if (error.message.includes('File not found')) {
580
+ console.error('File does not exist');
581
+ } else {
582
+ console.error('Error:', error.message);
583
+ }
584
+ }
585
+ ```
586
+
587
+ ## Complete Examples
588
+
589
+ ### Parse a Blog Feed
590
+
591
+ ```typescript
592
+ import { Parser } from 'rss.today';
593
+
594
+ async function displayLatestPosts(url: string, count: number = 5) {
595
+ const parser = new Parser();
596
+ const feed = await parser.parseURL(url);
597
+
598
+ console.log(`=== ${feed.title} ===`);
599
+ console.log(feed.description);
600
+ console.log(`Last updated: ${feed.lastBuildDate}\n`);
601
+
602
+ feed.items.slice(0, count).forEach((item, index) => {
603
+ console.log(`${index + 1}. ${item.title}`);
604
+ console.log(` ${item.link}`);
605
+ console.log(` ${item.pubDate}\n`);
606
+ });
607
+ }
608
+
609
+ displayLatestPosts('https://example.com/blog/feed.xml');
610
+ ```
611
+
612
+ ### Generate a Podcast Feed
613
+
614
+ ```typescript
615
+ import { Feed } from 'rss.today';
616
+
617
+ const feed = new Feed({
618
+ title: 'My Podcast',
619
+ description: 'A podcast about interesting topics',
620
+ link: 'https://example.com',
621
+ id: 'https://example.com/feed',
622
+ language: 'en',
623
+ });
624
+
625
+ // Add iTunes extension
626
+ feed.addExtension({
627
+ name: 'itunes',
628
+ objects: {
629
+ author: 'Podcast Author',
630
+ summary: 'A podcast about interesting topics',
631
+ explicit: 'no',
632
+ image: 'https://example.com/podcast.jpg',
633
+ },
634
+ });
635
+
636
+ // Add episodes
637
+ feed.addItem({
638
+ title: 'Episode 1: Introduction',
639
+ link: 'https://example.com/episode-1',
640
+ description: 'Welcome to our first episode!',
641
+ date: new Date('2024-01-01'),
642
+ enclosure: {
643
+ url: 'https://example.com/episode-1.mp3',
644
+ type: 'audio/mpeg',
645
+ length: '12345678',
646
+ },
647
+ });
648
+
649
+ const rss = feed.rss2();
650
+ console.log(rss);
651
+ ```
652
+
653
+ ### Parse from Multiple Sources
654
+
655
+ ```typescript
656
+ import { Parser } from 'rss.today';
657
+ import * as fs from 'fs/promises';
658
+
659
+ const parser = new Parser();
660
+
661
+ // Parse from URL
662
+ const feed1 = await parser.parseURL('https://example.com/feed.xml');
663
+
664
+ // Parse from string
665
+ const xml = await fs.readFile('./feed.xml', 'utf-8');
666
+ const feed2 = await parser.parseString(xml);
667
+
668
+ // Parse from file
669
+ const feed3 = await parser.parseFile('./feed.xml');
670
+
671
+ // Parse from buffer
672
+ const buffer = Buffer.from(xml);
673
+ const feed4 = await parser.parseBuffer(buffer);
674
+
675
+ // Parse from stream
676
+ const stream = fs.createReadStream('./feed.xml');
677
+ const feed5 = await parser.parseStream(stream);
678
+ ```
679
+
680
+ ## Development
681
+
682
+ ```bash
683
+ # Install dependencies
684
+ npm install
685
+
686
+ # Build
687
+ npm run build
688
+
689
+ # Run tests
690
+ npm test
691
+
692
+ # Run tests with coverage
693
+ npm run test:coverage
694
+ ```
695
+
696
+ ## License
697
+
698
+ ISC
699
+
700
+ ## Repository
701
+
702
+ [https://github.com/rssToday/rsstoday](https://github.com/rssToday/rsstoday)
@@ -0,0 +1,8 @@
1
+ import type { Feed } from "./feed";
2
+ /**
3
+ * Returns an Atom feed
4
+ * @param ins
5
+ */
6
+ declare const _default: (ins: Feed) => string;
7
+ export default _default;
8
+ //# sourceMappingURL=atom1.d.ts.map