@webstudio-is/http-client 0.271.0 → 0.273.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/lib/index.js +144 -15
- package/lib/types/index.d.ts +23 -22
- package/package.json +3 -2
package/lib/index.js
CHANGED
|
@@ -1,14 +1,56 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
2
|
import { createTRPCUntypedClient, httpBatchLink } from "@trpc/client";
|
|
3
|
+
import { Upload } from "tus-js-client";
|
|
3
4
|
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
isAssetFileDataString,
|
|
10
|
-
bundleVersion
|
|
5
|
+
importProjectBundleResult,
|
|
6
|
+
publishedProjectBundle,
|
|
7
|
+
maxProjectBundleSize,
|
|
8
|
+
stagedUploadPath,
|
|
9
|
+
stagedUploadProjectIdHeader
|
|
11
10
|
} from "@webstudio-is/protocol";
|
|
11
|
+
import { getBundleVersion, bundleVersion } from "@webstudio-is/protocol";
|
|
12
|
+
var maxResponsePreviewLength = 500;
|
|
13
|
+
var getResponsePreview = async (response) => {
|
|
14
|
+
try {
|
|
15
|
+
const text = await response.clone().text();
|
|
16
|
+
return text.replace(/\s+/g, " ").trim().slice(0, maxResponsePreviewLength);
|
|
17
|
+
} catch {
|
|
18
|
+
return "";
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
var createApiResponseErrorMessage = async (request, response) => {
|
|
22
|
+
const contentType = response.headers.get("content-type") ?? "unknown";
|
|
23
|
+
const status = `${response.status} ${response.statusText}`.trim();
|
|
24
|
+
const url = typeof request === "string" || request instanceof URL ? request.toString() : request.url;
|
|
25
|
+
const preview = await getResponsePreview(response);
|
|
26
|
+
const hint = response.status === 413 ? "The request may be too large for the API." : void 0;
|
|
27
|
+
return [
|
|
28
|
+
`API returned ${contentType} instead of JSON from ${url}.`,
|
|
29
|
+
`HTTP status: ${status}.`,
|
|
30
|
+
preview === "" ? void 0 : `Response preview: ${preview}`,
|
|
31
|
+
hint
|
|
32
|
+
].filter(Boolean).join("\n");
|
|
33
|
+
};
|
|
34
|
+
var fetchJsonResponse = async (request, init) => {
|
|
35
|
+
const response = await fetch(request, init);
|
|
36
|
+
const contentType = response.headers.get("content-type");
|
|
37
|
+
if (contentType?.includes("json") !== true) {
|
|
38
|
+
throw new Error(await createApiResponseErrorMessage(request, response));
|
|
39
|
+
}
|
|
40
|
+
return response;
|
|
41
|
+
};
|
|
42
|
+
var createHeaders = (headers) => {
|
|
43
|
+
return new Headers(createHeadersObject(headers));
|
|
44
|
+
};
|
|
45
|
+
var createHeadersObject = (headers) => {
|
|
46
|
+
const result = {};
|
|
47
|
+
for (const [name, value] of Object.entries(headers)) {
|
|
48
|
+
if (value !== void 0) {
|
|
49
|
+
result[name] = value;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return result;
|
|
53
|
+
};
|
|
12
54
|
var createTrpcClient = (origin, headers) => {
|
|
13
55
|
const { sourceOrigin } = parseBuilderUrl(origin);
|
|
14
56
|
const url = new URL("/trpc", sourceOrigin);
|
|
@@ -16,7 +58,8 @@ var createTrpcClient = (origin, headers) => {
|
|
|
16
58
|
links: [
|
|
17
59
|
httpBatchLink({
|
|
18
60
|
url: url.href,
|
|
19
|
-
headers
|
|
61
|
+
headers,
|
|
62
|
+
fetch: fetchJsonResponse
|
|
20
63
|
})
|
|
21
64
|
]
|
|
22
65
|
});
|
|
@@ -25,6 +68,50 @@ var createAuthTrpcClient = (params) => createTrpcClient(params.origin, {
|
|
|
25
68
|
...params.headers,
|
|
26
69
|
"x-auth-token": params.authToken
|
|
27
70
|
});
|
|
71
|
+
var stagedUploadChunkSize = 3 * 1024 * 1024;
|
|
72
|
+
var formatMebibytes = (bytes) => `${Math.round(bytes / 1024 / 1024)} MiB`;
|
|
73
|
+
var getAssetUploadUrl = ({
|
|
74
|
+
asset,
|
|
75
|
+
origin,
|
|
76
|
+
projectId
|
|
77
|
+
}) => {
|
|
78
|
+
const { sourceOrigin } = parseBuilderUrl(origin);
|
|
79
|
+
const url = new URL(
|
|
80
|
+
`/rest/assets/${encodeURIComponent(asset.name)}`,
|
|
81
|
+
sourceOrigin
|
|
82
|
+
);
|
|
83
|
+
url.searchParams.set("projectId", projectId);
|
|
84
|
+
url.searchParams.set("type", asset.type);
|
|
85
|
+
if (asset.type === "image") {
|
|
86
|
+
url.searchParams.set("width", String(asset.meta.width));
|
|
87
|
+
url.searchParams.set("height", String(asset.meta.height));
|
|
88
|
+
url.searchParams.set("format", asset.format);
|
|
89
|
+
}
|
|
90
|
+
return url;
|
|
91
|
+
};
|
|
92
|
+
var uploadAsset = async (params) => {
|
|
93
|
+
const { authToken, headers, origin, projectId, upload } = params;
|
|
94
|
+
const response = await fetchJsonResponse(
|
|
95
|
+
getAssetUploadUrl({
|
|
96
|
+
asset: upload.asset,
|
|
97
|
+
origin,
|
|
98
|
+
projectId
|
|
99
|
+
}),
|
|
100
|
+
{
|
|
101
|
+
method: "POST",
|
|
102
|
+
body: upload.data,
|
|
103
|
+
headers: createHeaders({
|
|
104
|
+
...headers,
|
|
105
|
+
"x-auth-token": authToken,
|
|
106
|
+
"content-type": "application/octet-stream"
|
|
107
|
+
})
|
|
108
|
+
}
|
|
109
|
+
);
|
|
110
|
+
const result = await response.json();
|
|
111
|
+
if (typeof result === "object" && result !== null && "errors" in result && typeof result.errors === "string") {
|
|
112
|
+
throw new Error(result.errors);
|
|
113
|
+
}
|
|
114
|
+
};
|
|
28
115
|
var loadProjectBundleByBuildId = async (params) => {
|
|
29
116
|
const headers = "serviceToken" in params ? { Authorization: params.serviceToken } : { "x-auth-token": params.authToken };
|
|
30
117
|
const data = await createTrpcClient(params.origin, {
|
|
@@ -33,7 +120,7 @@ var loadProjectBundleByBuildId = async (params) => {
|
|
|
33
120
|
}).query("build.loadProjectBundleByBuildId", {
|
|
34
121
|
buildId: params.buildId
|
|
35
122
|
});
|
|
36
|
-
return
|
|
123
|
+
return publishedProjectBundle.parse(data);
|
|
37
124
|
};
|
|
38
125
|
var loadProjectBundleByProjectId = async (params) => {
|
|
39
126
|
const data = await createAuthTrpcClient(params).query(
|
|
@@ -42,7 +129,7 @@ var loadProjectBundleByProjectId = async (params) => {
|
|
|
42
129
|
projectId: params.projectId
|
|
43
130
|
}
|
|
44
131
|
);
|
|
45
|
-
return
|
|
132
|
+
return publishedProjectBundle.parse(data);
|
|
46
133
|
};
|
|
47
134
|
var checkProjectBuildPermission = async (params) => {
|
|
48
135
|
await createAuthTrpcClient(params).query(
|
|
@@ -52,17 +139,59 @@ var checkProjectBuildPermission = async (params) => {
|
|
|
52
139
|
}
|
|
53
140
|
);
|
|
54
141
|
};
|
|
142
|
+
var getUploadIdFromUrl = (uploadUrl) => {
|
|
143
|
+
if (uploadUrl === null) {
|
|
144
|
+
throw new Error("Project bundle upload did not return an upload URL.");
|
|
145
|
+
}
|
|
146
|
+
const { pathname } = new URL(uploadUrl);
|
|
147
|
+
const uploadId = pathname.split("/").filter(Boolean).at(-1);
|
|
148
|
+
if (uploadId === void 0) {
|
|
149
|
+
throw new Error("Project bundle upload did not return an upload id.");
|
|
150
|
+
}
|
|
151
|
+
return decodeURIComponent(uploadId);
|
|
152
|
+
};
|
|
153
|
+
var uploadProjectBundleData = async (params) => {
|
|
154
|
+
const { sourceOrigin } = parseBuilderUrl(params.origin);
|
|
155
|
+
const endpoint = new URL(stagedUploadPath, sourceOrigin);
|
|
156
|
+
const data = JSON.stringify(params.data);
|
|
157
|
+
if (new TextEncoder().encode(data).byteLength > maxProjectBundleSize) {
|
|
158
|
+
throw new Error(
|
|
159
|
+
`Project bundle is too large to import. Maximum size is ${formatMebibytes(maxProjectBundleSize)}.`
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
const file = typeof Buffer === "undefined" ? new Blob([data], { type: "application/json" }) : Buffer.from(data);
|
|
163
|
+
return await new Promise((resolve, reject) => {
|
|
164
|
+
const upload = new Upload(file, {
|
|
165
|
+
endpoint: endpoint.href,
|
|
166
|
+
chunkSize: stagedUploadChunkSize,
|
|
167
|
+
retryDelays: [0, 1e3],
|
|
168
|
+
removeFingerprintOnSuccess: true,
|
|
169
|
+
storeFingerprintForResuming: false,
|
|
170
|
+
headers: createHeadersObject({
|
|
171
|
+
...params.headers,
|
|
172
|
+
"x-auth-token": params.authToken,
|
|
173
|
+
[stagedUploadProjectIdHeader]: params.projectId
|
|
174
|
+
}),
|
|
175
|
+
metadata: {
|
|
176
|
+
projectId: params.projectId
|
|
177
|
+
},
|
|
178
|
+
onError: reject,
|
|
179
|
+
onSuccess: () => resolve(getUploadIdFromUrl(upload.url))
|
|
180
|
+
});
|
|
181
|
+
upload.start();
|
|
182
|
+
});
|
|
183
|
+
};
|
|
55
184
|
var importProjectBundle = async (params) => {
|
|
185
|
+
const uploadId = await uploadProjectBundleData(params);
|
|
56
186
|
const result = await createAuthTrpcClient(params).mutation(
|
|
57
187
|
"build.importProjectBundle",
|
|
58
188
|
{
|
|
59
189
|
projectId: params.projectId,
|
|
60
|
-
|
|
61
|
-
assetFiles: params.assetFiles,
|
|
190
|
+
uploadId,
|
|
62
191
|
ignoreVersionCheck: params.ignoreVersionCheck
|
|
63
192
|
}
|
|
64
193
|
);
|
|
65
|
-
return
|
|
194
|
+
return importProjectBundleResult.parse(result);
|
|
66
195
|
};
|
|
67
196
|
var buildProjectDomainPrefix = "p-";
|
|
68
197
|
var parseBuilderUrl = (urlStr) => {
|
|
@@ -99,8 +228,8 @@ export {
|
|
|
99
228
|
checkProjectBuildPermission,
|
|
100
229
|
getBundleVersion,
|
|
101
230
|
importProjectBundle,
|
|
102
|
-
isAssetFileDataString,
|
|
103
231
|
loadProjectBundleByBuildId,
|
|
104
232
|
loadProjectBundleByProjectId,
|
|
105
|
-
parseBuilderUrl
|
|
233
|
+
parseBuilderUrl,
|
|
234
|
+
uploadAsset
|
|
106
235
|
};
|
package/lib/types/index.d.ts
CHANGED
|
@@ -1,35 +1,36 @@
|
|
|
1
|
-
import { type
|
|
2
|
-
export { getBundleVersion,
|
|
3
|
-
export type {
|
|
1
|
+
import { type ImportProjectBundleResult, type PublishedProjectBundle } from "@webstudio-is/protocol";
|
|
2
|
+
export { getBundleVersion, bundleVersion } from "@webstudio-is/protocol";
|
|
3
|
+
export type { PublishedProjectBundle, ProjectBundle, } from "@webstudio-is/protocol";
|
|
4
|
+
type RequestHeaders = Record<string, string | undefined>;
|
|
5
|
+
type AuthProjectParams = {
|
|
6
|
+
projectId: string;
|
|
7
|
+
origin: string;
|
|
8
|
+
authToken: string;
|
|
9
|
+
headers?: RequestHeaders;
|
|
10
|
+
};
|
|
11
|
+
type Asset = PublishedProjectBundle["assets"][number];
|
|
12
|
+
type BinaryAssetData = Blob | ArrayBuffer | ArrayBufferView<ArrayBuffer>;
|
|
13
|
+
type AssetUpload = {
|
|
14
|
+
asset: Asset;
|
|
15
|
+
data: BinaryAssetData;
|
|
16
|
+
};
|
|
17
|
+
export declare const uploadAsset: (params: AuthProjectParams & {
|
|
18
|
+
upload: AssetUpload;
|
|
19
|
+
}) => Promise<void>;
|
|
4
20
|
export declare const loadProjectBundleByBuildId: (params: {
|
|
5
21
|
buildId: string;
|
|
6
22
|
origin: string;
|
|
7
|
-
headers?:
|
|
23
|
+
headers?: RequestHeaders;
|
|
8
24
|
} & ({
|
|
9
25
|
serviceToken: string;
|
|
10
26
|
} | {
|
|
11
27
|
authToken: string;
|
|
12
28
|
})) => Promise<PublishedProjectBundle>;
|
|
13
|
-
export declare const loadProjectBundleByProjectId: (params:
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
authToken: string;
|
|
17
|
-
headers?: Record<string, string | undefined>;
|
|
18
|
-
}) => Promise<PublishedProjectBundle>;
|
|
19
|
-
export declare const checkProjectBuildPermission: (params: {
|
|
20
|
-
projectId: string;
|
|
21
|
-
origin: string;
|
|
22
|
-
authToken: string;
|
|
23
|
-
headers?: Record<string, string | undefined>;
|
|
24
|
-
}) => Promise<void>;
|
|
25
|
-
export declare const importProjectBundle: (params: {
|
|
26
|
-
projectId: string;
|
|
27
|
-
origin: string;
|
|
28
|
-
authToken: string;
|
|
29
|
+
export declare const loadProjectBundleByProjectId: (params: AuthProjectParams) => Promise<PublishedProjectBundle>;
|
|
30
|
+
export declare const checkProjectBuildPermission: (params: AuthProjectParams) => Promise<void>;
|
|
31
|
+
export declare const importProjectBundle: (params: AuthProjectParams & {
|
|
29
32
|
data: PublishedProjectBundle;
|
|
30
|
-
assetFiles?: AssetFileData[];
|
|
31
33
|
ignoreVersionCheck?: boolean;
|
|
32
|
-
headers?: Record<string, string | undefined>;
|
|
33
34
|
}) => Promise<ImportProjectBundleResult>;
|
|
34
35
|
export declare const parseBuilderUrl: (urlStr: string) => {
|
|
35
36
|
projectId: undefined;
|
package/package.json
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webstudio-is/http-client",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.273.0",
|
|
4
4
|
"description": "Webstudio HTTP Client",
|
|
5
5
|
"author": "Webstudio <github@webstudio.is>",
|
|
6
6
|
"homepage": "https://webstudio.is",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"dependencies": {
|
|
9
9
|
"@trpc/client": "^10.45.2",
|
|
10
|
-
"
|
|
10
|
+
"tus-js-client": "^4.3.1",
|
|
11
|
+
"@webstudio-is/protocol": "0.273.0"
|
|
11
12
|
},
|
|
12
13
|
"devDependencies": {
|
|
13
14
|
"vitest": "^3.1.2",
|