@apicity/meta 0.1.0-alpha.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Justin Tanner
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,356 @@
1
+ # @apicity/meta
2
+
3
+ [![npm](https://img.shields.io/npm/v/@apicity/meta?color=cb0000)](https://www.npmjs.com/package/@apicity/meta)
4
+ [![zero dependencies](https://img.shields.io/badge/dependencies-0-brightgreen)](package.json)
5
+ [![TypeScript](https://img.shields.io/badge/TypeScript-strict-blue?logo=typescript&logoColor=white)](tsconfig.json)
6
+
7
+ Instagram Graph API provider for posting reels via the public-URL flow (graph.instagram.com).
8
+
9
+ ## Installation
10
+
11
+ ```bash
12
+ npm install @apicity/meta
13
+ # or
14
+ pnpm add @apicity/meta
15
+ ```
16
+
17
+ ## Quick Start
18
+
19
+ ```typescript
20
+ import { meta as createMeta } from "@apicity/meta";
21
+
22
+ const meta = createMeta({ apiKey: process.env.META_API_KEY! });
23
+ ```
24
+
25
+ ## Setup
26
+
27
+ Instagram requires a **long-lived (60-day) user access token** from the
28
+ Instagram Login OAuth flow. The token is bound to a specific Instagram
29
+ Business or Creator account; personal accounts have no programmatic
30
+ publishing access.
31
+
32
+ ### 1. Configure your Meta App
33
+
34
+ Open [developers.facebook.com](https://developers.facebook.com), create a
35
+ new app (type: **Business**), then add the **Instagram** product.
36
+ Choose **API setup with Instagram login** and configure:
37
+
38
+ - Valid OAuth Redirect URI: `http://127.0.0.1:8765/callback`
39
+ - Permissions: `instagram_business_basic` + `instagram_business_content_publish`
40
+ - Save the **Instagram App ID** and **Instagram App Secret**
41
+
42
+ ### 2. Verify Instagram account type
43
+
44
+ The Instagram account you're publishing to must be **Business** or
45
+ **Creator** (not Personal). Switch in the Instagram mobile app under
46
+ Settings → Account type and tools. No Facebook Page link is required
47
+ for the Instagram Login flow.
48
+
49
+ ### 3. Mint a long-lived access token
50
+
51
+ Save the script below as `mint-ig-token.mjs` and run it:
52
+
53
+ ```bash
54
+ IG_CLIENT_ID=<your-instagram-app-id> \
55
+ IG_CLIENT_SECRET=<your-instagram-app-secret> \
56
+ node mint-ig-token.mjs
57
+ ```
58
+
59
+ It prints an authorize URL — open it, click **Allow**, and the helper
60
+ captures the redirect on `127.0.0.1:8765`, exchanges the code for a
61
+ short-lived token, then upgrades it to a long-lived (60-day) token and
62
+ prints `{ access_token, user_id, expires_in }`. Save both `access_token`
63
+ and `user_id` — you need both to call the API.
64
+
65
+ <details>
66
+ <summary><code>mint-ig-token.mjs</code> — zero-dep OAuth 2.0 helper</summary>
67
+
68
+ ```javascript
69
+ import http from "node:http";
70
+ import crypto from "node:crypto";
71
+
72
+ const CLIENT_ID = process.env.IG_CLIENT_ID;
73
+ const CLIENT_SECRET = process.env.IG_CLIENT_SECRET;
74
+ const REDIRECT = "http://127.0.0.1:8765/callback";
75
+ const SCOPES = [
76
+ "instagram_business_basic",
77
+ "instagram_business_content_publish",
78
+ ].join(",");
79
+
80
+ if (!CLIENT_ID || !CLIENT_SECRET) {
81
+ console.error("Set IG_CLIENT_ID and IG_CLIENT_SECRET");
82
+ process.exit(1);
83
+ }
84
+
85
+ const state = crypto.randomBytes(16).toString("hex");
86
+ const authURL = new URL("https://www.instagram.com/oauth/authorize");
87
+ authURL.searchParams.set("client_id", CLIENT_ID);
88
+ authURL.searchParams.set("redirect_uri", REDIRECT);
89
+ authURL.searchParams.set("response_type", "code");
90
+ authURL.searchParams.set("scope", SCOPES);
91
+ authURL.searchParams.set("state", state);
92
+
93
+ console.log("Open this URL and click Allow:\n" + authURL.toString());
94
+
95
+ const server = http.createServer(async (req, res) => {
96
+ const url = new URL(req.url, REDIRECT);
97
+ if (!url.pathname.startsWith("/callback")) {
98
+ res.writeHead(404).end();
99
+ return;
100
+ }
101
+ const code = url.searchParams.get("code");
102
+ if (!code || url.searchParams.get("state") !== state) {
103
+ res.writeHead(400).end("bad state");
104
+ server.close();
105
+ process.exit(1);
106
+ }
107
+
108
+ // 1. short-lived
109
+ const shortRes = await fetch(
110
+ "https://api.instagram.com/oauth/access_token",
111
+ {
112
+ method: "POST",
113
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
114
+ body: new URLSearchParams({
115
+ client_id: CLIENT_ID,
116
+ client_secret: CLIENT_SECRET,
117
+ grant_type: "authorization_code",
118
+ redirect_uri: REDIRECT,
119
+ code,
120
+ }),
121
+ }
122
+ );
123
+ const short = await shortRes.json();
124
+
125
+ // 2. long-lived (60-day)
126
+ const longURL = new URL("https://graph.instagram.com/access_token");
127
+ longURL.searchParams.set("grant_type", "ig_exchange_token");
128
+ longURL.searchParams.set("client_secret", CLIENT_SECRET);
129
+ longURL.searchParams.set("access_token", short.access_token);
130
+ const longRes = await fetch(longURL);
131
+ const long = await longRes.json();
132
+
133
+ console.log(JSON.stringify({
134
+ access_token: long.access_token,
135
+ expires_in: long.expires_in,
136
+ user_id: short.user_id,
137
+ }, null, 2));
138
+ res.writeHead(200).end("Authorized — check your terminal.");
139
+ server.close();
140
+ });
141
+
142
+ server.listen(8765, "127.0.0.1");
143
+ ```
144
+
145
+ </details>
146
+
147
+ ### 4. Use the token
148
+
149
+ ```typescript
150
+ import { meta as createMeta } from "@apicity/meta";
151
+
152
+ const meta = createIg({ accessToken: process.env.IG_ACCESS_TOKEN });
153
+ const igUserId = process.env.IG_USER_ID;
154
+
155
+ // Public-URL flow: host the mp4 somewhere (e.g. via @apicity/free-media-upload) and
156
+ // pass its URL. Meta GETs the video and processes it asynchronously.
157
+ const container = await ig.post.v25.media(igUserId, {
158
+ media_type: "REELS",
159
+ video_url: "https://example.com/clip.mp4",
160
+ caption: "hello from @apicity/meta",
161
+ });
162
+
163
+ // Poll until the container is ready.
164
+ let status = "IN_PROGRESS";
165
+ while (status === "IN_PROGRESS") {
166
+ await new Promise((r) => setTimeout(r, 5000));
167
+ const s = await ig.get.v25.container(container.id, {
168
+ fields: "status_code,status",
169
+ });
170
+ status = s.status_code ?? "FINISHED";
171
+ }
172
+
173
+ // Publish.
174
+ const post = await ig.post.v25.mediaPublish(igUserId, {
175
+ creation_id: container.id,
176
+ });
177
+ console.log(post.id);
178
+ ```
179
+
180
+ ## Real-world example: publish a Reel via the public-URL flow
181
+
182
+ Instagram's Graph API doesn't take video bytes directly — Meta needs a
183
+ publicly reachable URL it can `GET` and transcode asynchronously. The
184
+ snippet below chains `@apicity/free-media-upload` (catbox public hosting, free + zero
185
+ auth) into `@apicity/meta` to land an `mp4` on disk as a published Reel,
186
+ mirroring
187
+ [`tests/integration/ig-post-video.test.ts`](../../../tests/integration/ig-post-video.test.ts)
188
+ step-for-step. The catbox upload replays against
189
+ [`tests/recordings/free_2578706139/`](../../../tests/recordings/free_2578706139/);
190
+ the IG calls land in `tests/recordings/ig_*/post-video_*/recording.har`
191
+ once a Business/Creator account's `IG_ACCESS_TOKEN` is recorded.
192
+
193
+ ```typescript
194
+ import { readFileSync } from "node:fs";
195
+ import { meta as createMeta } from "@apicity/meta";
196
+ import { freeMediaUpload as createFreeMediaUpload } from "@apicity/free-media-upload";
197
+
198
+ const meta = createIg({ accessToken: process.env.IG_ACCESS_TOKEN! });
199
+ const igUserId = process.env.IG_USER_ID!; // 17-digit numeric, e.g. "17841471234567890"
200
+
201
+ // 1. Host the mp4 publicly. catbox.moe is auth-free and persistent —
202
+ // Meta's transcode worker will fetch this URL once during step 2,
203
+ // so any host that returns the bytes within ~30s works (S3 presigned
204
+ // URL, R2, your own CDN). @apicity/free-media-upload wraps the multipart upload
205
+ // and returns the resolved file URL as a string.
206
+ const bytes = readFileSync("./jump.mp4");
207
+ const blob = new Blob([bytes], { type: "video/mp4" });
208
+
209
+ const freeMediaUpload = createFree({});
210
+ const videoUrl = await free.catbox.upload({
211
+ file: blob,
212
+ filename: "jump.mp4",
213
+ });
214
+ console.log(videoUrl);
215
+ // → "https://files.catbox.moe/nn9sei.mp4"
216
+ // catbox returns a permanent public URL of the form
217
+ // `https://files.catbox.moe/<6char>.<ext>` — that's what Meta will GET.
218
+
219
+ // 2. Create a media container. For Reels you must pass `media_type:
220
+ // "REELS"` (NOT "VIDEO" — that's the legacy IGTV path Meta
221
+ // deprecated in 2024). The container is a server-side handle: Meta
222
+ // queues the transcode against `video_url` and returns its id
223
+ // immediately. Containers expire 24h after creation if you don't
224
+ // publish them.
225
+ const container = await ig.post.v25.media(igUserId, {
226
+ media_type: "REELS",
227
+ video_url: videoUrl,
228
+ caption: "jump #reels",
229
+ });
230
+ console.log(container.id);
231
+ // → "17889012345678901" (17-digit container id)
232
+
233
+ // 3. Poll the container's status_code until it leaves IN_PROGRESS.
234
+ // The state machine is IN_PROGRESS → FINISHED on success;
235
+ // FINISHED is the only state media_publish accepts. ERROR and
236
+ // EXPIRED are terminal failure states; PUBLISHED is what you'll
237
+ // see if you re-poll AFTER calling media_publish. The `fields`
238
+ // query param is required — by default the GET only returns `id`.
239
+ let statusCode: string = "IN_PROGRESS";
240
+ while (statusCode === "IN_PROGRESS") {
241
+ await new Promise((r) => setTimeout(r, 5000));
242
+ const s = await ig.get.v25.container(container.id, {
243
+ fields: "status_code,status",
244
+ });
245
+ statusCode = s.status_code ?? "FINISHED";
246
+ if (statusCode === "ERROR" || statusCode === "EXPIRED") {
247
+ throw new Error(`container ${container.id} ${statusCode}: ${s.status}`);
248
+ }
249
+ }
250
+ // statusCode → "FINISHED"
251
+ // s.status → "Finished: Media is ready to be published."
252
+
253
+ // 4. Publish. media_publish takes the container id (NOT the media url)
254
+ // and returns the new post's id — that's the permanent ig_id you'd
255
+ // use to construct an https://www.instagram.com/reel/<shortcode>/
256
+ // URL or to query insights later via Graph API.
257
+ const post = await ig.post.v25.mediaPublish(igUserId, {
258
+ creation_id: container.id,
259
+ });
260
+ console.log(post.id);
261
+ // → "17912345678901234" (17-digit post id, distinct from container id)
262
+ ```
263
+
264
+ **Notes**
265
+
266
+ - Meta requires a **Business** or **Creator** Instagram account plus a
267
+ Meta App approved for `instagram_business_content_publish`. Personal
268
+ accounts get `190` ("Invalid OAuth access token") even with a
269
+ syntactically valid token.
270
+ - `video_url` must be reachable from Meta's IPs and serve `Content-Type:
271
+ video/mp4`. Common gotchas: presigned S3 URLs that expire before the
272
+ transcoder pulls, hosts that require a `User-Agent`, and CDNs that
273
+ redirect to a different origin. catbox.moe sidesteps all three.
274
+ - The Reel itself must satisfy Meta's Reel constraints — 9:16 aspect,
275
+ 3–90s duration, ≤ 1GB, H.264 video, AAC audio. Mismatches surface as
276
+ `status_code: "ERROR"` during the poll, with the human-readable
277
+ reason in `status` (e.g. `"Error: The video is too short."`).
278
+ - Containers and posts use **distinct** 17-digit ids. The container id
279
+ is throwaway — you only need it for the GET poll and the subsequent
280
+ `media_publish` `creation_id`. The post id is permanent and survives
281
+ user deletion of the post.
282
+ - Errors throw `MetaError` with `status` (HTTP code), `body` (the parsed
283
+ Meta error envelope), and an optional `code`. Meta's two error shapes
284
+ — `error.error_user_msg` for user-facing validation and `error.message`
285
+ for everything else — are both surfaced in `MetaError.message`, so a
286
+ single `try/catch` reads naturally.
287
+
288
+ ## API Reference
289
+
290
+ 3 endpoints across 3 groups. Each method mirrors an upstream URL path.
291
+
292
+ ### container
293
+
294
+ <details>
295
+ <summary><code>GET</code> <b><code>meta.v25.container</code></b></summary>
296
+
297
+ <code>GET https://graph.instagram.com/v25.0/{containerId}{query}</code>
298
+
299
+ [Upstream docs ↗](https://developers.facebook.com/docs/instagram-platform/reference/ig-container/)
300
+
301
+ ```typescript
302
+ const res = await meta.v25.container({ /* ... */ });
303
+ ```
304
+
305
+ Source: [`packages/provider/meta/src/meta.ts`](src/meta.ts)
306
+
307
+ </details>
308
+
309
+ ### media
310
+
311
+ <details>
312
+ <summary><code>POST</code> <b><code>meta.v25.media</code></b></summary>
313
+
314
+ <code>POST https://graph.instagram.com/v25.0/{igUserId}/media</code>
315
+
316
+ [Upstream docs ↗](https://developers.facebook.com/docs/instagram-platform/instagram-graph-api/reference/ig-user/media/)
317
+
318
+ ```typescript
319
+ const res = await meta.v25.media({ /* ... */ });
320
+ ```
321
+
322
+ Source: [`packages/provider/meta/src/meta.ts`](src/meta.ts)
323
+
324
+ </details>
325
+
326
+ ### mediaPublish
327
+
328
+ <details>
329
+ <summary><code>POST</code> <b><code>meta.v25.mediaPublish</code></b></summary>
330
+
331
+ <code>POST https://graph.instagram.com/v25.0/{igUserId}/media_publish</code>
332
+
333
+ [Upstream docs ↗](https://developers.facebook.com/docs/instagram-platform/instagram-graph-api/reference/ig-user/media_publish/)
334
+
335
+ ```typescript
336
+ const res = await meta.v25.mediaPublish({ /* ... */ });
337
+ ```
338
+
339
+ Source: [`packages/provider/meta/src/meta.ts`](src/meta.ts)
340
+
341
+ </details>
342
+
343
+ ## Middleware
344
+
345
+ ```typescript
346
+ import { meta as createMeta, withRetry } from "@apicity/meta";
347
+
348
+ const meta = createMeta({ apiKey: process.env.META_API_KEY! });
349
+ const models = withRetry(meta.get.v1.models, { retries: 3 });
350
+ ```
351
+
352
+ Part of the [apicity](https://github.com/justintanner/apicity) monorepo.
353
+
354
+ ## License
355
+
356
+ MIT — see [LICENSE](LICENSE).
@@ -0,0 +1,8 @@
1
+ export interface EndpointExample {
2
+ source: string;
3
+ payload: unknown;
4
+ }
5
+ declare const EXAMPLES: Record<string, EndpointExample>;
6
+ export default EXAMPLES;
7
+ export declare function attachExamples<T>(provider: T): T;
8
+ //# sourceMappingURL=example.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"example.d.ts","sourceRoot":"","sources":["../../src/example.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,QAAA,MAAM,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAS7C,CAAC;AAEF,eAAe,QAAQ,CAAC;AAOxB,wBAAgB,cAAc,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,GAAG,CAAC,CA6ChD"}
@@ -0,0 +1,94 @@
1
+ // Auto-generated by `pnpm run gen:examples` — do not edit by hand.
2
+ // Source: tests/recordings/<provider>_*/<test>_*/recording.har
3
+ //
4
+ // Each entry is the green-path payload for one endpoint, mined from a real
5
+ // integration-test recording. `attachExamples` walks the provider tree and
6
+ // hangs the matching entry off each endpoint function as `.example`.
7
+ const EXAMPLES = {
8
+ "POST v25.media": {
9
+ "source": "meta/media-create",
10
+ "payload": {
11
+ "media_type": "REELS",
12
+ "video_url": "https://files.catbox.moe/example-reel.mp4",
13
+ "caption": "hello from @apicity/meta"
14
+ }
15
+ }
16
+ };
17
+ export default EXAMPLES;
18
+ // Walks each "<METHOD> <dotPath>" key onto the provider's tree. Tries the
19
+ // standard `provider.<method>.<dotPath>` shape first, then a few fallbacks
20
+ // to cover providers with non-standard layouts (fal's `.run.` namespace,
21
+ // kie's sub-providers, `free`'s flat root). Returns the same provider for
22
+ // drop-in use as `return attachExamples({ ... });`.
23
+ export function attachExamples(provider) {
24
+ const root = provider;
25
+ const HTTP_KEYS = new Set(["post", "get", "put", "delete", "patch", "head"]);
26
+ for (const [key, example] of Object.entries(EXAMPLES)) {
27
+ const sp = key.indexOf(" ");
28
+ if (sp < 0)
29
+ continue;
30
+ const method = key.slice(0, sp).toLowerCase();
31
+ const segs = key.slice(sp + 1).split(".");
32
+ const candidates = [
33
+ root[method],
34
+ root[method]?.run,
35
+ root,
36
+ ];
37
+ if (segs.length > 1) {
38
+ const sub = root[segs[0]];
39
+ if (sub && typeof sub === "object") {
40
+ const subMethod = sub[method];
41
+ if (subMethod)
42
+ candidates.push({ __nested: subMethod, __segs: segs.slice(1) });
43
+ }
44
+ }
45
+ let attached = false;
46
+ for (const c of candidates) {
47
+ const fn = walkToFn(c, segs);
48
+ if (fn) {
49
+ Object.assign(fn, { example });
50
+ attached = true;
51
+ break;
52
+ }
53
+ }
54
+ if (attached)
55
+ continue;
56
+ for (const k of Object.keys(root)) {
57
+ if (HTTP_KEYS.has(k))
58
+ continue;
59
+ if (!segs.includes(k))
60
+ continue;
61
+ const sub = root[k];
62
+ if (!sub || typeof sub !== "object")
63
+ continue;
64
+ const subMethod = sub[method];
65
+ if (!subMethod)
66
+ continue;
67
+ const fn = walkToFn(subMethod, segs);
68
+ if (fn) {
69
+ Object.assign(fn, { example });
70
+ break;
71
+ }
72
+ }
73
+ }
74
+ return provider;
75
+ }
76
+ function walkToFn(start, segs) {
77
+ if (start && typeof start === "object" && "__nested" in start) {
78
+ const wrapper = start;
79
+ return walkToFn(wrapper.__nested, wrapper.__segs);
80
+ }
81
+ let cur = start;
82
+ for (const seg of segs) {
83
+ if (cur === null || cur === undefined)
84
+ return null;
85
+ const t = typeof cur;
86
+ if (t !== "object" && t !== "function")
87
+ return null;
88
+ cur = cur[seg];
89
+ }
90
+ return typeof cur === "function"
91
+ ? cur
92
+ : null;
93
+ }
94
+ //# sourceMappingURL=example.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"example.js","sourceRoot":"","sources":["../../src/example.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ export { meta } from "./meta";
2
+ export { MetaError } from "./types";
3
+ export type { MetaOptions, MetaMediaCreateRequest, MetaMediaCreateResponse, MetaMediaCreateMethod, MetaContainerStatusCode, MetaContainerStatusQuery, MetaContainerStatusResponse, MetaContainerStatusMethod, MetaMediaPublishRequest, MetaMediaPublishResponse, MetaMediaPublishMethod, MetaPostV25Namespace, MetaPostNamespace, MetaGetV25Namespace, MetaGetNamespace, MetaProvider, } from "./types";
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAE9B,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEpC,YAAY,EACV,WAAW,EACX,sBAAsB,EACtB,uBAAuB,EACvB,qBAAqB,EACrB,uBAAuB,EACvB,wBAAwB,EACxB,2BAA2B,EAC3B,yBAAyB,EACzB,uBAAuB,EACvB,wBAAwB,EACxB,sBAAsB,EACtB,oBAAoB,EACpB,iBAAiB,EACjB,mBAAmB,EACnB,gBAAgB,EAChB,YAAY,GACb,MAAM,SAAS,CAAC"}
@@ -0,0 +1,3 @@
1
+ export { meta } from "./meta.js";
2
+ export { MetaError } from "./types.js";
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAE9B,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { MetaOptions, MetaProvider } from "./types";
2
+ export declare function meta(opts: MetaOptions): MetaProvider;
3
+ //# sourceMappingURL=meta.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"meta.d.ts","sourceRoot":"","sources":["../../src/meta.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,WAAW,EAOX,YAAY,EAEb,MAAM,SAAS,CAAC;AAOjB,wBAAgB,IAAI,CAAC,IAAI,EAAE,WAAW,GAAG,YAAY,CAiKpD"}
@@ -0,0 +1,104 @@
1
+ import { MetaError, } from "./types.js";
2
+ import { MetaMediaCreateRequestSchema, MetaMediaPublishRequestSchema, } from "./zod.js";
3
+ import { attachExamples } from "./example.js";
4
+ export function meta(opts) {
5
+ const baseURL = opts.baseURL ?? "https://graph.instagram.com";
6
+ const doFetch = opts.fetch ?? fetch;
7
+ const timeout = opts.timeout ?? 30000;
8
+ function attachAbortHandler(signal, controller) {
9
+ if (signal.aborted) {
10
+ controller.abort();
11
+ return;
12
+ }
13
+ signal.addEventListener("abort", () => controller.abort(), { once: true });
14
+ }
15
+ // Meta Graph API errors come as `{ error: { message, type, code, ... } }`
16
+ // for the legacy shape, with a top-level `error_user_msg` available on
17
+ // some validation failures. Surface whichever the server sent so the
18
+ // caller sees the actual reason rather than a generic "IG API error: 400".
19
+ function formatErrorMessage(status, body) {
20
+ if (typeof body === "object" && body !== null) {
21
+ const b = body;
22
+ if (b.error?.error_user_msg)
23
+ return `IG API error ${status}: ${b.error.error_user_msg}`;
24
+ if (b.error?.message)
25
+ return `IG API error ${status}: ${b.error.message}`;
26
+ }
27
+ return `IG API error: ${status}`;
28
+ }
29
+ async function makeJsonRequest(method, path, body, signal) {
30
+ const controller = new AbortController();
31
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
32
+ if (signal) {
33
+ attachAbortHandler(signal, controller);
34
+ }
35
+ try {
36
+ const headers = {
37
+ Authorization: `Bearer ${opts.accessToken}`,
38
+ };
39
+ const init = {
40
+ method,
41
+ headers,
42
+ signal: controller.signal,
43
+ };
44
+ if (body !== undefined) {
45
+ headers["Content-Type"] = "application/json";
46
+ init.body = JSON.stringify(body);
47
+ }
48
+ const res = await doFetch(`${baseURL}${path}`, init);
49
+ clearTimeout(timeoutId);
50
+ if (!res.ok) {
51
+ let resBody = null;
52
+ try {
53
+ resBody = await res.json();
54
+ }
55
+ catch {
56
+ // ignore parse errors
57
+ }
58
+ throw new MetaError(formatErrorMessage(res.status, resBody), res.status, resBody);
59
+ }
60
+ return (await res.json());
61
+ }
62
+ catch (error) {
63
+ clearTimeout(timeoutId);
64
+ if (error instanceof MetaError)
65
+ throw error;
66
+ throw new MetaError(`IG request failed: ${error}`, 500);
67
+ }
68
+ }
69
+ // sig-ok: numeric URL segments (`/v25.0/`) become identifier-safe (`v25`)
70
+ // POST https://graph.instagram.com/v25.0/{igUserId}/media
71
+ // Docs: https://developers.facebook.com/docs/instagram-platform/instagram-graph-api/reference/ig-user/media/
72
+ const mediaCreate = Object.assign(async (igUserId, req, signal) => {
73
+ return makeJsonRequest("POST", `/v25.0/${encodeURIComponent(igUserId)}/media`, req, signal);
74
+ }, { schema: MetaMediaCreateRequestSchema });
75
+ // sig-ok: numeric URL segments (`/v25.0/`) become identifier-safe (`v25`)
76
+ // GET https://graph.instagram.com/v25.0/{containerId}{query}
77
+ // Docs: https://developers.facebook.com/docs/instagram-platform/reference/ig-container/
78
+ async function containerStatus(containerId, params, signal) {
79
+ const query = params?.fields
80
+ ? `?fields=${encodeURIComponent(params.fields)}`
81
+ : "";
82
+ return makeJsonRequest("GET", `/v25.0/${encodeURIComponent(containerId)}${query}`, undefined, signal);
83
+ }
84
+ // sig-ok: numeric URL segments (`/v25.0/`) become identifier-safe (`v25`)
85
+ // POST https://graph.instagram.com/v25.0/{igUserId}/media_publish
86
+ // Docs: https://developers.facebook.com/docs/instagram-platform/instagram-graph-api/reference/ig-user/media_publish/
87
+ const mediaPublish = Object.assign(async (igUserId, req, signal) => {
88
+ return makeJsonRequest("POST", `/v25.0/${encodeURIComponent(igUserId)}/media_publish`, req, signal);
89
+ }, { schema: MetaMediaPublishRequestSchema });
90
+ return attachExamples({
91
+ post: {
92
+ v25: {
93
+ media: mediaCreate,
94
+ mediaPublish,
95
+ },
96
+ },
97
+ get: {
98
+ v25: {
99
+ container: containerStatus,
100
+ },
101
+ },
102
+ });
103
+ }
104
+ //# sourceMappingURL=meta.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"meta.js","sourceRoot":"","sources":["../../src/meta.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,55 @@
1
+ import type { z } from "zod";
2
+ import type { MetaMediaCreateRequest, MetaMediaPublishRequest } from "./zod";
3
+ export type { MetaOptions, MetaMediaCreateRequest, MetaMediaPublishRequest, } from "./zod";
4
+ export declare class MetaError extends Error {
5
+ readonly status: number;
6
+ readonly body: unknown;
7
+ readonly code?: string;
8
+ constructor(message: string, status: number, body?: unknown, code?: string);
9
+ }
10
+ export interface MetaMediaCreateResponse {
11
+ id: string;
12
+ }
13
+ export type MetaContainerStatusCode = "EXPIRED" | "ERROR" | "FINISHED" | "IN_PROGRESS" | "PUBLISHED";
14
+ export interface MetaContainerStatusResponse {
15
+ id: string;
16
+ status_code?: MetaContainerStatusCode;
17
+ status?: string;
18
+ }
19
+ export interface MetaContainerStatusQuery {
20
+ /** Comma-separated field list. Default returns only `id`; pass
21
+ * `"status_code,status"` to read processing state. */
22
+ fields?: string;
23
+ }
24
+ export interface MetaMediaCreateMethod {
25
+ (igUserId: string, req: MetaMediaCreateRequest, signal?: AbortSignal): Promise<MetaMediaCreateResponse>;
26
+ schema: z.ZodType<MetaMediaCreateRequest>;
27
+ }
28
+ export interface MetaContainerStatusMethod {
29
+ (containerId: string, query?: MetaContainerStatusQuery, signal?: AbortSignal): Promise<MetaContainerStatusResponse>;
30
+ }
31
+ export interface MetaMediaPublishResponse {
32
+ id: string;
33
+ }
34
+ export interface MetaMediaPublishMethod {
35
+ (igUserId: string, req: MetaMediaPublishRequest, signal?: AbortSignal): Promise<MetaMediaPublishResponse>;
36
+ schema: z.ZodType<MetaMediaPublishRequest>;
37
+ }
38
+ export interface MetaPostV25Namespace {
39
+ media: MetaMediaCreateMethod;
40
+ mediaPublish: MetaMediaPublishMethod;
41
+ }
42
+ export interface MetaPostNamespace {
43
+ v25: MetaPostV25Namespace;
44
+ }
45
+ export interface MetaGetV25Namespace {
46
+ container: MetaContainerStatusMethod;
47
+ }
48
+ export interface MetaGetNamespace {
49
+ v25: MetaGetV25Namespace;
50
+ }
51
+ export interface MetaProvider {
52
+ post: MetaPostNamespace;
53
+ get: MetaGetNamespace;
54
+ }
55
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAC7B,OAAO,KAAK,EAAE,sBAAsB,EAAE,uBAAuB,EAAE,MAAM,OAAO,CAAC;AAE7E,YAAY,EACV,WAAW,EACX,sBAAsB,EACtB,uBAAuB,GACxB,MAAM,OAAO,CAAC;AAIf,qBAAa,SAAU,SAAQ,KAAK;IAClC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;gBAEX,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,MAAM;CAO3E;AAMD,MAAM,WAAW,uBAAuB;IACtC,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,MAAM,MAAM,uBAAuB,GAC/B,SAAS,GACT,OAAO,GACP,UAAU,GACV,aAAa,GACb,WAAW,CAAC;AAKhB,MAAM,WAAW,2BAA2B;IAC1C,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,CAAC,EAAE,uBAAuB,CAAC;IACtC,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,wBAAwB;IACvC;2DACuD;IACvD,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAID,MAAM,WAAW,qBAAqB;IACpC,CACE,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,sBAAsB,EAC3B,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,uBAAuB,CAAC,CAAC;IACpC,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,sBAAsB,CAAC,CAAC;CAC3C;AAED,MAAM,WAAW,yBAAyB;IACxC,CACE,WAAW,EAAE,MAAM,EACnB,KAAK,CAAC,EAAE,wBAAwB,EAChC,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,2BAA2B,CAAC,CAAC;CACzC;AAED,MAAM,WAAW,wBAAwB;IACvC,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,MAAM,WAAW,sBAAsB;IACrC,CACE,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,uBAAuB,EAC5B,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,wBAAwB,CAAC,CAAC;IACrC,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,uBAAuB,CAAC,CAAC;CAC5C;AAID,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,qBAAqB,CAAC;IAC7B,YAAY,EAAE,sBAAsB,CAAC;CACtC;AAED,MAAM,WAAW,iBAAiB;IAChC,GAAG,EAAE,oBAAoB,CAAC;CAC3B;AAED,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,yBAAyB,CAAC;CACtC;AAED,MAAM,WAAW,gBAAgB;IAC/B,GAAG,EAAE,mBAAmB,CAAC;CAC1B;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,iBAAiB,CAAC;IACxB,GAAG,EAAE,gBAAgB,CAAC;CACvB"}
@@ -0,0 +1,14 @@
1
+ // -- Error -------------------------------------------------------------------
2
+ export class MetaError extends Error {
3
+ status;
4
+ body;
5
+ code;
6
+ constructor(message, status, body, code) {
7
+ super(message);
8
+ this.name = "MetaError";
9
+ this.status = status;
10
+ this.body = body ?? null;
11
+ this.code = code;
12
+ }
13
+ }
14
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AASA,+EAA+E;AAE/E,MAAM,OAAO,SAAU,SAAQ,KAAK;IACzB,MAAM,CAAS;IACf,IAAI,CAAU;IACd,IAAI,CAAU;IAEvB,YAAY,OAAe,EAAE,MAAc,EAAE,IAAc,EAAE,IAAa;QACxE,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,WAAW,CAAC;QACxB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,IAAI,GAAG,IAAI,IAAI,IAAI,CAAC;QACzB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;CACF"}
@@ -0,0 +1,79 @@
1
+ import { z } from "zod";
2
+ export declare const MetaOptionsSchema: z.ZodObject<{
3
+ accessToken: z.ZodString;
4
+ baseURL: z.ZodOptional<z.ZodString>;
5
+ timeout: z.ZodOptional<z.ZodNumber>;
6
+ fetch: z.ZodOptional<z.ZodType<typeof fetch, z.ZodTypeDef, typeof fetch>>;
7
+ }, "strip", z.ZodTypeAny, {
8
+ accessToken: string;
9
+ baseURL?: string | undefined;
10
+ timeout?: number | undefined;
11
+ fetch?: typeof fetch | undefined;
12
+ }, {
13
+ accessToken: string;
14
+ baseURL?: string | undefined;
15
+ timeout?: number | undefined;
16
+ fetch?: typeof fetch | undefined;
17
+ }>;
18
+ export type MetaOptions = z.infer<typeof MetaOptionsSchema>;
19
+ export declare const MetaMediaCreateRequestSchema: z.ZodObject<{
20
+ media_type: z.ZodEnum<["REELS", "VIDEO", "IMAGE", "CAROUSEL"]>;
21
+ video_url: z.ZodOptional<z.ZodString>;
22
+ image_url: z.ZodOptional<z.ZodString>;
23
+ caption: z.ZodOptional<z.ZodString>;
24
+ thumb_offset: z.ZodOptional<z.ZodNumber>;
25
+ share_to_feed: z.ZodOptional<z.ZodBoolean>;
26
+ location_id: z.ZodOptional<z.ZodString>;
27
+ user_tags: z.ZodOptional<z.ZodArray<z.ZodObject<{
28
+ username: z.ZodString;
29
+ x: z.ZodOptional<z.ZodNumber>;
30
+ y: z.ZodOptional<z.ZodNumber>;
31
+ }, "strip", z.ZodTypeAny, {
32
+ username: string;
33
+ x?: number | undefined;
34
+ y?: number | undefined;
35
+ }, {
36
+ username: string;
37
+ x?: number | undefined;
38
+ y?: number | undefined;
39
+ }>, "many">>;
40
+ collaborators: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
41
+ }, "strip", z.ZodTypeAny, {
42
+ media_type: "REELS" | "VIDEO" | "IMAGE" | "CAROUSEL";
43
+ video_url?: string | undefined;
44
+ image_url?: string | undefined;
45
+ caption?: string | undefined;
46
+ thumb_offset?: number | undefined;
47
+ share_to_feed?: boolean | undefined;
48
+ location_id?: string | undefined;
49
+ user_tags?: {
50
+ username: string;
51
+ x?: number | undefined;
52
+ y?: number | undefined;
53
+ }[] | undefined;
54
+ collaborators?: string[] | undefined;
55
+ }, {
56
+ media_type: "REELS" | "VIDEO" | "IMAGE" | "CAROUSEL";
57
+ video_url?: string | undefined;
58
+ image_url?: string | undefined;
59
+ caption?: string | undefined;
60
+ thumb_offset?: number | undefined;
61
+ share_to_feed?: boolean | undefined;
62
+ location_id?: string | undefined;
63
+ user_tags?: {
64
+ username: string;
65
+ x?: number | undefined;
66
+ y?: number | undefined;
67
+ }[] | undefined;
68
+ collaborators?: string[] | undefined;
69
+ }>;
70
+ export type MetaMediaCreateRequest = z.infer<typeof MetaMediaCreateRequestSchema>;
71
+ export declare const MetaMediaPublishRequestSchema: z.ZodObject<{
72
+ creation_id: z.ZodString;
73
+ }, "strip", z.ZodTypeAny, {
74
+ creation_id: string;
75
+ }, {
76
+ creation_id: string;
77
+ }>;
78
+ export type MetaMediaPublishRequest = z.infer<typeof MetaMediaPublishRequestSchema>;
79
+ //# sourceMappingURL=zod.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"zod.d.ts","sourceRoot":"","sources":["../../src/zod.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAWxB,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;EAK5B,CAAC;AAEH,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AA8B5D,eAAO,MAAM,4BAA4B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAUvC,CAAC;AAEH,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,KAAK,CAC1C,OAAO,4BAA4B,CACpC,CAAC;AASF,eAAO,MAAM,6BAA6B;;;;;;EAExC,CAAC;AAEH,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,KAAK,CAC3C,OAAO,6BAA6B,CACrC,CAAC"}
@@ -0,0 +1,59 @@
1
+ import { z } from "zod";
2
+ // ---------------------------------------------------------------------------
3
+ // Provider options
4
+ // ---------------------------------------------------------------------------
5
+ // Instagram Graph API requires a long-lived user access token from the
6
+ // Instagram Login OAuth flow (instagram_business_basic +
7
+ // instagram_business_content_publish scopes). The caller obtains the token
8
+ // externally and supplies it here; this package does not implement the OAuth
9
+ // dance. Tokens last 60 days and are refreshable for another 60 each time.
10
+ export const MetaOptionsSchema = z.object({
11
+ accessToken: z.string().min(1),
12
+ baseURL: z.string().optional(),
13
+ timeout: z.number().optional(),
14
+ fetch: z.custom().optional(),
15
+ });
16
+ // ---------------------------------------------------------------------------
17
+ // Shared building blocks
18
+ // ---------------------------------------------------------------------------
19
+ // Instagram object IDs are numeric strings. The Graph API uses 64-bit ints
20
+ // over the wire as decimal strings to avoid JS precision loss. Cap at 19
21
+ // digits (max int64).
22
+ const MetaIdStringSchema = z.string().regex(/^[0-9]{1,19}$/);
23
+ const MetaUserTagSchema = z.object({
24
+ username: z.string().min(1),
25
+ x: z.number().min(0).max(1).optional(),
26
+ y: z.number().min(0).max(1).optional(),
27
+ });
28
+ // ---------------------------------------------------------------------------
29
+ // POST /v25.0/{ig-user-id}/media
30
+ // ---------------------------------------------------------------------------
31
+ // `media_type` drives which url field is required. Carousel items reference
32
+ // their parent container via `is_carousel_item: true`, but full carousel
33
+ // support is out of scope for this first cut — REELS / VIDEO / IMAGE only.
34
+ const MetaMediaTypeSchema = z.enum(["REELS", "VIDEO", "IMAGE", "CAROUSEL"]);
35
+ // Server-side validation enforces that REELS / VIDEO require `video_url`,
36
+ // IMAGE requires `image_url`, etc. Mirror the docs at the SDK layer with
37
+ // simple optionality so error messages come back from the API verbatim
38
+ // instead of being pre-empted by an over-zealous local schema.
39
+ export const MetaMediaCreateRequestSchema = z.object({
40
+ media_type: MetaMediaTypeSchema,
41
+ video_url: z.string().url().optional(),
42
+ image_url: z.string().url().optional(),
43
+ caption: z.string().max(2200).optional(),
44
+ thumb_offset: z.number().int().min(0).optional(),
45
+ share_to_feed: z.boolean().optional(),
46
+ location_id: MetaIdStringSchema.optional(),
47
+ user_tags: z.array(MetaUserTagSchema).max(20).optional(),
48
+ collaborators: z.array(z.string().min(1)).max(3).optional(),
49
+ });
50
+ // ---------------------------------------------------------------------------
51
+ // POST /v25.0/{ig-user-id}/media_publish
52
+ // ---------------------------------------------------------------------------
53
+ // `creation_id` is the container ID returned from /media — must reach
54
+ // status_code === "FINISHED" before this endpoint will accept it (server
55
+ // returns 400 otherwise).
56
+ export const MetaMediaPublishRequestSchema = z.object({
57
+ creation_id: MetaIdStringSchema,
58
+ });
59
+ //# sourceMappingURL=zod.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"zod.js","sourceRoot":"","sources":["../../src/zod.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,8EAA8E;AAC9E,mBAAmB;AACnB,8EAA8E;AAE9E,uEAAuE;AACvE,yDAAyD;AACzD,2EAA2E;AAC3E,6EAA6E;AAC7E,2EAA2E;AAC3E,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,CAAC,MAAM,CAAC;IACxC,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAC9B,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC9B,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC9B,KAAK,EAAE,CAAC,CAAC,MAAM,EAAgB,CAAC,QAAQ,EAAE;CAC3C,CAAC,CAAC;AAIH,8EAA8E;AAC9E,yBAAyB;AACzB,8EAA8E;AAE9E,2EAA2E;AAC3E,yEAAyE;AACzE,sBAAsB;AACtB,MAAM,kBAAkB,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC;AAE7D,MAAM,iBAAiB,GAAG,CAAC,CAAC,MAAM,CAAC;IACjC,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAC3B,CAAC,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;IACtC,CAAC,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;CACvC,CAAC,CAAC;AAEH,8EAA8E;AAC9E,iCAAiC;AACjC,8EAA8E;AAE9E,4EAA4E;AAC5E,yEAAyE;AACzE,2EAA2E;AAC3E,MAAM,mBAAmB,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,UAAU,CAAC,CAAC,CAAC;AAE5E,0EAA0E;AAC1E,yEAAyE;AACzE,uEAAuE;AACvE,+DAA+D;AAC/D,MAAM,CAAC,MAAM,4BAA4B,GAAG,CAAC,CAAC,MAAM,CAAC;IACnD,UAAU,EAAE,mBAAmB;IAC/B,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;IACtC,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;IACtC,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE;IACxC,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;IAChD,aAAa,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;IACrC,WAAW,EAAE,kBAAkB,CAAC,QAAQ,EAAE;IAC1C,SAAS,EAAE,CAAC,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,QAAQ,EAAE;IACxD,aAAa,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;CAC5D,CAAC,CAAC;AAMH,8EAA8E;AAC9E,yCAAyC;AACzC,8EAA8E;AAE9E,sEAAsE;AACtE,yEAAyE;AACzE,0BAA0B;AAC1B,MAAM,CAAC,MAAM,6BAA6B,GAAG,CAAC,CAAC,MAAM,CAAC;IACpD,WAAW,EAAE,kBAAkB;CAChC,CAAC,CAAC"}
package/package.json ADDED
@@ -0,0 +1,62 @@
1
+ {
2
+ "name": "@apicity/meta",
3
+ "version": "0.1.0-alpha.0",
4
+ "description": "Instagram Graph API provider for posting reels via the public-URL flow (graph.instagram.com).",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/justintanner/apicity.git",
9
+ "directory": "provider/meta"
10
+ },
11
+ "publishConfig": {
12
+ "access": "public"
13
+ },
14
+ "type": "module",
15
+ "main": "dist/src/index.js",
16
+ "types": "dist/src/index.d.ts",
17
+ "exports": {
18
+ ".": {
19
+ "import": "./dist/src/index.js",
20
+ "types": "./dist/src/index.d.ts"
21
+ },
22
+ "./zod": {
23
+ "import": "./dist/src/zod.js",
24
+ "types": "./dist/src/zod.d.ts"
25
+ }
26
+ },
27
+ "peerDependencies": {
28
+ "zod": "^3.24.0"
29
+ },
30
+ "peerDependenciesMeta": {
31
+ "zod": {
32
+ "optional": true
33
+ }
34
+ },
35
+ "files": [
36
+ "dist",
37
+ "README.md",
38
+ "LICENSE"
39
+ ],
40
+ "sideEffects": false,
41
+ "keywords": [
42
+ "instagram",
43
+ "ig",
44
+ "meta",
45
+ "social",
46
+ "reels",
47
+ "post",
48
+ "media-publish",
49
+ "apicity"
50
+ ],
51
+ "author": "Justin Tanner",
52
+ "engines": {
53
+ "node": ">=18.0.0"
54
+ },
55
+ "homepage": "https://github.com/justintanner/apicity#readme",
56
+ "bugs": {
57
+ "url": "https://github.com/justintanner/apicity/issues"
58
+ },
59
+ "scripts": {
60
+ "build": "tsc -p tsconfig.json && node scripts/dist.mjs"
61
+ }
62
+ }