@things-factory/station 10.1.33

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 (185) hide show
  1. package/README.md +124 -0
  2. package/dist-client/index.d.ts +11 -0
  3. package/dist-client/index.js +13 -0
  4. package/dist-client/index.js.map +1 -0
  5. package/dist-client/station/scene/index.d.ts +11 -0
  6. package/dist-client/station/scene/index.js +12 -0
  7. package/dist-client/station/scene/index.js.map +1 -0
  8. package/dist-client/station/scene/station-context-binding.d.ts +25 -0
  9. package/dist-client/station/scene/station-context-binding.js +39 -0
  10. package/dist-client/station/scene/station-context-binding.js.map +1 -0
  11. package/dist-client/station/scene/station-context-scene.d.ts +28 -0
  12. package/dist-client/station/scene/station-context-scene.js +138 -0
  13. package/dist-client/station/scene/station-context-scene.js.map +1 -0
  14. package/dist-client/station/scene/station-counts-scene.d.ts +6 -0
  15. package/dist-client/station/scene/station-counts-scene.js +51 -0
  16. package/dist-client/station/scene/station-counts-scene.js.map +1 -0
  17. package/dist-client/station/scene/station-field-scene.d.ts +6 -0
  18. package/dist-client/station/scene/station-field-scene.js +89 -0
  19. package/dist-client/station/scene/station-field-scene.js.map +1 -0
  20. package/dist-client/station/scene/station-icons.d.ts +82 -0
  21. package/dist-client/station/scene/station-icons.js +131 -0
  22. package/dist-client/station/scene/station-icons.js.map +1 -0
  23. package/dist-client/station/scene/station-numpad-scene.d.ts +9 -0
  24. package/dist-client/station/scene/station-numpad-scene.js +144 -0
  25. package/dist-client/station/scene/station-numpad-scene.js.map +1 -0
  26. package/dist-client/station/scene/station-scene-style.d.ts +51 -0
  27. package/dist-client/station/scene/station-scene-style.js +108 -0
  28. package/dist-client/station/scene/station-scene-style.js.map +1 -0
  29. package/dist-client/station/scene/station-scene.d.ts +295 -0
  30. package/dist-client/station/scene/station-scene.js +68 -0
  31. package/dist-client/station/scene/station-scene.js.map +1 -0
  32. package/dist-client/station/scene/station-who-scene.d.ts +6 -0
  33. package/dist-client/station/scene/station-who-scene.js +48 -0
  34. package/dist-client/station/scene/station-who-scene.js.map +1 -0
  35. package/dist-client/station/scene/station-work-panel-scene.d.ts +28 -0
  36. package/dist-client/station/scene/station-work-panel-scene.js +37 -0
  37. package/dist-client/station/scene/station-work-panel-scene.js.map +1 -0
  38. package/dist-client/station/station-board-binding.d.ts +16 -0
  39. package/dist-client/station/station-board-binding.js +20 -0
  40. package/dist-client/station/station-board-binding.js.map +1 -0
  41. package/dist-client/station/station-context.d.ts +178 -0
  42. package/dist-client/station/station-context.js +30 -0
  43. package/dist-client/station/station-context.js.map +1 -0
  44. package/dist-client/station/station-palette.d.ts +34 -0
  45. package/dist-client/station/station-palette.js +119 -0
  46. package/dist-client/station/station-palette.js.map +1 -0
  47. package/dist-client/station/station-runtime.d.ts +47 -0
  48. package/dist-client/station/station-runtime.js +47 -0
  49. package/dist-client/station/station-runtime.js.map +1 -0
  50. package/dist-client/station/station-scene-context.d.ts +68 -0
  51. package/dist-client/station/station-scene-context.js +62 -0
  52. package/dist-client/station/station-scene-context.js.map +1 -0
  53. package/dist-client/station/station-screen-flow.d.ts +16 -0
  54. package/dist-client/station/station-screen-flow.js +58 -0
  55. package/dist-client/station/station-screen-flow.js.map +1 -0
  56. package/dist-client/station/station-tile-registry.d.ts +89 -0
  57. package/dist-client/station/station-tile-registry.js +86 -0
  58. package/dist-client/station/station-tile-registry.js.map +1 -0
  59. package/dist-client/station/station-work-panel-view.d.ts +23 -0
  60. package/dist-client/station/station-work-panel-view.js +125 -0
  61. package/dist-client/station/station-work-panel-view.js.map +1 -0
  62. package/dist-client/station/station-work-runtime.d.ts +47 -0
  63. package/dist-client/station/station-work-runtime.js +161 -0
  64. package/dist-client/station/station-work-runtime.js.map +1 -0
  65. package/dist-client/station/station-work-scenarios.d.ts +41 -0
  66. package/dist-client/station/station-work-scenarios.js +59 -0
  67. package/dist-client/station/station-work-scenarios.js.map +1 -0
  68. package/dist-client/station/task-counts.d.ts +32 -0
  69. package/dist-client/station/task-counts.js +26 -0
  70. package/dist-client/station/task-counts.js.map +1 -0
  71. package/dist-client/tsconfig.tsbuildinfo +1 -0
  72. package/dist-server/activities/activity-station-screen-control-withdrawal.d.ts +62 -0
  73. package/dist-server/activities/activity-station-screen-control-withdrawal.js +147 -0
  74. package/dist-server/activities/activity-station-screen-control-withdrawal.js.map +1 -0
  75. package/dist-server/activities/activity-station-screen-release.d.ts +81 -0
  76. package/dist-server/activities/activity-station-screen-release.js +147 -0
  77. package/dist-server/activities/activity-station-screen-release.js.map +1 -0
  78. package/dist-server/activities/index.d.ts +49 -0
  79. package/dist-server/activities/index.js +21 -0
  80. package/dist-server/activities/index.js.map +1 -0
  81. package/dist-server/index.d.ts +3 -0
  82. package/dist-server/index.js +8 -0
  83. package/dist-server/index.js.map +1 -0
  84. package/dist-server/service/index.d.ts +23 -0
  85. package/dist-server/service/index.js +41 -0
  86. package/dist-server/service/index.js.map +1 -0
  87. package/dist-server/service/station/deletion-changes.d.ts +14 -0
  88. package/dist-server/service/station/deletion-changes.js +25 -0
  89. package/dist-server/service/station/deletion-changes.js.map +1 -0
  90. package/dist-server/service/station/index.d.ts +13 -0
  91. package/dist-server/service/station/index.js +25 -0
  92. package/dist-server/service/station/index.js.map +1 -0
  93. package/dist-server/service/station/reversibility.d.ts +21 -0
  94. package/dist-server/service/station/reversibility.js +24 -0
  95. package/dist-server/service/station/reversibility.js.map +1 -0
  96. package/dist-server/service/station/screen-model-digest.d.ts +60 -0
  97. package/dist-server/service/station/screen-model-digest.js +97 -0
  98. package/dist-server/service/station/screen-model-digest.js.map +1 -0
  99. package/dist-server/service/station/staged-material.d.ts +62 -0
  100. package/dist-server/service/station/staged-material.js +44 -0
  101. package/dist-server/service/station/staged-material.js.map +1 -0
  102. package/dist-server/service/station/station-argument-sources.d.ts +31 -0
  103. package/dist-server/service/station/station-argument-sources.js +66 -0
  104. package/dist-server/service/station/station-argument-sources.js.map +1 -0
  105. package/dist-server/service/station/station-assignment-choice.d.ts +81 -0
  106. package/dist-server/service/station/station-assignment-choice.js +105 -0
  107. package/dist-server/service/station/station-assignment-choice.js.map +1 -0
  108. package/dist-server/service/station/station-assignment-resolver.d.ts +104 -0
  109. package/dist-server/service/station/station-assignment-resolver.js +426 -0
  110. package/dist-server/service/station/station-assignment-resolver.js.map +1 -0
  111. package/dist-server/service/station/station-assignment-service.d.ts +33 -0
  112. package/dist-server/service/station/station-assignment-service.js +64 -0
  113. package/dist-server/service/station/station-assignment-service.js.map +1 -0
  114. package/dist-server/service/station/station-axes.d.ts +9 -0
  115. package/dist-server/service/station/station-axes.js +16 -0
  116. package/dist-server/service/station/station-axes.js.map +1 -0
  117. package/dist-server/service/station/station-flow.d.ts +156 -0
  118. package/dist-server/service/station/station-flow.js +255 -0
  119. package/dist-server/service/station/station-flow.js.map +1 -0
  120. package/dist-server/service/station/station-generate.d.ts +76 -0
  121. package/dist-server/service/station/station-generate.js +178 -0
  122. package/dist-server/service/station/station-generate.js.map +1 -0
  123. package/dist-server/service/station/station-product.d.ts +123 -0
  124. package/dist-server/service/station/station-product.js +77 -0
  125. package/dist-server/service/station/station-product.js.map +1 -0
  126. package/dist-server/service/station/station-runtime-binding.d.ts +6 -0
  127. package/dist-server/service/station/station-runtime-binding.js +46 -0
  128. package/dist-server/service/station/station-runtime-binding.js.map +1 -0
  129. package/dist-server/service/station/station-screen-assignment.d.ts +94 -0
  130. package/dist-server/service/station/station-screen-assignment.js +154 -0
  131. package/dist-server/service/station/station-screen-assignment.js.map +1 -0
  132. package/dist-server/service/station/station-screen-control-withdrawal-resolver.d.ts +37 -0
  133. package/dist-server/service/station/station-screen-control-withdrawal-resolver.js +130 -0
  134. package/dist-server/service/station/station-screen-control-withdrawal-resolver.js.map +1 -0
  135. package/dist-server/service/station/station-screen-control-withdrawal.d.ts +104 -0
  136. package/dist-server/service/station/station-screen-control-withdrawal.js +128 -0
  137. package/dist-server/service/station/station-screen-control-withdrawal.js.map +1 -0
  138. package/dist-server/service/station/station-screen-draft-resolver.d.ts +47 -0
  139. package/dist-server/service/station/station-screen-draft-resolver.js +153 -0
  140. package/dist-server/service/station/station-screen-draft-resolver.js.map +1 -0
  141. package/dist-server/service/station/station-screen-layout.d.ts +119 -0
  142. package/dist-server/service/station/station-screen-layout.js +202 -0
  143. package/dist-server/service/station/station-screen-layout.js.map +1 -0
  144. package/dist-server/service/station/station-screen-list-resolver.d.ts +69 -0
  145. package/dist-server/service/station/station-screen-list-resolver.js +215 -0
  146. package/dist-server/service/station/station-screen-list-resolver.js.map +1 -0
  147. package/dist-server/service/station/station-screen-publish-subscriber.d.ts +22 -0
  148. package/dist-server/service/station/station-screen-publish-subscriber.js +90 -0
  149. package/dist-server/service/station/station-screen-publish-subscriber.js.map +1 -0
  150. package/dist-server/service/station/station-screen-release-gate.d.ts +66 -0
  151. package/dist-server/service/station/station-screen-release-gate.js +67 -0
  152. package/dist-server/service/station/station-screen-release-gate.js.map +1 -0
  153. package/dist-server/service/station/station-screen-release-record.d.ts +62 -0
  154. package/dist-server/service/station/station-screen-release-record.js +139 -0
  155. package/dist-server/service/station/station-screen-release-record.js.map +1 -0
  156. package/dist-server/service/station/station-screen-release-signature.d.ts +23 -0
  157. package/dist-server/service/station/station-screen-release-signature.js +32 -0
  158. package/dist-server/service/station/station-screen-release-signature.js.map +1 -0
  159. package/dist-server/service/station/station-screen-scorecard.d.ts +15 -0
  160. package/dist-server/service/station/station-screen-scorecard.js +35 -0
  161. package/dist-server/service/station/station-screen-scorecard.js.map +1 -0
  162. package/dist-server/service/station/station-screen-spec-resolver.d.ts +36 -0
  163. package/dist-server/service/station/station-screen-spec-resolver.js +202 -0
  164. package/dist-server/service/station/station-screen-spec-resolver.js.map +1 -0
  165. package/dist-server/service/station/station-screen-spec.d.ts +104 -0
  166. package/dist-server/service/station/station-screen-spec.js +114 -0
  167. package/dist-server/service/station/station-screen-spec.js.map +1 -0
  168. package/dist-server/service/station/station-screen-thumbnail.d.ts +11 -0
  169. package/dist-server/service/station/station-screen-thumbnail.js +36 -0
  170. package/dist-server/service/station/station-screen-thumbnail.js.map +1 -0
  171. package/dist-server/service/station/station-service-resolver.d.ts +27 -0
  172. package/dist-server/service/station/station-service-resolver.js +95 -0
  173. package/dist-server/service/station/station-service-resolver.js.map +1 -0
  174. package/dist-server/tsconfig.tsbuildinfo +1 -0
  175. package/package.json +44 -0
  176. package/shared/station-axes.d.ts +4 -0
  177. package/shared/station-axes.js +155 -0
  178. package/shared/station-screen-flow-check.d.ts +12 -0
  179. package/shared/station-screen-flow-check.js +74 -0
  180. package/things-scene.config.js +15 -0
  181. package/translations/en.json +42 -0
  182. package/translations/ja.json +42 -0
  183. package/translations/ko.json +42 -0
  184. package/translations/ms.json +42 -0
  185. package/translations/zh.json +42 -0
@@ -0,0 +1,128 @@
1
+ "use strict";
2
+ /**
3
+ * **통제 문서의 통제를 거두는 일** — 켜는 것만 막고 끄는 것을 열어 두면 통제가 한 방향으로만 선다.
4
+ *
5
+ * ═══════════════════════════════════════════════════════════════════════════
6
+ * ── 무엇이 열려 있었나 ─────────────────────────────────────────────────────
7
+ * 통제를 **켤 때**는 결재선이 있어야 했다(`turnsControlOn`). 그런데 `controlled: false` 로 저장하거나
8
+ * spec 을 지우는 데는 관문이 없었고, 발행 관문은 통제가 아닌 화면을 그대로 통과시킨다
9
+ * (`station-screen-publish-subscriber.ts` — `if (!spec?.controlled) return`). 그래서 화면을 만드는
10
+ * 권한만 든 작성자가 통제를 끄고 **서명 없는 도안을 발행**할 수 있었다.
11
+ *
12
+ * 판정(ADR-0047 ⑨): **결재로 세운 것은 결재로 거둔다.** 통제를 끄는 것과 통제 중인 spec 을 지우는
13
+ * 것은 발행 승인과 **같은 결재선**을 지난다. 끄는 것이 결재를 지났다면 발행 관문의 그 return 은
14
+ * 정당하다 — 그래서 관문은 그대로 둔다.
15
+ *
16
+ * ── 여기 있는 것 ──────────────────────────────────────────────────────────
17
+ * 판단만 둔다. DB 를 읽는 것은 부르는 쪽의 일이다 — 이 판단이 틀리면 통제 문서가 뚫리는데, 그것을
18
+ * 확인하는 시험이 DB·보드·spec·결재를 다 세운 뒤에야 물을 수 있으면 안 묻게 된다.
19
+ * ═══════════════════════════════════════════════════════════════════════════
20
+ */
21
+ Object.defineProperty(exports, "__esModule", { value: true });
22
+ exports.STATION_SCREEN_SPEC_CONTENT = exports.WITHDRAWAL_REVERSIBILITY = exports.CONTROL_WITHDRAWAL_ACTIONS = void 0;
23
+ exports.turnsControlOff = turnsControlOff;
24
+ exports.whyNotWithdraw = whyNotWithdraw;
25
+ exports.withdrawalReversibility = withdrawalReversibility;
26
+ exports.controlWithdrawalRequestName = controlWithdrawalRequestName;
27
+ exports.assertWithdrawalRaisable = assertWithdrawalRaisable;
28
+ exports.stationScreenSpecBefore = stationScreenSpecBefore;
29
+ const worklist_1 = require("@things-factory/worklist");
30
+ const shell_1 = require("@things-factory/shell");
31
+ /** 거두는 방식 둘 — 상신 때 정하고, 승인 때 그대로 한다. */
32
+ exports.CONTROL_WITHDRAWAL_ACTIONS = ['turn-off', 'delete'];
33
+ /**
34
+ * Both are reversible (ADR-0064, conditions for `reversible`): turning control on again and saving the
35
+ * settings again are the paired operations, releases are kept by board and not by the settings row's id,
36
+ * and the delete raise carries the settings as they were (`stationScreenSpecBefore`), so the approval
37
+ * record holds what to set up again.
38
+ */
39
+ exports.WITHDRAWAL_REVERSIBILITY = {
40
+ 'turn-off': 'reversible',
41
+ delete: 'reversible'
42
+ };
43
+ /**
44
+ * 이 저장이 통제를 **끄는** 저장인가.
45
+ *
46
+ * `after` 가 비어 있으면 끄는 것이 아니다 — 흐름만 저장하는 요청은 `controlled` 를 안 싣는다
47
+ * (§`saveStationScreenSpec`). 그것을 끄는 것으로 읽으면 통제 문서를 고칠 수조차 없게 된다.
48
+ */
49
+ function turnsControlOff(before, after) {
50
+ return !!before && after === false;
51
+ }
52
+ /**
53
+ * 승인된 지금 **상신 때 굳힌 것을 그대로 할 수 있는가** — 할 수 없으면 그 사유.
54
+ *
55
+ * `standing` 은 콜백이 **상신 때 굳힌 id 로** 다시 읽은 spec 이다. boardId 로 다시 찾지 않는다 — 그
56
+ * 사이 지우고 새로 만든 spec 은 결재자가 본 것이 아니다.
57
+ */
58
+ function whyNotWithdraw(standing) {
59
+ if (!standing) {
60
+ return 'control-withdrawal-spec-gone';
61
+ }
62
+ if (!standing.controlled) {
63
+ return 'control-already-off';
64
+ }
65
+ return undefined;
66
+ }
67
+ /**
68
+ * 되돌릴 수 있는가 — **방식마다 다르다.**
69
+ *
70
+ * 끄는 것은 다시 켤 수 있다. 다만 꺼져 있던 동안 서명 없이 나간 판은 되돌아오지 않는다. 지우는 것은
71
+ * 흐름·요구 축·통제 표시가 함께 사라져 되돌릴 길이 없다 — 다시 만드는 것은 새로 짓는 일이다.
72
+ */
73
+ function withdrawalReversibility(action) {
74
+ return {
75
+ reversible: exports.WITHDRAWAL_REVERSIBILITY[action] === 'reversible',
76
+ caveat: action === 'delete'
77
+ ? '결재 기록에 남은 화면 설정으로 다시 세울 수 있습니다. 통제를 다시 켜는 것도 결재를 거칩니다.'
78
+ : '통제는 다시 켤 수 있습니다. 꺼져 있는 동안 서명 없이 발행된 버전은 되돌아오지 않습니다.'
79
+ };
80
+ }
81
+ /** 상신 이름 — 읽는 쪽과 올리는 쪽이 같은 이름을 써야 겹친 상신을 찾는다. */
82
+ function controlWithdrawalRequestName(boardId) {
83
+ return `통제 해제 승인 ${boardId}`;
84
+ }
85
+ /**
86
+ * 올려도 되는가 — **안 되면 던진다.** 리졸버에는 조건이 없고 이 함수를 부르기만 한다.
87
+ *
88
+ * ── 왜 판단이 던지나 ───────────────────────────────────────────────────────
89
+ * 리졸버에 `if (refusal) throw` 를 두면, 그 조건이 죽어도(`if (false && …)`) 글자는 남는다. 글자로
90
+ * 문을 세는 분류기(`irreversible-doors-refuse.test.ts`)는 그것을 「거절한다」로 읽는다 — 거절하는 글자가
91
+ * 있는지를 셀 뿐 거절이 **도는지**는 못 본다. 그 층은 거동 시험이 맡는다
92
+ * (`station-screen-control-withdrawal.test.ts`, 아키텍트 2026-09-17). 조건을 이 함수 하나에 모아야 거동
93
+ * 시험이 부르는 것이 곧 상신이 부르는 것이 된다.
94
+ */
95
+ function assertWithdrawalRaisable(reading) {
96
+ if (!reading.reason?.trim()) {
97
+ throw new shell_1.Refusal('control-withdrawal-reason-missing', {}, 'enter the reason for withdrawing control.');
98
+ }
99
+ if (!reading.spec) {
100
+ throw new shell_1.Refusal('station-spec-missing', {}, 'the screen settings were not found.');
101
+ }
102
+ if (!reading.spec.controlled) {
103
+ throw new shell_1.Refusal('control-not-on', {}, 'this screen is not a controlled document.');
104
+ }
105
+ if (reading.waiting) {
106
+ throw new shell_1.Refusal('control-withdrawal-already-raised', {}, 'a control withdrawal for this screen is already awaiting approval.');
107
+ }
108
+ if (!reading.installed) {
109
+ throw new shell_1.Refusal('control-withdrawal-door-missing', {}, 'the control withdrawal approval form is not installed in this plant.');
110
+ }
111
+ const refusal = reading.line.refusal;
112
+ if (!refusal) {
113
+ return;
114
+ }
115
+ /* The line is the screen release line, judged by the same function as turning control on (ADR-0047 ②). */
116
+ throw (0, worklist_1.approvalLineRefusal)(refusal);
117
+ }
118
+ /** The settings' content columns — what a person needs to set the same screen up again. */
119
+ exports.STATION_SCREEN_SPEC_CONTENT = ['boardId', 'requires', 'target', 'controlled', 'kind', 'flow'];
120
+ /**
121
+ * The settings as they were before deletion, carried in the approval record. It is what makes the delete
122
+ * branch reversible: the flow, read axes and control mark stay readable after the row is gone
123
+ * (ADR-0064, condition ③ for `reversible`).
124
+ */
125
+ function stationScreenSpecBefore(spec) {
126
+ return Object.fromEntries(exports.STATION_SCREEN_SPEC_CONTENT.map(column => [column, spec[column] ?? null]));
127
+ }
128
+ //# sourceMappingURL=station-screen-control-withdrawal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"station-screen-control-withdrawal.js","sourceRoot":"","sources":["../../../server/service/station/station-screen-control-withdrawal.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * **통제 문서의 통제를 거두는 일** — 켜는 것만 막고 끄는 것을 열어 두면 통제가 한 방향으로만 선다.\n *\n * ═══════════════════════════════════════════════════════════════════════════\n * ── 무엇이 열려 있었나 ─────────────────────────────────────────────────────\n * 통제를 **켤 때**는 결재선이 있어야 했다(`turnsControlOn`). 그런데 `controlled: false` 로 저장하거나\n * spec 을 지우는 데는 관문이 없었고, 발행 관문은 통제가 아닌 화면을 그대로 통과시킨다\n * (`station-screen-publish-subscriber.ts` — `if (!spec?.controlled) return`). 그래서 화면을 만드는\n * 권한만 든 작성자가 통제를 끄고 **서명 없는 도안을 발행**할 수 있었다.\n *\n * 판정(ADR-0047 ⑨): **결재로 세운 것은 결재로 거둔다.** 통제를 끄는 것과 통제 중인 spec 을 지우는\n * 것은 발행 승인과 **같은 결재선**을 지난다. 끄는 것이 결재를 지났다면 발행 관문의 그 return 은\n * 정당하다 — 그래서 관문은 그대로 둔다.\n *\n * ── 여기 있는 것 ──────────────────────────────────────────────────────────\n * 판단만 둔다. DB 를 읽는 것은 부르는 쪽의 일이다 — 이 판단이 틀리면 통제 문서가 뚫리는데, 그것을\n * 확인하는 시험이 DB·보드·spec·결재를 다 세운 뒤에야 물을 수 있으면 안 묻게 된다.\n * ═══════════════════════════════════════════════════════════════════════════\n */\n\nimport { approvalLineRefusal, type ApprovalLineStanding } from '@things-factory/worklist'\nimport { Refusal, type Reversibility } from '@things-factory/shell'\n\n/** 거두는 방식 둘 — 상신 때 정하고, 승인 때 그대로 한다. */\nexport const CONTROL_WITHDRAWAL_ACTIONS = ['turn-off', 'delete'] as const\n\nexport type ControlWithdrawalAction = (typeof CONTROL_WITHDRAWAL_ACTIONS)[number]\n\n/**\n * Both are reversible (ADR-0064, conditions for `reversible`): turning control on again and saving the\n * settings again are the paired operations, releases are kept by board and not by the settings row's id,\n * and the delete raise carries the settings as they were (`stationScreenSpecBefore`), so the approval\n * record holds what to set up again.\n */\nexport const WITHDRAWAL_REVERSIBILITY: Record<ControlWithdrawalAction, Reversibility> = {\n 'turn-off': 'reversible',\n delete: 'reversible'\n}\n\n/**\n * 승인 콜백이 **하지 않고 끝내는** 사유.\n *\n * 상신과 승인 사이에 그 spec 이 바뀔 수 있다. 콜백은 상신 때 굳힌 것만 하고, 그 사이 들어온 변경을\n * 덮지 않는다 — 그러면 조용히 성공하는 대신 이 코드로 멈춘다.\n */\nexport type ControlWithdrawalRefusal =\n /** 상신 때 가리킨 spec 이 이제 없다. */\n | 'control-withdrawal-spec-gone'\n /** 그 spec 은 이미 통제가 아니다 — 거둘 것이 없다. */\n | 'control-already-off'\n\n/**\n * 이 저장이 통제를 **끄는** 저장인가.\n *\n * `after` 가 비어 있으면 끄는 것이 아니다 — 흐름만 저장하는 요청은 `controlled` 를 안 싣는다\n * (§`saveStationScreenSpec`). 그것을 끄는 것으로 읽으면 통제 문서를 고칠 수조차 없게 된다.\n */\nexport function turnsControlOff(before: boolean | null | undefined, after: boolean | null | undefined): boolean {\n return !!before && after === false\n}\n\n/**\n * 승인된 지금 **상신 때 굳힌 것을 그대로 할 수 있는가** — 할 수 없으면 그 사유.\n *\n * `standing` 은 콜백이 **상신 때 굳힌 id 로** 다시 읽은 spec 이다. boardId 로 다시 찾지 않는다 — 그\n * 사이 지우고 새로 만든 spec 은 결재자가 본 것이 아니다.\n */\nexport function whyNotWithdraw(\n standing: { controlled?: boolean | null } | null | undefined\n): ControlWithdrawalRefusal | undefined {\n if (!standing) {\n return 'control-withdrawal-spec-gone'\n }\n\n if (!standing.controlled) {\n return 'control-already-off'\n }\n\n return undefined\n}\n\n/**\n * 되돌릴 수 있는가 — **방식마다 다르다.**\n *\n * 끄는 것은 다시 켤 수 있다. 다만 꺼져 있던 동안 서명 없이 나간 판은 되돌아오지 않는다. 지우는 것은\n * 흐름·요구 축·통제 표시가 함께 사라져 되돌릴 길이 없다 — 다시 만드는 것은 새로 짓는 일이다.\n */\nexport function withdrawalReversibility(action: ControlWithdrawalAction): { reversible: boolean; caveat: string } {\n return {\n reversible: WITHDRAWAL_REVERSIBILITY[action] === 'reversible',\n caveat:\n action === 'delete'\n ? '결재 기록에 남은 화면 설정으로 다시 세울 수 있습니다. 통제를 다시 켜는 것도 결재를 거칩니다.'\n : '통제는 다시 켤 수 있습니다. 꺼져 있는 동안 서명 없이 발행된 버전은 되돌아오지 않습니다.'\n }\n}\n\n/** 상신 이름 — 읽는 쪽과 올리는 쪽이 같은 이름을 써야 겹친 상신을 찾는다. */\nexport function controlWithdrawalRequestName(boardId: string): string {\n return `통제 해제 승인 ${boardId}`\n}\n\n/** 올릴 때 본 것 — 읽는 것은 리졸버가 하고, 이 판단은 DB 를 모른다. */\nexport interface WithdrawalRaiseReading {\n reason?: string | null\n spec?: { controlled?: boolean | null } | null\n /** 같은 화면의 해제가 이미 결재 중인가. */\n waiting: boolean\n /** 통제 해제 승인 양식이 이 공장에 섰나. */\n installed: boolean\n /** **화면 발행 승인**의 결재선 판정 — 켤 때와 같은 함수가 낸 것(`controlLineStanding`). */\n line: ApprovalLineStanding\n}\n\n/**\n * 올려도 되는가 — **안 되면 던진다.** 리졸버에는 조건이 없고 이 함수를 부르기만 한다.\n *\n * ── 왜 판단이 던지나 ───────────────────────────────────────────────────────\n * 리졸버에 `if (refusal) throw` 를 두면, 그 조건이 죽어도(`if (false && …)`) 글자는 남는다. 글자로\n * 문을 세는 분류기(`irreversible-doors-refuse.test.ts`)는 그것을 「거절한다」로 읽는다 — 거절하는 글자가\n * 있는지를 셀 뿐 거절이 **도는지**는 못 본다. 그 층은 거동 시험이 맡는다\n * (`station-screen-control-withdrawal.test.ts`, 아키텍트 2026-09-17). 조건을 이 함수 하나에 모아야 거동\n * 시험이 부르는 것이 곧 상신이 부르는 것이 된다.\n */\nexport function assertWithdrawalRaisable(reading: WithdrawalRaiseReading): void {\n if (!reading.reason?.trim()) {\n throw new Refusal('control-withdrawal-reason-missing', {}, 'enter the reason for withdrawing control.')\n }\n\n if (!reading.spec) {\n throw new Refusal('station-spec-missing', {}, 'the screen settings were not found.')\n }\n\n if (!reading.spec.controlled) {\n throw new Refusal('control-not-on', {}, 'this screen is not a controlled document.')\n }\n\n if (reading.waiting) {\n throw new Refusal('control-withdrawal-already-raised', {}, 'a control withdrawal for this screen is already awaiting approval.')\n }\n\n if (!reading.installed) {\n throw new Refusal('control-withdrawal-door-missing', {}, 'the control withdrawal approval form is not installed in this plant.')\n }\n\n const refusal = reading.line.refusal\n\n if (!refusal) {\n return\n }\n\n /* The line is the screen release line, judged by the same function as turning control on (ADR-0047 ②). */\n throw approvalLineRefusal(refusal)\n}\n\n/** The settings' content columns — what a person needs to set the same screen up again. */\nexport const STATION_SCREEN_SPEC_CONTENT = ['boardId', 'requires', 'target', 'controlled', 'kind', 'flow'] as const\n\n/**\n * The settings as they were before deletion, carried in the approval record. It is what makes the delete\n * branch reversible: the flow, read axes and control mark stay readable after the row is gone\n * (ADR-0064, condition ③ for `reversible`).\n */\nexport function stationScreenSpecBefore(spec: Partial<Record<(typeof STATION_SCREEN_SPEC_CONTENT)[number], unknown>>) {\n return Object.fromEntries(STATION_SCREEN_SPEC_CONTENT.map(column => [column, spec[column] ?? null]))\n}\n"]}
@@ -0,0 +1,47 @@
1
+ import type { StationFlow } from './station-flow';
2
+ export declare class StationScreenScoreDimensionType {
3
+ key: string;
4
+ score: number;
5
+ max: number;
6
+ evidence: string;
7
+ }
8
+ export declare class StationScreenScorecardType {
9
+ score: number;
10
+ grade: string;
11
+ blockers: string[];
12
+ dimensions: StationScreenScoreDimensionType[];
13
+ }
14
+ export declare class StationFlowProblem {
15
+ level: string;
16
+ stepId?: string;
17
+ field?: string;
18
+ code: string;
19
+ params?: Record<string, string>;
20
+ says: string;
21
+ }
22
+ export declare class StationScreenDraft {
23
+ model: string;
24
+ requires: string[];
25
+ problems: StationFlowProblem[];
26
+ scorecard: StationScreenScorecardType;
27
+ }
28
+ /**
29
+ * What a flow would draw — **asked before it is saved.**
30
+ *
31
+ * ── Why the server draws the preview ──────────────────────────────────────
32
+ * The seed lays screens out with `screenModel`, and so does saving. If the composer laid its own
33
+ * preview out in the browser there would be two calculators, and the day they part the author sees
34
+ * one screen while the plant gets another. That kind of difference does not announce itself — it
35
+ * shows up as an operator saying the button moved.
36
+ *
37
+ * So the composer sends the flow and gets back exactly what would be stored.
38
+ *
39
+ * ── The checks ride along ─────────────────────────────────────────────────
40
+ * `checkFlow` runs here for the same reason, and because the author should not have to press save
41
+ * to find out that a required argument has nowhere to come from.
42
+ *
43
+ * There is no repository here and nothing is written. The whole answer is `draftScreen`.
44
+ */
45
+ export declare class StationScreenDraftResolver {
46
+ stationScreenDraft(flow: StationFlow): Promise<StationScreenDraft>;
47
+ }
@@ -0,0 +1,153 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.StationScreenDraftResolver = exports.StationScreenDraft = exports.StationFlowProblem = exports.StationScreenScorecardType = exports.StationScreenScoreDimensionType = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const type_graphql_1 = require("type-graphql");
6
+ const shell_1 = require("@things-factory/shell");
7
+ const station_screen_layout_1 = require("./station-screen-layout");
8
+ const station_screen_scorecard_1 = require("./station-screen-scorecard");
9
+ let StationScreenScoreDimensionType = class StationScreenScoreDimensionType {
10
+ };
11
+ exports.StationScreenScoreDimensionType = StationScreenScoreDimensionType;
12
+ tslib_1.__decorate([
13
+ (0, type_graphql_1.Field)(),
14
+ tslib_1.__metadata("design:type", String)
15
+ ], StationScreenScoreDimensionType.prototype, "key", void 0);
16
+ tslib_1.__decorate([
17
+ (0, type_graphql_1.Field)(),
18
+ tslib_1.__metadata("design:type", Number)
19
+ ], StationScreenScoreDimensionType.prototype, "score", void 0);
20
+ tslib_1.__decorate([
21
+ (0, type_graphql_1.Field)(),
22
+ tslib_1.__metadata("design:type", Number)
23
+ ], StationScreenScoreDimensionType.prototype, "max", void 0);
24
+ tslib_1.__decorate([
25
+ (0, type_graphql_1.Field)(),
26
+ tslib_1.__metadata("design:type", String)
27
+ ], StationScreenScoreDimensionType.prototype, "evidence", void 0);
28
+ exports.StationScreenScoreDimensionType = StationScreenScoreDimensionType = tslib_1.__decorate([
29
+ (0, type_graphql_1.ObjectType)({ description: 'one evidence-bound dimension of a station screen scorecard' })
30
+ ], StationScreenScoreDimensionType);
31
+ let StationScreenScorecardType = class StationScreenScorecardType {
32
+ };
33
+ exports.StationScreenScorecardType = StationScreenScorecardType;
34
+ tslib_1.__decorate([
35
+ (0, type_graphql_1.Field)(),
36
+ tslib_1.__metadata("design:type", Number)
37
+ ], StationScreenScorecardType.prototype, "score", void 0);
38
+ tslib_1.__decorate([
39
+ (0, type_graphql_1.Field)(),
40
+ tslib_1.__metadata("design:type", String)
41
+ ], StationScreenScorecardType.prototype, "grade", void 0);
42
+ tslib_1.__decorate([
43
+ (0, type_graphql_1.Field)(type => [String]),
44
+ tslib_1.__metadata("design:type", Array)
45
+ ], StationScreenScorecardType.prototype, "blockers", void 0);
46
+ tslib_1.__decorate([
47
+ (0, type_graphql_1.Field)(type => [StationScreenScoreDimensionType]),
48
+ tslib_1.__metadata("design:type", Array)
49
+ ], StationScreenScorecardType.prototype, "dimensions", void 0);
50
+ exports.StationScreenScorecardType = StationScreenScorecardType = tslib_1.__decorate([
51
+ (0, type_graphql_1.ObjectType)({ description: 'quality evidence for this screen draft; it is recomputed by the server' })
52
+ ], StationScreenScorecardType);
53
+ let StationFlowProblem = class StationFlowProblem {
54
+ };
55
+ exports.StationFlowProblem = StationFlowProblem;
56
+ tslib_1.__decorate([
57
+ (0, type_graphql_1.Field)({ description: "'error' — this will not work · 'warning' — this will work and mislead" }),
58
+ tslib_1.__metadata("design:type", String)
59
+ ], StationFlowProblem.prototype, "level", void 0);
60
+ tslib_1.__decorate([
61
+ (0, type_graphql_1.Field)({ nullable: true, description: 'the step it is about' }),
62
+ tslib_1.__metadata("design:type", String)
63
+ ], StationFlowProblem.prototype, "stepId", void 0);
64
+ tslib_1.__decorate([
65
+ (0, type_graphql_1.Field)({ nullable: true, description: 'the service field it is about' }),
66
+ tslib_1.__metadata("design:type", String)
67
+ ], StationFlowProblem.prototype, "field", void 0);
68
+ tslib_1.__decorate([
69
+ (0, type_graphql_1.Field)({ description: "which check this is — the screen says it in the reader's language" }),
70
+ tslib_1.__metadata("design:type", String)
71
+ ], StationFlowProblem.prototype, "code", void 0);
72
+ tslib_1.__decorate([
73
+ (0, type_graphql_1.Field)(type => shell_1.ScalarObject, { nullable: true, description: 'values to fill into that sentence' }),
74
+ tslib_1.__metadata("design:type", Object)
75
+ ], StationFlowProblem.prototype, "params", void 0);
76
+ tslib_1.__decorate([
77
+ (0, type_graphql_1.Field)({ description: 'the same thing in Korean, for logs and the scenario tool — not for the screen' }),
78
+ tslib_1.__metadata("design:type", String)
79
+ ], StationFlowProblem.prototype, "says", void 0);
80
+ exports.StationFlowProblem = StationFlowProblem = tslib_1.__decorate([
81
+ (0, type_graphql_1.ObjectType)({ description: 'something the author should know before this flow goes in front of an operator' })
82
+ ], StationFlowProblem);
83
+ let StationScreenDraft = class StationScreenDraft {
84
+ };
85
+ exports.StationScreenDraft = StationScreenDraft;
86
+ tslib_1.__decorate([
87
+ (0, type_graphql_1.Field)({ description: 'the board model the flow lays out' }),
88
+ tslib_1.__metadata("design:type", String)
89
+ ], StationScreenDraft.prototype, "model", void 0);
90
+ tslib_1.__decorate([
91
+ (0, type_graphql_1.Field)(type => [String], { description: 'station context axes the drawing would read' }),
92
+ tslib_1.__metadata("design:type", Array)
93
+ ], StationScreenDraft.prototype, "requires", void 0);
94
+ tslib_1.__decorate([
95
+ (0, type_graphql_1.Field)(type => [StationFlowProblem], { description: 'what the author should know' }),
96
+ tslib_1.__metadata("design:type", Array)
97
+ ], StationScreenDraft.prototype, "problems", void 0);
98
+ tslib_1.__decorate([
99
+ (0, type_graphql_1.Field)(type => StationScreenScorecardType, { description: 'server-derived quality evidence for this draft' }),
100
+ tslib_1.__metadata("design:type", StationScreenScorecardType)
101
+ ], StationScreenDraft.prototype, "scorecard", void 0);
102
+ exports.StationScreenDraft = StationScreenDraft = tslib_1.__decorate([
103
+ (0, type_graphql_1.ObjectType)({ description: 'what this flow would draw, without saving anything' })
104
+ ], StationScreenDraft);
105
+ /**
106
+ * What a flow would draw — **asked before it is saved.**
107
+ *
108
+ * ── Why the server draws the preview ──────────────────────────────────────
109
+ * The seed lays screens out with `screenModel`, and so does saving. If the composer laid its own
110
+ * preview out in the browser there would be two calculators, and the day they part the author sees
111
+ * one screen while the plant gets another. That kind of difference does not announce itself — it
112
+ * shows up as an operator saying the button moved.
113
+ *
114
+ * So the composer sends the flow and gets back exactly what would be stored.
115
+ *
116
+ * ── The checks ride along ─────────────────────────────────────────────────
117
+ * `checkFlow` runs here for the same reason, and because the author should not have to press save
118
+ * to find out that a required argument has nowhere to come from.
119
+ *
120
+ * There is no repository here and nothing is written. The whole answer is `draftScreen`.
121
+ */
122
+ let StationScreenDraftResolver = class StationScreenDraftResolver {
123
+ async stationScreenDraft(flow) {
124
+ const drafted = (0, station_screen_layout_1.draftScreen)(flow);
125
+ return {
126
+ model: drafted.model,
127
+ requires: drafted.requires,
128
+ problems: drafted.problems.map(problem => ({
129
+ level: problem.level,
130
+ stepId: problem.stepId,
131
+ field: problem.field,
132
+ code: problem.code,
133
+ params: problem.params,
134
+ says: problem.says
135
+ })),
136
+ scorecard: (0, station_screen_scorecard_1.inspectStationScreen)(flow, drafted.problems)
137
+ };
138
+ }
139
+ };
140
+ exports.StationScreenDraftResolver = StationScreenDraftResolver;
141
+ tslib_1.__decorate([
142
+ (0, type_graphql_1.Directive)('@privilege(category: "station", privilege: "query", domainOwnerGranted: true)'),
143
+ (0, type_graphql_1.Query)(returns => StationScreenDraft, { description: 'lay out a flow without saving it' }),
144
+ tslib_1.__param(0, (0, type_graphql_1.Arg)('flow', type => shell_1.ScalarObject)),
145
+ tslib_1.__metadata("design:type", Function),
146
+ tslib_1.__metadata("design:paramtypes", [Object]),
147
+ tslib_1.__metadata("design:returntype", Promise)
148
+ ], StationScreenDraftResolver.prototype, "stationScreenDraft", null);
149
+ exports.StationScreenDraftResolver = StationScreenDraftResolver = tslib_1.__decorate([
150
+ (0, type_graphql_1.Resolver)(),
151
+ (0, type_graphql_1.UseMiddleware)(shell_1.SpeakRefusals)
152
+ ], StationScreenDraftResolver);
153
+ //# sourceMappingURL=station-screen-draft-resolver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"station-screen-draft-resolver.js","sourceRoot":"","sources":["../../../server/service/station/station-screen-draft-resolver.ts"],"names":[],"mappings":"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","sourcesContent":["import { Arg, Directive, Field, ObjectType, Query, Resolver, UseMiddleware } from 'type-graphql'\n\nimport { ScalarObject, SpeakRefusals } from '@things-factory/shell'\n\nimport type { StationFlow } from './station-flow'\nimport { draftScreen } from './station-screen-layout'\nimport { inspectStationScreen } from './station-screen-scorecard'\n\n@ObjectType({ description: 'one evidence-bound dimension of a station screen scorecard' })\nexport class StationScreenScoreDimensionType {\n @Field()\n key: string\n\n @Field()\n score: number\n\n @Field()\n max: number\n\n @Field()\n evidence: string\n}\n\n@ObjectType({ description: 'quality evidence for this screen draft; it is recomputed by the server' })\nexport class StationScreenScorecardType {\n @Field()\n score: number\n\n @Field()\n grade: string\n\n @Field(type => [String])\n blockers: string[]\n\n @Field(type => [StationScreenScoreDimensionType])\n dimensions: StationScreenScoreDimensionType[]\n}\n\n@ObjectType({ description: 'something the author should know before this flow goes in front of an operator' })\nexport class StationFlowProblem {\n @Field({ description: \"'error' — this will not work · 'warning' — this will work and mislead\" })\n level: string\n\n @Field({ nullable: true, description: 'the step it is about' })\n stepId?: string\n\n @Field({ nullable: true, description: 'the service field it is about' })\n field?: string\n\n @Field({ description: \"which check this is — the screen says it in the reader's language\" })\n code: string\n\n @Field(type => ScalarObject, { nullable: true, description: 'values to fill into that sentence' })\n params?: Record<string, string>\n\n @Field({ description: 'the same thing in Korean, for logs and the scenario tool — not for the screen' })\n says: string\n}\n\n@ObjectType({ description: 'what this flow would draw, without saving anything' })\nexport class StationScreenDraft {\n @Field({ description: 'the board model the flow lays out' })\n model: string\n\n @Field(type => [String], { description: 'station context axes the drawing would read' })\n requires: string[]\n\n @Field(type => [StationFlowProblem], { description: 'what the author should know' })\n problems: StationFlowProblem[]\n\n @Field(type => StationScreenScorecardType, { description: 'server-derived quality evidence for this draft' })\n scorecard: StationScreenScorecardType\n}\n\n/**\n * What a flow would draw — **asked before it is saved.**\n *\n * ── Why the server draws the preview ──────────────────────────────────────\n * The seed lays screens out with `screenModel`, and so does saving. If the composer laid its own\n * preview out in the browser there would be two calculators, and the day they part the author sees\n * one screen while the plant gets another. That kind of difference does not announce itself — it\n * shows up as an operator saying the button moved.\n *\n * So the composer sends the flow and gets back exactly what would be stored.\n *\n * ── The checks ride along ─────────────────────────────────────────────────\n * `checkFlow` runs here for the same reason, and because the author should not have to press save\n * to find out that a required argument has nowhere to come from.\n *\n * There is no repository here and nothing is written. The whole answer is `draftScreen`.\n */\n@Resolver()\n@UseMiddleware(SpeakRefusals)\nexport class StationScreenDraftResolver {\n @Directive('@privilege(category: \"station\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(returns => StationScreenDraft, { description: 'lay out a flow without saving it' })\n async stationScreenDraft(@Arg('flow', type => ScalarObject) flow: StationFlow): Promise<StationScreenDraft> {\n const drafted = draftScreen(flow)\n\n return {\n model: drafted.model,\n requires: drafted.requires,\n problems: drafted.problems.map(problem => ({\n level: problem.level,\n stepId: problem.stepId,\n field: problem.field,\n code: problem.code,\n params: problem.params,\n says: problem.says\n })),\n scorecard: inspectStationScreen(flow, drafted.problems)\n }\n }\n}\n"]}
@@ -0,0 +1,119 @@
1
+ import type { StationFlow, StationProblem } from './station-flow';
2
+ /**
3
+ * 부품 목록 → things-scene 모델. **자리는 여기서 계산한다.**
4
+ *
5
+ * ── 왜 아무도 좌표를 안 드나 ───────────────────────────────────────────────
6
+ * 부품 하나를 더할 때마다 나머지를 손으로 밀어야 하고, 그렇게 만든 판은 2px 씩 어긋난다. 폼에서
7
+ * 눈에 걸리는 것이 대개 그 어긋남이다.
8
+ *
9
+ * ── 계산기는 **한 벌이다** ─────────────────────────────────────────────────
10
+ * 시드도 저작면도 여기를 지난다. 두 벌을 두면 시드로 세운 화면과 사람이 만든 화면이 다르게 생기고,
11
+ * 그때 어느 쪽이 맞는지 아무도 못 정한다. 그래서 이 파일은 시드가 아니라 **station 어휘**에 산다.
12
+ *
13
+ * ── 규칙은 하나다: 세로로 쌓고, 짝지을 것만 나란히 ─────────────────────────
14
+ * 숫자패드와 단추는 여럿이 나란히 놓인다 — 작업자가 손을 옮기는 거리가 짧아야 한다. 나머지는
15
+ * 한 줄에 하나다.
16
+ */
17
+ /**
18
+ * 화면에 놓이는 부품 하나 — **그려지는 것만.**
19
+ *
20
+ * 흐름의 단계(`StationStep`)와 다르다. 단계는 「사람이 무엇을 하나」이고 여기는 「무엇이 그려지나」다.
21
+ * 한 단계가 부품 둘을 낳기도 하고(값 넣기 → 숫자패드), 하나도 안 낳기도 한다(설정).
22
+ */
23
+ export type StationScreenPart =
24
+ /** 컨텍스트 축 하나를 값으로. `stationBindingNames()` 가 이름의 유일한 원본이다. */
25
+ {
26
+ part: 'field';
27
+ field: string;
28
+ label?: string;
29
+ big?: boolean;
30
+ } | {
31
+ part: 'who';
32
+ label?: string;
33
+ prompt?: string;
34
+ } | {
35
+ part: 'order';
36
+ label?: string;
37
+ } | {
38
+ part: 'counts';
39
+ label?: string;
40
+ } | {
41
+ part: 'numpad';
42
+ label?: string;
43
+ counts?: 'good' | 'scrap';
44
+ disabled?: boolean;
45
+ } | {
46
+ part: 'action';
47
+ label?: string;
48
+ action: 'start' | 'finish' | 'pause' | 'resume';
49
+ reasonCode?: string;
50
+ disabled?: boolean;
51
+ } | {
52
+ part: 'staged';
53
+ label?: string;
54
+ removable?: boolean;
55
+ } | {
56
+ part: 'directive';
57
+ label?: string;
58
+ } | {
59
+ part: 'maintenance';
60
+ label?: string;
61
+ } | {
62
+ part: 'context';
63
+ refreshSeconds?: number;
64
+ };
65
+ /**
66
+ * 부품에서 모델을 만든다.
67
+ *
68
+ * `context` 는 그려지지 않지만 **모델에는 들어간다** — 갱신 주기를 든 부품이라, 없으면 그 화면이
69
+ * 무엇을 얼마나 자주 읽을지 아무 데도 안 적힌다. 자리는 판 밖에 둔다.
70
+ */
71
+ export declare function screenModel(parts: StationScreenPart[]): string;
72
+ /**
73
+ * 흐름 → 그려질 부품.
74
+ *
75
+ * ── 단계와 부품은 하나가 아니다 ────────────────────────────────────────────
76
+ * 「값 넣기」 한 단계가 숫자패드 하나를 낳고, 「보기」 한 단계가 읽는 축 수만큼 값을 낳고, 「설정」은
77
+ * 하나도 안 낳는다(그려지지 않지만 모델에는 남는다 — 갱신 주기를 든 부품이라).
78
+ *
79
+ * ── 이름표가 없으면 축 이름을 쓴다 ─────────────────────────────────────────
80
+ * 빈 이름표로 두지 않는다. 작업대에서 맨숫자는 가장 자주 오독되는 것이고, 그 오독은 수량을 잘못
81
+ * 적는 것으로 이어진다.
82
+ */
83
+ export declare function partsOfFlow(flow: StationFlow): StationScreenPart[];
84
+ /**
85
+ * 이 실행 단계가 어떤 단추인가.
86
+ *
87
+ * 단계의 `status` 출처가 `{ from: 'step' }` 이라는 것은 「단계의 뜻이 정한다」는 말이고, 그 뜻이
88
+ * 여기서 단추 모양으로도 나온다. 아직 흐름이 그것을 따로 적지 않으므로 이름표에서 읽는다 —
89
+ * 흐름에 칸이 생기면 그때 그것을 본다.
90
+ */
91
+ /** 저장하지 않고 흐름이 무엇을 그릴지 — 저작면의 미리보기가 이것을 받는다. */
92
+ export interface StationScreenDraftResult {
93
+ model: string;
94
+ requires: string[];
95
+ problems: StationProblem[];
96
+ }
97
+ /**
98
+ * 흐름 하나가 낳는 **모든 것**을 한 번에 — 도안 · 읽는 축 · 저작자가 알아야 할 것.
99
+ *
100
+ * ── 왜 셋을 묶었나 ────────────────────────────────────────────────────────
101
+ * 저작면의 미리보기와 저장이 같은 계산을 봐야 한다. 미리보기가 도안만 받고 저장이 축을 따로 세면,
102
+ * 저작자가 본 화면과 배정 검증이 검사하는 축이 갈릴 수 있다 — 그리고 그 차이는 배정하는 날에야
103
+ * 드러난다.
104
+ *
105
+ * ── 빈 흐름은 오류가 아니다 ───────────────────────────────────────────────
106
+ * 저작면은 고칠 때마다 묻고, 그중에는 마지막 단계를 지운 편집도 있다. 그때 던지면 화면이 「그리지
107
+ * 못했습니다」를 말하는데 사실은 그릴 것이 없는 것뿐이다.
108
+ */
109
+ export declare function draftScreen(flow: StationFlow | null | undefined): StationScreenDraftResult;
110
+ /**
111
+ * 이 부품들이 읽는 컨텍스트 축.
112
+ *
113
+ * `StationScreenSpec.requires` 는 **파생값이다.** 손으로 적으면 부품을 고칠 때 어긋나고, 그러면
114
+ * 배정 검증이 이미 사실이 아닌 것을 검사한다.
115
+ *
116
+ * 흐름 쪽에는 같은 일을 하는 `axesRead` 가 따로 있다 — 들어오는 어휘가 다르기 때문이다(시드는
117
+ * 부품으로, 저작면은 단계로 들어온다). 규칙이 갈리지 않게 둘 다 여기 계산기 옆에 산다.
118
+ */
119
+ export declare function axesOfParts(parts: StationScreenPart[]): string[];