@things-factory/headless-twin 10.0.15 → 10.0.18
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 +18 -82
- package/dist-server/engine/canonical-ingest.js +183 -18
- 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 +4 -4
- 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 +90 -10
- package/dist-server/engine/ingest-health.js +125 -16
- 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 +212 -14
- package/dist-server/engine/kpi-fold.js +374 -21
- 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 +281 -36
- 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 +15 -14
- 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 +3 -3
- package/dist-server/engine/model-vocabulary.js.map +1 -1
- package/dist-server/engine/oee-accumulator.d.ts +6 -5
- package/dist-server/engine/oee-accumulator.js +6 -5
- 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 +307 -54
- package/dist-server/engine/twin-engine.js +822 -196
- 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 +30 -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 +116 -9
- 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 +76 -13
- package/dist-server/service/reference/reference-live.js.map +1 -1
- package/dist-server/service/reference/reference-master.d.ts +25 -16
- package/dist-server/service/reference/reference-master.js +35 -13
- 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 +12 -12
- package/dist-server/service/reference/reference-resolver.js.map +1 -1
- package/dist-server/service/reference/template-registry.d.ts +1 -1
- package/dist-server/service/reference/template-registry.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-backfill/backfill-period-facts.d.ts +24 -0
- package/dist-server/service/twin-backfill/backfill-period-facts.js +96 -0
- package/dist-server/service/twin-backfill/backfill-period-facts.js.map +1 -0
- package/dist-server/service/twin-backfill/backfill-shape.d.ts +34 -0
- package/dist-server/service/twin-backfill/backfill-shape.js +75 -0
- package/dist-server/service/twin-backfill/backfill-shape.js.map +1 -0
- package/dist-server/service/twin-backfill/index.d.ts +2 -0
- package/dist-server/service/twin-backfill/index.js +6 -0
- package/dist-server/service/twin-backfill/index.js.map +1 -0
- package/dist-server/service/twin-backfill/twin-backfill-resolver.d.ts +10 -0
- package/dist-server/service/twin-backfill/twin-backfill-resolver.js +44 -0
- package/dist-server/service/twin-backfill/twin-backfill-resolver.js.map +1 -0
- package/dist-server/service/twin-control/twin-control-mutation.js +3 -3
- package/dist-server/service/twin-control/twin-control-mutation.js.map +1 -1
- package/dist-server/service/twin-event/twin-event-keys.d.ts +1 -1
- package/dist-server/service/twin-event/twin-event-keys.js +2 -2
- 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/domain-catalog.js +2 -1
- package/dist-server/service/twin-lifecycle/domain-catalog.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 +34 -3
- 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 +12 -9
- package/dist-server/service/twin-model/isa95-coverage.js +33 -9
- 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 +2 -2
- 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 +7 -6
- package/dist-server/service/twin-model/project-structure.js.map +1 -1
- package/dist-server/service/twin-model/twin-equipment.d.ts +2 -2
- package/dist-server/service/twin-model/twin-equipment.js +3 -3
- 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 +15 -15
- 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 +51 -30
- package/dist-server/service/twin-model/twin-model-query.js.map +1 -1
- package/dist-server/service/twin-model/twin-model-tree-query.js +23 -1
- 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 +37 -8
- 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 +4 -4
- 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 +7 -6
- package/server/engine/attention-digest.ts +9 -9
- package/server/engine/canonical-ingest.ts +202 -87
- package/server/engine/command-routing.ts +1 -1
- package/server/engine/declared-stimulus.ts +1 -1
- package/server/engine/energy-topology.ts +2 -2
- package/server/engine/ingest-dedupe.ts +149 -0
- package/server/engine/ingest-health.ts +171 -22
- package/server/engine/integration-probes.ts +7 -7
- package/server/engine/kpi-baseline.ts +4 -4
- package/server/engine/kpi-fold.ts +592 -41
- package/server/engine/kpi-query.ts +307 -48
- package/server/engine/live-feed-registry.ts +1 -1
- package/server/engine/load-meter.ts +1 -1
- package/server/engine/local-declarations.ts +15 -14
- 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 +2 -2
- package/server/engine/oee-accumulator.ts +8 -6
- 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 +906 -205
- 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 +73 -5
- package/server/service/index.ts +7 -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 +145 -10
- package/server/service/reference/reference-hook.ts +145 -0
- package/server/service/reference/reference-live.ts +87 -15
- package/server/service/reference/reference-master.ts +51 -20
- package/server/service/reference/reference-resolver.ts +11 -11
- package/server/service/reference/template-registry.ts +1 -1
- 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-backfill/backfill-period-facts.ts +168 -0
- package/server/service/twin-backfill/backfill-shape.ts +86 -0
- package/server/service/twin-backfill/index.ts +3 -0
- package/server/service/twin-backfill/twin-backfill-resolver.ts +35 -0
- package/server/service/twin-control/twin-control-mutation.ts +2 -2
- package/server/service/twin-event/twin-event-keys.ts +2 -2
- 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/domain-catalog.ts +2 -1
- package/server/service/twin-lifecycle/twin-lifecycle-mutation.ts +58 -4
- 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 +34 -3
- package/server/service/twin-model/iec61850-coverage.ts +1 -1
- package/server/service/twin-model/isa95-coverage.ts +43 -16
- package/server/service/twin-model/item-ref.ts +2 -2
- package/server/service/twin-model/name-index.ts +2 -2
- package/server/service/twin-model/project-structure.ts +7 -6
- package/server/service/twin-model/twin-equipment.ts +16 -4
- 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 +15 -15
- package/server/service/twin-model/twin-model-mutation.ts +4 -4
- package/server/service/twin-model/twin-model-query.ts +49 -28
- package/server/service/twin-model/twin-model-tree-query.ts +26 -2
- 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 +40 -8
- 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 +4 -4
- 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 +6 -5
- package/test/backfill-shape.test.ts +89 -0
- package/test/boot-resume.test.ts +20 -7
- package/test/broadcast-cost-baseline.test.ts +2 -1
- package/test/broadcast-period.test.ts +2 -2
- package/test/broadcast-queue-overflow.test.ts +244 -0
- package/test/canonical-ingest-vocabularies.test.ts +259 -8
- package/test/canonical-quantity-door.test.ts +2 -2
- package/test/capability-mapping.test.ts +1 -1
- package/test/checkpoint-refuses-empty.test.ts +2 -2
- package/test/connector-capability-declaration.test.ts +88 -0
- package/test/contract-layer-guard.test.ts +48 -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 +7 -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/equipment-identity-reaches.test.ts +57 -0
- 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/fact-scope-wiring.test.ts +96 -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/generated-not-counted.test.ts +99 -0
- package/test/ingest-bench.test.ts +2 -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/ingest-wiring-guard.test.ts +197 -0
- 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 +266 -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 +9 -6
- package/test/load-meter.test.ts +1 -1
- package/test/local-declarations.test.ts +13 -12
- package/test/log-stamp.test.ts +1 -1
- package/test/master-to-twin.test.ts +2 -2
- 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 +20 -5
- package/test/operational-vocabulary.test.ts +2 -2
- package/test/project-structure-db.test.ts +2 -2
- package/test/projection-reaches-screen.test.ts +7 -2
- package/test/projection-reads-declared.test.ts +109 -0
- package/test/property-effects.test.ts +10 -6
- 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/scale-twin-bench.test.ts +2 -1
- 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 +19 -21
- 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 +1 -1
- 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/vocabulary-guard.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 +2 -2
- package/test/yield-loop.test.ts +4 -4
- package/tsconfig.shared.tsbuildinfo +1 -1
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* 좌표·representations 는 트윈 공간층(보완/추가). 실 시스템이 좌표를 안 주면 layout 없이 위상만 인제스트.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import type { DomainSystem, EquipmentLevel, TestSpecificationCriterion, WorkCalendarEntry } from '@operato/
|
|
10
|
+
import type { DomainSystem, EquipmentLevel, TestSpecificationCriterion, WorkCalendarEntry } from '@operato/ops-contract'
|
|
11
11
|
|
|
12
12
|
export interface RefLocation {
|
|
13
13
|
id: string
|
|
@@ -118,7 +118,7 @@ export interface RefTestResult {
|
|
|
118
118
|
specId: string
|
|
119
119
|
result: 'pass' | 'fail'
|
|
120
120
|
at?: string
|
|
121
|
-
/** 없으면 무기한 — 짐작으로 기한을 만들면 멀쩡한 자격자가
|
|
121
|
+
/** 없으면 무기한 — 짐작으로 기한을 만들면 멀쩡한 자격자가 오류 없이 사라진다. */
|
|
122
122
|
expiresAt?: string
|
|
123
123
|
}
|
|
124
124
|
|
|
@@ -150,7 +150,16 @@ export interface RefEquipment {
|
|
|
150
150
|
homeLocationId: string
|
|
151
151
|
mtbfMs?: number // 평균 고장 간격(확률적 고장) — 지정 시 커널이 breakdown 시뮬 → 주목(고장) 발생
|
|
152
152
|
mttrMs?: number // 평균 수리 시간
|
|
153
|
-
|
|
153
|
+
/**
|
|
154
|
+
* **이 설비의 전역 정체성** — 커널 선언의 `equipment[].identity` 로 그대로 간다.
|
|
155
|
+
*
|
|
156
|
+
* 예전 이름은 `gs1Id` 였다. 그런데 **GS1 키가 아닌 것이 정상이다** — 실측한 값은 인버터의
|
|
157
|
+
* 하드웨어 식별자(`ES100000007d8d061d-1`)이고, 원본이 GIAI 를 주면 그것도 여기 들어온다.
|
|
158
|
+
* 이름이 한 종류만 가리키면 다른 종류가 들어올 때 그 자리를 못 쓴다.
|
|
159
|
+
*
|
|
160
|
+
* 자리(`RefLocation.gs1Id`)는 그대로다 — 그쪽은 실제로 SGLN 이 온다.
|
|
161
|
+
*/
|
|
162
|
+
identity?: string
|
|
154
163
|
/**
|
|
155
164
|
* 교대(가동시간) — 이 시간대에만 배정된다. `startHour <= endHour` 면 같은 날, 넘어가면 야간(22→6).
|
|
156
165
|
* 미지정=24시간 가용. 교대 밖 자원은 고장·계획정지와 **구별되는 이유**로 쉰다(상태에 드러난다).
|
|
@@ -177,7 +186,7 @@ export interface RefEquipment {
|
|
|
177
186
|
*
|
|
178
187
|
* 없으면 **폐기한 것이 영구히 살아 있다**: 배정 대상에 남고, 가용 수에 들어가고, 화면에 뜬다.
|
|
179
188
|
* 도입 예정인 것은 반대로 오늘부터 있는 것처럼 보인다. 기간 밖이면 커널이 그 시각의 모델에서
|
|
180
|
-
* 빼고 **이유를 달아 남긴다**(`effectivity: 'not-yet' | 'expired'`) —
|
|
189
|
+
* 빼고 **이유를 달아 남긴다**(`effectivity: 'not-yet' | 'expired'`) — 알리지 않고 지우지 않는다.
|
|
181
190
|
*/
|
|
182
191
|
effectiveStart?: string
|
|
183
192
|
effectiveEnd?: string
|
|
@@ -226,7 +235,7 @@ export interface RefOperationVariability {
|
|
|
226
235
|
*
|
|
227
236
|
* **이것이 없으면 시뮬레이션의 시간은 우리가 코드에 고정한 상수다.** 원 시스템(또는 가상 레퍼런스)이
|
|
228
237
|
* 공정별 소요·변동·수율을 말해 줘야 예측이 그 현장의 예측이 된다. 주지 않으면 커널 기본값으로
|
|
229
|
-
* 굴러가고, **기본값을 썼다는 사실은 커널 `specCoverage()` 가 밝힌다**(
|
|
238
|
+
* 굴러가고, **기본값을 썼다는 사실은 커널 `specCoverage()` 가 밝힌다**(알리지 않고 넘어가지 않는다).
|
|
230
239
|
*/
|
|
231
240
|
export interface RefOperation {
|
|
232
241
|
/** 작업 종류 키 — 커널 `FlowTask.kind` 와 같은 값이어야 명세가 그 작업에 붙는다. */
|
|
@@ -366,7 +375,7 @@ export type MasterOrigin =
|
|
|
366
375
|
/**
|
|
367
376
|
* 템플릿 + 노브로 합성했다 — 다시 읽기 = 같은 노브로 다시 짓는다(합성은 결정적이다).
|
|
368
377
|
*
|
|
369
|
-
* `spaceId` 는 **만들 때 고른 현장**이다(합쳐 넣기). 기억하지 않으면 다시 읽을 때 트윈이
|
|
378
|
+
* `spaceId` 는 **만들 때 고른 현장**이다(합쳐 넣기). 기억하지 않으면 다시 읽을 때 트윈이 알리지 않고 자기
|
|
370
379
|
* 현장을 떠난다 — 템플릿이 낸 기본 공간으로 돌아가기 때문이다. 그러면 그 현장의 값(요금 등)도 함께
|
|
371
380
|
* 잃는다(2026-08-18 실측: 한 현장에 합쳐 둔 EMS 트윈 여덟이 재동기 뒤 각자 공간으로 흩어졌다).
|
|
372
381
|
*/
|
|
@@ -457,7 +466,7 @@ export interface ReferenceMaster {
|
|
|
457
466
|
* 여기에 담아 보낸다 — 그러면 `masterToTwin` 의 경고와 한 줄기로 합쳐져 화면까지 간다.
|
|
458
467
|
*
|
|
459
468
|
* 이 자리가 없던 동안 커넥터는 할 말이 있어도 **말할 곳이 없었다.** 그래서 도크를 랙으로 바꿔
|
|
460
|
-
* 놓고도
|
|
469
|
+
* 놓고도 알리지 않고 성공했다. 옮기는 사람만 아는 사실은 옮기는 자리에서 실어야 한다.
|
|
461
470
|
*/
|
|
462
471
|
warnings?: IngestWarning[]
|
|
463
472
|
/**
|
|
@@ -498,7 +507,7 @@ export interface IngestedTwin {
|
|
|
498
507
|
* 호스트가 절대 구간(`StartDateTime`/`FinishDateTime`)을 계산해 넣는 것이고, 그때 이 함수는 사라진다.
|
|
499
508
|
*
|
|
500
509
|
* 모르는 이름에는 **0 을 답하지 않는다**(`undefined`) — 0 은 "UTC 라고 선언했다" 는 뜻이고, 모르는 것을
|
|
501
|
-
* 그렇게 바꾸면 판정이
|
|
510
|
+
* 그렇게 바꾸면 판정이 오류 없이 틀린 값을 낸다.
|
|
502
511
|
*/
|
|
503
512
|
export function utcOffsetOf(timezone?: string, at: Date = new Date()): number | undefined {
|
|
504
513
|
if (!timezone) return undefined
|
|
@@ -553,7 +562,7 @@ export interface IngestWarning {
|
|
|
553
562
|
* 형상은 **시각별 비율**이라, 시간대가 없으면 현장 정오가 아니라 UTC 정오에 피크가 선다(KST 현장이면
|
|
554
563
|
* 밤 9시). 값이 그럴듯하게 나오므로 아무도 알아채지 못한다.
|
|
555
564
|
*
|
|
556
|
-
* 그래서 시간대를 **지어내지 않고**(서버 로컬이나 임의 기본값을 넣으면 그 트윈의 모든 시각 판정이
|
|
565
|
+
* 그래서 시간대를 **지어내지 않고**(서버 로컬이나 임의 기본값을 넣으면 그 트윈의 모든 시각 판정이 알리지 않고
|
|
557
566
|
* 그 가정 위에 선다) 대신 **그 조합을 말한다** — 공간에 시간대를 선언하면 사라지는 경고다.
|
|
558
567
|
*/
|
|
559
568
|
export const generationWithoutTimeZone = (spaceId: string, equipmentId: string): IngestWarning => ({
|
|
@@ -576,9 +585,20 @@ export const generationWithoutTimeZone = (spaceId: string, equipmentId: string):
|
|
|
576
585
|
export const unknownLocationType = (system: string, type: string, id: string): IngestWarning => ({
|
|
577
586
|
code: 'unknown-location-type',
|
|
578
587
|
params: { system, type, id },
|
|
588
|
+
/*
|
|
589
|
+
* ── 무슨 일이 벌어지는지 말한다 (2026-08-25) ────────────────────────────────
|
|
590
|
+
* 예전 문장은 「흐름에 참여하지 못할 수 있다」였다. 물건이 흐르는 도메인에서는 맞는 말인데,
|
|
591
|
+
* 에너지 트윈에는 흐름이 없어서 그 문장이 아무것도 알려 주지 않았다(실제로 태양광 발전소를
|
|
592
|
+
* 붙이면서 이 경고를 받은 사람이 「이 경고가 뜻이 있는가」를 되물었다).
|
|
593
|
+
*
|
|
594
|
+
* 종류를 모르면 실제로 일어나는 일은 하나다: **그 자리에 능력이 붙지 않는다.** 능력이 없으면
|
|
595
|
+
* 상태 필드도 없고, 롤업에도 들어가지 않고, 저작면 팔레트에도 서지 않는다. 도메인을 가리지 않고
|
|
596
|
+
* 참인 문장이므로 이것을 적는다.
|
|
597
|
+
*/
|
|
579
598
|
message:
|
|
580
599
|
`"${type}" is declared nowhere for ${system} (${id}) — neither in the master's own locationTypes nor in the built-in ` +
|
|
581
|
-
`catalog, so
|
|
600
|
+
`catalog. A location whose type is unknown gets no capabilities, so it carries no state, joins no rollup, ` +
|
|
601
|
+
`and (in domains that move things) may be left out of the flow.`
|
|
582
602
|
})
|
|
583
603
|
|
|
584
604
|
/**
|
|
@@ -595,7 +615,7 @@ export function declaredLocationTypes(productionSpec: unknown): string[] | undef
|
|
|
595
615
|
return keys.length ? keys : undefined
|
|
596
616
|
}
|
|
597
617
|
|
|
598
|
-
/** 시간대 문자열을 못 읽었다 — 시각 해석이 UTC 로 떨어진다(
|
|
618
|
+
/** 시간대 문자열을 못 읽었다 — 시각 해석이 UTC 로 떨어진다(알리지 않고 현지 시각으로 가정하지 않는다). */
|
|
599
619
|
export const unknownTimezone = (timezone: string): IngestWarning => ({
|
|
600
620
|
code: 'unknown-timezone',
|
|
601
621
|
params: { timezone },
|
|
@@ -620,7 +640,7 @@ export const noViewableRepresentation = (): IngestWarning => ({
|
|
|
620
640
|
*
|
|
621
641
|
* 예전에는 이런 참조가 model JSON 안의 맨 문자열이라 화면만 경고할 수 있었고, 다른 경로로 들어온
|
|
622
642
|
* 데이터는 아무도 안 봤다. 이제 행으로 풀면서 **인제스트가 직접 안다** — 그러니 인제스트가 말한다.
|
|
623
|
-
* 막지는 않는다(트윈은 model 로 이미 동작한다). 다만
|
|
643
|
+
* 막지는 않는다(트윈은 model 로 이미 동작한다). 다만 알리지 않고 넘기지 않는다.
|
|
624
644
|
*/
|
|
625
645
|
export const unresolvedReference = (field: string, ref: string): IngestWarning => ({
|
|
626
646
|
code: 'unresolved-reference',
|
|
@@ -642,7 +662,7 @@ export const projectionFailed = (reason: string): IngestWarning => ({
|
|
|
642
662
|
* **동작 중에 공장을 전환했다** — 미러는 멈추지 않으므로, 무엇이 사라졌는지 여기서 말한다.
|
|
643
663
|
*
|
|
644
664
|
* 멈췄다 세우는 구조 전환이라면 사용자가 "다시 세웠다" 는 것을 안다. 미러는 그 마디가 없어서
|
|
645
|
-
* 자리 하나·설비 한 대가
|
|
665
|
+
* 자리 하나·설비 한 대가 알리지 않고 없어질 수 있다 — 자리가 수천인 현장에서 화면의 수를 세어
|
|
646
666
|
* 눈치채기를 기대할 수는 없다. 늘어난 것도 함께 말한다(무엇이 반영되었나가 곧 인제스트의 답이다).
|
|
647
667
|
*/
|
|
648
668
|
export const structureAdopted = (rev: number, shift: { locationsAdded: number; locationsDropped: number; equipmentDropped: number; personsDropped: number; assetsDropped: number }): IngestWarning => ({
|
|
@@ -659,7 +679,7 @@ export const structureAdopted = (rev: number, shift: { locationsAdded: number; l
|
|
|
659
679
|
*
|
|
660
680
|
* 예: Oracle WMS 가 우리 표에 없는 `locn_type` 을 주면 그 자리는 `storage` 가 된다 — 도크가 랙으로
|
|
661
681
|
* 모델링되는 것이다. 인제스트를 통째로 실패시킬 일은 아니지만(로케이션 하나 때문에 현장 전체가
|
|
662
|
-
* 안 들어오는 편이 더 나쁘다),
|
|
682
|
+
* 안 들어오는 편이 더 나쁘다), **알리지 않고 넘어가면 안 된다.** 트윈이 실물과 다르게 생겼는데
|
|
663
683
|
* 아무도 그 사실을 모른 채 그 위에서 흐름을 논하게 된다.
|
|
664
684
|
*
|
|
665
685
|
* 값을 나열하는 이유: "3건" 만으로는 커넥터 매핑에 무엇을 더해야 하는지 알 수 없다.
|
|
@@ -678,7 +698,7 @@ export const describeWarnings = (warnings: readonly IngestWarning[]): string =>
|
|
|
678
698
|
/**
|
|
679
699
|
* 서버가 기본값으로 채운 노브들 — 사용자가 준 적 없는 값으로 트윈이 만들어졌음을 알린다.
|
|
680
700
|
*
|
|
681
|
-
*
|
|
701
|
+
* 알리지 않고 채우면 나중에 "왜 이 수치인가" 를 아무도 답할 수 없다. 막을 일은 아니다(기본값은 템플릿이
|
|
682
702
|
* 확신을 갖고 선언한 값이다) — 다만 밝힌다.
|
|
683
703
|
*/
|
|
684
704
|
export const knobDefaultsFilled = (keys: readonly string[]): IngestWarning => ({
|
|
@@ -688,7 +708,7 @@ export const knobDefaultsFilled = (keys: readonly string[]): IngestWarning => ({
|
|
|
688
708
|
})
|
|
689
709
|
|
|
690
710
|
/**
|
|
691
|
-
* 이 현장의 설비를 노브 값으로 채웠다 —
|
|
711
|
+
* 이 현장의 설비를 노브 값으로 채웠다 — **알리지 않고 채우지 않는다.**
|
|
692
712
|
*
|
|
693
713
|
* 에너지 트윈의 부하를 이웃 설비 id 로 선언할 때 쓴다. 몇 개를 가져왔는지 말해 주지 않으면, 사용자는
|
|
694
714
|
* 자기가 고르지 않은 설비들이 부하로 잡힌 것을 나중에 화면에서야 알게 된다.
|
|
@@ -722,14 +742,14 @@ export function masterToTwin(master: ReferenceMaster, catalog?: any): IngestedTw
|
|
|
722
742
|
/*
|
|
723
743
|
* **속성도 통과시킨다** — 설비와 같은 규율(손대지 않고 나른다). 여기가 없으면 자리에 실은 값이
|
|
724
744
|
* 커널에 닿지 않는다: 에너지의 계약전력(`contract.kW`)이 그 경로이고, 통과하지 않으면 커널이
|
|
725
|
-
* 계약을 모르는 채로 서서 **판정을 아예 하지 않는다**(오류 없이
|
|
745
|
+
* 계약을 모르는 채로 서서 **판정을 아예 하지 않는다**(오류 없이 알리지 않고).
|
|
726
746
|
*/
|
|
727
747
|
return { id: l.id, type: l.type, ...(l.name ? { name: l.name } : {}), ...(l.description ? { description: l.description } : {}), capacity: l.capacity ?? 0, ...(l.parallelism !== undefined ? { parallelism: l.parallelism } : {}), ...(l.level ? { level: l.level } : {}), ...(l.properties?.length ? { properties: l.properties } : {}), parentId: l.parentId, ...(l.upstreamId ? { upstreamId: l.upstreamId } : {}), gs1Id: l.gs1Id }
|
|
728
748
|
})
|
|
729
749
|
|
|
730
750
|
const equipment = (master.equipment ?? []).map(e => ({
|
|
731
751
|
id: e.id, kind: e.kind, ...(e.name ? { name: e.name } : {}), ...(e.description ? { description: e.description } : {}), ...(e.testResults?.length ? { testResults: e.testResults } : {}),
|
|
732
|
-
homeLocation: e.homeLocationId, mtbfMs: e.mtbfMs, mttrMs: e.mttrMs,
|
|
752
|
+
homeLocation: e.homeLocationId, mtbfMs: e.mtbfMs, mttrMs: e.mttrMs, ...(e.identity ? { identity: e.identity } : {}),
|
|
733
753
|
...(e.window ? { window: e.window } : {}),
|
|
734
754
|
/* 속성은 손대지 않고 통과 — 이동시간 추정기가 kind 별 속도를 여기서 읽는다(단위 코드 그대로). */
|
|
735
755
|
...(e.properties?.length ? { properties: e.properties } : {}), ...(e.workCalendar?.length ? { workCalendar: e.workCalendar } : {}), ...(e.testSpecificationIds?.length ? { testSpecificationIds: e.testSpecificationIds } : {}),
|
|
@@ -831,11 +851,22 @@ export function masterToTwin(master: ReferenceMaster, catalog?: any): IngestedTw
|
|
|
831
851
|
// 표현을 안 줘도 geo 만 있으면 위성 지도로 착지. (area 폴리곤 물질화는 좌표 투영 필요 → space #3.)
|
|
832
852
|
const geo = master.space.geo
|
|
833
853
|
const givenReps = master.space.representations ?? []
|
|
854
|
+
/*
|
|
855
|
+
* ── 이름을 지어내지 않는다 (2026-08-26) ────────────────────────────────────
|
|
856
|
+
* 여기가 합성한 표현에 `name: '위성 지도'` 를 넣고 있었다. 이 층은 언어 중립이어야 하는데 한국어가
|
|
857
|
+
* 그대로 저장됐다.
|
|
858
|
+
*
|
|
859
|
+
* 낱말 열쇠(`twin.representation.map` 같은 것)를 넣는 길도 있는데 택하지 않았다. **표현의 이름은
|
|
860
|
+
* 사람이 바꿀 수 있는 값**이고, 바꿀 수 있는 값 안에 열쇠가 들어 있으면 사람이 한 번 고치는 순간
|
|
861
|
+
* 그 자리만 열쇠가 아니게 되어 규칙이 둘이 된다.
|
|
862
|
+
*
|
|
863
|
+
* 그래서 이름을 넣지 않는다. 화면이 종류(`map`)로 부르고, 사람이 붙이면 그때부터 그 이름이다.
|
|
864
|
+
*/
|
|
834
865
|
const representations =
|
|
835
866
|
givenReps.length > 0
|
|
836
867
|
? givenReps
|
|
837
868
|
: geo
|
|
838
|
-
? [{ id: 'r-map', type: 'map',
|
|
869
|
+
? [{ id: 'r-map', type: 'map', primary: true, center: { lat: geo.lat, lon: geo.lon }, zoom: 17 }]
|
|
839
870
|
: []
|
|
840
871
|
if (representations.length === 0) {
|
|
841
872
|
warnings.push(noViewableRepresentation())
|
|
@@ -853,7 +884,7 @@ export function masterToTwin(master: ReferenceMaster, catalog?: any): IngestedTw
|
|
|
853
884
|
* 다시 읽을 사이트를 고른다 — **기억해 둔 사이트 하나만**.
|
|
854
885
|
*
|
|
855
886
|
* 못 찾으면 **첫 사이트로 때우지 않는다**: 다른 현장의 구조를 이 트윈에 덮어쓰는 일이고, 그것은 오류가
|
|
856
|
-
* 아니라
|
|
887
|
+
* 아니라 **오류 없이 틀린 공장**으로 나타난다. 기억이 없는 옛 트윈은 `undefined` 를 돌려주고, 부르는 쪽이
|
|
857
888
|
* 마스터의 정체(`source` = 인스턴스 id)로 찾는 길을 택한다.
|
|
858
889
|
*/
|
|
859
890
|
export function pickOriginSite<T extends { siteId: string }>(
|
|
@@ -7,7 +7,7 @@ import { ScalarObject, getRepository } from '@things-factory/shell'
|
|
|
7
7
|
|
|
8
8
|
import { TwinEngine } from '../../engine/index.js'
|
|
9
9
|
import { readRestartPolicy } from '../../engine/restart-policy.js'
|
|
10
|
-
import { readBoardEquipment } from '@operato/
|
|
10
|
+
import { readBoardEquipment } from '@operato/ops-contract'
|
|
11
11
|
|
|
12
12
|
import { applyKnobDefaults, filledKnobKeys } from './knob-defaults.js'
|
|
13
13
|
import type { KnobDef } from './template-registry.js'
|
|
@@ -244,7 +244,7 @@ export class TwinReferenceResolver {
|
|
|
244
244
|
/*
|
|
245
245
|
* **어느 현장의 트윈이었나를 지킨다** — 다시 읽기는 구조만 새로 그리는 일이고, 현장 소속은 그때
|
|
246
246
|
* 사람이 고른 것이다. 기억이 없는 옛 트윈은 **지금 있는 현장**을 그대로 쓴다(빈손으로 두면 템플릿
|
|
247
|
-
* 기본 공간으로 옮겨 간다 —
|
|
247
|
+
* 기본 공간으로 옮겨 간다 — 알리지 않고 현장을 떠나고 그 현장의 값도 잃는다).
|
|
248
248
|
*/
|
|
249
249
|
const keepSpace = origin.spaceId || inst.spaceId
|
|
250
250
|
/* 다시 읽기는 **구조만** 새로 그린다 — 재기동 정책은 그 트윈의 선언이므로 그대로 지킨다. */
|
|
@@ -304,7 +304,7 @@ export class TwinReferenceResolver {
|
|
|
304
304
|
const tpl = getTemplate(origin.templateId)
|
|
305
305
|
if (!tpl) throw new Error(`unknown template: ${origin.templateId} — this twin cannot be rebuilt`)
|
|
306
306
|
/* 저장해 둔 노브 그대로 다시 짓는다. 기본값을 다시 입히지 않는다 — 그 사이 템플릿의 기본값이
|
|
307
|
-
바뀌었다면, 사용자가 고르지 않은 값이
|
|
307
|
+
바뀌었다면, 사용자가 고르지 않은 값이 알리지 않고 현장을 바꾸게 된다. */
|
|
308
308
|
const { master } = tpl.build(origin.params ?? {})
|
|
309
309
|
master.origin = origin
|
|
310
310
|
return master
|
|
@@ -333,7 +333,7 @@ export class TwinReferenceResolver {
|
|
|
333
333
|
*
|
|
334
334
|
* 이제 ① 기억해 둔 사이트로 곧장 간다(`origin.siteId`) ② 기억이 없는 옛 트윈은 사이트를 살펴
|
|
335
335
|
* **마스터의 정체(=이 인스턴스 id)** 와 견준다. 어느 쪽도 못 찾으면 **첫 사이트로 때우지 않는다** —
|
|
336
|
-
* 다른 현장의 구조를 이 트윈에 덮는 것은 오류가 아니라
|
|
336
|
+
* 다른 현장의 구조를 이 트윈에 덮는 것은 오류가 아니라 오류 없이 틀린 공장으로 나타난다.
|
|
337
337
|
*/
|
|
338
338
|
const picked = pickOriginSite(sites, origin)
|
|
339
339
|
if (picked.site) return { ...(await adapter.fetchMaster(cfg, picked.site, onStep)), origin }
|
|
@@ -379,8 +379,8 @@ export class TwinReferenceResolver {
|
|
|
379
379
|
/*
|
|
380
380
|
* **빠진 노브를 서버가 채운다.** 기본값을 아는 곳은 노브 스키마(계약)인데 그것을 적용하는 곳이
|
|
381
381
|
* 위자드뿐이었다 — 그래서 화면을 거치지 않는 호출자(AI·스크립트·API)가 파라미터를 일부만 주면
|
|
382
|
-
* 템플릿 안에서 발생률이 0 이 되고 **아무것도 하지 않는 트윈**이
|
|
383
|
-
* 채운 노브 이름은 경고로 함께 돌려준다 —
|
|
382
|
+
* 템플릿 안에서 발생률이 0 이 되고 **아무것도 하지 않는 트윈**이 알리지 않고 만들어졌다.
|
|
383
|
+
* 채운 노브 이름은 경고로 함께 돌려준다 — 알리지 않고 채우는 것도 알리지 않고 비우는 것만큼 위험하다.
|
|
384
384
|
*/
|
|
385
385
|
const filled = filledKnobKeys(tpl.knobs, params)
|
|
386
386
|
const effective = applyKnobDefaults(tpl.knobs, params)
|
|
@@ -400,7 +400,7 @@ export class TwinReferenceResolver {
|
|
|
400
400
|
/* 고른 현장도 남긴다 — 없으면 다시 읽기가 트윈을 템플릿 기본 공간으로 옮겨 버린다(현장 값도 잃는다). */
|
|
401
401
|
master.origin = { kind: 'template', templateId, params: effective, ...(spaceId ? { spaceId } : {}) }
|
|
402
402
|
/* 공간 선택은 템플릿을 거치지 않는다 — 템플릿마다 이 노브를 알고 있어야 한다면 새 템플릿이
|
|
403
|
-
|
|
403
|
+
알리지 않고 무시하게 된다. 합성된 마스터에 한 곳에서 얹는다(레퍼런스 인제스트와 같은 경로). */
|
|
404
404
|
const res = await TwinEngine.ingestMaster(
|
|
405
405
|
domainId,
|
|
406
406
|
master,
|
|
@@ -638,7 +638,7 @@ export class TwinReferenceResolver {
|
|
|
638
638
|
@Query(returns => ScalarObject, { description: 'Discover sites/factories exposed by a reference. Returns { sites, error? } — an empty list with no error means the system genuinely has none, which is different from a failure.' })
|
|
639
639
|
async discoverReferenceSites(@Arg('source') source: string, @Ctx() context: ResolverContext): Promise<any> {
|
|
640
640
|
/*
|
|
641
|
-
* **"못 찾았다" 와 "없다" 를 구분한다.** 예전에는 넷을 빈 배열 하나로
|
|
641
|
+
* **"못 찾았다" 와 "없다" 를 구분한다.** 예전에는 넷을 빈 배열 하나로 합쳤고(레퍼런스 없음·어댑터
|
|
642
642
|
* 미상·조회 실패·정말 0건), 화면은 그 모두를 "사이트를 찾지 못했습니다" 라고만 말했다. 실 시스템
|
|
643
643
|
* 연결에서 가장 자주 막히는 자리인데 자격이 틀렸는지 주소가 틀렸는지 알 수 없었다.
|
|
644
644
|
* 판정과 문장 형태는 `discovery-result`(순수)가 갖는다.
|
|
@@ -650,7 +650,7 @@ export class TwinReferenceResolver {
|
|
|
650
650
|
try {
|
|
651
651
|
/*
|
|
652
652
|
* **기다려도 답이 없으면 사유를 준다.** 어댑터 계약에 시간 제한이 없어서, 주소가 틀린 엔드포인트는
|
|
653
|
-
* 화면을 무한히 돌게 만들었다 — 무한히 도는 화면은
|
|
653
|
+
* 화면을 무한히 돌게 만들었다 — 무한히 도는 화면은 들어온 것이 없는 실패의 다른 얼굴이다.
|
|
654
654
|
* 상한은 여기서 정한다(어댑터마다 다르게 두면 사용자가 어느 것을 기다리는지 알 수 없다).
|
|
655
655
|
*/
|
|
656
656
|
const sites = await withTimeout(adapter.discoverSites(ref.connectionConfig ?? {}), DISCOVER_TIMEOUT_MS)
|
|
@@ -672,7 +672,7 @@ export class TwinReferenceResolver {
|
|
|
672
672
|
* 데이터로는 가능한 일이 저작으로는 불가능한, `ingest-space` 가 이미 지적한 그 비대칭이다.
|
|
673
673
|
*
|
|
674
674
|
* 판정은 그 순수 모듈이 갖는다: 없는 공간은 거절(`resolveIngestSpace`), 사이트 여럿을 한 공간에
|
|
675
|
-
* 넣는 것도 거절(`refuseImportSpace`) — 서로 다른 현장이
|
|
675
|
+
* 넣는 것도 거절(`refuseImportSpace`) — 서로 다른 현장이 알리지 않고 합쳐지는 것을 막는다.
|
|
676
676
|
*/
|
|
677
677
|
@Directive('@privilege(category: "twin", privilege: "mutation", domainOwnerGranted: true, superUserGranted: true)')
|
|
678
678
|
@Mutation(returns => ScalarObject, {
|
|
@@ -762,7 +762,7 @@ export class TwinReferenceResolver {
|
|
|
762
762
|
/*
|
|
763
763
|
* 정책은 **선언이거나 의도에서 곧바로 따라오는 것**이다: `live=true` 는 미러를 띄우겠다는 뜻이고
|
|
764
764
|
* 미러는 정의상 `resync` 다(외부가 진실이므로 우리 과거를 이어 붙이지 않는다). 시뮬로 띄우려면
|
|
765
|
-
* 스스로 선언해야 한다 — 그 자리에서 우리가 고르면 재기동마다 이력을 지우는 트윈이
|
|
765
|
+
* 스스로 선언해야 한다 — 그 자리에서 우리가 고르면 재기동마다 이력을 지우는 트윈이 알리지 않고 생긴다.
|
|
766
766
|
*/
|
|
767
767
|
assessments[site.siteId] = assessMaster(master, site.siteId)
|
|
768
768
|
const policy = readRestartPolicy(restartPolicy ?? (live ? 'resync' : undefined), `importTwinReference("${source}")`)
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* 둘 다 같은 인제스트 경로(ingestMaster)로 흐른다 — 원천이 고정이냐 합성이냐만 다르다(board-authoring §10).
|
|
5
5
|
* 앱(operato-twin)이 bootstrap 에서 자기 아키타입을 registerTemplate 한다. 표현(map/image/model) 무관.
|
|
6
6
|
*/
|
|
7
|
-
import type { DomainSystem } from '@operato/
|
|
7
|
+
import type { DomainSystem } from '@operato/ops-contract'
|
|
8
8
|
|
|
9
9
|
import type { ReferenceMaster } from './reference-master.js'
|
|
10
10
|
|
|
@@ -73,7 +73,7 @@ export class TwinReference {
|
|
|
73
73
|
/**
|
|
74
74
|
* 마지막 동기에서 **못 옮긴 값의 수**. `null` = 세지 않았다(0 과 다르다).
|
|
75
75
|
*
|
|
76
|
-
* 세지 않은 구조 인제스트는 도크를 랙으로 바꿔 놓고도
|
|
76
|
+
* 세지 않은 구조 인제스트는 도크를 랙으로 바꿔 놓고도 성공이라 답하면서 아무 일도 하지 않는다. 그 사실을 사후에 물을 수
|
|
77
77
|
* 있어야 단계 판정이 「구조는 됐다」를 말할 수 있다 — 없는 동안 판정은 늘 `unmeasured` 였다.
|
|
78
78
|
* 기본값을 두지 않는다: 0 을 기본으로 깔면 「경고 없었다」와 「세지 않았다」가 같아진다.
|
|
79
79
|
*/
|
|
@@ -91,10 +91,10 @@ export class TwinReference {
|
|
|
91
91
|
*
|
|
92
92
|
* 커널이 그 갈림을 이미 적어 두었다(§`hydrateContinuity`): 「원천이 애초에 다시 말해 주지 않는 축」은
|
|
93
93
|
* 재기동 연속성으로 이어받는다. 「우리가 어디까지 읽었나」가 정확히 그 성질이므로 **어댑터의 사물함이
|
|
94
|
-
* 아니라 이 층**에 있다 — 원본이 늘 때마다 저장 기제가 늘고 그중 하나가
|
|
94
|
+
* 아니라 이 층**에 있다 — 원본이 늘 때마다 저장 기제가 늘고 그중 하나가 알리지 않고 다르게 동작하지 않게.
|
|
95
95
|
*
|
|
96
96
|
* ── 왜 캐시가 아니라 표인가 ────────────────────────────────────────────────
|
|
97
|
-
* 만료되면 창이 다시 미끄러지고, 그 손실은 오류 없이
|
|
97
|
+
* 만료되면 창이 다시 미끄러지고, 그 손실은 오류 없이 들어온 것이 없다. 스냅샷 체크포인트와 성질이 다르다
|
|
98
98
|
* (그쪽은 잃어도 원천이 정정해 준다 — 이 값은 **잃으면 원천에 묻지 않게 된다**).
|
|
99
99
|
*
|
|
100
100
|
* 모양은 `{ streams: { [흐름]: { since?, seen[] } }, firstAttachedAt? }` 다. **흐름 이름은 어댑터가
|
|
@@ -43,7 +43,7 @@ export class TwinAttentionQuery {
|
|
|
43
43
|
|
|
44
44
|
/**
|
|
45
45
|
* 잘랐다면 **몇 건을 잘랐는지**와 자리 색 요약까지 — 목록만 주면 화면이 "N건 더" 를 말할 수 없다.
|
|
46
|
-
* 자리별 최고 심각도는 **전량을 근거로**
|
|
46
|
+
* 자리별 최고 심각도는 **전량을 근거로** 계산한다(상위 N 으로 줄이면 20위 밖 신호를 가진 자리가 색을 잃는다).
|
|
47
47
|
*/
|
|
48
48
|
@Query(returns => ScalarObject, {
|
|
49
49
|
description: 'Attention signals for a space (all its lenses) or one twin, with the total count and the per-location highest severity folded from all of them.'
|
|
@@ -21,7 +21,7 @@ export type CommandAuditInput = {
|
|
|
21
21
|
* **원본(어댑터)이 준 답** — 허락과 일어남 사이에 끼어 있는 것을 같은 기록에 닫는다 (ADR-0029 §1 결정 C).
|
|
22
22
|
*
|
|
23
23
|
* ── 왜 이 칸이 필요한가 ──────────────────────────────────────────────────
|
|
24
|
-
* 명령이 원본으로 가면 감사는 「허락했다」까지만 증명한다 — 어댑터가
|
|
24
|
+
* 명령이 원본으로 가면 감사는 「허락했다」까지만 증명한다 — 어댑터가 오류 없이 실패하면 원장에는 승인만
|
|
25
25
|
* 남는다. 그래서 어댑터의 답을 **같은 행**에 되적는다.
|
|
26
26
|
*
|
|
27
27
|
* 셋을 구별한다:
|
|
@@ -103,7 +103,7 @@ export class TwinAuditEvent {
|
|
|
103
103
|
* **원본이 그래서 어떻게 했나** — 허락과 일어남 사이에 끼어 있는 것을 같은 행에 닫는다
|
|
104
104
|
* (ADR-0029 §1 결정 C, 2026-08-19).
|
|
105
105
|
*
|
|
106
|
-
* 명령이 원본으로 가면 감사는 「허락했다」까지만 증명한다 — 어댑터가
|
|
106
|
+
* 명령이 원본으로 가면 감사는 「허락했다」까지만 증명한다 — 어댑터가 오류 없이 실패하면 원장에는 승인만
|
|
107
107
|
* 남는다. 그래서 어댑터의 답을 되적는다.
|
|
108
108
|
*
|
|
109
109
|
* · 비어 있음 — **원본에 묻지 않았다**(커널이 받은 명령이다). 「모른다」와 다르다.
|
|
@@ -142,7 +142,7 @@ export class TwinAuditEvent {
|
|
|
142
142
|
|
|
143
143
|
/**
|
|
144
144
|
* 명령 인자 — **원형 그대로.** 무엇에 대한 명령이었는지가 감사의 핵심이라 요약하지 않는다.
|
|
145
|
-
* 지나치게 크면 자르되 `argsTruncated` 로 잘랐다는 사실을 남긴다(
|
|
145
|
+
* 지나치게 크면 자르되 `argsTruncated` 로 잘랐다는 사실을 남긴다(알리지 않고 버리지 않는다).
|
|
146
146
|
*/
|
|
147
147
|
@Column({ type: 'simple-json', nullable: true })
|
|
148
148
|
@Field(type => ScalarObject, { nullable: true, description: 'Command arguments as given.' })
|
|
@@ -46,7 +46,7 @@ export class TwinAuditQuery {
|
|
|
46
46
|
...(params ?? ({} as ListParam)),
|
|
47
47
|
/* 기본 정렬 — 최근 것부터. 감사는 "요즘" 에서 시작한다. */
|
|
48
48
|
sortings: params?.sortings?.length ? params.sortings : [{ name: 'eventTime', desc: true }],
|
|
49
|
-
/* 시각 필터는 날짜로 옮겨서 넘긴다 — 문자열 그대로면
|
|
49
|
+
/* 시각 필터는 날짜로 옮겨서 넘긴다 — 문자열 그대로면 알리지 않고 0건이 된다(저널과 같은 이유). */
|
|
50
50
|
filters: (params?.filters ?? []).map(f => (f.name === 'eventTime' ? { ...f, value: toDateValue(f.value) } : f))
|
|
51
51
|
}
|
|
52
52
|
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { getRepository } from '@things-factory/shell'
|
|
2
|
+
|
|
3
|
+
import { TwinEvent } from '../twin-event/twin-event.js'
|
|
4
|
+
import { TwinEngine } from '../../engine/twin-engine.js'
|
|
5
|
+
import { ingestCanonicalRecords } from '../../engine/canonical-ingest.js'
|
|
6
|
+
import { absoluteTime } from '../../engine/read-time.js'
|
|
7
|
+
import { judgeBackfill, splitAlreadyThere } from './backfill-shape.js'
|
|
8
|
+
|
|
9
|
+
/*
|
|
10
|
+
* 지난 기록 채우기 — **저널에만 쓴다. 트윈의 「지금」을 건드리지 않는다.**
|
|
11
|
+
*
|
|
12
|
+
* ── 왜 라이브 문으로 넣을 수 없나 (2026-08-30) ──────────────────────────────
|
|
13
|
+
* 커널의 상태는 「지금」이다. 늦게 온 옛 표본은 가드가 막는다(그렇게 만들었다). 지난 기록을 라이브
|
|
14
|
+
* 문으로 넣으면 둘 중 하나다 — 가드에 막히거나, 트윈의 시계가 과거로 끌린다. 둘 다 나쁘다.
|
|
15
|
+
*
|
|
16
|
+
* 그래서 이 길은 저널에만 쓴다. 상태는 그대로 두고, 저널을 읽는 쪽(성과 계산·이력 화면)만 그 사실을
|
|
17
|
+
* 보게 된다.
|
|
18
|
+
*
|
|
19
|
+
* ── 무엇을 받나 ────────────────────────────────────────────────────────────
|
|
20
|
+
* **마감된 구간 사실만** 받는다. 시점의 관측(계량 표본·설비 상태·발전 적산)은 받지 않는다 — 그것들은
|
|
21
|
+
* 커널의 상태를 바꾸는 사실이라, 상태를 건너뛰고 저널에만 넣으면 상태와 저널이 갈라진다.
|
|
22
|
+
*
|
|
23
|
+
* ── 같은 기간을 두 번 넣지 않는다 ──────────────────────────────────────────
|
|
24
|
+
* 마감된 구간 사실은 정체성이 내용에 있다(커널 0.7.81: 종류·대상·시작·끝). 그 id 로 이미 있는 것을
|
|
25
|
+
* 가려낸다. 저장소의 제약으로 막지 않는 이유는 드라이버가 다섯이고 충돌 처리가 제각각이기 때문이다 —
|
|
26
|
+
* 구간 단위로 한 번 읽어 가려내는 편이 이식성이 있고 값도 싸다(한 시간에 한 줄이면 하루 24줄이다).
|
|
27
|
+
*
|
|
28
|
+
* ── 보존보다 오래된 것은 받지 않는다 ───────────────────────────────────────
|
|
29
|
+
* 저널은 보존 기간이 지나면 지운다. 그보다 오래된 것을 채우면 다음 정리에서 지워진다. 조용히 넣고
|
|
30
|
+
* 지워지면 아무도 모르므로, **거절하고 이유를 말한다.**
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
export interface BackfillResult {
|
|
34
|
+
/** 저널에 새로 적은 수. */
|
|
35
|
+
written: number
|
|
36
|
+
/** 이미 있어서 건너뛴 수 — 다시 읽어도 중복이 쌓이지 않는다는 뜻이다. */
|
|
37
|
+
skipped: number
|
|
38
|
+
/** 받지 않은 것과 그 이유. 조용히 버리지 않는다. */
|
|
39
|
+
refused: { record: unknown; reason: string }[]
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** 이 도메인의 보존 기간(일). 정책이 없으면 `undefined` — 그때는 오래됨을 판정하지 않는다. */
|
|
43
|
+
async function retentionDaysOf(domainId: string): Promise<number | undefined> {
|
|
44
|
+
const perDomain = TwinEngine.retentionDaysOf ? await TwinEngine.retentionDaysOf(domainId).catch(() => undefined) : undefined
|
|
45
|
+
const days = perDomain ?? TwinEngine.JOURNAL_RETENTION_DAYS
|
|
46
|
+
return days && days > 0 ? days : undefined
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* 지난 기록을 저널에 채운다.
|
|
51
|
+
*
|
|
52
|
+
* `records` 는 라이브와 **같은 모양**이다 — 커넥터가 다른 어휘를 배울 필요가 없다. 검증·갈림·id 만들기는
|
|
53
|
+
* 커널의 유입이 그대로 한다(§`ingestCanonicalRecords`).
|
|
54
|
+
*/
|
|
55
|
+
export async function backfillPeriodFacts(input: {
|
|
56
|
+
domainId: string
|
|
57
|
+
instanceId: string
|
|
58
|
+
records: unknown[]
|
|
59
|
+
/** 이 시각을 「지금」으로 본다 — 보존 판정의 기준. 시험이 시각을 정할 수 있게 받는다. */
|
|
60
|
+
nowMs?: number
|
|
61
|
+
}): Promise<BackfillResult> {
|
|
62
|
+
const { domainId, instanceId, records } = input
|
|
63
|
+
const nowMs = input.nowMs ?? Date.now()
|
|
64
|
+
const refused: { record: unknown; reason: string }[] = []
|
|
65
|
+
|
|
66
|
+
if (!Array.isArray(records) || !records.length) return { written: 0, skipped: 0, refused }
|
|
67
|
+
|
|
68
|
+
/* 검증과 갈림은 라이브와 같은 것을 쓴다 — 두 벌이 되면 채운 사실과 관측한 사실이 다른 규칙을 지난다. */
|
|
69
|
+
const { accepted, rejected } = ingestCanonicalRecords(
|
|
70
|
+
records as any,
|
|
71
|
+
domainId,
|
|
72
|
+
new Date(nowMs).toISOString(),
|
|
73
|
+
/* 어느 트윈의 사실인가 — 마감된 구간의 이름이 이것으로 갈린다(커널 §`SubjectBasis`). */
|
|
74
|
+
TwinEngine.factScope(domainId, instanceId)
|
|
75
|
+
)
|
|
76
|
+
for (const r of rejected) refused.push({ record: r.record, reason: (r.errors ?? []).join(' · ') })
|
|
77
|
+
|
|
78
|
+
const days = await retentionDaysOf(domainId)
|
|
79
|
+
|
|
80
|
+
const judged = judgeBackfill(accepted as any[], { nowMs, retentionDays: days })
|
|
81
|
+
refused.push(...judged.refused)
|
|
82
|
+
const keep = judged.keep
|
|
83
|
+
|
|
84
|
+
if (!keep.length) {
|
|
85
|
+
TwinEngine.recordIngestResult(
|
|
86
|
+
domainId,
|
|
87
|
+
instanceId,
|
|
88
|
+
records.length,
|
|
89
|
+
refused.map(r => ({ record: r.record, errors: [r.reason] })),
|
|
90
|
+
nowMs,
|
|
91
|
+
0
|
|
92
|
+
)
|
|
93
|
+
return { written: 0, skipped: 0, refused }
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/*
|
|
97
|
+
* 이미 있는 것을 가려낸다 — 그 구간의 그 종류만 읽는다(색인이 받쳐 준다). 페이로드의 id 로 견주고,
|
|
98
|
+
* 저장소 제약에 기대지 않는다(드라이버 다섯).
|
|
99
|
+
*/
|
|
100
|
+
const times = keep.map(e => absoluteTime(e.eventTime)!.getTime())
|
|
101
|
+
const fromAt = new Date(Math.min(...times))
|
|
102
|
+
const toAt = new Date(Math.max(...times))
|
|
103
|
+
const types = [...new Set(keep.map(e => String(e.eventType)))]
|
|
104
|
+
|
|
105
|
+
const existing = await getRepository(TwinEvent)
|
|
106
|
+
.createQueryBuilder('e')
|
|
107
|
+
.select(['e.payload AS "payload"'])
|
|
108
|
+
.where('e.domain = :domainId', { domainId })
|
|
109
|
+
.andWhere('e.instanceId = :instanceId', { instanceId })
|
|
110
|
+
.andWhere('e.eventType IN (:...types)', { types })
|
|
111
|
+
.andWhere('e.eventTime BETWEEN :fromAt AND :toAt', { fromAt, toAt })
|
|
112
|
+
.getRawMany<{ payload: unknown }>()
|
|
113
|
+
|
|
114
|
+
const seen = new Set<string>()
|
|
115
|
+
for (const row of existing) {
|
|
116
|
+
const p: any = typeof row.payload === 'string' ? safeParse(row.payload) : row.payload
|
|
117
|
+
const id = p?.eventId
|
|
118
|
+
if (typeof id === 'string' && id) seen.add(id)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const { fresh, skipped } = splitAlreadyThere(keep, seen)
|
|
122
|
+
if (!fresh.length) {
|
|
123
|
+
TwinEngine.recordIngestResult(
|
|
124
|
+
domainId,
|
|
125
|
+
instanceId,
|
|
126
|
+
records.length,
|
|
127
|
+
refused.map(r => ({ record: r.record, errors: [r.reason] })),
|
|
128
|
+
nowMs,
|
|
129
|
+
skipped
|
|
130
|
+
)
|
|
131
|
+
return { written: 0, skipped, refused }
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/*
|
|
135
|
+
* 리비전은 **쓴 순서**다(일어난 순서가 아니다). 지난 사실이 지금 번호를 받는 것이 맞다 — 저널을
|
|
136
|
+
* 시각으로 읽는 쪽은 `eventTime` 을 보고, 리비전은 같은 시각을 가릴 때만 쓴다.
|
|
137
|
+
*/
|
|
138
|
+
const start = TwinEngine.journalHead(domainId, instanceId)
|
|
139
|
+
const items = fresh.map((event, i) => ({ event, revision: start + i + 1 }))
|
|
140
|
+
|
|
141
|
+
await TwinEngine.persistCarried(domainId, instanceId, items)
|
|
142
|
+
TwinEngine.advanceJournalHead(domainId, instanceId, items.length)
|
|
143
|
+
|
|
144
|
+
/*
|
|
145
|
+
* 장부에 적는다 — **유입을 부르는 곳은 모두 적는다.** 한 경로만 빠지면 그 트윈의 유입 건강이
|
|
146
|
+
* 「모름」으로 보인다(§`ingest-health-wiring`).
|
|
147
|
+
*
|
|
148
|
+
* 건네받지 못한 수(`undelivered`)는 **거절한 것과 이미 있어서 건너뛴 것의 합**이다. 건너뛴 것은
|
|
149
|
+
* 버려진 것이 아니지만 이번에 새로 적히지도 않았다 — 「통과」로 세면 채운 양이 실제보다 커 보인다.
|
|
150
|
+
*/
|
|
151
|
+
TwinEngine.recordIngestResult(
|
|
152
|
+
domainId,
|
|
153
|
+
instanceId,
|
|
154
|
+
records.length,
|
|
155
|
+
refused.map(r => ({ record: r.record, errors: [r.reason] })),
|
|
156
|
+
nowMs,
|
|
157
|
+
skipped
|
|
158
|
+
)
|
|
159
|
+
return { written: items.length, skipped, refused }
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function safeParse(text: string): unknown {
|
|
163
|
+
try {
|
|
164
|
+
return JSON.parse(text)
|
|
165
|
+
} catch {
|
|
166
|
+
return undefined
|
|
167
|
+
}
|
|
168
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* 지난 기록 채우기의 **판단** — 저장소를 모르는 순수 함수.
|
|
3
|
+
*
|
|
4
|
+
* 무엇을 받고 무엇을 거절하는지가 이 파일에 다 있다. 저장소를 아는 코드(`backfill-period-facts`)는
|
|
5
|
+
* 이 판단을 부르고 쓰기만 한다. 갈라 두는 이유는 이 저장소가 이미 쓰는 방식이다 —
|
|
6
|
+
* 판단은 시험이 데이터베이스 없이 지킬 수 있어야 한다(§`event-type-split-shape`).
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/** 이 길이 받는 사실 — 마감된 구간을 말하는 것들. */
|
|
10
|
+
export const PERIOD_FACTS: readonly string[] = [
|
|
11
|
+
'energy.usage.period',
|
|
12
|
+
'energy.generation.period',
|
|
13
|
+
'energy.generation.price',
|
|
14
|
+
'energy.tariff.basis',
|
|
15
|
+
'energy.bill'
|
|
16
|
+
]
|
|
17
|
+
|
|
18
|
+
export interface BackfillJudgement {
|
|
19
|
+
/** 저널에 넣어도 되는 것. */
|
|
20
|
+
keep: any[]
|
|
21
|
+
/** 받지 않은 것과 그 이유 — 조용히 버리지 않는다. */
|
|
22
|
+
refused: { record: unknown; reason: string }[]
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* 무엇을 채울 수 있나.
|
|
27
|
+
*
|
|
28
|
+
* 세 가지로 거절한다.
|
|
29
|
+
*
|
|
30
|
+
* 마감된 구간이 아니다 상태를 바꾸는 사실을 저널에만 넣으면 상태와 저널이 갈라진다
|
|
31
|
+
* 아직 오지 않은 시각 지난 기록이 아니다
|
|
32
|
+
* 보존보다 오래됐다 넣어도 다음 정리에서 지워진다 — 조용히 사라지느니 거절한다
|
|
33
|
+
*/
|
|
34
|
+
export function judgeBackfill(
|
|
35
|
+
envelopes: readonly any[],
|
|
36
|
+
opts: { nowMs: number; retentionDays?: number }
|
|
37
|
+
): BackfillJudgement {
|
|
38
|
+
const keep: any[] = []
|
|
39
|
+
const refused: { record: unknown; reason: string }[] = []
|
|
40
|
+
const horizonMs = opts.retentionDays === undefined ? undefined : opts.nowMs - opts.retentionDays * 86_400_000
|
|
41
|
+
|
|
42
|
+
for (const env of envelopes) {
|
|
43
|
+
const type = String(env?.eventType ?? '')
|
|
44
|
+
if (!PERIOD_FACTS.includes(type)) {
|
|
45
|
+
refused.push({
|
|
46
|
+
record: env,
|
|
47
|
+
reason:
|
|
48
|
+
`${type} 는 마감된 구간 사실이 아니다 — 이 길은 저널에만 쓰므로, 상태를 바꾸는 사실을 여기로 넣으면 ` +
|
|
49
|
+
'상태와 저널이 갈라진다. 라이브 문으로 보내라'
|
|
50
|
+
})
|
|
51
|
+
continue
|
|
52
|
+
}
|
|
53
|
+
const atMs = Date.parse(String(env?.eventTime ?? ''))
|
|
54
|
+
if (!Number.isFinite(atMs)) {
|
|
55
|
+
refused.push({ record: env, reason: '사건 시각을 읽을 수 없다' })
|
|
56
|
+
continue
|
|
57
|
+
}
|
|
58
|
+
if (atMs > opts.nowMs) {
|
|
59
|
+
refused.push({ record: env, reason: `아직 오지 않은 시각이다(${env?.eventTime}) — 지난 기록이 아니다` })
|
|
60
|
+
continue
|
|
61
|
+
}
|
|
62
|
+
if (horizonMs !== undefined && atMs < horizonMs) {
|
|
63
|
+
refused.push({
|
|
64
|
+
record: env,
|
|
65
|
+
reason:
|
|
66
|
+
`보존 기간(${opts.retentionDays}일)보다 오래됐다(${env?.eventTime}) — 넣어도 다음 정리에서 지워진다. ` +
|
|
67
|
+
'더 뒤까지 보려면 보존 기간을 늘려야 한다'
|
|
68
|
+
})
|
|
69
|
+
continue
|
|
70
|
+
}
|
|
71
|
+
keep.push(env)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return { keep, refused }
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* 이미 있는 것을 가려낸다 — **정체성은 내용에 있다**(커널 0.7.81).
|
|
79
|
+
*
|
|
80
|
+
* 저장소의 제약으로 막지 않는 이유는 드라이버가 다섯이고 충돌 처리가 제각각이기 때문이다.
|
|
81
|
+
* 구간 단위로 한 번 읽어 가려내는 편이 이식성이 있고 값도 싸다.
|
|
82
|
+
*/
|
|
83
|
+
export function splitAlreadyThere(keep: readonly any[], existingIds: ReadonlySet<string>): { fresh: any[]; skipped: number } {
|
|
84
|
+
const fresh = keep.filter(e => typeof e?.eventId === 'string' && e.eventId && !existingIds.has(e.eventId))
|
|
85
|
+
return { fresh, skipped: keep.length - fresh.length }
|
|
86
|
+
}
|