@things-factory/headless-twin 10.0.14 → 10.0.17
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 +4 -4
- package/dist-server/engine/attention-digest.js +7 -7
- package/dist-server/engine/attention-digest.js.map +1 -1
- package/dist-server/engine/canonical-ingest.d.ts +11 -2
- package/dist-server/engine/canonical-ingest.js +137 -8
- package/dist-server/engine/canonical-ingest.js.map +1 -1
- package/dist-server/engine/command-routing.js +1 -1
- package/dist-server/engine/command-routing.js.map +1 -1
- package/dist-server/engine/declared-stimulus.js +1 -1
- package/dist-server/engine/declared-stimulus.js.map +1 -1
- package/dist-server/engine/energy-topology.js +1 -1
- package/dist-server/engine/energy-topology.js.map +1 -1
- package/dist-server/engine/ingest-dedupe.d.ts +38 -0
- package/dist-server/engine/ingest-dedupe.js +145 -0
- package/dist-server/engine/ingest-dedupe.js.map +1 -0
- package/dist-server/engine/ingest-health.d.ts +124 -9
- package/dist-server/engine/ingest-health.js +169 -15
- package/dist-server/engine/ingest-health.js.map +1 -1
- package/dist-server/engine/integration-probes.d.ts +3 -3
- package/dist-server/engine/integration-probes.js +6 -6
- package/dist-server/engine/integration-probes.js.map +1 -1
- package/dist-server/engine/kpi-baseline.js +4 -4
- package/dist-server/engine/kpi-baseline.js.map +1 -1
- package/dist-server/engine/kpi-fold.d.ts +122 -12
- package/dist-server/engine/kpi-fold.js +247 -18
- package/dist-server/engine/kpi-fold.js.map +1 -1
- package/dist-server/engine/kpi-query.d.ts +11 -11
- package/dist-server/engine/kpi-query.js +185 -28
- package/dist-server/engine/kpi-query.js.map +1 -1
- package/dist-server/engine/live-feed-registry.d.ts +1 -1
- package/dist-server/engine/live-feed-registry.js +1 -1
- package/dist-server/engine/live-feed-registry.js.map +1 -1
- package/dist-server/engine/load-meter.d.ts +1 -1
- package/dist-server/engine/load-meter.js.map +1 -1
- package/dist-server/engine/local-declarations.d.ts +7 -7
- package/dist-server/engine/local-declarations.js +9 -9
- package/dist-server/engine/local-declarations.js.map +1 -1
- package/dist-server/engine/log.d.ts +1 -1
- package/dist-server/engine/log.js +1 -1
- package/dist-server/engine/log.js.map +1 -1
- package/dist-server/engine/measured-estimator.js +1 -1
- package/dist-server/engine/measured-estimator.js.map +1 -1
- package/dist-server/engine/measured-yield.js +1 -1
- package/dist-server/engine/measured-yield.js.map +1 -1
- package/dist-server/engine/model-basis.d.ts +1 -1
- package/dist-server/engine/model-basis.js +1 -1
- package/dist-server/engine/model-basis.js.map +1 -1
- package/dist-server/engine/model-gap.d.ts +3 -3
- package/dist-server/engine/model-gap.js +1 -1
- package/dist-server/engine/model-gap.js.map +1 -1
- package/dist-server/engine/model-vocabulary.js +1 -1
- package/dist-server/engine/model-vocabulary.js.map +1 -1
- package/dist-server/engine/oee-accumulator.d.ts +4 -4
- package/dist-server/engine/oee-accumulator.js +3 -3
- package/dist-server/engine/oee-accumulator.js.map +1 -1
- package/dist-server/engine/property-effects.js +2 -2
- package/dist-server/engine/property-effects.js.map +1 -1
- package/dist-server/engine/read-time.d.ts +22 -0
- package/dist-server/engine/read-time.js +81 -0
- package/dist-server/engine/read-time.js.map +1 -0
- package/dist-server/engine/restart-policy.js +1 -1
- package/dist-server/engine/restart-policy.js.map +1 -1
- package/dist-server/engine/runtime-key.js +1 -1
- package/dist-server/engine/runtime-key.js.map +1 -1
- package/dist-server/engine/spec-coverage.d.ts +2 -2
- package/dist-server/engine/spec-coverage.js +1 -1
- package/dist-server/engine/spec-coverage.js.map +1 -1
- package/dist-server/engine/stage-path.d.ts +4 -4
- package/dist-server/engine/stage-path.js +1 -1
- package/dist-server/engine/stage-path.js.map +1 -1
- package/dist-server/engine/state-axes.d.ts +1 -1
- package/dist-server/engine/state-axes.js +2 -2
- package/dist-server/engine/state-axes.js.map +1 -1
- package/dist-server/engine/travel-estimator.d.ts +2 -2
- package/dist-server/engine/travel-estimator.js +1 -1
- package/dist-server/engine/travel-estimator.js.map +1 -1
- package/dist-server/engine/twin-engine.d.ts +243 -53
- package/dist-server/engine/twin-engine.js +690 -191
- package/dist-server/engine/twin-engine.js.map +1 -1
- package/dist-server/engine/warm-start.d.ts +2 -2
- package/dist-server/engine/warm-start.js +5 -5
- package/dist-server/engine/warm-start.js.map +1 -1
- package/dist-server/index.js +1 -1
- package/dist-server/index.js.map +1 -1
- package/dist-server/migrations/1786000000000-RenameTwinInstanceBoardToModel.js +1 -1
- package/dist-server/migrations/1786000000000-RenameTwinInstanceBoardToModel.js.map +1 -1
- package/dist-server/migrations/1786100000000-PromoteEventActionAndSyncWarnings.js +3 -3
- package/dist-server/migrations/1786100000000-PromoteEventActionAndSyncWarnings.js.map +1 -1
- package/dist-server/migrations/1786200000000-CarryLiveFeedCursor.js +2 -2
- package/dist-server/migrations/1786200000000-CarryLiveFeedCursor.js.map +1 -1
- package/dist-server/migrations/1786300000000-IndexEventTypeByTime.d.ts +5 -0
- package/dist-server/migrations/1786300000000-IndexEventTypeByTime.js +54 -0
- package/dist-server/migrations/1786300000000-IndexEventTypeByTime.js.map +1 -0
- package/dist-server/migrations/1786400000000-IndexEventCreatedAt.d.ts +5 -0
- package/dist-server/migrations/1786400000000-IndexEventCreatedAt.js +44 -0
- package/dist-server/migrations/1786400000000-IndexEventCreatedAt.js.map +1 -0
- package/dist-server/migrations/1786500000000-CreateTwinSubjectEvents.d.ts +6 -0
- package/dist-server/migrations/1786500000000-CreateTwinSubjectEvents.js +84 -0
- package/dist-server/migrations/1786500000000-CreateTwinSubjectEvents.js.map +1 -0
- package/dist-server/migrations/index.js +7 -1
- package/dist-server/migrations/index.js.map +1 -1
- package/dist-server/routes.d.ts +1 -0
- package/dist-server/routes.js +69 -5
- package/dist-server/routes.js.map +1 -1
- package/dist-server/service/index.d.ts +2 -2
- package/dist-server/service/index.js +27 -24
- package/dist-server/service/index.js.map +1 -1
- package/dist-server/service/reference/control-routing.d.ts +1 -1
- package/dist-server/service/reference/control-routing.js +1 -1
- package/dist-server/service/reference/control-routing.js.map +1 -1
- package/dist-server/service/reference/discovery-result.d.ts +2 -2
- package/dist-server/service/reference/discovery-result.js +3 -3
- package/dist-server/service/reference/discovery-result.js.map +1 -1
- package/dist-server/service/reference/hook-contract.d.ts +43 -0
- package/dist-server/service/reference/hook-contract.js +59 -0
- package/dist-server/service/reference/hook-contract.js.map +1 -0
- package/dist-server/service/reference/ingest-space.d.ts +1 -1
- package/dist-server/service/reference/ingest-space.js +4 -4
- package/dist-server/service/reference/ingest-space.js.map +1 -1
- package/dist-server/service/reference/knob-defaults.d.ts +1 -1
- package/dist-server/service/reference/knob-defaults.js +4 -4
- package/dist-server/service/reference/knob-defaults.js.map +1 -1
- package/dist-server/service/reference/reference-adapter.d.ts +140 -8
- package/dist-server/service/reference/reference-adapter.js +27 -0
- package/dist-server/service/reference/reference-adapter.js.map +1 -1
- package/dist-server/service/reference/reference-hook.d.ts +20 -0
- package/dist-server/service/reference/reference-hook.js +133 -0
- package/dist-server/service/reference/reference-hook.js.map +1 -0
- package/dist-server/service/reference/reference-live.d.ts +2 -2
- package/dist-server/service/reference/reference-live.js +88 -13
- package/dist-server/service/reference/reference-live.js.map +1 -1
- package/dist-server/service/reference/reference-master.d.ts +14 -14
- package/dist-server/service/reference/reference-master.js +34 -12
- package/dist-server/service/reference/reference-master.js.map +1 -1
- package/dist-server/service/reference/reference-resolver.d.ts +1 -1
- package/dist-server/service/reference/reference-resolver.js +10 -10
- package/dist-server/service/reference/reference-resolver.js.map +1 -1
- package/dist-server/service/reference/twin-reference.d.ts +3 -3
- package/dist-server/service/reference/twin-reference.js.map +1 -1
- package/dist-server/service/twin-attention/twin-attention-query.d.ts +1 -1
- package/dist-server/service/twin-attention/twin-attention-query.js +1 -1
- package/dist-server/service/twin-attention/twin-attention-query.js.map +1 -1
- package/dist-server/service/twin-audit/command-audit.d.ts +1 -1
- package/dist-server/service/twin-audit/command-audit.js.map +1 -1
- package/dist-server/service/twin-audit/twin-audit-event.d.ts +1 -1
- package/dist-server/service/twin-audit/twin-audit-event.js.map +1 -1
- package/dist-server/service/twin-audit/twin-audit-query.js +1 -1
- package/dist-server/service/twin-audit/twin-audit-query.js.map +1 -1
- package/dist-server/service/twin-control/twin-control-mutation.js +1 -1
- package/dist-server/service/twin-control/twin-control-mutation.js.map +1 -1
- package/dist-server/service/twin-event/twin-event-keys.d.ts +22 -2
- package/dist-server/service/twin-event/twin-event-keys.js +37 -5
- package/dist-server/service/twin-event/twin-event-keys.js.map +1 -1
- package/dist-server/service/twin-event/twin-event-type.d.ts +1 -0
- package/dist-server/service/twin-event/twin-event-type.js +8 -1
- package/dist-server/service/twin-event/twin-event-type.js.map +1 -1
- package/dist-server/service/twin-event/twin-event.d.ts +2 -2
- package/dist-server/service/twin-event/twin-event.js +32 -4
- package/dist-server/service/twin-event/twin-event.js.map +1 -1
- package/dist-server/service/twin-forecast/forecast-metrics.js +1 -1
- package/dist-server/service/twin-forecast/forecast-metrics.js.map +1 -1
- package/dist-server/service/twin-forecast/gap-analytics.d.ts +1 -1
- package/dist-server/service/twin-forecast/gap-analytics.js.map +1 -1
- package/dist-server/service/twin-forecast/twin-forecast-query.js +29 -11
- package/dist-server/service/twin-forecast/twin-forecast-query.js.map +1 -1
- package/dist-server/service/twin-ingest-window/twin-ingest-window-query.d.ts +1 -1
- package/dist-server/service/twin-ingest-window/twin-ingest-window-query.js +1 -1
- package/dist-server/service/twin-ingest-window/twin-ingest-window-query.js.map +1 -1
- package/dist-server/service/twin-ingest-window/twin-ingest-window-writer.js +1 -1
- package/dist-server/service/twin-ingest-window/twin-ingest-window-writer.js.map +1 -1
- package/dist-server/service/twin-instance/twin-instance.js.map +1 -1
- package/dist-server/service/twin-journal/event-type-split-shape.d.ts +50 -0
- package/dist-server/service/twin-journal/event-type-split-shape.js +111 -0
- package/dist-server/service/twin-journal/event-type-split-shape.js.map +1 -0
- package/dist-server/service/twin-journal/event-type-split.d.ts +15 -0
- package/dist-server/service/twin-journal/event-type-split.js +102 -0
- package/dist-server/service/twin-journal/event-type-split.js.map +1 -0
- package/dist-server/service/twin-journal/journal-order.d.ts +15 -0
- package/dist-server/service/twin-journal/journal-order.js +17 -0
- package/dist-server/service/twin-journal/journal-order.js.map +1 -0
- package/dist-server/service/twin-journal/twin-journal-query.d.ts +5 -5
- package/dist-server/service/twin-journal/twin-journal-query.js +232 -21
- package/dist-server/service/twin-journal/twin-journal-query.js.map +1 -1
- package/dist-server/service/twin-lifecycle/twin-lifecycle-mutation.d.ts +27 -1
- package/dist-server/service/twin-lifecycle/twin-lifecycle-mutation.js +59 -3
- package/dist-server/service/twin-lifecycle/twin-lifecycle-mutation.js.map +1 -1
- package/dist-server/service/twin-metrics/twin-metrics-query.js +14 -1
- package/dist-server/service/twin-metrics/twin-metrics-query.js.map +1 -1
- package/dist-server/service/twin-model/axis-journal-evidence.js +1 -1
- package/dist-server/service/twin-model/axis-journal-evidence.js.map +1 -1
- package/dist-server/service/twin-model/epcis-coverage.js +36 -1
- package/dist-server/service/twin-model/epcis-coverage.js.map +1 -1
- package/dist-server/service/twin-model/iec61850-coverage.js +1 -1
- package/dist-server/service/twin-model/iec61850-coverage.js.map +1 -1
- package/dist-server/service/twin-model/isa95-coverage.d.ts +15 -2
- package/dist-server/service/twin-model/isa95-coverage.js +32 -8
- package/dist-server/service/twin-model/isa95-coverage.js.map +1 -1
- package/dist-server/service/twin-model/item-ref.js +2 -2
- package/dist-server/service/twin-model/item-ref.js.map +1 -1
- package/dist-server/service/twin-model/name-index.d.ts +1 -1
- package/dist-server/service/twin-model/name-index.js +1 -1
- package/dist-server/service/twin-model/name-index.js.map +1 -1
- package/dist-server/service/twin-model/project-structure.d.ts +1 -1
- package/dist-server/service/twin-model/project-structure.js +3 -3
- package/dist-server/service/twin-model/project-structure.js.map +1 -1
- package/dist-server/service/twin-model/twin-equipment.d.ts +1 -1
- package/dist-server/service/twin-model/twin-equipment.js +1 -1
- package/dist-server/service/twin-model/twin-equipment.js.map +1 -1
- package/dist-server/service/twin-model/twin-lineage-query.js +16 -5
- package/dist-server/service/twin-model/twin-lineage-query.js.map +1 -1
- package/dist-server/service/twin-model/twin-location.d.ts +1 -1
- package/dist-server/service/twin-model/twin-location.js.map +1 -1
- package/dist-server/service/twin-model/twin-model-item-query.d.ts +3 -3
- package/dist-server/service/twin-model/twin-model-item-query.js +14 -14
- package/dist-server/service/twin-model/twin-model-item-query.js.map +1 -1
- package/dist-server/service/twin-model/twin-model-mutation.d.ts +2 -2
- package/dist-server/service/twin-model/twin-model-mutation.js +4 -4
- package/dist-server/service/twin-model/twin-model-mutation.js.map +1 -1
- package/dist-server/service/twin-model/twin-model-query.js +117 -28
- package/dist-server/service/twin-model/twin-model-query.js.map +1 -1
- package/dist-server/service/twin-model/twin-model-tree-query.js +22 -0
- package/dist-server/service/twin-model/twin-model-tree-query.js.map +1 -1
- package/dist-server/service/twin-model/twin-operation.d.ts +1 -1
- package/dist-server/service/twin-model/twin-operation.js +1 -1
- package/dist-server/service/twin-model/twin-operation.js.map +1 -1
- package/dist-server/service/twin-space/move-space.js +2 -2
- package/dist-server/service/twin-space/move-space.js.map +1 -1
- package/dist-server/service/twin-space/representation-role.d.ts +2 -0
- package/dist-server/service/twin-space/representation-role.js +18 -0
- package/dist-server/service/twin-space/representation-role.js.map +1 -0
- package/dist-server/service/twin-space/space-integrity.d.ts +1 -1
- package/dist-server/service/twin-space/space-integrity.js.map +1 -1
- package/dist-server/service/twin-space/twin-space-representation.d.ts +2 -0
- package/dist-server/service/twin-space/twin-space-representation.js +16 -0
- package/dist-server/service/twin-space/twin-space-representation.js.map +1 -1
- package/dist-server/service/twin-space/twin-space-resolver.d.ts +2 -2
- package/dist-server/service/twin-space/twin-space-resolver.js +36 -7
- package/dist-server/service/twin-space/twin-space-resolver.js.map +1 -1
- package/dist-server/service/twin-structure/twin-structure.js +3 -3
- package/dist-server/service/twin-structure/twin-structure.js.map +1 -1
- package/dist-server/service/twin-subject/event-subjects.d.ts +43 -0
- package/dist-server/service/twin-subject/event-subjects.js +137 -0
- package/dist-server/service/twin-subject/event-subjects.js.map +1 -0
- package/dist-server/service/twin-subject/index.d.ts +9 -0
- package/dist-server/service/twin-subject/index.js +16 -0
- package/dist-server/service/twin-subject/index.js.map +1 -0
- package/dist-server/service/twin-subject/item-resolver.d.ts +10 -0
- package/dist-server/service/twin-subject/item-resolver.js +93 -0
- package/dist-server/service/twin-subject/item-resolver.js.map +1 -0
- package/dist-server/service/twin-subject/subject-query-shape.d.ts +22 -0
- package/dist-server/service/twin-subject/subject-query-shape.js +88 -0
- package/dist-server/service/twin-subject/subject-query-shape.js.map +1 -0
- package/dist-server/service/twin-subject/twin-subject-backfill.d.ts +23 -0
- package/dist-server/service/twin-subject/twin-subject-backfill.js +159 -0
- package/dist-server/service/twin-subject/twin-subject-backfill.js.map +1 -0
- package/dist-server/service/twin-subject/twin-subject-event.d.ts +14 -0
- package/dist-server/service/twin-subject/twin-subject-event.js +111 -0
- package/dist-server/service/twin-subject/twin-subject-event.js.map +1 -0
- package/dist-server/service/twin-subject/twin-subject-fast-path.d.ts +39 -0
- package/dist-server/service/twin-subject/twin-subject-fast-path.js +295 -0
- package/dist-server/service/twin-subject/twin-subject-fast-path.js.map +1 -0
- package/dist-server/service/twin-subject/twin-subject-query.d.ts +27 -0
- package/dist-server/service/twin-subject/twin-subject-query.js +232 -0
- package/dist-server/service/twin-subject/twin-subject-query.js.map +1 -0
- package/dist-server/service/twin-subject/twin-subject-writer.d.ts +27 -0
- package/dist-server/service/twin-subject/twin-subject-writer.js +166 -0
- package/dist-server/service/twin-subject/twin-subject-writer.js.map +1 -0
- package/dist-server/service/twin-target/twin-target-resolver.js +1 -1
- package/dist-server/service/twin-target/twin-target-resolver.js.map +1 -1
- package/dist-shared/entity-delta.d.ts +1 -1
- package/dist-shared/entity-delta.js +3 -3
- package/dist-shared/entity-delta.js.map +1 -1
- package/dist-shared/touched-items.js +2 -2
- package/dist-shared/touched-items.js.map +1 -1
- package/dist-shared/twin-level.js +1 -1
- package/dist-shared/twin-level.js.map +1 -1
- package/package.json +3 -3
- package/server/engine/attention-digest.ts +9 -9
- package/server/engine/canonical-ingest.ts +170 -13
- package/server/engine/command-routing.ts +1 -1
- package/server/engine/declared-stimulus.ts +1 -1
- package/server/engine/energy-topology.ts +1 -1
- package/server/engine/ingest-dedupe.ts +149 -0
- package/server/engine/ingest-health.ts +234 -20
- package/server/engine/integration-probes.ts +7 -7
- package/server/engine/kpi-baseline.ts +4 -4
- package/server/engine/kpi-fold.ts +395 -27
- package/server/engine/kpi-query.ts +212 -46
- package/server/engine/live-feed-registry.ts +1 -1
- package/server/engine/load-meter.ts +1 -1
- package/server/engine/local-declarations.ts +13 -13
- package/server/engine/log.ts +1 -1
- package/server/engine/measured-estimator.ts +1 -1
- package/server/engine/measured-yield.ts +1 -1
- package/server/engine/model-basis.ts +2 -2
- package/server/engine/model-gap.ts +3 -3
- package/server/engine/model-vocabulary.ts +1 -1
- package/server/engine/oee-accumulator.ts +4 -4
- package/server/engine/property-effects.ts +2 -2
- package/server/engine/read-time.ts +77 -0
- package/server/engine/restart-policy.ts +1 -1
- package/server/engine/runtime-key.ts +1 -1
- package/server/engine/spec-coverage.ts +3 -3
- package/server/engine/stage-path.ts +5 -5
- package/server/engine/state-axes.ts +2 -2
- package/server/engine/travel-estimator.ts +2 -2
- package/server/engine/twin-engine.ts +764 -201
- package/server/engine/warm-start.ts +6 -6
- package/server/index.ts +1 -1
- package/server/migrations/1786000000000-RenameTwinInstanceBoardToModel.ts +1 -1
- package/server/migrations/1786100000000-PromoteEventActionAndSyncWarnings.ts +3 -3
- package/server/migrations/1786200000000-CarryLiveFeedCursor.ts +2 -2
- package/server/migrations/1786300000000-IndexEventTypeByTime.ts +48 -0
- package/server/migrations/1786400000000-IndexEventCreatedAt.ts +38 -0
- package/server/migrations/1786500000000-CreateTwinSubjectEvents.ts +93 -0
- package/server/migrations/index.ts +7 -1
- package/server/routes.ts +68 -5
- package/server/service/index.ts +4 -1
- package/server/service/reference/control-routing.ts +1 -1
- package/server/service/reference/discovery-result.ts +3 -3
- package/server/service/reference/hook-contract.ts +103 -0
- package/server/service/reference/ingest-space.ts +4 -4
- package/server/service/reference/knob-defaults.ts +4 -4
- package/server/service/reference/reference-adapter.ts +166 -9
- package/server/service/reference/reference-hook.ts +145 -0
- package/server/service/reference/reference-live.ts +92 -14
- package/server/service/reference/reference-master.ts +39 -17
- package/server/service/reference/reference-resolver.ts +10 -10
- package/server/service/reference/twin-reference.ts +3 -3
- package/server/service/twin-attention/twin-attention-query.ts +1 -1
- package/server/service/twin-audit/command-audit.ts +1 -1
- package/server/service/twin-audit/twin-audit-event.ts +2 -2
- package/server/service/twin-audit/twin-audit-query.ts +1 -1
- package/server/service/twin-control/twin-control-mutation.ts +1 -1
- package/server/service/twin-event/twin-event-keys.ts +37 -5
- package/server/service/twin-event/twin-event-type.ts +18 -1
- package/server/service/twin-event/twin-event.ts +32 -6
- package/server/service/twin-forecast/forecast-metrics.ts +1 -1
- package/server/service/twin-forecast/gap-analytics.ts +1 -1
- package/server/service/twin-forecast/twin-forecast-query.ts +30 -11
- package/server/service/twin-ingest-window/twin-ingest-window-query.ts +1 -1
- package/server/service/twin-ingest-window/twin-ingest-window-writer.ts +1 -1
- package/server/service/twin-instance/twin-instance.ts +4 -4
- package/server/service/twin-journal/event-type-split-shape.ts +112 -0
- package/server/service/twin-journal/event-type-split.ts +144 -0
- package/server/service/twin-journal/journal-order.ts +20 -0
- package/server/service/twin-journal/twin-journal-query.ts +224 -20
- package/server/service/twin-lifecycle/twin-lifecycle-mutation.ts +57 -3
- package/server/service/twin-metrics/twin-metrics-query.ts +14 -1
- package/server/service/twin-model/axis-journal-evidence.ts +1 -1
- package/server/service/twin-model/epcis-coverage.ts +36 -1
- package/server/service/twin-model/iec61850-coverage.ts +1 -1
- package/server/service/twin-model/isa95-coverage.ts +45 -8
- package/server/service/twin-model/item-ref.ts +2 -2
- package/server/service/twin-model/name-index.ts +1 -1
- package/server/service/twin-model/project-structure.ts +4 -4
- package/server/service/twin-model/twin-equipment.ts +2 -2
- package/server/service/twin-model/twin-lineage-query.ts +16 -5
- package/server/service/twin-model/twin-location.ts +1 -1
- package/server/service/twin-model/twin-model-item-query.ts +14 -14
- package/server/service/twin-model/twin-model-mutation.ts +4 -4
- package/server/service/twin-model/twin-model-query.ts +117 -28
- package/server/service/twin-model/twin-model-tree-query.ts +25 -1
- package/server/service/twin-model/twin-operation.ts +2 -2
- package/server/service/twin-space/move-space.ts +3 -3
- package/server/service/twin-space/representation-role.ts +15 -0
- package/server/service/twin-space/space-integrity.ts +1 -1
- package/server/service/twin-space/twin-space-representation.ts +37 -0
- package/server/service/twin-space/twin-space-resolver.ts +39 -7
- package/server/service/twin-structure/twin-structure.ts +3 -3
- package/server/service/twin-subject/event-subjects.ts +155 -0
- package/server/service/twin-subject/index.ts +10 -0
- package/server/service/twin-subject/item-resolver.ts +92 -0
- package/server/service/twin-subject/subject-query-shape.ts +84 -0
- package/server/service/twin-subject/twin-subject-backfill.ts +196 -0
- package/server/service/twin-subject/twin-subject-event.ts +101 -0
- package/server/service/twin-subject/twin-subject-fast-path.ts +346 -0
- package/server/service/twin-subject/twin-subject-query.ts +210 -0
- package/server/service/twin-subject/twin-subject-writer.ts +189 -0
- package/server/service/twin-target/twin-target-resolver.ts +1 -1
- package/shared/entity-delta.ts +3 -3
- package/shared/touched-items.ts +2 -2
- package/shared/twin-level.ts +1 -1
- package/test/adopt-structure-live.test.ts +3 -3
- package/test/aggregate-not-findone.test.ts +112 -0
- package/test/attention-digest.test.ts +6 -6
- package/test/axis-read.test.ts +2 -2
- package/test/boot-resume.test.ts +20 -7
- package/test/broadcast-period.test.ts +2 -2
- package/test/broadcast-queue-overflow.test.ts +244 -0
- package/test/canonical-ingest-vocabularies.test.ts +179 -7
- package/test/canonical-quantity-door.test.ts +2 -2
- package/test/checkpoint-refuses-empty.test.ts +2 -2
- package/test/connector-capability-declaration.test.ts +88 -0
- package/test/control-capability.test.ts +1 -1
- package/test/cursor-stall-not-read-failure.test.ts +2 -2
- package/test/declaration-reaches-model.test.ts +5 -5
- package/test/declared-stimulus.test.ts +4 -4
- package/test/discovery-result.test.ts +2 -2
- package/test/entity-delta.test.ts +4 -4
- package/test/event-subjects.test.ts +147 -0
- package/test/event-time-column.test.ts +3 -3
- package/test/event-type-split.test.ts +155 -0
- package/test/fold-gap-speaks.test.ts +53 -0
- package/test/forecast-metrics.test.ts +1 -1
- package/test/forecast-tuning.test.ts +1 -1
- package/test/ingest-health-engine.test.ts +2 -2
- package/test/ingest-health-wiring.test.ts +7 -5
- package/test/ingest-health.test.ts +6 -6
- package/test/ingest-history.test.ts +2 -2
- package/test/ingest-idempotent.test.ts +132 -0
- package/test/ingest-space.test.ts +3 -3
- package/test/integration-probes.test.ts +3 -3
- package/test/integration-runner.test.ts +1 -1
- package/test/item-ref.test.ts +1 -1
- package/test/journal-order-axis.test.ts +80 -0
- package/test/journal-read-discipline.test.ts +16 -6
- package/test/journal-retention.test.ts +171 -29
- package/test/journal-sort-axis.test.ts +1 -1
- package/test/journal-write-door.test.ts +3 -3
- package/test/journal-write-trend.test.ts +1 -1
- package/test/kernel-kind-guard.test.ts +3 -3
- package/test/knob-defaults.test.ts +1 -1
- package/test/kpi-baseline-db.test.ts +2 -2
- package/test/kpi-fold.test.ts +185 -14
- package/test/kpi-query-bench.test.ts +3 -3
- package/test/live-cursor-wiring.test.ts +6 -6
- package/test/live-kernel-facts.test.ts +11 -11
- package/test/live-mirror-parity.test.ts +3 -3
- package/test/load-meter.test.ts +1 -1
- package/test/local-declarations.test.ts +10 -10
- package/test/log-stamp.test.ts +1 -1
- package/test/measured-yield.test.ts +2 -2
- package/test/merge-streams.test.ts +70 -0
- package/test/mirror-resumes-from-checkpoint.test.ts +13 -13
- package/test/model-basis.test.ts +2 -2
- package/test/model-gap.test.ts +4 -4
- package/test/model-vocabulary.test.ts +2 -2
- package/test/move-space.test.ts +1 -1
- package/test/oee-accumulator.test.ts +2 -2
- package/test/operational-vocabulary.test.ts +2 -2
- package/test/project-structure-db.test.ts +2 -2
- package/test/projection-reaches-screen.test.ts +2 -2
- package/test/property-effects.test.ts +3 -3
- package/test/read-failure-ledger.test.ts +106 -0
- package/test/read-failure-visible.test.ts +7 -7
- package/test/read-time.test.ts +118 -0
- package/test/reference-grounding.test.ts +1 -1
- package/test/reference-hook.test.ts +86 -0
- package/test/registry-key-guard.test.ts +1 -1
- package/test/rename-twin.test.ts +81 -0
- package/test/restart-policy.test.ts +3 -3
- package/test/resync-origin-site.test.ts +3 -3
- package/test/revision-axis.test.ts +14 -8
- package/test/runtime-key.test.ts +3 -3
- package/test/snapshot-freshness.test.ts +4 -4
- package/test/source-outcome-audit.test.ts +1 -1
- package/test/space-integrity.test.ts +1 -1
- package/test/space-representation.test.ts +66 -0
- package/test/space-scope-includes-stopped.test.ts +55 -0
- package/test/spec-coverage.test.ts +1 -1
- package/test/standard-coverage.test.ts +43 -1
- package/test/status-tally.test.ts +1 -1
- package/test/structure-revision-db.test.ts +7 -7
- package/test/subject-fast-path.test.ts +148 -0
- package/test/subject-read-model.test.ts +175 -0
- package/test/touched-items.test.ts +2 -2
- package/test/twin-audit.test.ts +1 -1
- package/test/twin-event-keys.test.ts +49 -3
- package/test/twin-model-item-db.test.ts +5 -5
- package/test/twin-model-tree-db.test.ts +35 -2
- package/test/twin-origin-resync.test.ts +1 -1
- package/test/warm-start-seam.test.ts +1 -1
- package/test/warm-start.test.ts +6 -6
- package/test/withheld-door.test.ts +95 -0
- package/test/yield-loop.test.ts +4 -4
- package/tsconfig.shared.tsbuildinfo +1 -1
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { twinLog } from '../../engine/log.js'
|
|
2
|
+
import { hydratedDate } from '../../engine/read-time.js'
|
|
2
3
|
import { Arg, Ctx, Int, Mutation, Query, Resolver, Directive} from 'type-graphql'
|
|
3
4
|
|
|
4
5
|
import { getRepository, ScalarObject } from '@things-factory/shell'
|
|
@@ -80,13 +81,31 @@ const calKey = (d: string, i: string, m: string, h: number) => `${d}:${i}:${m}:$
|
|
|
80
81
|
* 지평 H 만큼 앞을 봐야 "그때 예측한 것"과 "실제로 벌어진 것"을 맞대 볼 수 있으므로 끝에서 H 를 뺀다.
|
|
81
82
|
*/
|
|
82
83
|
async function gapWindow(domainId: string, instanceId: string, H: number): Promise<{ t0: number; lastVantage: number } | null> {
|
|
83
|
-
|
|
84
|
+
/*
|
|
85
|
+
* ── **양 끝을 집계로 묻는다** (2026-08-28) ──────────────────────────────────
|
|
86
|
+
*
|
|
87
|
+
* 예전에는 `findOne({ where: { domain: { id } }, order: { eventTime } })` 두 번이었다. 관계를 가진
|
|
88
|
+
* 엔티티에서 `findOne` 은 두 단계 질의가 되고 **안쪽에 상한이 없다** — 그 트윈의 저널을 전부 만든 뒤
|
|
89
|
+
* 한 줄을 고른다. 승화푸드는 337만 행이고, 그것을 두 번 했다.
|
|
90
|
+
*
|
|
91
|
+
* `MIN`/`MAX` 는 `(domain, instance, event_time)` 색인의 양 끝을 집으므로 각각 한 줄이다.
|
|
92
|
+
* 이 파일의 다른 자리와 같은 규율이다(§`twin-engine` 의 `tipOf`).
|
|
93
|
+
*/
|
|
84
94
|
const repo = getRepository(TwinEvent)
|
|
85
|
-
const
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
95
|
+
const edge = (agg: 'MIN' | 'MAX') =>
|
|
96
|
+
repo
|
|
97
|
+
.createQueryBuilder('e')
|
|
98
|
+
.select(`${agg}(e.eventTime)`, 'at')
|
|
99
|
+
.where('e.domain = :domainId', { domainId })
|
|
100
|
+
.andWhere('e.instanceId = :instanceId', { instanceId })
|
|
101
|
+
.getRawOne<{ at: unknown }>()
|
|
102
|
+
const [firstRow, lastRow] = await Promise.all([edge('MIN'), edge('MAX')])
|
|
103
|
+
/* 드라이버가 준 값은 시간대가 없을 수 있다 — 컬럼 변환을 지나게 한다(§`hydratedDate`). */
|
|
104
|
+
const first = hydratedDate(repo, 'eventTime', firstRow?.at)
|
|
105
|
+
const last = hydratedDate(repo, 'eventTime', lastRow?.at)
|
|
106
|
+
if (!first || !last) return null
|
|
107
|
+
const t0 = first.getTime()
|
|
108
|
+
const lastVantage = last.getTime() - H
|
|
90
109
|
return lastVantage > t0 ? { t0, lastVantage } : null
|
|
91
110
|
}
|
|
92
111
|
|
|
@@ -188,7 +207,7 @@ export class TwinForecastQuery {
|
|
|
188
207
|
* **배운 모델에만 적용한다.** 보정은 "그때 모델이 이만큼 치우쳐 있다" 를 배운 것이다. 소요시간
|
|
189
208
|
* 추정기(실측·거리)나 공정 명세가 바뀌어 모델이 달라지면 그 편향은 더 이상 사실이 아니고, 그대로
|
|
190
209
|
* 밀어 주면 이중 보정이 된다. 그래서 지문을 비교하고, 다르면 적용하지 않되 **그 사실을 응답에
|
|
191
|
-
* 싣는다** —
|
|
210
|
+
* 싣는다** — 오류 없이 사라지면 사용자는 학습이 왜 안 듣는지 알 수 없다(다시 배우면 된다).
|
|
192
211
|
* 지문을 알 수 없을 때는 비교하지 않는다(모르는 것을 근거로 보정을 끊지 않는다).
|
|
193
212
|
*/
|
|
194
213
|
const basis = await TwinEngine.modelBasis(domainId, instanceId)
|
|
@@ -312,7 +331,7 @@ export class TwinForecastQuery {
|
|
|
312
331
|
/*
|
|
313
332
|
* **가정이 어떻게 됐나** — 걸린 개입·거절된 개입·덮어쓴 선언.
|
|
314
333
|
*
|
|
315
|
-
* 이것을 감추면 두 가지가
|
|
334
|
+
* 이것을 감추면 두 가지가 들어온 것이 없어진다: 걸리지 않은 가정(「걸었는데 왜 안 바뀌나」)과, 덮어쓴
|
|
316
335
|
* 선언(사용자가 what-if 결과를 트윈의 현재 상태로 읽는다). 실제로 실행한 fork(`fc`)에서 읽는다 —
|
|
317
336
|
* 가정을 겪은 것은 그쪽이다.
|
|
318
337
|
*/
|
|
@@ -342,10 +361,10 @@ export class TwinForecastQuery {
|
|
|
342
361
|
*
|
|
343
362
|
* 그런데 그 카운터는 **fork 안에** 있고 fork 는 버려진다. 여기서 싣지 않으면 아무에게도 닿지
|
|
344
363
|
* 않는다 — 리듀서가 오래 세어 온 값을 스냅샷이 떨어뜨려 그것을 읽도록 쓰인 검사가 영원히
|
|
345
|
-
*
|
|
364
|
+
* 아무 신호도 내지 않았던 것과 **같은 실수**다. 세어 놓고 내보내지 않으면 안 센 것과 같다.
|
|
346
365
|
*
|
|
347
366
|
* 왜 화면이 이것을 알아야 하나: 계보는 **회수 범위**를 정하는 사슬이다. 구멍이 있는데 온전한
|
|
348
|
-
* 것처럼 보이면 회수가
|
|
367
|
+
* 것처럼 보이면 회수가 알리지 않고 좁아진다. 식품에서 그것은 되돌릴 수 없다.
|
|
349
368
|
*
|
|
350
369
|
* 두 곳에서 읽어 **큰 값**을 낸다 — 분포(회차 R개의 최대)와 궤적 fork 는 **둘 다 실제로 실행됐다**.
|
|
351
370
|
* 한쪽만 보면 다른 쪽이 겪은 구멍을 잃는다.
|
|
@@ -394,7 +413,7 @@ export class TwinForecastQuery {
|
|
|
394
413
|
* 시각이 되어, 합산이 다른 시점의 값을 더한다(`aggregatePoints` 주석 참조).
|
|
395
414
|
*
|
|
396
415
|
* 저널이 없거나 지평 H 를 담을 만큼 길지 않은 트윈은 **백테스트에 참여할 수 없다.** 그런 트윈은
|
|
397
|
-
* 빼되 누구를 뺐는지 돌려준다 —
|
|
416
|
+
* 빼되 누구를 뺐는지 돌려준다 — 알리지 않고 빼면 화면은 그 트윈까지 포함한 공장 총량으로 읽는다.
|
|
398
417
|
*/
|
|
399
418
|
const windows = await Promise.all(ids.map(async id => ({ id, w: await gapWindow(domainId, id, H) })))
|
|
400
419
|
const usable = windows.filter(x => x.w)
|
|
@@ -29,7 +29,7 @@ import { TwinIngestWindow } from './twin-ingest-window.js'
|
|
|
29
29
|
* 한 번에 돌려주는 창 수 상한 — 144창 × 10분 = 24시간.
|
|
30
30
|
*
|
|
31
31
|
* 상한이 필요한 이유는 이 표가 **자란다**는 것이다(트윈 1,000개에 하루 14.4만 행). 상한에서 밀려난
|
|
32
|
-
* 수는 **세어서 낸다** —
|
|
32
|
+
* 수는 **세어서 낸다** — 알리지 않고 자르면 화면이 「그 앞에는 아무 일도 없었다」로 읽는다.
|
|
33
33
|
*/
|
|
34
34
|
export const HISTORY_WINDOW_LIMIT = 144
|
|
35
35
|
|
|
@@ -11,7 +11,7 @@ import { TwinIngestWindow } from './twin-ingest-window.js'
|
|
|
11
11
|
* 두었고(`onIngestWindowClosed`), 이 파일이 부팅에서 한 번 등록한다. 그래서 기록 경로가 갈리지 않는다.
|
|
12
12
|
*
|
|
13
13
|
* ── 실패를 삼키지 않는다 ────────────────────────────────────────────────────
|
|
14
|
-
* 쓰기가 터져도 메모리 장부는 계속 굴러간다(`rollIngestWindow` 가 훅을 감싼다). 다만
|
|
14
|
+
* 쓰기가 터져도 메모리 장부는 계속 굴러간다(`rollIngestWindow` 가 훅을 감싼다). 다만 **알리지 않고 넘기지는
|
|
15
15
|
* 않는다** — 실패를 로그로 남긴다. 그러지 않으면 추이 표에 구멍이 생긴 이유를 나중에 알 수 없다.
|
|
16
16
|
*
|
|
17
17
|
* ── 왜 await 하지 않나 ──────────────────────────────────────────────────────
|
|
@@ -73,7 +73,7 @@ export class TwinInstance {
|
|
|
73
73
|
* 하나**였고, 그것은 원본의 성질이 아니라 **트윈과 자기 과거의 관계**다: 같은 시뮬레이터 원본을 보는 두
|
|
74
74
|
* 트윈이 하나는 이어가고 하나는 매번 처음부터 재현할 수 있다. 그래서 축은 남고 이름만 바뀌었다.
|
|
75
75
|
*
|
|
76
|
-
* **비어 있을 수 없고, 기본값도 없다.** 예전에는 미선언이
|
|
76
|
+
* **비어 있을 수 없고, 기본값도 없다.** 예전에는 미선언이 알리지 않고 `sim-experiment`(=저널 초기화)로
|
|
77
77
|
* 떨어졌다 — 값이 하나 어긋나는 순간 이력을 지우는 길이다. 이제 읽는 쪽이 **오류를 낸다**
|
|
78
78
|
* (`engine/restart-policy.ts`). 선언은 프로비저닝하는 쪽의 몫이다.
|
|
79
79
|
*/
|
|
@@ -83,7 +83,7 @@ export class TwinInstance {
|
|
|
83
83
|
|
|
84
84
|
/* 읽을 때 어휘 세대를 흡수한다(`nodes`/`movers`/`equipmentList` → `locations`/`equipment`). (vocabulary-guard: allow — 세대 이름 없이는 설명할 수 없다.)
|
|
85
85
|
자리마다 리더를 쓰게 하면 다음에 추가되는 자리가 또 빠지고, 빠진 것은 오류가 아니라 **빈 공장**이라
|
|
86
|
-
|
|
86
|
+
들어온 것이 없다(2026-08-06: 모델러 엔티티 목록이 통째로 비었다). 상세 `engine/model-vocabulary.ts`. */
|
|
87
87
|
@Column({ type: 'simple-json', nullable: true, transformer: modelColumnTransformer })
|
|
88
88
|
@Field(type => ScalarObject, { nullable: true, description: 'TwinModelDef blueprint (locations/equipment) required to replay events into state. Older stored boards used nodes/movers/equipmentList; they are normalised on read so consumers see one vocabulary.' }) // vocabulary-guard: allow — 옛 어휘를 흡수하는 것이 이 코드의 일이다(세대 이름을 적어야 흡수할 수 있다)
|
|
89
89
|
model?: any
|
|
@@ -92,7 +92,7 @@ export class TwinInstance {
|
|
|
92
92
|
* 로컬 선언 겹 — **구조는 원천이, 수는 우리가.**
|
|
93
93
|
*
|
|
94
94
|
* 요금 단가·계약 정보·축전지 충전율처럼 원 시스템이 모르는 값을 넣는 자리다. `model` 에 직접 쓰지
|
|
95
|
-
* 않는 이유는 그것이 **원천의 사본**이기 때문이다: 다시 읽기가 사본을 새로 그리면 직접 쓴 값은
|
|
95
|
+
* 않는 이유는 그것이 **원천의 사본**이기 때문이다: 다시 읽기가 사본을 새로 그리면 직접 쓴 값은 알리지 않고
|
|
96
96
|
* 사라진다(요금이 사라지면 금액도 사라지고, 사용자는 이유를 알 수 없다).
|
|
97
97
|
*
|
|
98
98
|
* 그래서 여기 남기고, 모델을 그릴 때마다 그 위에 얹는다(`engine/local-declarations`). 얹힌 값에는
|
|
@@ -164,7 +164,7 @@ export class TwinInstance {
|
|
|
164
164
|
* 이 세션에만 그 부류를 셋 겪었다(`model.operations` 두 집 · `materials` 두 집 · tenantId 중복).
|
|
165
165
|
*
|
|
166
166
|
* 복제한 값은 반드시 어긋난다 — 저널은 틱마다 늘고 이 컬럼은 저장할 때만 갱신되므로, 화면이
|
|
167
|
-
* 어느 쪽을 읽느냐로 다른 숫자가 나온다. 그
|
|
167
|
+
* 어느 쪽을 읽느냐로 다른 숫자가 나온다. 그 들어온 것이 없는 어긋남을 만들 이유가 없다.
|
|
168
168
|
*/
|
|
169
169
|
|
|
170
170
|
@CreateDateColumn()
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* **이 물음이 「여러 종류를 시각순으로」인가** — 순수 판단만 여기 둔다.
|
|
3
|
+
*
|
|
4
|
+
* 데이터베이스를 부르지 않는다. 그래서 시험이 이 파일을 그대로 부를 수 있다(데코레이터가 없다).
|
|
5
|
+
* 판단이 조회 코드 안에 섞여 있으면 그 판단만 확인할 방법이 없고, 그러면 「어떤 물음이 나눠 묻는
|
|
6
|
+
* 갈래로 가는가」가 아무도 읽지 못하는 규칙이 된다.
|
|
7
|
+
*
|
|
8
|
+
* 실측 근거와 고침의 뜻은 `event-type-split.ts` 머리에 있다.
|
|
9
|
+
*/
|
|
10
|
+
import { absoluteTime } from '../../engine/read-time.js'
|
|
11
|
+
|
|
12
|
+
/** 질의를 몇 개까지 나눌까 — 대상 × 종류. 이 이상이면 나누는 것이 손해다. */
|
|
13
|
+
export const MAX_SPLIT = 24
|
|
14
|
+
|
|
15
|
+
/** 종류 목록의 상한 — 이보다 많으면 「이 종류만」이 아니라 사실상 전체다. */
|
|
16
|
+
const MAX_TYPES = 12
|
|
17
|
+
|
|
18
|
+
export interface SplitShape {
|
|
19
|
+
eventTypes: string[]
|
|
20
|
+
/**
|
|
21
|
+
* 공정 단계 하나로 좁힌 경우 — 그 값을 갈래마다 함께 건다.
|
|
22
|
+
*
|
|
23
|
+
* 이 필터를 만나면 갈래를 포기하던 것을 고쳤다(2026-08-29). 원장은 종류를 담아 묻고 그 위에 단계
|
|
24
|
+
* 칩을 겹치는데, 칩을 고른 순간 예전 경로로 떨어져 **고르지 않은 쪽이 더 빨랐다.**
|
|
25
|
+
*
|
|
26
|
+
* 단계 컬럼에는 색인이 없다. 그래도 느려지지 않는다 — 종류 정확일치가 색인으로 시각 순서까지 주고,
|
|
27
|
+
* 단계는 그 안에서 행마다 확인하는 조건이다(걷는 행이 그 종류의 것으로 이미 좁혀져 있다).
|
|
28
|
+
*/
|
|
29
|
+
bizStep?: string
|
|
30
|
+
untilTime?: Date
|
|
31
|
+
fromTime?: Date
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* 이 물음이 「여러 종류를 시각순으로」인가 — 아니면 `null`.
|
|
36
|
+
*
|
|
37
|
+
* 받는 모양
|
|
38
|
+
* `eventType` 을 `in` 으로 · 시각 상한이나 구간 · 시각 내림차순 정렬
|
|
39
|
+
*
|
|
40
|
+
* 보내는 모양(일반 경로로)
|
|
41
|
+
* 자유 검색어 · 그 밖의 필터(축·상관 식별자·업무 단계) · 그 밖의 정렬이나 연산자
|
|
42
|
+
*/
|
|
43
|
+
export function splitShape(params: any, sortings: any[]): SplitShape | null {
|
|
44
|
+
const filters: any[] = Array.isArray(params?.filters) ? params.filters : []
|
|
45
|
+
if (typeof params?.search === 'string' && params.search.trim()) return null
|
|
46
|
+
if (sortings.length !== 1 || sortings[0]?.name !== 'eventTime' || sortings[0]?.desc !== true) return null
|
|
47
|
+
|
|
48
|
+
let eventTypes: string[] | undefined
|
|
49
|
+
let untilTime: Date | undefined
|
|
50
|
+
let fromTime: Date | undefined
|
|
51
|
+
let bizStep: string | undefined
|
|
52
|
+
for (const f of filters) {
|
|
53
|
+
if (f?.name === 'eventType') {
|
|
54
|
+
if (f.operator !== 'in' || !Array.isArray(f.value)) return null
|
|
55
|
+
const types: string[] = f.value.map((v: unknown) => String(v ?? '').trim()).filter((t: string) => !!t)
|
|
56
|
+
/* 하나면 나눌 것이 없다(이미 정확일치라 색인을 탄다). 너무 많으면 사실상 전체다. */
|
|
57
|
+
if (types.length < 2 || types.length > MAX_TYPES) return null
|
|
58
|
+
if (eventTypes) return null
|
|
59
|
+
eventTypes = [...new Set(types)]
|
|
60
|
+
} else if (f?.name === 'bizStep') {
|
|
61
|
+
/* 하나로 좁힌 경우만 받는다 — 여러 값이면 종류 × 단계로 갈래가 곱해진다. */
|
|
62
|
+
if (f.operator !== 'eq') return null
|
|
63
|
+
const v = String(f.value ?? '').trim()
|
|
64
|
+
if (!v) return null
|
|
65
|
+
if (bizStep) return null
|
|
66
|
+
bizStep = v
|
|
67
|
+
} else if (f?.name === 'eventTime') {
|
|
68
|
+
if (f.operator === 'lte' || f.operator === 'lt') {
|
|
69
|
+
untilTime = absoluteTime(f.value) ?? undefined
|
|
70
|
+
if (!untilTime) return null
|
|
71
|
+
} else if (f.operator === 'between' && Array.isArray(f.value)) {
|
|
72
|
+
fromTime = absoluteTime(f.value[0]) ?? undefined
|
|
73
|
+
untilTime = absoluteTime(f.value[1]) ?? undefined
|
|
74
|
+
if (!fromTime || !untilTime) return null
|
|
75
|
+
} else return null
|
|
76
|
+
} else return null
|
|
77
|
+
}
|
|
78
|
+
return eventTypes ? { eventTypes, untilTime, fromTime, bizStep } : null
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* **나눠 물은 갈래들을 합쳐 앞에서 `limit` 개** — 이 규율이 정확성의 근거다.
|
|
83
|
+
*
|
|
84
|
+
* ── 왜 갈래마다 `limit` 까지 받아야 하나 ────────────────────────────────────
|
|
85
|
+
* 갈래마다 `limit` 개를 받아 합치면, 합친 것의 앞 `limit` 개는 **전체에서도** 앞 `limit` 개다.
|
|
86
|
+
* 어떤 갈래에서 빠진 행은 그 갈래의 `limit` 번째보다 뒤이고, 우리는 전체에서 `limit` 개만 취하므로
|
|
87
|
+
* 그 행이 이 페이지에 들어올 수 없다.
|
|
88
|
+
*
|
|
89
|
+
* 갈래마다 **더 적게** 받으면 그 성질이 깨진다. 한 갈래가 앞을 다 차지하는 경우
|
|
90
|
+
* (종류 A 의 마지막이 종류 B 의 첫째보다 앞선다) 그 갈래에서 못 받은 것이 빠지고, 결과는 구간의
|
|
91
|
+
* 앞부분만 계산한 것이 된다 — 그런데 화면은 「일부만 읽었다」고만 말하고 어느 부분인지는 말하지 못한다.
|
|
92
|
+
* (2026-08-28 · UX 레인이 미리 짚어 준 함정이다.)
|
|
93
|
+
*
|
|
94
|
+
* 그래서 이 함수는 **`limit` 미만으로 받은 갈래가 있으면 그것을 말한다** — 부르는 쪽이 그 상태로
|
|
95
|
+
* 답하지 않게.
|
|
96
|
+
*/
|
|
97
|
+
export function mergeStreams<T>(
|
|
98
|
+
streams: readonly T[][],
|
|
99
|
+
limit: number,
|
|
100
|
+
/** 앞에 와야 하는 것이 음수 — `Array.prototype.sort` 와 같은 규약. */
|
|
101
|
+
compare: (a: T, b: T) => number
|
|
102
|
+
): { items: T[]; capped: boolean; underfilled: boolean } {
|
|
103
|
+
const underfilled = streams.some(s => s.length > limit)
|
|
104
|
+
const merged = streams.flat().sort(compare)
|
|
105
|
+
return {
|
|
106
|
+
items: merged.slice(0, limit),
|
|
107
|
+
/* 합친 것이 상한을 넘었으면 더 있었다는 뜻이다 — 잘랐다는 사실을 부르는 쪽이 말해야 한다. */
|
|
108
|
+
capped: merged.length > limit || streams.some(s => s.length >= limit),
|
|
109
|
+
/* 갈래가 상한보다 많이 받아 왔다 — 있을 수 없는 일이므로 부르는 쪽이 알아야 한다. */
|
|
110
|
+
underfilled
|
|
111
|
+
}
|
|
112
|
+
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { getRepository } from '@things-factory/shell'
|
|
2
|
+
|
|
3
|
+
import { TwinEvent } from '../twin-event/twin-event.js'
|
|
4
|
+
import { absoluteTime } from '../../engine/read-time.js'
|
|
5
|
+
import { MAX_SPLIT, type SplitShape } from './event-type-split-shape.js'
|
|
6
|
+
|
|
7
|
+
/*
|
|
8
|
+
* **여러 종류를 시각순으로 — 종류를 하나씩 나눠 묻는다** (2026-08-28 실측).
|
|
9
|
+
*
|
|
10
|
+
* ── 무엇이 느렸나 ───────────────────────────────────────────────────────────
|
|
11
|
+
* 에너지 원장은 「이 여덟 종류를 시각 내림차순으로 50건」을 묻는다. 그 모양에서 sqlite 는 종류 색인을
|
|
12
|
+
* **버린다**:
|
|
13
|
+
*
|
|
14
|
+
* 종류 IN + 시각순 SEARCH USING INDEX ix_twin_event_1 (domain, instance) 37.181초 · 결과 0건
|
|
15
|
+
* 같은 조건 · 정렬 없음 SEARCH USING INDEX ix_twin_event_7 (…, event_type=?) 0.003초
|
|
16
|
+
* 종류 하나 + 시각순 SEARCH USING INDEX ix_twin_event_7 (…, event_type=?) 0.003초
|
|
17
|
+
*
|
|
18
|
+
* 정렬을 붙이는 순간 여러 종류의 순서를 합칠 방법이 없어, 시각 색인으로 337만 행을 걸으며 행마다
|
|
19
|
+
* 종류를 확인한다. 승화푸드에 에너지 사건은 0건이다 — **없는 것을 찾느라 37초**를 쓴다.
|
|
20
|
+
*
|
|
21
|
+
* 360 이력이 64초였던 것과 같은 부류다: **거르는 축과 정렬 축이 어긋난다.**
|
|
22
|
+
*
|
|
23
|
+
* ── 고침 ────────────────────────────────────────────────────────────────────
|
|
24
|
+
* 종류를 **정확일치로 하나씩** 물으면 색인이 시각 순서까지 그대로 준다(정렬이 사라진다). 여덟 종류면
|
|
25
|
+
* 질의 여덟 개이고 각 0.003초다. 받은 것을 시각순으로 합쳐 한 페이지를 만든다.
|
|
26
|
+
*
|
|
27
|
+
* 새 색인도, 새 테이블도 필요 없다 — 있는 색인을 쓰게 하는 것이 전부다.
|
|
28
|
+
*
|
|
29
|
+
* ── 좁게 잡는다 ─────────────────────────────────────────────────────────────
|
|
30
|
+
* 이 모양이 아닌 물음은 답하지 않고(`null`) 저널의 일반 경로로 보낸다. 억지로 답하면 목록이 알리지
|
|
31
|
+
* 않고 달라진다.
|
|
32
|
+
*
|
|
33
|
+
* ── ★ **이 갈래를 걷을 조건** ────────────────────────────────────────────────
|
|
34
|
+
* 앱에서 종류를 여러 값으로 보내는 곳은 **에너지 원장 한 곳뿐**이다(2026-08-28 확인). 그 화면이
|
|
35
|
+
* 종류를 하나씩 고르는 모양으로 바뀌면 이 갈래를 부르는 데가 없어진다 — 그때 **지운다.**
|
|
36
|
+
*
|
|
37
|
+
* 부르는 데가 없는 갈래를 들고 있으면, 그것이 무언가를 받쳐 주는 것처럼 보이면서 실제로는 아무것도
|
|
38
|
+
* 하지 않는다. 지금 살려 두는 이유는 하나다: 실 고객 트윈에서 37초 질의가 돌고 있고 화면 수정은
|
|
39
|
+
* 다른 레인의 일이다.
|
|
40
|
+
*/
|
|
41
|
+
|
|
42
|
+
export interface SplitPage {
|
|
43
|
+
items: TwinEvent[]
|
|
44
|
+
nextCursor?: string
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* 나눠 묻고 합쳐 한 페이지 — 답할 수 없으면 `null`.
|
|
49
|
+
*
|
|
50
|
+
* 이어 읽기 지점은 **모든 갈래에 같이** 건다. 그래야 합친 결과가 커서 아래에서만 나온다 — 갈래마다
|
|
51
|
+
* 다르게 걸면 어떤 종류는 이미 지나간 구간을 다시 낸다.
|
|
52
|
+
*/
|
|
53
|
+
export async function eventTypeSplitPage(
|
|
54
|
+
domainId: string,
|
|
55
|
+
instanceIds: string[],
|
|
56
|
+
shape: SplitShape,
|
|
57
|
+
limit: number,
|
|
58
|
+
/** 동시각을 가리는 축 — 트윈 하나면 `revision`, 여럿이면 기본키(§`cursorTiebreak`). */
|
|
59
|
+
tiebreak: 'revision' | 'id',
|
|
60
|
+
after?: string
|
|
61
|
+
): Promise<SplitPage | null> {
|
|
62
|
+
const pairs = instanceIds.length * shape.eventTypes.length
|
|
63
|
+
if (!pairs || pairs > MAX_SPLIT) return null
|
|
64
|
+
|
|
65
|
+
let cursor: { at: Date; tie: string | number } | null = null
|
|
66
|
+
if (after) {
|
|
67
|
+
cursor = decodeCursorPair(after)
|
|
68
|
+
if (!cursor) return null
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const rows: TwinEvent[][] = []
|
|
72
|
+
for (const instanceId of instanceIds) {
|
|
73
|
+
for (const eventType of shape.eventTypes) {
|
|
74
|
+
const qb = getRepository(TwinEvent)
|
|
75
|
+
.createQueryBuilder('e')
|
|
76
|
+
.where('e.domain = :domainId', { domainId })
|
|
77
|
+
.andWhere('e.instanceId = :instanceId', { instanceId })
|
|
78
|
+
/* **정확일치**가 요점이다 — 그래야 색인이 시각 순서까지 준다(§ 위 실측). */
|
|
79
|
+
.andWhere('e.eventType = :eventType', { eventType })
|
|
80
|
+
/* 단계는 색인이 아니라 행 조건이다 — 종류 정확일치가 이미 걷는 범위를 좁혀 놓았다. */
|
|
81
|
+
if (shape.bizStep) qb.andWhere('e.bizStep = :bizStep', { bizStep: shape.bizStep })
|
|
82
|
+
if (shape.fromTime) qb.andWhere('e.eventTime >= :fromTime', { fromTime: shape.fromTime })
|
|
83
|
+
if (shape.untilTime) qb.andWhere('e.eventTime <= :untilTime', { untilTime: shape.untilTime })
|
|
84
|
+
if (cursor) {
|
|
85
|
+
qb.andWhere(`(e.eventTime < :curAt OR (e.eventTime = :curAt AND e.${tiebreak} < :curTie))`, {
|
|
86
|
+
curAt: cursor.at,
|
|
87
|
+
curTie: cursor.tie
|
|
88
|
+
})
|
|
89
|
+
}
|
|
90
|
+
const page = await qb
|
|
91
|
+
.orderBy('e.eventTime', 'DESC')
|
|
92
|
+
.addOrderBy(`e.${tiebreak}`, 'DESC')
|
|
93
|
+
.limit(limit)
|
|
94
|
+
.getMany()
|
|
95
|
+
.catch(() => null)
|
|
96
|
+
/* 한 갈래라도 못 읽으면 합친 결과가 반쪽이다 — 반쪽을 전부인 것처럼 내지 않는다. */
|
|
97
|
+
if (!page) return null
|
|
98
|
+
rows.push(page)
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/*
|
|
103
|
+
* 합친다. 갈래마다 `limit` 까지 받았으므로, 합쳐서 앞의 `limit` 개는 **전체에서도** 앞의 `limit` 개다
|
|
104
|
+
* (각 갈래가 자기 안에서 정렬돼 있고 그보다 앞선 것을 빠뜨릴 수 없다).
|
|
105
|
+
*/
|
|
106
|
+
const merged = rows
|
|
107
|
+
.flat()
|
|
108
|
+
.sort((a: any, b: any) => {
|
|
109
|
+
/* `getMany` 는 엔티티를 주므로 시각이 이미 날짜다 — 감싸면 드라이버 값을 손으로 읽는 모양이 된다. */
|
|
110
|
+
const at = (b.eventTime as Date).getTime() - (a.eventTime as Date).getTime()
|
|
111
|
+
if (at !== 0) return at
|
|
112
|
+
const x = a[tiebreak]
|
|
113
|
+
const y = b[tiebreak]
|
|
114
|
+
return x === y ? 0 : x < y ? 1 : -1
|
|
115
|
+
})
|
|
116
|
+
.slice(0, limit)
|
|
117
|
+
|
|
118
|
+
const last: any = merged[merged.length - 1]
|
|
119
|
+
const nextCursor =
|
|
120
|
+
merged.length === limit && last?.eventTime instanceof Date && last[tiebreak] !== undefined
|
|
121
|
+
? encodeCursorPair(last.eventTime, last[tiebreak])
|
|
122
|
+
: undefined
|
|
123
|
+
|
|
124
|
+
return { items: merged, nextCursor }
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/** 저널 경로와 같은 커서 — `[eventTime, 동시각 축]`(§`buildNextCursor`). */
|
|
128
|
+
function encodeCursorPair(at: Date, tie: string | number): string {
|
|
129
|
+
return Buffer.from(JSON.stringify([at, tie]), 'utf8').toString('base64url')
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function decodeCursorPair(cursor: string): { at: Date; tie: string | number } | null {
|
|
133
|
+
let values: unknown
|
|
134
|
+
try {
|
|
135
|
+
values = JSON.parse(Buffer.from(cursor, 'base64url').toString('utf8'))
|
|
136
|
+
} catch {
|
|
137
|
+
return null
|
|
138
|
+
}
|
|
139
|
+
if (!Array.isArray(values) || values.length !== 2) return null
|
|
140
|
+
const at = absoluteTime(values[0])
|
|
141
|
+
const tie = values[1]
|
|
142
|
+
if (!at || (typeof tie !== 'string' && typeof tie !== 'number')) return null
|
|
143
|
+
return { at, tie }
|
|
144
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 지난 기록 조회의 정렬 축 — **부르는 쪽이 무엇으로 물었는지에 맞춘다.**
|
|
3
|
+
*
|
|
4
|
+
* 리졸버 안에 두면 이 규칙을 확인할 방법이 소스를 글자로 전수 조회하는 것뿐이다. 규칙 자체를 부를 수 있게
|
|
5
|
+
* 밖으로 뺀다(`test/journal-order-axis.test.ts`).
|
|
6
|
+
*/
|
|
7
|
+
export function journalOrder(q: {
|
|
8
|
+
untilTime?: string | null
|
|
9
|
+
fromTime?: string | null
|
|
10
|
+
fromRevision?: number | null
|
|
11
|
+
toRevision?: number | null
|
|
12
|
+
}): {
|
|
13
|
+
eventTime?: 'DESC'
|
|
14
|
+
revision: 'DESC'
|
|
15
|
+
} {
|
|
16
|
+
/* `revision` 으로 물었으면 그 순서가 물음의 답이다. 거르는 축과 정렬 축을 맞추는 규칙과도 같다. */
|
|
17
|
+
const byTime = q.untilTime != null || q.fromTime != null
|
|
18
|
+
if (!byTime && (q.fromRevision != null || q.toRevision != null)) return { revision: 'DESC' }
|
|
19
|
+
return { eventTime: 'DESC', revision: 'DESC' }
|
|
20
|
+
}
|