@hot-updater/standalone 1.2.0-rc.8 → 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 +46 -1
- package/dist/index.d.cts +11 -1
- package/dist/index.d.mts +11 -1
- package/dist/index.mjs +46 -1
- package/package.json +4 -9
package/dist/index.cjs
CHANGED
|
@@ -272,8 +272,12 @@ const defaultRoutes = {
|
|
|
272
272
|
put: () => ({ path: "/upload" }),
|
|
273
273
|
get: () => ({ path: "/get" }),
|
|
274
274
|
exists: () => ({ path: "/exists" }),
|
|
275
|
-
delete: () => ({ path: "/delete" })
|
|
275
|
+
delete: () => ({ path: "/delete" }),
|
|
276
|
+
listObjects: () => ({ path: "/list" }),
|
|
277
|
+
deleteObjects: () => ({ path: "/delete-objects" })
|
|
276
278
|
};
|
|
279
|
+
/** Keys sent in one `deleteObjects` request. */
|
|
280
|
+
const DELETE_BATCH_SIZE = 1e3;
|
|
277
281
|
const createRoute = (defaultRoute, customRoute) => ({
|
|
278
282
|
path: customRoute?.path ?? defaultRoute.path,
|
|
279
283
|
headers: {
|
|
@@ -291,6 +295,14 @@ const standaloneStorage = (config) => {
|
|
|
291
295
|
const custom = config.routes?.[name];
|
|
292
296
|
return createRoute(fallback(...args), custom?.(...args));
|
|
293
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
|
+
});
|
|
294
306
|
const requestStorageUri = async (routeName, storageUri) => {
|
|
295
307
|
(0, _hot_updater_plugin_core.parseStorageUri)(storageUri, config.protocol);
|
|
296
308
|
const { path, headers } = resolveRoute(routeName, [{ storageUri }]);
|
|
@@ -342,6 +354,39 @@ const standaloneStorage = (config) => {
|
|
|
342
354
|
async delete({ storageUri }) {
|
|
343
355
|
await requestStorageUri("delete", storageUri);
|
|
344
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
|
+
}
|
|
345
390
|
}
|
|
346
391
|
});
|
|
347
392
|
};
|
package/dist/index.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { HotUpdaterCoreApi, Lazy, 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. */
|
|
@@ -52,6 +52,14 @@ interface StorageRoutes {
|
|
|
52
52
|
get: (input: StorageGetInput) => RouteConfig;
|
|
53
53
|
exists: (input: StorageExistsInput) => RouteConfig;
|
|
54
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;
|
|
55
63
|
}
|
|
56
64
|
interface StandaloneStorageConfig {
|
|
57
65
|
baseUrl: string;
|
|
@@ -80,6 +88,8 @@ declare const standaloneStorage: (config: StandaloneStorageConfig) => {
|
|
|
80
88
|
readonly delete: ({ storageUri }: StorageDeleteInput) => Promise<{
|
|
81
89
|
deleted: true;
|
|
82
90
|
}>;
|
|
91
|
+
readonly listObjects: (prefix?: string) => Promise<StorageObject[]>;
|
|
92
|
+
readonly deleteObjects: (keys: readonly string[]) => Promise<void>;
|
|
83
93
|
};
|
|
84
94
|
//#endregion
|
|
85
95
|
export { RouteConfig, STANDALONE_ADMIN_PROTOCOL, StandaloneDatabaseError, StandaloneRepository, type StandaloneRepositoryConfig, StandaloneStorageConfig, StorageRoutes, createStandaloneCoreApi, standaloneRepository, standaloneStorage };
|
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { HotUpdaterCoreApi, Lazy, 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. */
|
|
@@ -52,6 +52,14 @@ interface StorageRoutes {
|
|
|
52
52
|
get: (input: StorageGetInput) => RouteConfig;
|
|
53
53
|
exists: (input: StorageExistsInput) => RouteConfig;
|
|
54
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;
|
|
55
63
|
}
|
|
56
64
|
interface StandaloneStorageConfig {
|
|
57
65
|
baseUrl: string;
|
|
@@ -80,6 +88,8 @@ declare const standaloneStorage: (config: StandaloneStorageConfig) => {
|
|
|
80
88
|
readonly delete: ({ storageUri }: StorageDeleteInput) => Promise<{
|
|
81
89
|
deleted: true;
|
|
82
90
|
}>;
|
|
91
|
+
readonly listObjects: (prefix?: string) => Promise<StorageObject[]>;
|
|
92
|
+
readonly deleteObjects: (keys: readonly string[]) => Promise<void>;
|
|
83
93
|
};
|
|
84
94
|
//#endregion
|
|
85
95
|
export { RouteConfig, STANDALONE_ADMIN_PROTOCOL, StandaloneDatabaseError, StandaloneRepository, type StandaloneRepositoryConfig, StandaloneStorageConfig, StorageRoutes, createStandaloneCoreApi, standaloneRepository, standaloneStorage };
|
package/dist/index.mjs
CHANGED
|
@@ -271,8 +271,12 @@ const defaultRoutes = {
|
|
|
271
271
|
put: () => ({ path: "/upload" }),
|
|
272
272
|
get: () => ({ path: "/get" }),
|
|
273
273
|
exists: () => ({ path: "/exists" }),
|
|
274
|
-
delete: () => ({ path: "/delete" })
|
|
274
|
+
delete: () => ({ path: "/delete" }),
|
|
275
|
+
listObjects: () => ({ path: "/list" }),
|
|
276
|
+
deleteObjects: () => ({ path: "/delete-objects" })
|
|
275
277
|
};
|
|
278
|
+
/** Keys sent in one `deleteObjects` request. */
|
|
279
|
+
const DELETE_BATCH_SIZE = 1e3;
|
|
276
280
|
const createRoute = (defaultRoute, customRoute) => ({
|
|
277
281
|
path: customRoute?.path ?? defaultRoute.path,
|
|
278
282
|
headers: {
|
|
@@ -290,6 +294,14 @@ const standaloneStorage = (config) => {
|
|
|
290
294
|
const custom = config.routes?.[name];
|
|
291
295
|
return createRoute(fallback(...args), custom?.(...args));
|
|
292
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
|
+
});
|
|
293
305
|
const requestStorageUri = async (routeName, storageUri) => {
|
|
294
306
|
parseStorageUri(storageUri, config.protocol);
|
|
295
307
|
const { path, headers } = resolveRoute(routeName, [{ storageUri }]);
|
|
@@ -341,6 +353,39 @@ const standaloneStorage = (config) => {
|
|
|
341
353
|
async delete({ storageUri }) {
|
|
342
354
|
await requestStorageUri("delete", storageUri);
|
|
343
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
|
+
}
|
|
344
389
|
}
|
|
345
390
|
});
|
|
346
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",
|