meeglesdk
v0.2.1
Published
飞书项目 Open API TypeScript SDK
Downloads
1,068
Readme
meeglesdk
TypeScript SDK for Feishu Project (Meego) Open API.
Runtime: Node.js 18+ (server-side only; browser usage is not supported).
Release notes: CHANGELOG.md Release protocol: RELEASE.md
Install
npm install meeglesdkQuick Start
import { MeegoClient, withUserKey } from 'meeglesdk';
const client = new MeegoClient({
pluginId: 'YOUR_PLUGIN_ID',
pluginSecret: 'YOUR_PLUGIN_SECRET',
baseURL: 'https://project.feishu.cn',
});
const items = await client.workItem.query(
'project_key',
'story',
{ work_item_ids: [123456] },
withUserKey('user_key')
);Client Options
const client = new MeegoClient({
pluginId: 'YOUR_PLUGIN_ID',
pluginSecret: 'YOUR_PLUGIN_SECRET',
baseURL: 'https://project.feishu.cn', // optional
timeout: 30000, // optional, ms
retry: {
maxRetries: 2,
retryDelay: 1000, // base delay, exponential backoff
maxRetryDelay: 30000, // optional cap for backoff delay
retryableErrorCodes: [10429, 50006] // optional override
},
rateLimit: 'meego-openapi', // preset: doc-based limits (15 QPS + endpoint overrides)
// or customize:
// rateLimit: {
// enabled: true, // optional, default true when provided
// qps: 15, // per token QPS
// burst: 15, // optional burst size
// scope: 'token+method+path', // 'token' | 'token+path' | 'token+method+path'
// entryTtlMs: 600000, // optional, idle bucket cleanup (default 10 min)
// rules: [ // optional overrides
// { method: 'POST', path: '/open_api/work_item/actual_time/update', qps: 10 },
// ],
// },
tokenType: 0, // 0: plugin_access_token, 1: virtual_plugin_token
onTokenRefresh: (token, type, userKey) => {
// persist token if needed
},
logger: console, // optional
});Auth Flows
Plugin Token (Server)
const token = await client.auth.getPluginToken();User Token (Auth Code Exchange)
const { token, expire_time, refresh_token } = await client.auth.getUserToken(code);Manually Set User Token
client.setUserToken({
userKey,
token,
expireTime: expireTimeSeconds,
refreshToken,
refreshTokenExpireTime,
saasTenantKey: tenantKey,
});Request-Level Auth (Recommended for Multi-User)
Do not store user tokens on a shared client. Instead, pass auth per request:
await client.workItem.query(
'project_key',
'story',
{ work_item_ids: [123456] },
{ auth: { type: 'user', token: userToken } }
);
// Helper shortcuts (return request options)
await client.workItem.query(
'project_key',
'story',
{ work_item_ids: [123456] },
withUserKey('user_key')
);Rules:
type: 'user': use the provided user token;userKeyis not needed.type: 'plugin': use plugin token;userKeyis required for user-scoped APIs.type: 'auto'(default): ifuserKeyis provided and a cached token exists for that user, it is used; otherwise plugin token is used.authMode(optional): only applies when a plugin token is used; controlsx-auth-modefor strict read permission checks.
x-auth-mode (Force Read Auth for Plugin Token)
When calling read APIs with plugin_access_token (or virtual_plugin_token), you can set x-auth-mode to
force the server to respect the X-User-Key permissions.
This header has no effect when a user token is used.
authMode: 1-> strict mode (recommended when you must enforce the user's read permissions)authMode: 0or omitted -> compatibility mode
await client.workItem.query(
'project_key',
'story',
{ work_item_ids: [123456] },
{ auth: { type: 'plugin', userKey: 'user_key', authMode: 1 } }
);Token Management Notes
TokenManagercachesplugin_access_tokenand refreshes it automatically.user_access_tokenis cached peruserKeywhen you callauth.getUserTokenorclient.setUserToken.user_access_tokenwill be auto-refreshed per user when possible (requiresrefresh_token).- Use request-level
authfor multi-user apps; avoid sharing a client-scoped user token across users.
Timeout & Retry
- Default timeout: 30s.
- Retries use exponential backoff with a base delay and are enabled by default.
- Network errors, timeouts, and retryable API error codes are retried (default:
10429,50006). - Per-request overrides are supported via
options.retry,options.skipRetry, andoptions.timeout.
AI Field
await client.aiField.update({
ai_task_id: '7629005114472926736',
field_value: 'Generated field value',
});AI field webhooks use event types 8101 and 8102.
Work Item Field Options
const options = await client.workItem.getFieldOptions({
project_key: 'project_key',
work_item_type_key: 'story',
work_item_id: 123456,
field_key: 'priority',
});Compound Field Values
await client.workItem.updateCompoundFieldValue({
project_key: 'project_key',
work_item_id: 123456,
field_key: 'field_compound',
action: 'add',
fields: [[{ field_key: 'sub_field', field_value: 'value' }]],
});WBS View (Query + Body)
const wbs = await client.workItem.workflow.getWbsView(
'project_key',
'story',
123456,
{
query: { need_union_deliverable: true, need_schedule_table_agg: true },
body: {
expand: {
need_union_deliverable: true,
need_wbs_relation_chain_entity: true,
need_wbs_relation_chain_path: true,
},
},
},
{ auth: { type: 'plugin', userKey: 'user_key' } }
);WBS Draft
WBS Draft is a standalone sub-service under workItem.wbsDraft.
Do not call draft APIs from workItem.workflow.
const draft = await client.workItem.wbsDraft.createDraft({
project_key: 'project_key',
work_item_id: 123456,
work_item_type_key: 'story',
});
await client.workItem.wbsDraft.patchDraft({
project_key: 'project_key',
work_item_id: 123456,
draft_id: draft.draft_meta.draft_id,
operation: {
operation_type: 'UpdateName',
operation_value: {
update_name: {
uuid: 'node_xxx',
name: '更新后名称',
},
},
},
});
const confs = await client.workItem.wbsDraft.getSubWorkItemConf({
project_key: 'project_key',
work_item_id: 123456,
draft_id: draft.draft_meta.draft_id,
node_uuid: 'node_xxx',
});
const createProgress = await client.workItem.wbsDraft.getCreateDraftProgress({
project_key: 'project_key',
work_item_id: 123456,
draft_id: draft.draft_meta.draft_id,
});
const patchProgress = await client.workItem.wbsDraft.getPatchDraftProgress({
project_key: 'project_key',
transaction_id: 'tx_xxx',
});
const publishResult = await client.workItem.wbsDraft.publishDraft({
project_key: 'project_key',
work_item_id: 123456,
draft_id: draft.draft_meta.draft_id,
});
const publishProgress = await client.workItem.wbsDraft.getPublishDraftProgress({
project_key: 'project_key',
work_item_id: 123456,
draft_id: draft.draft_meta.draft_id,
publish_order_id: publishResult.publish_order_id,
});
const resetResult = await client.workItem.wbsDraft.resetDraft({
project_key: 'project_key',
work_item_id: 123456,
draft_id: draft.draft_meta.draft_id,
});
const resetProgress = await client.workItem.wbsDraft.getResetDraftProgress({
project_key: 'project_key',
work_item_id: 123456,
draft_id: draft.draft_meta.draft_id,
});
const structure = await client.workItem.wbsDraft.queryStructure({
project_key: 'project_key',
work_item_id: 123456,
draft_id: draft.draft_meta.draft_id,
});
const rowDetails = await client.workItem.wbsDraft.queryRowDetails({
project_key: 'project_key',
work_item_id: 123456,
draft_id: draft.draft_meta.draft_id,
uuids: ['row_xxx'],
row_field_marks: ['meta.*', 'base.*'],
});
const rowConfig = await client.workItem.wbsDraft.queryRowConfig({
project_key: 'project_key',
work_item_id: 123456,
draft_id: draft.draft_meta.draft_id,
uuid: 'row_xxx',
});WBS View
WBS View is a standalone sub-service under workItem.wbsView.
const structure = await client.workItem.wbsView.queryStructure({
project_key: 'project_key',
work_item_id: 123456,
});
const rowDetails = await client.workItem.wbsView.queryRowDetails({
project_key: 'project_key',
work_item_id: 123456,
uuids: ['row_xxx'],
row_field_marks: ['meta.*', 'base.schedule'],
});
const expandInfo = await client.workItem.wbsView.queryExpandInfo({
project_key: 'project_key',
work_item_id: 123456,
work_item_type_key: 'story',
expand: {
need_parent_work_item: true,
need_wbs_relation_chain_entity: true,
need_wbs_relation_chain_path: true,
},
});Universal Search (Field Select)
const request = {
project_key: 'project_key',
work_item_type_key: 'story',
search_group: {
conjunction: 'AND',
search_params: [{ param_key: 'created_at', value: Date.now() - 86400000, operator: '>' }],
},
field_selected: ['work_item_id', 'name'],
pagination: { page_size: 20 },
};
const result = await client.workItem.search.universalSearch(request, {
auth: { type: 'plugin', userKey: 'user_key' },
});
// Next page
const next = await client.workItem.search.universalSearch(
{
...request,
pagination: { page_size: 20, search_after: result.pagination.search_after },
},
{ auth: { type: 'plugin', userKey: 'user_key' } }
);Services Overview
Top-level services:
authworkItemspaceuserconfigviewmeasuretenanteventaiNodeaiField
workItem sub-services:
search,batch,comment,subtask,attachment,chat,workflow,wbsDraft,wbsView,workHour,review
Tests
Integration tests use real credentials in tests/config.ts.
bun testNetwork access is required.
Unit Tests (Edge Cases)
Unit tests mock network calls and focus on request parsing, retries, and token caching.
bun test tests/unitPerformance Benchmarks (Local)
Quick micro-benchmarks for request parsing and big-int handling (no network).
bun run tests/perf/benchmark.ts