@postfetch/core 0.2.2 → 0.2.4

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.
@@ -1,12 +1,48 @@
1
1
  import { type MediaItem, type PostfetchResult } from "./internal";
2
+ /** Options for {@link download}, {@link archive} and {@link toResponse}. */
2
3
  export type DownloadOptions = {
4
+ /** Custom `fetch` implementation. Defaults to the global `fetch`. */
3
5
  fetch?: typeof fetch;
4
6
  };
7
+ /** A zip archive produced by {@link archive}. */
5
8
  export type Archive = {
9
+ /** The zip bytes (store mode, built in-process). */
6
10
  bytes: Uint8Array;
11
+ /** Suggested download filename. */
7
12
  filename: string;
13
+ /** Always `"application/zip"`. */
8
14
  mime: "application/zip";
9
15
  };
16
+ /**
17
+ * Fetch a single media item from its CDN with the headers it requires.
18
+ *
19
+ * @param item A {@link MediaItem} from a {@link PostfetchResult}.
20
+ * @param options See {@link DownloadOptions}.
21
+ * @returns The upstream `Response`, ready to stream or buffer.
22
+ * @throws {PostfetchError} If the CDN responds with a non-OK status.
23
+ *
24
+ * @example
25
+ * ```ts
26
+ * const result = await postfetch(url);
27
+ * await Bun.write(result.items[0].filename, await download(result.items[0]));
28
+ * ```
29
+ */
10
30
  export declare function download(item: MediaItem, options?: DownloadOptions): Promise<Response>;
31
+ /**
32
+ * Download every item of a result and zip them in-process (store mode).
33
+ *
34
+ * @param result A {@link PostfetchResult}.
35
+ * @param options See {@link DownloadOptions}.
36
+ * @returns The zip bytes and a suggested filename.
37
+ */
11
38
  export declare function archive(result: PostfetchResult, options?: DownloadOptions): Promise<Archive>;
39
+ /**
40
+ * Turn a result into a ready-to-serve `Response`: a single item is streamed as
41
+ * its file, multiple items become a zip. Sets `content-disposition` and the
42
+ * `x-media-*` headers. This is what the showcase server and templates return.
43
+ *
44
+ * @param result A {@link PostfetchResult}.
45
+ * @param options See {@link DownloadOptions}.
46
+ * @returns A `Response` a server or edge function can return directly.
47
+ */
12
48
  export declare function toResponse(result: PostfetchResult, options?: DownloadOptions): Promise<Response>;
package/dist/index.d.ts CHANGED
@@ -1,3 +1,24 @@
1
+ /**
2
+ * Turn a public social post URL into its media.
3
+ *
4
+ * {@link postfetch} resolves Instagram, TikTok, YouTube, Facebook and X (Twitter)
5
+ * URLs to typed {@link MediaItem}s. Zero dependencies, only Web-standard APIs
6
+ * (`fetch`, `Response`, `URL`, `crypto`) — so it runs on Deno, Node, Bun,
7
+ * Cloudflare Workers and browsers alike. It only resolves the media; the
8
+ * {@link download}, {@link archive} and {@link toResponse} helpers turn a result
9
+ * into bytes or a `Response`.
10
+ *
11
+ * @example Resolve a reel and save the video
12
+ * ```ts
13
+ * import { postfetch, download } from "@postfetch/core";
14
+ *
15
+ * const result = await postfetch("https://www.instagram.com/reel/DZ0ixNxtvYq/");
16
+ * const [item] = result.items; // { kind: "video", url, headers, filename, ... }
17
+ * const response = await download(item);
18
+ * ```
19
+ *
20
+ * @module
21
+ */
1
22
  export { postfetch, detect, type PostfetchOptions } from "./postfetch";
2
23
  export { download, archive, toResponse, type Archive, type DownloadOptions } from "./download";
3
24
  export { PostfetchError } from "./internal";
@@ -1,18 +1,33 @@
1
+ /** A platform that {@link postfetch} can resolve. */
1
2
  export type Platform = "facebook" | "instagram" | "tiktok" | "twitter" | "youtube";
3
+ /** The kind of a {@link MediaItem}. */
2
4
  export type MediaKind = "audio" | "image" | "video";
5
+ /** A single resolved media file: a direct URL plus everything needed to fetch and name it. */
3
6
  export type MediaItem = {
7
+ /** Suggested download filename. */
4
8
  filename: string;
9
+ /** Headers required to fetch {@link MediaItem.url} from the CDN. */
5
10
  headers: HeadersInit;
11
+ /** Platform media id. */
6
12
  id: string;
13
+ /** Whether this item is a video, image or audio file. */
7
14
  kind: MediaKind;
15
+ /** MIME type of the media. */
8
16
  mime: string;
17
+ /** The platform this item came from. */
9
18
  platform: Platform;
19
+ /** Direct CDN URL of the media. */
10
20
  url: string;
11
21
  };
22
+ /** The result of resolving a post: its platform, id and one or more media items. */
12
23
  export type PostfetchResult = {
24
+ /** Suggested filename when zipping every item. */
13
25
  archiveFilename: string;
26
+ /** Platform post/media id. */
14
27
  id: string;
28
+ /** The post's media, in order. */
15
29
  items: MediaItem[];
30
+ /** The platform the post came from. */
16
31
  platform: Platform;
17
32
  };
18
33
  export type Net = (url: string, init?: RequestInit, attempts?: number) => Promise<Response>;
@@ -21,9 +36,19 @@ export type ResolveContext = {
21
36
  preferredWidth: number;
22
37
  url: string;
23
38
  };
39
+ /** An error carrying an HTTP status, so adapters can map failures onto responses. */
24
40
  export declare class PostfetchError extends Error {
41
+ /** HTTP status that best describes the failure. */
25
42
  readonly status: number;
26
- constructor(status: number, message: string);
43
+ /**
44
+ * Create an error tagged with an HTTP status.
45
+ *
46
+ * @param status HTTP status that best describes the failure.
47
+ * @param message Human-readable message.
48
+ */
49
+ constructor(
50
+ /** HTTP status that best describes the failure. */
51
+ status: number, message: string);
27
52
  }
28
53
  export type Json = Record<string, unknown>;
29
54
  export declare function createNet(baseFetch?: typeof fetch): Net;
@@ -1,7 +1,38 @@
1
1
  import { type Platform, type PostfetchResult } from "./internal";
2
+ /** Options for {@link postfetch}. */
2
3
  export type PostfetchOptions = {
4
+ /** Custom `fetch` implementation — inject one to unit-test resolvers offline. Defaults to the global `fetch`. */
3
5
  fetch?: typeof fetch;
6
+ /** Preferred media width in pixels; the closest available rendition is chosen. Defaults to `720`. */
4
7
  preferredWidth?: number;
5
8
  };
9
+ /**
10
+ * Resolve a public social post URL to its media.
11
+ *
12
+ * Detects the platform from the URL host, then returns the post's media as typed
13
+ * {@link MediaItem}s — direct CDN URLs plus the headers needed to fetch them. It
14
+ * performs no side effects beyond the lookup; use {@link download}, {@link archive}
15
+ * or {@link toResponse} to turn the result into bytes or a `Response`.
16
+ *
17
+ * @param url A post URL from Instagram, TikTok, YouTube, Facebook or X.
18
+ * @param options See {@link PostfetchOptions}.
19
+ * @returns The resolved post and its media items.
20
+ * @throws {PostfetchError} If the URL is empty or its host is unsupported.
21
+ *
22
+ * @example
23
+ * ```ts
24
+ * import { postfetch } from "@postfetch/core";
25
+ *
26
+ * const result = await postfetch("https://vt.tiktok.com/ZSxpHvCUM/");
27
+ * for (const item of result.items) console.log(item.kind, item.url);
28
+ * ```
29
+ */
6
30
  export declare function postfetch(url: string, options?: PostfetchOptions): Promise<PostfetchResult>;
31
+ /**
32
+ * Detect which platform a URL belongs to, from its host.
33
+ *
34
+ * @param url Any URL.
35
+ * @returns The matched {@link Platform}.
36
+ * @throws {PostfetchError} If the host is not a supported platform.
37
+ */
7
38
  export declare function detect(url: string): Platform;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@postfetch/core",
3
- "version": "0.2.2",
3
+ "version": "0.2.4",
4
4
  "description": "Zero-dependency typed core that turns Facebook, Instagram, TikTok, X and YouTube post URLs into media files.",
5
5
  "license": "MIT",
6
6
  "type": "module",
package/src/download.ts CHANGED
@@ -1,16 +1,36 @@
1
1
  import { createNet, PostfetchError, type MediaItem, type PostfetchResult } from "./internal";
2
2
  import { zip } from "./zip";
3
3
 
4
+ /** Options for {@link download}, {@link archive} and {@link toResponse}. */
4
5
  export type DownloadOptions = {
6
+ /** Custom `fetch` implementation. Defaults to the global `fetch`. */
5
7
  fetch?: typeof fetch;
6
8
  };
7
9
 
10
+ /** A zip archive produced by {@link archive}. */
8
11
  export type Archive = {
12
+ /** The zip bytes (store mode, built in-process). */
9
13
  bytes: Uint8Array;
14
+ /** Suggested download filename. */
10
15
  filename: string;
16
+ /** Always `"application/zip"`. */
11
17
  mime: "application/zip";
12
18
  };
13
19
 
20
+ /**
21
+ * Fetch a single media item from its CDN with the headers it requires.
22
+ *
23
+ * @param item A {@link MediaItem} from a {@link PostfetchResult}.
24
+ * @param options See {@link DownloadOptions}.
25
+ * @returns The upstream `Response`, ready to stream or buffer.
26
+ * @throws {PostfetchError} If the CDN responds with a non-OK status.
27
+ *
28
+ * @example
29
+ * ```ts
30
+ * const result = await postfetch(url);
31
+ * await Bun.write(result.items[0].filename, await download(result.items[0]));
32
+ * ```
33
+ */
14
34
  export async function download(item: MediaItem, options: DownloadOptions = {}): Promise<Response> {
15
35
  const net = createNet(options.fetch ?? globalThis.fetch);
16
36
  const response = await net(item.url, { headers: item.headers });
@@ -20,6 +40,13 @@ export async function download(item: MediaItem, options: DownloadOptions = {}):
20
40
  return response;
21
41
  }
22
42
 
43
+ /**
44
+ * Download every item of a result and zip them in-process (store mode).
45
+ *
46
+ * @param result A {@link PostfetchResult}.
47
+ * @param options See {@link DownloadOptions}.
48
+ * @returns The zip bytes and a suggested filename.
49
+ */
23
50
  export async function archive(result: PostfetchResult, options: DownloadOptions = {}): Promise<Archive> {
24
51
  const net = createNet(options.fetch ?? globalThis.fetch);
25
52
  const files = await Promise.all(
@@ -34,6 +61,15 @@ export async function archive(result: PostfetchResult, options: DownloadOptions
34
61
  return { bytes: zip(files), filename: result.archiveFilename, mime: "application/zip" };
35
62
  }
36
63
 
64
+ /**
65
+ * Turn a result into a ready-to-serve `Response`: a single item is streamed as
66
+ * its file, multiple items become a zip. Sets `content-disposition` and the
67
+ * `x-media-*` headers. This is what the showcase server and templates return.
68
+ *
69
+ * @param result A {@link PostfetchResult}.
70
+ * @param options See {@link DownloadOptions}.
71
+ * @returns A `Response` a server or edge function can return directly.
72
+ */
37
73
  export async function toResponse(result: PostfetchResult, options: DownloadOptions = {}): Promise<Response> {
38
74
  if (result.items.length === 1) {
39
75
  return singleResponse(result.items[0], options);
package/src/index.ts CHANGED
@@ -1,3 +1,24 @@
1
+ /**
2
+ * Turn a public social post URL into its media.
3
+ *
4
+ * {@link postfetch} resolves Instagram, TikTok, YouTube, Facebook and X (Twitter)
5
+ * URLs to typed {@link MediaItem}s. Zero dependencies, only Web-standard APIs
6
+ * (`fetch`, `Response`, `URL`, `crypto`) — so it runs on Deno, Node, Bun,
7
+ * Cloudflare Workers and browsers alike. It only resolves the media; the
8
+ * {@link download}, {@link archive} and {@link toResponse} helpers turn a result
9
+ * into bytes or a `Response`.
10
+ *
11
+ * @example Resolve a reel and save the video
12
+ * ```ts
13
+ * import { postfetch, download } from "@postfetch/core";
14
+ *
15
+ * const result = await postfetch("https://www.instagram.com/reel/DZ0ixNxtvYq/");
16
+ * const [item] = result.items; // { kind: "video", url, headers, filename, ... }
17
+ * const response = await download(item);
18
+ * ```
19
+ *
20
+ * @module
21
+ */
1
22
  export { postfetch, detect, type PostfetchOptions } from "./postfetch";
2
23
  export { download, archive, toResponse, type Archive, type DownloadOptions } from "./download";
3
24
  export { PostfetchError } from "./internal";
package/src/internal.ts CHANGED
@@ -1,21 +1,36 @@
1
+ /** A platform that {@link postfetch} can resolve. */
1
2
  export type Platform = "facebook" | "instagram" | "tiktok" | "twitter" | "youtube";
2
3
 
4
+ /** The kind of a {@link MediaItem}. */
3
5
  export type MediaKind = "audio" | "image" | "video";
4
6
 
7
+ /** A single resolved media file: a direct URL plus everything needed to fetch and name it. */
5
8
  export type MediaItem = {
9
+ /** Suggested download filename. */
6
10
  filename: string;
11
+ /** Headers required to fetch {@link MediaItem.url} from the CDN. */
7
12
  headers: HeadersInit;
13
+ /** Platform media id. */
8
14
  id: string;
15
+ /** Whether this item is a video, image or audio file. */
9
16
  kind: MediaKind;
17
+ /** MIME type of the media. */
10
18
  mime: string;
19
+ /** The platform this item came from. */
11
20
  platform: Platform;
21
+ /** Direct CDN URL of the media. */
12
22
  url: string;
13
23
  };
14
24
 
25
+ /** The result of resolving a post: its platform, id and one or more media items. */
15
26
  export type PostfetchResult = {
27
+ /** Suggested filename when zipping every item. */
16
28
  archiveFilename: string;
29
+ /** Platform post/media id. */
17
30
  id: string;
31
+ /** The post's media, in order. */
18
32
  items: MediaItem[];
33
+ /** The platform the post came from. */
19
34
  platform: Platform;
20
35
  };
21
36
 
@@ -27,8 +42,16 @@ export type ResolveContext = {
27
42
  url: string;
28
43
  };
29
44
 
45
+ /** An error carrying an HTTP status, so adapters can map failures onto responses. */
30
46
  export class PostfetchError extends Error {
47
+ /**
48
+ * Create an error tagged with an HTTP status.
49
+ *
50
+ * @param status HTTP status that best describes the failure.
51
+ * @param message Human-readable message.
52
+ */
31
53
  constructor(
54
+ /** HTTP status that best describes the failure. */
32
55
  readonly status: number,
33
56
  message: string,
34
57
  ) {
package/src/postfetch.ts CHANGED
@@ -5,11 +5,35 @@ import { resolveTiktok } from "./tiktok";
5
5
  import { resolveTwitter } from "./twitter";
6
6
  import { resolveYoutube } from "./youtube";
7
7
 
8
+ /** Options for {@link postfetch}. */
8
9
  export type PostfetchOptions = {
10
+ /** Custom `fetch` implementation — inject one to unit-test resolvers offline. Defaults to the global `fetch`. */
9
11
  fetch?: typeof fetch;
12
+ /** Preferred media width in pixels; the closest available rendition is chosen. Defaults to `720`. */
10
13
  preferredWidth?: number;
11
14
  };
12
15
 
16
+ /**
17
+ * Resolve a public social post URL to its media.
18
+ *
19
+ * Detects the platform from the URL host, then returns the post's media as typed
20
+ * {@link MediaItem}s — direct CDN URLs plus the headers needed to fetch them. It
21
+ * performs no side effects beyond the lookup; use {@link download}, {@link archive}
22
+ * or {@link toResponse} to turn the result into bytes or a `Response`.
23
+ *
24
+ * @param url A post URL from Instagram, TikTok, YouTube, Facebook or X.
25
+ * @param options See {@link PostfetchOptions}.
26
+ * @returns The resolved post and its media items.
27
+ * @throws {PostfetchError} If the URL is empty or its host is unsupported.
28
+ *
29
+ * @example
30
+ * ```ts
31
+ * import { postfetch } from "@postfetch/core";
32
+ *
33
+ * const result = await postfetch("https://vt.tiktok.com/ZSxpHvCUM/");
34
+ * for (const item of result.items) console.log(item.kind, item.url);
35
+ * ```
36
+ */
13
37
  export async function postfetch(url: string, options: PostfetchOptions = {}): Promise<PostfetchResult> {
14
38
  const trimmed = url.trim();
15
39
  if (trimmed.length === 0) {
@@ -34,6 +58,13 @@ export async function postfetch(url: string, options: PostfetchOptions = {}): Pr
34
58
  }
35
59
  }
36
60
 
61
+ /**
62
+ * Detect which platform a URL belongs to, from its host.
63
+ *
64
+ * @param url Any URL.
65
+ * @returns The matched {@link Platform}.
66
+ * @throws {PostfetchError} If the host is not a supported platform.
67
+ */
37
68
  export function detect(url: string): Platform {
38
69
  const host = asUrl(url).hostname;
39
70
  if (host.includes("tiktok.com")) {