@crimson-education/sdk
v0.2.4
Published
Crimson SDK for accessing Crimson App APIs
Downloads
543
Readme
@crimson-education/sdk 使用文档
@crimson-education/sdk 是一个用于访问 Crimson App(Roadmap 相关)后端接口的 TypeScript SDK,包含三层能力:
- Core(框架无关):
CrimsonClient+ 各业务 API(missions / tasks / roadmap / users / mission library) - iframe(跨域/嵌入场景):通过
postMessage初始化鉴权信息,并提供可订阅的鉴权状态 - React(可选):基于
@tanstack/react-query的 Provider 与 Hooks(通过@crimson-education/sdk/react引入)
说明:SDK 默认依赖运行时
fetch。在 Node.js 环境建议使用 Node 18+(内置fetch),或自行注入/polyfill。
安装与构建
安装
如果已发布到 npm:
npm i @crimson-education/sdk本地开发构建
在 crimson-sdk/ 目录下:
npm i
npm run build编译产物输出到 crimson-sdk/dist/(测试也使用该目录)。
快速开始(Core)
import { createCrimsonClient } from "@crimson-education/sdk";
const client = createCrimsonClient({
apiUrl: "https://api.example.com",
getToken: async () => "YOUR_TOKEN",
// 推荐:设置客户端标识,用于 API 调用追踪
clientId: "my-app",
// 可选:默认 Bearer;如使用自定义鉴权头格式可覆盖
// authScheme: "crimsonauthkey",
});
// 读取 roadmap context
const ctx = await client.roadmap.context("student-uid");
// 拉取 missions(不传 start/limit 时返回按 category 分组的旧结构)
const categories = await client.missions.list("student-uid");
// 拉取 missions(强制分页结构)
const page = await client.missions.listPaginated("student-uid", undefined, {
start: 0,
limit: 20,
});Core API 参考
1) CrimsonClient
入口:createCrimsonClient(config) / new CrimsonClient(config)
配置类型:CrimsonClientConfig
apiUrl: string:后端 API 基地址(SDK 会自动去掉末尾/)getToken: () => string | Promise<string>:获取 token 的函数authScheme?: string:鉴权 scheme(默认"Bearer")。SDK 会自动去掉 token 前缀"Bearer ",再拼接成Authorization: "<scheme> <token>"clientId?: string:客户端应用标识,用于 API 调用追踪(如"new-roadmap"、"capstone")clientVersion?: string:SDK 版本覆盖,默认使用 SDK 包版本
响应约定(CrimsonClient.fetch<T>):
- 若响应 JSON 形如
{ data: ... }且 没有pagination字段:SDK 会自动 解包,直接返回data - 若响应 JSON 形如
{ data: ..., pagination: ... }:SDK 返回整个对象(对应PaginatedResult<T>) - 若响应为空(如 204):返回
undefined - 若 HTTP 非 2xx:抛出
Error("Crimson SDK Error: <status> ... - <body>")
2) MissionsApi(client.missions)
涉及接口:
GET /roadmap/missionsPOST /roadmap/missionsPUT /roadmap/missions/:linkIdDELETE /roadmap/missions/:linkIdPOST /roadmap/missions/batchPOST /roadmap/missions/batch-deletePOST /roadmap/missions/batch-restore
主要方法:
list(userId, filters?)- 不传
start/limit:返回MissionsCategory[](兼容旧后端) - 传入
start/limit:返回PaginatedResult<Mission> filters(MissionFilters)支持:status[]、title、roadmapId、groupBy、dueDateStart、dueDateEnd、start、limit
- 不传
listPaginated(userId, filters?, pagination?)- 始终返回
PaginatedResult<Mission>(并在后端仍返回旧结构时做转换)
- 始终返回
create(data: Partial<Mission>)update(linkId: string, data: Partial<Mission>)delete(linkId: string)batchEdit(userId, roadmapId, missions: BatchMissionOperation[])- 单次请求内执行 add/update/delete(详见
BatchMissionAdd/Update/Delete类型)
- 单次请求内执行 add/update/delete(详见
batchDelete(linkIds: string[])batchRestore(linkIds: string[])
3) TasksApi(client.tasks)
涉及接口:
GET /roadmap/action-itemsGET /roadmap/action-items/creatorsPOST /roadmap/action-itemsPUT /roadmap/action-items/:idDELETE /roadmap/action-items/:idPOST /roadmap/action-items/:actionItemId/resourcesPOST /roadmap/uploadGET /roadmap/download
主要方法:
list(params, filters?)params支持:missionId/missionLinkIds/roadmapId/userIdfilters(TaskFilters)支持:status[]- 按状态过滤(如['PLANNED', 'DONE'])description- 按描述关键词过滤creatorId- 按创建者 ID 过滤dueDateStart- 按截止日期开始过滤(ISO 格式)dueDateEnd- 按截止日期结束过滤(ISO 格式)orderBy- 排序方式:'priority'|'dueDate'|'missionTitle'|'createdAt'start/limit- 分页参数
- 默认返回
Task[] - 当 同时提供
roadmapId且提供start/limit时:返回PaginatedResult<Task>
listPaginated(roadmapId, userId, filters?, pagination?)- 始终返回
PaginatedResult<Task>(内部复用list)
- 始终返回
getCreators(roadmapId)- 获取指定 roadmap 下所有任务的创建者列表
getDownloadUrl(key)- 获取 S3 资源的预签名下载 URL
create(data: Partial<Task>)- 支持两种模式:
- Mission 关联任务:提供
roadmapMissionId(或missionId) - 独立任务(Standalone):只提供
roadmapId,不关联 mission
- Mission 关联任务:提供
- 支持两种模式:
createStandalone(roadmapId: string, data)- 创建独立任务的便捷方法
update(id: string, data: Partial<Task>)- 支持更新
resources字段(完整替换)
- 支持更新
delete(id: string)addResources(actionItemId: string, resources: AddResourceInput | AddResourceInput[])- 向任务添加资源/附件
updateResources(taskId: string, resources: UpdateTaskResourceInput[])- 更新任务资源(支持新增、修改、删除)
- 包含
id的资源会被更新,不包含id的会新建,不在数组中的会被删除
getUploadUrl(filename: string, contentType: string): Promise<UploadUrlResponse>- 获取 S3 预签名上传 URL
- 返回
{ putUrl, url, key, bucket }
uploadFile(file: File | Blob, filename?: string): Promise<{ url, key }>- 上传文件到 S3 的便捷方法(内部调用
getUploadUrl+ PUT 上传)
- 上传文件到 S3 的便捷方法(内部调用
关于 Task 结构的“归一化”:
SDK 会将后端返回的 action item 做归一化,保证至少返回以下核心字段:
idname(优先name,否则使用description)date(优先date,否则使用dueDate)roadmapMissionId(优先roadmapMissionId,否则使用missionId/linkId)userId(优先userId,否则使用creatorId)isComplete(优先使用后端的isComplete,否则由status === DONE或finishedAt推导)
因此你可能看不到后端返回的所有原始字段(它们被统一抽象到上述字段里)。
4) RoadmapApi(client.roadmap)
涉及接口:
GET /roadmap/context?userId=...POST /roadmap/context
主要方法:
context(userId: string): Promise<RoadmapContext>createContext(userId: string): Promise<RoadmapContext>
5) UsersApi(client.users)
涉及接口:
GET /roadmap/users?ids=...
主要方法:
getByIds(userIds: string[]): Promise<User[]>- 根据用户 ID 列表获取用户信息
- 用于显示任务分配者(assignedBy)等场景
- 返回字段:
userId,firstName,lastName,email,profileImageId
getById(userId: string): Promise<User | undefined>- 获取单个用户信息的便捷方法
6) MissionLibraryApi(client.library)
涉及接口:
GET /roadmap/library/missionsPOST /roadmap/library/missionsPUT /roadmap/library/missions/:missionIdGET /roadmap/library/tasksPOST /roadmap/library/missions/copyPOST /roadmap/library/missions/assign-bulkPOST /roadmap/library/tasks/assign-bulkPOST /roadmap/library/tasks/create-from-predefinedGET /roadmap/missions/:id/detail
主要方法:
listTemplateMissions(filters?: TemplateMissionFilters): Promise<PaginatedResult<TemplateMission>>createTemplateMission(input: TemplateMissionCreateInput): Promise<TemplateMission>updateTemplateMission(id: string, raw: TemplateMission, update: TemplateMissionUpdateInput): Promise<TemplateMission>listTemplateTasks(filters?: TemplateTaskFilters): Promise<PaginatedResult<TemplateTask>>copyTemplateMission(input: CopyTemplateMissionInput): Promise<TemplateMission[]>assignBulkMission(input: AssignBulkMissionInput[]): Promise<{ code: number; msg?: string }>assignBulkTask(input: AssignBulkTaskInput[]): Promise<{ code: number; msg?: string }>createFromPredefinedTasks(input: { missionId: string; predefinedTaskIds: string[] }): Promise<TemplateTask[]>getMissionById(missionId: string): Promise<MissionDetail | null>- 注意:当后端返回非 2xx(例如 404)时,SDK 会抛异常;建议使用
try/catch处理。
- 注意:当后端返回非 2xx(例如 404)时,SDK 会抛异常;建议使用
iframe 层(嵌入/跨域场景)
入口:@crimson-education/sdk(主入口会导出 iframe 层)
典型用法:
- 在 iframe 内调用
setupIframeListener()监听父页面postMessage注入 token - 通过
getAuthState()/subscribeToAuthState()或 React HookuseAuthState()获取就绪状态
主要 API:
setupIframeListener(allowedOrigins?: string[]): CleanupFnallowedOrigins不传则使用默认白名单(也可通过NEXT_PUBLIC_ALLOWED_PARENTS覆盖)
getToken()/getUserId()/getStudentId()getAuthState(): AuthState/subscribeToAuthState(cb)persistStandaloneAuth(payload: ZoidProps)- 用于“非 iframe”本地调试:写入 localStorage 并触发就绪事件
常量:
XPROPS_READY_EVENTSTORAGE_KEYSVIRTUAL_MISSION_ID
父页面发送 INIT 消息的 payload 形状(示例):
window.frames[0]?.postMessage(
{
type: "INIT",
payload: {
token,
userId,
studentId,
user: {
/* 可选 */
},
},
},
"https://your-iframe-origin.example.com",
);React 层(@crimson-education/sdk/react)
React 层通过 peerDependencies 声明依赖(不会强制安装):
react(>=18)@tanstack/react-query(>=5)
CrimsonProvider
import { CrimsonProvider } from "@crimson-education/sdk/react";
export function App() {
return (
<CrimsonProvider
apiUrl="https://api.example.com"
clientId="my-app" // 推荐:设置客户端标识
>
<YourRoutes />
</CrimsonProvider>
);
}Props:
apiUrl: string:后端 API 基地址clientId?: string:客户端应用标识,用于 API 调用追踪allowedParentOrigins?: string[]:允许的父页面 origin 白名单queryClient?: QueryClient:可选的自定义 QueryClient 实例
CrimsonProvider 会:
- 创建并注入
CrimsonClient(包含 clientId 配置) - 自动从 iframe/xprops/localStorage 读取 token(使用 iframe 层的
getToken()) - 初始化
@tanstack/react-query的QueryClientProvider - 安装
postMessage监听(setupIframeListener)
Hooks(常用)
useAuthState():返回{ token, userId, studentId, ready, user? }useMissions(userId, enabled):拉取用户 missions(扁平化 Mission[])useMissionsInfinite(userId, filters?, options?):分页拉取 missionsuseTasks(missionId, enabled):拉取某 mission 的 tasksuseTasksInfinite(roadmapId, userId, filters?, options?):分页拉取 tasksuseRoadmapContext(userId, enabled):拉取 roadmap contextuseTaskCreators(roadmapId, enabled):获取任务创建者列表- CRUD mutation:
useCreateMission/useUpdateMission/useDeleteMissionuseCreateTask/useUpdateTask/useDeleteTaskuseCreateTemplateMission/useUpdateTemplateMissionuseGetDownloadUrl:获取文件下载地址
- 模板库相关:
useTemplateMissions/useTemplateMissionsInfiniteuseTemplateTasks/useTemplateTasksInfiniteuseTemplateMissionDetail(missionId)
常见问题
1) 为什么拿到的返回值不是 { data: ... }?
SDK 会默认解包后端常见的 { data: ... } 格式:大部分方法直接返回 data 本体。
只有当响应同时带 pagination(即 { data, pagination })时,才会返回完整对象。
2) Node 环境报 fetch is not defined?
请使用 Node 18+,或为运行环境提供 fetch polyfill(例如 undici)。
API 调用追踪
SDK 支持向后端发送客户端标识信息,用于追踪 API 调用来源。
发送的 Headers
当配置了 clientId 时,SDK 会自动在每个请求中发送以下 headers:
| Header | 说明 | 示例值 |
| ------------------- | -------------- | ------------------------- |
| X-Client-ID | 客户端应用标识 | new-roadmap, capstone |
| X-Client-Version | SDK 版本 | 0.2.0 |
| X-Client-Platform | 运行环境 | browser, node |
后端日志
后端会记录包含以下字段的结构化日志:
{
"type": "api_request",
"request_id": "uuid",
"client_id": "new-roadmap",
"client_version": "0.2.0",
"client_platform": "browser",
"auth_mode": "bearer",
"user_id": "auth0-xxx",
"tenant": "crimsonapp",
"method": "GET",
"path": "/roadmap/missions",
"status": 200,
"duration_ms": 123,
"timestamp": "2024-01-01T00:00:00.000Z"
}最佳实践
- 始终设置
clientId:便于区分不同应用的 API 调用 - 使用有意义的标识:如
new-roadmap、capstone、admin-dashboard - 响应头追踪:后端会返回
X-Request-IDheader,可用于调试和问题排查
