@things-factory/headless-twin 10.0.9 → 10.0.11

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.
Files changed (354) hide show
  1. package/dist-server/engine/canonical-ingest.d.ts +2 -2
  2. package/dist-server/engine/canonical-ingest.js +24 -2
  3. package/dist-server/engine/canonical-ingest.js.map +1 -1
  4. package/dist-server/engine/command-routing.js +1 -1
  5. package/dist-server/engine/command-routing.js.map +1 -1
  6. package/dist-server/engine/energy-topology.d.ts +86 -0
  7. package/dist-server/engine/energy-topology.js +144 -0
  8. package/dist-server/engine/energy-topology.js.map +1 -0
  9. package/dist-server/engine/index.d.ts +8 -3
  10. package/dist-server/engine/index.js +10 -3
  11. package/dist-server/engine/index.js.map +1 -1
  12. package/dist-server/engine/kpi-baseline.d.ts +1 -1
  13. package/dist-server/engine/kpi-baseline.js.map +1 -1
  14. package/dist-server/engine/kpi-fold.d.ts +137 -1
  15. package/dist-server/engine/kpi-fold.js +225 -0
  16. package/dist-server/engine/kpi-fold.js.map +1 -1
  17. package/dist-server/engine/kpi-query.d.ts +1 -1
  18. package/dist-server/engine/kpi-query.js +212 -18
  19. package/dist-server/engine/kpi-query.js.map +1 -1
  20. package/dist-server/engine/kpi-target.d.ts +17 -0
  21. package/dist-server/engine/kpi-target.js +24 -2
  22. package/dist-server/engine/kpi-target.js.map +1 -1
  23. package/dist-server/engine/live-feed-registry.d.ts +25 -0
  24. package/dist-server/engine/live-feed-registry.js +51 -0
  25. package/dist-server/engine/live-feed-registry.js.map +1 -0
  26. package/dist-server/engine/load-meter.d.ts +181 -0
  27. package/dist-server/engine/load-meter.js +267 -0
  28. package/dist-server/engine/load-meter.js.map +1 -0
  29. package/dist-server/engine/local-declarations.d.ts +262 -0
  30. package/dist-server/engine/local-declarations.js +528 -0
  31. package/dist-server/engine/local-declarations.js.map +1 -0
  32. package/dist-server/engine/model-basis.d.ts +39 -6
  33. package/dist-server/engine/model-basis.js +69 -9
  34. package/dist-server/engine/model-basis.js.map +1 -1
  35. package/dist-server/engine/{board-vocabulary.d.ts → model-vocabulary.d.ts} +4 -4
  36. package/dist-server/engine/{board-vocabulary.js → model-vocabulary.js} +37 -19
  37. package/dist-server/engine/model-vocabulary.js.map +1 -0
  38. package/dist-server/engine/oee-accumulator.d.ts +28 -0
  39. package/dist-server/engine/oee-accumulator.js +26 -1
  40. package/dist-server/engine/oee-accumulator.js.map +1 -1
  41. package/dist-server/engine/operation-basis.d.ts +32 -0
  42. package/dist-server/engine/operation-basis.js +87 -0
  43. package/dist-server/engine/operation-basis.js.map +1 -0
  44. package/dist-server/engine/property-effects.d.ts +30 -0
  45. package/dist-server/engine/property-effects.js +192 -0
  46. package/dist-server/engine/property-effects.js.map +1 -0
  47. package/dist-server/engine/runtime-key.d.ts +1 -1
  48. package/dist-server/engine/runtime-key.js +1 -1
  49. package/dist-server/engine/runtime-key.js.map +1 -1
  50. package/dist-server/engine/spec-coverage.js +3 -3
  51. package/dist-server/engine/spec-coverage.js.map +1 -1
  52. package/dist-server/engine/state-axes.d.ts +7 -2
  53. package/dist-server/engine/state-axes.js +8 -3
  54. package/dist-server/engine/state-axes.js.map +1 -1
  55. package/dist-server/engine/structure-diff.js +1 -1
  56. package/dist-server/engine/structure-diff.js.map +1 -1
  57. package/dist-server/engine/travel-estimator.d.ts +9 -2
  58. package/dist-server/engine/travel-estimator.js +11 -6
  59. package/dist-server/engine/travel-estimator.js.map +1 -1
  60. package/dist-server/engine/twin-engine.d.ts +332 -47
  61. package/dist-server/engine/twin-engine.js +1314 -279
  62. package/dist-server/engine/twin-engine.js.map +1 -1
  63. package/dist-server/engine/warm-start.d.ts +38 -4
  64. package/dist-server/engine/warm-start.js +38 -9
  65. package/dist-server/engine/warm-start.js.map +1 -1
  66. package/dist-server/index.d.ts +1 -0
  67. package/dist-server/index.js +17 -11
  68. package/dist-server/index.js.map +1 -1
  69. package/dist-server/migrations/1786000000000-RenameTwinInstanceBoardToModel.d.ts +5 -0
  70. package/dist-server/migrations/1786000000000-RenameTwinInstanceBoardToModel.js +48 -0
  71. package/dist-server/migrations/1786000000000-RenameTwinInstanceBoardToModel.js.map +1 -0
  72. package/dist-server/migrations/index.d.ts +2 -0
  73. package/dist-server/migrations/index.js +10 -0
  74. package/dist-server/migrations/index.js.map +1 -0
  75. package/dist-server/service/index.d.ts +3 -2
  76. package/dist-server/service/index.js +25 -17
  77. package/dist-server/service/index.js.map +1 -1
  78. package/dist-server/service/reference/discovery-result.d.ts +1 -1
  79. package/dist-server/service/reference/discovery-result.js +2 -2
  80. package/dist-server/service/reference/discovery-result.js.map +1 -1
  81. package/dist-server/service/reference/ingest-space.d.ts +24 -1
  82. package/dist-server/service/reference/ingest-space.js +17 -1
  83. package/dist-server/service/reference/ingest-space.js.map +1 -1
  84. package/dist-server/service/reference/reference-live.d.ts +23 -0
  85. package/dist-server/service/reference/reference-live.js +121 -4
  86. package/dist-server/service/reference/reference-live.js.map +1 -1
  87. package/dist-server/service/reference/reference-master.d.ts +194 -13
  88. package/dist-server/service/reference/reference-master.js +159 -28
  89. package/dist-server/service/reference/reference-master.js.map +1 -1
  90. package/dist-server/service/reference/reference-resolver.d.ts +31 -1
  91. package/dist-server/service/reference/reference-resolver.js +223 -12
  92. package/dist-server/service/reference/reference-resolver.js.map +1 -1
  93. package/dist-server/service/reference/template-registry.d.ts +19 -3
  94. package/dist-server/service/reference/template-registry.js.map +1 -1
  95. package/dist-server/service/twin-audit/command-audit.d.ts +37 -0
  96. package/dist-server/service/twin-audit/command-audit.js +53 -0
  97. package/dist-server/service/twin-audit/command-audit.js.map +1 -0
  98. package/dist-server/service/twin-audit/index.d.ts +4 -0
  99. package/dist-server/service/twin-audit/index.js +8 -0
  100. package/dist-server/service/twin-audit/index.js.map +1 -0
  101. package/dist-server/service/twin-audit/twin-audit-event.d.ts +24 -0
  102. package/dist-server/service/twin-audit/twin-audit-event.js +141 -0
  103. package/dist-server/service/twin-audit/twin-audit-event.js.map +1 -0
  104. package/dist-server/service/twin-audit/twin-audit-query.d.ts +4 -0
  105. package/dist-server/service/twin-audit/twin-audit-query.js +76 -0
  106. package/dist-server/service/twin-audit/twin-audit-query.js.map +1 -0
  107. package/dist-server/service/twin-control/twin-control-mutation.js +21 -1
  108. package/dist-server/service/twin-control/twin-control-mutation.js.map +1 -1
  109. package/dist-server/service/twin-event/twin-event-keys.d.ts +1 -1
  110. package/dist-server/service/twin-event/twin-event-keys.js +1 -1
  111. package/dist-server/service/twin-event/twin-event-keys.js.map +1 -1
  112. package/dist-server/service/twin-event/twin-event.d.ts +14 -5
  113. package/dist-server/service/twin-event/twin-event.js +45 -13
  114. package/dist-server/service/twin-event/twin-event.js.map +1 -1
  115. package/dist-server/service/twin-forecast/forecast-metrics.d.ts +13 -0
  116. package/dist-server/service/twin-forecast/forecast-metrics.js +57 -0
  117. package/dist-server/service/twin-forecast/forecast-metrics.js.map +1 -0
  118. package/dist-server/service/twin-forecast/forecast-tuning.d.ts +12 -0
  119. package/dist-server/service/twin-forecast/forecast-tuning.js +44 -0
  120. package/dist-server/service/twin-forecast/forecast-tuning.js.map +1 -0
  121. package/dist-server/service/twin-forecast/gap-analytics.d.ts +28 -2
  122. package/dist-server/service/twin-forecast/gap-analytics.js +43 -17
  123. package/dist-server/service/twin-forecast/gap-analytics.js.map +1 -1
  124. package/dist-server/service/twin-forecast/twin-forecast-query.js +217 -38
  125. package/dist-server/service/twin-forecast/twin-forecast-query.js.map +1 -1
  126. package/dist-server/service/twin-instance/twin-instance.d.ts +38 -5
  127. package/dist-server/service/twin-instance/twin-instance.js +66 -20
  128. package/dist-server/service/twin-instance/twin-instance.js.map +1 -1
  129. package/dist-server/service/twin-journal/twin-journal-query.d.ts +5 -2
  130. package/dist-server/service/twin-journal/twin-journal-query.js +71 -9
  131. package/dist-server/service/twin-journal/twin-journal-query.js.map +1 -1
  132. package/dist-server/service/twin-lifecycle/twin-lifecycle-mutation.d.ts +3 -5
  133. package/dist-server/service/twin-lifecycle/twin-lifecycle-mutation.js +24 -27
  134. package/dist-server/service/twin-lifecycle/twin-lifecycle-mutation.js.map +1 -1
  135. package/dist-server/service/twin-metrics/twin-metrics-query.d.ts +1 -0
  136. package/dist-server/service/twin-metrics/twin-metrics-query.js +62 -0
  137. package/dist-server/service/twin-metrics/twin-metrics-query.js.map +1 -1
  138. package/dist-server/service/twin-model/epcis-coverage.d.ts +14 -0
  139. package/dist-server/service/twin-model/epcis-coverage.js +174 -0
  140. package/dist-server/service/twin-model/epcis-coverage.js.map +1 -0
  141. package/dist-server/service/twin-model/iec61850-coverage.d.ts +14 -0
  142. package/dist-server/service/twin-model/iec61850-coverage.js +98 -0
  143. package/dist-server/service/twin-model/iec61850-coverage.js.map +1 -0
  144. package/dist-server/service/twin-model/index.d.ts +13 -0
  145. package/dist-server/service/twin-model/index.js +29 -0
  146. package/dist-server/service/twin-model/index.js.map +1 -0
  147. package/dist-server/service/twin-model/isa95-coverage.d.ts +48 -0
  148. package/dist-server/service/twin-model/isa95-coverage.js +155 -0
  149. package/dist-server/service/twin-model/isa95-coverage.js.map +1 -0
  150. package/dist-server/service/twin-model/project-structure.d.ts +16 -0
  151. package/dist-server/service/twin-model/project-structure.js +173 -0
  152. package/dist-server/service/twin-model/project-structure.js.map +1 -0
  153. package/dist-server/service/twin-model/standard-coverage.d.ts +9 -0
  154. package/dist-server/service/twin-model/standard-coverage.js +37 -0
  155. package/dist-server/service/twin-model/standard-coverage.js.map +1 -0
  156. package/dist-server/service/twin-model/twin-equipment.d.ts +64 -0
  157. package/dist-server/service/twin-model/twin-equipment.js +134 -0
  158. package/dist-server/service/twin-model/twin-equipment.js.map +1 -0
  159. package/dist-server/service/twin-model/twin-lineage-query.d.ts +3 -0
  160. package/dist-server/service/twin-model/twin-lineage-query.js +206 -0
  161. package/dist-server/service/twin-model/twin-lineage-query.js.map +1 -0
  162. package/dist-server/service/twin-model/twin-location.d.ts +58 -0
  163. package/dist-server/service/twin-model/twin-location.js +130 -0
  164. package/dist-server/service/twin-model/twin-location.js.map +1 -0
  165. package/dist-server/service/twin-model/twin-model-item-query.d.ts +141 -0
  166. package/dist-server/service/twin-model/twin-model-item-query.js +852 -0
  167. package/dist-server/service/twin-model/twin-model-item-query.js.map +1 -0
  168. package/dist-server/service/twin-model/twin-model-mutation.d.ts +57 -0
  169. package/dist-server/service/twin-model/twin-model-mutation.js +490 -0
  170. package/dist-server/service/twin-model/twin-model-mutation.js.map +1 -0
  171. package/dist-server/service/twin-model/twin-model-query.d.ts +43 -0
  172. package/dist-server/service/twin-model/twin-model-query.js +551 -0
  173. package/dist-server/service/twin-model/twin-model-query.js.map +1 -0
  174. package/dist-server/service/twin-model/twin-model-tree-query.d.ts +3 -0
  175. package/dist-server/service/twin-model/twin-model-tree-query.js +158 -0
  176. package/dist-server/service/twin-model/twin-model-tree-query.js.map +1 -0
  177. package/dist-server/service/twin-model/twin-operation.d.ts +61 -0
  178. package/dist-server/service/twin-model/twin-operation.js +137 -0
  179. package/dist-server/service/twin-model/twin-operation.js.map +1 -0
  180. package/dist-server/service/twin-space/move-space.d.ts +65 -0
  181. package/dist-server/service/twin-space/move-space.js +166 -0
  182. package/dist-server/service/twin-space/move-space.js.map +1 -0
  183. package/dist-server/service/twin-space/space-integrity.d.ts +97 -0
  184. package/dist-server/service/twin-space/space-integrity.js +182 -0
  185. package/dist-server/service/twin-space/space-integrity.js.map +1 -0
  186. package/dist-server/service/twin-space/twin-space-area.js +4 -4
  187. package/dist-server/service/twin-space/twin-space-area.js.map +1 -1
  188. package/dist-server/service/twin-space/twin-space-representation.js +6 -6
  189. package/dist-server/service/twin-space/twin-space-representation.js.map +1 -1
  190. package/dist-server/service/twin-space/twin-space-resolver.d.ts +63 -1
  191. package/dist-server/service/twin-space/twin-space-resolver.js +356 -18
  192. package/dist-server/service/twin-space/twin-space-resolver.js.map +1 -1
  193. package/dist-server/service/twin-space/twin-space.d.ts +17 -1
  194. package/dist-server/service/twin-space/twin-space.js +15 -5
  195. package/dist-server/service/twin-space/twin-space.js.map +1 -1
  196. package/dist-server/service/twin-structure/twin-structure.d.ts +1 -1
  197. package/dist-server/service/twin-structure/twin-structure.js +8 -8
  198. package/dist-server/service/twin-structure/twin-structure.js.map +1 -1
  199. package/dist-shared/axis-read.d.ts +12 -0
  200. package/dist-shared/axis-read.js +32 -0
  201. package/dist-shared/axis-read.js.map +1 -0
  202. package/{dist-server/engine → dist-shared}/entity-delta.d.ts +3 -0
  203. package/{dist-server/engine → dist-shared}/entity-delta.js +102 -2
  204. package/dist-shared/entity-delta.js.map +1 -0
  205. package/dist-shared/kpi-broadcast.js.map +1 -0
  206. package/dist-shared/twin-level.js.map +1 -0
  207. package/package.json +14 -12
  208. package/server/engine/canonical-ingest.ts +36 -4
  209. package/server/engine/command-routing.ts +1 -1
  210. package/server/engine/energy-topology.ts +189 -0
  211. package/server/engine/index.ts +10 -3
  212. package/server/engine/kpi-baseline.ts +1 -1
  213. package/server/engine/kpi-fold.ts +389 -1
  214. package/server/engine/kpi-query.ts +214 -19
  215. package/server/engine/kpi-target.ts +24 -2
  216. package/server/engine/live-feed-registry.ts +58 -0
  217. package/server/engine/load-meter.ts +384 -0
  218. package/server/engine/local-declarations.ts +700 -0
  219. package/server/engine/model-basis.ts +78 -10
  220. package/server/engine/{board-vocabulary.ts → model-vocabulary.ts} +36 -15
  221. package/server/engine/oee-accumulator.ts +34 -1
  222. package/server/engine/operation-basis.ts +100 -0
  223. package/server/engine/property-effects.ts +199 -0
  224. package/server/engine/runtime-key.ts +1 -1
  225. package/server/engine/spec-coverage.ts +3 -3
  226. package/server/engine/state-axes.ts +8 -3
  227. package/server/engine/structure-diff.ts +1 -1
  228. package/server/engine/travel-estimator.ts +11 -6
  229. package/server/engine/twin-engine.ts +1414 -274
  230. package/server/engine/warm-start.ts +83 -12
  231. package/server/index.ts +13 -10
  232. package/server/migrations/1786000000000-RenameTwinInstanceBoardToModel.ts +43 -0
  233. package/server/migrations/index.ts +7 -0
  234. package/server/service/index.ts +9 -1
  235. package/server/service/reference/discovery-result.ts +2 -2
  236. package/server/service/reference/ingest-space.ts +35 -1
  237. package/server/service/reference/reference-live.ts +119 -4
  238. package/server/service/reference/reference-master.ts +276 -35
  239. package/server/service/reference/reference-resolver.ts +232 -12
  240. package/server/service/reference/template-registry.ts +20 -5
  241. package/server/service/twin-audit/command-audit.ts +81 -0
  242. package/server/service/twin-audit/index.ts +5 -0
  243. package/server/service/twin-audit/twin-audit-event.ts +138 -0
  244. package/server/service/twin-audit/twin-audit-query.ts +72 -0
  245. package/server/service/twin-control/twin-control-mutation.ts +22 -1
  246. package/server/service/twin-event/twin-event-keys.ts +1 -1
  247. package/server/service/twin-event/twin-event.ts +59 -14
  248. package/server/service/twin-forecast/forecast-metrics.ts +60 -0
  249. package/server/service/twin-forecast/forecast-tuning.ts +40 -0
  250. package/server/service/twin-forecast/gap-analytics.ts +48 -9
  251. package/server/service/twin-forecast/twin-forecast-query.ts +208 -36
  252. package/server/service/twin-instance/twin-instance.ts +126 -25
  253. package/server/service/twin-journal/twin-journal-query.ts +74 -10
  254. package/server/service/twin-lifecycle/twin-lifecycle-mutation.ts +23 -22
  255. package/server/service/twin-metrics/twin-metrics-query.ts +59 -2
  256. package/server/service/twin-model/epcis-coverage.ts +195 -0
  257. package/server/service/twin-model/iec61850-coverage.ts +117 -0
  258. package/server/service/twin-model/index.ts +25 -0
  259. package/server/service/twin-model/isa95-coverage.ts +190 -0
  260. package/server/service/twin-model/project-structure.ts +199 -0
  261. package/server/service/twin-model/standard-coverage.ts +35 -0
  262. package/server/service/twin-model/twin-equipment.ts +157 -0
  263. package/server/service/twin-model/twin-lineage-query.ts +193 -0
  264. package/server/service/twin-model/twin-location.ts +147 -0
  265. package/server/service/twin-model/twin-model-item-query.ts +848 -0
  266. package/server/service/twin-model/twin-model-mutation.ts +503 -0
  267. package/server/service/twin-model/twin-model-query.ts +537 -0
  268. package/server/service/twin-model/twin-model-tree-query.ts +180 -0
  269. package/server/service/twin-model/twin-operation.ts +157 -0
  270. package/server/service/twin-space/move-space.ts +262 -0
  271. package/server/service/twin-space/space-integrity.ts +293 -0
  272. package/server/service/twin-space/twin-space-area.ts +4 -4
  273. package/server/service/twin-space/twin-space-representation.ts +6 -6
  274. package/server/service/twin-space/twin-space-resolver.ts +355 -21
  275. package/server/service/twin-space/twin-space.ts +40 -6
  276. package/server/service/twin-structure/twin-structure.ts +10 -10
  277. package/shared/axis-read.ts +28 -0
  278. package/{server/engine → shared}/entity-delta.ts +96 -1
  279. package/test/adopt-structure-live.test.ts +133 -0
  280. package/test/attention-digest.test.ts +3 -3
  281. package/test/axis-read.test.ts +87 -0
  282. package/test/boot-resume.test.ts +205 -0
  283. package/test/canonical-ingest-vocabularies.test.ts +118 -0
  284. package/test/capability-mapping.test.ts +1 -1
  285. package/test/column-type-portability.test.ts +122 -0
  286. package/test/command-routing.test.ts +1 -1
  287. package/test/declaration-reaches-model.test.ts +178 -0
  288. package/test/discovery-result.test.ts +1 -1
  289. package/test/energy-topology.test.ts +113 -0
  290. package/test/entity-delta.test.ts +171 -1
  291. package/test/event-time-column.test.ts +67 -0
  292. package/test/forecast-metrics.test.ts +71 -0
  293. package/test/forecast-tuning.test.ts +42 -0
  294. package/test/gap-analytics.test.ts +34 -7
  295. package/test/ingest-bench.test.ts +6 -6
  296. package/test/ingest-running-guard.test.ts +136 -0
  297. package/test/ingest-space.test.ts +29 -1
  298. package/test/instance-cache-lifecycle.test.ts +110 -0
  299. package/test/kernel-kind-guard.test.ts +96 -0
  300. package/test/kpi-baseline-db.test.ts +3 -1
  301. package/test/kpi-baseline.test.ts +3 -3
  302. package/test/kpi-fold.test.ts +393 -2
  303. package/test/kpi-query-bench.test.ts +5 -3
  304. package/test/lineage-survives-restart.test.ts +203 -0
  305. package/test/live-feed-registry.test.ts +67 -0
  306. package/test/live-kernel-facts.test.ts +161 -0
  307. package/test/live-mirror-parity.test.ts +22 -15
  308. package/test/load-meter.test.ts +314 -0
  309. package/test/local-declarations.test.ts +554 -0
  310. package/test/master-to-twin.test.ts +121 -33
  311. package/test/model-basis.test.ts +56 -1
  312. package/test/{board-vocabulary.test.ts → model-vocabulary.test.ts} +25 -25
  313. package/test/move-space.test.ts +149 -0
  314. package/test/mutation-gate.test.ts +19 -5
  315. package/test/oee-accumulator.test.ts +26 -7
  316. package/test/operation-basis.test.ts +100 -0
  317. package/test/operations-capability-db.test.ts +165 -0
  318. package/test/project-structure-db.test.ts +226 -0
  319. package/test/projection-reaches-screen.test.ts +272 -0
  320. package/test/property-effects.test.ts +96 -0
  321. package/test/resync-origin-site.test.ts +77 -0
  322. package/test/scale-twin-bench.test.ts +5 -5
  323. package/test/snapshot-freshness.test.ts +60 -0
  324. package/test/space-integrity.test.ts +223 -0
  325. package/test/standard-coverage.test.ts +94 -0
  326. package/test/state-axes.test.ts +1 -1
  327. package/test/streamline-e2e.test.ts +14 -14
  328. package/test/structure-revision-db.test.ts +11 -11
  329. package/test/tenant-registry-db.test.ts +5 -5
  330. package/test/twin-audit.test.ts +83 -0
  331. package/test/twin-model-item-db.test.ts +275 -0
  332. package/test/twin-model-tree-db.test.ts +176 -0
  333. package/test/twin-origin-resync.test.ts +114 -0
  334. package/test/warm-start-seam.test.ts +6 -6
  335. package/test/warm-start.test.ts +88 -3
  336. package/tsconfig.json +6 -1
  337. package/tsconfig.shared.json +23 -0
  338. package/tsconfig.shared.tsbuildinfo +1 -0
  339. package/tsconfig.tsbuildinfo +1 -0
  340. package/dist-server/engine/board-vocabulary.js.map +0 -1
  341. package/dist-server/engine/entity-delta.js.map +0 -1
  342. package/dist-server/engine/kpi-broadcast.js.map +0 -1
  343. package/dist-server/engine/twin-level.js.map +0 -1
  344. package/dist-server/service/twin-event/backfill-keys.d.ts +0 -11
  345. package/dist-server/service/twin-event/backfill-keys.js +0 -63
  346. package/dist-server/service/twin-event/backfill-keys.js.map +0 -1
  347. package/dist-server/tsconfig.tsbuildinfo +0 -1
  348. package/server/service/twin-event/backfill-keys.ts +0 -72
  349. /package/{dist-server/engine → dist-shared}/kpi-broadcast.d.ts +0 -0
  350. /package/{dist-server/engine → dist-shared}/kpi-broadcast.js +0 -0
  351. /package/{dist-server/engine → dist-shared}/twin-level.d.ts +0 -0
  352. /package/{dist-server/engine → dist-shared}/twin-level.js +0 -0
  353. /package/{server/engine → shared}/kpi-broadcast.ts +0 -0
  354. /package/{server/engine → shared}/twin-level.ts +0 -0
@@ -12,20 +12,30 @@ const domain_catalog_js_1 = require("./domain-catalog.js");
12
12
  * 트윈 인스턴스 생명주기 채널 — 프로비저닝(상향, ADR-0015).
13
13
  * State/Command/Scenario 는 이미 running 인 인스턴스를 다루지만, 이 채널은 인스턴스 자체를 만들고/설정하고/없앤다.
14
14
  * 생명주기: provision(save, 미기동) → start → stop → delete. 모두 domain 스코프(context.state.domain.id).
15
- * board(BoardDef+layout)=구조, scenario=자극 은 분리 — 여기선 구조만 세운다. 자극은 controlTwinScenario 로.
15
+ * model(TwinModelDef+layout)=구조, scenario=자극 은 분리 — 여기선 구조만 세운다. 자극은 controlTwinScenario 로.
16
16
  */
17
17
  let TwinLifecycleMutation = class TwinLifecycleMutation {
18
- /** 프로비저닝(저장, 미기동) — 구조를 레지스트리에 등록만. 재프로비전 시 구조 변경이면 저널 purge(엔진). */
19
- async saveTwinInstance(instanceId, kind, board, context) {
20
- await index_js_1.TwinEngine.provision(context.state.domain.id, instanceId, kind, board);
21
- return true;
22
- }
23
- /** 기동 — board 를 주면 provision+start, 없으면 레지스트리에 저장된 구조로 start. */
24
- async startTwinInstance(instanceId, context, kind, board) {
18
+ /*
19
+ * `saveTwinInstance` 는 **없앴다**(2026-08-08, ADR-0028).
20
+ *
21
+ * 트윈의 구조를 임의의 model 로 덮어쓰는 문이었고, 유일한 소비자는 손저작 편집기였다.
22
+ * 구조는 원본에서 온다 — 템플릿(`createFromTemplate`)이나 커넥터(`ingestMaster`)가 그 길이다.
23
+ * 문을 열어 두면 화면만 지워도 다음 소비자가 다시 생긴다.
24
+ *
25
+ * 시뮬 트윈을 세우는 것은 여전히 가능하다. 다만 그 입구는 **원본을 만드는 쪽**이지
26
+ * 트윈을 고치는 쪽이 아니다(ADR-0029 — 시뮬레이터는 원본의 한 종류).
27
+ */
28
+ /** 기동 — model 를 주면 provision+start, 없으면 레지스트리에 저장된 구조로 start. */
29
+ async startTwinInstance(instanceId, context, kind, model) {
25
30
  const domainId = context.state.domain.id;
26
31
  // 시각 기준은 **공간**이 갖는다(테넌트 아님) — 교대의 HH:MM 을 어느 기준으로 읽나.
27
- if (board)
28
- index_js_1.TwinEngine.start(instanceId, domainId, kind ?? 'wms', await index_js_1.TwinEngine.withSpaceTimeBase(board, domainId));
32
+ if (model) {
33
+ /* model 를 함께 주는 것은 프로비저닝이다 — 그러면 종류도 말해야 한다.
34
+ 예전에는 `kind ?? 'wms'` 로 메웠는데, 종류를 빠뜨린 야드/생산 트윈이 **창고로 조용히 기동**했다. */
35
+ if (!kind)
36
+ throw new Error('kind is required when model is given — it decides which domain kernel runs this twin');
37
+ index_js_1.TwinEngine.start(instanceId, domainId, kind, await index_js_1.TwinEngine.withSpaceTimeBase(model, domainId));
38
+ }
29
39
  else
30
40
  await index_js_1.TwinEngine.startFromRegistry(domainId, instanceId);
31
41
  return true;
@@ -55,7 +65,7 @@ let TwinLifecycleMutation = class TwinLifecycleMutation {
55
65
  async twinInstanceList(context) {
56
66
  return index_js_1.TwinEngine.list(context.state.domain.id);
57
67
  }
58
- /** 단건 상세 — 프로비저닝 에디터용 board(구조+layout) 포함. */
68
+ /** 단건 상세 — 프로비저닝 에디터용 model(구조+layout) 포함. */
59
69
  async twinInstanceDetail(instanceId, context) {
60
70
  return index_js_1.TwinEngine.detail(context.state.domain.id, instanceId);
61
71
  }
@@ -80,25 +90,12 @@ exports.TwinLifecycleMutation = TwinLifecycleMutation;
80
90
  tslib_1.__decorate([
81
91
  (0, type_graphql_1.Directive)('@privilege(category: "twin", privilege: "mutation", domainOwnerGranted: true, superUserGranted: true)'),
82
92
  (0, type_graphql_1.Mutation)(returns => Boolean, {
83
- description: 'Provision (save) a twin instance structure (kind: wms|yms|mes) from a BoardDef without starting it.'
84
- }),
85
- tslib_1.__param(0, (0, type_graphql_1.Arg)('instanceId')),
86
- tslib_1.__param(1, (0, type_graphql_1.Arg)('kind')),
87
- tslib_1.__param(2, (0, type_graphql_1.Arg)('board', type => shell_1.ScalarObject)),
88
- tslib_1.__param(3, (0, type_graphql_1.Ctx)()),
89
- tslib_1.__metadata("design:type", Function),
90
- tslib_1.__metadata("design:paramtypes", [String, String, Object, Object]),
91
- tslib_1.__metadata("design:returntype", Promise)
92
- ], TwinLifecycleMutation.prototype, "saveTwinInstance", null);
93
- tslib_1.__decorate([
94
- (0, type_graphql_1.Directive)('@privilege(category: "twin", privilege: "mutation", domainOwnerGranted: true, superUserGranted: true)'),
95
- (0, type_graphql_1.Mutation)(returns => Boolean, {
96
- description: 'Start a twin runtime instance and begin the worker tick. If board is given it provisions first; otherwise it starts from the saved registry board.'
93
+ description: 'Start a twin runtime instance and begin the worker tick. If model is given it provisions first; otherwise it starts from the saved registry model.'
97
94
  }),
98
95
  tslib_1.__param(0, (0, type_graphql_1.Arg)('instanceId')),
99
96
  tslib_1.__param(1, (0, type_graphql_1.Ctx)()),
100
97
  tslib_1.__param(2, (0, type_graphql_1.Arg)('kind', { nullable: true })),
101
- tslib_1.__param(3, (0, type_graphql_1.Arg)('board', type => shell_1.ScalarObject, { nullable: true })),
98
+ tslib_1.__param(3, (0, type_graphql_1.Arg)('model', type => shell_1.ScalarObject, { nullable: true })),
102
99
  tslib_1.__metadata("design:type", Function),
103
100
  tslib_1.__metadata("design:paramtypes", [String, Object, String, Object]),
104
101
  tslib_1.__metadata("design:returntype", Promise)
@@ -137,7 +134,7 @@ tslib_1.__decorate([
137
134
  tslib_1.__metadata("design:returntype", Promise)
138
135
  ], TwinLifecycleMutation.prototype, "twinInstanceList", null);
139
136
  tslib_1.__decorate([
140
- (0, type_graphql_1.Query)(returns => shell_1.ScalarObject, { nullable: true, description: 'Get one provisioned twin instance with its BoardDef (structure + layout) for editing.' }),
137
+ (0, type_graphql_1.Query)(returns => shell_1.ScalarObject, { nullable: true, description: 'Get one provisioned twin instance with its TwinModelDef (structure + layout) for editing.' }),
141
138
  tslib_1.__param(0, (0, type_graphql_1.Arg)('instanceId')),
142
139
  tslib_1.__param(1, (0, type_graphql_1.Ctx)()),
143
140
  tslib_1.__metadata("design:type", Function),
@@ -1 +1 @@
1
- {"version":3,"file":"twin-lifecycle-mutation.js","sourceRoot":"","sources":["../../../server/service/twin-lifecycle/twin-lifecycle-mutation.ts"],"names":[],"mappings":"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","sourcesContent":["import { Arg, Ctx, Mutation, Query, Resolver, Directive} from 'type-graphql'\n\nimport { ScalarObject } from '@things-factory/shell'\n\nimport { TwinEngine } from '../../engine/index.js'\n\nimport type { BoardDef } from '@operato/twin-kernel'\n\n/* 도메인 어휘 카탈로그(시스템별 노드 타입) — 커널이 SSOT. 앱/UI 는 이걸 소싱하고 재선언하지 않는다(방언 금지).\n * 조합은 domain-catalog.ts 가 소유 — 리졸버와 서버측 소비자(board-ai 도구 등)가 같은 형태를 본다. */\nimport { domainCatalog } from './domain-catalog.js'\n\n/*\n * 트윈 인스턴스 생명주기 채널 — 프로비저닝(상향, ADR-0015).\n * State/Command/Scenario 는 이미 running 인 인스턴스를 다루지만, 이 채널은 인스턴스 자체를 만들고/설정하고/없앤다.\n * 생명주기: provision(save, 미기동) → start → stop → delete. 모두 domain 스코프(context.state.domain.id).\n * board(BoardDef+layout)=구조, scenario=자극 은 분리 — 여기선 구조만 세운다. 자극은 controlTwinScenario 로.\n */\n@Resolver()\nexport class TwinLifecycleMutation {\n /** 프로비저닝(저장, 미기동) — 구조를 레지스트리에 등록만. 재프로비전 시 구조 변경이면 저널 purge(엔진). */\n @Directive('@privilege(category: \"twin\", privilege: \"mutation\", domainOwnerGranted: true, superUserGranted: true)')\n @Mutation(returns => Boolean, {\n description: 'Provision (save) a twin instance structure (kind: wms|yms|mes) from a BoardDef without starting it.'\n })\n async saveTwinInstance(\n @Arg('instanceId') instanceId: string,\n @Arg('kind') kind: string,\n @Arg('board', type => ScalarObject) board: any,\n @Ctx() context: ResolverContext\n ): Promise<boolean> {\n await TwinEngine.provision(context.state.domain.id, instanceId, kind, board as BoardDef)\n return true\n }\n\n /** 기동 — board 를 주면 provision+start, 없으면 레지스트리에 저장된 구조로 start. */\n @Directive('@privilege(category: \"twin\", privilege: \"mutation\", domainOwnerGranted: true, superUserGranted: true)')\n @Mutation(returns => Boolean, {\n description: 'Start a twin runtime instance and begin the worker tick. If board is given it provisions first; otherwise it starts from the saved registry board.'\n })\n async startTwinInstance(\n @Arg('instanceId') instanceId: string,\n @Ctx() context: ResolverContext,\n @Arg('kind', { nullable: true }) kind?: string,\n @Arg('board', type => ScalarObject, { nullable: true }) board?: any\n ): Promise<boolean> {\n const domainId = context.state.domain.id\n // 시각 기준은 **공간**이 갖는다(테넌트 아님) — 교대의 HH:MM 을 어느 기준으로 읽나.\n if (board) TwinEngine.start(instanceId, domainId, kind ?? 'wms', await TwinEngine.withSpaceTimeBase(board as BoardDef, domainId))\n else await TwinEngine.startFromRegistry(domainId, instanceId)\n return true\n }\n\n @Directive('@privilege(category: \"twin\", privilege: \"mutation\", domainOwnerGranted: true, superUserGranted: true)')\n @Mutation(returns => Boolean, { description: 'Stop a running twin runtime instance by its id (only if it belongs to the current domain).' })\n async stopTwinInstance(@Arg('instanceId') instanceId: string, @Ctx() context: ResolverContext): Promise<boolean> {\n if (!TwinEngine.owns(context.state.domain.id, instanceId)) return false // 테넌트 격리 — 남의 도메인 트윈 정지 차단\n await TwinEngine.stop(context.state.domain.id, instanceId)\n return true\n }\n\n /** 삭제 — 정지 + 레지스트리 삭제 + 저널 purge(domain 스코프). */\n @Directive('@privilege(category: \"twin\", privilege: \"mutation\", domainOwnerGranted: true, superUserGranted: true)')\n @Mutation(returns => Boolean, { description: 'Delete a twin instance: stop it, remove the registry entry and purge its event journal.' })\n async deleteTwinInstance(@Arg('instanceId') instanceId: string, @Ctx() context: ResolverContext): Promise<boolean> {\n await TwinEngine.remove(context.state.domain.id, instanceId)\n return true\n }\n\n /** 관리 목록 — 등록된 인스턴스 전체(kind/status/running/revision/노드·무버 수). 데이터 없으면 빈 배열. */\n /**\n * **이 공간의 트윈 id 들** — 한 현실을 여러 렌즈가 비추므로 공간을 물으면 그 렌즈 전부를 답한다.\n *\n * 화면이 전체 목록을 받아 걸러 쓰던 규칙을 서버로 모은다. 예측 화면은 같은 확장을 한 파일에서\n * **두 번** 복제하고 있었고, 규칙이 흩어지면 한쪽만 고쳐진다. 떠 있지 않은 것도 포함한다 —\n * 공간에 무엇이 있는지는 기동 여부와 다른 사실이다.\n */\n @Query(returns => [String], { description: 'Ids of the twins that live in a space (all its lenses), running or not.' })\n async twinSpaceInstanceIds(@Arg('spaceId') spaceId: string, @Ctx() context: ResolverContext): Promise<string[]> {\n return TwinEngine.instanceIdsOfSpace(context.state.domain.id, spaceId)\n }\n\n @Query(returns => [ScalarObject], { description: 'List provisioned twin instances in the current domain with management metadata.' })\n async twinInstanceList(@Ctx() context: ResolverContext): Promise<any[]> {\n return TwinEngine.list(context.state.domain.id)\n }\n\n /** 단건 상세 — 프로비저닝 에디터용 board(구조+layout) 포함. */\n @Query(returns => ScalarObject, { nullable: true, description: 'Get one provisioned twin instance with its BoardDef (structure + layout) for editing.' })\n async twinInstanceDetail(@Arg('instanceId') instanceId: string, @Ctx() context: ResolverContext): Promise<any | null> {\n return TwinEngine.detail(context.state.domain.id, instanceId)\n }\n\n /**\n * 라이브(메모리) 인스턴스 id 목록 — 콘솔 셀렉터·포트폴리오용. **현재 도메인만**(테넌트 격리).\n * 과거엔 Object.keys(TwinEngine.instances) 로 전역 반환 → 다른 도메인 트윈이 종합현황/셀렉터에 새던 버그.\n * 도메인 스코프 목록(twinInstanceList 와 동일 소스)에서 running 만 추린다.\n */\n @Query(returns => [String], { description: 'List ids of running (in-memory) twin runtime instances in the current domain (tenant-isolated).' })\n async twinInstances(@Ctx() context: ResolverContext): Promise<string[]> {\n const list = await TwinEngine.list(context.state.domain.id)\n return list.filter((x: any) => x.running).map((x: any) => x.instanceId)\n }\n\n /**\n * 도메인 어휘 카탈로그 — 커널(레퍼런스) SSOT 를 그대로 노출. { wms:{label,locationTypes[]}, yms:…, mes:… }.\n * 프로비저닝 UI 는 이걸 소싱해 노드 타입/시스템을 채운다(앱 내 하드코딩 금지).\n */\n @Query(returns => ScalarObject, { description: 'Domain vocabulary catalog (per-system location location types) sourced from the twin kernel — SSOT for provisioning UI.' })\n twinDomainCatalog(): any {\n return domainCatalog()\n }\n}\n"]}
1
+ {"version":3,"file":"twin-lifecycle-mutation.js","sourceRoot":"","sources":["../../../server/service/twin-lifecycle/twin-lifecycle-mutation.ts"],"names":[],"mappings":";;;;AAAA,+CAA4E;AAE5E,iDAAoD;AAEpD,oDAAkD;AAIlD;0EAC0E;AAC1E,2DAAmD;AAEnD;;;;;GAKG;AAEI,IAAM,qBAAqB,GAA3B,MAAM,qBAAqB;IAChC;;;;;;;;;OASG;IAEH,iEAAiE;IAK3D,AAAN,KAAK,CAAC,iBAAiB,CACF,UAAkB,EAC9B,OAAwB,EACE,IAAa,EACU,KAAW;QAEnE,MAAM,QAAQ,GAAG,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAA;QACxC,uDAAuD;QACvD,IAAI,KAAK,EAAE,CAAC;YACV;kFACsE;YACtE,IAAI,CAAC,IAAI;gBAAE,MAAM,IAAI,KAAK,CAAC,sFAAsF,CAAC,CAAA;YAClH,qBAAU,CAAC,KAAK,CAAC,UAAU,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,qBAAU,CAAC,iBAAiB,CAAC,KAAqB,EAAE,QAAQ,CAAC,CAAC,CAAA;QACnH,CAAC;;YACI,MAAM,qBAAU,CAAC,iBAAiB,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAA;QAC7D,OAAO,IAAI,CAAA;IACb,CAAC;IAIK,AAAN,KAAK,CAAC,gBAAgB,CAAoB,UAAkB,EAAS,OAAwB;QAC3F,IAAI,CAAC,qBAAU,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,EAAE,UAAU,CAAC;YAAE,OAAO,KAAK,CAAA,CAAC,2BAA2B;QACnG,MAAM,qBAAU,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,EAAE,UAAU,CAAC,CAAA;QAC1D,OAAO,IAAI,CAAA;IACb,CAAC;IAED,iDAAiD;IAG3C,AAAN,KAAK,CAAC,kBAAkB,CAAoB,UAAkB,EAAS,OAAwB;QAC7F,MAAM,qBAAU,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,EAAE,UAAU,CAAC,CAAA;QAC5D,OAAO,IAAI,CAAA;IACb,CAAC;IAED,+EAA+E;IAC/E;;;;;;OAMG;IAEG,AAAN,KAAK,CAAC,oBAAoB,CAAiB,OAAe,EAAS,OAAwB;QACzF,OAAO,qBAAU,CAAC,kBAAkB,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,EAAE,OAAO,CAAC,CAAA;IACxE,CAAC;IAGK,AAAN,KAAK,CAAC,gBAAgB,CAAQ,OAAwB;QACpD,OAAO,qBAAU,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;IACjD,CAAC;IAED,8CAA8C;IAExC,AAAN,KAAK,CAAC,kBAAkB,CAAoB,UAAkB,EAAS,OAAwB;QAC7F,OAAO,qBAAU,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,EAAE,UAAU,CAAC,CAAA;IAC/D,CAAC;IAED;;;;OAIG;IAEG,AAAN,KAAK,CAAC,aAAa,CAAQ,OAAwB;QACjD,MAAM,IAAI,GAAG,MAAM,qBAAU,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;QAC3D,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,CAAA;IACzE,CAAC;IAED;;;OAGG;IAEH,iBAAiB;QACf,OAAO,IAAA,iCAAa,GAAE,CAAA;IACxB,CAAC;CACF,CAAA;AA9FY,sDAAqB;AAiB1B;IAJL,IAAA,wBAAS,EAAC,uGAAuG,CAAC;IAClH,IAAA,uBAAQ,EAAC,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE;QAC5B,WAAW,EAAE,oJAAoJ;KAClK,CAAC;IAEC,mBAAA,IAAA,kBAAG,EAAC,YAAY,CAAC,CAAA;IACjB,mBAAA,IAAA,kBAAG,GAAE,CAAA;IACL,mBAAA,IAAA,kBAAG,EAAC,MAAM,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAA;IAC/B,mBAAA,IAAA,kBAAG,EAAC,OAAO,EAAE,IAAI,CAAC,EAAE,CAAC,oBAAY,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAA;;;;8DAYxD;AAIK;IAFL,IAAA,wBAAS,EAAC,uGAAuG,CAAC;IAClH,IAAA,uBAAQ,EAAC,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,WAAW,EAAE,4FAA4F,EAAE,CAAC;IACpH,mBAAA,IAAA,kBAAG,EAAC,YAAY,CAAC,CAAA;IAAsB,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;6DAInE;AAKK;IAFL,IAAA,wBAAS,EAAC,uGAAuG,CAAC;IAClH,IAAA,uBAAQ,EAAC,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,WAAW,EAAE,yFAAyF,EAAE,CAAC;IAC/G,mBAAA,IAAA,kBAAG,EAAC,YAAY,CAAC,CAAA;IAAsB,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;+DAGrE;AAWK;IADL,IAAA,oBAAK,EAAC,OAAO,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,EAAE,WAAW,EAAE,yEAAyE,EAAE,CAAC;IAC3F,mBAAA,IAAA,kBAAG,EAAC,SAAS,CAAC,CAAA;IAAmB,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;iEAEjE;AAGK;IADL,IAAA,oBAAK,EAAC,OAAO,CAAC,EAAE,CAAC,CAAC,oBAAY,CAAC,EAAE,EAAE,WAAW,EAAE,iFAAiF,EAAE,CAAC;IAC7G,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;6DAE5B;AAIK;IADL,IAAA,oBAAK,EAAC,OAAO,CAAC,EAAE,CAAC,oBAAY,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,2FAA2F,EAAE,CAAC;IACnI,mBAAA,IAAA,kBAAG,EAAC,YAAY,CAAC,CAAA;IAAsB,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;+DAErE;AAQK;IADL,IAAA,oBAAK,EAAC,OAAO,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,EAAE,WAAW,EAAE,iGAAiG,EAAE,CAAC;IAC1H,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;0DAGzB;AAOD;IADC,IAAA,oBAAK,EAAC,OAAO,CAAC,EAAE,CAAC,oBAAY,EAAE,EAAE,WAAW,EAAE,yHAAyH,EAAE,CAAC;;;;8DAG1K;gCA7FU,qBAAqB;IADjC,IAAA,uBAAQ,GAAE;GACE,qBAAqB,CA8FjC","sourcesContent":["import { Arg, Ctx, Mutation, Query, Resolver, Directive} from 'type-graphql'\n\nimport { ScalarObject } from '@things-factory/shell'\n\nimport { TwinEngine } from '../../engine/index.js'\n\nimport type { TwinModelDef } from '@operato/twin-kernel'\n\n/* 도메인 어휘 카탈로그(시스템별 노드 타입) — 커널이 SSOT. 앱/UI 는 이걸 소싱하고 재선언하지 않는다(방언 금지).\n * 조합은 domain-catalog.ts 가 소유 — 리졸버와 서버측 소비자(board-ai 도구 등)가 같은 형태를 본다. */\nimport { domainCatalog } from './domain-catalog.js'\n\n/*\n * 트윈 인스턴스 생명주기 채널 — 프로비저닝(상향, ADR-0015).\n * State/Command/Scenario 는 이미 running 인 인스턴스를 다루지만, 이 채널은 인스턴스 자체를 만들고/설정하고/없앤다.\n * 생명주기: provision(save, 미기동) → start → stop → delete. 모두 domain 스코프(context.state.domain.id).\n * model(TwinModelDef+layout)=구조, scenario=자극 은 분리 — 여기선 구조만 세운다. 자극은 controlTwinScenario 로.\n */\n@Resolver()\nexport class TwinLifecycleMutation {\n /*\n * `saveTwinInstance` 는 **없앴다**(2026-08-08, ADR-0028).\n *\n * 트윈의 구조를 임의의 model 로 덮어쓰는 문이었고, 유일한 소비자는 손저작 편집기였다.\n * 구조는 원본에서 온다 — 템플릿(`createFromTemplate`)이나 커넥터(`ingestMaster`)가 그 길이다.\n * 문을 열어 두면 화면만 지워도 다음 소비자가 다시 생긴다.\n *\n * 시뮬 트윈을 세우는 것은 여전히 가능하다. 다만 그 입구는 **원본을 만드는 쪽**이지\n * 트윈을 고치는 쪽이 아니다(ADR-0029 — 시뮬레이터는 원본의 한 종류).\n */\n\n /** 기동 — model 를 주면 provision+start, 없으면 레지스트리에 저장된 구조로 start. */\n @Directive('@privilege(category: \"twin\", privilege: \"mutation\", domainOwnerGranted: true, superUserGranted: true)')\n @Mutation(returns => Boolean, {\n description: 'Start a twin runtime instance and begin the worker tick. If model is given it provisions first; otherwise it starts from the saved registry model.'\n })\n async startTwinInstance(\n @Arg('instanceId') instanceId: string,\n @Ctx() context: ResolverContext,\n @Arg('kind', { nullable: true }) kind?: string,\n @Arg('model', type => ScalarObject, { nullable: true }) model?: any\n ): Promise<boolean> {\n const domainId = context.state.domain.id\n // 시각 기준은 **공간**이 갖는다(테넌트 아님) — 교대의 HH:MM 을 어느 기준으로 읽나.\n if (model) {\n /* model 를 함께 주는 것은 프로비저닝이다 — 그러면 종류도 말해야 한다.\n 예전에는 `kind ?? 'wms'` 로 메웠는데, 종류를 빠뜨린 야드/생산 트윈이 **창고로 조용히 기동**했다. */\n if (!kind) throw new Error('kind is required when model is given — it decides which domain kernel runs this twin')\n TwinEngine.start(instanceId, domainId, kind, await TwinEngine.withSpaceTimeBase(model as TwinModelDef, domainId))\n }\n else await TwinEngine.startFromRegistry(domainId, instanceId)\n return true\n }\n\n @Directive('@privilege(category: \"twin\", privilege: \"mutation\", domainOwnerGranted: true, superUserGranted: true)')\n @Mutation(returns => Boolean, { description: 'Stop a running twin runtime instance by its id (only if it belongs to the current domain).' })\n async stopTwinInstance(@Arg('instanceId') instanceId: string, @Ctx() context: ResolverContext): Promise<boolean> {\n if (!TwinEngine.owns(context.state.domain.id, instanceId)) return false // 테넌트 격리 — 남의 도메인 트윈 정지 차단\n await TwinEngine.stop(context.state.domain.id, instanceId)\n return true\n }\n\n /** 삭제 — 정지 + 레지스트리 삭제 + 저널 purge(domain 스코프). */\n @Directive('@privilege(category: \"twin\", privilege: \"mutation\", domainOwnerGranted: true, superUserGranted: true)')\n @Mutation(returns => Boolean, { description: 'Delete a twin instance: stop it, remove the registry entry and purge its event journal.' })\n async deleteTwinInstance(@Arg('instanceId') instanceId: string, @Ctx() context: ResolverContext): Promise<boolean> {\n await TwinEngine.remove(context.state.domain.id, instanceId)\n return true\n }\n\n /** 관리 목록 — 등록된 인스턴스 전체(kind/status/running/revision/노드·무버 수). 데이터 없으면 빈 배열. */\n /**\n * **이 공간의 트윈 id 들** — 한 현실을 여러 렌즈가 비추므로 공간을 물으면 그 렌즈 전부를 답한다.\n *\n * 화면이 전체 목록을 받아 걸러 쓰던 규칙을 서버로 모은다. 예측 화면은 같은 확장을 한 파일에서\n * **두 번** 복제하고 있었고, 규칙이 흩어지면 한쪽만 고쳐진다. 떠 있지 않은 것도 포함한다 —\n * 공간에 무엇이 있는지는 기동 여부와 다른 사실이다.\n */\n @Query(returns => [String], { description: 'Ids of the twins that live in a space (all its lenses), running or not.' })\n async twinSpaceInstanceIds(@Arg('spaceId') spaceId: string, @Ctx() context: ResolverContext): Promise<string[]> {\n return TwinEngine.instanceIdsOfSpace(context.state.domain.id, spaceId)\n }\n\n @Query(returns => [ScalarObject], { description: 'List provisioned twin instances in the current domain with management metadata.' })\n async twinInstanceList(@Ctx() context: ResolverContext): Promise<any[]> {\n return TwinEngine.list(context.state.domain.id)\n }\n\n /** 단건 상세 — 프로비저닝 에디터용 model(구조+layout) 포함. */\n @Query(returns => ScalarObject, { nullable: true, description: 'Get one provisioned twin instance with its TwinModelDef (structure + layout) for editing.' })\n async twinInstanceDetail(@Arg('instanceId') instanceId: string, @Ctx() context: ResolverContext): Promise<any | null> {\n return TwinEngine.detail(context.state.domain.id, instanceId)\n }\n\n /**\n * 라이브(메모리) 인스턴스 id 목록 — 콘솔 셀렉터·포트폴리오용. **현재 도메인만**(테넌트 격리).\n * 과거엔 Object.keys(TwinEngine.instances) 로 전역 반환 → 다른 도메인 트윈이 종합현황/셀렉터에 새던 버그.\n * 도메인 스코프 목록(twinInstanceList 와 동일 소스)에서 running 만 추린다.\n */\n @Query(returns => [String], { description: 'List ids of running (in-memory) twin runtime instances in the current domain (tenant-isolated).' })\n async twinInstances(@Ctx() context: ResolverContext): Promise<string[]> {\n const list = await TwinEngine.list(context.state.domain.id)\n return list.filter((x: any) => x.running).map((x: any) => x.instanceId)\n }\n\n /**\n * 도메인 어휘 카탈로그 — 커널(레퍼런스) SSOT 를 그대로 노출. { wms:{label,locationTypes[]}, yms:…, mes:… }.\n * 프로비저닝 UI 는 이걸 소싱해 노드 타입/시스템을 채운다(앱 내 하드코딩 금지).\n */\n @Query(returns => ScalarObject, { description: 'Domain vocabulary catalog (per-system location location types) sourced from the twin kernel — SSOT for provisioning UI.' })\n twinDomainCatalog(): any {\n return domainCatalog()\n }\n}\n"]}
@@ -1,4 +1,5 @@
1
1
  export declare class TwinMetricsQuery {
2
2
  twinMetrics(instanceId: string, context: ResolverContext): any;
3
3
  twinMetricsAll(context: ResolverContext): Promise<any[]>;
4
+ twinLoad(context: ResolverContext): Promise<any>;
4
5
  }
@@ -3,13 +3,17 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.TwinMetricsQuery = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const type_graphql_1 = require("type-graphql");
6
+ const typeorm_1 = require("typeorm");
6
7
  const shell_1 = require("@things-factory/shell");
7
8
  const index_js_1 = require("../../engine/index.js");
9
+ const load_meter_js_1 = require("../../engine/load-meter.js");
8
10
  /*
9
11
  * 트윈 처리량 계측(모니터, ④-1) — 라이브 동기 인터페이스 부하를 읽는 일급 지표.
10
12
  * 유입률(ingestRate)·방송률(broadcastRate)·저널률(journalRate)·백로그(backlog)·누적. 폴링으로 라이브 관측.
11
13
  * (부하가 화면에 안 보인다던 문제의 정공법 — "얼마나 들어오고 얼마나 방송/기록되나"를 수치로.)
12
14
  */
15
+ /* CPU 는 누적값이라 **표본 사이의 차이**로만 점유율이 나온다. 그래서 호출 사이에 상태가 남아야 한다. */
16
+ const CPU_SAMPLER = (0, load_meter_js_1.newCpuSampler)();
13
17
  let TwinMetricsQuery = class TwinMetricsQuery {
14
18
  twinMetrics(instanceId, context) {
15
19
  if (!index_js_1.TwinEngine.owns(context.state.domain.id, instanceId))
@@ -19,6 +23,54 @@ let TwinMetricsQuery = class TwinMetricsQuery {
19
23
  async twinMetricsAll(context) {
20
24
  return index_js_1.TwinEngine.allMetrics(context.state.domain.id); // 도메인 스코프
21
25
  }
26
+ /*
27
+ * ── 이벤트 루프 부하 — **운영 계기판이다. 테넌트 기능이 아니다.** ─────────────
28
+ *
29
+ * `twinMetrics` 는 "얼마나 들어오고 나가나"(초당 이벤트)를 답한다. 여기서 묻는 것은 "그 일을 하느라
30
+ * 이벤트 루프를 얼마나 붙잡고 있나" 다. 시뮬 틱은 트윈마다 setInterval 로 **메인 루프에서 동기로**
31
+ * 돌기 때문에, 한 트윈이 느려지면 HTTP·구독이 함께 느려진다. 실제로 그 상태를 겪었는데 볼 계기판이
32
+ * 없어서 "포트는 열렸는데 응답이 없다" 로만 보였다.
33
+ *
34
+ * ── 왜 도메인으로 자르지 않나 ───────────────────────────────────────────────
35
+ * 처음에는 호출자의 도메인만 냈다. 테넌트 격리로는 맞아 보이지만 **이벤트 루프는 프로세스 하나가
36
+ * 공유한다.** 실측으로 드러났다: 한 도메인 트윈 22개가 CPU 를 110% 쓰는 동안, 다른 도메인 화면은
37
+ * `실행 중 0 · 루프 점유 —` 였다. "내 도메인은 한가한데 왜 느리지" 를 설명할 수 없었다.
38
+ *
39
+ * 그렇다고 테넌트에게 남의 부하를 조금 보여 주는 것도 답이 아니다 — 쓸 수 없는 정보이고 봐서도 안 된다.
40
+ * 층이 잘못이었다: 이것은 **프로세스를 운영하는 사람의 계기판**이다. 그래서 도메인으로 자르지 않고
41
+ * (전 테넌트 합산) **슈퍼유저에게만** 연다.
42
+ */
43
+ async twinLoad(context) {
44
+ const fleet = index_js_1.TwinEngine.fleet(); // 프로세스 전체 — 도메인으로 자르지 않는다(위 주석)
45
+ /* 도메인 id 는 UUID 라 사람이 못 읽는다. 읽을 수 있는 이름을 채운다 — **모르면 id 를 그대로 둔다**
46
+ (지어내지 않는다). 라이브 인스턴스에는 이미 붙어 있고, 시뮬은 여기서 한 번 조회한다. */
47
+ const ids = [...new Set((fleet.byDomain ?? []).map((d) => d.domainId).filter((v) => v && v !== '(unknown)'))];
48
+ if (ids.length) {
49
+ const found = await (0, shell_1.getRepository)(shell_1.Domain).find({ where: { id: (0, typeorm_1.In)(ids) } });
50
+ const nameOf = new Map(found.map(d => [d.id, d.subdomain || d.name]));
51
+ for (const d of fleet.byDomain)
52
+ if (nameOf.get(d.domainId))
53
+ d.label = nameOf.get(d.domainId);
54
+ for (const r of fleet.slowest ?? [])
55
+ if (r.domainId && nameOf.get(r.domainId))
56
+ r.domainLabel = nameOf.get(r.domainId);
57
+ for (const r of fleet.details ?? [])
58
+ if (r.domainId && nameOf.get(r.domainId))
59
+ r.domainLabel = nameOf.get(r.domainId);
60
+ }
61
+ /*
62
+ * 프로세스가 실제로 쓴 CPU — **계기판의 다른 숫자와 뜻이 다르다.**
63
+ *
64
+ * 위의 값들은 트윈의 계측된 작업만 잰 것이라, 그 밖의 일(GraphQL·TypeORM·GC·webpack·HTTP)은
65
+ * 분모에도 분자에도 없다. "루프 점유 <1%" 인데 CPU 는 110% 였던 상태를 실제로 겪었다. 두 숫자를
66
+ * 나란히 두어야 **트윈이 범인인지** 가릴 수 있다.
67
+ *
68
+ * 표본 간 차이라 첫 호출은 `null` 이다 — 0 이 아니다.
69
+ */
70
+ const usage = process.cpuUsage();
71
+ const cpu = (0, load_meter_js_1.sampleCpu)(CPU_SAMPLER, usage.user + usage.system, Date.now());
72
+ return { ...fleet, processCpuRatio: cpu?.ratio ?? null, processCpuSpanMs: cpu?.spanMs ?? null };
73
+ }
22
74
  };
23
75
  exports.TwinMetricsQuery = TwinMetricsQuery;
24
76
  tslib_1.__decorate([
@@ -36,6 +88,16 @@ tslib_1.__decorate([
36
88
  tslib_1.__metadata("design:paramtypes", [Object]),
37
89
  tslib_1.__metadata("design:returntype", Promise)
38
90
  ], TwinMetricsQuery.prototype, "twinMetricsAll", null);
91
+ tslib_1.__decorate([
92
+ (0, type_graphql_1.Directive)('@privilege(superUserGranted: true)'),
93
+ (0, type_graphql_1.Query)(returns => shell_1.ScalarObject, {
94
+ description: 'Event-loop load of every twin in this process, for system operators (superuser only). Deliberately NOT scoped to the caller\'s domain: the event loop is shared by the whole process, so a tenant-scoped view reports an idle domain while the machine is saturated by someone else. Reports how many instances are running (by mode), how many forecast forks have been created, and per instance how long each kind of work takes (kernel tick, state snapshot, broadcast payload, publish, journal write, live ingest, Monte-Carlo forecast, fork trajectory). Each instance reports both `recent` (the last closed sampling window — what it is doing now) and `total` (cumulative since boot), with `busyRatio` telling how much of the window that twin held the loop. `fleetBusyRatio` above 1 means the twins together cannot finish inside one tick interval, which is when HTTP and subscriptions start to stall. Work that has never been measured is reported as null rather than zero, because zero would read as "instant".'
95
+ }),
96
+ tslib_1.__param(0, (0, type_graphql_1.Ctx)()),
97
+ tslib_1.__metadata("design:type", Function),
98
+ tslib_1.__metadata("design:paramtypes", [Object]),
99
+ tslib_1.__metadata("design:returntype", Promise)
100
+ ], TwinMetricsQuery.prototype, "twinLoad", null);
39
101
  exports.TwinMetricsQuery = TwinMetricsQuery = tslib_1.__decorate([
40
102
  (0, type_graphql_1.Resolver)()
41
103
  ], TwinMetricsQuery);
@@ -1 +1 @@
1
- {"version":3,"file":"twin-metrics-query.js","sourceRoot":"","sources":["../../../server/service/twin-metrics/twin-metrics-query.ts"],"names":[],"mappings":";;;;AAAA,+CAAwD;AAExD,iDAAoD;AAEpD,oDAAkD;AAElD;;;;GAIG;AAEI,IAAM,gBAAgB,GAAtB,MAAM,gBAAgB;IAE3B,WAAW,CAAoB,UAAkB,EAAS,OAAwB;QAChF,IAAI,CAAC,qBAAU,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,EAAE,UAAU,CAAC;YAAE,OAAO,IAAI,CAAA,CAAC,SAAS;QAChF,OAAO,qBAAU,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,EAAE,UAAU,CAAC,CAAA;IAChE,CAAC;IAGK,AAAN,KAAK,CAAC,cAAc,CAAQ,OAAwB;QAClD,OAAO,qBAAU,CAAC,UAAU,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA,CAAC,UAAU;IAClE,CAAC;CACF,CAAA;AAXY,4CAAgB;AAE3B;IADC,IAAA,oBAAK,EAAC,OAAO,CAAC,EAAE,CAAC,oBAAY,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,kJAAkJ,EAAE,CAAC;IACvM,mBAAA,IAAA,kBAAG,EAAC,YAAY,CAAC,CAAA;IAAsB,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;mDAGxD;AAGK;IADL,IAAA,oBAAK,EAAC,OAAO,CAAC,EAAE,CAAC,CAAC,oBAAY,CAAC,EAAE,EAAE,WAAW,EAAE,uGAAuG,EAAE,CAAC;IACrI,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;sDAE1B;2BAVU,gBAAgB;IAD5B,IAAA,uBAAQ,GAAE;GACE,gBAAgB,CAW5B","sourcesContent":["import { Arg, Ctx, Query, Resolver } from 'type-graphql'\n\nimport { ScalarObject } from '@things-factory/shell'\n\nimport { TwinEngine } from '../../engine/index.js'\n\n/*\n * 트윈 처리량 계측(모니터, ④-1) — 라이브 동기 인터페이스 부하를 읽는 일급 지표.\n * 유입률(ingestRate)·방송률(broadcastRate)·저널률(journalRate)·백로그(backlog)·누적. 폴링으로 라이브 관측.\n * (부하가 화면에 안 보인다던 문제의 정공법 — \"얼마나 들어오고 얼마나 방송/기록되나\"를 수치로.)\n */\n@Resolver()\nexport class TwinMetricsQuery {\n @Query(returns => ScalarObject, { nullable: true, description: 'Live throughput metrics of one twin instance (ingest/broadcast/journal rate, backlog). Null if not a live instance or not in the current domain.' })\n twinMetrics(@Arg('instanceId') instanceId: string, @Ctx() context: ResolverContext): any {\n if (!TwinEngine.owns(context.state.domain.id, instanceId)) return null // 테넌트 격리\n return TwinEngine.metrics(context.state.domain.id, instanceId)\n }\n\n @Query(returns => [ScalarObject], { description: 'Live throughput metrics of all running live twin instances in the current domain (monitor dashboard).' })\n async twinMetricsAll(@Ctx() context: ResolverContext): Promise<any[]> {\n return TwinEngine.allMetrics(context.state.domain.id) // 도메인 스코프\n }\n}\n"]}
1
+ {"version":3,"file":"twin-metrics-query.js","sourceRoot":"","sources":["../../../server/service/twin-metrics/twin-metrics-query.ts"],"names":[],"mappings":"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","sourcesContent":["import { Arg, Ctx, Directive, Query, Resolver } from 'type-graphql'\n\nimport { In } from 'typeorm'\n\nimport { Domain, getRepository, ScalarObject } from '@things-factory/shell'\n\nimport { TwinEngine } from '../../engine/index.js'\nimport { newCpuSampler, sampleCpu } from '../../engine/load-meter.js'\n\n/*\n * 트윈 처리량 계측(모니터, ④-1) — 라이브 동기 인터페이스 부하를 읽는 일급 지표.\n * 유입률(ingestRate)·방송률(broadcastRate)·저널률(journalRate)·백로그(backlog)·누적. 폴링으로 라이브 관측.\n * (부하가 화면에 안 보인다던 문제의 정공법 — \"얼마나 들어오고 얼마나 방송/기록되나\"를 수치로.)\n */\n/* CPU 는 누적값이라 **표본 사이의 차이**로만 점유율이 나온다. 그래서 호출 사이에 상태가 남아야 한다. */\nconst CPU_SAMPLER = newCpuSampler()\n\n@Resolver()\nexport class TwinMetricsQuery {\n @Query(returns => ScalarObject, { nullable: true, description: 'Live throughput metrics of one twin instance (ingest/broadcast/journal rate, backlog). Null if not a live instance or not in the current domain.' })\n twinMetrics(@Arg('instanceId') instanceId: string, @Ctx() context: ResolverContext): any {\n if (!TwinEngine.owns(context.state.domain.id, instanceId)) return null // 테넌트 격리\n return TwinEngine.metrics(context.state.domain.id, instanceId)\n }\n\n @Query(returns => [ScalarObject], { description: 'Live throughput metrics of all running live twin instances in the current domain (monitor dashboard).' })\n async twinMetricsAll(@Ctx() context: ResolverContext): Promise<any[]> {\n return TwinEngine.allMetrics(context.state.domain.id) // 도메인 스코프\n }\n\n /*\n * ── 이벤트 루프 부하 — **운영 계기판이다. 테넌트 기능이 아니다.** ─────────────\n *\n * `twinMetrics` 는 \"얼마나 들어오고 나가나\"(초당 이벤트)를 답한다. 여기서 묻는 것은 \"그 일을 하느라\n * 이벤트 루프를 얼마나 붙잡고 있나\" 다. 시뮬 틱은 트윈마다 setInterval 로 **메인 루프에서 동기로**\n * 돌기 때문에, 한 트윈이 느려지면 HTTP·구독이 함께 느려진다. 실제로 그 상태를 겪었는데 볼 계기판이\n * 없어서 \"포트는 열렸는데 응답이 없다\" 로만 보였다.\n *\n * ── 왜 도메인으로 자르지 않나 ───────────────────────────────────────────────\n * 처음에는 호출자의 도메인만 냈다. 테넌트 격리로는 맞아 보이지만 **이벤트 루프는 프로세스 하나가\n * 공유한다.** 실측으로 드러났다: 한 도메인 트윈 22개가 CPU 를 110% 쓰는 동안, 다른 도메인 화면은\n * `실행 중 0 · 루프 점유 —` 였다. \"내 도메인은 한가한데 왜 느리지\" 를 설명할 수 없었다.\n *\n * 그렇다고 테넌트에게 남의 부하를 조금 보여 주는 것도 답이 아니다 — 쓸 수 없는 정보이고 봐서도 안 된다.\n * 층이 잘못이었다: 이것은 **프로세스를 운영하는 사람의 계기판**이다. 그래서 도메인으로 자르지 않고\n * (전 테넌트 합산) **슈퍼유저에게만** 연다.\n */\n @Directive('@privilege(superUserGranted: true)')\n @Query(returns => ScalarObject, {\n description:\n 'Event-loop load of every twin in this process, for system operators (superuser only). Deliberately NOT scoped to the caller\\'s domain: the event loop is shared by the whole process, so a tenant-scoped view reports an idle domain while the machine is saturated by someone else. Reports how many instances are running (by mode), how many forecast forks have been created, and per instance how long each kind of work takes (kernel tick, state snapshot, broadcast payload, publish, journal write, live ingest, Monte-Carlo forecast, fork trajectory). Each instance reports both `recent` (the last closed sampling window — what it is doing now) and `total` (cumulative since boot), with `busyRatio` telling how much of the window that twin held the loop. `fleetBusyRatio` above 1 means the twins together cannot finish inside one tick interval, which is when HTTP and subscriptions start to stall. Work that has never been measured is reported as null rather than zero, because zero would read as \"instant\".'\n })\n async twinLoad(@Ctx() context: ResolverContext): Promise<any> {\n const fleet = TwinEngine.fleet() // 프로세스 전체 — 도메인으로 자르지 않는다(위 주석)\n\n /* 도메인 id 는 UUID 라 사람이 못 읽는다. 읽을 수 있는 이름을 채운다 — **모르면 id 를 그대로 둔다**\n (지어내지 않는다). 라이브 인스턴스에는 이미 붙어 있고, 시뮬은 여기서 한 번 조회한다. */\n const ids = [...new Set((fleet.byDomain ?? []).map((d: any) => d.domainId).filter((v: any) => v && v !== '(unknown)'))]\n if (ids.length) {\n const found = await getRepository(Domain).find({ where: { id: In(ids as string[]) } })\n const nameOf = new Map(found.map(d => [d.id, d.subdomain || d.name]))\n for (const d of fleet.byDomain) if (nameOf.get(d.domainId)) d.label = nameOf.get(d.domainId) as string\n for (const r of fleet.slowest ?? []) if (r.domainId && nameOf.get(r.domainId)) r.domainLabel = nameOf.get(r.domainId)\n for (const r of fleet.details ?? []) if (r.domainId && nameOf.get(r.domainId)) r.domainLabel = nameOf.get(r.domainId)\n }\n\n /*\n * 프로세스가 실제로 쓴 CPU — **계기판의 다른 숫자와 뜻이 다르다.**\n *\n * 위의 값들은 트윈의 계측된 작업만 잰 것이라, 그 밖의 일(GraphQL·TypeORM·GC·webpack·HTTP)은\n * 분모에도 분자에도 없다. \"루프 점유 <1%\" 인데 CPU 는 110% 였던 상태를 실제로 겪었다. 두 숫자를\n * 나란히 두어야 **트윈이 범인인지** 가릴 수 있다.\n *\n * 표본 간 차이라 첫 호출은 `null` 이다 — 0 이 아니다.\n */\n const usage = process.cpuUsage()\n const cpu = sampleCpu(CPU_SAMPLER, usage.user + usage.system, Date.now())\n\n return { ...fleet, processCpuRatio: cpu?.ratio ?? null, processCpuSpanMs: cpu?.spanMs ?? null }\n }\n}\n"]}
@@ -0,0 +1,14 @@
1
+ import type { Isa95Concept } from './isa95-coverage.js';
2
+ export declare const EPCIS_CONCEPTS: Isa95Concept[];
3
+ export declare function epcisCoverage(axes: string[]): {
4
+ model: string;
5
+ concepts: (Isa95Concept & {
6
+ axisMissing?: boolean;
7
+ })[];
8
+ totals: {
9
+ concepts: number;
10
+ structure: number;
11
+ behavior: number;
12
+ surface: number;
13
+ };
14
+ };
@@ -0,0 +1,174 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.EPCIS_CONCEPTS = void 0;
4
+ exports.epcisCoverage = epcisCoverage;
5
+ /* Part: EPCIS 2.0 의 사건 종류 — 무엇이 일어났는가. */
6
+ const EVENTS = [
7
+ {
8
+ std: 'ObjectEvent',
9
+ part: 'events',
10
+ label: 'twin.epcis.ObjectEvent',
11
+ axis: 'items',
12
+ structure: 'full',
13
+ behavior: 'full',
14
+ surface: 'full',
15
+ note: 'twin.epcis.note.ObjectEvent'
16
+ },
17
+ {
18
+ std: 'AggregationEvent',
19
+ part: 'events',
20
+ label: 'twin.epcis.AggregationEvent',
21
+ axis: null,
22
+ shownOn: ['items'],
23
+ structure: 'full',
24
+ behavior: 'full',
25
+ surface: 'partial',
26
+ note: 'twin.epcis.note.AggregationEvent'
27
+ },
28
+ {
29
+ std: 'TransactionEvent',
30
+ part: 'events',
31
+ label: 'twin.epcis.TransactionEvent',
32
+ axis: 'orders',
33
+ structure: 'full',
34
+ behavior: 'full',
35
+ surface: 'partial',
36
+ note: 'twin.epcis.note.TransactionEvent'
37
+ },
38
+ {
39
+ std: 'TransformationEvent',
40
+ part: 'events',
41
+ label: 'twin.epcis.TransformationEvent',
42
+ axis: null,
43
+ shownOn: ['items', 'operations'],
44
+ structure: 'full',
45
+ behavior: 'full',
46
+ surface: 'full',
47
+ note: 'twin.epcis.note.TransformationEvent'
48
+ },
49
+ /* EPCIS 2.0 이 더한 사건 — 우리는 타입조차 두지 않았다. 없는 것은 없다고 적는다. */
50
+ {
51
+ std: 'AssociationEvent',
52
+ part: 'events',
53
+ label: 'twin.epcis.AssociationEvent',
54
+ axis: null,
55
+ structure: 'none',
56
+ behavior: 'none',
57
+ surface: 'none',
58
+ note: 'twin.epcis.note.AssociationEvent'
59
+ }
60
+ ];
61
+ /* Part: 사건이 답하는 다섯 가지 — 무엇을·어디서·언제·왜, 그리고 어떤 거래로. */
62
+ const FIELDS = [
63
+ {
64
+ std: 'epcList / quantityList',
65
+ part: 'fields',
66
+ label: 'twin.epcis.identity',
67
+ axis: 'items',
68
+ structure: 'full',
69
+ behavior: 'full',
70
+ surface: 'full',
71
+ note: 'twin.epcis.note.identity'
72
+ },
73
+ {
74
+ std: 'bizStep / disposition (CBV)',
75
+ part: 'fields',
76
+ label: 'twin.epcis.cbv',
77
+ axis: null,
78
+ shownOn: ['items', 'tasks'],
79
+ structure: 'full',
80
+ behavior: 'full',
81
+ surface: 'full',
82
+ note: 'twin.epcis.note.cbv'
83
+ },
84
+ {
85
+ std: 'readPoint / bizLocation',
86
+ part: 'fields',
87
+ label: 'twin.epcis.where',
88
+ axis: 'locations',
89
+ structure: 'full',
90
+ behavior: 'full',
91
+ surface: 'full',
92
+ note: 'twin.epcis.note.where'
93
+ },
94
+ {
95
+ std: 'bizTransactionList',
96
+ part: 'fields',
97
+ label: 'twin.epcis.bizTransaction',
98
+ axis: 'orders',
99
+ structure: 'full',
100
+ behavior: 'full',
101
+ surface: 'partial',
102
+ note: 'twin.epcis.note.bizTransaction'
103
+ },
104
+ {
105
+ std: 'ilmd',
106
+ part: 'fields',
107
+ label: 'twin.epcis.ilmd',
108
+ axis: null,
109
+ shownOn: ['items'],
110
+ structure: 'full',
111
+ behavior: 'full',
112
+ surface: 'partial',
113
+ note: 'twin.epcis.note.ilmd'
114
+ },
115
+ {
116
+ std: 'errorDeclaration',
117
+ part: 'fields',
118
+ label: 'twin.epcis.errorDeclaration',
119
+ axis: null,
120
+ shownOn: ['items'],
121
+ structure: 'full',
122
+ behavior: 'partial',
123
+ surface: 'none',
124
+ note: 'twin.epcis.note.errorDeclaration'
125
+ },
126
+ /* 모양만 있고 아무도 채우지 않는 것들 — 구조만 있음. */
127
+ {
128
+ std: 'sourceList / destinationList',
129
+ part: 'fields',
130
+ label: 'twin.epcis.sourceDestination',
131
+ axis: null,
132
+ structure: 'partial',
133
+ behavior: 'none',
134
+ surface: 'none',
135
+ note: 'twin.epcis.note.sourceDestination'
136
+ },
137
+ {
138
+ std: 'persistentDisposition',
139
+ part: 'fields',
140
+ label: 'twin.epcis.persistentDisposition',
141
+ axis: null,
142
+ structure: 'partial',
143
+ behavior: 'none',
144
+ surface: 'none',
145
+ note: 'twin.epcis.note.persistentDisposition'
146
+ },
147
+ {
148
+ std: 'sensorElementList',
149
+ part: 'fields',
150
+ label: 'twin.epcis.sensorElement',
151
+ axis: null,
152
+ structure: 'partial',
153
+ behavior: 'none',
154
+ surface: 'none',
155
+ note: 'twin.epcis.note.sensorElement'
156
+ }
157
+ ];
158
+ exports.EPCIS_CONCEPTS = [...EVENTS, ...FIELDS];
159
+ function epcisCoverage(axes) {
160
+ const known = new Set(axes);
161
+ const concepts = exports.EPCIS_CONCEPTS.map(c => {
162
+ const axisMissing = !!c.axis && !known.has(c.axis);
163
+ const shownOn = c.shownOn?.filter(a => known.has(a));
164
+ const fixed = shownOn && shownOn.length !== c.shownOn?.length ? { ...c, shownOn } : c;
165
+ return axisMissing ? { ...fixed, structure: 'none', axisMissing } : fixed;
166
+ });
167
+ const count = (k) => concepts.filter(c => c[k] === 'full').length;
168
+ return {
169
+ model: 'GS1 EPCIS 2.0',
170
+ concepts,
171
+ totals: { concepts: concepts.length, structure: count('structure'), behavior: count('behavior'), surface: count('surface') }
172
+ };
173
+ }
174
+ //# sourceMappingURL=epcis-coverage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"epcis-coverage.js","sourceRoot":"","sources":["../../../server/service/twin-model/epcis-coverage.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * GS1 EPCIS 2.0 적합성 기준 — **물류·제조 트윈이 표준의 어디를 채우고 있나.**\n *\n * ── 왜 따로 있나 ────────────────────────────────────────────────────────────\n * 적합성 패널이 ISA-95 하나만 말하고 있었다. 그런데 창고·야드·공장 트윈에서 「무슨 일이 있었나」와\n * 「이 번호가 무엇을 가리키나」를 정하는 것은 EPCIS 다. ISA-95 표만 보이면 그 트윈이 기대는 기준의\n * 절반이 화면에 없다.\n *\n * ── 수준은 근거를 보고 적었다 ───────────────────────────────────────────────\n * 「있다/없다」를 기억으로 적지 않는다. 각 줄의 판단 근거:\n * · 타입 선언 — 커널 `epcis.ts`(이벤트·필드의 모양)\n * · 실제 사용 — 커널 도메인(`mes-kernel`·`face2-adapter`·`observed-reducer`)과 호스트 인제스트\n * · 화면 노출 — 저널·계보·엔티티 360\n * 모양만 있고 아무도 채우지 않는 필드는 **구조만 있음**으로 적는다(그게 사실이다).\n *\n * ISA-95 표와 같은 규율이다(`isa95-coverage.ts` 머리말): 사람이 판단해 적고, 틀리면 여기를 고친다.\n */\nimport type { Isa95Concept, CoverageLevel } from './isa95-coverage.js'\n\n/* Part: EPCIS 2.0 의 사건 종류 — 무엇이 일어났는가. */\nconst EVENTS: Isa95Concept[] = [\n {\n std: 'ObjectEvent',\n part: 'events',\n label: 'twin.epcis.ObjectEvent',\n axis: 'items',\n structure: 'full',\n behavior: 'full',\n surface: 'full',\n note: 'twin.epcis.note.ObjectEvent'\n },\n {\n std: 'AggregationEvent',\n part: 'events',\n label: 'twin.epcis.AggregationEvent',\n axis: null,\n shownOn: ['items'],\n structure: 'full',\n behavior: 'full',\n surface: 'partial',\n note: 'twin.epcis.note.AggregationEvent'\n },\n {\n std: 'TransactionEvent',\n part: 'events',\n label: 'twin.epcis.TransactionEvent',\n axis: 'orders',\n structure: 'full',\n behavior: 'full',\n surface: 'partial',\n note: 'twin.epcis.note.TransactionEvent'\n },\n {\n std: 'TransformationEvent',\n part: 'events',\n label: 'twin.epcis.TransformationEvent',\n axis: null,\n shownOn: ['items', 'operations'],\n structure: 'full',\n behavior: 'full',\n surface: 'full',\n note: 'twin.epcis.note.TransformationEvent'\n },\n /* EPCIS 2.0 이 더한 사건 — 우리는 타입조차 두지 않았다. 없는 것은 없다고 적는다. */\n {\n std: 'AssociationEvent',\n part: 'events',\n label: 'twin.epcis.AssociationEvent',\n axis: null,\n structure: 'none',\n behavior: 'none',\n surface: 'none',\n note: 'twin.epcis.note.AssociationEvent'\n }\n]\n\n/* Part: 사건이 답하는 다섯 가지 — 무엇을·어디서·언제·왜, 그리고 어떤 거래로. */\nconst FIELDS: Isa95Concept[] = [\n {\n std: 'epcList / quantityList',\n part: 'fields',\n label: 'twin.epcis.identity',\n axis: 'items',\n structure: 'full',\n behavior: 'full',\n surface: 'full',\n note: 'twin.epcis.note.identity'\n },\n {\n std: 'bizStep / disposition (CBV)',\n part: 'fields',\n label: 'twin.epcis.cbv',\n axis: null,\n shownOn: ['items', 'tasks'],\n structure: 'full',\n behavior: 'full',\n surface: 'full',\n note: 'twin.epcis.note.cbv'\n },\n {\n std: 'readPoint / bizLocation',\n part: 'fields',\n label: 'twin.epcis.where',\n axis: 'locations',\n structure: 'full',\n behavior: 'full',\n surface: 'full',\n note: 'twin.epcis.note.where'\n },\n {\n std: 'bizTransactionList',\n part: 'fields',\n label: 'twin.epcis.bizTransaction',\n axis: 'orders',\n structure: 'full',\n behavior: 'full',\n surface: 'partial',\n note: 'twin.epcis.note.bizTransaction'\n },\n {\n std: 'ilmd',\n part: 'fields',\n label: 'twin.epcis.ilmd',\n axis: null,\n shownOn: ['items'],\n structure: 'full',\n behavior: 'full',\n surface: 'partial',\n note: 'twin.epcis.note.ilmd'\n },\n {\n std: 'errorDeclaration',\n part: 'fields',\n label: 'twin.epcis.errorDeclaration',\n axis: null,\n shownOn: ['items'],\n structure: 'full',\n behavior: 'partial',\n surface: 'none',\n note: 'twin.epcis.note.errorDeclaration'\n },\n /* 모양만 있고 아무도 채우지 않는 것들 — 구조만 있음. */\n {\n std: 'sourceList / destinationList',\n part: 'fields',\n label: 'twin.epcis.sourceDestination',\n axis: null,\n structure: 'partial',\n behavior: 'none',\n surface: 'none',\n note: 'twin.epcis.note.sourceDestination'\n },\n {\n std: 'persistentDisposition',\n part: 'fields',\n label: 'twin.epcis.persistentDisposition',\n axis: null,\n structure: 'partial',\n behavior: 'none',\n surface: 'none',\n note: 'twin.epcis.note.persistentDisposition'\n },\n {\n std: 'sensorElementList',\n part: 'fields',\n label: 'twin.epcis.sensorElement',\n axis: null,\n structure: 'partial',\n behavior: 'none',\n surface: 'none',\n note: 'twin.epcis.note.sensorElement'\n }\n]\n\nexport const EPCIS_CONCEPTS: Isa95Concept[] = [...EVENTS, ...FIELDS]\n\nexport function epcisCoverage(axes: string[]): {\n model: string\n concepts: (Isa95Concept & { axisMissing?: boolean })[]\n totals: { concepts: number; structure: number; behavior: number; surface: number }\n} {\n const known = new Set(axes)\n const concepts = EPCIS_CONCEPTS.map(c => {\n const axisMissing = !!c.axis && !known.has(c.axis)\n const shownOn = c.shownOn?.filter(a => known.has(a))\n const fixed = shownOn && shownOn.length !== c.shownOn?.length ? { ...c, shownOn } : c\n return axisMissing ? { ...fixed, structure: 'none' as CoverageLevel, axisMissing } : fixed\n })\n const count = (k: 'structure' | 'behavior' | 'surface') => concepts.filter(c => c[k] === 'full').length\n return {\n model: 'GS1 EPCIS 2.0',\n concepts,\n totals: { concepts: concepts.length, structure: count('structure'), behavior: count('behavior'), surface: count('surface') }\n }\n}\n"]}
@@ -0,0 +1,14 @@
1
+ import type { Isa95Concept } from './isa95-coverage.js';
2
+ export declare const IEC61850_CONCEPTS: Isa95Concept[];
3
+ export declare function iec61850Coverage(axes: string[]): {
4
+ model: string;
5
+ concepts: (Isa95Concept & {
6
+ axisMissing?: boolean;
7
+ })[];
8
+ totals: {
9
+ concepts: number;
10
+ structure: number;
11
+ behavior: number;
12
+ surface: number;
13
+ };
14
+ };
@@ -0,0 +1,98 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.IEC61850_CONCEPTS = void 0;
4
+ exports.iec61850Coverage = iec61850Coverage;
5
+ /*
6
+ * Part 7-4 — 논리 노드 클래스. 이 프로파일이 쓰는 것만 적는다(표준 전체를 옮기지 않는다):
7
+ * 계량·계측·개폐·발전·저장.
8
+ */
9
+ const LOGICAL_NODES = [
10
+ /*
11
+ * 계량(적산) — 커넥터가 누적 전력량을 실어 오고 상태가 지점별로 들고 있다. 화면은 계량 컴포넌트가
12
+ * 지점의 값을 보인다.
13
+ */
14
+ {
15
+ std: 'MMTR',
16
+ part: '7-4',
17
+ label: 'twin.iec61850.MMTR',
18
+ axis: 'equipment',
19
+ structure: 'full',
20
+ behavior: 'full',
21
+ surface: 'full',
22
+ note: 'twin.iec61850.note.MMTR'
23
+ },
24
+ /*
25
+ * 계측(유효전력·역률) — 표본이 수요 구간으로 접히고, 구간·피크·계약 대비가 그 위에 선다.
26
+ * 이 프로파일에서 가장 두껍게 채운 자리다.
27
+ */
28
+ {
29
+ std: 'MMXU',
30
+ part: '7-4',
31
+ label: 'twin.iec61850.MMXU',
32
+ axis: 'demandWindows',
33
+ structure: 'full',
34
+ behavior: 'full',
35
+ surface: 'full',
36
+ note: 'twin.iec61850.note.MMXU'
37
+ },
38
+ /*
39
+ * ── 셋이 함께 올라갔다 (2026-08-15) ──────────────────────────────────────
40
+ * 개폐·발전·저장은 오래 「구조만 있음」이었다. 능력이 상태 필드를 선언해 두었는데 **값을 받는 문이
41
+ * 없었기 때문**이다. 이제 셋 다 실제로 돈다: 설비 상태가 계약에 서고(`EquipmentState`), 커널이
42
+ * `energy.equipment` 로 받아 그 설비에 적고, 씬 컴포넌트가 그린다.
43
+ *
44
+ * 세 층을 함께 올리는 이유: 값이 없는데 화면만 만들면 영원히 「—」인 카드가 되고, 화면이 없는데
45
+ * 기능만 올리면 아무도 그 값을 보지 못한다. 「값이 먼저, 컴포넌트는 그 다음」의 결과다.
46
+ */
47
+ {
48
+ std: 'XCBR',
49
+ part: '7-4',
50
+ label: 'twin.iec61850.XCBR',
51
+ axis: null,
52
+ shownOn: ['equipment'],
53
+ structure: 'full',
54
+ behavior: 'full',
55
+ surface: 'full',
56
+ note: 'twin.iec61850.note.XCBR'
57
+ },
58
+ {
59
+ std: 'DPVA',
60
+ part: '7-4',
61
+ label: 'twin.iec61850.DPVA',
62
+ axis: null,
63
+ shownOn: ['equipment'],
64
+ structure: 'full',
65
+ behavior: 'full',
66
+ surface: 'full',
67
+ note: 'twin.iec61850.note.DPVA'
68
+ },
69
+ {
70
+ std: 'ZBAT',
71
+ part: '7-4',
72
+ label: 'twin.iec61850.ZBAT',
73
+ axis: null,
74
+ shownOn: ['equipment'],
75
+ structure: 'full',
76
+ behavior: 'full',
77
+ surface: 'full',
78
+ note: 'twin.iec61850.note.ZBAT'
79
+ }
80
+ ];
81
+ exports.IEC61850_CONCEPTS = [...LOGICAL_NODES]; // vocabulary-guard: allow — 표준 용어(위 주석)
82
+ function iec61850Coverage(axes) {
83
+ const known = new Set(axes);
84
+ const concepts = exports.IEC61850_CONCEPTS.map(c => {
85
+ const axisMissing = !!c.axis && !known.has(c.axis);
86
+ const shownOn = c.shownOn?.filter(a => known.has(a));
87
+ const fixed = shownOn && shownOn.length !== c.shownOn?.length ? { ...c, shownOn } : c;
88
+ return axisMissing ? { ...fixed, structure: 'none', axisMissing } : fixed;
89
+ });
90
+ const count = (k) => concepts.filter(c => c[k] === 'full').length;
91
+ return {
92
+ /* 이름에 (vocabulary) 를 달아 둔다 — 프로토콜 적합성으로 읽히지 않게. */
93
+ model: 'IEC 61850 (vocabulary)',
94
+ concepts,
95
+ totals: { concepts: concepts.length, structure: count('structure'), behavior: count('behavior'), surface: count('surface') }
96
+ };
97
+ }
98
+ //# sourceMappingURL=iec61850-coverage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"iec61850-coverage.js","sourceRoot":"","sources":["../../../server/service/twin-model/iec61850-coverage.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * IEC 61850 적합성 기준 — **에너지 트윈이 표준의 어디를 채우고 있나.**\n *\n * ── 왜 따로 있나 ────────────────────────────────────────────────────────────\n * 인스펙션의 적합성 패널은 트윈 종류와 무관하게 ISA-95 하나만 말하고 있었다. 에너지 트윈에서 그것은\n * 거짓은 아니지만(설비·계층·속성은 에너지에서도 ISA-95 자원이다) **부족하다** — 계량기·차단기·태양광·\n * 축전지의 어휘를 주는 것은 IEC 61850 이고, 무엇이 비었는지도 그 목록으로만 셀 수 있다.\n *\n * ── 주장 수준을 못 박는다 ───────────────────────────────────────────────────\n * 여기서 재는 것은 **어휘와 모델**이다. 프로토콜(MMS·GOOSE·샘플드값)을 구현했다는 뜻이 아니며,\n * 그 주장은 적합성 시험이 뒷받침해야 한다. 표 이름을 「IEC 61850 (vocabulary)」로 쓰는 이유다.\n *\n * ── 값은 관측이 아니라 선언이다 ─────────────────────────────────────────────\n * ISA-95 표와 같은 규율이다(`isa95-coverage.ts` 머리말): 사람이 판단해 적고, 틀리면 여기를 고친다.\n * 자동으로 알 수 있는 것 하나만 자동으로 한다 — 축 이름이 커널 축 목록에 없으면 구조가 없는 것이다.\n */\nimport type { Isa95Concept, CoverageLevel } from './isa95-coverage.js'\n\n/*\n * Part 7-4 — 논리 노드 클래스. 이 프로파일이 쓰는 것만 적는다(표준 전체를 옮기지 않는다):\n * 계량·계측·개폐·발전·저장.\n */\nconst LOGICAL_NODES: Isa95Concept[] = [ // vocabulary-guard: allow — IEC 61850 의 「논리 노드(logical node)」는 표준 용어다(우리가 은퇴시킨 node 어휘가 아니다)\n /*\n * 계량(적산) — 커넥터가 누적 전력량을 실어 오고 상태가 지점별로 들고 있다. 화면은 계량 컴포넌트가\n * 지점의 값을 보인다.\n */\n {\n std: 'MMTR',\n part: '7-4',\n label: 'twin.iec61850.MMTR',\n axis: 'equipment',\n structure: 'full',\n behavior: 'full',\n surface: 'full',\n note: 'twin.iec61850.note.MMTR'\n },\n /*\n * 계측(유효전력·역률) — 표본이 수요 구간으로 접히고, 구간·피크·계약 대비가 그 위에 선다.\n * 이 프로파일에서 가장 두껍게 채운 자리다.\n */\n {\n std: 'MMXU',\n part: '7-4',\n label: 'twin.iec61850.MMXU',\n axis: 'demandWindows',\n structure: 'full',\n behavior: 'full',\n surface: 'full',\n note: 'twin.iec61850.note.MMXU'\n },\n /*\n * ── 셋이 함께 올라갔다 (2026-08-15) ──────────────────────────────────────\n * 개폐·발전·저장은 오래 「구조만 있음」이었다. 능력이 상태 필드를 선언해 두었는데 **값을 받는 문이\n * 없었기 때문**이다. 이제 셋 다 실제로 돈다: 설비 상태가 계약에 서고(`EquipmentState`), 커널이\n * `energy.equipment` 로 받아 그 설비에 적고, 씬 컴포넌트가 그린다.\n *\n * 세 층을 함께 올리는 이유: 값이 없는데 화면만 만들면 영원히 「—」인 카드가 되고, 화면이 없는데\n * 기능만 올리면 아무도 그 값을 보지 못한다. 「값이 먼저, 컴포넌트는 그 다음」의 결과다.\n */\n {\n std: 'XCBR',\n part: '7-4',\n label: 'twin.iec61850.XCBR',\n axis: null,\n shownOn: ['equipment'],\n structure: 'full',\n behavior: 'full',\n surface: 'full',\n note: 'twin.iec61850.note.XCBR'\n },\n {\n std: 'DPVA',\n part: '7-4',\n label: 'twin.iec61850.DPVA',\n axis: null,\n shownOn: ['equipment'],\n structure: 'full',\n behavior: 'full',\n surface: 'full',\n note: 'twin.iec61850.note.DPVA'\n },\n {\n std: 'ZBAT',\n part: '7-4',\n label: 'twin.iec61850.ZBAT',\n axis: null,\n shownOn: ['equipment'],\n structure: 'full',\n behavior: 'full',\n surface: 'full',\n note: 'twin.iec61850.note.ZBAT'\n }\n]\n\nexport const IEC61850_CONCEPTS: Isa95Concept[] = [...LOGICAL_NODES] // vocabulary-guard: allow — 표준 용어(위 주석)\n\nexport function iec61850Coverage(axes: string[]): {\n model: string\n concepts: (Isa95Concept & { axisMissing?: boolean })[]\n totals: { concepts: number; structure: number; behavior: number; surface: number }\n} {\n const known = new Set(axes)\n const concepts = IEC61850_CONCEPTS.map(c => {\n const axisMissing = !!c.axis && !known.has(c.axis)\n const shownOn = c.shownOn?.filter(a => known.has(a))\n const fixed = shownOn && shownOn.length !== c.shownOn?.length ? { ...c, shownOn } : c\n return axisMissing ? { ...fixed, structure: 'none' as CoverageLevel, axisMissing } : fixed\n })\n const count = (k: 'structure' | 'behavior' | 'surface') => concepts.filter(c => c[k] === 'full').length\n return {\n /* 이름에 (vocabulary) 를 달아 둔다 — 프로토콜 적합성으로 읽히지 않게. */\n model: 'IEC 61850 (vocabulary)',\n concepts,\n totals: { concepts: concepts.length, structure: count('structure'), behavior: count('behavior'), surface: count('surface') }\n }\n}\n"]}