From 1f3f37c86c1abd801200aec95317c39b5d30f206 Mon Sep 17 00:00:00 2001 From: tdgao Date: Thu, 27 Aug 2026 13:39:50 -0600 Subject: [PATCH] refactor: links validator --- .../composables/project-field-validation.ts | 51 +- .../[type]/[project]/settings/license.vue | 6 +- .../pages/[type]/[project]/settings/links.vue | 151 ++-- packages/moderation/src/data/nags/links.ts | 128 +-- packages/moderation/src/index.ts | 3 +- .../src/validators/link-checks/index.ts | 848 ------------------ .../src/validators/link-checks/tests.ts | 141 --- .../src/validators/links/block-list.ts | 24 + .../src/validators/links/domain-list.ts | 45 + .../moderation/src/validators/links/index.ts | 19 + .../src/validators/links/remote-checks.ts | 119 +++ .../src/validators/links/syntax-checks.ts | 352 ++++++++ .../moderation/src/validators/links/tests.ts | 205 +++++ .../moderation/src/validators/links/types.ts | 93 ++ .../src/validators/links/validation.ts | 467 ++++++++++ .../src/validators/project-fields/index.ts | 2 +- .../src/validators/project-links/index.ts | 82 -- .../src/validators/project-links/tests.ts | 53 -- 18 files changed, 1499 insertions(+), 1290 deletions(-) delete mode 100644 packages/moderation/src/validators/link-checks/index.ts delete mode 100644 packages/moderation/src/validators/link-checks/tests.ts create mode 100644 packages/moderation/src/validators/links/block-list.ts create mode 100644 packages/moderation/src/validators/links/domain-list.ts create mode 100644 packages/moderation/src/validators/links/index.ts create mode 100644 packages/moderation/src/validators/links/remote-checks.ts create mode 100644 packages/moderation/src/validators/links/syntax-checks.ts create mode 100644 packages/moderation/src/validators/links/tests.ts create mode 100644 packages/moderation/src/validators/links/types.ts create mode 100644 packages/moderation/src/validators/links/validation.ts delete mode 100644 packages/moderation/src/validators/project-links/index.ts delete mode 100644 packages/moderation/src/validators/project-links/tests.ts diff --git a/apps/frontend/src/composables/project-field-validation.ts b/apps/frontend/src/composables/project-field-validation.ts index d317331889..217b9727f7 100644 --- a/apps/frontend/src/composables/project-field-validation.ts +++ b/apps/frontend/src/composables/project-field-validation.ts @@ -1,10 +1,9 @@ import { - checkLink, extractProjectLinks, - getLinkCheckState, type LinkCheckContext, type LinkCheckResult, type ProjectTextValidationResult, + validateLink, validateProjectDescription, validateProjectSummary, validateProjectTitle, @@ -30,6 +29,47 @@ export function useProjectSummaryValidation( return computed(() => validateProjectSummary(toValue(summary), toValue(title))) } +export function useLinkValidation(context: MaybeRefOrGetter) { + const result = ref(null) + const pending = ref(false) + let debounceTimer: ReturnType | undefined + let requestId = 0 + + watch( + () => toValue(context), + (value) => { + clearTimeout(debounceTimer) + const currentRequestId = ++requestId + result.value = null + + if (import.meta.server || !value.url) { + pending.value = false + return + } + + pending.value = true + debounceTimer = setTimeout(async () => { + try { + const validation = await validateLink(value) + if (currentRequestId === requestId) result.value = validation ?? null + } catch { + if (currentRequestId === requestId) result.value = null + } finally { + if (currentRequestId === requestId) pending.value = false + } + }, 500) + }, + { deep: true, immediate: true }, + ) + + onScopeDispose(() => { + clearTimeout(debounceTimer) + requestId++ + }) + + return { pending, result } +} + export function useProjectDescriptionValidation( description: MaybeRefOrGetter, ) { @@ -66,12 +106,11 @@ export function useProjectDescriptionValidation( })) try { - await Promise.all(contexts.map((context) => checkLink(context))) + const checks = ( + await Promise.all(contexts.map((context) => validateLink(context))) + ).filter((check): check is LinkCheckResult => check !== undefined) if (currentRequestId !== requestId) return - const checks = contexts - .map((context) => getLinkCheckState(context)) - .filter((check): check is LinkCheckResult => check !== undefined) linkValidation.value = checks.find((check) => check.severity === 'error') ?? checks.find((check) => check.severity === 'warn') ?? diff --git a/apps/frontend/src/pages/[type]/[project]/settings/license.vue b/apps/frontend/src/pages/[type]/[project]/settings/license.vue index 659ce9e2a7..ca99c64ec0 100644 --- a/apps/frontend/src/pages/[type]/[project]/settings/license.vue +++ b/apps/frontend/src/pages/[type]/[project]/settings/license.vue @@ -149,7 +149,6 @@