@sonamu-kit/tasks 0.0.1

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 (280) hide show
  1. package/.swcrc +17 -0
  2. package/README.md +7 -0
  3. package/dist/backend.d.ts +107 -0
  4. package/dist/backend.d.ts.map +1 -0
  5. package/dist/backend.js +3 -0
  6. package/dist/backend.js.map +1 -0
  7. package/dist/chaos.test.d.ts +2 -0
  8. package/dist/chaos.test.d.ts.map +1 -0
  9. package/dist/chaos.test.js +92 -0
  10. package/dist/chaos.test.js.map +1 -0
  11. package/dist/client.d.ts +178 -0
  12. package/dist/client.d.ts.map +1 -0
  13. package/dist/client.js +223 -0
  14. package/dist/client.js.map +1 -0
  15. package/dist/client.test.d.ts +2 -0
  16. package/dist/client.test.d.ts.map +1 -0
  17. package/dist/client.test.js +339 -0
  18. package/dist/client.test.js.map +1 -0
  19. package/dist/config.d.ts +22 -0
  20. package/dist/config.d.ts.map +1 -0
  21. package/dist/config.js +23 -0
  22. package/dist/config.js.map +1 -0
  23. package/dist/config.test.d.ts +2 -0
  24. package/dist/config.test.d.ts.map +1 -0
  25. package/dist/config.test.js +24 -0
  26. package/dist/config.test.js.map +1 -0
  27. package/dist/core/duration.d.ts +22 -0
  28. package/dist/core/duration.d.ts.map +1 -0
  29. package/dist/core/duration.js +64 -0
  30. package/dist/core/duration.js.map +1 -0
  31. package/dist/core/duration.test.d.ts +2 -0
  32. package/dist/core/duration.test.d.ts.map +1 -0
  33. package/dist/core/duration.test.js +265 -0
  34. package/dist/core/duration.test.js.map +1 -0
  35. package/dist/core/error.d.ts +15 -0
  36. package/dist/core/error.d.ts.map +1 -0
  37. package/dist/core/error.js +25 -0
  38. package/dist/core/error.js.map +1 -0
  39. package/dist/core/error.test.d.ts +2 -0
  40. package/dist/core/error.test.d.ts.map +1 -0
  41. package/dist/core/error.test.js +63 -0
  42. package/dist/core/error.test.js.map +1 -0
  43. package/dist/core/json.d.ts +5 -0
  44. package/dist/core/json.d.ts.map +1 -0
  45. package/dist/core/json.js +3 -0
  46. package/dist/core/json.js.map +1 -0
  47. package/dist/core/result.d.ts +22 -0
  48. package/dist/core/result.d.ts.map +1 -0
  49. package/dist/core/result.js +22 -0
  50. package/dist/core/result.js.map +1 -0
  51. package/dist/core/result.test.d.ts +2 -0
  52. package/dist/core/result.test.d.ts.map +1 -0
  53. package/dist/core/result.test.js +19 -0
  54. package/dist/core/result.test.js.map +1 -0
  55. package/dist/core/retry.d.ts +21 -0
  56. package/dist/core/retry.d.ts.map +1 -0
  57. package/dist/core/retry.js +25 -0
  58. package/dist/core/retry.js.map +1 -0
  59. package/dist/core/retry.test.d.ts +2 -0
  60. package/dist/core/retry.test.d.ts.map +1 -0
  61. package/dist/core/retry.test.js +37 -0
  62. package/dist/core/retry.test.js.map +1 -0
  63. package/dist/core/schema.d.ts +57 -0
  64. package/dist/core/schema.d.ts.map +1 -0
  65. package/dist/core/schema.js +4 -0
  66. package/dist/core/schema.js.map +1 -0
  67. package/dist/core/step.d.ts +96 -0
  68. package/dist/core/step.d.ts.map +1 -0
  69. package/dist/core/step.js +78 -0
  70. package/dist/core/step.js.map +1 -0
  71. package/dist/core/step.test.d.ts +2 -0
  72. package/dist/core/step.test.d.ts.map +1 -0
  73. package/dist/core/step.test.js +356 -0
  74. package/dist/core/step.test.js.map +1 -0
  75. package/dist/core/workflow.d.ts +78 -0
  76. package/dist/core/workflow.d.ts.map +1 -0
  77. package/dist/core/workflow.js +46 -0
  78. package/dist/core/workflow.js.map +1 -0
  79. package/dist/core/workflow.test.d.ts +2 -0
  80. package/dist/core/workflow.test.d.ts.map +1 -0
  81. package/dist/core/workflow.test.js +172 -0
  82. package/dist/core/workflow.test.js.map +1 -0
  83. package/dist/database/backend.d.ts +60 -0
  84. package/dist/database/backend.d.ts.map +1 -0
  85. package/dist/database/backend.js +387 -0
  86. package/dist/database/backend.js.map +1 -0
  87. package/dist/database/backend.test.d.ts +2 -0
  88. package/dist/database/backend.test.d.ts.map +1 -0
  89. package/dist/database/backend.test.js +17 -0
  90. package/dist/database/backend.test.js.map +1 -0
  91. package/dist/database/backend.testsuite.d.ts +20 -0
  92. package/dist/database/backend.testsuite.d.ts.map +1 -0
  93. package/dist/database/backend.testsuite.js +1174 -0
  94. package/dist/database/backend.testsuite.js.map +1 -0
  95. package/dist/database/base.d.ts +12 -0
  96. package/dist/database/base.d.ts.map +1 -0
  97. package/dist/database/base.js +19 -0
  98. package/dist/database/base.js.map +1 -0
  99. package/dist/database/migrations/20251212000000_0_init.js +9 -0
  100. package/dist/database/migrations/20251212000000_0_init.js.map +1 -0
  101. package/dist/database/migrations/20251212000000_1_tables.js +88 -0
  102. package/dist/database/migrations/20251212000000_1_tables.js.map +1 -0
  103. package/dist/database/migrations/20251212000000_2_fk.js +48 -0
  104. package/dist/database/migrations/20251212000000_2_fk.js.map +1 -0
  105. package/dist/database/migrations/20251212000000_3_indexes.js +107 -0
  106. package/dist/database/migrations/20251212000000_3_indexes.js.map +1 -0
  107. package/dist/database/pubsub.d.ts +17 -0
  108. package/dist/database/pubsub.d.ts.map +1 -0
  109. package/dist/database/pubsub.js +70 -0
  110. package/dist/database/pubsub.js.map +1 -0
  111. package/dist/database/pubsub.test.d.ts +2 -0
  112. package/dist/database/pubsub.test.d.ts.map +1 -0
  113. package/dist/database/pubsub.test.js +86 -0
  114. package/dist/database/pubsub.test.js.map +1 -0
  115. package/dist/errors.d.ts +8 -0
  116. package/dist/errors.d.ts.map +1 -0
  117. package/dist/errors.js +21 -0
  118. package/dist/errors.js.map +1 -0
  119. package/dist/execution.d.ts +82 -0
  120. package/dist/execution.d.ts.map +1 -0
  121. package/dist/execution.js +182 -0
  122. package/dist/execution.js.map +1 -0
  123. package/dist/execution.test.d.ts +2 -0
  124. package/dist/execution.test.d.ts.map +1 -0
  125. package/dist/execution.test.js +556 -0
  126. package/dist/execution.test.js.map +1 -0
  127. package/dist/index.d.ts +8 -0
  128. package/dist/index.d.ts.map +1 -0
  129. package/dist/index.js +6 -0
  130. package/dist/index.js.map +1 -0
  131. package/dist/internal.d.ts +12 -0
  132. package/dist/internal.d.ts.map +1 -0
  133. package/dist/internal.js +5 -0
  134. package/dist/internal.js.map +1 -0
  135. package/dist/practices/01-remote-workflow.d.ts +2 -0
  136. package/dist/practices/01-remote-workflow.d.ts.map +1 -0
  137. package/dist/practices/01-remote-workflow.js +69 -0
  138. package/dist/practices/01-remote-workflow.js.map +1 -0
  139. package/dist/practices/01-remote.d.ts +2 -0
  140. package/dist/practices/01-remote.d.ts.map +1 -0
  141. package/dist/practices/01-remote.js +87 -0
  142. package/dist/practices/01-remote.js.map +1 -0
  143. package/dist/practices/02-local.d.ts +2 -0
  144. package/dist/practices/02-local.d.ts.map +1 -0
  145. package/dist/practices/02-local.js +84 -0
  146. package/dist/practices/02-local.js.map +1 -0
  147. package/dist/practices/03-local-retry.d.ts +2 -0
  148. package/dist/practices/03-local-retry.d.ts.map +1 -0
  149. package/dist/practices/03-local-retry.js +85 -0
  150. package/dist/practices/03-local-retry.js.map +1 -0
  151. package/dist/practices/04-scheduler-dispose.d.ts +2 -0
  152. package/dist/practices/04-scheduler-dispose.d.ts.map +1 -0
  153. package/dist/practices/04-scheduler-dispose.js +65 -0
  154. package/dist/practices/04-scheduler-dispose.js.map +1 -0
  155. package/dist/practices/05-router.d.ts +2 -0
  156. package/dist/practices/05-router.d.ts.map +1 -0
  157. package/dist/practices/05-router.js +80 -0
  158. package/dist/practices/05-router.js.map +1 -0
  159. package/dist/registry.d.ts +33 -0
  160. package/dist/registry.d.ts.map +1 -0
  161. package/dist/registry.js +54 -0
  162. package/dist/registry.js.map +1 -0
  163. package/dist/registry.test.d.ts +2 -0
  164. package/dist/registry.test.d.ts.map +1 -0
  165. package/dist/registry.test.js +95 -0
  166. package/dist/registry.test.js.map +1 -0
  167. package/dist/scheduler.d.ts +22 -0
  168. package/dist/scheduler.d.ts.map +1 -0
  169. package/dist/scheduler.js +117 -0
  170. package/dist/scheduler.js.map +1 -0
  171. package/dist/tasks/index.d.ts +4 -0
  172. package/dist/tasks/index.d.ts.map +1 -0
  173. package/dist/tasks/index.js +5 -0
  174. package/dist/tasks/index.js.map +1 -0
  175. package/dist/tasks/local-task.d.ts +6 -0
  176. package/dist/tasks/local-task.d.ts.map +1 -0
  177. package/dist/tasks/local-task.js +95 -0
  178. package/dist/tasks/local-task.js.map +1 -0
  179. package/dist/tasks/remote-task.d.ts +11 -0
  180. package/dist/tasks/remote-task.d.ts.map +1 -0
  181. package/dist/tasks/remote-task.js +213 -0
  182. package/dist/tasks/remote-task.js.map +1 -0
  183. package/dist/tasks/shared.d.ts +8 -0
  184. package/dist/tasks/shared.d.ts.map +1 -0
  185. package/dist/tasks/shared.js +41 -0
  186. package/dist/tasks/shared.js.map +1 -0
  187. package/dist/testing/connection.d.ts +7 -0
  188. package/dist/testing/connection.d.ts.map +1 -0
  189. package/dist/testing/connection.js +38 -0
  190. package/dist/testing/connection.js.map +1 -0
  191. package/dist/types/config.d.ts +44 -0
  192. package/dist/types/config.d.ts.map +1 -0
  193. package/dist/types/config.js +3 -0
  194. package/dist/types/config.js.map +1 -0
  195. package/dist/types/context.d.ts +18 -0
  196. package/dist/types/context.d.ts.map +1 -0
  197. package/dist/types/context.js +4 -0
  198. package/dist/types/context.js.map +1 -0
  199. package/dist/types/events.d.ts +43 -0
  200. package/dist/types/events.d.ts.map +1 -0
  201. package/dist/types/events.js +3 -0
  202. package/dist/types/events.js.map +1 -0
  203. package/dist/types/index.d.ts +6 -0
  204. package/dist/types/index.d.ts.map +1 -0
  205. package/dist/types/index.js +3 -0
  206. package/dist/types/index.js.map +1 -0
  207. package/dist/types/task-items.d.ts +12 -0
  208. package/dist/types/task-items.d.ts.map +1 -0
  209. package/dist/types/task-items.js +3 -0
  210. package/dist/types/task-items.js.map +1 -0
  211. package/dist/types/utils.d.ts +4 -0
  212. package/dist/types/utils.d.ts.map +1 -0
  213. package/dist/types/utils.js +8 -0
  214. package/dist/types/utils.js.map +1 -0
  215. package/dist/worker.d.ts +61 -0
  216. package/dist/worker.d.ts.map +1 -0
  217. package/dist/worker.js +206 -0
  218. package/dist/worker.js.map +1 -0
  219. package/dist/worker.test.d.ts +2 -0
  220. package/dist/worker.test.d.ts.map +1 -0
  221. package/dist/worker.test.js +1163 -0
  222. package/dist/worker.test.js.map +1 -0
  223. package/dist/workflow.d.ts +44 -0
  224. package/dist/workflow.d.ts.map +1 -0
  225. package/dist/workflow.js +21 -0
  226. package/dist/workflow.js.map +1 -0
  227. package/dist/workflow.test.d.ts +2 -0
  228. package/dist/workflow.test.d.ts.map +1 -0
  229. package/dist/workflow.test.js +73 -0
  230. package/dist/workflow.test.js.map +1 -0
  231. package/nodemon.json +6 -0
  232. package/package.json +63 -0
  233. package/scripts/migrate.ts +11 -0
  234. package/src/backend.ts +133 -0
  235. package/src/chaos.test.ts +108 -0
  236. package/src/client.test.ts +297 -0
  237. package/src/client.ts +331 -0
  238. package/src/config.test.ts +23 -0
  239. package/src/config.ts +35 -0
  240. package/src/core/duration.test.ts +326 -0
  241. package/src/core/duration.ts +86 -0
  242. package/src/core/error.test.ts +77 -0
  243. package/src/core/error.ts +30 -0
  244. package/src/core/json.ts +2 -0
  245. package/src/core/result.test.ts +13 -0
  246. package/src/core/result.ts +29 -0
  247. package/src/core/retry.test.ts +41 -0
  248. package/src/core/retry.ts +29 -0
  249. package/src/core/schema.ts +74 -0
  250. package/src/core/step.test.ts +362 -0
  251. package/src/core/step.ts +152 -0
  252. package/src/core/workflow.test.ts +184 -0
  253. package/src/core/workflow.ts +127 -0
  254. package/src/database/backend.test.ts +16 -0
  255. package/src/database/backend.testsuite.ts +1376 -0
  256. package/src/database/backend.ts +655 -0
  257. package/src/database/base.ts +23 -0
  258. package/src/database/migrations/20251212000000_0_init.ts +10 -0
  259. package/src/database/migrations/20251212000000_1_tables.ts +54 -0
  260. package/src/database/migrations/20251212000000_2_fk.ts +46 -0
  261. package/src/database/migrations/20251212000000_3_indexes.ts +82 -0
  262. package/src/database/pubsub.test.ts +92 -0
  263. package/src/database/pubsub.ts +92 -0
  264. package/src/execution.test.ts +508 -0
  265. package/src/execution.ts +291 -0
  266. package/src/index.ts +7 -0
  267. package/src/internal.ts +11 -0
  268. package/src/practices/01-remote-workflow.ts +61 -0
  269. package/src/registry.test.ts +122 -0
  270. package/src/registry.ts +65 -0
  271. package/src/testing/connection.ts +44 -0
  272. package/src/worker.test.ts +1138 -0
  273. package/src/worker.ts +281 -0
  274. package/src/workflow.test.ts +68 -0
  275. package/src/workflow.ts +84 -0
  276. package/table_ddl.sql +60 -0
  277. package/templates/openworkflow.config.ts +22 -0
  278. package/tsconfig.json +40 -0
  279. package/tsconfig.test.json +4 -0
  280. package/vite.config.ts +13 -0
@@ -0,0 +1,213 @@
1
+ import { findRoute } from "rou3";
2
+ import { isSonamuTaskError } from "../errors.js";
3
+ import { routedAction } from "./shared.js";
4
+ // start, stop, fetch 이벤트는 router를 잡기 전에 일어나서 별도로 분리
5
+ export async function saveUnroutedTaskEvent(knex, data) {
6
+ switch(data.type){
7
+ case "start":
8
+ return knex.insert({
9
+ event_type: data.type,
10
+ info_id: knex.fn.uuidToBin(data.info.id),
11
+ info_name: data.info.name,
12
+ timestamp: data.timestamp
13
+ }).into("sonamu_task_events");
14
+ case "stop":
15
+ return knex.insert({
16
+ event_type: data.type,
17
+ info_id: knex.fn.uuidToBin(data.info.id),
18
+ info_name: data.info.name,
19
+ timestamp: data.timestamp,
20
+ reason: data.reason,
21
+ error_message: data.error?.message,
22
+ error_stack: data.error?.stack
23
+ }).into("sonamu_task_events");
24
+ case "fetch":
25
+ return knex.insert({
26
+ event_type: data.type,
27
+ info_id: knex.fn.uuidToBin(data.info.id),
28
+ info_name: data.info.name,
29
+ timestamp: data.timestamp
30
+ }).into("sonamu_task_events");
31
+ default:
32
+ throw new Error(`Unknown task event type: ${data.type}`);
33
+ }
34
+ }
35
+ // router에서 매칭 후 생긴 이벤트를 처리
36
+ export async function saveRoutedTaskEvent(knex, router, data) {
37
+ const queries = [];
38
+ const matched = findRoute(router, "", data.task.namespace);
39
+ switch(data.type){
40
+ case "process:start":
41
+ queries.push(knex.insert({
42
+ event_type: data.type,
43
+ info_id: knex.fn.uuidToBin(data.info.id),
44
+ info_name: data.info.name,
45
+ timestamp: data.timestamp,
46
+ task_item_id: knex.fn.uuidToBin(data.task.id),
47
+ attempt: data.task.attempt
48
+ }).into("sonamu_task_events"));
49
+ break;
50
+ case "process:complete":
51
+ // sonamu_task_items의 TaskItem을 sonamu_archived_task_items로 이동
52
+ queries.push(knex.insert({
53
+ event_type: data.type,
54
+ info_id: knex.fn.uuidToBin(data.info.id),
55
+ info_name: data.info.name,
56
+ timestamp: data.timestamp,
57
+ task_item_id: knex.fn.uuidToBin(data.task.id),
58
+ attempt: data.task.attempt
59
+ }).into("sonamu_task_events"));
60
+ queries.push(knex("sonamu_task_items").where("id", knex.fn.uuidToBin(data.task.id)).delete());
61
+ queries.push(knex.insert({
62
+ id: knex.fn.uuidToBin(data.task.id),
63
+ created_at: data.task.createdAt,
64
+ completed_at: data.timestamp,
65
+ namespace: data.task.namespace,
66
+ payload: data.task.payload,
67
+ attempt: data.task.attempt,
68
+ status: "completed"
69
+ }).into("sonamu_archived_task_items"));
70
+ break;
71
+ case "process:error":
72
+ queries.push(knex.insert({
73
+ event_type: data.type,
74
+ info_id: knex.fn.uuidToBin(data.info.id),
75
+ info_name: data.info.name,
76
+ timestamp: data.timestamp,
77
+ task_item_id: knex.fn.uuidToBin(data.task.id),
78
+ attempt: data.task.attempt,
79
+ reason: data.reason,
80
+ error_message: data.error?.message,
81
+ error_stack: data.error?.stack
82
+ }).into("sonamu_task_events"));
83
+ if (data.task.attempt >= (matched?.data.retry.maxAttempts ?? 1)) {
84
+ // 최대 실행 횟수를 넘겼을 경우, 에러로 sonamu_archived_task_items로 옮김
85
+ queries.push(knex("sonamu_task_items").where("id", knex.fn.uuidToBin(data.task.id)).delete());
86
+ queries.push(knex.insert({
87
+ id: knex.fn.uuidToBin(data.task.id),
88
+ created_at: data.task.createdAt,
89
+ completed_at: data.timestamp,
90
+ namespace: data.task.namespace,
91
+ payload: data.task.payload,
92
+ attempt: data.task.attempt,
93
+ status: "error"
94
+ }).into("sonamu_archived_task_items"));
95
+ } else {
96
+ // 재시도 횟수 증가 및 상태 업데이트
97
+ queries.push(knex("sonamu_task_items").where("id", knex.fn.uuidToBin(data.task.id)).update({
98
+ status: "pending",
99
+ attempt: data.task.attempt + 1,
100
+ updated_at: data.timestamp
101
+ }));
102
+ }
103
+ break;
104
+ default:
105
+ throw new Error(`Unknown task type: ${data}`);
106
+ }
107
+ await Promise.all(queries);
108
+ }
109
+ // Scheduler에서 Remote Task가 돌아갈 때 실행되는 부분
110
+ // TODO: Retry를 지금은 서버에서 처리하기 떄문에 delay가 적용되지 않음.
111
+ export async function wrapRemoteTask(router, info, onEvent, knex) {
112
+ const trx = await knex.transaction();
113
+ await (async ()=>{
114
+ const event = {
115
+ type: "fetch",
116
+ info: info,
117
+ timestamp: new Date()
118
+ };
119
+ onEvent(event);
120
+ await saveUnroutedTaskEvent(trx, event);
121
+ })();
122
+ // NOTE: 대기 처리를 Queue를 별도로 분리한다면 where status = "pending_for_retry"를 추가하면 됨.
123
+ const rawTask = await trx.select("id", "created_at", "updated_at", "namespace", "status", "attempt", "payload").from("sonamu_task_items").forUpdate().skipLocked().limit(1).first();
124
+ if (!rawTask) {
125
+ await trx.rollback();
126
+ return;
127
+ }
128
+ const item = {
129
+ id: knex.fn.binToUuid(rawTask.id),
130
+ createdAt: rawTask.created_at,
131
+ updatedAt: rawTask.updated_at,
132
+ status: rawTask.status,
133
+ namespace: rawTask.namespace,
134
+ attempt: rawTask.attempt,
135
+ payload: rawTask.payload
136
+ };
137
+ await (async ()=>{
138
+ const event = {
139
+ type: "process:start",
140
+ task: item,
141
+ timestamp: new Date(),
142
+ info: info
143
+ };
144
+ onEvent(event);
145
+ await saveRoutedTaskEvent(trx, router, event);
146
+ })();
147
+ // Router에서 Route를 찾음
148
+ const matched = findRoute(router, "", item.namespace);
149
+ if (!matched) {
150
+ await (async ()=>{
151
+ const event = {
152
+ type: "process:error",
153
+ task: item,
154
+ timestamp: new Date(),
155
+ info: info,
156
+ reason: "no_route"
157
+ };
158
+ onEvent(event);
159
+ await saveRoutedTaskEvent(trx, router, event);
160
+ })();
161
+ await trx.commit();
162
+ return;
163
+ }
164
+ try {
165
+ await routedAction(matched, item);
166
+ await (async ()=>{
167
+ const event = {
168
+ type: "process:complete",
169
+ task: item,
170
+ timestamp: new Date(),
171
+ info: matched.data.info
172
+ };
173
+ onEvent(event);
174
+ await saveRoutedTaskEvent(trx, router, event);
175
+ })();
176
+ } catch (err) {
177
+ let evt;
178
+ if (isSonamuTaskError(err)) {
179
+ evt = {
180
+ type: "process:error",
181
+ task: item,
182
+ timestamp: new Date(),
183
+ info: matched.data.info,
184
+ reason: err.type,
185
+ error: err.cause ?? err
186
+ };
187
+ } else if (err instanceof Error) {
188
+ evt = {
189
+ type: "process:error",
190
+ task: item,
191
+ timestamp: new Date(),
192
+ info: matched.data.info,
193
+ reason: "exception",
194
+ error: err
195
+ };
196
+ } else {
197
+ evt = {
198
+ type: "process:error",
199
+ task: item,
200
+ timestamp: new Date(),
201
+ info: matched.data.info,
202
+ reason: "exception",
203
+ error: new Error(`Unknown Error: ${err}`)
204
+ };
205
+ }
206
+ onEvent(evt);
207
+ await saveRoutedTaskEvent(trx, router, evt);
208
+ } finally{
209
+ await trx.commit();
210
+ }
211
+ }
212
+
213
+ //# sourceMappingURL=remote-task.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/tasks/remote-task.ts"],"sourcesContent":["import type { Knex } from \"knex\";\nimport type {\n SchedulerInfo,\n TaskItem,\n UnroutedTaskEvent,\n RoutedTaskEvent,\n TaskRouterContext,\n TaskEvent,\n} from \"../types\";\nimport { type RouterContext, findRoute } from \"rou3\";\nimport { isSonamuTaskError } from \"../errors\";\nimport { routedAction } from \"./shared\";\n\n// start, stop, fetch 이벤트는 router를 잡기 전에 일어나서 별도로 분리\nexport async function saveUnroutedTaskEvent(knex: Knex, data: UnroutedTaskEvent) {\n switch (data.type) {\n case \"start\":\n return knex\n .insert({\n event_type: data.type,\n info_id: knex.fn.uuidToBin(data.info.id),\n info_name: data.info.name,\n timestamp: data.timestamp,\n })\n .into(\"sonamu_task_events\");\n case \"stop\":\n return knex\n .insert({\n event_type: data.type,\n info_id: knex.fn.uuidToBin(data.info.id),\n info_name: data.info.name,\n timestamp: data.timestamp,\n reason: data.reason,\n error_message: data.error?.message,\n error_stack: data.error?.stack,\n })\n .into(\"sonamu_task_events\");\n case \"fetch\":\n return knex\n .insert({\n event_type: data.type,\n info_id: knex.fn.uuidToBin(data.info.id),\n info_name: data.info.name,\n timestamp: data.timestamp,\n })\n .into(\"sonamu_task_events\");\n\n default:\n throw new Error(`Unknown task event type: ${(data as any).type}`);\n }\n}\n\n// router에서 매칭 후 생긴 이벤트를 처리\nexport async function saveRoutedTaskEvent<T extends RoutedTaskEvent>(\n knex: Knex,\n router: RouterContext<TaskRouterContext & { info: SchedulerInfo }>,\n data: T,\n) {\n const queries: Knex.QueryBuilder[] = [];\n const matched = findRoute(router, \"\", data.task.namespace);\n\n switch (data.type) {\n case \"process:start\":\n queries.push(\n knex\n .insert({\n event_type: data.type,\n info_id: knex.fn.uuidToBin(data.info.id),\n info_name: data.info.name,\n timestamp: data.timestamp,\n task_item_id: knex.fn.uuidToBin(data.task.id),\n attempt: data.task.attempt,\n })\n .into(\"sonamu_task_events\"),\n );\n break;\n\n case \"process:complete\":\n // sonamu_task_items의 TaskItem을 sonamu_archived_task_items로 이동\n queries.push(\n knex\n .insert({\n event_type: data.type,\n info_id: knex.fn.uuidToBin(data.info.id),\n info_name: data.info.name,\n timestamp: data.timestamp,\n task_item_id: knex.fn.uuidToBin(data.task.id),\n attempt: data.task.attempt,\n })\n .into(\"sonamu_task_events\"),\n );\n queries.push(knex(\"sonamu_task_items\").where(\"id\", knex.fn.uuidToBin(data.task.id)).delete());\n queries.push(\n knex\n .insert({\n id: knex.fn.uuidToBin(data.task.id),\n created_at: data.task.createdAt,\n completed_at: data.timestamp,\n namespace: data.task.namespace,\n payload: data.task.payload,\n attempt: data.task.attempt,\n status: \"completed\",\n })\n .into(\"sonamu_archived_task_items\"),\n );\n break;\n\n case \"process:error\":\n queries.push(\n knex\n .insert({\n event_type: data.type,\n info_id: knex.fn.uuidToBin(data.info.id),\n info_name: data.info.name,\n timestamp: data.timestamp,\n task_item_id: knex.fn.uuidToBin(data.task.id),\n attempt: data.task.attempt,\n reason: data.reason,\n error_message: data.error?.message,\n error_stack: data.error?.stack,\n })\n .into(\"sonamu_task_events\"),\n );\n\n if (data.task.attempt >= (matched?.data.retry.maxAttempts ?? 1)) {\n // 최대 실행 횟수를 넘겼을 경우, 에러로 sonamu_archived_task_items로 옮김\n queries.push(\n knex(\"sonamu_task_items\").where(\"id\", knex.fn.uuidToBin(data.task.id)).delete(),\n );\n\n queries.push(\n knex\n .insert({\n id: knex.fn.uuidToBin(data.task.id),\n created_at: data.task.createdAt,\n completed_at: data.timestamp,\n namespace: data.task.namespace,\n payload: data.task.payload,\n attempt: data.task.attempt,\n status: \"error\",\n })\n .into(\"sonamu_archived_task_items\"),\n );\n } else {\n // 재시도 횟수 증가 및 상태 업데이트\n queries.push(\n knex(\"sonamu_task_items\")\n .where(\"id\", knex.fn.uuidToBin(data.task.id))\n .update({\n status: \"pending\",\n attempt: data.task.attempt + 1,\n updated_at: data.timestamp,\n }),\n );\n }\n break;\n\n default:\n throw new Error(`Unknown task type: ${data}`);\n }\n\n await Promise.all(queries);\n}\n\n// Scheduler에서 Remote Task가 돌아갈 때 실행되는 부분\n// TODO: Retry를 지금은 서버에서 처리하기 떄문에 delay가 적용되지 않음.\nexport async function wrapRemoteTask(\n router: RouterContext<TaskRouterContext & { info: SchedulerInfo }>,\n info: SchedulerInfo,\n onEvent: (data: TaskEvent) => void,\n knex: Knex,\n) {\n const trx = await knex.transaction();\n await (async () => {\n const event: UnroutedTaskEvent = {\n type: \"fetch\",\n info: info,\n timestamp: new Date(),\n };\n\n onEvent(event);\n await saveUnroutedTaskEvent(trx, event);\n })();\n\n // NOTE: 대기 처리를 Queue를 별도로 분리한다면 where status = \"pending_for_retry\"를 추가하면 됨.\n const rawTask = await trx\n .select(\"id\", \"created_at\", \"updated_at\", \"namespace\", \"status\", \"attempt\", \"payload\")\n .from(\"sonamu_task_items\")\n .forUpdate()\n .skipLocked()\n .limit(1)\n .first();\n\n if (!rawTask) {\n await trx.rollback();\n return;\n }\n\n const item: TaskItem = {\n id: knex.fn.binToUuid(rawTask.id),\n createdAt: rawTask.created_at,\n updatedAt: rawTask.updated_at,\n status: rawTask.status,\n namespace: rawTask.namespace,\n attempt: rawTask.attempt,\n payload: rawTask.payload,\n };\n\n await (async () => {\n const event: RoutedTaskEvent = {\n type: \"process:start\",\n task: item,\n timestamp: new Date(),\n info: info,\n };\n\n onEvent(event);\n await saveRoutedTaskEvent(trx, router, event);\n })();\n\n // Router에서 Route를 찾음\n const matched = findRoute(router, \"\", item.namespace);\n if (!matched) {\n await (async () => {\n const event: RoutedTaskEvent = {\n type: \"process:error\",\n task: item,\n timestamp: new Date(),\n info: info,\n reason: \"no_route\",\n };\n\n onEvent(event);\n await saveRoutedTaskEvent(trx, router, event);\n })();\n await trx.commit();\n return;\n }\n\n try {\n await routedAction(matched, item);\n await (async () => {\n const event: RoutedTaskEvent = {\n type: \"process:complete\",\n task: item,\n timestamp: new Date(),\n info: matched.data.info,\n };\n\n onEvent(event);\n await saveRoutedTaskEvent(trx, router, event);\n })();\n } catch (err) {\n let evt: RoutedTaskEvent;\n if (isSonamuTaskError(err)) {\n evt = {\n type: \"process:error\",\n task: item,\n timestamp: new Date(),\n info: matched.data.info,\n reason: err.type,\n error: err.cause ?? err,\n };\n } else if (err instanceof Error) {\n evt = {\n type: \"process:error\",\n task: item,\n timestamp: new Date(),\n info: matched.data.info,\n reason: \"exception\",\n error: err,\n };\n } else {\n evt = {\n type: \"process:error\",\n task: item,\n timestamp: new Date(),\n info: matched.data.info,\n reason: \"exception\",\n error: new Error(`Unknown Error: ${err}`),\n };\n }\n\n onEvent(evt);\n await saveRoutedTaskEvent(trx, router, evt);\n } finally {\n await trx.commit();\n }\n}\n"],"names":["findRoute","isSonamuTaskError","routedAction","saveUnroutedTaskEvent","knex","data","type","insert","event_type","info_id","fn","uuidToBin","info","id","info_name","name","timestamp","into","reason","error_message","error","message","error_stack","stack","Error","saveRoutedTaskEvent","router","queries","matched","task","namespace","push","task_item_id","attempt","where","delete","created_at","createdAt","completed_at","payload","status","retry","maxAttempts","update","updated_at","Promise","all","wrapRemoteTask","onEvent","trx","transaction","event","Date","rawTask","select","from","forUpdate","skipLocked","limit","first","rollback","item","binToUuid","updatedAt","commit","err","evt","cause"],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import type { MatchedRoute } from "rou3";
2
+ import type { TaskItem, TaskRouterContext, SchedulerInfo } from "../types";
3
+ import { z } from "zod";
4
+ export declare function convertTo<T extends z.ZodType>(schema: T, payload: Buffer | string | any): Promise<z.infer<T>>;
5
+ export declare function routedAction(matched: MatchedRoute<TaskRouterContext & {
6
+ info: SchedulerInfo;
7
+ }>, taskInfo: TaskItem): Promise<void>;
8
+ //# sourceMappingURL=shared.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../src/tasks/shared.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,MAAM,CAAC;AACzC,OAAO,KAAK,EAAE,QAAQ,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAE3E,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,wBAAsB,SAAS,CAAC,CAAC,SAAS,CAAC,CAAC,OAAO,EACjD,MAAM,EAAE,CAAC,EACT,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,GAAG,GAC7B,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAkBrB;AAGD,wBAAsB,YAAY,CAChC,OAAO,EAAE,YAAY,CAAC,iBAAiB,GAAG;IAAE,IAAI,EAAE,aAAa,CAAA;CAAE,CAAC,EAClE,QAAQ,EAAE,QAAQ,iBAsBnB"}
@@ -0,0 +1,41 @@
1
+ import { SonamuTaskError } from "../errors.js";
2
+ // UTF-8 JSON Buffer를 Zod Type으로 변환하는 함수.
3
+ // NOTE: 여기를 잘 처리하면 serialize/parse를 JSON이 아닌 형식으로도 변환할 수 있음.
4
+ export async function convertTo(schema, payload) {
5
+ try {
6
+ if (payload instanceof Buffer) {
7
+ return schema.parseAsync(JSON.parse(payload.toString()));
8
+ }
9
+ if (typeof payload === "string") {
10
+ return schema.parseAsync(JSON.parse(payload));
11
+ }
12
+ return schema.parseAsync(payload);
13
+ } catch (error) {
14
+ if (error instanceof Error) {
15
+ throw new SonamuTaskError("validation", error);
16
+ }
17
+ throw new SonamuTaskError("validation");
18
+ }
19
+ }
20
+ // Router에서 매칭 후, 모든 TaskItem의 처리
21
+ export async function routedAction(matched, taskInfo) {
22
+ const { data: ctx, params } = matched;
23
+ // 재시도 횟수를 체크함.
24
+ if (ctx.retry.maxAttempts < taskInfo.attempt) {
25
+ throw new SonamuTaskError("max_retries_exceeded");
26
+ }
27
+ // 데이터를 파싱해서 context를 만들고 실제 route 함수를 실행.
28
+ const result = ctx.target({
29
+ taskItem: {
30
+ ...taskInfo,
31
+ payload: await convertTo(ctx.schema, taskInfo.payload)
32
+ },
33
+ params,
34
+ retry: ctx.retry
35
+ });
36
+ if (result instanceof Promise) {
37
+ await result;
38
+ }
39
+ }
40
+
41
+ //# sourceMappingURL=shared.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/tasks/shared.ts"],"sourcesContent":["import type { MatchedRoute } from \"rou3\";\nimport type { TaskItem, TaskRouterContext, SchedulerInfo } from \"../types\";\nimport { SonamuTaskError } from \"../errors\";\nimport { z } from \"zod\";\n\n// UTF-8 JSON Buffer를 Zod Type으로 변환하는 함수.\n// NOTE: 여기를 잘 처리하면 serialize/parse를 JSON이 아닌 형식으로도 변환할 수 있음.\nexport async function convertTo<T extends z.ZodType>(\n schema: T,\n payload: Buffer | string | any,\n): Promise<z.infer<T>> {\n try {\n if (payload instanceof Buffer) {\n return schema.parseAsync(JSON.parse(payload.toString()));\n }\n\n if (typeof payload === \"string\") {\n return schema.parseAsync(JSON.parse(payload));\n }\n\n return schema.parseAsync(payload);\n } catch (error) {\n if (error instanceof Error) {\n throw new SonamuTaskError(\"validation\", error);\n }\n\n throw new SonamuTaskError(\"validation\");\n }\n}\n\n// Router에서 매칭 후, 모든 TaskItem의 처리\nexport async function routedAction(\n matched: MatchedRoute<TaskRouterContext & { info: SchedulerInfo }>,\n taskInfo: TaskItem,\n) {\n const { data: ctx, params } = matched;\n\n // 재시도 횟수를 체크함.\n if (ctx.retry.maxAttempts < taskInfo.attempt) {\n throw new SonamuTaskError(\"max_retries_exceeded\");\n }\n\n // 데이터를 파싱해서 context를 만들고 실제 route 함수를 실행.\n const result = ctx.target({\n taskItem: {\n ...taskInfo,\n payload: await convertTo(ctx.schema, taskInfo.payload),\n },\n params,\n retry: ctx.retry,\n });\n\n if (result instanceof Promise) {\n await result;\n }\n}\n"],"names":["SonamuTaskError","convertTo","schema","payload","Buffer","parseAsync","JSON","parse","toString","error","Error","routedAction","matched","taskInfo","data","ctx","params","retry","maxAttempts","attempt","result","target","taskItem","Promise"],"mappings":"AAEA,SAASA,eAAe,QAAQ,eAAY;AAG5C,yCAAyC;AACzC,6DAA6D;AAC7D,OAAO,eAAeC,UACpBC,MAAS,EACTC,OAA8B;IAE9B,IAAI;QACF,IAAIA,mBAAmBC,QAAQ;YAC7B,OAAOF,OAAOG,UAAU,CAACC,KAAKC,KAAK,CAACJ,QAAQK,QAAQ;QACtD;QAEA,IAAI,OAAOL,YAAY,UAAU;YAC/B,OAAOD,OAAOG,UAAU,CAACC,KAAKC,KAAK,CAACJ;QACtC;QAEA,OAAOD,OAAOG,UAAU,CAACF;IAC3B,EAAE,OAAOM,OAAO;QACd,IAAIA,iBAAiBC,OAAO;YAC1B,MAAM,IAAIV,gBAAgB,cAAcS;QAC1C;QAEA,MAAM,IAAIT,gBAAgB;IAC5B;AACF;AAEA,iCAAiC;AACjC,OAAO,eAAeW,aACpBC,OAAkE,EAClEC,QAAkB;IAElB,MAAM,EAAEC,MAAMC,GAAG,EAAEC,MAAM,EAAE,GAAGJ;IAE9B,eAAe;IACf,IAAIG,IAAIE,KAAK,CAACC,WAAW,GAAGL,SAASM,OAAO,EAAE;QAC5C,MAAM,IAAInB,gBAAgB;IAC5B;IAEA,0CAA0C;IAC1C,MAAMoB,SAASL,IAAIM,MAAM,CAAC;QACxBC,UAAU;YACR,GAAGT,QAAQ;YACXV,SAAS,MAAMF,UAAUc,IAAIb,MAAM,EAAEW,SAASV,OAAO;QACvD;QACAa;QACAC,OAAOF,IAAIE,KAAK;IAClB;IAEA,IAAIG,kBAAkBG,SAAS;QAC7B,MAAMH;IACR;AACF"}
@@ -0,0 +1,7 @@
1
+ import { type Knex } from "knex";
2
+ import { BackendPostgres } from "../database/backend";
3
+ export declare const KNEX_GLOBAL_CONFIG: Knex.Config;
4
+ export declare function migrate(): Promise<void>;
5
+ export declare function createBackend(): Promise<BackendPostgres>;
6
+ export declare function stopBackend(): Promise<void>;
7
+ //# sourceMappingURL=connection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../../src/testing/connection.ts"],"names":[],"mappings":"AACA,OAAa,EAAE,KAAK,IAAI,EAAE,MAAM,MAAM,CAAC;AACvC,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAKtD,eAAO,MAAM,kBAAkB,EAAE,IAAI,CAAC,MAY5B,CAAC;AAEX,wBAAsB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAE7C;AAED,wBAAsB,aAAa,IAAI,OAAO,CAAC,eAAe,CAAC,CAU9D;AAED,wBAAsB,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAMjD"}
@@ -0,0 +1,38 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import knex from "knex";
3
+ import { BackendPostgres } from "../database/backend.js";
4
+ import { migrate as baseMigrate, DEFAULT_SCHEMA } from "../database/base.js";
5
+ let backend = null;
6
+ export const KNEX_GLOBAL_CONFIG = {
7
+ client: "pg",
8
+ connection: {
9
+ host: "127.0.0.1",
10
+ port: 5432,
11
+ user: "postgres",
12
+ password: "miomock123",
13
+ database: "postgres"
14
+ },
15
+ pool: {
16
+ max: 50
17
+ }
18
+ };
19
+ export async function migrate() {
20
+ await baseMigrate(knex(KNEX_GLOBAL_CONFIG), DEFAULT_SCHEMA);
21
+ }
22
+ export async function createBackend() {
23
+ if (backend) {
24
+ return backend;
25
+ }
26
+ backend = await BackendPostgres.connect(KNEX_GLOBAL_CONFIG, {
27
+ namespaceId: randomUUID()
28
+ });
29
+ return backend;
30
+ }
31
+ export async function stopBackend() {
32
+ if (backend) {
33
+ await backend.stop();
34
+ }
35
+ backend = null;
36
+ }
37
+
38
+ //# sourceMappingURL=connection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/testing/connection.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport knex, { type Knex } from \"knex\";\nimport { BackendPostgres } from \"../database/backend\";\nimport { migrate as baseMigrate, DEFAULT_SCHEMA } from \"../database/base\";\n\nlet backend: BackendPostgres | null = null;\n\nexport const KNEX_GLOBAL_CONFIG: Knex.Config = {\n client: \"pg\",\n connection: {\n host: \"127.0.0.1\",\n port: 5432,\n user: \"postgres\",\n password: \"miomock123\",\n database: \"postgres\",\n },\n pool: {\n max: 50,\n },\n} as const;\n\nexport async function migrate(): Promise<void> {\n await baseMigrate(knex(KNEX_GLOBAL_CONFIG), DEFAULT_SCHEMA);\n}\n\nexport async function createBackend(): Promise<BackendPostgres> {\n if (backend) {\n return backend;\n }\n\n backend = await BackendPostgres.connect(KNEX_GLOBAL_CONFIG, {\n namespaceId: randomUUID(),\n });\n\n return backend;\n}\n\nexport async function stopBackend(): Promise<void> {\n if (backend) {\n await backend.stop();\n }\n\n backend = null;\n}\n"],"names":["randomUUID","knex","BackendPostgres","migrate","baseMigrate","DEFAULT_SCHEMA","backend","KNEX_GLOBAL_CONFIG","client","connection","host","port","user","password","database","pool","max","createBackend","connect","namespaceId","stopBackend","stop"],"mappings":"AAAA,SAASA,UAAU,QAAQ,cAAc;AACzC,OAAOC,UAAyB,OAAO;AACvC,SAASC,eAAe,QAAQ,yBAAsB;AACtD,SAASC,WAAWC,WAAW,EAAEC,cAAc,QAAQ,sBAAmB;AAE1E,IAAIC,UAAkC;AAEtC,OAAO,MAAMC,qBAAkC;IAC7CC,QAAQ;IACRC,YAAY;QACVC,MAAM;QACNC,MAAM;QACNC,MAAM;QACNC,UAAU;QACVC,UAAU;IACZ;IACAC,MAAM;QACJC,KAAK;IACP;AACF,EAAW;AAEX,OAAO,eAAeb;IACpB,MAAMC,YAAYH,KAAKM,qBAAqBF;AAC9C;AAEA,OAAO,eAAeY;IACpB,IAAIX,SAAS;QACX,OAAOA;IACT;IAEAA,UAAU,MAAMJ,gBAAgBgB,OAAO,CAACX,oBAAoB;QAC1DY,aAAanB;IACf;IAEA,OAAOM;AACT;AAEA,OAAO,eAAec;IACpB,IAAId,SAAS;QACX,MAAMA,QAAQe,IAAI;IACpB;IAEAf,UAAU;AACZ"}
@@ -0,0 +1,44 @@
1
+ import type { Knex } from "knex";
2
+ import type { UnroutedTaskEvent } from "./events";
3
+ import type { z } from "zod";
4
+ import type { Duration } from "date-fns";
5
+ import type { TaskRouterContext } from "./context";
6
+ export interface RetryConfig {
7
+ maxAttempts: number;
8
+ delay?: Duration | ((attempt: number) => Duration);
9
+ }
10
+ export type OnEventFunction<T extends UnroutedTaskEvent = UnroutedTaskEvent> = (event: T) => void | Promise<void>;
11
+ export interface RemoteTaskConfig {
12
+ type: "remote";
13
+ expression: string;
14
+ options?: {
15
+ timezone?: string;
16
+ name?: string;
17
+ noOverlap?: boolean;
18
+ maxExecutions?: number;
19
+ maxRandomDelay?: number;
20
+ };
21
+ }
22
+ export interface LocalTaskConfig<T extends z.ZodType = z.ZodType> {
23
+ type: "local";
24
+ expression: string;
25
+ namespace: string;
26
+ payload: Buffer | z.infer<T>;
27
+ options?: {
28
+ timezone?: string;
29
+ name?: string;
30
+ noOverlap?: boolean;
31
+ maxExecutions?: number;
32
+ maxRandomDelay?: number;
33
+ };
34
+ }
35
+ export interface SchedulerConfig {
36
+ database: Knex.Config;
37
+ name?: string;
38
+ tasks: (RemoteTaskConfig | LocalTaskConfig)[];
39
+ retry?: RetryConfig;
40
+ routes: (Omit<TaskRouterContext, "retry"> & {
41
+ retry?: RetryConfig;
42
+ })[];
43
+ }
44
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/types/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AACjC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAClD,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAC7B,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAEnD,MAAM,WAAW,WAAW;IAE1B,WAAW,EAAE,MAAM,CAAC;IAEpB,KAAK,CAAC,EAAE,QAAQ,GAAG,CAAC,CAAC,OAAO,EAAE,MAAM,KAAK,QAAQ,CAAC,CAAC;CACpD;AAED,MAAM,MAAM,eAAe,CAAC,CAAC,SAAS,iBAAiB,GAAG,iBAAiB,IAAI,CAC7E,KAAK,EAAE,CAAC,KACL,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAG1B,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,QAAQ,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE;QACR,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,SAAS,CAAC,EAAE,OAAO,CAAC;QACpB,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,cAAc,CAAC,EAAE,MAAM,CAAC;KACzB,CAAC;CACH;AAGD,MAAM,WAAW,eAAe,CAAC,CAAC,SAAS,CAAC,CAAC,OAAO,GAAG,CAAC,CAAC,OAAO;IAC9D,IAAI,EAAE,OAAO,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IAEnB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAC7B,OAAO,CAAC,EAAE;QACR,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,SAAS,CAAC,EAAE,OAAO,CAAC;QACpB,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,cAAc,CAAC,EAAE,MAAM,CAAC;KACzB,CAAC;CACH;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC;IAGtB,IAAI,CAAC,EAAE,MAAM,CAAC;IAGd,KAAK,EAAE,CAAC,gBAAgB,GAAG,eAAe,CAAC,EAAE,CAAC;IAG9C,KAAK,CAAC,EAAE,WAAW,CAAC;IAGpB,MAAM,EAAE,CAAC,IAAI,CAAC,iBAAiB,EAAE,OAAO,CAAC,GAAG;QAAE,KAAK,CAAC,EAAE,WAAW,CAAA;KAAE,CAAC,EAAE,CAAC;CACxE"}
@@ -0,0 +1,3 @@
1
+ export { };
2
+
3
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/types/config.ts"],"sourcesContent":["import type { Knex } from \"knex\";\nimport type { UnroutedTaskEvent } from \"./events\";\nimport type { z } from \"zod\";\nimport type { Duration } from \"date-fns\";\nimport type { TaskRouterContext } from \"./context\";\n\nexport interface RetryConfig {\n // 최대 횟수 (def: 1, 재시도를 안함)\n maxAttempts: number;\n // 재시도 간격\n delay?: Duration | ((attempt: number) => Duration);\n}\n\nexport type OnEventFunction<T extends UnroutedTaskEvent = UnroutedTaskEvent> = (\n event: T,\n) => void | Promise<void>;\n\n// MySQL에서 받아와서 있으면 실행할 태스크\nexport interface RemoteTaskConfig {\n type: \"remote\";\n expression: string;\n options?: {\n timezone?: string;\n name?: string;\n noOverlap?: boolean;\n maxExecutions?: number;\n maxRandomDelay?: number;\n };\n}\n\n// 로컬에서 router로 잡아서 실행할 태스크\nexport interface LocalTaskConfig<T extends z.ZodType = z.ZodType> {\n type: \"local\";\n expression: string;\n // router 태울 namespace\n namespace: string;\n payload: Buffer | z.infer<T>;\n options?: {\n timezone?: string;\n name?: string;\n noOverlap?: boolean;\n maxExecutions?: number;\n maxRandomDelay?: number;\n };\n}\n\nexport interface SchedulerConfig {\n database: Knex.Config;\n\n // TaskNode에 이름을 지정할 수 있음\n name?: string;\n\n // Task 설정\n tasks: (RemoteTaskConfig | LocalTaskConfig)[];\n\n // 전역적 재시도 설정 (없으면 각 Task의 설정을 따름)\n retry?: RetryConfig;\n\n // 어느 Namespace의 Task를 어떻게 처리할지\n routes: (Omit<TaskRouterContext, \"retry\"> & { retry?: RetryConfig })[];\n}\n"],"names":[],"mappings":"AA8CA,WAcC"}
@@ -0,0 +1,18 @@
1
+ import type { z } from "zod";
2
+ import type { RetryConfig } from "./config";
3
+ import type { TaskItem } from "./task-items";
4
+ import type { Callback } from "./utils";
5
+ export interface TaskRouterContext<T extends z.ZodType = z.ZodType> {
6
+ path: string;
7
+ retry: RetryConfig;
8
+ schema: T;
9
+ target: Callback<TaskContext<T>, void>;
10
+ }
11
+ export type TaskContext<T extends z.ZodType = z.ZodType> = {
12
+ params?: Record<string, string>;
13
+ retry: RetryConfig;
14
+ taskItem: Omit<TaskItem, "payload"> & {
15
+ payload: z.infer<T>;
16
+ };
17
+ };
18
+ //# sourceMappingURL=context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/types/context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAC7B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAC5C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAGxC,MAAM,WAAW,iBAAiB,CAAC,CAAC,SAAS,CAAC,CAAC,OAAO,GAAG,CAAC,CAAC,OAAO;IAChE,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,WAAW,CAAC;IACnB,MAAM,EAAE,CAAC,CAAC;IACV,MAAM,EAAE,QAAQ,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;CACxC;AAGD,MAAM,MAAM,WAAW,CAAC,CAAC,SAAS,CAAC,CAAC,OAAO,GAAG,CAAC,CAAC,OAAO,IAAI;IACzD,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,KAAK,EAAE,WAAW,CAAC;IACnB,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,SAAS,CAAC,GAAG;QAAE,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;KAAE,CAAC;CAC/D,CAAC"}
@@ -0,0 +1,4 @@
1
+ // 각 Task Route 함수가 가지고 있을 Context 정보
2
+ export { };
3
+
4
+ //# sourceMappingURL=context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/types/context.ts"],"sourcesContent":["import type { z } from \"zod\";\nimport type { RetryConfig } from \"./config\";\nimport type { TaskItem } from \"./task-items\";\nimport type { Callback } from \"./utils\";\n\n// Router에서 가지고 있을 Context 정보\nexport interface TaskRouterContext<T extends z.ZodType = z.ZodType> {\n path: string;\n retry: RetryConfig;\n schema: T;\n target: Callback<TaskContext<T>, void>;\n}\n\n// 각 Task Route 함수가 가지고 있을 Context 정보\nexport type TaskContext<T extends z.ZodType = z.ZodType> = {\n params?: Record<string, string>;\n retry: RetryConfig;\n taskItem: Omit<TaskItem, \"payload\"> & { payload: z.infer<T> };\n};\n"],"names":[],"mappings":"AAaA,qCAAqC;AACrC,WAIE"}
@@ -0,0 +1,43 @@
1
+ import type { TaskItem } from "./task-items";
2
+ export type EventType = "start" | "stop" | "fetch" | "process:start" | "process:error" | "process:complete";
3
+ export type TerminationReasonEnum = "app_shutdown" | "process_signal" | "unknown";
4
+ export type ProcessErrorReasonEnum = "no_route" | "validation" | "timeout" | "max_retries_exceeded" | "exception";
5
+ export interface SchedulerInfo {
6
+ id: string;
7
+ name?: string;
8
+ }
9
+ interface BaseNodeEvent {
10
+ type: EventType;
11
+ info: SchedulerInfo;
12
+ timestamp: Date;
13
+ }
14
+ export interface StartEvent extends BaseNodeEvent {
15
+ type: "start";
16
+ }
17
+ export interface StopEvent extends BaseNodeEvent {
18
+ type: "stop";
19
+ reason: TerminationReasonEnum;
20
+ error?: Error;
21
+ }
22
+ export interface FetchEvent extends BaseNodeEvent {
23
+ type: "fetch";
24
+ }
25
+ export interface ProcessStartEvent extends BaseNodeEvent {
26
+ type: "process:start";
27
+ task: TaskItem;
28
+ }
29
+ export interface ProcessCompleteEvent extends BaseNodeEvent {
30
+ type: "process:complete";
31
+ task: TaskItem;
32
+ }
33
+ export interface ProcessErrorEvent extends BaseNodeEvent {
34
+ type: "process:error";
35
+ task: TaskItem;
36
+ reason: ProcessErrorReasonEnum;
37
+ error?: Error;
38
+ }
39
+ export type RoutedTaskEvent = ProcessStartEvent | ProcessCompleteEvent | ProcessErrorEvent;
40
+ export type UnroutedTaskEvent = StartEvent | StopEvent | FetchEvent;
41
+ export type TaskEvent = UnroutedTaskEvent | RoutedTaskEvent;
42
+ export {};
43
+ //# sourceMappingURL=events.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../../src/types/events.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAG7C,MAAM,MAAM,SAAS,GACjB,OAAO,GACP,MAAM,GACN,OAAO,GACP,eAAe,GACf,eAAe,GACf,kBAAkB,CAAC;AAMvB,MAAM,MAAM,qBAAqB,GAAG,cAAc,GAAG,gBAAgB,GAAG,SAAS,CAAC;AAQlF,MAAM,MAAM,sBAAsB,GAC9B,UAAU,GACV,YAAY,GACZ,SAAS,GACT,sBAAsB,GACtB,WAAW,CAAC;AAGhB,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,UAAU,aAAa;IACrB,IAAI,EAAE,SAAS,CAAC;IAChB,IAAI,EAAE,aAAa,CAAC;IACpB,SAAS,EAAE,IAAI,CAAC;CACjB;AAGD,MAAM,WAAW,UAAW,SAAQ,aAAa;IAC/C,IAAI,EAAE,OAAO,CAAC;CACf;AAGD,MAAM,WAAW,SAAU,SAAQ,aAAa;IAC9C,IAAI,EAAE,MAAM,CAAC;IAEb,MAAM,EAAE,qBAAqB,CAAC;IAE9B,KAAK,CAAC,EAAE,KAAK,CAAC;CACf;AAGD,MAAM,WAAW,UAAW,SAAQ,aAAa;IAC/C,IAAI,EAAE,OAAO,CAAC;CACf;AAGD,MAAM,WAAW,iBAAkB,SAAQ,aAAa;IACtD,IAAI,EAAE,eAAe,CAAC;IACtB,IAAI,EAAE,QAAQ,CAAC;CAChB;AAGD,MAAM,WAAW,oBAAqB,SAAQ,aAAa;IACzD,IAAI,EAAE,kBAAkB,CAAC;IACzB,IAAI,EAAE,QAAQ,CAAC;CAChB;AAGD,MAAM,WAAW,iBAAkB,SAAQ,aAAa;IACtD,IAAI,EAAE,eAAe,CAAC;IACtB,IAAI,EAAE,QAAQ,CAAC;IACf,MAAM,EAAE,sBAAsB,CAAC;IAE/B,KAAK,CAAC,EAAE,KAAK,CAAC;CACf;AAED,MAAM,MAAM,eAAe,GAAG,iBAAiB,GAAG,oBAAoB,GAAG,iBAAiB,CAAC;AAC3F,MAAM,MAAM,iBAAiB,GAAG,UAAU,GAAG,SAAS,GAAG,UAAU,CAAC;AACpE,MAAM,MAAM,SAAS,GAAG,iBAAiB,GAAG,eAAe,CAAC"}
@@ -0,0 +1,3 @@
1
+ export { };
2
+
3
+ //# sourceMappingURL=events.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/types/events.ts"],"sourcesContent":["import type { TaskItem } from \"./task-items\";\n\n// Event에는 시간, Event 타입, Task에 대한 namespace, Task ID, 처리한 Task의 노드 이름, 이벤트에 대한 메타 정보 데이터 등이 들어감.\nexport type EventType =\n | \"start\"\n | \"stop\"\n | \"fetch\"\n | \"process:start\"\n | \"process:error\"\n | \"process:complete\";\n\n// Task가 왜 중단되는지를 남김.\n// - app_shutdown: 애플리케이션의 정상적인 종료\n// - process_signal: 외부에서의 프로세스 시그널로 인한 중단\n// - unknown: 모름, 그래서 call stack을 남겨야함.\nexport type TerminationReasonEnum = \"app_shutdown\" | \"process_signal\" | \"unknown\";\n\n// Process Error 사유\n// - no_route: Task가 이 namespace를 지원하지 않음\n// - serialization: 인자 등 데이터의 검증에 실패함\n// - timeout: 처리 중 타임아웃\n// - max_retries_exceeded: 최대 재시도 횟수 초과\n// - exception: 다른 예외 발생\nexport type ProcessErrorReasonEnum =\n | \"no_route\"\n | \"validation\"\n | \"timeout\"\n | \"max_retries_exceeded\"\n | \"exception\";\n\n// id는 자동으로 생성된 uuidv7, 이름은 설정을 통해 명시적으로 지정 가능\nexport interface SchedulerInfo {\n id: string;\n name?: string;\n}\n\ninterface BaseNodeEvent {\n type: EventType;\n info: SchedulerInfo;\n timestamp: Date;\n}\n\n// Task가 시작될 때 생기는 이벤트\nexport interface StartEvent extends BaseNodeEvent {\n type: \"start\";\n}\n\n// Task가 종료될 때 생기는 이벤트\nexport interface StopEvent extends BaseNodeEvent {\n type: \"stop\";\n // Task가 종료된 이유\n reason: TerminationReasonEnum;\n // StopEvent.type가 unknown일 때는 call stack을 기록\n error?: Error;\n}\n\n// Remote Task에서 Task Item을 가져올 때 생기는 이벤트\nexport interface FetchEvent extends BaseNodeEvent {\n type: \"fetch\";\n}\n\n// Task에서 TaskItem 처리를 시작할 때 생기는 이벤트\nexport interface ProcessStartEvent extends BaseNodeEvent {\n type: \"process:start\";\n task: TaskItem;\n}\n\n// Task에서 TaskItem 처리를 완료했을 때 생기는 이벤트\nexport interface ProcessCompleteEvent extends BaseNodeEvent {\n type: \"process:complete\";\n task: TaskItem;\n}\n\n// Task에서 TaskItem 처리를 실패했을 때 생기는 이벤트\nexport interface ProcessErrorEvent extends BaseNodeEvent {\n type: \"process:error\";\n task: TaskItem;\n reason: ProcessErrorReasonEnum;\n // ProcessErrorEvent.type가 no_route, timeout, max_retries_exceeded이 아닐 때는 call stack을 기록\n error?: Error;\n}\n\nexport type RoutedTaskEvent = ProcessStartEvent | ProcessCompleteEvent | ProcessErrorEvent;\nexport type UnroutedTaskEvent = StartEvent | StopEvent | FetchEvent;\nexport type TaskEvent = UnroutedTaskEvent | RoutedTaskEvent;\n"],"names":[],"mappings":"AAoFA,WAA4D"}
@@ -0,0 +1,6 @@
1
+ export * from "./utils";
2
+ export type * from "./config";
3
+ export type * from "./context";
4
+ export type * from "./events";
5
+ export type * from "./task-items";
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,mBAAmB,UAAU,CAAC;AAC9B,mBAAmB,WAAW,CAAC;AAC/B,mBAAmB,UAAU,CAAC;AAC9B,mBAAmB,cAAc,CAAC"}
@@ -0,0 +1,3 @@
1
+ export * from "./utils.js";
2
+
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/types/index.ts"],"sourcesContent":["export * from \"./utils\";\nexport type * from \"./config\";\nexport type * from \"./context\";\nexport type * from \"./events\";\nexport type * from \"./task-items\";\n"],"names":[],"mappings":"AAAA,cAAc,aAAU"}
@@ -0,0 +1,12 @@
1
+ import type { z } from "zod";
2
+ export type TaskItemState = "pending" | "error" | "completed";
3
+ export interface TaskItem<T extends z.ZodType = z.ZodType> {
4
+ id: string;
5
+ createdAt: Date;
6
+ updatedAt: Date;
7
+ status: TaskItemState;
8
+ namespace: string;
9
+ attempt: number;
10
+ payload: Buffer | z.infer<T>;
11
+ }
12
+ //# sourceMappingURL=task-items.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"task-items.d.ts","sourceRoot":"","sources":["../../src/types/task-items.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAO7B,MAAM,MAAM,aAAa,GAAG,SAAS,GAAG,OAAO,GAAG,WAAW,CAAC;AAE9D,MAAM,WAAW,QAAQ,CAAC,CAAC,SAAS,CAAC,CAAC,OAAO,GAAG,CAAC,CAAC,OAAO;IACvD,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,IAAI,CAAC;IAChB,SAAS,EAAE,IAAI,CAAC;IAChB,MAAM,EAAE,aAAa,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAEhB,OAAO,EAAE,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;CAC9B"}
@@ -0,0 +1,3 @@
1
+ export { };
2
+
3
+ //# sourceMappingURL=task-items.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/types/task-items.ts"],"sourcesContent":["import type { z } from \"zod\";\n\n// Task 상태\n// - pending: 대기 중\n// - pending_for_retry: 재시도 대기 중\n// - error: 처리 중 에러 발생 (처리 종료)\n// - completed: 처리 완료\nexport type TaskItemState = \"pending\" | \"error\" | \"completed\";\n\nexport interface TaskItem<T extends z.ZodType = z.ZodType> {\n id: string;\n createdAt: Date;\n updatedAt: Date;\n status: TaskItemState;\n namespace: string;\n attempt: number;\n // Task의 payload.\n payload: Buffer | z.infer<T>;\n}\n"],"names":[],"mappings":"AASA,WASC"}
@@ -0,0 +1,4 @@
1
+ export type Resolvable<T> = T | Promise<T> | (() => T | Promise<T>);
2
+ export type Callback<TArg, TReturn> = ((arg: TArg) => TReturn) | ((arg: TArg) => Promise<TReturn>);
3
+ export declare function resolve<T>(value: Resolvable<T>): Promise<T>;
4
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/types/utils.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,UAAU,CAAC,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;AACpE,MAAM,MAAM,QAAQ,CAAC,IAAI,EAAE,OAAO,IAAI,CAAC,CAAC,GAAG,EAAE,IAAI,KAAK,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,IAAI,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC;AAEnG,wBAAsB,OAAO,CAAC,CAAC,EAAE,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAMjE"}
@@ -0,0 +1,8 @@
1
+ export async function resolve(value) {
2
+ if (value instanceof Function) {
3
+ return value();
4
+ }
5
+ return value;
6
+ }
7
+
8
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/types/utils.ts"],"sourcesContent":["export type Resolvable<T> = T | Promise<T> | (() => T | Promise<T>);\nexport type Callback<TArg, TReturn> = ((arg: TArg) => TReturn) | ((arg: TArg) => Promise<TReturn>);\n\nexport async function resolve<T>(value: Resolvable<T>): Promise<T> {\n if (value instanceof Function) {\n return value();\n }\n\n return value;\n}\n"],"names":["resolve","value","Function"],"mappings":"AAGA,OAAO,eAAeA,QAAWC,KAAoB;IACnD,IAAIA,iBAAiBC,UAAU;QAC7B,OAAOD;IACT;IAEA,OAAOA;AACT"}