@forumulate/sdk

中文文档
0.13.8发布于 2026/11/13MITTypeScriptCommonJS零依赖近 30 天下载 5025

用于Forumulate REST API的已打入类型脚本客户端.

中文使用文档
文档目录20 节
  1. 安装
  2. 认证
  3. 快速启动
  4. 每个操作都是一种方法
  5. 倾斜
  6. 共同任务
  7. 1. 写出并出版 " 降标 " 一页
  8. 2. 列出空间中的所有内容
  9. 3. 搜索
  10. 4. 启动论坛专题并作出答复
  11. 5. 校验网签
  12. 6. 翻译,任何作品
  13. 7. 您公布的政策的大门注册和注册
  14. 每个终点 : typed()
  15. 错误
  16. 报复和自责
  17. 选项
  18. 链接
  19. 维护者: 重新生成类型
  20. 许可证

@ 论坛/ sdk

输入的脚本客户端 论坛 REST API:一个组织的文献、论坛、博客和其他一切。 它没有运行时间依赖关系,运行在Node 18+, Bun, Deno 和浏览器中( global) fetch) (中文(简体) ).

安装

npm install @forumulate/sdk

认证

Mint 个人访问 账户 —— 托肯斯 (单位:千美元)论坛.com/账户/托肯) (中文(简体) ). 象征性需要 read 读取和 read + 键 write 改变一切. API 令牌和范围 解释两道门是象征性的.

import { ForumulateClient } from '@forumulate/sdk';

// Reads FORUMULATE_TOKEN and FORUMULATE_URL (default https://forumulate.com).
const forumulate = ForumulateClient.fromEnv();

// …or explicitly. The API base is the site plus /api.
const explicit = new ForumulateClient({
  baseUrl: 'https://forumulate.com/api',
  token: process.env.FORUMULATE_TOKEN,
});
变量默认含义
FORUMULATE_TOKEN无个人访问 Token, 发送为 Authorization: Bearer。 。 。 .
FORUMULATE_URLhttps://forumulate.com遗址. API是它的 API是它的 API是它的 /api。一个已经结束于 /api 用于当前.
FORUMULATE_API_URL无准确的 API 基础,用于本地 API 没有 /api 前缀( e)http://localhost:4000) (中文(简体) ).

这个 国 际 编辑 MCP 服务器 读取相同的变量.

快速启动

const { user } = await forumulate.auth.me();
const { orgs } = await forumulate.orgs.list();
const orgId = orgs[0]!.id; // ids and slugs are both accepted wherever an org id is

// Every list is an async iterator over every row, across every page.
for await (const space of forumulate.spaces.list({ orgId })) {
  console.log(space.slug, space.type);
}

每个操作都是一种方法

每个API的~1800操作都是以其操作Id命名的方法: ForumTopics_get 实值 forumulate.forum.topics.get, (中文(简体) ), Webhook_rotate 实值 forumulate.webhooks.rotate。路径、查询、正文和回复从 openAPI 光谱和命名空间由它产生,所以在 API 参考 在这里是一种方法.

// One argument: path params at the top level, `query` and `body` nested.
const { space } = await forumulate.spaces.get({ orgId, spaceId });
await forumulate.blog.create({ orgId, spaceId, body: { title: 'v2 is out', content } });
await forumulate.webhooks.update({ orgId, webhookId, body: { active: false } });
await forumulate.webhooks.delete({ orgId, webhookId }); // `delete` aliases the API's `remove`
await forumulate.tokens.rotate({ id: patId });
await forumulate.forum.taxonomy.listCategories({ orgId, spaceId }); // a sub-namespace per area of a namespace
区域命名空间
组织orgs, (中文(简体) ), spaces, (中文(简体) ), sites, (中文(简体) ), members, (中文(简体) ), invites, (中文(简体) ), roles, (中文(简体) ), grants, (中文(简体) ), groups, (中文(简体) ), tokens, (中文(简体) ), users, (中文(简体) ), billing, (中文(简体) ), sso, (中文(简体) ), scim
文档items, (中文(简体) ), pages, (中文(简体) ), collections, (中文(简体) ), shapes, (中文(简体) ), snapshots, (中文(简体) ), changeRequests, (中文(简体) ), docs, (中文(简体) ), openapi, (中文(简体) ), comments
社区和出版blog, (中文(简体) ), forum, (中文(简体) ), community, (中文(简体) ), moderation, (中文(简体) ), chat, (中文(简体) ), messages, (中文(简体) ), changelog, (中文(简体) ), roadmap, (中文(简体) ), newsletter
空间类型status, (中文(简体) ), events, (中文(简体) ), courses, (中文(简体) ), faq, (中文(简体) ), careers, (中文(简体) ), legal, (中文(简体) ), forms, (中文(简体) ), questions
平台webhooks, (中文(简体) ), domains, (中文(简体) ), themes, (中文(简体) ), extensions, (中文(简体) ), integrations, (中文(简体) ), notifications, (中文(简体) ), mail, (中文(简体) ), assets, (中文(简体) ), embeds, (中文(简体) ), privacy
搜索、人工智能和分析search, (中文(简体) ), ai, (中文(简体) ), analytics
无头CMScontent (图书馆、条目、模型、地方、密钥、资产.), delivery
公共阅读器( 无符号)public,则 public… 每个区域的子名称空间 : forum.publicTopic, (中文(简体) ), status.public, (中文(简体) ), blog.public.

method.operationId 告诉你是什么操作 一个方法呼叫。 以 id 表示,类型相同:

const { space } = await forumulate.request('Space_get', { orgId, spaceId });
for await (const hook of forumulate.paginate('Webhook_list', { orgId })) console.log(hook.url);

0.5位助手(spaces.list(orgId), (中文(简体) ), items.create(orgId, spaceId, input)...) 仍然工作着 不变,但 auth.register 财务报告和审定财务报表 auth.login,在 0.6 中被删除:他们的路线在 签入后转到了阿特拉斯系统,所以每个呼叫者都失败了. 以个人访问托肯认证 。 如果用生成的方法分享一个名称,它就是相同的操作,而成员 无论是呼叫形状.

倾斜

列表返回 a PagePromise数字 : await 当API回答第一页时, for await 每行都是这样.

const first = await forumulate.blog.list({ orgId, spaceId, query: { limit: 50 } });
first.page.nextCursor; // the envelope: page.{limit, hasMore, nextCursor, total}, rows under data

for await (const post of forumulate.blog.list({ orgId, spaceId })) {
  /* every post */
}
for await (const page of forumulate.blog.list({ orgId, spaceId }).pages()) {
  /* every page */
}

走着走着 page.nextCursor,以及 ?page= 对于没有光标的排序列表。 这个 生成的方法发送 Forumulate-Version (SDK的版本生成于, API_VERSION, (中文(简体) ), 除非你设定 apiVersion),因为只有一个版本的答案携带了信封.

共同任务

1. 写出并出版 " 降标 " 一页

items 是类型不可知的表面: 相同的呼叫会写出一个 docs 页面、博客文章或论坛主题 .

const { item } = await forumulate.items.create(orgId, spaceId, {
  title: 'Install',
  slug: 'install',
  body: '# Install\n\nRun `npm i acme`.',
});
await forumulate.items.publish(orgId, spaceId, item.id);

// Later: replace the body and republish.
await forumulate.items.setContent(orgId, spaceId, item.id, { body: '# Install\n\n…' });
await forumulate.items.publish(orgId, spaceId, item.id);

2. 列出空间中的所有内容

const res = await forumulate.items.list(orgId, spaceId, { limit: 100, include: ['body'] });
for (const it of res.items) console.log(it.id, it.title, it.contentIn);
// res.page.nextCursor pages onward; res.sync.checkpoint drives an incremental sync.

问 body。 。 。 . sections 仅为写在形状上的棋子而填入,所以空格为自由形式的页面答案 include: ['sections'] 单用空物体.

// Everything you can see in an org: docs, forum, blog.
const { hits } = await forumulate.search.unified(orgId, 'rate limits', { limit: 10 });

// A public docs space, no token needed.
const anon = new ForumulateClient({ baseUrl: 'https://forumulate.com/api' });
const { hits: pub } = await anon.public.search(orgId, spaceId, 'rate limits');

4. 启动论坛专题并作出答复

const doc = (text: string) => ({
  type: 'doc' as const,
  content: [{ type: 'paragraph', content: [{ type: 'text', text }] }],
});
const { topic } = await forumulate.forum.createTopic(orgId, forumSpaceId, {
  title: 'v2 is out',
  content: doc('Here is what changed.'),
});
const topicId = (topic as { id: string }).id;
await forumulate.forum.reply(orgId, forumSpaceId, topicId, doc('Upgrade notes are in the docs.'));

5. 校验网签

import { constructWebhookEvent } from '@forumulate/sdk';

// Verifies the signature and its timestamp, THEN parses; throws if either fails.
const event = await constructWebhookEvent({
  secret: process.env.FORUMULATE_WEBHOOK_SECRET!,
  body: rawBody, // the exact bytes received, before JSON.parse
  signature: req.headers['x-forumulate-signature'] as string,
  timestamp: req.headers['x-forumulate-timestamp'] as string, // deliveries older than 5 min fail
});

verifyWebhookSignature (另见: forumulate.webhooks.verifySignature)是同一支票返回 a 布尔。 以 forumulate.webhooks.create({ orgId, body: { url, events } })· ; webhooks.rotate, (中文(简体) ), webhooks.ping, (中文(简体) ), webhooks.deliveries 财务报告和审定财务报表 webhooks.replay 之后管理它.

6. 翻译,任何作品

client.translations 从空格类型中选择正确的路径:一个docs页面的语言双,一个帖子,主题或发行的翻译,或者一个事件. 你只给原作和语言取名. 机器翻译使用你的组织自己的DeepL密钥或AI提供者. 见 各地翻译。 。 。 .

const cov = await client.translations.status(orgId, spaceId); // piece × language → missing/draft/published/outdated
await client.translations.locales.set(orgId, { spaceId }, [{ code: 'es', label: 'Español' }]);
await client.translations.auto(orgId, spaceId, pageId, 'es');
const es = await client.translations.get(orgId, spaceId, pageId, 'es'); // { title, markdown, state }
await client.translations.put(orgId, spaceId, pageId, 'es', { markdown: '…', published: true });

7. 您公布的政策的大门注册和注册

client.policies 和三个独立的函数是服务器的一半 政策接受门: 您的合法空间 发布术语,隐私政策等,被用户在任何应用程序中接受,无论在任何标识中.

import { ForumulateClient, signPolicyTicket, verifyAcceptanceToken, policyGatePageUrl } from '@forumulate/sdk';

const client = ForumulateClient.fromEnv();
const space = { orgId: process.env.FORUMULATE_ORG!, spaceId: process.env.FORUMULATE_LEGAL_SPACE! };

// What does this user still have to accept before signing in?
const status = await client.policies.status({ ...space, subject: user.id, event: 'sign_in' });

if (!status.satisfied) {
  // Send them to the hosted acceptance page with a ticket you sign yourself (no API call).
  const ticket = await signPolicyTicket(
    { ...space, subject: user.id, event: 'sign_in', returnUrl: 'https://app.example.com/policy/return' },
    process.env.FORUMULATE_POLICY_GATE_KEYS!, // "pgk_…:pgs_…"
  );
  return redirect(policyGatePageUrl({ ...space, ticket }));
}

// On the way back (or from the widget): verify the acceptance token offline, with key rotation.
const r = await verifyAcceptanceToken(token, {
  keys: process.env.FORUMULATE_POLICY_GATE_KEYS,
  subject: user.id,
  requireSatisfied: true,
});

还有 client.policies.accept 编号 : claim 编号 : verify 编号 : acceptances 编号 : export 编号 : erase,以及 client.policies.list({ spaceId, secret }) 读取 Forumulate 策略 feed v1. verifyPolicyWebhook(headers, rawBody, secret) 检查a policy.published 编号 : policy.retired 通知,以及 signPolicyFeedRequest 编号 : verifyPolicyFeedRequest 执行种子的要求 签名,所以任何平台都可以消耗feed或服务自己的. Auth0的食谱 苏帕基地的办事员, Firebase, Auth.js 和一个自定义后端 : 政策门秘方。 。 。 .

每个终点 : typed()

以上帮助者覆盖了通路. typed(method, path, init) 覆盖 OpenAPI 光谱中的每个路由, 并包含路径、 参数、 查询、 车体和响应全部推断, 以及路径和方法上的自动完成 :

const { spaces } = await forumulate.typed('get', '/orgs/{orgId}/spaces', {
  params: { orgId },
  query: { limit: 50 },
});

await forumulate.typed('post', '/orgs/{orgId}/spaces', {
  params: { orgId },
  body: { name: 'Docs', slug: 'docs', type: 'DOCS', visibility: 'PUBLIC' },
});

过 raw: true 对于未受伤害者 Response (二进制下载). 为了其他一切, request(method, path, { body, query, headers }) 是未键入的逃生舱门 typed 援助者们必定要援助他们.

响应文件( PDF, PNG, ZIP) 或流分辨率到 Response; 文本答案( CSV, Markdown, iCal) 解析为 a string。 。 。 .

错误

每个非-2xx响应都会发出一个 ForumulateError 带有 API 错误信封 : status, (中文(简体) ), code, (中文(简体) ), message, (中文(简体) ), details (外地一级,用于 validation.failed, (中文(简体) ), requestId (在支持请求中引用) 财务报告和审定财务报表 retryAfter (秒,当API说).

import { ForumulateError } from '@forumulate/sdk';

try {
  await forumulate.orgs.create({ body: { name: '', slug: '' } });
} catch (err) {
  if (err instanceof ForumulateError && err.code === 'validation.failed') {
    console.log(err.details); // { name: ['Required'], … }
  }
}

A 类 401 表示该令牌已丢失或被撤销. A 类 403 说明它缺乏范围或能力 API 令牌和范围。一个网络失败,其寿命超过其重试是 ForumulateError 与 status: 0 财务报告和审定财务报表 code: 'network.error'。 。 。 .

报复和自责

安全重复的请求将在 408, (中文(简体) ), 429, (中文(简体) ), 502, (中文(简体) ), 503, (中文(简体) ), 504 网络 失败: 默认重复两次, 等待什么 Retry-After 说: 而不是睡觉过, 否则指数回落与焦躁。 A 类 500 未重审.

每个POST和PACH都带有 Idempotency-Key,每次呼叫一次,再使用一次 重试,所以写一个超时的回试,没有跑两次. 传出你自己去去除尘器 跨进程:

await forumulate.items.create({ orgId, spaceId, body, idempotencyKey: message.id });

每通电话都要 headers, (中文(简体) ), signal (单位:千美元) AbortSignal和 maxRetries。 。 。 .

选项

new ForumulateClient({
  baseUrl: 'https://forumulate.com/api',
  token,
  apiVersion: '2027-02-01', // Forumulate-Version for every request (default: generated methods only)
  maxRetries: 2, // 0 turns retries off
  autoIdempotencyKey: true, // generated Idempotency-Key on POST/PATCH
  userAgent: 'my-app/1.0', // default forumulate-sdk-js/<version> outside a browser
  headers: { 'X-Trace': id }, // sent on every request
  fetch: customFetch, // any fetch-compatible function
  credentials: true, // browser on the same origin: send the session cookie instead of a token
});

默认 User-Agent 浏览器外的事项:边缘防火墙挑战默认库用户代理.

软件包没有运行时的依赖性,并且标记为无副作用 命名空间大约为100个KB未被忽略(约20个KB gziped),命名空间是先用后建.

维护者: 重新生成类型

src/schema.ts 备份 typed()。它是生成的,而且只有类型;不要手工编辑。 从部署的光谱中刷新它 :

curl -s https://forumulate.com/api/openapi.json -o packages/sdk/openapi.json
pnpm --filter @forumulate/sdk generate-schema   # → src/schema.ts, then src/operations.gen.ts

src/operations.gen.ts 是命名空间后面的路由表( y)generate-operations 重建它 (仅此一次)。 安置在 scripts/generate-operations.mjs: 操作ID标签的短地图 前缀为命名空间,所以新标签在某个地方登陆时不需要编辑. src/coverage.test.ts 失败 当表格僵化时,当操作没有方法时,或者当一个0.5助手和生成一个 方法共享名称而不是操作.

许可证

麻省理工学院

版本与发布记录

@forumulate/sdk 共发布 28 个版本,首次发布于 2026/10/03;过去 12 个月发布了 28 个版本。

版本发布日期距今
0.13.8latest2026/11/132 天前
0.13.72026/11/133 天前
0.13.62026/11/133 天前
0.13.52026/11/123 天前
0.13.42026/11/124 天前
0.13.32026/11/124 天前

关于 @forumulate/sdk 的常见问题

@forumulate/sdk 是什么?

用于Forumulate REST API的已打入类型脚本客户端.

如何安装 @forumulate/sdk?

在项目目录运行 npm install @forumulate/sdk,或使用 pnpm add / yarn add / bun add。

@forumulate/sdk 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@forumulate/sdk 支持 ESM 还是 CommonJS?

以 CommonJS 形式发布,可以直接 require();在 ES Module 项目中也可以用 import 引入(Node.js 会把 module.exports 作为默认导出)。

@forumulate/sdk 可以商用吗?

@forumulate/sdk 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@forumulate/sdk 的下载量有多少?

根据 npm 官方下载统计,@forumulate/sdk 在 2026/10/16 – 2026/11/14 这 30 天内被下载 5,025 次,日均约 168 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@forumulate/sdk 还在维护吗?

最新版本 0.13.8 发布于 2026/11/13(2 天前);至今共发布 28 个版本,其中过去 12 个月发布了 28 个;首次发布于 2026/10/03。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。