@things-factory/headless-twin 10.0.7 → 10.0.9
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.
- package/dist-server/engine/attention-digest.d.ts +52 -0
- package/dist-server/engine/attention-digest.js +76 -0
- package/dist-server/engine/attention-digest.js.map +1 -0
- package/dist-server/engine/board-vocabulary.d.ts +17 -0
- package/dist-server/engine/board-vocabulary.js +63 -0
- package/dist-server/engine/board-vocabulary.js.map +1 -0
- package/dist-server/engine/command-routing.d.ts +33 -0
- package/dist-server/engine/command-routing.js +53 -0
- package/dist-server/engine/command-routing.js.map +1 -0
- package/dist-server/engine/entity-delta.d.ts +13 -6
- package/dist-server/engine/entity-delta.js +38 -12
- package/dist-server/engine/entity-delta.js.map +1 -1
- package/dist-server/engine/index.d.ts +6 -0
- package/dist-server/engine/index.js +10 -0
- package/dist-server/engine/index.js.map +1 -1
- package/dist-server/engine/kpi-baseline.d.ts +78 -0
- package/dist-server/engine/kpi-baseline.js +123 -0
- package/dist-server/engine/kpi-baseline.js.map +1 -0
- package/dist-server/engine/kpi-broadcast.d.ts +4 -0
- package/dist-server/engine/kpi-broadcast.js +16 -0
- package/dist-server/engine/kpi-broadcast.js.map +1 -0
- package/dist-server/engine/kpi-query.d.ts +31 -0
- package/dist-server/engine/kpi-query.js +50 -2
- package/dist-server/engine/kpi-query.js.map +1 -1
- package/dist-server/engine/live-attentions.d.ts +1 -0
- package/dist-server/engine/live-attentions.js +7 -1
- package/dist-server/engine/live-attentions.js.map +1 -1
- package/dist-server/engine/runtime-key.d.ts +15 -0
- package/dist-server/engine/runtime-key.js +64 -0
- package/dist-server/engine/runtime-key.js.map +1 -0
- package/dist-server/engine/state-axes.d.ts +16 -0
- package/dist-server/engine/state-axes.js +54 -0
- package/dist-server/engine/state-axes.js.map +1 -0
- package/dist-server/engine/twin-engine.d.ts +92 -16
- package/dist-server/engine/twin-engine.js +335 -77
- package/dist-server/engine/twin-engine.js.map +1 -1
- package/dist-server/engine/twin-level.d.ts +23 -0
- package/dist-server/engine/twin-level.js +52 -0
- package/dist-server/engine/twin-level.js.map +1 -0
- package/dist-server/engine/warm-start.d.ts +58 -12
- package/dist-server/engine/warm-start.js +80 -9
- package/dist-server/engine/warm-start.js.map +1 -1
- package/dist-server/service/reference/discovery-result.d.ts +34 -0
- package/dist-server/service/reference/discovery-result.js +84 -0
- package/dist-server/service/reference/discovery-result.js.map +1 -0
- package/dist-server/service/reference/ingest-space.d.ts +30 -0
- package/dist-server/service/reference/ingest-space.js +63 -0
- package/dist-server/service/reference/ingest-space.js.map +1 -0
- package/dist-server/service/reference/knob-defaults.d.ts +20 -0
- package/dist-server/service/reference/knob-defaults.js +59 -0
- package/dist-server/service/reference/knob-defaults.js.map +1 -0
- package/dist-server/service/reference/reference-live.js +2 -2
- package/dist-server/service/reference/reference-live.js.map +1 -1
- package/dist-server/service/reference/reference-master.d.ts +37 -2
- package/dist-server/service/reference/reference-master.js +55 -5
- package/dist-server/service/reference/reference-master.js.map +1 -1
- package/dist-server/service/reference/reference-resolver.d.ts +2 -2
- package/dist-server/service/reference/reference-resolver.js +71 -15
- package/dist-server/service/reference/reference-resolver.js.map +1 -1
- package/dist-server/service/twin-attention/twin-attention-query.d.ts +8 -1
- package/dist-server/service/twin-attention/twin-attention-query.js +39 -8
- package/dist-server/service/twin-attention/twin-attention-query.js.map +1 -1
- package/dist-server/service/twin-control/twin-control-mutation.d.ts +2 -0
- package/dist-server/service/twin-control/twin-control-mutation.js +29 -10
- package/dist-server/service/twin-control/twin-control-mutation.js.map +1 -1
- package/dist-server/service/twin-forecast/twin-forecast-query.js +2 -1
- package/dist-server/service/twin-forecast/twin-forecast-query.js.map +1 -1
- package/dist-server/service/twin-instance/twin-instance.js +4 -2
- package/dist-server/service/twin-instance/twin-instance.js.map +1 -1
- package/dist-server/service/twin-journal/twin-journal-query.d.ts +6 -2
- package/dist-server/service/twin-journal/twin-journal-query.js +25 -7
- package/dist-server/service/twin-journal/twin-journal-query.js.map +1 -1
- package/dist-server/service/twin-lifecycle/twin-lifecycle-mutation.d.ts +8 -0
- package/dist-server/service/twin-lifecycle/twin-lifecycle-mutation.js +23 -1
- package/dist-server/service/twin-lifecycle/twin-lifecycle-mutation.js.map +1 -1
- package/dist-server/service/twin-metrics/twin-metrics-query.js +1 -1
- package/dist-server/service/twin-metrics/twin-metrics-query.js.map +1 -1
- package/dist-server/service/twin-space/twin-space-resolver.js +9 -0
- package/dist-server/service/twin-space/twin-space-resolver.js.map +1 -1
- package/dist-server/service/twin-state/twin-state-subscription.js +1 -1
- package/dist-server/service/twin-state/twin-state-subscription.js.map +1 -1
- package/dist-server/service/twin-structure/twin-structure.js +2 -1
- package/dist-server/service/twin-structure/twin-structure.js.map +1 -1
- package/dist-server/service/twin-target/twin-target-resolver.js +21 -4
- package/dist-server/service/twin-target/twin-target-resolver.js.map +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/package.json +6 -6
- package/server/engine/attention-digest.ts +102 -0
- package/server/engine/board-vocabulary.ts +61 -0
- package/server/engine/command-routing.ts +67 -0
- package/server/engine/entity-delta.ts +33 -11
- package/server/engine/index.ts +10 -0
- package/server/engine/kpi-baseline.ts +202 -0
- package/server/engine/kpi-broadcast.ts +13 -0
- package/server/engine/kpi-query.ts +81 -3
- package/server/engine/live-attentions.ts +7 -2
- package/server/engine/runtime-key.ts +58 -0
- package/server/engine/state-axes.ts +55 -0
- package/server/engine/twin-engine.ts +350 -77
- package/server/engine/twin-level.ts +48 -0
- package/server/engine/warm-start.ts +130 -16
- package/server/service/reference/discovery-result.ts +95 -0
- package/server/service/reference/ingest-space.ts +70 -0
- package/server/service/reference/knob-defaults.ts +59 -0
- package/server/service/reference/reference-live.ts +2 -2
- package/server/service/reference/reference-master.ts +94 -8
- package/server/service/reference/reference-resolver.ts +80 -16
- package/server/service/twin-attention/twin-attention-query.ts +43 -6
- package/server/service/twin-control/twin-control-mutation.ts +31 -12
- package/server/service/twin-forecast/twin-forecast-query.ts +3 -2
- package/server/service/twin-instance/twin-instance.ts +6 -2
- package/server/service/twin-journal/twin-journal-query.ts +35 -5
- package/server/service/twin-lifecycle/twin-lifecycle-mutation.ts +18 -2
- package/server/service/twin-metrics/twin-metrics-query.ts +1 -1
- package/server/service/twin-space/twin-space-resolver.ts +10 -1
- package/server/service/twin-state/twin-state-subscription.ts +1 -1
- package/server/service/twin-structure/twin-structure.ts +4 -1
- package/server/service/twin-target/twin-target-resolver.ts +22 -4
- package/test/attention-digest.test.ts +135 -0
- package/test/board-vocabulary.test.ts +114 -0
- package/test/capability-mapping.test.ts +4 -4
- package/test/command-routing.test.ts +61 -0
- package/test/discovery-result.test.ts +75 -0
- package/test/entity-delta.test.ts +25 -25
- package/test/ingest-bench.test.ts +3 -3
- package/test/ingest-space.test.ts +50 -0
- package/test/knob-defaults.test.ts +72 -0
- package/test/kpi-baseline-db.test.ts +214 -0
- package/test/kpi-baseline.test.ts +196 -0
- package/test/kpi-query-bench.test.ts +128 -0
- package/test/live-mirror-parity.test.ts +35 -2
- package/test/master-to-twin.test.ts +7 -3
- package/test/mutation-gate.test.ts +108 -0
- package/test/oee-accumulator.test.ts +65 -1
- package/test/registry-key-guard.test.ts +80 -0
- package/test/runtime-key.test.ts +66 -0
- package/test/scale-twin-bench.test.ts +2 -2
- package/test/state-axes.test.ts +74 -0
- package/test/streamline-e2e.test.ts +2 -2
- package/test/structure-revision-db.test.ts +5 -4
- package/test/tenant-registry-db.test.ts +149 -0
- package/test/warm-start-seam.test.ts +140 -0
- package/test/warm-start.test.ts +138 -4
package/test/warm-start.test.ts
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
import { test } from 'node:test'
|
|
9
9
|
import assert from 'node:assert/strict'
|
|
10
10
|
|
|
11
|
-
import { planWarmStart } from '../server/engine/warm-start.ts'
|
|
11
|
+
import { planWarmStart, unwrapState } from '../server/engine/warm-start.ts'
|
|
12
12
|
|
|
13
13
|
const STATE = {
|
|
14
14
|
locations: [{ id: 'dock-recv' }, { id: 'rack-1' }],
|
|
@@ -27,13 +27,82 @@ test('상태가 있으면 심는다 — 재고·노드·무버', () => {
|
|
|
27
27
|
assert.deepEqual(plan.seed.locations, STATE.locations)
|
|
28
28
|
})
|
|
29
29
|
|
|
30
|
-
test('오더는 심지 않는다 —
|
|
30
|
+
test('원값 없는 오더는 심지 않는다 — 남은 수량을 알 수 없어 지어내야 하기 때문', () => {
|
|
31
|
+
/* STATE 의 오더는 progress 만 들고 있다 — 커널 판정(requested !== undefined)과 같은 기준으로 뺀다. */
|
|
31
32
|
const plan = planWarmStart(STATE, undefined, true)
|
|
32
33
|
assert.equal(plan.action, 'hydrate')
|
|
33
34
|
if (plan.action !== 'hydrate') return
|
|
35
|
+
assert.equal(plan.seed.orders, undefined)
|
|
36
|
+
assert.equal(plan.orderCount, 0)
|
|
37
|
+
assert.equal(plan.ordersWithoutDemand, 1)
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
test('뺀 오더의 수를 센다 — "주문이 없다" 와 "주문을 못 심었다" 가 같아지면 안 된다', () => {
|
|
41
|
+
/*
|
|
42
|
+
* 이 숫자가 로그의 근거다. 세지 않고 조용히 빼면, 재기동 후 빈 주문 목록을 보고
|
|
43
|
+
* 사용자는 "원래 없었다" 로 읽는다. 실제로 그 화면이 오래 남아 있었다.
|
|
44
|
+
*/
|
|
45
|
+
const mixed = {
|
|
46
|
+
...STATE,
|
|
47
|
+
orders: [
|
|
48
|
+
{ id: 'SO-1', kind: 'outbound', status: 'created', progress: 0.5 },
|
|
49
|
+
{ id: 'SO-2', kind: 'outbound', status: 'created', requested: 100, fulfilled: 40 },
|
|
50
|
+
{ id: 'SO-3', kind: 'outbound', status: 'created', requested: 30 }
|
|
51
|
+
]
|
|
52
|
+
}
|
|
53
|
+
const plan = planWarmStart(mixed, undefined, true)
|
|
54
|
+
assert.equal(plan.action, 'hydrate')
|
|
55
|
+
if (plan.action !== 'hydrate') return
|
|
56
|
+
assert.equal(plan.orderCount, 2)
|
|
57
|
+
assert.equal(plan.ordersWithoutDemand, 1)
|
|
58
|
+
assert.deepEqual((plan.seed.orders as any[]).map(o => o.id), ['SO-2', 'SO-3'])
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
test('진행 중 주문을 심는다 — 재기동하면 주문이 사라졌던 결함의 회귀 방지', () => {
|
|
62
|
+
/*
|
|
63
|
+
* 예전에는 씨앗이 자리·물품·설비 셋만 넘겨, 재고는 맞는데 주문 목록이 비었다. 원값이 있는
|
|
64
|
+
* 오더는 반드시 씨앗에 실려야 한다 — 이 단언이 그 결함으로 되돌아가는 것을 막는다.
|
|
65
|
+
*/
|
|
66
|
+
const plan = planWarmStart(
|
|
67
|
+
{ ...STATE, orders: [{ id: 'SO-9', kind: 'outbound', status: 'created', requested: 50, fulfilled: 10, lines: [{ gtin: 'g1', requested: 50 }] }] },
|
|
68
|
+
undefined,
|
|
69
|
+
true
|
|
70
|
+
)
|
|
71
|
+
assert.equal(plan.action, 'hydrate')
|
|
72
|
+
if (plan.action !== 'hydrate') return
|
|
73
|
+
assert.equal(plan.orderCount, 1)
|
|
74
|
+
assert.equal(plan.ordersWithoutDemand, 0)
|
|
75
|
+
assert.equal((plan.seed.orders as any[])[0].requested, 50)
|
|
76
|
+
assert.equal((plan.seed.orders as any[])[0].fulfilled, 10)
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
test('작업·사람·자산도 함께 심는다 — 커널이 심을 수 있는 축을 호스트가 버리지 않는다', () => {
|
|
80
|
+
const plan = planWarmStart(
|
|
81
|
+
{ ...STATE, persons: [{ id: 'p1' }, { id: 'p2' }], assets: [{ id: 'pallet-1' }] },
|
|
82
|
+
undefined,
|
|
83
|
+
true
|
|
84
|
+
)
|
|
85
|
+
assert.equal(plan.action, 'hydrate')
|
|
86
|
+
if (plan.action !== 'hydrate') return
|
|
87
|
+
assert.equal(plan.taskCount, 1)
|
|
88
|
+
assert.equal(plan.personCount, 2)
|
|
89
|
+
assert.equal(plan.assetCount, 1)
|
|
90
|
+
assert.deepEqual(plan.seed.tasks, STATE.tasks)
|
|
91
|
+
assert.equal((plan.seed.persons as any[]).length, 2)
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
test('없는 축은 씨앗에 키를 만들지 않는다 — 빈 배열로 커널 판정을 흐리지 않는다', () => {
|
|
95
|
+
const plan = planWarmStart({ locations: [{ id: 'n1' }], items: [], equipment: [] }, undefined, true)
|
|
96
|
+
assert.equal(plan.action, 'hydrate')
|
|
97
|
+
if (plan.action !== 'hydrate') return
|
|
34
98
|
assert.deepEqual(Object.keys(plan.seed).sort(), ['equipment', 'items', 'locations'])
|
|
35
|
-
|
|
36
|
-
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
test('오더만 있어도 심는다 — 구조가 보드에서 오는 경우가 있다', () => {
|
|
102
|
+
const plan = planWarmStart({ orders: [{ id: 'SO-1', requested: 10 }] } as any, undefined, true)
|
|
103
|
+
assert.equal(plan.action, 'hydrate')
|
|
104
|
+
if (plan.action !== 'hydrate') return
|
|
105
|
+
assert.equal(plan.orderCount, 1)
|
|
37
106
|
})
|
|
38
107
|
|
|
39
108
|
test('벤치 트윈에는 심지 않는다 — 새 시작에서 용량을 재는 것이 목적이다', () => {
|
|
@@ -76,3 +145,68 @@ test('빠진 배열은 빈 배열로 다뤄지되 없는 것을 지어내지 않
|
|
|
76
145
|
assert.deepEqual(plan.seed.equipment, [])
|
|
77
146
|
assert.equal(plan.itemCount, 1)
|
|
78
147
|
})
|
|
148
|
+
|
|
149
|
+
test('확인해 둔 주목 신호를 이어받는다 — 재기동하면 다시 빨개지던 것', () => {
|
|
150
|
+
/*
|
|
151
|
+
* 확인 처리는 상태에서 파생되지 않는 유일한 축이다. 씨앗에서 빠지면 재기동 후 확인해 둔 신호가
|
|
152
|
+
* 전부 다시 활성으로 뜬다 — 오류는 나지 않고, 사용자는 "또 터졌다" 로 읽는다.
|
|
153
|
+
*/
|
|
154
|
+
const plan = planWarmStart({ ...STATE, acked: ['bottleneck:bin-1', 'breakdown:fk-1'] } as any, undefined, true)
|
|
155
|
+
assert.equal(plan.action, 'hydrate')
|
|
156
|
+
if (plan.action !== 'hydrate') return
|
|
157
|
+
assert.deepEqual(plan.seed.acked, ['bottleneck:bin-1', 'breakdown:fk-1'])
|
|
158
|
+
assert.equal(plan.ackedCount, 2)
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
test('확인 처리만 있어도 심는다 — 신호를 확인해 둔 사실 자체가 이어받을 값어치가 있다', () => {
|
|
162
|
+
const plan = planWarmStart({ acked: ['bottleneck:bin-1'] } as any, undefined, true)
|
|
163
|
+
assert.equal(plan.action, 'hydrate')
|
|
164
|
+
if (plan.action !== 'hydrate') return
|
|
165
|
+
assert.equal(plan.ackedCount, 1)
|
|
166
|
+
})
|
|
167
|
+
|
|
168
|
+
test('확인 처리가 문자열 목록이 아니면 없는 것으로 본다 — 지어내지 않는다', () => {
|
|
169
|
+
const plan = planWarmStart({ ...STATE, acked: 'nope' } as any, undefined, true)
|
|
170
|
+
assert.equal(plan.action, 'hydrate')
|
|
171
|
+
if (plan.action !== 'hydrate') return
|
|
172
|
+
assert.equal(plan.ackedCount, 0)
|
|
173
|
+
assert.equal(plan.seed.acked, undefined)
|
|
174
|
+
})
|
|
175
|
+
|
|
176
|
+
/*
|
|
177
|
+
* ── 겹포장 회귀 방지 (2026-08-05) ──────────────────────────────────────────
|
|
178
|
+
* 체크포인트가 `{revision, state:{revision, state:{실제}}}` 로 저장돼, 꺼낸 값에 축이 하나도 없었다.
|
|
179
|
+
* 판정은 "심을 것이 없다"(no-state)로 답하고 **조용히** 넘어갔다 — 저널에 수천 건이 있고 캐시도
|
|
180
|
+
* 최신인데 트윈이 빈 채로 떴다. 오류도 로그도 없어서 이 경로가 처음부터 죽어 있었다.
|
|
181
|
+
*/
|
|
182
|
+
test('봉투로 감싸여 와도 벗겨서 심는다 — 체크포인트가 겹포장돼 복원이 죽었던 자리', () => {
|
|
183
|
+
const enveloped = { revision: 2684, state: { revision: 2684, state: STATE } }
|
|
184
|
+
const plan = planWarmStart(enveloped as any, undefined, true)
|
|
185
|
+
assert.equal(plan.action, 'hydrate', '봉투를 받으면 심을 것이 없다고 답하면 안 된다')
|
|
186
|
+
if (plan.action !== 'hydrate') return
|
|
187
|
+
assert.equal(plan.itemCount, 3)
|
|
188
|
+
assert.deepEqual(plan.seed.locations, STATE.locations)
|
|
189
|
+
})
|
|
190
|
+
|
|
191
|
+
test('한 겹 봉투도 벗긴다 — 저장 방식이 바뀌는 동안 두 모양이 함께 남는다', () => {
|
|
192
|
+
const plan = planWarmStart({ revision: 1, state: STATE } as any, undefined, true)
|
|
193
|
+
assert.equal(plan.action, 'hydrate')
|
|
194
|
+
if (plan.action !== 'hydrate') return
|
|
195
|
+
assert.equal(plan.itemCount, 3)
|
|
196
|
+
})
|
|
197
|
+
|
|
198
|
+
test('벗길 것이 없으면 그대로 쓴다 — 상태를 봉투로 착각하지 않는다', () => {
|
|
199
|
+
const plan = planWarmStart(STATE, undefined, true)
|
|
200
|
+
assert.equal(plan.action, 'hydrate')
|
|
201
|
+
if (plan.action !== 'hydrate') return
|
|
202
|
+
assert.equal(plan.itemCount, 3)
|
|
203
|
+
})
|
|
204
|
+
|
|
205
|
+
test('빈 봉투는 여전히 no-state — 벗겼는데도 아무 축이 없으면 심을 것이 없다', () => {
|
|
206
|
+
assert.deepEqual(planWarmStart({ revision: 5, state: { revision: 5 } } as any, undefined, true), { action: 'skip', reason: 'no-state' })
|
|
207
|
+
})
|
|
208
|
+
|
|
209
|
+
test('unwrapState 는 상태를 먼저 알아본다 — state 라는 이름의 축이 있어도 벗기지 않는다', () => {
|
|
210
|
+
const st = { items: [{ epc: 'a' }], state: 'whatever' }
|
|
211
|
+
assert.equal(unwrapState(st as any), st, '축이 있으면 그것이 상태다')
|
|
212
|
+
})
|