@inflexa-ai/harness 0.35.8 → 0.36.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/auth/types.d.ts +5 -14
- package/dist/auth/types.js +3 -10
- package/dist/billing/usage-recorder.d.ts +1 -1
- package/dist/contracts/analogy-report.d.ts +1 -1
- package/dist/contracts/analogy-report.js +1 -1
- package/dist/contracts/chat-parts.d.ts +2 -3
- package/dist/contracts/index.d.ts +0 -4
- package/dist/contracts/index.js +0 -2
- package/dist/index.d.ts +0 -7
- package/dist/index.js +0 -21
- package/dist/lib/metrics.d.ts +1 -1
- package/dist/lib/otel-spans.d.ts +0 -2
- package/dist/lib/otel-spans.js +0 -2
- package/dist/loop/genai-spans.d.ts +14 -0
- package/dist/loop/genai-spans.js +35 -0
- package/dist/loop/run-agent.d.ts +1 -1
- package/dist/loop/run-agent.js +37 -4
- package/dist/runtime/assemble.d.ts +0 -3
- package/dist/runtime/assemble.js +0 -3
- package/dist/state/index.d.ts +0 -2
- package/dist/state/index.js +0 -1
- package/dist/state/init.js +1 -60
- package/dist/state/purge-analysis.d.ts +1 -2
- package/dist/state/purge-analysis.js +1 -2
- package/dist/tasks/regulatory-corpus-boot-refresh.js +1 -1
- package/dist/tools/bio/gene-preclinical-profile.d.ts +1 -3
- package/dist/tools/bio/gene-preclinical-profile.js +1 -3
- package/dist/tools/lib/chembl-client.d.ts +1 -4
- package/dist/tools/lib/chembl-client.js +1 -4
- package/dist/tools/lib/clinical-benchmarks-client.d.ts +1 -1
- package/dist/tools/lib/clinical-benchmarks-client.js +1 -1
- package/dist/tools/lib/clinical-trials-client.d.ts +0 -3
- package/dist/tools/lib/clinical-trials-client.js +0 -3
- package/dist/tools/lib/identifier-resolver.d.ts +5 -7
- package/dist/tools/lib/identifier-resolver.js +5 -7
- package/dist/tools/lib/iuphar-client.d.ts +1 -3
- package/dist/tools/lib/iuphar-client.js +1 -3
- package/dist/tools/lib/opentargets-client.d.ts +1 -1
- package/dist/tools/lib/opentargets-client.js +1 -1
- package/dist/tools/lib/protein-family-normalize.d.ts +5 -7
- package/dist/tools/lib/protein-family-normalize.js +5 -7
- package/dist/tools/lib/uniprot-client.d.ts +0 -7
- package/dist/tools/lib/uniprot-client.js +2 -9
- package/package.json +1 -1
- package/dist/contracts/target-assessment-row.d.ts +0 -85
- package/dist/contracts/target-assessment-row.js +0 -37
- package/dist/contracts/target-dossier.d.ts +0 -12423
- package/dist/contracts/target-dossier.js +0 -1282
- package/dist/prompts/target-assessment/briefs/executive-recommendation.d.ts +0 -1
- package/dist/prompts/target-assessment/briefs/executive-recommendation.js +0 -85
- package/dist/prompts/target-assessment/briefs/index.d.ts +0 -4
- package/dist/prompts/target-assessment/briefs/index.js +0 -4
- package/dist/prompts/target-assessment/briefs/liability-bullets.d.ts +0 -1
- package/dist/prompts/target-assessment/briefs/liability-bullets.js +0 -114
- package/dist/prompts/target-assessment/briefs/target-organ-liabilities.d.ts +0 -1
- package/dist/prompts/target-assessment/briefs/target-organ-liabilities.js +0 -37
- package/dist/prompts/target-assessment/briefs/translational-commentary.d.ts +0 -1
- package/dist/prompts/target-assessment/briefs/translational-commentary.js +0 -23
- package/dist/prompts/target-assessment/drugs-in-class.d.ts +0 -1
- package/dist/prompts/target-assessment/drugs-in-class.js +0 -29
- package/dist/prompts/target-assessment/investigation/adversarial-critique.d.ts +0 -1
- package/dist/prompts/target-assessment/investigation/adversarial-critique.js +0 -67
- package/dist/prompts/target-assessment/investigation/claim-reverification.d.ts +0 -1
- package/dist/prompts/target-assessment/investigation/claim-reverification.js +0 -49
- package/dist/prompts/target-assessment/investigation/index.d.ts +0 -3
- package/dist/prompts/target-assessment/investigation/index.js +0 -3
- package/dist/prompts/target-assessment/investigation/mechanism-proposal.d.ts +0 -1
- package/dist/prompts/target-assessment/investigation/mechanism-proposal.js +0 -49
- package/dist/prompts/target-assessment/modulator-triage.d.ts +0 -1
- package/dist/prompts/target-assessment/modulator-triage.js +0 -31
- package/dist/prompts/target-assessment/off-target-annotator.d.ts +0 -1
- package/dist/prompts/target-assessment/off-target-annotator.js +0 -28
- package/dist/prompts/target-assessment/tox-voice/core.d.ts +0 -1
- package/dist/prompts/target-assessment/tox-voice/core.js +0 -74
- package/dist/prompts/target-assessment/tox-voice/critique.d.ts +0 -7
- package/dist/prompts/target-assessment/tox-voice/critique.js +0 -109
- package/dist/prompts/target-assessment/tox-voice/exemplars.d.ts +0 -8
- package/dist/prompts/target-assessment/tox-voice/exemplars.js +0 -24
- package/dist/prompts/target-assessment/tox-voice/index.d.ts +0 -4
- package/dist/prompts/target-assessment/tox-voice/index.js +0 -4
- package/dist/prompts/target-assessment/tox-voice/vocabulary.d.ts +0 -18
- package/dist/prompts/target-assessment/tox-voice/vocabulary.js +0 -61
- package/dist/state/target-assessments.d.ts +0 -68
- package/dist/state/target-assessments.js +0 -222
- package/dist/workflows/execute-target-assessment.d.ts +0 -118
- package/dist/workflows/execute-target-assessment.js +0 -631
- package/dist/workflows/target-assessment/assemblers/index.d.ts +0 -5
- package/dist/workflows/target-assessment/assemblers/index.js +0 -4
- package/dist/workflows/target-assessment/assemblers/literature.d.ts +0 -281
- package/dist/workflows/target-assessment/assemblers/literature.js +0 -971
- package/dist/workflows/target-assessment/assemblers/orchestrator.d.ts +0 -118
- package/dist/workflows/target-assessment/assemblers/orchestrator.js +0 -945
- package/dist/workflows/target-assessment/assemblers/safety.d.ts +0 -374
- package/dist/workflows/target-assessment/assemblers/safety.js +0 -1068
- package/dist/workflows/target-assessment/assemblers/trials.d.ts +0 -30
- package/dist/workflows/target-assessment/assemblers/trials.js +0 -75
- package/dist/workflows/target-assessment/collectors/index.d.ts +0 -114
- package/dist/workflows/target-assessment/collectors/index.js +0 -538
- package/dist/workflows/target-assessment/coverage.d.ts +0 -97
- package/dist/workflows/target-assessment/coverage.js +0 -86
- package/dist/workflows/target-assessment/decisions/index.d.ts +0 -86
- package/dist/workflows/target-assessment/decisions/index.js +0 -127
- package/dist/workflows/target-assessment/fanout/index.d.ts +0 -32
- package/dist/workflows/target-assessment/fanout/index.js +0 -156
- package/dist/workflows/target-assessment/investigation/index.d.ts +0 -97
- package/dist/workflows/target-assessment/investigation/index.js +0 -419
- package/dist/workflows/target-assessment/lib/approval-precedents.d.ts +0 -73
- package/dist/workflows/target-assessment/lib/approval-precedents.js +0 -185
- package/dist/workflows/target-assessment/lib/citation-validator.d.ts +0 -46
- package/dist/workflows/target-assessment/lib/citation-validator.js +0 -316
- package/dist/workflows/target-assessment/lib/clinical-consequence-annotator.d.ts +0 -65
- package/dist/workflows/target-assessment/lib/clinical-consequence-annotator.js +0 -174
- package/dist/workflows/target-assessment/lib/clinical-evidence-attribution.d.ts +0 -44
- package/dist/workflows/target-assessment/lib/clinical-evidence-attribution.js +0 -152
- package/dist/workflows/target-assessment/lib/compute-derived.d.ts +0 -20
- package/dist/workflows/target-assessment/lib/compute-derived.js +0 -261
- package/dist/workflows/target-assessment/lib/compute-selectivity.d.ts +0 -15
- package/dist/workflows/target-assessment/lib/compute-selectivity.js +0 -18
- package/dist/workflows/target-assessment/lib/dedup-modulators.d.ts +0 -37
- package/dist/workflows/target-assessment/lib/dedup-modulators.js +0 -157
- package/dist/workflows/target-assessment/lib/evidence-conflict-detector.d.ts +0 -17
- package/dist/workflows/target-assessment/lib/evidence-conflict-detector.js +0 -100
- package/dist/workflows/target-assessment/lib/expression-constants.d.ts +0 -21
- package/dist/workflows/target-assessment/lib/expression-constants.js +0 -21
- package/dist/workflows/target-assessment/lib/family-complex-supplement.d.ts +0 -20
- package/dist/workflows/target-assessment/lib/family-complex-supplement.js +0 -36
- package/dist/workflows/target-assessment/lib/fda-label-safety.d.ts +0 -72
- package/dist/workflows/target-assessment/lib/fda-label-safety.js +0 -166
- package/dist/workflows/target-assessment/lib/heterodimer-filter.d.ts +0 -30
- package/dist/workflows/target-assessment/lib/heterodimer-filter.js +0 -44
- package/dist/workflows/target-assessment/lib/hpo-organ-map.d.ts +0 -34
- package/dist/workflows/target-assessment/lib/hpo-organ-map.js +0 -63
- package/dist/workflows/target-assessment/lib/impc-organ-map.d.ts +0 -19
- package/dist/workflows/target-assessment/lib/impc-organ-map.js +0 -39
- package/dist/workflows/target-assessment/lib/intended-polypharm-filter.d.ts +0 -4
- package/dist/workflows/target-assessment/lib/intended-polypharm-filter.js +0 -24
- package/dist/workflows/target-assessment/lib/llm-step.d.ts +0 -80
- package/dist/workflows/target-assessment/lib/llm-step.js +0 -101
- package/dist/workflows/target-assessment/lib/meddra-organ-map.d.ts +0 -40
- package/dist/workflows/target-assessment/lib/meddra-organ-map.js +0 -138
- package/dist/workflows/target-assessment/lib/recommendation-quality-gates.d.ts +0 -17
- package/dist/workflows/target-assessment/lib/recommendation-quality-gates.js +0 -99
- package/dist/workflows/target-assessment/lib/recommendation-sanitizer.d.ts +0 -2
- package/dist/workflows/target-assessment/lib/recommendation-sanitizer.js +0 -51
- package/dist/workflows/target-assessment/lib/regulatory-actions.d.ts +0 -25
- package/dist/workflows/target-assessment/lib/regulatory-actions.js +0 -176
- package/dist/workflows/target-assessment/lib/safety-corroboration.d.ts +0 -44
- package/dist/workflows/target-assessment/lib/safety-corroboration.js +0 -273
- package/dist/workflows/target-assessment/lib/structured-llm.d.ts +0 -61
- package/dist/workflows/target-assessment/lib/structured-llm.js +0 -102
- package/dist/workflows/target-assessment/lib/target-identity-filter.d.ts +0 -59
- package/dist/workflows/target-assessment/lib/target-identity-filter.js +0 -95
- package/dist/workflows/target-assessment/lib/therapeutic-programs.d.ts +0 -20
- package/dist/workflows/target-assessment/lib/therapeutic-programs.js +0 -68
- package/dist/workflows/target-assessment/metrics.d.ts +0 -16
- package/dist/workflows/target-assessment/metrics.js +0 -30
- package/dist/workflows/target-assessment/phase4-assemble.d.ts +0 -3182
- package/dist/workflows/target-assessment/phase4-assemble.js +0 -39
- package/dist/workflows/target-assessment/phase5-persist.d.ts +0 -59
- package/dist/workflows/target-assessment/phase5-persist.js +0 -349
- package/dist/workflows/target-assessment/progress-stream.d.ts +0 -75
- package/dist/workflows/target-assessment/progress-stream.js +0 -110
- package/dist/workflows/target-assessment/progress.d.ts +0 -39
- package/dist/workflows/target-assessment/progress.js +0 -77
- package/dist/workflows/target-assessment/schemas.d.ts +0 -970
- package/dist/workflows/target-assessment/schemas.js +0 -318
- package/dist/workflows/target-assessment/steps/collectors/pathways.d.ts +0 -15
- package/dist/workflows/target-assessment/steps/collectors/pathways.js +0 -58
- package/dist/workflows/target-assessment/steps/fanout/aes-for-one-class-drug-step.d.ts +0 -34
- package/dist/workflows/target-assessment/steps/fanout/aes-for-one-class-drug-step.js +0 -31
- package/dist/workflows/target-assessment/steps/fanout/aes-for-one-trial-step.d.ts +0 -57
- package/dist/workflows/target-assessment/steps/fanout/aes-for-one-trial-step.js +0 -54
- package/dist/workflows/target-assessment/steps/fanout/faers-for-one-modulator-step.d.ts +0 -35
- package/dist/workflows/target-assessment/steps/fanout/faers-for-one-modulator-step.js +0 -32
- package/dist/workflows/target-assessment/steps/fanout/index.d.ts +0 -14
- package/dist/workflows/target-assessment/steps/fanout/index.js +0 -10
- package/dist/workflows/target-assessment/steps/fanout/polypharm-for-one-modulator-step.d.ts +0 -29
- package/dist/workflows/target-assessment/steps/fanout/polypharm-for-one-modulator-step.js +0 -29
- package/dist/workflows/target-assessment/steps/phase2-aggregate.d.ts +0 -657
- package/dist/workflows/target-assessment/steps/phase2-aggregate.js +0 -34
- package/dist/workflows/target-assessment/steps/phase3-aggregate.d.ts +0 -806
- package/dist/workflows/target-assessment/steps/phase3-aggregate.js +0 -16
- package/dist/workflows/target-assessment/steps/synthesis/diagnostics.d.ts +0 -28
- package/dist/workflows/target-assessment/steps/synthesis/diagnostics.js +0 -27
- package/dist/workflows/target-assessment/sub-workflows/index.d.ts +0 -15
- package/dist/workflows/target-assessment/sub-workflows/index.js +0 -11
- package/dist/workflows/target-assessment/sub-workflows/per-class-drug-aes.d.ts +0 -41
- package/dist/workflows/target-assessment/sub-workflows/per-class-drug-aes.js +0 -12
- package/dist/workflows/target-assessment/sub-workflows/per-modulator-faers.d.ts +0 -41
- package/dist/workflows/target-assessment/sub-workflows/per-modulator-faers.js +0 -12
- package/dist/workflows/target-assessment/sub-workflows/per-modulator-polypharm.d.ts +0 -37
- package/dist/workflows/target-assessment/sub-workflows/per-modulator-polypharm.js +0 -12
- package/dist/workflows/target-assessment/sub-workflows/per-trial-aes.d.ts +0 -66
- package/dist/workflows/target-assessment/sub-workflows/per-trial-aes.js +0 -12
- package/dist/workflows/target-assessment/synthesis/index.d.ts +0 -607
- package/dist/workflows/target-assessment/synthesis/index.js +0 -354
- package/dist/workflows/target-assessment/synthesis-probes.d.ts +0 -56
- package/dist/workflows/target-assessment/synthesis-probes.js +0 -320
|
@@ -1,538 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Phase-1 collector functions for the harness DBOS workflow.
|
|
3
|
-
*
|
|
4
|
-
* Each collector is a pure async function `(input: ResolvedTarget) =>
|
|
5
|
-
* Promise<CoverageBundle>`. Bodies: try/catch wraps a `withHost`-
|
|
6
|
-
* guarded tool-client call (or a couple in parallel) and returns
|
|
7
|
-
* `{coverage: "available", data}` on success or `{coverage: "queried_no_data",
|
|
8
|
-
* error}` on failure. A handful of collectors return
|
|
9
|
-
* `{coverage: "not_loaded", reason}` when a required upstream identifier is
|
|
10
|
-
* missing — that signals "we never tried" vs. "we tried and got nothing."
|
|
11
|
-
*
|
|
12
|
-
* No collector throws — coverage is the failure mode. The DBOS workflow body
|
|
13
|
-
* wraps each call in `DBOS.runStep({name: "ta-collector:{collectorId}"})` so
|
|
14
|
-
* recovery on a fresh replica replays the cached coverage envelope instead
|
|
15
|
-
* of re-issuing the HTTP call.
|
|
16
|
-
*
|
|
17
|
-
* No `deps` parameter is threaded through today (collectors call the tool
|
|
18
|
-
* clients directly) — every external request goes through the global
|
|
19
|
-
* `withHost(...)` semaphore. The function signature reserves the future
|
|
20
|
-
* `deps` slot for test injection without churning callers.
|
|
21
|
-
*/
|
|
22
|
-
import { createNoopLogger } from "../../../lib/console-logger.js";
|
|
23
|
-
import { withHost } from "../../../lib/host-concurrency.js";
|
|
24
|
-
import { getMultiSpeciesExpression } from "../../../tools/lib/bgee-client.js";
|
|
25
|
-
import { getSomaticMutationFrequencies } from "../../../tools/lib/cbioportal-client.js";
|
|
26
|
-
import { getModulatorsViaActivity, getTargetModulators } from "../../../tools/lib/chembl-client.js";
|
|
27
|
-
import { searchFailedTrials, searchTrialsForTarget } from "../../../tools/lib/clinical-trials-client.js";
|
|
28
|
-
import { filterInformative, searchClinvar } from "../../../tools/lib/clinvar-client.js";
|
|
29
|
-
import { getKoPhenotypeProfile } from "../../../tools/lib/impc-client.js";
|
|
30
|
-
import { getFamilyHeterodimers } from "../../../tools/lib/iuphar-client.js";
|
|
31
|
-
import { getGenePhenotypeProfile } from "../../../tools/lib/monarch-client.js";
|
|
32
|
-
import { getFaersByDrug, getFaersSeriousness } from "../../../tools/lib/openfda-client.js";
|
|
33
|
-
import { getBaselineExpression, getTargetSafetyLiabilities, searchTargetAssociations } from "../../../tools/lib/opentargets-client.js";
|
|
34
|
-
import { getPathwayMemberships } from "../../../tools/lib/pathway-client.js";
|
|
35
|
-
import { searchPubmed } from "../../../tools/lib/pubmed-client.js";
|
|
36
|
-
import { getInteractionPartners } from "../../../tools/lib/string-client.js";
|
|
37
|
-
import { findTherapeuticProgramsForTarget } from "../lib/therapeutic-programs.js";
|
|
38
|
-
import { dedupPathwaysAcrossSpecies } from "../steps/collectors/pathways.js";
|
|
39
|
-
function fail(err) {
|
|
40
|
-
return {
|
|
41
|
-
coverage: "queried_no_data",
|
|
42
|
-
error: { message: err instanceof Error ? err.message : String(err) },
|
|
43
|
-
};
|
|
44
|
-
}
|
|
45
|
-
// ── 1. cBioPortal ────────────────────────────────────────────────────
|
|
46
|
-
export async function collectCbioportal(input) {
|
|
47
|
-
try {
|
|
48
|
-
const result = await withHost("cbioportal", () => getSomaticMutationFrequencies(input.geneSymbol));
|
|
49
|
-
if (result.entrezGeneId == null) {
|
|
50
|
-
return {
|
|
51
|
-
coverage: "queried_no_data",
|
|
52
|
-
error: {
|
|
53
|
-
message: `cBioPortal could not resolve Entrez id for ${input.geneSymbol}`,
|
|
54
|
-
},
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
return { coverage: "available", data: result };
|
|
58
|
-
}
|
|
59
|
-
catch (err) {
|
|
60
|
-
return fail(err);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
// ── 2. ChEMBL modulators ─────────────────────────────────────────────
|
|
64
|
-
export async function collectChemblModulators(input, ctx) {
|
|
65
|
-
const chemblId = input.ids.chembl;
|
|
66
|
-
if (!chemblId) {
|
|
67
|
-
return { coverage: "not_loaded", reason: "no ChEMBL target id resolved" };
|
|
68
|
-
}
|
|
69
|
-
try {
|
|
70
|
-
const primaryRows = await withHost("chembl", () => getTargetModulators(chemblId, { minPhase: 2, limit: 200 }, ctx.logger));
|
|
71
|
-
const activityRows = await withHost("chembl", () => getModulatorsViaActivity({
|
|
72
|
-
target_chembl_id: chemblId,
|
|
73
|
-
max_phase_min: 2,
|
|
74
|
-
molecule_type: "Small molecule",
|
|
75
|
-
}, ctx.logger));
|
|
76
|
-
const primaryIds = new Set(primaryRows.map((m) => m.moleculeChemblId));
|
|
77
|
-
const novelActivityRows = activityRows
|
|
78
|
-
.filter((m) => !primaryIds.has(m.moleculeChemblId))
|
|
79
|
-
.map((m) => ({ ...m, evidence_source: "chembl_activity" }));
|
|
80
|
-
const modulators = [...primaryRows, ...novelActivityRows];
|
|
81
|
-
return {
|
|
82
|
-
coverage: "available",
|
|
83
|
-
data: { targetChemblId: chemblId, modulators },
|
|
84
|
-
};
|
|
85
|
-
}
|
|
86
|
-
catch (err) {
|
|
87
|
-
return fail(err);
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
// ── 3. ClinVar ───────────────────────────────────────────────────────
|
|
91
|
-
export async function collectClinvar(input, ctx) {
|
|
92
|
-
try {
|
|
93
|
-
const result = await withHost("ncbi", () => searchClinvar(ctx.ncbiApiKey, input.geneSymbol, { limit: 200 }));
|
|
94
|
-
const filtered = filterInformative(result.variants);
|
|
95
|
-
return {
|
|
96
|
-
coverage: "available",
|
|
97
|
-
data: {
|
|
98
|
-
totalFound: result.totalFound,
|
|
99
|
-
variants: filtered.map((v) => ({
|
|
100
|
-
variationId: v.variationId,
|
|
101
|
-
title: v.title,
|
|
102
|
-
clinicalSignificance: v.clinicalSignificance,
|
|
103
|
-
reviewStatus: v.reviewStatus,
|
|
104
|
-
conditions: v.conditions,
|
|
105
|
-
molecularConsequence: v.molecularConsequence,
|
|
106
|
-
accession: v.accession,
|
|
107
|
-
})),
|
|
108
|
-
},
|
|
109
|
-
};
|
|
110
|
-
}
|
|
111
|
-
catch (err) {
|
|
112
|
-
return fail(err);
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
// ── 4. ClinicalTrials.gov ────────────────────────────────────────────
|
|
116
|
-
function tagTrials(trials, collection_query, collection_channel) {
|
|
117
|
-
return trials.map((trial) => ({
|
|
118
|
-
...trial,
|
|
119
|
-
collection_query,
|
|
120
|
-
collection_channel,
|
|
121
|
-
}));
|
|
122
|
-
}
|
|
123
|
-
export async function collectCtgov(input) {
|
|
124
|
-
try {
|
|
125
|
-
const [active, failed] = await Promise.all([
|
|
126
|
-
withHost("ctgov", () => searchTrialsForTarget(input.geneSymbol, { limit: 100 })),
|
|
127
|
-
withHost("ctgov", () => searchFailedTrials(input.geneSymbol, 100)),
|
|
128
|
-
]);
|
|
129
|
-
return {
|
|
130
|
-
coverage: "available",
|
|
131
|
-
data: {
|
|
132
|
-
active: tagTrials(active.trials, input.geneSymbol, "target_symbol"),
|
|
133
|
-
failed: tagTrials(failed.trials, input.geneSymbol, "failed_intervention"),
|
|
134
|
-
},
|
|
135
|
-
};
|
|
136
|
-
}
|
|
137
|
-
catch (err) {
|
|
138
|
-
return fail(err);
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
// ── 5. Expression — human (Open Targets baseline) ────────────────────
|
|
142
|
-
export async function collectExpressionHuman(input) {
|
|
143
|
-
const ensemblId = input.ids.ensembl;
|
|
144
|
-
if (!ensemblId) {
|
|
145
|
-
return { coverage: "not_loaded", reason: "no Ensembl id resolved" };
|
|
146
|
-
}
|
|
147
|
-
try {
|
|
148
|
-
const expressions = await withHost("opentargets", () => getBaselineExpression(ensemblId));
|
|
149
|
-
if (expressions.length === 0) {
|
|
150
|
-
return {
|
|
151
|
-
coverage: "queried_no_data",
|
|
152
|
-
error: {
|
|
153
|
-
message: `Open Targets returned no baseline expression for ${ensemblId}`,
|
|
154
|
-
},
|
|
155
|
-
};
|
|
156
|
-
}
|
|
157
|
-
// `baselineExpression` mixes datasources under one row type, and each one
|
|
158
|
-
// carries its own unit: GTEx gives bulk-tissue TPM, Tabula Sapiens gives
|
|
159
|
-
// single-cell CPM pseudobulk, and DICE gives immune-cell TPM. The bundle
|
|
160
|
-
// declares one source and one unit, thus it can hold rows of one datasource
|
|
161
|
-
// only. Tabula Sapiens also outnumbers GTEx about 20 to 1, thus an unfiltered
|
|
162
|
-
// list drowns the tissue table in cell types.
|
|
163
|
-
const gtex = expressions.filter((e) => e.datasourceId === "gtex");
|
|
164
|
-
if (gtex.length === 0) {
|
|
165
|
-
return {
|
|
166
|
-
coverage: "queried_no_data",
|
|
167
|
-
error: {
|
|
168
|
-
message: `Open Targets carries no GTEx baseline expression for ${ensemblId} (${expressions.length} rows of other datasources dropped)`,
|
|
169
|
-
},
|
|
170
|
-
};
|
|
171
|
-
}
|
|
172
|
-
const tissues = gtex.map((e) => ({
|
|
173
|
-
tissueLabel: e.tissueLabel,
|
|
174
|
-
organSystem: e.organSystem,
|
|
175
|
-
value: e.rna?.value ?? null,
|
|
176
|
-
}));
|
|
177
|
-
return {
|
|
178
|
-
coverage: "available",
|
|
179
|
-
data: {
|
|
180
|
-
source: "gtex",
|
|
181
|
-
unit: "tpm",
|
|
182
|
-
normalization_notes: "Open Targets baseline expression, GTEx bulk-tissue rows only, as the median TPM of each tissue",
|
|
183
|
-
tissues,
|
|
184
|
-
},
|
|
185
|
-
};
|
|
186
|
-
}
|
|
187
|
-
catch (err) {
|
|
188
|
-
return fail(err);
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
// ── 6. Expression — multi-species (Bgee) ─────────────────────────────
|
|
192
|
-
export async function collectExpressionMultiSpecies(input) {
|
|
193
|
-
try {
|
|
194
|
-
const result = await withHost("bgee", () => getMultiSpeciesExpression(input.geneSymbol));
|
|
195
|
-
if (!result.humanEnsemblId) {
|
|
196
|
-
return {
|
|
197
|
-
coverage: "queried_no_data",
|
|
198
|
-
error: {
|
|
199
|
-
message: `Bgee could not resolve Ensembl id for ${input.geneSymbol}`,
|
|
200
|
-
},
|
|
201
|
-
};
|
|
202
|
-
}
|
|
203
|
-
return {
|
|
204
|
-
coverage: "available",
|
|
205
|
-
data: {
|
|
206
|
-
geneSymbol: result.geneSymbol,
|
|
207
|
-
humanEnsemblId: result.humanEnsemblId,
|
|
208
|
-
bySpecies: result.bySpecies.map((s) => ({
|
|
209
|
-
species: s.species,
|
|
210
|
-
taxonId: s.taxonId,
|
|
211
|
-
ensemblId: s.ensemblId,
|
|
212
|
-
source: "bgee",
|
|
213
|
-
unit: "expression_score",
|
|
214
|
-
normalization_notes: "Bgee expression score (0–100) bucketed into absent/low/medium/high; not a transcript-abundance unit",
|
|
215
|
-
tissues: s.tissues.map((t) => ({
|
|
216
|
-
tissue: t.tissue,
|
|
217
|
-
rank: t.rank,
|
|
218
|
-
expressionScore: t.expressionScore,
|
|
219
|
-
})),
|
|
220
|
-
})),
|
|
221
|
-
notFound: result.notFound,
|
|
222
|
-
},
|
|
223
|
-
};
|
|
224
|
-
}
|
|
225
|
-
catch (err) {
|
|
226
|
-
return fail(err);
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
// ── 7. FAERS by target ───────────────────────────────────────────────
|
|
230
|
-
export async function collectFaersByTarget(input) {
|
|
231
|
-
const probe = input.geneSymbol;
|
|
232
|
-
try {
|
|
233
|
-
const [byDrug, seriousness] = await Promise.all([
|
|
234
|
-
withHost("openfda", () => getFaersByDrug(probe, { limit: 25 })),
|
|
235
|
-
withHost("openfda", () => getFaersSeriousness(probe)),
|
|
236
|
-
]);
|
|
237
|
-
const total = byDrug.totalReports ?? 0;
|
|
238
|
-
if (total === 0 && byDrug.adverseEvents.length === 0) {
|
|
239
|
-
return {
|
|
240
|
-
coverage: "queried_no_data",
|
|
241
|
-
error: { message: `FAERS returned no reports for symbol ${probe}` },
|
|
242
|
-
};
|
|
243
|
-
}
|
|
244
|
-
return {
|
|
245
|
-
coverage: "available",
|
|
246
|
-
data: {
|
|
247
|
-
drugProbed: probe,
|
|
248
|
-
totalReports: byDrug.totalReports ?? null,
|
|
249
|
-
topReactions: byDrug.adverseEvents,
|
|
250
|
-
seriousness,
|
|
251
|
-
},
|
|
252
|
-
};
|
|
253
|
-
}
|
|
254
|
-
catch (err) {
|
|
255
|
-
return fail(err);
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
// ── 8. Family complexes (IUPHAR) ─────────────────────────────────────
|
|
259
|
-
export async function collectFamilyComplexes(input, ctx) {
|
|
260
|
-
const geneSymbol = input.geneSymbol;
|
|
261
|
-
const uniprot = input.ids.uniprot;
|
|
262
|
-
const lookup = uniprot ?? geneSymbol;
|
|
263
|
-
if (!lookup) {
|
|
264
|
-
return {
|
|
265
|
-
coverage: "not_loaded",
|
|
266
|
-
reason: "no gene symbol or UniProt accession",
|
|
267
|
-
};
|
|
268
|
-
}
|
|
269
|
-
try {
|
|
270
|
-
const heterodimers = await withHost("iuphar", () => getFamilyHeterodimers(lookup));
|
|
271
|
-
if (heterodimers.length === 0) {
|
|
272
|
-
return {
|
|
273
|
-
coverage: "queried_no_data",
|
|
274
|
-
error: {
|
|
275
|
-
message: `IUPHAR returned no heterodimer complexes for ${lookup}`,
|
|
276
|
-
},
|
|
277
|
-
};
|
|
278
|
-
}
|
|
279
|
-
const accessorySet = new Set();
|
|
280
|
-
const complexes = heterodimers.map((h) => {
|
|
281
|
-
const accessoryNames = h.accessories.map((a) => a.name).filter(Boolean);
|
|
282
|
-
for (const n of accessoryNames)
|
|
283
|
-
accessorySet.add(n);
|
|
284
|
-
return {
|
|
285
|
-
complexName: h.complex.name,
|
|
286
|
-
complexId: h.complex.targetId,
|
|
287
|
-
accessoryNames,
|
|
288
|
-
subunitNames: h.subunits.map((s) => s.name).filter(Boolean),
|
|
289
|
-
};
|
|
290
|
-
});
|
|
291
|
-
return {
|
|
292
|
-
coverage: "available",
|
|
293
|
-
data: {
|
|
294
|
-
primaryTargetGene: geneSymbol,
|
|
295
|
-
primaryTargetUniprot: uniprot,
|
|
296
|
-
accessoryProteinNames: [...accessorySet].sort(),
|
|
297
|
-
complexes,
|
|
298
|
-
},
|
|
299
|
-
};
|
|
300
|
-
}
|
|
301
|
-
catch (err) {
|
|
302
|
-
// `getFamilyHeterodimers` answers an unknown target with an empty list, thus
|
|
303
|
-
// a throw here is always an unexpected cause. The IUPHAR outage of 2026 hid
|
|
304
|
-
// behind this envelope for months, because `queried_no_data` reads as a
|
|
305
|
-
// clean miss to every consumer of the dossier.
|
|
306
|
-
const log = (ctx.logger ?? createNoopLogger()).named("ta-collector.family-complexes");
|
|
307
|
-
log.error("IUPHAR family-complex lookup failed", { lookup, ...log.errorFields(err) });
|
|
308
|
-
return fail(err);
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
// ── 9. IMPC ───────────────────────────────────────────────────────────
|
|
312
|
-
export async function collectImpc(input) {
|
|
313
|
-
try {
|
|
314
|
-
const profile = await withHost("ncbi", () => getKoPhenotypeProfile(input.geneSymbol));
|
|
315
|
-
if (!profile.mouseMarkerSymbol) {
|
|
316
|
-
return {
|
|
317
|
-
coverage: "queried_no_data",
|
|
318
|
-
error: {
|
|
319
|
-
message: `IMPC has no mouse marker for ${input.geneSymbol}`,
|
|
320
|
-
},
|
|
321
|
-
};
|
|
322
|
-
}
|
|
323
|
-
return {
|
|
324
|
-
coverage: "available",
|
|
325
|
-
data: {
|
|
326
|
-
mouseMarkerSymbol: profile.mouseMarkerSymbol,
|
|
327
|
-
mgiAccessionId: profile.mgiAccessionId,
|
|
328
|
-
viability: profile.viability,
|
|
329
|
-
viabilityCalls: profile.viabilityCalls,
|
|
330
|
-
mpTerms: profile.mpTerms,
|
|
331
|
-
organSystems: profile.organSystems,
|
|
332
|
-
sexDimorphic: profile.sexDimorphic,
|
|
333
|
-
phenotypeCount: profile.phenotypeCount,
|
|
334
|
-
},
|
|
335
|
-
};
|
|
336
|
-
}
|
|
337
|
-
catch (err) {
|
|
338
|
-
return fail(err);
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
// ── 10. Monarch Initiative ───────────────────────────────────────────
|
|
342
|
-
export async function collectMonarch(input) {
|
|
343
|
-
// Monarch resolves prefixed identifiers; HGNC is its native gene namespace
|
|
344
|
-
// and NCBI Gene is the fallback it also indexes.
|
|
345
|
-
const geneCurie = input.ids.hgnc ?? (input.ids.entrez ? `NCBIGene:${input.ids.entrez}` : null);
|
|
346
|
-
if (!geneCurie) {
|
|
347
|
-
return {
|
|
348
|
-
coverage: "not_loaded",
|
|
349
|
-
reason: "no HGNC or NCBI Gene id resolved",
|
|
350
|
-
};
|
|
351
|
-
}
|
|
352
|
-
try {
|
|
353
|
-
const profile = await withHost("monarch", () => getGenePhenotypeProfile(geneCurie));
|
|
354
|
-
if (profile.phenotypes.length === 0) {
|
|
355
|
-
return {
|
|
356
|
-
coverage: "queried_no_data",
|
|
357
|
-
error: {
|
|
358
|
-
message: `Monarch holds no human phenotype associations for ${geneCurie}`,
|
|
359
|
-
},
|
|
360
|
-
};
|
|
361
|
-
}
|
|
362
|
-
return {
|
|
363
|
-
coverage: "available",
|
|
364
|
-
data: {
|
|
365
|
-
geneCurie: profile.geneCurie,
|
|
366
|
-
phenotypes: profile.phenotypes.map((p) => ({
|
|
367
|
-
hpoId: p.hpoId,
|
|
368
|
-
label: p.label,
|
|
369
|
-
ancestorIds: p.ancestorIds,
|
|
370
|
-
publications: p.publications,
|
|
371
|
-
diseaseContext: p.diseaseContext,
|
|
372
|
-
frequencyPercent: p.frequencyPercent,
|
|
373
|
-
primaryKnowledgeSource: p.primaryKnowledgeSource,
|
|
374
|
-
})),
|
|
375
|
-
},
|
|
376
|
-
};
|
|
377
|
-
}
|
|
378
|
-
catch (err) {
|
|
379
|
-
return fail(err);
|
|
380
|
-
}
|
|
381
|
-
}
|
|
382
|
-
// ── 11. Open Targets ─────────────────────────────────────────────────
|
|
383
|
-
export async function collectOpenTargets(input) {
|
|
384
|
-
const ensemblId = input.ids.ensembl;
|
|
385
|
-
if (!ensemblId) {
|
|
386
|
-
return { coverage: "not_loaded", reason: "no Ensembl id resolved" };
|
|
387
|
-
}
|
|
388
|
-
try {
|
|
389
|
-
const [info, safety, expression] = await Promise.all([
|
|
390
|
-
withHost("opentargets", () => searchTargetAssociations(ensemblId, 50)),
|
|
391
|
-
withHost("opentargets", () => getTargetSafetyLiabilities(ensemblId)),
|
|
392
|
-
withHost("opentargets", () => getBaselineExpression(ensemblId)),
|
|
393
|
-
]);
|
|
394
|
-
if (!info) {
|
|
395
|
-
return {
|
|
396
|
-
coverage: "queried_no_data",
|
|
397
|
-
error: { message: `no Open Targets entry for ${ensemblId}` },
|
|
398
|
-
};
|
|
399
|
-
}
|
|
400
|
-
return {
|
|
401
|
-
coverage: "available",
|
|
402
|
-
data: {
|
|
403
|
-
ensemblId: info.ensemblId,
|
|
404
|
-
approvedSymbol: info.approvedSymbol,
|
|
405
|
-
approvedName: info.approvedName,
|
|
406
|
-
tractability: info.tractability,
|
|
407
|
-
associations: info.associations,
|
|
408
|
-
safetyLiabilities: safety?.safetyLiabilities ?? [],
|
|
409
|
-
baselineExpression: expression.map((e) => ({
|
|
410
|
-
tissueId: e.tissueId,
|
|
411
|
-
tissueLabel: e.tissueLabel,
|
|
412
|
-
organSystem: e.organSystem,
|
|
413
|
-
datasourceId: e.datasourceId,
|
|
414
|
-
rna: e.rna,
|
|
415
|
-
})),
|
|
416
|
-
},
|
|
417
|
-
};
|
|
418
|
-
}
|
|
419
|
-
catch (err) {
|
|
420
|
-
return fail(err);
|
|
421
|
-
}
|
|
422
|
-
}
|
|
423
|
-
// ── 12. Pathways (Reactome + KEGG via the unified client) ────────────
|
|
424
|
-
export async function collectPathways(input) {
|
|
425
|
-
try {
|
|
426
|
-
const raw = await withHost("reactome", () => getPathwayMemberships(input.geneSymbol));
|
|
427
|
-
const rows = raw.map((p) => ({
|
|
428
|
-
id: p.id,
|
|
429
|
-
name: p.name,
|
|
430
|
-
source: p.source,
|
|
431
|
-
url: p.url ?? "",
|
|
432
|
-
entity_uniprots: p.entity_uniprots,
|
|
433
|
-
}));
|
|
434
|
-
const cleaned = dedupPathwaysAcrossSpecies(rows, {
|
|
435
|
-
geneSymbolEchoFilter: input.geneSymbol,
|
|
436
|
-
assessmentUniprot: input.ids.uniprot ?? undefined,
|
|
437
|
-
});
|
|
438
|
-
if (cleaned.length === 0) {
|
|
439
|
-
return {
|
|
440
|
-
coverage: "queried_no_data",
|
|
441
|
-
error: {
|
|
442
|
-
message: `KEGG and Reactome returned no pathways for ${input.geneSymbol}`,
|
|
443
|
-
},
|
|
444
|
-
};
|
|
445
|
-
}
|
|
446
|
-
return { coverage: "available", data: { pathways: cleaned } };
|
|
447
|
-
}
|
|
448
|
-
catch (err) {
|
|
449
|
-
return fail(err);
|
|
450
|
-
}
|
|
451
|
-
}
|
|
452
|
-
// ── 13. PubMed index ─────────────────────────────────────────────────
|
|
453
|
-
export async function collectPubmedIndex(input, ctx) {
|
|
454
|
-
const query = `"${input.geneSymbol}"[Gene]`;
|
|
455
|
-
try {
|
|
456
|
-
const result = await withHost("ncbi", () => searchPubmed(ctx.ncbiApiKey, query, {
|
|
457
|
-
maxResults: 50,
|
|
458
|
-
sort: "relevance",
|
|
459
|
-
}));
|
|
460
|
-
if (result.totalFound === 0) {
|
|
461
|
-
return {
|
|
462
|
-
coverage: "queried_no_data",
|
|
463
|
-
error: { message: `PubMed returned no hits for ${query}` },
|
|
464
|
-
};
|
|
465
|
-
}
|
|
466
|
-
return {
|
|
467
|
-
coverage: "available",
|
|
468
|
-
data: {
|
|
469
|
-
totalFound: result.totalFound,
|
|
470
|
-
topPmids: result.results.map((r) => r.pmid),
|
|
471
|
-
results: result.results,
|
|
472
|
-
},
|
|
473
|
-
};
|
|
474
|
-
}
|
|
475
|
-
catch (err) {
|
|
476
|
-
return fail(err);
|
|
477
|
-
}
|
|
478
|
-
}
|
|
479
|
-
// ── 14. STRING PPI ───────────────────────────────────────────────────
|
|
480
|
-
export async function collectStringPpi(input) {
|
|
481
|
-
try {
|
|
482
|
-
const partners = await withHost("string", () => getInteractionPartners([input.geneSymbol], {
|
|
483
|
-
species: 9606,
|
|
484
|
-
minScore: 400,
|
|
485
|
-
limit: 50,
|
|
486
|
-
}));
|
|
487
|
-
if (partners.length === 0) {
|
|
488
|
-
return {
|
|
489
|
-
coverage: "queried_no_data",
|
|
490
|
-
error: {
|
|
491
|
-
message: `STRING returned no partners for ${input.geneSymbol}`,
|
|
492
|
-
},
|
|
493
|
-
};
|
|
494
|
-
}
|
|
495
|
-
return { coverage: "available", data: { partners } };
|
|
496
|
-
}
|
|
497
|
-
catch (err) {
|
|
498
|
-
return fail(err);
|
|
499
|
-
}
|
|
500
|
-
}
|
|
501
|
-
// ── 15. Therapeutic programs (non-ChEMBL curated set) ────────────────
|
|
502
|
-
export async function collectTherapeuticPrograms(input) {
|
|
503
|
-
const programs = findTherapeuticProgramsForTarget({
|
|
504
|
-
geneSymbol: input.geneSymbol,
|
|
505
|
-
uniprot: input.ids.uniprot ?? null,
|
|
506
|
-
});
|
|
507
|
-
if (programs.length === 0) {
|
|
508
|
-
return {
|
|
509
|
-
coverage: "queried_no_data",
|
|
510
|
-
error: { message: "no non-ChEMBL therapeutic programs registered" },
|
|
511
|
-
};
|
|
512
|
-
}
|
|
513
|
-
return { coverage: "available", data: { programs } };
|
|
514
|
-
}
|
|
515
|
-
// ── Manifest ─────────────────────────────────────────────────────────
|
|
516
|
-
/**
|
|
517
|
-
* Stable id used as the durable-step name suffix —
|
|
518
|
-
* `ta-collector:{collectorId}`. The DBOS workflow body iterates this
|
|
519
|
-
* manifest so adding a collector is one entry here + one new exported
|
|
520
|
-
* function above, with no scheduler edits.
|
|
521
|
-
*/
|
|
522
|
-
export const COLLECTOR_MANIFEST = [
|
|
523
|
-
{ id: "cbioportal", run: collectCbioportal },
|
|
524
|
-
{ id: "chembl-modulators", run: collectChemblModulators },
|
|
525
|
-
{ id: "clinvar", run: collectClinvar },
|
|
526
|
-
{ id: "ctgov", run: collectCtgov },
|
|
527
|
-
{ id: "expression-human", run: collectExpressionHuman },
|
|
528
|
-
{ id: "expression-multi-species", run: collectExpressionMultiSpecies },
|
|
529
|
-
{ id: "faers-by-target", run: collectFaersByTarget },
|
|
530
|
-
{ id: "family-complexes", run: collectFamilyComplexes },
|
|
531
|
-
{ id: "impc", run: collectImpc },
|
|
532
|
-
{ id: "monarch", run: collectMonarch },
|
|
533
|
-
{ id: "opentargets", run: collectOpenTargets },
|
|
534
|
-
{ id: "pathways", run: collectPathways },
|
|
535
|
-
{ id: "pubmed-index", run: collectPubmedIndex },
|
|
536
|
-
{ id: "string-ppi", run: collectStringPpi },
|
|
537
|
-
{ id: "therapeutic-programs", run: collectTherapeuticPrograms },
|
|
538
|
-
];
|
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Shared coverage discriminator for target-assessment workflow steps.
|
|
3
|
-
*
|
|
4
|
-
* Per design §6, every collector / decision / fan-out / synthesis step
|
|
5
|
-
* MUST wrap its body in try/catch and return a `{ coverage, ... }` shape
|
|
6
|
-
* instead of throwing. Only Phase 0 (target resolution) is allowed to
|
|
7
|
-
* throw — that legitimately aborts the workflow.
|
|
8
|
-
*
|
|
9
|
-
* `CoverageSchema` in `contracts/target-dossier.ts` is the one enumeration of
|
|
10
|
-
* the coverage states, and the helpers below emit its members. A step envelope
|
|
11
|
-
* carries the three states a step can reach on its own; `filtered` is a
|
|
12
|
-
* judgement one of our own filters makes, so only the assembler helpers below
|
|
13
|
-
* produce it.
|
|
14
|
-
*/
|
|
15
|
-
import { z } from "zod";
|
|
16
|
-
export declare const SerializedErrorSchema: z.ZodObject<{
|
|
17
|
-
message: z.ZodString;
|
|
18
|
-
name: z.ZodOptional<z.ZodString>;
|
|
19
|
-
stack: z.ZodOptional<z.ZodString>;
|
|
20
|
-
}, z.core.$strip>;
|
|
21
|
-
export type SerializedError = z.infer<typeof SerializedErrorSchema>;
|
|
22
|
-
/**
|
|
23
|
-
* Build the schema for a `{ coverage, data, error? }` step output.
|
|
24
|
-
*
|
|
25
|
-
* Use as: `outputSchema: withCoverage(MyDataSchema)` where `MyDataSchema`
|
|
26
|
-
* is the shape of the Phase-1 / Phase-2 / etc. payload for this step.
|
|
27
|
-
*/
|
|
28
|
-
export declare function withCoverage<TData extends z.ZodTypeAny>(dataSchema: TData): z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
29
|
-
coverage: z.ZodLiteral<"available">;
|
|
30
|
-
data: TData;
|
|
31
|
-
}, z.core.$strip>, z.ZodObject<{
|
|
32
|
-
coverage: z.ZodLiteral<"queried_no_data">;
|
|
33
|
-
error: z.ZodOptional<z.ZodObject<{
|
|
34
|
-
message: z.ZodString;
|
|
35
|
-
name: z.ZodOptional<z.ZodString>;
|
|
36
|
-
stack: z.ZodOptional<z.ZodString>;
|
|
37
|
-
}, z.core.$strip>>;
|
|
38
|
-
}, z.core.$strip>, z.ZodObject<{
|
|
39
|
-
coverage: z.ZodLiteral<"not_loaded">;
|
|
40
|
-
reason: z.ZodOptional<z.ZodString>;
|
|
41
|
-
}, z.core.$strip>], "coverage">;
|
|
42
|
-
/**
|
|
43
|
-
* Convenience wrapper for `{ rows: T[] }` payloads. Maps an empty rows
|
|
44
|
-
* array to `coverage: "queried_no_data"` so the dossier discriminator's
|
|
45
|
-
* "available ≡ we have data" invariant holds without per-callsite
|
|
46
|
-
* length checks. Use at any assembler callsite that emits a `rows`
|
|
47
|
-
* shape from a query that may legitimately return zero items.
|
|
48
|
-
*/
|
|
49
|
-
export declare function coverageFromRows<T>(rows: T[], opts?: {
|
|
50
|
-
reason?: string;
|
|
51
|
-
}): {
|
|
52
|
-
coverage: "available";
|
|
53
|
-
data: {
|
|
54
|
-
rows: T[];
|
|
55
|
-
};
|
|
56
|
-
} | {
|
|
57
|
-
coverage: "queried_no_data";
|
|
58
|
-
error: SerializedError;
|
|
59
|
-
};
|
|
60
|
-
/**
|
|
61
|
-
* Build a dossier-section envelope for a row list one of our own filters
|
|
62
|
-
* narrowed.
|
|
63
|
-
*
|
|
64
|
-
* A filter is not the same as an empty upstream, and the difference is the
|
|
65
|
-
* whole point of the four-state contract. Deciding coverage from the source
|
|
66
|
-
* and then filtering the payload loses it: a section whose every row our
|
|
67
|
-
* threshold discarded reports a clean `available` holding nothing, which reads
|
|
68
|
-
* as "the source had nothing to say".
|
|
69
|
-
*
|
|
70
|
-
* So the decision is made once, from both counts:
|
|
71
|
-
* - nothing survived and something was dropped → `filtered`, naming the
|
|
72
|
-
* filter and the count it discarded, with no `data`;
|
|
73
|
-
* - some survived and some were dropped → `available` carrying
|
|
74
|
-
* `dropped_count`, so the section does not overstate its completeness;
|
|
75
|
-
* - nothing survived and nothing was dropped → `queried_no_data`: the list
|
|
76
|
-
* was empty before any filter of ours ran.
|
|
77
|
-
*/
|
|
78
|
-
export declare function coverageFromFilteredRows<TData>(args: {
|
|
79
|
-
data: TData;
|
|
80
|
-
retainedCount: number;
|
|
81
|
-
droppedCount: number;
|
|
82
|
-
/** What ran, in a reader's terms — carried verbatim on the `filtered` branch. */
|
|
83
|
-
filter: string;
|
|
84
|
-
/** Why the source itself held nothing, used only when no filter dropped anything. */
|
|
85
|
-
emptyReason: string;
|
|
86
|
-
}): {
|
|
87
|
-
coverage: "available";
|
|
88
|
-
data: TData;
|
|
89
|
-
dropped_count?: number;
|
|
90
|
-
} | {
|
|
91
|
-
coverage: "filtered";
|
|
92
|
-
filter: string;
|
|
93
|
-
dropped_count: number;
|
|
94
|
-
} | {
|
|
95
|
-
coverage: "queried_no_data";
|
|
96
|
-
error: SerializedError;
|
|
97
|
-
};
|