freelang-v4 4.3.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 (261) hide show
  1. package/README.md +548 -0
  2. package/dist/ast.d.ts +367 -0
  3. package/dist/ast.js +4 -0
  4. package/dist/ast.js.map +1 -0
  5. package/dist/async-basic.test.d.ts +1 -0
  6. package/dist/async-basic.test.js +88 -0
  7. package/dist/async-basic.test.js.map +1 -0
  8. package/dist/async-jest.test.d.ts +1 -0
  9. package/dist/async-jest.test.js +99 -0
  10. package/dist/async-jest.test.js.map +1 -0
  11. package/dist/channel-jest.test.d.ts +1 -0
  12. package/dist/channel-jest.test.js +148 -0
  13. package/dist/channel-jest.test.js.map +1 -0
  14. package/dist/checker-jest.test.d.ts +1 -0
  15. package/dist/checker-jest.test.js +160 -0
  16. package/dist/checker-jest.test.js.map +1 -0
  17. package/dist/checker.d.ts +149 -0
  18. package/dist/checker.js +1565 -0
  19. package/dist/checker.js.map +1 -0
  20. package/dist/checker.test.d.ts +1 -0
  21. package/dist/checker.test.js +217 -0
  22. package/dist/checker.test.js.map +1 -0
  23. package/dist/compiler-jest.test.d.ts +1 -0
  24. package/dist/compiler-jest.test.js +233 -0
  25. package/dist/compiler-jest.test.js.map +1 -0
  26. package/dist/compiler.d.ts +127 -0
  27. package/dist/compiler.js +1588 -0
  28. package/dist/compiler.js.map +1 -0
  29. package/dist/compiler.test.d.ts +1 -0
  30. package/dist/compiler.test.js +313 -0
  31. package/dist/compiler.test.js.map +1 -0
  32. package/dist/db-100m-full.d.ts +5 -0
  33. package/dist/db-100m-full.js +78 -0
  34. package/dist/db-100m-full.js.map +1 -0
  35. package/dist/db-100m-no-index.d.ts +12 -0
  36. package/dist/db-100m-no-index.js +119 -0
  37. package/dist/db-100m-no-index.js.map +1 -0
  38. package/dist/db-100m-real.d.ts +5 -0
  39. package/dist/db-100m-real.js +131 -0
  40. package/dist/db-100m-real.js.map +1 -0
  41. package/dist/db-100m-streaming.d.ts +15 -0
  42. package/dist/db-100m-streaming.js +164 -0
  43. package/dist/db-100m-streaming.js.map +1 -0
  44. package/dist/db-100m-test.d.ts +5 -0
  45. package/dist/db-100m-test.js +111 -0
  46. package/dist/db-100m-test.js.map +1 -0
  47. package/dist/db-jest.test.d.ts +1 -0
  48. package/dist/db-jest.test.js +182 -0
  49. package/dist/db-jest.test.js.map +1 -0
  50. package/dist/db-runtime.d.ts +24 -0
  51. package/dist/db-runtime.js +204 -0
  52. package/dist/db-runtime.js.map +1 -0
  53. package/dist/db.d.ts +249 -0
  54. package/dist/db.js +593 -0
  55. package/dist/db.js.map +1 -0
  56. package/dist/file-io-jest.test.d.ts +1 -0
  57. package/dist/file-io-jest.test.js +225 -0
  58. package/dist/file-io-jest.test.js.map +1 -0
  59. package/dist/for-of-jest.test.d.ts +1 -0
  60. package/dist/for-of-jest.test.js +230 -0
  61. package/dist/for-of-jest.test.js.map +1 -0
  62. package/dist/for-of.test.d.ts +1 -0
  63. package/dist/for-of.test.js +305 -0
  64. package/dist/for-of.test.js.map +1 -0
  65. package/dist/function-literal-jest.test.d.ts +1 -0
  66. package/dist/function-literal-jest.test.js +180 -0
  67. package/dist/function-literal-jest.test.js.map +1 -0
  68. package/dist/function-literal.test.d.ts +1 -0
  69. package/dist/function-literal.test.js +245 -0
  70. package/dist/function-literal.test.js.map +1 -0
  71. package/dist/generics-jest.test.d.ts +1 -0
  72. package/dist/generics-jest.test.js +93 -0
  73. package/dist/generics-jest.test.js.map +1 -0
  74. package/dist/ir-gen.d.ts +15 -0
  75. package/dist/ir-gen.js +400 -0
  76. package/dist/ir-gen.js.map +1 -0
  77. package/dist/ir.d.ts +114 -0
  78. package/dist/ir.js +5 -0
  79. package/dist/ir.js.map +1 -0
  80. package/dist/lexer.d.ts +110 -0
  81. package/dist/lexer.js +467 -0
  82. package/dist/lexer.js.map +1 -0
  83. package/dist/lexer.test.d.ts +1 -0
  84. package/dist/lexer.test.js +426 -0
  85. package/dist/lexer.test.js.map +1 -0
  86. package/dist/main.d.ts +2 -0
  87. package/dist/main.js +241 -0
  88. package/dist/main.js.map +1 -0
  89. package/dist/module-jest.test.d.ts +1 -0
  90. package/dist/module-jest.test.js +123 -0
  91. package/dist/module-jest.test.js.map +1 -0
  92. package/dist/parser.d.ts +56 -0
  93. package/dist/parser.js +1060 -0
  94. package/dist/parser.js.map +1 -0
  95. package/dist/parser.test.d.ts +1 -0
  96. package/dist/parser.test.js +461 -0
  97. package/dist/parser.test.js.map +1 -0
  98. package/dist/pattern-matching-jest.test.d.ts +1 -0
  99. package/dist/pattern-matching-jest.test.js +158 -0
  100. package/dist/pattern-matching-jest.test.js.map +1 -0
  101. package/dist/pkg/init.d.ts +1 -0
  102. package/dist/pkg/init.js +118 -0
  103. package/dist/pkg/init.js.map +1 -0
  104. package/dist/pkg/install.d.ts +1 -0
  105. package/dist/pkg/install.js +77 -0
  106. package/dist/pkg/install.js.map +1 -0
  107. package/dist/pkg/registry.d.ts +23 -0
  108. package/dist/pkg/registry.js +106 -0
  109. package/dist/pkg/registry.js.map +1 -0
  110. package/dist/pkg/run.d.ts +1 -0
  111. package/dist/pkg/run.js +76 -0
  112. package/dist/pkg/run.js.map +1 -0
  113. package/dist/pkg/toml.d.ts +5 -0
  114. package/dist/pkg/toml.js +117 -0
  115. package/dist/pkg/toml.js.map +1 -0
  116. package/dist/repl.d.ts +15 -0
  117. package/dist/repl.js +197 -0
  118. package/dist/repl.js.map +1 -0
  119. package/dist/runtime/bytecode.d.ts +92 -0
  120. package/dist/runtime/bytecode.js +253 -0
  121. package/dist/runtime/bytecode.js.map +1 -0
  122. package/dist/runtime/value.d.ts +102 -0
  123. package/dist/runtime/value.js +302 -0
  124. package/dist/runtime/value.js.map +1 -0
  125. package/dist/runtime/vm.d.ts +65 -0
  126. package/dist/runtime/vm.js +293 -0
  127. package/dist/runtime/vm.js.map +1 -0
  128. package/dist/struct-instance-jest.test.d.ts +1 -0
  129. package/dist/struct-instance-jest.test.js +209 -0
  130. package/dist/struct-instance-jest.test.js.map +1 -0
  131. package/dist/struct-instance.test.d.ts +1 -0
  132. package/dist/struct-instance.test.js +291 -0
  133. package/dist/struct-instance.test.js.map +1 -0
  134. package/dist/struct-jest.test.d.ts +1 -0
  135. package/dist/struct-jest.test.js +176 -0
  136. package/dist/struct-jest.test.js.map +1 -0
  137. package/dist/struct.test.d.ts +1 -0
  138. package/dist/struct.test.js +231 -0
  139. package/dist/struct.test.js.map +1 -0
  140. package/dist/trait-jest.test.d.ts +1 -0
  141. package/dist/trait-jest.test.js +120 -0
  142. package/dist/trait-jest.test.js.map +1 -0
  143. package/dist/vm-jest.test.d.ts +1 -0
  144. package/dist/vm-jest.test.js +569 -0
  145. package/dist/vm-jest.test.js.map +1 -0
  146. package/dist/vm.d.ts +81 -0
  147. package/dist/vm.js +1956 -0
  148. package/dist/vm.js.map +1 -0
  149. package/dist/vm.test.d.ts +1 -0
  150. package/dist/vm.test.js +337 -0
  151. package/dist/vm.test.js.map +1 -0
  152. package/dist/web-repl/sandbox.d.ts +11 -0
  153. package/dist/web-repl/sandbox.js +76 -0
  154. package/dist/web-repl/sandbox.js.map +1 -0
  155. package/dist/web-repl/server.d.ts +1 -0
  156. package/dist/web-repl/server.js +111 -0
  157. package/dist/web-repl/server.js.map +1 -0
  158. package/dist/while-loop-jest.test.d.ts +1 -0
  159. package/dist/while-loop-jest.test.js +201 -0
  160. package/dist/while-loop-jest.test.js.map +1 -0
  161. package/dist/while-loop.test.d.ts +1 -0
  162. package/dist/while-loop.test.js +262 -0
  163. package/dist/while-loop.test.js.map +1 -0
  164. package/docs/EXPERIENCE.md +787 -0
  165. package/docs/README.md +175 -0
  166. package/docs/V1_V2_V3_ANALYSIS.md +107 -0
  167. package/docs/_config.yml +36 -0
  168. package/docs/api-reference.md +459 -0
  169. package/docs/architecture.md +470 -0
  170. package/docs/benchmarks.md +295 -0
  171. package/docs/comparison.md +454 -0
  172. package/docs/index.md +335 -0
  173. package/docs/language-completeness.md +228 -0
  174. package/docs/learning-guide.md +651 -0
  175. package/package.json +65 -0
  176. package/src/api/deploy_key.fl +294 -0
  177. package/src/api/issue.fl +302 -0
  178. package/src/api/org.fl +356 -0
  179. package/src/api/repo.fl +394 -0
  180. package/src/api/team.fl +299 -0
  181. package/src/api/user.fl +385 -0
  182. package/src/api/webhook.fl +273 -0
  183. package/src/ast.ts +158 -0
  184. package/src/async-basic.test.ts +94 -0
  185. package/src/async-jest.test.ts +107 -0
  186. package/src/channel-jest.test.ts +158 -0
  187. package/src/checker-jest.test.ts +189 -0
  188. package/src/checker.test.ts +279 -0
  189. package/src/checker.ts +1861 -0
  190. package/src/commands/analyze.fl +227 -0
  191. package/src/commands/auth.fl +315 -0
  192. package/src/commands/batch.fl +349 -0
  193. package/src/commands/config.fl +199 -0
  194. package/src/commands/deploy_key.fl +352 -0
  195. package/src/commands/issue.fl +275 -0
  196. package/src/commands/main.fl +492 -0
  197. package/src/commands/org.fl +425 -0
  198. package/src/commands/repo.fl +581 -0
  199. package/src/commands/team.fl +244 -0
  200. package/src/commands/user.fl +423 -0
  201. package/src/commands/webhook.fl +400 -0
  202. package/src/compiler-jest.test.ts +275 -0
  203. package/src/compiler.test.ts +375 -0
  204. package/src/compiler.ts +1770 -0
  205. package/src/config.fl +175 -0
  206. package/src/core/batch.fl +355 -0
  207. package/src/core/cache.fl +284 -0
  208. package/src/core/ensure.fl +324 -0
  209. package/src/db-100m-full.ts +96 -0
  210. package/src/db-100m-no-index.ts +133 -0
  211. package/src/db-100m-real.ts +152 -0
  212. package/src/db-100m-streaming.ts +154 -0
  213. package/src/db-100m-test.ts +136 -0
  214. package/src/db-jest.test.ts +161 -0
  215. package/src/db-runtime.ts +242 -0
  216. package/src/db.ts +676 -0
  217. package/src/errors.fl +134 -0
  218. package/src/for-of-jest.test.ts +246 -0
  219. package/src/for-of.test.ts +308 -0
  220. package/src/function-literal-jest.test.ts +193 -0
  221. package/src/function-literal.test.ts +248 -0
  222. package/src/generics-jest.test.ts +104 -0
  223. package/src/http/client.fl +327 -0
  224. package/src/ir-gen.ts +459 -0
  225. package/src/ir.ts +80 -0
  226. package/src/lexer.test.ts +499 -0
  227. package/src/lexer.ts +522 -0
  228. package/src/main.ts +223 -0
  229. package/src/models.fl +162 -0
  230. package/src/module-jest.test.ts +145 -0
  231. package/src/parser.test.ts +542 -0
  232. package/src/parser.ts +1211 -0
  233. package/src/pattern-matching-jest.test.ts +170 -0
  234. package/src/pkg/init.ts +91 -0
  235. package/src/pkg/install.ts +56 -0
  236. package/src/pkg/registry.ts +103 -0
  237. package/src/pkg/run.ts +49 -0
  238. package/src/pkg/toml.ts +129 -0
  239. package/src/repl.ts +190 -0
  240. package/src/runtime/bytecode.ts +291 -0
  241. package/src/runtime/value.ts +322 -0
  242. package/src/runtime/vm.ts +354 -0
  243. package/src/self-host/bootstrap.fl +68 -0
  244. package/src/self-host/interpreter.fl +361 -0
  245. package/src/self-host/lexer-simple.fl +22 -0
  246. package/src/self-host/lexer.fl +305 -0
  247. package/src/self-host/parser.fl +580 -0
  248. package/src/struct-instance-jest.test.ts +221 -0
  249. package/src/struct-instance.test.ts +293 -0
  250. package/src/struct-jest.test.ts +187 -0
  251. package/src/struct.test.ts +234 -0
  252. package/src/trait-jest.test.ts +136 -0
  253. package/src/vm-jest.test.ts +754 -0
  254. package/src/vm.ts +1976 -0
  255. package/src/web-repl/public/index.html +50 -0
  256. package/src/web-repl/public/main.js +105 -0
  257. package/src/web-repl/public/style.css +225 -0
  258. package/src/web-repl/sandbox.ts +88 -0
  259. package/src/web-repl/server.ts +97 -0
  260. package/src/while-loop-jest.test.ts +218 -0
  261. package/src/while-loop.test.ts +267 -0
@@ -0,0 +1,294 @@
1
+ // FreeLang v4.2 — Gogs CLI DeployKeyService
2
+ // 배포 키 관리 API 서비스
3
+ // 150라인: add, list, delete, enable, disable
4
+
5
+ /// 배포 키 서비스
6
+ struct DeployKeyService {
7
+ client: HttpClient
8
+ cache: CacheManager
9
+ }
10
+
11
+ /// DeployKeyService 생성
12
+ fn new_deploy_key_service(client: HttpClient, cache: CacheManager) -> DeployKeyService {
13
+ DeployKeyService {
14
+ client: client,
15
+ cache: cache
16
+ }
17
+ }
18
+
19
+ // ==================== 배포 키 CRUD ====================
20
+
21
+ /// 배포 키 목록 조회 (저장소 단위)
22
+ async fn list_deploy_keys(service: DeployKeyService, owner: str, repo: str) -> Result<[DeployKey]> {
23
+ var cache_key = "deploy_keys:" + owner + "/" + repo
24
+
25
+ // 캐시 확인
26
+ var cached = cache_get(service.cache, cache_key)
27
+ if cached != "" {
28
+ println("[cache] hit: " + cache_key)
29
+ var keys_arr = json_parse(cached) as [DeployKey]
30
+ return Result::Ok(keys_arr)
31
+ }
32
+
33
+ // API 호출
34
+ var path = "/api/v1/repos/" + owner + "/" + repo + "/keys"
35
+ var resp = await http_get(service.client, path)
36
+
37
+ if is_error(resp.status) {
38
+ return Result::Err(response_to_error(resp))
39
+ }
40
+
41
+ // 캐시 저장
42
+ cache_set(service.cache, cache_key, resp.body)
43
+
44
+ // 파싱
45
+ var keys_arr = json_parse(resp.body) as [DeployKey]
46
+ Result::Ok(keys_arr)
47
+ }
48
+
49
+ /// 배포 키 상세 조회
50
+ async fn get_deploy_key(service: DeployKeyService, owner: str, repo: str, key_id: i32) -> Result<DeployKey> {
51
+ var cache_key = "deploy_key:" + owner + "/" + repo + "/" + str(key_id)
52
+
53
+ // 캐시 확인
54
+ var cached = cache_get(service.cache, cache_key)
55
+ if cached != "" {
56
+ println("[cache] hit: " + cache_key)
57
+ var key = json_parse(cached) as DeployKey
58
+ return Result::Ok(key)
59
+ }
60
+
61
+ // API 호출
62
+ var path = "/api/v1/repos/" + owner + "/" + repo + "/keys/" + str(key_id)
63
+ var resp = await http_get(service.client, path)
64
+
65
+ if is_error(resp.status) {
66
+ return Result::Err(response_to_error(resp))
67
+ }
68
+
69
+ // 캐시 저장
70
+ cache_set(service.cache, cache_key, resp.body)
71
+
72
+ // 파싱
73
+ var key = json_parse(resp.body) as DeployKey
74
+ Result::Ok(key)
75
+ }
76
+
77
+ /// 배포 키 추가
78
+ async fn add_deploy_key(service: DeployKeyService, owner: str, repo: str, req: CreateDeployKeyRequest) -> Result<DeployKey> {
79
+ var path = "/api/v1/repos/" + owner + "/" + repo + "/keys"
80
+ var json_data = json_stringify(req)
81
+ var resp = await http_post_json(service.client, path, json_data)
82
+
83
+ if is_error(resp.status) {
84
+ return Result::Err(response_to_error(resp))
85
+ }
86
+
87
+ // 캐시 무효화
88
+ cache_invalidate(service.cache, "deploy_keys:" + owner + "/" + repo)
89
+
90
+ // 파싱
91
+ var key = json_parse(resp.body) as DeployKey
92
+ Result::Ok(key)
93
+ }
94
+
95
+ /// 배포 키 삭제
96
+ async fn delete_deploy_key(service: DeployKeyService, owner: str, repo: str, key_id: i32) -> Result<str> {
97
+ var path = "/api/v1/repos/" + owner + "/" + repo + "/keys/" + str(key_id)
98
+ var resp = await http_delete(service.client, path)
99
+
100
+ if is_error(resp.status) {
101
+ return Result::Err(response_to_error(resp))
102
+ }
103
+
104
+ // 캐시 무효화
105
+ var cache_key = "deploy_key:" + owner + "/" + repo + "/" + str(key_id)
106
+ cache_invalidate(service.cache, cache_key)
107
+ cache_invalidate(service.cache, "deploy_keys:" + owner + "/" + repo)
108
+
109
+ Result::Ok("Deploy key deleted")
110
+ }
111
+
112
+ // ==================== 배포 키 활성화/비활성화 ====================
113
+
114
+ /// 배포 키 활성화
115
+ async fn enable_deploy_key(service: DeployKeyService, owner: str, repo: str, key_id: i32) -> Result<str> {
116
+ var path = "/api/v1/repos/" + owner + "/" + repo + "/keys/" + str(key_id) + "/enable"
117
+ var resp = await http_put(service.client, path, "{}")
118
+
119
+ if is_error(resp.status) {
120
+ return Result::Err(response_to_error(resp))
121
+ }
122
+
123
+ // 캐시 무효화
124
+ var cache_key = "deploy_key:" + owner + "/" + repo + "/" + str(key_id)
125
+ cache_invalidate(service.cache, cache_key)
126
+ cache_invalidate(service.cache, "deploy_keys:" + owner + "/" + repo)
127
+
128
+ Result::Ok("Deploy key enabled")
129
+ }
130
+
131
+ /// 배포 키 비활성화
132
+ async fn disable_deploy_key(service: DeployKeyService, owner: str, repo: str, key_id: i32) -> Result<str> {
133
+ var path = "/api/v1/repos/" + owner + "/" + repo + "/keys/" + str(key_id) + "/disable"
134
+ var resp = await http_put(service.client, path, "{}")
135
+
136
+ if is_error(resp.status) {
137
+ return Result::Err(response_to_error(resp))
138
+ }
139
+
140
+ // 캐시 무효화
141
+ var cache_key = "deploy_key:" + owner + "/" + repo + "/" + str(key_id)
142
+ cache_invalidate(service.cache, cache_key)
143
+ cache_invalidate(service.cache, "deploy_keys:" + owner + "/" + repo)
144
+
145
+ Result::Ok("Deploy key disabled")
146
+ }
147
+
148
+ // ==================== 배포 키 유틸리티 ====================
149
+
150
+ /// 배포 키 존재 여부 확인
151
+ async fn has_deploy_key(service: DeployKeyService, owner: str, repo: str, title: str) -> Result<bool> {
152
+ var keys_result = await list_deploy_keys(service, owner, repo)
153
+
154
+ match keys_result {
155
+ Result::Ok(keys) => {
156
+ var i: i32 = 0
157
+ while i < length(keys) {
158
+ if keys[i].title == title {
159
+ return Result::Ok(true)
160
+ }
161
+ i = i + 1
162
+ }
163
+ Result::Ok(false)
164
+ }
165
+ Result::Err(err) => Result::Err(err)
166
+ }
167
+ }
168
+
169
+ /// 배포 키 검색 (제목으로)
170
+ async fn find_deploy_key_by_title(service: DeployKeyService, owner: str, repo: str, title: str) -> Result<DeployKey> {
171
+ var keys_result = await list_deploy_keys(service, owner, repo)
172
+
173
+ match keys_result {
174
+ Result::Ok(keys) => {
175
+ var i: i32 = 0
176
+ while i < length(keys) {
177
+ if keys[i].title == title {
178
+ return Result::Ok(keys[i])
179
+ }
180
+ i = i + 1
181
+ }
182
+ Result::Err(Error::NotFound("Deploy key not found: " + title))
183
+ }
184
+ Result::Err(err) => Result::Err(err)
185
+ }
186
+ }
187
+
188
+ // ==================== 배포 키 읽기 전용 ====================
189
+
190
+ /// 읽기 전용 배포 키 추가
191
+ async fn add_readonly_deploy_key(service: DeployKeyService, owner: str, repo: str, title: str, key: str) -> Result<DeployKey> {
192
+ var req = CreateDeployKeyRequest {
193
+ title: title,
194
+ key: key,
195
+ read_only: true
196
+ }
197
+ await add_deploy_key(service, owner, repo, req)
198
+ }
199
+
200
+ /// 읽기-쓰기 배포 키 추가
201
+ async fn add_readwrite_deploy_key(service: DeployKeyService, owner: str, repo: str, title: str, key: str) -> Result<DeployKey> {
202
+ var req = CreateDeployKeyRequest {
203
+ title: title,
204
+ key: key,
205
+ read_only: false
206
+ }
207
+ await add_deploy_key(service, owner, repo, req)
208
+ }
209
+
210
+ // ==================== 별명 (Alias) ====================
211
+
212
+ /// 배포 키 추가 (별명)
213
+ async fn new_deploy_key(service: DeployKeyService, owner: str, repo: str, title: str, key: str) -> Result<DeployKey> {
214
+ var req = CreateDeployKeyRequest {
215
+ title: title,
216
+ key: key,
217
+ read_only: true
218
+ }
219
+ await add_deploy_key(service, owner, repo, req)
220
+ }
221
+
222
+ /// 배포 키 삭제 (별명)
223
+ async fn rm_deploy_key(service: DeployKeyService, owner: str, repo: str, key_id: i32) -> Result<str> {
224
+ await delete_deploy_key(service, owner, repo, key_id)
225
+ }
226
+
227
+ // ==================== 배포 키 생성 유틸리티 ====================
228
+
229
+ /// SSH 배포 키 추가 (기본 읽기 전용)
230
+ async fn add_ssh_deploy_key(service: DeployKeyService, owner: str, repo: str, title: str, pub_key: str) -> Result<DeployKey> {
231
+ await add_readonly_deploy_key(service, owner, repo, title, pub_key)
232
+ }
233
+
234
+ /// 배포 키 ensure 패턴
235
+ async fn ensure_deploy_key(
236
+ service: DeployKeyService,
237
+ owner: str,
238
+ repo: str,
239
+ title: str,
240
+ key: str,
241
+ read_only: bool
242
+ ) -> Result<EnsureAction> {
243
+ // 먼저 기존 키 검색
244
+ var exists_result = await has_deploy_key(service, owner, repo, title)
245
+
246
+ match exists_result {
247
+ Result::Ok(exists) => {
248
+ if exists {
249
+ return Result::Ok(EnsureAction::Unchanged)
250
+ }
251
+
252
+ // 키 생성
253
+ var req = CreateDeployKeyRequest {
254
+ title: title,
255
+ key: key,
256
+ read_only: read_only
257
+ }
258
+
259
+ var create_result = await add_deploy_key(service, owner, repo, req)
260
+
261
+ match create_result {
262
+ Result::Ok(_) => {
263
+ Result::Ok(EnsureAction::Created)
264
+ }
265
+ Result::Err(err) => {
266
+ Result::Ok(EnsureAction::Error(error_message(err)))
267
+ }
268
+ }
269
+ }
270
+ Result::Err(err) => Result::Err(err)
271
+ }
272
+ }
273
+
274
+ // Helper 함수
275
+ fn error_message(err: Error) -> str {
276
+ match err {
277
+ Error::NotFound(msg) => "Not found: " + msg,
278
+ Error::BadRequest(msg) => "Bad request: " + msg,
279
+ Error::Unauthorized(msg) => "Unauthorized: " + msg,
280
+ Error::Forbidden(msg) => "Forbidden: " + msg,
281
+ Error::NetworkError(msg) => "Network error: " + msg,
282
+ Error::HttpError(status, msg) => "HTTP " + str(status) + ": " + msg,
283
+ Error::Unknown(msg) => "Unknown error: " + msg,
284
+ _ => "Error occurred",
285
+ }
286
+ }
287
+
288
+ // 필요한 함수들 (다른 모듈에서 임포트):
289
+ // - http_get, http_post_json, http_delete, http_put
290
+ // - is_error, response_to_error
291
+ // - cache_get, cache_set, cache_invalidate
292
+ // - json_stringify, json_parse
293
+ // - println, length, str, match
294
+ // - EnsureAction
@@ -0,0 +1,302 @@
1
+ // FreeLang v4.2 — Gogs CLI IssueService
2
+ // 이슈 관리 API 서비스
3
+ // 250라인: create, list, get, close, update, edit
4
+
5
+ /// 이슈 서비스
6
+ struct IssueService {
7
+ client: HttpClient
8
+ cache: CacheManager
9
+ }
10
+
11
+ /// IssueService 생성
12
+ fn new_issue_service(client: HttpClient, cache: CacheManager) -> IssueService {
13
+ IssueService {
14
+ client: client,
15
+ cache: cache
16
+ }
17
+ }
18
+
19
+ // ==================== 이슈 CRUD ====================
20
+
21
+ /// 이슈 목록 조회 (저장소 단위)
22
+ async fn list_issues(service: IssueService, owner: str, repo: str, state: str) -> Result<[Issue]> {
23
+ var cache_key = "issues:" + owner + "/" + repo + ":" + state
24
+
25
+ // 캐시 확인
26
+ var cached = cache_get(service.cache, cache_key)
27
+ if cached != "" {
28
+ println("[cache] hit: " + cache_key)
29
+ var issues_arr = json_parse(cached) as [Issue]
30
+ return Result::Ok(issues_arr)
31
+ }
32
+
33
+ // API 호출
34
+ var path = "/api/v1/repos/" + owner + "/" + repo + "/issues?state=" + state
35
+ var resp = await http_get(service.client, path)
36
+
37
+ if is_error(resp.status) {
38
+ return Result::Err(response_to_error(resp))
39
+ }
40
+
41
+ // 캐시 저장
42
+ cache_set(service.cache, cache_key, resp.body)
43
+
44
+ // 파싱
45
+ var issues_arr = json_parse(resp.body) as [Issue]
46
+ Result::Ok(issues_arr)
47
+ }
48
+
49
+ /// 이슈 상세 조회
50
+ async fn get_issue(service: IssueService, owner: str, repo: str, number: i32) -> Result<Issue> {
51
+ var cache_key = "issue:" + owner + "/" + repo + "/" + str(number)
52
+
53
+ // 캐시 확인
54
+ var cached = cache_get(service.cache, cache_key)
55
+ if cached != "" {
56
+ println("[cache] hit: " + cache_key)
57
+ var issue = json_parse(cached) as Issue
58
+ return Result::Ok(issue)
59
+ }
60
+
61
+ // API 호출
62
+ var path = "/api/v1/repos/" + owner + "/" + repo + "/issues/" + str(number)
63
+ var resp = await http_get(service.client, path)
64
+
65
+ if is_error(resp.status) {
66
+ return Result::Err(response_to_error(resp))
67
+ }
68
+
69
+ // 캐시 저장
70
+ cache_set(service.cache, cache_key, resp.body)
71
+
72
+ // 파싱
73
+ var issue = json_parse(resp.body) as Issue
74
+ Result::Ok(issue)
75
+ }
76
+
77
+ /// 이슈 생성
78
+ async fn create_issue(service: IssueService, owner: str, repo: str, req: CreateIssueRequest) -> Result<Issue> {
79
+ var path = "/api/v1/repos/" + owner + "/" + repo + "/issues"
80
+ var json_data = json_stringify(req)
81
+ var resp = await http_post_json(service.client, path, json_data)
82
+
83
+ if is_error(resp.status) {
84
+ return Result::Err(response_to_error(resp))
85
+ }
86
+
87
+ // 캐시 무효화
88
+ cache_invalidate(service.cache, "issues:" + owner + "/" + repo + ":open")
89
+ cache_invalidate(service.cache, "issues:" + owner + "/" + repo + ":all")
90
+
91
+ // 파싱
92
+ var issue = json_parse(resp.body) as Issue
93
+ Result::Ok(issue)
94
+ }
95
+
96
+ /// 이슈 업데이트
97
+ async fn update_issue(service: IssueService, owner: str, repo: str, number: i32, req: UpdateIssueRequest) -> Result<Issue> {
98
+ var path = "/api/v1/repos/" + owner + "/" + repo + "/issues/" + str(number)
99
+ var json_data = json_stringify(req)
100
+ var resp = await http_patch(service.client, path, json_data)
101
+
102
+ if is_error(resp.status) {
103
+ return Result::Err(response_to_error(resp))
104
+ }
105
+
106
+ // 캐시 무효화
107
+ var cache_key = "issue:" + owner + "/" + repo + "/" + str(number)
108
+ cache_invalidate(service.cache, cache_key)
109
+ cache_invalidate(service.cache, "issues:" + owner + "/" + repo + ":open")
110
+ cache_invalidate(service.cache, "issues:" + owner + "/" + repo + ":all")
111
+
112
+ // 파싱
113
+ var issue = json_parse(resp.body) as Issue
114
+ Result::Ok(issue)
115
+ }
116
+
117
+ /// 이슈 수정 (편의 함수)
118
+ async fn edit_issue(service: IssueService, owner: str, repo: str, number: i32, title: str, body: str) -> Result<Issue> {
119
+ var req = UpdateIssueRequest {
120
+ title: title,
121
+ body: body,
122
+ state: ""
123
+ }
124
+ await update_issue(service, owner, repo, number, req)
125
+ }
126
+
127
+ // ==================== 이슈 상태 ====================
128
+
129
+ /// 이슈 닫기
130
+ async fn close_issue(service: IssueService, owner: str, repo: str, number: i32) -> Result<Issue> {
131
+ var req = UpdateIssueRequest {
132
+ title: "",
133
+ body: "",
134
+ state: "closed"
135
+ }
136
+ await update_issue(service, owner, repo, number, req)
137
+ }
138
+
139
+ /// 이슈 다시 열기
140
+ async fn reopen_issue(service: IssueService, owner: str, repo: str, number: i32) -> Result<Issue> {
141
+ var req = UpdateIssueRequest {
142
+ title: "",
143
+ body: "",
144
+ state: "open"
145
+ }
146
+ await update_issue(service, owner, repo, number, req)
147
+ }
148
+
149
+ // ==================== 이슈 코멘트 ====================
150
+
151
+ /// 이슈 코멘트 목록
152
+ async fn list_issue_comments(service: IssueService, owner: str, repo: str, number: i32) -> Result<[IssueComment]> {
153
+ var cache_key = "comments:" + owner + "/" + repo + "/" + str(number)
154
+
155
+ // 캐시 확인
156
+ var cached = cache_get(service.cache, cache_key)
157
+ if cached != "" {
158
+ println("[cache] hit: " + cache_key)
159
+ var comments_arr = json_parse(cached) as [IssueComment]
160
+ return Result::Ok(comments_arr)
161
+ }
162
+
163
+ // API 호출
164
+ var path = "/api/v1/repos/" + owner + "/" + repo + "/issues/" + str(number) + "/comments"
165
+ var resp = await http_get(service.client, path)
166
+
167
+ if is_error(resp.status) {
168
+ return Result::Err(response_to_error(resp))
169
+ }
170
+
171
+ // 캐시 저장
172
+ cache_set(service.cache, cache_key, resp.body)
173
+
174
+ // 파싱
175
+ var comments_arr = json_parse(resp.body) as [IssueComment]
176
+ Result::Ok(comments_arr)
177
+ }
178
+
179
+ /// 이슈 코멘트 추가
180
+ async fn add_issue_comment(service: IssueService, owner: str, repo: str, number: i32, body: str) -> Result<IssueComment> {
181
+ var path = "/api/v1/repos/" + owner + "/" + repo + "/issues/" + str(number) + "/comments"
182
+ var req_body = "{\"body\": \"" + body + "\"}"
183
+ var resp = await http_post_json(service.client, path, req_body)
184
+
185
+ if is_error(resp.status) {
186
+ return Result::Err(response_to_error(resp))
187
+ }
188
+
189
+ // 캐시 무효화
190
+ var cache_key = "comments:" + owner + "/" + repo + "/" + str(number)
191
+ cache_invalidate(service.cache, cache_key)
192
+
193
+ // 파싱
194
+ var comment = json_parse(resp.body) as IssueComment
195
+ Result::Ok(comment)
196
+ }
197
+
198
+ /// 이슈 코멘트 수정
199
+ async fn update_issue_comment(service: IssueService, owner: str, repo: str, comment_id: i32, body: str) -> Result<IssueComment> {
200
+ var path = "/api/v1/repos/" + owner + "/" + repo + "/issues/comments/" + str(comment_id)
201
+ var req_body = "{\"body\": \"" + body + "\"}"
202
+ var resp = await http_patch(service.client, path, req_body)
203
+
204
+ if is_error(resp.status) {
205
+ return Result::Err(response_to_error(resp))
206
+ }
207
+
208
+ // 파싱
209
+ var comment = json_parse(resp.body) as IssueComment
210
+ Result::Ok(comment)
211
+ }
212
+
213
+ /// 이슈 코멘트 삭제
214
+ async fn delete_issue_comment(service: IssueService, owner: str, repo: str, comment_id: i32) -> Result<str> {
215
+ var path = "/api/v1/repos/" + owner + "/" + repo + "/issues/comments/" + str(comment_id)
216
+ var resp = await http_delete(service.client, path)
217
+
218
+ if is_error(resp.status) {
219
+ return Result::Err(response_to_error(resp))
220
+ }
221
+
222
+ Result::Ok("Comment deleted")
223
+ }
224
+
225
+ // ==================== 이슈 라벨 ====================
226
+
227
+ /// 이슈에 라벨 추가
228
+ async fn add_issue_label(service: IssueService, owner: str, repo: str, number: i32, label: str) -> Result<str> {
229
+ var path = "/api/v1/repos/" + owner + "/" + repo + "/issues/" + str(number) + "/labels"
230
+ var body_str = "{\"labels\": [\"" + label + "\"]}"
231
+ var resp = await http_post_json(service.client, path, body_str)
232
+
233
+ if is_error(resp.status) {
234
+ return Result::Err(response_to_error(resp))
235
+ }
236
+
237
+ // 캐시 무효화
238
+ var cache_key = "issue:" + owner + "/" + repo + "/" + str(number)
239
+ cache_invalidate(service.cache, cache_key)
240
+
241
+ Result::Ok("Label added to issue")
242
+ }
243
+
244
+ // ==================== 이슈 검색 ====================
245
+
246
+ /// 이슈 검색
247
+ async fn search_issues(service: IssueService, owner: str, repo: str, query: str) -> Result<[Issue]> {
248
+ var path = "/api/v1/repos/" + owner + "/" + repo + "/issues/search?q=" + query
249
+ var resp = await http_get(service.client, path)
250
+
251
+ if is_error(resp.status) {
252
+ return Result::Err(response_to_error(resp))
253
+ }
254
+
255
+ // 파싱
256
+ var issues_arr = json_parse(resp.body) as [Issue]
257
+ Result::Ok(issues_arr)
258
+ }
259
+
260
+ // ==================== 별명 (Alias) ====================
261
+
262
+ /// 이슈 생성 (별명)
263
+ async fn new_issue(service: IssueService, owner: str, repo: str, title: str, body: str) -> Result<Issue> {
264
+ var req = CreateIssueRequest {
265
+ title: title,
266
+ body: body
267
+ }
268
+ await create_issue(service, owner, repo, req)
269
+ }
270
+
271
+ /// 이슈 목록 조회 (열린 이슈만) (별명)
272
+ async fn list_open_issues(service: IssueService, owner: str, repo: str) -> Result<[Issue]> {
273
+ await list_issues(service, owner, repo, "open")
274
+ }
275
+
276
+ /// 이슈 목록 조회 (닫힌 이슈만) (별명)
277
+ async fn list_closed_issues(service: IssueService, owner: str, repo: str) -> Result<[Issue]> {
278
+ await list_issues(service, owner, repo, "closed")
279
+ }
280
+
281
+ // UpdateIssueRequest 모델 정의
282
+ struct UpdateIssueRequest {
283
+ title: str
284
+ body: str
285
+ state: str
286
+ }
287
+
288
+ // IssueComment 모델 정의
289
+ struct IssueComment {
290
+ id: i32
291
+ body: str
292
+ user: User
293
+ created_at: str
294
+ updated_at: str
295
+ }
296
+
297
+ // 필요한 함수들 (다른 모듈에서 임포트):
298
+ // - http_get, http_post_json, http_delete, http_patch
299
+ // - is_error, response_to_error
300
+ // - cache_get, cache_set, cache_invalidate
301
+ // - json_stringify, json_parse
302
+ // - println, length, str