← Public packages
@kody/google
Call Gmail, Calendar, Tasks, Drive, Docs, Sheets, People, YouTube, and Analytics through saved Google OAuth.
src/accounts.ts
130 lines · 4.8 KB · TypeScriptimport { kody } from 'kody:runtime'
export const DEFAULT_GOOGLE_INTEGRATION = 'google'
export type GoogleAuthParams = {
/** Saved Kody OAuth integration name. Wins over `account`. Defaults to `google`. */
integration?: string
/** Alias for `integration`. */
integrationName?: string
/**
* Account selector. Omitted / `default` / `google` → `google`.
* Values that already start with `google` are used as-is.
* Any other value is treated as a purpose suffix (`work` → `google-work`).
*/
account?: string
}
export type GoogleAccountInfo = {
account: string
integrationName: string
label: string
purpose: string | null
useWhen: string
}
function normalizeIntegrationName(value: string): string {
return value.trim().toLowerCase().replace(/[\s_]+/g, '-')
}
function purposeFromIntegration(integrationName: string): string | null {
if (integrationName === DEFAULT_GOOGLE_INTEGRATION) return null
if (integrationName.startsWith('google-')) return integrationName.slice('google-'.length)
return integrationName
}
/**
* Resolve and describe saved Google OAuth integrations (`google` or `google-<purpose>`).
* @param integrationName - Saved OAuth integration name to describe.
* @returns Account info with label, purpose, and when-to-use guidance.
* @example
* import accounts from 'kody:@kody/google/accounts'
* const { defaultIntegration, accounts: list } = await accounts()
* // => { defaultIntegration: 'google', accounts: [...], ... }
*/
export function describeGoogleAccount(integrationName: string): GoogleAccountInfo {
const name = normalizeIntegrationName(integrationName)
const purpose = purposeFromIntegration(name)
return {
account: purpose ?? 'default',
integrationName: name,
label: purpose ? `Google (${purpose})` : 'Google (default)',
purpose,
useWhen: purpose
? `Use the saved OAuth integration named "${name}" for the "${purpose}" Google account.`
: 'Use the default saved OAuth integration named "google".',
}
}
/**
* Resolve which saved Google OAuth integration to use.
*
* Multi-account is the Kody `<provider>-<purpose>` convention, not a closed
* alias enum. Connect extra accounts as `google-work`, `google-youtube`, etc.
* and pass `integration: 'google-work'` (or `account: 'work'`).
*/
export function resolveGoogleAccount(
input: GoogleAuthParams | string = {},
): GoogleAccountInfo {
const raw: GoogleAuthParams = typeof input === 'string' ? { account: input } : input || {}
const explicit = raw.integration ?? raw.integrationName
if (explicit && explicit.trim()) return describeGoogleAccount(explicit)
const account = raw.account?.trim()
if (!account || account === 'default' || account === DEFAULT_GOOGLE_INTEGRATION) {
return describeGoogleAccount(DEFAULT_GOOGLE_INTEGRATION)
}
const normalized = normalizeIntegrationName(account)
if (normalized === DEFAULT_GOOGLE_INTEGRATION || normalized.startsWith('google-')) {
return describeGoogleAccount(normalized)
}
return describeGoogleAccount(`${DEFAULT_GOOGLE_INTEGRATION}-${normalized}`)
}
export function isGoogleIntegrationName(name: string): boolean {
return name === DEFAULT_GOOGLE_INTEGRATION || name.startsWith('google-')
}
export async function listGoogleIntegrationNames(): Promise<string[]> {
const listed = await kody.integrationList({})
const rows = Array.isArray(listed)
? listed
: Array.isArray((listed as { integrations?: unknown }).integrations)
? (listed as { integrations: unknown[] }).integrations
: []
const names = new Set<string>()
for (const row of rows) {
if (!row || typeof row !== 'object') continue
const name = (row as { name?: unknown }).name
if (typeof name === 'string' && isGoogleIntegrationName(name)) names.add(name)
}
if (names.size === 0) names.add(DEFAULT_GOOGLE_INTEGRATION)
return [...names].sort()
}
export async function listGoogleAccounts(): Promise<GoogleAccountInfo[]> {
const names = await listGoogleIntegrationNames()
return names.map((name) => describeGoogleAccount(name))
}
/**
* Describe how this package selects Google OAuth integrations.
* @returns Default integration name, connected accounts, and connect URL.
* @example
* import accounts from 'kody:@kody/google/accounts'
* const { defaultIntegration } = await accounts()
* // => 'google'
*/
export default async function accounts() {
return {
defaultIntegration: DEFAULT_GOOGLE_INTEGRATION,
naming: 'google or google-<purpose>',
accounts: await listGoogleAccounts(),
connectDefault: 'https://kody.codes/connect/oauth?provider=google',
notes: [
'Pass integrationName / integration for each saved connection. Default is "google".',
'Name extra connections google-<purpose> (google-work, google-youtube). There is no reserved brand-channel alias table.',
'Inbox reading and Drive-wide access need gmail.readonly / drive.readonly on the Google OAuth client you register.',
],
}
}