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
@@ -17,12 +17,15 @@ const defaultFetchFn = async (url, options) => {
17
17
  };
18
18
  const normalizeInput = async (input, fetchFn) => {
19
19
  if (typeof input === "object") return input;
20
- const response = await fetchFn(input);
21
- return {
22
- url: response.url,
23
- content: typeof response.body === "string" ? response.body : "",
24
- headers: response.headers
25
- };
20
+ try {
21
+ const response = await fetchFn(input);
22
+ return {
23
+ url: response.url,
24
+ content: typeof response.body === "string" ? response.body : void 0,
25
+ headers: response.headers
26
+ };
27
+ } catch {}
28
+ return { url: input };
26
29
  };
27
30
  const getLinkOfType = (links, rel) => {
28
31
  return links?.find((link) => link.rel === rel);
@@ -48,11 +51,11 @@ const defaultResolveSiteUrlFn = (input) => {
48
51
  const normalizeUriEntry = (entry, resolveUrlFn, baseUrl) => {
49
52
  if (typeof entry.uri === "string") return {
50
53
  ...entry,
51
- uri: resolveUrlFn(entry.uri, baseUrl)
54
+ uri: resolveUrlFn(entry.uri, baseUrl) ?? entry.uri
52
55
  };
53
56
  return {
54
57
  ...entry,
55
- uri: entry.uri.map((uri) => resolveUrlFn(uri, baseUrl))
58
+ uri: entry.uri.map((uri) => resolveUrlFn(uri, baseUrl) ?? uri)
56
59
  };
57
60
  };
58
61
  const normalizeMethodsConfig = (sourceInput, siteInput, methods, defaults) => {
@@ -17,12 +17,15 @@ const defaultFetchFn = async (url, options) => {
17
17
  };
18
18
  const normalizeInput = async (input, fetchFn) => {
19
19
  if (typeof input === "object") return input;
20
- const response = await fetchFn(input);
21
- return {
22
- url: response.url,
23
- content: typeof response.body === "string" ? response.body : "",
24
- headers: response.headers
25
- };
20
+ try {
21
+ const response = await fetchFn(input);
22
+ return {
23
+ url: response.url,
24
+ content: typeof response.body === "string" ? response.body : void 0,
25
+ headers: response.headers
26
+ };
27
+ } catch {}
28
+ return { url: input };
26
29
  };
27
30
  const getLinkOfType = (links, rel) => {
28
31
  return links?.find((link) => link.rel === rel);
@@ -48,11 +51,11 @@ const defaultResolveSiteUrlFn = (input) => {
48
51
  const normalizeUriEntry = (entry, resolveUrlFn, baseUrl) => {
49
52
  if (typeof entry.uri === "string") return {
50
53
  ...entry,
51
- uri: resolveUrlFn(entry.uri, baseUrl)
54
+ uri: resolveUrlFn(entry.uri, baseUrl) ?? entry.uri
52
55
  };
53
56
  return {
54
57
  ...entry,
55
- uri: entry.uri.map((uri) => resolveUrlFn(uri, baseUrl))
58
+ uri: entry.uri.map((uri) => resolveUrlFn(uri, baseUrl) ?? uri)
56
59
  };
57
60
  };
58
61
  const normalizeMethodsConfig = (sourceInput, siteInput, methods, defaults) => {
@@ -8,9 +8,15 @@ var errors = {
8
8
  };
9
9
  var hints = {
10
10
  "youtube:all": "All uploads",
11
- "youtube:videos": "Videos only",
12
- "youtube:shorts": "Shorts only",
13
- "youtube:live": "Live streams only",
11
+ "youtube:videos": "Videos",
12
+ "youtube:shorts": "Shorts",
13
+ "youtube:live": "Live streams",
14
+ "youtube:popular-videos": "Popular videos",
15
+ "youtube:popular-shorts": "Popular shorts",
16
+ "youtube:popular-live": "Popular live streams",
17
+ "youtube:member-videos": "Member videos",
18
+ "youtube:member-shorts": "Member shorts",
19
+ "youtube:member-live": "Member live streams",
14
20
  "youtube:playlist": "Playlist",
15
21
  "github:activity": "Activity",
16
22
  "github:releases": "Releases",
@@ -120,7 +126,8 @@ var hints = {
120
126
  "v2ex:tab": "Tab",
121
127
  "ximalaya:album": "Album",
122
128
  "lemmy:community": "Community",
123
- "lemmy:user": "User"
129
+ "lemmy:user": "User",
130
+ "apple-podcasts:podcast": "Podcast"
124
131
  };
125
132
  //#endregion
126
133
  Object.defineProperty(exports, "errors", {
@@ -8,9 +8,15 @@ var errors = {
8
8
  };
9
9
  var hints = {
10
10
  "youtube:all": "All uploads",
11
- "youtube:videos": "Videos only",
12
- "youtube:shorts": "Shorts only",
13
- "youtube:live": "Live streams only",
11
+ "youtube:videos": "Videos",
12
+ "youtube:shorts": "Shorts",
13
+ "youtube:live": "Live streams",
14
+ "youtube:popular-videos": "Popular videos",
15
+ "youtube:popular-shorts": "Popular shorts",
16
+ "youtube:popular-live": "Popular live streams",
17
+ "youtube:member-videos": "Member videos",
18
+ "youtube:member-shorts": "Member shorts",
19
+ "youtube:member-live": "Member live streams",
14
20
  "youtube:playlist": "Playlist",
15
21
  "github:activity": "Activity",
16
22
  "github:releases": "Releases",
@@ -120,7 +126,8 @@ var hints = {
120
126
  "v2ex:tab": "Tab",
121
127
  "ximalaya:album": "Album",
122
128
  "lemmy:community": "Community",
123
- "lemmy:user": "User"
129
+ "lemmy:user": "User",
130
+ "apple-podcasts:podcast": "Podcast"
124
131
  };
125
132
  //#endregion
126
133
  export { errors, hints };
@@ -5,6 +5,7 @@ import { HtmlMethodOptions } from "./uris/html/types.cjs";
5
5
  import { PlatformMethodOptions } from "./uris/platform/types.cjs";
6
6
 
7
7
  //#region src/common/types.d.ts
8
+ type MaybePromise<T> = T | Promise<T>;
8
9
  type UriEntry = string | Array<string>;
9
10
  type DiscoverUriHint = {
10
11
  key: string;
@@ -20,7 +21,7 @@ type LinkSelector = {
20
21
  rel: string;
21
22
  types?: Array<string>;
22
23
  };
23
- type DiscoverResolveUrlFn = (url: string, baseUrl: string | undefined) => string;
24
+ type DiscoverResolveUrlFn = (url: string, baseUrl: string | undefined) => string | undefined;
24
25
  type DiscoverResolveSiteUrlFn = (input: DiscoverInputObject) => string | undefined;
25
26
  type DiscoverFetchFnOptions = {
26
27
  method?: 'GET' | 'HEAD';
@@ -33,7 +34,7 @@ type DiscoverFetchFnResponse = {
33
34
  status: number;
34
35
  statusText: string;
35
36
  };
36
- type DiscoverFetchFn = (url: string, options?: DiscoverFetchFnOptions) => Promise<DiscoverFetchFnResponse>;
37
+ type DiscoverFetchFn = (url: string, options?: DiscoverFetchFnOptions) => MaybePromise<DiscoverFetchFnResponse>;
37
38
  type DiscoverProgress = {
38
39
  tested: number;
39
40
  total: number;
@@ -58,7 +59,7 @@ type DiscoverExtractFn<TValid> = (input: {
58
59
  content: string;
59
60
  headers?: Headers;
60
61
  status?: number;
61
- }) => Promise<DiscoverResult<TValid>> | DiscoverResult<TValid>;
62
+ }) => MaybePromise<DiscoverResult<TValid>>;
62
63
  type DiscoverInputObject = {
63
64
  url: string;
64
65
  content?: string;
@@ -85,4 +86,4 @@ type DiscoverOptions<TValid, TMethods extends DiscoverMethod = DiscoverMethod> =
85
86
  includeInvalid?: boolean;
86
87
  };
87
88
  //#endregion
88
- export { DiscoverExtractFn, DiscoverFetchFn, DiscoverFetchFnOptions, DiscoverFetchFnResponse, DiscoverInput, DiscoverInputObject, DiscoverMethod, DiscoverMethodsConfig, DiscoverOnProgressFn, DiscoverOptions, DiscoverProgress, DiscoverResolveSiteUrlFn, DiscoverResolveUrlFn, DiscoverResult, DiscoverUriEntry, DiscoverUriHint, LinkSelector, UriEntry };
89
+ export { DiscoverExtractFn, DiscoverFetchFn, DiscoverFetchFnOptions, DiscoverFetchFnResponse, DiscoverInput, DiscoverInputObject, DiscoverMethod, DiscoverMethodsConfig, DiscoverOnProgressFn, DiscoverOptions, DiscoverProgress, DiscoverResolveSiteUrlFn, DiscoverResolveUrlFn, DiscoverResult, DiscoverUriEntry, DiscoverUriHint, LinkSelector, MaybePromise, UriEntry };
@@ -5,6 +5,7 @@ import { HtmlMethodOptions } from "./uris/html/types.js";
5
5
  import { PlatformMethodOptions } from "./uris/platform/types.js";
6
6
 
7
7
  //#region src/common/types.d.ts
8
+ type MaybePromise<T> = T | Promise<T>;
8
9
  type UriEntry = string | Array<string>;
9
10
  type DiscoverUriHint = {
10
11
  key: string;
@@ -20,7 +21,7 @@ type LinkSelector = {
20
21
  rel: string;
21
22
  types?: Array<string>;
22
23
  };
23
- type DiscoverResolveUrlFn = (url: string, baseUrl: string | undefined) => string;
24
+ type DiscoverResolveUrlFn = (url: string, baseUrl: string | undefined) => string | undefined;
24
25
  type DiscoverResolveSiteUrlFn = (input: DiscoverInputObject) => string | undefined;
25
26
  type DiscoverFetchFnOptions = {
26
27
  method?: 'GET' | 'HEAD';
@@ -33,7 +34,7 @@ type DiscoverFetchFnResponse = {
33
34
  status: number;
34
35
  statusText: string;
35
36
  };
36
- type DiscoverFetchFn = (url: string, options?: DiscoverFetchFnOptions) => Promise<DiscoverFetchFnResponse>;
37
+ type DiscoverFetchFn = (url: string, options?: DiscoverFetchFnOptions) => MaybePromise<DiscoverFetchFnResponse>;
37
38
  type DiscoverProgress = {
38
39
  tested: number;
39
40
  total: number;
@@ -58,7 +59,7 @@ type DiscoverExtractFn<TValid> = (input: {
58
59
  content: string;
59
60
  headers?: Headers;
60
61
  status?: number;
61
- }) => Promise<DiscoverResult<TValid>> | DiscoverResult<TValid>;
62
+ }) => MaybePromise<DiscoverResult<TValid>>;
62
63
  type DiscoverInputObject = {
63
64
  url: string;
64
65
  content?: string;
@@ -85,4 +86,4 @@ type DiscoverOptions<TValid, TMethods extends DiscoverMethod = DiscoverMethod> =
85
86
  includeInvalid?: boolean;
86
87
  };
87
88
  //#endregion
88
- export { DiscoverExtractFn, DiscoverFetchFn, DiscoverFetchFnOptions, DiscoverFetchFnResponse, DiscoverInput, DiscoverInputObject, DiscoverMethod, DiscoverMethodsConfig, DiscoverOnProgressFn, DiscoverOptions, DiscoverProgress, DiscoverResolveSiteUrlFn, DiscoverResolveUrlFn, DiscoverResult, DiscoverUriEntry, DiscoverUriHint, LinkSelector, UriEntry };
89
+ export { DiscoverExtractFn, DiscoverFetchFn, DiscoverFetchFnOptions, DiscoverFetchFnResponse, DiscoverInput, DiscoverInputObject, DiscoverMethod, DiscoverMethodsConfig, DiscoverOnProgressFn, DiscoverOptions, DiscoverProgress, DiscoverResolveSiteUrlFn, DiscoverResolveUrlFn, DiscoverResult, DiscoverUriEntry, DiscoverUriHint, LinkSelector, MaybePromise, UriEntry };
@@ -3,7 +3,7 @@ const ipAddressRegex = /^\d+\.\d+\.\d+\.\d+$/;
3
3
  const resolveUri = (uri, base, origin, pathname) => {
4
4
  if (uri.startsWith("/")) return `${origin}${uri}`;
5
5
  if (uri.startsWith("?")) return `${origin}${pathname}${uri}`;
6
- return new URL(uri, base).toString();
6
+ return new URL(uri, base).href;
7
7
  };
8
8
  const generateUrlCombinations = (baseUrls, uris) => {
9
9
  return baseUrls.flatMap((base) => {
@@ -3,7 +3,7 @@ const ipAddressRegex = /^\d+\.\d+\.\d+\.\d+$/;
3
3
  const resolveUri = (uri, base, origin, pathname) => {
4
4
  if (uri.startsWith("/")) return `${origin}${uri}`;
5
5
  if (uri.startsWith("?")) return `${origin}${pathname}${uri}`;
6
- return new URL(uri, base).toString();
6
+ return new URL(uri, base).href;
7
7
  };
8
8
  const generateUrlCombinations = (baseUrls, uris) => {
9
9
  return baseUrls.flatMap((base) => {
@@ -1,9 +1,9 @@
1
1
  const require_utils = require("../../utils.cjs");
2
2
  //#region src/common/uris/headers/index.ts
3
+ const linkSplitRegex = /,(?=\s*<)/;
3
4
  const urlRegex = /<([^<>]+)>/;
4
5
  const relRegex = /rel\s*=\s*["']?([^"';,]+)["']?/i;
5
6
  const typeRegex = /type\s*=\s*["']?([^"';,]+)["']?/i;
6
- const linkSplitRegex = /,(?=\s*<)/;
7
7
  const discoverUrisFromHeaders = (headers, options) => {
8
8
  const uris = /* @__PURE__ */ new Set();
9
9
  const linkHeader = headers.get("link");
@@ -1,9 +1,9 @@
1
1
  import { matchesAnyOfLinkSelectors } from "../../utils.js";
2
2
  //#region src/common/uris/headers/index.ts
3
+ const linkSplitRegex = /,(?=\s*<)/;
3
4
  const urlRegex = /<([^<>]+)>/;
4
5
  const relRegex = /rel\s*=\s*["']?([^"';,]+)["']?/i;
5
6
  const typeRegex = /type\s*=\s*["']?([^"';,]+)["']?/i;
6
- const linkSplitRegex = /,(?=\s*<)/;
7
7
  const discoverUrisFromHeaders = (headers, options) => {
8
8
  const uris = /* @__PURE__ */ new Set();
9
9
  const linkHeader = headers.get("link");
@@ -1,9 +1,9 @@
1
- import { DiscoverFetchFn, DiscoverUriEntry } from "../../types.cjs";
1
+ import { DiscoverFetchFn, DiscoverUriEntry, MaybePromise } from "../../types.cjs";
2
2
 
3
3
  //#region src/common/uris/platform/types.d.ts
4
4
  type PlatformHandler = {
5
5
  match: (url: string, content?: string, headers?: Headers) => boolean;
6
- resolve: (url: string, content?: string, headers?: Headers, fetchFn?: DiscoverFetchFn) => Array<DiscoverUriEntry> | Promise<Array<DiscoverUriEntry>>;
6
+ resolve: (url: string, content?: string, headers?: Headers, fetchFn?: DiscoverFetchFn) => MaybePromise<Array<DiscoverUriEntry>>;
7
7
  };
8
8
  type PlatformMethodOptions = {
9
9
  baseUrl: string;
@@ -1,9 +1,9 @@
1
- import { DiscoverFetchFn, DiscoverUriEntry } from "../../types.js";
1
+ import { DiscoverFetchFn, DiscoverUriEntry, MaybePromise } from "../../types.js";
2
2
 
3
3
  //#region src/common/uris/platform/types.d.ts
4
4
  type PlatformHandler = {
5
5
  match: (url: string, content?: string, headers?: Headers) => boolean;
6
- resolve: (url: string, content?: string, headers?: Headers, fetchFn?: DiscoverFetchFn) => Array<DiscoverUriEntry> | Promise<Array<DiscoverUriEntry>>;
6
+ resolve: (url: string, content?: string, headers?: Headers, fetchFn?: DiscoverFetchFn) => MaybePromise<Array<DiscoverUriEntry>>;
7
7
  };
8
8
  type PlatformMethodOptions = {
9
9
  baseUrl: string;
@@ -53,7 +53,11 @@ const omitEmpty = (array) => {
53
53
  return result;
54
54
  };
55
55
  const resolveUrl = (url, baseUrl) => {
56
- return baseUrl ? new URL(url, baseUrl).href : url;
56
+ try {
57
+ return new URL(url, baseUrl).href;
58
+ } catch {
59
+ return;
60
+ }
57
61
  };
58
62
  const matchesAnyOfLinkSelectors = (rel, type, selectors) => {
59
63
  return selectors.some((selector) => {
@@ -53,7 +53,11 @@ const omitEmpty = (array) => {
53
53
  return result;
54
54
  };
55
55
  const resolveUrl = (url, baseUrl) => {
56
- return baseUrl ? new URL(url, baseUrl).href : url;
56
+ try {
57
+ return new URL(url, baseUrl).href;
58
+ } catch {
59
+ return;
60
+ }
57
61
  };
58
62
  const matchesAnyOfLinkSelectors = (rel, type, selectors) => {
59
63
  return selectors.some((selector) => {
@@ -2,8 +2,10 @@ const require_utils = require("../common/utils.cjs");
2
2
  const require_bluesky = require("./platform/handlers/bluesky.cjs");
3
3
  const require_codeberg = require("./platform/handlers/codeberg.cjs");
4
4
  const require_deviantart = require("./platform/handlers/deviantart.cjs");
5
+ const require_devto = require("./platform/handlers/devto.cjs");
5
6
  const require_github = require("./platform/handlers/github.cjs");
6
7
  const require_githubGist = require("./platform/handlers/githubGist.cjs");
8
+ const require_gitlab = require("./platform/handlers/gitlab.cjs");
7
9
  const require_lobsters = require("./platform/handlers/lobsters.cjs");
8
10
  const require_mastodon = require("./platform/handlers/mastodon.cjs");
9
11
  const require_reddit = require("./platform/handlers/reddit.cjs");
@@ -13,6 +15,7 @@ const require_tumblr = require("./platform/handlers/tumblr.cjs");
13
15
  const defaultIconRels = [
14
16
  "icon",
15
17
  "shortcut",
18
+ "alternate icon",
16
19
  "apple-touch-icon",
17
20
  "apple-touch-icon-precomposed"
18
21
  ];
@@ -40,6 +43,7 @@ const defaultGuessOptions = { uris: defaultGuessPaths };
40
43
  const defaultPlatformOptions = { handlers: [
41
44
  require_github.githubHandler,
42
45
  require_githubGist.githubGistHandler,
46
+ require_gitlab.gitlabHandler,
43
47
  require_mastodon.mastodonHandler,
44
48
  require_bluesky.blueskyHandler,
45
49
  require_reddit.redditHandler,
@@ -47,7 +51,8 @@ const defaultPlatformOptions = { handlers: [
47
51
  require_codeberg.codebergHandler,
48
52
  require_lobsters.lobstersHandler,
49
53
  require_sourceforge.sourceforgeHandler,
50
- require_deviantart.deviantartHandler
54
+ require_deviantart.deviantartHandler,
55
+ require_devto.devtoHandler
51
56
  ] };
52
57
  //#endregion
53
58
  exports.defaultFeedOptions = defaultFeedOptions;
@@ -2,8 +2,10 @@ import { omitEmpty } from "../common/utils.js";
2
2
  import { blueskyHandler } from "./platform/handlers/bluesky.js";
3
3
  import { codebergHandler } from "./platform/handlers/codeberg.js";
4
4
  import { deviantartHandler } from "./platform/handlers/deviantart.js";
5
+ import { devtoHandler } from "./platform/handlers/devto.js";
5
6
  import { githubHandler } from "./platform/handlers/github.js";
6
7
  import { githubGistHandler } from "./platform/handlers/githubGist.js";
8
+ import { gitlabHandler } from "./platform/handlers/gitlab.js";
7
9
  import { lobstersHandler } from "./platform/handlers/lobsters.js";
8
10
  import { mastodonHandler } from "./platform/handlers/mastodon.js";
9
11
  import { redditHandler } from "./platform/handlers/reddit.js";
@@ -13,6 +15,7 @@ import { tumblrHandler } from "./platform/handlers/tumblr.js";
13
15
  const defaultIconRels = [
14
16
  "icon",
15
17
  "shortcut",
18
+ "alternate icon",
16
19
  "apple-touch-icon",
17
20
  "apple-touch-icon-precomposed"
18
21
  ];
@@ -40,6 +43,7 @@ const defaultGuessOptions = { uris: defaultGuessPaths };
40
43
  const defaultPlatformOptions = { handlers: [
41
44
  githubHandler,
42
45
  githubGistHandler,
46
+ gitlabHandler,
43
47
  mastodonHandler,
44
48
  blueskyHandler,
45
49
  redditHandler,
@@ -47,7 +51,8 @@ const defaultPlatformOptions = { handlers: [
47
51
  codebergHandler,
48
52
  lobstersHandler,
49
53
  sourceforgeHandler,
50
- deviantartHandler
54
+ deviantartHandler,
55
+ devtoHandler
51
56
  ] };
52
57
  //#endregion
53
58
  export { defaultFeedOptions, defaultGuessOptions, defaultGuessPaths, defaultHeadersOptions, defaultHtmlOptions, defaultIconRels, defaultPlatformOptions, linkSelectors };
@@ -9,7 +9,7 @@ const isProfilePath = (pathname) => {
9
9
  const blueskyHandler = {
10
10
  match: (url) => {
11
11
  try {
12
- return require_utils.isHostOf(url, hosts) && isProfilePath(new URL(url).pathname);
12
+ return isProfilePath(new URL(url).pathname) && require_utils.isHostOf(url, hosts);
13
13
  } catch {}
14
14
  return false;
15
15
  },
@@ -9,7 +9,7 @@ const isProfilePath = (pathname) => {
9
9
  const blueskyHandler = {
10
10
  match: (url) => {
11
11
  try {
12
- return isHostOf(url, hosts) && isProfilePath(new URL(url).pathname);
12
+ return isProfilePath(new URL(url).pathname) && isHostOf(url, hosts);
13
13
  } catch {}
14
14
  return false;
15
15
  },
@@ -1,15 +1,16 @@
1
1
  const require_utils = require("../../../common/utils.cjs");
2
2
  const require_codeberg = require("../../../feeds/platform/handlers/codeberg.cjs");
3
3
  //#region src/favicons/platform/handlers/codeberg.ts
4
+ const userRegex = /^\/([^/.]+)/;
4
5
  const codebergHandler = {
5
6
  match: (url) => {
6
7
  return require_utils.isHostOf(url, require_codeberg.hosts);
7
8
  },
8
9
  resolve: (url) => {
9
10
  const { origin, pathname } = new URL(url);
10
- const segments = pathname.split("/").filter(Boolean);
11
- if (segments.length === 0) return [];
12
- const username = segments[0];
11
+ const match = pathname.match(userRegex);
12
+ if (!match?.[1]) return [];
13
+ const username = match[1];
13
14
  if (require_utils.isAnyOf(username, require_codeberg.excludedPaths)) return [];
14
15
  return [{ uri: `${origin}/user/avatar/${username}/512` }];
15
16
  }
@@ -1,15 +1,16 @@
1
1
  import { isAnyOf, isHostOf } from "../../../common/utils.js";
2
2
  import { excludedPaths, hosts } from "../../../feeds/platform/handlers/codeberg.js";
3
3
  //#region src/favicons/platform/handlers/codeberg.ts
4
+ const userRegex = /^\/([^/.]+)/;
4
5
  const codebergHandler = {
5
6
  match: (url) => {
6
7
  return isHostOf(url, hosts);
7
8
  },
8
9
  resolve: (url) => {
9
10
  const { origin, pathname } = new URL(url);
10
- const segments = pathname.split("/").filter(Boolean);
11
- if (segments.length === 0) return [];
12
- const username = segments[0];
11
+ const match = pathname.match(userRegex);
12
+ if (!match?.[1]) return [];
13
+ const username = match[1];
13
14
  if (isAnyOf(username, excludedPaths)) return [];
14
15
  return [{ uri: `${origin}/user/avatar/${username}/512` }];
15
16
  }
@@ -0,0 +1,31 @@
1
+ const require_utils = require("../../../common/utils.cjs");
2
+ const require_utils$1 = require("../../utils.cjs");
3
+ const require_devto = require("../../../feeds/platform/handlers/devto.cjs");
4
+ //#region src/favicons/platform/handlers/devto.ts
5
+ const userRegex = /^\/([^/.]+)/;
6
+ const devtoHandler = {
7
+ match: (url) => {
8
+ try {
9
+ const { pathname } = new URL(url);
10
+ const match = pathname.match(userRegex);
11
+ if (!require_utils.isHostOf(url, require_devto.hosts) || !match?.[1]) return false;
12
+ if (match[1] === "t") return false;
13
+ return !require_utils.isAnyOf(match[1], require_devto.excludedPaths);
14
+ } catch {}
15
+ return false;
16
+ },
17
+ resolve: async (url, _content, _headers, fetchFn) => {
18
+ if (!fetchFn) return [];
19
+ try {
20
+ const { pathname } = new URL(url);
21
+ const match = pathname.match(userRegex);
22
+ if (!match?.[1] || match[1] === "t") return [];
23
+ const username = match[1];
24
+ const profileImage = require_utils$1.parseBodyJson((await fetchFn(`https://dev.to/api/users/by_username?url=${encodeURIComponent(username)}`)).body)?.profile_image;
25
+ if (require_utils$1.isNonEmptyString(profileImage)) return [{ uri: profileImage }];
26
+ } catch {}
27
+ return [];
28
+ }
29
+ };
30
+ //#endregion
31
+ exports.devtoHandler = devtoHandler;
@@ -0,0 +1,31 @@
1
+ import { isAnyOf, isHostOf } from "../../../common/utils.js";
2
+ import { isNonEmptyString, parseBodyJson } from "../../utils.js";
3
+ import { excludedPaths, hosts } from "../../../feeds/platform/handlers/devto.js";
4
+ //#region src/favicons/platform/handlers/devto.ts
5
+ const userRegex = /^\/([^/.]+)/;
6
+ const devtoHandler = {
7
+ match: (url) => {
8
+ try {
9
+ const { pathname } = new URL(url);
10
+ const match = pathname.match(userRegex);
11
+ if (!isHostOf(url, hosts) || !match?.[1]) return false;
12
+ if (match[1] === "t") return false;
13
+ return !isAnyOf(match[1], excludedPaths);
14
+ } catch {}
15
+ return false;
16
+ },
17
+ resolve: async (url, _content, _headers, fetchFn) => {
18
+ if (!fetchFn) return [];
19
+ try {
20
+ const { pathname } = new URL(url);
21
+ const match = pathname.match(userRegex);
22
+ if (!match?.[1] || match[1] === "t") return [];
23
+ const username = match[1];
24
+ const profileImage = parseBodyJson((await fetchFn(`https://dev.to/api/users/by_username?url=${encodeURIComponent(username)}`)).body)?.profile_image;
25
+ if (isNonEmptyString(profileImage)) return [{ uri: profileImage }];
26
+ } catch {}
27
+ return [];
28
+ }
29
+ };
30
+ //#endregion
31
+ export { devtoHandler };
@@ -1,15 +1,16 @@
1
1
  const require_utils = require("../../../common/utils.cjs");
2
2
  const require_github = require("../../../feeds/platform/handlers/github.cjs");
3
3
  //#region src/favicons/platform/handlers/github.ts
4
+ const userRegex = /^\/([^/.]+)/;
4
5
  const githubHandler = {
5
6
  match: (url) => {
6
7
  return require_utils.isHostOf(url, require_github.hosts);
7
8
  },
8
9
  resolve: (url) => {
9
10
  const { pathname } = new URL(url);
10
- const segments = pathname.split("/").filter(Boolean);
11
- if (segments.length === 0) return [];
12
- const user = segments[0];
11
+ const match = pathname.match(userRegex);
12
+ if (!match?.[1]) return [];
13
+ const user = match[1];
13
14
  if (require_utils.isAnyOf(user, require_github.excludedPaths)) return [];
14
15
  return [{ uri: `https://github.com/${user}.png` }];
15
16
  }
@@ -1,15 +1,16 @@
1
1
  import { isAnyOf, isHostOf } from "../../../common/utils.js";
2
2
  import { excludedPaths, hosts } from "../../../feeds/platform/handlers/github.js";
3
3
  //#region src/favicons/platform/handlers/github.ts
4
+ const userRegex = /^\/([^/.]+)/;
4
5
  const githubHandler = {
5
6
  match: (url) => {
6
7
  return isHostOf(url, hosts);
7
8
  },
8
9
  resolve: (url) => {
9
10
  const { pathname } = new URL(url);
10
- const segments = pathname.split("/").filter(Boolean);
11
- if (segments.length === 0) return [];
12
- const user = segments[0];
11
+ const match = pathname.match(userRegex);
12
+ if (!match?.[1]) return [];
13
+ const user = match[1];
13
14
  if (isAnyOf(user, excludedPaths)) return [];
14
15
  return [{ uri: `https://github.com/${user}.png` }];
15
16
  }
@@ -1,15 +1,16 @@
1
1
  const require_utils = require("../../../common/utils.cjs");
2
2
  const require_githubGist = require("../../../feeds/platform/handlers/githubGist.cjs");
3
3
  //#region src/favicons/platform/handlers/githubGist.ts
4
+ const userRegex = /^\/([^/.]+)/;
4
5
  const githubGistHandler = {
5
6
  match: (url) => {
6
7
  return require_utils.isHostOf(url, require_githubGist.hosts);
7
8
  },
8
9
  resolve: (url) => {
9
10
  const { pathname } = new URL(url);
10
- const segments = pathname.split("/").filter(Boolean);
11
- if (segments.length === 0) return [];
12
- const user = segments[0];
11
+ const match = pathname.match(userRegex);
12
+ if (!match?.[1]) return [];
13
+ const user = match[1];
13
14
  if (require_utils.isAnyOf(user, require_githubGist.excludedPaths)) return [];
14
15
  return [{ uri: `https://github.com/${user}.png` }];
15
16
  }
@@ -1,15 +1,16 @@
1
1
  import { isAnyOf, isHostOf } from "../../../common/utils.js";
2
2
  import { excludedPaths, hosts } from "../../../feeds/platform/handlers/githubGist.js";
3
3
  //#region src/favicons/platform/handlers/githubGist.ts
4
+ const userRegex = /^\/([^/.]+)/;
4
5
  const githubGistHandler = {
5
6
  match: (url) => {
6
7
  return isHostOf(url, hosts);
7
8
  },
8
9
  resolve: (url) => {
9
10
  const { pathname } = new URL(url);
10
- const segments = pathname.split("/").filter(Boolean);
11
- if (segments.length === 0) return [];
12
- const user = segments[0];
11
+ const match = pathname.match(userRegex);
12
+ if (!match?.[1]) return [];
13
+ const user = match[1];
13
14
  if (isAnyOf(user, excludedPaths)) return [];
14
15
  return [{ uri: `https://github.com/${user}.png` }];
15
16
  }
@@ -0,0 +1,38 @@
1
+ const require_utils = require("../../../common/utils.cjs");
2
+ const require_utils$1 = require("../../utils.cjs");
3
+ const require_gitlab = require("../../../feeds/platform/handlers/gitlab.cjs");
4
+ //#region src/favicons/platform/handlers/gitlab.ts
5
+ const userRegex = /^\/([^/]+?)(?:\.atom)?(?:\/|$)/;
6
+ const fetchAvatarUrl = async (apiUrl, fetchFn) => {
7
+ const data = require_utils$1.parseBodyJson((await fetchFn(apiUrl)).body);
8
+ const entry = Array.isArray(data) ? data[0] : data;
9
+ if (require_utils$1.isNonEmptyString(entry?.avatar_url)) return entry.avatar_url;
10
+ };
11
+ const gitlabHandler = {
12
+ match: (url, content, headers) => {
13
+ try {
14
+ if (require_utils.isHostOf(url, require_gitlab.hosts)) return true;
15
+ const { pathname } = new URL(url);
16
+ if (!userRegex.test(pathname)) return false;
17
+ if (content && require_gitlab.isGitlabHtml(content)) return true;
18
+ if (headers && require_gitlab.isGitlabHeaders(headers)) return true;
19
+ } catch {}
20
+ return false;
21
+ },
22
+ resolve: async (url, _content, _headers, fetchFn) => {
23
+ if (!fetchFn) return [];
24
+ try {
25
+ const { origin, pathname } = new URL(url);
26
+ const match = pathname.match(userRegex);
27
+ if (!match?.[1]) return [];
28
+ const username = match[1];
29
+ if (require_utils.isAnyOf(username, require_gitlab.excludedPaths)) return [];
30
+ const encodedName = encodeURIComponent(username);
31
+ const avatarUrl = await fetchAvatarUrl(`${origin}/api/v4/users?username=${encodedName}`, fetchFn) ?? await fetchAvatarUrl(`${origin}/api/v4/groups/${encodedName}`, fetchFn);
32
+ if (avatarUrl) return [{ uri: avatarUrl }];
33
+ } catch {}
34
+ return [];
35
+ }
36
+ };
37
+ //#endregion
38
+ exports.gitlabHandler = gitlabHandler;