@things-factory/headless-twin 10.1.45 → 10.1.46
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/checkpoint-fold.d.ts +5 -0
- package/dist-server/engine/checkpoint-fold.js +17 -0
- package/dist-server/engine/checkpoint-fold.js.map +1 -0
- package/dist-server/engine/twin-engine.js +10 -4
- package/dist-server/engine/twin-engine.js.map +1 -1
- package/dist-server/engine/warm-start.d.ts +4 -0
- package/dist-server/engine/warm-start.js +6 -2
- package/dist-server/engine/warm-start.js.map +1 -1
- package/dist-shared/entity-delta.d.ts +4 -1
- package/dist-shared/entity-delta.js +22 -28
- package/dist-shared/entity-delta.js.map +1 -1
- package/package.json +6 -6
- package/server/engine/checkpoint-fold.ts +12 -0
- package/server/engine/twin-engine.ts +8 -2
- package/server/engine/warm-start.ts +10 -2
- package/shared/entity-delta.ts +27 -2
- package/test/live-mirror-parity.test.ts +4 -0
- package/test/order-fold-host.test.ts +112 -0
- package/tsconfig.shared.tsbuildinfo +1 -1
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.foldForCheckpoint = foldForCheckpoint;
|
|
4
|
+
/*
|
|
5
|
+
* **체크포인트를 쓰기 직전에 끝난 오더를 접는다** — ADR-0092 결정 1.
|
|
6
|
+
*
|
|
7
|
+
* 커널은 스스로 접지 않는다. 접는 때를 체크포인트에 맞춰야 접힌 쪽이 씨앗과 같은 때에 적히고, 재기동 동치(`재개점 + 꼬리 ==
|
|
8
|
+
* 0부터 접기`)가 선다. 그래서 체크포인트를 쓰는 호스트가 부른다. 두 구동이 가진 커널이 다르다 — 미러는 `projector`(관측 모드
|
|
9
|
+
* 커널), 시뮬은 `kernel` 이다. 둘 중 그 구동의 것을 부르고, 접는 문이 없는 옛 커널이면 아무것도 하지 않는다.
|
|
10
|
+
*/
|
|
11
|
+
function foldForCheckpoint(inst) {
|
|
12
|
+
const k = inst?.mode === 'live' ? inst.projector : inst?.kernel;
|
|
13
|
+
if (typeof k?.foldFinishedOrders !== 'function')
|
|
14
|
+
return 0;
|
|
15
|
+
return Number(k.foldFinishedOrders()) || 0;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=checkpoint-fold.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"checkpoint-fold.js","sourceRoot":"","sources":["../../server/engine/checkpoint-fold.ts"],"names":[],"mappings":";;AAOA,8CAIC;AAXD;;;;;;GAMG;AACH,SAAgB,iBAAiB,CAAC,IAA0E;IAC1G,MAAM,CAAC,GAAG,IAAI,EAAE,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,EAAE,MAAM,CAAA;IAC/D,IAAI,OAAO,CAAC,EAAE,kBAAkB,KAAK,UAAU;QAAE,OAAO,CAAC,CAAA;IACzD,OAAO,MAAM,CAAC,CAAC,CAAC,kBAAkB,EAAE,CAAC,IAAI,CAAC,CAAA;AAC5C,CAAC","sourcesContent":["/*\n * **체크포인트를 쓰기 직전에 끝난 오더를 접는다** — ADR-0092 결정 1.\n *\n * 커널은 스스로 접지 않는다. 접는 때를 체크포인트에 맞춰야 접힌 쪽이 씨앗과 같은 때에 적히고, 재기동 동치(`재개점 + 꼬리 ==\n * 0부터 접기`)가 선다. 그래서 체크포인트를 쓰는 호스트가 부른다. 두 구동이 가진 커널이 다르다 — 미러는 `projector`(관측 모드\n * 커널), 시뮬은 `kernel` 이다. 둘 중 그 구동의 것을 부르고, 접는 문이 없는 옛 커널이면 아무것도 하지 않는다.\n */\nexport function foldForCheckpoint(inst: { mode?: 'sim' | 'live'; projector?: any; kernel?: any } | undefined): number {\n const k = inst?.mode === 'live' ? inst.projector : inst?.kernel\n if (typeof k?.foldFinishedOrders !== 'function') return 0\n return Number(k.foldFinishedOrders()) || 0\n}\n"]}
|
|
@@ -46,6 +46,8 @@ const restart_policy_js_1 = require("./restart-policy.js");
|
|
|
46
46
|
const kpi_query_js_1 = require("./kpi-query.js");
|
|
47
47
|
const node_crypto_1 = require("node:crypto");
|
|
48
48
|
const entity_delta_js_1 = require("@things-factory/headless-twin/dist-shared/entity-delta.js");
|
|
49
|
+
const ops_contract_1 = require("@operato/ops-contract");
|
|
50
|
+
const checkpoint_fold_js_1 = require("./checkpoint-fold.js");
|
|
49
51
|
const load_meter_js_1 = require("./load-meter.js");
|
|
50
52
|
const loop_lag_js_1 = require("./loop-lag.js");
|
|
51
53
|
const touched_items_js_1 = require("@things-factory/headless-twin/dist-shared/touched-items.js");
|
|
@@ -56,7 +58,7 @@ const attention_digest_js_1 = require("./attention-digest.js");
|
|
|
56
58
|
const live_feed_registry_js_1 = require("./live-feed-registry.js");
|
|
57
59
|
const ingest_health_js_1 = require("./ingest-health.js");
|
|
58
60
|
const ingest_dedupe_js_1 = require("./ingest-dedupe.js");
|
|
59
|
-
const
|
|
61
|
+
const ops_contract_2 = require("@operato/ops-contract");
|
|
60
62
|
/* 커널 런타임 로드 — CJS 번들(dist-cjs). 타입은 위 import type 로. replay = 이벤트열→상태 재구성(복구·시간여행). */
|
|
61
63
|
const { WmsKernel, YmsKernel, MesKernel, EmsKernel, TwinRuntime, StateProjector, replay, replayFrom, replayWithCheckpoint, replaySegments } = require('@operato/twin-kernel');
|
|
62
64
|
const { readBoardLocations, readBoardEquipment, DOMAIN_CATALOG, OP_EVENT, validateScenario } = require('@operato/ops-contract');
|
|
@@ -300,6 +302,8 @@ class TwinEngine {
|
|
|
300
302
|
* 그것을 다시 `{revision, state}` 로 감싸 넣어 왔다 → 꺼낸 값에 축이 하나도 없어 웜스타트가
|
|
301
303
|
* 알리지 않고 넘어갔다(저널에 수천 건이 있어도 트윈이 빈 채로 떴다).
|
|
302
304
|
*/
|
|
305
|
+
/* 끝난 오더를 접고 나서 상태를 뜬다 — 접힌 쪽이 이 체크포인트와 같은 때에 적힌다(ADR-0092 · `checkpoint-fold.ts`). */
|
|
306
|
+
(0, checkpoint_fold_js_1.foldForCheckpoint)(inst);
|
|
303
307
|
const state = (0, warm_start_js_1.unwrapState)(this.snapshot(domainId, instanceId));
|
|
304
308
|
if (!state)
|
|
305
309
|
return;
|
|
@@ -1354,7 +1358,7 @@ class TwinEngine {
|
|
|
1354
1358
|
if (!cached)
|
|
1355
1359
|
return;
|
|
1356
1360
|
const plan = (0, warm_start_js_1.planLiveContinuity)(cached);
|
|
1357
|
-
if (!plan.ackedCount && !plan.attentionSinceCount && !plan.energy)
|
|
1361
|
+
if (!plan.ackedCount && !plan.attentionSinceCount && !plan.energy && !plan.foldedOrders)
|
|
1358
1362
|
return;
|
|
1359
1363
|
if (typeof kernel.hydrateContinuity !== 'function') {
|
|
1360
1364
|
(0, log_js_1.twinWarn)(`[twin-engine] kernel for "${id}" cannot carry continuity (no hydrateContinuity) — the open demand window, ` +
|
|
@@ -3255,7 +3259,7 @@ class TwinEngine {
|
|
|
3255
3259
|
*/
|
|
3256
3260
|
if (model?.utcOffsetMinutes === undefined) {
|
|
3257
3261
|
for (const e of (model?.equipment ?? [])) {
|
|
3258
|
-
const hasProfile = (e?.properties ?? []).some(p => String(p?.id) ===
|
|
3262
|
+
const hasProfile = (e?.properties ?? []).some(p => String(p?.id) === ops_contract_2.EMS_PROPERTY.genDailyProfile && String(p?.value ?? '').trim());
|
|
3259
3263
|
if (hasProfile)
|
|
3260
3264
|
warnings.push((0, reference_master_js_3.generationWithoutTimeZone)(String(model?.spaceId ?? ''), String(e.id)));
|
|
3261
3265
|
}
|
|
@@ -3372,7 +3376,9 @@ class TwinEngine {
|
|
|
3372
3376
|
* 그물이다** — 사건 없이 바뀌는 자리를 찾으면 그것을 고치는 것이 답이고 이 그물은 시간을 벌 뿐이다.
|
|
3373
3377
|
*/
|
|
3374
3378
|
const full = inst.fullBroadcastDue === true || !inst.dirtyItems;
|
|
3375
|
-
|
|
3379
|
+
/* 「최근 T 완료」의 T 는 모델의 선언이다(ADR-0092) — 커널이 불러올 때 이미 거절했으므로 여기 오는 값은 옳다. */
|
|
3380
|
+
const fold = (0, ops_contract_1.orderFoldPolicyOf)(inst.model?.orderFold);
|
|
3381
|
+
const deltas = (0, entity_delta_js_1.buildEntityDeltas)(st, inst.id, full ? undefined : { items: inst.dirtyItems }, 'policy' in fold ? fold.policy : undefined);
|
|
3376
3382
|
this.broadcastPasses++;
|
|
3377
3383
|
if (full)
|
|
3378
3384
|
this.broadcastFullPasses++;
|