@miaixz/sdk 0.5.0
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 +202 -0
- package/NOTICE +7 -0
- package/README.md +412 -0
- package/dist/api/client.d.ts +223 -0
- package/dist/api/client.d.ts.map +1 -0
- package/dist/api/client.js +1069 -0
- package/dist/api/client.js.map +1 -0
- package/dist/api/endpoint.d.ts +10 -0
- package/dist/api/endpoint.d.ts.map +1 -0
- package/dist/api/endpoint.js +51 -0
- package/dist/api/endpoint.js.map +1 -0
- package/dist/api/errors.d.ts +180 -0
- package/dist/api/errors.d.ts.map +1 -0
- package/dist/api/errors.js +173 -0
- package/dist/api/errors.js.map +1 -0
- package/dist/api/index.d.ts +6 -0
- package/dist/api/index.d.ts.map +1 -0
- package/dist/api/index.js +5 -0
- package/dist/api/index.js.map +1 -0
- package/dist/api/request.d.ts +135 -0
- package/dist/api/request.d.ts.map +1 -0
- package/dist/api/request.js +2 -0
- package/dist/api/request.js.map +1 -0
- package/dist/api/response.d.ts +69 -0
- package/dist/api/response.d.ts.map +1 -0
- package/dist/api/response.js +43 -0
- package/dist/api/response.js.map +1 -0
- package/dist/api/telemetry.d.ts +139 -0
- package/dist/api/telemetry.d.ts.map +1 -0
- package/dist/api/telemetry.js +2 -0
- package/dist/api/telemetry.js.map +1 -0
- package/dist/appearance/appearance.d.ts +161 -0
- package/dist/appearance/appearance.d.ts.map +1 -0
- package/dist/appearance/appearance.js +614 -0
- package/dist/appearance/appearance.js.map +1 -0
- package/dist/appearance/index.d.ts +3 -0
- package/dist/appearance/index.d.ts.map +1 -0
- package/dist/appearance/index.js +3 -0
- package/dist/appearance/index.js.map +1 -0
- package/dist/auth/auth.d.ts +197 -0
- package/dist/auth/auth.d.ts.map +1 -0
- package/dist/auth/auth.js +314 -0
- package/dist/auth/auth.js.map +1 -0
- package/dist/auth/index.d.ts +2 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +2 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/config/config.d.ts +117 -0
- package/dist/config/config.d.ts.map +1 -0
- package/dist/config/config.js +241 -0
- package/dist/config/config.js.map +1 -0
- package/dist/config/index.d.ts +2 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +2 -0
- package/dist/config/index.js.map +1 -0
- package/dist/consts/index.d.ts +35 -0
- package/dist/consts/index.d.ts.map +1 -0
- package/dist/consts/index.js +35 -0
- package/dist/consts/index.js.map +1 -0
- package/dist/context/context.d.ts +123 -0
- package/dist/context/context.d.ts.map +1 -0
- package/dist/context/context.js +252 -0
- package/dist/context/context.js.map +1 -0
- package/dist/context/index.d.ts +2 -0
- package/dist/context/index.d.ts.map +1 -0
- package/dist/context/index.js +2 -0
- package/dist/context/index.js.map +1 -0
- package/dist/contracts/host-context.d.ts +132 -0
- package/dist/contracts/host-context.d.ts.map +1 -0
- package/dist/contracts/host-context.js +2 -0
- package/dist/contracts/host-context.js.map +1 -0
- package/dist/contracts/index.d.ts +8 -0
- package/dist/contracts/index.d.ts.map +1 -0
- package/dist/contracts/index.js +2 -0
- package/dist/contracts/index.js.map +1 -0
- package/dist/contracts/lifecycle.d.ts +49 -0
- package/dist/contracts/lifecycle.d.ts.map +1 -0
- package/dist/contracts/lifecycle.js +2 -0
- package/dist/contracts/lifecycle.js.map +1 -0
- package/dist/contracts/module-manifest.d.ts +110 -0
- package/dist/contracts/module-manifest.d.ts.map +1 -0
- package/dist/contracts/module-manifest.js +317 -0
- package/dist/contracts/module-manifest.js.map +1 -0
- package/dist/contracts/navigation.d.ts +36 -0
- package/dist/contracts/navigation.d.ts.map +1 -0
- package/dist/contracts/navigation.js +11 -0
- package/dist/contracts/navigation.js.map +1 -0
- package/dist/contracts/permission.d.ts +8 -0
- package/dist/contracts/permission.d.ts.map +1 -0
- package/dist/contracts/permission.js +14 -0
- package/dist/contracts/permission.js.map +1 -0
- package/dist/contracts/post-message.d.ts +124 -0
- package/dist/contracts/post-message.d.ts.map +1 -0
- package/dist/contracts/post-message.js +2 -0
- package/dist/contracts/post-message.js.map +1 -0
- package/dist/contracts/route.d.ts +45 -0
- package/dist/contracts/route.d.ts.map +1 -0
- package/dist/contracts/route.js +35 -0
- package/dist/contracts/route.js.map +1 -0
- package/dist/events/events.d.ts +191 -0
- package/dist/events/events.d.ts.map +1 -0
- package/dist/events/events.js +486 -0
- package/dist/events/events.js.map +1 -0
- package/dist/events/index.d.ts +2 -0
- package/dist/events/index.d.ts.map +1 -0
- package/dist/events/index.js +2 -0
- package/dist/events/index.js.map +1 -0
- package/dist/files/files.d.ts +138 -0
- package/dist/files/files.d.ts.map +1 -0
- package/dist/files/files.js +144 -0
- package/dist/files/files.js.map +1 -0
- package/dist/files/index.d.ts +2 -0
- package/dist/files/index.d.ts.map +1 -0
- package/dist/files/index.js +2 -0
- package/dist/files/index.js.map +1 -0
- package/dist/formatters/index.d.ts +44 -0
- package/dist/formatters/index.d.ts.map +1 -0
- package/dist/formatters/index.js +50 -0
- package/dist/formatters/index.js.map +1 -0
- package/dist/i18n/default-translator.d.ts +17 -0
- package/dist/i18n/default-translator.d.ts.map +1 -0
- package/dist/i18n/default-translator.js +19 -0
- package/dist/i18n/default-translator.js.map +1 -0
- package/dist/i18n/i18n.d.ts +248 -0
- package/dist/i18n/i18n.d.ts.map +1 -0
- package/dist/i18n/i18n.js +617 -0
- package/dist/i18n/i18n.js.map +1 -0
- package/dist/i18n/index.d.ts +2 -0
- package/dist/i18n/index.d.ts.map +1 -0
- package/dist/i18n/index.js +2 -0
- package/dist/i18n/index.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -0
- package/dist/models/index.d.ts +2 -0
- package/dist/models/index.d.ts.map +1 -0
- package/dist/models/index.js +2 -0
- package/dist/models/index.js.map +1 -0
- package/dist/permissions/index.d.ts +2 -0
- package/dist/permissions/index.d.ts.map +1 -0
- package/dist/permissions/index.js +2 -0
- package/dist/permissions/index.js.map +1 -0
- package/dist/permissions/permissions.d.ts +67 -0
- package/dist/permissions/permissions.d.ts.map +1 -0
- package/dist/permissions/permissions.js +123 -0
- package/dist/permissions/permissions.js.map +1 -0
- package/dist/runtime/direct-host-bridge.d.ts +11 -0
- package/dist/runtime/direct-host-bridge.d.ts.map +1 -0
- package/dist/runtime/direct-host-bridge.js +299 -0
- package/dist/runtime/direct-host-bridge.js.map +1 -0
- package/dist/runtime/index.d.ts +5 -0
- package/dist/runtime/index.d.ts.map +1 -0
- package/dist/runtime/index.js +3 -0
- package/dist/runtime/index.js.map +1 -0
- package/dist/runtime/post-message-bridge.d.ts +20 -0
- package/dist/runtime/post-message-bridge.d.ts.map +1 -0
- package/dist/runtime/post-message-bridge.js +1115 -0
- package/dist/runtime/post-message-bridge.js.map +1 -0
- package/dist/sdk.d.ts +177 -0
- package/dist/sdk.d.ts.map +1 -0
- package/dist/sdk.js +214 -0
- package/dist/sdk.js.map +1 -0
- package/dist/storage/index.d.ts +2 -0
- package/dist/storage/index.d.ts.map +1 -0
- package/dist/storage/index.js +2 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/storage/storage.d.ts +246 -0
- package/dist/storage/storage.d.ts.map +1 -0
- package/dist/storage/storage.js +358 -0
- package/dist/storage/storage.js.map +1 -0
- package/dist/types/api.d.ts +82 -0
- package/dist/types/api.d.ts.map +1 -0
- package/dist/types/api.js +2 -0
- package/dist/types/api.js.map +1 -0
- package/dist/types/appearance.d.ts +85 -0
- package/dist/types/appearance.d.ts.map +1 -0
- package/dist/types/appearance.js +48 -0
- package/dist/types/appearance.js.map +1 -0
- package/dist/types/config.d.ts +49 -0
- package/dist/types/config.d.ts.map +1 -0
- package/dist/types/config.js +2 -0
- package/dist/types/config.js.map +1 -0
- package/dist/types/context.d.ts +41 -0
- package/dist/types/context.d.ts.map +1 -0
- package/dist/types/context.js +2 -0
- package/dist/types/context.js.map +1 -0
- package/dist/types/file.d.ts +48 -0
- package/dist/types/file.d.ts.map +1 -0
- package/dist/types/file.js +2 -0
- package/dist/types/file.js.map +1 -0
- package/dist/types/index.d.ts +12 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +12 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/organization.d.ts +56 -0
- package/dist/types/organization.d.ts.map +1 -0
- package/dist/types/organization.js +2 -0
- package/dist/types/organization.js.map +1 -0
- package/dist/types/pagination.d.ts +55 -0
- package/dist/types/pagination.d.ts.map +1 -0
- package/dist/types/pagination.js +13 -0
- package/dist/types/pagination.js.map +1 -0
- package/dist/types/permissions.d.ts +26 -0
- package/dist/types/permissions.d.ts.map +1 -0
- package/dist/types/permissions.js +2 -0
- package/dist/types/permissions.js.map +1 -0
- package/dist/types/space.d.ts +43 -0
- package/dist/types/space.d.ts.map +1 -0
- package/dist/types/space.js +2 -0
- package/dist/types/space.js.map +1 -0
- package/dist/types/tenant.d.ts +31 -0
- package/dist/types/tenant.d.ts.map +1 -0
- package/dist/types/tenant.js +2 -0
- package/dist/types/tenant.js.map +1 -0
- package/dist/types/user.d.ts +48 -0
- package/dist/types/user.d.ts.map +1 -0
- package/dist/types/user.js +2 -0
- package/dist/types/user.js.map +1 -0
- package/dist/utils/date.d.ts +9 -0
- package/dist/utils/date.d.ts.map +1 -0
- package/dist/utils/date.js +11 -0
- package/dist/utils/date.js.map +1 -0
- package/dist/utils/index.d.ts +5 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +5 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/number.d.ts +11 -0
- package/dist/utils/number.d.ts.map +1 -0
- package/dist/utils/number.js +13 -0
- package/dist/utils/number.js.map +1 -0
- package/dist/utils/object.d.ts +9 -0
- package/dist/utils/object.d.ts.map +1 -0
- package/dist/utils/object.js +11 -0
- package/dist/utils/object.js.map +1 -0
- package/dist/utils/string.d.ts +9 -0
- package/dist/utils/string.d.ts.map +1 -0
- package/dist/utils/string.js +11 -0
- package/dist/utils/string.js.map +1 -0
- package/dist/validators/index.d.ts +8 -0
- package/dist/validators/index.d.ts.map +1 -0
- package/dist/validators/index.js +8 -0
- package/dist/validators/index.js.map +1 -0
- package/package.json +156 -0
|
@@ -0,0 +1,1069 @@
|
|
|
1
|
+
import { miaixzDefaultRequestTimeoutMs, miaixzHeaders } from "../consts/index.js";
|
|
2
|
+
import { miaixzDefaultI18n } from "../i18n/index.js";
|
|
3
|
+
import { isRecord } from "../utils/object.js";
|
|
4
|
+
import { normalizeMiaixzApiEndpoint } from "./endpoint.js";
|
|
5
|
+
import { MiaixzAbortError, MiaixzApiError, MiaixzNetworkError, MiaixzSdkError, MiaixzTimeoutError, } from "./errors.js";
|
|
6
|
+
import { isMiaixzApiEnvelope, isMiaixzApiSuccess, } from "./response.js";
|
|
7
|
+
const retryableStatuses = new Set([408, 429, 500, 502, 503, 504]);
|
|
8
|
+
const retryableMethods = new Set(["GET", "HEAD", "PUT", "DELETE"]);
|
|
9
|
+
const csrfProtectedMethods = new Set(["POST", "PUT", "PATCH", "DELETE"]);
|
|
10
|
+
const contextHeaderNames = new Set([
|
|
11
|
+
miaixzHeaders.traceId.toLowerCase(),
|
|
12
|
+
miaixzHeaders.userId.toLowerCase(),
|
|
13
|
+
miaixzHeaders.tenantId.toLowerCase(),
|
|
14
|
+
miaixzHeaders.organizationId.toLowerCase(),
|
|
15
|
+
miaixzHeaders.departmentId.toLowerCase(),
|
|
16
|
+
miaixzHeaders.spaceId.toLowerCase(),
|
|
17
|
+
miaixzHeaders.locale.toLowerCase(),
|
|
18
|
+
miaixzHeaders.timezone.toLowerCase(),
|
|
19
|
+
]);
|
|
20
|
+
/**
|
|
21
|
+
* Validates and normalizes an API base URL for the selected environment.
|
|
22
|
+
*
|
|
23
|
+
* @param value - Base URL to validate and normalize.
|
|
24
|
+
* @param environment - Runtime environment controlling HTTP loopback access.
|
|
25
|
+
* @param translate - Translator used for validation failures.
|
|
26
|
+
* @returns Normalized base URL without trailing separators.
|
|
27
|
+
* @throws MiaixzApiError When the endpoint violates the frozen transport policy.
|
|
28
|
+
*/
|
|
29
|
+
function normalizeBaseUrl(value, environment, translate) {
|
|
30
|
+
const normalized = normalizeMiaixzApiEndpoint(value, environment);
|
|
31
|
+
if (normalized === undefined) {
|
|
32
|
+
throw new MiaixzApiError(translate("sdk.error.api.baseUrlInvalid"), {
|
|
33
|
+
status: 0,
|
|
34
|
+
method: "CONFIG",
|
|
35
|
+
url: "[INVALID_URL]",
|
|
36
|
+
code: "API_BASE_URL_INVALID",
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
return normalized;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Validates a positive finite timeout value.
|
|
43
|
+
*
|
|
44
|
+
* @param value - Timeout value in milliseconds.
|
|
45
|
+
* @param translate - Translator used for validation failures.
|
|
46
|
+
* @returns Validated timeout value.
|
|
47
|
+
*/
|
|
48
|
+
function normalizeTimeout(value, translate) {
|
|
49
|
+
if (!Number.isFinite(value) || value <= 0) {
|
|
50
|
+
throw new MiaixzSdkError(translate("sdk.error.api.timeoutInvalid"), {
|
|
51
|
+
code: "API_TIMEOUT_INVALID",
|
|
52
|
+
details: { timeoutMs: value },
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
return value;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Validates a non-negative integer retry count.
|
|
59
|
+
*
|
|
60
|
+
* @param value - Retry count to validate.
|
|
61
|
+
* @param translate - Translator used for validation failures.
|
|
62
|
+
* @returns Validated retry count.
|
|
63
|
+
*/
|
|
64
|
+
function normalizeRetry(value, translate) {
|
|
65
|
+
if (!Number.isInteger(value) || value < 0 || value > 5) {
|
|
66
|
+
throw new MiaixzSdkError(translate("sdk.error.api.retryInvalid"), {
|
|
67
|
+
code: "API_RETRY_INVALID",
|
|
68
|
+
details: { retry: value },
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
return value;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Resolves and validates the response-envelope mode before a request is sent.
|
|
75
|
+
*
|
|
76
|
+
* @param responseType - Response body parser selected by the caller.
|
|
77
|
+
* @param envelope - Optional caller-supplied envelope behavior.
|
|
78
|
+
* @param translate - Translator used for validation failures.
|
|
79
|
+
* @returns The initial envelope mode for the request.
|
|
80
|
+
*/
|
|
81
|
+
function normalizeEnvelopeMode(responseType, envelope, translate) {
|
|
82
|
+
if ((responseType === "text" ||
|
|
83
|
+
responseType === "blob" ||
|
|
84
|
+
responseType === "arrayBuffer" ||
|
|
85
|
+
responseType === "void") &&
|
|
86
|
+
envelope !== undefined &&
|
|
87
|
+
envelope !== "none") {
|
|
88
|
+
throw new MiaixzSdkError(translate("sdk.error.api.envelopeModeInvalid"), {
|
|
89
|
+
code: "API_ENVELOPE_MODE_INVALID",
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
if (responseType === "text" ||
|
|
93
|
+
responseType === "blob" ||
|
|
94
|
+
responseType === "arrayBuffer" ||
|
|
95
|
+
responseType === "void") {
|
|
96
|
+
return "none";
|
|
97
|
+
}
|
|
98
|
+
return envelope ?? "required";
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Creates a stable request-origin validation error without exposing the rejected value.
|
|
102
|
+
*
|
|
103
|
+
* @param translate - Translator used for the public error message.
|
|
104
|
+
* @returns A safe request-origin error.
|
|
105
|
+
*/
|
|
106
|
+
function createRequestOriginError(translate) {
|
|
107
|
+
return new MiaixzSdkError(translate("sdk.error.api.requestOriginInvalid"), {
|
|
108
|
+
code: "API_REQUEST_ORIGIN_INVALID",
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Resolves a caller request path while rejecting every absolute or cross-origin form.
|
|
113
|
+
*
|
|
114
|
+
* @param baseUrl - Validated base URL used for relative resolution.
|
|
115
|
+
* @param path - Caller-supplied relative or slash-relative path.
|
|
116
|
+
* @param translate - Translator used for validation failures.
|
|
117
|
+
* @returns A fully resolved URL on the configured origin.
|
|
118
|
+
* @throws MiaixzSdkError When the path is absolute, protocol-relative, or cross-origin.
|
|
119
|
+
*/
|
|
120
|
+
function resolveRequestUrl(baseUrl, path, translate) {
|
|
121
|
+
if (typeof path !== "string")
|
|
122
|
+
throw createRequestOriginError(translate);
|
|
123
|
+
const trimmedStart = path.trimStart();
|
|
124
|
+
if (/^[a-z][a-z\d+.-]*:/i.test(trimmedStart) || /^[\\/]{2}/.test(trimmedStart)) {
|
|
125
|
+
throw createRequestOriginError(translate);
|
|
126
|
+
}
|
|
127
|
+
try {
|
|
128
|
+
const base = new URL(baseUrl);
|
|
129
|
+
const resolved = new URL(path, `${baseUrl}/`);
|
|
130
|
+
if (resolved.origin !== base.origin ||
|
|
131
|
+
resolved.protocol !== base.protocol ||
|
|
132
|
+
resolved.username !== "" ||
|
|
133
|
+
resolved.password !== "") {
|
|
134
|
+
throw createRequestOriginError(translate);
|
|
135
|
+
}
|
|
136
|
+
return resolved.href;
|
|
137
|
+
}
|
|
138
|
+
catch (cause) {
|
|
139
|
+
if (cause instanceof MiaixzSdkError)
|
|
140
|
+
throw cause;
|
|
141
|
+
throw createRequestOriginError(translate);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Revalidates an interceptor-produced URL against the configured base origin.
|
|
146
|
+
*
|
|
147
|
+
* @param baseUrl - Validated base URL defining the only permitted origin.
|
|
148
|
+
* @param value - Interceptor-produced URL value.
|
|
149
|
+
* @param translate - Translator used for validation failures.
|
|
150
|
+
* @returns A normalized absolute URL on the configured origin.
|
|
151
|
+
* @throws MiaixzSdkError When the final URL resolves outside the configured origin.
|
|
152
|
+
*/
|
|
153
|
+
function validatePreparedRequestUrl(baseUrl, value, translate) {
|
|
154
|
+
if (typeof value !== "string")
|
|
155
|
+
throw createRequestOriginError(translate);
|
|
156
|
+
try {
|
|
157
|
+
const base = new URL(baseUrl);
|
|
158
|
+
const resolved = new URL(value, `${baseUrl}/`);
|
|
159
|
+
if (resolved.origin !== base.origin ||
|
|
160
|
+
resolved.protocol !== base.protocol ||
|
|
161
|
+
resolved.username !== "" ||
|
|
162
|
+
resolved.password !== "") {
|
|
163
|
+
throw createRequestOriginError(translate);
|
|
164
|
+
}
|
|
165
|
+
return resolved.href;
|
|
166
|
+
}
|
|
167
|
+
catch (cause) {
|
|
168
|
+
if (cause instanceof MiaixzSdkError)
|
|
169
|
+
throw cause;
|
|
170
|
+
throw createRequestOriginError(translate);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Reports whether the final request contains any runtime-context header.
|
|
175
|
+
*
|
|
176
|
+
* @param headers - Final request headers to inspect.
|
|
177
|
+
* @param providedNames - Header names returned by the configured context provider.
|
|
178
|
+
* @returns Whether at least one context header remains on the request.
|
|
179
|
+
*/
|
|
180
|
+
function hasContextHeader(headers, providedNames) {
|
|
181
|
+
return [...contextHeaderNames, ...providedNames].some((name) => headers.has(name));
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Computes the mandatory Fetch redirect mode from final authentication and context state.
|
|
185
|
+
*
|
|
186
|
+
* @param headers - Final request headers after interception.
|
|
187
|
+
* @param credentials - Final Fetch credentials mode.
|
|
188
|
+
* @param authenticationEnabled - Whether an authentication integration is active.
|
|
189
|
+
* @param providedContextNames - Header names returned by the context provider.
|
|
190
|
+
* @returns The fixed safe redirect mode for the request.
|
|
191
|
+
*/
|
|
192
|
+
function resolveRedirectMode(headers, credentials, authenticationEnabled, providedContextNames) {
|
|
193
|
+
return authenticationEnabled ||
|
|
194
|
+
credentials === "include" ||
|
|
195
|
+
headers.has(miaixzHeaders.authorization) ||
|
|
196
|
+
headers.has("Cookie") ||
|
|
197
|
+
headers.has("Proxy-Authorization") ||
|
|
198
|
+
headers.has("X-Api-Key") ||
|
|
199
|
+
headers.has(miaixzHeaders.csrfToken) ||
|
|
200
|
+
hasContextHeader(headers, providedContextNames)
|
|
201
|
+
? "error"
|
|
202
|
+
: "follow";
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Creates a CSRF failure without retaining provider output or exceptions.
|
|
206
|
+
*
|
|
207
|
+
* @param translate - Translator used for the public error message.
|
|
208
|
+
* @returns A safe missing-token error.
|
|
209
|
+
*/
|
|
210
|
+
function createCsrfTokenMissingError(translate) {
|
|
211
|
+
return new MiaixzSdkError(translate("sdk.error.api.csrfTokenMissing"), {
|
|
212
|
+
code: "CSRF_TOKEN_MISSING",
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Applies the authoritative Cookie/BFF CSRF policy to final request headers.
|
|
217
|
+
*
|
|
218
|
+
* @param headers - Final request headers to update.
|
|
219
|
+
* @param method - Final HTTP method used by Fetch.
|
|
220
|
+
* @param csrf - Optional CSRF policy configured on the client.
|
|
221
|
+
* @param translate - Translator used for missing-token failures.
|
|
222
|
+
* @returns Promise settled after the final header policy is applied.
|
|
223
|
+
* @throws MiaixzSdkError When a protected request has no non-empty provider token.
|
|
224
|
+
*/
|
|
225
|
+
async function applyCsrfPolicy(headers, method, csrf, translate) {
|
|
226
|
+
if (csrf?.required !== true)
|
|
227
|
+
return;
|
|
228
|
+
headers.delete(miaixzHeaders.csrfToken);
|
|
229
|
+
if (!csrfProtectedMethods.has(method))
|
|
230
|
+
return;
|
|
231
|
+
let token;
|
|
232
|
+
try {
|
|
233
|
+
token = await csrf.tokenProvider?.();
|
|
234
|
+
}
|
|
235
|
+
catch {
|
|
236
|
+
throw createCsrfTokenMissingError(translate);
|
|
237
|
+
}
|
|
238
|
+
if (typeof token !== "string" || token.trim().length === 0) {
|
|
239
|
+
throw createCsrfTokenMissingError(translate);
|
|
240
|
+
}
|
|
241
|
+
headers.set(miaixzHeaders.csrfToken, token.trim());
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Converts query primitives to their URL representation.
|
|
245
|
+
*
|
|
246
|
+
* @param value - Query value to serialize.
|
|
247
|
+
* @returns URL-compatible string representation.
|
|
248
|
+
*/
|
|
249
|
+
function serializeQueryValue(value) {
|
|
250
|
+
return value instanceof Date ? value.toISOString() : String(value);
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Appends scalar or repeated query values without disturbing an existing hash.
|
|
254
|
+
* `null` and `undefined` values are omitted.
|
|
255
|
+
*
|
|
256
|
+
* @param url - Request URL to update.
|
|
257
|
+
* @param query - Optional query parameters to append.
|
|
258
|
+
* @returns URL containing the serialized query parameters.
|
|
259
|
+
* @public
|
|
260
|
+
*/
|
|
261
|
+
export function appendMiaixzQuery(url, query) {
|
|
262
|
+
if (!query)
|
|
263
|
+
return url;
|
|
264
|
+
const hashIndex = url.indexOf("#");
|
|
265
|
+
const hash = hashIndex >= 0 ? url.slice(hashIndex) : "";
|
|
266
|
+
const base = hashIndex >= 0 ? url.slice(0, hashIndex) : url;
|
|
267
|
+
const parameters = new URLSearchParams();
|
|
268
|
+
for (const [key, rawValue] of Object.entries(query)) {
|
|
269
|
+
const values = Array.isArray(rawValue) ? rawValue : [rawValue];
|
|
270
|
+
for (const value of values) {
|
|
271
|
+
if (value !== null && value !== undefined)
|
|
272
|
+
parameters.append(key, serializeQueryValue(value));
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
const serialized = parameters.toString();
|
|
276
|
+
if (!serialized)
|
|
277
|
+
return url;
|
|
278
|
+
return `${base}${base.includes("?") ? "&" : "?"}${serialized}${hash}`;
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Detects body types that Fetch can transmit without JSON serialization.
|
|
282
|
+
*
|
|
283
|
+
* @param value - Request body candidate to inspect.
|
|
284
|
+
* @returns Whether the value is a native Fetch body.
|
|
285
|
+
*/
|
|
286
|
+
function isNativeBody(value) {
|
|
287
|
+
return (typeof value === "string" ||
|
|
288
|
+
value instanceof Blob ||
|
|
289
|
+
value instanceof FormData ||
|
|
290
|
+
value instanceof URLSearchParams ||
|
|
291
|
+
value instanceof ArrayBuffer ||
|
|
292
|
+
ArrayBuffer.isView(value) ||
|
|
293
|
+
(typeof ReadableStream !== "undefined" && value instanceof ReadableStream));
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* Serializes plain records and arrays as JSON while preserving native bodies.
|
|
297
|
+
*
|
|
298
|
+
* @param body - Optional request body to serialize.
|
|
299
|
+
* @param headers - Request headers updated with a JSON content type when needed.
|
|
300
|
+
* @returns Native or serialized body accepted by Fetch.
|
|
301
|
+
*/
|
|
302
|
+
function serializeBody(body, headers) {
|
|
303
|
+
if (body === undefined)
|
|
304
|
+
return undefined;
|
|
305
|
+
if (isNativeBody(body))
|
|
306
|
+
return body;
|
|
307
|
+
if (!headers.has("Content-Type"))
|
|
308
|
+
headers.set("Content-Type", "application/json");
|
|
309
|
+
return JSON.stringify(body);
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* Reads the standard or legacy request identifier header.
|
|
313
|
+
*
|
|
314
|
+
* @param headers - Response headers to inspect.
|
|
315
|
+
* @returns Request identifier when the server supplied one.
|
|
316
|
+
*/
|
|
317
|
+
function getRequestId(headers) {
|
|
318
|
+
return headers.get(miaixzHeaders.requestId) ?? headers.get("x-request-id") ?? undefined;
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* Determines whether the selected response mode represents a JSON API response.
|
|
322
|
+
*
|
|
323
|
+
* @param response - HTTP response to inspect.
|
|
324
|
+
* @param responseType - Configured response parsing mode.
|
|
325
|
+
* @returns Whether the response is expected to contain a JSON envelope.
|
|
326
|
+
*/
|
|
327
|
+
function expectsJsonEnvelope(response, responseType) {
|
|
328
|
+
if (response.status === 204 || response.status === 205 || responseType === "void")
|
|
329
|
+
return false;
|
|
330
|
+
if (responseType === "json")
|
|
331
|
+
return true;
|
|
332
|
+
if (responseType !== "auto")
|
|
333
|
+
return false;
|
|
334
|
+
const contentType = response.headers.get("content-type")?.toLowerCase() ?? "";
|
|
335
|
+
return contentType.includes("json") || contentType.includes("+json");
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Parses a response using the explicit mode or its Content-Type header.
|
|
339
|
+
*
|
|
340
|
+
* @param response - HTTP response whose body should be parsed.
|
|
341
|
+
* @param responseType - Configured response parsing mode.
|
|
342
|
+
* @returns Parsed response body.
|
|
343
|
+
*/
|
|
344
|
+
async function parseBody(response, responseType) {
|
|
345
|
+
if (responseType === "void" || response.status === 204 || response.status === 205)
|
|
346
|
+
return undefined;
|
|
347
|
+
if (responseType === "blob")
|
|
348
|
+
return response.blob();
|
|
349
|
+
if (responseType === "arrayBuffer")
|
|
350
|
+
return response.arrayBuffer();
|
|
351
|
+
if (responseType === "text")
|
|
352
|
+
return response.text();
|
|
353
|
+
if (responseType === "json")
|
|
354
|
+
return parseJsonBody(response);
|
|
355
|
+
const contentType = response.headers.get("content-type")?.toLowerCase() ?? "";
|
|
356
|
+
if (contentType.includes("json") || contentType.includes("+json"))
|
|
357
|
+
return parseJsonBody(response);
|
|
358
|
+
if (contentType.startsWith("text/"))
|
|
359
|
+
return response.text();
|
|
360
|
+
return response.blob();
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* Parses JSON while distinguishing an empty body from malformed non-empty JSON.
|
|
364
|
+
*
|
|
365
|
+
* @param response - JSON response to parse.
|
|
366
|
+
* @returns Parsed JSON, or undefined when the body is empty.
|
|
367
|
+
*/
|
|
368
|
+
async function parseJsonBody(response) {
|
|
369
|
+
const fallback = response.clone();
|
|
370
|
+
try {
|
|
371
|
+
return await response.json();
|
|
372
|
+
}
|
|
373
|
+
catch (cause) {
|
|
374
|
+
const text = await fallback.text().catch(() => undefined);
|
|
375
|
+
if (text === "")
|
|
376
|
+
return undefined;
|
|
377
|
+
throw cause;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
/**
|
|
381
|
+
* Normalizes an unsuccessful HTTP body into stable problem details.
|
|
382
|
+
*
|
|
383
|
+
* @param body - Parsed unsuccessful response body.
|
|
384
|
+
* @param response - Original HTTP response.
|
|
385
|
+
* @returns Stable problem details extracted from the response.
|
|
386
|
+
*/
|
|
387
|
+
function extractProblem(body, response) {
|
|
388
|
+
if (isMiaixzApiEnvelope(body)) {
|
|
389
|
+
return {
|
|
390
|
+
code: isMiaixzApiSuccess(body) ? `HTTP_${response.status}` : String(body.errcode),
|
|
391
|
+
};
|
|
392
|
+
}
|
|
393
|
+
if (!isRecord(body))
|
|
394
|
+
return {};
|
|
395
|
+
const result = {};
|
|
396
|
+
if (typeof body.code === "string")
|
|
397
|
+
result.code = body.code;
|
|
398
|
+
if (typeof body.requestId === "string")
|
|
399
|
+
result.requestId = body.requestId;
|
|
400
|
+
return result;
|
|
401
|
+
}
|
|
402
|
+
/**
|
|
403
|
+
* Removes URL credentials, fragments, and query values before an error exposes the URL.
|
|
404
|
+
*
|
|
405
|
+
* @param value - Absolute request URL to sanitize.
|
|
406
|
+
* @returns URL containing only origin, path, and redacted query keys.
|
|
407
|
+
*/
|
|
408
|
+
function sanitizeErrorUrl(value) {
|
|
409
|
+
try {
|
|
410
|
+
const url = new URL(value);
|
|
411
|
+
const query = [...url.searchParams.keys()].map((key) => `${encodeURIComponent(key)}=[REDACTED]`);
|
|
412
|
+
return `${url.origin}${url.pathname}${query.length > 0 ? `?${query.join("&")}` : ""}`;
|
|
413
|
+
}
|
|
414
|
+
catch {
|
|
415
|
+
return "[INVALID_URL]";
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
/**
|
|
419
|
+
* Wraps response parsing or runtime validation failures in the frozen SDK error contract.
|
|
420
|
+
*
|
|
421
|
+
* @param cause - Parser failure to preserve when it is an Error instance.
|
|
422
|
+
* @param requestId - Optional server request identifier.
|
|
423
|
+
* @param translate - Translator used for the public error message.
|
|
424
|
+
* @returns Structured response-validation error.
|
|
425
|
+
*/
|
|
426
|
+
function createResponseInvalidError(cause, requestId, translate) {
|
|
427
|
+
const details = {
|
|
428
|
+
...(requestId === undefined ? {} : { requestId }),
|
|
429
|
+
...(cause instanceof Error ? {} : { type: typeof cause }),
|
|
430
|
+
};
|
|
431
|
+
return new MiaixzSdkError(translate("sdk.error.api.responseInvalid"), {
|
|
432
|
+
code: "API_RESPONSE_INVALID",
|
|
433
|
+
cause,
|
|
434
|
+
...(Object.keys(details).length === 0 ? {} : { details: Object.freeze(details) }),
|
|
435
|
+
});
|
|
436
|
+
}
|
|
437
|
+
/**
|
|
438
|
+
* Applies an optional runtime response parser after all response interceptors.
|
|
439
|
+
*
|
|
440
|
+
* @typeParam T - Verified response value returned to the caller.
|
|
441
|
+
* @param value - Untrusted response value after interception.
|
|
442
|
+
* @param parser - Optional application runtime parser.
|
|
443
|
+
* @param requestId - Optional server request identifier.
|
|
444
|
+
* @param translate - Translator used for parser failures.
|
|
445
|
+
* @returns Parsed response data, or the compatibility generic value when no parser exists.
|
|
446
|
+
*/
|
|
447
|
+
function parseResponseData(value, parser, requestId, translate) {
|
|
448
|
+
if (!parser)
|
|
449
|
+
return value;
|
|
450
|
+
try {
|
|
451
|
+
return parser(value);
|
|
452
|
+
}
|
|
453
|
+
catch (cause) {
|
|
454
|
+
throw createResponseInvalidError(cause, requestId, translate);
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
/**
|
|
458
|
+
* Combines caller cancellation with an SDK timeout and returns cleanup controls.
|
|
459
|
+
*
|
|
460
|
+
* @param signal - Optional cancellation signal supplied by the caller.
|
|
461
|
+
* @param timeoutMs - Timeout threshold in milliseconds.
|
|
462
|
+
* @returns Combined abort context and cleanup controls.
|
|
463
|
+
*/
|
|
464
|
+
function createAbortContext(signal, timeoutMs) {
|
|
465
|
+
const controller = new AbortController();
|
|
466
|
+
let didTimeout = false;
|
|
467
|
+
const abortFromParent = () => controller.abort(signal?.reason);
|
|
468
|
+
signal?.addEventListener("abort", abortFromParent, { once: true });
|
|
469
|
+
if (signal?.aborted)
|
|
470
|
+
abortFromParent();
|
|
471
|
+
const timeout = setTimeout(() => {
|
|
472
|
+
didTimeout = true;
|
|
473
|
+
controller.abort();
|
|
474
|
+
}, timeoutMs);
|
|
475
|
+
return {
|
|
476
|
+
signal: controller.signal,
|
|
477
|
+
timedOut: () => didTimeout,
|
|
478
|
+
dispose: () => {
|
|
479
|
+
clearTimeout(timeout);
|
|
480
|
+
signal?.removeEventListener("abort", abortFromParent);
|
|
481
|
+
},
|
|
482
|
+
};
|
|
483
|
+
}
|
|
484
|
+
/**
|
|
485
|
+
* Calculates Retry-After or bounded exponential backoff in milliseconds.
|
|
486
|
+
*
|
|
487
|
+
* @param attempt - Zero-based retry attempt number.
|
|
488
|
+
* @param response - Optional response containing a Retry-After header.
|
|
489
|
+
* @returns Delay in milliseconds before the next retry.
|
|
490
|
+
*/
|
|
491
|
+
function retryDelay(attempt, response) {
|
|
492
|
+
const retryAfter = response?.headers.get("retry-after");
|
|
493
|
+
if (retryAfter) {
|
|
494
|
+
const seconds = Number(retryAfter);
|
|
495
|
+
if (Number.isFinite(seconds))
|
|
496
|
+
return Math.max(0, seconds * 1000);
|
|
497
|
+
const timestamp = Date.parse(retryAfter);
|
|
498
|
+
if (Number.isFinite(timestamp))
|
|
499
|
+
return Math.max(0, timestamp - Date.now());
|
|
500
|
+
}
|
|
501
|
+
return Math.min(250 * 2 ** attempt, 2_000);
|
|
502
|
+
}
|
|
503
|
+
/**
|
|
504
|
+
* Waits for a retry delay while remaining cancellable by the caller.
|
|
505
|
+
*
|
|
506
|
+
* @param milliseconds - Delay duration in milliseconds.
|
|
507
|
+
* @param signal - Optional cancellation signal supplied by the caller.
|
|
508
|
+
* @param translate - Translator used for cancellation errors.
|
|
509
|
+
* @returns Promise that resolves after the delay.
|
|
510
|
+
*/
|
|
511
|
+
async function wait(milliseconds, signal, translate) {
|
|
512
|
+
if (milliseconds <= 0)
|
|
513
|
+
return;
|
|
514
|
+
if (signal?.aborted)
|
|
515
|
+
throw new MiaixzAbortError(signal.reason, translate);
|
|
516
|
+
await new Promise((resolve, reject) => {
|
|
517
|
+
const complete = () => {
|
|
518
|
+
signal?.removeEventListener("abort", abort);
|
|
519
|
+
resolve();
|
|
520
|
+
};
|
|
521
|
+
const timeout = setTimeout(complete, milliseconds);
|
|
522
|
+
const abort = () => {
|
|
523
|
+
clearTimeout(timeout);
|
|
524
|
+
signal?.removeEventListener("abort", abort);
|
|
525
|
+
reject(new MiaixzAbortError(signal?.reason, translate));
|
|
526
|
+
};
|
|
527
|
+
signal?.addEventListener("abort", abort, { once: true });
|
|
528
|
+
});
|
|
529
|
+
}
|
|
530
|
+
const sensitiveHeaderNames = new Set([
|
|
531
|
+
"authorization",
|
|
532
|
+
"cookie",
|
|
533
|
+
"set-cookie",
|
|
534
|
+
"x-api-key",
|
|
535
|
+
"x-csrf-token",
|
|
536
|
+
"proxy-authorization",
|
|
537
|
+
]);
|
|
538
|
+
const sensitiveFieldNames = new Set([
|
|
539
|
+
"token",
|
|
540
|
+
"accesstoken",
|
|
541
|
+
"refreshtoken",
|
|
542
|
+
"password",
|
|
543
|
+
"secret",
|
|
544
|
+
"apikey",
|
|
545
|
+
"key",
|
|
546
|
+
]);
|
|
547
|
+
const telemetryMaximumDepth = 8;
|
|
548
|
+
const telemetryMaximumCollectionSize = 100;
|
|
549
|
+
const telemetryMaximumStringLength = 1_024;
|
|
550
|
+
const redactedValue = "[REDACTED]";
|
|
551
|
+
const truncatedValue = "[TRUNCATED]";
|
|
552
|
+
const circularValue = "[CIRCULAR]";
|
|
553
|
+
/**
|
|
554
|
+
* Reports whether an object can be traversed without invoking custom collection behavior.
|
|
555
|
+
*
|
|
556
|
+
* @param value - Object candidate to inspect.
|
|
557
|
+
* @returns Whether the value is an object literal or null-prototype record.
|
|
558
|
+
*/
|
|
559
|
+
function isPlainTelemetryObject(value) {
|
|
560
|
+
const prototype = Object.getPrototypeOf(value);
|
|
561
|
+
return prototype === Object.prototype || prototype === null;
|
|
562
|
+
}
|
|
563
|
+
/**
|
|
564
|
+
* Normalizes a field name before comparing it with the sensitive-name registry.
|
|
565
|
+
*
|
|
566
|
+
* @param value - Field name to normalize.
|
|
567
|
+
* @returns Lowercase field name without separators.
|
|
568
|
+
*/
|
|
569
|
+
function normalizeSensitiveFieldName(value) {
|
|
570
|
+
return value.replace(/[-_]/g, "").toLowerCase();
|
|
571
|
+
}
|
|
572
|
+
/**
|
|
573
|
+
* Truncates a string by Unicode code points rather than UTF-16 code units.
|
|
574
|
+
*
|
|
575
|
+
* @param value - String value to limit.
|
|
576
|
+
* @returns Original or safely truncated string.
|
|
577
|
+
*/
|
|
578
|
+
function truncateTelemetryString(value) {
|
|
579
|
+
const characters = [...value];
|
|
580
|
+
if (characters.length <= telemetryMaximumStringLength)
|
|
581
|
+
return value;
|
|
582
|
+
return `${characters.slice(0, telemetryMaximumStringLength).join("")}${truncatedValue}`;
|
|
583
|
+
}
|
|
584
|
+
/**
|
|
585
|
+
* Creates a fixed safe summary for a non-JSON request-body value.
|
|
586
|
+
*
|
|
587
|
+
* @param value - Value whose content must not be recorded.
|
|
588
|
+
* @returns Frozen type and byte-size summary.
|
|
589
|
+
*/
|
|
590
|
+
function summarizeTelemetryValue(value) {
|
|
591
|
+
let type = typeof value;
|
|
592
|
+
let bytes;
|
|
593
|
+
if (typeof value === "string") {
|
|
594
|
+
type = "string";
|
|
595
|
+
bytes = new TextEncoder().encode(value).byteLength;
|
|
596
|
+
}
|
|
597
|
+
else if (value instanceof URLSearchParams) {
|
|
598
|
+
type = "URLSearchParams";
|
|
599
|
+
bytes = new TextEncoder().encode(value.toString()).byteLength;
|
|
600
|
+
}
|
|
601
|
+
else if (value instanceof Blob) {
|
|
602
|
+
type = value.constructor.name || "Blob";
|
|
603
|
+
bytes = value.size;
|
|
604
|
+
}
|
|
605
|
+
else if (value instanceof ArrayBuffer) {
|
|
606
|
+
type = "ArrayBuffer";
|
|
607
|
+
bytes = value.byteLength;
|
|
608
|
+
}
|
|
609
|
+
else if (ArrayBuffer.isView(value)) {
|
|
610
|
+
type = value.constructor.name;
|
|
611
|
+
bytes = value.byteLength;
|
|
612
|
+
}
|
|
613
|
+
else if (value instanceof FormData) {
|
|
614
|
+
type = "FormData";
|
|
615
|
+
}
|
|
616
|
+
else if (typeof ReadableStream !== "undefined" && value instanceof ReadableStream) {
|
|
617
|
+
type = "ReadableStream";
|
|
618
|
+
}
|
|
619
|
+
else if (value !== null && typeof value === "object") {
|
|
620
|
+
type = value.constructor?.name || "Object";
|
|
621
|
+
}
|
|
622
|
+
return Object.freeze({ type, bytes });
|
|
623
|
+
}
|
|
624
|
+
/**
|
|
625
|
+
* Recursively creates a bounded, getter-safe telemetry snapshot.
|
|
626
|
+
*
|
|
627
|
+
* @param value - Runtime value to sanitize.
|
|
628
|
+
* @param depth - Current recursion depth.
|
|
629
|
+
* @param ancestors - Objects in the active traversal path.
|
|
630
|
+
* @returns Frozen safe telemetry value.
|
|
631
|
+
*/
|
|
632
|
+
function sanitizeTelemetryValue(value, depth = 0, ancestors = new Set()) {
|
|
633
|
+
if (typeof value === "string")
|
|
634
|
+
return truncateTelemetryString(value);
|
|
635
|
+
if (value === null ||
|
|
636
|
+
typeof value === "number" ||
|
|
637
|
+
typeof value === "boolean" ||
|
|
638
|
+
typeof value === "undefined") {
|
|
639
|
+
return value;
|
|
640
|
+
}
|
|
641
|
+
if (typeof value !== "object")
|
|
642
|
+
return summarizeTelemetryValue(value);
|
|
643
|
+
if (ancestors.has(value))
|
|
644
|
+
return circularValue;
|
|
645
|
+
if (depth >= telemetryMaximumDepth)
|
|
646
|
+
return truncatedValue;
|
|
647
|
+
if (!Array.isArray(value) && !isPlainTelemetryObject(value)) {
|
|
648
|
+
return summarizeTelemetryValue(value);
|
|
649
|
+
}
|
|
650
|
+
const nextAncestors = new Set(ancestors);
|
|
651
|
+
nextAncestors.add(value);
|
|
652
|
+
if (Array.isArray(value)) {
|
|
653
|
+
const limit = Math.min(value.length, telemetryMaximumCollectionSize);
|
|
654
|
+
const result = [];
|
|
655
|
+
for (let index = 0; index < limit; index += 1) {
|
|
656
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, String(index));
|
|
657
|
+
result.push(descriptor && "value" in descriptor
|
|
658
|
+
? sanitizeTelemetryValue(descriptor.value, depth + 1, nextAncestors)
|
|
659
|
+
: truncatedValue);
|
|
660
|
+
}
|
|
661
|
+
if (value.length > telemetryMaximumCollectionSize) {
|
|
662
|
+
result[telemetryMaximumCollectionSize - 1] = truncatedValue;
|
|
663
|
+
}
|
|
664
|
+
return Object.freeze(result);
|
|
665
|
+
}
|
|
666
|
+
const result = {};
|
|
667
|
+
const keys = Object.keys(value);
|
|
668
|
+
const limit = Math.min(keys.length, telemetryMaximumCollectionSize);
|
|
669
|
+
for (let index = 0; index < limit; index += 1) {
|
|
670
|
+
const key = keys[index];
|
|
671
|
+
if (key === undefined)
|
|
672
|
+
continue;
|
|
673
|
+
if (sensitiveFieldNames.has(normalizeSensitiveFieldName(key))) {
|
|
674
|
+
result[key] = redactedValue;
|
|
675
|
+
continue;
|
|
676
|
+
}
|
|
677
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
678
|
+
result[key] =
|
|
679
|
+
descriptor && "value" in descriptor
|
|
680
|
+
? sanitizeTelemetryValue(descriptor.value, depth + 1, nextAncestors)
|
|
681
|
+
: truncatedValue;
|
|
682
|
+
}
|
|
683
|
+
if (keys.length > telemetryMaximumCollectionSize) {
|
|
684
|
+
const lastKey = keys[telemetryMaximumCollectionSize - 1];
|
|
685
|
+
if (lastKey !== undefined)
|
|
686
|
+
result[lastKey] = truncatedValue;
|
|
687
|
+
}
|
|
688
|
+
return Object.freeze(result);
|
|
689
|
+
}
|
|
690
|
+
/**
|
|
691
|
+
* Sanitizes a request body while reducing every non-JSON body to a safe summary.
|
|
692
|
+
*
|
|
693
|
+
* @param body - Original or interceptor-produced request body.
|
|
694
|
+
* @returns Frozen redacted JSON value or fixed non-JSON body summary.
|
|
695
|
+
*/
|
|
696
|
+
function sanitizeTelemetryBody(body) {
|
|
697
|
+
if (body !== null &&
|
|
698
|
+
typeof body === "object" &&
|
|
699
|
+
(Array.isArray(body) || isPlainTelemetryObject(body))) {
|
|
700
|
+
return sanitizeTelemetryValue(body);
|
|
701
|
+
}
|
|
702
|
+
return summarizeTelemetryValue(body);
|
|
703
|
+
}
|
|
704
|
+
/**
|
|
705
|
+
* Creates a frozen case-insensitive redacted header snapshot.
|
|
706
|
+
*
|
|
707
|
+
* @param headers - Headers to copy without exposing credentials.
|
|
708
|
+
* @returns Frozen safe header record.
|
|
709
|
+
*/
|
|
710
|
+
function sanitizeTelemetryHeaders(headers) {
|
|
711
|
+
const result = {};
|
|
712
|
+
new Headers(headers).forEach((value, key) => {
|
|
713
|
+
result[key] = sensitiveHeaderNames.has(key.toLowerCase()) ? redactedValue : value;
|
|
714
|
+
});
|
|
715
|
+
return Object.freeze(result);
|
|
716
|
+
}
|
|
717
|
+
/**
|
|
718
|
+
* Removes URL credentials, fragments, and query values for telemetry events.
|
|
719
|
+
*
|
|
720
|
+
* @param value - Absolute request URL to sanitize.
|
|
721
|
+
* @returns Safe URL containing only origin, path, and redacted query keys.
|
|
722
|
+
*/
|
|
723
|
+
function sanitizeTelemetryUrl(value) {
|
|
724
|
+
return sanitizeErrorUrl(value);
|
|
725
|
+
}
|
|
726
|
+
/**
|
|
727
|
+
* Resolves or securely creates the logical request identifier.
|
|
728
|
+
*
|
|
729
|
+
* @param headers - Request headers that receive a generated identifier.
|
|
730
|
+
* @param translate - Translator used for secure-random failures.
|
|
731
|
+
* @returns Caller-supplied or newly generated request identifier.
|
|
732
|
+
* @throws MiaixzSdkError When secure random UUID generation is unavailable.
|
|
733
|
+
*/
|
|
734
|
+
function resolveLogicalRequestId(headers, translate) {
|
|
735
|
+
const inherited = headers.get(miaixzHeaders.requestId);
|
|
736
|
+
if (inherited)
|
|
737
|
+
return inherited;
|
|
738
|
+
if (typeof globalThis.crypto?.randomUUID !== "function") {
|
|
739
|
+
throw new MiaixzSdkError(translate("sdk.error.api.cryptoUnavailable"), {
|
|
740
|
+
code: "API_CRYPTO_UNAVAILABLE",
|
|
741
|
+
});
|
|
742
|
+
}
|
|
743
|
+
const requestId = globalThis.crypto.randomUUID();
|
|
744
|
+
headers.set(miaixzHeaders.requestId, requestId);
|
|
745
|
+
return requestId;
|
|
746
|
+
}
|
|
747
|
+
/**
|
|
748
|
+
* Invokes one primary telemetry hook without allowing it to affect request behavior.
|
|
749
|
+
*
|
|
750
|
+
* @typeParam T - Immutable telemetry event type.
|
|
751
|
+
* @param telemetry - Optional host-provided telemetry hooks.
|
|
752
|
+
* @param hookName - Primary hook identifier used for error reporting.
|
|
753
|
+
* @param hook - Primary hook to invoke.
|
|
754
|
+
* @param event - Frozen event delivered to the hook.
|
|
755
|
+
* @returns Promise settled after the hook and any hook-error observer finish.
|
|
756
|
+
*/
|
|
757
|
+
async function invokeTelemetryHook(telemetry, hookName, hook, event) {
|
|
758
|
+
if (!hook)
|
|
759
|
+
return;
|
|
760
|
+
try {
|
|
761
|
+
await hook(event);
|
|
762
|
+
}
|
|
763
|
+
catch (error) {
|
|
764
|
+
try {
|
|
765
|
+
telemetry?.onHookError?.(error, hookName);
|
|
766
|
+
}
|
|
767
|
+
catch {
|
|
768
|
+
// Telemetry is observational; a secondary hook failure is intentionally ignored.
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
/**
|
|
773
|
+
* Converts an unknown attempt failure to the stable public SDK error model.
|
|
774
|
+
*
|
|
775
|
+
* @param error - Failure caught during the request attempt.
|
|
776
|
+
* @param abortContext - Attempt timeout state.
|
|
777
|
+
* @param callerSignal - Optional caller cancellation signal.
|
|
778
|
+
* @param timeoutMs - Attempt timeout threshold.
|
|
779
|
+
* @param translate - Translator used for normalized errors.
|
|
780
|
+
* @returns Stable SDK error for both telemetry and the request caller.
|
|
781
|
+
*/
|
|
782
|
+
function normalizeAttemptError(error, abortContext, callerSignal, timeoutMs, translate) {
|
|
783
|
+
if (error instanceof MiaixzSdkError)
|
|
784
|
+
return error;
|
|
785
|
+
if (abortContext.timedOut())
|
|
786
|
+
return new MiaixzTimeoutError(timeoutMs, error, translate);
|
|
787
|
+
if (callerSignal?.aborted)
|
|
788
|
+
return new MiaixzAbortError(error, translate);
|
|
789
|
+
return new MiaixzNetworkError(error, translate);
|
|
790
|
+
}
|
|
791
|
+
/**
|
|
792
|
+
* Creates and delivers a frozen request telemetry event.
|
|
793
|
+
*
|
|
794
|
+
* @param telemetry - Optional host-provided telemetry hooks.
|
|
795
|
+
* @param requestId - Logical request identifier.
|
|
796
|
+
* @param method - Request method.
|
|
797
|
+
* @param prepared - Final request produced by interceptors.
|
|
798
|
+
* @param body - Original or interceptor-produced request body.
|
|
799
|
+
* @param startedAt - Attempt start timestamp.
|
|
800
|
+
* @returns Promise settled after telemetry observation completes.
|
|
801
|
+
*/
|
|
802
|
+
async function emitRequestTelemetry(telemetry, requestId, method, prepared, body, startedAt) {
|
|
803
|
+
const event = Object.freeze({
|
|
804
|
+
requestId,
|
|
805
|
+
method,
|
|
806
|
+
url: sanitizeTelemetryUrl(prepared.url),
|
|
807
|
+
attempt: prepared.attempt,
|
|
808
|
+
headers: sanitizeTelemetryHeaders(prepared.init.headers),
|
|
809
|
+
...(body === undefined ? {} : { body: sanitizeTelemetryBody(body) }),
|
|
810
|
+
startedAt,
|
|
811
|
+
});
|
|
812
|
+
await invokeTelemetryHook(telemetry, "request", telemetry?.onRequest, event);
|
|
813
|
+
}
|
|
814
|
+
/**
|
|
815
|
+
* Creates and delivers a frozen response telemetry event.
|
|
816
|
+
*
|
|
817
|
+
* @param telemetry - Optional host-provided telemetry hooks.
|
|
818
|
+
* @param requestId - Logical request identifier.
|
|
819
|
+
* @param method - Request method.
|
|
820
|
+
* @param prepared - Final request produced by interceptors.
|
|
821
|
+
* @param response - Raw Fetch response.
|
|
822
|
+
* @param startedAt - Attempt start timestamp.
|
|
823
|
+
* @returns Promise settled after telemetry observation completes.
|
|
824
|
+
*/
|
|
825
|
+
async function emitResponseTelemetry(telemetry, requestId, method, prepared, response, startedAt) {
|
|
826
|
+
const event = Object.freeze({
|
|
827
|
+
requestId,
|
|
828
|
+
method,
|
|
829
|
+
url: sanitizeTelemetryUrl(prepared.url),
|
|
830
|
+
attempt: prepared.attempt,
|
|
831
|
+
status: response.status,
|
|
832
|
+
durationMs: Math.max(0, Date.now() - startedAt),
|
|
833
|
+
headers: sanitizeTelemetryHeaders(response.headers),
|
|
834
|
+
});
|
|
835
|
+
await invokeTelemetryHook(telemetry, "response", telemetry?.onResponse, event);
|
|
836
|
+
}
|
|
837
|
+
/**
|
|
838
|
+
* Creates and delivers a frozen failure telemetry event.
|
|
839
|
+
*
|
|
840
|
+
* @param telemetry - Optional host-provided telemetry hooks.
|
|
841
|
+
* @param requestId - Logical request identifier.
|
|
842
|
+
* @param method - Request method.
|
|
843
|
+
* @param prepared - Final request produced by interceptors.
|
|
844
|
+
* @param error - Normalized SDK error.
|
|
845
|
+
* @param startedAt - Attempt start timestamp.
|
|
846
|
+
* @returns Promise settled after telemetry observation completes.
|
|
847
|
+
*/
|
|
848
|
+
async function emitErrorTelemetry(telemetry, requestId, method, prepared, error, startedAt) {
|
|
849
|
+
const summary = Object.freeze({ name: error.name, code: error.code, message: error.message });
|
|
850
|
+
const event = Object.freeze({
|
|
851
|
+
requestId,
|
|
852
|
+
method,
|
|
853
|
+
url: sanitizeTelemetryUrl(prepared.url),
|
|
854
|
+
attempt: prepared.attempt,
|
|
855
|
+
durationMs: Math.max(0, Date.now() - startedAt),
|
|
856
|
+
error: summary,
|
|
857
|
+
});
|
|
858
|
+
await invokeTelemetryHook(telemetry, "error", telemetry?.onError, event);
|
|
859
|
+
}
|
|
860
|
+
/**
|
|
861
|
+
* Creates a Fetch-based API client with auth, runtime context, retries, i18n,
|
|
862
|
+
* and automatic `{ errcode, errmsg, data }` validation and data unwrapping.
|
|
863
|
+
*
|
|
864
|
+
* @param options - Endpoint and runtime adapters shared by all requests.
|
|
865
|
+
* @returns A reusable typed client whose response `data` is already unwrapped.
|
|
866
|
+
* @throws MiaixzApiError When the configured base URL violates the endpoint policy.
|
|
867
|
+
* @throws MiaixzSdkError When no Fetch implementation is available.
|
|
868
|
+
* @public
|
|
869
|
+
*/
|
|
870
|
+
export function createApiClient(options) {
|
|
871
|
+
const translate = options.translate ?? miaixzDefaultI18n.t;
|
|
872
|
+
const environment = options.environment ?? "production";
|
|
873
|
+
const baseUrl = normalizeBaseUrl(options.baseUrl, environment, translate);
|
|
874
|
+
const fetchImplementation = options.fetch ?? globalThis.fetch;
|
|
875
|
+
if (!fetchImplementation) {
|
|
876
|
+
throw new MiaixzSdkError(translate("sdk.error.api.fetchUnavailable"), {
|
|
877
|
+
code: "FETCH_UNAVAILABLE",
|
|
878
|
+
});
|
|
879
|
+
}
|
|
880
|
+
const request = async (path, requestOptions = {}) => {
|
|
881
|
+
const method = requestOptions.method ?? "GET";
|
|
882
|
+
const url = appendMiaixzQuery(resolveRequestUrl(baseUrl, path, translate), requestOptions.query);
|
|
883
|
+
const timeoutMs = normalizeTimeout(requestOptions.timeoutMs ?? options.timeoutMs ?? miaixzDefaultRequestTimeoutMs, translate);
|
|
884
|
+
const maximumRetries = normalizeRetry(requestOptions.retry ?? options.retry ?? 0, translate);
|
|
885
|
+
const responseType = requestOptions.responseType ?? "auto";
|
|
886
|
+
const envelopeMode = normalizeEnvelopeMode(responseType, requestOptions.envelope, translate);
|
|
887
|
+
const headers = new Headers(options.headers);
|
|
888
|
+
const providedContextHeaderNames = new Set();
|
|
889
|
+
new Headers(requestOptions.headers).forEach((value, key) => headers.set(key, value));
|
|
890
|
+
const authenticationEnabled = requestOptions.authenticate !== false &&
|
|
891
|
+
(options.authorizationProvider !== undefined ||
|
|
892
|
+
options.tokenProvider !== undefined ||
|
|
893
|
+
options.csrf?.required === true);
|
|
894
|
+
if (requestOptions.authenticate !== false) {
|
|
895
|
+
const authorization = await options.authorizationProvider?.();
|
|
896
|
+
const token = authorization ? undefined : await options.tokenProvider?.();
|
|
897
|
+
if (!headers.has(miaixzHeaders.authorization)) {
|
|
898
|
+
if (authorization)
|
|
899
|
+
headers.set(miaixzHeaders.authorization, authorization);
|
|
900
|
+
else if (token)
|
|
901
|
+
headers.set(miaixzHeaders.authorization, `Bearer ${token}`);
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
if (requestOptions.includeContext !== false) {
|
|
905
|
+
const contextHeaders = await options.contextHeadersProvider?.();
|
|
906
|
+
new Headers(contextHeaders).forEach((value, key) => {
|
|
907
|
+
providedContextHeaderNames.add(key.toLowerCase());
|
|
908
|
+
if (!headers.has(key))
|
|
909
|
+
headers.set(key, value);
|
|
910
|
+
});
|
|
911
|
+
}
|
|
912
|
+
const logicalRequestId = resolveLogicalRequestId(headers, translate);
|
|
913
|
+
const body = serializeBody(requestOptions.body, headers);
|
|
914
|
+
let attempt = 0;
|
|
915
|
+
while (true) {
|
|
916
|
+
const startedAt = Date.now();
|
|
917
|
+
const abortContext = createAbortContext(requestOptions.signal, timeoutMs);
|
|
918
|
+
let prepared = {
|
|
919
|
+
url,
|
|
920
|
+
attempt,
|
|
921
|
+
init: {
|
|
922
|
+
method,
|
|
923
|
+
headers,
|
|
924
|
+
credentials: requestOptions.credentials ?? options.credentials ?? "same-origin",
|
|
925
|
+
signal: abortContext.signal,
|
|
926
|
+
...(body === undefined ? {} : { body }),
|
|
927
|
+
},
|
|
928
|
+
};
|
|
929
|
+
try {
|
|
930
|
+
for (const interceptor of options.requestInterceptors ?? [])
|
|
931
|
+
prepared = await interceptor(prepared);
|
|
932
|
+
const preparedUrl = validatePreparedRequestUrl(baseUrl, prepared.url, translate);
|
|
933
|
+
const preparedHeaders = new Headers(prepared.init.headers);
|
|
934
|
+
const preparedMethod = String(prepared.init.method ?? method).toUpperCase();
|
|
935
|
+
await applyCsrfPolicy(preparedHeaders, preparedMethod, options.csrf, translate);
|
|
936
|
+
preparedHeaders.set(miaixzHeaders.requestId, logicalRequestId);
|
|
937
|
+
const redirect = resolveRedirectMode(preparedHeaders, prepared.init.credentials, authenticationEnabled, providedContextHeaderNames);
|
|
938
|
+
prepared = {
|
|
939
|
+
...prepared,
|
|
940
|
+
url: preparedUrl,
|
|
941
|
+
attempt,
|
|
942
|
+
init: { ...prepared.init, headers: preparedHeaders, redirect },
|
|
943
|
+
};
|
|
944
|
+
await emitRequestTelemetry(options.telemetry, logicalRequestId, method, prepared, prepared.init.body === body ? requestOptions.body : prepared.init.body, startedAt);
|
|
945
|
+
const rawResponse = await fetchImplementation(prepared.url, prepared.init);
|
|
946
|
+
await emitResponseTelemetry(options.telemetry, logicalRequestId, method, prepared, rawResponse, startedAt);
|
|
947
|
+
let parsedBody;
|
|
948
|
+
try {
|
|
949
|
+
parsedBody = await parseBody(rawResponse, rawResponse.ok ? responseType : "auto");
|
|
950
|
+
}
|
|
951
|
+
catch (cause) {
|
|
952
|
+
if (rawResponse.ok) {
|
|
953
|
+
throw createResponseInvalidError(cause, getRequestId(rawResponse.headers), translate);
|
|
954
|
+
}
|
|
955
|
+
parsedBody = undefined;
|
|
956
|
+
}
|
|
957
|
+
if (!rawResponse.ok) {
|
|
958
|
+
const problem = extractProblem(parsedBody, rawResponse);
|
|
959
|
+
const responseRequestId = problem.requestId ?? getRequestId(rawResponse.headers);
|
|
960
|
+
const httpError = new MiaixzApiError(translate("sdk.error.http", { status: rawResponse.status }), {
|
|
961
|
+
status: rawResponse.status,
|
|
962
|
+
method,
|
|
963
|
+
url: sanitizeErrorUrl(prepared.url),
|
|
964
|
+
code: problem.code ?? `HTTP_${rawResponse.status}`,
|
|
965
|
+
...(responseRequestId === undefined ? {} : { requestId: responseRequestId }),
|
|
966
|
+
retryable: retryableStatuses.has(rawResponse.status),
|
|
967
|
+
});
|
|
968
|
+
const shouldRetry = attempt < maximumRetries &&
|
|
969
|
+
retryableMethods.has(method) &&
|
|
970
|
+
retryableStatuses.has(rawResponse.status);
|
|
971
|
+
if (shouldRetry) {
|
|
972
|
+
await emitErrorTelemetry(options.telemetry, logicalRequestId, method, prepared, httpError, startedAt);
|
|
973
|
+
await wait(retryDelay(attempt, rawResponse), requestOptions.signal, translate);
|
|
974
|
+
attempt += 1;
|
|
975
|
+
continue;
|
|
976
|
+
}
|
|
977
|
+
throw httpError;
|
|
978
|
+
}
|
|
979
|
+
const envelope = isMiaixzApiEnvelope(parsedBody) ? parsedBody : undefined;
|
|
980
|
+
const effectiveEnvelopeMode = responseType === "auto" && !expectsJsonEnvelope(rawResponse, responseType)
|
|
981
|
+
? "none"
|
|
982
|
+
: envelopeMode;
|
|
983
|
+
if (effectiveEnvelopeMode === "required" &&
|
|
984
|
+
expectsJsonEnvelope(rawResponse, responseType) &&
|
|
985
|
+
envelope === undefined) {
|
|
986
|
+
const invalidEnvelopeRequestId = getRequestId(rawResponse.headers);
|
|
987
|
+
throw new MiaixzApiError(translate("sdk.error.api.envelopeInvalid"), {
|
|
988
|
+
status: rawResponse.status,
|
|
989
|
+
method,
|
|
990
|
+
url: sanitizeErrorUrl(prepared.url),
|
|
991
|
+
code: "API_ENVELOPE_INVALID",
|
|
992
|
+
...(invalidEnvelopeRequestId === undefined
|
|
993
|
+
? {}
|
|
994
|
+
: { requestId: invalidEnvelopeRequestId }),
|
|
995
|
+
retryable: false,
|
|
996
|
+
});
|
|
997
|
+
}
|
|
998
|
+
if (effectiveEnvelopeMode !== "none" &&
|
|
999
|
+
envelope !== undefined &&
|
|
1000
|
+
!isMiaixzApiSuccess(envelope)) {
|
|
1001
|
+
const businessRequestId = getRequestId(rawResponse.headers);
|
|
1002
|
+
const businessCode = String(envelope.errcode);
|
|
1003
|
+
throw new MiaixzApiError(translate("sdk.error.api.business"), {
|
|
1004
|
+
status: rawResponse.status,
|
|
1005
|
+
method,
|
|
1006
|
+
url: sanitizeErrorUrl(prepared.url),
|
|
1007
|
+
code: businessCode,
|
|
1008
|
+
...(businessRequestId === undefined ? {} : { requestId: businessRequestId }),
|
|
1009
|
+
retryable: false,
|
|
1010
|
+
});
|
|
1011
|
+
}
|
|
1012
|
+
const requestId = getRequestId(rawResponse.headers);
|
|
1013
|
+
let result = {
|
|
1014
|
+
data: effectiveEnvelopeMode !== "none" && envelope !== undefined ? envelope.data : parsedBody,
|
|
1015
|
+
status: rawResponse.status,
|
|
1016
|
+
statusText: rawResponse.statusText,
|
|
1017
|
+
headers: rawResponse.headers,
|
|
1018
|
+
url: rawResponse.url || prepared.url,
|
|
1019
|
+
...(requestId === undefined ? {} : { requestId }),
|
|
1020
|
+
};
|
|
1021
|
+
for (const interceptor of options.responseInterceptors ?? [])
|
|
1022
|
+
result = await interceptor(result);
|
|
1023
|
+
const data = parseResponseData(result.data, requestOptions.parse, result.requestId, translate);
|
|
1024
|
+
return Object.freeze({ ...result, data });
|
|
1025
|
+
}
|
|
1026
|
+
catch (error) {
|
|
1027
|
+
const normalizedError = normalizeAttemptError(error, abortContext, requestOptions.signal, timeoutMs, translate);
|
|
1028
|
+
await emitErrorTelemetry(options.telemetry, logicalRequestId, method, prepared, normalizedError, startedAt);
|
|
1029
|
+
const shouldRetryNetwork = !(error instanceof MiaixzSdkError) &&
|
|
1030
|
+
!abortContext.timedOut() &&
|
|
1031
|
+
!requestOptions.signal?.aborted &&
|
|
1032
|
+
attempt < maximumRetries &&
|
|
1033
|
+
retryableMethods.has(method);
|
|
1034
|
+
if (shouldRetryNetwork) {
|
|
1035
|
+
try {
|
|
1036
|
+
await wait(retryDelay(attempt), requestOptions.signal, translate);
|
|
1037
|
+
}
|
|
1038
|
+
catch (delayError) {
|
|
1039
|
+
const normalizedDelayError = delayError instanceof MiaixzSdkError
|
|
1040
|
+
? delayError
|
|
1041
|
+
: new MiaixzAbortError(delayError, translate);
|
|
1042
|
+
await emitErrorTelemetry(options.telemetry, logicalRequestId, method, prepared, normalizedDelayError, startedAt);
|
|
1043
|
+
throw normalizedDelayError;
|
|
1044
|
+
}
|
|
1045
|
+
attempt += 1;
|
|
1046
|
+
continue;
|
|
1047
|
+
}
|
|
1048
|
+
throw normalizedError;
|
|
1049
|
+
}
|
|
1050
|
+
finally {
|
|
1051
|
+
abortContext.dispose();
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
1054
|
+
};
|
|
1055
|
+
return {
|
|
1056
|
+
baseUrl,
|
|
1057
|
+
request,
|
|
1058
|
+
get: (path, requestOptions) => request(path, { ...requestOptions, method: "GET" }),
|
|
1059
|
+
post: (path, body, requestOptions) => request(path, { ...requestOptions, method: "POST", ...(body === undefined ? {} : { body }) }),
|
|
1060
|
+
put: (path, body, requestOptions) => request(path, { ...requestOptions, method: "PUT", ...(body === undefined ? {} : { body }) }),
|
|
1061
|
+
patch: (path, body, requestOptions) => request(path, {
|
|
1062
|
+
...requestOptions,
|
|
1063
|
+
method: "PATCH",
|
|
1064
|
+
...(body === undefined ? {} : { body }),
|
|
1065
|
+
}),
|
|
1066
|
+
delete: (path, requestOptions) => request(path, { ...requestOptions, method: "DELETE" }),
|
|
1067
|
+
};
|
|
1068
|
+
}
|
|
1069
|
+
//# sourceMappingURL=client.js.map
|