@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 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.8",
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.8",
46
- "@hot-updater/plugin-core": "1.2.0-rc.8"
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.8"
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",