@hot-updater/console 0.35.12 → 0.36.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.
@@ -1,6 +1,6 @@
1
1
  import { c as createServerFn, i as TSS_SERVER_FUNCTION } from "./createServerFn-DzU0k62V.mjs";
2
2
  import { n as isRuntimeStoragePlugin } from "./storageProfile-FVkjPoGb.mjs";
3
- //#region node_modules/.nitro/vite/services/ssr/assets/api-rpc-CToa1VZt.js
3
+ //#region node_modules/.nitro/vite/services/ssr/assets/api-rpc-BIbDUKSz.js
4
4
  var createServerRpc = (serverFnMeta, splitImportFn) => {
5
5
  const url = "/_serverFn/" + serverFnMeta.id;
6
6
  return Object.assign(splitImportFn, {
@@ -71,6 +71,7 @@ var getBundles = createServerFn({ method: "GET" }).inputValidator((input) => inp
71
71
  const query = {
72
72
  channel: data?.channel ?? void 0,
73
73
  platform: data?.platform ?? void 0,
74
+ targetAppVersion: data?.targetAppVersion ?? void 0,
74
75
  page: typeof data?.page === "number" && Number.isInteger(data.page) && data.page > 1 ? data.page : void 0,
75
76
  limit: data?.limit ? Number(data.limit) : 20,
76
77
  after: data?.after ?? void 0,
@@ -80,7 +81,8 @@ var getBundles = createServerFn({ method: "GET" }).inputValidator((input) => inp
80
81
  const bundleQueryOptions = {
81
82
  where: {
82
83
  channel: query.channel,
83
- platform: query.platform
84
+ platform: query.platform,
85
+ targetAppVersion: query.targetAppVersion
84
86
  },
85
87
  limit: query.limit,
86
88
  page: query.page,
@@ -250,7 +252,7 @@ var deleteBundle_createServerFn_handler = createServerRpc({
250
252
  var deleteBundle = createServerFn({ method: "POST" }).inputValidator((input) => input).handler(deleteBundle_createServerFn_handler, async ({ data }) => {
251
253
  try {
252
254
  const { prepareConfig } = await import("./config.server-BSS366KT.mjs");
253
- const { deleteBundle: deleteBundleWithStorage } = await import("./deleteBundle-De_OKj6m.mjs");
255
+ const { deleteBundle: deleteBundleWithStorage } = await import("./deleteBundle-BgbZTtpW.mjs");
254
256
  const { databasePlugin, storagePlugin } = await prepareConfig();
255
257
  await deleteBundleWithStorage(data, {
256
258
  databasePlugin,
@@ -263,5 +265,28 @@ var deleteBundle = createServerFn({ method: "POST" }).inputValidator((input) =>
263
265
  throw error;
264
266
  }
265
267
  });
268
+ var deleteBundles_createServerFn_handler = createServerRpc({
269
+ id: "61de3b69779c5ce2a5f5d3c27e2cfaca8fba8f14c8803559b9cd733c456494fa",
270
+ name: "deleteBundles",
271
+ filename: "src/lib/api-rpc.ts"
272
+ }, (opts) => deleteBundles.__executeServer(opts));
273
+ var deleteBundles = createServerFn({ method: "POST" }).inputValidator((input) => input).handler(deleteBundles_createServerFn_handler, async ({ data }) => {
274
+ try {
275
+ const { prepareConfig } = await import("./config.server-BSS366KT.mjs");
276
+ const { deleteBundles: deleteBundlesWithStorage } = await import("./deleteBundle-BgbZTtpW.mjs");
277
+ const { databasePlugin, storagePlugin } = await prepareConfig();
278
+ return {
279
+ success: true,
280
+ ...await deleteBundlesWithStorage(data, {
281
+ databasePlugin,
282
+ storagePlugin,
283
+ waitForStorageCleanup: false
284
+ })
285
+ };
286
+ } catch (error) {
287
+ console.error("Error during bundle deletion:", error);
288
+ throw error;
289
+ }
290
+ });
266
291
  //#endregion
267
- export { createBundle_createServerFn_handler, deleteBundle_createServerFn_handler, getBundleChildCounts_createServerFn_handler, getBundleChildren_createServerFn_handler, getBundleDownloadUrl_createServerFn_handler, getBundle_createServerFn_handler, getBundles_createServerFn_handler, getChannels_createServerFn_handler, getConfigLoaded_createServerFn_handler, getConfig_createServerFn_handler, promoteBundle_createServerFn_handler, updateBundle_createServerFn_handler };
292
+ export { createBundle_createServerFn_handler, deleteBundle_createServerFn_handler, deleteBundles_createServerFn_handler, getBundleChildCounts_createServerFn_handler, getBundleChildren_createServerFn_handler, getBundleDownloadUrl_createServerFn_handler, getBundle_createServerFn_handler, getBundles_createServerFn_handler, getChannels_createServerFn_handler, getConfigLoaded_createServerFn_handler, getConfig_createServerFn_handler, promoteBundle_createServerFn_handler, updateBundle_createServerFn_handler };
@@ -2,7 +2,7 @@ import { a as getManifestStorageUri, i as getBundlePatches, r as getAssetBaseSto
2
2
  import { randomUUID } from "node:crypto";
3
3
  import path from "node:path";
4
4
  import fs from "node:fs/promises";
5
- //#region node_modules/.nitro/vite/services/ssr/assets/deleteBundle-De_OKj6m.js
5
+ //#region node_modules/.nitro/vite/services/ssr/assets/deleteBundle-BgbZTtpW.js
6
6
  var createStorageUriWithRelativePath = ({ baseStorageUri, relativePath }) => {
7
7
  const storageUrl = new URL(baseStorageUri);
8
8
  storageUrl.pathname = `${storageUrl.pathname.replace(/\/+$/, "")}/${relativePath.replace(/\\/g, "/").split("/").filter(Boolean).map((segment) => encodeURIComponent(segment)).join("/")}`;
@@ -58,58 +58,84 @@ async function loadBundleManifest(manifestStorageUri, storagePlugin) {
58
58
  const manifestBytes = await downloadStorageBytes(manifestStorageUri, storagePlugin);
59
59
  return JSON.parse(new TextDecoder().decode(manifestBytes));
60
60
  }
61
- async function deleteBundle({ bundleId }, { databasePlugin, storagePlugin, waitForStorageCleanup = true }) {
62
- const bundle = await databasePlugin.getBundleById(bundleId);
63
- if (!bundle) throw new Error("Bundle not found");
64
- const cleanupCandidates = [
65
- bundle.storageUri,
66
- getManifestStorageUri(bundle),
67
- getAssetBaseStorageUri(bundle),
68
- getPatchStorageUri(bundle),
69
- ...getBundlePatches(bundle).map((patch) => patch.patchStorageUri)
70
- ].filter((value) => Boolean(value));
71
- for (const candidate of cleanupCandidates) resolveStorageUriForDeletion(candidate, storagePlugin);
72
- await databasePlugin.deleteBundle(bundle);
73
- await databasePlugin.commitBundle();
61
+ async function cleanupBundleStorage(bundle, storagePlugin) {
62
+ const cleanupUris = /* @__PURE__ */ new Set();
63
+ const addCleanupUri = (storageUri) => {
64
+ if (!storageUri) return;
65
+ const resolvedStorageUri = resolveStorageUriForDeletion(storageUri, storagePlugin);
66
+ if (resolvedStorageUri) cleanupUris.add(resolvedStorageUri);
67
+ };
68
+ addCleanupUri(bundle.storageUri);
69
+ addCleanupUri(getManifestStorageUri(bundle) ?? void 0);
70
+ addCleanupUri(getPatchStorageUri(bundle) ?? void 0);
71
+ for (const patch of getBundlePatches(bundle)) addCleanupUri(patch.patchStorageUri);
72
+ const manifestStorageUri = getManifestStorageUri(bundle);
73
+ const assetBaseStorageUri = getAssetBaseStorageUri(bundle);
74
+ if (assetBaseStorageUri) if (!manifestStorageUri) {
75
+ if (!isContentAddressedAssetBaseStorageUri(assetBaseStorageUri)) addCleanupUri(assetBaseStorageUri);
76
+ } else if (isContentAddressedAssetBaseStorageUri(assetBaseStorageUri)) {} else try {
77
+ const manifest = await loadBundleManifest(manifestStorageUri, storagePlugin);
78
+ for (const storageUri of getLegacyBundleAssetCleanupUris({
79
+ assetBaseStorageUri,
80
+ manifest
81
+ })) addCleanupUri(storageUri);
82
+ } catch (error) {
83
+ console.error("Failed to load bundle manifest for storage cleanup:", error);
84
+ if (!isContentAddressedAssetBaseStorageUri(assetBaseStorageUri)) addCleanupUri(assetBaseStorageUri);
85
+ }
86
+ if (cleanupUris.size === 0) return;
87
+ for (const storageUri of cleanupUris) try {
88
+ await storagePlugin.profiles.node.delete(storageUri);
89
+ } catch (error) {
90
+ console.error("Failed to delete bundle from storage:", error);
91
+ }
92
+ }
93
+ async function deleteBundles({ bundleIds }, { databasePlugin, storagePlugin, waitForStorageCleanup = true }) {
94
+ const uniqueBundleIds = [...new Set(bundleIds)];
95
+ const { data: matchedBundles } = await databasePlugin.getBundles({
96
+ where: { id: { in: uniqueBundleIds } },
97
+ limit: uniqueBundleIds.length
98
+ });
99
+ const matchedById = new Map(matchedBundles.map((bundle) => [bundle.id, bundle]));
100
+ const bundles = uniqueBundleIds.flatMap((bundleId) => {
101
+ const bundle = matchedById.get(bundleId);
102
+ return bundle ? [bundle] : [];
103
+ });
104
+ const missingBundleIds = uniqueBundleIds.filter((bundleId) => !matchedById.has(bundleId));
105
+ for (const bundle of bundles) {
106
+ const cleanupCandidates = [
107
+ bundle.storageUri,
108
+ getManifestStorageUri(bundle),
109
+ getAssetBaseStorageUri(bundle),
110
+ getPatchStorageUri(bundle),
111
+ ...getBundlePatches(bundle).map((patch) => patch.patchStorageUri)
112
+ ].filter((value) => Boolean(value));
113
+ for (const candidate of cleanupCandidates) resolveStorageUriForDeletion(candidate, storagePlugin);
114
+ }
115
+ if (bundles.length > 0) {
116
+ for (const bundle of bundles) await databasePlugin.deleteBundle(bundle);
117
+ await databasePlugin.commitBundle();
118
+ }
74
119
  const cleanupStorage = async () => {
75
- const cleanupUris = /* @__PURE__ */ new Set();
76
- const addCleanupUri = (storageUri) => {
77
- if (!storageUri) return;
78
- const resolvedStorageUri = resolveStorageUriForDeletion(storageUri, storagePlugin);
79
- if (resolvedStorageUri) cleanupUris.add(resolvedStorageUri);
80
- };
81
- addCleanupUri(bundle.storageUri);
82
- addCleanupUri(getManifestStorageUri(bundle) ?? void 0);
83
- addCleanupUri(getPatchStorageUri(bundle) ?? void 0);
84
- for (const patch of getBundlePatches(bundle)) addCleanupUri(patch.patchStorageUri);
85
- const manifestStorageUri = getManifestStorageUri(bundle);
86
- const assetBaseStorageUri = getAssetBaseStorageUri(bundle);
87
- if (assetBaseStorageUri) if (!manifestStorageUri) {
88
- if (!isContentAddressedAssetBaseStorageUri(assetBaseStorageUri)) addCleanupUri(assetBaseStorageUri);
89
- } else if (isContentAddressedAssetBaseStorageUri(assetBaseStorageUri)) {} else try {
90
- const manifest = await loadBundleManifest(manifestStorageUri, storagePlugin);
91
- for (const storageUri of getLegacyBundleAssetCleanupUris({
92
- assetBaseStorageUri,
93
- manifest
94
- })) addCleanupUri(storageUri);
95
- } catch (error) {
96
- console.error("Failed to load bundle manifest for storage cleanup:", error);
97
- if (!isContentAddressedAssetBaseStorageUri(assetBaseStorageUri)) addCleanupUri(assetBaseStorageUri);
98
- }
99
- if (cleanupUris.size === 0) return;
100
- for (const storageUri of cleanupUris) try {
101
- await storagePlugin.profiles.node.delete(storageUri);
102
- } catch (error) {
103
- console.error("Failed to delete bundle from storage:", error);
104
- }
120
+ for (const bundle of bundles) await cleanupBundleStorage(bundle, storagePlugin);
105
121
  };
106
122
  if (waitForStorageCleanup) {
107
123
  await cleanupStorage();
108
- return;
124
+ return {
125
+ deletedBundleIds: bundles.map((bundle) => bundle.id),
126
+ missingBundleIds
127
+ };
109
128
  }
110
129
  cleanupStorage().catch((error) => {
111
130
  console.error("Failed to clean up bundle storage:", error);
112
131
  });
132
+ return {
133
+ deletedBundleIds: bundles.map((bundle) => bundle.id),
134
+ missingBundleIds
135
+ };
136
+ }
137
+ async function deleteBundle({ bundleId }, dependencies) {
138
+ if ((await deleteBundles({ bundleIds: [bundleId] }, dependencies)).missingBundleIds.length > 0) throw new Error(`Bundle not found: ${bundleId}`);
113
139
  }
114
140
  //#endregion
115
- export { deleteBundle };
141
+ export { deleteBundle, deleteBundles };
@@ -8,7 +8,7 @@ import { t as QueryClient } from "../_libs/tanstack__query-core.mjs";
8
8
  import { r as QueryClientProvider } from "../_libs/tanstack__react-query.mjs";
9
9
  import { t as z } from "../_libs/next-themes.mjs";
10
10
  import { t as Toaster } from "../_libs/sonner.mjs";
11
- //#region node_modules/.nitro/vite/services/ssr/assets/router-DB_SK2GY.js
11
+ //#region node_modules/.nitro/vite/services/ssr/assets/router-C3hqEy3k.js
12
12
  var import_react = /* @__PURE__ */ __toESM(require_react());
13
13
  var import_jsx_runtime = require_jsx_runtime();
14
14
  function HotUpdaterLogo({ className }) {
@@ -118,6 +118,7 @@ function AppSidebar() {
118
118
  search: {
119
119
  channel: void 0,
120
120
  platform: void 0,
121
+ targetAppVersion: void 0,
121
122
  page: void 0,
122
123
  after: void 0,
123
124
  before: void 0,
@@ -146,6 +147,7 @@ function AppSidebar() {
146
147
  search: {
147
148
  channel: void 0,
148
149
  platform: void 0,
150
+ targetAppVersion: void 0,
149
151
  page: void 0,
150
152
  after: void 0,
151
153
  before: void 0,
@@ -166,6 +168,7 @@ function AppSidebar() {
166
168
  var homeSearch = {
167
169
  channel: void 0,
168
170
  platform: void 0,
171
+ targetAppVersion: void 0,
169
172
  page: void 0,
170
173
  after: void 0,
171
174
  before: void 0,
@@ -215,7 +218,7 @@ var Toaster$1 = ({ ...props }) => {
215
218
  ...props
216
219
  });
217
220
  };
218
- var styles_default = "/assets/styles-C0q26dS-.css";
221
+ var styles_default = "/assets/styles-C0Re4quB.css";
219
222
  var Route$1 = createRootRoute({
220
223
  head: () => ({
221
224
  meta: [
@@ -270,7 +273,7 @@ function RootLayout() {
270
273
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Outlet, {})
271
274
  })] });
272
275
  }
273
- var $$splitComponentImporter = () => import("./routes-BgAWo6j6.mjs");
276
+ var $$splitComponentImporter = () => import("./routes-B0lQvvlC.mjs");
274
277
  var rootRouteChildren = { IndexRoute: createFileRoute("/")({
275
278
  component: lazyRouteComponent($$splitComponentImporter, "component"),
276
279
  validateSearch: (search) => {
@@ -278,6 +281,7 @@ var rootRouteChildren = { IndexRoute: createFileRoute("/")({
278
281
  return {
279
282
  channel: search.channel,
280
283
  platform: search.platform,
284
+ targetAppVersion: search.targetAppVersion,
281
285
  page: parsedPage !== void 0 && Number.isInteger(parsedPage) && parsedPage > 1 ? parsedPage : void 0,
282
286
  after: search.after,
283
287
  before: search.before,
@@ -14,11 +14,11 @@ import { m as useSearch, p as useNavigate } from "../_libs/@tanstack/react-route
14
14
  import { n as useStore, t as useForm } from "../_libs/@tanstack/react-form+[...].mjs";
15
15
  import { i as useQueryClient, n as useQuery, t as useMutation } from "../_libs/tanstack__react-query.mjs";
16
16
  import { n as toast } from "../_libs/sonner.mjs";
17
- import { t as getServerFnById } from "../__23tanstack-start-server-fn-resolver-DVQ3oVr1.mjs";
17
+ import { t as getServerFnById } from "../__23tanstack-start-server-fn-resolver-CgwgZ0-i.mjs";
18
18
  import { t as normalizeRange } from "../_libs/verkit.mjs";
19
19
  import { i as getCoreRowModel, n as useReactTable, r as createColumnHelper, t as flexRender } from "../_libs/@tanstack/react-table+[...].mjs";
20
20
  import { n as require_dayjs_min, t as require_relativeTime } from "../_libs/dayjs.mjs";
21
- //#region node_modules/.nitro/vite/services/ssr/assets/routes-BgAWo6j6.js
21
+ //#region node_modules/.nitro/vite/services/ssr/assets/routes-B0lQvvlC.js
22
22
  var import_react = /* @__PURE__ */ __toESM(require_react());
23
23
  var import_jsx_runtime = require_jsx_runtime();
24
24
  var import_dayjs_min = /* @__PURE__ */ __toESM(require_dayjs_min());
@@ -411,6 +411,7 @@ var updateBundle = createServerFn({ method: "POST" }).inputValidator((input) =>
411
411
  var promoteBundle = createServerFn({ method: "POST" }).inputValidator((input) => input).handler(createSsrRpc("c4ef3bbb77ea8a4410623fd18a4741e2ff0668b1a97510bcdd032a8428eaa5da"));
412
412
  createServerFn({ method: "POST" }).inputValidator((input) => input).handler(createSsrRpc("16cc7c2f080ea5b73e0c6bba815b110dbd7727796036d248ba0d8ae819ddef08"));
413
413
  var deleteBundle = createServerFn({ method: "POST" }).inputValidator((input) => input).handler(createSsrRpc("3a27ff5679228b86a346b3fe42142e7a5bf0264f73173c0dbe5f07fb37c43cf7"));
414
+ var deleteBundles = createServerFn({ method: "POST" }).inputValidator((input) => input).handler(createSsrRpc("61de3b69779c5ce2a5f5d3c27e2cfaca8fba8f14c8803559b9cd733c456494fa"));
414
415
  var bundleListQueryKey = ["bundles"];
415
416
  var queryKeys = {
416
417
  config: ["config"],
@@ -445,6 +446,14 @@ function removeBundleFromQueryData(data, bundleId) {
445
446
  data: data.data.filter((bundle) => bundle.id !== bundleId)
446
447
  };
447
448
  }
449
+ function removeBundlesFromQueryData(data, bundleIds) {
450
+ if (!data) return data;
451
+ const bundleIdSet = new Set(bundleIds);
452
+ return {
453
+ ...data,
454
+ data: data.data.filter((bundle) => !bundleIdSet.has(bundle.id))
455
+ };
456
+ }
448
457
  var hasOwn = (value, key) => Object.prototype.hasOwnProperty.call(value, key);
449
458
  var invalidateInBackground = (queryClient, queryKey) => {
450
459
  queryClient.invalidateQueries({ queryKey }).catch(() => void 0);
@@ -540,6 +549,19 @@ function useDeleteBundleMutation() {
540
549
  }
541
550
  });
542
551
  }
552
+ function useDeleteBundlesMutation() {
553
+ const queryClient = useQueryClient();
554
+ return useMutation({
555
+ mutationFn: (params) => deleteBundles({ data: params }),
556
+ onSuccess: (_, vars) => {
557
+ for (const bundleId of vars.bundleIds) queryClient.removeQueries({ queryKey: queryKeys.bundle(bundleId) });
558
+ queryClient.setQueriesData({ queryKey: queryKeys.bundles.all }, (data) => removeBundlesFromQueryData(data, vars.bundleIds));
559
+ invalidateInBackground(queryClient, queryKeys.bundles.all);
560
+ invalidateInBackground(queryClient, queryKeys.bundleChildren.all);
561
+ invalidateInBackground(queryClient, queryKeys.channels);
562
+ }
563
+ });
564
+ }
543
565
  function AlertDialog$1({ ...props }) {
544
566
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Root2, {
545
567
  "data-slot": "alert-dialog",
@@ -758,6 +780,7 @@ function useFilterParams() {
758
780
  const filters = {
759
781
  channel: search.channel,
760
782
  platform: search.platform,
783
+ targetAppVersion: search.targetAppVersion,
761
784
  page: search.page,
762
785
  after: search.after,
763
786
  before: search.before
@@ -774,14 +797,16 @@ function useFilterParams() {
774
797
  const getNextFilters = (newFilters) => {
775
798
  const hasChannel = Object.hasOwn(newFilters, "channel");
776
799
  const hasPlatform = Object.hasOwn(newFilters, "platform");
800
+ const hasTargetAppVersion = Object.hasOwn(newFilters, "targetAppVersion");
777
801
  const hasPage = Object.hasOwn(newFilters, "page");
778
802
  const hasAfter = Object.hasOwn(newFilters, "after");
779
803
  const hasBefore = Object.hasOwn(newFilters, "before");
780
- const shouldResetPagination = hasChannel || hasPlatform;
804
+ const shouldResetPagination = hasChannel || hasPlatform || hasTargetAppVersion;
781
805
  const nextPage = hasPage && newFilters.page !== void 0 && newFilters.page > 1 ? newFilters.page : void 0;
782
806
  return {
783
807
  channel: hasChannel ? newFilters.channel : filters.channel,
784
808
  platform: hasPlatform ? newFilters.platform : filters.platform,
809
+ targetAppVersion: hasTargetAppVersion ? newFilters.targetAppVersion : filters.targetAppVersion,
785
810
  page: shouldResetPagination ? void 0 : hasPage ? nextPage : filters.page,
786
811
  after: shouldResetPagination ? void 0 : hasAfter ? newFilters.after : filters.after,
787
812
  before: shouldResetPagination ? void 0 : hasBefore ? newFilters.before : filters.before
@@ -812,6 +837,7 @@ function useFilterParams() {
812
837
  navigateWithSearch({
813
838
  channel: void 0,
814
839
  platform: void 0,
840
+ targetAppVersion: void 0,
815
841
  page: void 0,
816
842
  after: void 0,
817
843
  before: void 0,
@@ -2341,7 +2367,7 @@ function DeleteStatusIcon({ status }) {
2341
2367
  }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TooltipContent, { children: label })] });
2342
2368
  }
2343
2369
  function SelectedBundlesDeleteDialog({ bundles, open, onOpenChange, onComplete }) {
2344
- const deleteBundleMutation = useDeleteBundleMutation();
2370
+ const deleteBundlesMutation = useDeleteBundlesMutation();
2345
2371
  const [phase, setPhase] = (0, import_react.useState)("confirming");
2346
2372
  const [items, setItems] = (0, import_react.useState)(() => createDeleteItems(bundles));
2347
2373
  const isDeleting = phase === "deleting";
@@ -2350,15 +2376,8 @@ function SelectedBundlesDeleteDialog({ bundles, open, onOpenChange, onComplete }
2350
2376
  const failedBundleIds = items.filter((item) => item.status === "failed").map((item) => item.bundle.id);
2351
2377
  const hasFailures = failedBundleIds.length > 0;
2352
2378
  const title = phase === "confirming" ? "Delete selected bundles?" : "Deleting bundles";
2353
- const description = phase === "confirming" ? `This action cannot be undone. This will permanently delete ${bundles.length} bundles and remove them from storage.` : `${activeCount} of ${totalCount} delete requests finished.`;
2354
- const deleteButtonLabel = (0, import_react.useMemo)(() => {
2355
- if (!isDeleting) return "Delete";
2356
- return `Deleting ${Math.min(activeCount + 1, totalCount)}/${totalCount}`;
2357
- }, [
2358
- activeCount,
2359
- isDeleting,
2360
- totalCount
2361
- ]);
2379
+ const description = phase === "confirming" ? `This action cannot be undone. This deletes ${bundles.length} bundle records and schedules artifact cleanup; shared assets are reclaimed by storage prune.` : phase === "deleting" ? `Deleting ${totalCount} bundles in one batch.` : `${activeCount} of ${totalCount} delete requests finished.`;
2380
+ const deleteButtonLabel = isDeleting ? "Deleting..." : "Delete";
2362
2381
  (0, import_react.useEffect)(() => {
2363
2382
  if (open && phase === "confirming") setItems(createDeleteItems(bundles));
2364
2383
  }, [
@@ -2377,44 +2396,36 @@ function SelectedBundlesDeleteDialog({ bundles, open, onOpenChange, onComplete }
2377
2396
  const runDeletion = async (bundleIds) => {
2378
2397
  if (isDeleting || bundleIds.length === 0) return;
2379
2398
  const bundleIdSet = new Set(bundleIds);
2380
- const deletedBundleIds = [];
2381
- const nextFailedBundleIds = [];
2382
2399
  setPhase("deleting");
2383
2400
  setItems((currentItems) => currentItems.map((item) => bundleIdSet.has(item.bundle.id) ? {
2384
2401
  bundle: item.bundle,
2385
- status: "queued"
2402
+ status: "deleting"
2386
2403
  } : item));
2387
- for (const bundleId of bundleIds) {
2388
- setItems((currentItems) => currentItems.map((item) => item.bundle.id === bundleId ? {
2404
+ try {
2405
+ const result = await deleteBundlesMutation.mutateAsync({ bundleIds: [...bundleIds] });
2406
+ setItems((currentItems) => currentItems.map((item) => bundleIdSet.has(item.bundle.id) ? {
2389
2407
  bundle: item.bundle,
2390
- status: "deleting"
2408
+ status: "deleted"
2391
2409
  } : item));
2392
- try {
2393
- await deleteBundleMutation.mutateAsync({ bundleId });
2394
- deletedBundleIds.push(bundleId);
2395
- setItems((currentItems) => currentItems.map((item) => item.bundle.id === bundleId ? {
2396
- bundle: item.bundle,
2397
- status: "deleted"
2398
- } : item));
2399
- } catch (error) {
2400
- nextFailedBundleIds.push(bundleId);
2401
- setItems((currentItems) => currentItems.map((item) => item.bundle.id === bundleId ? {
2402
- bundle: item.bundle,
2403
- message: getDeleteErrorMessage(error),
2404
- status: "failed"
2405
- } : item));
2406
- }
2407
- }
2408
- setPhase("complete");
2409
- onComplete({
2410
- deletedBundleIds,
2411
- failedBundleIds: nextFailedBundleIds
2412
- });
2413
- if (nextFailedBundleIds.length > 0) {
2414
- toast.error(`${deletedBundleIds.length} deleted, ${nextFailedBundleIds.length} failed`);
2415
- return;
2410
+ setPhase("complete");
2411
+ onComplete({
2412
+ deletedBundleIds: bundleIds,
2413
+ failedBundleIds: []
2414
+ });
2415
+ toast.success(result.missingBundleIds.length > 0 ? `${result.deletedBundleIds.length} deleted, ${result.missingBundleIds.length} already absent` : `${bundleIds.length} bundles deleted successfully`);
2416
+ } catch (error) {
2417
+ setItems((currentItems) => currentItems.map((item) => bundleIdSet.has(item.bundle.id) ? {
2418
+ bundle: item.bundle,
2419
+ message: getDeleteErrorMessage(error),
2420
+ status: "failed"
2421
+ } : item));
2422
+ setPhase("complete");
2423
+ onComplete({
2424
+ deletedBundleIds: [],
2425
+ failedBundleIds: bundleIds
2426
+ });
2427
+ toast.error(`0 deleted, ${bundleIds.length} failed`);
2416
2428
  }
2417
- toast.success(`${deletedBundleIds.length} bundles deleted successfully`);
2418
2429
  };
2419
2430
  const handleDelete = () => {
2420
2431
  runDeletion(items.map((item) => item.bundle.id));
@@ -2896,7 +2907,14 @@ function BundlesTable({ bundles, pagination, expandedBundleId, selectedBundleId,
2896
2907
  function FilterToolbar() {
2897
2908
  const { filters, setFilters, resetFilters } = useFilterParams();
2898
2909
  const { data: channels = [] } = useChannelsQuery();
2899
- const hasActiveFilters = filters.channel || filters.platform;
2910
+ const [targetAppVersion, setTargetAppVersion] = (0, import_react.useState)(filters.targetAppVersion ?? "");
2911
+ (0, import_react.useEffect)(() => {
2912
+ setTargetAppVersion(filters.targetAppVersion ?? "");
2913
+ }, [filters.targetAppVersion]);
2914
+ const hasActiveFilters = filters.channel || filters.platform || filters.targetAppVersion;
2915
+ const applyTargetAppVersion = () => {
2916
+ setFilters({ targetAppVersion: targetAppVersion.trim() || void 0 });
2917
+ };
2900
2918
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("header", {
2901
2919
  className: "sticky top-0 z-10 flex shrink-0 flex-wrap items-center gap-2 border-b bg-background px-3 py-3 sm:h-12 sm:flex-nowrap sm:bg-card/70 sm:px-4 sm:py-0 sm:backdrop-blur-sm",
2902
2920
  children: [
@@ -2929,6 +2947,17 @@ function FilterToolbar() {
2929
2947
  })
2930
2948
  ] })]
2931
2949
  }),
2950
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Input, {
2951
+ "aria-label": "Target app version",
2952
+ className: "h-8 w-full min-w-[132px] text-xs sm:w-[160px]",
2953
+ placeholder: "Target version",
2954
+ value: targetAppVersion,
2955
+ onBlur: applyTargetAppVersion,
2956
+ onChange: (event) => setTargetAppVersion(event.target.value),
2957
+ onKeyDown: (event) => {
2958
+ if (event.key === "Enter") applyTargetAppVersion();
2959
+ }
2960
+ }),
2932
2961
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Select$1, {
2933
2962
  value: filters.channel || "all",
2934
2963
  onValueChange: (value) => setFilters({ channel: value === "all" ? void 0 : value }),
@@ -2960,6 +2989,7 @@ function BundlesPage() {
2960
2989
  const { data: bundlesData, isLoading } = useBundlesQuery({
2961
2990
  channel: filters.channel,
2962
2991
  platform: filters.platform,
2992
+ targetAppVersion: filters.targetAppVersion,
2963
2993
  page: filters.page,
2964
2994
  after: filters.after,
2965
2995
  before: filters.before,
@@ -3,7 +3,7 @@ import { a as X_TSS_RAW_RESPONSE, d as getStartOptions, f as isRedirect, g as sa
3
3
  import { u as require_react } from "../_libs/@floating-ui/react-dom+[...].mjs";
4
4
  import { P as require_jsx_runtime } from "../_libs/@radix-ui/react-alert-dialog+[...].mjs";
5
5
  import { n as defineHandlerCallback, o as RouterProvider, t as renderRouterToStream } from "../_libs/@tanstack/react-router+[...].mjs";
6
- import { t as getServerFnById } from "../__23tanstack-start-server-fn-resolver-DVQ3oVr1.mjs";
6
+ import { t as getServerFnById } from "../__23tanstack-start-server-fn-resolver-CgwgZ0-i.mjs";
7
7
  import { PassThrough, Readable } from "node:stream";
8
8
  import { AsyncLocalStorage } from "node:async_hooks";
9
9
  require_react();
@@ -3831,7 +3831,7 @@ var defaultSerovalPlugins = [
3831
3831
  * the dev styles URL for route-scoped CSS collection.
3832
3832
  */
3833
3833
  async function getStartManifest(matchedRoutes) {
3834
- const { tsrStartManifest } = await import("../_tanstack-start-manifest_v-x-_byvUV.mjs");
3834
+ const { tsrStartManifest } = await import("../_tanstack-start-manifest_v-Bpx52WOU.mjs");
3835
3835
  const startManifest = tsrStartManifest();
3836
3836
  let routes = startManifest.routes;
3837
3837
  routes[rootRouteId];
@@ -5359,7 +5359,7 @@ var getBaseManifest = getProdBaseManifest;
5359
5359
  var createEarlyHintsForRequest = createEarlyHintsCollector;
5360
5360
  async function loadEntries() {
5361
5361
  const [routerEntry, startEntry, pluginAdapters] = await Promise.all([
5362
- import("./router-DB_SK2GY.mjs"),
5362
+ import("./router-C3hqEy3k.mjs"),
5363
5363
  import("./start-02sLteYG.mjs"),
5364
5364
  import("./empty-plugin-adapters-B4HCzgmM.mjs")
5365
5365
  ]);
@@ -1,19 +1,19 @@
1
- //#region node_modules/.nitro/vite/services/ssr/assets/_tanstack-start-manifest_v-x-_byvUV.js
1
+ //#region node_modules/.nitro/vite/services/ssr/assets/_tanstack-start-manifest_v-Bpx52WOU.js
2
2
  var tsrStartManifest = () => ({ routes: {
3
3
  __root__: {
4
4
  filePath: "/home/runner/work/hot-updater/hot-updater/packages/console/src/routes/__root.tsx",
5
5
  children: ["/"],
6
- preloads: ["/assets/index-CE2a9O8V.js", "/assets/dist-DJX53faP.js"],
6
+ preloads: ["/assets/index-Iylis5kY.js", "/assets/dist-DJX53faP.js"],
7
7
  scripts: [{ attrs: {
8
8
  type: "module",
9
9
  async: !0,
10
- src: "/assets/index-CE2a9O8V.js"
10
+ src: "/assets/index-Iylis5kY.js"
11
11
  } }]
12
12
  },
13
13
  "/": {
14
14
  filePath: "/home/runner/work/hot-updater/hot-updater/packages/console/src/routes/index.tsx",
15
15
  children: void 0,
16
- preloads: ["/assets/routes-XKmyd2vv.js"]
16
+ preloads: ["/assets/routes-HT21hs0C.js"]
17
17
  }
18
18
  } });
19
19
  //#endregion