- {customDomain.verification_errors?.includes(
+ {customDomain?.verification_errors?.includes(
'custom hostname does not CNAME to this zone.'
) && (
)}
- {!isValidating && customDomain.ssl.status === 'pending_validation' && (
+ {!isValidating && customDomain?.ssl.status === 'pending_validation' && (
)}
- {customDomain.ssl.status === 'pending_deployment' && (
+ {customDomain?.ssl.status === 'pending_deployment' && (
diff --git a/apps/studio/components/to-be-cleaned/Storage/AnalyticBucketDetails/CopyEnvButton.tsx b/apps/studio/components/interfaces/Storage/AnalyticBucketDetails/CopyEnvButton.tsx
similarity index 100%
rename from apps/studio/components/to-be-cleaned/Storage/AnalyticBucketDetails/CopyEnvButton.tsx
rename to apps/studio/components/interfaces/Storage/AnalyticBucketDetails/CopyEnvButton.tsx
diff --git a/apps/studio/components/to-be-cleaned/Storage/AnalyticBucketDetails/DecryptedReadOnlyInput.tsx b/apps/studio/components/interfaces/Storage/AnalyticBucketDetails/DecryptedReadOnlyInput.tsx
similarity index 100%
rename from apps/studio/components/to-be-cleaned/Storage/AnalyticBucketDetails/DecryptedReadOnlyInput.tsx
rename to apps/studio/components/interfaces/Storage/AnalyticBucketDetails/DecryptedReadOnlyInput.tsx
diff --git a/apps/studio/components/to-be-cleaned/Storage/AnalyticBucketDetails/NamespaceRow.tsx b/apps/studio/components/interfaces/Storage/AnalyticBucketDetails/NamespaceRow.tsx
similarity index 100%
rename from apps/studio/components/to-be-cleaned/Storage/AnalyticBucketDetails/NamespaceRow.tsx
rename to apps/studio/components/interfaces/Storage/AnalyticBucketDetails/NamespaceRow.tsx
diff --git a/apps/studio/components/to-be-cleaned/Storage/AnalyticBucketDetails/SimpleConfigurationDetails.tsx b/apps/studio/components/interfaces/Storage/AnalyticBucketDetails/SimpleConfigurationDetails.tsx
similarity index 100%
rename from apps/studio/components/to-be-cleaned/Storage/AnalyticBucketDetails/SimpleConfigurationDetails.tsx
rename to apps/studio/components/interfaces/Storage/AnalyticBucketDetails/SimpleConfigurationDetails.tsx
diff --git a/apps/studio/components/to-be-cleaned/Storage/AnalyticBucketDetails/constants.ts b/apps/studio/components/interfaces/Storage/AnalyticBucketDetails/constants.ts
similarity index 100%
rename from apps/studio/components/to-be-cleaned/Storage/AnalyticBucketDetails/constants.ts
rename to apps/studio/components/interfaces/Storage/AnalyticBucketDetails/constants.ts
diff --git a/apps/studio/components/to-be-cleaned/Storage/AnalyticBucketDetails/index.tsx b/apps/studio/components/interfaces/Storage/AnalyticBucketDetails/index.tsx
similarity index 100%
rename from apps/studio/components/to-be-cleaned/Storage/AnalyticBucketDetails/index.tsx
rename to apps/studio/components/interfaces/Storage/AnalyticBucketDetails/index.tsx
diff --git a/apps/studio/components/to-be-cleaned/Storage/AnalyticBucketDetails/useIcebergWrapper.tsx b/apps/studio/components/interfaces/Storage/AnalyticBucketDetails/useIcebergWrapper.tsx
similarity index 100%
rename from apps/studio/components/to-be-cleaned/Storage/AnalyticBucketDetails/useIcebergWrapper.tsx
rename to apps/studio/components/interfaces/Storage/AnalyticBucketDetails/useIcebergWrapper.tsx
diff --git a/apps/studio/components/layouts/StorageLayout/BucketRow.tsx b/apps/studio/components/interfaces/Storage/BucketRow.tsx
similarity index 100%
rename from apps/studio/components/layouts/StorageLayout/BucketRow.tsx
rename to apps/studio/components/interfaces/Storage/BucketRow.tsx
diff --git a/apps/studio/components/interfaces/Storage/CreateBucketModal.tsx b/apps/studio/components/interfaces/Storage/CreateBucketModal.tsx
index b9b58052e7bce..1523c488ee8c7 100644
--- a/apps/studio/components/interfaces/Storage/CreateBucketModal.tsx
+++ b/apps/studio/components/interfaces/Storage/CreateBucketModal.tsx
@@ -9,12 +9,12 @@ import { toast } from 'sonner'
import z from 'zod'
import { useParams } from 'common'
-import { useIcebergWrapperExtension } from 'components/to-be-cleaned/Storage/AnalyticBucketDetails/useIcebergWrapper'
-import { StorageSizeUnits } from 'components/to-be-cleaned/Storage/StorageSettings/StorageSettings.constants'
+import { useIcebergWrapperExtension } from 'components/interfaces/Storage/AnalyticBucketDetails/useIcebergWrapper'
+import { StorageSizeUnits } from 'components/interfaces/Storage/StorageSettings/StorageSettings.constants'
import {
convertFromBytes,
convertToBytes,
-} from 'components/to-be-cleaned/Storage/StorageSettings/StorageSettings.utils'
+} from 'components/interfaces/Storage/StorageSettings/StorageSettings.utils'
import { useProjectStorageConfigQuery } from 'data/config/project-storage-config-query'
import { useBucketCreateMutation } from 'data/storage/bucket-create-mutation'
import { useIcebergWrapperCreateMutation } from 'data/storage/iceberg-wrapper-create-mutation'
diff --git a/apps/studio/components/to-be-cleaned/Storage/DeleteBucketModal.tsx b/apps/studio/components/interfaces/Storage/DeleteBucketModal.tsx
similarity index 100%
rename from apps/studio/components/to-be-cleaned/Storage/DeleteBucketModal.tsx
rename to apps/studio/components/interfaces/Storage/DeleteBucketModal.tsx
diff --git a/apps/studio/components/interfaces/Storage/EditBucketModal.tsx b/apps/studio/components/interfaces/Storage/EditBucketModal.tsx
index 5abb63cd4d582..5a9ff0ea2a949 100644
--- a/apps/studio/components/interfaces/Storage/EditBucketModal.tsx
+++ b/apps/studio/components/interfaces/Storage/EditBucketModal.tsx
@@ -5,11 +5,11 @@ import { useEffect, useState } from 'react'
import { toast } from 'sonner'
import { Button, Collapsible, Form, Input, Listbox, Modal, Toggle, cn } from 'ui'
-import { StorageSizeUnits } from 'components/to-be-cleaned/Storage/StorageSettings/StorageSettings.constants'
+import { StorageSizeUnits } from 'components/interfaces/Storage/StorageSettings/StorageSettings.constants'
import {
convertFromBytes,
convertToBytes,
-} from 'components/to-be-cleaned/Storage/StorageSettings/StorageSettings.utils'
+} from 'components/interfaces/Storage/StorageSettings/StorageSettings.utils'
import { InlineLink } from 'components/ui/InlineLink'
import { useProjectStorageConfigQuery } from 'data/config/project-storage-config-query'
import { useBucketUpdateMutation } from 'data/storage/bucket-update-mutation'
diff --git a/apps/studio/components/to-be-cleaned/Storage/EmptyBucketModal.tsx b/apps/studio/components/interfaces/Storage/EmptyBucketModal.tsx
similarity index 100%
rename from apps/studio/components/to-be-cleaned/Storage/EmptyBucketModal.tsx
rename to apps/studio/components/interfaces/Storage/EmptyBucketModal.tsx
diff --git a/apps/studio/components/layouts/StorageLayout/Storage.Commands.tsx b/apps/studio/components/interfaces/Storage/Storage.Commands.tsx
similarity index 100%
rename from apps/studio/components/layouts/StorageLayout/Storage.Commands.tsx
rename to apps/studio/components/interfaces/Storage/Storage.Commands.tsx
diff --git a/apps/studio/components/to-be-cleaned/Storage/Storage.constants.ts b/apps/studio/components/interfaces/Storage/Storage.constants.ts
similarity index 100%
rename from apps/studio/components/to-be-cleaned/Storage/Storage.constants.ts
rename to apps/studio/components/interfaces/Storage/Storage.constants.ts
diff --git a/apps/studio/components/interfaces/Storage/Storage.types.ts b/apps/studio/components/interfaces/Storage/Storage.types.ts
index 8de8a32625ee4..c9943befe0157 100644
--- a/apps/studio/components/interfaces/Storage/Storage.types.ts
+++ b/apps/studio/components/interfaces/Storage/Storage.types.ts
@@ -1,4 +1,5 @@
import type { PolicyFormField } from 'components/interfaces/Auth/Policies/Policies.types'
+import { STORAGE_ROW_STATUS, STORAGE_ROW_TYPES } from './Storage.constants'
export interface StoragePolicyFormField extends PolicyFormField {
allowedOperations: string[]
@@ -14,3 +15,38 @@ export interface BucketCreatePayload extends BucketUpdatePayload {
id: string
name?: string
}
+
+export interface StorageColumn {
+ id: string | null
+ name: string
+ status: string
+ items: StorageItem[]
+ hasMoreItems?: boolean
+ isLoadingMoreItems?: boolean
+}
+
+export interface StorageItem {
+ id: string | null
+ name: string
+ type: STORAGE_ROW_TYPES
+ status: STORAGE_ROW_STATUS
+ metadata: StorageItemMetadata | null
+ created_at: string | null
+ updated_at: string | null
+ last_accessed_at: string | null
+ // UI specific properties, not from API
+ isCorrupted: boolean
+ path?: string
+}
+
+export type StorageItemWithColumn = StorageItem & { columnIndex: number }
+
+export interface StorageItemMetadata {
+ cacheControl: string
+ contentLength: number
+ size: number
+ httpStatusCode: number
+ eTag: string
+ lastModified: string
+ mimetype: string
+}
diff --git a/apps/studio/components/to-be-cleaned/Storage/Storage.utils.ts b/apps/studio/components/interfaces/Storage/Storage.utils.ts
similarity index 98%
rename from apps/studio/components/to-be-cleaned/Storage/Storage.utils.ts
rename to apps/studio/components/interfaces/Storage/Storage.utils.ts
index 5a50eab4ec476..36f64ef075fd3 100644
--- a/apps/studio/components/to-be-cleaned/Storage/Storage.utils.ts
+++ b/apps/studio/components/interfaces/Storage/Storage.utils.ts
@@ -1,6 +1,6 @@
import { groupBy, difference } from 'lodash'
import { STORAGE_CLIENT_LIBRARY_MAPPINGS } from './Storage.constants'
-import type { StoragePolicyFormField } from 'components/interfaces/Storage/Storage.types'
+import type { StoragePolicyFormField } from './Storage.types'
const shortHash = (str: string) => {
let hash = 0
diff --git a/apps/studio/components/layouts/StorageLayout/StorageBucketsError.tsx b/apps/studio/components/interfaces/Storage/StorageBucketsError.tsx
similarity index 100%
rename from apps/studio/components/layouts/StorageLayout/StorageBucketsError.tsx
rename to apps/studio/components/interfaces/Storage/StorageBucketsError.tsx
diff --git a/apps/studio/components/to-be-cleaned/Storage/StorageExplorer/ColumnContextMenu.tsx b/apps/studio/components/interfaces/Storage/StorageExplorer/ColumnContextMenu.tsx
similarity index 100%
rename from apps/studio/components/to-be-cleaned/Storage/StorageExplorer/ColumnContextMenu.tsx
rename to apps/studio/components/interfaces/Storage/StorageExplorer/ColumnContextMenu.tsx
diff --git a/apps/studio/components/to-be-cleaned/Storage/StorageExplorer/ConfirmDeleteModal.tsx b/apps/studio/components/interfaces/Storage/StorageExplorer/ConfirmDeleteModal.tsx
similarity index 100%
rename from apps/studio/components/to-be-cleaned/Storage/StorageExplorer/ConfirmDeleteModal.tsx
rename to apps/studio/components/interfaces/Storage/StorageExplorer/ConfirmDeleteModal.tsx
diff --git a/apps/studio/components/to-be-cleaned/Storage/StorageExplorer/CustomExpiryModal.tsx b/apps/studio/components/interfaces/Storage/StorageExplorer/CustomExpiryModal.tsx
similarity index 100%
rename from apps/studio/components/to-be-cleaned/Storage/StorageExplorer/CustomExpiryModal.tsx
rename to apps/studio/components/interfaces/Storage/StorageExplorer/CustomExpiryModal.tsx
diff --git a/apps/studio/components/to-be-cleaned/Storage/StorageExplorer/FileExplorer.tsx b/apps/studio/components/interfaces/Storage/StorageExplorer/FileExplorer.tsx
similarity index 100%
rename from apps/studio/components/to-be-cleaned/Storage/StorageExplorer/FileExplorer.tsx
rename to apps/studio/components/interfaces/Storage/StorageExplorer/FileExplorer.tsx
diff --git a/apps/studio/components/to-be-cleaned/Storage/StorageExplorer/FileExplorerColumn.tsx b/apps/studio/components/interfaces/Storage/StorageExplorer/FileExplorerColumn.tsx
similarity index 95%
rename from apps/studio/components/to-be-cleaned/Storage/StorageExplorer/FileExplorerColumn.tsx
rename to apps/studio/components/interfaces/Storage/StorageExplorer/FileExplorerColumn.tsx
index 7d76865392393..84167c8d27107 100644
--- a/apps/studio/components/to-be-cleaned/Storage/StorageExplorer/FileExplorerColumn.tsx
+++ b/apps/studio/components/interfaces/Storage/StorageExplorer/FileExplorerColumn.tsx
@@ -1,11 +1,14 @@
import { Transition } from '@headlessui/react'
+import { PermissionAction } from '@supabase/shared-types/out/constants'
import { get, noop, sum } from 'lodash'
import { Upload } from 'lucide-react'
import { useEffect, useRef, useState } from 'react'
import { useContextMenu } from 'react-contexify'
+import { toast } from 'sonner'
import InfiniteList from 'components/ui/InfiniteList'
import ShimmeringLoader from 'components/ui/ShimmeringLoader'
+import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
import { BASE_PATH } from 'lib/constants'
import { formatBytes } from 'lib/helpers'
import { useStorageExplorerStateSnapshot } from 'state/storage-explorer'
@@ -82,6 +85,7 @@ const FileExplorerColumn = ({
const fileExplorerColumnRef = useRef(null)
const snap = useStorageExplorerStateSnapshot()
+ const canUpdateStorage = useCheckPermissions(PermissionAction.STORAGE_WRITE, '*')
useEffect(() => {
if (fileExplorerColumnRef) {
@@ -126,7 +130,12 @@ const FileExplorerColumn = ({
const onDrop = (event: any) => {
onDragOver(event)
- onFilesUpload(event, index)
+
+ if (!canUpdateStorage) {
+ toast('You need additional permissions to upload files to this project')
+ } else {
+ onFilesUpload(event, index)
+ }
}
const SelectAllCheckbox = () => (
diff --git a/apps/studio/components/to-be-cleaned/Storage/StorageExplorer/FileExplorerHeader.tsx b/apps/studio/components/interfaces/Storage/StorageExplorer/FileExplorerHeader.tsx
similarity index 100%
rename from apps/studio/components/to-be-cleaned/Storage/StorageExplorer/FileExplorerHeader.tsx
rename to apps/studio/components/interfaces/Storage/StorageExplorer/FileExplorerHeader.tsx
diff --git a/apps/studio/components/to-be-cleaned/Storage/StorageExplorer/FileExplorerHeaderSelection.tsx b/apps/studio/components/interfaces/Storage/StorageExplorer/FileExplorerHeaderSelection.tsx
similarity index 100%
rename from apps/studio/components/to-be-cleaned/Storage/StorageExplorer/FileExplorerHeaderSelection.tsx
rename to apps/studio/components/interfaces/Storage/StorageExplorer/FileExplorerHeaderSelection.tsx
diff --git a/apps/studio/components/to-be-cleaned/Storage/StorageExplorer/FileExplorerRow.tsx b/apps/studio/components/interfaces/Storage/StorageExplorer/FileExplorerRow.tsx
similarity index 100%
rename from apps/studio/components/to-be-cleaned/Storage/StorageExplorer/FileExplorerRow.tsx
rename to apps/studio/components/interfaces/Storage/StorageExplorer/FileExplorerRow.tsx
diff --git a/apps/studio/components/to-be-cleaned/Storage/StorageExplorer/FileExplorerRowEditing.tsx b/apps/studio/components/interfaces/Storage/StorageExplorer/FileExplorerRowEditing.tsx
similarity index 100%
rename from apps/studio/components/to-be-cleaned/Storage/StorageExplorer/FileExplorerRowEditing.tsx
rename to apps/studio/components/interfaces/Storage/StorageExplorer/FileExplorerRowEditing.tsx
diff --git a/apps/studio/components/to-be-cleaned/Storage/StorageExplorer/FolderContextMenu.tsx b/apps/studio/components/interfaces/Storage/StorageExplorer/FolderContextMenu.tsx
similarity index 100%
rename from apps/studio/components/to-be-cleaned/Storage/StorageExplorer/FolderContextMenu.tsx
rename to apps/studio/components/interfaces/Storage/StorageExplorer/FolderContextMenu.tsx
diff --git a/apps/studio/components/to-be-cleaned/Storage/StorageExplorer/ItemContextMenu.tsx b/apps/studio/components/interfaces/Storage/StorageExplorer/ItemContextMenu.tsx
similarity index 100%
rename from apps/studio/components/to-be-cleaned/Storage/StorageExplorer/ItemContextMenu.tsx
rename to apps/studio/components/interfaces/Storage/StorageExplorer/ItemContextMenu.tsx
diff --git a/apps/studio/components/to-be-cleaned/Storage/StorageExplorer/MoveItemsModal.tsx b/apps/studio/components/interfaces/Storage/StorageExplorer/MoveItemsModal.tsx
similarity index 100%
rename from apps/studio/components/to-be-cleaned/Storage/StorageExplorer/MoveItemsModal.tsx
rename to apps/studio/components/interfaces/Storage/StorageExplorer/MoveItemsModal.tsx
diff --git a/apps/studio/components/to-be-cleaned/Storage/StorageExplorer/PreviewPane.tsx b/apps/studio/components/interfaces/Storage/StorageExplorer/PreviewPane.tsx
similarity index 100%
rename from apps/studio/components/to-be-cleaned/Storage/StorageExplorer/PreviewPane.tsx
rename to apps/studio/components/interfaces/Storage/StorageExplorer/PreviewPane.tsx
diff --git a/apps/studio/components/to-be-cleaned/Storage/StorageExplorer/StorageExplorer.tsx b/apps/studio/components/interfaces/Storage/StorageExplorer/StorageExplorer.tsx
similarity index 100%
rename from apps/studio/components/to-be-cleaned/Storage/StorageExplorer/StorageExplorer.tsx
rename to apps/studio/components/interfaces/Storage/StorageExplorer/StorageExplorer.tsx
diff --git a/apps/studio/components/to-be-cleaned/Storage/StorageExplorer/StorageExplorer.utils.tsx b/apps/studio/components/interfaces/Storage/StorageExplorer/StorageExplorer.utils.tsx
similarity index 100%
rename from apps/studio/components/to-be-cleaned/Storage/StorageExplorer/StorageExplorer.utils.tsx
rename to apps/studio/components/interfaces/Storage/StorageExplorer/StorageExplorer.utils.tsx
diff --git a/apps/studio/components/to-be-cleaned/Storage/StorageExplorer/useCopyUrl.tsx b/apps/studio/components/interfaces/Storage/StorageExplorer/useCopyUrl.tsx
similarity index 100%
rename from apps/studio/components/to-be-cleaned/Storage/StorageExplorer/useCopyUrl.tsx
rename to apps/studio/components/interfaces/Storage/StorageExplorer/useCopyUrl.tsx
diff --git a/apps/studio/components/to-be-cleaned/Storage/StorageExplorer/useFetchFileUrlQuery.tsx b/apps/studio/components/interfaces/Storage/StorageExplorer/useFetchFileUrlQuery.tsx
similarity index 100%
rename from apps/studio/components/to-be-cleaned/Storage/StorageExplorer/useFetchFileUrlQuery.tsx
rename to apps/studio/components/interfaces/Storage/StorageExplorer/useFetchFileUrlQuery.tsx
diff --git a/apps/studio/components/to-be-cleaned/Storage/StorageExplorer/useSelectedBucket.ts b/apps/studio/components/interfaces/Storage/StorageExplorer/useSelectedBucket.ts
similarity index 100%
rename from apps/studio/components/to-be-cleaned/Storage/StorageExplorer/useSelectedBucket.ts
rename to apps/studio/components/interfaces/Storage/StorageExplorer/useSelectedBucket.ts
diff --git a/apps/studio/components/layouts/StorageLayout/StorageMenu.tsx b/apps/studio/components/interfaces/Storage/StorageMenu.tsx
similarity index 98%
rename from apps/studio/components/layouts/StorageLayout/StorageMenu.tsx
rename to apps/studio/components/interfaces/Storage/StorageMenu.tsx
index 094cbc5013dd5..152fa8ed73545 100644
--- a/apps/studio/components/layouts/StorageLayout/StorageMenu.tsx
+++ b/apps/studio/components/interfaces/Storage/StorageMenu.tsx
@@ -8,8 +8,8 @@ import { useState } from 'react'
import { useParams } from 'common'
import CreateBucketModal from 'components/interfaces/Storage/CreateBucketModal'
import EditBucketModal from 'components/interfaces/Storage/EditBucketModal'
-import { DeleteBucketModal } from 'components/to-be-cleaned/Storage'
-import { EmptyBucketModal } from 'components/to-be-cleaned/Storage/EmptyBucketModal'
+import { DeleteBucketModal } from 'components/interfaces/Storage'
+import { EmptyBucketModal } from 'components/interfaces/Storage/EmptyBucketModal'
import { ButtonTooltip } from 'components/ui/ButtonTooltip'
import ShimmeringLoader from 'components/ui/ShimmeringLoader'
import { Bucket, useBucketsQuery } from 'data/storage/buckets-query'
diff --git a/apps/studio/components/to-be-cleaned/Storage/StoragePolicies/StoragePolicies.constants.ts b/apps/studio/components/interfaces/Storage/StoragePolicies/StoragePolicies.constants.ts
similarity index 100%
rename from apps/studio/components/to-be-cleaned/Storage/StoragePolicies/StoragePolicies.constants.ts
rename to apps/studio/components/interfaces/Storage/StoragePolicies/StoragePolicies.constants.ts
diff --git a/apps/studio/components/to-be-cleaned/Storage/StoragePolicies/StoragePolicies.tsx b/apps/studio/components/interfaces/Storage/StoragePolicies/StoragePolicies.tsx
similarity index 100%
rename from apps/studio/components/to-be-cleaned/Storage/StoragePolicies/StoragePolicies.tsx
rename to apps/studio/components/interfaces/Storage/StoragePolicies/StoragePolicies.tsx
diff --git a/apps/studio/components/to-be-cleaned/Storage/StoragePolicies/StoragePoliciesBucketRow.tsx b/apps/studio/components/interfaces/Storage/StoragePolicies/StoragePoliciesBucketRow.tsx
similarity index 100%
rename from apps/studio/components/to-be-cleaned/Storage/StoragePolicies/StoragePoliciesBucketRow.tsx
rename to apps/studio/components/interfaces/Storage/StoragePolicies/StoragePoliciesBucketRow.tsx
diff --git a/apps/studio/components/to-be-cleaned/Storage/StoragePolicies/StoragePoliciesEditPolicyModal.tsx b/apps/studio/components/interfaces/Storage/StoragePolicies/StoragePoliciesEditPolicyModal.tsx
similarity index 100%
rename from apps/studio/components/to-be-cleaned/Storage/StoragePolicies/StoragePoliciesEditPolicyModal.tsx
rename to apps/studio/components/interfaces/Storage/StoragePolicies/StoragePoliciesEditPolicyModal.tsx
diff --git a/apps/studio/components/to-be-cleaned/Storage/StoragePolicies/StoragePoliciesEditor.tsx b/apps/studio/components/interfaces/Storage/StoragePolicies/StoragePoliciesEditor.tsx
similarity index 100%
rename from apps/studio/components/to-be-cleaned/Storage/StoragePolicies/StoragePoliciesEditor.tsx
rename to apps/studio/components/interfaces/Storage/StoragePolicies/StoragePoliciesEditor.tsx
diff --git a/apps/studio/components/to-be-cleaned/Storage/StoragePolicies/StoragePoliciesPlaceholder.tsx b/apps/studio/components/interfaces/Storage/StoragePolicies/StoragePoliciesPlaceholder.tsx
similarity index 100%
rename from apps/studio/components/to-be-cleaned/Storage/StoragePolicies/StoragePoliciesPlaceholder.tsx
rename to apps/studio/components/interfaces/Storage/StoragePolicies/StoragePoliciesPlaceholder.tsx
diff --git a/apps/studio/components/to-be-cleaned/Storage/StoragePolicies/StoragePoliciesReview.tsx b/apps/studio/components/interfaces/Storage/StoragePolicies/StoragePoliciesReview.tsx
similarity index 100%
rename from apps/studio/components/to-be-cleaned/Storage/StoragePolicies/StoragePoliciesReview.tsx
rename to apps/studio/components/interfaces/Storage/StoragePolicies/StoragePoliciesReview.tsx
diff --git a/apps/studio/components/to-be-cleaned/Storage/StorageSettings/CreateCredentialModal.tsx b/apps/studio/components/interfaces/Storage/StorageSettings/CreateCredentialModal.tsx
similarity index 100%
rename from apps/studio/components/to-be-cleaned/Storage/StorageSettings/CreateCredentialModal.tsx
rename to apps/studio/components/interfaces/Storage/StorageSettings/CreateCredentialModal.tsx
diff --git a/apps/studio/components/to-be-cleaned/Storage/StorageSettings/RevokeCredentialModal.tsx b/apps/studio/components/interfaces/Storage/StorageSettings/RevokeCredentialModal.tsx
similarity index 100%
rename from apps/studio/components/to-be-cleaned/Storage/StorageSettings/RevokeCredentialModal.tsx
rename to apps/studio/components/interfaces/Storage/StorageSettings/RevokeCredentialModal.tsx
diff --git a/apps/studio/components/to-be-cleaned/Storage/StorageSettings/S3Connection.tsx b/apps/studio/components/interfaces/Storage/StorageSettings/S3Connection.tsx
similarity index 100%
rename from apps/studio/components/to-be-cleaned/Storage/StorageSettings/S3Connection.tsx
rename to apps/studio/components/interfaces/Storage/StorageSettings/S3Connection.tsx
diff --git a/apps/studio/components/to-be-cleaned/Storage/StorageSettings/StorageCredItem.tsx b/apps/studio/components/interfaces/Storage/StorageSettings/StorageCredItem.tsx
similarity index 100%
rename from apps/studio/components/to-be-cleaned/Storage/StorageSettings/StorageCredItem.tsx
rename to apps/studio/components/interfaces/Storage/StorageSettings/StorageCredItem.tsx
diff --git a/apps/studio/components/to-be-cleaned/Storage/StorageSettings/StorageSettings.constants.ts b/apps/studio/components/interfaces/Storage/StorageSettings/StorageSettings.constants.ts
similarity index 100%
rename from apps/studio/components/to-be-cleaned/Storage/StorageSettings/StorageSettings.constants.ts
rename to apps/studio/components/interfaces/Storage/StorageSettings/StorageSettings.constants.ts
diff --git a/apps/studio/components/to-be-cleaned/Storage/StorageSettings/StorageSettings.tsx b/apps/studio/components/interfaces/Storage/StorageSettings/StorageSettings.tsx
similarity index 100%
rename from apps/studio/components/to-be-cleaned/Storage/StorageSettings/StorageSettings.tsx
rename to apps/studio/components/interfaces/Storage/StorageSettings/StorageSettings.tsx
diff --git a/apps/studio/components/to-be-cleaned/Storage/StorageSettings/StorageSettings.utils.ts b/apps/studio/components/interfaces/Storage/StorageSettings/StorageSettings.utils.ts
similarity index 100%
rename from apps/studio/components/to-be-cleaned/Storage/StorageSettings/StorageSettings.utils.ts
rename to apps/studio/components/interfaces/Storage/StorageSettings/StorageSettings.utils.ts
diff --git a/apps/studio/components/to-be-cleaned/Storage/index.ts b/apps/studio/components/interfaces/Storage/index.ts
similarity index 100%
rename from apps/studio/components/to-be-cleaned/Storage/index.ts
rename to apps/studio/components/interfaces/Storage/index.ts
diff --git a/apps/studio/components/layouts/ProjectLayout/LayoutHeader/MergeRequestButton.tsx b/apps/studio/components/layouts/ProjectLayout/LayoutHeader/MergeRequestButton.tsx
index ec88c81611570..977dd1bf517c1 100644
--- a/apps/studio/components/layouts/ProjectLayout/LayoutHeader/MergeRequestButton.tsx
+++ b/apps/studio/components/layouts/ProjectLayout/LayoutHeader/MergeRequestButton.tsx
@@ -3,19 +3,24 @@ import { GitMerge } from 'lucide-react'
import { useBranchesQuery } from 'data/branches/branches-query'
import { useParams } from 'common'
import { useSelectedProject } from 'hooks/misc/useSelectedProject'
+import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization'
import { useRouter } from 'next/router'
import { useBranchUpdateMutation } from 'data/branches/branch-update-mutation'
+import { useSendEventMutation } from 'data/telemetry/send-event-mutation'
import { toast } from 'sonner'
export const MergeRequestButton = () => {
const { ref } = useParams()
const router = useRouter()
const projectDetails = useSelectedProject()
+ const selectedOrg = useSelectedOrganization()
const projectRef = projectDetails?.parent_project_ref || ref
const { data: branches } = useBranchesQuery({ projectRef }, { enabled: Boolean(projectDetails) })
+ const { mutate: sendEvent } = useSendEventMutation()
+
const { mutate: updateBranch, isLoading: isUpdating } = useBranchUpdateMutation({
onError: () => {
toast.error(`Failed to open merge request`)
@@ -44,6 +49,17 @@ export const MergeRequestButton = () => {
onSuccess: () => {
toast.success('Merge request created')
router.push(`/project/${selectedBranch.project_ref}/merge`)
+ sendEvent({
+ action: 'branch_create_merge_request_button_clicked',
+ properties: {
+ branchType: selectedBranch.persistent ? 'persistent' : 'preview',
+ origin: 'header',
+ },
+ groups: {
+ project: projectRef ?? 'Unknown',
+ organization: selectedOrg?.slug ?? 'Unknown',
+ },
+ })
},
}
)
diff --git a/apps/studio/components/layouts/StorageLayout/StorageLayout.tsx b/apps/studio/components/layouts/StorageLayout/StorageLayout.tsx
index 4b0eb1d68bdcb..5e32ed53df1e8 100644
--- a/apps/studio/components/layouts/StorageLayout/StorageLayout.tsx
+++ b/apps/studio/components/layouts/StorageLayout/StorageLayout.tsx
@@ -2,7 +2,7 @@ import { ReactNode } from 'react'
import { withAuth } from 'hooks/misc/withAuth'
import ProjectLayout from '../ProjectLayout/ProjectLayout'
-import StorageMenu from './StorageMenu'
+import StorageMenu from '../../interfaces/Storage/StorageMenu'
export interface StorageLayoutProps {
title: string
diff --git a/apps/studio/components/layouts/useLayoutNavCommands.ts b/apps/studio/components/layouts/useLayoutNavCommands.ts
index 1957bcc72781b..0ce1cf08c0be2 100644
--- a/apps/studio/components/layouts/useLayoutNavCommands.ts
+++ b/apps/studio/components/layouts/useLayoutNavCommands.ts
@@ -9,7 +9,7 @@ import { useLogsGotoCommands } from './LogsLayout/Logs.Commands'
import { useProjectSettingsGotoCommands } from './ProjectSettingsLayout/ProjectSettings.Commands'
import { useReportsGotoCommands } from './ReportsLayout/Reports.Commands'
import { useSqlEditorGotoCommands } from './SQLEditorLayout/SqlEditor.Commands'
-import { useStorageGotoCommands } from './StorageLayout/Storage.Commands'
+import { useStorageGotoCommands } from '../interfaces/Storage/Storage.Commands'
import { useTableEditorGotoCommands } from './TableEditorLayout/TableEditor.Commands'
export function useLayoutNavCommands() {
diff --git a/apps/studio/components/to-be-cleaned/Storage/Storage.types.ts b/apps/studio/components/to-be-cleaned/Storage/Storage.types.ts
deleted file mode 100644
index b29ce67266960..0000000000000
--- a/apps/studio/components/to-be-cleaned/Storage/Storage.types.ts
+++ /dev/null
@@ -1,36 +0,0 @@
-import { STORAGE_ROW_STATUS, STORAGE_ROW_TYPES } from './Storage.constants'
-
-export interface StorageColumn {
- id: string | null
- name: string
- status: string
- items: StorageItem[]
- hasMoreItems?: boolean
- isLoadingMoreItems?: boolean
-}
-
-export interface StorageItem {
- id: string | null
- name: string
- type: STORAGE_ROW_TYPES
- status: STORAGE_ROW_STATUS
- metadata: StorageItemMetadata | null
- created_at: string | null
- updated_at: string | null
- last_accessed_at: string | null
- // UI specific properties, not from API
- isCorrupted: boolean
- path?: string
-}
-
-export type StorageItemWithColumn = StorageItem & { columnIndex: number }
-
-export interface StorageItemMetadata {
- cacheControl: string
- contentLength: number
- size: number
- httpStatusCode: number
- eTag: string
- lastModified: string
- mimetype: string
-}
diff --git a/apps/studio/components/ui/Charts/ComposedChart.tsx b/apps/studio/components/ui/Charts/ComposedChart.tsx
index 3dd9ae6aa3b03..78e1e45a09c7b 100644
--- a/apps/studio/components/ui/Charts/ComposedChart.tsx
+++ b/apps/studio/components/ui/Charts/ComposedChart.tsx
@@ -1,12 +1,14 @@
'use client'
import dayjs from 'dayjs'
+import { formatBytes } from 'lib/helpers'
import { useTheme } from 'next-themes'
import { ComponentProps, useEffect, useState } from 'react'
import {
Area,
Bar,
CartesianGrid,
+ Label,
Line,
ComposedChart as RechartComposedChart,
ReferenceArea,
@@ -14,7 +16,6 @@ import {
Tooltip,
XAxis,
YAxis,
- Label,
} from 'recharts'
import { CategoricalChartState } from 'recharts/types/chart/types'
import { cn } from 'ui'
@@ -28,11 +29,14 @@ import {
} from './Charts.constants'
import { CommonChartProps, Datum } from './Charts.types'
import { numberFormatter, useChartSize } from './Charts.utils'
-import { calculateTotalChartAggregate, CustomLabel, CustomTooltip } from './ComposedChart.utils'
+import {
+ calculateTotalChartAggregate,
+ CustomLabel,
+ CustomTooltip,
+ MultiAttribute,
+} from './ComposedChart.utils'
import NoDataPlaceholder from './NoDataPlaceholder'
-import { MultiAttribute } from './ComposedChart.utils'
import { ChartHighlight } from './useChartHighlight'
-import { formatBytes } from 'lib/helpers'
export interface ComposedChartProps extends CommonChartProps {
attributes: MultiAttribute[]
diff --git a/apps/studio/components/ui/Charts/ComposedChartHandler.tsx b/apps/studio/components/ui/Charts/ComposedChartHandler.tsx
index 25ab41228ebb3..400a7a40afc70 100644
--- a/apps/studio/components/ui/Charts/ComposedChartHandler.tsx
+++ b/apps/studio/components/ui/Charts/ComposedChartHandler.tsx
@@ -1,21 +1,22 @@
-import React, { PropsWithChildren, useState, useMemo, useEffect, useRef } from 'react'
-import { useRouter } from 'next/router'
import { Loader2 } from 'lucide-react'
+import { useRouter } from 'next/router'
+import React, { PropsWithChildren, useEffect, useMemo, useRef, useState } from 'react'
import { cn, WarningIcon } from 'ui'
import Panel from 'components/ui/Panel'
import ComposedChart from './ComposedChart'
import { AnalyticsInterval, DataPoint } from 'data/analytics/constants'
-import { InfraMonitoringAttribute } from 'data/analytics/infra-monitoring-query'
import { useInfraMonitoringQueries } from 'data/analytics/infra-monitoring-queries'
-import { ProjectDailyStatsAttribute } from 'data/analytics/project-daily-stats-query'
+import { InfraMonitoringAttribute } from 'data/analytics/infra-monitoring-query'
import { useProjectDailyStatsQueries } from 'data/analytics/project-daily-stats-queries'
+import { ProjectDailyStatsAttribute } from 'data/analytics/project-daily-stats-query'
import { useDatabaseSelectorStateSnapshot } from 'state/database-selector'
import { useChartHighlight } from './useChartHighlight'
-import type { ChartData } from './Charts.types'
+import dayjs from 'dayjs'
import type { UpdateDateRange } from 'pages/project/[ref]/reports/database'
+import type { ChartData } from './Charts.types'
import { MultiAttribute } from './ComposedChart.utils'
export interface ComposedChartHandlerProps {
@@ -199,7 +200,12 @@ const ComposedChartHandler = ({
point[attr] = value
})
- return point as DataPoint
+ const formattedDataPoint: DataPoint =
+ !('period_start' in point) && 'timestamp' in point
+ ? { ...point, period_start: dayjs.utc(point.timestamp).unix() * 1000 }
+ : point
+
+ return formattedDataPoint
})
return combined as DataPoint[]
@@ -275,6 +281,7 @@ const ComposedChartHandler = ({
attributes={attributes}
data={combinedData as DataPoint[]}
format={format}
+ // [Joshen] This is where it's messing up
xAxisKey="period_start"
yAxisKey={attributes[0].attribute}
highlightedValue={_highlightedValue}
diff --git a/apps/studio/data/storage/iceberg-wrapper-create-mutation.ts b/apps/studio/data/storage/iceberg-wrapper-create-mutation.ts
index 95cac1e501b16..50e1c1713a898 100644
--- a/apps/studio/data/storage/iceberg-wrapper-create-mutation.ts
+++ b/apps/studio/data/storage/iceberg-wrapper-create-mutation.ts
@@ -1,14 +1,14 @@
import { PermissionAction } from '@supabase/shared-types/out/constants'
import { WRAPPERS } from 'components/interfaces/Integrations/Wrappers/Wrappers.constants'
+import {
+ getCatalogURI,
+ getConnectionURL,
+} from 'components/interfaces/Storage/StorageSettings/StorageSettings.utils'
import {
useIsProjectActive,
useProjectContext,
} from 'components/layouts/ProjectLayout/ProjectContext'
-import {
- getCatalogURI,
- getConnectionURL,
-} from 'components/to-be-cleaned/Storage/StorageSettings/StorageSettings.utils'
import { getKeys, useAPIKeysQuery } from 'data/api-keys/api-keys-query'
import { useProjectSettingsV2Query } from 'data/config/project-settings-v2-query'
import { useProjectStorageConfigQuery } from 'data/config/project-storage-config-query'
diff --git a/apps/studio/package.json b/apps/studio/package.json
index c485159f62263..3085a3d6de7f8 100644
--- a/apps/studio/package.json
+++ b/apps/studio/package.json
@@ -189,6 +189,7 @@
"eslint-plugin-barrel-files": "^2.0.7",
"graphql-ws": "5.14.1",
"import-in-the-middle": "^1.13.1",
+ "jsdom-testing-mocks": "^1.13.1",
"msw": "^2.3.0",
"next-router-mock": "^0.9.13",
"postcss": "^8.5.3",
diff --git a/apps/studio/pages/project/[ref]/branches/index.tsx b/apps/studio/pages/project/[ref]/branches/index.tsx
index 975d6b9e3a12f..3902c17937eb1 100644
--- a/apps/studio/pages/project/[ref]/branches/index.tsx
+++ b/apps/studio/pages/project/[ref]/branches/index.tsx
@@ -18,6 +18,7 @@ import NoPermission from 'components/ui/NoPermission'
import { useBranchDeleteMutation } from 'data/branches/branch-delete-mutation'
import { Branch, useBranchesQuery } from 'data/branches/branches-query'
import { useGitHubConnectionsQuery } from 'data/integrations/github-connections-query'
+import { useSendEventMutation } from 'data/telemetry/send-event-mutation'
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization'
import { useSelectedProject } from 'hooks/misc/useSelectedProject'
@@ -35,6 +36,8 @@ const BranchesPage: NextPageWithLayout = () => {
const [selectedBranchToDelete, setSelectedBranchToDelete] = useState()
+ const { mutate: sendEvent } = useSendEventMutation()
+
const isBranch = project?.parent_project_ref !== undefined
const projectRef =
project !== undefined ? (isBranch ? project.parent_project_ref : ref) : undefined
@@ -95,6 +98,18 @@ const BranchesPage: NextPageWithLayout = () => {
if (selectedBranchToDelete.project_ref === ref) {
router.push(`/project/${selectedBranchToDelete.parent_project_ref}/branches`)
}
+ // Track delete button click
+ sendEvent({
+ action: 'branch_delete_button_clicked',
+ properties: {
+ branchType: selectedBranchToDelete.persistent ? 'persistent' : 'preview',
+ origin: 'branches_page',
+ },
+ groups: {
+ project: projectRef ?? 'Unknown',
+ organization: selectedOrg?.slug ?? 'Unknown',
+ },
+ })
},
}
)
diff --git a/apps/studio/pages/project/[ref]/branches/merge-requests.tsx b/apps/studio/pages/project/[ref]/branches/merge-requests.tsx
index 1ee8c0bc86c52..056a3e1c0b3fd 100644
--- a/apps/studio/pages/project/[ref]/branches/merge-requests.tsx
+++ b/apps/studio/pages/project/[ref]/branches/merge-requests.tsx
@@ -23,6 +23,7 @@ import NoPermission from 'components/ui/NoPermission'
import { useBranchUpdateMutation } from 'data/branches/branch-update-mutation'
import { Branch, useBranchesQuery } from 'data/branches/branches-query'
import { useGitHubConnectionsQuery } from 'data/integrations/github-connections-query'
+import { useSendEventMutation } from 'data/telemetry/send-event-mutation'
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization'
import { useSelectedProject } from 'hooks/misc/useSelectedProject'
@@ -83,6 +84,8 @@ const MergeRequestsPage: NextPageWithLayout = () => {
const isError = isErrorConnections || isErrorBranches
+ const { mutate: sendEvent } = useSendEventMutation()
+
const { mutate: updateBranch, isLoading: isUpdating } = useBranchUpdateMutation({
onError: () => {
toast.error(`Failed to update the branch`)
@@ -100,6 +103,20 @@ const MergeRequestsPage: NextPageWithLayout = () => {
{
onSuccess: () => {
toast.success('Merge request created')
+
+ // Track merge request creation
+ sendEvent({
+ action: 'branch_create_merge_request_button_clicked',
+ properties: {
+ branchType: branch.persistent ? 'persistent' : 'preview',
+ origin: 'merge_page',
+ },
+ groups: {
+ project: projectRef ?? 'Unknown',
+ organization: selectedOrg?.slug ?? 'Unknown',
+ },
+ })
+
router.push(`/project/${branch.project_ref}/merge`)
},
}
@@ -118,6 +135,15 @@ const MergeRequestsPage: NextPageWithLayout = () => {
{
onSuccess: () => {
toast.success('Merge request closed')
+
+ // Track merge request closed
+ sendEvent({
+ action: 'branch_close_merge_request_button_clicked',
+ groups: {
+ project: projectRef ?? 'Unknown',
+ organization: selectedOrg?.slug ?? 'Unknown',
+ },
+ })
},
}
)
@@ -282,6 +308,7 @@ const MergeRequestsPageWrapper = ({ children }: PropsWithChildren<{}>) => {
const router = useRouter()
const { ref } = useParams()
const project = useSelectedProject()
+ const selectedOrg = useSelectedOrganization()
const gitlessBranching = useIsBranching2Enabled()
const isBranch = project?.parent_project_ref !== undefined
@@ -291,6 +318,8 @@ const MergeRequestsPageWrapper = ({ children }: PropsWithChildren<{}>) => {
const { data: branches } = useBranchesQuery({ projectRef })
const previewBranches = (branches || []).filter((b) => !b.is_default)
+ const { mutate: sendEvent } = useSendEventMutation()
+
const { mutate: updateBranch, isLoading: isUpdating } = useBranchUpdateMutation({
onError: () => {
toast.error(`Failed to update the branch`)
@@ -308,6 +337,20 @@ const MergeRequestsPageWrapper = ({ children }: PropsWithChildren<{}>) => {
{
onSuccess: () => {
toast.success('Merge request created')
+
+ // Track merge request creation
+ sendEvent({
+ action: 'branch_create_merge_request_button_clicked',
+ properties: {
+ branchType: branch.persistent ? 'persistent' : 'preview',
+ origin: 'branch_selector',
+ },
+ groups: {
+ project: projectRef ?? 'Unknown',
+ organization: selectedOrg?.slug ?? 'Unknown',
+ },
+ })
+
router.push(`/project/${branch.project_ref}/merge`)
},
}
diff --git a/apps/studio/pages/project/[ref]/merge.tsx b/apps/studio/pages/project/[ref]/merge.tsx
index de80a26e713c4..0d175d2bfa3fd 100644
--- a/apps/studio/pages/project/[ref]/merge.tsx
+++ b/apps/studio/pages/project/[ref]/merge.tsx
@@ -22,9 +22,11 @@ import { useBranchMergeMutation } from 'data/branches/branch-merge-mutation'
import { useBranchPushMutation } from 'data/branches/branch-push-mutation'
import { useBranchUpdateMutation } from 'data/branches/branch-update-mutation'
import { useBranchesQuery } from 'data/branches/branches-query'
+import { useSendEventMutation } from 'data/telemetry/send-event-mutation'
import { useBranchMergeDiff } from 'hooks/branches/useBranchMergeDiff'
import { useWorkflowManagement } from 'hooks/branches/useWorkflowManagement'
import { useProjectByRef, useSelectedProject } from 'hooks/misc/useSelectedProject'
+import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization'
import type { NextPageWithLayout } from 'types'
import {
Badge,
@@ -43,6 +45,7 @@ const MergePage: NextPageWithLayout = () => {
const router = useRouter()
const { ref } = useParams()
const project = useSelectedProject()
+ const selectedOrg = useSelectedOrganization()
const [isSubmitting, setIsSubmitting] = useState(false)
const [workflowFinalStatus, setWorkflowFinalStatus] = useState(null)
@@ -192,18 +195,44 @@ const MergePage: NextPageWithLayout = () => {
if (data?.workflow_run_id) {
addWorkflowRun(data.workflow_run_id)
}
+
+ // Track branch update
+ sendEvent({
+ action: 'branch_updated',
+ properties: {
+ source: 'merge_page',
+ },
+ groups: {
+ project: parentProjectRef ?? 'Unknown',
+ organization: selectedOrg?.slug ?? 'Unknown',
+ },
+ })
},
onError: (error) => {
toast.error(`Failed to update branch: ${error.message}`)
},
})
+ const { mutate: sendEvent } = useSendEventMutation()
+
const { mutate: mergeBranch, isLoading: isMerging } = useBranchMergeMutation({
onSuccess: (data) => {
setIsSubmitting(false)
if (data.workflowRunId) {
toast.success('Branch merge initiated!')
addWorkflowRun(data.workflowRunId)
+
+ // Track successful merge
+ sendEvent({
+ action: 'branch_merge_succeeded',
+ properties: {
+ branchType: currentBranch?.persistent ? 'persistent' : 'preview',
+ },
+ groups: {
+ project: parentProjectRef ?? 'Unknown',
+ organization: selectedOrg?.slug ?? 'Unknown',
+ },
+ })
} else {
toast.info('No changes to merge')
}
@@ -211,6 +240,19 @@ const MergePage: NextPageWithLayout = () => {
onError: (error) => {
setIsSubmitting(false)
toast.error(`Failed to merge branch: ${error.message}`)
+
+ // Track failed merge
+ sendEvent({
+ action: 'branch_merge_failed',
+ properties: {
+ branchType: currentBranch?.persistent ? 'persistent' : 'preview',
+ error: error.message,
+ },
+ groups: {
+ project: parentProjectRef ?? 'Unknown',
+ organization: selectedOrg?.slug ?? 'Unknown',
+ },
+ })
},
})
@@ -218,6 +260,17 @@ const MergePage: NextPageWithLayout = () => {
onSuccess: () => {
toast.success('Branch closed successfully')
router.push(`/project/${parentProjectRef}/branches`)
+ // Track delete button click
+ sendEvent({
+ action: 'branch_delete_button_clicked',
+ properties: {
+ origin: 'merge_page',
+ },
+ groups: {
+ project: parentProjectRef ?? 'Unknown',
+ organization: selectedOrg?.slug ?? 'Unknown',
+ },
+ })
},
onError: (error) => {
toast.error(`Failed to close branch: ${error.message}`)
@@ -243,6 +296,16 @@ const MergePage: NextPageWithLayout = () => {
const handleMerge = () => {
if (!currentBranch?.id || !parentProjectRef || !ref) return
setIsSubmitting(true)
+
+ // Track merge attempt
+ sendEvent({
+ action: 'branch_merge_submitted',
+ groups: {
+ project: parentProjectRef ?? 'Unknown',
+ organization: selectedOrg?.slug ?? 'Unknown',
+ },
+ })
+
mergeBranch({
id: currentBranch.id,
branchProjectRef: ref,
@@ -381,6 +444,13 @@ const MergePage: NextPageWithLayout = () => {
onSuccess: () => {
toast.success('Successfully closed merge request')
router.push(`/project/${project?.ref}/branches?tab=prs`)
+ sendEvent({
+ action: 'branch_close_merge_request_button_clicked',
+ groups: {
+ project: parentProjectRef ?? 'Unknown',
+ organization: selectedOrg?.slug ?? 'Unknown',
+ },
+ })
},
}
)
diff --git a/apps/studio/pages/project/[ref]/settings/storage.tsx b/apps/studio/pages/project/[ref]/settings/storage.tsx
index 2d489c4836265..b07d9931f5ba2 100644
--- a/apps/studio/pages/project/[ref]/settings/storage.tsx
+++ b/apps/studio/pages/project/[ref]/settings/storage.tsx
@@ -6,8 +6,8 @@ import {
ScaffoldHeader,
ScaffoldTitle,
} from 'components/layouts/Scaffold'
-import { StorageSettings } from 'components/to-be-cleaned/Storage'
-import { S3Connection } from 'components/to-be-cleaned/Storage/StorageSettings/S3Connection'
+import { StorageSettings } from 'components/interfaces/Storage'
+import { S3Connection } from 'components/interfaces/Storage/StorageSettings/S3Connection'
import type { NextPageWithLayout } from 'types'
const PageLayout: NextPageWithLayout = () => {
diff --git a/apps/studio/pages/project/[ref]/storage/buckets/[bucketId].tsx b/apps/studio/pages/project/[ref]/storage/buckets/[bucketId].tsx
index 39cf2671d820f..0e2c5031023bc 100644
--- a/apps/studio/pages/project/[ref]/storage/buckets/[bucketId].tsx
+++ b/apps/studio/pages/project/[ref]/storage/buckets/[bucketId].tsx
@@ -2,11 +2,11 @@ import { useParams } from 'common'
import DefaultLayout from 'components/layouts/DefaultLayout'
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
-import StorageBucketsError from 'components/layouts/StorageLayout/StorageBucketsError'
+import StorageBucketsError from 'components/interfaces/Storage/StorageBucketsError'
import StorageLayout from 'components/layouts/StorageLayout/StorageLayout'
-import { StorageExplorer } from 'components/to-be-cleaned/Storage'
-import { AnalyticBucketDetails } from 'components/to-be-cleaned/Storage/AnalyticBucketDetails'
-import { useSelectedBucket } from 'components/to-be-cleaned/Storage/StorageExplorer/useSelectedBucket'
+import { StorageExplorer } from 'components/interfaces/Storage'
+import { AnalyticBucketDetails } from 'components/interfaces/Storage/AnalyticBucketDetails'
+import { useSelectedBucket } from 'components/interfaces/Storage/StorageExplorer/useSelectedBucket'
import { useStorageExplorerStateSnapshot } from 'state/storage-explorer'
import type { NextPageWithLayout } from 'types'
@@ -16,6 +16,7 @@ const PageLayout: NextPageWithLayout = () => {
const { projectRef } = useStorageExplorerStateSnapshot()
const { bucket, error, isSuccess, isError } = useSelectedBucket()
+ // [Joshen] Checking against projectRef from storage explorer to check if the store has initialized
if (!project || !projectRef) return null
return (
diff --git a/apps/studio/pages/project/[ref]/storage/buckets/index.tsx b/apps/studio/pages/project/[ref]/storage/buckets/index.tsx
index 85ee54975229b..08680ac7113a0 100644
--- a/apps/studio/pages/project/[ref]/storage/buckets/index.tsx
+++ b/apps/studio/pages/project/[ref]/storage/buckets/index.tsx
@@ -1,7 +1,7 @@
import { useParams } from 'common'
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
-import StorageBucketsError from 'components/layouts/StorageLayout/StorageBucketsError'
+import StorageBucketsError from 'components/interfaces/Storage/StorageBucketsError'
import StorageLayout from 'components/layouts/StorageLayout/StorageLayout'
import DefaultLayout from 'components/layouts/DefaultLayout'
import ProductEmptyState from 'components/to-be-cleaned/ProductEmptyState'
diff --git a/apps/studio/pages/project/[ref]/storage/policies.tsx b/apps/studio/pages/project/[ref]/storage/policies.tsx
index d2daedb89251a..fe93cf7bc3547 100644
--- a/apps/studio/pages/project/[ref]/storage/policies.tsx
+++ b/apps/studio/pages/project/[ref]/storage/policies.tsx
@@ -1,6 +1,6 @@
import StorageLayout from 'components/layouts/StorageLayout/StorageLayout'
import DefaultLayout from 'components/layouts/DefaultLayout'
-import { StoragePolicies } from 'components/to-be-cleaned/Storage'
+import { StoragePolicies } from 'components/interfaces/Storage'
import type { NextPageWithLayout } from 'types'
const StoragePoliciesPage: NextPageWithLayout = () => {
diff --git a/apps/studio/state/storage-explorer.tsx b/apps/studio/state/storage-explorer.tsx
index 373423eb11b79..ffe8907e4412a 100644
--- a/apps/studio/state/storage-explorer.tsx
+++ b/apps/studio/state/storage-explorer.tsx
@@ -14,13 +14,13 @@ import {
STORAGE_SORT_BY,
STORAGE_SORT_BY_ORDER,
STORAGE_VIEWS,
-} from 'components/to-be-cleaned/Storage/Storage.constants'
+} from 'components/interfaces/Storage/Storage.constants'
import {
StorageColumn,
StorageItem,
StorageItemMetadata,
StorageItemWithColumn,
-} from 'components/to-be-cleaned/Storage/Storage.types'
+} from 'components/interfaces/Storage/Storage.types'
import {
calculateTotalRemainingTime,
downloadFile,
@@ -28,8 +28,8 @@ import {
formatFolderItems,
formatTime,
getFilesDataTransferItems,
-} from 'components/to-be-cleaned/Storage/StorageExplorer/StorageExplorer.utils'
-import { convertFromBytes } from 'components/to-be-cleaned/Storage/StorageSettings/StorageSettings.utils'
+} from 'components/interfaces/Storage/StorageExplorer/StorageExplorer.utils'
+import { convertFromBytes } from 'components/interfaces/Storage/StorageSettings/StorageSettings.utils'
import { InlineLink } from 'components/ui/InlineLink'
import { getKeys, useAPIKeysQuery } from 'data/api-keys/api-keys-query'
import { configKeys } from 'data/config/keys'
@@ -1758,14 +1758,11 @@ export const StorageExplorerStateContextProvider = ({ children }: PropsWithChild
const isDifferentProject = snap.projectRef !== project?.ref
const isDifferentResumableUploadUrl = snap.resumableUploadUrl !== resumableUploadUrl
- if (
- !isPaused &&
- hasDataReady &&
- (isDifferentProject || isDifferentResumableUploadUrl) &&
- serviceKey
- ) {
+ const serviceApiKey = serviceKey?.api_key ?? 'unknown'
+
+ if (!isPaused && hasDataReady && (isDifferentProject || isDifferentResumableUploadUrl)) {
const clientEndpoint = `${IS_PLATFORM ? 'https' : protocol}://${endpoint}`
- const supabaseClient = createClient(clientEndpoint, serviceKey.api_key, {
+ const supabaseClient = createClient(clientEndpoint, serviceApiKey, {
auth: {
persistSession: false,
autoRefreshToken: false,
@@ -1785,11 +1782,11 @@ export const StorageExplorerStateContextProvider = ({ children }: PropsWithChild
projectRef: project?.ref ?? '',
supabaseClient,
resumableUploadUrl,
- serviceKey: serviceKey.api_key,
+ serviceKey: serviceApiKey,
})
)
}
- }, [project?.ref, stateRef, serviceKey, isPaused, protocol, endpoint])
+ }, [project?.ref, stateRef, serviceKey?.api_key, isPaused, protocol, endpoint])
return (
diff --git a/apps/studio/tests/components/Storage/StorageSettings.utils.test.ts b/apps/studio/tests/components/Storage/StorageSettings.utils.test.ts
index cdfc5f97e06a8..82bf4350f1d54 100644
--- a/apps/studio/tests/components/Storage/StorageSettings.utils.test.ts
+++ b/apps/studio/tests/components/Storage/StorageSettings.utils.test.ts
@@ -1,8 +1,8 @@
-import { StorageSizeUnits } from 'components/to-be-cleaned/Storage/StorageSettings/StorageSettings.constants'
+import { StorageSizeUnits } from 'components/interfaces/Storage/StorageSettings/StorageSettings.constants'
import {
convertFromBytes,
convertToBytes,
-} from 'components/to-be-cleaned/Storage/StorageSettings/StorageSettings.utils'
+} from 'components/interfaces/Storage/StorageSettings/StorageSettings.utils'
import { describe, test, expect } from 'vitest'
describe('StorageSettings.utils: convertFromBytes', () => {
diff --git a/apps/studio/tests/features/logs/LogsPreviewer.test.tsx b/apps/studio/tests/features/logs/LogsPreviewer.test.tsx
index cc7051267e8ca..af44a42a9501b 100644
--- a/apps/studio/tests/features/logs/LogsPreviewer.test.tsx
+++ b/apps/studio/tests/features/logs/LogsPreviewer.test.tsx
@@ -32,7 +32,7 @@ vi.mock('lib/gotrue', async (importOriginal) => ({
beforeEach(() => {
addAPIMock({
method: 'get',
- path: '/platform/projects/default/analytics/endpoints/logs.all',
+ path: '/platform/projects/:ref/analytics/endpoints/logs.all',
response: LOGS_API_MOCKS,
})
})
diff --git a/apps/studio/tests/lib/msw.ts b/apps/studio/tests/lib/msw.ts
index 5d473acce0e4c..383df91d8c8df 100644
--- a/apps/studio/tests/lib/msw.ts
+++ b/apps/studio/tests/lib/msw.ts
@@ -1,25 +1,66 @@
import { setupServer } from 'msw/node'
import { GlobalAPIMocks } from './msw-global-api-mocks'
-import { http, HttpResponse } from 'msw'
+import { http, HttpResponse, HttpResponseResolver } from 'msw'
import { API_URL } from 'lib/constants'
+import type { paths } from '../../data/api'
export const mswServer = setupServer(...GlobalAPIMocks)
-export const addAPIMock = ({
+mswServer.events.on('request:start', ({ request }) => {
+ console.log('[MSW] Outgoing:', request.method, request.url)
+})
+
+// Recursively changes params in an endpoint path segment from {param}
+// format to :param format which is expected by Mock Service Worker
+type RemapParams = T extends `${infer Before}{${infer Param}}${infer After}`
+ ? `${Before}:${Param}${RemapParams}`
+ : T
+
+type TrimQueryParams = T extends `${infer Endpoint}?${string}` ? Endpoint : T
+
+// We need to remap all keys from our openapi-typescript generated types
+// so that they follow MSW's parameter formatting
+type RemapPaths = {
+ [K in keyof paths as RemapParams]: paths[K]
+}
+
+type Endpoints = keyof RemapPaths
+type Methods = Exclude
+
+// Extract either the 200 or 201 response object
+type SuccessResponse = RemapPaths[P][M] extends {
+ responses: { 200: { content: { 'application/json': infer R200 } } }
+}
+ ? R200
+ : RemapPaths[P][M] extends {
+ responses: { 201: { content: { 'application/json': infer R201 } } }
+ }
+ ? R201
+ : never
+
+const isResponseResolver = (val: unknown): val is HttpResponseResolver => typeof val === `function`
+
+export const addAPIMock =
({
method,
path,
response,
}: {
- method: keyof typeof http
- path: string
- response: any
+ method: M
+ path: P
+ response: SuccessResponse, M> | HttpResponseResolver
}) => {
const fullPath = `${API_URL}${path}`
- console.log('[MSW] Adding mock:', method, fullPath)
+ console.log('[MSW] Adding mock:', method.toUpperCase(), fullPath)
mswServer.use(
- http[method](fullPath, () => {
- return HttpResponse.json(response)
- })
+ http[method](
+ fullPath,
+ isResponseResolver(response)
+ ? response
+ : ({ request }) => {
+ console.log('[MSW] Handling request:', request.method, request.url, response)
+ return HttpResponse.json(response ?? null)
+ }
+ )
)
}
diff --git a/apps/studio/tests/setup/polyfills.js b/apps/studio/tests/setup/polyfills.js
deleted file mode 100644
index d80798f25fe1d..0000000000000
--- a/apps/studio/tests/setup/polyfills.js
+++ /dev/null
@@ -1,13 +0,0 @@
-const { TextDecoder, TextEncoder } = require('node:util')
-
-Object.defineProperties(globalThis, {
- TextDecoder: { value: TextDecoder },
- TextEncoder: { value: TextEncoder },
- CSS: {
- value: {
- supports: (k, v) => false,
- escape: (v) => v,
- },
- },
- TransformStream: { value: null },
-})
diff --git a/apps/studio/tests/setup/polyfills.ts b/apps/studio/tests/setup/polyfills.ts
new file mode 100644
index 0000000000000..f8702bb7e714b
--- /dev/null
+++ b/apps/studio/tests/setup/polyfills.ts
@@ -0,0 +1,37 @@
+import { TextDecoder, TextEncoder } from 'node:util'
+import { ReadableStream, TransformStream } from 'node:stream/web'
+import { vi } from 'vitest'
+import { configMocks } from 'jsdom-testing-mocks'
+import { act } from '@testing-library/react'
+
+configMocks({ act })
+
+// Warning: `restoreMocks: true` in vitest.config.ts will
+// cause this global mockImplementation to be **reset**
+// before any tests are run!
+Object.defineProperty(window, 'matchMedia', {
+ writable: true,
+ value: vi.fn().mockImplementation((query) => ({
+ matches: false,
+ media: query,
+ onchange: null,
+ addListener: vi.fn(), // deprecated
+ removeListener: vi.fn(), // deprecated
+ addEventListener: vi.fn(),
+ removeEventListener: vi.fn(),
+ dispatchEvent: vi.fn(),
+ })),
+})
+
+Object.defineProperties(globalThis, {
+ TextDecoder: { value: TextDecoder },
+ TextEncoder: { value: TextEncoder },
+ CSS: {
+ value: {
+ supports: (_k: any, _v: any) => false,
+ escape: (v: any) => v,
+ },
+ },
+ ReadableStream: { value: ReadableStream },
+ TransformStream: { value: TransformStream },
+})
diff --git a/apps/studio/tests/vitestSetup.ts b/apps/studio/tests/vitestSetup.ts
index 23c1903adfaaa..00ac0696c074a 100644
--- a/apps/studio/tests/vitestSetup.ts
+++ b/apps/studio/tests/vitestSetup.ts
@@ -5,22 +5,6 @@ import { afterAll, afterEach, beforeAll, vi } from 'vitest'
import { routerMock } from './lib/route-mock'
import { mswServer } from './lib/msw'
-mswServer.listen({ onUnhandledRequest: 'error' })
-
-Object.defineProperty(window, 'matchMedia', {
- writable: true,
- value: vi.fn().mockImplementation((query) => ({
- matches: false,
- media: query,
- onchange: null,
- addListener: vi.fn(), // deprecated
- removeListener: vi.fn(), // deprecated
- addEventListener: vi.fn(),
- removeEventListener: vi.fn(),
- dispatchEvent: vi.fn(),
- })),
-})
-
// Uncomment this if HTML in errors are being annoying.
//
// configure({
@@ -32,6 +16,7 @@ Object.defineProperty(window, 'matchMedia', {
// })
beforeAll(() => {
+ mswServer.listen({ onUnhandledRequest: `error` })
vi.mock('next/router', () => require('next-router-mock'))
vi.mock('next/navigation', async () => {
const actual = await vi.importActual('next/navigation')
diff --git a/apps/studio/vitest.config.ts b/apps/studio/vitest.config.ts
index 9ed6851ec8e57..1f45347020853 100644
--- a/apps/studio/vitest.config.ts
+++ b/apps/studio/vitest.config.ts
@@ -1,8 +1,8 @@
+import { resolve } from 'node:path'
+import { fileURLToPath } from 'node:url'
+import { defineConfig } from 'vitest/config'
import react from '@vitejs/plugin-react'
-import path, { resolve } from 'path'
-import { fileURLToPath } from 'url'
import tsconfigPaths from 'vite-tsconfig-paths'
-import { defineConfig } from 'vitest/config'
// Some tools like Vitest VSCode extensions, have trouble with resolving relative paths,
// as they use the directory of the test file as `cwd`, which makes them believe that
@@ -18,14 +18,12 @@ export default defineConfig({
],
resolve: {
alias: {
- '@ui': path.resolve(__dirname, './../../packages/ui/src'),
+ '@ui': resolve(__dirname, './../../packages/ui/src'),
},
},
test: {
globals: true,
environment: 'jsdom', // TODO(kamil): This should be set per test via header in .tsx files only
- include: [resolve(dirname, './**/*.test.{ts,tsx}')],
- restoreMocks: true,
setupFiles: [
resolve(dirname, './tests/vitestSetup.ts'),
resolve(dirname, './tests/setup/polyfills.js'),
diff --git a/packages/common/telemetry-constants.ts b/packages/common/telemetry-constants.ts
index 9417d17b7b483..44c2f1db80637 100644
--- a/packages/common/telemetry-constants.ts
+++ b/packages/common/telemetry-constants.ts
@@ -9,6 +9,11 @@
* @module telemetry-frontend
*/
+type TelemetryGroups = {
+ project: string
+ organization: string
+}
+
/**
* Triggered when a user signs up. When signing up with Email and Password, this is only triggered once user confirms their email.
*
@@ -66,10 +71,7 @@ export interface ConnectionStringCopiedEvent {
*/
connectionMethod: 'direct' | 'transaction_pooler' | 'session_pooler'
}
- groups: {
- project: string
- organization: string
- }
+ groups: TelemetryGroups
}
/**
@@ -91,10 +93,7 @@ export interface CronJobCreatedEvent {
*/
schedule: string
}
- groups: {
- project: string
- organization: string
- }
+ groups: TelemetryGroups
}
/**
@@ -116,10 +115,7 @@ export interface CronJobUpdatedEvent {
*/
schedule: string
}
- groups: {
- project: string
- organization: string
- }
+ groups: TelemetryGroups
}
/**
@@ -131,10 +127,7 @@ export interface CronJobUpdatedEvent {
*/
export interface CronJobDeletedEvent {
action: 'cron_job_deleted'
- groups: {
- project: string
- organization: string
- }
+ groups: TelemetryGroups
}
/**
@@ -146,10 +139,7 @@ export interface CronJobDeletedEvent {
*/
export interface CronJobCreateClickedEvent {
action: 'cron_job_create_clicked'
- groups: {
- project: string
- organization: string
- }
+ groups: TelemetryGroups
}
/**
@@ -161,10 +151,7 @@ export interface CronJobCreateClickedEvent {
*/
export interface CronJobUpdateClickedEvent {
action: 'cron_job_update_clicked'
- groups: {
- project: string
- organization: string
- }
+ groups: TelemetryGroups
}
/**
@@ -176,10 +163,7 @@ export interface CronJobUpdateClickedEvent {
*/
export interface CronJobDeleteClickedEvent {
action: 'cron_job_delete_clicked'
- groups: {
- project: string
- organization: string
- }
+ groups: TelemetryGroups
}
/**
@@ -191,10 +175,7 @@ export interface CronJobDeleteClickedEvent {
*/
export interface CronJobHistoryClickedEvent {
action: 'cron_job_history_clicked'
- groups: {
- project: string
- organization: string
- }
+ groups: TelemetryGroups
}
/**
@@ -213,10 +194,7 @@ export interface FeaturePreviewEnabledEvent {
*/
feature: string
}
- groups: {
- project: string
- organization: string
- }
+ groups: TelemetryGroups
}
/**
@@ -235,10 +213,7 @@ export interface FeaturePreviewDisabledEvent {
*/
feature: string
}
- groups: {
- project: string
- organization: string
- }
+ groups: TelemetryGroups
}
/**
@@ -256,10 +231,7 @@ export interface ProjectCreationSimpleVersionSubmittedEvent {
properties: {
instanceSize: string
}
- groups: {
- project: string
- organization: string
- }
+ groups: TelemetryGroups
}
/**
@@ -277,9 +249,7 @@ export interface ProjectCreationSimpleVersionConfirmModalOpenedEvent {
properties: {
instanceSize: string
}
- groups: {
- organization: string
- }
+ groups: Omit
}
/**
@@ -357,10 +327,7 @@ export interface ProjectCreationSecondStepSubmittedEvent {
*/
export interface RealtimeInspectorListenChannelClickedEvent {
action: 'realtime_inspector_listen_channel_clicked'
- groups: {
- project: string
- organization: string
- }
+ groups: TelemetryGroups
}
/**
@@ -372,10 +339,7 @@ export interface RealtimeInspectorListenChannelClickedEvent {
*/
export interface RealtimeInspectorBroadcastSentEvent {
action: 'realtime_inspector_broadcast_sent'
- groups: {
- project: string
- organization: string
- }
+ groups: TelemetryGroups
}
/**
@@ -387,10 +351,7 @@ export interface RealtimeInspectorBroadcastSentEvent {
*/
export interface RealtimeInspectorMessageClickedEvent {
action: 'realtime_inspector_message_clicked'
- groups: {
- project: string
- organization: string
- }
+ groups: TelemetryGroups
}
/**
@@ -402,10 +363,7 @@ export interface RealtimeInspectorMessageClickedEvent {
*/
export interface RealtimeInspectorCopyMessageClickedEvent {
action: 'realtime_inspector_copy_message_clicked'
- groups: {
- project: string
- organization: string
- }
+ groups: TelemetryGroups
}
/**
@@ -417,10 +375,7 @@ export interface RealtimeInspectorCopyMessageClickedEvent {
*/
export interface RealtimeInspectorFiltersAppliedEvent {
action: 'realtime_inspector_filters_applied'
- groups: {
- project: string
- organization: string
- }
+ groups: TelemetryGroups
}
/**
@@ -432,10 +387,7 @@ export interface RealtimeInspectorFiltersAppliedEvent {
*/
export interface RealtimeInspectorDatabaseRoleUpdatedEvent {
action: 'realtime_inspector_database_role_updated'
- groups: {
- project: string
- organization: string
- }
+ groups: TelemetryGroups
}
/**
@@ -457,10 +409,7 @@ export interface RealtimeToggleTableClickedEvent {
*/
origin: 'tableSidePanel' | 'tableGridHeader'
}
- groups: {
- project: string
- organization: string
- }
+ groups: TelemetryGroups
}
/**
@@ -478,10 +427,7 @@ export interface SqlEditorQuickstartClickedEvent {
*/
quickstartName: string
}
- groups: {
- project: string
- organization: string
- }
+ groups: TelemetryGroups
}
/**
@@ -499,10 +445,7 @@ export interface SqlEditorTemplateClickedEvent {
*/
templateName: string
}
- groups: {
- project: string
- organization: string
- }
+ groups: TelemetryGroups
}
/**
@@ -514,10 +457,7 @@ export interface SqlEditorTemplateClickedEvent {
*/
export interface SqlEditorResultDownloadCsvClickedEvent {
action: 'sql_editor_result_download_csv_clicked'
- groups: {
- project: string
- organization: string
- }
+ groups: TelemetryGroups
}
/**
@@ -529,10 +469,7 @@ export interface SqlEditorResultDownloadCsvClickedEvent {
*/
export interface SqlEditorResultCopyMarkdownClickedEvent {
action: 'sql_editor_result_copy_markdown_clicked'
- groups: {
- project: string
- organization: string
- }
+ groups: TelemetryGroups
}
/**
@@ -544,10 +481,7 @@ export interface SqlEditorResultCopyMarkdownClickedEvent {
*/
export interface SqlEditorResultCopyJsonClickedEvent {
action: 'sql_editor_result_copy_json_clicked'
- groups: {
- project: string
- organization: string
- }
+ groups: TelemetryGroups
}
/**
@@ -558,10 +492,7 @@ export interface SqlEditorResultCopyJsonClickedEvent {
*/
export interface AssistantPromptSubmittedEvent {
action: 'assistant_prompt_submitted'
- groups: {
- project: string
- organization: string
- }
+ groups: TelemetryGroups
}
/**
@@ -572,10 +503,7 @@ export interface AssistantPromptSubmittedEvent {
*/
export interface AssistantDebugSubmittedEvent {
action: 'assistant_debug_submitted'
- groups: {
- project: string
- organization: string
- }
+ groups: TelemetryGroups
}
/**
@@ -593,10 +521,7 @@ export interface AssistantSuggestionRunQueryClickedEvent {
queryType: string
category?: string
}
- groups: {
- project: string
- organization: string
- }
+ groups: TelemetryGroups
}
/**
@@ -617,10 +542,7 @@ export interface AssistantSqlDiffHandlerEvaluatedEvent {
*/
handlerAccepted: boolean
}
- groups: {
- project: string
- organization: string
- }
+ groups: TelemetryGroups
}
/**
@@ -638,10 +560,7 @@ export interface AssistantEditInSqlEditorClickedEvent {
isInSQLEditor: boolean
isInNewSnippet: boolean
}
- groups: {
- project: string
- organization: string
- }
+ groups: TelemetryGroups
}
/**
@@ -653,10 +572,7 @@ export interface AssistantEditInSqlEditorClickedEvent {
*/
export interface CustomReportAddSQLBlockClicked {
action: 'custom_report_add_sql_block_clicked'
- groups: {
- project: string
- organization: string
- }
+ groups: TelemetryGroups
}
/**
@@ -668,10 +584,7 @@ export interface CustomReportAddSQLBlockClicked {
*/
export interface CustomReportAssistantSQLBlockAddedEvent {
action: 'custom_report_assistant_sql_block_added'
- groups: {
- project: string
- organization: string
- }
+ groups: TelemetryGroups
}
/**
@@ -941,10 +854,7 @@ export interface SignInButtonClickedEvent {
*/
export interface HelpButtonClickedEvent {
action: 'help_button_clicked'
- groups: {
- project?: string
- organization?: string
- }
+ groups: Partial
}
/**
@@ -955,10 +865,7 @@ export interface HelpButtonClickedEvent {
*/
export interface SendFeedbackButtonClickedEvent {
action: 'send_feedback_button_clicked'
- groups: {
- project?: string
- organization?: string
- }
+ groups: Partial
}
/**
@@ -976,10 +883,7 @@ export interface ExampleProjectCardClickedEvent {
*/
cardTitle: string
}
- groups: {
- project: string
- organization: string
- }
+ groups: TelemetryGroups
}
/**
@@ -999,10 +903,7 @@ export interface ImportDataButtonClickedEvent {
*/
tableType: 'New Table' | 'Existing Table'
}
- groups: {
- project: string
- organization: string
- }
+ groups: TelemetryGroups
}
/**
@@ -1014,10 +915,7 @@ export interface ImportDataButtonClickedEvent {
*/
export interface ImportDataAddedEvent {
action: 'import_data_added'
- groups: {
- project: string
- organization: string
- }
+ groups: TelemetryGroups
}
/**
@@ -1029,10 +927,7 @@ export interface ImportDataAddedEvent {
*/
export interface SqlEditorQueryRunButtonClickedEvent {
action: 'sql_editor_query_run_button_clicked'
- groups: {
- project: string
- organization: string
- }
+ groups: TelemetryGroups
}
/**
@@ -1054,7 +949,7 @@ export interface StudioPricingPlanCtaClickedEvent {
*/
currentPlan?: string
}
- groups: { organization: string }
+ groups: Omit
}
/**
@@ -1073,7 +968,7 @@ export interface StudioPricingSidePanelOpenedEvent {
*/
origin?: string
}
- groups: { organization: string }
+ groups: Omit
}
/**
@@ -1085,10 +980,7 @@ export interface StudioPricingSidePanelOpenedEvent {
*/
export interface ReportsDatabaseGrafanaBannerClickedEvent {
action: 'reports_database_grafana_banner_clicked'
- groups: {
- project: string
- organization: string
- }
+ groups: TelemetryGroups
}
/**
@@ -1108,10 +1000,7 @@ export interface EdgeFunctionDeployButtonClickedEvent {
*/
origin: 'functions_editor' | 'functions_ai_assistant'
}
- groups: {
- project: string
- organization: string
- }
+ groups: TelemetryGroups
}
/**
@@ -1123,10 +1012,7 @@ export interface EdgeFunctionDeployButtonClickedEvent {
*/
export interface EdgeFunctionDeployUpdatesConfirmClickedEvent {
action: 'edge_function_deploy_updates_confirm_clicked'
- groups: {
- project: string
- organization: string
- }
+ groups: TelemetryGroups
}
/**
@@ -1147,10 +1033,7 @@ export interface EdgeFunctionAiAssistantButtonClickedEvent {
*/
origin: 'no_functions_block' | 'secondary_action' | 'functions_editor_chat'
}
- groups: {
- project: string
- organization: string
- }
+ groups: TelemetryGroups
}
/**
@@ -1170,10 +1053,7 @@ export interface EdgeFunctionViaEditorButtonClickedEvent {
*/
origin: 'no_functions_block' | 'secondary_action'
}
- groups: {
- project: string
- organization: string
- }
+ groups: TelemetryGroups
}
/**
@@ -1194,10 +1074,7 @@ export interface EdgeFunctionTemplateClickedEvent {
*/
origin: 'functions_page' | 'editor_page'
}
- groups: {
- project: string
- organization: string
- }
+ groups: TelemetryGroups
}
/**
@@ -1217,10 +1094,7 @@ export interface EdgeFunctionViaCliButtonClickedEvent {
*/
origin: 'no_functions_block' | 'secondary_action'
}
- groups: {
- project: string
- organization: string
- }
+ groups: TelemetryGroups
}
/**
@@ -1232,10 +1106,7 @@ export interface EdgeFunctionViaCliButtonClickedEvent {
*/
export interface EdgeFunctionDeployUpdatesButtonClickedEvent {
action: 'edge_function_deploy_updates_button_clicked'
- groups: {
- project: string
- organization: string
- }
+ groups: TelemetryGroups
}
/**
@@ -1253,10 +1124,7 @@ export interface EdgeFunctionTestSendButtonClickedEvent {
*/
httpMethod: string
}
- groups: {
- project: string
- organization: string
- }
+ groups: TelemetryGroups
}
/**
@@ -1268,10 +1136,7 @@ export interface EdgeFunctionTestSendButtonClickedEvent {
*/
export interface EdgeFunctionTestSidePanelOpenedEvent {
action: 'edge_function_test_side_panel_opened'
- groups: {
- project: string
- organization: string
- }
+ groups: TelemetryGroups
}
/**
@@ -1286,10 +1151,7 @@ export interface SupportTicketSubmittedEvent {
properties: {
ticketCategory: string
}
- groups: {
- project?: string
- organization?: string
- }
+ groups: Partial
}
/**
@@ -1302,10 +1164,7 @@ export interface SupportTicketSubmittedEvent {
*/
export interface AiAssistantInSupportFormClickedEvent {
action: 'ai_assistant_in_support_form_clicked'
- groups: {
- project?: string
- organization?: string
- }
+ groups: Partial
}
/**
@@ -1337,9 +1196,7 @@ export interface OrganizationMfaEnforcementUpdated {
properties: {
mfaEnforced: boolean
}
- groups: {
- organization: string
- }
+ groups: Omit
}
/**
@@ -1357,10 +1214,7 @@ export interface ForeignDataWrapperCreatedEvent {
*/
wrapperType: string
}
- groups: {
- project: string
- organization: string
- }
+ groups: TelemetryGroups
}
/**
@@ -1378,10 +1232,166 @@ export interface StorageBucketCreatedEvent {
*/
bucketType?: string
}
- groups: {
- project: string
- organization: string
+ groups: TelemetryGroups
+}
+
+/**
+ * Triggered when a new branch is created.
+ *
+ * @group Events
+ * @source studio
+ * @page /dashboard/project/{ref}/branches
+ */
+export interface BranchCreateButtonClickedEvent {
+ action: 'branch_create_button_clicked'
+ properties: {
+ /**
+ * The type of branch created, e.g. preview, persistent
+ */
+ branchType: 'preview' | 'persistent'
+ /**
+ * Whether the branch was created with a git branch association
+ */
+ gitlessBranching: boolean
}
+ groups: TelemetryGroups
+}
+
+/**
+ * Triggered when a branch delete button is clicked.
+ *
+ * @group Events
+ * @source studio
+ * @page /dashboard/project/{ref}/branches
+ */
+export interface BranchDeleteButtonClickedEvent {
+ action: 'branch_delete_button_clicked'
+ properties: {
+ /**
+ * The type of branch being deleted, e.g. preview, persistent
+ */
+ branchType?: 'preview' | 'persistent'
+ /**
+ * Where the delete action was initiated from
+ */
+ origin: 'branches_page' | 'merge_page'
+ }
+ groups: TelemetryGroups
+}
+
+/**
+ * Triggered when a create merge request is clicked for a branch.
+ *
+ * @group Events
+ * @source studio
+ * @page /dashboard/project/{ref}/branches
+ */
+export interface BranchCreateMergeRequestButtonClickedEvent {
+ action: 'branch_create_merge_request_button_clicked'
+ properties: {
+ /**
+ * The type of branch being merged, e.g. preview, persistent
+ */
+ branchType: 'preview' | 'persistent'
+ origin: 'header' | 'merge_page' | 'branch_selector'
+ }
+ groups: TelemetryGroups
+}
+
+/**
+ * Triggered when a merge request is closed.
+ *
+ * @group Events
+ * @source studio
+ * @page /dashboard/project/{ref}/branches/merge-requests
+ */
+export interface BranchCloseMergeRequestButtonClickedEvent {
+ action: 'branch_close_merge_request_button_clicked'
+ groups: TelemetryGroups
+}
+
+/**
+ * Triggered when a user clicks the merge button successfully to attempt merging a branch.
+ *
+ * @group Events
+ * @source studio
+ * @page /dashboard/project/{ref}/merge
+ */
+export interface BranchMergeSubmittedEvent {
+ action: 'branch_merge_submitted'
+ groups: TelemetryGroups
+}
+
+/**
+ * Triggered when a branch merge is successful.
+ *
+ * @group Events
+ * @source studio
+ * @page /dashboard/project/{ref}/merge
+ */
+export interface BranchMergeSucceededEvent {
+ action: 'branch_merge_succeeded'
+ properties: {
+ /**
+ * The type of branch being merged, e.g. preview, persistent
+ */
+ branchType: 'preview' | 'persistent'
+ }
+ groups: TelemetryGroups
+}
+
+/**
+ * Triggered when a branch merge fails.
+ *
+ * @group Events
+ * @source studio
+ * @page /dashboard/project/{ref}/merge
+ */
+export interface BranchMergeFailedEvent {
+ action: 'branch_merge_failed'
+ properties: {
+ /**
+ * The type of branch being merged, e.g. preview, persistent
+ */
+ branchType: 'preview' | 'persistent'
+ /**
+ * The error message or reason for failure
+ */
+ error?: string
+ }
+ groups: TelemetryGroups
+}
+
+/**
+ * Triggered when a branch is updated on push with latest changes from production.
+ * Does not include renaming and linking to GitHub branch.
+ *
+ * @group Events
+ * @source studio
+ * @page /dashboard/project/{ref}/merge
+ */
+export interface BranchUpdatedEvent {
+ action: 'branch_updated'
+ properties: {
+ /**
+ * The source of the update action
+ */
+ source: 'merge_page' | 'out_of_date_notice'
+ modifiedEdgeFunctions?: boolean
+ }
+ groups: TelemetryGroups
+}
+
+/**
+ * Triggered when a user clicks the review with assistant button for a merge.
+ *
+ * @group Events
+ * @source studio
+ * @page /dashboard/project/{ref}/merge
+ */
+export interface BranchReviewWithAssistantClickedEvent {
+ action: 'branch_review_with_assistant_clicked'
+ groups: TelemetryGroups
}
/**
@@ -1465,3 +1475,12 @@ export type TelemetryEvent =
| OrganizationMfaEnforcementUpdated
| ForeignDataWrapperCreatedEvent
| StorageBucketCreatedEvent
+ | BranchCreateButtonClickedEvent
+ | BranchDeleteButtonClickedEvent
+ | BranchCreateMergeRequestButtonClickedEvent
+ | BranchCloseMergeRequestButtonClickedEvent
+ | BranchMergeSubmittedEvent
+ | BranchMergeSucceededEvent
+ | BranchMergeFailedEvent
+ | BranchUpdatedEvent
+ | BranchReviewWithAssistantClickedEvent
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index fb38e65784442..6f2a902f58768 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -189,7 +189,7 @@ importers:
version: 15.3.1(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4)
next-contentlayer2:
specifier: 0.4.6
- version: 0.4.6(contentlayer2@0.4.6(esbuild@0.25.2)(markdown-wasm@1.2.0)(supports-color@8.1.1))(esbuild@0.25.2)(markdown-wasm@1.2.0)(next@15.3.1(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(supports-color@8.1.1)
+ version: 0.4.6(contentlayer2@0.4.6(esbuild@0.25.2)(markdown-wasm@1.2.0)(supports-color@8.1.1))(esbuild@0.25.2)(markdown-wasm@1.2.0)(next@15.3.1(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(supports-color@8.1.1)
next-themes:
specifier: ^0.3.0
version: 0.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
@@ -1132,6 +1132,9 @@ importers:
import-in-the-middle:
specifier: ^1.13.1
version: 1.13.1
+ jsdom-testing-mocks:
+ specifier: ^1.13.1
+ version: 1.13.1
msw:
specifier: ^2.3.0
version: 2.4.11(typescript@5.5.2)
@@ -1308,13 +1311,13 @@ importers:
version: 15.3.1(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4)
next-contentlayer2:
specifier: 0.4.6
- version: 0.4.6(contentlayer2@0.4.6(esbuild@0.25.2)(markdown-wasm@1.2.0)(supports-color@8.1.1))(esbuild@0.25.2)(markdown-wasm@1.2.0)(next@15.3.1(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(supports-color@8.1.1)
+ version: 0.4.6(contentlayer2@0.4.6(esbuild@0.25.2)(markdown-wasm@1.2.0)(supports-color@8.1.1))(esbuild@0.25.2)(markdown-wasm@1.2.0)(next@15.3.1(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(supports-color@8.1.1)
next-themes:
specifier: ^0.3.0
version: 0.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
openai:
specifier: ^5.9.0
- version: 5.9.0(ws@8.18.1)(zod@3.23.8)
+ version: 5.9.0(ws@8.18.3)(zod@3.23.8)
openapi-fetch:
specifier: 0.12.4
version: 0.12.4
@@ -9680,6 +9683,9 @@ packages:
peerDependencies:
ajv: 4.11.8 - 8
+ bezier-easing@2.1.0:
+ resolution: {integrity: sha512-gbIqZ/eslnUFC1tjEvtz0sgx+xTK20wDnYMIA27VA04R7w6xxXQPZDbibjA9DTWZRA2CXtwHykkVzlCaAJAZig==}
+
big-integer@1.6.51:
resolution: {integrity: sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==}
engines: {node: '>=0.6'}
@@ -10373,6 +10379,9 @@ packages:
css-in-js-utils@3.1.0:
resolution: {integrity: sha512-fJAcud6B3rRu+KHYk+Bwf+WFL2MDCJJ1XG9x137tJQ0xYxor7XziQtuGFbWNdqrvF4Tk26O3H73nfVqXt/fW1A==}
+ css-mediaquery@0.1.2:
+ resolution: {integrity: sha512-COtn4EROW5dBGlE/4PiKnh6rZpAPxDeFLaEEwt4i10jpDMFt2EhQGS79QmmrO+iKCHv0PU/HrOWEhijFd1x99Q==}
+
css-select@5.1.0:
resolution: {integrity: sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==}
@@ -13119,6 +13128,10 @@ packages:
jsbn@1.1.0:
resolution: {integrity: sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==}
+ jsdom-testing-mocks@1.13.1:
+ resolution: {integrity: sha512-8BAsnuoO4DLGTf7LDbSm8fcx5CUHSv4h+bdUbwyt6rMYAXWjeHLRx9f8sYiSxoOTXy3S1e06pe87KER39o1ckA==}
+ engines: {node: '>=14'}
+
jsdom@20.0.3:
resolution: {integrity: sha512-SYhBvTh89tTfCD/CRdSOm13mOBa42iTaTyfyEWBdKcGdPxPtLFBXuHR8XHb33YNYaP+lLbmSvBTsnoesCNJEsQ==}
engines: {node: '>=14'}
@@ -18200,6 +18213,18 @@ packages:
utf-8-validate:
optional: true
+ ws@8.18.3:
+ resolution: {integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==}
+ engines: {node: '>=10.0.0'}
+ peerDependencies:
+ bufferutil: ^4.0.1
+ utf-8-validate: '>=5.0.2'
+ peerDependenciesMeta:
+ bufferutil:
+ optional: true
+ utf-8-validate:
+ optional: true
+
xml-lexer@0.2.2:
resolution: {integrity: sha512-G0i98epIwiUEiKmMcavmVdhtymW+pCAohMRgybyIME9ygfVu8QheIi+YoQh3ngiThsT0SQzJT4R0sKDEv8Ou0w==}
@@ -21387,10 +21412,10 @@ snapshots:
'@graphql-tools/utils': 10.8.6(graphql@16.10.0)
'@whatwg-node/disposablestack': 0.0.6
graphql: 16.10.0
- graphql-ws: 6.0.4(graphql@16.10.0)(ws@8.18.1)
- isomorphic-ws: 5.0.0(ws@8.18.1)
+ graphql-ws: 6.0.4(graphql@16.10.0)(ws@8.18.3)
+ isomorphic-ws: 5.0.0(ws@8.18.3)
tslib: 2.8.1
- ws: 8.18.1
+ ws: 8.18.3
transitivePeerDependencies:
- '@fastify/websocket'
- bufferutil
@@ -21403,10 +21428,10 @@ snapshots:
'@graphql-tools/utils': 10.8.6(graphql@16.11.0)
'@whatwg-node/disposablestack': 0.0.6
graphql: 16.11.0
- graphql-ws: 6.0.4(graphql@16.11.0)(ws@8.18.1)
- isomorphic-ws: 5.0.0(ws@8.18.1)
+ graphql-ws: 6.0.4(graphql@16.11.0)(ws@8.18.3)
+ isomorphic-ws: 5.0.0(ws@8.18.3)
tslib: 2.8.1
- ws: 8.18.1
+ ws: 8.18.3
transitivePeerDependencies:
- '@fastify/websocket'
- bufferutil
@@ -21448,9 +21473,9 @@ snapshots:
'@graphql-tools/utils': 10.8.6(graphql@16.10.0)
'@types/ws': 8.5.10
graphql: 16.10.0
- isomorphic-ws: 5.0.0(ws@8.18.1)
+ isomorphic-ws: 5.0.0(ws@8.18.3)
tslib: 2.8.1
- ws: 8.18.1
+ ws: 8.18.3
transitivePeerDependencies:
- bufferutil
- utf-8-validate
@@ -21460,9 +21485,9 @@ snapshots:
'@graphql-tools/utils': 10.8.6(graphql@16.11.0)
'@types/ws': 8.5.10
graphql: 16.11.0
- isomorphic-ws: 5.0.0(ws@8.18.1)
+ isomorphic-ws: 5.0.0(ws@8.18.3)
tslib: 2.8.1
- ws: 8.18.1
+ ws: 8.18.3
transitivePeerDependencies:
- bufferutil
- utf-8-validate
@@ -26940,7 +26965,7 @@ snapshots:
'@supabase/node-fetch': 2.6.15
'@types/phoenix': 1.6.4
'@types/ws': 8.5.10
- ws: 8.18.1
+ ws: 8.18.3
transitivePeerDependencies:
- bufferutil
- utf-8-validate
@@ -26950,7 +26975,7 @@ snapshots:
'@supabase/node-fetch': 2.6.15
'@types/phoenix': 1.6.4
'@types/ws': 8.5.10
- ws: 8.18.1
+ ws: 8.18.3
transitivePeerDependencies:
- bufferutil
- utf-8-validate
@@ -29238,6 +29263,8 @@ snapshots:
jsonpointer: 5.0.1
leven: 3.1.0
+ bezier-easing@2.1.0: {}
+
big-integer@1.6.51: {}
big.js@5.2.2: {}
@@ -30029,6 +30056,8 @@ snapshots:
dependencies:
hyphenate-style-name: 1.0.4
+ css-mediaquery@0.1.2: {}
+
css-select@5.1.0:
dependencies:
boolbase: 1.0.0
@@ -32057,12 +32086,19 @@ snapshots:
graphql: 16.10.0
optionalDependencies:
ws: 8.18.1
+ optional: true
- graphql-ws@6.0.4(graphql@16.11.0)(ws@8.18.1):
+ graphql-ws@6.0.4(graphql@16.10.0)(ws@8.18.3):
+ dependencies:
+ graphql: 16.10.0
+ optionalDependencies:
+ ws: 8.18.3
+
+ graphql-ws@6.0.4(graphql@16.11.0)(ws@8.18.3):
dependencies:
graphql: 16.11.0
optionalDependencies:
- ws: 8.18.1
+ ws: 8.18.3
graphql@16.10.0: {}
@@ -33038,6 +33074,10 @@ snapshots:
dependencies:
ws: 8.18.1
+ isomorphic-ws@5.0.0(ws@8.18.3):
+ dependencies:
+ ws: 8.18.3
+
isomorphic.js@0.2.5: {}
istanbul-lib-coverage@3.2.2: {}
@@ -33159,6 +33199,11 @@ snapshots:
jsbn@1.1.0: {}
+ jsdom-testing-mocks@1.13.1:
+ dependencies:
+ bezier-easing: 2.1.0
+ css-mediaquery: 0.1.2
+
jsdom@20.0.3(supports-color@8.1.1):
dependencies:
abab: 2.0.6
@@ -33185,7 +33230,7 @@ snapshots:
whatwg-encoding: 2.0.0
whatwg-mimetype: 3.0.0
whatwg-url: 11.0.0
- ws: 8.18.1
+ ws: 8.18.3
xml-name-validator: 4.0.0
transitivePeerDependencies:
- bufferutil
@@ -34975,7 +35020,7 @@ snapshots:
neo-async@2.6.2: {}
- next-contentlayer2@0.4.6(contentlayer2@0.4.6(esbuild@0.25.2)(markdown-wasm@1.2.0)(supports-color@8.1.1))(esbuild@0.25.2)(markdown-wasm@1.2.0)(next@15.3.1(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(supports-color@8.1.1):
+ next-contentlayer2@0.4.6(contentlayer2@0.4.6(esbuild@0.25.2)(markdown-wasm@1.2.0)(supports-color@8.1.1))(esbuild@0.25.2)(markdown-wasm@1.2.0)(next@15.3.1(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(supports-color@8.1.1):
dependencies:
'@contentlayer2/core': 0.4.3(esbuild@0.25.2)(markdown-wasm@1.2.0)(supports-color@8.1.1)
'@contentlayer2/utils': 0.4.3
@@ -35545,9 +35590,9 @@ snapshots:
transitivePeerDependencies:
- encoding
- openai@5.9.0(ws@8.18.1)(zod@3.23.8):
+ openai@5.9.0(ws@8.18.3)(zod@3.23.8):
optionalDependencies:
- ws: 8.18.1
+ ws: 8.18.3
zod: 3.23.8
openapi-fetch@0.12.4:
@@ -40030,6 +40075,8 @@ snapshots:
ws@8.18.1: {}
+ ws@8.18.3: {}
+
xml-lexer@0.2.2:
dependencies:
eventemitter3: 2.0.3