useDataApiRevokeOnCreateDefault.ts 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475
  1. // @ts-nocheck
  2. import { useEffect, useRef } from 'react'
  3. import { usePHFlag } from '../ui/useFlag'
  4. import { IS_TEST_ENV } from '@/lib/constants'
  5. import { useTrack } from '@/lib/telemetry/track'
  6. /**
  7. * Controls the default state of the "Automatically expose new tables"
  8. * checkbox at project creation. When the flag is on, the checkbox defaults
  9. * to unchecked (i.e. revoke SQL runs). When off/absent, the checkbox defaults
  10. * to checked (current behaviour — default grants remain).
  11. */
  12. export const useDataApiRevokeOnCreateDefaultEnabled = (): boolean => {
  13. const flag = usePHFlag<boolean>('dataApiRevokeOnCreateDefault')
  14. // Preserve current behaviour (default grants remain) in tests so existing
  15. // E2E flows don't change silently. Tests that need the revoke-default path
  16. // should opt in explicitly.
  17. if (IS_TEST_ENV) {
  18. return false
  19. }
  20. return !!flag
  21. }
  22. type DefaultPrivilegesExposureOptions =
  23. | {
  24. surface: 'main'
  25. dataApiDefaultPrivileges: boolean
  26. hasUserModified: boolean
  27. }
  28. | {
  29. surface: 'vercel'
  30. orgSlug: string | undefined
  31. dataApiDefaultPrivileges: boolean
  32. hasUserModified: boolean
  33. }
  34. /**
  35. * Fires `project_creation_default_privileges_exposed` once per mount, once the
  36. * flag has resolved AND the form value is consistent with the flag's expected
  37. * default. The convergence gate avoids a render-ordering race: caller-side
  38. * sync effects (in /new/[slug] and the Vercel deploy flow) update the form
  39. * value when the flag resolves late, but child effects fire before parent
  40. * effects in the same commit, so without the gate the exposure would capture
  41. * the stale pre-sync value. If the user has dirtied the field, fire
  42. * immediately with their explicit value. Deduplicated via ref.
  43. */
  44. export const useTrackDefaultPrivilegesExposure = (options: DefaultPrivilegesExposureOptions) => {
  45. const track = useTrack()
  46. const flag = usePHFlag<boolean>('dataApiRevokeOnCreateDefault')
  47. const hasTracked = useRef(false)
  48. const { surface, dataApiDefaultPrivileges, hasUserModified } = options
  49. const orgSlug = options.surface === 'vercel' ? options.orgSlug : undefined
  50. useEffect(() => {
  51. if (hasTracked.current) return
  52. if (flag === undefined) return
  53. if (surface === 'vercel' && !orgSlug) return
  54. // Gate on form-flag convergence unless the user explicitly dirtied the field.
  55. if (!hasUserModified && dataApiDefaultPrivileges !== !flag) return
  56. hasTracked.current = true
  57. track(
  58. 'project_creation_default_privileges_exposed',
  59. {
  60. surface,
  61. dataApiDefaultPrivileges,
  62. dataApiRevokeOnCreateDefaultEnabled: flag,
  63. },
  64. surface === 'vercel' ? { organization: orgSlug } : undefined
  65. )
  66. }, [flag, track, surface, dataApiDefaultPrivileges, hasUserModified, orgSlug])
  67. }