@things-factory/headless-twin 10.0.8 → 10.0.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (381) hide show
  1. package/dist-server/engine/attention-digest.d.ts +52 -0
  2. package/dist-server/engine/attention-digest.js +76 -0
  3. package/dist-server/engine/attention-digest.js.map +1 -0
  4. package/dist-server/engine/canonical-ingest.d.ts +2 -2
  5. package/dist-server/engine/canonical-ingest.js +24 -2
  6. package/dist-server/engine/canonical-ingest.js.map +1 -1
  7. package/dist-server/engine/command-routing.d.ts +33 -0
  8. package/dist-server/engine/command-routing.js +53 -0
  9. package/dist-server/engine/command-routing.js.map +1 -0
  10. package/dist-server/engine/energy-topology.d.ts +86 -0
  11. package/dist-server/engine/energy-topology.js +144 -0
  12. package/dist-server/engine/energy-topology.js.map +1 -0
  13. package/dist-server/engine/index.d.ts +11 -0
  14. package/dist-server/engine/index.js +17 -0
  15. package/dist-server/engine/index.js.map +1 -1
  16. package/dist-server/engine/kpi-baseline.d.ts +78 -0
  17. package/dist-server/engine/kpi-baseline.js +123 -0
  18. package/dist-server/engine/kpi-baseline.js.map +1 -0
  19. package/dist-server/engine/kpi-fold.d.ts +137 -1
  20. package/dist-server/engine/kpi-fold.js +225 -0
  21. package/dist-server/engine/kpi-fold.js.map +1 -1
  22. package/dist-server/engine/kpi-query.d.ts +32 -1
  23. package/dist-server/engine/kpi-query.js +262 -20
  24. package/dist-server/engine/kpi-query.js.map +1 -1
  25. package/dist-server/engine/kpi-target.d.ts +17 -0
  26. package/dist-server/engine/kpi-target.js +24 -2
  27. package/dist-server/engine/kpi-target.js.map +1 -1
  28. package/dist-server/engine/live-attentions.d.ts +1 -0
  29. package/dist-server/engine/live-attentions.js +7 -1
  30. package/dist-server/engine/live-attentions.js.map +1 -1
  31. package/dist-server/engine/live-feed-registry.d.ts +25 -0
  32. package/dist-server/engine/live-feed-registry.js +51 -0
  33. package/dist-server/engine/live-feed-registry.js.map +1 -0
  34. package/dist-server/engine/load-meter.d.ts +181 -0
  35. package/dist-server/engine/load-meter.js +267 -0
  36. package/dist-server/engine/load-meter.js.map +1 -0
  37. package/dist-server/engine/local-declarations.d.ts +262 -0
  38. package/dist-server/engine/local-declarations.js +528 -0
  39. package/dist-server/engine/local-declarations.js.map +1 -0
  40. package/dist-server/engine/model-basis.d.ts +39 -6
  41. package/dist-server/engine/model-basis.js +69 -9
  42. package/dist-server/engine/model-basis.js.map +1 -1
  43. package/dist-server/engine/model-vocabulary.d.ts +17 -0
  44. package/dist-server/engine/model-vocabulary.js +81 -0
  45. package/dist-server/engine/model-vocabulary.js.map +1 -0
  46. package/dist-server/engine/oee-accumulator.d.ts +28 -0
  47. package/dist-server/engine/oee-accumulator.js +26 -1
  48. package/dist-server/engine/oee-accumulator.js.map +1 -1
  49. package/dist-server/engine/operation-basis.d.ts +32 -0
  50. package/dist-server/engine/operation-basis.js +87 -0
  51. package/dist-server/engine/operation-basis.js.map +1 -0
  52. package/dist-server/engine/property-effects.d.ts +30 -0
  53. package/dist-server/engine/property-effects.js +192 -0
  54. package/dist-server/engine/property-effects.js.map +1 -0
  55. package/dist-server/engine/runtime-key.d.ts +15 -0
  56. package/dist-server/engine/runtime-key.js +64 -0
  57. package/dist-server/engine/runtime-key.js.map +1 -0
  58. package/dist-server/engine/spec-coverage.js +3 -3
  59. package/dist-server/engine/spec-coverage.js.map +1 -1
  60. package/dist-server/engine/state-axes.d.ts +21 -0
  61. package/dist-server/engine/state-axes.js +59 -0
  62. package/dist-server/engine/state-axes.js.map +1 -0
  63. package/dist-server/engine/structure-diff.js +1 -1
  64. package/dist-server/engine/structure-diff.js.map +1 -1
  65. package/dist-server/engine/travel-estimator.d.ts +9 -2
  66. package/dist-server/engine/travel-estimator.js +11 -6
  67. package/dist-server/engine/travel-estimator.js.map +1 -1
  68. package/dist-server/engine/twin-engine.d.ts +418 -57
  69. package/dist-server/engine/twin-engine.js +1599 -306
  70. package/dist-server/engine/twin-engine.js.map +1 -1
  71. package/dist-server/engine/warm-start.d.ts +93 -13
  72. package/dist-server/engine/warm-start.js +114 -14
  73. package/dist-server/engine/warm-start.js.map +1 -1
  74. package/dist-server/index.d.ts +1 -0
  75. package/dist-server/index.js +17 -11
  76. package/dist-server/index.js.map +1 -1
  77. package/dist-server/migrations/1786000000000-RenameTwinInstanceBoardToModel.d.ts +5 -0
  78. package/dist-server/migrations/1786000000000-RenameTwinInstanceBoardToModel.js +48 -0
  79. package/dist-server/migrations/1786000000000-RenameTwinInstanceBoardToModel.js.map +1 -0
  80. package/dist-server/migrations/index.d.ts +2 -0
  81. package/dist-server/migrations/index.js +10 -0
  82. package/dist-server/migrations/index.js.map +1 -0
  83. package/dist-server/service/index.d.ts +3 -2
  84. package/dist-server/service/index.js +25 -17
  85. package/dist-server/service/index.js.map +1 -1
  86. package/dist-server/service/reference/discovery-result.d.ts +34 -0
  87. package/dist-server/service/reference/discovery-result.js +84 -0
  88. package/dist-server/service/reference/discovery-result.js.map +1 -0
  89. package/dist-server/service/reference/ingest-space.d.ts +53 -0
  90. package/dist-server/service/reference/ingest-space.js +79 -0
  91. package/dist-server/service/reference/ingest-space.js.map +1 -0
  92. package/dist-server/service/reference/knob-defaults.d.ts +20 -0
  93. package/dist-server/service/reference/knob-defaults.js +59 -0
  94. package/dist-server/service/reference/knob-defaults.js.map +1 -0
  95. package/dist-server/service/reference/reference-live.d.ts +23 -0
  96. package/dist-server/service/reference/reference-live.js +123 -6
  97. package/dist-server/service/reference/reference-live.js.map +1 -1
  98. package/dist-server/service/reference/reference-master.d.ts +227 -11
  99. package/dist-server/service/reference/reference-master.js +206 -25
  100. package/dist-server/service/reference/reference-master.js.map +1 -1
  101. package/dist-server/service/reference/reference-resolver.d.ts +33 -3
  102. package/dist-server/service/reference/reference-resolver.js +290 -23
  103. package/dist-server/service/reference/reference-resolver.js.map +1 -1
  104. package/dist-server/service/reference/template-registry.d.ts +19 -3
  105. package/dist-server/service/reference/template-registry.js.map +1 -1
  106. package/dist-server/service/twin-attention/twin-attention-query.d.ts +8 -1
  107. package/dist-server/service/twin-attention/twin-attention-query.js +39 -8
  108. package/dist-server/service/twin-attention/twin-attention-query.js.map +1 -1
  109. package/dist-server/service/twin-audit/command-audit.d.ts +37 -0
  110. package/dist-server/service/twin-audit/command-audit.js +53 -0
  111. package/dist-server/service/twin-audit/command-audit.js.map +1 -0
  112. package/dist-server/service/twin-audit/index.d.ts +4 -0
  113. package/dist-server/service/twin-audit/index.js +8 -0
  114. package/dist-server/service/twin-audit/index.js.map +1 -0
  115. package/dist-server/service/twin-audit/twin-audit-event.d.ts +24 -0
  116. package/dist-server/service/twin-audit/twin-audit-event.js +125 -0
  117. package/dist-server/service/twin-audit/twin-audit-event.js.map +1 -0
  118. package/dist-server/service/twin-audit/twin-audit-query.d.ts +4 -0
  119. package/dist-server/service/twin-audit/twin-audit-query.js +76 -0
  120. package/dist-server/service/twin-audit/twin-audit-query.js.map +1 -0
  121. package/dist-server/service/twin-control/twin-control-mutation.d.ts +2 -0
  122. package/dist-server/service/twin-control/twin-control-mutation.js +50 -11
  123. package/dist-server/service/twin-control/twin-control-mutation.js.map +1 -1
  124. package/dist-server/service/twin-event/twin-event-keys.d.ts +1 -1
  125. package/dist-server/service/twin-event/twin-event-keys.js +1 -1
  126. package/dist-server/service/twin-event/twin-event-keys.js.map +1 -1
  127. package/dist-server/service/twin-event/twin-event.d.ts +14 -5
  128. package/dist-server/service/twin-event/twin-event.js +46 -14
  129. package/dist-server/service/twin-event/twin-event.js.map +1 -1
  130. package/dist-server/service/twin-forecast/forecast-metrics.d.ts +13 -0
  131. package/dist-server/service/twin-forecast/forecast-metrics.js +57 -0
  132. package/dist-server/service/twin-forecast/forecast-metrics.js.map +1 -0
  133. package/dist-server/service/twin-forecast/forecast-tuning.d.ts +12 -0
  134. package/dist-server/service/twin-forecast/forecast-tuning.js +44 -0
  135. package/dist-server/service/twin-forecast/forecast-tuning.js.map +1 -0
  136. package/dist-server/service/twin-forecast/gap-analytics.d.ts +28 -2
  137. package/dist-server/service/twin-forecast/gap-analytics.js +43 -17
  138. package/dist-server/service/twin-forecast/gap-analytics.js.map +1 -1
  139. package/dist-server/service/twin-forecast/twin-forecast-query.js +219 -39
  140. package/dist-server/service/twin-forecast/twin-forecast-query.js.map +1 -1
  141. package/dist-server/service/twin-instance/twin-instance.d.ts +38 -5
  142. package/dist-server/service/twin-instance/twin-instance.js +66 -18
  143. package/dist-server/service/twin-instance/twin-instance.js.map +1 -1
  144. package/dist-server/service/twin-journal/twin-journal-query.d.ts +11 -4
  145. package/dist-server/service/twin-journal/twin-journal-query.js +95 -15
  146. package/dist-server/service/twin-journal/twin-journal-query.js.map +1 -1
  147. package/dist-server/service/twin-lifecycle/twin-lifecycle-mutation.d.ts +11 -5
  148. package/dist-server/service/twin-lifecycle/twin-lifecycle-mutation.js +46 -27
  149. package/dist-server/service/twin-lifecycle/twin-lifecycle-mutation.js.map +1 -1
  150. package/dist-server/service/twin-metrics/twin-metrics-query.d.ts +1 -0
  151. package/dist-server/service/twin-metrics/twin-metrics-query.js +63 -1
  152. package/dist-server/service/twin-metrics/twin-metrics-query.js.map +1 -1
  153. package/dist-server/service/twin-model/epcis-coverage.d.ts +14 -0
  154. package/dist-server/service/twin-model/epcis-coverage.js +174 -0
  155. package/dist-server/service/twin-model/epcis-coverage.js.map +1 -0
  156. package/dist-server/service/twin-model/iec61850-coverage.d.ts +14 -0
  157. package/dist-server/service/twin-model/iec61850-coverage.js +98 -0
  158. package/dist-server/service/twin-model/iec61850-coverage.js.map +1 -0
  159. package/dist-server/service/twin-model/index.d.ts +13 -0
  160. package/dist-server/service/twin-model/index.js +29 -0
  161. package/dist-server/service/twin-model/index.js.map +1 -0
  162. package/dist-server/service/twin-model/isa95-coverage.d.ts +48 -0
  163. package/dist-server/service/twin-model/isa95-coverage.js +155 -0
  164. package/dist-server/service/twin-model/isa95-coverage.js.map +1 -0
  165. package/dist-server/service/twin-model/project-structure.d.ts +16 -0
  166. package/dist-server/service/twin-model/project-structure.js +173 -0
  167. package/dist-server/service/twin-model/project-structure.js.map +1 -0
  168. package/dist-server/service/twin-model/standard-coverage.d.ts +9 -0
  169. package/dist-server/service/twin-model/standard-coverage.js +37 -0
  170. package/dist-server/service/twin-model/standard-coverage.js.map +1 -0
  171. package/dist-server/service/twin-model/twin-equipment.d.ts +64 -0
  172. package/dist-server/service/twin-model/twin-equipment.js +134 -0
  173. package/dist-server/service/twin-model/twin-equipment.js.map +1 -0
  174. package/dist-server/service/twin-model/twin-lineage-query.d.ts +3 -0
  175. package/dist-server/service/twin-model/twin-lineage-query.js +206 -0
  176. package/dist-server/service/twin-model/twin-lineage-query.js.map +1 -0
  177. package/dist-server/service/twin-model/twin-location.d.ts +58 -0
  178. package/dist-server/service/twin-model/twin-location.js +130 -0
  179. package/dist-server/service/twin-model/twin-location.js.map +1 -0
  180. package/dist-server/service/twin-model/twin-model-item-query.d.ts +141 -0
  181. package/dist-server/service/twin-model/twin-model-item-query.js +852 -0
  182. package/dist-server/service/twin-model/twin-model-item-query.js.map +1 -0
  183. package/dist-server/service/twin-model/twin-model-mutation.d.ts +57 -0
  184. package/dist-server/service/twin-model/twin-model-mutation.js +490 -0
  185. package/dist-server/service/twin-model/twin-model-mutation.js.map +1 -0
  186. package/dist-server/service/twin-model/twin-model-query.d.ts +43 -0
  187. package/dist-server/service/twin-model/twin-model-query.js +551 -0
  188. package/dist-server/service/twin-model/twin-model-query.js.map +1 -0
  189. package/dist-server/service/twin-model/twin-model-tree-query.d.ts +3 -0
  190. package/dist-server/service/twin-model/twin-model-tree-query.js +158 -0
  191. package/dist-server/service/twin-model/twin-model-tree-query.js.map +1 -0
  192. package/dist-server/service/twin-model/twin-operation.d.ts +61 -0
  193. package/dist-server/service/twin-model/twin-operation.js +137 -0
  194. package/dist-server/service/twin-model/twin-operation.js.map +1 -0
  195. package/dist-server/service/twin-space/move-space.d.ts +65 -0
  196. package/dist-server/service/twin-space/move-space.js +166 -0
  197. package/dist-server/service/twin-space/move-space.js.map +1 -0
  198. package/dist-server/service/twin-space/space-integrity.d.ts +97 -0
  199. package/dist-server/service/twin-space/space-integrity.js +182 -0
  200. package/dist-server/service/twin-space/space-integrity.js.map +1 -0
  201. package/dist-server/service/twin-space/twin-space-area.js +4 -4
  202. package/dist-server/service/twin-space/twin-space-area.js.map +1 -1
  203. package/dist-server/service/twin-space/twin-space-representation.js +6 -6
  204. package/dist-server/service/twin-space/twin-space-representation.js.map +1 -1
  205. package/dist-server/service/twin-space/twin-space-resolver.d.ts +63 -1
  206. package/dist-server/service/twin-space/twin-space-resolver.js +365 -18
  207. package/dist-server/service/twin-space/twin-space-resolver.js.map +1 -1
  208. package/dist-server/service/twin-space/twin-space.d.ts +17 -1
  209. package/dist-server/service/twin-space/twin-space.js +15 -5
  210. package/dist-server/service/twin-space/twin-space.js.map +1 -1
  211. package/dist-server/service/twin-state/twin-state-subscription.js +1 -1
  212. package/dist-server/service/twin-state/twin-state-subscription.js.map +1 -1
  213. package/dist-server/service/twin-structure/twin-structure.d.ts +1 -1
  214. package/dist-server/service/twin-structure/twin-structure.js +8 -7
  215. package/dist-server/service/twin-structure/twin-structure.js.map +1 -1
  216. package/dist-server/service/twin-target/twin-target-resolver.js +21 -4
  217. package/dist-server/service/twin-target/twin-target-resolver.js.map +1 -1
  218. package/dist-shared/axis-read.d.ts +12 -0
  219. package/dist-shared/axis-read.js +32 -0
  220. package/dist-shared/axis-read.js.map +1 -0
  221. package/dist-shared/entity-delta.d.ts +29 -0
  222. package/dist-shared/entity-delta.js +287 -0
  223. package/dist-shared/entity-delta.js.map +1 -0
  224. package/dist-shared/kpi-broadcast.d.ts +4 -0
  225. package/dist-shared/kpi-broadcast.js +16 -0
  226. package/dist-shared/kpi-broadcast.js.map +1 -0
  227. package/dist-shared/twin-level.d.ts +23 -0
  228. package/dist-shared/twin-level.js +52 -0
  229. package/dist-shared/twin-level.js.map +1 -0
  230. package/package.json +14 -12
  231. package/server/engine/attention-digest.ts +102 -0
  232. package/server/engine/canonical-ingest.ts +36 -4
  233. package/server/engine/command-routing.ts +67 -0
  234. package/server/engine/energy-topology.ts +189 -0
  235. package/server/engine/index.ts +17 -0
  236. package/server/engine/kpi-baseline.ts +202 -0
  237. package/server/engine/kpi-fold.ts +389 -1
  238. package/server/engine/kpi-query.ts +295 -22
  239. package/server/engine/kpi-target.ts +24 -2
  240. package/server/engine/live-attentions.ts +7 -2
  241. package/server/engine/live-feed-registry.ts +58 -0
  242. package/server/engine/load-meter.ts +384 -0
  243. package/server/engine/local-declarations.ts +700 -0
  244. package/server/engine/model-basis.ts +78 -10
  245. package/server/engine/model-vocabulary.ts +82 -0
  246. package/server/engine/oee-accumulator.ts +34 -1
  247. package/server/engine/operation-basis.ts +100 -0
  248. package/server/engine/property-effects.ts +199 -0
  249. package/server/engine/runtime-key.ts +58 -0
  250. package/server/engine/spec-coverage.ts +3 -3
  251. package/server/engine/state-axes.ts +60 -0
  252. package/server/engine/structure-diff.ts +1 -1
  253. package/server/engine/travel-estimator.ts +11 -6
  254. package/server/engine/twin-engine.ts +1710 -297
  255. package/server/engine/warm-start.ts +207 -22
  256. package/server/index.ts +13 -10
  257. package/server/migrations/1786000000000-RenameTwinInstanceBoardToModel.ts +43 -0
  258. package/server/migrations/index.ts +7 -0
  259. package/server/service/index.ts +9 -1
  260. package/server/service/reference/discovery-result.ts +95 -0
  261. package/server/service/reference/ingest-space.ts +104 -0
  262. package/server/service/reference/knob-defaults.ts +59 -0
  263. package/server/service/reference/reference-live.ts +121 -6
  264. package/server/service/reference/reference-master.ts +362 -35
  265. package/server/service/reference/reference-resolver.ts +307 -23
  266. package/server/service/reference/template-registry.ts +20 -5
  267. package/server/service/twin-attention/twin-attention-query.ts +43 -6
  268. package/server/service/twin-audit/command-audit.ts +81 -0
  269. package/server/service/twin-audit/index.ts +5 -0
  270. package/server/service/twin-audit/twin-audit-event.ts +112 -0
  271. package/server/service/twin-audit/twin-audit-query.ts +72 -0
  272. package/server/service/twin-control/twin-control-mutation.ts +53 -13
  273. package/server/service/twin-event/twin-event-keys.ts +1 -1
  274. package/server/service/twin-event/twin-event.ts +52 -15
  275. package/server/service/twin-forecast/forecast-metrics.ts +60 -0
  276. package/server/service/twin-forecast/forecast-tuning.ts +40 -0
  277. package/server/service/twin-forecast/gap-analytics.ts +48 -9
  278. package/server/service/twin-forecast/twin-forecast-query.ts +211 -38
  279. package/server/service/twin-instance/twin-instance.ts +127 -22
  280. package/server/service/twin-journal/twin-journal-query.ts +108 -14
  281. package/server/service/twin-lifecycle/twin-lifecycle-mutation.ts +40 -23
  282. package/server/service/twin-metrics/twin-metrics-query.ts +60 -3
  283. package/server/service/twin-model/epcis-coverage.ts +195 -0
  284. package/server/service/twin-model/iec61850-coverage.ts +117 -0
  285. package/server/service/twin-model/index.ts +25 -0
  286. package/server/service/twin-model/isa95-coverage.ts +190 -0
  287. package/server/service/twin-model/project-structure.ts +199 -0
  288. package/server/service/twin-model/standard-coverage.ts +35 -0
  289. package/server/service/twin-model/twin-equipment.ts +150 -0
  290. package/server/service/twin-model/twin-lineage-query.ts +193 -0
  291. package/server/service/twin-model/twin-location.ts +140 -0
  292. package/server/service/twin-model/twin-model-item-query.ts +848 -0
  293. package/server/service/twin-model/twin-model-mutation.ts +503 -0
  294. package/server/service/twin-model/twin-model-query.ts +537 -0
  295. package/server/service/twin-model/twin-model-tree-query.ts +180 -0
  296. package/server/service/twin-model/twin-operation.ts +150 -0
  297. package/server/service/twin-space/move-space.ts +262 -0
  298. package/server/service/twin-space/space-integrity.ts +293 -0
  299. package/server/service/twin-space/twin-space-area.ts +4 -4
  300. package/server/service/twin-space/twin-space-representation.ts +6 -6
  301. package/server/service/twin-space/twin-space-resolver.ts +365 -22
  302. package/server/service/twin-space/twin-space.ts +40 -6
  303. package/server/service/twin-state/twin-state-subscription.ts +1 -1
  304. package/server/service/twin-structure/twin-structure.ts +10 -7
  305. package/server/service/twin-target/twin-target-resolver.ts +22 -4
  306. package/shared/axis-read.ts +28 -0
  307. package/shared/entity-delta.ts +286 -0
  308. package/shared/kpi-broadcast.ts +13 -0
  309. package/shared/twin-level.ts +48 -0
  310. package/test/adopt-structure-live.test.ts +133 -0
  311. package/test/attention-digest.test.ts +135 -0
  312. package/test/axis-read.test.ts +87 -0
  313. package/test/boot-resume.test.ts +205 -0
  314. package/test/canonical-ingest-vocabularies.test.ts +118 -0
  315. package/test/capability-mapping.test.ts +5 -5
  316. package/test/command-routing.test.ts +61 -0
  317. package/test/declaration-reaches-model.test.ts +178 -0
  318. package/test/discovery-result.test.ts +75 -0
  319. package/test/energy-topology.test.ts +113 -0
  320. package/test/entity-delta.test.ts +195 -25
  321. package/test/event-time-column.test.ts +67 -0
  322. package/test/forecast-metrics.test.ts +71 -0
  323. package/test/forecast-tuning.test.ts +42 -0
  324. package/test/gap-analytics.test.ts +34 -7
  325. package/test/ingest-bench.test.ts +9 -9
  326. package/test/ingest-running-guard.test.ts +136 -0
  327. package/test/ingest-space.test.ts +78 -0
  328. package/test/instance-cache-lifecycle.test.ts +110 -0
  329. package/test/kernel-kind-guard.test.ts +96 -0
  330. package/test/knob-defaults.test.ts +72 -0
  331. package/test/kpi-baseline-db.test.ts +216 -0
  332. package/test/kpi-baseline.test.ts +196 -0
  333. package/test/kpi-fold.test.ts +393 -2
  334. package/test/kpi-query-bench.test.ts +130 -0
  335. package/test/lineage-survives-restart.test.ts +203 -0
  336. package/test/live-feed-registry.test.ts +67 -0
  337. package/test/live-kernel-facts.test.ts +161 -0
  338. package/test/live-mirror-parity.test.ts +54 -14
  339. package/test/load-meter.test.ts +314 -0
  340. package/test/local-declarations.test.ts +554 -0
  341. package/test/master-to-twin.test.ts +128 -36
  342. package/test/model-basis.test.ts +56 -1
  343. package/test/model-vocabulary.test.ts +114 -0
  344. package/test/move-space.test.ts +149 -0
  345. package/test/mutation-gate.test.ts +122 -0
  346. package/test/oee-accumulator.test.ts +90 -7
  347. package/test/operation-basis.test.ts +100 -0
  348. package/test/operations-capability-db.test.ts +165 -0
  349. package/test/project-structure-db.test.ts +226 -0
  350. package/test/projection-reaches-screen.test.ts +272 -0
  351. package/test/property-effects.test.ts +96 -0
  352. package/test/registry-key-guard.test.ts +80 -0
  353. package/test/resync-origin-site.test.ts +77 -0
  354. package/test/runtime-key.test.ts +66 -0
  355. package/test/scale-twin-bench.test.ts +7 -7
  356. package/test/snapshot-freshness.test.ts +60 -0
  357. package/test/space-integrity.test.ts +223 -0
  358. package/test/standard-coverage.test.ts +94 -0
  359. package/test/state-axes.test.ts +74 -0
  360. package/test/streamline-e2e.test.ts +16 -16
  361. package/test/structure-revision-db.test.ts +16 -15
  362. package/test/tenant-registry-db.test.ts +149 -0
  363. package/test/twin-audit.test.ts +83 -0
  364. package/test/twin-model-item-db.test.ts +275 -0
  365. package/test/twin-model-tree-db.test.ts +176 -0
  366. package/test/twin-origin-resync.test.ts +114 -0
  367. package/test/warm-start-seam.test.ts +140 -0
  368. package/test/warm-start.test.ts +223 -4
  369. package/tsconfig.json +6 -1
  370. package/tsconfig.shared.json +23 -0
  371. package/tsconfig.shared.tsbuildinfo +1 -0
  372. package/tsconfig.tsbuildinfo +1 -0
  373. package/dist-server/engine/entity-delta.d.ts +0 -19
  374. package/dist-server/engine/entity-delta.js +0 -161
  375. package/dist-server/engine/entity-delta.js.map +0 -1
  376. package/dist-server/service/twin-event/backfill-keys.d.ts +0 -11
  377. package/dist-server/service/twin-event/backfill-keys.js +0 -63
  378. package/dist-server/service/twin-event/backfill-keys.js.map +0 -1
  379. package/dist-server/tsconfig.tsbuildinfo +0 -1
  380. package/server/engine/entity-delta.ts +0 -169
  381. package/server/service/twin-event/backfill-keys.ts +0 -72
@@ -4,9 +4,26 @@ const tslib_1 = require("tslib");
4
4
  tslib_1.__exportStar(require("./twin-engine.js"), exports);
5
5
  tslib_1.__exportStar(require("./canonical-ingest.js"), exports);
6
6
  tslib_1.__exportStar(require("./kpi-fold.js"), exports);
7
+ tslib_1.__exportStar(require("./property-effects.js"), exports);
8
+ tslib_1.__exportStar(require("./kpi-baseline.js"), exports);
9
+ /* 지표 정본 표(TWIN_METRIC)·목표 판정 — 소비처(twin-ai 등)가 지표 이름을 다시 열거하지 않게. */
10
+ tslib_1.__exportStar(require("./kpi-target.js"), exports);
11
+ tslib_1.__exportStar(require("@things-factory/headless-twin/dist-shared/kpi-broadcast.js"), exports);
12
+ /* 집약 채널 태그(접두사 + 트윈별 태그 생성) — 소비처가 문자열을 다시 적지 않게 내보낸다. */
13
+ tslib_1.__exportStar(require("@things-factory/headless-twin/dist-shared/entity-delta.js"), exports);
14
+ /* 공간 계층의 단계 — 씬 표현·성과 카드·AI 가 같은 목록을 쓴다. */
15
+ tslib_1.__exportStar(require("@things-factory/headless-twin/dist-shared/twin-level.js"), exports);
16
+ /* 부하 계기판 — 어떤 작업이 이벤트 루프를 얼마나 쓰는지. */
17
+ tslib_1.__exportStar(require("./load-meter.js"), exports);
18
+ /* 런타임 레지스트리 키 규약 — 소비처(다른 키 캐시·테스트)가 `:` 를 다시 적지 않게 함께 내보낸다. */
19
+ tslib_1.__exportStar(require("./runtime-key.js"), exports);
7
20
  tslib_1.__exportStar(require("./kpi-query.js"), exports);
8
21
  tslib_1.__exportStar(require("./spec-coverage.js"), exports);
9
22
  tslib_1.__exportStar(require("./travel-estimator.js"), exports);
10
23
  tslib_1.__exportStar(require("./measured-estimator.js"), exports);
11
24
  tslib_1.__exportStar(require("./model-basis.js"), exports);
25
+ tslib_1.__exportStar(require("./live-feed-registry.js"), exports);
26
+ /* 로컬 선언(현장·트윈) — 소비처(twin-ai 도구)가 명세·검사 규칙을 다시 적지 않게. */
27
+ tslib_1.__exportStar(require("./local-declarations.js"), exports);
28
+ tslib_1.__exportStar(require("./operation-basis.js"), exports);
12
29
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../server/engine/index.ts"],"names":[],"mappings":";;;AAAA,2DAAgC;AAChC,gEAAqC;AACrC,wDAA6B;AAC7B,yDAA8B;AAC9B,6DAAkC;AAClC,gEAAqC;AACrC,kEAAuC;AACvC,2DAAgC","sourcesContent":["export * from './twin-engine.js'\nexport * from './canonical-ingest.js'\nexport * from './kpi-fold.js'\nexport * from './kpi-query.js'\nexport * from './spec-coverage.js'\nexport * from './travel-estimator.js'\nexport * from './measured-estimator.js'\nexport * from './model-basis.js'\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../server/engine/index.ts"],"names":[],"mappings":";;;AAAA,2DAAgC;AAChC,gEAAqC;AACrC,wDAA6B;AAC7B,gEAAqC;AACrC,4DAAiC;AACjC,qEAAqE;AACrE,0DAA+B;AAC/B,qGAA0E;AAC1E,0DAA0D;AAC1D,oGAAyE;AACzE,4CAA4C;AAC5C,kGAAuE;AACvE,sCAAsC;AACtC,0DAA+B;AAC/B,gEAAgE;AAChE,2DAAgC;AAChC,yDAA8B;AAC9B,6DAAkC;AAClC,gEAAqC;AACrC,kEAAuC;AACvC,2DAAgC;AAChC,kEAAuC;AACvC,yDAAyD;AACzD,kEAAuC;AACvC,+DAAoC","sourcesContent":["export * from './twin-engine.js'\nexport * from './canonical-ingest.js'\nexport * from './kpi-fold.js'\nexport * from './property-effects.js'\nexport * from './kpi-baseline.js'\n/* 지표 정본 표(TWIN_METRIC)·목표 판정 — 소비처(twin-ai 등)가 지표 이름을 다시 열거하지 않게. */\nexport * from './kpi-target.js'\nexport * from '@things-factory/headless-twin/dist-shared/kpi-broadcast.js'\n/* 집약 채널 태그(접두사 + 트윈별 태그 생성) — 소비처가 문자열을 다시 적지 않게 내보낸다. */\nexport * from '@things-factory/headless-twin/dist-shared/entity-delta.js'\n/* 공간 계층의 단계 — 씬 표현·성과 카드·AI 가 같은 목록을 쓴다. */\nexport * from '@things-factory/headless-twin/dist-shared/twin-level.js'\n/* 부하 계기판 — 어떤 작업이 이벤트 루프를 얼마나 쓰는지. */\nexport * from './load-meter.js'\n/* 런타임 레지스트리 키 규약 — 소비처(다른 키 캐시·테스트)가 `:` 를 다시 적지 않게 함께 내보낸다. */\nexport * from './runtime-key.js'\nexport * from './kpi-query.js'\nexport * from './spec-coverage.js'\nexport * from './travel-estimator.js'\nexport * from './measured-estimator.js'\nexport * from './model-basis.js'\nexport * from './live-feed-registry.js'\n/* 로컬 선언(현장·트윈) — 소비처(twin-ai 도구)가 명세·검사 규칙을 다시 적지 않게. */\nexport * from './local-declarations.js'\nexport * from './operation-basis.js'\n"]}
@@ -0,0 +1,78 @@
1
+ /** 창 하나 — 폴드의 창과 같은 모양(ms). */
2
+ export interface BaselineWindow {
3
+ fromMs: number;
4
+ toMs: number;
5
+ }
6
+ /** 며칠 전 같은 시간대인가, 몇 주 전 같은 요일·시간대인가. */
7
+ export type BaselineStride = 'day' | 'week';
8
+ /** 한 번에 볼 수 있는 과거 구간 수 — 창마다 조회가 한 번 더 붙으므로 상한을 둔다. */
9
+ export declare const MAX_BASELINE_PERIODS = 14;
10
+ /**
11
+ * 비교할 과거 창들 — 현재 창을 **같은 길이로** 하루(또는 한 주)씩 뒤로 옮긴다.
12
+ *
13
+ * 가까운 것부터 준다(어제 → 그제 → …). 화면이 시간 순서를 다시 정하지 않아도 되게.
14
+ *
15
+ * **한계를 밝힌다**: 옮기는 것은 ms 산술이다. 트윈 공간은 고정 오프셋(`utcOffsetMinutes`)을 선언하므로
16
+ * 현재 모델에서는 "같은 시간대" 가 정확하다. 일광절약시간을 쓰는 시간대를 나중에 지원하면 전환이 있는
17
+ * 주의 하루가 한 시간 밀린다 — 그때는 오프셋 기준 캘린더 산술로 바꿔야 한다(지금 지어내지 않는다).
18
+ */
19
+ export declare function baselineWindows(fromMs: number, toMs: number, periods: number, stride?: BaselineStride): BaselineWindow[];
20
+ /** 지표의 성질 — 이름과 값 꺼내기는 호출부(`TWIN_METRIC`)가 소유한다. */
21
+ export interface MetricSpecLike {
22
+ direction: 'higher' | 'lower';
23
+ unit: string;
24
+ }
25
+ /** 과거 창 하나에서 꺼낸 값들 — 그 창에 기록이 없으면 값이 `undefined` 다. */
26
+ export interface BaselineSampleInput<M extends string> {
27
+ fromMs: number;
28
+ toMs: number;
29
+ values: Partial<Record<M, number | undefined>>;
30
+ }
31
+ /** 과거 창 하나의 결과 — **결측도 남긴다**(빼면 "지난 7일" 이 거짓이 된다). */
32
+ export interface BaselineSample {
33
+ fromMs: number;
34
+ toMs: number;
35
+ measured: boolean;
36
+ value?: number;
37
+ }
38
+ /** 왜 자리를 말하지 않는지. */
39
+ export type BaselineNoPosition = 'no-current' | 'too-few-samples';
40
+ /**
41
+ * 분포 안에서 현재의 자리 — **방향을 적용한 읽기.**
42
+ *
43
+ * 임의의 임계가 없다: 표본 전부보다 좋으면 `best`, 전부보다 나쁘면 `worst`, 그 사이는 중앙값을 기준으로
44
+ * 구분한다. `typical` 은 중앙값과 **같을 때**만이다.
45
+ */
46
+ export type BaselineStanding = 'best' | 'better-than-typical' | 'typical' | 'worse-than-typical' | 'worst';
47
+ export interface MetricBaseline<M extends string = string> {
48
+ metric: M;
49
+ direction: 'higher' | 'lower';
50
+ unit: string;
51
+ /** 지금 창의 값 — 없으면 `undefined`(0 이 아니다). */
52
+ current?: number;
53
+ /** 요청한 과거 창 전부(결측 포함, 가까운 것부터). */
54
+ samples: BaselineSample[];
55
+ /** 요청한 창 수 / 그중 실제로 측정된 수 — 둘을 함께 봐야 "지난 7일" 을 믿을 수 있다. */
56
+ requested: number;
57
+ measuredCount: number;
58
+ /** 측정된 표본만의 요약. 표본이 없으면 전부 `undefined`. */
59
+ min?: number;
60
+ median?: number;
61
+ max?: number;
62
+ /** 측정 표본을 오름차순으로 놓았을 때 현재 값이 몇 번째인가(1 = 가장 작음) — **방향 없는 사실**. */
63
+ rank?: number;
64
+ /** 그 순위를 셀 때의 표본 수(+현재 1). 순위만 보면 표본이 둘이었는지 열이었는지 알 수 없다. */
65
+ rankOf?: number;
66
+ /** 방향을 적용한 읽기. 말할 수 없으면 없다. */
67
+ standing?: BaselineStanding;
68
+ /** 자리를 말하지 않은 이유. */
69
+ noPosition?: BaselineNoPosition;
70
+ }
71
+ /**
72
+ * 지표별로 분포와 자리를 만든다.
73
+ *
74
+ * @param specs 지표 성질(방향·단위). 열거된 지표만 낸다 — 모르는 이름을 짐작하지 않는다.
75
+ * @param current 지금 창에서 꺼낸 값들(결측은 `undefined`).
76
+ * @param inputs 과거 창들에서 꺼낸 값들(가까운 것부터).
77
+ */
78
+ export declare function summarizeBaseline<M extends string>(specs: Readonly<Record<M, MetricSpecLike>>, current: Partial<Record<M, number | undefined>>, inputs: readonly BaselineSampleInput<M>[]): MetricBaseline<M>[];
@@ -0,0 +1,123 @@
1
+ "use strict";
2
+ /*
3
+ * 여러 구간 동시 비교 — **"어제보다 나아졌다" 를 넘어선다.** 순수.
4
+ *
5
+ * ── 왜 필요한가 (성과 맵 ③) ─────────────────────────────────────────────────
6
+ * 지금 비교 축은 **한 구간**만 본다(직전 구간 또는 하루 전). 그래서 판단이 딱 한 문장에서 멈춘다:
7
+ * "어제보다 나아졌다." **어제도 나빴으면 그 말은 쓸모가 없다.** 목표(`kpi-target`)가 그 한계를 한쪽에서
8
+ * 풀지만, 목표를 아직 걸지 않은 현장은 여전히 자기 숫자가 평소와 같은지도 알 수 없다.
9
+ *
10
+ * 최근 며칠의 **같은 시간대**를 함께 접으면 목표 없이도 말할 수 있다 — "오늘 이 시간대는 지난 7일 중
11
+ * 가장 낮다." 그것이 이 파일이 만드는 것이다: 창 여러 개의 **분포**와 그 안에서 현재의 **자리**.
12
+ *
13
+ * ── 이 파일이 하지 않는 것 ──────────────────────────────────────────────────
14
+ * · **지표 어휘를 모른다.** 이름·방향·값 꺼내는 법은 `kpi-target.ts` 의 `TWIN_METRIC` 이 소유한다. 여기는
15
+ * **이미 꺼낸 값**만 받는다 — 어휘가 두 곳에 생기면 그 사이에 매핑 표가 자라고 그것이 곧 방언이다.
16
+ * (덤으로: 이 모듈은 런타임 import 가 없어 테스트가 소스를 그대로 든다.)
17
+ * · **DB·조회를 모른다.** 창 목록만 계산하고, 그 창을 읽는 것은 호출부의 일이다.
18
+ * · **좋고 나쁨의 임계를 발명하지 않는다.** "평소보다 나쁘다" 는 중앙값과의 비교로만 말한다 — 임의의
19
+ * 퍼센트 임계를 두면 그 숫자가 곧 숨은 목표가 된다.
20
+ *
21
+ * ── 정직성 규율 (이 도메인의 핵심) ──────────────────────────────────────────
22
+ * ① **결측 창을 조용히 빼지 않는다.** 요청한 창 전부를 `measured` 와 함께 돌려주고, 분포는 측정된
23
+ * 것만으로 만든다. 빼고 말하면 "지난 7일" 이 실제로는 3일이었다는 것을 아무도 모른다.
24
+ * ② **표본이 둘 미만이면 자리를 말하지 않는다.** 하나를 분포라 부를 수 없다.
25
+ * ③ **현재 값이 없으면 자리를 말하지 않는다.** 결측을 0 으로 읽으면 시간 지표는 "낮을수록 좋다" 라서
26
+ * **일이 없던 구간이 최고 성적**으로 보고된다(목표 판정에서 이미 겪은 결함이다).
27
+ * ④ 왜 말하지 않는지를 `reason` 으로 밝힌다 — 조용히 빈 값을 주면 화면이 "분포 없음" 을 "평범함" 으로
28
+ * 그린다.
29
+ */
30
+ Object.defineProperty(exports, "__esModule", { value: true });
31
+ exports.MAX_BASELINE_PERIODS = void 0;
32
+ exports.baselineWindows = baselineWindows;
33
+ exports.summarizeBaseline = summarizeBaseline;
34
+ /** 한 번에 볼 수 있는 과거 구간 수 — 창마다 조회가 한 번 더 붙으므로 상한을 둔다. */
35
+ exports.MAX_BASELINE_PERIODS = 14;
36
+ const MS_DAY = 24 * 60 * 60_000;
37
+ /**
38
+ * 비교할 과거 창들 — 현재 창을 **같은 길이로** 하루(또는 한 주)씩 뒤로 옮긴다.
39
+ *
40
+ * 가까운 것부터 준다(어제 → 그제 → …). 화면이 시간 순서를 다시 정하지 않아도 되게.
41
+ *
42
+ * **한계를 밝힌다**: 옮기는 것은 ms 산술이다. 트윈 공간은 고정 오프셋(`utcOffsetMinutes`)을 선언하므로
43
+ * 현재 모델에서는 "같은 시간대" 가 정확하다. 일광절약시간을 쓰는 시간대를 나중에 지원하면 전환이 있는
44
+ * 주의 하루가 한 시간 밀린다 — 그때는 오프셋 기준 캘린더 산술로 바꿔야 한다(지금 지어내지 않는다).
45
+ */
46
+ function baselineWindows(fromMs, toMs, periods, stride = 'day') {
47
+ if (!Number.isFinite(fromMs) || !Number.isFinite(toMs) || toMs <= fromMs)
48
+ return [];
49
+ const count = Math.max(0, Math.min(exports.MAX_BASELINE_PERIODS, Math.floor(periods)));
50
+ const step = stride === 'week' ? 7 * MS_DAY : MS_DAY;
51
+ return Array.from({ length: count }, (_, i) => ({
52
+ fromMs: fromMs - step * (i + 1),
53
+ toMs: toMs - step * (i + 1)
54
+ }));
55
+ }
56
+ /** 중앙값 — 짝수 개면 가운데 둘의 평균(폴드의 분위수 규약과 달리 여기 표본은 아주 작다). */
57
+ function medianOf(sorted) {
58
+ if (!sorted.length)
59
+ return undefined;
60
+ const mid = sorted.length >> 1;
61
+ return sorted.length % 2 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2;
62
+ }
63
+ /**
64
+ * 지표별로 분포와 자리를 만든다.
65
+ *
66
+ * @param specs 지표 성질(방향·단위). 열거된 지표만 낸다 — 모르는 이름을 짐작하지 않는다.
67
+ * @param current 지금 창에서 꺼낸 값들(결측은 `undefined`).
68
+ * @param inputs 과거 창들에서 꺼낸 값들(가까운 것부터).
69
+ */
70
+ function summarizeBaseline(specs, current, inputs) {
71
+ const out = [];
72
+ for (const metric of Object.keys(specs)) {
73
+ const spec = specs[metric];
74
+ const samples = inputs.map(s => {
75
+ const v = s.values[metric];
76
+ const measured = typeof v === 'number' && Number.isFinite(v);
77
+ return measured ? { fromMs: s.fromMs, toMs: s.toMs, measured: true, value: v } : { fromMs: s.fromMs, toMs: s.toMs, measured: false };
78
+ });
79
+ const values = samples.filter(s => s.measured).map(s => s.value);
80
+ const sorted = [...values].sort((a, b) => a - b);
81
+ const cur = current[metric];
82
+ const hasCurrent = typeof cur === 'number' && Number.isFinite(cur);
83
+ const row = {
84
+ metric,
85
+ direction: spec.direction,
86
+ unit: spec.unit,
87
+ ...(hasCurrent ? { current: cur } : {}),
88
+ samples,
89
+ requested: inputs.length,
90
+ measuredCount: values.length,
91
+ ...(sorted.length ? { min: sorted[0], median: medianOf(sorted), max: sorted[sorted.length - 1] } : {})
92
+ };
93
+ /* 자리를 말할 수 없는 두 경우 — 이유를 밝힌다(조용한 빈 값이 "평범함" 으로 읽힌다). */
94
+ if (!hasCurrent)
95
+ row.noPosition = 'no-current';
96
+ else if (sorted.length < 2)
97
+ row.noPosition = 'too-few-samples';
98
+ else {
99
+ const value = cur;
100
+ const median = medianOf(sorted);
101
+ /* 오름차순에서 몇 번째인가 — 같은 값은 뒤에 놓는다(동률이면 "더 큰 쪽" 으로 세지 않는다). */
102
+ row.rank = sorted.filter(v => v < value).length + 1;
103
+ row.rankOf = sorted.length + 1;
104
+ const higherIsBetter = spec.direction === 'higher';
105
+ const beatsAll = higherIsBetter ? value > sorted[sorted.length - 1] : value < sorted[0];
106
+ const losesToAll = higherIsBetter ? value < sorted[0] : value > sorted[sorted.length - 1];
107
+ /* 중앙값보다 큰 것이 "좋은" 쪽인가 — 방향을 여기 한 번만 적용한다. */
108
+ const aboveMedianIsBetter = higherIsBetter === value > median;
109
+ row.standing = beatsAll
110
+ ? 'best'
111
+ : losesToAll
112
+ ? 'worst'
113
+ : value === median
114
+ ? 'typical'
115
+ : aboveMedianIsBetter
116
+ ? 'better-than-typical'
117
+ : 'worse-than-typical';
118
+ }
119
+ out.push(row);
120
+ }
121
+ return out;
122
+ }
123
+ //# sourceMappingURL=kpi-baseline.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kpi-baseline.js","sourceRoot":"","sources":["../../server/engine/kpi-baseline.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * 여러 구간 동시 비교 — **\"어제보다 나아졌다\" 를 넘어선다.** 순수.\n *\n * ── 왜 필요한가 (성과 맵 ③) ─────────────────────────────────────────────────\n * 지금 비교 축은 **한 구간**만 본다(직전 구간 또는 하루 전). 그래서 판단이 딱 한 문장에서 멈춘다:\n * \"어제보다 나아졌다.\" **어제도 나빴으면 그 말은 쓸모가 없다.** 목표(`kpi-target`)가 그 한계를 한쪽에서\n * 풀지만, 목표를 아직 걸지 않은 현장은 여전히 자기 숫자가 평소와 같은지도 알 수 없다.\n *\n * 최근 며칠의 **같은 시간대**를 함께 접으면 목표 없이도 말할 수 있다 — \"오늘 이 시간대는 지난 7일 중\n * 가장 낮다.\" 그것이 이 파일이 만드는 것이다: 창 여러 개의 **분포**와 그 안에서 현재의 **자리**.\n *\n * ── 이 파일이 하지 않는 것 ──────────────────────────────────────────────────\n * · **지표 어휘를 모른다.** 이름·방향·값 꺼내는 법은 `kpi-target.ts` 의 `TWIN_METRIC` 이 소유한다. 여기는\n * **이미 꺼낸 값**만 받는다 — 어휘가 두 곳에 생기면 그 사이에 매핑 표가 자라고 그것이 곧 방언이다.\n * (덤으로: 이 모듈은 런타임 import 가 없어 테스트가 소스를 그대로 든다.)\n * · **DB·조회를 모른다.** 창 목록만 계산하고, 그 창을 읽는 것은 호출부의 일이다.\n * · **좋고 나쁨의 임계를 발명하지 않는다.** \"평소보다 나쁘다\" 는 중앙값과의 비교로만 말한다 — 임의의\n * 퍼센트 임계를 두면 그 숫자가 곧 숨은 목표가 된다.\n *\n * ── 정직성 규율 (이 도메인의 핵심) ──────────────────────────────────────────\n * ① **결측 창을 조용히 빼지 않는다.** 요청한 창 전부를 `measured` 와 함께 돌려주고, 분포는 측정된\n * 것만으로 만든다. 빼고 말하면 \"지난 7일\" 이 실제로는 3일이었다는 것을 아무도 모른다.\n * ② **표본이 둘 미만이면 자리를 말하지 않는다.** 하나를 분포라 부를 수 없다.\n * ③ **현재 값이 없으면 자리를 말하지 않는다.** 결측을 0 으로 읽으면 시간 지표는 \"낮을수록 좋다\" 라서\n * **일이 없던 구간이 최고 성적**으로 보고된다(목표 판정에서 이미 겪은 결함이다).\n * ④ 왜 말하지 않는지를 `reason` 으로 밝힌다 — 조용히 빈 값을 주면 화면이 \"분포 없음\" 을 \"평범함\" 으로\n * 그린다.\n */\n\n/** 창 하나 — 폴드의 창과 같은 모양(ms). */\nexport interface BaselineWindow {\n fromMs: number\n toMs: number\n}\n\n/** 며칠 전 같은 시간대인가, 몇 주 전 같은 요일·시간대인가. */\nexport type BaselineStride = 'day' | 'week'\n\n/** 한 번에 볼 수 있는 과거 구간 수 — 창마다 조회가 한 번 더 붙으므로 상한을 둔다. */\nexport const MAX_BASELINE_PERIODS = 14\n\nconst MS_DAY = 24 * 60 * 60_000\n\n/**\n * 비교할 과거 창들 — 현재 창을 **같은 길이로** 하루(또는 한 주)씩 뒤로 옮긴다.\n *\n * 가까운 것부터 준다(어제 → 그제 → …). 화면이 시간 순서를 다시 정하지 않아도 되게.\n *\n * **한계를 밝힌다**: 옮기는 것은 ms 산술이다. 트윈 공간은 고정 오프셋(`utcOffsetMinutes`)을 선언하므로\n * 현재 모델에서는 \"같은 시간대\" 가 정확하다. 일광절약시간을 쓰는 시간대를 나중에 지원하면 전환이 있는\n * 주의 하루가 한 시간 밀린다 — 그때는 오프셋 기준 캘린더 산술로 바꿔야 한다(지금 지어내지 않는다).\n */\nexport function baselineWindows(\n fromMs: number,\n toMs: number,\n periods: number,\n stride: BaselineStride = 'day'\n): BaselineWindow[] {\n if (!Number.isFinite(fromMs) || !Number.isFinite(toMs) || toMs <= fromMs) return []\n const count = Math.max(0, Math.min(MAX_BASELINE_PERIODS, Math.floor(periods)))\n const step = stride === 'week' ? 7 * MS_DAY : MS_DAY\n\n return Array.from({ length: count }, (_, i) => ({\n fromMs: fromMs - step * (i + 1),\n toMs: toMs - step * (i + 1)\n }))\n}\n\n/** 지표의 성질 — 이름과 값 꺼내기는 호출부(`TWIN_METRIC`)가 소유한다. */\nexport interface MetricSpecLike {\n direction: 'higher' | 'lower'\n unit: string\n}\n\n/** 과거 창 하나에서 꺼낸 값들 — 그 창에 기록이 없으면 값이 `undefined` 다. */\nexport interface BaselineSampleInput<M extends string> {\n fromMs: number\n toMs: number\n values: Partial<Record<M, number | undefined>>\n}\n\n/** 과거 창 하나의 결과 — **결측도 남긴다**(빼면 \"지난 7일\" 이 거짓이 된다). */\nexport interface BaselineSample {\n fromMs: number\n toMs: number\n measured: boolean\n value?: number\n}\n\n/** 왜 자리를 말하지 않는지. */\nexport type BaselineNoPosition = 'no-current' | 'too-few-samples'\n\n/**\n * 분포 안에서 현재의 자리 — **방향을 적용한 읽기.**\n *\n * 임의의 임계가 없다: 표본 전부보다 좋으면 `best`, 전부보다 나쁘면 `worst`, 그 사이는 중앙값을 기준으로\n * 구분한다. `typical` 은 중앙값과 **같을 때**만이다.\n */\nexport type BaselineStanding = 'best' | 'better-than-typical' | 'typical' | 'worse-than-typical' | 'worst'\n\nexport interface MetricBaseline<M extends string = string> {\n metric: M\n direction: 'higher' | 'lower'\n unit: string\n /** 지금 창의 값 — 없으면 `undefined`(0 이 아니다). */\n current?: number\n /** 요청한 과거 창 전부(결측 포함, 가까운 것부터). */\n samples: BaselineSample[]\n /** 요청한 창 수 / 그중 실제로 측정된 수 — 둘을 함께 봐야 \"지난 7일\" 을 믿을 수 있다. */\n requested: number\n measuredCount: number\n /** 측정된 표본만의 요약. 표본이 없으면 전부 `undefined`. */\n min?: number\n median?: number\n max?: number\n /** 측정 표본을 오름차순으로 놓았을 때 현재 값이 몇 번째인가(1 = 가장 작음) — **방향 없는 사실**. */\n rank?: number\n /** 그 순위를 셀 때의 표본 수(+현재 1). 순위만 보면 표본이 둘이었는지 열이었는지 알 수 없다. */\n rankOf?: number\n /** 방향을 적용한 읽기. 말할 수 없으면 없다. */\n standing?: BaselineStanding\n /** 자리를 말하지 않은 이유. */\n noPosition?: BaselineNoPosition\n}\n\n/** 중앙값 — 짝수 개면 가운데 둘의 평균(폴드의 분위수 규약과 달리 여기 표본은 아주 작다). */\nfunction medianOf(sorted: readonly number[]): number | undefined {\n if (!sorted.length) return undefined\n const mid = sorted.length >> 1\n return sorted.length % 2 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2\n}\n\n/**\n * 지표별로 분포와 자리를 만든다.\n *\n * @param specs 지표 성질(방향·단위). 열거된 지표만 낸다 — 모르는 이름을 짐작하지 않는다.\n * @param current 지금 창에서 꺼낸 값들(결측은 `undefined`).\n * @param inputs 과거 창들에서 꺼낸 값들(가까운 것부터).\n */\nexport function summarizeBaseline<M extends string>(\n specs: Readonly<Record<M, MetricSpecLike>>,\n current: Partial<Record<M, number | undefined>>,\n inputs: readonly BaselineSampleInput<M>[]\n): MetricBaseline<M>[] {\n const out: MetricBaseline<M>[] = []\n\n for (const metric of Object.keys(specs) as M[]) {\n const spec = specs[metric]\n const samples: BaselineSample[] = inputs.map(s => {\n const v = s.values[metric]\n const measured = typeof v === 'number' && Number.isFinite(v)\n return measured ? { fromMs: s.fromMs, toMs: s.toMs, measured: true, value: v as number } : { fromMs: s.fromMs, toMs: s.toMs, measured: false }\n })\n\n const values = samples.filter(s => s.measured).map(s => s.value as number)\n const sorted = [...values].sort((a, b) => a - b)\n const cur = current[metric]\n const hasCurrent = typeof cur === 'number' && Number.isFinite(cur)\n\n const row: MetricBaseline<M> = {\n metric,\n direction: spec.direction,\n unit: spec.unit,\n ...(hasCurrent ? { current: cur as number } : {}),\n samples,\n requested: inputs.length,\n measuredCount: values.length,\n ...(sorted.length ? { min: sorted[0], median: medianOf(sorted), max: sorted[sorted.length - 1] } : {})\n }\n\n /* 자리를 말할 수 없는 두 경우 — 이유를 밝힌다(조용한 빈 값이 \"평범함\" 으로 읽힌다). */\n if (!hasCurrent) row.noPosition = 'no-current'\n else if (sorted.length < 2) row.noPosition = 'too-few-samples'\n else {\n const value = cur as number\n const median = medianOf(sorted) as number\n /* 오름차순에서 몇 번째인가 — 같은 값은 뒤에 놓는다(동률이면 \"더 큰 쪽\" 으로 세지 않는다). */\n row.rank = sorted.filter(v => v < value).length + 1\n row.rankOf = sorted.length + 1\n\n const higherIsBetter = spec.direction === 'higher'\n const beatsAll = higherIsBetter ? value > sorted[sorted.length - 1] : value < sorted[0]\n const losesToAll = higherIsBetter ? value < sorted[0] : value > sorted[sorted.length - 1]\n /* 중앙값보다 큰 것이 \"좋은\" 쪽인가 — 방향을 여기 한 번만 적용한다. */\n const aboveMedianIsBetter = higherIsBetter === value > median\n\n row.standing = beatsAll\n ? 'best'\n : losesToAll\n ? 'worst'\n : value === median\n ? 'typical'\n : aboveMedianIsBetter\n ? 'better-than-typical'\n : 'worse-than-typical'\n }\n\n out.push(row)\n }\n\n return out\n}\n"]}
@@ -1,4 +1,4 @@
1
- import { type WorkCalendarEntry } from '@operato/twin-kernel';
1
+ import { type ElectricityCost, type TariffDeclaration, type WorkCalendarEntry } from '@operato/twin-kernel';
2
2
  /** 저널 한 줄 — 필요한 것만(엔티티·typeorm 비의존). */
3
3
  export interface KpiEvent {
4
4
  eventType?: string;
@@ -56,6 +56,117 @@ export interface KpiResult {
56
56
  tasks: number;
57
57
  orders: number;
58
58
  };
59
+ /**
60
+ * 에너지 — **이 창의 전력량과 원단위.** 에너지 트윈이 없으면 `undefined`(0 이 아니다).
61
+ *
62
+ * ── 왜 KPI 폴드 안에 있나 (2026-08-14, §10 7단계) ─────────────────────────
63
+ * 원단위는 분자(전력량)와 분모(산출)를 **같은 창**에서 나눈 값이다. 질의를 따로 두면 두 창을 맞추는
64
+ * 일이 소비처의 몫이 되고, 어긋난 두 사실을 나눈 숫자는 아무것도 뜻하지 않는다(커널의 규칙이 그것을
65
+ * 거절한다). 같은 폴드 안에 두면 창이 하나뿐이라 어긋날 수 없다.
66
+ *
67
+ * 분자는 **저널의 구간 마감 사건**에서 온다(`energy.demand.window`) — 커널 상태가 아니라 저널이므로
68
+ * 멈춘 트윈·과거 구간에도 답할 수 있다.
69
+ */
70
+ energy?: {
71
+ /** 이 창의 전력량. 셀 수 있는 구간이 없으면 `undefined`. */
72
+ kWh?: number;
73
+ /**
74
+ * 이 창의 **최대수요**(kW)와 그것이 난 구간의 시작.
75
+ *
76
+ * 전력량과 다른 질문이다: 전력량은 요금의 사용량 부분이고, 최대수요는 **기본요금**과 계약 위반을
77
+ * 정한다. 평균으로 갈음할 수 없어서 커널이 구간의 최대들 중 가장 큰 값을 따로 낸다.
78
+ */
79
+ peakKW?: number;
80
+ peakStartMs?: number;
81
+ /**
82
+ * 그 피크 구간에서 **설비가 끌어당긴** 최대(발전·방전 차감 전)와 그 차이(깎인 양).
83
+ *
84
+ * 요금은 계통 수요(`peakKW`)로 매겨지지만, 「무엇이 그 수요를 만들었나」와 「무엇이 덜어냈나」는
85
+ * 이 두 수의 간격이 답한다. 발전·축전을 선언하지 않은 현장에서는 없다(차감할 것이 없다).
86
+ */
87
+ peakGrossKW?: number;
88
+ shavedKW?: number;
89
+ /** 파생의 근거 — 구간 평균부하 × 길이(`mean-kw`). */
90
+ basis: string;
91
+ /** 센 구간 수 · 못 센 구간 수(계측이 끊긴 구간) — 조용히 자르지 않는다. */
92
+ counted: number;
93
+ skipped: number;
94
+ /** 완료 오더당 전력량 — 답할 수 없으면 `null` 과 이유. */
95
+ intensity: {
96
+ value: number | null;
97
+ unit?: string;
98
+ reason?: string;
99
+ };
100
+ /**
101
+ * 배전 계통의 **자리별 몫** — 「이 분기가 얼마 썼나」.
102
+ *
103
+ * ── 이것은 측정이 아니라 배분이다 ──────────────────────────────────────
104
+ * 저널에 있는 에너지 사실은 **현장 총량 한 줄**이다(뿌리 계량기 합). 계량기별 구간 사실이 없으므로
105
+ * 자리별 몫은 **나눈 것**이고, 무엇으로 나눴는지(`weightKind`)를 값과 함께 낸다. 근거를 감추면
106
+ * 나눈 숫자가 잰 숫자와 같은 무게로 읽힌다 — CBAM·ESG 보고에서 첫 질문에 깨지는 지점이다.
107
+ *
108
+ * 가중치는 **작업 가동시간**이다. 정격(kW)으로 나누지 않는다: 시뮬 트윈의 총부하가 정격의 합으로
109
+ * 파생되므로, 그 총량을 다시 정격으로 나누면 넣은 값이 그대로 나오는 동어반복이 된다.
110
+ *
111
+ * 그래서 이 값에는 **가정이 하나 실려 있다**: 전기가 가동시간에 비례한다. 공조·조명처럼 공정에
112
+ * 붙지 않는 부하도 그 비율에 섞인다 — 화면이 그 문장을 함께 말해야 한다.
113
+ */
114
+ byFeeder?: {
115
+ /** 자리(분기·수전) id. 자리를 모르는 자원은 `unknown` 으로 모인다(조용히 버리지 않는다). */
116
+ key: string;
117
+ kWh: number;
118
+ /**
119
+ * 이 몫의 근거 — **커널이 정한다.**
120
+ *
121
+ * 선언된 부하가 여럿이면 배분(`apportioned`)이다. 선언된 부하가 **하나뿐인** 현장에서는 커널이
122
+ * 「그 계량기는 그것만 잰다」고 읽어 `measured` 를 낸다 — 모델이 그렇게 말했으므로 그것도 사실이다.
123
+ * 우리가 어느 쪽으로도 낙인찍지 않는다.
124
+ */
125
+ basis: 'apportioned' | 'measured';
126
+ /**
127
+ * 무엇으로 나눴나 — **배분일 때만** 있다.
128
+ *
129
+ * 분기마다 계량기가 있는 현장에서는 나누지 않는다(그 값이 곧 계량이다). 그때 이 칸을
130
+ * `runtimeMs` 로 채우면 「가동시간으로 나눴다」는 없는 사실을 말하게 된다.
131
+ */
132
+ weightKind?: 'runtimeMs';
133
+ /** 이 자리의 몫이 전체의 얼마였나 — 배분일 때만. */
134
+ weightShare?: number;
135
+ /**
136
+ * **피크가 찍힌 순간** 이 분기가 얼마였나(kW) — 요금이 걸린 수다.
137
+ *
138
+ * 창의 평균이나 합이 아니다: 최대수요를 낸 구간의 그 순간 값이다(다른 순간을 섞으면 「어느 분기를
139
+ * 깎아야 피크가 내려가나」의 답이 흐려진다). 그 구간에 이 분기의 표본이 없었으면 **없다**.
140
+ */
141
+ peakKW?: number;
142
+ }[];
143
+ /**
144
+ * 나누지 못한 양과 그 이유 — **조용히 0 으로 만들지 않는다.**
145
+ * `no-weights` = 그 창에 돌아간 자원이 없다(가동시간이 전부 0). 그러면 총량은 그대로 남는다.
146
+ */
147
+ unattributedKWh?: number;
148
+ /**
149
+ * 왜 나누지 못했나(커널 코드) — `no-weights`(가동시간이 전부 0) · `no-consumers`(덮는 대상 미선언) ·
150
+ * `not-submetered`(뿌리에서 재었으나 하위 계량이 덮지 않는 부분 — 결손이 아니라 계량 범위의 사실).
151
+ */
152
+ unattributedReason?: string;
153
+ /**
154
+ * 분기 계량의 합이 뿌리 계량보다 **큰** 양 — 두 계량기의 회계가 다르다는 사실이다.
155
+ *
156
+ * 값을 깎아 맞추지 않는다: 그것은 계량을 우리가 고쳐 쓰는 일이고, 어느 쪽이 맞는지는 현장이 안다.
157
+ */
158
+ submeterExcessKWh?: number;
159
+ /**
160
+ * 배분에서 **빠진 자원 수** — 그 창에 가동 기록이 없는 설비다. 0kWh 를 주지 않는 대신 몇 대가
161
+ * 빠졌는지 센다(대기전력이 있는 설비에서 0 은 곧 거짓이다).
162
+ */
163
+ excludedResources?: number;
164
+ /**
165
+ * 이 창의 전기요금 — **청구서가 아니다.** 계산과 그 한계(TOU·래칫·최소청구액 미모델)는 커널
166
+ * (`electricityCost`)이 갖고 여기서는 나른다. 단가가 없으면 `reason: 'no-tariff'` 로 온다.
167
+ */
168
+ cost?: ElectricityCost;
169
+ };
59
170
  /** 리드타임 — 생성(created)에서 완료까지. 사용자가 체감하는 소요 시간. */
60
171
  leadTime: DurationStats;
61
172
  /** 작업시간 — 착수(in-progress)에서 완료까지. 대기를 뺀 실작업. */
@@ -106,6 +217,31 @@ export interface KpiFoldOptions {
106
217
  * 호출부가 넘긴다. 없으면 구역 축은 'unknown' 으로 모인다(조용히 다른 축으로 바꾸지 않는다).
107
218
  */
108
219
  locationArea?: Record<string, string>;
220
+ /**
221
+ * 자원 → **배전 계통의 자리**(분기·수전) 지도.
222
+ *
223
+ * ── 왜 밖에서 오나 ───────────────────────────────────────────────────────
224
+ * 이벤트에는 전기 계통이 없다. 「이 설비가 어느 분기에서 먹는가」는 에너지 트윈의 모델이 아는
225
+ * 선언이고(설비 하나에 사실 둘 — 공장에서는 자원, 전기에서는 부하), 그 선언을 읽는 것은 호출부의 일이다.
226
+ *
227
+ * 없으면 분기 분해를 **하지 않는다** — 자리를 모르는 채 나누면 그 숫자는 아무 것의 몫도 아니다.
228
+ */
229
+ resourceFeeder?: Record<string, string>;
230
+ /**
231
+ * **계량 지점 → 그것이 재는 분기** 지도.
232
+ *
233
+ * 있으면 분기별 전력량을 계량으로 읽는다(배분보다 강한 근거). 이 지도는 모델의 선언에서 오고
234
+ * (계량기의 자리), 그것을 읽는 것은 호출부의 일이다 — 폴드는 저널만 본다.
235
+ */
236
+ meterFeeder?: Record<string, string>;
237
+ /**
238
+ * 선언된 **전기요금 단가** — 수를 금액으로 바꾸는 유일한 입력.
239
+ *
240
+ * 이벤트에는 요금이 없다. 단가는 현장의 계약이고(에너지 트윈 모델의 수전 자리에 선언한다), 그것을
241
+ * 읽는 것은 호출부의 일이다. 없으면 금액을 만들지 않는다 — 짐작한 단가로 낸 금액은 숫자가 있다는
242
+ * 것만으로 사람을 결정으로 밀어붙인다.
243
+ */
244
+ tariff?: TariffDeclaration;
109
245
  /**
110
246
  * 교대 선언 — **이벤트에는 교대가 없다**(시각만 있다). 현장 마스터가 아는 값이라 호출부가 넘긴다.
111
247
  * 없으면 교대 축은 'unknown' 으로 모인다(다른 축으로 조용히 바꾸지 않는다).