@things-factory/headless-twin 10.1.277 → 10.1.279
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/package.json +21 -7
- package/dist-server/engine/attention-digest.js.map +0 -1
- package/dist-server/engine/canonical-ingest.js.map +0 -1
- package/dist-server/engine/checkpoint-fold.js.map +0 -1
- package/dist-server/engine/command-routing.js.map +0 -1
- package/dist-server/engine/declared-stimulus.js.map +0 -1
- package/dist-server/engine/energy-topology.js.map +0 -1
- package/dist-server/engine/event-time.js.map +0 -1
- package/dist-server/engine/index.js.map +0 -1
- package/dist-server/engine/ingest-dedupe.js.map +0 -1
- package/dist-server/engine/ingest-health.js.map +0 -1
- package/dist-server/engine/integration-coverage.js.map +0 -1
- package/dist-server/engine/integration-probes.js.map +0 -1
- package/dist-server/engine/integration-runner.js.map +0 -1
- package/dist-server/engine/integration-target-profile.js.map +0 -1
- package/dist-server/engine/kpi-baseline.js.map +0 -1
- package/dist-server/engine/kpi-fold.js.map +0 -1
- package/dist-server/engine/kpi-query.js.map +0 -1
- package/dist-server/engine/kpi-target.js.map +0 -1
- package/dist-server/engine/live-attentions.js.map +0 -1
- package/dist-server/engine/live-feed-registry.js.map +0 -1
- package/dist-server/engine/load-meter.js.map +0 -1
- package/dist-server/engine/local-declarations.js.map +0 -1
- package/dist-server/engine/log.js.map +0 -1
- package/dist-server/engine/loop-lag.js.map +0 -1
- package/dist-server/engine/measured-estimator.js.map +0 -1
- package/dist-server/engine/measured-yield.js.map +0 -1
- package/dist-server/engine/model-basis.js.map +0 -1
- package/dist-server/engine/model-gap.js.map +0 -1
- package/dist-server/engine/model-refusal.js.map +0 -1
- package/dist-server/engine/model-vocabulary.js.map +0 -1
- package/dist-server/engine/oee-accumulator.js.map +0 -1
- package/dist-server/engine/operation-basis.js.map +0 -1
- package/dist-server/engine/producer-guard.js.map +0 -1
- package/dist-server/engine/property-effects.js.map +0 -1
- package/dist-server/engine/read-time.js.map +0 -1
- package/dist-server/engine/restart-policy.js.map +0 -1
- package/dist-server/engine/runtime-key.js.map +0 -1
- package/dist-server/engine/spec-coverage.js.map +0 -1
- package/dist-server/engine/stage-path.js.map +0 -1
- package/dist-server/engine/state-axes.js.map +0 -1
- package/dist-server/engine/structure-diff.js.map +0 -1
- package/dist-server/engine/travel-estimator.js.map +0 -1
- package/dist-server/engine/twin-engine.js.map +0 -1
- package/dist-server/engine/twin-state-channel.js.map +0 -1
- package/dist-server/engine/warm-start.js.map +0 -1
- package/dist-server/index.js.map +0 -1
- package/dist-server/migrations/1786000000000-RenameTwinInstanceBoardToModel.js.map +0 -1
- package/dist-server/migrations/1786100000000-PromoteEventActionAndSyncWarnings.js.map +0 -1
- package/dist-server/migrations/1786200000000-CarryLiveFeedCursor.js.map +0 -1
- package/dist-server/migrations/1786300000000-IndexEventTypeByTime.js.map +0 -1
- package/dist-server/migrations/1786400000000-IndexEventCreatedAt.js.map +0 -1
- package/dist-server/migrations/1786500000000-CreateTwinSubjectEvents.js.map +0 -1
- package/dist-server/migrations/1786600000000-RenameOperatoMesAdapterType.js.map +0 -1
- package/dist-server/migrations/1786700000000-AddDispatchRetryColumns.js.map +0 -1
- package/dist-server/migrations/1786800000000-CreateTwinReferenceReconciliations.js.map +0 -1
- package/dist-server/migrations/index.js.map +0 -1
- package/dist-server/routes.js.map +0 -1
- package/dist-server/service/actuation/actuation-advisor.js.map +0 -1
- package/dist-server/service/actuation/actuation-outcome.js.map +0 -1
- package/dist-server/service/actuation/actuation-rule-resolver.js.map +0 -1
- package/dist-server/service/actuation/actuation-rule.js.map +0 -1
- package/dist-server/service/actuation/approval-gateway.js.map +0 -1
- package/dist-server/service/actuation/catch-up-effects.js.map +0 -1
- package/dist-server/service/actuation/command-dispatcher.js.map +0 -1
- package/dist-server/service/actuation/command-store.js.map +0 -1
- package/dist-server/service/actuation/dispatch-after-approval.js.map +0 -1
- package/dist-server/service/actuation/effect-match.js.map +0 -1
- package/dist-server/service/actuation/index.js.map +0 -1
- package/dist-server/service/actuation/retry-plan.js.map +0 -1
- package/dist-server/service/actuation/rule-evaluate.js.map +0 -1
- package/dist-server/service/actuation/rule-loop.js.map +0 -1
- package/dist-server/service/actuation/rule-runner.js.map +0 -1
- package/dist-server/service/actuation/rule-wiring.js.map +0 -1
- package/dist-server/service/actuation/settle-effects.js.map +0 -1
- package/dist-server/service/actuation/sweep-dispatch.js.map +0 -1
- package/dist-server/service/actuation/twin-command-resolver.js.map +0 -1
- package/dist-server/service/actuation/twin-command.js.map +0 -1
- package/dist-server/service/actuation/twin-now.js.map +0 -1
- package/dist-server/service/index.js.map +0 -1
- package/dist-server/service/reference/actuation-routing.js.map +0 -1
- package/dist-server/service/reference/actuation-target.js.map +0 -1
- package/dist-server/service/reference/actuation-verification.js.map +0 -1
- package/dist-server/service/reference/attention-lane.js.map +0 -1
- package/dist-server/service/reference/connection-portability-resolver.js.map +0 -1
- package/dist-server/service/reference/connection-portability.js.map +0 -1
- package/dist-server/service/reference/control-routing.js.map +0 -1
- package/dist-server/service/reference/discovery-result.js.map +0 -1
- package/dist-server/service/reference/fill-cadence.js.map +0 -1
- package/dist-server/service/reference/fill-dedupe.js.map +0 -1
- package/dist-server/service/reference/fill-loop.js.map +0 -1
- package/dist-server/service/reference/fill-span.js.map +0 -1
- package/dist-server/service/reference/hook-contract.js.map +0 -1
- package/dist-server/service/reference/hook-rejected.js.map +0 -1
- package/dist-server/service/reference/hook-store.js.map +0 -1
- package/dist-server/service/reference/index.js.map +0 -1
- package/dist-server/service/reference/ingest-space.js.map +0 -1
- package/dist-server/service/reference/knob-defaults.js.map +0 -1
- package/dist-server/service/reference/live-cadence.js.map +0 -1
- package/dist-server/service/reference/live-feed-lease.js.map +0 -1
- package/dist-server/service/reference/live-ingest.js.map +0 -1
- package/dist-server/service/reference/reference-adapter.js.map +0 -1
- package/dist-server/service/reference/reference-assessment.js.map +0 -1
- package/dist-server/service/reference/reference-fill-run.js.map +0 -1
- package/dist-server/service/reference/reference-fill.js.map +0 -1
- package/dist-server/service/reference/reference-hook.js.map +0 -1
- package/dist-server/service/reference/reference-link-groups.js.map +0 -1
- package/dist-server/service/reference/reference-links.js.map +0 -1
- package/dist-server/service/reference/reference-live.js.map +0 -1
- package/dist-server/service/reference/reference-master.js.map +0 -1
- package/dist-server/service/reference/reference-probe.js.map +0 -1
- package/dist-server/service/reference/reference-progress-channel.js.map +0 -1
- package/dist-server/service/reference/reference-progress-subscription.js.map +0 -1
- package/dist-server/service/reference/reference-progress.js.map +0 -1
- package/dist-server/service/reference/reference-reconciliation-query.js.map +0 -1
- package/dist-server/service/reference/reference-reconciliation-writer.js.map +0 -1
- package/dist-server/service/reference/reference-reconciliation.js.map +0 -1
- package/dist-server/service/reference/reference-registry.js.map +0 -1
- package/dist-server/service/reference/reference-resolver.js.map +0 -1
- package/dist-server/service/reference/template-registry.js.map +0 -1
- package/dist-server/service/reference/twin-reference-reconciliation.js.map +0 -1
- package/dist-server/service/reference/twin-reference.js.map +0 -1
- package/dist-server/service/twin-attention/index.js.map +0 -1
- package/dist-server/service/twin-attention/twin-attention-query.js.map +0 -1
- package/dist-server/service/twin-audit/command-audit.js.map +0 -1
- package/dist-server/service/twin-audit/index.js.map +0 -1
- package/dist-server/service/twin-audit/twin-audit-event.js.map +0 -1
- package/dist-server/service/twin-audit/twin-audit-query.js.map +0 -1
- package/dist-server/service/twin-backfill/backfill-period-facts.js.map +0 -1
- package/dist-server/service/twin-backfill/backfill-shape.js.map +0 -1
- package/dist-server/service/twin-backfill/index.js.map +0 -1
- package/dist-server/service/twin-backfill/twin-backfill-resolver.js.map +0 -1
- package/dist-server/service/twin-control/index.js.map +0 -1
- package/dist-server/service/twin-control/twin-control-mutation.js.map +0 -1
- package/dist-server/service/twin-event/index.js.map +0 -1
- package/dist-server/service/twin-event/journal-count.js.map +0 -1
- package/dist-server/service/twin-event/twin-event-keys.js.map +0 -1
- package/dist-server/service/twin-event/twin-event-type.js.map +0 -1
- package/dist-server/service/twin-event/twin-event.js.map +0 -1
- package/dist-server/service/twin-forecast/forecast-metrics.js.map +0 -1
- package/dist-server/service/twin-forecast/forecast-tuning.js.map +0 -1
- package/dist-server/service/twin-forecast/gap-analytics.js.map +0 -1
- package/dist-server/service/twin-forecast/index.js.map +0 -1
- package/dist-server/service/twin-forecast/twin-forecast-query.js.map +0 -1
- package/dist-server/service/twin-ingest-window/index.js.map +0 -1
- package/dist-server/service/twin-ingest-window/twin-ingest-window-query.js.map +0 -1
- package/dist-server/service/twin-ingest-window/twin-ingest-window-writer.js.map +0 -1
- package/dist-server/service/twin-ingest-window/twin-ingest-window.js.map +0 -1
- package/dist-server/service/twin-instance/index.js.map +0 -1
- package/dist-server/service/twin-instance/twin-instance.js.map +0 -1
- package/dist-server/service/twin-journal/event-type-split-shape.js.map +0 -1
- package/dist-server/service/twin-journal/event-type-split.js.map +0 -1
- package/dist-server/service/twin-journal/index.js.map +0 -1
- package/dist-server/service/twin-journal/journal-order.js.map +0 -1
- package/dist-server/service/twin-journal/twin-journal-query.js.map +0 -1
- package/dist-server/service/twin-lifecycle/domain-catalog.js.map +0 -1
- package/dist-server/service/twin-lifecycle/index.js.map +0 -1
- package/dist-server/service/twin-lifecycle/twin-lifecycle-mutation.js.map +0 -1
- package/dist-server/service/twin-metrics/index.js.map +0 -1
- package/dist-server/service/twin-metrics/twin-metrics-query.js.map +0 -1
- package/dist-server/service/twin-model/axis-journal-evidence.js.map +0 -1
- package/dist-server/service/twin-model/choose-recipe.js.map +0 -1
- package/dist-server/service/twin-model/epcis-coverage.js.map +0 -1
- package/dist-server/service/twin-model/external-resolver.js.map +0 -1
- package/dist-server/service/twin-model/iec61850-coverage.js.map +0 -1
- package/dist-server/service/twin-model/index.js.map +0 -1
- package/dist-server/service/twin-model/isa95-coverage.js.map +0 -1
- package/dist-server/service/twin-model/item-ref.js.map +0 -1
- package/dist-server/service/twin-model/name-index.js.map +0 -1
- package/dist-server/service/twin-model/project-structure.js.map +0 -1
- package/dist-server/service/twin-model/standard-coverage.js.map +0 -1
- package/dist-server/service/twin-model/status-tally.js.map +0 -1
- package/dist-server/service/twin-model/twin-equipment.js.map +0 -1
- package/dist-server/service/twin-model/twin-lineage-query.js.map +0 -1
- package/dist-server/service/twin-model/twin-location.js.map +0 -1
- package/dist-server/service/twin-model/twin-model-item-query.js.map +0 -1
- package/dist-server/service/twin-model/twin-model-mutation.js.map +0 -1
- package/dist-server/service/twin-model/twin-model-query.js.map +0 -1
- package/dist-server/service/twin-model/twin-model-tree-query.js.map +0 -1
- package/dist-server/service/twin-model/twin-operation.js.map +0 -1
- package/dist-server/service/twin-readiness/index.js.map +0 -1
- package/dist-server/service/twin-readiness/twin-readiness-query.js.map +0 -1
- package/dist-server/service/twin-space/area-resolver.js.map +0 -1
- package/dist-server/service/twin-space/index.js.map +0 -1
- package/dist-server/service/twin-space/move-space.js.map +0 -1
- package/dist-server/service/twin-space/representation-role.js.map +0 -1
- package/dist-server/service/twin-space/space-integrity.js.map +0 -1
- package/dist-server/service/twin-space/twin-area.js.map +0 -1
- package/dist-server/service/twin-space/twin-space-area.js.map +0 -1
- package/dist-server/service/twin-space/twin-space-representation.js.map +0 -1
- package/dist-server/service/twin-space/twin-space-resolver.js.map +0 -1
- package/dist-server/service/twin-space/twin-space.js.map +0 -1
- package/dist-server/service/twin-state/index.js.map +0 -1
- package/dist-server/service/twin-state/twin-state-subscription.js.map +0 -1
- package/dist-server/service/twin-structure/index.js.map +0 -1
- package/dist-server/service/twin-structure/twin-structure.js.map +0 -1
- package/dist-server/service/twin-subject/event-subjects.js.map +0 -1
- package/dist-server/service/twin-subject/index.js.map +0 -1
- package/dist-server/service/twin-subject/item-resolver.js.map +0 -1
- package/dist-server/service/twin-subject/subject-query-shape.js.map +0 -1
- package/dist-server/service/twin-subject/twin-subject-backfill.js.map +0 -1
- package/dist-server/service/twin-subject/twin-subject-event.js.map +0 -1
- package/dist-server/service/twin-subject/twin-subject-fast-path.js.map +0 -1
- package/dist-server/service/twin-subject/twin-subject-query.js.map +0 -1
- package/dist-server/service/twin-subject/twin-subject-writer.js.map +0 -1
- package/dist-server/service/twin-target/index.js.map +0 -1
- package/dist-server/service/twin-target/twin-target-resolver.js.map +0 -1
- package/dist-server/service/twin-target/twin-target.js.map +0 -1
- package/dist-shared/axis-read.js.map +0 -1
- package/dist-shared/entity-delta.js.map +0 -1
- package/dist-shared/kpi-broadcast.js.map +0 -1
- package/dist-shared/touched-items.js.map +0 -1
- package/dist-shared/twin-level.js.map +0 -1
- package/server/engine/attention-digest.ts +0 -102
- package/server/engine/canonical-ingest.ts +0 -511
- package/server/engine/checkpoint-fold.ts +0 -12
- package/server/engine/command-routing.ts +0 -67
- package/server/engine/declared-stimulus.ts +0 -66
- package/server/engine/energy-topology.ts +0 -189
- package/server/engine/event-time.ts +0 -96
- package/server/engine/index.ts +0 -51
- package/server/engine/ingest-dedupe.ts +0 -24
- package/server/engine/ingest-health.ts +0 -24
- package/server/engine/integration-coverage.ts +0 -147
- package/server/engine/integration-probes.ts +0 -144
- package/server/engine/integration-runner.ts +0 -95
- package/server/engine/integration-target-profile.ts +0 -103
- package/server/engine/kpi-baseline.ts +0 -202
- package/server/engine/kpi-fold.ts +0 -1516
- package/server/engine/kpi-query.ts +0 -1210
- package/server/engine/kpi-target.ts +0 -248
- package/server/engine/live-attentions.ts +0 -30
- package/server/engine/live-feed-registry.ts +0 -59
- package/server/engine/load-meter.ts +0 -393
- package/server/engine/local-declarations.ts +0 -779
- package/server/engine/log.ts +0 -72
- package/server/engine/loop-lag.ts +0 -120
- package/server/engine/measured-estimator.ts +0 -91
- package/server/engine/measured-yield.ts +0 -89
- package/server/engine/model-basis.ts +0 -162
- package/server/engine/model-gap.ts +0 -168
- package/server/engine/model-refusal.ts +0 -31
- package/server/engine/model-vocabulary.ts +0 -82
- package/server/engine/oee-accumulator.ts +0 -195
- package/server/engine/operation-basis.ts +0 -131
- package/server/engine/producer-guard.ts +0 -64
- package/server/engine/property-effects.ts +0 -216
- package/server/engine/read-time.ts +0 -77
- package/server/engine/restart-policy.ts +0 -55
- package/server/engine/runtime-key.ts +0 -58
- package/server/engine/spec-coverage.ts +0 -105
- package/server/engine/stage-path.ts +0 -172
- package/server/engine/state-axes.ts +0 -60
- package/server/engine/structure-diff.ts +0 -88
- package/server/engine/travel-estimator.ts +0 -138
- package/server/engine/twin-engine.ts +0 -4979
- package/server/engine/twin-state-channel.ts +0 -39
- package/server/engine/warm-start.ts +0 -265
- package/server/index.ts +0 -90
- package/server/migrations/1786000000000-RenameTwinInstanceBoardToModel.ts +0 -43
- package/server/migrations/1786100000000-PromoteEventActionAndSyncWarnings.ts +0 -54
- package/server/migrations/1786200000000-CarryLiveFeedCursor.ts +0 -53
- package/server/migrations/1786300000000-IndexEventTypeByTime.ts +0 -48
- package/server/migrations/1786400000000-IndexEventCreatedAt.ts +0 -38
- package/server/migrations/1786500000000-CreateTwinSubjectEvents.ts +0 -93
- package/server/migrations/1786600000000-RenameOperatoMesAdapterType.ts +0 -66
- package/server/migrations/1786700000000-AddDispatchRetryColumns.ts +0 -82
- package/server/migrations/1786800000000-CreateTwinReferenceReconciliations.ts +0 -39
- package/server/migrations/index.ts +0 -25
- package/server/routes.ts +0 -75
- package/server/service/actuation/actuation-advisor.ts +0 -131
- package/server/service/actuation/actuation-outcome.ts +0 -97
- package/server/service/actuation/actuation-rule-resolver.ts +0 -147
- package/server/service/actuation/actuation-rule.ts +0 -139
- package/server/service/actuation/approval-gateway.ts +0 -44
- package/server/service/actuation/catch-up-effects.ts +0 -152
- package/server/service/actuation/command-dispatcher.ts +0 -474
- package/server/service/actuation/command-store.ts +0 -236
- package/server/service/actuation/dispatch-after-approval.ts +0 -71
- package/server/service/actuation/effect-match.ts +0 -124
- package/server/service/actuation/index.ts +0 -27
- package/server/service/actuation/retry-plan.ts +0 -145
- package/server/service/actuation/rule-evaluate.ts +0 -352
- package/server/service/actuation/rule-loop.ts +0 -160
- package/server/service/actuation/rule-runner.ts +0 -216
- package/server/service/actuation/rule-wiring.ts +0 -84
- package/server/service/actuation/settle-effects.ts +0 -115
- package/server/service/actuation/sweep-dispatch.ts +0 -179
- package/server/service/actuation/twin-command-resolver.ts +0 -387
- package/server/service/actuation/twin-command.ts +0 -232
- package/server/service/actuation/twin-now.ts +0 -97
- package/server/service/index.ts +0 -100
- package/server/service/reference/actuation-routing.ts +0 -192
- package/server/service/reference/actuation-target.ts +0 -163
- package/server/service/reference/actuation-verification.ts +0 -85
- package/server/service/reference/attention-lane.ts +0 -21
- package/server/service/reference/connection-portability-resolver.ts +0 -171
- package/server/service/reference/connection-portability.ts +0 -329
- package/server/service/reference/control-routing.ts +0 -62
- package/server/service/reference/discovery-result.ts +0 -95
- package/server/service/reference/fill-cadence.ts +0 -29
- package/server/service/reference/fill-dedupe.ts +0 -66
- package/server/service/reference/fill-loop.ts +0 -153
- package/server/service/reference/fill-span.ts +0 -31
- package/server/service/reference/hook-contract.ts +0 -364
- package/server/service/reference/hook-rejected.ts +0 -53
- package/server/service/reference/hook-store.ts +0 -28
- package/server/service/reference/index.ts +0 -18
- package/server/service/reference/ingest-space.ts +0 -104
- package/server/service/reference/knob-defaults.ts +0 -59
- package/server/service/reference/live-cadence.ts +0 -129
- package/server/service/reference/live-feed-lease.ts +0 -56
- package/server/service/reference/live-ingest.ts +0 -94
- package/server/service/reference/reference-adapter.ts +0 -985
- package/server/service/reference/reference-assessment.ts +0 -250
- package/server/service/reference/reference-fill-run.ts +0 -179
- package/server/service/reference/reference-fill.ts +0 -223
- package/server/service/reference/reference-hook.ts +0 -479
- package/server/service/reference/reference-link-groups.ts +0 -21
- package/server/service/reference/reference-links.ts +0 -77
- package/server/service/reference/reference-live.ts +0 -420
- package/server/service/reference/reference-master.ts +0 -999
- package/server/service/reference/reference-probe.ts +0 -264
- package/server/service/reference/reference-progress-channel.ts +0 -26
- package/server/service/reference/reference-progress-subscription.ts +0 -78
- package/server/service/reference/reference-progress.ts +0 -95
- package/server/service/reference/reference-reconciliation-query.ts +0 -28
- package/server/service/reference/reference-reconciliation-writer.ts +0 -38
- package/server/service/reference/reference-reconciliation.ts +0 -55
- package/server/service/reference/reference-registry.ts +0 -17
- package/server/service/reference/reference-resolver.ts +0 -969
- package/server/service/reference/template-registry.ts +0 -85
- package/server/service/reference/twin-reference-reconciliation.ts +0 -27
- package/server/service/reference/twin-reference.ts +0 -190
- package/server/service/twin-attention/index.ts +0 -3
- package/server/service/twin-attention/twin-attention-query.ts +0 -70
- package/server/service/twin-audit/command-audit.ts +0 -114
- package/server/service/twin-audit/index.ts +0 -5
- package/server/service/twin-audit/twin-audit-event.ts +0 -158
- package/server/service/twin-audit/twin-audit-query.ts +0 -72
- package/server/service/twin-backfill/backfill-period-facts.ts +0 -168
- package/server/service/twin-backfill/backfill-shape.ts +0 -86
- package/server/service/twin-backfill/index.ts +0 -3
- package/server/service/twin-backfill/twin-backfill-resolver.ts +0 -35
- package/server/service/twin-control/index.ts +0 -3
- package/server/service/twin-control/twin-control-mutation.ts +0 -150
- package/server/service/twin-event/index.ts +0 -3
- package/server/service/twin-event/journal-count.ts +0 -53
- package/server/service/twin-event/twin-event-keys.ts +0 -215
- package/server/service/twin-event/twin-event-type.ts +0 -68
- package/server/service/twin-event/twin-event.ts +0 -250
- package/server/service/twin-forecast/forecast-metrics.ts +0 -106
- package/server/service/twin-forecast/forecast-tuning.ts +0 -40
- package/server/service/twin-forecast/gap-analytics.ts +0 -139
- package/server/service/twin-forecast/index.ts +0 -3
- package/server/service/twin-forecast/twin-forecast-query.ts +0 -486
- package/server/service/twin-ingest-window/index.ts +0 -7
- package/server/service/twin-ingest-window/twin-ingest-window-query.ts +0 -125
- package/server/service/twin-ingest-window/twin-ingest-window-writer.ts +0 -57
- package/server/service/twin-ingest-window/twin-ingest-window.ts +0 -113
- package/server/service/twin-instance/index.ts +0 -3
- package/server/service/twin-instance/twin-instance.ts +0 -199
- package/server/service/twin-journal/event-type-split-shape.ts +0 -112
- package/server/service/twin-journal/event-type-split.ts +0 -144
- package/server/service/twin-journal/index.ts +0 -3
- package/server/service/twin-journal/journal-order.ts +0 -20
- package/server/service/twin-journal/twin-journal-query.ts +0 -615
- package/server/service/twin-lifecycle/domain-catalog.ts +0 -51
- package/server/service/twin-lifecycle/index.ts +0 -3
- package/server/service/twin-lifecycle/twin-lifecycle-mutation.ts +0 -285
- package/server/service/twin-metrics/index.ts +0 -3
- package/server/service/twin-metrics/twin-metrics-query.ts +0 -172
- package/server/service/twin-model/axis-journal-evidence.ts +0 -57
- package/server/service/twin-model/choose-recipe.ts +0 -31
- package/server/service/twin-model/epcis-coverage.ts +0 -251
- package/server/service/twin-model/external-resolver.ts +0 -173
- package/server/service/twin-model/iec61850-coverage.ts +0 -111
- package/server/service/twin-model/index.ts +0 -25
- package/server/service/twin-model/isa95-coverage.ts +0 -320
- package/server/service/twin-model/item-ref.ts +0 -80
- package/server/service/twin-model/name-index.ts +0 -94
- package/server/service/twin-model/project-structure.ts +0 -213
- package/server/service/twin-model/standard-coverage.ts +0 -35
- package/server/service/twin-model/status-tally.ts +0 -37
- package/server/service/twin-model/twin-equipment.ts +0 -191
- package/server/service/twin-model/twin-lineage-query.ts +0 -327
- package/server/service/twin-model/twin-location.ts +0 -147
- package/server/service/twin-model/twin-model-item-query.ts +0 -1029
- package/server/service/twin-model/twin-model-mutation.ts +0 -503
- package/server/service/twin-model/twin-model-query.ts +0 -792
- package/server/service/twin-model/twin-model-tree-query.ts +0 -211
- package/server/service/twin-model/twin-operation.ts +0 -157
- package/server/service/twin-readiness/index.ts +0 -3
- package/server/service/twin-readiness/twin-readiness-query.ts +0 -96
- package/server/service/twin-space/area-resolver.ts +0 -75
- package/server/service/twin-space/index.ts +0 -8
- package/server/service/twin-space/move-space.ts +0 -262
- package/server/service/twin-space/representation-role.ts +0 -15
- package/server/service/twin-space/space-integrity.ts +0 -293
- package/server/service/twin-space/twin-area.ts +0 -63
- package/server/service/twin-space/twin-space-area.ts +0 -66
- package/server/service/twin-space/twin-space-representation.ts +0 -99
- package/server/service/twin-space/twin-space-resolver.ts +0 -582
- package/server/service/twin-space/twin-space.ts +0 -148
- package/server/service/twin-state/index.ts +0 -3
- package/server/service/twin-state/twin-state-subscription.ts +0 -62
- package/server/service/twin-structure/index.ts +0 -3
- package/server/service/twin-structure/twin-structure.ts +0 -96
- package/server/service/twin-subject/event-subjects.ts +0 -162
- package/server/service/twin-subject/index.ts +0 -10
- package/server/service/twin-subject/item-resolver.ts +0 -92
- package/server/service/twin-subject/subject-query-shape.ts +0 -84
- package/server/service/twin-subject/twin-subject-backfill.ts +0 -196
- package/server/service/twin-subject/twin-subject-event.ts +0 -101
- package/server/service/twin-subject/twin-subject-fast-path.ts +0 -346
- package/server/service/twin-subject/twin-subject-query.ts +0 -210
- package/server/service/twin-subject/twin-subject-writer.ts +0 -189
- package/server/service/twin-target/index.ts +0 -5
- package/server/service/twin-target/twin-target-resolver.ts +0 -142
- package/server/service/twin-target/twin-target.ts +0 -97
- package/server/types/global.d.ts +0 -22
- package/shared/axis-read.ts +0 -28
- package/shared/entity-delta.ts +0 -335
- package/shared/kpi-broadcast.ts +0 -13
- package/shared/touched-items.ts +0 -73
- package/shared/twin-level.ts +0 -48
- package/test/ack-shape-alignment.test.ts +0 -97
- package/test/actuation-advisor.test.ts +0 -55
- package/test/actuation-approval-door.test.ts +0 -411
- package/test/actuation-dispatch.test.ts +0 -209
- package/test/actuation-effect.test.ts +0 -162
- package/test/actuation-outcome.test.ts +0 -152
- package/test/actuation-rule-runner.test.ts +0 -281
- package/test/actuation-rule.test.ts +0 -266
- package/test/actuation-seam.test.ts +0 -255
- package/test/actuation-settle.test.ts +0 -164
- package/test/actuation-target.test.ts +0 -145
- package/test/actuation-verification-state.test.ts +0 -139
- package/test/adopt-structure-live.test.ts +0 -133
- package/test/aggregate-not-findone.test.ts +0 -112
- package/test/area-resolver-db.test.ts +0 -110
- package/test/attention-digest.test.ts +0 -161
- package/test/axis-declaration-passthrough-db.test.ts +0 -152
- package/test/axis-journal-evidence.test.ts +0 -71
- package/test/axis-read.test.ts +0 -188
- package/test/backfill-shape.test.ts +0 -89
- package/test/boot-resume.test.ts +0 -287
- package/test/broadcast-cost-baseline.test.ts +0 -201
- package/test/broadcast-period.test.ts +0 -58
- package/test/broadcast-queue-overflow.test.ts +0 -244
- package/test/canonical-ingest-vocabularies.test.ts +0 -451
- package/test/canonical-quantity-door.test.ts +0 -61
- package/test/capability-mapping.test.ts +0 -89
- package/test/checkpoint-refuses-empty.test.ts +0 -103
- package/test/choose-recipe.test.ts +0 -117
- package/test/column-type-portability.test.ts +0 -122
- package/test/command-routing.test.ts +0 -61
- package/test/command-store-writes-columns.test.ts +0 -126
- package/test/connection-portability-doors.test.ts +0 -87
- package/test/connector-capability-declaration.test.ts +0 -195
- package/test/contract-layer-guard.test.ts +0 -48
- package/test/control-capability.test.ts +0 -98
- package/test/cursor-stall-not-read-failure.test.ts +0 -126
- package/test/declaration-reaches-model.test.ts +0 -226
- package/test/declared-location-types.test.ts +0 -89
- package/test/declared-stimulus.test.ts +0 -88
- package/test/dedupe-transformation.test.ts +0 -112
- package/test/discovery-result.test.ts +0 -75
- package/test/dispatch-after-approval-source.test.ts +0 -81
- package/test/duration-estimators.test.ts +0 -144
- package/test/energy-topology.test.ts +0 -113
- package/test/entity-delta.test.ts +0 -352
- package/test/equipment-identity-reaches.test.ts +0 -57
- package/test/event-subjects.test.ts +0 -148
- package/test/event-time-column.test.ts +0 -67
- package/test/event-type-split.test.ts +0 -178
- package/test/external-resolver.test.ts +0 -177
- package/test/fact-scope-wiring.test.ts +0 -96
- package/test/fill-dedupe.test.ts +0 -59
- package/test/fill-loop.test.ts +0 -35
- package/test/first-envelope.test.ts +0 -153
- package/test/fold-gap-speaks.test.ts +0 -53
- package/test/fold-shift-dst.test.ts +0 -110
- package/test/forecast-metrics.test.ts +0 -157
- package/test/forecast-tuning.test.ts +0 -42
- package/test/gap-analytics.test.ts +0 -113
- package/test/generated-not-counted.test.ts +0 -99
- package/test/guard-roots.ts +0 -76
- package/test/home-in-area.test.ts +0 -90
- package/test/hook-address.test.ts +0 -28
- package/test/hook-rejected-shape.test.ts +0 -86
- package/test/hook-response-sum.test.ts +0 -193
- package/test/hook-sequence.test.ts +0 -333
- package/test/ingest-bench.test.ts +0 -70
- package/test/ingest-dedupe.test.ts +0 -56
- package/test/ingest-expected-quiet.test.ts +0 -111
- package/test/ingest-health-engine.test.ts +0 -248
- package/test/ingest-health-wiring.test.ts +0 -120
- package/test/ingest-health.test.ts +0 -306
- package/test/ingest-history.test.ts +0 -247
- package/test/ingest-idempotent.test.ts +0 -211
- package/test/ingest-reconcile-four-ways.test.ts +0 -97
- package/test/ingest-rules-header.test.ts +0 -100
- package/test/ingest-running-guard.test.ts +0 -136
- package/test/ingest-space.test.ts +0 -78
- package/test/ingest-window-rolls-unobserved.test.ts +0 -72
- package/test/ingest-wiring-guard.test.ts +0 -197
- package/test/instance-cache-lifecycle.test.ts +0 -110
- package/test/intake-mapping-parity.test.ts +0 -175
- package/test/integration-probes.test.ts +0 -103
- package/test/integration-runner.test.ts +0 -95
- package/test/item-ref.test.ts +0 -78
- package/test/journal-order-axis.test.ts +0 -80
- package/test/journal-read-discipline.test.ts +0 -186
- package/test/journal-retention.test.ts +0 -275
- package/test/journal-sort-axis.test.ts +0 -142
- package/test/journal-write-door.test.ts +0 -110
- package/test/journal-write-trend.test.ts +0 -115
- package/test/kernel-kind-guard.test.ts +0 -98
- package/test/knob-defaults.test.ts +0 -72
- package/test/kpi-baseline-db.test.ts +0 -216
- package/test/kpi-baseline.test.ts +0 -196
- package/test/kpi-fold.test.ts +0 -1456
- package/test/kpi-query-bench.test.ts +0 -130
- package/test/lens-own-places.test.ts +0 -50
- package/test/lineage-survives-restart.test.ts +0 -234
- package/test/live-cadence.test.ts +0 -170
- package/test/live-cursor-wiring.test.ts +0 -87
- package/test/live-feed-lease.test.ts +0 -63
- package/test/live-feed-registry.test.ts +0 -75
- package/test/live-kernel-facts.test.ts +0 -161
- package/test/live-mirror-parity.test.ts +0 -286
- package/test/load-meter.test.ts +0 -328
- package/test/local-declarations.test.ts +0 -662
- package/test/log-stamp.test.ts +0 -59
- package/test/loop-lag.test.ts +0 -82
- package/test/master-to-twin.test.ts +0 -279
- package/test/measured-yield.test.ts +0 -90
- package/test/merge-streams.test.ts +0 -70
- package/test/mirror-resumes-from-checkpoint.test.ts +0 -192
- package/test/model-basis.test.ts +0 -141
- package/test/model-gap.test.ts +0 -153
- package/test/model-refusal.test.ts +0 -75
- package/test/model-vocabulary.test.ts +0 -114
- package/test/move-space.test.ts +0 -149
- package/test/mutation-gate.test.ts +0 -122
- package/test/oee-accumulator.test.ts +0 -204
- package/test/oee-site-calendar.test.ts +0 -146
- package/test/open-doors.test.ts +0 -197
- package/test/operation-basis.test.ts +0 -127
- package/test/operational-vocabulary.test.ts +0 -108
- package/test/operations-capability-db.test.ts +0 -165
- package/test/order-and-transaction-split.test.ts +0 -107
- package/test/order-fold-host.test.ts +0 -112
- package/test/producer-guard.test.ts +0 -66
- package/test/project-structure-db.test.ts +0 -226
- package/test/projection-reaches-screen.test.ts +0 -277
- package/test/projection-reads-declared.test.ts +0 -109
- package/test/property-effects.test.ts +0 -128
- package/test/provenance-promotion.test.ts +0 -99
- package/test/read-failure-ledger.test.ts +0 -106
- package/test/read-failure-visible.test.ts +0 -145
- package/test/read-time.test.ts +0 -118
- package/test/reference-fill.test.ts +0 -235
- package/test/reference-grounding.test.ts +0 -70
- package/test/reference-hook.test.ts +0 -177
- package/test/reference-links.test.ts +0 -56
- package/test/reference-reconciliation.test.ts +0 -39
- package/test/registry-key-guard.test.ts +0 -80
- package/test/rejection-carries-event-id.test.ts +0 -101
- package/test/rename-twin.test.ts +0 -89
- package/test/resolve-ts-siblings.mjs +0 -52
- package/test/restart-policy.test.ts +0 -111
- package/test/resync-origin-site.test.ts +0 -83
- package/test/retry-plan.test.ts +0 -139
- package/test/revision-axis.test.ts +0 -110
- package/test/runtime-key.test.ts +0 -66
- package/test/save-intake-collision.test.ts +0 -91
- package/test/scale-twin-bench.test.ts +0 -103
- package/test/secrets-at-rest.test.ts +0 -135
- package/test/snapshot-freshness.test.ts +0 -60
- package/test/source-outcome-audit.test.ts +0 -104
- package/test/space-integrity.test.ts +0 -223
- package/test/space-representation.test.ts +0 -66
- package/test/space-scope-includes-stopped.test.ts +0 -55
- package/test/spec-coverage.test.ts +0 -113
- package/test/stage-path.test.ts +0 -95
- package/test/standard-coverage.test.ts +0 -148
- package/test/state-axes.test.ts +0 -74
- package/test/status-tally.test.ts +0 -55
- package/test/streamline-e2e.test.ts +0 -76
- package/test/structure-revision-db.test.ts +0 -315
- package/test/subject-fast-path.test.ts +0 -148
- package/test/subject-read-model.test.ts +0 -175
- package/test/sweep-dispatch.test.ts +0 -191
- package/test/tenant-registry-db.test.ts +0 -149
- package/test/time-range.test.ts +0 -152
- package/test/touched-items.test.ts +0 -61
- package/test/twin-audit.test.ts +0 -83
- package/test/twin-event-keys.test.ts +0 -193
- package/test/twin-model-item-db.test.ts +0 -319
- package/test/twin-model-parent-relation-db.test.ts +0 -125
- package/test/twin-model-tree-db.test.ts +0 -209
- package/test/twin-origin-resync.test.ts +0 -133
- package/test/twin-state-channel.test.ts +0 -111
- package/test/vocabulary-guard.test.ts +0 -43
- package/test/warm-start-seam.test.ts +0 -162
- package/test/warm-start.test.ts +0 -297
- package/test/withheld-door.test.ts +0 -95
- package/test/yield-loop.test.ts +0 -199
- package/tsconfig.json +0 -15
- package/tsconfig.shared.json +0 -23
- package/tsconfig.shared.tsbuildinfo +0 -1
- package/tsconfig.tsbuildinfo +0 -1
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* **저널을 쓰는 문은 하나다** — 그리고 멈출 때 남은 것을 흘린다 (ingest-scale §7.1, 2026-08-20).
|
|
3
|
-
*
|
|
4
|
-
* ── 무엇이 관측됐나 ─────────────────────────────────────────────────────────
|
|
5
|
-
* 데모 자극을 켠 트윈 23개가 도는 서버에서 프로세스가 CPU 81~112% 를 계속 쓰고, 아무 일도 하지 않는
|
|
6
|
-
* 질의가 `twinLoad` 8초 · `twinInstanceList` 14초였다. `sample` 프로파일은 `JSON.parse`·대형 GC·
|
|
7
|
-
* 스레드풀을 가리켰고, 계기는 **틱의 몫이 16%** 라고 말했다 — 틱이 아니었다.
|
|
8
|
-
*
|
|
9
|
-
* 코드가 이유였다: 기록 경로가 둘인데 **배치는 라이브만**이었다.
|
|
10
|
-
* · 라이브 — 200ms 마다 모아 벌크 저장
|
|
11
|
-
* · 시뮬 — 커널이 낸 **델타 하나마다** 행 하나 저장(그리고 그 안에서 구조 리비전을 물었다)
|
|
12
|
-
*
|
|
13
|
-
* 같은 파일이 **브로드캐스팅**에서 같은 함정을 이미 겪고 고쳤다(틱 하나가 35초). 쓰기만 단건으로 남아 있었다.
|
|
14
|
-
* ADR-0030 이 *"기록 경로가 둘인 것은 시뮬만 옆문으로 들어오기 때문"* 이라 예고한 그 자리다.
|
|
15
|
-
*
|
|
16
|
-
* ── 성능을 사려고 사실을 팔지 않는다 ────────────────────────────────────────
|
|
17
|
-
* 배치로 옮기면 주기 사이에 멈출 때 그 구간이 사라진다 — **라이브도 이미 그랬다**(최대 200ms). 그래서
|
|
18
|
-
* 정지 때 남은 것을 흘리는 자리를 함께 만들었다. 상태만 남기고 저널을 잃으면 「왜 그렇게 됐나」가 빈다.
|
|
19
|
-
*
|
|
20
|
-
* 이 시험은 소스로 규율을 지킨다(DB 없이 도는 스위트다). 정량화는 들어온 것이 없는 서버의 `journalRate` 가 답한다.
|
|
21
|
-
*/
|
|
22
|
-
import { test } from 'node:test'
|
|
23
|
-
import assert from 'node:assert/strict'
|
|
24
|
-
import { readFileSync } from 'node:fs'
|
|
25
|
-
import { fileURLToPath } from 'node:url'
|
|
26
|
-
|
|
27
|
-
const engine = readFileSync(fileURLToPath(new URL('../server/engine/twin-engine.ts', import.meta.url)), 'utf-8')
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* 메서드 본문만 — 다른 자리의 같은 낱말에 걸리지 않게.
|
|
31
|
-
*
|
|
32
|
-
* **다음 메서드 선언에서 끊는다.** 길이로 자르면 옆 메서드가 딸려 와, 「이 메서드가 몇 번 부르나」를
|
|
33
|
-
* 세는 단정이 오류 없이 틀린 값을 낸다(실제로 그렇게 2를 세었다).
|
|
34
|
-
*/
|
|
35
|
-
function methodOf(src: string, name: string): string {
|
|
36
|
-
const at = src.indexOf(`${name}(`)
|
|
37
|
-
assert.ok(at >= 0, `${name} 가 없다`)
|
|
38
|
-
const rest = src.slice(at + name.length)
|
|
39
|
-
const next = rest.search(/\n (static|private static|async|[a-zA-Z]+\()/)
|
|
40
|
-
return rest.slice(0, next > 0 ? next : 3000)
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
test('시뮬이 델타마다 행을 쓰지 않는다 — 모아서 한 문으로 보낸다', () => {
|
|
44
|
-
const at = engine.indexOf("if (msg.kind === 'delta')")
|
|
45
|
-
const body = engine.slice(at, at + 2600)
|
|
46
|
-
assert.match(body, /pendingJournal/, '델타는 버퍼에 담긴다')
|
|
47
|
-
assert.equal(/this\.persist\(domainId, id, msg\)/.test(body), false, '단건 저장으로 되돌아갔다')
|
|
48
|
-
})
|
|
49
|
-
|
|
50
|
-
test('단건 저장 경로가 아예 없다 — 남겨 두면 다시 부를 자리가 된다', () => {
|
|
51
|
-
assert.equal(/static async persist\(domainId/.test(engine), false, '`persist` 가 되살아났다')
|
|
52
|
-
/* 쓰는 문은 둘이지만 **부르는 자리는 하나**다(`flushJournal`). */
|
|
53
|
-
/* 약속을 돌려준다 — 정지가 기다릴 수 있어야 한다(기다리지 않으면 웜스타트가 빈 저널을 읽는다). */
|
|
54
|
-
assert.match(engine, /private static flushJournal\(inst: InstanceRuntime\): Promise<void>/)
|
|
55
|
-
assert.match(engine, /await this\.flushJournal\(i\)/, '정지는 흘리기를 기다린다')
|
|
56
|
-
assert.match(engine, /static async persistBatch\(/)
|
|
57
|
-
assert.match(engine, /static async persistCarried\(/)
|
|
58
|
-
/* 넣기만 한다 — `save` 는 행마다 SELECT 를 한 번 더 한다(시험 로그에서 그 질의가 보였다). */
|
|
59
|
-
assert.match(engine, /private static async insertRows\(/)
|
|
60
|
-
assert.equal(/repo\.save\(rows, \{ chunk: 500 \}\)/.test(engine), false, '저널을 save 로 넣는 모양으로 되돌아갔다')
|
|
61
|
-
})
|
|
62
|
-
|
|
63
|
-
test('커널이 매긴 리비전을 호스트가 다시 번호 붙이지 않는다 — 시간여행이 그것을 딛는다', () => {
|
|
64
|
-
const flush = methodOf(engine, 'private static flushJournal')
|
|
65
|
-
/* 시뮬은 자기 리비전을 들고 오고(`{ event, revision }`), 라이브는 flush 에서 이어 붙인다. */
|
|
66
|
-
assert.match(flush, /typeof b\.revision === 'number'/, '들고 온 리비전을 가려낸다')
|
|
67
|
-
assert.match(flush, /inst\.revision = start \+ plain\.length/, '라이브만 이어 붙인다')
|
|
68
|
-
const carried = methodOf(engine, 'static async persistCarried')
|
|
69
|
-
assert.match(carried, /it\.revision/, '들고 온 리비전을 그대로 쓴다')
|
|
70
|
-
/* 구조 리비전은 배치에 **한 번**만 묻는다 — 행마다 묻던 것이 문제였다. */
|
|
71
|
-
assert.equal((carried.match(/structureRevOf/g) ?? []).length, 1)
|
|
72
|
-
})
|
|
73
|
-
|
|
74
|
-
test('멈출 때 남은 것을 흘린다 — 성능을 사려고 사실을 팔지 않는다', () => {
|
|
75
|
-
const stop = methodOf(engine, 'static async stop')
|
|
76
|
-
assert.match(stop, /this\.flushJournal\(i\)/, '정지 경로가 버퍼를 흘린다')
|
|
77
|
-
const flushAt = stop.indexOf('this.flushJournal(i)')
|
|
78
|
-
const dropAt = stop.indexOf('delete this.instances')
|
|
79
|
-
assert.ok(flushAt >= 0 && dropAt >= 0 && flushAt < dropAt, '런타임을 버리기 전에 흘려야 한다')
|
|
80
|
-
})
|
|
81
|
-
|
|
82
|
-
test('두 구동이 같은 계기를 든다 — 세지 않는 개선은 되돌려도 아무도 모른다', () => {
|
|
83
|
-
/*
|
|
84
|
-
* 계기(`inst.metrics`)가 `startLive` 에만 있었다. 그래서 도는 트윈 22개가 시뮬인 서버에서 「초당 몇 행을
|
|
85
|
-
* 쓰나」·「밀린 게 있나」를 물을 자리가 없었고, 모니터 목록에서도 시뮬이 알리지 않고 빠졌다(계기가 null 이라
|
|
86
|
-
* 걸러졌다) — 부하가 거의 없는 것처럼 보였다.
|
|
87
|
-
*/
|
|
88
|
-
assert.match(engine, /private static newMetrics\(\): TwinMetrics/, '계기 한 벌을 한 곳에서 만든다')
|
|
89
|
-
const sim = engine.slice(engine.indexOf("mode: 'sim'"), engine.indexOf("mode: 'sim'") + 1200)
|
|
90
|
-
assert.match(sim, /metrics: this\.newMetrics\(\)/, '시뮬도 계기를 든다')
|
|
91
|
-
/* 비율 갱신이 라이브만 보면 시뮬 칸은 영원히 0 이다. */
|
|
92
|
-
assert.equal(/const m = isLive \? inst\.metrics : undefined/.test(engine), false, '비율 갱신이 시뮬을 다시 잘라 냈다')
|
|
93
|
-
})
|
|
94
|
-
|
|
95
|
-
test('적은 것을 장부에 남긴다 — 계기는 지금만 답하고 재기동에 0 으로 돌아간다', () => {
|
|
96
|
-
const flush = methodOf(engine, 'private static flushJournal')
|
|
97
|
-
/* 실패한 배치를 「남았다」고 세지 않으려면 **쓴 뒤에** 적어야 한다. */
|
|
98
|
-
assert.match(flush, /this\.recordJournalWrite\(inst\.domainId, inst\.id, written\)/)
|
|
99
|
-
const thenAt = flush.indexOf('.then(')
|
|
100
|
-
const recAt = flush.indexOf('this.recordJournalWrite')
|
|
101
|
-
assert.ok(thenAt >= 0 && recAt > thenAt, '약속이 풀린 뒤에 적는다 — 터진 배치를 세지 않게')
|
|
102
|
-
})
|
|
103
|
-
|
|
104
|
-
test('구조 리비전은 「없다」도 기억한다 — 없는 트윈이 부를 때마다 질의하지 않게', () => {
|
|
105
|
-
const body = methodOf(engine, 'static async structureRevOf')
|
|
106
|
-
assert.match(body, /this\.structureRevCache\[key\] = latest \? latest\.rev : null/, '미스를 기억한다')
|
|
107
|
-
assert.equal(/if \(latest\) this\.structureRevCache/.test(body), false, '미스를 흘려버리는 모양으로 되돌아갔다')
|
|
108
|
-
/* 캐시가 `null` 을 담으므로 「모름」과 구별된다 — 형이 그 사실을 말해야 다음 사람이 0 으로 읽지 않는다. */
|
|
109
|
-
assert.match(engine, /structureRevCache: Record<string, number \| null>/)
|
|
110
|
-
})
|
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* **적은 것도 추이로 남는다** — 「저널이 얼마나 쓰이고 있나」를 나중에도 물을 수 있게 (2026-08-20).
|
|
3
|
-
*
|
|
4
|
-
* ── 왜 필요한가 ─────────────────────────────────────────────────────────────
|
|
5
|
-
* 계기(`journalRate`·`journaledTotal`)는 **지금**만 답한다. 프로세스를 다시 띄우면 0 에서 시작하므로
|
|
6
|
-
* 「어제 이 시각에도 이랬나」·「고친 뒤로 정말 줄었나」를 물을 자리가 없었다. 그런데 2026-08-20 에 고친
|
|
7
|
-
* 것이 바로 쓰기 경로(델타마다 한 행 → 창당 한 번)다. 세지 않는 개선은 되돌아가도 아무도 모른다.
|
|
8
|
-
*
|
|
9
|
-
* ── 왜 새 장부를 만들지 않았나 ──────────────────────────────────────────────
|
|
10
|
-
* 유입 창은 「닫힌 창만 최근」·「0 과 없음을 가른다」·「밀려난 것을 세어 드러낸다」를 이미 지키고, 닫힐 때
|
|
11
|
-
* 행으로 남는다. 같은 규율을 두 벌 만들면 한 벌만 고쳐진다. 그래서 그 창에 칸을 더했다.
|
|
12
|
-
*
|
|
13
|
-
* ── 이 시험이 지키는 것 ─────────────────────────────────────────────────────
|
|
14
|
-
* ① 유입이 한 번도 없어도 쓰기가 창을 넘긴다 — 시뮬 트윈이 추이에서 사라지지 않게.
|
|
15
|
-
* ② 행과 왕복을 **따로** 센다 — 행당 왕복이 1 로 되돌아가면 그것이 보인다.
|
|
16
|
-
* ③ 쓰기는 **「마지막 유입」을 갱신하지 않는다** — 섞으면 끊긴 피드가 옛 창으로 초록을 낸다.
|
|
17
|
-
* ④ 없는 것을 0 건 쓰기로 만들지 않는다.
|
|
18
|
-
* ⑤ 쓰기만 있는 장부의 시각이 1969년으로 뜨지 않는다.
|
|
19
|
-
*/
|
|
20
|
-
import { test } from 'node:test'
|
|
21
|
-
import assert from 'node:assert/strict'
|
|
22
|
-
|
|
23
|
-
import {
|
|
24
|
-
INGEST_WINDOW_MS,
|
|
25
|
-
STALE_AFTER_MS,
|
|
26
|
-
ingestHealth,
|
|
27
|
-
newIngestLedger,
|
|
28
|
-
recordIngest,
|
|
29
|
-
recordJournalWrite
|
|
30
|
-
} from '../server/engine/ingest-health.ts'
|
|
31
|
-
|
|
32
|
-
const T0 = 1_700_000_000_000
|
|
33
|
-
|
|
34
|
-
/* ── ① 유입 없이도 추이가 생긴다 ──────────────────────────────────────────── */
|
|
35
|
-
|
|
36
|
-
test('받는 것 없이 적기만 하는 트윈도 추이에 남는다 — 시뮬이 오류 없이 빠지지 않게', () => {
|
|
37
|
-
const l = newIngestLedger()
|
|
38
|
-
const closed: any[] = []
|
|
39
|
-
|
|
40
|
-
recordJournalWrite(l, 40, T0, w => closed.push(w))
|
|
41
|
-
recordJournalWrite(l, 60, T0 + 1000, w => closed.push(w))
|
|
42
|
-
/* 창 길이를 넘긴 뒤의 쓰기가 앞 창을 닫는다. */
|
|
43
|
-
recordJournalWrite(l, 10, T0 + INGEST_WINDOW_MS + 1, w => closed.push(w))
|
|
44
|
-
|
|
45
|
-
assert.equal(closed.length, 1, '창이 닫혀 영속 훅까지 불린다')
|
|
46
|
-
assert.equal(closed[0].rows, 100)
|
|
47
|
-
assert.equal(closed[0].offered, 0, '유입은 0 이다 — 그것도 사실이다')
|
|
48
|
-
|
|
49
|
-
const view = ingestHealth(l, 'not-applicable', undefined, T0 + INGEST_WINDOW_MS + 2)
|
|
50
|
-
assert.equal(view.trend.length, 1, '유입 시각이 없다는 이유로 빈 화면을 내지 않는다')
|
|
51
|
-
assert.equal(view.trend[0].rows, 100)
|
|
52
|
-
assert.equal(view.total.rows, 110, '누적은 열린 창까지 센다')
|
|
53
|
-
})
|
|
54
|
-
|
|
55
|
-
/* ── ② 행과 왕복을 따로 센다 ──────────────────────────────────────────────── */
|
|
56
|
-
|
|
57
|
-
test('행과 왕복을 따로 센다 — 행당 왕복이 1 로 되돌아가면 그것이 보인다', () => {
|
|
58
|
-
const batched = newIngestLedger()
|
|
59
|
-
recordJournalWrite(batched, 500, T0)
|
|
60
|
-
recordJournalWrite(batched, 500, T0 + 200)
|
|
61
|
-
assert.equal(batched.rows, 1000)
|
|
62
|
-
assert.equal(batched.flushes, 2, '모아 보낸 것은 두 번이다')
|
|
63
|
-
|
|
64
|
-
/* 되돌아간 모양 — 같은 1,000행에 왕복 1,000번. 행 수만 세면 두 장부가 똑같이 보인다. */
|
|
65
|
-
const perDelta = newIngestLedger()
|
|
66
|
-
for (let i = 0; i < 1000; i++) recordJournalWrite(perDelta, 1, T0 + i)
|
|
67
|
-
assert.equal(perDelta.rows, batched.rows, '행 수는 같다 — 그래서 행만으로는 가려낼 수 없다')
|
|
68
|
-
assert.ok(perDelta.flushes > batched.flushes * 100, '왕복 수가 그 차이를 드러낸다')
|
|
69
|
-
})
|
|
70
|
-
|
|
71
|
-
/* ── ③ 축을 섞지 않는다 ───────────────────────────────────────────────────── */
|
|
72
|
-
|
|
73
|
-
test('쓰기가 「마지막 유입」을 갱신하지 않는다 — 끊긴 피드가 초록으로 보이지 않게', () => {
|
|
74
|
-
const l = newIngestLedger()
|
|
75
|
-
/* 건강한 유입이 한동안 있었고, 그 뒤 피드가 끊겼다. */
|
|
76
|
-
recordIngest(l, 100, [], T0)
|
|
77
|
-
recordIngest(l, 100, [], T0 + INGEST_WINDOW_MS + 1) /* 앞 창을 닫아 「최근」을 만든다 */
|
|
78
|
-
|
|
79
|
-
const muchLater = T0 + INGEST_WINDOW_MS + STALE_AFTER_MS + 60_000
|
|
80
|
-
/* 그 사이에도 트윈은 틱이 만든 델타를 계속 적는다 — 라이브에서 실제로 그렇다. */
|
|
81
|
-
for (let t = T0 + 60_000; t < muchLater; t += 60_000) recordJournalWrite(l, 5, t)
|
|
82
|
-
|
|
83
|
-
const view = ingestHealth(l, 'attached', undefined, muchLater)
|
|
84
|
-
/*
|
|
85
|
-
* 판정이 초록이 아니어야 한다. 어떤 말로 부르는지는 창의 모양에 달렸다(마지막 닫힌 창이 유입 0 이면
|
|
86
|
-
* 「유입 없음」, 창이 아직 안 닫혔으면 「끊긴 피드」) — 둘 다 「받고 있지 않다」다. 못 박는 것은 **적고
|
|
87
|
-
* 있다는 사실이 「받고 있다」로 번역되지 않는다**는 것이다.
|
|
88
|
-
*/
|
|
89
|
-
assert.notEqual(view.verdict, 'healthy', '적고 있다는 사실이 「받고 있다」로 번역되지 않는다')
|
|
90
|
-
assert.equal(view.lastAt, new Date(T0 + INGEST_WINDOW_MS + 1).toISOString(), '마지막 유입 시각이 쓰기로 밀리지 않는다')
|
|
91
|
-
assert.equal(view.lastWriteAt, new Date(muchLater - 60_000).toISOString(), '적은 시각은 따로 낸다')
|
|
92
|
-
})
|
|
93
|
-
|
|
94
|
-
/* ── ④⑤ 없는 것과 0 ──────────────────────────────────────────────────────── */
|
|
95
|
-
|
|
96
|
-
test('없는 것을 0 건 쓰기로 만들지 않는다', () => {
|
|
97
|
-
const l = newIngestLedger()
|
|
98
|
-
for (const bad of [0, -3, NaN, Infinity]) recordJournalWrite(l, bad as number, T0)
|
|
99
|
-
assert.equal(l.flushes, 0, '아무 일도 없었으면 왕복도 없다')
|
|
100
|
-
assert.equal(l.rows, 0)
|
|
101
|
-
assert.equal(l.lastWriteAtMs, -1, '적지 않았으면 적은 시각도 없다')
|
|
102
|
-
assert.equal(ingestHealth(l, 'not-applicable', undefined, T0).lastWriteAt, null)
|
|
103
|
-
})
|
|
104
|
-
|
|
105
|
-
test('쓰기만 있는 장부의 시각이 1969년으로 뜨지 않는다 — `-1` 을 시각으로 읽지 않는다', () => {
|
|
106
|
-
const l = newIngestLedger()
|
|
107
|
-
recordJournalWrite(l, 7, T0)
|
|
108
|
-
recordJournalWrite(l, 7, T0 + INGEST_WINDOW_MS + 1)
|
|
109
|
-
|
|
110
|
-
const view = ingestHealth(l, 'not-applicable', undefined, T0 + INGEST_WINDOW_MS + 2)
|
|
111
|
-
assert.equal(view.lastAt, null, '받은 적이 없으면 유입 시각은 **없다**(0 이 아니다)')
|
|
112
|
-
for (const w of [...view.trend, view.recent!]) {
|
|
113
|
-
assert.ok(new Date(w.to).getUTCFullYear() >= 2023, `창 끝 시각이 ${w.to} 로 떴다`)
|
|
114
|
-
}
|
|
115
|
-
})
|
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* 커널 선택 가드 — **모르는 종류로는 뜨지 않는다.**
|
|
3
|
-
*
|
|
4
|
-
* ── 무엇이 있었나 (2026-08-08) ───────────────────────────────────────────────
|
|
5
|
-
* 커널을 고르는 자리 셋이 모두 이랬다:
|
|
6
|
-
*
|
|
7
|
-
* const Kernel = KERNELS[reg.kind ?? 'wms'] ?? WmsKernel
|
|
8
|
-
*
|
|
9
|
-
* 폴백이 둘 겹쳐 있었다. ①은 컬럼이 비었을 때, ②는 표에 없는 값일 때. 그리고 `kind` 는
|
|
10
|
-
* 검증 없는 자유 문자열이다(`@Arg('kind') kind: string` — enum 도 화이트리스트도 없다).
|
|
11
|
-
* 그래서 오타 하나·대소문자 하나로 **야드/생산 트윈이 오류 없이 창고 커널로 돌았다.**
|
|
12
|
-
* 오류가 없으니 화면에는 트윈이 정상으로 보이고, 안에서 도는 규칙만 다른 도메인의 것이다.
|
|
13
|
-
* 예측 경로가 가장 나빴다 — 야드의 미래를 창고 규칙으로 굴려 놓고 숫자만 뜬다.
|
|
14
|
-
*
|
|
15
|
-
* ── 왜 검사로 남기나 ────────────────────────────────────────────────────────
|
|
16
|
-
* 자리를 고치는 것만으로는 부족하다. 커널 종류가 늘 때(다음은 TMS 든 무엇이든) 새 자리가
|
|
17
|
-
* 생기고, `?? WmsKernel` 은 "안전한 기본값" 처럼 보여서 다시 자란다. 실제로 세 자리에
|
|
18
|
-
* 똑같이 자라 있었다. 틀린 공장을 알리지 않고 띄우는 것보다 뜨지 않는 편이 낫다.
|
|
19
|
-
*
|
|
20
|
-
* 이 검사가 막는 것은 **표를 밖에서 직접 색인하는 것**이다. 종류→커널 해소는 `kernelFor`
|
|
21
|
-
* 하나만 한다. 새 자리를 쓸 때 이 검사가 걸리면, 폴백을 붙이지 말고 `kernelFor` 를 부를 것.
|
|
22
|
-
*/
|
|
23
|
-
import { test } from 'node:test'
|
|
24
|
-
import assert from 'node:assert/strict'
|
|
25
|
-
import { readdirSync, readFileSync, statSync } from 'node:fs'
|
|
26
|
-
import { join } from 'node:path'
|
|
27
|
-
|
|
28
|
-
import { guardRoots } from './guard-roots.ts'
|
|
29
|
-
|
|
30
|
-
const ENGINE = new URL('../server/engine/twin-engine.ts', import.meta.url).pathname
|
|
31
|
-
const MUTATION = new URL('../server/service/twin-lifecycle/twin-lifecycle-mutation.ts', import.meta.url).pathname
|
|
32
|
-
|
|
33
|
-
/*
|
|
34
|
-
* 종류 폴백은 **한 패키지에 갇혀 있지 않다.** 처음에 twin-engine 두 자리만 고쳤더니, 라이브 미러를
|
|
35
|
-
* 띄우는 호출자 넷(operato-twin 의 벤치·부하시험·프리셋 프로비저닝, headless 의 reference-live)이
|
|
36
|
-
* `det.kind ?? 'wms'` 로 **엔진이 오류를 내기 전에 먼저 값을 정하고** 있었다. 커넥터 spec 도 마찬가지였다.
|
|
37
|
-
* 그래서 두 패키지의 서버 코드를 함께 훑는다.
|
|
38
|
-
*/
|
|
39
|
-
const ROOTS = guardRoots(import.meta.url, ['../server'])
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* 코드만 본다 — 주석은 **과거를 설명하느라 옛 코드를 그대로 인용한다**(바로 위 머리말이 그렇다).
|
|
43
|
-
* 줄 단위로 `*`·`//` 만 걸러내면 여러 줄 주석의 둘째 줄부터 새어 나오므로 블록을 통째로 지운다.
|
|
44
|
-
*/
|
|
45
|
-
function codeOf(path: string): string[] {
|
|
46
|
-
return readFileSync(path, 'utf-8')
|
|
47
|
-
.replace(/\/\*[\s\S]*?\*\//g, '') // 블록 주석
|
|
48
|
-
.split('\n')
|
|
49
|
-
.map(l => l.replace(/\/\/.*$/, '')) // 줄 주석
|
|
50
|
-
.filter(l => l.trim())
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
test('커널 폴백이 없다 — 모르는 종류는 창고로 떨어지지 않는다', () => {
|
|
54
|
-
const offenders = codeOf(ENGINE).filter(l => /\?\?\s*WmsKernel|\|\|\s*WmsKernel/.test(l))
|
|
55
|
-
assert.deepEqual(offenders, [], '커널 선택에 폴백이 다시 생겼다 — kernelFor 가 오류를 내게 할 것')
|
|
56
|
-
})
|
|
57
|
-
|
|
58
|
-
test('종류→커널 해소는 kernelFor 한 곳만 한다', () => {
|
|
59
|
-
/* 표를 직접 색인하는 줄. 정의부(`const KERNELS: Record…`)와 kernelFor 내부는 제외. */
|
|
60
|
-
const indexing = codeOf(ENGINE).filter(l => /KERNELS\[/.test(l) && !/const K = KERNELS\[kind\]/.test(l))
|
|
61
|
-
assert.deepEqual(indexing, [], 'KERNELS 를 밖에서 색인하고 있다 — kernelFor(kind) 를 쓸 것')
|
|
62
|
-
})
|
|
63
|
-
|
|
64
|
-
test('kernelFor 는 모르는 종류에 오류를 낸다 — 알리지 않고 고르지 않는다', () => {
|
|
65
|
-
const src = readFileSync(ENGINE, 'utf-8')
|
|
66
|
-
const fn = src.slice(src.indexOf('function kernelFor'))
|
|
67
|
-
assert.match(fn.slice(0, 300), /throw new Error/, 'kernelFor 가 오류를 내지 않으면 이 계약이 무너진다')
|
|
68
|
-
})
|
|
69
|
-
|
|
70
|
-
test("board 를 주는 기동은 kind 를 요구한다 — `kind ?? 'wms'` 로 메우지 않는다", () => {
|
|
71
|
-
const offenders = codeOf(MUTATION).filter(l => /kind\s*\?\?\s*['"]/.test(l))
|
|
72
|
-
assert.deepEqual(offenders, [], '종류를 빠뜨린 프로비저닝이 다시 창고로 기동한다')
|
|
73
|
-
})
|
|
74
|
-
|
|
75
|
-
/** 두 패키지의 서버 .ts 전부. */
|
|
76
|
-
function serverFiles(): string[] {
|
|
77
|
-
const out: string[] = []
|
|
78
|
-
const walk = (dir: string) => {
|
|
79
|
-
for (const e of readdirSync(dir)) {
|
|
80
|
-
const p = join(dir, e)
|
|
81
|
-
if (statSync(p).isDirectory()) walk(p)
|
|
82
|
-
else if (p.endsWith('.ts') && !p.endsWith('.d.ts')) out.push(p)
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
for (const r of ROOTS) walk(r)
|
|
86
|
-
return out
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
test('어느 서버 코드도 커널 종류를 리터럴로 메우지 않는다 — 호출자가 엔진의 검증을 앞질렀다', () => {
|
|
90
|
-
/* 예시 JSON 안의 `system: 'wms'` 는 필드를 보여 주는 것이라 대상이 아니다 — 폴백 연산자만 본다. */
|
|
91
|
-
const offenders: string[] = []
|
|
92
|
-
for (const f of serverFiles()) {
|
|
93
|
-
codeOf(f).forEach(l => {
|
|
94
|
-
if (/(\?\?|\|\|)\s*['"](wms|yms|mes)['"]/.test(l)) offenders.push(`${f.split('/packages/')[1]}: ${l.trim().slice(0, 90)}`)
|
|
95
|
-
})
|
|
96
|
-
}
|
|
97
|
-
assert.deepEqual(offenders, [], '종류 폴백이 다시 생겼다 — 선언이 없으면 메우지 말고 던질 것')
|
|
98
|
-
})
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* 노브 기본값 — **알리지 않고 아무것도 안 하는 트윈**이 만들어지지 않게.
|
|
3
|
-
*
|
|
4
|
-
* 기본값을 아는 곳은 노브 스키마(계약)인데 그것을 적용하는 곳이 위자드뿐이었다. 그래서 화면을 거치지
|
|
5
|
-
* 않는 호출자가 파라미터를 일부만 주면 템플릿 안에서 발생률이 0 이 되고, 이벤트를 하나도 만들지 않는
|
|
6
|
-
* 트윈이 아무 말 없이 생겼다(실제로 밟았고 템플릿이 깨진 것으로 오진했다).
|
|
7
|
-
*/
|
|
8
|
-
import { test } from 'node:test'
|
|
9
|
-
import assert from 'node:assert/strict'
|
|
10
|
-
|
|
11
|
-
import { applyKnobDefaults, filledKnobKeys } from '../server/service/reference/knob-defaults.ts'
|
|
12
|
-
|
|
13
|
-
const KNOBS = [
|
|
14
|
-
{ key: 'id', default: 'wms-new' },
|
|
15
|
-
{ key: 'inboundRate', default: 40 },
|
|
16
|
-
{ key: 'outboundRate', default: 30 },
|
|
17
|
-
{ key: 'geo', default: { lat: 37.5, lon: 127.0 } },
|
|
18
|
-
{ key: 'noDefault' } // 기본값을 선언하지 않은 노브
|
|
19
|
-
]
|
|
20
|
-
|
|
21
|
-
test('빠진 노브를 기본값으로 채운다 — 부분 파라미터 호출이 죽은 트윈을 만들지 않게', () => {
|
|
22
|
-
const out = applyKnobDefaults(KNOBS, { id: 'mine' })
|
|
23
|
-
assert.equal(out.id, 'mine', '준 값은 그대로')
|
|
24
|
-
assert.equal(out.inboundRate, 40)
|
|
25
|
-
assert.equal(out.outboundRate, 30)
|
|
26
|
-
assert.deepEqual(out.geo, { lat: 37.5, lon: 127.0 })
|
|
27
|
-
assert.ok(!('noDefault' in out), '기본값 없는 노브는 만들어 내지 않는다')
|
|
28
|
-
})
|
|
29
|
-
|
|
30
|
-
test('0 은 덮지 않는다 — "자극 없이 구조만" 이라는 선언을 뒤집지 않게', () => {
|
|
31
|
-
/* falsy 로 판정해 덮으면 발생률 0(도착 없음)이 40 으로 바뀐다. 없는 것과 0 은 다르다. */
|
|
32
|
-
const out = applyKnobDefaults(KNOBS, { inboundRate: 0, outboundRate: 0 })
|
|
33
|
-
assert.equal(out.inboundRate, 0)
|
|
34
|
-
assert.equal(out.outboundRate, 0)
|
|
35
|
-
})
|
|
36
|
-
|
|
37
|
-
test('빈 문자열·false 도 뜻 있는 값이라 덮지 않는다', () => {
|
|
38
|
-
const out = applyKnobDefaults([{ key: 'name', default: 'New site' }, { key: 'flag', default: true }], { name: '', flag: false })
|
|
39
|
-
assert.equal(out.name, '')
|
|
40
|
-
assert.equal(out.flag, false)
|
|
41
|
-
})
|
|
42
|
-
|
|
43
|
-
test('null 은 "주지 않음" 으로 본다 — JSON·GraphQL 경로가 그렇게 보낸다', () => {
|
|
44
|
-
const out = applyKnobDefaults(KNOBS, { inboundRate: null as any })
|
|
45
|
-
assert.equal(out.inboundRate, 40)
|
|
46
|
-
})
|
|
47
|
-
|
|
48
|
-
test('선언 밖 파라미터는 손대지 않는다 — 템플릿의 폴백 경로를 끊지 않게', () => {
|
|
49
|
-
/* 템플릿은 `geo` 대신 `lat`/`lon` 을 읽는 폴백을 가진다. 모르는 키를 걷어내면 그 경로가 끊긴다. */
|
|
50
|
-
const out = applyKnobDefaults(KNOBS, { lat: 1, lon: 2 })
|
|
51
|
-
assert.equal(out.lat, 1)
|
|
52
|
-
assert.equal(out.lon, 2)
|
|
53
|
-
})
|
|
54
|
-
|
|
55
|
-
test('입력을 바꾸지 않는다 — 호출자의 객체가 몰래 변하면 추적이 어렵다', () => {
|
|
56
|
-
const given = { id: 'mine' }
|
|
57
|
-
applyKnobDefaults(KNOBS, given)
|
|
58
|
-
assert.deepEqual(given, { id: 'mine' })
|
|
59
|
-
})
|
|
60
|
-
|
|
61
|
-
test('노브가 없거나 파라미터가 없어도 무너지지 않는다', () => {
|
|
62
|
-
assert.deepEqual(applyKnobDefaults(null, { a: 1 }), { a: 1 })
|
|
63
|
-
assert.deepEqual(applyKnobDefaults(KNOBS, null).inboundRate, 40)
|
|
64
|
-
assert.deepEqual(applyKnobDefaults(undefined, undefined), {})
|
|
65
|
-
})
|
|
66
|
-
|
|
67
|
-
test('무엇을 채웠는지 알린다 — 준 적 없는 값으로 만들어진 것을 밝히게', () => {
|
|
68
|
-
assert.deepEqual(filledKnobKeys(KNOBS, { id: 'mine', inboundRate: 0 }), ['outboundRate', 'geo'],
|
|
69
|
-
'준 것과 0 은 채운 목록에 없다')
|
|
70
|
-
assert.deepEqual(filledKnobKeys(KNOBS, null), ['id', 'inboundRate', 'outboundRate', 'geo'])
|
|
71
|
-
assert.deepEqual(filledKnobKeys(KNOBS, { id: 'a', inboundRate: 1, outboundRate: 2, geo: {} }), [], '다 주면 빈 목록')
|
|
72
|
-
})
|
|
@@ -1,216 +0,0 @@
|
|
|
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
|
-
restartPolicy: 'reset', /* 선언은 이제 필수 — 비워 두면 읽는 자리마다 기본값을 고른다 */
|
|
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
|
-
})
|