@postfetch/core 0.4.1 → 0.5.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.
package/dist/index.js CHANGED
@@ -266,12 +266,16 @@ async function instagramUnavailable(net, code) {
266
266
  const response = await net(url.href, { headers: mobileHeaders() }, 1).catch(() => null);
267
267
  const body = response ? await response.text().catch(() => "") : "";
268
268
  const reason = instagramUnavailableReason(response?.status ?? 0, body);
269
- return new PostfetchError(reasonStatus(reason), `Instagram post unavailable: ${reason}`, reason);
269
+ const probe = response ? `oembed ${response.status}` : "oembed unreachable";
270
+ return new PostfetchError(reasonStatus(reason), `Instagram post unavailable: ${reason} (${probe})`, reason);
270
271
  }
271
272
  function instagramUnavailableReason(status, body) {
272
273
  if (status === 404) {
273
274
  return "notFound";
274
275
  }
276
+ if (status === 429) {
277
+ return "rateLimited";
278
+ }
275
279
  if (status !== 0 && status < 400) {
276
280
  return "unavailable";
277
281
  }
@@ -1008,9 +1012,6 @@ async function resolveReddit(input) {
1008
1012
  const token = await appToken(input.net);
1009
1013
  const post = await fetchPost(input.net, token, id);
1010
1014
  const items = await mediaItems3(input, post, id);
1011
- if (items.length === 0) {
1012
- throw new Error("Reddit media not found");
1013
- }
1014
1015
  return { archiveFilename: filename(`reddit_${id}.zip`), id, items, metadata: redditMetadata(post), platform: "reddit" };
1015
1016
  }
1016
1017
  function redditMetadata(post) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@postfetch/core",
3
- "version": "0.4.1",
3
+ "version": "0.5.0",
4
4
  "description": "Zero-dependency typed core that turns Facebook, Instagram, Pinterest, Reddit, SoundCloud, TikTok, X and YouTube post URLs into media files.",
5
5
  "license": "MIT",
6
6
  "type": "module",
package/src/instagram.ts CHANGED
@@ -79,16 +79,23 @@ async function instagramUnavailable(net: Net, code: string): Promise<PostfetchEr
79
79
  const response = await net(url.href, { headers: mobileHeaders() }, 1).catch(() => null);
80
80
  const body = response ? await response.text().catch(() => "") : "";
81
81
  const reason = instagramUnavailableReason(response?.status ?? 0, body);
82
- return new PostfetchError(reasonStatus(reason), `Instagram post unavailable: ${reason}`, reason);
82
+ // Carry the oembed status so an unclassified failure still leaves a raw clue
83
+ // (200 = post is public but no media could be parsed; 429 = throttled; …).
84
+ const probe = response ? `oembed ${response.status}` : "oembed unreachable";
85
+ return new PostfetchError(reasonStatus(reason), `Instagram post unavailable: ${reason} (${probe})`, reason);
83
86
  }
84
87
 
85
88
  // Classifies why the post is gone from the oembed endpoint, which returns a
86
- // telling status and body (404 when removed; 400 with a MIN_AGE_ACCOUNT block
87
- // for 18+ accounts) while the media endpoints just come back empty.
89
+ // telling status and body (404 when removed; 429 when throttled; 400 with a
90
+ // MIN_AGE_ACCOUNT block for 18+ accounts) while the media endpoints just come
91
+ // back empty.
88
92
  export function instagramUnavailableReason(status: number, body: string): PostfetchReason {
89
93
  if (status === 404) {
90
94
  return "notFound";
91
95
  }
96
+ if (status === 429) {
97
+ return "rateLimited";
98
+ }
92
99
  if (status !== 0 && status < 400) {
93
100
  return "unavailable";
94
101
  }
package/src/reddit.ts CHANGED
@@ -27,10 +27,10 @@ export async function resolveReddit(input: ResolveContext): Promise<PostfetchRes
27
27
  const id = await postId(input.net, input.url);
28
28
  const token = await appToken(input.net);
29
29
  const post = await fetchPost(input.net, token, id);
30
+ // A text post is a valid result with metadata and no media — fetchPost has
31
+ // already thrown if the post itself is missing, so empty items here mean a
32
+ // self/text post, not a failure.
30
33
  const items = await mediaItems(input, post, id);
31
- if (items.length === 0) {
32
- throw new Error("Reddit media not found");
33
- }
34
34
  return { archiveFilename: filename(`reddit_${id}.zip`), id, items, metadata: redditMetadata(post), platform: "reddit" };
35
35
  }
36
36