@things-factory/ops-master 10.0.18 → 10.1.0

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.
Files changed (269) hide show
  1. package/dist-client/bootstrap.d.ts +18 -0
  2. package/dist-client/bootstrap.js +36 -0
  3. package/dist-client/bootstrap.js.map +1 -0
  4. package/dist-client/index.d.ts +15 -1
  5. package/dist-client/index.js +18 -1
  6. package/dist-client/index.js.map +1 -1
  7. package/dist-client/pages/master/equipment-class.d.ts +12 -0
  8. package/dist-client/pages/master/equipment-class.js +42 -0
  9. package/dist-client/pages/master/equipment-class.js.map +1 -0
  10. package/dist-client/pages/master/equipment.d.ts +12 -0
  11. package/dist-client/pages/master/equipment.js +57 -0
  12. package/dist-client/pages/master/equipment.js.map +1 -0
  13. package/dist-client/pages/master/grist-iso-duration.d.ts +20 -0
  14. package/dist-client/pages/master/grist-iso-duration.js +70 -0
  15. package/dist-client/pages/master/grist-iso-duration.js.map +1 -0
  16. package/dist-client/pages/master/grist-reference.d.ts +91 -0
  17. package/dist-client/pages/master/grist-reference.js +419 -0
  18. package/dist-client/pages/master/grist-reference.js.map +1 -0
  19. package/dist-client/pages/master/import-rows.d.ts +35 -0
  20. package/dist-client/pages/master/import-rows.js +66 -0
  21. package/dist-client/pages/master/import-rows.js.map +1 -0
  22. package/dist-client/pages/master/iso-duration.d.ts +34 -0
  23. package/dist-client/pages/master/iso-duration.js +76 -0
  24. package/dist-client/pages/master/iso-duration.js.map +1 -0
  25. package/dist-client/pages/master/master-options.d.ts +33 -0
  26. package/dist-client/pages/master/master-options.js +47 -0
  27. package/dist-client/pages/master/master-options.js.map +1 -0
  28. package/dist-client/pages/master/master-page-base.d.ts +357 -0
  29. package/dist-client/pages/master/master-page-base.js +626 -0
  30. package/dist-client/pages/master/master-page-base.js.map +1 -0
  31. package/dist-client/pages/master/material-class.d.ts +12 -0
  32. package/dist-client/pages/master/material-class.js +42 -0
  33. package/dist-client/pages/master/material-class.js.map +1 -0
  34. package/dist-client/pages/master/material-definition.d.ts +12 -0
  35. package/dist-client/pages/master/material-definition.js +62 -0
  36. package/dist-client/pages/master/material-definition.js.map +1 -0
  37. package/dist-client/pages/master/operation-definition.d.ts +12 -0
  38. package/dist-client/pages/master/operation-definition.js +49 -0
  39. package/dist-client/pages/master/operation-definition.js.map +1 -0
  40. package/dist-client/pages/master/operational-location.d.ts +84 -0
  41. package/dist-client/pages/master/operational-location.js +426 -0
  42. package/dist-client/pages/master/operational-location.js.map +1 -0
  43. package/dist-client/pages/master/person.d.ts +12 -0
  44. package/dist-client/pages/master/person.js +41 -0
  45. package/dist-client/pages/master/person.js.map +1 -0
  46. package/dist-client/pages/master/personnel-class.d.ts +12 -0
  47. package/dist-client/pages/master/personnel-class.js +43 -0
  48. package/dist-client/pages/master/personnel-class.js.map +1 -0
  49. package/dist-client/pages/master/physical-asset-class.d.ts +12 -0
  50. package/dist-client/pages/master/physical-asset-class.js +40 -0
  51. package/dist-client/pages/master/physical-asset-class.js.map +1 -0
  52. package/dist-client/pages/master/physical-asset.d.ts +12 -0
  53. package/dist-client/pages/master/physical-asset.js +41 -0
  54. package/dist-client/pages/master/physical-asset.js.map +1 -0
  55. package/dist-client/pages/master/recipe-definition.d.ts +136 -0
  56. package/dist-client/pages/master/recipe-definition.js +404 -0
  57. package/dist-client/pages/master/recipe-definition.js.map +1 -0
  58. package/dist-client/pages/master/reference-targets.d.ts +42 -0
  59. package/dist-client/pages/master/reference-targets.js +82 -0
  60. package/dist-client/pages/master/reference-targets.js.map +1 -0
  61. package/dist-client/pages/master/route-definition.d.ts +19 -0
  62. package/dist-client/pages/master/route-definition.js +40 -0
  63. package/dist-client/pages/master/route-definition.js.map +1 -0
  64. package/dist-client/pages/master/stop-reason.d.ts +12 -0
  65. package/dist-client/pages/master/stop-reason.js +48 -0
  66. package/dist-client/pages/master/stop-reason.js.map +1 -0
  67. package/dist-client/pages/master/test-specification.d.ts +93 -0
  68. package/dist-client/pages/master/test-specification.js +449 -0
  69. package/dist-client/pages/master/test-specification.js.map +1 -0
  70. package/dist-client/pages/master-axes.d.ts +20 -29
  71. package/dist-client/pages/master-axes.js +32 -169
  72. package/dist-client/pages/master-axes.js.map +1 -1
  73. package/dist-client/route.d.ts +1 -1
  74. package/dist-client/route.js +76 -3
  75. package/dist-client/route.js.map +1 -1
  76. package/dist-client/tsconfig.tsbuildinfo +1 -1
  77. package/dist-server/service/asset/asset-lifecycle.d.ts +30 -7
  78. package/dist-server/service/asset/asset-lifecycle.js +33 -6
  79. package/dist-server/service/asset/asset-lifecycle.js.map +1 -1
  80. package/dist-server/service/asset/asset-master-resolver.d.ts +9 -0
  81. package/dist-server/service/asset/asset-master-resolver.js +41 -0
  82. package/dist-server/service/asset/asset-master-resolver.js.map +1 -1
  83. package/dist-server/service/asset/asset-master.js +1 -1
  84. package/dist-server/service/asset/asset-master.js.map +1 -1
  85. package/dist-server/service/asset/asset-query.d.ts +9 -1
  86. package/dist-server/service/asset/asset-query.js +28 -6
  87. package/dist-server/service/asset/asset-query.js.map +1 -1
  88. package/dist-server/service/asset/physical-asset-class.js +1 -1
  89. package/dist-server/service/asset/physical-asset-class.js.map +1 -1
  90. package/dist-server/service/asset/physical-asset.d.ts +17 -4
  91. package/dist-server/service/asset/physical-asset.js +6 -6
  92. package/dist-server/service/asset/physical-asset.js.map +1 -1
  93. package/dist-server/service/equipment/equipment-class.js +1 -1
  94. package/dist-server/service/equipment/equipment-class.js.map +1 -1
  95. package/dist-server/service/equipment/equipment-master-resolver.d.ts +9 -0
  96. package/dist-server/service/equipment/equipment-master-resolver.js +58 -0
  97. package/dist-server/service/equipment/equipment-master-resolver.js.map +1 -1
  98. package/dist-server/service/equipment/equipment-master.d.ts +3 -2
  99. package/dist-server/service/equipment/equipment-master.js +21 -4
  100. package/dist-server/service/equipment/equipment-master.js.map +1 -1
  101. package/dist-server/service/equipment/equipment-query.d.ts +9 -1
  102. package/dist-server/service/equipment/equipment-query.js +28 -6
  103. package/dist-server/service/equipment/equipment-query.js.map +1 -1
  104. package/dist-server/service/equipment/equipment.d.ts +27 -1
  105. package/dist-server/service/equipment/equipment.js +8 -3
  106. package/dist-server/service/equipment/equipment.js.map +1 -1
  107. package/dist-server/service/equipment/state-period-rules.d.ts +19 -0
  108. package/dist-server/service/equipment/state-period-rules.js +35 -1
  109. package/dist-server/service/equipment/state-period-rules.js.map +1 -1
  110. package/dist-server/service/index.d.ts +12 -2
  111. package/dist-server/service/index.js +21 -1
  112. package/dist-server/service/index.js.map +1 -1
  113. package/dist-server/service/location/location-master-resolver.d.ts +29 -0
  114. package/dist-server/service/location/location-master-resolver.js +228 -1
  115. package/dist-server/service/location/location-master-resolver.js.map +1 -1
  116. package/dist-server/service/location/location-query.d.ts +9 -1
  117. package/dist-server/service/location/location-query.js +24 -9
  118. package/dist-server/service/location/location-query.js.map +1 -1
  119. package/dist-server/service/location/operational-location.d.ts +37 -0
  120. package/dist-server/service/location/operational-location.js +22 -2
  121. package/dist-server/service/location/operational-location.js.map +1 -1
  122. package/dist-server/service/master-axis.d.ts +1 -1
  123. package/dist-server/service/master-axis.js +16 -1
  124. package/dist-server/service/master-axis.js.map +1 -1
  125. package/dist-server/service/master-conformance-resolver.d.ts +34 -0
  126. package/dist-server/service/master-conformance-resolver.js +143 -0
  127. package/dist-server/service/master-conformance-resolver.js.map +1 -0
  128. package/dist-server/service/master-conformance.d.ts +80 -0
  129. package/dist-server/service/master-conformance.js +266 -0
  130. package/dist-server/service/master-conformance.js.map +1 -0
  131. package/dist-server/service/master-page-query.d.ts +33 -0
  132. package/dist-server/service/master-page-query.js +55 -0
  133. package/dist-server/service/master-page-query.js.map +1 -0
  134. package/dist-server/service/master-page.d.ts +85 -0
  135. package/dist-server/service/master-page.js +104 -0
  136. package/dist-server/service/master-page.js.map +1 -0
  137. package/dist-server/service/material/material-class.js +1 -1
  138. package/dist-server/service/material/material-class.js.map +1 -1
  139. package/dist-server/service/material/material-definition.js +2 -2
  140. package/dist-server/service/material/material-definition.js.map +1 -1
  141. package/dist-server/service/material/material-master-resolver.d.ts +62 -0
  142. package/dist-server/service/material/material-master-resolver.js +213 -0
  143. package/dist-server/service/material/material-master-resolver.js.map +1 -1
  144. package/dist-server/service/material/material-query.d.ts +9 -1
  145. package/dist-server/service/material/material-query.js +28 -6
  146. package/dist-server/service/material/material-query.js.map +1 -1
  147. package/dist-server/service/material/recipe-index.d.ts +23 -0
  148. package/dist-server/service/material/recipe-index.js +44 -0
  149. package/dist-server/service/material/recipe-index.js.map +1 -0
  150. package/dist-server/service/operation/index.d.ts +2 -1
  151. package/dist-server/service/operation/index.js +2 -1
  152. package/dist-server/service/operation/index.js.map +1 -1
  153. package/dist-server/service/operation/operation-definition.d.ts +1 -1
  154. package/dist-server/service/operation/operation-definition.js +4 -4
  155. package/dist-server/service/operation/operation-definition.js.map +1 -1
  156. package/dist-server/service/operation/operation-master-resolver.d.ts +24 -0
  157. package/dist-server/service/operation/operation-master-resolver.js +107 -0
  158. package/dist-server/service/operation/operation-master-resolver.js.map +1 -1
  159. package/dist-server/service/operation/operation-query.d.ts +10 -2
  160. package/dist-server/service/operation/operation-query.js +25 -10
  161. package/dist-server/service/operation/operation-query.js.map +1 -1
  162. package/dist-server/service/operation/operation-readiness-resolver.d.ts +39 -0
  163. package/dist-server/service/operation/operation-readiness-resolver.js +111 -0
  164. package/dist-server/service/operation/operation-readiness-resolver.js.map +1 -0
  165. package/dist-server/service/operation/operation-readiness.d.ts +34 -0
  166. package/dist-server/service/operation/operation-readiness.js +133 -0
  167. package/dist-server/service/operation/operation-readiness.js.map +1 -0
  168. package/dist-server/service/personnel/person.js +3 -3
  169. package/dist-server/service/personnel/person.js.map +1 -1
  170. package/dist-server/service/personnel/personnel-class-readiness.d.ts +22 -0
  171. package/dist-server/service/personnel/personnel-class-readiness.js +98 -0
  172. package/dist-server/service/personnel/personnel-class-readiness.js.map +1 -0
  173. package/dist-server/service/personnel/personnel-class.d.ts +1 -1
  174. package/dist-server/service/personnel/personnel-class.js +2 -2
  175. package/dist-server/service/personnel/personnel-class.js.map +1 -1
  176. package/dist-server/service/personnel/personnel-master-resolver.d.ts +9 -0
  177. package/dist-server/service/personnel/personnel-master-resolver.js +43 -0
  178. package/dist-server/service/personnel/personnel-master-resolver.js.map +1 -1
  179. package/dist-server/service/personnel/personnel-master.js +1 -1
  180. package/dist-server/service/personnel/personnel-master.js.map +1 -1
  181. package/dist-server/service/personnel/personnel-query.d.ts +9 -1
  182. package/dist-server/service/personnel/personnel-query.js +28 -6
  183. package/dist-server/service/personnel/personnel-query.js.map +1 -1
  184. package/dist-server/service/personnel/qualification.d.ts +7 -0
  185. package/dist-server/service/personnel/qualification.js +25 -0
  186. package/dist-server/service/personnel/qualification.js.map +1 -1
  187. package/dist-server/service/recipe/index.d.ts +9 -0
  188. package/dist-server/service/recipe/index.js +14 -0
  189. package/dist-server/service/recipe/index.js.map +1 -0
  190. package/dist-server/service/recipe/recipe-definition.d.ts +71 -0
  191. package/dist-server/service/recipe/recipe-definition.js +130 -0
  192. package/dist-server/service/recipe/recipe-definition.js.map +1 -0
  193. package/dist-server/service/recipe/recipe-master-resolver.d.ts +28 -0
  194. package/dist-server/service/recipe/recipe-master-resolver.js +124 -0
  195. package/dist-server/service/recipe/recipe-master-resolver.js.map +1 -0
  196. package/dist-server/service/recipe/recipe-master.d.ts +37 -0
  197. package/dist-server/service/recipe/recipe-master.js +47 -0
  198. package/dist-server/service/recipe/recipe-master.js.map +1 -0
  199. package/dist-server/service/recipe/recipe-query.d.ts +21 -0
  200. package/dist-server/service/recipe/recipe-query.js +75 -0
  201. package/dist-server/service/recipe/recipe-query.js.map +1 -0
  202. package/dist-server/service/recipe/route-definition.d.ts +51 -0
  203. package/dist-server/service/recipe/route-definition.js +113 -0
  204. package/dist-server/service/recipe/route-definition.js.map +1 -0
  205. package/dist-server/service/resource/class-lineage.d.ts +46 -0
  206. package/dist-server/service/resource/class-lineage.js +80 -0
  207. package/dist-server/service/resource/class-lineage.js.map +1 -0
  208. package/dist-server/service/resource/class-members.d.ts +36 -0
  209. package/dist-server/service/resource/class-members.js +50 -0
  210. package/dist-server/service/resource/class-members.js.map +1 -0
  211. package/dist-server/service/resource/referrers.d.ts +54 -0
  212. package/dist-server/service/resource/referrers.js +99 -0
  213. package/dist-server/service/resource/referrers.js.map +1 -0
  214. package/dist-server/service/status/master-status.d.ts +0 -7
  215. package/dist-server/service/status/master-status.js +5 -6
  216. package/dist-server/service/status/master-status.js.map +1 -1
  217. package/dist-server/service/test-spec/criteria-check.d.ts +53 -0
  218. package/dist-server/service/test-spec/criteria-check.js +142 -0
  219. package/dist-server/service/test-spec/criteria-check.js.map +1 -0
  220. package/dist-server/service/test-spec/criteria-rows.d.ts +89 -0
  221. package/dist-server/service/test-spec/criteria-rows.js +139 -0
  222. package/dist-server/service/test-spec/criteria-rows.js.map +1 -0
  223. package/dist-server/service/test-spec/test-spec-master-resolver.d.ts +33 -7
  224. package/dist-server/service/test-spec/test-spec-master-resolver.js +137 -2
  225. package/dist-server/service/test-spec/test-spec-master-resolver.js.map +1 -1
  226. package/dist-server/service/test-spec/test-spec-query.d.ts +9 -1
  227. package/dist-server/service/test-spec/test-spec-query.js +23 -9
  228. package/dist-server/service/test-spec/test-spec-query.js.map +1 -1
  229. package/dist-server/service/test-spec/test-specification.js +1 -1
  230. package/dist-server/service/test-spec/test-specification.js.map +1 -1
  231. package/dist-server/tsconfig.tsbuildinfo +1 -1
  232. package/helps/ops-master/equipment-class.ko.md +11 -0
  233. package/helps/ops-master/equipment-class.md +11 -0
  234. package/helps/ops-master/equipment.ko.md +11 -0
  235. package/helps/ops-master/equipment.md +11 -0
  236. package/helps/ops-master/material-class.ko.md +11 -0
  237. package/helps/ops-master/material-class.md +11 -0
  238. package/helps/ops-master/material-definition.ko.md +11 -0
  239. package/helps/ops-master/material-definition.md +11 -0
  240. package/helps/ops-master/operation-definition.ko.md +11 -0
  241. package/helps/ops-master/operation-definition.md +11 -0
  242. package/helps/ops-master/operational-location.ko.md +11 -0
  243. package/helps/ops-master/operational-location.md +11 -0
  244. package/helps/ops-master/person.ko.md +11 -0
  245. package/helps/ops-master/person.md +11 -0
  246. package/helps/ops-master/personnel-class.ko.md +11 -0
  247. package/helps/ops-master/personnel-class.md +11 -0
  248. package/helps/ops-master/physical-asset-class.ko.md +11 -0
  249. package/helps/ops-master/physical-asset-class.md +11 -0
  250. package/helps/ops-master/physical-asset.ko.md +11 -0
  251. package/helps/ops-master/physical-asset.md +11 -0
  252. package/helps/ops-master/recipe-definition.ko.md +11 -0
  253. package/helps/ops-master/recipe-definition.md +11 -0
  254. package/helps/ops-master/route-definition.ko.md +11 -0
  255. package/helps/ops-master/route-definition.md +11 -0
  256. package/helps/ops-master/stop-reason.ko.md +11 -0
  257. package/helps/ops-master/stop-reason.md +11 -0
  258. package/helps/ops-master/test-specification.ko.md +11 -0
  259. package/helps/ops-master/test-specification.md +11 -0
  260. package/package.json +5 -5
  261. package/things-factory.config.js +22 -2
  262. package/translations/en.json +89 -6
  263. package/translations/ja.json +96 -1
  264. package/translations/ko.json +96 -13
  265. package/translations/ms.json +96 -1
  266. package/translations/zh.json +96 -1
  267. package/dist-client/pages/master-list.d.ts +0 -68
  268. package/dist-client/pages/master-list.js +0 -267
  269. package/dist-client/pages/master-list.js.map +0 -1
@@ -0,0 +1,80 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.rootClassesOf = rootClassesOf;
4
+ exports.classPathOf = classPathOf;
5
+ exports.rootClassesOfMaterial = rootClassesOfMaterial;
6
+ /**
7
+ * 한 등급에서 위로 걸어 **뿌리 등급**들을 낸다.
8
+ *
9
+ * 뿌리는 `baseIds` 가 없는 등급이다. 「완제품」·「반제품」·「원재료」처럼 더 위가 없는 것.
10
+ *
11
+ * 답이 여럿일 수 있다 — 한 등급이 두 갈래를 상속하면 뿌리가 둘이다. **하나로 줄이지 않는다.**
12
+ * 줄이면 화면이 「완제품」이라고 단정하는데 실제로는 「완제품이면서 반제품」인 모순을 감춘다. 여럿이면
13
+ * 여럿으로 내고, 그것을 본 사람이 등급을 고치게 한다.
14
+ */
15
+ function rootClassesOf(className, index) {
16
+ const roots = [];
17
+ const seen = new Set();
18
+ const walk = (name) => {
19
+ /*
20
+ * **고리를 밟으면 멈춘다.** 화면에서 `baseIds` 를 손으로 넣을 수 있으니 A→B→A 가 만들어질 수
21
+ * 있다. 그때 이 함수가 안 돌아오면 목록 한 줄이 서버를 세운다.
22
+ */
23
+ if (seen.has(name)) {
24
+ return;
25
+ }
26
+ seen.add(name);
27
+ const klass = index.get(name);
28
+ /*
29
+ * **모르는 등급은 그 자체를 뿌리로 낸다.** 등급 표에 없는 이름이 `baseIds` 에 적혀 있을 수 있다
30
+ * (지워졌거나 오타). 조용히 버리면 계보가 짧아진 채 그럴듯해 보인다 — 이름을 그대로 내서 이상한
31
+ * 것이 화면에 보이게 한다.
32
+ */
33
+ if (!klass) {
34
+ roots.push(name);
35
+ return;
36
+ }
37
+ const bases = (klass.baseIds ?? []).filter(one => one);
38
+ if (bases.length === 0) {
39
+ roots.push(name);
40
+ return;
41
+ }
42
+ for (const base of bases) {
43
+ walk(base);
44
+ }
45
+ };
46
+ walk(className);
47
+ return [...new Set(roots)];
48
+ }
49
+ /**
50
+ * 한 등급에서 뿌리까지의 **경로**를 낸다 — 뿌리가 앞, 자기가 뒤.
51
+ *
52
+ * `['원재료', 'ECU 기구부품']` 처럼 읽는다. 화면이 이것을 그대로 보이면 「이 품목이 무엇인가」를
53
+ * 사람이 한 번에 읽는다.
54
+ *
55
+ * 갈래가 여럿이면 **첫 갈래만** 낸다. 경로는 보여 주기 위한 것이고, 판정은 `rootClassesOf` 로 한다.
56
+ * 여기서 여러 경로를 다 내면 화면 한 칸에 못 담는다.
57
+ */
58
+ function classPathOf(className, index) {
59
+ const path = [];
60
+ const seen = new Set();
61
+ let current = className;
62
+ while (current && !seen.has(current)) {
63
+ seen.add(current);
64
+ path.unshift(current);
65
+ const bases = (index.get(current)?.baseIds ?? []).filter(one => one);
66
+ current = bases[0];
67
+ }
68
+ return path;
69
+ }
70
+ /**
71
+ * 품목 하나가 어느 뿌리 등급들에 드는가 — 등급이 여럿이니 뿌리도 여럿일 수 있다.
72
+ *
73
+ * **등급이 하나도 없으면 빈 배열이다.** 「원재료」로 넘겨짚지 않는다 — 등급을 안 붙인 품목과 원재료를
74
+ * 섞으면, 붙이는 것을 잊었다는 사실이 화면에서 사라진다.
75
+ */
76
+ function rootClassesOfMaterial(classNames, index) {
77
+ const roots = (classNames ?? []).flatMap(one => rootClassesOf(one, index));
78
+ return [...new Set(roots)];
79
+ }
80
+ //# sourceMappingURL=class-lineage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"class-lineage.js","sourceRoot":"","sources":["../../../server/service/resource/class-lineage.ts"],"names":[],"mappings":";;AA+BA,sCAyCC;AAWD,kCAeC;AAQD,sDAIC;AAxFD;;;;;;;;GAQG;AACH,SAAgB,aAAa,CAAC,SAAiB,EAAE,KAAiB;IAChE,MAAM,KAAK,GAAa,EAAE,CAAA;IAC1B,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAA;IAE9B,MAAM,IAAI,GAAG,CAAC,IAAY,EAAE,EAAE;QAC5B;;;WAGG;QACH,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;YACnB,OAAM;QACR,CAAC;QACD,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;QAEd,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;QAE7B;;;;WAIG;QACH,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;YAChB,OAAM;QACR,CAAC;QAED,MAAM,KAAK,GAAG,CAAC,KAAK,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,CAAA;QAEtD,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACvB,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;YAChB,OAAM;QACR,CAAC;QAED,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,IAAI,CAAC,IAAI,CAAC,CAAA;QACZ,CAAC;IACH,CAAC,CAAA;IAED,IAAI,CAAC,SAAS,CAAC,CAAA;IAEf,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,CAAA;AAC5B,CAAC;AAED;;;;;;;;GAQG;AACH,SAAgB,WAAW,CAAC,SAAiB,EAAE,KAAiB;IAC9D,MAAM,IAAI,GAAa,EAAE,CAAA;IACzB,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAA;IAE9B,IAAI,OAAO,GAAuB,SAAS,CAAA;IAE3C,OAAO,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;QACrC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA;QACjB,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAA;QAErB,MAAM,KAAK,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,OAAO,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,CAAA;QACpE,OAAO,GAAG,KAAK,CAAC,CAAC,CAAC,CAAA;IACpB,CAAC;IAED,OAAO,IAAI,CAAA;AACb,CAAC;AAED;;;;;GAKG;AACH,SAAgB,qBAAqB,CAAC,UAAgC,EAAE,KAAiB;IACvF,MAAM,KAAK,GAAG,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,aAAa,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAA;IAE1E,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,CAAA;AAC5B,CAAC","sourcesContent":["import type { ResourceClassFields } from './resource-class'\n\n/**\n * 등급 상속을 걷는다 — **`baseIds` 는 네 등급 표에 다 선언되어 있는데 아무도 걷지 않았다.**\n *\n * 표준 `MaterialClassBaseID` · `PersonnelClassBaseID` 는 등급이 등급을 상속한다고 말한다. 우리 표에도\n * 칸이 있고 화면에서 편집도 되고 트윈에도 나간다. 그런데 그 값을 **읽어서 무엇을 답하는 곳이 한 군데도\n * 없었다.** 그래서 「이 품목이 완제품인가」를 물으면 등급 이름 `MC-ECU-FG` 가 나왔다 — 사람은 읽지만\n * 화면은 못 읽는다.\n *\n * ISA-95 에는 「완제품/반제품/원재료」를 담는 전용 요소가 없다. `MaterialDefinitionType` 을 전수로 보면\n * `ID` · `Version` · `Description*` · `PublishedDate` · `EffectiveStartDate`/`EndDate` · `HierarchyScope` ·\n * `SpatialDefinition` · `MaterialDefinitionProperty*` · `MaterialClassID*` · `MaterialLotSourceID*` ·\n * `TestSpecificationID*` · `AssemblyDefinition*` 뿐이다. 표준이 그 구분을 두는 자리는 **등급**이고,\n * 등급 표의 주석도 「원자재·완제품처럼 품목을 묶는 상위 개념」이라고 적고 있다.\n *\n * 그러니 새 축을 만들지 않는다. 이미 있는 상속을 걷는다.\n */\n\n/** 등급 이름 → 등급. 이름이 등급의 식별자다(표준 `MaterialClassID`). */\nexport type ClassIndex = ReadonlyMap<string, ResourceClassFields>\n\n/**\n * 한 등급에서 위로 걸어 **뿌리 등급**들을 낸다.\n *\n * 뿌리는 `baseIds` 가 없는 등급이다. 「완제품」·「반제품」·「원재료」처럼 더 위가 없는 것.\n *\n * 답이 여럿일 수 있다 — 한 등급이 두 갈래를 상속하면 뿌리가 둘이다. **하나로 줄이지 않는다.**\n * 줄이면 화면이 「완제품」이라고 단정하는데 실제로는 「완제품이면서 반제품」인 모순을 감춘다. 여럿이면\n * 여럿으로 내고, 그것을 본 사람이 등급을 고치게 한다.\n */\nexport function rootClassesOf(className: string, index: ClassIndex): string[] {\n const roots: string[] = []\n const seen = new Set<string>()\n\n const walk = (name: string) => {\n /*\n * **고리를 밟으면 멈춘다.** 화면에서 `baseIds` 를 손으로 넣을 수 있으니 A→B→A 가 만들어질 수\n * 있다. 그때 이 함수가 안 돌아오면 목록 한 줄이 서버를 세운다.\n */\n if (seen.has(name)) {\n return\n }\n seen.add(name)\n\n const klass = index.get(name)\n\n /*\n * **모르는 등급은 그 자체를 뿌리로 낸다.** 등급 표에 없는 이름이 `baseIds` 에 적혀 있을 수 있다\n * (지워졌거나 오타). 조용히 버리면 계보가 짧아진 채 그럴듯해 보인다 — 이름을 그대로 내서 이상한\n * 것이 화면에 보이게 한다.\n */\n if (!klass) {\n roots.push(name)\n return\n }\n\n const bases = (klass.baseIds ?? []).filter(one => one)\n\n if (bases.length === 0) {\n roots.push(name)\n return\n }\n\n for (const base of bases) {\n walk(base)\n }\n }\n\n walk(className)\n\n return [...new Set(roots)]\n}\n\n/**\n * 한 등급에서 뿌리까지의 **경로**를 낸다 — 뿌리가 앞, 자기가 뒤.\n *\n * `['원재료', 'ECU 기구부품']` 처럼 읽는다. 화면이 이것을 그대로 보이면 「이 품목이 무엇인가」를\n * 사람이 한 번에 읽는다.\n *\n * 갈래가 여럿이면 **첫 갈래만** 낸다. 경로는 보여 주기 위한 것이고, 판정은 `rootClassesOf` 로 한다.\n * 여기서 여러 경로를 다 내면 화면 한 칸에 못 담는다.\n */\nexport function classPathOf(className: string, index: ClassIndex): string[] {\n const path: string[] = []\n const seen = new Set<string>()\n\n let current: string | undefined = className\n\n while (current && !seen.has(current)) {\n seen.add(current)\n path.unshift(current)\n\n const bases = (index.get(current)?.baseIds ?? []).filter(one => one)\n current = bases[0]\n }\n\n return path\n}\n\n/**\n * 품목 하나가 어느 뿌리 등급들에 드는가 — 등급이 여럿이니 뿌리도 여럿일 수 있다.\n *\n * **등급이 하나도 없으면 빈 배열이다.** 「원재료」로 넘겨짚지 않는다 — 등급을 안 붙인 품목과 원재료를\n * 섞으면, 붙이는 것을 잊었다는 사실이 화면에서 사라진다.\n */\nexport function rootClassesOfMaterial(classNames: string[] | undefined, index: ClassIndex): string[] {\n const roots = (classNames ?? []).flatMap(one => rootClassesOf(one, index))\n\n return [...new Set(roots)]\n}\n"]}
@@ -0,0 +1,36 @@
1
+ import { Domain } from '@things-factory/shell';
2
+ /**
3
+ * **이 등급에 무엇이 드나** — 등급 넷이 나눠 쓰는 한 벌.
4
+ *
5
+ * ── 왜 필요한가 ────────────────────────────────────────────────────────────
6
+ * 등급을 만들어 놓고 거기 아무도 안 들어 있는 일이 실제로 있었다. 그러면 「자격이 맞는 사람만 그
7
+ * 공정에 선다」는 규칙이 한 번도 걸린 적이 없다는 뜻인데, 화면에는 등급 이름만 있어서 그 사실이
8
+ * 안 보였다.
9
+ *
10
+ * 등급을 없앨 때도 마찬가지다. 무엇이 딸려 자격을 잃는지 알 수 없었다.
11
+ *
12
+ * ── 넷이 구조가 같다 ───────────────────────────────────────────────────────
13
+ * 표준이 `PersonnelClass` · `EquipmentClass` · `PhysicalAssetClass` · `MaterialClass` 를 따로
14
+ * 정의하지만 「소속을 배열로 든다」는 모양은 같다. 그래서 세는 함수도 하나다 — 넷으로 나누면
15
+ * 한쪽만 고쳐지는 날이 온다.
16
+ *
17
+ * ── 배열 칸이라 훑는다 ─────────────────────────────────────────────────────
18
+ * `personnelClassIds` 는 `simple-json` 이다. 데이터베이스가 그 안을 못 보므로 `IN` 으로 못 좁힌다.
19
+ *
20
+ * **그래서 상한이 있고, 상한에 걸리면 말한다.** 조용히 자르면 「이 등급에 아무도 없다」와 「너무
21
+ * 많아 다 못 봤다」가 같아 보인다. 앞엣것은 고쳐야 할 선언이고 뒤엣것은 멀쩡한 등급이다.
22
+ */
23
+ /** 한 번에 훑을 소속 행 수. 품목의 제조법 훑기와 같은 값을 쓴다. */
24
+ export declare const MEMBER_SCAN_LIMIT = 2000;
25
+ export interface ClassMembers {
26
+ /** 등급 이름 → 그 등급에 든 것들의 이름. */
27
+ members: Map<string, string[]>;
28
+ /** 상한에 걸려 다 보지 못했나. */
29
+ capped: boolean;
30
+ }
31
+ /**
32
+ * 소속 표를 훑어 등급별로 뒤집는다.
33
+ *
34
+ * `Entity` 는 소속을 가진 쪽(사람 · 설비 · 자산 · 품목)이고, `field` 는 그 소속을 담은 칸 이름이다.
35
+ */
36
+ export declare function scanClassMembers(Entity: any, field: string, domain: Domain): Promise<ClassMembers>;
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MEMBER_SCAN_LIMIT = void 0;
4
+ exports.scanClassMembers = scanClassMembers;
5
+ const shell_1 = require("@things-factory/shell");
6
+ const referrers_1 = require("./referrers");
7
+ /**
8
+ * **이 등급에 무엇이 드나** — 등급 넷이 나눠 쓰는 한 벌.
9
+ *
10
+ * ── 왜 필요한가 ────────────────────────────────────────────────────────────
11
+ * 등급을 만들어 놓고 거기 아무도 안 들어 있는 일이 실제로 있었다. 그러면 「자격이 맞는 사람만 그
12
+ * 공정에 선다」는 규칙이 한 번도 걸린 적이 없다는 뜻인데, 화면에는 등급 이름만 있어서 그 사실이
13
+ * 안 보였다.
14
+ *
15
+ * 등급을 없앨 때도 마찬가지다. 무엇이 딸려 자격을 잃는지 알 수 없었다.
16
+ *
17
+ * ── 넷이 구조가 같다 ───────────────────────────────────────────────────────
18
+ * 표준이 `PersonnelClass` · `EquipmentClass` · `PhysicalAssetClass` · `MaterialClass` 를 따로
19
+ * 정의하지만 「소속을 배열로 든다」는 모양은 같다. 그래서 세는 함수도 하나다 — 넷으로 나누면
20
+ * 한쪽만 고쳐지는 날이 온다.
21
+ *
22
+ * ── 배열 칸이라 훑는다 ─────────────────────────────────────────────────────
23
+ * `personnelClassIds` 는 `simple-json` 이다. 데이터베이스가 그 안을 못 보므로 `IN` 으로 못 좁힌다.
24
+ *
25
+ * **그래서 상한이 있고, 상한에 걸리면 말한다.** 조용히 자르면 「이 등급에 아무도 없다」와 「너무
26
+ * 많아 다 못 봤다」가 같아 보인다. 앞엣것은 고쳐야 할 선언이고 뒤엣것은 멀쩡한 등급이다.
27
+ */
28
+ /** 한 번에 훑을 소속 행 수. 품목의 제조법 훑기와 같은 값을 쓴다. */
29
+ exports.MEMBER_SCAN_LIMIT = 2000;
30
+ /**
31
+ * 소속 표를 훑어 등급별로 뒤집는다.
32
+ *
33
+ * `Entity` 는 소속을 가진 쪽(사람 · 설비 · 자산 · 품목)이고, `field` 는 그 소속을 담은 칸 이름이다.
34
+ */
35
+ async function scanClassMembers(Entity, field, domain) {
36
+ const repo = (0, shell_1.getRepository)(Entity);
37
+ const total = await repo.count({ where: { domain: { id: domain.id } } });
38
+ /*
39
+ * `id` 를 함께 뽑는다. 빼면 TypeORM 이 「no such column: distinctAlias.…_id」로 터진다 — `take` 를
40
+ * 쓰면 중복 제거를 위해 기본 키로 한 번 더 감싸는데, 그 키를 안 뽑으면 감쌀 것이 없다.
41
+ */
42
+ const rows = await repo.find({
43
+ where: { domain: { id: domain.id } },
44
+ select: ['id', 'name', field],
45
+ take: exports.MEMBER_SCAN_LIMIT
46
+ });
47
+ const members = (0, referrers_1.indexMultiReferrers)(rows, one => one[field] ?? [], one => one.name);
48
+ return { members, capped: total > rows.length };
49
+ }
50
+ //# sourceMappingURL=class-members.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"class-members.js","sourceRoot":"","sources":["../../../server/service/resource/class-members.ts"],"names":[],"mappings":";;;AAyCA,4CAqBC;AA9DD,iDAA6D;AAE7D,2CAAiD;AAEjD;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,4CAA4C;AAC/B,QAAA,iBAAiB,GAAG,IAAI,CAAA;AASrC;;;;GAIG;AACI,KAAK,UAAU,gBAAgB,CAAC,MAAW,EAAE,KAAa,EAAE,MAAc;IAC/E,MAAM,IAAI,GAAG,IAAA,qBAAa,EAAC,MAAM,CAAC,CAAA;IAClC,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,EAAS,EAAE,CAAC,CAAA;IAE/E;;;OAGG;IACH,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC;QAC3B,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,EAAS;QAC3C,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAQ;QACpC,IAAI,EAAE,yBAAiB;KACxB,CAAC,CAAA;IAEF,MAAM,OAAO,GAAG,IAAA,+BAAmB,EACjC,IAAa,EACb,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,EACvB,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,IAAI,CAChB,CAAA;IAED,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,GAAI,IAAc,CAAC,MAAM,EAAE,CAAA;AAC5D,CAAC","sourcesContent":["import { Domain, getRepository } from '@things-factory/shell'\n\nimport { indexMultiReferrers } from './referrers'\n\n/**\n * **이 등급에 무엇이 드나** — 등급 넷이 나눠 쓰는 한 벌.\n *\n * ── 왜 필요한가 ────────────────────────────────────────────────────────────\n * 등급을 만들어 놓고 거기 아무도 안 들어 있는 일이 실제로 있었다. 그러면 「자격이 맞는 사람만 그\n * 공정에 선다」는 규칙이 한 번도 걸린 적이 없다는 뜻인데, 화면에는 등급 이름만 있어서 그 사실이\n * 안 보였다.\n *\n * 등급을 없앨 때도 마찬가지다. 무엇이 딸려 자격을 잃는지 알 수 없었다.\n *\n * ── 넷이 구조가 같다 ───────────────────────────────────────────────────────\n * 표준이 `PersonnelClass` · `EquipmentClass` · `PhysicalAssetClass` · `MaterialClass` 를 따로\n * 정의하지만 「소속을 배열로 든다」는 모양은 같다. 그래서 세는 함수도 하나다 — 넷으로 나누면\n * 한쪽만 고쳐지는 날이 온다.\n *\n * ── 배열 칸이라 훑는다 ─────────────────────────────────────────────────────\n * `personnelClassIds` 는 `simple-json` 이다. 데이터베이스가 그 안을 못 보므로 `IN` 으로 못 좁힌다.\n *\n * **그래서 상한이 있고, 상한에 걸리면 말한다.** 조용히 자르면 「이 등급에 아무도 없다」와 「너무\n * 많아 다 못 봤다」가 같아 보인다. 앞엣것은 고쳐야 할 선언이고 뒤엣것은 멀쩡한 등급이다.\n */\n\n/** 한 번에 훑을 소속 행 수. 품목의 제조법 훑기와 같은 값을 쓴다. */\nexport const MEMBER_SCAN_LIMIT = 2000\n\nexport interface ClassMembers {\n /** 등급 이름 → 그 등급에 든 것들의 이름. */\n members: Map<string, string[]>\n /** 상한에 걸려 다 보지 못했나. */\n capped: boolean\n}\n\n/**\n * 소속 표를 훑어 등급별로 뒤집는다.\n *\n * `Entity` 는 소속을 가진 쪽(사람 · 설비 · 자산 · 품목)이고, `field` 는 그 소속을 담은 칸 이름이다.\n */\nexport async function scanClassMembers(Entity: any, field: string, domain: Domain): Promise<ClassMembers> {\n const repo = getRepository(Entity)\n const total = await repo.count({ where: { domain: { id: domain.id } } as any })\n\n /*\n * `id` 를 함께 뽑는다. 빼면 TypeORM 이 「no such column: distinctAlias.…_id」로 터진다 — `take` 를\n * 쓰면 중복 제거를 위해 기본 키로 한 번 더 감싸는데, 그 키를 안 뽑으면 감쌀 것이 없다.\n */\n const rows = await repo.find({\n where: { domain: { id: domain.id } } as any,\n select: ['id', 'name', field] as any,\n take: MEMBER_SCAN_LIMIT\n })\n\n const members = indexMultiReferrers(\n rows as any[],\n one => one[field] ?? [],\n one => one.name\n )\n\n return { members, capped: total > (rows as any[]).length }\n}\n"]}
@@ -0,0 +1,54 @@
1
+ /**
2
+ * **나를 가리키는 것들** — 들어오는 관계를 세는 공통 부분.
3
+ *
4
+ * ── 왜 필요한가 ────────────────────────────────────────────────────────────
5
+ * 마스터의 관계는 전부 「남의 표 칸에 든 이름」 한 가지 모양이다. 커널이 이름으로 주소를 잡기
6
+ * 때문이다. 그래서 나가는 쪽은 그 칸을 그대로 보이면 되는데, **들어오는 쪽은 아무도 안 세었다.**
7
+ *
8
+ * 자리 하나를 열어도 「여기서 누가 일하나 · 무슨 설비가 있나」를 알 수 없었다. 사람 화면을 열어
9
+ * 근무 자리로 거르는 수밖에 없었고, 설비는 또 설비 화면에서 같은 일을 해야 했다.
10
+ *
11
+ * ── 줄마다 세지 않는다 ─────────────────────────────────────────────────────
12
+ * 필드 리졸버는 줄마다 불린다. 거기서 그때그때 세면 `limit 20` 에 질의 20회다 — N+1 이다.
13
+ * 그래서 **페이지의 이름들을 한 번에** 묻고 그 답을 요청 안에 담는다. 담는 것은 결과가 아니라
14
+ * **약속**이다 — 줄들이 거의 동시에 묻기 때문에, 결과를 담으면 스무 개가 각자 질의를 띄운다.
15
+ *
16
+ * `childCount` 가 같은 방식을 쓴다(`location-master-resolver.ts`). 여기서는 그 방식을 축 여럿이
17
+ * 나눠 쓸 수 있게 뽑아 두었다.
18
+ */
19
+ /** 한 줄이 무엇을 가리키나 — 가리키는 이름과, 그 줄 자신의 이름. */
20
+ export interface Referrer {
21
+ /** 이 줄이 가리키는 대상의 이름. 비었으면 아무도 안 가리킨다. */
22
+ points?: string | null;
23
+ /** 이 줄의 이름. 사람이 목록에서 알아보는 값이다. */
24
+ name?: string | null;
25
+ }
26
+ /**
27
+ * 대상 이름 → 그것을 가리키는 줄들의 이름.
28
+ *
29
+ * **빈 값은 버린다.** 가리키는 곳이 없는 줄과 이름 없는 줄은 관계가 아니다. 그것을 빈 문자열
30
+ * 키로 모으면 「아무 데도 안 놓인 것들」이 한 자리에 뭉쳐 그럴듯한 관계처럼 보인다.
31
+ */
32
+ export declare function indexReferrers(rows: Referrer[]): Map<string, string[]>;
33
+ /**
34
+ * 화면에 보일 만큼만 자르고 **몇이 남았는지 함께 낸다.**
35
+ *
36
+ * 조용히 자르면 「여기 셋이 있다」와 「여기 서른이 있는데 셋만 보인다」가 같아 보인다. 자른 수를
37
+ * 말하지 않으면 그 칸을 보고 사람이 판단을 내린다.
38
+ */
39
+ export declare function cappedList(names: string[] | undefined, limit: number): {
40
+ shown: string[];
41
+ hidden: number;
42
+ };
43
+ /**
44
+ * **배열 칸을 가리키는 것들.**
45
+ *
46
+ * `steps` 나 `materialClassIds` 처럼 한 줄이 여럿을 가리키는 칸이 있다. 이런 칸은 `IN` 으로 못
47
+ * 좁힌다 — 저장이 `simple-json` 이라 데이터베이스가 그 안을 못 본다. 그래서 표를 훑어 여기서
48
+ * 뒤집는다.
49
+ *
50
+ * **훑는 데는 반드시 상한이 있어야 하고, 상한에 걸렸으면 말해야 한다.** 조용히 자르면 「이 공정을
51
+ * 아무도 안 쓴다」와 「너무 많아 다 못 봤다」가 같아 보인다. 앞엣것은 지워도 되는 공정이고
52
+ * 뒤엣것은 지우면 라인이 선다.
53
+ */
54
+ export declare function indexMultiReferrers<T>(rows: T[], pointsOf: (row: T) => (string | null | undefined)[], nameOf: (row: T) => string | null | undefined): Map<string, string[]>;
@@ -0,0 +1,99 @@
1
+ "use strict";
2
+ /**
3
+ * **나를 가리키는 것들** — 들어오는 관계를 세는 공통 부분.
4
+ *
5
+ * ── 왜 필요한가 ────────────────────────────────────────────────────────────
6
+ * 마스터의 관계는 전부 「남의 표 칸에 든 이름」 한 가지 모양이다. 커널이 이름으로 주소를 잡기
7
+ * 때문이다. 그래서 나가는 쪽은 그 칸을 그대로 보이면 되는데, **들어오는 쪽은 아무도 안 세었다.**
8
+ *
9
+ * 자리 하나를 열어도 「여기서 누가 일하나 · 무슨 설비가 있나」를 알 수 없었다. 사람 화면을 열어
10
+ * 근무 자리로 거르는 수밖에 없었고, 설비는 또 설비 화면에서 같은 일을 해야 했다.
11
+ *
12
+ * ── 줄마다 세지 않는다 ─────────────────────────────────────────────────────
13
+ * 필드 리졸버는 줄마다 불린다. 거기서 그때그때 세면 `limit 20` 에 질의 20회다 — N+1 이다.
14
+ * 그래서 **페이지의 이름들을 한 번에** 묻고 그 답을 요청 안에 담는다. 담는 것은 결과가 아니라
15
+ * **약속**이다 — 줄들이 거의 동시에 묻기 때문에, 결과를 담으면 스무 개가 각자 질의를 띄운다.
16
+ *
17
+ * `childCount` 가 같은 방식을 쓴다(`location-master-resolver.ts`). 여기서는 그 방식을 축 여럿이
18
+ * 나눠 쓸 수 있게 뽑아 두었다.
19
+ */
20
+ Object.defineProperty(exports, "__esModule", { value: true });
21
+ exports.indexReferrers = indexReferrers;
22
+ exports.cappedList = cappedList;
23
+ exports.indexMultiReferrers = indexMultiReferrers;
24
+ /**
25
+ * 대상 이름 → 그것을 가리키는 줄들의 이름.
26
+ *
27
+ * **빈 값은 버린다.** 가리키는 곳이 없는 줄과 이름 없는 줄은 관계가 아니다. 그것을 빈 문자열
28
+ * 키로 모으면 「아무 데도 안 놓인 것들」이 한 자리에 뭉쳐 그럴듯한 관계처럼 보인다.
29
+ */
30
+ function indexReferrers(rows) {
31
+ const index = new Map();
32
+ for (const row of rows) {
33
+ const target = (row.points ?? '').trim();
34
+ const who = (row.name ?? '').trim();
35
+ if (!target || !who) {
36
+ continue;
37
+ }
38
+ const list = index.get(target) ?? [];
39
+ /*
40
+ * 같은 줄이 두 번 들어오면 한 번만 센다. 한 줄이 같은 대상을 두 칸에서 가리키는 축이 생길 수
41
+ * 있고(자리와 예비 자리처럼), 그때 개수가 부풀면 화면이 「둘이 있다」고 말한다.
42
+ */
43
+ if (!list.includes(who)) {
44
+ list.push(who);
45
+ }
46
+ index.set(target, list);
47
+ }
48
+ /* 목록의 순서를 정한다 — 같은 자료에 같은 화면이 나와야 한다. */
49
+ for (const list of index.values()) {
50
+ list.sort();
51
+ }
52
+ return index;
53
+ }
54
+ /**
55
+ * 화면에 보일 만큼만 자르고 **몇이 남았는지 함께 낸다.**
56
+ *
57
+ * 조용히 자르면 「여기 셋이 있다」와 「여기 서른이 있는데 셋만 보인다」가 같아 보인다. 자른 수를
58
+ * 말하지 않으면 그 칸을 보고 사람이 판단을 내린다.
59
+ */
60
+ function cappedList(names, limit) {
61
+ const all = names ?? [];
62
+ return { shown: all.slice(0, limit), hidden: Math.max(0, all.length - limit) };
63
+ }
64
+ /**
65
+ * **배열 칸을 가리키는 것들.**
66
+ *
67
+ * `steps` 나 `materialClassIds` 처럼 한 줄이 여럿을 가리키는 칸이 있다. 이런 칸은 `IN` 으로 못
68
+ * 좁힌다 — 저장이 `simple-json` 이라 데이터베이스가 그 안을 못 본다. 그래서 표를 훑어 여기서
69
+ * 뒤집는다.
70
+ *
71
+ * **훑는 데는 반드시 상한이 있어야 하고, 상한에 걸렸으면 말해야 한다.** 조용히 자르면 「이 공정을
72
+ * 아무도 안 쓴다」와 「너무 많아 다 못 봤다」가 같아 보인다. 앞엣것은 지워도 되는 공정이고
73
+ * 뒤엣것은 지우면 라인이 선다.
74
+ */
75
+ function indexMultiReferrers(rows, pointsOf, nameOf) {
76
+ const index = new Map();
77
+ for (const row of rows) {
78
+ const who = (nameOf(row) ?? '').trim();
79
+ if (!who) {
80
+ continue;
81
+ }
82
+ for (const raw of pointsOf(row) ?? []) {
83
+ const target = (raw ?? '').trim();
84
+ if (!target) {
85
+ continue;
86
+ }
87
+ const list = index.get(target) ?? [];
88
+ if (!list.includes(who)) {
89
+ list.push(who);
90
+ }
91
+ index.set(target, list);
92
+ }
93
+ }
94
+ for (const list of index.values()) {
95
+ list.sort();
96
+ }
97
+ return index;
98
+ }
99
+ //# sourceMappingURL=referrers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"referrers.js","sourceRoot":"","sources":["../../../server/service/resource/referrers.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;GAiBG;;AAgBH,wCA8BC;AAQD,gCAIC;AAaD,kDAoCC;AAjGD;;;;;GAKG;AACH,SAAgB,cAAc,CAAC,IAAgB;IAC7C,MAAM,KAAK,GAAG,IAAI,GAAG,EAAoB,CAAA;IAEzC,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,MAAM,MAAM,GAAG,CAAC,GAAG,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAA;QACxC,MAAM,GAAG,GAAG,CAAC,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAA;QAEnC,IAAI,CAAC,MAAM,IAAI,CAAC,GAAG,EAAE,CAAC;YACpB,SAAQ;QACV,CAAC;QAED,MAAM,IAAI,GAAG,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,CAAA;QAEpC;;;WAGG;QACH,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;YACxB,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;QAChB,CAAC;QAED,KAAK,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAA;IACzB,CAAC;IAED,yCAAyC;IACzC,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC;QAClC,IAAI,CAAC,IAAI,EAAE,CAAA;IACb,CAAC;IAED,OAAO,KAAK,CAAA;AACd,CAAC;AAED;;;;;GAKG;AACH,SAAgB,UAAU,CAAC,KAA2B,EAAE,KAAa;IACnE,MAAM,GAAG,GAAG,KAAK,IAAI,EAAE,CAAA;IAEvB,OAAO,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,CAAC,MAAM,GAAG,KAAK,CAAC,EAAE,CAAA;AAChF,CAAC;AAED;;;;;;;;;;GAUG;AACH,SAAgB,mBAAmB,CACjC,IAAS,EACT,QAAmD,EACnD,MAA6C;IAE7C,MAAM,KAAK,GAAG,IAAI,GAAG,EAAoB,CAAA;IAEzC,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAA;QAEtC,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,SAAQ;QACV,CAAC;QAED,KAAK,MAAM,GAAG,IAAI,QAAQ,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC;YACtC,MAAM,MAAM,GAAG,CAAC,GAAG,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAA;YAEjC,IAAI,CAAC,MAAM,EAAE,CAAC;gBACZ,SAAQ;YACV,CAAC;YAED,MAAM,IAAI,GAAG,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,CAAA;YAEpC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;gBACxB,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;YAChB,CAAC;YAED,KAAK,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAA;QACzB,CAAC;IACH,CAAC;IAED,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC;QAClC,IAAI,CAAC,IAAI,EAAE,CAAA;IACb,CAAC;IAED,OAAO,KAAK,CAAA;AACd,CAAC","sourcesContent":["/**\n * **나를 가리키는 것들** — 들어오는 관계를 세는 공통 부분.\n *\n * ── 왜 필요한가 ────────────────────────────────────────────────────────────\n * 마스터의 관계는 전부 「남의 표 칸에 든 이름」 한 가지 모양이다. 커널이 이름으로 주소를 잡기\n * 때문이다. 그래서 나가는 쪽은 그 칸을 그대로 보이면 되는데, **들어오는 쪽은 아무도 안 세었다.**\n *\n * 자리 하나를 열어도 「여기서 누가 일하나 · 무슨 설비가 있나」를 알 수 없었다. 사람 화면을 열어\n * 근무 자리로 거르는 수밖에 없었고, 설비는 또 설비 화면에서 같은 일을 해야 했다.\n *\n * ── 줄마다 세지 않는다 ─────────────────────────────────────────────────────\n * 필드 리졸버는 줄마다 불린다. 거기서 그때그때 세면 `limit 20` 에 질의 20회다 — N+1 이다.\n * 그래서 **페이지의 이름들을 한 번에** 묻고 그 답을 요청 안에 담는다. 담는 것은 결과가 아니라\n * **약속**이다 — 줄들이 거의 동시에 묻기 때문에, 결과를 담으면 스무 개가 각자 질의를 띄운다.\n *\n * `childCount` 가 같은 방식을 쓴다(`location-master-resolver.ts`). 여기서는 그 방식을 축 여럿이\n * 나눠 쓸 수 있게 뽑아 두었다.\n */\n\n/** 한 줄이 무엇을 가리키나 — 가리키는 이름과, 그 줄 자신의 이름. */\nexport interface Referrer {\n /** 이 줄이 가리키는 대상의 이름. 비었으면 아무도 안 가리킨다. */\n points?: string | null\n /** 이 줄의 이름. 사람이 목록에서 알아보는 값이다. */\n name?: string | null\n}\n\n/**\n * 대상 이름 → 그것을 가리키는 줄들의 이름.\n *\n * **빈 값은 버린다.** 가리키는 곳이 없는 줄과 이름 없는 줄은 관계가 아니다. 그것을 빈 문자열\n * 키로 모으면 「아무 데도 안 놓인 것들」이 한 자리에 뭉쳐 그럴듯한 관계처럼 보인다.\n */\nexport function indexReferrers(rows: Referrer[]): Map<string, string[]> {\n const index = new Map<string, string[]>()\n\n for (const row of rows) {\n const target = (row.points ?? '').trim()\n const who = (row.name ?? '').trim()\n\n if (!target || !who) {\n continue\n }\n\n const list = index.get(target) ?? []\n\n /*\n * 같은 줄이 두 번 들어오면 한 번만 센다. 한 줄이 같은 대상을 두 칸에서 가리키는 축이 생길 수\n * 있고(자리와 예비 자리처럼), 그때 개수가 부풀면 화면이 「둘이 있다」고 말한다.\n */\n if (!list.includes(who)) {\n list.push(who)\n }\n\n index.set(target, list)\n }\n\n /* 목록의 순서를 정한다 — 같은 자료에 같은 화면이 나와야 한다. */\n for (const list of index.values()) {\n list.sort()\n }\n\n return index\n}\n\n/**\n * 화면에 보일 만큼만 자르고 **몇이 남았는지 함께 낸다.**\n *\n * 조용히 자르면 「여기 셋이 있다」와 「여기 서른이 있는데 셋만 보인다」가 같아 보인다. 자른 수를\n * 말하지 않으면 그 칸을 보고 사람이 판단을 내린다.\n */\nexport function cappedList(names: string[] | undefined, limit: number): { shown: string[]; hidden: number } {\n const all = names ?? []\n\n return { shown: all.slice(0, limit), hidden: Math.max(0, all.length - limit) }\n}\n\n/**\n * **배열 칸을 가리키는 것들.**\n *\n * `steps` 나 `materialClassIds` 처럼 한 줄이 여럿을 가리키는 칸이 있다. 이런 칸은 `IN` 으로 못\n * 좁힌다 — 저장이 `simple-json` 이라 데이터베이스가 그 안을 못 본다. 그래서 표를 훑어 여기서\n * 뒤집는다.\n *\n * **훑는 데는 반드시 상한이 있어야 하고, 상한에 걸렸으면 말해야 한다.** 조용히 자르면 「이 공정을\n * 아무도 안 쓴다」와 「너무 많아 다 못 봤다」가 같아 보인다. 앞엣것은 지워도 되는 공정이고\n * 뒤엣것은 지우면 라인이 선다.\n */\nexport function indexMultiReferrers<T>(\n rows: T[],\n pointsOf: (row: T) => (string | null | undefined)[],\n nameOf: (row: T) => string | null | undefined\n): Map<string, string[]> {\n const index = new Map<string, string[]>()\n\n for (const row of rows) {\n const who = (nameOf(row) ?? '').trim()\n\n if (!who) {\n continue\n }\n\n for (const raw of pointsOf(row) ?? []) {\n const target = (raw ?? '').trim()\n\n if (!target) {\n continue\n }\n\n const list = index.get(target) ?? []\n\n if (!list.includes(who)) {\n list.push(who)\n }\n\n index.set(target, list)\n }\n }\n\n for (const list of index.values()) {\n list.sort()\n }\n\n return index\n}\n"]}
@@ -26,13 +26,6 @@ export declare class MasterAxisStatus {
26
26
  }
27
27
  export declare class OpsMasterStatus {
28
28
  axes: MasterAxisStatus[];
29
- /**
30
- * 우리가 맞춘 계약 판 — 읽는 쪽이 자기 판과 비교한다.
31
- *
32
- * 앞선 판이면 당기지 않고 말하는 것이 맞다. 반쯤 읽은 마스터로 모델을 세우면 없던 것이 「없다」로
33
- * 읽히고, 그것은 오류보고없이 틀린다.
34
- */
35
- contractVersion: string;
36
29
  }
37
30
  export declare class OpsMasterStatusQuery {
38
31
  /**
@@ -14,8 +14,9 @@ const material_definition_1 = require("../material/material-definition");
14
14
  const operation_definition_1 = require("../operation/operation-definition");
15
15
  const person_1 = require("../personnel/person");
16
16
  const personnel_class_1 = require("../personnel/personnel-class");
17
+ const recipe_definition_1 = require("../recipe/recipe-definition");
18
+ const route_definition_1 = require("../recipe/route-definition");
17
19
  const test_specification_1 = require("../test-spec/test-specification");
18
- const ops_contract_1 = require("@operato/ops-contract");
19
20
  const axis_stamp_1 = require("./axis-stamp");
20
21
  /**
21
22
  * 축이 언제 바뀌었나 — **당길지 말지를 싸게 판단하는 자리.**
@@ -66,10 +67,6 @@ tslib_1.__decorate([
66
67
  (0, type_graphql_1.Field)(type => [MasterAxisStatus]),
67
68
  tslib_1.__metadata("design:type", Array)
68
69
  ], OpsMasterStatus.prototype, "axes", void 0);
69
- tslib_1.__decorate([
70
- (0, type_graphql_1.Field)({ description: 'version of @operato/ops-contract this master was written against' }),
71
- tslib_1.__metadata("design:type", String)
72
- ], OpsMasterStatus.prototype, "contractVersion", void 0);
73
70
  exports.OpsMasterStatus = OpsMasterStatus = tslib_1.__decorate([
74
71
  (0, type_graphql_1.ObjectType)({ description: 'Change status of every master axis' })
75
72
  ], OpsMasterStatus);
@@ -110,9 +107,11 @@ let OpsMasterStatusQuery = class OpsMasterStatusQuery {
110
107
  axisStatus('materialClasses', (0, shell_1.getRepository)(material_class_1.MaterialClass), domain.id),
111
108
  axisStatus('materialDefinitions', (0, shell_1.getRepository)(material_definition_1.MaterialDefinition), domain.id),
112
109
  axisStatus('operations', (0, shell_1.getRepository)(operation_definition_1.OperationDefinition), domain.id),
110
+ axisStatus('recipes', (0, shell_1.getRepository)(recipe_definition_1.RecipeDefinition), domain.id),
111
+ axisStatus('routes', (0, shell_1.getRepository)(route_definition_1.RouteDefinition), domain.id),
113
112
  axisStatus('testSpecifications', (0, shell_1.getRepository)(test_specification_1.TestSpecification), domain.id)
114
113
  ]);
115
- return { axes, contractVersion: ops_contract_1.CONTRACT_VERSION };
114
+ return { axes };
116
115
  }
117
116
  };
118
117
  exports.OpsMasterStatusQuery = OpsMasterStatusQuery;
@@ -1 +1 @@
1
- {"version":3,"file":"master-status.js","sourceRoot":"","sources":["../../../server/service/status/master-status.ts"],"names":[],"mappings":"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","sourcesContent":["import { Ctx, Field, Int, ObjectType, Query, Resolver } from 'type-graphql'\nimport type { ObjectLiteral, Repository } from 'typeorm'\n\nimport { getRepository } from '@things-factory/shell'\n\nimport { PhysicalAsset } from '../asset/physical-asset'\nimport { PhysicalAssetClass } from '../asset/physical-asset-class'\nimport { Equipment } from '../equipment/equipment'\nimport { EquipmentClass } from '../equipment/equipment-class'\nimport { OperationalLocation } from '../location/operational-location'\nimport { MaterialClass } from '../material/material-class'\nimport { MaterialDefinition } from '../material/material-definition'\nimport { OperationDefinition } from '../operation/operation-definition'\nimport { Person } from '../personnel/person'\nimport { PersonnelClass } from '../personnel/personnel-class'\nimport { TestSpecification } from '../test-spec/test-specification'\nimport { CONTRACT_VERSION } from '@operato/ops-contract'\n\nimport { axisStamp } from './axis-stamp'\n\n/**\n * 축이 언제 바뀌었나 — **당길지 말지를 싸게 판단하는 자리.**\n *\n * ── 왜 필요한가 ─────────────────────────────────────────────────────────────\n * 트윈은 마스터를 주기적으로 당긴다. 그런데 인원 마스터가 수천 건이면 바뀐 것이 없어도 매번 그만큼을\n * 옮기게 된다. 이 질의는 **축마다 마지막으로 바뀐 시각과 건수만** 내므로, 트윈이 자기가 마지막으로 본\n * 값과 비교해 바뀐 축만 당길 수 있다.\n *\n * ── 왜 지우지 않는가 ────────────────────────────────────────────────────────\n * 건수가 줄지 않는 것을 전제로 한다. 마스터에서 무엇을 「없앨」 때 행을 지우지 않고 유효 기간으로\n * 끝내기 때문이다(§`effectiveEnd`). 지우면 그 개체를 가리키는 지난 기록이 가리킬 것을 잃는다.\n */\n@ObjectType({ description: 'When one master axis last changed' })\nexport class MasterAxisStatus {\n @Field({ description: 'axis name, matching the pull query' })\n axis: string\n\n @Field(type => Int, { description: 'number of rows in this axis' })\n count: number\n\n /** 마지막으로 바뀐 시각. 축이 비어 있으면 없다 — 「비어 있다」와 「바뀐 적 없다」를 섞지 않는다. */\n @Field({ nullable: true, description: 'last change time, absent when the axis is empty' })\n changedAt?: Date\n\n /**\n * 이 축의 상태를 대표하는 값 하나 — **크기로 비교하지 말고 같은지만 본다.**\n *\n * 시각을 크기로 비교하면 시계가 되감길 때(백업 복원 · 시각 보정) 바뀐 것을 놓친다. 개수를 함께 담아\n * 두므로 같은 초에 두 번 바뀌어도 표시가 달라진다. 읽는 쪽은 자기가 마지막으로 본 표시와 **다르면**\n * 당기면 된다.\n */\n @Field({ description: 'opaque value — compare for equality, never for order' })\n stamp: string\n}\n\n@ObjectType({ description: 'Change status of every master axis' })\nexport class OpsMasterStatus {\n @Field(type => [MasterAxisStatus])\n axes: MasterAxisStatus[]\n\n /**\n * 우리가 맞춘 계약 판 — 읽는 쪽이 자기 판과 비교한다.\n *\n * 앞선 판이면 당기지 않고 말하는 것이 맞다. 반쯤 읽은 마스터로 모델을 세우면 없던 것이 「없다」로\n * 읽히고, 그것은 오류보고없이 틀린다.\n */\n @Field({ description: 'version of @operato/ops-contract this master was written against' })\n contractVersion: string\n}\n\n/** 축 하나의 건수와 마지막 변경 시각. 두 값을 한 질의로 읽는다. */\nasync function axisStatus<T extends ObjectLiteral>(\n axis: string,\n repository: Repository<T>,\n domainId: string\n): Promise<MasterAxisStatus> {\n const row = await repository\n .createQueryBuilder('row')\n .select('COUNT(1)', 'count')\n .addSelect('MAX(row.updatedAt)', 'changedAt')\n .where('row.domain = :domainId', { domainId })\n .getRawOne<{ count: string | number; changedAt: Date | string | null }>()\n\n const count = Number(row?.count ?? 0)\n const changedAt = row?.changedAt ? new Date(row.changedAt) : undefined\n\n return {\n axis,\n count,\n stamp: axisStamp(count, changedAt),\n ...(changedAt ? { changedAt } : {})\n }\n}\n\n\n@Resolver(OpsMasterStatus)\nexport class OpsMasterStatusQuery {\n /**\n * 모든 축의 변경 상태 — 트윈이 주기적으로 부르는 자리.\n *\n * 한 번에 낸다. 축마다 따로 물으면 일곱 번 오가고, 그 사이에 마스터가 바뀌면 축들이 서로 다른 시점을\n * 보게 된다.\n */\n @Query(returns => OpsMasterStatus, { description: 'Change status of every master axis' })\n async opsMasterStatus(@Ctx() context: ResolverContext): Promise<OpsMasterStatus> {\n const { domain } = context.state\n\n const axes = await Promise.all([\n axisStatus('personnelClasses', getRepository(PersonnelClass), domain.id),\n axisStatus('persons', getRepository(Person), domain.id),\n axisStatus('equipmentClasses', getRepository(EquipmentClass), domain.id),\n axisStatus('equipment', getRepository(Equipment), domain.id),\n axisStatus('assetClasses', getRepository(PhysicalAssetClass), domain.id),\n axisStatus('assets', getRepository(PhysicalAsset), domain.id),\n axisStatus('locations', getRepository(OperationalLocation), domain.id),\n axisStatus('materialClasses', getRepository(MaterialClass), domain.id),\n axisStatus('materialDefinitions', getRepository(MaterialDefinition), domain.id),\n axisStatus('operations', getRepository(OperationDefinition), domain.id),\n axisStatus('testSpecifications', getRepository(TestSpecification), domain.id)\n ])\n\n return { axes, contractVersion: CONTRACT_VERSION }\n }\n}\n"]}
1
+ {"version":3,"file":"master-status.js","sourceRoot":"","sources":["../../../server/service/status/master-status.ts"],"names":[],"mappings":"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","sourcesContent":["import { Ctx, Field, Int, ObjectType, Query, Resolver } from 'type-graphql'\nimport type { ObjectLiteral, Repository } from 'typeorm'\n\nimport { getRepository } from '@things-factory/shell'\n\nimport { PhysicalAsset } from '../asset/physical-asset'\nimport { PhysicalAssetClass } from '../asset/physical-asset-class'\nimport { Equipment } from '../equipment/equipment'\nimport { EquipmentClass } from '../equipment/equipment-class'\nimport { OperationalLocation } from '../location/operational-location'\nimport { MaterialClass } from '../material/material-class'\nimport { MaterialDefinition } from '../material/material-definition'\nimport { OperationDefinition } from '../operation/operation-definition'\nimport { Person } from '../personnel/person'\nimport { PersonnelClass } from '../personnel/personnel-class'\nimport { RecipeDefinition } from '../recipe/recipe-definition'\nimport { RouteDefinition } from '../recipe/route-definition'\nimport { TestSpecification } from '../test-spec/test-specification'\n\nimport { axisStamp } from './axis-stamp'\n\n/**\n * 축이 언제 바뀌었나 — **당길지 말지를 싸게 판단하는 자리.**\n *\n * ── 왜 필요한가 ─────────────────────────────────────────────────────────────\n * 트윈은 마스터를 주기적으로 당긴다. 그런데 인원 마스터가 수천 건이면 바뀐 것이 없어도 매번 그만큼을\n * 옮기게 된다. 이 질의는 **축마다 마지막으로 바뀐 시각과 건수만** 내므로, 트윈이 자기가 마지막으로 본\n * 값과 비교해 바뀐 축만 당길 수 있다.\n *\n * ── 왜 지우지 않는가 ────────────────────────────────────────────────────────\n * 건수가 줄지 않는 것을 전제로 한다. 마스터에서 무엇을 「없앨」 때 행을 지우지 않고 유효 기간으로\n * 끝내기 때문이다(§`effectiveEnd`). 지우면 그 개체를 가리키는 지난 기록이 가리킬 것을 잃는다.\n */\n@ObjectType({ description: 'When one master axis last changed' })\nexport class MasterAxisStatus {\n @Field({ description: 'axis name, matching the pull query' })\n axis: string\n\n @Field(type => Int, { description: 'number of rows in this axis' })\n count: number\n\n /** 마지막으로 바뀐 시각. 축이 비어 있으면 없다 — 「비어 있다」와 「바뀐 적 없다」를 섞지 않는다. */\n @Field({ nullable: true, description: 'last change time, absent when the axis is empty' })\n changedAt?: Date\n\n /**\n * 이 축의 상태를 대표하는 값 하나 — **크기로 비교하지 말고 같은지만 본다.**\n *\n * 시각을 크기로 비교하면 시계가 되감길 때(백업 복원 · 시각 보정) 바뀐 것을 놓친다. 개수를 함께 담아\n * 두므로 같은 초에 두 번 바뀌어도 표시가 달라진다. 읽는 쪽은 자기가 마지막으로 본 표시와 **다르면**\n * 당기면 된다.\n */\n @Field({ description: 'opaque value — compare for equality, never for order' })\n stamp: string\n}\n\n@ObjectType({ description: 'Change status of every master axis' })\nexport class OpsMasterStatus {\n @Field(type => [MasterAxisStatus])\n axes: MasterAxisStatus[]\n\n}\n\n/** 축 하나의 건수와 마지막 변경 시각. 두 값을 한 질의로 읽는다. */\nasync function axisStatus<T extends ObjectLiteral>(\n axis: string,\n repository: Repository<T>,\n domainId: string\n): Promise<MasterAxisStatus> {\n const row = await repository\n .createQueryBuilder('row')\n .select('COUNT(1)', 'count')\n .addSelect('MAX(row.updatedAt)', 'changedAt')\n .where('row.domain = :domainId', { domainId })\n .getRawOne<{ count: string | number; changedAt: Date | string | null }>()\n\n const count = Number(row?.count ?? 0)\n const changedAt = row?.changedAt ? new Date(row.changedAt) : undefined\n\n return {\n axis,\n count,\n stamp: axisStamp(count, changedAt),\n ...(changedAt ? { changedAt } : {})\n }\n}\n\n\n@Resolver(OpsMasterStatus)\nexport class OpsMasterStatusQuery {\n /**\n * 모든 축의 변경 상태 — 트윈이 주기적으로 부르는 자리.\n *\n * 한 번에 낸다. 축마다 따로 물으면 일곱 번 오가고, 그 사이에 마스터가 바뀌면 축들이 서로 다른 시점을\n * 보게 된다.\n */\n @Query(returns => OpsMasterStatus, { description: 'Change status of every master axis' })\n async opsMasterStatus(@Ctx() context: ResolverContext): Promise<OpsMasterStatus> {\n const { domain } = context.state\n\n const axes = await Promise.all([\n axisStatus('personnelClasses', getRepository(PersonnelClass), domain.id),\n axisStatus('persons', getRepository(Person), domain.id),\n axisStatus('equipmentClasses', getRepository(EquipmentClass), domain.id),\n axisStatus('equipment', getRepository(Equipment), domain.id),\n axisStatus('assetClasses', getRepository(PhysicalAssetClass), domain.id),\n axisStatus('assets', getRepository(PhysicalAsset), domain.id),\n axisStatus('locations', getRepository(OperationalLocation), domain.id),\n axisStatus('materialClasses', getRepository(MaterialClass), domain.id),\n axisStatus('materialDefinitions', getRepository(MaterialDefinition), domain.id),\n axisStatus('operations', getRepository(OperationDefinition), domain.id),\n axisStatus('recipes', getRepository(RecipeDefinition), domain.id),\n axisStatus('routes', getRepository(RouteDefinition), domain.id),\n axisStatus('testSpecifications', getRepository(TestSpecification), domain.id)\n ])\n\n return { axes }\n }\n}\n"]}
@@ -0,0 +1,53 @@
1
+ /**
2
+ * 검사 기준이 **판정에 쓸 수 있는 모양인가** — 순수 판단.
3
+ *
4
+ * ── 왜 저장하기 전에 보나 ──────────────────────────────────────────────────
5
+ * 기준을 저장하는 자리와 그 기준으로 판정하는 자리가 다르다. 판정하는 쪽(`measurement-judge`)은
6
+ * **모르는 것을 통과로 읽지 않도록** 만들어져 있어서, 망가진 기준이 들어와도 오류를 내지 않고 조용히
7
+ * 「판정하지 않음」이 된다. 그 조용함이 여기서는 위험하다 — 기준을 적은 사람은 적었다고 믿고,
8
+ * 검사원은 통과했다고 읽는다.
9
+ *
10
+ * 세 가지가 특히 조용하다.
11
+ *
12
+ * 한계가 숫자가 아니다 그리드 칸에서 온 `"39.7"` 은 글자다. `값 < "39.7"` 은 견주기가 되긴 하지만
13
+ * `"약 40"` 같은 것이 들어오면 어느 쪽 비교도 참이 아니어서 **모든 값이
14
+ * 한계 안**으로 읽힌다. 그 기준은 아무것도 막지 않는다.
15
+ * 한계에 단위가 없다 관측이 단위를 말해도 견줄 단위가 없으면 단위 검사를 건너뛴다. 화씨 관측이
16
+ * 섭씨 한계와 견주어지고 아무도 모른다.
17
+ * 기준 이름이 비었다 잰 값은 이름으로 기준을 찾는다. 이름이 없으면 영원히 「안 쟀음」이 된다.
18
+ *
19
+ * ── 막지 않는 것 ───────────────────────────────────────────────────────────
20
+ * **숫자 한계가 없는 기준은 정상이다.** 「표면에 흠이 없을 것」처럼 사람이 보고 판단하는 기준이 있고,
21
+ * 그때는 `expression` 만 있다. 그것까지 막으면 현장의 사실을 담을 수 없다.
22
+ *
23
+ * 다만 `limit` 을 **적다 만 것**과 애초에 안 적은 것은 다르다. 앞은 막고 뒤는 통과시킨다.
24
+ */
25
+ /** 저장하려는 기준 하나 — 무엇이 들어올지 모르므로 열어 둔다. */
26
+ export type CriterionInput = Record<string, unknown>;
27
+ export interface CriteriaVerdict {
28
+ ok: boolean;
29
+ /** 무엇이 왜 막혔는지. 한 줄에 하나씩, 고칠 자리를 가리킨다. */
30
+ problems: string[];
31
+ }
32
+ export declare function judgeCriteria(criteria: readonly CriterionInput[]): CriteriaVerdict;
33
+ export declare function criteriaRejected(verdict: CriteriaVerdict): boolean;
34
+ /**
35
+ * 그리드가 보낸 값을 기준 목록으로 읽는다.
36
+ *
37
+ * 화면은 JSON 글자로 보내고 저장 칸은 배열이다. **읽지 못한 것을 빈 목록으로 바꾸지 않는다** —
38
+ * 그러면 사람이 적은 기준이 통째로 사라지고 명세는 기준 없는 명세가 된다.
39
+ */
40
+ /**
41
+ * 읽음 · 못 읽음을 **글자로** 가른다.
42
+ *
43
+ * `ok: boolean` 으로 두면 이 저장소의 설정(`strict: false`)에서 `if (!read.ok)` 가 갈래를 좁히지 못해
44
+ * 못 읽은 쪽의 칸을 꺼낼 수 없다. 글자 표식은 어느 설정에서나 좁혀진다.
45
+ */
46
+ export type CriteriaRead = {
47
+ read: 'ok';
48
+ criteria: CriterionInput[];
49
+ } | {
50
+ read: 'failed';
51
+ detail: string;
52
+ };
53
+ export declare function readCriteria(value: unknown): CriteriaRead;