@cobaltcore-dev/aurora 2.0.0 → 2.1.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.
Files changed (115) hide show
  1. package/dist/client/ContentHeader-fKKZVu-o.mjs +116 -0
  2. package/dist/client/ContentHeader-fKKZVu-o.mjs.map +1 -0
  3. package/dist/client/{DeleteFlavorModal-C2MqUgGR.mjs → DeleteFlavorModal-Be2GFiB_.mjs} +3 -2
  4. package/dist/client/DeleteFlavorModal-Be2GFiB_.mjs.map +1 -0
  5. package/dist/client/{RouteIdLevelDefaultError-CSseJ1yG.mjs → RouteIdLevelDefaultError-DPF_x2tG.mjs} +9 -9
  6. package/dist/client/RouteIdLevelDefaultError-DPF_x2tG.mjs.map +1 -0
  7. package/dist/client/StorageNotFound-B-zVinVD.mjs +30 -0
  8. package/dist/client/StorageNotFound-B-zVinVD.mjs.map +1 -0
  9. package/dist/client/{_flavorId-DYv_StX22.mjs → _flavorId-0PioWpEp.mjs} +4 -4
  10. package/dist/client/_flavorId-0PioWpEp.mjs.map +1 -0
  11. package/dist/client/{_flavorId-YV3jCS5g.mjs → _flavorId-B7fp6Nk8.mjs} +3 -3
  12. package/dist/client/{_flavorId-YV3jCS5g.mjs.map → _flavorId-B7fp6Nk8.mjs.map} +1 -1
  13. package/dist/client/{_flavorId-CaA4-Pnf.mjs → _flavorId-CgueqKTs.mjs} +3 -3
  14. package/dist/client/{_flavorId-CaA4-Pnf.mjs.map → _flavorId-CgueqKTs.mjs.map} +1 -1
  15. package/dist/client/{_floatingIpId-Dpsc7GU4.mjs → _floatingIpId-B8r64AL9.mjs} +3 -3
  16. package/dist/client/{_floatingIpId-Dpsc7GU4.mjs.map → _floatingIpId-B8r64AL9.mjs.map} +1 -1
  17. package/dist/client/{_floatingIpId-BeYElanB.mjs → _floatingIpId-lEobpOER.mjs} +2 -2
  18. package/dist/client/{_floatingIpId-BeYElanB.mjs.map → _floatingIpId-lEobpOER.mjs.map} +1 -1
  19. package/dist/client/{_imageId-pBSS0YcC.mjs → _imageId-Ccv8PedW.mjs} +3 -3
  20. package/dist/client/{_imageId-pBSS0YcC.mjs.map → _imageId-Ccv8PedW.mjs.map} +1 -1
  21. package/dist/client/_imageId-CiqEQB_J.mjs +558 -0
  22. package/dist/client/{_imageId-mfKp3JHn.mjs.map → _imageId-CiqEQB_J.mjs.map} +1 -1
  23. package/dist/client/{_imageId-mpXUMCh_.mjs → _imageId-D9LR-02T.mjs} +3 -3
  24. package/dist/client/{_imageId-mpXUMCh_.mjs.map → _imageId-D9LR-02T.mjs.map} +1 -1
  25. package/dist/client/{_projectId-BnTBxKfp.mjs → _projectId-BDARsteX.mjs} +3 -3
  26. package/dist/client/{_projectId-BnTBxKfp.mjs.map → _projectId-BDARsteX.mjs.map} +1 -1
  27. package/dist/client/{_projectId-CVrdJ52s.mjs → _projectId-CtyAHqhH.mjs} +2 -2
  28. package/dist/client/{_projectId-CVrdJ52s.mjs.map → _projectId-CtyAHqhH.mjs.map} +1 -1
  29. package/dist/client/_projectId-D3VOH5lN.mjs +94 -0
  30. package/dist/client/_projectId-D3VOH5lN.mjs.map +1 -0
  31. package/dist/client/{_projectId-AjgJX6Gl.mjs → _projectId-ZBk6vka0.mjs} +83 -81
  32. package/dist/client/_projectId-ZBk6vka0.mjs.map +1 -0
  33. package/dist/client/{_securityGroupId-AO3wnH8g.mjs → _securityGroupId-BwgZnoiR.mjs} +2 -2
  34. package/dist/client/{_securityGroupId-AO3wnH8g.mjs.map → _securityGroupId-BwgZnoiR.mjs.map} +1 -1
  35. package/dist/client/{_securityGroupId-Bq0ZyG6z.mjs → _securityGroupId-Cz2NUHdB.mjs} +3 -3
  36. package/dist/client/{_securityGroupId-Bq0ZyG6z.mjs.map → _securityGroupId-Cz2NUHdB.mjs.map} +1 -1
  37. package/dist/client/{_storageType-CzZ4u9x_.mjs → _storageType-BNpP1A2N.mjs} +3 -2
  38. package/dist/client/_storageType-BNpP1A2N.mjs.map +1 -0
  39. package/dist/client/_storageType-BxSGKhAI.mjs +15 -0
  40. package/dist/client/_storageType-BxSGKhAI.mjs.map +1 -0
  41. package/dist/client/_storageType-DZYCmiLt.mjs +30 -0
  42. package/dist/client/_storageType-DZYCmiLt.mjs.map +1 -0
  43. package/dist/client/{_storageType-c-DAu8qr.mjs → _storageType-UqAMP-8d.mjs} +286 -285
  44. package/dist/client/_storageType-UqAMP-8d.mjs.map +1 -0
  45. package/dist/client/_storageType-u51_7OwJ.mjs +7 -0
  46. package/dist/client/_storageType-u51_7OwJ.mjs.map +1 -0
  47. package/dist/client/components/Errors/RouteIdLevelDefaultError.d.ts +2 -1
  48. package/dist/client/{flavors-ZudiAiTU.mjs → flavors-Ce3J-cFa.mjs} +2 -2
  49. package/dist/client/{flavors-ZudiAiTU.mjs.map → flavors-Ce3J-cFa.mjs.map} +1 -1
  50. package/dist/client/{flavors-Bgq3KZmx.mjs → flavors-D5ThLaXO.mjs} +84 -77
  51. package/dist/client/flavors-D5ThLaXO.mjs.map +1 -0
  52. package/dist/client/{floatingips-B1oLH8z6.mjs → floatingips-DXnRlLvV.mjs} +2 -2
  53. package/dist/client/{floatingips-B1oLH8z6.mjs.map → floatingips-DXnRlLvV.mjs.map} +1 -1
  54. package/dist/client/{floatingips-BtLg3yEQ.mjs → floatingips-HRbuLxiN.mjs} +3 -3
  55. package/dist/client/{floatingips-BtLg3yEQ.mjs.map → floatingips-HRbuLxiN.mjs.map} +1 -1
  56. package/dist/client/{floatingips-BeQEkVH8.mjs → floatingips-nN1kQmsu.mjs} +2 -2
  57. package/dist/client/{floatingips-BeQEkVH8.mjs.map → floatingips-nN1kQmsu.mjs.map} +1 -1
  58. package/dist/client/{images-CnTW3bpy.mjs → images-C8M3ysWR.mjs} +3 -3
  59. package/dist/client/{images-CnTW3bpy.mjs.map → images-C8M3ysWR.mjs.map} +1 -1
  60. package/dist/client/{images-DZHDYDQu.mjs → images-Cf5R28NR.mjs} +2 -2
  61. package/dist/client/{images-DZHDYDQu.mjs.map → images-Cf5R28NR.mjs.map} +1 -1
  62. package/dist/client/index.js +198 -197
  63. package/dist/client/index.js.map +1 -1
  64. package/dist/client/{objects-jYQBFzwg.mjs → objects-BudUAfWY.mjs} +4265 -4275
  65. package/dist/client/objects-BudUAfWY.mjs.map +1 -0
  66. package/dist/client/objects-Dc7sKUr8.mjs +67 -0
  67. package/dist/client/objects-Dc7sKUr8.mjs.map +1 -0
  68. package/dist/client/objects-Dw7oeTq2.mjs +80 -0
  69. package/dist/client/objects-Dw7oeTq2.mjs.map +1 -0
  70. package/dist/client/objects-_g7nGIn5.mjs +25 -0
  71. package/dist/client/objects-_g7nGIn5.mjs.map +1 -0
  72. package/dist/client/objects-eBxvY6_n.mjs +78 -0
  73. package/dist/client/objects-eBxvY6_n.mjs.map +1 -0
  74. package/dist/client/projects-yiK0HGSA.mjs.map +1 -1
  75. package/dist/client/{securitygroups-BRndk7wh.mjs → securitygroups-67fFxiwt.mjs} +2 -2
  76. package/dist/client/{securitygroups-BRndk7wh.mjs.map → securitygroups-67fFxiwt.mjs.map} +1 -1
  77. package/dist/client/{securitygroups--0PS2wbM.mjs → securitygroups-DXrfyuVF.mjs} +3 -3
  78. package/dist/client/{securitygroups--0PS2wbM.mjs.map → securitygroups-DXrfyuVF.mjs.map} +1 -1
  79. package/dist/client/{securitygroups-Cs2Op-tu.mjs → securitygroups-WLB1_IM6.mjs} +3 -3
  80. package/dist/client/{securitygroups-Cs2Op-tu.mjs.map → securitygroups-WLB1_IM6.mjs.map} +1 -1
  81. package/dist/client/{securitygroups-CdN4yAAw.mjs → securitygroups-o0IXZM6S.mjs} +2 -2
  82. package/dist/client/{securitygroups-CdN4yAAw.mjs.map → securitygroups-o0IXZM6S.mjs.map} +1 -1
  83. package/dist/client/serviceCatalog-B3lKWC9V.mjs +6 -0
  84. package/dist/client/serviceCatalog-B3lKWC9V.mjs.map +1 -0
  85. package/dist/client/storageProviders-CJ5rOh13.mjs +15 -0
  86. package/dist/client/storageProviders-CJ5rOh13.mjs.map +1 -0
  87. package/dist/client/trpcClient.d.ts +96 -3
  88. package/dist/client/utils/serviceCatalog.d.ts +9 -0
  89. package/dist/client/utils/storageProviders.d.ts +44 -0
  90. package/dist/server/index.d.ts +96 -3
  91. package/dist/server/index.js +1224 -387
  92. package/package.json +1 -1
  93. package/dist/client/ContentHeader-CPJbgYWn.mjs +0 -115
  94. package/dist/client/ContentHeader-CPJbgYWn.mjs.map +0 -1
  95. package/dist/client/DeleteFlavorModal-C2MqUgGR.mjs.map +0 -1
  96. package/dist/client/RouteIdLevelDefaultError-CSseJ1yG.mjs.map +0 -1
  97. package/dist/client/_flavorId-DYv_StX22.mjs.map +0 -1
  98. package/dist/client/_imageId-mfKp3JHn.mjs +0 -558
  99. package/dist/client/_projectId-AjgJX6Gl.mjs.map +0 -1
  100. package/dist/client/_projectId-DgciZslU.mjs +0 -92
  101. package/dist/client/_projectId-DgciZslU.mjs.map +0 -1
  102. package/dist/client/_storageType-BRWWJtow.mjs +0 -14
  103. package/dist/client/_storageType-BRWWJtow.mjs.map +0 -1
  104. package/dist/client/_storageType-CzZ4u9x_.mjs.map +0 -1
  105. package/dist/client/_storageType-c-DAu8qr.mjs.map +0 -1
  106. package/dist/client/_storageType-zeSZe--V.mjs +0 -7
  107. package/dist/client/_storageType-zeSZe--V.mjs.map +0 -1
  108. package/dist/client/flavors-Bgq3KZmx.mjs.map +0 -1
  109. package/dist/client/objects-25xsh4Ia.mjs +0 -66
  110. package/dist/client/objects-25xsh4Ia.mjs.map +0 -1
  111. package/dist/client/objects-KXY6dgwq.mjs +0 -82
  112. package/dist/client/objects-KXY6dgwq.mjs.map +0 -1
  113. package/dist/client/objects-QryuClU3.mjs +0 -82
  114. package/dist/client/objects-QryuClU3.mjs.map +0 -1
  115. package/dist/client/objects-jYQBFzwg.mjs.map +0 -1
@@ -28658,6 +28658,22 @@ var openstackErrorMiddleware = t.middleware(async ({ next }) => {
28658
28658
  }
28659
28659
  return result;
28660
28660
  });
28661
+ function mapScopeError(error, ctx, opts) {
28662
+ if (error instanceof import_signal_openstack.SignalOpenstackApiError && error.statusCode === 401) {
28663
+ if (ctx.validateSession() === true) {
28664
+ return new import_server.TRPCError({
28665
+ code: "NOT_FOUND",
28666
+ message: opts.notFoundMessage
28667
+ });
28668
+ }
28669
+ return new import_server.TRPCError({
28670
+ code: "UNAUTHORIZED",
28671
+ message: "Your session is no longer valid. Please sign in again."
28672
+ });
28673
+ }
28674
+ return error;
28675
+ }
28676
+ __name(mapScopeError, "mapScopeError");
28661
28677
  var router = t.router;
28662
28678
  var auroraRouter = t.router;
28663
28679
  var mergeRouters = t.mergeRouters;
@@ -28683,13 +28699,26 @@ var domainScopedInputSchema = import_zod.z.object({
28683
28699
  var projectScopedProcedure = protectedProcedure.input(projectScopedInputSchema).use(/* @__PURE__ */ __name(async function rescopeToProject(opts) {
28684
28700
  const { ctx, next, input } = opts;
28685
28701
  const { project_id } = input;
28686
- const openstackSession = await ctx.rescopeSession({
28687
- projectId: project_id
28688
- });
28702
+ let openstackSession;
28703
+ try {
28704
+ openstackSession = await ctx.rescopeSession({
28705
+ projectId: project_id
28706
+ });
28707
+ } catch (error) {
28708
+ throw mapScopeError(error, ctx, {
28709
+ notFoundMessage: "Project not found or not accessible with your current session."
28710
+ });
28711
+ }
28689
28712
  if (!openstackSession) {
28713
+ if (ctx.validateSession() === false) {
28714
+ throw new import_server.TRPCError({
28715
+ code: "UNAUTHORIZED",
28716
+ message: "Your session is no longer valid. Please log in again."
28717
+ });
28718
+ }
28690
28719
  throw new import_server.TRPCError({
28691
- code: "UNAUTHORIZED",
28692
- message: "Failed to scope session to project. User may not have access to this project."
28720
+ code: "NOT_FOUND",
28721
+ message: "Project not found or not accessible with your current session."
28693
28722
  });
28694
28723
  }
28695
28724
  return next({
@@ -28717,13 +28746,26 @@ var domainScopedProcedure = protectedProcedure.input(domainScopedInputSchema).us
28717
28746
  message: `Access denied. User does not have access to domain ${domain_id}`
28718
28747
  });
28719
28748
  }
28720
- const openstackSession = await ctx.rescopeSession({
28721
- domainId: domain_id
28722
- });
28749
+ let openstackSession;
28750
+ try {
28751
+ openstackSession = await ctx.rescopeSession({
28752
+ domainId: domain_id
28753
+ });
28754
+ } catch (error) {
28755
+ throw mapScopeError(error, ctx, {
28756
+ notFoundMessage: "Domain not found or not accessible with your current session."
28757
+ });
28758
+ }
28723
28759
  if (!openstackSession) {
28760
+ if (ctx.validateSession() === false) {
28761
+ throw new import_server.TRPCError({
28762
+ code: "UNAUTHORIZED",
28763
+ message: "Your session is no longer valid. Please log in again."
28764
+ });
28765
+ }
28724
28766
  throw new import_server.TRPCError({
28725
- code: "UNAUTHORIZED",
28726
- message: "Failed to scope session to domain. Please try again or contact support."
28767
+ code: "NOT_FOUND",
28768
+ message: "Domain not found or not accessible with your current session."
28727
28769
  });
28728
28770
  }
28729
28771
  return next({
@@ -28785,9 +28827,16 @@ var sessionRouter = {
28785
28827
  setCurrentScope: protectedProcedure.input(discriminatedSchema).mutation(async ({ input, ctx }) => {
28786
28828
  switch (input.type) {
28787
28829
  case "domain": {
28788
- const session = await ctx.rescopeSession({
28789
- domainId: input.domainId
28790
- });
28830
+ let session;
28831
+ try {
28832
+ session = await ctx.rescopeSession({
28833
+ domainId: input.domainId
28834
+ });
28835
+ } catch (error) {
28836
+ throw mapScopeError(error, ctx, {
28837
+ notFoundMessage: "This domain doesn't exist or is not accessible with your current session. Select an available domain."
28838
+ });
28839
+ }
28791
28840
  const token = session?.getToken();
28792
28841
  if (!token?.tokenData.domain) {
28793
28842
  throw new import_server2.TRPCError({
@@ -28801,9 +28850,16 @@ var sessionRouter = {
28801
28850
  };
28802
28851
  }
28803
28852
  case "project": {
28804
- const session = await ctx.rescopeSession({
28805
- projectId: input.projectId
28806
- });
28853
+ let session;
28854
+ try {
28855
+ session = await ctx.rescopeSession({
28856
+ projectId: input.projectId
28857
+ });
28858
+ } catch (error) {
28859
+ throw mapScopeError(error, ctx, {
28860
+ notFoundMessage: "This project doesn't exist or is not accessible from your current domain. Please select a project from your current domain."
28861
+ });
28862
+ }
28807
28863
  const token = session?.getToken();
28808
28864
  if (!token?.tokenData.project) {
28809
28865
  throw new import_server2.TRPCError({
@@ -29037,7 +29093,13 @@ function filterBySearchParams(items, searchTerm, searchFields) {
29037
29093
  const searchLower = searchTerm.toLowerCase().trim();
29038
29094
  return items.filter((item) => searchFields.some((field) => {
29039
29095
  const value = item[field];
29040
- return typeof value === "string" && value.toLowerCase().includes(searchLower);
29096
+ if (typeof value === "string") {
29097
+ return value.toLowerCase().includes(searchLower);
29098
+ }
29099
+ if (typeof value === "number") {
29100
+ return value.toString().includes(searchLower);
29101
+ }
29102
+ return false;
29041
29103
  }));
29042
29104
  }
29043
29105
  __name(filterBySearchParams, "filterBySearchParams");
@@ -29990,7 +30052,10 @@ var imageRouter = {
29990
30052
  let filteredImages = allImages;
29991
30053
  if (hasSearchTerm) {
29992
30054
  filteredImages = filterBySearchParams(filteredImages, queryInput.name, [
29993
- "name"
30055
+ "id",
30056
+ "name",
30057
+ "owner",
30058
+ "size"
29994
30059
  ]);
29995
30060
  }
29996
30061
  if (queryInput.visibility && queryInput.visibility !== "all") {
@@ -30070,7 +30135,10 @@ var imageRouter = {
30070
30135
  let filteredImages = allImages;
30071
30136
  if (queryInput.name && queryInput.name.trim()) {
30072
30137
  filteredImages = filterBySearchParams(filteredImages, queryInput.name, [
30073
- "name"
30138
+ "id",
30139
+ "name",
30140
+ "owner",
30141
+ "size"
30074
30142
  ]);
30075
30143
  }
30076
30144
  if (queryInput.visibility && queryInput.visibility !== "all") {
@@ -30609,6 +30677,7 @@ var imageRouter = {
30609
30677
  });
30610
30678
  if (name) {
30611
30679
  filteredImages = filterBySearchParams(filteredImages, name, [
30680
+ "id",
30612
30681
  "name"
30613
30682
  ]);
30614
30683
  }
@@ -31172,9 +31241,18 @@ function includesSearchTerm(flavor, searchTerm) {
31172
31241
  const searchableValues = [
31173
31242
  flavor.id,
31174
31243
  flavor.name,
31175
- flavor.description
31244
+ flavor.description,
31245
+ flavor.vcpus,
31246
+ flavor.ram,
31247
+ flavor.disk,
31248
+ flavor.swap
31176
31249
  ];
31177
- return searchableValues.some((value) => value != null && typeof value === "string" && regex.test(value));
31250
+ return searchableValues.some((value) => {
31251
+ if (value == null) return false;
31252
+ if (typeof value === "string") return regex.test(value);
31253
+ if (typeof value === "number") return regex.test(value.toString());
31254
+ return false;
31255
+ });
31178
31256
  }
31179
31257
  __name(includesSearchTerm, "includesSearchTerm");
31180
31258
  function filterAndSortFlavors(flavors, searchTerm, sortBy, sortDirection) {
@@ -31195,6 +31273,26 @@ function filterAndSortFlavors(flavors, searchTerm, sortBy, sortDirection) {
31195
31273
  return result;
31196
31274
  }
31197
31275
  __name(filterAndSortFlavors, "filterAndSortFlavors");
31276
+ function supportsDescriptionField(microversion) {
31277
+ const [major, minor] = microversion.split(".").map(Number);
31278
+ return major > 2 || major === 2 && minor >= 55;
31279
+ }
31280
+ __name(supportsDescriptionField, "supportsDescriptionField");
31281
+ async function getMaxMicroversion(compute) {
31282
+ try {
31283
+ const response = await compute.get("");
31284
+ const data = await response.json();
31285
+ if (data.version) {
31286
+ return data.version.version || "2.1";
31287
+ } else if (data.versions) {
31288
+ const currentVersion = data.versions.find((v) => v.status === "CURRENT");
31289
+ return currentVersion?.version || "2.1";
31290
+ }
31291
+ } catch {
31292
+ }
31293
+ return "2.1";
31294
+ }
31295
+ __name(getMaxMicroversion, "getMaxMicroversion");
31198
31296
  async function getFlavorById(compute, flavorId) {
31199
31297
  if (!flavorId || flavorId.trim() === "") {
31200
31298
  throw new import_server7.TRPCError({
@@ -31217,7 +31315,13 @@ async function getFlavorById(compute, flavorId) {
31217
31315
  }
31218
31316
  let response;
31219
31317
  try {
31220
- response = await compute.get(`flavors/${encodedId}`);
31318
+ const maxVersion = await getMaxMicroversion(compute);
31319
+ const options = supportsDescriptionField(maxVersion) ? {
31320
+ headers: {
31321
+ "OpenStack-API-Version": `compute ${maxVersion}`
31322
+ }
31323
+ } : void 0;
31324
+ response = await compute.get(`flavors/${encodedId}`, options);
31221
31325
  } catch (error) {
31222
31326
  if (error instanceof import_server7.TRPCError) throw error;
31223
31327
  const statusCode = getStatusCodeFromError(error);
@@ -31249,11 +31353,20 @@ __name(getFlavorById, "getFlavorById");
31249
31353
  async function fetchFlavors(compute, isPublic) {
31250
31354
  let response;
31251
31355
  try {
31252
- response = await compute.get("flavors/detail", {
31356
+ const maxVersion = await getMaxMicroversion(compute);
31357
+ const options = supportsDescriptionField(maxVersion) ? {
31358
+ queryParams: {
31359
+ is_public: isPublic
31360
+ },
31361
+ headers: {
31362
+ "OpenStack-API-Version": `compute ${maxVersion}`
31363
+ }
31364
+ } : {
31253
31365
  queryParams: {
31254
31366
  is_public: isPublic
31255
31367
  }
31256
- });
31368
+ };
31369
+ response = await compute.get("flavors/detail", options);
31257
31370
  } catch (error) {
31258
31371
  if (error instanceof import_server7.TRPCError) throw error;
31259
31372
  const statusCode = getStatusCodeFromError(error);
@@ -31282,16 +31395,32 @@ async function fetchFlavors(compute, isPublic) {
31282
31395
  }
31283
31396
  }
31284
31397
  __name(fetchFlavors, "fetchFlavors");
31285
- async function createFlavor(compute, flavorData) {
31398
+ async function createFlavor(compute, flavorData, microversion) {
31286
31399
  const requestBody = {
31287
31400
  flavor: flavorData
31288
31401
  };
31402
+ const headers = {};
31403
+ if (microversion && flavorData.description && supportsDescriptionField(microversion)) {
31404
+ headers["OpenStack-API-Version"] = `compute ${microversion}`;
31405
+ }
31289
31406
  let response;
31290
31407
  try {
31291
- response = await compute.post("flavors", requestBody);
31408
+ response = await compute.post("flavors", requestBody, Object.keys(headers).length ? {
31409
+ headers
31410
+ } : void 0);
31292
31411
  } catch (error) {
31293
31412
  if (error instanceof import_server7.TRPCError) throw error;
31294
31413
  const statusCode = getStatusCodeFromError(error);
31414
+ if (statusCode === 400) {
31415
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
31416
+ if (errorMessage.includes("description")) {
31417
+ throw new import_server7.TRPCError({
31418
+ code: "BAD_REQUEST",
31419
+ message: errorMessage,
31420
+ cause: error
31421
+ });
31422
+ }
31423
+ }
31295
31424
  handleHttpError(statusCode, CREATE_FLAVOR_STATUS_MAP, ERROR_CODES.CREATE_FLAVOR_FAILED);
31296
31425
  }
31297
31426
  if (!response.ok) {
@@ -31302,7 +31431,6 @@ async function createFlavor(compute, flavorData) {
31302
31431
  const jsonData = JSON.parse(rawData);
31303
31432
  return jsonData.flavor;
31304
31433
  } catch (error) {
31305
- console.error("Error parsing flavor response:", error);
31306
31434
  throw new import_server7.TRPCError({
31307
31435
  code: "INTERNAL_SERVER_ERROR",
31308
31436
  message: ERROR_CODES.CREATE_FLAVOR_FAILED,
@@ -31583,6 +31711,38 @@ __name(removeTenantAccess, "removeTenantAccess");
31583
31711
  var import_server8 = require("@trpc/server");
31584
31712
  var import_signal_openstack3 = __toESM(require_esm());
31585
31713
  var flavorRouter = {
31714
+ getComputeApiVersion: projectScopedProcedure.input(import_zod9.z.object({
31715
+ project_id: import_zod9.z.string()
31716
+ })).query(async ({ ctx }) => {
31717
+ try {
31718
+ const compute = ctx.openstack?.service("compute");
31719
+ if (!compute) {
31720
+ throw new import_server8.TRPCError({
31721
+ code: "INTERNAL_SERVER_ERROR",
31722
+ message: ERROR_CODES.COMPUTE_SERVICE_UNAVAILABLE
31723
+ });
31724
+ }
31725
+ const response = await compute.get("");
31726
+ const data = await response.json();
31727
+ let versionString = "2.1";
31728
+ if (data.version) {
31729
+ versionString = data.version.version || "2.1";
31730
+ } else if (data.versions) {
31731
+ const currentVersion = data.versions.find((v) => v.status === "CURRENT");
31732
+ versionString = currentVersion?.version || "2.1";
31733
+ }
31734
+ return {
31735
+ version: versionString,
31736
+ supportsDescription: supportsDescriptionField(versionString)
31737
+ };
31738
+ } catch (error) {
31739
+ console.error("Failed to detect Nova version:", error);
31740
+ return {
31741
+ version: "2.1",
31742
+ supportsDescription: false
31743
+ };
31744
+ }
31745
+ }),
31586
31746
  getFlavorById: projectScopedProcedure.input(import_zod9.z.object({
31587
31747
  project_id: import_zod9.z.string(),
31588
31748
  flavorId: import_zod9.z.string()
@@ -31670,7 +31830,8 @@ var flavorRouter = {
31670
31830
  swap: import_zod9.z.number().optional(),
31671
31831
  rxtx_factor: import_zod9.z.number().optional(),
31672
31832
  "OS-FLV-EXT-DATA:ephemeral": import_zod9.z.number().optional(),
31673
- "os-flavor-access:is_public": import_zod9.z.boolean().optional()
31833
+ "os-flavor-access:is_public": import_zod9.z.boolean().optional(),
31834
+ description: import_zod9.z.string().optional()
31674
31835
  })
31675
31836
  })).mutation(async ({ input, ctx }) => {
31676
31837
  try {
@@ -31682,11 +31843,35 @@ var flavorRouter = {
31682
31843
  message: ERROR_CODES.COMPUTE_SERVICE_UNAVAILABLE
31683
31844
  });
31684
31845
  }
31846
+ let supportsDescription = false;
31847
+ let detectedVersion = "2.1";
31848
+ try {
31849
+ const versionResponse = await compute.get("");
31850
+ const versionData = await versionResponse.json();
31851
+ let versionString = "2.1";
31852
+ if (versionData.version) {
31853
+ versionString = versionData.version.version || "2.1";
31854
+ } else if (versionData.versions) {
31855
+ const currentVersion = versionData.versions.find((v) => v.status === "CURRENT");
31856
+ versionString = currentVersion?.version || "2.1";
31857
+ }
31858
+ detectedVersion = versionString;
31859
+ supportsDescription = supportsDescriptionField(versionString);
31860
+ } catch {
31861
+ supportsDescription = false;
31862
+ }
31863
+ const flavorToSend = supportsDescription ? flavor : {
31864
+ ...flavor
31865
+ };
31866
+ if (!supportsDescription) {
31867
+ const { description, ...rest } = flavorToSend;
31868
+ Object.assign(flavorToSend, rest);
31869
+ }
31685
31870
  const flavorData = {
31686
- ...flavor,
31871
+ ...flavorToSend,
31687
31872
  "OS-FLV-EXT-DATA:ephemeral": flavor["OS-FLV-EXT-DATA:ephemeral"] || 0
31688
31873
  };
31689
- const result = await createFlavor(compute, flavorData);
31874
+ const result = await createFlavor(compute, flavorData, supportsDescription ? detectedVersion : void 0);
31690
31875
  return result;
31691
31876
  } catch (error) {
31692
31877
  if (error instanceof import_server8.TRPCError) {
@@ -34419,6 +34604,16 @@ __name(resolveEC2Credential, "resolveEC2Credential");
34419
34604
 
34420
34605
  // src/server/Storage/clients/s3Client.ts
34421
34606
  var import_client_s3 = require("@aws-sdk/client-s3");
34607
+
34608
+ // src/server/Storage/constants.ts
34609
+ var S3_MAX_KEYS_PER_REQUEST = 1e3;
34610
+ var S3_PRESIGN_MAX_EXPIRY_SECONDS = 604800;
34611
+ var S3_MAX_SCAN_PAGES = 20;
34612
+ var S3_CONNECTION_TIMEOUT_MS = 5e3;
34613
+ var S3_MAX_BUFFERED_VERSIONS_PER_KEY = 2e4;
34614
+ var MAX_REPORTED_DELETE_ERRORS = 100;
34615
+
34616
+ // src/server/Storage/clients/s3Client.ts
34422
34617
  function createS3Client(access, secret, endpoint, region = "default") {
34423
34618
  if (!access?.trim()) {
34424
34619
  throw new Error("S3 access key is required");
@@ -34436,7 +34631,10 @@ function createS3Client(access, secret, endpoint, region = "default") {
34436
34631
  accessKeyId: access,
34437
34632
  secretAccessKey: secret
34438
34633
  },
34439
- forcePathStyle: true
34634
+ forcePathStyle: true,
34635
+ requestHandler: {
34636
+ connectionTimeout: S3_CONNECTION_TIMEOUT_MS
34637
+ }
34440
34638
  });
34441
34639
  }
34442
34640
  __name(createS3Client, "createS3Client");
@@ -34547,6 +34745,8 @@ var cephUploadProcedure = protectedProcedure.use(/* @__PURE__ */ __name(async fu
34547
34745
  var import_server13 = require("@trpc/server");
34548
34746
  var import_signal_openstack4 = __toESM(require_esm());
34549
34747
  var S3_ERROR_MAP = {
34748
+ NotFound: "NOT_FOUND",
34749
+ Forbidden: "FORBIDDEN",
34550
34750
  NoSuchBucket: "NOT_FOUND",
34551
34751
  NoSuchKey: "NOT_FOUND",
34552
34752
  NoSuchUpload: "NOT_FOUND",
@@ -34619,12 +34819,6 @@ __name(mapS3ErrorToTRPCError, "mapS3ErrorToTRPCError");
34619
34819
 
34620
34820
  // src/server/Storage/types/ceph.ts
34621
34821
  var import_zod13 = require("zod");
34622
-
34623
- // src/server/Storage/constants.ts
34624
- var S3_MAX_KEYS_PER_REQUEST = 1e3;
34625
- var S3_PRESIGN_MAX_EXPIRY_SECONDS = 604800;
34626
-
34627
- // src/server/Storage/types/ceph.ts
34628
34822
  var ec2CredentialSchema = import_zod13.z.object({
34629
34823
  id: import_zod13.z.string(),
34630
34824
  access: import_zod13.z.string(),
@@ -34660,6 +34854,59 @@ var createBucketOutputSchema = import_zod13.z.object({
34660
34854
  var deleteBucketInputSchema = projectScopedInputSchema.extend({
34661
34855
  bucketName: import_zod13.z.string().min(1)
34662
34856
  });
34857
+ var headBucketInputSchema = projectScopedInputSchema.extend({
34858
+ bucketName: existingBucketNameSchema
34859
+ });
34860
+ var bucketStateInputSchema = projectScopedInputSchema.extend({
34861
+ bucketName: existingBucketNameSchema
34862
+ });
34863
+ var bucketStateOutputSchema = import_zod13.z.object({
34864
+ /**
34865
+ * Raw versioning status, straight from `GetBucketVersioning` — the same value
34866
+ * `versioning.getStatus` reports.
34867
+ *
34868
+ * Exposed here so a caller that needs to tell "Suspended" from "Unversioned" (the bucket
34869
+ * header renders a different badge for each) does not have to issue a second
34870
+ * `GetBucketVersioning` alongside this one. `isVersioningEnabled` below is the collapsed
34871
+ * form most callers actually want.
34872
+ */
34873
+ status: import_zod13.z.enum([
34874
+ "Enabled",
34875
+ "Suspended",
34876
+ "Unversioned"
34877
+ ]),
34878
+ isVersioningEnabled: import_zod13.z.boolean(),
34879
+ /**
34880
+ * No current (non-delete-marker) objects. Always exact, never affected by `isPartialScan`:
34881
+ * it comes from a dedicated one-key `ListObjectsV2`, which by definition lists only current
34882
+ * objects and omits delete markers, so "no keys returned" *is* emptiness.
34883
+ *
34884
+ * Deliberately a boolean, not a count - no caller ever needed more than the yes/no, and a
34885
+ * one-key probe has no count to give.
34886
+ */
34887
+ isEmpty: import_zod13.z.boolean(),
34888
+ /**
34889
+ * Every key in the bucket is hidden behind a delete marker: versioning is on, the scan saw
34890
+ * entries, and none of them was a real (non-delete-marker) version.
34891
+ *
34892
+ * Forced to `false` under `isPartialScan`, because a truncated scan that happened to see only
34893
+ * delete markers cannot tell "no real versions exist" from "no real versions yet".
34894
+ */
34895
+ hasOnlyDeleteMarkers: import_zod13.z.boolean(),
34896
+ /** At least one non-current version or delete marker exists. Reliable when true. */
34897
+ hasOldVersionsOrDeleteMarkers: import_zod13.z.boolean(),
34898
+ /**
34899
+ * True when the version-history scan stopped before it could rule the history flags in either
34900
+ * direction: it hit the page ceiling, the request was aborted, or S3 returned a truncated
34901
+ * page carrying no continuation marker to follow.
34902
+ *
34903
+ * It says nothing about `isEmpty` or `isVersioningEnabled`, which are established by their own
34904
+ * single requests. Of the two flags it does cover, `hasOldVersionsOrDeleteMarkers: true`
34905
+ * remains reliable - finding one such entry is proof - while `false` means "none found so
34906
+ * far", and `hasOnlyDeleteMarkers` is forced to `false` outright.
34907
+ */
34908
+ isPartialScan: import_zod13.z.boolean()
34909
+ });
34663
34910
  var s3StatusSchema = import_zod13.z.object({
34664
34911
  hasCredentials: import_zod13.z.boolean()
34665
34912
  });
@@ -34727,6 +34974,9 @@ var deleteVersionsBulkInputSchema = projectScopedInputSchema.extend({
34727
34974
  versionId: import_zod13.z.string().min(1)
34728
34975
  })).min(1).max(1e4)
34729
34976
  });
34977
+ var deleteNonCurrentVersionsInputSchema = projectScopedInputSchema.extend({
34978
+ containerName: existingBucketNameSchema
34979
+ });
34730
34980
  var deletedObjectSchema = import_zod13.z.object({
34731
34981
  key: import_zod13.z.string(),
34732
34982
  versionId: import_zod13.z.string().optional(),
@@ -34745,6 +34995,22 @@ var deleteObjectsBulkOutputSchema = import_zod13.z.object({
34745
34995
  deletedCount: import_zod13.z.number().int().nonnegative(),
34746
34996
  errorCount: import_zod13.z.number().int().nonnegative()
34747
34997
  });
34998
+ var deleteNonCurrentVersionsOutputSchema = import_zod13.z.object({
34999
+ errors: import_zod13.z.array(deleteObjectErrorSchema),
35000
+ deletedCount: import_zod13.z.number().int().nonnegative(),
35001
+ errorCount: import_zod13.z.number().int().nonnegative(),
35002
+ /**
35003
+ * The run did not cover the whole bucket, so non-current versions may survive.
35004
+ * Set when the request was aborted (including during the last page's deletes),
35005
+ * when S3 returned a truncated page without a continuation marker, and when a
35006
+ * key had to be skipped (NoCurrentVersion, MissingVersionId, TooManyVersions) -
35007
+ * the last of which also records an entry in `errors`, so a caller must treat
35008
+ * this flag and `errorCount` as independent rather than as alternatives.
35009
+ * The counts remain accurate for what was actually deleted - this flag only
35010
+ * denies that the wipe was exhaustive, which a bare success count would imply.
35011
+ */
35012
+ isPartial: import_zod13.z.boolean()
35013
+ });
34748
35014
  var createFolderInputSchema2 = projectScopedInputSchema.extend({
34749
35015
  containerName: import_zod13.z.string().min(1),
34750
35016
  folderPath: import_zod13.z.string().min(1)
@@ -34863,19 +35129,6 @@ var bucketPolicyStatementSchema = import_zod13.z.object({
34863
35129
  message: "Principal object must contain at least one of: AWS, Service, or Federated"
34864
35130
  });
34865
35131
  }
34866
- if (typeof val === "object" && val.AWS) {
34867
- const arns = Array.isArray(val.AWS) ? val.AWS : [
34868
- val.AWS
34869
- ];
34870
- for (const arn of arns) {
34871
- if (arn !== "*" && !/^arn:aws:iam::\d{12}:(?:root|user\/.+|role\/.+)$/.test(arn)) {
34872
- ctx.addIssue({
34873
- code: "custom",
34874
- message: `Invalid AWS principal ARN format: ${arn}. Expected arn:aws:iam::ACCOUNT-ID:root or arn:aws:iam::ACCOUNT-ID:(user|role)/NAME`
34875
- });
34876
- }
34877
- }
34878
- }
34879
35132
  }),
34880
35133
  NotPrincipal: import_zod13.z.union([
34881
35134
  import_zod13.z.string(),
@@ -35272,6 +35525,193 @@ var containerRouter = {
35272
35525
  }
35273
35526
  }),
35274
35527
  /**
35528
+ * Existence probe for a single bucket — one HeadBucket request, no body.
35529
+ *
35530
+ * Exists for route guards that must answer "is there such a bucket?" before rendering a
35531
+ * bucket page. `list` would answer it too, but ListBuckets only returns buckets the
35532
+ * caller *owns*: a bucket reachable through a policy would read as missing. HeadBucket
35533
+ * asks about the bucket itself, so 404 means 404 and 403 means 403.
35534
+ *
35535
+ * @throws TRPCError NOT_FOUND - bucket does not exist (SDK error name `NotFound`)
35536
+ * @throws TRPCError FORBIDDEN - no credentials, or the bucket exists but is not readable
35537
+ */
35538
+ head: cephProtectedProcedure.input(headBucketInputSchema).query(async ({ ctx, input }) => {
35539
+ const s3 = ctx.getCephClient();
35540
+ const { bucketName } = input;
35541
+ try {
35542
+ await s3.send(new import_client_s32.HeadBucketCommand({
35543
+ Bucket: bucketName
35544
+ }));
35545
+ return {
35546
+ exists: true
35547
+ };
35548
+ } catch (error) {
35549
+ throw mapS3ErrorToTRPCError(error, {
35550
+ operation: "head bucket",
35551
+ bucket: bucketName
35552
+ });
35553
+ }
35554
+ }),
35555
+ /**
35556
+ * Authoritative bucket emptiness/version state.
35557
+ *
35558
+ * Replaces three separate client-side probes (`useBucketInfo`, `DeleteBucketModal`,
35559
+ * `EmptyBucketModal`) that each read only the first page of `objects.list` and derived
35560
+ * `isBucketEmpty`/`hasOldVersionsOrDeleteMarkers` from it — silently wrong for buckets whose
35561
+ * first 100 (or 1000) entries don't tell the whole story.
35562
+ *
35563
+ * Cost, honestly:
35564
+ * - unversioned bucket: **exactly two** requests, whatever its size. Without a version
35565
+ * history there is nothing to scan, so both history flags are false by definition.
35566
+ * - versioned bucket with both an old version (or delete marker) and a surviving real
35567
+ * version: **three** — one page settles both history flags and the scan stops.
35568
+ * - versioned bucket whose history is clean: the scan has nothing to find and no way to know
35569
+ * that early, so it runs to the end of the bucket or to `S3_MAX_SCAN_PAGES`.
35570
+ * - versioned bucket holding *only* delete markers, with no surviving non-current version —
35571
+ * what a `NoncurrentVersionExpiration` lifecycle rule leaves behind once it has expired the
35572
+ * real versions: same cost as the clean-history shape, and for the same reason. The scan
35573
+ * sees `hasOldVersionsOrDeleteMarkers` on page one but can only prove `hasOnlyDeleteMarkers`
35574
+ * by reaching the end, since any later page could still hold a real version.
35575
+ *
35576
+ * The last two are the shapes that cost real round-trips. Both are bounded, and in both the
35577
+ * scan is doing work that has an answer to produce — it is not spinning to re-learn something
35578
+ * already known. Callers that force `staleTime: 0` (the two destructive modals, deliberately)
35579
+ * pay it on every open.
35580
+ *
35581
+ * `isEmpty` is deliberately established by its own one-key `ListObjectsV2` rather than by the
35582
+ * scan: that command lists current objects only, so it answers emptiness exactly, in one
35583
+ * request, on a bucket of any size. That matters beyond speed — emptiness gates "Delete
35584
+ * Bucket", and deriving it from a scan that may be truncated left large buckets permanently
35585
+ * unconfirmable and therefore permanently undeletable, with a "refresh and try again" that
35586
+ * could never succeed.
35587
+ *
35588
+ * Versioning status is read from S3 here, not taken from client input, so the answer can't be
35589
+ * computed against a stale client-cached status. It is also returned raw as `status`, so a
35590
+ * caller needing the three-way value has no reason to issue a second `GetBucketVersioning`
35591
+ * against `versioning.getStatus` alongside this call.
35592
+ *
35593
+ * @throws TRPCError NOT_FOUND - bucket does not exist
35594
+ * @throws TRPCError FORBIDDEN - no credentials or access denied
35595
+ */
35596
+ getState: cephProtectedProcedure.input(bucketStateInputSchema).query(async ({ ctx, input }) => {
35597
+ const s3 = ctx.getCephClient();
35598
+ const { bucketName } = input;
35599
+ let status;
35600
+ let isVersioningEnabled;
35601
+ let isEmpty;
35602
+ try {
35603
+ const [versioningResponse, currentObjectsResponse] = await Promise.all([
35604
+ s3.send(new import_client_s32.GetBucketVersioningCommand({
35605
+ Bucket: bucketName
35606
+ }), {
35607
+ abortSignal: ctx.req.signal
35608
+ }),
35609
+ s3.send(new import_client_s32.ListObjectsV2Command({
35610
+ Bucket: bucketName,
35611
+ MaxKeys: 1
35612
+ }), {
35613
+ abortSignal: ctx.req.signal
35614
+ })
35615
+ ]);
35616
+ status = versioningResponse.Status ?? "Unversioned";
35617
+ isVersioningEnabled = status === "Enabled" || status === "Suspended";
35618
+ isEmpty = (currentObjectsResponse.KeyCount ?? currentObjectsResponse.Contents?.length ?? 0) === 0;
35619
+ } catch (error) {
35620
+ console.error("getState: failed to read bucket versioning status or emptiness", {
35621
+ bucket: bucketName,
35622
+ error
35623
+ });
35624
+ throw mapS3ErrorToTRPCError(error, {
35625
+ operation: "get bucket state",
35626
+ bucket: bucketName
35627
+ });
35628
+ }
35629
+ if (!isVersioningEnabled) {
35630
+ return bucketStateOutputSchema.parse({
35631
+ status,
35632
+ isVersioningEnabled: false,
35633
+ isEmpty,
35634
+ hasOnlyDeleteMarkers: false,
35635
+ hasOldVersionsOrDeleteMarkers: false,
35636
+ isPartialScan: false
35637
+ });
35638
+ }
35639
+ let hasOldVersionOrDeleteMarker = false;
35640
+ let hasRealVersion = false;
35641
+ let anyEntrySeen = false;
35642
+ let keyMarker;
35643
+ let versionIdMarker;
35644
+ let pages = 0;
35645
+ let isPartialScan = false;
35646
+ while (true) {
35647
+ if (ctx.req.signal?.aborted) {
35648
+ isPartialScan = true;
35649
+ break;
35650
+ }
35651
+ let response;
35652
+ try {
35653
+ response = await s3.send(new import_client_s32.ListObjectVersionsCommand({
35654
+ Bucket: bucketName,
35655
+ MaxKeys: S3_MAX_KEYS_PER_REQUEST,
35656
+ KeyMarker: keyMarker,
35657
+ VersionIdMarker: versionIdMarker
35658
+ }), {
35659
+ abortSignal: ctx.req.signal
35660
+ });
35661
+ } catch (error) {
35662
+ if (ctx.req.signal?.aborted) {
35663
+ isPartialScan = true;
35664
+ break;
35665
+ }
35666
+ console.error("getState: version scan failed", {
35667
+ bucket: bucketName,
35668
+ pages,
35669
+ error
35670
+ });
35671
+ throw mapS3ErrorToTRPCError(error, {
35672
+ operation: "get bucket state",
35673
+ bucket: bucketName
35674
+ });
35675
+ }
35676
+ for (const v of response.Versions ?? []) {
35677
+ anyEntrySeen = true;
35678
+ hasRealVersion = true;
35679
+ if (!v.IsLatest) {
35680
+ hasOldVersionOrDeleteMarker = true;
35681
+ }
35682
+ }
35683
+ if ((response.DeleteMarkers?.length ?? 0) > 0) {
35684
+ anyEntrySeen = true;
35685
+ hasOldVersionOrDeleteMarker = true;
35686
+ }
35687
+ pages++;
35688
+ if (hasOldVersionOrDeleteMarker && hasRealVersion) {
35689
+ break;
35690
+ }
35691
+ if (!response.IsTruncated) {
35692
+ break;
35693
+ }
35694
+ if (!response.NextKeyMarker) {
35695
+ isPartialScan = true;
35696
+ break;
35697
+ }
35698
+ if (pages >= S3_MAX_SCAN_PAGES) {
35699
+ isPartialScan = true;
35700
+ break;
35701
+ }
35702
+ keyMarker = response.NextKeyMarker;
35703
+ versionIdMarker = response.NextVersionIdMarker;
35704
+ }
35705
+ return bucketStateOutputSchema.parse({
35706
+ status,
35707
+ isVersioningEnabled,
35708
+ isEmpty,
35709
+ hasOnlyDeleteMarkers: !isPartialScan && anyEntrySeen && !hasRealVersion,
35710
+ hasOldVersionsOrDeleteMarkers: hasOldVersionOrDeleteMarker,
35711
+ isPartialScan
35712
+ });
35713
+ }),
35714
+ /**
35275
35715
  * Create a new S3 bucket.
35276
35716
  *
35277
35717
  * Uses AWS SDK CreateBucketCommand. The AWS SDK automatically adds LocationConstraint
@@ -35920,6 +36360,232 @@ var objectRouter = {
35920
36360
  return bulkDeleteItems(s3, containerName, uniqueVersions, ctx.req.signal, "delete versions");
35921
36361
  }),
35922
36362
  /**
36363
+ * Delete every non-current version and delete marker in a bucket, keeping
36364
+ * each key's current live version intact ("Delete Versions" bucket action).
36365
+ *
36366
+ * Deliberately NOT built on top of `deleteAll` (which the "Empty Bucket"
36367
+ * checkbox also uses): that procedure's full-wipe branch discards
36368
+ * `IsLatest` entirely and re-scans from scratch whenever a page is
36369
+ * exhausted, specifically so it can catch delete markers created by its
36370
+ * own non-versioned-delete branch. Bolting a third "keep the latest"
36371
+ * mode onto that already-intricate loop would risk regressing the other
36372
+ * two. This procedure keeps its own single-pass scan instead.
36373
+ *
36374
+ * ⚠️ IsLatest correctness across pages:
36375
+ * `IsLatest` is computed by S3 at *list time*, not stored permanently. If a
36376
+ * key's versions are split across two list pages and we deleted anything
36377
+ * from that key after page 1 but before fetching page 2, the remaining
36378
+ * version would be re-evaluated as IsLatest=true on page 2 - resurrecting
36379
+ * a version (or worse, a deleted object) that should have stayed removed.
36380
+ * To avoid this, a key's records are only ever acted on once its full
36381
+ * group has been seen: the last key-group on every truncated page is
36382
+ * buffered (`pendingKey`/`pendingItems`) and merged with the next page's
36383
+ * leading group (S3 returns Versions/DeleteMarkers sorted by key, so a
36384
+ * split key's remaining records always appear at the very start of the
36385
+ * next page) before that group is decided on and deleted.
36386
+ *
36387
+ * Per completed key-group: if the current (IsLatest) record is a regular
36388
+ * version, every other record in the group is deleted and the current
36389
+ * version is kept. If the current record is a delete marker, the entire
36390
+ * group - including the marker - is deleted, since a versioned bucket has
36391
+ * no other way to represent "this key was deleted" once its history is
36392
+ * gone; keeping the marker without its superseded versions would leave the
36393
+ * key listed in the "Deleted" tab with nothing left to restore.
36394
+ *
36395
+ * No second pass is needed (unlike `deleteAll`'s full-wipe branch): every
36396
+ * deletion here targets a specific `VersionId`, which never creates a new
36397
+ * delete marker, so nothing new can appear behind the scan as it
36398
+ * progresses. One pass over the bucket is exhaustive.
36399
+ *
36400
+ * @throws TRPCError NOT_FOUND - bucket does not exist
36401
+ * @throws TRPCError FORBIDDEN - no credentials or access denied
36402
+ */
36403
+ deleteNonCurrentVersions: cephProtectedProcedure.input(deleteNonCurrentVersionsInputSchema).mutation(async ({ ctx, input }) => {
36404
+ const s3 = ctx.getCephClient();
36405
+ const { containerName } = input;
36406
+ const errors = [];
36407
+ let deletedCount = 0;
36408
+ let errorCount = 0;
36409
+ let isPartial = false;
36410
+ const recordErrors = /* @__PURE__ */ __name((newErrors) => {
36411
+ errorCount += newErrors.length;
36412
+ for (const error of newErrors) {
36413
+ if (errors.length >= MAX_REPORTED_DELETE_ERRORS) break;
36414
+ errors.push(error);
36415
+ }
36416
+ }, "recordErrors");
36417
+ let keyMarker;
36418
+ let versionIdMarker;
36419
+ let pendingKey;
36420
+ let pendingItems = [];
36421
+ let sameMarkerCount = 0;
36422
+ const MAX_SAME_MARKER = 5;
36423
+ let skipKey;
36424
+ while (true) {
36425
+ if (ctx.req.signal?.aborted) {
36426
+ isPartial = true;
36427
+ break;
36428
+ }
36429
+ let response;
36430
+ try {
36431
+ response = await s3.send(new import_client_s33.ListObjectVersionsCommand({
36432
+ Bucket: containerName,
36433
+ MaxKeys: S3_MAX_KEYS_PER_REQUEST,
36434
+ KeyMarker: keyMarker,
36435
+ VersionIdMarker: versionIdMarker
36436
+ }), {
36437
+ abortSignal: ctx.req.signal
36438
+ });
36439
+ } catch (error) {
36440
+ if (ctx.req.signal?.aborted) {
36441
+ isPartial = true;
36442
+ break;
36443
+ }
36444
+ throw mapS3ErrorToTRPCError(error, {
36445
+ operation: "delete non-current versions",
36446
+ bucket: containerName
36447
+ });
36448
+ }
36449
+ const combined = [
36450
+ ...(response.Versions ?? []).map((v) => ({
36451
+ Key: v.Key ?? "",
36452
+ VersionId: v.VersionId,
36453
+ IsLatest: v.IsLatest ?? false,
36454
+ isDeleteMarker: false
36455
+ })),
36456
+ ...(response.DeleteMarkers ?? []).map((dm) => ({
36457
+ Key: dm.Key ?? "",
36458
+ VersionId: dm.VersionId,
36459
+ IsLatest: dm.IsLatest ?? false,
36460
+ isDeleteMarker: true
36461
+ }))
36462
+ ].sort((a, b) => a.Key < b.Key ? -1 : a.Key > b.Key ? 1 : 0);
36463
+ const pageGroups = [];
36464
+ for (const item of combined) {
36465
+ const last = pageGroups[pageGroups.length - 1];
36466
+ if (last && last.key === item.Key) {
36467
+ last.items.push(item);
36468
+ } else {
36469
+ pageGroups.push({
36470
+ key: item.Key,
36471
+ items: [
36472
+ item
36473
+ ]
36474
+ });
36475
+ }
36476
+ }
36477
+ if (pendingItems.length > 0) {
36478
+ if (pageGroups.length > 0 && pageGroups[0].key === pendingKey) {
36479
+ pageGroups[0].items = [
36480
+ ...pendingItems,
36481
+ ...pageGroups[0].items
36482
+ ];
36483
+ } else {
36484
+ pageGroups.unshift({
36485
+ key: pendingKey,
36486
+ items: pendingItems
36487
+ });
36488
+ }
36489
+ pendingItems = [];
36490
+ pendingKey = void 0;
36491
+ }
36492
+ if (skipKey !== void 0) {
36493
+ const remaining = pageGroups.filter((group) => group.key !== skipKey);
36494
+ if (remaining.length === pageGroups.length) skipKey = void 0;
36495
+ pageGroups.length = 0;
36496
+ pageGroups.push(...remaining);
36497
+ }
36498
+ let groupsToProcess = pageGroups;
36499
+ if (response.IsTruncated && response.NextKeyMarker && pageGroups.length > 0) {
36500
+ const markerIndex = pageGroups.findIndex((group) => group.key === response.NextKeyMarker);
36501
+ const index = markerIndex >= 0 ? markerIndex : pageGroups.length - 1;
36502
+ const deferredGroup = pageGroups[index];
36503
+ groupsToProcess = pageGroups.filter((_, i) => i !== index);
36504
+ if (deferredGroup.items.length > S3_MAX_BUFFERED_VERSIONS_PER_KEY) {
36505
+ console.error(`[deleteNonCurrentVersions] Abandoning key with more than ${S3_MAX_BUFFERED_VERSIONS_PER_KEY} versions in bucket ${containerName}`);
36506
+ recordErrors([
36507
+ {
36508
+ key: deferredGroup.key,
36509
+ code: "TooManyVersions",
36510
+ message: `Key has more than ${S3_MAX_BUFFERED_VERSIONS_PER_KEY} versions and was skipped`
36511
+ }
36512
+ ]);
36513
+ isPartial = true;
36514
+ skipKey = deferredGroup.key;
36515
+ } else {
36516
+ pendingKey = deferredGroup.key;
36517
+ pendingItems = deferredGroup.items;
36518
+ }
36519
+ }
36520
+ const itemsToDelete = [];
36521
+ for (const group of groupsToProcess) {
36522
+ const current = group.items.find((item) => item.IsLatest);
36523
+ if (current === void 0) {
36524
+ console.error(`[deleteNonCurrentVersions] Skipping key with no current version in bucket ${containerName}`);
36525
+ recordErrors([
36526
+ {
36527
+ key: group.key,
36528
+ code: "NoCurrentVersion",
36529
+ message: "No version flagged as current; skipped to avoid deleting the live object"
36530
+ }
36531
+ ]);
36532
+ isPartial = true;
36533
+ continue;
36534
+ }
36535
+ const keepCurrentVersion = !current.isDeleteMarker;
36536
+ for (const item of group.items) {
36537
+ if (keepCurrentVersion && item === current) continue;
36538
+ if (!item.VersionId) {
36539
+ console.error(`[deleteNonCurrentVersions] Skipping item without VersionId in bucket ${containerName}`);
36540
+ recordErrors([
36541
+ {
36542
+ key: item.Key,
36543
+ code: "MissingVersionId",
36544
+ message: "Version record carried no VersionId; skipped to avoid creating a delete marker"
36545
+ }
36546
+ ]);
36547
+ isPartial = true;
36548
+ continue;
36549
+ }
36550
+ itemsToDelete.push({
36551
+ key: item.Key,
36552
+ versionId: item.VersionId
36553
+ });
36554
+ }
36555
+ }
36556
+ if (itemsToDelete.length > 0) {
36557
+ const pageResult = await bulkDeleteItems(s3, containerName, itemsToDelete, ctx.req.signal, "delete non-current versions");
36558
+ deletedCount += pageResult.deletedCount;
36559
+ recordErrors(pageResult.errors);
36560
+ }
36561
+ if (!response.IsTruncated || !response.NextKeyMarker) {
36562
+ if (response.IsTruncated || ctx.req.signal?.aborted) isPartial = true;
36563
+ break;
36564
+ }
36565
+ const currentMarkerKey = `${keyMarker ?? ""}:${versionIdMarker ?? ""}`;
36566
+ const newMarkerKey = `${response.NextKeyMarker ?? ""}:${response.NextVersionIdMarker ?? ""}`;
36567
+ if (currentMarkerKey === newMarkerKey) {
36568
+ sameMarkerCount++;
36569
+ if (sameMarkerCount >= MAX_SAME_MARKER) {
36570
+ throw new import_server14.TRPCError({
36571
+ code: "INTERNAL_SERVER_ERROR",
36572
+ message: `Pagination stalled: markers not advancing after ${MAX_SAME_MARKER} iterations while deleting non-current versions from bucket ${containerName}.`
36573
+ });
36574
+ }
36575
+ } else {
36576
+ sameMarkerCount = 0;
36577
+ }
36578
+ keyMarker = response.NextKeyMarker;
36579
+ versionIdMarker = response.NextVersionIdMarker;
36580
+ }
36581
+ return deleteNonCurrentVersionsOutputSchema.parse({
36582
+ errors,
36583
+ deletedCount,
36584
+ errorCount,
36585
+ isPartial
36586
+ });
36587
+ }),
36588
+ /**
35923
36589
  * Create a folder (zero-byte object with key ending in "/").
35924
36590
  *
35925
36591
  * In S3, folders are virtual - they're just zero-byte objects with keys ending in "/".
@@ -36688,7 +37354,6 @@ var ec2CredentialRouter = {
36688
37354
 
36689
37355
  // src/server/Storage/routers/ceph/versioningRouter.ts
36690
37356
  var import_client_s34 = require("@aws-sdk/client-s3");
36691
- var import_zod17 = require("zod");
36692
37357
 
36693
37358
  // src/server/Storage/types/versioning.ts
36694
37359
  var import_zod16 = require("zod");
@@ -36761,6 +37426,72 @@ var listObjectVersionsInputSchema = projectScopedInputSchema.extend({
36761
37426
  bucket: import_zod16.z.string().min(1),
36762
37427
  key: import_zod16.z.string().min(1)
36763
37428
  });
37429
+ var checkDeletedContentInputSchema = projectScopedInputSchema.extend({
37430
+ bucket: import_zod16.z.string().min(1),
37431
+ prefix: import_zod16.z.string().optional(),
37432
+ folders: import_zod16.z.array(import_zod16.z.string()).max(1e3).optional()
37433
+ }).refine((input) => input.prefix !== void 0 || (input.folders?.length ?? 0) > 0, {
37434
+ message: "Either prefix or a non-empty folders array is required",
37435
+ path: [
37436
+ "prefix"
37437
+ ]
37438
+ }).refine((input) => input.folders?.every((folder) => folder.startsWith(input.prefix ?? "")) ?? true, {
37439
+ message: "Every folder must live under prefix",
37440
+ path: [
37441
+ "folders"
37442
+ ]
37443
+ });
37444
+ var checkDeletedContentOutputSchema = import_zod16.z.array(import_zod16.z.object({
37445
+ prefix: import_zod16.z.string(),
37446
+ hasDeletedContent: import_zod16.z.boolean(),
37447
+ isFolderDeleted: import_zod16.z.boolean(),
37448
+ folderDeleteMarkerVersionId: import_zod16.z.string().optional(),
37449
+ folderMarkerVersionId: import_zod16.z.string().optional(),
37450
+ isPartialScan: import_zod16.z.boolean()
37451
+ }));
37452
+
37453
+ // src/server/Storage/helpers/versionScan.ts
37454
+ function folderPrefixOf(key, prefix) {
37455
+ if (!key.startsWith(prefix)) return void 0;
37456
+ const rest = key.slice(prefix.length);
37457
+ const i = rest.indexOf("/");
37458
+ return i === -1 ? void 0 : prefix + rest.slice(0, i + 1);
37459
+ }
37460
+ __name(folderPrefixOf, "folderPrefixOf");
37461
+ function isFolderCovered(folderPrefix, stoppedAtKey) {
37462
+ return stoppedAtKey === void 0 ? true : folderPrefix < stoppedAtKey && !stoppedAtKey.startsWith(folderPrefix);
37463
+ }
37464
+ __name(isFolderCovered, "isFolderCovered");
37465
+ function longestCommonPrefix(folders) {
37466
+ if (folders.length === 0) return "";
37467
+ let common = folders[0];
37468
+ for (let i = 1; i < folders.length && common.length > 0; i++) {
37469
+ const folder = folders[i];
37470
+ let j = 0;
37471
+ while (j < common.length && j < folder.length && common[j] === folder[j]) j++;
37472
+ common = common.slice(0, j);
37473
+ }
37474
+ if (common.endsWith("/") && folders.some((folder) => folder === common)) {
37475
+ common = common.slice(0, -1);
37476
+ }
37477
+ const lastSlash = common.lastIndexOf("/");
37478
+ return lastSlash === -1 ? "" : common.slice(0, lastSlash + 1);
37479
+ }
37480
+ __name(longestCommonPrefix, "longestCommonPrefix");
37481
+ function greatestKeyOnPage(versions, deleteMarkers) {
37482
+ let greatest;
37483
+ for (const entries of [
37484
+ versions,
37485
+ deleteMarkers
37486
+ ]) {
37487
+ for (const entry of entries ?? []) {
37488
+ const key = entry.Key;
37489
+ if (key !== void 0 && (greatest === void 0 || key > greatest)) greatest = key;
37490
+ }
37491
+ }
37492
+ return greatest;
37493
+ }
37494
+ __name(greatestKeyOnPage, "greatestKeyOnPage");
36764
37495
 
36765
37496
  // src/server/Storage/routers/ceph/versioningRouter.ts
36766
37497
  var versioningRouter = {
@@ -37006,104 +37737,144 @@ var versioningRouter = {
37006
37737
  /**
37007
37738
  * Check if folders contain deleted files (files with delete markers) or if the folder marker itself is deleted.
37008
37739
  *
37009
- * For each folder, checks:
37010
- * 1. If the folder marker itself (key ending in "/") has a delete marker
37011
- * 2. If any nested objects have delete markers
37740
+ * Performs a single paginated, delimiter-less scan of the parent prefix (either the
37741
+ * caller-supplied `prefix`, or — for backward compatibility — the longest common prefix of
37742
+ * `folders`) and attributes every key it sees to its direct-child folder. This replaces the
37743
+ * previous design of one unbounded scan per folder: the union of what N per-folder scans
37744
+ * would return is exactly what one scan of their common parent returns, minus "loose" objects
37745
+ * that live directly under the parent (which aren't attributed to any folder and are ignored).
37012
37746
  *
37013
- * Note: This can be expensive for many folders (N S3 requests for N folders).
37014
- * Use sparingly and consider caching.
37747
+ * Cost: at most `S3_MAX_SCAN_PAGES` requests total, independent of how many folders are being
37748
+ * checked (as opposed to the old N-scans-for-N-folders behaviour).
37749
+ *
37750
+ * Honours `ctx.req.signal`: if the caller navigates away mid-scan, the loop stops and returns
37751
+ * whatever it has accumulated so far, flagged via `isPartialScan`.
37752
+ *
37753
+ * `isPartialScan` (per folder) is `true` when the scan hit the page ceiling (or was aborted)
37754
+ * before fully covering that folder's key range. It is the caller's job to treat it as
37755
+ * "unknown", not as "confirmed empty": `hasDeletedContent: false` together with
37756
+ * `isPartialScan: true` means "not fully checked", not "clean". A `true` value for
37757
+ * `hasDeletedContent` or `isFolderDeleted` is always reliable, partial scan or not — an
37758
+ * incomplete scan can only miss a delete marker, never invent one.
37015
37759
  *
37016
37760
  * @throws TRPCError NOT_FOUND - bucket does not exist
37017
37761
  * @throws TRPCError FORBIDDEN - no credentials or access denied
37018
37762
  */
37019
- checkDeletedContent: cephProtectedProcedure.input(import_zod17.z.object({
37020
- project_id: import_zod17.z.string(),
37021
- bucket: import_zod17.z.string().min(1),
37022
- folders: import_zod17.z.array(import_zod17.z.string()).max(100)
37023
- })).query(async ({ ctx, input }) => {
37763
+ checkDeletedContent: cephProtectedProcedure.input(checkDeletedContentInputSchema).query(async ({ ctx, input }) => {
37024
37764
  const s3 = ctx.getCephClient();
37025
- try {
37026
- const results = await Promise.all(input.folders.map(async (folderPrefix) => {
37027
- try {
37028
- let hasDeletedNestedObjects = false;
37029
- let isFolderMarkerDeleted = false;
37030
- let folderDeleteMarkerVersionId;
37031
- let folderMarkerVersionId;
37032
- let keyMarker;
37033
- let versionIdMarker;
37034
- do {
37035
- const response = await s3.send(new import_client_s34.ListObjectVersionsCommand({
37036
- Bucket: input.bucket,
37037
- Prefix: folderPrefix,
37038
- MaxKeys: 100,
37039
- KeyMarker: keyMarker,
37040
- VersionIdMarker: versionIdMarker
37041
- }));
37042
- if (!folderMarkerVersionId && response.Versions) {
37043
- const folderMarkerVersions = response.Versions.filter((v) => v.Key === folderPrefix);
37044
- if (folderMarkerVersions.length > 0) {
37045
- const latestVersion = folderMarkerVersions.sort((a, b) => {
37046
- const aTime = a.LastModified?.getTime() ?? 0;
37047
- const bTime = b.LastModified?.getTime() ?? 0;
37048
- return bTime - aTime;
37049
- })[0];
37050
- if (latestVersion) {
37051
- folderMarkerVersionId = latestVersion.VersionId;
37052
- }
37053
- }
37054
- }
37055
- if (!isFolderMarkerDeleted && response.DeleteMarkers) {
37056
- const folderMarkerDeleteMarkers = response.DeleteMarkers.filter((dm) => dm.Key === folderPrefix);
37057
- if (folderMarkerDeleteMarkers.length > 0) {
37058
- const latestFolderMarker = folderMarkerDeleteMarkers.find((dm) => dm.IsLatest);
37059
- if (latestFolderMarker) {
37060
- isFolderMarkerDeleted = true;
37061
- folderDeleteMarkerVersionId = latestFolderMarker.VersionId;
37062
- }
37063
- }
37064
- }
37065
- if (!hasDeletedNestedObjects) {
37066
- const deleteMarkersExcludingFolder = response.DeleteMarkers?.filter((dm) => dm.Key !== folderPrefix && dm.IsLatest === true) ?? [];
37067
- hasDeletedNestedObjects = deleteMarkersExcludingFolder.length > 0;
37068
- }
37069
- if (hasDeletedNestedObjects && isFolderMarkerDeleted && folderMarkerVersionId) {
37070
- break;
37071
- }
37072
- if (!response.IsTruncated || !response.NextKeyMarker) {
37073
- break;
37074
- }
37075
- keyMarker = response.NextKeyMarker;
37076
- versionIdMarker = response.NextVersionIdMarker;
37077
- } while (true);
37078
- return {
37079
- prefix: folderPrefix,
37080
- hasDeletedContent: isFolderMarkerDeleted || hasDeletedNestedObjects,
37081
- isFolderDeleted: isFolderMarkerDeleted,
37082
- folderDeleteMarkerVersionId,
37083
- folderMarkerVersionId
37084
- };
37085
- } catch {
37086
- return {
37087
- prefix: folderPrefix,
37088
- hasDeletedContent: false,
37089
- isFolderDeleted: false
37090
- };
37765
+ const scanPrefix = input.prefix ?? longestCommonPrefix(input.folders ?? []);
37766
+ const acc = /* @__PURE__ */ new Map();
37767
+ const getOrCreate = /* @__PURE__ */ __name((folder) => {
37768
+ let entry = acc.get(folder);
37769
+ if (!entry) {
37770
+ entry = {
37771
+ hasDeletedNested: false,
37772
+ isFolderDeleted: false
37773
+ };
37774
+ acc.set(folder, entry);
37775
+ }
37776
+ return entry;
37777
+ }, "getOrCreate");
37778
+ let keyMarker;
37779
+ let versionIdMarker;
37780
+ let pages = 0;
37781
+ let stoppedAtKey;
37782
+ while (true) {
37783
+ if (ctx.req.signal?.aborted) {
37784
+ stoppedAtKey = keyMarker ?? "";
37785
+ break;
37786
+ }
37787
+ let response;
37788
+ try {
37789
+ response = await s3.send(new import_client_s34.ListObjectVersionsCommand({
37790
+ Bucket: input.bucket,
37791
+ Prefix: scanPrefix || void 0,
37792
+ MaxKeys: S3_MAX_KEYS_PER_REQUEST,
37793
+ KeyMarker: keyMarker,
37794
+ VersionIdMarker: versionIdMarker
37795
+ }), {
37796
+ abortSignal: ctx.req.signal
37797
+ });
37798
+ } catch (error) {
37799
+ if (ctx.req.signal?.aborted) {
37800
+ stoppedAtKey = keyMarker ?? "";
37801
+ break;
37091
37802
  }
37092
- }));
37093
- return results;
37094
- } catch (error) {
37095
- throw mapS3ErrorToTRPCError(error, {
37096
- operation: "check deleted content",
37097
- bucket: input.bucket
37098
- });
37803
+ console.error("checkDeletedContent scan failed", {
37804
+ bucket: input.bucket,
37805
+ prefix: scanPrefix,
37806
+ pages,
37807
+ error
37808
+ });
37809
+ throw mapS3ErrorToTRPCError(error, {
37810
+ operation: "check deleted content",
37811
+ bucket: input.bucket
37812
+ });
37813
+ }
37814
+ for (const v of response.Versions ?? []) {
37815
+ const key = v.Key ?? "";
37816
+ const folder = folderPrefixOf(key, scanPrefix);
37817
+ if (!folder) continue;
37818
+ if (key === folder) {
37819
+ const entry = getOrCreate(folder);
37820
+ const lastModified = v.LastModified?.getTime() ?? 0;
37821
+ if (entry.folderMarkerVersionId === void 0 || lastModified > (entry.folderMarkerLastModified ?? -1)) {
37822
+ entry.folderMarkerVersionId = v.VersionId;
37823
+ entry.folderMarkerLastModified = lastModified;
37824
+ }
37825
+ }
37826
+ }
37827
+ for (const dm of response.DeleteMarkers ?? []) {
37828
+ const key = dm.Key ?? "";
37829
+ const folder = folderPrefixOf(key, scanPrefix);
37830
+ if (!folder) continue;
37831
+ const entry = getOrCreate(folder);
37832
+ if (key === folder) {
37833
+ if (dm.IsLatest) {
37834
+ entry.isFolderDeleted = true;
37835
+ entry.folderDeleteMarkerVersionId = dm.VersionId;
37836
+ }
37837
+ } else if (dm.IsLatest === true) {
37838
+ entry.hasDeletedNested = true;
37839
+ }
37840
+ }
37841
+ pages++;
37842
+ if (!response.IsTruncated) {
37843
+ stoppedAtKey = void 0;
37844
+ break;
37845
+ }
37846
+ if (!response.NextKeyMarker) {
37847
+ stoppedAtKey = greatestKeyOnPage(response.Versions, response.DeleteMarkers) ?? keyMarker ?? "";
37848
+ break;
37849
+ }
37850
+ if (pages >= S3_MAX_SCAN_PAGES) {
37851
+ stoppedAtKey = response.NextKeyMarker;
37852
+ break;
37853
+ }
37854
+ keyMarker = response.NextKeyMarker;
37855
+ versionIdMarker = response.NextVersionIdMarker;
37099
37856
  }
37857
+ const folders = input.folders ?? [
37858
+ ...acc.keys()
37859
+ ];
37860
+ return checkDeletedContentOutputSchema.parse(folders.map((prefix) => {
37861
+ const entry = acc.get(prefix);
37862
+ return {
37863
+ prefix,
37864
+ hasDeletedContent: (entry?.isFolderDeleted ?? false) || (entry?.hasDeletedNested ?? false),
37865
+ isFolderDeleted: entry?.isFolderDeleted ?? false,
37866
+ folderDeleteMarkerVersionId: entry?.folderDeleteMarkerVersionId,
37867
+ folderMarkerVersionId: entry?.folderMarkerVersionId,
37868
+ isPartialScan: !isFolderCovered(prefix, stoppedAtKey)
37869
+ };
37870
+ }));
37100
37871
  })
37101
37872
  };
37102
37873
 
37103
37874
  // src/server/Storage/routers/ceph/bucketPolicyRouter.ts
37104
37875
  var import_client_s35 = require("@aws-sdk/client-s3");
37105
37876
  var import_server16 = require("@trpc/server");
37106
- var import_zod18 = require("zod");
37877
+ var import_zod17 = require("zod");
37107
37878
  var policySetRateLimits = /* @__PURE__ */ new Map();
37108
37879
  function checkPolicySetRateLimit(bucketName, projectId) {
37109
37880
  const key = `${projectId}:${bucketName}`;
@@ -37139,12 +37910,12 @@ function validateResourceARNsMatchBucket(policy, bucketName) {
37139
37910
  statement.Resource
37140
37911
  ];
37141
37912
  for (const resource of resources) {
37142
- const arnRegex = /^arn:aws:s3:::([^/]+)/;
37913
+ const arnRegex = /^arn:aws:s3::[^:/]*:([^/]+)/;
37143
37914
  const match = resource.match(arnRegex);
37144
37915
  if (!match) {
37145
37916
  throw new import_server16.TRPCError({
37146
37917
  code: "BAD_REQUEST",
37147
- message: `Policy Resource '${resource}' is not a valid S3 bucket ARN for bucket '${bucketName}'. Expected: arn:aws:s3:::${bucketName} or arn:aws:s3:::${bucketName}/*`
37918
+ message: `Policy Resource '${resource}' is not a valid S3 bucket ARN for bucket '${bucketName}'. Expected: arn:aws:s3:::${bucketName}, arn:aws:s3:::${bucketName}/* or arn:aws:s3::TENANT:${bucketName}`
37148
37919
  });
37149
37920
  }
37150
37921
  if (match[1] !== bucketName) {
@@ -37179,12 +37950,22 @@ function validatePolicySemantics(policy) {
37179
37950
  }
37180
37951
  }
37181
37952
  __name(validatePolicySemantics, "validatePolicySemantics");
37953
+ function parseStoredPolicy(policyText) {
37954
+ try {
37955
+ const result = bucketPolicyDocumentSchema.safeParse(JSON.parse(policyText));
37956
+ return result.success ? result.data : null;
37957
+ } catch {
37958
+ return null;
37959
+ }
37960
+ }
37961
+ __name(parseStoredPolicy, "parseStoredPolicy");
37182
37962
  var bucketPolicyRouter = {
37183
37963
  /**
37184
37964
  * Get the current bucket policy.
37185
37965
  *
37186
- * Returns the policy as both a parsed object and raw JSON string.
37187
- * Returns null if no policy is set (not an error).
37966
+ * Returns the policy as both a parsed object and raw JSON string. `policyText` is always the
37967
+ * document exactly as RGW stored it; `policy` is null when no policy is set, and also when the
37968
+ * stored document doesn't fit our schema — the editor shows the raw text either way.
37188
37969
  *
37189
37970
  * @throws TRPCError NOT_FOUND - bucket does not exist
37190
37971
  * @throws TRPCError FORBIDDEN - no credentials or access denied
@@ -37203,10 +37984,8 @@ var bucketPolicyRouter = {
37203
37984
  policyText: null
37204
37985
  };
37205
37986
  }
37206
- const policyObj = JSON.parse(policyText);
37207
- const policy = bucketPolicyDocumentSchema.parse(policyObj);
37208
37987
  return {
37209
- policy,
37988
+ policy: parseStoredPolicy(policyText),
37210
37989
  policyText
37211
37990
  };
37212
37991
  } catch (error) {
@@ -37268,7 +38047,7 @@ var bucketPolicyRouter = {
37268
38047
  message: "Invalid JSON format"
37269
38048
  });
37270
38049
  }
37271
- if (error instanceof import_zod18.z.ZodError) {
38050
+ if (error instanceof import_zod17.z.ZodError) {
37272
38051
  const zodError = error;
37273
38052
  const firstError = zodError.issues?.[0];
37274
38053
  if (!firstError) {
@@ -37915,40 +38694,40 @@ var buildObjectStorageRouters = /* @__PURE__ */ __name((policyDir) => ({
37915
38694
  }), "buildObjectStorageRouters");
37916
38695
 
37917
38696
  // src/server/Project/routers/projectRouter.ts
37918
- var import_zod20 = require("zod");
38697
+ var import_zod19 = require("zod");
37919
38698
  var import_server19 = require("@trpc/server");
37920
38699
 
37921
38700
  // src/server/Project/types/models.ts
37922
- var import_zod19 = require("zod");
37923
- var baseProjectSchema = import_zod19.z.object({
37924
- id: import_zod19.z.string(),
37925
- name: import_zod19.z.string(),
37926
- enabled: import_zod19.z.boolean(),
37927
- domain_id: import_zod19.z.string().nullable().optional(),
37928
- parent_id: import_zod19.z.string().nullable().optional(),
37929
- is_domain: import_zod19.z.boolean().nullable().optional(),
37930
- tags: import_zod19.z.array(import_zod19.z.string()).nullable().optional(),
37931
- options: import_zod19.z.record(import_zod19.z.string(), import_zod19.z.string().or(import_zod19.z.boolean())).nullable().optional(),
37932
- description: import_zod19.z.string().nullable().optional(),
37933
- links: import_zod19.z.object({
37934
- self: import_zod19.z.string().url().nullable().optional()
38701
+ var import_zod18 = require("zod");
38702
+ var baseProjectSchema = import_zod18.z.object({
38703
+ id: import_zod18.z.string(),
38704
+ name: import_zod18.z.string(),
38705
+ enabled: import_zod18.z.boolean(),
38706
+ domain_id: import_zod18.z.string().nullable().optional(),
38707
+ parent_id: import_zod18.z.string().nullable().optional(),
38708
+ is_domain: import_zod18.z.boolean().nullable().optional(),
38709
+ tags: import_zod18.z.array(import_zod18.z.string()).nullable().optional(),
38710
+ options: import_zod18.z.record(import_zod18.z.string(), import_zod18.z.string().or(import_zod18.z.boolean())).nullable().optional(),
38711
+ description: import_zod18.z.string().nullable().optional(),
38712
+ links: import_zod18.z.object({
38713
+ self: import_zod18.z.string().url().nullable().optional()
37935
38714
  }).optional()
37936
38715
  });
37937
38716
  var projectSchema = baseProjectSchema.extend({
37938
- domain_name: import_zod19.z.string().optional(),
37939
- parents: import_zod19.z.array(import_zod19.z.object({
38717
+ domain_name: import_zod18.z.string().optional(),
38718
+ parents: import_zod18.z.array(import_zod18.z.object({
37940
38719
  project: baseProjectSchema
37941
38720
  })).optional()
37942
38721
  });
37943
- var projectResponseSchema = import_zod19.z.object({
38722
+ var projectResponseSchema = import_zod18.z.object({
37944
38723
  project: projectSchema
37945
38724
  });
37946
- var projectsResponseSchema = import_zod19.z.object({
37947
- projects: import_zod19.z.array(projectSchema),
37948
- links: import_zod19.z.object({
37949
- self: import_zod19.z.string().url().optional(),
37950
- previous: import_zod19.z.string().url().nullable().optional(),
37951
- next: import_zod19.z.string().url().nullable().optional()
38725
+ var projectsResponseSchema = import_zod18.z.object({
38726
+ projects: import_zod18.z.array(projectSchema),
38727
+ links: import_zod18.z.object({
38728
+ self: import_zod18.z.string().url().optional(),
38729
+ previous: import_zod18.z.string().url().nullable().optional(),
38730
+ next: import_zod18.z.string().url().nullable().optional()
37952
38731
  }).optional()
37953
38732
  });
37954
38733
 
@@ -38035,10 +38814,10 @@ var projectRouter = {
38035
38814
  return void 0;
38036
38815
  }
38037
38816
  const domainsData = domainsResponse ? await domainsResponse.json().catch(() => null) : null;
38038
- const domainsResponseSchema = import_zod20.z.object({
38039
- domains: import_zod20.z.array(import_zod20.z.object({
38040
- id: import_zod20.z.string(),
38041
- name: import_zod20.z.string()
38817
+ const domainsResponseSchema = import_zod19.z.object({
38818
+ domains: import_zod19.z.array(import_zod19.z.object({
38819
+ id: import_zod19.z.string(),
38820
+ name: import_zod19.z.string()
38042
38821
  }))
38043
38822
  });
38044
38823
  const parsedDomains = domainsResponseSchema.safeParse(domainsData);
@@ -38058,8 +38837,8 @@ var projectRouter = {
38058
38837
  * filtered by the optional search term. Uses the OpenStack /v3/auth/projects
38059
38838
  * endpoint which works with any valid token.
38060
38839
  */
38061
- searchProjects: protectedProcedure.input(import_zod20.z.object({
38062
- search: import_zod20.z.string().optional()
38840
+ searchProjects: protectedProcedure.input(import_zod19.z.object({
38841
+ search: import_zod19.z.string().optional()
38063
38842
  }).optional()).query(async ({ ctx, input }) => {
38064
38843
  if (!ctx.openstack) {
38065
38844
  throw new import_server19.TRPCError({
@@ -38089,8 +38868,65 @@ var projectRouter = {
38089
38868
  projects.sort((a, b) => a.name.localeCompare(b.name));
38090
38869
  return projects;
38091
38870
  }),
38092
- getProject: protectedProcedure.input(import_zod20.z.object({
38093
- projectId: import_zod20.z.string()
38871
+ /**
38872
+ * List projects with unified search functionality
38873
+ *
38874
+ * Returns all projects that the authenticated user has access to,
38875
+ * filtered by ID, name, and description using the standard filterBySearchParams helper.
38876
+ * This endpoint provides consistent search behavior with other resources in the system.
38877
+ */
38878
+ listProjectsWithSearch: protectedProcedure.input(import_zod19.z.object({
38879
+ searchTerm: import_zod19.z.string().optional()
38880
+ })).query(async ({ ctx, input }) => {
38881
+ if (!ctx.openstack) {
38882
+ throw new import_server19.TRPCError({
38883
+ code: "UNAUTHORIZED",
38884
+ message: "No authenticated session"
38885
+ });
38886
+ }
38887
+ const token = ctx.openstack.getToken();
38888
+ if (!token?.authToken) {
38889
+ throw new import_server19.TRPCError({
38890
+ code: "UNAUTHORIZED",
38891
+ message: "No auth token available"
38892
+ });
38893
+ }
38894
+ const [response, domainsResponse] = await Promise.all([
38895
+ callIdentityAPI(ctx.identityEndpoint, token.authToken, "auth/projects"),
38896
+ callIdentityAPI(ctx.identityEndpoint, token.authToken, "auth/domains").catch(() => null)
38897
+ ]);
38898
+ const data = await response.json();
38899
+ const parsedData = projectsResponseSchema.safeParse(data);
38900
+ if (!parsedData.success) {
38901
+ console.error("Zod Parsing Error:", parsedData.error.message);
38902
+ return void 0;
38903
+ }
38904
+ const domainsData = domainsResponse ? await domainsResponse.json().catch(() => null) : null;
38905
+ const domainsResponseSchema = import_zod19.z.object({
38906
+ domains: import_zod19.z.array(import_zod19.z.object({
38907
+ id: import_zod19.z.string(),
38908
+ name: import_zod19.z.string()
38909
+ }))
38910
+ });
38911
+ const parsedDomains = domainsResponseSchema.safeParse(domainsData);
38912
+ const domainMap = new Map((parsedDomains.success ? parsedDomains.data.domains : []).map((d) => [
38913
+ d.id,
38914
+ d.name
38915
+ ]));
38916
+ const projectsWithDomain = parsedData.data.projects.map((project) => ({
38917
+ ...project,
38918
+ domain_name: project.domain_id ? domainMap.get(project.domain_id) ?? void 0 : void 0
38919
+ }));
38920
+ return filterBySearchParams(projectsWithDomain, input.searchTerm, [
38921
+ "id",
38922
+ "name",
38923
+ "description",
38924
+ "domain_name",
38925
+ "domain_id"
38926
+ ]);
38927
+ }),
38928
+ getProject: protectedProcedure.input(import_zod19.z.object({
38929
+ projectId: import_zod19.z.string()
38094
38930
  })).query(async ({ ctx, input }) => {
38095
38931
  if (!ctx.openstack) {
38096
38932
  throw new import_server19.TRPCError({
@@ -38200,16 +39036,16 @@ __name(validateOpenstackService, "validateOpenstackService");
38200
39036
  var import_signal_openstack7 = __toESM(require_esm());
38201
39037
 
38202
39038
  // src/server/Network/types/floatingIp.ts
38203
- var import_zod22 = require("zod");
39039
+ var import_zod21 = require("zod");
38204
39040
 
38205
39041
  // src/server/Network/types/index.ts
38206
- var import_zod21 = require("zod");
38207
- var ISO8601TimestampSchema = import_zod21.z.string().brand("ISO8601Timestamp");
38208
- var SortDirSchema = import_zod21.z.enum([
39042
+ var import_zod20 = require("zod");
39043
+ var ISO8601TimestampSchema = import_zod20.z.string().brand("ISO8601Timestamp");
39044
+ var SortDirSchema = import_zod20.z.enum([
38209
39045
  "asc",
38210
39046
  "desc"
38211
39047
  ]);
38212
- var NetworkPortStatusSchema = import_zod21.z.enum([
39048
+ var NetworkPortStatusSchema = import_zod20.z.enum([
38213
39049
  "ACTIVE",
38214
39050
  "DOWN",
38215
39051
  "BUILD",
@@ -38217,153 +39053,153 @@ var NetworkPortStatusSchema = import_zod21.z.enum([
38217
39053
  ]);
38218
39054
 
38219
39055
  // src/server/Network/types/floatingIp.ts
38220
- var FloatingIpStatusSchema = import_zod22.z.enum([
39056
+ var FloatingIpStatusSchema = import_zod21.z.enum([
38221
39057
  "ACTIVE",
38222
39058
  "DOWN",
38223
39059
  "ERROR"
38224
39060
  ]);
38225
- var PortDetailsSchema = import_zod22.z.object({
39061
+ var PortDetailsSchema = import_zod21.z.object({
38226
39062
  /** The status of the port */
38227
39063
  status: FloatingIpStatusSchema,
38228
39064
  /** The name of the port */
38229
- name: import_zod22.z.string(),
39065
+ name: import_zod21.z.string(),
38230
39066
  /** Administrative state of the port (true = UP, false = DOWN) */
38231
- admin_state_up: import_zod22.z.boolean(),
39067
+ admin_state_up: import_zod21.z.boolean(),
38232
39068
  /** The ID of the network the port is attached to */
38233
- network_id: import_zod22.z.string(),
39069
+ network_id: import_zod21.z.string(),
38234
39070
  /** The owner of the port device */
38235
- device_owner: import_zod22.z.string(),
39071
+ device_owner: import_zod21.z.string(),
38236
39072
  /** The MAC address of the port */
38237
- mac_address: import_zod22.z.string(),
39073
+ mac_address: import_zod21.z.string(),
38238
39074
  /** The ID of the device the port is attached to */
38239
- device_id: import_zod22.z.string()
39075
+ device_id: import_zod21.z.string()
38240
39076
  });
38241
- var PortForwardingSchema = import_zod22.z.object({
39077
+ var PortForwardingSchema = import_zod21.z.object({
38242
39078
  /** Port forwarding protocol */
38243
- protocol: import_zod22.z.enum([
39079
+ protocol: import_zod21.z.enum([
38244
39080
  "tcp",
38245
39081
  "udp"
38246
39082
  ]),
38247
39083
  /** The internal fixed IP address associated with the port forwarding */
38248
- internal_ip_address: import_zod22.z.string(),
39084
+ internal_ip_address: import_zod21.z.string(),
38249
39085
  /** The internal port number or starting port of the range */
38250
- internal_port: import_zod22.z.number().optional(),
39086
+ internal_port: import_zod21.z.number().optional(),
38251
39087
  /** The internal port range (e.g., "1024:2048") */
38252
- internal_port_range: import_zod22.z.string().optional(),
39088
+ internal_port_range: import_zod21.z.string().optional(),
38253
39089
  /** The internal port ID for the port forwarding (requires floating-ip-port-forwarding-detail extension) */
38254
- internal_port_id: import_zod22.z.string().optional(),
39090
+ internal_port_id: import_zod21.z.string().optional(),
38255
39091
  /** The external port number or starting port of the range */
38256
- external_port: import_zod22.z.number().optional(),
39092
+ external_port: import_zod21.z.number().optional(),
38257
39093
  /** The external port range (e.g., "8000:9000") */
38258
- external_port_range: import_zod22.z.string().optional(),
39094
+ external_port_range: import_zod21.z.string().optional(),
38259
39095
  /** The ID of the port forwarding resource */
38260
- id: import_zod22.z.string(),
39096
+ id: import_zod21.z.string(),
38261
39097
  /** Description of the port forwarding rule (requires floating-ip-port-forwarding-description extension) */
38262
- description: import_zod22.z.string().nullable().optional()
39098
+ description: import_zod21.z.string().nullable().optional()
38263
39099
  });
38264
- var FloatingIpSchema = import_zod22.z.object({
39100
+ var FloatingIpSchema = import_zod21.z.object({
38265
39101
  /** The ID of the router for the floating IP (null if not associated) */
38266
- router_id: import_zod22.z.string().nullable(),
39102
+ router_id: import_zod21.z.string().nullable(),
38267
39103
  /** A human-readable description for the resource */
38268
- description: import_zod22.z.string().nullable().optional(),
39104
+ description: import_zod21.z.string().nullable().optional(),
38269
39105
  /** Whether this is a distributed floating IP (requires floating-ip-distributed extension) */
38270
- distributed: import_zod22.z.boolean().optional(),
39106
+ distributed: import_zod21.z.boolean().optional(),
38271
39107
  /** A valid DNS domain (requires dns-integration extension) */
38272
- dns_domain: import_zod22.z.string().optional(),
39108
+ dns_domain: import_zod21.z.string().optional(),
38273
39109
  /** A valid DNS name (requires dns-integration extension) */
38274
- dns_name: import_zod22.z.string().optional(),
39110
+ dns_name: import_zod21.z.string().optional(),
38275
39111
  /** Time at which the resource was created (UTC ISO8601 format, requires standard-attr-timestamp extension) */
38276
39112
  created_at: ISO8601TimestampSchema.optional(),
38277
39113
  /** Time at which the resource was last updated (UTC ISO8601 format, requires standard-attr-timestamp extension) */
38278
39114
  updated_at: ISO8601TimestampSchema.optional(),
38279
39115
  /** The revision number of the resource (set by server, for concurrency control) */
38280
- revision_number: import_zod22.z.number(),
39116
+ revision_number: import_zod21.z.number(),
38281
39117
  /** The ID of the project that owns the floating IP */
38282
- project_id: import_zod22.z.string(),
39118
+ project_id: import_zod21.z.string(),
38283
39119
  /** The ID of the tenant (deprecated, use project_id) */
38284
- tenant_id: import_zod22.z.string(),
39120
+ tenant_id: import_zod21.z.string(),
38285
39121
  /** The ID of the network associated with the floating IP */
38286
- floating_network_id: import_zod22.z.string(),
39122
+ floating_network_id: import_zod21.z.string(),
38287
39123
  /** The fixed IP address associated with the floating IP (null if not associated) */
38288
- fixed_ip_address: import_zod22.z.string().nullable(),
39124
+ fixed_ip_address: import_zod21.z.string().nullable(),
38289
39125
  /** The floating IP address */
38290
- floating_ip_address: import_zod22.z.string(),
39126
+ floating_ip_address: import_zod21.z.string(),
38291
39127
  /** The ID of the port associated with the floating IP (null if not associated) */
38292
- port_id: import_zod22.z.string().nullable(),
39128
+ port_id: import_zod21.z.string().nullable(),
38293
39129
  /** The ID of the floating IP address */
38294
- id: import_zod22.z.string(),
39130
+ id: import_zod21.z.string(),
38295
39131
  /** The status of the floating IP */
38296
39132
  status: FloatingIpStatusSchema,
38297
39133
  /** The information of the associated port (null if not associated, requires fip-port-details extension) */
38298
39134
  port_details: PortDetailsSchema.nullable().optional(),
38299
39135
  /** The list of tags on the resource (requires standard-attr-tag extension) */
38300
- tags: import_zod22.z.array(import_zod22.z.string()).optional(),
39136
+ tags: import_zod21.z.array(import_zod21.z.string()).optional(),
38301
39137
  /** The associated port forwarding resources for the floating IP (requires expose-port-forwarding-in-fip extension) */
38302
- port_forwardings: import_zod22.z.array(PortForwardingSchema).nullish(),
39138
+ port_forwardings: import_zod21.z.array(PortForwardingSchema).nullish(),
38303
39139
  /** The ID of the QoS policy of the network where this floating IP is plugged (requires qos-fip extension) */
38304
- qos_network_policy_id: import_zod22.z.string().optional(),
39140
+ qos_network_policy_id: import_zod21.z.string().optional(),
38305
39141
  /** The ID of the QoS policy associated with the floating IP (requires qos extension) */
38306
- qos_policy_id: import_zod22.z.string().optional()
38307
- });
38308
- var FloatingIpIdInputSchema = import_zod22.z.object({
38309
- project_id: import_zod22.z.string(),
38310
- floatingip_id: import_zod22.z.string()
38311
- });
38312
- var FloatingIpCreateRequestSchema = import_zod22.z.object({
38313
- project_id: import_zod22.z.string().trim().min(1, "project_id must be a non-empty string"),
38314
- floating_network_id: import_zod22.z.string(),
38315
- fixed_ip_address: import_zod22.z.string().optional(),
38316
- floating_ip_address: import_zod22.z.string().optional(),
38317
- port_id: import_zod22.z.string().optional(),
38318
- subnet_id: import_zod22.z.string().optional(),
38319
- distributed: import_zod22.z.boolean().optional(),
38320
- description: import_zod22.z.string().optional(),
38321
- dns_domain: import_zod22.z.string().optional(),
38322
- dns_name: import_zod22.z.string().optional(),
38323
- qos_policy_id: import_zod22.z.string().optional()
38324
- });
38325
- var FloatingIpUpdateRequestSchema = import_zod22.z.object({
38326
- project_id: import_zod22.z.string(),
38327
- floatingip_id: import_zod22.z.string(),
38328
- port_id: import_zod22.z.string().nullable(),
38329
- fixed_ip_address: import_zod22.z.string().optional(),
38330
- description: import_zod22.z.string().optional(),
38331
- distributed: import_zod22.z.boolean().optional()
38332
- });
38333
- var FloatingIpResponseSchema = import_zod22.z.object({
39142
+ qos_policy_id: import_zod21.z.string().optional()
39143
+ });
39144
+ var FloatingIpIdInputSchema = import_zod21.z.object({
39145
+ project_id: import_zod21.z.string(),
39146
+ floatingip_id: import_zod21.z.string()
39147
+ });
39148
+ var FloatingIpCreateRequestSchema = import_zod21.z.object({
39149
+ project_id: import_zod21.z.string().trim().min(1, "project_id must be a non-empty string"),
39150
+ floating_network_id: import_zod21.z.string(),
39151
+ fixed_ip_address: import_zod21.z.string().optional(),
39152
+ floating_ip_address: import_zod21.z.string().optional(),
39153
+ port_id: import_zod21.z.string().optional(),
39154
+ subnet_id: import_zod21.z.string().optional(),
39155
+ distributed: import_zod21.z.boolean().optional(),
39156
+ description: import_zod21.z.string().optional(),
39157
+ dns_domain: import_zod21.z.string().optional(),
39158
+ dns_name: import_zod21.z.string().optional(),
39159
+ qos_policy_id: import_zod21.z.string().optional()
39160
+ });
39161
+ var FloatingIpUpdateRequestSchema = import_zod21.z.object({
39162
+ project_id: import_zod21.z.string(),
39163
+ floatingip_id: import_zod21.z.string(),
39164
+ port_id: import_zod21.z.string().nullable(),
39165
+ fixed_ip_address: import_zod21.z.string().optional(),
39166
+ description: import_zod21.z.string().optional(),
39167
+ distributed: import_zod21.z.boolean().optional()
39168
+ });
39169
+ var FloatingIpResponseSchema = import_zod21.z.object({
38334
39170
  floatingip: FloatingIpSchema
38335
39171
  });
38336
- var FloatingIpListResponseSchema = import_zod22.z.object({
39172
+ var FloatingIpListResponseSchema = import_zod21.z.object({
38337
39173
  /** A list of floating IP objects */
38338
- floatingips: import_zod22.z.array(FloatingIpSchema)
39174
+ floatingips: import_zod21.z.array(FloatingIpSchema)
38339
39175
  });
38340
- var FloatingIpQueryParametersSchema = import_zod22.z.object({
39176
+ var FloatingIpQueryParametersSchema = import_zod21.z.object({
38341
39177
  /** Project ID for rescoping (required for projectScopedProcedure) */
38342
- project_id: import_zod22.z.string(),
39178
+ project_id: import_zod21.z.string(),
38343
39179
  /** Filter by the ID of the floating IP */
38344
- id: import_zod22.z.string().optional(),
39180
+ id: import_zod21.z.string().optional(),
38345
39181
  /** Filter by the ID of the router for the floating IP */
38346
- router_id: import_zod22.z.string().nullable().optional(),
39182
+ router_id: import_zod21.z.string().nullable().optional(),
38347
39183
  /** Filter by the status of the floating IP */
38348
39184
  status: FloatingIpStatusSchema.optional(),
38349
39185
  /** Filter by the ID of the project that owns the resource */
38350
- tenant_id: import_zod22.z.string().optional(),
39186
+ tenant_id: import_zod21.z.string().optional(),
38351
39187
  /** Filter by the revision number of the resource */
38352
- revision_number: import_zod22.z.number().optional(),
39188
+ revision_number: import_zod21.z.number().optional(),
38353
39189
  /** Filter by the human-readable description of the resource */
38354
- description: import_zod22.z.string().nullable().optional(),
39190
+ description: import_zod21.z.string().nullable().optional(),
38355
39191
  /** Filter by the ID of the network associated with the floating IP */
38356
- floating_network_id: import_zod22.z.string().optional(),
39192
+ floating_network_id: import_zod21.z.string().optional(),
38357
39193
  /** Filter by the fixed IP address associated with the floating IP */
38358
- fixed_ip_address: import_zod22.z.string().optional(),
39194
+ fixed_ip_address: import_zod21.z.string().optional(),
38359
39195
  /** Filter by the floating IP address */
38360
- floating_ip_address: import_zod22.z.string().optional(),
39196
+ floating_ip_address: import_zod21.z.string().optional(),
38361
39197
  /** Filter by the ID of a port associated with the floating IP */
38362
- port_id: import_zod22.z.string().nullable().optional(),
39198
+ port_id: import_zod21.z.string().nullable().optional(),
38363
39199
  /** Sort direction (asc or desc) */
38364
39200
  sort_dir: SortDirSchema.optional(),
38365
39201
  /** Sort key - valid keys: fixed_ip_address, floating_ip_address, floating_network_id, id, router_id, status, tenant_id, project_id */
38366
- sort_key: import_zod22.z.enum([
39202
+ sort_key: import_zod21.z.enum([
38367
39203
  "fixed_ip_address",
38368
39204
  "floating_ip_address",
38369
39205
  "floating_network_id",
@@ -38374,73 +39210,73 @@ var FloatingIpQueryParametersSchema = import_zod22.z.object({
38374
39210
  "project_id"
38375
39211
  ]).optional(),
38376
39212
  /** Filter by tags (comma-separated, resources must match all tags) - requires standard-attr-tag extension */
38377
- tags: import_zod22.z.array(import_zod22.z.string()).optional(),
39213
+ tags: import_zod21.z.array(import_zod21.z.string()).optional(),
38378
39214
  /** Filter by tags with OR logic (comma-separated, resources match any tag) */
38379
- "tags-any": import_zod22.z.string().optional(),
39215
+ "tags-any": import_zod21.z.string().optional(),
38380
39216
  /** Exclude resources with these tags (comma-separated, resources must match all excluded tags) */
38381
- "not-tags": import_zod22.z.string().optional(),
39217
+ "not-tags": import_zod21.z.string().optional(),
38382
39218
  /** Exclude resources with these tags using OR logic (comma-separated, resources match any excluded tag) */
38383
- "not-tags-any": import_zod22.z.string().optional(),
39219
+ "not-tags-any": import_zod21.z.string().optional(),
38384
39220
  /** Specific fields to return (can be repeated) */
38385
- fields: import_zod22.z.union([
38386
- import_zod22.z.string(),
38387
- import_zod22.z.array(import_zod22.z.string())
39221
+ fields: import_zod21.z.union([
39222
+ import_zod21.z.string(),
39223
+ import_zod21.z.array(import_zod21.z.string())
38388
39224
  ]).optional(),
38389
39225
  /** Pagination limit */
38390
- limit: import_zod22.z.number().optional(),
39226
+ limit: import_zod21.z.number().optional(),
38391
39227
  /** Pagination marker (ID of the last item in the previous list) */
38392
- marker: import_zod22.z.string().optional(),
39228
+ marker: import_zod21.z.string().optional(),
38393
39229
  /** Pagination direction (false = ascending, true = descending) */
38394
- page_reverse: import_zod22.z.boolean().optional(),
39230
+ page_reverse: import_zod21.z.boolean().optional(),
38395
39231
  // BFF-side search (filtered in BFF layer, not sent to OpenStack)
38396
- searchTerm: import_zod22.z.string().optional()
38397
- });
38398
- var ExternalNetworksQuerySchema = import_zod22.z.object({
38399
- project_id: import_zod22.z.string(),
38400
- "router:external": import_zod22.z.literal(true).default(true)
38401
- });
38402
- var ExternalNetworkSchema = import_zod22.z.object({
38403
- id: import_zod22.z.string(),
38404
- name: import_zod22.z.string(),
38405
- project_id: import_zod22.z.string(),
38406
- "router:external": import_zod22.z.boolean().optional(),
38407
- shared: import_zod22.z.boolean(),
39232
+ searchTerm: import_zod21.z.string().optional()
39233
+ });
39234
+ var ExternalNetworksQuerySchema = import_zod21.z.object({
39235
+ project_id: import_zod21.z.string(),
39236
+ "router:external": import_zod21.z.literal(true).default(true)
39237
+ });
39238
+ var ExternalNetworkSchema = import_zod21.z.object({
39239
+ id: import_zod21.z.string(),
39240
+ name: import_zod21.z.string(),
39241
+ project_id: import_zod21.z.string(),
39242
+ "router:external": import_zod21.z.boolean().optional(),
39243
+ shared: import_zod21.z.boolean(),
38408
39244
  status: NetworkPortStatusSchema,
38409
39245
  updated_at: ISO8601TimestampSchema.optional(),
38410
- is_default: import_zod22.z.boolean().optional()
39246
+ is_default: import_zod21.z.boolean().optional()
38411
39247
  });
38412
- var ExternalNetworksResponseSchema = import_zod22.z.object({
38413
- networks: import_zod22.z.array(ExternalNetworkSchema)
39248
+ var ExternalNetworksResponseSchema = import_zod21.z.object({
39249
+ networks: import_zod21.z.array(ExternalNetworkSchema)
38414
39250
  });
38415
- var DnsDomainSchema = import_zod22.z.object({
38416
- id: import_zod22.z.string(),
38417
- name: import_zod22.z.string()
39251
+ var DnsDomainSchema = import_zod21.z.object({
39252
+ id: import_zod21.z.string(),
39253
+ name: import_zod21.z.string()
38418
39254
  });
38419
- var DnsDomainResponseSchema = import_zod22.z.object({
38420
- zones: import_zod22.z.array(DnsDomainSchema)
39255
+ var DnsDomainResponseSchema = import_zod21.z.object({
39256
+ zones: import_zod21.z.array(DnsDomainSchema)
38421
39257
  });
38422
- var AvailablePortsQuerySchema = import_zod22.z.object({
38423
- project_id: import_zod22.z.string(),
39258
+ var AvailablePortsQuerySchema = import_zod21.z.object({
39259
+ project_id: import_zod21.z.string(),
38424
39260
  /** Only ACTIVE ports are eligible for floating IP association */
38425
- status: import_zod22.z.literal("ACTIVE").default("ACTIVE"),
39261
+ status: import_zod21.z.literal("ACTIVE").default("ACTIVE"),
38426
39262
  /** Only administratively UP ports are eligible for floating IP association */
38427
- admin_state_up: import_zod22.z.literal(true).default(true)
39263
+ admin_state_up: import_zod21.z.literal(true).default(true)
38428
39264
  });
38429
- var AvailablePortSchema = import_zod22.z.object({
39265
+ var AvailablePortSchema = import_zod21.z.object({
38430
39266
  /** The ID of the port */
38431
- id: import_zod22.z.string(),
39267
+ id: import_zod21.z.string(),
38432
39268
  /** Human-readable name of the port */
38433
- name: import_zod22.z.string().nullable().optional(),
39269
+ name: import_zod21.z.string().nullable().optional(),
38434
39270
  /** List of fixed IPs assigned to the port, each entry pairs an IP address with its subnet. */
38435
- fixed_ips: import_zod22.z.array(import_zod22.z.object({
39271
+ fixed_ips: import_zod21.z.array(import_zod21.z.object({
38436
39272
  /** The fixed IP address assigned to the port */
38437
- ip_address: import_zod22.z.string(),
39273
+ ip_address: import_zod21.z.string(),
38438
39274
  /** The ID of the subnet the IP belongs to */
38439
- subnet_id: import_zod22.z.string().optional()
39275
+ subnet_id: import_zod21.z.string().optional()
38440
39276
  })).optional()
38441
39277
  });
38442
- var AvailablePortsResponseSchema = import_zod22.z.object({
38443
- ports: import_zod22.z.array(AvailablePortSchema)
39278
+ var AvailablePortsResponseSchema = import_zod21.z.object({
39279
+ ports: import_zod21.z.array(AvailablePortSchema)
38444
39280
  });
38445
39281
 
38446
39282
  // src/server/Network/helpers/errorHandling.ts
@@ -38548,6 +39384,7 @@ var floatingIpRouter = {
38548
39384
  const data = await response.json();
38549
39385
  const { floatingips } = parseOrThrow(FloatingIpListResponseSchema, data, "floatingIpRouter.list");
38550
39386
  return filterBySearchParams(floatingips, searchTerm, [
39387
+ "id",
38551
39388
  "description",
38552
39389
  "floating_ip_address",
38553
39390
  "fixed_ip_address",
@@ -38701,112 +39538,112 @@ var floatingIpRouter = {
38701
39538
  };
38702
39539
 
38703
39540
  // src/server/Network/types/securityGroup.ts
38704
- var import_zod23 = require("zod");
38705
- var securityGroupRuleSchema = import_zod23.z.object({
38706
- id: import_zod23.z.string(),
38707
- direction: import_zod23.z.enum([
39541
+ var import_zod22 = require("zod");
39542
+ var securityGroupRuleSchema = import_zod22.z.object({
39543
+ id: import_zod22.z.string(),
39544
+ direction: import_zod22.z.enum([
38708
39545
  "ingress",
38709
39546
  "egress"
38710
39547
  ]).optional(),
38711
- ethertype: import_zod23.z.enum([
39548
+ ethertype: import_zod22.z.enum([
38712
39549
  "IPv4",
38713
39550
  "IPv6"
38714
39551
  ]).optional(),
38715
- description: import_zod23.z.string().nullable().optional(),
38716
- security_group_id: import_zod23.z.string().optional(),
38717
- protocol: import_zod23.z.string().nullable().optional(),
38718
- port_range_min: import_zod23.z.number().nullable().optional(),
38719
- port_range_max: import_zod23.z.number().nullable().optional(),
38720
- remote_ip_prefix: import_zod23.z.string().nullable().optional(),
38721
- remote_group_id: import_zod23.z.string().nullable().optional(),
38722
- remote_address_group_id: import_zod23.z.string().nullable().optional(),
38723
- tenant_id: import_zod23.z.string().nullable().optional(),
38724
- project_id: import_zod23.z.string().nullable().optional(),
38725
- revision_number: import_zod23.z.number().optional(),
38726
- tags: import_zod23.z.array(import_zod23.z.string()).optional(),
38727
- created_at: import_zod23.z.string().optional(),
38728
- updated_at: import_zod23.z.string().nullable().optional()
39552
+ description: import_zod22.z.string().nullable().optional(),
39553
+ security_group_id: import_zod22.z.string().optional(),
39554
+ protocol: import_zod22.z.string().nullable().optional(),
39555
+ port_range_min: import_zod22.z.number().nullable().optional(),
39556
+ port_range_max: import_zod22.z.number().nullable().optional(),
39557
+ remote_ip_prefix: import_zod22.z.string().nullable().optional(),
39558
+ remote_group_id: import_zod22.z.string().nullable().optional(),
39559
+ remote_address_group_id: import_zod22.z.string().nullable().optional(),
39560
+ tenant_id: import_zod22.z.string().nullable().optional(),
39561
+ project_id: import_zod22.z.string().nullable().optional(),
39562
+ revision_number: import_zod22.z.number().optional(),
39563
+ tags: import_zod22.z.array(import_zod22.z.string()).optional(),
39564
+ created_at: import_zod22.z.string().optional(),
39565
+ updated_at: import_zod22.z.string().nullable().optional()
38729
39566
  });
38730
- var securityGroupSchema = import_zod23.z.object({
38731
- id: import_zod23.z.string(),
38732
- name: import_zod23.z.string().nullable().optional(),
38733
- description: import_zod23.z.string().nullable().optional(),
38734
- tenant_id: import_zod23.z.string().nullable().optional(),
38735
- project_id: import_zod23.z.string().nullable().optional(),
38736
- stateful: import_zod23.z.boolean().optional(),
38737
- shared: import_zod23.z.boolean().optional(),
38738
- tags: import_zod23.z.array(import_zod23.z.string()).optional(),
38739
- security_group_rules: import_zod23.z.array(securityGroupRuleSchema).optional(),
38740
- revision_number: import_zod23.z.number().optional(),
38741
- created_at: import_zod23.z.string().optional(),
38742
- updated_at: import_zod23.z.string().nullable().optional()
38743
- });
38744
- var securityGroupsResponseSchema = import_zod23.z.object({
38745
- security_groups: import_zod23.z.array(securityGroupSchema)
38746
- });
38747
- var securityGroupResponseSchema = import_zod23.z.object({
39567
+ var securityGroupSchema = import_zod22.z.object({
39568
+ id: import_zod22.z.string(),
39569
+ name: import_zod22.z.string().nullable().optional(),
39570
+ description: import_zod22.z.string().nullable().optional(),
39571
+ tenant_id: import_zod22.z.string().nullable().optional(),
39572
+ project_id: import_zod22.z.string().nullable().optional(),
39573
+ stateful: import_zod22.z.boolean().optional(),
39574
+ shared: import_zod22.z.boolean().optional(),
39575
+ tags: import_zod22.z.array(import_zod22.z.string()).optional(),
39576
+ security_group_rules: import_zod22.z.array(securityGroupRuleSchema).optional(),
39577
+ revision_number: import_zod22.z.number().optional(),
39578
+ created_at: import_zod22.z.string().optional(),
39579
+ updated_at: import_zod22.z.string().nullable().optional()
39580
+ });
39581
+ var securityGroupsResponseSchema = import_zod22.z.object({
39582
+ security_groups: import_zod22.z.array(securityGroupSchema)
39583
+ });
39584
+ var securityGroupResponseSchema = import_zod22.z.object({
38748
39585
  security_group: securityGroupSchema
38749
39586
  });
38750
- var securityGroupRuleResponseSchema = import_zod23.z.object({
39587
+ var securityGroupRuleResponseSchema = import_zod22.z.object({
38751
39588
  security_group_rule: securityGroupRuleSchema
38752
39589
  });
38753
39590
  var listSecurityGroupsInputSchema = projectScopedInputSchema.extend({
38754
39591
  // Sorting
38755
- sort_key: import_zod23.z.string().optional(),
39592
+ sort_key: import_zod22.z.string().optional(),
38756
39593
  sort_dir: SortDirSchema.optional(),
38757
39594
  // Basic filtering
38758
- name: import_zod23.z.string().optional(),
38759
- description: import_zod23.z.string().optional(),
38760
- tenant_id: import_zod23.z.string().optional(),
38761
- shared: import_zod23.z.boolean().optional(),
39595
+ name: import_zod22.z.string().optional(),
39596
+ description: import_zod22.z.string().optional(),
39597
+ tenant_id: import_zod22.z.string().optional(),
39598
+ shared: import_zod22.z.boolean().optional(),
38762
39599
  // Tag-based filtering (string values follow Neutron semantics)
38763
- tags: import_zod23.z.string().optional(),
38764
- tags_any: import_zod23.z.string().optional(),
38765
- not_tags: import_zod23.z.string().optional(),
38766
- not_tags_any: import_zod23.z.string().optional(),
39600
+ tags: import_zod22.z.string().optional(),
39601
+ tags_any: import_zod22.z.string().optional(),
39602
+ not_tags: import_zod22.z.string().optional(),
39603
+ not_tags_any: import_zod22.z.string().optional(),
38767
39604
  // Filtered in the BFF layer, not forwarded to Neutron
38768
- stateful: import_zod23.z.boolean().optional(),
39605
+ stateful: import_zod22.z.boolean().optional(),
38769
39606
  // BFF-side search (filtered in BFF layer, not sent to OpenStack)
38770
- searchTerm: import_zod23.z.string().optional()
39607
+ searchTerm: import_zod22.z.string().optional()
38771
39608
  });
38772
39609
  var getSecurityGroupByIdInputSchema = projectScopedInputSchema.extend({
38773
- securityGroupId: import_zod23.z.string()
39610
+ securityGroupId: import_zod22.z.string()
38774
39611
  });
38775
39612
  var createSecurityGroupInputSchema = projectScopedInputSchema.extend({
38776
- name: import_zod23.z.string().min(1, "Name is required"),
38777
- description: import_zod23.z.string().optional(),
38778
- stateful: import_zod23.z.boolean().optional()
39613
+ name: import_zod22.z.string().min(1, "Name is required"),
39614
+ description: import_zod22.z.string().optional(),
39615
+ stateful: import_zod22.z.boolean().optional()
38779
39616
  });
38780
39617
  var deleteSecurityGroupInputSchema = projectScopedInputSchema.extend({
38781
- securityGroupId: import_zod23.z.string()
39618
+ securityGroupId: import_zod22.z.string()
38782
39619
  });
38783
39620
  var updateSecurityGroupInputSchema = projectScopedInputSchema.extend({
38784
- securityGroupId: import_zod23.z.string(),
38785
- name: import_zod23.z.string().min(1, "Name is required").optional(),
38786
- description: import_zod23.z.string().optional(),
38787
- stateful: import_zod23.z.boolean().optional()
39621
+ securityGroupId: import_zod22.z.string(),
39622
+ name: import_zod22.z.string().min(1, "Name is required").optional(),
39623
+ description: import_zod22.z.string().optional(),
39624
+ stateful: import_zod22.z.boolean().optional()
38788
39625
  });
38789
39626
  var deleteSecurityGroupRuleInputSchema = projectScopedInputSchema.extend({
38790
- ruleId: import_zod23.z.string()
39627
+ ruleId: import_zod22.z.string()
38791
39628
  });
38792
39629
  var createSecurityGroupRuleInputSchema = projectScopedInputSchema.extend({
38793
- security_group_id: import_zod23.z.string(),
38794
- direction: import_zod23.z.enum([
39630
+ security_group_id: import_zod22.z.string(),
39631
+ direction: import_zod22.z.enum([
38795
39632
  "ingress",
38796
39633
  "egress"
38797
39634
  ]),
38798
- ethertype: import_zod23.z.enum([
39635
+ ethertype: import_zod22.z.enum([
38799
39636
  "IPv4",
38800
39637
  "IPv6"
38801
39638
  ]).default("IPv4"),
38802
- description: import_zod23.z.string().optional(),
38803
- protocol: import_zod23.z.string().nullable().optional(),
38804
- port_range_min: import_zod23.z.number().int().max(65535).nullable().optional(),
38805
- port_range_max: import_zod23.z.number().int().max(65535).nullable().optional(),
39639
+ description: import_zod22.z.string().optional(),
39640
+ protocol: import_zod22.z.string().nullable().optional(),
39641
+ port_range_min: import_zod22.z.number().int().max(65535).nullable().optional(),
39642
+ port_range_max: import_zod22.z.number().int().max(65535).nullable().optional(),
38806
39643
  // Transform empty strings to undefined for proper validation
38807
- remote_ip_prefix: import_zod23.z.string().nullable().optional().transform((val) => val === "" ? void 0 : val),
38808
- remote_group_id: import_zod23.z.string().nullable().optional().transform((val) => val === "" ? void 0 : val),
38809
- remote_address_group_id: import_zod23.z.string().nullable().optional().transform((val) => val === "" ? void 0 : val)
39644
+ remote_ip_prefix: import_zod22.z.string().nullable().optional().transform((val) => val === "" ? void 0 : val),
39645
+ remote_group_id: import_zod22.z.string().nullable().optional().transform((val) => val === "" ? void 0 : val),
39646
+ remote_address_group_id: import_zod22.z.string().nullable().optional().transform((val) => val === "" ? void 0 : val)
38810
39647
  });
38811
39648
 
38812
39649
  // src/server/Network/helpers/securityGroupHelpers.ts
@@ -39335,42 +40172,42 @@ var securityGroupRuleRouter = {
39335
40172
  };
39336
40173
 
39337
40174
  // src/server/Network/types/rbacPolicy.ts
39338
- var import_zod24 = require("zod");
39339
- var rbacPolicySchema = import_zod24.z.object({
39340
- id: import_zod24.z.string(),
39341
- object_type: import_zod24.z.enum([
40175
+ var import_zod23 = require("zod");
40176
+ var rbacPolicySchema = import_zod23.z.object({
40177
+ id: import_zod23.z.string(),
40178
+ object_type: import_zod23.z.enum([
39342
40179
  "qos_policy",
39343
40180
  "network",
39344
40181
  "security_group"
39345
40182
  ]),
39346
- object_id: import_zod24.z.string(),
39347
- action: import_zod24.z.enum([
40183
+ object_id: import_zod23.z.string(),
40184
+ action: import_zod23.z.enum([
39348
40185
  "access_as_shared",
39349
40186
  "access_as_external"
39350
40187
  ]),
39351
- target_tenant: import_zod24.z.string(),
39352
- tenant_id: import_zod24.z.string().nullable().optional(),
39353
- project_id: import_zod24.z.string().nullable().optional()
40188
+ target_tenant: import_zod23.z.string(),
40189
+ tenant_id: import_zod23.z.string().nullable().optional(),
40190
+ project_id: import_zod23.z.string().nullable().optional()
39354
40191
  });
39355
- var rbacPoliciesResponseSchema = import_zod24.z.object({
39356
- rbac_policies: import_zod24.z.array(rbacPolicySchema)
40192
+ var rbacPoliciesResponseSchema = import_zod23.z.object({
40193
+ rbac_policies: import_zod23.z.array(rbacPolicySchema)
39357
40194
  });
39358
- var rbacPolicyResponseSchema = import_zod24.z.object({
40195
+ var rbacPolicyResponseSchema = import_zod23.z.object({
39359
40196
  rbac_policy: rbacPolicySchema
39360
40197
  });
39361
40198
  var listRBACPoliciesForSecurityGroupInputSchema = projectScopedInputSchema.extend({
39362
- securityGroupId: import_zod24.z.string()
40199
+ securityGroupId: import_zod23.z.string()
39363
40200
  });
39364
40201
  var createRBACPolicyInputSchema = projectScopedInputSchema.extend({
39365
- securityGroupId: import_zod24.z.string(),
39366
- targetTenant: import_zod24.z.string().trim().min(1, "Target project ID is required")
40202
+ securityGroupId: import_zod23.z.string(),
40203
+ targetTenant: import_zod23.z.string().trim().min(1, "Target project ID is required")
39367
40204
  });
39368
40205
  var updateRBACPolicyInputSchema = projectScopedInputSchema.extend({
39369
- policyId: import_zod24.z.string(),
39370
- targetTenant: import_zod24.z.string().trim().min(1, "Target project ID is required")
40206
+ policyId: import_zod23.z.string(),
40207
+ targetTenant: import_zod23.z.string().trim().min(1, "Target project ID is required")
39371
40208
  });
39372
40209
  var deleteRBACPolicyInputSchema = projectScopedInputSchema.extend({
39373
- policyId: import_zod24.z.string()
40210
+ policyId: import_zod23.z.string()
39374
40211
  });
39375
40212
 
39376
40213
  // src/server/Network/helpers/rbacPolicyHelpers.ts
@@ -40127,7 +40964,7 @@ __name(createContext, "createContext");
40127
40964
  // src/server/server.ts
40128
40965
  var import_path2 = __toESM(require("path"));
40129
40966
  var import_node_stream4 = require("stream");
40130
- var import_zod25 = require("zod");
40967
+ var import_zod24 = require("zod");
40131
40968
 
40132
40969
  // src/server/aurora-fastify-plugins/csrfProtection.ts
40133
40970
  var import_csrf_protection = __toESM(require("@fastify/csrf-protection"));
@@ -40328,7 +41165,7 @@ async function createServer(config) {
40328
41165
  router: appRouter,
40329
41166
  createContext: /* @__PURE__ */ __name((opts) => createContext(opts, contextConfig), "createContext"),
40330
41167
  onError: /* @__PURE__ */ __name((err) => {
40331
- if (err.error.cause instanceof import_zod25.ZodError) err.error.message = err.error.cause.issues.map((e) => e.message).join(",");
41168
+ if (err.error.cause instanceof import_zod24.ZodError) err.error.message = err.error.cause.issues.map((e) => e.message).join(",");
40332
41169
  }, "onError")
40333
41170
  }
40334
41171
  });