@operato/twin-kernel 0.6.2 → 0.6.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.
- package/dist/contract.d.ts +18 -0
- package/dist/domain-catalog.d.ts +41 -1
- package/dist/domain-catalog.js +61 -1
- package/dist-cjs/index.cjs +62 -0
- package/package.json +1 -1
package/dist/contract.d.ts
CHANGED
|
@@ -806,6 +806,14 @@ export interface AssetState extends EffectivePeriod {
|
|
|
806
806
|
*/
|
|
807
807
|
effectivity?: Effectivity;
|
|
808
808
|
}
|
|
809
|
+
/**
|
|
810
|
+
* 작업 하나의 관측 상태 — **ISA-95 `SegmentResponse`**(Part 4, 실적).
|
|
811
|
+
*
|
|
812
|
+
* 1차 출처(B2MML v0701): `SegmentResponseType`(B2MML-OperationsPerformance.xsd) — 실적 하나에 응답이
|
|
813
|
+
* 여럿 들리고, 응답마다 어느 공정 구간을 언제 누구·무엇으로 했는지가 실린다(`PersonnelActual` ·
|
|
814
|
+
* `EquipmentActual` · `MaterialActual`). 이 계약의 `personnel`·`resourceRef`·`materialActual` 이 그
|
|
815
|
+
* 자리들이다.
|
|
816
|
+
*/
|
|
809
817
|
export interface TaskState {
|
|
810
818
|
id: string;
|
|
811
819
|
kind: string;
|
|
@@ -856,6 +864,16 @@ export interface TaskState {
|
|
|
856
864
|
uom?: string;
|
|
857
865
|
}[];
|
|
858
866
|
}
|
|
867
|
+
/**
|
|
868
|
+
* 오더 하나의 관측 상태 — **ISA-95 `OperationsRequest`**(Part 4, 일정).
|
|
869
|
+
*
|
|
870
|
+
* 1차 출처(B2MML v0701): `OperationsRequestType`(B2MML-OperationsSchedule.xsd) — 일정 하나에 요구가
|
|
871
|
+
* 여럿 들리고, 요구는 `SegmentRequirement` 로 쪼개진다. 여기 담는 것은 **원본이 낸 요구를 트윈이
|
|
872
|
+
* 관측한 것**이라 계획 자체가 아니다(그 구분이 ADR-0030 의 이유다).
|
|
873
|
+
*
|
|
874
|
+
* 실적 쪽 대응은 `TaskState`(`SegmentResponse`) 다 — 요구와 응답을 한 짝으로 읽어야 "얼마나 시켰고
|
|
875
|
+
* 얼마나 됐나" 가 나온다.
|
|
876
|
+
*/
|
|
859
877
|
export interface OrderState {
|
|
860
878
|
id: string;
|
|
861
879
|
kind: string;
|
package/dist/domain-catalog.d.ts
CHANGED
|
@@ -34,15 +34,40 @@ export interface TwinAxisInfo {
|
|
|
34
34
|
* (저장·계약의 이름과 갈라지면 그 순간 방언이 생긴다).
|
|
35
35
|
*/
|
|
36
36
|
axis: string;
|
|
37
|
+
/**
|
|
38
|
+
* 이 축을 **어디서 읽나.**
|
|
39
|
+
*
|
|
40
|
+
* 없으면 `'document'` — 지금까지의 모든 축이 그렇다(선언이고, 인제스트가 쓴다). 그런데 표준이
|
|
41
|
+
* 요구하는 것 중에는 **선언이 아닌 것**이 있다: 작업 일정과 실적은 사람이 적어 두는 값이 아니라
|
|
42
|
+
* 현장이 낳는 사실이고, 그 집은 커널 상태와 저널이다(ISA-95 Part 4).
|
|
43
|
+
*
|
|
44
|
+
* 이 칸이 없으면 축만 늘려도 소비처가 board 에서 찾다가 **언제나 0 을 답한다** — 오류 없이,
|
|
45
|
+
* 그냥 빈 공장처럼. 이 프로젝트가 이미 그 모양으로 한 번 무너졌다(옛 어휘 보드가 자리 0·설비 0).
|
|
46
|
+
*
|
|
47
|
+
* · `document` — 저장된 트윈 모델 안. `path` 가 그 자리를 말한다
|
|
48
|
+
* · `state` — 커널 상태. **지금의 사실**이고 커널이 돌 때만 있다(멈추면 `null`, 0 이 아니다)
|
|
49
|
+
* · `journal` — 저널. 과거까지 있고 커널이 멈춰도 남는다
|
|
50
|
+
*/
|
|
51
|
+
source?: 'document' | 'state' | 'journal';
|
|
52
|
+
/**
|
|
53
|
+
* 이 축이 **시간축을 갖나** — 과거 시점으로 되짚을 수 있는가(저널 폴드).
|
|
54
|
+
*
|
|
55
|
+
* 선언은 시간축이 없다(그 시점의 선언은 구조 리비전이 답한다). 관측은 있다 — 그래서 이 표시가
|
|
56
|
+
* 필요하다: 화면이 시각 커서를 이 축에 걸어도 되는지 알아야 한다.
|
|
57
|
+
*/
|
|
58
|
+
historical?: boolean;
|
|
37
59
|
/**
|
|
38
60
|
* board 안에서의 자리. 최상위면 `axis` 와 같고, 중첩이면 경로다
|
|
39
61
|
* (`productionSpec.definition.recipes`).
|
|
40
62
|
*
|
|
63
|
+
* **`source: 'document'` 일 때만 있다.** 상태·저널 축에는 board 안의 자리가 없다 — 빈 문자열로
|
|
64
|
+
* 두면 소비처가 board 루트를 읽고 통째로 잘못된 답을 만든다.
|
|
65
|
+
*
|
|
41
66
|
* **레시피·라우트는 저장상 `productionSpec` 안에 있지만 개념으로는 1급**이다 — 사람은
|
|
42
67
|
* "레시피" 를 찾지 "생산 정의 안의 레시피" 를 찾지 않는다. 저장 위치가 개념을 가두면
|
|
43
68
|
* 화면이 저장 구조를 흉내 내게 되고, 그건 `ADR-0032`(board 는 캐시) 뒤에 무의미해진다.
|
|
44
69
|
*/
|
|
45
|
-
path
|
|
70
|
+
path?: string;
|
|
46
71
|
/** i18n 키 — 사람 언어는 표현 계층이 렌더한다(L2). */
|
|
47
72
|
label: string;
|
|
48
73
|
/** 표준 온톨로지 투영. 표준에 자리가 없으면 **빈 객체**(숨기지 않는다). */
|
|
@@ -147,6 +172,21 @@ export interface TwinPropertyInfo {
|
|
|
147
172
|
export declare const TWIN_PROPERTIES: TwinPropertyInfo[];
|
|
148
173
|
/** 이 축이 보여 줄 속성들 — 없으면 빈 배열(속성 없는 축이 정상이다). */
|
|
149
174
|
export declare const propertiesOf: (axis: string) => TwinPropertyInfo[];
|
|
175
|
+
/**
|
|
176
|
+
* 이 축은 **선언에서 읽나** — `source` 를 말하지 않은 축은 문서 축이다(기존 14축).
|
|
177
|
+
*
|
|
178
|
+
* 소비처가 `source` 를 직접 비교하면 기본값 규칙이 자리마다 복사되고, 한 곳이 빠지면 그 축은
|
|
179
|
+
* board 루트를 읽어 조용히 잘못된 답을 낸다. 판정은 여기 하나다.
|
|
180
|
+
*/
|
|
181
|
+
export declare const axisSource: (axis: string) => "document" | "state" | "journal";
|
|
182
|
+
/**
|
|
183
|
+
* 문서에서 이 축을 찾을 자리 — **문서 축이 아니면 던진다.**
|
|
184
|
+
*
|
|
185
|
+
* 상태·저널 축에는 board 안의 자리가 없다. 그런데 옛 소비처는 `info.path` 를 무조건 읽었으므로,
|
|
186
|
+
* 그 자리가 `undefined` 면 board 루트를 읽고 **오류 없이 0 을 답한다**(이 프로젝트가 겪은 실패 모양).
|
|
187
|
+
* 그래서 조용히 넘기지 않고 부르는 쪽을 멈춘다.
|
|
188
|
+
*/
|
|
189
|
+
export declare function documentPath(axis: string): string;
|
|
150
190
|
/** 이 축에서 나가는 관계 · 이 축으로 들어오는 관계 — 항목 패널의 "이어진 것" 양방향. */
|
|
151
191
|
export declare const relationsFrom: (axis: string) => TwinRelationInfo[];
|
|
152
192
|
export declare const relationsTo: (axis: string) => TwinRelationInfo[];
|
package/dist/domain-catalog.js
CHANGED
|
@@ -43,7 +43,28 @@ export const TWIN_AXES = [
|
|
|
43
43
|
{ axis: 'routes', path: 'productionSpec.definition.routes', label: 'twin.axis.routes', kind: 'spec',
|
|
44
44
|
standardClass: { isa95: 'OperationsSegment' } },
|
|
45
45
|
{ axis: 'materials', path: 'productionSpec.definition.materials', label: 'twin.axis.materials', kind: 'class',
|
|
46
|
-
standardClass: { isa95: 'MaterialDefinition' } }
|
|
46
|
+
standardClass: { isa95: 'MaterialDefinition' } },
|
|
47
|
+
/*
|
|
48
|
+
* ── ISA-95 Part 4 — 일정과 실적 ────────────────────────────────────────────────
|
|
49
|
+
*
|
|
50
|
+
* 여기부터는 **선언이 아니다.** 오더와 작업은 사람이 board 에 적어 두는 값이 아니라 현장이 낳는
|
|
51
|
+
* 사실이고, 그 집은 커널 상태와 저널이다. 그래서 `source` 가 `'state'` 이고 `path` 가 없다.
|
|
52
|
+
*
|
|
53
|
+
* ── 왜 축이어야 하나 ──────────────────────────────────────────────────────────
|
|
54
|
+
* 이 둘이 축이 아니던 동안 "이 트윈이 표준의 어디를 채우나" 라는 질문은 **절반만** 답했다. Part 2
|
|
55
|
+
* 자원 모델은 여덟이 완전한데 Part 4 는 통째로 비어 있었다 — 런타임에는 실재하는데 조회 모델에는
|
|
56
|
+
* 없었기 때문이다. 실적을 성과 화면이 답한다는 것은 사실이지만, 그건 **집계**이고 개념 자체를
|
|
57
|
+
* 가리켜 걸어 들어갈 자리는 없었다(어느 오더가 어느 공정에서 무엇을 먹었나).
|
|
58
|
+
*
|
|
59
|
+
* ── 왜 테이블로 투영하지 않나 ─────────────────────────────────────────────────
|
|
60
|
+
* 정본이 저널이다. 구조 엔티티는 **원본이 낼 수 있는 것만** 담고(ADR-0032-D), 오더는 원본이 낸
|
|
61
|
+
* 것이지만 트윈이 **관측한** 것이다. 투영은 규모에서 성능이 필요해질 때 KPI 폴드와 같은 급의
|
|
62
|
+
* 별도 결정으로 한다.
|
|
63
|
+
*/
|
|
64
|
+
{ axis: 'orders', label: 'twin.axis.orders', kind: 'instance', source: 'state', historical: true,
|
|
65
|
+
standardClass: { isa95: 'OperationsRequest', epcis: 'TransactionEvent' } },
|
|
66
|
+
{ axis: 'tasks', label: 'twin.axis.tasks', kind: 'instance', source: 'state', historical: true,
|
|
67
|
+
standardClass: { isa95: 'SegmentResponse', epcis: 'TransformationEvent' } }
|
|
47
68
|
];
|
|
48
69
|
/** 관계 전체 — 지도의 선과 항목의 이웃이 여기서 나온다(화면은 이 목록을 갖지 않는다). */
|
|
49
70
|
export const TWIN_RELATIONS = [
|
|
@@ -63,6 +84,23 @@ export const TWIN_RELATIONS = [
|
|
|
63
84
|
{ from: 'recipes', field: 'inputs[].material', target: { kind: 'axis', axis: 'materials' }, via: 'twin.rel.input' },
|
|
64
85
|
{ from: 'recipes', field: 'outputs[].material', target: { kind: 'axis', axis: 'materials' }, via: 'twin.rel.output' },
|
|
65
86
|
{ from: 'routes', field: 'steps[]', target: { kind: 'axis', axis: 'operations' }, via: 'twin.rel.step' },
|
|
87
|
+
/*
|
|
88
|
+
* 일정·실적의 관계 — **관측 관계다.** 선언 관계는 "그렇게 만들기로 했다" 이고, 이것은 "그렇게
|
|
89
|
+
* 일어났다" 다. 관계가 없으면 이 두 축은 지도에서 떠 있는 카드가 된다(그러면 걸어 들어갈 수 없다).
|
|
90
|
+
*/
|
|
91
|
+
/*
|
|
92
|
+
* 오더가 무엇을 만드나 — **자재 키가 아니라 GS1 품목 참조다**(`lines[].gtin`). 자재와 이어 주는
|
|
93
|
+
* 것은 `productionSpec.binding` 이고 그 해소는 호스트가 한다 — 자리·구역과 같은 `external` 이다.
|
|
94
|
+
* 축을 직접 가리키게 적으면 없는 필드를 가리키는 선언이 된다(계약을 보고 고쳤다).
|
|
95
|
+
*/
|
|
96
|
+
{ from: 'orders', field: 'lines[].gtin', target: { kind: 'external', entity: 'gs1.itemRef' }, via: 'twin.rel.item', optional: true },
|
|
97
|
+
{ from: 'tasks', field: 'orderId', target: { kind: 'axis', axis: 'orders' }, via: 'twin.rel.order', optional: true },
|
|
98
|
+
/* 작업의 `kind` 는 그 공정의 키다(라우트의 단계 이름 그대로 — `mes-kernel` 의 공정 목록이 그 증거). */
|
|
99
|
+
{ from: 'tasks', field: 'kind', target: { kind: 'axis', axis: 'operations' }, via: 'twin.rel.operation', optional: true },
|
|
100
|
+
{ from: 'tasks', field: 'fromNode', target: { kind: 'axis', axis: 'locations' }, via: 'twin.rel.from', optional: true },
|
|
101
|
+
{ from: 'tasks', field: 'toNode', target: { kind: 'axis', axis: 'locations' }, via: 'twin.rel.at', optional: true },
|
|
102
|
+
{ from: 'tasks', field: 'resourceRef', target: { kind: 'axis', axis: 'equipment' }, via: 'twin.rel.by', optional: true },
|
|
103
|
+
{ from: 'tasks', field: 'personnel[]', target: { kind: 'axis', axis: 'persons' }, via: 'twin.rel.crew', optional: true },
|
|
66
104
|
/* 공정이 도는 자리·자원 — 항목이 아니라 **타입**을 가리킨다 */
|
|
67
105
|
{ from: 'operations', field: 'locationType', target: { kind: 'type', role: 'location' }, via: 'twin.rel.at', optional: true },
|
|
68
106
|
{ from: 'operations', field: 'resourceType', target: { kind: 'type', role: 'equipment' }, via: 'twin.rel.by', optional: true }
|
|
@@ -89,6 +127,28 @@ export const TWIN_PROPERTIES = [
|
|
|
89
127
|
];
|
|
90
128
|
/** 이 축이 보여 줄 속성들 — 없으면 빈 배열(속성 없는 축이 정상이다). */
|
|
91
129
|
export const propertiesOf = (axis) => TWIN_PROPERTIES.filter(p => p.axis === axis);
|
|
130
|
+
/**
|
|
131
|
+
* 이 축은 **선언에서 읽나** — `source` 를 말하지 않은 축은 문서 축이다(기존 14축).
|
|
132
|
+
*
|
|
133
|
+
* 소비처가 `source` 를 직접 비교하면 기본값 규칙이 자리마다 복사되고, 한 곳이 빠지면 그 축은
|
|
134
|
+
* board 루트를 읽어 조용히 잘못된 답을 낸다. 판정은 여기 하나다.
|
|
135
|
+
*/
|
|
136
|
+
export const axisSource = (axis) => TWIN_AXES.find(a => a.axis === axis)?.source ?? 'document';
|
|
137
|
+
/**
|
|
138
|
+
* 문서에서 이 축을 찾을 자리 — **문서 축이 아니면 던진다.**
|
|
139
|
+
*
|
|
140
|
+
* 상태·저널 축에는 board 안의 자리가 없다. 그런데 옛 소비처는 `info.path` 를 무조건 읽었으므로,
|
|
141
|
+
* 그 자리가 `undefined` 면 board 루트를 읽고 **오류 없이 0 을 답한다**(이 프로젝트가 겪은 실패 모양).
|
|
142
|
+
* 그래서 조용히 넘기지 않고 부르는 쪽을 멈춘다.
|
|
143
|
+
*/
|
|
144
|
+
export function documentPath(axis) {
|
|
145
|
+
const info = TWIN_AXES.find(a => a.axis === axis);
|
|
146
|
+
if (!info)
|
|
147
|
+
throw new Error(`unknown twin axis: "${axis}"`);
|
|
148
|
+
if (axisSource(axis) !== 'document' || !info.path)
|
|
149
|
+
throw new Error(`axis "${axis}" is not read from the document (source: ${axisSource(axis)}) — read it from the ${axisSource(axis)}`);
|
|
150
|
+
return info.path;
|
|
151
|
+
}
|
|
92
152
|
/** 이 축에서 나가는 관계 · 이 축으로 들어오는 관계 — 항목 패널의 "이어진 것" 양방향. */
|
|
93
153
|
export const relationsFrom = (axis) => TWIN_RELATIONS.filter(r => r.from === axis);
|
|
94
154
|
export const relationsTo = (axis) => TWIN_RELATIONS.filter(r => r.target.kind === 'axis' && r.target.axis === axis);
|
package/dist-cjs/index.cjs
CHANGED
|
@@ -71,6 +71,7 @@ __export(index_exports, {
|
|
|
71
71
|
aggregationEvent: () => aggregationEvent,
|
|
72
72
|
analyzeCapacity: () => analyzeCapacity,
|
|
73
73
|
axisInfo: () => axisInfo,
|
|
74
|
+
axisSource: () => axisSource,
|
|
74
75
|
capabilitiesForType: () => capabilitiesForType,
|
|
75
76
|
classClosure: () => classClosure,
|
|
76
77
|
compareStates: () => compareStates,
|
|
@@ -79,6 +80,7 @@ __export(index_exports, {
|
|
|
79
80
|
conversionFactorOf: () => conversionFactorOf,
|
|
80
81
|
counterfactualAt: () => counterfactualAt,
|
|
81
82
|
deriveAttentions: () => deriveAttentions,
|
|
83
|
+
documentPath: () => documentPath,
|
|
82
84
|
dueStatusOf: () => dueStatusOf,
|
|
83
85
|
effectivityAt: () => effectivityAt,
|
|
84
86
|
fefoPolicy: () => fefoPolicy,
|
|
@@ -1672,6 +1674,39 @@ var TWIN_AXES = [
|
|
|
1672
1674
|
label: "twin.axis.materials",
|
|
1673
1675
|
kind: "class",
|
|
1674
1676
|
standardClass: { isa95: "MaterialDefinition" }
|
|
1677
|
+
},
|
|
1678
|
+
/*
|
|
1679
|
+
* ── ISA-95 Part 4 — 일정과 실적 ────────────────────────────────────────────────
|
|
1680
|
+
*
|
|
1681
|
+
* 여기부터는 **선언이 아니다.** 오더와 작업은 사람이 board 에 적어 두는 값이 아니라 현장이 낳는
|
|
1682
|
+
* 사실이고, 그 집은 커널 상태와 저널이다. 그래서 `source` 가 `'state'` 이고 `path` 가 없다.
|
|
1683
|
+
*
|
|
1684
|
+
* ── 왜 축이어야 하나 ──────────────────────────────────────────────────────────
|
|
1685
|
+
* 이 둘이 축이 아니던 동안 "이 트윈이 표준의 어디를 채우나" 라는 질문은 **절반만** 답했다. Part 2
|
|
1686
|
+
* 자원 모델은 여덟이 완전한데 Part 4 는 통째로 비어 있었다 — 런타임에는 실재하는데 조회 모델에는
|
|
1687
|
+
* 없었기 때문이다. 실적을 성과 화면이 답한다는 것은 사실이지만, 그건 **집계**이고 개념 자체를
|
|
1688
|
+
* 가리켜 걸어 들어갈 자리는 없었다(어느 오더가 어느 공정에서 무엇을 먹었나).
|
|
1689
|
+
*
|
|
1690
|
+
* ── 왜 테이블로 투영하지 않나 ─────────────────────────────────────────────────
|
|
1691
|
+
* 정본이 저널이다. 구조 엔티티는 **원본이 낼 수 있는 것만** 담고(ADR-0032-D), 오더는 원본이 낸
|
|
1692
|
+
* 것이지만 트윈이 **관측한** 것이다. 투영은 규모에서 성능이 필요해질 때 KPI 폴드와 같은 급의
|
|
1693
|
+
* 별도 결정으로 한다.
|
|
1694
|
+
*/
|
|
1695
|
+
{
|
|
1696
|
+
axis: "orders",
|
|
1697
|
+
label: "twin.axis.orders",
|
|
1698
|
+
kind: "instance",
|
|
1699
|
+
source: "state",
|
|
1700
|
+
historical: true,
|
|
1701
|
+
standardClass: { isa95: "OperationsRequest", epcis: "TransactionEvent" }
|
|
1702
|
+
},
|
|
1703
|
+
{
|
|
1704
|
+
axis: "tasks",
|
|
1705
|
+
label: "twin.axis.tasks",
|
|
1706
|
+
kind: "instance",
|
|
1707
|
+
source: "state",
|
|
1708
|
+
historical: true,
|
|
1709
|
+
standardClass: { isa95: "SegmentResponse", epcis: "TransformationEvent" }
|
|
1675
1710
|
}
|
|
1676
1711
|
];
|
|
1677
1712
|
var TWIN_RELATIONS = [
|
|
@@ -1691,6 +1726,23 @@ var TWIN_RELATIONS = [
|
|
|
1691
1726
|
{ from: "recipes", field: "inputs[].material", target: { kind: "axis", axis: "materials" }, via: "twin.rel.input" },
|
|
1692
1727
|
{ from: "recipes", field: "outputs[].material", target: { kind: "axis", axis: "materials" }, via: "twin.rel.output" },
|
|
1693
1728
|
{ from: "routes", field: "steps[]", target: { kind: "axis", axis: "operations" }, via: "twin.rel.step" },
|
|
1729
|
+
/*
|
|
1730
|
+
* 일정·실적의 관계 — **관측 관계다.** 선언 관계는 "그렇게 만들기로 했다" 이고, 이것은 "그렇게
|
|
1731
|
+
* 일어났다" 다. 관계가 없으면 이 두 축은 지도에서 떠 있는 카드가 된다(그러면 걸어 들어갈 수 없다).
|
|
1732
|
+
*/
|
|
1733
|
+
/*
|
|
1734
|
+
* 오더가 무엇을 만드나 — **자재 키가 아니라 GS1 품목 참조다**(`lines[].gtin`). 자재와 이어 주는
|
|
1735
|
+
* 것은 `productionSpec.binding` 이고 그 해소는 호스트가 한다 — 자리·구역과 같은 `external` 이다.
|
|
1736
|
+
* 축을 직접 가리키게 적으면 없는 필드를 가리키는 선언이 된다(계약을 보고 고쳤다).
|
|
1737
|
+
*/
|
|
1738
|
+
{ from: "orders", field: "lines[].gtin", target: { kind: "external", entity: "gs1.itemRef" }, via: "twin.rel.item", optional: true },
|
|
1739
|
+
{ from: "tasks", field: "orderId", target: { kind: "axis", axis: "orders" }, via: "twin.rel.order", optional: true },
|
|
1740
|
+
/* 작업의 `kind` 는 그 공정의 키다(라우트의 단계 이름 그대로 — `mes-kernel` 의 공정 목록이 그 증거). */
|
|
1741
|
+
{ from: "tasks", field: "kind", target: { kind: "axis", axis: "operations" }, via: "twin.rel.operation", optional: true },
|
|
1742
|
+
{ from: "tasks", field: "fromNode", target: { kind: "axis", axis: "locations" }, via: "twin.rel.from", optional: true },
|
|
1743
|
+
{ from: "tasks", field: "toNode", target: { kind: "axis", axis: "locations" }, via: "twin.rel.at", optional: true },
|
|
1744
|
+
{ from: "tasks", field: "resourceRef", target: { kind: "axis", axis: "equipment" }, via: "twin.rel.by", optional: true },
|
|
1745
|
+
{ from: "tasks", field: "personnel[]", target: { kind: "axis", axis: "persons" }, via: "twin.rel.crew", optional: true },
|
|
1694
1746
|
/* 공정이 도는 자리·자원 — 항목이 아니라 **타입**을 가리킨다 */
|
|
1695
1747
|
{ from: "operations", field: "locationType", target: { kind: "type", role: "location" }, via: "twin.rel.at", optional: true },
|
|
1696
1748
|
{ from: "operations", field: "resourceType", target: { kind: "type", role: "equipment" }, via: "twin.rel.by", optional: true }
|
|
@@ -1744,6 +1796,14 @@ var TWIN_PROPERTIES = [
|
|
|
1744
1796
|
}
|
|
1745
1797
|
];
|
|
1746
1798
|
var propertiesOf = (axis) => TWIN_PROPERTIES.filter((p) => p.axis === axis);
|
|
1799
|
+
var axisSource = (axis) => TWIN_AXES.find((a) => a.axis === axis)?.source ?? "document";
|
|
1800
|
+
function documentPath(axis) {
|
|
1801
|
+
const info = TWIN_AXES.find((a) => a.axis === axis);
|
|
1802
|
+
if (!info) throw new Error(`unknown twin axis: "${axis}"`);
|
|
1803
|
+
if (axisSource(axis) !== "document" || !info.path)
|
|
1804
|
+
throw new Error(`axis "${axis}" is not read from the document (source: ${axisSource(axis)}) \u2014 read it from the ${axisSource(axis)}`);
|
|
1805
|
+
return info.path;
|
|
1806
|
+
}
|
|
1747
1807
|
var relationsFrom = (axis) => TWIN_RELATIONS.filter((r) => r.from === axis);
|
|
1748
1808
|
var relationsTo = (axis) => TWIN_RELATIONS.filter((r) => r.target.kind === "axis" && r.target.axis === axis);
|
|
1749
1809
|
function axisInfo(axis) {
|
|
@@ -4986,6 +5046,7 @@ function retiredVocabularyIn(line) {
|
|
|
4986
5046
|
aggregationEvent,
|
|
4987
5047
|
analyzeCapacity,
|
|
4988
5048
|
axisInfo,
|
|
5049
|
+
axisSource,
|
|
4989
5050
|
capabilitiesForType,
|
|
4990
5051
|
classClosure,
|
|
4991
5052
|
compareStates,
|
|
@@ -4994,6 +5055,7 @@ function retiredVocabularyIn(line) {
|
|
|
4994
5055
|
conversionFactorOf,
|
|
4995
5056
|
counterfactualAt,
|
|
4996
5057
|
deriveAttentions,
|
|
5058
|
+
documentPath,
|
|
4997
5059
|
dueStatusOf,
|
|
4998
5060
|
effectivityAt,
|
|
4999
5061
|
fefoPolicy,
|
package/package.json
CHANGED