@things-factory/headless-twin 10.0.8 → 10.0.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-server/engine/attention-digest.d.ts +52 -0
- package/dist-server/engine/attention-digest.js +76 -0
- package/dist-server/engine/attention-digest.js.map +1 -0
- package/dist-server/engine/canonical-ingest.d.ts +2 -2
- package/dist-server/engine/canonical-ingest.js +24 -2
- package/dist-server/engine/canonical-ingest.js.map +1 -1
- package/dist-server/engine/command-routing.d.ts +33 -0
- package/dist-server/engine/command-routing.js +53 -0
- package/dist-server/engine/command-routing.js.map +1 -0
- package/dist-server/engine/energy-topology.d.ts +86 -0
- package/dist-server/engine/energy-topology.js +144 -0
- package/dist-server/engine/energy-topology.js.map +1 -0
- package/dist-server/engine/index.d.ts +11 -0
- package/dist-server/engine/index.js +17 -0
- package/dist-server/engine/index.js.map +1 -1
- package/dist-server/engine/kpi-baseline.d.ts +78 -0
- package/dist-server/engine/kpi-baseline.js +123 -0
- package/dist-server/engine/kpi-baseline.js.map +1 -0
- package/dist-server/engine/kpi-fold.d.ts +137 -1
- package/dist-server/engine/kpi-fold.js +225 -0
- package/dist-server/engine/kpi-fold.js.map +1 -1
- package/dist-server/engine/kpi-query.d.ts +32 -1
- package/dist-server/engine/kpi-query.js +262 -20
- package/dist-server/engine/kpi-query.js.map +1 -1
- package/dist-server/engine/kpi-target.d.ts +17 -0
- package/dist-server/engine/kpi-target.js +24 -2
- package/dist-server/engine/kpi-target.js.map +1 -1
- package/dist-server/engine/live-attentions.d.ts +1 -0
- package/dist-server/engine/live-attentions.js +7 -1
- package/dist-server/engine/live-attentions.js.map +1 -1
- package/dist-server/engine/live-feed-registry.d.ts +25 -0
- package/dist-server/engine/live-feed-registry.js +51 -0
- package/dist-server/engine/live-feed-registry.js.map +1 -0
- package/dist-server/engine/load-meter.d.ts +181 -0
- package/dist-server/engine/load-meter.js +267 -0
- package/dist-server/engine/load-meter.js.map +1 -0
- package/dist-server/engine/local-declarations.d.ts +262 -0
- package/dist-server/engine/local-declarations.js +528 -0
- package/dist-server/engine/local-declarations.js.map +1 -0
- package/dist-server/engine/model-basis.d.ts +39 -6
- package/dist-server/engine/model-basis.js +69 -9
- package/dist-server/engine/model-basis.js.map +1 -1
- package/dist-server/engine/model-vocabulary.d.ts +17 -0
- package/dist-server/engine/model-vocabulary.js +81 -0
- package/dist-server/engine/model-vocabulary.js.map +1 -0
- package/dist-server/engine/oee-accumulator.d.ts +28 -0
- package/dist-server/engine/oee-accumulator.js +26 -1
- package/dist-server/engine/oee-accumulator.js.map +1 -1
- package/dist-server/engine/operation-basis.d.ts +32 -0
- package/dist-server/engine/operation-basis.js +87 -0
- package/dist-server/engine/operation-basis.js.map +1 -0
- package/dist-server/engine/property-effects.d.ts +30 -0
- package/dist-server/engine/property-effects.js +192 -0
- package/dist-server/engine/property-effects.js.map +1 -0
- package/dist-server/engine/runtime-key.d.ts +15 -0
- package/dist-server/engine/runtime-key.js +64 -0
- package/dist-server/engine/runtime-key.js.map +1 -0
- package/dist-server/engine/spec-coverage.js +3 -3
- package/dist-server/engine/spec-coverage.js.map +1 -1
- package/dist-server/engine/state-axes.d.ts +21 -0
- package/dist-server/engine/state-axes.js +59 -0
- package/dist-server/engine/state-axes.js.map +1 -0
- package/dist-server/engine/structure-diff.js +1 -1
- package/dist-server/engine/structure-diff.js.map +1 -1
- package/dist-server/engine/travel-estimator.d.ts +9 -2
- package/dist-server/engine/travel-estimator.js +11 -6
- package/dist-server/engine/travel-estimator.js.map +1 -1
- package/dist-server/engine/twin-engine.d.ts +418 -57
- package/dist-server/engine/twin-engine.js +1599 -306
- package/dist-server/engine/twin-engine.js.map +1 -1
- package/dist-server/engine/warm-start.d.ts +93 -13
- package/dist-server/engine/warm-start.js +114 -14
- package/dist-server/engine/warm-start.js.map +1 -1
- package/dist-server/index.d.ts +1 -0
- package/dist-server/index.js +17 -11
- package/dist-server/index.js.map +1 -1
- package/dist-server/migrations/1786000000000-RenameTwinInstanceBoardToModel.d.ts +5 -0
- package/dist-server/migrations/1786000000000-RenameTwinInstanceBoardToModel.js +48 -0
- package/dist-server/migrations/1786000000000-RenameTwinInstanceBoardToModel.js.map +1 -0
- package/dist-server/migrations/index.d.ts +2 -0
- package/dist-server/migrations/index.js +10 -0
- package/dist-server/migrations/index.js.map +1 -0
- package/dist-server/service/index.d.ts +3 -2
- package/dist-server/service/index.js +25 -17
- package/dist-server/service/index.js.map +1 -1
- package/dist-server/service/reference/discovery-result.d.ts +34 -0
- package/dist-server/service/reference/discovery-result.js +84 -0
- package/dist-server/service/reference/discovery-result.js.map +1 -0
- package/dist-server/service/reference/ingest-space.d.ts +53 -0
- package/dist-server/service/reference/ingest-space.js +79 -0
- package/dist-server/service/reference/ingest-space.js.map +1 -0
- package/dist-server/service/reference/knob-defaults.d.ts +20 -0
- package/dist-server/service/reference/knob-defaults.js +59 -0
- package/dist-server/service/reference/knob-defaults.js.map +1 -0
- package/dist-server/service/reference/reference-live.d.ts +23 -0
- package/dist-server/service/reference/reference-live.js +123 -6
- package/dist-server/service/reference/reference-live.js.map +1 -1
- package/dist-server/service/reference/reference-master.d.ts +227 -11
- package/dist-server/service/reference/reference-master.js +206 -25
- package/dist-server/service/reference/reference-master.js.map +1 -1
- package/dist-server/service/reference/reference-resolver.d.ts +33 -3
- package/dist-server/service/reference/reference-resolver.js +290 -23
- package/dist-server/service/reference/reference-resolver.js.map +1 -1
- package/dist-server/service/reference/template-registry.d.ts +19 -3
- package/dist-server/service/reference/template-registry.js.map +1 -1
- package/dist-server/service/twin-attention/twin-attention-query.d.ts +8 -1
- package/dist-server/service/twin-attention/twin-attention-query.js +39 -8
- package/dist-server/service/twin-attention/twin-attention-query.js.map +1 -1
- package/dist-server/service/twin-audit/command-audit.d.ts +37 -0
- package/dist-server/service/twin-audit/command-audit.js +53 -0
- package/dist-server/service/twin-audit/command-audit.js.map +1 -0
- package/dist-server/service/twin-audit/index.d.ts +4 -0
- package/dist-server/service/twin-audit/index.js +8 -0
- package/dist-server/service/twin-audit/index.js.map +1 -0
- package/dist-server/service/twin-audit/twin-audit-event.d.ts +24 -0
- package/dist-server/service/twin-audit/twin-audit-event.js +125 -0
- package/dist-server/service/twin-audit/twin-audit-event.js.map +1 -0
- package/dist-server/service/twin-audit/twin-audit-query.d.ts +4 -0
- package/dist-server/service/twin-audit/twin-audit-query.js +76 -0
- package/dist-server/service/twin-audit/twin-audit-query.js.map +1 -0
- package/dist-server/service/twin-control/twin-control-mutation.d.ts +2 -0
- package/dist-server/service/twin-control/twin-control-mutation.js +50 -11
- package/dist-server/service/twin-control/twin-control-mutation.js.map +1 -1
- package/dist-server/service/twin-event/twin-event-keys.d.ts +1 -1
- package/dist-server/service/twin-event/twin-event-keys.js +1 -1
- package/dist-server/service/twin-event/twin-event-keys.js.map +1 -1
- package/dist-server/service/twin-event/twin-event.d.ts +14 -5
- package/dist-server/service/twin-event/twin-event.js +46 -14
- package/dist-server/service/twin-event/twin-event.js.map +1 -1
- package/dist-server/service/twin-forecast/forecast-metrics.d.ts +13 -0
- package/dist-server/service/twin-forecast/forecast-metrics.js +57 -0
- package/dist-server/service/twin-forecast/forecast-metrics.js.map +1 -0
- package/dist-server/service/twin-forecast/forecast-tuning.d.ts +12 -0
- package/dist-server/service/twin-forecast/forecast-tuning.js +44 -0
- package/dist-server/service/twin-forecast/forecast-tuning.js.map +1 -0
- package/dist-server/service/twin-forecast/gap-analytics.d.ts +28 -2
- package/dist-server/service/twin-forecast/gap-analytics.js +43 -17
- package/dist-server/service/twin-forecast/gap-analytics.js.map +1 -1
- package/dist-server/service/twin-forecast/twin-forecast-query.js +219 -39
- package/dist-server/service/twin-forecast/twin-forecast-query.js.map +1 -1
- package/dist-server/service/twin-instance/twin-instance.d.ts +38 -5
- package/dist-server/service/twin-instance/twin-instance.js +66 -18
- package/dist-server/service/twin-instance/twin-instance.js.map +1 -1
- package/dist-server/service/twin-journal/twin-journal-query.d.ts +11 -4
- package/dist-server/service/twin-journal/twin-journal-query.js +95 -15
- package/dist-server/service/twin-journal/twin-journal-query.js.map +1 -1
- package/dist-server/service/twin-lifecycle/twin-lifecycle-mutation.d.ts +11 -5
- package/dist-server/service/twin-lifecycle/twin-lifecycle-mutation.js +46 -27
- package/dist-server/service/twin-lifecycle/twin-lifecycle-mutation.js.map +1 -1
- package/dist-server/service/twin-metrics/twin-metrics-query.d.ts +1 -0
- package/dist-server/service/twin-metrics/twin-metrics-query.js +63 -1
- package/dist-server/service/twin-metrics/twin-metrics-query.js.map +1 -1
- package/dist-server/service/twin-model/epcis-coverage.d.ts +14 -0
- package/dist-server/service/twin-model/epcis-coverage.js +174 -0
- package/dist-server/service/twin-model/epcis-coverage.js.map +1 -0
- package/dist-server/service/twin-model/iec61850-coverage.d.ts +14 -0
- package/dist-server/service/twin-model/iec61850-coverage.js +98 -0
- package/dist-server/service/twin-model/iec61850-coverage.js.map +1 -0
- package/dist-server/service/twin-model/index.d.ts +13 -0
- package/dist-server/service/twin-model/index.js +29 -0
- package/dist-server/service/twin-model/index.js.map +1 -0
- package/dist-server/service/twin-model/isa95-coverage.d.ts +48 -0
- package/dist-server/service/twin-model/isa95-coverage.js +155 -0
- package/dist-server/service/twin-model/isa95-coverage.js.map +1 -0
- package/dist-server/service/twin-model/project-structure.d.ts +16 -0
- package/dist-server/service/twin-model/project-structure.js +173 -0
- package/dist-server/service/twin-model/project-structure.js.map +1 -0
- package/dist-server/service/twin-model/standard-coverage.d.ts +9 -0
- package/dist-server/service/twin-model/standard-coverage.js +37 -0
- package/dist-server/service/twin-model/standard-coverage.js.map +1 -0
- package/dist-server/service/twin-model/twin-equipment.d.ts +64 -0
- package/dist-server/service/twin-model/twin-equipment.js +134 -0
- package/dist-server/service/twin-model/twin-equipment.js.map +1 -0
- package/dist-server/service/twin-model/twin-lineage-query.d.ts +3 -0
- package/dist-server/service/twin-model/twin-lineage-query.js +206 -0
- package/dist-server/service/twin-model/twin-lineage-query.js.map +1 -0
- package/dist-server/service/twin-model/twin-location.d.ts +58 -0
- package/dist-server/service/twin-model/twin-location.js +130 -0
- package/dist-server/service/twin-model/twin-location.js.map +1 -0
- package/dist-server/service/twin-model/twin-model-item-query.d.ts +141 -0
- package/dist-server/service/twin-model/twin-model-item-query.js +852 -0
- package/dist-server/service/twin-model/twin-model-item-query.js.map +1 -0
- package/dist-server/service/twin-model/twin-model-mutation.d.ts +57 -0
- package/dist-server/service/twin-model/twin-model-mutation.js +490 -0
- package/dist-server/service/twin-model/twin-model-mutation.js.map +1 -0
- package/dist-server/service/twin-model/twin-model-query.d.ts +43 -0
- package/dist-server/service/twin-model/twin-model-query.js +551 -0
- package/dist-server/service/twin-model/twin-model-query.js.map +1 -0
- package/dist-server/service/twin-model/twin-model-tree-query.d.ts +3 -0
- package/dist-server/service/twin-model/twin-model-tree-query.js +158 -0
- package/dist-server/service/twin-model/twin-model-tree-query.js.map +1 -0
- package/dist-server/service/twin-model/twin-operation.d.ts +61 -0
- package/dist-server/service/twin-model/twin-operation.js +137 -0
- package/dist-server/service/twin-model/twin-operation.js.map +1 -0
- package/dist-server/service/twin-space/move-space.d.ts +65 -0
- package/dist-server/service/twin-space/move-space.js +166 -0
- package/dist-server/service/twin-space/move-space.js.map +1 -0
- package/dist-server/service/twin-space/space-integrity.d.ts +97 -0
- package/dist-server/service/twin-space/space-integrity.js +182 -0
- package/dist-server/service/twin-space/space-integrity.js.map +1 -0
- package/dist-server/service/twin-space/twin-space-area.js +4 -4
- package/dist-server/service/twin-space/twin-space-area.js.map +1 -1
- package/dist-server/service/twin-space/twin-space-representation.js +6 -6
- package/dist-server/service/twin-space/twin-space-representation.js.map +1 -1
- package/dist-server/service/twin-space/twin-space-resolver.d.ts +63 -1
- package/dist-server/service/twin-space/twin-space-resolver.js +365 -18
- package/dist-server/service/twin-space/twin-space-resolver.js.map +1 -1
- package/dist-server/service/twin-space/twin-space.d.ts +17 -1
- package/dist-server/service/twin-space/twin-space.js +15 -5
- package/dist-server/service/twin-space/twin-space.js.map +1 -1
- package/dist-server/service/twin-state/twin-state-subscription.js +1 -1
- package/dist-server/service/twin-state/twin-state-subscription.js.map +1 -1
- package/dist-server/service/twin-structure/twin-structure.d.ts +1 -1
- package/dist-server/service/twin-structure/twin-structure.js +8 -7
- package/dist-server/service/twin-structure/twin-structure.js.map +1 -1
- package/dist-server/service/twin-target/twin-target-resolver.js +21 -4
- package/dist-server/service/twin-target/twin-target-resolver.js.map +1 -1
- package/dist-shared/axis-read.d.ts +12 -0
- package/dist-shared/axis-read.js +32 -0
- package/dist-shared/axis-read.js.map +1 -0
- package/dist-shared/entity-delta.d.ts +29 -0
- package/dist-shared/entity-delta.js +287 -0
- package/dist-shared/entity-delta.js.map +1 -0
- package/dist-shared/kpi-broadcast.d.ts +4 -0
- package/dist-shared/kpi-broadcast.js +16 -0
- package/dist-shared/kpi-broadcast.js.map +1 -0
- package/dist-shared/twin-level.d.ts +23 -0
- package/dist-shared/twin-level.js +52 -0
- package/dist-shared/twin-level.js.map +1 -0
- package/package.json +14 -12
- package/server/engine/attention-digest.ts +102 -0
- package/server/engine/canonical-ingest.ts +36 -4
- package/server/engine/command-routing.ts +67 -0
- package/server/engine/energy-topology.ts +189 -0
- package/server/engine/index.ts +17 -0
- package/server/engine/kpi-baseline.ts +202 -0
- package/server/engine/kpi-fold.ts +389 -1
- package/server/engine/kpi-query.ts +295 -22
- package/server/engine/kpi-target.ts +24 -2
- package/server/engine/live-attentions.ts +7 -2
- package/server/engine/live-feed-registry.ts +58 -0
- package/server/engine/load-meter.ts +384 -0
- package/server/engine/local-declarations.ts +700 -0
- package/server/engine/model-basis.ts +78 -10
- package/server/engine/model-vocabulary.ts +82 -0
- package/server/engine/oee-accumulator.ts +34 -1
- package/server/engine/operation-basis.ts +100 -0
- package/server/engine/property-effects.ts +199 -0
- package/server/engine/runtime-key.ts +58 -0
- package/server/engine/spec-coverage.ts +3 -3
- package/server/engine/state-axes.ts +60 -0
- package/server/engine/structure-diff.ts +1 -1
- package/server/engine/travel-estimator.ts +11 -6
- package/server/engine/twin-engine.ts +1710 -297
- package/server/engine/warm-start.ts +207 -22
- package/server/index.ts +13 -10
- package/server/migrations/1786000000000-RenameTwinInstanceBoardToModel.ts +43 -0
- package/server/migrations/index.ts +7 -0
- package/server/service/index.ts +9 -1
- package/server/service/reference/discovery-result.ts +95 -0
- package/server/service/reference/ingest-space.ts +104 -0
- package/server/service/reference/knob-defaults.ts +59 -0
- package/server/service/reference/reference-live.ts +121 -6
- package/server/service/reference/reference-master.ts +362 -35
- package/server/service/reference/reference-resolver.ts +307 -23
- package/server/service/reference/template-registry.ts +20 -5
- package/server/service/twin-attention/twin-attention-query.ts +43 -6
- package/server/service/twin-audit/command-audit.ts +81 -0
- package/server/service/twin-audit/index.ts +5 -0
- package/server/service/twin-audit/twin-audit-event.ts +112 -0
- package/server/service/twin-audit/twin-audit-query.ts +72 -0
- package/server/service/twin-control/twin-control-mutation.ts +53 -13
- package/server/service/twin-event/twin-event-keys.ts +1 -1
- package/server/service/twin-event/twin-event.ts +52 -15
- package/server/service/twin-forecast/forecast-metrics.ts +60 -0
- package/server/service/twin-forecast/forecast-tuning.ts +40 -0
- package/server/service/twin-forecast/gap-analytics.ts +48 -9
- package/server/service/twin-forecast/twin-forecast-query.ts +211 -38
- package/server/service/twin-instance/twin-instance.ts +127 -22
- package/server/service/twin-journal/twin-journal-query.ts +108 -14
- package/server/service/twin-lifecycle/twin-lifecycle-mutation.ts +40 -23
- package/server/service/twin-metrics/twin-metrics-query.ts +60 -3
- package/server/service/twin-model/epcis-coverage.ts +195 -0
- package/server/service/twin-model/iec61850-coverage.ts +117 -0
- package/server/service/twin-model/index.ts +25 -0
- package/server/service/twin-model/isa95-coverage.ts +190 -0
- package/server/service/twin-model/project-structure.ts +199 -0
- package/server/service/twin-model/standard-coverage.ts +35 -0
- package/server/service/twin-model/twin-equipment.ts +150 -0
- package/server/service/twin-model/twin-lineage-query.ts +193 -0
- package/server/service/twin-model/twin-location.ts +140 -0
- package/server/service/twin-model/twin-model-item-query.ts +848 -0
- package/server/service/twin-model/twin-model-mutation.ts +503 -0
- package/server/service/twin-model/twin-model-query.ts +537 -0
- package/server/service/twin-model/twin-model-tree-query.ts +180 -0
- package/server/service/twin-model/twin-operation.ts +150 -0
- package/server/service/twin-space/move-space.ts +262 -0
- package/server/service/twin-space/space-integrity.ts +293 -0
- package/server/service/twin-space/twin-space-area.ts +4 -4
- package/server/service/twin-space/twin-space-representation.ts +6 -6
- package/server/service/twin-space/twin-space-resolver.ts +365 -22
- package/server/service/twin-space/twin-space.ts +40 -6
- package/server/service/twin-state/twin-state-subscription.ts +1 -1
- package/server/service/twin-structure/twin-structure.ts +10 -7
- package/server/service/twin-target/twin-target-resolver.ts +22 -4
- package/shared/axis-read.ts +28 -0
- package/shared/entity-delta.ts +286 -0
- package/shared/kpi-broadcast.ts +13 -0
- package/shared/twin-level.ts +48 -0
- package/test/adopt-structure-live.test.ts +133 -0
- package/test/attention-digest.test.ts +135 -0
- package/test/axis-read.test.ts +87 -0
- package/test/boot-resume.test.ts +205 -0
- package/test/canonical-ingest-vocabularies.test.ts +118 -0
- package/test/capability-mapping.test.ts +5 -5
- package/test/command-routing.test.ts +61 -0
- package/test/declaration-reaches-model.test.ts +178 -0
- package/test/discovery-result.test.ts +75 -0
- package/test/energy-topology.test.ts +113 -0
- package/test/entity-delta.test.ts +195 -25
- package/test/event-time-column.test.ts +67 -0
- package/test/forecast-metrics.test.ts +71 -0
- package/test/forecast-tuning.test.ts +42 -0
- package/test/gap-analytics.test.ts +34 -7
- package/test/ingest-bench.test.ts +9 -9
- package/test/ingest-running-guard.test.ts +136 -0
- package/test/ingest-space.test.ts +78 -0
- package/test/instance-cache-lifecycle.test.ts +110 -0
- package/test/kernel-kind-guard.test.ts +96 -0
- package/test/knob-defaults.test.ts +72 -0
- package/test/kpi-baseline-db.test.ts +216 -0
- package/test/kpi-baseline.test.ts +196 -0
- package/test/kpi-fold.test.ts +393 -2
- package/test/kpi-query-bench.test.ts +130 -0
- package/test/lineage-survives-restart.test.ts +203 -0
- package/test/live-feed-registry.test.ts +67 -0
- package/test/live-kernel-facts.test.ts +161 -0
- package/test/live-mirror-parity.test.ts +54 -14
- package/test/load-meter.test.ts +314 -0
- package/test/local-declarations.test.ts +554 -0
- package/test/master-to-twin.test.ts +128 -36
- package/test/model-basis.test.ts +56 -1
- package/test/model-vocabulary.test.ts +114 -0
- package/test/move-space.test.ts +149 -0
- package/test/mutation-gate.test.ts +122 -0
- package/test/oee-accumulator.test.ts +90 -7
- package/test/operation-basis.test.ts +100 -0
- package/test/operations-capability-db.test.ts +165 -0
- package/test/project-structure-db.test.ts +226 -0
- package/test/projection-reaches-screen.test.ts +272 -0
- package/test/property-effects.test.ts +96 -0
- package/test/registry-key-guard.test.ts +80 -0
- package/test/resync-origin-site.test.ts +77 -0
- package/test/runtime-key.test.ts +66 -0
- package/test/scale-twin-bench.test.ts +7 -7
- package/test/snapshot-freshness.test.ts +60 -0
- package/test/space-integrity.test.ts +223 -0
- package/test/standard-coverage.test.ts +94 -0
- package/test/state-axes.test.ts +74 -0
- package/test/streamline-e2e.test.ts +16 -16
- package/test/structure-revision-db.test.ts +16 -15
- package/test/tenant-registry-db.test.ts +149 -0
- package/test/twin-audit.test.ts +83 -0
- package/test/twin-model-item-db.test.ts +275 -0
- package/test/twin-model-tree-db.test.ts +176 -0
- package/test/twin-origin-resync.test.ts +114 -0
- package/test/warm-start-seam.test.ts +140 -0
- package/test/warm-start.test.ts +223 -4
- package/tsconfig.json +6 -1
- package/tsconfig.shared.json +23 -0
- package/tsconfig.shared.tsbuildinfo +1 -0
- package/tsconfig.tsbuildinfo +1 -0
- package/dist-server/engine/entity-delta.d.ts +0 -19
- package/dist-server/engine/entity-delta.js +0 -161
- package/dist-server/engine/entity-delta.js.map +0 -1
- package/dist-server/service/twin-event/backfill-keys.d.ts +0 -11
- package/dist-server/service/twin-event/backfill-keys.js +0 -63
- package/dist-server/service/twin-event/backfill-keys.js.map +0 -1
- package/dist-server/tsconfig.tsbuildinfo +0 -1
- package/server/engine/entity-delta.ts +0 -169
- package/server/service/twin-event/backfill-keys.ts +0 -72
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* ★ 여러 구간 동시 비교 — **실 DB로 배관을 넘어 본다.**
|
|
3
|
+
*
|
|
4
|
+
* 분포 산수는 `kpi-baseline.test.ts`(순수)가 지킨다. 여기서 지키는 것은 **호스트 배관**이다:
|
|
5
|
+
* 창을 하루씩 뒤로 옮겨 **각 창을 따로 읽는가** · 그 창을 같은 규칙으로 접는가 · 지표 값을 정본 표
|
|
6
|
+
* (`TWIN_METRIC`)로 꺼내는가 · 기록 없는 날을 결측으로 남기는가.
|
|
7
|
+
*
|
|
8
|
+
* 이 배관은 **경계를 넘어야만** 틀리는 것이 드러난다. 실제로 앱의 시뮬 트윈은 저널이 두 시간 남짓이라
|
|
9
|
+
* "지난 7일" 을 물어도 과거 창이 모두 비어 있어(정직하게 자리를 거부한다) 분포 경로를 한 번도 밟지
|
|
10
|
+
* 못했다. 그래서 여러 날에 걸친 저널을 손으로 심어 그 경로를 통과시킨다.
|
|
11
|
+
*
|
|
12
|
+
* 실 엔티티·실 네이밍 전략의 sqlite in-memory 스키마에 **빌드 산출물**을 실행한다.
|
|
13
|
+
* **소스를 고친 뒤에는 `npm run build:server` 를 먼저 돌려야 반영된다.**
|
|
14
|
+
*/
|
|
15
|
+
import { test, before, after } from 'node:test'
|
|
16
|
+
import assert from 'node:assert/strict'
|
|
17
|
+
import { createRequire } from 'node:module'
|
|
18
|
+
import type { DataSource } from 'typeorm'
|
|
19
|
+
|
|
20
|
+
const req = createRequire(import.meta.url)
|
|
21
|
+
const { NamingStrategy, addDataSource, entities: shellEntities, Domain } = req('@things-factory/shell')
|
|
22
|
+
const { entities: authEntities } = req('@things-factory/auth-base')
|
|
23
|
+
const { entities: twinEntities } = req('../dist-server/service/index.js')
|
|
24
|
+
const { computeTwinKpi } = req('../dist-server/engine/index.js')
|
|
25
|
+
const { TwinEvent } = req('../dist-server/service/twin-event/twin-event.js')
|
|
26
|
+
const { TwinInstance } = req('../dist-server/service/twin-instance/twin-instance.js')
|
|
27
|
+
const { DataSource: TypeOrmDataSource } = req('typeorm')
|
|
28
|
+
|
|
29
|
+
let ds: DataSource
|
|
30
|
+
let domainId: string
|
|
31
|
+
|
|
32
|
+
const INSTANCE = 'baseline-probe'
|
|
33
|
+
const MS_DAY = 24 * 60 * 60_000
|
|
34
|
+
|
|
35
|
+
/** 창의 끝 — 고정 시각을 쓴다(테스트가 벽시계에 흔들리지 않게). */
|
|
36
|
+
const TO = Date.parse('2026-03-10T10:00:00.000Z')
|
|
37
|
+
const FROM = TO - 60 * 60_000
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* 완료 한 건 = 시작 + 완료 두 줄. 소요시간이 짝맞춤에서 나오므로 둘 다 필요하다.
|
|
41
|
+
* 완료 시각이 창 안에 들면 그 창의 성과로 센다.
|
|
42
|
+
*/
|
|
43
|
+
function taskRows(dayBack: number, count: number, workMs: number, revBase: number) {
|
|
44
|
+
const rows: any[] = []
|
|
45
|
+
const endBase = TO - dayBack * MS_DAY - 60_000 // 창 끝 1분 전에 완료
|
|
46
|
+
for (let i = 0; i < count; i++) {
|
|
47
|
+
const taskId = `t-${dayBack}-${i}`
|
|
48
|
+
const done = endBase - i * 1000
|
|
49
|
+
const started = done - workMs
|
|
50
|
+
rows.push(
|
|
51
|
+
{
|
|
52
|
+
domain: { id: domainId },
|
|
53
|
+
instanceId: INSTANCE,
|
|
54
|
+
revision: revBase + i * 2,
|
|
55
|
+
eventType: 'task.status',
|
|
56
|
+
eventTime: new Date(started).toISOString(),
|
|
57
|
+
payload: { data: { taskId, status: 'in-progress', resourceRef: 'fk-1' } }
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
domain: { id: domainId },
|
|
61
|
+
instanceId: INSTANCE,
|
|
62
|
+
revision: revBase + i * 2 + 1,
|
|
63
|
+
eventType: 'task.status',
|
|
64
|
+
eventTime: new Date(done).toISOString(),
|
|
65
|
+
/* 완료 어휘는 **커널이 소유한다**(`foldTaskRecords`) — 'done' 이 아니라 'completed' 다.
|
|
66
|
+
처음에 'done' 으로 심었더니 창은 잡히는데 건수가 0 이었다(이 DB 스펙이 그것을 잡았다). */
|
|
67
|
+
payload: { data: { taskId, status: 'completed', resourceRef: 'fk-1' } }
|
|
68
|
+
}
|
|
69
|
+
)
|
|
70
|
+
}
|
|
71
|
+
return rows
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
before(async () => {
|
|
75
|
+
ds = new TypeOrmDataSource({
|
|
76
|
+
type: 'sqlite',
|
|
77
|
+
database: ':memory:',
|
|
78
|
+
synchronize: true,
|
|
79
|
+
dropSchema: true,
|
|
80
|
+
namingStrategy: new NamingStrategy(),
|
|
81
|
+
entities: [...shellEntities, ...authEntities, ...twinEntities],
|
|
82
|
+
logging: false
|
|
83
|
+
})
|
|
84
|
+
await ds.initialize()
|
|
85
|
+
addDataSource('default', ds)
|
|
86
|
+
const d = await ds.getRepository(Domain).save({ name: 'baseline-test', subdomain: 'baseline-test' } as any)
|
|
87
|
+
domainId = d.id
|
|
88
|
+
|
|
89
|
+
await ds.getRepository(TwinInstance).save({
|
|
90
|
+
domain: { id: domainId },
|
|
91
|
+
instanceId: INSTANCE,
|
|
92
|
+
kind: 'wms',
|
|
93
|
+
realityMode: 'sim-experiment', /* 선언은 이제 필수 — 비워 두면 읽는 자리마다 기본값을 고른다 */
|
|
94
|
+
purpose: 'operational',
|
|
95
|
+
status: 'stopped',
|
|
96
|
+
model: { locations: [{ id: 'dock', type: 'dock', capacity: 10 }], equipment: [{ id: 'fk-1', kind: 'forklift', homeLocation: 'dock' }] }
|
|
97
|
+
} as any)
|
|
98
|
+
|
|
99
|
+
/*
|
|
100
|
+
* 지금 창 20건 · 어제 40건 · 그제 60건 · **3일 전은 기록 없음** · 4일 전 80건.
|
|
101
|
+
* 3일 전을 비워 두는 것이 이 스펙의 요점이다 — 그 날이 결측으로 남고 분포에서 빠지는지 본다
|
|
102
|
+
* (조용히 빼면 "지난 4일" 이 실제로는 3일이었다는 것을 아무도 모른다).
|
|
103
|
+
*/
|
|
104
|
+
const rows = [
|
|
105
|
+
...taskRows(0, 20, 30_000, 1000),
|
|
106
|
+
...taskRows(1, 40, 40_000, 3000),
|
|
107
|
+
...taskRows(2, 60, 50_000, 6000),
|
|
108
|
+
...taskRows(4, 80, 60_000, 9000)
|
|
109
|
+
]
|
|
110
|
+
await ds.getRepository(TwinEvent).save(rows)
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
after(async () => {
|
|
114
|
+
await ds?.destroy()
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
const kpi = (periods: number, stride?: 'day' | 'week') =>
|
|
118
|
+
computeTwinKpi({
|
|
119
|
+
domainId,
|
|
120
|
+
instanceId: INSTANCE,
|
|
121
|
+
fromTime: new Date(FROM).toISOString(),
|
|
122
|
+
toTime: new Date(TO).toISOString(),
|
|
123
|
+
baseline: { periods, ...(stride ? { stride } : {}) }
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
test('과거 창을 하루씩 따로 읽어 분포를 만든다', async () => {
|
|
127
|
+
const out: any = await kpi(4)
|
|
128
|
+
const tasks = out.baseline.metrics.find((m: any) => m.metric === 'throughput.tasks')
|
|
129
|
+
|
|
130
|
+
assert.equal(out.baseline.stride, 'day')
|
|
131
|
+
assert.equal(out.baseline.periods, 4)
|
|
132
|
+
assert.equal(tasks.current, 20, '지금 창')
|
|
133
|
+
/* 어제 40 · 그제 60 · 3일 전 없음 · 4일 전 80. */
|
|
134
|
+
assert.deepEqual([tasks.min, tasks.median, tasks.max], [40, 60, 80])
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
test('기록 없는 날을 **결측으로 남긴다** — 요청한 창 수와 측정된 수를 함께 답한다', async () => {
|
|
138
|
+
const out: any = await kpi(4)
|
|
139
|
+
const tasks = out.baseline.metrics.find((m: any) => m.metric === 'throughput.tasks')
|
|
140
|
+
|
|
141
|
+
assert.equal(tasks.requested, 4)
|
|
142
|
+
assert.equal(tasks.measuredCount, 3, '3일 전은 기록이 없다')
|
|
143
|
+
assert.equal(tasks.samples.length, 4, '창 자체는 네 개 모두 돌려준다')
|
|
144
|
+
assert.deepEqual(tasks.samples.map((s: any) => s.measured), [true, true, false, true])
|
|
145
|
+
assert.deepEqual(tasks.samples.map((s: any) => s.value), [40, 60, undefined, 80])
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
test('처리량이 지난 며칠 중 가장 낮으면 worst — 이 축이 답하려던 질문', async () => {
|
|
149
|
+
/* 예전에는 "어제보다 나아졌나" 까지만 답할 수 있었다. 지금 창(20)은 어제(40)보다도 낮고
|
|
150
|
+
최근 어느 날보다도 낮다 — 그것을 한 번에 말할 수 있어야 한다. */
|
|
151
|
+
const out: any = await kpi(4)
|
|
152
|
+
const tasks = out.baseline.metrics.find((m: any) => m.metric === 'throughput.tasks')
|
|
153
|
+
|
|
154
|
+
assert.equal(tasks.standing, 'worst')
|
|
155
|
+
assert.equal(tasks.rank, 1, '오름차순 첫째')
|
|
156
|
+
assert.equal(tasks.rankOf, 4, '표본 3 + 현재 1')
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
test('방향은 지표가 안다 — 작업시간이 가장 짧으면 best', async () => {
|
|
160
|
+
const out: any = await kpi(4)
|
|
161
|
+
const work = out.baseline.metrics.find((m: any) => m.metric === 'workTime.p50Ms')
|
|
162
|
+
|
|
163
|
+
assert.equal(work.current, 30_000)
|
|
164
|
+
assert.deepEqual([work.min, work.max], [40_000, 60_000])
|
|
165
|
+
assert.equal(work.standing, 'best', '짧을수록 좋다')
|
|
166
|
+
})
|
|
167
|
+
|
|
168
|
+
test('구간 수를 줄이면 그만큼만 본다 — 창을 지어내지 않는다', async () => {
|
|
169
|
+
const out: any = await kpi(1)
|
|
170
|
+
const tasks = out.baseline.metrics.find((m: any) => m.metric === 'throughput.tasks')
|
|
171
|
+
|
|
172
|
+
assert.equal(tasks.requested, 1)
|
|
173
|
+
assert.equal(tasks.measuredCount, 1)
|
|
174
|
+
/* 표본 하나는 분포가 아니다. */
|
|
175
|
+
assert.equal(tasks.noPosition, 'too-few-samples')
|
|
176
|
+
assert.equal(tasks.standing, undefined)
|
|
177
|
+
})
|
|
178
|
+
|
|
179
|
+
test('주 단위는 같은 요일을 본다 — 하루 전 기록에 걸리지 않는다', async () => {
|
|
180
|
+
const out: any = await kpi(2, 'week')
|
|
181
|
+
const tasks = out.baseline.metrics.find((m: any) => m.metric === 'throughput.tasks')
|
|
182
|
+
|
|
183
|
+
assert.equal(out.baseline.stride, 'week')
|
|
184
|
+
/* 심어 둔 기록은 최근 나흘뿐이라 지난주 같은 요일에는 아무것도 없다 — 지어내지 않는다. */
|
|
185
|
+
assert.equal(tasks.measuredCount, 0)
|
|
186
|
+
assert.equal(tasks.noPosition, 'too-few-samples')
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
test('요청하지 않으면 분포를 만들지 않는다 — 조회가 공짜가 아니다', async () => {
|
|
190
|
+
const out: any = await computeTwinKpi({
|
|
191
|
+
domainId,
|
|
192
|
+
instanceId: INSTANCE,
|
|
193
|
+
fromTime: new Date(FROM).toISOString(),
|
|
194
|
+
toTime: new Date(TO).toISOString()
|
|
195
|
+
})
|
|
196
|
+
|
|
197
|
+
assert.equal(out.baseline, undefined)
|
|
198
|
+
})
|
|
199
|
+
|
|
200
|
+
test('지금 창에 기록이 없어도 분포는 낸다 — 그때가 가장 알고 싶은 순간이다', async () => {
|
|
201
|
+
/* 심어 둔 기록이 없는 시각을 창으로 잡는다(3일 전). 평소엔 일이 있었는데 지금 없다 = 멈춘 신호. */
|
|
202
|
+
const quietTo = TO - 3 * MS_DAY
|
|
203
|
+
const out: any = await computeTwinKpi({
|
|
204
|
+
domainId,
|
|
205
|
+
instanceId: INSTANCE,
|
|
206
|
+
fromTime: new Date(quietTo - 60 * 60_000).toISOString(),
|
|
207
|
+
toTime: new Date(quietTo).toISOString(),
|
|
208
|
+
baseline: { periods: 2 }
|
|
209
|
+
})
|
|
210
|
+
|
|
211
|
+
assert.equal(out.measured, false, '지금 창에는 측정할 기록이 없다')
|
|
212
|
+
assert.ok(out.baseline, '그래도 분포는 낸다')
|
|
213
|
+
const tasks = out.baseline.metrics.find((m: any) => m.metric === 'throughput.tasks')
|
|
214
|
+
assert.equal(tasks.measuredCount, 1, '4일 전 하루만 걸린다(5일 전은 기록 없음)')
|
|
215
|
+
assert.equal(tasks.samples.find((s: any) => s.measured).value, 80)
|
|
216
|
+
})
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* 여러 구간 동시 비교 — **"어제보다 나아졌다" 를 넘어선다.**
|
|
3
|
+
*
|
|
4
|
+
* 비교 축이 한 구간만 볼 때는 판단이 한 문장에서 멈췄다: "어제보다 나아졌다." 어제도 나빴으면 쓸모가
|
|
5
|
+
* 없다. 최근 며칠의 같은 시간대를 함께 보면 목표 없이도 "지난 7일 중 가장 낮다" 를 말할 수 있다.
|
|
6
|
+
*
|
|
7
|
+
* 이 테스트가 지키는 것은 산수보다 **정직성**이다. 이 도메인에서 값이 없는 것을 0 으로 읽으면 시간
|
|
8
|
+
* 지표는 "낮을수록 좋다" 라서 **일이 없던 구간이 최고 성적으로** 보고된다 — 목표 판정에서 이미 겪은
|
|
9
|
+
* 결함이라 여기서 되풀이하지 않는다.
|
|
10
|
+
*/
|
|
11
|
+
import { test } from 'node:test'
|
|
12
|
+
import assert from 'node:assert/strict'
|
|
13
|
+
|
|
14
|
+
import {
|
|
15
|
+
MAX_BASELINE_PERIODS,
|
|
16
|
+
baselineWindows,
|
|
17
|
+
summarizeBaseline,
|
|
18
|
+
type MetricSpecLike
|
|
19
|
+
} from '../server/engine/kpi-baseline.ts'
|
|
20
|
+
|
|
21
|
+
const MS_DAY = 24 * 60 * 60_000
|
|
22
|
+
const HOUR = 60 * 60_000
|
|
23
|
+
|
|
24
|
+
/* 실제 표(`TWIN_METRIC`)의 성질만 흉내 낸 최소 사양 — 어휘는 그쪽이 소유한다. */
|
|
25
|
+
const SPECS = {
|
|
26
|
+
'throughput.tasks': { direction: 'higher', unit: 'count' },
|
|
27
|
+
'workTime.p50Ms': { direction: 'lower', unit: 'ms' }
|
|
28
|
+
} satisfies Record<string, MetricSpecLike>
|
|
29
|
+
|
|
30
|
+
type M = keyof typeof SPECS
|
|
31
|
+
|
|
32
|
+
/** 과거 창 표본 — 값만 신경 쓰는 테스트를 짧게 쓰기 위한 도우미. */
|
|
33
|
+
const sample = (dayBack: number, values: Partial<Record<M, number | undefined>>) => ({
|
|
34
|
+
fromMs: -dayBack * MS_DAY,
|
|
35
|
+
toMs: -dayBack * MS_DAY + HOUR,
|
|
36
|
+
values
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
test('같은 시간대를 하루씩 뒤로 옮긴다 — 가까운 것부터', () => {
|
|
40
|
+
const from = 10 * MS_DAY
|
|
41
|
+
const to = from + HOUR
|
|
42
|
+
|
|
43
|
+
const windows = baselineWindows(from, to, 3)
|
|
44
|
+
|
|
45
|
+
assert.equal(windows.length, 3)
|
|
46
|
+
assert.deepEqual(windows[0], { fromMs: from - MS_DAY, toMs: to - MS_DAY }, '어제가 먼저')
|
|
47
|
+
assert.deepEqual(windows[2], { fromMs: from - 3 * MS_DAY, toMs: to - 3 * MS_DAY })
|
|
48
|
+
/* 길이는 그대로여야 한다 — 길이가 달라지면 처리량을 비교할 수 없다. */
|
|
49
|
+
for (const w of windows) assert.equal(w.toMs - w.fromMs, to - from)
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
test('주 단위는 같은 요일을 본다 — 주말·평일이 섞이지 않게', () => {
|
|
53
|
+
const from = 30 * MS_DAY
|
|
54
|
+
const windows = baselineWindows(from, from + HOUR, 2, 'week')
|
|
55
|
+
|
|
56
|
+
assert.equal(windows[0].fromMs, from - 7 * MS_DAY)
|
|
57
|
+
assert.equal(windows[1].fromMs, from - 14 * MS_DAY)
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
test('구간 수에 상한이 있고, 이상한 입력은 창을 만들지 않는다', () => {
|
|
61
|
+
assert.equal(baselineWindows(0, HOUR, 999).length, MAX_BASELINE_PERIODS)
|
|
62
|
+
assert.equal(baselineWindows(0, HOUR, 0).length, 0)
|
|
63
|
+
assert.equal(baselineWindows(0, HOUR, -3).length, 0)
|
|
64
|
+
/* 끝이 시작보다 앞이면 창이 아니다 — 지어내지 않는다. */
|
|
65
|
+
assert.equal(baselineWindows(HOUR, 0, 3).length, 0)
|
|
66
|
+
assert.equal(baselineWindows(Number.NaN, HOUR, 3).length, 0)
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
test('분포는 측정된 표본만으로 만들되, **결측 창도 남긴다**', () => {
|
|
70
|
+
/* 빼고 말하면 "지난 5일" 이 실제로는 3일이었다는 것을 아무도 모른다. */
|
|
71
|
+
const [row] = summarizeBaseline({ 'throughput.tasks': SPECS['throughput.tasks'] }, { 'throughput.tasks': 50 }, [
|
|
72
|
+
sample(1, { 'throughput.tasks': 40 }),
|
|
73
|
+
sample(2, {}), // 기록 없음
|
|
74
|
+
sample(3, { 'throughput.tasks': 60 }),
|
|
75
|
+
sample(4, { 'throughput.tasks': undefined }), // 기록 없음
|
|
76
|
+
sample(5, { 'throughput.tasks': 80 })
|
|
77
|
+
])
|
|
78
|
+
|
|
79
|
+
assert.equal(row.requested, 5)
|
|
80
|
+
assert.equal(row.measuredCount, 3)
|
|
81
|
+
assert.equal(row.samples.length, 5, '요청한 창 전부를 돌려준다')
|
|
82
|
+
assert.deepEqual(
|
|
83
|
+
row.samples.map(s => s.measured),
|
|
84
|
+
[true, false, true, false, true]
|
|
85
|
+
)
|
|
86
|
+
assert.deepEqual([row.min, row.median, row.max], [40, 60, 80])
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
test('현재 값이 없으면 순위를 내지 않는다 — 결측을 0 으로 읽으면 최고 성적으로 뒤집힌다', () => {
|
|
90
|
+
/* 시간 지표는 "낮을수록 좋다" 라서 결측을 0 으로 놓으면 **일이 없던 구간이 1등**이 된다. */
|
|
91
|
+
const [row] = summarizeBaseline({ 'workTime.p50Ms': SPECS['workTime.p50Ms'] }, {}, [
|
|
92
|
+
sample(1, { 'workTime.p50Ms': 30_000 }),
|
|
93
|
+
sample(2, { 'workTime.p50Ms': 40_000 })
|
|
94
|
+
])
|
|
95
|
+
|
|
96
|
+
assert.equal(row.current, undefined)
|
|
97
|
+
assert.equal(row.standing, undefined)
|
|
98
|
+
assert.equal(row.rank, undefined)
|
|
99
|
+
assert.equal(row.noPosition, 'no-current')
|
|
100
|
+
/* 그래도 분포 자체는 낸다 — "평소 얼마였나" 는 여전히 답할 수 있는 사실이다. */
|
|
101
|
+
assert.deepEqual([row.min, row.max], [30_000, 40_000])
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
test('표본이 둘 미만이면 순위를 내지 않는다 — 하나를 분포라 부를 수 없다', () => {
|
|
105
|
+
const one = summarizeBaseline({ 'throughput.tasks': SPECS['throughput.tasks'] }, { 'throughput.tasks': 10 }, [
|
|
106
|
+
sample(1, { 'throughput.tasks': 40 })
|
|
107
|
+
])[0]
|
|
108
|
+
|
|
109
|
+
assert.equal(one.noPosition, 'too-few-samples')
|
|
110
|
+
assert.equal(one.standing, undefined)
|
|
111
|
+
|
|
112
|
+
const none = summarizeBaseline({ 'throughput.tasks': SPECS['throughput.tasks'] }, { 'throughput.tasks': 10 }, [])[0]
|
|
113
|
+
assert.equal(none.noPosition, 'too-few-samples')
|
|
114
|
+
assert.equal(none.min, undefined)
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
test('방향을 지표가 안다 — 같은 자리가 처리량과 소요시간에서 반대로 읽힌다', () => {
|
|
118
|
+
const past = [sample(1, { 'throughput.tasks': 40, 'workTime.p50Ms': 40_000 }), sample(2, { 'throughput.tasks': 60, 'workTime.p50Ms': 60_000 }), sample(3, { 'throughput.tasks': 80, 'workTime.p50Ms': 80_000 })]
|
|
119
|
+
|
|
120
|
+
/* 값이 표본 전부보다 크다 — 처리량은 최고, 소요시간은 최악이다. */
|
|
121
|
+
const rows = summarizeBaseline(SPECS, { 'throughput.tasks': 90, 'workTime.p50Ms': 90_000 }, past)
|
|
122
|
+
const by = Object.fromEntries(rows.map(r => [r.metric, r]))
|
|
123
|
+
|
|
124
|
+
assert.equal(by['throughput.tasks'].standing, 'best')
|
|
125
|
+
assert.equal(by['workTime.p50Ms'].standing, 'worst')
|
|
126
|
+
/* 순위는 **방향 없는 사실**이다 — 둘 다 오름차순 4번째다. */
|
|
127
|
+
assert.equal(by['throughput.tasks'].rank, 4)
|
|
128
|
+
assert.equal(by['workTime.p50Ms'].rank, 4)
|
|
129
|
+
assert.equal(by['workTime.p50Ms'].rankOf, 4)
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
test('전부보다 나쁘면 worst — 처리량이 지난 3일 중 가장 낮은 경우', () => {
|
|
133
|
+
const [row] = summarizeBaseline({ 'throughput.tasks': SPECS['throughput.tasks'] }, { 'throughput.tasks': 10 }, [
|
|
134
|
+
sample(1, { 'throughput.tasks': 40 }),
|
|
135
|
+
sample(2, { 'throughput.tasks': 60 }),
|
|
136
|
+
sample(3, { 'throughput.tasks': 80 })
|
|
137
|
+
])
|
|
138
|
+
|
|
139
|
+
assert.equal(row.standing, 'worst')
|
|
140
|
+
assert.equal(row.rank, 1, '오름차순 첫째')
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
test('중앙값과 같으면 typical — 임의의 임계를 만들지 않는다', () => {
|
|
144
|
+
const [row] = summarizeBaseline({ 'throughput.tasks': SPECS['throughput.tasks'] }, { 'throughput.tasks': 60 }, [
|
|
145
|
+
sample(1, { 'throughput.tasks': 40 }),
|
|
146
|
+
sample(2, { 'throughput.tasks': 60 }),
|
|
147
|
+
sample(3, { 'throughput.tasks': 80 })
|
|
148
|
+
])
|
|
149
|
+
|
|
150
|
+
assert.equal(row.standing, 'typical')
|
|
151
|
+
})
|
|
152
|
+
|
|
153
|
+
test('중앙값 양쪽을 방향에 따라 구분한다', () => {
|
|
154
|
+
const past = [sample(1, { 'throughput.tasks': 40 }), sample(2, { 'throughput.tasks': 60 }), sample(3, { 'throughput.tasks': 80 })]
|
|
155
|
+
const spec = { 'throughput.tasks': SPECS['throughput.tasks'] }
|
|
156
|
+
|
|
157
|
+
assert.equal(summarizeBaseline(spec, { 'throughput.tasks': 70 }, past)[0].standing, 'better-than-typical')
|
|
158
|
+
assert.equal(summarizeBaseline(spec, { 'throughput.tasks': 50 }, past)[0].standing, 'worse-than-typical')
|
|
159
|
+
|
|
160
|
+
/* 소요시간은 반대다 — 중앙값보다 낮은 것이 좋다. */
|
|
161
|
+
const lower = { 'workTime.p50Ms': SPECS['workTime.p50Ms'] }
|
|
162
|
+
const pastMs = [sample(1, { 'workTime.p50Ms': 40 }), sample(2, { 'workTime.p50Ms': 60 }), sample(3, { 'workTime.p50Ms': 80 })]
|
|
163
|
+
assert.equal(summarizeBaseline(lower, { 'workTime.p50Ms': 50 }, pastMs)[0].standing, 'better-than-typical')
|
|
164
|
+
assert.equal(summarizeBaseline(lower, { 'workTime.p50Ms': 70 }, pastMs)[0].standing, 'worse-than-typical')
|
|
165
|
+
})
|
|
166
|
+
|
|
167
|
+
test('짝수 표본의 중앙값은 가운데 둘의 평균이다', () => {
|
|
168
|
+
const [row] = summarizeBaseline({ 'throughput.tasks': SPECS['throughput.tasks'] }, { 'throughput.tasks': 55 }, [
|
|
169
|
+
sample(1, { 'throughput.tasks': 40 }),
|
|
170
|
+
sample(2, { 'throughput.tasks': 50 }),
|
|
171
|
+
sample(3, { 'throughput.tasks': 60 }),
|
|
172
|
+
sample(4, { 'throughput.tasks': 70 })
|
|
173
|
+
])
|
|
174
|
+
|
|
175
|
+
assert.equal(row.median, 55)
|
|
176
|
+
assert.equal(row.standing, 'typical')
|
|
177
|
+
})
|
|
178
|
+
|
|
179
|
+
test('열거된 지표만 낸다 — 모르는 이름을 짐작하지 않는다', () => {
|
|
180
|
+
const rows = summarizeBaseline({ 'throughput.tasks': SPECS['throughput.tasks'] }, { 'throughput.tasks': 10, 'made.up': 99 } as any, [
|
|
181
|
+
sample(1, { 'throughput.tasks': 5 } as any)
|
|
182
|
+
])
|
|
183
|
+
|
|
184
|
+
assert.deepEqual(rows.map(r => r.metric), ['throughput.tasks'])
|
|
185
|
+
})
|
|
186
|
+
|
|
187
|
+
test('무한·NaN 은 측정으로 세지 않는다', () => {
|
|
188
|
+
const [row] = summarizeBaseline({ 'throughput.tasks': SPECS['throughput.tasks'] }, { 'throughput.tasks': Number.NaN }, [
|
|
189
|
+
sample(1, { 'throughput.tasks': Number.POSITIVE_INFINITY }),
|
|
190
|
+
sample(2, { 'throughput.tasks': 40 }),
|
|
191
|
+
sample(3, { 'throughput.tasks': 60 })
|
|
192
|
+
])
|
|
193
|
+
|
|
194
|
+
assert.equal(row.measuredCount, 2, '무한은 표본이 아니다')
|
|
195
|
+
assert.equal(row.noPosition, 'no-current', 'NaN 은 값이 아니다')
|
|
196
|
+
})
|