@operato/twin-kernel 0.4.2 → 0.4.3

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.
@@ -376,6 +376,7 @@ export declare abstract class FlowEngine implements TwinKernel {
376
376
  /** 주목 신호가 **처음 성립한 시각**(id → ISO). 조건이 사라지면 지운다 — 재발은 새 시작이다. */
377
377
  private _attentionSince;
378
378
  dispatch(cmd: Command): CommandAck;
379
+ private dispatchInner;
379
380
  /** 도메인 커맨드 처리(order.release 등). 기본은 거절 — 도메인이 override. */
380
381
  protected handleCommand(cmd: Command): CommandAck;
381
382
  readonly scenario: ScenarioControl;
@@ -617,6 +618,17 @@ export declare abstract class FlowEngine implements TwinKernel {
617
618
  private oeeOf;
618
619
  /** 정책에 넘길 특정 타입 자리의 관측 뷰 — 예약(그 자리로 향하는 in-flight task) 포함. */
619
620
  protected slotViews(locationType: string): SlotView[];
621
+ /**
622
+ * 지금 처리 중인 커맨드의 상관값 — **디스패치 동안에만 있다.**
623
+ *
624
+ * 커맨드가 낳은 이벤트에 이 값을 실어야 "이 지시가 실제로 무엇을 일으켰나" 를 나중에 물을 수 있다.
625
+ * 그게 없으면 승인 기록은 "허락했다" 까지이고, 그 뒤 공장이 어떻게 움직였는지와 이어지지 않는다.
626
+ *
627
+ * **한계를 밝힌다**: 여기서 잇는 것은 그 자리에서 방출된 이벤트뿐이다. 나중 틱에 일어나는 후속
628
+ * (예: resourceDown 이 정한 수리 완료)은 시뮬 시간의 결과라 이어지지 않는다. 즉시 인과만 잇는다 —
629
+ * 먼 인과까지 같은 값으로 묶으면 "이 승인 때문"이라는 말이 사실보다 넓어진다.
630
+ */
631
+ private _correlationId?;
620
632
  protected emit(event: EpcisEvent): void;
621
633
  protected emitOp(eventType: string, data: unknown): void;
622
634
  /**
@@ -546,6 +546,20 @@ export class FlowEngine {
546
546
  /** 주목 신호가 **처음 성립한 시각**(id → ISO). 조건이 사라지면 지운다 — 재발은 새 시작이다. */
547
547
  _attentionSince = new Map(); // 확인(ack)된 주목 신호 id — 조건 지속돼도 acknowledged 로 표시(재발 시 재활성)
548
548
  dispatch(cmd) {
549
+ /*
550
+ * 이 커맨드가 낳는 이벤트에 상관값을 단다. 명시값이 없으면 **commandId 를 쓴다** — 호출자가
551
+ * 따로 챙기지 않아도 모든 지시가 자기 결과와 이어진다(감사 기록이 commandId 를 이미 갖고 있다).
552
+ * finally 로 반드시 걷는다: 남겨 두면 그 뒤 틱에서 일어난 무관한 일까지 이 커맨드 탓이 된다.
553
+ */
554
+ this._correlationId = cmd.correlationId ?? cmd.commandId;
555
+ try {
556
+ return this.dispatchInner(cmd);
557
+ }
558
+ finally {
559
+ this._correlationId = undefined;
560
+ }
561
+ }
562
+ dispatchInner(cmd) {
549
563
  const ok = () => ({ commandId: cmd.commandId, accepted: true });
550
564
  // 거절 사유 = 언어 중립 코드 + 원시 파라미터. error 는 영어 폴백(로그·개발자용).
551
565
  const fail = (errorCode, errorParams) => ({ commandId: cmd.commandId, accepted: false, errorCode, errorParams, error: errorCode });
@@ -1282,15 +1296,26 @@ export class FlowEngine {
1282
1296
  views.push({ id: n.id, capacity: n.capacity, occupancy: n.occupancy, reserved: reserved.get(n.id) ?? 0 });
1283
1297
  return views;
1284
1298
  }
1299
+ /**
1300
+ * 지금 처리 중인 커맨드의 상관값 — **디스패치 동안에만 있다.**
1301
+ *
1302
+ * 커맨드가 낳은 이벤트에 이 값을 실어야 "이 지시가 실제로 무엇을 일으켰나" 를 나중에 물을 수 있다.
1303
+ * 그게 없으면 승인 기록은 "허락했다" 까지이고, 그 뒤 공장이 어떻게 움직였는지와 이어지지 않는다.
1304
+ *
1305
+ * **한계를 밝힌다**: 여기서 잇는 것은 그 자리에서 방출된 이벤트뿐이다. 나중 틱에 일어나는 후속
1306
+ * (예: resourceDown 이 정한 수리 완료)은 시뮬 시간의 결과라 이어지지 않는다. 즉시 인과만 잇는다 —
1307
+ * 먼 인과까지 같은 값으로 묶으면 "이 승인 때문"이라는 말이 사실보다 넓어진다.
1308
+ */
1309
+ _correlationId;
1285
1310
  emit(event) {
1286
1311
  this.revision++;
1287
- const e = { eventId: `${this.tenantId}-evt-${++this.eventSeq}`, eventType: `epcis.${event.type}`, eventTime: event.eventTime, tenantId: this.tenantId, data: event };
1312
+ const e = { eventId: `${this.tenantId}-evt-${++this.eventSeq}`, eventType: `epcis.${event.type}`, eventTime: event.eventTime, tenantId: this.tenantId, ...(this._correlationId ? { correlationId: this._correlationId } : {}), data: event };
1288
1313
  for (const h of this.handlers)
1289
1314
  h(e);
1290
1315
  }
1291
1316
  emitOp(eventType, data) {
1292
1317
  this.revision++;
1293
- const e = { eventId: `${this.tenantId}-evt-${++this.eventSeq}`, eventType, eventTime: this.now(), tenantId: this.tenantId, data };
1318
+ const e = { eventId: `${this.tenantId}-evt-${++this.eventSeq}`, eventType, eventTime: this.now(), tenantId: this.tenantId, ...(this._correlationId ? { correlationId: this._correlationId } : {}), data };
1294
1319
  for (const h of this.handlers)
1295
1320
  h(e);
1296
1321
  }
@@ -2353,6 +2353,14 @@ var FlowEngine = class {
2353
2353
  _attentionSince = /* @__PURE__ */ new Map();
2354
2354
  // 확인(ack)된 주목 신호 id — 조건 지속돼도 acknowledged 로 표시(재발 시 재활성)
2355
2355
  dispatch(cmd) {
2356
+ this._correlationId = cmd.correlationId ?? cmd.commandId;
2357
+ try {
2358
+ return this.dispatchInner(cmd);
2359
+ } finally {
2360
+ this._correlationId = void 0;
2361
+ }
2362
+ }
2363
+ dispatchInner(cmd) {
2356
2364
  const ok = () => ({ commandId: cmd.commandId, accepted: true });
2357
2365
  const fail = (errorCode, errorParams) => ({ commandId: cmd.commandId, accepted: false, errorCode, errorParams, error: errorCode });
2358
2366
  switch (cmd.type) {
@@ -3014,14 +3022,25 @@ var FlowEngine = class {
3014
3022
  for (const n of this.locations.values()) if (n.type === locationType) views.push({ id: n.id, capacity: n.capacity, occupancy: n.occupancy, reserved: reserved.get(n.id) ?? 0 });
3015
3023
  return views;
3016
3024
  }
3025
+ /**
3026
+ * 지금 처리 중인 커맨드의 상관값 — **디스패치 동안에만 있다.**
3027
+ *
3028
+ * 커맨드가 낳은 이벤트에 이 값을 실어야 "이 지시가 실제로 무엇을 일으켰나" 를 나중에 물을 수 있다.
3029
+ * 그게 없으면 승인 기록은 "허락했다" 까지이고, 그 뒤 공장이 어떻게 움직였는지와 이어지지 않는다.
3030
+ *
3031
+ * **한계를 밝힌다**: 여기서 잇는 것은 그 자리에서 방출된 이벤트뿐이다. 나중 틱에 일어나는 후속
3032
+ * (예: resourceDown 이 정한 수리 완료)은 시뮬 시간의 결과라 이어지지 않는다. 즉시 인과만 잇는다 —
3033
+ * 먼 인과까지 같은 값으로 묶으면 "이 승인 때문"이라는 말이 사실보다 넓어진다.
3034
+ */
3035
+ _correlationId;
3017
3036
  emit(event) {
3018
3037
  this.revision++;
3019
- const e = { eventId: `${this.tenantId}-evt-${++this.eventSeq}`, eventType: `epcis.${event.type}`, eventTime: event.eventTime, tenantId: this.tenantId, data: event };
3038
+ const e = { eventId: `${this.tenantId}-evt-${++this.eventSeq}`, eventType: `epcis.${event.type}`, eventTime: event.eventTime, tenantId: this.tenantId, ...this._correlationId ? { correlationId: this._correlationId } : {}, data: event };
3020
3039
  for (const h of this.handlers) h(e);
3021
3040
  }
3022
3041
  emitOp(eventType, data) {
3023
3042
  this.revision++;
3024
- const e = { eventId: `${this.tenantId}-evt-${++this.eventSeq}`, eventType, eventTime: this.now(), tenantId: this.tenantId, data };
3043
+ const e = { eventId: `${this.tenantId}-evt-${++this.eventSeq}`, eventType, eventTime: this.now(), tenantId: this.tenantId, ...this._correlationId ? { correlationId: this._correlationId } : {}, data };
3025
3044
  for (const h of this.handlers) h(e);
3026
3045
  }
3027
3046
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@operato/twin-kernel",
3
- "version": "0.4.2",
3
+ "version": "0.4.3",
4
4
  "type": "module",
5
5
  "description": "Twin Domain Kernel — framework-agnostic, zero-dep (domain + sim + 3-channel contract). WMS/YMS/MES, EPCIS 2.0 · ISA-95.",
6
6
  "publishConfig": {