@mark1russell7/netdiag 0.0.0-bootstrap.0 → 0.1.0
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/LICENSE +21 -0
- package/README.md +91 -1
- package/build/diagnosis-BTa-Dfwl.js +820 -0
- package/build/diagnosis-BTa-Dfwl.js.map +1 -0
- package/build/diagnosis.d.ts +7 -0
- package/build/diagnosis.js +1 -0
- package/build/index-CP1hzELI.d.ts +198 -0
- package/build/index-CP1hzELI.d.ts.map +1 -0
- package/build/index.d.ts +287 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +436 -0
- package/build/index.js.map +1 -0
- package/package.json +63 -4
- package/src/diagnosis/catalog.ts +357 -0
- package/src/diagnosis/index.ts +34 -0
- package/src/diagnosis/model.ts +322 -0
- package/src/fact.ts +87 -0
- package/src/index.ts +33 -0
- package/src/layer.ts +31 -0
- package/src/probe.ts +61 -0
- package/src/run.ts +163 -0
- package/src/scope.ts +171 -0
- package/src/target.ts +116 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diagnosis-BTa-Dfwl.js","names":[],"sources":["../src/layer.ts","../src/diagnosis/model.ts","../src/diagnosis/catalog.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.\",\n values: [\"ok\", \"nxdomain\", \"servfail\", \"refused\", \"timeout\"],\n healthy: { ok: 0.97, nxdomain: 0.01, servfail: 0.005, refused: 0.005, timeout: 0.01 },\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 4193 or loopback).\",\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 (above 8).\",\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 (above 20).\",\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\", 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: \"1\",\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"],"mappings":";;;;;;;;;;;;;;;AAaA,MAAa,SAAS;CAAC;CAAQ;CAAQ;CAAM;CAAS;CAAQ;CAAO;CAAO;CAAO;AAAM;AAKzF,MAAM,QAAqC,IAAI,IAAI,OAAO,KAAK,OAAO,SAAS,CAAC,OAAO,IAAI,CAAC,CAAC;;AAG7F,SAAgB,QAAQ,OAAgC;CACtD,OAAO,OAAO,UAAU,YAAY,MAAM,IAAI,KAAK;AACrD;;AAGA,SAAgB,UAAU,OAAsB;CAC9C,MAAM,OAAO,MAAM,IAAI,KAAK;CAC5B,IAAI,SAAS,KAAA,GAAW,MAAM,IAAI,WAAW,IAAI,OAAO,KAAK,EAAE,iBAAiB;CAChF,OAAO;AACT;;;ACVA,MAAa,QAAQ;CAAC;CAAa;CAAQ;CAAW;CAAY;CAAQ;AAAS;;AAoBnF,MAAa,YAAgD;CAAE,MAAM;CAAM,QAAQ;CAAK,OAAO;CAAM,MAAM;AAAI;AAe/G,SAAS,cAAc,YAAmD;CACxE,OAAO,OAAO,WAAW,aAAa;AACxC;;AA6CA,MAAa,OAAO;;AAEpB,MAAa,UAAU;;AAavB,MAAa,QAAQ;AAErB,MAAM,gBAAgB;AAEtB,SAAS,qBAAqB,OAAe,SAAkB,MAA8B;CAC3F,MAAM,WAAqB,CAAC;CAC5B,KAAK,MAAM,SAAS,OAAO,KAAK,IAAI,GAAG,IAAI,CAAC,QAAQ,OAAO,SAAS,KAAK,GAAG,SAAS,KAAK,GAAG,MAAM,KAAK,MAAM,sBAAsB,QAAQ,GAAG,EAAE;CACjJ,KAAK,MAAM,SAAS,QAAQ,QAAQ;EAClC,MAAM,IAAI,KAAK;EACf,IAAI,MAAM,KAAA,GAAW,SAAS,KAAK,GAAG,MAAM,eAAe,MAAM,OAAO,QAAQ,GAAG,qBAAqB;OACnG,IAAI,CAAC,OAAO,SAAS,CAAC,KAAK,IAAA,MAAW,SAAS,KAAK,GAAG,MAAM,wBAAwB,MAAM,OAAO,EAAE,eAAe,MAAM,UAAU;CAC1I;CACA,MAAM,MAAM,OAAO,OAAO,IAAI,CAAC,CAAC,QAAQ,GAAG,MAAM,IAAI,GAAG,CAAC;CACzD,IAAI,KAAK,IAAI,MAAM,CAAC,IAAI,eAAe,SAAS,KAAK,GAAG,MAAM,oCAAoC,IAAI,SAAS;CAC/G,OAAO;AACT;;AAGA,SAAgB,OAAO,SAAkB,YAAwD;CAC/F,IAAI,CAAC,cAAc,UAAU,GAAG,OAAO,GAAG,UAAU;CACpD,MAAM,WAAW,OAAO,WAAW,WAAW,WAAW,CAAC,WAAW,MAAM,IAAI,CAAC,GAAG,WAAW,MAAM;CACpG,MAAM,UAAU,SAAS,QAAQ,UAAU,CAAC,QAAQ,OAAO,SAAS,KAAK,CAAC;CAC1E,IAAI,QAAQ,SAAS,GAAG,OAAO,IAAI,QAAQ,KAAK,UAAU,IAAI,MAAM,sBAAsB,QAAQ,GAAG,EAAE,CAAC;CACxG,IAAI,SAAS,WAAW,KAAK,IAAI,IAAI,QAAQ,CAAC,CAAC,SAAS,SAAS,QAAQ,OAAO,IAAI,CAAC,0BAA0B,QAAQ,GAAG,wBAAwB,CAAC;CACnJ,MAAM,SAAS,QAAQ,OAAO,QAAQ,UAAU,CAAC,SAAS,SAAS,KAAK,CAAC;CACzE,MAAM,OAAO,OAAO,WAAW,IAAI,IAAI,UAAU,WAAW;CAC5D,MAAM,OAA+B,CAAC;CACtC,KAAK,MAAM,SAAS,UAAU,KAAK,SAAS,OAAO,SAAS;CAC5D,KAAK,MAAM,SAAS,QAAQ,KAAK,UAAU,IAAI,QAAQ,OAAO;CAC9D,OAAO,GAAG,IAAI;AAChB;AAEA,MAAM,KAAK;;;;;;AAOX,SAAgB,eAAe,MAA8C;CAC3E,MAAM,WAAqB,CAAC;CAC5B,MAAM,2BAAW,IAAI,IAAqB;CAC1C,KAAK,MAAM,WAAW,KAAK,UAAU;EACnC,IAAI,CAAC,GAAG,KAAK,QAAQ,EAAE,GAAG,SAAS,KAAK,IAAI,QAAQ,GAAG,6BAA6B;EACpF,IAAI,SAAS,IAAI,QAAQ,EAAE,GAAG,SAAS,KAAK,eAAe,QAAQ,GAAG,cAAc;EACpF,IAAI,QAAQ,OAAO,SAAS,KAAK,IAAI,IAAI,QAAQ,MAAM,CAAC,CAAC,SAAS,QAAQ,OAAO,QAC/E,SAAS,KAAK,eAAe,QAAQ,GAAG,yCAAyC;EAEnF,SAAS,KAAK,GAAG,qBAAqB,cAAc,QAAQ,MAAM,SAAS,QAAQ,OAAO,CAAC;EAC3F,SAAS,IAAI,QAAQ,IAAI,OAAO;CAClC;CACA,MAAM,aAA2B,CAAC;EAAE,IAAI;EAAM,MAAM;EAAQ,SAAS;EAA6B,MAAM;CAAwB,CAAC;CACjI,MAAM,yBAAS,IAAI,IAAoB,CAAC,CAAC,MAAM,KAAK,QAAQ,QAAQ,CAAC,CAAC,CAAC;CACvE,MAAM,yBAAS,IAAI,IAAuC;CAC1D,KAAK,MAAM,SAAS,KAAK,QAAQ;EAC/B,IAAI,CAAC,GAAG,KAAK,MAAM,EAAE,KAAK,MAAM,OAAA,UAAe,MAAM,OAAA,WAAgB,SAAS,KAAK,IAAI,MAAM,GAAG,2BAA2B;EAC3H,IAAI,OAAO,IAAI,MAAM,EAAE,GAAG,SAAS,KAAK,aAAa,MAAM,GAAG,cAAc;EAC5E,OAAO,IAAI,MAAM,IAAI,MAAM,SAAS,CAAC;EACrC,WAAW,KAAK;GAAE,IAAI,MAAM;GAAI,MAAM;GAAS,SAAS,MAAM;GAAS,MAAM,MAAM;GAAM,MAAM,MAAM;GAAM,OAAO,MAAM;EAAM,CAAC;EAC/H,MAAM,wBAAQ,IAAI,IAA0B;EAC5C,KAAK,MAAM,CAAC,WAAW,eAAe,OAAO,QAAQ,MAAM,QAAQ,GAAG;GACpE,MAAM,UAAU,SAAS,IAAI,SAAS;GACtC,IAAI,YAAY,KAAA,GAAW;IACzB,SAAS,KAAK,aAAa,MAAM,GAAG,gCAAgC,UAAU,EAAE;IAChF;GACF;GACA,MAAM,OAAO,OAAO,SAAS,UAAU;GACvC,IAAI,KAAK,QAAQ,OAAO,SAAS,KAAK,GAAG,KAAK,MAAM,KAAK,YAAY,GAAG,MAAM,GAAG,IAAI,SAAS,CAAC;QAC1F;IACH,SAAS,KAAK,GAAG,qBAAqB,GAAG,MAAM,GAAG,OAAO,aAAa,SAAS,KAAK,KAAK,CAAC;IAC1F,MAAM,IAAI,WAAW,KAAK,KAAK;GACjC;EACF;EACA,IAAI,MAAM,SAAS,GAAG,SAAS,KAAK,aAAa,MAAM,GAAG,sBAAsB;EAChF,OAAO,IAAI,MAAM,IAAI,KAAK;CAC5B;CACA,WAAW,KAAK;EACd,IAAI;EACJ,MAAM;EACN,SAAS;EACT,MAAM;CACR,CAAC;CACD,OAAO,IAAI,SAAS,KAAK,QAAQ,WAAW,CAAC;CAC7C,KAAK,MAAM,CAAC,IAAI,UAAU,QAAQ,IAAI,CAAC,OAAO,SAAS,KAAK,KAAK,SAAS,GAAG,SAAS,KAAK,gBAAgB,GAAG,MAAM,MAAM,0BAA0B;CACpJ,IAAI,SAAS,SAAS,GAAG,OAAO,IAAI,QAAQ;CAE5C,MAAM,QAAQ,CAAC,GAAG,OAAO,OAAO,CAAC,CAAC,CAAC,QAAQ,GAAG,MAAM,IAAI,GAAG,CAAC;CAC5D,MAAM,QAAQ,IAAI,IAAI,WAAW,KAAK,MAAM,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC;CAC5D,OAAO,GAAG;EACR,SAAS,KAAK;EACd;EACA;EACA,QAAQ,gBAAgB,OAAO,IAAI,UAAU,KAAK,KAAK;EACvD,WAAW,YAAY,WAAW,OAAO,SAAS;GAChD,MAAM,UAAU,SAAS,IAAI,SAAS;GACtC,IAAI,YAAY,KAAA,KAAa,CAAC,QAAQ,OAAO,SAAS,KAAK,GAAG,OAAO;GACrE,IAAI,eAAA,WAAwB,OAAO,IAAI,QAAQ,OAAO;GAEtD,MAAM,UADQ,OAAO,IAAI,UACL,CAAC,EAAE,IAAI,SAAS;GAEpC,SADgB,WAAW,MAAM,IAAI,UAAU,MAAM,aAClC,YAAY,KAAA,IAAY,UAAU,QAAQ,QAAA,CAAS,UAAU;EAClF;CACF,CAAC;AACH;AA2BA,SAAS,OAAO,SAAkB,UAAoE;CACpG,MAAM,OAAmB,CAAC;CAC1B,MAAM,UAAoD,CAAC;CAC3D,KAAK,MAAM,QAAQ,UAAU;EAC3B,MAAM,UAAU,QAAQ,SAAS,IAAI,KAAK,OAAO;EACjD,IAAI,YAAY,KAAA,GAAW,QAAQ,KAAK;GAAE,UAAU;GAAM,QAAQ,8BAA8B,KAAK,QAAQ;EAAG,CAAC;OAC5G,IAAI,CAAC,QAAQ,OAAO,SAAS,KAAK,KAAK,GAAG,QAAQ,KAAK;GAAE,UAAU;GAAM,QAAQ,IAAI,KAAK,MAAM,sBAAsB,KAAK,QAAQ;EAAG,CAAC;OACvI,KAAK,KAAK,IAAI;CACrB;CACA,OAAO;EAAE;EAAM;CAAQ;AACzB;;;;;;;;AASA,SAAgB,SAAS,SAAkB,UAA0C;CACnF,MAAM,EAAE,MAAM,YAAY,OAAO,SAAS,QAAQ;CAClD,MAAM,OAAO,QAAQ,WAAW,KAAK,MAAM;EACzC,IAAI,MAAM,KAAK,IAAI,QAAQ,MAAM,EAAE,EAAE,CAAC;EACtC,KAAK,MAAM,QAAQ,MAAM,OAAO,KAAK,IAAI,QAAQ,WAAW,EAAE,IAAI,KAAK,SAAS,KAAK,OAAO,KAAK,OAAO,CAAC;EACzG,OAAO;CACT,CAAC;CACD,MAAM,MAAM,KAAK,IAAI,GAAG,IAAI;CAC5B,MAAM,UAAU,KAAK,KAAK,QAAQ,KAAK,IAAI,MAAM,GAAG,CAAC;CACrD,MAAM,QAAQ,QAAQ,QAAQ,GAAG,MAAM,IAAI,GAAG,CAAC;CAO/C,OAAO;EAAE,QAJM,QAAQ,WACpB,KAAK,GAAG,OAAO;GAAE,GAAG;GAAG,cAAc,QAAQ,MAAM,KAAK;GAAO,OAAO;EAAE,EAAE,CAAC,CAC3E,MAAM,GAAG,MAAM,EAAE,cAAc,EAAE,eAAe,EAAE,QAAQ,EAAE,KAAK,CAAC,CAClE,KAAK,EAAE,OAAO,QAAQ,GAAG,YAAY,KAC1B;EAAG;EAAM;CAAQ;AACjC;;;;;;;AAeA,SAAgB,QAAQ,SAAkB,UAA+B,YAAoB,SAAiB,MAAsB;CAClI,MAAM,EAAE,SAAS,OAAO,SAAS,QAAQ;CACzC,OAAO,KACJ,KAAK,UAAU;EACd,UAAU;EACV,QAAQ,KAAK,MAAM,QAAQ,WAAW,YAAY,KAAK,SAAS,KAAK,OAAO,KAAK,OAAO,CAAC,IACvF,KAAK,MAAM,QAAQ,WAAW,QAAQ,KAAK,SAAS,KAAK,OAAO,KAAK,OAAO,CAAC;CACjF,EAAE,CAAC,CACF,MAAM,GAAG,MAAM,KAAK,IAAI,EAAE,MAAM,IAAI,KAAK,IAAI,EAAE,MAAM,CAAC;AAC3D;;;;;AAMA,SAAgB,WAAW,SAAkB,WAAsB,SAAiB,SAAgC;CAClH,MAAM,QAAQ,QAAQ,SAAS,IAAI,OAAO;CAC1C,IAAI,UAAU,KAAA,GAAW,MAAM,IAAI,WAAW,8BAA8B,SAAS;CACrF,MAAM,OAA+B,CAAC;CACtC,KAAK,MAAM,SAAS,MAAM,QACxB,KAAK,SAAS,UAAU,OAAO,QAAQ,KAAK,UAAU,MAAM,MAAM,cAAc,QAAQ,WAAW,MAAM,IAAI,SAAS,OAAO,OAAO,GAAG,CAAC;CAE1I,OAAO;AACT;;;;;;;;ACzTA,MAAa,oBAAwC;CACnD;EACE,IAAI;EACJ,OAAO;EACP,SAAS;EACT,QAAQ,CAAC,MAAM,MAAM;EACrB,SAAS;GAAE,IAAI;GAAO,MAAM;EAAM;CACpC;CACA;EACE,IAAI;EACJ,OAAO;EACP,SAAS;EACT,QAAQ;GAAC;GAAU;GAAQ;EAAM;EACjC,SAAS;GAAE,QAAQ;GAAK,MAAM;GAAM,MAAM;EAAK;CACjD;CACA;EACE,IAAI;EACJ,OAAO;EACP,SAAS;EACT,QAAQ,CAAC,SAAS,MAAM;EACxB,SAAS;GAAE,OAAO;GAAM,MAAM;EAAK;CACrC;CACA;EACE,IAAI;EACJ,OAAO;EACP,SAAS;EACT,QAAQ;GAAC;GAAQ;GAAQ;EAAM;EAC/B,SAAS;GAAE,MAAM;GAAK,MAAM;GAAM,MAAM;EAAK;CAC/C;CACA;EACE,IAAI;EACJ,OAAO;EACP,SAAS;EACT,QAAQ;GAAC;GAAM;GAAY;GAAY;GAAW;EAAS;EAC3D,SAAS;GAAE,IAAI;GAAM,UAAU;GAAM,UAAU;GAAO,SAAS;GAAO,SAAS;EAAK;CACtF;CACA;EACE,IAAI;EACJ,OAAO;EACP,SAAS;EACT,QAAQ,CAAC,UAAU,SAAS;EAC5B,SAAS;GAAE,QAAQ;GAAM,SAAS;EAAK;CACzC;CACA;EACE,IAAI;EACJ,OAAO;EACP,SAAS;EACT,QAAQ;GAAC;GAAQ;GAAa;EAAW;EACzC,SAAS;GAAE,MAAM;GAAK,aAAa;GAAM,aAAa;EAAK;CAC7D;CACA;EACE,IAAI;EACJ,OAAO;EACP,SAAS;EACT,QAAQ;GAAC;GAAS;GAAQ;EAAa;EACvC,SAAS;GAAE,OAAO;GAAM,MAAM;GAAM,aAAa;EAAK;CACxD;CACA;EACE,IAAI;EACJ,OAAO;EACP,SAAS;EACT,QAAQ;GAAC;GAAa;GAAW;GAAW;GAAe;EAAO;EAClE,SAAS;GAAE,WAAW;GAAM,SAAS;GAAM,SAAS;GAAM,aAAa;GAAO,OAAO;EAAM;CAC7F;CACA;EACE,IAAI;EACJ,OAAO;EACP,SAAS;EACT,QAAQ;GAAC;GAAS;GAAQ;EAAO;EACjC,SAAS;GAAE,OAAO;GAAK,MAAM;GAAM,OAAO;EAAK;CACjD;CACA;EACE,IAAI;EACJ,OAAO;EACP,SAAS;EACT,QAAQ;GAAC;GAAM;GAAc;GAAS;GAAW;EAAO;EACxD,SAAS;GAAE,IAAI;GAAM,cAAc;GAAM,OAAO;GAAM,SAAS;GAAO,OAAO;EAAM;CACrF;CACA;EACE,IAAI;EACJ,OAAO;EACP,SAAS;EACT,QAAQ;GAAC;GAAO;GAAO;EAAM;EAC7B,SAAS;GAAE,KAAK;GAAM,KAAK;GAAK,MAAM;EAAK;CAC7C;CACA;EACE,IAAI;EACJ,OAAO;EACP,SAAS;EACT,QAAQ,CAAC,UAAU,SAAS;EAC5B,SAAS;GAAE,QAAQ;GAAM,SAAS;EAAK;CACzC;CACA;EACE,IAAI;EACJ,OAAO;EACP,SAAS;EACT,QAAQ;GAAC;GAAM;GAAgB;GAAgB;EAAM;EACrD,SAAS;GAAE,IAAI;GAAM,gBAAgB;GAAM,gBAAgB;GAAM,MAAM;EAAK;CAC9E;CACA;EACE,IAAI;EACJ,OAAO;EACP,SAAS;EACT,QAAQ;GAAC;GAAO;GAAO;EAAM;EAC7B,SAAS;GAAE,KAAK;GAAK,KAAK;GAAK,MAAM;EAAI;CAC3C;AACF;;;;;AAMA,MAAa,kBAAoC;CAC/C;EACE,IAAI;EACJ,MAAM;EACN,OAAO;EACP,SAAS;EACT,MAAM;EACN,UAAU;GACR,cAAc;IAAE,QAAQ;IAAQ,UAAU;GAAO;GACjD,iBAAiB;IAAE,QAAQ;IAAQ,UAAU;GAAO;GACpD,cAAc;IAAE,QAAQ;IAAW,UAAU;GAAS;GACtD,cAAc;IAAE,QAAQ,CAAC,QAAQ,aAAa;IAAG,UAAU;GAAO;GAClE,eAAe;IAAE,QAAQ,CAAC,eAAe,SAAS;IAAG,UAAU;GAAO;EACxE;CACF;CACA;EACE,IAAI;EACJ,MAAM;EACN,OAAO;EACP,SAAS;EACT,MAAM;EACN,UAAU;GACR,eAAe;IAAE,QAAQ;IAAQ,UAAU;GAAS;GACpD,aAAa;IAAE,QAAQ,CAAC,QAAQ,MAAM;IAAG,UAAU;GAAQ;GAC3D,YAAY;IAAE,QAAQ,CAAC,QAAQ,OAAO;IAAG,UAAU;GAAQ;EAC7D;CACF;CACA;EACE,IAAI;EACJ,MAAM;EACN,OAAO;EACP,SAAS;EACT,MAAM;EACN,UAAU;GACR,iBAAiB;IAAE,QAAQ;IAAQ,UAAU;GAAS;GACtD,cAAc;IAAE,QAAQ;IAAW,UAAU;GAAS;GACtD,cAAc;IAAE,QAAQ,CAAC,QAAQ,aAAa;IAAG,UAAU;GAAS;GACpE,eAAe;IAAE,QAAQ,CAAC,WAAW,aAAa;IAAG,UAAU;GAAS;EAC1E;CACF;CACA;EACE,IAAI;EACJ,MAAM;EACN,OAAO;EACP,SAAS;EACT,MAAM;EACN,UAAU;GACR,eAAe;IAAE,QAAQ;IAAW,UAAU;GAAQ;GACtD,iBAAiB;IAAE,QAAQ;IAAc,UAAU;GAAS;GAC5D,aAAa;IAAE,QAAQ;IAAW,UAAU;GAAQ;EACtD;CACF;CACA;EACE,IAAI;EACJ,MAAM;EACN,OAAO;EACP,SAAS;EACT,MAAM;EACN,UAAU,EACR,cAAc;GAAE,QAAQ;GAAW,UAAU;EAAS,EACxD;CACF;CACA;EACE,IAAI;EACJ,MAAM;EACN,OAAO;EACP,SAAS;EACT,MAAM;EACN,UAAU,EACR,cAAc;GAAE,QAAQ,CAAC,YAAY,SAAS;GAAG,UAAU;EAAS,EACtE;CACF;CACA;EACE,IAAI;EACJ,MAAM;EACN,OAAO;EACP,SAAS;EACT,MAAM;EACN,UAAU,EACR,cAAc;GAAE,QAAQ;GAAY,UAAU;EAAS,EACzD;CACF;CACA;EACE,IAAI;EACJ,MAAM;EACN,OAAO;EACP,SAAS;EACT,MAAM;EACN,UAAU,EACR,eAAe;GAAE,QAAQ;GAAW,UAAU;EAAS,EACzD;CACF;CACA;EACE,IAAI;EACJ,MAAM;EACN,OAAO;EACP,SAAS;EACT,MAAM;EACN,UAAU;GACR,eAAe;IAAE,QAAQ,CAAC,WAAW,aAAa;IAAG,UAAU;GAAS;GACxE,cAAc;IAAE,QAAQ,CAAC,QAAQ,aAAa;IAAG,UAAU;GAAS;EACtE;CACF;CACA;EACE,IAAI;EACJ,MAAM;EACN,OAAO;EACP,SAAS;EACT,MAAM;EACN,UAAU;GACR,eAAe;IAAE,QAAQ;IAAW,UAAU;GAAS;GACvD,cAAc;IAAE,OAAO;IAAK,MAAM;IAAM,aAAa;GAAK;EAC5D;CACF;CACA;EACE,IAAI;EACJ,MAAM;EACN,OAAO;EACP,SAAS;EACT,MAAM;EACN,UAAU,EACR,cAAc;GAAE,QAAQ;GAAQ,UAAU;EAAS,EACrD;CACF;CACA;EACE,IAAI;EACJ,MAAM;EACN,OAAO;EACP,SAAS;EACT,MAAM;EACN,UAAU;GACR,eAAe;IAAE,WAAW;IAAK,SAAS;IAAM,SAAS;IAAM,aAAa;IAAM,OAAO;GAAK;GAC9F,iBAAiB;IAAE,QAAQ;IAAS,UAAU;GAAS;EACzD;CACF;CACA;EACE,IAAI;EACJ,MAAM;EACN,OAAO;EACP,SAAS;EACT,MAAM;EACN,UAAU;GACR,aAAa;IAAE,QAAQ,CAAC,QAAQ,MAAM;IAAG,UAAU;GAAS;GAC5D,YAAY;IAAE,QAAQ,CAAC,QAAQ,OAAO;IAAG,UAAU;GAAQ;GAC3D,aAAa;IAAE,QAAQ,CAAC,OAAO,MAAM;IAAG,UAAU;GAAO;EAC3D;CACF;CACA;EACE,IAAI;EACJ,MAAM;EACN,OAAO;EACP,SAAS;EACT,MAAM;EACN,UAAU;GACR,iBAAiB;IAAE,QAAQ;IAAW,UAAU;GAAS;GACzD,eAAe;IAAE,QAAQ;IAAQ,UAAU;GAAQ;EACrD;CACF;CACA;EACE,IAAI;EACJ,MAAM;EACN,OAAO;EACP,SAAS;EACT,MAAM;EACN,UAAU;GACR,aAAa;IAAE,QAAQ;IAAa,UAAU;GAAS;GACvD,YAAY;IAAE,QAAQ,CAAC,QAAQ,OAAO;IAAG,UAAU;GAAO;EAC5D;CACF;CACA;EACE,IAAI;EACJ,MAAM;EACN,OAAO;EACP,SAAS;EACT,MAAM;EACN,UAAU;GACR,aAAa;IAAE,QAAQ,CAAC,OAAO,MAAM;IAAG,UAAU;GAAQ;GAC1D,aAAa;IAAE,QAAQ;IAAW,UAAU;GAAQ;GACpD,YAAY;IAAE,QAAQ;IAAS,UAAU;GAAS;EACpD;CACF;CACA;EACE,IAAI;EACJ,MAAM;EACN,OAAO;EACP,SAAS;EACT,MAAM;EACN,UAAU,EACR,iBAAiB;GAAE,QAAQ;GAAc,UAAU;EAAS,EAC9D;CACF;CACA;EACE,IAAI;EACJ,MAAM;EACN,OAAO;EACP,SAAS;EACT,MAAM;EACN,UAAU,EACR,iBAAiB;GAAE,QAAQ;GAAS,UAAU;EAAS,EACzD;CACF;CACA;EACE,IAAI;EACJ,MAAM;EACN,OAAO;EACP,SAAS;EACT,MAAM;EACN,UAAU;GACR,aAAa;IAAE,QAAQ;IAAQ,UAAU;GAAS;GAClD,aAAa;IAAE,QAAQ,CAAC,OAAO,MAAM;IAAG,UAAU;GAAO;EAC3D;CACF;CACA;EACE,IAAI;EACJ,MAAM;EACN,OAAO;EACP,SAAS;EACT,MAAM;EACN,UAAU,EACR,eAAe;GAAE,QAAQ;GAAgB,UAAU;EAAS,EAC9D;CACF;AACF;;AAGA,MAAa,wBAAqC;CAChD,SAAS;CACT,UAAU;CACV,QAAQ;AACV;;AAGA,MAAa,0BAAmC;CAC9C,MAAM,WAAW,eAAe,qBAAqB;CACrD,IAAI,SAAS,QAAQ,OAAO,MAAM,IAAI,MAAM,sCAAsC,SAAS,MAAM,KAAK,GAAG,GAAG;CAC5G,OAAO,SAAS;AAClB,EAAA,CAAG"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The diagnosis of netdiag: the model of the parts, the catalog of faults, and the probability of each cause.
|
|
3
|
+
*
|
|
4
|
+
* This entry imports no `node:` module, so a browser can also use it.
|
|
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 };
|
|
@@ -0,0 +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";
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
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;
|
|
22
|
+
/** A probability for each value of a feature. The probabilities are more than 0, and their sum is 1. */
|
|
23
|
+
type Distribution = Readonly<Record<string, number>>;
|
|
24
|
+
/**
|
|
25
|
+
* A feature is one discrete observation that the facts give, for example the outcome of a TCP connect.
|
|
26
|
+
*
|
|
27
|
+
* The distribution `healthy` gives the probability of each value when the network has no fault.
|
|
28
|
+
*/
|
|
29
|
+
interface Feature {
|
|
30
|
+
readonly id: string;
|
|
31
|
+
readonly layer: Layer;
|
|
32
|
+
readonly summary: string;
|
|
33
|
+
readonly values: readonly string[];
|
|
34
|
+
readonly healthy: Distribution;
|
|
35
|
+
}
|
|
36
|
+
/** The parts of the model. Each fault is in one part. */
|
|
37
|
+
declare const PARTS: readonly ["interface", "wifi", "gateway", "resolver", "path", "service"];
|
|
38
|
+
/** A part of the model: the network interface, the Wi-Fi link, the gateway, the DNS resolver, the path or the remote service. */
|
|
39
|
+
type Part = (typeof PARTS)[number];
|
|
40
|
+
/**
|
|
41
|
+
* The reach of a fault.
|
|
42
|
+
*
|
|
43
|
+
* - `shared`: the fault changes the observations of each host, for example a TLS proxy of the local network.
|
|
44
|
+
* - `target`: the fault changes only the observations of the primary target, for example a slow server.
|
|
45
|
+
*/
|
|
46
|
+
type Reach = "shared" | "target";
|
|
47
|
+
/**
|
|
48
|
+
* The strength of a prediction. The rules of practice use these strengths. The lab runs then replace them with fitted
|
|
49
|
+
* distributions.
|
|
50
|
+
*/
|
|
51
|
+
type Strength = "sure" | "strong" | "usual" | "weak";
|
|
52
|
+
/** The probability that each strength gives to the expected values together. */
|
|
53
|
+
declare const STRENGTHS: Readonly<Record<Strength, number>>;
|
|
54
|
+
/**
|
|
55
|
+
* A prediction of a fault for one feature: a full distribution, or the expected values and a strength.
|
|
56
|
+
*
|
|
57
|
+
* With `expect`, the expected values share the probability of the strength equally. The other values share the rest.
|
|
58
|
+
*/
|
|
59
|
+
type Prediction = Distribution | Expectation;
|
|
60
|
+
/** The expected values of a feature, and the strength of the prediction. */
|
|
61
|
+
interface Expectation {
|
|
62
|
+
readonly expect: string | readonly string[];
|
|
63
|
+
readonly strength: Strength;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* A fault of the catalog.
|
|
67
|
+
*
|
|
68
|
+
* The fault predicts a distribution for each feature that it changes. For each other feature, it predicts the
|
|
69
|
+
* distribution `healthy` of the feature. The prior is a relative weight. The default prior is 1.
|
|
70
|
+
*/
|
|
71
|
+
interface Fault {
|
|
72
|
+
readonly id: string;
|
|
73
|
+
readonly part: Part;
|
|
74
|
+
readonly reach: Reach;
|
|
75
|
+
/** The cause in plain words, for example "No program listens on the port." */
|
|
76
|
+
readonly summary: string;
|
|
77
|
+
/** The next step for the user in plain words. */
|
|
78
|
+
readonly step: string;
|
|
79
|
+
readonly prior?: number | undefined;
|
|
80
|
+
readonly predicts: Readonly<Record<string, Prediction>>;
|
|
81
|
+
}
|
|
82
|
+
/** The data of a catalog: the features, the faults, and the priors of the hypotheses "no fault" and "unknown cause". */
|
|
83
|
+
interface CatalogSpec {
|
|
84
|
+
readonly version: string;
|
|
85
|
+
readonly features: readonly Feature[];
|
|
86
|
+
readonly faults: readonly Fault[];
|
|
87
|
+
readonly priors?: {
|
|
88
|
+
readonly none?: number | undefined;
|
|
89
|
+
readonly unknown?: number | undefined;
|
|
90
|
+
} | undefined;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* A hypothesis of the model.
|
|
94
|
+
*
|
|
95
|
+
* - `none`: the network has no fault.
|
|
96
|
+
* - `fault`: one fault of the catalog.
|
|
97
|
+
* - `unknown`: a cause that the catalog does not have. It gives each value of a feature the same probability.
|
|
98
|
+
*/
|
|
99
|
+
interface Hypothesis {
|
|
100
|
+
readonly id: string;
|
|
101
|
+
readonly kind: "none" | "fault" | "unknown";
|
|
102
|
+
readonly summary: string;
|
|
103
|
+
readonly step: string;
|
|
104
|
+
readonly part?: Part | undefined;
|
|
105
|
+
readonly reach?: Reach | undefined;
|
|
106
|
+
}
|
|
107
|
+
/** The id of the hypothesis "no fault". */
|
|
108
|
+
declare const NONE = "none";
|
|
109
|
+
/** The id of the hypothesis "unknown cause". */
|
|
110
|
+
declare const UNKNOWN = "unknown";
|
|
111
|
+
/** A catalog with each distribution in its full form, and the logarithms of the probabilities. */
|
|
112
|
+
interface Catalog {
|
|
113
|
+
readonly version: string;
|
|
114
|
+
readonly features: ReadonlyMap<string, Feature>;
|
|
115
|
+
readonly hypotheses: readonly Hypothesis[];
|
|
116
|
+
/** This function gives the probability of a value of a feature under a hypothesis, for a primary or a reference host. */
|
|
117
|
+
likelihood(hypothesis: string, feature: string, value: string, primary: boolean): number;
|
|
118
|
+
prior(hypothesis: string): number;
|
|
119
|
+
}
|
|
120
|
+
/** The lowest probability of a value. With a probability of 0, one wrong observation removes a cause. */
|
|
121
|
+
declare const FLOOR = .001;
|
|
122
|
+
/** This function gives the full distribution of a prediction, or the problems of the prediction. */
|
|
123
|
+
declare function expand(feature: Feature, prediction: Prediction): Result<Distribution, string[]>;
|
|
124
|
+
/**
|
|
125
|
+
* This function examines a catalog and makes its tables.
|
|
126
|
+
*
|
|
127
|
+
* It refuses a repeated id, an unknown feature or value, a probability below `FLOOR`, and a distribution with a sum that is not 1.
|
|
128
|
+
*/
|
|
129
|
+
declare function compileCatalog(spec: CatalogSpec): Result<Catalog, string[]>;
|
|
130
|
+
/**
|
|
131
|
+
* One observation of a feature.
|
|
132
|
+
*
|
|
133
|
+
* `primary` tells if the observation is of the primary target or of a reference host. `facts` gives the ids of the facts
|
|
134
|
+
* that gave the observation, for the explanation.
|
|
135
|
+
*/
|
|
136
|
+
interface Evidence {
|
|
137
|
+
readonly feature: string;
|
|
138
|
+
readonly value: string;
|
|
139
|
+
readonly primary: boolean;
|
|
140
|
+
readonly facts?: readonly string[] | undefined;
|
|
141
|
+
}
|
|
142
|
+
/** A cause with its probability. */
|
|
143
|
+
interface Cause extends Hypothesis {
|
|
144
|
+
readonly probability: number;
|
|
145
|
+
}
|
|
146
|
+
/** The result of a diagnosis: the causes from the most probable, and the evidence that the model did not use, with the reason. */
|
|
147
|
+
interface Diagnosis {
|
|
148
|
+
readonly causes: readonly Cause[];
|
|
149
|
+
readonly used: readonly Evidence[];
|
|
150
|
+
readonly ignored: readonly {
|
|
151
|
+
readonly evidence: Evidence;
|
|
152
|
+
readonly reason: string;
|
|
153
|
+
}[];
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* This function gives the probability of each hypothesis from the evidence, with the rule of Bayes.
|
|
157
|
+
*
|
|
158
|
+
* The model takes the observations as independent for a given hypothesis. The calculation uses logarithms, so a long
|
|
159
|
+
* list of evidence does not go to zero. The order of the evidence does not change the result.
|
|
160
|
+
* An exact tie keeps the order of the catalog, with "no fault" first.
|
|
161
|
+
*/
|
|
162
|
+
declare function diagnose(catalog: Catalog, evidence: readonly Evidence[]): Diagnosis;
|
|
163
|
+
/** The part of one observation in the comparison of two hypotheses. */
|
|
164
|
+
interface Contribution {
|
|
165
|
+
readonly evidence: Evidence;
|
|
166
|
+
/** The ratio of the likelihoods of the two hypotheses, as a base-10 logarithm. 1 is a factor of 10 for the first hypothesis. */
|
|
167
|
+
readonly weight: number;
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* This function tells why the model prefers one hypothesis to a second hypothesis.
|
|
171
|
+
*
|
|
172
|
+
* It gives each observation with its weight, from the largest weight in absolute value. The default second hypothesis is
|
|
173
|
+
* "no fault".
|
|
174
|
+
*/
|
|
175
|
+
declare function explain(catalog: Catalog, evidence: readonly Evidence[], hypothesis: string, versus?: string): Contribution[];
|
|
176
|
+
/**
|
|
177
|
+
* This function gives the probability of each value of a feature that is not observed yet, from the probabilities of
|
|
178
|
+
* the causes. The choice of the next probe uses it.
|
|
179
|
+
*/
|
|
180
|
+
declare function predictive(catalog: Catalog, diagnosis: Diagnosis, feature: string, primary: boolean): Distribution;
|
|
181
|
+
/**
|
|
182
|
+
* The features of the standard catalog.
|
|
183
|
+
*
|
|
184
|
+
* The readers of the facts give these features. The distribution `healthy` of each feature is a rule of practice. The
|
|
185
|
+
* lab runs (ND-7) then fit it.
|
|
186
|
+
*/
|
|
187
|
+
declare const STANDARD_FEATURES: readonly Feature[];
|
|
188
|
+
/**
|
|
189
|
+
* The faults of the standard catalog. Each prediction is a rule of practice. The lab runs (ND-7) then fit the
|
|
190
|
+
* distributions, and the priors.
|
|
191
|
+
*/
|
|
192
|
+
declare const STANDARD_FAULTS: readonly Fault[];
|
|
193
|
+
/** The standard catalog: the rules of practice as the first likelihoods. Each hypothesis has the prior weight 1. */
|
|
194
|
+
declare const STANDARD_CATALOG_SPEC: CatalogSpec;
|
|
195
|
+
/** The standard catalog, compiled. */
|
|
196
|
+
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
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index-CP1hzELI.d.ts","names":[],"sources":["../src/layer.ts","../src/diagnosis/model.ts","../src/diagnosis/catalog.ts"],"mappings":";;;;;;;;;;;;;;cAaa;;KAGD,gBAAgB;;iBAKZ,QAAQ,iBAAiB,SAAS;;iBAKlC,UAAU,OAAO;;KCtBrB,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"}
|
package/build/index.d.ts
ADDED
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
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";
|
|
2
|
+
import { Result } from "@mark1russell7/optional";
|
|
3
|
+
import { Clock } from "@mark1russell7/algebra/clock";
|
|
4
|
+
/** The kind of the host of a target: a DNS name, an IPv4 address or an IPv6 address. */
|
|
5
|
+
type HostKind = "name" | "ipv4" | "ipv6";
|
|
6
|
+
/**
|
|
7
|
+
* A host that a probe examines, and an optional port.
|
|
8
|
+
*
|
|
9
|
+
* The host is in its canonical form: lower case, with no final dot and no brackets around an IPv6 address.
|
|
10
|
+
*/
|
|
11
|
+
interface Target {
|
|
12
|
+
readonly host: string;
|
|
13
|
+
readonly kind: HostKind;
|
|
14
|
+
readonly port?: number | undefined;
|
|
15
|
+
}
|
|
16
|
+
/** This function gives the kind of a host in its canonical form. */
|
|
17
|
+
export declare function hostKind(host: string): HostKind;
|
|
18
|
+
/** This function gives the canonical form of a host: lower case, with no final dot and no brackets. */
|
|
19
|
+
export declare function canonicalHost(host: string): string;
|
|
20
|
+
/** This function makes a target from a host and an optional port. */
|
|
21
|
+
export declare function target(host: string, port?: number): Target;
|
|
22
|
+
/** This function gives the text of a target: `host`, `host:port`, or `[address]:port` for an IPv6 address. */
|
|
23
|
+
export declare function formatTarget(t: Target): string;
|
|
24
|
+
/** This function tells if two targets have the same host and the same port. */
|
|
25
|
+
export declare function sameTarget(a: Target, b: Target): boolean;
|
|
26
|
+
/**
|
|
27
|
+
* This function reads a target from the text that a user gives.
|
|
28
|
+
*
|
|
29
|
+
* It accepts a URL (`https://example.com/a`), a host (`example.com`) and a host with a port (`example.com:8443`).
|
|
30
|
+
* It also accepts an IPv4 address, and an IPv6 address in brackets with a port (`[2001:db8::1]:443`).
|
|
31
|
+
* A URL with a known scheme and no port gets the port of its scheme.
|
|
32
|
+
*/
|
|
33
|
+
export declare function parseTarget(text: string): Result<Target, string>;
|
|
34
|
+
/**
|
|
35
|
+
* The time of a fact, in milliseconds on the monotonic clock of the runner.
|
|
36
|
+
*
|
|
37
|
+
* The field `origin` of the runner gives the wall-clock time of one monotonic time. Thus each fact time has a wall-clock time.
|
|
38
|
+
*/
|
|
39
|
+
interface FactTime {
|
|
40
|
+
readonly start: number;
|
|
41
|
+
readonly end: number;
|
|
42
|
+
}
|
|
43
|
+
/** The source of a fact: the probe and its version, and the outside tool when the probe starts one. */
|
|
44
|
+
interface FactSource {
|
|
45
|
+
readonly probe: string;
|
|
46
|
+
readonly version: string;
|
|
47
|
+
readonly tool?: string | undefined;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* A typed fact of one layer of the network stack.
|
|
51
|
+
*
|
|
52
|
+
* A fact has a layer, a time, a source and a value. Its kind names the type of its value, for example `tcp.connect`.
|
|
53
|
+
* The subject is the target of the fact. A fact is plain data, so a record of facts is JSON.
|
|
54
|
+
*/
|
|
55
|
+
interface Fact<V = unknown> {
|
|
56
|
+
/** The id is unique in the facts of one runner, for example `f12`. */
|
|
57
|
+
readonly id: string;
|
|
58
|
+
readonly kind: string;
|
|
59
|
+
readonly layer: Layer;
|
|
60
|
+
readonly subject: Target;
|
|
61
|
+
readonly time: FactTime;
|
|
62
|
+
readonly source: FactSource;
|
|
63
|
+
readonly value: V;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* The definition of a kind of fact: its name, its layer and the check of its value.
|
|
67
|
+
*
|
|
68
|
+
* The check is the contract of the value. The runner refuses a value that does not pass it, thus each fact of a kind has a
|
|
69
|
+
* value of the type `V`.
|
|
70
|
+
*/
|
|
71
|
+
interface FactKind<V> {
|
|
72
|
+
readonly kind: string;
|
|
73
|
+
readonly layer: Layer;
|
|
74
|
+
readonly summary: string;
|
|
75
|
+
readonly check: (value: unknown) => value is V;
|
|
76
|
+
}
|
|
77
|
+
/** The value type of a fact kind. */
|
|
78
|
+
type ValueOf<K> = K extends FactKind<infer V> ? V : never;
|
|
79
|
+
/** This function makes a fact kind. The name has lower-case words with dots, for example `tls.handshake`. */
|
|
80
|
+
export declare function defineFactKind<V>(spec: FactKind<V>): FactKind<V>;
|
|
81
|
+
/** This function tells if a fact is of a kind, and if its value passes the check of the kind. */
|
|
82
|
+
export declare function isFactOf<V>(kind: FactKind<V>, fact: Fact): fact is Fact<V>;
|
|
83
|
+
/** This function gives the facts of one kind, in their order. */
|
|
84
|
+
export declare function factsOf<V>(kind: FactKind<V>, facts: readonly Fact[]): Fact<V>[];
|
|
85
|
+
/**
|
|
86
|
+
* An observation is a fact that a probe parsed, before the runner gives it an id, a layer and a source.
|
|
87
|
+
*
|
|
88
|
+
* The time is optional. Without a time, the fact gets the time of the run of the probe.
|
|
89
|
+
*/
|
|
90
|
+
interface Observation<V = unknown> {
|
|
91
|
+
readonly kind: FactKind<V>;
|
|
92
|
+
readonly subject: Target;
|
|
93
|
+
readonly value: V;
|
|
94
|
+
readonly time?: FactTime | undefined;
|
|
95
|
+
}
|
|
96
|
+
/** This function makes an observation with a type check of its value against its kind. */
|
|
97
|
+
export declare function observe<V>(kind: FactKind<V>, subject: Target, value: V, time?: FactTime): Observation<V>;
|
|
98
|
+
/**
|
|
99
|
+
* The mode of a probe.
|
|
100
|
+
*
|
|
101
|
+
* - `active`: the probe sends packets to its peers, for example a TCP connect.
|
|
102
|
+
* - `passive`: the probe reads only the state of this computer, for example its routes. It has no peers.
|
|
103
|
+
*/
|
|
104
|
+
type ProbeMode = "active" | "passive";
|
|
105
|
+
/** The context of a run: the clock of the runner, and the signal that stops the run. */
|
|
106
|
+
interface ProbeContext {
|
|
107
|
+
readonly clock: Clock;
|
|
108
|
+
readonly signal: AbortSignal;
|
|
109
|
+
}
|
|
110
|
+
/** The availability of a probe on this computer. An unavailable probe tells why, for example a tool that is not installed. */
|
|
111
|
+
type Availability = {
|
|
112
|
+
readonly available: true;
|
|
113
|
+
readonly version?: string | undefined;
|
|
114
|
+
} | {
|
|
115
|
+
readonly available: false;
|
|
116
|
+
readonly reason: string;
|
|
117
|
+
};
|
|
118
|
+
/**
|
|
119
|
+
* The contract of a probe. A probe is an adapter: it tells if it is available, it does its work, and it parses its raw output into
|
|
120
|
+
* observations.
|
|
121
|
+
*
|
|
122
|
+
* - `peers` gives each host that `run` sends packets to. A passive probe gives no peers. The runner examines each peer
|
|
123
|
+
* against the scope before `run` starts.
|
|
124
|
+
* - `run` does the effects and gives the raw output. A network failure is an observation, not an error. For example, a
|
|
125
|
+
* TCP connect that times out gives the outcome `timeout`. `run` throws only for a fault of the probe.
|
|
126
|
+
* - `parse` is a pure function of the raw output and the input. Thus a record of a raw output gives the same facts again,
|
|
127
|
+
* also after a correction of the parser.
|
|
128
|
+
*
|
|
129
|
+
* The input and the raw output are plain JSON data, so a record can keep them.
|
|
130
|
+
*/
|
|
131
|
+
interface Probe<I, R> {
|
|
132
|
+
/** The id has lower-case words with dots, for example `tcp.connect`. */
|
|
133
|
+
readonly id: string;
|
|
134
|
+
/** The version of the probe changes when its facts change. */
|
|
135
|
+
readonly version: string;
|
|
136
|
+
readonly summary: string;
|
|
137
|
+
readonly layer: Layer;
|
|
138
|
+
readonly mode: ProbeMode;
|
|
139
|
+
/** The kinds of the facts that the probe gives. The runner refuses an observation of a different kind. */
|
|
140
|
+
readonly kinds: readonly FactKind<unknown>[];
|
|
141
|
+
/** The outside tool that the probe starts, for example `ping`. A native probe has no tool. */
|
|
142
|
+
readonly tool?: string | undefined;
|
|
143
|
+
peers(input: I): readonly Target[];
|
|
144
|
+
available(context: ProbeContext): Promise<Availability>;
|
|
145
|
+
run(input: I, context: ProbeContext): Promise<R>;
|
|
146
|
+
parse(raw: R, input: I): readonly Observation[];
|
|
147
|
+
}
|
|
148
|
+
/** This function makes a probe, and examines its fields. */
|
|
149
|
+
export declare function defineProbe<I, R>(probe: Probe<I, R>): Probe<I, R>;
|
|
150
|
+
/**
|
|
151
|
+
* The basis of a grant: why a target is in the scope.
|
|
152
|
+
*
|
|
153
|
+
* - `named`: the user named the target for this diagnosis.
|
|
154
|
+
* - `owned`: the user declares that the user owns the network or the host.
|
|
155
|
+
* - `configured`: the configuration of the computer of the user names it, for example the gateway or the DNS resolver.
|
|
156
|
+
* - `resolved`: a DNS answer for a granted name gave this address.
|
|
157
|
+
*/
|
|
158
|
+
type GrantBasis = "named" | "owned" | "configured" | "resolved";
|
|
159
|
+
/**
|
|
160
|
+
* A grant puts targets in the scope.
|
|
161
|
+
*
|
|
162
|
+
* A `host` grant puts a DNS name in the scope, and with `subdomains` each name under it. A `range` grant puts the
|
|
163
|
+
* addresses of a CIDR range in the scope, for example `192.0.2.0/24` or `2001:db8::/32`.
|
|
164
|
+
*/
|
|
165
|
+
type Grant = HostGrant | RangeGrant;
|
|
166
|
+
/** A grant of a DNS name, and with `subdomains` of each name under it. */
|
|
167
|
+
interface HostGrant {
|
|
168
|
+
readonly kind: "host";
|
|
169
|
+
readonly host: string;
|
|
170
|
+
readonly subdomains?: boolean | undefined;
|
|
171
|
+
readonly basis: GrantBasis;
|
|
172
|
+
readonly note?: string | undefined;
|
|
173
|
+
}
|
|
174
|
+
/** A grant of the addresses of a CIDR range, for example `192.0.2.0/24` or `2001:db8::/32`. */
|
|
175
|
+
interface RangeGrant {
|
|
176
|
+
readonly kind: "range";
|
|
177
|
+
readonly cidr: string;
|
|
178
|
+
readonly basis: GrantBasis;
|
|
179
|
+
readonly note?: string | undefined;
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* The scope of a diagnosis: the grants, and the largest number of ports for each host.
|
|
183
|
+
*
|
|
184
|
+
* An active probe sends packets only to a peer in the scope. The limit of ports stops a port scan: netdiag does no
|
|
185
|
+
* vulnerability scan.
|
|
186
|
+
*/
|
|
187
|
+
interface Scope {
|
|
188
|
+
readonly grants: readonly Grant[];
|
|
189
|
+
readonly maxPortsPerHost: number;
|
|
190
|
+
}
|
|
191
|
+
/** The default limit of the different ports of one host in one runner. */
|
|
192
|
+
export declare const DEFAULT_MAX_PORTS_PER_HOST = 8;
|
|
193
|
+
/** The decision of the scope for one peer of an active probe. A refusal has the reason in plain words. */
|
|
194
|
+
type ScopeDecision = {
|
|
195
|
+
readonly allowed: true;
|
|
196
|
+
readonly grant: Grant;
|
|
197
|
+
} | {
|
|
198
|
+
readonly allowed: false;
|
|
199
|
+
readonly reason: string;
|
|
200
|
+
};
|
|
201
|
+
/** The ports that a runner sent packets to, for each host. */
|
|
202
|
+
type PortLedger = ReadonlyMap<string, ReadonlySet<number>>;
|
|
203
|
+
/** This function gives the problems of a grant, or an empty list for a correct grant. */
|
|
204
|
+
export declare function grantProblems(grant: Grant): string[];
|
|
205
|
+
/**
|
|
206
|
+
* This function makes a scope from grants. It refuses a grant with a problem, so a typing error cannot widen the scope.
|
|
207
|
+
*
|
|
208
|
+
* The default limit of ports is `DEFAULT_MAX_PORTS_PER_HOST`.
|
|
209
|
+
*/
|
|
210
|
+
export declare function scopeOf(grants: readonly Grant[], options?: {
|
|
211
|
+
readonly maxPortsPerHost?: number;
|
|
212
|
+
}): Result<Scope, string[]>;
|
|
213
|
+
/**
|
|
214
|
+
* This function gives the grant for a target that the user names. A name gets a host grant. An IP address gets a
|
|
215
|
+
* range grant of one address.
|
|
216
|
+
*/
|
|
217
|
+
export declare function namedGrant(t: Target, note?: string): Grant;
|
|
218
|
+
/** This function gives the grant of one address that a DNS answer for a granted name gave. */
|
|
219
|
+
export declare function resolvedGrant(name: string, address: string, factId?: string): RangeGrant;
|
|
220
|
+
/** This function gives a scope with more grants. The limit of ports does not change. */
|
|
221
|
+
export declare function widen(scope: Scope, grants: readonly Grant[]): Result<Scope, string[]>;
|
|
222
|
+
/**
|
|
223
|
+
* This function decides if an active probe can send packets to a peer.
|
|
224
|
+
*
|
|
225
|
+
* The peer must match a grant of the scope. A new port of a host must also stay in the limit of ports for each host.
|
|
226
|
+
* The ledger has the ports that the runner sent packets to before.
|
|
227
|
+
*/
|
|
228
|
+
export declare function decideScope(scope: Scope, peer: Target, ledger?: PortLedger): ScopeDecision;
|
|
229
|
+
/**
|
|
230
|
+
* The outcome of one run of a probe.
|
|
231
|
+
*
|
|
232
|
+
* - `done`: the probe did its work. The facts include each network failure that it saw.
|
|
233
|
+
* - `refused`: a peer is not in the scope, so the probe did not start.
|
|
234
|
+
* - `unavailable`: the probe cannot operate on this computer.
|
|
235
|
+
* - `failed`: the probe or its parser has a fault. The raw output is present when the run ended.
|
|
236
|
+
* - `aborted`: the signal stopped the run.
|
|
237
|
+
*/
|
|
238
|
+
type ProbeOutcome<R> = {
|
|
239
|
+
readonly status: "done";
|
|
240
|
+
readonly time: FactTime;
|
|
241
|
+
readonly raw: R;
|
|
242
|
+
readonly facts: readonly Fact[];
|
|
243
|
+
} | {
|
|
244
|
+
readonly status: "refused";
|
|
245
|
+
readonly peer: Target;
|
|
246
|
+
readonly reason: string;
|
|
247
|
+
} | {
|
|
248
|
+
readonly status: "unavailable";
|
|
249
|
+
readonly reason: string;
|
|
250
|
+
} | {
|
|
251
|
+
readonly status: "failed";
|
|
252
|
+
readonly stage: "probe" | "run" | "parse";
|
|
253
|
+
readonly error: string;
|
|
254
|
+
readonly raw?: R | undefined;
|
|
255
|
+
} | {
|
|
256
|
+
readonly status: "aborted";
|
|
257
|
+
readonly reason: string;
|
|
258
|
+
};
|
|
259
|
+
/** The wall-clock time of one monotonic time of the runner. */
|
|
260
|
+
interface ClockOrigin {
|
|
261
|
+
readonly wall: string;
|
|
262
|
+
readonly monotonic: number;
|
|
263
|
+
}
|
|
264
|
+
/** The options of a runner. */
|
|
265
|
+
interface RunnerOptions {
|
|
266
|
+
readonly scope: Scope;
|
|
267
|
+
readonly clock: Clock;
|
|
268
|
+
/** The wall-clock time of the start, as an ISO 8601 text. The default is the time of the computer. */
|
|
269
|
+
readonly wall?: string | undefined;
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* A runner starts probes in one scope, and keeps the facts of the diagnosis.
|
|
273
|
+
*
|
|
274
|
+
* It gives each fact an id in sequence. It keeps the ledger of the ports of each host, for the limit of ports of the scope.
|
|
275
|
+
*/
|
|
276
|
+
interface Runner {
|
|
277
|
+
readonly origin: ClockOrigin;
|
|
278
|
+
scope(): Scope;
|
|
279
|
+
facts(): readonly Fact[];
|
|
280
|
+
/** This method adds grants to the scope, for example the addresses of a granted name. */
|
|
281
|
+
grant(grants: readonly Grant[]): void;
|
|
282
|
+
run<I, R>(probe: Probe<I, R>, input: I, signal?: AbortSignal): Promise<ProbeOutcome<R>>;
|
|
283
|
+
}
|
|
284
|
+
/** This function makes a runner. */
|
|
285
|
+
export declare function createRunner(options: RunnerOptions): Runner;
|
|
286
|
+
export { type Availability, type Catalog, type CatalogSpec, type Cause, type ClockOrigin, type Contribution, type Diagnosis, type Distribution, type Evidence, type Expectation, FLOOR, type Fact, type FactKind, type FactSource, type FactTime, type Fault, type Feature, type Grant, type GrantBasis, type HostGrant, type HostKind, type Hypothesis, LAYERS, type Layer, NONE, type Observation, PARTS, type Part, type PortLedger, type Prediction, type Probe, type ProbeContext, type ProbeMode, type ProbeOutcome, type RangeGrant, type Reach, type Runner, type RunnerOptions, STANDARD_CATALOG, STANDARD_CATALOG_SPEC, STANDARD_FAULTS, STANDARD_FEATURES, STRENGTHS, type Scope, type ScopeDecision, type Strength, type Target, UNKNOWN, type ValueOf, compileCatalog, diagnose, expand, explain, isLayer, layerRank, predictive };
|
|
287
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/target.ts","../src/fact.ts","../src/probe.ts","../src/scope.ts","../src/run.ts"],"mappings":";;;;KAIY;;;;;;UAOK;WACN;WACA,MAAM;WACN;;;wBAcK,SAAS,eAAe;;wBAQxB,cAAc;;wBAQd,OAAO,cAAc,gBAAgB;;wBAMrC,aAAa,GAAG;;wBAMhB,WAAW,GAAG,QAAQ,GAAG;;;;;;;;wBAwBzB,YAAY,eAAe,OAAO;;;;;;UCxEjC;WACN;WACA;;;UAIM;WACN;WACA;WACA;;;;;;;;UASM,KAAK;;WAEX;WACA;WACA,OAAO;WACP,SAAS;WACT,MAAM;WACN,QAAQ;WACR,OAAO;;;;;;;;UASD,SAAS;WACf;WACA,OAAO;WACP;WACA,QAAQ,mBAAmB,SAAS;;;KAInC,QAAQ,KAAK,UAAU,eAAe,KAAK;;wBAKvC,eAAe,GAAG,MAAM,SAAS,KAAK,SAAS;;wBAM/C,SAAS,GAAG,MAAM,SAAS,IAAI,MAAM,OAAO,QAAQ,KAAK;;wBAKzD,QAAQ,GAAG,MAAM,SAAS,IAAI,gBAAgB,SAAS,KAAK;;;;;;UAS3D,YAAY;WAClB,MAAM,SAAS;WACf,SAAS;WACT,OAAO;WACP,OAAO;;;wBAIF,QAAQ,GAAG,MAAM,SAAS,IAAI,SAAS,QAAQ,OAAO,GAAG,OAAO,WAAW,YAAY;;;;;;;KCzE3F;;UAGK;WACN,OAAO;WACP,QAAQ;;;KAIP;WACG;WAA0B;;WAC1B;WAA2B;;;;;;;;;;;;;;;UAezB,MAAM,GAAG;;WAEf;;WAEA;WACA;WACA,OAAO;WACP,MAAM;;WAEN,gBAAgB;;WAEhB;EACT,MAAM,OAAO,aAAa;EAC1B,UAAU,SAAS,eAAe,QAAQ;EAC1C,IAAI,OAAO,GAAG,SAAS,eAAe,QAAQ;EAC9C,MAAM,KAAK,GAAG,OAAO,aAAa;;;wBAIpB,YAAY,GAAG,GAAG,OAAO,MAAM,GAAG,KAAK,MAAM,GAAG;;;;;;;;;KC5CpD;;;;;;;KAQA,QAAQ,YAAY;;UAGf;WACN;WACA;WACA;WACA,OAAO;WACP;;;UAIM;WACN;WACA;WACA,OAAO;WACP;;;;;;;;UASM;WACN,iBAAiB;WACjB;;;qBAIE;;KAGD;WACG;WAAwB,OAAO;;WAC/B;WAAyB;;;KAG5B,aAAa,oBAAoB;;wBAoB7B,cAAc,OAAO;;;;;;wBAgBrB,QAAQ,iBAAiB,SAAS;WAAoB;IAAkC,OAAO;;;;;wBAY/F,WAAW,GAAG,QAAQ,gBAAgB;;wBAMtC,cAAc,cAAc,iBAAiB,kBAAkB;;wBAS/D,MAAM,OAAO,OAAO,iBAAiB,UAAU,OAAO;;;;;;;wBA8BtD,YAAY,OAAO,OAAO,MAAM,QAAQ,SAAQ,aAAyB;;;;;;;;;;KCzI7E,aAAa;WACV;WAAyB,MAAM;WAAmB,KAAK;WAAY,gBAAgB;;WACnF;WAA4B,MAAM;WAAiB;;WACnD;WAAgC;;WAChC;WAA2B;WAA2C;WAAwB,MAAM;;WACpG;WAA4B;;;UAG1B;WACN;WACA;;;UAIM;WACN,OAAO;WACP,OAAO;;WAEP;;;;;;;UAQM;WACN,QAAQ;EACjB,SAAS;EACT,kBAAkB;;EAElB,MAAM,iBAAiB;EACvB,IAAI,GAAG,GAAG,OAAO,MAAM,GAAG,IAAI,OAAO,GAAG,SAAS,cAAc,QAAQ,aAAa;;;wBActE,aAAa,SAAS,gBAAgB"}
|