@things-factory/headless-twin 10.1.278 โ†’ 10.1.280

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,969 +0,0 @@
1
- import { pubsub } from '@things-factory/shell'
2
- import { HOST_STEPS, type ReferenceProgress } from './reference-progress.js'
3
- import { REFERENCE_PROGRESS_TOPIC, referenceProgressPayload } from './reference-progress-channel.js'
4
- import { assessMaster } from './reference-assessment.js'
5
- import { Arg, Ctx, Int, Mutation, Query, Resolver, Directive } from 'type-graphql'
6
-
7
- import { Refusal, ScalarObject, getRepository, refusalKey } from '@things-factory/shell'
8
-
9
- import { TwinEngine } from '../../engine/index.js'
10
- import { readRestartPolicy } from '../../engine/restart-policy.js'
11
- import { readBoardEquipment } from '@operato/ops-contract'
12
-
13
- import { applyKnobDefaults, filledKnobKeys } from './knob-defaults.js'
14
- import type { KnobDef } from './template-registry.js'
15
- import { knobDefaultsFilled, pickOriginSite, siteEquipmentAdopted } from './reference-master.js'
16
- import { TwinInstance } from '../twin-instance/twin-instance.js'
17
- import { TwinReference } from './twin-reference.js'
18
- import { runFill, seqReportOf } from './reference-fill-run.js'
19
- import { listReferences, getReference } from './reference-registry.js'
20
- import { getAdapter, listAdapterTypes, listAdapters } from './reference-adapter.js'
21
- import {
22
- discovered, failed, detailOf,
23
- referenceNotFound, adapterUnknown, discoveryFailed, discoveryTimedOut
24
- } from './discovery-result.js'
25
- import { refuseImportSpace } from './ingest-space.js'
26
- import { actuationAddressed, actuationResetPatch } from './actuation-verification.js'
27
- import { refuseIntakeCollision } from './connection-portability.js'
28
- import { startReferenceLiveFeed } from './reference-live.js'
29
- import { listTemplates, getTemplate } from './template-registry.js'
30
-
31
- /*
32
- * Face2 ๋ ˆํผ๋Ÿฐ์Šค ์ธ์ œ์ŠคํŠธ ์ฑ„๋„(ADR-0018) โ€” ๋“ฑ๋ก๋œ ๋ ˆํผ๋Ÿฐ์Šค ์†Œ์Šค๋ฅผ ์—ด๊ฑฐํ•˜๊ณ , ๊ณจ๋ผ์„œ ํŠธ์œˆ์œผ๋กœ ์ธ์ œ์ŠคํŠธ.
33
- * "์—ฐ๊ฒฐํ•˜๊ณ  ๋‚ด ๊ฒƒ์„ ๋ถˆ๋Ÿฌ์˜จ๋‹ค" โ€” ์—”ํ‹ฐํ‹ฐ๋Š” ๋ ˆํผ๋Ÿฐ์Šค์—์„œ ์˜ค์ง€ ์†๋ฐฐ์„ /๋ฐœ๋ช…์ด ์•„๋‹ˆ๋‹ค.
34
- */
35
-
36
- /**
37
- * ๋ฐœ๊ฒฌ ์กฐํšŒ์˜ ์‹œ๊ฐ„ ์ƒํ•œ โ€” 15์ดˆ.
38
- *
39
- * ์–ด๋Œ‘ํ„ฐ ๊ณ„์•ฝ์— ์‹œ๊ฐ„ ์ œํ•œ์ด ์—†๋‹ค. ์ฃผ์†Œ๊ฐ€ ํ‹€๋ฆฐ ์—”๋“œํฌ์ธํŠธ๋Š” ๋‹ตํ•˜์ง€ ์•Š๊ณ , ๊ทธ๋Ÿฌ๋ฉด ํ™”๋ฉด์ด ๋ฌดํ•œํžˆ ๋กœ๋”ฉ๋œ๋‹ค โ€”
40
- * ์‚ฌ์šฉ์ž์—๊ฒŒ๋Š” "์‹คํŒจ" ๋ณด๋‹ค ๋ชปํ•œ ์ƒํƒœ๋‹ค(๋ฌด์—‡์„ ๊ธฐ๋‹ค๋ฆฌ๋Š”์ง€ ๋ชจ๋ฅธ ์ฑ„ ๊ธฐ๋‹ค๋ฆฐ๋‹ค). ์‹ค ์‹œ์Šคํ…œ ์กฐํšŒ๊ฐ€ ๋ช‡ ์ดˆ
41
- * ๊ฑธ๋ฆฌ๋Š” ๊ฒƒ์€ ์ •์ƒ์ด๋ฏ€๋กœ ๋„‰๋„‰ํžˆ ๋‘๋˜, ๋์€ ์žˆ์–ด์•ผ ํ•œ๋‹ค.
42
- */
43
- const DISCOVER_TIMEOUT_MS = 15_000
44
- const TIMEOUT = 'discover-timeout'
45
-
46
- function withTimeout<T>(work: Promise<T>, ms: number): Promise<T> {
47
- return new Promise<T>((resolve, reject) => {
48
- const timer = setTimeout(() => reject(Object.assign(new Error('timeout'), { code: TIMEOUT })), ms)
49
- work.then(
50
- v => { clearTimeout(timer); resolve(v) },
51
- e => { clearTimeout(timer); reject(e) }
52
- )
53
- })
54
- }
55
-
56
- /**
57
- * ๊ฑฐ์ ˆ์„ **์‚ฌ์šฉ์ž ์–ธ์–ด๋กœ ์˜ฎ๊ฒจ ๋ณด๋‚ธ๋‹ค** โ€” ๋ฒˆ์—ญ์€ ํ•œ ๊ณณ์—์„œ ํ•œ๋‹ค.
58
- *
59
- * โ”€โ”€ ์™œ ์„œ๋ฒ„์ธ๊ฐ€ (2026-08-14) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
60
- * ์‹ค์ธก: ์‹คํ–‰ ์ค‘์ธ ํŠธ์œˆ์„ ๋‹ค์‹œ ํ”„๋กœ๋น„์ €๋‹ํ•˜๋ฉด ใ€Œinstance "order-check" is running โ€” stop before
61
- * re-provisioningใ€ ์ด๋ผ๋Š” **์˜์–ด ํ•œ ์ค„์ด ํ† ์ŠคํŠธ๋กœ** ๋–ด๋‹ค(๋‹ค์„ฏ ์–ธ์–ด ์ œํ’ˆ์—์„œ).
62
- *
63
- * ์„œ๋ฒ„์™€ ์•ฑ์€ **๊ฐ™์€ ์‚ฌ์ „**์„ ์“ฐ๊ณ , ์‚ฌ์šฉ์ž์˜ ํ˜„์žฌ ์–ธ์–ด๊ฐ€ ๋ฐฑ์—”๋“œ์— ๋ฐ˜์˜๋œ๋‹ค. ๊ทธ๋ž˜์„œ ์ด ๋ฌธ๊ตฌ๋Š” ์„œ๋ฒ„๊ฐ€
64
- * ์˜ฎ๊ธฐ๋Š” ๊ฒƒ์ด ๋งž๋‹ค โ€” ํ”„๋ ˆ์ž„์›Œํฌ๊ฐ€ ์ด๋ฏธ ๊ฐ™์€ ๊ทœ์•ฝ์ด๋‹ค(`authenticate-401-middleware` ๊ฐ€
65
- * `context.t('error.' + errorCode, detail)` ๋กœ ์˜ฎ๊ธด๋‹ค).
66
- *
67
- * โ”€โ”€ ๊ทธ๋Ÿฌ๋ฉด ์ฝ”๋“œ๋Š” ์™œ ํ•จ๊ป˜ ๋ณด๋‚ด๋‚˜ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
68
- * ํ™”๋ฉด์ด **๊ตฌ์กฐ๋กœ** ๋‹ค๋ค„์•ผ ํ•˜๋Š” ๊ฑฐ์ ˆ์ด ๋”ฐ๋กœ ์žˆ๋‹ค(์ •ํ•ฉ์„ฑ ํ‘œ์˜ ํ–‰ยท์‹ฌ๊ฐ๋„ยท์ˆ˜๋ฆฌ ๋ฒ„ํŠผ โ€” ๊ทธ๊ฒƒ๋“ค์€
69
- * `twin.compose.chk.*` ์ฒ˜๋Ÿผ ํ™”๋ฉด์ด ์˜ฎ๊ธด๋‹ค). ๊ทธ๋ฆฌ๊ณ  ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์•„๋‹Œ ์†Œ๋น„์ž(AIยท์Šคํฌ๋ฆฝํŠธยทAPI)์—๊ฒŒ๋Š”
70
- * ์ฝ”๋“œ๊ฐ€ ๊ณ„์•ฝ์ด๋‹ค. ๋ฌธ์žฅ๊ณผ ์ฝ”๋“œ๋Š” ์„œ๋กœ๋ฅผ ๋Œ€์‹ ํ•˜์ง€ ์•Š๋Š”๋‹ค.
71
- *
72
- * **๋‘ ๊ณณ์—์„œ ๊ฐ™์€ ์ฝ”๋“œ๋ฅผ ์˜ฎ๊ธฐ์ง€ ์•Š๋Š”๋‹ค** โ€” ๋ฌธ์žฅ์ด ๋‘ ๋ฒŒ์ด ๋˜๋ฉด ๋ฐ˜๋“œ์‹œ ์–ด๊ธ‹๋‚œ๋‹ค.
73
- */
74
- function refusal(context: ResolverContext, e: any, fallback: string): Record<string, unknown> {
75
- const code = typeof e?.code === 'string' ? e.code : undefined
76
- const params = e?.params && typeof e.params === 'object' ? e.params : undefined
77
- /* ์˜์–ด canonical ์€ ํ•ญ์ƒ ๋‚จ๊ธด๋‹ค โ€” ๋ฒˆ์—ญ ํ‚ค๊ฐ€ ์—†์œผ๋ฉด ๊ทธ๊ฒƒ์ด ๋ณด์ด๊ณ , ๋กœ๊ทธ์—๋Š” ๊ทธ๊ฒƒ์ด ๋‚จ๋Š”๋‹ค. */
78
- const canonical = e?.message || fallback
79
- /* A shell `Refusal` (a kernel refusal of the model, `asModelRefusal`) has one sentence, under `error.<code>`
80
- โ€” the same key `SpeakRefusals` reads on every other road, so it is not written twice. */
81
- const key = e instanceof Refusal ? refusalKey(e.code) : `twin.provision.err.${code}`
82
- const message =
83
- code && typeof context.t === 'function'
84
- ? String(context.t(key, { ...(params ?? {}), defaultValue: canonical }))
85
- : canonical
86
- return { ok: false, error: message, ...(code ? { errorCode: code } : {}), ...(params ? { errorParams: params } : {}) }
87
- }
88
-
89
- /**
90
- * `siteEquipmentIds` ๋…ธ๋ธŒ๋ฅผ ์ด ํ˜„์žฅ์˜ ์„ค๋น„๋กœ ์ฑ„์šด๋‹ค โ€” **๋น„์–ด ์žˆ์„ ๋•Œ๋งŒ.**
91
- *
92
- * ์‚ฌ์šฉ์ž๊ฐ€ ๋ชฉ๋ก์„ ์ง์ ‘ ์ค€ ๊ฒฝ์šฐ์—๋Š” ์†๋Œ€์ง€ ์•Š๋Š”๋‹ค(๊ณ ๋ฅธ ๊ฒƒ์ด ์ •๋ณธ์ด๋‹ค). ํ˜„์žฅ์ด ์—†์œผ๋ฉด ์ฑ„์šธ ๊ฒƒ๋„ ์—†๋‹ค.
93
- * ๋“ฑ๋ก๋ถ€์˜ ๋ชจ๋ธ์—์„œ ์ฝ๋Š”๋‹ค(๋Ÿฐํƒ€์ž„์ด ์•„๋‹ˆ๋‹ค) โ€” ํŠธ์œˆ์ด ๋„๋Š” ์ค‘์ด ์•„๋‹ˆ์–ด๋„ ๊ทธ ํ˜„์žฅ์˜ ์„ค๋น„๋Š” ์‚ฌ์‹ค์ด๋‹ค.
94
- */
95
- async function resolveSiteEquipmentKnobs(
96
- knobs: readonly KnobDef[] | undefined,
97
- effective: Record<string, any>,
98
- domainId: string,
99
- spaceId?: string
100
- ): Promise<{ key: string; count: number }[]> {
101
- const targets = (knobs ?? []).filter(k => k?.type === 'siteEquipment')
102
- if (!targets.length) return []
103
- const out: { key: string; count: number }[] = []
104
- let ids: { id: string; kind: string }[] | undefined
105
- for (const k of targets) {
106
- const given = effective[k.key]
107
- if (Array.isArray(given) && given.length) continue // ์‚ฌ์šฉ์ž๊ฐ€ ๊ณ ๋ฅธ ๊ฒƒ์ด ์ •๋ณธ์ด๋‹ค
108
- if (!spaceId) {
109
- effective[k.key] = []
110
- continue
111
- }
112
- if (ids === undefined) {
113
- const rows = await getRepository(TwinInstance).find({ where: { domain: { id: domainId }, spaceId } })
114
- const seen = new Map<string, { id: string; kind: string }>()
115
- for (const r of rows) {
116
- for (const e of readBoardEquipment((r.model as any) ?? {})) {
117
- if (!e?.id || seen.has(String(e.id))) continue
118
- seen.set(String(e.id), { id: String(e.id), kind: String((e as any).kind ?? '') })
119
- }
120
- }
121
- ids = [...seen.values()]
122
- }
123
- effective[k.key] = ids
124
- out.push({ key: k.key, count: ids.length })
125
- }
126
- return out
127
- }
128
-
129
- @Resolver()
130
- export class TwinReferenceResolver {
131
- /** ๋“ฑ๋ก๋œ ๋ ˆํผ๋Ÿฐ์Šค ์†Œ์Šค ๋ชฉ๋ก(์š”์•ฝ + ์ด๋ฏธ ์ธ์ œ์ŠคํŠธ๋๋Š”์ง€). */
132
- @Query(returns => [ScalarObject], { description: 'List registered reference sources (virtual or real adapters) available to ingest.' })
133
- async referenceSources(@Ctx() context: ResolverContext): Promise<any[]> {
134
- const domainId = context.state.domain.id
135
- const out: any[] = []
136
- for (const m of listReferences()) {
137
- const reg = await getRepository(TwinInstance).findOne({ where: { domain: { id: domainId }, instanceId: m.source } })
138
- out.push({
139
- source: m.source,
140
- system: m.system,
141
- siteName: m.siteName,
142
- locationCount: m.locations.filter(l => l.role === 'location' || l.role === 'both').length,
143
- areaCount: m.locations.filter(l => l.role === 'area' || l.role === 'both').length,
144
- equipmentCount: (m.equipment ?? []).length,
145
- imported: !!reg,
146
- running: TwinEngine.owns(domainId, m.source)
147
- })
148
- }
149
- return out
150
- }
151
-
152
- /** ์†Œ์Šค๋ฅผ ์ธ์ œ์ŠคํŠธ โ†’ ๊ณต๊ฐ„ + ์ธ์Šคํ„ด์Šค ์ƒ์„ฑ(provision). start ๋ฉด ๊ธฐ๋™๊นŒ์ง€. */
153
- @Directive('@privilege(category: "twin", privilege: "mutation", domainOwnerGranted: true, superUserGranted: true)')
154
- @Mutation(returns => ScalarObject, {
155
- description:
156
- "Ingest a registered reference source into a twin (space + instance). Optionally start it. restartPolicy declares how the new twin treats its own past on reboot: 'resync' (read the source again) | 'resume' (continue its journal) | 'reset' (wipe and replay from the seed). It has no default โ€” a twin cannot exist without the declaration."
157
- })
158
- async importFromReference(
159
- @Arg('source') source: string,
160
- @Arg('restartPolicy') restartPolicy: string,
161
- @Ctx() context: ResolverContext,
162
- @Arg('start', { nullable: true }) start?: boolean
163
- ): Promise<any> {
164
- const master = getReference(source)
165
- if (!master) return { ok: false, error: `unknown reference source: ${source}` }
166
- const domainId = context.state.domain.id
167
- try {
168
- /* ์–ด๋””์„œ ์™”๋Š”์ง€ ๋‚จ๊ธด๋‹ค โ€” ์ด๊ฒƒ์ด ์—†์œผ๋ฉด ์ด ํŠธ์œˆ์€ ๋‘ ๋ฒˆ ๋‹ค์‹œ ์ฝ์„ ์ˆ˜ ์—†๋‹ค.
169
- ์ด ๊ฒฝ๋กœ๋Š” **๋ฉ”๋ชจ๋ฆฌ์— ๋“ฑ๋ก๋œ ๋งˆ์Šคํ„ฐ ํ•˜๋‚˜**๋ฅผ ๊ทธ๋Œ€๋กœ ์“ฐ๋ฏ€๋กœ ์‚ฌ์ดํŠธ ์„œ์ˆ ์ž๊ฐ€ ์—†๋‹ค(์‚ฌ์ดํŠธ ์ •์ฒด๋ฅผ
170
- ์ ์„ ๊ฒƒ์ด ์—†๋‹ค). ๋‹ค์‹œ ์ฝ๊ธฐ๋„ ๊ฐ™์€ ๋“ฑ๋ก๋ณธ์„ ์ง‘์œผ๋ฏ€๋กœ ์‚ฌ์ดํŠธ๋ฅผ ์ฐพ์„ ํ•„์š”๊ฐ€ ์—†๋‹ค. */
171
- const res = await TwinEngine.ingestMaster(
172
- domainId,
173
- { ...master, origin: { kind: 'reference', source } },
174
- readRestartPolicy(restartPolicy, 'importFromReference'),
175
- undefined,
176
- context.state.user
177
- )
178
- if (start) await TwinEngine.startFromRegistry(domainId, res.instanceId)
179
- return { ok: true, instanceId: res.instanceId, spaceId: res.spaceId, warnings: res.warnings }
180
- } catch (e: any) {
181
- return refusal(context, e, 'ingest failed')
182
- }
183
- }
184
-
185
- /**
186
- * **์›๋ณธ์—์„œ ๋‹ค์‹œ ์ฝ๋Š”๋‹ค** โ€” ์ด ํŠธ์œˆ์˜ ๊ตฌ์กฐ๋ฅผ ์ง€๊ธˆ์˜ ์›๋ณธ ๋ชจ์Šต์œผ๋กœ ๊ฐฑ์‹ ํ•œ๋‹ค.
187
- *
188
- * โ”€โ”€ ์™œ ์ง„์ž…์ ์ด ํ•˜๋‚˜์—ฌ์•ผ ํ•˜๋‚˜ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
189
- * ์ง€๊ธˆ๊นŒ์ง€ ๋‹ค์‹œ ์ฝ๊ธฐ๋Š” **๋งŒ๋“œ๋Š” ๊ฒฝ๋กœ์— ์–นํ˜€ ์žˆ์—ˆ๋‹ค**(`importFromReference`ยท`createFromTemplate`).
190
- * ๊ทธ๋ž˜์„œ ์ด๋ฏธ ์žˆ๋Š” ํŠธ์œˆ์„ ๊ฐฑ์‹ ํ•˜๋ ค๋ฉด ๋งŒ๋“ค ๋•Œ ๋ฌด์—‡์„ ๊ณจ๋ž๋Š”์ง€ ์‚ฌ๋žŒ์ด ๊ธฐ์–ตํ•ด ๊ฐ™์€ ํ™”๋ฉด์„ ๋‹ค์‹œ
191
- * ๊ฑฐ์ณ์•ผ ํ–ˆ๊ณ , ํ…œํ”Œ๋ฆฟ์œผ๋กœ ์„ธ์šด ํŠธ์œˆ์€ ๋…ธ๋ธŒ๋ฅผ ์–ด๋””์—๋„ ์•ˆ ๋‚จ๊ฒจ์„œ **์•„์˜ˆ ๊ฐฑ์‹ ํ•  ๋ฐฉ๋ฒ•์ด ์—†์—ˆ๋‹ค.**
192
- * ์›์ฒœ์ด ๋ฌด์—‡์ด๋“  ํŠธ์œˆ ์ž์‹ ์ด ๊ธฐ์–ตํ•˜๊ฒŒ ํ•˜๊ณ (`TwinInstance.origin`), ์—ฌ๊ธฐ ํ•œ ๊ณณ์—์„œ ๊ทธ๊ฒƒ์„ ๋”ฐ๋ฅธ๋‹ค.
193
- *
194
- * โ”€โ”€ ๋ฌด์—‡์ด ์ผ์–ด๋‚˜๋‚˜ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
195
- * ์›๋ณธ์—์„œ ๋งˆ์Šคํ„ฐ๋ฅผ ๋‹ค์‹œ ์–ป์–ด ์ธ์ œ์ŠคํŠธ์— ๊ทธ๋Œ€๋กœ ํƒœ์šด๋‹ค. ๊ทธ๋‹ค์Œ์€ ์ธ์ œ์ŠคํŠธ๊ฐ€ ์•„๋Š” ์ผ์ด๋‹ค โ€”
196
- * ๊ตฌ์กฐ๊ฐ€ ๋‹ฌ๋ผ์กŒ์œผ๋ฉด ์ƒˆ ๋ฆฌ๋น„์ „์ด ๋‚จ๊ณ , ๊ตฌ์กฐ ํ–‰์ด ๋‹ค์‹œ ๊ทธ๋ ค์ง€๊ณ , **์‹คํ–‰ ์ค‘์ธ ๋ฏธ๋Ÿฌ๋Š” ๋ฉˆ์ถ”์ง€ ์•Š๊ณ 
197
- * ์ „ํ™˜ํ•œ๋‹ค.** ์‹คํ–‰ ์ค‘์ธ ์‹œ๋ฎฌ๋ ˆ์ด์…˜์€ ๊ฑฐ์ ˆ๋œ๋‹ค(์กฐ๊ฑด์ด ๋ฐ”๋€ ์‹คํ—˜์€ ๋‹ค๋ฅธ ์‹คํ—˜์ด๋‹ค).
198
- *
199
- * โ”€โ”€ ๋ชป ํ•  ๋•Œ๋Š” ๋ชป ํ•œ๋‹ค๊ณ  ํ•œ๋‹ค โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
200
- * `origin` ์ด ์—†๋Š” ํŠธ์œˆ(์ด ์ž๋ฆฌ๊ฐ€ ์ƒ๊ธฐ๊ธฐ ์ „์— ๋งŒ๋“ค์–ด์ง„ ๊ฒƒ)์€ ๋‹ค์‹œ ์ฝ์„ ์ˆ˜ ์—†๋‹ค. ์ง์ž‘์œผ๋กœ
201
- * ํ…œํ”Œ๋ฆฟ์„ ๊ณ ๋ฅด๊ฑฐ๋‚˜ ์ฒซ ๋ ˆํผ๋Ÿฐ์Šค๋ฅผ ์ง‘์ง€ ์•Š๋Š”๋‹ค โ€” ์—‰๋šฑํ•œ ์›๋ณธ์œผ๋กœ ํ˜„์žฅ์„ ๋ฎ๋Š” ๊ฒƒ์ด ํ›จ์”ฌ ๋‚˜์˜๋‹ค.
202
- */
203
- /**
204
- * **๊ทธ ๋ฒˆํ˜ธ๋“ค์ด ๋ฌด์—‡์ด์—ˆ๋‚˜** โ€” ๊ตฌ๋ฉ์„ ๋งŒ๋‚ฌ์„ ๋•Œ ์›๋ณธ์— ๋ฌป๋Š”๋‹ค.
205
- *
206
- * `fillTwinScope` ๊ฐ€ ใ€Œ418 ๋‹ค์Œ์ด 420ใ€์ด๋ผ๊ณ  ๋ฉˆ์ถ”๋ฉด, ๊ทธ 419 ๊ฐ€ ๋ฌด์—‡์ด์—ˆ๋Š”์ง€ ์•Œ์•„์•ผ ๋‹ค์Œ์„
207
- * ์ •ํ•  ์ˆ˜ ์žˆ๋‹ค. ๊ทธ ๊ธธ์ด ์—†์–ด์„œ ์‚ฌ๋žŒ์ด ์›๋ณธ DB ๋ฅผ ์†์œผ๋กœ ๋’ค์กŒ๋‹ค.
208
- *
209
- * **ํŒ์ •ํ•˜์ง€ ์•Š๋Š”๋‹ค.** ์›๋ณธ์ด ์•„๋Š” ๊ฒƒ์„ ๊ทธ๋Œ€๋กœ ๋‚ธ๋‹ค โ€” `lost` ๋Š” ใ€Œ๋ฒˆํ˜ธ๋Š” ๋‚˜๊ฐ”๋Š”๋ฐ ํ–‰์ด ์—†๋‹คใ€์ด์ง€
210
- * ใ€Œํฌ๊ธฐํ•ด๋„ ๋œ๋‹คใ€๊ฐ€ ์•„๋‹ˆ๋‹ค. ๊ทธ ํŒ๋‹จ์€ ๋˜๋Œ๋ฆด ์ˆ˜ ์—†์–ด์„œ(์ปค์„œ๊ฐ€ ์ง€๋‚˜๊ฐ€๋ฉด ๋์ด๋‹ค) ์‚ฌ๋žŒ์˜ ๋ชซ์ด๋‹ค.
211
- */
212
- /*
213
- * **์ฝ๊ธฐ์ธ๋ฐ ์“ฐ๊ธฐ์™€ ๊ฐ™์€ ๋ฌธ์„ ์“ด๋‹ค.** ์ด ๋ฌธ์€ ์›๋ณธ์˜ ๋ด‰ํˆฌ๋ฅผ ๊ทธ๋Œ€๋กœ ๋ƒ…๋‹ˆ๋‹ค โ€” ๊ทธ ์‹œ์Šคํ…œ์ด ๋ฌด์—‡์„
214
- * ์–ธ์ œ ๋ณด๋ƒˆ๋Š”์ง€๊ฐ€ ๋“œ๋Ÿฌ๋‚ฉ๋‹ˆ๋‹ค. `twinConnectionBundle` ๊ณผ ๊ฐ™์€ ๋ฌด๊ฒŒ์ด๊ณ , ์ด ์ €์žฅ์†Œ๋Š” ๊ทธ ๋ฌด๊ฒŒ๋ฅผ
215
- * ใ€Œ์“ฐ๊ธฐ๋Š” GATE ํ•˜๋‚˜ใ€๋กœ ํ†ต์ผํ•ด ๋‘์—ˆ์Šต๋‹ˆ๋‹ค(ADR-0027).
216
- */
217
- @Directive('@privilege(category: "twin", privilege: "mutation", domainOwnerGranted: true, superUserGranted: true)')
218
- @Query(returns => ScalarObject, {
219
- description:
220
- 'Ask the source what it knows about a range of sequence numbers. Each number comes back as present ยท lost ยท unissued ยท unknown โ€” the source reports, the human decides.'
221
- })
222
- async twinSeqReport(
223
- @Arg('instanceId') instanceId: string,
224
- @Arg('scope') scope: string,
225
- @Arg('from', type => Int) from: number,
226
- @Arg('to', type => Int) to: number,
227
- @Ctx() context: ResolverContext
228
- ): Promise<any> {
229
- return seqReportOf({ domainId: context.state.domain.id, instanceId, scope, from, to })
230
- }
231
-
232
- /**
233
- * **๋น ์ง„ ๊ตฌ๊ฐ„์„ ๋‹ค์‹œ ๋ฐ›๋Š”๋‹ค** โ€” ํ›…์€ ๋ฐ˜๋“œ์‹œ ๋†“์นฉ๋‹ˆ๋‹ค: ์šฐ๋ฆฌ๊ฐ€ ๋‚ด๋ ค๊ฐ€ ์žˆ์„ ๋•Œ, ๊ทธ์ชฝ์ด ๋ชป ๋ณด๋ƒˆ์„ ๋•Œ.
234
- *
235
- * โ”€โ”€ ์™œ ๋ฌธ์ด ํ•„์š”ํ•œ๊ฐ€ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
236
- * ๋ฒˆํ˜ธ๊ฐ€ ๋น„๋ฉด `takeInSequence` ๊ฐ€ ์•Œ์•„์ฑ•๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ **์•Œ์•„์ฑˆ ๋’ค ๊ทธ ์‚ฌ์ด๋ฅผ ์ฑ„์šฐ๋Š” ๊ธธ์ด ์—†์—ˆ์Šต๋‹ˆ๋‹ค.**
237
- * `fillSince` ๋Š” 2026-08-31 ๋ถ€ํ„ฐ ์žˆ์—ˆ๊ณ  ๋ถ€๋ฅด๋Š” ๊ณณ์ด 0๊ณณ์ด์—ˆ์Šต๋‹ˆ๋‹ค.
238
- *
239
- * ์‹ค์ œ๋กœ plant ์•„์›ƒ๋ฐ•์Šค์˜ 1~20 ์„ ์žƒ๊ณ  ๋˜์ฐพ์„ ๋ฐฉ๋ฒ•์ด ์—†๋Š” ์ƒํƒœ๋ฅผ ๋งŒ๋‚ฌ์Šต๋‹ˆ๋‹ค.
240
- *
241
- * โ”€โ”€ `from` ์€ ์žƒ์€ ๊ตฌ๊ฐ„์„ ๋˜์ฐพ์„ ๋•Œ๋งŒ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
242
- * ์ฃผ์ง€ ์•Š์œผ๋ฉด ์ปค์„œ๋ถ€ํ„ฐ ์ด์–ด ๋ฐ›์Šต๋‹ˆ๋‹ค. ์ฃผ๋ฉด **๊ทธ ๋ฒˆํ˜ธ๋ถ€ํ„ฐ ๋‹ค์‹œ ๋ฌป์Šต๋‹ˆ๋‹ค** โ€” ์ด๋ฏธ ๋“ค์–ด๊ฐ„ ๊ฒƒ์€ ์œ ์ž…
243
- * ๊ฒฝ๊ณ„๊ฐ€ ๋˜ํ’€์ด๋กœ ๊ฑฐ๋ฅด๋ฏ€๋กœ ๋‘ ๋ฒˆ ์•‰์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
244
- *
245
- * ์ €์žฅ๋œ ์ปค์„œ๋ฅผ ๋˜๋Œ๋ฆฌ์ง€ ์•Š์Šต๋‹ˆ๋‹ค. `fillSince` ๋Š” ๋ฒˆํ˜ธ ๊ฒ€์‚ฌ๋ฅผ ์ง€๋‚œ ๋งŒํผ๋งŒ ์ ์œผ๋ฏ€๋กœ, ๋˜์ฐพ์€ ๋’ค
246
- * ์ปค์„œ๊ฐ€ ์›๋ž˜ ์ž๋ฆฌ๋ณด๋‹ค ์•ž์„œ์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
247
- */
248
- @Directive('@privilege(category: "twin", privilege: "mutation", domainOwnerGranted: true, superUserGranted: true)')
249
- @Mutation(returns => ScalarObject, {
250
- description:
251
- 'Fetch the stretch this twin missed for one scope and ingest it through the same checks as the push path. Without `from` it continues from the cursor; with `from` it asks again from that number โ€” already-ingested facts are filtered as duplicates.'
252
- })
253
- async fillTwinScope(
254
- @Arg('instanceId') instanceId: string,
255
- @Arg('scope', { description: 'The unit the source numbers by โ€” the same name its envelopes carry (e.g. EVENTS).' }) scope: string,
256
- @Ctx() context: ResolverContext,
257
- @Arg('from', type => Int, { nullable: true, description: 'Ask again from this sequence number. Omit to continue from the cursor.' })
258
- from?: number,
259
- @Arg('maxPages', type => Int, { nullable: true, description: 'Page limit for one run. Default 50.' }) maxPages?: number
260
- ): Promise<any> {
261
- return runFill({ domainId: context.state.domain.id, instanceId, scope, from: from ?? undefined, maxPages: maxPages ?? undefined })
262
- }
263
-
264
- @Directive('@privilege(category: "twin", privilege: "mutation", domainOwnerGranted: true, superUserGranted: true)')
265
- @Mutation(returns => ScalarObject, {
266
- description:
267
- "Re-read a twin's structure from the source it remembers (TwinInstance.origin). Works for both reference- and template-born twins. A running mirror adopts the new structure without stopping; a running simulation is refused."
268
- })
269
- async resyncTwin(@Arg('instanceId') instanceId: string, @Ctx() context: ResolverContext): Promise<any> {
270
- const domainId = context.state.domain.id
271
- const inst = await getRepository(TwinInstance).findOne({ where: { domain: { id: domainId }, instanceId } })
272
- if (!inst) return { ok: false, error: `unknown twin: ${instanceId}` }
273
-
274
- const origin = inst.origin as any
275
- if (!origin?.kind)
276
- return {
277
- ok: false,
278
- error: `twin "${instanceId}" does not remember where it came from โ€” it cannot be re-read. Re-create it from its template or reference so the origin is recorded.`
279
- }
280
-
281
- /*
282
- * ์ง„ํ–‰์„ ์•Œ๋ฆฌ๋Š” ํ†ต๋กœ โ€” ๋งŒ๋“ค ๋•Œ์™€ **๊ฐ™์€ ๊ฒƒ**์ด๋‹ค. ๋ ˆํผ๋Ÿฐ์Šค์—์„œ ์˜จ ํŠธ์œˆ๋งŒ ์•Œ๋ฆฐ๋‹ค(ํ…œํ”Œ๋ฆฟ์—์„œ ์˜จ
283
- * ํŠธ์œˆ์€ ์›๋ณธ์„ ์ฝ์ง€ ์•Š์œผ๋ฏ€๋กœ ์•Œ๋ฆด ๋‹จ๊ณ„๊ฐ€ ์—†๋‹ค).
284
- *
285
- * ๋ถ„๋ชจ๋Š” ๊ทธ ์–ด๋Œ‘ํ„ฐ๊ฐ€ ์„ ์–ธํ•œ ๋‹จ๊ณ„ ์ˆ˜๋‹ค(`masterSteps`) + ํ˜ธ์ŠคํŠธ ๋‹จ๊ณ„. ์„ ์–ธํ•˜์ง€ ์•Š์€ ์–ด๋Œ‘ํ„ฐ๋Š”
286
- * `total` ์ด ์—†๊ณ , ๊ทธ๋•Œ ํ™”๋ฉด์€ ํผ์„ผํŠธ๋ฅผ ๋งŒ๋“ค์ง€ ์•Š๋Š”๋‹ค โ€” ๋ชจ๋ฅด๋Š” ์ง„ํ–‰๋ฅ ์„ ์ง€์–ด๋‚ด์ง€ ์•Š๋Š”๋‹ค.
287
- */
288
- const progressSource: string | undefined = origin.kind === 'reference' ? origin.source : undefined
289
- const refRow = progressSource
290
- ? await getRepository(TwinReference).findOne({ where: { domain: { id: domainId }, source: progressSource } })
291
- : null
292
- const adapterForSteps = refRow ? getAdapter(refRow.adapterType) : undefined
293
- const declaredSteps = adapterForSteps?.masterSteps?.length
294
- const stepTotal = declaredSteps === undefined ? undefined : declaredSteps + HOST_STEPS.length
295
- const siteId = origin.siteId ?? instanceId
296
- let lastDone = 0
297
- let lastStep: ReferenceProgress['step'] = (adapterForSteps?.masterSteps?.[0] ?? 'provision') as ReferenceProgress['step']
298
- const emit = (p: Omit<ReferenceProgress, 'siteCount' | 'siteId' | 'siteIndex'>) => {
299
- if (!progressSource) return
300
- lastDone = p.done
301
- lastStep = p.step
302
- pubsub.publish(
303
- REFERENCE_PROGRESS_TOPIC,
304
- referenceProgressPayload(domainId, { ...p, siteId, siteIndex: 0, siteCount: 1, total: p.total ?? stepTotal, source: progressSource })
305
- )
306
- }
307
-
308
- try {
309
- const master = await this.masterFromOrigin(domainId, origin, instanceId, st =>
310
- emit({ step: st.key, done: st.done, total: stepTotal, detail: st.detail })
311
- )
312
- emit({ step: 'provision', done: declaredSteps ?? 0, total: stepTotal })
313
- /*
314
- * **์–ด๋А ํ˜„์žฅ์˜ ํŠธ์œˆ์ด์—ˆ๋‚˜๋ฅผ ์ง€ํ‚จ๋‹ค** โ€” ๋‹ค์‹œ ์ฝ๊ธฐ๋Š” ๊ตฌ์กฐ๋งŒ ์ƒˆ๋กœ ๊ทธ๋ฆฌ๋Š” ์ผ์ด๊ณ , ํ˜„์žฅ ์†Œ์†์€ ๊ทธ๋•Œ
315
- * ์‚ฌ๋žŒ์ด ๊ณ ๋ฅธ ๊ฒƒ์ด๋‹ค. ๊ธฐ์–ต์ด ์—†๋Š” ์˜› ํŠธ์œˆ์€ **์ง€๊ธˆ ์žˆ๋Š” ํ˜„์žฅ**์„ ๊ทธ๋Œ€๋กœ ์“ด๋‹ค(๋นˆ์†์œผ๋กœ ๋‘๋ฉด ํ…œํ”Œ๋ฆฟ
316
- * ๊ธฐ๋ณธ ๊ณต๊ฐ„์œผ๋กœ ์˜ฎ๊ฒจ ๊ฐ„๋‹ค โ€” ์•Œ๋ฆฌ์ง€ ์•Š๊ณ  ํ˜„์žฅ์„ ๋– ๋‚˜๊ณ  ๊ทธ ํ˜„์žฅ์˜ ๊ฐ’๋„ ์žƒ๋Š”๋‹ค).
317
- */
318
- const keepSpace = origin.spaceId || inst.spaceId
319
- /* ๋‹ค์‹œ ์ฝ๊ธฐ๋Š” **๊ตฌ์กฐ๋งŒ** ์ƒˆ๋กœ ๊ทธ๋ฆฐ๋‹ค โ€” ์žฌ๊ธฐ๋™ ์ •์ฑ…์€ ๊ทธ ํŠธ์œˆ์˜ ์„ ์–ธ์ด๋ฏ€๋กœ ๊ทธ๋Œ€๋กœ ์ง€ํ‚จ๋‹ค. */
320
- const res = await TwinEngine.ingestMaster(
321
- domainId,
322
- master,
323
- readRestartPolicy(inst.restartPolicy, `resync of twin "${instanceId}"`),
324
- keepSpace ? { spaceId: keepSpace } : undefined,
325
- context.state.user
326
- )
327
- emit({ step: 'start', done: (declaredSteps ?? 0) + 1, total: stepTotal })
328
- /*
329
- * **์ดํ‰๋„ ์ƒˆ๋กœ ๋‚จ๊ธด๋‹ค.** ๋‹ค์‹œ ์ฝ์—ˆ๋Š”๋ฐ ์ดํ‰์ด ์˜›๊ฒƒ์ด๋ฉด ํ™”๋ฉด์ด ์ง€๋‚œ ์ฝ๊ธฐ๋ฅผ ํ˜„์žฌ๋กœ ๋ณด์—ฌ ์ค€๋‹ค โ€”
330
- * ๊ทธ ํ‘œ๋ฅผ ๋ณด๊ณ  ์‚ฌ๋žŒ์ด ์›๋ณธ์„ ๊ณ ์น˜๋Ÿฌ ๊ฐ„๋‹ค.
331
- */
332
- if (refRow && progressSource) {
333
- const prevSpec = (refRow.scopeSpec as any) ?? {}
334
- await getRepository(TwinReference).update(
335
- { id: refRow.id },
336
- {
337
- scopeSpec: { ...prevSpec, assessments: { ...(prevSpec.assessments ?? {}), [siteId]: assessMaster(master, siteId) } },
338
- /*
339
- * โ”€โ”€ ์ฝ์€ ์‹œ๊ฐ๊ณผ ๊ฒฝ๊ณ  ์ˆ˜๋„ ์ƒˆ๋กœ ๋‚จ๊ธด๋‹ค (2026-09-06) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
340
- * ์ฒ˜์Œ ์—ฐ๊ฒฐํ•˜๋Š” ๊ฒฝ๋กœ๋Š” ์ด ๋‘˜์„ ์“ฐ๋Š”๋ฐ(ยง`connectReference`) ๋‹ค์‹œ ์ฝ๋Š” ๊ฒฝ๋กœ๋Š” ์ดํ‰๋งŒ
341
- * ์ผ๋‹ค. ๊ทธ๋ž˜์„œ `lastSyncedAt` ์ด ใ€Œ์ฒ˜์Œ ํŠธ์œˆ์„ ๋งŒ๋“  ์‹œ๊ฐใ€์— ๋จธ๋ฌผ๋ €๋‹ค.
342
- *
343
- * ์ด๊ฒƒ์ด ์‹ค์ œ๋กœ ์ž˜๋ชป๋œ ํŒ๋‹จ์„ ๋งŒ๋“ค์—ˆ๋‹ค. ์ธํ‹ฐ๊ทธ๋ ˆ์ด์…˜ ๋ ˆ์ธ์ด ์ปค๋„ฅํ„ฐ๋ฅผ ๊ณ ์น˜๊ณ  ์žฌ๋™๊ธฐํ™”๋ฅผ
344
- * ์ฒญํ•œ ๋’ค ์ด ์นธ์œผ๋กœ ํ™•์ธํ–ˆ๋Š”๋ฐ, ๊ฐ’์ด ๊ทธ๋Œ€๋กœ์—ฌ์„œ ใ€Œ์•ˆ ๋Œ์•˜๋‹คใ€๊ณ  ๊ฒฐ๋ก  ๋‚ด๋ ธ๋‹ค. ์‹ค์ œ๋กœ๋Š”
345
- * ๋Œ์•˜๊ณ  ํ’ˆ๋ชฉ 106๊ฑด์ด ๋“ค์–ด์™€ ์žˆ์—ˆ๋‹ค.
346
- *
347
- * ๊ฒฝ๊ณ  ์ˆ˜๋„ ๊ฐ™๋‹ค. ๋‹ค์‹œ ์ฝ์œผ๋ฉด์„œ ๊ฒฝ๊ณ ๊ฐ€ ์ค„๊ฑฐ๋‚˜ ๋Š˜์—ˆ๋Š”๋ฐ ํ™”๋ฉด์€ ์ง€๋‚œ ์ˆ˜๋ฅผ ๋ณด์ธ๋‹ค.
348
- */
349
- lastSyncedAt: new Date().toISOString(),
350
- lastWarningCount: Array.isArray(res.warnings) ? res.warnings.length : null
351
- } as any
352
- )
353
- }
354
- return { ok: true, instanceId: res.instanceId, spaceId: res.spaceId, warnings: res.warnings }
355
- } catch (e: any) {
356
- /* ๋ง‰ํžŒ ์ž๋ฆฌ๋ฅผ ๊ทธ๋Œ€๋กœ ์•Œ๋ฆฐ๋‹ค โ€” ใ€Œ๋๋‚ธ ์ˆ˜ใ€์˜ ๋‹ค์Œ ๋‹จ๊ณ„๋‹ค(๋งŒ๋“ค ๋•Œ์™€ ๊ฐ™์€ ๊ทœ์น™). */
357
- emit({
358
- step: (adapterForSteps?.masterSteps?.[lastDone] ?? lastStep) as ReferenceProgress['step'],
359
- done: lastDone,
360
- total: stepTotal,
361
- failed: true,
362
- error: e?.message ?? String(e)
363
- })
364
- return refusal(context, e, 'resync failed')
365
- }
366
- }
367
-
368
- /** ๊ธฐ์–ตํ•ด ๋‘” ์›์ฒœ์—์„œ ๋งˆ์Šคํ„ฐ๋ฅผ ๋‹ค์‹œ ์–ป๋Š”๋‹ค โ€” ์–ด๋Œ‘ํ„ฐ๋“  ํ…œํ”Œ๋ฆฟ์ด๋“  ์ด ํ•œ ๊ณณ์—์„œ ๊ฐˆ๋ฆฐ๋‹ค. */
369
- private async masterFromOrigin(
370
- domainId: string,
371
- origin: any,
372
- instanceId: string,
373
- /*
374
- * **๋‹ค์‹œ ์ฝ๊ธฐ๋„ ์ง„ํ–‰์„ ๋ณด์—ฌ์•ผ ํ•œ๋‹ค** (2026-08-22).
375
- *
376
- * ๋งŒ๋“ค ๋•Œ๋Š” 8๋‹จ๊ณ„๊ฐ€ ํ™”๋ฉด์— ํ๋ฅด๋Š”๋ฐ ๋‹ค์‹œ ์ฝ์„ ๋•Œ๋Š” ์•„๋ฌด๊ฒƒ๋„ ์—†์—ˆ๋‹ค โ€” ์‚ฌ์šฉ์ž๊ฐ€ ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๊ณ 
377
- * ใ€Œ์ง„ํ–‰๋˜๊ณ  ์žˆ๋Š”์ง€ ํ™•์ธํ•  ์ˆ˜๊ฐ€ ์—†๋‹คใ€๊ณ  ํ–ˆ๋‹ค. ์‹ค์ œ๋กœ chef ๋Š” ์›๋ณธ ์ฝ๊ธฐ๊ฐ€ 14~21์ดˆ์ด๊ณ  ๊ทธ ์‚ฌ์ด
378
- * ํ™”๋ฉด์— ์•„๋ฌด ๋ณ€ํ™”๊ฐ€ ์—†๋‹ค.
379
- *
380
- * ํ†ต๋กœ๋Š” ๋งŒ๋“ค ๋•Œ์™€ **๊ฐ™์€ ๊ฒƒ**์ด๋‹ค(`twin-reference-progress`). ํ™”๋ฉด๋„ ๊ฐ™์€ ํ‘œ์‹œ๋ฅผ ์“ด๋‹ค โ€”
381
- * ๋‘ ๋ฒŒ์„ ๋งŒ๋“ค๋ฉด ํ•œ์ชฝ๋งŒ ๊ณ ์ณ์ง€๊ณ  ์‚ฌ๋žŒ์€ ๊ฐ™์€ ์ผ์ด ๋‹ค๋ฅด๊ฒŒ ๋ณด์ด๋Š” ๊ฒƒ์„ ๊ฒฐํ•จ์œผ๋กœ ์ฝ๋Š”๋‹ค.
382
- */
383
- onStep?: (s: { key: any; done: number; total?: number; detail?: string }) => void
384
- ): Promise<any> {
385
- if (origin.kind === 'template') {
386
- const tpl = getTemplate(origin.templateId)
387
- if (!tpl) throw new Error(`unknown template: ${origin.templateId} โ€” this twin cannot be rebuilt`)
388
- /* ์ €์žฅํ•ด ๋‘” ๋…ธ๋ธŒ ๊ทธ๋Œ€๋กœ ๋‹ค์‹œ ์ง“๋Š”๋‹ค. ๊ธฐ๋ณธ๊ฐ’์„ ๋‹ค์‹œ ์ž…ํžˆ์ง€ ์•Š๋Š”๋‹ค โ€” ๊ทธ ์‚ฌ์ด ํ…œํ”Œ๋ฆฟ์˜ ๊ธฐ๋ณธ๊ฐ’์ด
389
- ๋ฐ”๋€Œ์—ˆ๋‹ค๋ฉด, ์‚ฌ์šฉ์ž๊ฐ€ ๊ณ ๋ฅด์ง€ ์•Š์€ ๊ฐ’์ด ์•Œ๋ฆฌ์ง€ ์•Š๊ณ  ํ˜„์žฅ์„ ๋ฐ”๊พธ๊ฒŒ ๋œ๋‹ค. */
390
- const { master } = tpl.build(origin.params ?? {})
391
- master.origin = origin
392
- return master
393
- }
394
-
395
- /* ๋ ˆํผ๋Ÿฐ์Šค โ€” ๋ฉ”๋ชจ๋ฆฌ ๋“ฑ๋ก๋ณธ์ด ์žˆ์œผ๋ฉด ๊ทธ๊ฒƒ, ์—†์œผ๋ฉด ์ €์žฅ๋œ ์–ด๋Œ‘ํ„ฐ ์—ฐ๊ฒฐ๋กœ ๋‹ค์‹œ ๊ฐ€์ ธ์˜จ๋‹ค.
396
- (๋ถ€ํŒ… ๋’ค์—๋Š” ๋ฉ”๋ชจ๋ฆฌ ๋“ฑ๋ก๋ณธ์ด ๋น„์–ด ์žˆ์œผ๋ฏ€๋กœ ์–ด๋Œ‘ํ„ฐ ๊ฒฝ๋กœ๊ฐ€ ์‹ค์ œ๋กœ ์“ฐ์ด๋Š” ๊ธธ์ด๋‹ค.) */
397
- const inMemory = getReference(origin.source)
398
- if (inMemory) return { ...inMemory, origin }
399
-
400
- const ref = await getRepository(TwinReference).findOne({ where: { domain: { id: domainId }, source: origin.source } })
401
- if (!ref) throw new Error(`reference "${origin.source}" is no longer registered โ€” nothing to re-read from`)
402
- const adapter = getAdapter(ref.adapterType)
403
- if (!adapter) throw new Error(`no adapter for type: ${ref.adapterType}`)
404
- const cfg = ref.connectionConfig ?? {}
405
- const sites = await adapter.discoverSites(cfg)
406
-
407
- /*
408
- * โ”€โ”€ ๋ฌด์—‡์„ ๋ฌด์—‡๊ณผ ๊ฒฌ์ฃผ๋‚˜ (2026-08-18 ์‹ค์ธก์œผ๋กœ ๊ณ ์นจ) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
409
- *
410
- * ์˜ˆ์ „์—๋Š” `master.source === origin.source` ๋กœ ์‚ฌ์ดํŠธ๋ฅผ ์ฐพ์•˜๋‹ค. ๊ทธ๋Ÿฐ๋ฐ `origin.source` ๋Š” **๋ ˆํผ๋Ÿฐ์Šค
411
- * ๋“ฑ๋ก ์ด๋ฆ„**์ด๊ณ (๊ทธ ์ฐธ์กฐ์˜ ์‚ฌ์ดํŠธ ์ „๋ถ€์— ๊ฐ™๋‹ค) `master.source` ๋Š” **์‚ฌ์ดํŠธ๋ณ„ ์ธ์Šคํ„ด์Šค id** ๋‹ค โ€”
412
- * ์„œ๋กœ ๋‹ค๋ฅธ ๊ฒƒ์„ ๊ฒฌ์คฌ์œผ๋‹ˆ ์‚ฌ์ดํŠธ๊ฐ€ ๋ฉ€์ฉกํžˆ ์žˆ์–ด๋„ ใ€Œ๋” ์ด์ƒ ๋ณด๊ณ ํ•˜์ง€ ์•Š๋Š”๋‹คใ€๋กœ ๊ฑฐ์ ˆํ–ˆ๋‹ค. ์‚ฌ์ดํŠธ๊ฐ€
413
- * ์—ฌ๋Ÿฟ์ธ ์ฐธ์กฐ์—์„œ ์˜จ ํŠธ์œˆ์€ ๊ทธ๋ž˜์„œ **์ „๋ถ€** ์žฌ๋™๊ธฐ๊ฐ€ ๋ถˆ๊ฐ€ํ–ˆ๊ณ , ์˜ค๋ฅ˜ ๋ฌธ์žฅ์€ ์›์ธ์„ ์ฐธ์กฐ ์ชฝ์œผ๋กœ
414
- * ์ž˜๋ชป ๊ฐ€๋ฆฌ์ผฐ๋‹ค.
415
- *
416
- * ์ด์ œ โ‘  ๊ธฐ์–ตํ•ด ๋‘” ์‚ฌ์ดํŠธ๋กœ ๊ณง์žฅ ๊ฐ„๋‹ค(`origin.siteId`) โ‘ก ๊ธฐ์–ต์ด ์—†๋Š” ์˜› ํŠธ์œˆ์€ ์‚ฌ์ดํŠธ๋ฅผ ์‚ดํŽด
417
- * **๋งˆ์Šคํ„ฐ์˜ ์ •์ฒด(=์ด ์ธ์Šคํ„ด์Šค id)** ์™€ ๊ฒฌ์ค€๋‹ค. ์–ด๋А ์ชฝ๋„ ๋ชป ์ฐพ์œผ๋ฉด **์ฒซ ์‚ฌ์ดํŠธ๋กœ ๋•Œ์šฐ์ง€ ์•Š๋Š”๋‹ค** โ€”
418
- * ๋‹ค๋ฅธ ํ˜„์žฅ์˜ ๊ตฌ์กฐ๋ฅผ ์ด ํŠธ์œˆ์— ๋ฎ๋Š” ๊ฒƒ์€ ์˜ค๋ฅ˜๊ฐ€ ์•„๋‹ˆ๋ผ ์˜ค๋ฅ˜ ์—†์ด ํ‹€๋ฆฐ ๊ณต์žฅ์œผ๋กœ ๋‚˜ํƒ€๋‚œ๋‹ค.
419
- */
420
- const picked = pickOriginSite(sites, origin)
421
- if (picked.site) return { ...(await adapter.fetchMaster(cfg, picked.site, onStep)), origin }
422
- if (picked.reason === 'site-gone')
423
- throw new Error(
424
- `reference "${origin.source}" no longer reports site "${origin.siteId}" โ€” refusing to overwrite this twin with a different site`
425
- )
426
-
427
- for (const site of sites) {
428
- const master = await adapter.fetchMaster(cfg, site)
429
- if (master.source === instanceId) return { ...master, origin: { ...origin, siteId: site.siteId } }
430
- }
431
- throw new Error(
432
- `reference "${origin.source}" does not report a site that rebuilds twin "${instanceId}" ` +
433
- `(this twin was imported before the site identity was recorded, and no site's master carries its id) โ€” refusing to overwrite it with a different site`
434
- )
435
- }
436
-
437
- /** ๋“ฑ๋ก๋œ ํŠธ์œˆ ํ…œํ”Œ๋ฆฟ(ํŒŒ๋ผ๋ฏธํ„ฐํ™” ์•„ํ‚คํƒ€์ž…) ๋ชฉ๋ก โ€” ๋…ธ๋ธŒ ์Šคํ‚ค๋งˆ ํฌํ•จ(ํด๋ผ์ด์–ธํŠธ ํผ์šฉ). */
438
- @Query(returns => [ScalarObject], { description: 'List registered twin templates (parameterized archetypes) with their knob schema.' })
439
- async twinTemplates(@Ctx() _context: ResolverContext): Promise<any[]> {
440
- return listTemplates().map(t => ({ id: t.id, label: t.label, system: t.system, description: t.description, detail: t.detail, knobs: t.knobs }))
441
- }
442
-
443
- /** ํ…œํ”Œ๋ฆฟ + ๋…ธ๋ธŒ โ†’ ํ•ฉ์„ฑ ๋งˆ์Šคํ„ฐ โ†’ ์ธ์ œ์ŠคํŠธ(+๊ธฐ๋™+์‹œ๋‚˜๋ฆฌ์˜ค). ๋ ˆํผ๋Ÿฐ์Šค import ์™€ ๊ฐ™์€ ๊ฒฝ๋กœ, ์›์ฒœ๋งŒ ํ•ฉ์„ฑ. */
444
- @Directive('@privilege(category: "twin", privilege: "mutation", domainOwnerGranted: true, superUserGranted: true)')
445
- @Mutation(returns => ScalarObject, {
446
- description:
447
- "Instantiate a twin template with params โ†’ synthetic master โ†’ ingest (+start+scenario). restartPolicy declares how the new twin treats its own past on reboot ('resync' | 'resume' | 'reset') โ€” there is no default. Returns {ok, instanceId, spaceId}."
448
- })
449
- async createFromTemplate(
450
- @Arg('templateId') templateId: string,
451
- @Arg('params', type => ScalarObject) params: any,
452
- @Arg('restartPolicy') restartPolicy: string,
453
- @Ctx() context: ResolverContext,
454
- @Arg('start', { nullable: true }) start?: boolean,
455
- @Arg('spaceId', { nullable: true, description: 'Existing space to co-locate this twin into (add a lens to a site). Omit to create its own space.' }) spaceId?: string
456
- ): Promise<any> {
457
- const tpl = getTemplate(templateId)
458
- if (!tpl) return { ok: false, error: `unknown template: ${templateId}` }
459
- const domainId = context.state.domain.id
460
- try {
461
- /*
462
- * **๋น ์ง„ ๋…ธ๋ธŒ๋ฅผ ์„œ๋ฒ„๊ฐ€ ์ฑ„์šด๋‹ค.** ๊ธฐ๋ณธ๊ฐ’์„ ์•„๋Š” ๊ณณ์€ ๋…ธ๋ธŒ ์Šคํ‚ค๋งˆ(๊ณ„์•ฝ)์ธ๋ฐ ๊ทธ๊ฒƒ์„ ์ ์šฉํ•˜๋Š” ๊ณณ์ด
463
- * ์œ„์ž๋“œ๋ฟ์ด์—ˆ๋‹ค โ€” ๊ทธ๋ž˜์„œ ํ™”๋ฉด์„ ๊ฑฐ์น˜์ง€ ์•Š๋Š” ํ˜ธ์ถœ์ž(AIยท์Šคํฌ๋ฆฝํŠธยทAPI)๊ฐ€ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์ผ๋ถ€๋งŒ ์ฃผ๋ฉด
464
- * ํ…œํ”Œ๋ฆฟ ์•ˆ์—์„œ ๋ฐœ์ƒ๋ฅ ์ด 0 ์ด ๋˜๊ณ  **์•„๋ฌด๊ฒƒ๋„ ํ•˜์ง€ ์•Š๋Š” ํŠธ์œˆ**์ด ์•Œ๋ฆฌ์ง€ ์•Š๊ณ  ๋งŒ๋“ค์–ด์กŒ๋‹ค.
465
- * ์ฑ„์šด ๋…ธ๋ธŒ ์ด๋ฆ„์€ ๊ฒฝ๊ณ ๋กœ ํ•จ๊ป˜ ๋Œ๋ ค์ค€๋‹ค โ€” ์•Œ๋ฆฌ์ง€ ์•Š๊ณ  ์ฑ„์šฐ๋Š” ๊ฒƒ๋„ ์•Œ๋ฆฌ์ง€ ์•Š๊ณ  ๋น„์šฐ๋Š” ๊ฒƒ๋งŒํผ ์œ„ํ—˜ํ•˜๋‹ค.
466
- */
467
- const filled = filledKnobKeys(tpl.knobs, params)
468
- const effective = applyKnobDefaults(tpl.knobs, params)
469
- /*
470
- * **์ด ํ˜„์žฅ์— ์ด๋ฏธ ์žˆ๋Š” ์„ค๋น„๋ฅผ ๋…ธ๋ธŒ ๊ฐ’์œผ๋กœ ํ™•์ •ํ•œ๋‹ค** (`siteEquipmentIds`).
471
- *
472
- * ์—๋„ˆ์ง€ ํŠธ์œˆ์ด ใ€Œ๊ณต์žฅ์ด ๋Œ๋ฉด ์ „๊ธฐ๊ฐ€ ์˜ค๋ฅธ๋‹คใ€๋ฅผ ๋งํ•˜๋ ค๋ฉด ๋ถ€ํ•˜๋ฅผ **์ด์›ƒ ํŠธ์œˆ์˜ ์„ค๋น„ id ๋กœ**
473
- * ์„ ์–ธํ•ด์•ผ ํ•œ๋‹ค(์„ค๋น„ ํ•˜๋‚˜์— ์‚ฌ์‹ค ๋‘˜). ๊ทธ ๋ชฉ๋ก์„ ๋นŒ๋“œํ•  ๋•Œ๋งˆ๋‹ค ํ˜„์žฅ์—์„œ ๋‹ค์‹œ ์ฝ์œผ๋ฉด, ์‚ฌ์ด์—
474
- * ์„ค๋น„๊ฐ€ ๋Š˜๊ฑฐ๋‚˜ ์ค€ ๋งŒํผ **๋‹ค์‹œ ์ง€์„ ๋•Œ ๋‹ค๋ฅธ ํŠธ์œˆ**์ด ๋œ๋‹ค. ๊ทธ๋ž˜์„œ ์—ฌ๊ธฐ์„œ ํ•œ ๋ฒˆ ํ•ด์„ํ•˜๊ณ 
475
- * ๊ทธ ๊ฒฐ๊ณผ๋ฅผ ๋…ธ๋ธŒ ๊ฐ’์œผ๋กœ ๋‚จ๊ธด๋‹ค(์•„๋ž˜ `origin.params` ์— ๊ทธ๋Œ€๋กœ ์ €์žฅ๋œ๋‹ค).
476
- */
477
- const adopted = await resolveSiteEquipmentKnobs(tpl.knobs, effective, domainId, spaceId)
478
- const { master, scenario } = tpl.build(effective)
479
- /* **๋ฌด์—‡์œผ๋กœ ์ง€์—ˆ๋Š”์ง€ ๋‚จ๊ธด๋‹ค.** ํ•ฉ์„ฑ์€ ๊ฒฐ์ •์ ์ด๋ฏ€๋กœ ๊ฐ™์€ ๋…ธ๋ธŒ๋ฉด ๊ฐ™์€ ๋งˆ์Šคํ„ฐ๊ฐ€ ๋‹ค์‹œ ๋‚˜์˜จ๋‹ค โ€”
480
- ๊ทธ๋ž˜์„œ ํ…œํ”Œ๋ฆฟ ํŠธ์œˆ๋„ ์–ด๋Œ‘ํ„ฐ ํŠธ์œˆ๊ณผ ๋˜‘๊ฐ™์ด ๋‹ค์‹œ ์ฝ์„ ์ˆ˜ ์žˆ๋‹ค. ๋‚จ๊ธฐ์ง€ ์•Š์œผ๋ฉด ๊ทธ ํŠธ์œˆ์€
481
- ์ฒซ๋‚ ์˜ ๋ชจ์Šต์— ์˜์›ํžˆ ๋จธ๋ฌธ๋‹ค(์›๋ณธ์„ ์žƒ์€ ํŠธ์œˆ). */
482
- /* ๊ณ ๋ฅธ ํ˜„์žฅ๋„ ๋‚จ๊ธด๋‹ค โ€” ์—†์œผ๋ฉด ๋‹ค์‹œ ์ฝ๊ธฐ๊ฐ€ ํŠธ์œˆ์„ ํ…œํ”Œ๋ฆฟ ๊ธฐ๋ณธ ๊ณต๊ฐ„์œผ๋กœ ์˜ฎ๊ฒจ ๋ฒ„๋ฆฐ๋‹ค(ํ˜„์žฅ ๊ฐ’๋„ ์žƒ๋Š”๋‹ค). */
483
- master.origin = { kind: 'template', templateId, params: effective, ...(spaceId ? { spaceId } : {}) }
484
- /* ๊ณต๊ฐ„ ์„ ํƒ์€ ํ…œํ”Œ๋ฆฟ์„ ๊ฑฐ์น˜์ง€ ์•Š๋Š”๋‹ค โ€” ํ…œํ”Œ๋ฆฟ๋งˆ๋‹ค ์ด ๋…ธ๋ธŒ๋ฅผ ์•Œ๊ณ  ์žˆ์–ด์•ผ ํ•œ๋‹ค๋ฉด ์ƒˆ ํ…œํ”Œ๋ฆฟ์ด
485
- ์•Œ๋ฆฌ์ง€ ์•Š๊ณ  ๋ฌด์‹œํ•˜๊ฒŒ ๋œ๋‹ค. ํ•ฉ์„ฑ๋œ ๋งˆ์Šคํ„ฐ์— ํ•œ ๊ณณ์—์„œ ์–น๋Š”๋‹ค(๋ ˆํผ๋Ÿฐ์Šค ์ธ์ œ์ŠคํŠธ์™€ ๊ฐ™์€ ๊ฒฝ๋กœ). */
486
- const res = await TwinEngine.ingestMaster(
487
- domainId,
488
- master,
489
- readRestartPolicy(restartPolicy, `createFromTemplate("${templateId}")`),
490
- { spaceId },
491
- context.state.user
492
- )
493
- if (start !== false) {
494
- await TwinEngine.startFromRegistry(domainId, res.instanceId)
495
- if (scenario) {
496
- const runtime = TwinEngine.runtime(domainId, res.instanceId)
497
- if (runtime) {
498
- runtime.scenario.load(scenario)
499
- runtime.scenario.start()
500
- }
501
- }
502
- }
503
- const warnings = [
504
- ...(filled.length ? [knobDefaultsFilled(filled)] : []),
505
- ...adopted.filter(a => a.count > 0).map(a => siteEquipmentAdopted(a.key, a.count)),
506
- ...res.warnings
507
- ]
508
- return { ok: true, instanceId: res.instanceId, spaceId: res.spaceId, warnings }
509
- } catch (e: any) {
510
- return refusal(context, e, 'template instantiation failed')
511
- }
512
- }
513
-
514
- /* โ”€โ”€ ๋ ˆํผ๋Ÿฐ์Šค ๊ด€๋ฆฌ(์—”ํ‹ฐํ‹ฐ ๊ธฐ๋ฐ˜ยทํ…Œ๋„ŒํŠธ ์Šค์ฝ”ํ”„) โ€” reference-management P1 โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
515
- * ์ „์—ญ ์ธ๋ฉ”๋ชจ๋ฆฌ(referenceSources/importFromReference)์™€ ๊ณต์กด. ์ด์ชฝ์€ TwinReference ํ–‰ + ์–ด๋Œ‘ํ„ฐ ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ.
516
- */
517
-
518
- @Query(returns => [ScalarObject], { description: 'List tenant-scoped persisted twin references (adapter-backed).' })
519
- async twinReferences(@Ctx() context: ResolverContext): Promise<any[]> {
520
- const domainId = context.state.domain.id
521
- const rows = await getRepository(TwinReference).find({ where: { domain: { id: domainId } } })
522
- /*
523
- * provenance ๋ผ์ด๋ธŒ ๋Œ€์กฐ โ€” ์ง€์›Œ์ง„ ํŠธ์œˆ์€ producedCount ์—์„œ ์ž๋™ ์ œ์™ธ(์ƒํƒœ ๋™๊ธฐ์˜ lazy ๋ฐฉ์‹).
524
- *
525
- * **id ๋งŒ ์ฝ๋Š”๋‹ค** (2026-08-21). ์˜ˆ์ „์—๋Š” ์ธ์Šคํ„ด์Šค ํ–‰ ์ „์ฒด๋ฅผ ์ฝ์—ˆ๊ณ , ๊ทธ ํ–‰์—๋Š” **๋ชจ๋ธ JSON** ์ด
526
- * ๋“ค์–ด ์žˆ๋‹ค(์ž๋ฆฌ ์ˆ˜๋ฐฑ ๊ฐœ + ๋ฐœ์ž๊ตญ ์ขŒํ‘œ). ๊ทธ๋ž˜์„œ ์ด ๋ชฉ๋ก์„ ์—ฌ๋Š” ๋ฐ ์ˆ˜ ๋ฉ”๊ฐ€๋ฐ”์ดํŠธ๋ฅผ ์ฝ๊ณ  ํŒŒ์‹ฑํ–ˆ๊ณ ,
527
- * ํ™”๋ฉด์€ ๊ทธ๋™์•ˆ ใ€Œ์•„์ง ์—ฐ๊ฒฐ๋œ ์‹œ์Šคํ…œ์ด ์—†์Šต๋‹ˆ๋‹คใ€๋ฅผ ๋ณด์ด๊ณ  ์žˆ์—ˆ๋‹ค โ€” ์‚ฌ์šฉ์ž์—๊ฒŒ๋Š” ใ€Œ์—†๋‹คใ€๋Š” ๋‹ต์œผ๋กœ
528
- * ์ฝํžŒ๋‹ค. ์—ฌ๊ธฐ์„œ ํ•„์š”ํ•œ ๊ฒƒ์€ ์‚ด์•„ ์žˆ๋Š” id ์ง‘ํ•ฉ ํ•˜๋‚˜๋‹ค.
529
- */
530
- const liveIds = new Set(
531
- (await getRepository(TwinInstance).find({ where: { domain: { id: domainId } }, select: ['instanceId'] })).map(
532
- i => i.instanceId
533
- )
534
- )
535
- return rows.map(r => {
536
- const produced = (((r.scopeSpec as any)?.produced ?? []) as any[]).filter(p => liveIds.has(p.instanceId))
537
- return {
538
- source: r.source, system: r.system, siteName: r.siteName, description: r.description,
539
- adapterType: r.adapterType, status: r.status, lastSyncedAt: r.lastSyncedAt, lastError: r.lastError,
540
- producedCount: produced.length, produced,
541
- /*
542
- * **์กฐ์น˜๊ฐ€ ์ €์ชฝ์— ๋‹ฟ์•„ ๋ดค๋‚˜** (ADR-0046 โ‘ก). ์นธ ์…‹์€ 2026-09 ๋ถ€ํ„ฐ ์„œ ์žˆ์—ˆ๋Š”๋ฐ ๋ชฉ๋ก๋„ ์ƒ์„ธ๋„
543
- * ์ด ๊ฐ’์„ ์‹ฃ์ง€ ์•Š์•„ **ํ™”๋ฉด์ด ๋ณผ ๊ธธ์ด ์—†์—ˆ๋‹ค** โ€” ์ €์žฅ๋œ ์‚ฌ์‹ค์ด ์•„๋ฌด์—๊ฒŒ๋„ ๋‹ฟ์ง€ ์•Š์•˜๋‹ค.
544
- *
545
- * ๋ณด๋‚ผ ์ฃผ์†Œ๊ฐ€ ์ ํžŒ ์—ฐ๊ฒฐ์—๋งŒ ์‹ฃ๋Š”๋‹ค. ์ฝ๊ธฐ๋งŒ ํ•˜๋Š” ์—ฐ๊ฒฐ์— ใ€Œ์•„์ง ํ™•์ธ ์•ˆ ๋จใ€์ด ์„œ๋ฉด ํ•  ์ผ์ด ์—†๋Š”
546
- * ์‚ฌ๋žŒ์—๊ฒŒ ํ•  ์ผ์ด ์žˆ๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋ณด์ธ๋‹ค.
547
- */
548
- ...(actuationAddressed(r.connectionConfig as any)
549
- ? {
550
- actuationState: r.actuationState ?? 'unverified',
551
- actuationVerifiedAt: r.actuationVerifiedAt ?? null,
552
- actuationFailure: r.actuationFailure ?? null
553
- }
554
- : {})
555
- }
556
- })
557
- }
558
-
559
- @Query(returns => [String], { description: 'List registered reference adapter type keys.' })
560
- referenceAdapterTypes(): string[] {
561
- return listAdapterTypes()
562
- }
563
-
564
- /**
565
- * ์ธํ‹ฐ๊ทธ๋ ˆ์ด์…˜ ๊ทธ๋ฃนํ•‘ (reference-management ยง2.3ยทD11) โ€” ์‚ฐ์ถœ๋ฌผ์„ ์ปค๋„ฅํ„ฐ ํƒ€์ž… โ–ธ Connection โ–ธ ์ธ์Šคํ„ด์Šค 2๋‹จ์œผ๋กœ.
566
- * ํŠธ์œˆ-๋„ค์ดํ‹ฐ๋ธŒ ์˜จํ†จ๋กœ์ง€(integration ๋ฌด์˜์กด): TwinReference ๊ฐ€ ๊ทธ๋ฃนํ•‘ ๋…ธ๋“œ(source=connectionName),
567
- * scopeSpec.connectorType ๋กœ ์ƒ์œ„ ๋ถ„๋ฅ˜. producedCount ๋Š” ๋ผ์ด๋ธŒ ์ธ์Šคํ„ด์Šค๋กœ ๋Œ€์กฐ(์ง€์›Œ์ง„ ํŠธ์œˆ ์ œ์™ธ).
568
- */
569
- @Query(returns => [ScalarObject], { description: 'Group integration items 2-tier: connector type โ–ธ connection โ–ธ produced twins. (reference-management ยง2.3)' })
570
- async twinConnections(@Ctx() context: ResolverContext): Promise<any[]> {
571
- const domainId = context.state.domain.id
572
- const rows = await getRepository(TwinReference).find({ where: { domain: { id: domainId } } })
573
- const liveIds = new Set((await getRepository(TwinInstance).find({ where: { domain: { id: domainId } } })).map(i => i.instanceId))
574
- const groups = new Map<string, any[]>()
575
- for (const r of rows) {
576
- const connectorType = (r.scopeSpec as any)?.connectorType ?? r.adapterType
577
- const producedCount = (((r.scopeSpec as any)?.produced ?? []) as any[]).filter(p => liveIds.has(p.instanceId)).length
578
- const conn = { source: r.source, name: r.siteName, status: r.status, adapterType: r.adapterType, producedCount, lastSyncedAt: r.lastSyncedAt }
579
- if (!groups.has(connectorType)) groups.set(connectorType, [])
580
- groups.get(connectorType)!.push(conn)
581
- }
582
- return [...groups.entries()].map(([connectorType, connections]) => ({
583
- connectorType,
584
- connectionCount: connections.length,
585
- instanceCount: connections.reduce((s, c) => s + c.producedCount, 0),
586
- connections
587
- }))
588
- }
589
-
590
- /** ํ•œ Connection ๊ทธ๋ฃน ๋…ธ๋“œ ์ƒ์„ธ โ€” ๊ทธ ์•„๋ž˜ ์ƒ์„ฑ๋œ ํŠธ์œˆ ์ธ์Šคํ„ด์Šค(๋ผ์ด๋ธŒ ๋Œ€์กฐยท์ƒํƒœยท๊ธฐ๋™์—ฌ๋ถ€). */
591
- @Query(returns => ScalarObject, { nullable: true, description: 'One connection grouping location with its produced twin instances (live-checked). (reference-management ยง2.3)' })
592
- async twinConnection(@Arg('source') source: string, @Ctx() context: ResolverContext): Promise<any | null> {
593
- const domainId = context.state.domain.id
594
- const r = await getRepository(TwinReference).findOne({ where: { domain: { id: domainId }, source } })
595
- if (!r) return null
596
- const produced = ((r.scopeSpec as any)?.produced ?? []) as any[]
597
- const instMap = new Map((await getRepository(TwinInstance).find({ where: { domain: { id: domainId } } })).map(i => [i.instanceId, i]))
598
- const instances = produced
599
- .filter(p => instMap.has(p.instanceId))
600
- .map(p => {
601
- const inst = instMap.get(p.instanceId) as any
602
- return { instanceId: p.instanceId, spaceId: p.spaceId, siteId: p.siteId, kind: inst.kind, status: inst.status, running: TwinEngine.owns(domainId, p.instanceId) }
603
- })
604
- return {
605
- source: r.source,
606
- connectorType: (r.scopeSpec as any)?.connectorType ?? r.adapterType,
607
- name: r.siteName,
608
- status: r.status,
609
- system: r.system,
610
- lastSyncedAt: r.lastSyncedAt,
611
- instanceCount: instances.length,
612
- instances
613
- }
614
- }
615
-
616
- /** ๋‹จ๊ฑด ์ƒ์„ธ(๋ทฐ/ํŽธ์ง‘) โ€” connectionConfig ํฌํ•จ, ์–ด๋Œ‘ํ„ฐ ์Šคํ‚ค๋งˆ์˜ secret ํ•„๋“œ๋Š” ๋งˆ์Šคํ‚น(์žˆ์œผ๋ฉด 'โ€ขโ€ขโ€ขโ€ขโ€ขโ€ข'). */
617
- @Query(returns => ScalarObject, { nullable: true, description: 'Get one tenant-scoped reference with its connection config (secret fields masked) for view/edit.' })
618
- async twinReference(@Arg('source') source: string, @Ctx() context: ResolverContext): Promise<any | null> {
619
- const r = await getRepository(TwinReference).findOne({ where: { domain: { id: context.state.domain.id }, source } })
620
- if (!r) return null
621
- const secretKeys = new Set((getAdapter(r.adapterType)?.meta?.configSchema ?? []).filter(f => f.secret).map(f => f.key))
622
- const cfg: any = {}
623
- for (const [k, v] of Object.entries((r.connectionConfig as any) ?? {})) cfg[k] = secretKeys.has(k) ? (v ? 'โ€ขโ€ขโ€ขโ€ขโ€ขโ€ข' : '') : v
624
- /*
625
- * **์ด ์—ฐ๊ฒฐ์ด ๋งŒ๋“  ํŠธ์œˆ id ๋ฅผ ํ•จ๊ป˜ ๋‚ธ๋‹ค.** ์ƒ์„ธ ํ™”๋ฉด์ด ใ€Œ์ด ์—ฐ๊ฒฐ์ด ์ž˜ ๋™๊ธฐํ™”๋˜๊ณ  ์žˆ๋‚˜ใ€๋ฅผ ๋ฌผ์œผ๋ ค๋ฉด
626
- * ์–ด๋А ํŠธ์œˆ์„ ๋ฌผ์–ด์•ผ ํ•˜๋Š”์ง€ ์•Œ์•„์•ผ ํ•œ๋‹ค. ์˜ˆ์ „์—๋Š” ๊ฐœ์ˆ˜(`producedCount`)๋งŒ ์žˆ์–ด์„œ ํ™”๋ฉด์ด id ๋ฅผ
627
- * ์•Œ ๋ฐฉ๋ฒ•์ด ์—†์—ˆ๋‹ค.
628
- */
629
- const producedInstanceIds = [
630
- ...new Set(
631
- ((r.scopeSpec as any)?.produced ?? [])
632
- .map((p: any) => p?.instanceId)
633
- .filter((x: any): x is string => typeof x === 'string' && !!x)
634
- )
635
- ]
636
- return {
637
- source: r.source, system: r.system, siteName: r.siteName, description: r.description,
638
- adapterType: r.adapterType, connectionConfig: cfg, status: r.status, lastSyncedAt: r.lastSyncedAt, lastError: r.lastError,
639
- producedInstanceIds,
640
- /* ๋ชฉ๋ก๊ณผ ๊ฐ™์€ ๊ทœ์น™ โ€” ๋ณด๋‚ผ ์ฃผ์†Œ๊ฐ€ ์ ํžŒ ์—ฐ๊ฒฐ๋งŒ ๊ฒ€์ฆ ์ƒํƒœ๋ฅผ ์‹ฃ๋Š”๋‹ค(ADR-0046 โ‘ก). */
641
- ...(actuationAddressed(r.connectionConfig as any)
642
- ? {
643
- actuationState: r.actuationState ?? 'unverified',
644
- actuationVerifiedAt: r.actuationVerifiedAt ?? null,
645
- actuationFailure: r.actuationFailure ?? null
646
- }
647
- : {})
648
- }
649
- }
650
-
651
- @Query(returns => [ScalarObject], { description: 'List reference connector metadata (label, domain, config schema, availability, capabilities) for the connect UI.' })
652
- referenceAdapters(): any[] {
653
- /*
654
- * **๋Šฅ๋ ฅ์„ ํ•จ๊ป˜ ๋‚ธ๋‹ค** (2026-08-21).
655
- *
656
- * `capabilities` ๋Š” ์–ด๋Œ‘ํ„ฐ ๋ฟŒ๋ฆฌ์— ์žˆ๊ณ  `meta` ์—๋Š” ์—†์—ˆ๋‹ค. ๊ทธ๋ž˜์„œ ์—ฐ๊ฒฐ ํ™”๋ฉด์ด ใ€Œ์ด ์›๋ณธ์ด ๋ผ์ด๋ธŒ๋ฅผ
657
- * ๋‚ผ ์ˆ˜ ์žˆ๋‚˜ใ€๋ฅผ ์•Œ ์ˆ˜ ์—†์—ˆ๊ณ , ํŠธ์œˆ์„ ๋งŒ๋“ค ๋•Œ `live` ๋ฅผ ๋งํ•˜์ง€ ๋ชปํ•ด **์žฌ๊ธฐ๋™ ์ •์ฑ…์ด ์—†๋‹ค๊ณ  ๊ฑฐ์ ˆ**๋๋‹ค
658
- * (์‹ค ์—ฐ๋™์—์„œ ๊ทธ๊ฒƒ์— ๋ง‰ํ˜”๋‹ค โ€” ๋ฏธ๋Ÿฌ๋Š” ์ •์˜์ƒ `resync` ์ด๋ฏ€๋กœ `live` ํ•˜๋‚˜๋กœ ์ •ํ•ด์ง„๋‹ค).
659
- *
660
- * ํ™”๋ฉด์ด ๊ทธ ์‚ฌ์‹ค์„ ์•Œ์•„์•ผ ์ •์ฑ…์„ ์‚ฌ๋žŒ์—๊ฒŒ ๋ฌผ์„์ง€ ์Šค์Šค๋กœ ์ •ํ• ์ง€ ๊ฐ€๋ฅผ ์ˆ˜ ์žˆ๋‹ค.
661
- */
662
- return listAdapters().map(a => ({ type: a.type, ...(a.meta ?? {}), capabilities: a.capabilities ?? [] }))
663
- }
664
-
665
- @Directive('@privilege(category: "twin", privilege: "mutation", domainOwnerGranted: true, superUserGranted: true)')
666
- @Mutation(returns => ScalarObject, { description: 'Create or update a tenant-scoped twin reference (persisted).' })
667
- async saveTwinReference(
668
- @Arg('source') source: string,
669
- @Arg('patch', type => ScalarObject) patch: any,
670
- @Ctx() context: ResolverContext
671
- ): Promise<any> {
672
- const domainId = context.state.domain.id
673
- const repo = getRepository(TwinReference)
674
- const existing = await repo.findOne({ where: { domain: { id: domainId }, source } })
675
- // ํ•„๋“œ ๋‹จ์œ„ ๋ณ‘ํ•ฉ โ€” ํŽธ์ง‘ ์‹œ ์ƒ๋žต๋œ ํ‚ค(์˜ˆ: ๋งˆ์Šคํ‚น๋œ secret)๋Š” ๊ธฐ์กด ๊ฐ’ ์œ ์ง€.
676
- const connectionConfig = patch.connectionConfig
677
- ? { ...((existing?.connectionConfig as any) ?? {}), ...patch.connectionConfig }
678
- : existing?.connectionConfig
679
-
680
- /*
681
- * ๊ฐ™์€ ๋ฐ›๋Š” ์‚ฌ์ดํŠธ๋ฅผ ๊ฐ€๋ฆฌํ‚ค๋Š” ์—ฐ๊ฒฐ ๋‘˜์€ **์ €์žฅํ•˜๋Š” ์ˆœ๊ฐ„ ๊ฑฐ๋ถ€ํ•œ๋‹ค** (ADR-0046 โ‘ก-1).
682
- * ๋“ค์—ฌ์˜ค๊ธฐ(planImport)์—๋Š” ์žˆ๋˜ ๊ฒ€์‚ฌ๊ฐ€ ํ™”๋ฉด ์ €์žฅ์—๋Š” ์—†์–ด์„œ, ์—ฐ๊ฒฐ์„ ๋ฒ ๊ปด ์ด๋ฆ„๋งŒ ๋ฐ”๊พธ๋ฉด ๋‘ ํŠธ์œˆ์˜
683
- * ์กฐ์น˜๊ฐ€ ํ•œ ๊ณต์žฅ์œผ๋กœ ๊ฐ”๋‹ค โ€” ์ €์ชฝ์€ 200 ์œผ๋กœ ๋ฐ›๊ณ  ์‹ค์ œ๋กœ ๋งŒ๋“ ๋‹ค. ์ง(intakeEndpointยทintakeSiteId)์€
684
- * ์•”ํ˜ธํ™”๋œ ์นธ ์•ˆ์— ์žˆ์–ด DB ์œ ๋‹ˆํฌ๋กœ ๋ชป ์žก์œผ๋‹ˆ ์—ฌ๊ธฐ์„œ ์žก๋Š”๋‹ค(ํŒ์ •: connection-portability.ts).
685
- */
686
- const others = await repo.find({ where: { domain: { id: domainId } } })
687
- const taken = refuseIntakeCollision(source, connectionConfig as any, others as any)
688
- if (taken) return { ok: false, source, error: taken.message, errorCode: taken.code, errorParams: taken.params }
689
-
690
- /*
691
- * **์ฃผ์†Œ๋ฅผ ๋ฐ”๊พธ๋Š” ์ €์žฅ์€ ๊ฒ€์ฆ์„ ๋˜๋Œ๋ฆฐ๋‹ค** (ADR-0046 โ‘ก). ์ง€๋‚œ ์™•๋ณต์€ ์ง€๋‚œ ์ฃผ์†Œ์˜ ์ด์•ผ๊ธฐ๋ผ ์ƒˆ
692
- * ์ฃผ์†Œ์— ๋Œ€ํ•ด์„œ๋Š” ์•„๋ฌด ๋ง๋„ ํ•˜์ง€ ์•Š๋Š”๋‹ค. ํ™”๋ฉด์ด ๊ทธ ์ดˆ๋ก์„ ๊ทธ๋Œ€๋กœ ๋‘๋ฉด ๋‹ค๋ฅธ ๊ณต์žฅ์„ ๊ฐ€๋ฆฌํ‚ค๋Š” ์—ฐ๊ฒฐ์ด
693
- * ใ€Œํ™•์ธ๋จใ€์œผ๋กœ ๋ณด์ธ๋‹ค. ๋˜๋Œ๋ฆฐ ์ƒํƒœ๋Š” ์‹คํŒจ๊ฐ€ ์•„๋‹ˆ๋‹ค โ€” ์•„์ง ์•ˆ ํ•ด ๋ณธ ๊ฒƒ์ด๋‹ค.
694
- */
695
- const reset = actuationResetPatch(existing?.connectionConfig as any, connectionConfig as any)
696
-
697
- const saved = await repo.save(
698
- repo.create({
699
- ...(existing ?? {}),
700
- ...(reset ?? {}),
701
- domain: { id: domainId } as any,
702
- source,
703
- system: patch.system ?? existing?.system,
704
- siteName: patch.siteName ?? existing?.siteName,
705
- description: patch.description ?? existing?.description,
706
- adapterType: patch.adapterType ?? existing?.adapterType,
707
- connectionConfig,
708
- mappingSpec: patch.mappingSpec ?? existing?.mappingSpec,
709
- scopeSpec: patch.scopeSpec ?? existing?.scopeSpec,
710
- status: patch.status ?? existing?.status ?? 'draft'
711
- })
712
- )
713
- return { ok: true, source: saved.source }
714
- }
715
-
716
- /** ์—ฐ๊ฒฐ ํ•ด์ œ โ€” removeTwins=true ๋ฉด ์ด ๋ ˆํผ๋Ÿฐ์Šค๊ฐ€ ๋งŒ๋“  ํŠธ์œˆ(provenance)๋„ ํ•จ๊ป˜ ์ œ๊ฑฐ(๋ผ์ดํ”„์‚ฌ์ดํด ์—ฐ์‡„). ๊ธฐ๋ณธ์€ ํŠธ์œˆ ๋ณด์กด(๋ถ„๋ฆฌ). */
717
- @Directive('@privilege(category: "twin", privilege: "mutation", domainOwnerGranted: true, superUserGranted: true)')
718
- @Mutation(returns => Boolean, { description: 'Delete a tenant-scoped reference. If removeTwins, also remove the twin instances it produced (provenance cascade); otherwise twins are kept (detached).' })
719
- async deleteTwinReference(
720
- @Arg('source') source: string,
721
- @Ctx() context: ResolverContext,
722
- @Arg('removeTwins', { nullable: true }) removeTwins?: boolean
723
- ): Promise<boolean> {
724
- const domainId = context.state.domain.id
725
- const repo = getRepository(TwinReference)
726
- const ref = await repo.findOne({ where: { domain: { id: domainId }, source } })
727
- if (removeTwins && ref) {
728
- for (const p of (((ref.scopeSpec as any)?.produced ?? []) as any[])) {
729
- try {
730
- await TwinEngine.remove(domainId, p.instanceId)
731
- } catch {
732
- /* ์ด๋ฏธ ์—†๊ฑฐ๋‚˜ ์‹คํŒจ โ€” ์—ฐ์‡„๋Š” best-effort. */
733
- }
734
- }
735
- }
736
- await repo.delete({ domain: { id: domainId } as any, source })
737
- return true
738
- }
739
-
740
- @Directive('@privilege(category: "twin", privilege: "mutation", domainOwnerGranted: true, superUserGranted: true)')
741
- @Mutation(returns => ScalarObject, { description: 'Test a reference connection via its adapter; updates status.' })
742
- async testReferenceConnection(@Arg('source') source: string, @Ctx() context: ResolverContext): Promise<any> {
743
- const repo = getRepository(TwinReference)
744
- const ref = await repo.findOne({ where: { domain: { id: context.state.domain.id }, source } })
745
- if (!ref) return { ok: false, error: `unknown reference: ${source}` }
746
- const adapter = getAdapter(ref.adapterType)
747
- if (!adapter) return { ok: false, error: `no adapter for type: ${ref.adapterType}` }
748
- try {
749
- const res = await adapter.testConnection(ref.connectionConfig ?? {})
750
- await repo.update({ id: ref.id }, { status: res.ok ? 'connected' : 'error', lastError: res.error ?? null } as any)
751
- return res
752
- } catch (e: any) {
753
- return { ok: false, error: e?.message ?? 'test failed' }
754
- }
755
- }
756
-
757
- /**
758
- * ์–ด๋Œ‘ํ„ฐ ๊ธฐ๋ฐ˜ ์ธ์ œ์ŠคํŠธ โ€” discoverSites โ†’ ์‚ฌ์ดํŠธ๋ณ„ fetchMaster โ†’ ingestMaster (fan-out, ยง2.2ยทยง4.2).
759
- * P1 ์€ ๋Œ€๊ฐœ N=1(virtual/๋‹จ์ผ). start ๋ฉด ๊ฐ ์ธ์Šคํ„ด์Šค ๊ธฐ๋™. ๋ผ์ด๋ธŒ ํ”ผ๋“œยทmode ๋Š” face2-inbound-live ์†Œ์œ .
760
- */
761
- /** ๋ ˆํผ๋Ÿฐ์Šค๊ฐ€ ๋…ธ์ถœํ•˜๋Š” ์‚ฌ์ดํŠธ/๊ณต์žฅ ๋ฐœ๊ฒฌ โ€” ํŠธ์œˆ ์ƒ์„ฑ ์‹œ "์–ด๋А ๊ณต์žฅ" ์„ ํƒ์šฉ(ยง2.2). */
762
- @Query(returns => ScalarObject, { description: 'Discover sites/factories exposed by a reference. Returns { sites, error? } โ€” an empty list with no error means the system genuinely has none, which is different from a failure.' })
763
- async discoverReferenceSites(@Arg('source') source: string, @Ctx() context: ResolverContext): Promise<any> {
764
- /*
765
- * **"๋ชป ์ฐพ์•˜๋‹ค" ์™€ "์—†๋‹ค" ๋ฅผ ๊ตฌ๋ถ„ํ•œ๋‹ค.** ์˜ˆ์ „์—๋Š” ๋„ท์„ ๋นˆ ๋ฐฐ์—ด ํ•˜๋‚˜๋กœ ํ•ฉ์ณค๊ณ (๋ ˆํผ๋Ÿฐ์Šค ์—†์Œยท์–ด๋Œ‘ํ„ฐ
766
- * ๋ฏธ์ƒยท์กฐํšŒ ์‹คํŒจยท์ •๋ง 0๊ฑด), ํ™”๋ฉด์€ ๊ทธ ๋ชจ๋‘๋ฅผ "์‚ฌ์ดํŠธ๋ฅผ ์ฐพ์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค" ๋ผ๊ณ ๋งŒ ๋งํ–ˆ๋‹ค. ์‹ค ์‹œ์Šคํ…œ
767
- * ์—ฐ๊ฒฐ์—์„œ ๊ฐ€์žฅ ์ž์ฃผ ๋ง‰ํžˆ๋Š” ์ž๋ฆฌ์ธ๋ฐ ์ž๊ฒฉ์ด ํ‹€๋ ธ๋Š”์ง€ ์ฃผ์†Œ๊ฐ€ ํ‹€๋ ธ๋Š”์ง€ ์•Œ ์ˆ˜ ์—†์—ˆ๋‹ค.
768
- * ํŒ์ •๊ณผ ๋ฌธ์žฅ ํ˜•ํƒœ๋Š” `discovery-result`(์ˆœ์ˆ˜)๊ฐ€ ๊ฐ–๋Š”๋‹ค.
769
- */
770
- const ref = await getRepository(TwinReference).findOne({ where: { domain: { id: context.state.domain.id }, source } })
771
- if (!ref) return failed(referenceNotFound(source))
772
- const adapter = getAdapter(ref.adapterType)
773
- if (!adapter) return failed(adapterUnknown(ref.adapterType, source))
774
- try {
775
- /*
776
- * **๊ธฐ๋‹ค๋ ค๋„ ๋‹ต์ด ์—†์œผ๋ฉด ์‚ฌ์œ ๋ฅผ ์ค€๋‹ค.** ์–ด๋Œ‘ํ„ฐ ๊ณ„์•ฝ์— ์‹œ๊ฐ„ ์ œํ•œ์ด ์—†์–ด์„œ, ์ฃผ์†Œ๊ฐ€ ํ‹€๋ฆฐ ์—”๋“œํฌ์ธํŠธ๋Š”
777
- * ํ™”๋ฉด์„ ๋ฌดํ•œํžˆ ๋Œ๊ฒŒ ๋งŒ๋“ค์—ˆ๋‹ค โ€” ๋ฌดํ•œํžˆ ๋„๋Š” ํ™”๋ฉด์€ ๋“ค์–ด์˜จ ๊ฒƒ์ด ์—†๋Š” ์‹คํŒจ์˜ ๋‹ค๋ฅธ ์–ผ๊ตด์ด๋‹ค.
778
- * ์ƒํ•œ์€ ์—ฌ๊ธฐ์„œ ์ •ํ•œ๋‹ค(์–ด๋Œ‘ํ„ฐ๋งˆ๋‹ค ๋‹ค๋ฅด๊ฒŒ ๋‘๋ฉด ์‚ฌ์šฉ์ž๊ฐ€ ์–ด๋А ๊ฒƒ์„ ๊ธฐ๋‹ค๋ฆฌ๋Š”์ง€ ์•Œ ์ˆ˜ ์—†๋‹ค).
779
- */
780
- const sites = await withTimeout(adapter.discoverSites(ref.connectionConfig ?? {}), DISCOVER_TIMEOUT_MS)
781
- return discovered(sites)
782
- } catch (e: any) {
783
- if (e?.code === TIMEOUT) return failed(discoveryTimedOut(source, DISCOVER_TIMEOUT_MS / 1000))
784
- return failed(discoveryFailed(source, detailOf(e)))
785
- }
786
- }
787
-
788
- /**
789
- * ํŠธ์œˆ ์ƒ์„ฑ โ€” ๋ ˆํผ๋Ÿฐ์Šค์—์„œ ์‚ฌ์ดํŠธ ๋ฐœ๊ฒฌ โ†’ (siteIds ์žˆ์œผ๋ฉด ๊ทธ ๊ณต์žฅ๋งŒ) โ†’ fetchMaster โ†’ provision. ์‚ฌ์ดํŠธ๋ณ„ resilient.
790
- * ์—ฌ๋Ÿฌ ๊ณต์žฅ์„ ๊ด€ํ• ํ•˜๋Š” ์†Œ์Šค๋Š” siteIds ๋กœ "์–ด๋А ๊ณต์žฅ"์„ ๊ณจ๋ผ ์ƒ์„ฑ(1:N, ยง2.1ยทยง2.2).
791
- *
792
- * โ”€โ”€ ํ˜„์žฅ ์„ ํƒ์ด ์ด ๊ฒฝ๋กœ์—๋„ ์žˆ๋‹ค (2026-08-13) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
793
- * ํ•œ ํ˜„์‹ค์„ ์—ฌ๋Ÿฌ ๋ Œ์ฆˆ๊ฐ€ ๋น„์ถ˜๋‹ค(WMSยทMESยทYMS). ๊ทธ๋Ÿฐ๋ฐ **๊ทธ ์„ ํƒ์ด ํ…œํ”Œ๋ฆฟ ๊ฒฝ๋กœ์—๋งŒ** ์žˆ์–ด์„œ
794
- * (`createFromTemplate(spaceId)`), ์‹ค ์‹œ์Šคํ…œ์—์„œ ๊ฐ€์ ธ์˜จ ํŠธ์œˆ์€ ์–ด๋Œ‘ํ„ฐ๊ฐ€ ํŒŒ์ƒํ•œ ๊ณต๊ฐ„ id ๊ฐ€ ์šฐ์—ฐํžˆ
795
- * ์ผ์น˜ํ•  ๋•Œ๋งŒ ํ•œ ํ˜„์žฅ์— ๋ชจ์˜€๋‹ค โ€” ์‚ฌ๋žŒ์ด "์ด MES ์™€ ๊ฐ™์€ ํ˜„์žฅ์— ๋„ฃ์–ด๋ผ" ๋ฅผ ๋งํ•  ๋ฐฉ๋ฒ•์ด ์—†์—ˆ๋‹ค.
796
- * ๋ฐ์ดํ„ฐ๋กœ๋Š” ๊ฐ€๋Šฅํ•œ ์ผ์ด ์ €์ž‘์œผ๋กœ๋Š” ๋ถˆ๊ฐ€๋Šฅํ•œ, `ingest-space` ๊ฐ€ ์ด๋ฏธ ์ง€์ ํ•œ ๊ทธ ๋น„๋Œ€์นญ์ด๋‹ค.
797
- *
798
- * ํŒ์ •์€ ๊ทธ ์ˆœ์ˆ˜ ๋ชจ๋“ˆ์ด ๊ฐ–๋Š”๋‹ค: ์—†๋Š” ๊ณต๊ฐ„์€ ๊ฑฐ์ ˆ(`resolveIngestSpace`), ์‚ฌ์ดํŠธ ์—ฌ๋Ÿฟ์„ ํ•œ ๊ณต๊ฐ„์—
799
- * ๋„ฃ๋Š” ๊ฒƒ๋„ ๊ฑฐ์ ˆ(`refuseImportSpace`) โ€” ์„œ๋กœ ๋‹ค๋ฅธ ํ˜„์žฅ์ด ์•Œ๋ฆฌ์ง€ ์•Š๊ณ  ํ•ฉ์ณ์ง€๋Š” ๊ฒƒ์„ ๋ง‰๋Š”๋‹ค.
800
- */
801
- @Directive('@privilege(category: "twin", privilege: "mutation", domainOwnerGranted: true, superUserGranted: true)')
802
- @Mutation(returns => ScalarObject, {
803
- description:
804
- "Create twins from a reference: discover sites โ†’ filter by siteIds (if given) โ†’ fetch master(s) โ†’ provision. spaceId co-locates the twin into an existing space (one site only). start=false to only provision; live=true to run as a mirror (adapter openLiveFeed โ†’ live projector) instead of sim. restartPolicy declares how each new twin treats its own past on reboot ('resync' | 'resume' | 'reset'); omit it and live=true implies 'resync' โ€” a mirror reads the source again โ€” while a simulation must declare 'resume' or 'reset' for itself."
805
- })
806
- async importTwinReference(
807
- @Arg('source') source: string,
808
- @Ctx() context: ResolverContext,
809
- @Arg('siteIds', type => [String], { nullable: true }) siteIds?: string[],
810
- @Arg('start', { nullable: true }) start?: boolean,
811
- @Arg('live', { nullable: true }) live?: boolean,
812
- @Arg('spaceId', { nullable: true, description: 'Existing space to co-locate this twin into (add a lens to a site). Only when importing a single site. Omit to give each site its own space.' }) spaceId?: string,
813
- @Arg('restartPolicy', { nullable: true, description: "How each new twin treats its own past on reboot: 'resync' | 'resume' | 'reset'. Required unless live=true (a mirror is 'resync' by definition)." }) restartPolicy?: string
814
- ): Promise<any> {
815
- const domainId = context.state.domain.id
816
- const repo = getRepository(TwinReference)
817
- const ref = await repo.findOne({ where: { domain: { id: domainId }, source } })
818
- if (!ref) return { ok: false, error: `unknown reference: ${source}` }
819
- const adapter = getAdapter(ref.adapterType)
820
- if (!adapter) return { ok: false, error: `no adapter for type: ${ref.adapterType}` }
821
- const cfg = ref.connectionConfig ?? {}
822
- try {
823
- let sites = await adapter.discoverSites(cfg)
824
- if (siteIds?.length) sites = sites.filter(s => siteIds.includes(s.siteId))
825
- /* ์•„๋ฌด๊ฒƒ๋„ ์“ฐ๊ธฐ ์ „์— ๊ฑฐ์ ˆํ•œ๋‹ค โ€” ํ•œ ์‚ฌ์ดํŠธ๋ฅผ ๋„˜์œผ๋ฉด "์ด ํ˜„์žฅ์— ๋”ํ•œ๋‹ค" ๊ฐ€ ์„ฑ๋ฆฝํ•˜์ง€ ์•Š๋Š”๋‹ค. */
826
- const refusal = refuseImportSpace(sites.length, spaceId)
827
- if (refusal) return { ok: false, error: refusal.message, errorCode: refusal.code, errorParams: refusal.params }
828
- const instances: any[] = []
829
- const failed: any[] = []
830
- /*
831
- * **์ง„ํ–‰์„ ๊ตฌ๋…์œผ๋กœ ํ˜๋ฆฐ๋‹ค** (2026-08-21).
832
- *
833
- * ๋งˆ์Šคํ„ฐ ์ฝ๊ธฐ๋Š” ์›๋ณธ์— ์—ฌ๋Ÿฌ ์งˆ์˜๋ฅผ ๋ณด๋‚ด๋Š” ๊ธด ์ž‘์—…์ด๊ณ , ํ™”๋ฉด์€ ๊ทธ ์‚ฌ์ด ์•„๋ฌด๊ฒƒ๋„ ์•Œ ์ˆ˜ ์—†์—ˆ๋‹ค.
834
- * ๋‹จ๊ณ„๋ฅผ ์•„๋Š” ๊ฒƒ์€ ์–ด๋Œ‘ํ„ฐ๋ฟ์ด๋ฏ€๋กœ ์–ด๋Œ‘ํ„ฐ๊ฐ€ ๋งํ•˜๊ณ (ํ‘œ์ค€ ๋‚ฑ๋ง), ํ˜ธ์ŠคํŠธ๊ฐ€ ์ž๊ธฐ ๋‘ ๋‹จ๊ณ„(์ƒ์„ฑยท๊ธฐ๋™)๋ฅผ
835
- * ๋”ํ•ด ํ˜๋ฆฐ๋‹ค.
836
- *
837
- * **๋ถ„๋ชจ๋Š” ์–ด๋Œ‘ํ„ฐ๊ฐ€ ์„ ์–ธํ•œ ๊ฒƒ**์ด๋‹ค(`masterSteps`). ์„ ์–ธํ•˜์ง€ ์•Š์€ ์–ด๋Œ‘ํ„ฐ๋Š” `total` ์ด ์—†๊ณ , ๊ทธ๋•Œ
838
- * ํ™”๋ฉด์€ ํผ์„ผํŠธ๋ฅผ ๋งŒ๋“ค์ง€ ์•Š๋Š”๋‹ค โ€” ๋ชจ๋ฅด๋Š” ์ง„ํ–‰๋ฅ ์„ ์ง€์–ด๋‚ด์ง€ ์•Š๋Š”๋‹ค.
839
- */
840
- const declared = adapter.masterSteps?.length
841
- const stepTotal = declared === undefined ? undefined : declared + HOST_STEPS.length
842
- const emit = (p: Omit<ReferenceProgress, 'siteCount'>) =>
843
- pubsub.publish(REFERENCE_PROGRESS_TOPIC, referenceProgressPayload(domainId, { ...p, siteCount: sites.length, total: p.total ?? stepTotal, source }))
844
-
845
- /*
846
- * **์ฝ์€ ๋’ค์˜ ์ดํ‰์„ ์‚ฌ์ดํŠธ๋งˆ๋‹ค ๋‚จ๊ธด๋‹ค** (2026-08-22).
847
- *
848
- * ์ดํ‰์€ ์ฝ๋Š” ์ˆœ๊ฐ„์—๋งŒ ์žˆ์—ˆ๋‹ค โ€” ๋Œ€ํ™”์ƒ์ž๋ฅผ ๋‹ซ์œผ๋ฉด ์‚ฌ๋ผ์ง€๊ณ , ๋‹ค์‹œ ๋ณด๋ ค๋ฉด ๊ฐ™์€ ์ฝ๊ธฐ๋ฅผ ๋˜ ํ•ด์•ผ ํ–ˆ๋‹ค.
849
- * ๊ทธ๋Ÿฐ๋ฐ ์‚ฌ๋žŒ์ด ๊ทธ๊ฒƒ์„ ๋ฌป๋Š” ์ž๋ฆฌ๋Š” ใ€Œ๋ชจ๋ธ ์‚ดํŽด๋ณด๊ธฐใ€๋‹ค(ํŠธ์œˆ์„ ๋ณด๋‹ค๊ฐ€ ใ€Œ์ด๊ฑด ์™œ ๋น„์—ˆ๋‚˜ใ€๋ฅผ ๋ฌป๋Š”๋‹ค).
850
- * ๊ทธ๋ž˜์„œ ๋ ˆํผ๋Ÿฐ์Šค ํ–‰์— ๋‚จ๊ฒจ ๊ทธ ํ™”๋ฉด์ด ์–ธ์ œ๋“  ์ฝ๊ฒŒ ํ•œ๋‹ค.
851
- *
852
- * ์‚ฌ์‹ค๋งŒ ๋‹ด๋Š”๋‹ค(`assessMaster`) โ€” ๋ฌธ์žฅ์€ ํ™”๋ฉด์ด ๋งŒ๋“ค๊ณ , ๋” ์•Œ๊ณ  ์‹ถ์„ ๋•Œ AI ๊ฐ€ ์ด ์‚ฌ์‹ค๋งŒ ๊ทผ๊ฑฐ๋กœ
853
- * ์„œ์ˆ ํ•œ๋‹ค. ๋‹ค์‹œ ์ฝ์œผ๋ฉด ๊ทธ ์‚ฌ์ดํŠธ์˜ ์ดํ‰์ด ์ƒˆ๊ฒƒ์œผ๋กœ ๋ฐ”๋€๋‹ค(์ง€๋‚œ ์ฝ๊ธฐ์˜ ์ดํ‰์„ ํ˜„์žฌ๋กœ ๋ณด์ด๊ฒŒ
854
- * ๋‘์ง€ ์•Š๋Š”๋‹ค).
855
- */
856
- const assessments: Record<string, unknown> = {}
857
-
858
- for (const [siteIndex, site] of sites.entries()) {
859
- /*
860
- * **์‹คํŒจ๋ฅผ ์‹ค์ œ๋กœ ๋ง‰ํžŒ ๋‹จ๊ณ„์— ์ ๋Š”๋‹ค** (2026-08-22).
861
- *
862
- * ์˜ˆ์ „์—๋Š” ์‹คํŒจ๋ฅผ ๋ฌด์กฐ๊ฑด `provision` ์œผ๋กœ ์ ์—ˆ๋‹ค. ๊ทธ๋ž˜์„œ ์›๋ณธ์„ ์ฝ๋‹ค ๋Š๊ธด ๊ฒƒ๋„ ํ™”๋ฉด์—๋Š”
863
- * ใ€ŒํŠธ์œˆ ์ƒ์„ฑ์—์„œ ์‹คํŒจใ€๋กœ ๋–ด๋‹ค โ€” ์‚ฌ๋žŒ์ด ์›์ธ์„ ์—‰๋šฑํ•œ ์ž๋ฆฌ์—์„œ ์ฐพ๋Š”๋‹ค(์‹ค์ œ๋กœ ๊ทธ๋ ‡๊ฒŒ ๋๋‹ค).
864
- *
865
- * ๊ทœ์•ฝ์ด ๋‹ต์„ ์ค€๋‹ค: `done` ์€ **๋๋‚ธ ๋‹จ๊ณ„ ์ˆ˜**๋‹ค(`ReferenceProgress.done`). ๊ทธ๋Ÿฌ๋ฉด ์ง€๊ธˆ ๋ง‰ํžŒ ๊ฒƒ์€
866
- * ๊ทธ ๋‹ค์Œ ๋‹จ๊ณ„, ์ฆ‰ `masterSteps[done]` ์ด๋‹ค. ๊ทธ ํ‘œ๋ฅผ ๋„˜์–ด์„œ๋ฉด(ํ˜ธ์ŠคํŠธ ๋‹จ๊ณ„) ๋งˆ์ง€๋ง‰์œผ๋กœ ์•Œ๋ฆฐ ๊ฒƒ์„ ์“ด๋‹ค.
867
- *
868
- * ์ด ๊ทœ์น™์€ ๋‘ ๊ฐ€์ง€ ๋ณด๊ณ  ๋ฐฉ์‹์— ํ•จ๊ป˜ ๋งž๋Š”๋‹ค โ€” ๋‹จ๊ณ„ **์‹œ์ž‘**์— ์•Œ๋ฆฌ๋Š” ์–ด๋Œ‘ํ„ฐ(`done` ์ด ์•„์ง 0)์™€
869
- * ๋‹จ๊ณ„ **์™„๋ฃŒ**์— ์•Œ๋ฆฌ๋Š” ์–ด๋Œ‘ํ„ฐ(`done` ์ด 1 ๋Š˜์–ด๋‚œ๋‹ค) ๋ชจ๋‘ ใ€Œ๋๋‚ธ ์ˆ˜ใ€๋งŒ ์ง€ํ‚ค๋ฉด ๋œ๋‹ค.
870
- */
871
- let lastDone = 0
872
- let lastStep: ReferenceProgress['step'] = (adapter.masterSteps?.[0] ?? 'provision') as ReferenceProgress['step']
873
- const track = (p: Omit<ReferenceProgress, 'siteCount'>) => {
874
- lastDone = p.done
875
- lastStep = p.step
876
- emit(p)
877
- }
878
- try {
879
- const master = await adapter.fetchMaster(cfg, site, st =>
880
- track({ siteId: site.siteId, siteIndex, step: st.key, done: st.done, total: stepTotal, detail: st.detail })
881
- )
882
- /* ์–ด๋А ๋ ˆํผ๋Ÿฐ์Šค์˜ **์–ด๋А ์‚ฌ์ดํŠธ**์—์„œ ์™”๋Š”์ง€ ๋‚จ๊ธด๋‹ค โ€” ๋‹ค์‹œ ์ฝ๊ธฐ๊ฐ€ ๊ฐ™์€ ์–ด๋Œ‘ํ„ฐยท๊ฐ™์€ ์‚ฌ์ดํŠธ๋กœ ๋Œ์•„์˜ค๋ ค๋ฉด
883
- ์‚ฌ์ดํŠธ ์ •์ฒด๊ฐ€ ์žˆ์–ด์•ผ ํ•œ๋‹ค(`source` ๋Š” ์ฐธ์กฐ ์ด๋ฆ„์ด๋ผ ์‚ฌ์ดํŠธ ์ „๋ถ€์— ๊ฐ™๋‹ค). */
884
- /*
885
- * ์ •์ฑ…์€ **์„ ์–ธ์ด๊ฑฐ๋‚˜ ์˜๋„์—์„œ ๊ณง๋ฐ”๋กœ ๋”ฐ๋ผ์˜ค๋Š” ๊ฒƒ**์ด๋‹ค: `live=true` ๋Š” ๋ฏธ๋Ÿฌ๋ฅผ ๋„์šฐ๊ฒ ๋‹ค๋Š” ๋œป์ด๊ณ 
886
- * ๋ฏธ๋Ÿฌ๋Š” ์ •์˜์ƒ `resync` ๋‹ค(์™ธ๋ถ€๊ฐ€ ์ง„์‹ค์ด๋ฏ€๋กœ ์šฐ๋ฆฌ ๊ณผ๊ฑฐ๋ฅผ ์ด์–ด ๋ถ™์ด์ง€ ์•Š๋Š”๋‹ค). ์‹œ๋ฎฌ๋กœ ๋„์šฐ๋ ค๋ฉด
887
- * ์Šค์Šค๋กœ ์„ ์–ธํ•ด์•ผ ํ•œ๋‹ค โ€” ๊ทธ ์ž๋ฆฌ์—์„œ ์šฐ๋ฆฌ๊ฐ€ ๊ณ ๋ฅด๋ฉด ์žฌ๊ธฐ๋™๋งˆ๋‹ค ์ด๋ ฅ์„ ์ง€์šฐ๋Š” ํŠธ์œˆ์ด ์•Œ๋ฆฌ์ง€ ์•Š๊ณ  ์ƒ๊ธด๋‹ค.
888
- */
889
- assessments[site.siteId] = assessMaster(master, site.siteId)
890
- const policy = readRestartPolicy(restartPolicy ?? (live ? 'resync' : undefined), `importTwinReference("${source}")`)
891
- track({ siteId: site.siteId, siteIndex, step: 'provision', done: declared ?? 0, total: stepTotal })
892
- const res = await TwinEngine.ingestMaster(
893
- domainId,
894
- { ...master, origin: { kind: 'reference', source, siteId: site.siteId, ...(spaceId ? { spaceId } : {}) } },
895
- policy,
896
- { spaceId },
897
- context.state.user
898
- )
899
- track({ siteId: site.siteId, siteIndex, step: 'start', done: (declared ?? 0) + 1, total: stepTotal })
900
- if (start !== false) {
901
- // live=true + ์–ด๋Œ‘ํ„ฐ๊ฐ€ openLiveFeed ์ง€์› โ†’ mirror(์‹ค ์ด๋ฒคํŠธ ๋ฏธ๋Ÿฌ). ์•„๋‹ˆ๋ฉด sim ๊ธฐ๋™(fallback).
902
- /* ์ฝ๊ธฐ ์ปค์„œ๋ฅผ ์ €์žฅํ•  ์ž๋ฆฌ๋ฅผ ํ•จ๊ป˜ ๋„˜๊ธด๋‹ค โ€” ๋ ˆํผ๋Ÿฐ์Šค์˜ ์ด๋ฆ„์€ ํ˜ธ์ŠคํŠธ๊ฐ€ ๋ถ™์ด๋Š” ๊ฒƒ์ด๊ณ 
903
- ์ปค๋„ฅํ„ฐ๋Š” ๊ทธ๊ฒƒ์„ ์•Œ ์ˆ˜ ์—†๋‹ค(ยง`startReferenceLiveFeed`). */
904
- const wentLive = live
905
- ? await startReferenceLiveFeed(domainId, ref.adapterType, cfg, site, res.instanceId, ref.source)
906
- : false
907
- if (!wentLive) await TwinEngine.startFromRegistry(domainId, res.instanceId)
908
- instances.push({ siteId: site.siteId, instanceId: res.instanceId, spaceId: res.spaceId, mode: wentLive ? 'live' : 'sim', warnings: res.warnings })
909
- } else {
910
- instances.push({ siteId: site.siteId, instanceId: res.instanceId, spaceId: res.spaceId, mode: 'provisioned', warnings: res.warnings })
911
- }
912
- } catch (e: any) {
913
- /*
914
- * **์‹คํŒจํ•œ ๋‹จ๊ณ„๋ฅผ ๊ทธ๋Œ€๋กœ ์•Œ๋ฆฐ๋‹ค.** ํ™”๋ฉด์ด ์ง„ํ–‰ ํ‘œ์‹œ๋ฅผ ์ง€์šฐ์ง€ ์•Š๊ณ  ๊ทธ ์ž๋ฆฌ๋ฅผ ๋ถ‰๊ฒŒ ๋‘์–ด์•ผ
915
- * ์‚ฌ๋žŒ์ด ใ€Œ์–ด๋””๊นŒ์ง€ ๊ฐ”๋Š”์ง€ใ€๋ฅผ ์•ˆ๋‹ค. ๋ง‰ํžŒ ์ž๋ฆฌ๋Š” ใ€Œ๋๋‚ธ ์ˆ˜ใ€์˜ ๋‹ค์Œ ๋‹จ๊ณ„๋‹ค(์œ„ ์ฃผ์„).
916
- */
917
- const at = (adapter.masterSteps?.[lastDone] ?? lastStep) as ReferenceProgress['step']
918
- emit({ siteId: site.siteId, siteIndex, step: at, done: lastDone, total: stepTotal, failed: true, error: e?.message ?? String(e) })
919
-
920
- failed.push({ siteId: site.siteId, error: e?.message ?? 'failed', ...(e?.code ? { errorCode: e.code, errorParams: e.params } : {}) })
921
- }
922
- }
923
- // provenance โ€” ์ด ๋ ˆํผ๋Ÿฐ์Šค๊ฐ€ ๋งŒ๋“  ์ธ์Šคํ„ด์Šค ์ถ”์ (scopeSpec.produced). ์‚ญ์ œ ์—ฐ์‡„ยท์ƒํƒœ ๋™๊ธฐยท๋„ค๋น„๊ฒŒ์ด์…˜ ๊ทผ๊ฑฐ.
924
- const producedNew = instances.map(i => ({ siteId: i.siteId, instanceId: i.instanceId, spaceId: i.spaceId }))
925
- const prev: any[] = ((ref.scopeSpec as any)?.produced ?? []).filter((p: any) => !producedNew.some(n => n.instanceId === p.instanceId))
926
- await repo.update(
927
- { id: ref.id },
928
- {
929
- status: 'connected',
930
- lastSyncedAt: new Date().toISOString(),
931
- /* ๋ชป ์˜ฎ๊ธด ๊ฐ’์„ **์„ธ์–ด ๋‚จ๊ธด๋‹ค**. ์•„๋ฌด ์ธ์Šคํ„ด์Šค๋„ ๋ณด๊ณ ํ•˜์ง€ ์•Š์•˜์œผ๋ฉด `null` โ€” ใ€Œ์—†์—ˆ๋‹คใ€๋กœ ์œ„์žฅํ•˜์ง€ ์•Š๋Š”๋‹ค. */
932
- lastWarningCount: instances.some((i: any) => Array.isArray(i.warnings))
933
- ? instances.reduce((n: number, i: any) => n + (Array.isArray(i.warnings) ? i.warnings.length : 0), 0)
934
- : null,
935
- /*
936
- * **์˜์–ด canonical ๋กœ ๋‚จ๊ธด๋‹ค.** ์ด ๊ฐ’์€ DB ์— ์ €์žฅ๋˜๊ณ  ์—ฐ๊ฒฐ ํ™”๋ฉด์ด ๊ทธ๋Œ€๋กœ ๋ฟŒ๋ฆฐ๋‹ค โ€” ์˜ˆ์ „์—๋Š”
937
- * ํ•œ๊ธ€ ์‚ฐ๋ฌธ(`N๊ฐœ ์‚ฌ์ดํŠธ ์‹คํŒจ`)์ด๋ผ ๋‹ค์„ฏ ์–ธ์–ด ํ™”๋ฉด์— ํ•œ๊ธ€์ด ์ƒˆ์–ด ๋‚˜๊ฐ”๋‹ค. ์–ด๋Œ‘ํ„ฐ๊ฐ€ ์ฃผ๋Š” ์‚ฌ์œ ๋„
938
- * ๊ฐ™์€ ํ•„๋“œ์— ๋“ค์–ด์˜ค๋ฏ€๋กœ(๊ทธ๊ฒƒ์€ ์› ์‹œ์Šคํ…œ์˜ ๋ง์ด๋‹ค) ํ‚ค๊ฐ€ ์•„๋‹ˆ๋ผ ์˜์–ด ๋ฌธ์žฅ์œผ๋กœ ํ†ต์ผํ•œ๋‹ค.
939
- * ์–ด๋А ์‚ฌ์ดํŠธ๊ฐ€ ์™œ ์‹คํŒจํ–ˆ๋Š”์ง€๋Š” `failed[]` ๊ฐ€ ๋‹ตํ•œ๋‹ค โ€” ํ™”๋ฉด์ด ๊ทธ๊ฒƒ์„ ๋ณด์—ฌ ์ค€๋‹ค.
940
- */
941
- lastError: failed.length ? `${failed.length} of ${sites.length} site(s) failed: ${failed.map((f: any) => f.siteId).join(', ')}` : null,
942
- /*
943
- * โ”€โ”€ ์‹คํŒจ ์‚ฌ์œ ๋ฅผ **๋‚จ๊ธด๋‹ค** (2026-08-21) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
944
- *
945
- * ์‚ฌ์œ ๋Š” ๋Œ€ํ™”์ƒ์ž์—๋งŒ ์žˆ์—ˆ๋‹ค. ๋‹ซ์œผ๋ฉด ์‚ฌ๋ผ์ง€๊ณ , ๋‹ค์‹œ ๋ณด๋ ค๋ฉด ๊ฐ™์€ ์‹คํŒจ๋ฅผ ๋‹ค์‹œ ๋งŒ๋“ค์–ด์•ผ ํ–ˆ๋‹ค โ€”
946
- * ์‹ค์ œ๋กœ ใ€Œ์Šนํ™”ํ‘ธ๋“œใ€ ์—ฐ๊ฒฐ์ด ๊ทธ ์ƒํƒœ์˜€๋‹ค(์š”์•ฝ ํ•œ ์ค„๋งŒ ๋‚จ๊ณ  ์›์ธ์€ ์–ด๋””์—๋„ ์—†์—ˆ๋‹ค).
947
- *
948
- * `scopeSpec` ์€ ์ด๋ฏธ ์ด ๋ ˆํผ๋Ÿฐ์Šค์˜ ๋ถ€์† ์‚ฌ์‹ค์„ ๋‹ด๋Š” ์ž๋ฆฌ์ด๋ฏ€๋กœ(`produced`) ์Šคํ‚ค๋งˆ๋ฅผ ๋Š˜๋ฆฌ์ง€ ์•Š๊ณ 
949
- * ์—ฌ๊ธฐ ์ ๋Š”๋‹ค. ์„ฑ๊ณตํ•œ ํšŒ์ฐจ์—๋Š” **๋น„์šด๋‹ค** โ€” ์ง€๋‚˜๊ฐ„ ์‹คํŒจ๋ฅผ ํ˜„์žฌ ์ƒํƒœ์ฒ˜๋Ÿผ ๋ณด์ด๊ฒŒ ๋‘์ง€ ์•Š๋Š”๋‹ค.
950
- */
951
- scopeSpec: {
952
- ...((ref.scopeSpec as any) ?? {}),
953
- produced: [...prev, ...producedNew],
954
- /* ์ด๋ฒˆ์— ์ฝ์€ ์‚ฌ์ดํŠธ์˜ ์ดํ‰๋งŒ ๊ฐˆ์•„ ๋ผ์šด๋‹ค โ€” ์ฝ์ง€ ์•Š์€ ์‚ฌ์ดํŠธ์˜ ๊ฒƒ์€ ๊ทธ๋Œ€๋กœ ๋‘”๋‹ค. */
955
- assessments: { ...(((ref.scopeSpec as any) ?? {}).assessments ?? {}), ...assessments },
956
- ...(failed.length
957
- ? { lastFailures: failed.map((f: any) => ({ siteId: f.siteId, error: f.error, at: new Date().toISOString() })) }
958
- : { lastFailures: [] })
959
- }
960
- } as any
961
- )
962
- return { ok: instances.length > 0, instances, failed }
963
- } catch (e: any) {
964
- /* ๋ ˆํผ๋Ÿฐ์Šค ํ–‰์—๋Š” **์˜์–ด canonical** ์„ ๋‚จ๊ธด๋‹ค โ€” ์ €์žฅ๋˜๋Š” ๊ฐ’์€ ์–ธ์–ด์ค‘๋ฆฝ์ด์–ด์•ผ ๋‹ค์‹œ ์ฝ์„ ์ˆ˜ ์žˆ๋‹ค. */
965
- await repo.update({ id: ref.id }, { status: 'error', lastError: e?.message ?? 'ingest failed' } as any)
966
- return refusal(context, e, 'ingest failed')
967
- }
968
- }
969
- }