@withwiz/cms-kit 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (327) hide show
  1. package/README.ko.md +146 -0
  2. package/README.md +146 -0
  3. package/dist/chunk-2REYWBQ6.js +32 -0
  4. package/dist/chunk-2REYWBQ6.js.map +1 -0
  5. package/dist/chunk-2SYUEQ6J.mjs +17 -0
  6. package/dist/chunk-2SYUEQ6J.mjs.map +1 -0
  7. package/dist/chunk-3ILEMZAL.mjs +32 -0
  8. package/dist/chunk-3ILEMZAL.mjs.map +1 -0
  9. package/dist/chunk-3Y2TQCHU.mjs +33 -0
  10. package/dist/chunk-3Y2TQCHU.mjs.map +1 -0
  11. package/dist/chunk-4LSFAAZW.js +1 -0
  12. package/dist/chunk-4LSFAAZW.js.map +1 -0
  13. package/dist/chunk-4W3X7OKF.js +100 -0
  14. package/dist/chunk-4W3X7OKF.js.map +1 -0
  15. package/dist/chunk-5JUIMXNF.js +108 -0
  16. package/dist/chunk-5JUIMXNF.js.map +1 -0
  17. package/dist/chunk-5RDMODGV.mjs +34 -0
  18. package/dist/chunk-5RDMODGV.mjs.map +1 -0
  19. package/dist/chunk-6ES4W2DL.mjs +1 -0
  20. package/dist/chunk-6ES4W2DL.mjs.map +1 -0
  21. package/dist/chunk-6EZ3QE5N.js +71 -0
  22. package/dist/chunk-6EZ3QE5N.js.map +1 -0
  23. package/dist/chunk-6JFERXNE.mjs +16 -0
  24. package/dist/chunk-6JFERXNE.mjs.map +1 -0
  25. package/dist/chunk-7SWW3ZIN.js +17 -0
  26. package/dist/chunk-7SWW3ZIN.js.map +1 -0
  27. package/dist/chunk-AXR56AUD.mjs +1 -0
  28. package/dist/chunk-AXR56AUD.mjs.map +1 -0
  29. package/dist/chunk-BE7JDL64.js +154 -0
  30. package/dist/chunk-BE7JDL64.js.map +1 -0
  31. package/dist/chunk-BHN2SRZC.js +51 -0
  32. package/dist/chunk-BHN2SRZC.js.map +1 -0
  33. package/dist/chunk-BJTO5JO5.mjs +11 -0
  34. package/dist/chunk-BJTO5JO5.mjs.map +1 -0
  35. package/dist/chunk-BWKKHCDG.mjs +164 -0
  36. package/dist/chunk-BWKKHCDG.mjs.map +1 -0
  37. package/dist/chunk-C5WFZJM4.js +218 -0
  38. package/dist/chunk-C5WFZJM4.js.map +1 -0
  39. package/dist/chunk-CGVFB23E.mjs +123 -0
  40. package/dist/chunk-CGVFB23E.mjs.map +1 -0
  41. package/dist/chunk-D2VDTRXO.js +1 -0
  42. package/dist/chunk-D2VDTRXO.js.map +1 -0
  43. package/dist/chunk-D75ADHVM.mjs +181 -0
  44. package/dist/chunk-D75ADHVM.mjs.map +1 -0
  45. package/dist/chunk-DCIF4XJL.js +309 -0
  46. package/dist/chunk-DCIF4XJL.js.map +1 -0
  47. package/dist/chunk-DGUM43GV.js +11 -0
  48. package/dist/chunk-DGUM43GV.js.map +1 -0
  49. package/dist/chunk-DSORCGJK.js +44 -0
  50. package/dist/chunk-DSORCGJK.js.map +1 -0
  51. package/dist/chunk-E5YUAOJI.js +27 -0
  52. package/dist/chunk-E5YUAOJI.js.map +1 -0
  53. package/dist/chunk-EG6NVPP2.js +164 -0
  54. package/dist/chunk-EG6NVPP2.js.map +1 -0
  55. package/dist/chunk-EL7YGOTY.js +1 -0
  56. package/dist/chunk-EL7YGOTY.js.map +1 -0
  57. package/dist/chunk-EM4MQEWV.mjs +100 -0
  58. package/dist/chunk-EM4MQEWV.mjs.map +1 -0
  59. package/dist/chunk-FCCCG5Z7.mjs +108 -0
  60. package/dist/chunk-FCCCG5Z7.mjs.map +1 -0
  61. package/dist/chunk-FEPGJ7WN.mjs +218 -0
  62. package/dist/chunk-FEPGJ7WN.mjs.map +1 -0
  63. package/dist/chunk-HXVWBFCN.js +16 -0
  64. package/dist/chunk-HXVWBFCN.js.map +1 -0
  65. package/dist/chunk-I5YMTJV6.mjs +51 -0
  66. package/dist/chunk-I5YMTJV6.mjs.map +1 -0
  67. package/dist/chunk-IMNWW7Q5.mjs +13 -0
  68. package/dist/chunk-IMNWW7Q5.mjs.map +1 -0
  69. package/dist/chunk-J5N76ASB.mjs +114 -0
  70. package/dist/chunk-J5N76ASB.mjs.map +1 -0
  71. package/dist/chunk-JBV26CUG.mjs +51 -0
  72. package/dist/chunk-JBV26CUG.mjs.map +1 -0
  73. package/dist/chunk-JMWXVXK5.js +53 -0
  74. package/dist/chunk-JMWXVXK5.js.map +1 -0
  75. package/dist/chunk-KWKV2GAH.js +60 -0
  76. package/dist/chunk-KWKV2GAH.js.map +1 -0
  77. package/dist/chunk-LDFE33ML.mjs +53 -0
  78. package/dist/chunk-LDFE33ML.mjs.map +1 -0
  79. package/dist/chunk-LF5LOI7K.mjs +174 -0
  80. package/dist/chunk-LF5LOI7K.mjs.map +1 -0
  81. package/dist/chunk-MMKAMH4U.mjs +16 -0
  82. package/dist/chunk-MMKAMH4U.mjs.map +1 -0
  83. package/dist/chunk-MUHFEEYW.js +33 -0
  84. package/dist/chunk-MUHFEEYW.js.map +1 -0
  85. package/dist/chunk-MUQYK5EO.js +1 -0
  86. package/dist/chunk-MUQYK5EO.js.map +1 -0
  87. package/dist/chunk-OBNALAMG.mjs +71 -0
  88. package/dist/chunk-OBNALAMG.mjs.map +1 -0
  89. package/dist/chunk-OLEYNPW6.js +25 -0
  90. package/dist/chunk-OLEYNPW6.js.map +1 -0
  91. package/dist/chunk-OYJRR3G2.js +123 -0
  92. package/dist/chunk-OYJRR3G2.js.map +1 -0
  93. package/dist/chunk-PLVG7AW3.js +16 -0
  94. package/dist/chunk-PLVG7AW3.js.map +1 -0
  95. package/dist/chunk-PYD4CES4.js +1 -0
  96. package/dist/chunk-PYD4CES4.js.map +1 -0
  97. package/dist/chunk-QACD2U6P.mjs +1 -0
  98. package/dist/chunk-QACD2U6P.mjs.map +1 -0
  99. package/dist/chunk-R5GGIVD4.js +10 -0
  100. package/dist/chunk-R5GGIVD4.js.map +1 -0
  101. package/dist/chunk-RCK5WCU5.mjs +25 -0
  102. package/dist/chunk-RCK5WCU5.mjs.map +1 -0
  103. package/dist/chunk-S6UUSM7S.js +13 -0
  104. package/dist/chunk-S6UUSM7S.js.map +1 -0
  105. package/dist/chunk-S7GOXCRM.js +114 -0
  106. package/dist/chunk-S7GOXCRM.js.map +1 -0
  107. package/dist/chunk-SMV5D5LO.mjs +28 -0
  108. package/dist/chunk-SMV5D5LO.mjs.map +1 -0
  109. package/dist/chunk-T7EVFD4G.mjs +60 -0
  110. package/dist/chunk-T7EVFD4G.mjs.map +1 -0
  111. package/dist/chunk-TLH3V4SY.mjs +1 -0
  112. package/dist/chunk-TLH3V4SY.mjs.map +1 -0
  113. package/dist/chunk-TUQ2CVAK.mjs +10 -0
  114. package/dist/chunk-TUQ2CVAK.mjs.map +1 -0
  115. package/dist/chunk-U6PVJKTB.js +51 -0
  116. package/dist/chunk-U6PVJKTB.js.map +1 -0
  117. package/dist/chunk-UXMMEEX4.mjs +27 -0
  118. package/dist/chunk-UXMMEEX4.mjs.map +1 -0
  119. package/dist/chunk-VVXNFFKI.mjs +44 -0
  120. package/dist/chunk-VVXNFFKI.mjs.map +1 -0
  121. package/dist/chunk-VZZ3RU3Q.js +28 -0
  122. package/dist/chunk-VZZ3RU3Q.js.map +1 -0
  123. package/dist/chunk-W4FWIZ2Y.js +34 -0
  124. package/dist/chunk-W4FWIZ2Y.js.map +1 -0
  125. package/dist/chunk-YEQFKOWD.mjs +154 -0
  126. package/dist/chunk-YEQFKOWD.mjs.map +1 -0
  127. package/dist/chunk-YPBKY4KY.mjs +1 -0
  128. package/dist/chunk-YPBKY4KY.mjs.map +1 -0
  129. package/dist/chunk-YY7K5CXX.js +174 -0
  130. package/dist/chunk-YY7K5CXX.js.map +1 -0
  131. package/dist/chunk-ZFORODOX.js +181 -0
  132. package/dist/chunk-ZFORODOX.js.map +1 -0
  133. package/dist/chunk-ZULSFSDG.mjs +309 -0
  134. package/dist/chunk-ZULSFSDG.mjs.map +1 -0
  135. package/dist/components/AdminManagerBase.d.mts +26 -0
  136. package/dist/components/AdminManagerBase.d.ts +26 -0
  137. package/dist/components/AdminManagerBase.js +13 -0
  138. package/dist/components/AdminManagerBase.js.map +1 -0
  139. package/dist/components/AdminManagerBase.mjs +12 -0
  140. package/dist/components/AdminManagerBase.mjs.map +1 -0
  141. package/dist/components/AdminManagerConfig.d.mts +83 -0
  142. package/dist/components/AdminManagerConfig.d.ts +83 -0
  143. package/dist/components/AdminManagerConfig.js +2 -0
  144. package/dist/components/AdminManagerConfig.js.map +1 -0
  145. package/dist/components/AdminManagerConfig.mjs +2 -0
  146. package/dist/components/AdminManagerConfig.mjs.map +1 -0
  147. package/dist/components/AdminShell.d.mts +30 -0
  148. package/dist/components/AdminShell.d.ts +30 -0
  149. package/dist/components/AdminShell.js +12 -0
  150. package/dist/components/AdminShell.js.map +1 -0
  151. package/dist/components/AdminShell.mjs +11 -0
  152. package/dist/components/AdminShell.mjs.map +1 -0
  153. package/dist/components/JsonLd.d.mts +32 -0
  154. package/dist/components/JsonLd.d.ts +32 -0
  155. package/dist/components/JsonLd.js +11 -0
  156. package/dist/components/JsonLd.js.map +1 -0
  157. package/dist/components/JsonLd.mjs +11 -0
  158. package/dist/components/JsonLd.mjs.map +1 -0
  159. package/dist/components/ResizableImage.d.mts +18 -0
  160. package/dist/components/ResizableImage.d.ts +18 -0
  161. package/dist/components/ResizableImage.js +10 -0
  162. package/dist/components/ResizableImage.js.map +1 -0
  163. package/dist/components/ResizableImage.mjs +9 -0
  164. package/dist/components/ResizableImage.mjs.map +1 -0
  165. package/dist/components/ToggleSwitch.d.mts +11 -0
  166. package/dist/components/ToggleSwitch.d.ts +11 -0
  167. package/dist/components/ToggleSwitch.js +10 -0
  168. package/dist/components/ToggleSwitch.js.map +1 -0
  169. package/dist/components/ToggleSwitch.mjs +9 -0
  170. package/dist/components/ToggleSwitch.mjs.map +1 -0
  171. package/dist/components/image-drop-zone.css +115 -0
  172. package/dist/components/index.d.mts +26 -0
  173. package/dist/components/index.d.ts +26 -0
  174. package/dist/components/index.js +35 -0
  175. package/dist/components/index.js.map +1 -0
  176. package/dist/components/index.mjs +35 -0
  177. package/dist/components/index.mjs.map +1 -0
  178. package/dist/components/toggle-switch.css +55 -0
  179. package/dist/hooks/index.d.mts +45 -0
  180. package/dist/hooks/index.d.ts +45 -0
  181. package/dist/hooks/index.js +25 -0
  182. package/dist/hooks/index.js.map +1 -0
  183. package/dist/hooks/index.mjs +25 -0
  184. package/dist/hooks/index.mjs.map +1 -0
  185. package/dist/hooks/useImageDropZone.d.mts +37 -0
  186. package/dist/hooks/useImageDropZone.d.ts +37 -0
  187. package/dist/hooks/useImageDropZone.js +13 -0
  188. package/dist/hooks/useImageDropZone.js.map +1 -0
  189. package/dist/hooks/useImageDropZone.mjs +12 -0
  190. package/dist/hooks/useImageDropZone.mjs.map +1 -0
  191. package/dist/hooks/useScrollReveal.d.mts +6 -0
  192. package/dist/hooks/useScrollReveal.d.ts +6 -0
  193. package/dist/hooks/useScrollReveal.js +12 -0
  194. package/dist/hooks/useScrollReveal.js.map +1 -0
  195. package/dist/hooks/useScrollReveal.mjs +11 -0
  196. package/dist/hooks/useScrollReveal.mjs.map +1 -0
  197. package/dist/image-drop-zone.css +115 -0
  198. package/dist/index-00a40-FQ.d.mts +136 -0
  199. package/dist/index-00a40-FQ.d.ts +136 -0
  200. package/dist/index.d.mts +35 -0
  201. package/dist/index.d.ts +35 -0
  202. package/dist/index.js +169 -0
  203. package/dist/index.js.map +1 -0
  204. package/dist/index.mjs +169 -0
  205. package/dist/index.mjs.map +1 -0
  206. package/dist/infrastructure/index.d.mts +4 -0
  207. package/dist/infrastructure/index.d.ts +4 -0
  208. package/dist/infrastructure/index.js +20 -0
  209. package/dist/infrastructure/index.js.map +1 -0
  210. package/dist/infrastructure/index.mjs +20 -0
  211. package/dist/infrastructure/index.mjs.map +1 -0
  212. package/dist/infrastructure/middleware/index.d.mts +3 -0
  213. package/dist/infrastructure/middleware/index.d.ts +3 -0
  214. package/dist/infrastructure/middleware/index.js +15 -0
  215. package/dist/infrastructure/middleware/index.js.map +1 -0
  216. package/dist/infrastructure/middleware/index.mjs +15 -0
  217. package/dist/infrastructure/middleware/index.mjs.map +1 -0
  218. package/dist/infrastructure/middleware/wrappers.d.mts +10 -0
  219. package/dist/infrastructure/middleware/wrappers.d.ts +10 -0
  220. package/dist/infrastructure/middleware/wrappers.js +15 -0
  221. package/dist/infrastructure/middleware/wrappers.js.map +1 -0
  222. package/dist/infrastructure/middleware/wrappers.mjs +15 -0
  223. package/dist/infrastructure/middleware/wrappers.mjs.map +1 -0
  224. package/dist/infrastructure/prisma.d.mts +5 -0
  225. package/dist/infrastructure/prisma.d.ts +5 -0
  226. package/dist/infrastructure/prisma.js +12 -0
  227. package/dist/infrastructure/prisma.js.map +1 -0
  228. package/dist/infrastructure/prisma.mjs +12 -0
  229. package/dist/infrastructure/prisma.mjs.map +1 -0
  230. package/dist/services/index.d.mts +22 -0
  231. package/dist/services/index.d.ts +22 -0
  232. package/dist/services/index.js +36 -0
  233. package/dist/services/index.js.map +1 -0
  234. package/dist/services/index.mjs +36 -0
  235. package/dist/services/index.mjs.map +1 -0
  236. package/dist/toggle-switch.css +55 -0
  237. package/dist/types/common.d.mts +14 -0
  238. package/dist/types/common.d.ts +14 -0
  239. package/dist/types/common.js +8 -0
  240. package/dist/types/common.js.map +1 -0
  241. package/dist/types/common.mjs +8 -0
  242. package/dist/types/common.mjs.map +1 -0
  243. package/dist/types/index.d.mts +1 -0
  244. package/dist/types/index.d.ts +1 -0
  245. package/dist/types/index.js +9 -0
  246. package/dist/types/index.js.map +1 -0
  247. package/dist/types/index.mjs +9 -0
  248. package/dist/types/index.mjs.map +1 -0
  249. package/dist/utils/admin-fetch.d.mts +11 -0
  250. package/dist/utils/admin-fetch.d.ts +11 -0
  251. package/dist/utils/admin-fetch.js +13 -0
  252. package/dist/utils/admin-fetch.js.map +1 -0
  253. package/dist/utils/admin-fetch.mjs +13 -0
  254. package/dist/utils/admin-fetch.mjs.map +1 -0
  255. package/dist/utils/api-helpers.d.mts +20 -0
  256. package/dist/utils/api-helpers.d.ts +20 -0
  257. package/dist/utils/api-helpers.js +12 -0
  258. package/dist/utils/api-helpers.js.map +1 -0
  259. package/dist/utils/api-helpers.mjs +12 -0
  260. package/dist/utils/api-helpers.mjs.map +1 -0
  261. package/dist/utils/date.d.mts +5 -0
  262. package/dist/utils/date.d.ts +5 -0
  263. package/dist/utils/date.js +12 -0
  264. package/dist/utils/date.js.map +1 -0
  265. package/dist/utils/date.mjs +12 -0
  266. package/dist/utils/date.mjs.map +1 -0
  267. package/dist/utils/html-sanitizer.d.mts +56 -0
  268. package/dist/utils/html-sanitizer.d.ts +56 -0
  269. package/dist/utils/html-sanitizer.js +11 -0
  270. package/dist/utils/html-sanitizer.js.map +1 -0
  271. package/dist/utils/html-sanitizer.mjs +11 -0
  272. package/dist/utils/html-sanitizer.mjs.map +1 -0
  273. package/dist/utils/image-variant-utils.d.mts +14 -0
  274. package/dist/utils/image-variant-utils.d.ts +14 -0
  275. package/dist/utils/image-variant-utils.js +10 -0
  276. package/dist/utils/image-variant-utils.js.map +1 -0
  277. package/dist/utils/image-variant-utils.mjs +10 -0
  278. package/dist/utils/image-variant-utils.mjs.map +1 -0
  279. package/dist/utils/image-variants.d.mts +13 -0
  280. package/dist/utils/image-variants.d.ts +13 -0
  281. package/dist/utils/image-variants.js +12 -0
  282. package/dist/utils/image-variants.js.map +1 -0
  283. package/dist/utils/image-variants.mjs +12 -0
  284. package/dist/utils/image-variants.mjs.map +1 -0
  285. package/dist/utils/index.d.mts +88 -0
  286. package/dist/utils/index.d.ts +88 -0
  287. package/dist/utils/index.js +92 -0
  288. package/dist/utils/index.js.map +1 -0
  289. package/dist/utils/index.mjs +92 -0
  290. package/dist/utils/index.mjs.map +1 -0
  291. package/dist/utils/jwt.d.mts +19 -0
  292. package/dist/utils/jwt.d.ts +19 -0
  293. package/dist/utils/jwt.js +9 -0
  294. package/dist/utils/jwt.js.map +1 -0
  295. package/dist/utils/jwt.mjs +9 -0
  296. package/dist/utils/jwt.mjs.map +1 -0
  297. package/dist/utils/r2-helpers.d.mts +9 -0
  298. package/dist/utils/r2-helpers.d.ts +9 -0
  299. package/dist/utils/r2-helpers.js +20 -0
  300. package/dist/utils/r2-helpers.js.map +1 -0
  301. package/dist/utils/r2-helpers.mjs +20 -0
  302. package/dist/utils/r2-helpers.mjs.map +1 -0
  303. package/dist/utils/r2-storage.d.mts +38 -0
  304. package/dist/utils/r2-storage.d.ts +38 -0
  305. package/dist/utils/r2-storage.js +17 -0
  306. package/dist/utils/r2-storage.js.map +1 -0
  307. package/dist/utils/r2-storage.mjs +17 -0
  308. package/dist/utils/r2-storage.mjs.map +1 -0
  309. package/dist/utils/route-params.d.mts +3 -0
  310. package/dist/utils/route-params.d.ts +3 -0
  311. package/dist/utils/route-params.js +8 -0
  312. package/dist/utils/route-params.js.map +1 -0
  313. package/dist/utils/route-params.mjs +8 -0
  314. package/dist/utils/route-params.mjs.map +1 -0
  315. package/dist/validators/index.d.mts +2 -0
  316. package/dist/validators/index.d.ts +2 -0
  317. package/dist/validators/index.js +11 -0
  318. package/dist/validators/index.js.map +1 -0
  319. package/dist/validators/index.mjs +11 -0
  320. package/dist/validators/index.mjs.map +1 -0
  321. package/dist/validators/shared.d.mts +6 -0
  322. package/dist/validators/shared.d.ts +6 -0
  323. package/dist/validators/shared.js +10 -0
  324. package/dist/validators/shared.js.map +1 -0
  325. package/dist/validators/shared.mjs +10 -0
  326. package/dist/validators/shared.mjs.map +1 -0
  327. package/package.json +257 -0
@@ -0,0 +1,154 @@
1
+ import {
2
+ namespacedError,
3
+ resolveR2CredentialsConfig,
4
+ resolveR2PublicUrl
5
+ } from "./chunk-BWKKHCDG.mjs";
6
+
7
+ // src/utils/r2-storage.ts
8
+ import { S3Client, PutObjectCommand, DeleteObjectCommand } from "@aws-sdk/client-s3";
9
+ import { logError } from "@withwiz/toolkit/core/logger/logger";
10
+ function sanitizeStorageKey(key) {
11
+ if (typeof key !== "string" || key.length === 0) {
12
+ throw namespacedError(
13
+ `storage key is empty or not a string; refusing to send to storage.`
14
+ );
15
+ }
16
+ if (key.startsWith("/")) {
17
+ throw namespacedError(
18
+ `storage key "${key}" is absolute / leading-slash; it could escape the intended namespace. Use a relative key (e.g. "news/x.jpg").`
19
+ );
20
+ }
21
+ if (key.includes("\\")) {
22
+ throw namespacedError(
23
+ `storage key "${key}" contains a backslash; refusing (namespace-escape risk).`
24
+ );
25
+ }
26
+ for (let i = 0; i < key.length; i++) {
27
+ if (key.charCodeAt(i) < 32) {
28
+ throw namespacedError(
29
+ `storage key contains control characters; refusing (namespace-escape risk).`
30
+ );
31
+ }
32
+ }
33
+ const segments = key.split("/");
34
+ if (segments.some((seg) => seg === ".." || seg === ".")) {
35
+ throw namespacedError(
36
+ `storage key "${key}" contains a path-traversal segment ("." / ".."); it could escape the intended namespace.`
37
+ );
38
+ }
39
+ return key;
40
+ }
41
+ var client = null;
42
+ var clientSnapshot = null;
43
+ function requireR2Credentials() {
44
+ const c = resolveR2CredentialsConfig();
45
+ const missing = [];
46
+ if (!c.accountId) missing.push("accountId");
47
+ if (!c.accessKeyId) missing.push("accessKeyId");
48
+ if (!c.secretAccessKey) missing.push("secretAccessKey");
49
+ if (!c.bucketName) missing.push("bucketName");
50
+ if (missing.length > 0) {
51
+ throw namespacedError(
52
+ `R2 credentials are missing: ${missing.join(", ")}. Inject \`setCmsConfig({ storage: { r2: { accountId, accessKeyId, secretAccessKey, bucketName } } })\` or set the legacy R2_ACCOUNT_ID / R2_ACCESS_KEY_ID / R2_SECRET_ACCESS_KEY / R2_BUCKET_NAME environment variables. There is no safe default for storage credentials.`
53
+ );
54
+ }
55
+ return c;
56
+ }
57
+ function getClient() {
58
+ const c = requireR2Credentials();
59
+ const snapshot = `${c.accountId}|${c.accessKeyId}`;
60
+ if (!client || clientSnapshot !== snapshot) {
61
+ client = new S3Client({
62
+ region: "auto",
63
+ endpoint: `https://${c.accountId}.r2.cloudflarestorage.com`,
64
+ credentials: {
65
+ accessKeyId: c.accessKeyId,
66
+ secretAccessKey: c.secretAccessKey
67
+ }
68
+ });
69
+ clientSnapshot = snapshot;
70
+ }
71
+ return client;
72
+ }
73
+ function isR2Enabled() {
74
+ const c = resolveR2CredentialsConfig();
75
+ return !!(c.accountId && c.accessKeyId && c.secretAccessKey && c.bucketName);
76
+ }
77
+ function buildPublicUrl(key, bucket) {
78
+ const base = resolveR2PublicUrl();
79
+ return base ? `${base}/${key}` : `https://${bucket}.r2.dev/${key}`;
80
+ }
81
+ async function uploadToR2(key, buffer, contentType) {
82
+ const safeKey = sanitizeStorageKey(key);
83
+ const s3 = getClient();
84
+ const { bucketName: bucket } = requireR2Credentials();
85
+ await s3.send(
86
+ new PutObjectCommand({
87
+ Bucket: bucket,
88
+ Key: safeKey,
89
+ Body: buffer,
90
+ ContentType: contentType
91
+ })
92
+ );
93
+ return { url: buildPublicUrl(safeKey, bucket), key: safeKey, size: buffer.length };
94
+ }
95
+ async function uploadImageWithVariants(originalKey, originalBuffer, originalContentType) {
96
+ const { generateImageVariants } = await import("./utils/image-variants.mjs");
97
+ const original = await uploadToR2(originalKey, originalBuffer, originalContentType);
98
+ const baseKey = originalKey.replace(/\.[^.]+$/, "");
99
+ const variants = {};
100
+ const variantKeys = [];
101
+ try {
102
+ const imageVariants = await generateImageVariants(originalBuffer, baseKey, originalContentType);
103
+ const { bucketName: bucket } = requireR2Credentials();
104
+ await Promise.all(
105
+ imageVariants.map(async (v) => {
106
+ try {
107
+ await uploadToR2(v.key, v.buffer, v.contentType);
108
+ variants[v.size] = buildPublicUrl(v.key, bucket);
109
+ variantKeys.push(v.key);
110
+ } catch (err) {
111
+ logError(`[image-variant] Failed to upload variant ${v.key}`, {
112
+ error: err instanceof Error ? err.message : err,
113
+ originalKey,
114
+ size: v.size
115
+ });
116
+ }
117
+ })
118
+ );
119
+ if (variantKeys.length === 0) {
120
+ logError(`[image-variant] No variants generated for ${originalKey}`);
121
+ }
122
+ } catch (err) {
123
+ logError(`[image-variant] Failed to generate variants for ${originalKey}`, {
124
+ error: err instanceof Error ? err.message : err
125
+ });
126
+ }
127
+ return {
128
+ url: original.url,
129
+ key: original.key,
130
+ size: original.size,
131
+ variants,
132
+ variantKeys
133
+ };
134
+ }
135
+ async function deleteFromR2(key) {
136
+ const safeKey = sanitizeStorageKey(key);
137
+ const s3 = getClient();
138
+ const { bucketName: bucket } = requireR2Credentials();
139
+ await s3.send(
140
+ new DeleteObjectCommand({
141
+ Bucket: bucket,
142
+ Key: safeKey
143
+ })
144
+ );
145
+ }
146
+
147
+ export {
148
+ sanitizeStorageKey,
149
+ isR2Enabled,
150
+ uploadToR2,
151
+ uploadImageWithVariants,
152
+ deleteFromR2
153
+ };
154
+ //# sourceMappingURL=chunk-YEQFKOWD.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/r2-storage.ts"],"sourcesContent":["import { S3Client, PutObjectCommand, DeleteObjectCommand } from '@aws-sdk/client-s3';\nimport { logError, logInfo } from '@withwiz/toolkit/core/logger/logger';\nimport {\n namespacedError,\n resolveR2CredentialsConfig,\n resolveR2PublicUrl,\n} from '../config';\n\n/**\n * storage object key 를 검증/정규화한다 (spec.md §4.6 / Sprint 1 S5).\n *\n * caller/user 파생 key 가 의도된 namespace 를 탈출(path traversal /\n * absolute / prefix-escape)하지 못하도록 한다. 위험 key 는 `@withwiz/cms-kit:`\n * 네임스페이스 에러로 즉시 거부한다. 양성(benign) key 는 *바이트 동일하게*\n * 통과시킨다 (blanket reject 아님 — 정규화로 인한 mangling 없음).\n *\n * 거부 규칙:\n * - 빈 값 / 비문자열\n * - 선행 `/` (절대/leading-slash: `/absolute`, `/news/x.jpg`)\n * - 백슬래시 포함 (`\\` — 윈도우식 절대/우회)\n * - 제어문자 (codepoint < 0x20)\n * - `.` / `..` path 세그먼트 (`../`, `a/../../b`, `news/../../secret`)\n */\nexport function sanitizeStorageKey(key: string): string {\n if (typeof key !== 'string' || key.length === 0) {\n throw namespacedError(\n `storage key is empty or not a string; refusing to send to storage.`,\n );\n }\n if (key.startsWith('/')) {\n throw namespacedError(\n `storage key \"${key}\" is absolute / leading-slash; it could escape the ` +\n `intended namespace. Use a relative key (e.g. \"news/x.jpg\").`,\n );\n }\n if (key.includes('\\\\')) {\n throw namespacedError(\n `storage key \"${key}\" contains a backslash; refusing (namespace-escape risk).`,\n );\n }\n for (let i = 0; i < key.length; i++) {\n if (key.charCodeAt(i) < 0x20) {\n throw namespacedError(\n `storage key contains control characters; refusing (namespace-escape risk).`,\n );\n }\n }\n const segments = key.split('/');\n if (segments.some((seg) => seg === '..' || seg === '.')) {\n throw namespacedError(\n `storage key \"${key}\" contains a path-traversal segment (\".\" / \"..\"); ` +\n `it could escape the intended namespace.`,\n );\n }\n // benign key: byte-identical passthrough.\n return key;\n}\n\nlet client: S3Client | null = null;\n// 자격증명/엔드포인트가 바뀌면 캐시된 S3Client 를 무효화하기 위한 스냅샷.\n// 같은 프로세스 내에서 `setCmsConfig` 가 재호출되거나 R2_* env 가 바뀐 경우\n// 다음 호출에서 새 클라이언트가 만들어진다.\nlet clientSnapshot: string | null = null;\n\nfunction requireR2Credentials(): {\n accountId: string;\n accessKeyId: string;\n secretAccessKey: string;\n bucketName: string;\n} {\n const c = resolveR2CredentialsConfig();\n const missing: string[] = [];\n if (!c.accountId) missing.push('accountId');\n if (!c.accessKeyId) missing.push('accessKeyId');\n if (!c.secretAccessKey) missing.push('secretAccessKey');\n if (!c.bucketName) missing.push('bucketName');\n if (missing.length > 0) {\n throw namespacedError(\n `R2 credentials are missing: ${missing.join(', ')}. ` +\n 'Inject `setCmsConfig({ storage: { r2: { accountId, accessKeyId, secretAccessKey, bucketName } } })` ' +\n 'or set the legacy R2_ACCOUNT_ID / R2_ACCESS_KEY_ID / R2_SECRET_ACCESS_KEY / R2_BUCKET_NAME ' +\n 'environment variables. There is no safe default for storage credentials.',\n );\n }\n return c as {\n accountId: string;\n accessKeyId: string;\n secretAccessKey: string;\n bucketName: string;\n };\n}\n\nfunction getClient(): S3Client {\n const c = requireR2Credentials();\n const snapshot = `${c.accountId}|${c.accessKeyId}`;\n if (!client || clientSnapshot !== snapshot) {\n client = new S3Client({\n region: 'auto',\n endpoint: `https://${c.accountId}.r2.cloudflarestorage.com`,\n credentials: {\n accessKeyId: c.accessKeyId,\n secretAccessKey: c.secretAccessKey,\n },\n });\n clientSnapshot = snapshot;\n }\n return client;\n}\n\nexport function isR2Enabled(): boolean {\n const c = resolveR2CredentialsConfig();\n return !!(c.accountId && c.accessKeyId && c.secretAccessKey && c.bucketName);\n}\n\nfunction buildPublicUrl(key: string, bucket: string): string {\n const base = resolveR2PublicUrl();\n return base ? `${base}/${key}` : `https://${bucket}.r2.dev/${key}`;\n}\n\nexport async function uploadToR2(\n key: string,\n buffer: Buffer,\n contentType: string,\n): Promise<{ url: string; key: string; size: number }> {\n const safeKey = sanitizeStorageKey(key);\n const s3 = getClient();\n const { bucketName: bucket } = requireR2Credentials();\n\n await s3.send(\n new PutObjectCommand({\n Bucket: bucket,\n Key: safeKey,\n Body: buffer,\n ContentType: contentType,\n }),\n );\n\n return { url: buildPublicUrl(safeKey, bucket), key: safeKey, size: buffer.length };\n}\n\nexport interface ImageVariantUrls {\n lg?: string;\n md?: string;\n sm?: string;\n thumb?: string;\n}\n\nexport async function uploadImageWithVariants(\n originalKey: string,\n originalBuffer: Buffer,\n originalContentType: string,\n): Promise<{\n url: string;\n key: string;\n size: number;\n variants: ImageVariantUrls;\n variantKeys: string[];\n}> {\n const { generateImageVariants } = await import('./image-variants');\n\n const original = await uploadToR2(originalKey, originalBuffer, originalContentType);\n\n const baseKey = originalKey.replace(/\\.[^.]+$/, '');\n\n const variants: ImageVariantUrls = {};\n const variantKeys: string[] = [];\n\n try {\n const imageVariants = await generateImageVariants(originalBuffer, baseKey, originalContentType);\n const { bucketName: bucket } = requireR2Credentials();\n\n await Promise.all(\n imageVariants.map(async (v) => {\n try {\n await uploadToR2(v.key, v.buffer, v.contentType);\n variants[v.size] = buildPublicUrl(v.key, bucket);\n variantKeys.push(v.key);\n } catch (err) {\n logError(`[image-variant] Failed to upload variant ${v.key}`, {\n error: err instanceof Error ? err.message : err,\n originalKey,\n size: v.size,\n });\n }\n }),\n );\n\n if (variantKeys.length === 0) {\n logError(`[image-variant] No variants generated for ${originalKey}`);\n }\n } catch (err) {\n logError(`[image-variant] Failed to generate variants for ${originalKey}`, {\n error: err instanceof Error ? err.message : err,\n });\n }\n\n return {\n url: original.url,\n key: original.key,\n size: original.size,\n variants,\n variantKeys,\n };\n}\n\nexport async function deleteFromR2(key: string): Promise<void> {\n const safeKey = sanitizeStorageKey(key);\n const s3 = getClient();\n const { bucketName: bucket } = requireR2Credentials();\n await s3.send(\n new DeleteObjectCommand({\n Bucket: bucket,\n Key: safeKey,\n }),\n );\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1 @@
1
+ //# sourceMappingURL=chunk-YPBKY4KY.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,174 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
2
+
3
+
4
+ var _chunkS7GOXCRMjs = require('./chunk-S7GOXCRM.js');
5
+
6
+
7
+ var _chunkKWKV2GAHjs = require('./chunk-KWKV2GAH.js');
8
+
9
+
10
+ var _chunkEG6NVPP2js = require('./chunk-EG6NVPP2.js');
11
+
12
+ // src/hooks/useImageDropZone.ts
13
+ var _react = require('react');
14
+ var ALLOWED_TYPES = ["image/jpeg", "image/png", "image/webp", "image/gif"];
15
+ var MAX_UPLOAD_SIZE = 5 * 1024 * 1024;
16
+ function validateFile(file) {
17
+ if (!ALLOWED_TYPES.includes(file.type)) {
18
+ const allowed = ALLOWED_TYPES.map((t) => t.split("/")[1]).join(", ");
19
+ return `\uC9C0\uC6D0\uD558\uC9C0 \uC54A\uB294 \uD30C\uC77C \uD615\uC2DD\uC785\uB2C8\uB2E4. (\uD5C8\uC6A9: ${allowed})`;
20
+ }
21
+ const sizeError = _chunkS7GOXCRMjs.validateImageSize.call(void 0, file);
22
+ if (sizeError) return sizeError;
23
+ return null;
24
+ }
25
+ async function uploadSingleFile(file) {
26
+ const fd = new FormData();
27
+ fd.append("file", file);
28
+ const { uploadEndpoint } = _chunkEG6NVPP2js.resolveRouteConfig.call(void 0, );
29
+ const res = await _chunkKWKV2GAHjs.adminFetch.call(void 0, uploadEndpoint, {
30
+ method: "POST",
31
+ body: fd
32
+ });
33
+ const json = await res.json();
34
+ if (!json.success) {
35
+ throw new Error(_optionalChain([json, 'access', _ => _.error, 'optionalAccess', _2 => _2.message]) || "\uC5C5\uB85C\uB4DC \uC2E4\uD328");
36
+ }
37
+ return json.data;
38
+ }
39
+ function useImageDropZone(options) {
40
+ const {
41
+ multiple = false,
42
+ maxFiles,
43
+ onUpload,
44
+ onKeyTracked,
45
+ disabled = false
46
+ } = options;
47
+ const [isDragOver, setIsDragOver] = _react.useState.call(void 0, false);
48
+ const [isUploading, setIsUploading] = _react.useState.call(void 0, false);
49
+ const [isResizing, setIsResizing] = _react.useState.call(void 0, false);
50
+ const [error, setError] = _react.useState.call(void 0, null);
51
+ const dragCounterRef = _react.useRef.call(void 0, 0);
52
+ const processFiles = _react.useCallback.call(void 0,
53
+ async (files) => {
54
+ if (disabled || files.length === 0) return;
55
+ let toProcess = multiple ? files : [files[0]];
56
+ if (maxFiles && toProcess.length > maxFiles) {
57
+ toProcess = toProcess.slice(0, maxFiles);
58
+ setError(`\uCD5C\uB300 ${maxFiles}\uAC1C\uAE4C\uC9C0 \uC5C5\uB85C\uB4DC\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4.`);
59
+ }
60
+ for (const file of toProcess) {
61
+ const validationError = validateFile(file);
62
+ if (validationError) {
63
+ setError(validationError);
64
+ return;
65
+ }
66
+ }
67
+ setError(null);
68
+ setIsResizing(true);
69
+ let resizedFiles;
70
+ try {
71
+ resizedFiles = [];
72
+ for (const file of toProcess) {
73
+ const result = await _chunkS7GOXCRMjs.resizeImageIfNeeded.call(void 0, file);
74
+ if (result.wasResized && process.env.NODE_ENV === "development") {
75
+ console.log(
76
+ `[image-optimize] ${(result.originalSize / 1024 / 1024).toFixed(1)}MB \u2192 ${(result.newSize / 1024 / 1024).toFixed(1)}MB`
77
+ );
78
+ }
79
+ if (result.file.size > MAX_UPLOAD_SIZE) {
80
+ setError(
81
+ `\uC774\uBBF8\uC9C0 \uCD5C\uC801\uD654 \uD6C4\uC5D0\uB3C4 5MB\uB97C \uCD08\uACFC\uD569\uB2C8\uB2E4. (${(result.file.size / 1024 / 1024).toFixed(1)}MB) \uB354 \uC791\uC740 \uC774\uBBF8\uC9C0\uB97C \uC0AC\uC6A9\uD574 \uC8FC\uC138\uC694.`
82
+ );
83
+ setIsResizing(false);
84
+ return;
85
+ }
86
+ resizedFiles.push(result.file);
87
+ }
88
+ } catch (err) {
89
+ const msg = err instanceof Error ? err.message : "\uC774\uBBF8\uC9C0 \uCD5C\uC801\uD654 \uC911 \uC624\uB958 \uBC1C\uC0DD";
90
+ setError(msg);
91
+ setIsResizing(false);
92
+ return;
93
+ }
94
+ setIsResizing(false);
95
+ setIsUploading(true);
96
+ try {
97
+ for (const file of resizedFiles) {
98
+ const result = await uploadSingleFile(file);
99
+ onUpload(result);
100
+ if (result.key) _optionalChain([onKeyTracked, 'optionalCall', _3 => _3(result.key)]);
101
+ if (result.variantKeys) {
102
+ for (const vk of result.variantKeys) _optionalChain([onKeyTracked, 'optionalCall', _4 => _4(vk)]);
103
+ }
104
+ }
105
+ } catch (err) {
106
+ const msg = err instanceof Error ? err.message : "\uC5C5\uB85C\uB4DC \uC911 \uC624\uB958 \uBC1C\uC0DD";
107
+ setError(msg);
108
+ } finally {
109
+ setIsUploading(false);
110
+ }
111
+ },
112
+ [multiple, maxFiles, onUpload, onKeyTracked, disabled]
113
+ );
114
+ const onDragEnter = _react.useCallback.call(void 0, (e) => {
115
+ e.preventDefault();
116
+ e.stopPropagation();
117
+ dragCounterRef.current++;
118
+ if (_optionalChain([e, 'access', _5 => _5.dataTransfer, 'optionalAccess', _6 => _6.types, 'access', _7 => _7.includes, 'call', _8 => _8("Files")])) {
119
+ setIsDragOver(true);
120
+ }
121
+ }, []);
122
+ const onDragOver = _react.useCallback.call(void 0, (e) => {
123
+ e.preventDefault();
124
+ e.stopPropagation();
125
+ }, []);
126
+ const onDragLeave = _react.useCallback.call(void 0, (e) => {
127
+ e.preventDefault();
128
+ e.stopPropagation();
129
+ dragCounterRef.current--;
130
+ if (dragCounterRef.current === 0) {
131
+ setIsDragOver(false);
132
+ }
133
+ }, []);
134
+ const onDrop = _react.useCallback.call(void 0,
135
+ (e) => {
136
+ e.preventDefault();
137
+ e.stopPropagation();
138
+ dragCounterRef.current = 0;
139
+ setIsDragOver(false);
140
+ if (disabled) return;
141
+ const files = Array.from(_optionalChain([e, 'access', _9 => _9.dataTransfer, 'optionalAccess', _10 => _10.files]) || []).filter(
142
+ (f) => f.type.startsWith("image/")
143
+ );
144
+ if (files.length === 0) {
145
+ setError("\uC774\uBBF8\uC9C0 \uD30C\uC77C\uB9CC \uC5C5\uB85C\uB4DC\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4.");
146
+ return;
147
+ }
148
+ processFiles(files);
149
+ },
150
+ [processFiles, disabled]
151
+ );
152
+ const handleFileInput = _react.useCallback.call(void 0,
153
+ async (fileList) => {
154
+ const files = Array.from(fileList || []);
155
+ if (files.length > 0) {
156
+ await processFiles(files);
157
+ }
158
+ },
159
+ [processFiles]
160
+ );
161
+ return {
162
+ isDragOver,
163
+ isUploading,
164
+ isResizing,
165
+ error,
166
+ dragHandlers: { onDragEnter, onDragOver, onDragLeave, onDrop },
167
+ handleFileInput
168
+ };
169
+ }
170
+
171
+
172
+
173
+ exports.useImageDropZone = useImageDropZone;
174
+ //# sourceMappingURL=chunk-YY7K5CXX.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/Users/uni4love/project/workspace/211-withwiz/node-packages/withwiz-cms-kit/dist/chunk-YY7K5CXX.js","../src/hooks/useImageDropZone.ts"],"names":[],"mappings":"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","file":"/Users/uni4love/project/workspace/211-withwiz/node-packages/withwiz-cms-kit/dist/chunk-YY7K5CXX.js","sourcesContent":[null,"\"use client\";\n\nimport { useState, useCallback, useRef, type DragEvent } from \"react\";\nimport { adminFetch } from \"../utils/admin-fetch\";\nimport {\n resizeImageIfNeeded,\n validateImageSize,\n} from \"../utils/image-resize\";\nimport { resolveRouteConfig } from \"../config\";\n\nconst ALLOWED_TYPES = [\"image/jpeg\", \"image/png\", \"image/webp\", \"image/gif\"];\nconst MAX_UPLOAD_SIZE = 5 * 1024 * 1024; // 5MB\n\ninterface ImageVariantUrls {\n lg?: string;\n md?: string;\n sm?: string;\n thumb?: string;\n}\n\ninterface UploadResult {\n url: string;\n key: string;\n size: number;\n variants?: ImageVariantUrls;\n variantKeys?: string[];\n}\n\ninterface UseImageDropZoneOptions {\n multiple?: boolean;\n maxFiles?: number;\n onUpload: (result: UploadResult) => void;\n onKeyTracked?: (key: string) => void;\n disabled?: boolean;\n}\n\nfunction validateFile(file: File): string | null {\n if (!ALLOWED_TYPES.includes(file.type)) {\n const allowed = ALLOWED_TYPES.map((t) => t.split(\"/\")[1]).join(\", \");\n return `지원하지 않는 파일 형식입니다. (허용: ${allowed})`;\n }\n const sizeError = validateImageSize(file);\n if (sizeError) return sizeError;\n return null;\n}\n\nasync function uploadSingleFile(file: File): Promise<UploadResult> {\n const fd = new FormData();\n fd.append(\"file\", file);\n // upload 엔드포인트는 §5 config boundary 에서 해석된다 (consumer override\n // 가능; 미설정 시 레거시 기본 경로 — spec.md §4.1 C2 / B5).\n const { uploadEndpoint } = resolveRouteConfig();\n const res = await adminFetch(uploadEndpoint, {\n method: \"POST\",\n body: fd,\n });\n const json = await res.json();\n if (!json.success) {\n throw new Error(json.error?.message || \"업로드 실패\");\n }\n return json.data;\n}\n\nexport function useImageDropZone(options: UseImageDropZoneOptions) {\n const {\n multiple = false,\n maxFiles,\n onUpload,\n onKeyTracked,\n disabled = false,\n } = options;\n\n const [isDragOver, setIsDragOver] = useState(false);\n const [isUploading, setIsUploading] = useState(false);\n const [isResizing, setIsResizing] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const dragCounterRef = useRef(0);\n\n const processFiles = useCallback(\n async (files: File[]) => {\n if (disabled || files.length === 0) return;\n\n let toProcess = multiple ? files : [files[0]];\n\n if (maxFiles && toProcess.length > maxFiles) {\n toProcess = toProcess.slice(0, maxFiles);\n setError(`최대 ${maxFiles}개까지 업로드할 수 있습니다.`);\n }\n\n for (const file of toProcess) {\n const validationError = validateFile(file);\n if (validationError) {\n setError(validationError);\n return;\n }\n }\n\n setError(null);\n\n setIsResizing(true);\n let resizedFiles: File[];\n try {\n resizedFiles = [];\n for (const file of toProcess) {\n const result = await resizeImageIfNeeded(file);\n if (result.wasResized && process.env.NODE_ENV === \"development\") {\n console.log(\n `[image-optimize] ${(result.originalSize / 1024 / 1024).toFixed(1)}MB → ${(result.newSize / 1024 / 1024).toFixed(1)}MB`\n );\n }\n if (result.file.size > MAX_UPLOAD_SIZE) {\n setError(\n `이미지 최적화 후에도 5MB를 초과합니다. (${(result.file.size / 1024 / 1024).toFixed(1)}MB) 더 작은 이미지를 사용해 주세요.`\n );\n setIsResizing(false);\n return;\n }\n resizedFiles.push(result.file);\n }\n } catch (err) {\n const msg =\n err instanceof Error ? err.message : \"이미지 최적화 중 오류 발생\";\n setError(msg);\n setIsResizing(false);\n return;\n }\n setIsResizing(false);\n\n setIsUploading(true);\n try {\n for (const file of resizedFiles) {\n const result = await uploadSingleFile(file);\n onUpload(result);\n if (result.key) onKeyTracked?.(result.key);\n if (result.variantKeys) {\n for (const vk of result.variantKeys) onKeyTracked?.(vk);\n }\n }\n } catch (err) {\n const msg =\n err instanceof Error ? err.message : \"업로드 중 오류 발생\";\n setError(msg);\n } finally {\n setIsUploading(false);\n }\n },\n [multiple, maxFiles, onUpload, onKeyTracked, disabled]\n );\n\n const onDragEnter = useCallback((e: DragEvent) => {\n e.preventDefault();\n e.stopPropagation();\n dragCounterRef.current++;\n if (e.dataTransfer?.types.includes(\"Files\")) {\n setIsDragOver(true);\n }\n }, []);\n\n const onDragOver = useCallback((e: DragEvent) => {\n e.preventDefault();\n e.stopPropagation();\n }, []);\n\n const onDragLeave = useCallback((e: DragEvent) => {\n e.preventDefault();\n e.stopPropagation();\n dragCounterRef.current--;\n if (dragCounterRef.current === 0) {\n setIsDragOver(false);\n }\n }, []);\n\n const onDrop = useCallback(\n (e: DragEvent) => {\n e.preventDefault();\n e.stopPropagation();\n dragCounterRef.current = 0;\n setIsDragOver(false);\n\n if (disabled) return;\n\n const files = Array.from(e.dataTransfer?.files || []).filter((f) =>\n f.type.startsWith(\"image/\")\n );\n\n if (files.length === 0) {\n setError(\"이미지 파일만 업로드할 수 있습니다.\");\n return;\n }\n\n processFiles(files);\n },\n [processFiles, disabled]\n );\n\n const handleFileInput = useCallback(\n async (fileList: FileList | null) => {\n const files = Array.from(fileList || []);\n if (files.length > 0) {\n await processFiles(files);\n }\n },\n [processFiles]\n );\n\n return {\n isDragOver,\n isUploading,\n isResizing,\n error,\n dragHandlers: { onDragEnter, onDragOver, onDragLeave, onDrop },\n handleFileInput,\n };\n}\n"]}
@@ -0,0 +1,181 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }// src/components/ResizableImage.tsx
2
+ var _core = require('@tiptap/core');
3
+
4
+
5
+
6
+ var _react = require('@tiptap/react');
7
+ var _react3 = require('react');
8
+ var _jsxruntime = require('react/jsx-runtime');
9
+ function AlignIcon({ type }) {
10
+ const w = 12;
11
+ const lines = [
12
+ { y: 0, lw: w },
13
+ { y: 3.5, lw: 7 },
14
+ { y: 7, lw: 10 }
15
+ ];
16
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "svg", { width: w, height: "10", viewBox: `0 0 ${w} 10`, fill: "currentColor", children: lines.map((l, i) => {
17
+ const x = type === "center" ? (w - l.lw) / 2 : type === "right" ? w - l.lw : 0;
18
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "rect", { x, y: l.y, width: l.lw, height: "1.5", rx: ".5" }, i);
19
+ }) });
20
+ }
21
+ function ResizableImageComponent(props) {
22
+ const { node, updateAttributes, selected } = props;
23
+ const imgRef = _react3.useRef.call(void 0, null);
24
+ const [resizing, setResizing] = _react3.useState.call(void 0, false);
25
+ const src = node.attrs.src;
26
+ const alt = node.attrs.alt || "";
27
+ const title = node.attrs.title || void 0;
28
+ const width = node.attrs.width;
29
+ const align = node.attrs.align || "center";
30
+ const startResize = _react3.useCallback.call(void 0,
31
+ (e, handle) => {
32
+ e.preventDefault();
33
+ e.stopPropagation();
34
+ const startX = e.clientX;
35
+ const startY = e.clientY;
36
+ const startWidth = _optionalChain([imgRef, 'access', _ => _.current, 'optionalAccess', _2 => _2.offsetWidth]) || 300;
37
+ const startHeight = _optionalChain([imgRef, 'access', _3 => _3.current, 'optionalAccess', _4 => _4.offsetHeight]) || 200;
38
+ const aspectRatio = startWidth / startHeight;
39
+ const isEdge = handle.length === 1;
40
+ const isHorizontal = handle === "e" || handle === "w";
41
+ const signX = handle.includes("e") ? 1 : -1;
42
+ const signY = handle.includes("s") ? 1 : -1;
43
+ const calcNewWidth = (ev) => {
44
+ const dx = (ev.clientX - startX) * signX;
45
+ const dy = (ev.clientY - startY) * signY;
46
+ let delta;
47
+ if (isEdge) {
48
+ delta = isHorizontal ? dx : dy * aspectRatio;
49
+ } else {
50
+ delta = Math.abs(dx) > Math.abs(dy) ? dx : dy * aspectRatio;
51
+ }
52
+ return Math.max(80, startWidth + delta);
53
+ };
54
+ const onMouseMove = (ev) => {
55
+ const newWidth = calcNewWidth(ev);
56
+ if (imgRef.current) {
57
+ imgRef.current.style.width = `${newWidth}px`;
58
+ }
59
+ };
60
+ const onMouseUp = (ev) => {
61
+ document.removeEventListener("mousemove", onMouseMove);
62
+ document.removeEventListener("mouseup", onMouseUp);
63
+ setResizing(false);
64
+ updateAttributes({ width: Math.round(calcNewWidth(ev)) });
65
+ };
66
+ setResizing(true);
67
+ document.addEventListener("mousemove", onMouseMove);
68
+ document.addEventListener("mouseup", onMouseUp);
69
+ },
70
+ [updateAttributes]
71
+ );
72
+ const handles = ["nw", "n", "ne", "w", "e", "sw", "s", "se"];
73
+ return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
74
+ _react.NodeViewWrapper,
75
+ {
76
+ className: `resizable-image-wrapper resizable-image-align-${align} ${selected ? "selected" : ""} ${resizing ? "resizing" : ""}`,
77
+ children: [
78
+ selected && !resizing && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "resizable-image-align-toolbar", children: ["left", "center", "right"].map((a) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
79
+ "button",
80
+ {
81
+ type: "button",
82
+ className: `resizable-image-align-btn ${align === a ? "active" : ""}`,
83
+ onMouseDown: (e) => {
84
+ e.preventDefault();
85
+ updateAttributes({ align: a });
86
+ },
87
+ title: a === "left" ? "\uC67C\uCABD \uC815\uB82C" : a === "center" ? "\uAC00\uC6B4\uB370 \uC815\uB82C" : "\uC624\uB978\uCABD \uC815\uB82C",
88
+ children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, AlignIcon, { type: a })
89
+ },
90
+ a
91
+ )) }),
92
+ /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
93
+ "div",
94
+ {
95
+ className: "resizable-image-container",
96
+ style: { width: width ? `${width}px` : void 0 },
97
+ children: [
98
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
99
+ "img",
100
+ {
101
+ ref: imgRef,
102
+ src,
103
+ alt,
104
+ title,
105
+ style: { width: width ? `${width}px` : void 0 },
106
+ draggable: false
107
+ }
108
+ ),
109
+ (selected || resizing) && handles.map((h) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
110
+ "div",
111
+ {
112
+ className: `resizable-image-handle resizable-image-handle-${h}`,
113
+ onMouseDown: (e) => startResize(e, h)
114
+ },
115
+ h
116
+ ))
117
+ ]
118
+ }
119
+ )
120
+ ]
121
+ }
122
+ );
123
+ }
124
+ var ResizableImage = _core.Node.create({
125
+ name: "image",
126
+ group: "block",
127
+ atom: true,
128
+ draggable: true,
129
+ addAttributes() {
130
+ return {
131
+ src: { default: null },
132
+ alt: { default: null },
133
+ title: { default: null },
134
+ width: { default: null },
135
+ align: {
136
+ default: "center",
137
+ parseHTML: (element) => element.getAttribute("data-align") || "center",
138
+ renderHTML: (attributes) => ({
139
+ "data-align": attributes.align
140
+ })
141
+ }
142
+ };
143
+ },
144
+ parseHTML() {
145
+ return [{ tag: "img[src]" }];
146
+ },
147
+ renderHTML({ HTMLAttributes }) {
148
+ const attrs = { ...HTMLAttributes };
149
+ const styles = [];
150
+ if (attrs.width) {
151
+ styles.push(`width: ${attrs.width}px`);
152
+ }
153
+ const align = attrs["data-align"] || "center";
154
+ styles.push("display: block");
155
+ if (align === "center") {
156
+ styles.push("margin-left: auto", "margin-right: auto");
157
+ } else if (align === "right") {
158
+ styles.push("margin-left: auto");
159
+ }
160
+ if (styles.length) attrs.style = styles.join("; ");
161
+ return ["img", _core.mergeAttributes.call(void 0, attrs)];
162
+ },
163
+ addNodeView() {
164
+ return _react.ReactNodeViewRenderer.call(void 0, ResizableImageComponent);
165
+ },
166
+ addCommands() {
167
+ return {
168
+ setImage: (options) => ({ commands }) => {
169
+ return commands.insertContent({
170
+ type: this.name,
171
+ attrs: options
172
+ });
173
+ }
174
+ };
175
+ }
176
+ });
177
+
178
+
179
+
180
+ exports.ResizableImage = ResizableImage;
181
+ //# sourceMappingURL=chunk-ZFORODOX.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/Users/uni4love/project/workspace/211-withwiz/node-packages/withwiz-cms-kit/dist/chunk-ZFORODOX.js","../src/components/ResizableImage.tsx"],"names":[],"mappings":"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","file":"/Users/uni4love/project/workspace/211-withwiz/node-packages/withwiz-cms-kit/dist/chunk-ZFORODOX.js","sourcesContent":[null,"\"use client\";\n\nimport { Node, mergeAttributes } from \"@tiptap/core\";\nimport {\n ReactNodeViewRenderer,\n NodeViewWrapper,\n type NodeViewProps,\n} from \"@tiptap/react\";\nimport { useCallback, useRef, useState } from \"react\";\n\ntype Handle = \"nw\" | \"ne\" | \"sw\" | \"se\" | \"n\" | \"s\" | \"e\" | \"w\";\n\nfunction AlignIcon({ type }: { type: \"left\" | \"center\" | \"right\" }) {\n const w = 12;\n const lines = [\n { y: 0, lw: w },\n { y: 3.5, lw: 7 },\n { y: 7, lw: 10 },\n ];\n return (\n <svg width={w} height=\"10\" viewBox={`0 0 ${w} 10`} fill=\"currentColor\">\n {lines.map((l, i) => {\n const x =\n type === \"center\"\n ? (w - l.lw) / 2\n : type === \"right\"\n ? w - l.lw\n : 0;\n return <rect key={i} x={x} y={l.y} width={l.lw} height=\"1.5\" rx=\".5\" />;\n })}\n </svg>\n );\n}\n\nfunction ResizableImageComponent(props: NodeViewProps) {\n const { node, updateAttributes, selected } = props;\n const imgRef = useRef<HTMLImageElement>(null);\n const [resizing, setResizing] = useState(false);\n\n const src = node.attrs.src as string;\n const alt = (node.attrs.alt as string) || \"\";\n const title = (node.attrs.title as string) || undefined;\n const width = node.attrs.width as number | null;\n const align = (node.attrs.align as string) || \"center\";\n\n const startResize = useCallback(\n (e: React.MouseEvent, handle: Handle) => {\n e.preventDefault();\n e.stopPropagation();\n\n const startX = e.clientX;\n const startY = e.clientY;\n const startWidth = imgRef.current?.offsetWidth || 300;\n const startHeight = imgRef.current?.offsetHeight || 200;\n const aspectRatio = startWidth / startHeight;\n\n const isEdge = handle.length === 1; // \"n\", \"s\", \"e\", \"w\"\n const isHorizontal = handle === \"e\" || handle === \"w\";\n\n // x 방향 부호: 오른쪽 핸들이면 +1, 왼쪽이면 -1\n const signX = handle.includes(\"e\") ? 1 : -1;\n // y 방향 부호: 아래쪽 핸들이면 +1, 위쪽이면 -1\n const signY = handle.includes(\"s\") ? 1 : -1;\n\n const calcNewWidth = (ev: MouseEvent) => {\n const dx = (ev.clientX - startX) * signX;\n const dy = (ev.clientY - startY) * signY;\n\n let delta: number;\n if (isEdge) {\n // 변 중간 핸들: 해당 축 방향만 사용하되 비율 유지\n delta = isHorizontal ? dx : dy * aspectRatio;\n } else {\n // 모서리 핸들: 더 큰 변화량 기준으로 비율 유지\n delta = Math.abs(dx) > Math.abs(dy) ? dx : dy * aspectRatio;\n }\n return Math.max(80, startWidth + delta);\n };\n\n const onMouseMove = (ev: MouseEvent) => {\n const newWidth = calcNewWidth(ev);\n if (imgRef.current) {\n imgRef.current.style.width = `${newWidth}px`;\n }\n };\n\n const onMouseUp = (ev: MouseEvent) => {\n document.removeEventListener(\"mousemove\", onMouseMove);\n document.removeEventListener(\"mouseup\", onMouseUp);\n setResizing(false);\n updateAttributes({ width: Math.round(calcNewWidth(ev)) });\n };\n\n setResizing(true);\n document.addEventListener(\"mousemove\", onMouseMove);\n document.addEventListener(\"mouseup\", onMouseUp);\n },\n [updateAttributes]\n );\n\n const handles: Handle[] = [\"nw\", \"n\", \"ne\", \"w\", \"e\", \"sw\", \"s\", \"se\"];\n\n return (\n <NodeViewWrapper\n className={`resizable-image-wrapper resizable-image-align-${align} ${selected ? \"selected\" : \"\"} ${resizing ? \"resizing\" : \"\"}`}\n >\n {selected && !resizing && (\n <div className=\"resizable-image-align-toolbar\">\n {([\"left\", \"center\", \"right\"] as const).map((a) => (\n <button\n key={a}\n type=\"button\"\n className={`resizable-image-align-btn ${align === a ? \"active\" : \"\"}`}\n onMouseDown={(e) => {\n e.preventDefault();\n updateAttributes({ align: a });\n }}\n title={a === \"left\" ? \"왼쪽 정렬\" : a === \"center\" ? \"가운데 정렬\" : \"오른쪽 정렬\"}\n >\n <AlignIcon type={a} />\n </button>\n ))}\n </div>\n )}\n <div\n className=\"resizable-image-container\"\n style={{ width: width ? `${width}px` : undefined }}\n >\n <img\n ref={imgRef}\n src={src}\n alt={alt}\n title={title}\n style={{ width: width ? `${width}px` : undefined }}\n draggable={false}\n />\n {(selected || resizing) &&\n handles.map((h) => (\n <div\n key={h}\n className={`resizable-image-handle resizable-image-handle-${h}`}\n onMouseDown={(e) => startResize(e, h)}\n />\n ))}\n </div>\n </NodeViewWrapper>\n );\n}\n\ndeclare module \"@tiptap/core\" {\n interface Commands<ReturnType> {\n resizableImage: {\n setImage: (options: {\n src: string;\n alt?: string;\n title?: string;\n width?: number;\n align?: string;\n }) => ReturnType;\n };\n }\n}\n\nexport const ResizableImage = Node.create({\n name: \"image\",\n group: \"block\",\n atom: true,\n draggable: true,\n\n addAttributes() {\n return {\n src: { default: null },\n alt: { default: null },\n title: { default: null },\n width: { default: null },\n align: {\n default: \"center\",\n parseHTML: (element: HTMLElement) =>\n element.getAttribute(\"data-align\") || \"center\",\n renderHTML: (attributes: Record<string, unknown>) => ({\n \"data-align\": attributes.align,\n }),\n },\n };\n },\n\n parseHTML() {\n return [{ tag: \"img[src]\" }];\n },\n\n renderHTML({ HTMLAttributes }) {\n const attrs = { ...HTMLAttributes };\n const styles: string[] = [];\n if (attrs.width) {\n styles.push(`width: ${attrs.width}px`);\n }\n const align = attrs[\"data-align\"] || \"center\";\n styles.push(\"display: block\");\n if (align === \"center\") {\n styles.push(\"margin-left: auto\", \"margin-right: auto\");\n } else if (align === \"right\") {\n styles.push(\"margin-left: auto\");\n }\n if (styles.length) attrs.style = styles.join(\"; \");\n return [\"img\", mergeAttributes(attrs)];\n },\n\n addNodeView() {\n return ReactNodeViewRenderer(ResizableImageComponent);\n },\n\n addCommands() {\n return {\n setImage:\n (options: {\n src: string;\n alt?: string;\n title?: string;\n width?: number;\n align?: string;\n }) =>\n ({ commands }) => {\n return commands.insertContent({\n type: this.name,\n attrs: options,\n });\n },\n } satisfies Partial<import(\"@tiptap/core\").RawCommands>;\n },\n});\n"]}