@springbrand/http 0.1.0-alpha.0 → 0.1.0-alpha.10
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/.agents/skills/dune-react-http/SKILL.md +34 -9
- package/README.md +104 -15
- package/dist/catalog.d.ts +113 -0
- package/dist/catalog.js +135 -0
- package/dist/client.js +45 -21
- package/dist/error-handler.d.ts +1 -1
- package/dist/error-handler.js +6 -3
- package/dist/errors.d.ts +8 -1
- package/dist/errors.js +15 -2
- package/dist/hono.d.ts +49 -0
- package/dist/hono.js +44 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +3 -2
- package/dist/query.js +2 -2
- package/dist/response.js +11 -14
- package/dist/sdk-error.d.ts +10 -0
- package/dist/sdk-error.js +20 -0
- package/dist/server/body.d.ts +10 -0
- package/dist/server/body.js +67 -0
- package/dist/server/content-disposition.d.ts +2 -0
- package/dist/server/content-disposition.js +7 -0
- package/dist/server/error-diagnostics.d.ts +7 -0
- package/dist/server/error-diagnostics.js +25 -0
- package/dist/server/index.d.ts +3 -0
- package/dist/server/index.js +11 -0
- package/dist/server/response.js +2 -1
- package/dist/server/service-auth.d.ts +8 -1
- package/dist/server/service-auth.js +24 -6
- package/dist/types.d.ts +9 -2
- package/dist/url.js +2 -2
- package/package.json +41 -13
package/dist/server/index.js
CHANGED
|
@@ -1,11 +1,22 @@
|
|
|
1
1
|
import { fail, ok, toErrorResponse } from "./response.js";
|
|
2
2
|
import { SERVICE_TOKEN_HEADER, createServiceRequest, verifyServiceToken } from "./service-auth.js";
|
|
3
|
+
import { limitRequestBody, readBytesLimited, readJson, readJsonLimited, requireIfMatchRevision } from "./body.js";
|
|
4
|
+
import { contentDispositionHeader } from "./content-disposition.js";
|
|
5
|
+
import { errorDiagnostics, redactSensitiveText } from "./error-diagnostics.js";
|
|
3
6
|
import { toSafeDiagnostics } from "../diagnostics.js";
|
|
4
7
|
export {
|
|
5
8
|
SERVICE_TOKEN_HEADER,
|
|
9
|
+
contentDispositionHeader,
|
|
6
10
|
createServiceRequest,
|
|
11
|
+
errorDiagnostics,
|
|
7
12
|
fail,
|
|
13
|
+
limitRequestBody,
|
|
8
14
|
ok,
|
|
15
|
+
readBytesLimited,
|
|
16
|
+
readJson,
|
|
17
|
+
readJsonLimited,
|
|
18
|
+
redactSensitiveText,
|
|
19
|
+
requireIfMatchRevision,
|
|
9
20
|
toErrorResponse,
|
|
10
21
|
toSafeDiagnostics,
|
|
11
22
|
verifyServiceToken
|
package/dist/server/response.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { httpStatusOf } from "@springbrand/errors";
|
|
1
2
|
import { ApiError, errorDefinitions } from "../errors.js";
|
|
2
3
|
function responseHeaders(options) {
|
|
3
4
|
const requestId = options.requestId ?? crypto.randomUUID();
|
|
@@ -22,7 +23,7 @@ function toErrorResponse(error, options = {}) {
|
|
|
22
23
|
if (error instanceof ApiError && error.kind === "http" && Number.isInteger(error.status) && error.status >= 400 && error.status <= 599 && /^[A-Z][A-Z0-9_]{0,127}$/.test(error.code) && !["NETWORK_ERROR", "REQUEST_TIMEOUT", "RESPONSE_TOO_LARGE"].includes(error.code)) {
|
|
23
24
|
return fail({ status: error.status, code: error.code, message: error.message, errors: error.errors, details: error.details, requestId: options.requestId ?? error.requestId, headers: options.headers });
|
|
24
25
|
}
|
|
25
|
-
return fail({ status:
|
|
26
|
+
return fail({ status: httpStatusOf("INTERNAL_ERROR"), code: "INTERNAL_ERROR", message: errorDefinitions.INTERNAL_ERROR, ...options });
|
|
26
27
|
}
|
|
27
28
|
export {
|
|
28
29
|
fail,
|
|
@@ -1,3 +1,10 @@
|
|
|
1
1
|
export declare const SERVICE_TOKEN_HEADER = "X-SpringBrand-Service-Token";
|
|
2
|
-
|
|
2
|
+
/**
|
|
3
|
+
* 构造绑定来源的服务请求。传入 Request 时会转发它的方法、请求头、请求体和 signal;
|
|
4
|
+
* 同源 URL 原样使用,其他来源只保留路径和查询参数并绑定到 baseUrl。
|
|
5
|
+
* 默认删除用户的 Authorization 和 Cookie;只有下游需要按调用者身份鉴权时,才显式传 forwardAuthorization。
|
|
6
|
+
*/
|
|
7
|
+
export declare function createServiceRequest(baseUrl: string | URL, token: string, input: string | URL | Request, init?: RequestInit, behavior?: {
|
|
8
|
+
forwardAuthorization?: boolean;
|
|
9
|
+
}): Request;
|
|
3
10
|
export declare function verifyServiceToken(provided: string | null | undefined, expected: string): Promise<boolean>;
|
|
@@ -1,13 +1,31 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { serviceUrl, boundUrl } from "../url.js";
|
|
2
2
|
const SERVICE_TOKEN_HEADER = "X-SpringBrand-Service-Token";
|
|
3
|
-
function createServiceRequest(baseUrl, token,
|
|
3
|
+
function createServiceRequest(baseUrl, token, input, init = {}, behavior = {}) {
|
|
4
4
|
if (!token.trim()) throw new TypeError("Service token is required");
|
|
5
|
-
const
|
|
6
|
-
const
|
|
7
|
-
|
|
5
|
+
const base = serviceUrl(String(baseUrl));
|
|
6
|
+
const source = input instanceof Request ? input : void 0;
|
|
7
|
+
let path = source ? new URL(source.url) : input;
|
|
8
|
+
if (path instanceof URL && source && path.origin !== base.origin) path = `${path.pathname}${path.search}`;
|
|
9
|
+
const url = boundUrl(base, path);
|
|
10
|
+
const headers = new Headers(source?.headers);
|
|
11
|
+
new Headers(init.headers).forEach((value, key) => headers.set(key, value));
|
|
12
|
+
if (!behavior.forwardAuthorization) headers.delete("Authorization");
|
|
8
13
|
headers.delete("Cookie");
|
|
9
14
|
headers.set(SERVICE_TOKEN_HEADER, token);
|
|
10
|
-
|
|
15
|
+
const body = init.body !== void 0 ? init.body : source?.body ?? null;
|
|
16
|
+
const options = {
|
|
17
|
+
...init,
|
|
18
|
+
method: init.method ?? source?.method ?? "GET",
|
|
19
|
+
headers,
|
|
20
|
+
body,
|
|
21
|
+
signal: init.signal ?? source?.signal ?? null,
|
|
22
|
+
cache: "no-store",
|
|
23
|
+
redirect: "manual"
|
|
24
|
+
};
|
|
25
|
+
if (body instanceof ReadableStream) options.duplex = "half";
|
|
26
|
+
if ("credentials" in Request.prototype) options.credentials = "omit";
|
|
27
|
+
else delete options.credentials;
|
|
28
|
+
return new Request(url, options);
|
|
11
29
|
}
|
|
12
30
|
async function verifyServiceToken(provided, expected) {
|
|
13
31
|
if (!provided || !expected) return false;
|
package/dist/types.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ApiError } from "./errors.js";
|
|
1
|
+
import type { ApiError, errorDefinitions } from "./errors.js";
|
|
2
2
|
export type HttpMethod = "GET" | "HEAD" | "POST" | "PUT" | "PATCH" | "DELETE" | "OPTIONS";
|
|
3
3
|
export type ResponseMode = "envelope" | "json" | "text" | "blob";
|
|
4
4
|
export type Feedback = "auto" | "inline" | "silent";
|
|
@@ -77,6 +77,8 @@ export type HttpRequestOptions = {
|
|
|
77
77
|
idempotencyKey?: string;
|
|
78
78
|
onUploadProgress?: (progress: UploadProgress) => void;
|
|
79
79
|
};
|
|
80
|
+
/** 替换 SDK 自身产生的提示文案;服务端返回的 errMsg 不受影响。 */
|
|
81
|
+
export type HttpMessages = Partial<Record<keyof typeof errorDefinitions | "MUTATION_RESULT_UNCERTAIN", string>>;
|
|
80
82
|
export type HttpClientConfig = {
|
|
81
83
|
baseUrl: string;
|
|
82
84
|
timeoutMs?: number;
|
|
@@ -88,11 +90,16 @@ export type HttpClientConfig = {
|
|
|
88
90
|
decodeResponse?: (data: unknown, info: ResponseInfo) => unknown;
|
|
89
91
|
decodeError?: (data: unknown, info: ResponseInfo) => ApiError;
|
|
90
92
|
isUnauthorized?: (error: ApiError) => boolean;
|
|
91
|
-
|
|
93
|
+
/**
|
|
94
|
+
* 当前 Bearer 被确认失效时调用。同一 token 的重复失效只处理一次:
|
|
95
|
+
* 返回 Promise 时,Promise 完成后恢复处理(例如复检会话后 token 仍有效);返回 void 时直到 token 变化。
|
|
96
|
+
*/
|
|
97
|
+
onUnauthorized?: (error: ApiError) => void | Promise<unknown>;
|
|
92
98
|
notify?: (notice: RequestNotice) => void;
|
|
93
99
|
onBusinessError?: (error: ApiError, context: ErrorContext) => boolean;
|
|
94
100
|
feedbackOwner?: FeedbackOwner;
|
|
95
101
|
onFailure?: (failure: RequestFailure) => void;
|
|
102
|
+
messages?: HttpMessages;
|
|
96
103
|
/** 注入标准 Fetch,便于测试或适配宿主;请求仍由 Axios 执行。 */
|
|
97
104
|
fetch?: typeof globalThis.fetch;
|
|
98
105
|
};
|
package/dist/url.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { sdkError } from "./sdk-error.js";
|
|
2
2
|
function invalidUrl() {
|
|
3
|
-
throw
|
|
3
|
+
throw sdkError("REQUEST_INVALID_INPUT", { status: 0, kind: "input" });
|
|
4
4
|
}
|
|
5
5
|
function serviceUrl(baseUrl) {
|
|
6
6
|
try {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@springbrand/http",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.10",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"publishConfig": {
|
|
@@ -38,36 +38,64 @@
|
|
|
38
38
|
"import": "./dist/query.js",
|
|
39
39
|
"default": "./dist/query.js"
|
|
40
40
|
},
|
|
41
|
+
"./catalog": {
|
|
42
|
+
"types": "./dist/catalog.d.ts",
|
|
43
|
+
"import": "./dist/catalog.js",
|
|
44
|
+
"default": "./dist/catalog.js"
|
|
45
|
+
},
|
|
46
|
+
"./hono": {
|
|
47
|
+
"types": "./dist/hono.d.ts",
|
|
48
|
+
"import": "./dist/hono.js",
|
|
49
|
+
"default": "./dist/hono.js"
|
|
50
|
+
},
|
|
41
51
|
"./server": {
|
|
42
52
|
"types": "./dist/server/index.d.ts",
|
|
43
53
|
"import": "./dist/server/index.js",
|
|
44
54
|
"default": "./dist/server/index.js"
|
|
45
55
|
},
|
|
56
|
+
"./types": {
|
|
57
|
+
"types": "./dist/types.d.ts"
|
|
58
|
+
},
|
|
46
59
|
"./package.json": "./package.json"
|
|
47
60
|
},
|
|
48
|
-
"scripts": {
|
|
49
|
-
"build": "vite build && tsc -p tsconfig.build.json",
|
|
50
|
-
"watch": "vite build --watch",
|
|
51
|
-
"typecheck": "tsc --noEmit",
|
|
52
|
-
"lint": "eslint src tests --ext ts --max-warnings 0",
|
|
53
|
-
"test": "vitest run"
|
|
54
|
-
},
|
|
55
61
|
"dependencies": {
|
|
56
|
-
"axios": "1.20.0"
|
|
62
|
+
"axios": "1.20.0",
|
|
63
|
+
"content-disposition": "2.0.1"
|
|
57
64
|
},
|
|
58
65
|
"peerDependencies": {
|
|
59
|
-
"@
|
|
66
|
+
"@springbrand/errors": ">=0.0.0-0",
|
|
67
|
+
"@tanstack/react-query": "^5.104.1",
|
|
68
|
+
"hono": "^4.12.0",
|
|
69
|
+
"zod": "^4.0.0"
|
|
60
70
|
},
|
|
61
71
|
"peerDependenciesMeta": {
|
|
62
72
|
"@tanstack/react-query": {
|
|
63
73
|
"optional": true
|
|
74
|
+
},
|
|
75
|
+
"hono": {
|
|
76
|
+
"optional": true
|
|
77
|
+
},
|
|
78
|
+
"zod": {
|
|
79
|
+
"optional": true
|
|
64
80
|
}
|
|
65
81
|
},
|
|
66
82
|
"devDependencies": {
|
|
67
83
|
"@tanstack/react-query": "5.104.1",
|
|
68
|
-
"
|
|
84
|
+
"@types/node": "^20.8.3",
|
|
85
|
+
"esbuild": "0.27.7",
|
|
86
|
+
"hono": "4.12.33",
|
|
87
|
+
"miniflare": "5.20260910.0-alpha",
|
|
69
88
|
"typescript": "^5.0.2",
|
|
70
89
|
"vite": "^5.4.19",
|
|
71
|
-
"
|
|
90
|
+
"vitest": "3.2.6",
|
|
91
|
+
"zod": "4.6.5",
|
|
92
|
+
"@springbrand/errors": "0.1.0-alpha.4"
|
|
93
|
+
},
|
|
94
|
+
"scripts": {
|
|
95
|
+
"build": "vite build && tsc -p tsconfig.build.json",
|
|
96
|
+
"watch": "vite build --watch",
|
|
97
|
+
"typecheck": "tsc --noEmit",
|
|
98
|
+
"lint": "eslint src tests --ext ts --max-warnings 0",
|
|
99
|
+
"test": "vitest run"
|
|
72
100
|
}
|
|
73
|
-
}
|
|
101
|
+
}
|