@resonatehq/sdk 0.6.2 → 0.7.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 (202) hide show
  1. package/README.md +24 -45
  2. package/dist/dev/network.d.ts +16 -0
  3. package/dist/dev/network.d.ts.map +1 -0
  4. package/dist/dev/network.js +89 -0
  5. package/dist/dev/network.js.map +1 -0
  6. package/dist/dev/server.d.ts +87 -0
  7. package/dist/dev/server.d.ts.map +1 -0
  8. package/dist/dev/server.js +891 -0
  9. package/dist/dev/server.js.map +1 -0
  10. package/dist/sim/example-1.d.ts +2 -0
  11. package/dist/sim/example-1.d.ts.map +1 -0
  12. package/dist/sim/example-1.js +94 -0
  13. package/dist/sim/example-1.js.map +1 -0
  14. package/dist/sim/example-2.d.ts +2 -0
  15. package/dist/sim/example-2.d.ts.map +1 -0
  16. package/dist/sim/example-2.js +95 -0
  17. package/dist/sim/example-2.js.map +1 -0
  18. package/dist/sim/example-3.d.ts +2 -0
  19. package/dist/sim/example-3.d.ts.map +1 -0
  20. package/dist/sim/example-3.js +95 -0
  21. package/dist/sim/example-3.js.map +1 -0
  22. package/dist/sim/main.d.ts +14 -0
  23. package/dist/sim/main.d.ts.map +1 -0
  24. package/dist/sim/main.js +206 -0
  25. package/dist/sim/main.js.map +1 -0
  26. package/dist/sim/src/server.d.ts +15 -0
  27. package/dist/sim/src/server.d.ts.map +1 -0
  28. package/dist/sim/src/server.js +85 -0
  29. package/dist/sim/src/server.js.map +1 -0
  30. package/dist/sim/src/simulator.d.ts +65 -0
  31. package/dist/sim/src/simulator.d.ts.map +1 -0
  32. package/dist/sim/src/simulator.js +235 -0
  33. package/dist/sim/src/simulator.js.map +1 -0
  34. package/dist/sim/src/worker.d.ts +22 -0
  35. package/dist/sim/src/worker.d.ts.map +1 -0
  36. package/dist/sim/src/worker.js +175 -0
  37. package/dist/sim/src/worker.js.map +1 -0
  38. package/dist/src/clock.d.ts +11 -0
  39. package/dist/src/clock.d.ts.map +1 -0
  40. package/dist/src/clock.js +17 -0
  41. package/dist/src/clock.js.map +1 -0
  42. package/dist/src/computation.d.ts +40 -0
  43. package/dist/src/computation.d.ts.map +1 -0
  44. package/dist/src/computation.js +225 -0
  45. package/dist/src/computation.js.map +1 -0
  46. package/dist/src/context.d.ts +120 -0
  47. package/dist/src/context.d.ts.map +1 -0
  48. package/dist/src/context.js +288 -0
  49. package/dist/src/context.js.map +1 -0
  50. package/dist/src/coroutine.d.ts +35 -0
  51. package/dist/src/coroutine.d.ts.map +1 -0
  52. package/dist/src/coroutine.js +251 -0
  53. package/dist/src/coroutine.js.map +1 -0
  54. package/dist/src/decorator.d.ts +14 -0
  55. package/dist/src/decorator.d.ts.map +1 -0
  56. package/dist/src/decorator.js +208 -0
  57. package/dist/src/decorator.js.map +1 -0
  58. package/dist/src/handler.d.ts +40 -0
  59. package/dist/src/handler.d.ts.map +1 -0
  60. package/dist/src/handler.js +198 -0
  61. package/dist/src/handler.js.map +1 -0
  62. package/dist/src/heartbeat.d.ts +22 -0
  63. package/dist/src/heartbeat.d.ts.map +1 -0
  64. package/dist/src/heartbeat.js +87 -0
  65. package/dist/src/heartbeat.js.map +1 -0
  66. package/dist/src/index.d.ts +8 -0
  67. package/dist/src/index.d.ts.map +1 -0
  68. package/dist/src/index.js +25 -0
  69. package/dist/src/index.js.map +1 -0
  70. package/dist/src/network/network.d.ts +216 -0
  71. package/dist/src/network/network.d.ts.map +1 -0
  72. package/dist/{core/logger.js → src/network/network.js} +2 -1
  73. package/dist/src/network/network.js.map +1 -0
  74. package/dist/src/network/remote.d.ts +67 -0
  75. package/dist/src/network/remote.d.ts.map +1 -0
  76. package/dist/src/network/remote.js +514 -0
  77. package/dist/src/network/remote.js.map +1 -0
  78. package/dist/src/nursery.d.ts +19 -0
  79. package/dist/src/nursery.d.ts.map +1 -0
  80. package/dist/src/nursery.js +102 -0
  81. package/dist/src/nursery.js.map +1 -0
  82. package/dist/src/processor/processor.d.ts +11 -0
  83. package/dist/src/processor/processor.d.ts.map +1 -0
  84. package/dist/src/processor/processor.js +24 -0
  85. package/dist/src/processor/processor.js.map +1 -0
  86. package/dist/src/promises.d.ts +23 -0
  87. package/dist/src/promises.d.ts.map +1 -0
  88. package/dist/src/promises.js +157 -0
  89. package/dist/src/promises.js.map +1 -0
  90. package/dist/src/registry.d.ts +15 -0
  91. package/dist/src/registry.d.ts.map +1 -0
  92. package/dist/src/registry.js +26 -0
  93. package/dist/src/registry.js.map +1 -0
  94. package/dist/src/resonate-inner.d.ts +45 -0
  95. package/dist/src/resonate-inner.d.ts.map +1 -0
  96. package/dist/src/resonate-inner.js +157 -0
  97. package/dist/src/resonate-inner.js.map +1 -0
  98. package/dist/src/resonate.d.ts +92 -0
  99. package/dist/src/resonate.d.ts.map +1 -0
  100. package/dist/src/resonate.js +240 -0
  101. package/dist/src/resonate.js.map +1 -0
  102. package/dist/src/schedules.d.ts +9 -0
  103. package/dist/src/schedules.d.ts.map +1 -0
  104. package/dist/src/schedules.js +65 -0
  105. package/dist/src/schedules.js.map +1 -0
  106. package/dist/src/tasks.d.ts +9 -0
  107. package/dist/src/tasks.d.ts.map +1 -0
  108. package/dist/src/tasks.js +61 -0
  109. package/dist/src/tasks.js.map +1 -0
  110. package/dist/src/types.d.ts +86 -0
  111. package/dist/src/types.d.ts.map +1 -0
  112. package/dist/src/types.js +14 -0
  113. package/dist/src/types.js.map +1 -0
  114. package/dist/src/util.d.ts +14 -0
  115. package/dist/src/util.d.ts.map +1 -0
  116. package/dist/src/util.js +69 -0
  117. package/dist/src/util.js.map +1 -0
  118. package/dist/tsconfig.build.tsbuildinfo +1 -0
  119. package/package.json +51 -52
  120. package/dist/core/encoder.d.ts +0 -5
  121. package/dist/core/encoder.d.ts.map +0 -1
  122. package/dist/core/encoder.js +0 -3
  123. package/dist/core/encoder.js.map +0 -1
  124. package/dist/core/encoders/base64.d.ts +0 -6
  125. package/dist/core/encoders/base64.d.ts.map +0 -1
  126. package/dist/core/encoders/base64.js +0 -13
  127. package/dist/core/encoders/base64.js.map +0 -1
  128. package/dist/core/encoders/json.d.ts +0 -6
  129. package/dist/core/encoders/json.d.ts.map +0 -1
  130. package/dist/core/encoders/json.js +0 -77
  131. package/dist/core/encoders/json.js.map +0 -1
  132. package/dist/core/errors.d.ts +0 -24
  133. package/dist/core/errors.d.ts.map +0 -1
  134. package/dist/core/errors.js +0 -42
  135. package/dist/core/errors.js.map +0 -1
  136. package/dist/core/logger.d.ts +0 -10
  137. package/dist/core/logger.d.ts.map +0 -1
  138. package/dist/core/logger.js.map +0 -1
  139. package/dist/core/loggers/logger.d.ts +0 -13
  140. package/dist/core/loggers/logger.d.ts.map +0 -1
  141. package/dist/core/loggers/logger.js +0 -48
  142. package/dist/core/loggers/logger.js.map +0 -1
  143. package/dist/core/options.d.ts +0 -160
  144. package/dist/core/options.d.ts.map +0 -1
  145. package/dist/core/options.js +0 -18
  146. package/dist/core/options.js.map +0 -1
  147. package/dist/core/promises/promises.d.ts +0 -210
  148. package/dist/core/promises/promises.d.ts.map +0 -1
  149. package/dist/core/promises/promises.js +0 -300
  150. package/dist/core/promises/promises.js.map +0 -1
  151. package/dist/core/promises/types.d.ts +0 -26
  152. package/dist/core/promises/types.d.ts.map +0 -1
  153. package/dist/core/promises/types.js +0 -37
  154. package/dist/core/promises/types.js.map +0 -1
  155. package/dist/core/retry.d.ts +0 -37
  156. package/dist/core/retry.d.ts.map +0 -1
  157. package/dist/core/retry.js +0 -141
  158. package/dist/core/retry.js.map +0 -1
  159. package/dist/core/schedules/schedules.d.ts +0 -22
  160. package/dist/core/schedules/schedules.d.ts.map +0 -1
  161. package/dist/core/schedules/schedules.js +0 -29
  162. package/dist/core/schedules/schedules.js.map +0 -1
  163. package/dist/core/schedules/types.d.ts +0 -19
  164. package/dist/core/schedules/types.d.ts.map +0 -1
  165. package/dist/core/schedules/types.js +0 -8
  166. package/dist/core/schedules/types.js.map +0 -1
  167. package/dist/core/storage.d.ts +0 -6
  168. package/dist/core/storage.d.ts.map +0 -1
  169. package/dist/core/storage.js +0 -3
  170. package/dist/core/storage.js.map +0 -1
  171. package/dist/core/storages/memory.d.ts +0 -8
  172. package/dist/core/storages/memory.d.ts.map +0 -1
  173. package/dist/core/storages/memory.js +0 -29
  174. package/dist/core/storages/memory.js.map +0 -1
  175. package/dist/core/storages/withTimeout.d.ts +0 -10
  176. package/dist/core/storages/withTimeout.d.ts.map +0 -1
  177. package/dist/core/storages/withTimeout.js +0 -44
  178. package/dist/core/storages/withTimeout.js.map +0 -1
  179. package/dist/core/store.d.ts +0 -142
  180. package/dist/core/store.d.ts.map +0 -1
  181. package/dist/core/store.js +0 -3
  182. package/dist/core/store.js.map +0 -1
  183. package/dist/core/stores/local.d.ts +0 -57
  184. package/dist/core/stores/local.d.ts.map +0 -1
  185. package/dist/core/stores/local.js +0 -405
  186. package/dist/core/stores/local.js.map +0 -1
  187. package/dist/core/stores/remote.d.ts +0 -51
  188. package/dist/core/stores/remote.d.ts.map +0 -1
  189. package/dist/core/stores/remote.js +0 -431
  190. package/dist/core/stores/remote.js.map +0 -1
  191. package/dist/core/utils.d.ts +0 -85
  192. package/dist/core/utils.d.ts.map +0 -1
  193. package/dist/core/utils.js +0 -121
  194. package/dist/core/utils.js.map +0 -1
  195. package/dist/index.d.ts +0 -19
  196. package/dist/index.d.ts.map +0 -1
  197. package/dist/index.js +0 -57
  198. package/dist/index.js.map +0 -1
  199. package/dist/resonate.d.ts +0 -312
  200. package/dist/resonate.d.ts.map +0 -1
  201. package/dist/resonate.js +0 -718
  202. package/dist/resonate.js.map +0 -1
@@ -0,0 +1,891 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.Server = void 0;
37
+ const cron_parser_1 = require("cron-parser");
38
+ const util = __importStar(require("../src/util"));
39
+ class TagRouter {
40
+ tag;
41
+ constructor(tag = "resonate:invoke") {
42
+ this.tag = tag;
43
+ }
44
+ route(promise) {
45
+ return promise.tags?.[this.tag];
46
+ }
47
+ }
48
+ class ServerError extends Error {
49
+ code;
50
+ constructor(message, code) {
51
+ super(message);
52
+ this.code = code;
53
+ }
54
+ }
55
+ class Server {
56
+ promises;
57
+ tasks;
58
+ schedules;
59
+ routers;
60
+ targets;
61
+ constructor() {
62
+ this.promises = new Map();
63
+ this.tasks = new Map();
64
+ this.schedules = new Map();
65
+ this.routers = new Array(new TagRouter());
66
+ this.targets = { default: "local://any@default" };
67
+ }
68
+ next(time) {
69
+ let timeout = undefined;
70
+ // Check pending promises
71
+ for (const promise of this.promises.values()) {
72
+ if (timeout === 0) {
73
+ return timeout;
74
+ }
75
+ if (promise.state === "pending") {
76
+ timeout = timeout === undefined ? promise.timeout : Math.min(promise.timeout, timeout);
77
+ }
78
+ }
79
+ // Check tasks
80
+ for (const task of this.tasks.values()) {
81
+ if (timeout === 0) {
82
+ return timeout;
83
+ }
84
+ if (task.state === "init") {
85
+ timeout = timeout === undefined ? 0 : Math.min(0, timeout);
86
+ }
87
+ else if (["claimed", "enqueued"].includes(task.state)) {
88
+ util.assertDefined(task.expiry);
89
+ timeout = timeout === undefined ? task.expiry : Math.min(task.expiry, timeout);
90
+ }
91
+ }
92
+ // Check schedules
93
+ for (const schedule of this.schedules.values()) {
94
+ if (timeout === 0) {
95
+ return timeout;
96
+ }
97
+ util.assertDefined(schedule.nextRunTime);
98
+ timeout = timeout === undefined ? schedule.nextRunTime : Math.min(schedule.nextRunTime, timeout);
99
+ }
100
+ // Convert to delay relative to `time`, clamped to signed 32-bit range
101
+ timeout = timeout !== undefined ? Math.min(Math.max(0, timeout - time), 2147483647) : timeout;
102
+ return timeout;
103
+ }
104
+ step(time) {
105
+ for (const schedule of this.schedules.values()) {
106
+ util.assertDefined(schedule.nextRunTime);
107
+ if (time < schedule.nextRunTime) {
108
+ continue;
109
+ }
110
+ try {
111
+ this.createPromise({
112
+ id: schedule.promiseId.replace("{{.timestamp}}", time.toString()),
113
+ timeout: time + schedule.promiseTimeout,
114
+ param: schedule.promiseParam,
115
+ tags: schedule.promiseTags,
116
+ strict: false,
117
+ time,
118
+ });
119
+ }
120
+ catch { }
121
+ const { applied } = this.transitionSchedule({ id: schedule.id, to: "created", updating: true, time });
122
+ util.assert(applied, `step(): failed to transition schedule '${schedule.id}' to 'created' state`);
123
+ }
124
+ // Reject timed-out promises
125
+ for (const promise of this.promises.values()) {
126
+ if (promise.state === "pending" && time >= promise.timeout) {
127
+ const { applied } = this.transitionPromise({ id: promise.id, to: "rejected_timedout", time });
128
+ util.assert(applied, `step(): promise '${promise.id}' expected to be timed out but transition did not apply`);
129
+ }
130
+ }
131
+ // Transition expired tasks back to init
132
+ for (const task of this.tasks.values()) {
133
+ if (["enqueued", "claimed"].includes(task.state)) {
134
+ util.assertDefined(task.expiry);
135
+ if (time >= task.expiry) {
136
+ const { applied } = this.transitionTask({ id: task.id, to: "init", force: true, time });
137
+ util.assert(applied, `step(): failed to force-reinit expired task '${task.id}'`);
138
+ }
139
+ }
140
+ }
141
+ const inFlightRootPromiseIds = new Set();
142
+ for (const task of this.tasks.values()) {
143
+ if (["enqueued", "claimed"].includes(task.state)) {
144
+ inFlightRootPromiseIds.add(task.rootPromiseId);
145
+ }
146
+ }
147
+ const msgs = [];
148
+ for (const task of this.tasks.values()) {
149
+ if (task.state !== "init" || inFlightRootPromiseIds.has(task.rootPromiseId)) {
150
+ continue;
151
+ }
152
+ let msg;
153
+ if (task.type === "invoke") {
154
+ msg = {
155
+ msg: {
156
+ type: "invoke",
157
+ task: {
158
+ ...task,
159
+ timeout: this.getPromise({ id: task.rootPromiseId }).timeout,
160
+ },
161
+ },
162
+ recv: task.recv,
163
+ };
164
+ }
165
+ else if (task.type === "resume") {
166
+ msg = {
167
+ msg: {
168
+ type: "resume",
169
+ task: {
170
+ ...task,
171
+ timeout: this.getPromise({ id: task.rootPromiseId }).timeout,
172
+ },
173
+ },
174
+ recv: task.recv,
175
+ };
176
+ }
177
+ else {
178
+ util.assert(task.type === "notify", `step(): unexpected task type '${task.type}' for notify message`);
179
+ msg = {
180
+ msg: {
181
+ type: "notify",
182
+ promise: this.getPromise({ id: task.rootPromiseId }),
183
+ },
184
+ recv: task.recv,
185
+ };
186
+ }
187
+ msgs.push(msg);
188
+ if (task.type === "notify") {
189
+ const { applied } = this.transitionTask({ id: task.id, to: "completed", time });
190
+ util.assert(applied, `step(): failed to complete notify task '${task.id}'`);
191
+ }
192
+ else {
193
+ const { applied } = this.transitionTask({ id: task.id, to: "enqueued", time });
194
+ util.assert(applied, `step(): failed to enqueue task '${task.id}' after invoking/resuming`);
195
+ }
196
+ inFlightRootPromiseIds.add(task.rootPromiseId);
197
+ }
198
+ return msgs;
199
+ }
200
+ process(requ, time) {
201
+ switch (requ.kind) {
202
+ case "createPromise": {
203
+ return {
204
+ kind: requ.kind,
205
+ promise: this.createPromise({
206
+ ...requ,
207
+ time,
208
+ }),
209
+ };
210
+ }
211
+ case "createPromiseAndTask": {
212
+ const { promise, task } = this.createPromiseAndTask({
213
+ id: requ.promise.id,
214
+ timeout: requ.promise.timeout,
215
+ processId: requ.task.processId,
216
+ ttl: requ.task.ttl,
217
+ param: requ.promise.param,
218
+ tags: requ.promise.tags,
219
+ iKey: requ.iKey,
220
+ strict: requ.strict,
221
+ time,
222
+ });
223
+ return {
224
+ kind: requ.kind,
225
+ promise: promise,
226
+ task: task,
227
+ };
228
+ }
229
+ case "readPromise": {
230
+ return {
231
+ kind: requ.kind,
232
+ promise: this.readPromise({ ...requ }),
233
+ };
234
+ }
235
+ case "completePromise": {
236
+ return {
237
+ kind: requ.kind,
238
+ promise: this.completePromise({
239
+ ...requ,
240
+ time,
241
+ }),
242
+ };
243
+ }
244
+ case "createCallback": {
245
+ return {
246
+ kind: requ.kind,
247
+ ...this.createCallback({
248
+ ...requ,
249
+ time,
250
+ }),
251
+ };
252
+ }
253
+ case "createSubscription": {
254
+ return {
255
+ kind: requ.kind,
256
+ ...this.createSubscription({ ...requ, time }),
257
+ };
258
+ }
259
+ case "createSchedule": {
260
+ return {
261
+ kind: requ.kind,
262
+ schedule: this.createSchedule({
263
+ id: requ.id,
264
+ cron: requ.cron,
265
+ promiseId: requ.promiseId,
266
+ promiseTimeout: requ.promiseTimeout,
267
+ iKey: requ.iKey,
268
+ description: requ.description,
269
+ tags: requ.tags,
270
+ promiseParam: requ.promiseParam,
271
+ promiseTags: requ.promiseTags,
272
+ time,
273
+ }),
274
+ };
275
+ }
276
+ case "readSchedule": {
277
+ return { kind: requ.kind, schedule: this.readSchedule({ ...requ }) };
278
+ }
279
+ case "deleteSchedule": {
280
+ this.deleteSchedule({ ...requ, time });
281
+ return { kind: requ.kind };
282
+ }
283
+ case "claimTask": {
284
+ return {
285
+ kind: "claimTask",
286
+ message: this.claimTask({
287
+ ...requ,
288
+ time,
289
+ }),
290
+ };
291
+ }
292
+ case "completeTask": {
293
+ return {
294
+ kind: "completeTask",
295
+ task: this.completeTask({ ...requ, time }),
296
+ };
297
+ }
298
+ case "heartbeatTasks": {
299
+ return {
300
+ kind: "heartbeatTasks",
301
+ tasksAffected: this.heartbeatTasks({ ...requ, time }),
302
+ };
303
+ }
304
+ case "dropTask": {
305
+ this.dropTask({ ...requ, time });
306
+ return { kind: "dropTask" };
307
+ }
308
+ default:
309
+ throw new Error(`Unsupported request kind: ${requ.kind}`);
310
+ }
311
+ }
312
+ createPromise({ id, timeout, param, tags, iKey, strict, time, }) {
313
+ return this._createPromise({
314
+ id,
315
+ timeout,
316
+ param,
317
+ tags,
318
+ iKey,
319
+ strict,
320
+ time,
321
+ }).promise;
322
+ }
323
+ createPromiseAndTask({ id, timeout, processId, ttl, param, tags, iKey, strict, time, }) {
324
+ const { promise, task } = this._createPromise({
325
+ id,
326
+ timeout,
327
+ processId,
328
+ ttl,
329
+ param,
330
+ tags,
331
+ iKey,
332
+ strict,
333
+ time,
334
+ });
335
+ if (task === undefined) {
336
+ return { promise };
337
+ }
338
+ return { promise, task: { ...task, timeout: promise.timeout } };
339
+ }
340
+ _createPromise({ id, timeout, param, tags, iKey, strict, processId, ttl, time, }) {
341
+ const { promise, task, applied } = this.transitionPromise({
342
+ id,
343
+ to: "pending",
344
+ strict,
345
+ timeout,
346
+ iKey,
347
+ value: param,
348
+ tags,
349
+ time,
350
+ });
351
+ util.assert(!applied || ["pending", "rejected_timedout"].includes(promise.state), `createPromise: unexpected promise state '${promise.state}' after transition to 'pending' for promise '${id}'`);
352
+ if (applied && task !== undefined && processId !== undefined) {
353
+ const { task: newTask, applied: appliedTask } = this.transitionTask({
354
+ id: task.id,
355
+ to: "claimed",
356
+ counter: 1,
357
+ processId,
358
+ ttl,
359
+ time,
360
+ });
361
+ util.assert(appliedTask, `createPromise: failed to claim task '${task.id}' for subsequent processing`);
362
+ return { promise: promise, task: newTask };
363
+ }
364
+ return { promise, task };
365
+ }
366
+ readPromise({ id }) {
367
+ return this.getPromise({ id });
368
+ }
369
+ completePromise({ id, state, value, iKey, strict, time, }) {
370
+ const { promise, applied } = this.transitionPromise({
371
+ id,
372
+ to: state,
373
+ strict,
374
+ iKey,
375
+ value,
376
+ time,
377
+ });
378
+ util.assert(!applied || [state, "rejected_timedout"].includes(promise.state), `completePromise: after transition to '${state}', promise '${id}' is in unexpected state '${promise.state}'`);
379
+ return promise;
380
+ }
381
+ createSubscription({ id, timeout, recv, time, }) {
382
+ {
383
+ const record = this.promises.get(id);
384
+ if (!record) {
385
+ throw new ServerError("not found", "not_found");
386
+ }
387
+ const cbId = `__notify:${id}:${id}`;
388
+ if (record.state !== "pending" || record.callbacks?.has(cbId)) {
389
+ return { promise: record, callback: undefined };
390
+ }
391
+ const callback = {
392
+ id: cbId,
393
+ type: "notify",
394
+ promiseId: id,
395
+ rootPromiseId: id,
396
+ recv,
397
+ timeout,
398
+ createdOn: time,
399
+ };
400
+ if (!record.callbacks) {
401
+ record.callbacks = new Map();
402
+ }
403
+ // register and return
404
+ record.callbacks.set(cbId, callback);
405
+ return {
406
+ promise: record,
407
+ callback: callback,
408
+ };
409
+ }
410
+ }
411
+ createCallback({ id, rootPromiseId, timeout, recv, time, }) {
412
+ const record = this.promises.get(id);
413
+ if (!record) {
414
+ throw new ServerError("not found", "not_found");
415
+ }
416
+ if (record.state !== "pending" || record.callbacks?.has(id)) {
417
+ return { promise: record, callback: undefined };
418
+ }
419
+ const callback = {
420
+ id: `__resume:${rootPromiseId}:${id}`,
421
+ type: "resume",
422
+ promiseId: id,
423
+ rootPromiseId,
424
+ recv,
425
+ timeout,
426
+ createdOn: time,
427
+ };
428
+ if (!record.callbacks) {
429
+ record.callbacks = new Map();
430
+ }
431
+ record.callbacks.set(callback.id, callback);
432
+ return { promise: record, callback: callback };
433
+ }
434
+ claimTask({ id, counter, processId, ttl, time, }) {
435
+ const { task, applied } = this.transitionTask({
436
+ id,
437
+ to: "claimed",
438
+ counter,
439
+ processId,
440
+ ttl,
441
+ time,
442
+ });
443
+ util.assert(applied, `claimTask: failed to claim task '${id}' with counter ${counter} using processId '${processId}'`);
444
+ switch (task.type) {
445
+ case "invoke": {
446
+ const promise = this.getPromise({ id: task.rootPromiseId });
447
+ return {
448
+ kind: task.type,
449
+ promises: {
450
+ root: { id: promise.id, data: promise },
451
+ },
452
+ };
453
+ }
454
+ case "resume": {
455
+ return {
456
+ kind: task.type,
457
+ promises: {
458
+ root: {
459
+ id: task.rootPromiseId,
460
+ data: this.getPromise({ id: task.rootPromiseId }),
461
+ },
462
+ leaf: {
463
+ id: task.leafPromiseId,
464
+ data: this.getPromise({ id: task.leafPromiseId }),
465
+ },
466
+ },
467
+ };
468
+ }
469
+ default:
470
+ throw new Error(`claimTask: unexpected task type '${task.type}' for task '${id}'`);
471
+ }
472
+ }
473
+ completeTask({ id, counter, time }) {
474
+ const { task } = this.transitionTask({ id, to: "completed", counter, time });
475
+ return task;
476
+ }
477
+ dropTask({ id, counter, time }) {
478
+ const { applied } = this.transitionTask({ id, to: "init", counter, time });
479
+ util.assert(applied);
480
+ return;
481
+ }
482
+ heartbeatTasks({ processId, time }) {
483
+ let affectedTasks = 0;
484
+ for (const task of this.tasks.values()) {
485
+ if (task.state !== "claimed" || task.processId !== processId) {
486
+ continue;
487
+ }
488
+ const { applied } = this.transitionTask({ id: task.id, to: "claimed", force: true, time });
489
+ util.assert(applied, `heartbeatTasks: failed to refresh heartbeat for task '${task.id}' owned by process '${processId}'`);
490
+ affectedTasks += 1;
491
+ }
492
+ return affectedTasks;
493
+ }
494
+ createSchedule({ id, cron, promiseId, promiseTimeout, iKey, description, tags, promiseParam, promiseTags, time, }) {
495
+ return this.transitionSchedule({
496
+ id,
497
+ to: "created",
498
+ cron,
499
+ promiseId,
500
+ promiseTimeout,
501
+ iKey,
502
+ description,
503
+ tags,
504
+ promiseParam,
505
+ promiseTags,
506
+ time,
507
+ }).schedule;
508
+ }
509
+ readSchedule({ id }) {
510
+ const schedule = this.schedules.get(id);
511
+ if (schedule === undefined) {
512
+ throw new ServerError("schedule not found", "not_found");
513
+ }
514
+ return schedule;
515
+ }
516
+ deleteSchedule({ id, time }) {
517
+ const { applied } = this.transitionSchedule({ id, to: "deleted", time });
518
+ util.assert(applied, `deleteSchedule: failed to delete schedule '${id}'`);
519
+ }
520
+ getPromise({ id }) {
521
+ const record = this.promises.get(id);
522
+ if (!record) {
523
+ throw new ServerError("not found", "not_found");
524
+ }
525
+ return record;
526
+ }
527
+ transitionPromise({ id, to, strict, timeout, iKey, value, tags, time, }) {
528
+ const { promise, applied } = this._transitionPromise({
529
+ id,
530
+ to,
531
+ strict,
532
+ timeout,
533
+ iKey,
534
+ value,
535
+ tags,
536
+ time,
537
+ });
538
+ // Initialize invocation tasks on pending
539
+ if (applied && promise.state === "pending") {
540
+ for (const router of this.routers) {
541
+ const recv = router.route(promise);
542
+ if (recv !== undefined) {
543
+ const { task, applied } = this.transitionTask({
544
+ id: `__invoke:${id}`,
545
+ to: "init",
546
+ type: "invoke",
547
+ recv: this.targets[recv] ?? recv,
548
+ rootPromiseId: promise.id,
549
+ leafPromiseId: promise.id,
550
+ timeout: promise.timeout,
551
+ time,
552
+ });
553
+ util.assert(applied, `transitionPromise: failed to init invoke task for promise '${id}' on route '${recv}'`);
554
+ return { promise, task, applied };
555
+ }
556
+ }
557
+ }
558
+ // Complete any tasks and schedule callbacks after resolution
559
+ if (applied && ["resolved", "rejected", "rejected_canceled", "rejected_timedout"].includes(promise.state)) {
560
+ // Mark existing tasks as completed
561
+ for (const task of this.tasks.values()) {
562
+ if (task.rootPromiseId === id && ["init", "enqueued", "claimed"].includes(task.state)) {
563
+ const { applied } = this.transitionTask({
564
+ id: task.id,
565
+ to: "completed",
566
+ force: true,
567
+ time,
568
+ });
569
+ util.assert(applied, `transitionPromise: failed to complete task '${task.id}' for promise '${id}'`);
570
+ }
571
+ }
572
+ // Initialize callback tasks
573
+ if (promise.callbacks) {
574
+ for (const callback of promise.callbacks.values()) {
575
+ const { applied } = this.transitionTask({
576
+ ...callback,
577
+ to: "init",
578
+ leafPromiseId: callback.promiseId,
579
+ time,
580
+ });
581
+ util.assert(applied, `transitionPromise: failed to init callback task '${callback.id}' for promise '${id}'`);
582
+ }
583
+ promise.callbacks.clear();
584
+ }
585
+ }
586
+ return { promise, applied };
587
+ }
588
+ _transitionPromise({ id, to, strict, timeout, iKey, value, tags, time, }) {
589
+ let record = this.promises.get(id);
590
+ // Create new promise
591
+ if (record === undefined && to === "pending") {
592
+ util.assertDefined(timeout);
593
+ record = {
594
+ id,
595
+ state: to,
596
+ timeout,
597
+ iKeyForCreate: iKey,
598
+ param: value,
599
+ value: undefined,
600
+ tags: tags ?? {},
601
+ createdOn: time,
602
+ };
603
+ this.promises.set(id, record);
604
+ return { promise: record, applied: true };
605
+ }
606
+ // Cannot complete non-existent promise
607
+ if (record === undefined && ["resolved", "rejected", "rejected_canceled"].includes(to)) {
608
+ throw new ServerError(`transitionPromise(${to}): promise '${id}' not found`, "not_found");
609
+ }
610
+ // No-op re-create pending if before timeout and same iKey
611
+ if (record?.state === "pending" &&
612
+ to === "pending" &&
613
+ time < record.timeout &&
614
+ ikeyMatch(record.iKeyForCreate, iKey)) {
615
+ return { promise: record, applied: false };
616
+ }
617
+ // Auto-timeout transition
618
+ if (record?.state === "pending" &&
619
+ to === "pending" &&
620
+ !strict &&
621
+ time >= record.timeout &&
622
+ ikeyMatch(record.iKeyForCreate, iKey)) {
623
+ return this._transitionPromise({ id, to: "rejected_timedout", time });
624
+ }
625
+ // Resolve or reject before timeout
626
+ if (record?.state === "pending" &&
627
+ ["resolved", "rejected", "rejected_canceled"].includes(to) &&
628
+ time < record.timeout) {
629
+ record = {
630
+ ...record,
631
+ state: to,
632
+ iKeyForComplete: iKey,
633
+ value: value,
634
+ completedOn: time,
635
+ };
636
+ this.promises.set(id, record);
637
+ return { promise: record, applied: true };
638
+ }
639
+ // Attempt completion after timeout without strict -> treat as timeout
640
+ if (record?.state === "pending" &&
641
+ ["resolved", "rejected", "rejected_canceled"].includes(to) &&
642
+ !strict &&
643
+ time >= record.timeout) {
644
+ return this._transitionPromise({ id, to: "rejected_timedout", time });
645
+ }
646
+ // Strict completion after timeout -> error
647
+ if (record?.state === "pending" &&
648
+ ["resolved", "rejected", "rejected_canceled"].includes(to) &&
649
+ strict &&
650
+ time >= record.timeout) {
651
+ throw new ServerError(`transitionPromise(${to}): promise '${id}' already timed out at ${record.timeout}`, "forbidden");
652
+ }
653
+ // Transition to timed-out
654
+ if (record?.state === "pending" && to === "rejected_timedout") {
655
+ util.assert(time >= record.timeout, `transitionPromise(rejected_timedout): cannot time out promise '${id}' before its timeout (${record.timeout})`);
656
+ record = {
657
+ ...record,
658
+ state: record.tags?.["resonate:timeout"] === "true" ? "resolved" : to,
659
+ };
660
+ this.promises.set(id, record);
661
+ return { promise: record, applied: true };
662
+ }
663
+ // No-op re-pending or re-completing without strict and matching iKey
664
+ if (record?.state !== undefined &&
665
+ ["resolved", "rejected", "rejected_canceled", "rejected_timedout"].includes(record.state) &&
666
+ to === "pending" &&
667
+ !strict &&
668
+ ikeyMatch(record.iKeyForCreate, iKey)) {
669
+ return { promise: record, applied: false };
670
+ }
671
+ if (record !== undefined &&
672
+ ["resolved", "rejected", "rejected_canceled"].includes(record.state) &&
673
+ ["resolved", "rejected", "rejected_canceled"].includes(to) &&
674
+ !strict &&
675
+ ikeyMatch(record.iKeyForComplete, iKey)) {
676
+ return { promise: record, applied: false };
677
+ }
678
+ if (record?.state === "rejected_timedout" &&
679
+ ["resolved", "rejected", "rejected_canceled"].includes(to) &&
680
+ !strict) {
681
+ return { promise: record, applied: false };
682
+ }
683
+ if (record !== undefined &&
684
+ ["resolved", "rejected", "rejected_canceled"].includes(record.state) &&
685
+ ["resolved", "rejected", "rejected_canceled"].includes(to) &&
686
+ strict &&
687
+ ikeyMatch(record.iKeyForComplete, iKey) &&
688
+ record.state === to) {
689
+ return { promise: record, applied: false };
690
+ }
691
+ // Fallback
692
+ throw new ServerError(`transitionPromise(${to}): unexpected transition for promise '${id}'`, "conflict");
693
+ }
694
+ transitionTask({ id, to, type, recv, timeout, rootPromiseId, leafPromiseId, counter, processId, ttl, force, time, }) {
695
+ let record = this.tasks.get(id);
696
+ if (record === undefined && to === "init") {
697
+ util.assertDefined(type);
698
+ util.assertDefined(recv);
699
+ util.assertDefined(rootPromiseId);
700
+ util.assertDefined(leafPromiseId);
701
+ util.assertDefined(timeout);
702
+ record = {
703
+ id,
704
+ counter: 1,
705
+ timeout,
706
+ state: to,
707
+ type,
708
+ recv,
709
+ rootPromiseId,
710
+ leafPromiseId,
711
+ createdOn: time,
712
+ };
713
+ this.tasks.set(id, record);
714
+ return { task: record, applied: true };
715
+ }
716
+ if (record?.state === "init" && to === "enqueued") {
717
+ record = {
718
+ ...record,
719
+ state: to,
720
+ expiry: time + 5000, // 5 secs
721
+ };
722
+ this.tasks.set(id, record);
723
+ return { task: record, applied: true };
724
+ }
725
+ if (record?.state === "init" && to === "claimed" && record.counter === counter) {
726
+ util.assertDefined(processId);
727
+ util.assertDefined(ttl);
728
+ util.assertDefined(counter);
729
+ record = {
730
+ ...record,
731
+ state: to,
732
+ processId,
733
+ ttl,
734
+ expiry: time + ttl,
735
+ };
736
+ this.tasks.set(id, record);
737
+ return { task: record, applied: true };
738
+ }
739
+ if (record?.state === "enqueued" && to === "claimed" && record.counter === counter) {
740
+ util.assertDefined(ttl);
741
+ util.assertDefined(counter);
742
+ util.assertDefined(processId);
743
+ record = {
744
+ ...record,
745
+ state: to,
746
+ processId,
747
+ ttl,
748
+ expiry: time + ttl,
749
+ };
750
+ this.tasks.set(id, record);
751
+ return { task: record, applied: true };
752
+ }
753
+ if (record !== undefined &&
754
+ ["init", "enqueued"].includes(record.state) &&
755
+ record.type === "notify" &&
756
+ to === "completed") {
757
+ record = {
758
+ ...record,
759
+ state: to,
760
+ completedOn: time,
761
+ };
762
+ this.tasks.set(id, record);
763
+ return { task: record, applied: true };
764
+ }
765
+ if (record !== undefined && record.state === "claimed" && record.counter === counter && to === "init") {
766
+ record = {
767
+ ...record,
768
+ counter: record.counter + 1,
769
+ state: to,
770
+ processId: undefined,
771
+ ttl: undefined,
772
+ expiry: undefined,
773
+ };
774
+ this.tasks.set(id, record);
775
+ return { task: record, applied: true };
776
+ }
777
+ if (record !== undefined && ["enqueued", "claimed"].includes(record.state) && to === "init") {
778
+ util.assertDefined(record.expiry);
779
+ record = {
780
+ ...record,
781
+ counter: record.counter + 1,
782
+ state: to,
783
+ processId: undefined,
784
+ ttl: undefined,
785
+ expiry: undefined,
786
+ };
787
+ this.tasks.set(id, record);
788
+ return { task: record, applied: true };
789
+ }
790
+ if (record?.state === "claimed" && to === "claimed" && force) {
791
+ util.assertDefined(record.ttl);
792
+ record = {
793
+ ...record,
794
+ expiry: time + record.ttl,
795
+ };
796
+ this.tasks.set(id, record);
797
+ return { task: record, applied: true };
798
+ }
799
+ if (record?.state === "claimed" &&
800
+ to === "completed" &&
801
+ record.counter === counter &&
802
+ record.expiry !== undefined &&
803
+ record.expiry >= time) {
804
+ record = {
805
+ ...record,
806
+ state: to,
807
+ completedOn: time,
808
+ };
809
+ this.tasks.set(id, record);
810
+ return { task: record, applied: true };
811
+ }
812
+ if (record !== undefined &&
813
+ ["init", "enqueued", "claimed"].includes(record?.state) &&
814
+ to === "completed" &&
815
+ force) {
816
+ record = {
817
+ ...record,
818
+ state: to,
819
+ };
820
+ this.tasks.set(id, record);
821
+ return { task: record, applied: true };
822
+ }
823
+ if (record?.state === "completed" && to === "completed") {
824
+ return { task: record, applied: false };
825
+ }
826
+ if (record === undefined) {
827
+ throw new ServerError("Task not found", "not_found");
828
+ }
829
+ throw new ServerError("task is already claimed, completed, or an invalid counter was provided", "conflict");
830
+ }
831
+ transitionSchedule({ id, to, cron, promiseId, promiseTimeout, iKey, description, tags, promiseParam, promiseTags, updating, time, }) {
832
+ let record = this.schedules.get(id);
833
+ // Create new schedule
834
+ if (record === undefined && to === "created") {
835
+ util.assertDefined(cron);
836
+ util.assertDefined(promiseId);
837
+ util.assertDefined(promiseTimeout);
838
+ util.assert(promiseTimeout >= 0, "transitionSchedule(created): 'promiseTimeout' must be non-negative");
839
+ record = {
840
+ id,
841
+ description,
842
+ cron,
843
+ tags: tags ?? {},
844
+ promiseId,
845
+ promiseTimeout,
846
+ promiseParam,
847
+ promiseTags: promiseTags ?? {},
848
+ nextRunTime: cron_parser_1.CronExpressionParser.parse(cron).next().getMilliseconds(),
849
+ iKey,
850
+ createdOn: time,
851
+ lastRunTime: 0,
852
+ };
853
+ this.schedules.set(id, record);
854
+ return { schedule: record, applied: true };
855
+ }
856
+ // No-op if same iKey
857
+ if (record !== undefined && to === "created" && ikeyMatch(iKey, record.iKey)) {
858
+ return { schedule: record, applied: false };
859
+ }
860
+ // Update existing schedule
861
+ if (record !== undefined && to === "created" && updating) {
862
+ record = {
863
+ ...record,
864
+ lastRunTime: record.nextRunTime,
865
+ nextRunTime: cron_parser_1.CronExpressionParser.parse(record.cron).next().getMilliseconds(),
866
+ };
867
+ this.schedules.set(id, record);
868
+ return { schedule: record, applied: true };
869
+ }
870
+ // Schedule exists and not updating
871
+ if (record !== undefined && to === "created") {
872
+ throw new ServerError(`transitionSchedule(created): schedule '${id}' already exists and 'updating' flag is false`, "conflict");
873
+ }
874
+ // Delete non-existent
875
+ if (record === undefined && to === "deleted") {
876
+ throw new ServerError(`transitionSchedule(deleted): schedule '${id}' not found`, "not_found");
877
+ }
878
+ // Delete existing
879
+ if (record !== undefined && to === "deleted") {
880
+ this.schedules.delete(id);
881
+ return { schedule: record, applied: true };
882
+ }
883
+ // Fallback error
884
+ throw new ServerError(`transitionSchedule(${to}): unexpected transition for schedule '${id}'`, "conflict");
885
+ }
886
+ }
887
+ exports.Server = Server;
888
+ function ikeyMatch(left, right) {
889
+ return left !== undefined && right !== undefined && left === right;
890
+ }
891
+ //# sourceMappingURL=server.js.map