@freeloapp/js-sdk

中文文档
2.7.5发布于 2026/11/03MITTypeScriptESM + CJS零依赖构建来源已验证近 30 天下载 2168

用于 Freelo.io API 的官方 JavaScript/ TypeScript SDK

中文使用文档
文档目录30 节
  1. 特征
  2. 安装
  3. 快速启动
  4. 认证
  5. 基本认证
  6. 守护者托肯
  7. OAuth 2.1 (PKCE) (捷克语)
  8. 配置
  9. 默认信头
  10. 多用途/ 请求认证
  11. API 参考
  12. 项目
  13. 任务
  14. 任务标签
  15. 通知自己自己的行动
  16. 任务列表
  17. 评论
  18. 时间跟踪
  19. 搜索
  20. 倾斜
  21. 处理错误
  22. 限制费率
  23. 工具函数
  24. 日期公用事业
  25. 货币
  26. 更新 SDK 当 API 更改时
  27. 类型脚本支持
  28. 从v1移徙
  29. 捐款
  30. 许可证

自由式SDK

官方 JavaScript/ TypeScript SDK 为 Freelo.io (英语) API (英语).

npm version TypeScript License: MIT

特征

  • 从 OpenAPI 自动生成 总是与Freelo API同步
  • 带有类型定义的完整类型脚本支持
  • 0 运行时间依赖( 使用本地获取)
  • 树能震动 ——只导入您需要的
  • 在Node.js 18+、浏览器和所有主要框架工作
  • 基于承诺的合成/等待API
  • 多租户假想的每个请求授权
  • 建造的平面、货币和日期公用事业

安装

npm install @freeloapp/js-sdk

快速启动

import { createFreelo, getProjects, createTask } from '@freeloapp/js-sdk';

// Initialize client (sets global default)
createFreelo({
  auth: { type: 'basic', email: '[email protected]', apiKey: 'your-api-key' },
  userAgent: 'YourApp/1.0 ([email protected])',
});

// Get all projects
const { data: projects } = await getProjects();

// Create a task
const { data: task } = await createTask({
  path: { tasklist_id: 123 },
  body: {
    name: 'New Task',
    worker_ids: [userId],
  },
});

认证

SDK支持三种认证方法:

基本认证

获取您的 API 密钥 Freelo 设置。 。 。 .

createFreelo({
  auth: { type: 'basic', email: '[email protected]', apiKey: 'your-api-key' },
  userAgent: 'YourApp/1.0',
});

守护者托肯

使用预取的JWT或 PASETO 令牌 :

createFreelo({
  auth: { type: 'bearer', token: 'your-jwt-token' },
  userAgent: 'YourApp/1.0',
});

OAuth 2.1 (PKCE) (捷克语)

对于通过Freelo的身份提供者认证用户的应用程序(identity.freelo.io) (中文(简体) ). SDK处理自动令牌刷新——你处理初始授权流.

第1步: 启动 OAuth 流量

import {
  generatePKCEChallenge,
  buildAuthorizationUrl,
} from '@freeloapp/js-sdk';

// Generate PKCE challenge (store verifier for step 2)
const pkce = await generatePKCEChallenge();
sessionStorage.setItem('pkce_verifier', pkce.codeVerifier);

// Build authorization URL and redirect the user
const authUrl = buildAuthorizationUrl({
  clientId: 'your-client-id',
  redirectUri: 'https://yourapp.com/callback',
  codeChallenge: pkce.codeChallenge,
  scope: ['read', 'write', 'projects', 'tasks'],
  state: crypto.randomUUID(),
});
window.location.href = authUrl;

第2步: 将授权代码换为令牌

import { exchangeCode } from '@freeloapp/js-sdk';

// In your callback handler (e.g. /callback)
const params = new URLSearchParams(window.location.search);
const tokens = await exchangeCode({
  code: params.get('code')!,
  codeVerifier: sessionStorage.getItem('pkce_verifier')!,
  clientId: 'your-client-id',
  redirectUri: 'https://yourapp.com/callback',
});

步骤 3: 创建 SDK 客户端

import { createFreelo, getProjects } from '@freeloapp/js-sdk';

createFreelo({
  auth: {
    type: 'oauth',
    accessToken: tokens.accessToken,
    refreshToken: tokens.refreshToken,
    clientId: 'your-client-id',
    expiresAt: tokens.expiresAt,
    onTokenRefreshed: (newTokens) => {
      // Persist new tokens (localStorage, database, etc.)
      localStorage.setItem('freelo_tokens', JSON.stringify(newTokens));
    },
  },
  userAgent: 'YourApp/1.0',
});

// Use the SDK normally — token refresh happens automatically
const { data: projects } = await getProjects();

SDK主动刷新令牌(到期前为30个)并反应(401个回复). 同步请求共享一个刷新呼叫 .

其它 OAuth 公用事业:

import {
  refreshAccessToken,    // Manually refresh a token
  revokeToken,           // Revoke an access or refresh token
  discoverOAuthServer,   // Fetch OAuth server metadata (RFC 8414)
} from '@freeloapp/js-sdk';

配置

import { createFreelo } from '@freeloapp/js-sdk';

const client = createFreelo({
  auth: {                        // Required: authentication credentials
    type: 'basic',               //   Basic Auth with email + API key
    email: '[email protected]',
    apiKey: 'your-api-key',
  },
  userAgent: 'YourApp/1.0',      // Required: Identifies your application
  baseUrl: 'https://api.freelo.io/v1',  // Optional: API base URL
  logging: true,                 // Optional: Log requests to console
  headers: {                     // Optional: default headers on every request
    'X-Custom-Header': 'custom-value',
  },
});

默认信头

使用 headers 选项,当您需要在每个请求中发送一个信头时 - 例如,添加跟踪或客户身份识别元数据,或何时 User-Agent 无法从 JavaScript( 浏览器, 嵌入的网络) 设置 视图),您需要通过自定义头来识别客户端:

createFreelo({
  auth: { type: 'bearer', token: '...' },
  userAgent: 'YourApp/1.0',
  headers: {
    'X-Custom-Header': 'custom-value',
  },
});

按请求 headers 覆盖这些默认值 。 内置 Authorization 财务报告和审定财务报表 User-Agent 信头总是获胜——使用 auth 财务报告和审定财务报表 userAgent 来改变它们.

多用途/ 请求认证

为不同的用户创建多个客户端实例 :

import { createFreelo, getProjects } from '@freeloapp/js-sdk';

// Basic Auth client
const basicClient = createFreelo({
  auth: { type: 'basic', email: '[email protected]', apiKey: 'admin-key' },
  userAgent: 'MyApp/1.0',
});

// Bearer token client
const tokenClient = createFreelo({
  auth: { type: 'bearer', token: 'your-jwt-token' },
  userAgent: 'MyApp/1.0',
});

// OAuth client
const oauthClient = createFreelo({
  auth: { type: 'oauth', accessToken: '...', refreshToken: '...', clientId: '...' },
  userAgent: 'MyApp/1.0',
});

// Use default client (last one created)
const { data: projects } = await getProjects();

// Or pass client explicitly
const { data: adminProjects } = await getProjects({ client: basicClient });

API 参考

所有 SDK 函数都是自动生成的 Freelo OpenAPI 光谱。每个函数直接映射到一个API端点.

项目

import {
  getProjects,
  getAllProjects,
  getProject,
  createProject,
  deleteProject,
  archiveProject,
  activateProject,
  getProjectWorkers,
} from '@freeloapp/js-sdk';

// List own active projects
const { data: projects } = await getProjects();

// List all projects (paginated)
const { data } = await getAllProjects({ query: { p: 0 } });

// Get project by ID
const { data: project } = await getProject({ path: { project_id: 42 } });

// Create project
const { data: newProject } = await createProject({
  body: { name: 'New Project', currency: 'CZK' },
});

// Delete / archive / activate
await deleteProject({ path: { project_id: 42 } });
await archiveProject({ path: { project_id: 42 } });
await activateProject({ path: { project_id: 42 } });

任务

import {
  getAllTasks,
  getTask,
  createTask,
  editTask,
  finishTask,
  deleteTask,
  moveTask,
} from '@freeloapp/js-sdk';

// List all tasks (paginated)
const { data } = await getAllTasks({ query: { p: 0 } });

// Get task by ID
const { data: task } = await getTask({ path: { task_id: 99 } });

// Create task in a tasklist
const { data: newTask } = await createTask({
  path: { tasklist_id: 5 },
  body: { name: 'New Task', worker_ids: [1] },
});

// Edit task (name, description, labels, worker, due dates, priority, tracking users)
await editTask({
  path: { task_id: 99 },
  body: {
    name: 'Updated Name',
    description: '<p>Rewritten task body</p>', // overwrites the whole description
  },
});

// Finish / delete / move
await finishTask({ path: { task_id: 99 } });
await deleteTask({ path: { task_id: 99 } });
await moveTask({
  path: { task_id: 99 },
  body: { tasklist_id: 10 },
});

任务标签

import {
  findAvailableTaskLabels,
  createTaskLabels,
  addTaskLabelsToTask,
  removeTaskLabelsFromTask,
  mergeTaskLabels,
  getTaskLabelColors,
} from '@freeloapp/js-sdk';

// All labels available to the caller, optionally narrowed to one project
const { data: all } = await findAvailableTaskLabels();
const { data: inProject } = await findAvailableTaskLabels({
  query: { project_id: 42 },
});

// Fetch-or-create — an existing label with the same name AND color is reused
await createTaskLabels({ body: { labels: [{ name: 'bug', color: '#ff0000' }] } });

// Attach / detach on a task (by uuid, or by name + color)
await addTaskLabelsToTask({
  path: { task_id: 99 },
  body: { labels: [{ name: 'bug', color: '#ff0000' }] },
});
await removeTaskLabelsFromTask({
  path: { task_id: 99 },
  body: { labels: [{ uuid: 'aaaaaaaa-...' }] },
});

// Merge labels — every task carrying a source label ends up with the target label
await mergeTaskLabels({
  body: {
    from_uuids: ['aaaaaaaa-...', 'bbbbbbbb-...'],
    to_uuid: 'cccccccc-...',
  },
});

合并注释:目标的名称和颜色来自现有的 to_uuid 标签, 所有UUID必须归呼叫者所有( 否则 404),替换适用 仅用于呼叫者为指挥官的项目中的任务,以及源标签 定义与任务脱节,但不被删除.

通知自己自己的行动

默认情况下,API会从通知收件人中省略动作的作者. 过 notify_author: true 用于自动化 运行在自己的标志下。 获得支持 createTask 联合国 subtasks, (中文(简体) ), editTask, (中文(简体) ), activateTask, (中文(简体) ), finishTask, (中文(简体) ), editTaskcheck, (中文(简体) ), finishTaskcheck, (中文(简体) ), createComment, (中文(简体) ), editComment 财务报告和审定财务报表 editNote。 。 。 .

await createComment({
  path: { task_id: 99 },
  body: { content: 'Deployed to production', notify_author: true },
});

它只有在您是用户、工人或跟踪用户时才生效 目标.

任务列表

import {
  getAllTasklists,
  getTasklist,
  createTasklist,
} from '@freeloapp/js-sdk';

const { data } = await getAllTasklists();
const { data: tasklist } = await getTasklist({ path: { tasklist_id: 5 } });
const { data: newTasklist } = await createTasklist({
  path: { project_id: 42 },
  body: { name: 'New Tasklist' },
});

评论

import { getAllComments, createComment, editComment } from '@freeloapp/js-sdk';

const { data } = await getAllComments({ path: { task_id: 99 } });
const { data: comment } = await createComment({
  path: { task_id: 99 },
  body: { content: 'Hello!' },
});

时间跟踪

import { startTimeTracking, stopTimeTracking, editTimeTracking } from '@freeloapp/js-sdk';

await startTimeTracking({
  path: { task_id: 99 },
  body: { note: 'Working on feature' },
});

await stopTimeTracking({ path: { task_id: 99 } });
import { search } from '@freeloapp/js-sdk';

const { data: results } = await search({
  body: { search: 'bug fix' },
});

倾斜

SDK提供处理分解反应的公用设施:

import { fetchAllPages, iteratePages, createPaginator, getAllProjects } from '@freeloapp/js-sdk';

// Iterate through all items one by one
for await (const project of iteratePages(
  (page) => getAllProjects({ query: { p: page } }),
  (response) => response.data ?? []
)) {
  console.log(project.name);
}

// Fetch all pages at once
const allProjects = await fetchAllPages(
  (page) => getAllProjects({ query: { p: page } }),
  (response) => response.data ?? []
);

// Create a reusable paginator
const paginator = createPaginator(
  (page) => getAllProjects({ query: { p: page } }),
  (response) => response.data ?? []
);
const all = await paginator.fetchAll();

处理错误

import { getProject, isFreeloError, isRateLimited, isUnauthorized, isNotFound } from '@freeloapp/js-sdk';

const { data, error, response } = await getProject({ path: { project_id: 123 } });

if (error) {
  if (isRateLimited(error)) {
    console.log(`Rate limited, retry after ${response?.headers.get('Retry-After') ?? '?'} s`);
  } else if (isUnauthorized(error)) {
    console.log('Invalid credentials');
  } else if (isNotFound(error)) {
    console.log('Project not found');
  } else if (isFreeloError(error)) {
    console.log('API error:', error.message);
  }
} else {
  console.log('Project:', data);
}

限制费率

每个 Freelo 用户的 API 费率限制请求( token buck) 。 限制由同一用户的所有应用程序和符号共同使用,而准确的数字是可配置并可能改变的——不要硬编码.

  • 超过时,你会得到一个 429 状态代码为 a Retry-After 头(秒). 在重试前至少要等那么长时间; 在重复的429s上使用指数回放 。 SDK自动登录警告.
  • 答复还附有信息 机构间工作组 RateLimit 页眉,比如说. RateLimit-Policy: "default";q=60;w=20 (单位:千美元) q 请求数 w 秒) 和 时间 RateLimit: "default";r=42;t=5 (单位:千美元)r 剩余请求, 全部填入 t 页:1. Retry-After 是权威的.

工具函数

日期公用事业

import { dateToApi, dateFromApi, today, daysFromNow } from '@freeloapp/js-sdk';

dateToApi(new Date());       // "2024-01-15"
dateFromApi('2024-01-15');   // Date object
today();                     // Today in API format
daysFromNow(7);              // 7 days from now

货币

import { currencyToApi, currencyFromApi, formatCurrency } from '@freeloapp/js-sdk';

currencyToApi(123.45);              // "12345"
currencyFromApi("12345");           // 123.45
formatCurrency(123.45, 'CZK');     // "123,45 Kc"

更新 SDK 当 API 更改时

SDK来自在线 OpenAPI spec at https://api.freelo.io/docs/v1/freelo-api.yaml。 。 。 .

npm run generate          # Re-generate from latest spec
npm run generate:check    # Verify generated code matches committed version

CI 管道检查 每周一次的光谱变化.

类型脚本支持

所有类型都是自动生成和完全输入的 :

import type { ProjectFull, TaskFull, UserBasic, TaskCreate } from '@freeloapp/js-sdk';

从v1移徙

v2 将基于类的 API 替换为可树动函数 :

// v1 (class-based)
import { Freelo } from '@freeloapp/js-sdk';
const freelo = new Freelo({ email, apiKey, userAgent });
const projects = await freelo.projects.list();

// v2 (function-based, tree-shakeable)
import { createFreelo, getProjects } from '@freeloapp/js-sdk';
createFreelo({ auth: { type: 'basic', email, apiKey }, userAgent });
const { data: projects } = await getProjects();

捐款

欢迎捐款! 在提交拉动请求之前,请阅读我们提供的资料准则.

许可证

麻省理工学院

版本与发布记录

@freeloapp/js-sdk 共发布 17 个版本,首次发布于 2026/02/28;过去 12 个月发布了 17 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
2.7.5latest2026/11/0312 天前
2.7.42026/10/2125 天前
2.7.32026/09/231 个月前
2.7.22026/09/211 个月前
2.7.12026/08/282 个月前
2.7.02026/08/282 个月前

关于 @freeloapp/js-sdk 的常见问题

@freeloapp/js-sdk 是什么?

用于 Freelo.io API 的官方 JavaScript/ TypeScript SDK

如何安装 @freeloapp/js-sdk?

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

@freeloapp/js-sdk 支持 TypeScript 吗?

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

@freeloapp/js-sdk 支持 ESM 还是 CommonJS?

同时提供 ES Module 与 CommonJS 入口,import 和 require() 都可以使用,打包工具会自动选择合适的版本。

@freeloapp/js-sdk 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=18。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@freeloapp/js-sdk 可以商用吗?

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

@freeloapp/js-sdk 的下载量有多少?

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

@freeloapp/js-sdk 还在维护吗?

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