@cobaltcore-dev/aurora 1.2.0 → 2.0.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 (132) hide show
  1. package/dist/client/DeleteFlavorModal-C2MqUgGR.mjs +801 -0
  2. package/dist/client/DeleteFlavorModal-C2MqUgGR.mjs.map +1 -0
  3. package/dist/client/{FiltersInput-CKI6zWnm.mjs → FiltersInput-Becwxo_J.mjs} +36 -33
  4. package/dist/client/FiltersInput-Becwxo_J.mjs.map +1 -0
  5. package/dist/client/FloatingIpActionModals-Cq10WljB.mjs +466 -0
  6. package/dist/client/FloatingIpActionModals-Cq10WljB.mjs.map +1 -0
  7. package/dist/client/ImageToastNotifications-BWfzOUMc.mjs +1135 -0
  8. package/dist/client/ImageToastNotifications-BWfzOUMc.mjs.map +1 -0
  9. package/dist/client/RouteIdLevelDefaultError-CSseJ1yG.mjs +26 -0
  10. package/dist/client/RouteIdLevelDefaultError-CSseJ1yG.mjs.map +1 -0
  11. package/dist/client/{TwoColumnDescriptionList-NvgvErua.mjs → TwoColumnDescriptionList-DKS42RKa.mjs} +9 -3
  12. package/dist/client/TwoColumnDescriptionList-DKS42RKa.mjs.map +1 -0
  13. package/dist/client/{_flavorId-a9Gu66R1.mjs → _flavorId-CaA4-Pnf.mjs} +4 -3
  14. package/dist/client/_flavorId-CaA4-Pnf.mjs.map +1 -0
  15. package/dist/client/_flavorId-DYv_StX22.mjs +276 -0
  16. package/dist/client/_flavorId-DYv_StX22.mjs.map +1 -0
  17. package/dist/client/_flavorId-YV3jCS5g.mjs +22 -0
  18. package/dist/client/_flavorId-YV3jCS5g.mjs.map +1 -0
  19. package/dist/client/{_floatingIpId-AZ11Ksmu.mjs → _floatingIpId-BeYElanB.mjs} +2 -2
  20. package/dist/client/_floatingIpId-BeYElanB.mjs.map +1 -0
  21. package/dist/client/_floatingIpId-Dpsc7GU4.mjs +196 -0
  22. package/dist/client/_floatingIpId-Dpsc7GU4.mjs.map +1 -0
  23. package/dist/client/_imageId-mfKp3JHn.mjs +558 -0
  24. package/dist/client/_imageId-mfKp3JHn.mjs.map +1 -0
  25. package/dist/client/_imageId-mpXUMCh_.mjs +22 -0
  26. package/dist/client/_imageId-mpXUMCh_.mjs.map +1 -0
  27. package/dist/client/{_imageId-D76BxvFY.mjs → _imageId-pBSS0YcC.mjs} +4 -3
  28. package/dist/client/_imageId-pBSS0YcC.mjs.map +1 -0
  29. package/dist/client/{_projectId-B3wmSala.mjs → _projectId-AjgJX6Gl.mjs} +2 -2
  30. package/dist/client/_projectId-AjgJX6Gl.mjs.map +1 -0
  31. package/dist/client/{_projectId-DPzv0CCp.mjs → _projectId-BnTBxKfp.mjs} +4 -4
  32. package/dist/client/_projectId-BnTBxKfp.mjs.map +1 -0
  33. package/dist/client/{_projectId-DfeVJy0G.mjs → _projectId-CVrdJ52s.mjs} +2 -2
  34. package/dist/client/_projectId-CVrdJ52s.mjs.map +1 -0
  35. package/dist/client/_projectId-DgciZslU.mjs +92 -0
  36. package/dist/client/_projectId-DgciZslU.mjs.map +1 -0
  37. package/dist/client/{_securityGroupId-D85mSigA.mjs → _securityGroupId-AO3wnH8g.mjs} +2 -2
  38. package/dist/client/_securityGroupId-AO3wnH8g.mjs.map +1 -0
  39. package/dist/client/{_securityGroupId-C5huJNfO.mjs → _securityGroupId-Bq0ZyG6z.mjs} +750 -741
  40. package/dist/client/_securityGroupId-Bq0ZyG6z.mjs.map +1 -0
  41. package/dist/client/_storageType-c-DAu8qr.mjs +858 -0
  42. package/dist/client/_storageType-c-DAu8qr.mjs.map +1 -0
  43. package/dist/client/components/{Error → Errors}/RouteError.d.ts +5 -0
  44. package/dist/client/components/Errors/RouteIdLevelDefaultError.d.ts +17 -0
  45. package/dist/client/components/Errors/ServiceLevelDefaultError.d.ts +9 -0
  46. package/dist/client/components/ListToolbar/SelectedFilters.d.ts +7 -2
  47. package/dist/client/components/ListToolbar/types.d.ts +6 -0
  48. package/dist/client/components/ListToolbar/valueLabels.d.ts +11 -0
  49. package/dist/client/flavors-Bgq3KZmx.mjs +659 -0
  50. package/dist/client/flavors-Bgq3KZmx.mjs.map +1 -0
  51. package/dist/client/{flavors-B0rbqffG.mjs → flavors-ZudiAiTU.mjs} +2 -2
  52. package/dist/client/{flavors-B0rbqffG.mjs.map → flavors-ZudiAiTU.mjs.map} +1 -1
  53. package/dist/client/floatingips-B1oLH8z6.mjs +551 -0
  54. package/dist/client/floatingips-B1oLH8z6.mjs.map +1 -0
  55. package/dist/client/floatingips-BeQEkVH8.mjs +14 -0
  56. package/dist/client/floatingips-BeQEkVH8.mjs.map +1 -0
  57. package/dist/client/floatingips-BtLg3yEQ.mjs +22 -0
  58. package/dist/client/floatingips-BtLg3yEQ.mjs.map +1 -0
  59. package/dist/client/floatingips-Cz9k80cc.mjs.map +1 -1
  60. package/dist/client/{images-BGQv9kKT.mjs → images-CnTW3bpy.mjs} +810 -770
  61. package/dist/client/images-CnTW3bpy.mjs.map +1 -0
  62. package/dist/client/{images-CPmMa4SQ.mjs → images-DZHDYDQu.mjs} +2 -2
  63. package/dist/client/{images-CPmMa4SQ.mjs.map → images-DZHDYDQu.mjs.map} +1 -1
  64. package/dist/client/index.js +412 -409
  65. package/dist/client/index.js.map +1 -1
  66. package/dist/client/{objects-BN-59Th0.mjs → objects-25xsh4Ia.mjs} +2 -2
  67. package/dist/client/objects-25xsh4Ia.mjs.map +1 -0
  68. package/dist/client/objects-KXY6dgwq.mjs +82 -0
  69. package/dist/client/objects-KXY6dgwq.mjs.map +1 -0
  70. package/dist/client/objects-QryuClU3.mjs +82 -0
  71. package/dist/client/objects-QryuClU3.mjs.map +1 -0
  72. package/dist/client/{objects-BE_u5yRt.mjs → objects-jYQBFzwg.mjs} +4671 -4702
  73. package/dist/client/objects-jYQBFzwg.mjs.map +1 -0
  74. package/dist/client/projects-yiK0HGSA.mjs.map +1 -1
  75. package/dist/client/securitygroups--0PS2wbM.mjs +580 -0
  76. package/dist/client/securitygroups--0PS2wbM.mjs.map +1 -0
  77. package/dist/client/{securitygroups-DEJQBcxm.mjs → securitygroups-BRndk7wh.mjs} +2 -2
  78. package/dist/client/{securitygroups-DEJQBcxm.mjs.map → securitygroups-BRndk7wh.mjs.map} +1 -1
  79. package/dist/client/securitygroups-CdN4yAAw.mjs +14 -0
  80. package/dist/client/securitygroups-CdN4yAAw.mjs.map +1 -0
  81. package/dist/client/securitygroups-Cs2Op-tu.mjs +22 -0
  82. package/dist/client/securitygroups-Cs2Op-tu.mjs.map +1 -0
  83. package/dist/client/securitygroups-CtKne8UQ.mjs.map +1 -1
  84. package/dist/client/trpcClient.d.ts +6 -1
  85. package/dist/client/useModalTracking-BYd37pXm.mjs +37 -0
  86. package/dist/client/useModalTracking-BYd37pXm.mjs.map +1 -0
  87. package/dist/client/useSecurityGroupPermissions--hK4aR8Y.mjs +294 -0
  88. package/dist/client/useSecurityGroupPermissions--hK4aR8Y.mjs.map +1 -0
  89. package/dist/server/index.d.ts +6 -1
  90. package/dist/server/index.js +96 -30
  91. package/package.json +4 -4
  92. package/dist/client/DeleteFlavorModal-V44QCJBd.mjs +0 -773
  93. package/dist/client/DeleteFlavorModal-V44QCJBd.mjs.map +0 -1
  94. package/dist/client/FiltersInput-CKI6zWnm.mjs.map +0 -1
  95. package/dist/client/FloatingIpActionModals-CwEC5GR-.mjs +0 -460
  96. package/dist/client/FloatingIpActionModals-CwEC5GR-.mjs.map +0 -1
  97. package/dist/client/ImageToastNotifications-m6c3tVPb.mjs +0 -1152
  98. package/dist/client/ImageToastNotifications-m6c3tVPb.mjs.map +0 -1
  99. package/dist/client/TwoColumnDescriptionList-NvgvErua.mjs.map +0 -1
  100. package/dist/client/_flavorId-9PcBBW52.mjs +0 -231
  101. package/dist/client/_flavorId-9PcBBW52.mjs.map +0 -1
  102. package/dist/client/_flavorId-a9Gu66R1.mjs.map +0 -1
  103. package/dist/client/_floatingIpId-AE9Fu8l6.mjs +0 -210
  104. package/dist/client/_floatingIpId-AE9Fu8l6.mjs.map +0 -1
  105. package/dist/client/_floatingIpId-AZ11Ksmu.mjs.map +0 -1
  106. package/dist/client/_imageId-BtAql2Kr.mjs +0 -570
  107. package/dist/client/_imageId-BtAql2Kr.mjs.map +0 -1
  108. package/dist/client/_imageId-D76BxvFY.mjs.map +0 -1
  109. package/dist/client/_projectId-7Nl1MQTJ.mjs +0 -99
  110. package/dist/client/_projectId-7Nl1MQTJ.mjs.map +0 -1
  111. package/dist/client/_projectId-B3wmSala.mjs.map +0 -1
  112. package/dist/client/_projectId-DPzv0CCp.mjs.map +0 -1
  113. package/dist/client/_projectId-DfeVJy0G.mjs.map +0 -1
  114. package/dist/client/_securityGroupId-C5huJNfO.mjs.map +0 -1
  115. package/dist/client/_securityGroupId-D85mSigA.mjs.map +0 -1
  116. package/dist/client/_storageType-Dy9uCVXr.mjs +0 -845
  117. package/dist/client/_storageType-Dy9uCVXr.mjs.map +0 -1
  118. package/dist/client/flavors-Rl20bxVU.mjs +0 -628
  119. package/dist/client/flavors-Rl20bxVU.mjs.map +0 -1
  120. package/dist/client/floatingips-xbO_mu4f.mjs +0 -576
  121. package/dist/client/floatingips-xbO_mu4f.mjs.map +0 -1
  122. package/dist/client/images-BGQv9kKT.mjs.map +0 -1
  123. package/dist/client/objects-BE_u5yRt.mjs.map +0 -1
  124. package/dist/client/objects-BN-59Th0.mjs.map +0 -1
  125. package/dist/client/objects-D9Luf9KB.mjs +0 -66
  126. package/dist/client/objects-D9Luf9KB.mjs.map +0 -1
  127. package/dist/client/securitygroups-BjhgLBHm.mjs +0 -620
  128. package/dist/client/securitygroups-BjhgLBHm.mjs.map +0 -1
  129. package/dist/client/useModalTracking-BcOKTYRN.mjs +0 -33
  130. package/dist/client/useModalTracking-BcOKTYRN.mjs.map +0 -1
  131. package/dist/client/useSecurityGroupPermissions-BOyLQLdC.mjs +0 -227
  132. package/dist/client/useSecurityGroupPermissions-BOyLQLdC.mjs.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useSecurityGroupPermissions--hK4aR8Y.mjs","names":["getSecurityGroupDeletedToast","name","message","description","getSecurityGroupDeleteErrorToast","errorMessage","getSecurityGroupUpdatedToast","getSecurityGroupUpdateErrorToast","getSecurityGroupRuleCreatedToast","getSecurityGroupRuleCreateErrorToast","getSecurityGroupRuleDeletedToast","getSecurityGroupRuleDeleteErrorToast","getRBACPolicyAddedToast","targetTenant","getRBACPolicyAddErrorToast","getRBACPolicyDeletedToast","getRBACPolicyDeleteErrorToast","React","useState","useEffect","Modal","Form","FormRow","FormSection","TextInput","Button","ButtonRow","Spinner","ModalFooter","Textarea","Message","Status","EditSecurityGroupModal","securityGroup","open","onClose","onUpdate","isLoading","error","useLingui","properties","setProperties","name","description","errors","setErrors","handleInputChange","e","value","type","target","checked","prev","newErrors","validateForm","trim","t","Object","keys","length","handleSubmit","preventDefault","updateData","undefined","id","handleClose","onCancel","size","title","modalFooter","className","variant","onClick","disabled","data-testid","dismissible","status","label","onChange","required","errortext","placeholder","rows","z","useForm","useStore","React","Modal","Stack","Message","Form","FormSection","TextInput","useModalTracking","DeleteSecurityGroupDialog","isOpen","onClose","securityGroup","onDelete","isDeleting","error","useLingui","trackClose","markSubmitted","resetTracking","actionPrefix","formSchema","object","confirm","string","refine","value","message","t","form","defaultValues","validators","onSubmit","id","canDelete","store","state","isSubmitting","values","securityGroupName","name","handleClose","reset","open","onCancel","size","title","confirmButtonLabel","confirmButtonVariant","onConfirm","handleSubmit","cancelButtonLabel","disableConfirmButton","disableCancelButton","disableCloseButton","direction","gap","variant","p","className","e","preventDefault","Field","children","field","label","onChange","handleChange","target","placeholder","disabled","autoComplete","autoFocus","data-testid","required","trpcReact","useSecurityGroupPermissions","projectId","data","permissions","canView","canCreate","canUpdate","canDelete","canCreateRule","canDeleteRule","canManageAccess","canViewRBAC","isLoading","isError","network","canUser","useQuery","project_id","permission","enabled","Boolean","select","canCreateRBAC","canDeleteRBAC","staleTime","Infinity","gcTime"],"sources":["../../src/client/routes/_auth/projects/$projectId/network/securitygroups/-components/SecurityGroupToastNotifications.tsx","../../src/client/routes/_auth/projects/$projectId/network/securitygroups/-components/-modals/EditSecurityGroupModal.tsx","../../src/client/routes/_auth/projects/$projectId/network/securitygroups/-components/-modals/DeleteSecurityGroupDialog.tsx","../../src/client/routes/_auth/projects/$projectId/network/securitygroups/-hooks/useSecurityGroupPermissions.ts"],"sourcesContent":["import { ReactNode } from \"react\"\nimport { NotificationOptions } from \"@cloudoperators/juno-ui-components\"\nimport { Trans } from \"@lingui/react/macro\"\n\ntype ToastReturnType = { message: ReactNode } & NotificationOptions\n\n// ── Security Group operations ──────────────────────────────────────────────\n\nexport const getSecurityGroupDeletedToast = (name: string): ToastReturnType => ({\n message: <Trans>Security Group Deleted</Trans>,\n description: <Trans>Security group \"{name}\" was successfully deleted.</Trans>,\n})\n\nexport const getSecurityGroupDeleteErrorToast = (errorMessage: string): ToastReturnType => ({\n message: <Trans>Failed to Delete Security Group</Trans>,\n description: <Trans>Could not delete security group: {errorMessage}</Trans>,\n})\n\nexport const getSecurityGroupUpdatedToast = (name: string): ToastReturnType => ({\n message: <Trans>Security Group Updated</Trans>,\n description: <Trans>Security group \"{name}\" was successfully updated.</Trans>,\n})\n\nexport const getSecurityGroupUpdateErrorToast = (errorMessage: string): ToastReturnType => ({\n message: <Trans>Failed to Update Security Group</Trans>,\n description: <Trans>Could not update security group: {errorMessage}</Trans>,\n})\n\n// ── Rule operations ────────────────────────────────────────────────────────\n\nexport const getSecurityGroupRuleCreatedToast = (): ToastReturnType => ({\n message: <Trans>Rule Created</Trans>,\n description: <Trans>Security group rule was successfully created.</Trans>,\n})\n\nexport const getSecurityGroupRuleCreateErrorToast = (errorMessage: string): ToastReturnType => ({\n message: <Trans>Failed to Create Rule</Trans>,\n description: <Trans>Could not create security group rule: {errorMessage}</Trans>,\n})\n\nexport const getSecurityGroupRuleDeletedToast = (): ToastReturnType => ({\n message: <Trans>Rule Deleted</Trans>,\n description: <Trans>Security group rule was successfully deleted.</Trans>,\n})\n\nexport const getSecurityGroupRuleDeleteErrorToast = (errorMessage: string): ToastReturnType => ({\n message: <Trans>Failed to Delete Rule</Trans>,\n description: <Trans>Could not delete security group rule: {errorMessage}</Trans>,\n})\n\n// ── RBAC Policy operations ─────────────────────────────────────────────────\n\nexport const getRBACPolicyAddedToast = (targetTenant: string): ToastReturnType => ({\n message: <Trans>Security Group Shared</Trans>,\n description: <Trans>Security group was successfully shared with project \"{targetTenant}\".</Trans>,\n})\n\nexport const getRBACPolicyAddErrorToast = (errorMessage: string): ToastReturnType => ({\n message: <Trans>Failed to Share Security Group</Trans>,\n description: <Trans>Could not share security group: {errorMessage}</Trans>,\n})\n\nexport const getRBACPolicyDeletedToast = (targetTenant: string): ToastReturnType => ({\n message: <Trans>Access Revoked</Trans>,\n description: <Trans>Access for project \"{targetTenant}\" was successfully revoked.</Trans>,\n})\n\nexport const getRBACPolicyDeleteErrorToast = (errorMessage: string): ToastReturnType => ({\n message: <Trans>Failed to Revoke Access</Trans>,\n description: <Trans>Could not revoke access: {errorMessage}</Trans>,\n})\n","import React, { useState, useEffect } from \"react\"\nimport { Trans, useLingui } from \"@lingui/react/macro\"\nimport {\n Modal,\n Form,\n FormRow,\n FormSection,\n TextInput,\n Button,\n ButtonRow,\n Spinner,\n ModalFooter,\n Textarea,\n Message,\n Status,\n} from \"@cloudoperators/juno-ui-components\"\nimport type { SecurityGroup } from \"@/server/Network/types/securityGroup\"\nimport { UpdateSecurityGroupInput } from \"@/server/Network/types/securityGroup\"\n\ninterface EditSecurityGroupModalProps {\n securityGroup: SecurityGroup\n open: boolean\n onClose: () => void\n onUpdate?: (\n securityGroupId: string,\n data: Omit<UpdateSecurityGroupInput, \"securityGroupId\" | \"project_id\">\n ) => Promise<void>\n isLoading?: boolean\n error?: string | null\n}\n\ninterface SecurityGroupProperties {\n name: string\n description: string\n}\n\nexport const EditSecurityGroupModal: React.FC<EditSecurityGroupModalProps> = ({\n securityGroup,\n open,\n onClose,\n onUpdate,\n isLoading = false,\n error = null,\n}) => {\n const { t } = useLingui()\n\n const [properties, setProperties] = useState<SecurityGroupProperties>({\n name: securityGroup.name || \"\",\n description: securityGroup.description || \"\",\n })\n const [errors, setErrors] = useState<{ [key: string]: string }>({})\n\n // Update properties when securityGroup changes\n useEffect(() => {\n setProperties({\n name: securityGroup.name || \"\",\n description: securityGroup.description || \"\",\n })\n }, [securityGroup])\n\n const handleInputChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {\n const { name, value, type } = e.target\n const checked = (e.target as HTMLInputElement).checked\n\n setProperties((prev) => ({\n ...prev,\n [name]: type === \"checkbox\" ? checked : value,\n }))\n\n if (errors[name]) {\n setErrors((prev) => {\n const newErrors = { ...prev }\n delete newErrors[name]\n return newErrors\n })\n }\n }\n\n const validateForm = (): boolean => {\n const newErrors: { [key: string]: string } = {}\n\n if (!properties.name || properties.name.trim() === \"\") {\n newErrors.name = t`Security group name is required`\n }\n\n setErrors(newErrors)\n return Object.keys(newErrors).length === 0\n }\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault()\n\n if (!validateForm()) {\n return\n }\n\n if (onUpdate) {\n // Prepare base update data without stateful\n const updateData: Omit<UpdateSecurityGroupInput, \"securityGroupId\" | \"project_id\"> = {\n name: properties.name.trim(),\n description: properties.description.trim() || undefined,\n }\n\n // Note: We deliberately do NOT include 'stateful' field here\n // because it requires special 'update_security_group:stateful' permission\n // which needs cloud admin role. Regular users can only update name/description.\n\n await onUpdate(securityGroup.id, updateData)\n }\n }\n\n const handleClose = () => {\n setErrors({})\n onClose()\n }\n\n return (\n <Modal\n open={open}\n onCancel={handleClose}\n size=\"large\"\n title={t`Edit Security Group`}\n modalFooter={\n <ModalFooter className=\"flex justify-end\">\n <ButtonRow>\n <Button variant=\"default\" onClick={handleClose} disabled={isLoading}>\n <Trans>Cancel</Trans>\n </Button>\n <Button\n variant=\"primary\"\n onClick={(e) => {\n handleSubmit(e)\n }}\n disabled={isLoading}\n data-testid=\"update-security-group-button\"\n >\n {isLoading ? <Spinner size=\"small\" /> : <Trans>Update Security Group</Trans>}\n </Button>\n </ButtonRow>\n </ModalFooter>\n }\n >\n {/* Error Message */}\n {error && (\n <Message dismissible={false} variant=\"error\" className=\"mb-4\">\n {error}\n </Message>\n )}\n\n {isLoading && <Status status=\"progress\" title={t`Updating Security Group...`} className=\"mt-0\" />}\n\n {!isLoading && (\n <Form className=\"mb-6\">\n <FormSection className=\"mb-6\">\n <FormRow className=\"mb-6\">\n <TextInput\n id=\"name\"\n name=\"name\"\n label={t`Name`}\n value={properties.name}\n onChange={handleInputChange}\n required\n errortext={errors.name}\n placeholder={t`Type name`}\n disabled={isLoading}\n />\n </FormRow>\n\n <FormRow className=\"mb-6\">\n <Textarea\n id=\"description\"\n name=\"description\"\n label={t`Description`}\n value={properties.description}\n onChange={handleInputChange}\n placeholder={t`Description`}\n disabled={isLoading}\n rows={3}\n />\n </FormRow>\n {/* Note: Stateful checkbox is not shown here because it requires 'update_security_group:stateful' permission (cloud admin only) */}\n </FormSection>\n </Form>\n )}\n </Modal>\n )\n}\n","import { z } from \"zod\"\nimport { useForm, useStore } from \"@tanstack/react-form\"\nimport React from \"react\"\nimport { Modal, Stack, Message, Form, FormSection, TextInput } from \"@cloudoperators/juno-ui-components\"\nimport { Trans, useLingui } from \"@lingui/react/macro\"\nimport type { SecurityGroup } from \"@/server/Network/types/securityGroup\"\nimport { useModalTracking } from \"@/client/hooks/useModalTracking\"\n\ninterface DeleteSecurityGroupDialogProps {\n isOpen: boolean\n securityGroup: SecurityGroup\n onClose: () => void\n onDelete: (securityGroupId: string) => void\n isDeleting?: boolean\n error?: string | null\n}\n\nexport const DeleteSecurityGroupDialog: React.FC<DeleteSecurityGroupDialogProps> = ({\n isOpen,\n onClose,\n securityGroup,\n onDelete,\n isDeleting = false,\n error = null,\n}) => {\n const { t } = useLingui()\n\n const { trackClose, markSubmitted, resetTracking } = useModalTracking({\n isOpen,\n actionPrefix: \"network.securitygroup.delete\",\n })\n\n const formSchema = z.object({\n confirm: z.string().refine((value) => value === \"delete\", {\n message: t`Type \"delete\" to confirm`,\n }),\n })\n\n const form = useForm({\n defaultValues: {\n confirm: \"\",\n },\n validators: {\n onSubmit: formSchema,\n },\n onSubmit: async () => {\n if (isDeleting) return\n\n markSubmitted()\n onDelete(securityGroup.id)\n },\n })\n\n const canDelete = useStore(form.store, (state) => state.isSubmitting || state.values.confirm !== \"delete\")\n\n const securityGroupName = securityGroup.name || securityGroup.id\n\n const handleClose = () => {\n trackClose()\n form.reset()\n resetTracking()\n onClose()\n }\n\n return (\n <Modal\n open={isOpen}\n onCancel={handleClose}\n size=\"small\"\n title={t`Delete Security Group \"${securityGroupName}\"`}\n confirmButtonLabel={isDeleting ? t`Deleting...` : t`Delete Group`}\n confirmButtonVariant=\"primary-danger\"\n onConfirm={form.handleSubmit}\n cancelButtonLabel={t`Cancel`}\n disableConfirmButton={canDelete || isDeleting}\n disableCancelButton={isDeleting}\n disableCloseButton={isDeleting}\n >\n <Stack direction=\"vertical\" gap=\"4\">\n {error && <Message variant=\"error\">{error}</Message>}\n\n <p className=\"text-theme-default\">\n <Trans>This action cannot be undone. The security group will be permanently deleted.</Trans>\n </p>\n\n <Form\n className=\"mb-0\"\n id=\"delete-security-group-form\"\n onSubmit={(e) => {\n e.preventDefault()\n form.handleSubmit()\n }}\n >\n <FormSection>\n <form.Field\n name=\"confirm\"\n children={(field) => (\n <TextInput\n label={t`Type \"delete\" to confirm`}\n id={field.name}\n name={field.name}\n value={field.state.value}\n onChange={(e) => field.handleChange(e.target.value)}\n placeholder=\"delete\"\n disabled={isDeleting}\n autoComplete=\"off\"\n autoFocus\n data-testid=\"delete-confirmation-input\"\n required\n />\n )}\n />\n </FormSection>\n </Form>\n </Stack>\n </Modal>\n )\n}\n","import { trpcReact } from \"@/client/trpcClient\"\n\nexport interface SecurityGroupPermissions {\n canView: boolean\n canCreate: boolean\n canUpdate: boolean\n canDelete: boolean\n canCreateRule: boolean\n canDeleteRule: boolean\n canManageAccess: boolean\n canViewRBAC: boolean\n}\n\n/**\n * Hook to fetch security group permissions for the current user\n * Uses React Query with infinite cache since permissions don't change during session\n */\nexport function useSecurityGroupPermissions(projectId: string) {\n const {\n data: permissions = {\n canView: false,\n canCreate: false,\n canUpdate: false,\n canDelete: false,\n canCreateRule: false,\n canDeleteRule: false,\n canManageAccess: false,\n canViewRBAC: false,\n },\n isLoading,\n isError,\n } = trpcReact.network.canUser.useQuery(\n {\n project_id: projectId || \"\",\n permission: [\n \"network:security_groups:read\",\n \"network:security_groups:create\",\n \"network:security_groups:update\",\n \"network:security_groups:delete\",\n \"network:security_group_rules:create\",\n \"network:security_group_rules:delete\",\n \"network:rbac_policies:create\",\n \"network:rbac_policies:delete\",\n \"network:rbac_policies:read\",\n ],\n },\n {\n enabled: Boolean(projectId), // Only fetch if we have a project ID\n select: ([\n canView,\n canCreate,\n canUpdate,\n canDelete,\n canCreateRule,\n canDeleteRule,\n canCreateRBAC,\n canDeleteRBAC,\n canViewRBAC,\n ]) => ({\n canView,\n canCreate,\n canUpdate,\n canDelete,\n canCreateRule,\n canDeleteRule,\n canManageAccess: canCreateRBAC && canDeleteRBAC,\n canViewRBAC,\n }),\n staleTime: Infinity, // Permissions don't change during session\n gcTime: Infinity, // Keep in cache forever (previously cacheTime)\n }\n )\n\n return {\n permissions,\n isLoading,\n isError,\n }\n}\n"],"mappings":"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"}
@@ -965,6 +965,7 @@ declare const buildBaseRouter: (policyDir: string) => _trpc_server.TRPCBuiltRout
965
965
  tags_any?: string | undefined;
966
966
  not_tags?: string | undefined;
967
967
  not_tags_any?: string | undefined;
968
+ stateful?: boolean | undefined;
968
969
  searchTerm?: string | undefined;
969
970
  };
970
971
  output: {
@@ -1515,7 +1516,11 @@ declare const buildBaseRouter: (policyDir: string) => _trpc_server.TRPCBuiltRout
1515
1516
  tempUrlKey?: string | undefined;
1516
1517
  tempUrlKey2?: string | undefined;
1517
1518
  };
1518
- output: boolean;
1519
+ output: {
1520
+ created: true;
1521
+ optionsApplied: boolean;
1522
+ optionsError?: string | undefined;
1523
+ };
1519
1524
  meta: object;
1520
1525
  }>;
1521
1526
  getContainerMetadata: _trpc_server.TRPCQueryProcedure<{
@@ -7,7 +7,11 @@ var __getProtoOf = Object.getPrototypeOf;
7
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
8
8
  var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
9
9
  var __commonJS = (cb, mod) => function __require() {
10
- return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
10
+ try {
11
+ return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
12
+ } catch (e) {
13
+ throw mod = 0, e;
14
+ }
11
15
  };
12
16
  var __export = (target, all) => {
13
17
  for (var name in all)
@@ -27306,7 +27310,7 @@ var require_pathHelpers = __commonJS({
27306
27310
  });
27307
27311
  exports2.encodeOpenstackPathSegment = encodeOpenstackPathSegment2;
27308
27312
  exports2.encodeOpenstackPath = encodeOpenstackPath;
27309
- exports2.validateAndEncodeResourceId = validateAndEncodeResourceId6;
27313
+ exports2.validateAndEncodeResourceId = validateAndEncodeResourceId8;
27310
27314
  var error_1 = require_error();
27311
27315
  function encodeOpenstackPathSegment2(segment, label = "Path segment") {
27312
27316
  if (!segment || typeof segment !== "string") {
@@ -27328,10 +27332,10 @@ var require_pathHelpers = __commonJS({
27328
27332
  return segments.map((seg, idx) => encodeOpenstackPathSegment2(seg, `Path segment ${idx}`)).join("/");
27329
27333
  }
27330
27334
  __name(encodeOpenstackPath, "encodeOpenstackPath");
27331
- function validateAndEncodeResourceId6(id, resourceType = "Resource") {
27335
+ function validateAndEncodeResourceId8(id, resourceType = "Resource") {
27332
27336
  return encodeOpenstackPathSegment2(id, `${resourceType} ID`);
27333
27337
  }
27334
- __name(validateAndEncodeResourceId6, "validateAndEncodeResourceId");
27338
+ __name(validateAndEncodeResourceId8, "validateAndEncodeResourceId");
27335
27339
  }
27336
27340
  });
27337
27341
 
@@ -32490,7 +32494,7 @@ function buildObjectMetadataHeaders(options) {
32490
32494
  }
32491
32495
  __name(buildObjectMetadataHeaders, "buildObjectMetadataHeaders");
32492
32496
  function mapErrorResponseToTRPCError2(error, context) {
32493
- const { operation, container, object, additionalInfo } = context;
32497
+ const { operation, container, object, additionalInfo, preconditionFailedMeansAlreadyExists } = context;
32494
32498
  const baseMessage = `Failed to ${operation}`;
32495
32499
  const containerInfo = container ? ` container: ${container}` : "";
32496
32500
  const objectInfo = object ? `, object: ${object}` : "";
@@ -32521,6 +32525,14 @@ function mapErrorResponseToTRPCError2(error, context) {
32521
32525
  code: "CONFLICT",
32522
32526
  message: `Conflict - ${operation}${containerInfo}${objectInfo}${extraInfo}`
32523
32527
  });
32528
+ case 412:
32529
+ return preconditionFailedMeansAlreadyExists ? new import_server10.TRPCError({
32530
+ code: "CONFLICT",
32531
+ message: `Conflict - ${operation}${containerInfo}${objectInfo} - already exists${extraInfo}`
32532
+ }) : new import_server10.TRPCError({
32533
+ code: "PRECONDITION_FAILED",
32534
+ message: `Precondition failed - ${operation}${containerInfo}${objectInfo}${extraInfo}`
32535
+ });
32524
32536
  case 413:
32525
32537
  return new import_server10.TRPCError({
32526
32538
  code: "PAYLOAD_TOO_LARGE",
@@ -32814,6 +32826,11 @@ var createContainerInputSchema = baseContainerInputSchema.extend({
32814
32826
  tempUrlKey: import_zod11.z.string().optional(),
32815
32827
  tempUrlKey2: import_zod11.z.string().optional()
32816
32828
  });
32829
+ var createContainerResultSchema = import_zod11.z.object({
32830
+ created: import_zod11.z.literal(true),
32831
+ optionsApplied: import_zod11.z.boolean(),
32832
+ optionsError: import_zod11.z.string().optional()
32833
+ });
32817
32834
  var updateContainerMetadataInputSchema = baseContainerInputSchema.extend({
32818
32835
  metadata: import_zod11.z.record(import_zod11.z.string(), import_zod11.z.string()).optional(),
32819
32836
  removeMetadata: import_zod11.z.array(import_zod11.z.string()).optional(),
@@ -33151,21 +33168,54 @@ var swiftRouter = {
33151
33168
  createContainer: projectScopedProcedure.input(projectScopedInputSchema.extend(createContainerInputSchema.shape)).mutation(async ({ input, ctx }) => {
33152
33169
  return withErrorHandling2(async () => {
33153
33170
  const { account, container, ...options } = input;
33171
+ const { storagePolicy, ...applyOptions } = options;
33154
33172
  const openstackSession = ctx.openstack;
33155
33173
  const swift = openstackSession?.service("swift");
33156
33174
  validateSwiftService(swift);
33157
- const headers = buildContainerMetadataHeaders(options);
33158
33175
  const accountPath = account || "";
33159
33176
  const url = accountPath ? `${accountPath}/${encodeURIComponent(container)}` : encodeURIComponent(container);
33160
- await swift.put(url, void 0, {
33161
- headers
33177
+ const probeHeaders = buildContainerMetadataHeaders({
33178
+ storagePolicy
33179
+ });
33180
+ const response = await swift.put(url, void 0, {
33181
+ headers: probeHeaders
33162
33182
  }).catch((error) => {
33163
33183
  throw mapErrorResponseToTRPCError2(error, {
33164
33184
  operation: "create container",
33165
33185
  container
33166
33186
  });
33167
33187
  });
33168
- return true;
33188
+ if (response.status === 202) {
33189
+ throw new import_server11.TRPCError({
33190
+ code: "CONFLICT",
33191
+ message: `Conflict - create container - container already exists: ${container}`
33192
+ });
33193
+ }
33194
+ const optionHeaders = buildContainerMetadataHeaders(applyOptions);
33195
+ if (Object.keys(optionHeaders).length === 0) {
33196
+ return {
33197
+ created: true,
33198
+ optionsApplied: true
33199
+ };
33200
+ }
33201
+ let optionsError;
33202
+ await swift.post(url, void 0, {
33203
+ headers: optionHeaders
33204
+ }).catch((error) => {
33205
+ optionsError = mapErrorResponseToTRPCError2(error, {
33206
+ operation: "apply container settings",
33207
+ container,
33208
+ additionalInfo: "container was created but its metadata/ACL/quota could not be applied"
33209
+ }).message;
33210
+ });
33211
+ return optionsError === void 0 ? {
33212
+ created: true,
33213
+ optionsApplied: true
33214
+ } : {
33215
+ created: true,
33216
+ optionsApplied: false,
33217
+ optionsError
33218
+ };
33169
33219
  }, "create container");
33170
33220
  }),
33171
33221
  /**
@@ -33591,7 +33641,8 @@ var swiftRouter = {
33591
33641
  const normalizedPath = normalizeFolderPath(folderPath);
33592
33642
  const headers = {
33593
33643
  "Content-Type": "application/directory",
33594
- "Content-Length": "0"
33644
+ "Content-Length": "0",
33645
+ "If-None-Match": "*"
33595
33646
  };
33596
33647
  if (metadata) {
33597
33648
  Object.entries(metadata).forEach(([key, value]) => {
@@ -33600,14 +33651,14 @@ var swiftRouter = {
33600
33651
  }
33601
33652
  const accountPath = account || "";
33602
33653
  const url = accountPath ? `${accountPath}/${encodeURIComponent(container)}/${encodeURIComponent(normalizedPath)}` : `${encodeURIComponent(container)}/${encodeURIComponent(normalizedPath)}`;
33603
- await swift.put(url, {
33604
- headers,
33605
- body: new ArrayBuffer(0)
33654
+ await swift.put(url, new ArrayBuffer(0), {
33655
+ headers
33606
33656
  }).catch((error) => {
33607
33657
  throw mapErrorResponseToTRPCError2(error, {
33608
33658
  operation: "create folder",
33609
33659
  container,
33610
- object: normalizedPath
33660
+ object: normalizedPath,
33661
+ preconditionFailedMeansAlreadyExists: true
33611
33662
  });
33612
33663
  });
33613
33664
  return true;
@@ -34506,6 +34557,7 @@ var S3_ERROR_MAP = {
34506
34557
  BucketAlreadyExists: "CONFLICT",
34507
34558
  BucketAlreadyOwnedByYou: "CONFLICT",
34508
34559
  BucketNotEmpty: "PRECONDITION_FAILED",
34560
+ PreconditionFailed: "CONFLICT",
34509
34561
  InvalidBucketState: "BAD_REQUEST",
34510
34562
  VersioningNotEnabled: "PRECONDITION_FAILED",
34511
34563
  AccessDenied: "FORBIDDEN",
@@ -34530,7 +34582,7 @@ function mapS3ErrorToTRPCError(error, context) {
34530
34582
  }
34531
34583
  const s3Error = error;
34532
34584
  const errorCode = s3Error.Code ?? s3Error.name ?? "";
34533
- const trpcCode = S3_ERROR_MAP[errorCode] ?? "INTERNAL_SERVER_ERROR";
34585
+ const trpcCode = errorCode === "PreconditionFailed" && !context.preconditionFailedMeansAlreadyExists ? "PRECONDITION_FAILED" : S3_ERROR_MAP[errorCode] ?? "INTERNAL_SERVER_ERROR";
34534
34586
  if (!S3_ERROR_MAP[errorCode] && errorCode) {
34535
34587
  import_signal_openstack4.logger.warn("Unmapped S3 error code", {
34536
34588
  errorCode,
@@ -34549,6 +34601,8 @@ function mapS3ErrorToTRPCError(error, context) {
34549
34601
  parts.push("Invalid access key \u2014 your credentials may be expired or incorrect");
34550
34602
  } else if (errorCode === "BucketNotEmpty") {
34551
34603
  parts.push("The bucket is not empty. Some objects or versions may still exist. Use 'Empty Bucket' first to delete all contents.");
34604
+ } else if (errorCode === "PreconditionFailed") {
34605
+ parts.push(context.preconditionFailedMeansAlreadyExists ? "The object already exists" : "Precondition failed");
34552
34606
  } else if (s3Error.message) {
34553
34607
  parts.push(s3Error.message);
34554
34608
  }
@@ -35869,8 +35923,10 @@ var objectRouter = {
35869
35923
  * Create a folder (zero-byte object with key ending in "/").
35870
35924
  *
35871
35925
  * In S3, folders are virtual - they're just zero-byte objects with keys ending in "/".
35872
- * This operation is idempotent - creating an existing folder succeeds.
35926
+ * The write is conditional (IfNoneMatch: "*"), so creating a folder that
35927
+ * already exists throws CONFLICT instead of silently overwriting it.
35873
35928
  *
35929
+ * @throws TRPCError CONFLICT - a folder (or object) with this key already exists
35874
35930
  * @throws TRPCError NOT_FOUND - bucket does not exist
35875
35931
  * @throws TRPCError FORBIDDEN - no credentials or access denied
35876
35932
  */
@@ -35883,14 +35939,16 @@ var objectRouter = {
35883
35939
  Bucket: containerName,
35884
35940
  Key: normalizedPath,
35885
35941
  Body: Buffer.from(""),
35886
- ContentLength: 0
35942
+ ContentLength: 0,
35943
+ IfNoneMatch: "*"
35887
35944
  }));
35888
35945
  return true;
35889
35946
  } catch (error) {
35890
35947
  throw mapS3ErrorToTRPCError(error, {
35891
35948
  operation: "create folder",
35892
35949
  bucket: containerName,
35893
- key: normalizedPath
35950
+ key: normalizedPath,
35951
+ preconditionFailedMeansAlreadyExists: true
35894
35952
  });
35895
35953
  }
35896
35954
  }),
@@ -38706,6 +38764,8 @@ var listSecurityGroupsInputSchema = projectScopedInputSchema.extend({
38706
38764
  tags_any: import_zod23.z.string().optional(),
38707
38765
  not_tags: import_zod23.z.string().optional(),
38708
38766
  not_tags_any: import_zod23.z.string().optional(),
38767
+ // Filtered in the BFF layer, not forwarded to Neutron
38768
+ stateful: import_zod23.z.boolean().optional(),
38709
38769
  // BFF-side search (filtered in BFF layer, not sent to OpenStack)
38710
38770
  searchTerm: import_zod23.z.string().optional()
38711
38771
  });
@@ -39048,6 +39108,10 @@ var deduplicateSecurityGroupsById = /* @__PURE__ */ __name((items) => {
39048
39108
  }
39049
39109
  return Array.from(seen.values());
39050
39110
  }, "deduplicateSecurityGroupsById");
39111
+ var filterSecurityGroupsByStateful = /* @__PURE__ */ __name((items, stateful) => {
39112
+ if (stateful === void 0) return items;
39113
+ return items.filter((item) => (item.stateful ?? true) === stateful);
39114
+ }, "filterSecurityGroupsByStateful");
39051
39115
  var sortSecurityGroups = /* @__PURE__ */ __name((items, sortKey, sortDir = "asc") => {
39052
39116
  if (!sortKey) return items;
39053
39117
  return [
@@ -39099,7 +39163,7 @@ __name(fetchSecurityGroupsWithParams, "fetchSecurityGroupsWithParams");
39099
39163
  var securityGroupRouter = {
39100
39164
  list: projectScopedProcedure.input(listSecurityGroupsInputSchema).query(async ({ input, ctx }) => {
39101
39165
  return withErrorHandling3(async () => {
39102
- const { searchTerm, project_id, shared, sort_key, sort_dir, ...queryInput } = input;
39166
+ const { searchTerm, project_id, shared, stateful, sort_key, sort_dir, ...queryInput } = input;
39103
39167
  const network = getNetworkService(ctx);
39104
39168
  if (shared !== void 0) {
39105
39169
  const securityGroups = await fetchSecurityGroupsWithParams(network, {
@@ -39108,7 +39172,7 @@ var securityGroupRouter = {
39108
39172
  sort_key,
39109
39173
  sort_dir
39110
39174
  });
39111
- return filterBySearchParams(securityGroups, searchTerm, [
39175
+ return filterBySearchParams(filterSecurityGroupsByStateful(securityGroups, stateful), searchTerm, [
39112
39176
  "name",
39113
39177
  "description",
39114
39178
  "id"
@@ -39129,6 +39193,7 @@ var securityGroupRouter = {
39129
39193
  ...ownGroups,
39130
39194
  ...sharedGroups
39131
39195
  ]);
39196
+ combined = filterSecurityGroupsByStateful(combined, stateful);
39132
39197
  combined = filterBySearchParams(combined, searchTerm, [
39133
39198
  "name",
39134
39199
  "description",
@@ -39214,13 +39279,15 @@ var securityGroupRouter = {
39214
39279
  };
39215
39280
 
39216
39281
  // src/server/Network/routers/securityGroupRuleRouter.ts
39282
+ var import_signal_openstack9 = __toESM(require_esm());
39217
39283
  var SECURITY_GROUP_RULES_BASE_URL = "v2.0/security-group-rules";
39218
39284
  var securityGroupRuleRouter = {
39219
39285
  delete: projectScopedProcedure.input(deleteSecurityGroupRuleInputSchema).mutation(async ({ input, ctx }) => {
39220
39286
  return withErrorHandling3(async () => {
39221
39287
  const { ruleId } = input;
39222
39288
  const network = getNetworkService(ctx);
39223
- const response = await network.del(`${SECURITY_GROUP_RULES_BASE_URL}/${ruleId}`);
39289
+ const encodedRuleId = (0, import_signal_openstack9.validateAndEncodeResourceId)(ruleId, "Security group rule");
39290
+ const response = await network.del(`${SECURITY_GROUP_RULES_BASE_URL}/${encodedRuleId}`);
39224
39291
  if (!response?.ok) {
39225
39292
  throw SecurityGroupRuleErrorHandlers.delete(response, ruleId);
39226
39293
  }
@@ -39461,6 +39528,7 @@ function parseRBACPoliciesListResponse(data, operation) {
39461
39528
  __name(parseRBACPoliciesListResponse, "parseRBACPoliciesListResponse");
39462
39529
 
39463
39530
  // src/server/Network/routers/rbacPolicyRouter.ts
39531
+ var import_signal_openstack10 = __toESM(require_esm());
39464
39532
  var RBAC_POLICIES_BASE_URL = "v2.0/rbac-policies";
39465
39533
  var rbacPolicyRouter = {
39466
39534
  list: projectScopedProcedure.input(listRBACPoliciesForSecurityGroupInputSchema).query(async ({ input, ctx }) => {
@@ -39509,7 +39577,8 @@ var rbacPolicyRouter = {
39509
39577
  target_tenant: targetTenant
39510
39578
  }
39511
39579
  };
39512
- const response = await network.put(`${RBAC_POLICIES_BASE_URL}/${policyId}`, requestBody);
39580
+ const encodedPolicyId = (0, import_signal_openstack10.validateAndEncodeResourceId)(policyId, "RBAC policy");
39581
+ const response = await network.put(`${RBAC_POLICIES_BASE_URL}/${encodedPolicyId}`, requestBody);
39513
39582
  if (!response.ok) {
39514
39583
  throw RBACPolicyErrorHandlers.update(response, policyId);
39515
39584
  }
@@ -39521,7 +39590,8 @@ var rbacPolicyRouter = {
39521
39590
  return withErrorHandling3(async () => {
39522
39591
  const { policyId } = input;
39523
39592
  const network = getNetworkService(ctx);
39524
- const response = await network.del(`${RBAC_POLICIES_BASE_URL}/${policyId}`);
39593
+ const encodedPolicyId = (0, import_signal_openstack10.validateAndEncodeResourceId)(policyId, "RBAC policy");
39594
+ const response = await network.del(`${RBAC_POLICIES_BASE_URL}/${encodedPolicyId}`);
39525
39595
  if (!response.ok) {
39526
39596
  throw RBACPolicyErrorHandlers.delete(response, policyId);
39527
39597
  }
@@ -39686,10 +39756,6 @@ var NETWORK_MAPPINGS = {
39686
39756
  engine: "network",
39687
39757
  rule: "create_security_group_rule"
39688
39758
  },
39689
- "network:security_group_rules:update": {
39690
- engine: "network",
39691
- rule: "update_security_group_rule"
39692
- },
39693
39759
  "network:security_group_rules:delete": {
39694
39760
  engine: "network",
39695
39761
  rule: "delete_security_group_rule"
@@ -39766,7 +39832,7 @@ var buildAppRouter = /* @__PURE__ */ __name((policyDir, extraRouters = []) => {
39766
39832
  }, "buildAppRouter");
39767
39833
 
39768
39834
  // src/server/context.ts
39769
- var import_signal_openstack9 = __toESM(require_esm());
39835
+ var import_signal_openstack11 = __toESM(require_esm());
39770
39836
 
39771
39837
  // src/server/sessionCookie.ts
39772
39838
  var import_cookie = require("@fastify/cookie");
@@ -39854,7 +39920,7 @@ async function createContext(opts, config) {
39854
39920
  writable: false
39855
39921
  });
39856
39922
  if (currentAuthToken) {
39857
- openstackSession = await (0, import_signal_openstack9.SignalOpenstackSession)(normalizedEndpoint, {
39923
+ openstackSession = await (0, import_signal_openstack11.SignalOpenstackSession)(normalizedEndpoint, {
39858
39924
  auth: {
39859
39925
  identity: {
39860
39926
  methods: [
@@ -39917,7 +39983,7 @@ async function createContext(opts, config) {
39917
39983
  }
39918
39984
  }, "getUserInfo");
39919
39985
  const createSession = /* @__PURE__ */ __name(async (params) => {
39920
- openstackSession = await (0, import_signal_openstack9.SignalOpenstackSession)(normalizedEndpoint, {
39986
+ openstackSession = await (0, import_signal_openstack11.SignalOpenstackSession)(normalizedEndpoint, {
39921
39987
  auth: {
39922
39988
  identity: {
39923
39989
  methods: [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cobaltcore-dev/aurora",
3
- "version": "1.2.0",
3
+ "version": "2.0.0",
4
4
  "private": false,
5
5
  "description": "Aurora OpenStack dashboard — server and client library",
6
6
  "license": "Apache-2.0",
@@ -105,10 +105,10 @@
105
105
  "vite-plugin-dts": "^5.0.3",
106
106
  "vite-plugin-svgr": "^5.2.0",
107
107
  "vite-tsconfig-paths": "^6.1.1",
108
- "vitest": "^4.1.10",
108
+ "vitest": "^4.1.11",
109
109
  "@cobaltcore-dev/aurora-config": "0.0.1",
110
- "@cobaltcore-dev/policy-engine": "2.0.0",
111
- "@cobaltcore-dev/signal-openstack": "1.0.1"
110
+ "@cobaltcore-dev/signal-openstack": "1.0.1",
111
+ "@cobaltcore-dev/policy-engine": "2.0.0"
112
112
  },
113
113
  "scripts": {
114
114
  "build": "pnpm build:server && pnpm build:client",