@fudanda/client 0.0.0-stage → 1.20261015.1-beta.1
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/LICENSE +21 -0
- package/README.md +70 -2
- package/dist/client.d.ts +63 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +205 -0
- package/dist/client.js.map +1 -0
- package/dist/contracts.d.ts +28 -0
- package/dist/contracts.d.ts.map +1 -0
- package/dist/contracts.js +75 -0
- package/dist/contracts.js.map +1 -0
- package/dist/errors.d.ts +11 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +21 -0
- package/dist/errors.js.map +1 -0
- package/dist/event-stream.d.ts +17 -0
- package/dist/event-stream.d.ts.map +1 -0
- package/dist/event-stream.js +102 -0
- package/dist/event-stream.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/subscriptions.d.ts +22 -0
- package/dist/subscriptions.d.ts.map +1 -0
- package/dist/subscriptions.js +93 -0
- package/dist/subscriptions.js.map +1 -0
- package/package.json +51 -4
- package/src/client.ts +203 -0
- package/src/contracts.ts +90 -0
- package/src/errors.ts +18 -0
- package/src/event-stream.ts +74 -0
- package/src/index.ts +9 -0
- package/src/subscriptions.ts +74 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 iwangbowen
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,71 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @fudanda/client
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
单组织 Go 基础版的独立 TypeScript API 客户端。运行时只依赖 `@fudanda/shared`,不依赖 React、Semi UI、TanStack Query、Web 源码或浏览器存储。支持 workspace 开发和根目录 `pack:packages` 的编译后 tarball 交付,可在仓库外安装,并由普通 Node 直接导入 ESM。
|
|
4
|
+
|
|
5
|
+
```ts
|
|
6
|
+
import { Client, call, operationURL } from '@fudanda/client';
|
|
7
|
+
import { goAuthContract, positionContract } from '@fudanda/shared/identity';
|
|
8
|
+
|
|
9
|
+
const client = new Client({
|
|
10
|
+
onUnauthorized: () => { /* 由宿主更新登录状态 */ },
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
// 浏览器发送 HttpOnly Cookie;刷新页面后重新获取 CSRF。
|
|
14
|
+
const session = await call(client, goAuthContract.me);
|
|
15
|
+
client.setCsrfToken(session.csrfToken);
|
|
16
|
+
|
|
17
|
+
const page = await call(client, positionContract.list, {
|
|
18
|
+
query: { page: 1, pageSize: 10, keyword: '工程' },
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
const csv = await client.readBlob(operationURL(positionContract.exportCsv, { query: {} }));
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## 职责
|
|
25
|
+
|
|
26
|
+
- `Client`:JSON、原始响应、multipart 上传、XHR 上传进度、取消和二进制下载;统一 Cookie/CSRF、请求头、链路 ID 和错误处理。
|
|
27
|
+
- `call` / `callRaw`:从 shared 契约派生 `/api/v1` 路径、参数和响应;执行响应校验及单组织设置投影,保留更新时省略的字段。`call` 解包数据并抛业务错误,`callRaw` 返回 `{ code, message, data }`。
|
|
28
|
+
- `operationURL`:为上传、预览和下载生成契约路径。未开放操作会在发送请求前被拒绝。
|
|
29
|
+
- `ClientError`:无会话、网络故障、响应格式或下载错误,含 `status`、`reason` 和可用的 `requestId`;取消保留 `AbortError`。`ApiError` 表示非零业务响应码。
|
|
30
|
+
|
|
31
|
+
登录可通过 `call(client, goAuthContract.login, { body: ... })` 发起;登录成功后由宿主用响应中的 `csrfToken` 更新客户端。退出、改密后由宿主清除 CSRF 和用户状态。认证请求返回 401 时客户端清除 CSRF 并调用 `onUnauthorized`,503 不清除会话。登录失败不触发宿主强退事件。
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
import { fileContract } from '@fudanda/shared/platform';
|
|
35
|
+
|
|
36
|
+
const form = new FormData();
|
|
37
|
+
form.append('file', file);
|
|
38
|
+
await client.postForm(operationURL(fileContract.uploadOne), form, {
|
|
39
|
+
signal: controller.signal,
|
|
40
|
+
onProgress: percent => { /* 由宿主显示进度 */ },
|
|
41
|
+
});
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
实际应用应通过 `operationURL(fileContract.uploadOne)` 获取上传路径,复用 shared 契约。文件下载返回 Blob;保存文件、Toast、重定向、用户信息和查询缓存由宿主负责。普通上传与进度上传使用相同的最新 CSRF,浏览器自行生成 multipart boundary。
|
|
45
|
+
|
|
46
|
+
默认同源,`baseURL` 可显式设置为 HTTP origin;客户端不会接受请求路径中的外部 URL。`transport` 可注入 fetch,`xhrFactory` 可注入上传实现。Node 调用需要注入维护 Cookie 的 transport;Node 的原生 fetch 不会自动保存服务端会话 Cookie。
|
|
47
|
+
|
|
48
|
+
宿主业务使用 `new Client({ operations: [hostContract.list] })` 明确注册 `/api/v1/extensions/` 契约,随后通过 `call` 调用;注册彼此隔离,不影响其他 Client 的首版限制。详见 [宿主接入说明](../../docs/guide/go-host-integration.md)。
|
|
49
|
+
|
|
50
|
+
## 验证
|
|
51
|
+
|
|
52
|
+
```sh
|
|
53
|
+
npm run check:types
|
|
54
|
+
npm run check:boundaries
|
|
55
|
+
npm run test:client
|
|
56
|
+
npm run lint -w @fudanda/client
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Web 的 `go-transport`、`go-api-client` 和文件请求仅保留宿主适配;React Query hooks、会话 Provider、下载保存和提示仍位于 `packages/web`,原页面沿用原来的调用入口。
|
|
60
|
+
|
|
61
|
+
## 脚本密钥与订阅
|
|
62
|
+
|
|
63
|
+
`new Client({ baseURL, apiKey })` 使用受限 Bearer API Key,默认 `credentials: 'omit'`,不要求 CSRF;客户端不保存密钥到浏览器存储。Cookie 模式行为保持不变。服务端同时检查密钥范围、用户当前权限及数据范围;密钥不能管理密钥、授权或安全设置。
|
|
64
|
+
|
|
65
|
+
`subscribe(client, { onChange, onUnauthorized, onError })` 提供 fetch SSE,兼容 Cookie 与 Key。变更只提示重新查询,支持内存游标、断线重连及认证失效终止。返回 `{ close, done }`;宿主卸载时调用 `close()` 并等待 `done`,查询缓存由宿主管理。
|
|
66
|
+
|
|
67
|
+
完整示例、接口、S3 和只读 MCP 说明见 [集成扩展](../../docs/guide/go-integrations.md)。
|
|
68
|
+
|
|
69
|
+
`subscribeOperation(client, operation, {params, parse, onEvent, onConnection, onError, onUnauthorized})` 订阅该 Client 已注册的 GET SSE 契约。提供 `{close, done}`、UTF-8 分片解析和重连;业务缓存与 token 合并由宿主负责。自动化订阅收到 `ready` / `done` 后重新查询持久结果,不将临时预览作为最终输出,见[流式运行](../../docs/guide/workflow-stream-performance.md)。
|
|
70
|
+
|
|
71
|
+
Casdoor 登录和目录管理通过 `goAuthContract` / `identityDirectoryContract` 调用,完整流程见 [身份接入指南](../../docs/guide/go-identity.md)。只有本地登录、外部登录开始/完成及会话冲突确认允许匿名 POST,目录写入仍要求 Cookie/CSRF。
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { ApiResponse } from '@fudanda/shared/core';
|
|
2
|
+
import type { AnyOperation } from '@fudanda/shared/core';
|
|
3
|
+
export type Method = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
|
|
4
|
+
export type ApiEnvelope<T> = ApiResponse<T> & {
|
|
5
|
+
error?: string;
|
|
6
|
+
requestId?: string;
|
|
7
|
+
retryAfterSeconds?: number;
|
|
8
|
+
};
|
|
9
|
+
export interface RequestOptions {
|
|
10
|
+
signal?: AbortSignal | null;
|
|
11
|
+
headers?: HeadersInit;
|
|
12
|
+
anonymousWrite?: boolean;
|
|
13
|
+
}
|
|
14
|
+
export interface ClientOptions {
|
|
15
|
+
baseURL?: string;
|
|
16
|
+
credentials?: RequestCredentials;
|
|
17
|
+
transport?: typeof fetch;
|
|
18
|
+
xhrFactory?: () => XMLHttpRequest;
|
|
19
|
+
onUnauthorized?: () => void;
|
|
20
|
+
/** For scripts/server integrations. Never persist or expose keys in browser storage. */
|
|
21
|
+
apiKey?: string;
|
|
22
|
+
/** Explicit host contracts; built-in operations remain restricted to the release catalog. */
|
|
23
|
+
operations?: readonly AnyOperation[];
|
|
24
|
+
}
|
|
25
|
+
export declare function validateGoPath(path: string): void;
|
|
26
|
+
/** Cookie/CSRF transport. UI, user state and query caches belong to the host. */
|
|
27
|
+
export declare class Client {
|
|
28
|
+
private readonly options;
|
|
29
|
+
private csrfToken;
|
|
30
|
+
private readonly unauthorizedListeners;
|
|
31
|
+
private readonly baseURL;
|
|
32
|
+
private readonly send;
|
|
33
|
+
private readonly credentials;
|
|
34
|
+
private readonly operations;
|
|
35
|
+
constructor(options?: ClientOptions);
|
|
36
|
+
/** Registration is instance-local and requires the exact declared operation. */
|
|
37
|
+
resolveOperationPath(op: AnyOperation): string | undefined;
|
|
38
|
+
setCsrfToken(value: string | null): void;
|
|
39
|
+
clearSession(): void;
|
|
40
|
+
/** Long-lived authenticated transports use the same expiry notifications as HTTP 401. */
|
|
41
|
+
expireSession(): void;
|
|
42
|
+
sessionHeaders(): Record<string, string>;
|
|
43
|
+
/** Hosts can observe expiry without replacing the caller's callback. */
|
|
44
|
+
subscribeUnauthorized(listener: () => void): () => void;
|
|
45
|
+
private headers;
|
|
46
|
+
private received;
|
|
47
|
+
fetchRaw(path: string, options?: RequestInit & RequestOptions): Promise<Response>;
|
|
48
|
+
private envelope;
|
|
49
|
+
request<T>(method: Method, path: string, options?: RequestOptions & {
|
|
50
|
+
body?: unknown;
|
|
51
|
+
}): Promise<ApiEnvelope<T>>;
|
|
52
|
+
requestRaw<T>(path: string, options?: RequestInit & RequestOptions): Promise<ApiEnvelope<T>>;
|
|
53
|
+
get<T>(path: string, options?: RequestOptions): Promise<ApiEnvelope<T>>;
|
|
54
|
+
post<T>(path: string, body?: unknown, options?: RequestOptions): Promise<ApiEnvelope<T>>;
|
|
55
|
+
put<T>(path: string, body?: unknown, options?: RequestOptions): Promise<ApiEnvelope<T>>;
|
|
56
|
+
patch<T>(path: string, body?: unknown, options?: RequestOptions): Promise<ApiEnvelope<T>>;
|
|
57
|
+
delete<T>(path: string, body?: unknown, options?: RequestOptions): Promise<ApiEnvelope<T>>;
|
|
58
|
+
readBlob(path: string, options?: RequestInit & RequestOptions): Promise<Blob>;
|
|
59
|
+
postForm<T>(path: string, body: FormData, options?: RequestOptions & {
|
|
60
|
+
onProgress?: (percent: number) => void;
|
|
61
|
+
}): Promise<ApiEnvelope<T>>;
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import { ClientError } from './errors.js';
|
|
2
|
+
const loginPaths = ['/api/v1/auth/login', '/api/v1/auth/session-conflict/resolve', '/api/v1/auth/external/start', '/api/v1/auth/external/complete'];
|
|
3
|
+
export function validateGoPath(path) {
|
|
4
|
+
if (!/^\/api\/v1\/[a-zA-Z0-9][a-zA-Z0-9_%./-]*(?:\?[a-zA-Z0-9%=&_.+-]*)?$/.test(path)
|
|
5
|
+
|| path.includes('//')
|
|
6
|
+
|| decodeURIComponent(path.split('?')[0]).includes('//')
|
|
7
|
+
|| /(?:^|\/)\.{1,2}(?:\/|$)/.test(decodeURIComponent(path.split('?')[0]))
|
|
8
|
+
|| !new URL(path, 'http://localhost').pathname.startsWith('/api/v1/')) {
|
|
9
|
+
throw new Error('Invalid Go API path');
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
/** Cookie/CSRF transport. UI, user state and query caches belong to the host. */
|
|
13
|
+
export class Client {
|
|
14
|
+
options;
|
|
15
|
+
csrfToken = null;
|
|
16
|
+
unauthorizedListeners = new Set();
|
|
17
|
+
baseURL;
|
|
18
|
+
send;
|
|
19
|
+
credentials;
|
|
20
|
+
operations = new Map();
|
|
21
|
+
constructor(options = {}) {
|
|
22
|
+
this.options = options;
|
|
23
|
+
this.baseURL = (options.baseURL ?? '').replace(/\/$/, '');
|
|
24
|
+
if (this.baseURL && (!/^https?:\/\//.test(this.baseURL) || new URL(this.baseURL).origin !== this.baseURL))
|
|
25
|
+
throw new Error('baseURL must be an HTTP origin');
|
|
26
|
+
this.send = options.transport ?? ((...args) => globalThis.fetch(...args));
|
|
27
|
+
if (options.apiKey && !/^arc_[a-f0-9]{64}$/.test(options.apiKey))
|
|
28
|
+
throw new Error('Invalid ArcBase API Key');
|
|
29
|
+
this.credentials = options.credentials ?? (options.apiKey ? 'omit' : 'same-origin');
|
|
30
|
+
const routes = new Set();
|
|
31
|
+
for (const op of options.operations ?? []) {
|
|
32
|
+
validateGoPath(op.fullPath.replaceAll(/\{[a-zA-Z][a-zA-Z0-9_]*\}/g, '1'));
|
|
33
|
+
if (!op.fullPath.startsWith('/api/v1/extensions/'))
|
|
34
|
+
throw new Error('Host operations must use /api/v1/extensions/');
|
|
35
|
+
const key = `${op.method} ${op.fullPath}`;
|
|
36
|
+
if (routes.has(key))
|
|
37
|
+
throw new Error('Duplicate host operation');
|
|
38
|
+
routes.add(key);
|
|
39
|
+
this.operations.set(op, op.fullPath);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/** Registration is instance-local and requires the exact declared operation. */
|
|
43
|
+
resolveOperationPath(op) { return this.operations.get(op); }
|
|
44
|
+
setCsrfToken(value) { this.csrfToken = value; }
|
|
45
|
+
clearSession() { this.csrfToken = null; }
|
|
46
|
+
/** Long-lived authenticated transports use the same expiry notifications as HTTP 401. */
|
|
47
|
+
expireSession() { this.received(401); }
|
|
48
|
+
sessionHeaders() { return this.csrfToken ? { 'X-CSRF-Token': this.csrfToken } : {}; }
|
|
49
|
+
/** Hosts can observe expiry without replacing the caller's callback. */
|
|
50
|
+
subscribeUnauthorized(listener) {
|
|
51
|
+
this.unauthorizedListeners.add(listener);
|
|
52
|
+
return () => { this.unauthorizedListeners.delete(listener); };
|
|
53
|
+
}
|
|
54
|
+
headers(method, path, body, options) {
|
|
55
|
+
validateGoPath(path);
|
|
56
|
+
if (options.anonymousWrite && (method !== 'POST' || !loginPaths.includes(path)))
|
|
57
|
+
throw new Error('Only origin-checked login actions may write without CSRF');
|
|
58
|
+
const write = !['GET', 'HEAD', 'OPTIONS'].includes(method);
|
|
59
|
+
if (write && !options.anonymousWrite && !this.csrfToken && !this.options.apiKey)
|
|
60
|
+
throw new ClientError(401, 'session_missing', '登录状态尚未恢复');
|
|
61
|
+
const headers = new Headers(options.headers);
|
|
62
|
+
// Caller headers cannot supply a different identity or stale CSRF secret.
|
|
63
|
+
headers.delete('authorization');
|
|
64
|
+
headers.delete('cookie');
|
|
65
|
+
headers.delete('x-csrf-token');
|
|
66
|
+
if (this.options.apiKey)
|
|
67
|
+
headers.set('Authorization', `Bearer ${this.options.apiKey}`);
|
|
68
|
+
if (body instanceof FormData)
|
|
69
|
+
headers.delete('content-type');
|
|
70
|
+
else if (body !== undefined && body !== null && !headers.has('content-type'))
|
|
71
|
+
headers.set('Content-Type', 'application/json');
|
|
72
|
+
if (write && !options.anonymousWrite && !this.options.apiKey)
|
|
73
|
+
headers.set('X-CSRF-Token', this.csrfToken);
|
|
74
|
+
return headers;
|
|
75
|
+
}
|
|
76
|
+
received(status, anonymousWrite = false) {
|
|
77
|
+
if (status === 401) {
|
|
78
|
+
this.clearSession();
|
|
79
|
+
if (!anonymousWrite) {
|
|
80
|
+
for (const listener of this.unauthorizedListeners)
|
|
81
|
+
listener();
|
|
82
|
+
this.options.onUnauthorized?.();
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
async fetchRaw(path, options = {}) {
|
|
87
|
+
const { anonymousWrite, ...init } = options;
|
|
88
|
+
const method = (init.method ?? 'GET').toUpperCase();
|
|
89
|
+
const headers = this.headers(method, path, init.body, { ...options, anonymousWrite });
|
|
90
|
+
let response;
|
|
91
|
+
try {
|
|
92
|
+
response = await this.send(this.baseURL + path, { ...init, method, headers, credentials: this.credentials, cache: 'no-store' });
|
|
93
|
+
}
|
|
94
|
+
catch (error) {
|
|
95
|
+
if (error && typeof error === 'object' && 'name' in error && error.name === 'AbortError')
|
|
96
|
+
throw error;
|
|
97
|
+
throw new ClientError(0, 'network_error', '网络请求失败,请检查网络连接');
|
|
98
|
+
}
|
|
99
|
+
// A host transport may resolve after cancellation; ignore that stale result.
|
|
100
|
+
if (init.signal?.aborted)
|
|
101
|
+
throw new DOMException('已取消', 'AbortError');
|
|
102
|
+
this.received(response.status, anonymousWrite);
|
|
103
|
+
return response;
|
|
104
|
+
}
|
|
105
|
+
async envelope(response) {
|
|
106
|
+
const requestId = response.headers.get('X-Request-Id') ?? undefined;
|
|
107
|
+
let payload;
|
|
108
|
+
try {
|
|
109
|
+
payload = await response.json();
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
throw new ClientError(response.status, 'invalid_response', '服务端响应格式错误', requestId);
|
|
113
|
+
}
|
|
114
|
+
if (!payload || typeof payload !== 'object' || !('code' in payload) || typeof payload.code !== 'number'
|
|
115
|
+
|| !('message' in payload) || typeof payload.message !== 'string' || !('data' in payload))
|
|
116
|
+
throw new ClientError(response.status, 'invalid_response', '服务端响应格式错误', requestId);
|
|
117
|
+
const result = payload;
|
|
118
|
+
if (!response.ok && result.code === 0)
|
|
119
|
+
throw new ClientError(response.status, 'invalid_response', 'HTTP 状态与响应结果不一致', requestId);
|
|
120
|
+
const retryAfterSeconds = Number(response.headers.get('Retry-After'));
|
|
121
|
+
return { ...result, ...(requestId ? { requestId } : {}), ...(retryAfterSeconds > 0 ? { retryAfterSeconds } : {}) };
|
|
122
|
+
}
|
|
123
|
+
async request(method, path, options = {}) {
|
|
124
|
+
const { body, ...rest } = options;
|
|
125
|
+
const raw = body === undefined ? undefined : body instanceof FormData ? body : JSON.stringify(body);
|
|
126
|
+
return this.requestRaw(path, { ...rest, method, body: raw });
|
|
127
|
+
}
|
|
128
|
+
async requestRaw(path, options = {}) {
|
|
129
|
+
return this.envelope(await this.fetchRaw(path, options));
|
|
130
|
+
}
|
|
131
|
+
get(path, options) { return this.request('GET', path, options); }
|
|
132
|
+
post(path, body, options) { return this.request('POST', path, { ...options, body, anonymousWrite: loginPaths.includes(path) }); }
|
|
133
|
+
put(path, body, options) { return this.request('PUT', path, { ...options, body }); }
|
|
134
|
+
patch(path, body, options) { return this.request('PATCH', path, { ...options, body }); }
|
|
135
|
+
delete(path, body, options) { return this.request('DELETE', path, { ...options, body }); }
|
|
136
|
+
async readBlob(path, options = {}) {
|
|
137
|
+
const response = await this.fetchRaw(path, options);
|
|
138
|
+
if (!response.ok) {
|
|
139
|
+
const result = await this.envelope(response);
|
|
140
|
+
throw new ClientError(response.status, result.error ?? 'download_failed', result.message || '下载失败', result.requestId);
|
|
141
|
+
}
|
|
142
|
+
if (response.headers.get('Content-Type')?.includes('application/json') && !response.headers.has('Content-Disposition'))
|
|
143
|
+
throw new ClientError(response.status, 'invalid_response', '文件响应格式错误');
|
|
144
|
+
return response.blob();
|
|
145
|
+
}
|
|
146
|
+
async postForm(path, body, options = {}) {
|
|
147
|
+
if (!options.onProgress)
|
|
148
|
+
return this.request('POST', path, { ...options, body });
|
|
149
|
+
const headers = this.headers('POST', path, body, options);
|
|
150
|
+
if (options.signal?.aborted)
|
|
151
|
+
throw new DOMException('已取消', 'AbortError');
|
|
152
|
+
const xhr = this.options.xhrFactory?.() ?? new XMLHttpRequest();
|
|
153
|
+
return new Promise((resolve, reject) => {
|
|
154
|
+
xhr.open('POST', this.baseURL + path);
|
|
155
|
+
xhr.withCredentials = this.credentials === 'include';
|
|
156
|
+
headers.forEach((value, name) => xhr.setRequestHeader(name, value));
|
|
157
|
+
const onProgress = (event) => {
|
|
158
|
+
if (event.lengthComputable)
|
|
159
|
+
options.onProgress?.(Math.round(event.loaded / event.total * 100));
|
|
160
|
+
};
|
|
161
|
+
const onAbort = () => { xhr.abort(); reject(new DOMException('已取消', 'AbortError')); };
|
|
162
|
+
options.signal?.addEventListener('abort', onAbort, { once: true });
|
|
163
|
+
const cleanup = () => {
|
|
164
|
+
options.signal?.removeEventListener('abort', onAbort);
|
|
165
|
+
xhr.upload.removeEventListener('progress', onProgress);
|
|
166
|
+
xhr.removeEventListener('load', onLoad);
|
|
167
|
+
xhr.removeEventListener('error', onError);
|
|
168
|
+
xhr.removeEventListener('abort', onAborted);
|
|
169
|
+
xhr.removeEventListener('timeout', onTimeout);
|
|
170
|
+
};
|
|
171
|
+
const onLoad = () => {
|
|
172
|
+
cleanup();
|
|
173
|
+
this.received(xhr.status);
|
|
174
|
+
const responseHeaders = new Headers();
|
|
175
|
+
for (const name of ['Content-Type', 'X-Request-Id', 'Retry-After']) {
|
|
176
|
+
const value = xhr.getResponseHeader(name);
|
|
177
|
+
if (value)
|
|
178
|
+
responseHeaders.set(name, value);
|
|
179
|
+
}
|
|
180
|
+
try {
|
|
181
|
+
void this.envelope(new Response(xhr.responseText, { status: xhr.status, headers: responseHeaders })).then(resolve, reject);
|
|
182
|
+
}
|
|
183
|
+
catch (error) {
|
|
184
|
+
reject(error);
|
|
185
|
+
}
|
|
186
|
+
};
|
|
187
|
+
const onError = () => { cleanup(); reject(new ClientError(0, 'network_error', '网络请求失败,请检查网络连接')); };
|
|
188
|
+
const onAborted = () => { cleanup(); reject(new DOMException('已取消', 'AbortError')); };
|
|
189
|
+
const onTimeout = () => { cleanup(); reject(new ClientError(0, 'timeout', '上传超时')); };
|
|
190
|
+
xhr.upload.addEventListener('progress', onProgress);
|
|
191
|
+
xhr.addEventListener('load', onLoad);
|
|
192
|
+
xhr.addEventListener('error', onError);
|
|
193
|
+
xhr.addEventListener('abort', onAborted);
|
|
194
|
+
xhr.addEventListener('timeout', onTimeout);
|
|
195
|
+
try {
|
|
196
|
+
xhr.send(body);
|
|
197
|
+
}
|
|
198
|
+
catch {
|
|
199
|
+
cleanup();
|
|
200
|
+
reject(new ClientError(0, 'network_error', '网络请求失败,请检查网络连接'));
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAsBvC,MAAM,UAAU,GAAG,CAAC,oBAAoB,EAAE,uCAAuC,EAAE,6BAA6B,EAAE,gCAAgC,CAAC,CAAC;AAEpJ,MAAM,UAAU,cAAc,CAAC,IAAY;IACzC,IAAI,CAAC,qEAAqE,CAAC,IAAI,CAAC,IAAI,CAAC;WAChF,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC;WACnB,kBAAkB,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC;WACrD,yBAAyB,CAAC,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;WACtE,CAAC,IAAI,GAAG,CAAC,IAAI,EAAE,kBAAkB,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QACxE,MAAM,IAAI,KAAK,CAAC,qBAAqB,CAAC,CAAC;IACzC,CAAC;AACH,CAAC;AAED,iFAAiF;AACjF,MAAM,OAAO,MAAM;IAQY,OAAO;IAP5B,SAAS,GAAkB,IAAI,CAAC;IACvB,qBAAqB,GAAG,IAAI,GAAG,EAAc,CAAC;IAC9C,OAAO,CAAS;IAChB,IAAI,CAAe;IACnB,WAAW,CAAqB;IAChC,UAAU,GAAG,IAAI,GAAG,EAAwB,CAAC;IAE9D,YAA6B,OAAO,GAAkB,EAAE;uBAA3B,OAAO;QAClC,IAAI,CAAC,OAAO,GAAG,CAAC,OAAO,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QAC1D,IAAI,IAAI,CAAC,OAAO,IAAI,CAAC,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,IAAI,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,MAAM,KAAK,IAAI,CAAC,OAAO,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC;QAC7J,IAAI,CAAC,IAAI,GAAG,OAAO,CAAC,SAAS,IAAI,CAAC,CAAC,GAAG,IAAI,EAAE,EAAE,CAAC,UAAU,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC;QAC1E,IAAI,OAAO,CAAC,MAAM,IAAI,CAAC,oBAAoB,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;QAC7G,IAAI,CAAC,WAAW,GAAG,OAAO,CAAC,WAAW,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC;QACpF,MAAM,MAAM,GAAG,IAAI,GAAG,EAAU,CAAC;QACjC,KAAK,MAAM,EAAE,IAAI,OAAO,CAAC,UAAU,IAAI,EAAE,EAAE,CAAC;YAC1C,cAAc,CAAC,EAAE,CAAC,QAAQ,CAAC,UAAU,CAAC,4BAA4B,EAAE,GAAG,CAAC,CAAC,CAAC;YAC1E,IAAI,CAAC,EAAE,CAAC,QAAQ,CAAC,UAAU,CAAC,qBAAqB,CAAC;gBAAE,MAAM,IAAI,KAAK,CAAC,8CAA8C,CAAC,CAAC;YACpH,MAAM,GAAG,GAAG,GAAG,EAAE,CAAC,MAAM,IAAI,EAAE,CAAC,QAAQ,EAAE,CAAC;YAC1C,IAAI,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC;gBAAE,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAC;YACjE,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,EAAE,EAAE,CAAC,QAAQ,CAAC,CAAC;QACxD,CAAC;IACH,CAAC;IAED,gFAAgF;IAChF,oBAAoB,CAAC,EAAgB,IAAwB,OAAO,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IAE9F,YAAY,CAAC,KAAoB,IAAU,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC,CAAC,CAAC;IACpE,YAAY,KAAW,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,CAAC,CAAC;IAC/C,yFAAyF;IACzF,aAAa,KAAW,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IAC7C,cAAc,KAA6B,OAAO,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAE7G,wEAAwE;IACxE,qBAAqB,CAAC,QAAoB;QACxC,IAAI,CAAC,qBAAqB,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QACzC,OAAO,GAAG,EAAE,GAAG,IAAI,CAAC,qBAAqB,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;IAChE,CAAC;IAEO,OAAO,CAAC,MAAc,EAAE,IAAY,EAAE,IAAiC,EAAE,OAAuB;QACtG,cAAc,CAAC,IAAI,CAAC,CAAC;QACrB,IAAI,OAAO,CAAC,cAAc,IAAI,CAAC,MAAM,KAAK,MAAM,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,0DAA0D,CAAC,CAAC;QAC7J,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QAC3D,IAAI,KAAK,IAAI,CAAC,OAAO,CAAC,cAAc,IAAI,CAAC,IAAI,CAAC,SAAS,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM;YAAE,MAAM,IAAI,WAAW,CAAC,GAAG,EAAE,iBAAiB,EAAE,UAAU,CAAC,CAAC;QAC3I,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAC7C,0EAA0E;QAC1E,OAAO,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC;QAChC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QACzB,OAAO,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC;QAC/B,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM;YAAE,OAAO,CAAC,GAAG,CAAC,eAAe,EAAE,UAAU,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;QACvF,IAAI,IAAI,YAAY,QAAQ;YAAE,OAAO,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC;aACxD,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC;YAAE,OAAO,CAAC,GAAG,CAAC,cAAc,EAAE,kBAAkB,CAAC,CAAC;QAC9H,IAAI,KAAK,IAAI,CAAC,OAAO,CAAC,cAAc,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM;YAAE,OAAO,CAAC,GAAG,CAAC,cAAc,EAAE,IAAI,CAAC,SAAU,CAAC,CAAC;QAC3G,OAAO,OAAO,CAAC;IACjB,CAAC;IAEO,QAAQ,CAAC,MAAc,EAAE,cAAc,GAAG,KAAK;QACrD,IAAI,MAAM,KAAK,GAAG,EAAE,CAAC;YACnB,IAAI,CAAC,YAAY,EAAE,CAAC;YACpB,IAAI,CAAC,cAAc,EAAE,CAAC;gBACpB,KAAK,MAAM,QAAQ,IAAI,IAAI,CAAC,qBAAqB;oBAAE,QAAQ,EAAE,CAAC;gBAC9D,IAAI,CAAC,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC;YAClC,CAAC;QACH,CAAC;IACH,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,IAAY,EAAE,OAAO,GAAiC,EAAE;QACrE,MAAM,EAAE,cAAc,EAAE,GAAG,IAAI,EAAE,GAAG,OAAO,CAAC;QAC5C,MAAM,MAAM,GAAG,CAAC,IAAI,CAAC,MAAM,IAAI,KAAK,CAAC,CAAC,WAAW,EAAE,CAAC;QACpD,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,EAAE,GAAG,OAAO,EAAE,cAAc,EAAE,CAAC,CAAC;QACtF,IAAI,QAAkB,CAAC;QACvB,IAAI,CAAC;YAAC,QAAQ,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,GAAG,IAAI,EAAE,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,IAAI,CAAC,WAAW,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,CAAC;QAAC,CAAC;QACxI,OAAO,KAAK,EAAE,CAAC;YACb,IAAI,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,MAAM,IAAI,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,YAAY;gBAAE,MAAM,KAAK,CAAC;YACtG,MAAM,IAAI,WAAW,CAAC,CAAC,EAAE,eAAe,EAAE,gBAAgB,CAAC,CAAC;QAC9D,CAAC;QACD,6EAA6E;QAC7E,IAAI,IAAI,CAAC,MAAM,EAAE,OAAO;YAAE,MAAM,IAAI,YAAY,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC;QACtE,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;QAC/C,OAAO,QAAQ,CAAC;IAClB,CAAC;IAEO,KAAK,CAAC,QAAQ,CAAI,QAAkB;QAC1C,MAAM,SAAS,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,SAAS,CAAC;QACpE,IAAI,OAAgB,CAAC;QACrB,IAAI,CAAC;YAAC,OAAO,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;QAAC,CAAC;QACxC,MAAM,CAAC;YAAC,MAAM,IAAI,WAAW,CAAC,QAAQ,CAAC,MAAM,EAAE,kBAAkB,EAAE,WAAW,EAAE,SAAS,CAAC,CAAC;QAAC,CAAC;QAC7F,IAAI,CAAC,OAAO,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,CAAC,CAAC,MAAM,IAAI,OAAO,CAAC,IAAI,OAAO,OAAO,CAAC,IAAI,KAAK,QAAQ;eAClG,CAAC,CAAC,SAAS,IAAI,OAAO,CAAC,IAAI,OAAO,OAAO,CAAC,OAAO,KAAK,QAAQ,IAAI,CAAC,CAAC,MAAM,IAAI,OAAO,CAAC;YAAE,MAAM,IAAI,WAAW,CAAC,QAAQ,CAAC,MAAM,EAAE,kBAAkB,EAAE,WAAW,EAAE,SAAS,CAAC,CAAC;QAChL,MAAM,MAAM,GAAG,OAAyB,CAAC;QACzC,IAAI,CAAC,QAAQ,CAAC,EAAE,IAAI,MAAM,CAAC,IAAI,KAAK,CAAC;YAAE,MAAM,IAAI,WAAW,CAAC,QAAQ,CAAC,MAAM,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,SAAS,CAAC,CAAC;QAChI,MAAM,iBAAiB,GAAG,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC;QACtE,OAAO,EAAE,GAAG,MAAM,EAAE,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,CAAC,iBAAiB,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,iBAAiB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;IACrH,CAAC;IAED,KAAK,CAAC,OAAO,CAAI,MAAc,EAAE,IAAY,EAAE,OAAO,GAAwC,EAAE;QAC9F,MAAM,EAAE,IAAI,EAAE,GAAG,IAAI,EAAE,GAAG,OAAO,CAAC;QAClC,MAAM,GAAG,GAAG,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,YAAY,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;QACpG,OAAO,IAAI,CAAC,UAAU,CAAI,IAAI,EAAE,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC;IAClE,CAAC;IAED,KAAK,CAAC,UAAU,CAAI,IAAY,EAAE,OAAO,GAAiC,EAAE;QAC1E,OAAO,IAAI,CAAC,QAAQ,CAAI,MAAM,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC;IAC9D,CAAC;IAED,GAAG,CAAI,IAAY,EAAE,OAAwB,IAAI,OAAO,IAAI,CAAC,OAAO,CAAI,KAAK,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;IAChG,IAAI,CAAI,IAAY,EAAE,IAAc,EAAE,OAAwB,IAAI,OAAO,IAAI,CAAC,OAAO,CAAI,MAAM,EAAE,IAAI,EAAE,EAAE,GAAG,OAAO,EAAE,IAAI,EAAE,cAAc,EAAE,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IAC1K,GAAG,CAAI,IAAY,EAAE,IAAc,EAAE,OAAwB,IAAI,OAAO,IAAI,CAAC,OAAO,CAAI,KAAK,EAAE,IAAI,EAAE,EAAE,GAAG,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IAC7H,KAAK,CAAI,IAAY,EAAE,IAAc,EAAE,OAAwB,IAAI,OAAO,IAAI,CAAC,OAAO,CAAI,OAAO,EAAE,IAAI,EAAE,EAAE,GAAG,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IACjI,MAAM,CAAI,IAAY,EAAE,IAAc,EAAE,OAAwB,IAAI,OAAO,IAAI,CAAC,OAAO,CAAI,QAAQ,EAAE,IAAI,EAAE,EAAE,GAAG,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IAEnI,KAAK,CAAC,QAAQ,CAAC,IAAY,EAAE,OAAO,GAAiC,EAAE;QACrE,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QACpD,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAU,QAAQ,CAAC,CAAC;YACtD,MAAM,IAAI,WAAW,CAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,IAAI,iBAAiB,EAAE,MAAM,CAAC,OAAO,IAAI,MAAM,EAAE,MAAM,CAAC,SAAS,CAAC,CAAC;QACxH,CAAC;QACD,IAAI,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,EAAE,QAAQ,CAAC,kBAAkB,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC;YAAE,MAAM,IAAI,WAAW,CAAC,QAAQ,CAAC,MAAM,EAAE,kBAAkB,EAAE,UAAU,CAAC,CAAC;QAC/L,OAAO,QAAQ,CAAC,IAAI,EAAE,CAAC;IACzB,CAAC;IAED,KAAK,CAAC,QAAQ,CAAI,IAAY,EAAE,IAAc,EAAE,OAAO,GAAgE,EAAE;QACvH,IAAI,CAAC,OAAO,CAAC,UAAU;YAAE,OAAO,IAAI,CAAC,OAAO,CAAI,MAAM,EAAE,IAAI,EAAE,EAAE,GAAG,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;QACpF,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;QAC1D,IAAI,OAAO,CAAC,MAAM,EAAE,OAAO;YAAE,MAAM,IAAI,YAAY,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC;QACzE,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,IAAI,IAAI,cAAc,EAAE,CAAC;QAChE,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrC,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC;YACtC,GAAG,CAAC,eAAe,GAAG,IAAI,CAAC,WAAW,KAAK,SAAS,CAAC;YACrD,OAAO,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC,GAAG,CAAC,gBAAgB,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC;YACpE,MAAM,UAAU,GAAG,CAAC,KAAoB,EAAE,EAAE;gBAC1C,IAAI,KAAK,CAAC,gBAAgB;oBAAE,OAAO,CAAC,UAAU,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,KAAK,CAAC,KAAK,GAAG,GAAG,CAAC,CAAC,CAAC;YACjG,CAAC,CAAC;YACF,MAAM,OAAO,GAAG,GAAG,EAAE,GAAG,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC,MAAM,CAAC,IAAI,YAAY,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YACtF,OAAO,CAAC,MAAM,EAAE,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;YACnE,MAAM,OAAO,GAAG,GAAG,EAAE;gBACnB,OAAO,CAAC,MAAM,EAAE,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;gBACtD,GAAG,CAAC,MAAM,CAAC,mBAAmB,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;gBACvD,GAAG,CAAC,mBAAmB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;gBACxC,GAAG,CAAC,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;gBAC1C,GAAG,CAAC,mBAAmB,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;gBAC5C,GAAG,CAAC,mBAAmB,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;YAChD,CAAC,CAAC;YACF,MAAM,MAAM,GAAG,GAAG,EAAE;gBAClB,OAAO,EAAE,CAAC;gBACV,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;gBAC1B,MAAM,eAAe,GAAG,IAAI,OAAO,EAAE,CAAC;gBACtC,KAAK,MAAM,IAAI,IAAI,CAAC,cAAc,EAAE,cAAc,EAAE,aAAa,CAAC,EAAE,CAAC;oBACnE,MAAM,KAAK,GAAG,GAAG,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC;oBAC1C,IAAI,KAAK;wBAAE,eAAe,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;gBAC9C,CAAC;gBACD,IAAI,CAAC;oBAAC,KAAK,IAAI,CAAC,QAAQ,CAAI,IAAI,QAAQ,CAAC,GAAG,CAAC,YAAY,EAAE,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,eAAe,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;gBAAC,CAAC;gBACtI,OAAO,KAAK,EAAE,CAAC;oBAAC,MAAM,CAAC,KAAK,CAAC,CAAC;gBAAC,CAAC;YAClC,CAAC,CAAC;YACF,MAAM,OAAO,GAAG,GAAG,EAAE,GAAG,OAAO,EAAE,CAAC,CAAC,MAAM,CAAC,IAAI,WAAW,CAAC,CAAC,EAAE,eAAe,EAAE,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YACpG,MAAM,SAAS,GAAG,GAAG,EAAE,GAAG,OAAO,EAAE,CAAC,CAAC,MAAM,CAAC,IAAI,YAAY,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YACtF,MAAM,SAAS,GAAG,GAAG,EAAE,GAAG,OAAO,EAAE,CAAC,CAAC,MAAM,CAAC,IAAI,WAAW,CAAC,CAAC,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YACtF,GAAG,CAAC,MAAM,CAAC,gBAAgB,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;YACpD,GAAG,CAAC,gBAAgB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;YACrC,GAAG,CAAC,gBAAgB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YACvC,GAAG,CAAC,gBAAgB,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;YACzC,GAAG,CAAC,gBAAgB,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;YAC3C,IAAI,CAAC;gBAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAAC,CAAC;YACvB,MAAM,CAAC;gBAAC,OAAO,EAAE,CAAC;gBAAC,MAAM,CAAC,IAAI,WAAW,CAAC,CAAC,EAAE,eAAe,EAAE,gBAAgB,CAAC,CAAC,CAAC;YAAC,CAAC;QACrF,CAAC,CAAC,CAAC;IACL,CAAC;CACF"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { type AnyOperation, type ApiResponse, type EmptyInput, type InputOf, type OutputOf, type ParamsSchema, type ShapeInput } from '@fudanda/shared/core';
|
|
2
|
+
import type { RequestOptions } from './client.js';
|
|
3
|
+
export interface JsonClient {
|
|
4
|
+
resolveOperationPath?(op: AnyOperation): string | undefined;
|
|
5
|
+
get<T>(url: string, options?: RequestOptions): Promise<ApiResponse<T>>;
|
|
6
|
+
post<T>(url: string, body?: unknown, options?: RequestOptions): Promise<ApiResponse<T>>;
|
|
7
|
+
put<T>(url: string, body?: unknown, options?: RequestOptions): Promise<ApiResponse<T>>;
|
|
8
|
+
patch<T>(url: string, body?: unknown, options?: RequestOptions): Promise<ApiResponse<T>>;
|
|
9
|
+
delete<T>(url: string, body?: unknown, options?: RequestOptions): Promise<ApiResponse<T>>;
|
|
10
|
+
}
|
|
11
|
+
export declare function isFoundationOperation(op: AnyOperation): boolean;
|
|
12
|
+
export declare function foundationPath(op: AnyOperation): string;
|
|
13
|
+
export declare function foundationRequestBody(op: AnyOperation, body: unknown): unknown;
|
|
14
|
+
export declare function toQueryString(params: object): string;
|
|
15
|
+
type InputArgs<Op extends AnyOperation> = Record<never, never> extends InputOf<Op> ? [input?: InputOf<Op>] : [input: InputOf<Op>];
|
|
16
|
+
export type UrlInputOf<Op extends AnyOperation> = (Op['params'] extends ParamsSchema ? {
|
|
17
|
+
params: ShapeInput<Op['params']>;
|
|
18
|
+
} : EmptyInput) & (Op['query'] extends ParamsSchema ? {
|
|
19
|
+
query: ShapeInput<Op['query']>;
|
|
20
|
+
} : EmptyInput);
|
|
21
|
+
type UrlArgs<Op extends AnyOperation> = Record<never, never> extends UrlInputOf<Op> ? [input?: UrlInputOf<Op>] : [input: UrlInputOf<Op>];
|
|
22
|
+
export declare function operationURL<Op extends AnyOperation>(op: Op, ...args: UrlArgs<Op>): string;
|
|
23
|
+
/** Typed calls reuse shared schemas, including single-organization projections. */
|
|
24
|
+
export declare function callRaw<Op extends AnyOperation>(client: JsonClient, op: Op, ...args: [...InputArgs<Op>, options?: RequestOptions]): Promise<ApiResponse<OutputOf<Op>>>;
|
|
25
|
+
export declare function call<Op extends AnyOperation>(client: JsonClient, op: Op, ...args: [...InputArgs<Op>, options?: RequestOptions]): Promise<OutputOf<Op>>;
|
|
26
|
+
export declare function unwrap<T>(response: ApiResponse<T>): T;
|
|
27
|
+
export {};
|
|
28
|
+
//# sourceMappingURL=contracts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contracts.d.ts","sourceRoot":"","sources":["../src/contracts.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { fillPath } from '@fudanda/shared/core';
|
|
2
|
+
import { foundationOperations } from '@fudanda/shared/foundation-operations';
|
|
3
|
+
import { foundationSettingsBody, foundationSettingsOperation } from '@fudanda/shared/settings/foundation';
|
|
4
|
+
import { ApiError } from './errors.js';
|
|
5
|
+
const paths = new Map(foundationOperations.map(([, op]) => [op.fullPath, op.fullPath.replace(/^\/api\/(?!v1\/)/, '/api/v1/')]));
|
|
6
|
+
export function isFoundationOperation(op) { return paths.has(op.fullPath); }
|
|
7
|
+
export function foundationPath(op) {
|
|
8
|
+
const path = paths.get(op.fullPath);
|
|
9
|
+
if (!path)
|
|
10
|
+
throw new Error(`操作 ${op.basePath}:${op.name} 尚未接入 Go`);
|
|
11
|
+
return path;
|
|
12
|
+
}
|
|
13
|
+
export function foundationRequestBody(op, body) {
|
|
14
|
+
if (!op.body || body === undefined || body instanceof FormData)
|
|
15
|
+
return body;
|
|
16
|
+
const parsed = op.body.parse(body);
|
|
17
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed))
|
|
18
|
+
return parsed;
|
|
19
|
+
const partial = op.method === 'put' || op.method === 'patch';
|
|
20
|
+
const supplied = body && typeof body === 'object' ? body : {};
|
|
21
|
+
const removed = new Set(['tenantId', 'tenantCode', 'tenantViewId', 'viewingTenantId']);
|
|
22
|
+
return Object.fromEntries(Object.entries(parsed).filter(([key]) => !removed.has(key) && (!partial || Object.hasOwn(supplied, key))));
|
|
23
|
+
}
|
|
24
|
+
export function toQueryString(params) {
|
|
25
|
+
const search = new URLSearchParams();
|
|
26
|
+
for (const [key, value] of Object.entries(params)) {
|
|
27
|
+
if (value !== undefined && value !== null && value !== '')
|
|
28
|
+
search.set(key, String(value));
|
|
29
|
+
}
|
|
30
|
+
const query = search.toString();
|
|
31
|
+
return query ? `?${query}` : '';
|
|
32
|
+
}
|
|
33
|
+
export function operationURL(op, ...args) {
|
|
34
|
+
const values = args[0];
|
|
35
|
+
return fillPath(foundationPath(op), values?.params) + (values?.query ? toQueryString(values.query) : '');
|
|
36
|
+
}
|
|
37
|
+
/** Typed calls reuse shared schemas, including single-organization projections. */
|
|
38
|
+
export async function callRaw(client, op, ...args) {
|
|
39
|
+
if (op.kind !== 'json')
|
|
40
|
+
throw new Error(`契约操作「${op.name}」为 ${op.kind} 响应,请使用文件通道`);
|
|
41
|
+
const input = args[0];
|
|
42
|
+
const options = args[1];
|
|
43
|
+
const headers = new Headers(options?.headers);
|
|
44
|
+
for (const [key, value] of Object.entries(input?.headers ?? {})) {
|
|
45
|
+
if (value !== undefined && value !== null)
|
|
46
|
+
headers.set(key, String(value));
|
|
47
|
+
}
|
|
48
|
+
const requestOptions = { ...options, headers };
|
|
49
|
+
const hostPath = client.resolveOperationPath?.(op);
|
|
50
|
+
const url = hostPath ? fillPath(hostPath, input?.params) + (input?.query ? toQueryString(input.query) : '') : operationURL(op, ...[input]);
|
|
51
|
+
const projected = hostPath ? op : foundationSettingsOperation(op);
|
|
52
|
+
const body = hostPath ? hostRequestBody(op, input?.body) : foundationRequestBody(projected, foundationSettingsBody(op, input?.body));
|
|
53
|
+
const response = op.method === 'get'
|
|
54
|
+
? await client.get(url, requestOptions)
|
|
55
|
+
: await client[op.method](url, body, requestOptions);
|
|
56
|
+
return response.code === 0 ? { ...response, data: projected.response.parse(response.data) } : response;
|
|
57
|
+
}
|
|
58
|
+
function hostRequestBody(op, body) {
|
|
59
|
+
if (!op.body || body === undefined || body instanceof FormData)
|
|
60
|
+
return body;
|
|
61
|
+
const parsed = op.body.parse(body);
|
|
62
|
+
if ((op.method === 'put' || op.method === 'patch') && parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
|
|
63
|
+
return Object.fromEntries(Object.entries(parsed).filter(([key]) => body && typeof body === 'object' && Object.hasOwn(body, key)));
|
|
64
|
+
}
|
|
65
|
+
return parsed;
|
|
66
|
+
}
|
|
67
|
+
export async function call(client, op, ...args) {
|
|
68
|
+
return unwrap(await callRaw(client, op, ...args));
|
|
69
|
+
}
|
|
70
|
+
export function unwrap(response) {
|
|
71
|
+
if (response.code !== 0)
|
|
72
|
+
throw new ApiError(response.code, response.message);
|
|
73
|
+
return response.data;
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=contracts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contracts.js","sourceRoot":"","sources":["../src/contracts.ts"],"names":[],"mappings":"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"}
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export declare class ClientError extends Error {
|
|
2
|
+
readonly status: number;
|
|
3
|
+
readonly reason: string;
|
|
4
|
+
readonly requestId?: string | undefined;
|
|
5
|
+
constructor(status: number, reason: string, message: string, requestId?: string | undefined);
|
|
6
|
+
}
|
|
7
|
+
export declare class ApiError extends Error {
|
|
8
|
+
readonly code: number;
|
|
9
|
+
constructor(code: number, message: string);
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,qBAAa,WAAY,SAAQ,KAAK;IAElC,QAAQ,CAAC,MAAM,EAAE,MAAM;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM;IAEvB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM;IAJ7B,YACW,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACvB,OAAO,EAAE,MAAM,EACN,SAAS,CAAC,EAAE,MAAM,YAAA,EAI5B;CACF;AAED,qBAAa,QAAS,SAAQ,KAAK;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM;IAAjC,YAAqB,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAGjD;CACF"}
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export class ClientError extends Error {
|
|
2
|
+
status;
|
|
3
|
+
reason;
|
|
4
|
+
requestId;
|
|
5
|
+
constructor(status, reason, message, requestId) {
|
|
6
|
+
super(message);
|
|
7
|
+
this.status = status;
|
|
8
|
+
this.reason = reason;
|
|
9
|
+
this.requestId = requestId;
|
|
10
|
+
this.name = 'ClientError';
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
export class ApiError extends Error {
|
|
14
|
+
code;
|
|
15
|
+
constructor(code, message) {
|
|
16
|
+
super(message || `请求失败(code=${code})`);
|
|
17
|
+
this.code = code;
|
|
18
|
+
this.name = 'ApiError';
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,WAAY,SAAQ,KAAK;IAEzB,MAAM;IACN,MAAM;IAEN,SAAS;IAJpB,YACW,MAAc,EACd,MAAc,EACvB,OAAe,EACN,SAAkB;QAE3B,KAAK,CAAC,OAAO,CAAC,CAAC;sBALN,MAAM;sBACN,MAAM;yBAEN,SAAS;QAGlB,IAAI,CAAC,IAAI,GAAG,aAAa,CAAC;IAC5B,CAAC;CACF;AAED,MAAM,OAAO,QAAS,SAAQ,KAAK;IACZ,IAAI;IAAzB,YAAqB,IAAY,EAAE,OAAe;QAChD,KAAK,CAAC,OAAO,IAAI,aAAa,IAAI,GAAG,CAAC,CAAC;oBADpB,IAAI;QAEvB,IAAI,CAAC,IAAI,GAAG,UAAU,CAAC;IACzB,CAAC;CACF"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { AnyOperation } from '@fudanda/shared/core';
|
|
2
|
+
import type { Client } from './client.js';
|
|
3
|
+
import type { Subscription } from './subscriptions.js';
|
|
4
|
+
export interface EventStreamOptions<T> {
|
|
5
|
+
params: Record<string, string | number>;
|
|
6
|
+
signal?: AbortSignal;
|
|
7
|
+
parse: (event: string, data: unknown) => T;
|
|
8
|
+
onEvent: (event: T) => void;
|
|
9
|
+
onConnection?: (state: 'connecting' | 'connected' | 'reconnecting' | 'closed') => void;
|
|
10
|
+
onError?: (error: unknown) => void;
|
|
11
|
+
onUnauthorized?: () => void;
|
|
12
|
+
reconnectDelay?: number;
|
|
13
|
+
}
|
|
14
|
+
/** An instance-local registered SSE operation. Reconnect begins with a server ready
|
|
15
|
+
* frame; the consumer must requery durable data instead of trusting token replay. */
|
|
16
|
+
export declare function subscribeOperation<T>(client: Client, op: AnyOperation, options: EventStreamOptions<T>): Subscription;
|
|
17
|
+
//# sourceMappingURL=event-stream.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"event-stream.d.ts","sourceRoot":"","sources":["../src/event-stream.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAGzD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AACvC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAEpD,MAAM,WAAW,kBAAkB,CAAC,CAAC;IACnC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAC,MAAM,GAAC,MAAM,CAAC,CAAC;IACrC,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,KAAK,EAAE,CAAC,KAAK,EAAC,MAAM,EAAE,IAAI,EAAC,OAAO,KAAG,CAAC,CAAC;IACvC,OAAO,EAAE,CAAC,KAAK,EAAC,CAAC,KAAG,IAAI,CAAC;IACzB,YAAY,CAAC,EAAE,CAAC,KAAK,EAAC,YAAY,GAAC,WAAW,GAAC,cAAc,GAAC,QAAQ,KAAG,IAAI,CAAC;IAC9E,OAAO,CAAC,EAAE,CAAC,KAAK,EAAC,OAAO,KAAG,IAAI,CAAC;IAChC,cAAc,CAAC,EAAE,MAAI,IAAI,CAAC;IAC1B,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AACD;qFACqF;AACrF,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,MAAM,EAAC,MAAM,EAAE,EAAE,EAAC,YAAY,EAAE,OAAO,EAAC,kBAAkB,CAAC,CAAC,CAAC,GAAE,YAAY,CAuDhH"}
|