@productcraft/social 1.0.4 → 1.1.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 +187 -175
- package/api.md +4993 -0
- package/dist/index.cjs +5172 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +6238 -32
- package/dist/index.d.ts +6238 -32
- package/dist/index.js +5107 -8
- package/dist/index.js.map +1 -1
- package/dist/testing.cjs +3226 -0
- package/dist/testing.cjs.map +1 -0
- package/dist/testing.d.cts +32 -0
- package/dist/testing.d.ts +32 -0
- package/dist/testing.js +3224 -0
- package/dist/testing.js.map +1 -0
- package/manifest.json +3080 -0
- package/package.json +14 -4
package/README.md
CHANGED
|
@@ -1,229 +1,241 @@
|
|
|
1
1
|
# @productcraft/social
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Node.js / TypeScript client for **ProductCraft Social**: communities, actors, posts and comments, ranked feeds, stories, the follow graph, direct conversations, notifications and moderation.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
npm install @productcraft/social
|
|
7
|
-
```
|
|
8
|
-
|
|
9
|
-
Server-side only. Customer-facing apps integrate via a backend (BFF pattern) that holds the workspace PAK (`pcft_live_…`).
|
|
5
|
+
Every method, type, error class and mock is generated from the production OpenAPI spec. Coming from 1.0's raw `social.client.POST("/v1/…")` calls? `.client` still works unchanged; the [1.1 migration guide](https://github.com/clauderanelagh/productcraft-node/blob/main/docs/migrations/social-1.1.0.md) shows the resource method for every call shape.
|
|
10
6
|
|
|
11
|
-
##
|
|
7
|
+
## Install
|
|
12
8
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|---|---|---|---|
|
|
17
|
-
| **PlatformUser admin** | `/v1/workspaces/{workspace_id}/...` | cookie or PAK | Create communities, run moderation, read analytics. |
|
|
18
|
-
| **Customer backend** | `/v1/communities/{community_id}/...` | PAK + `X-Acting-As: <actor_id>` header | Day-to-day reads/writes on behalf of an EndUser. |
|
|
9
|
+
```sh
|
|
10
|
+
npm install @productcraft/social
|
|
11
|
+
```
|
|
19
12
|
|
|
20
|
-
|
|
13
|
+
Node 20.3 or later, Bun, Deno and edge runtimes. Server-side only: your backend holds the workspace key (`pcft_live_…`) and calls Social on behalf of your users. Or install the umbrella package for every product: `npm install productcraft`.
|
|
21
14
|
|
|
22
|
-
## Quick start
|
|
15
|
+
## Quick start
|
|
23
16
|
|
|
24
|
-
```ts
|
|
17
|
+
```ts
|
|
25
18
|
import { Social } from "@productcraft/social";
|
|
26
19
|
|
|
27
|
-
// Reads PRODUCTCRAFT_API_KEY from the environment
|
|
20
|
+
// Reads PRODUCTCRAFT_API_KEY from the environment.
|
|
28
21
|
const social = new Social();
|
|
29
22
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
);
|
|
37
|
-
```
|
|
23
|
+
// Workspace-level: communities themselves (the workspace id is resolved from the key, or pass workspaceId).
|
|
24
|
+
const { id: communityId } = await social.communities.create({ display_name: "Founders Club", slug: "founders" });
|
|
25
|
+
|
|
26
|
+
// Community-level: bind the community once, then act as one of its actors.
|
|
27
|
+
const community = social.community(communityId);
|
|
28
|
+
const { actor } = await community.actors.create({ external_id: "user_123", display_name: "Ada" }); // upsert by your user id
|
|
29
|
+
const c = community.as(actor.id); // fills actor_id in every body or query that declares it
|
|
38
30
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
const actor_id = "<actor-uuid>"; // the EndUser's Social actor id
|
|
43
|
-
|
|
44
|
-
const { data, error } = await social.client.POST(
|
|
45
|
-
"/v1/communities/{community_id}/posts",
|
|
46
|
-
{
|
|
47
|
-
params: {
|
|
48
|
-
path: { community_id: "<community-uuid>" },
|
|
49
|
-
header: { "X-Acting-As": actor_id },
|
|
50
|
-
},
|
|
51
|
-
body: {
|
|
52
|
-
// `actor_id` is required in the body too — the header sets caller
|
|
53
|
-
// identity; the body field is the author of the post (typically
|
|
54
|
-
// the same UUID, but allowed to differ on admin-impersonation flows).
|
|
55
|
-
actor_id,
|
|
56
|
-
text: "Hello, world!",
|
|
57
|
-
visibility: "public",
|
|
58
|
-
},
|
|
59
|
-
},
|
|
60
|
-
);
|
|
31
|
+
const post = await c.posts.create({ body: "Hello from the SDK" });
|
|
32
|
+
await c.posts.comments.create(post.id, { body: "First!" });
|
|
33
|
+
for await (const item of c.actors.feed.list(actor.id, { limit: 50 })) console.log(item);
|
|
61
34
|
```
|
|
62
35
|
|
|
63
|
-
|
|
36
|
+
## Two levels: workspace and community
|
|
64
37
|
|
|
65
|
-
|
|
38
|
+
| Calls on | Reach them through | Typical caller |
|
|
39
|
+
|---|---|---|
|
|
40
|
+
| Communities (create, list, analytics), moderation, workspace webhooks, stats | `social.communities.*`, `social.webhooks.*`, `social.stats` | Your admin tooling |
|
|
41
|
+
| Everything inside one community: actors, posts, comments, feeds, follows, conversations, notifications, community webhooks | `social.community(communityId).…` | Your product backend, per request |
|
|
66
42
|
|
|
67
|
-
|
|
43
|
+
`.as(actorId)` returns a copy of the community scope that fills `actor_id` (the acting user) wherever the API declares it. A call that passes its own `actor_id` wins:
|
|
68
44
|
|
|
69
|
-
```ts
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
});
|
|
45
|
+
```ts
|
|
46
|
+
import { Social } from "@productcraft/social";
|
|
47
|
+
declare const communityId: string, adaId: string, graceId: string, postId: string;
|
|
48
|
+
const social = new Social();
|
|
74
49
|
|
|
75
|
-
|
|
76
|
-
await
|
|
77
|
-
|
|
78
|
-
|
|
50
|
+
const asAda = social.community(communityId).as(adaId);
|
|
51
|
+
await asAda.follows.create({ dst_actor_id: graceId }); // actor_id: adaId
|
|
52
|
+
await asAda.posts.reactions.create(postId, { type: "like", actor_id: graceId }); // explicit wins
|
|
53
|
+
const unread = await asAda.actors.notifications.unreadCount(adaId);
|
|
54
|
+
const dm = await asAda.conversations.create({ kind: "direct", recipient_actor_id: graceId });
|
|
55
|
+
await asAda.conversations.messages.create(dm.id, { body: "Hi Grace" });
|
|
79
56
|
```
|
|
80
57
|
|
|
81
|
-
|
|
58
|
+
Social's wire is snake_case and so are the request and response types: no re-casing. Positional arguments (`postId`, `actorId`) and client options are camelCase.
|
|
59
|
+
|
|
60
|
+
## Credentials
|
|
61
|
+
|
|
62
|
+
| Credential | Pass | Use it for |
|
|
63
|
+
|---|---|---|
|
|
64
|
+
| Workspace API key (`pcft_live_…`) | `apiKey`, or `PRODUCTCRAFT_API_KEY` in the environment | Your backend, for everything |
|
|
65
|
+
| Platform session (a signed-in person) | `auth: { type: "bearer", token }` plus `workspaceId` | Admin tooling acting as a console user |
|
|
66
|
+
|
|
67
|
+
An explicit `apiKey` or `auth` always wins over the environment. The environment fallback is server-only: never put a key in `NEXT_PUBLIC_*` or any variable a bundler inlines into browser code. A keyed client constructed in a browser throws unless you set `dangerouslyAllowBrowser: true`.
|
|
68
|
+
|
|
69
|
+
<!-- generated:start (scripts/gen-sdk.mjs; edits between the markers are overwritten) -->
|
|
70
|
+
|
|
71
|
+
## Resources
|
|
72
|
+
|
|
73
|
+
Full reference: [api.md](./api.md).
|
|
74
|
+
|
|
75
|
+
- `social.communities`: `actors.comments.list`, `actors.reactions.list`, `analytics`, `create`, `delete`, `list`, `moderation.actions.create`, `moderation.actors.shadowBan`, `moderation.actors.unshadowBan`, `moderation.audit.list`, `moderation.blockedTerms.create`, `moderation.blockedTerms.delete`, `moderation.blockedTerms.list`, `moderation.conversations.list`, `moderation.conversations.messages.list`, `moderation.flags.list`, `moderation.flags.resolve`, `moderation.shadowBanned.list`, `retrieve`, `update`, `webhooks.endpoints.create`, `webhooks.endpoints.delete`, `webhooks.endpoints.list`, `webhooks.endpoints.retrieve`, `webhooks.endpoints.rotateSecret`, `webhooks.endpoints.test`, `webhooks.endpoints.update`, `webhooks.eventTypes.list`, `webhooks.events.list`, `webhooks.events.replay`, `webhooks.events.retrieve`
|
|
76
|
+
- `social.community(…).actors`: `archived.list`, `assets.list`, `batch`, `blocks.list`, `bookmarks.list`, `byExternal.batch`, `byExternal.retrieve`, `closeFriends.list`, `counters`, `create`, `delete`, `dismissedSuggestions.list`, `drafts.list`, `feed.list`, `followRequests.list`, `followers.list`, `following.list`, `hashtagFollows.create`, `hashtagFollows.delete`, `hashtagFollows.list`, `hides.list`, `highlights.list`, `insights`, `list`, `mentions.list`, `mutedTerms.create`, `mutedTerms.delete`, `mutedTerms.list`, `mutes.list`, `notificationPrefs.list`, `notificationPrefs.update`, `notifications.list`, `notifications.markAllRead`, `notifications.unreadCount`, `notifications.update`, `postState`, `relationships.list`, `restricts.list`, `retrieve`, `scheduledPosts.list`, `storyTray.list`, `suggestedFollows.list`, `tagged.list`, `update`
|
|
77
|
+
- `social.community(…).assets`: `complete`, `create`, `delete`, `retrieve`
|
|
78
|
+
- `social.community(…).blocks`: `create`, `delete`
|
|
79
|
+
- `social.community(…).closeFriends`: `create`, `delete`
|
|
80
|
+
- `social.community(…).collections`: `create`, `delete`, `items.create`, `items.delete`, `items.list`, `items.update`, `list`, `retrieve`, `update`
|
|
81
|
+
- `social.community(…).connections`: `accept`, `create`, `delete`
|
|
82
|
+
- `social.community(…).conversations`: `create`, `delete`, `list`, `markRead`, `members.create`, `members.delete`, `members.update`, `messages.create`, `messages.delete`, `messages.list`, `messages.reactions.create`, `messages.reactions.delete`, `messages.reactions.list`, `messages.retrieve`, `messages.update`, `retrieve`, `unreadCount`, `update`
|
|
83
|
+
- `social.community(…).counters`: `list`
|
|
84
|
+
- `social.community(…).discoverFeed`: `list`
|
|
85
|
+
- `social.community(…).dismissedSuggestions`: `create`, `delete`
|
|
86
|
+
- `social.community(…).flags`: `create`, `list`
|
|
87
|
+
- `social.community(…).followRequests`: `approve`, `delete`
|
|
88
|
+
- `social.community(…).follows`: `batch`, `create`, `delete`
|
|
89
|
+
- `social.community(…).hashtags`: `list`, `posts.list`, `retrieve`
|
|
90
|
+
- `social.community(…).hides`: `create`, `delete`
|
|
91
|
+
- `social.community(…).lists`: `create`, `delete`, `feed.list`, `list`, `members.create`, `members.delete`, `members.list`, `retrieve`, `update`
|
|
92
|
+
- `social.community(…).mutes`: `create`, `delete`
|
|
93
|
+
- `social.community(…).posts`: `bookmark`, `comments.approve`, `comments.create`, `comments.delete`, `comments.list`, `comments.pin`, `comments.reactions.create`, `comments.reactions.delete`, `comments.reactions.list`, `comments.retrieve`, `comments.unpin`, `comments.update`, `create`, `delete`, `insights`, `list`, `pollResults`, `quotes.list`, `reactions.create`, `reactions.delete`, `reactions.list`, `recordImpressions`, `recordView`, `repost`, `reposts.list`, `retrieve`, `revisions.list`, `tags.approve`, `tags.delete`, `unbookmark`, `unrepost`, `update`, `viewers.list`, `voters.list`, `votes.create`, `votes.delete`, `votes.retrieve`
|
|
94
|
+
- `social.community(…).restricts`: `create`, `delete`
|
|
95
|
+
- `social.community(…).search`: `actors.list`, `hashtags.list`, `posts.list`
|
|
96
|
+
- `social.community(…).webhooks`: `endpoints.create`, `endpoints.delete`, `endpoints.list`, `endpoints.retrieve`, `endpoints.rotateSecret`, `endpoints.test`, `endpoints.update`, `eventTypes.list`, `events.list`, `events.replay`, `events.retrieve`
|
|
97
|
+
- `social.stats`: `retrieve`
|
|
98
|
+
- `social.webhooks`: `endpoints.create`, `endpoints.delete`, `endpoints.list`, `endpoints.retrieve`, `endpoints.rotateSecret`, `endpoints.test`, `endpoints.update`, `eventTypes.list`, `events.list`, `events.replay`, `events.retrieve`
|
|
99
|
+
|
|
100
|
+
## Pagination
|
|
101
|
+
|
|
102
|
+
List methods return a `PagePromise`: `for await` walks every item across pages, `await` gives one page.
|
|
103
|
+
|
|
104
|
+
```ts
|
|
105
|
+
import { Social } from "@productcraft/social";
|
|
106
|
+
const social = new Social({ apiKey: process.env.PRODUCTCRAFT_API_KEY });
|
|
107
|
+
for await (const item of social.communities.list({ limit: 50 })) console.log(item);
|
|
108
|
+
const page = await social.communities.list();
|
|
109
|
+
if (page.hasNextPage()) await page.getNextPage();
|
|
110
|
+
const all = await social.communities.list().toArray({ maxItems: 500 });
|
|
111
|
+
```
|
|
82
112
|
|
|
83
|
-
##
|
|
113
|
+
## Errors
|
|
84
114
|
|
|
85
|
-
|
|
115
|
+
Every failure is a class you can catch. `APIError` carries `status`, `code`, `message`, `errors[]`, `requestId` and `docUrl`.
|
|
86
116
|
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
|
|
117
|
+
```ts
|
|
118
|
+
import { Social, NotFoundError, RateLimitError, APIError } from "@productcraft/social";
|
|
119
|
+
const social = new Social();
|
|
120
|
+
try {
|
|
121
|
+
await social.communities.retrieve("00000000-0000-0000-0000-000000000000");
|
|
122
|
+
} catch (err) {
|
|
123
|
+
if (err instanceof NotFoundError) console.log(err.code, err.requestId, err.docUrl);
|
|
124
|
+
else if (err instanceof RateLimitError) console.log(err.retryAfter);
|
|
125
|
+
else if (err instanceof APIError) console.log(err.status, err.errors);
|
|
126
|
+
else throw err;
|
|
127
|
+
}
|
|
90
128
|
```
|
|
91
129
|
|
|
92
|
-
|
|
93
|
-
- Hosts are never read from the environment. Override them in code with `baseUrl`.
|
|
94
|
-
- This is a server-only fallback. Never put a key in `NEXT_PUBLIC_*` or any other variable a bundler inlines into browser code.
|
|
130
|
+
## Retries and timeouts
|
|
95
131
|
|
|
96
|
-
|
|
132
|
+
Network errors, 408, 429, 5xx and `409 IDEMPOTENCY_IN_PROGRESS` are retried twice by default with exponential backoff and jitter, honouring `Retry-After` up to 60 s. A POST or PATCH without an idempotency key is never retried. Each attempt times out after 60 s, so the worst case with defaults is about 3 minutes; pass `signal: AbortSignal.timeout(ms)` for an overall deadline.
|
|
97
133
|
|
|
98
|
-
```ts
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
| { type: "bearer", token: "eyJ..." }
|
|
103
|
-
| { type: "cookie", value: "auth_token=..." },
|
|
104
|
-
baseUrl: "https://social.example.test", // optional override
|
|
105
|
-
fetch: customFetch, // optional
|
|
106
|
-
});
|
|
134
|
+
```ts
|
|
135
|
+
import { Social } from "@productcraft/social";
|
|
136
|
+
const social = new Social({ maxRetries: 3, timeout: 20_000 });
|
|
137
|
+
await social.communities.list({}, { maxRetries: 0, signal: AbortSignal.timeout(5_000) });
|
|
107
138
|
```
|
|
108
139
|
|
|
109
|
-
##
|
|
140
|
+
## Idempotency
|
|
110
141
|
|
|
111
|
-
|
|
142
|
+
Writes that support it send an `Idempotency-Key` automatically and reuse it on every retry. Pass your own to make a retry across processes safe. If the API's idempotency store is unavailable it runs the request without it (fail-open), so a retry during that window can execute twice.
|
|
143
|
+
|
|
144
|
+
```ts
|
|
145
|
+
import { Social } from "@productcraft/social";
|
|
146
|
+
const social = new Social({ apiKey: process.env.PRODUCTCRAFT_API_KEY });
|
|
147
|
+
const community = await social.communities.create({ display_name: "My Product Community", slug: "my-product-community" }, { idempotencyKey: "order-1234" });
|
|
148
|
+
```
|
|
112
149
|
|
|
113
|
-
|
|
114
|
-
// Create a post
|
|
115
|
-
await social.client.POST(
|
|
116
|
-
"/v1/communities/{community_id}/posts",
|
|
117
|
-
{ params: { path: { community_id: communityId } }, body: { text: "..." } },
|
|
118
|
-
);
|
|
150
|
+
## Webhooks
|
|
119
151
|
|
|
120
|
-
|
|
121
|
-
await social.client.GET(
|
|
122
|
-
"/v1/communities/{community_id}/actors/{actor_id}/feed",
|
|
123
|
-
{ params: { path: { community_id: communityId, actor_id: actorId } } },
|
|
124
|
-
);
|
|
152
|
+
`social.webhooks.unwrap()` verifies the signature against the **raw** body and returns a typed `Social.UnwrapWebhookEvent`. The secret comes from `{ secret }`, the client's `webhookSecret`, or `PRODUCTCRAFT_SOCIAL_WEBHOOK_SECRET`.
|
|
125
153
|
|
|
126
|
-
|
|
127
|
-
await social.client.GET(
|
|
128
|
-
"/v1/communities/{community_id}/discover-feed",
|
|
129
|
-
{ params: { path: { community_id: communityId } } },
|
|
130
|
-
);
|
|
154
|
+
Next.js route handler:
|
|
131
155
|
|
|
132
|
-
|
|
156
|
+
```ts
|
|
157
|
+
import { Social } from "@productcraft/social";
|
|
158
|
+
const social = new Social();
|
|
159
|
+
export async function POST(req: Request) {
|
|
160
|
+
const event = await social.webhooks.unwrap({ payload: await req.text(), headers: req.headers });
|
|
161
|
+
switch (event.type) {
|
|
162
|
+
case "actor.updated":
|
|
163
|
+
console.log(event.data);
|
|
164
|
+
break;
|
|
165
|
+
default:
|
|
166
|
+
break; // UnknownWebhookEvent: types added later
|
|
167
|
+
}
|
|
168
|
+
return new Response(null, { status: 204 });
|
|
169
|
+
}
|
|
133
170
|
```
|
|
134
171
|
|
|
135
|
-
|
|
172
|
+
Express (keep the body raw):
|
|
136
173
|
|
|
137
|
-
```ts
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
)
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
{ ... },
|
|
148
|
-
);
|
|
174
|
+
```ts
|
|
175
|
+
import express from "express";
|
|
176
|
+
import { Social } from "@productcraft/social";
|
|
177
|
+
const social = new Social();
|
|
178
|
+
const app = express();
|
|
179
|
+
app.post("/webhooks", express.raw({ type: "application/json" }), async (req, res) => {
|
|
180
|
+
const event = await social.webhooks.unwrap({ payload: req.body, headers: req.headers });
|
|
181
|
+
console.log(event.type);
|
|
182
|
+
res.sendStatus(204);
|
|
183
|
+
});
|
|
149
184
|
```
|
|
150
185
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
```ts before
|
|
154
|
-
// Follow / unfollow
|
|
155
|
-
await social.client.PUT(
|
|
156
|
-
"/v1/communities/{community_id}/follows/{src_actor_id}/{dst_actor_id}",
|
|
157
|
-
{ ... },
|
|
158
|
-
);
|
|
186
|
+
Hono:
|
|
159
187
|
|
|
160
|
-
|
|
161
|
-
|
|
188
|
+
```ts
|
|
189
|
+
import { Hono } from "hono";
|
|
190
|
+
import { Social } from "@productcraft/social";
|
|
191
|
+
const social = new Social();
|
|
192
|
+
const app = new Hono();
|
|
193
|
+
app.post("/webhooks", async (c) => {
|
|
194
|
+
const event = await social.webhooks.unwrap({ payload: await c.req.text(), headers: c.req.raw.headers });
|
|
195
|
+
console.log(event.type);
|
|
196
|
+
return c.body(null, 204);
|
|
197
|
+
});
|
|
162
198
|
```
|
|
163
199
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
await social.
|
|
175
|
-
|
|
176
|
-
{ ... },
|
|
177
|
-
);
|
|
178
|
-
|
|
179
|
-
// Mark all read
|
|
180
|
-
await social.client.POST(
|
|
181
|
-
"/v1/communities/{community_id}/actors/{actor_id}/notifications/read-all",
|
|
182
|
-
{ ... },
|
|
183
|
-
);
|
|
200
|
+
## Raw responses
|
|
201
|
+
|
|
202
|
+
Every returned object has a non-enumerable `lastResponse` (`requestId`, `status`, `headers`). `.withResponse()` adds the `Response` and parsed rate-limit headers.
|
|
203
|
+
|
|
204
|
+
```ts
|
|
205
|
+
import { Social } from "@productcraft/social";
|
|
206
|
+
const social = new Social({ apiKey: process.env.PRODUCTCRAFT_API_KEY });
|
|
207
|
+
const id = "9b2f6c1e-0d6a-4a63-9a43-2f1c3e4d5a6b";
|
|
208
|
+
const obj = await social.communities.retrieve(id);
|
|
209
|
+
console.log(obj.lastResponse.requestId);
|
|
210
|
+
const { data, response, requestId, rateLimit } = await social.communities.retrieve(id).withResponse();
|
|
211
|
+
console.log(data, response.status, requestId, rateLimit?.remaining);
|
|
184
212
|
```
|
|
185
213
|
|
|
186
|
-
|
|
214
|
+
## Testing
|
|
187
215
|
|
|
188
|
-
|
|
189
|
-
// Start / open a conversation
|
|
190
|
-
await social.client.POST(
|
|
191
|
-
"/v1/communities/{community_id}/conversations",
|
|
192
|
-
{ params: { ... }, body: { members: ["act_a", "act_b"] } },
|
|
193
|
-
);
|
|
216
|
+
`@productcraft/social/testing` exports `createMockFetch()`, a `fetch` that answers every operation with its spec example. Override one operation by name.
|
|
194
217
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
);
|
|
218
|
+
```ts
|
|
219
|
+
import { Social } from "@productcraft/social";
|
|
220
|
+
import { createMockFetch } from "@productcraft/social/testing";
|
|
221
|
+
const fetch = createMockFetch();
|
|
222
|
+
const social = new Social({ apiKey: "test", workspaceId: "9b2f6c1e-0d6a-4a63-9a43-2f1c3e4d5a6b", fetch });
|
|
223
|
+
await social.communities.list();
|
|
224
|
+
console.log(fetch.calls.length);
|
|
200
225
|
```
|
|
201
226
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
// List flags
|
|
206
|
-
await social.client.GET(
|
|
207
|
-
"/v1/workspaces/{workspace_id}/communities/{community_id}/moderation/flags",
|
|
208
|
-
{ ... },
|
|
209
|
-
);
|
|
210
|
-
|
|
211
|
-
// Act on a flag (resolve / dismiss / take action on the target)
|
|
212
|
-
await social.client.POST(
|
|
213
|
-
"/v1/workspaces/{workspace_id}/communities/{community_id}/moderation/flags/{flag_id}/actions",
|
|
214
|
-
{ params: { ... }, body: { action: "remove_content", note: "..." } },
|
|
215
|
-
);
|
|
216
|
-
|
|
217
|
-
// Shadow-ban an actor
|
|
218
|
-
await social.client.PUT(
|
|
219
|
-
"/v1/workspaces/{workspace_id}/communities/{community_id}/moderation/actors/{actor_id}/shadow-ban",
|
|
220
|
-
{ ... },
|
|
221
|
-
);
|
|
222
|
-
```
|
|
227
|
+
## Escape hatch
|
|
228
|
+
|
|
229
|
+
`.client` is the typed `openapi-fetch` client over the same credential and base URL (no retries). Use it for anything the resource methods do not cover.
|
|
223
230
|
|
|
224
|
-
|
|
231
|
+
```ts
|
|
232
|
+
import { Social } from "@productcraft/social";
|
|
233
|
+
const social = new Social({ apiKey: process.env.PRODUCTCRAFT_API_KEY });
|
|
234
|
+
const { data, error } = await social.client.GET("/v1/workspaces/{workspace_id}/communities", { params: { path: { workspace_id: "9b2f6c1e-0d6a-4a63-9a43-2f1c3e4d5a6b" } } });
|
|
235
|
+
console.log(data, error);
|
|
236
|
+
```
|
|
225
237
|
|
|
226
|
-
|
|
238
|
+
<!-- generated:end -->
|
|
227
239
|
|
|
228
240
|
## License
|
|
229
241
|
|