@hot-updater/standalone 1.1.3 → 1.1.5

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 CHANGED
@@ -240,8 +240,12 @@ const defaultRoutes = {
240
240
  put: () => ({ path: "/upload" }),
241
241
  get: () => ({ path: "/get" }),
242
242
  exists: () => ({ path: "/exists" }),
243
- delete: () => ({ path: "/delete" })
243
+ delete: () => ({ path: "/delete" }),
244
+ listObjects: () => ({ path: "/list" }),
245
+ deleteObjects: () => ({ path: "/delete-objects" })
244
246
  };
247
+ /** Keys sent in one `deleteObjects` request. */
248
+ const DELETE_BATCH_SIZE = 1e3;
245
249
  const createRoute = (defaultRoute, customRoute) => ({
246
250
  path: customRoute?.path ?? defaultRoute.path,
247
251
  headers: {
@@ -259,6 +263,14 @@ const standaloneStorage = (config) => {
259
263
  const custom = config.routes?.[name];
260
264
  return createRoute(fallback(...args), custom?.(...args));
261
265
  };
266
+ const postJson = (path, headers, body) => fetch(`${config.baseUrl}${path}`, {
267
+ method: "POST",
268
+ headers: {
269
+ "content-type": "application/json",
270
+ ...getHeaders(headers)
271
+ },
272
+ body: JSON.stringify(body)
273
+ });
262
274
  const requestStorageUri = async (routeName, storageUri) => {
263
275
  (0, _hot_updater_plugin_core.parseStorageUri)(storageUri, config.protocol);
264
276
  const { path, headers } = resolveRoute(routeName, [{ storageUri }]);
@@ -310,6 +322,39 @@ const standaloneStorage = (config) => {
310
322
  async delete({ storageUri }) {
311
323
  await requestStorageUri("delete", storageUri);
312
324
  return { deleted: true };
325
+ },
326
+ async listObjects(prefix) {
327
+ const { path, headers } = resolveRoute("listObjects", [{ prefix }]);
328
+ const objects = [];
329
+ let cursor;
330
+ do {
331
+ const response = await postJson(path, headers, {
332
+ prefix,
333
+ cursor
334
+ });
335
+ if (response.status === 404) throw new Error(`The storage service has no ${path} route, which storage prune needs.`);
336
+ if (!response.ok) throw new Error(`Failed to list storage objects: ${response.statusText}`);
337
+ const page = await response.json();
338
+ for (const object of page.objects) {
339
+ (0, _hot_updater_plugin_core.parseStorageUri)(object.storageUri, config.protocol);
340
+ objects.push({
341
+ key: object.key,
342
+ storageUri: object.storageUri,
343
+ size: object.size,
344
+ ...object.lastModifiedAt === void 0 ? {} : { lastModifiedAt: new Date(object.lastModifiedAt) }
345
+ });
346
+ }
347
+ cursor = page.cursor;
348
+ } while (cursor !== void 0);
349
+ return objects;
350
+ },
351
+ async deleteObjects(keys) {
352
+ for (let offset = 0; offset < keys.length; offset += DELETE_BATCH_SIZE) {
353
+ const batch = keys.slice(offset, offset + DELETE_BATCH_SIZE);
354
+ const { path, headers } = resolveRoute("deleteObjects", [{ keys: batch }]);
355
+ const response = await postJson(path, headers, { keys: batch });
356
+ if (!response.ok) throw new Error(`Failed to delete storage objects: ${response.statusText}`);
357
+ }
313
358
  }
314
359
  });
315
360
  };
package/dist/index.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { HotUpdaterCoreApi, RemoteDatabase, StorageDeleteInput, StorageExistsInput, StorageGetInput, StoragePutInput } from "@hot-updater/plugin-core";
1
+ import { HotUpdaterCoreApi, 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. */
@@ -48,6 +48,14 @@ interface StorageRoutes {
48
48
  get: (input: StorageGetInput) => RouteConfig;
49
49
  exists: (input: StorageExistsInput) => RouteConfig;
50
50
  delete: (input: StorageDeleteInput) => RouteConfig;
51
+ /** Lists objects for `hot-updater storage prune`. */
52
+ listObjects: (input: {
53
+ readonly prefix?: string;
54
+ }) => RouteConfig;
55
+ /** Deletes exact keys for `hot-updater storage prune --yes`. */
56
+ deleteObjects: (input: {
57
+ readonly keys: readonly string[];
58
+ }) => RouteConfig;
51
59
  }
52
60
  interface StandaloneStorageConfig {
53
61
  baseUrl: string;
@@ -71,6 +79,8 @@ declare const standaloneStorage: (config: StandaloneStorageConfig) => {
71
79
  readonly delete: ({ storageUri }: StorageDeleteInput) => Promise<{
72
80
  deleted: true;
73
81
  }>;
82
+ readonly listObjects: (prefix?: string) => Promise<StorageObject[]>;
83
+ readonly deleteObjects: (keys: readonly string[]) => Promise<void>;
74
84
  };
75
85
  //#endregion
76
86
  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, RemoteDatabase, StorageDeleteInput, StorageExistsInput, StorageGetInput, StoragePutInput } from "@hot-updater/plugin-core";
1
+ import { HotUpdaterCoreApi, 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. */
@@ -48,6 +48,14 @@ interface StorageRoutes {
48
48
  get: (input: StorageGetInput) => RouteConfig;
49
49
  exists: (input: StorageExistsInput) => RouteConfig;
50
50
  delete: (input: StorageDeleteInput) => RouteConfig;
51
+ /** Lists objects for `hot-updater storage prune`. */
52
+ listObjects: (input: {
53
+ readonly prefix?: string;
54
+ }) => RouteConfig;
55
+ /** Deletes exact keys for `hot-updater storage prune --yes`. */
56
+ deleteObjects: (input: {
57
+ readonly keys: readonly string[];
58
+ }) => RouteConfig;
51
59
  }
52
60
  interface StandaloneStorageConfig {
53
61
  baseUrl: string;
@@ -71,6 +79,8 @@ declare const standaloneStorage: (config: StandaloneStorageConfig) => {
71
79
  readonly delete: ({ storageUri }: StorageDeleteInput) => Promise<{
72
80
  deleted: true;
73
81
  }>;
82
+ readonly listObjects: (prefix?: string) => Promise<StorageObject[]>;
83
+ readonly deleteObjects: (keys: readonly string[]) => Promise<void>;
74
84
  };
75
85
  //#endregion
76
86
  export { RouteConfig, STANDALONE_ADMIN_PROTOCOL, StandaloneDatabaseError, StandaloneRepository, type StandaloneRepositoryConfig, StandaloneStorageConfig, StorageRoutes, createStandaloneCoreApi, standaloneRepository, standaloneStorage };
package/dist/index.mjs CHANGED
@@ -239,8 +239,12 @@ const defaultRoutes = {
239
239
  put: () => ({ path: "/upload" }),
240
240
  get: () => ({ path: "/get" }),
241
241
  exists: () => ({ path: "/exists" }),
242
- delete: () => ({ path: "/delete" })
242
+ delete: () => ({ path: "/delete" }),
243
+ listObjects: () => ({ path: "/list" }),
244
+ deleteObjects: () => ({ path: "/delete-objects" })
243
245
  };
246
+ /** Keys sent in one `deleteObjects` request. */
247
+ const DELETE_BATCH_SIZE = 1e3;
244
248
  const createRoute = (defaultRoute, customRoute) => ({
245
249
  path: customRoute?.path ?? defaultRoute.path,
246
250
  headers: {
@@ -258,6 +262,14 @@ const standaloneStorage = (config) => {
258
262
  const custom = config.routes?.[name];
259
263
  return createRoute(fallback(...args), custom?.(...args));
260
264
  };
265
+ const postJson = (path, headers, body) => fetch(`${config.baseUrl}${path}`, {
266
+ method: "POST",
267
+ headers: {
268
+ "content-type": "application/json",
269
+ ...getHeaders(headers)
270
+ },
271
+ body: JSON.stringify(body)
272
+ });
261
273
  const requestStorageUri = async (routeName, storageUri) => {
262
274
  parseStorageUri(storageUri, config.protocol);
263
275
  const { path, headers } = resolveRoute(routeName, [{ storageUri }]);
@@ -309,6 +321,39 @@ const standaloneStorage = (config) => {
309
321
  async delete({ storageUri }) {
310
322
  await requestStorageUri("delete", storageUri);
311
323
  return { deleted: true };
324
+ },
325
+ async listObjects(prefix) {
326
+ const { path, headers } = resolveRoute("listObjects", [{ prefix }]);
327
+ const objects = [];
328
+ let cursor;
329
+ do {
330
+ const response = await postJson(path, headers, {
331
+ prefix,
332
+ cursor
333
+ });
334
+ if (response.status === 404) throw new Error(`The storage service has no ${path} route, which storage prune needs.`);
335
+ if (!response.ok) throw new Error(`Failed to list storage objects: ${response.statusText}`);
336
+ const page = await response.json();
337
+ for (const object of page.objects) {
338
+ parseStorageUri(object.storageUri, config.protocol);
339
+ objects.push({
340
+ key: object.key,
341
+ storageUri: object.storageUri,
342
+ size: object.size,
343
+ ...object.lastModifiedAt === void 0 ? {} : { lastModifiedAt: new Date(object.lastModifiedAt) }
344
+ });
345
+ }
346
+ cursor = page.cursor;
347
+ } while (cursor !== void 0);
348
+ return objects;
349
+ },
350
+ async deleteObjects(keys) {
351
+ for (let offset = 0; offset < keys.length; offset += DELETE_BATCH_SIZE) {
352
+ const batch = keys.slice(offset, offset + DELETE_BATCH_SIZE);
353
+ const { path, headers } = resolveRoute("deleteObjects", [{ keys: batch }]);
354
+ const response = await postJson(path, headers, { keys: batch });
355
+ if (!response.ok) throw new Error(`Failed to delete storage objects: ${response.statusText}`);
356
+ }
312
357
  }
313
358
  });
314
359
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hot-updater/standalone",
3
- "version": "1.1.3",
3
+ "version": "1.1.5",
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.1.3",
46
- "@hot-updater/plugin-core": "1.1.3"
45
+ "@hot-updater/protocol": "1.1.5",
46
+ "@hot-updater/plugin-core": "1.1.5"
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.1.3"
56
- },
57
- "nx": {
58
- "implicitDependencies": [
59
- "!@hot-updater/test-utils"
60
- ]
55
+ "@hot-updater/test-utils": "1.1.5"
61
56
  },
62
57
  "scripts": {
63
58
  "build": "tsdown",