# @freeloapp/js-sdk 中文文档

规范地址：https://npmcn.com/package/%40freeloapp/js-sdk/

版本：2\.7\.5
npm 元数据更新时间：2026-11-03T15:23:24.174Z
中文译文更新时间：2026-11-15T16:53:39.601Z
软件包许可：MIT

本文由上游 README 翻译，可能存在偏差，请结合原文核对 API、命令与配置。版权与许可归原作者所有。

原始 npm 页面：https://www.npmjs.com/package/%40freeloapp/js-sdk
原文对照：https://npmcn.com/package/%40freeloapp/js-sdk/?lang=original
项目主页：https://github.com/freeloapp/js-sdk#readme
源码仓库：https://github.com/freeloapp/js-sdk

## 关键信息（来自 npm 元数据）

- 包体积（解压后）：1.5 MB，9 个文件
- 模块格式：ESM + CJS
- TypeScript 类型：包内自带
- Node.js 要求：\>=18
- 运行依赖：0 个
- 首次发布：2026-02-28T08:38:12.579Z
- 最新版本发布：2026-11-03T15:23:23.829Z
- 近 30 天下载量：2168（2026-10-16 至 2026-11-14，npm 官方下载统计）
- 已发布版本数：17（近 12 个月 17 个）

## 中文简介

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

## 安装

```sh
npm install @freeloapp/js-sdk
```

也可以使用：`pnpm add @freeloapp/js-sdk`、`yarn add @freeloapp/js-sdk`、`bun add @freeloapp/js-sdk`

## README

# 自由式SDK

官方 JavaScript/ TypeScript SDK 为 [Freelo.io (英语)](https://app.freelo.io/) API (英语).

[![npm version](https://img.shields.io/npm/v/@freeloapp/js-sdk.svg)](https://www.npmjs.com/package/@freeloapp/js-sdk) [![TypeScript](https://img.shields.io/badge/TypeScript-Ready-blue.svg)](https://www.typescriptlang.org/) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

## 特征

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

## 安装

```bash
npm install @freeloapp/js-sdk
```

## 快速启动

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

// Initialize client (sets global default)
createFreelo({
  auth: { type: 'basic', email: 'your@email.tld', apiKey: 'your-api-key' },
  userAgent: 'YourApp/1.0 (contact@yourapp.com)',
});

// 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 设置](https://app.freelo.io/profil/nastaveni)。 。 。 .

```typescript
createFreelo({
  auth: { type: 'basic', email: 'your@email.tld', apiKey: 'your-api-key' },
  userAgent: 'YourApp/1.0',
});
```

### 守护者托肯

使用预取的JWT或 PASETO 令牌 :

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

### OAuth 2.1 (PKCE) (捷克语)

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

**第1步: 启动 OAuth 流量**

```typescript
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步: 将授权代码换为令牌**

```typescript
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 客户端**

```typescript
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 公用事业:**

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

## 配置

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

const client = createFreelo({
  auth: {                        // Required: authentication credentials
    type: 'basic',               //   Basic Auth with email + API key
    email: 'your@email.tld',
    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( 浏览器, 嵌入的网络) 设置 视图),您需要通过自定义头来识别客户端:

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

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

## 多用途/ 请求认证

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

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

// Basic Auth client
const basicClient = createFreelo({
  auth: { type: 'basic', email: 'admin@company.com', 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 光谱](https://api.freelo.io/docs/v1/freelo-api.yaml)。每个函数直接映射到一个API端点.

### 项目

```typescript
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 } });
```

### 任务

```typescript
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 },
});
```

### 任务标签

```typescript
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`。 。 。 .

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

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

### 任务列表

```typescript
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' },
});
```

### 评论

```typescript
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!' },
});
```

### 时间跟踪

```typescript
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 } });
```

### 搜索

```typescript
import { search } from '@freeloapp/js-sdk';

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

## 倾斜

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

```typescript
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();
```

## 处理错误

```typescript
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` 页眉](https://datatracker.ietf.org/doc/draft-ietf-httpapi-ratelimit-headers/),比如说. `RateLimit-Policy: "default";q=60;w=20` (单位:千美元) `q` 请求数 `w` 秒) 和 时间 `RateLimit: "default";r=42;t=5` (单位:千美元)`r` 剩余请求, 全部填入 `t` 页:1. `Retry-After` 是权威的.

## 工具函数

### 日期公用事业

```typescript
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
```

### 货币

```typescript
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`。 。 。 .

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

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

## 类型脚本支持

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

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

## 从v1移徙

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

```typescript
// 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 是什么？

用于 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 和提交记录判断。

