@things-factory/figure-service 10.1.69 → 10.1.71

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 (871) hide show
  1. package/dist-server/index.js +6 -11
  2. package/dist-server/index.js.map +1 -1
  3. package/dist-server/routers/figure-thumbnail-router.js +22 -0
  4. package/dist-server/routers/figure-thumbnail-router.js.map +1 -1
  5. package/dist-server/service/figure-recipe/figure-recipe-resolver.d.ts +4 -4
  6. package/dist-server/service/figure-recipe/figure-recipe-resolver.js +36 -26
  7. package/dist-server/service/figure-recipe/figure-recipe-resolver.js.map +1 -1
  8. package/dist-server/service/figure-recipe/figure-recipe-templates.d.ts +28 -0
  9. package/dist-server/service/figure-recipe/figure-recipe-templates.js +57 -0
  10. package/dist-server/service/figure-recipe/figure-recipe-templates.js.map +1 -0
  11. package/dist-server/service/figure-recipe/figure-recipe-type.d.ts +11 -0
  12. package/dist-server/service/figure-recipe/figure-recipe-type.js +48 -0
  13. package/dist-server/service/figure-recipe/figure-recipe-type.js.map +1 -0
  14. package/dist-server/service/figure-recipe/index.d.ts +2 -4
  15. package/dist-server/service/figure-recipe/index.js +2 -4
  16. package/dist-server/service/figure-recipe/index.js.map +1 -1
  17. package/dist-server/service/index.d.ts +4 -4
  18. package/dist-server/service/index.js +7 -8
  19. package/dist-server/service/index.js.map +1 -1
  20. package/dist-server/tsconfig.tsbuildinfo +1 -1
  21. package/package.json +6 -4
  22. package/recipes/_lib/model.mts +71 -0
  23. package/recipes/air-cylinder/build.mts +14 -0
  24. package/recipes/air-cylinder/facts.md +3 -0
  25. package/recipes/air-cylinder/guide.en.md +18 -0
  26. package/recipes/air-cylinder/guide.ko.md +18 -0
  27. package/recipes/air-cylinder/models/default.json +1 -0
  28. package/recipes/air-cylinder/recipe.json +129 -0
  29. package/recipes/air-cylinder/spec.json +45 -0
  30. package/recipes/air-cylinder/thumbnail.png +0 -0
  31. package/recipes/air-shower/build.mts +14 -0
  32. package/recipes/air-shower/facts.md +3 -0
  33. package/recipes/air-shower/guide.en.md +17 -0
  34. package/recipes/air-shower/guide.ko.md +17 -0
  35. package/recipes/air-shower/models/default.json +1 -0
  36. package/recipes/air-shower/recipe.json +111 -0
  37. package/recipes/air-shower/spec.json +18 -0
  38. package/recipes/air-shower/thumbnail.png +0 -0
  39. package/recipes/barrier-gate/build.mts +11 -0
  40. package/recipes/barrier-gate/facts.md +3 -0
  41. package/recipes/barrier-gate/guide.en.md +18 -0
  42. package/recipes/barrier-gate/guide.ko.md +18 -0
  43. package/recipes/barrier-gate/models/default.json +1 -0
  44. package/recipes/barrier-gate/recipe.json +87 -0
  45. package/recipes/barrier-gate/spec.json +45 -0
  46. package/recipes/barrier-gate/thumbnail.png +0 -0
  47. package/recipes/base-frame/build.mts +11 -0
  48. package/recipes/base-frame/facts.md +3 -0
  49. package/recipes/base-frame/guide.en.md +18 -0
  50. package/recipes/base-frame/guide.ko.md +18 -0
  51. package/recipes/base-frame/models/default.json +1 -0
  52. package/recipes/base-frame/recipe.json +259 -0
  53. package/recipes/base-frame/spec.json +72 -0
  54. package/recipes/base-frame/thumbnail.png +0 -0
  55. package/recipes/belt-surface/build.mts +12 -0
  56. package/recipes/belt-surface/facts.md +3 -0
  57. package/recipes/belt-surface/guide.en.md +18 -0
  58. package/recipes/belt-surface/guide.ko.md +18 -0
  59. package/recipes/belt-surface/models/default.json +1 -0
  60. package/recipes/belt-surface/recipe.json +116 -0
  61. package/recipes/belt-surface/spec.json +69 -0
  62. package/recipes/belt-surface/thumbnail.png +0 -0
  63. package/recipes/bin-row/build.mts +11 -0
  64. package/recipes/bin-row/facts.md +3 -0
  65. package/recipes/bin-row/guide.en.md +18 -0
  66. package/recipes/bin-row/guide.ko.md +18 -0
  67. package/recipes/bin-row/models/default.json +1 -0
  68. package/recipes/bin-row/recipe.json +87 -0
  69. package/recipes/bin-row/spec.json +45 -0
  70. package/recipes/bin-row/thumbnail.png +0 -0
  71. package/recipes/bollard-row/build.mts +11 -0
  72. package/recipes/bollard-row/facts.md +3 -0
  73. package/recipes/bollard-row/guide.en.md +18 -0
  74. package/recipes/bollard-row/guide.ko.md +18 -0
  75. package/recipes/bollard-row/models/default.json +1 -0
  76. package/recipes/bollard-row/recipe.json +107 -0
  77. package/recipes/bollard-row/spec.json +71 -0
  78. package/recipes/bollard-row/thumbnail.png +0 -0
  79. package/recipes/buzzer/build.mts +10 -0
  80. package/recipes/buzzer/facts.md +3 -0
  81. package/recipes/buzzer/guide.en.md +17 -0
  82. package/recipes/buzzer/guide.ko.md +17 -0
  83. package/recipes/buzzer/models/default.json +1 -0
  84. package/recipes/buzzer/recipe.json +68 -0
  85. package/recipes/buzzer/spec.json +18 -0
  86. package/recipes/buzzer/thumbnail.png +0 -0
  87. package/recipes/cabinet-doors/build.mts +21 -0
  88. package/recipes/cabinet-doors/facts.md +3 -0
  89. package/recipes/cabinet-doors/guide.en.md +19 -0
  90. package/recipes/cabinet-doors/guide.ko.md +19 -0
  91. package/recipes/cabinet-doors/models/default.json +1 -0
  92. package/recipes/cabinet-doors/models/doors=1.json +1 -0
  93. package/recipes/cabinet-doors/models/doors=2.json +1 -0
  94. package/recipes/cabinet-doors/recipe.json +240 -0
  95. package/recipes/cabinet-doors/spec.json +36 -0
  96. package/recipes/cabinet-doors/thumbnail.png +0 -0
  97. package/recipes/cable-tray/build.mts +16 -0
  98. package/recipes/cable-tray/facts.md +3 -0
  99. package/recipes/cable-tray/guide.en.md +18 -0
  100. package/recipes/cable-tray/guide.ko.md +18 -0
  101. package/recipes/cable-tray/models/default.json +1 -0
  102. package/recipes/cable-tray/recipe.json +198 -0
  103. package/recipes/cable-tray/spec.json +45 -0
  104. package/recipes/cable-tray/thumbnail.png +0 -0
  105. package/recipes/camera-mount/build.mts +10 -0
  106. package/recipes/camera-mount/facts.md +3 -0
  107. package/recipes/camera-mount/guide.en.md +16 -0
  108. package/recipes/camera-mount/guide.ko.md +16 -0
  109. package/recipes/camera-mount/models/default.json +1 -0
  110. package/recipes/camera-mount/recipe.json +108 -0
  111. package/recipes/camera-mount/spec.json +18 -0
  112. package/recipes/camera-mount/thumbnail.png +0 -0
  113. package/recipes/canopy/build.mts +17 -0
  114. package/recipes/canopy/facts.md +3 -0
  115. package/recipes/canopy/guide.en.md +18 -0
  116. package/recipes/canopy/guide.ko.md +18 -0
  117. package/recipes/canopy/models/default.json +1 -0
  118. package/recipes/canopy/recipe.json +281 -0
  119. package/recipes/canopy/spec.json +81 -0
  120. package/recipes/canopy/thumbnail.png +0 -0
  121. package/recipes/cart/build.mts +24 -0
  122. package/recipes/cart/facts.md +3 -0
  123. package/recipes/cart/guide.en.md +16 -0
  124. package/recipes/cart/guide.ko.md +16 -0
  125. package/recipes/cart/models/default.json +1 -0
  126. package/recipes/cart/recipe.json +555 -0
  127. package/recipes/cart/spec.json +11 -0
  128. package/recipes/cart/thumbnail.png +0 -0
  129. package/recipes/cart-handle/build.mts +13 -0
  130. package/recipes/cart-handle/facts.md +3 -0
  131. package/recipes/cart-handle/guide.en.md +16 -0
  132. package/recipes/cart-handle/guide.ko.md +16 -0
  133. package/recipes/cart-handle/models/default.json +1 -0
  134. package/recipes/cart-handle/recipe.json +161 -0
  135. package/recipes/cart-handle/spec.json +18 -0
  136. package/recipes/cart-handle/thumbnail.png +0 -0
  137. package/recipes/casters-four/build.mts +16 -0
  138. package/recipes/casters-four/facts.md +3 -0
  139. package/recipes/casters-four/guide.en.md +19 -0
  140. package/recipes/casters-four/guide.ko.md +19 -0
  141. package/recipes/casters-four/models/default.json +1 -0
  142. package/recipes/casters-four/recipe.json +409 -0
  143. package/recipes/casters-four/spec.json +72 -0
  144. package/recipes/casters-four/thumbnail.png +0 -0
  145. package/recipes/ceiling-rail-carriage/build.mts +15 -0
  146. package/recipes/ceiling-rail-carriage/facts.md +3 -0
  147. package/recipes/ceiling-rail-carriage/guide.en.md +17 -0
  148. package/recipes/ceiling-rail-carriage/guide.ko.md +17 -0
  149. package/recipes/ceiling-rail-carriage/models/default.json +1 -0
  150. package/recipes/ceiling-rail-carriage/recipe.json +127 -0
  151. package/recipes/ceiling-rail-carriage/spec.json +19 -0
  152. package/recipes/ceiling-rail-carriage/thumbnail.png +0 -0
  153. package/recipes/chain-rails/build.mts +15 -0
  154. package/recipes/chain-rails/facts.md +3 -0
  155. package/recipes/chain-rails/guide.en.md +18 -0
  156. package/recipes/chain-rails/guide.ko.md +18 -0
  157. package/recipes/chain-rails/models/default.json +1 -0
  158. package/recipes/chain-rails/recipe.json +166 -0
  159. package/recipes/chain-rails/spec.json +54 -0
  160. package/recipes/chain-rails/thumbnail.png +0 -0
  161. package/recipes/charging-station/build.mts +9 -0
  162. package/recipes/charging-station/facts.md +3 -0
  163. package/recipes/charging-station/guide.en.md +16 -0
  164. package/recipes/charging-station/guide.ko.md +16 -0
  165. package/recipes/charging-station/models/default.json +1 -0
  166. package/recipes/charging-station/recipe.json +105 -0
  167. package/recipes/charging-station/spec.json +18 -0
  168. package/recipes/charging-station/thumbnail.png +0 -0
  169. package/recipes/column-row/build.mts +11 -0
  170. package/recipes/column-row/facts.md +3 -0
  171. package/recipes/column-row/guide.en.md +18 -0
  172. package/recipes/column-row/guide.ko.md +18 -0
  173. package/recipes/column-row/models/default.json +1 -0
  174. package/recipes/column-row/recipe.json +103 -0
  175. package/recipes/column-row/spec.json +71 -0
  176. package/recipes/column-row/thumbnail.png +0 -0
  177. package/recipes/control-cabinet/build.mts +14 -0
  178. package/recipes/control-cabinet/facts.md +3 -0
  179. package/recipes/control-cabinet/guide.en.md +17 -0
  180. package/recipes/control-cabinet/guide.ko.md +17 -0
  181. package/recipes/control-cabinet/models/default.json +1 -0
  182. package/recipes/control-cabinet/recipe.json +111 -0
  183. package/recipes/control-cabinet/spec.json +18 -0
  184. package/recipes/control-cabinet/thumbnail.png +0 -0
  185. package/recipes/conveyor-skirts/build.mts +9 -0
  186. package/recipes/conveyor-skirts/facts.md +3 -0
  187. package/recipes/conveyor-skirts/guide.en.md +16 -0
  188. package/recipes/conveyor-skirts/guide.ko.md +16 -0
  189. package/recipes/conveyor-skirts/models/default.json +1 -0
  190. package/recipes/conveyor-skirts/recipe.json +141 -0
  191. package/recipes/conveyor-skirts/spec.json +18 -0
  192. package/recipes/conveyor-skirts/thumbnail.png +0 -0
  193. package/recipes/counter-display/build.mts +13 -0
  194. package/recipes/counter-display/facts.md +3 -0
  195. package/recipes/counter-display/guide.en.md +17 -0
  196. package/recipes/counter-display/guide.ko.md +17 -0
  197. package/recipes/counter-display/models/default.json +1 -0
  198. package/recipes/counter-display/recipe.json +106 -0
  199. package/recipes/counter-display/spec.json +18 -0
  200. package/recipes/counter-display/thumbnail.png +0 -0
  201. package/recipes/dock-bumpers/build.mts +9 -0
  202. package/recipes/dock-bumpers/facts.md +3 -0
  203. package/recipes/dock-bumpers/guide.en.md +16 -0
  204. package/recipes/dock-bumpers/guide.ko.md +16 -0
  205. package/recipes/dock-bumpers/models/default.json +1 -0
  206. package/recipes/dock-bumpers/recipe.json +107 -0
  207. package/recipes/dock-bumpers/spec.json +18 -0
  208. package/recipes/dock-bumpers/thumbnail.png +0 -0
  209. package/recipes/dock-leveler/build.mts +11 -0
  210. package/recipes/dock-leveler/facts.md +3 -0
  211. package/recipes/dock-leveler/guide.en.md +16 -0
  212. package/recipes/dock-leveler/guide.ko.md +16 -0
  213. package/recipes/dock-leveler/models/default.json +1 -0
  214. package/recipes/dock-leveler/recipe.json +69 -0
  215. package/recipes/dock-leveler/spec.json +18 -0
  216. package/recipes/dock-leveler/thumbnail.png +0 -0
  217. package/recipes/double-hinged-door/build.mts +19 -0
  218. package/recipes/double-hinged-door/facts.md +3 -0
  219. package/recipes/double-hinged-door/guide.en.md +19 -0
  220. package/recipes/double-hinged-door/guide.ko.md +19 -0
  221. package/recipes/double-hinged-door/models/default.json +1 -0
  222. package/recipes/double-hinged-door/recipe.json +273 -0
  223. package/recipes/double-hinged-door/spec.json +69 -0
  224. package/recipes/double-hinged-door/thumbnail.png +0 -0
  225. package/recipes/drawer-stack/build.mts +17 -0
  226. package/recipes/drawer-stack/facts.md +3 -0
  227. package/recipes/drawer-stack/guide.en.md +18 -0
  228. package/recipes/drawer-stack/guide.ko.md +18 -0
  229. package/recipes/drawer-stack/models/default.json +1 -0
  230. package/recipes/drawer-stack/models/drawers=2.json +1 -0
  231. package/recipes/drawer-stack/models/drawers=3.json +1 -0
  232. package/recipes/drawer-stack/models/drawers=4.json +1 -0
  233. package/recipes/drawer-stack/recipe.json +789 -0
  234. package/recipes/drawer-stack/spec.json +37 -0
  235. package/recipes/drawer-stack/thumbnail.png +0 -0
  236. package/recipes/drip-tray/build.mts +12 -0
  237. package/recipes/drip-tray/facts.md +3 -0
  238. package/recipes/drip-tray/guide.en.md +16 -0
  239. package/recipes/drip-tray/guide.ko.md +16 -0
  240. package/recipes/drip-tray/models/default.json +1 -0
  241. package/recipes/drip-tray/recipe.json +86 -0
  242. package/recipes/drip-tray/spec.json +18 -0
  243. package/recipes/drip-tray/thumbnail.png +0 -0
  244. package/recipes/duct-fan/build.mts +14 -0
  245. package/recipes/duct-fan/facts.md +3 -0
  246. package/recipes/duct-fan/guide.en.md +16 -0
  247. package/recipes/duct-fan/guide.ko.md +16 -0
  248. package/recipes/duct-fan/models/default.json +1 -0
  249. package/recipes/duct-fan/recipe.json +122 -0
  250. package/recipes/duct-fan/spec.json +18 -0
  251. package/recipes/duct-fan/thumbnail.png +0 -0
  252. package/recipes/emergency-stop/build.mts +14 -0
  253. package/recipes/emergency-stop/facts.md +3 -0
  254. package/recipes/emergency-stop/guide.en.md +17 -0
  255. package/recipes/emergency-stop/guide.ko.md +17 -0
  256. package/recipes/emergency-stop/models/default.json +1 -0
  257. package/recipes/emergency-stop/recipe.json +109 -0
  258. package/recipes/emergency-stop/spec.json +18 -0
  259. package/recipes/emergency-stop/thumbnail.png +0 -0
  260. package/recipes/end-covers/build.mts +13 -0
  261. package/recipes/end-covers/facts.md +3 -0
  262. package/recipes/end-covers/guide.en.md +17 -0
  263. package/recipes/end-covers/guide.ko.md +17 -0
  264. package/recipes/end-covers/models/default.json +1 -0
  265. package/recipes/end-covers/recipe.json +141 -0
  266. package/recipes/end-covers/spec.json +18 -0
  267. package/recipes/end-covers/thumbnail.png +0 -0
  268. package/recipes/ffu-row/build.mts +11 -0
  269. package/recipes/ffu-row/facts.md +3 -0
  270. package/recipes/ffu-row/guide.en.md +18 -0
  271. package/recipes/ffu-row/guide.ko.md +18 -0
  272. package/recipes/ffu-row/models/default.json +1 -0
  273. package/recipes/ffu-row/recipe.json +82 -0
  274. package/recipes/ffu-row/spec.json +45 -0
  275. package/recipes/ffu-row/thumbnail.png +0 -0
  276. package/recipes/fire-extinguisher/build.mts +13 -0
  277. package/recipes/fire-extinguisher/facts.md +3 -0
  278. package/recipes/fire-extinguisher/guide.en.md +16 -0
  279. package/recipes/fire-extinguisher/guide.ko.md +16 -0
  280. package/recipes/fire-extinguisher/models/default.json +1 -0
  281. package/recipes/fire-extinguisher/recipe.json +114 -0
  282. package/recipes/fire-extinguisher/spec.json +18 -0
  283. package/recipes/fire-extinguisher/thumbnail.png +0 -0
  284. package/recipes/first-aid-box/build.mts +9 -0
  285. package/recipes/first-aid-box/facts.md +3 -0
  286. package/recipes/first-aid-box/guide.en.md +16 -0
  287. package/recipes/first-aid-box/guide.ko.md +16 -0
  288. package/recipes/first-aid-box/models/default.json +1 -0
  289. package/recipes/first-aid-box/recipe.json +97 -0
  290. package/recipes/first-aid-box/spec.json +18 -0
  291. package/recipes/first-aid-box/thumbnail.png +0 -0
  292. package/recipes/floor-zone/build.mts +17 -0
  293. package/recipes/floor-zone/facts.md +3 -0
  294. package/recipes/floor-zone/guide.en.md +18 -0
  295. package/recipes/floor-zone/guide.ko.md +18 -0
  296. package/recipes/floor-zone/models/default.json +1 -0
  297. package/recipes/floor-zone/recipe.json +257 -0
  298. package/recipes/floor-zone/spec.json +72 -0
  299. package/recipes/floor-zone/thumbnail.png +0 -0
  300. package/recipes/folding-door/build.mts +20 -0
  301. package/recipes/folding-door/facts.md +3 -0
  302. package/recipes/folding-door/guide.en.md +18 -0
  303. package/recipes/folding-door/guide.ko.md +18 -0
  304. package/recipes/folding-door/models/default.json +1 -0
  305. package/recipes/folding-door/recipe.json +217 -0
  306. package/recipes/folding-door/spec.json +44 -0
  307. package/recipes/folding-door/thumbnail.png +0 -0
  308. package/recipes/fork/build.mts +13 -0
  309. package/recipes/fork/facts.md +3 -0
  310. package/recipes/fork/guide.en.md +18 -0
  311. package/recipes/fork/guide.ko.md +18 -0
  312. package/recipes/fork/models/default.json +1 -0
  313. package/recipes/fork/recipe.json +136 -0
  314. package/recipes/fork/spec.json +45 -0
  315. package/recipes/fork/thumbnail.png +0 -0
  316. package/recipes/gantry-frame/build.mts +13 -0
  317. package/recipes/gantry-frame/facts.md +3 -0
  318. package/recipes/gantry-frame/guide.en.md +18 -0
  319. package/recipes/gantry-frame/guide.ko.md +18 -0
  320. package/recipes/gantry-frame/models/default.json +1 -0
  321. package/recipes/gantry-frame/recipe.json +178 -0
  322. package/recipes/gantry-frame/spec.json +63 -0
  323. package/recipes/gantry-frame/thumbnail.png +0 -0
  324. package/recipes/gripper-2f/build.mts +16 -0
  325. package/recipes/gripper-2f/facts.md +3 -0
  326. package/recipes/gripper-2f/guide.en.md +18 -0
  327. package/recipes/gripper-2f/guide.ko.md +18 -0
  328. package/recipes/gripper-2f/models/default.json +1 -0
  329. package/recipes/gripper-2f/recipe.json +172 -0
  330. package/recipes/gripper-2f/spec.json +45 -0
  331. package/recipes/gripper-2f/thumbnail.png +0 -0
  332. package/recipes/guard-cover/build.mts +13 -0
  333. package/recipes/guard-cover/facts.md +3 -0
  334. package/recipes/guard-cover/guide.en.md +18 -0
  335. package/recipes/guard-cover/guide.ko.md +18 -0
  336. package/recipes/guard-cover/models/default.json +1 -0
  337. package/recipes/guard-cover/recipe.json +92 -0
  338. package/recipes/guard-cover/spec.json +18 -0
  339. package/recipes/guard-cover/thumbnail.png +0 -0
  340. package/recipes/handrail/build.mts +15 -0
  341. package/recipes/handrail/facts.md +3 -0
  342. package/recipes/handrail/guide.en.md +18 -0
  343. package/recipes/handrail/guide.ko.md +18 -0
  344. package/recipes/handrail/models/default.json +1 -0
  345. package/recipes/handrail/recipe.json +140 -0
  346. package/recipes/handrail/spec.json +45 -0
  347. package/recipes/handrail/thumbnail.png +0 -0
  348. package/recipes/hatch/build.mts +17 -0
  349. package/recipes/hatch/facts.md +3 -0
  350. package/recipes/hatch/guide.en.md +18 -0
  351. package/recipes/hatch/guide.ko.md +18 -0
  352. package/recipes/hatch/models/default.json +1 -0
  353. package/recipes/hatch/recipe.json +194 -0
  354. package/recipes/hatch/spec.json +69 -0
  355. package/recipes/hatch/thumbnail.png +0 -0
  356. package/recipes/hinged-door/build.mts +26 -0
  357. package/recipes/hinged-door/facts.md +21 -0
  358. package/recipes/hinged-door/guide.en.md +19 -0
  359. package/recipes/hinged-door/guide.ko.md +19 -0
  360. package/recipes/hinged-door/models/default.json +1 -0
  361. package/recipes/hinged-door/models/hinge=left.json +1 -0
  362. package/recipes/hinged-door/models/hinge=right.json +1 -0
  363. package/recipes/hinged-door/recipe.json +252 -0
  364. package/recipes/hinged-door/spec.json +23 -0
  365. package/recipes/hinged-door/thumbnail.png +0 -0
  366. package/recipes/hmi-monitor/build.mts +14 -0
  367. package/recipes/hmi-monitor/facts.md +3 -0
  368. package/recipes/hmi-monitor/guide.en.md +17 -0
  369. package/recipes/hmi-monitor/guide.ko.md +17 -0
  370. package/recipes/hmi-monitor/models/default.json +1 -0
  371. package/recipes/hmi-monitor/recipe.json +108 -0
  372. package/recipes/hmi-monitor/spec.json +18 -0
  373. package/recipes/hmi-monitor/thumbnail.png +0 -0
  374. package/recipes/hopper/build.mts +14 -0
  375. package/recipes/hopper/facts.md +3 -0
  376. package/recipes/hopper/guide.en.md +18 -0
  377. package/recipes/hopper/guide.ko.md +18 -0
  378. package/recipes/hopper/models/default.json +1 -0
  379. package/recipes/hopper/recipe.json +137 -0
  380. package/recipes/hopper/spec.json +22 -0
  381. package/recipes/hopper/thumbnail.png +0 -0
  382. package/recipes/interlock-switch/build.mts +8 -0
  383. package/recipes/interlock-switch/facts.md +3 -0
  384. package/recipes/interlock-switch/guide.en.md +17 -0
  385. package/recipes/interlock-switch/guide.ko.md +17 -0
  386. package/recipes/interlock-switch/models/default.json +1 -0
  387. package/recipes/interlock-switch/recipe.json +63 -0
  388. package/recipes/interlock-switch/spec.json +18 -0
  389. package/recipes/interlock-switch/thumbnail.png +0 -0
  390. package/recipes/junction-box/build.mts +8 -0
  391. package/recipes/junction-box/facts.md +3 -0
  392. package/recipes/junction-box/guide.en.md +16 -0
  393. package/recipes/junction-box/guide.ko.md +16 -0
  394. package/recipes/junction-box/models/default.json +1 -0
  395. package/recipes/junction-box/recipe.json +64 -0
  396. package/recipes/junction-box/spec.json +18 -0
  397. package/recipes/junction-box/thumbnail.png +0 -0
  398. package/recipes/ladder/build.mts +13 -0
  399. package/recipes/ladder/facts.md +3 -0
  400. package/recipes/ladder/guide.en.md +18 -0
  401. package/recipes/ladder/guide.ko.md +18 -0
  402. package/recipes/ladder/models/default.json +1 -0
  403. package/recipes/ladder/recipe.json +191 -0
  404. package/recipes/ladder/spec.json +44 -0
  405. package/recipes/ladder/thumbnail.png +0 -0
  406. package/recipes/legs-four/build.mts +12 -0
  407. package/recipes/legs-four/facts.md +3 -0
  408. package/recipes/legs-four/guide.en.md +18 -0
  409. package/recipes/legs-four/guide.ko.md +18 -0
  410. package/recipes/legs-four/models/default.json +1 -0
  411. package/recipes/legs-four/recipe.json +217 -0
  412. package/recipes/legs-four/spec.json +72 -0
  413. package/recipes/legs-four/thumbnail.png +0 -0
  414. package/recipes/leveling-feet/build.mts +11 -0
  415. package/recipes/leveling-feet/facts.md +3 -0
  416. package/recipes/leveling-feet/guide.en.md +18 -0
  417. package/recipes/leveling-feet/guide.ko.md +18 -0
  418. package/recipes/leveling-feet/models/default.json +1 -0
  419. package/recipes/leveling-feet/recipe.json +233 -0
  420. package/recipes/leveling-feet/spec.json +72 -0
  421. package/recipes/leveling-feet/thumbnail.png +0 -0
  422. package/recipes/lift-updown/build.mts +13 -0
  423. package/recipes/lift-updown/facts.md +3 -0
  424. package/recipes/lift-updown/guide.en.md +18 -0
  425. package/recipes/lift-updown/guide.ko.md +18 -0
  426. package/recipes/lift-updown/models/default.json +1 -0
  427. package/recipes/lift-updown/recipe.json +107 -0
  428. package/recipes/lift-updown/spec.json +45 -0
  429. package/recipes/lift-updown/thumbnail.png +0 -0
  430. package/recipes/light-curtain/build.mts +11 -0
  431. package/recipes/light-curtain/facts.md +3 -0
  432. package/recipes/light-curtain/guide.en.md +18 -0
  433. package/recipes/light-curtain/guide.ko.md +18 -0
  434. package/recipes/light-curtain/models/default.json +1 -0
  435. package/recipes/light-curtain/recipe.json +127 -0
  436. package/recipes/light-curtain/spec.json +54 -0
  437. package/recipes/light-curtain/thumbnail.png +0 -0
  438. package/recipes/limit-switch/build.mts +14 -0
  439. package/recipes/limit-switch/facts.md +3 -0
  440. package/recipes/limit-switch/guide.en.md +17 -0
  441. package/recipes/limit-switch/guide.ko.md +17 -0
  442. package/recipes/limit-switch/models/default.json +1 -0
  443. package/recipes/limit-switch/recipe.json +107 -0
  444. package/recipes/limit-switch/spec.json +17 -0
  445. package/recipes/limit-switch/thumbnail.png +0 -0
  446. package/recipes/linear-guide/build.mts +15 -0
  447. package/recipes/linear-guide/facts.md +3 -0
  448. package/recipes/linear-guide/guide.en.md +18 -0
  449. package/recipes/linear-guide/guide.ko.md +18 -0
  450. package/recipes/linear-guide/models/default.json +1 -0
  451. package/recipes/linear-guide/recipe.json +145 -0
  452. package/recipes/linear-guide/spec.json +44 -0
  453. package/recipes/linear-guide/thumbnail.png +0 -0
  454. package/recipes/locker-row/build.mts +11 -0
  455. package/recipes/locker-row/facts.md +3 -0
  456. package/recipes/locker-row/guide.en.md +18 -0
  457. package/recipes/locker-row/guide.ko.md +18 -0
  458. package/recipes/locker-row/models/default.json +1 -0
  459. package/recipes/locker-row/recipe.json +82 -0
  460. package/recipes/locker-row/spec.json +45 -0
  461. package/recipes/locker-row/thumbnail.png +0 -0
  462. package/recipes/motor-box/build.mts +10 -0
  463. package/recipes/motor-box/facts.md +3 -0
  464. package/recipes/motor-box/guide.en.md +17 -0
  465. package/recipes/motor-box/guide.ko.md +17 -0
  466. package/recipes/motor-box/models/default.json +1 -0
  467. package/recipes/motor-box/recipe.json +64 -0
  468. package/recipes/motor-box/spec.json +18 -0
  469. package/recipes/motor-box/thumbnail.png +0 -0
  470. package/recipes/observation-window/build.mts +16 -0
  471. package/recipes/observation-window/facts.md +3 -0
  472. package/recipes/observation-window/guide.en.md +19 -0
  473. package/recipes/observation-window/guide.ko.md +19 -0
  474. package/recipes/observation-window/models/default.json +1 -0
  475. package/recipes/observation-window/recipe.json +185 -0
  476. package/recipes/observation-window/spec.json +68 -0
  477. package/recipes/observation-window/thumbnail.png +0 -0
  478. package/recipes/operator-panel/build.mts +14 -0
  479. package/recipes/operator-panel/facts.md +3 -0
  480. package/recipes/operator-panel/guide.en.md +18 -0
  481. package/recipes/operator-panel/guide.ko.md +18 -0
  482. package/recipes/operator-panel/models/buttons=2.json +1 -0
  483. package/recipes/operator-panel/models/buttons=3.json +1 -0
  484. package/recipes/operator-panel/models/buttons=4.json +1 -0
  485. package/recipes/operator-panel/models/buttons=5.json +1 -0
  486. package/recipes/operator-panel/models/default.json +1 -0
  487. package/recipes/operator-panel/recipe.json +210 -0
  488. package/recipes/operator-panel/spec.json +38 -0
  489. package/recipes/operator-panel/thumbnail.png +0 -0
  490. package/recipes/pallet/build.mts +12 -0
  491. package/recipes/pallet/facts.md +3 -0
  492. package/recipes/pallet/guide.en.md +17 -0
  493. package/recipes/pallet/guide.ko.md +17 -0
  494. package/recipes/pallet/models/default.json +1 -0
  495. package/recipes/pallet/recipe.json +181 -0
  496. package/recipes/pallet/spec.json +18 -0
  497. package/recipes/pallet/thumbnail.png +0 -0
  498. package/recipes/partition-wall/build.mts +16 -0
  499. package/recipes/partition-wall/facts.md +3 -0
  500. package/recipes/partition-wall/guide.en.md +18 -0
  501. package/recipes/partition-wall/guide.ko.md +18 -0
  502. package/recipes/partition-wall/models/default.json +1 -0
  503. package/recipes/partition-wall/recipe.json +203 -0
  504. package/recipes/partition-wall/spec.json +54 -0
  505. package/recipes/partition-wall/thumbnail.png +0 -0
  506. package/recipes/pass-box/build.mts +14 -0
  507. package/recipes/pass-box/facts.md +3 -0
  508. package/recipes/pass-box/guide.en.md +17 -0
  509. package/recipes/pass-box/guide.ko.md +17 -0
  510. package/recipes/pass-box/models/default.json +1 -0
  511. package/recipes/pass-box/recipe.json +107 -0
  512. package/recipes/pass-box/spec.json +18 -0
  513. package/recipes/pass-box/thumbnail.png +0 -0
  514. package/recipes/peg-board/build.mts +14 -0
  515. package/recipes/peg-board/facts.md +3 -0
  516. package/recipes/peg-board/guide.en.md +17 -0
  517. package/recipes/peg-board/guide.ko.md +17 -0
  518. package/recipes/peg-board/models/default.json +1 -0
  519. package/recipes/peg-board/recipe.json +120 -0
  520. package/recipes/peg-board/spec.json +45 -0
  521. package/recipes/peg-board/thumbnail.png +0 -0
  522. package/recipes/photo-sensor-pair/build.mts +11 -0
  523. package/recipes/photo-sensor-pair/facts.md +3 -0
  524. package/recipes/photo-sensor-pair/guide.en.md +18 -0
  525. package/recipes/photo-sensor-pair/guide.ko.md +18 -0
  526. package/recipes/photo-sensor-pair/models/default.json +1 -0
  527. package/recipes/photo-sensor-pair/recipe.json +145 -0
  528. package/recipes/photo-sensor-pair/spec.json +54 -0
  529. package/recipes/photo-sensor-pair/thumbnail.png +0 -0
  530. package/recipes/pipe-run/build.mts +11 -0
  531. package/recipes/pipe-run/facts.md +3 -0
  532. package/recipes/pipe-run/guide.en.md +18 -0
  533. package/recipes/pipe-run/guide.ko.md +18 -0
  534. package/recipes/pipe-run/models/default.json +1 -0
  535. package/recipes/pipe-run/recipe.json +100 -0
  536. package/recipes/pipe-run/spec.json +44 -0
  537. package/recipes/pipe-run/thumbnail.png +0 -0
  538. package/recipes/platform/build.mts +14 -0
  539. package/recipes/platform/facts.md +3 -0
  540. package/recipes/platform/guide.en.md +18 -0
  541. package/recipes/platform/guide.ko.md +18 -0
  542. package/recipes/platform/models/default.json +1 -0
  543. package/recipes/platform/recipe.json +281 -0
  544. package/recipes/platform/spec.json +81 -0
  545. package/recipes/platform/thumbnail.png +0 -0
  546. package/recipes/power-box/build.mts +10 -0
  547. package/recipes/power-box/facts.md +3 -0
  548. package/recipes/power-box/guide.en.md +17 -0
  549. package/recipes/power-box/guide.ko.md +17 -0
  550. package/recipes/power-box/models/default.json +1 -0
  551. package/recipes/power-box/recipe.json +64 -0
  552. package/recipes/power-box/spec.json +18 -0
  553. package/recipes/power-box/thumbnail.png +0 -0
  554. package/recipes/pressure-gauge/build.mts +10 -0
  555. package/recipes/pressure-gauge/facts.md +3 -0
  556. package/recipes/pressure-gauge/guide.en.md +17 -0
  557. package/recipes/pressure-gauge/guide.ko.md +17 -0
  558. package/recipes/pressure-gauge/models/default.json +1 -0
  559. package/recipes/pressure-gauge/recipe.json +65 -0
  560. package/recipes/pressure-gauge/spec.json +18 -0
  561. package/recipes/pressure-gauge/thumbnail.png +0 -0
  562. package/recipes/pull-cord/build.mts +11 -0
  563. package/recipes/pull-cord/facts.md +3 -0
  564. package/recipes/pull-cord/guide.en.md +17 -0
  565. package/recipes/pull-cord/guide.ko.md +17 -0
  566. package/recipes/pull-cord/models/default.json +1 -0
  567. package/recipes/pull-cord/recipe.json +75 -0
  568. package/recipes/pull-cord/spec.json +18 -0
  569. package/recipes/pull-cord/thumbnail.png +0 -0
  570. package/recipes/pump/build.mts +13 -0
  571. package/recipes/pump/facts.md +3 -0
  572. package/recipes/pump/guide.en.md +17 -0
  573. package/recipes/pump/guide.ko.md +17 -0
  574. package/recipes/pump/models/default.json +1 -0
  575. package/recipes/pump/recipe.json +109 -0
  576. package/recipes/pump/spec.json +18 -0
  577. package/recipes/pump/thumbnail.png +0 -0
  578. package/recipes/pusher/build.mts +11 -0
  579. package/recipes/pusher/facts.md +3 -0
  580. package/recipes/pusher/guide.en.md +18 -0
  581. package/recipes/pusher/guide.ko.md +18 -0
  582. package/recipes/pusher/models/default.json +1 -0
  583. package/recipes/pusher/recipe.json +105 -0
  584. package/recipes/pusher/spec.json +45 -0
  585. package/recipes/pusher/thumbnail.png +0 -0
  586. package/recipes/robot-plinth/build.mts +9 -0
  587. package/recipes/robot-plinth/facts.md +3 -0
  588. package/recipes/robot-plinth/guide.en.md +17 -0
  589. package/recipes/robot-plinth/guide.ko.md +17 -0
  590. package/recipes/robot-plinth/models/default.json +1 -0
  591. package/recipes/robot-plinth/recipe.json +115 -0
  592. package/recipes/robot-plinth/spec.json +45 -0
  593. package/recipes/robot-plinth/thumbnail.png +0 -0
  594. package/recipes/roll-up-shutter/build.mts +17 -0
  595. package/recipes/roll-up-shutter/facts.md +3 -0
  596. package/recipes/roll-up-shutter/guide.en.md +18 -0
  597. package/recipes/roll-up-shutter/guide.ko.md +18 -0
  598. package/recipes/roll-up-shutter/models/default.json +1 -0
  599. package/recipes/roll-up-shutter/recipe.json +200 -0
  600. package/recipes/roll-up-shutter/spec.json +69 -0
  601. package/recipes/roll-up-shutter/thumbnail.png +0 -0
  602. package/recipes/roller-row/build.mts +18 -0
  603. package/recipes/roller-row/facts.md +20 -0
  604. package/recipes/roller-row/guide.en.md +21 -0
  605. package/recipes/roller-row/guide.ko.md +21 -0
  606. package/recipes/roller-row/models/default.json +1 -0
  607. package/recipes/roller-row/recipe.json +179 -0
  608. package/recipes/roller-row/spec.json +80 -0
  609. package/recipes/roller-row/thumbnail.png +0 -0
  610. package/recipes/room-shell/build.mts +16 -0
  611. package/recipes/room-shell/facts.md +3 -0
  612. package/recipes/room-shell/guide.en.md +18 -0
  613. package/recipes/room-shell/guide.ko.md +18 -0
  614. package/recipes/room-shell/models/default.json +1 -0
  615. package/recipes/room-shell/recipe.json +324 -0
  616. package/recipes/room-shell/spec.json +81 -0
  617. package/recipes/room-shell/thumbnail.png +0 -0
  618. package/recipes/rotating-beacon/build.mts +17 -0
  619. package/recipes/rotating-beacon/facts.md +3 -0
  620. package/recipes/rotating-beacon/guide.en.md +17 -0
  621. package/recipes/rotating-beacon/guide.ko.md +17 -0
  622. package/recipes/rotating-beacon/models/default.json +1 -0
  623. package/recipes/rotating-beacon/recipe.json +160 -0
  624. package/recipes/rotating-beacon/spec.json +18 -0
  625. package/recipes/rotating-beacon/thumbnail.png +0 -0
  626. package/recipes/safety-fence/build.mts +15 -0
  627. package/recipes/safety-fence/facts.md +3 -0
  628. package/recipes/safety-fence/guide.en.md +19 -0
  629. package/recipes/safety-fence/guide.ko.md +19 -0
  630. package/recipes/safety-fence/models/default.json +1 -0
  631. package/recipes/safety-fence/recipe.json +161 -0
  632. package/recipes/safety-fence/spec.json +80 -0
  633. package/recipes/safety-fence/thumbnail.png +0 -0
  634. package/recipes/safety-mat/build.mts +11 -0
  635. package/recipes/safety-mat/facts.md +3 -0
  636. package/recipes/safety-mat/guide.en.md +18 -0
  637. package/recipes/safety-mat/guide.ko.md +18 -0
  638. package/recipes/safety-mat/models/default.json +1 -0
  639. package/recipes/safety-mat/recipe.json +93 -0
  640. package/recipes/safety-mat/spec.json +44 -0
  641. package/recipes/safety-mat/thumbnail.png +0 -0
  642. package/recipes/safety-sign-stand/build.mts +16 -0
  643. package/recipes/safety-sign-stand/facts.md +3 -0
  644. package/recipes/safety-sign-stand/guide.en.md +16 -0
  645. package/recipes/safety-sign-stand/guide.ko.md +16 -0
  646. package/recipes/safety-sign-stand/models/default.json +1 -0
  647. package/recipes/safety-sign-stand/recipe.json +144 -0
  648. package/recipes/safety-sign-stand/spec.json +11 -0
  649. package/recipes/safety-sign-stand/thumbnail.png +0 -0
  650. package/recipes/scanner-mount/build.mts +10 -0
  651. package/recipes/scanner-mount/facts.md +3 -0
  652. package/recipes/scanner-mount/guide.en.md +16 -0
  653. package/recipes/scanner-mount/guide.ko.md +16 -0
  654. package/recipes/scanner-mount/models/default.json +1 -0
  655. package/recipes/scanner-mount/recipe.json +147 -0
  656. package/recipes/scanner-mount/spec.json +18 -0
  657. package/recipes/scanner-mount/thumbnail.png +0 -0
  658. package/recipes/selector-switch/build.mts +14 -0
  659. package/recipes/selector-switch/facts.md +3 -0
  660. package/recipes/selector-switch/guide.en.md +17 -0
  661. package/recipes/selector-switch/guide.ko.md +17 -0
  662. package/recipes/selector-switch/models/default.json +1 -0
  663. package/recipes/selector-switch/recipe.json +104 -0
  664. package/recipes/selector-switch/spec.json +11 -0
  665. package/recipes/selector-switch/thumbnail.png +0 -0
  666. package/recipes/shelf-tiers/build.mts +12 -0
  667. package/recipes/shelf-tiers/facts.md +3 -0
  668. package/recipes/shelf-tiers/guide.en.md +18 -0
  669. package/recipes/shelf-tiers/guide.ko.md +18 -0
  670. package/recipes/shelf-tiers/models/default.json +1 -0
  671. package/recipes/shelf-tiers/models/tiers=2.json +1 -0
  672. package/recipes/shelf-tiers/models/tiers=3.json +1 -0
  673. package/recipes/shelf-tiers/models/tiers=4.json +1 -0
  674. package/recipes/shelf-tiers/models/tiers=5.json +1 -0
  675. package/recipes/shelf-tiers/recipe.json +196 -0
  676. package/recipes/shelf-tiers/spec.json +64 -0
  677. package/recipes/shelf-tiers/thumbnail.png +0 -0
  678. package/recipes/side-guides/build.mts +12 -0
  679. package/recipes/side-guides/facts.md +3 -0
  680. package/recipes/side-guides/guide.en.md +18 -0
  681. package/recipes/side-guides/guide.ko.md +18 -0
  682. package/recipes/side-guides/models/default.json +1 -0
  683. package/recipes/side-guides/recipe.json +147 -0
  684. package/recipes/side-guides/spec.json +54 -0
  685. package/recipes/side-guides/thumbnail.png +0 -0
  686. package/recipes/sign-board/build.mts +11 -0
  687. package/recipes/sign-board/facts.md +3 -0
  688. package/recipes/sign-board/guide.en.md +18 -0
  689. package/recipes/sign-board/guide.ko.md +18 -0
  690. package/recipes/sign-board/models/default.json +1 -0
  691. package/recipes/sign-board/recipe.json +93 -0
  692. package/recipes/sign-board/spec.json +44 -0
  693. package/recipes/sign-board/thumbnail.png +0 -0
  694. package/recipes/signal-tower-3/build.mts +19 -0
  695. package/recipes/signal-tower-3/facts.md +3 -0
  696. package/recipes/signal-tower-3/guide.en.md +17 -0
  697. package/recipes/signal-tower-3/guide.ko.md +17 -0
  698. package/recipes/signal-tower-3/models/default.json +1 -0
  699. package/recipes/signal-tower-3/recipe.json +216 -0
  700. package/recipes/signal-tower-3/spec.json +19 -0
  701. package/recipes/signal-tower-3/thumbnail.png +0 -0
  702. package/recipes/sliding-door/build.mts +47 -0
  703. package/recipes/sliding-door/facts.md +26 -0
  704. package/recipes/sliding-door/guide.en.md +22 -0
  705. package/recipes/sliding-door/guide.ko.md +22 -0
  706. package/recipes/sliding-door/models/default.json +1 -0
  707. package/recipes/sliding-door/models/leaves=1,lamp=false.json +1 -0
  708. package/recipes/sliding-door/models/leaves=1,lamp=true.json +1 -0
  709. package/recipes/sliding-door/models/leaves=2,lamp=false.json +1 -0
  710. package/recipes/sliding-door/models/leaves=2,lamp=true.json +1 -0
  711. package/recipes/sliding-door/recipe.json +506 -0
  712. package/recipes/sliding-door/spec.json +26 -0
  713. package/recipes/sliding-door/thumbnail.png +0 -0
  714. package/recipes/stairs/build.mts +11 -0
  715. package/recipes/stairs/facts.md +3 -0
  716. package/recipes/stairs/guide.en.md +19 -0
  717. package/recipes/stairs/guide.ko.md +19 -0
  718. package/recipes/stairs/models/default.json +1 -0
  719. package/recipes/stairs/recipe.json +110 -0
  720. package/recipes/stairs/spec.json +94 -0
  721. package/recipes/stairs/thumbnail.png +0 -0
  722. package/recipes/status-lamp/build.mts +17 -0
  723. package/recipes/status-lamp/facts.md +3 -0
  724. package/recipes/status-lamp/guide.en.md +17 -0
  725. package/recipes/status-lamp/guide.ko.md +17 -0
  726. package/recipes/status-lamp/models/default.json +1 -0
  727. package/recipes/status-lamp/recipe.json +172 -0
  728. package/recipes/status-lamp/spec.json +18 -0
  729. package/recipes/status-lamp/thumbnail.png +0 -0
  730. package/recipes/stopper/build.mts +12 -0
  731. package/recipes/stopper/facts.md +3 -0
  732. package/recipes/stopper/guide.en.md +18 -0
  733. package/recipes/stopper/guide.ko.md +18 -0
  734. package/recipes/stopper/models/default.json +1 -0
  735. package/recipes/stopper/recipe.json +108 -0
  736. package/recipes/stopper/spec.json +45 -0
  737. package/recipes/stopper/thumbnail.png +0 -0
  738. package/recipes/suction-pads/build.mts +11 -0
  739. package/recipes/suction-pads/facts.md +3 -0
  740. package/recipes/suction-pads/guide.en.md +18 -0
  741. package/recipes/suction-pads/guide.ko.md +18 -0
  742. package/recipes/suction-pads/models/default.json +1 -0
  743. package/recipes/suction-pads/recipe.json +86 -0
  744. package/recipes/suction-pads/spec.json +45 -0
  745. package/recipes/suction-pads/thumbnail.png +0 -0
  746. package/recipes/tank/build.mts +12 -0
  747. package/recipes/tank/facts.md +3 -0
  748. package/recipes/tank/guide.en.md +18 -0
  749. package/recipes/tank/guide.ko.md +18 -0
  750. package/recipes/tank/models/default.json +1 -0
  751. package/recipes/tank/recipe.json +105 -0
  752. package/recipes/tank/spec.json +44 -0
  753. package/recipes/tank/thumbnail.png +0 -0
  754. package/recipes/telescopic-arm/build.mts +15 -0
  755. package/recipes/telescopic-arm/facts.md +3 -0
  756. package/recipes/telescopic-arm/guide.en.md +17 -0
  757. package/recipes/telescopic-arm/guide.ko.md +17 -0
  758. package/recipes/telescopic-arm/models/default.json +1 -0
  759. package/recipes/telescopic-arm/recipe.json +108 -0
  760. package/recipes/telescopic-arm/spec.json +18 -0
  761. package/recipes/telescopic-arm/thumbnail.png +0 -0
  762. package/recipes/toe-board/build.mts +11 -0
  763. package/recipes/toe-board/facts.md +3 -0
  764. package/recipes/toe-board/guide.en.md +18 -0
  765. package/recipes/toe-board/guide.ko.md +18 -0
  766. package/recipes/toe-board/models/default.json +1 -0
  767. package/recipes/toe-board/recipe.json +92 -0
  768. package/recipes/toe-board/spec.json +45 -0
  769. package/recipes/toe-board/thumbnail.png +0 -0
  770. package/recipes/tote/build.mts +10 -0
  771. package/recipes/tote/facts.md +3 -0
  772. package/recipes/tote/guide.en.md +18 -0
  773. package/recipes/tote/guide.ko.md +18 -0
  774. package/recipes/tote/models/default.json +1 -0
  775. package/recipes/tote/recipe.json +82 -0
  776. package/recipes/tote/spec.json +45 -0
  777. package/recipes/tote/thumbnail.png +0 -0
  778. package/recipes/traffic-cones/build.mts +15 -0
  779. package/recipes/traffic-cones/facts.md +3 -0
  780. package/recipes/traffic-cones/guide.en.md +18 -0
  781. package/recipes/traffic-cones/guide.ko.md +18 -0
  782. package/recipes/traffic-cones/models/default.json +1 -0
  783. package/recipes/traffic-cones/recipe.json +130 -0
  784. package/recipes/traffic-cones/spec.json +22 -0
  785. package/recipes/traffic-cones/thumbnail.png +0 -0
  786. package/recipes/transfer-belts/build.mts +14 -0
  787. package/recipes/transfer-belts/facts.md +3 -0
  788. package/recipes/transfer-belts/guide.en.md +18 -0
  789. package/recipes/transfer-belts/guide.ko.md +18 -0
  790. package/recipes/transfer-belts/models/default.json +1 -0
  791. package/recipes/transfer-belts/recipe.json +174 -0
  792. package/recipes/transfer-belts/spec.json +45 -0
  793. package/recipes/transfer-belts/thumbnail.png +0 -0
  794. package/recipes/tray-stack/build.mts +13 -0
  795. package/recipes/tray-stack/facts.md +3 -0
  796. package/recipes/tray-stack/guide.en.md +18 -0
  797. package/recipes/tray-stack/guide.ko.md +18 -0
  798. package/recipes/tray-stack/models/default.json +1 -0
  799. package/recipes/tray-stack/models/trays=3.json +1 -0
  800. package/recipes/tray-stack/models/trays=4.json +1 -0
  801. package/recipes/tray-stack/models/trays=5.json +1 -0
  802. package/recipes/tray-stack/recipe.json +289 -0
  803. package/recipes/tray-stack/spec.json +20 -0
  804. package/recipes/tray-stack/thumbnail.png +0 -0
  805. package/recipes/trough/build.mts +11 -0
  806. package/recipes/trough/facts.md +3 -0
  807. package/recipes/trough/guide.en.md +17 -0
  808. package/recipes/trough/guide.ko.md +17 -0
  809. package/recipes/trough/models/default.json +1 -0
  810. package/recipes/trough/recipe.json +75 -0
  811. package/recipes/trough/spec.json +18 -0
  812. package/recipes/trough/thumbnail.png +0 -0
  813. package/recipes/turntable/build.mts +15 -0
  814. package/recipes/turntable/facts.md +3 -0
  815. package/recipes/turntable/guide.en.md +18 -0
  816. package/recipes/turntable/guide.ko.md +18 -0
  817. package/recipes/turntable/models/default.json +1 -0
  818. package/recipes/turntable/recipe.json +140 -0
  819. package/recipes/turntable/spec.json +43 -0
  820. package/recipes/turntable/thumbnail.png +0 -0
  821. package/recipes/valve-handle/build.mts +17 -0
  822. package/recipes/valve-handle/facts.md +3 -0
  823. package/recipes/valve-handle/guide.en.md +17 -0
  824. package/recipes/valve-handle/guide.ko.md +17 -0
  825. package/recipes/valve-handle/models/default.json +1 -0
  826. package/recipes/valve-handle/recipe.json +149 -0
  827. package/recipes/valve-handle/spec.json +18 -0
  828. package/recipes/valve-handle/thumbnail.png +0 -0
  829. package/recipes/waste-bin/build.mts +10 -0
  830. package/recipes/waste-bin/facts.md +3 -0
  831. package/recipes/waste-bin/guide.en.md +17 -0
  832. package/recipes/waste-bin/guide.ko.md +17 -0
  833. package/recipes/waste-bin/models/default.json +1 -0
  834. package/recipes/waste-bin/recipe.json +82 -0
  835. package/recipes/waste-bin/spec.json +45 -0
  836. package/recipes/waste-bin/thumbnail.png +0 -0
  837. package/recipes/work-light/build.mts +18 -0
  838. package/recipes/work-light/facts.md +3 -0
  839. package/recipes/work-light/guide.en.md +17 -0
  840. package/recipes/work-light/guide.ko.md +17 -0
  841. package/recipes/work-light/models/default.json +1 -0
  842. package/recipes/work-light/recipe.json +162 -0
  843. package/recipes/work-light/spec.json +18 -0
  844. package/recipes/work-light/thumbnail.png +0 -0
  845. package/recipes/workbench-top/build.mts +12 -0
  846. package/recipes/workbench-top/facts.md +3 -0
  847. package/recipes/workbench-top/guide.en.md +18 -0
  848. package/recipes/workbench-top/guide.ko.md +18 -0
  849. package/recipes/workbench-top/models/default.json +1 -0
  850. package/recipes/workbench-top/recipe.json +99 -0
  851. package/recipes/workbench-top/spec.json +45 -0
  852. package/recipes/workbench-top/thumbnail.png +0 -0
  853. package/scripts/build-templates.mts +64 -0
  854. package/scripts/templates.test.mts +89 -0
  855. package/server/index.ts +6 -10
  856. package/server/routers/figure-thumbnail-router.ts +24 -0
  857. package/server/service/figure-recipe/figure-recipe-resolver.ts +28 -16
  858. package/server/service/figure-recipe/figure-recipe-templates.test.ts +47 -0
  859. package/server/service/figure-recipe/figure-recipe-templates.ts +70 -0
  860. package/server/service/figure-recipe/figure-recipe-type.ts +31 -0
  861. package/server/service/figure-recipe/index.ts +2 -4
  862. package/server/service/index.ts +4 -4
  863. package/dist-server/service/figure-recipe/figure-recipe-store.d.ts +0 -30
  864. package/dist-server/service/figure-recipe/figure-recipe-store.js +0 -79
  865. package/dist-server/service/figure-recipe/figure-recipe-store.js.map +0 -1
  866. package/dist-server/service/figure-recipe/figure-recipe.d.ts +0 -19
  867. package/dist-server/service/figure-recipe/figure-recipe.js +0 -67
  868. package/dist-server/service/figure-recipe/figure-recipe.js.map +0 -1
  869. package/server/service/figure-recipe/figure-recipe-db.test.ts +0 -103
  870. package/server/service/figure-recipe/figure-recipe-store.ts +0 -75
  871. package/server/service/figure-recipe/figure-recipe.ts +0 -51
@@ -13,18 +13,13 @@ require("./routes.js");
13
13
  const figure_tools_js_1 = require("./service/figure/figure-tools.js");
14
14
  (0, figure_tools_js_1.registerFigureToolCategories)();
15
15
  /*
16
- * The built-in recipes into the system domain, as auth-base fills its privileges: once, and again gives the same result
17
- * (ADR-0095 condition 2). A recipe that fails its check is said here, not half put in.
16
+ * The recipe templates are read once at start (ADR-0095, amended): what was served, and any file whose form is wrong,
17
+ * said here rather than found by a person as a missing template.
18
18
  */
19
- const figure_recipe_store_js_1 = require("./service/figure-recipe/figure-recipe-store.js");
20
- process.on('bootstrap-module-start', async () => {
21
- try {
22
- const { added, skipped, refused } = await (0, figure_recipe_store_js_1.seedBuiltInRecipes)();
23
- console.log(`[figure-service:bootstrap] recipes added ${added.length}, already there ${skipped.length}${refused.length ? `, refused: ${refused.join(' | ')}` : ''}`);
24
- }
25
- catch (error) {
26
- console.error('[figure-service:bootstrap] recipes were not seeded:', error);
27
- }
19
+ const figure_recipe_templates_js_1 = require("./service/figure-recipe/figure-recipe-templates.js");
20
+ process.on('bootstrap-module-start', () => {
21
+ const { templates, refused } = (0, figure_recipe_templates_js_1.recipeTemplates)();
22
+ console.log(`[figure-service:bootstrap] recipe templates ${templates.length}${refused.length ? `, left out: ${refused.join(' | ')}` : ''}`);
28
23
  });
29
24
  tslib_1.__exportStar(require("./service/index.js"), exports);
30
25
  tslib_1.__exportStar(require("./migrations/index.js"), exports);
@@ -1 +1 @@
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;;;GAGG;AACH,2FAAmF;AACnF,OAAO,CAAC,EAAE,CAAC,wBAA+B,EAAE,KAAK,IAAI,EAAE;IACrD,IAAI,CAAC;QACH,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,MAAM,IAAA,2CAAkB,GAAE,CAAA;QAC9D,OAAO,CAAC,GAAG,CAAC,4CAA4C,KAAK,CAAC,MAAM,mBAAmB,OAAO,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,cAAc,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAA;IACtK,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,qDAAqD,EAAE,KAAK,CAAC,CAAA;IAC7E,CAAC;AACH,CAAC,CAAC,CAAA;AAEF,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\n/*\n * The built-in recipes into the system domain, as auth-base fills its privileges: once, and again gives the same result\n * (ADR-0095 condition 2). A recipe that fails its check is said here, not half put in.\n */\nimport { seedBuiltInRecipes } from './service/figure-recipe/figure-recipe-store.js'\nprocess.on('bootstrap-module-start' as any, async () => {\n try {\n const { added, skipped, refused } = await seedBuiltInRecipes()\n console.log(`[figure-service:bootstrap] recipes added ${added.length}, already there ${skipped.length}${refused.length ? `, refused: ${refused.join(' | ')}` : ''}`)\n } catch (error) {\n console.error('[figure-service:bootstrap] recipes were not seeded:', error)\n }\n})\n\nexport * from './service/index.js'\nexport * from './migrations/index.js'\nexport { registerFigureToolCategories } from './service/figure/figure-tools.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;;;GAGG;AACH,mGAAoF;AACpF,OAAO,CAAC,EAAE,CAAC,wBAA+B,EAAE,GAAG,EAAE;IAC/C,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,GAAG,IAAA,4CAAe,GAAE,CAAA;IAChD,OAAO,CAAC,GAAG,CAAC,+CAA+C,SAAS,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,eAAe,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAA;AAC7I,CAAC,CAAC,CAAA;AAEF,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\n/*\n * The recipe templates are read once at start (ADR-0095, amended): what was served, and any file whose form is wrong,\n * said here rather than found by a person as a missing template.\n */\nimport { recipeTemplates } from './service/figure-recipe/figure-recipe-templates.js'\nprocess.on('bootstrap-module-start' as any, () => {\n const { templates, refused } = recipeTemplates()\n console.log(`[figure-service:bootstrap] recipe templates ${templates.length}${refused.length ? `, left out: ${refused.join(' | ')}` : ''}`)\n})\n\nexport * from './service/index.js'\nexport * from './migrations/index.js'\nexport { registerFigureToolCategories } from './service/figure/figure-tools.js'\n"]}
@@ -6,7 +6,9 @@ const koa_router_1 = tslib_1.__importDefault(require("koa-router"));
6
6
  const typeorm_1 = require("typeorm");
7
7
  const auth_base_1 = require("@things-factory/auth-base");
8
8
  const shell_1 = require("@things-factory/shell");
9
+ const fs_1 = require("fs");
9
10
  const figure_js_1 = require("../service/figure/figure.js");
11
+ const figure_recipe_templates_js_1 = require("../service/figure-recipe/figure-recipe-templates.js");
10
12
  /**
11
13
  * 썸네일을 **그림으로** 내보내는 길.
12
14
  *
@@ -90,4 +92,24 @@ exports.figureThumbnailRouter.get('/figure-thumbnail/:id', async (context, next)
90
92
  }
91
93
  context.body = decoded.buffer;
92
94
  });
95
+ /**
96
+ * A recipe template's thumbnail (ADR-0095 amended): the file drawn from its model, served behind the same door as the
97
+ * templates themselves. The file changes only with a release, so a day's private cache is enough.
98
+ */
99
+ exports.figureThumbnailRouter.get('/figure-recipe-thumbnail/:name', async (context) => {
100
+ const { domain, user, unsafeIP, prohibitedPrivileges } = context.state;
101
+ const allowed = await (0, auth_base_1.checkPermission)({ category: 'figure', privilege: 'query', owner: true }, user, domain, unsafeIP, prohibitedPrivileges);
102
+ if (!allowed) {
103
+ context.status = 403;
104
+ return;
105
+ }
106
+ const template = (0, figure_recipe_templates_js_1.recipeTemplates)().templates.find(t => t.recipe.name === context.params.name);
107
+ if (!template?.thumbnail) {
108
+ context.status = 404;
109
+ return;
110
+ }
111
+ context.type = 'image/png';
112
+ context.set('Cache-Control', 'private, max-age=86400');
113
+ context.body = (0, fs_1.readFileSync)(template.thumbnail);
114
+ });
93
115
  //# sourceMappingURL=figure-thumbnail-router.js.map
@@ -1 +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"]}
1
+ {"version":3,"file":"figure-thumbnail-router.js","sourceRoot":"","sources":["../../server/routers/figure-thumbnail-router.ts"],"names":[],"mappings":"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","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 { readFileSync } from 'fs'\n\nimport { Figure } from '../service/figure/figure.js'\nimport { recipeTemplates } from '../service/figure-recipe/figure-recipe-templates.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\n/**\n * A recipe template's thumbnail (ADR-0095 amended): the file drawn from its model, served behind the same door as the\n * templates themselves. The file changes only with a release, so a day's private cache is enough.\n */\nfigureThumbnailRouter.get('/figure-recipe-thumbnail/:name', async context => {\n const { domain, user, unsafeIP, prohibitedPrivileges } = context.state\n const allowed = await checkPermission({ category: 'figure', privilege: 'query', owner: true }, user, domain, unsafeIP, prohibitedPrivileges)\n if (!allowed) {\n context.status = 403\n return\n }\n const template = recipeTemplates().templates.find(t => t.recipe.name === context.params.name)\n if (!template?.thumbnail) {\n context.status = 404\n return\n }\n context.type = 'image/png'\n context.set('Cache-Control', 'private, max-age=86400')\n context.body = readFileSync(template.thumbnail)\n})\n"]}
@@ -1,5 +1,5 @@
1
- import { FigureRecipe } from './figure-recipe.js';
2
- export declare class FigureRecipeResolver {
3
- figureRecipes(all: boolean, context: ResolverContext): Promise<FigureRecipe[]>;
4
- saveFigureRecipe(source: string, state: string, context: ResolverContext): Promise<FigureRecipe>;
1
+ import { FigureRecipeTemplate } from './figure-recipe-type.js';
2
+ export declare class FigureRecipeTemplateQuery {
3
+ figureRecipeTemplates(lang: string): FigureRecipeTemplate[];
4
+ figureRecipeTemplate(name: string, lang: string): FigureRecipeTemplate | null;
5
5
  }
@@ -1,41 +1,51 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.FigureRecipeResolver = void 0;
3
+ exports.FigureRecipeTemplateQuery = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const type_graphql_1 = require("type-graphql");
6
6
  const shell_1 = require("@things-factory/shell");
7
- const figure_recipe_js_1 = require("./figure-recipe.js");
8
- const figure_recipe_store_js_1 = require("./figure-recipe-store.js");
9
- let FigureRecipeResolver = class FigureRecipeResolver {
10
- async figureRecipes(all, context) {
11
- return await (0, figure_recipe_store_js_1.recipesFor)(context.state.domain, all ? undefined : 'released');
7
+ const figure_recipe_type_js_1 = require("./figure-recipe-type.js");
8
+ const figure_recipe_templates_js_1 = require("./figure-recipe-templates.js");
9
+ const view = (t, lang, withGuide) => ({
10
+ name: t.recipe.name,
11
+ category: t.category,
12
+ tags: t.tags,
13
+ target: t.recipe.target,
14
+ title: (0, figure_recipe_templates_js_1.inLanguage)(t.recipe.title, lang),
15
+ description: (0, figure_recipe_templates_js_1.inLanguage)(t.recipe.description, lang),
16
+ source: JSON.stringify(t.recipe),
17
+ ...(withGuide ? { guide: (0, figure_recipe_templates_js_1.inLanguage)(t.guides, lang) } : {}),
18
+ hasThumbnail: !!t.thumbnail
19
+ });
20
+ let FigureRecipeTemplateQuery = class FigureRecipeTemplateQuery {
21
+ figureRecipeTemplates(lang) {
22
+ return (0, figure_recipe_templates_js_1.recipeTemplates)().templates.map(t => view(t, lang, false));
12
23
  }
13
- async saveFigureRecipe(source, state, context) {
14
- return await (0, figure_recipe_store_js_1.saveRecipe)(context.state.domain, source, state === 'draft' ? 'draft' : 'released');
24
+ figureRecipeTemplate(name, lang) {
25
+ const t = (0, figure_recipe_templates_js_1.recipeTemplates)().templates.find(one => one.recipe.name === name);
26
+ return t ? view(t, lang, true) : null;
15
27
  }
16
28
  };
17
- exports.FigureRecipeResolver = FigureRecipeResolver;
29
+ exports.FigureRecipeTemplateQuery = FigureRecipeTemplateQuery;
18
30
  tslib_1.__decorate([
19
31
  (0, type_graphql_1.Directive)('@privilege(category: "figure", privilege: "query", domainOwnerGranted: true)'),
20
- (0, type_graphql_1.Query)(returns => [figure_recipe_js_1.FigureRecipe], { description: "The recipes this domain sees: its own and its parent's (its own stand in by name). Released ones unless all is asked." }),
21
- tslib_1.__param(0, (0, type_graphql_1.Arg)('all', { nullable: true, description: 'Drafts as well.' })),
22
- tslib_1.__param(1, (0, type_graphql_1.Ctx)()),
32
+ (0, type_graphql_1.Query)(returns => [figure_recipe_type_js_1.FigureRecipeTemplate], { description: 'The recipe templates the product ships, in the language asked. Guides are left out of the list.' }),
33
+ tslib_1.__param(0, (0, type_graphql_1.Arg)('lang', { nullable: true, description: 'The language of the titles (ko, en, …).' })),
23
34
  tslib_1.__metadata("design:type", Function),
24
- tslib_1.__metadata("design:paramtypes", [Boolean, Object]),
25
- tslib_1.__metadata("design:returntype", Promise)
26
- ], FigureRecipeResolver.prototype, "figureRecipes", null);
35
+ tslib_1.__metadata("design:paramtypes", [String]),
36
+ tslib_1.__metadata("design:returntype", Array)
37
+ ], FigureRecipeTemplateQuery.prototype, "figureRecipeTemplates", null);
27
38
  tslib_1.__decorate([
28
- (0, type_graphql_1.Directive)('@privilege(category: "figure", privilege: "mutation", domainOwnerGranted: true)'),
29
- (0, type_graphql_1.Mutation)(returns => figure_recipe_js_1.FigureRecipe, { description: 'Saves a recipe into this domain by its name, after putting it into an empty room with its defaults (ADR-0095).' }),
30
- tslib_1.__param(0, (0, type_graphql_1.Arg)('source', { description: 'The recipe JSON (figure-model V3Recipe).' })),
31
- tslib_1.__param(1, (0, type_graphql_1.Arg)('state', { nullable: true, description: "'draft' or 'released' (the default)." })),
32
- tslib_1.__param(2, (0, type_graphql_1.Ctx)()),
39
+ (0, type_graphql_1.Directive)('@privilege(category: "figure", privilege: "query", domainOwnerGranted: true)'),
40
+ (0, type_graphql_1.Query)(returns => figure_recipe_type_js_1.FigureRecipeTemplate, { nullable: true, description: 'One recipe template with its guide, in the language asked.' }),
41
+ tslib_1.__param(0, (0, type_graphql_1.Arg)('name', { description: 'The template name.' })),
42
+ tslib_1.__param(1, (0, type_graphql_1.Arg)('lang', { nullable: true, description: 'The language of the words (ko, en, …).' })),
33
43
  tslib_1.__metadata("design:type", Function),
34
- tslib_1.__metadata("design:paramtypes", [String, String, Object]),
35
- tslib_1.__metadata("design:returntype", Promise)
36
- ], FigureRecipeResolver.prototype, "saveFigureRecipe", null);
37
- exports.FigureRecipeResolver = FigureRecipeResolver = tslib_1.__decorate([
38
- (0, type_graphql_1.Resolver)(figure_recipe_js_1.FigureRecipe),
44
+ tslib_1.__metadata("design:paramtypes", [String, String]),
45
+ tslib_1.__metadata("design:returntype", figure_recipe_type_js_1.FigureRecipeTemplate)
46
+ ], FigureRecipeTemplateQuery.prototype, "figureRecipeTemplate", null);
47
+ exports.FigureRecipeTemplateQuery = FigureRecipeTemplateQuery = tslib_1.__decorate([
48
+ (0, type_graphql_1.Resolver)(figure_recipe_type_js_1.FigureRecipeTemplate),
39
49
  (0, type_graphql_1.UseMiddleware)(shell_1.SpeakRefusals)
40
- ], FigureRecipeResolver);
50
+ ], FigureRecipeTemplateQuery);
41
51
  //# sourceMappingURL=figure-recipe-resolver.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"figure-recipe-resolver.js","sourceRoot":"","sources":["../../../server/service/figure-recipe/figure-recipe-resolver.ts"],"names":[],"mappings":";;;;AAAA,+CAA4F;AAE5F,iDAAqD;AAErD,yDAAiD;AACjD,qEAAiE;AAI1D,IAAM,oBAAoB,GAA1B,MAAM,oBAAoB;IAGzB,AAAN,KAAK,CAAC,aAAa,CAAiE,GAAY,EAAS,OAAwB;QAC/H,OAAO,MAAM,IAAA,mCAAU,EAAC,OAAO,CAAC,KAAK,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,UAAU,CAAC,CAAA;IAC7E,CAAC;IAIK,AAAN,KAAK,CAAC,gBAAgB,CACwD,MAAc,EACH,KAAa,EAC7F,OAAwB;QAE/B,OAAO,MAAM,IAAA,mCAAU,EAAC,OAAO,CAAC,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,KAAK,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC,CAAA;IACjG,CAAC;CACF,CAAA;AAhBY,oDAAoB;AAGzB;IAFL,IAAA,wBAAS,EAAC,8EAA8E,CAAC;IACzF,IAAA,oBAAK,EAAC,OAAO,CAAC,EAAE,CAAC,CAAC,+BAAY,CAAC,EAAE,EAAE,WAAW,EAAE,uHAAuH,EAAE,CAAC;IACtJ,mBAAA,IAAA,kBAAG,EAAC,KAAK,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,iBAAiB,EAAE,CAAC,CAAA;IAAgB,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;yDAEvG;AAIK;IAFL,IAAA,wBAAS,EAAC,iFAAiF,CAAC;IAC5F,IAAA,uBAAQ,EAAC,OAAO,CAAC,EAAE,CAAC,+BAAY,EAAE,EAAE,WAAW,EAAE,gHAAgH,EAAE,CAAC;IAElK,mBAAA,IAAA,kBAAG,EAAC,QAAQ,EAAE,EAAE,WAAW,EAAE,0CAA0C,EAAE,CAAC,CAAA;IAC1E,mBAAA,IAAA,kBAAG,EAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,sCAAsC,EAAE,CAAC,CAAA;IACrF,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;4DAGP;+BAfU,oBAAoB;IAFhC,IAAA,uBAAQ,EAAC,+BAAY,CAAC;IACtB,IAAA,4BAAa,EAAC,qBAAa,CAAC;GAChB,oBAAoB,CAgBhC","sourcesContent":["import { Arg, Ctx, Directive, Mutation, Query, Resolver, UseMiddleware } from 'type-graphql'\n\nimport { SpeakRefusals } from '@things-factory/shell'\n\nimport { FigureRecipe } from './figure-recipe.js'\nimport { recipesFor, saveRecipe } from './figure-recipe-store.js'\n\n@Resolver(FigureRecipe)\n@UseMiddleware(SpeakRefusals)\nexport class FigureRecipeResolver {\n @Directive('@privilege(category: \"figure\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(returns => [FigureRecipe], { description: \"The recipes this domain sees: its own and its parent's (its own stand in by name). Released ones unless all is asked.\" })\n async figureRecipes(@Arg('all', { nullable: true, description: 'Drafts as well.' }) all: boolean, @Ctx() context: ResolverContext): Promise<FigureRecipe[]> {\n return await recipesFor(context.state.domain, all ? undefined : 'released')\n }\n\n @Directive('@privilege(category: \"figure\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Mutation(returns => FigureRecipe, { description: 'Saves a recipe into this domain by its name, after putting it into an empty room with its defaults (ADR-0095).' })\n async saveFigureRecipe(\n @Arg('source', { description: 'The recipe JSON (figure-model V3Recipe).' }) source: string,\n @Arg('state', { nullable: true, description: \"'draft' or 'released' (the default).\" }) state: string,\n @Ctx() context: ResolverContext\n ): Promise<FigureRecipe> {\n return await saveRecipe(context.state.domain, source, state === 'draft' ? 'draft' : 'released')\n }\n}\n"]}
1
+ {"version":3,"file":"figure-recipe-resolver.js","sourceRoot":"","sources":["../../../server/service/figure-recipe/figure-recipe-resolver.ts"],"names":[],"mappings":";;;;AAAA,+CAA6E;AAE7E,iDAAqD;AAErD,mEAA8D;AAC9D,6EAA+F;AAE/F,MAAM,IAAI,GAAG,CAAC,CAAiB,EAAE,IAAwB,EAAE,SAAkB,EAAwB,EAAE,CAAC,CAAC;IACvG,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,IAAI;IACnB,QAAQ,EAAE,CAAC,CAAC,QAAQ;IACpB,IAAI,EAAE,CAAC,CAAC,IAAI;IACZ,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,MAAM;IACvB,KAAK,EAAE,IAAA,uCAAU,EAAC,CAAC,CAAC,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC;IACvC,WAAW,EAAE,IAAA,uCAAU,EAAC,CAAC,CAAC,MAAM,CAAC,WAAW,EAAE,IAAI,CAAC;IACnD,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC;IAChC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,IAAA,uCAAU,EAAC,CAAC,CAAC,MAAM,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IAC3D,YAAY,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS;CAC5B,CAAC,CAAA;AAIK,IAAM,yBAAyB,GAA/B,MAAM,yBAAyB;IAGpC,qBAAqB,CAA0F,IAAY;QACzH,OAAO,IAAA,4CAAe,GAAE,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC,CAAA;IACnE,CAAC;IAID,oBAAoB,CACkC,IAAY,EACwB,IAAY;QAEpG,MAAM,CAAC,GAAG,IAAA,4CAAe,GAAE,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,KAAK,IAAI,CAAC,CAAA;QAC3E,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;IACvC,CAAC;CACF,CAAA;AAhBY,8DAAyB;AAGpC;IAFC,IAAA,wBAAS,EAAC,8EAA8E,CAAC;IACzF,IAAA,oBAAK,EAAC,OAAO,CAAC,EAAE,CAAC,CAAC,4CAAoB,CAAC,EAAE,EAAE,WAAW,EAAE,iGAAiG,EAAE,CAAC;IACtI,mBAAA,IAAA,kBAAG,EAAC,MAAM,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,yCAAyC,EAAE,CAAC,CAAA;;;;sEAE7G;AAID;IAFC,IAAA,wBAAS,EAAC,8EAA8E,CAAC;IACzF,IAAA,oBAAK,EAAC,OAAO,CAAC,EAAE,CAAC,4CAAoB,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,4DAA4D,EAAE,CAAC;IAEnI,mBAAA,IAAA,kBAAG,EAAC,MAAM,EAAE,EAAE,WAAW,EAAE,oBAAoB,EAAE,CAAC,CAAA;IAClD,mBAAA,IAAA,kBAAG,EAAC,MAAM,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,wCAAwC,EAAE,CAAC,CAAA;;;4CACtF,4CAAoB;qEAGtB;oCAfU,yBAAyB;IAFrC,IAAA,uBAAQ,EAAC,4CAAoB,CAAC;IAC9B,IAAA,4BAAa,EAAC,qBAAa,CAAC;GAChB,yBAAyB,CAgBrC","sourcesContent":["import { Arg, Directive, Query, Resolver, UseMiddleware } from 'type-graphql'\n\nimport { SpeakRefusals } from '@things-factory/shell'\n\nimport { FigureRecipeTemplate } from './figure-recipe-type.js'\nimport { inLanguage, recipeTemplates, type RecipeTemplate } from './figure-recipe-templates.js'\n\nconst view = (t: RecipeTemplate, lang: string | undefined, withGuide: boolean): FigureRecipeTemplate => ({\n name: t.recipe.name,\n category: t.category,\n tags: t.tags,\n target: t.recipe.target,\n title: inLanguage(t.recipe.title, lang),\n description: inLanguage(t.recipe.description, lang),\n source: JSON.stringify(t.recipe),\n ...(withGuide ? { guide: inLanguage(t.guides, lang) } : {}),\n hasThumbnail: !!t.thumbnail\n})\n\n@Resolver(FigureRecipeTemplate)\n@UseMiddleware(SpeakRefusals)\nexport class FigureRecipeTemplateQuery {\n @Directive('@privilege(category: \"figure\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(returns => [FigureRecipeTemplate], { description: 'The recipe templates the product ships, in the language asked. Guides are left out of the list.' })\n figureRecipeTemplates(@Arg('lang', { nullable: true, description: 'The language of the titles (ko, en, …).' }) lang: string): FigureRecipeTemplate[] {\n return recipeTemplates().templates.map(t => view(t, lang, false))\n }\n\n @Directive('@privilege(category: \"figure\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(returns => FigureRecipeTemplate, { nullable: true, description: 'One recipe template with its guide, in the language asked.' })\n figureRecipeTemplate(\n @Arg('name', { description: 'The template name.' }) name: string,\n @Arg('lang', { nullable: true, description: 'The language of the words (ko, en, …).' }) lang: string\n ): FigureRecipeTemplate | null {\n const t = recipeTemplates().templates.find(one => one.recipe.name === name)\n return t ? view(t, lang, true) : null\n }\n}\n"]}
@@ -0,0 +1,28 @@
1
+ import type { V3Recipe } from '@hatiolab/figure-model';
2
+ /**
3
+ * The recipe templates the product ships (ADR-0095, amended 2026-09-26): static files, served, not managed.
4
+ *
5
+ * recipes/<name>/recipe.json the command list, its parameters, category, tags, titles
6
+ * recipes/<name>/guide.<lang>.md how to use it, per language (English stands in for a missing one)
7
+ * recipes/<name>/thumbnail.png drawn by a script from the recipe put into the test room
8
+ *
9
+ * There is no table and no screen that changes them: a template is added or changed as a file, with the code. The
10
+ * start reads their form only; putting each into the test room is the tests' work (`figure-recipe-templates.test.ts`).
11
+ */
12
+ export interface RecipeTemplate {
13
+ recipe: V3Recipe;
14
+ category: string;
15
+ tags: string[];
16
+ /** Guides by language code, as written. */
17
+ guides: Record<string, string>;
18
+ thumbnail: string | null;
19
+ }
20
+ /** The folder the templates are in: `recipes/` at the package root, beside `server/` and `dist-server/`. */
21
+ export declare const RECIPE_DIR: string;
22
+ /** Every template, read once. One whose form is wrong is left out and named, not half served. */
23
+ export declare function recipeTemplates(dir?: string): {
24
+ templates: RecipeTemplate[];
25
+ refused: string[];
26
+ };
27
+ /** A word in the language asked: that language, its base (`ko` for `ko-KR`), English, then the first there is. */
28
+ export declare function inLanguage(words: Record<string, string> | undefined, lang: string | undefined): string;
@@ -0,0 +1,57 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RECIPE_DIR = void 0;
4
+ exports.recipeTemplates = recipeTemplates;
5
+ exports.inLanguage = inLanguage;
6
+ const fs_1 = require("fs");
7
+ const path_1 = require("path");
8
+ const figure_model_1 = require("@hatiolab/figure-model");
9
+ /** The folder the templates are in: `recipes/` at the package root, beside `server/` and `dist-server/`. */
10
+ exports.RECIPE_DIR = (0, path_1.resolve)(__dirname, '..', '..', '..', 'recipes');
11
+ let loaded = null;
12
+ /** Every template, read once. One whose form is wrong is left out and named, not half served. */
13
+ function recipeTemplates(dir = exports.RECIPE_DIR) {
14
+ if (loaded && dir === exports.RECIPE_DIR)
15
+ return loaded;
16
+ const templates = [];
17
+ const refused = [];
18
+ /* A folder starting with _ holds what the models are built with, not a template. */
19
+ const folders = (0, fs_1.existsSync)(dir) ? (0, fs_1.readdirSync)(dir, { withFileTypes: true }).filter(e => e.isDirectory() && !e.name.startsWith('_')).map(e => e.name).sort() : [];
20
+ for (const folder of folders) {
21
+ const at = (0, path_1.join)(dir, folder);
22
+ try {
23
+ const raw = JSON.parse((0, fs_1.readFileSync)((0, path_1.join)(at, 'recipe.json'), 'utf-8'));
24
+ const recipe = (0, figure_model_1.parseV3Recipe)(raw);
25
+ if (recipe.name !== folder)
26
+ throw new Error(`the recipe is named ${recipe.name}, its folder ${folder}`);
27
+ const guides = {};
28
+ for (const file of (0, fs_1.readdirSync)(at)) {
29
+ const lang = /^guide\.([a-zA-Z-]+)\.md$/.exec(file)?.[1];
30
+ if (lang)
31
+ guides[lang] = (0, fs_1.readFileSync)((0, path_1.join)(at, file), 'utf-8');
32
+ }
33
+ templates.push({
34
+ recipe,
35
+ category: typeof raw.category === 'string' ? raw.category : 'other',
36
+ tags: Array.isArray(raw.tags) ? raw.tags.filter((t) => typeof t === 'string') : [],
37
+ guides,
38
+ thumbnail: (0, fs_1.existsSync)((0, path_1.join)(at, 'thumbnail.png')) ? (0, path_1.join)(at, 'thumbnail.png') : null
39
+ });
40
+ }
41
+ catch (error) {
42
+ refused.push(`${folder}: ${error instanceof Error ? error.message : String(error)}`);
43
+ }
44
+ }
45
+ const result = { templates, refused };
46
+ if (dir === exports.RECIPE_DIR)
47
+ loaded = result;
48
+ return result;
49
+ }
50
+ /** A word in the language asked: that language, its base (`ko` for `ko-KR`), English, then the first there is. */
51
+ function inLanguage(words, lang) {
52
+ if (!words)
53
+ return '';
54
+ const asked = lang || 'en';
55
+ return words[asked] ?? words[asked.split('-')[0]] ?? words.en ?? Object.values(words)[0] ?? '';
56
+ }
57
+ //# sourceMappingURL=figure-recipe-templates.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"figure-recipe-templates.js","sourceRoot":"","sources":["../../../server/service/figure-recipe/figure-recipe-templates.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync, readdirSync, readFileSync } from 'fs'\nimport { join, resolve } from 'path'\n\nimport { parseV3Recipe } from '@hatiolab/figure-model'\nimport type { V3Recipe } from '@hatiolab/figure-model'\n\n/**\n * The recipe templates the product ships (ADR-0095, amended 2026-09-26): static files, served, not managed.\n *\n * recipes/<name>/recipe.json the command list, its parameters, category, tags, titles\n * recipes/<name>/guide.<lang>.md how to use it, per language (English stands in for a missing one)\n * recipes/<name>/thumbnail.png drawn by a script from the recipe put into the test room\n *\n * There is no table and no screen that changes them: a template is added or changed as a file, with the code. The\n * start reads their form only; putting each into the test room is the tests' work (`figure-recipe-templates.test.ts`).\n */\nexport interface RecipeTemplate {\n recipe: V3Recipe\n category: string\n tags: string[]\n /** Guides by language code, as written. */\n guides: Record<string, string>\n thumbnail: string | null\n}\n\n/** The folder the templates are in: `recipes/` at the package root, beside `server/` and `dist-server/`. */\nexport const RECIPE_DIR = resolve(__dirname, '..', '..', '..', 'recipes')\n\nlet loaded: { templates: RecipeTemplate[]; refused: string[] } | null = null\n\n/** Every template, read once. One whose form is wrong is left out and named, not half served. */\nexport function recipeTemplates(dir: string = RECIPE_DIR): { templates: RecipeTemplate[]; refused: string[] } {\n if (loaded && dir === RECIPE_DIR) return loaded\n const templates: RecipeTemplate[] = []\n const refused: string[] = []\n /* A folder starting with _ holds what the models are built with, not a template. */\n const folders = existsSync(dir) ? readdirSync(dir, { withFileTypes: true }).filter(e => e.isDirectory() && !e.name.startsWith('_')).map(e => e.name).sort() : []\n for (const folder of folders) {\n const at = join(dir, folder)\n try {\n const raw = JSON.parse(readFileSync(join(at, 'recipe.json'), 'utf-8'))\n const recipe = parseV3Recipe(raw)\n if (recipe.name !== folder) throw new Error(`the recipe is named ${recipe.name}, its folder ${folder}`)\n const guides: Record<string, string> = {}\n for (const file of readdirSync(at)) {\n const lang = /^guide\\.([a-zA-Z-]+)\\.md$/.exec(file)?.[1]\n if (lang) guides[lang] = readFileSync(join(at, file), 'utf-8')\n }\n templates.push({\n recipe,\n category: typeof raw.category === 'string' ? raw.category : 'other',\n tags: Array.isArray(raw.tags) ? raw.tags.filter((t: unknown) => typeof t === 'string') : [],\n guides,\n thumbnail: existsSync(join(at, 'thumbnail.png')) ? join(at, 'thumbnail.png') : null\n })\n } catch (error) {\n refused.push(`${folder}: ${error instanceof Error ? error.message : String(error)}`)\n }\n }\n const result = { templates, refused }\n if (dir === RECIPE_DIR) loaded = result\n return result\n}\n\n/** A word in the language asked: that language, its base (`ko` for `ko-KR`), English, then the first there is. */\nexport function inLanguage(words: Record<string, string> | undefined, lang: string | undefined): string {\n if (!words) return ''\n const asked = lang || 'en'\n return words[asked] ?? words[asked.split('-')[0]!] ?? words.en ?? Object.values(words)[0] ?? ''\n}\n"]}
@@ -0,0 +1,11 @@
1
+ export declare class FigureRecipeTemplate {
2
+ name: string;
3
+ category: string;
4
+ tags: string[];
5
+ target: string;
6
+ title: string;
7
+ description: string;
8
+ source: string;
9
+ guide?: string;
10
+ hasThumbnail: boolean;
11
+ }
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FigureRecipeTemplate = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const type_graphql_1 = require("type-graphql");
6
+ let FigureRecipeTemplate = class FigureRecipeTemplate {
7
+ };
8
+ exports.FigureRecipeTemplate = FigureRecipeTemplate;
9
+ tslib_1.__decorate([
10
+ (0, type_graphql_1.Field)({ description: 'The template name: lower case, digits and hyphens.' }),
11
+ tslib_1.__metadata("design:type", String)
12
+ ], FigureRecipeTemplate.prototype, "name", void 0);
13
+ tslib_1.__decorate([
14
+ (0, type_graphql_1.Field)({ description: 'The category it is listed under (door, conveyor, …).' }),
15
+ tslib_1.__metadata("design:type", String)
16
+ ], FigureRecipeTemplate.prototype, "category", void 0);
17
+ tslib_1.__decorate([
18
+ (0, type_graphql_1.Field)(type => [String], { description: 'Words it is found by.' }),
19
+ tslib_1.__metadata("design:type", Array)
20
+ ], FigureRecipeTemplate.prototype, "tags", void 0);
21
+ tslib_1.__decorate([
22
+ (0, type_graphql_1.Field)({ description: "'part' when it is put onto a chosen part, 'figure' when onto the whole figure." }),
23
+ tslib_1.__metadata("design:type", String)
24
+ ], FigureRecipeTemplate.prototype, "target", void 0);
25
+ tslib_1.__decorate([
26
+ (0, type_graphql_1.Field)({ description: 'Its title in the language asked (English stands in).' }),
27
+ tslib_1.__metadata("design:type", String)
28
+ ], FigureRecipeTemplate.prototype, "title", void 0);
29
+ tslib_1.__decorate([
30
+ (0, type_graphql_1.Field)({ description: 'Its one-line description in the language asked (English stands in).' }),
31
+ tslib_1.__metadata("design:type", String)
32
+ ], FigureRecipeTemplate.prototype, "description", void 0);
33
+ tslib_1.__decorate([
34
+ (0, type_graphql_1.Field)({ description: 'The recipe JSON (figure-model V3Recipe), as the modeller applies it.' }),
35
+ tslib_1.__metadata("design:type", String)
36
+ ], FigureRecipeTemplate.prototype, "source", void 0);
37
+ tslib_1.__decorate([
38
+ (0, type_graphql_1.Field)({ nullable: true, description: 'How to use it, as Markdown, in the language asked (English stands in). Only when one template is asked for.' }),
39
+ tslib_1.__metadata("design:type", String)
40
+ ], FigureRecipeTemplate.prototype, "guide", void 0);
41
+ tslib_1.__decorate([
42
+ (0, type_graphql_1.Field)({ description: 'Whether it has a thumbnail image.' }),
43
+ tslib_1.__metadata("design:type", Boolean)
44
+ ], FigureRecipeTemplate.prototype, "hasThumbnail", void 0);
45
+ exports.FigureRecipeTemplate = FigureRecipeTemplate = tslib_1.__decorate([
46
+ (0, type_graphql_1.ObjectType)({ description: 'A recipe template the product ships: parts, their fastenings and motion, put onto a part in one step (ADR-0095).' })
47
+ ], FigureRecipeTemplate);
48
+ //# sourceMappingURL=figure-recipe-type.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"figure-recipe-type.js","sourceRoot":"","sources":["../../../server/service/figure-recipe/figure-recipe-type.ts"],"names":[],"mappings":";;;;AAAA,+CAAgD;AAGzC,IAAM,oBAAoB,GAA1B,MAAM,oBAAoB;CA2BhC,CAAA;AA3BY,oDAAoB;AAE/B;IADC,IAAA,oBAAK,EAAC,EAAE,WAAW,EAAE,oDAAoD,EAAE,CAAC;;kDACjE;AAGZ;IADC,IAAA,oBAAK,EAAC,EAAE,WAAW,EAAE,sDAAsD,EAAE,CAAC;;sDAC/D;AAGhB;IADC,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,EAAE,WAAW,EAAE,uBAAuB,EAAE,CAAC;;kDACpD;AAGd;IADC,IAAA,oBAAK,EAAC,EAAE,WAAW,EAAE,gFAAgF,EAAE,CAAC;;oDAC3F;AAGd;IADC,IAAA,oBAAK,EAAC,EAAE,WAAW,EAAE,sDAAsD,EAAE,CAAC;;mDAClE;AAGb;IADC,IAAA,oBAAK,EAAC,EAAE,WAAW,EAAE,qEAAqE,EAAE,CAAC;;yDAC3E;AAGnB;IADC,IAAA,oBAAK,EAAC,EAAE,WAAW,EAAE,sEAAsE,EAAE,CAAC;;oDACjF;AAGd;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,6GAA6G,EAAE,CAAC;;mDACxI;AAGd;IADC,IAAA,oBAAK,EAAC,EAAE,WAAW,EAAE,mCAAmC,EAAE,CAAC;;0DACvC;+BA1BV,oBAAoB;IADhC,IAAA,yBAAU,EAAC,EAAE,WAAW,EAAE,kHAAkH,EAAE,CAAC;GACnI,oBAAoB,CA2BhC","sourcesContent":["import { Field, ObjectType } from 'type-graphql'\n\n@ObjectType({ description: 'A recipe template the product ships: parts, their fastenings and motion, put onto a part in one step (ADR-0095).' })\nexport class FigureRecipeTemplate {\n @Field({ description: 'The template name: lower case, digits and hyphens.' })\n name: string\n\n @Field({ description: 'The category it is listed under (door, conveyor, …).' })\n category: string\n\n @Field(type => [String], { description: 'Words it is found by.' })\n tags: string[]\n\n @Field({ description: \"'part' when it is put onto a chosen part, 'figure' when onto the whole figure.\" })\n target: string\n\n @Field({ description: 'Its title in the language asked (English stands in).' })\n title: string\n\n @Field({ description: 'Its one-line description in the language asked (English stands in).' })\n description: string\n\n @Field({ description: 'The recipe JSON (figure-model V3Recipe), as the modeller applies it.' })\n source: string\n\n @Field({ nullable: true, description: 'How to use it, as Markdown, in the language asked (English stands in). Only when one template is asked for.' })\n guide?: string\n\n @Field({ description: 'Whether it has a thumbnail image.' })\n hasThumbnail: boolean\n}\n"]}
@@ -1,4 +1,2 @@
1
- import { FigureRecipe } from './figure-recipe.js';
2
- import { FigureRecipeResolver } from './figure-recipe-resolver.js';
3
- export declare const entities: (typeof FigureRecipe)[];
4
- export declare const resolvers: (typeof FigureRecipeResolver)[];
1
+ import { FigureRecipeTemplateQuery } from './figure-recipe-resolver.js';
2
+ export declare const resolvers: (typeof FigureRecipeTemplateQuery)[];
@@ -1,8 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.resolvers = exports.entities = void 0;
4
- const figure_recipe_js_1 = require("./figure-recipe.js");
3
+ exports.resolvers = void 0;
5
4
  const figure_recipe_resolver_js_1 = require("./figure-recipe-resolver.js");
6
- exports.entities = [figure_recipe_js_1.FigureRecipe];
7
- exports.resolvers = [figure_recipe_resolver_js_1.FigureRecipeResolver];
5
+ exports.resolvers = [figure_recipe_resolver_js_1.FigureRecipeTemplateQuery];
8
6
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../server/service/figure-recipe/index.ts"],"names":[],"mappings":";;;AAAA,yDAAiD;AACjD,2EAAkE;AAErD,QAAA,QAAQ,GAAG,CAAC,+BAAY,CAAC,CAAA;AACzB,QAAA,SAAS,GAAG,CAAC,gDAAoB,CAAC,CAAA","sourcesContent":["import { FigureRecipe } from './figure-recipe.js'\nimport { FigureRecipeResolver } from './figure-recipe-resolver.js'\n\nexport const entities = [FigureRecipe]\nexport const resolvers = [FigureRecipeResolver]\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../server/service/figure-recipe/index.ts"],"names":[],"mappings":";;;AAAA,2EAAuE;AAE1D,QAAA,SAAS,GAAG,CAAC,qDAAyB,CAAC,CAAA","sourcesContent":["import { FigureRecipeTemplateQuery } from './figure-recipe-resolver.js'\n\nexport const resolvers = [FigureRecipeTemplateQuery]\n"]}
@@ -2,10 +2,10 @@ export * from './figure/figure.js';
2
2
  export * from './figure/figure-type.js';
3
3
  export * from './figure/figure-format.js';
4
4
  export * from './figure/figure-history.js';
5
- export * from './figure-recipe/figure-recipe.js';
6
- export { recipesFor, saveRecipe, seedBuiltInRecipes, builtInRecipes } from './figure-recipe/figure-recipe-store.js';
7
- export declare const entities: typeof import("./figure-recipe/figure-recipe.js").FigureRecipe[];
5
+ export * from './figure-recipe/figure-recipe-type.js';
6
+ export { recipeTemplates, inLanguage, RECIPE_DIR } from './figure-recipe/figure-recipe-templates.js';
7
+ export declare const entities: (typeof import("./figure/figure.js").Figure | typeof import("./figure/figure-history.js").FigureHistory)[];
8
8
  export declare const subscribers: typeof import("./figure/event-subscriber.js").FigureHistoryEntitySubscriber[];
9
9
  export declare const schema: {
10
- resolverClasses: (typeof import("./figure/figure-query.js").FigureQuery | typeof import("./figure/figure-mutation.js").FigureMutation | typeof import("./figure-recipe/figure-recipe-resolver.js").FigureRecipeResolver)[];
10
+ resolverClasses: (typeof import("./figure/figure-query.js").FigureQuery | typeof import("./figure/figure-mutation.js").FigureMutation | typeof import("./figure-recipe/figure-recipe-resolver.js").FigureRecipeTemplateQuery)[];
11
11
  };
@@ -1,22 +1,21 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.schema = exports.subscribers = exports.entities = exports.builtInRecipes = exports.seedBuiltInRecipes = exports.saveRecipe = exports.recipesFor = void 0;
3
+ exports.schema = exports.subscribers = exports.entities = exports.RECIPE_DIR = exports.inLanguage = exports.recipeTemplates = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  // Entity types
6
6
  tslib_1.__exportStar(require("./figure/figure.js"), exports);
7
7
  tslib_1.__exportStar(require("./figure/figure-type.js"), exports);
8
8
  tslib_1.__exportStar(require("./figure/figure-format.js"), exports);
9
9
  tslib_1.__exportStar(require("./figure/figure-history.js"), exports);
10
- tslib_1.__exportStar(require("./figure-recipe/figure-recipe.js"), exports);
11
- var figure_recipe_store_js_1 = require("./figure-recipe/figure-recipe-store.js");
12
- Object.defineProperty(exports, "recipesFor", { enumerable: true, get: function () { return figure_recipe_store_js_1.recipesFor; } });
13
- Object.defineProperty(exports, "saveRecipe", { enumerable: true, get: function () { return figure_recipe_store_js_1.saveRecipe; } });
14
- Object.defineProperty(exports, "seedBuiltInRecipes", { enumerable: true, get: function () { return figure_recipe_store_js_1.seedBuiltInRecipes; } });
15
- Object.defineProperty(exports, "builtInRecipes", { enumerable: true, get: function () { return figure_recipe_store_js_1.builtInRecipes; } });
10
+ tslib_1.__exportStar(require("./figure-recipe/figure-recipe-type.js"), exports);
11
+ var figure_recipe_templates_js_1 = require("./figure-recipe/figure-recipe-templates.js");
12
+ Object.defineProperty(exports, "recipeTemplates", { enumerable: true, get: function () { return figure_recipe_templates_js_1.recipeTemplates; } });
13
+ Object.defineProperty(exports, "inLanguage", { enumerable: true, get: function () { return figure_recipe_templates_js_1.inLanguage; } });
14
+ Object.defineProperty(exports, "RECIPE_DIR", { enumerable: true, get: function () { return figure_recipe_templates_js_1.RECIPE_DIR; } });
16
15
  // Entities, resolvers
17
16
  const index_js_1 = require("./figure/index.js");
18
17
  const index_js_2 = require("./figure-recipe/index.js");
19
- exports.entities = [...index_js_1.entities, ...index_js_2.entities];
18
+ exports.entities = [...index_js_1.entities];
20
19
  /* 판본 스냅샷을 뜨는 구독자. 셸이 이 이름으로 모아 TypeORM 에 등록한다(board-service 와 같다). */
21
20
  exports.subscribers = [...index_js_1.subscribers];
22
21
  /*
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../server/service/index.ts"],"names":[],"mappings":";;;;AAAA,eAAe;AACf,6DAAkC;AAClC,kEAAuC;AACvC,oEAAyC;AACzC,qEAA0C;AAC1C,2EAAgD;AAChD,iFAAmH;AAA1G,oHAAA,UAAU,OAAA;AAAE,oHAAA,UAAU,OAAA;AAAE,4HAAA,kBAAkB,OAAA;AAAE,wHAAA,cAAc,OAAA;AAEnE,sBAAsB;AACtB,gDAI0B;AAC1B,uDAAmG;AAEtF,QAAA,QAAQ,GAAG,CAAC,GAAG,mBAAc,EAAE,GAAG,mBAAc,CAAC,CAAA;AAE9D,sEAAsE;AACzD,QAAA,WAAW,GAAG,CAAC,GAAG,sBAAiB,CAAC,CAAA;AAEjD;;;;;;;;;GASG;AACU,QAAA,MAAM,GAAG;IACpB,eAAe,EAAE,CAAC,GAAG,oBAAe,EAAE,GAAG,oBAAe,CAAC;CAC1D,CAAA","sourcesContent":["// Entity types\nexport * from './figure/figure.js'\nexport * from './figure/figure-type.js'\nexport * from './figure/figure-format.js'\nexport * from './figure/figure-history.js'\nexport * from './figure-recipe/figure-recipe.js'\nexport { recipesFor, saveRecipe, seedBuiltInRecipes, builtInRecipes } from './figure-recipe/figure-recipe-store.js'\n\n// Entities, resolvers\nimport {\n entities as FigureEntities,\n resolvers as FigureResolvers,\n subscribers as FigureSubscribers\n} from './figure/index.js'\nimport { entities as RecipeEntities, resolvers as RecipeResolvers } from './figure-recipe/index.js'\n\nexport const entities = [...FigureEntities, ...RecipeEntities]\n\n/* 판본 스냅샷을 뜨는 구독자. 셸이 이 이름으로 모아 TypeORM 에 등록한다(board-service 와 같다). */\nexport const subscribers = [...FigureSubscribers]\n\n/*\n * The shell reads `schema.resolverClasses` from every module — nothing else.\n *\n * shell/dist-server/schema.js orderedModuleNames.map(dep => loader(dep).schema)\n *\n * This module used to export the classes as a bare `resolvers` array, a name the shell never looks\n * at. The table was migrated and the resolver classes were compiled, and still every query failed\n * with `Cannot query field \"figures\" on type \"Query\"` — the field was not in the schema at all.\n * board-service, ops-master and font-base all export the shape below.\n */\nexport const schema = {\n resolverClasses: [...FigureResolvers, ...RecipeResolvers]\n}\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../server/service/index.ts"],"names":[],"mappings":";;;;AAAA,eAAe;AACf,6DAAkC;AAClC,kEAAuC;AACvC,oEAAyC;AACzC,qEAA0C;AAC1C,gFAAqD;AACrD,yFAAoG;AAA3F,6HAAA,eAAe,OAAA;AAAE,wHAAA,UAAU,OAAA;AAAE,wHAAA,UAAU,OAAA;AAEhD,sBAAsB;AACtB,gDAI0B;AAC1B,uDAAuE;AAE1D,QAAA,QAAQ,GAAG,CAAC,GAAG,mBAAc,CAAC,CAAA;AAE3C,sEAAsE;AACzD,QAAA,WAAW,GAAG,CAAC,GAAG,sBAAiB,CAAC,CAAA;AAEjD;;;;;;;;;GASG;AACU,QAAA,MAAM,GAAG;IACpB,eAAe,EAAE,CAAC,GAAG,oBAAe,EAAE,GAAG,oBAAe,CAAC;CAC1D,CAAA","sourcesContent":["// Entity types\nexport * from './figure/figure.js'\nexport * from './figure/figure-type.js'\nexport * from './figure/figure-format.js'\nexport * from './figure/figure-history.js'\nexport * from './figure-recipe/figure-recipe-type.js'\nexport { recipeTemplates, inLanguage, RECIPE_DIR } from './figure-recipe/figure-recipe-templates.js'\n\n// Entities, resolvers\nimport {\n entities as FigureEntities,\n resolvers as FigureResolvers,\n subscribers as FigureSubscribers\n} from './figure/index.js'\nimport { resolvers as RecipeResolvers } from './figure-recipe/index.js'\n\nexport const entities = [...FigureEntities]\n\n/* 판본 스냅샷을 뜨는 구독자. 셸이 이 이름으로 모아 TypeORM 에 등록한다(board-service 와 같다). */\nexport const subscribers = [...FigureSubscribers]\n\n/*\n * The shell reads `schema.resolverClasses` from every module — nothing else.\n *\n * shell/dist-server/schema.js orderedModuleNames.map(dep => loader(dep).schema)\n *\n * This module used to export the classes as a bare `resolvers` array, a name the shell never looks\n * at. The table was migrated and the resolver classes were compiled, and still every query failed\n * with `Cannot query field \"figures\" on type \"Query\"` — the field was not in the schema at all.\n * board-service, ops-master and font-base all export the shape below.\n */\nexport const schema = {\n resolverClasses: [...FigureResolvers, ...RecipeResolvers]\n}\n"]}