@things-factory/headless-twin 10.1.278 → 10.1.279

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (614) hide show
  1. package/package.json +21 -7
  2. package/dist-server/engine/attention-digest.js.map +0 -1
  3. package/dist-server/engine/canonical-ingest.js.map +0 -1
  4. package/dist-server/engine/checkpoint-fold.js.map +0 -1
  5. package/dist-server/engine/command-routing.js.map +0 -1
  6. package/dist-server/engine/declared-stimulus.js.map +0 -1
  7. package/dist-server/engine/energy-topology.js.map +0 -1
  8. package/dist-server/engine/event-time.js.map +0 -1
  9. package/dist-server/engine/index.js.map +0 -1
  10. package/dist-server/engine/ingest-dedupe.js.map +0 -1
  11. package/dist-server/engine/ingest-health.js.map +0 -1
  12. package/dist-server/engine/integration-coverage.js.map +0 -1
  13. package/dist-server/engine/integration-probes.js.map +0 -1
  14. package/dist-server/engine/integration-runner.js.map +0 -1
  15. package/dist-server/engine/integration-target-profile.js.map +0 -1
  16. package/dist-server/engine/kpi-baseline.js.map +0 -1
  17. package/dist-server/engine/kpi-fold.js.map +0 -1
  18. package/dist-server/engine/kpi-query.js.map +0 -1
  19. package/dist-server/engine/kpi-target.js.map +0 -1
  20. package/dist-server/engine/live-attentions.js.map +0 -1
  21. package/dist-server/engine/live-feed-registry.js.map +0 -1
  22. package/dist-server/engine/load-meter.js.map +0 -1
  23. package/dist-server/engine/local-declarations.js.map +0 -1
  24. package/dist-server/engine/log.js.map +0 -1
  25. package/dist-server/engine/loop-lag.js.map +0 -1
  26. package/dist-server/engine/measured-estimator.js.map +0 -1
  27. package/dist-server/engine/measured-yield.js.map +0 -1
  28. package/dist-server/engine/model-basis.js.map +0 -1
  29. package/dist-server/engine/model-gap.js.map +0 -1
  30. package/dist-server/engine/model-refusal.js.map +0 -1
  31. package/dist-server/engine/model-vocabulary.js.map +0 -1
  32. package/dist-server/engine/oee-accumulator.js.map +0 -1
  33. package/dist-server/engine/operation-basis.js.map +0 -1
  34. package/dist-server/engine/producer-guard.js.map +0 -1
  35. package/dist-server/engine/property-effects.js.map +0 -1
  36. package/dist-server/engine/read-time.js.map +0 -1
  37. package/dist-server/engine/restart-policy.js.map +0 -1
  38. package/dist-server/engine/runtime-key.js.map +0 -1
  39. package/dist-server/engine/spec-coverage.js.map +0 -1
  40. package/dist-server/engine/stage-path.js.map +0 -1
  41. package/dist-server/engine/state-axes.js.map +0 -1
  42. package/dist-server/engine/structure-diff.js.map +0 -1
  43. package/dist-server/engine/travel-estimator.js.map +0 -1
  44. package/dist-server/engine/twin-engine.js.map +0 -1
  45. package/dist-server/engine/twin-state-channel.js.map +0 -1
  46. package/dist-server/engine/warm-start.js.map +0 -1
  47. package/dist-server/index.js.map +0 -1
  48. package/dist-server/migrations/1786000000000-RenameTwinInstanceBoardToModel.js.map +0 -1
  49. package/dist-server/migrations/1786100000000-PromoteEventActionAndSyncWarnings.js.map +0 -1
  50. package/dist-server/migrations/1786200000000-CarryLiveFeedCursor.js.map +0 -1
  51. package/dist-server/migrations/1786300000000-IndexEventTypeByTime.js.map +0 -1
  52. package/dist-server/migrations/1786400000000-IndexEventCreatedAt.js.map +0 -1
  53. package/dist-server/migrations/1786500000000-CreateTwinSubjectEvents.js.map +0 -1
  54. package/dist-server/migrations/1786600000000-RenameOperatoMesAdapterType.js.map +0 -1
  55. package/dist-server/migrations/1786700000000-AddDispatchRetryColumns.js.map +0 -1
  56. package/dist-server/migrations/1786800000000-CreateTwinReferenceReconciliations.js.map +0 -1
  57. package/dist-server/migrations/index.js.map +0 -1
  58. package/dist-server/routes.js.map +0 -1
  59. package/dist-server/service/actuation/actuation-advisor.js.map +0 -1
  60. package/dist-server/service/actuation/actuation-outcome.js.map +0 -1
  61. package/dist-server/service/actuation/actuation-rule-resolver.js.map +0 -1
  62. package/dist-server/service/actuation/actuation-rule.js.map +0 -1
  63. package/dist-server/service/actuation/approval-gateway.js.map +0 -1
  64. package/dist-server/service/actuation/catch-up-effects.js.map +0 -1
  65. package/dist-server/service/actuation/command-dispatcher.js.map +0 -1
  66. package/dist-server/service/actuation/command-store.js.map +0 -1
  67. package/dist-server/service/actuation/dispatch-after-approval.js.map +0 -1
  68. package/dist-server/service/actuation/effect-match.js.map +0 -1
  69. package/dist-server/service/actuation/index.js.map +0 -1
  70. package/dist-server/service/actuation/retry-plan.js.map +0 -1
  71. package/dist-server/service/actuation/rule-evaluate.js.map +0 -1
  72. package/dist-server/service/actuation/rule-loop.js.map +0 -1
  73. package/dist-server/service/actuation/rule-runner.js.map +0 -1
  74. package/dist-server/service/actuation/rule-wiring.js.map +0 -1
  75. package/dist-server/service/actuation/settle-effects.js.map +0 -1
  76. package/dist-server/service/actuation/sweep-dispatch.js.map +0 -1
  77. package/dist-server/service/actuation/twin-command-resolver.js.map +0 -1
  78. package/dist-server/service/actuation/twin-command.js.map +0 -1
  79. package/dist-server/service/actuation/twin-now.js.map +0 -1
  80. package/dist-server/service/index.js.map +0 -1
  81. package/dist-server/service/reference/actuation-routing.js.map +0 -1
  82. package/dist-server/service/reference/actuation-target.js.map +0 -1
  83. package/dist-server/service/reference/actuation-verification.js.map +0 -1
  84. package/dist-server/service/reference/attention-lane.js.map +0 -1
  85. package/dist-server/service/reference/connection-portability-resolver.js.map +0 -1
  86. package/dist-server/service/reference/connection-portability.js.map +0 -1
  87. package/dist-server/service/reference/control-routing.js.map +0 -1
  88. package/dist-server/service/reference/discovery-result.js.map +0 -1
  89. package/dist-server/service/reference/fill-cadence.js.map +0 -1
  90. package/dist-server/service/reference/fill-dedupe.js.map +0 -1
  91. package/dist-server/service/reference/fill-loop.js.map +0 -1
  92. package/dist-server/service/reference/fill-span.js.map +0 -1
  93. package/dist-server/service/reference/hook-contract.js.map +0 -1
  94. package/dist-server/service/reference/hook-rejected.js.map +0 -1
  95. package/dist-server/service/reference/hook-store.js.map +0 -1
  96. package/dist-server/service/reference/index.js.map +0 -1
  97. package/dist-server/service/reference/ingest-space.js.map +0 -1
  98. package/dist-server/service/reference/knob-defaults.js.map +0 -1
  99. package/dist-server/service/reference/live-cadence.js.map +0 -1
  100. package/dist-server/service/reference/live-feed-lease.js.map +0 -1
  101. package/dist-server/service/reference/live-ingest.js.map +0 -1
  102. package/dist-server/service/reference/reference-adapter.js.map +0 -1
  103. package/dist-server/service/reference/reference-assessment.js.map +0 -1
  104. package/dist-server/service/reference/reference-fill-run.js.map +0 -1
  105. package/dist-server/service/reference/reference-fill.js.map +0 -1
  106. package/dist-server/service/reference/reference-hook.js.map +0 -1
  107. package/dist-server/service/reference/reference-link-groups.js.map +0 -1
  108. package/dist-server/service/reference/reference-links.js.map +0 -1
  109. package/dist-server/service/reference/reference-live.js.map +0 -1
  110. package/dist-server/service/reference/reference-master.js.map +0 -1
  111. package/dist-server/service/reference/reference-probe.js.map +0 -1
  112. package/dist-server/service/reference/reference-progress-channel.js.map +0 -1
  113. package/dist-server/service/reference/reference-progress-subscription.js.map +0 -1
  114. package/dist-server/service/reference/reference-progress.js.map +0 -1
  115. package/dist-server/service/reference/reference-reconciliation-query.js.map +0 -1
  116. package/dist-server/service/reference/reference-reconciliation-writer.js.map +0 -1
  117. package/dist-server/service/reference/reference-reconciliation.js.map +0 -1
  118. package/dist-server/service/reference/reference-registry.js.map +0 -1
  119. package/dist-server/service/reference/reference-resolver.js.map +0 -1
  120. package/dist-server/service/reference/template-registry.js.map +0 -1
  121. package/dist-server/service/reference/twin-reference-reconciliation.js.map +0 -1
  122. package/dist-server/service/reference/twin-reference.js.map +0 -1
  123. package/dist-server/service/twin-attention/index.js.map +0 -1
  124. package/dist-server/service/twin-attention/twin-attention-query.js.map +0 -1
  125. package/dist-server/service/twin-audit/command-audit.js.map +0 -1
  126. package/dist-server/service/twin-audit/index.js.map +0 -1
  127. package/dist-server/service/twin-audit/twin-audit-event.js.map +0 -1
  128. package/dist-server/service/twin-audit/twin-audit-query.js.map +0 -1
  129. package/dist-server/service/twin-backfill/backfill-period-facts.js.map +0 -1
  130. package/dist-server/service/twin-backfill/backfill-shape.js.map +0 -1
  131. package/dist-server/service/twin-backfill/index.js.map +0 -1
  132. package/dist-server/service/twin-backfill/twin-backfill-resolver.js.map +0 -1
  133. package/dist-server/service/twin-control/index.js.map +0 -1
  134. package/dist-server/service/twin-control/twin-control-mutation.js.map +0 -1
  135. package/dist-server/service/twin-event/index.js.map +0 -1
  136. package/dist-server/service/twin-event/journal-count.js.map +0 -1
  137. package/dist-server/service/twin-event/twin-event-keys.js.map +0 -1
  138. package/dist-server/service/twin-event/twin-event-type.js.map +0 -1
  139. package/dist-server/service/twin-event/twin-event.js.map +0 -1
  140. package/dist-server/service/twin-forecast/forecast-metrics.js.map +0 -1
  141. package/dist-server/service/twin-forecast/forecast-tuning.js.map +0 -1
  142. package/dist-server/service/twin-forecast/gap-analytics.js.map +0 -1
  143. package/dist-server/service/twin-forecast/index.js.map +0 -1
  144. package/dist-server/service/twin-forecast/twin-forecast-query.js.map +0 -1
  145. package/dist-server/service/twin-ingest-window/index.js.map +0 -1
  146. package/dist-server/service/twin-ingest-window/twin-ingest-window-query.js.map +0 -1
  147. package/dist-server/service/twin-ingest-window/twin-ingest-window-writer.js.map +0 -1
  148. package/dist-server/service/twin-ingest-window/twin-ingest-window.js.map +0 -1
  149. package/dist-server/service/twin-instance/index.js.map +0 -1
  150. package/dist-server/service/twin-instance/twin-instance.js.map +0 -1
  151. package/dist-server/service/twin-journal/event-type-split-shape.js.map +0 -1
  152. package/dist-server/service/twin-journal/event-type-split.js.map +0 -1
  153. package/dist-server/service/twin-journal/index.js.map +0 -1
  154. package/dist-server/service/twin-journal/journal-order.js.map +0 -1
  155. package/dist-server/service/twin-journal/twin-journal-query.js.map +0 -1
  156. package/dist-server/service/twin-lifecycle/domain-catalog.js.map +0 -1
  157. package/dist-server/service/twin-lifecycle/index.js.map +0 -1
  158. package/dist-server/service/twin-lifecycle/twin-lifecycle-mutation.js.map +0 -1
  159. package/dist-server/service/twin-metrics/index.js.map +0 -1
  160. package/dist-server/service/twin-metrics/twin-metrics-query.js.map +0 -1
  161. package/dist-server/service/twin-model/axis-journal-evidence.js.map +0 -1
  162. package/dist-server/service/twin-model/choose-recipe.js.map +0 -1
  163. package/dist-server/service/twin-model/epcis-coverage.js.map +0 -1
  164. package/dist-server/service/twin-model/external-resolver.js.map +0 -1
  165. package/dist-server/service/twin-model/iec61850-coverage.js.map +0 -1
  166. package/dist-server/service/twin-model/index.js.map +0 -1
  167. package/dist-server/service/twin-model/isa95-coverage.js.map +0 -1
  168. package/dist-server/service/twin-model/item-ref.js.map +0 -1
  169. package/dist-server/service/twin-model/name-index.js.map +0 -1
  170. package/dist-server/service/twin-model/project-structure.js.map +0 -1
  171. package/dist-server/service/twin-model/standard-coverage.js.map +0 -1
  172. package/dist-server/service/twin-model/status-tally.js.map +0 -1
  173. package/dist-server/service/twin-model/twin-equipment.js.map +0 -1
  174. package/dist-server/service/twin-model/twin-lineage-query.js.map +0 -1
  175. package/dist-server/service/twin-model/twin-location.js.map +0 -1
  176. package/dist-server/service/twin-model/twin-model-item-query.js.map +0 -1
  177. package/dist-server/service/twin-model/twin-model-mutation.js.map +0 -1
  178. package/dist-server/service/twin-model/twin-model-query.js.map +0 -1
  179. package/dist-server/service/twin-model/twin-model-tree-query.js.map +0 -1
  180. package/dist-server/service/twin-model/twin-operation.js.map +0 -1
  181. package/dist-server/service/twin-readiness/index.js.map +0 -1
  182. package/dist-server/service/twin-readiness/twin-readiness-query.js.map +0 -1
  183. package/dist-server/service/twin-space/area-resolver.js.map +0 -1
  184. package/dist-server/service/twin-space/index.js.map +0 -1
  185. package/dist-server/service/twin-space/move-space.js.map +0 -1
  186. package/dist-server/service/twin-space/representation-role.js.map +0 -1
  187. package/dist-server/service/twin-space/space-integrity.js.map +0 -1
  188. package/dist-server/service/twin-space/twin-area.js.map +0 -1
  189. package/dist-server/service/twin-space/twin-space-area.js.map +0 -1
  190. package/dist-server/service/twin-space/twin-space-representation.js.map +0 -1
  191. package/dist-server/service/twin-space/twin-space-resolver.js.map +0 -1
  192. package/dist-server/service/twin-space/twin-space.js.map +0 -1
  193. package/dist-server/service/twin-state/index.js.map +0 -1
  194. package/dist-server/service/twin-state/twin-state-subscription.js.map +0 -1
  195. package/dist-server/service/twin-structure/index.js.map +0 -1
  196. package/dist-server/service/twin-structure/twin-structure.js.map +0 -1
  197. package/dist-server/service/twin-subject/event-subjects.js.map +0 -1
  198. package/dist-server/service/twin-subject/index.js.map +0 -1
  199. package/dist-server/service/twin-subject/item-resolver.js.map +0 -1
  200. package/dist-server/service/twin-subject/subject-query-shape.js.map +0 -1
  201. package/dist-server/service/twin-subject/twin-subject-backfill.js.map +0 -1
  202. package/dist-server/service/twin-subject/twin-subject-event.js.map +0 -1
  203. package/dist-server/service/twin-subject/twin-subject-fast-path.js.map +0 -1
  204. package/dist-server/service/twin-subject/twin-subject-query.js.map +0 -1
  205. package/dist-server/service/twin-subject/twin-subject-writer.js.map +0 -1
  206. package/dist-server/service/twin-target/index.js.map +0 -1
  207. package/dist-server/service/twin-target/twin-target-resolver.js.map +0 -1
  208. package/dist-server/service/twin-target/twin-target.js.map +0 -1
  209. package/dist-shared/axis-read.js.map +0 -1
  210. package/dist-shared/entity-delta.js.map +0 -1
  211. package/dist-shared/kpi-broadcast.js.map +0 -1
  212. package/dist-shared/touched-items.js.map +0 -1
  213. package/dist-shared/twin-level.js.map +0 -1
  214. package/server/engine/attention-digest.ts +0 -102
  215. package/server/engine/canonical-ingest.ts +0 -511
  216. package/server/engine/checkpoint-fold.ts +0 -12
  217. package/server/engine/command-routing.ts +0 -67
  218. package/server/engine/declared-stimulus.ts +0 -66
  219. package/server/engine/energy-topology.ts +0 -189
  220. package/server/engine/event-time.ts +0 -96
  221. package/server/engine/index.ts +0 -51
  222. package/server/engine/ingest-dedupe.ts +0 -24
  223. package/server/engine/ingest-health.ts +0 -24
  224. package/server/engine/integration-coverage.ts +0 -147
  225. package/server/engine/integration-probes.ts +0 -144
  226. package/server/engine/integration-runner.ts +0 -95
  227. package/server/engine/integration-target-profile.ts +0 -103
  228. package/server/engine/kpi-baseline.ts +0 -202
  229. package/server/engine/kpi-fold.ts +0 -1516
  230. package/server/engine/kpi-query.ts +0 -1210
  231. package/server/engine/kpi-target.ts +0 -248
  232. package/server/engine/live-attentions.ts +0 -30
  233. package/server/engine/live-feed-registry.ts +0 -59
  234. package/server/engine/load-meter.ts +0 -393
  235. package/server/engine/local-declarations.ts +0 -779
  236. package/server/engine/log.ts +0 -72
  237. package/server/engine/loop-lag.ts +0 -120
  238. package/server/engine/measured-estimator.ts +0 -91
  239. package/server/engine/measured-yield.ts +0 -89
  240. package/server/engine/model-basis.ts +0 -162
  241. package/server/engine/model-gap.ts +0 -168
  242. package/server/engine/model-refusal.ts +0 -31
  243. package/server/engine/model-vocabulary.ts +0 -82
  244. package/server/engine/oee-accumulator.ts +0 -195
  245. package/server/engine/operation-basis.ts +0 -131
  246. package/server/engine/producer-guard.ts +0 -64
  247. package/server/engine/property-effects.ts +0 -216
  248. package/server/engine/read-time.ts +0 -77
  249. package/server/engine/restart-policy.ts +0 -55
  250. package/server/engine/runtime-key.ts +0 -58
  251. package/server/engine/spec-coverage.ts +0 -105
  252. package/server/engine/stage-path.ts +0 -172
  253. package/server/engine/state-axes.ts +0 -60
  254. package/server/engine/structure-diff.ts +0 -88
  255. package/server/engine/travel-estimator.ts +0 -138
  256. package/server/engine/twin-engine.ts +0 -4979
  257. package/server/engine/twin-state-channel.ts +0 -39
  258. package/server/engine/warm-start.ts +0 -265
  259. package/server/index.ts +0 -90
  260. package/server/migrations/1786000000000-RenameTwinInstanceBoardToModel.ts +0 -43
  261. package/server/migrations/1786100000000-PromoteEventActionAndSyncWarnings.ts +0 -54
  262. package/server/migrations/1786200000000-CarryLiveFeedCursor.ts +0 -53
  263. package/server/migrations/1786300000000-IndexEventTypeByTime.ts +0 -48
  264. package/server/migrations/1786400000000-IndexEventCreatedAt.ts +0 -38
  265. package/server/migrations/1786500000000-CreateTwinSubjectEvents.ts +0 -93
  266. package/server/migrations/1786600000000-RenameOperatoMesAdapterType.ts +0 -66
  267. package/server/migrations/1786700000000-AddDispatchRetryColumns.ts +0 -82
  268. package/server/migrations/1786800000000-CreateTwinReferenceReconciliations.ts +0 -39
  269. package/server/migrations/index.ts +0 -25
  270. package/server/routes.ts +0 -75
  271. package/server/service/actuation/actuation-advisor.ts +0 -131
  272. package/server/service/actuation/actuation-outcome.ts +0 -97
  273. package/server/service/actuation/actuation-rule-resolver.ts +0 -147
  274. package/server/service/actuation/actuation-rule.ts +0 -139
  275. package/server/service/actuation/approval-gateway.ts +0 -44
  276. package/server/service/actuation/catch-up-effects.ts +0 -152
  277. package/server/service/actuation/command-dispatcher.ts +0 -474
  278. package/server/service/actuation/command-store.ts +0 -236
  279. package/server/service/actuation/dispatch-after-approval.ts +0 -71
  280. package/server/service/actuation/effect-match.ts +0 -124
  281. package/server/service/actuation/index.ts +0 -27
  282. package/server/service/actuation/retry-plan.ts +0 -145
  283. package/server/service/actuation/rule-evaluate.ts +0 -352
  284. package/server/service/actuation/rule-loop.ts +0 -160
  285. package/server/service/actuation/rule-runner.ts +0 -216
  286. package/server/service/actuation/rule-wiring.ts +0 -84
  287. package/server/service/actuation/settle-effects.ts +0 -115
  288. package/server/service/actuation/sweep-dispatch.ts +0 -179
  289. package/server/service/actuation/twin-command-resolver.ts +0 -387
  290. package/server/service/actuation/twin-command.ts +0 -232
  291. package/server/service/actuation/twin-now.ts +0 -97
  292. package/server/service/index.ts +0 -100
  293. package/server/service/reference/actuation-routing.ts +0 -192
  294. package/server/service/reference/actuation-target.ts +0 -163
  295. package/server/service/reference/actuation-verification.ts +0 -85
  296. package/server/service/reference/attention-lane.ts +0 -21
  297. package/server/service/reference/connection-portability-resolver.ts +0 -171
  298. package/server/service/reference/connection-portability.ts +0 -329
  299. package/server/service/reference/control-routing.ts +0 -62
  300. package/server/service/reference/discovery-result.ts +0 -95
  301. package/server/service/reference/fill-cadence.ts +0 -29
  302. package/server/service/reference/fill-dedupe.ts +0 -66
  303. package/server/service/reference/fill-loop.ts +0 -153
  304. package/server/service/reference/fill-span.ts +0 -31
  305. package/server/service/reference/hook-contract.ts +0 -364
  306. package/server/service/reference/hook-rejected.ts +0 -53
  307. package/server/service/reference/hook-store.ts +0 -28
  308. package/server/service/reference/index.ts +0 -18
  309. package/server/service/reference/ingest-space.ts +0 -104
  310. package/server/service/reference/knob-defaults.ts +0 -59
  311. package/server/service/reference/live-cadence.ts +0 -129
  312. package/server/service/reference/live-feed-lease.ts +0 -56
  313. package/server/service/reference/live-ingest.ts +0 -94
  314. package/server/service/reference/reference-adapter.ts +0 -985
  315. package/server/service/reference/reference-assessment.ts +0 -250
  316. package/server/service/reference/reference-fill-run.ts +0 -179
  317. package/server/service/reference/reference-fill.ts +0 -223
  318. package/server/service/reference/reference-hook.ts +0 -479
  319. package/server/service/reference/reference-link-groups.ts +0 -21
  320. package/server/service/reference/reference-links.ts +0 -77
  321. package/server/service/reference/reference-live.ts +0 -420
  322. package/server/service/reference/reference-master.ts +0 -999
  323. package/server/service/reference/reference-probe.ts +0 -264
  324. package/server/service/reference/reference-progress-channel.ts +0 -26
  325. package/server/service/reference/reference-progress-subscription.ts +0 -78
  326. package/server/service/reference/reference-progress.ts +0 -95
  327. package/server/service/reference/reference-reconciliation-query.ts +0 -28
  328. package/server/service/reference/reference-reconciliation-writer.ts +0 -38
  329. package/server/service/reference/reference-reconciliation.ts +0 -55
  330. package/server/service/reference/reference-registry.ts +0 -17
  331. package/server/service/reference/reference-resolver.ts +0 -969
  332. package/server/service/reference/template-registry.ts +0 -85
  333. package/server/service/reference/twin-reference-reconciliation.ts +0 -27
  334. package/server/service/reference/twin-reference.ts +0 -190
  335. package/server/service/twin-attention/index.ts +0 -3
  336. package/server/service/twin-attention/twin-attention-query.ts +0 -70
  337. package/server/service/twin-audit/command-audit.ts +0 -114
  338. package/server/service/twin-audit/index.ts +0 -5
  339. package/server/service/twin-audit/twin-audit-event.ts +0 -158
  340. package/server/service/twin-audit/twin-audit-query.ts +0 -72
  341. package/server/service/twin-backfill/backfill-period-facts.ts +0 -168
  342. package/server/service/twin-backfill/backfill-shape.ts +0 -86
  343. package/server/service/twin-backfill/index.ts +0 -3
  344. package/server/service/twin-backfill/twin-backfill-resolver.ts +0 -35
  345. package/server/service/twin-control/index.ts +0 -3
  346. package/server/service/twin-control/twin-control-mutation.ts +0 -150
  347. package/server/service/twin-event/index.ts +0 -3
  348. package/server/service/twin-event/journal-count.ts +0 -53
  349. package/server/service/twin-event/twin-event-keys.ts +0 -215
  350. package/server/service/twin-event/twin-event-type.ts +0 -68
  351. package/server/service/twin-event/twin-event.ts +0 -250
  352. package/server/service/twin-forecast/forecast-metrics.ts +0 -106
  353. package/server/service/twin-forecast/forecast-tuning.ts +0 -40
  354. package/server/service/twin-forecast/gap-analytics.ts +0 -139
  355. package/server/service/twin-forecast/index.ts +0 -3
  356. package/server/service/twin-forecast/twin-forecast-query.ts +0 -486
  357. package/server/service/twin-ingest-window/index.ts +0 -7
  358. package/server/service/twin-ingest-window/twin-ingest-window-query.ts +0 -125
  359. package/server/service/twin-ingest-window/twin-ingest-window-writer.ts +0 -57
  360. package/server/service/twin-ingest-window/twin-ingest-window.ts +0 -113
  361. package/server/service/twin-instance/index.ts +0 -3
  362. package/server/service/twin-instance/twin-instance.ts +0 -199
  363. package/server/service/twin-journal/event-type-split-shape.ts +0 -112
  364. package/server/service/twin-journal/event-type-split.ts +0 -144
  365. package/server/service/twin-journal/index.ts +0 -3
  366. package/server/service/twin-journal/journal-order.ts +0 -20
  367. package/server/service/twin-journal/twin-journal-query.ts +0 -615
  368. package/server/service/twin-lifecycle/domain-catalog.ts +0 -51
  369. package/server/service/twin-lifecycle/index.ts +0 -3
  370. package/server/service/twin-lifecycle/twin-lifecycle-mutation.ts +0 -285
  371. package/server/service/twin-metrics/index.ts +0 -3
  372. package/server/service/twin-metrics/twin-metrics-query.ts +0 -172
  373. package/server/service/twin-model/axis-journal-evidence.ts +0 -57
  374. package/server/service/twin-model/choose-recipe.ts +0 -31
  375. package/server/service/twin-model/epcis-coverage.ts +0 -251
  376. package/server/service/twin-model/external-resolver.ts +0 -173
  377. package/server/service/twin-model/iec61850-coverage.ts +0 -111
  378. package/server/service/twin-model/index.ts +0 -25
  379. package/server/service/twin-model/isa95-coverage.ts +0 -320
  380. package/server/service/twin-model/item-ref.ts +0 -80
  381. package/server/service/twin-model/name-index.ts +0 -94
  382. package/server/service/twin-model/project-structure.ts +0 -213
  383. package/server/service/twin-model/standard-coverage.ts +0 -35
  384. package/server/service/twin-model/status-tally.ts +0 -37
  385. package/server/service/twin-model/twin-equipment.ts +0 -191
  386. package/server/service/twin-model/twin-lineage-query.ts +0 -327
  387. package/server/service/twin-model/twin-location.ts +0 -147
  388. package/server/service/twin-model/twin-model-item-query.ts +0 -1029
  389. package/server/service/twin-model/twin-model-mutation.ts +0 -503
  390. package/server/service/twin-model/twin-model-query.ts +0 -792
  391. package/server/service/twin-model/twin-model-tree-query.ts +0 -211
  392. package/server/service/twin-model/twin-operation.ts +0 -157
  393. package/server/service/twin-readiness/index.ts +0 -3
  394. package/server/service/twin-readiness/twin-readiness-query.ts +0 -96
  395. package/server/service/twin-space/area-resolver.ts +0 -75
  396. package/server/service/twin-space/index.ts +0 -8
  397. package/server/service/twin-space/move-space.ts +0 -262
  398. package/server/service/twin-space/representation-role.ts +0 -15
  399. package/server/service/twin-space/space-integrity.ts +0 -293
  400. package/server/service/twin-space/twin-area.ts +0 -63
  401. package/server/service/twin-space/twin-space-area.ts +0 -66
  402. package/server/service/twin-space/twin-space-representation.ts +0 -99
  403. package/server/service/twin-space/twin-space-resolver.ts +0 -582
  404. package/server/service/twin-space/twin-space.ts +0 -148
  405. package/server/service/twin-state/index.ts +0 -3
  406. package/server/service/twin-state/twin-state-subscription.ts +0 -62
  407. package/server/service/twin-structure/index.ts +0 -3
  408. package/server/service/twin-structure/twin-structure.ts +0 -96
  409. package/server/service/twin-subject/event-subjects.ts +0 -162
  410. package/server/service/twin-subject/index.ts +0 -10
  411. package/server/service/twin-subject/item-resolver.ts +0 -92
  412. package/server/service/twin-subject/subject-query-shape.ts +0 -84
  413. package/server/service/twin-subject/twin-subject-backfill.ts +0 -196
  414. package/server/service/twin-subject/twin-subject-event.ts +0 -101
  415. package/server/service/twin-subject/twin-subject-fast-path.ts +0 -346
  416. package/server/service/twin-subject/twin-subject-query.ts +0 -210
  417. package/server/service/twin-subject/twin-subject-writer.ts +0 -189
  418. package/server/service/twin-target/index.ts +0 -5
  419. package/server/service/twin-target/twin-target-resolver.ts +0 -142
  420. package/server/service/twin-target/twin-target.ts +0 -97
  421. package/server/types/global.d.ts +0 -22
  422. package/shared/axis-read.ts +0 -28
  423. package/shared/entity-delta.ts +0 -335
  424. package/shared/kpi-broadcast.ts +0 -13
  425. package/shared/touched-items.ts +0 -73
  426. package/shared/twin-level.ts +0 -48
  427. package/test/ack-shape-alignment.test.ts +0 -97
  428. package/test/actuation-advisor.test.ts +0 -55
  429. package/test/actuation-approval-door.test.ts +0 -411
  430. package/test/actuation-dispatch.test.ts +0 -209
  431. package/test/actuation-effect.test.ts +0 -162
  432. package/test/actuation-outcome.test.ts +0 -152
  433. package/test/actuation-rule-runner.test.ts +0 -281
  434. package/test/actuation-rule.test.ts +0 -266
  435. package/test/actuation-seam.test.ts +0 -255
  436. package/test/actuation-settle.test.ts +0 -164
  437. package/test/actuation-target.test.ts +0 -145
  438. package/test/actuation-verification-state.test.ts +0 -139
  439. package/test/adopt-structure-live.test.ts +0 -133
  440. package/test/aggregate-not-findone.test.ts +0 -112
  441. package/test/area-resolver-db.test.ts +0 -110
  442. package/test/attention-digest.test.ts +0 -161
  443. package/test/axis-declaration-passthrough-db.test.ts +0 -152
  444. package/test/axis-journal-evidence.test.ts +0 -71
  445. package/test/axis-read.test.ts +0 -188
  446. package/test/backfill-shape.test.ts +0 -89
  447. package/test/boot-resume.test.ts +0 -287
  448. package/test/broadcast-cost-baseline.test.ts +0 -201
  449. package/test/broadcast-period.test.ts +0 -58
  450. package/test/broadcast-queue-overflow.test.ts +0 -244
  451. package/test/canonical-ingest-vocabularies.test.ts +0 -451
  452. package/test/canonical-quantity-door.test.ts +0 -61
  453. package/test/capability-mapping.test.ts +0 -89
  454. package/test/checkpoint-refuses-empty.test.ts +0 -103
  455. package/test/choose-recipe.test.ts +0 -117
  456. package/test/column-type-portability.test.ts +0 -122
  457. package/test/command-routing.test.ts +0 -61
  458. package/test/command-store-writes-columns.test.ts +0 -126
  459. package/test/connection-portability-doors.test.ts +0 -87
  460. package/test/connector-capability-declaration.test.ts +0 -195
  461. package/test/contract-layer-guard.test.ts +0 -48
  462. package/test/control-capability.test.ts +0 -98
  463. package/test/cursor-stall-not-read-failure.test.ts +0 -126
  464. package/test/declaration-reaches-model.test.ts +0 -226
  465. package/test/declared-location-types.test.ts +0 -89
  466. package/test/declared-stimulus.test.ts +0 -88
  467. package/test/dedupe-transformation.test.ts +0 -112
  468. package/test/discovery-result.test.ts +0 -75
  469. package/test/dispatch-after-approval-source.test.ts +0 -81
  470. package/test/duration-estimators.test.ts +0 -144
  471. package/test/energy-topology.test.ts +0 -113
  472. package/test/entity-delta.test.ts +0 -352
  473. package/test/equipment-identity-reaches.test.ts +0 -57
  474. package/test/event-subjects.test.ts +0 -148
  475. package/test/event-time-column.test.ts +0 -67
  476. package/test/event-type-split.test.ts +0 -178
  477. package/test/external-resolver.test.ts +0 -177
  478. package/test/fact-scope-wiring.test.ts +0 -96
  479. package/test/fill-dedupe.test.ts +0 -59
  480. package/test/fill-loop.test.ts +0 -35
  481. package/test/first-envelope.test.ts +0 -153
  482. package/test/fold-gap-speaks.test.ts +0 -53
  483. package/test/fold-shift-dst.test.ts +0 -110
  484. package/test/forecast-metrics.test.ts +0 -157
  485. package/test/forecast-tuning.test.ts +0 -42
  486. package/test/gap-analytics.test.ts +0 -113
  487. package/test/generated-not-counted.test.ts +0 -99
  488. package/test/guard-roots.ts +0 -76
  489. package/test/home-in-area.test.ts +0 -90
  490. package/test/hook-address.test.ts +0 -28
  491. package/test/hook-rejected-shape.test.ts +0 -86
  492. package/test/hook-response-sum.test.ts +0 -193
  493. package/test/hook-sequence.test.ts +0 -333
  494. package/test/ingest-bench.test.ts +0 -70
  495. package/test/ingest-dedupe.test.ts +0 -56
  496. package/test/ingest-expected-quiet.test.ts +0 -111
  497. package/test/ingest-health-engine.test.ts +0 -248
  498. package/test/ingest-health-wiring.test.ts +0 -120
  499. package/test/ingest-health.test.ts +0 -306
  500. package/test/ingest-history.test.ts +0 -247
  501. package/test/ingest-idempotent.test.ts +0 -211
  502. package/test/ingest-reconcile-four-ways.test.ts +0 -97
  503. package/test/ingest-rules-header.test.ts +0 -100
  504. package/test/ingest-running-guard.test.ts +0 -136
  505. package/test/ingest-space.test.ts +0 -78
  506. package/test/ingest-window-rolls-unobserved.test.ts +0 -72
  507. package/test/ingest-wiring-guard.test.ts +0 -197
  508. package/test/instance-cache-lifecycle.test.ts +0 -110
  509. package/test/intake-mapping-parity.test.ts +0 -175
  510. package/test/integration-probes.test.ts +0 -103
  511. package/test/integration-runner.test.ts +0 -95
  512. package/test/item-ref.test.ts +0 -78
  513. package/test/journal-order-axis.test.ts +0 -80
  514. package/test/journal-read-discipline.test.ts +0 -186
  515. package/test/journal-retention.test.ts +0 -275
  516. package/test/journal-sort-axis.test.ts +0 -142
  517. package/test/journal-write-door.test.ts +0 -110
  518. package/test/journal-write-trend.test.ts +0 -115
  519. package/test/kernel-kind-guard.test.ts +0 -98
  520. package/test/knob-defaults.test.ts +0 -72
  521. package/test/kpi-baseline-db.test.ts +0 -216
  522. package/test/kpi-baseline.test.ts +0 -196
  523. package/test/kpi-fold.test.ts +0 -1456
  524. package/test/kpi-query-bench.test.ts +0 -130
  525. package/test/lens-own-places.test.ts +0 -50
  526. package/test/lineage-survives-restart.test.ts +0 -234
  527. package/test/live-cadence.test.ts +0 -170
  528. package/test/live-cursor-wiring.test.ts +0 -87
  529. package/test/live-feed-lease.test.ts +0 -63
  530. package/test/live-feed-registry.test.ts +0 -75
  531. package/test/live-kernel-facts.test.ts +0 -161
  532. package/test/live-mirror-parity.test.ts +0 -286
  533. package/test/load-meter.test.ts +0 -328
  534. package/test/local-declarations.test.ts +0 -662
  535. package/test/log-stamp.test.ts +0 -59
  536. package/test/loop-lag.test.ts +0 -82
  537. package/test/master-to-twin.test.ts +0 -279
  538. package/test/measured-yield.test.ts +0 -90
  539. package/test/merge-streams.test.ts +0 -70
  540. package/test/mirror-resumes-from-checkpoint.test.ts +0 -192
  541. package/test/model-basis.test.ts +0 -141
  542. package/test/model-gap.test.ts +0 -153
  543. package/test/model-refusal.test.ts +0 -75
  544. package/test/model-vocabulary.test.ts +0 -114
  545. package/test/move-space.test.ts +0 -149
  546. package/test/mutation-gate.test.ts +0 -122
  547. package/test/oee-accumulator.test.ts +0 -204
  548. package/test/oee-site-calendar.test.ts +0 -146
  549. package/test/open-doors.test.ts +0 -197
  550. package/test/operation-basis.test.ts +0 -127
  551. package/test/operational-vocabulary.test.ts +0 -108
  552. package/test/operations-capability-db.test.ts +0 -165
  553. package/test/order-and-transaction-split.test.ts +0 -107
  554. package/test/order-fold-host.test.ts +0 -112
  555. package/test/producer-guard.test.ts +0 -66
  556. package/test/project-structure-db.test.ts +0 -226
  557. package/test/projection-reaches-screen.test.ts +0 -277
  558. package/test/projection-reads-declared.test.ts +0 -109
  559. package/test/property-effects.test.ts +0 -128
  560. package/test/provenance-promotion.test.ts +0 -99
  561. package/test/read-failure-ledger.test.ts +0 -106
  562. package/test/read-failure-visible.test.ts +0 -145
  563. package/test/read-time.test.ts +0 -118
  564. package/test/reference-fill.test.ts +0 -235
  565. package/test/reference-grounding.test.ts +0 -70
  566. package/test/reference-hook.test.ts +0 -177
  567. package/test/reference-links.test.ts +0 -56
  568. package/test/reference-reconciliation.test.ts +0 -39
  569. package/test/registry-key-guard.test.ts +0 -80
  570. package/test/rejection-carries-event-id.test.ts +0 -101
  571. package/test/rename-twin.test.ts +0 -89
  572. package/test/resolve-ts-siblings.mjs +0 -52
  573. package/test/restart-policy.test.ts +0 -111
  574. package/test/resync-origin-site.test.ts +0 -83
  575. package/test/retry-plan.test.ts +0 -139
  576. package/test/revision-axis.test.ts +0 -110
  577. package/test/runtime-key.test.ts +0 -66
  578. package/test/save-intake-collision.test.ts +0 -91
  579. package/test/scale-twin-bench.test.ts +0 -103
  580. package/test/secrets-at-rest.test.ts +0 -135
  581. package/test/snapshot-freshness.test.ts +0 -60
  582. package/test/source-outcome-audit.test.ts +0 -104
  583. package/test/space-integrity.test.ts +0 -223
  584. package/test/space-representation.test.ts +0 -66
  585. package/test/space-scope-includes-stopped.test.ts +0 -55
  586. package/test/spec-coverage.test.ts +0 -113
  587. package/test/stage-path.test.ts +0 -95
  588. package/test/standard-coverage.test.ts +0 -148
  589. package/test/state-axes.test.ts +0 -74
  590. package/test/status-tally.test.ts +0 -55
  591. package/test/streamline-e2e.test.ts +0 -76
  592. package/test/structure-revision-db.test.ts +0 -315
  593. package/test/subject-fast-path.test.ts +0 -148
  594. package/test/subject-read-model.test.ts +0 -175
  595. package/test/sweep-dispatch.test.ts +0 -191
  596. package/test/tenant-registry-db.test.ts +0 -149
  597. package/test/time-range.test.ts +0 -152
  598. package/test/touched-items.test.ts +0 -61
  599. package/test/twin-audit.test.ts +0 -83
  600. package/test/twin-event-keys.test.ts +0 -193
  601. package/test/twin-model-item-db.test.ts +0 -319
  602. package/test/twin-model-parent-relation-db.test.ts +0 -125
  603. package/test/twin-model-tree-db.test.ts +0 -209
  604. package/test/twin-origin-resync.test.ts +0 -133
  605. package/test/twin-state-channel.test.ts +0 -111
  606. package/test/vocabulary-guard.test.ts +0 -43
  607. package/test/warm-start-seam.test.ts +0 -162
  608. package/test/warm-start.test.ts +0 -297
  609. package/test/withheld-door.test.ts +0 -95
  610. package/test/yield-loop.test.ts +0 -199
  611. package/tsconfig.json +0 -15
  612. package/tsconfig.shared.json +0 -23
  613. package/tsconfig.shared.tsbuildinfo +0 -1
  614. package/tsconfig.tsbuildinfo +0 -1
@@ -1,171 +0,0 @@
1
- /*
2
- * **연결을 배포 사이로 옮기는 문 셋** — 몸통은 `connection-portability.ts` 의 순수 함수들이다.
3
- *
4
- * ── 무엇을 옮기나 (사용자와 맞춘 개념, 2026-09-05) ─────────────────────────
5
- * **옮기는 것은 「붙는 법」이지 「붙은 결과」가 아니다.**
6
- *
7
- * 옮긴다 연결 선언 — 주소 · 도메인 · 라인 크기 · 자재 이름공간
8
- * 안 옮긴다 마스터 원본에서 다시 읽으면 된다
9
- * 안 옮긴다 사실(저널) 원본이 다시 보내면 된다
10
- * 안 옮긴다 잰 값 그 서버에서 다시 세면 된다
11
- * 안 옮긴다 비밀값 그 환경의 것이지 셋업이 아니다
12
- *
13
- * **패키지는 정본이 아니라 운반 수단이다.** 정본은 언제나 그 배포의 DB 이고, 패키지는 쓰고 버린다.
14
- *
15
- * ── 주체는 받는 쪽이다 ─────────────────────────────────────────────────────
16
- * 운영이 개발에서 **당겨온다.** 개발이 운영으로 밀면 개발 환경에 운영 자격이 살고, 그 통로는 실수로도
17
- * 열린다. 그래서 `pullTwinConnectionsFrom` 이 본래 원하던 문이고, 나머지 둘은 저쪽에 못 닿을 때의
18
- * 길이다.
19
- *
20
- * ── 쓰기 전에 계획을 보인다 ────────────────────────────────────────────────
21
- * `planImport` 가 새로/갱신/건너뜀과 **사람이 채울 비밀값**을 먼저 낸다. 부딪힘(두 연결이 같은 공장에
22
- * 조치를 내는 것)이 하나라도 있으면 **아무것도 쓰지 않는다** — 반만 적용된 연결 설정은 어느 쪽이
23
- * 참인지 아무도 모른다.
24
- */
25
- import { Arg, Ctx, Directive, Mutation, Query, Resolver } from 'type-graphql'
26
- import { ScalarObject, getRepository } from '@things-factory/shell'
27
-
28
- import { TwinReference } from './twin-reference.js'
29
- import { exportConnections, intakeCollisions, planImport, sayPlan, secretEnvName } from './connection-portability.js'
30
-
31
- /** 그 환경이 이 비밀값을 줄 수 있나 — **값은 읽지 않는다.** 이름의 존재만 본다. */
32
- function hasSecret(source: string, key: string): boolean {
33
- const name = secretEnvName(source, key)
34
- const v = process.env[name]
35
- return typeof v === 'string' && v.trim().length > 0
36
- }
37
-
38
- @Resolver()
39
- export class ConnectionPortabilityResolver {
40
- /**
41
- * 지금 연결을 패키지로 낸다 — **비밀값은 안 나간다.**
42
- *
43
- * 이름 규칙으로 걸러서 **새 비밀 칸도 기본으로 안 나간다**(`SECRET_KEYS`). 나가지 않은 것은
44
- * 패키지가 이름으로 말하고, 받는 쪽이 그 이름을 사람에게 보인다.
45
- */
46
- /*
47
- * **읽기인데 쓰기와 같은 문을 쓴다.** 이 패키지는 연결 주소·도메인과 **비밀 칸의 이름**까지 낸다 —
48
- * 그 이름만으로도 그 배포가 무엇에 붙어 있는지 드러난다. 권한 가드가 「쓰기는 GATE 하나」로
49
- * 통일해 두었고, 이 문은 읽기지만 그 무게가 쓰기 쪽이다(ADR-0027).
50
- */
51
- @Directive('@privilege(category: "twin", privilege: "mutation", domainOwnerGranted: true, superUserGranted: true)')
52
- @Query(returns => ScalarObject, {
53
- description:
54
- 'Export this deployment’s twin connection declarations as a portable bundle. Secrets are never included — the bundle names them so the receiving side can ask a human.'
55
- })
56
- async twinConnectionBundle(
57
- @Ctx() context: ResolverContext,
58
- @Arg('note', { nullable: true, description: 'Free note carried in the bundle (why this export was made).' }) note?: string
59
- ): Promise<any> {
60
- const domain = context.state.domain
61
- const rows = await getRepository(TwinReference).find({ where: { domain: { id: domain.id } } })
62
- return exportConnections(rows as any, { at: new Date().toISOString(), domain: domain.subdomain ?? domain.name, ...(note ? { note } : {}) })
63
- }
64
-
65
- /**
66
- * 패키지를 받아 적용한다 — **`dryRun` 이 기본이다.**
67
- *
68
- * 쓰기가 기본이면 「무엇이 바뀌나」를 보기 위해 쓰게 된다. 계획을 먼저 보고 사람이 정하는 것이
69
- * 이 문의 요점이고, 그래서 적용은 명시해야 일어난다.
70
- */
71
- @Directive('@privilege(category: "twin", privilege: "mutation", domainOwnerGranted: true, superUserGranted: true)')
72
- @Mutation(returns => ScalarObject, {
73
- description:
74
- 'Apply a connection bundle. Returns the plan (new / updated / skipped, secrets a human must supply, collisions). Defaults to dryRun — pass apply:true to write.'
75
- })
76
- async importTwinConnections(
77
- @Arg('bundle', type => ScalarObject) bundle: any,
78
- @Ctx() context: ResolverContext,
79
- @Arg('apply', { nullable: true, description: 'Write the plan. Omit or false to only see what would happen.' }) apply?: boolean
80
- ): Promise<any> {
81
- const domainId = context.state.domain.id
82
- const repo = getRepository(TwinReference)
83
- const existing = await repo.find({ where: { domain: { id: domainId } } })
84
-
85
- const plan = planImport(bundle, existing as any, hasSecret)
86
- const said = sayPlan(plan)
87
-
88
- /*
89
- * 부딪힘이 있으면 **아무것도 쓰지 않는다.** 두 연결이 같은 공장에 조치를 내면 어느 쪽이 그 라인의
90
- * 주인인지 정할 수 없고, 반만 적용하면 그 물음이 조용히 남는다.
91
- */
92
- if (plan.collisions.length || plan.errors.length) {
93
- return { ok: false, applied: 0, plan, said, refused: plan.collisions.length ? 'intake-collision' : 'bundle-error' }
94
- }
95
- if (!apply) return { ok: true, applied: 0, plan, said, dryRun: true }
96
-
97
- let applied = 0
98
- for (const a of plan.actions) {
99
- /* 건너뛴 것은 이유가 계획에 남아 있다 — 여기서 다시 판정하지 않는다. */
100
- if (a.action === 'skip') continue
101
-
102
- const c = a.connection
103
- /*
104
- * **비밀값을 빈 값으로 안 채운다.** 패키지에 비밀값이 없고, `planImport` 가 그 이름을
105
- * `missingSecrets`·`needsHuman` 으로 이미 냈다. 여기서 그 칸을 `undefined` 로 쓰면 사람이
106
- * 화면에서 넣어 둔 값을 파일 하나가 지운다.
107
- *
108
- * 그래서 갱신은 **선언 칸만** 건드린다. 이미 있는 연결의 `connectionConfig` 는 패키지가 든
109
- * 것만 덮어쓰고 나머지 키는 그대로 남긴다.
110
- */
111
- const found = await repo.findOne({ where: { domain: { id: domainId }, source: c.source } })
112
- const declared = {
113
- ...(c.system ? { system: c.system } : {}),
114
- ...(c.siteName ? { siteName: c.siteName } : {}),
115
- adapterType: c.adapterType,
116
- ...(c.scope ? { scopeSpec: c.scope } : {})
117
- } as any
118
-
119
- if (found) {
120
- const merged = { ...((found as any).connectionConfig ?? {}), ...(c.config ?? {}) }
121
- await repo.update({ id: (found as any).id }, { ...declared, connectionConfig: merged } as any)
122
- } else {
123
- await repo.save(repo.create({ source: c.source, ...declared, connectionConfig: c.config ?? {}, domain: { id: domainId } as any }))
124
- }
125
- applied++
126
- }
127
- return { ok: true, applied, plan, said }
128
- }
129
-
130
- /**
131
- * 저쪽 배포에서 당겨와 적용한다 — **본래 원한 문.**
132
- *
133
- * 받는 쪽이 주체다. 저쪽의 `twinConnectionBundle` 을 불러 이 배포에 적용한다 — 위 두 문을 잇는
134
- * 얇은 층이고, 판정과 쓰기는 `importTwinConnections` 와 같은 규칙을 쓴다.
135
- *
136
- * **자격은 부르는 사람이 준다.** 이 서버가 저쪽 자격을 들고 있으면 그 통로가 상시로 열려 있게 되고,
137
- * 그것은 옮기는 일에 필요한 것보다 넓다.
138
- */
139
- @Directive('@privilege(category: "twin", privilege: "mutation", domainOwnerGranted: true, superUserGranted: true)')
140
- @Mutation(returns => ScalarObject, {
141
- description:
142
- 'Pull connection declarations from another deployment and apply them here. The caller supplies that deployment’s URL and token — this server does not keep them.'
143
- })
144
- async pullTwinConnectionsFrom(
145
- @Arg('endpoint', { description: 'That deployment’s GraphQL endpoint.' }) endpoint: string,
146
- @Ctx() context: ResolverContext,
147
- @Arg('token', { nullable: true, description: 'Bearer token for that deployment. Not stored.' }) token?: string,
148
- @Arg('apply', { nullable: true }) apply?: boolean
149
- ): Promise<any> {
150
- let bundle: any
151
- try {
152
- const res = await fetch(endpoint, {
153
- method: 'POST',
154
- headers: { 'Content-Type': 'application/json', ...(token ? { Authorization: `Bearer ${token}` } : {}) },
155
- body: JSON.stringify({ query: '{ twinConnectionBundle }' })
156
- })
157
- const json: any = await res.json()
158
- /* 저쪽이 오류를 냈으면 그 말을 그대로 올린다 — 여기서 다시 지으면 원인이 흐려진다. */
159
- if (json?.errors?.length) return { ok: false, applied: 0, refused: 'remote-error', remote: json.errors.map((e: any) => e?.message) }
160
- bundle = json?.data?.twinConnectionBundle
161
- } catch (e: any) {
162
- return { ok: false, applied: 0, refused: 'unreachable', remote: [String(e?.message ?? e)] }
163
- }
164
- if (!bundle) return { ok: false, applied: 0, refused: 'empty-bundle' }
165
-
166
- return this.importTwinConnections(bundle, context, apply)
167
- }
168
- }
169
-
170
- /** 이 리졸버가 다루는 부딪힘 판정 — 화면이 같은 함수를 쓸 수 있게 다시 내보낸다. */
171
- export { intakeCollisions }
@@ -1,329 +0,0 @@
1
- /*
2
- * **연결을 내보내고 다시 들여옵니다** — 실제 셋업을 파일로 보관하고 되세웁니다.
3
- *
4
- * ── 이것은 데모가 아니라 제품 기능입니다 (2026-09-05) ───────────────────────
5
- * 데모 시드(`seed-demo-twins.ts`)와 **다른 것**입니다. 그쪽은 내장 가상 마스터로 보여 줄 트윈을
6
- * 만들고 `TWIN_PROVISION_DEMO` 뒤에 숨습니다. 이것은 **실제로 붙인 연결을 옮기는 길**이라
7
- * 데모 스위치 뒤에 두지 않습니다 — 백업·이관·복구가 데모일 수 없습니다.
8
- *
9
- * 그래서 특정 공장을 제품 코드에 적지 않습니다. 적으면 배포마다 남의 공장이 하나씩 따라가고,
10
- * 데모 시드 머리말이 적어 둔 「프로덕션 배포는 **트윈 0** 에서 시작한다」가 깨집니다. 무엇을 세울지는
11
- * **파일이 말하고 코드는 그 파일을 읽을 줄만 압니다.**
12
- *
13
- * ── 왜 필요했나 ────────────────────────────────────────────────────────────
14
- * 실 MES 두 공장을 트윈에 세웠는데(`mes-line-a` · `mes-miratek`) **그 셋업이 한 데이터베이스
15
- * 안에만 있었습니다.** 잃으면 다시 손으로 만들어야 하고, 다른 배포에 옮길 길도 없었습니다.
16
- *
17
- * ── 담는 것과 안 담는 것 ────────────────────────────────────────────────────
18
- *
19
- * 담는다 연결 선언 — 주소 · 도메인 · 사이트 · 치수 · 이름공간 · 받는 자리
20
- * 안 담는다 비밀값 — 토큰 · 웹훅 서명값 · 받는 쪽 비밀값
21
- * 안 담는다 사실(저널) — 원본이 다시 보내면 된다. 담으면 **가짜 과거가 배포마다 복제된다**
22
- * 안 담는다 마스터에서 온 트윈 모델 — 연결이 있으면 재동기화가 다시 만든다
23
- *
24
- * **비밀값은 이름만 적습니다.** 어느 칸이 채워져 있었는지는 알려 주되 값은 안 나갑니다 — 파일은
25
- * 주고받는 물건이고, 한 번 나간 비밀값은 되돌릴 수 없습니다. 들여올 때 「이 셋이 없다」고 말하고
26
- * 사람이 연결 화면에서 채웁니다.
27
- *
28
- * ── 왜 사실을 안 담나 ───────────────────────────────────────────────────────
29
- * 저널은 되풀이 읽히고 미러로 복제되고 구독으로 방송됩니다. 내보낸 사실을 다른 배포에 들여오면
30
- * **일어나지 않은 일이 그 공장의 지난 기록에 섭니다.** 커서만 옮겨 두면 원본이 그 뒤부터 다시
31
- * 보내 주므로, 사실은 원본에서 오는 것이 맞습니다.
32
- */
33
-
34
- /** 값이 아니라 **채워져 있었다는 사실**만 나가는 칸들. */
35
- export const SECRET_KEYS = ['token', 'hookSecret', 'intakeSecret', 'password', 'apiKey'] as const
36
-
37
- export interface PortableConnection {
38
- source: string
39
- system?: string
40
- siteName?: string
41
- adapterType: string
42
- /** 비밀값을 뺀 연결 설정. */
43
- config: Record<string, unknown>
44
- /** 내보낼 때 채워져 있던 비밀 칸 이름 — 값은 없습니다. */
45
- secretsRequired: string[]
46
- /** 이 연결이 무엇을 세우나. */
47
- scope?: unknown
48
- }
49
-
50
- export interface ConnectionBundle {
51
- kind: 'operato-twin/connections'
52
- version: 1
53
- exportedAt: string
54
- /** 어디서 내보냈나 — 사람이 나중에 「이게 어느 배포 것인가」를 물을 때 답합니다. */
55
- exportedFrom?: { domain?: string; note?: string }
56
- connections: PortableConnection[]
57
- }
58
-
59
- /**
60
- * 무엇을 세우나 **선언만** 남깁니다 — 지난 동기화의 **측정값은 뺍니다.**
61
- *
62
- * `scopeSpec` 에는 `produced`(선언) 옆에 `assessments`(그때 몇 개가 섰나) · `lastFailures` 가 함께
63
- * 삽니다. 그것까지 내보내면 새 배포가 **다른 기계에서 잰 수**를 자기 것으로 보입니다 — 「설비 42」가
64
- * 아무것도 안 세운 배포의 화면에 서고, 그 수가 어디서 왔는지 아무도 모릅니다.
65
- *
66
- * 재는 것은 그 배포에서 다시 재야 합니다. 오늘 「설정을 고쳤다」와 「모델이 다시 섰다」를 가른 것과
67
- * 같은 구별입니다.
68
- */
69
- function producedOf(scopeSpec: any): { produced: unknown } | undefined {
70
- const produced = scopeSpec?.produced
71
- return Array.isArray(produced) && produced.length ? { produced } : undefined
72
- }
73
-
74
- const isSecret = (key: string): boolean =>
75
- (SECRET_KEYS as readonly string[]).includes(key) || /secret|token|password|apikey/i.test(key)
76
-
77
- /**
78
- * 연결 행들을 옮길 수 있는 모양으로 — **비밀값을 빼고**.
79
- *
80
- * 빼는 판정을 이름으로 합니다. 목록으로만 하면 커넥터가 새 비밀 칸을 더할 때 그 값이 조용히
81
- * 파일로 나갑니다 — 이름 규칙을 함께 두어 **새 칸이 기본으로 안 나가게** 합니다.
82
- */
83
- export function exportConnections(
84
- rows: readonly { source: string; system?: string; siteName?: string; adapterType: string; connectionConfig?: any; scopeSpec?: any }[],
85
- meta: { at: string; domain?: string; note?: string }
86
- ): ConnectionBundle {
87
- return {
88
- kind: 'operato-twin/connections',
89
- version: 1,
90
- exportedAt: meta.at,
91
- ...(meta.domain || meta.note ? { exportedFrom: { ...(meta.domain ? { domain: meta.domain } : {}), ...(meta.note ? { note: meta.note } : {}) } } : {}),
92
- connections: rows.map(r => {
93
- const cfg = (r.connectionConfig ?? {}) as Record<string, unknown>
94
- const kept: Record<string, unknown> = {}
95
- const secrets: string[] = []
96
- for (const [k, v] of Object.entries(cfg)) {
97
- if (isSecret(k)) {
98
- /* 비어 있던 칸은 적지 않습니다 — 「채워야 한다」와 「이 커넥터에 그런 칸이 있다」는 다릅니다. */
99
- if (String(v ?? '').trim()) secrets.push(k)
100
- continue
101
- }
102
- kept[k] = v
103
- }
104
- return {
105
- source: r.source,
106
- ...(r.system ? { system: r.system } : {}),
107
- ...(r.siteName ? { siteName: r.siteName } : {}),
108
- adapterType: r.adapterType,
109
- config: kept,
110
- secretsRequired: secrets.sort(),
111
- ...(producedOf(r.scopeSpec) ? { scope: producedOf(r.scopeSpec) } : {})
112
- }
113
- })
114
- }
115
- }
116
-
117
- export type ImportAction =
118
- | { action: 'create'; connection: PortableConnection; missingSecrets: string[] }
119
- | { action: 'update'; connection: PortableConnection; missingSecrets: string[]; keptSecrets: string[] }
120
- | { action: 'skip'; source: string; why: string }
121
-
122
- export interface ImportPlan {
123
- actions: ImportAction[]
124
- /** 사람이 연결 화면에서 채워야 하는 것 — 이것이 비어야 「시드만으로 끝」입니다. */
125
- /**
126
- * 사람이 환경에 넣어야 하는 비밀값.
127
- *
128
- * `env` 를 함께 냅니다 — **그 이름을 사람이 손으로 적기 때문**입니다. 화면이 이름을 따로 만들면
129
- * 규칙이 두 곳에 살고, 갈리는 날 「넣었는데 안 읽힌다」가 됩니다. 실제로 났습니다(2026-09-06):
130
- * 화면이 `TWIN_SECRET__MES-MIRATEK__HOOKSECRET` 을 알려 줬는데 서버가 읽는 이름은
131
- * `TWIN_SECRET__MES_MIRATEK__HOOK_SECRET` 이었습니다. 하이픈과 낙타 경계 둘 다 달랐습니다.
132
- *
133
- * `keys` 와 순서가 같습니다 — `keys[i]` 의 이름이 `env[i]` 입니다.
134
- */
135
- needsHuman: { source: string; keys: string[]; env: string[] }[]
136
- /** 들여온 **뒤에** 두 연결이 같은 곳에 조치를 내게 되나 — 있으면 들여오면 안 됩니다. */
137
- collisions: { intake: string; sources: string[] }[]
138
- errors: string[]
139
- }
140
-
141
- /**
142
- * 들여오기 **계획**을 먼저 냅니다 — 쓰기 전에 무엇이 일어날지 사람이 봅니다.
143
- *
144
- * ── 이미 있는 연결을 덮지 않습니다 ──────────────────────────────────────────
145
- * 같은 이름이 이미 있으면 **선언만 갱신하고 비밀값은 그대로 둡니다.** 사람이 화면에서 넣은 값을
146
- * 파일이 지우면, 파일 하나 들여왔다가 도는 연결이 멈춥니다. 데모 시드도 같은 규율을 적어 두었습니다
147
- * (「옆 설정을 지우지 않는다 — 시드는 자기가 아는 것만 채운다」).
148
- *
149
- * ── 없는 비밀값을 지어내지 않습니다 ─────────────────────────────────────────
150
- * 빈 값으로 채워 두면 연결이 서고 **동기화할 때 처음 실패합니다.** 그때는 무엇이 없어서인지 화면에서
151
- * 멉니다. 여기서 이름을 대고 멈추는 것이 낫습니다.
152
- */
153
- export function planImport(
154
- bundle: unknown,
155
- existing: readonly { source: string; connectionConfig?: any }[],
156
- /** 지금 환경이 줄 수 있는 비밀값 이름들 — 값은 안 받습니다(이 함수는 값을 만지지 않습니다). */
157
- availableSecrets: (source: string, key: string) => boolean = () => false
158
- ): ImportPlan {
159
- const errors: string[] = []
160
- const b = bundle as ConnectionBundle
161
- if (!b || typeof b !== 'object') return { actions: [], needsHuman: [], collisions: [], errors: ['들여올 것이 객체가 아닙니다'] }
162
- if (b.kind !== 'operato-twin/connections') {
163
- return { actions: [], needsHuman: [], collisions: [], errors: [`이 파일은 연결 설정 파일가 아닙니다(kind: ${JSON.stringify((b as any).kind ?? null)})`] }
164
- }
165
- if (b.version !== 1) errors.push(`모르는 버전입니다(version: ${JSON.stringify((b as any).version)}) — 읽을 수 있는 것만 읽습니다`)
166
- if (!Array.isArray(b.connections)) return { actions: [], needsHuman: [], collisions: [], errors: [...errors, 'connections 가 목록이 아닙니다'] }
167
-
168
- const have = new Map(existing.map(e => [e.source, e]))
169
- const seen = new Set<string>()
170
- const actions: ImportAction[] = []
171
- const needsHuman: { source: string; keys: string[]; env: string[] }[] = []
172
-
173
- for (const c of b.connections) {
174
- const source = String(c?.source ?? '').trim()
175
- if (!source) {
176
- errors.push('이름 없는 연결이 있습니다 — 건너뜁니다')
177
- continue
178
- }
179
- if (seen.has(source)) {
180
- actions.push({ action: 'skip', source, why: '같은 이름이 파일에 두 번 있습니다' })
181
- continue
182
- }
183
- seen.add(source)
184
- if (!String(c?.adapterType ?? '').trim()) {
185
- actions.push({ action: 'skip', source, why: '커넥터 종류가 없습니다 — 무엇으로 붙일지 모릅니다' })
186
- continue
187
- }
188
-
189
- const cur = have.get(source)
190
- const curCfg = (cur?.connectionConfig ?? {}) as Record<string, unknown>
191
- const wanted = Array.isArray(c.secretsRequired) ? c.secretsRequired.map(String) : []
192
- const kept = wanted.filter(k => String(curCfg[k] ?? '').trim())
193
- const missing = wanted.filter(k => !kept.includes(k) && !availableSecrets(source, k))
194
-
195
- if (missing.length) needsHuman.push({ source, keys: missing, env: missing.map(k => secretEnvName(source, k)) })
196
- actions.push(
197
- cur
198
- ? { action: 'update', connection: c, missingSecrets: missing, keptSecrets: kept }
199
- : { action: 'create', connection: c, missingSecrets: missing }
200
- )
201
- }
202
-
203
- /*
204
- * **들여온 뒤의 모습으로 봅니다.** 파일 안에서만 보면 「이미 있는 연결과 부딪히는 것」을 못
205
- * 잡습니다 — 그게 실제로 더 쉽게 나는 쪽입니다(하나는 화면에서, 하나는 파일에서 생깁니다).
206
- */
207
- const after = [
208
- /* 행 모양 그대로 넘긴다 — 옮기는 자리는 `intakeCollisions` 안 한 곳이다(ADR-0053 ①). */
209
- ...existing.filter(e => !seen.has(e.source)),
210
- ...actions
211
- .filter((a): a is Extract<ImportAction, { connection: PortableConnection }> => a.action !== 'skip')
212
- .map(a => ({ source: a.connection.source, config: a.connection.config }))
213
- ]
214
- return { actions, needsHuman, collisions: intakeCollisions(after), errors }
215
- }
216
-
217
- /** 사람이 읽는 한 줄 — 화면과 로그가 같은 문장을 쓰게. */
218
- export function sayPlan(plan: ImportPlan): string {
219
- const n = (a: ImportAction['action']) => plan.actions.filter(x => x.action === a).length
220
- const head = `연결 ${plan.actions.length}개 — 새로 ${n('create')} · 갱신 ${n('update')} · 건너뜀 ${n('skip')}`
221
- /* 부딪힘을 먼저 말합니다 — 비밀값이 없는 것은 안 도는 것이고, 부딪히는 것은 **잘못 도는 것**입니다. */
222
- if (plan.collisions.length) {
223
- const who = plan.collisions.map(c => `${c.sources.join(' · ')} → ${c.intake}`).join(', ')
224
- return `${head}. ⚠ **두 연결이 같은 곳에 조치를 냅니다** — ${who}. 다른 공장의 작업지시가 한 공장에 섭니다`
225
- }
226
- if (!plan.needsHuman.length) return `${head}. 비밀값은 다 있습니다`
227
- const who = plan.needsHuman.map(h => `${h.source}(${h.keys.join(' · ')})`).join(', ')
228
- return `${head}. **비밀값을 사람이 넣어야 합니다** — ${who}. 연결 화면에서 채우면 그때부터 돕니다`
229
- }
230
-
231
- /**
232
- * 비밀값이 사는 **환경 변수 이름** — `mes-miratek` · `hookSecret` → `TWIN_SECRET__MES_MIRATEK__HOOK_SECRET`.
233
- *
234
- * 패키지에는 비밀 칸의 이름만 있고 값은 환경에서 옵니다. 그 이름을 **사람이 손으로 적으므로** 규칙이
235
- * 흔들리면 「넣었는데 안 읽힌다」가 됩니다 — 오늘 종일 잡은 「양쪽 다 초록인데 안 간다」의 설정 쪽
236
- * 버전입니다. 그래서 이 규칙을 한 곳에 두고 시험으로 못 박습니다.
237
- *
238
- * 낙타 이름의 경계를 밑줄로 가릅니다 — 안 그러면 `HOOKSECRET` 과 `HOOK_SECRET` 이 갈려 한쪽만 읽힙니다.
239
- */
240
- export function secretEnvName(source: string, key: string): string {
241
- const up = (x: string) =>
242
- x
243
- .replace(/([a-z0-9])([A-Z])/g, '$1_$2')
244
- .replace(/[^A-Za-z0-9]+/g, '_')
245
- .replace(/^_+|_+$/g, '')
246
- .toUpperCase()
247
- return `TWIN_SECRET__${up(source)}__${up(key)}`
248
- }
249
-
250
- /**
251
- * **두 연결이 같은 곳에 조치를 내지 않나** — 같으면 다른 공장의 작업지시가 한 공장에 섭니다.
252
- *
253
- * ── 왜 이 검사가 생겼나 (2026-09-05) ────────────────────────────────────────
254
- * `mes-miratek` 이 서면서 한 커넥터가 처음으로 **공장 둘**을 달았습니다. 조치가 나가는 주소는
255
- * `intakeSiteId` 가 정하는데(`operato-plant.ts:389`), 그 값이 트윈의 사이트 이름을 **이깁니다** —
256
- * 트윈에서는 `mes-line-a` 인데 MES 의 도메인은 `system` 이라 그 override 가 필요합니다.
257
- *
258
- * 그래서 **연결을 베껴 만들고 그 칸만 안 고치면** 두 트윈의 조치가 같은 공장으로 갑니다. 오류가
259
- * 안 납니다 — 저쪽은 정상적인 작업지시로 받고 **실제로 만듭니다.**
260
- *
261
- * 게이트에 이미 「사이트를 모르면 보내지 않는다」가 있는데(`no-site`), 그 이유가 적혀 있는 문장이
262
- * **「잘못된 공장이 실제로 만든다」** 입니다. 이것은 그 둘째 길입니다 — 사이트를 아는데 **틀린
263
- * 사이트를 압니다.**
264
- *
265
- * 파일로 연결을 만들 수 있게 되면서 더 쉬워졌습니다: 패키지를 베껴 이름만 바꾸면 받는 자리가
266
- * 그대로 따라옵니다.
267
- */
268
- export function intakeCollisions(
269
- connections: readonly {
270
- source: string
271
- adapterType?: string
272
- /** 패키지·계획이 쓰는 이름. */
273
- config?: Record<string, unknown> | null
274
- /** **DB 행이 쓰는 이름** — `TwinReference` 의 칸이다. 둘 다 받는다(ADR-0053 ①). */
275
- connectionConfig?: Record<string, unknown> | null
276
- }[]
277
- ): { intake: string; sources: string[] }[] {
278
- const byIntake = new Map<string, string[]>()
279
- for (const c of connections) {
280
- /*
281
- * **모양이 둘인 것을 여기서 안다** (ADR-0053 ①, 2026-09-16).
282
- *
283
- * 패키지는 `config`, `TwinReference` 행은 `connectionConfig` 다. 전에는 `config` 만 읽어서, 행을
284
- * 그대로 넘기면 같은 충돌에도 `[]` 를 냈다 — 오류도 경고도 없이 **저장이 통과하고 가드는 선 채로
285
- * 아무 일도 안 한다.** 부르는 쪽마다 옮기게 두면 다음 소비처가 또 밟는다(이 함수는 화면이 쓰라고
286
- * 리졸버에서 다시 내보내진다).
287
- *
288
- * 「없다」가 편한 답인 자리라 더 위험하다 — 빨강이 아니라 **조용한 초록**으로 흐른다.
289
- */
290
- const cfg = c.config ?? c.connectionConfig ?? {}
291
- /* 받는 자리를 정하는 규칙은 커넥터의 것과 같아야 합니다 — 여기서 다시 지으면 두 벌이 됩니다. */
292
- const intake = String(cfg.intakeSiteId ?? cfg.siteId ?? '').trim()
293
- /* 조치를 낼 수 없는 연결은 부딪힐 것이 없습니다 — 주소가 없으면 아무 데도 안 갑니다. */
294
- if (!intake || !String(cfg.intakeEndpoint ?? '').trim()) continue
295
- const key = `${String(cfg.intakeEndpoint).trim().replace(/\/$/, '')}::${intake}`
296
- byIntake.set(key, [...(byIntake.get(key) ?? []), c.source])
297
- }
298
- return [...byIntake.entries()]
299
- .filter(([, sources]) => sources.length > 1)
300
- .map(([intake, sources]) => ({ intake, sources: sources.sort() }))
301
- }
302
-
303
- /**
304
- * **화면 저장의 문** — 이 연결을 이 설정으로 저장하면 다른 연결과 받는 사이트가 겹치나 (ADR-0046 ②-1).
305
- *
306
- * 들여오기(`planImport`)는 `intakeCollisions` 로 거부하는데 화면 저장(`saveTwinReference`)은 부르지 않아서,
307
- * 연결을 베껴 이름만 바꾸면 경고 0 으로 두 트윈의 조치가 한 공장에 섰다. 판정은 같은 함수 하나로 한다.
308
- * 경고가 아니라 거부이고, 사람이 그 자리에서 고치도록 **이미 그 사이트를 쓰는 연결의 이름**을 싣는다.
309
- */
310
- export function refuseIntakeCollision(
311
- source: string,
312
- config: Record<string, unknown> | undefined,
313
- others: readonly { source: string; siteName?: string | null; connectionConfig?: Record<string, unknown> | null }[]
314
- ): { code: 'intake-site-taken'; message: string; params: { site: string; connection: string } } | null {
315
- const rest = others.filter(o => o.source !== source)
316
- /* `others` 는 DB 행이다 — 옮기지 않고 그대로 넘긴다(ADR-0053 ①). */
317
- const hit = intakeCollisions([...rest, { source, config: config ?? {} }]).find(c => c.sources.includes(source))
318
- if (!hit) return null
319
- const connection = rest
320
- .filter(o => hit.sources.includes(o.source))
321
- .map(o => o.siteName || o.source)
322
- .join(', ')
323
- const site = String(config?.intakeSiteId ?? config?.siteId ?? '').trim()
324
- return {
325
- code: 'intake-site-taken',
326
- message: `intake site ${site} is already used by ${connection}`,
327
- params: { site, connection }
328
- }
329
- }
@@ -1,62 +0,0 @@
1
- /*
2
- * **구동은 원본을 지난다** — 트윈을 우회하지 않는다 (ADR-0029 §8, 2026-08-19).
3
- *
4
- * ── 무엇이 뚫려 있었나 ──────────────────────────────────────────────────────
5
- * `controlTwinScenario` 가 `TwinEngine.runtime(...).scenario` 를 **직접** 잡았다. 그래서 시뮬만 할 수 있는
6
- * 일(자극·배속·정지)이 계약 밖의 지름길로 들어왔고, 실 시스템에는 그 자리가 아예 없었다 — ADR-0029 의
7
- * 판정 문장에 걸리는 상태였다.
8
- *
9
- * 이제 길은 하나다: **트윈 → 그 트윈의 원본 → 어댑터가 선언한 `control` 능력.** 선언하지 않은 원본에는
10
- * 문이 열리지 않고, 그 사실을 코드로 말한다(`not-declared`). 실 WMS 가 정지되지 않는 것은 결손이 아니다.
11
- *
12
- * ── 전송은 여기서 묻지 않는다 (§7) ──────────────────────────────────────────
13
- * 인프로세스 시뮬레이터는 같은 계약을 인프로세스로 이행한다. 나중에 그것이 다른 프로세스로 나가도 이 경로는
14
- * 바뀌지 않는다 — 「계약을 지나는가」만 보고 「네트워크를 타는가」는 묻지 않는다.
15
- */
16
- import { getRepository } from '@things-factory/shell'
17
-
18
- import { TwinInstance } from '../twin-instance/twin-instance.js'
19
- import { TwinReference } from './twin-reference.js'
20
- import { capabilitiesOf, getAdapter, type ControlCommand, type ControlResult, type SiteDescriptor } from './reference-adapter.js'
21
-
22
- /**
23
- * 이 트윈의 원본에 구동 명령을 보낸다 — **원본이 없거나 능력을 선언하지 않으면 거절한다.**
24
- *
25
- * 거절을 알리지 않고 하지 않는다: 사유 코드를 돌려주고 화면이 그것을 사람 말로 옮긴다. 「아무 일도 일어나지
26
- * 않았다」가 가장 비싼 답이다.
27
- */
28
- export async function routeControl(domainId: string, instanceId: string, command: ControlCommand): Promise<ControlResult> {
29
- const inst = await getRepository(TwinInstance).findOne({ where: { domain: { id: domainId }, instanceId } })
30
- if (!inst) return { ok: false, code: 'unknown-twin', detail: instanceId }
31
-
32
- const ref = await getRepository(TwinReference).findOne({ where: { domain: { id: domainId }, source: instanceId } })
33
- if (!ref) {
34
- /* 원본을 모르는 트윈 — 구동할 대상이 없다. 트윈을 직접 흔드는 길은 두지 않는다(그것이 지름길이었다). */
35
- return { ok: false, code: 'no-source', detail: `twin "${instanceId}" does not remember a source to drive` }
36
- }
37
-
38
- const adapter = getAdapter(ref.adapterType)
39
- if (!adapter) return { ok: false, code: 'no-adapter', detail: ref.adapterType }
40
- if (!capabilitiesOf(adapter).includes('control')) {
41
- return {
42
- ok: false,
43
- code: 'not-declared',
44
- detail: `source "${ref.source}" (${ref.adapterType}) does not declare the control capability — a real system is not paused or seeded, and that is a fact rather than a gap`
45
- }
46
- }
47
-
48
- /*
49
- * 사이트를 함께 넘긴다 — 원본 하나가 여러 사이트를 낼 수 있다(`discoverSites`). 우리가 아는 것은
50
- * 이 트윈이 어느 사이트에서 왔는지이고(`origin.siteId`), 모르면 넘기지 않는다(지어내지 않는다).
51
- */
52
- const siteId = (inst.origin as any)?.siteId
53
- const site: SiteDescriptor | undefined = siteId ? { siteId } : undefined
54
- return adapter.control!(ref.connectionConfig ?? {}, site, { ...command, instanceId, domainId })
55
- }
56
-
57
- /** 이 트윈의 원본이 선언한 능력 — 화면이 「무엇을 할 수 있나」를 먼저 알 수 있게. */
58
- export async function controlCapabilityOf(domainId: string, instanceId: string): Promise<{ source?: string; adapterType?: string; capabilities: string[] }> {
59
- const ref = await getRepository(TwinReference).findOne({ where: { domain: { id: domainId }, source: instanceId } })
60
- if (!ref) return { capabilities: [] }
61
- return { source: ref.source, adapterType: ref.adapterType, capabilities: capabilitiesOf(getAdapter(ref.adapterType)) }
62
- }
@@ -1,95 +0,0 @@
1
- /*
2
- * 사이트 발견 결과 — **"못 찾았다" 와 "없다" 를 구분한다.** 순수.
3
- *
4
- * ── 왜 있나 (2026-08-06) ────────────────────────────────────────────────────
5
- * 실 시스템을 연결해 "거기 있는 것" 으로 트윈을 만드는 경로에서, 발견이 실패하면 화면이
6
- * **"사이트를 찾지 못했습니다"** 라고만 말했다. 네 가지가 그 한 문장에 섞여 있었다.
7
- *
8
- * ① 그 레퍼런스가 등록돼 있지 않다 (`!ref → []`)
9
- * ② 어댑터 종류를 모른다 (`!adapter → []`)
10
- * ③ 연결·조회가 실패했다 (`catch { return [] }`)
11
- * ④ 정말로 사이트가 없다 (빈 배열)
12
- *
13
- * ①②③ 은 **고칠 수 있는 문제**이고 ④ 는 사실이다. 그런데 화면이 같은 말을 하므로 사용자는 자격이
14
- * 틀렸는지, 주소가 틀렸는지, 정말 공장이 없는지 알 수 없다 — 실 시스템 연결에서 가장 자주 막히는
15
- * 자리인데 거기서 아무 단서도 주지 않았다.
16
- *
17
- * ── 왜 문장이 아니라 코드인가 ───────────────────────────────────────────────
18
- * 이 프로젝트가 이미 쓰는 규약을 따른다(주의 신호·커맨드 거절·인제스트 경고와 같은 모양):
19
- * `code`(언어 중립) + `params`(문장에 끼울 값) + `message`(영어 canonical, 로그·폴백).
20
- * 화면은 `twin.connect.discover.<code>` 로 사람 말을 만든다.
21
- *
22
- * ── 원 시스템의 오류 문장을 감추지 않는다 ────────────────────────────────────
23
- * 어댑터가 준 사유(자격 거부·타임아웃·404)는 **그 시스템만 아는 사실**이라 우리가 다시 쓸 수 없다.
24
- * `detail` 로 그대로 실어 보내 화면이 보조 문장으로 보인다 — 원문이 있으면 사용자가 자기 관리자에게
25
- * 물어볼 수 있고, 없으면 우리 짐작만 남는다.
26
- */
27
-
28
- /** 발견이 왜 아무것도 주지 못했나 — 언어 중립 코드. */
29
- export type DiscoveryErrorCode = 'reference-not-found' | 'adapter-unknown' | 'discovery-failed' | 'discovery-timeout'
30
-
31
- export interface DiscoveryError {
32
- code: DiscoveryErrorCode
33
- params?: Record<string, string | number>
34
- /** 영어 canonical — 로그에 남고, 번역이 없으면 그대로 보인다. */
35
- message: string
36
- }
37
-
38
- export interface DiscoveryResult<S = unknown> {
39
- /** 발견된 사이트들. 실패했으면 빈 배열이고 `error` 가 이유를 말한다. */
40
- sites: S[]
41
- /** 실패 사유. **없으면 성공이다** — 빈 배열 + 사유 없음 = "연결은 됐지만 사이트가 없다". */
42
- error?: DiscoveryError
43
- }
44
-
45
- /** 등록된 레퍼런스가 없다 — 목록이 낡았거나 다른 테넌트의 것이다. */
46
- export const referenceNotFound = (source: string): DiscoveryError => ({
47
- code: 'reference-not-found',
48
- params: { source },
49
- message: `reference "${source}" is not registered in this tenant`
50
- })
51
-
52
- /** 어댑터 종류를 모른다 — 배포에 그 커넥터가 없다(패키지 미설치·오타). */
53
- export const adapterUnknown = (adapterType: string, source: string): DiscoveryError => ({
54
- code: 'adapter-unknown',
55
- params: { adapterType, source },
56
- message: `no connector of type "${adapterType}" is available for reference "${source}"`
57
- })
58
-
59
- /** 연결·조회가 실패했다 — 원 시스템이 준 사유를 그대로 싣는다. */
60
- export const discoveryFailed = (source: string, detail: string): DiscoveryError => ({
61
- code: 'discovery-failed',
62
- params: { source, detail },
63
- message: `could not read sites from "${source}": ${detail}`
64
- })
65
-
66
- /** 기다려도 답이 없었다 — 무한히 도는 화면보다 사유가 낫다. */
67
- export const discoveryTimedOut = (source: string, seconds: number): DiscoveryError => ({
68
- code: 'discovery-timeout',
69
- params: { source, seconds },
70
- message: `"${source}" did not answer within ${seconds}s`
71
- })
72
-
73
- /**
74
- * 어댑터가 준 것을 결과로 처리한다.
75
- *
76
- * **사이트가 없는 것을 실패로 만들지 않는다** — 연결이 되고 조회가 성공했는데 공장이 하나도 없는
77
- * 시스템은 있을 수 있다(권한이 좁거나 아직 등록되지 않은 경우). 그것은 사실이고, 화면은 "없다" 고
78
- * 말해야 한다. 실패로 합치면 사용자가 있지도 않은 문제를 찾는다.
79
- */
80
- export function discovered<S>(sites: readonly S[] | undefined | null): DiscoveryResult<S> {
81
- return { sites: Array.isArray(sites) ? [...sites] : [] }
82
- }
83
-
84
- /** 실패를 결과로 처리한다 — 사이트는 비어 있다(반쪽 목록을 성공처럼 보이게 하지 않는다). */
85
- export function failed<S>(error: DiscoveryError): DiscoveryResult<S> {
86
- return { sites: [], error }
87
- }
88
-
89
- /** 어댑터가 오류를 낸 것에서 사람이 읽을 사유를 뽑는다 — 형태를 가정하지 않는다. */
90
- export function detailOf(e: unknown): string {
91
- if (e instanceof Error && e.message) return e.message
92
- if (typeof e === 'string' && e) return e
93
- /* 무엇이 왔는지 모르면 그렇게 말한다 — 빈 문장으로 "이유 없음" 처럼 보이게 하지 않는다. */
94
- return 'the connector failed without a message'
95
- }