feedscout 1.7.0 → 1.8.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 (91) hide show
  1. package/dist/common/discover/utils.cjs +11 -8
  2. package/dist/common/discover/utils.js +11 -8
  3. package/dist/common/locales.cjs +11 -4
  4. package/dist/common/locales.js +11 -4
  5. package/dist/common/types.d.cts +5 -4
  6. package/dist/common/types.d.ts +5 -4
  7. package/dist/common/uris/guess/utils.cjs +1 -1
  8. package/dist/common/uris/guess/utils.js +1 -1
  9. package/dist/common/uris/headers/index.cjs +1 -1
  10. package/dist/common/uris/headers/index.js +1 -1
  11. package/dist/common/uris/platform/types.d.cts +2 -2
  12. package/dist/common/uris/platform/types.d.ts +2 -2
  13. package/dist/common/utils.cjs +5 -1
  14. package/dist/common/utils.js +5 -1
  15. package/dist/favicons/defaults.cjs +6 -1
  16. package/dist/favicons/defaults.js +6 -1
  17. package/dist/favicons/platform/handlers/bluesky.cjs +1 -1
  18. package/dist/favicons/platform/handlers/bluesky.js +1 -1
  19. package/dist/favicons/platform/handlers/codeberg.cjs +4 -3
  20. package/dist/favicons/platform/handlers/codeberg.js +4 -3
  21. package/dist/favicons/platform/handlers/devto.cjs +31 -0
  22. package/dist/favicons/platform/handlers/devto.js +31 -0
  23. package/dist/favicons/platform/handlers/github.cjs +4 -3
  24. package/dist/favicons/platform/handlers/github.js +4 -3
  25. package/dist/favicons/platform/handlers/githubGist.cjs +4 -3
  26. package/dist/favicons/platform/handlers/githubGist.js +4 -3
  27. package/dist/favicons/platform/handlers/gitlab.cjs +38 -0
  28. package/dist/favicons/platform/handlers/gitlab.js +38 -0
  29. package/dist/favicons/platform/handlers/lobsters.cjs +3 -3
  30. package/dist/favicons/platform/handlers/lobsters.js +3 -3
  31. package/dist/favicons/platform/handlers/mastodon.cjs +5 -3
  32. package/dist/favicons/platform/handlers/mastodon.js +5 -3
  33. package/dist/favicons/platform/handlers/reddit.cjs +10 -8
  34. package/dist/favicons/platform/handlers/reddit.js +10 -8
  35. package/dist/feeds/defaults.cjs +4 -2
  36. package/dist/feeds/defaults.js +4 -2
  37. package/dist/feeds/platform/handlers/applePodcasts.cjs +26 -0
  38. package/dist/feeds/platform/handlers/applePodcasts.js +26 -0
  39. package/dist/feeds/platform/handlers/behance.cjs +2 -2
  40. package/dist/feeds/platform/handlers/behance.js +2 -2
  41. package/dist/feeds/platform/handlers/dailymotion.cjs +4 -4
  42. package/dist/feeds/platform/handlers/dailymotion.js +4 -4
  43. package/dist/feeds/platform/handlers/devto.cjs +6 -4
  44. package/dist/feeds/platform/handlers/devto.js +5 -5
  45. package/dist/feeds/platform/handlers/gitlab.cjs +4 -0
  46. package/dist/feeds/platform/handlers/gitlab.js +1 -1
  47. package/dist/feeds/platform/handlers/hatenablog.cjs +4 -4
  48. package/dist/feeds/platform/handlers/hatenablog.js +4 -4
  49. package/dist/feeds/platform/handlers/itchio.cjs +10 -10
  50. package/dist/feeds/platform/handlers/itchio.js +10 -10
  51. package/dist/feeds/platform/handlers/lobsters.cjs +8 -8
  52. package/dist/feeds/platform/handlers/lobsters.js +8 -8
  53. package/dist/feeds/platform/handlers/mastodon.cjs +4 -4
  54. package/dist/feeds/platform/handlers/mastodon.js +4 -4
  55. package/dist/feeds/platform/handlers/medium.cjs +10 -10
  56. package/dist/feeds/platform/handlers/medium.js +10 -10
  57. package/dist/feeds/platform/handlers/paragraph.cjs +2 -2
  58. package/dist/feeds/platform/handlers/paragraph.js +2 -2
  59. package/dist/feeds/platform/handlers/producthunt.cjs +4 -4
  60. package/dist/feeds/platform/handlers/producthunt.js +4 -4
  61. package/dist/feeds/platform/handlers/reddit.cjs +10 -10
  62. package/dist/feeds/platform/handlers/reddit.js +10 -10
  63. package/dist/feeds/platform/handlers/stackExchange.cjs +6 -6
  64. package/dist/feeds/platform/handlers/stackExchange.js +6 -6
  65. package/dist/feeds/platform/handlers/steam.cjs +4 -4
  66. package/dist/feeds/platform/handlers/steam.js +4 -4
  67. package/dist/feeds/platform/handlers/substack.cjs +10 -3
  68. package/dist/feeds/platform/handlers/substack.js +11 -4
  69. package/dist/feeds/platform/handlers/tumblr.cjs +2 -2
  70. package/dist/feeds/platform/handlers/tumblr.js +2 -2
  71. package/dist/feeds/platform/handlers/v2ex.cjs +4 -4
  72. package/dist/feeds/platform/handlers/v2ex.js +4 -4
  73. package/dist/feeds/platform/handlers/vimeo.cjs +2 -2
  74. package/dist/feeds/platform/handlers/vimeo.js +2 -2
  75. package/dist/feeds/platform/handlers/wordpress.cjs +6 -6
  76. package/dist/feeds/platform/handlers/wordpress.js +6 -6
  77. package/dist/feeds/platform/handlers/ximalaya.cjs +2 -2
  78. package/dist/feeds/platform/handlers/ximalaya.js +2 -2
  79. package/dist/feeds/platform/handlers/youtube.cjs +36 -21
  80. package/dist/feeds/platform/handlers/youtube.js +36 -21
  81. package/dist/hubs/discover/index.cjs +1 -2
  82. package/dist/hubs/discover/index.js +1 -2
  83. package/dist/hubs/feed/index.cjs +4 -4
  84. package/dist/hubs/feed/index.js +4 -4
  85. package/dist/hubs/headers/index.cjs +2 -2
  86. package/dist/hubs/headers/index.js +2 -2
  87. package/dist/hubs/html/index.cjs +2 -2
  88. package/dist/hubs/html/index.js +2 -2
  89. package/package.json +4 -3
  90. package/dist/hubs/discover/utils.cjs +0 -18
  91. package/dist/hubs/discover/utils.js +0 -18
@@ -16,11 +16,11 @@ const sorts = [
16
16
  "top-sellers",
17
17
  "on-sale"
18
18
  ];
19
- const byUserPathRegex = /^\/games\/by-([^/]+)/;
20
- const tagPathRegex = /^\/games\/tag-([^/]+)/;
21
- const sortPathRegex = /^\/games\/([^/.]+)/;
22
- const sectionPathRegex = /^\/([^/.]+)/;
23
- const gamePathRegex = /^\/([^/]+)/;
19
+ const byUserRegex = /^\/games\/by-([^/]+)/;
20
+ const tagRegex = /^\/games\/tag-([^/]+)/;
21
+ const sortRegex = /^\/games\/([^/.]+)/;
22
+ const sectionRegex = /^\/([^/.]+)/;
23
+ const gameRegex = /^\/([^/]+)/;
24
24
  const itchioHandler = {
25
25
  match: (url) => {
26
26
  return isHostOf(url, mainHosts) || isSubdomainOf(url, "itch.io");
@@ -30,7 +30,7 @@ const itchioHandler = {
30
30
  const lowerHostname = hostname.toLowerCase();
31
31
  if (!mainHosts.includes(lowerHostname) && lowerHostname.endsWith(".itch.io")) {
32
32
  const creator = lowerHostname.replace(".itch.io", "");
33
- const gameMatch = pathname.match(gamePathRegex);
33
+ const gameMatch = pathname.match(gameRegex);
34
34
  if (gameMatch?.[1]) return [{
35
35
  uri: `https://${creator}.itch.io/${gameMatch[1]}/devlog.rss`,
36
36
  hint: composeHint("itchio:devlog")
@@ -40,17 +40,17 @@ const itchioHandler = {
40
40
  hint: composeHint("itchio:games")
41
41
  }];
42
42
  }
43
- const byUserMatch = pathname.match(byUserPathRegex);
43
+ const byUserMatch = pathname.match(byUserRegex);
44
44
  if (byUserMatch?.[1]) return [{
45
45
  uri: `https://itch.io/games/by-${byUserMatch[1]}.xml`,
46
46
  hint: composeHint("itchio:games")
47
47
  }];
48
- const tagMatch = pathname.match(tagPathRegex);
48
+ const tagMatch = pathname.match(tagRegex);
49
49
  if (tagMatch?.[1]) return [{
50
50
  uri: `https://itch.io/games/tag-${tagMatch[1]}.xml`,
51
51
  hint: composeHint("itchio:tag")
52
52
  }];
53
- const sortMatch = pathname.match(sortPathRegex);
53
+ const sortMatch = pathname.match(sortRegex);
54
54
  if (sortMatch?.[1] && isAnyOf(sortMatch[1], sorts)) return [{
55
55
  uri: `https://itch.io/games/${sortMatch[1]}.xml`,
56
56
  hint: composeHint("itchio:games")
@@ -63,7 +63,7 @@ const itchioHandler = {
63
63
  uri: "https://itch.io/devlogs.xml",
64
64
  hint: composeHint("itchio:devlog")
65
65
  }];
66
- const sectionMatch = pathname.match(sectionPathRegex);
66
+ const sectionMatch = pathname.match(sectionRegex);
67
67
  if (sectionMatch?.[1] && isAnyOf(sectionMatch[1], sections)) return [{
68
68
  uri: `https://itch.io/${sectionMatch[1]}.xml`,
69
69
  hint: composeHint("itchio:section")
@@ -1,32 +1,32 @@
1
1
  const require_utils = require("../../../common/utils.cjs");
2
2
  //#region src/feeds/platform/handlers/lobsters.ts
3
3
  const hosts = ["lobste.rs"];
4
- const tagPathRegex = /^\/t\/([a-zA-Z0-9,_-]+)/;
5
- const domainPathRegex = /^\/domains\/([^/]+)/;
6
- const userPathRegex = /^\/~([a-zA-Z0-9_-]+)/;
7
- const topPathRegex = /^\/top(?:\/(1d|3d|1w|1m|1y))?\/?$/;
4
+ const tagRegex = /^\/t\/([a-zA-Z0-9,_-]+)/;
5
+ const domainRegex = /^\/domains\/([^/]+)/;
6
+ const userRegex = /^\/~([a-zA-Z0-9_-]+)/;
7
+ const topRegex = /^\/top(?:\/(1d|3d|1w|1m|1y))?\/?$/;
8
8
  const lobstersHandler = {
9
9
  match: (url) => {
10
10
  return require_utils.isHostOf(url, hosts);
11
11
  },
12
12
  resolve: (url) => {
13
13
  const { pathname } = new URL(url);
14
- const tagMatch = pathname.match(tagPathRegex);
14
+ const tagMatch = pathname.match(tagRegex);
15
15
  if (tagMatch?.[1]) return [{
16
16
  uri: `https://lobste.rs/t/${tagMatch[1]}.rss`,
17
17
  hint: require_utils.composeHint("lobsters:tag")
18
18
  }];
19
- const domainMatch = pathname.match(domainPathRegex);
19
+ const domainMatch = pathname.match(domainRegex);
20
20
  if (domainMatch?.[1]) return [{
21
21
  uri: `https://lobste.rs/domains/${domainMatch[1]}.rss`,
22
22
  hint: require_utils.composeHint("lobsters:domain")
23
23
  }];
24
- const userMatch = pathname.match(userPathRegex);
24
+ const userMatch = pathname.match(userRegex);
25
25
  if (userMatch?.[1]) return [{
26
26
  uri: `https://lobste.rs/~${userMatch[1]}/stories.rss`,
27
27
  hint: require_utils.composeHint("lobsters:stories")
28
28
  }];
29
- const topMatch = pathname.match(topPathRegex);
29
+ const topMatch = pathname.match(topRegex);
30
30
  if (topMatch) {
31
31
  const period = topMatch[1];
32
32
  if (period) return [{
@@ -1,32 +1,32 @@
1
1
  import { composeHint, isHostOf } from "../../../common/utils.js";
2
2
  //#region src/feeds/platform/handlers/lobsters.ts
3
3
  const hosts = ["lobste.rs"];
4
- const tagPathRegex = /^\/t\/([a-zA-Z0-9,_-]+)/;
5
- const domainPathRegex = /^\/domains\/([^/]+)/;
6
- const userPathRegex = /^\/~([a-zA-Z0-9_-]+)/;
7
- const topPathRegex = /^\/top(?:\/(1d|3d|1w|1m|1y))?\/?$/;
4
+ const tagRegex = /^\/t\/([a-zA-Z0-9,_-]+)/;
5
+ const domainRegex = /^\/domains\/([^/]+)/;
6
+ const userRegex = /^\/~([a-zA-Z0-9_-]+)/;
7
+ const topRegex = /^\/top(?:\/(1d|3d|1w|1m|1y))?\/?$/;
8
8
  const lobstersHandler = {
9
9
  match: (url) => {
10
10
  return isHostOf(url, hosts);
11
11
  },
12
12
  resolve: (url) => {
13
13
  const { pathname } = new URL(url);
14
- const tagMatch = pathname.match(tagPathRegex);
14
+ const tagMatch = pathname.match(tagRegex);
15
15
  if (tagMatch?.[1]) return [{
16
16
  uri: `https://lobste.rs/t/${tagMatch[1]}.rss`,
17
17
  hint: composeHint("lobsters:tag")
18
18
  }];
19
- const domainMatch = pathname.match(domainPathRegex);
19
+ const domainMatch = pathname.match(domainRegex);
20
20
  if (domainMatch?.[1]) return [{
21
21
  uri: `https://lobste.rs/domains/${domainMatch[1]}.rss`,
22
22
  hint: composeHint("lobsters:domain")
23
23
  }];
24
- const userMatch = pathname.match(userPathRegex);
24
+ const userMatch = pathname.match(userRegex);
25
25
  if (userMatch?.[1]) return [{
26
26
  uri: `https://lobste.rs/~${userMatch[1]}/stories.rss`,
27
27
  hint: composeHint("lobsters:stories")
28
28
  }];
29
- const topMatch = pathname.match(topPathRegex);
29
+ const topMatch = pathname.match(topRegex);
30
30
  if (topMatch) {
31
31
  const period = topMatch[1];
32
32
  if (period) return [{
@@ -1,8 +1,8 @@
1
1
  const require_utils = require("../../../common/utils.cjs");
2
2
  const require_mastodon = require("../../../favicons/platform/handlers/mastodon.cjs");
3
3
  //#region src/feeds/platform/handlers/mastodon.ts
4
- const profilePathRegex = /^\/@([^/]+)/;
5
- const tagPathRegex = /^\/tags\/([^/]+)/;
4
+ const profileRegex = /^\/@([^/]+)/;
5
+ const tagRegex = /^\/tags\/([^/]+)/;
6
6
  const isProfilePath = (pathname) => {
7
7
  const segments = pathname.split("/").filter(Boolean);
8
8
  return segments.length > 0 && segments[0].startsWith("@");
@@ -24,12 +24,12 @@ const mastodonHandler = {
24
24
  resolve: (url) => {
25
25
  try {
26
26
  const { origin, pathname } = new URL(url);
27
- const userMatch = pathname.match(profilePathRegex);
27
+ const userMatch = pathname.match(profileRegex);
28
28
  if (userMatch?.[1]) return [{
29
29
  uri: `${origin}/@${userMatch[1]}.rss`,
30
30
  hint: require_utils.composeHint("mastodon:posts")
31
31
  }];
32
- const tagMatch = pathname.match(tagPathRegex);
32
+ const tagMatch = pathname.match(tagRegex);
33
33
  if (tagMatch?.[1]) return [{
34
34
  uri: `${origin}/tags/${tagMatch[1]}.rss`,
35
35
  hint: require_utils.composeHint("mastodon:tag")
@@ -1,8 +1,8 @@
1
1
  import { composeHint } from "../../../common/utils.js";
2
2
  import { isMastodonHeaders, isMastodonHtml } from "../../../favicons/platform/handlers/mastodon.js";
3
3
  //#region src/feeds/platform/handlers/mastodon.ts
4
- const profilePathRegex = /^\/@([^/]+)/;
5
- const tagPathRegex = /^\/tags\/([^/]+)/;
4
+ const profileRegex = /^\/@([^/]+)/;
5
+ const tagRegex = /^\/tags\/([^/]+)/;
6
6
  const isProfilePath = (pathname) => {
7
7
  const segments = pathname.split("/").filter(Boolean);
8
8
  return segments.length > 0 && segments[0].startsWith("@");
@@ -24,12 +24,12 @@ const mastodonHandler = {
24
24
  resolve: (url) => {
25
25
  try {
26
26
  const { origin, pathname } = new URL(url);
27
- const userMatch = pathname.match(profilePathRegex);
27
+ const userMatch = pathname.match(profileRegex);
28
28
  if (userMatch?.[1]) return [{
29
29
  uri: `${origin}/@${userMatch[1]}.rss`,
30
30
  hint: composeHint("mastodon:posts")
31
31
  }];
32
- const tagMatch = pathname.match(tagPathRegex);
32
+ const tagMatch = pathname.match(tagRegex);
33
33
  if (tagMatch?.[1]) return [{
34
34
  uri: `${origin}/tags/${tagMatch[1]}.rss`,
35
35
  hint: composeHint("mastodon:tag")
@@ -1,10 +1,10 @@
1
1
  const require_utils = require("../../../common/utils.cjs");
2
2
  //#region src/feeds/platform/handlers/medium.ts
3
- const userPathRegex = /^\/@([^/]+)/;
4
- const tagPathRegex = /^\/tag\/([^/]+)/;
5
- const publicationTagPathRegex = /^\/([^/@][^/]+)\/tagged\/([^/]+)/;
6
- const publicationPathRegex = /^\/([^/@][^/]+)/;
7
- const subdomainTagPathRegex = /^\/tagged\/([^/]+)/;
3
+ const userRegex = /^\/@([^/]+)/;
4
+ const tagRegex = /^\/tag\/([^/]+)/;
5
+ const publicationTagRegex = /^\/([^/@][^/]+)\/tagged\/([^/]+)/;
6
+ const publicationRegex = /^\/([^/@][^/]+)/;
7
+ const subdomainTagRegex = /^\/tagged\/([^/]+)/;
8
8
  const hosts = ["medium.com", "www.medium.com"];
9
9
  const excludedPaths = [
10
10
  "search",
@@ -21,17 +21,17 @@ const mediumHandler = {
21
21
  const { hostname, pathname } = new URL(url);
22
22
  const lowerHostname = hostname.toLowerCase();
23
23
  if (hosts.includes(lowerHostname)) {
24
- const userMatch = pathname.match(userPathRegex);
24
+ const userMatch = pathname.match(userRegex);
25
25
  if (userMatch?.[1]) return [{
26
26
  uri: `https://medium.com/feed/@${userMatch[1]}`,
27
27
  hint: require_utils.composeHint("medium:posts")
28
28
  }];
29
- const tagMatch = pathname.match(tagPathRegex);
29
+ const tagMatch = pathname.match(tagRegex);
30
30
  if (tagMatch?.[1]) return [{
31
31
  uri: `https://medium.com/feed/tag/${tagMatch[1]}`,
32
32
  hint: require_utils.composeHint("medium:tag")
33
33
  }];
34
- const pubTagMatch = pathname.match(publicationTagPathRegex);
34
+ const pubTagMatch = pathname.match(publicationTagRegex);
35
35
  if (pubTagMatch?.[1] && pubTagMatch?.[2]) {
36
36
  const publication = pubTagMatch[1];
37
37
  const tag = pubTagMatch[2];
@@ -40,7 +40,7 @@ const mediumHandler = {
40
40
  hint: require_utils.composeHint("medium:tagged")
41
41
  }];
42
42
  }
43
- const pubMatch = pathname.match(publicationPathRegex);
43
+ const pubMatch = pathname.match(publicationRegex);
44
44
  if (pubMatch?.[1]) {
45
45
  const publication = pubMatch[1];
46
46
  if (!require_utils.isAnyOf(publication, excludedPaths)) return [{
@@ -51,7 +51,7 @@ const mediumHandler = {
51
51
  }
52
52
  if (lowerHostname.endsWith(".medium.com") && lowerHostname !== "medium.com" && lowerHostname !== "www.medium.com") {
53
53
  const subdomain = lowerHostname.replace(".medium.com", "");
54
- const tagMatch = pathname.match(subdomainTagPathRegex);
54
+ const tagMatch = pathname.match(subdomainTagRegex);
55
55
  if (tagMatch?.[1]) return [{
56
56
  uri: `https://medium.com/feed/${subdomain}/tagged/${tagMatch[1]}`,
57
57
  hint: require_utils.composeHint("medium:tagged")
@@ -1,10 +1,10 @@
1
1
  import { composeHint, isAnyOf, isHostOf, isSubdomainOf } from "../../../common/utils.js";
2
2
  //#region src/feeds/platform/handlers/medium.ts
3
- const userPathRegex = /^\/@([^/]+)/;
4
- const tagPathRegex = /^\/tag\/([^/]+)/;
5
- const publicationTagPathRegex = /^\/([^/@][^/]+)\/tagged\/([^/]+)/;
6
- const publicationPathRegex = /^\/([^/@][^/]+)/;
7
- const subdomainTagPathRegex = /^\/tagged\/([^/]+)/;
3
+ const userRegex = /^\/@([^/]+)/;
4
+ const tagRegex = /^\/tag\/([^/]+)/;
5
+ const publicationTagRegex = /^\/([^/@][^/]+)\/tagged\/([^/]+)/;
6
+ const publicationRegex = /^\/([^/@][^/]+)/;
7
+ const subdomainTagRegex = /^\/tagged\/([^/]+)/;
8
8
  const hosts = ["medium.com", "www.medium.com"];
9
9
  const excludedPaths = [
10
10
  "search",
@@ -21,17 +21,17 @@ const mediumHandler = {
21
21
  const { hostname, pathname } = new URL(url);
22
22
  const lowerHostname = hostname.toLowerCase();
23
23
  if (hosts.includes(lowerHostname)) {
24
- const userMatch = pathname.match(userPathRegex);
24
+ const userMatch = pathname.match(userRegex);
25
25
  if (userMatch?.[1]) return [{
26
26
  uri: `https://medium.com/feed/@${userMatch[1]}`,
27
27
  hint: composeHint("medium:posts")
28
28
  }];
29
- const tagMatch = pathname.match(tagPathRegex);
29
+ const tagMatch = pathname.match(tagRegex);
30
30
  if (tagMatch?.[1]) return [{
31
31
  uri: `https://medium.com/feed/tag/${tagMatch[1]}`,
32
32
  hint: composeHint("medium:tag")
33
33
  }];
34
- const pubTagMatch = pathname.match(publicationTagPathRegex);
34
+ const pubTagMatch = pathname.match(publicationTagRegex);
35
35
  if (pubTagMatch?.[1] && pubTagMatch?.[2]) {
36
36
  const publication = pubTagMatch[1];
37
37
  const tag = pubTagMatch[2];
@@ -40,7 +40,7 @@ const mediumHandler = {
40
40
  hint: composeHint("medium:tagged")
41
41
  }];
42
42
  }
43
- const pubMatch = pathname.match(publicationPathRegex);
43
+ const pubMatch = pathname.match(publicationRegex);
44
44
  if (pubMatch?.[1]) {
45
45
  const publication = pubMatch[1];
46
46
  if (!isAnyOf(publication, excludedPaths)) return [{
@@ -51,7 +51,7 @@ const mediumHandler = {
51
51
  }
52
52
  if (lowerHostname.endsWith(".medium.com") && lowerHostname !== "medium.com" && lowerHostname !== "www.medium.com") {
53
53
  const subdomain = lowerHostname.replace(".medium.com", "");
54
- const tagMatch = pathname.match(subdomainTagPathRegex);
54
+ const tagMatch = pathname.match(subdomainTagRegex);
55
55
  if (tagMatch?.[1]) return [{
56
56
  uri: `https://medium.com/feed/${subdomain}/tagged/${tagMatch[1]}`,
57
57
  hint: composeHint("medium:tagged")
@@ -1,14 +1,14 @@
1
1
  const require_utils = require("../../../common/utils.cjs");
2
2
  //#region src/feeds/platform/handlers/paragraph.ts
3
3
  const hosts = ["paragraph.com", "www.paragraph.com"];
4
- const userPathRegex = /^\/@([^/]+)/;
4
+ const userRegex = /^\/@([^/]+)/;
5
5
  const paragraphHandler = {
6
6
  match: (url) => {
7
7
  return require_utils.isHostOf(url, hosts);
8
8
  },
9
9
  resolve: (url) => {
10
10
  const { pathname } = new URL(url);
11
- const userMatch = pathname.match(userPathRegex);
11
+ const userMatch = pathname.match(userRegex);
12
12
  if (!userMatch?.[1]) return [];
13
13
  const username = userMatch[1];
14
14
  return [{
@@ -1,14 +1,14 @@
1
1
  import { composeHint, isHostOf } from "../../../common/utils.js";
2
2
  //#region src/feeds/platform/handlers/paragraph.ts
3
3
  const hosts = ["paragraph.com", "www.paragraph.com"];
4
- const userPathRegex = /^\/@([^/]+)/;
4
+ const userRegex = /^\/@([^/]+)/;
5
5
  const paragraphHandler = {
6
6
  match: (url) => {
7
7
  return isHostOf(url, hosts);
8
8
  },
9
9
  resolve: (url) => {
10
10
  const { pathname } = new URL(url);
11
- const userMatch = pathname.match(userPathRegex);
11
+ const userMatch = pathname.match(userRegex);
12
12
  if (!userMatch?.[1]) return [];
13
13
  const username = userMatch[1];
14
14
  return [{
@@ -1,20 +1,20 @@
1
1
  const require_utils = require("../../../common/utils.cjs");
2
2
  //#region src/feeds/platform/handlers/producthunt.ts
3
3
  const hosts = ["producthunt.com", "www.producthunt.com"];
4
- const topicPathRegex = /^\/topics\/([a-zA-Z0-9_-]+)/;
5
- const categoryPathRegex = /^\/categories\/([a-zA-Z0-9_-]+)/;
4
+ const topicRegex = /^\/topics\/([a-zA-Z0-9_-]+)/;
5
+ const categoryRegex = /^\/categories\/([a-zA-Z0-9_-]+)/;
6
6
  const producthuntHandler = {
7
7
  match: (url) => {
8
8
  return require_utils.isHostOf(url, hosts);
9
9
  },
10
10
  resolve: (url) => {
11
11
  const { pathname } = new URL(url);
12
- const topicMatch = pathname.match(topicPathRegex);
12
+ const topicMatch = pathname.match(topicRegex);
13
13
  if (topicMatch?.[1]) return [{
14
14
  uri: `https://www.producthunt.com/feed?topic=${topicMatch[1]}`,
15
15
  hint: require_utils.composeHint("producthunt:topic")
16
16
  }];
17
- const categoryMatch = pathname.match(categoryPathRegex);
17
+ const categoryMatch = pathname.match(categoryRegex);
18
18
  if (categoryMatch?.[1]) return [{
19
19
  uri: `https://www.producthunt.com/feed?category=${categoryMatch[1]}`,
20
20
  hint: require_utils.composeHint("producthunt:category")
@@ -1,20 +1,20 @@
1
1
  import { composeHint, isHostOf } from "../../../common/utils.js";
2
2
  //#region src/feeds/platform/handlers/producthunt.ts
3
3
  const hosts = ["producthunt.com", "www.producthunt.com"];
4
- const topicPathRegex = /^\/topics\/([a-zA-Z0-9_-]+)/;
5
- const categoryPathRegex = /^\/categories\/([a-zA-Z0-9_-]+)/;
4
+ const topicRegex = /^\/topics\/([a-zA-Z0-9_-]+)/;
5
+ const categoryRegex = /^\/categories\/([a-zA-Z0-9_-]+)/;
6
6
  const producthuntHandler = {
7
7
  match: (url) => {
8
8
  return isHostOf(url, hosts);
9
9
  },
10
10
  resolve: (url) => {
11
11
  const { pathname } = new URL(url);
12
- const topicMatch = pathname.match(topicPathRegex);
12
+ const topicMatch = pathname.match(topicRegex);
13
13
  if (topicMatch?.[1]) return [{
14
14
  uri: `https://www.producthunt.com/feed?topic=${topicMatch[1]}`,
15
15
  hint: composeHint("producthunt:topic")
16
16
  }];
17
- const categoryMatch = pathname.match(categoryPathRegex);
17
+ const categoryMatch = pathname.match(categoryRegex);
18
18
  if (categoryMatch?.[1]) return [{
19
19
  uri: `https://www.producthunt.com/feed?category=${categoryMatch[1]}`,
20
20
  hint: composeHint("producthunt:category")
@@ -1,10 +1,10 @@
1
1
  const require_utils = require("../../../common/utils.cjs");
2
2
  //#region src/feeds/platform/handlers/reddit.ts
3
- const commentsPathRegex = /^\/r\/([^/]+)\/comments\/([^/]+)/;
4
- const subredditPathRegex = /^\/r\/([^/]+)(?:\/([^/]+))?/;
5
- const multiredditPathRegex = /^\/user\/([^/]+)\/m\/([^/]+)/;
6
- const userPathRegex = /^\/(u|user)\/([^/]+)/;
7
- const domainPathRegex = /^\/domain\/([^/]+)/;
3
+ const commentsRegex = /^\/r\/([^/]+)\/comments\/([^/]+)/;
4
+ const subredditRegex = /^\/r\/([^/]+)(?:\/([^/]+))?/;
5
+ const multiredditRegex = /^\/user\/([^/]+)\/m\/([^/]+)/;
6
+ const userRegex = /^\/(u|user)\/([^/]+)/;
7
+ const domainRegex = /^\/domain\/([^/]+)/;
8
8
  const hosts = [
9
9
  "reddit.com",
10
10
  "www.reddit.com",
@@ -28,12 +28,12 @@ const redditHandler = {
28
28
  uri: "https://www.reddit.com/.rss",
29
29
  hint: require_utils.composeHint("reddit:posts")
30
30
  }];
31
- const commentsMatch = pathname.match(commentsPathRegex);
31
+ const commentsMatch = pathname.match(commentsRegex);
32
32
  if (commentsMatch?.[1] && commentsMatch?.[2]) return [{
33
33
  uri: `https://www.reddit.com/r/${commentsMatch[1]}/comments/${commentsMatch[2]}/.rss`,
34
34
  hint: require_utils.composeHint("reddit:post-comments")
35
35
  }];
36
- const subredditMatch = pathname.match(subredditPathRegex);
36
+ const subredditMatch = pathname.match(subredditRegex);
37
37
  if (subredditMatch?.[1]) {
38
38
  const subreddit = subredditMatch[1];
39
39
  const sort = subredditMatch[2];
@@ -52,17 +52,17 @@ const redditHandler = {
52
52
  });
53
53
  return uris;
54
54
  }
55
- const multiredditMatch = pathname.match(multiredditPathRegex);
55
+ const multiredditMatch = pathname.match(multiredditRegex);
56
56
  if (multiredditMatch?.[1] && multiredditMatch?.[2]) return [{
57
57
  uri: `https://www.reddit.com/user/${multiredditMatch[1]}/m/${multiredditMatch[2]}/.rss`,
58
58
  hint: require_utils.composeHint("reddit:multireddit")
59
59
  }];
60
- const userMatch = pathname.match(userPathRegex);
60
+ const userMatch = pathname.match(userRegex);
61
61
  if (userMatch?.[2]) return [{
62
62
  uri: `https://www.reddit.com/user/${userMatch[2]}/.rss`,
63
63
  hint: require_utils.composeHint("reddit:posts")
64
64
  }];
65
- const domainMatch = pathname.match(domainPathRegex);
65
+ const domainMatch = pathname.match(domainRegex);
66
66
  if (domainMatch?.[1]) return [{
67
67
  uri: `https://www.reddit.com/domain/${domainMatch[1]}/.rss`,
68
68
  hint: require_utils.composeHint("reddit:posts")
@@ -1,10 +1,10 @@
1
1
  import { composeHint, isAnyOf, isHostOf } from "../../../common/utils.js";
2
2
  //#region src/feeds/platform/handlers/reddit.ts
3
- const commentsPathRegex = /^\/r\/([^/]+)\/comments\/([^/]+)/;
4
- const subredditPathRegex = /^\/r\/([^/]+)(?:\/([^/]+))?/;
5
- const multiredditPathRegex = /^\/user\/([^/]+)\/m\/([^/]+)/;
6
- const userPathRegex = /^\/(u|user)\/([^/]+)/;
7
- const domainPathRegex = /^\/domain\/([^/]+)/;
3
+ const commentsRegex = /^\/r\/([^/]+)\/comments\/([^/]+)/;
4
+ const subredditRegex = /^\/r\/([^/]+)(?:\/([^/]+))?/;
5
+ const multiredditRegex = /^\/user\/([^/]+)\/m\/([^/]+)/;
6
+ const userRegex = /^\/(u|user)\/([^/]+)/;
7
+ const domainRegex = /^\/domain\/([^/]+)/;
8
8
  const hosts = [
9
9
  "reddit.com",
10
10
  "www.reddit.com",
@@ -28,12 +28,12 @@ const redditHandler = {
28
28
  uri: "https://www.reddit.com/.rss",
29
29
  hint: composeHint("reddit:posts")
30
30
  }];
31
- const commentsMatch = pathname.match(commentsPathRegex);
31
+ const commentsMatch = pathname.match(commentsRegex);
32
32
  if (commentsMatch?.[1] && commentsMatch?.[2]) return [{
33
33
  uri: `https://www.reddit.com/r/${commentsMatch[1]}/comments/${commentsMatch[2]}/.rss`,
34
34
  hint: composeHint("reddit:post-comments")
35
35
  }];
36
- const subredditMatch = pathname.match(subredditPathRegex);
36
+ const subredditMatch = pathname.match(subredditRegex);
37
37
  if (subredditMatch?.[1]) {
38
38
  const subreddit = subredditMatch[1];
39
39
  const sort = subredditMatch[2];
@@ -52,17 +52,17 @@ const redditHandler = {
52
52
  });
53
53
  return uris;
54
54
  }
55
- const multiredditMatch = pathname.match(multiredditPathRegex);
55
+ const multiredditMatch = pathname.match(multiredditRegex);
56
56
  if (multiredditMatch?.[1] && multiredditMatch?.[2]) return [{
57
57
  uri: `https://www.reddit.com/user/${multiredditMatch[1]}/m/${multiredditMatch[2]}/.rss`,
58
58
  hint: composeHint("reddit:multireddit")
59
59
  }];
60
- const userMatch = pathname.match(userPathRegex);
60
+ const userMatch = pathname.match(userRegex);
61
61
  if (userMatch?.[2]) return [{
62
62
  uri: `https://www.reddit.com/user/${userMatch[2]}/.rss`,
63
63
  hint: composeHint("reddit:posts")
64
64
  }];
65
- const domainMatch = pathname.match(domainPathRegex);
65
+ const domainMatch = pathname.match(domainRegex);
66
66
  if (domainMatch?.[1]) return [{
67
67
  uri: `https://www.reddit.com/domain/${domainMatch[1]}/.rss`,
68
68
  hint: composeHint("reddit:posts")
@@ -1,8 +1,8 @@
1
1
  const require_utils = require("../../../common/utils.cjs");
2
2
  //#region src/feeds/platform/handlers/stackExchange.ts
3
- const tagPathRegex = /^\/questions\/tagged\/([\w.+-]+)/;
4
- const questionPathRegex = /^\/questions\/(\d+)/;
5
- const userPathRegex = /^\/users\/(\d+)/;
3
+ const tagRegex = /^\/questions\/tagged\/([\w.+-]+)/;
4
+ const questionRegex = /^\/questions\/(\d+)/;
5
+ const userRegex = /^\/users\/(\d+)/;
6
6
  const domains = [
7
7
  "stackoverflow.com",
8
8
  "serverfault.com",
@@ -18,17 +18,17 @@ const stackExchangeHandler = {
18
18
  },
19
19
  resolve: (url) => {
20
20
  const { origin, pathname } = new URL(url);
21
- const tagMatch = pathname.match(tagPathRegex);
21
+ const tagMatch = pathname.match(tagRegex);
22
22
  if (tagMatch?.[1]) return [{
23
23
  uri: `${origin}/feeds/tag/${tagMatch[1]}`,
24
24
  hint: require_utils.composeHint("stackexchange:tag")
25
25
  }];
26
- const questionMatch = pathname.match(questionPathRegex);
26
+ const questionMatch = pathname.match(questionRegex);
27
27
  if (questionMatch?.[1]) return [{
28
28
  uri: `${origin}/feeds/question/${questionMatch[1]}`,
29
29
  hint: require_utils.composeHint("stackexchange:question")
30
30
  }];
31
- const userMatch = pathname.match(userPathRegex);
31
+ const userMatch = pathname.match(userRegex);
32
32
  if (userMatch?.[1]) return [{
33
33
  uri: `${origin}/feeds/user/${userMatch[1]}`,
34
34
  hint: require_utils.composeHint("stackexchange:user")
@@ -1,8 +1,8 @@
1
1
  import { composeHint, isHostOf, isSubdomainOf } from "../../../common/utils.js";
2
2
  //#region src/feeds/platform/handlers/stackExchange.ts
3
- const tagPathRegex = /^\/questions\/tagged\/([\w.+-]+)/;
4
- const questionPathRegex = /^\/questions\/(\d+)/;
5
- const userPathRegex = /^\/users\/(\d+)/;
3
+ const tagRegex = /^\/questions\/tagged\/([\w.+-]+)/;
4
+ const questionRegex = /^\/questions\/(\d+)/;
5
+ const userRegex = /^\/users\/(\d+)/;
6
6
  const domains = [
7
7
  "stackoverflow.com",
8
8
  "serverfault.com",
@@ -18,17 +18,17 @@ const stackExchangeHandler = {
18
18
  },
19
19
  resolve: (url) => {
20
20
  const { origin, pathname } = new URL(url);
21
- const tagMatch = pathname.match(tagPathRegex);
21
+ const tagMatch = pathname.match(tagRegex);
22
22
  if (tagMatch?.[1]) return [{
23
23
  uri: `${origin}/feeds/tag/${tagMatch[1]}`,
24
24
  hint: composeHint("stackexchange:tag")
25
25
  }];
26
- const questionMatch = pathname.match(questionPathRegex);
26
+ const questionMatch = pathname.match(questionRegex);
27
27
  if (questionMatch?.[1]) return [{
28
28
  uri: `${origin}/feeds/question/${questionMatch[1]}`,
29
29
  hint: composeHint("stackexchange:question")
30
30
  }];
31
- const userMatch = pathname.match(userPathRegex);
31
+ const userMatch = pathname.match(userRegex);
32
32
  if (userMatch?.[1]) return [{
33
33
  uri: `${origin}/feeds/user/${userMatch[1]}`,
34
34
  hint: composeHint("stackexchange:user")
@@ -1,7 +1,7 @@
1
1
  const require_utils = require("../../../common/utils.cjs");
2
2
  //#region src/feeds/platform/handlers/steam.ts
3
- const appPathRegex = /^\/(?:news\/)?app\/(\d+)/;
4
- const groupPathRegex = /^\/groups\/([^/]+)/;
3
+ const appRegex = /^\/(?:news\/)?app\/(\d+)/;
4
+ const groupRegex = /^\/groups\/([^/]+)/;
5
5
  const hosts = ["store.steampowered.com", "steamcommunity.com"];
6
6
  const steamHandler = {
7
7
  match: (url) => {
@@ -9,13 +9,13 @@ const steamHandler = {
9
9
  },
10
10
  resolve: (url) => {
11
11
  const { hostname, pathname } = new URL(url);
12
- const appMatch = pathname.match(appPathRegex);
12
+ const appMatch = pathname.match(appRegex);
13
13
  if (appMatch?.[1]) return [{
14
14
  uri: `https://store.steampowered.com/feeds/news/app/${appMatch[1]}/`,
15
15
  hint: require_utils.composeHint("steam:news")
16
16
  }];
17
17
  if (hostname === "steamcommunity.com") {
18
- const groupMatch = pathname.match(groupPathRegex);
18
+ const groupMatch = pathname.match(groupRegex);
19
19
  if (groupMatch?.[1]) return [{
20
20
  uri: `https://steamcommunity.com/groups/${groupMatch[1]}/rss`,
21
21
  hint: require_utils.composeHint("steam:group")
@@ -1,7 +1,7 @@
1
1
  import { composeHint, isHostOf } from "../../../common/utils.js";
2
2
  //#region src/feeds/platform/handlers/steam.ts
3
- const appPathRegex = /^\/(?:news\/)?app\/(\d+)/;
4
- const groupPathRegex = /^\/groups\/([^/]+)/;
3
+ const appRegex = /^\/(?:news\/)?app\/(\d+)/;
4
+ const groupRegex = /^\/groups\/([^/]+)/;
5
5
  const hosts = ["store.steampowered.com", "steamcommunity.com"];
6
6
  const steamHandler = {
7
7
  match: (url) => {
@@ -9,13 +9,13 @@ const steamHandler = {
9
9
  },
10
10
  resolve: (url) => {
11
11
  const { hostname, pathname } = new URL(url);
12
- const appMatch = pathname.match(appPathRegex);
12
+ const appMatch = pathname.match(appRegex);
13
13
  if (appMatch?.[1]) return [{
14
14
  uri: `https://store.steampowered.com/feeds/news/app/${appMatch[1]}/`,
15
15
  hint: composeHint("steam:news")
16
16
  }];
17
17
  if (hostname === "steamcommunity.com") {
18
- const groupMatch = pathname.match(groupPathRegex);
18
+ const groupMatch = pathname.match(groupRegex);
19
19
  if (groupMatch?.[1]) return [{
20
20
  uri: `https://steamcommunity.com/groups/${groupMatch[1]}/rss`,
21
21
  hint: composeHint("steam:group")
@@ -1,13 +1,20 @@
1
1
  const require_utils = require("../../../common/utils.cjs");
2
2
  //#region src/feeds/platform/handlers/substack.ts
3
+ const profileRegex = /^\/@([\w-]+)/;
3
4
  const substackHandler = {
4
5
  match: (url) => {
5
- return require_utils.isSubdomainOf(url, "substack.com");
6
+ if (require_utils.isSubdomainOf(url, "substack.com")) return true;
7
+ return require_utils.isHostOf(url, "substack.com") && profileRegex.test(new URL(url).pathname);
6
8
  },
7
9
  resolve: (url) => {
8
- const { origin } = new URL(url);
10
+ const parsed = new URL(url);
11
+ const profileMatch = parsed.pathname.match(profileRegex);
12
+ if (require_utils.isHostOf(url, "substack.com") && profileMatch?.[1]) return [{
13
+ uri: `https://${profileMatch[1]}.substack.com/feed`,
14
+ hint: require_utils.composeHint("substack:newsletter")
15
+ }];
9
16
  return [{
10
- uri: `${origin}/feed`,
17
+ uri: `${parsed.origin}/feed`,
11
18
  hint: require_utils.composeHint("substack:newsletter")
12
19
  }];
13
20
  }