@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
@@ -0,0 +1,206 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TwinLineageQuery = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const type_graphql_1 = require("type-graphql");
6
+ const shell_1 = require("@things-factory/shell");
7
+ const twin_instance_js_1 = require("../twin-instance/twin-instance.js");
8
+ const twin_event_js_1 = require("../twin-event/twin-event.js");
9
+ /*
10
+ * 계보 — **이 개체는 무엇에서 나왔나**, 그리고 **설계대로 만들어졌나.**
11
+ *
12
+ * ── 구성과 계보는 주어가 다르다 ────────────────────────────────────────────────
13
+ * 구성(BOM)의 주어는 품목 **정의**다 — "트레일러는 프레임 1개로 만든다"(선언).
14
+ * 계보의 주어는 품목 **개체**다 — "이 트레일러는 저 프레임 로트에서 나왔다"(관측).
15
+ * 둘은 같은 모양이라 겹쳐 볼 수 있고, **그 차이가 곧 진단**이다: "설계는 1개인데 이건 2개를 먹었다".
16
+ * 트윈이 아니면 못 하는 답이고, 이 질의가 그 답의 재료다.
17
+ *
18
+ * ── 어디서 읽나 ────────────────────────────────────────────────────────────────
19
+ * 계보는 상태가 아니라 **저널**에 있다(EPCIS `TransformationEvent` 의 입력·산출 목록). 상태는
20
+ * 지금 무엇이 있는지만 알고, 없어진 원자재가 무엇이었는지는 이벤트만 안다.
21
+ *
22
+ * ── 규모 ───────────────────────────────────────────────────────────────────────
23
+ * 입력 목록은 **컬럼이 아니라 봉투 안**에 있어서 인덱스로 좁힐 수 없다. 그래서 변환 이벤트만
24
+ * 창으로 끊어 읽고, **끊었으면 끊었다고 말한다.** 그 사실을 감추면 "이 개체는 원자재가 없다" 로
25
+ * 읽히고, 계보에서 그 오해는 리콜 범위를 좁게 잡는 결과로 이어진다.
26
+ */
27
+ /** GS1 SGTIN 의 **품목 참조** — 개체(serial)를 떼고 품목만 남긴다(`…sgtin:0614141.077101.42` → `077101`). */
28
+ function itemRefOf(epc) {
29
+ const m = /sgtin:[^.]+\.([^.]+)\./.exec(epc);
30
+ return m ? m[1] : null;
31
+ }
32
+ let TwinLineageQuery = class TwinLineageQuery {
33
+ async twinItemLineage(instanceId, epc, context, maxDepth, window) {
34
+ const domainId = context.state.domain.id;
35
+ const inst = await (0, shell_1.getRepository)(twin_instance_js_1.TwinInstance).findOne({ where: { domain: { id: domainId }, instanceId } });
36
+ if (!inst)
37
+ return null;
38
+ const depthCap = Math.min(Math.max(maxDepth ?? 6, 1), 12);
39
+ const windowCap = Math.min(Math.max(window ?? 5000, 1), 50000);
40
+ /*
41
+ * 변환 이벤트만 읽는다. **최신부터** 읽는다 — 계보를 묻는 개체는 대개 최근에 만들어진 것이고,
42
+ * 창을 넘겨 잘릴 때 잃는 쪽이 오래된 것이어야 덜 아프다.
43
+ */
44
+ const rows = await (0, shell_1.getRepository)(twin_event_js_1.TwinEvent).find({
45
+ where: { domain: { id: domainId }, instanceId, eventType: 'epcis.TransformationEvent' },
46
+ order: { eventTime: 'DESC' },
47
+ take: windowCap
48
+ });
49
+ const truncated = rows.length >= windowCap;
50
+ /* 산출 → 그 변환. 한 개체는 한 번만 만들어지지만, 데이터가 그렇지 않을 수 있으므로 목록으로 든다. */
51
+ const madeBy = new Map();
52
+ for (const r of rows) {
53
+ const d = r.payload?.data ?? {};
54
+ const outs = d.outputEPCList ?? [];
55
+ const ins = d.inputEPCList ?? [];
56
+ const step = { inputs: ins, at: r.eventTime, tx: d.bizTransactionList?.[0]?.bizTransaction };
57
+ for (const o of outs) {
58
+ const list = madeBy.get(o) ?? [];
59
+ list.push(step);
60
+ madeBy.set(o, list);
61
+ }
62
+ }
63
+ /* 거슬러 올라간다 — 너비 우선, 지나온 것은 다시 파지 않는다(사이클·공유 부품). */
64
+ const items = [{ epc, itemRef: itemRefOf(epc), depth: 0 }];
65
+ const edges = [];
66
+ const seen = new Set([epc]);
67
+ let depthTruncated = false;
68
+ let frontier = [{ epc, depth: 0 }];
69
+ while (frontier.length) {
70
+ const next = [];
71
+ for (const cur of frontier) {
72
+ if (cur.depth >= depthCap) {
73
+ depthTruncated = true;
74
+ continue;
75
+ }
76
+ for (const step of madeBy.get(cur.epc) ?? []) {
77
+ for (const input of step.inputs) {
78
+ edges.push({ from: cur.epc, to: input, at: step.at, tx: step.tx, depth: cur.depth + 1 });
79
+ if (seen.has(input))
80
+ continue;
81
+ seen.add(input);
82
+ items.push({ epc: input, itemRef: itemRefOf(input), depth: cur.depth + 1 });
83
+ next.push({ epc: input, depth: cur.depth + 1 });
84
+ }
85
+ }
86
+ }
87
+ frontier = next;
88
+ }
89
+ /*
90
+ * **겹쳐 본다.** 이 개체의 품목이 어느 레시피의 산출이면, 그 레시피가 선언한 투입과 실제로
91
+ * 소비된 것을 자재별로 나란히 센다.
92
+ *
93
+ * 개체(EPC)와 자재 키를 잇는 것은 `productionSpec.binding`(자재 키 → GS1 품목 참조)이다.
94
+ * 바인딩이 없으면 **비교하지 않는다** — 짐작으로 이으면 "설계와 다르다" 는 거짓 진단이 나오고,
95
+ * 그건 아무 말도 안 하는 것보다 나쁘다.
96
+ */
97
+ const spec = inst.model?.productionSpec;
98
+ const binding = spec?.binding ?? {};
99
+ const refToMaterial = new Map(Object.entries(binding).map(([mat, ref]) => [String(ref), mat]));
100
+ const materialOf = (e) => {
101
+ const ref = itemRefOf(e);
102
+ return ref ? refToMaterial.get(ref) ?? null : null;
103
+ };
104
+ const myMaterial = materialOf(epc);
105
+ const recipes = spec?.definition?.recipes ?? [];
106
+ const recipe = myMaterial ? recipes.find((r) => (r.outputs ?? []).some((o) => o.material === myMaterial)) : undefined;
107
+ let conformance = null;
108
+ if (!Object.keys(binding).length) {
109
+ conformance = { comparable: false, reason: 'no-binding' };
110
+ }
111
+ else if (!myMaterial) {
112
+ conformance = { comparable: false, reason: 'item-not-bound' };
113
+ }
114
+ else if (!recipe) {
115
+ conformance = { comparable: false, reason: 'no-recipe-for-material' };
116
+ }
117
+ else {
118
+ /*
119
+ * 관측: **바인딩된 자재까지 내려가 센다** — 중간 산물(WIP)은 통과한다.
120
+ *
121
+ * 다단 라인에서 완제품의 직접 투입은 원자재가 아니라 **WIP** 다: 원자재는 첫 공정에서
122
+ * 소비되고 완제품은 마지막 공정에서 나온다. 그래서 직접 투입만 세면 레시피가 선언한 자재와
123
+ * **영원히 안 맞는다**(검사가 이것을 잡았다).
124
+ *
125
+ * WIP 은 라우트의 내부 산물이고 선언된 자재가 아니다 — 레시피는 자재로 말하므로 비교도
126
+ * 자재로 해야 한다. 바인딩에 있으면 거기서 멈추고, 없으면 그 위로 계속 따라간다.
127
+ *
128
+ * 다만 **다른 완제품까지 넘어가지 않는다**: 바인딩된 것을 만나면 멈추므로, 하위조립도
129
+ * 자재로 선언돼 있으면 그 자리에서 세어진다(그 아래는 그 자재 자신의 레시피가 볼 일이다).
130
+ */
131
+ const observed = new Map();
132
+ let unbound = 0;
133
+ const parentsOf = (e) => edges.filter(x => x.from === e).map(x => x.to);
134
+ const walked = new Set([epc]);
135
+ let layer = parentsOf(epc);
136
+ while (layer.length) {
137
+ const deeper = [];
138
+ for (const e of layer) {
139
+ if (walked.has(e))
140
+ continue;
141
+ walked.add(e);
142
+ const mat = materialOf(e);
143
+ if (mat) {
144
+ observed.set(mat, (observed.get(mat) ?? 0) + 1);
145
+ continue;
146
+ }
147
+ const up = parentsOf(e);
148
+ /* 바인딩에도 없고 더 올라갈 곳도 없다 — 정말 모르는 투입이다(수만 낸다). */
149
+ if (!up.length) {
150
+ unbound++;
151
+ continue;
152
+ }
153
+ deeper.push(...up);
154
+ }
155
+ layer = deeper;
156
+ }
157
+ const declared = new Map((recipe.inputs ?? []).map((i) => [i.material, i.qty ?? null]));
158
+ const keys = [...new Set([...declared.keys(), ...observed.keys()])];
159
+ conformance = {
160
+ comparable: true,
161
+ material: myMaterial,
162
+ recipe: recipe.key,
163
+ /* 자재마다 선언·관측을 나란히. 어느 한쪽에만 있는 것도 사실이다(빠뜨리지 않는다). */
164
+ lines: keys.map(m => ({
165
+ material: m,
166
+ declared: declared.has(m) ? declared.get(m) : null,
167
+ observed: observed.get(m) ?? 0,
168
+ matches: declared.get(m) === (observed.get(m) ?? 0)
169
+ })),
170
+ /* 바인딩에 없는 투입 — 설계에 없는 것이 들어갔을 수도, 바인딩이 덜 선언됐을 수도 있다.
171
+ 어느 쪽인지 우리는 모르므로 **수만 낸다.** */
172
+ unboundInputs: unbound
173
+ };
174
+ }
175
+ return {
176
+ instanceId,
177
+ epc,
178
+ itemRef: itemRefOf(epc),
179
+ material: myMaterial,
180
+ items,
181
+ edges,
182
+ conformance,
183
+ /* 무엇을 근거로 답했나 — 창을 넘겨 끊겼으면 계보가 실제보다 얕게 보인다. */
184
+ basis: { source: 'journal', transformationsRead: rows.length, truncated, depthTruncated, depthCap, windowCap }
185
+ };
186
+ }
187
+ };
188
+ exports.TwinLineageQuery = TwinLineageQuery;
189
+ tslib_1.__decorate([
190
+ (0, type_graphql_1.Query)(returns => shell_1.ScalarObject, {
191
+ nullable: true,
192
+ description: 'Where one item came from (observed) laid next to what its type declares (BOM). Walks TransformationEvent input/output lists in the journal; reports the window it read and whether it was truncated. The declared|observed difference is the diagnosis: "the recipe says one frame, this one consumed two".'
193
+ }),
194
+ tslib_1.__param(0, (0, type_graphql_1.Arg)('instanceId')),
195
+ tslib_1.__param(1, (0, type_graphql_1.Arg)('epc')),
196
+ tslib_1.__param(2, (0, type_graphql_1.Ctx)()),
197
+ tslib_1.__param(3, (0, type_graphql_1.Arg)('maxDepth', type => type_graphql_1.Int, { nullable: true })),
198
+ tslib_1.__param(4, (0, type_graphql_1.Arg)('window', type => type_graphql_1.Int, { nullable: true })),
199
+ tslib_1.__metadata("design:type", Function),
200
+ tslib_1.__metadata("design:paramtypes", [String, String, Object, Number, Number]),
201
+ tslib_1.__metadata("design:returntype", Promise)
202
+ ], TwinLineageQuery.prototype, "twinItemLineage", null);
203
+ exports.TwinLineageQuery = TwinLineageQuery = tslib_1.__decorate([
204
+ (0, type_graphql_1.Resolver)()
205
+ ], TwinLineageQuery);
206
+ //# sourceMappingURL=twin-lineage-query.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"twin-lineage-query.js","sourceRoot":"","sources":["../../../server/service/twin-model/twin-lineage-query.ts"],"names":[],"mappings":"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","sourcesContent":["import { Arg, Ctx, Int, Query, Resolver } from 'type-graphql'\n\nimport { ScalarObject, getRepository } from '@things-factory/shell'\n\nimport { TwinInstance } from '../twin-instance/twin-instance.js'\nimport { TwinEvent } from '../twin-event/twin-event.js'\n\n/*\n * 계보 — **이 개체는 무엇에서 나왔나**, 그리고 **설계대로 만들어졌나.**\n *\n * ── 구성과 계보는 주어가 다르다 ────────────────────────────────────────────────\n * 구성(BOM)의 주어는 품목 **정의**다 — \"트레일러는 프레임 1개로 만든다\"(선언).\n * 계보의 주어는 품목 **개체**다 — \"이 트레일러는 저 프레임 로트에서 나왔다\"(관측).\n * 둘은 같은 모양이라 겹쳐 볼 수 있고, **그 차이가 곧 진단**이다: \"설계는 1개인데 이건 2개를 먹었다\".\n * 트윈이 아니면 못 하는 답이고, 이 질의가 그 답의 재료다.\n *\n * ── 어디서 읽나 ────────────────────────────────────────────────────────────────\n * 계보는 상태가 아니라 **저널**에 있다(EPCIS `TransformationEvent` 의 입력·산출 목록). 상태는\n * 지금 무엇이 있는지만 알고, 없어진 원자재가 무엇이었는지는 이벤트만 안다.\n *\n * ── 규모 ───────────────────────────────────────────────────────────────────────\n * 입력 목록은 **컬럼이 아니라 봉투 안**에 있어서 인덱스로 좁힐 수 없다. 그래서 변환 이벤트만\n * 창으로 끊어 읽고, **끊었으면 끊었다고 말한다.** 그 사실을 감추면 \"이 개체는 원자재가 없다\" 로\n * 읽히고, 계보에서 그 오해는 리콜 범위를 좁게 잡는 결과로 이어진다.\n */\n\n/** GS1 SGTIN 의 **품목 참조** — 개체(serial)를 떼고 품목만 남긴다(`…sgtin:0614141.077101.42` → `077101`). */\nfunction itemRefOf(epc: string): string | null {\n const m = /sgtin:[^.]+\\.([^.]+)\\./.exec(epc)\n return m ? m[1] : null\n}\n\n@Resolver()\nexport class TwinLineageQuery {\n @Query(returns => ScalarObject, {\n nullable: true,\n description:\n 'Where one item came from (observed) laid next to what its type declares (BOM). Walks TransformationEvent input/output lists in the journal; reports the window it read and whether it was truncated. The declared|observed difference is the diagnosis: \"the recipe says one frame, this one consumed two\".'\n })\n async twinItemLineage(\n @Arg('instanceId') instanceId: string,\n @Arg('epc') epc: string,\n @Ctx() context: ResolverContext,\n @Arg('maxDepth', type => Int, { nullable: true }) maxDepth?: number,\n @Arg('window', type => Int, { nullable: true }) window?: number\n ): Promise<any | null> {\n const domainId = context.state.domain.id\n const inst = await getRepository(TwinInstance).findOne({ where: { domain: { id: domainId }, instanceId } })\n if (!inst) return null\n\n const depthCap = Math.min(Math.max(maxDepth ?? 6, 1), 12)\n const windowCap = Math.min(Math.max(window ?? 5000, 1), 50000)\n\n /*\n * 변환 이벤트만 읽는다. **최신부터** 읽는다 — 계보를 묻는 개체는 대개 최근에 만들어진 것이고,\n * 창을 넘겨 잘릴 때 잃는 쪽이 오래된 것이어야 덜 아프다.\n */\n const rows: any[] = await getRepository(TwinEvent).find({\n where: { domain: { id: domainId } as any, instanceId, eventType: 'epcis.TransformationEvent' } as any,\n order: { eventTime: 'DESC' } as any,\n take: windowCap\n })\n const truncated = rows.length >= windowCap\n\n /* 산출 → 그 변환. 한 개체는 한 번만 만들어지지만, 데이터가 그렇지 않을 수 있으므로 목록으로 든다. */\n const madeBy = new Map<string, { inputs: string[]; at?: string; tx?: string }[]>()\n for (const r of rows) {\n const d = r.payload?.data ?? {}\n const outs: string[] = d.outputEPCList ?? []\n const ins: string[] = d.inputEPCList ?? []\n const step = { inputs: ins, at: r.eventTime, tx: d.bizTransactionList?.[0]?.bizTransaction }\n for (const o of outs) {\n const list = madeBy.get(o) ?? []\n list.push(step)\n madeBy.set(o, list)\n }\n }\n\n /* 거슬러 올라간다 — 너비 우선, 지나온 것은 다시 파지 않는다(사이클·공유 부품). */\n const items: any[] = [{ epc, itemRef: itemRefOf(epc), depth: 0 }]\n const edges: any[] = []\n const seen = new Set<string>([epc])\n let depthTruncated = false\n let frontier = [{ epc, depth: 0 }]\n while (frontier.length) {\n const next: typeof frontier = []\n for (const cur of frontier) {\n if (cur.depth >= depthCap) { depthTruncated = true; continue }\n for (const step of madeBy.get(cur.epc) ?? []) {\n for (const input of step.inputs) {\n edges.push({ from: cur.epc, to: input, at: step.at, tx: step.tx, depth: cur.depth + 1 })\n if (seen.has(input)) continue\n seen.add(input)\n items.push({ epc: input, itemRef: itemRefOf(input), depth: cur.depth + 1 })\n next.push({ epc: input, depth: cur.depth + 1 })\n }\n }\n }\n frontier = next\n }\n\n /*\n * **겹쳐 본다.** 이 개체의 품목이 어느 레시피의 산출이면, 그 레시피가 선언한 투입과 실제로\n * 소비된 것을 자재별로 나란히 센다.\n *\n * 개체(EPC)와 자재 키를 잇는 것은 `productionSpec.binding`(자재 키 → GS1 품목 참조)이다.\n * 바인딩이 없으면 **비교하지 않는다** — 짐작으로 이으면 \"설계와 다르다\" 는 거짓 진단이 나오고,\n * 그건 아무 말도 안 하는 것보다 나쁘다.\n */\n const spec: any = (inst.model as any)?.productionSpec\n const binding: Record<string, string> = spec?.binding ?? {}\n const refToMaterial = new Map(Object.entries(binding).map(([mat, ref]) => [String(ref), mat]))\n const materialOf = (e: string) => {\n const ref = itemRefOf(e)\n return ref ? refToMaterial.get(ref) ?? null : null\n }\n\n const myMaterial = materialOf(epc)\n const recipes: any[] = spec?.definition?.recipes ?? []\n const recipe = myMaterial ? recipes.find((r: any) => (r.outputs ?? []).some((o: any) => o.material === myMaterial)) : undefined\n\n let conformance: any = null\n if (!Object.keys(binding).length) {\n conformance = { comparable: false, reason: 'no-binding' }\n } else if (!myMaterial) {\n conformance = { comparable: false, reason: 'item-not-bound' }\n } else if (!recipe) {\n conformance = { comparable: false, reason: 'no-recipe-for-material' }\n } else {\n /*\n * 관측: **바인딩된 자재까지 내려가 센다** — 중간 산물(WIP)은 통과한다.\n *\n * 다단 라인에서 완제품의 직접 투입은 원자재가 아니라 **WIP** 다: 원자재는 첫 공정에서\n * 소비되고 완제품은 마지막 공정에서 나온다. 그래서 직접 투입만 세면 레시피가 선언한 자재와\n * **영원히 안 맞는다**(검사가 이것을 잡았다).\n *\n * WIP 은 라우트의 내부 산물이고 선언된 자재가 아니다 — 레시피는 자재로 말하므로 비교도\n * 자재로 해야 한다. 바인딩에 있으면 거기서 멈추고, 없으면 그 위로 계속 따라간다.\n *\n * 다만 **다른 완제품까지 넘어가지 않는다**: 바인딩된 것을 만나면 멈추므로, 하위조립도\n * 자재로 선언돼 있으면 그 자리에서 세어진다(그 아래는 그 자재 자신의 레시피가 볼 일이다).\n */\n const observed = new Map<string, number>()\n let unbound = 0\n const parentsOf = (e: string) => edges.filter(x => x.from === e).map(x => x.to)\n const walked = new Set<string>([epc])\n let layer = parentsOf(epc)\n while (layer.length) {\n const deeper: string[] = []\n for (const e of layer) {\n if (walked.has(e)) continue\n walked.add(e)\n const mat = materialOf(e)\n if (mat) { observed.set(mat, (observed.get(mat) ?? 0) + 1); continue }\n const up = parentsOf(e)\n /* 바인딩에도 없고 더 올라갈 곳도 없다 — 정말 모르는 투입이다(수만 낸다). */\n if (!up.length) { unbound++; continue }\n deeper.push(...up)\n }\n layer = deeper\n }\n const declared = new Map<string, number>((recipe.inputs ?? []).map((i: any) => [i.material, i.qty ?? null]))\n const keys = [...new Set([...declared.keys(), ...observed.keys()])]\n conformance = {\n comparable: true,\n material: myMaterial,\n recipe: recipe.key,\n /* 자재마다 선언·관측을 나란히. 어느 한쪽에만 있는 것도 사실이다(빠뜨리지 않는다). */\n lines: keys.map(m => ({\n material: m,\n declared: declared.has(m) ? declared.get(m) : null,\n observed: observed.get(m) ?? 0,\n matches: declared.get(m) === (observed.get(m) ?? 0)\n })),\n /* 바인딩에 없는 투입 — 설계에 없는 것이 들어갔을 수도, 바인딩이 덜 선언됐을 수도 있다.\n 어느 쪽인지 우리는 모르므로 **수만 낸다.** */\n unboundInputs: unbound\n }\n }\n\n return {\n instanceId,\n epc,\n itemRef: itemRefOf(epc),\n material: myMaterial,\n items,\n edges,\n conformance,\n /* 무엇을 근거로 답했나 — 창을 넘겨 끊겼으면 계보가 실제보다 얕게 보인다. */\n basis: { source: 'journal', transformationsRead: rows.length, truncated, depthTruncated, depthCap, windowCap }\n }\n }\n}\n"]}
@@ -0,0 +1,58 @@
1
+ import { Domain } from '@things-factory/shell';
2
+ import { TwinInstance } from '../twin-instance/twin-instance.js';
3
+ import { TwinArea } from '../twin-space/twin-area.js';
4
+ export declare class TwinLocation {
5
+ readonly id: string;
6
+ domain?: Domain;
7
+ domainId?: string;
8
+ instance?: TwinInstance;
9
+ instanceRowId?: string;
10
+ /** 커널 계약의 `TwinModelDef.locations[].id` **그대로**. 바인딩은 id===id 다(ADR-0014). */
11
+ locationId: string;
12
+ /**
13
+ * **사람이 부르는 이름** — 프레임워크 엔티티 규약의 `name`/`description`.
14
+ *
15
+ * ── 왜 이것이 빠져 있었고, 무엇이 망가졌나 ────────────────────────────────────
16
+ * 투영이 마스터의 `name` 을 버리고 있었다. 그래서 행에는 id 만 남고, 화면은 이름을 만들 재료가
17
+ * 없어 `cut-station` 을 그대로 찍었다 — 그 결함이 목록·표·관계 그림마다 따로 나타나서 표면을
18
+ * 하나씩 고치게 만들었다. 표면의 문제가 아니라 **행에 이름이 없던 문제**다.
19
+ *
20
+ * 원본이 이름을 말하지 않으면 비운다(id 를 이름으로 베끼지 않는다 — 그러면 "이름이 있다" 는
21
+ * 거짓이 되고, 화면이 이름 없음을 알아볼 수 없게 된다).
22
+ */
23
+ name?: string;
24
+ description?: string;
25
+ /**
26
+ * 커널 카탈로그의 자리 타입 키(`storage`·`dock`·`yard-slot`…).
27
+ *
28
+ * **FK 를 걸지 않는다** — 타입 카탈로그는 커널이 SSOT 이고(ADR-0018) DB 로 내리면
29
+ * **커널 어휘의 사본**이 생긴다. 문자열 + 부팅 시 검증이 맞다(ADR-0032-F).
30
+ */
31
+ type: string;
32
+ capacity?: number;
33
+ parallelism?: number;
34
+ /**
35
+ * 속한 구역 — **실제 FK**.
36
+ *
37
+ * 커널 계약에서는 `locations[].parentId` 가 model 안에 대상이 없는 참조였다(마스터에서 구역도
38
+ * 자리와 한 목록이었다가 인제스트가 갈라 `TwinArea` 로 보내기 때문). 커널은 그것을
39
+ * `external: 'space.area'` 로 선언했고, **호스트에서는 평범한 FK 가 된다.**
40
+ */
41
+ area?: TwinArea;
42
+ areaRowId?: string;
43
+ /** GS1 식별자(SGLN 등) — 원본이 주면 싣는다. */
44
+ gs1Id?: string;
45
+ /**
46
+ * 이 자리의 **선언 속성** — 계약전력·요금 단가처럼 자리에 얹히는 수(ISA-95 Property).
47
+ *
48
+ * ── 왜 늦게 생겼나 (2026-08-18) ────────────────────────────────────────────
49
+ * 설비 행에는 이 칸이 있었는데 자리 행에는 없었다. 그래서 수전 지점에 계약전력·요금을 넣어도 **항목
50
+ * 패널의 「원본이 말한 값」에 그 줄이 아예 없었다** — 사용자는 값을 넣고도 화면에서 찾을 수 없었고,
51
+ * 「이 값이 무엇을 바꾸나」는 문장도 붙을 자리가 없었다(조용한 누락).
52
+ */
53
+ properties?: any;
54
+ sourceRef: string;
55
+ ingestedAt: Date;
56
+ createdAt?: Date;
57
+ updatedAt?: Date;
58
+ }
@@ -0,0 +1,130 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TwinLocation = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const typeorm_1 = require("typeorm");
6
+ const type_graphql_1 = require("type-graphql");
7
+ const shell_1 = require("@things-factory/shell");
8
+ const twin_instance_js_1 = require("../twin-instance/twin-instance.js");
9
+ const twin_area_js_1 = require("../twin-space/twin-area.js");
10
+ /*
11
+ * TwinLocation — 이 트윈이 반영하는 **자리**. ISA-95 `OperationalLocation` / EPCIS `SGLN`.
12
+ *
13
+ * ── 왜 테이블인가 (ADR-0032, 2026-08-08) ────────────────────────────────────
14
+ * 지금까지 자리는 `TwinInstance.model` JSON 안에 있었다. 그래서 ⑴ 부분 갱신이 없어 쓰는 쪽이
15
+ * 빠뜨리면 삭제였고 ⑵ 라우트→공정·설비→자리 참조에 FK 를 못 걸어 **끊어진 참조를 화면이 보고만**
16
+ * 했고 ⑶ 5개 드라이버에서 JSON 질의가 이식되지 않아 "설비 몇 대?" 조차 서버가 못 답했다.
17
+ *
18
+ * ── 이것은 **원본에서 온 캐시**다 (ADR-0032-D) ──────────────────────────────
19
+ * 잘 만든 엔티티는 정본처럼 **생겼다.** 그래서 소유 침범을 부른다 — 누군가 "여기에만 있는 컬럼" 을
20
+ * 더하고, 쓰기 경로를 열고, 그러면 `ADR-0028`(트윈은 만들지 않는다)이 무너진다.
21
+ *
22
+ * 규율 셋을 구조로 박는다:
23
+ * · `sourceRef`·`ingestedAt` 필수 — 어디서 언제 왔는지 없는 행은 만들 수 없다
24
+ * · 쓰기 경로는 **인제스트 하나**
25
+ * · **전부 지우고 다시 만들 수 있다** — 원본이 낼 수 없는 값을 여기 두지 않는다
26
+ */
27
+ let TwinLocation = class TwinLocation {
28
+ };
29
+ exports.TwinLocation = TwinLocation;
30
+ tslib_1.__decorate([
31
+ (0, typeorm_1.PrimaryGeneratedColumn)('uuid'),
32
+ (0, type_graphql_1.Field)(type => type_graphql_1.ID),
33
+ tslib_1.__metadata("design:type", String)
34
+ ], TwinLocation.prototype, "id", void 0);
35
+ tslib_1.__decorate([
36
+ (0, typeorm_1.ManyToOne)(type => shell_1.Domain),
37
+ (0, type_graphql_1.Field)(type => shell_1.Domain, { nullable: true, description: 'Owning tenant domain.' }),
38
+ tslib_1.__metadata("design:type", shell_1.Domain)
39
+ ], TwinLocation.prototype, "domain", void 0);
40
+ tslib_1.__decorate([
41
+ (0, typeorm_1.RelationId)((e) => e.domain),
42
+ tslib_1.__metadata("design:type", String)
43
+ ], TwinLocation.prototype, "domainId", void 0);
44
+ tslib_1.__decorate([
45
+ (0, typeorm_1.ManyToOne)(type => twin_instance_js_1.TwinInstance, { onDelete: 'CASCADE' }),
46
+ (0, type_graphql_1.Field)(type => twin_instance_js_1.TwinInstance, { nullable: true, description: 'Owning twin instance.' }),
47
+ tslib_1.__metadata("design:type", twin_instance_js_1.TwinInstance)
48
+ ], TwinLocation.prototype, "instance", void 0);
49
+ tslib_1.__decorate([
50
+ (0, typeorm_1.RelationId)((e) => e.instance),
51
+ tslib_1.__metadata("design:type", String)
52
+ ], TwinLocation.prototype, "instanceRowId", void 0);
53
+ tslib_1.__decorate([
54
+ (0, typeorm_1.Column)(),
55
+ (0, type_graphql_1.Field)({ description: 'Location id as declared by the source (kernel TwinModelDef.locations[].id).' }),
56
+ tslib_1.__metadata("design:type", String)
57
+ ], TwinLocation.prototype, "locationId", void 0);
58
+ tslib_1.__decorate([
59
+ (0, typeorm_1.Column)({ nullable: true }),
60
+ (0, type_graphql_1.Field)({ nullable: true, description: 'Human name as declared by the source. Empty when the source does not name it (never copied from the id).' }),
61
+ tslib_1.__metadata("design:type", String)
62
+ ], TwinLocation.prototype, "name", void 0);
63
+ tslib_1.__decorate([
64
+ (0, typeorm_1.Column)({ nullable: true }),
65
+ (0, type_graphql_1.Field)({ nullable: true, description: 'Longer description when the source provides one.' }),
66
+ tslib_1.__metadata("design:type", String)
67
+ ], TwinLocation.prototype, "description", void 0);
68
+ tslib_1.__decorate([
69
+ (0, typeorm_1.Column)(),
70
+ (0, type_graphql_1.Field)({ description: 'Location type key from the kernel catalog (not an FK — the catalog is the kernel SSOT).' }),
71
+ tslib_1.__metadata("design:type", String)
72
+ ], TwinLocation.prototype, "type", void 0);
73
+ tslib_1.__decorate([
74
+ (0, typeorm_1.Column)({ type: 'int', nullable: true }),
75
+ (0, type_graphql_1.Field)(type => type_graphql_1.Int, { nullable: true, description: 'Declared capacity. Absent when the source does not say.' }),
76
+ tslib_1.__metadata("design:type", Number)
77
+ ], TwinLocation.prototype, "capacity", void 0);
78
+ tslib_1.__decorate([
79
+ (0, typeorm_1.Column)({ type: 'int', nullable: true }),
80
+ (0, type_graphql_1.Field)(type => type_graphql_1.Int, { nullable: true, description: 'How many things can be worked on here at once, when the source declares it.' }),
81
+ tslib_1.__metadata("design:type", Number)
82
+ ], TwinLocation.prototype, "parallelism", void 0);
83
+ tslib_1.__decorate([
84
+ (0, typeorm_1.ManyToOne)(type => twin_area_js_1.TwinArea, { nullable: true, onDelete: 'SET NULL' }),
85
+ (0, type_graphql_1.Field)(type => twin_area_js_1.TwinArea, { nullable: true, description: 'Logical area this location belongs to.' }),
86
+ tslib_1.__metadata("design:type", twin_area_js_1.TwinArea)
87
+ ], TwinLocation.prototype, "area", void 0);
88
+ tslib_1.__decorate([
89
+ (0, typeorm_1.RelationId)((e) => e.area),
90
+ tslib_1.__metadata("design:type", String)
91
+ ], TwinLocation.prototype, "areaRowId", void 0);
92
+ tslib_1.__decorate([
93
+ (0, typeorm_1.Column)({ nullable: true }),
94
+ (0, type_graphql_1.Field)({ nullable: true, description: 'GS1 identifier supplied by the source (e.g. SGLN).' }),
95
+ tslib_1.__metadata("design:type", String)
96
+ ], TwinLocation.prototype, "gs1Id", void 0);
97
+ tslib_1.__decorate([
98
+ (0, typeorm_1.Column)({ type: 'simple-json', nullable: true }),
99
+ (0, type_graphql_1.Field)(type => shell_1.ScalarObject, { nullable: true, description: 'Properties declared on this location (ISA-95 Property: value + dataType + unit of measure). Carries locally-declared numbers too, with source: local.' }),
100
+ tslib_1.__metadata("design:type", Object)
101
+ ], TwinLocation.prototype, "properties", void 0);
102
+ tslib_1.__decorate([
103
+ (0, typeorm_1.Column)(),
104
+ (0, type_graphql_1.Field)({ description: 'Which reference (source system) this row came from. Required — a row with no origin cannot exist.' }),
105
+ tslib_1.__metadata("design:type", String)
106
+ ], TwinLocation.prototype, "sourceRef", void 0);
107
+ tslib_1.__decorate([
108
+ (0, typeorm_1.Column)({ type: 'datetime' }),
109
+ (0, type_graphql_1.Field)({ description: 'When this row was projected from the source.' }),
110
+ tslib_1.__metadata("design:type", Date)
111
+ ], TwinLocation.prototype, "ingestedAt", void 0);
112
+ tslib_1.__decorate([
113
+ (0, typeorm_1.CreateDateColumn)(),
114
+ (0, type_graphql_1.Field)({ nullable: true }),
115
+ tslib_1.__metadata("design:type", Date)
116
+ ], TwinLocation.prototype, "createdAt", void 0);
117
+ tslib_1.__decorate([
118
+ (0, typeorm_1.UpdateDateColumn)(),
119
+ (0, type_graphql_1.Field)({ nullable: true }),
120
+ tslib_1.__metadata("design:type", Date)
121
+ ], TwinLocation.prototype, "updatedAt", void 0);
122
+ exports.TwinLocation = TwinLocation = tslib_1.__decorate([
123
+ (0, typeorm_1.Entity)({ name: 'twin_locations' }),
124
+ (0, typeorm_1.Index)('ix_twin_location_0', (e) => [e.domain, e.instance, e.locationId], { unique: true })
125
+ /* 타입으로 좁혀 보기(인스펙션의 "이 타입의 자리들") — 공정이 타입을 가리키므로 잦은 질의다. */
126
+ ,
127
+ (0, typeorm_1.Index)('ix_twin_location_1', (e) => [e.domain, e.instance, e.type]),
128
+ (0, type_graphql_1.ObjectType)({ description: 'A location reflected into the twin (ISA-95 OperationalLocation / EPCIS SGLN). Projected from the source; never authored here.' })
129
+ ], TwinLocation);
130
+ //# sourceMappingURL=twin-location.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"twin-location.js","sourceRoot":"","sources":["../../../server/service/twin-model/twin-location.ts"],"names":[],"mappings":"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","sourcesContent":["import { CreateDateColumn, Entity, Index, Column, RelationId, ManyToOne, PrimaryGeneratedColumn, UpdateDateColumn } from 'typeorm'\nimport { ObjectType, Field, ID, Int } from 'type-graphql'\n\nimport { Domain, ScalarObject } from '@things-factory/shell'\n\nimport { TwinInstance } from '../twin-instance/twin-instance.js'\nimport { TwinArea } from '../twin-space/twin-area.js'\n\n/*\n * TwinLocation — 이 트윈이 반영하는 **자리**. ISA-95 `OperationalLocation` / EPCIS `SGLN`.\n *\n * ── 왜 테이블인가 (ADR-0032, 2026-08-08) ────────────────────────────────────\n * 지금까지 자리는 `TwinInstance.model` JSON 안에 있었다. 그래서 ⑴ 부분 갱신이 없어 쓰는 쪽이\n * 빠뜨리면 삭제였고 ⑵ 라우트→공정·설비→자리 참조에 FK 를 못 걸어 **끊어진 참조를 화면이 보고만**\n * 했고 ⑶ 5개 드라이버에서 JSON 질의가 이식되지 않아 \"설비 몇 대?\" 조차 서버가 못 답했다.\n *\n * ── 이것은 **원본에서 온 캐시**다 (ADR-0032-D) ──────────────────────────────\n * 잘 만든 엔티티는 정본처럼 **생겼다.** 그래서 소유 침범을 부른다 — 누군가 \"여기에만 있는 컬럼\" 을\n * 더하고, 쓰기 경로를 열고, 그러면 `ADR-0028`(트윈은 만들지 않는다)이 무너진다.\n *\n * 규율 셋을 구조로 박는다:\n * · `sourceRef`·`ingestedAt` 필수 — 어디서 언제 왔는지 없는 행은 만들 수 없다\n * · 쓰기 경로는 **인제스트 하나**\n * · **전부 지우고 다시 만들 수 있다** — 원본이 낼 수 없는 값을 여기 두지 않는다\n */\n@Entity({ name: 'twin_locations' })\n@Index('ix_twin_location_0', (e: TwinLocation) => [e.domain, e.instance, e.locationId], { unique: true })\n/* 타입으로 좁혀 보기(인스펙션의 \"이 타입의 자리들\") — 공정이 타입을 가리키므로 잦은 질의다. */\n@Index('ix_twin_location_1', (e: TwinLocation) => [e.domain, e.instance, e.type])\n@ObjectType({ description: 'A location reflected into the twin (ISA-95 OperationalLocation / EPCIS SGLN). Projected from the source; never authored here.' })\nexport class TwinLocation {\n @PrimaryGeneratedColumn('uuid')\n @Field(type => ID)\n readonly id: string\n\n @ManyToOne(type => Domain)\n @Field(type => Domain, { nullable: true, description: 'Owning tenant domain.' })\n domain?: Domain\n\n @RelationId((e: TwinLocation) => e.domain)\n domainId?: string\n\n /* 이 자리가 속한 트윈. 트윈이 사라지면 그 구조도 함께 사라진다(투영이므로 재생성 가능). */\n @ManyToOne(type => TwinInstance, { onDelete: 'CASCADE' })\n @Field(type => TwinInstance, { nullable: true, description: 'Owning twin instance.' })\n instance?: TwinInstance\n\n @RelationId((e: TwinLocation) => e.instance)\n instanceRowId?: string\n\n /** 커널 계약의 `TwinModelDef.locations[].id` **그대로**. 바인딩은 id===id 다(ADR-0014). */\n @Column()\n @Field({ description: 'Location id as declared by the source (kernel TwinModelDef.locations[].id).' })\n locationId: string\n\n /**\n * **사람이 부르는 이름** — 프레임워크 엔티티 규약의 `name`/`description`.\n *\n * ── 왜 이것이 빠져 있었고, 무엇이 망가졌나 ────────────────────────────────────\n * 투영이 마스터의 `name` 을 버리고 있었다. 그래서 행에는 id 만 남고, 화면은 이름을 만들 재료가\n * 없어 `cut-station` 을 그대로 찍었다 — 그 결함이 목록·표·관계 그림마다 따로 나타나서 표면을\n * 하나씩 고치게 만들었다. 표면의 문제가 아니라 **행에 이름이 없던 문제**다.\n *\n * 원본이 이름을 말하지 않으면 비운다(id 를 이름으로 베끼지 않는다 — 그러면 \"이름이 있다\" 는\n * 거짓이 되고, 화면이 이름 없음을 알아볼 수 없게 된다).\n */\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'Human name as declared by the source. Empty when the source does not name it (never copied from the id).' })\n name?: string\n\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'Longer description when the source provides one.' })\n description?: string\n\n /**\n * 커널 카탈로그의 자리 타입 키(`storage`·`dock`·`yard-slot`…).\n *\n * **FK 를 걸지 않는다** — 타입 카탈로그는 커널이 SSOT 이고(ADR-0018) DB 로 내리면\n * **커널 어휘의 사본**이 생긴다. 문자열 + 부팅 시 검증이 맞다(ADR-0032-F).\n */\n @Column()\n @Field({ description: 'Location type key from the kernel catalog (not an FK — the catalog is the kernel SSOT).' })\n type: string\n\n @Column({ type: 'int', nullable: true })\n @Field(type => Int, { nullable: true, description: 'Declared capacity. Absent when the source does not say.' })\n capacity?: number\n\n @Column({ type: 'int', nullable: true })\n @Field(type => Int, { nullable: true, description: 'How many things can be worked on here at once, when the source declares it.' })\n parallelism?: number\n\n /**\n * 속한 구역 — **실제 FK**.\n *\n * 커널 계약에서는 `locations[].parentId` 가 model 안에 대상이 없는 참조였다(마스터에서 구역도\n * 자리와 한 목록이었다가 인제스트가 갈라 `TwinArea` 로 보내기 때문). 커널은 그것을\n * `external: 'space.area'` 로 선언했고, **호스트에서는 평범한 FK 가 된다.**\n */\n @ManyToOne(type => TwinArea, { nullable: true, onDelete: 'SET NULL' })\n @Field(type => TwinArea, { nullable: true, description: 'Logical area this location belongs to.' })\n area?: TwinArea\n\n @RelationId((e: TwinLocation) => e.area)\n areaRowId?: string\n\n /** GS1 식별자(SGLN 등) — 원본이 주면 싣는다. */\n @Column({ nullable: true })\n @Field({ nullable: true, description: 'GS1 identifier supplied by the source (e.g. SGLN).' })\n gs1Id?: string\n\n /**\n * 이 자리의 **선언 속성** — 계약전력·요금 단가처럼 자리에 얹히는 수(ISA-95 Property).\n *\n * ── 왜 늦게 생겼나 (2026-08-18) ────────────────────────────────────────────\n * 설비 행에는 이 칸이 있었는데 자리 행에는 없었다. 그래서 수전 지점에 계약전력·요금을 넣어도 **항목\n * 패널의 「원본이 말한 값」에 그 줄이 아예 없었다** — 사용자는 값을 넣고도 화면에서 찾을 수 없었고,\n * 「이 값이 무엇을 바꾸나」는 문장도 붙을 자리가 없었다(조용한 누락).\n */\n @Column({ type: 'simple-json', nullable: true })\n @Field(type => ScalarObject, { nullable: true, description: 'Properties declared on this location (ISA-95 Property: value + dataType + unit of measure). Carries locally-declared numbers too, with source: local.' })\n properties?: any\n\n /* ── 캐시라는 사실을 구조로 (ADR-0032-D) ── */\n @Column()\n @Field({ description: 'Which reference (source system) this row came from. Required — a row with no origin cannot exist.' })\n sourceRef: string\n\n @Column({ type: 'datetime' })\n @Field({ description: 'When this row was projected from the source.' })\n ingestedAt: Date\n\n @CreateDateColumn()\n @Field({ nullable: true })\n createdAt?: Date\n\n @UpdateDateColumn()\n @Field({ nullable: true })\n updatedAt?: Date\n}\n"]}
@@ -0,0 +1,141 @@
1
+ /**
2
+ * 항목 조회 — **개념 안의 목록**(③)과 **항목 하나의 상세**(④).
3
+ *
4
+ * ── 이 파일이 도메인을 알지 않는다 ─────────────────────────────────────────────
5
+ * 어느 축이 있고 무엇을 가리키는지는 **커널 카탈로그가 선언**한다(`TWIN_AXES`·`TWIN_RELATIONS`).
6
+ * 그래서 야드의 도크도어나 에너지의 계량기가 늘어도 여기 손댈 것이 없다(ADR-0031). 이 파일에
7
+ * 도메인 이름이 한 번도 나오지 않는 것이 그 증거다.
8
+ *
9
+ * ── 조용히 자르지 않는다 ──────────────────────────────────────────────────────
10
+ * 목록은 항상 **총 건수**와 함께 온다. 그리고 어떻게 셌는지(`basis`)를 밝힌다 — 행이면 DB 가
11
+ * 페이징했고, 문서면 서버가 문서를 통째로 읽어 잘라 냈다. 후자는 규모에서 한계가 되므로 그 사실이
12
+ * 화면에 드러나야 이주 압력이 살아 있다.
13
+ */
14
+ export declare class TwinModelItemQuery {
15
+ twinModelEntities(instanceId: string, axis: string, context: ResolverContext, search?: string, offset?: number, limit?: number,
16
+ /**
17
+ * 어느 시각의 사실인가 — **관측 축에만 뜻이 있다.**
18
+ *
19
+ * 선언 축은 시간축이 없다(그 시점의 선언은 구조 리비전이 답한다). 그래서 문서 축에 이 값을 주면
20
+ * 쓰지 않는다 — 무시했다는 사실은 응답의 `basis` 가 말한다(`document`).
21
+ */
22
+ untilTime?: string): Promise<any | null>;
23
+ /**
24
+ * 행에서 **내부 배관을 걷어낸다** — 사용자가 볼 것은 현장의 사실이지 우리 스키마가 아니다.
25
+ *
26
+ * 손으로 목록을 두지 않는다. 관계와 그 조인 컬럼이 무엇인지는 **엔티티 메타데이터**가 알고,
27
+ * 표로 두면 컬럼이 늘 때마다 여기를 고쳐야 하며 잊으면 그대로 새어 나간다 — 실제로
28
+ * `domainId`·`instanceRowId` 같은 내부 UUID 가 화면까지 갔다.
29
+ *
30
+ * 식별자도 뺀다: 항목 자신의 id 는 이미 `id`/`itemId` 로 나가므로 두 번 보일 이유가 없다.
31
+ */
32
+ private strip;
33
+ /**
34
+ * **자재는 자기 GS1 품목 참조를 안다** — 읽기 경계에서 이어 붙인다.
35
+ *
36
+ * 정본은 `productionSpec.binding` 하나다(커널이 EPC↔자재를 그 표로 잇는다). 그런데 그 사실은
37
+ * **자재마다의 속성**이다: 이 자재가 GS1 품목 몇 번인가, 그래서 개체로 추적되는가(= 계보를 비교할
38
+ * 수 있는가). 자재 목록에 그것이 없으면 자재는 자기 정체를 모르고, 사용자는 그 사실을 딴 축에서
39
+ * 찾아야 한다.
40
+ *
41
+ * 그래서 **저장은 한 곳(binding), 표시는 읽는 자리에서 이어 붙임** — 이름표와 같은 방식이다.
42
+ * 값을 복제해 두면 두 곳이 어긋나고, 어긋난 쪽을 아무도 모른다.
43
+ */
44
+ private withBinding;
45
+ /**
46
+ * **오더가 무엇을 만드나** — GS1 품목 참조를 자재로 다시 계산해 붙인다.
47
+ *
48
+ * 커널의 오더는 품목을 GS1 으로 말한다(`urn:epc:idpat:sgtin:0614141.078101.*`). 사람에게 그 문자열은
49
+ * 아무 뜻이 없고, 그 품목의 이름은 **자재 선언**이 갖고 있다. 둘을 잇는 것은 `productionSpec.binding`
50
+ * (자재 키 → 품목 참조)이므로 **역방향으로 다시 계산한다.**
51
+ *
52
+ * 정본은 그대로 둔다: `gtin` 은 커널이 말한 값이고, 여기서 더하는 것은 **자재 키 하나**다(그 이름은
53
+ * 이름표가 붙인다 — 값을 복제하지 않는다). 다시 계산하지 못하면 아무것도 더하지 않는다: 짐작으로 이으면
54
+ * "이 오더는 저것을 만든다" 는 거짓이 되고, 그건 모르는 것보다 나쁘다.
55
+ */
56
+ private withOrderMaterial;
57
+ /** 행 하나 → 목록 항목. 컬럼 이름을 화면이 알지 않아도 되게 `id` + 나머지로 낸다. */
58
+ private rowItem;
59
+ twinModelItem(instanceId: string, axis: string, itemId: string, context: ResolverContext): Promise<any | null>;
60
+ /**
61
+ * **id → 이름**, 모든 축을 한 표로.
62
+ *
63
+ * ── 왜 이 표가 필요한가 ──────────────────────────────────────────────────────
64
+ * 행은 다른 것을 **id 로** 가리킨다(사람의 `personnelClassIds`, 설비의 `homeLocationId`, 공정의
65
+ * `locationType`). 그 id 의 이름은 **가리켜진 축의 행**에 있으므로, 화면이 그것을 알려면 축을
66
+ * 하나씩 더 읽어야 한다 — 그래서 지금까지 표면마다 그냥 id 를 찍었다(`cutter-op`, `cut-station`).
67
+ * 표면을 하나씩 고치는 것은 끝이 없다: 새 화면은 처음부터 다시 id 를 찍는다.
68
+ *
69
+ * 그래서 **서버가 붙인다.** 서버는 이미 모델을 손에 들고 있고, 어느 축이 있는지는 커널 카탈로그가
70
+ * 선언한다(`TWIN_AXES`) — 도메인이 늘어도 이 함수는 그대로다.
71
+ *
72
+ * 값은 그 항목이 **자기 이름이라고 말한 것**이다(`name`·`label`·`description`). 그중 어떤 것은
73
+ * i18n 키이므로(등급의 `description`) 번역은 화면의 일로 남긴다. 이름이 없으면 표에 넣지 않는다 —
74
+ * 없는 것을 지어내지 않고, 그 경우 화면은 id 를 그대로 보인다.
75
+ */
76
+ private nameIndex;
77
+ /**
78
+ * 이 응답이 **실제로 가리킨 것만** 골라 낸다.
79
+ *
80
+ * 모델 전체 표를 그대로 실으면 자리가 10만인 현장에서 목록 한 페이지에 10만 줄짜리 이름표가
81
+ * 따라온다. 나온 값에 걸리는 것만 싣는다 — 규모에서 성립하는 유일한 방식이다.
82
+ */
83
+ private namesFor;
84
+ /** 등급 id → 표시명 키. 인원·설비·자산·품목 등급을 한 표로 — 어느 자원이든 같은 방식으로 요구된다. */
85
+ private classLabels;
86
+ /**
87
+ * 관측이 본 이 항목 — 커널이 실행 중일 때만. **실행 중이 아니면 `null` 이지 빈 객체가 아니다**
88
+ * (관측이 없는 것과 비교하지 못한 것은 다르다).
89
+ */
90
+ /**
91
+ * 이 공정의 **생산 능력** — 공정 축 항목에만 붙는다.
92
+ *
93
+ * 구간은 **앞으로 24시간**이다. 표준의 약정·가용은 *앞으로 쓸* 용량이므로 지난 구간을 두고 말하면
94
+ * 뜻이 없고, 이 화면에는 사용자가 고른 창이 없다. 그래서 하루를 쓰고 **그 사실을 값과 함께 낸다**
95
+ * (화면이 라벨로 말한다) — 어느 구간의 값인지 모르면 능력은 읽을 수 없다.
96
+ *
97
+ * **수요를 지어내지 않는다.** 여기서 내는 값(상한·약정·달성불가·가용·제약하는 축)은 전부 공급 쪽이다 —
98
+ * 자원 수·사이클·가동률에서 나온다. 수요는 `ok`·병목·하루 상한에만 쓰이고 그것들은 내지 않으므로,
99
+ * 계산에 넘기는 수요 `1` 은 결과에 새지 않는다(성과 화면은 사용자가 걸어 둔 목표를 쓴다 — 그 화면은
100
+ * "목표를 낼 수 있나" 에 답하고 여기서는 "얼마나 더 할 수 있나" 에 답한다).
101
+ *
102
+ * 안 실행 중이면 붙이지 않는다 — 능력은 지금 자원의 사실에서 나오고, 없는 것을 지어내지 않는다.
103
+ */
104
+ private capabilityOfOperation;
105
+ /**
106
+ * 이 항목을 트윈이 **관측한 값** — 그리고 **무엇을 근거로** 아는지.
107
+ *
108
+ * 봉투를 푸는 것을 빠뜨려서(`unwrapState`) 문서 축의 관측 열이 통째로 비어 있던 자리다(사람을 열면
109
+ * 트윈이 아는 것이 전부 `—` 였다).
110
+ *
111
+ * 근거를 값과 함께 낸다 — 목록이 오래 해 온 그대로다(§twinModelItemList). 라이브(권위)와 저널
112
+ * 폴드(파생)는 **같은 모양의 객체**라서, 근거가 없으면 다시 계산한 값을 "지금 이렇다" 로 읽는다. 근거를
113
+ * 실을 자리가 없던 동안에는 멈춘 트윈의 저널을 아예 접지 않고 `—` 로 두는 편이 정직했다. 이제
114
+ * 자리가 있으니 접는다 — 멈춘 트윈에도 마지막으로 알던 사실이 있고, 그것을 감출 이유는 없다.
115
+ *
116
+ * 다시 계산할 것이 없으면(모델도 저널도 없다) `null` 이다 — **없는 것과 비교하지 못한 것은 다르다.**
117
+ */
118
+ private observedOf;
119
+ /**
120
+ * 이어진 것 — **양방향.** 선언된 관계(`TWIN_RELATIONS`)만 따라간다.
121
+ *
122
+ * 관계를 코드가 알지 않으므로 도메인이 늘어도 여기 손댈 것이 없다. 대신 **선언에 없는 관계는
123
+ * 그리지 않는다** — 지도에 없는 길을 그리는 것보다 없다고 두는 것이 정직하다.
124
+ *
125
+ * 들어오는 쪽(누가 나를 가리키나)은 그 축의 항목을 살펴야 안다. 규모 규율상 **깊이 1 · 방향별
126
+ * 상한**이고, 넘치면 몇이 더 있는지 말한다(목록으로 보낼 수 있게).
127
+ */
128
+ private linksOf;
129
+ /**
130
+ * 선언 필드 경로 → 값들. `inputs[].material` 처럼 **배열을 지나는 경로**를 다룬다.
131
+ * 배열 표기(`[]`)가 관계 선언의 문법이므로 그것을 따르는 것이 곧 선언을 존중하는 것이다.
132
+ */
133
+ private fieldValue;
134
+ /**
135
+ * 행에서 관계 값을 꺼낸다 — FK 는 객체(관계 미로딩이면 없음)이고 문서 필드명과 컬럼명이 다를 수 있다.
136
+ *
137
+ * 여기서 컬럼 이름을 **축마다 표로 두지 않는다.** 후보를 순서대로 보는 편이 축이 늘 때 고칠 것이
138
+ * 없다: 관계 필드 이름 그대로 · 그 이름의 FK 객체의 id 컬럼.
139
+ */
140
+ private relationValueOfRow;
141
+ }