Open-Source Form Builder
This report summarizes scanner evidence for heyform/heyform, a TypeScript project with 9,057 GitHub stars. SynthScan examined 164,067 lines of code across 1015 source files, recording 460 pattern matches distributed across 5 syntactic categories. The legacy adjusted pattern signal is 3.3 (Low pattern signal); it is not an authorship estimate.
Use the findings to ask whether maintainers can explain intent, verify dependencies, simplify unnecessary scaffolding, and focus review effort. Risk Payload v1 adds ownership, supply-chain, over-engineering, and review-effort vectors when available. These are prompts for human verification, not probabilities, security findings, or measured maintenance costs.
Use each vector to guide manual verification of the underlying findings.
Payload schema: 1.0.0 · Engine: 2.0.0 · Scoring: heuristic-density-v1 · Scan date: 2026-10-31.
Can maintainers explain intent, assumptions, and trade-offs? 335 finding(s).
Are imports declared and package/API names verified? 93 finding(s).
Are wrappers, guards, and repeated structures necessary? 365 finding(s).
Which changes need focused human review? 460 finding(s).
This chart tracks the legacy adjusted pattern score across scan runs. Changes can reflect source changes, scan coverage, or detector/scoring versions; they do not measure AI adoption or prove that a team refactored code. Compare runs only when their scoring versions are compatible. The dashed line tracks the raw pattern-hit count.
Groups findings by configured severity weight. Severity is not a confidence estimate, security rating, or proof that a package is missing. Verify imports against the project's manifests and inspect each finding in its local design context.
This horizontal bar chart shows where configured pattern scores accumulate across directories. Treat the bars as locations to inspect, not as evidence of authorship or code quality. Use the file-level findings to decide whether a directory needs a focused ownership, dependency, complexity, or review check.
The scanner identified 460 distinct pattern matches across 5 syntactic categories. Each entry below represents a discrete location in the source code where the engine recorded a detector match to inspect in context. Expand any category row to inspect the individual file paths, line numbers, code snippets, and the lexical context (CODE, COMMENT, or STRING) in which each match was detected.
Reading the findings table: The Severity column indicates the configured finding weight (CRITICAL / HIGH / MEDIUM / LOW). The Context column identifies whether the match occurred inside executable code, an inline comment, or a string literal — legacy comment-context matches receive a ×1.5 score weight. The ⚡ bolt icon marks clustered matches: three or more patterns within a 10-line window, each receiving an additional ×1.5 density multiplier under the legacy scoring model.
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | packages/webapp/test/file-upload.test.ts | 10 | function testNormalizesCurrentAndLegacyFileValues() { | CODE |
| LOW | packages/webapp/test/file-upload.test.ts | 49 | function testSupportsSelfHostedUrlsAndRejectsUnsafeProtocols() { | CODE |
| LOW | packages/webapp/test/file-upload.test.ts | 73 | function testDashboardOnlyLinksToTrustedUploadOrigins() { | CODE |
| LOW | packages/webapp/test/file-upload.test.ts | 106 | function testOnlyTrustedStripeReceiptsAreClickable() { | CODE |
| LOW | packages/webapp/src/utils/file-upload.ts | 36 | export function isTrustedStripeReceiptUrl(value: unknown): value is string { | CODE |
| LOW | …s/webapp/src/pages/form/Settings/EmailNotification.tsx | 18 | export default function FormSettingsEmailNotification() { | CODE |
| LOW | …bapp/src/pages/form/Settings/notificationRecipients.ts | 43 | export function parseNotificationRecipients(value?: string): string[] { | CODE |
| LOW | …bapp/src/pages/form/Settings/notificationRecipients.ts | 57 | export function validateNotificationRecipients( | CODE |
| LOW | packages/webapp/src/pages/form/Integrations/utils.ts | 6 | export function getIntegrationDescription(t: TFunction, app: IntegratedAppType) { | CODE |
| LOW | packages/webapp/src/pages/form/Integrations/utils.ts | 52 | export function getIntegrationDefaultConfig(app: IntegratedAppType) { | CODE |
| LOW | packages/webapp/src/pages/form/Integrations/utils.ts | 61 | export function getIntegrationAuthorizeUrl(formId: string, appId: string) { | CODE |
| LOW | packages/webapp/src/pages/form/Builder/utils/field.ts | 17 | export function removeInvalidNextFieldIds(rawFields: FormFieldType[]): FormFieldType[] { | CODE |
| LOW | …kages/webapp/src/pages/form/Builder/RichText/index.tsx | 134 | function handleToolbarSelectionChange() { | CODE |
| LOW | …ckages/webapp/src/pages/form/Builder/RichText/utils.ts | 95 | export function getTextPrecedingAtTrigger( | CODE |
| LOW | …app/src/pages/workspace/Settings/ConnectedAccounts.tsx | 81 | export default function WorkspaceConnectedAccounts() { | CODE |
| LOW | packages/webapp/src/consts/env.ts | 57 | export function getVerifyEmailResendCooldownSeconds() { | CODE |
| LOW | packages/server/test/abuse-limits.test.ts | 7 | function testAbuseInputsAreNormalizedAndDeduplicated() { | CODE |
| LOW | packages/server/test/abuse-limits.test.ts | 21 | async function testTranslationJobsUseUserAndTeamBudgetsAndCanRetryAfterFailure() { | CODE |
| LOW | packages/server/test/redis-config.test.ts | 7 | function testRedisClientsShareTlsConfiguration() { | CODE |
| LOW | packages/server/test/redis-config.test.ts | 26 | function testRedisTlsJsonConfiguration() { | CODE |
| LOW | packages/server/test/permission.guard.test.ts | 7 | function createGraphqlExecutionContext({ | CODE |
| LOW | packages/server/test/permission.guard.test.ts | 27 | function createHttpExecutionContext({ | CODE |
| LOW | packages/server/test/permission.guard.test.ts | 81 | async function testCollaboratorCanPassTeamGuard() { | CODE |
| LOW | packages/server/test/permission.guard.test.ts | 156 | async function testHttpFormGuardReadsRequestQuery() { | CODE |
| LOW | packages/server/test/cors.test.ts | 55 | function testBuildsUniqueAllowlist() { | CODE |
| LOW | packages/server/test/cors.test.ts | 67 | function testAllowsOnlyConfiguredOrigins() { | CODE |
| LOW | packages/server/test/cors.test.ts | 76 | async function testOriginCheckerCallback() { | CODE |
| LOW | packages/server/test/cors.test.ts | 103 | async function testCorsMiddlewareDoesNotReflectDeniedOrigins() { | CODE |
| LOW | packages/server/test/ai-json.test.ts | 165 | function testJsonRepairDoesNotValidateFieldOrRichTextSchemas() { | CODE |
| LOW | packages/server/test/exception-security.test.ts | 36 | function testHttpErrorsAlwaysReceiveAResponse() { | CODE |
| LOW | packages/server/test/exception-security.test.ts | 60 | function testGraphqlFilterMasksUnexpectedErrors() { | CODE |
| LOW | packages/server/test/exception-security.test.ts | 81 | async function testGraphqlFormatterOnlyExposesClientSafeMessages() { | CODE |
| LOW | packages/server/test/integration-auth.test.ts | 429 | function testDefineAppValidatesAuthAndQueue() { | CODE |
| LOW | packages/server/test/integration-auth.test.ts | 502 | async function testAddQueueAppliesAppOptions() { | CODE |
| LOW | packages/server/test/integration-auth.test.ts | 548 | async function testHidesAppsWithoutProviderCredentials() { | CODE |
| LOW | packages/server/test/integration-auth.test.ts | 579 | async function testApiKeyIsStoredEncryptedAndNeverReturned() { | CODE |
| LOW | packages/server/test/integration-auth.test.ts | 640 | async function testMissingApiKeyFailsWithoutRetry() { | CODE |
| LOW | packages/server/test/integration-auth.test.ts | 649 | async function testUnreadableApiKeyNeedsANewKey() { | CODE |
| LOW | packages/server/test/integration-auth.test.ts | 746 | async function testStateIsBoundToUserAndSingleUse() { | CODE |
| LOW | packages/server/test/integration-auth.test.ts | 794 | async function testConnectingCreatesATeamConnection() { | CODE |
| LOW | packages/server/test/integration-auth.test.ts | 867 | async function testRejectsConnectionsThatCantBeUsed() { | CODE |
| LOW | packages/server/test/integration-auth.test.ts | 911 | async function testSameAccountSharesOneConnection() { | CODE |
| LOW | packages/server/test/integration-auth.test.ts | 985 | async function testReconnectingResumesEveryIntegrationUsingIt() { | CODE |
| LOW | packages/server/test/integration-auth.test.ts | 1031 | async function testSettingsPickOneOfTheTeamsAccounts() { | CODE |
| LOW | packages/server/test/integration-auth.test.ts | 1096 | async function testPickingAnAccountThatNeedsReconnecting() { | CODE |
| LOW | packages/server/test/integration-auth.test.ts | 1152 | async function testListsTheTeamsAccountsForAnApp() { | CODE |
| LOW | packages/server/test/integration-auth.test.ts | 1240 | async function testTeamSettingsListConnectedAccounts() { | CODE |
| LOW | packages/server/test/integration-auth.test.ts | 1304 | async function testDeletingAnIntegrationKeepsTheAccount() { | CODE |
| LOW | packages/server/test/integration-auth.test.ts | 1335 | async function testDisconnectingRevokesAndHoldsIntegrations() { | CODE |
| LOW | packages/server/test/integration-auth.test.ts | 1373 | async function testOnlyTheConnectorOrOwnerCanDisconnect() { | CODE |
| LOW | packages/server/test/integration-auth.test.ts | 1407 | async function testRevokesAnAccessTokenWithoutRefreshToken() { | CODE |
| LOW | packages/server/test/integration-auth.test.ts | 1421 | async function testFailedRevocationNeverBlocksDisconnecting() { | CODE |
| LOW | packages/server/test/integration-auth.test.ts | 1458 | async function testNothingToRevokeStillDisconnects() { | CODE |
| LOW | packages/server/test/integration-auth.test.ts | 1486 | async function testAccountUsedByAnotherWorkspaceIsNotRevoked() { | CODE |
| LOW | packages/server/test/integration-auth.test.ts | 1527 | async function testConcurrentDisconnectsStillRevokeTheGrant() { | CODE |
| LOW | packages/server/test/integration-auth.test.ts | 1552 | async function testTurningOnResendsWaitingSubmissions() { | CODE |
| LOW | packages/server/test/integration-auth.test.ts | 1573 | async function testValidTokenIsUsedWithoutRefresh() { | CODE |
| LOW | packages/server/test/integration-auth.test.ts | 1590 | async function testExpiredTokenIsRefreshed() { | CODE |
| LOW | packages/server/test/integration-auth.test.ts | 1630 | async function testRefreshMovesTokensToTheCurrentKey() { | CODE |
| LOW | packages/server/test/integration-auth.test.ts | 1657 | async function testOneRefreshServesEveryIntegration() { | CODE |
| 273 more matches not shown… | ||||
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | packages/webapp/src/locales/ja.json | 590 | "placeholder": "フォームとヘルプドキュメントを検索する", | CODE |
| LOW | packages/webapp/src/locales/ja.json | 920 | "placeholder": "例: 100xxxxxxxxxxxxx", | CODE |
| LOW | packages/webapp/src/locales/ja.json | 926 | "placeholder": "例: UA-XXXXX-Y", | CODE |
| LOW | packages/webapp/src/locales/ja.json | 979 | "placeholder": "オーディエンスを選択してください" | CODE |
| LOW | packages/webapp/src/locales/ja.json | 1140 | "placeholder": "ここにリダイレクトURLを入力してください。例:https://example.com", | CODE |
| LOW | packages/webapp/src/locales/ja.json | 1207 | "placeholder": "言語を選択" | CODE |
| LOW | packages/webapp/src/locales/ja.json | 1223 | "placeholder": "name@example.com", | CODE |
| LOW | packages/webapp/src/locales/de.json | 590 | "placeholder": "Suche nach Formularen und Hilfedokumenten", | CODE |
| LOW | packages/webapp/src/locales/de.json | 920 | "placeholder": "z.B. 100xxxxxxxxxxxxx", | CODE |
| LOW | packages/webapp/src/locales/de.json | 926 | "placeholder": "z.B. UA-XXXXX-Y", | CODE |
| LOW | packages/webapp/src/locales/de.json | 979 | "placeholder": "Wählen Sie ein Publikum aus" | CODE |
| LOW | packages/webapp/src/locales/de.json | 1140 | "placeholder": "Geben Sie hier die Weiterleitungs-URL ein, z.B. https://example.com", | CODE |
| LOW | packages/webapp/src/locales/de.json | 1211 | "placeholder": "Sprachen auswählen" | CODE |
| LOW | packages/webapp/src/locales/de.json | 1227 | "placeholder": "name@example.com", | CODE |
| LOW | packages/webapp/src/locales/pl.json | 590 | "placeholder": "Szukaj formularzy i dokumentów pomocy", | CODE |
| LOW | packages/webapp/src/locales/pl.json | 920 | "placeholder": "np. 100xxxxxxxxxxxxx", | CODE |
| LOW | packages/webapp/src/locales/pl.json | 926 | "placeholder": "np. UA-XXXXX-Y", | CODE |
| LOW | packages/webapp/src/locales/pl.json | 979 | "placeholder": "Wybierz odbiorców" | CODE |
| LOW | packages/webapp/src/locales/pl.json | 1140 | "placeholder": "Wprowadź tutaj URL przekierowania, np. https://example.com", | CODE |
| LOW | packages/webapp/src/locales/pl.json | 1207 | "placeholder": "Wybierz języki" | CODE |
| LOW | packages/webapp/src/locales/pl.json | 1223 | "placeholder": "name@example.com", | CODE |
| LOW | packages/webapp/src/locales/zh-cn.json | 590 | "placeholder": "搜索表单和帮助文档", | CODE |
| LOW | packages/webapp/src/locales/zh-cn.json | 999 | "placeholder": "例如 100xxxxxxxxxxxxx", | CODE |
| LOW | packages/webapp/src/locales/zh-cn.json | 1005 | "placeholder": "例如 UA-XXXXX-Y", | CODE |
| LOW | packages/webapp/src/locales/zh-cn.json | 1058 | "placeholder": "选择一个受众群体" | CODE |
| LOW | packages/webapp/src/locales/zh-cn.json | 1172 | "placeholder": "https://docs.google.com/spreadsheets/d/…" | CODE |
| LOW | packages/webapp/src/locales/zh-cn.json | 1177 | "placeholder": "第一个工作表" | CODE |
| LOW | packages/webapp/src/locales/zh-cn.json | 1266 | "placeholder": "在此处输入重定向 URL,例如:https://example.com", | CODE |
| LOW | packages/webapp/src/locales/zh-cn.json | 1337 | "placeholder": "选择语言" | CODE |
| LOW | packages/webapp/src/locales/zh-cn.json | 1353 | "placeholder": "name@example.com", | CODE |
| LOW | packages/webapp/src/locales/en.json | 590 | "placeholder": "Search for forms and help documents", | CODE |
| LOW | packages/webapp/src/locales/en.json | 1001 | "placeholder": "e.g. 100xxxxxxxxxxxxx", | CODE |
| LOW | packages/webapp/src/locales/en.json | 1007 | "placeholder": "e.g. UA-XXXXX-Y", | CODE |
| LOW | packages/webapp/src/locales/en.json | 1060 | "placeholder": "Select an audience" | CODE |
| LOW | packages/webapp/src/locales/en.json | 1175 | "placeholder": "https://docs.google.com/spreadsheets/d/…" | CODE |
| LOW | packages/webapp/src/locales/en.json | 1180 | "placeholder": "First tab" | CODE |
| LOW | packages/webapp/src/locales/en.json | 1269 | "placeholder": "Enter the redirect URL here, e.g., https://example.com", | CODE |
| LOW | packages/webapp/src/locales/en.json | 1340 | "placeholder": "Select languages" | CODE |
| LOW | packages/webapp/src/locales/en.json | 1356 | "placeholder": "name@example.com", | CODE |
| LOW | packages/webapp/src/locales/it.json | 590 | "placeholder": "Cerca moduli e documenti di aiuto", | CODE |
| LOW | packages/webapp/src/locales/it.json | 982 | "placeholder": "es. 100xxxxxxxxxxxxx", | CODE |
| LOW | packages/webapp/src/locales/it.json | 988 | "placeholder": "es. UA-XXXXX-Y", | CODE |
| LOW | packages/webapp/src/locales/it.json | 1041 | "placeholder": "Seleziona un pubblico" | CODE |
| LOW | packages/webapp/src/locales/it.json | 1202 | "placeholder": "Inserisci l'URL di reindirizzamento qui, es. https://esempio.com", | CODE |
| LOW | packages/webapp/src/locales/it.json | 1269 | "placeholder": "Seleziona lingue" | CODE |
| LOW | packages/webapp/src/locales/it.json | 1285 | "placeholder": "name@example.com", | CODE |
| LOW | packages/webapp/src/locales/fr.json | 590 | "placeholder": "Rechercher des formulaires et des documents d'aide", | CODE |
| LOW | packages/webapp/src/locales/fr.json | 924 | "placeholder": "ex. 100xxxxxxxxxxxxx", | CODE |
| LOW | packages/webapp/src/locales/fr.json | 930 | "placeholder": "ex. UA-XXXXX-Y", | CODE |
| LOW | packages/webapp/src/locales/fr.json | 983 | "placeholder": "Sélectionnez un public" | CODE |
| LOW | packages/webapp/src/locales/fr.json | 1144 | "placeholder": "Entrez l'URL de redirection ici, par exemple, https://example.com", | CODE |
| LOW | packages/webapp/src/locales/fr.json | 1211 | "placeholder": "Sélectionnez des langues" | CODE |
| LOW | packages/webapp/src/locales/fr.json | 1227 | "placeholder": "name@example.com", | CODE |
| LOW | packages/webapp/src/locales/pt-br.json | 590 | "placeholder": "Pesquise formulários e documentos de ajuda", | CODE |
| LOW | packages/webapp/src/locales/pt-br.json | 982 | "placeholder": "por exemplo 100xxxxxxxxxxxx", | CODE |
| LOW | packages/webapp/src/locales/pt-br.json | 988 | "placeholder": "por exemplo UA-XXXXX-Y", | CODE |
| LOW | packages/webapp/src/locales/pt-br.json | 1041 | "placeholder": "Selecione um público" | CODE |
| LOW | packages/webapp/src/locales/pt-br.json | 1202 | "placeholder": "Insira a URL de redirecionamento aqui, por exemplo, https://example.com", | CODE |
| LOW | packages/webapp/src/locales/pt-br.json | 1269 | "placeholder": "Selecione idiomas" | CODE |
| LOW | packages/webapp/src/locales/pt-br.json | 1285 | "placeholder": "name@example.com", | CODE |
| 33 more matches not shown… | ||||
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| MEDIUM | packages/server/test/e2e/auth-flows.e2e.test.ts | 28 | // ── updateUserPassword ─────────────────────────────────────────────────── | COMMENT |
| MEDIUM | packages/server/test/e2e/auth-flows.e2e.test.ts | 71 | // ── sendResetPasswordEmail + resetPassword ─────────────────────────────── | COMMENT |
| MEDIUM | packages/server/test/e2e/auth-flows.e2e.test.ts | 129 | // ── Membership-revocation edge ─────────────────────────────────────────── | COMMENT |
| MEDIUM | packages/server/test/e2e/permission-matrix.e2e.test.ts | 201 | // ── 1. Truly public — anonymous + everyone ─────────────────────────── | COMMENT |
| MEDIUM | packages/server/test/e2e/permission-matrix.e2e.test.ts | 227 | // ── 2. Authenticated, no team context ──────────────────────────────── | COMMENT |
| MEDIUM | packages/server/test/e2e/permission-matrix.e2e.test.ts | 270 | // ── 3. Team-scoped reads — team members only ───────────────────────── | COMMENT |
| MEDIUM | packages/server/test/e2e/input-validation.e2e.test.ts | 46 | // ── Sign-up & auth ─────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | packages/server/test/e2e/input-validation.e2e.test.ts | 71 | // ── Team / Workspace ───────────────────────────────────────────────────── | COMMENT |
| MEDIUM | packages/server/test/e2e/input-validation.e2e.test.ts | 103 | // ── Project ────────────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | packages/server/test/e2e/input-validation.e2e.test.ts | 118 | // ── Form ───────────────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | packages/server/test/e2e/input-validation.e2e.test.ts | 154 | // ── Submission ─────────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | packages/server/test/e2e/team-flow.e2e.test.ts | 74 | // ── Setup phase: one signup per role ────────────────────────────────────── | COMMENT |
| MEDIUM | packages/server/test/e2e/team-flow.e2e.test.ts | 145 | // ── Verification phase: admin sees roles assigned correctly ─────────────── | COMMENT |
| MEDIUM | packages/server/test/e2e/team-flow.e2e.test.ts | 171 | // ── Project + form lifecycle ────────────────────────────────────────────── | COMMENT |
| MEDIUM | packages/server/test/e2e/team-flow.e2e.test.ts | 344 | // ── Permission probes for non-admin roles ───────────────────────────────── | COMMENT |
| MEDIUM | packages/server/test/e2e/team-flow.e2e.test.ts | 393 | // ── Delete cascade: form → project → team ───────────────────────────────── | COMMENT |
| MEDIUM | packages/server/test/e2e/stateful-edges.e2e.test.ts | 35 | // ── Helpers in-suite ────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | packages/server/test/e2e/stateful-edges.e2e.test.ts | 73 | // ── Optimistic-locking / version edges ─────────────────────────────────── | COMMENT |
| MEDIUM | packages/server/test/e2e/stateful-edges.e2e.test.ts | 91 | // ── Trash lifecycle ────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | packages/server/test/e2e/stateful-edges.e2e.test.ts | 142 | // ── Cross-team data leakage ────────────────────────────────────────────── | COMMENT |
| MEDIUM | packages/server/test/e2e/stateful-edges.e2e.test.ts | 163 | // ── Invite / join edges ────────────────────────────────────────────────── | COMMENT |
| MEDIUM | packages/server/test/e2e/stateful-edges.e2e.test.ts | 211 | // ── Team membership / ownership edges ──────────────────────────────────── | COMMENT |
| MEDIUM | packages/server/test/e2e/stateful-edges.e2e.test.ts | 237 | // ── Public form / submission edges ─────────────────────────────────────── | COMMENT |
| MEDIUM | packages/server/test/e2e/rate-limit.e2e.test.ts | 21 | // ── Login attempt lockout (5 wrong / 15-min window) ────────────────────── | COMMENT |
| MEDIUM | packages/server/test/e2e/rate-limit.e2e.test.ts | 50 | // ── sendResetPasswordEmail cooldown (60s per user) ─────────────────────── | COMMENT |
| MEDIUM | packages/server/test/e2e/helpers/gql.ts | 9 | // ── Auth ──────────────────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | packages/server/test/e2e/helpers/gql.ts | 22 | // ── User ──────────────────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | packages/server/test/e2e/helpers/gql.ts | 58 | // ── Team / Workspace ──────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | packages/server/test/e2e/helpers/gql.ts | 195 | // ── Project ───────────────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | packages/server/test/e2e/helpers/gql.ts | 238 | // ── Form ──────────────────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | packages/server/test/e2e/helpers/gql.ts | 458 | // ── Submission ────────────────────────────────────────────────────────────── | COMMENT |
| MEDIUM | packages/server/test/e2e/helpers/gql.ts | 484 | // ── Catalog ───────────────────────────────────────────────────────────────── | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| HIGH | packages/server/src/utils/ai-fields.ts | 14 | // Invalid optional scalars are discarded, without coercing AI-generated values. | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | packages/server/src/apps/oauth2-providers.ts | 1 | // OAuth2 providers that `auth: { type: 'oauth2', provider }` can name. | COMMENT |