@hot-updater/standalone 1.2.0-rc.7 → 1.2.0-rc.9
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/dist/index.cjs +58 -11
- package/dist/index.d.cts +23 -4
- package/dist/index.d.mts +23 -4
- package/dist/index.mjs +59 -12
- package/package.json +4 -9
package/dist/index.cjs
CHANGED
|
@@ -12,11 +12,13 @@ var StandaloneDatabaseError = class extends Error {
|
|
|
12
12
|
}
|
|
13
13
|
};
|
|
14
14
|
const isRecord$1 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
15
|
+
/** `headers` with each lazy value resolved. */
|
|
16
|
+
const resolveHeaders = async (headers = {}) => Object.fromEntries(await Promise.all(Object.entries(headers).map(async ([name, value]) => [name, await (0, _hot_updater_plugin_core.resolveLazy)(value)])));
|
|
15
17
|
const createStandaloneHttp = (config) => {
|
|
16
18
|
const buildUrl = (path) => `${config.baseUrl.replace(/\/+$/, "")}${path}`;
|
|
17
|
-
const headers = (routeHeaders) => ({
|
|
19
|
+
const headers = async (routeHeaders) => ({
|
|
18
20
|
"Content-Type": "application/json",
|
|
19
|
-
...config.commonHeaders,
|
|
21
|
+
...await resolveHeaders(config.commonHeaders),
|
|
20
22
|
...routeHeaders
|
|
21
23
|
});
|
|
22
24
|
const requestFailed = async (response) => {
|
|
@@ -108,7 +110,7 @@ const createStandaloneCoreApi = (config) => {
|
|
|
108
110
|
const http = createStandaloneHttp(config);
|
|
109
111
|
let checked;
|
|
110
112
|
const checkProtocol = async () => {
|
|
111
|
-
const response = await fetch(http.buildUrl("/version"), { headers: http.headers({ "Cache-Control": "no-cache" }) });
|
|
113
|
+
const response = await fetch(http.buildUrl("/version"), { headers: await http.headers({ "Cache-Control": "no-cache" }) });
|
|
112
114
|
const body = response.status === 404 ? {} : await http.parseJson(response);
|
|
113
115
|
const protocol = isRecord(body) ? body.adminProtocol : void 0;
|
|
114
116
|
if (typeof protocol !== "number" || protocol < 2) throw new StandaloneDatabaseError("request-failed", `The server at ${config.baseUrl} does not report Hot Updater admin API protocol 2 at /version. Set baseUrl to the path where the server mounts handlers.admin, such as https://example.com/hot-updater/admin.`, response.status);
|
|
@@ -125,7 +127,7 @@ const createStandaloneCoreApi = (config) => {
|
|
|
125
127
|
for (const [key, value] of Object.entries(query)) url.searchParams.set(key, value);
|
|
126
128
|
const response = await fetch(url, {
|
|
127
129
|
method,
|
|
128
|
-
headers: http.headers(method === "GET" ? { "Cache-Control": "no-cache" } : void 0),
|
|
130
|
+
headers: await http.headers(method === "GET" ? { "Cache-Control": "no-cache" } : void 0),
|
|
129
131
|
...body === void 0 ? {} : { body: JSON.stringify(body) }
|
|
130
132
|
});
|
|
131
133
|
if (response.status === 204) return null;
|
|
@@ -252,8 +254,8 @@ const standaloneRepository = (config) => {
|
|
|
252
254
|
return Object.freeze({
|
|
253
255
|
name: "standalone-repository",
|
|
254
256
|
core: createStandaloneCoreApi(config),
|
|
255
|
-
fetchAdmin: (path, init = {}) => {
|
|
256
|
-
const headers = new Headers(http.headers({ "Cache-Control": "no-cache" }));
|
|
257
|
+
fetchAdmin: async (path, init = {}) => {
|
|
258
|
+
const headers = new Headers(await http.headers({ "Cache-Control": "no-cache" }));
|
|
257
259
|
new Headers(init.headers).forEach((value, name) => {
|
|
258
260
|
headers.set(name, value);
|
|
259
261
|
});
|
|
@@ -270,8 +272,12 @@ const defaultRoutes = {
|
|
|
270
272
|
put: () => ({ path: "/upload" }),
|
|
271
273
|
get: () => ({ path: "/get" }),
|
|
272
274
|
exists: () => ({ path: "/exists" }),
|
|
273
|
-
delete: () => ({ path: "/delete" })
|
|
275
|
+
delete: () => ({ path: "/delete" }),
|
|
276
|
+
listObjects: () => ({ path: "/list" }),
|
|
277
|
+
deleteObjects: () => ({ path: "/delete-objects" })
|
|
274
278
|
};
|
|
279
|
+
/** Keys sent in one `deleteObjects` request. */
|
|
280
|
+
const DELETE_BATCH_SIZE = 1e3;
|
|
275
281
|
const createRoute = (defaultRoute, customRoute) => ({
|
|
276
282
|
path: customRoute?.path ?? defaultRoute.path,
|
|
277
283
|
headers: {
|
|
@@ -280,8 +286,8 @@ const createRoute = (defaultRoute, customRoute) => ({
|
|
|
280
286
|
}
|
|
281
287
|
});
|
|
282
288
|
const standaloneStorage = (config) => {
|
|
283
|
-
const getHeaders = (routeHeaders) => ({
|
|
284
|
-
...config.commonHeaders,
|
|
289
|
+
const getHeaders = async (routeHeaders) => ({
|
|
290
|
+
...await resolveHeaders(config.commonHeaders),
|
|
285
291
|
...routeHeaders
|
|
286
292
|
});
|
|
287
293
|
const resolveRoute = (name, args) => {
|
|
@@ -289,6 +295,14 @@ const standaloneStorage = (config) => {
|
|
|
289
295
|
const custom = config.routes?.[name];
|
|
290
296
|
return createRoute(fallback(...args), custom?.(...args));
|
|
291
297
|
};
|
|
298
|
+
const postJson = async (path, headers, body) => fetch(`${config.baseUrl}${path}`, {
|
|
299
|
+
method: "POST",
|
|
300
|
+
headers: {
|
|
301
|
+
"content-type": "application/json",
|
|
302
|
+
...await getHeaders(headers)
|
|
303
|
+
},
|
|
304
|
+
body: JSON.stringify(body)
|
|
305
|
+
});
|
|
292
306
|
const requestStorageUri = async (routeName, storageUri) => {
|
|
293
307
|
(0, _hot_updater_plugin_core.parseStorageUri)(storageUri, config.protocol);
|
|
294
308
|
const { path, headers } = resolveRoute(routeName, [{ storageUri }]);
|
|
@@ -296,7 +310,7 @@ const standaloneStorage = (config) => {
|
|
|
296
310
|
method: routeName === "delete" ? "DELETE" : "POST",
|
|
297
311
|
headers: {
|
|
298
312
|
"content-type": "application/json",
|
|
299
|
-
...getHeaders(headers)
|
|
313
|
+
...await getHeaders(headers)
|
|
300
314
|
},
|
|
301
315
|
body: JSON.stringify({ storageUri })
|
|
302
316
|
});
|
|
@@ -319,7 +333,7 @@ const standaloneStorage = (config) => {
|
|
|
319
333
|
formData.append("key", key);
|
|
320
334
|
const response = await fetch(`${config.baseUrl}${path}`, {
|
|
321
335
|
method: "POST",
|
|
322
|
-
headers: getHeaders(headers),
|
|
336
|
+
headers: await getHeaders(headers),
|
|
323
337
|
body: formData
|
|
324
338
|
});
|
|
325
339
|
if (!response.ok) throw new Error(`Failed to upload bundle: ${response.statusText}`);
|
|
@@ -340,6 +354,39 @@ const standaloneStorage = (config) => {
|
|
|
340
354
|
async delete({ storageUri }) {
|
|
341
355
|
await requestStorageUri("delete", storageUri);
|
|
342
356
|
return { deleted: true };
|
|
357
|
+
},
|
|
358
|
+
async listObjects(prefix) {
|
|
359
|
+
const { path, headers } = resolveRoute("listObjects", [{ prefix }]);
|
|
360
|
+
const objects = [];
|
|
361
|
+
let cursor;
|
|
362
|
+
do {
|
|
363
|
+
const response = await postJson(path, headers, {
|
|
364
|
+
prefix,
|
|
365
|
+
cursor
|
|
366
|
+
});
|
|
367
|
+
if (response.status === 404) throw new Error(`The storage service has no ${path} route, which storage prune needs.`);
|
|
368
|
+
if (!response.ok) throw new Error(`Failed to list storage objects: ${response.statusText}`);
|
|
369
|
+
const page = await response.json();
|
|
370
|
+
for (const object of page.objects) {
|
|
371
|
+
(0, _hot_updater_plugin_core.parseStorageUri)(object.storageUri, config.protocol);
|
|
372
|
+
objects.push({
|
|
373
|
+
key: object.key,
|
|
374
|
+
storageUri: object.storageUri,
|
|
375
|
+
size: object.size,
|
|
376
|
+
...object.lastModifiedAt === void 0 ? {} : { lastModifiedAt: new Date(object.lastModifiedAt) }
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
cursor = page.cursor;
|
|
380
|
+
} while (cursor !== void 0);
|
|
381
|
+
return objects;
|
|
382
|
+
},
|
|
383
|
+
async deleteObjects(keys) {
|
|
384
|
+
for (let offset = 0; offset < keys.length; offset += DELETE_BATCH_SIZE) {
|
|
385
|
+
const batch = keys.slice(offset, offset + DELETE_BATCH_SIZE);
|
|
386
|
+
const { path, headers } = resolveRoute("deleteObjects", [{ keys: batch }]);
|
|
387
|
+
const response = await postJson(path, headers, { keys: batch });
|
|
388
|
+
if (!response.ok) throw new Error(`Failed to delete storage objects: ${response.statusText}`);
|
|
389
|
+
}
|
|
343
390
|
}
|
|
344
391
|
});
|
|
345
392
|
};
|
package/dist/index.d.cts
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
|
-
import { HotUpdaterCoreApi, RemoteDatabase, StorageDeleteInput, StorageExistsInput, StorageGetInput, StoragePutInput } from "@hot-updater/plugin-core";
|
|
1
|
+
import { HotUpdaterCoreApi, Lazy, RemoteDatabase, StorageDeleteInput, StorageExistsInput, StorageGetInput, StorageObject, StoragePutInput } from "@hot-updater/plugin-core";
|
|
2
2
|
//#region src/standaloneRoutes.d.ts
|
|
3
3
|
interface StandaloneRepositoryConfig {
|
|
4
4
|
/** Base URL of the Hot Updater admin handler. */
|
|
5
5
|
readonly baseUrl: string;
|
|
6
|
-
/**
|
|
7
|
-
|
|
6
|
+
/**
|
|
7
|
+
* Headers sent with every admin request, such as its authorization. A
|
|
8
|
+
* value may be a function that returns it, such as `secret("<name>")` in
|
|
9
|
+
* `hot-updater.config.ts`, called on the first request.
|
|
10
|
+
*/
|
|
11
|
+
readonly commonHeaders?: Readonly<Record<string, Lazy<string>>>;
|
|
8
12
|
}
|
|
9
13
|
//#endregion
|
|
10
14
|
//#region src/standaloneCore.d.ts
|
|
@@ -48,12 +52,25 @@ interface StorageRoutes {
|
|
|
48
52
|
get: (input: StorageGetInput) => RouteConfig;
|
|
49
53
|
exists: (input: StorageExistsInput) => RouteConfig;
|
|
50
54
|
delete: (input: StorageDeleteInput) => RouteConfig;
|
|
55
|
+
/** Lists objects for `hot-updater storage prune`. */
|
|
56
|
+
listObjects: (input: {
|
|
57
|
+
readonly prefix?: string;
|
|
58
|
+
}) => RouteConfig;
|
|
59
|
+
/** Deletes exact keys for `hot-updater storage prune --yes`. */
|
|
60
|
+
deleteObjects: (input: {
|
|
61
|
+
readonly keys: readonly string[];
|
|
62
|
+
}) => RouteConfig;
|
|
51
63
|
}
|
|
52
64
|
interface StandaloneStorageConfig {
|
|
53
65
|
baseUrl: string;
|
|
54
66
|
/** Storage URI protocol owned by the remote implementation. */
|
|
55
67
|
protocol: string;
|
|
56
|
-
|
|
68
|
+
/**
|
|
69
|
+
* Headers sent with every request. A value may be a function that returns
|
|
70
|
+
* it, such as `secret("<name>")` in `hot-updater.config.ts`, called on
|
|
71
|
+
* the first request.
|
|
72
|
+
*/
|
|
73
|
+
commonHeaders?: Record<string, Lazy<string>>;
|
|
57
74
|
routes?: Partial<StorageRoutes>;
|
|
58
75
|
}
|
|
59
76
|
declare const standaloneStorage: (config: StandaloneStorageConfig) => {
|
|
@@ -71,6 +88,8 @@ declare const standaloneStorage: (config: StandaloneStorageConfig) => {
|
|
|
71
88
|
readonly delete: ({ storageUri }: StorageDeleteInput) => Promise<{
|
|
72
89
|
deleted: true;
|
|
73
90
|
}>;
|
|
91
|
+
readonly listObjects: (prefix?: string) => Promise<StorageObject[]>;
|
|
92
|
+
readonly deleteObjects: (keys: readonly string[]) => Promise<void>;
|
|
74
93
|
};
|
|
75
94
|
//#endregion
|
|
76
95
|
export { RouteConfig, STANDALONE_ADMIN_PROTOCOL, StandaloneDatabaseError, StandaloneRepository, type StandaloneRepositoryConfig, StandaloneStorageConfig, StorageRoutes, createStandaloneCoreApi, standaloneRepository, standaloneStorage };
|
package/dist/index.d.mts
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
|
-
import { HotUpdaterCoreApi, RemoteDatabase, StorageDeleteInput, StorageExistsInput, StorageGetInput, StoragePutInput } from "@hot-updater/plugin-core";
|
|
1
|
+
import { HotUpdaterCoreApi, Lazy, RemoteDatabase, StorageDeleteInput, StorageExistsInput, StorageGetInput, StorageObject, StoragePutInput } from "@hot-updater/plugin-core";
|
|
2
2
|
//#region src/standaloneRoutes.d.ts
|
|
3
3
|
interface StandaloneRepositoryConfig {
|
|
4
4
|
/** Base URL of the Hot Updater admin handler. */
|
|
5
5
|
readonly baseUrl: string;
|
|
6
|
-
/**
|
|
7
|
-
|
|
6
|
+
/**
|
|
7
|
+
* Headers sent with every admin request, such as its authorization. A
|
|
8
|
+
* value may be a function that returns it, such as `secret("<name>")` in
|
|
9
|
+
* `hot-updater.config.ts`, called on the first request.
|
|
10
|
+
*/
|
|
11
|
+
readonly commonHeaders?: Readonly<Record<string, Lazy<string>>>;
|
|
8
12
|
}
|
|
9
13
|
//#endregion
|
|
10
14
|
//#region src/standaloneCore.d.ts
|
|
@@ -48,12 +52,25 @@ interface StorageRoutes {
|
|
|
48
52
|
get: (input: StorageGetInput) => RouteConfig;
|
|
49
53
|
exists: (input: StorageExistsInput) => RouteConfig;
|
|
50
54
|
delete: (input: StorageDeleteInput) => RouteConfig;
|
|
55
|
+
/** Lists objects for `hot-updater storage prune`. */
|
|
56
|
+
listObjects: (input: {
|
|
57
|
+
readonly prefix?: string;
|
|
58
|
+
}) => RouteConfig;
|
|
59
|
+
/** Deletes exact keys for `hot-updater storage prune --yes`. */
|
|
60
|
+
deleteObjects: (input: {
|
|
61
|
+
readonly keys: readonly string[];
|
|
62
|
+
}) => RouteConfig;
|
|
51
63
|
}
|
|
52
64
|
interface StandaloneStorageConfig {
|
|
53
65
|
baseUrl: string;
|
|
54
66
|
/** Storage URI protocol owned by the remote implementation. */
|
|
55
67
|
protocol: string;
|
|
56
|
-
|
|
68
|
+
/**
|
|
69
|
+
* Headers sent with every request. A value may be a function that returns
|
|
70
|
+
* it, such as `secret("<name>")` in `hot-updater.config.ts`, called on
|
|
71
|
+
* the first request.
|
|
72
|
+
*/
|
|
73
|
+
commonHeaders?: Record<string, Lazy<string>>;
|
|
57
74
|
routes?: Partial<StorageRoutes>;
|
|
58
75
|
}
|
|
59
76
|
declare const standaloneStorage: (config: StandaloneStorageConfig) => {
|
|
@@ -71,6 +88,8 @@ declare const standaloneStorage: (config: StandaloneStorageConfig) => {
|
|
|
71
88
|
readonly delete: ({ storageUri }: StorageDeleteInput) => Promise<{
|
|
72
89
|
deleted: true;
|
|
73
90
|
}>;
|
|
91
|
+
readonly listObjects: (prefix?: string) => Promise<StorageObject[]>;
|
|
92
|
+
readonly deleteObjects: (keys: readonly string[]) => Promise<void>;
|
|
74
93
|
};
|
|
75
94
|
//#endregion
|
|
76
95
|
export { RouteConfig, STANDALONE_ADMIN_PROTOCOL, StandaloneDatabaseError, StandaloneRepository, type StandaloneRepositoryConfig, StandaloneStorageConfig, StorageRoutes, createStandaloneCoreApi, standaloneRepository, standaloneStorage };
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DatabaseBundleNotFoundError, ReleaseCatalogMutationError, ReleaseManagementError, createStorageAdapter, parseStorageUri } from "@hot-updater/plugin-core";
|
|
1
|
+
import { DatabaseBundleNotFoundError, ReleaseCatalogMutationError, ReleaseManagementError, createStorageAdapter, parseStorageUri, resolveLazy } from "@hot-updater/plugin-core";
|
|
2
2
|
//#region src/standaloneHttp.ts
|
|
3
3
|
var StandaloneDatabaseError = class extends Error {
|
|
4
4
|
code;
|
|
@@ -11,11 +11,13 @@ var StandaloneDatabaseError = class extends Error {
|
|
|
11
11
|
}
|
|
12
12
|
};
|
|
13
13
|
const isRecord$1 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
14
|
+
/** `headers` with each lazy value resolved. */
|
|
15
|
+
const resolveHeaders = async (headers = {}) => Object.fromEntries(await Promise.all(Object.entries(headers).map(async ([name, value]) => [name, await resolveLazy(value)])));
|
|
14
16
|
const createStandaloneHttp = (config) => {
|
|
15
17
|
const buildUrl = (path) => `${config.baseUrl.replace(/\/+$/, "")}${path}`;
|
|
16
|
-
const headers = (routeHeaders) => ({
|
|
18
|
+
const headers = async (routeHeaders) => ({
|
|
17
19
|
"Content-Type": "application/json",
|
|
18
|
-
...config.commonHeaders,
|
|
20
|
+
...await resolveHeaders(config.commonHeaders),
|
|
19
21
|
...routeHeaders
|
|
20
22
|
});
|
|
21
23
|
const requestFailed = async (response) => {
|
|
@@ -107,7 +109,7 @@ const createStandaloneCoreApi = (config) => {
|
|
|
107
109
|
const http = createStandaloneHttp(config);
|
|
108
110
|
let checked;
|
|
109
111
|
const checkProtocol = async () => {
|
|
110
|
-
const response = await fetch(http.buildUrl("/version"), { headers: http.headers({ "Cache-Control": "no-cache" }) });
|
|
112
|
+
const response = await fetch(http.buildUrl("/version"), { headers: await http.headers({ "Cache-Control": "no-cache" }) });
|
|
111
113
|
const body = response.status === 404 ? {} : await http.parseJson(response);
|
|
112
114
|
const protocol = isRecord(body) ? body.adminProtocol : void 0;
|
|
113
115
|
if (typeof protocol !== "number" || protocol < 2) throw new StandaloneDatabaseError("request-failed", `The server at ${config.baseUrl} does not report Hot Updater admin API protocol 2 at /version. Set baseUrl to the path where the server mounts handlers.admin, such as https://example.com/hot-updater/admin.`, response.status);
|
|
@@ -124,7 +126,7 @@ const createStandaloneCoreApi = (config) => {
|
|
|
124
126
|
for (const [key, value] of Object.entries(query)) url.searchParams.set(key, value);
|
|
125
127
|
const response = await fetch(url, {
|
|
126
128
|
method,
|
|
127
|
-
headers: http.headers(method === "GET" ? { "Cache-Control": "no-cache" } : void 0),
|
|
129
|
+
headers: await http.headers(method === "GET" ? { "Cache-Control": "no-cache" } : void 0),
|
|
128
130
|
...body === void 0 ? {} : { body: JSON.stringify(body) }
|
|
129
131
|
});
|
|
130
132
|
if (response.status === 204) return null;
|
|
@@ -251,8 +253,8 @@ const standaloneRepository = (config) => {
|
|
|
251
253
|
return Object.freeze({
|
|
252
254
|
name: "standalone-repository",
|
|
253
255
|
core: createStandaloneCoreApi(config),
|
|
254
|
-
fetchAdmin: (path, init = {}) => {
|
|
255
|
-
const headers = new Headers(http.headers({ "Cache-Control": "no-cache" }));
|
|
256
|
+
fetchAdmin: async (path, init = {}) => {
|
|
257
|
+
const headers = new Headers(await http.headers({ "Cache-Control": "no-cache" }));
|
|
256
258
|
new Headers(init.headers).forEach((value, name) => {
|
|
257
259
|
headers.set(name, value);
|
|
258
260
|
});
|
|
@@ -269,8 +271,12 @@ const defaultRoutes = {
|
|
|
269
271
|
put: () => ({ path: "/upload" }),
|
|
270
272
|
get: () => ({ path: "/get" }),
|
|
271
273
|
exists: () => ({ path: "/exists" }),
|
|
272
|
-
delete: () => ({ path: "/delete" })
|
|
274
|
+
delete: () => ({ path: "/delete" }),
|
|
275
|
+
listObjects: () => ({ path: "/list" }),
|
|
276
|
+
deleteObjects: () => ({ path: "/delete-objects" })
|
|
273
277
|
};
|
|
278
|
+
/** Keys sent in one `deleteObjects` request. */
|
|
279
|
+
const DELETE_BATCH_SIZE = 1e3;
|
|
274
280
|
const createRoute = (defaultRoute, customRoute) => ({
|
|
275
281
|
path: customRoute?.path ?? defaultRoute.path,
|
|
276
282
|
headers: {
|
|
@@ -279,8 +285,8 @@ const createRoute = (defaultRoute, customRoute) => ({
|
|
|
279
285
|
}
|
|
280
286
|
});
|
|
281
287
|
const standaloneStorage = (config) => {
|
|
282
|
-
const getHeaders = (routeHeaders) => ({
|
|
283
|
-
...config.commonHeaders,
|
|
288
|
+
const getHeaders = async (routeHeaders) => ({
|
|
289
|
+
...await resolveHeaders(config.commonHeaders),
|
|
284
290
|
...routeHeaders
|
|
285
291
|
});
|
|
286
292
|
const resolveRoute = (name, args) => {
|
|
@@ -288,6 +294,14 @@ const standaloneStorage = (config) => {
|
|
|
288
294
|
const custom = config.routes?.[name];
|
|
289
295
|
return createRoute(fallback(...args), custom?.(...args));
|
|
290
296
|
};
|
|
297
|
+
const postJson = async (path, headers, body) => fetch(`${config.baseUrl}${path}`, {
|
|
298
|
+
method: "POST",
|
|
299
|
+
headers: {
|
|
300
|
+
"content-type": "application/json",
|
|
301
|
+
...await getHeaders(headers)
|
|
302
|
+
},
|
|
303
|
+
body: JSON.stringify(body)
|
|
304
|
+
});
|
|
291
305
|
const requestStorageUri = async (routeName, storageUri) => {
|
|
292
306
|
parseStorageUri(storageUri, config.protocol);
|
|
293
307
|
const { path, headers } = resolveRoute(routeName, [{ storageUri }]);
|
|
@@ -295,7 +309,7 @@ const standaloneStorage = (config) => {
|
|
|
295
309
|
method: routeName === "delete" ? "DELETE" : "POST",
|
|
296
310
|
headers: {
|
|
297
311
|
"content-type": "application/json",
|
|
298
|
-
...getHeaders(headers)
|
|
312
|
+
...await getHeaders(headers)
|
|
299
313
|
},
|
|
300
314
|
body: JSON.stringify({ storageUri })
|
|
301
315
|
});
|
|
@@ -318,7 +332,7 @@ const standaloneStorage = (config) => {
|
|
|
318
332
|
formData.append("key", key);
|
|
319
333
|
const response = await fetch(`${config.baseUrl}${path}`, {
|
|
320
334
|
method: "POST",
|
|
321
|
-
headers: getHeaders(headers),
|
|
335
|
+
headers: await getHeaders(headers),
|
|
322
336
|
body: formData
|
|
323
337
|
});
|
|
324
338
|
if (!response.ok) throw new Error(`Failed to upload bundle: ${response.statusText}`);
|
|
@@ -339,6 +353,39 @@ const standaloneStorage = (config) => {
|
|
|
339
353
|
async delete({ storageUri }) {
|
|
340
354
|
await requestStorageUri("delete", storageUri);
|
|
341
355
|
return { deleted: true };
|
|
356
|
+
},
|
|
357
|
+
async listObjects(prefix) {
|
|
358
|
+
const { path, headers } = resolveRoute("listObjects", [{ prefix }]);
|
|
359
|
+
const objects = [];
|
|
360
|
+
let cursor;
|
|
361
|
+
do {
|
|
362
|
+
const response = await postJson(path, headers, {
|
|
363
|
+
prefix,
|
|
364
|
+
cursor
|
|
365
|
+
});
|
|
366
|
+
if (response.status === 404) throw new Error(`The storage service has no ${path} route, which storage prune needs.`);
|
|
367
|
+
if (!response.ok) throw new Error(`Failed to list storage objects: ${response.statusText}`);
|
|
368
|
+
const page = await response.json();
|
|
369
|
+
for (const object of page.objects) {
|
|
370
|
+
parseStorageUri(object.storageUri, config.protocol);
|
|
371
|
+
objects.push({
|
|
372
|
+
key: object.key,
|
|
373
|
+
storageUri: object.storageUri,
|
|
374
|
+
size: object.size,
|
|
375
|
+
...object.lastModifiedAt === void 0 ? {} : { lastModifiedAt: new Date(object.lastModifiedAt) }
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
cursor = page.cursor;
|
|
379
|
+
} while (cursor !== void 0);
|
|
380
|
+
return objects;
|
|
381
|
+
},
|
|
382
|
+
async deleteObjects(keys) {
|
|
383
|
+
for (let offset = 0; offset < keys.length; offset += DELETE_BATCH_SIZE) {
|
|
384
|
+
const batch = keys.slice(offset, offset + DELETE_BATCH_SIZE);
|
|
385
|
+
const { path, headers } = resolveRoute("deleteObjects", [{ keys: batch }]);
|
|
386
|
+
const response = await postJson(path, headers, { keys: batch });
|
|
387
|
+
if (!response.ok) throw new Error(`Failed to delete storage objects: ${response.statusText}`);
|
|
388
|
+
}
|
|
342
389
|
}
|
|
343
390
|
});
|
|
344
391
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hot-updater/standalone",
|
|
3
|
-
"version": "1.2.0-rc.
|
|
3
|
+
"version": "1.2.0-rc.9",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "React Native OTA solution for self-hosted",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -42,8 +42,8 @@
|
|
|
42
42
|
"access": "public"
|
|
43
43
|
},
|
|
44
44
|
"dependencies": {
|
|
45
|
-
"@hot-updater/protocol": "1.2.0-rc.
|
|
46
|
-
"@hot-updater/plugin-core": "1.2.0-rc.
|
|
45
|
+
"@hot-updater/protocol": "1.2.0-rc.9",
|
|
46
|
+
"@hot-updater/plugin-core": "1.2.0-rc.9"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
49
|
"mime": "2.6.0",
|
|
@@ -52,12 +52,7 @@
|
|
|
52
52
|
"msw": "^2.7.0",
|
|
53
53
|
"verkit": "0.4.0",
|
|
54
54
|
"vitest": "4.1.4",
|
|
55
|
-
"@hot-updater/test-utils": "1.2.0-rc.
|
|
56
|
-
},
|
|
57
|
-
"nx": {
|
|
58
|
-
"implicitDependencies": [
|
|
59
|
-
"!@hot-updater/test-utils"
|
|
60
|
-
]
|
|
55
|
+
"@hot-updater/test-utils": "1.2.0-rc.9"
|
|
61
56
|
},
|
|
62
57
|
"scripts": {
|
|
63
58
|
"build": "tsdown",
|