@classytic/stage 0.2.0 → 0.4.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 +1 -1
- package/README.md +5 -1
- package/dist/_virtual/_rolldown/runtime.mjs +18 -0
- package/dist/assets/index.mjs +0 -1
- package/dist/atomic/aufbau.d.mts +144 -0
- package/dist/atomic/aufbau.mjs +384 -0
- package/dist/atomic/elements.d.mts +67 -0
- package/dist/atomic/elements.mjs +785 -0
- package/dist/atomic/emission.d.mts +112 -0
- package/dist/atomic/emission.mjs +179 -0
- package/dist/atomic/index.d.mts +8 -0
- package/dist/atomic/index.mjs +9 -0
- package/dist/atomic/periodic.d.mts +82 -0
- package/dist/atomic/periodic.mjs +231 -0
- package/dist/atomic/properties.d.mts +100 -0
- package/dist/atomic/properties.mjs +574 -0
- package/dist/atomic/shells.d.mts +137 -0
- package/dist/atomic/shells.mjs +170 -0
- package/dist/atomic/trends.d.mts +148 -0
- package/dist/atomic/trends.mjs +225 -0
- package/dist/builder/Palette.mjs +50 -89
- package/dist/builder/SceneBuilder.mjs +15 -73
- package/dist/chem/chromatography.d.mts +66 -0
- package/dist/chem/chromatography.mjs +63 -0
- package/dist/chem/complex.d.mts +114 -0
- package/dist/chem/complex.mjs +234 -0
- package/dist/chem/cycle.d.mts +75 -0
- package/dist/chem/cycle.mjs +145 -0
- package/dist/chem/equation.d.mts +86 -0
- package/dist/chem/equation.mjs +168 -0
- package/dist/chem/formula.d.mts +90 -0
- package/dist/chem/formula.mjs +201 -0
- package/dist/chem/functional-groups.d.mts +31 -0
- package/dist/chem/functional-groups.mjs +187 -0
- package/dist/chem/hazard.d.mts +94 -0
- package/dist/chem/hazard.mjs +329 -0
- package/dist/chem/index.d.mts +34 -33
- package/dist/chem/index.mjs +34 -28
- package/dist/chem/ionic.d.mts +98 -0
- package/dist/chem/ionic.mjs +156 -0
- package/dist/chem/layout.d.mts +46 -0
- package/dist/chem/layout.mjs +342 -0
- package/dist/chem/lewis.d.mts +79 -0
- package/dist/chem/lewis.mjs +163 -0
- package/dist/chem/molecular-orbital.d.mts +114 -0
- package/dist/chem/molecular-orbital.mjs +201 -0
- package/dist/chem/molecule.d.mts +154 -0
- package/dist/chem/molecule.mjs +282 -0
- package/dist/chem/nmr.d.mts +153 -0
- package/dist/chem/nmr.mjs +165 -0
- package/dist/chem/polymer.d.mts +65 -0
- package/dist/chem/polymer.mjs +179 -0
- package/dist/chem/redox.d.mts +138 -0
- package/dist/chem/redox.mjs +328 -0
- package/dist/chem/relations.d.mts +183 -0
- package/dist/chem/relations.mjs +299 -0
- package/dist/chem/skeleton.d.mts +51 -0
- package/dist/chem/skeleton.mjs +186 -0
- package/dist/chem/solution.d.mts +116 -0
- package/dist/chem/solution.mjs +186 -0
- package/dist/chem/species.d.mts +47 -0
- package/dist/chem/species.mjs +128 -0
- package/dist/chem/stereo.d.mts +108 -0
- package/dist/chem/stereo.mjs +286 -0
- package/dist/chem/stoichiometry.d.mts +95 -0
- package/dist/chem/stoichiometry.mjs +145 -0
- package/dist/chem/synthesis.d.mts +113 -0
- package/dist/chem/synthesis.mjs +281 -0
- package/dist/chem/vsepr.d.mts +80 -0
- package/dist/chem/vsepr.mjs +247 -0
- package/dist/circuit/ac.d.mts +163 -0
- package/dist/circuit/ac.mjs +452 -0
- package/dist/circuit/dc.d.mts +92 -0
- package/dist/circuit/dc.mjs +333 -0
- package/dist/circuit/index.d.mts +5 -92
- package/dist/circuit/index.mjs +5 -332
- package/dist/circuit/network.d.mts +99 -0
- package/dist/circuit/network.mjs +440 -0
- package/dist/circuit/topology.d.mts +102 -0
- package/dist/circuit/topology.mjs +143 -0
- package/dist/colour/absorption.d.mts +79 -0
- package/dist/colour/absorption.mjs +137 -0
- package/dist/colour/index.d.mts +6 -0
- package/dist/colour/index.mjs +7 -0
- package/dist/colour/name.d.mts +14 -0
- package/dist/colour/name.mjs +76 -0
- package/dist/colour/observer.d.mts +31 -0
- package/dist/colour/observer.mjs +46 -0
- package/dist/colour/spectrum.d.mts +61 -0
- package/dist/colour/spectrum.mjs +108 -0
- package/dist/colour/srgb.d.mts +49 -0
- package/dist/colour/srgb.mjs +108 -0
- package/dist/core/constants.d.mts +138 -0
- package/dist/core/constants.mjs +139 -0
- package/dist/core/index.d.mts +4 -1
- package/dist/core/index.mjs +4 -1
- package/dist/core/math.d.mts +26 -0
- package/dist/core/math.mjs +37 -0
- package/dist/core/vec3.d.mts +94 -0
- package/dist/core/vec3.mjs +123 -0
- package/dist/core/worked.d.mts +35 -0
- package/dist/core/worked.mjs +44 -0
- package/dist/electro/cell.d.mts +102 -0
- package/dist/electro/cell.mjs +170 -0
- package/dist/electro/conductivity.d.mts +157 -0
- package/dist/electro/conductivity.mjs +156 -0
- package/dist/electro/couples.d.mts +114 -0
- package/dist/electro/couples.mjs +334 -0
- package/dist/electro/electrolysis.d.mts +96 -0
- package/dist/electro/electrolysis.mjs +274 -0
- package/dist/electro/faraday.d.mts +150 -0
- package/dist/electro/faraday.mjs +118 -0
- package/dist/electro/index.d.mts +6 -0
- package/dist/electro/index.mjs +7 -0
- package/dist/finance/accounting/adjustments.d.mts +17 -0
- package/dist/finance/accounting/adjustments.mjs +202 -0
- package/dist/finance/accounting/analysis.d.mts +8 -0
- package/dist/finance/accounting/analysis.mjs +205 -0
- package/dist/finance/accounting/clubs.d.mts +21 -0
- package/dist/finance/accounting/clubs.mjs +217 -0
- package/dist/finance/accounting/company-statements.d.mts +7 -0
- package/dist/finance/accounting/company-statements.mjs +150 -0
- package/dist/finance/accounting/harness.d.mts +7 -0
- package/dist/finance/accounting/harness.mjs +45 -0
- package/dist/finance/accounting/index.d.mts +18 -0
- package/dist/finance/accounting/index.mjs +18 -0
- package/dist/finance/accounting/journalize.d.mts +16 -0
- package/dist/finance/accounting/journalize.mjs +128 -0
- package/dist/finance/accounting/manufacturing.d.mts +8 -0
- package/dist/finance/accounting/manufacturing.mjs +236 -0
- package/dist/finance/accounting/partnership.d.mts +7 -0
- package/dist/finance/accounting/partnership.mjs +152 -0
- package/dist/finance/accounting/period-case.d.mts +6 -0
- package/dist/finance/accounting/period-case.mjs +71 -0
- package/dist/finance/accounting/post.d.mts +9 -0
- package/dist/finance/accounting/post.mjs +84 -0
- package/dist/finance/accounting/precision.d.mts +9 -0
- package/dist/finance/accounting/precision.mjs +15 -0
- package/dist/finance/accounting/rules.d.mts +21 -0
- package/dist/finance/accounting/rules.mjs +37 -0
- package/dist/finance/accounting/solve.d.mts +28 -0
- package/dist/finance/accounting/solve.mjs +92 -0
- package/dist/finance/accounting/statements.d.mts +7 -0
- package/dist/finance/accounting/statements.mjs +142 -0
- package/dist/finance/accounting/types.d.mts +649 -0
- package/dist/finance/accounting/validate.d.mts +7 -0
- package/dist/finance/accounting/validate.mjs +67 -0
- package/dist/finance/accounting/verification.d.mts +24 -0
- package/dist/finance/accounting/verification.mjs +386 -0
- package/dist/finance/bizsim.d.mts +75 -0
- package/dist/finance/bizsim.mjs +296 -0
- package/dist/finance/index.d.mts +95 -0
- package/dist/finance/index.mjs +145 -0
- package/dist/finance/investment-math.d.mts +7 -0
- package/dist/finance/investment-math.mjs +18 -0
- package/dist/finance/management-accounting/budgeting.d.mts +7 -0
- package/dist/finance/management-accounting/budgeting.mjs +189 -0
- package/dist/finance/management-accounting/cash-budget.d.mts +7 -0
- package/dist/finance/management-accounting/cash-budget.mjs +154 -0
- package/dist/finance/management-accounting/costing.d.mts +8 -0
- package/dist/finance/management-accounting/costing.mjs +175 -0
- package/dist/finance/management-accounting/decisions.d.mts +8 -0
- package/dist/finance/management-accounting/decisions.mjs +164 -0
- package/dist/finance/management-accounting/index.d.mts +13 -0
- package/dist/finance/management-accounting/index.mjs +13 -0
- package/dist/finance/management-accounting/integrated-budget.d.mts +8 -0
- package/dist/finance/management-accounting/integrated-budget.mjs +67 -0
- package/dist/finance/management-accounting/inventory-costing.d.mts +7 -0
- package/dist/finance/management-accounting/inventory-costing.mjs +173 -0
- package/dist/finance/management-accounting/investment-appraisal.d.mts +7 -0
- package/dist/finance/management-accounting/investment-appraisal.mjs +148 -0
- package/dist/finance/management-accounting/management-case.d.mts +8 -0
- package/dist/finance/management-accounting/management-case.mjs +91 -0
- package/dist/finance/management-accounting/master-budget.d.mts +7 -0
- package/dist/finance/management-accounting/master-budget.mjs +137 -0
- package/dist/finance/management-accounting/production-costing.d.mts +8 -0
- package/dist/finance/management-accounting/production-costing.mjs +214 -0
- package/dist/finance/management-accounting/types.d.mts +644 -0
- package/dist/finance/management-accounting/variances.d.mts +7 -0
- package/dist/finance/management-accounting/variances.mjs +150 -0
- package/dist/index.d.mts +9 -8
- package/dist/index.mjs +5 -5
- package/dist/interaction/MovableDot.mjs +19 -0
- package/dist/interaction/useDraggable.mjs +24 -4
- package/dist/math/analysis.d.mts +61 -0
- package/dist/math/analysis.mjs +187 -0
- package/dist/math/answer-check.d.mts +50 -0
- package/dist/math/answer-check.mjs +106 -0
- package/dist/math/calculus.d.mts +17 -1
- package/dist/math/calculus.mjs +365 -2
- package/dist/math/complex.d.mts +93 -0
- package/dist/math/complex.mjs +239 -0
- package/dist/math/expr.d.mts +23 -0
- package/dist/math/expr.mjs +36 -0
- package/dist/math/index.d.mts +9 -23
- package/dist/math/index.mjs +9 -38
- package/dist/math/latex.mjs +10 -1
- package/dist/math/parse.mjs +1 -1
- package/dist/math/poly.d.mts +55 -0
- package/dist/math/poly.mjs +273 -0
- package/dist/math/rearrange.d.mts +14 -0
- package/dist/math/rearrange.mjs +125 -0
- package/dist/math/solve.d.mts +21 -0
- package/dist/math/solve.mjs +75 -0
- package/dist/networking/addressing.d.mts +26 -0
- package/dist/networking/addressing.mjs +33 -0
- package/dist/networking/cloud/access.d.mts +36 -0
- package/dist/networking/cloud/access.mjs +95 -0
- package/dist/networking/cloud/containers.d.mts +43 -0
- package/dist/networking/cloud/containers.mjs +54 -0
- package/dist/networking/cloud/edge.d.mts +17 -0
- package/dist/networking/cloud/edge.mjs +31 -0
- package/dist/networking/cloud/failure-domains.d.mts +19 -0
- package/dist/networking/cloud/failure-domains.mjs +22 -0
- package/dist/networking/cloud/geo-routing.d.mts +22 -0
- package/dist/networking/cloud/geo-routing.mjs +64 -0
- package/dist/networking/cloud/index.d.mts +18 -0
- package/dist/networking/cloud/index.mjs +19 -0
- package/dist/networking/cloud/inference-reliability.d.mts +30 -0
- package/dist/networking/cloud/inference-reliability.mjs +76 -0
- package/dist/networking/cloud/inference.d.mts +118 -0
- package/dist/networking/cloud/inference.mjs +179 -0
- package/dist/networking/cloud/infrastructure.d.mts +45 -0
- package/dist/networking/cloud/infrastructure.mjs +96 -0
- package/dist/networking/cloud/observability.d.mts +24 -0
- package/dist/networking/cloud/observability.mjs +34 -0
- package/dist/networking/cloud/orchestration.d.mts +32 -0
- package/dist/networking/cloud/orchestration.mjs +38 -0
- package/dist/networking/cloud/recovery.d.mts +21 -0
- package/dist/networking/cloud/recovery.mjs +27 -0
- package/dist/networking/cloud/releases.d.mts +42 -0
- package/dist/networking/cloud/releases.mjs +135 -0
- package/dist/networking/cloud/scaling.d.mts +25 -0
- package/dist/networking/cloud/scaling.mjs +65 -0
- package/dist/networking/cloud/secrets.d.mts +39 -0
- package/dist/networking/cloud/secrets.mjs +68 -0
- package/dist/networking/cloud/server-deployment.d.mts +29 -0
- package/dist/networking/cloud/server-deployment.mjs +72 -0
- package/dist/networking/cloud/storage.d.mts +16 -0
- package/dist/networking/cloud/storage.mjs +58 -0
- package/dist/networking/cloud/vpc.d.mts +18 -0
- package/dist/networking/cloud/vpc.mjs +100 -0
- package/dist/networking/delivery.d.mts +34 -0
- package/dist/networking/delivery.mjs +52 -0
- package/dist/networking/index.d.mts +47 -0
- package/dist/networking/index.mjs +47 -0
- package/dist/networking/ipv6.d.mts +28 -0
- package/dist/networking/ipv6.mjs +17 -0
- package/dist/networking/journeys.d.mts +83 -0
- package/dist/networking/journeys.mjs +161 -0
- package/dist/networking/models/airtime.d.mts +87 -0
- package/dist/networking/models/airtime.mjs +130 -0
- package/dist/networking/models/cellular.d.mts +157 -0
- package/dist/networking/models/cellular.mjs +252 -0
- package/dist/networking/models/encapsulation.d.mts +98 -0
- package/dist/networking/models/encapsulation.mjs +132 -0
- package/dist/networking/models/handover.d.mts +116 -0
- package/dist/networking/models/handover.mjs +209 -0
- package/dist/networking/models/handshake.d.mts +90 -0
- package/dist/networking/models/handshake.mjs +122 -0
- package/dist/networking/models/index.d.mts +14 -0
- package/dist/networking/models/index.mjs +15 -0
- package/dist/networking/models/media.d.mts +82 -0
- package/dist/networking/models/media.mjs +188 -0
- package/dist/networking/models/satellite.d.mts +86 -0
- package/dist/networking/models/satellite.mjs +121 -0
- package/dist/networking/models/spectrum.d.mts +83 -0
- package/dist/networking/models/spectrum.mjs +235 -0
- package/dist/networking/models/switch.d.mts +80 -0
- package/dist/networking/models/switch.mjs +155 -0
- package/dist/networking/models/vlan.d.mts +62 -0
- package/dist/networking/models/vlan.mjs +100 -0
- package/dist/networking/models/vpn.d.mts +66 -0
- package/dist/networking/models/vpn.mjs +125 -0
- package/dist/networking/models/wifi-privacy.d.mts +82 -0
- package/dist/networking/models/wifi-privacy.mjs +130 -0
- package/dist/networking/models/wifi.d.mts +240 -0
- package/dist/networking/models/wifi.mjs +562 -0
- package/dist/networking/nat-traversal.d.mts +24 -0
- package/dist/networking/nat-traversal.mjs +38 -0
- package/dist/networking/protocols.d.mts +17 -0
- package/dist/networking/protocols.mjs +57 -0
- package/dist/networking/queue.d.mts +12 -0
- package/dist/networking/queue.mjs +18 -0
- package/dist/networking/route-learning.d.mts +23 -0
- package/dist/networking/route-learning.mjs +43 -0
- package/dist/networking/routing.d.mts +11 -0
- package/dist/networking/routing.mjs +25 -0
- package/dist/networking/signals.d.mts +26 -0
- package/dist/networking/signals.mjs +35 -0
- package/dist/networking/subnet.d.mts +49 -0
- package/dist/networking/subnet.mjs +89 -0
- package/dist/networking/tcp-reliability.d.mts +24 -0
- package/dist/networking/tcp-reliability.mjs +38 -0
- package/dist/networking/topology/contract.d.mts +123 -0
- package/dist/networking/topology/index.d.mts +4 -0
- package/dist/networking/topology/index.mjs +4 -0
- package/dist/networking/topology/presets.d.mts +7 -0
- package/dist/networking/topology/presets.mjs +302 -0
- package/dist/networking/topology/simulation.d.mts +8 -0
- package/dist/networking/topology/simulation.mjs +179 -0
- package/dist/networking/wireless.d.mts +11 -0
- package/dist/networking/wireless.mjs +17 -0
- package/dist/optics/imaging.d.mts +218 -0
- package/dist/optics/imaging.mjs +276 -0
- package/dist/optics/index.d.mts +7 -0
- package/dist/optics/index.mjs +8 -0
- package/dist/optics/laws.d.mts +70 -0
- package/dist/optics/laws.mjs +195 -0
- package/dist/optics/prism.d.mts +148 -0
- package/dist/optics/prism.mjs +197 -0
- package/dist/optics/refraction.d.mts +201 -0
- package/dist/optics/refraction.mjs +270 -0
- package/dist/optics/wave-optics.d.mts +171 -0
- package/dist/optics/wave-optics.mjs +259 -0
- package/dist/physics/ac/index.d.mts +183 -0
- package/dist/physics/ac/index.mjs +203 -0
- package/dist/physics/dynamics/forces.d.mts +98 -0
- package/dist/physics/dynamics/forces.mjs +153 -0
- package/dist/physics/dynamics/friction.d.mts +76 -0
- package/dist/physics/dynamics/friction.mjs +119 -0
- package/dist/physics/dynamics/system.d.mts +68 -0
- package/dist/physics/dynamics/system.mjs +125 -0
- package/dist/physics/electrostatics/index.d.mts +169 -0
- package/dist/physics/electrostatics/index.mjs +249 -0
- package/dist/physics/energy/centre-of-mass.d.mts +115 -0
- package/dist/physics/energy/centre-of-mass.mjs +180 -0
- package/dist/physics/energy/conservation.d.mts +81 -0
- package/dist/physics/energy/conservation.mjs +129 -0
- package/dist/physics/energy/power.d.mts +90 -0
- package/dist/physics/energy/power.mjs +167 -0
- package/dist/physics/energy/spring.d.mts +73 -0
- package/dist/physics/energy/spring.mjs +129 -0
- package/dist/physics/energy/store.d.mts +63 -0
- package/dist/physics/energy/store.mjs +108 -0
- package/dist/physics/energy/theorem.d.mts +65 -0
- package/dist/physics/energy/theorem.mjs +113 -0
- package/dist/physics/energy/work.d.mts +114 -0
- package/dist/physics/energy/work.mjs +219 -0
- package/dist/physics/gravitation/body.d.mts +85 -0
- package/dist/physics/gravitation/body.mjs +168 -0
- package/dist/physics/gravitation/kepler.d.mts +127 -0
- package/dist/physics/gravitation/kepler.mjs +216 -0
- package/dist/physics/gravitation/law.d.mts +117 -0
- package/dist/physics/gravitation/law.mjs +175 -0
- package/dist/physics/gravitation/orbit.d.mts +119 -0
- package/dist/physics/gravitation/orbit.mjs +202 -0
- package/dist/physics/gravitation/potential.d.mts +114 -0
- package/dist/physics/gravitation/potential.mjs +202 -0
- package/dist/physics/gravitation/variation.d.mts +97 -0
- package/dist/physics/gravitation/variation.mjs +175 -0
- package/dist/physics/index.d.mts +35 -0
- package/dist/physics/index.mjs +36 -0
- package/dist/physics/integral-sweep.d.mts +63 -0
- package/dist/physics/integral-sweep.mjs +126 -0
- package/dist/physics/kinematics/equations.d.mts +47 -0
- package/dist/physics/kinematics/equations.mjs +126 -0
- package/dist/physics/kinematics/free-fall.d.mts +53 -0
- package/dist/physics/kinematics/free-fall.mjs +71 -0
- package/dist/physics/kinematics/given.d.mts +16 -0
- package/dist/physics/kinematics/given.mjs +55 -0
- package/dist/physics/kinematics/roots.d.mts +41 -0
- package/dist/physics/kinematics/roots.mjs +72 -0
- package/dist/physics/kinematics/solve.d.mts +52 -0
- package/dist/physics/kinematics/solve.mjs +172 -0
- package/dist/physics/magnetism/biot-savart.d.mts +161 -0
- package/dist/physics/magnetism/biot-savart.mjs +353 -0
- package/dist/physics/magnetism/flux.d.mts +94 -0
- package/dist/physics/magnetism/flux.mjs +313 -0
- package/dist/physics/magnetism/geomagnetism.d.mts +151 -0
- package/dist/physics/magnetism/geomagnetism.mjs +184 -0
- package/dist/physics/magnetism/index.d.mts +12 -0
- package/dist/physics/magnetism/index.mjs +74 -0
- package/dist/physics/magnetism/lorentz.d.mts +28 -0
- package/dist/physics/magnetism/lorentz.mjs +88 -0
- package/dist/physics/magnetism/magnetic-force.d.mts +102 -0
- package/dist/physics/magnetism/magnetic-force.mjs +169 -0
- package/dist/physics/momentum/collide.d.mts +77 -0
- package/dist/physics/momentum/collide.mjs +115 -0
- package/dist/physics/momentum/impulse.d.mts +89 -0
- package/dist/physics/momentum/impulse.mjs +159 -0
- package/dist/physics/momentum/planar.d.mts +60 -0
- package/dist/physics/momentum/planar.mjs +110 -0
- package/dist/physics/projectile.d.mts +107 -0
- package/dist/physics/projectile.mjs +185 -0
- package/dist/physics/rotation/angular.d.mts +85 -0
- package/dist/physics/rotation/angular.mjs +130 -0
- package/dist/physics/rotation/circular.d.mts +79 -0
- package/dist/physics/rotation/circular.mjs +131 -0
- package/dist/physics/rotation/inertia.d.mts +164 -0
- package/dist/physics/rotation/inertia.mjs +332 -0
- package/dist/physics/rotation/torque.d.mts +65 -0
- package/dist/physics/rotation/torque.mjs +134 -0
- package/dist/physics/vectors.d.mts +64 -0
- package/dist/physics/vectors.mjs +114 -0
- package/dist/primitives/Dot.d.mts +2 -15
- package/dist/primitives/Dot.mjs +6 -4
- package/dist/primitives/Grid.d.mts +33 -17
- package/dist/primitives/Grid.mjs +89 -17
- package/dist/primitives/Label.d.mts +1 -14
- package/dist/primitives/Label.mjs +3 -2
- package/dist/primitives/Lines.d.mts +4 -32
- package/dist/primitives/Lines.mjs +10 -8
- package/dist/primitives/Shapes.d.mts +5 -43
- package/dist/primitives/Shapes.mjs +12 -10
- package/dist/primitives/index.d.mts +2 -2
- package/dist/primitives/index.mjs +2 -2
- package/dist/primitives/props.mjs +31 -0
- package/dist/quantity/index.d.mts +119 -0
- package/dist/quantity/index.mjs +417 -0
- package/dist/relation/derive.d.mts +74 -0
- package/dist/relation/derive.mjs +73 -0
- package/dist/relation/index.d.mts +56 -0
- package/dist/relation/index.mjs +234 -0
- package/dist/scene/Scene.d.mts +6 -1
- package/dist/scene/Scene.mjs +15 -40
- package/dist/scene/evaluators.mjs +1 -1
- package/dist/sim/thermal.mjs +45 -20
- package/dist/thermo/calorimetry.d.mts +207 -0
- package/dist/thermo/calorimetry.mjs +345 -0
- package/dist/thermo/cycles.d.mts +142 -0
- package/dist/thermo/cycles.mjs +256 -0
- package/dist/thermo/entropy.d.mts +87 -0
- package/dist/thermo/entropy.mjs +140 -0
- package/dist/thermo/expansion.d.mts +130 -0
- package/dist/thermo/expansion.mjs +178 -0
- package/dist/thermo/gas.d.mts +197 -0
- package/dist/thermo/gas.mjs +279 -0
- package/dist/thermo/heat-transfer.d.mts +164 -0
- package/dist/thermo/heat-transfer.mjs +217 -0
- package/dist/thermo/index.d.mts +9 -82
- package/dist/thermo/index.mjs +9 -132
- package/dist/thermo/processes.d.mts +95 -0
- package/dist/thermo/processes.mjs +154 -0
- package/dist/thermo/temperature.d.mts +174 -0
- package/dist/thermo/temperature.mjs +236 -0
- package/dist/view/Stage.mjs +4 -11
- package/dist/waves/damping.d.mts +224 -0
- package/dist/waves/damping.mjs +317 -0
- package/dist/waves/doppler.d.mts +72 -0
- package/dist/waves/doppler.mjs +125 -0
- package/dist/waves/index.d.mts +10 -0
- package/dist/waves/index.mjs +10 -0
- package/dist/waves/pendulum.d.mts +130 -0
- package/dist/waves/pendulum.mjs +211 -0
- package/dist/waves/pitch.d.mts +125 -0
- package/dist/waves/pitch.mjs +158 -0
- package/dist/waves/shm.d.mts +170 -0
- package/dist/waves/shm.mjs +279 -0
- package/dist/waves/springs.d.mts +103 -0
- package/dist/waves/springs.mjs +176 -0
- package/dist/waves/superposition.d.mts +124 -0
- package/dist/waves/superposition.mjs +196 -0
- package/dist/waves/wave.d.mts +154 -0
- package/dist/waves/wave.mjs +178 -0
- package/package.json +103 -23
- package/styles.css +125 -8
- package/dist/assets/kit/index.mjs +0 -4
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
//#region src/networking/models/vpn.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* What a VPN actually hides, from whom, and what it quietly costs.
|
|
4
|
+
*
|
|
5
|
+
* This topic is taught almost entirely by advertising, and the advertising is wrong in both
|
|
6
|
+
* directions. A VPN does not make anyone anonymous, and it also does not hand a provider your
|
|
7
|
+
* banking password. The truthful version is more interesting than either claim, and it falls out of
|
|
8
|
+
* one mechanism: the original packet is wrapped inside a new one addressed to the gateway, and the
|
|
9
|
+
* inside is encrypted. So anybody between you and the gateway can see that you are talking to the
|
|
10
|
+
* gateway and nothing else, and the gateway can see everything the encryption around it does not
|
|
11
|
+
* cover.
|
|
12
|
+
*
|
|
13
|
+
* The conclusion worth carrying out of the lesson is that a VPN MOVES trust rather than removing
|
|
14
|
+
* it: whatever your network operator could previously see, the VPN provider now sees instead. That
|
|
15
|
+
* is sometimes an excellent trade (hostile café Wi-Fi, a censoring network) and sometimes a bad one
|
|
16
|
+
* (a free provider funded by selling exactly that view).
|
|
17
|
+
*
|
|
18
|
+
* The second honest point is that HTTPS and a VPN protect different things, so the interesting
|
|
19
|
+
* cases are the combinations rather than either alone. And no arrangement of either hides anything
|
|
20
|
+
* from the site you are actually talking to, because it is the one you are talking to.
|
|
21
|
+
*/
|
|
22
|
+
type ObserverId = "wifi" | "isp" | "gateway" | "website";
|
|
23
|
+
interface Observer {
|
|
24
|
+
id: ObserverId;
|
|
25
|
+
name: string;
|
|
26
|
+
/** For the diagram, where a box is narrower than a sentence. */
|
|
27
|
+
short: string;
|
|
28
|
+
/** Where this watcher sits, in the learner's words. */
|
|
29
|
+
where: string;
|
|
30
|
+
}
|
|
31
|
+
declare const OBSERVERS: Observer[];
|
|
32
|
+
interface Setup {
|
|
33
|
+
vpn: boolean;
|
|
34
|
+
https: boolean;
|
|
35
|
+
}
|
|
36
|
+
interface Field {
|
|
37
|
+
value: string;
|
|
38
|
+
hidden: boolean;
|
|
39
|
+
}
|
|
40
|
+
interface Sighting {
|
|
41
|
+
observer: Observer;
|
|
42
|
+
/** Who this watcher believes is sending. */
|
|
43
|
+
from: Field;
|
|
44
|
+
/** Where this watcher believes it is going. */
|
|
45
|
+
to: Field;
|
|
46
|
+
contents: Field;
|
|
47
|
+
verdict: string;
|
|
48
|
+
}
|
|
49
|
+
/** Watchers that exist in this setup. Without a tunnel there is no gateway to watch. */
|
|
50
|
+
declare const observersFor: (setup: Setup) => Observer[];
|
|
51
|
+
declare function observe(id: ObserverId, setup: Setup): Sighting | null;
|
|
52
|
+
/** Every watcher's view of one setup, which is where the pattern shows up. */
|
|
53
|
+
declare const surveyOf: (setup: Setup) => Sighting[];
|
|
54
|
+
/** Watchers who can name the site being visited. The count is the honest summary of a setup. */
|
|
55
|
+
declare const canSeeDestination: (setup: Setup) => ObserverId[];
|
|
56
|
+
/** Watchers who can read what you actually sent. */
|
|
57
|
+
declare const canReadContents: (setup: Setup) => ObserverId[];
|
|
58
|
+
/**
|
|
59
|
+
* The tunnel's own cost. A VPN wraps a whole packet inside another one, so the outer headers plus
|
|
60
|
+
* the encryption are pure overhead, and the room left for real data shrinks by the same amount.
|
|
61
|
+
* WireGuard's 60 bytes is the honest modern figure; older tunnels cost noticeably more.
|
|
62
|
+
*/
|
|
63
|
+
declare const TUNNEL_OVERHEAD_BYTES = 60;
|
|
64
|
+
declare const usableBytes: (mtu: number, setup: Setup) => number;
|
|
65
|
+
//#endregion
|
|
66
|
+
export { Field, OBSERVERS, Observer, ObserverId, Setup, Sighting, TUNNEL_OVERHEAD_BYTES, canReadContents, canSeeDestination, observe, observersFor, surveyOf, usableBytes };
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
//#region src/networking/models/vpn.ts
|
|
2
|
+
const OBSERVERS = [
|
|
3
|
+
{
|
|
4
|
+
id: "wifi",
|
|
5
|
+
name: "The café Wi-Fi",
|
|
6
|
+
short: "Café Wi-Fi",
|
|
7
|
+
where: "anyone else on the same network, including whoever runs it"
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
id: "isp",
|
|
11
|
+
name: "Your internet provider",
|
|
12
|
+
short: "Your provider",
|
|
13
|
+
where: "every packet you send crosses their equipment"
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
id: "gateway",
|
|
17
|
+
name: "The VPN provider",
|
|
18
|
+
short: "VPN provider",
|
|
19
|
+
where: "the far end of the tunnel, where the wrapping comes off"
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
id: "website",
|
|
23
|
+
name: "The website",
|
|
24
|
+
short: "The website",
|
|
25
|
+
where: "the machine you are actually talking to"
|
|
26
|
+
}
|
|
27
|
+
];
|
|
28
|
+
const YOU = "You";
|
|
29
|
+
const GATEWAY = "VPN gateway";
|
|
30
|
+
const SITE = "example.com";
|
|
31
|
+
const CIPHER = "unreadable";
|
|
32
|
+
/** Watchers that exist in this setup. Without a tunnel there is no gateway to watch. */
|
|
33
|
+
const observersFor = (setup) => OBSERVERS.filter((observer) => observer.id !== "gateway" || setup.vpn);
|
|
34
|
+
function observe(id, setup) {
|
|
35
|
+
const observer = OBSERVERS.find((item) => item.id === id);
|
|
36
|
+
if (!observer || id === "gateway" && !setup.vpn) return null;
|
|
37
|
+
if (id === "website") return {
|
|
38
|
+
observer,
|
|
39
|
+
from: {
|
|
40
|
+
value: setup.vpn ? GATEWAY : YOU,
|
|
41
|
+
hidden: false
|
|
42
|
+
},
|
|
43
|
+
to: {
|
|
44
|
+
value: SITE,
|
|
45
|
+
hidden: false
|
|
46
|
+
},
|
|
47
|
+
contents: {
|
|
48
|
+
value: "your request, in full",
|
|
49
|
+
hidden: false
|
|
50
|
+
},
|
|
51
|
+
verdict: setup.vpn ? "Sees a request arriving from the VPN gateway rather than from you, and reads it in full. It has to: it is the one answering." : "Sees your own address and reads the request in full. HTTPS protects it from everyone in between, never from the far end."
|
|
52
|
+
};
|
|
53
|
+
if (setup.vpn) {
|
|
54
|
+
if (id === "gateway") return {
|
|
55
|
+
observer,
|
|
56
|
+
from: {
|
|
57
|
+
value: YOU,
|
|
58
|
+
hidden: false
|
|
59
|
+
},
|
|
60
|
+
to: {
|
|
61
|
+
value: SITE,
|
|
62
|
+
hidden: false
|
|
63
|
+
},
|
|
64
|
+
contents: setup.https ? {
|
|
65
|
+
value: CIPHER,
|
|
66
|
+
hidden: true
|
|
67
|
+
} : {
|
|
68
|
+
value: "your request, in full",
|
|
69
|
+
hidden: false
|
|
70
|
+
},
|
|
71
|
+
verdict: setup.https ? "Knows who you are and every site you ask for, but HTTPS still keeps the contents from them. This is the trade: your provider stopped seeing this and they started." : "Knows who you are, every site you ask for, and everything you send. Without HTTPS you have simply changed which company watches you."
|
|
72
|
+
};
|
|
73
|
+
return {
|
|
74
|
+
observer,
|
|
75
|
+
from: {
|
|
76
|
+
value: YOU,
|
|
77
|
+
hidden: false
|
|
78
|
+
},
|
|
79
|
+
to: {
|
|
80
|
+
value: GATEWAY,
|
|
81
|
+
hidden: false
|
|
82
|
+
},
|
|
83
|
+
contents: {
|
|
84
|
+
value: CIPHER,
|
|
85
|
+
hidden: true
|
|
86
|
+
},
|
|
87
|
+
verdict: "Can tell you are using a VPN and nothing else. Which sites you visit is inside the wrapping, so it is not available here at any price."
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
return {
|
|
91
|
+
observer,
|
|
92
|
+
from: {
|
|
93
|
+
value: YOU,
|
|
94
|
+
hidden: false
|
|
95
|
+
},
|
|
96
|
+
to: {
|
|
97
|
+
value: SITE,
|
|
98
|
+
hidden: false
|
|
99
|
+
},
|
|
100
|
+
contents: setup.https ? {
|
|
101
|
+
value: CIPHER,
|
|
102
|
+
hidden: true
|
|
103
|
+
} : {
|
|
104
|
+
value: "your request, in full",
|
|
105
|
+
hidden: false
|
|
106
|
+
},
|
|
107
|
+
verdict: setup.https ? "Sees which sites you visit and when, but not what you asked for or what came back." : "Sees which sites you visit and can read every word of it."
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
/** Every watcher's view of one setup, which is where the pattern shows up. */
|
|
111
|
+
const surveyOf = (setup) => observersFor(setup).map((observer) => observe(observer.id, setup)).filter((sighting) => sighting !== null);
|
|
112
|
+
/** Watchers who can name the site being visited. The count is the honest summary of a setup. */
|
|
113
|
+
const canSeeDestination = (setup) => surveyOf(setup).filter((sighting) => !sighting.to.hidden && sighting.to.value === SITE).map((sighting) => sighting.observer.id);
|
|
114
|
+
/** Watchers who can read what you actually sent. */
|
|
115
|
+
const canReadContents = (setup) => surveyOf(setup).filter((sighting) => !sighting.contents.hidden).map((sighting) => sighting.observer.id);
|
|
116
|
+
/**
|
|
117
|
+
* The tunnel's own cost. A VPN wraps a whole packet inside another one, so the outer headers plus
|
|
118
|
+
* the encryption are pure overhead, and the room left for real data shrinks by the same amount.
|
|
119
|
+
* WireGuard's 60 bytes is the honest modern figure; older tunnels cost noticeably more.
|
|
120
|
+
*/
|
|
121
|
+
const TUNNEL_OVERHEAD_BYTES = 60;
|
|
122
|
+
const usableBytes = (mtu, setup) => mtu - (setup.vpn ? 60 : 0);
|
|
123
|
+
|
|
124
|
+
//#endregion
|
|
125
|
+
export { OBSERVERS, TUNNEL_OVERHEAD_BYTES, canReadContents, canSeeDestination, observe, observersFor, surveyOf, usableBytes };
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
//#region src/networking/models/wifi-privacy.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* What the person at the next table can actually read.
|
|
4
|
+
*
|
|
5
|
+
* Chapter 1 established the fact this whole chapter turns on: radio reaches everyone in range,
|
|
6
|
+
* whether or not they were meant to have it. Nothing about Wi-Fi decides who RECEIVES your signal.
|
|
7
|
+
* Every laptop in the cafe already has it. The only question ever at issue is who can make sense of
|
|
8
|
+
* it, and that is decided by encryption in two separate places that protect two different things.
|
|
9
|
+
*
|
|
10
|
+
* Students arrive with one of two wrong models. Either "it has a password, so it is private", or
|
|
11
|
+
* "there is a padlock in the browser, so it is private". Each is half right in a way that leaves a
|
|
12
|
+
* real hole, and the point of this model is to make both holes visible at once.
|
|
13
|
+
*
|
|
14
|
+
* The one that surprises people is the shared password. A cafe password written on a chalkboard is
|
|
15
|
+
* not a secret, and on WPA2 it is also the material from which every customer's key is derived. Any
|
|
16
|
+
* other customer who has that password, and who was listening when your device joined, can decrypt
|
|
17
|
+
* your traffic. The network is encrypted and it is not private FROM the people it is shared with.
|
|
18
|
+
* WPA3 fixes exactly this, which is the whole reason it exists and is almost never how it is sold.
|
|
19
|
+
*
|
|
20
|
+
* The other hole runs the opposite way. HTTPS protects what you sent from everyone, including the
|
|
21
|
+
* cafe and your provider, and it does not hide WHERE you sent it. The destination is still on the
|
|
22
|
+
* wire, so anyone who can read the link learns every site you visited even though they see none of
|
|
23
|
+
* the contents.
|
|
24
|
+
*/
|
|
25
|
+
/** How the radio link itself is protected. */
|
|
26
|
+
type Protection = "open" | "wep" | "wpa2" | "wpa3";
|
|
27
|
+
/** How the conversation with the website is protected, independently of the link. */
|
|
28
|
+
type Transport = "http" | "https";
|
|
29
|
+
/** Who is listening, which decides whether the link's key is available to them. */
|
|
30
|
+
type Listener = "stranger" | "customer";
|
|
31
|
+
interface ProtectionSpec {
|
|
32
|
+
id: Protection;
|
|
33
|
+
label: string;
|
|
34
|
+
note: string;
|
|
35
|
+
}
|
|
36
|
+
declare const PROTECTIONS: Record<Protection, ProtectionSpec>;
|
|
37
|
+
declare const LISTENERS: Record<Listener, {
|
|
38
|
+
id: Listener;
|
|
39
|
+
label: string;
|
|
40
|
+
note: string;
|
|
41
|
+
}>;
|
|
42
|
+
/**
|
|
43
|
+
* Can this listener decrypt the radio link?
|
|
44
|
+
*
|
|
45
|
+
* The WEP row is the one worth reading twice. It is nominally encryption and belongs in the same
|
|
46
|
+
* column as an open network, because a key that anyone can recover is not a key. Stating that in the
|
|
47
|
+
* model rather than in a footnote is the difference between a student who avoids WEP and one who
|
|
48
|
+
* ticks the box marked "encrypted".
|
|
49
|
+
*/
|
|
50
|
+
declare function linkReadable(protection: Protection, listener: Listener): boolean;
|
|
51
|
+
interface Exposure {
|
|
52
|
+
id: string;
|
|
53
|
+
/** What is at stake, in the words someone would use about their own life. */
|
|
54
|
+
label: string;
|
|
55
|
+
visible: boolean;
|
|
56
|
+
/** Shown when visible. Entirely fictional, and obviously so. */
|
|
57
|
+
value: string;
|
|
58
|
+
/** Why it is or is not readable, which is the part that has to be learned. */
|
|
59
|
+
why: string;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Everything a listener learns, most harmless first.
|
|
63
|
+
*
|
|
64
|
+
* Ordered that way on purpose. A list that opens with the password makes this look like a lesson
|
|
65
|
+
* about passwords, and it is not: the top two rows leak on EVERY setting including the best one, and
|
|
66
|
+
* a student who notices that has understood something about radio that no amount of encryption
|
|
67
|
+
* changes.
|
|
68
|
+
*/
|
|
69
|
+
declare function whatTheySee(protection: Protection, transport: Transport, listener: Listener): Exposure[];
|
|
70
|
+
interface PrivacyVerdict {
|
|
71
|
+
/** How many of the five things this listener can read. */
|
|
72
|
+
exposed: number;
|
|
73
|
+
total: number;
|
|
74
|
+
/** The single sentence a learner should take away from this combination. */
|
|
75
|
+
headline: string;
|
|
76
|
+
tone: "bad" | "mixed" | "good";
|
|
77
|
+
}
|
|
78
|
+
declare function judgePrivacy(protection: Protection, transport: Transport, listener: Listener): PrivacyVerdict;
|
|
79
|
+
/** The honest summary: which of the two protections fixes which hole. */
|
|
80
|
+
declare const ADVICE: string[];
|
|
81
|
+
//#endregion
|
|
82
|
+
export { ADVICE, Exposure, LISTENERS, Listener, PROTECTIONS, PrivacyVerdict, Protection, ProtectionSpec, Transport, judgePrivacy, linkReadable, whatTheySee };
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
//#region src/networking/models/wifi-privacy.ts
|
|
2
|
+
const PROTECTIONS = {
|
|
3
|
+
open: {
|
|
4
|
+
id: "open",
|
|
5
|
+
label: "Open, no password",
|
|
6
|
+
note: "The link carries plain radio that anyone in range can decode with ordinary hardware. Common in cafes, airports and hotels precisely because it is convenient."
|
|
7
|
+
},
|
|
8
|
+
wep: {
|
|
9
|
+
id: "wep",
|
|
10
|
+
label: "WEP",
|
|
11
|
+
note: "Encrypted in name only. The key can be recovered from captured traffic in minutes with tools that have existed since 2001, so treat a WEP network as an open one."
|
|
12
|
+
},
|
|
13
|
+
wpa2: {
|
|
14
|
+
id: "wpa2",
|
|
15
|
+
label: "WPA2, shared password",
|
|
16
|
+
note: "Genuinely encrypted against outsiders. Every user derives their key from the SAME shared password, so anyone who knows it and captured your device joining can derive your key too."
|
|
17
|
+
},
|
|
18
|
+
wpa3: {
|
|
19
|
+
id: "wpa3",
|
|
20
|
+
label: "WPA3",
|
|
21
|
+
note: "Each session negotiates its own key, so knowing the password does not let anyone decrypt anybody else. This is the actual point of WPA3 and it is rarely how it is advertised."
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
const LISTENERS = {
|
|
25
|
+
stranger: {
|
|
26
|
+
id: "stranger",
|
|
27
|
+
label: "Someone in range",
|
|
28
|
+
note: "A person sitting nearby who never joined the network. They receive every packet regardless, because that is what radio does."
|
|
29
|
+
},
|
|
30
|
+
customer: {
|
|
31
|
+
id: "customer",
|
|
32
|
+
label: "Another customer",
|
|
33
|
+
note: "Someone who read the same password off the same chalkboard. They are inside the network, which turns out to matter more than most people expect."
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* Can this listener decrypt the radio link?
|
|
38
|
+
*
|
|
39
|
+
* The WEP row is the one worth reading twice. It is nominally encryption and belongs in the same
|
|
40
|
+
* column as an open network, because a key that anyone can recover is not a key. Stating that in the
|
|
41
|
+
* model rather than in a footnote is the difference between a student who avoids WEP and one who
|
|
42
|
+
* ticks the box marked "encrypted".
|
|
43
|
+
*/
|
|
44
|
+
function linkReadable(protection, listener) {
|
|
45
|
+
if (protection === "open" || protection === "wep") return true;
|
|
46
|
+
if (protection === "wpa2") return listener === "customer";
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Everything a listener learns, most harmless first.
|
|
51
|
+
*
|
|
52
|
+
* Ordered that way on purpose. A list that opens with the password makes this look like a lesson
|
|
53
|
+
* about passwords, and it is not: the top two rows leak on EVERY setting including the best one, and
|
|
54
|
+
* a student who notices that has understood something about radio that no amount of encryption
|
|
55
|
+
* changes.
|
|
56
|
+
*/
|
|
57
|
+
function whatTheySee(protection, transport, listener) {
|
|
58
|
+
const readable = linkReadable(protection, listener);
|
|
59
|
+
const contentReadable = readable && transport === "http";
|
|
60
|
+
return [
|
|
61
|
+
{
|
|
62
|
+
id: "presence",
|
|
63
|
+
label: "That you are here at all",
|
|
64
|
+
visible: true,
|
|
65
|
+
value: "a device transmitting, a few metres away",
|
|
66
|
+
why: "Radio cannot be made private in this respect. Anything that transmits announces that something is transmitting, on every setting there is."
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
id: "device",
|
|
70
|
+
label: "Your device's address",
|
|
71
|
+
visible: true,
|
|
72
|
+
value: "a4:83:e7:2c:91:0f",
|
|
73
|
+
why: "Addresses ride outside the encryption, because the network needs them to deliver anything at all. This is why phones now invent a new one per network."
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
id: "site",
|
|
77
|
+
label: "Which sites you visited",
|
|
78
|
+
visible: readable,
|
|
79
|
+
value: "bank.example.com, then clinic.example.org",
|
|
80
|
+
why: readable ? "The destination has to be readable by whatever carries the packet. HTTPS hides what you sent and not where you sent it." : "The link is encrypted and this listener has no key for it, so even the destinations are hidden."
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
id: "page",
|
|
84
|
+
label: "What was on the page",
|
|
85
|
+
visible: contentReadable,
|
|
86
|
+
value: "Account balance, transactions, the lot",
|
|
87
|
+
why: contentReadable ? "Plain HTTP puts the page on the air as text. Anyone who can read the link can read the page." : readable ? "HTTPS encrypts the page end to end, so reading the link is not enough." : "The link cannot be read, and the page is separately encrypted on top of that."
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
id: "credentials",
|
|
91
|
+
label: "What you typed in",
|
|
92
|
+
visible: contentReadable,
|
|
93
|
+
value: "user=you@example.com password=OpenSesame1",
|
|
94
|
+
why: contentReadable ? "A form submitted over plain HTTP travels as text, including the password field." : readable ? "HTTPS protects what you submit even when the link around it is wide open." : "Protected twice over: the link is encrypted and so is the conversation inside it."
|
|
95
|
+
}
|
|
96
|
+
];
|
|
97
|
+
}
|
|
98
|
+
function judgePrivacy(protection, transport, listener) {
|
|
99
|
+
const rows = whatTheySee(protection, transport, listener);
|
|
100
|
+
const exposed = rows.filter((row) => row.visible).length;
|
|
101
|
+
const readable = linkReadable(protection, listener);
|
|
102
|
+
let headline;
|
|
103
|
+
let tone;
|
|
104
|
+
if (readable && transport === "http") {
|
|
105
|
+
headline = "Everything, including what you typed. This is a stranger reading over your shoulder.";
|
|
106
|
+
tone = "bad";
|
|
107
|
+
} else if (readable) {
|
|
108
|
+
headline = "Not the contents, but every site you visited. That alone says a great deal about you.";
|
|
109
|
+
tone = "mixed";
|
|
110
|
+
} else {
|
|
111
|
+
headline = "Only that a device is here, and its address. The link and the contents are both closed.";
|
|
112
|
+
tone = "good";
|
|
113
|
+
}
|
|
114
|
+
return {
|
|
115
|
+
exposed,
|
|
116
|
+
total: rows.length,
|
|
117
|
+
headline,
|
|
118
|
+
tone
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
/** The honest summary: which of the two protections fixes which hole. */
|
|
122
|
+
const ADVICE = [
|
|
123
|
+
"HTTPS protects what you send from everyone, including the cafe, the network owner and your provider. It does not hide where you sent it.",
|
|
124
|
+
"A network password protects the link from outsiders. On WPA2 it does not protect you from the other people who have that same password.",
|
|
125
|
+
"WPA3 gives every session its own key, so a shared password stops being a shared key. This is the hole it was designed to close.",
|
|
126
|
+
"Nothing hides the fact that a device is transmitting, or the address it transmits with, because the network needs both to work."
|
|
127
|
+
];
|
|
128
|
+
|
|
129
|
+
//#endregion
|
|
130
|
+
export { ADVICE, LISTENERS, PROTECTIONS, judgePrivacy, linkReadable, whatTheySee };
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
//#region src/networking/models/wifi.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Indoor radio coverage: why the far bedroom is dead, and why moving the router two metres fixes it.
|
|
4
|
+
*
|
|
5
|
+
* Wireless is taught as a list of standards and a table of speeds, which produces students who can
|
|
6
|
+
* state that 5 GHz is faster and cannot explain why their own phone drops to 2.4 GHz in the kitchen.
|
|
7
|
+
* The thing that decides real coverage is not the standard. It is DISTANCE and WHAT IS IN THE WAY,
|
|
8
|
+
* and both are geometry, so both can be drawn.
|
|
9
|
+
*
|
|
10
|
+
* Two mechanisms, and students need to feel them separately:
|
|
11
|
+
*
|
|
12
|
+
* Spreading. A transmitter radiates into a sphere. Double the distance and the same power is
|
|
13
|
+
* spread over four times the area, so the received power falls with the SQUARE of distance even
|
|
14
|
+
* with nothing in the way. Indoors it falls faster still, because energy is absorbed rather than
|
|
15
|
+
* simply spread, which is what the path-loss exponent below carries.
|
|
16
|
+
*
|
|
17
|
+
* Absorption. Every wall takes a fixed bite out of the signal as it passes through, and the size
|
|
18
|
+
* of that bite depends on the material and on the frequency. This is the half that a speed table
|
|
19
|
+
* hides completely, and it is the half that decides where a router should sit.
|
|
20
|
+
*
|
|
21
|
+
* The frequency trade is the payoff, and it is genuinely counter-intuitive: 5 GHz is the faster band
|
|
22
|
+
* and the WORSE one. A shorter wave starts about 6 dB down at the same distance and loses roughly
|
|
23
|
+
* twice as much through every wall it meets. So the honest statement is not "5 GHz is better", it is
|
|
24
|
+
* "5 GHz is faster where it reaches, and it reaches less far". A learner who has dragged a router
|
|
25
|
+
* around a floor plan owns that sentence. A learner who read a table does not.
|
|
26
|
+
*
|
|
27
|
+
* Everything here is in decibels, because absorption is multiplicative: two brick walls do not
|
|
28
|
+
* subtract twice, they halve twice. Decibels turn that into addition, which is the entire reason
|
|
29
|
+
* radio engineering uses them, and it is worth a learner meeting that reason in a place where the
|
|
30
|
+
* arithmetic is visibly easier rather than in a definition.
|
|
31
|
+
*/
|
|
32
|
+
/** The two bands a home or campus access point actually offers. */
|
|
33
|
+
type Band = "2.4" | "5";
|
|
34
|
+
interface BandSpec {
|
|
35
|
+
id: Band;
|
|
36
|
+
label: string;
|
|
37
|
+
/** Centre frequency in MHz, used for the one-metre reference loss. */
|
|
38
|
+
megahertz: number;
|
|
39
|
+
/** Non-overlapping 20 MHz channels. The reason a crowded 2.4 GHz band stays slow. */
|
|
40
|
+
channels: number;
|
|
41
|
+
/** Ceiling for a good link on this band, in Mbps, for a typical two-stream client. */
|
|
42
|
+
peakMbps: number;
|
|
43
|
+
note: string;
|
|
44
|
+
}
|
|
45
|
+
declare const BANDS: Record<Band, BandSpec>;
|
|
46
|
+
/**
|
|
47
|
+
* Wall materials, ordered as a learner meets them: the partition you could put a fist through,
|
|
48
|
+
* up to the lift shaft nothing gets through at all.
|
|
49
|
+
*/
|
|
50
|
+
type WallMaterial = "drywall" | "wood" | "glass" | "brick" | "concrete" | "metal";
|
|
51
|
+
interface MaterialSpec {
|
|
52
|
+
id: WallMaterial;
|
|
53
|
+
label: string;
|
|
54
|
+
/** One-way loss in dB, per band. Typical measured values for a single wall of this kind. */
|
|
55
|
+
loss: Record<Band, number>;
|
|
56
|
+
note: string;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Loss per wall, in dB. These are the numbers a site survey actually uses, rounded to whole
|
|
60
|
+
* decibels because the spread between real buildings is wider than the rounding.
|
|
61
|
+
*
|
|
62
|
+
* The column to read across is the band. Concrete costs 12 dB at 2.4 GHz and 20 dB at 5 GHz: the
|
|
63
|
+
* SAME wall takes eight extra decibels out of the faster band, which is a further sixfold drop in
|
|
64
|
+
* power. One interior wall is survivable on either band. Two concrete walls is where 5 GHz stops
|
|
65
|
+
* being a choice.
|
|
66
|
+
*/
|
|
67
|
+
declare const MATERIALS: Record<WallMaterial, MaterialSpec>;
|
|
68
|
+
/** A wall segment on the plan, in metres. */
|
|
69
|
+
interface Wall {
|
|
70
|
+
x1: number;
|
|
71
|
+
y1: number;
|
|
72
|
+
x2: number;
|
|
73
|
+
y2: number;
|
|
74
|
+
material: WallMaterial;
|
|
75
|
+
}
|
|
76
|
+
/** A named rectangular room, in metres, used for per-room verdicts. */
|
|
77
|
+
interface Room {
|
|
78
|
+
id: string;
|
|
79
|
+
name: string;
|
|
80
|
+
x: number;
|
|
81
|
+
y: number;
|
|
82
|
+
w: number;
|
|
83
|
+
h: number;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* A place a router could realistically stand, named the way someone would say it out loud.
|
|
87
|
+
*
|
|
88
|
+
* Offering named spots rather than a free drag is a teaching decision, not a technical one. A
|
|
89
|
+
* learner dragging a marker optimises by watching the colour and stops as soon as it turns green,
|
|
90
|
+
* having formed no reason. Choosing between "the corner where the cable enters" and "the hallway
|
|
91
|
+
* wall" is a choice between two things you could actually do on a Saturday, and it has to be
|
|
92
|
+
* justified before the answer appears.
|
|
93
|
+
*/
|
|
94
|
+
interface Spot {
|
|
95
|
+
id: string;
|
|
96
|
+
name: string;
|
|
97
|
+
x: number;
|
|
98
|
+
y: number;
|
|
99
|
+
}
|
|
100
|
+
interface FloorPlan {
|
|
101
|
+
id: string;
|
|
102
|
+
name: string;
|
|
103
|
+
/** Overall extent in metres, so the drawing and the physics share one coordinate system. */
|
|
104
|
+
widthM: number;
|
|
105
|
+
heightM: number;
|
|
106
|
+
rooms: Room[];
|
|
107
|
+
walls: Wall[];
|
|
108
|
+
spots: Spot[];
|
|
109
|
+
note: string;
|
|
110
|
+
}
|
|
111
|
+
interface Point {
|
|
112
|
+
x: number;
|
|
113
|
+
y: number;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Transmit power of a domestic access point, in dBm. 20 dBm is 100 mW, the legal ceiling in most
|
|
117
|
+
* places and what essentially every home router emits, which is worth knowing because it means
|
|
118
|
+
* "buy a more powerful router" is not available as an answer. The only levers are position,
|
|
119
|
+
* frequency, and a second access point.
|
|
120
|
+
*/
|
|
121
|
+
declare const TX_POWER_DBM = 20;
|
|
122
|
+
/**
|
|
123
|
+
* How fast power falls with distance indoors.
|
|
124
|
+
*
|
|
125
|
+
* In free space the exponent is 2: pure spreading over a sphere. Indoors, 3.0 is the standard
|
|
126
|
+
* figure for a home or small office, because furniture, floors and people absorb as well as the
|
|
127
|
+
* geometry spreading. The walls are counted separately and explicitly, so this number represents
|
|
128
|
+
* everything that is NOT a wall the learner placed.
|
|
129
|
+
*/
|
|
130
|
+
declare const PATH_LOSS_EXPONENT = 3;
|
|
131
|
+
/**
|
|
132
|
+
* The floor below which a number stops meaning anything, in dBm.
|
|
133
|
+
*
|
|
134
|
+
* Thermal noise in a 20 MHz channel sits near -101 dBm, so a receiver cannot distinguish a signal
|
|
135
|
+
* from the noise it arrives in much below this. An uncapped model happily reports -114 dBm, which
|
|
136
|
+
* looks precise and is fiction: the honest statement is "below the noise floor", and the lab should
|
|
137
|
+
* say that rather than print a number a real radio could never measure.
|
|
138
|
+
*/
|
|
139
|
+
declare const NOISE_FLOOR_DBM = -100;
|
|
140
|
+
/**
|
|
141
|
+
* Loss over the first metre, in dB. This is where frequency enters before any wall is involved:
|
|
142
|
+
* about 40 dB at 2.4 GHz and 46 dB at 5 GHz. The faster band starts six decibels behind at the
|
|
143
|
+
* very first metre, having passed through nothing at all.
|
|
144
|
+
*/
|
|
145
|
+
declare function referenceLossDb(band: Band): number;
|
|
146
|
+
/** The walls a straight line from the router to a point passes through. */
|
|
147
|
+
declare function wallsBetween(plan: FloorPlan, from: Point, to: Point): Wall[];
|
|
148
|
+
interface Reading {
|
|
149
|
+
/** Received power in dBm. Negative, and less negative is better. */
|
|
150
|
+
rssi: number;
|
|
151
|
+
distanceM: number;
|
|
152
|
+
/** Every wall on the straight path, in no particular order. */
|
|
153
|
+
obstacles: Wall[];
|
|
154
|
+
/** Total absorption from those walls, in dB. */
|
|
155
|
+
wallLossDb: number;
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Received signal strength at a point, by the log-distance model with explicit wall terms.
|
|
159
|
+
*
|
|
160
|
+
* This treats the signal as travelling in a straight line and ignores reflection, which in a real
|
|
161
|
+
* flat delivers some signal around a corner that this model calls dead. That simplification is
|
|
162
|
+
* deliberate and is the right one for teaching: it keeps the cause of every number visible, and a
|
|
163
|
+
* model that says "a wall costs you this much" is exactly the intuition a learner should leave with.
|
|
164
|
+
*/
|
|
165
|
+
declare function signalAt(plan: FloorPlan, router: Point, point: Point, band: Band): Reading;
|
|
166
|
+
type Grade = "excellent" | "good" | "usable" | "weak" | "dead";
|
|
167
|
+
interface GradeSpec {
|
|
168
|
+
id: Grade;
|
|
169
|
+
label: string;
|
|
170
|
+
/** Lowest RSSI in dBm that still counts as this grade. */
|
|
171
|
+
floor: number;
|
|
172
|
+
/** What the learner could actually do standing here. */
|
|
173
|
+
can: string;
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* The grade ladder, in dBm.
|
|
177
|
+
*
|
|
178
|
+
* These are the thresholds a real site survey designs against rather than round numbers chosen to
|
|
179
|
+
* look tidy: -67 dBm is the industry target for voice and video, and -70 is where ordinary data
|
|
180
|
+
* starts to struggle. Naming what a learner could DO at each level, rather than calling it "3 bars",
|
|
181
|
+
* is the point. Bars are a made-up scale that differs between handsets; dBm is a measurement.
|
|
182
|
+
*/
|
|
183
|
+
declare const GRADES: GradeSpec[];
|
|
184
|
+
declare function grade(rssi: number): GradeSpec;
|
|
185
|
+
/**
|
|
186
|
+
* A rough throughput for a reading, in Mbps.
|
|
187
|
+
*
|
|
188
|
+
* Real rate selection is a ladder of modulation schemes, and the shape that matters to a learner is
|
|
189
|
+
* that throughput does not fade gently with distance: it holds up, then falls off a cliff as each
|
|
190
|
+
* rung becomes unreadable. Interpolating between the grade thresholds reproduces that staircase
|
|
191
|
+
* feel without pretending to model MCS tables.
|
|
192
|
+
*/
|
|
193
|
+
declare function throughputMbps(rssi: number, band: Band): number;
|
|
194
|
+
interface RoomVerdict {
|
|
195
|
+
room: Room;
|
|
196
|
+
/** The weakest sample inside the room: a room is only as good as its worst corner. */
|
|
197
|
+
rssi: number;
|
|
198
|
+
grade: GradeSpec;
|
|
199
|
+
mbps: number;
|
|
200
|
+
/** Materials responsible for the worst corner, most costly first. */
|
|
201
|
+
blamed: WallMaterial[];
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Grade every room from its WORST corner rather than its centre.
|
|
205
|
+
*
|
|
206
|
+
* Judging a room by its middle is how a coverage plan passes on paper and fails in the building:
|
|
207
|
+
* the desk is against the far wall, not in the centre of the floor. Reporting the worst sample also
|
|
208
|
+
* makes the lab honest when a learner parks the router in the hallway and every room reads "usable"
|
|
209
|
+
* from its midpoint while the bed and the desk do not.
|
|
210
|
+
*/
|
|
211
|
+
declare function roomVerdicts(plan: FloorPlan, router: Point, band: Band): RoomVerdict[];
|
|
212
|
+
/**
|
|
213
|
+
* RSSI on a regular grid over the whole plan, for the heat map.
|
|
214
|
+
*
|
|
215
|
+
* Returned row-major as `[row][col]` with row 0 at the top, matching how the SVG draws it, so the
|
|
216
|
+
* scene never has to flip an index and quietly mirror the picture.
|
|
217
|
+
*/
|
|
218
|
+
declare function coverageGrid(plan: FloorPlan, router: Point, band: Band, cols: number, rows: number): number[][];
|
|
219
|
+
/** Share of the rooms that reach at least `usable`, which is the number a learner is trying to raise. */
|
|
220
|
+
declare function coverageScore(plan: FloorPlan, router: Point, band: Band): number;
|
|
221
|
+
/**
|
|
222
|
+
* Floor plans a learner recognises, which is most of the work.
|
|
223
|
+
*
|
|
224
|
+
* A generic rectangle labelled "room A / room B" teaches the model and none of the intuition. The
|
|
225
|
+
* three-bedroom flat below is the ordinary shape of an apartment in Dhaka or Chattogram: brick
|
|
226
|
+
* partitions, a concrete wet wall around the bathroom, and the router wherever the broadband cable
|
|
227
|
+
* happened to enter, which is almost never where it should be. A learner who fixes THIS plan can
|
|
228
|
+
* walk home and fix theirs, and that transfer is the whole reason to draw a room instead of a graph.
|
|
229
|
+
*/
|
|
230
|
+
declare const FLOOR_PLANS: FloorPlan[];
|
|
231
|
+
/**
|
|
232
|
+
* The best spot on the plan, found by trying a coarse grid of positions.
|
|
233
|
+
*
|
|
234
|
+
* Present this only AFTER a learner has committed to a position of their own. Shown first it is an
|
|
235
|
+
* answer key and they will drag toward it without forming a reason; shown second it is feedback on
|
|
236
|
+
* a guess they already had to justify, and the gap between the two is the lesson.
|
|
237
|
+
*/
|
|
238
|
+
declare function bestPlacement(plan: FloorPlan, band: Band, step?: number): Point;
|
|
239
|
+
//#endregion
|
|
240
|
+
export { BANDS, Band, BandSpec, FLOOR_PLANS, FloorPlan, GRADES, Grade, GradeSpec, MATERIALS, MaterialSpec, NOISE_FLOOR_DBM, PATH_LOSS_EXPONENT, Point, Reading, Room, RoomVerdict, Spot, TX_POWER_DBM, Wall, WallMaterial, bestPlacement, coverageGrid, coverageScore, grade, referenceLossDb, roomVerdicts, signalAt, throughputMbps, wallsBetween };
|