@things-factory/headless-twin 10.1.40 → 10.1.44

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.
@@ -0,0 +1,13 @@
1
+ export declare const INSTANCE_TAKEN = "instance-taken-by-another-source";
2
+ export interface ProducerRefusal {
3
+ code: typeof INSTANCE_TAKEN;
4
+ params: {
5
+ instance: string;
6
+ producer: string;
7
+ };
8
+ message: string;
9
+ }
10
+ /** Who produced a twin, as one comparable string — or undefined when the origin says nothing usable. */
11
+ export declare function producerOf(origin: unknown): string | undefined;
12
+ /** null when `incoming` may rebuild the twin `prior` was made by; otherwise the refusal. */
13
+ export declare function refuseOverwriteByAnotherProducer(instance: string, priorOrigin: unknown, incomingOrigin: unknown): ProducerRefusal | null;
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+ /*
3
+ * **A twin made by one producer is not rebuilt by another.** Pure.
4
+ *
5
+ * ── What went wrong (2026-09-24) ─────────────────────────────────────────────
6
+ * An import's instance id is the adapter's site id (`master.source`). Importing the warehouse connection
7
+ * `wms-miratek` with site id `miratek` — the same as the plant connection's — landed on the running plant
8
+ * mirror `miratek`, and `ingestMaster` gave it the warehouse structure as a new revision: 16 places,
9
+ * 23 equipment, 17 people and 14 assets gone. The row already said who made it (`origin.source` was
10
+ * `mes-miratek`); nothing asked.
11
+ *
12
+ * ── The rule (chief architect ruling, 2026-09-24) ────────────────────────────
13
+ * Compare producers by identity, not by the whole origin: a reference is kind + source, a template is
14
+ * kind + templateId. The same import carries more or fewer fields at different call sites (siteId,
15
+ * spaceId), and a template re-run has new params — those are the same producer re-reading its own twin.
16
+ *
17
+ * both declared, same producer allowed (resync, re-import, re-run)
18
+ * both declared, different refused, naming the producer that owns it
19
+ * prior undeclared, incoming not refused — nobody can say who made it; delete it first to replace it
20
+ * prior declared, incoming not refused — same reason, the other way
21
+ * neither declared allowed — the demo seed re-ingesting its own twin at boot. Closes when
22
+ * the seed declares a template origin.
23
+ */
24
+ Object.defineProperty(exports, "__esModule", { value: true });
25
+ exports.INSTANCE_TAKEN = void 0;
26
+ exports.producerOf = producerOf;
27
+ exports.refuseOverwriteByAnotherProducer = refuseOverwriteByAnotherProducer;
28
+ exports.INSTANCE_TAKEN = 'instance-taken-by-another-source';
29
+ /** Who produced a twin, as one comparable string — or undefined when the origin says nothing usable. */
30
+ function producerOf(origin) {
31
+ if (!origin || typeof origin !== 'object')
32
+ return undefined;
33
+ const o = origin;
34
+ if (o.kind === 'reference' && typeof o.source === 'string' && o.source)
35
+ return `reference:${o.source}`;
36
+ if (o.kind === 'template' && typeof o.templateId === 'string' && o.templateId)
37
+ return `template:${o.templateId}`;
38
+ /* A kind this guard does not know is still somebody — keep it distinct rather than reading it as none. */
39
+ if (typeof o.kind === 'string' && o.kind)
40
+ return `${o.kind}:${String(o.source ?? o.templateId ?? '')}`;
41
+ return undefined;
42
+ }
43
+ function nameOf(origin) {
44
+ const o = (origin ?? {});
45
+ return String(o.source ?? o.templateId ?? '') || 'undeclared';
46
+ }
47
+ /** null when `incoming` may rebuild the twin `prior` was made by; otherwise the refusal. */
48
+ function refuseOverwriteByAnotherProducer(instance, priorOrigin, incomingOrigin) {
49
+ const had = producerOf(priorOrigin);
50
+ const comes = producerOf(incomingOrigin);
51
+ if (!had && !comes)
52
+ return null;
53
+ if (had && comes && had === comes)
54
+ return null;
55
+ const producer = had ? nameOf(priorOrigin) : 'undeclared';
56
+ return {
57
+ code: exports.INSTANCE_TAKEN,
58
+ params: { instance, producer },
59
+ message: `twin "${instance}" was made by ${producer} — delete it first, or import this site under another id`
60
+ };
61
+ }
62
+ //# sourceMappingURL=producer-guard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"producer-guard.js","sourceRoot":"","sources":["../../server/engine/producer-guard.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;;;AAWH,gCAQC;AAQD,4EAeC;AAxCY,QAAA,cAAc,GAAG,kCAAkC,CAAA;AAQhE,wGAAwG;AACxG,SAAgB,UAAU,CAAC,MAAe;IACxC,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ;QAAE,OAAO,SAAS,CAAA;IAC3D,MAAM,CAAC,GAAG,MAAoE,CAAA;IAC9E,IAAI,CAAC,CAAC,IAAI,KAAK,WAAW,IAAI,OAAO,CAAC,CAAC,MAAM,KAAK,QAAQ,IAAI,CAAC,CAAC,MAAM;QAAE,OAAO,aAAa,CAAC,CAAC,MAAM,EAAE,CAAA;IACtG,IAAI,CAAC,CAAC,IAAI,KAAK,UAAU,IAAI,OAAO,CAAC,CAAC,UAAU,KAAK,QAAQ,IAAI,CAAC,CAAC,UAAU;QAAE,OAAO,YAAY,CAAC,CAAC,UAAU,EAAE,CAAA;IAChH,0GAA0G;IAC1G,IAAI,OAAO,CAAC,CAAC,IAAI,KAAK,QAAQ,IAAI,CAAC,CAAC,IAAI;QAAE,OAAO,GAAG,CAAC,CAAC,IAAI,IAAI,MAAM,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,UAAU,IAAI,EAAE,CAAC,EAAE,CAAA;IACtG,OAAO,SAAS,CAAA;AAClB,CAAC;AAED,SAAS,MAAM,CAAC,MAAe;IAC7B,MAAM,CAAC,GAAG,CAAC,MAAM,IAAI,EAAE,CAA+C,CAAA;IACtE,OAAO,MAAM,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,UAAU,IAAI,EAAE,CAAC,IAAI,YAAY,CAAA;AAC/D,CAAC;AAED,4FAA4F;AAC5F,SAAgB,gCAAgC,CAC9C,QAAgB,EAChB,WAAoB,EACpB,cAAuB;IAEvB,MAAM,GAAG,GAAG,UAAU,CAAC,WAAW,CAAC,CAAA;IACnC,MAAM,KAAK,GAAG,UAAU,CAAC,cAAc,CAAC,CAAA;IACxC,IAAI,CAAC,GAAG,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAA;IAC/B,IAAI,GAAG,IAAI,KAAK,IAAI,GAAG,KAAK,KAAK;QAAE,OAAO,IAAI,CAAA;IAC9C,MAAM,QAAQ,GAAG,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,YAAY,CAAA;IACzD,OAAO;QACL,IAAI,EAAE,sBAAc;QACpB,MAAM,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE;QAC9B,OAAO,EAAE,SAAS,QAAQ,iBAAiB,QAAQ,0DAA0D;KAC9G,CAAA;AACH,CAAC","sourcesContent":["/*\n * **A twin made by one producer is not rebuilt by another.** Pure.\n *\n * ── What went wrong (2026-09-24) ─────────────────────────────────────────────\n * An import's instance id is the adapter's site id (`master.source`). Importing the warehouse connection\n * `wms-miratek` with site id `miratek` — the same as the plant connection's — landed on the running plant\n * mirror `miratek`, and `ingestMaster` gave it the warehouse structure as a new revision: 16 places,\n * 23 equipment, 17 people and 14 assets gone. The row already said who made it (`origin.source` was\n * `mes-miratek`); nothing asked.\n *\n * ── The rule (chief architect ruling, 2026-09-24) ────────────────────────────\n * Compare producers by identity, not by the whole origin: a reference is kind + source, a template is\n * kind + templateId. The same import carries more or fewer fields at different call sites (siteId,\n * spaceId), and a template re-run has new params — those are the same producer re-reading its own twin.\n *\n * both declared, same producer allowed (resync, re-import, re-run)\n * both declared, different refused, naming the producer that owns it\n * prior undeclared, incoming not refused — nobody can say who made it; delete it first to replace it\n * prior declared, incoming not refused — same reason, the other way\n * neither declared allowed — the demo seed re-ingesting its own twin at boot. Closes when\n * the seed declares a template origin.\n */\n\nexport const INSTANCE_TAKEN = 'instance-taken-by-another-source'\n\nexport interface ProducerRefusal {\n code: typeof INSTANCE_TAKEN\n params: { instance: string; producer: string }\n message: string\n}\n\n/** Who produced a twin, as one comparable string — or undefined when the origin says nothing usable. */\nexport function producerOf(origin: unknown): string | undefined {\n if (!origin || typeof origin !== 'object') return undefined\n const o = origin as { kind?: unknown; source?: unknown; templateId?: unknown }\n if (o.kind === 'reference' && typeof o.source === 'string' && o.source) return `reference:${o.source}`\n if (o.kind === 'template' && typeof o.templateId === 'string' && o.templateId) return `template:${o.templateId}`\n /* A kind this guard does not know is still somebody — keep it distinct rather than reading it as none. */\n if (typeof o.kind === 'string' && o.kind) return `${o.kind}:${String(o.source ?? o.templateId ?? '')}`\n return undefined\n}\n\nfunction nameOf(origin: unknown): string {\n const o = (origin ?? {}) as { source?: unknown; templateId?: unknown }\n return String(o.source ?? o.templateId ?? '') || 'undeclared'\n}\n\n/** null when `incoming` may rebuild the twin `prior` was made by; otherwise the refusal. */\nexport function refuseOverwriteByAnotherProducer(\n instance: string,\n priorOrigin: unknown,\n incomingOrigin: unknown\n): ProducerRefusal | null {\n const had = producerOf(priorOrigin)\n const comes = producerOf(incomingOrigin)\n if (!had && !comes) return null\n if (had && comes && had === comes) return null\n const producer = had ? nameOf(priorOrigin) : 'undeclared'\n return {\n code: INSTANCE_TAKEN,\n params: { instance, producer },\n message: `twin \"${instance}\" was made by ${producer} — delete it first, or import this site under another id`\n }\n}\n"]}
@@ -23,6 +23,7 @@ const warm_start_js_1 = require("./warm-start.js");
23
23
  const local_declarations_js_1 = require("./local-declarations.js");
24
24
  const runtime_key_js_1 = require("./runtime-key.js");
25
25
  const twin_state_channel_js_1 = require("./twin-state-channel.js");
26
+ const producer_guard_js_1 = require("./producer-guard.js");
26
27
  const command_routing_js_1 = require("./command-routing.js");
27
28
  const twin_instance_js_1 = require("../service/twin-instance/twin-instance.js");
28
29
  /* 자극의 집은 원본이다(ADR-0029) — 그 행을 읽고 쓴다. */
@@ -1001,6 +1002,7 @@ class TwinEngine {
1001
1002
  `${plan.itemCount} item(s)`,
1002
1003
  `${plan.equipmentCount} equipment`,
1003
1004
  `${plan.orderCount} open order(s)`,
1005
+ ...(plan.foldedOrderCount ? [`${plan.foldedOrderCount} folded finished order(s)`] : []),
1004
1006
  `${plan.taskCount} task(s)`,
1005
1007
  ...(plan.personCount ? [`${plan.personCount} person(s)`] : []),
1006
1008
  ...(plan.assetCount ? [`${plan.assetCount} asset(s)`] : []),
@@ -3078,6 +3080,19 @@ class TwinEngine {
3078
3080
  * 대상이 사라졌으면(구조가 바뀌었다) 그 사실을 경고로 낸다 — 갈 곳 없는 선언을 알리지 않고 버리지 않는다.
3079
3081
  */
3080
3082
  const prior = await (0, shell_1.getRepository)(twin_instance_js_1.TwinInstance).findOne({ where: { domain: { id: domainId }, instanceId: master.source } });
3083
+ /*
3084
+ * A twin another producer made is not rebuilt here (engine/producer-guard.ts). Refused before anything
3085
+ * below writes — `prior` is saved a few lines on, and refusing after that would leave it half-changed.
3086
+ */
3087
+ if (prior) {
3088
+ const taken = (0, producer_guard_js_1.refuseOverwriteByAnotherProducer)(master.source, prior.origin, master.origin);
3089
+ if (taken) {
3090
+ const err = new Error(taken.message);
3091
+ err.code = taken.code;
3092
+ err.params = taken.params;
3093
+ throw err;
3094
+ }
3095
+ }
3081
3096
  /*
3082
3097
  * 겹이 **둘**이다 — 현장(공간)의 것과 이 트윈의 것. 순서가 규율이다: 현장 먼저, 트윈이 나중.
3083
3098
  * 요금처럼 현장이 정한 수는 그 현장의 트윈 전부가 같이 읽어야 하고, 한 트윈만 다르게 두는 실험은