feedscout 2.0.0-next.2 → 2.0.0-rc.2

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 (190) hide show
  1. package/README.md +1 -1
  2. package/dist/blogrolls.d.ts +4 -4
  3. package/dist/blogrolls.js +3 -3
  4. package/dist/favicons.d.ts +3 -3
  5. package/dist/favicons.js +2 -2
  6. package/dist/feeds.d.ts +4 -4
  7. package/dist/feeds.js +3 -3
  8. package/dist/hubs.d.ts +1 -1
  9. package/dist/index.d.ts +7 -7
  10. package/dist/index.js +6 -6
  11. package/dist/methods.d.ts +7 -7
  12. package/dist/methods.js +7 -7
  13. package/dist/node_modules/trousse/dist/arrays.js +9 -0
  14. package/dist/node_modules/trousse/dist/is.js +19 -0
  15. package/dist/node_modules/trousse/dist/matching.js +37 -0
  16. package/dist/node_modules/trousse/dist/urls.js +23 -0
  17. package/dist/{blogrolls → src/blogrolls}/defaults.d.ts +1 -2
  18. package/dist/{blogrolls → src/blogrolls}/defaults.js +19 -11
  19. package/dist/{blogrolls → src/blogrolls}/extractors.js +3 -2
  20. package/dist/{common → src/common}/discover/defaults.js +1 -3
  21. package/dist/{common → src/common}/discover/index.js +23 -10
  22. package/dist/{common → src/common}/discover/utils.js +10 -3
  23. package/dist/src/common/locales.js +337 -0
  24. package/dist/{common → src/common}/types.d.ts +9 -1
  25. package/dist/{common → src/common}/uris/feed/index.js +1 -1
  26. package/dist/src/common/uris/guess/index.js +16 -0
  27. package/dist/{common → src/common}/uris/guess/types.d.ts +3 -0
  28. package/dist/{common → src/common}/uris/guess/utils.d.ts +2 -1
  29. package/dist/src/common/uris/guess/utils.js +83 -0
  30. package/dist/src/common/uris/headers/index.js +14 -0
  31. package/dist/{common → src/common}/uris/html/handlers.js +18 -1
  32. package/dist/{common → src/common}/uris/html/index.js +17 -1
  33. package/dist/src/common/uris/html/types.d.ts +14 -0
  34. package/dist/src/common/utils.js +51 -0
  35. package/dist/{favicons → src/favicons}/defaults.js +1 -1
  36. package/dist/{favicons → src/favicons}/extractors.js +3 -8
  37. package/dist/{favicons → src/favicons}/platform/handlers/bluesky.js +3 -2
  38. package/dist/{favicons → src/favicons}/platform/handlers/codeberg.js +2 -1
  39. package/dist/{favicons → src/favicons}/platform/handlers/deviantart.js +2 -1
  40. package/dist/{favicons → src/favicons}/platform/handlers/devto.js +4 -2
  41. package/dist/{favicons → src/favicons}/platform/handlers/github.js +2 -1
  42. package/dist/{favicons → src/favicons}/platform/handlers/githubGist.js +2 -1
  43. package/dist/{favicons → src/favicons}/platform/handlers/gitlab.js +4 -2
  44. package/dist/{favicons → src/favicons}/platform/handlers/lobsters.js +1 -1
  45. package/dist/{favicons → src/favicons}/platform/handlers/mastodon.js +2 -1
  46. package/dist/{favicons → src/favicons}/platform/handlers/reddit.js +3 -2
  47. package/dist/{favicons → src/favicons}/platform/handlers/sourceforge.js +1 -1
  48. package/dist/{favicons → src/favicons}/platform/handlers/tumblr.js +1 -1
  49. package/dist/{favicons → src/favicons}/utils.js +1 -4
  50. package/dist/{feeds → src/feeds}/defaults.d.ts +6 -3
  51. package/dist/src/feeds/defaults.js +300 -0
  52. package/dist/{feeds → src/feeds}/extractors.js +3 -2
  53. package/dist/src/feeds/platform/handlers/acast.js +28 -0
  54. package/dist/src/feeds/platform/handlers/ameblo.js +38 -0
  55. package/dist/{feeds → src/feeds}/platform/handlers/applePodcasts.js +3 -2
  56. package/dist/src/feeds/platform/handlers/arena.js +44 -0
  57. package/dist/src/feeds/platform/handlers/artstation.js +54 -0
  58. package/dist/src/feeds/platform/handlers/audioboom.js +24 -0
  59. package/dist/src/feeds/platform/handlers/bearblog.js +46 -0
  60. package/dist/{feeds → src/feeds}/platform/handlers/behance.js +10 -1
  61. package/dist/src/feeds/platform/handlers/blogspot.js +72 -0
  62. package/dist/{feeds → src/feeds}/platform/handlers/bluesky.js +2 -1
  63. package/dist/src/feeds/platform/handlers/bookwyrm.js +48 -0
  64. package/dist/src/feeds/platform/handlers/buttondown.js +45 -0
  65. package/dist/src/feeds/platform/handlers/buzzsprout.js +23 -0
  66. package/dist/{feeds → src/feeds}/platform/handlers/codeberg.js +8 -6
  67. package/dist/{feeds → src/feeds}/platform/handlers/csdn.js +2 -1
  68. package/dist/{feeds → src/feeds}/platform/handlers/dailymotion.js +19 -1
  69. package/dist/{feeds → src/feeds}/platform/handlers/deviantart.js +28 -7
  70. package/dist/{feeds → src/feeds}/platform/handlers/devto.js +11 -1
  71. package/dist/src/feeds/platform/handlers/discourse.js +66 -0
  72. package/dist/{feeds → src/feeds}/platform/handlers/douban.js +2 -1
  73. package/dist/src/feeds/platform/handlers/dreamwidth.js +49 -0
  74. package/dist/src/feeds/platform/handlers/exblog.js +36 -0
  75. package/dist/src/feeds/platform/handlers/fireside.js +25 -0
  76. package/dist/src/feeds/platform/handlers/friendica.js +44 -0
  77. package/dist/src/feeds/platform/handlers/ghost.js +31 -0
  78. package/dist/{feeds → src/feeds}/platform/handlers/github.js +9 -1
  79. package/dist/{feeds → src/feeds}/platform/handlers/githubGist.js +19 -6
  80. package/dist/{feeds → src/feeds}/platform/handlers/gitlab.js +34 -14
  81. package/dist/{feeds → src/feeds}/platform/handlers/goodreads.js +20 -9
  82. package/dist/src/feeds/platform/handlers/hackernews.js +22 -0
  83. package/dist/{feeds → src/feeds}/platform/handlers/hashnode.js +3 -2
  84. package/dist/{feeds → src/feeds}/platform/handlers/hatenablog.js +6 -2
  85. package/dist/src/feeds/platform/handlers/hearthis.js +34 -0
  86. package/dist/src/feeds/platform/handlers/heyWorld.js +19 -0
  87. package/dist/src/feeds/platform/handlers/insanejournal.js +70 -0
  88. package/dist/{feeds → src/feeds}/platform/handlers/itchio.js +27 -2
  89. package/dist/{feeds → src/feeds}/platform/handlers/kickstarter.js +2 -1
  90. package/dist/src/feeds/platform/handlers/lemmy.js +88 -0
  91. package/dist/{feeds → src/feeds}/platform/handlers/letterboxd.js +7 -1
  92. package/dist/src/feeds/platform/handlers/libsyn.js +26 -0
  93. package/dist/src/feeds/platform/handlers/listed.js +21 -0
  94. package/dist/src/feeds/platform/handlers/livejournal.js +69 -0
  95. package/dist/{feeds → src/feeds}/platform/handlers/lobsters.js +2 -1
  96. package/dist/{feeds → src/feeds}/platform/handlers/mastodon.js +32 -0
  97. package/dist/src/feeds/platform/handlers/mataroa.js +17 -0
  98. package/dist/{feeds → src/feeds}/platform/handlers/medium.js +5 -3
  99. package/dist/src/feeds/platform/handlers/microblog.js +56 -0
  100. package/dist/src/feeds/platform/handlers/misskey.js +40 -0
  101. package/dist/src/feeds/platform/handlers/myanimelist.js +44 -0
  102. package/dist/src/feeds/platform/handlers/naverBlog.js +22 -0
  103. package/dist/src/feeds/platform/handlers/nebula.js +70 -0
  104. package/dist/src/feeds/platform/handlers/note.js +55 -0
  105. package/dist/src/feeds/platform/handlers/observable.js +35 -0
  106. package/dist/src/feeds/platform/handlers/odysee.js +21 -0
  107. package/dist/src/feeds/platform/handlers/pagecord.js +17 -0
  108. package/dist/{feeds → src/feeds}/platform/handlers/paragraph.js +3 -3
  109. package/dist/src/feeds/platform/handlers/pika.js +36 -0
  110. package/dist/{feeds → src/feeds}/platform/handlers/pinterest.js +16 -1
  111. package/dist/src/feeds/platform/handlers/pixelfed.js +46 -0
  112. package/dist/src/feeds/platform/handlers/pleroma.js +34 -0
  113. package/dist/src/feeds/platform/handlers/podbean.js +30 -0
  114. package/dist/src/feeds/platform/handlers/podigee.js +30 -0
  115. package/dist/src/feeds/platform/handlers/posthaven.js +25 -0
  116. package/dist/{feeds → src/feeds}/platform/handlers/producthunt.js +2 -1
  117. package/dist/src/feeds/platform/handlers/prose.js +27 -0
  118. package/dist/src/feeds/platform/handlers/qiita.js +60 -0
  119. package/dist/src/feeds/platform/handlers/reddit.js +150 -0
  120. package/dist/src/feeds/platform/handlers/rssCom.js +21 -0
  121. package/dist/src/feeds/platform/handlers/seesaa.js +23 -0
  122. package/dist/{feeds → src/feeds}/platform/handlers/soundcloud.js +3 -1
  123. package/dist/src/feeds/platform/handlers/sourceforge.js +53 -0
  124. package/dist/src/feeds/platform/handlers/spreaker.js +22 -0
  125. package/dist/{feeds → src/feeds}/platform/handlers/stackExchange.js +28 -3
  126. package/dist/{feeds → src/feeds}/platform/handlers/steam.js +9 -1
  127. package/dist/{feeds → src/feeds}/platform/handlers/substack.js +2 -1
  128. package/dist/src/feeds/platform/handlers/tildes.js +42 -0
  129. package/dist/src/feeds/platform/handlers/tistory.js +17 -0
  130. package/dist/src/feeds/platform/handlers/transistor.js +30 -0
  131. package/dist/{feeds → src/feeds}/platform/handlers/tumblr.js +2 -1
  132. package/dist/{feeds → src/feeds}/platform/handlers/v2ex.js +2 -1
  133. package/dist/src/feeds/platform/handlers/velog.js +25 -0
  134. package/dist/{feeds → src/feeds}/platform/handlers/vimeo.js +9 -1
  135. package/dist/src/feeds/platform/handlers/weblogLol.js +27 -0
  136. package/dist/src/feeds/platform/handlers/weebly.js +26 -0
  137. package/dist/src/feeds/platform/handlers/wordpress.js +210 -0
  138. package/dist/{feeds → src/feeds}/platform/handlers/wpengine.js +1 -1
  139. package/dist/src/feeds/platform/handlers/writeas.js +53 -0
  140. package/dist/{feeds → src/feeds}/platform/handlers/ximalaya.js +3 -2
  141. package/dist/{feeds → src/feeds}/platform/handlers/youtube.js +6 -2
  142. package/dist/src/feeds/platform/handlers/zenn.js +56 -0
  143. package/package.json +10 -11
  144. package/dist/common/locales.js +0 -133
  145. package/dist/common/uris/guess/index.js +0 -8
  146. package/dist/common/uris/guess/utils.js +0 -40
  147. package/dist/common/uris/headers/index.js +0 -26
  148. package/dist/common/uris/html/types.d.ts +0 -12
  149. package/dist/common/utils.d.ts +0 -10
  150. package/dist/common/utils.js +0 -79
  151. package/dist/feeds/defaults.js +0 -168
  152. package/dist/feeds/platform/handlers/blogspot.js +0 -36
  153. package/dist/feeds/platform/handlers/lemmy.js +0 -46
  154. package/dist/feeds/platform/handlers/reddit.js +0 -74
  155. package/dist/feeds/platform/handlers/sourceforge.js +0 -19
  156. package/dist/feeds/platform/handlers/wordpress.js +0 -64
  157. package/dist/utils.d.ts +0 -2
  158. package/dist/utils.js +0 -2
  159. /package/dist/{blogrolls → src/blogrolls}/extractors.d.ts +0 -0
  160. /package/dist/{blogrolls → src/blogrolls}/index.d.ts +0 -0
  161. /package/dist/{blogrolls → src/blogrolls}/index.js +0 -0
  162. /package/dist/{blogrolls → src/blogrolls}/types.d.ts +0 -0
  163. /package/dist/{common → src/common}/discover/defaults.d.ts +0 -0
  164. /package/dist/{common → src/common}/discover/utils.d.ts +0 -0
  165. /package/dist/{common → src/common}/types.js +0 -0
  166. /package/dist/{common → src/common}/uris/feed/index.d.ts +0 -0
  167. /package/dist/{common → src/common}/uris/feed/types.d.ts +0 -0
  168. /package/dist/{common → src/common}/uris/guess/index.d.ts +0 -0
  169. /package/dist/{common → src/common}/uris/headers/index.d.ts +0 -0
  170. /package/dist/{common → src/common}/uris/headers/types.d.ts +0 -0
  171. /package/dist/{common → src/common}/uris/html/index.d.ts +0 -0
  172. /package/dist/{common → src/common}/uris/index.js +0 -0
  173. /package/dist/{common → src/common}/uris/platform/index.d.ts +0 -0
  174. /package/dist/{common → src/common}/uris/platform/index.js +0 -0
  175. /package/dist/{common → src/common}/uris/platform/types.d.ts +0 -0
  176. /package/dist/{favicons → src/favicons}/defaults.d.ts +0 -0
  177. /package/dist/{favicons → src/favicons}/extractors.d.ts +0 -0
  178. /package/dist/{favicons → src/favicons}/index.d.ts +0 -0
  179. /package/dist/{favicons → src/favicons}/index.js +0 -0
  180. /package/dist/{favicons → src/favicons}/types.d.ts +0 -0
  181. /package/dist/{feeds → src/feeds}/extractors.d.ts +0 -0
  182. /package/dist/{feeds → src/feeds}/index.d.ts +0 -0
  183. /package/dist/{feeds → src/feeds}/index.js +0 -0
  184. /package/dist/{feeds → src/feeds}/types.d.ts +0 -0
  185. /package/dist/{hubs → src/hubs}/discover/index.d.ts +0 -0
  186. /package/dist/{hubs → src/hubs}/discover/index.js +0 -0
  187. /package/dist/{hubs → src/hubs}/discover/types.d.ts +0 -0
  188. /package/dist/{hubs → src/hubs}/feed/index.js +0 -0
  189. /package/dist/{hubs → src/hubs}/headers/index.js +0 -0
  190. /package/dist/{hubs → src/hubs}/html/index.js +0 -0
@@ -0,0 +1,337 @@
1
+ //#region src/common/locales.json
2
+ var errors = {
3
+ "platformMethodRequiresUrl": "Platform method requires url to be provided in input",
4
+ "feedMethodRequiresContent": "Feed method requires content to be provided in input",
5
+ "htmlMethodRequiresContent": "HTML method requires content to be provided in input",
6
+ "headersMethodRequiresHeaders": "Headers method requires headers to be provided in input",
7
+ "guessMethodRequiresUrl": "Guess method requires url to be provided in input"
8
+ };
9
+ var hints = {
10
+ "youtube:all": "All uploads",
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",
20
+ "youtube:playlist": "Playlist",
21
+ "github:activity": "Activity",
22
+ "github:releases": "Releases",
23
+ "github:commits": "Commits",
24
+ "github:tags": "Tags",
25
+ "github:wiki": "Wiki",
26
+ "github:discussions": "Discussions",
27
+ "github:discussion-category": "Discussion category",
28
+ "github:branch-commits": "Branch commits",
29
+ "github:file-history": "File history",
30
+ "reddit:posts": "Posts",
31
+ "reddit:post-comments": "Post comments",
32
+ "reddit:comments": "Comments",
33
+ "reddit:multireddit": "Multireddit",
34
+ "reddit:user-submitted": "Submitted",
35
+ "reddit:user-comments": "Comments",
36
+ "reddit:search": "Search",
37
+ "reddit:wiki": "Wiki",
38
+ "reddit:subreddits": "Subreddits",
39
+ "gitlab:activity": "Activity",
40
+ "gitlab:releases": "Releases",
41
+ "gitlab:tags": "Tags",
42
+ "gitlab:issues": "Issues",
43
+ "gitlab:merge-requests": "Merge requests",
44
+ "gitlab:branch-commits": "Branch commits",
45
+ "codeberg:activity": "Activity",
46
+ "codeberg:releases": "Releases",
47
+ "codeberg:tags": "Tags",
48
+ "codeberg:branch-commits": "Branch commits",
49
+ "codeberg:file-history": "File history",
50
+ "wordpress:category-rss": "Category (RSS)",
51
+ "wordpress:category-atom": "Category (Atom)",
52
+ "wordpress:category-rdf": "Category (RDF)",
53
+ "wordpress:tag-rss": "Tag (RSS)",
54
+ "wordpress:tag-atom": "Tag (Atom)",
55
+ "wordpress:tag-rdf": "Tag (RDF)",
56
+ "wordpress:author-rss": "Author (RSS)",
57
+ "wordpress:author-atom": "Author (Atom)",
58
+ "wordpress:author-rdf": "Author (RDF)",
59
+ "wordpress:date-archive-rss": "Date archive (RSS)",
60
+ "wordpress:date-archive-atom": "Date archive (Atom)",
61
+ "wordpress:date-archive-rdf": "Date archive (RDF)",
62
+ "wordpress:posts-rss": "Posts (RSS)",
63
+ "wordpress:posts-rdf": "Posts (RDF)",
64
+ "wordpress:posts-atom": "Posts (Atom)",
65
+ "wordpress:comments-rss": "Comments (RSS)",
66
+ "wordpress:comments-rdf": "Comments (RDF)",
67
+ "wordpress:comments-atom": "Comments (Atom)",
68
+ "wordpress:post-comments-rss": "Post comments (RSS)",
69
+ "wordpress:post-comments-rdf": "Post comments (RDF)",
70
+ "wordpress:post-comments-atom": "Post comments (Atom)",
71
+ "blogspot:posts-atom": "Posts (Atom)",
72
+ "blogspot:posts-rss": "Posts (RSS)",
73
+ "blogspot:posts-summary-atom": "Posts summary (Atom)",
74
+ "blogspot:posts-summary-rss": "Posts summary (RSS)",
75
+ "blogspot:comments-atom": "Comments (Atom)",
76
+ "blogspot:comments-rss": "Comments (RSS)",
77
+ "blogspot:label-atom": "Label (Atom)",
78
+ "blogspot:label-rss": "Label (RSS)",
79
+ "blogspot:post-comments-atom": "Post comments (Atom)",
80
+ "blogspot:post-comments-rss": "Post comments (RSS)",
81
+ "behance:portfolio": "Portfolio",
82
+ "behance:appreciated": "Appreciated",
83
+ "behance:projects": "Featured projects",
84
+ "behance:featured": "Featured by Adobe",
85
+ "bluesky:posts": "Posts",
86
+ "dailymotion:videos": "Videos",
87
+ "dailymotion:playlist": "Playlist",
88
+ "dailymotion:channel": "Channel",
89
+ "dailymotion:trending": "Trending",
90
+ "dailymotion:search": "Search",
91
+ "deviantart:tag": "Tag",
92
+ "deviantart:favorites": "Favorites",
93
+ "deviantart:gallery": "Gallery",
94
+ "deviantart:deviations": "Deviations",
95
+ "deviantart:journal": "Journal",
96
+ "deviantart:daily-deviations": "Daily Deviations",
97
+ "deviantart:popular": "Popular",
98
+ "devto:posts": "Posts",
99
+ "devto:tag": "Tag",
100
+ "devto:community": "Community",
101
+ "devto:latest": "Latest",
102
+ "github-gist:gists": "Gists",
103
+ "github-gist:starred": "Starred",
104
+ "github-gist:forks": "Forks",
105
+ "github-gist:discover": "Discover",
106
+ "kickstarter:updates": "Updates",
107
+ "kickstarter:projects": "Projects",
108
+ "letterboxd:diary": "Diary",
109
+ "letterboxd:journal": "Journal",
110
+ "lobsters:tag": "Tag",
111
+ "lobsters:domain": "Domain",
112
+ "lobsters:stories": "Stories",
113
+ "lobsters:top": "Top stories",
114
+ "lobsters:newest": "Newest",
115
+ "lobsters:comments": "Comments",
116
+ "mastodon:posts": "Posts",
117
+ "mastodon:tag": "Tag",
118
+ "mastodon:tagged": "Tagged",
119
+ "mastodon:replies": "Posts with replies",
120
+ "mastodon:media": "Media",
121
+ "medium:posts": "Posts",
122
+ "medium:tag": "Tag",
123
+ "medium:tagged": "Tagged",
124
+ "medium:publication": "Publication",
125
+ "pinterest:pins": "Pins",
126
+ "pinterest:board": "Board",
127
+ "producthunt:topic": "Topic",
128
+ "producthunt:category": "Category",
129
+ "producthunt:products": "Products",
130
+ "soundcloud:tracks": "Tracks",
131
+ "stackexchange:tag": "Tag",
132
+ "stackexchange:question": "Question",
133
+ "stackexchange:user": "User",
134
+ "stackexchange:newest": "Newest questions",
135
+ "stackexchange:collective": "Collective",
136
+ "steam:news": "News",
137
+ "steam:news-global": "News (global)",
138
+ "steam:daily-deals": "Daily deals",
139
+ "steam:group": "Group",
140
+ "substack:newsletter": "Newsletter",
141
+ "tumblr:posts": "Posts",
142
+ "tumblr:tag": "Tag",
143
+ "goodreads:updates": "Updates",
144
+ "goodreads:reviews": "Reviews",
145
+ "goodreads:shelf": "Shelf",
146
+ "sourceforge:activity": "Recent activity",
147
+ "sourceforge:files": "File releases",
148
+ "sourceforge:news-rss": "News (RSS)",
149
+ "sourceforge:news-atom": "News (Atom)",
150
+ "sourceforge:discussion": "Discussion",
151
+ "sourceforge:discussion-atom": "Discussion (Atom)",
152
+ "sourceforge:project-feed": "Project feed",
153
+ "sourceforge:bugs": "Bugs",
154
+ "velog:posts": "Posts",
155
+ "velog:trending": "Trending",
156
+ "vimeo:videos": "Videos",
157
+ "vimeo:likes": "Likes",
158
+ "vimeo:channel": "Channel",
159
+ "vimeo:group": "Group",
160
+ "vimeo:album": "Album",
161
+ "hashnode:blog": "Blog",
162
+ "paragraph:blog": "Blog",
163
+ "hatenablog:posts-rss": "Posts (RSS)",
164
+ "hatenablog:posts-atom": "Posts (Atom)",
165
+ "hatenablog:category-rss": "Category (RSS)",
166
+ "hatenablog:category-atom": "Category (Atom)",
167
+ "hatenablog:author-rss": "Author (RSS)",
168
+ "hatenablog:author-atom": "Author (Atom)",
169
+ "itchio:devlog": "Devlog",
170
+ "itchio:games": "Games",
171
+ "itchio:tag": "Tag",
172
+ "itchio:platform": "Platform",
173
+ "itchio:genre": "Genre",
174
+ "itchio:made-with": "Made with",
175
+ "itchio:section": "Section",
176
+ "itchio:featured": "Featured",
177
+ "itchio:new": "New",
178
+ "itchio:sales": "Sales",
179
+ "itchio:blog": "Blog",
180
+ "csdn:blog": "Blog",
181
+ "douban:interests": "Interests",
182
+ "douban:reviews": "Reviews",
183
+ "douban:notes": "Notes",
184
+ "douban:subjectReviews": "Subject reviews",
185
+ "v2ex:index": "Index",
186
+ "v2ex:node": "Node",
187
+ "v2ex:member": "Member",
188
+ "v2ex:tab": "Tab",
189
+ "ximalaya:album": "Album",
190
+ "lemmy:community": "Community",
191
+ "lemmy:user": "User",
192
+ "lemmy:all": "All",
193
+ "lemmy:local": "Local",
194
+ "apple-podcasts:podcast": "Podcast",
195
+ "writeas:blog": "Blog",
196
+ "writeas:tag": "Tag",
197
+ "prose:blog": "Blog",
198
+ "prose:tag": "Tag",
199
+ "prose:discovery": "Discovery",
200
+ "pagecord:blog": "Blog",
201
+ "listed:blog": "Blog",
202
+ "fireside:podcast-rss": "Podcast (RSS)",
203
+ "fireside:podcast-json": "Podcast (JSON)",
204
+ "exblog:posts-rss": "Posts (RSS)",
205
+ "exblog:posts-atom": "Posts (Atom)",
206
+ "exblog:category-rss": "Category (RSS)",
207
+ "exblog:category-atom": "Category (Atom)",
208
+ "dreamwidth:posts-rss": "Posts (RSS)",
209
+ "dreamwidth:posts-atom": "Posts (Atom)",
210
+ "dreamwidth:posts-tag-rss": "Tag (RSS)",
211
+ "dreamwidth:posts-tag-atom": "Tag (Atom)",
212
+ "dreamwidth:userpics-atom": "Userpics (Atom)",
213
+ "buttondown:newsletter": "Newsletter",
214
+ "bearblog:posts-atom": "Posts (Atom)",
215
+ "bearblog:posts-rss": "Posts (RSS)",
216
+ "bearblog:tag-atom": "Tag (Atom)",
217
+ "bearblog:tag-rss": "Tag (RSS)",
218
+ "bearblog:discover-atom": "Trending (Atom)",
219
+ "bearblog:discover-rss": "Trending (RSS)",
220
+ "transistor:podcast": "Podcast",
221
+ "odysee:videos": "Videos",
222
+ "myanimelist:anime": "Anime list",
223
+ "myanimelist:manga": "Manga list",
224
+ "myanimelist:recently-watched": "Recently watched",
225
+ "myanimelist:recently-read": "Recently read",
226
+ "myanimelist:news": "News",
227
+ "myanimelist:featured": "Featured",
228
+ "hackernews:front": "Front page",
229
+ "hackernews:show": "Show HN",
230
+ "tistory:blog": "Blog",
231
+ "note:blog": "Blog",
232
+ "note:hashtag": "Hashtag",
233
+ "note:magazine": "Magazine",
234
+ "note:featured": "Featured",
235
+ "artstation:portfolio": "Portfolio",
236
+ "artstation:artwork": "Artwork",
237
+ "artstation:artwork-trending": "Artwork (Trending)",
238
+ "nebula:videos": "Videos",
239
+ "nebula:videos-plus": "Videos (Plus)",
240
+ "nebula:videos-all": "All Videos",
241
+ "nebula:videos-all-plus": "All Videos (Plus)",
242
+ "nebula:category": "Category",
243
+ "nebula:category-plus": "Category (Plus)",
244
+ "nebula:channels": "Recently Added Channels",
245
+ "acast:podcast": "Podcast",
246
+ "ameblo:posts-rss": "Posts (RSS)",
247
+ "ameblo:posts-atom": "Posts (Atom)",
248
+ "ameblo:posts-rdf": "Posts (RDF)",
249
+ "arena:profile": "Profile",
250
+ "arena:channel": "Channel",
251
+ "arena:editorial": "Editorial",
252
+ "audioboom:podcast": "Podcast",
253
+ "bookwyrm:activity": "Activity",
254
+ "bookwyrm:reviews": "Reviews",
255
+ "bookwyrm:quotes": "Quotes",
256
+ "bookwyrm:comments": "Comments",
257
+ "bookwyrm:shelf": "Shelf",
258
+ "buzzsprout:podcast": "Podcast",
259
+ "discourse:latest": "Latest",
260
+ "discourse:posts": "Latest posts",
261
+ "discourse:top": "Top",
262
+ "discourse:category": "Category",
263
+ "discourse:topic": "Topic",
264
+ "discourse:activity": "Activity",
265
+ "friendica:posts": "Posts",
266
+ "friendica:comments": "Comments",
267
+ "friendica:replies": "Replies",
268
+ "friendica:activity": "Activity",
269
+ "ghost:blog": "Blog",
270
+ "ghost:tag": "Tag",
271
+ "ghost:author": "Author",
272
+ "hearthis:tracks": "Tracks",
273
+ "hey-world:blog": "Blog",
274
+ "insanejournal:posts-rss": "Posts (RSS)",
275
+ "insanejournal:posts-atom": "Posts (Atom)",
276
+ "insanejournal:posts-tag-rss": "Tag (RSS)",
277
+ "insanejournal:posts-tag-atom": "Tag (Atom)",
278
+ "insanejournal:userpics-atom": "Userpics (Atom)",
279
+ "libsyn:podcast": "Podcast",
280
+ "livejournal:posts-rss": "Posts (RSS)",
281
+ "livejournal:posts-atom": "Posts (Atom)",
282
+ "livejournal:posts-tag-rss": "Tag (RSS)",
283
+ "livejournal:posts-tag-atom": "Tag (Atom)",
284
+ "livejournal:userpics-atom": "Userpics (Atom)",
285
+ "mataroa:blog": "Blog",
286
+ "microblog:posts-rss": "Posts (RSS)",
287
+ "microblog:posts-json": "Posts (JSON)",
288
+ "microblog:podcast": "Podcast",
289
+ "microblog:podcast-json": "Podcast (JSON)",
290
+ "microblog:category-rss": "Category (RSS)",
291
+ "microblog:category-json": "Category (JSON)",
292
+ "microblog:archive": "Archive",
293
+ "microblog:photos": "Photos",
294
+ "microblog:replies": "Replies",
295
+ "misskey:posts-atom": "Posts (Atom)",
296
+ "misskey:posts-rss": "Posts (RSS)",
297
+ "misskey:posts-json": "Posts (JSON)",
298
+ "naver-blog:blog": "Blog",
299
+ "observable:notebooks": "Notebooks",
300
+ "observable:collection": "Collection",
301
+ "observable:recent": "Recent",
302
+ "observable:trending": "Trending",
303
+ "pika:posts-atom": "Posts (Atom)",
304
+ "pika:posts-rss": "Posts (RSS)",
305
+ "pika:tag-atom": "Tag (Atom)",
306
+ "pika:tag-rss": "Tag (RSS)",
307
+ "pixelfed:posts": "Posts",
308
+ "pleroma:posts": "Posts",
309
+ "pleroma:posts-rss": "Posts (RSS)",
310
+ "podbean:podcast": "Podcast",
311
+ "podigee:podcast": "Podcast",
312
+ "posthaven:posts": "Posts",
313
+ "posthaven:tag": "Tag",
314
+ "qiita:posts": "Posts",
315
+ "qiita:tag": "Tag",
316
+ "qiita:organization": "Organization",
317
+ "qiita:popular": "Popular items",
318
+ "qiita:zine": "Qiita Zine",
319
+ "rss-com:podcast": "Podcast",
320
+ "seesaa:posts-rss2": "Posts (RSS 2.0)",
321
+ "seesaa:posts-rdf": "Posts (RDF)",
322
+ "spreaker:podcast": "Podcast",
323
+ "tildes:topics-rss": "Topics (RSS)",
324
+ "tildes:topics-atom": "Topics (Atom)",
325
+ "tildes:group-rss": "Group (RSS)",
326
+ "tildes:group-atom": "Group (Atom)",
327
+ "weblog-lol:posts-rss": "Posts (RSS)",
328
+ "weblog-lol:posts-atom": "Posts (Atom)",
329
+ "weblog-lol:posts-json": "Posts (JSON)",
330
+ "weebly:blog": "Blog",
331
+ "zenn:posts": "Posts",
332
+ "zenn:topic": "Topic",
333
+ "zenn:publication": "Publication",
334
+ "zenn:trending": "Trending"
335
+ };
336
+ //#endregion
337
+ export { errors, hints };
@@ -6,6 +6,7 @@ import { PlatformMethodOptions } from "./uris/platform/types.js";
6
6
 
7
7
  //#region src/common/types.d.ts
8
8
  type MaybePromise<T> = T | Promise<T>;
9
+ type Pattern = string | RegExp;
9
10
  type UriEntry = string | Array<string>;
10
11
  type DiscoverUriHint = {
11
12
  key: string;
@@ -42,6 +43,11 @@ type DiscoverProgress = {
42
43
  current: string;
43
44
  };
44
45
  type DiscoverOnProgressFn = (progress: DiscoverProgress) => void;
46
+ type DiscoverErrorContext = {
47
+ phase: 'fetchInput' | 'resolveSiteUrl';
48
+ url?: string;
49
+ };
50
+ type DiscoverOnErrorFn = (error: unknown, context: DiscoverErrorContext) => void;
45
51
  type DiscoverResult<TValid = object> = ({
46
52
  url: string;
47
53
  isValid: true;
@@ -82,8 +88,10 @@ type DiscoverOptions<TValid, TMethods extends DiscoverMethod = DiscoverMethod> =
82
88
  stopOnFirstMethod?: boolean;
83
89
  stopOnFirstResult?: boolean;
84
90
  concurrency?: number;
91
+ maxUris?: number;
85
92
  onProgress?: DiscoverOnProgressFn;
93
+ onError?: DiscoverOnErrorFn;
86
94
  includeInvalid?: boolean;
87
95
  };
88
96
  //#endregion
89
- export { DiscoverExtractFn, DiscoverFetchFn, DiscoverFetchFnOptions, DiscoverFetchFnResponse, DiscoverInput, DiscoverInputObject, DiscoverMethod, DiscoverMethodsConfig, DiscoverOnProgressFn, DiscoverOptions, DiscoverProgress, DiscoverResolveSiteUrlFn, DiscoverResolveUrlFn, DiscoverResult, DiscoverUriEntry, DiscoverUriHint, LinkSelector, MaybePromise, UriEntry };
97
+ export { DiscoverExtractFn, DiscoverFetchFn, DiscoverFetchFnOptions, DiscoverFetchFnResponse, DiscoverInput, DiscoverInputObject, DiscoverMethod, DiscoverMethodsConfig, DiscoverOnProgressFn, DiscoverOptions, DiscoverProgress, DiscoverResolveSiteUrlFn, DiscoverResolveUrlFn, DiscoverResult, DiscoverUriEntry, DiscoverUriHint, LinkSelector, MaybePromise, Pattern, UriEntry };
@@ -1,4 +1,4 @@
1
- import { omitEmpty } from "../../utils.js";
1
+ import { omitEmpty } from "../../../../node_modules/trousse/dist/arrays.js";
2
2
  import { parseFeed } from "feedsmith";
3
3
  //#region src/common/uris/feed/index.ts
4
4
  const discoverUrisFromFeed = (content, options) => {
@@ -0,0 +1,16 @@
1
+ import { extractSectionBaseUrls, generatePathUrlCombinations, generateUrlCombinations, getAncestorPathBases } from "./utils.js";
2
+ //#region src/common/uris/guess/index.ts
3
+ const discoverUrisFromGuess = (options) => {
4
+ const { baseUrl, uris, additionalBaseUrls = [], maxAncestorDepth = 0 } = options;
5
+ const { content, sectionNames } = options;
6
+ const combinations = generateUrlCombinations([baseUrl, ...additionalBaseUrls], uris);
7
+ const pathBases = [];
8
+ if (maxAncestorDepth > 0) pathBases.push(...getAncestorPathBases(baseUrl, maxAncestorDepth));
9
+ if (content && sectionNames && sectionNames.length > 0) {
10
+ for (const sectionBase of extractSectionBaseUrls(content, baseUrl, sectionNames)) if (!pathBases.includes(sectionBase)) pathBases.push(sectionBase);
11
+ }
12
+ if (pathBases.length > 0) combinations.push(...generatePathUrlCombinations(pathBases, uris));
13
+ return combinations;
14
+ };
15
+ //#endregion
16
+ export { discoverUrisFromGuess };
@@ -5,6 +5,9 @@ type GuessMethodOptions = {
5
5
  baseUrl: string;
6
6
  uris: Array<UriEntry>;
7
7
  additionalBaseUrls?: Array<string>;
8
+ maxAncestorDepth?: number;
9
+ content?: string;
10
+ sectionNames?: Array<string>;
8
11
  };
9
12
  //#endregion
10
13
  export { GuessMethodOptions };
@@ -2,7 +2,8 @@ import { UriEntry } from "../../types.js";
2
2
 
3
3
  //#region src/common/uris/guess/utils.d.ts
4
4
  declare const generateUrlCombinations: (baseUrls: Array<string>, uris: Array<UriEntry>) => Array<UriEntry>;
5
+ declare const extractSectionBaseUrls: (content: string, baseUrl: string, sectionNames: Array<string>) => Array<string>;
5
6
  declare const getWwwCounterpart: (baseUrl: string) => string;
6
7
  declare const getSubdomainVariants: (baseUrl: string, prefixes: Array<string>) => Array<string>;
7
8
  //#endregion
8
- export { generateUrlCombinations, getSubdomainVariants, getWwwCounterpart };
9
+ export { extractSectionBaseUrls, generateUrlCombinations, getSubdomainVariants, getWwwCounterpart };
@@ -0,0 +1,83 @@
1
+ import { getPathSegments, parseUrl } from "../../../../node_modules/trousse/dist/urls.js";
2
+ import { Parser } from "htmlparser2";
3
+ //#region src/common/uris/guess/utils.ts
4
+ const ipAddressRegex = /^\d+\.\d+\.\d+\.\d+$/;
5
+ const resolveUri = (uri, base, origin, pathname) => {
6
+ if (uri.startsWith("/")) return `${origin}${uri}`;
7
+ if (uri.startsWith("?")) return `${origin}${pathname}${uri}`;
8
+ return new URL(uri, base).href;
9
+ };
10
+ const generateUrlCombinations = (baseUrls, uris) => {
11
+ return baseUrls.flatMap((base) => {
12
+ const parsed = new URL(base);
13
+ const origin = parsed.origin;
14
+ const pathname = parsed.pathname;
15
+ return uris.map((uri) => {
16
+ if (typeof uri === "string") return resolveUri(uri, base, origin, pathname);
17
+ return uri.map((alternative) => resolveUri(alternative, base, origin, pathname));
18
+ });
19
+ });
20
+ };
21
+ const resolvePathUri = (uri, base) => {
22
+ if (uri.startsWith("/")) return new URL(uri.slice(1), base).href;
23
+ return new URL(uri, base).href;
24
+ };
25
+ const generatePathUrlCombinations = (pathBases, uris) => {
26
+ return pathBases.flatMap((base) => {
27
+ const normalizedBase = base.endsWith("/") ? base : `${base}/`;
28
+ return uris.map((uri) => {
29
+ if (typeof uri === "string") return resolvePathUri(uri, normalizedBase);
30
+ return uri.map((alternative) => resolvePathUri(alternative, normalizedBase));
31
+ });
32
+ });
33
+ };
34
+ const getAncestorPathBases = (baseUrl, maxDepth) => {
35
+ const url = parseUrl(baseUrl);
36
+ if (!url) return [];
37
+ const segments = url.pathname.split("/").slice(1, -1).filter((segment) => segment !== "");
38
+ const bases = [];
39
+ let base = `${url.origin}/`;
40
+ for (const segment of segments.slice(0, maxDepth)) {
41
+ base += `${segment}/`;
42
+ bases.push(base);
43
+ }
44
+ return bases;
45
+ };
46
+ const extractSectionBaseUrls = (content, baseUrl, sectionNames) => {
47
+ const base = parseUrl(baseUrl);
48
+ if (!base) return [];
49
+ const bases = /* @__PURE__ */ new Set();
50
+ const parser = new Parser({ onopentag: (name, attributes) => {
51
+ if (name !== "a" || !attributes.href) return;
52
+ const url = parseUrl(attributes.href, base);
53
+ if (!url || url.origin !== base.origin) return;
54
+ const segments = getPathSegments(url);
55
+ const segment = segments.length === 1 ? segments[0] : void 0;
56
+ if (segment && sectionNames.includes(segment.toLowerCase())) bases.add(`${url.origin}/${segment}/`);
57
+ } }, { decodeEntities: true });
58
+ parser.write(content);
59
+ parser.end();
60
+ return [...bases];
61
+ };
62
+ const getWwwCounterpart = (baseUrl) => {
63
+ const url = new URL(baseUrl);
64
+ const port = url.port ? `:${url.port}` : "";
65
+ if (url.hostname.startsWith("www.")) return `${url.protocol}//${url.hostname.slice(4)}${port}`;
66
+ return `${url.protocol}//www.${url.hostname}${port}`;
67
+ };
68
+ const getSubdomainVariants = (baseUrl, prefixes) => {
69
+ const url = new URL(baseUrl);
70
+ const hostname = url.hostname;
71
+ const isIpAddress = ipAddressRegex.test(hostname);
72
+ if (hostname === "localhost" || isIpAddress) return [];
73
+ const hostnameParts = hostname.split(".");
74
+ if (hostnameParts.length < 2) return [];
75
+ const rootDomain = hostnameParts.slice(-2).join(".");
76
+ const protocol = url.protocol;
77
+ const port = url.port ? `:${url.port}` : "";
78
+ return prefixes.map((prefix) => {
79
+ return `${protocol}//${prefix === "" ? rootDomain : `${prefix}.${rootDomain}`}${port}`;
80
+ });
81
+ };
82
+ //#endregion
83
+ export { extractSectionBaseUrls, generatePathUrlCombinations, generateUrlCombinations, getAncestorPathBases, getSubdomainVariants, getWwwCounterpart };
@@ -0,0 +1,14 @@
1
+ import { matchesAnyOfLinkSelectors } from "../../utils.js";
2
+ import LinkHeader from "http-link-header";
3
+ //#region src/common/uris/headers/index.ts
4
+ const discoverUrisFromHeaders = (headers, options) => {
5
+ const linkHeader = headers.get("link");
6
+ if (!linkHeader) return [];
7
+ const uris = /* @__PURE__ */ new Set();
8
+ try {
9
+ for (const ref of LinkHeader.parse(linkHeader).refs) if (ref.rel && matchesAnyOfLinkSelectors(ref.rel, ref.type, options.linkSelectors)) uris.add(ref.uri);
10
+ } catch {}
11
+ return [...uris];
12
+ };
13
+ //#endregion
14
+ export { discoverUrisFromHeaders };
@@ -1,6 +1,12 @@
1
- import { endsWithAnyOf, includesAnyOf, matchesAnyOfLinkSelectors } from "../../utils.js";
1
+ import { endsWithAnyOf, includesAnyOf } from "../../../../node_modules/trousse/dist/matching.js";
2
+ import { parseUrl } from "../../../../node_modules/trousse/dist/urls.js";
3
+ import { matchesAnyOfLinkSelectors } from "../../utils.js";
2
4
  //#region src/common/uris/html/handlers.ts
3
5
  const handleOpenTag = (context, name, attribs, _isImplied) => {
6
+ if (name === "base" && context.baseHref === void 0) {
7
+ const href = attribs.href?.trim();
8
+ if (href) context.baseHref = href;
9
+ }
4
10
  if (name === "link" && attribs.href) {
5
11
  const rel = attribs.rel?.toLowerCase();
6
12
  if (!rel) return;
@@ -16,6 +22,17 @@ const handleOpenTag = (context, name, attribs, _isImplied) => {
16
22
  context.currentAnchor.href = attribs.href;
17
23
  context.currentAnchor.text = "";
18
24
  if (endsWithAnyOf(lowerHref, context.options.anchorUris)) context.discoveredUris.add(attribs.href);
25
+ if (context.options.anchorPathSegments?.length) {
26
+ const pathname = parseUrl(attribs.href, "https://feedscout.invalid")?.pathname;
27
+ if (pathname && includesAnyOf(pathname, context.options.anchorPathSegments)) context.discoveredUris.add(attribs.href);
28
+ }
29
+ }
30
+ if (context.currentAnchor.href && context.options.anchorAttributes?.length) for (const attribute of context.options.anchorAttributes) {
31
+ const value = attribs[attribute];
32
+ if (value && includesAnyOf(value, context.options.anchorLabels)) {
33
+ context.discoveredUris.add(context.currentAnchor.href);
34
+ break;
35
+ }
19
36
  }
20
37
  };
21
38
  const handleText = (context, text) => {
@@ -13,7 +13,23 @@ const discoverUrisFromHtml = (html, options) => {
13
13
  const parser = new Parser(createHtmlUrisHandlers(context), { decodeEntities: true });
14
14
  parser.write(html);
15
15
  parser.end();
16
- return [...context.discoveredUris];
16
+ const uris = [...context.discoveredUris];
17
+ if (context.baseHref) {
18
+ let base;
19
+ try {
20
+ base = options.baseUrl ? new URL(context.baseHref, options.baseUrl).href : context.baseHref;
21
+ } catch {
22
+ base = options.baseUrl;
23
+ }
24
+ return uris.map((uri) => {
25
+ try {
26
+ return new URL(uri, base).href;
27
+ } catch {
28
+ return uri;
29
+ }
30
+ });
31
+ }
32
+ return uris;
17
33
  };
18
34
  //#endregion
19
35
  export { discoverUrisFromHtml };
@@ -0,0 +1,14 @@
1
+ import { LinkSelector, Pattern } from "../../types.js";
2
+
3
+ //#region src/common/uris/html/types.d.ts
4
+ type HtmlMethodOptions = {
5
+ baseUrl?: string;
6
+ linkSelectors: Array<LinkSelector>;
7
+ anchorUris: Array<Pattern>;
8
+ anchorPathSegments?: Array<Pattern>;
9
+ anchorIgnoredUris: Array<Pattern>;
10
+ anchorLabels: Array<Pattern>;
11
+ anchorAttributes?: Array<string>;
12
+ };
13
+ //#endregion
14
+ export { HtmlMethodOptions };
@@ -0,0 +1,51 @@
1
+ import { anyWordMatchesAnyOf, isAnyOf } from "../../node_modules/trousse/dist/matching.js";
2
+ import { hints } from "./locales.js";
3
+ //#region src/common/utils.ts
4
+ const composeHint = (key) => ({
5
+ key,
6
+ label: hints[key]
7
+ });
8
+ const isSuccessfulStatus = (status) => {
9
+ return status === void 0 || status >= 200 && status < 300;
10
+ };
11
+ const toPositiveInteger = (value, fallback) => {
12
+ return typeof value === "number" && Number.isInteger(value) && value >= 1 ? value : fallback;
13
+ };
14
+ const normalizeMimeType = (type) => {
15
+ return type.split(";")[0].trim().toLowerCase();
16
+ };
17
+ const isOfAllowedMimeType = (type, allowedTypes) => {
18
+ if (allowedTypes.length === 0) return true;
19
+ if (!type) return false;
20
+ return isAnyOf(type, allowedTypes, normalizeMimeType);
21
+ };
22
+ const hasMetaContent = (content, name, value) => {
23
+ const escapedName = name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
24
+ const escapedValue = value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
25
+ return new RegExp(`<meta(?=[^>]*(?:name|property)=["']${escapedName}["'])(?=[^>]*content=["']${escapedValue})`, "i").test(content);
26
+ };
27
+ const matchesAnyOfLinkSelectors = (rel, type, selectors) => {
28
+ return selectors.some((selector) => {
29
+ if (!anyWordMatchesAnyOf(rel, [selector.rel])) return false;
30
+ if (!selector.types) return true;
31
+ return isOfAllowedMimeType(type, selector.types);
32
+ });
33
+ };
34
+ const processConcurrently = async (items, processFn, options) => {
35
+ if (!(options.concurrency >= 1)) return;
36
+ const active = /* @__PURE__ */ new Set();
37
+ let index = 0;
38
+ while (index < items.length || active.size > 0) {
39
+ if (options.shouldStop?.()) break;
40
+ while (active.size < options.concurrency && index < items.length) {
41
+ const item = items[index++];
42
+ const promise = processFn(item).catch(() => {}).finally(() => {
43
+ active.delete(promise);
44
+ });
45
+ active.add(promise);
46
+ }
47
+ if (active.size > 0) await Promise.race(active);
48
+ }
49
+ };
50
+ //#endregion
51
+ export { composeHint, hasMetaContent, isSuccessfulStatus, matchesAnyOfLinkSelectors, processConcurrently, toPositiveInteger };
@@ -1,4 +1,4 @@
1
- import { omitEmpty } from "../common/utils.js";
1
+ import { omitEmpty } from "../../node_modules/trousse/dist/arrays.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";