@things-factory/headless-twin 10.1.28 → 10.1.31
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/service/reference/actuation-verification.d.ts +22 -0
- package/dist-server/service/reference/actuation-verification.js +37 -0
- package/dist-server/service/reference/actuation-verification.js.map +1 -1
- package/dist-server/service/reference/reference-resolver.js +32 -2
- package/dist-server/service/reference/reference-resolver.js.map +1 -1
- package/dist-server/service/twin-attention/twin-attention-query.d.ts +1 -3
- package/dist-server/service/twin-attention/twin-attention-query.js +15 -6
- package/dist-server/service/twin-attention/twin-attention-query.js.map +1 -1
- package/dist-server/service/twin-journal/twin-journal-query.d.ts +15 -0
- package/dist-server/service/twin-journal/twin-journal-query.js +18 -3
- package/dist-server/service/twin-journal/twin-journal-query.js.map +1 -1
- package/dist-server/service/twin-model/twin-lineage-query.js +14 -0
- package/dist-server/service/twin-model/twin-lineage-query.js.map +1 -1
- package/dist-server/service/twin-model/twin-model-item-query.js +11 -6
- package/dist-server/service/twin-model/twin-model-item-query.js.map +1 -1
- package/package.json +8 -8
- package/server/service/reference/actuation-verification.ts +42 -0
- package/server/service/reference/reference-resolver.ts +33 -2
- package/server/service/twin-attention/twin-attention-query.ts +13 -6
- package/server/service/twin-journal/twin-journal-query.ts +19 -2
- package/server/service/twin-model/twin-lineage-query.ts +13 -0
- package/server/service/twin-model/twin-model-item-query.ts +33 -6
- package/test/actuation-verification-state.test.ts +46 -1
- package/test/attention-digest.test.ts +26 -0
- package/test/axis-declaration-passthrough-db.test.ts +43 -1
- package/test/event-type-split.test.ts +23 -0
- package/test/lineage-survives-restart.test.ts +15 -0
- package/test/twin-model-item-db.test.ts +18 -1
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -20,3 +20,25 @@ export interface ActuationOutcomePatch {
|
|
|
20
20
|
* 서로 다른 말을 한다. 모르는 것은 안 적는다.
|
|
21
21
|
*/
|
|
22
22
|
export declare function actuationOutcomePatch(result: ActuationResult, at: string): ActuationOutcomePatch;
|
|
23
|
+
/**
|
|
24
|
+
* 저장이 조치의 주소를 바꾸나 — **바꿨으면 검증은 저장 시각부터 다시 없다**(ADR-0046 ②).
|
|
25
|
+
*
|
|
26
|
+
* 2026-09-18 실측: 칸 셋은 서 있고 왕복의 결과를 적는 자리도 있는데, 주소를 바꿔도 `verified` 가
|
|
27
|
+
* 그대로 남았다. 화면은 다른 공장을 가리키는 연결을 「확인됨」으로 그린다 — 그 초록이 가장 위험한
|
|
28
|
+
* 거짓이다.
|
|
29
|
+
*
|
|
30
|
+
* `unverified` 는 실패가 아니다. 아직 그 주소로 넘겨 본 적이 없다는 사실이고, 사람이 할 일은 한 번
|
|
31
|
+
* 넘겨 보는 것이다.
|
|
32
|
+
*/
|
|
33
|
+
export declare function actuationResetPatch(before: Record<string, unknown> | null | undefined, after: Record<string, unknown> | null | undefined): {
|
|
34
|
+
actuationState: ActuationState;
|
|
35
|
+
actuationVerifiedAt: null;
|
|
36
|
+
actuationFailure: null;
|
|
37
|
+
} | null;
|
|
38
|
+
/**
|
|
39
|
+
* 이 연결이 조치를 **보낼 수 있게 적혀 있나** — 주소가 없으면 어디로도 안 간다.
|
|
40
|
+
*
|
|
41
|
+
* 검증 상태를 화면에 보일지 가르는 값이다. 읽기만 하는 연결(마스터 동기만 하는 커넥터)에 「아직
|
|
42
|
+
* 확인 안 됨」을 그리면, 할 일이 없는 사람에게 할 일이 있는 것처럼 보인다.
|
|
43
|
+
*/
|
|
44
|
+
export declare function actuationAddressed(cfg: Record<string, unknown> | null | undefined): boolean;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.actuationOutcomePatch = actuationOutcomePatch;
|
|
4
|
+
exports.actuationResetPatch = actuationResetPatch;
|
|
5
|
+
exports.actuationAddressed = actuationAddressed;
|
|
4
6
|
/**
|
|
5
7
|
* 결과 → 적을 것.
|
|
6
8
|
*
|
|
@@ -25,4 +27,39 @@ function actuationOutcomePatch(result, at) {
|
|
|
25
27
|
: null
|
|
26
28
|
};
|
|
27
29
|
}
|
|
30
|
+
/**
|
|
31
|
+
* 조치가 가는 주소를 이루는 칸들 — 이 중 하나라도 바뀌면 지난 왕복은 **다른 주소의 이야기**다.
|
|
32
|
+
*
|
|
33
|
+
* `intakeSiteId` 가 없으면 `siteId` 가 그 자리를 대신한다(부딪힘 판정과 같은 규칙이다 —
|
|
34
|
+
* `connection-portability.ts`). 비밀값을 함께 보는 이유는, 주소가 같아도 저쪽이 우리를 못 알아보면
|
|
35
|
+
* 그 왕복이 다시 성립하지 않기 때문이다.
|
|
36
|
+
*/
|
|
37
|
+
const ACTUATION_ADDRESS = ['intakeEndpoint', 'intakeSiteId', 'siteId', 'intakeSecret'];
|
|
38
|
+
const addressOf = (cfg) => ACTUATION_ADDRESS.map(k => String(cfg?.[k] ?? '').trim().replace(/\/$/, '')).join('\u0000');
|
|
39
|
+
/**
|
|
40
|
+
* 저장이 조치의 주소를 바꾸나 — **바꿨으면 검증은 저장 시각부터 다시 없다**(ADR-0046 ②).
|
|
41
|
+
*
|
|
42
|
+
* 2026-09-18 실측: 칸 셋은 서 있고 왕복의 결과를 적는 자리도 있는데, 주소를 바꿔도 `verified` 가
|
|
43
|
+
* 그대로 남았다. 화면은 다른 공장을 가리키는 연결을 「확인됨」으로 그린다 — 그 초록이 가장 위험한
|
|
44
|
+
* 거짓이다.
|
|
45
|
+
*
|
|
46
|
+
* `unverified` 는 실패가 아니다. 아직 그 주소로 넘겨 본 적이 없다는 사실이고, 사람이 할 일은 한 번
|
|
47
|
+
* 넘겨 보는 것이다.
|
|
48
|
+
*/
|
|
49
|
+
function actuationResetPatch(before, after) {
|
|
50
|
+
if (addressOf(before) === addressOf(after))
|
|
51
|
+
return null;
|
|
52
|
+
return { actuationState: 'unverified', actuationVerifiedAt: null, actuationFailure: null };
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* 이 연결이 조치를 **보낼 수 있게 적혀 있나** — 주소가 없으면 어디로도 안 간다.
|
|
56
|
+
*
|
|
57
|
+
* 검증 상태를 화면에 보일지 가르는 값이다. 읽기만 하는 연결(마스터 동기만 하는 커넥터)에 「아직
|
|
58
|
+
* 확인 안 됨」을 그리면, 할 일이 없는 사람에게 할 일이 있는 것처럼 보인다.
|
|
59
|
+
*/
|
|
60
|
+
function actuationAddressed(cfg) {
|
|
61
|
+
const endpoint = String(cfg?.intakeEndpoint ?? '').trim();
|
|
62
|
+
const site = String(cfg?.intakeSiteId ?? cfg?.siteId ?? '').trim();
|
|
63
|
+
return !!endpoint && !!site;
|
|
64
|
+
}
|
|
28
65
|
//# sourceMappingURL=actuation-verification.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"actuation-verification.js","sourceRoot":"","sources":["../../../server/service/reference/actuation-verification.ts"],"names":[],"mappings":";;AA+BA,sDAWC;
|
|
1
|
+
{"version":3,"file":"actuation-verification.js","sourceRoot":"","sources":["../../../server/service/reference/actuation-verification.ts"],"names":[],"mappings":";;AA+BA,sDAWC;AAwBD,kDAMC;AAQD,gDAIC;AAlED;;;;;;;;;;;;GAYG;AACH,SAAgB,qBAAqB,CAAC,MAAuB,EAAE,EAAU;IACvE,IAAI,MAAM,CAAC,EAAE,EAAE,CAAC;QACd,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,mBAAmB,EAAE,EAAE,EAAE,gBAAgB,EAAE,IAAI,EAAE,CAAA;IACxF,CAAC;IAED,OAAO;QACL,cAAc,EAAE,QAAQ;QACxB,gBAAgB,EAAE,MAAM,CAAC,SAAS;YAChC,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,SAAS,EAAE,EAAE,EAAE,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE;YAC/F,CAAC,CAAC,IAAI;KACT,CAAA;AACH,CAAC;AAED;;;;;;GAMG;AACH,MAAM,iBAAiB,GAAG,CAAC,gBAAgB,EAAE,cAAc,EAAE,QAAQ,EAAE,cAAc,CAAU,CAAA;AAE/F,MAAM,SAAS,GAAG,CAAC,GAA+C,EAAE,EAAE,CACpE,iBAAiB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,MAAM,CAAE,GAAW,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;AAEtG;;;;;;;;;GASG;AACH,SAAgB,mBAAmB,CACjC,MAAkD,EAClD,KAAiD;IAEjD,IAAI,SAAS,CAAC,MAAM,CAAC,KAAK,SAAS,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,CAAA;IACvD,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,mBAAmB,EAAE,IAAI,EAAE,gBAAgB,EAAE,IAAI,EAAE,CAAA;AAC5F,CAAC;AAED;;;;;GAKG;AACH,SAAgB,kBAAkB,CAAC,GAA+C;IAChF,MAAM,QAAQ,GAAG,MAAM,CAAE,GAAW,EAAE,cAAc,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAA;IAClE,MAAM,IAAI,GAAG,MAAM,CAAE,GAAW,EAAE,YAAY,IAAK,GAAW,EAAE,MAAM,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAA;IACpF,OAAO,CAAC,CAAC,QAAQ,IAAI,CAAC,CAAC,IAAI,CAAA;AAC7B,CAAC","sourcesContent":["/*\n * 조치 왕복의 결과를 레퍼런스에 어떻게 적을 것인가 — **순수하게.**\n *\n * `actuation-routing.ts` 에 두면 시험이 그 파일을 들여야 하고, 그 파일은 엔티티를 값으로 들인다\n * (데코레이터가 붙은 클래스). 가벼운 모듈로 떼어 두면 판단만 따로 확인할 수 있다.\n *\n * 여기서 들이는 것은 **타입뿐**이다 — 런타임에 남지 않는다.\n */\nimport type { ActuationFailure, ActuationState } from './twin-reference.js'\nimport type { ActuationResult } from './reference-adapter.js'\n\n/** 레퍼런스에 적을 칸들. `actuationVerifiedAt` 이 없는 것은 「건드리지 않는다」는 뜻이다. */\nexport interface ActuationOutcomePatch {\n actuationState: ActuationState\n actuationVerifiedAt?: string\n actuationFailure: ActuationFailure | null\n}\n\n/**\n * 결과 → 적을 것.\n *\n * ── 세 가지를 지킨다 ───────────────────────────────────────────────────────\n * **받아들여지면 지난 거절을 지운다.** 남겨 두면 화면이 「됐는데 실패 사유가 있다」를 그린다.\n *\n * **거절은 성공 시각을 건드리지 않는다.** 「전에 한 번 닿았다」와 「이번에 안 됐다」는 둘 다 사실이고,\n * 지우면 앞엣것이 사라진다 — 사람이 「한 번이라도 닿은 적 있나」를 물을 자리가 없어진다.\n *\n * **코드가 없으면 사유를 비운다.** 자리를 채우려고 `error` 문장을 옮기지 않는다. `lastError` 가 이미\n * 겪었다 — 저장된 사람 문장은 번역도 집계도 안 되고, 커넥터가 그 문장을 고치는 날 옛 행과 새 행이\n * 서로 다른 말을 한다. 모르는 것은 안 적는다.\n */\nexport function actuationOutcomePatch(result: ActuationResult, at: string): ActuationOutcomePatch {\n if (result.ok) {\n return { actuationState: 'verified', actuationVerifiedAt: at, actuationFailure: null }\n }\n\n return {\n actuationState: 'failed',\n actuationFailure: result.errorCode\n ? { code: result.errorCode, at, ...(result.errorParams ? { params: result.errorParams } : {}) }\n : null\n }\n}\n\n/**\n * 조치가 가는 주소를 이루는 칸들 — 이 중 하나라도 바뀌면 지난 왕복은 **다른 주소의 이야기**다.\n *\n * `intakeSiteId` 가 없으면 `siteId` 가 그 자리를 대신한다(부딪힘 판정과 같은 규칙이다 —\n * `connection-portability.ts`). 비밀값을 함께 보는 이유는, 주소가 같아도 저쪽이 우리를 못 알아보면\n * 그 왕복이 다시 성립하지 않기 때문이다.\n */\nconst ACTUATION_ADDRESS = ['intakeEndpoint', 'intakeSiteId', 'siteId', 'intakeSecret'] as const\n\nconst addressOf = (cfg: Record<string, unknown> | null | undefined) =>\n ACTUATION_ADDRESS.map(k => String((cfg as any)?.[k] ?? '').trim().replace(/\\/$/, '')).join('\\u0000')\n\n/**\n * 저장이 조치의 주소를 바꾸나 — **바꿨으면 검증은 저장 시각부터 다시 없다**(ADR-0046 ②).\n *\n * 2026-09-18 실측: 칸 셋은 서 있고 왕복의 결과를 적는 자리도 있는데, 주소를 바꿔도 `verified` 가\n * 그대로 남았다. 화면은 다른 공장을 가리키는 연결을 「확인됨」으로 그린다 — 그 초록이 가장 위험한\n * 거짓이다.\n *\n * `unverified` 는 실패가 아니다. 아직 그 주소로 넘겨 본 적이 없다는 사실이고, 사람이 할 일은 한 번\n * 넘겨 보는 것이다.\n */\nexport function actuationResetPatch(\n before: Record<string, unknown> | null | undefined,\n after: Record<string, unknown> | null | undefined\n): { actuationState: ActuationState; actuationVerifiedAt: null; actuationFailure: null } | null {\n if (addressOf(before) === addressOf(after)) return null\n return { actuationState: 'unverified', actuationVerifiedAt: null, actuationFailure: null }\n}\n\n/**\n * 이 연결이 조치를 **보낼 수 있게 적혀 있나** — 주소가 없으면 어디로도 안 간다.\n *\n * 검증 상태를 화면에 보일지 가르는 값이다. 읽기만 하는 연결(마스터 동기만 하는 커넥터)에 「아직\n * 확인 안 됨」을 그리면, 할 일이 없는 사람에게 할 일이 있는 것처럼 보인다.\n */\nexport function actuationAddressed(cfg: Record<string, unknown> | null | undefined): boolean {\n const endpoint = String((cfg as any)?.intakeEndpoint ?? '').trim()\n const site = String((cfg as any)?.intakeSiteId ?? (cfg as any)?.siteId ?? '').trim()\n return !!endpoint && !!site\n}\n"]}
|
|
@@ -19,6 +19,7 @@ const reference_registry_js_1 = require("./reference-registry.js");
|
|
|
19
19
|
const reference_adapter_js_1 = require("./reference-adapter.js");
|
|
20
20
|
const discovery_result_js_1 = require("./discovery-result.js");
|
|
21
21
|
const ingest_space_js_1 = require("./ingest-space.js");
|
|
22
|
+
const actuation_verification_js_1 = require("./actuation-verification.js");
|
|
22
23
|
const connection_portability_js_1 = require("./connection-portability.js");
|
|
23
24
|
const reference_live_js_1 = require("./reference-live.js");
|
|
24
25
|
const template_registry_js_1 = require("./template-registry.js");
|
|
@@ -425,7 +426,21 @@ let TwinReferenceResolver = class TwinReferenceResolver {
|
|
|
425
426
|
return {
|
|
426
427
|
source: r.source, system: r.system, siteName: r.siteName, description: r.description,
|
|
427
428
|
adapterType: r.adapterType, status: r.status, lastSyncedAt: r.lastSyncedAt, lastError: r.lastError,
|
|
428
|
-
producedCount: produced.length, produced
|
|
429
|
+
producedCount: produced.length, produced,
|
|
430
|
+
/*
|
|
431
|
+
* **조치가 저쪽에 닿아 봤나** (ADR-0046 ②). 칸 셋은 2026-09 부터 서 있었는데 목록도 상세도
|
|
432
|
+
* 이 값을 싣지 않아 **화면이 볼 길이 없었다** — 저장된 사실이 아무에게도 닿지 않았다.
|
|
433
|
+
*
|
|
434
|
+
* 보낼 주소가 적힌 연결에만 싣는다. 읽기만 하는 연결에 「아직 확인 안 됨」이 서면 할 일이 없는
|
|
435
|
+
* 사람에게 할 일이 있는 것처럼 보인다.
|
|
436
|
+
*/
|
|
437
|
+
...((0, actuation_verification_js_1.actuationAddressed)(r.connectionConfig)
|
|
438
|
+
? {
|
|
439
|
+
actuationState: r.actuationState ?? 'unverified',
|
|
440
|
+
actuationVerifiedAt: r.actuationVerifiedAt ?? null,
|
|
441
|
+
actuationFailure: r.actuationFailure ?? null
|
|
442
|
+
}
|
|
443
|
+
: {})
|
|
429
444
|
};
|
|
430
445
|
});
|
|
431
446
|
}
|
|
@@ -504,7 +519,15 @@ let TwinReferenceResolver = class TwinReferenceResolver {
|
|
|
504
519
|
return {
|
|
505
520
|
source: r.source, system: r.system, siteName: r.siteName, description: r.description,
|
|
506
521
|
adapterType: r.adapterType, connectionConfig: cfg, status: r.status, lastSyncedAt: r.lastSyncedAt, lastError: r.lastError,
|
|
507
|
-
producedInstanceIds
|
|
522
|
+
producedInstanceIds,
|
|
523
|
+
/* 목록과 같은 규칙 — 보낼 주소가 적힌 연결만 검증 상태를 싣는다(ADR-0046 ②). */
|
|
524
|
+
...((0, actuation_verification_js_1.actuationAddressed)(r.connectionConfig)
|
|
525
|
+
? {
|
|
526
|
+
actuationState: r.actuationState ?? 'unverified',
|
|
527
|
+
actuationVerifiedAt: r.actuationVerifiedAt ?? null,
|
|
528
|
+
actuationFailure: r.actuationFailure ?? null
|
|
529
|
+
}
|
|
530
|
+
: {})
|
|
508
531
|
};
|
|
509
532
|
}
|
|
510
533
|
referenceAdapters() {
|
|
@@ -537,8 +560,15 @@ let TwinReferenceResolver = class TwinReferenceResolver {
|
|
|
537
560
|
const taken = (0, connection_portability_js_1.refuseIntakeCollision)(source, connectionConfig, others);
|
|
538
561
|
if (taken)
|
|
539
562
|
return { ok: false, source, error: taken.message, errorCode: taken.code, errorParams: taken.params };
|
|
563
|
+
/*
|
|
564
|
+
* **주소를 바꾸는 저장은 검증을 되돌린다** (ADR-0046 ②). 지난 왕복은 지난 주소의 이야기라 새
|
|
565
|
+
* 주소에 대해서는 아무 말도 하지 않는다. 화면이 그 초록을 그대로 두면 다른 공장을 가리키는 연결이
|
|
566
|
+
* 「확인됨」으로 보인다. 되돌린 상태는 실패가 아니다 — 아직 안 해 본 것이다.
|
|
567
|
+
*/
|
|
568
|
+
const reset = (0, actuation_verification_js_1.actuationResetPatch)(existing?.connectionConfig, connectionConfig);
|
|
540
569
|
const saved = await repo.save(repo.create({
|
|
541
570
|
...(existing ?? {}),
|
|
571
|
+
...(reset ?? {}),
|
|
542
572
|
domain: { id: domainId },
|
|
543
573
|
source,
|
|
544
574
|
system: patch.system ?? existing?.system,
|