@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,315 +0,0 @@
1
- /*
2
- * ★ 구조 리비전 — **실 DB로 경계를 넘어 본다.**
3
- *
4
- * 커널의 마디 계산(`replaySegments`)는 그쪽 테스트가 지킨다. 여기서 지키는 것은 **호스트 배선**이다:
5
- * 리비전을 언제 새로 만드는가 · 이벤트에 번호를 찍는가 · 재생이 그 번호로 마디를 나누는가 ·
6
- * 컬럼이 생기기 전 행(번호 없음)을 어디에 붙이는가.
7
- *
8
- * 이 배선은 **경계를 넘어야만** 틀리는 것이 드러난다. 그런데 지금 앱의 트윈은 전부 리비전 1이라
9
- * 경계를 한 번도 안 넘어 봤다 — 오늘 하루에만 "테스트는 통과하는데 배선이 틀린" 경우를 셋 봤으므로,
10
- * 그 상태로 두지 않는다.
11
- *
12
- * 실 엔티티·실 네이밍 전략으로 만든 sqlite in-memory 스키마에 **빌드 산출물**을 그대로 실행한다.
13
- * 소스를 고친 뒤에는 `npm run build:server` 를 먼저 돌려야 반영된다.
14
- */
15
- import { test, before, after, beforeEach } from 'node:test'
16
- import assert from 'node:assert/strict'
17
- import { createRequire } from 'node:module'
18
- import type { DataSource } from 'typeorm'
19
-
20
- /*
21
- * **CJS 로 실어 온다.** `dist-server` 는 CJS 이고 그 안의 twin-engine 은 커널을 `require` 로 든다.
22
- * 그것을 ESM `import` 로 끌면 Node 의 ESM→CJS 번역기가 내부 단언에서 터진다(ERR_INTERNAL_ASSERTION).
23
- * 실 산출물을 그대로 시험하는 것이 이 스펙의 요점이므로, 부르는 쪽이 CJS 로 맞춘다.
24
- */
25
- const req = createRequire(import.meta.url)
26
- const { NamingStrategy, addDataSource, entities: shellEntities, Domain } = req('@things-factory/shell')
27
- const { entities: authEntities } = req('@things-factory/auth-base')
28
- const { entities: twinEntities } = req('../dist-server/service/index.js')
29
- const { TwinEngine } = req('../dist-server/engine/index.js')
30
- const { TwinEvent } = req('../dist-server/service/twin-event/twin-event.js')
31
- const { TwinStructure } = req('../dist-server/service/twin-structure/twin-structure.js')
32
- const { TwinInstance } = req('../dist-server/service/twin-instance/twin-instance.js')
33
- const { WmsKernel, EventJournal } = req('@operato/twin-kernel')
34
- const { DataSource: TypeOrmDataSource } = req('typeorm')
35
-
36
- let ds: DataSource
37
- let domainId: string
38
- const INSTANCE = 'struct-probe'
39
- const GTIN = 'urn:epc:idpat:sgtin:0614141.012345.*'
40
-
41
- /** 저장 자리 `bins` 개 + 지게차 `forklifts` 대 — 구조를 다이얼한다. */
42
- const boardWith = (bins: number, forklifts: number): any => ({
43
- spaceId: 'struct-space',
44
- /* 이 픽스처가 식별자를 만드는 이름공간 — **픽스처마다 다르게** 둔다(같은 접두 아래에서 서로 다른
45
- 시험의 번호가 부딪치면 표준의 유일성 요구를 깬다, CBV 2.0 §8.2.4). 커널은 선언 없이 팔레트·문서를
46
- 만들지 않는다(0.7.45 에서 날조 프리픽스를 지웠다). */
47
- identity: { namespaces: ['https://twin.test/structure-revision'] },
48
- utcOffsetMinutes: 0,
49
- locations: [
50
- { id: 'dock-recv', type: 'dock', capacity: 100 },
51
- { id: 'staging', type: 'staging', capacity: 100 },
52
- ...Array.from({ length: bins }, (_, i) => ({ id: `bin-${i + 1}`, type: 'storage', capacity: 20 }))
53
- ],
54
- equipment: Array.from({ length: forklifts }, (_, i) => ({ id: `fk-${i + 1}`, kind: 'forklift', homeLocation: 'dock-recv' }))
55
- })
56
-
57
- /** 커널을 실제로 굴려 봉투를 얻는다 — 손으로 지어낸 모양은 검증이 아니다. */
58
- function realEvents(model: any, ticks: number): any[] {
59
- const journal = new EventJournal()
60
- const k = new WmsKernel('struct')
61
- k.onEvent((e: any) => journal.append(e))
62
- k.loadTwinModel(model)
63
- k.scenario.load({
64
- seed: 11,
65
- generators: [{ kind: 'inbound-arrival', rate: { distribution: 'constant', meanPerHour: 120 }, content: { skuMix: [{ gtin: GTIN, weight: 1 }], qtyPerLine: { min: 1, max: 1 } } }]
66
- })
67
- k.scenario.start()
68
- for (let i = 0; i < ticks; i++) k.tick(1000)
69
- return journal.all() as any[]
70
- }
71
-
72
- before(async () => {
73
- ds = new TypeOrmDataSource({
74
- type: 'sqlite',
75
- database: ':memory:',
76
- synchronize: true,
77
- dropSchema: true,
78
- namingStrategy: new NamingStrategy(),
79
- entities: [...shellEntities, ...authEntities, ...twinEntities],
80
- logging: false
81
- })
82
- await ds.initialize()
83
- addDataSource('default', ds)
84
- const d = await ds.getRepository(Domain).save({ name: 'struct-test', subdomain: 'struct-test' } as any)
85
- domainId = d.id
86
- })
87
-
88
- after(async () => {
89
- await ds?.destroy()
90
- })
91
-
92
- beforeEach(async () => {
93
- for (const target of [TwinEvent, TwinStructure, TwinInstance]) await ds.query(`DELETE FROM "${ds.getMetadata(target).tableName}"`)
94
- /* 엔진의 리비전 캐시도 비운다 — 스펙 간에 남으면 다음 스펙이 옛 번호를 찍는다. */
95
- ;(TwinEngine as any).structureRevCache = {}
96
- /* 키가 `(domainId, instanceId)` 로 바뀌었다 — id 만으로 지우면 아무것도 지워지지 않는다. */
97
- delete (TwinEngine as any).recovered[`${domainId}:${INSTANCE}`]
98
- })
99
-
100
- test('같은 구조로 다시 프로비저닝하면 리비전이 늘지 않는다 — 부팅마다 늘면 이력이 잘게 쪼개진다', async () => {
101
- const model = boardWith(2, 1)
102
- await TwinEngine.provision(domainId, INSTANCE, 'wms', model, 'reset')
103
- await TwinEngine.provision(domainId, INSTANCE, 'wms', model, 'reset')
104
- await TwinEngine.provision(domainId, INSTANCE, 'wms', JSON.parse(JSON.stringify(model)), 'reset')
105
-
106
- const revs = await ds.getRepository(TwinStructure).find({ where: { domain: { id: domainId }, instanceId: INSTANCE } })
107
- assert.equal(revs.length, 1)
108
- assert.equal(revs[0].rev, 1)
109
- })
110
-
111
- test('구조가 바뀌면 새 리비전이 생기고 **저널은 살아남는다**', async () => {
112
- await TwinEngine.provision(domainId, INSTANCE, 'wms', boardWith(2, 1), 'reset')
113
- const first = realEvents(boardWith(2, 1), 300)
114
- assert.ok(first.length > 0, '이벤트가 있어야 "살아남는지" 를 물을 수 있다')
115
- await TwinEngine.persistBatch(domainId, INSTANCE, first, 0)
116
-
117
- await TwinEngine.provision(domainId, INSTANCE, 'wms', boardWith(6, 3), 'reset')
118
-
119
- const kept = await ds.getRepository(TwinEvent).count({ where: { domain: { id: domainId }, instanceId: INSTANCE } })
120
- assert.equal(kept, first.length, '구조가 바뀌었다고 이력을 지우면 안 된다 — 그것이 이 작업의 전부다')
121
-
122
- const revs = await ds.getRepository(TwinStructure).find({ where: { domain: { id: domainId }, instanceId: INSTANCE }, order: { rev: 'ASC' } })
123
- assert.deepEqual(revs.map(r => r.rev), [1, 2])
124
- assert.notEqual(revs[0].signature, revs[1].signature)
125
- })
126
-
127
- test('이벤트가 자기 구조 번호를 달고 저장된다 — 안 달면 나중에 어느 공장인지 알 길이 없다', async () => {
128
- await TwinEngine.provision(domainId, INSTANCE, 'wms', boardWith(2, 1), 'reset')
129
- await TwinEngine.persistBatch(domainId, INSTANCE, realEvents(boardWith(2, 1), 200), 0)
130
- await TwinEngine.provision(domainId, INSTANCE, 'wms', boardWith(6, 3), 'reset')
131
- const before = await ds.getRepository(TwinEvent).count({ where: { domain: { id: domainId }, instanceId: INSTANCE } })
132
- await TwinEngine.persistBatch(domainId, INSTANCE, realEvents(boardWith(6, 3), 200), before)
133
-
134
- const rows = await ds.getRepository(TwinEvent).find({ where: { domain: { id: domainId }, instanceId: INSTANCE } })
135
- const byRev = new Map<number | undefined, number>()
136
- for (const r of rows) byRev.set(r.structureRev, (byRev.get(r.structureRev) ?? 0) + 1)
137
- assert.deepEqual([...byRev.keys()].sort(), [1, 2], '두 구조에서 각각 쓰인 이벤트가 있어야 한다')
138
- assert.ok((byRev.get(1) ?? 0) > 0 && (byRev.get(2) ?? 0) > 0)
139
- })
140
-
141
- test('★ 재생이 경계를 넘는다 — 앞선 사실은 남고, 새 구조의 설비가 들어온다', async () => {
142
- await TwinEngine.provision(domainId, INSTANCE, 'wms', boardWith(2, 1), 'reset')
143
- const first = realEvents(boardWith(2, 1), 300)
144
- await TwinEngine.persistBatch(domainId, INSTANCE, first, 0)
145
-
146
- await TwinEngine.provision(domainId, INSTANCE, 'wms', boardWith(6, 3), 'reset')
147
- await TwinEngine.persistBatch(domainId, INSTANCE, realEvents(boardWith(6, 3), 300), first.length)
148
-
149
- const state = await TwinEngine.recover(domainId, INSTANCE)
150
- assert.ok(state.items.length > 0, '앞 마디의 물품이 사라지면 이력을 지운 것과 다를 바 없다')
151
- assert.equal(state.equipment.length, 3, '새 구조의 지게차 셋이 상태에 들어와야 한다')
152
- assert.equal(state.locations.filter((n: any) => n.type === 'storage').length, 6, '늘린 자리가 안 보이면 늘린 의미가 없다')
153
- })
154
-
155
- test('구조 번호가 없는 옛 행은 **가장 오래된 구조**로 처리한다 — 새 공장에 대고 계산하지 않는다', async () => {
156
- /*
157
- * 컬럼이 생기기 전에 쓰인 행은 `structureRev` 가 비어 있다. 그것을 현재 보드로 계산하면 그때 없던
158
- * 설비에서 일이 있었던 것처럼 보인다 — 이 규칙이 그 행들을 제자리에 붙인다.
159
- */
160
- await TwinEngine.provision(domainId, INSTANCE, 'wms', boardWith(2, 1), 'reset')
161
- const legacy = realEvents(boardWith(2, 1), 300)
162
- await TwinEngine.persistBatch(domainId, INSTANCE, legacy, 0)
163
- /* 컬럼이 없던 시절을 흉내 낸다 — 번호를 지운다. */
164
- await ds.query(`UPDATE "${ds.getMetadata(TwinEvent).tableName}" SET structure_rev = NULL`)
165
-
166
- await TwinEngine.provision(domainId, INSTANCE, 'wms', boardWith(6, 3), 'reset')
167
- const state = await TwinEngine.recover(domainId, INSTANCE)
168
- assert.ok(state.items.length > 0, '번호 없는 옛 행도 읽혀야 한다 — 알리지 않고 버리지 않는다')
169
- assert.equal(state.equipment.length, 3, '마지막 구조가 자원의 진실이다')
170
- })
171
-
172
- test('리비전이 하나뿐이면 예전과 똑같이 계산한다 — 구조 전환이 결과를 바꾸지 않는다', async () => {
173
- await TwinEngine.provision(domainId, INSTANCE, 'wms', boardWith(3, 2), 'reset')
174
- const events = realEvents(boardWith(3, 2), 300)
175
- await TwinEngine.persistBatch(domainId, INSTANCE, events, 0)
176
-
177
- const state = await TwinEngine.recover(domainId, INSTANCE)
178
- assert.equal(state.equipment.length, 2)
179
- assert.equal(state.locations.filter((n: any) => n.type === 'storage').length, 3)
180
- assert.ok(state.items.length > 0)
181
- })
182
-
183
- test('꺼진 트윈의 이력도 읽을 수 있다 — 저널은 트윈이 꺼져 있을 때 가장 필요하다', async () => {
184
- await TwinEngine.provision(domainId, INSTANCE, 'wms', boardWith(2, 1), 'reset')
185
- assert.equal(TwinEngine.owns(domainId, INSTANCE), false, '떠 있지 않다')
186
- assert.equal(await TwinEngine.ownsRegistered(domainId, INSTANCE), true, '그래도 이 테넌트 것이다')
187
- assert.equal(await TwinEngine.ownsRegistered(domainId, 'someone-elses-twin'), false)
188
- })
189
-
190
- test('시간여행은 **그때의 공장**을 보여준다 — 과거에 최신 구조를 얹으면 없던 설비가 선다', async () => {
191
- /*
192
- * "지금" 을 물으면 최신 구조를 얹는 것이 맞다(재프로비저닝 직후 늘린 설비가 보여야 하니까).
193
- * 그런데 그 규칙을 과거 조회에도 적용하면, 그 시점에 존재하지도 않던 설비가 화면에 선다 —
194
- * 이 작업이 막으려던 바로 그 거짓말이다. 규칙이 "지금" 에만 걸리는지 못박는다.
195
- */
196
- await TwinEngine.provision(domainId, INSTANCE, 'wms', boardWith(2, 1), 'reset')
197
- const first = realEvents(boardWith(2, 1), 300)
198
- await TwinEngine.persistBatch(domainId, INSTANCE, first, 0)
199
-
200
- await TwinEngine.provision(domainId, INSTANCE, 'wms', boardWith(6, 3), 'reset')
201
-
202
- const now = await TwinEngine.recover(domainId, INSTANCE)
203
- assert.equal(now.equipment.length, 3, '"지금" 은 늘린 설비를 보여준다')
204
-
205
- const past = await TwinEngine.recover(domainId, INSTANCE, first.length)
206
- assert.equal(past.equipment.length, 1, '그 시점에는 지게차가 하나뿐이었다')
207
- assert.equal(past.locations.filter((n: any) => n.type === 'storage').length, 2, '그때는 자리도 둘이었다')
208
- })
209
-
210
- test('구조 이력이 **무엇이 달라졌는지** 알린다 — 보드 두 개를 눈으로 비교할 수는 없다', async () => {
211
- await TwinEngine.provision(domainId, INSTANCE, 'wms', boardWith(2, 1), 'reset')
212
- const first = realEvents(boardWith(2, 1), 200)
213
- await TwinEngine.persistBatch(domainId, INSTANCE, first, 0)
214
- await TwinEngine.provision(domainId, INSTANCE, 'wms', boardWith(6, 3), 'reset', '도장 능력 보강')
215
- await TwinEngine.persistBatch(domainId, INSTANCE, realEvents(boardWith(6, 3), 100), first.length)
216
-
217
- const history = await TwinEngine.structureHistory(domainId, INSTANCE)
218
- assert.equal(history.length, 2)
219
-
220
- /* 최초 리비전은 비교 대상이 없다 — 전부 새로 생긴 것으로 센다. */
221
- assert.equal(history[0].rev, 1)
222
- assert.equal(history[0].diff.locationsAdded, 4, '자리 넷(도크·스테이징·빈 둘)')
223
- assert.ok(history[0].events > 0, '그 구조 아래 일어난 일의 수를 함께 말한다')
224
-
225
- const second = history[1]
226
- assert.equal(second.rev, 2)
227
- assert.equal(second.comment, '도장 능력 보강')
228
- assert.equal(second.diff.locationsAdded, 4, '저장 자리 넷이 늘었다')
229
- assert.equal(second.diff.equipmentAdded, 2, '지게차 둘이 늘었다')
230
- assert.deepEqual(
231
- second.diff.locationTypes.find(c => c.key === 'storage'),
232
- { key: 'storage', before: 2, after: 6, delta: 4 },
233
- '"저장 자리 2 → 6" 이라고 읽혀야 한다'
234
- )
235
- assert.deepEqual(second.diff.equipmentKinds.find(c => c.key === 'forklift'), { key: 'forklift', before: 1, after: 3, delta: 2 })
236
- })
237
-
238
- test('번호 없는 옛 행도 가장 오래된 리비전의 몫으로 센다 — 재생과 같은 규칙', async () => {
239
- await TwinEngine.provision(domainId, INSTANCE, 'wms', boardWith(2, 1), 'reset')
240
- const legacy = realEvents(boardWith(2, 1), 200)
241
- await TwinEngine.persistBatch(domainId, INSTANCE, legacy, 0)
242
- await ds.query(`UPDATE "${ds.getMetadata(TwinEvent).tableName}" SET structure_rev = NULL`)
243
- await TwinEngine.provision(domainId, INSTANCE, 'wms', boardWith(6, 3), 'reset')
244
-
245
- const history = await TwinEngine.structureHistory(domainId, INSTANCE)
246
- assert.equal(history[0].events, legacy.length, '재생이 rev 1 로 계산하는 행들을 세는 쪽만 빠뜨리면 두 화면이 다른 말을 한다')
247
- assert.equal(history[1].events, 0)
248
- })
249
-
250
- test('지문 표기가 낡았을 뿐이면 리비전을 만들지 않는다 — 계산 방식을 바꿔도 이력이 부풀지 않는다', async () => {
251
- /*
252
- * 실제로 당했다. 지문을 원문에서 해시로 바꾸자 저장된 문자열이 무엇과도 안 맞아, 아무것도 안
253
- * 바뀐 트윈들이 재기동 한 번에 전부 "구조가 바뀌었다" 로 기록됐다. 저장된 **보드**와 비교하면
254
- * 그런 일이 없다.
255
- */
256
- const model = boardWith(2, 1)
257
- await TwinEngine.provision(domainId, INSTANCE, 'wms', model, 'reset')
258
- const table = ds.getMetadata(TwinStructure).tableName
259
- await ds.query(`UPDATE "${table}" SET signature = 'old-style-signature-from-a-previous-algorithm'`)
260
-
261
- await TwinEngine.provision(domainId, INSTANCE, 'wms', model, 'reset')
262
-
263
- const revs = await ds.getRepository(TwinStructure).find({ where: { domain: { id: domainId }, instanceId: INSTANCE } })
264
- assert.equal(revs.length, 1, '공장은 그대로인데 리비전이 늘면 이력이 거짓으로 부푼다')
265
- assert.notEqual(revs[0].signature, 'old-style-signature-from-a-previous-algorithm', '표기는 새 방식으로 고쳐 둔다')
266
- })
267
-
268
- /*
269
- * ── 이어지는 현실(sim-world) — 재기동해도 이력이 끊기지 않는다 ────────────────
270
- *
271
- * 예전에는 sim-world 선언이 저널을 지우고 fresh 로 띄웠다. 실제로 없던 것은 **번호 이어 세기
272
- * 하나**였다: 상태 복원은 이미 저널 재생으로 하고 있었고, 막힌 것은 커널이 0부터 다시 세어 기존
273
- * 행과 겹치는 문제였다. 그 겹침은 오류를 내지 않고 알리지 않고 재생 순서를 뒤섞는다.
274
- */
275
- test('sim-world 로 다시 띄워도 저널이 살아남는다 — 선언이 지키는 약속', async () => {
276
- await TwinEngine.provision(domainId, INSTANCE, 'wms', boardWith(2, 1), 'reset')
277
- const first = realEvents(boardWith(2, 1), 300)
278
- await TwinEngine.persistBatch(domainId, INSTANCE, first, 0)
279
-
280
- const rt = await TwinEngine.bootDeclared(domainId, INSTANCE, 'resume')
281
- try {
282
- const kept = await ds.getRepository(TwinEvent).count({ where: { domain: { id: domainId }, instanceId: INSTANCE } })
283
- assert.equal(kept, first.length, 'sim-world 는 이어지는 현실이다 — 지우면 선언이 거짓이 된다')
284
- } finally {
285
- await TwinEngine.stop(domainId, INSTANCE)
286
- }
287
- })
288
-
289
- test('★ 재기동 후 새 이벤트는 마지막 번호 뒤에 온다 — 겹치면 재생 순서가 뒤섞인다', async () => {
290
- await TwinEngine.provision(domainId, INSTANCE, 'wms', boardWith(2, 1), 'reset')
291
- const first = realEvents(boardWith(2, 1), 300)
292
- await TwinEngine.persistBatch(domainId, INSTANCE, first, 0)
293
-
294
- const rt = await TwinEngine.bootDeclared(domainId, INSTANCE, 'resume')
295
- try {
296
- const kernel: any = rt.kernel
297
- assert.ok(kernel, '커널이 떠야 이어 세기를 물을 수 있다')
298
- assert.ok(
299
- kernel.getSnapshot().revision >= first.length,
300
- `이어 세지 않았다 — 저널 마지막 ${first.length}, 커널 ${kernel.getSnapshot().revision}`
301
- )
302
- } finally {
303
- await TwinEngine.stop(domainId, INSTANCE)
304
- }
305
- })
306
-
307
- test('저널이 비어 있으면 이어 셀 것이 없다 — 새로 시작하는 트윈에 영향 없음', async () => {
308
- await TwinEngine.provision(domainId, INSTANCE, 'wms', boardWith(2, 1), 'reset')
309
- const rt = await TwinEngine.bootDeclared(domainId, INSTANCE, 'resume')
310
- try {
311
- assert.equal((rt.kernel as any).getSnapshot().revision, 0)
312
- } finally {
313
- await TwinEngine.stop(domainId, INSTANCE)
314
- }
315
- })
@@ -1,148 +0,0 @@
1
- /*
2
- * ★ 화면이 이미 부르는 문에서 파생 테이블을 쓴다 — 그리고 못 답할 때는 물러난다.
3
- *
4
- * ── 무엇을 놓쳤던가 (2026-08-28) ────────────────────────────────────────────
5
- * 대상별 사건 목록을 만들고 8,382,017줄을 채웠는데, 360 화면은 예전부터 `twinEventList` 를 부르고
6
- * 있었다. 그래서 조회는 그대로 64초였다. **자리는 있고 길이 없었다** — 가장 자주 나는 결함이다.
7
- *
8
- * 이 시험이 지키는 것은 그 길이다.
9
- * ① 어떤 물음이 파생 테이블로 가는가 — 좁게, 그리고 그 판단이 순수한가
10
- * ② 못 답하는 물음에 억지로 답하지 않는가
11
- * ③ 답할 수 없는 구간을 「사건이 없다」로 그리게 만들지 않는가
12
- * ④ 화면이 부르는 자리에 실제로 붙어 있는가
13
- */
14
- import { test } from 'node:test'
15
- import assert from 'node:assert/strict'
16
- import { readFileSync } from 'node:fs'
17
- import { join } from 'node:path'
18
-
19
- import { readModelShape, AXIS_KIND } from '../server/service/twin-subject/subject-query-shape.js'
20
-
21
- const dir = (...p: string[]) => join(import.meta.dirname, '..', 'server', ...p)
22
- const fastPath = readFileSync(dir('service', 'twin-subject', 'twin-subject-fast-path.ts'), 'utf8')
23
- const journal = readFileSync(dir('service', 'twin-journal', 'twin-journal-query.ts'), 'utf8')
24
-
25
- /** 360 화면이 실제로 보내는 모양(§`loadTwinEventPage`) — 축 하나를 부분일치로, 시각 내림차순. */
26
- const panelParams = (axis: string, value: string, extra: any[] = []) => ({
27
- filters: [{ name: axis, operator: 'search', value }, ...extra],
28
- sortings: [{ name: 'eventTime', desc: true }]
29
- })
30
-
31
- const DESC = [{ name: 'eventTime', desc: true }]
32
-
33
- test('★ 360 화면이 보내는 네 축이 모두 파생 테이블로 간다', () => {
34
- /* 이것이 이 작업의 목적이다. 하나라도 빠지면 그 축의 이력은 여전히 64초다. */
35
- const expected: Record<string, string> = { epc: 'item', orderId: 'order', locationId: 'location', moverId: 'equipment' }
36
- for (const [axis, kind] of Object.entries(expected)) {
37
- const shape = readModelShape(panelParams(axis, 'V-1'), DESC)
38
- assert.ok(shape, `${axis} 축이 파생 테이블로 가야 한다`)
39
- assert.equal(shape!.kind, kind)
40
- assert.equal(shape!.value, 'V-1')
41
- }
42
- assert.deepEqual(Object.keys(AXIS_KIND).sort(), ['epc', 'locationId', 'moverId', 'orderId'])
43
- })
44
-
45
- test('시각 상한(시간여행)이 함께 와도 간다 — 화면이 과거를 볼 때 보내는 모양', () => {
46
- const shape = readModelShape(panelParams('epc', 'ITEM-1', [{ name: 'eventTime', operator: 'lte', value: '2026-08-01T00:00:00.000Z' }]), DESC)
47
- assert.ok(shape)
48
- assert.equal(shape!.untilTime?.toISOString(), '2026-08-01T00:00:00.000Z')
49
- })
50
-
51
- test('구간으로 물으면 두 끝을 모두 읽는다', () => {
52
- const shape = readModelShape(
53
- panelParams('locationId', 'BUFFER-CK1', [
54
- { name: 'eventTime', operator: 'between', value: ['2026-08-01T00:00:00.000Z', '2026-08-02T00:00:00.000Z'] }
55
- ]),
56
- DESC
57
- )
58
- assert.ok(shape)
59
- assert.equal(shape!.fromTime?.toISOString(), '2026-08-01T00:00:00.000Z')
60
- assert.equal(shape!.untilTime?.toISOString(), '2026-08-02T00:00:00.000Z')
61
- })
62
-
63
- test('★ 맞지 않는 물음은 답하지 않고 저널로 보낸다 — 억지로 답하면 목록이 알리지 않고 달라진다', () => {
64
- const cases: [string, any, any[]][] = [
65
- ['자유 검색어', { ...panelParams('epc', 'ITEM-1'), search: '냉동' }, DESC],
66
- ['종류 필터', panelParams('epc', 'ITEM-1', [{ name: 'eventType', operator: 'in', value: ['task.status'] }]), DESC],
67
- ['상관 식별자', panelParams('epc', 'ITEM-1', [{ name: 'correlationId', operator: 'eq', value: 'C1' }]), DESC],
68
- ['축 둘', { filters: [{ name: 'epc', operator: 'eq', value: 'A' }, { name: 'orderId', operator: 'eq', value: 'B' }], sortings: DESC }, DESC],
69
- ['축 없음', { filters: [], sortings: DESC }, DESC],
70
- ['여러 값(in)', panelParams('epc', 'A').filters && { filters: [{ name: 'epc', operator: 'in', value: ['A', 'B'] }], sortings: DESC }, DESC],
71
- ['리비전 정렬', panelParams('epc', 'ITEM-1'), [{ name: 'revision', desc: true }]],
72
- ['오름차순 정렬', panelParams('epc', 'ITEM-1'), [{ name: 'eventTime', desc: false }]],
73
- ['정렬 둘', panelParams('epc', 'ITEM-1'), [{ name: 'eventTime', desc: true }, { name: 'revision', desc: true }]],
74
- ['시각 하한만', panelParams('epc', 'ITEM-1', [{ name: 'eventTime', operator: 'gte', value: '2026-08-01T00:00:00.000Z' }]), DESC],
75
- ['못 읽는 시각', panelParams('epc', 'ITEM-1', [{ name: 'eventTime', operator: 'lte', value: '어제' }]), DESC],
76
- ['빈 축 값', panelParams('epc', ' '), DESC]
77
- ]
78
- for (const [why, params, sortings] of cases) {
79
- assert.equal(readModelShape(params, sortings), null, `${why} 는 저널로 가야 한다`)
80
- }
81
- })
82
-
83
- test('★ 적지 못한 줄이 있으면 이 경로를 쓰지 않는다 — 그 창에는 구멍이 있다', () => {
84
- assert.match(
85
- fastPath,
86
- /subjectWriteFailures\(domainId, instanceId\) > 0\) return null/,
87
- '실패가 세어져 있으면 온전한 쪽(저널)으로 보내야 한다'
88
- )
89
- })
90
-
91
- test('★ 원시 값을 직접 날짜로 읽지 않는다 — 시간대가 어긋난다', () => {
92
- /*
93
- * sqlite 가 주는 값은 시간대가 없는 문자열이고, `new Date()` 로 읽으면 한국 시간으로 해석해 UTC 로
94
- * 9시간을 뺀다. 실측: 커서의 시각이 9시간 이르게 나가 이어 읽기가 구간을 건너뛰고 본 줄이 또 나왔다
95
- * (120건 중 15건). 그래서 경계는 엔티티로 받고, 커서의 시각은 저널 줄에서 가져온다.
96
- */
97
- /* 경계는 엔티티로 받는다 — 숫자(리비전)는 원시 값이어도 뜻이 변하지 않으므로 날짜에만 걸린다. */
98
- assert.doesNotMatch(fastPath, /getRawOne<\{ at:/, '날짜를 원시 값으로 받던 자리가 남아 있다')
99
- assert.match(fastPath, /sub\?\.eventTime instanceof Date/)
100
- assert.match(fastPath, /journal\?\.eventTime instanceof Date/)
101
- assert.match(fastPath, /row\?\.eventTime instanceof Date/, '커서의 시각은 저널 줄의 날짜여야 한다')
102
- })
103
-
104
- test('★ 담지 못한 구간을 「사건이 없다」로 그리게 만들지 않는다', () => {
105
- /* 경계 아래를 물으면 물러난다. */
106
- assert.match(fastPath, /cursor\.at\.getTime\(\) <= cov\.from\.getTime\(\)\) return null/)
107
- assert.match(fastPath, /shape\.untilTime\.getTime\(\) < cov\.from\.getTime\(\)\) return null/)
108
- /*
109
- * 이 대상의 줄을 다 냈을 때가 가장 위험한 자리다. 커서를 내지 않으면 화면은 「여기서 끝」으로 읽고,
110
- * 경계 아래에 저널만 아는 사건이 있으면 그것이 거짓이 된다.
111
- */
112
- assert.match(fastPath, /boundaryCursor\(cov\.from\)/, '짧게 끝난 페이지는 경계 아래를 읽을 커서를 내야 한다')
113
- assert.match(fastPath, /function boundaryCursor/, '그 커서를 만드는 곳이 있어야 한다')
114
- /* 저널의 처음까지 덮을 때만 끝이다. */
115
- assert.match(fastPath, /cov\.full\s*\n?\s*\?\s*undefined/, '덮고 있을 때만 커서를 내지 않는다')
116
- })
117
-
118
- test('★ 전체 건수와 「더 있나」를 함께 정직하게 낸다 — 화면은 건수를 모를 때 capped 로 판단한다', () => {
119
- /*
120
- * 화면의 `hasMore` 는 전체 건수를 모르면 `capped` 를 본다. 그래서 「모른다」면서 `capped` 를 내리면
121
- * 목록이 알리지 않고 끝난 것처럼 보인다. 두 칸이 한 판단에서 나와야 한다.
122
- */
123
- assert.match(fastPath, /const capped = total === undefined/)
124
- assert.match(fastPath, /if \(page\.fullyCovered\)/, '덮고 있을 때만 정확한 건수를 낸다')
125
- })
126
-
127
- test('★ 화면이 부르는 자리에 붙어 있다 — 새 문을 열어 두고 기다리지 않는다', () => {
128
- /* 이 한 줄이 없으면 테이블은 채워지고도 쓰이지 않는다(2026-08-27 에 실제로 그랬다). */
129
- assert.match(journal, /const fast = await readModelPage\(domain\.id, instanceIds\[0\], effective, limit\)/)
130
- assert.match(journal, /if \(fast\) return fast/)
131
- /* 붙는 자리가 저널 질의를 만들기 **전**이어야 한다 — 뒤에 두면 느린 질의를 이미 치른 뒤다. */
132
- assert.ok(
133
- journal.indexOf('readModelPage(domain.id') < journal.indexOf('const qb = getQueryBuilderFromListParams'),
134
- '저널 질의를 만들기 전에 물어야 한다'
135
- )
136
- /* 트윈 하나일 때만 — 여러 트윈을 합친 목록은 이 테이블의 물음이 아니다. */
137
- assert.match(journal, /if \(instanceIds\.length === 1\) \{\s*\n\s*const fast = await readModelPage/)
138
- })
139
-
140
- test('품목만 식별자를 되돌린다 — 적는 쪽과 같은 함수로', () => {
141
- /*
142
- * 화면은 짧게 줄인 품목 식별자를 들고 있고(`net/class/product-M1`) 저장된 것은 전체 식별자다.
143
- * 되돌림을 두 벌로 적으면 어느 날 서로 다른 답을 내고, 그때 이력이 빈다.
144
- */
145
- assert.match(fastPath, /if \(kind !== 'item'\) return value/)
146
- assert.match(fastPath, /itemResolverFor\(domainId, instanceId, det\.model\)/)
147
- assert.match(fastPath, /if \(!itemOf\) return null/, '선언을 모르면 짐작하지 않고 물러난다')
148
- })
@@ -1,175 +0,0 @@
1
- /*
2
- * ★ 조회용 파생 표의 규율 — 앞으로만 채우고, 담지 않은 구간을 말한다.
3
- *
4
- * ── 왜 이 표를 만들었나 (2026-08-27 실측) ───────────────────────────────────
5
- * 360 화면의 「이 대상의 이력」이 64초다. 「무엇으로 걸러 시각순으로 정렬해 앞에서 30개」라는 모양에
6
- * 맞는 목차가 저널에 없고, 얹어도 풀리지 않는다.
7
- *
8
- * 여러 값으로 거르면(IN) 정렬 때문에 목차를 못 쓴다 — 실측한 질의 계획
9
- * 축이 늘 때마다 저널의 목차가 늘어난다 — 360 의 축 넷이면 13개
10
- * 품목과 로트가 한 문자열에 붙어 있어 품목만으로는 찾을 수 없다 — 정확일치가 0건
11
- *
12
- * ── 이 테스트가 무엇을 지키나 ───────────────────────────────────────────────
13
- * DB 를 띄우지 않는다. 지켜야 할 것은 **규율**이고 그것은 순수하게 확인할 수 있다 — 목차가 조회 모양과
14
- * 같은 순서인가, 소급 채우기를 하지 않는가, 담지 않은 구간을 말하는가, 저널이 먼저인가.
15
- *
16
- * 설계: `operato-twin/design/plans/read-models.md`
17
- */
18
- import { test } from 'node:test'
19
- import assert from 'node:assert/strict'
20
- import { readFileSync } from 'node:fs'
21
- import { join } from 'node:path'
22
-
23
- const dir = (...p: string[]) => join(import.meta.dirname, '..', 'server', ...p)
24
- const entity = readFileSync(dir('service', 'twin-subject', 'twin-subject-event.ts'), 'utf8')
25
- const writer = readFileSync(dir('service', 'twin-subject', 'twin-subject-writer.ts'), 'utf8')
26
- const query = readFileSync(dir('service', 'twin-subject', 'twin-subject-query.ts'), 'utf8')
27
- const resolver = readFileSync(dir('service', 'twin-subject', 'item-resolver.ts'), 'utf8')
28
- const engine = readFileSync(dir('engine', 'twin-engine.ts'), 'utf8')
29
-
30
- test('★ 목차 순서가 조회 모양과 같다 — 이 순서가 이 표의 존재 이유다', () => {
31
- /*
32
- * 「이 트윈의 이 대상의 것을 최신순으로 30개」가 빠르려면 목차가 거르는 값 다음에 시각이어야 한다.
33
- * 순서가 바뀌면 다시 정렬하게 되고, 그것이 저널에서 64초가 된 바로 그 원인이다.
34
- */
35
- assert.match(
36
- entity,
37
- /ix_twin_subject_event_0'[\s\S]{0,160}?\[e\.domain, e\.instanceId, e\.subjectKind, e\.subjectId, e\.eventTime\]/,
38
- '(도메인, 트윈, 종류, 식별자, 시각) 순서여야 한다'
39
- )
40
- /* 보관 상한을 지우는 축도 시각이 마지막이어야 한다(경계를 시각으로 찾으므로). */
41
- assert.match(
42
- entity,
43
- /ix_twin_subject_event_1'[\s\S]{0,160}?\[e\.domain, e\.instanceId, e\.eventTime\]/,
44
- '(도메인, 트윈, 시각) 목차가 있어야 한다'
45
- )
46
- })
47
-
48
- test('★ 시각 컬럼에 드라이버 방언을 적지 않는다 — datetime 은 Postgres 가 거절한다', () => {
49
- const at = entity.indexOf('eventTime: Date')
50
- assert.ok(at > 0, 'eventTime 이 있어야 한다')
51
- const decl = entity.slice(Math.max(0, at - 400), at)
52
- assert.doesNotMatch(decl, /type: 'datetime'/, 'sqlite·MySQL 의 이름을 적지 않는다')
53
- assert.doesNotMatch(decl, /type: 'timestamp'/, 'Postgres 의 이름도 적지 않는다')
54
- })
55
-
56
- test('★ 저널이 먼저다 — 이 표에 적다가 실패해도 저널 쓰기를 되돌리지 않는다', () => {
57
- /* 사실의 원본은 저널이고 이 표는 다시 만들 수 있다. 반대로 두면 조회를 빠르게 하려고 만든 표가
58
- 사실을 잃는 원인이 된다. */
59
- assert.match(writer, /catch \(err: any\)[\s\S]{0,200}?noteFailure/, '실패를 잡아 세어야 한다')
60
- assert.match(writer, /return 0\s*\n\s*\}/, '실패해도 던지지 않는다')
61
- })
62
-
63
- test('★ 알리지 않고 비지 않는다 — 적지 못한 줄 수를 조회가 함께 낸다', () => {
64
- /* 알리지 않고 비면 화면이 「그 대상에는 사건이 없다」고 말하게 된다. 그것이 이 부류에서 가장 나쁜 결함이다. */
65
- assert.match(writer, /export function subjectWriteFailures/, '적지 못한 수를 물을 수 있어야 한다')
66
- assert.match(query, /writeFailures: subjectWriteFailures\(/, '조회가 그 수를 실어 보낸다')
67
- assert.match(query, /writeFailures: number/, '응답에 그 칸이 있어야 한다')
68
- })
69
-
70
- test('★ 담지 않은 구간을 말한다 — 앞으로만 채우므로 그 앞은 「없다」가 아니다', () => {
71
- assert.match(query, /coveredFrom\?: Date/, '경계를 낼 칸이 있어야 한다')
72
- assert.match(query, /orderBy\('s\.eventTime', 'ASC'\)[\s\S]{0,120}?limit\(1\)/, '가장 오래된 줄 하나로 구한다')
73
- /* 저장하지 않고 조회할 때 구한다 — 저장한 값은 실제 자료와 어긋날 수 있다. */
74
- assert.doesNotMatch(entity, /coveredFrom/, '경계를 표에 저장하지 않는다')
75
- })
76
-
77
- test('★ 종류가 닫힌 목록이고, 모르는 값은 빈 목록이 아니라 오류다', () => {
78
- /* 빈 목록으로 답하면 오타가 「그 대상에는 사건이 없다」로 보인다. */
79
- assert.match(query, /SUBJECT_KINDS as readonly string\[\]\)\.includes\(kind\)/, '닫힌 목록으로 검사한다')
80
- assert.match(query, /throw new Error\(`unknown subjectKind/, '모르는 종류는 오류를 낸다')
81
- assert.match(query, /subjectId is required/, '빈 식별자로 트윈 전체를 읽지 않는다')
82
- })
83
-
84
- test('★ 페이지를 시각으로 넘긴다 — 머리에 쌓이는 목록에서 건너뛰기는 본 줄을 다시 낸다', () => {
85
- assert.match(query, /eventTime < :before/, '시각 커서로 넘긴다')
86
- assert.doesNotMatch(query, /\.skip\(/, '건너뛰기(offset)를 쓰지 않는다')
87
- })
88
-
89
- test('★ 품목은 선언과 견주어 판정한다 — 문자열을 자르지 않는다', () => {
90
- /*
91
- * 어느 부호가 로트를 가리키는지는 연결된 시스템마다 다르고 커널 계약이 정한 적이 없다. 한 시스템의
92
- * 관습(`-lot-`)을 규칙으로 굳히면 이 코드가 그 시스템 위에 서게 된다.
93
- */
94
- assert.match(resolver, /resolveByDeclaration\(itemRefOf\(epc\)/, '선언과 견주어 찾는다')
95
- /* 주석은 그 부호를 설명으로 쓴다 — 코드에 있는지만 본다(주석 줄을 걷어 내고 검사한다). */
96
- const code = resolver
97
- .split('\n')
98
- .filter(l => !/^\s*(\*|\/\*|\/\/)/.test(l))
99
- .join('\n')
100
- assert.doesNotMatch(code, /'-lot-'|"-lot-"|split\('-'\)|indexOf\('-lot/, '문자열을 자르는 규칙을 넣지 않는다')
101
- /* 사건마다 부르므로 기억해 둔다. 선언이 바뀌면 세대가 달라져 다시 판정한다. */
102
- assert.match(resolver, /generation !== generation|entry\.generation !== generation/, '선언 세대를 본다')
103
- })
104
-
105
- test('★ 저널을 쓰는 두 문이 모두 이 표에 적는다 — 한쪽만 적으면 그만큼이 화면에서 빈다', () => {
106
- const carried = engine.indexOf('static async persistCarried')
107
- const batch = engine.indexOf('static async persistBatch')
108
- assert.ok(carried > 0 && batch > 0, '두 문이 있어야 한다')
109
- assert.match(engine.slice(carried, carried + 900), /this\.writeSubjects\(/, 'persistCarried 가 적는다')
110
- assert.match(engine.slice(batch, batch + 900), /this\.writeSubjects\(/, 'persistBatch 가 적는다')
111
- })
112
-
113
- test('★ 소급해서 채우지 않는다 — 마이그레이션이 표만 만든다', () => {
114
- const migration = readFileSync(dir('migrations', '1786500000000-CreateTwinSubjectEvents.ts'), 'utf8')
115
- assert.match(migration, /createTable/, '표를 만든다')
116
- assert.match(migration, /createIndex/, '목차를 만든다')
117
- /* 채우는 코드가 있으면 새 조회 축마다 3,128만 줄을 다시 만드는 구조가 된다. */
118
- assert.doesNotMatch(migration, /INSERT|insert\(|from twin_events|TwinEvent/i, '기존 사건을 옮기지 않는다')
119
- const list = readFileSync(dir('migrations', 'index.ts'), 'utf8')
120
- assert.match(list, /CreateTwinSubjectEvents1786500000000/, '마이그레이션 목록에 배선돼 있어야 한다')
121
- })
122
-
123
- test('★ 이 표의 보관 정리는 체크포인트 상한을 보지 않는다 — 잃을 사실이 없다', () => {
124
- /*
125
- * 저널의 정리는 체크포인트가 반영하지 못한 사건을 지우지 않는다(그것을 지우면 사실이 사라진다).
126
- * 이 표는 사건에서 파생된 값이라 지워도 사실이 사라지지 않는다 — 그 구간의 조회가 느려질 뿐이다.
127
- */
128
- assert.match(writer, /export async function pruneSubjectRows/, '정리 함수가 있어야 한다')
129
- assert.doesNotMatch(writer, /revision <= :upTo/, '체크포인트 상한을 보지 않는다')
130
- assert.match(writer, /orderBy\('s\.eventTime', 'ASC'\)/, '경계를 시각으로 찾는다 — 목차를 쓰려면 그래야 한다')
131
- /* 저널 정리와 같은 창에서 돈다 — 두 표가 같은 보관 기간을 본다. */
132
- assert.match(engine, /pruneSubjectRows\(/, '저널 정리 창에서 함께 부른다')
133
- })
134
-
135
- test('★ 어디까지 옮겼는지 이 프로세스가 든다 — 파생 테이블의 내용으로 알아내지 않는다', () => {
136
- /*
137
- * 저장하지 않고 파생 테이블의 내용으로 진행 지점을 알아내려 했고 세 번 틀렸다.
138
- *
139
- * ① 최대 리비전 뒤부터 실시간으로 채워진 줄의 최댓값이 곧 저널의 끝이라 옮길 구간을 다 건너뛴다
140
- * ② 최소 리비전 앞까지 1부터 적으면 최솟값이 1이 되어 구간이 0으로 접힌다
141
- * ③ 최소 시각 아래로 사건이 시각순으로 오지 않는 트윈에서 곧바로 끝난다
142
- *
143
- * 채워진 것은 구간이 아니라 집합이다. 어느 축으로도 하나의 경계로 말할 수 없다.
144
- *
145
- * 그래서 알아내려 하지 않고 든다. **테이블에 적지 않는다**(사용자 판단) — 사건이 들어올 때 파생
146
- * 테이블도 같이 적히므로, 옛 사건을 옮기는 일은 새 파생 테이블을 만들 때와 계산 규칙을 고쳤을 때뿐이다.
147
- * 대가는 서버가 내려가면 그 몫을 다시 읽는 것이다.
148
- */
149
- const backfill = readFileSync(dir('service', 'twin-subject', 'twin-subject-backfill.ts'), 'utf8')
150
- assert.match(backfill, /const inFlight = new Map</, '이 프로세스가 든다')
151
- assert.doesNotMatch(backfill, /TwinProjectionProgress|progressRepo/, '테이블에 적지 않는다')
152
- assert.doesNotMatch(backfill, /MIN\(s\.revision\)|MAX\(s\.revision\)|MIN\(s\.eventTime\)/, '파생 테이블의 내용으로 알아내지 않는다')
153
- /* 목표는 처음 부를 때 한 번 정한다 — 매번 다시 재면 새 사건이 들어와 끝나지 않는다. */
154
- assert.match(backfill, /targetRevision: Number\(tip\?\.max \?\? 0\)/, '목표를 한 번 정한다')
155
- /* 다 옮기면 들고 있지 않는다 — 남겨 두면 다음 호출이 옛 목표를 본다. */
156
- assert.match(backfill, /if \(complete\) inFlight\.delete\(key\)/, '끝나면 버린다')
157
- })
158
-
159
- test('★ 목록의 순서가 같은 시각에서도 정해진다 — 페이지가 본 줄을 다시 내지 않게', () => {
160
- assert.match(query, /addOrderBy\('s\.revision', 'DESC'\)/, '같은 시각은 리비전으로 가른다')
161
- })
162
-
163
- test('★ 선언한 엔티티를 등록한다 — 선언만 하면 테이블이 만들어지지 않는다', () => {
164
- /*
165
- * 파생 테이블 엔티티를 만들고 `entities` 에 넣지 않은 적이 있다. 그러면 테이블이 만들어지지 않고,
166
- * 그것을 쓰는 코드가 「그런 테이블 없다」로 실패한다. 파일이 있는 것과 등록된 것은 다르다.
167
- */
168
- const index = readFileSync(dir('service', 'twin-subject', 'index.ts'), 'utf8')
169
- assert.match(index, /entities = \[TwinSubjectEvent\]/, '이 폴더의 엔티티가 등록돼 있어야 한다')
170
- const services = readFileSync(dir('service', 'index.ts'), 'utf8')
171
- assert.match(services, /\.\.\.TwinSubjectEntities/, '서비스 목록에도 실려야 한다')
172
- /* 마이그레이션의 테이블 이름은 TypeORM 이 만든 것과 글자 그대로 같아야 한다. */
173
- const migration = readFileSync(dir('migrations', '1786500000000-CreateTwinSubjectEvents.ts'), 'utf8')
174
- assert.match(migration, /const TABLE = 'twin_subject_events'/, '실제 테이블 이름을 쓴다')
175
- })