@ coloop-ai/ 答复者- sdk
类型安全客户端绑定 答复者.io 合伙人API. 操作来自提供者的 OpenAPI 文档和 包裹在一个基因组SDK.
项目由CoLoop维护,不附属于或核可 由答复者.io——在使用前审查其API术语.
安装
pnpm add @coloop-ai/respondent-sdk
zod 是一个运行时间依赖( e)^3.25.0 || ^4.0.0(笑声) (掌声)
生成 Zod 计划导入自 zod/v4。 。 。 .
快开始
import { RespondentSdk } from '@coloop-ai/respondent-sdk'
const respondent = new RespondentSdk({
apiKey: process.env.RESPONDENT_API_KEY!, // sent as x-api-key
apiSecret: process.env.RESPONDENT_API_SECRET!, // sent as x-api-secret
timeoutMs: 10_000, // abort requests that hang for 10s
})
// Lookup IDs differ between staging and production, so resolve them at
// runtime rather than hardcoding them.
const topics = await respondent.lookups.topics({ pageSize: 1 })
const topicId = topics.results[0]?.id
if (!topicId) throw new Error('No project topics on this environment')
const project = await respondent.projects.create({
publicTitle: 'Coffee habits study',
publicInternalName: 'coffee-habits-q1',
targetMarketType: 'b2c',
typeOfResearch: 'remote',
targetResearchMethodology: 'oneOnOne',
participantTimeRequiredMinutes: 30,
incentiveAmount: 50,
targetNumberOfParticipants: 5,
externalResearcher: {
researcherId: 'researcher-1',
researcherName: 'Ada Lovelace',
bookingUrl: 'https://example.com/book',
},
targetProjectTopics: [topicId],
})
await respondent.screenerQuestions.create(project.id, {
text: 'How often do you drink coffee?',
questionType: 'radio',
answers: [
{ text: 'Daily', answerValue: 1 }, // 1 qualifies
{ text: 'Never', answerValue: 2 }, // 2 disqualifies
],
})
// `publicDescription` is required at publish time, so set it first.
await respondent.projects.update(project.id, {
publicDescription: 'A 30 minute chat about how you drink coffee.',
})
await respondent.projects.publish(project.id)
然后让每个申请人走进招募周期:
const responses = await respondent.screenerResponses.list(project.id, {
status: 'PENDING',
})
for (const response of responses.results ?? []) {
await respondent.screenerResponses.qualify(project.id, response.id, {
qualifyStatus: true,
qualifiedOverriden: false,
disqualifyReasons: [],
message: '',
})
await respondent.screenerResponses.invite(project.id, response.id, {
meetingLink: 'https://example.com/session',
bookingLink: 'https://example.com/book',
})
}
// Then, in a later request — only once the session has actually happened,
// because this is what starts the incentive payment:
//
// await respondent.screenerResponses.markAttended(projectId, responseId)
一个可运行的版本生活在 examples/recruit-participants-demo.ts。 。 。 .
环境
| 环境 | 基础 URL |
|---|---|
| 生产 | https://api.respondent.io |
| 粘贴 | https://api-staging.respondent.io |
生产是默认的。 目标通过导出常数中转 :
import {
RESPONDENT_STAGING_BASE_URL,
RespondentSdk,
} from '@coloop-ai/respondent-sdk'
const respondent = new RespondentSdk({
apiKey: process.env.RESPONDENT_STAGING_API_KEY!,
apiSecret: process.env.RESPONDENT_STAGING_API_SECRET!,
baseUrl: RESPONDENT_STAGING_BASE_URL,
})
粘接和生产是独立的世界: 单独的证书,单独的
网络用户,以及 不同的查找标识 工业、技能、专题和工作
标题。 运行时解析搜索ID respondent.lookups.* 改为
硬编码.
提供者的 OpenAPI 文档只声明中转服务器 。 生产
URL来自
他们的文档,则
生成客户端的默认值被绑定为生产 openapi-ts.config.ts。 。 。 .
认证
每个请求都带两个信头, x-api-key (身份证)和 x-api-secret
(密语). 全权证书由答辩方伙伴小组签发
生产准入需要经过审查的中试演示和
签署了《生活津贴》。 没有OAuth流量,也没有文件记载的范围模型.
证书和配置客户端保存于 #private 字段,没有
所以记录或连载 RespondentSdk 实例
无法打印,错误包含一个编辑的请求摘要而不是
Request 对象。 软件包没有导出
到达配置客户端: 生成的操作为平面
函数将客户端作为参数,所以没有类持有
建立客户登记册.
服务器控制的文本也被擦除。 答复头被减为a
名称的简短允许列表 — 一个标题 NAME 也由服务器控制, 所以一个
清单中未包括其价值—— 以及尚存价值,
和 statusText 和有效载荷, 发生任何密钥或
密钥替换为 [redacted]。将信息、代码和细节读回
所以它们也被覆盖了 一个反映
x-api-key 回来 X-Request-Id,或引用到错误消息中,或回答
带有一个以它命名的标题,但不能将其设置在错误上. cause 这是
基本错误,附则不变.
SDK也设定了 redirect: 'error'。节点将自定义信头转发到
源码变化方向调整——不同于 Authorization - 所以在重新定向后
将手 x-api-key 财务报告和审定财务报表 x-api-secret 指向任何主机重定向名称。 A 类
重定向请求以 a 失败 RespondentSdkTransportError 换句话说.
处理错误
每一个 SDK 产生的故障都会产生一个子类: RespondentSdkError,所以请
失败模式可以区分 :
| 错误 | 升起时 | cause |
|---|---|---|
RespondentSdkApiError | aPI以非-2xx状态(status, (中文(简体) ), statusText, (中文(简体) ), payload, (中文(简体) ), responseHeaders, (中文(简体) ), code, (中文(简体) ), detail(中文(简体) ) | 无——已读 payload |
RespondentSdkTransportError | 没有到达任何回复——DNS、连接重设或拒绝重定向 | 隐藏的获取错误 |
RespondentSdkResponseError | 其身体无法解码的成功状态(status, (中文(简体) ), statusText, (中文(简体) ), responseHeaders(中文(简体) ) | 页:1 SyntaxError |
RespondentSdkTimeoutError | 配置内容 timeoutMs 逾期(timeoutMs(中文(简体) ) | 无——已读 timeoutMs |
一个失败是故意的不是 SDK 错误: 当你通过您的
拥有 AbortSignal它拒绝你 signal.reason 完全如您所愿,你已经准备好了.
每个 SDK 错误都带有编辑请求摘要 request (单位:千美元)url, (中文(简体) ), method, (中文(简体) ),
标题 x-api-key 编号 : x-api-secret 改为 [redacted]),以及
携带 cause 仅当存在潜在错误时。 没有带错误
Response数字 : RespondentSdkApiError 财务报告和审定财务报表 RespondentSdkResponseError 曝光
status, (中文(简体) ), statusText (单位:千美元) responseHeaders 地图代替。 那张地图只能保存
允许的列表标题 retry-after,利率限制头,
content-type, (中文(简体) ), date 和请求编号头——其数值被擦除;
每个其它标题都被删除,名字也包括在内.
isRespondentSdkError 与其中任何一个匹配; isRespondentSdkApiError, (中文(简体) ),
isRespondentSdkTransportError, (中文(简体) ), isRespondentSdkResponseError 财务报告和审定财务报表
isRespondentSdkTimeoutError 狭.
import {
RespondentSdk,
isRespondentSdkApiError,
} from '@coloop-ai/respondent-sdk'
try {
await respondent.projects.close(projectId, {})
} catch (error) {
if (!isRespondentSdkApiError(error)) throw error
if (error.status === 429) {
console.warn('Respondent throttled request', error.payload)
return
}
throw new Error(
`Failed to close project (${String(error.status)}): ${error.message}`,
)
}
提供者不记录一个错误响应计划, 及其 OpenAPI
文档未声明出错机构. RespondentSdkApiError 因此,探测
error, (中文(简体) ), message 财务报告和审定财务报表 detail 字段,用于可读信息并坠落
回HTTP状态线. error.payload 总是持有生体.
费率限额记录为250项请求/秒(500起,100 000 000起/天)
和50项请求/第二次(100起,200 000起/日)进行中。 429号
反应机构和任何 Retry-After 头为无相.
请求超时
设定 timeoutMs 以中止超过期限的请求。 最后期限涵盖
读取响应正文,而不仅仅是接收响应头,所以是一个服务器
答案,然后摊子仍然游走它。 SDK拒绝
RespondentSdkTimeoutError,可以视之为可重试.
最多只能是正数 2_147_483_647 (约24.8天)和
建筑师会投出相反的. setTimeout 将更长的延迟回合包成
1ms,所以很长的最后期限 几乎会立即中止所有请求.
import {
RespondentSdk,
RespondentSdkTimeoutError,
} from '@coloop-ai/respondent-sdk'
const respondent = new RespondentSdk({
apiKey: process.env.RESPONDENT_API_KEY!,
apiSecret: process.env.RESPONDENT_API_SECRET!,
timeoutMs: 5_000,
})
try {
await respondent.projects.list()
} catch (error) {
if (error instanceof RespondentSdkTimeoutError) {
console.warn('Respondent request timed out, retrying shortly')
}
}
取消请求
每个操作都使用带有 AbortSignal数字 :
const controller = new AbortController()
setTimeout(() => controller.abort(new Error('user navigated away')), 1_000)
await respondent.projects.list(
{ status: 'DRAFT' },
{
signal: controller.signal,
},
)
电话用信号拒绝 reason 完全如您所愿,你已经准备好了 一个配置
timeoutMs 仍然应用在您的信号上; 无论哪个火先获胜.
观察请求
过 onRequestSettled 以便他听到每一个请求,一旦它解决,
无论它成功还是失败。 该事件带有已编辑的请求摘要
(单位:千美元)url, (中文(简体) ), method,并替换了带有证书的页眉 [redacted], (中文(简体) ),
回应 status 当一个人到达时, durationMs,以及 error 当呼叫时
拒绝:非-2xx状态的 SDK 错误、运输故障或超时,
或你自己的流产原因。 它用于伐木和测量,不能
掷出.
const respondent = new RespondentSdk({
apiKey: process.env.RESPONDENT_API_KEY!,
apiSecret: process.env.RESPONDENT_API_SECRET!,
onRequestSettled: (event) => {
const { method, url } = event.request
if (event.error === undefined) {
console.info(`respondent ${method} ${url} ${String(event.status)}`)
} else {
console.error(`respondent ${method} ${url} failed`, event.error)
}
},
})
一个边缘:2xx的身体无法被解码,报告时有其状态,没有
error因为反应本身还不错 电话仍然拒绝
RespondentSdkResponseError。 。 。 .
倾斜
列表终点 page (默认) 1, (中文(简体) ), pageSize (默认) 50和
includeCount (默认) false返回时 { totalResults?, page, pageSize, results }。 。 。 .
totalResults 只有在您通过时才出现 includeCount: true。 。 。 .
高级别方法
RespondentSdk 将操作组合成模块:
respondent.projects—— 说list, (中文(简体) ),create, (中文(简体) ),retrieve, (中文(简体) ),update, (中文(简体) ),delete, (中文(简体) ),copy, (中文(简体) ),publish, (中文(简体) ),pause, (中文(简体) ),close, (中文(简体) ),audienceSizeEstimate, (中文(简体) ),replaceExternalScreenerQuestions, (中文(简体) ),uploadFile, (中文(简体) ),pitchSuggestions。 。 。 .respondent.screenerQuestions—— 说list, (中文(简体) ),create, (中文(简体) ),replaceAll, (中文(简体) ),retrieve, (中文(简体) ),update, (中文(简体) ),delete, (中文(简体) ),reorder。 。 。 .respondent.screenerResponses—— 说list, (中文(简体) ),retrieve, (中文(简体) ),payoutSummary, (中文(简体) ),qualify, (中文(简体) ),invite, (中文(简体) ),schedule, (中文(简体) ),markAttended, (中文(简体) ),markNoShow, (中文(简体) ),reject, (中文(简体) ),report, (中文(简体) ),favorite, (中文(简体) ),hide, (中文(简体) ),cancelInvite, (中文(简体) ),cancelBooking, (中文(简体) ),cancelBookingAndReinvite, (中文(简体) ),participantCancelBooking, (中文(简体) ),ingestExternalScreenerAnswers, (中文(简体) ),payout。 。 。 .respondent.quota—— 说create, (中文(简体) ),retrieve, (中文(简体) ),update, (中文(简体) ),delete。 。 。 .respondent.webhooks—— 说list, (中文(简体) ),create, (中文(简体) ),retrieve, (中文(简体) ),deactivate, (中文(简体) ),listEventTypes, (中文(简体) ),simulate。 。 。 .respondent.pricing—— 说balanceSummary。 。 。 .respondent.profiles—— 说retrieve, (中文(简体) ),createTestParticipant(只结.respondent.teamRespondents—— 说list, (中文(简体) ),retrieve, (中文(简体) ),batchInvite。 。 。 .respondent.messaging—— 说conversations(单位:千美元)list, (中文(简体) ),create, (中文(简体) ),retrieve, (中文(简体) ),update, (中文(简体) ),markAsRead, (中文(简体) ),addParticipant, (中文(简体) ),removeParticipant和messages(单位:千美元)list, (中文(简体) ),retrieve, (中文(简体) ),inbox, (中文(简体) ),create) (中文(简体) ). 常务代表 (单位:千美元)listConversations, (中文(简体) ),createConversation, (中文(简体) ),listMessages, (中文(简体) ),createMessage, (中文(简体) ),inbox坐在舱内本身.respondent.lookups—— 说values, (中文(简体) ),industries, (中文(简体) ),jobTitles, (中文(简体) ),skills, (中文(简体) ),topics。 。 。 .
终点 提供者的标记已贬值(GET /v1/teams/{teamId}/projects, (中文(简体) ),
POST /v1/messaging/messages, 团队范围收件箱, 和腐烂的
添加- participant 路由) 没有被包. 它们仍然可以通过
生成客户端 .
网络呼号
Webhook 有效载荷和签名计划不在提供者的 OpenAPI 中
文档,所以 @coloop-ai/respondent-sdk/webhooks 和Zod一起做手模,还有..
添加签名验证 .
命令事项: 校验字节, 存储事件并声明其 id 在一个 原子步骤,回答2xx,只有这样,工作才能完成——从中读回 存储记录.
使这种变化持久化的活性:一行被储存在工作之前一直等待 成功, 唯一开始工作的事情 是投票 等待的行, 因此,在索赔交易之外,没有什么东西可以拖取交货.
import type { UnknownWebhookEvent } from '@coloop-ai/respondent-sdk/webhooks'
import {
WebhookEvent,
getWebhookSignatureHeader,
verifyAndDedupeWebhook,
} from '@coloop-ai/respondent-sdk/webhooks'
/**
* Write the whole event down and claim its `uuid`, in ONE atomic step — a
* unique index on `uuid` plus an "insert, ignore conflict" statement that
* stores the payload alongside it.
*
* Inserting the `uuid` on its own is NOT "processed". It records only that the
* event is safely on disk. Two concurrent copies of a replay both look new if
* the check and the claim are separate steps.
*/
async function claimAndStore(
event: UnknownWebhookEvent,
): Promise<'stored' | 'duplicate'> {
const inserted = await db
.insertInto('respondent_webhook_inbox')
.values({ uuid: event.uuid, payload: JSON.stringify(event) })
.onConflict((c) => c.column('uuid').doNothing())
.executeTakeFirst()
return inserted.numInsertedOrUpdatedRows === 1n ? 'stored' : 'duplicate'
}
export async function handleWebhook(request: Request) {
// Read the RAW BYTES, before any JSON parsing. Not `request.text()`: that
// decodes leniently, so it has already replaced invalid UTF-8 with U+FFFD
// before the SDK can reject the body.
const rawBody = new Uint8Array(await request.arrayBuffer())
const outcome = await verifyAndDedupeWebhook({
rawBody,
signatureHeader: getWebhookSignatureHeader(request.headers),
privateKey: process.env.RESPONDENT_WEBHOOK_PRIVATE_KEY!,
claimAndStore,
})
if (outcome.status === 'invalid_signature') {
return new Response('Forbidden', { status: 403 })
}
if (outcome.status === 'stored') {
// A nudge, not the trigger. The row is already pending on disk and the
// poll below picks it up regardless, so a failed wake-up strands nothing —
// which is why its failure is swallowed rather than answered non-2xx.
await wakeWebhookWorker().catch(() => {})
}
// A duplicate or an unparseable body is 2xx too: the provider retries
// anything else, and a replay must not do the work twice.
return new Response('OK')
}
工作开始于对尚未完成的行的民意调查,所以从不 取决于上面的警钟是否成功:
// Run this on a schedule, and on each wake-up.
async function drainPendingDeliveries() {
const pending = await db
.selectFrom('respondent_webhook_inbox')
.select(['uuid'])
.where('processedAt', 'is', null)
.orderBy('receivedAt')
.limit(100)
.execute()
for (const row of pending) {
await processStoredDelivery(row.uuid)
}
}
async function processStoredDelivery(uuid: string) {
const row = await db
.selectFrom('respondent_webhook_inbox')
.select(['payload'])
.where('uuid', '=', uuid)
.where('processedAt', 'is', null)
.executeTakeFirst()
if (!row) return // already processed, or never stored
const parsed = WebhookEvent.safeParse(JSON.parse(row.payload))
if (parsed.success) {
switch (parsed.data.event) {
case 'SCREENER_RESPONSES.CREATED': {
const { id, projectId } = parsed.data.payload.resource
// Payloads carry IDs only — fetch the response to read it.
const response = await respondent.screenerResponses.retrieve(
projectId,
id,
)
break
}
case 'PROJECTS.UPDATED': {
console.log(parsed.data.payload.resource.updatedFields)
break
}
}
} else {
console.warn('Unhandled webhook payload', parsed.error)
}
// Only now is the delivery processed.
await db
.updateTable('respondent_webhook_inbox')
.set({ processedAt: new Date() })
.where('uuid', '=', uuid)
.execute()
}
在请求处理人内部进行业务工作,在索赔之后,如何
交货丢失: uuid 已经声称,所以当工作抛出
提供者的重试作为复制品回来,工作永远不会发生. 储存
事件首先使重试没有必要——记录在磁盘上,a
抛出它等待下一次尝试.
紧接后 claimAndStore 返回失败,当队列是
唯一触发器: 行会犯, 排队抛出, 提供者的重试
读作重复, 从来没有运行过作品。 要么写工作
在与收件箱行相同的交易中,或如上所述,对待决交易进行投票
排行并视入等为最优化.
A 类 Uint8Array 一旦签名, 机体就用致命的 UTF-8 验证解码
已经核实了,所以一个不合法的UTF-8的尸体 回来了作为 malformed_body
而不是被修复。 宽放的解码器取代 U+FFFD 用于
无效序列, JSON.parse 接受已修复的文本, 以及您的事件
然后商店就是供货商从未送来的 检查只用字节 :
通过 new Uint8Array(await request.arrayBuffer()),因为 await request.text() 已经为您做了宽大解码 string
rawBody 当它到达时被接受.
如果您不想重新播放检查, 请用手进行验证 :
verifyWebhookSignatureFromRawBody 抓起线条字节
verifyWebhookSignatureFromParsedBody 散开 JSON.stringify(parsedBody)读取
下一节,然后选择.
这五个事件类型是 PROJECTS.UPDATED, (中文(简体) ), SCREENER_RESPONSES.CREATED, (中文(简体) ),
SCREENER_RESPONSES.UPDATED, (中文(简体) ), MESSAGES.CREATED 财务报告和审定财务报表 CONVERSATIONS.CREATED。 。 。 .
WebhookEvent 双倍的受歧视的工会类型
(单位:千美元)import type { WebhookEvent })和工会验证人
(单位:千美元)WebhookEvent.safeParse) (中文(简体) ). 每个事件计划都是个松散的物体,所以
提供商后来补充说,保留而不是剥去.
UnknownWebhookEvent 接受 SDK 尚未建模的事件类型 .
提供方的文档和没有的
这个 privateKey 用于签名的返回方式 POST /v1/webhooks,还有
以 GET /v1/webhooks 财务报告和审定财务报表 GET /v1/webhooks/{webhookId} 所有三个答案都与
同样的 WebhookDto ——所以一个丢失的密钥可以被读回而不是被再创建.
供养者
只签名样本
显示一个HMAC在车体上方, 底部64- 编码, 在
Respondent-Webhook-Signature 页眉为 <algorithm>=<signature>。 。 。 .
三个空白问题:
- 散列算法没有文献记录 . 他们的样本解析了算法
退出头部值本身,并发布无示例头。 因为
信头是攻击者控制的, 校验只接受一个
允许列表( e)
sha256, (中文(简体) ),sha512(默认情况下). 把它加宽allowedAlgorithms一旦提供者确认他们发送了哪一个. - 他们签的字节在任何地方都没有说明. 他们的花草样本
JSON.stringify(request.body)——被解析并被重序的对象——即 无法保证复制到线上的字节。 这个SDK 船舶的读取和主张都没有证明:verifyWebhookSignatureFromRawBody(默认和什么)verifyAndDedupeWebhook(b) 将交付的字节拖走verifyWebhookSignatureFromParsedBody散开JSON.stringify(parsedBody)。 。 。 . 只有真正的送货才能解决问题. 将网点指向终点 记录原始正文和签名头,并发送一个与respondent.webhooks.simulate(webhookId, { event }),看看谁是援助者 返回时true直到你这样做, 将选择视为未经证实. - 没有时间戳头和回放保护. HMAC 证明
真实性,而非新鲜度,供应商对最多5个交货进行重试
时间。 关于事件的重复
uuid—— 说verifyAndDedupeWebhook这样做是为了 你,一个原子claimAndStore将事件记录为 它声称身份证明.
两个较小的硬化笔记: 载有多个签名头的快递
以未署名方式处理, 一份文摘只有在有条形加法的情况下才解码
基础64. Buffer.from(value, 'base64') 自己默默接受跟踪
垃圾,并接受多个拼写 相同的文摘.
其他行动事实:每个小队都有1个正在运行的网页,事件起火 只针对通过 API 创建的项目, 您必须在 3 秒内返回 2xx, 并且 故障每隔10分钟重审5次。 网络呼号 配置在装配中不结转到生产中.
生成 Zod 计划
每个 OpenAPI 组件计划也作为 Zod 计划在下面排放
@coloop-ai/respondent-sdk/zod名称 z<SchemaName>。用它们来作曲
供货商在自己计划中的语句:
import * as z from 'zod/v4'
import { zGender, zScreenerResponseStatus } from '@coloop-ai/respondent-sdk/zod'
const Participant = z.object({
gender: zGender,
status: zScreenerResponseStatus,
})
生成的文件导入自 zod/v4,这决定了两者 [email protected]+
财务报告和审定财务报表 zod@4。 。 。 .
生成客户端访问
如果您需要完全控制请求选项, 请呼叫生成的操作 直接说 每个函数都是简单的函数,它占用了您配置的客户端; 没有生成的类,因此没有保存配置的注册 客户:
import { getV1Projects, sdk } from '@coloop-ai/respondent-sdk'
const client = sdk.createClient({
baseUrl: 'https://api.respondent.io',
headers: {
'x-api-key': process.env.RESPONDENT_API_KEY!,
'x-api-secret': process.env.RESPONDENT_API_SECRET!,
},
responseStyle: 'data',
})
const response = await getV1Projects({
client,
headers: {
'x-api-key': process.env.RESPONDENT_API_KEY!,
'x-api-secret': process.env.RESPONDENT_API_SECRET!,
},
query: { status: 'DRAFT' },
})
提供者声明 x-api-key 财务报告和审定财务报表 x-api-secret 作为标题参数比较
而不是作为 OpenAPI 安全方案,所以生成的类型需要它们每次调用
即使客户已经设定了 三项行动——
GET /v1/team-respondents, (中文(简体) ), GET /v1/team-respondents/profiles/{profileId} 财务报告和审定财务报表
PUT /v1/team-respondents/batch-invite ——声明完全没有标题参数,
这几乎肯定是一种疏忽 而不是未经证实的
路由; SDK 向每个请求发送证书.
responseStyle 接受 'data' (只有反应机构)或 'fields'
(单位:千美元){ data, request, response }) (中文(简体) ). RespondentSdk 用途 'fields' 内部和
返回尸体.
所有请求/答复类型均导出自 @coloop-ai/respondent-sdk。 。 。 .
在提供者的类别中已知的怪事
在此录制, 这样它们不会被误认为 SDK 错误 :
CreateB2cProjectDto.targetGenders编号 :CreateB2bProjectDto.targetGenders是一个 单人Gender,而所有其他人口目标 (单位:千美元)targetEthnicities, (中文(简体) ),targetAgeGroups, (中文(简体) ),targetEducation, (中文(简体) ),targetHouseholdIncome)是数组。 复数名有惑.QualifyScreenerResponseDto标记所有需要的四个字段,包括 拼写错误qualifiedOverriden编辑disqualifyReasons编号 :message字段 只有在取消资格时才适用.ProfileLocation.zipcode财务报告和审定财务报表ScreenerResponseInvitation.message声明"type": "string"与"default": null销售商剥离这些缺省 -- 而Zod生成器的代码是不编译的.ScreenerResponsesPaginatedDto不标记results需要时 其他发泡DTO是,所以response.results仅此而已.PUT /v1/projects/{projectId}/files/form-data声明Content-Type财务报告和审定财务报表Content-Length标题参数uploadFile身体财产可选择。 销售者删除两个标题参数并制作 需要文件 : 获取内容设置类型—— 带有多段边框, 任何人事先都不能知道--长度和上传-- 没有文件的电话从来就没有用过.- 三次小组答复行动宣布不
x-api-key编号 :x-api-secret标题参数, 而其他操作都会这样做 . - 文件宣布没有安全方案,没有错误计划,没有429
答复,以及
servers区块列表只运行.
scripts/normalize-openapi.ts 保持所有三个正常化, 每个报告
当计谋被更新的时候 pnpm run schema:validate 如果失败
卖出的文件会从正常形式中消失.
脚本
pnpm run schema:update- 唯一触及网络的脚本:获取 提供者的 OpenAPI 文档, 正常化, 在内存中验证, 然后替换schemas/openapi.json解剖论.pnpm run generate- 验证供应商的光谱并再生src/generated/**从它。 离线和可复制:始终相同的承诺 生成相同的输出.pnpm run generate:check– 重新生成到抓取目录中并失败 与承诺不同src/generated。 。 。 .pnpm run schema:validate- 验证销售商的规格并检查它仍然是 已经恢复正常.pnpm run tc- 排入排出量.pnpm run lint– ESLint横跨仓库.pnpm run test-维特.pnpm run test:dist- 烟雾测试 已建成的捆绑node --test。无 dev工具链,因此它也在节点18上运行. 建设先.pnpm run format- 更漂亮的检查.pnpm run build- 双重无害环境管理/CJS捆绑dist/通过tsdown.pnpm run prepare-运行build,因此软件包也从 基特语( git)github:Genei-Ltd/respondent-sdk#<sha>) (中文(简体) ).pnpm run check- 计划验证,generate:check,类型检查,林特, 格式检查,构建,测试,然后是 Dist 烟雾测试。 这是运行 在出版前, 以及CI运行.
节点版本
已公布的一揽子目标节点18及以上engines.node) (中文(简体) ). B. 拟订《公约》
SDK需要更新运行时间—— devEngines.runtime 请求22.18+节点,即:
什麽 @hey-api/openapi-ts,ESLint和Vitest需要.
这一差距得到弥补,而不是假设: .github/workflows/check.yml 运行
pnpm run check 在节点22上,然后装入已建成的捆绑并锻炼它们——
无害环境管理和CJS的切入点,每个子路径,请求,拒绝的再定向
超时和多段上传——在节点18,20,22和24上. 这些烟雾测试
(单位:千美元)tests/dist-smoke.mjs运行 pnpm run test:dist(a) 使用情况 node --test 编辑
软件包本身的运行时间依赖性, 仅此而已 .
许可证
根据麻省理工学院的许可获释. 见 LICENSE 详细情况.