Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions cli/src/app/debug.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,15 @@ function describeFetchFailure(error: unknown, endpoint: string) {

export type { AppDebugOptions as OptionsBaseDebug } from '../schemas/app'

export async function markSnag(channel: string, orgId: string, apikey: string, event: string, appId?: string, icon = '✅') {
export async function markSnag(channel: string, orgId: string, apikey: string, event: string, appId?: string, icon = '✅', tags: Record<string, string | number | boolean> = {}) {
const allTags = { ...(appId ? { 'app-id': appId } : {}), ...tags }
await sendEvent(apikey, {
channel,
event,
icon,
org_id: orgId,
tracking_version: 2,
...(appId ? { tags: { 'app-id': appId } } : {}),
...(Object.keys(allTags).length > 0 ? { tags: allTags } : {}),
notify: false,
})
}
Expand Down
7 changes: 5 additions & 2 deletions cli/src/app/info.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { version as nodeVersion } from 'node:process'
import { log, spinner } from '@clack/prompts'
import pack from '../../package.json'
import { trackEvent } from '../analytics/track'
import { getAllPackagesDependencies, getAppId, getBundleVersion, getConfig } from '../utils'
import { getAllPackagesDependencies, getAppId, getBundleVersion, getCapgoPluginTags, getConfig } from '../utils'
import { getLatestVersion } from '../utils/latest-version'

async function getLatestDependencies(installedDependencies: Record<string, string>) {
Expand Down Expand Up @@ -109,7 +109,10 @@ export async function getInfoInternal(options: DoctorInfoOptions, silent = false
channel: 'cli-usage',
event: 'Doctor Ran',
icon: '👨‍⚕️',
tags: computeDoctorAnalyticsTags(installedDependencies, latestDependencies),
tags: {
...computeDoctorAnalyticsTags(installedDependencies, latestDependencies),
...getCapgoPluginTags(options.packageJson),
},
})

if (JSON.stringify(installedDependencies) !== JSON.stringify(latestDependencies)) {
Expand Down
4 changes: 3 additions & 1 deletion cli/src/bundle/upload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import { confirmWithRememberedChoice } from '../promptPreferences'
import { showReplicationProgress } from '../replicationProgress'
import { formatTable } from '../terminal-table'
import { usesAlwaysDirectUpdate } from '../updaterConfig'
import { baseKeyV2, BROTLI_MIN_UPDATER_VERSION_V5, BROTLI_MIN_UPDATER_VERSION_V6, BROTLI_MIN_UPDATER_VERSION_V7, canPromptInteractively, checkChecksum, checkCompatibilityCloud, checkPlanValidUpload, checkRemoteCliMessages, createSupabaseClient, deletedFailedVersion, findRoot, findSavedKey, formatError, getAppId, getBundleVersion, getCompatibilityDetails, getConfig, getInstalledVersion, getLocalConfig, getLocalDependencies, getOrganizationId, getPMAndCommand, getRemoteFileConfig, hasCliPermission, hasOrganizationPerm, isCompatible, isDeprecatedPluginVersion, OrganizationPerm, regexSemver, resolveUserIdFromApiKey, sendEvent, updateConfigUpdater, updateOrCreateChannel, updateOrCreateVersion, UPLOAD_TIMEOUT, uploadTUS, uploadUrl, zipFile } from '../utils'
import { baseKeyV2, BROTLI_MIN_UPDATER_VERSION_V5, BROTLI_MIN_UPDATER_VERSION_V6, BROTLI_MIN_UPDATER_VERSION_V7, canPromptInteractively, checkChecksum, checkCompatibilityCloud, checkPlanValidUpload, checkRemoteCliMessages, createSupabaseClient, deletedFailedVersion, findRoot, findSavedKey, formatError, getAppId, getBundleVersion, getCapgoPluginTags, getCompatibilityDetails, getConfig, getInstalledVersion, getLocalConfig, getLocalDependencies, getOrganizationId, getPMAndCommand, getRemoteFileConfig, hasCliPermission, hasOrganizationPerm, isCompatible, isDeprecatedPluginVersion, OrganizationPerm, regexSemver, resolveUserIdFromApiKey, sendEvent, updateConfigUpdater, updateOrCreateChannel, updateOrCreateVersion, UPLOAD_TIMEOUT, uploadTUS, uploadUrl, zipFile } from '../utils'
import { getVersionSuggestions, interactiveVersionBump } from '../versionHelpers'
import { maybePromptBuilderCta, shouldBlockIncompatibleUpload } from './builder-cta'
import { checkIndexPosition, searchInDirectory } from './check'
Expand Down Expand Up @@ -1376,6 +1376,7 @@ export async function uploadBundleInternal(preAppid: string, options: OptionsUpl
tags: {
'app-id': appid,
'bundle': bundle,
...getCapgoPluginTags(options.packageJson),
},
notify: false,
}, options.verbose)
Expand All @@ -1389,6 +1390,7 @@ export async function uploadBundleInternal(preAppid: string, options: OptionsUpl
tags: {
'app-id': appid,
'bundle': bundle,
...getCapgoPluginTags(options.packageJson),
},
notify: false,
notifyConsole: true,
Expand Down
30 changes: 27 additions & 3 deletions cli/src/init/command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import { copyToClipboard, revealInFinder } from '../support/clipboard'
import { appendInternalLog, getInternalLogPath, startInternalLog } from '../support/internal-log'
import { showReplicationProgress } from '../replicationProgress'
import { formatRunnerCommand, splitRunnerCommand } from '../runner-command'
import { createSupabaseClient, defaultApiHost, findBuildCommandForProjectType, findMainFile, findMainFileForProjectType, findProjectType, findRoot, findSavedKey, findSavedKeySilent, formatError, getAllPackagesDependencies, getAppId, getBundleVersion, getConfig, getLocalConfig, getNativeProjectResetAdvice, getOrganizationListWithPermission, getPackageScripts, getPMAndCommand, hasCliPermission, PACKNAME, projectIsMonorepo, resolveUserIdFromApiKey, updateConfigbyKey, updateConfigUpdater, validateIosUpdaterSync } from '../utils'
import { createSupabaseClient, defaultApiHost, findBuildCommandForProjectType, findMainFile, findMainFileForProjectType, findProjectType, findRoot, findSavedKey, findSavedKeySilent, formatError, getAllPackagesDependencies, getAppId, getBundleVersion, getCapgoPluginTags, getConfig, getLocalConfig, getNativeProjectResetAdvice, getOrganizationListWithPermission, getPackageScripts, getPMAndCommand, hasCliPermission, PACKNAME, projectIsMonorepo, resolveUserIdFromApiKey, updateConfigbyKey, updateConfigUpdater, validateIosUpdaterSync } from '../utils'
import { buildAppIdConflictSuggestions, isAppAlreadyExistsError } from './app-conflict'
import { cancel as pCancel, confirm as pConfirm, intro as pIntro, isCancel as pIsCancel, log as pLog, outro as pOutro, select as pSelect, spinner as pSpinner, text as pText } from './prompts'
import { appendInitStreamingLine, clearInitStreamingOutput, setInitCodeDiff, setInitEncryptionSummary, setInitVersionWarning, startInitStreamingOutput, stopInitInkSession, updateInitStreamingStatus } from './runtime'
Expand Down Expand Up @@ -552,7 +552,11 @@ async function runInitDoctorDiagnostics(): Promise<void> {
}

async function exitCanceledInitOnboarding(orgId: string, apikey: string, message = 'You can resume the onboarding anytime by running the same command again'): Promise<never> {
await markSnag('onboarding-v2', orgId, apikey, 'canceled', undefined, '🤷')
await markSnag('onboarding-v2', orgId, apikey, 'canceled', undefined, '🤷', {
last_step: lastStepName,
elapsed_ms: Date.now() - initStartedAt,
...getCapgoPluginTags(globalPathToPackageJson),
})
pOutro(`Bye 👋\n💡 ${message}`)
exit(1)
}
Expand Down Expand Up @@ -1371,8 +1375,27 @@ async function warnIfNotInCapacitorRoot() {
}
}

// Onboarding telemetry context: step timing plus the last reached step so the
// 'canceled' event can report where and how late users abort.
let initStartedAt = Date.now()
let lastStepStartedAt = initStartedAt
let lastStepName = 'init-start'

function resetInitTelemetryContext() {
initStartedAt = Date.now()
lastStepStartedAt = initStartedAt
lastStepName = 'init-start'
}

async function markStep(orgId: string, apikey: string, step: string, appId: string) {
return markSnag('onboarding-v2', orgId, apikey, `onboarding-step-${step}`, appId)
const now = Date.now()
const elapsedMs = now - lastStepStartedAt
lastStepStartedAt = now
lastStepName = step
return markSnag('onboarding-v2', orgId, apikey, `onboarding-step-${step}`, appId, '✅', {
elapsed_ms: elapsedMs,
...getCapgoPluginTags(globalPathToPackageJson),
})
}

/**
Expand Down Expand Up @@ -4291,6 +4314,7 @@ async function maybeStarCapgoRepo(includeSkillsRepository = false, repository?:
}

export async function initApp(apikeyCommand: string, appId: string, options: SuperOptions) {
resetInitTelemetryContext()
globalSupaHost = options.supaHost // honor --supa-host for the support-logs upload
const pm = getPMAndCommand()
// Start the verbose internal log early so it captures the whole run (incl.
Expand Down
51 changes: 51 additions & 0 deletions cli/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,57 @@ export function getBundleVersion(f: string = findRoot(cwd()), file: string | und
return packageJson.version ?? ''
}

// Cached so analytics call sites read package.json at most once per process.
let cachedCapgoPackages: string[] | undefined

/**
* List the @capgo/* packages declared in the project's package.json files
* (dependencies + devDependencies), sorted alphabetically.
* Used for telemetry only: never throws, returns [] when nothing is readable.
* The first call wins the cache, later calls reuse it regardless of path.
*/
export function listCapgoPackages(packageJsonPath?: string): string[] {
if (cachedCapgoPackages)
return cachedCapgoPackages
const found = new Set<string>()
try {
const files = packageJsonPath
? packageJsonPath.split(',').map(file => file.trim()).filter(Boolean)
: [join(findRoot(cwd()), PACKNAME)]
for (const file of files) {
try {
if (!existsSync(file))
continue
const pkg = JSON.parse(readFileSync(file, 'utf-8'))
const dependencies = [...Object.keys(pkg.dependencies ?? {}), ...Object.keys(pkg.devDependencies ?? {})]
for (const dependency of dependencies) {
if (dependency.startsWith('@capgo/'))
found.add(dependency)
}
}
catch {
// Unreadable or invalid package.json: skip it, telemetry must not break commands.
}
}
}
catch {
// Root resolution failed: report no plugins instead of throwing.
}
cachedCapgoPackages = [...found].sort()
return cachedCapgoPackages
}

/**
* Plugin tags shared by key analytics events (init steps, doctor, upload).
*/
export function getCapgoPluginTags(packageJsonPath?: string): { capgo_plugins: string, capgo_plugin_count: number } {
const plugins = listCapgoPackages(packageJsonPath)
return {
capgo_plugins: plugins.join(','),
capgo_plugin_count: plugins.length,
}
}
Comment on lines +257 to +306

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Cache key ignores packageJsonPath, so telemetry can become path-order dependent.

After the first call, listCapgoPackages(...) always returns the first cached result and ignores later packageJsonPath values. That breaks the path-specific contract expected by callers (getCapgoPluginTags(options.packageJson) in Doctor/Upload) and can attach plugin tags from the wrong manifest set in long-lived processes.

💡 Suggested fix (cache by normalized path set)
-// Cached so analytics call sites read package.json at most once per process.
-let cachedCapgoPackages: string[] | undefined
+// Cache per normalized package.json path set.
+const cachedCapgoPackagesByPath = new Map<string, string[]>()

 export function listCapgoPackages(packageJsonPath?: string): string[] {
-  if (cachedCapgoPackages)
-    return cachedCapgoPackages
-  const found = new Set<string>()
+  const files = packageJsonPath
+    ? packageJsonPath.split(',').map(file => file.trim()).filter(Boolean)
+    : [join(findRoot(cwd()), PACKNAME)]
+  const normalizedFiles = files.map(file => resolve(file)).sort((a, b) => a.localeCompare(b))
+  const cacheKey = normalizedFiles.join(',')
+  const cached = cachedCapgoPackagesByPath.get(cacheKey)
+  if (cached)
+    return cached
+
+  const found = new Set<string>()

   try {
-    const files = packageJsonPath
-      ? packageJsonPath.split(',').map(file => file.trim()).filter(Boolean)
-      : [join(findRoot(cwd()), PACKNAME)]
     for (const file of files) {
       try {
         if (!existsSync(file))
           continue
         const pkg = JSON.parse(readFileSync(file, 'utf-8'))
@@
   catch {
     // Root resolution failed: report no plugins instead of throwing.
   }
-  cachedCapgoPackages = [...found].sort()
-  return cachedCapgoPackages
+  const result = [...found].sort()
+  cachedCapgoPackagesByPath.set(cacheKey, result)
+  return result
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cli/src/utils.ts` around lines 257 - 306, The cachedCapgoPackages global
currently ignores packageJsonPath so listCapgoPackages(packageJsonPath) always
returns the first result; change the cache to key by the normalized
packageJsonPath set (e.g., use a Map keyed by a deterministic string like a
sorted, comma-joined absolute paths or single path) and adjust listCapgoPackages
to compute that key from packageJsonPath (normalizing, splitting, trimming,
resolving to absolute paths) before checking/setting the cache; update
cachedCapgoPackages to the new Map type and ensure getCapgoPluginTags still
calls listCapgoPackages(packageJsonPath) unchanged so telemetry respects the
provided path(s).


function returnVersion(version: string) {
const tmpVersion = version.replace('^', '').replace('~', '')
if (canParse(tmpVersion)) {
Expand Down
28 changes: 28 additions & 0 deletions cli/test/test-doctor-analytics.mjs
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
#!/usr/bin/env node
import assert from 'node:assert/strict'
import { mkdtempSync, writeFileSync } from 'node:fs'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { computeDoctorAnalyticsTags } from '../src/app/info.ts'
import { getCapgoPluginTags, listCapgoPackages } from '../src/utils.ts'

console.log('🧪 Testing doctor analytics tags...\n')

Expand Down Expand Up @@ -31,4 +35,28 @@ assert.equal(allOutdated.is_outdated, true)
assert.equal(allOutdated.dependency_count, 2)
assert.equal(allOutdated.outdated_count, 2)

// --- capgo plugin tags helper (shared by init/doctor/upload events) ---
const dir = mkdtempSync(join(tmpdir(), 'capgo-plugin-tags-'))
const pkgPath = join(dir, 'package.json')
writeFileSync(pkgPath, JSON.stringify({
dependencies: {
'@capgo/capacitor-updater': '^7.0.0',
'@capacitor/core': '^7.0.0',
},
devDependencies: {
'@capgo/cli': '^7.0.0',
'@capgo/capacitor-social-login': '^1.0.0',
},
}))

const plugins = listCapgoPackages(pkgPath)
assert.deepEqual(plugins, ['@capgo/capacitor-social-login', '@capgo/capacitor-updater', '@capgo/cli'], 'deps + devDeps, sorted, @capgo/* only')

const pluginTags = getCapgoPluginTags(pkgPath)
assert.equal(pluginTags.capgo_plugins, '@capgo/capacitor-social-login,@capgo/capacitor-updater,@capgo/cli')
assert.equal(pluginTags.capgo_plugin_count, 3)

// The result is cached per process: later calls reuse it whatever the path.
assert.deepEqual(listCapgoPackages('/nonexistent/package.json'), plugins)

console.log('✅ doctor analytics tags tests passed')
13 changes: 13 additions & 0 deletions cli/test/test-v2-event-migration.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,19 @@ try {
assert.equal(body.user_id, undefined, 'CLI must not send user_id (backend derives the actor from the key)')
assert.deepEqual(body.tags, { 'app-id': 'com.example.app' })

// The optional tags parameter merges caller tags with the app-id tag.
await markSnag('onboarding-v2', 'org-123', 'capgo-key', 'canceled', undefined, '🤷', {
last_step: 'add-app',
elapsed_ms: 1234,
})

const eventRequests = requests.filter(request => request.url.endsWith('/private/events'))
assert.equal(eventRequests.length, 2, 'Expected one request per markSnag call')
const canceledBody = JSON.parse(eventRequests[1].init.body)
assert.equal(canceledBody.event, 'canceled')
assert.equal(canceledBody.icon, '🤷')
assert.deepEqual(canceledBody.tags, { last_step: 'add-app', elapsed_ms: 1234 })

console.log('✅ v2 event migration tests passed')
}
finally {
Expand Down
2 changes: 2 additions & 0 deletions src/components/dashboard/StepsApp.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import IconLoader from '~icons/lucide/loader-2'
import InviteTeammateModal from '~/components/dashboard/InviteTeammateModal.vue'
import { createDefaultApiKey, findUsablePlainApiKey } from '~/services/apikeys'
import { stepElapsed } from '~/services/onboardingTimer'
import { pushEvent } from '~/services/posthog'
import { getLocalConfig, isLocal, useSupabase } from '~/services/supabase'
import { sendEvent } from '~/services/tracking'
Expand Down Expand Up @@ -93,6 +94,7 @@
org_id: orgId,
tracking_version: 2,
notify: false,
tags: { step_elapsed_ms: stepElapsed() },
}).catch()
pushEvent(`user:onboarding-step-${stepToName(step.value)}`, config.supaHost, { org_id: orgId })
}
Expand Down Expand Up @@ -315,7 +317,7 @@

clearWatchers()

pollTimer.value = window.setInterval(async () => {

Check warning on line 320 in src/components/dashboard/StepsApp.vue

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Prefer `globalThis` over `window`.

See more on https://sonarcloud.io/project/issues?id=Cap-go_capgo&issues=AZ64YArOZBZf2NUk520d&open=AZ64YArOZBZf2NUk520d&pullRequest=2491
try {
const current = await getAppsCount()
if (initialCount.value !== null && current > initialCount.value) {
Expand Down
2 changes: 2 additions & 0 deletions src/components/dashboard/StepsBuild.vue
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import IconAndroid from '~icons/mdi/android'
import IconApple from '~icons/mdi/apple'
import { createDefaultApiKey, findUsablePlainApiKey } from '~/services/apikeys'
import { stepElapsed } from '~/services/onboardingTimer'
import { pushEvent } from '~/services/posthog'
import { getLocalConfig, isLocal, useSupabase } from '~/services/supabase'
import { sendEvent } from '~/services/tracking'
Expand Down Expand Up @@ -150,6 +151,7 @@
org_id: orgId,
tracking_version: 2,
notify: false,
tags: { step_elapsed_ms: stepElapsed() },
}).catch()
pushEvent(`user:onboarding-build-${stepToName(step.value)}`, config.supaHost, { org_id: orgId })
}
Expand Down Expand Up @@ -307,7 +309,7 @@

clearWatchers()

pollTimer.value = window.setInterval(async () => {

Check warning on line 312 in src/components/dashboard/StepsBuild.vue

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Prefer `globalThis` over `window`.

See more on https://sonarcloud.io/project/issues?id=Cap-go_capgo&issues=AZ64YArkZBZf2NUk520e&open=AZ64YArkZBZf2NUk520e&pullRequest=2491
try {
const current = await getBuildRequestsCount(platform)
if (initialCount.value !== null && current > initialCount.value) {
Expand Down
2 changes: 2 additions & 0 deletions src/components/dashboard/StepsBundle.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import IconLoader from '~icons/lucide/loader-2'
import InviteTeammateModal from '~/components/dashboard/InviteTeammateModal.vue'
import { createDefaultApiKey, findUsablePlainApiKey } from '~/services/apikeys'
import { stepElapsed } from '~/services/onboardingTimer'
import { pushEvent } from '~/services/posthog'
import { getLocalConfig, isLocal, useSupabase } from '~/services/supabase'
import { sendEvent } from '~/services/tracking'
Expand Down Expand Up @@ -80,6 +81,7 @@
org_id: orgId,
tracking_version: 2,
notify: false,
tags: { step_elapsed_ms: stepElapsed() },
}).catch()
pushEvent(`user:onboarding-bundle-${stepToName(step.value)}`, config.supaHost, { org_id: orgId })
}
Expand Down Expand Up @@ -242,7 +244,7 @@

clearWatchers()

pollTimer.value = window.setInterval(async () => {

Check warning on line 247 in src/components/dashboard/StepsBundle.vue

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Prefer `globalThis` over `window`.

See more on https://sonarcloud.io/project/issues?id=Cap-go_capgo&issues=AZ64YAr5ZBZf2NUk520f&open=AZ64YAr5ZBZf2NUk520f&pullRequest=2491
try {
const current = await getVersionsCount()
if (initialCount.value !== null && current > initialCount.value) {
Expand Down
3 changes: 3 additions & 0 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { setupLayouts } from 'virtual:generated-layouts'
import { createApp } from 'vue'
import { createRouter, createWebHistory } from 'vue-router'
import { routes } from 'vue-router/auto-routes'
import { captureFirstTouch } from '~/services/attribution'
import { installDeepLinkHandler } from '~/services/deepLinks'
import { posthogLoader } from '~/services/posthog'
import { getErrorMessage, isKnownCrawlerNoiseErrorMessage, isStaleAssetErrorMessage } from '~/services/staleAssetErrors'
Expand Down Expand Up @@ -187,6 +188,8 @@ router.beforeEach((to, from, next) => {

const config = getLocalConfig()
posthogLoader(config.supaHost)
// Capture first-touch attribution (UTM params, referrer) before any navigation strips them
captureFirstTouch()

// install all modules under `modules/`
type UserModule = (ctx: { app: typeof app, router: Router }) => void
Expand Down
12 changes: 11 additions & 1 deletion src/pages/login.vue
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,13 @@
import iconPassword from '~icons/ph/key?raw'
import mfaIcon from '~icons/simple-icons/2fas?raw'
import { hideLoader } from '~/services/loader'
import { autoAuth, defaultApiHost, hashEmail, useSupabase } from '~/services/supabase'
import { pushEvent } from '~/services/posthog'
import { autoAuth, defaultApiHost, getLocalConfig, hashEmail, useSupabase } from '~/services/supabase'
import { openSupport } from '~/services/support'

const route = useRoute('/login')
const supabase = useSupabase()
const config = getLocalConfig()
const isLoading = ref(false)
const isMobile = ref(Capacitor.isNativePlatform())
const turnstileToken = ref('')
Expand Down Expand Up @@ -296,6 +298,11 @@
isLoading.value = false
console.error('error', error)
setErrors('login-account', [error.message], {})
// Coarse failure reason for analytics; never include credentials
const reason = error.message.includes('Invalid login credentials')
? 'invalid_credentials'
: error.message.includes('captcha') ? 'captcha' : 'unknown'

Check warning on line 304 in src/pages/login.vue

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Extract this nested ternary operation into an independent statement.

See more on https://sonarcloud.io/project/issues?id=Cap-go_capgo&issues=AZ64YAqOZBZf2NUk520Q&open=AZ64YAqOZBZf2NUk520Q&pullRequest=2491
pushEvent('user:login-failed', config.supaHost, { reason })
if (error.message.includes('Invalid login credentials')) {
turnstileToken.value = ''
captchaComponent.value?.reset()
Expand Down Expand Up @@ -389,6 +396,7 @@

if (error) {
console.error('SSO login error', error)
pushEvent('user:login-failed', config.supaHost, { reason: error.message.includes('captcha') ? 'captcha' : 'sso_failed' })
turnstileToken.value = ''
captchaComponent.value?.reset()
if (error.message.includes('captcha')) {
Expand All @@ -407,6 +415,7 @@
}
catch (err) {
console.error('SSO login error', err)
pushEvent('user:login-failed', config.supaHost, { reason: 'sso_failed' })
turnstileToken.value = ''
captchaComponent.value?.reset()
toast.error(t('invalid-auth'))
Expand All @@ -423,6 +432,7 @@
})

if (verify.error) {
pushEvent('user:login-failed', config.supaHost, { reason: 'mfa_failed' })
toast.error(t('invalid-mfa-code'))
console.error('verify error', verify.error)
isLoading.value = false
Expand Down
Loading
Loading