@things-factory/headless-twin 10.1.277 → 10.1.279

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 (614) hide show
  1. package/package.json +21 -7
  2. package/dist-server/engine/attention-digest.js.map +0 -1
  3. package/dist-server/engine/canonical-ingest.js.map +0 -1
  4. package/dist-server/engine/checkpoint-fold.js.map +0 -1
  5. package/dist-server/engine/command-routing.js.map +0 -1
  6. package/dist-server/engine/declared-stimulus.js.map +0 -1
  7. package/dist-server/engine/energy-topology.js.map +0 -1
  8. package/dist-server/engine/event-time.js.map +0 -1
  9. package/dist-server/engine/index.js.map +0 -1
  10. package/dist-server/engine/ingest-dedupe.js.map +0 -1
  11. package/dist-server/engine/ingest-health.js.map +0 -1
  12. package/dist-server/engine/integration-coverage.js.map +0 -1
  13. package/dist-server/engine/integration-probes.js.map +0 -1
  14. package/dist-server/engine/integration-runner.js.map +0 -1
  15. package/dist-server/engine/integration-target-profile.js.map +0 -1
  16. package/dist-server/engine/kpi-baseline.js.map +0 -1
  17. package/dist-server/engine/kpi-fold.js.map +0 -1
  18. package/dist-server/engine/kpi-query.js.map +0 -1
  19. package/dist-server/engine/kpi-target.js.map +0 -1
  20. package/dist-server/engine/live-attentions.js.map +0 -1
  21. package/dist-server/engine/live-feed-registry.js.map +0 -1
  22. package/dist-server/engine/load-meter.js.map +0 -1
  23. package/dist-server/engine/local-declarations.js.map +0 -1
  24. package/dist-server/engine/log.js.map +0 -1
  25. package/dist-server/engine/loop-lag.js.map +0 -1
  26. package/dist-server/engine/measured-estimator.js.map +0 -1
  27. package/dist-server/engine/measured-yield.js.map +0 -1
  28. package/dist-server/engine/model-basis.js.map +0 -1
  29. package/dist-server/engine/model-gap.js.map +0 -1
  30. package/dist-server/engine/model-refusal.js.map +0 -1
  31. package/dist-server/engine/model-vocabulary.js.map +0 -1
  32. package/dist-server/engine/oee-accumulator.js.map +0 -1
  33. package/dist-server/engine/operation-basis.js.map +0 -1
  34. package/dist-server/engine/producer-guard.js.map +0 -1
  35. package/dist-server/engine/property-effects.js.map +0 -1
  36. package/dist-server/engine/read-time.js.map +0 -1
  37. package/dist-server/engine/restart-policy.js.map +0 -1
  38. package/dist-server/engine/runtime-key.js.map +0 -1
  39. package/dist-server/engine/spec-coverage.js.map +0 -1
  40. package/dist-server/engine/stage-path.js.map +0 -1
  41. package/dist-server/engine/state-axes.js.map +0 -1
  42. package/dist-server/engine/structure-diff.js.map +0 -1
  43. package/dist-server/engine/travel-estimator.js.map +0 -1
  44. package/dist-server/engine/twin-engine.js.map +0 -1
  45. package/dist-server/engine/twin-state-channel.js.map +0 -1
  46. package/dist-server/engine/warm-start.js.map +0 -1
  47. package/dist-server/index.js.map +0 -1
  48. package/dist-server/migrations/1786000000000-RenameTwinInstanceBoardToModel.js.map +0 -1
  49. package/dist-server/migrations/1786100000000-PromoteEventActionAndSyncWarnings.js.map +0 -1
  50. package/dist-server/migrations/1786200000000-CarryLiveFeedCursor.js.map +0 -1
  51. package/dist-server/migrations/1786300000000-IndexEventTypeByTime.js.map +0 -1
  52. package/dist-server/migrations/1786400000000-IndexEventCreatedAt.js.map +0 -1
  53. package/dist-server/migrations/1786500000000-CreateTwinSubjectEvents.js.map +0 -1
  54. package/dist-server/migrations/1786600000000-RenameOperatoMesAdapterType.js.map +0 -1
  55. package/dist-server/migrations/1786700000000-AddDispatchRetryColumns.js.map +0 -1
  56. package/dist-server/migrations/1786800000000-CreateTwinReferenceReconciliations.js.map +0 -1
  57. package/dist-server/migrations/index.js.map +0 -1
  58. package/dist-server/routes.js.map +0 -1
  59. package/dist-server/service/actuation/actuation-advisor.js.map +0 -1
  60. package/dist-server/service/actuation/actuation-outcome.js.map +0 -1
  61. package/dist-server/service/actuation/actuation-rule-resolver.js.map +0 -1
  62. package/dist-server/service/actuation/actuation-rule.js.map +0 -1
  63. package/dist-server/service/actuation/approval-gateway.js.map +0 -1
  64. package/dist-server/service/actuation/catch-up-effects.js.map +0 -1
  65. package/dist-server/service/actuation/command-dispatcher.js.map +0 -1
  66. package/dist-server/service/actuation/command-store.js.map +0 -1
  67. package/dist-server/service/actuation/dispatch-after-approval.js.map +0 -1
  68. package/dist-server/service/actuation/effect-match.js.map +0 -1
  69. package/dist-server/service/actuation/index.js.map +0 -1
  70. package/dist-server/service/actuation/retry-plan.js.map +0 -1
  71. package/dist-server/service/actuation/rule-evaluate.js.map +0 -1
  72. package/dist-server/service/actuation/rule-loop.js.map +0 -1
  73. package/dist-server/service/actuation/rule-runner.js.map +0 -1
  74. package/dist-server/service/actuation/rule-wiring.js.map +0 -1
  75. package/dist-server/service/actuation/settle-effects.js.map +0 -1
  76. package/dist-server/service/actuation/sweep-dispatch.js.map +0 -1
  77. package/dist-server/service/actuation/twin-command-resolver.js.map +0 -1
  78. package/dist-server/service/actuation/twin-command.js.map +0 -1
  79. package/dist-server/service/actuation/twin-now.js.map +0 -1
  80. package/dist-server/service/index.js.map +0 -1
  81. package/dist-server/service/reference/actuation-routing.js.map +0 -1
  82. package/dist-server/service/reference/actuation-target.js.map +0 -1
  83. package/dist-server/service/reference/actuation-verification.js.map +0 -1
  84. package/dist-server/service/reference/attention-lane.js.map +0 -1
  85. package/dist-server/service/reference/connection-portability-resolver.js.map +0 -1
  86. package/dist-server/service/reference/connection-portability.js.map +0 -1
  87. package/dist-server/service/reference/control-routing.js.map +0 -1
  88. package/dist-server/service/reference/discovery-result.js.map +0 -1
  89. package/dist-server/service/reference/fill-cadence.js.map +0 -1
  90. package/dist-server/service/reference/fill-dedupe.js.map +0 -1
  91. package/dist-server/service/reference/fill-loop.js.map +0 -1
  92. package/dist-server/service/reference/fill-span.js.map +0 -1
  93. package/dist-server/service/reference/hook-contract.js.map +0 -1
  94. package/dist-server/service/reference/hook-rejected.js.map +0 -1
  95. package/dist-server/service/reference/hook-store.js.map +0 -1
  96. package/dist-server/service/reference/index.js.map +0 -1
  97. package/dist-server/service/reference/ingest-space.js.map +0 -1
  98. package/dist-server/service/reference/knob-defaults.js.map +0 -1
  99. package/dist-server/service/reference/live-cadence.js.map +0 -1
  100. package/dist-server/service/reference/live-feed-lease.js.map +0 -1
  101. package/dist-server/service/reference/live-ingest.js.map +0 -1
  102. package/dist-server/service/reference/reference-adapter.js.map +0 -1
  103. package/dist-server/service/reference/reference-assessment.js.map +0 -1
  104. package/dist-server/service/reference/reference-fill-run.js.map +0 -1
  105. package/dist-server/service/reference/reference-fill.js.map +0 -1
  106. package/dist-server/service/reference/reference-hook.js.map +0 -1
  107. package/dist-server/service/reference/reference-link-groups.js.map +0 -1
  108. package/dist-server/service/reference/reference-links.js.map +0 -1
  109. package/dist-server/service/reference/reference-live.js.map +0 -1
  110. package/dist-server/service/reference/reference-master.js.map +0 -1
  111. package/dist-server/service/reference/reference-probe.js.map +0 -1
  112. package/dist-server/service/reference/reference-progress-channel.js.map +0 -1
  113. package/dist-server/service/reference/reference-progress-subscription.js.map +0 -1
  114. package/dist-server/service/reference/reference-progress.js.map +0 -1
  115. package/dist-server/service/reference/reference-reconciliation-query.js.map +0 -1
  116. package/dist-server/service/reference/reference-reconciliation-writer.js.map +0 -1
  117. package/dist-server/service/reference/reference-reconciliation.js.map +0 -1
  118. package/dist-server/service/reference/reference-registry.js.map +0 -1
  119. package/dist-server/service/reference/reference-resolver.js.map +0 -1
  120. package/dist-server/service/reference/template-registry.js.map +0 -1
  121. package/dist-server/service/reference/twin-reference-reconciliation.js.map +0 -1
  122. package/dist-server/service/reference/twin-reference.js.map +0 -1
  123. package/dist-server/service/twin-attention/index.js.map +0 -1
  124. package/dist-server/service/twin-attention/twin-attention-query.js.map +0 -1
  125. package/dist-server/service/twin-audit/command-audit.js.map +0 -1
  126. package/dist-server/service/twin-audit/index.js.map +0 -1
  127. package/dist-server/service/twin-audit/twin-audit-event.js.map +0 -1
  128. package/dist-server/service/twin-audit/twin-audit-query.js.map +0 -1
  129. package/dist-server/service/twin-backfill/backfill-period-facts.js.map +0 -1
  130. package/dist-server/service/twin-backfill/backfill-shape.js.map +0 -1
  131. package/dist-server/service/twin-backfill/index.js.map +0 -1
  132. package/dist-server/service/twin-backfill/twin-backfill-resolver.js.map +0 -1
  133. package/dist-server/service/twin-control/index.js.map +0 -1
  134. package/dist-server/service/twin-control/twin-control-mutation.js.map +0 -1
  135. package/dist-server/service/twin-event/index.js.map +0 -1
  136. package/dist-server/service/twin-event/journal-count.js.map +0 -1
  137. package/dist-server/service/twin-event/twin-event-keys.js.map +0 -1
  138. package/dist-server/service/twin-event/twin-event-type.js.map +0 -1
  139. package/dist-server/service/twin-event/twin-event.js.map +0 -1
  140. package/dist-server/service/twin-forecast/forecast-metrics.js.map +0 -1
  141. package/dist-server/service/twin-forecast/forecast-tuning.js.map +0 -1
  142. package/dist-server/service/twin-forecast/gap-analytics.js.map +0 -1
  143. package/dist-server/service/twin-forecast/index.js.map +0 -1
  144. package/dist-server/service/twin-forecast/twin-forecast-query.js.map +0 -1
  145. package/dist-server/service/twin-ingest-window/index.js.map +0 -1
  146. package/dist-server/service/twin-ingest-window/twin-ingest-window-query.js.map +0 -1
  147. package/dist-server/service/twin-ingest-window/twin-ingest-window-writer.js.map +0 -1
  148. package/dist-server/service/twin-ingest-window/twin-ingest-window.js.map +0 -1
  149. package/dist-server/service/twin-instance/index.js.map +0 -1
  150. package/dist-server/service/twin-instance/twin-instance.js.map +0 -1
  151. package/dist-server/service/twin-journal/event-type-split-shape.js.map +0 -1
  152. package/dist-server/service/twin-journal/event-type-split.js.map +0 -1
  153. package/dist-server/service/twin-journal/index.js.map +0 -1
  154. package/dist-server/service/twin-journal/journal-order.js.map +0 -1
  155. package/dist-server/service/twin-journal/twin-journal-query.js.map +0 -1
  156. package/dist-server/service/twin-lifecycle/domain-catalog.js.map +0 -1
  157. package/dist-server/service/twin-lifecycle/index.js.map +0 -1
  158. package/dist-server/service/twin-lifecycle/twin-lifecycle-mutation.js.map +0 -1
  159. package/dist-server/service/twin-metrics/index.js.map +0 -1
  160. package/dist-server/service/twin-metrics/twin-metrics-query.js.map +0 -1
  161. package/dist-server/service/twin-model/axis-journal-evidence.js.map +0 -1
  162. package/dist-server/service/twin-model/choose-recipe.js.map +0 -1
  163. package/dist-server/service/twin-model/epcis-coverage.js.map +0 -1
  164. package/dist-server/service/twin-model/external-resolver.js.map +0 -1
  165. package/dist-server/service/twin-model/iec61850-coverage.js.map +0 -1
  166. package/dist-server/service/twin-model/index.js.map +0 -1
  167. package/dist-server/service/twin-model/isa95-coverage.js.map +0 -1
  168. package/dist-server/service/twin-model/item-ref.js.map +0 -1
  169. package/dist-server/service/twin-model/name-index.js.map +0 -1
  170. package/dist-server/service/twin-model/project-structure.js.map +0 -1
  171. package/dist-server/service/twin-model/standard-coverage.js.map +0 -1
  172. package/dist-server/service/twin-model/status-tally.js.map +0 -1
  173. package/dist-server/service/twin-model/twin-equipment.js.map +0 -1
  174. package/dist-server/service/twin-model/twin-lineage-query.js.map +0 -1
  175. package/dist-server/service/twin-model/twin-location.js.map +0 -1
  176. package/dist-server/service/twin-model/twin-model-item-query.js.map +0 -1
  177. package/dist-server/service/twin-model/twin-model-mutation.js.map +0 -1
  178. package/dist-server/service/twin-model/twin-model-query.js.map +0 -1
  179. package/dist-server/service/twin-model/twin-model-tree-query.js.map +0 -1
  180. package/dist-server/service/twin-model/twin-operation.js.map +0 -1
  181. package/dist-server/service/twin-readiness/index.js.map +0 -1
  182. package/dist-server/service/twin-readiness/twin-readiness-query.js.map +0 -1
  183. package/dist-server/service/twin-space/area-resolver.js.map +0 -1
  184. package/dist-server/service/twin-space/index.js.map +0 -1
  185. package/dist-server/service/twin-space/move-space.js.map +0 -1
  186. package/dist-server/service/twin-space/representation-role.js.map +0 -1
  187. package/dist-server/service/twin-space/space-integrity.js.map +0 -1
  188. package/dist-server/service/twin-space/twin-area.js.map +0 -1
  189. package/dist-server/service/twin-space/twin-space-area.js.map +0 -1
  190. package/dist-server/service/twin-space/twin-space-representation.js.map +0 -1
  191. package/dist-server/service/twin-space/twin-space-resolver.js.map +0 -1
  192. package/dist-server/service/twin-space/twin-space.js.map +0 -1
  193. package/dist-server/service/twin-state/index.js.map +0 -1
  194. package/dist-server/service/twin-state/twin-state-subscription.js.map +0 -1
  195. package/dist-server/service/twin-structure/index.js.map +0 -1
  196. package/dist-server/service/twin-structure/twin-structure.js.map +0 -1
  197. package/dist-server/service/twin-subject/event-subjects.js.map +0 -1
  198. package/dist-server/service/twin-subject/index.js.map +0 -1
  199. package/dist-server/service/twin-subject/item-resolver.js.map +0 -1
  200. package/dist-server/service/twin-subject/subject-query-shape.js.map +0 -1
  201. package/dist-server/service/twin-subject/twin-subject-backfill.js.map +0 -1
  202. package/dist-server/service/twin-subject/twin-subject-event.js.map +0 -1
  203. package/dist-server/service/twin-subject/twin-subject-fast-path.js.map +0 -1
  204. package/dist-server/service/twin-subject/twin-subject-query.js.map +0 -1
  205. package/dist-server/service/twin-subject/twin-subject-writer.js.map +0 -1
  206. package/dist-server/service/twin-target/index.js.map +0 -1
  207. package/dist-server/service/twin-target/twin-target-resolver.js.map +0 -1
  208. package/dist-server/service/twin-target/twin-target.js.map +0 -1
  209. package/dist-shared/axis-read.js.map +0 -1
  210. package/dist-shared/entity-delta.js.map +0 -1
  211. package/dist-shared/kpi-broadcast.js.map +0 -1
  212. package/dist-shared/touched-items.js.map +0 -1
  213. package/dist-shared/twin-level.js.map +0 -1
  214. package/server/engine/attention-digest.ts +0 -102
  215. package/server/engine/canonical-ingest.ts +0 -511
  216. package/server/engine/checkpoint-fold.ts +0 -12
  217. package/server/engine/command-routing.ts +0 -67
  218. package/server/engine/declared-stimulus.ts +0 -66
  219. package/server/engine/energy-topology.ts +0 -189
  220. package/server/engine/event-time.ts +0 -96
  221. package/server/engine/index.ts +0 -51
  222. package/server/engine/ingest-dedupe.ts +0 -24
  223. package/server/engine/ingest-health.ts +0 -24
  224. package/server/engine/integration-coverage.ts +0 -147
  225. package/server/engine/integration-probes.ts +0 -144
  226. package/server/engine/integration-runner.ts +0 -95
  227. package/server/engine/integration-target-profile.ts +0 -103
  228. package/server/engine/kpi-baseline.ts +0 -202
  229. package/server/engine/kpi-fold.ts +0 -1516
  230. package/server/engine/kpi-query.ts +0 -1210
  231. package/server/engine/kpi-target.ts +0 -248
  232. package/server/engine/live-attentions.ts +0 -30
  233. package/server/engine/live-feed-registry.ts +0 -59
  234. package/server/engine/load-meter.ts +0 -393
  235. package/server/engine/local-declarations.ts +0 -779
  236. package/server/engine/log.ts +0 -72
  237. package/server/engine/loop-lag.ts +0 -120
  238. package/server/engine/measured-estimator.ts +0 -91
  239. package/server/engine/measured-yield.ts +0 -89
  240. package/server/engine/model-basis.ts +0 -162
  241. package/server/engine/model-gap.ts +0 -168
  242. package/server/engine/model-refusal.ts +0 -31
  243. package/server/engine/model-vocabulary.ts +0 -82
  244. package/server/engine/oee-accumulator.ts +0 -195
  245. package/server/engine/operation-basis.ts +0 -131
  246. package/server/engine/producer-guard.ts +0 -64
  247. package/server/engine/property-effects.ts +0 -216
  248. package/server/engine/read-time.ts +0 -77
  249. package/server/engine/restart-policy.ts +0 -55
  250. package/server/engine/runtime-key.ts +0 -58
  251. package/server/engine/spec-coverage.ts +0 -105
  252. package/server/engine/stage-path.ts +0 -172
  253. package/server/engine/state-axes.ts +0 -60
  254. package/server/engine/structure-diff.ts +0 -88
  255. package/server/engine/travel-estimator.ts +0 -138
  256. package/server/engine/twin-engine.ts +0 -4979
  257. package/server/engine/twin-state-channel.ts +0 -39
  258. package/server/engine/warm-start.ts +0 -265
  259. package/server/index.ts +0 -90
  260. package/server/migrations/1786000000000-RenameTwinInstanceBoardToModel.ts +0 -43
  261. package/server/migrations/1786100000000-PromoteEventActionAndSyncWarnings.ts +0 -54
  262. package/server/migrations/1786200000000-CarryLiveFeedCursor.ts +0 -53
  263. package/server/migrations/1786300000000-IndexEventTypeByTime.ts +0 -48
  264. package/server/migrations/1786400000000-IndexEventCreatedAt.ts +0 -38
  265. package/server/migrations/1786500000000-CreateTwinSubjectEvents.ts +0 -93
  266. package/server/migrations/1786600000000-RenameOperatoMesAdapterType.ts +0 -66
  267. package/server/migrations/1786700000000-AddDispatchRetryColumns.ts +0 -82
  268. package/server/migrations/1786800000000-CreateTwinReferenceReconciliations.ts +0 -39
  269. package/server/migrations/index.ts +0 -25
  270. package/server/routes.ts +0 -75
  271. package/server/service/actuation/actuation-advisor.ts +0 -131
  272. package/server/service/actuation/actuation-outcome.ts +0 -97
  273. package/server/service/actuation/actuation-rule-resolver.ts +0 -147
  274. package/server/service/actuation/actuation-rule.ts +0 -139
  275. package/server/service/actuation/approval-gateway.ts +0 -44
  276. package/server/service/actuation/catch-up-effects.ts +0 -152
  277. package/server/service/actuation/command-dispatcher.ts +0 -474
  278. package/server/service/actuation/command-store.ts +0 -236
  279. package/server/service/actuation/dispatch-after-approval.ts +0 -71
  280. package/server/service/actuation/effect-match.ts +0 -124
  281. package/server/service/actuation/index.ts +0 -27
  282. package/server/service/actuation/retry-plan.ts +0 -145
  283. package/server/service/actuation/rule-evaluate.ts +0 -352
  284. package/server/service/actuation/rule-loop.ts +0 -160
  285. package/server/service/actuation/rule-runner.ts +0 -216
  286. package/server/service/actuation/rule-wiring.ts +0 -84
  287. package/server/service/actuation/settle-effects.ts +0 -115
  288. package/server/service/actuation/sweep-dispatch.ts +0 -179
  289. package/server/service/actuation/twin-command-resolver.ts +0 -387
  290. package/server/service/actuation/twin-command.ts +0 -232
  291. package/server/service/actuation/twin-now.ts +0 -97
  292. package/server/service/index.ts +0 -100
  293. package/server/service/reference/actuation-routing.ts +0 -192
  294. package/server/service/reference/actuation-target.ts +0 -163
  295. package/server/service/reference/actuation-verification.ts +0 -85
  296. package/server/service/reference/attention-lane.ts +0 -21
  297. package/server/service/reference/connection-portability-resolver.ts +0 -171
  298. package/server/service/reference/connection-portability.ts +0 -329
  299. package/server/service/reference/control-routing.ts +0 -62
  300. package/server/service/reference/discovery-result.ts +0 -95
  301. package/server/service/reference/fill-cadence.ts +0 -29
  302. package/server/service/reference/fill-dedupe.ts +0 -66
  303. package/server/service/reference/fill-loop.ts +0 -153
  304. package/server/service/reference/fill-span.ts +0 -31
  305. package/server/service/reference/hook-contract.ts +0 -364
  306. package/server/service/reference/hook-rejected.ts +0 -53
  307. package/server/service/reference/hook-store.ts +0 -28
  308. package/server/service/reference/index.ts +0 -18
  309. package/server/service/reference/ingest-space.ts +0 -104
  310. package/server/service/reference/knob-defaults.ts +0 -59
  311. package/server/service/reference/live-cadence.ts +0 -129
  312. package/server/service/reference/live-feed-lease.ts +0 -56
  313. package/server/service/reference/live-ingest.ts +0 -94
  314. package/server/service/reference/reference-adapter.ts +0 -985
  315. package/server/service/reference/reference-assessment.ts +0 -250
  316. package/server/service/reference/reference-fill-run.ts +0 -179
  317. package/server/service/reference/reference-fill.ts +0 -223
  318. package/server/service/reference/reference-hook.ts +0 -479
  319. package/server/service/reference/reference-link-groups.ts +0 -21
  320. package/server/service/reference/reference-links.ts +0 -77
  321. package/server/service/reference/reference-live.ts +0 -420
  322. package/server/service/reference/reference-master.ts +0 -999
  323. package/server/service/reference/reference-probe.ts +0 -264
  324. package/server/service/reference/reference-progress-channel.ts +0 -26
  325. package/server/service/reference/reference-progress-subscription.ts +0 -78
  326. package/server/service/reference/reference-progress.ts +0 -95
  327. package/server/service/reference/reference-reconciliation-query.ts +0 -28
  328. package/server/service/reference/reference-reconciliation-writer.ts +0 -38
  329. package/server/service/reference/reference-reconciliation.ts +0 -55
  330. package/server/service/reference/reference-registry.ts +0 -17
  331. package/server/service/reference/reference-resolver.ts +0 -969
  332. package/server/service/reference/template-registry.ts +0 -85
  333. package/server/service/reference/twin-reference-reconciliation.ts +0 -27
  334. package/server/service/reference/twin-reference.ts +0 -190
  335. package/server/service/twin-attention/index.ts +0 -3
  336. package/server/service/twin-attention/twin-attention-query.ts +0 -70
  337. package/server/service/twin-audit/command-audit.ts +0 -114
  338. package/server/service/twin-audit/index.ts +0 -5
  339. package/server/service/twin-audit/twin-audit-event.ts +0 -158
  340. package/server/service/twin-audit/twin-audit-query.ts +0 -72
  341. package/server/service/twin-backfill/backfill-period-facts.ts +0 -168
  342. package/server/service/twin-backfill/backfill-shape.ts +0 -86
  343. package/server/service/twin-backfill/index.ts +0 -3
  344. package/server/service/twin-backfill/twin-backfill-resolver.ts +0 -35
  345. package/server/service/twin-control/index.ts +0 -3
  346. package/server/service/twin-control/twin-control-mutation.ts +0 -150
  347. package/server/service/twin-event/index.ts +0 -3
  348. package/server/service/twin-event/journal-count.ts +0 -53
  349. package/server/service/twin-event/twin-event-keys.ts +0 -215
  350. package/server/service/twin-event/twin-event-type.ts +0 -68
  351. package/server/service/twin-event/twin-event.ts +0 -250
  352. package/server/service/twin-forecast/forecast-metrics.ts +0 -106
  353. package/server/service/twin-forecast/forecast-tuning.ts +0 -40
  354. package/server/service/twin-forecast/gap-analytics.ts +0 -139
  355. package/server/service/twin-forecast/index.ts +0 -3
  356. package/server/service/twin-forecast/twin-forecast-query.ts +0 -486
  357. package/server/service/twin-ingest-window/index.ts +0 -7
  358. package/server/service/twin-ingest-window/twin-ingest-window-query.ts +0 -125
  359. package/server/service/twin-ingest-window/twin-ingest-window-writer.ts +0 -57
  360. package/server/service/twin-ingest-window/twin-ingest-window.ts +0 -113
  361. package/server/service/twin-instance/index.ts +0 -3
  362. package/server/service/twin-instance/twin-instance.ts +0 -199
  363. package/server/service/twin-journal/event-type-split-shape.ts +0 -112
  364. package/server/service/twin-journal/event-type-split.ts +0 -144
  365. package/server/service/twin-journal/index.ts +0 -3
  366. package/server/service/twin-journal/journal-order.ts +0 -20
  367. package/server/service/twin-journal/twin-journal-query.ts +0 -615
  368. package/server/service/twin-lifecycle/domain-catalog.ts +0 -51
  369. package/server/service/twin-lifecycle/index.ts +0 -3
  370. package/server/service/twin-lifecycle/twin-lifecycle-mutation.ts +0 -285
  371. package/server/service/twin-metrics/index.ts +0 -3
  372. package/server/service/twin-metrics/twin-metrics-query.ts +0 -172
  373. package/server/service/twin-model/axis-journal-evidence.ts +0 -57
  374. package/server/service/twin-model/choose-recipe.ts +0 -31
  375. package/server/service/twin-model/epcis-coverage.ts +0 -251
  376. package/server/service/twin-model/external-resolver.ts +0 -173
  377. package/server/service/twin-model/iec61850-coverage.ts +0 -111
  378. package/server/service/twin-model/index.ts +0 -25
  379. package/server/service/twin-model/isa95-coverage.ts +0 -320
  380. package/server/service/twin-model/item-ref.ts +0 -80
  381. package/server/service/twin-model/name-index.ts +0 -94
  382. package/server/service/twin-model/project-structure.ts +0 -213
  383. package/server/service/twin-model/standard-coverage.ts +0 -35
  384. package/server/service/twin-model/status-tally.ts +0 -37
  385. package/server/service/twin-model/twin-equipment.ts +0 -191
  386. package/server/service/twin-model/twin-lineage-query.ts +0 -327
  387. package/server/service/twin-model/twin-location.ts +0 -147
  388. package/server/service/twin-model/twin-model-item-query.ts +0 -1029
  389. package/server/service/twin-model/twin-model-mutation.ts +0 -503
  390. package/server/service/twin-model/twin-model-query.ts +0 -792
  391. package/server/service/twin-model/twin-model-tree-query.ts +0 -211
  392. package/server/service/twin-model/twin-operation.ts +0 -157
  393. package/server/service/twin-readiness/index.ts +0 -3
  394. package/server/service/twin-readiness/twin-readiness-query.ts +0 -96
  395. package/server/service/twin-space/area-resolver.ts +0 -75
  396. package/server/service/twin-space/index.ts +0 -8
  397. package/server/service/twin-space/move-space.ts +0 -262
  398. package/server/service/twin-space/representation-role.ts +0 -15
  399. package/server/service/twin-space/space-integrity.ts +0 -293
  400. package/server/service/twin-space/twin-area.ts +0 -63
  401. package/server/service/twin-space/twin-space-area.ts +0 -66
  402. package/server/service/twin-space/twin-space-representation.ts +0 -99
  403. package/server/service/twin-space/twin-space-resolver.ts +0 -582
  404. package/server/service/twin-space/twin-space.ts +0 -148
  405. package/server/service/twin-state/index.ts +0 -3
  406. package/server/service/twin-state/twin-state-subscription.ts +0 -62
  407. package/server/service/twin-structure/index.ts +0 -3
  408. package/server/service/twin-structure/twin-structure.ts +0 -96
  409. package/server/service/twin-subject/event-subjects.ts +0 -162
  410. package/server/service/twin-subject/index.ts +0 -10
  411. package/server/service/twin-subject/item-resolver.ts +0 -92
  412. package/server/service/twin-subject/subject-query-shape.ts +0 -84
  413. package/server/service/twin-subject/twin-subject-backfill.ts +0 -196
  414. package/server/service/twin-subject/twin-subject-event.ts +0 -101
  415. package/server/service/twin-subject/twin-subject-fast-path.ts +0 -346
  416. package/server/service/twin-subject/twin-subject-query.ts +0 -210
  417. package/server/service/twin-subject/twin-subject-writer.ts +0 -189
  418. package/server/service/twin-target/index.ts +0 -5
  419. package/server/service/twin-target/twin-target-resolver.ts +0 -142
  420. package/server/service/twin-target/twin-target.ts +0 -97
  421. package/server/types/global.d.ts +0 -22
  422. package/shared/axis-read.ts +0 -28
  423. package/shared/entity-delta.ts +0 -335
  424. package/shared/kpi-broadcast.ts +0 -13
  425. package/shared/touched-items.ts +0 -73
  426. package/shared/twin-level.ts +0 -48
  427. package/test/ack-shape-alignment.test.ts +0 -97
  428. package/test/actuation-advisor.test.ts +0 -55
  429. package/test/actuation-approval-door.test.ts +0 -411
  430. package/test/actuation-dispatch.test.ts +0 -209
  431. package/test/actuation-effect.test.ts +0 -162
  432. package/test/actuation-outcome.test.ts +0 -152
  433. package/test/actuation-rule-runner.test.ts +0 -281
  434. package/test/actuation-rule.test.ts +0 -266
  435. package/test/actuation-seam.test.ts +0 -255
  436. package/test/actuation-settle.test.ts +0 -164
  437. package/test/actuation-target.test.ts +0 -145
  438. package/test/actuation-verification-state.test.ts +0 -139
  439. package/test/adopt-structure-live.test.ts +0 -133
  440. package/test/aggregate-not-findone.test.ts +0 -112
  441. package/test/area-resolver-db.test.ts +0 -110
  442. package/test/attention-digest.test.ts +0 -161
  443. package/test/axis-declaration-passthrough-db.test.ts +0 -152
  444. package/test/axis-journal-evidence.test.ts +0 -71
  445. package/test/axis-read.test.ts +0 -188
  446. package/test/backfill-shape.test.ts +0 -89
  447. package/test/boot-resume.test.ts +0 -287
  448. package/test/broadcast-cost-baseline.test.ts +0 -201
  449. package/test/broadcast-period.test.ts +0 -58
  450. package/test/broadcast-queue-overflow.test.ts +0 -244
  451. package/test/canonical-ingest-vocabularies.test.ts +0 -451
  452. package/test/canonical-quantity-door.test.ts +0 -61
  453. package/test/capability-mapping.test.ts +0 -89
  454. package/test/checkpoint-refuses-empty.test.ts +0 -103
  455. package/test/choose-recipe.test.ts +0 -117
  456. package/test/column-type-portability.test.ts +0 -122
  457. package/test/command-routing.test.ts +0 -61
  458. package/test/command-store-writes-columns.test.ts +0 -126
  459. package/test/connection-portability-doors.test.ts +0 -87
  460. package/test/connector-capability-declaration.test.ts +0 -195
  461. package/test/contract-layer-guard.test.ts +0 -48
  462. package/test/control-capability.test.ts +0 -98
  463. package/test/cursor-stall-not-read-failure.test.ts +0 -126
  464. package/test/declaration-reaches-model.test.ts +0 -226
  465. package/test/declared-location-types.test.ts +0 -89
  466. package/test/declared-stimulus.test.ts +0 -88
  467. package/test/dedupe-transformation.test.ts +0 -112
  468. package/test/discovery-result.test.ts +0 -75
  469. package/test/dispatch-after-approval-source.test.ts +0 -81
  470. package/test/duration-estimators.test.ts +0 -144
  471. package/test/energy-topology.test.ts +0 -113
  472. package/test/entity-delta.test.ts +0 -352
  473. package/test/equipment-identity-reaches.test.ts +0 -57
  474. package/test/event-subjects.test.ts +0 -148
  475. package/test/event-time-column.test.ts +0 -67
  476. package/test/event-type-split.test.ts +0 -178
  477. package/test/external-resolver.test.ts +0 -177
  478. package/test/fact-scope-wiring.test.ts +0 -96
  479. package/test/fill-dedupe.test.ts +0 -59
  480. package/test/fill-loop.test.ts +0 -35
  481. package/test/first-envelope.test.ts +0 -153
  482. package/test/fold-gap-speaks.test.ts +0 -53
  483. package/test/fold-shift-dst.test.ts +0 -110
  484. package/test/forecast-metrics.test.ts +0 -157
  485. package/test/forecast-tuning.test.ts +0 -42
  486. package/test/gap-analytics.test.ts +0 -113
  487. package/test/generated-not-counted.test.ts +0 -99
  488. package/test/guard-roots.ts +0 -76
  489. package/test/home-in-area.test.ts +0 -90
  490. package/test/hook-address.test.ts +0 -28
  491. package/test/hook-rejected-shape.test.ts +0 -86
  492. package/test/hook-response-sum.test.ts +0 -193
  493. package/test/hook-sequence.test.ts +0 -333
  494. package/test/ingest-bench.test.ts +0 -70
  495. package/test/ingest-dedupe.test.ts +0 -56
  496. package/test/ingest-expected-quiet.test.ts +0 -111
  497. package/test/ingest-health-engine.test.ts +0 -248
  498. package/test/ingest-health-wiring.test.ts +0 -120
  499. package/test/ingest-health.test.ts +0 -306
  500. package/test/ingest-history.test.ts +0 -247
  501. package/test/ingest-idempotent.test.ts +0 -211
  502. package/test/ingest-reconcile-four-ways.test.ts +0 -97
  503. package/test/ingest-rules-header.test.ts +0 -100
  504. package/test/ingest-running-guard.test.ts +0 -136
  505. package/test/ingest-space.test.ts +0 -78
  506. package/test/ingest-window-rolls-unobserved.test.ts +0 -72
  507. package/test/ingest-wiring-guard.test.ts +0 -197
  508. package/test/instance-cache-lifecycle.test.ts +0 -110
  509. package/test/intake-mapping-parity.test.ts +0 -175
  510. package/test/integration-probes.test.ts +0 -103
  511. package/test/integration-runner.test.ts +0 -95
  512. package/test/item-ref.test.ts +0 -78
  513. package/test/journal-order-axis.test.ts +0 -80
  514. package/test/journal-read-discipline.test.ts +0 -186
  515. package/test/journal-retention.test.ts +0 -275
  516. package/test/journal-sort-axis.test.ts +0 -142
  517. package/test/journal-write-door.test.ts +0 -110
  518. package/test/journal-write-trend.test.ts +0 -115
  519. package/test/kernel-kind-guard.test.ts +0 -98
  520. package/test/knob-defaults.test.ts +0 -72
  521. package/test/kpi-baseline-db.test.ts +0 -216
  522. package/test/kpi-baseline.test.ts +0 -196
  523. package/test/kpi-fold.test.ts +0 -1456
  524. package/test/kpi-query-bench.test.ts +0 -130
  525. package/test/lens-own-places.test.ts +0 -50
  526. package/test/lineage-survives-restart.test.ts +0 -234
  527. package/test/live-cadence.test.ts +0 -170
  528. package/test/live-cursor-wiring.test.ts +0 -87
  529. package/test/live-feed-lease.test.ts +0 -63
  530. package/test/live-feed-registry.test.ts +0 -75
  531. package/test/live-kernel-facts.test.ts +0 -161
  532. package/test/live-mirror-parity.test.ts +0 -286
  533. package/test/load-meter.test.ts +0 -328
  534. package/test/local-declarations.test.ts +0 -662
  535. package/test/log-stamp.test.ts +0 -59
  536. package/test/loop-lag.test.ts +0 -82
  537. package/test/master-to-twin.test.ts +0 -279
  538. package/test/measured-yield.test.ts +0 -90
  539. package/test/merge-streams.test.ts +0 -70
  540. package/test/mirror-resumes-from-checkpoint.test.ts +0 -192
  541. package/test/model-basis.test.ts +0 -141
  542. package/test/model-gap.test.ts +0 -153
  543. package/test/model-refusal.test.ts +0 -75
  544. package/test/model-vocabulary.test.ts +0 -114
  545. package/test/move-space.test.ts +0 -149
  546. package/test/mutation-gate.test.ts +0 -122
  547. package/test/oee-accumulator.test.ts +0 -204
  548. package/test/oee-site-calendar.test.ts +0 -146
  549. package/test/open-doors.test.ts +0 -197
  550. package/test/operation-basis.test.ts +0 -127
  551. package/test/operational-vocabulary.test.ts +0 -108
  552. package/test/operations-capability-db.test.ts +0 -165
  553. package/test/order-and-transaction-split.test.ts +0 -107
  554. package/test/order-fold-host.test.ts +0 -112
  555. package/test/producer-guard.test.ts +0 -66
  556. package/test/project-structure-db.test.ts +0 -226
  557. package/test/projection-reaches-screen.test.ts +0 -277
  558. package/test/projection-reads-declared.test.ts +0 -109
  559. package/test/property-effects.test.ts +0 -128
  560. package/test/provenance-promotion.test.ts +0 -99
  561. package/test/read-failure-ledger.test.ts +0 -106
  562. package/test/read-failure-visible.test.ts +0 -145
  563. package/test/read-time.test.ts +0 -118
  564. package/test/reference-fill.test.ts +0 -235
  565. package/test/reference-grounding.test.ts +0 -70
  566. package/test/reference-hook.test.ts +0 -177
  567. package/test/reference-links.test.ts +0 -56
  568. package/test/reference-reconciliation.test.ts +0 -39
  569. package/test/registry-key-guard.test.ts +0 -80
  570. package/test/rejection-carries-event-id.test.ts +0 -101
  571. package/test/rename-twin.test.ts +0 -89
  572. package/test/resolve-ts-siblings.mjs +0 -52
  573. package/test/restart-policy.test.ts +0 -111
  574. package/test/resync-origin-site.test.ts +0 -83
  575. package/test/retry-plan.test.ts +0 -139
  576. package/test/revision-axis.test.ts +0 -110
  577. package/test/runtime-key.test.ts +0 -66
  578. package/test/save-intake-collision.test.ts +0 -91
  579. package/test/scale-twin-bench.test.ts +0 -103
  580. package/test/secrets-at-rest.test.ts +0 -135
  581. package/test/snapshot-freshness.test.ts +0 -60
  582. package/test/source-outcome-audit.test.ts +0 -104
  583. package/test/space-integrity.test.ts +0 -223
  584. package/test/space-representation.test.ts +0 -66
  585. package/test/space-scope-includes-stopped.test.ts +0 -55
  586. package/test/spec-coverage.test.ts +0 -113
  587. package/test/stage-path.test.ts +0 -95
  588. package/test/standard-coverage.test.ts +0 -148
  589. package/test/state-axes.test.ts +0 -74
  590. package/test/status-tally.test.ts +0 -55
  591. package/test/streamline-e2e.test.ts +0 -76
  592. package/test/structure-revision-db.test.ts +0 -315
  593. package/test/subject-fast-path.test.ts +0 -148
  594. package/test/subject-read-model.test.ts +0 -175
  595. package/test/sweep-dispatch.test.ts +0 -191
  596. package/test/tenant-registry-db.test.ts +0 -149
  597. package/test/time-range.test.ts +0 -152
  598. package/test/touched-items.test.ts +0 -61
  599. package/test/twin-audit.test.ts +0 -83
  600. package/test/twin-event-keys.test.ts +0 -193
  601. package/test/twin-model-item-db.test.ts +0 -319
  602. package/test/twin-model-parent-relation-db.test.ts +0 -125
  603. package/test/twin-model-tree-db.test.ts +0 -209
  604. package/test/twin-origin-resync.test.ts +0 -133
  605. package/test/twin-state-channel.test.ts +0 -111
  606. package/test/vocabulary-guard.test.ts +0 -43
  607. package/test/warm-start-seam.test.ts +0 -162
  608. package/test/warm-start.test.ts +0 -297
  609. package/test/withheld-door.test.ts +0 -95
  610. package/test/yield-loop.test.ts +0 -199
  611. package/tsconfig.json +0 -15
  612. package/tsconfig.shared.json +0 -23
  613. package/tsconfig.shared.tsbuildinfo +0 -1
  614. package/tsconfig.tsbuildinfo +0 -1
@@ -1,582 +0,0 @@
1
- import { twinError } from '../../engine/log.js'
2
- import { Arg, Ctx, Mutation, Query, Resolver, Directive} from 'type-graphql'
3
-
4
- import { ScalarObject, getRepository } from '@things-factory/shell'
5
-
6
- import { TwinSpace } from './twin-space.js'
7
- import { TwinSpaceRepresentation } from './twin-space-representation.js'
8
- import { REPRESENTATION_ROLES } from './representation-role.js'
9
- import { TwinSpaceArea } from './twin-space-area.js'
10
- import { TwinArea } from './twin-area.js'
11
- import { TwinInstance } from '../twin-instance/twin-instance.js'
12
- import { TwinAuditEvent } from '../twin-audit/twin-audit-event.js'
13
- import { TwinEngine } from '../../engine/index.js'
14
- import { judgeMoveTwinSpace, judgeDetachTwinSpace } from './move-space.js'
15
- import { checkSpaceIntegrity, sortFindings, type IntegrityPolygon } from './space-integrity.js'
16
-
17
- /*
18
- * 공간 구성 채널(design/ui/space-composition.md) — Site(Space) + 복수 표현(Representation) + 영역(Area).
19
- * model 내용은 board-service Board 에(표현 config.boardId 참조, 중복 저장 금지). 모두 domain 스코프.
20
- */
21
- @Resolver()
22
- export class TwinSpaceResolver {
23
- /* ── Space(Site) ────────────────────────────────────────────────── */
24
- /**
25
- * 이 테넌트의 현장 목록.
26
- *
27
- * ── 세 번만 묻는다 (2026-08-13) ──────────────────────────────────────────
28
- * 예전에는 현장마다 표현 수를 세고 인스턴스를 조회했다 — 현장 13곳이면 **질의 27번**이고, 첫
29
- * 호출이 2.4초였다(헤더가 뜰 때마다 이 목록을 읽는다). 규모가 커지면 그대로 자란다.
30
- *
31
- * 이제 세 번 묻고 메모리에서 맞춘다: 현장 · 표현(공간 id 만) · 인스턴스(필요한 컬럼만).
32
- * 표현은 **id 만 실어 온다**(`loadRelationIds`) — 설정 blob 을 읽어 세는 것은 낭비다.
33
- */
34
- @Query(returns => [ScalarObject], { description: 'List tenant Spaces (Sites) in the current domain.' })
35
- async twinSpaces(@Ctx() context: ResolverContext): Promise<any[]> {
36
- const domainId = context.state.domain.id
37
- const [rows, reps, insts] = await Promise.all([
38
- getRepository(TwinSpace).find({ where: { domain: { id: domainId } }, order: { createdAt: 'ASC' } }),
39
- /* 관계를 **조인해서** 읽는다 — `loadRelationIds`+`select` 조합은 관계 자리를 비워 두어
40
- 표현 수가 전부 0 이 됐다(2026-08-13 실측으로 잡음). 조인 한 번이 현장마다 세는 것보다 싸다. */
41
- getRepository(TwinSpaceRepresentation).find({ where: { domain: { id: domainId } }, relations: { space: true } }),
42
- getRepository(TwinInstance).find({ where: { domain: { id: domainId } }, select: { instanceId: true, spaceId: true, purpose: true, copyOf: true } })
43
- ])
44
-
45
- const repCount = new Map<string, number>()
46
- for (const rep of reps) {
47
- const sid = rep.space?.id
48
- if (sid) repCount.set(sid, (repCount.get(sid) ?? 0) + 1)
49
- }
50
- /* 벤치 여부·원본 참조 — 인스턴스 purpose 근거(1급). 사본 현장을 헤더 선택기에서 구별·선택 가능하게. */
51
- const benchOf = new Map<string, { copyOf?: string }>()
52
- for (const i of insts) if ((i as any).purpose === 'bench' && i.spaceId && !benchOf.has(i.spaceId)) benchOf.set(i.spaceId, { copyOf: i.copyOf ?? undefined })
53
-
54
- return rows.map(r => {
55
- const bench = benchOf.get(r.spaceId)
56
- return {
57
- spaceId: r.spaceId,
58
- name: r.name,
59
- description: r.description,
60
- geo: r.geo,
61
- timezone: r.timezone,
62
- primaryRepresentationId: r.primaryRepresentationId,
63
- extentX: r.extentX,
64
- extentY: r.extentY,
65
- representationCount: repCount.get(r.id) ?? 0,
66
- bench: !!bench,
67
- copyOf: bench?.copyOf
68
- }
69
- })
70
- }
71
-
72
- /** 단건 — 표현(+영역) 트리 포함. */
73
- @Query(returns => ScalarObject, { nullable: true, description: 'Get one Space with its representations (and their areas).' })
74
- async twinSpace(@Arg('spaceId') spaceId: string, @Ctx() context: ResolverContext): Promise<any | null> {
75
- const domainId = context.state.domain.id
76
- const r = await getRepository(TwinSpace).findOne({ where: { domain: { id: domainId }, spaceId } })
77
- if (!r) return null
78
- /*
79
- * 묶은 이름 다음에 순서로 정렬한다. `group` 이 비어 있는 행이 앞에 오는지 뒤에 오는지는 드라이버마다
80
- * 다르므로 그 순서에 기대지 않는다 — 화면이 다시 묶는다(UX 레인과 그렇게 정했다).
81
- */
82
- const reps = await getRepository(TwinSpaceRepresentation).find({
83
- where: { domain: { id: domainId }, space: { id: r.id } },
84
- order: { group: 'ASC', seq: 'ASC' }
85
- })
86
- const representations: any[] = []
87
- for (const rep of reps) {
88
- const areas = await getRepository(TwinSpaceArea).find({ where: { domain: { id: domainId }, representation: { id: rep.id } }, order: { createdAt: 'ASC' } })
89
- representations.push({ id: rep.id, type: rep.type, name: rep.name, seq: rep.seq, group: rep.group, role: rep.role, isPrimary: rep.isPrimary, config: rep.config, areas: areas.map(a => ({ id: a.id, name: a.name, type: a.type, geometry: a.geometry, drillTo: a.drillTo, binding: a.binding, style: a.style })) })
90
- }
91
- return { spaceId: r.spaceId, name: r.name, description: r.description, geo: r.geo, timezone: r.timezone, primaryRepresentationId: r.primaryRepresentationId, extentX: r.extentX, extentY: r.extentY, representations }
92
- }
93
-
94
- @Directive('@privilege(category: "twin", privilege: "mutation", domainOwnerGranted: true, superUserGranted: true)')
95
- @Mutation(returns => Boolean, { description: 'Create or update a Space (name/description/geo/timezone/primaryRepresentationId).' })
96
- async saveTwinSpace(
97
- @Arg('spaceId') spaceId: string,
98
- @Arg('patch', type => ScalarObject) patch: any,
99
- @Ctx() context: ResolverContext
100
- ): Promise<boolean> {
101
- const repo = getRepository(TwinSpace)
102
- const domainId = context.state.domain.id
103
- const existing = await repo.findOne({ where: { domain: { id: domainId }, spaceId } })
104
- /* 진짜 부분 병합 — patch 에 준 키만 갱신(나머지 필드 보존). id 는 관계로 재설정하지 않도록 제외. */
105
- const { id: _drop, ...clean } = patch ?? {}
106
- await repo.save(repo.create({ ...(existing ?? {}), ...clean, domain: { id: domainId } as any, spaceId }))
107
- return true
108
- }
109
-
110
- /**
111
- * 공간 삭제 — **이 공간의 트윈이 있으면 거절한다.** 그래서 트윈이 현장을 잃는 일은 없다.
112
- *
113
- * ── 왜 모양을 바꿨나 (2026-08-13) ────────────────────────────────────────
114
- * 예전에는 `Boolean` 을 돌려주고 거절은 **한국어 문장을 냈다.** 두 가지가 틀렸다:
115
- * ① 서버가 화면의 말을 정했다 — 일본어·스페인어 사용자에게 한국어가 새어 나간다.
116
- * ② 오류를 낸 예외를 화면이 받지 않아 **아무 일도 없는 것처럼** 보였다. 사용자는 삭제를 눌렀고
117
- * 화면은 그대로였다. 거절을 알리지 않고 하는 것은 실패보다 나쁘다.
118
- * 이제 `{ok, errorCode, errorParams}` 로 낸다 — 화면이 `twin.compose.del.<code>` 로 말을 만든다.
119
- */
120
- @Directive('@privilege(category: "twin", privilege: "mutation", domainOwnerGranted: true, superUserGranted: true)')
121
- @Mutation(returns => ScalarObject, {
122
- description:
123
- 'Delete a Space and its representations/areas. Refused while twins still live in it (a twin never loses its site this way). Returns {ok, errorCode, errorParams}.'
124
- })
125
- async deleteTwinSpace(@Arg('spaceId') spaceId: string, @Ctx() context: ResolverContext): Promise<any> {
126
- const domainId = context.state.domain.id
127
- // 라이프사이클 가드 — 이 공간에 바인딩된 트윈 인스턴스가 있으면 삭제 차단(dangling 방지).
128
- // spaceId 컬럼(space #3) 로 직접 질의 — 전 인스턴스 로드+JS필터 제거(효율·1급 질의).
129
- const bound = await getRepository(TwinInstance).find({ where: { domain: { id: domainId }, spaceId } })
130
- if (bound.length) {
131
- const twins = bound.map(i => i.name || i.instanceId)
132
- return {
133
- ok: false,
134
- errorCode: 'space-has-twins',
135
- errorParams: { spaceId, count: bound.length, twins: twins.slice(0, 5).join(', ') },
136
- error: `space "${spaceId}" still has ${bound.length} twin(s) — remove or move them first (${twins.join(', ')})`
137
- }
138
- }
139
- const space = await getRepository(TwinSpace).findOne({ where: { domain: { id: domainId }, spaceId } })
140
- if (space) {
141
- const reps = await getRepository(TwinSpaceRepresentation).find({ where: { domain: { id: domainId }, space: { id: space.id } } })
142
- for (const rep of reps) await getRepository(TwinSpaceArea).delete({ domain: { id: domainId } as any, representation: { id: rep.id } as any })
143
- await getRepository(TwinSpaceRepresentation).delete({ domain: { id: domainId } as any, space: { id: space.id } as any })
144
- await getRepository(TwinArea).delete({ domain: { id: domainId } as any, space: { id: space.id } as any }) // 논리 area 도 함께 정리
145
- }
146
- await getRepository(TwinSpace).delete({ domain: { id: domainId } as any, spaceId })
147
- return { ok: true }
148
- }
149
-
150
- /**
151
- * 이미 있는 트윈을 이 현장으로 가져온다 — **소속 갱신**(이력 이전이 아니다).
152
- *
153
- * 저널·투영 행은 `instanceId` 로 매여 있어 트윈을 따라오고, 구역·표현은 공간의 것이라 남는다
154
- * (근거·판정: `move-space.ts`). 그래서 이 뮤테이션이 쓰는 것은 두 곳뿐이다 —
155
- * 레지스트리의 `spaceId` 와 저장된 model 의 `spaceId`(커널·보드가 같은 공간을 가리켜야 한다).
156
- * 새 공간의 시간대로 `utcOffsetMinutes` 도 다시 파생한다(공간이 시간의 기준이다).
157
- *
158
- * `dryRun` 은 **같은 판정을 미리** 보여 준다 — 화면이 잃을 것을 먼저 말하고 사용자가 정한다.
159
- * 판정을 두 벌로 두면 미리보기와 실제가 어긋나므로 한 함수를 두 번 부른다.
160
- */
161
- @Directive('@privilege(category: "twin", privilege: "mutation", domainOwnerGranted: true, superUserGranted: true)')
162
- @Mutation(returns => ScalarObject, {
163
- description:
164
- 'Move an existing twin into a space (co-locate it as another lens). The journal and projected rows follow the twin; areas and representations stay with the old space. dryRun returns the same judgement without writing. Returns {ok, warnings, errorCode}.'
165
- })
166
- async moveTwinToSpace(
167
- @Arg('instanceId') instanceId: string,
168
- @Arg('spaceId') spaceId: string,
169
- @Ctx() context: ResolverContext,
170
- @Arg('dryRun', { nullable: true, description: 'Judge only — report refusal/warnings without moving.' }) dryRun?: boolean
171
- ): Promise<any> {
172
- const domainId = context.state.domain.id
173
- const instRepo = getRepository(TwinInstance)
174
- const spaceRepo = getRepository(TwinSpace)
175
-
176
- const inst = await instRepo.findOne({ where: { domain: { id: domainId }, instanceId } })
177
- const from = inst?.spaceId ? await spaceRepo.findOne({ where: { domain: { id: domainId }, spaceId: inst.spaceId } }) : null
178
- const to = await spaceRepo.findOne({ where: { domain: { id: domainId }, spaceId } })
179
-
180
- /* 판정에 필요한 사실만 모아 넘긴다 — 판정 자체는 순수 모듈이 한다. */
181
- const toAreaIds = to
182
- ? (await getRepository(TwinArea).find({ where: { domain: { id: domainId } as any, space: { id: to.id } as any } })).map(a => a.areaId)
183
- : []
184
- const fromRepresentationCount = from
185
- ? await getRepository(TwinSpaceRepresentation).count({ where: { domain: { id: domainId } as any, space: { id: from.id } as any } })
186
- : 0
187
- const model: any = inst?.model ?? {}
188
- const areaRefs: (string | undefined)[] = (model.locations ?? []).map((l: any) => l?.parentId)
189
-
190
- const judgement = judgeMoveTwinSpace({
191
- instanceId: inst?.instanceId,
192
- fromSpaceId: inst?.spaceId,
193
- toSpaceId: spaceId,
194
- toSpaceExists: !!to,
195
- purpose: (inst as any)?.purpose,
196
- running: !!inst && !!TwinEngine.runtime(domainId, instanceId),
197
- fromTimezone: from?.timezone ?? undefined,
198
- toTimezone: to?.timezone ?? undefined,
199
- areaRefs,
200
- toAreaIds,
201
- fromRepresentationCount
202
- })
203
- if (judgement.refusal) {
204
- const r = judgement.refusal
205
- return { ok: false, error: r.message, errorCode: r.code, errorParams: r.params, warnings: [] }
206
- }
207
- if (dryRun) return { ok: true, dryRun: true, warnings: judgement.warnings }
208
-
209
- /* 두 곳을 함께 쓴다 — 레지스트리와 model 이 다른 공간을 가리키면 보드·커널이 갈라진다. */
210
- const moved = { ...model, spaceId }
211
- const withTime = await TwinEngine.withSpaceTimeBase(moved, domainId, spaceId)
212
- await instRepo.save(instRepo.create({ ...(inst as any), spaceId, model: withTime }))
213
-
214
- /*
215
- * 감사 기록 — 소속을 바꾼 것은 사람의 결정이고, 나중에 "왜 이 트윈이 저 현장에 있나" 를 묻게 된다.
216
- * 기록이 실패해도 이전 결과는 돌려준다(감사가 본업을 막지 않는다). 다만 삼키지 않고 로그에 남긴다.
217
- */
218
- try {
219
- const repo = getRepository(TwinAuditEvent)
220
- await repo.save(
221
- repo.create({
222
- domain: context.state.domain,
223
- instanceId,
224
- commandType: 'twin.moveToSpace',
225
- accepted: true,
226
- origin: 'user',
227
- actorId: context.state.user?.id,
228
- args: { from: inst?.spaceId ?? null, to: spaceId, warnings: judgement.warnings.map(w => w.code) }
229
- } as any)
230
- )
231
- } catch (err) {
232
- twinError(`[twin-space] move audit not recorded for "${instanceId}"`, err)
233
- }
234
-
235
- return { ok: true, warnings: judgement.warnings }
236
- }
237
-
238
- /**
239
- * 트윈을 현장에서 뗀다 — **어느 현장에도 속하지 않음**으로 되돌린다.
240
- *
241
- * 더하기·옮기기만 있으면 잘못 넣은 것을 되돌릴 수 없다. 떼도 저널·로케이션·설비는 트윈에 남고
242
- * (`instanceId` 로 매여 있다), 구역·표현은 옛 현장에 남는다. 잃는 것은 **볼 무대**다 —
243
- * 판정이 그것을 말한다(`judgeDetachTwinSpace`). `dryRun` 은 같은 판정을 미리 낸다.
244
- */
245
- @Directive('@privilege(category: "twin", privilege: "mutation", domainOwnerGranted: true, superUserGranted: true)')
246
- @Mutation(returns => ScalarObject, {
247
- description:
248
- 'Detach a twin from its space (it ends up on no site). The journal and projected rows stay with the twin; areas and representations stay with the space. dryRun returns the same judgement without writing. Returns {ok, warnings, errorCode}.'
249
- })
250
- async detachTwinFromSpace(
251
- @Arg('instanceId') instanceId: string,
252
- @Ctx() context: ResolverContext,
253
- @Arg('dryRun', { nullable: true, description: 'Judge only — report refusal/warnings without detaching.' }) dryRun?: boolean
254
- ): Promise<any> {
255
- const domainId = context.state.domain.id
256
- const instRepo = getRepository(TwinInstance)
257
- const inst = await instRepo.findOne({ where: { domain: { id: domainId }, instanceId } })
258
- const from = inst?.spaceId
259
- ? await getRepository(TwinSpace).findOne({ where: { domain: { id: domainId }, spaceId: inst.spaceId } })
260
- : null
261
- const fromRepresentationCount = from
262
- ? await getRepository(TwinSpaceRepresentation).count({ where: { domain: { id: domainId } as any, space: { id: from.id } as any } })
263
- : 0
264
-
265
- const judgement = judgeDetachTwinSpace({
266
- instanceId: inst?.instanceId,
267
- fromSpaceId: inst?.spaceId,
268
- purpose: (inst as any)?.purpose,
269
- fromRepresentationCount,
270
- fromTimezone: from?.timezone ?? undefined
271
- })
272
- if (judgement.refusal) {
273
- const r = judgement.refusal
274
- return { ok: false, error: r.message, errorCode: r.code, errorParams: r.params, warnings: [] }
275
- }
276
- if (dryRun) return { ok: true, dryRun: true, warnings: judgement.warnings }
277
-
278
- /* 두 곳을 함께 비운다 — 레지스트리와 model 이 어긋나면 어느 쪽이 사실인지 알 수 없다.
279
- 시간 기준(utcOffsetMinutes)은 **남긴다**: 지우면 알리지 않고 UTC 로 읽힌다(판정이 그렇게 말했다). */
280
- const { spaceId: _dropped, ...model } = (inst?.model as any) ?? {}
281
- await instRepo.update({ id: (inst as any).id }, { spaceId: null as any, model })
282
-
283
- try {
284
- const repo = getRepository(TwinAuditEvent)
285
- await repo.save(
286
- repo.create({
287
- domain: context.state.domain,
288
- instanceId,
289
- commandType: 'twin.detachFromSpace',
290
- accepted: true,
291
- origin: 'user',
292
- actorId: context.state.user?.id,
293
- args: { from: inst?.spaceId ?? null, warnings: judgement.warnings.map(w => w.code) }
294
- } as any)
295
- )
296
- } catch (err) {
297
- twinError(`[twin-space] detach audit not recorded for "${instanceId}"`, err)
298
- }
299
-
300
- return { ok: true, warnings: judgement.warnings }
301
- }
302
-
303
- /**
304
- * 공간 ↔ 트윈 ↔ 가시화 정합성 점검 — **사실을 모아 순수 판정에 넘긴다.**
305
- *
306
- * 세 축은 서로를 id 로만 가리키고 DB 가 그 참조를 지켜 주지 않는다. 트윈을 옮기거나 떼거나 원본을
307
- * 다시 읽으면 아무 오류 없이 **가리킬 것이 없는 도형**과 **소속 없는 로케이션**이 남는다. 사람이
308
- * 눈으로 대조하던 일을 기계가 한다(판정·근거: `space-integrity.ts`).
309
- *
310
- * `spaceId` 를 주면 그 현장만, 없으면 테넌트 전체를 본다.
311
- */
312
- @Query(returns => [ScalarObject], {
313
- description:
314
- 'Check space ↔ twin ↔ visualization integrity: dangling site refs, registry/model mismatch, polygons binding areas or entities that are not there, primary representation, unbound board representations. Pass spaceId to check one site.'
315
- })
316
- async twinSpaceIntegrity(
317
- @Ctx() context: ResolverContext,
318
- @Arg('spaceId', { nullable: true }) spaceId?: string
319
- ): Promise<any[]> {
320
- const domainId = context.state.domain.id
321
- const spaceRows = await getRepository(TwinSpace).find({ where: { domain: { id: domainId } } })
322
- const instRows = await getRepository(TwinInstance).find({ where: { domain: { id: domainId } } })
323
- const repRows = await getRepository(TwinSpaceRepresentation).find({ where: { domain: { id: domainId } }, relations: ['space'] })
324
- const areaRows = await getRepository(TwinArea).find({ where: { domain: { id: domainId } }, relations: ['space'] })
325
- const polyRows = await getRepository(TwinSpaceArea).find({ where: { domain: { id: domainId } }, relations: ['representation'] })
326
-
327
- const areasBySpace: Record<string, string[]> = {}
328
- for (const a of areaRows) {
329
- const sid = (a as any).space?.spaceId
330
- if (!sid) continue
331
- ;(areasBySpace[sid] ??= []).push(a.areaId)
332
- }
333
-
334
- /*
335
- * 폴리곤 바인딩의 실제 모양은 `{ groupId?, locationId?, locationIds? }` 다(지도 저작기 기준).
336
- * 여러 로케이션을 묶은 도형은 **각각을 따로** 본다 — 하나만 끊겨도 그 도형은 반쯤 거짓이 된다.
337
- */
338
- const polysByRep: Record<string, IntegrityPolygon[]> = {}
339
- for (const p of polyRows) {
340
- const rid = (p as any).representation?.id
341
- if (!rid) continue
342
- const b: any = p.binding ?? {}
343
- const list = polysByRep[rid] ??= []
344
- if (b.groupId) list.push({ id: p.id, groupId: b.groupId })
345
- const targets: string[] = [...(b.locationId ? [b.locationId] : []), ...(Array.isArray(b.locationIds) ? b.locationIds : [])]
346
- for (const t of targets) list.push({ id: p.id, targetKind: 'location', targetId: t })
347
- }
348
-
349
- /* 보드 id 는 표현 config 가 들고 있다 — 존재 확인은 보드 서비스의 일이므로, 여기서는 표현이
350
- 가리키는 값이 있는지만 본다(없는 보드까지 보려면 board-service 를 물어야 한다: 아래 주석). */
351
- const boardIds = repRows.map(r => (r.config as any)?.boardId).filter(Boolean) as string[]
352
-
353
- const findings = checkSpaceIntegrity({
354
- spaces: spaceRows.map(s => ({ spaceId: s.spaceId, primaryRepresentationId: s.primaryRepresentationId })),
355
- instances: instRows.map(i => {
356
- const model: any = i.model ?? {}
357
- /*
358
- * 어휘가 맞는지는 **실행 중인 트윈만** 답할 수 있다 — 커널이 무엇을 버렸는지는 런타임의 사실이다.
359
- * 정지한 트윈은 `undefined`(모른다)로 둔다. 저널을 다시 계산해 알아내지는 않는다: 이 질의는
360
- * 테넌트 전체를 훑으므로 트윈마다 폴드를 걸면 정합성 점검이 가장 무거운 질의가 된다.
361
- */
362
- const live: any = TwinEngine.isRunning(domainId, i.instanceId)
363
- ? TwinEngine.snapshot(domainId, i.instanceId)
364
- : undefined
365
- return {
366
- instanceId: i.instanceId,
367
- spaceId: i.spaceId,
368
- modelSpaceId: model.spaceId,
369
- purpose: (i as any).purpose,
370
- locationIds: (model.locations ?? []).map((l: any) => l?.id).filter(Boolean),
371
- equipmentIds: (model.equipment ?? []).map((e: any) => e?.id).filter(Boolean),
372
- areaRefs: (model.locations ?? []).map((l: any) => l?.parentId),
373
- ...(Array.isArray(live?.unhandled) ? { unhandled: live.unhandled } : {})
374
- }
375
- }),
376
- representations: repRows.map(r => ({
377
- id: r.id,
378
- spaceId: (r as any).space?.spaceId ?? '',
379
- type: r.type,
380
- boardId: (r.config as any)?.boardId ?? null,
381
- polygons: polysByRep[r.id] ?? []
382
- })),
383
- areasBySpace,
384
- boardIds
385
- })
386
-
387
- const scoped = spaceId ? findings.filter(f => f.spaceId === spaceId || (!f.spaceId && !spaceId)) : findings
388
- return sortFindings(scoped)
389
- }
390
-
391
- /**
392
- * 기계적으로 고칠 수 있는 어긋남만 고친다 — **사람의 판단이 필요한 것은 손대지 않는다.**
393
- *
394
- * 지금 고치는 것은 하나다: **대표 표현 포인터**. 공간이 없는 표현 id 를 가리키고 있으면, 행의
395
- * `isPrimary` 가 켜진 표현(없으면 첫 표현)으로 다시 가리키고, 표현이 없으면 비운다. 근거가 있고
396
- * 답이 하나뿐인 부류라 자동으로 고쳐도 사실을 바꾸지 않는다.
397
- *
398
- * 폴리곤이 없는 구역·개체를 가리키는 것은 **고치지 않는다.** 그 도형을 지워야 하는지, 트윈을 다시
399
- * 데려와야 하는지, 구역을 다시 그려야 하는지는 사람이 정할 일이다 — 기계가 고르면 사용자가 그린
400
- * 도형이 오류 없이 사라진다.
401
- */
402
- @Directive('@privilege(category: "twin", privilege: "mutation", domainOwnerGranted: true, superUserGranted: true)')
403
- @Mutation(returns => ScalarObject, {
404
- description:
405
- 'Repair mechanically fixable space integrity findings (currently: a primary representation pointer that points nowhere). Dangling polygon bindings are left alone — that is a human decision. Returns {repaired:[{spaceId, code}], skipped}.'
406
- })
407
- async repairTwinSpaceIntegrity(
408
- @Ctx() context: ResolverContext,
409
- @Arg('spaceId', { nullable: true }) spaceId?: string
410
- ): Promise<any> {
411
- const domainId = context.state.domain.id
412
- const spaceRepo = getRepository(TwinSpace)
413
- const repRepo = getRepository(TwinSpaceRepresentation)
414
- const spaces = await spaceRepo.find({
415
- where: spaceId ? { domain: { id: domainId }, spaceId } : { domain: { id: domainId } }
416
- })
417
- const repaired: any[] = []
418
- for (const space of spaces) {
419
- const reps = await repRepo.find({ where: { domain: { id: domainId } as any, space: { id: space.id } as any }, order: { seq: 'ASC' } })
420
- const pointer = (space.primaryRepresentationId ?? '').trim()
421
- if (pointer && reps.some(r => r.id === pointer)) continue /* 이미 옳다 */
422
- const primary = reps.find(r => r.isPrimary) ?? reps[0]
423
- const next = primary?.id ?? null
424
- if (pointer === (next ?? '')) continue
425
- await spaceRepo.save(spaceRepo.create({ ...space, primaryRepresentationId: next }))
426
- if (primary) for (const r of reps) if (!!r.isPrimary !== (r.id === primary.id)) await repRepo.save(repRepo.create({ ...r, isPrimary: r.id === primary.id }))
427
- repaired.push({ spaceId: space.spaceId, code: 'primary-representation-missing', from: pointer || null, to: next })
428
- }
429
- return { ok: true, repaired, count: repaired.length }
430
- }
431
-
432
- /* ── Area(논리 구역, 표현 무관·공유·계층) — area 단일화 space #2 ─────── */
433
- @Query(returns => [ScalarObject], { description: 'List logical areas (representation-agnostic hierarchy) of a space. (area unification)' })
434
- async twinAreas(@Arg('spaceId') spaceId: string, @Ctx() context: ResolverContext): Promise<any[]> {
435
- const domainId = context.state.domain.id
436
- const rows = await getRepository(TwinArea).find({ where: { domain: { id: domainId }, space: { spaceId } }, order: { createdAt: 'ASC' } })
437
- return rows.map(a => ({ areaId: a.areaId, name: a.name, type: a.type, parentId: a.parentId, layout: a.layout }))
438
- }
439
-
440
- @Directive('@privilege(category: "twin", privilege: "mutation", domainOwnerGranted: true, superUserGranted: true)')
441
- @Mutation(returns => Boolean, { description: 'Create/update a logical area (shared across representations). (area unification)' })
442
- async saveTwinArea(
443
- @Arg('spaceId') spaceId: string,
444
- @Arg('areaId') areaId: string,
445
- @Arg('patch', type => ScalarObject) patch: any,
446
- @Ctx() context: ResolverContext
447
- ): Promise<boolean> {
448
- const domainId = context.state.domain.id
449
- const space = await getRepository(TwinSpace).findOne({ where: { domain: { id: domainId }, spaceId } })
450
- if (!space) throw new Error(`space "${spaceId}" not found`)
451
- const repo = getRepository(TwinArea)
452
- const existing = await repo.findOne({ where: { domain: { id: domainId }, space: { id: space.id }, areaId } })
453
- await repo.save(
454
- repo.create({
455
- ...(existing ?? {}),
456
- domain: { id: domainId } as any,
457
- space: { id: space.id } as any,
458
- areaId,
459
- name: patch?.name ?? existing?.name ?? areaId,
460
- type: patch?.type ?? existing?.type,
461
- parentId: patch?.parentId !== undefined ? patch.parentId : existing?.parentId,
462
- layout: patch?.layout ?? existing?.layout
463
- })
464
- )
465
- return true
466
- }
467
-
468
- /*
469
- * 참조기반 회수(reference-counted) — area 는 참조가 남아 있으면 삭제 차단, 고아면 제거.
470
- * 참조: 자식 area(parentId) · 표현별 폴리곤(TwinSpaceArea binding.groupId/areaId).
471
- * (노드/엔티티 담김·drillTo 참조는 P2 areaId 배선 후 확장.)
472
- */
473
- @Directive('@privilege(category: "twin", privilege: "mutation", domainOwnerGranted: true, superUserGranted: true)')
474
- @Mutation(returns => Boolean, { description: 'Delete a logical area only if unreferenced (reference-counted). Throws if referenced. (area unification)' })
475
- async deleteTwinArea(@Arg('spaceId') spaceId: string, @Arg('areaId') areaId: string, @Ctx() context: ResolverContext): Promise<boolean> {
476
- const domainId = context.state.domain.id
477
- const space = await getRepository(TwinSpace).findOne({ where: { domain: { id: domainId }, spaceId } })
478
- if (!space) return false
479
- // 참조 1: 자식 area
480
- const children = await getRepository(TwinArea).count({ where: { domain: { id: domainId }, space: { id: space.id }, parentId: areaId } })
481
- // 참조 2: 표현별 폴리곤(이 공간의 표현들에 이 area 를 그린 것). binding.groupId/areaId 는 JSON → 로드 후 JS 필터(멀티DB).
482
- const reps = await getRepository(TwinSpaceRepresentation).find({ where: { domain: { id: domainId }, space: { id: space.id } } })
483
- let polygonRefs = 0
484
- for (const rep of reps) {
485
- const polys = await getRepository(TwinSpaceArea).find({ where: { domain: { id: domainId }, representation: { id: rep.id } } })
486
- polygonRefs += polys.filter(p => (p.binding as any)?.groupId === areaId || (p.binding as any)?.areaId === areaId).length
487
- }
488
- if (children > 0 || polygonRefs > 0) {
489
- throw new Error(`deleteArea: area "${areaId}" cannot be deleted due to existing references (children: ${children}, polygonRefs: ${polygonRefs}). Remove references first.`)
490
- }
491
- await getRepository(TwinArea).delete({ domain: { id: domainId } as any, space: { id: space.id } as any, areaId })
492
- return true
493
- }
494
-
495
- /* ── Representation ─────────────────────────────────────────────── */
496
- @Directive('@privilege(category: "twin", privilege: "mutation", domainOwnerGranted: true, superUserGranted: true)')
497
- @Mutation(returns => ScalarObject, { description: 'Create/update a representation (map|image|model). Returns {id}.' })
498
- async saveSpaceRepresentation(
499
- @Arg('spaceId') spaceId: string,
500
- @Arg('rep', type => ScalarObject) rep: any,
501
- @Ctx() context: ResolverContext
502
- ): Promise<any> {
503
- const domainId = context.state.domain.id
504
- const space = await getRepository(TwinSpace).findOne({ where: { domain: { id: domainId }, spaceId } })
505
- if (!space) return { ok: false, error: `space not found: ${spaceId}` }
506
- const repo = getRepository(TwinSpaceRepresentation)
507
- const existing = rep?.id ? await repo.findOne({ where: { id: rep.id } }) : null
508
- /*
509
- * `role` 은 닫힌 값이다 — 모르는 값을 그냥 저장하면 화면이 그것을 어느 쪽으로도 그리지 못하고,
510
- * 그 표현이 목록에서 오류 없이 사라진다. 비어 있는 것은 `index` 를 뜻하므로 그대로 둔다.
511
- */
512
- const role = rep.role == null || String(rep.role).trim() === '' ? undefined : String(rep.role).trim()
513
- if (role && !(REPRESENTATION_ROLES as readonly string[]).includes(role)) {
514
- return { ok: false, error: `unknown representation role "${role}" — one of: ${REPRESENTATION_ROLES.join(', ')}` }
515
- }
516
- /* 묶는 이름은 자유 문자열이다. 다만 공백만 있는 값은 「묶이지 않음」과 같으므로 비운다. */
517
- const group = rep.group == null || String(rep.group).trim() === '' ? undefined : String(rep.group).trim()
518
-
519
- const saved = await repo.save(
520
- repo.create({
521
- ...(existing ?? {}),
522
- domain: { id: domainId } as any,
523
- space: { id: space.id } as any,
524
- type: rep.type,
525
- name: rep.name,
526
- seq: rep.seq ?? 0,
527
- isPrimary: !!rep.isPrimary,
528
- config: rep.config,
529
- group: group ?? null,
530
- role: role ?? null
531
- } as Partial<TwinSpaceRepresentation>)
532
- )
533
- return { ok: true, id: saved.id }
534
- }
535
-
536
- @Directive('@privilege(category: "twin", privilege: "mutation", domainOwnerGranted: true, superUserGranted: true)')
537
- @Mutation(returns => Boolean, { description: 'Delete a representation and its areas.' })
538
- async deleteSpaceRepresentation(@Arg('id') id: string, @Ctx() context: ResolverContext): Promise<boolean> {
539
- const domainId = context.state.domain.id
540
- await getRepository(TwinSpaceArea).delete({ domain: { id: domainId } as any, representation: { id } as any })
541
- await getRepository(TwinSpaceRepresentation).delete({ domain: { id: domainId } as any, id } as any)
542
- return true
543
- }
544
-
545
- @Directive('@privilege(category: "twin", privilege: "mutation", domainOwnerGranted: true, superUserGranted: true)')
546
- @Mutation(returns => Boolean, { description: 'Set the primary representation of a space.' })
547
- async setPrimaryRepresentation(@Arg('spaceId') spaceId: string, @Arg('repId') repId: string, @Ctx() context: ResolverContext): Promise<boolean> {
548
- const domainId = context.state.domain.id
549
- const spaceRepo = getRepository(TwinSpace)
550
- const space = await spaceRepo.findOne({ where: { domain: { id: domainId }, spaceId } })
551
- if (!space) return false
552
- await spaceRepo.save(spaceRepo.create({ ...space, primaryRepresentationId: repId }))
553
- const repRepo = getRepository(TwinSpaceRepresentation)
554
- const reps = await repRepo.find({ where: { domain: { id: domainId }, space: { id: space.id } } })
555
- for (const rep of reps) if (!!rep.isPrimary !== (rep.id === repId)) await repRepo.save(repRepo.create({ ...rep, isPrimary: rep.id === repId }))
556
- return true
557
- }
558
-
559
- /* ── Area (map/image 폴리곤) ────────────────────────────────────── */
560
- @Directive('@privilege(category: "twin", privilege: "mutation", domainOwnerGranted: true, superUserGranted: true)')
561
- @Mutation(returns => ScalarObject, { description: 'Create/update a polygon area (geometry + drillTo model + binding). Returns {id}.' })
562
- async saveSpaceArea(
563
- @Arg('representationId') representationId: string,
564
- @Arg('area', type => ScalarObject) area: any,
565
- @Ctx() context: ResolverContext
566
- ): Promise<any> {
567
- const domainId = context.state.domain.id
568
- const repo = getRepository(TwinSpaceArea)
569
- const existing = area?.id ? await repo.findOne({ where: { id: area.id } }) : null
570
- /* drillTo/binding 은 선택적 링크 — 클라가 해제(undefined)로 보내면 JSON 에서 키가 빠져 TypeORM 이 "변경 없음"으로
571
- * 무시하므로 기존 값이 남는다. null 로 강제해 명시적으로 컬럼을 비운다(저장은 늘 전체 필드를 보냄 → 덮어쓰기 안전). */
572
- const saved = await repo.save(repo.create({ ...(existing ?? {}), domain: { id: domainId } as any, representation: { id: representationId } as any, name: area.name, type: area.type, geometry: area.geometry, drillTo: area.drillTo ?? null, binding: area.binding ?? null, style: area.style }))
573
- return { ok: true, id: saved.id }
574
- }
575
-
576
- @Directive('@privilege(category: "twin", privilege: "mutation", domainOwnerGranted: true, superUserGranted: true)')
577
- @Mutation(returns => Boolean, { description: 'Delete a polygon area.' })
578
- async deleteSpaceArea(@Arg('id') id: string, @Ctx() context: ResolverContext): Promise<boolean> {
579
- await getRepository(TwinSpaceArea).delete({ domain: { id: context.state.domain.id } as any, id } as any)
580
- return true
581
- }
582
- }