@mark1russell7/netdiag 0.1.0 → 0.1.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.
Files changed (45) hide show
  1. package/README.md +153 -1
  2. package/build/address.d.ts +56 -0
  3. package/build/address.d.ts.map +1 -0
  4. package/build/address.js +172 -0
  5. package/build/address.js.map +1 -0
  6. package/build/{diagnosis-BTa-Dfwl.js → diagnosis-a4hRzFxp.js} +113 -11
  7. package/build/diagnosis-a4hRzFxp.js.map +1 -0
  8. package/build/diagnosis.d.ts +3 -2
  9. package/build/diagnosis.js +1 -1
  10. package/build/{index-CP1hzELI.d.ts → index-K2dGiYxW.d.ts} +83 -22
  11. package/build/index-K2dGiYxW.d.ts.map +1 -0
  12. package/build/index.d.ts +253 -148
  13. package/build/index.d.ts.map +1 -1
  14. package/build/index.js +1084 -161
  15. package/build/index.js.map +1 -1
  16. package/build/kinds-CQv769lK.js +341 -0
  17. package/build/kinds-CQv769lK.js.map +1 -0
  18. package/build/kinds-_NShfZpU.d.ts +377 -0
  19. package/build/kinds-_NShfZpU.d.ts.map +1 -0
  20. package/build/layer-BNg0VYuK.d.ts +22 -0
  21. package/build/layer-BNg0VYuK.d.ts.map +1 -0
  22. package/build/tools.d.ts +206 -0
  23. package/build/tools.d.ts.map +1 -0
  24. package/build/tools.js +896 -0
  25. package/build/tools.js.map +1 -0
  26. package/package.json +17 -5
  27. package/src/address/index.ts +155 -0
  28. package/src/diagnosis/catalog.ts +8 -8
  29. package/src/diagnosis/index.ts +13 -0
  30. package/src/diagnosis/score.ts +160 -0
  31. package/src/index.ts +35 -0
  32. package/src/kinds.ts +460 -0
  33. package/src/probes/native.ts +354 -0
  34. package/src/probes/socket.ts +520 -0
  35. package/src/readers.ts +367 -0
  36. package/src/scope.ts +5 -24
  37. package/src/tools/common.ts +47 -0
  38. package/src/tools/host.ts +91 -0
  39. package/src/tools/index.ts +38 -0
  40. package/src/tools/licenses.ts +37 -0
  41. package/src/tools/mtr.ts +156 -0
  42. package/src/tools/ping.ts +231 -0
  43. package/src/tools/traceroute.ts +313 -0
  44. package/build/diagnosis-BTa-Dfwl.js.map +0 -1
  45. package/build/index-CP1hzELI.d.ts.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diagnosis-a4hRzFxp.js","names":[],"sources":["../src/layer.ts","../src/diagnosis/model.ts","../src/diagnosis/catalog.ts","../src/diagnosis/score.ts"],"sourcesContent":["/**\n * The layers of the network stack that netdiag examines, from the bottom to the top.\n *\n * - `link`: the network interface and its physical link.\n * - `wifi`: the Wi-Fi link: the signal, the channel and the rate.\n * - `ip`: the addresses of the interfaces.\n * - `route`: the routes of this computer and its gateway.\n * - `path`: the hops through the provider networks to the remote host.\n * - `dns`: the resolution of names to addresses.\n * - `tcp`: the connections to a port.\n * - `tls`: the handshake and the certificate of the server.\n * - `http`: the request and the response.\n */\nexport const LAYERS = [\"link\", \"wifi\", \"ip\", \"route\", \"path\", \"dns\", \"tcp\", \"tls\", \"http\"] as const;\n\n/** A layer of the network stack. The list `LAYERS` gives each layer in its order. */\nexport type Layer = (typeof LAYERS)[number];\n\nconst RANKS: ReadonlyMap<string, number> = new Map(LAYERS.map((layer, rank) => [layer, rank]));\n\n/** This function tells if a value is the name of a layer. */\nexport function isLayer(value: unknown): value is Layer {\n return typeof value === \"string\" && RANKS.has(value);\n}\n\n/** This function gives the position of a layer in `LAYERS`. The bottom layer `link` has the position 0. */\nexport function layerRank(layer: Layer): number {\n const rank = RANKS.get(layer);\n if (rank === undefined) throw new RangeError(`\"${String(layer)}\" is not a layer`);\n return rank;\n}\n","import { err, ok, type Result } from \"@mark1russell7/optional\";\nimport type { Layer } from \"../layer.ts\";\n\n/** A probability for each value of a feature. The probabilities are more than 0, and their sum is 1. */\nexport type Distribution = Readonly<Record<string, number>>;\n\n/**\n * A feature is one discrete observation that the facts give, for example the outcome of a TCP connect.\n *\n * The distribution `healthy` gives the probability of each value when the network has no fault.\n */\nexport interface Feature {\n readonly id: string;\n readonly layer: Layer;\n readonly summary: string;\n readonly values: readonly string[];\n readonly healthy: Distribution;\n}\n\n/** The parts of the model. Each fault is in one part. */\nexport const PARTS = [\"interface\", \"wifi\", \"gateway\", \"resolver\", \"path\", \"service\"] as const;\n\n/** A part of the model: the network interface, the Wi-Fi link, the gateway, the DNS resolver, the path or the remote service. */\nexport type Part = (typeof PARTS)[number];\n\n/**\n * The reach of a fault.\n *\n * - `shared`: the fault changes the observations of each host, for example a TLS proxy of the local network.\n * - `target`: the fault changes only the observations of the primary target, for example a slow server.\n */\nexport type Reach = \"shared\" | \"target\";\n\n/**\n * The strength of a prediction. The rules of practice use these strengths. The lab runs then replace them with fitted\n * distributions.\n */\nexport type Strength = \"sure\" | \"strong\" | \"usual\" | \"weak\";\n\n/** The probability that each strength gives to the expected values together. */\nexport const STRENGTHS: Readonly<Record<Strength, number>> = { sure: 0.98, strong: 0.9, usual: 0.75, weak: 0.6 };\n\n/**\n * A prediction of a fault for one feature: a full distribution, or the expected values and a strength.\n *\n * With `expect`, the expected values share the probability of the strength equally. The other values share the rest.\n */\nexport type Prediction = Distribution | Expectation;\n\n/** The expected values of a feature, and the strength of the prediction. */\nexport interface Expectation {\n readonly expect: string | readonly string[];\n readonly strength: Strength;\n}\n\nfunction isExpectation(prediction: Prediction): prediction is Expectation {\n return typeof prediction.strength === \"string\";\n}\n\n/**\n * A fault of the catalog.\n *\n * The fault predicts a distribution for each feature that it changes. For each other feature, it predicts the\n * distribution `healthy` of the feature. The prior is a relative weight. The default prior is 1.\n */\nexport interface Fault {\n readonly id: string;\n readonly part: Part;\n readonly reach: Reach;\n /** The cause in plain words, for example \"No program listens on the port.\" */\n readonly summary: string;\n /** The next step for the user in plain words. */\n readonly step: string;\n readonly prior?: number | undefined;\n readonly predicts: Readonly<Record<string, Prediction>>;\n}\n\n/** The data of a catalog: the features, the faults, and the priors of the hypotheses \"no fault\" and \"unknown cause\". */\nexport interface CatalogSpec {\n readonly version: string;\n readonly features: readonly Feature[];\n readonly faults: readonly Fault[];\n readonly priors?: { readonly none?: number | undefined; readonly unknown?: number | undefined } | undefined;\n}\n\n/**\n * A hypothesis of the model.\n *\n * - `none`: the network has no fault.\n * - `fault`: one fault of the catalog.\n * - `unknown`: a cause that the catalog does not have. It gives each value of a feature the same probability.\n */\nexport interface Hypothesis {\n readonly id: string;\n readonly kind: \"none\" | \"fault\" | \"unknown\";\n readonly summary: string;\n readonly step: string;\n readonly part?: Part | undefined;\n readonly reach?: Reach | undefined;\n}\n\n/** The id of the hypothesis \"no fault\". */\nexport const NONE = \"none\";\n/** The id of the hypothesis \"unknown cause\". */\nexport const UNKNOWN = \"unknown\";\n\n/** A catalog with each distribution in its full form, and the logarithms of the probabilities. */\nexport interface Catalog {\n readonly version: string;\n readonly features: ReadonlyMap<string, Feature>;\n readonly hypotheses: readonly Hypothesis[];\n /** This function gives the probability of a value of a feature under a hypothesis, for a primary or a reference host. */\n likelihood(hypothesis: string, feature: string, value: string, primary: boolean): number;\n prior(hypothesis: string): number;\n}\n\n/** The lowest probability of a value. With a probability of 0, one wrong observation removes a cause. */\nexport const FLOOR = 0.001;\n\nconst SUM_TOLERANCE = 1e-9;\n\nfunction distributionProblems(where: string, feature: Feature, dist: Distribution): string[] {\n const problems: string[] = [];\n for (const value of Object.keys(dist)) if (!feature.values.includes(value)) problems.push(`${where}: \"${value}\" is not a value of ${feature.id}.`);\n for (const value of feature.values) {\n const p = dist[value];\n if (p === undefined) problems.push(`${where}: the value \"${value}\" of ${feature.id} has no probability.`);\n else if (!Number.isFinite(p) || p < FLOOR) problems.push(`${where}: the probability of \"${value}\" is ${p}. It must be ${FLOOR} or more.`);\n }\n const sum = Object.values(dist).reduce((a, b) => a + b, 0);\n if (Math.abs(sum - 1) > SUM_TOLERANCE) problems.push(`${where}: the sum of the probabilities is ${sum}, not 1.`);\n return problems;\n}\n\n/** This function gives the full distribution of a prediction, or the problems of the prediction. */\nexport function expand(feature: Feature, prediction: Prediction): Result<Distribution, string[]> {\n if (!isExpectation(prediction)) return ok(prediction);\n const expected = typeof prediction.expect === \"string\" ? [prediction.expect] : [...prediction.expect];\n const unknown = expected.filter((value) => !feature.values.includes(value));\n if (unknown.length > 0) return err(unknown.map((value) => `\"${value}\" is not a value of ${feature.id}.`));\n if (expected.length === 0 || new Set(expected).size !== expected.length) return err([`The expected values of ${feature.id} are empty or repeated.`]);\n const others = feature.values.filter((value) => !expected.includes(value));\n const mass = others.length === 0 ? 1 : STRENGTHS[prediction.strength];\n const dist: Record<string, number> = {};\n for (const value of expected) dist[value] = mass / expected.length;\n for (const value of others) dist[value] = (1 - mass) / others.length;\n return ok(dist);\n}\n\nconst ID = /^[a-z][a-z0-9]*(?:[.-][a-z0-9]+)*$/;\n\n/**\n * This function examines a catalog and makes its tables.\n *\n * It refuses a repeated id, an unknown feature or value, a probability below `FLOOR`, and a distribution with a sum that is not 1.\n */\nexport function compileCatalog(spec: CatalogSpec): Result<Catalog, string[]> {\n const problems: string[] = [];\n const features = new Map<string, Feature>();\n for (const feature of spec.features) {\n if (!ID.test(feature.id)) problems.push(`\"${feature.id}\" is not a valid feature id.`);\n if (features.has(feature.id)) problems.push(`The feature ${feature.id} is repeated.`);\n if (feature.values.length < 2 || new Set(feature.values).size !== feature.values.length) {\n problems.push(`The feature ${feature.id} must have two or more different values.`);\n }\n problems.push(...distributionProblems(`healthy of ${feature.id}`, feature, feature.healthy));\n features.set(feature.id, feature);\n }\n const hypotheses: Hypothesis[] = [{ id: NONE, kind: \"none\", summary: \"The network has no fault.\", step: \"No step is necessary.\" }];\n const priors = new Map<string, number>([[NONE, spec.priors?.none ?? 1]]);\n const tables = new Map<string, Map<string, Distribution>>();\n for (const fault of spec.faults) {\n if (!ID.test(fault.id) || fault.id === NONE || fault.id === UNKNOWN) problems.push(`\"${fault.id}\" is not a valid fault id.`);\n if (priors.has(fault.id)) problems.push(`The fault ${fault.id} is repeated.`);\n priors.set(fault.id, fault.prior ?? 1);\n hypotheses.push({ id: fault.id, kind: \"fault\", summary: fault.summary, step: fault.step, part: fault.part, reach: fault.reach });\n const table = new Map<string, Distribution>();\n for (const [featureId, prediction] of Object.entries(fault.predicts)) {\n const feature = features.get(featureId);\n if (feature === undefined) {\n problems.push(`The fault ${fault.id} predicts the unknown feature ${featureId}.`);\n continue;\n }\n const dist = expand(feature, prediction);\n if (dist.tag === \"err\") problems.push(...dist.error.map((problem) => `${fault.id}: ${problem}`));\n else {\n problems.push(...distributionProblems(`${fault.id} for ${featureId}`, feature, dist.value));\n table.set(featureId, dist.value);\n }\n }\n if (table.size === 0) problems.push(`The fault ${fault.id} predicts no feature.`);\n tables.set(fault.id, table);\n }\n hypotheses.push({\n id: UNKNOWN,\n kind: \"unknown\",\n summary: \"The catalog does not have the cause.\",\n step: \"Collect more facts, and report the facts as a new fault of the catalog.\",\n });\n priors.set(UNKNOWN, spec.priors?.unknown ?? 1);\n for (const [id, prior] of priors) if (!Number.isFinite(prior) || prior <= 0) problems.push(`The prior of ${id} is ${prior}. It must be more than 0.`);\n if (problems.length > 0) return err(problems);\n\n const total = [...priors.values()].reduce((a, b) => a + b, 0);\n const reach = new Map(hypotheses.map((h) => [h.id, h.reach]));\n return ok({\n version: spec.version,\n features,\n hypotheses,\n prior: (hypothesis) => (priors.get(hypothesis) ?? 0) / total,\n likelihood(hypothesis, featureId, value, primary) {\n const feature = features.get(featureId);\n if (feature === undefined || !feature.values.includes(value)) return Number.NaN;\n if (hypothesis === UNKNOWN) return 1 / feature.values.length;\n const fault = tables.get(hypothesis);\n const changed = fault?.get(featureId);\n const applies = primary || reach.get(hypothesis) === \"shared\";\n return (applies && changed !== undefined ? changed : feature.healthy)[value] ?? Number.NaN;\n },\n });\n}\n\n/**\n * One observation of a feature.\n *\n * `primary` tells if the observation is of the primary target or of a reference host. `facts` gives the ids of the facts\n * that gave the observation, for the explanation.\n */\nexport interface Evidence {\n readonly feature: string;\n readonly value: string;\n readonly primary: boolean;\n readonly facts?: readonly string[] | undefined;\n}\n\n/** A cause with its probability. */\nexport interface Cause extends Hypothesis {\n readonly probability: number;\n}\n\n/** The result of a diagnosis: the causes from the most probable, and the evidence that the model did not use, with the reason. */\nexport interface Diagnosis {\n readonly causes: readonly Cause[];\n readonly used: readonly Evidence[];\n readonly ignored: readonly { readonly evidence: Evidence; readonly reason: string }[];\n}\n\nfunction usable(catalog: Catalog, evidence: readonly Evidence[]): Pick<Diagnosis, \"used\" | \"ignored\"> {\n const used: Evidence[] = [];\n const ignored: { evidence: Evidence; reason: string }[] = [];\n for (const item of evidence) {\n const feature = catalog.features.get(item.feature);\n if (feature === undefined) ignored.push({ evidence: item, reason: `The catalog has no feature ${item.feature}.` });\n else if (!feature.values.includes(item.value)) ignored.push({ evidence: item, reason: `\"${item.value}\" is not a value of ${item.feature}.` });\n else used.push(item);\n }\n return { used, ignored };\n}\n\n/**\n * This function gives the probability of each hypothesis from the evidence, with the rule of Bayes.\n *\n * The model takes the observations as independent for a given hypothesis. The calculation uses logarithms, so a long\n * list of evidence does not go to zero. The order of the evidence does not change the result.\n * An exact tie keeps the order of the catalog, with \"no fault\" first.\n */\nexport function diagnose(catalog: Catalog, evidence: readonly Evidence[]): Diagnosis {\n const { used, ignored } = usable(catalog, evidence);\n const logs = catalog.hypotheses.map((h) => {\n let sum = Math.log(catalog.prior(h.id));\n for (const item of used) sum += Math.log(catalog.likelihood(h.id, item.feature, item.value, item.primary));\n return sum;\n });\n const top = Math.max(...logs);\n const weights = logs.map((log) => Math.exp(log - top));\n const total = weights.reduce((a, b) => a + b, 0);\n // An exact tie keeps the order of the catalog: \"no fault\" first, then the faults, then \"unknown cause\". Thus a fault\n // whose features are not observed does not come before \"no fault\".\n const causes = catalog.hypotheses\n .map((h, i) => ({ ...h, probability: (weights[i] ?? 0) / total, order: i }))\n .sort((a, b) => b.probability - a.probability || a.order - b.order)\n .map(({ order: _order, ...cause }) => cause);\n return { causes, used, ignored };\n}\n\n/** The part of one observation in the comparison of two hypotheses. */\nexport interface Contribution {\n readonly evidence: Evidence;\n /** The ratio of the likelihoods of the two hypotheses, as a base-10 logarithm. 1 is a factor of 10 for the first hypothesis. */\n readonly weight: number;\n}\n\n/**\n * This function tells why the model prefers one hypothesis to a second hypothesis.\n *\n * It gives each observation with its weight, from the largest weight in absolute value. The default second hypothesis is\n * \"no fault\".\n */\nexport function explain(catalog: Catalog, evidence: readonly Evidence[], hypothesis: string, versus: string = NONE): Contribution[] {\n const { used } = usable(catalog, evidence);\n return used\n .map((item) => ({\n evidence: item,\n weight: Math.log10(catalog.likelihood(hypothesis, item.feature, item.value, item.primary)) -\n Math.log10(catalog.likelihood(versus, item.feature, item.value, item.primary)),\n }))\n .sort((a, b) => Math.abs(b.weight) - Math.abs(a.weight));\n}\n\n/**\n * This function gives the probability of each value of a feature that is not observed yet, from the probabilities of\n * the causes. The choice of the next probe uses it.\n */\nexport function predictive(catalog: Catalog, diagnosis: Diagnosis, feature: string, primary: boolean): Distribution {\n const known = catalog.features.get(feature);\n if (known === undefined) throw new RangeError(`The catalog has no feature ${feature}`);\n const dist: Record<string, number> = {};\n for (const value of known.values) {\n dist[value] = diagnosis.causes.reduce((sum, cause) => sum + cause.probability * catalog.likelihood(cause.id, feature, value, primary), 0);\n }\n return dist;\n}\n","import { compileCatalog, type Catalog, type CatalogSpec, type Feature, type Fault } from \"./model.ts\";\n\n/**\n * The features of the standard catalog.\n *\n * The readers of the facts give these features. The distribution `healthy` of each feature is a rule of practice. The\n * lab runs (ND-7) then fit it.\n */\nexport const STANDARD_FEATURES: readonly Feature[] = [\n {\n id: \"link.state\",\n layer: \"link\",\n summary: \"The state of the link of the network interface.\",\n values: [\"up\", \"down\"],\n healthy: { up: 0.999, down: 0.001 },\n },\n {\n id: \"wifi.signal\",\n layer: \"wifi\",\n summary: \"The strength of the Wi-Fi signal: strong above -60 dBm, weak below -75 dBm.\",\n values: [\"strong\", \"fair\", \"weak\"],\n healthy: { strong: 0.7, fair: 0.25, weak: 0.05 },\n },\n {\n id: \"gateway.reply\",\n layer: \"route\",\n summary: \"The gateway answers an ICMP echo request.\",\n values: [\"reply\", \"none\"],\n healthy: { reply: 0.97, none: 0.03 },\n },\n {\n id: \"path.loss\",\n layer: \"path\",\n summary: \"The packet loss on the path: none, some (up to 5 %) or high (more than 5 %).\",\n values: [\"none\", \"some\", \"high\"],\n healthy: { none: 0.9, some: 0.08, high: 0.02 },\n },\n {\n id: \"dns.answer\",\n layer: \"dns\",\n summary: \"The result of the DNS lookup of the name of the target. The value unreachable tells that no DNS service listens on the resolver.\",\n values: [\"ok\", \"nxdomain\", \"servfail\", \"refused\", \"timeout\", \"unreachable\"],\n healthy: { ok: 0.965, nxdomain: 0.01, servfail: 0.005, refused: 0.005, timeout: 0.01, unreachable: 0.005 },\n },\n {\n id: \"dns.address\",\n layer: \"dns\",\n summary: \"The kind of the addresses of a public name: public, or private (RFC 1918, RFC 6598, RFC 4193, loopback, link-local or unspecified).\",\n values: [\"public\", \"private\"],\n healthy: { public: 0.95, private: 0.05 },\n },\n {\n id: \"ip.family\",\n layer: \"ip\",\n summary: \"The address families that connect, for a host with IPv4 and IPv6 addresses.\",\n values: [\"both\", \"ipv4-only\", \"ipv6-only\"],\n healthy: { both: 0.9, \"ipv4-only\": 0.08, \"ipv6-only\": 0.02 },\n },\n {\n id: \"icmp.reply\",\n layer: \"ip\",\n summary: \"The host answers an ICMP echo request, does not answer, or a router tells that it is unreachable.\",\n values: [\"reply\", \"none\", \"unreachable\"],\n healthy: { reply: 0.85, none: 0.14, unreachable: 0.01 },\n },\n {\n id: \"tcp.connect\",\n layer: \"tcp\",\n summary: \"The outcome of a TCP connect to the port of the target.\",\n values: [\"connected\", \"refused\", \"timeout\", \"unreachable\", \"reset\"],\n healthy: { connected: 0.97, refused: 0.01, timeout: 0.01, unreachable: 0.005, reset: 0.005 },\n },\n {\n id: \"tcp.time\",\n layer: \"tcp\",\n summary: \"The time of the TCP connect: short (below 150 ms), long, or retry (900 ms or more, a SYN sent again).\",\n values: [\"short\", \"long\", \"retry\"],\n healthy: { short: 0.8, long: 0.18, retry: 0.02 },\n },\n {\n id: \"tls.handshake\",\n layer: \"tls\",\n summary: \"The outcome of the TLS handshake.\",\n values: [\"ok\", \"cert-error\", \"alert\", \"timeout\", \"reset\"],\n healthy: { ok: 0.97, \"cert-error\": 0.01, alert: 0.01, timeout: 0.005, reset: 0.005 },\n },\n {\n id: \"tls.ratio\",\n layer: \"tls\",\n summary: \"The time of the TLS handshake divided by the time of the TCP connect: low (below 3), mid, or high (8 or more).\",\n values: [\"low\", \"mid\", \"high\"],\n healthy: { low: 0.75, mid: 0.2, high: 0.05 },\n },\n {\n id: \"tls.trust\",\n layer: \"tls\",\n summary: \"The issuer of the certificate: a public root of trust, or a private root.\",\n values: [\"public\", \"private\"],\n healthy: { public: 0.97, private: 0.03 },\n },\n {\n id: \"http.status\",\n layer: \"http\",\n summary: \"The class of the HTTP status: ok (2xx and 3xx), client-error (4xx), server-error (5xx), or none.\",\n values: [\"ok\", \"client-error\", \"server-error\", \"none\"],\n healthy: { ok: 0.95, \"client-error\": 0.03, \"server-error\": 0.01, none: 0.01 },\n },\n {\n id: \"http.ttfb\",\n layer: \"http\",\n summary: \"The time to the first byte divided by the time of the TCP connect: low (below 4), mid, or high (20 or more).\",\n values: [\"low\", \"mid\", \"high\"],\n healthy: { low: 0.6, mid: 0.3, high: 0.1 },\n },\n];\n\n/**\n * The faults of the standard catalog. Each prediction is a rule of practice. The lab runs (ND-7) then fit the\n * distributions, and the priors.\n */\nexport const STANDARD_FAULTS: readonly Fault[] = [\n {\n id: \"interface.down\",\n part: \"interface\",\n reach: \"shared\",\n summary: \"The network interface has no link.\",\n step: \"Connect the cable or the Wi-Fi network, and make sure that the interface is on.\",\n predicts: {\n \"link.state\": { expect: \"down\", strength: \"sure\" },\n \"gateway.reply\": { expect: \"none\", strength: \"sure\" },\n \"dns.answer\": { expect: \"timeout\", strength: \"strong\" },\n \"icmp.reply\": { expect: [\"none\", \"unreachable\"], strength: \"sure\" },\n \"tcp.connect\": { expect: [\"unreachable\", \"timeout\"], strength: \"sure\" },\n },\n },\n {\n id: \"wifi.weak\",\n part: \"wifi\",\n reach: \"shared\",\n summary: \"The Wi-Fi signal is weak, so the link loses packets.\",\n step: \"Move nearer to the access point, or use a cable.\",\n predicts: {\n \"wifi.signal\": { expect: \"weak\", strength: \"strong\" },\n \"path.loss\": { expect: [\"some\", \"high\"], strength: \"usual\" },\n \"tcp.time\": { expect: [\"long\", \"retry\"], strength: \"usual\" },\n },\n },\n {\n id: \"gateway.down\",\n part: \"gateway\",\n reach: \"shared\",\n summary: \"The gateway does not forward packets.\",\n step: \"Restart the router, or examine the link from the router to the provider.\",\n predicts: {\n \"gateway.reply\": { expect: \"none\", strength: \"strong\" },\n \"dns.answer\": { expect: \"timeout\", strength: \"strong\" },\n \"icmp.reply\": { expect: [\"none\", \"unreachable\"], strength: \"strong\" },\n \"tcp.connect\": { expect: [\"timeout\", \"unreachable\"], strength: \"strong\" },\n },\n },\n {\n id: \"captive.portal\",\n part: \"gateway\",\n reach: \"shared\",\n summary: \"A captive portal holds the traffic until you sign in.\",\n step: \"Open a web page in a browser, and accept the terms of the network.\",\n predicts: {\n \"dns.address\": { expect: \"private\", strength: \"usual\" },\n \"tls.handshake\": { expect: \"cert-error\", strength: \"strong\" },\n \"tls.trust\": { expect: \"private\", strength: \"usual\" },\n },\n },\n {\n id: \"resolver.down\",\n part: \"resolver\",\n reach: \"shared\",\n summary: \"The DNS resolver does not answer.\",\n step: \"Use a different DNS resolver, or examine the resolver of the router.\",\n predicts: {\n \"dns.answer\": { expect: [\"timeout\", \"unreachable\"], strength: \"strong\" },\n },\n },\n {\n id: \"resolver.failing\",\n part: \"resolver\",\n reach: \"shared\",\n summary: \"The DNS resolver answers with an error for each name.\",\n step: \"Use a different DNS resolver, and examine the DNSSEC settings of the resolver.\",\n predicts: {\n \"dns.answer\": { expect: [\"servfail\", \"refused\"], strength: \"strong\" },\n },\n },\n {\n id: \"name.missing\",\n part: \"service\",\n reach: \"target\",\n summary: \"The name does not exist in DNS.\",\n step: \"Examine the spelling of the name, and the records of its DNS zone.\",\n predicts: {\n \"dns.answer\": { expect: \"nxdomain\", strength: \"strong\" },\n },\n },\n {\n id: \"port.closed\",\n part: \"service\",\n reach: \"target\",\n summary: \"No program listens on the port.\",\n step: \"Start the service, or use the correct port.\",\n predicts: {\n \"tcp.connect\": { expect: \"refused\", strength: \"strong\" },\n },\n },\n {\n id: \"host.down\",\n part: \"service\",\n reach: \"target\",\n summary: \"The host is down.\",\n step: \"Start the host, or examine its network link.\",\n predicts: {\n \"tcp.connect\": { expect: [\"timeout\", \"unreachable\"], strength: \"strong\" },\n \"icmp.reply\": { expect: [\"none\", \"unreachable\"], strength: \"strong\" },\n },\n },\n {\n id: \"filter.drop\",\n part: \"path\",\n reach: \"target\",\n summary: \"A filter drops the packets to the port.\",\n step: \"Examine the firewall rules of the host and of each network on the path.\",\n predicts: {\n \"tcp.connect\": { expect: \"timeout\", strength: \"strong\" },\n \"icmp.reply\": { reply: 0.5, none: 0.45, unreachable: 0.05 },\n },\n },\n {\n id: \"filter.icmp\",\n part: \"path\",\n reach: \"target\",\n summary: \"A filter blocks ICMP. The host is up.\",\n step: \"No step is necessary for the service. Use TCP to examine the host.\",\n predicts: {\n \"icmp.reply\": { expect: \"none\", strength: \"strong\" },\n },\n },\n {\n id: \"filter.reset\",\n part: \"path\",\n reach: \"target\",\n summary: \"A device on the path resets the connections.\",\n step: \"Examine the firewalls and the filters of content on the path, and try a different network.\",\n predicts: {\n \"tcp.connect\": { connected: 0.5, refused: 0.02, timeout: 0.02, unreachable: 0.02, reset: 0.44 },\n \"tls.handshake\": { expect: \"reset\", strength: \"strong\" },\n },\n },\n {\n id: \"path.loss\",\n part: \"path\",\n reach: \"target\",\n summary: \"A network on the path loses packets.\",\n step: \"Find the first hop with loss, and tell the provider of that network.\",\n predicts: {\n \"path.loss\": { expect: [\"some\", \"high\"], strength: \"strong\" },\n \"tcp.time\": { expect: [\"long\", \"retry\"], strength: \"usual\" },\n \"http.ttfb\": { expect: [\"mid\", \"high\"], strength: \"weak\" },\n },\n },\n {\n id: \"path.mtu\",\n part: \"path\",\n reach: \"shared\",\n summary: \"Large packets do not pass, and no ICMP message tells the size (an MTU black hole).\",\n step: \"Lower the MTU of the interface or of the VPN, or enable MSS clamping on the router.\",\n predicts: {\n \"tls.handshake\": { expect: \"timeout\", strength: \"strong\" },\n \"http.status\": { expect: \"none\", strength: \"usual\" },\n },\n },\n {\n id: \"ipv6.broken\",\n part: \"path\",\n reach: \"shared\",\n summary: \"IPv6 does not work, and IPv4 works.\",\n step: \"Examine the IPv6 settings of the router, or turn off IPv6 until the provider repairs it.\",\n predicts: {\n \"ip.family\": { expect: \"ipv4-only\", strength: \"strong\" },\n \"tcp.time\": { expect: [\"long\", \"retry\"], strength: \"weak\" },\n },\n },\n {\n id: \"tls.proxy\",\n part: \"path\",\n reach: \"shared\",\n summary: \"A proxy examines the TLS traffic.\",\n step: \"Compare the issuer of the certificate with the issuer on a different network, and ask the owner of the network.\",\n predicts: {\n \"tls.ratio\": { expect: [\"mid\", \"high\"], strength: \"usual\" },\n \"tls.trust\": { expect: \"private\", strength: \"usual\" },\n \"tcp.time\": { expect: \"short\", strength: \"strong\" },\n },\n },\n {\n id: \"tls.cert-invalid\",\n part: \"service\",\n reach: \"target\",\n summary: \"The certificate of the server is not valid: it expired, or it is for a different name.\",\n step: \"Renew the certificate of the server, or correct its names.\",\n predicts: {\n \"tls.handshake\": { expect: \"cert-error\", strength: \"strong\" },\n },\n },\n {\n id: \"tls.mismatch\",\n part: \"service\",\n reach: \"target\",\n summary: \"The client and the server have no TLS version or cipher in common.\",\n step: \"Enable TLS 1.2 or TLS 1.3 with modern ciphers on the server.\",\n predicts: {\n \"tls.handshake\": { expect: \"alert\", strength: \"strong\" },\n },\n },\n {\n id: \"server.slow\",\n part: \"service\",\n reach: \"target\",\n summary: \"The server is slow.\",\n step: \"Examine the load and the logs of the server.\",\n predicts: {\n \"http.ttfb\": { expect: \"high\", strength: \"strong\" },\n \"tls.ratio\": { expect: [\"mid\", \"high\"], strength: \"weak\" },\n },\n },\n {\n id: \"server.error\",\n part: \"service\",\n reach: \"target\",\n summary: \"The server answers with an error.\",\n step: \"Examine the logs of the server and of its upstream services.\",\n predicts: {\n \"http.status\": { expect: \"server-error\", strength: \"strong\" },\n },\n },\n];\n\n/** The standard catalog: the rules of practice as the first likelihoods. Each hypothesis has the prior weight 1. */\nexport const STANDARD_CATALOG_SPEC: CatalogSpec = {\n version: \"2\",\n features: STANDARD_FEATURES,\n faults: STANDARD_FAULTS,\n};\n\n/** The standard catalog, compiled. */\nexport const STANDARD_CATALOG: Catalog = (() => {\n const compiled = compileCatalog(STANDARD_CATALOG_SPEC);\n if (compiled.tag === \"err\") throw new Error(`The standard catalog has problems: ${compiled.error.join(\" \")}`);\n return compiled.value;\n})();\n","import { err, ok, type Result } from \"@mark1russell7/optional\";\nimport { type ClassScore, type Confusion, type Proportion, brier, classScores, confusion, wilson } from \"@mark1russell7/rigor-statistics\";\nimport type { Catalog, Diagnosis } from \"./model.ts\";\n\n/** The format of a lab record. */\nexport const LAB_RECORD_FORMAT = \"netdiag.lab-record/1\";\n\n/** The probability that a diagnosis gives to one cause. */\nexport interface LabCause {\n readonly id: string;\n readonly probability: number;\n}\n\n/** One run of a lab case: the hypothesis that the lab made true, and the diagnosis of netdiag. */\nexport interface LabRecord {\n readonly format: typeof LAB_RECORD_FORMAT;\n /** The id of the case, for example `tls.cert-invalid/expired`. */\n readonly case: string;\n /** The version of the catalog that gave the diagnosis. */\n readonly catalog: string;\n /** The hypothesis that the lab made true: a fault of the catalog, `none` or `unknown`. */\n readonly truth: string;\n /** The most probable cause of the diagnosis. */\n readonly predicted: string;\n /** The rank of the truth in the causes, from 1. */\n readonly rank: number;\n /** The probability of each cause, from the most probable. */\n readonly causes: readonly LabCause[];\n}\n\n/**\n * This function makes the record of one run of a lab case. The truth must be a hypothesis of the catalog, and the\n * diagnosis must have a cause.\n */\nexport function labRecordOf(catalog: Catalog, caseId: string, truth: string, diagnosis: Diagnosis): Result<LabRecord, string> {\n if (!catalog.hypotheses.some((hypothesis) => hypothesis.id === truth)) return err(`The truth \"${truth}\" is not a hypothesis of the catalog ${catalog.version}.`);\n const first = diagnosis.causes[0];\n if (first === undefined) return err(\"The diagnosis has no cause.\");\n const index = diagnosis.causes.findIndex((cause) => cause.id === truth);\n if (index < 0) return err(`The diagnosis has no cause \"${truth}\".`);\n return ok({\n format: LAB_RECORD_FORMAT,\n case: caseId,\n catalog: catalog.version,\n truth,\n predicted: first.id,\n rank: index + 1,\n causes: diagnosis.causes.map((cause) => ({ id: cause.id, probability: cause.probability })),\n });\n}\n\n/** The scores of the lab records of one catalog. */\nexport interface LabScore {\n /** The version of the catalog. */\n readonly catalog: string;\n /** The number of records. */\n readonly n: number;\n /** The number of records where the predicted cause is the truth. */\n readonly correct: number;\n /** The Wilson interval of the part of the records where the predicted cause is the truth. */\n readonly accuracy: Proportion;\n /** The confusion table. It has a row for each hypothesis of the catalog, in the order of the catalog. */\n readonly table: Confusion<string>;\n /** The scores of each hypothesis, in the order of the catalog. */\n readonly classes: readonly ClassScore<string>[];\n /** The mean Brier score of the records: the squared distance of the probabilities from the truth, from 0 to 2. */\n readonly brier: number | undefined;\n}\n\n/**\n * This function scores the lab records of one catalog. The recall of a hypothesis is the part of its runs where the\n * diagnosis names it. A record of a different catalog version, or with a hypothesis that is not in the catalog, is an error.\n */\nexport function scoreLab(catalog: Catalog, records: readonly LabRecord[]): Result<LabScore, string[]> {\n const ids = catalog.hypotheses.map((hypothesis) => hypothesis.id);\n const known = new Set(ids);\n const problems: string[] = [];\n records.forEach((record, i) => {\n const name = `Record ${i} (${record.case})`;\n if (record.format !== LAB_RECORD_FORMAT) problems.push(`${name} has the format \"${String(record.format)}\", not \"${LAB_RECORD_FORMAT}\".`);\n if (record.catalog !== catalog.version) problems.push(`${name} comes from the catalog ${record.catalog}, not ${catalog.version}.`);\n for (const id of new Set([record.truth, record.predicted, ...record.causes.map((cause) => cause.id)])) {\n if (!known.has(id)) problems.push(`${name} names \"${id}\", which is not a hypothesis of the catalog.`);\n }\n });\n if (problems.length > 0) return err(problems);\n const table = confusion(\n records.map((record) => record.truth),\n records.map((record) => record.predicted),\n ids,\n );\n const forecasts = records.flatMap((record) => {\n const probability = new Map(record.causes.map((cause) => [cause.id, cause.probability]));\n return ids.map((id) => ({ p: probability.get(id) ?? 0, hit: id === record.truth }));\n });\n return ok({\n catalog: catalog.version,\n n: table.n,\n correct: table.correct,\n accuracy: wilson(table.correct, table.n),\n table,\n classes: classScores(table),\n brier: records.length === 0 ? undefined : brier(forecasts) * ids.length,\n });\n}\n\n/** The least lower end of the Wilson interval of the recall, for each hypothesis that the gate holds. */\nexport type LabBars = Readonly<Record<string, number>>;\n\n/** The verdict of the gate on one hypothesis: `no-read` when no record has it as the truth. */\nexport type LabStatus = \"pass\" | \"fail\" | \"no-read\";\n\n/** The verdict of the gate on one hypothesis. */\nexport interface LabVerdict {\n readonly hypothesis: string;\n readonly status: LabStatus;\n /** The bar of the hypothesis. */\n readonly bar: number;\n /** The lower end of the recall interval. It is `undefined` when no record has the hypothesis as the truth. */\n readonly low: number | undefined;\n /** The number of records with the hypothesis as the truth. */\n readonly support: number;\n}\n\n/** The result of the accuracy gate. */\nexport interface LabGate {\n /** It is `true` only when each verdict is `pass`. A hypothesis with no read does not pass. */\n readonly pass: boolean;\n /** The verdicts, in the order of the catalog. */\n readonly verdicts: readonly LabVerdict[];\n /** The hypotheses with no bar, for example a fault that no lab can make. */\n readonly unbarred: readonly string[];\n}\n\n/**\n * This function holds the recall of each hypothesis with a bar. A hypothesis passes when the lower end of its\n * recall interval is the bar or more. A bar for a hypothesis that is not in the score is an error.\n */\nexport function labGate(score: LabScore, bars: LabBars): Result<LabGate, string[]> {\n const problems: string[] = [];\n const labels = new Set(score.table.labels);\n for (const [hypothesis, bar] of Object.entries(bars)) {\n if (!labels.has(hypothesis)) problems.push(`The bar of \"${hypothesis}\" is not for a hypothesis of the catalog ${score.catalog}.`);\n if (!(bar >= 0 && bar <= 1)) problems.push(`The bar of \"${hypothesis}\" is ${bar}. A bar is from 0 to 1.`);\n }\n if (problems.length > 0) return err(problems);\n const verdicts: LabVerdict[] = [];\n const unbarred: string[] = [];\n for (const scores of score.classes) {\n const bar = bars[scores.label];\n if (bar === undefined) {\n unbarred.push(scores.label);\n continue;\n }\n const low = scores.support === 0 ? undefined : scores.recallInterval.low;\n const status: LabStatus = low === undefined ? \"no-read\" : low >= bar ? \"pass\" : \"fail\";\n verdicts.push({ hypothesis: scores.label, status, bar, low, support: scores.support });\n }\n return ok({ pass: verdicts.length > 0 && verdicts.every((verdict) => verdict.status === \"pass\"), verdicts, unbarred });\n}\n"],"mappings":"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"}
@@ -3,5 +3,6 @@
3
3
  *
4
4
  * This entry imports no `node:` module, so a browser can also use it.
5
5
  */
6
- import { A as LAYERS, C as Strength, D as expand, E as diagnose, M as isLayer, N as layerRank, O as explain, S as STRENGTHS, T as compileCatalog, _ as NONE, a as Catalog, b as Prediction, c as Contribution, d as Evidence, f as Expectation, g as Hypothesis, h as Feature, i as STANDARD_FEATURES, j as Layer, k as predictive, l as Diagnosis, m as Fault, n as STANDARD_CATALOG_SPEC, o as CatalogSpec, p as FLOOR, r as STANDARD_FAULTS, s as Cause, t as STANDARD_CATALOG, u as Distribution, v as PARTS, w as UNKNOWN, x as Reach, y as Part } from "./index-CP1hzELI.js";
7
- export { type Catalog, type CatalogSpec, type Cause, type Contribution, type Diagnosis, type Distribution, type Evidence, type Expectation, FLOOR, type Fault, type Feature, type Hypothesis, LAYERS, type Layer, NONE, PARTS, type Part, type Prediction, type Reach, STANDARD_CATALOG, STANDARD_CATALOG_SPEC, STANDARD_FAULTS, STANDARD_FEATURES, STRENGTHS, type Strength, UNKNOWN, compileCatalog, diagnose, expand, explain, isLayer, layerRank, predictive };
6
+ import { i as layerRank, n as Layer, r as isLayer, t as LAYERS } from "./layer-BNg0VYuK.js";
7
+ import { A as Part, B as predictive, C as Expectation, D as Hypothesis, E as Feature, F as UNKNOWN, I as compileCatalog, L as diagnose, M as Reach, N as STRENGTHS, O as NONE, P as Strength, R as expand, S as Evidence, T as Fault, _ as CatalogSpec, a as LabRecord, b as Diagnosis, c as LabVerdict, d as scoreLab, f as STANDARD_CATALOG, g as Catalog, h as STANDARD_FEATURES, i as LabGate, j as Prediction, k as PARTS, l as labGate, m as STANDARD_FAULTS, n as LabBars, o as LabScore, p as STANDARD_CATALOG_SPEC, r as LabCause, s as LabStatus, t as LAB_RECORD_FORMAT, u as labRecordOf, v as Cause, w as FLOOR, x as Distribution, y as Contribution, z as explain } from "./index-K2dGiYxW.js";
8
+ export { type Catalog, type CatalogSpec, type Cause, type Contribution, type Diagnosis, type Distribution, type Evidence, type Expectation, FLOOR, type Fault, type Feature, type Hypothesis, LAB_RECORD_FORMAT, LAYERS, type LabBars, type LabCause, type LabGate, type LabRecord, type LabScore, type LabStatus, type LabVerdict, type Layer, NONE, PARTS, type Part, type Prediction, type Reach, STANDARD_CATALOG, STANDARD_CATALOG_SPEC, STANDARD_FAULTS, STANDARD_FEATURES, STRENGTHS, type Strength, UNKNOWN, compileCatalog, diagnose, expand, explain, isLayer, labGate, labRecordOf, layerRank, predictive, scoreLab };
@@ -1 +1 @@
1
- export { a as FLOOR, h as LAYERS, o as NONE, s as PARTS, t as STANDARD_CATALOG, n as STANDARD_CATALOG_SPEC, r as STANDARD_FAULTS, i as STANDARD_FEATURES, c as STRENGTHS, l as UNKNOWN, u as compileCatalog, d as diagnose, f as expand, p as explain, g as isLayer, _ as layerRank, m as predictive } from "./diagnosis-BTa-Dfwl.js";
1
+ export { l as FLOOR, t as LAB_RECORD_FORMAT, y as LAYERS, u as NONE, d as PARTS, a as STANDARD_CATALOG, o as STANDARD_CATALOG_SPEC, s as STANDARD_FAULTS, c as STANDARD_FEATURES, f as STRENGTHS, p as UNKNOWN, m as compileCatalog, h as diagnose, g as expand, _ as explain, b as isLayer, n as labGate, r as labRecordOf, x as layerRank, v as predictive, i as scoreLab } from "./diagnosis-a4hRzFxp.js";
@@ -1,24 +1,6 @@
1
+ import { n as Layer } from "./layer-BNg0VYuK.js";
1
2
  import { Result } from "@mark1russell7/optional";
2
- /**
3
- * The layers of the network stack that netdiag examines, from the bottom to the top.
4
- *
5
- * - `link`: the network interface and its physical link.
6
- * - `wifi`: the Wi-Fi link: the signal, the channel and the rate.
7
- * - `ip`: the addresses of the interfaces.
8
- * - `route`: the routes of this computer and its gateway.
9
- * - `path`: the hops through the provider networks to the remote host.
10
- * - `dns`: the resolution of names to addresses.
11
- * - `tcp`: the connections to a port.
12
- * - `tls`: the handshake and the certificate of the server.
13
- * - `http`: the request and the response.
14
- */
15
- declare const LAYERS: readonly ["link", "wifi", "ip", "route", "path", "dns", "tcp", "tls", "http"];
16
- /** A layer of the network stack. The list `LAYERS` gives each layer in its order. */
17
- type Layer = (typeof LAYERS)[number];
18
- /** This function tells if a value is the name of a layer. */
19
- declare function isLayer(value: unknown): value is Layer;
20
- /** This function gives the position of a layer in `LAYERS`. The bottom layer `link` has the position 0. */
21
- declare function layerRank(layer: Layer): number;
3
+ import { ClassScore, Confusion, Proportion } from "@mark1russell7/rigor-statistics";
22
4
  /** A probability for each value of a feature. The probabilities are more than 0, and their sum is 1. */
23
5
  type Distribution = Readonly<Record<string, number>>;
24
6
  /**
@@ -194,5 +176,84 @@ declare const STANDARD_FAULTS: readonly Fault[];
194
176
  declare const STANDARD_CATALOG_SPEC: CatalogSpec;
195
177
  /** The standard catalog, compiled. */
196
178
  declare const STANDARD_CATALOG: Catalog;
197
- export { LAYERS as A, Strength as C, expand as D, diagnose as E, isLayer as M, layerRank as N, explain as O, STRENGTHS as S, compileCatalog as T, NONE as _, Catalog as a, Prediction as b, Contribution as c, Evidence as d, Expectation as f, Hypothesis as g, Feature as h, STANDARD_FEATURES as i, Layer as j, predictive as k, Diagnosis as l, Fault as m, STANDARD_CATALOG_SPEC as n, CatalogSpec as o, FLOOR as p, STANDARD_FAULTS as r, Cause as s, STANDARD_CATALOG as t, Distribution as u, PARTS as v, UNKNOWN as w, Reach as x, Part as y };
198
- //# sourceMappingURL=index-CP1hzELI.d.ts.map
179
+ /** The format of a lab record. */
180
+ declare const LAB_RECORD_FORMAT = "netdiag.lab-record/1";
181
+ /** The probability that a diagnosis gives to one cause. */
182
+ interface LabCause {
183
+ readonly id: string;
184
+ readonly probability: number;
185
+ }
186
+ /** One run of a lab case: the hypothesis that the lab made true, and the diagnosis of netdiag. */
187
+ interface LabRecord {
188
+ readonly format: typeof LAB_RECORD_FORMAT;
189
+ /** The id of the case, for example `tls.cert-invalid/expired`. */
190
+ readonly case: string;
191
+ /** The version of the catalog that gave the diagnosis. */
192
+ readonly catalog: string;
193
+ /** The hypothesis that the lab made true: a fault of the catalog, `none` or `unknown`. */
194
+ readonly truth: string;
195
+ /** The most probable cause of the diagnosis. */
196
+ readonly predicted: string;
197
+ /** The rank of the truth in the causes, from 1. */
198
+ readonly rank: number;
199
+ /** The probability of each cause, from the most probable. */
200
+ readonly causes: readonly LabCause[];
201
+ }
202
+ /**
203
+ * This function makes the record of one run of a lab case. The truth must be a hypothesis of the catalog, and the
204
+ * diagnosis must have a cause.
205
+ */
206
+ declare function labRecordOf(catalog: Catalog, caseId: string, truth: string, diagnosis: Diagnosis): Result<LabRecord, string>;
207
+ /** The scores of the lab records of one catalog. */
208
+ interface LabScore {
209
+ /** The version of the catalog. */
210
+ readonly catalog: string;
211
+ /** The number of records. */
212
+ readonly n: number;
213
+ /** The number of records where the predicted cause is the truth. */
214
+ readonly correct: number;
215
+ /** The Wilson interval of the part of the records where the predicted cause is the truth. */
216
+ readonly accuracy: Proportion;
217
+ /** The confusion table. It has a row for each hypothesis of the catalog, in the order of the catalog. */
218
+ readonly table: Confusion<string>;
219
+ /** The scores of each hypothesis, in the order of the catalog. */
220
+ readonly classes: readonly ClassScore<string>[];
221
+ /** The mean Brier score of the records: the squared distance of the probabilities from the truth, from 0 to 2. */
222
+ readonly brier: number | undefined;
223
+ }
224
+ /**
225
+ * This function scores the lab records of one catalog. The recall of a hypothesis is the part of its runs where the
226
+ * diagnosis names it. A record of a different catalog version, or with a hypothesis that is not in the catalog, is an error.
227
+ */
228
+ declare function scoreLab(catalog: Catalog, records: readonly LabRecord[]): Result<LabScore, string[]>;
229
+ /** The least lower end of the Wilson interval of the recall, for each hypothesis that the gate holds. */
230
+ type LabBars = Readonly<Record<string, number>>;
231
+ /** The verdict of the gate on one hypothesis: `no-read` when no record has it as the truth. */
232
+ type LabStatus = "pass" | "fail" | "no-read";
233
+ /** The verdict of the gate on one hypothesis. */
234
+ interface LabVerdict {
235
+ readonly hypothesis: string;
236
+ readonly status: LabStatus;
237
+ /** The bar of the hypothesis. */
238
+ readonly bar: number;
239
+ /** The lower end of the recall interval. It is `undefined` when no record has the hypothesis as the truth. */
240
+ readonly low: number | undefined;
241
+ /** The number of records with the hypothesis as the truth. */
242
+ readonly support: number;
243
+ }
244
+ /** The result of the accuracy gate. */
245
+ interface LabGate {
246
+ /** It is `true` only when each verdict is `pass`. A hypothesis with no read does not pass. */
247
+ readonly pass: boolean;
248
+ /** The verdicts, in the order of the catalog. */
249
+ readonly verdicts: readonly LabVerdict[];
250
+ /** The hypotheses with no bar, for example a fault that no lab can make. */
251
+ readonly unbarred: readonly string[];
252
+ }
253
+ /**
254
+ * This function holds the recall of each hypothesis with a bar. A hypothesis passes when the lower end of its
255
+ * recall interval is the bar or more. A bar for a hypothesis that is not in the score is an error.
256
+ */
257
+ declare function labGate(score: LabScore, bars: LabBars): Result<LabGate, string[]>;
258
+ export { Part as A, predictive as B, Expectation as C, Hypothesis as D, Feature as E, UNKNOWN as F, compileCatalog as I, diagnose as L, Reach as M, STRENGTHS as N, NONE as O, Strength as P, expand as R, Evidence as S, Fault as T, CatalogSpec as _, LabRecord as a, Diagnosis as b, LabVerdict as c, scoreLab as d, STANDARD_CATALOG as f, Catalog as g, STANDARD_FEATURES as h, LabGate as i, Prediction as j, PARTS as k, labGate as l, STANDARD_FAULTS as m, LabBars as n, LabScore as o, STANDARD_CATALOG_SPEC as p, LabCause as r, LabStatus as s, LAB_RECORD_FORMAT as t, labRecordOf as u, Cause as v, FLOOR as w, Distribution as x, Contribution as y, explain as z };
259
+ //# sourceMappingURL=index-K2dGiYxW.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index-K2dGiYxW.d.ts","names":[],"sources":["../src/diagnosis/model.ts","../src/diagnosis/catalog.ts","../src/diagnosis/score.ts"],"mappings":";;;;KAIY,eAAe,SAAS;;;;;;UAOnB;WACN;WACA,OAAO;WACP;WACA;WACA,SAAS;;;cAIP;;KAGD,eAAe;;;;;;;KAQf;;;;;KAMA;;cAGC,WAAW,SAAS,OAAO;;;;;;KAO5B,aAAa,eAAe;;UAGvB;WACN;WACA,UAAU;;;;;;;;UAaJ;WACN;WACA,MAAM;WACN,OAAO;;WAEP;;WAEA;WACA;WACA,UAAU,SAAS,eAAe;;;UAI5B;WACN;WACA,mBAAmB;WACnB,iBAAiB;WACjB;aAAoB;aAAoC;;;;;;;;;;UAUlD;WACN;WACA;WACA;WACA;WACA,OAAO;WACP,QAAQ;;;cAIN;;cAEA;;UAGI;WACN;WACA,UAAU,oBAAoB;WAC9B,qBAAqB;;EAE9B,WAAW,oBAAoB,iBAAiB,eAAe;EAC/D,MAAM;;;cAIK;;iBAkBG,OAAO,SAAS,SAAS,YAAY,aAAa,OAAO;;;;;;iBAqBzD,eAAe,MAAM,cAAc,OAAO;;;;;;;UAwEzC;WACN;WACA;WACA;WACA;;;UAIM,cAAc;WACpB;;;UAIM;WACN,iBAAiB;WACjB,eAAe;WACf;aAA6B,UAAU;aAAmB;;;;;;;;;;iBAsBrD,SAAS,SAAS,SAAS,mBAAmB,aAAa;;UAoB1D;WACN,UAAU;;WAEV;;;;;;;;iBASK,QAAQ,SAAS,SAAS,mBAAmB,YAAY,oBAAoB,kBAAwB;;;;;iBAerG,WAAW,SAAS,SAAS,WAAW,WAAW,iBAAiB,mBAAmB;;;;;;;cCjT1F,4BAA4B;;;;;cAgH5B,0BAA0B;;cAiO1B,uBAAuB;;cAOvB,kBAAkB;;cC3VlB;;UAGI;WACN;WACA;;;UAIM;WACN,eAAe;;WAEf;;WAEA;;WAEA;;WAEA;;WAEA;;WAEA,iBAAiB;;;;;;iBAOZ,YAAY,SAAS,SAAS,gBAAgB,eAAe,WAAW,YAAY,OAAO;;UAkB1F;;WAEN;;WAEA;;WAEA;;WAEA,UAAU;;WAEV,OAAO;;WAEP,kBAAkB;;WAElB;;;;;;iBAOK,SAAS,SAAS,SAAS,kBAAkB,cAAc,OAAO;;KAkCtE,UAAU,SAAS;;KAGnB;;UAGK;WACN;WACA,QAAQ;;WAER;;WAEA;;WAEA;;;UAIM;;WAEN;;WAEA,mBAAmB;;WAEnB;;;;;;iBAOK,QAAQ,OAAO,UAAU,MAAM,UAAU,OAAO"}