uapi-browser-sdk 0.1.13 → 0.1.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +62 -3
- package/dist/src/errors.d.ts +16 -0
- package/dist/src/errors.js +22 -0
- package/dist/src/index.js +7 -2
- package/package.json +1 -1
- package/src/errors.ts +39 -0
- package/src/index.ts +10 -2
package/README.md
CHANGED
|
@@ -18,11 +18,13 @@ npm i uapi-browser-sdk
|
|
|
18
18
|
```ts
|
|
19
19
|
import { UapiClient } from 'uapi-browser-sdk'
|
|
20
20
|
|
|
21
|
-
const client = new UapiClient('https://uapis.cn
|
|
22
|
-
const result = await client.
|
|
21
|
+
const client = new UapiClient('https://uapis.cn', 'YOUR_API_KEY')
|
|
22
|
+
const result = await client.misc.getMiscHotboard({ type: 'weibo' })
|
|
23
23
|
console.log(result)
|
|
24
24
|
```
|
|
25
25
|
|
|
26
|
+
这个接口默认只要传 `type` 就可以拿当前热榜。`time`、`keyword`、`timeStart`、`timeEnd`、`limit`、`sources` 都是按场景再传的可选参数。
|
|
27
|
+
|
|
26
28
|
## CDN 引入
|
|
27
29
|
|
|
28
30
|
`uapi-browser-sdk` 发布在 npm,因而可以直接通过常见 CDN 以 ES Module 方式加载。建议在生产环境固定版本号(`@latest` 仅用于快速试用)。
|
|
@@ -33,7 +35,7 @@ console.log(result)
|
|
|
33
35
|
<script type="module">
|
|
34
36
|
import { UapiClient } from 'https://cdn.jsdelivr.net/npm/uapi-browser-sdk@latest/dist/index.js';
|
|
35
37
|
|
|
36
|
-
const client = new UapiClient('https://uapis.cn
|
|
38
|
+
const client = new UapiClient('https://uapis.cn', 'YOUR_API_KEY');
|
|
37
39
|
const data = await client.social.getSocialQqUserinfo({ qq: '10001' });
|
|
38
40
|
console.log(data);
|
|
39
41
|
</script>
|
|
@@ -70,6 +72,63 @@ SDK 采用原生 `fetch`,自动补上 `Authorization` 头且不依赖任何 No
|
|
|
70
72
|
|
|
71
73
|
如果你需要查看字段细节或内部逻辑,仓库中的 `./internal` 目录同步保留了由 `openapi-generator` 生成的完整结构体,随时可供参考。
|
|
72
74
|
|
|
75
|
+
## 响应元信息
|
|
76
|
+
|
|
77
|
+
每次请求完成后,SDK 会自动把响应 Header 解析成结构化的 `ResponseMeta`,你不用自己拆原始字符串。
|
|
78
|
+
|
|
79
|
+
成功时可以通过 `client.lastResponseMeta` 读取,失败时可以通过 `err.meta` 读取,两条路径拿到的是同一套字段。
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
import { UapiClient, UapiError } from 'uapi-browser-sdk'
|
|
83
|
+
|
|
84
|
+
const client = new UapiClient('https://uapis.cn', 'YOUR_API_KEY')
|
|
85
|
+
|
|
86
|
+
// 成功路径
|
|
87
|
+
await client.social.getSocialQqUserinfo({ qq: '10001' })
|
|
88
|
+
const meta = client.lastResponseMeta
|
|
89
|
+
if (meta) {
|
|
90
|
+
console.log('这次请求原价:', meta.creditsRequested ?? 0, '积分')
|
|
91
|
+
console.log('这次实际扣费:', meta.creditsCharged ?? 0, '积分')
|
|
92
|
+
console.log('特殊计价:', meta.creditsPricing ?? '原价')
|
|
93
|
+
console.log('余额剩余:', meta.balanceRemainingCents ?? 0, '分')
|
|
94
|
+
console.log('资源包剩余:', meta.quotaRemainingCredits ?? 0, '积分')
|
|
95
|
+
console.log('当前有效额度桶:', meta.activeQuotaBuckets ?? 0)
|
|
96
|
+
console.log('额度用空即停:', meta.stopOnEmpty ?? false)
|
|
97
|
+
console.log('Key QPS:', meta.billingKeyRateRemaining ?? 0, '/', meta.billingKeyRateLimit ?? 0, meta.billingKeyRateUnit ?? 'req')
|
|
98
|
+
console.log('Request ID:', meta.requestId)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// 失败路径
|
|
102
|
+
try {
|
|
103
|
+
await client.social.getSocialQqUserinfo({ qq: '10001' })
|
|
104
|
+
} catch (err) {
|
|
105
|
+
if (err instanceof UapiError && err.meta) {
|
|
106
|
+
console.log('Retry-After 秒数:', err.meta.retryAfterSeconds ?? null)
|
|
107
|
+
console.log('Retry-After 原始值:', err.meta.retryAfterRaw ?? null)
|
|
108
|
+
console.log('访客 QPS:', err.meta.visitorRateRemaining ?? 0, '/', err.meta.visitorRateLimit ?? 0)
|
|
109
|
+
console.log('Request ID:', err.meta.requestId)
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
常用字段一览:
|
|
115
|
+
|
|
116
|
+
| 字段 | 说明 |
|
|
117
|
+
|------|------|
|
|
118
|
+
| `creditsRequested` | 这次请求原本要扣多少积分,也就是请求价 |
|
|
119
|
+
| `creditsCharged` | 这次请求实际扣了多少积分 |
|
|
120
|
+
| `creditsPricing` | 特殊计价原因,例如缓存半价 `cache-hit-half-price` |
|
|
121
|
+
| `balanceRemainingCents` | 账户余额剩余(分) |
|
|
122
|
+
| `quotaRemainingCredits` | 资源包剩余积分 |
|
|
123
|
+
| `activeQuotaBuckets` | 当前还有多少个有效额度桶参与计费 |
|
|
124
|
+
| `stopOnEmpty` | 额度耗尽后是否直接停止服务 |
|
|
125
|
+
| `retryAfterSeconds` / `retryAfterRaw` | 限流后的等待时长;当服务端返回 HTTP 时间字符串时看 `retryAfterRaw` |
|
|
126
|
+
| `requestId` | 请求唯一 ID,排障时使用 |
|
|
127
|
+
| `billingKeyRateLimit` / `billingKeyRateRemaining` | Billing Key 当前 QPS 规则的上限与剩余 |
|
|
128
|
+
| `billingIpRateLimit` / `billingIpRateRemaining` | Billing Key 单 IP 当前 QPS 规则的上限与剩余 |
|
|
129
|
+
| `visitorRateLimit` / `visitorRateRemaining` | 访客当前 QPS 规则的上限与剩余 |
|
|
130
|
+
| `rateLimitPolicies` / `rateLimits` | 完整结构化限流策略数据 |
|
|
131
|
+
|
|
73
132
|
## 错误模型概览
|
|
74
133
|
|
|
75
134
|
| HTTP 状态码 | SDK 错误类型 | 附加信息 |
|
package/dist/src/errors.d.ts
CHANGED
|
@@ -12,6 +12,7 @@ export interface RateLimitStateEntry {
|
|
|
12
12
|
}
|
|
13
13
|
export interface ResponseMeta {
|
|
14
14
|
requestId?: string;
|
|
15
|
+
retryAfterRaw?: string;
|
|
15
16
|
retryAfterSeconds?: number;
|
|
16
17
|
debitStatus?: string;
|
|
17
18
|
creditsRequested?: number;
|
|
@@ -29,6 +30,21 @@ export interface ResponseMeta {
|
|
|
29
30
|
quotaRemainingCredits?: number;
|
|
30
31
|
visitorQuotaLimitCredits?: number;
|
|
31
32
|
visitorQuotaRemainingCredits?: number;
|
|
33
|
+
billingKeyRateLimit?: number;
|
|
34
|
+
billingKeyRateRemaining?: number;
|
|
35
|
+
billingKeyRateUnit?: string;
|
|
36
|
+
billingKeyRateWindowSeconds?: number;
|
|
37
|
+
billingKeyRateResetAfterSeconds?: number;
|
|
38
|
+
billingIpRateLimit?: number;
|
|
39
|
+
billingIpRateRemaining?: number;
|
|
40
|
+
billingIpRateUnit?: string;
|
|
41
|
+
billingIpRateWindowSeconds?: number;
|
|
42
|
+
billingIpRateResetAfterSeconds?: number;
|
|
43
|
+
visitorRateLimit?: number;
|
|
44
|
+
visitorRateRemaining?: number;
|
|
45
|
+
visitorRateUnit?: string;
|
|
46
|
+
visitorRateWindowSeconds?: number;
|
|
47
|
+
visitorRateResetAfterSeconds?: number;
|
|
32
48
|
rawHeaders: Record<string, string>;
|
|
33
49
|
}
|
|
34
50
|
export declare class UapiError extends Error {
|
package/dist/src/errors.js
CHANGED
|
@@ -161,8 +161,15 @@ export function extractMetaFromHeaders(headers) {
|
|
|
161
161
|
resetAfterSeconds: parseNumber(item.params.t),
|
|
162
162
|
};
|
|
163
163
|
}
|
|
164
|
+
const billingKeyRatePolicy = rateLimitPolicies['billing-key-rate'];
|
|
165
|
+
const billingKeyRateState = rateLimits['billing-key-rate'];
|
|
166
|
+
const billingIpRatePolicy = rateLimitPolicies['billing-ip-rate'];
|
|
167
|
+
const billingIpRateState = rateLimits['billing-ip-rate'];
|
|
168
|
+
const visitorRatePolicy = rateLimitPolicies['visitor-rate'];
|
|
169
|
+
const visitorRateState = rateLimits['visitor-rate'];
|
|
164
170
|
return {
|
|
165
171
|
requestId: rawHeaders['x-request-id'],
|
|
172
|
+
retryAfterRaw: rawHeaders['retry-after'],
|
|
166
173
|
retryAfterSeconds: parseNumber(rawHeaders['retry-after']),
|
|
167
174
|
debitStatus: rawHeaders['uapi-debit-status'],
|
|
168
175
|
creditsRequested: parseNumber(rawHeaders['uapi-credits-requested']),
|
|
@@ -180,6 +187,21 @@ export function extractMetaFromHeaders(headers) {
|
|
|
180
187
|
quotaRemainingCredits: rateLimits['billing-quota']?.remaining,
|
|
181
188
|
visitorQuotaLimitCredits: rateLimitPolicies['visitor-quota']?.quota,
|
|
182
189
|
visitorQuotaRemainingCredits: rateLimits['visitor-quota']?.remaining,
|
|
190
|
+
billingKeyRateLimit: billingKeyRatePolicy?.quota,
|
|
191
|
+
billingKeyRateRemaining: billingKeyRateState?.remaining,
|
|
192
|
+
billingKeyRateUnit: billingKeyRatePolicy?.unit ?? billingKeyRateState?.unit,
|
|
193
|
+
billingKeyRateWindowSeconds: billingKeyRatePolicy?.windowSeconds,
|
|
194
|
+
billingKeyRateResetAfterSeconds: billingKeyRateState?.resetAfterSeconds,
|
|
195
|
+
billingIpRateLimit: billingIpRatePolicy?.quota,
|
|
196
|
+
billingIpRateRemaining: billingIpRateState?.remaining,
|
|
197
|
+
billingIpRateUnit: billingIpRatePolicy?.unit ?? billingIpRateState?.unit,
|
|
198
|
+
billingIpRateWindowSeconds: billingIpRatePolicy?.windowSeconds,
|
|
199
|
+
billingIpRateResetAfterSeconds: billingIpRateState?.resetAfterSeconds,
|
|
200
|
+
visitorRateLimit: visitorRatePolicy?.quota,
|
|
201
|
+
visitorRateRemaining: visitorRateState?.remaining,
|
|
202
|
+
visitorRateUnit: visitorRatePolicy?.unit ?? visitorRateState?.unit,
|
|
203
|
+
visitorRateWindowSeconds: visitorRatePolicy?.windowSeconds,
|
|
204
|
+
visitorRateResetAfterSeconds: visitorRateState?.resetAfterSeconds,
|
|
183
205
|
rawHeaders,
|
|
184
206
|
};
|
|
185
207
|
}
|
package/dist/src/index.js
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
import { extractMetaFromHeaders, mapError } from './errors.js';
|
|
2
2
|
export { UapiError, mapError, extractMetaFromHeaders } from './errors.js';
|
|
3
|
+
const API_PREFIX = '/api/v1';
|
|
4
|
+
function normalizeBaseURL(baseURL) {
|
|
5
|
+
const trimmed = baseURL.replace(/\/+$/, '');
|
|
6
|
+
return trimmed.endsWith(API_PREFIX) ? trimmed.slice(0, -API_PREFIX.length) : trimmed;
|
|
7
|
+
}
|
|
3
8
|
export class UapiClient {
|
|
4
9
|
constructor(baseURL, token) {
|
|
5
|
-
this.baseURL = baseURL;
|
|
10
|
+
this.baseURL = normalizeBaseURL(baseURL);
|
|
6
11
|
this.token = token;
|
|
7
12
|
const clipzyZaiXianJianTieBan = new ClipzyZaiXianJianTieBanApi(this);
|
|
8
13
|
this.clipzyZaiXianJianTieBan = clipzyZaiXianJianTieBan;
|
|
@@ -57,7 +62,7 @@ export class UapiClient {
|
|
|
57
62
|
return this._lastResponseMeta;
|
|
58
63
|
}
|
|
59
64
|
async _request(method, path, params, body, headers, responseKind = 'json') {
|
|
60
|
-
const url = new URL(this.baseURL
|
|
65
|
+
const url = new URL(this.baseURL + path);
|
|
61
66
|
if (params) {
|
|
62
67
|
Object.entries(params).forEach(([key, value]) => {
|
|
63
68
|
if (value !== undefined) {
|
package/package.json
CHANGED
package/src/errors.ts
CHANGED
|
@@ -14,6 +14,7 @@ export interface RateLimitStateEntry {
|
|
|
14
14
|
|
|
15
15
|
export interface ResponseMeta {
|
|
16
16
|
requestId?: string
|
|
17
|
+
retryAfterRaw?: string
|
|
17
18
|
retryAfterSeconds?: number
|
|
18
19
|
debitStatus?: string
|
|
19
20
|
creditsRequested?: number
|
|
@@ -31,6 +32,21 @@ export interface ResponseMeta {
|
|
|
31
32
|
quotaRemainingCredits?: number
|
|
32
33
|
visitorQuotaLimitCredits?: number
|
|
33
34
|
visitorQuotaRemainingCredits?: number
|
|
35
|
+
billingKeyRateLimit?: number
|
|
36
|
+
billingKeyRateRemaining?: number
|
|
37
|
+
billingKeyRateUnit?: string
|
|
38
|
+
billingKeyRateWindowSeconds?: number
|
|
39
|
+
billingKeyRateResetAfterSeconds?: number
|
|
40
|
+
billingIpRateLimit?: number
|
|
41
|
+
billingIpRateRemaining?: number
|
|
42
|
+
billingIpRateUnit?: string
|
|
43
|
+
billingIpRateWindowSeconds?: number
|
|
44
|
+
billingIpRateResetAfterSeconds?: number
|
|
45
|
+
visitorRateLimit?: number
|
|
46
|
+
visitorRateRemaining?: number
|
|
47
|
+
visitorRateUnit?: string
|
|
48
|
+
visitorRateWindowSeconds?: number
|
|
49
|
+
visitorRateResetAfterSeconds?: number
|
|
34
50
|
rawHeaders: Record<string, string>
|
|
35
51
|
}
|
|
36
52
|
|
|
@@ -192,8 +208,16 @@ export function extractMetaFromHeaders(headers: Headers | Record<string, unknown
|
|
|
192
208
|
}
|
|
193
209
|
}
|
|
194
210
|
|
|
211
|
+
const billingKeyRatePolicy = rateLimitPolicies['billing-key-rate']
|
|
212
|
+
const billingKeyRateState = rateLimits['billing-key-rate']
|
|
213
|
+
const billingIpRatePolicy = rateLimitPolicies['billing-ip-rate']
|
|
214
|
+
const billingIpRateState = rateLimits['billing-ip-rate']
|
|
215
|
+
const visitorRatePolicy = rateLimitPolicies['visitor-rate']
|
|
216
|
+
const visitorRateState = rateLimits['visitor-rate']
|
|
217
|
+
|
|
195
218
|
return {
|
|
196
219
|
requestId: rawHeaders['x-request-id'],
|
|
220
|
+
retryAfterRaw: rawHeaders['retry-after'],
|
|
197
221
|
retryAfterSeconds: parseNumber(rawHeaders['retry-after']),
|
|
198
222
|
debitStatus: rawHeaders['uapi-debit-status'],
|
|
199
223
|
creditsRequested: parseNumber(rawHeaders['uapi-credits-requested']),
|
|
@@ -211,6 +235,21 @@ export function extractMetaFromHeaders(headers: Headers | Record<string, unknown
|
|
|
211
235
|
quotaRemainingCredits: rateLimits['billing-quota']?.remaining,
|
|
212
236
|
visitorQuotaLimitCredits: rateLimitPolicies['visitor-quota']?.quota,
|
|
213
237
|
visitorQuotaRemainingCredits: rateLimits['visitor-quota']?.remaining,
|
|
238
|
+
billingKeyRateLimit: billingKeyRatePolicy?.quota,
|
|
239
|
+
billingKeyRateRemaining: billingKeyRateState?.remaining,
|
|
240
|
+
billingKeyRateUnit: billingKeyRatePolicy?.unit ?? billingKeyRateState?.unit,
|
|
241
|
+
billingKeyRateWindowSeconds: billingKeyRatePolicy?.windowSeconds,
|
|
242
|
+
billingKeyRateResetAfterSeconds: billingKeyRateState?.resetAfterSeconds,
|
|
243
|
+
billingIpRateLimit: billingIpRatePolicy?.quota,
|
|
244
|
+
billingIpRateRemaining: billingIpRateState?.remaining,
|
|
245
|
+
billingIpRateUnit: billingIpRatePolicy?.unit ?? billingIpRateState?.unit,
|
|
246
|
+
billingIpRateWindowSeconds: billingIpRatePolicy?.windowSeconds,
|
|
247
|
+
billingIpRateResetAfterSeconds: billingIpRateState?.resetAfterSeconds,
|
|
248
|
+
visitorRateLimit: visitorRatePolicy?.quota,
|
|
249
|
+
visitorRateRemaining: visitorRateState?.remaining,
|
|
250
|
+
visitorRateUnit: visitorRatePolicy?.unit ?? visitorRateState?.unit,
|
|
251
|
+
visitorRateWindowSeconds: visitorRatePolicy?.windowSeconds,
|
|
252
|
+
visitorRateResetAfterSeconds: visitorRateState?.resetAfterSeconds,
|
|
214
253
|
rawHeaders,
|
|
215
254
|
}
|
|
216
255
|
}
|
package/src/index.ts
CHANGED
|
@@ -2,6 +2,14 @@ import type * as Internal from '../internal/src/index.js'
|
|
|
2
2
|
import { UapiError, type ResponseMeta, extractMetaFromHeaders, mapError } from './errors.js'
|
|
3
3
|
export { UapiError, mapError, extractMetaFromHeaders } from './errors.js'
|
|
4
4
|
export type { RateLimitPolicyEntry, RateLimitStateEntry, ResponseMeta } from './errors.js'
|
|
5
|
+
|
|
6
|
+
const API_PREFIX = '/api/v1'
|
|
7
|
+
|
|
8
|
+
function normalizeBaseURL(baseURL: string): string {
|
|
9
|
+
const trimmed = baseURL.replace(/\/+$/, '')
|
|
10
|
+
return trimmed.endsWith(API_PREFIX) ? trimmed.slice(0, -API_PREFIX.length) : trimmed
|
|
11
|
+
}
|
|
12
|
+
|
|
5
13
|
export type GetClipzyGetResponse =
|
|
6
14
|
Internal.GetClipzyGet200Response
|
|
7
15
|
export interface GetClipzyGetArgs {
|
|
@@ -830,7 +838,7 @@ export class UapiClient {
|
|
|
830
838
|
readonly "智能搜索": ZhiNengSouSuoApi
|
|
831
839
|
|
|
832
840
|
constructor(baseURL: string, token?: string) {
|
|
833
|
-
this.baseURL = baseURL
|
|
841
|
+
this.baseURL = normalizeBaseURL(baseURL)
|
|
834
842
|
this.token = token
|
|
835
843
|
const clipzyZaiXianJianTieBan = new ClipzyZaiXianJianTieBanApi(this)
|
|
836
844
|
this.clipzyZaiXianJianTieBan = clipzyZaiXianJianTieBan
|
|
@@ -894,7 +902,7 @@ export class UapiClient {
|
|
|
894
902
|
headers?: Record<string, string>,
|
|
895
903
|
responseKind: 'json' | 'text' | 'arrayBuffer' = 'json',
|
|
896
904
|
) {
|
|
897
|
-
const url = new URL(this.baseURL
|
|
905
|
+
const url = new URL(this.baseURL + path)
|
|
898
906
|
if (params) {
|
|
899
907
|
Object.entries(params).forEach(([key, value]) => {
|
|
900
908
|
if (value !== undefined) {
|