@tabsircg/fb-sdk 1.0.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/README.md +384 -0
- package/dist/client.js +20 -0
- package/dist/client.js.map +1 -0
- package/dist/httpClient.js +39 -0
- package/dist/httpClient.js.map +1 -0
- package/dist/internal/error.js +9 -0
- package/dist/internal/error.js.map +1 -0
- package/dist/internal/fetchers.js +56 -0
- package/dist/internal/fetchers.js.map +1 -0
- package/dist/internal/poller.js +55 -0
- package/dist/internal/poller.js.map +1 -0
- package/dist/internal/utils.js +25 -0
- package/dist/internal/utils.js.map +1 -0
- package/dist/lib/edge.js +33 -0
- package/dist/lib/edge.js.map +1 -0
- package/dist/lib/transformCase.js +39 -0
- package/dist/lib/transformCase.js.map +1 -0
- package/dist/resources/CommentResource.js +65 -0
- package/dist/resources/CommentResource.js.map +1 -0
- package/dist/resources/InsightResource.js +43 -0
- package/dist/resources/InsightResource.js.map +1 -0
- package/dist/resources/PageResource.js +123 -0
- package/dist/resources/PageResource.js.map +1 -0
- package/dist/resources/PostResource.js +26 -0
- package/dist/resources/PostResource.js.map +1 -0
- package/dist/resources/UserResource.js +18 -0
- package/dist/resources/UserResource.js.map +1 -0
- package/dist/resources/comment/CommentResource.js +58 -0
- package/dist/resources/comment/CommentResource.js.map +1 -0
- package/dist/resources/comment/PageCommentResouorce.js +79 -0
- package/dist/resources/comment/PageCommentResouorce.js.map +1 -0
- package/dist/store/memory.js +38 -0
- package/dist/store/memory.js.map +1 -0
- package/dist/store/redis.js +16 -0
- package/dist/store/redis.js.map +1 -0
- package/dist/store/types.js +2 -0
- package/dist/store/types.js.map +1 -0
- package/dist/tests/index.js +23 -0
- package/dist/tests/index.js.map +1 -0
- package/dist/types/facebookinsights.js +2 -0
- package/dist/types/facebookinsights.js.map +1 -0
- package/dist/types/facebookmedia.js +14 -0
- package/dist/types/facebookmedia.js.map +1 -0
- package/dist/types/facebookpage.js +2 -0
- package/dist/types/facebookpage.js.map +1 -0
- package/dist/types/facebookpost.js +2 -0
- package/dist/types/facebookpost.js.map +1 -0
- package/dist/types/facebookuser.js +2 -0
- package/dist/types/facebookuser.js.map +1 -0
- package/dist/types/shared.js +6 -0
- package/dist/types/shared.js.map +1 -0
- package/dist/types/webhook.js +2 -0
- package/dist/types/webhook.js.map +1 -0
- package/dist/utils.js +25 -0
- package/dist/utils.js.map +1 -0
- package/dist/webhook/handler.js +49 -0
- package/dist/webhook/handler.js.map +1 -0
- package/package.json +34 -0
package/README.md
ADDED
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
# fb-sdk
|
|
2
|
+
|
|
3
|
+
A typed Node.js SDK for the Facebook Graph API (v25.0). Select only the fields you need, get full autocomplete while writing the selector, strict compile-time validation that rejects unknown fields, and a return type narrowed to exactly what you selected — all without runtime overhead.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install fb-sdk
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
**Requirements:** Node.js 18+, TypeScript 5.9+
|
|
12
|
+
|
|
13
|
+
**Dependencies:** `axios`, `form-data`
|
|
14
|
+
|
|
15
|
+
## Quick Start
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
import { fbGraph } from "fb-sdk";
|
|
19
|
+
|
|
20
|
+
const fb = fbGraph("your-access-token");
|
|
21
|
+
|
|
22
|
+
// Get a post — only the fields you select exist on the result
|
|
23
|
+
const post = await fb.post("postId").get({
|
|
24
|
+
id: true,
|
|
25
|
+
message: true,
|
|
26
|
+
comments: {
|
|
27
|
+
options: { filter: "toplevel" },
|
|
28
|
+
fields: { id: true, message: true },
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
post.id; // string
|
|
33
|
+
post.message; // string | undefined
|
|
34
|
+
post.comments.data[0].message; // string
|
|
35
|
+
post.comments.paging; // Paging
|
|
36
|
+
// post.fullPicture — compile error, not selected
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Field Selection
|
|
40
|
+
|
|
41
|
+
Every `get` and `list` method accepts a **field selector** — an object whose shape mirrors the resource type. You pick fields by setting them to `true`. The SDK converts this object into the Graph API `fields` query parameter and infers a return type containing only the selected fields.
|
|
42
|
+
|
|
43
|
+
### Scalar fields
|
|
44
|
+
|
|
45
|
+
```typescript
|
|
46
|
+
{ id: true, message: true }
|
|
47
|
+
// → fields=id,message
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Nested objects
|
|
51
|
+
|
|
52
|
+
For non-collection nested objects, nest the selector directly:
|
|
53
|
+
|
|
54
|
+
```typescript
|
|
55
|
+
{ picture: { data: { url: true, height: true } } }
|
|
56
|
+
// → fields=picture{data{url,height}}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
You can also pass `true` to select all fields of a nested object:
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
{
|
|
63
|
+
picture: true;
|
|
64
|
+
}
|
|
65
|
+
// → fields=picture
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Edges (collections)
|
|
69
|
+
|
|
70
|
+
Edges — like `comments` on a post — are paginated collections. They require the `{ fields }` wrapper:
|
|
71
|
+
|
|
72
|
+
```typescript
|
|
73
|
+
{
|
|
74
|
+
comments: {
|
|
75
|
+
fields: { id: true, message: true }
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
// → fields=comments{id,message}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Passing `true` selects all fields on the edge's items:
|
|
82
|
+
|
|
83
|
+
```typescript
|
|
84
|
+
{
|
|
85
|
+
comments: true;
|
|
86
|
+
}
|
|
87
|
+
// → fields=comments
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Edge options
|
|
91
|
+
|
|
92
|
+
Some edges accept extra parameters (limit, pagination cursors, filters). These go in `options`:
|
|
93
|
+
|
|
94
|
+
```typescript
|
|
95
|
+
{
|
|
96
|
+
comments: {
|
|
97
|
+
options: { filter: "toplevel", limit: 10 },
|
|
98
|
+
fields: { id: true, message: true }
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
// → fields=comments.filter(toplevel).limit(10){id,message}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Available options vary per edge. The `comments` edge supports `filter` and `summary` on top of the base `limit`, `after`, and `before`. The SDK infers the correct options type for each edge — you get autocomplete for what's available.
|
|
105
|
+
|
|
106
|
+
### Return type narrowing
|
|
107
|
+
|
|
108
|
+
The return type contains **only** what you selected. If you select `{ id: true, message: true }` on a `FacebookPost`, the result type is `{ id: string; message: string | undefined }` — not the full `FacebookPost`. This applies recursively to nested objects and edges.
|
|
109
|
+
|
|
110
|
+
When an edge has option-dependent response fields (e.g., `comments` includes a `summary` object), those fields appear in the return type only when `options` is provided in the selector.
|
|
111
|
+
|
|
112
|
+
## Available Resources
|
|
113
|
+
|
|
114
|
+
### `fb.post(postId)`
|
|
115
|
+
|
|
116
|
+
Operations on a specific post by ID.
|
|
117
|
+
|
|
118
|
+
#### `fb.post(postId).get(fields)`
|
|
119
|
+
|
|
120
|
+
Fetch a single post.
|
|
121
|
+
|
|
122
|
+
```typescript
|
|
123
|
+
const post = await fb.post("postId").get({
|
|
124
|
+
id: true,
|
|
125
|
+
statusType: true,
|
|
126
|
+
createdTime: true,
|
|
127
|
+
shares: true,
|
|
128
|
+
reactions: true,
|
|
129
|
+
});
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
#### `fb.post(postId).expire(time, type)`
|
|
133
|
+
|
|
134
|
+
Set an expiration on a post.
|
|
135
|
+
|
|
136
|
+
```typescript
|
|
137
|
+
await fb.post("postId").expire(Date.now() + 86400000, "expire_only");
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
#### `fb.post(postId).comments.list(fields)`
|
|
141
|
+
|
|
142
|
+
Fetch comments on a post. Also provides a `.create(data)` method to add a comment.
|
|
143
|
+
|
|
144
|
+
```typescript
|
|
145
|
+
const comments = await fb.post("postId").comments.list({
|
|
146
|
+
id: true,
|
|
147
|
+
message: true,
|
|
148
|
+
from: { name: true },
|
|
149
|
+
});
|
|
150
|
+
// comments.data — Comment[]
|
|
151
|
+
// comments.paging — Paging
|
|
152
|
+
|
|
153
|
+
// Create a comment
|
|
154
|
+
const newComment = await fb.post("postId").comments.create({
|
|
155
|
+
message: "Hello world!"
|
|
156
|
+
});
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
### `fb.me`
|
|
162
|
+
|
|
163
|
+
Operations on the authenticated user.
|
|
164
|
+
|
|
165
|
+
#### `fb.me.get(fields)`
|
|
166
|
+
|
|
167
|
+
Fetch the current user's profile.
|
|
168
|
+
|
|
169
|
+
```typescript
|
|
170
|
+
const me = await fb.me.get({ id: true, name: true });
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
#### `fb.me.accounts(fields)`
|
|
174
|
+
|
|
175
|
+
List Facebook Pages the user manages.
|
|
176
|
+
|
|
177
|
+
```typescript
|
|
178
|
+
const pages = await fb.me.accounts({
|
|
179
|
+
id: true,
|
|
180
|
+
name: true,
|
|
181
|
+
accessToken: true,
|
|
182
|
+
});
|
|
183
|
+
// pages.data — FacebookPage[]
|
|
184
|
+
// pages.paging — Paging
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
---
|
|
188
|
+
|
|
189
|
+
### `fb.page(pageId)`
|
|
190
|
+
|
|
191
|
+
Operations scoped to a specific Page. Returns sub-resources for posts, videos, reels, images, and aggregated comments.
|
|
192
|
+
|
|
193
|
+
```typescript
|
|
194
|
+
const page = fb.page("pageId");
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
#### `page.posts.list(query)`
|
|
198
|
+
|
|
199
|
+
List posts on a Page. Note: to fetch a specific post, use `fb.post(postId).get()`.
|
|
200
|
+
|
|
201
|
+
```typescript
|
|
202
|
+
const feed = await page.posts.list({
|
|
203
|
+
fields: { id: true, message: true, createdTime: true },
|
|
204
|
+
});
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
#### `page.comments.list(query, config)`
|
|
208
|
+
|
|
209
|
+
Fetch an aggregated stream of comments across recent page posts.
|
|
210
|
+
|
|
211
|
+
```typescript
|
|
212
|
+
const allComments = await page.comments.list({
|
|
213
|
+
fields: { id: true, message: true, createdTime: true }
|
|
214
|
+
});
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
#### `page.videos.list(query)` / `page.videos.publish(data)`
|
|
218
|
+
|
|
219
|
+
List videos or publish a new video.
|
|
220
|
+
|
|
221
|
+
```typescript
|
|
222
|
+
const videos = await page.videos.list({
|
|
223
|
+
fields: { id: true, title: true, status: true },
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
const { postId } = await page.videos.publish({
|
|
227
|
+
fileUrl: "https://example.com/video.mp4",
|
|
228
|
+
title: "My Video",
|
|
229
|
+
description: "A description",
|
|
230
|
+
thumbnailUrl: "https://example.com/thumb.jpg",
|
|
231
|
+
});
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
Publishing handles the upload, waits for processing via polling, and returns the resulting post ID. Throws `FacebookUploadError` on failure.
|
|
235
|
+
|
|
236
|
+
#### `page.reels.list(query)` / `page.reels.publish(data)`
|
|
237
|
+
|
|
238
|
+
List or publish reels. Publishing uses Facebook's resumable upload protocol (start session → upload file → finish session) and polls until the reel is processed.
|
|
239
|
+
|
|
240
|
+
```typescript
|
|
241
|
+
const { postId } = await page.reels.publish({
|
|
242
|
+
fileUrl: "https://example.com/reel.mp4",
|
|
243
|
+
title: "My Reel",
|
|
244
|
+
thumbnailUrl: "https://example.com/thumb.jpg",
|
|
245
|
+
});
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
#### `page.images.list(query)` / `page.images.publish(data)`
|
|
249
|
+
|
|
250
|
+
List or publish images.
|
|
251
|
+
|
|
252
|
+
```typescript
|
|
253
|
+
const { postId } = await page.images.publish({
|
|
254
|
+
url: "https://example.com/image.jpg",
|
|
255
|
+
caption: "My image",
|
|
256
|
+
});
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
---
|
|
260
|
+
|
|
261
|
+
### `fb.comment(commentId)`
|
|
262
|
+
|
|
263
|
+
Operations to manage a single comment node.
|
|
264
|
+
|
|
265
|
+
```typescript
|
|
266
|
+
const myComment = fb.comment("commentId");
|
|
267
|
+
|
|
268
|
+
// Update message
|
|
269
|
+
await myComment.update({ message: "new message" });
|
|
270
|
+
|
|
271
|
+
// Like / unlike
|
|
272
|
+
await myComment.like();
|
|
273
|
+
await myComment.unlike();
|
|
274
|
+
|
|
275
|
+
// Delete
|
|
276
|
+
await myComment.delete();
|
|
277
|
+
|
|
278
|
+
// Get nested replies
|
|
279
|
+
const subComments = await myComment.replies.list({ id: true, message: true });
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
## Adding New Endpoints
|
|
283
|
+
|
|
284
|
+
To add a new resource or edge, follow this pattern:
|
|
285
|
+
|
|
286
|
+
### 1. Define the raw type
|
|
287
|
+
|
|
288
|
+
Create or update a file in `src/types/`. Define the raw interface with snake_case keys matching the Facebook API response:
|
|
289
|
+
|
|
290
|
+
```typescript
|
|
291
|
+
// src/types/facebookstory.ts
|
|
292
|
+
import { KeysToCamel } from "../lib/transformCase.js";
|
|
293
|
+
|
|
294
|
+
interface FacebookStoryRaw {
|
|
295
|
+
id: string;
|
|
296
|
+
created_time: string;
|
|
297
|
+
media_url: string;
|
|
298
|
+
}
|
|
299
|
+
export type FacebookStory = KeysToCamel<FacebookStoryRaw>;
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
### 2. Define edge options (if needed)
|
|
303
|
+
|
|
304
|
+
If the edge supports custom parameters beyond the base `limit`/`after`/`before`, extend `EdgeOptions` in `src/types/shared.ts`:
|
|
305
|
+
|
|
306
|
+
```typescript
|
|
307
|
+
export interface StoryEdgeOptions extends EdgeOptions {
|
|
308
|
+
since?: number;
|
|
309
|
+
}
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
### 3. Use `CollectionOf` for edges with option-dependent fields
|
|
313
|
+
|
|
314
|
+
If the edge's response includes extra fields only when certain options are passed, use `CollectionOf` with an intersection:
|
|
315
|
+
|
|
316
|
+
```typescript
|
|
317
|
+
interface ParentRaw {
|
|
318
|
+
stories: CollectionOf<FacebookStoryRaw, StoryEdgeOptions> & {
|
|
319
|
+
extra_field: string; // only present when options are provided
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
### 4. Create the resource
|
|
325
|
+
|
|
326
|
+
In `src/resources/`, create a factory function using `GetNode` and `ListEdge` types:
|
|
327
|
+
|
|
328
|
+
```typescript
|
|
329
|
+
import { GetNode, ListEdge } from "../types/shared.js";
|
|
330
|
+
import { FacebookStory } from "../types/facebookstory.js";
|
|
331
|
+
|
|
332
|
+
export type ListStories = ListEdge<FacebookStory>;
|
|
333
|
+
export type GetStory = GetNode<FacebookStory>;
|
|
334
|
+
|
|
335
|
+
export const createStoryResource = (http: HttpClient, pageId: string) => {
|
|
336
|
+
const list: ListStories = async (query) =>
|
|
337
|
+
http.get(`/${pageId}/stories`, {
|
|
338
|
+
params: { fields: toGraphFields(query.fields), ...query.options },
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
const get: GetStory = async (fields) =>
|
|
342
|
+
http.get(`/${pageId}`, {
|
|
343
|
+
params: { fields: toGraphFields(fields) },
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
return { list, get };
|
|
347
|
+
};
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
### 5. Wire into the SDK
|
|
351
|
+
|
|
352
|
+
Add the resource to the appropriate factory in `src/client.ts`:
|
|
353
|
+
|
|
354
|
+
```typescript
|
|
355
|
+
export function fbGraph(accessToken: string) {
|
|
356
|
+
const http = createHttpClient(accessToken);
|
|
357
|
+
return {
|
|
358
|
+
post: (postId: string) => createPostResource(http, postId),
|
|
359
|
+
page: (pageId: string) => ({
|
|
360
|
+
...createPageResource(http, pageId),
|
|
361
|
+
stories: createStoryResource(http, pageId),
|
|
362
|
+
}),
|
|
363
|
+
me: createUserResource(http),
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
## Type System Overview
|
|
369
|
+
|
|
370
|
+
The SDK's type system follows a three-stage pipeline:
|
|
371
|
+
|
|
372
|
+
### 1. Selector generation (`FbFieldSelector<T>`)
|
|
373
|
+
|
|
374
|
+
Given a resource type `T`, `FbFieldSelector<T>` generates the shape of a valid field selector object. Scalar fields become `true | undefined`. Nested objects become recursive selectors or `true`. Collection edges become `{ options?: O; fields: FbFieldSelector<Inner> } | true`, where `O` is the edge-specific options type extracted from the `CollectionOf` phantom type. Recursion depth is bounded by a `Decrement` counter (default depth: 1 level of nesting).
|
|
375
|
+
|
|
376
|
+
### 2. Strict validation (`DeepStrict<Valid, Inferred>`)
|
|
377
|
+
|
|
378
|
+
TypeScript's structural type system doesn't reject excess properties when generics are involved. `DeepStrict` solves this by mapping every key in the inferred selector — if the key exists in the valid selector shape, it passes through; otherwise it becomes `never`, causing a compile error. This gives you red squiggles on typos and invalid fields.
|
|
379
|
+
|
|
380
|
+
### 3. Return type narrowing (`FbPickDeep<T, F>`)
|
|
381
|
+
|
|
382
|
+
`FbPickDeep<T, F>` walks the resource type `T` in parallel with your selector `F` and keeps only the fields you selected. For collections, it wraps the picked inner type in `{ data: Picked[]; paging: Paging }`. If the selector included `options`, option-dependent fields (defined via `& { ... }` on `CollectionOf`) are also included via `CleanCollection`.
|
|
383
|
+
|
|
384
|
+
The result is end-to-end type safety: autocomplete guides you to valid fields, the compiler rejects invalid ones, and the return type contains exactly what you asked for.
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { createHttpClient } from "./httpClient.js";
|
|
2
|
+
import { createPostResource } from "./resources/PostResource.js";
|
|
3
|
+
import { createPageResource } from "./resources/PageResource.js";
|
|
4
|
+
import { createUserResource } from "./resources/UserResource.js";
|
|
5
|
+
import { createCommentResource } from "./resources/comment/CommentResource.js";
|
|
6
|
+
export function createFbSdk(config = {}) {
|
|
7
|
+
return (accessToken) => {
|
|
8
|
+
const http = createHttpClient(accessToken);
|
|
9
|
+
return {
|
|
10
|
+
post: (postId) => createPostResource({ http, id: postId, config }),
|
|
11
|
+
page: (pageId) => createPageResource({ http, id: pageId, config }),
|
|
12
|
+
comment: (commentId) => createCommentResource({ http, id: commentId, config }),
|
|
13
|
+
me: createUserResource({ http, config, id: "me" }),
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
export { createMemoryStore } from "./store/memory.js";
|
|
18
|
+
export { createRedisStore } from "./store/redis.js";
|
|
19
|
+
export { createWebhookHandler } from "./webhook/handler.js";
|
|
20
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAc,MAAM,iBAAiB,CAAC;AAC/D,OAAO,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AACjE,OAAO,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AACjE,OAAO,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AACjE,OAAO,EAAE,qBAAqB,EAAE,MAAM,wCAAwC,CAAC;AAa/E,MAAM,UAAU,WAAW,CAAC,SAAsB,EAAE;IAClD,OAAO,CAAC,WAAmB,EAAE,EAAE;QAC7B,MAAM,IAAI,GAAG,gBAAgB,CAAC,WAAW,CAAC,CAAC;QAC3C,OAAO;YACL,IAAI,EAAE,CAAC,MAAc,EAAE,EAAE,CAAC,kBAAkB,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;YAC1E,IAAI,EAAE,CAAC,MAAc,EAAE,EAAE,CAAC,kBAAkB,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;YAC1E,OAAO,EAAE,CAAC,SAAiB,EAAE,EAAE,CAAC,qBAAqB,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC;YACtF,EAAE,EAAE,kBAAkB,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC;SACnD,CAAC;IACJ,CAAC,CAAC;AACJ,CAAC;AAED,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import axios from "axios";
|
|
2
|
+
import { toCamel, toSnakeObj } from "./lib/transformCase.js";
|
|
3
|
+
import FormData from "form-data";
|
|
4
|
+
export const api = axios.create({ family: 4 });
|
|
5
|
+
const fbApi = axios.create({
|
|
6
|
+
baseURL: "https://graph.facebook.com/v25.0",
|
|
7
|
+
family: 4,
|
|
8
|
+
headers: { "Accept-Encoding": "gzip, deflate, br" },
|
|
9
|
+
});
|
|
10
|
+
export function createHttpClient(accessToken) {
|
|
11
|
+
return {
|
|
12
|
+
get: async (path, options) => {
|
|
13
|
+
const res = await fbApi.get(path, {
|
|
14
|
+
params: { access_token: accessToken, ...options?.params },
|
|
15
|
+
});
|
|
16
|
+
const data = toCamel(res.data);
|
|
17
|
+
return options?.safe ? { data, status: res.status } : data;
|
|
18
|
+
},
|
|
19
|
+
post: async (path, data, options) => {
|
|
20
|
+
const isForm = data instanceof FormData;
|
|
21
|
+
const res = await fbApi.post(path, isForm ? data : toSnakeObj(data), {
|
|
22
|
+
headers: isForm ? data.getHeaders() : {},
|
|
23
|
+
...(options?.safe && { validateStatus: (s) => s === 200 || s === 504 }),
|
|
24
|
+
params: { access_token: accessToken },
|
|
25
|
+
});
|
|
26
|
+
const body = toCamel(res.data);
|
|
27
|
+
return options?.safe ? { data: body, status: res.status } : body;
|
|
28
|
+
},
|
|
29
|
+
delete: async (path, options) => {
|
|
30
|
+
const res = await fbApi.delete(path, {
|
|
31
|
+
params: { access_token: accessToken, ...options?.params },
|
|
32
|
+
});
|
|
33
|
+
const data = toCamel(res.data);
|
|
34
|
+
return options?.safe ? { data, status: res.status } : data;
|
|
35
|
+
},
|
|
36
|
+
getToken: () => accessToken,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=httpClient.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"httpClient.js","sourceRoot":"","sources":["../src/httpClient.ts"],"names":[],"mappings":"AAAA,OAAO,KAA6B,MAAM,OAAO,CAAC;AAClD,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAC7D,OAAO,QAAQ,MAAM,WAAW,CAAC;AAEjC,MAAM,CAAC,MAAM,GAAG,GAAG,KAAK,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC;AAC/C,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC;IACzB,OAAO,EAAE,kCAAkC;IAC3C,MAAM,EAAE,CAAC;IACT,OAAO,EAAE,EAAE,iBAAiB,EAAE,mBAAmB,EAAE;CACpD,CAAC,CAAC;AAwBH,MAAM,UAAU,gBAAgB,CAAC,WAAmB;IAClD,OAAO;QACL,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE;YAC3B,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE;gBAChC,MAAM,EAAE,EAAE,YAAY,EAAE,WAAW,EAAE,GAAG,OAAO,EAAE,MAAM,EAAE;aAC1D,CAAC,CAAC;YACH,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YAC/B,OAAO,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;QAC7D,CAAC;QAED,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE;YAClC,MAAM,MAAM,GAAG,IAAI,YAAY,QAAQ,CAAC;YACxC,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE;gBACnE,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE;gBACxC,GAAG,CAAC,OAAO,EAAE,IAAI,IAAI,EAAE,cAAc,EAAE,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,GAAG,EAAE,CAAC;gBAC/E,MAAM,EAAE,EAAE,YAAY,EAAE,WAAW,EAAE;aACtC,CAAC,CAAC;YACH,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YAE/B,OAAO,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;QACnE,CAAC;QAED,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE;YAC9B,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,MAAM,CAAC,IAAI,EAAE;gBACnC,MAAM,EAAE,EAAE,YAAY,EAAE,WAAW,EAAE,GAAG,OAAO,EAAE,MAAM,EAAE;aAC1D,CAAC,CAAC;YACH,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YAC/B,OAAO,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;QAC7D,CAAC;QAED,QAAQ,EAAE,GAAG,EAAE,CAAC,WAAW;KAC5B,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"error.js","sourceRoot":"","sources":["../../src/internal/error.ts"],"names":[],"mappings":"AAEA,MAAM,OAAO,mBAAoB,SAAQ,KAAK;IAG1B;IAFlB,YACE,OAAe,EACC,MAAgC;QAEhD,KAAK,CAAC,OAAO,CAAC,CAAC;QAFC,WAAM,GAAN,MAAM,CAA0B;QAGhD,IAAI,CAAC,IAAI,GAAG,qBAAqB,CAAC;IACpC,CAAC;CACF"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import FormData from "form-data";
|
|
2
|
+
import { ORDER, } from "../types/shared.js";
|
|
3
|
+
export const fetchComments = async (http, { postIds, fieldsStr, options, cursors }) => {
|
|
4
|
+
const allComments = [];
|
|
5
|
+
const nextCursors = {};
|
|
6
|
+
const remaining = [];
|
|
7
|
+
const chunks = [];
|
|
8
|
+
for (let i = 0; i < postIds.length; i += 50) {
|
|
9
|
+
chunks.push(postIds.slice(i, i + 50));
|
|
10
|
+
}
|
|
11
|
+
for (const chunk of chunks) {
|
|
12
|
+
const batch = chunk.map((postId) => {
|
|
13
|
+
const params = new URLSearchParams();
|
|
14
|
+
params.set("fields", fieldsStr);
|
|
15
|
+
params.set("order", options?.order || ORDER.NEWEST);
|
|
16
|
+
params.set("limit", "5");
|
|
17
|
+
if (options?.filter)
|
|
18
|
+
params.set("filter", options.filter);
|
|
19
|
+
if (options?.summary !== undefined)
|
|
20
|
+
params.set("summary", String(options.summary));
|
|
21
|
+
if (options?.since)
|
|
22
|
+
params.set("since", String(options.since));
|
|
23
|
+
if (options?.until)
|
|
24
|
+
params.set("until", String(options.until));
|
|
25
|
+
const cursor = cursors[postId];
|
|
26
|
+
if (cursor)
|
|
27
|
+
params.set("after", cursor);
|
|
28
|
+
console.log(params.toString());
|
|
29
|
+
return {
|
|
30
|
+
method: "GET",
|
|
31
|
+
relative_url: `${postId}/comments?${params.toString()}`,
|
|
32
|
+
};
|
|
33
|
+
});
|
|
34
|
+
const form = new FormData();
|
|
35
|
+
form.append("batch", JSON.stringify(batch));
|
|
36
|
+
form.append("include_headers", "false");
|
|
37
|
+
const responses = await http.post("/", form);
|
|
38
|
+
const responseArray = Array.isArray(responses) ? responses : [];
|
|
39
|
+
for (let i = 0; i < responseArray.length; i++) {
|
|
40
|
+
const resp = responseArray[i];
|
|
41
|
+
const postId = chunk[i];
|
|
42
|
+
if (!resp || resp.code !== 200)
|
|
43
|
+
continue;
|
|
44
|
+
const parsed = JSON.parse(resp.body);
|
|
45
|
+
for (const comment of parsed.data) {
|
|
46
|
+
allComments.push({ ...comment, _postId: postId });
|
|
47
|
+
}
|
|
48
|
+
if (parsed.paging?.next && parsed.paging.cursors?.after) {
|
|
49
|
+
nextCursors[postId] = parsed.paging.cursors.after;
|
|
50
|
+
remaining.push(postId);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return { comments: allComments, nextCursors, remaining };
|
|
55
|
+
};
|
|
56
|
+
//# sourceMappingURL=fetchers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fetchers.js","sourceRoot":"","sources":["../../src/internal/fetchers.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,WAAW,CAAC;AAGjC,OAAO,EAIL,KAAK,GACN,MAAM,oBAAoB,CAAC;AAiB5B,MAAM,CAAC,MAAM,aAAa,GAAkB,KAAK,EAC/C,IAAI,EACJ,EAAE,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,EACxC,EAAE;IACF,MAAM,WAAW,GAAyC,EAAE,CAAC;IAC7D,MAAM,WAAW,GAA2B,EAAE,CAAC;IAC/C,MAAM,SAAS,GAAa,EAAE,CAAC;IAG/B,MAAM,MAAM,GAAe,EAAE,CAAC;IAC9B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC;QAC5C,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;IACxC,CAAC;IAED,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,MAAM,KAAK,GAAsB,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE;YACpD,MAAM,MAAM,GAAG,IAAI,eAAe,EAAE,CAAC;YACrC,MAAM,CAAC,GAAG,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;YAChC,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,IAAI,KAAK,CAAC,MAAM,CAAC,CAAC;YACpD,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;YAEzB,IAAI,OAAO,EAAE,MAAM;gBAAE,MAAM,CAAC,GAAG,CAAC,QAAQ,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;YAC1D,IAAI,OAAO,EAAE,OAAO,KAAK,SAAS;gBAAE,MAAM,CAAC,GAAG,CAAC,SAAS,EAAE,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC;YACnF,IAAI,OAAO,EAAE,KAAK;gBAAE,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC;YAC/D,IAAI,OAAO,EAAE,KAAK;gBAAE,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC;YAE/D,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;YAC/B,IAAI,MAAM;gBAAE,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;YAExC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC;YAC/B,OAAO;gBACL,MAAM,EAAE,KAAK;gBACb,YAAY,EAAE,GAAG,MAAM,aAAa,MAAM,CAAC,QAAQ,EAAE,EAAE;aACxD,CAAC;QACJ,CAAC,CAAC,CAAC;QAEH,MAAM,IAAI,GAAG,IAAI,QAAQ,EAAE,CAAC;QAC5B,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC;QAC5C,IAAI,CAAC,MAAM,CAAC,iBAAiB,EAAE,OAAO,CAAC,CAAC;QACxC,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,IAAI,CAAqB,GAAG,EAAE,IAAI,CAAC,CAAC;QAEjE,MAAM,aAAa,GAAG,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC;QAEhE,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,aAAa,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YAC9C,MAAM,IAAI,GAAG,aAAa,CAAC,CAAC,CAAC,CAAC;YAC9B,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,CAAE,CAAC;YACzB,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,KAAK,GAAG;gBAAE,SAAS;YAEzC,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAGlC,CAAC;YAEF,KAAK,MAAM,OAAO,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC;gBAClC,WAAW,CAAC,IAAI,CAAC,EAAE,GAAG,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;YACpD,CAAC;YAED,IAAI,MAAM,CAAC,MAAM,EAAE,IAAI,IAAI,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,KAAK,EAAE,CAAC;gBACxD,WAAW,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC;gBAClD,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACzB,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,WAAW,EAAE,SAAS,EAAE,CAAC;AAC3D,CAAC,CAAC"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { FacebookUploadError } from "./error.js";
|
|
2
|
+
const getProcessingError = (status) => {
|
|
3
|
+
if (status.videoStatus === "error")
|
|
4
|
+
return "Video upload failed";
|
|
5
|
+
const phase = status.uploadingPhase?.status === "error"
|
|
6
|
+
? status.uploadingPhase
|
|
7
|
+
: status.processingPhase?.status === "error"
|
|
8
|
+
? status.processingPhase
|
|
9
|
+
: status.publishingPhase?.status === "error"
|
|
10
|
+
? status.publishingPhase
|
|
11
|
+
: null;
|
|
12
|
+
return phase?.errors?.[0]?.message;
|
|
13
|
+
};
|
|
14
|
+
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
15
|
+
export function poll(fn, config = {}) {
|
|
16
|
+
const { maxAttempts = 30, intervalMs = 10000 } = config;
|
|
17
|
+
return async (...args) => {
|
|
18
|
+
for (let i = 0; i < maxAttempts; i++) {
|
|
19
|
+
const result = await fn(...args);
|
|
20
|
+
if (result !== undefined)
|
|
21
|
+
return result;
|
|
22
|
+
await sleep(intervalMs);
|
|
23
|
+
}
|
|
24
|
+
throw new Error(`Polling timed out after ${maxAttempts} attempts (${(maxAttempts * intervalMs) / 60000} min)`);
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
export const pollVideoStatus = poll(async (listVideos, trackingId) => {
|
|
28
|
+
const videos = await listVideos({
|
|
29
|
+
fields: {
|
|
30
|
+
status: true,
|
|
31
|
+
postId: true,
|
|
32
|
+
universalVideoId: true,
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
|
+
const target = videos.data.find((v) => v.universalVideoId === trackingId);
|
|
36
|
+
if (!target)
|
|
37
|
+
return undefined;
|
|
38
|
+
const error = getProcessingError(target.status);
|
|
39
|
+
if (error)
|
|
40
|
+
throw new FacebookUploadError(error, target.status);
|
|
41
|
+
if (target.status.publishingPhase?.status === "complete") {
|
|
42
|
+
return { postId: target.postId };
|
|
43
|
+
}
|
|
44
|
+
return undefined;
|
|
45
|
+
}, { maxAttempts: 30, intervalMs: 20000 });
|
|
46
|
+
export const pollReelStatus = poll(async (getReel) => {
|
|
47
|
+
const { postId, status } = await getReel({ postId: true, status: true });
|
|
48
|
+
const error = getProcessingError(status);
|
|
49
|
+
if (error)
|
|
50
|
+
throw new FacebookUploadError(error, status);
|
|
51
|
+
if (postId)
|
|
52
|
+
return { postId };
|
|
53
|
+
return undefined;
|
|
54
|
+
}, { maxAttempts: 30, intervalMs: 10000 });
|
|
55
|
+
//# sourceMappingURL=poller.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"poller.js","sourceRoot":"","sources":["../../src/internal/poller.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { toSnakeCase } from "../lib/transformCase.js";
|
|
2
|
+
function serializeEdgeOptions(options) {
|
|
3
|
+
if (!options)
|
|
4
|
+
return "";
|
|
5
|
+
return Object.entries(options)
|
|
6
|
+
.filter(([, v]) => v !== undefined)
|
|
7
|
+
.map(([k, v]) => `.${toSnakeCase(k)}(${v})`)
|
|
8
|
+
.join("");
|
|
9
|
+
}
|
|
10
|
+
export function toGraphFields(fields) {
|
|
11
|
+
return Object.entries(fields)
|
|
12
|
+
.filter(([, value]) => value !== undefined && value !== false)
|
|
13
|
+
.map(([key, value]) => {
|
|
14
|
+
const snakeKey = toSnakeCase(key);
|
|
15
|
+
if (value === true)
|
|
16
|
+
return snakeKey;
|
|
17
|
+
if (value.fields) {
|
|
18
|
+
const opts = serializeEdgeOptions(value.options);
|
|
19
|
+
return `${snakeKey}${opts}{${toGraphFields(value.fields)}}`;
|
|
20
|
+
}
|
|
21
|
+
return `${snakeKey}{${toGraphFields(value)}}`;
|
|
22
|
+
})
|
|
23
|
+
.join(",");
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/internal/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAEtD,SAAS,oBAAoB,CAAC,OAAiC;IAC7D,IAAI,CAAC,OAAO;QAAE,OAAO,EAAE,CAAC;IACxB,OAAO,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC;SAC3B,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,SAAS,CAAC;SAClC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC;SAC3C,IAAI,CAAC,EAAE,CAAC,CAAC;AACd,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,MAA2B;IACvD,OAAO,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC;SAC1B,MAAM,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,KAAK,CAAC;SAC7D,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE;QACpB,MAAM,QAAQ,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC;QAClC,IAAI,KAAK,KAAK,IAAI;YAAE,OAAO,QAAQ,CAAC;QAGpC,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;YACjB,MAAM,IAAI,GAAG,oBAAoB,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YACjD,OAAO,GAAG,QAAQ,GAAG,IAAI,IAAI,aAAa,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC;QAC9D,CAAC;QAGD,OAAO,GAAG,QAAQ,IAAI,aAAa,CAAC,KAAK,CAAC,GAAG,CAAC;IAChD,CAAC,CAAC;SACD,IAAI,CAAC,GAAG,CAAC,CAAC;AACf,CAAC"}
|
package/dist/lib/edge.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { toGraphFields } from "../utils.js";
|
|
2
|
+
function cleanParams(obj) {
|
|
3
|
+
return Object.fromEntries(Object.entries(obj).filter(([, v]) => v !== undefined));
|
|
4
|
+
}
|
|
5
|
+
export function listEdge(http, path, defaults) {
|
|
6
|
+
return ((fields, options) => {
|
|
7
|
+
return http.get(path, {
|
|
8
|
+
params: cleanParams({ fields: toGraphFields(fields), ...defaults, ...options }),
|
|
9
|
+
});
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
export function listByParentEdge(http, suffix, defaults) {
|
|
13
|
+
return ((parentId, fields, options) => {
|
|
14
|
+
return http.get(`/${parentId}${suffix}`, {
|
|
15
|
+
params: cleanParams({ fields: toGraphFields(fields), ...defaults, ...options }),
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
export function getEdge(http) {
|
|
20
|
+
return ((id, fields) => {
|
|
21
|
+
return http.get(`/${id}`, {
|
|
22
|
+
params: { fields: toGraphFields(fields) },
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
export function getFixedEdge(http, path) {
|
|
27
|
+
return ((fields) => {
|
|
28
|
+
return http.get(path, {
|
|
29
|
+
params: { fields: toGraphFields(fields) },
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=edge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"edge.js","sourceRoot":"","sources":["../../src/lib/edge.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAgD5C,SAAS,WAAW,CAAC,GAA4B;IAC/C,OAAO,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC;AACpF,CAAC;AAKD,MAAM,UAAU,QAAQ,CACtB,IAAgB,EAChB,IAAY,EACZ,QAA0B;IAE1B,OAAO,CAAC,CAAC,MAA+B,EAAE,OAAgB,EAAE,EAAE;QAC5D,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE;YACpB,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,EAAE,aAAa,CAAC,MAAM,CAAC,EAAE,GAAG,QAAQ,EAAE,GAAG,OAAO,EAAE,CAAC;SAChF,CAAC,CAAC;IACL,CAAC,CAAmC,CAAC;AACvC,CAAC;AAGD,MAAM,UAAU,gBAAgB,CAC9B,IAAgB,EAChB,MAAc,EACd,QAA0B;IAE1B,OAAO,CAAC,CAAC,QAAgB,EAAE,MAA+B,EAAE,OAAgB,EAAE,EAAE;QAC9E,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,QAAQ,GAAG,MAAM,EAAE,EAAE;YACvC,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,EAAE,aAAa,CAAC,MAAM,CAAC,EAAE,GAAG,QAAQ,EAAE,GAAG,OAAO,EAAE,CAAC;SAChF,CAAC,CAAC;IACL,CAAC,CAA2C,CAAC;AAC/C,CAAC;AAGD,MAAM,UAAU,OAAO,CAAgC,IAAgB;IACrE,OAAO,CAAC,CAAC,EAAU,EAAE,MAA+B,EAAE,EAAE;QACtD,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,EAAE;YACxB,MAAM,EAAE,EAAE,MAAM,EAAE,aAAa,CAAC,MAAM,CAAC,EAAE;SAC1C,CAAC,CAAC;IACL,CAAC,CAA0B,CAAC;AAC9B,CAAC;AAGD,MAAM,UAAU,YAAY,CAC1B,IAAgB,EAChB,IAAY;IAEZ,OAAO,CAAC,CAAC,MAA+B,EAAE,EAAE;QAC1C,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE;YACpB,MAAM,EAAE,EAAE,MAAM,EAAE,aAAa,CAAC,MAAM,CAAC,EAAE;SAC1C,CAAC,CAAC;IACL,CAAC,CAA+B,CAAC;AACnC,CAAC"}
|