feedscout 1.0.0-beta.2 → 1.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 (84) hide show
  1. package/README.md +100 -68
  2. package/dist/blogrolls/defaults.cjs +13 -13
  3. package/dist/blogrolls/defaults.d.cts +5 -5
  4. package/dist/blogrolls/defaults.d.ts +5 -5
  5. package/dist/blogrolls/defaults.js +10 -10
  6. package/dist/blogrolls/extractors.cjs +2 -2
  7. package/dist/blogrolls/extractors.d.cts +3 -3
  8. package/dist/blogrolls/extractors.d.ts +3 -3
  9. package/dist/blogrolls/extractors.js +2 -2
  10. package/dist/blogrolls/index.cjs +2 -1
  11. package/dist/blogrolls/index.d.cts +2 -2
  12. package/dist/blogrolls/index.d.ts +2 -2
  13. package/dist/blogrolls/index.js +3 -2
  14. package/dist/blogrolls/types.d.cts +2 -2
  15. package/dist/blogrolls/types.d.ts +2 -2
  16. package/dist/blogrolls.cjs +5 -5
  17. package/dist/blogrolls.d.cts +4 -4
  18. package/dist/blogrolls.d.ts +4 -4
  19. package/dist/blogrolls.js +3 -3
  20. package/dist/common/discover/index.cjs +2 -2
  21. package/dist/common/discover/index.js +2 -2
  22. package/dist/common/discover/utils.cjs +12 -9
  23. package/dist/common/discover/utils.js +12 -9
  24. package/dist/common/locales.cjs +1 -0
  25. package/dist/common/locales.js +1 -0
  26. package/dist/common/types.d.cts +5 -31
  27. package/dist/common/types.d.ts +5 -31
  28. package/dist/common/uris/guess/types.d.cts +0 -21
  29. package/dist/common/uris/guess/types.d.ts +0 -21
  30. package/dist/common/uris/guess/utils.cjs +2 -53
  31. package/dist/common/uris/guess/utils.d.cts +1 -52
  32. package/dist/common/uris/guess/utils.d.ts +1 -52
  33. package/dist/common/uris/guess/utils.js +2 -53
  34. package/dist/common/uris/index.cjs +2 -0
  35. package/dist/common/uris/index.js +2 -0
  36. package/dist/common/uris/platform/index.cjs +12 -0
  37. package/dist/common/uris/platform/index.js +11 -0
  38. package/dist/common/uris/platform/types.d.cts +11 -0
  39. package/dist/common/uris/platform/types.d.ts +11 -0
  40. package/dist/common/utils.cjs +9 -0
  41. package/dist/common/utils.js +7 -1
  42. package/dist/feeds/defaults.cjs +39 -14
  43. package/dist/feeds/defaults.d.cts +7 -5
  44. package/dist/feeds/defaults.d.ts +7 -5
  45. package/dist/feeds/defaults.js +35 -10
  46. package/dist/feeds/extractors.cjs +2 -2
  47. package/dist/feeds/extractors.d.cts +3 -3
  48. package/dist/feeds/extractors.d.ts +3 -3
  49. package/dist/feeds/extractors.js +2 -2
  50. package/dist/feeds/index.cjs +2 -1
  51. package/dist/feeds/index.d.cts +2 -2
  52. package/dist/feeds/index.d.ts +2 -2
  53. package/dist/feeds/index.js +4 -3
  54. package/dist/feeds/platform/handlers/blogspot.cjs +15 -0
  55. package/dist/feeds/platform/handlers/blogspot.js +15 -0
  56. package/dist/feeds/platform/handlers/bluesky.cjs +18 -0
  57. package/dist/feeds/platform/handlers/bluesky.js +18 -0
  58. package/dist/feeds/platform/handlers/github.cjs +83 -0
  59. package/dist/feeds/platform/handlers/github.js +83 -0
  60. package/dist/feeds/platform/handlers/gitlab.cjs +19 -0
  61. package/dist/feeds/platform/handlers/gitlab.js +19 -0
  62. package/dist/feeds/platform/handlers/kickstarter.cjs +21 -0
  63. package/dist/feeds/platform/handlers/kickstarter.js +21 -0
  64. package/dist/feeds/platform/handlers/reddit.cjs +44 -0
  65. package/dist/feeds/platform/handlers/reddit.js +44 -0
  66. package/dist/feeds/platform/handlers/soundcloud.cjs +37 -0
  67. package/dist/feeds/platform/handlers/soundcloud.js +37 -0
  68. package/dist/feeds/platform/handlers/substack.cjs +15 -0
  69. package/dist/feeds/platform/handlers/substack.js +15 -0
  70. package/dist/feeds/platform/handlers/tumblr.cjs +15 -0
  71. package/dist/feeds/platform/handlers/tumblr.js +15 -0
  72. package/dist/feeds/platform/handlers/wordpress.cjs +19 -0
  73. package/dist/feeds/platform/handlers/wordpress.js +19 -0
  74. package/dist/feeds/platform/handlers/youtube.cjs +44 -0
  75. package/dist/feeds/platform/handlers/youtube.js +44 -0
  76. package/dist/feeds/types.d.cts +2 -3
  77. package/dist/feeds/types.d.ts +2 -3
  78. package/dist/feeds.cjs +7 -6
  79. package/dist/feeds.d.cts +4 -4
  80. package/dist/feeds.d.ts +4 -4
  81. package/dist/feeds.js +3 -3
  82. package/dist/index.d.cts +2 -3
  83. package/dist/index.d.ts +2 -3
  84. package/package.json +20 -20
package/README.md CHANGED
@@ -4,7 +4,9 @@
4
4
  [![npm version](https://img.shields.io/npm/v/feedscout.svg)](https://www.npmjs.com/package/feedscout)
5
5
  [![license](https://img.shields.io/npm/l/feedscout.svg)](https://github.com/macieklamberski/feedscout/blob/main/LICENSE)
6
6
 
7
- Advanced feed autodiscovery for JavaScript. Collect feeds from webpages using multiple discovery methods.
7
+ Advanced feed autodiscovery for JavaScript. Collect feed information from any webpage using multiple discovery methods.
8
+
9
+ Finds feeds by scanning links and anchors in HTML content, parsing HTTP headers, and guessing common paths, then validates each URL by fetching and parsing the feed.
8
10
 
9
11
  **[Read full docs ↗](https://feedscout.dev)**
10
12
    ·  
@@ -12,36 +14,63 @@ Advanced feed autodiscovery for JavaScript. Collect feeds from webpages using mu
12
14
 
13
15
  ---
14
16
 
15
- ## Overview
17
+ ## Features
16
18
 
17
- Feedscout makes it easy to discover feeds from webpages using multiple methods.
19
+ ### Supported Content
18
20
 
19
- ### HTML Discovery
21
+ - **Feeds** — RSS, Atom, JSON Feed, and RDF. Each feed is validated and returns metadata like format, title, description, and site URL.
22
+ - **Blogrolls** — OPML files containing feed subscriptions. Validated and returns title.
23
+ - **WebSub hubs** — Find hubs for real-time feed update notifications.
20
24
 
21
- Extracts feed URIs from HTML content using multiple strategies:
25
+ ### Discovery Methods
22
26
 
23
- - **Link elements** — `<link rel="alternate">` with feed MIME types and `<link rel="feed">` elements
24
- - **Anchor elements** — `<a href="…">` matching `/feed`, `/rss.xml` or containing "RSS", "Subscribe", etc.
27
+ - **Platform** — Generates feed URLs for YouTube, GitHub, WordPress, and other popular platforms using URL pattern matching.
28
+ - **HTML** — Scans `<link>` elements with feed MIME types and `<a>` elements matching feed patterns or labels like "RSS", "Subscribe".
29
+ - **Headers** — Parses HTTP `Link` headers for `rel="alternate"` with feed MIME types per RFC 8288.
30
+ - **Guess** — Tests common paths (e.g. `/feed`, `/rss.xml`, `/atom.xml`) against the base URL as a fallback.
25
31
 
26
- This method uses [htmlparser2](https://github.com/fb55/htmlparser2) for efficient parsing. Follows [RSS Board](https://www.rssboard.org/rss-autodiscovery) and [WHATWG](https://blog.whatwg.org/feed-autodiscovery) specs.
32
+ ### Customization
27
33
 
28
- ### HTTP Headers Discovery
34
+ - **Custom extractors** — Override the default parser to extract additional metadata from feeds and blogrolls.
35
+ - **Configurable methods** — Enable/disable discovery methods or customize their options.
36
+ - **Adapter system** — Use native fetch or easily integrate with Axios, Got, or Ky.
37
+ - **Concurrency control** — Limit parallel requests during validation.
38
+ - **Progress tracking** — Monitor discovery progress with callbacks.
39
+ - **Type-safe** — Full TypeScript support with exported types.
40
+ - **Tree-shakable** — Import only what you need.
29
41
 
30
- Parses HTTP `Link` headers for `rel="alternate"` with feed MIME types per [RFC 8288](https://www.rfc-editor.org/rfc/rfc8288). Useful when feeds are advertised via HTTP headers rather than HTML metadata.
42
+ ## Quick Start
31
43
 
32
- ### Guess Method
44
+ This is a short guide on how to get you up and running with Feedscout.
33
45
 
34
- Tests common feed paths (`/feed`, `/rss.xml`, `/atom.xml`, etc.) against the base URL. Useful as fallback when other methods fail.
46
+ For a full overview of all the features, [visit the documentation](https://feedscout.dev).
35
47
 
36
- ## Quick Start
48
+ ### Installation
37
49
 
38
50
  ```bash
39
51
  npm install feedscout
40
52
  ```
41
53
 
42
- ### Basic Usage
54
+ ### Discover Feeds
55
+
56
+ ```typescript
57
+ import { discoverFeeds } from 'feedscout'
58
+
59
+ const feeds = await discoverFeeds('https://example.com', {
60
+ methods: ['html', 'headers'],
61
+ })
62
+
63
+ // [{
64
+ // url: 'https://example.com/feed.xml',
65
+ // isValid: true,
66
+ // format: 'rss',
67
+ // title: 'Example Blog',
68
+ // description: 'A blog about examples',
69
+ // siteUrl: 'https://example.com',
70
+ // }]
71
+ ```
43
72
 
44
- Given HTML content on https://example.com:
73
+ Or with existing HTML content:
45
74
 
46
75
  ```html
47
76
  <html>
@@ -54,76 +83,79 @@ Given HTML content on https://example.com:
54
83
  </html>
55
84
  ```
56
85
 
57
- Feedscout discovers and extracts feeds data:
58
-
59
86
  ```typescript
60
- import { discoverFeeds } from 'feedscout'
61
-
62
87
  const feeds = await discoverFeeds(
63
88
  { url: 'https://example.com', content: html },
64
- { methods: ['html', 'guess'] },
89
+ { methods: ['html'] },
65
90
  )
66
91
 
67
- // [{ url: 'https://example.com/feed.xml', isValid: true, format: 'rss', title: '...' }]
92
+ // [
93
+ // {
94
+ // url: 'https://example.com/feed.xml',
95
+ // isValid: true,
96
+ // format: 'rss',
97
+ // title: 'Example Blog',
98
+ // description: 'A blog about examples',
99
+ // siteUrl: 'https://example.com',
100
+ // },
101
+ // {
102
+ // url: 'https://example.com/rss',
103
+ // isValid: true,
104
+ // format: 'rss',
105
+ // title: 'Example Blog',
106
+ // description: 'A blog about examples',
107
+ // siteUrl: 'https://example.com',
108
+ // },
109
+ // ]
68
110
  ```
69
111
 
70
- By default, native `fetch()` is used. For projects using other HTTP libraries, adapters are available:
112
+ Or with HTTP headers:
113
+
114
+ ```http
115
+ Link: </feed.xml>; rel="alternate"; type="application/rss+xml"
116
+ ```
71
117
 
72
118
  ```typescript
73
- import axios from 'axios'
74
- import { createAxiosAdapter } from 'feedscout/adapters'
119
+ const feeds = await discoverFeeds(
120
+ { url: 'https://example.com', headers },
121
+ { methods: ['headers'] },
122
+ )
75
123
 
76
- const feeds = await discoverFeeds(input, {
77
- methods: ['html', 'guess'],
78
- fetchFn: createAxiosAdapter(axios),
79
- })
124
+ // [{
125
+ // url: 'https://example.com/feed.xml',
126
+ // isValid: true,
127
+ // format: 'rss',
128
+ // title: 'Example Blog',
129
+ // description: 'A blog about examples',
130
+ // siteUrl: 'https://example.com',
131
+ // }]
80
132
  ```
81
133
 
82
- | Adapter | Library |
83
- |---------|---------|
84
- | `createNativeFetchAdapter` | Native `fetch` (default) |
85
- | `createAxiosAdapter` | [axios](https://axios-http.com) |
86
- | `createGotAdapter` | [got](https://github.com/sindresorhus/got) |
87
- | `createKyAdapter` | [ky](https://github.com/sindresorhus/ky) |
88
-
134
+ ### Discover Blogrolls
89
135
 
90
- ### Using Existing Response Data
136
+ ```typescript
137
+ import { discoverBlogrolls } from 'feedscout'
91
138
 
92
- If you already have the response, you can provide it directly instead of fetching the data again:
139
+ const blogrolls = await discoverBlogrolls('https://example.com', {
140
+ methods: ['html'],
141
+ })
93
142
 
94
- ```typescript
95
- const feeds = await discoverFeeds(
96
- {
97
- url: 'https://example.com',
98
- content: htmlContent,
99
- headers: responseHeaders,
100
- },
101
- {
102
- methods: ['html', 'headers', 'guess'],
103
- },
104
- )
143
+ // [{
144
+ // url: 'https://example.com/blogroll.opml',
145
+ // isValid: true,
146
+ // title: 'My Blogroll',
147
+ // }]
105
148
  ```
106
149
 
107
- ### Custom Options with Object Syntax
108
-
109
- If you want more control over what types of links, anchors or labels are treated as feeds, use object syntax to customize method options:
150
+ ### Discover WebSub Hubs
110
151
 
111
152
  ```typescript
112
- const feeds = await discoverFeeds('https://example.com', {
113
- methods: {
114
- html: {
115
- anchorLabels: ['rss', 'feed', 'subscribe'],
116
- anchorUris: ['/feed', '/rss'],
117
- },
118
- headers: true, // Use defaults
119
- guess: {
120
- uris: ['/custom-feed', '/blog/rss'],
121
- },
122
- },
123
- concurrency: 5,
124
- stopOnFirst: true,
125
- }
126
- )
127
- ```
153
+ import { discoverHubs } from 'feedscout'
154
+
155
+ const hubs = await discoverHubs('https://example.com/feed.xml')
128
156
 
129
- For all available options, [visit the reference page in documentation ↗](https://feedscout.dev).
157
+ // [{
158
+ // hub: 'https://pubsubhubbub.appspot.com',
159
+ // topic: 'https://example.com/feed.xml',
160
+ // }]
161
+ ```
@@ -1,23 +1,23 @@
1
1
 
2
2
  //#region src/blogrolls/defaults.ts
3
- const opmlMimeTypes = [
3
+ const mimeTypes = [
4
4
  "text/x-opml",
5
5
  "application/xml",
6
6
  "text/xml"
7
7
  ];
8
- const blogrollUrisMinimal = [
8
+ const urisMinimal = [
9
9
  "/.well-known/recommendations.opml",
10
10
  "/blogroll.opml",
11
11
  "/opml.xml"
12
12
  ];
13
- const blogrollUrisBalanced = [
14
- ...blogrollUrisMinimal,
13
+ const urisBalanced = [
14
+ ...urisMinimal,
15
15
  "/blogroll.xml",
16
16
  "/subscriptions.opml",
17
17
  "/recommendations.opml"
18
18
  ];
19
- const blogrollUrisComprehensive = [
20
- ...blogrollUrisBalanced,
19
+ const urisComprehensive = [
20
+ ...urisBalanced,
21
21
  "/links.opml",
22
22
  "/feeds.opml",
23
23
  "/subscriptions.xml"
@@ -30,24 +30,24 @@ const anchorLabels = [
30
30
  ];
31
31
  const linkSelectors = [{ rel: "blogroll" }, {
32
32
  rel: "outline",
33
- types: opmlMimeTypes
33
+ types: mimeTypes
34
34
  }];
35
35
  const defaultHtmlOptions = {
36
36
  linkSelectors,
37
- anchorUris: blogrollUrisComprehensive,
37
+ anchorUris: urisComprehensive,
38
38
  anchorIgnoredUris: [],
39
39
  anchorLabels
40
40
  };
41
41
  const defaultHeadersOptions = { linkSelectors };
42
- const defaultGuessOptions = { uris: blogrollUrisBalanced };
42
+ const defaultGuessOptions = { uris: urisBalanced };
43
43
 
44
44
  //#endregion
45
45
  exports.anchorLabels = anchorLabels;
46
- exports.blogrollUrisBalanced = blogrollUrisBalanced;
47
- exports.blogrollUrisComprehensive = blogrollUrisComprehensive;
48
- exports.blogrollUrisMinimal = blogrollUrisMinimal;
49
46
  exports.defaultGuessOptions = defaultGuessOptions;
50
47
  exports.defaultHeadersOptions = defaultHeadersOptions;
51
48
  exports.defaultHtmlOptions = defaultHtmlOptions;
52
49
  exports.linkSelectors = linkSelectors;
53
- exports.opmlMimeTypes = opmlMimeTypes;
50
+ exports.mimeTypes = mimeTypes;
51
+ exports.urisBalanced = urisBalanced;
52
+ exports.urisComprehensive = urisComprehensive;
53
+ exports.urisMinimal = urisMinimal;
@@ -4,14 +4,14 @@ import { HtmlMethodOptions } from "../common/uris/html/types.cjs";
4
4
  import { LinkSelector } from "../common/types.cjs";
5
5
 
6
6
  //#region src/blogrolls/defaults.d.ts
7
- declare const opmlMimeTypes: string[];
8
- declare const blogrollUrisMinimal: string[];
9
- declare const blogrollUrisBalanced: string[];
10
- declare const blogrollUrisComprehensive: string[];
7
+ declare const mimeTypes: string[];
8
+ declare const urisMinimal: string[];
9
+ declare const urisBalanced: string[];
10
+ declare const urisComprehensive: string[];
11
11
  declare const anchorLabels: string[];
12
12
  declare const linkSelectors: Array<LinkSelector>;
13
13
  declare const defaultHtmlOptions: Omit<HtmlMethodOptions, 'baseUrl'>;
14
14
  declare const defaultHeadersOptions: Omit<HeadersMethodOptions, 'baseUrl'>;
15
15
  declare const defaultGuessOptions: Omit<GuessMethodOptions, 'baseUrl'>;
16
16
  //#endregion
17
- export { anchorLabels, blogrollUrisBalanced, blogrollUrisComprehensive, blogrollUrisMinimal, defaultGuessOptions, defaultHeadersOptions, defaultHtmlOptions, linkSelectors, opmlMimeTypes };
17
+ export { anchorLabels, defaultGuessOptions, defaultHeadersOptions, defaultHtmlOptions, linkSelectors, mimeTypes, urisBalanced, urisComprehensive, urisMinimal };
@@ -4,14 +4,14 @@ import { HtmlMethodOptions } from "../common/uris/html/types.js";
4
4
  import { LinkSelector } from "../common/types.js";
5
5
 
6
6
  //#region src/blogrolls/defaults.d.ts
7
- declare const opmlMimeTypes: string[];
8
- declare const blogrollUrisMinimal: string[];
9
- declare const blogrollUrisBalanced: string[];
10
- declare const blogrollUrisComprehensive: string[];
7
+ declare const mimeTypes: string[];
8
+ declare const urisMinimal: string[];
9
+ declare const urisBalanced: string[];
10
+ declare const urisComprehensive: string[];
11
11
  declare const anchorLabels: string[];
12
12
  declare const linkSelectors: Array<LinkSelector>;
13
13
  declare const defaultHtmlOptions: Omit<HtmlMethodOptions, 'baseUrl'>;
14
14
  declare const defaultHeadersOptions: Omit<HeadersMethodOptions, 'baseUrl'>;
15
15
  declare const defaultGuessOptions: Omit<GuessMethodOptions, 'baseUrl'>;
16
16
  //#endregion
17
- export { anchorLabels, blogrollUrisBalanced, blogrollUrisComprehensive, blogrollUrisMinimal, defaultGuessOptions, defaultHeadersOptions, defaultHtmlOptions, linkSelectors, opmlMimeTypes };
17
+ export { anchorLabels, defaultGuessOptions, defaultHeadersOptions, defaultHtmlOptions, linkSelectors, mimeTypes, urisBalanced, urisComprehensive, urisMinimal };
@@ -1,22 +1,22 @@
1
1
  //#region src/blogrolls/defaults.ts
2
- const opmlMimeTypes = [
2
+ const mimeTypes = [
3
3
  "text/x-opml",
4
4
  "application/xml",
5
5
  "text/xml"
6
6
  ];
7
- const blogrollUrisMinimal = [
7
+ const urisMinimal = [
8
8
  "/.well-known/recommendations.opml",
9
9
  "/blogroll.opml",
10
10
  "/opml.xml"
11
11
  ];
12
- const blogrollUrisBalanced = [
13
- ...blogrollUrisMinimal,
12
+ const urisBalanced = [
13
+ ...urisMinimal,
14
14
  "/blogroll.xml",
15
15
  "/subscriptions.opml",
16
16
  "/recommendations.opml"
17
17
  ];
18
- const blogrollUrisComprehensive = [
19
- ...blogrollUrisBalanced,
18
+ const urisComprehensive = [
19
+ ...urisBalanced,
20
20
  "/links.opml",
21
21
  "/feeds.opml",
22
22
  "/subscriptions.xml"
@@ -29,16 +29,16 @@ const anchorLabels = [
29
29
  ];
30
30
  const linkSelectors = [{ rel: "blogroll" }, {
31
31
  rel: "outline",
32
- types: opmlMimeTypes
32
+ types: mimeTypes
33
33
  }];
34
34
  const defaultHtmlOptions = {
35
35
  linkSelectors,
36
- anchorUris: blogrollUrisComprehensive,
36
+ anchorUris: urisComprehensive,
37
37
  anchorIgnoredUris: [],
38
38
  anchorLabels
39
39
  };
40
40
  const defaultHeadersOptions = { linkSelectors };
41
- const defaultGuessOptions = { uris: blogrollUrisBalanced };
41
+ const defaultGuessOptions = { uris: urisBalanced };
42
42
 
43
43
  //#endregion
44
- export { anchorLabels, blogrollUrisBalanced, blogrollUrisComprehensive, blogrollUrisMinimal, defaultGuessOptions, defaultHeadersOptions, defaultHtmlOptions, linkSelectors, opmlMimeTypes };
44
+ export { anchorLabels, defaultGuessOptions, defaultHeadersOptions, defaultHtmlOptions, linkSelectors, mimeTypes, urisBalanced, urisComprehensive, urisMinimal };
@@ -1,7 +1,7 @@
1
1
  let feedsmith = require("feedsmith");
2
2
 
3
3
  //#region src/blogrolls/extractors.ts
4
- const feedsmithExtractor = async ({ content, url }) => {
4
+ const defaultExtractor = async ({ content, url }) => {
5
5
  if (!content) return {
6
6
  url,
7
7
  isValid: false
@@ -21,4 +21,4 @@ const feedsmithExtractor = async ({ content, url }) => {
21
21
  };
22
22
 
23
23
  //#endregion
24
- exports.feedsmithExtractor = feedsmithExtractor;
24
+ exports.defaultExtractor = defaultExtractor;
@@ -1,7 +1,7 @@
1
1
  import { DiscoverExtractFn } from "../common/types.cjs";
2
- import { BlogrollResultValid } from "./types.cjs";
2
+ import { BlogrollResult } from "./types.cjs";
3
3
 
4
4
  //#region src/blogrolls/extractors.d.ts
5
- declare const feedsmithExtractor: DiscoverExtractFn<BlogrollResultValid>;
5
+ declare const defaultExtractor: DiscoverExtractFn<BlogrollResult>;
6
6
  //#endregion
7
- export { feedsmithExtractor };
7
+ export { defaultExtractor };
@@ -1,7 +1,7 @@
1
1
  import { DiscoverExtractFn } from "../common/types.js";
2
- import { BlogrollResultValid } from "./types.js";
2
+ import { BlogrollResult } from "./types.js";
3
3
 
4
4
  //#region src/blogrolls/extractors.d.ts
5
- declare const feedsmithExtractor: DiscoverExtractFn<BlogrollResultValid>;
5
+ declare const defaultExtractor: DiscoverExtractFn<BlogrollResult>;
6
6
  //#endregion
7
- export { feedsmithExtractor };
7
+ export { defaultExtractor };
@@ -1,7 +1,7 @@
1
1
  import { parseOpml } from "feedsmith";
2
2
 
3
3
  //#region src/blogrolls/extractors.ts
4
- const feedsmithExtractor = async ({ content, url }) => {
4
+ const defaultExtractor = async ({ content, url }) => {
5
5
  if (!content) return {
6
6
  url,
7
7
  isValid: false
@@ -21,4 +21,4 @@ const feedsmithExtractor = async ({ content, url }) => {
21
21
  };
22
22
 
23
23
  //#endregion
24
- export { feedsmithExtractor };
24
+ export { defaultExtractor };
@@ -9,9 +9,10 @@ const discoverBlogrolls = async (input, options) => {
9
9
  return require_index.discover(input, {
10
10
  ...options,
11
11
  fetchFn: options.fetchFn ?? require_adapters.createNativeFetchAdapter(),
12
- extractFn: options.extractFn ?? require_extractors.feedsmithExtractor,
12
+ extractFn: options.extractFn ?? require_extractors.defaultExtractor,
13
13
  normalizeUrlFn: options.normalizeUrlFn ?? require_utils.normalizeUrl
14
14
  }, {
15
+ platform: { handlers: [] },
15
16
  html: require_defaults.defaultHtmlOptions,
16
17
  headers: require_defaults.defaultHeadersOptions,
17
18
  guess: require_defaults.defaultGuessOptions
@@ -1,7 +1,7 @@
1
1
  import { DiscoverInput, DiscoverOptions, DiscoverResult } from "../common/types.cjs";
2
- import { BlogrollResultValid } from "./types.cjs";
2
+ import { BlogrollResult } from "./types.cjs";
3
3
 
4
4
  //#region src/blogrolls/index.d.ts
5
- declare const discoverBlogrolls: <TValid extends BlogrollResultValid = BlogrollResultValid>(input: DiscoverInput, options: DiscoverOptions<TValid>) => Promise<Array<DiscoverResult<TValid>>>;
5
+ declare const discoverBlogrolls: <TValid extends BlogrollResult = BlogrollResult>(input: DiscoverInput, options: DiscoverOptions<TValid>) => Promise<Array<DiscoverResult<TValid>>>;
6
6
  //#endregion
7
7
  export { discoverBlogrolls };
@@ -1,7 +1,7 @@
1
1
  import { DiscoverInput, DiscoverOptions, DiscoverResult } from "../common/types.js";
2
- import { BlogrollResultValid } from "./types.js";
2
+ import { BlogrollResult } from "./types.js";
3
3
 
4
4
  //#region src/blogrolls/index.d.ts
5
- declare const discoverBlogrolls: <TValid extends BlogrollResultValid = BlogrollResultValid>(input: DiscoverInput, options: DiscoverOptions<TValid>) => Promise<Array<DiscoverResult<TValid>>>;
5
+ declare const discoverBlogrolls: <TValid extends BlogrollResult = BlogrollResult>(input: DiscoverInput, options: DiscoverOptions<TValid>) => Promise<Array<DiscoverResult<TValid>>>;
6
6
  //#endregion
7
7
  export { discoverBlogrolls };
@@ -2,16 +2,17 @@ import { createNativeFetchAdapter } from "../common/discover/adapters.js";
2
2
  import { normalizeUrl } from "../common/utils.js";
3
3
  import { discover } from "../common/discover/index.js";
4
4
  import { defaultGuessOptions, defaultHeadersOptions, defaultHtmlOptions } from "./defaults.js";
5
- import { feedsmithExtractor } from "./extractors.js";
5
+ import { defaultExtractor } from "./extractors.js";
6
6
 
7
7
  //#region src/blogrolls/index.ts
8
8
  const discoverBlogrolls = async (input, options) => {
9
9
  return discover(input, {
10
10
  ...options,
11
11
  fetchFn: options.fetchFn ?? createNativeFetchAdapter(),
12
- extractFn: options.extractFn ?? feedsmithExtractor,
12
+ extractFn: options.extractFn ?? defaultExtractor,
13
13
  normalizeUrlFn: options.normalizeUrlFn ?? normalizeUrl
14
14
  }, {
15
+ platform: { handlers: [] },
15
16
  html: defaultHtmlOptions,
16
17
  headers: defaultHeadersOptions,
17
18
  guess: defaultGuessOptions
@@ -1,6 +1,6 @@
1
1
  //#region src/blogrolls/types.d.ts
2
- type BlogrollResultValid = {
2
+ type BlogrollResult = {
3
3
  title?: string;
4
4
  };
5
5
  //#endregion
6
- export { BlogrollResultValid };
6
+ export { BlogrollResult };
@@ -1,6 +1,6 @@
1
1
  //#region src/blogrolls/types.d.ts
2
- type BlogrollResultValid = {
2
+ type BlogrollResult = {
3
3
  title?: string;
4
4
  };
5
5
  //#endregion
6
- export { BlogrollResultValid };
6
+ export { BlogrollResult };
@@ -2,12 +2,12 @@ const require_defaults = require('./blogrolls/defaults.cjs');
2
2
  const require_extractors = require('./blogrolls/extractors.cjs');
3
3
 
4
4
  exports.anchorLabels = require_defaults.anchorLabels;
5
- exports.blogrollUrisBalanced = require_defaults.blogrollUrisBalanced;
6
- exports.blogrollUrisComprehensive = require_defaults.blogrollUrisComprehensive;
7
- exports.blogrollUrisMinimal = require_defaults.blogrollUrisMinimal;
5
+ exports.defaultExtractor = require_extractors.defaultExtractor;
8
6
  exports.defaultGuessOptions = require_defaults.defaultGuessOptions;
9
7
  exports.defaultHeadersOptions = require_defaults.defaultHeadersOptions;
10
8
  exports.defaultHtmlOptions = require_defaults.defaultHtmlOptions;
11
- exports.feedsmithExtractor = require_extractors.feedsmithExtractor;
12
9
  exports.linkSelectors = require_defaults.linkSelectors;
13
- exports.opmlMimeTypes = require_defaults.opmlMimeTypes;
10
+ exports.mimeTypes = require_defaults.mimeTypes;
11
+ exports.urisBalanced = require_defaults.urisBalanced;
12
+ exports.urisComprehensive = require_defaults.urisComprehensive;
13
+ exports.urisMinimal = require_defaults.urisMinimal;
@@ -1,4 +1,4 @@
1
- import { anchorLabels, blogrollUrisBalanced, blogrollUrisComprehensive, blogrollUrisMinimal, defaultGuessOptions, defaultHeadersOptions, defaultHtmlOptions, linkSelectors, opmlMimeTypes } from "./blogrolls/defaults.cjs";
2
- import { BlogrollResultValid } from "./blogrolls/types.cjs";
3
- import { feedsmithExtractor } from "./blogrolls/extractors.cjs";
4
- export { BlogrollResultValid, anchorLabels, blogrollUrisBalanced, blogrollUrisComprehensive, blogrollUrisMinimal, defaultGuessOptions, defaultHeadersOptions, defaultHtmlOptions, feedsmithExtractor, linkSelectors, opmlMimeTypes };
1
+ import { anchorLabels, defaultGuessOptions, defaultHeadersOptions, defaultHtmlOptions, linkSelectors, mimeTypes, urisBalanced, urisComprehensive, urisMinimal } from "./blogrolls/defaults.cjs";
2
+ import { BlogrollResult } from "./blogrolls/types.cjs";
3
+ import { defaultExtractor } from "./blogrolls/extractors.cjs";
4
+ export { BlogrollResult, anchorLabels, defaultExtractor, defaultGuessOptions, defaultHeadersOptions, defaultHtmlOptions, linkSelectors, mimeTypes, urisBalanced, urisComprehensive, urisMinimal };
@@ -1,4 +1,4 @@
1
- import { anchorLabels, blogrollUrisBalanced, blogrollUrisComprehensive, blogrollUrisMinimal, defaultGuessOptions, defaultHeadersOptions, defaultHtmlOptions, linkSelectors, opmlMimeTypes } from "./blogrolls/defaults.js";
2
- import { BlogrollResultValid } from "./blogrolls/types.js";
3
- import { feedsmithExtractor } from "./blogrolls/extractors.js";
4
- export { BlogrollResultValid, anchorLabels, blogrollUrisBalanced, blogrollUrisComprehensive, blogrollUrisMinimal, defaultGuessOptions, defaultHeadersOptions, defaultHtmlOptions, feedsmithExtractor, linkSelectors, opmlMimeTypes };
1
+ import { anchorLabels, defaultGuessOptions, defaultHeadersOptions, defaultHtmlOptions, linkSelectors, mimeTypes, urisBalanced, urisComprehensive, urisMinimal } from "./blogrolls/defaults.js";
2
+ import { BlogrollResult } from "./blogrolls/types.js";
3
+ import { defaultExtractor } from "./blogrolls/extractors.js";
4
+ export { BlogrollResult, anchorLabels, defaultExtractor, defaultGuessOptions, defaultHeadersOptions, defaultHtmlOptions, linkSelectors, mimeTypes, urisBalanced, urisComprehensive, urisMinimal };
package/dist/blogrolls.js CHANGED
@@ -1,4 +1,4 @@
1
- import { anchorLabels, blogrollUrisBalanced, blogrollUrisComprehensive, blogrollUrisMinimal, defaultGuessOptions, defaultHeadersOptions, defaultHtmlOptions, linkSelectors, opmlMimeTypes } from "./blogrolls/defaults.js";
2
- import { feedsmithExtractor } from "./blogrolls/extractors.js";
1
+ import { anchorLabels, defaultGuessOptions, defaultHeadersOptions, defaultHtmlOptions, linkSelectors, mimeTypes, urisBalanced, urisComprehensive, urisMinimal } from "./blogrolls/defaults.js";
2
+ import { defaultExtractor } from "./blogrolls/extractors.js";
3
3
 
4
- export { anchorLabels, blogrollUrisBalanced, blogrollUrisComprehensive, blogrollUrisMinimal, defaultGuessOptions, defaultHeadersOptions, defaultHtmlOptions, feedsmithExtractor, linkSelectors, opmlMimeTypes };
4
+ export { anchorLabels, defaultExtractor, defaultGuessOptions, defaultHeadersOptions, defaultHtmlOptions, linkSelectors, mimeTypes, urisBalanced, urisComprehensive, urisMinimal };
@@ -4,7 +4,7 @@ const require_utils$1 = require('./utils.cjs');
4
4
 
5
5
  //#region src/common/discover/index.ts
6
6
  const discover = async (input, options, defaults) => {
7
- const { methods, fetchFn, extractFn, normalizeUrlFn, concurrency = 3, stopOnFirst = false, includeInvalid = false, onProgress } = options;
7
+ const { methods, fetchFn, extractFn, normalizeUrlFn, concurrency = 3, stopOnFirstResult = false, includeInvalid = false, onProgress } = options;
8
8
  const normalizedInput = await require_utils$1.normalizeInput(input, fetchFn);
9
9
  if (normalizedInput.content) {
10
10
  const result = await extractFn({
@@ -45,7 +45,7 @@ const discover = async (input, options, defaults) => {
45
45
  };
46
46
  await require_utils.processConcurrently(uris, processUri, {
47
47
  concurrency,
48
- shouldStop: () => stopOnFirst && found > 0
48
+ shouldStop: () => stopOnFirstResult && found > 0
49
49
  });
50
50
  return includeInvalid ? results : results.filter((result) => result.isValid);
51
51
  };
@@ -4,7 +4,7 @@ import { normalizeInput, normalizeMethodsConfig } from "./utils.js";
4
4
 
5
5
  //#region src/common/discover/index.ts
6
6
  const discover = async (input, options, defaults) => {
7
- const { methods, fetchFn, extractFn, normalizeUrlFn, concurrency = 3, stopOnFirst = false, includeInvalid = false, onProgress } = options;
7
+ const { methods, fetchFn, extractFn, normalizeUrlFn, concurrency = 3, stopOnFirstResult = false, includeInvalid = false, onProgress } = options;
8
8
  const normalizedInput = await normalizeInput(input, fetchFn);
9
9
  if (normalizedInput.content) {
10
10
  const result = await extractFn({
@@ -45,7 +45,7 @@ const discover = async (input, options, defaults) => {
45
45
  };
46
46
  await processConcurrently(uris, processUri, {
47
47
  concurrency,
48
- shouldStop: () => stopOnFirst && found > 0
48
+ shouldStop: () => stopOnFirstResult && found > 0
49
49
  });
50
50
  return includeInvalid ? results : results.filter((result) => result.isValid);
51
51
  };
@@ -10,18 +10,21 @@ const normalizeInput = async (input, fetchFn) => {
10
10
  headers: response.headers
11
11
  };
12
12
  };
13
- /**
14
- * Normalizes user-facing methods config to internal methods config format.
15
- *
16
- * Handles:
17
- * - Array → object conversion
18
- * - true → {} normalization
19
- * - Merging with default options
20
- * - Building complete method configurations with input data
21
- */
22
13
  const normalizeMethodsConfig = (input, methods, defaults) => {
23
14
  const methodsObj = Array.isArray(methods) ? Object.fromEntries(methods.map((method) => [method, true])) : methods;
24
15
  const methodsConfig = {};
16
+ if (methodsObj.platform) {
17
+ if (!input.url || input.url === "") throw new Error(require_locales.errors.platformMethodRequiresUrl);
18
+ const platformOptions = methodsObj.platform === true ? {} : methodsObj.platform;
19
+ methodsConfig.platform = {
20
+ html: input.content ?? "",
21
+ options: {
22
+ ...defaults.platform,
23
+ ...platformOptions,
24
+ baseUrl: input.url
25
+ }
26
+ };
27
+ }
25
28
  if (methodsObj.html) {
26
29
  if (input.content === void 0) throw new Error(require_locales.errors.htmlMethodRequiresContent);
27
30
  const htmlOptions = methodsObj.html === true ? {} : methodsObj.html;