@things-factory/figure-service 10.1.3 โ†’ 10.1.6

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 (67) hide show
  1. package/coverage/clover.xml +6 -0
  2. package/coverage/coverage-final.json +1 -0
  3. package/coverage/lcov-report/base.css +224 -0
  4. package/coverage/lcov-report/block-navigation.js +87 -0
  5. package/coverage/lcov-report/favicon.png +0 -0
  6. package/coverage/lcov-report/index.html +101 -0
  7. package/coverage/lcov-report/prettify.css +1 -0
  8. package/coverage/lcov-report/prettify.js +2 -0
  9. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  10. package/coverage/lcov-report/sorter.js +210 -0
  11. package/coverage/lcov.info +0 -0
  12. package/dist-server/index.d.ts +2 -0
  13. package/dist-server/index.js +13 -0
  14. package/dist-server/index.js.map +1 -1
  15. package/dist-server/routers/figure-thumbnail-router.d.ts +24 -0
  16. package/dist-server/routers/figure-thumbnail-router.js +93 -0
  17. package/dist-server/routers/figure-thumbnail-router.js.map +1 -0
  18. package/dist-server/routes.d.ts +1 -0
  19. package/dist-server/routes.js +21 -0
  20. package/dist-server/routes.js.map +1 -0
  21. package/dist-server/service/figure/event-subscriber.d.ts +21 -0
  22. package/dist-server/service/figure/event-subscriber.js +51 -0
  23. package/dist-server/service/figure/event-subscriber.js.map +1 -0
  24. package/dist-server/service/figure/figure-gate.d.ts +55 -0
  25. package/dist-server/service/figure/figure-gate.js +64 -0
  26. package/dist-server/service/figure/figure-gate.js.map +1 -0
  27. package/dist-server/service/figure/figure-history.d.ts +43 -0
  28. package/dist-server/service/figure/figure-history.js +155 -0
  29. package/dist-server/service/figure/figure-history.js.map +1 -0
  30. package/dist-server/service/figure/figure-mutation.d.ts +45 -0
  31. package/dist-server/service/figure/figure-mutation.js +174 -1
  32. package/dist-server/service/figure/figure-mutation.js.map +1 -1
  33. package/dist-server/service/figure/figure-query.d.ts +24 -1
  34. package/dist-server/service/figure/figure-query.js +75 -0
  35. package/dist-server/service/figure/figure-query.js.map +1 -1
  36. package/dist-server/service/figure/figure-tools.d.ts +10 -0
  37. package/dist-server/service/figure/figure-tools.js +304 -0
  38. package/dist-server/service/figure/figure-tools.js.map +1 -0
  39. package/dist-server/service/figure/figure-type.d.ts +25 -1
  40. package/dist-server/service/figure/figure-type.js +58 -5
  41. package/dist-server/service/figure/figure-type.js.map +1 -1
  42. package/dist-server/service/figure/figure.d.ts +9 -0
  43. package/dist-server/service/figure/figure.js +15 -3
  44. package/dist-server/service/figure/figure.js.map +1 -1
  45. package/dist-server/service/figure/index.d.ts +4 -1
  46. package/dist-server/service/figure/index.js +5 -2
  47. package/dist-server/service/figure/index.js.map +1 -1
  48. package/dist-server/service/index.d.ts +6 -2
  49. package/dist-server/service/index.js +17 -2
  50. package/dist-server/service/index.js.map +1 -1
  51. package/dist-server/tsconfig.tsbuildinfo +1 -0
  52. package/package.json +9 -6
  53. package/server/index.ts +13 -0
  54. package/server/routers/figure-thumbnail-router.ts +110 -0
  55. package/server/routes.ts +24 -0
  56. package/server/service/figure/event-subscriber.ts +49 -0
  57. package/server/service/figure/figure-gate.ts +119 -0
  58. package/server/service/figure/figure-history.ts +130 -0
  59. package/server/service/figure/figure-mutation.ts +180 -1
  60. package/server/service/figure/figure-propose.test.ts +136 -4
  61. package/server/service/figure/figure-query.ts +63 -1
  62. package/server/service/figure/figure-tools.test.ts +284 -0
  63. package/server/service/figure/figure-tools.ts +320 -0
  64. package/server/service/figure/figure-type.ts +49 -2
  65. package/server/service/figure/figure.ts +15 -3
  66. package/server/service/figure/index.ts +4 -1
  67. package/server/service/index.ts +23 -2
@@ -0,0 +1,210 @@
1
+ /* eslint-disable */
2
+ var addSorting = (function() {
3
+ 'use strict';
4
+ var cols,
5
+ currentSort = {
6
+ index: 0,
7
+ desc: false
8
+ };
9
+
10
+ // returns the summary table element
11
+ function getTable() {
12
+ return document.querySelector('.coverage-summary');
13
+ }
14
+ // returns the thead element of the summary table
15
+ function getTableHeader() {
16
+ return getTable().querySelector('thead tr');
17
+ }
18
+ // returns the tbody element of the summary table
19
+ function getTableBody() {
20
+ return getTable().querySelector('tbody');
21
+ }
22
+ // returns the th element for nth column
23
+ function getNthColumn(n) {
24
+ return getTableHeader().querySelectorAll('th')[n];
25
+ }
26
+
27
+ function onFilterInput() {
28
+ const searchValue = document.getElementById('fileSearch').value;
29
+ const rows = document.getElementsByTagName('tbody')[0].children;
30
+
31
+ // Try to create a RegExp from the searchValue. If it fails (invalid regex),
32
+ // it will be treated as a plain text search
33
+ let searchRegex;
34
+ try {
35
+ searchRegex = new RegExp(searchValue, 'i'); // 'i' for case-insensitive
36
+ } catch (error) {
37
+ searchRegex = null;
38
+ }
39
+
40
+ for (let i = 0; i < rows.length; i++) {
41
+ const row = rows[i];
42
+ let isMatch = false;
43
+
44
+ if (searchRegex) {
45
+ // If a valid regex was created, use it for matching
46
+ isMatch = searchRegex.test(row.textContent);
47
+ } else {
48
+ // Otherwise, fall back to the original plain text search
49
+ isMatch = row.textContent
50
+ .toLowerCase()
51
+ .includes(searchValue.toLowerCase());
52
+ }
53
+
54
+ row.style.display = isMatch ? '' : 'none';
55
+ }
56
+ }
57
+
58
+ // loads the search box
59
+ function addSearchBox() {
60
+ var template = document.getElementById('filterTemplate');
61
+ var templateClone = template.content.cloneNode(true);
62
+ templateClone.getElementById('fileSearch').oninput = onFilterInput;
63
+ template.parentElement.appendChild(templateClone);
64
+ }
65
+
66
+ // loads all columns
67
+ function loadColumns() {
68
+ var colNodes = getTableHeader().querySelectorAll('th'),
69
+ colNode,
70
+ cols = [],
71
+ col,
72
+ i;
73
+
74
+ for (i = 0; i < colNodes.length; i += 1) {
75
+ colNode = colNodes[i];
76
+ col = {
77
+ key: colNode.getAttribute('data-col'),
78
+ sortable: !colNode.getAttribute('data-nosort'),
79
+ type: colNode.getAttribute('data-type') || 'string'
80
+ };
81
+ cols.push(col);
82
+ if (col.sortable) {
83
+ col.defaultDescSort = col.type === 'number';
84
+ colNode.innerHTML =
85
+ colNode.innerHTML + '<span class="sorter"></span>';
86
+ }
87
+ }
88
+ return cols;
89
+ }
90
+ // attaches a data attribute to every tr element with an object
91
+ // of data values keyed by column name
92
+ function loadRowData(tableRow) {
93
+ var tableCols = tableRow.querySelectorAll('td'),
94
+ colNode,
95
+ col,
96
+ data = {},
97
+ i,
98
+ val;
99
+ for (i = 0; i < tableCols.length; i += 1) {
100
+ colNode = tableCols[i];
101
+ col = cols[i];
102
+ val = colNode.getAttribute('data-value');
103
+ if (col.type === 'number') {
104
+ val = Number(val);
105
+ }
106
+ data[col.key] = val;
107
+ }
108
+ return data;
109
+ }
110
+ // loads all row data
111
+ function loadData() {
112
+ var rows = getTableBody().querySelectorAll('tr'),
113
+ i;
114
+
115
+ for (i = 0; i < rows.length; i += 1) {
116
+ rows[i].data = loadRowData(rows[i]);
117
+ }
118
+ }
119
+ // sorts the table using the data for the ith column
120
+ function sortByIndex(index, desc) {
121
+ var key = cols[index].key,
122
+ sorter = function(a, b) {
123
+ a = a.data[key];
124
+ b = b.data[key];
125
+ return a < b ? -1 : a > b ? 1 : 0;
126
+ },
127
+ finalSorter = sorter,
128
+ tableBody = document.querySelector('.coverage-summary tbody'),
129
+ rowNodes = tableBody.querySelectorAll('tr'),
130
+ rows = [],
131
+ i;
132
+
133
+ if (desc) {
134
+ finalSorter = function(a, b) {
135
+ return -1 * sorter(a, b);
136
+ };
137
+ }
138
+
139
+ for (i = 0; i < rowNodes.length; i += 1) {
140
+ rows.push(rowNodes[i]);
141
+ tableBody.removeChild(rowNodes[i]);
142
+ }
143
+
144
+ rows.sort(finalSorter);
145
+
146
+ for (i = 0; i < rows.length; i += 1) {
147
+ tableBody.appendChild(rows[i]);
148
+ }
149
+ }
150
+ // removes sort indicators for current column being sorted
151
+ function removeSortIndicators() {
152
+ var col = getNthColumn(currentSort.index),
153
+ cls = col.className;
154
+
155
+ cls = cls.replace(/ sorted$/, '').replace(/ sorted-desc$/, '');
156
+ col.className = cls;
157
+ }
158
+ // adds sort indicators for current column being sorted
159
+ function addSortIndicators() {
160
+ getNthColumn(currentSort.index).className += currentSort.desc
161
+ ? ' sorted-desc'
162
+ : ' sorted';
163
+ }
164
+ // adds event listeners for all sorter widgets
165
+ function enableUI() {
166
+ var i,
167
+ el,
168
+ ithSorter = function ithSorter(i) {
169
+ var col = cols[i];
170
+
171
+ return function() {
172
+ var desc = col.defaultDescSort;
173
+
174
+ if (currentSort.index === i) {
175
+ desc = !currentSort.desc;
176
+ }
177
+ sortByIndex(i, desc);
178
+ removeSortIndicators();
179
+ currentSort.index = i;
180
+ currentSort.desc = desc;
181
+ addSortIndicators();
182
+ };
183
+ };
184
+ for (i = 0; i < cols.length; i += 1) {
185
+ if (cols[i].sortable) {
186
+ // add the click event handler on the th so users
187
+ // dont have to click on those tiny arrows
188
+ el = getNthColumn(i).querySelector('.sorter').parentElement;
189
+ if (el.addEventListener) {
190
+ el.addEventListener('click', ithSorter(i));
191
+ } else {
192
+ el.attachEvent('onclick', ithSorter(i));
193
+ }
194
+ }
195
+ }
196
+ }
197
+ // adds sorting functionality to the UI
198
+ return function() {
199
+ if (!getTable()) {
200
+ return;
201
+ }
202
+ cols = loadColumns();
203
+ loadData();
204
+ addSearchBox();
205
+ addSortIndicators();
206
+ enableUI();
207
+ };
208
+ })();
209
+
210
+ window.addEventListener('load', addSorting);
File without changes
@@ -1,2 +1,4 @@
1
+ import './routes.js';
1
2
  export * from './service/index.js';
2
3
  export * from './migrations/index.js';
4
+ export { registerFigureToolCategories } from './service/figure/figure-tools.js';
@@ -1,6 +1,19 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registerFigureToolCategories = void 0;
3
4
  const tslib_1 = require("tslib");
5
+ require("./routes.js");
6
+ /*
7
+ * ์ €์ž‘ ๋„๊ตฌ๋ฅผ ์–ด์‹œ์Šคํ„ดํŠธ ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ์— ๋“ฑ๋กํ•œ๋‹ค.
8
+ *
9
+ * ์ด ํŒจํ‚ค์ง€๊ฐ€ ์ ์žฌ๋˜๋ฉด ๋Œ€ํ™”๋ฉด(๋„ํฌ)์ด ์ €์ž‘ ์ค‘์ธ Figure ์˜ ์ •๋ณธยท๋น„์šฉยท๋ฐœํ–‰ ํŒ์ •์„ ์ฝ๊ณ  ํ›„๋ณด๋ฅผ
10
+ * ์š”์ฒญํ•  ์ˆ˜ ์žˆ๋‹ค. ๋Šฅ๋ ฅ์„ ๋ณต์ œํ•˜์ง€ ์•Š๊ณ  figure-service ๊ฐ€ ์ด๋ฏธ ๊ฐ€์ง„ ์ง„์ž…์ ์„ ํˆฌ์˜ํ•œ๋‹ค โ€”
11
+ * ๋…ธ์ถœ ๋ฒ”์œ„๋Š” ๋Œ€ํ™”๋ฉด์ด `toolCategories` ๋กœ ๊ณ ๋ฅธ๋‹ค(twin-ai ยท board-import ์™€ ๊ฐ™์€ ๋ฐฉ์‹).
12
+ */
13
+ const figure_tools_js_1 = require("./service/figure/figure-tools.js");
14
+ (0, figure_tools_js_1.registerFigureToolCategories)();
4
15
  tslib_1.__exportStar(require("./service/index.js"), exports);
5
16
  tslib_1.__exportStar(require("./migrations/index.js"), exports);
17
+ var figure_tools_js_2 = require("./service/figure/figure-tools.js");
18
+ Object.defineProperty(exports, "registerFigureToolCategories", { enumerable: true, get: function () { return figure_tools_js_2.registerFigureToolCategories; } });
6
19
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../server/index.ts"],"names":[],"mappings":";;;AAAA,6DAAkC;AAClC,gEAAqC","sourcesContent":["export * from './service/index.js'\nexport * from './migrations/index.js'\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../server/index.ts"],"names":[],"mappings":";;;;AAAA,uBAAoB;AAEpB;;;;;;GAMG;AACH,sEAA+E;AAC/E,IAAA,8CAA4B,GAAE,CAAA;AAE9B,6DAAkC;AAClC,gEAAqC;AACrC,oEAA+E;AAAtE,+HAAA,4BAA4B,OAAA","sourcesContent":["import './routes.js'\n\n/*\n * ์ €์ž‘ ๋„๊ตฌ๋ฅผ ์–ด์‹œ์Šคํ„ดํŠธ ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ์— ๋“ฑ๋กํ•œ๋‹ค.\n *\n * ์ด ํŒจํ‚ค์ง€๊ฐ€ ์ ์žฌ๋˜๋ฉด ๋Œ€ํ™”๋ฉด(๋„ํฌ)์ด ์ €์ž‘ ์ค‘์ธ Figure ์˜ ์ •๋ณธยท๋น„์šฉยท๋ฐœํ–‰ ํŒ์ •์„ ์ฝ๊ณ  ํ›„๋ณด๋ฅผ\n * ์š”์ฒญํ•  ์ˆ˜ ์žˆ๋‹ค. ๋Šฅ๋ ฅ์„ ๋ณต์ œํ•˜์ง€ ์•Š๊ณ  figure-service ๊ฐ€ ์ด๋ฏธ ๊ฐ€์ง„ ์ง„์ž…์ ์„ ํˆฌ์˜ํ•œ๋‹ค โ€”\n * ๋…ธ์ถœ ๋ฒ”์œ„๋Š” ๋Œ€ํ™”๋ฉด์ด `toolCategories` ๋กœ ๊ณ ๋ฅธ๋‹ค(twin-ai ยท board-import ์™€ ๊ฐ™์€ ๋ฐฉ์‹).\n */\nimport { registerFigureToolCategories } from './service/figure/figure-tools.js'\nregisterFigureToolCategories()\n\nexport * from './service/index.js'\nexport * from './migrations/index.js'\nexport { registerFigureToolCategories } from './service/figure/figure-tools.js'\n"]}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * ์ธ๋„ค์ผ์„ **๊ทธ๋ฆผ์œผ๋กœ** ๋‚ด๋ณด๋‚ด๋Š” ๊ธธ.
3
+ *
4
+ * ## ์™œ ํ•„์š”ํ•œ๊ฐ€
5
+ *
6
+ * ์ธ๋„ค์ผ์€ base64 ๋ฌธ์ž์—ด๋กœ ์นธ์— ๋“ค์–ด ์žˆ๋‹ค(`Board.thumbnail` ๊ณผ ๊ฐ™์€ ๋ฐฉ์‹). ๊ทธ๊ฒƒ์„ ๋ชฉ๋ก ์งˆ์˜๊ฐ€
7
+ * ํ•จ๊ป˜ ์‹ค์–ด ์˜ค๋ฉด ์นดํƒˆ๋กœ๊ทธ๋ฅผ ์—ด ๋•Œ๋งˆ๋‹ค ๊ทธ๋ฆผ ์ „๋ถ€๊ฐ€ JSON ์•ˆ์— ๋‹ด๊ฒจ ๋‹ค์‹œ ์˜จ๋‹ค โ€” data URL ์€
8
+ * ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์บ์‹œํ•  ์ˆ˜ ์—†๋‹ค. [ํ™•์ธํ•จ] ํ‘œ๋ณธ 14๊ฐœ์— 227KB, ํ•œ ์žฅ ์ตœ๋Œ€ 20KB.
9
+ *
10
+ * ๊ทธ๋ฆผ์œผ๋กœ ๋‚ด๋ณด๋‚ด๋ฉด ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์บ์‹œํ•˜๊ณ , ๋ถ€๋ฅด๋Š” ์ชฝ์ด `?v=thumbnailUpdatedAt` ์„ ๋ถ™์ด๋ฏ€๋กœ ๊ทธ๋ฆผ์ด
11
+ * ๋‹ค์‹œ ๋งŒ๋“ค์–ด์ง„ ๊ฒƒ๋งŒ ๋‹ค์‹œ ๋ฐ›๋Š”๋‹ค. ๋ชฉ๋ก ์งˆ์˜์—์„œ๋Š” `thumbnail` ์„ ์•„์˜ˆ ๊ณ ๋ฅด์ง€ ์•Š๋Š”๋‹ค.
12
+ *
13
+ * ## board ๋ฅผ ๋ฒ ๋ผ๋ฉด์„œ ๊ณ ์นœ ๊ฒƒ ์…‹
14
+ *
15
+ * `standalone-board-service-router` ์˜ `/thumbnail/:id` ๊ฐ€ ๋ณธ์ด์ง€๋งŒ ๊ทธ๋Œ€๋กœ ๋‘๋ฉด ์•ˆ ๋˜๋Š” ๊ฒƒ๋“ค์ด ์žˆ๋‹ค.
16
+ *
17
+ * 1. **๊ธธ ์ด๋ฆ„์ด ๊ฒน์นœ๋‹ค.** ์ด ์•ฑ์€ board ๋„ ํ•จ๊ป˜ ์‹ฃ๋Š”๋‹ค(๋ฉ”๋‰ด๊ฐ€ board-ui ๋ฅผ ๋Œ์–ด์˜จ๋‹ค).
18
+ * `/thumbnail/:id` ๋กœ ๋‘๋ฉด ๋จผ์ € ๋“ฑ๋ก๋œ ์ชฝ์ด ์ด๊ธฐ๊ณ , ๋‚จ์˜ ํ‘œ์—์„œ id ๋ฅผ ์ฐพ์•„ 404 ๋ฅผ ๋‚ธ๋‹ค.
19
+ * 2. **ํ–‰ ์ „์ฒด๋ฅผ ์ฝ๋Š”๋‹ค.** board ๋Š” `findOneBy` ๋กœ ํ–‰์„ ๋‹ค ์ฝ๋Š”๋‹ค. figure ํ–‰์—๋Š” ์ •๋ณธ(`source`)์ด
20
+ * ๋ถ™์–ด ์žˆ์–ด, ๊ทธ๋ฆผ ํ•œ ์žฅ์— ์ •๋ณธ๊นŒ์ง€ ์ฝ๊ฒŒ ๋œ๋‹ค. ํ•„์š”ํ•œ ์นธ๋งŒ ๊ณ ๋ฅธ๋‹ค.
21
+ * 3. **png ๋กœ ๋ชป ๋ฐ•๋Š”๋‹ค.** figure ์ธ๋„ค์ผ์€ webp ๋‹ค(๋ชจ๋ธ๋Ÿฌ๊ฐ€ ๊ทธ๋ ‡๊ฒŒ ๋งŒ๋“ ๋‹ค). data URL ์ด ์Šค์Šค๋กœ
22
+ * ๋งํ•˜๋Š” ํ˜•์‹์„ ์“ด๋‹ค โ€” ๋ชป ๋ฐ•์œผ๋ฉด ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ํ™•์žฅ์ž์™€ ๋‹ค๋ฅธ ๋ฐ”์ดํŠธ๋ฅผ ๋ฐ›๋Š”๋‹ค.
23
+ */
24
+ export declare const figureThumbnailRouter: any;
@@ -0,0 +1,93 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.figureThumbnailRouter = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const koa_router_1 = tslib_1.__importDefault(require("koa-router"));
6
+ const typeorm_1 = require("typeorm");
7
+ const auth_base_1 = require("@things-factory/auth-base");
8
+ const shell_1 = require("@things-factory/shell");
9
+ const figure_js_1 = require("../service/figure/figure.js");
10
+ /**
11
+ * ์ธ๋„ค์ผ์„ **๊ทธ๋ฆผ์œผ๋กœ** ๋‚ด๋ณด๋‚ด๋Š” ๊ธธ.
12
+ *
13
+ * ## ์™œ ํ•„์š”ํ•œ๊ฐ€
14
+ *
15
+ * ์ธ๋„ค์ผ์€ base64 ๋ฌธ์ž์—ด๋กœ ์นธ์— ๋“ค์–ด ์žˆ๋‹ค(`Board.thumbnail` ๊ณผ ๊ฐ™์€ ๋ฐฉ์‹). ๊ทธ๊ฒƒ์„ ๋ชฉ๋ก ์งˆ์˜๊ฐ€
16
+ * ํ•จ๊ป˜ ์‹ค์–ด ์˜ค๋ฉด ์นดํƒˆ๋กœ๊ทธ๋ฅผ ์—ด ๋•Œ๋งˆ๋‹ค ๊ทธ๋ฆผ ์ „๋ถ€๊ฐ€ JSON ์•ˆ์— ๋‹ด๊ฒจ ๋‹ค์‹œ ์˜จ๋‹ค โ€” data URL ์€
17
+ * ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์บ์‹œํ•  ์ˆ˜ ์—†๋‹ค. [ํ™•์ธํ•จ] ํ‘œ๋ณธ 14๊ฐœ์— 227KB, ํ•œ ์žฅ ์ตœ๋Œ€ 20KB.
18
+ *
19
+ * ๊ทธ๋ฆผ์œผ๋กœ ๋‚ด๋ณด๋‚ด๋ฉด ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์บ์‹œํ•˜๊ณ , ๋ถ€๋ฅด๋Š” ์ชฝ์ด `?v=thumbnailUpdatedAt` ์„ ๋ถ™์ด๋ฏ€๋กœ ๊ทธ๋ฆผ์ด
20
+ * ๋‹ค์‹œ ๋งŒ๋“ค์–ด์ง„ ๊ฒƒ๋งŒ ๋‹ค์‹œ ๋ฐ›๋Š”๋‹ค. ๋ชฉ๋ก ์งˆ์˜์—์„œ๋Š” `thumbnail` ์„ ์•„์˜ˆ ๊ณ ๋ฅด์ง€ ์•Š๋Š”๋‹ค.
21
+ *
22
+ * ## board ๋ฅผ ๋ฒ ๋ผ๋ฉด์„œ ๊ณ ์นœ ๊ฒƒ ์…‹
23
+ *
24
+ * `standalone-board-service-router` ์˜ `/thumbnail/:id` ๊ฐ€ ๋ณธ์ด์ง€๋งŒ ๊ทธ๋Œ€๋กœ ๋‘๋ฉด ์•ˆ ๋˜๋Š” ๊ฒƒ๋“ค์ด ์žˆ๋‹ค.
25
+ *
26
+ * 1. **๊ธธ ์ด๋ฆ„์ด ๊ฒน์นœ๋‹ค.** ์ด ์•ฑ์€ board ๋„ ํ•จ๊ป˜ ์‹ฃ๋Š”๋‹ค(๋ฉ”๋‰ด๊ฐ€ board-ui ๋ฅผ ๋Œ์–ด์˜จ๋‹ค).
27
+ * `/thumbnail/:id` ๋กœ ๋‘๋ฉด ๋จผ์ € ๋“ฑ๋ก๋œ ์ชฝ์ด ์ด๊ธฐ๊ณ , ๋‚จ์˜ ํ‘œ์—์„œ id ๋ฅผ ์ฐพ์•„ 404 ๋ฅผ ๋‚ธ๋‹ค.
28
+ * 2. **ํ–‰ ์ „์ฒด๋ฅผ ์ฝ๋Š”๋‹ค.** board ๋Š” `findOneBy` ๋กœ ํ–‰์„ ๋‹ค ์ฝ๋Š”๋‹ค. figure ํ–‰์—๋Š” ์ •๋ณธ(`source`)์ด
29
+ * ๋ถ™์–ด ์žˆ์–ด, ๊ทธ๋ฆผ ํ•œ ์žฅ์— ์ •๋ณธ๊นŒ์ง€ ์ฝ๊ฒŒ ๋œ๋‹ค. ํ•„์š”ํ•œ ์นธ๋งŒ ๊ณ ๋ฅธ๋‹ค.
30
+ * 3. **png ๋กœ ๋ชป ๋ฐ•๋Š”๋‹ค.** figure ์ธ๋„ค์ผ์€ webp ๋‹ค(๋ชจ๋ธ๋Ÿฌ๊ฐ€ ๊ทธ๋ ‡๊ฒŒ ๋งŒ๋“ ๋‹ค). data URL ์ด ์Šค์Šค๋กœ
31
+ * ๋งํ•˜๋Š” ํ˜•์‹์„ ์“ด๋‹ค โ€” ๋ชป ๋ฐ•์œผ๋ฉด ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ํ™•์žฅ์ž์™€ ๋‹ค๋ฅธ ๋ฐ”์ดํŠธ๋ฅผ ๋ฐ›๋Š”๋‹ค.
32
+ */
33
+ exports.figureThumbnailRouter = new koa_router_1.default();
34
+ /** `data:image/webp;base64,....` โ†’ ํ˜•์‹๊ณผ ๋ฐ”์ดํŠธ. ์•„๋‹ˆ๋ฉด undefined. */
35
+ function decodeDataUrl(dataUrl) {
36
+ const matched = /^data:([^;,]+);base64,(.*)$/s.exec(dataUrl);
37
+ if (!matched) {
38
+ return undefined;
39
+ }
40
+ return { type: matched[1], buffer: Buffer.from(matched[2], 'base64') };
41
+ }
42
+ exports.figureThumbnailRouter.get('/figure-thumbnail/:id', async (context, next) => {
43
+ const { domain, user, unsafeIP, prohibitedPrivileges } = context.state;
44
+ /*
45
+ **์งˆ์˜์™€ ๊ฐ™์€ ๋ฌธ์„ ์“ด๋‹ค.**
46
+
47
+ `User.hasPrivilege('query','figure',โ€ฆ)` ๋งŒ ๋ณด๋ฉด ์•ˆ ๋œ๋‹ค โ€” figure ์งˆ์˜๋Š”
48
+ `@privilege(category: "figure", privilege: "query", domainOwnerGranted: true)` ๋กœ ์„ ์–ธ๋˜์–ด ์žˆ๊ณ ,
49
+ ๋„๋ฉ”์ธ ์†Œ์œ ์ž๋Š” `figure:query` ๊ถŒํ•œ ์ค„์ด ์—†์–ด๋„ ํ†ต๊ณผํ•œ๋‹ค. ๊ทธ ํ—ˆ์šฉ์„ ์—ฌ๊ธฐ์„œ ๋นผ๋ฉด ๋ชฉ๋ก์€ ๋ณด์ด๋Š”๋ฐ
50
+ ๊ทธ๋ฆผ๋งŒ 403 ์ด ๋˜์–ด, ํ™”๋ฉด์€ ใ€Œ๊ทธ๋ฆผ์ด ์•„์ง ์—†๋‹คใ€๊ณ  ๊ฑฐ์ง“๋งํ•œ๋‹ค. [ํ™•์ธํ•จ] ์‹ค์ œ๋กœ ์นด๋“œ 14์žฅ์ด ์ „๋ถ€
51
+ 403 ์ด์—ˆ๋‹ค.
52
+
53
+ ์กฐ๊ฑด์„ ์†์œผ๋กœ ๋‹ค์‹œ ์ ์ง€ ์•Š๊ณ  ์ง€์‹œ์ž๊ฐ€ ์“ฐ๋Š” ํ•จ์ˆ˜๋ฅผ ๊ทธ๋Œ€๋กœ ์“ด๋‹ค. ์„ ์–ธ์ด ๋ฐ”๋€Œ๋ฉด ์—ฌ๊ธฐ๋„ ํ•จ๊ป˜ ๋ฐ”๋€๋‹ค.
54
+ */
55
+ const allowed = await (0, auth_base_1.checkPermission)({ category: 'figure', privilege: 'query', owner: true }, user, domain, unsafeIP, prohibitedPrivileges);
56
+ if (!allowed) {
57
+ context.status = 403;
58
+ return;
59
+ }
60
+ const { id } = context.params;
61
+ /*
62
+ ์ฝ๊ธฐ๋Š” ๋ถ€๋ชจ ๋„๋ฉ”์ธ๊นŒ์ง€ ๋ณธ๋‹ค โ€” `figure` ์งˆ์˜์™€ ๊ฐ™์€ ๋ฒ”์œ„๋‹ค. ๋ฌผ๋ ค๋ฐ›์€ ํ˜•์ƒ์„ ๋ชฉ๋ก์—๋Š” ๋ณด์—ฌ ์ฃผ๊ณ 
63
+ ๊ทธ๋ฆผ๋งŒ 404 ๋ฅผ ๋‚ด๋ฉด, ํ™”๋ฉด์€ ใ€Œ๊ทธ๋ฆผ์ด ์•„์ง ์—†๋‹คใ€๊ณ  ๊ฑฐ์ง“๋งํ•˜๊ฒŒ ๋œ๋‹ค.
64
+ */
65
+ const found = await (0, shell_1.getRepository)(figure_js_1.Figure).findOne({
66
+ where: { domain: { id: (0, typeorm_1.In)([domain.id, domain.parentId].filter(Boolean)) }, id },
67
+ select: { id: true, thumbnail: true, thumbnailUpdatedAt: true }
68
+ });
69
+ const decoded = found?.thumbnail ? decodeDataUrl(found.thumbnail) : undefined;
70
+ if (!decoded) {
71
+ context.status = 404;
72
+ return;
73
+ }
74
+ /*
75
+ **๊ฐœ์ธ ์บ์‹œ๋งŒ ํ—ˆ๋ฝํ•œ๋‹ค.** ์ด ๊ทธ๋ฆผ์€ ๊ถŒํ•œ์„ ๋ณธ ๋’ค์— ๋‚ด์ฃผ๋Š” ๊ฒƒ์ด๋ฏ€๋กœ ๊ณต์šฉ ์บ์‹œ(ํ”„๋ก์‹œ)์— ๋‚จ์œผ๋ฉด
76
+ ์•ˆ ๋œ๋‹ค. ๋ถ€๋ฅด๋Š” ์ชฝ์ด `?v=` ๋กœ ํŒ์„ ๊ฐ€๋ฆฌ๋ฏ€๋กœ ์˜ค๋ž˜ ๋‘์–ด๋„ ๋‚ก์€ ๊ทธ๋ฆผ์ด ๋‚จ์ง€ ์•Š๊ณ , `v` ์—†์ด ๋ถ€๋ฅธ
77
+ ๊ฒฝ์šฐ๋ฅผ ์œ„ํ•ด ์‹œ๊ฐ์„ ํ•จ๊ป˜ ์‹ค์–ด ๋‹ค์‹œ ๋ฌผ์–ด๋ณผ ์ˆ˜ ์žˆ๊ฒŒ ํ•œ๋‹ค.
78
+ */
79
+ const stamp = found.thumbnailUpdatedAt;
80
+ context.type = decoded.type;
81
+ context.set('Cache-Control', 'private, max-age=31536000');
82
+ if (stamp) {
83
+ context.set('Last-Modified', new Date(stamp).toUTCString());
84
+ context.set('ETag', `"${new Date(stamp).getTime()}"`);
85
+ /* ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ๊ฐ€์ง„ ๊ฒƒ์ด ์•„์ง ๊ทธ๊ฒƒ์ด๋ฉด ๋ฐ”์ดํŠธ๋ฅผ ๋ณด๋‚ด์ง€ ์•Š๋Š”๋‹ค. */
86
+ if (context.fresh) {
87
+ context.status = 304;
88
+ return;
89
+ }
90
+ }
91
+ context.body = decoded.buffer;
92
+ });
93
+ //# sourceMappingURL=figure-thumbnail-router.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"figure-thumbnail-router.js","sourceRoot":"","sources":["../../server/routers/figure-thumbnail-router.ts"],"names":[],"mappings":";;;;AAAA,oEAA+B;AAC/B,qCAA4B;AAE5B,yDAA2D;AAC3D,iDAAqD;AAErD,2DAAoD;AAEpD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACU,QAAA,qBAAqB,GAAG,IAAI,oBAAM,EAAE,CAAA;AAEjD,8DAA8D;AAC9D,SAAS,aAAa,CAAC,OAAe;IACpC,MAAM,OAAO,GAAG,8BAA8B,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;IAC5D,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO,SAAS,CAAA;IAClB,CAAC;IAED,OAAO,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,QAAQ,CAAC,EAAE,CAAA;AACxE,CAAC;AAED,6BAAqB,CAAC,GAAG,CAAC,uBAAuB,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE;IACzE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,oBAAoB,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;IAEtE;;;;;;;;;;MAUE;IACF,MAAM,OAAO,GAAG,MAAM,IAAA,2BAAe,EACnC,EAAE,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,EACvD,IAAI,EACJ,MAAM,EACN,QAAQ,EACR,oBAAoB,CACrB,CAAA;IAED,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO,CAAC,MAAM,GAAG,GAAG,CAAA;QACpB,OAAM;IACR,CAAC;IAED,MAAM,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,MAAM,CAAA;IAE7B;;;MAGE;IACF,MAAM,KAAK,GAAG,MAAM,IAAA,qBAAa,EAAC,kBAAM,CAAC,CAAC,OAAO,CAAC;QAChD,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,IAAA,YAAE,EAAC,CAAC,MAAM,CAAC,EAAE,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE;QAC/E,MAAM,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,kBAAkB,EAAE,IAAI,EAAE;KAChE,CAAC,CAAA;IAEF,MAAM,OAAO,GAAG,KAAK,EAAE,SAAS,CAAC,CAAC,CAAC,aAAa,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAA;IAE7E,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO,CAAC,MAAM,GAAG,GAAG,CAAA;QACpB,OAAM;IACR,CAAC;IAED;;;;MAIE;IACF,MAAM,KAAK,GAAG,KAAM,CAAC,kBAAkB,CAAA;IAEvC,OAAO,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,CAAA;IAC3B,OAAO,CAAC,GAAG,CAAC,eAAe,EAAE,2BAA2B,CAAC,CAAA;IACzD,IAAI,KAAK,EAAE,CAAC;QACV,OAAO,CAAC,GAAG,CAAC,eAAe,EAAE,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,WAAW,EAAE,CAAC,CAAA;QAC3D,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,OAAO,EAAE,GAAG,CAAC,CAAA;QAErD,uCAAuC;QACvC,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;YAClB,OAAO,CAAC,MAAM,GAAG,GAAG,CAAA;YACpB,OAAM;QACR,CAAC;IACH,CAAC;IAED,OAAO,CAAC,IAAI,GAAG,OAAO,CAAC,MAAM,CAAA;AAC/B,CAAC,CAAC,CAAA","sourcesContent":["import Router from 'koa-router'\nimport { In } from 'typeorm'\n\nimport { checkPermission } from '@things-factory/auth-base'\nimport { getRepository } from '@things-factory/shell'\n\nimport { Figure } from '../service/figure/figure.js'\n\n/**\n * ์ธ๋„ค์ผ์„ **๊ทธ๋ฆผ์œผ๋กœ** ๋‚ด๋ณด๋‚ด๋Š” ๊ธธ.\n *\n * ## ์™œ ํ•„์š”ํ•œ๊ฐ€\n *\n * ์ธ๋„ค์ผ์€ base64 ๋ฌธ์ž์—ด๋กœ ์นธ์— ๋“ค์–ด ์žˆ๋‹ค(`Board.thumbnail` ๊ณผ ๊ฐ™์€ ๋ฐฉ์‹). ๊ทธ๊ฒƒ์„ ๋ชฉ๋ก ์งˆ์˜๊ฐ€\n * ํ•จ๊ป˜ ์‹ค์–ด ์˜ค๋ฉด ์นดํƒˆ๋กœ๊ทธ๋ฅผ ์—ด ๋•Œ๋งˆ๋‹ค ๊ทธ๋ฆผ ์ „๋ถ€๊ฐ€ JSON ์•ˆ์— ๋‹ด๊ฒจ ๋‹ค์‹œ ์˜จ๋‹ค โ€” data URL ์€\n * ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์บ์‹œํ•  ์ˆ˜ ์—†๋‹ค. [ํ™•์ธํ•จ] ํ‘œ๋ณธ 14๊ฐœ์— 227KB, ํ•œ ์žฅ ์ตœ๋Œ€ 20KB.\n *\n * ๊ทธ๋ฆผ์œผ๋กœ ๋‚ด๋ณด๋‚ด๋ฉด ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์บ์‹œํ•˜๊ณ , ๋ถ€๋ฅด๋Š” ์ชฝ์ด `?v=thumbnailUpdatedAt` ์„ ๋ถ™์ด๋ฏ€๋กœ ๊ทธ๋ฆผ์ด\n * ๋‹ค์‹œ ๋งŒ๋“ค์–ด์ง„ ๊ฒƒ๋งŒ ๋‹ค์‹œ ๋ฐ›๋Š”๋‹ค. ๋ชฉ๋ก ์งˆ์˜์—์„œ๋Š” `thumbnail` ์„ ์•„์˜ˆ ๊ณ ๋ฅด์ง€ ์•Š๋Š”๋‹ค.\n *\n * ## board ๋ฅผ ๋ฒ ๋ผ๋ฉด์„œ ๊ณ ์นœ ๊ฒƒ ์…‹\n *\n * `standalone-board-service-router` ์˜ `/thumbnail/:id` ๊ฐ€ ๋ณธ์ด์ง€๋งŒ ๊ทธ๋Œ€๋กœ ๋‘๋ฉด ์•ˆ ๋˜๋Š” ๊ฒƒ๋“ค์ด ์žˆ๋‹ค.\n *\n * 1. **๊ธธ ์ด๋ฆ„์ด ๊ฒน์นœ๋‹ค.** ์ด ์•ฑ์€ board ๋„ ํ•จ๊ป˜ ์‹ฃ๋Š”๋‹ค(๋ฉ”๋‰ด๊ฐ€ board-ui ๋ฅผ ๋Œ์–ด์˜จ๋‹ค).\n * `/thumbnail/:id` ๋กœ ๋‘๋ฉด ๋จผ์ € ๋“ฑ๋ก๋œ ์ชฝ์ด ์ด๊ธฐ๊ณ , ๋‚จ์˜ ํ‘œ์—์„œ id ๋ฅผ ์ฐพ์•„ 404 ๋ฅผ ๋‚ธ๋‹ค.\n * 2. **ํ–‰ ์ „์ฒด๋ฅผ ์ฝ๋Š”๋‹ค.** board ๋Š” `findOneBy` ๋กœ ํ–‰์„ ๋‹ค ์ฝ๋Š”๋‹ค. figure ํ–‰์—๋Š” ์ •๋ณธ(`source`)์ด\n * ๋ถ™์–ด ์žˆ์–ด, ๊ทธ๋ฆผ ํ•œ ์žฅ์— ์ •๋ณธ๊นŒ์ง€ ์ฝ๊ฒŒ ๋œ๋‹ค. ํ•„์š”ํ•œ ์นธ๋งŒ ๊ณ ๋ฅธ๋‹ค.\n * 3. **png ๋กœ ๋ชป ๋ฐ•๋Š”๋‹ค.** figure ์ธ๋„ค์ผ์€ webp ๋‹ค(๋ชจ๋ธ๋Ÿฌ๊ฐ€ ๊ทธ๋ ‡๊ฒŒ ๋งŒ๋“ ๋‹ค). data URL ์ด ์Šค์Šค๋กœ\n * ๋งํ•˜๋Š” ํ˜•์‹์„ ์“ด๋‹ค โ€” ๋ชป ๋ฐ•์œผ๋ฉด ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ํ™•์žฅ์ž์™€ ๋‹ค๋ฅธ ๋ฐ”์ดํŠธ๋ฅผ ๋ฐ›๋Š”๋‹ค.\n */\nexport const figureThumbnailRouter = new Router()\n\n/** `data:image/webp;base64,....` โ†’ ํ˜•์‹๊ณผ ๋ฐ”์ดํŠธ. ์•„๋‹ˆ๋ฉด undefined. */\nfunction decodeDataUrl(dataUrl: string): { type: string; buffer: Buffer } | undefined {\n const matched = /^data:([^;,]+);base64,(.*)$/s.exec(dataUrl)\n if (!matched) {\n return undefined\n }\n\n return { type: matched[1], buffer: Buffer.from(matched[2], 'base64') }\n}\n\nfigureThumbnailRouter.get('/figure-thumbnail/:id', async (context, next) => {\n const { domain, user, unsafeIP, prohibitedPrivileges } = context.state\n\n /*\n **์งˆ์˜์™€ ๊ฐ™์€ ๋ฌธ์„ ์“ด๋‹ค.**\n\n `User.hasPrivilege('query','figure',โ€ฆ)` ๋งŒ ๋ณด๋ฉด ์•ˆ ๋œ๋‹ค โ€” figure ์งˆ์˜๋Š”\n `@privilege(category: \"figure\", privilege: \"query\", domainOwnerGranted: true)` ๋กœ ์„ ์–ธ๋˜์–ด ์žˆ๊ณ ,\n ๋„๋ฉ”์ธ ์†Œ์œ ์ž๋Š” `figure:query` ๊ถŒํ•œ ์ค„์ด ์—†์–ด๋„ ํ†ต๊ณผํ•œ๋‹ค. ๊ทธ ํ—ˆ์šฉ์„ ์—ฌ๊ธฐ์„œ ๋นผ๋ฉด ๋ชฉ๋ก์€ ๋ณด์ด๋Š”๋ฐ\n ๊ทธ๋ฆผ๋งŒ 403 ์ด ๋˜์–ด, ํ™”๋ฉด์€ ใ€Œ๊ทธ๋ฆผ์ด ์•„์ง ์—†๋‹คใ€๊ณ  ๊ฑฐ์ง“๋งํ•œ๋‹ค. [ํ™•์ธํ•จ] ์‹ค์ œ๋กœ ์นด๋“œ 14์žฅ์ด ์ „๋ถ€\n 403 ์ด์—ˆ๋‹ค.\n\n ์กฐ๊ฑด์„ ์†์œผ๋กœ ๋‹ค์‹œ ์ ์ง€ ์•Š๊ณ  ์ง€์‹œ์ž๊ฐ€ ์“ฐ๋Š” ํ•จ์ˆ˜๋ฅผ ๊ทธ๋Œ€๋กœ ์“ด๋‹ค. ์„ ์–ธ์ด ๋ฐ”๋€Œ๋ฉด ์—ฌ๊ธฐ๋„ ํ•จ๊ป˜ ๋ฐ”๋€๋‹ค.\n */\n const allowed = await checkPermission(\n { category: 'figure', privilege: 'query', owner: true },\n user,\n domain,\n unsafeIP,\n prohibitedPrivileges\n )\n\n if (!allowed) {\n context.status = 403\n return\n }\n\n const { id } = context.params\n\n /*\n ์ฝ๊ธฐ๋Š” ๋ถ€๋ชจ ๋„๋ฉ”์ธ๊นŒ์ง€ ๋ณธ๋‹ค โ€” `figure` ์งˆ์˜์™€ ๊ฐ™์€ ๋ฒ”์œ„๋‹ค. ๋ฌผ๋ ค๋ฐ›์€ ํ˜•์ƒ์„ ๋ชฉ๋ก์—๋Š” ๋ณด์—ฌ ์ฃผ๊ณ \n ๊ทธ๋ฆผ๋งŒ 404 ๋ฅผ ๋‚ด๋ฉด, ํ™”๋ฉด์€ ใ€Œ๊ทธ๋ฆผ์ด ์•„์ง ์—†๋‹คใ€๊ณ  ๊ฑฐ์ง“๋งํ•˜๊ฒŒ ๋œ๋‹ค.\n */\n const found = await getRepository(Figure).findOne({\n where: { domain: { id: In([domain.id, domain.parentId].filter(Boolean)) }, id },\n select: { id: true, thumbnail: true, thumbnailUpdatedAt: true }\n })\n\n const decoded = found?.thumbnail ? decodeDataUrl(found.thumbnail) : undefined\n\n if (!decoded) {\n context.status = 404\n return\n }\n\n /*\n **๊ฐœ์ธ ์บ์‹œ๋งŒ ํ—ˆ๋ฝํ•œ๋‹ค.** ์ด ๊ทธ๋ฆผ์€ ๊ถŒํ•œ์„ ๋ณธ ๋’ค์— ๋‚ด์ฃผ๋Š” ๊ฒƒ์ด๋ฏ€๋กœ ๊ณต์šฉ ์บ์‹œ(ํ”„๋ก์‹œ)์— ๋‚จ์œผ๋ฉด\n ์•ˆ ๋œ๋‹ค. ๋ถ€๋ฅด๋Š” ์ชฝ์ด `?v=` ๋กœ ํŒ์„ ๊ฐ€๋ฆฌ๋ฏ€๋กœ ์˜ค๋ž˜ ๋‘์–ด๋„ ๋‚ก์€ ๊ทธ๋ฆผ์ด ๋‚จ์ง€ ์•Š๊ณ , `v` ์—†์ด ๋ถ€๋ฅธ\n ๊ฒฝ์šฐ๋ฅผ ์œ„ํ•ด ์‹œ๊ฐ์„ ํ•จ๊ป˜ ์‹ค์–ด ๋‹ค์‹œ ๋ฌผ์–ด๋ณผ ์ˆ˜ ์žˆ๊ฒŒ ํ•œ๋‹ค.\n */\n const stamp = found!.thumbnailUpdatedAt\n\n context.type = decoded.type\n context.set('Cache-Control', 'private, max-age=31536000')\n if (stamp) {\n context.set('Last-Modified', new Date(stamp).toUTCString())\n context.set('ETag', `\"${new Date(stamp).getTime()}\"`)\n\n /* ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ๊ฐ€์ง„ ๊ฒƒ์ด ์•„์ง ๊ทธ๊ฒƒ์ด๋ฉด ๋ฐ”์ดํŠธ๋ฅผ ๋ณด๋‚ด์ง€ ์•Š๋Š”๋‹ค. */\n if (context.fresh) {\n context.status = 304\n return\n }\n }\n\n context.body = decoded.buffer\n})\n"]}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const env_1 = require("@things-factory/env");
4
+ const shell_1 = require("@things-factory/shell");
5
+ const figure_thumbnail_router_js_1 = require("./routers/figure-thumbnail-router.js");
6
+ /**
7
+ * ๋„๋ฉ”์ธ ๊ฒฝ๋กœ๊ฐ€ ์ฃผ์†Œ์— ๋“ค์–ด๊ฐ€๋Š” ๋ฐฐ์น˜์ธ๊ฐ€.
8
+ *
9
+ * `subdomain` ์„ค์ •์ด ์žˆ์œผ๋ฉด ๋„๋ฉ”์ธ์€ ํ˜ธ์ŠคํŠธ ์ด๋ฆ„์œผ๋กœ ๊ฐˆ๋ฆฌ๋ฏ€๋กœ ๊ฒฝ๋กœ์— ๋„๋ฉ”์ธ์ด ์—†๋‹ค. ์—†์œผ๋ฉด
10
+ * `/domain/{subdomain}/...` ๋กœ ๊ฐˆ๋ฆฐ๋‹ค โ€” board-service ๊ฐ€ ๊ฐ™์€ ํŒ๋‹จ์„ ๊ฐ™์€ ๋ฐฉ์‹์œผ๋กœ ํ•œ๋‹ค.
11
+ */
12
+ const isPathBaseDomain = !env_1.config.get('subdomain');
13
+ process.on('bootstrap-module-domain-private-route', (app, domainPrivateRouter) => {
14
+ if (isPathBaseDomain) {
15
+ domainPrivateRouter.use(`/${(0, shell_1.getRoutePrefixForDomainType)()}/:domain`, figure_thumbnail_router_js_1.figureThumbnailRouter.routes(), figure_thumbnail_router_js_1.figureThumbnailRouter.allowedMethods());
16
+ }
17
+ else {
18
+ domainPrivateRouter.use('', figure_thumbnail_router_js_1.figureThumbnailRouter.routes(), figure_thumbnail_router_js_1.figureThumbnailRouter.allowedMethods());
19
+ }
20
+ });
21
+ //# sourceMappingURL=routes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routes.js","sourceRoot":"","sources":["../server/routes.ts"],"names":[],"mappings":";;AAAA,6CAA4C;AAC5C,iDAAmE;AAEnE,qFAA4E;AAE5E;;;;;GAKG;AACH,MAAM,gBAAgB,GAAG,CAAC,YAAM,CAAC,GAAG,CAAC,WAAW,CAAC,CAAA;AAEjD,OAAO,CAAC,EAAE,CAAC,uCAA8C,EAAE,CAAC,GAAG,EAAE,mBAAmB,EAAE,EAAE;IACtF,IAAI,gBAAgB,EAAE,CAAC;QACrB,mBAAmB,CAAC,GAAG,CACrB,IAAI,IAAA,mCAA2B,GAAE,UAAU,EAC3C,kDAAqB,CAAC,MAAM,EAAE,EAC9B,kDAAqB,CAAC,cAAc,EAAE,CACvC,CAAA;IACH,CAAC;SAAM,CAAC;QACN,mBAAmB,CAAC,GAAG,CAAC,EAAE,EAAE,kDAAqB,CAAC,MAAM,EAAE,EAAE,kDAAqB,CAAC,cAAc,EAAE,CAAC,CAAA;IACrG,CAAC;AACH,CAAC,CAAC,CAAA","sourcesContent":["import { config } from '@things-factory/env'\nimport { getRoutePrefixForDomainType } from '@things-factory/shell'\n\nimport { figureThumbnailRouter } from './routers/figure-thumbnail-router.js'\n\n/**\n * ๋„๋ฉ”์ธ ๊ฒฝ๋กœ๊ฐ€ ์ฃผ์†Œ์— ๋“ค์–ด๊ฐ€๋Š” ๋ฐฐ์น˜์ธ๊ฐ€.\n *\n * `subdomain` ์„ค์ •์ด ์žˆ์œผ๋ฉด ๋„๋ฉ”์ธ์€ ํ˜ธ์ŠคํŠธ ์ด๋ฆ„์œผ๋กœ ๊ฐˆ๋ฆฌ๋ฏ€๋กœ ๊ฒฝ๋กœ์— ๋„๋ฉ”์ธ์ด ์—†๋‹ค. ์—†์œผ๋ฉด\n * `/domain/{subdomain}/...` ๋กœ ๊ฐˆ๋ฆฐ๋‹ค โ€” board-service ๊ฐ€ ๊ฐ™์€ ํŒ๋‹จ์„ ๊ฐ™์€ ๋ฐฉ์‹์œผ๋กœ ํ•œ๋‹ค.\n */\nconst isPathBaseDomain = !config.get('subdomain')\n\nprocess.on('bootstrap-module-domain-private-route' as any, (app, domainPrivateRouter) => {\n if (isPathBaseDomain) {\n domainPrivateRouter.use(\n `/${getRoutePrefixForDomainType()}/:domain`,\n figureThumbnailRouter.routes(),\n figureThumbnailRouter.allowedMethods()\n )\n } else {\n domainPrivateRouter.use('', figureThumbnailRouter.routes(), figureThumbnailRouter.allowedMethods())\n }\n})\n"]}
@@ -0,0 +1,21 @@
1
+ import { HistoryEntitySubscriber } from '@operato/typeorm-history';
2
+ import { Figure } from './figure.js';
3
+ import { FigureHistory } from './figure-history.js';
4
+ /**
5
+ * ํŒ๋ณธ์„ ๋‚จ๊ธด๋‹ค โ€” **๋ฐœํ–‰๋œ ์ €์žฅ์—๋งŒ.**
6
+ *
7
+ * `@operato/typeorm-history` ๋Š” ์›๋ž˜ ๋ชจ๋“  ์ €์žฅ์„ ๋ฒ ๋‚€๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ๋„ํ˜•์€ ์ €์ž‘ ์ค‘์— ์ˆ˜์‹ญ ๋ฒˆ ์ €์žฅ๋˜๊ณ ,
8
+ * ๊ทธ ์ „๋ถ€๋ฅผ ๋‚จ๊ธฐ๋ฉด ํŒ๋ณธ ๋ชฉ๋ก์ด ใ€Œ๋‚ด๊ฐ€ ์˜ค๋Š˜ ๋ช‡ ๋ฒˆ ๋ˆŒ๋ €๋‚˜ใ€๊ฐ€ ๋œ๋‹ค. ์‚ฌ๋žŒ์ด ํŒ์ด๋ผ๊ณ  ๋ถ€๋ฅด๋Š” ๊ฒƒ์€
9
+ * **๋ฐœํ–‰ํ•œ ๊ฒƒ**์ด๋ฏ€๋กœ ๊ทธ๋•Œ๋งŒ ๋ฒ ๋‚€๋‹ค. board ๊ฐ€ ๊ฐ™์€ ๊ทœ์น™์œผ๋กœ ๋ˆ๋‹ค(`board-service` ์˜ ๊ฐ™์€ ์ด๋ฆ„ ํŒŒ์ผ).
10
+ *
11
+ * ๋ฐœํ–‰ ๋ฉ”๋ชจ๋Š” Figure ์˜ ์นธ์ด ์•„๋‹ˆ๋‹ค. `releaseFigure` ๊ฐ€ ์ €์žฅํ•  ์—”ํ‹ฐํ‹ฐ์— `__releaseComment` ๋กœ ์–น์–ด
12
+ * ๋‘๋ฉด ์—ฌ๊ธฐ์„œ ํŒ๋ณธ์˜ `comment` ๋กœ ์˜ฎ๊ธด๋‹ค โ€” ๋ฉ”๋ชจ๋Š” ๊ทธ ํŒ์˜ ๊ฒƒ์ด์ง€ ๋„ํ˜•์˜ ๊ฒƒ์ด ์•„๋‹ˆ๋‹ค.
13
+ */
14
+ export declare class FigureHistoryEntitySubscriber extends HistoryEntitySubscriber<Figure, FigureHistory> {
15
+ get entity(): typeof Figure;
16
+ get historyEntity(): typeof FigureHistory;
17
+ beforeInsertHistory(history: FigureHistory, entity: Figure): FigureHistory;
18
+ beforeUpdateHistory(history: FigureHistory, entity: Figure): FigureHistory;
19
+ afterInsert(event: any): Promise<void>;
20
+ afterUpdate(event: any): Promise<void>;
21
+ }
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FigureHistoryEntitySubscriber = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const typeorm_1 = require("typeorm");
6
+ const typeorm_history_1 = require("@operato/typeorm-history");
7
+ const figure_js_1 = require("./figure.js");
8
+ const figure_history_js_1 = require("./figure-history.js");
9
+ /**
10
+ * ํŒ๋ณธ์„ ๋‚จ๊ธด๋‹ค โ€” **๋ฐœํ–‰๋œ ์ €์žฅ์—๋งŒ.**
11
+ *
12
+ * `@operato/typeorm-history` ๋Š” ์›๋ž˜ ๋ชจ๋“  ์ €์žฅ์„ ๋ฒ ๋‚€๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ๋„ํ˜•์€ ์ €์ž‘ ์ค‘์— ์ˆ˜์‹ญ ๋ฒˆ ์ €์žฅ๋˜๊ณ ,
13
+ * ๊ทธ ์ „๋ถ€๋ฅผ ๋‚จ๊ธฐ๋ฉด ํŒ๋ณธ ๋ชฉ๋ก์ด ใ€Œ๋‚ด๊ฐ€ ์˜ค๋Š˜ ๋ช‡ ๋ฒˆ ๋ˆŒ๋ €๋‚˜ใ€๊ฐ€ ๋œ๋‹ค. ์‚ฌ๋žŒ์ด ํŒ์ด๋ผ๊ณ  ๋ถ€๋ฅด๋Š” ๊ฒƒ์€
14
+ * **๋ฐœํ–‰ํ•œ ๊ฒƒ**์ด๋ฏ€๋กœ ๊ทธ๋•Œ๋งŒ ๋ฒ ๋‚€๋‹ค. board ๊ฐ€ ๊ฐ™์€ ๊ทœ์น™์œผ๋กœ ๋ˆ๋‹ค(`board-service` ์˜ ๊ฐ™์€ ์ด๋ฆ„ ํŒŒ์ผ).
15
+ *
16
+ * ๋ฐœํ–‰ ๋ฉ”๋ชจ๋Š” Figure ์˜ ์นธ์ด ์•„๋‹ˆ๋‹ค. `releaseFigure` ๊ฐ€ ์ €์žฅํ•  ์—”ํ‹ฐํ‹ฐ์— `__releaseComment` ๋กœ ์–น์–ด
17
+ * ๋‘๋ฉด ์—ฌ๊ธฐ์„œ ํŒ๋ณธ์˜ `comment` ๋กœ ์˜ฎ๊ธด๋‹ค โ€” ๋ฉ”๋ชจ๋Š” ๊ทธ ํŒ์˜ ๊ฒƒ์ด์ง€ ๋„ํ˜•์˜ ๊ฒƒ์ด ์•„๋‹ˆ๋‹ค.
18
+ */
19
+ let FigureHistoryEntitySubscriber = class FigureHistoryEntitySubscriber extends typeorm_history_1.HistoryEntitySubscriber {
20
+ get entity() {
21
+ return figure_js_1.Figure;
22
+ }
23
+ get historyEntity() {
24
+ return figure_history_js_1.FigureHistory;
25
+ }
26
+ beforeInsertHistory(history, entity) {
27
+ ;
28
+ history.comment = entity.__releaseComment ?? null;
29
+ return history;
30
+ }
31
+ beforeUpdateHistory(history, entity) {
32
+ ;
33
+ history.comment = entity.__releaseComment ?? null;
34
+ return history;
35
+ }
36
+ async afterInsert(event) {
37
+ if (event.entity?.state == 'released') {
38
+ await super.afterInsert(event);
39
+ }
40
+ }
41
+ async afterUpdate(event) {
42
+ if (event.entity?.state == 'released') {
43
+ await super.afterUpdate(event);
44
+ }
45
+ }
46
+ };
47
+ exports.FigureHistoryEntitySubscriber = FigureHistoryEntitySubscriber;
48
+ exports.FigureHistoryEntitySubscriber = FigureHistoryEntitySubscriber = tslib_1.__decorate([
49
+ (0, typeorm_1.EventSubscriber)()
50
+ ], FigureHistoryEntitySubscriber);
51
+ //# sourceMappingURL=event-subscriber.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-subscriber.js","sourceRoot":"","sources":["../../../server/service/figure/event-subscriber.ts"],"names":[],"mappings":";;;;AAAA,qCAAyC;AAEzC,8DAAkE;AAElE,2CAAoC;AACpC,2DAAmD;AAEnD;;;;;;;;;GASG;AAEI,IAAM,6BAA6B,GAAnC,MAAM,6BAA8B,SAAQ,yCAA8C;IAC/F,IAAW,MAAM;QACf,OAAO,kBAAM,CAAA;IACf,CAAC;IAED,IAAW,aAAa;QACtB,OAAO,iCAAa,CAAA;IACtB,CAAC;IAEM,mBAAmB,CAAC,OAAsB,EAAE,MAAc;QAC/D,CAAC;QAAC,OAAe,CAAC,OAAO,GAAI,MAAc,CAAC,gBAAgB,IAAI,IAAI,CAAA;QACpE,OAAO,OAAO,CAAA;IAChB,CAAC;IAEM,mBAAmB,CAAC,OAAsB,EAAE,MAAc;QAC/D,CAAC;QAAC,OAAe,CAAC,OAAO,GAAI,MAAc,CAAC,gBAAgB,IAAI,IAAI,CAAA;QACpE,OAAO,OAAO,CAAA;IAChB,CAAC;IAEM,KAAK,CAAC,WAAW,CAAC,KAAU;QACjC,IAAI,KAAK,CAAC,MAAM,EAAE,KAAK,IAAI,UAAU,EAAE,CAAC;YACtC,MAAM,KAAK,CAAC,WAAW,CAAC,KAAK,CAAC,CAAA;QAChC,CAAC;IACH,CAAC;IAEM,KAAK,CAAC,WAAW,CAAC,KAAU;QACjC,IAAI,KAAK,CAAC,MAAM,EAAE,KAAK,IAAI,UAAU,EAAE,CAAC;YACtC,MAAM,KAAK,CAAC,WAAW,CAAC,KAAK,CAAC,CAAA;QAChC,CAAC;IACH,CAAC;CACF,CAAA;AA9BY,sEAA6B;wCAA7B,6BAA6B;IADzC,IAAA,yBAAe,GAAE;GACL,6BAA6B,CA8BzC","sourcesContent":["import { EventSubscriber } from 'typeorm'\n\nimport { HistoryEntitySubscriber } from '@operato/typeorm-history'\n\nimport { Figure } from './figure.js'\nimport { FigureHistory } from './figure-history.js'\n\n/**\n * ํŒ๋ณธ์„ ๋‚จ๊ธด๋‹ค โ€” **๋ฐœํ–‰๋œ ์ €์žฅ์—๋งŒ.**\n *\n * `@operato/typeorm-history` ๋Š” ์›๋ž˜ ๋ชจ๋“  ์ €์žฅ์„ ๋ฒ ๋‚€๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ๋„ํ˜•์€ ์ €์ž‘ ์ค‘์— ์ˆ˜์‹ญ ๋ฒˆ ์ €์žฅ๋˜๊ณ ,\n * ๊ทธ ์ „๋ถ€๋ฅผ ๋‚จ๊ธฐ๋ฉด ํŒ๋ณธ ๋ชฉ๋ก์ด ใ€Œ๋‚ด๊ฐ€ ์˜ค๋Š˜ ๋ช‡ ๋ฒˆ ๋ˆŒ๋ €๋‚˜ใ€๊ฐ€ ๋œ๋‹ค. ์‚ฌ๋žŒ์ด ํŒ์ด๋ผ๊ณ  ๋ถ€๋ฅด๋Š” ๊ฒƒ์€\n * **๋ฐœํ–‰ํ•œ ๊ฒƒ**์ด๋ฏ€๋กœ ๊ทธ๋•Œ๋งŒ ๋ฒ ๋‚€๋‹ค. board ๊ฐ€ ๊ฐ™์€ ๊ทœ์น™์œผ๋กœ ๋ˆ๋‹ค(`board-service` ์˜ ๊ฐ™์€ ์ด๋ฆ„ ํŒŒ์ผ).\n *\n * ๋ฐœํ–‰ ๋ฉ”๋ชจ๋Š” Figure ์˜ ์นธ์ด ์•„๋‹ˆ๋‹ค. `releaseFigure` ๊ฐ€ ์ €์žฅํ•  ์—”ํ‹ฐํ‹ฐ์— `__releaseComment` ๋กœ ์–น์–ด\n * ๋‘๋ฉด ์—ฌ๊ธฐ์„œ ํŒ๋ณธ์˜ `comment` ๋กœ ์˜ฎ๊ธด๋‹ค โ€” ๋ฉ”๋ชจ๋Š” ๊ทธ ํŒ์˜ ๊ฒƒ์ด์ง€ ๋„ํ˜•์˜ ๊ฒƒ์ด ์•„๋‹ˆ๋‹ค.\n */\n@EventSubscriber()\nexport class FigureHistoryEntitySubscriber extends HistoryEntitySubscriber<Figure, FigureHistory> {\n public get entity() {\n return Figure\n }\n\n public get historyEntity() {\n return FigureHistory\n }\n\n public beforeInsertHistory(history: FigureHistory, entity: Figure): FigureHistory {\n ;(history as any).comment = (entity as any).__releaseComment ?? null\n return history\n }\n\n public beforeUpdateHistory(history: FigureHistory, entity: Figure): FigureHistory {\n ;(history as any).comment = (entity as any).__releaseComment ?? null\n return history\n }\n\n public async afterInsert(event: any): Promise<void> {\n if (event.entity?.state == 'released') {\n await super.afterInsert(event)\n }\n }\n\n public async afterUpdate(event: any): Promise<void> {\n if (event.entity?.state == 'released') {\n await super.afterUpdate(event)\n }\n }\n}\n"]}
@@ -0,0 +1,55 @@
1
+ /**
2
+ * ๋ฐœํ–‰ํ•ด๋„ ๋˜๋‚˜ โ€” **์„œ๋ฒ„๊ฐ€ ํŒ์ •ํ•œ๋‹ค.**
3
+ *
4
+ * ## ์™œ ์„œ๋ฒ„์ธ๊ฐ€
5
+ *
6
+ * ์ด ๊ฒ€์‚ฌ๋Š” ๋ธŒ๋ผ์šฐ์ € ํ•˜๋„ค์Šค์—๋งŒ ์žˆ์—ˆ๋‹ค(`test/browser/check-figure-scaling.mjs`). ์‚ฌ๋žŒ์ด ๋Œ๋ฆด ๋•Œ๋งŒ
7
+ * ๋Œ์•˜๊ณ , ๋ฐœํ–‰ ๊ฒฝ๋กœ์—๋Š” ์•„๋ฌด ๊ฒ€์‚ฌ๋„ ์—†์—ˆ๋‹ค โ€” ํ˜•์‹๋งŒ ๋งž์œผ๋ฉด ๋ฌด์—‡์ด๋“  ๋ฐœํ–‰๋˜์—ˆ๋‹ค. ๋ฐœํ–‰์€ ์†Œ๋น„์ฒ˜์—๊ฒŒ
8
+ * ํ•˜๋Š” ์•ฝ์†์ด๋ฏ€๋กœ, ๊ทธ ์•ฝ์†์„ ์ง€ํ‚ฌ ์ˆ˜ ์žˆ๋Š”์ง€๋Š” ์•ฝ์†ํ•˜๋Š” ์ž๋ฆฌ๊ฐ€ ์žฌ์•ผ ํ•œ๋‹ค.
9
+ *
10
+ * ## ๋ฌด์—‡์„ ๋ง‰๊ณ  ๋ฌด์—‡์„ ์•Œ๋ฆฌ๋‚˜
11
+ *
12
+ * ํŒ์ •์€ `figure-model` ์˜ `gate.ts` ๊ฐ€ ์ด๋ฏธ ๋ฌธ์„œ๋กœ ์ •ํ•ด ๋‘์—ˆ๋‹ค. ์—ฌ๊ธฐ์„œ ์ƒˆ๋กœ ์ •ํ•˜์ง€ ์•Š๋Š”๋‹ค.
13
+ *
14
+ * sizing-splits ํฌ๊ธฐ๋ฅผ ๋ฐ”๊พธ๋ฉด ์กฐ๊ฐ์ด ๊ฐˆ๋ผ์ง„๋‹ค **๋ง‰๋Š”๋‹ค**
15
+ * sizing-loose 1๋ฐฐ์—์„œ ์ด๋ฏธ ์—ฌ๋Ÿฌ ์กฐ๊ฐ์ด๋‹ค ์•Œ๋ฆฐ๋‹ค
16
+ * violations ํ˜•์‹์ด ๊ถŒํ•˜์ง€ ์•Š๋Š” ๊ฒƒ ์•Œ๋ฆฐ๋‹ค (์ €์žฅ ๋•Œ ์ด๋ฏธ ๋ณด์—ฌ ์ค€ ๊ฒƒ๋“ค)
17
+ *
18
+ * ๊ฐˆ๋ผ์ง์„ ๋ง‰๋Š” ์ด์œ ๋Š” ์‚ฌ๋žŒ ๋ˆˆ์— ใ€Œ๋ถ€์„œ์กŒ๋‹คใ€๋กœ ๋ณด์ด๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค โ€” ์‹œ๊ทธ๋„ํƒ€์›Œ๋ฅผ 3๋ฐฐ๋กœ ์„ธ์šฐ๋‹ˆ ๋žจํ”„๊ฐ€
19
+ * ๊ธฐ๋‘ฅ ์œ„ ๊ณต์ค‘์— ๋–ด๋˜ ๊ทธ๊ฒƒ์ด๊ณ , ์•ต์ปค ๊ทœ์น™์ด ์ƒ๊ธด ์ด์œ ๋‹ค. ์†Œ๋น„์ฒ˜๊ฐ€ ํ˜•์ƒ์„ ๋Š˜๋ ค ์“ฐ๋Š” ๊ฒƒ์€ ๋ง‰์„ ์ˆ˜
20
+ * ์—†์œผ๋‹ˆ(๊ทธ๊ฒƒ์ด ์ด ํ˜•์‹์˜ ๊ฐ’์ด๋‹ค), ๋Š˜๋ ค๋„ ๊ฒฌ๋””๋Š”์ง€๋ฅผ ๋ฐœํ–‰ ์ „์— ๋ณธ๋‹ค.
21
+ *
22
+ * ## ์žฌ ๋ณธ ๊ฐ’
23
+ *
24
+ * [ํ™•์ธํ•จ] 2026-09-09 ยท ํ‘œ๋ณธ 15๊ฐœ(์‹œ๋“œ 14 + ์ €์ž‘ 1)
25
+ * ํ˜•์‹ ์˜ค๋ฅ˜ 0 ยท 1๋ฐฐ ๋ถ„๋ฆฌ 0 ยท **๊ฐˆ๋ผ์ง 1**(MIXER, zยทy-z ์กฐํ•ฉ) ยท ์กฐ์šฉํ•จ 14
26
+ *
27
+ * ๊ฒŒ์ดํŠธ๊ฐ€ ๋ฒฝ์ด ์•„๋‹ˆ๋ผ๋Š” ๋œป์ด๋‹ค. ์—ด๋„ท์€ ๊ทธ๋Œ€๋กœ ๋ฐœํ–‰๋˜๊ณ , ํ•˜๋‚˜๋Š” ๊ณ ์น  ๊ฒƒ์ด ์žˆ๋‹ค.
28
+ */
29
+ /** ๊ฒŒ์ดํŠธ๊ฐ€ ๋‚ธ ํ•œ ๊ฐ€์ง€. `blocking` ์ด ์ฐธ์ด๋ฉด ๋ฐœํ–‰์ด ์•ˆ ๋œ๋‹ค. */
30
+ export interface GateFinding {
31
+ code: string;
32
+ message: string;
33
+ /**
34
+ * **์™œ ๊ทธ๊ฒƒ์ด ๋ฌธ์ œ์ธ๊ฐ€.** ์žฐ ๊ฒƒ๋งŒ ๋ณด์—ฌ ์ฃผ๋ฉด ์ฝ๋Š” ์‚ฌ๋žŒ์ด ใ€Œ๊ทธ๋ž˜์„œ?ใ€์—์„œ ๋ฉˆ์ถ˜๋‹ค โ€” ํ™”๋ฉด์—
35
+ * ใ€Œ1 ์กฐ๊ฐ์ด 2 ์กฐ๊ฐ์œผ๋กœ ๊ฐˆ๋ผ์ง„๋‹คใ€๋งŒ ๋–ด์„ ๋•Œ ์‹ค์ œ๋กœ ๊ทธ๋žฌ๋‹ค.
36
+ */
37
+ why?: string;
38
+ /** **๋ฌด์—‡์„ ๋ฐ”๊พธ๋ฉด ๋˜๋‚˜.** ๋ถ€ํ’ˆ ์ด๋ฆ„๊ณผ ๊ฐ’์œผ๋กœ. ํ˜•์‹์ด ๋งŒ๋“  ๋ฌธ์žฅ์„ ๊ทธ๋Œ€๋กœ ์˜ฎ๊ธด๋‹ค. */
39
+ how?: string;
40
+ /** ์–ด๋””๋ฅผ ๊ฐ€๋ฆฌํ‚ค๋‚˜ โ€” ๊ฐˆ๋ผ์ง์€ ๊ฐˆ๋ผ์ง„ ๋ถ€ํ’ˆ ์ด๋ฆ„๋“ค, ํ˜•์‹ ์œ„๋ฐ˜์€ ์ •๋ณธ ์•ˆ์˜ ๊ฒฝ๋กœ. */
41
+ at?: string;
42
+ blocking: boolean;
43
+ }
44
+ export interface Inspection {
45
+ findings: GateFinding[];
46
+ /** ๋ง‰๋Š” ๊ฒƒ์ด ํ•˜๋‚˜๋ผ๋„ ์žˆ๋‚˜. ํ™”๋ฉด์ด ์ด๊ฒƒ์œผ๋กœ ๋‹จ์ถ”๋ฅผ ์ž ๊ทผ๋‹ค. */
47
+ blocked: boolean;
48
+ }
49
+ /**
50
+ * ์ •๋ณธ ํ•˜๋‚˜๋ฅผ ํŒ์ •ํ•œ๋‹ค.
51
+ *
52
+ * ์ •๋ณธ์ด ์—†๊ฑฐ๋‚˜(์•„์ง ์ €์žฅ ์ „) JSON ์ด ์•„๋‹ˆ๋ฉด **๋ง‰๋Š”๋‹ค** โ€” ๋ฐœํ–‰ํ•  ๋Œ€์ƒ์ด ์—†๋Š” ๊ฒƒ์ด๋‹ค. ๊ทธ ๊ฒฝ์šฐ๋ฅผ
53
+ * ์กฐ์šฉํžˆ ํ†ต๊ณผ์‹œํ‚ค๋ฉด ๋นˆ ๊ฒƒ์ด ๋ฐœํ–‰๋œ๋‹ค.
54
+ */
55
+ export declare function inspectSource(sourceJson?: string | null): Inspection;
@@ -0,0 +1,64 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.inspectSource = inspectSource;
4
+ const figure_model_1 = require("@hatiolab/figure-model");
5
+ /**
6
+ * ์ •๋ณธ ํ•˜๋‚˜๋ฅผ ํŒ์ •ํ•œ๋‹ค.
7
+ *
8
+ * ์ •๋ณธ์ด ์—†๊ฑฐ๋‚˜(์•„์ง ์ €์žฅ ์ „) JSON ์ด ์•„๋‹ˆ๋ฉด **๋ง‰๋Š”๋‹ค** โ€” ๋ฐœํ–‰ํ•  ๋Œ€์ƒ์ด ์—†๋Š” ๊ฒƒ์ด๋‹ค. ๊ทธ ๊ฒฝ์šฐ๋ฅผ
9
+ * ์กฐ์šฉํžˆ ํ†ต๊ณผ์‹œํ‚ค๋ฉด ๋นˆ ๊ฒƒ์ด ๋ฐœํ–‰๋œ๋‹ค.
10
+ */
11
+ function inspectSource(sourceJson) {
12
+ if (!sourceJson) {
13
+ return {
14
+ blocked: true,
15
+ findings: [
16
+ {
17
+ code: 'no-source',
18
+ message: '์ €์žฅ๋œ ์ •๋ณธ์ด ์—†๋‹ค. ์ €์žฅํ•˜๋ฉด ๋ฐœํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค',
19
+ blocking: true
20
+ }
21
+ ]
22
+ };
23
+ }
24
+ let parsed;
25
+ try {
26
+ parsed = JSON.parse(sourceJson);
27
+ }
28
+ catch (e) {
29
+ return {
30
+ blocked: true,
31
+ findings: [{ code: 'source-not-json', message: `์ •๋ณธ์ด JSON ์ด ์•„๋‹ˆ๋‹ค: ${e.message}`, blocking: true }]
32
+ };
33
+ }
34
+ const result = (0, figure_model_1.validate)(parsed);
35
+ const findings = [];
36
+ /*
37
+ ํ˜•์‹ ์˜ค๋ฅ˜๋Š” **์ €์žฅ์„ ํ†ต๊ณผํ•  ์ˆ˜ ์—†๋‹ค**(`parseSource` ๊ฐ€ ๊ฑฐ์ ˆํ•œ๋‹ค). ๊ทธ๋ž˜๋„ ์—ฌ๊ธฐ์„œ ๋‹ค์‹œ ๋ณด๋Š” ์ด์œ ๋Š”,
38
+ ์ด ํŒ์ •์ด ์ €์žฅ ๊ฒฝ๋กœ์™€ ๋…๋ฆฝ์ด์–ด์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค โ€” ์˜› ์ •๋ณธ์ด ๋‚จ์•„ ์žˆ๊ฑฐ๋‚˜ ํ˜•์‹์ด ์—„ํ•ด์ง„ ๋’ค๋ผ๋ฉด
39
+ ์ €์žฅ ๋•Œ๋Š” ๋งž์•˜๋˜ ๊ฒƒ์ด ์ง€๊ธˆ์€ ํ‹€๋ฆด ์ˆ˜ ์žˆ๋‹ค. ๊ทธ๋•Œ ๋ฐœํ–‰์ด ํ†ต๊ณผํ•˜๋ฉด ์•ˆ ๋œ๋‹ค.
40
+ */
41
+ for (const error of result.errors) {
42
+ findings.push({ code: error.code, message: error.message, at: error.path, blocking: true });
43
+ }
44
+ for (const violation of result.violations) {
45
+ findings.push({ code: violation.code, message: violation.message, blocking: false });
46
+ }
47
+ if (result.errors.length === 0) {
48
+ const report = (0, figure_model_1.sizingReport)((0, figure_model_1.compile)(parsed));
49
+ for (const finding of report.findings) {
50
+ findings.push({
51
+ code: finding.code,
52
+ message: finding.message,
53
+ /* ์™œยท์–ด๋–ป๊ฒŒ๋Š” ํ˜•์‹์ด ์†Œ์œ ํ•œ๋‹ค. ์„œ๋ฒ„๊ฐ€ ๋‹ค์‹œ ์“ฐ๋ฉด ๋‘ ๋ฒŒ์ด ๋˜๊ณ  ๊ณง ์–ด๊ธ‹๋‚œ๋‹ค. */
54
+ why: finding.why,
55
+ how: finding.how,
56
+ /* ์ˆซ์ž๊ฐ€ ์•„๋‹ˆ๋ผ ์ž๋ฆฌ๋ฅผ ๋งํ•œ๋‹ค โ€” ์–ด๋А ๋ถ€ํ’ˆ๋“ค์ด ๊ฐˆ๋ผ์ ธ ๋‚˜์™”๋‚˜. */
57
+ at: finding.clumps.map(clump => [...clump].sort().join('+')).join(' | ') || undefined,
58
+ blocking: finding.code === 'sizing-splits'
59
+ });
60
+ }
61
+ }
62
+ return { findings, blocked: findings.some(finding => finding.blocking) };
63
+ }
64
+ //# sourceMappingURL=figure-gate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"figure-gate.js","sourceRoot":"","sources":["../../../server/service/figure/figure-gate.ts"],"names":[],"mappings":";;AA4DA,sCA0DC;AAtHD,yDAAwE;AAsDxE;;;;;GAKG;AACH,SAAgB,aAAa,CAAC,UAA0B;IACtD,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,OAAO;YACL,OAAO,EAAE,IAAI;YACb,QAAQ,EAAE;gBACR;oBACE,IAAI,EAAE,WAAW;oBACjB,OAAO,EAAE,2BAA2B;oBACpC,QAAQ,EAAE,IAAI;iBACf;aACF;SACF,CAAA;IACH,CAAC;IAED,IAAI,MAAe,CAAA;IACnB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAA;IACjC,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,OAAO;YACL,OAAO,EAAE,IAAI;YACb,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,iBAAiB,EAAE,OAAO,EAAE,mBAAoB,CAAW,CAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;SAC5G,CAAA;IACH,CAAC;IAED,MAAM,MAAM,GAAG,IAAA,uBAAQ,EAAC,MAAM,CAAC,CAAA;IAC/B,MAAM,QAAQ,GAAkB,EAAE,CAAA;IAElC;;;;MAIE;IACF,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;QAClC,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,EAAE,EAAE,KAAK,CAAC,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAA;IAC7F,CAAC;IAED,KAAK,MAAM,SAAS,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC;QAC1C,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,SAAS,CAAC,IAAI,EAAE,OAAO,EAAE,SAAS,CAAC,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,CAAA;IACtF,CAAC;IAED,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC/B,MAAM,MAAM,GAAG,IAAA,2BAAY,EAAC,IAAA,sBAAO,EAAC,MAAsB,CAAC,CAAC,CAAA;QAE5D,KAAK,MAAM,OAAO,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;YACtC,QAAQ,CAAC,IAAI,CAAC;gBACZ,IAAI,EAAE,OAAO,CAAC,IAAI;gBAClB,OAAO,EAAE,OAAO,CAAC,OAAO;gBACxB,gDAAgD;gBAChD,GAAG,EAAE,OAAO,CAAC,GAAG;gBAChB,GAAG,EAAE,OAAO,CAAC,GAAG;gBAChB,wCAAwC;gBACxC,EAAE,EAAE,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,SAAS;gBACrF,QAAQ,EAAE,OAAO,CAAC,IAAI,KAAK,eAAe;aAC3C,CAAC,CAAA;QACJ,CAAC;IACH,CAAC;IAED,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAA;AAC1E,CAAC","sourcesContent":["import { compile, sizingReport, validate } from '@hatiolab/figure-model'\nimport type { FigureSource } from '@hatiolab/figure-model'\n\n/**\n * ๋ฐœํ–‰ํ•ด๋„ ๋˜๋‚˜ โ€” **์„œ๋ฒ„๊ฐ€ ํŒ์ •ํ•œ๋‹ค.**\n *\n * ## ์™œ ์„œ๋ฒ„์ธ๊ฐ€\n *\n * ์ด ๊ฒ€์‚ฌ๋Š” ๋ธŒ๋ผ์šฐ์ € ํ•˜๋„ค์Šค์—๋งŒ ์žˆ์—ˆ๋‹ค(`test/browser/check-figure-scaling.mjs`). ์‚ฌ๋žŒ์ด ๋Œ๋ฆด ๋•Œ๋งŒ\n * ๋Œ์•˜๊ณ , ๋ฐœํ–‰ ๊ฒฝ๋กœ์—๋Š” ์•„๋ฌด ๊ฒ€์‚ฌ๋„ ์—†์—ˆ๋‹ค โ€” ํ˜•์‹๋งŒ ๋งž์œผ๋ฉด ๋ฌด์—‡์ด๋“  ๋ฐœํ–‰๋˜์—ˆ๋‹ค. ๋ฐœํ–‰์€ ์†Œ๋น„์ฒ˜์—๊ฒŒ\n * ํ•˜๋Š” ์•ฝ์†์ด๋ฏ€๋กœ, ๊ทธ ์•ฝ์†์„ ์ง€ํ‚ฌ ์ˆ˜ ์žˆ๋Š”์ง€๋Š” ์•ฝ์†ํ•˜๋Š” ์ž๋ฆฌ๊ฐ€ ์žฌ์•ผ ํ•œ๋‹ค.\n *\n * ## ๋ฌด์—‡์„ ๋ง‰๊ณ  ๋ฌด์—‡์„ ์•Œ๋ฆฌ๋‚˜\n *\n * ํŒ์ •์€ `figure-model` ์˜ `gate.ts` ๊ฐ€ ์ด๋ฏธ ๋ฌธ์„œ๋กœ ์ •ํ•ด ๋‘์—ˆ๋‹ค. ์—ฌ๊ธฐ์„œ ์ƒˆ๋กœ ์ •ํ•˜์ง€ ์•Š๋Š”๋‹ค.\n *\n * sizing-splits ํฌ๊ธฐ๋ฅผ ๋ฐ”๊พธ๋ฉด ์กฐ๊ฐ์ด ๊ฐˆ๋ผ์ง„๋‹ค **๋ง‰๋Š”๋‹ค**\n * sizing-loose 1๋ฐฐ์—์„œ ์ด๋ฏธ ์—ฌ๋Ÿฌ ์กฐ๊ฐ์ด๋‹ค ์•Œ๋ฆฐ๋‹ค\n * violations ํ˜•์‹์ด ๊ถŒํ•˜์ง€ ์•Š๋Š” ๊ฒƒ ์•Œ๋ฆฐ๋‹ค (์ €์žฅ ๋•Œ ์ด๋ฏธ ๋ณด์—ฌ ์ค€ ๊ฒƒ๋“ค)\n *\n * ๊ฐˆ๋ผ์ง์„ ๋ง‰๋Š” ์ด์œ ๋Š” ์‚ฌ๋žŒ ๋ˆˆ์— ใ€Œ๋ถ€์„œ์กŒ๋‹คใ€๋กœ ๋ณด์ด๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค โ€” ์‹œ๊ทธ๋„ํƒ€์›Œ๋ฅผ 3๋ฐฐ๋กœ ์„ธ์šฐ๋‹ˆ ๋žจํ”„๊ฐ€\n * ๊ธฐ๋‘ฅ ์œ„ ๊ณต์ค‘์— ๋–ด๋˜ ๊ทธ๊ฒƒ์ด๊ณ , ์•ต์ปค ๊ทœ์น™์ด ์ƒ๊ธด ์ด์œ ๋‹ค. ์†Œ๋น„์ฒ˜๊ฐ€ ํ˜•์ƒ์„ ๋Š˜๋ ค ์“ฐ๋Š” ๊ฒƒ์€ ๋ง‰์„ ์ˆ˜\n * ์—†์œผ๋‹ˆ(๊ทธ๊ฒƒ์ด ์ด ํ˜•์‹์˜ ๊ฐ’์ด๋‹ค), ๋Š˜๋ ค๋„ ๊ฒฌ๋””๋Š”์ง€๋ฅผ ๋ฐœํ–‰ ์ „์— ๋ณธ๋‹ค.\n *\n * ## ์žฌ ๋ณธ ๊ฐ’\n *\n * [ํ™•์ธํ•จ] 2026-09-09 ยท ํ‘œ๋ณธ 15๊ฐœ(์‹œ๋“œ 14 + ์ €์ž‘ 1)\n * ํ˜•์‹ ์˜ค๋ฅ˜ 0 ยท 1๋ฐฐ ๋ถ„๋ฆฌ 0 ยท **๊ฐˆ๋ผ์ง 1**(MIXER, zยทy-z ์กฐํ•ฉ) ยท ์กฐ์šฉํ•จ 14\n *\n * ๊ฒŒ์ดํŠธ๊ฐ€ ๋ฒฝ์ด ์•„๋‹ˆ๋ผ๋Š” ๋œป์ด๋‹ค. ์—ด๋„ท์€ ๊ทธ๋Œ€๋กœ ๋ฐœํ–‰๋˜๊ณ , ํ•˜๋‚˜๋Š” ๊ณ ์น  ๊ฒƒ์ด ์žˆ๋‹ค.\n */\n\n/** ๊ฒŒ์ดํŠธ๊ฐ€ ๋‚ธ ํ•œ ๊ฐ€์ง€. `blocking` ์ด ์ฐธ์ด๋ฉด ๋ฐœํ–‰์ด ์•ˆ ๋œ๋‹ค. */\nexport interface GateFinding {\n code: string\n message: string\n /**\n * **์™œ ๊ทธ๊ฒƒ์ด ๋ฌธ์ œ์ธ๊ฐ€.** ์žฐ ๊ฒƒ๋งŒ ๋ณด์—ฌ ์ฃผ๋ฉด ์ฝ๋Š” ์‚ฌ๋žŒ์ด ใ€Œ๊ทธ๋ž˜์„œ?ใ€์—์„œ ๋ฉˆ์ถ˜๋‹ค โ€” ํ™”๋ฉด์—\n * ใ€Œ1 ์กฐ๊ฐ์ด 2 ์กฐ๊ฐ์œผ๋กœ ๊ฐˆ๋ผ์ง„๋‹คใ€๋งŒ ๋–ด์„ ๋•Œ ์‹ค์ œ๋กœ ๊ทธ๋žฌ๋‹ค.\n */\n why?: string\n /** **๋ฌด์—‡์„ ๋ฐ”๊พธ๋ฉด ๋˜๋‚˜.** ๋ถ€ํ’ˆ ์ด๋ฆ„๊ณผ ๊ฐ’์œผ๋กœ. ํ˜•์‹์ด ๋งŒ๋“  ๋ฌธ์žฅ์„ ๊ทธ๋Œ€๋กœ ์˜ฎ๊ธด๋‹ค. */\n how?: string\n /** ์–ด๋””๋ฅผ ๊ฐ€๋ฆฌํ‚ค๋‚˜ โ€” ๊ฐˆ๋ผ์ง์€ ๊ฐˆ๋ผ์ง„ ๋ถ€ํ’ˆ ์ด๋ฆ„๋“ค, ํ˜•์‹ ์œ„๋ฐ˜์€ ์ •๋ณธ ์•ˆ์˜ ๊ฒฝ๋กœ. */\n at?: string\n blocking: boolean\n}\n\nexport interface Inspection {\n findings: GateFinding[]\n /** ๋ง‰๋Š” ๊ฒƒ์ด ํ•˜๋‚˜๋ผ๋„ ์žˆ๋‚˜. ํ™”๋ฉด์ด ์ด๊ฒƒ์œผ๋กœ ๋‹จ์ถ”๋ฅผ ์ž ๊ทผ๋‹ค. */\n blocked: boolean\n}\n\n/**\n * ์ •๋ณธ ํ•˜๋‚˜๋ฅผ ํŒ์ •ํ•œ๋‹ค.\n *\n * ์ •๋ณธ์ด ์—†๊ฑฐ๋‚˜(์•„์ง ์ €์žฅ ์ „) JSON ์ด ์•„๋‹ˆ๋ฉด **๋ง‰๋Š”๋‹ค** โ€” ๋ฐœํ–‰ํ•  ๋Œ€์ƒ์ด ์—†๋Š” ๊ฒƒ์ด๋‹ค. ๊ทธ ๊ฒฝ์šฐ๋ฅผ\n * ์กฐ์šฉํžˆ ํ†ต๊ณผ์‹œํ‚ค๋ฉด ๋นˆ ๊ฒƒ์ด ๋ฐœํ–‰๋œ๋‹ค.\n */\nexport function inspectSource(sourceJson?: string | null): Inspection {\n if (!sourceJson) {\n return {\n blocked: true,\n findings: [\n {\n code: 'no-source',\n message: '์ €์žฅ๋œ ์ •๋ณธ์ด ์—†๋‹ค. ์ €์žฅํ•˜๋ฉด ๋ฐœํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค',\n blocking: true\n }\n ]\n }\n }\n\n let parsed: unknown\n try {\n parsed = JSON.parse(sourceJson)\n } catch (e) {\n return {\n blocked: true,\n findings: [{ code: 'source-not-json', message: `์ •๋ณธ์ด JSON ์ด ์•„๋‹ˆ๋‹ค: ${(e as Error).message}`, blocking: true }]\n }\n }\n\n const result = validate(parsed)\n const findings: GateFinding[] = []\n\n /*\n ํ˜•์‹ ์˜ค๋ฅ˜๋Š” **์ €์žฅ์„ ํ†ต๊ณผํ•  ์ˆ˜ ์—†๋‹ค**(`parseSource` ๊ฐ€ ๊ฑฐ์ ˆํ•œ๋‹ค). ๊ทธ๋ž˜๋„ ์—ฌ๊ธฐ์„œ ๋‹ค์‹œ ๋ณด๋Š” ์ด์œ ๋Š”,\n ์ด ํŒ์ •์ด ์ €์žฅ ๊ฒฝ๋กœ์™€ ๋…๋ฆฝ์ด์–ด์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค โ€” ์˜› ์ •๋ณธ์ด ๋‚จ์•„ ์žˆ๊ฑฐ๋‚˜ ํ˜•์‹์ด ์—„ํ•ด์ง„ ๋’ค๋ผ๋ฉด\n ์ €์žฅ ๋•Œ๋Š” ๋งž์•˜๋˜ ๊ฒƒ์ด ์ง€๊ธˆ์€ ํ‹€๋ฆด ์ˆ˜ ์žˆ๋‹ค. ๊ทธ๋•Œ ๋ฐœํ–‰์ด ํ†ต๊ณผํ•˜๋ฉด ์•ˆ ๋œ๋‹ค.\n */\n for (const error of result.errors) {\n findings.push({ code: error.code, message: error.message, at: error.path, blocking: true })\n }\n\n for (const violation of result.violations) {\n findings.push({ code: violation.code, message: violation.message, blocking: false })\n }\n\n if (result.errors.length === 0) {\n const report = sizingReport(compile(parsed as FigureSource))\n\n for (const finding of report.findings) {\n findings.push({\n code: finding.code,\n message: finding.message,\n /* ์™œยท์–ด๋–ป๊ฒŒ๋Š” ํ˜•์‹์ด ์†Œ์œ ํ•œ๋‹ค. ์„œ๋ฒ„๊ฐ€ ๋‹ค์‹œ ์“ฐ๋ฉด ๋‘ ๋ฒŒ์ด ๋˜๊ณ  ๊ณง ์–ด๊ธ‹๋‚œ๋‹ค. */\n why: finding.why,\n how: finding.how,\n /* ์ˆซ์ž๊ฐ€ ์•„๋‹ˆ๋ผ ์ž๋ฆฌ๋ฅผ ๋งํ•œ๋‹ค โ€” ์–ด๋А ๋ถ€ํ’ˆ๋“ค์ด ๊ฐˆ๋ผ์ ธ ๋‚˜์™”๋‚˜. */\n at: finding.clumps.map(clump => [...clump].sort().join('+')).join(' | ') || undefined,\n blocking: finding.code === 'sizing-splits'\n })\n }\n }\n\n return { findings, blocked: findings.some(finding => finding.blocking) }\n}\n"]}