登记Product Hunt候选并补充Kilo公司个案
This commit is contained in:
@@ -43,11 +43,20 @@ export type ResearchCoverage = {
|
||||
otherOrMissingYear: number; unclearAI: number; dateConflicts: number; failedProfiles: number;
|
||||
discoverySha256: string;
|
||||
};
|
||||
export type ResearchCandidateBatch = {
|
||||
id: string; sourceId: string; url: string; accessedAt: string; scope: string; complete: boolean;
|
||||
records: {
|
||||
id: string; name: string; url: string | null; categoryId: string;
|
||||
kind: "product" | "promotion"; profileStatus: "readable" | "unavailable" | "archive-only";
|
||||
status: "pending-year" | "linked" | "existing-outside-cohort" | "promotion";
|
||||
companyId?: string; note: string;
|
||||
}[];
|
||||
};
|
||||
export type ResearchSnapshot = {
|
||||
version: string; asOf: string; scope: string; limitations: string[];
|
||||
categories: ResearchCategory[]; companies: ResearchCompany[];
|
||||
observations: ResearchObservation[]; evidence: ResearchEvidence[];
|
||||
reviews: ResearchReview[]; coverage?: ResearchCoverage;
|
||||
reviews: ResearchReview[]; coverage?: ResearchCoverage; candidateBatches?: ResearchCandidateBatch[];
|
||||
};
|
||||
|
||||
export function evidenceUrl(value: string): string | undefined {
|
||||
@@ -98,6 +107,28 @@ export function validateResearch(snapshot: ResearchSnapshot) {
|
||||
!Number.isFinite(o.value))
|
||||
throw new Error(`Invalid research observation: ${o.id}`);
|
||||
}
|
||||
unique((snapshot.candidateBatches ?? []).map((b) => b.id), "candidate batches");
|
||||
unique((snapshot.candidateBatches ?? []).flatMap((b) => b.records.map((r) => r.id)), "candidates");
|
||||
for (const batch of snapshot.candidateBatches ?? []) {
|
||||
if (!/^S\d{2}$/.test(batch.sourceId) || !evidenceUrl(batch.url) || !batch.scope.trim() ||
|
||||
typeof batch.complete !== "boolean" || !/^\d{4}-\d{2}-\d{2}$/.test(batch.accessedAt) ||
|
||||
Number.isNaN(Date.parse(batch.accessedAt)) ||
|
||||
new Date(batch.accessedAt).toISOString().slice(0, 10) !== batch.accessedAt ||
|
||||
batch.accessedAt > snapshot.asOf)
|
||||
throw new Error(`Invalid research candidate batch: ${batch.id}`);
|
||||
for (const row of batch.records) {
|
||||
const linked = ["linked", "existing-outside-cohort"].includes(row.status);
|
||||
if (!row.name.trim() || !row.note.trim() || !categories.has(row.categoryId) ||
|
||||
!["product", "promotion"].includes(row.kind) ||
|
||||
!["readable", "unavailable", "archive-only"].includes(row.profileStatus) ||
|
||||
!["pending-year", "linked", "existing-outside-cohort", "promotion"].includes(row.status) ||
|
||||
(row.kind === "product" && (!row.url || !evidenceUrl(row.url))) ||
|
||||
(row.url !== null && !evidenceUrl(row.url)) ||
|
||||
linked !== !!row.companyId || (row.companyId && !companies.has(row.companyId)) ||
|
||||
(row.kind === "promotion") !== (row.status === "promotion"))
|
||||
throw new Error(`Invalid research candidate: ${row.id}`);
|
||||
}
|
||||
}
|
||||
const coverage = snapshot.coverage;
|
||||
if (coverage && (coverage.profilesRead + coverage.failedProfiles !== coverage.candidateRecords ||
|
||||
coverage.matchingProfiles !== coverage.existingProfiles + coverage.newProfiles ||
|
||||
|
||||
Reference in New Issue
Block a user