@restatedev/restate-sdk 0.7.3-worker → 0.8.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 (231) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +29 -51
  3. package/dist/clients/workflow_client.d.ts +77 -0
  4. package/dist/clients/workflow_client.d.ts.map +1 -0
  5. package/dist/clients/workflow_client.js +172 -0
  6. package/dist/clients/workflow_client.js.map +1 -0
  7. package/dist/connection/buffered_connection.js +44 -0
  8. package/dist/connection/buffered_connection.js.map +1 -0
  9. package/dist/connection/connection.js +13 -0
  10. package/dist/connection/connection.js.map +1 -0
  11. package/dist/connection/embedded_connection.js +59 -0
  12. package/dist/connection/embedded_connection.js.map +1 -0
  13. package/dist/connection/http_connection.js +203 -0
  14. package/dist/connection/http_connection.js.map +1 -0
  15. package/dist/connection/lambda_connection.js +58 -0
  16. package/dist/connection/lambda_connection.js.map +1 -0
  17. package/dist/{restate_context.d.ts → context.d.ts} +239 -170
  18. package/dist/context.d.ts.map +1 -0
  19. package/dist/context.js +113 -0
  20. package/dist/context.js.map +1 -0
  21. package/dist/{restate_context_impl.d.ts → context_impl.d.ts} +26 -30
  22. package/dist/context_impl.d.ts.map +1 -0
  23. package/dist/context_impl.js +439 -0
  24. package/dist/context_impl.js.map +1 -0
  25. package/dist/embedded/api.d.ts +2 -2
  26. package/dist/embedded/api.d.ts.map +1 -1
  27. package/dist/embedded/api.js +35 -0
  28. package/dist/embedded/api.js.map +1 -0
  29. package/dist/embedded/handler.d.ts +2 -2
  30. package/dist/embedded/handler.d.ts.map +1 -1
  31. package/dist/embedded/handler.js +26 -0
  32. package/dist/embedded/handler.js.map +1 -0
  33. package/dist/embedded/http2_remote.js +91 -0
  34. package/dist/embedded/http2_remote.js.map +1 -0
  35. package/dist/embedded/invocation.d.ts.map +1 -1
  36. package/dist/embedded/invocation.js +94 -0
  37. package/dist/embedded/invocation.js.map +1 -0
  38. package/dist/endpoint/endpoint_impl.d.ts +35 -0
  39. package/dist/endpoint/endpoint_impl.d.ts.map +1 -0
  40. package/dist/endpoint/endpoint_impl.js +405 -0
  41. package/dist/endpoint/endpoint_impl.js.map +1 -0
  42. package/dist/endpoint/http2_handler.d.ts +11 -0
  43. package/dist/endpoint/http2_handler.d.ts.map +1 -0
  44. package/dist/endpoint/http2_handler.js +119 -0
  45. package/dist/endpoint/http2_handler.js.map +1 -0
  46. package/dist/endpoint/lambda_handler.d.ts +15 -0
  47. package/dist/endpoint/lambda_handler.d.ts.map +1 -0
  48. package/dist/endpoint/lambda_handler.js +144 -0
  49. package/dist/endpoint/lambda_handler.js.map +1 -0
  50. package/dist/endpoint.d.ts +161 -0
  51. package/dist/endpoint.d.ts.map +1 -0
  52. package/dist/endpoint.js +22 -0
  53. package/dist/endpoint.js.map +1 -0
  54. package/dist/generated/dev/restate/events.js +371 -0
  55. package/dist/generated/dev/restate/events.js.map +1 -0
  56. package/dist/generated/dev/restate/ext.js +215 -0
  57. package/dist/generated/dev/restate/ext.js.map +1 -0
  58. package/dist/generated/google/protobuf/descriptor.js +6676 -0
  59. package/dist/generated/google/protobuf/descriptor.js.map +1 -0
  60. package/dist/generated/google/protobuf/empty.js +107 -0
  61. package/dist/generated/google/protobuf/empty.js.map +1 -0
  62. package/dist/generated/google/protobuf/struct.js +754 -0
  63. package/dist/generated/google/protobuf/struct.js.map +1 -0
  64. package/dist/generated/proto/discovery.js +364 -0
  65. package/dist/generated/proto/discovery.js.map +1 -0
  66. package/dist/generated/proto/dynrpc.js +668 -0
  67. package/dist/generated/proto/dynrpc.js.map +1 -0
  68. package/dist/generated/proto/javascript.d.ts +13 -0
  69. package/dist/generated/proto/javascript.d.ts.map +1 -1
  70. package/dist/generated/proto/javascript.js +416 -0
  71. package/dist/generated/proto/javascript.js.map +1 -0
  72. package/dist/generated/proto/protocol.d.ts +43 -0
  73. package/dist/generated/proto/protocol.d.ts.map +1 -1
  74. package/dist/generated/proto/protocol.js +2641 -0
  75. package/dist/generated/proto/protocol.js.map +1 -0
  76. package/dist/generated/proto/services.js +1535 -0
  77. package/dist/generated/proto/services.js.map +1 -0
  78. package/dist/generated/proto/test.js +321 -0
  79. package/dist/generated/proto/test.js.map +1 -0
  80. package/dist/invocation.d.ts +4 -1
  81. package/dist/invocation.d.ts.map +1 -1
  82. package/dist/invocation.js +157 -0
  83. package/dist/invocation.js.map +1 -0
  84. package/dist/io/decoder.d.ts +1 -0
  85. package/dist/io/decoder.d.ts.map +1 -1
  86. package/dist/io/decoder.js +140 -0
  87. package/dist/io/decoder.js.map +1 -0
  88. package/dist/io/encoder.d.ts +1 -2
  89. package/dist/io/encoder.d.ts.map +1 -1
  90. package/dist/io/encoder.js +68 -0
  91. package/dist/io/encoder.js.map +1 -0
  92. package/dist/journal.d.ts +13 -4
  93. package/dist/journal.d.ts.map +1 -1
  94. package/dist/journal.js +405 -0
  95. package/dist/journal.js.map +1 -0
  96. package/dist/local_state_store.d.ts +5 -3
  97. package/dist/local_state_store.d.ts.map +1 -1
  98. package/dist/local_state_store.js +82 -0
  99. package/dist/local_state_store.js.map +1 -0
  100. package/dist/logger.d.ts +19 -0
  101. package/dist/logger.d.ts.map +1 -0
  102. package/dist/logger.js +90 -0
  103. package/dist/logger.js.map +1 -0
  104. package/dist/promise_combinator_tracker.d.ts +29 -0
  105. package/dist/promise_combinator_tracker.d.ts.map +1 -0
  106. package/dist/promise_combinator_tracker.js +128 -0
  107. package/dist/promise_combinator_tracker.js.map +1 -0
  108. package/dist/public_api.d.ts +5 -5
  109. package/dist/public_api.d.ts.map +1 -1
  110. package/dist/public_api.js +60 -0
  111. package/dist/public_api.js.map +1 -0
  112. package/dist/state_machine.d.ts +19 -12
  113. package/dist/state_machine.d.ts.map +1 -1
  114. package/dist/state_machine.js +437 -0
  115. package/dist/state_machine.js.map +1 -0
  116. package/dist/types/errors.d.ts +12 -3
  117. package/dist/types/errors.d.ts.map +1 -1
  118. package/dist/types/errors.js +273 -0
  119. package/dist/types/errors.js.map +1 -0
  120. package/dist/types/grpc.d.ts +6 -4
  121. package/dist/types/grpc.d.ts.map +1 -1
  122. package/dist/types/grpc.js +81 -0
  123. package/dist/types/grpc.js.map +1 -0
  124. package/dist/types/protocol.d.ts +9 -5
  125. package/dist/types/protocol.d.ts.map +1 -1
  126. package/dist/types/protocol.js +147 -0
  127. package/dist/types/protocol.js.map +1 -0
  128. package/dist/types/router.d.ts +8 -8
  129. package/dist/types/router.d.ts.map +1 -1
  130. package/dist/types/router.js +36 -0
  131. package/dist/types/router.js.map +1 -0
  132. package/dist/types/types.d.ts +1 -0
  133. package/dist/types/types.d.ts.map +1 -1
  134. package/dist/types/types.js +138 -0
  135. package/dist/types/types.js.map +1 -0
  136. package/dist/utils/{assumpsions.d.ts → assumptions.d.ts} +1 -1
  137. package/dist/utils/{assumpsions.d.ts.map → assumptions.d.ts.map} +1 -1
  138. package/dist/utils/assumptions.js +101 -0
  139. package/dist/utils/assumptions.js.map +1 -0
  140. package/dist/utils/message_logger.d.ts +28 -0
  141. package/dist/utils/message_logger.d.ts.map +1 -0
  142. package/dist/utils/message_logger.js +88 -0
  143. package/dist/utils/message_logger.js.map +1 -0
  144. package/dist/utils/promises.d.ts +15 -0
  145. package/dist/utils/promises.d.ts.map +1 -0
  146. package/dist/utils/promises.js +67 -0
  147. package/dist/utils/promises.js.map +1 -0
  148. package/dist/utils/public_utils.js +49 -0
  149. package/dist/utils/public_utils.js.map +1 -0
  150. package/dist/utils/rand.d.ts +1 -1
  151. package/dist/utils/rand.d.ts.map +1 -1
  152. package/dist/utils/rand.js +114 -0
  153. package/dist/utils/rand.js.map +1 -0
  154. package/dist/utils/utils.d.ts +1 -10
  155. package/dist/utils/utils.d.ts.map +1 -1
  156. package/dist/utils/utils.js +122 -0
  157. package/dist/utils/utils.js.map +1 -0
  158. package/dist/workflows/workflow.d.ts +101 -0
  159. package/dist/workflows/workflow.d.ts.map +1 -0
  160. package/dist/workflows/workflow.js +80 -0
  161. package/dist/workflows/workflow.js.map +1 -0
  162. package/dist/workflows/workflow_state_service.d.ts +35 -0
  163. package/dist/workflows/workflow_state_service.d.ts.map +1 -0
  164. package/dist/workflows/workflow_state_service.js +201 -0
  165. package/dist/workflows/workflow_state_service.js.map +1 -0
  166. package/dist/workflows/workflow_wrapper_service.d.ts +10 -0
  167. package/dist/workflows/workflow_wrapper_service.d.ts.map +1 -0
  168. package/dist/workflows/workflow_wrapper_service.js +264 -0
  169. package/dist/workflows/workflow_wrapper_service.js.map +1 -0
  170. package/package.json +38 -39
  171. package/src/clients/workflow_client.ts +290 -0
  172. package/src/connection/buffered_connection.ts +47 -0
  173. package/src/connection/connection.ts +34 -0
  174. package/src/connection/embedded_connection.ts +62 -0
  175. package/src/connection/http_connection.ts +228 -0
  176. package/src/connection/lambda_connection.ts +69 -0
  177. package/src/context.ts +633 -0
  178. package/src/context_impl.ts +721 -0
  179. package/src/embedded/api.ts +57 -0
  180. package/src/embedded/handler.ts +36 -0
  181. package/src/embedded/http2_remote.ts +103 -0
  182. package/src/embedded/invocation.ts +126 -0
  183. package/src/endpoint/endpoint_impl.ts +623 -0
  184. package/src/endpoint/http2_handler.ts +151 -0
  185. package/src/endpoint/lambda_handler.ts +181 -0
  186. package/src/endpoint.ts +187 -0
  187. package/src/generated/dev/restate/events.ts +430 -0
  188. package/src/generated/dev/restate/ext.ts +238 -0
  189. package/src/generated/google/protobuf/descriptor.ts +7889 -0
  190. package/src/generated/google/protobuf/empty.ts +150 -0
  191. package/src/generated/google/protobuf/struct.ts +878 -0
  192. package/src/generated/proto/discovery.ts +423 -0
  193. package/src/generated/proto/dynrpc.ts +768 -0
  194. package/src/generated/proto/javascript.ts +488 -0
  195. package/src/generated/proto/protocol.ts +3091 -0
  196. package/src/generated/proto/services.ts +1834 -0
  197. package/src/generated/proto/test.ts +387 -0
  198. package/src/invocation.ts +212 -0
  199. package/src/io/decoder.ts +171 -0
  200. package/src/io/encoder.ts +72 -0
  201. package/src/journal.ts +537 -0
  202. package/src/local_state_store.ts +94 -0
  203. package/src/logger.ts +121 -0
  204. package/src/promise_combinator_tracker.ts +191 -0
  205. package/src/public_api.ts +53 -0
  206. package/src/state_machine.ts +635 -0
  207. package/src/types/errors.ts +297 -0
  208. package/src/types/grpc.ts +97 -0
  209. package/src/types/protocol.ts +201 -0
  210. package/src/types/router.ts +118 -0
  211. package/src/types/types.ts +160 -0
  212. package/src/utils/assumptions.ts +131 -0
  213. package/src/utils/message_logger.ts +112 -0
  214. package/src/utils/promises.ts +118 -0
  215. package/src/utils/public_utils.ts +91 -0
  216. package/src/utils/rand.ts +142 -0
  217. package/src/utils/utils.ts +178 -0
  218. package/src/workflows/workflow.ts +178 -0
  219. package/src/workflows/workflow_state_service.ts +299 -0
  220. package/src/workflows/workflow_wrapper_service.ts +314 -0
  221. package/dist/cloudflare_bundle.js +0 -27387
  222. package/dist/restate_context.d.ts.map +0 -1
  223. package/dist/restate_context_impl.d.ts.map +0 -1
  224. package/dist/server/base_restate_server.d.ts +0 -32
  225. package/dist/server/base_restate_server.d.ts.map +0 -1
  226. package/dist/server/restate_lambda_handler.d.ts +0 -104
  227. package/dist/server/restate_lambda_handler.d.ts.map +0 -1
  228. package/dist/server/restate_server.d.ts +0 -97
  229. package/dist/server/restate_server.d.ts.map +0 -1
  230. package/dist/utils/logger.d.ts +0 -60
  231. package/dist/utils/logger.d.ts.map +0 -1
@@ -0,0 +1,91 @@
1
+ /*
2
+ * Copyright (c) 2023-2024 - Restate Software, Inc., Restate GmbH
3
+ *
4
+ * This file is part of the Restate SDK for Node.js/TypeScript,
5
+ * which is released under the MIT license.
6
+ *
7
+ * You can find a copy of the license in file LICENSE in the root
8
+ * directory of this repository or package, or at
9
+ * https://github.com/restatedev/sdk-typescript/blob/main/LICENSE
10
+ */
11
+
12
+ /**
13
+ * Retry policy that decides how to delay between retries.
14
+ */
15
+ export interface RetryPolicy {
16
+ computeNextDelay(previousDelayMs: number): number;
17
+ }
18
+
19
+ /**
20
+ * A {@link RetryPolicy} that keeps a fixed delay between retries.
21
+ */
22
+ export const FIXED_DELAY: RetryPolicy = {
23
+ computeNextDelay(previousDelayMs: number): number {
24
+ return previousDelayMs;
25
+ },
26
+ };
27
+
28
+ /**
29
+ * A {@link RetryPolicy} that does an exponential backoff delay between retries.
30
+ * Each delay is twice as long as the previous delay.
31
+ */
32
+ export const EXPONENTIAL_BACKOFF: RetryPolicy = {
33
+ computeNextDelay(previousDelayMs: number): number {
34
+ return 2 * previousDelayMs;
35
+ },
36
+ };
37
+
38
+ /**
39
+ * All properties related to retrying, like the policy (exponential, fixed, ...), the
40
+ * initial delay, the maximum number of retries.
41
+ */
42
+ export interface RetrySettings {
43
+ /**
44
+ * The initial delay between retries. As more retries happen, the delay may change per the policy.
45
+ */
46
+ initialDelayMs?: number;
47
+
48
+ /**
49
+ * Optionally, the maximum delay between retries. No matter what the policy says, this is the maximum time
50
+ * that Restate sleeps between retries.
51
+ * If not set, there is effectively no limit (internally the limit is Number.MAX_SAFE_INTEGER).
52
+ */
53
+ maxDelayMs?: number;
54
+
55
+ /**
56
+ * Optionally, the maximum number of retries before this function fails with an exception.
57
+ * If not set, there is effectively no limit (internally the limit is Number.MAX_SAFE_INTEGER).
58
+ */
59
+ maxRetries?: number;
60
+
61
+ /**
62
+ * Optionally, the {@link RetryPolicy} to use. Defaults to {@link EXPONENTIAL_BACKOFF}.
63
+ */
64
+ policy?: RetryPolicy;
65
+
66
+ /**
67
+ * Optionally, the name of side effect action that is used in error- and log messages around retries.
68
+ */
69
+ name?: string;
70
+ }
71
+
72
+ /**
73
+ * The default initial delay between retries: 10 milliseconds.
74
+ */
75
+ export const DEFAULT_INITIAL_DELAY_MS = 10;
76
+
77
+ /**
78
+ * Default retry policy that retries an infinite number of times, with exponential backoff
79
+ * and a starting delay of 10 milliseconds.
80
+ */
81
+ export const DEFAULT_INFINITE_EXPONENTIAL_BACKOFF: RetrySettings = {
82
+ initialDelayMs: DEFAULT_INITIAL_DELAY_MS,
83
+ maxDelayMs: Number.MAX_SAFE_INTEGER,
84
+ maxRetries: Number.MAX_SAFE_INTEGER,
85
+ policy: EXPONENTIAL_BACKOFF,
86
+ };
87
+
88
+ /**
89
+ * Retry policy that does no retries.
90
+ */
91
+ export const NO_RETRIES: RetrySettings = { maxRetries: 0 };
@@ -0,0 +1,142 @@
1
+ /*
2
+ * Copyright (c) 2023-2024 - Restate Software, Inc., Restate GmbH
3
+ *
4
+ * This file is part of the Restate SDK for Node.js/TypeScript,
5
+ * which is released under the MIT license.
6
+ *
7
+ * You can find a copy of the license in file LICENSE in the root
8
+ * directory of this repository or package, or at
9
+ * https://github.com/restatedev/sdk-typescript/blob/main/LICENSE
10
+ */
11
+
12
+ //! Some parts copied from https://github.com/uuidjs/uuid/blob/main/src/stringify.js
13
+ //! License MIT
14
+
15
+ import { Rand } from "../context";
16
+ import { ErrorCodes, TerminalError } from "../types/errors";
17
+ import { CallContexType, ContextImpl } from "../context_impl";
18
+ import { createHash } from "crypto";
19
+
20
+ export class RandImpl implements Rand {
21
+ private randstate256: [bigint, bigint, bigint, bigint];
22
+
23
+ constructor(id: Buffer | [bigint, bigint, bigint, bigint]) {
24
+ if (id instanceof Buffer) {
25
+ // hash the invocation ID, which is known to contain 74 bits of entropy
26
+ const hash = createHash("sha256").update(id).digest();
27
+
28
+ this.randstate256 = [
29
+ hash.readBigUInt64LE(0),
30
+ hash.readBigUInt64LE(8),
31
+ hash.readBigUInt64LE(16),
32
+ hash.readBigUInt64LE(24),
33
+ ];
34
+ } else {
35
+ this.randstate256 = id;
36
+ }
37
+ }
38
+
39
+ static U64_MASK = (1n << 64n) - 1n;
40
+
41
+ // xoshiro256++
42
+ // https://prng.di.unimi.it/xoshiro256plusplus.c - public domain
43
+ u64(): bigint {
44
+ const result: bigint =
45
+ (RandImpl.rotl(
46
+ (this.randstate256[0] + this.randstate256[3]) & RandImpl.U64_MASK,
47
+ 23n
48
+ ) +
49
+ this.randstate256[0]) &
50
+ RandImpl.U64_MASK;
51
+
52
+ const t: bigint = (this.randstate256[1] << 17n) & RandImpl.U64_MASK;
53
+
54
+ this.randstate256[2] ^= this.randstate256[0];
55
+ this.randstate256[3] ^= this.randstate256[1];
56
+ this.randstate256[1] ^= this.randstate256[2];
57
+ this.randstate256[0] ^= this.randstate256[3];
58
+
59
+ this.randstate256[2] ^= t;
60
+
61
+ this.randstate256[3] = RandImpl.rotl(this.randstate256[3], 45n);
62
+
63
+ return result;
64
+ }
65
+
66
+ static rotl(x: bigint, k: bigint): bigint {
67
+ return ((x << k) & RandImpl.U64_MASK) | (x >> (64n - k));
68
+ }
69
+
70
+ checkContext() {
71
+ const context = ContextImpl.callContext.getStore();
72
+ if (context && context.type === CallContexType.SideEffect) {
73
+ throw new TerminalError(
74
+ `You may not call methods on Rand from within a side effect.`,
75
+ { errorCode: ErrorCodes.INTERNAL }
76
+ );
77
+ }
78
+ }
79
+
80
+ static U53_MASK = (1n << 53n) - 1n;
81
+
82
+ public random(): number {
83
+ this.checkContext();
84
+
85
+ // first generate a uint in range [0,2^53), which can be mapped 1:1 to a float64 in [0,1)
86
+ const u53 = this.u64() & RandImpl.U53_MASK;
87
+ // then divide by 2^53, which will simply update the exponent
88
+ return Number(u53) / 2 ** 53;
89
+ }
90
+
91
+ public uuidv4(): string {
92
+ this.checkContext();
93
+
94
+ const buf = Buffer.alloc(16);
95
+ buf.writeBigUInt64LE(this.u64(), 0);
96
+ buf.writeBigUInt64LE(this.u64(), 8);
97
+ // Per 4.4, set bits for version and `clock_seq_hi_and_reserved`
98
+ buf[6] = (buf[6] & 0x0f) | 0x40;
99
+ buf[8] = (buf[8] & 0x3f) | 0x80;
100
+ return uuidStringify(buf);
101
+ }
102
+ }
103
+
104
+ const byteToHex: string[] = [];
105
+
106
+ for (let i = 0; i < 256; ++i) {
107
+ byteToHex.push((i + 0x100).toString(16).slice(1));
108
+ }
109
+
110
+ /**
111
+ * Convert array of 16 byte values to UUID string format of the form:
112
+ * XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
113
+ */
114
+ function uuidStringify(arr: Buffer, offset = 0) {
115
+ // Note: Be careful editing this code! It's been tuned for performance
116
+ // and works in ways you may not expect. See https://github.com/uuidjs/uuid/pull/434
117
+ //
118
+ // Note to future-self: No, you can't remove the `toLowerCase()` call.
119
+ // REF: https://github.com/uuidjs/uuid/pull/677#issuecomment-1757351351
120
+ return (
121
+ byteToHex[arr[offset + 0]] +
122
+ byteToHex[arr[offset + 1]] +
123
+ byteToHex[arr[offset + 2]] +
124
+ byteToHex[arr[offset + 3]] +
125
+ "-" +
126
+ byteToHex[arr[offset + 4]] +
127
+ byteToHex[arr[offset + 5]] +
128
+ "-" +
129
+ byteToHex[arr[offset + 6]] +
130
+ byteToHex[arr[offset + 7]] +
131
+ "-" +
132
+ byteToHex[arr[offset + 8]] +
133
+ byteToHex[arr[offset + 9]] +
134
+ "-" +
135
+ byteToHex[arr[offset + 10]] +
136
+ byteToHex[arr[offset + 11]] +
137
+ byteToHex[arr[offset + 12]] +
138
+ byteToHex[arr[offset + 13]] +
139
+ byteToHex[arr[offset + 14]] +
140
+ byteToHex[arr[offset + 15]]
141
+ ).toLowerCase();
142
+ }
@@ -0,0 +1,178 @@
1
+ /*
2
+ * Copyright (c) 2023-2024 - Restate Software, Inc., Restate GmbH
3
+ *
4
+ * This file is part of the Restate SDK for Node.js/TypeScript,
5
+ * which is released under the MIT license.
6
+ *
7
+ * You can find a copy of the license in file LICENSE in the root
8
+ * directory of this repository or package, or at
9
+ * https://github.com/restatedev/sdk-typescript/blob/main/LICENSE
10
+ */
11
+
12
+ /* eslint-disable @typescript-eslint/no-explicit-any */
13
+
14
+ import {
15
+ BackgroundInvokeEntryMessage,
16
+ ClearStateEntryMessage,
17
+ CompleteAwakeableEntryMessage,
18
+ GetStateEntryMessage,
19
+ InvokeEntryMessage,
20
+ OutputStreamEntryMessage,
21
+ SetStateEntryMessage,
22
+ } from "../generated/proto/protocol";
23
+ import {
24
+ AWAKEABLE_ENTRY_MESSAGE_TYPE,
25
+ BACKGROUND_INVOKE_ENTRY_MESSAGE_TYPE,
26
+ CLEAR_STATE_ENTRY_MESSAGE_TYPE,
27
+ COMPLETE_AWAKEABLE_ENTRY_MESSAGE_TYPE,
28
+ GET_STATE_ENTRY_MESSAGE_TYPE,
29
+ INVOKE_ENTRY_MESSAGE_TYPE,
30
+ OUTPUT_STREAM_ENTRY_MESSAGE_TYPE,
31
+ SET_STATE_ENTRY_MESSAGE_TYPE,
32
+ SIDE_EFFECT_ENTRY_MESSAGE_TYPE,
33
+ SLEEP_ENTRY_MESSAGE_TYPE,
34
+ } from "../types/protocol";
35
+
36
+ export function jsonSerialize(obj: any): string {
37
+ return JSON.stringify(obj, (_, v) =>
38
+ typeof v === "bigint" ? "BIGINT::" + v.toString() : v
39
+ );
40
+ }
41
+
42
+ export function jsonDeserialize<T>(json: string): T {
43
+ return JSON.parse(json, (_, v) =>
44
+ typeof v === "string" && v.startsWith("BIGINT::")
45
+ ? BigInt(v.substring(8))
46
+ : v
47
+ ) as T;
48
+ }
49
+
50
+ // When using google.protobuf.Value in RPC handler responses, we want to roughly match the behaviour of JSON.stringify
51
+ // for example in converting Date objects to a UTC string
52
+ export function jsonSafeAny(key: string, value: any): any {
53
+ if (
54
+ value !== undefined &&
55
+ value !== null &&
56
+ typeof value.toJSON == "function"
57
+ ) {
58
+ return value.toJSON(key) as any;
59
+ } else if (globalThis.Array.isArray(value)) {
60
+ // in place replace
61
+ value.forEach((_, i) => (value[i] = jsonSafeAny(i.toString(), value[i])));
62
+ return value;
63
+ } else if (typeof value === "object") {
64
+ Object.keys(value).forEach((key) => {
65
+ value[key] = jsonSafeAny(key, value[key]);
66
+ });
67
+ return value;
68
+ } else {
69
+ // primitive that doesn't have a toJSON method, with no children
70
+ return value;
71
+ }
72
+ }
73
+
74
+ export function formatMessageAsJson(obj: any): string {
75
+ const newObj = { ...(obj as Record<string, unknown>) };
76
+ for (const [key, value] of Object.entries(newObj)) {
77
+ if (Buffer.isBuffer(value)) {
78
+ newObj[key] = value.toString().trim();
79
+ }
80
+ }
81
+ // Stringify object. Replace bigintToString serializer to prevent "BigInt not serializable" errors
82
+ return JSON.stringify(obj, (_, v) =>
83
+ typeof v === "bigint" ? v.toString() + "n" : v
84
+ );
85
+ }
86
+
87
+ /**
88
+ * Equality functions
89
+ * @param msg1 the current message from user code
90
+ * @param msg2 the replayed message
91
+ */
92
+ // These functions are used to check whether a replayed message matches the current user code.
93
+ // We check the fields which we can check
94
+ // (the fields which do not contain results, because these might be filled in the result)
95
+
96
+ const getStateMsgEquality = (
97
+ msg1: GetStateEntryMessage,
98
+ msg2: GetStateEntryMessage
99
+ ) => {
100
+ return msg1.key.equals(msg2.key);
101
+ };
102
+
103
+ const invokeMsgEquality = (
104
+ msg1: InvokeEntryMessage | BackgroundInvokeEntryMessage,
105
+ msg2: InvokeEntryMessage | BackgroundInvokeEntryMessage
106
+ ) => {
107
+ return (
108
+ msg1.serviceName === msg2.serviceName &&
109
+ msg1.methodName === msg2.methodName &&
110
+ msg1.parameter.equals(msg2.parameter)
111
+ );
112
+ };
113
+
114
+ const setStateMsgEquality = (
115
+ msg1: SetStateEntryMessage,
116
+ msg2: SetStateEntryMessage
117
+ ) => {
118
+ return msg1.key.equals(msg2.key) && msg1.value.equals(msg2.value);
119
+ };
120
+
121
+ const clearStateMsgEquality = (
122
+ msg1: ClearStateEntryMessage,
123
+ msg2: ClearStateEntryMessage
124
+ ) => {
125
+ return msg1.key.equals(msg2.key);
126
+ };
127
+
128
+ const completeAwakeableMsgEquality = (
129
+ msg1: CompleteAwakeableEntryMessage,
130
+ msg2: CompleteAwakeableEntryMessage
131
+ ) => {
132
+ if (!(msg1.id === msg2.id)) {
133
+ return false;
134
+ }
135
+
136
+ if (msg1.value && msg2.value) {
137
+ return msg1.value.equals(msg2.value);
138
+ } else if (msg1.failure && msg2.failure) {
139
+ return (
140
+ msg1.failure?.code === msg2.failure?.code &&
141
+ msg1.failure?.message === msg2.failure?.message
142
+ );
143
+ } else {
144
+ return false;
145
+ }
146
+ };
147
+
148
+ const outputMsgEquality = (
149
+ msg1: OutputStreamEntryMessage,
150
+ msg2: OutputStreamEntryMessage
151
+ ) => {
152
+ if (msg1.value && msg2.value) {
153
+ return msg1.value.equals(msg2.value);
154
+ } else if (msg1.failure && msg2.failure) {
155
+ return (
156
+ msg1.failure?.code === msg2.failure?.code &&
157
+ msg1.failure?.message === msg2.failure?.message
158
+ );
159
+ } else {
160
+ return false;
161
+ }
162
+ };
163
+
164
+ export const equalityCheckers = new Map<
165
+ bigint,
166
+ (msg1: any, msg2: any) => boolean
167
+ >([
168
+ [GET_STATE_ENTRY_MESSAGE_TYPE, getStateMsgEquality],
169
+ [SET_STATE_ENTRY_MESSAGE_TYPE, setStateMsgEquality],
170
+ [CLEAR_STATE_ENTRY_MESSAGE_TYPE, clearStateMsgEquality],
171
+ [INVOKE_ENTRY_MESSAGE_TYPE, invokeMsgEquality],
172
+ [BACKGROUND_INVOKE_ENTRY_MESSAGE_TYPE, invokeMsgEquality],
173
+ [COMPLETE_AWAKEABLE_ENTRY_MESSAGE_TYPE, completeAwakeableMsgEquality],
174
+ [OUTPUT_STREAM_ENTRY_MESSAGE_TYPE, outputMsgEquality],
175
+ [AWAKEABLE_ENTRY_MESSAGE_TYPE, () => true],
176
+ [SIDE_EFFECT_ENTRY_MESSAGE_TYPE, () => true],
177
+ [SLEEP_ENTRY_MESSAGE_TYPE, () => true],
178
+ ]);
@@ -0,0 +1,178 @@
1
+ /*
2
+ * Copyright (c) 2023-2024 - Restate Software, Inc., Restate GmbH
3
+ *
4
+ * This file is part of the Restate SDK for Node.js/TypeScript,
5
+ * which is released under the MIT license.
6
+ *
7
+ * You can find a copy of the license in file LICENSE in the root
8
+ * directory of this repository or package, or at
9
+ * https://github.com/restatedev/sdk-typescript/blob/main/LICENSE
10
+ */
11
+
12
+ import * as restate from "../public_api";
13
+ import * as wws from "./workflow_wrapper_service";
14
+ import * as wss from "./workflow_state_service";
15
+
16
+ const STATE_SERVICE_PATH_SUFFIX = "_state";
17
+
18
+ // ----------------------------------------------------------------------------
19
+ // workflow definition / registration
20
+ // ----------------------------------------------------------------------------
21
+
22
+ /**
23
+ * Creates a new workflow service that will be served under the given path.
24
+ *
25
+ * A workflow must consist of
26
+ * - one run method: `run(ctx: WfContext, params: T) => Promise<R>`
27
+ * - an arbitrary number of interaction methods: `foo(ctx: SharedWfContext, params: X) => Promise<Y>`
28
+ */
29
+ export function workflow<R, T, U>(
30
+ path: string,
31
+ workflow: Workflow<R, T, U>
32
+ ): WorkflowServices<R, T, U> {
33
+ // the state service manages all state and promises for us
34
+ const stateServiceRouter = wss.workflowStateService;
35
+ const stateServiceApi: restate.ServiceApi<wss.api> = {
36
+ path: path + STATE_SERVICE_PATH_SUFFIX,
37
+ };
38
+
39
+ // the wrapper service manages life cycle, contexts, delegation to the state service
40
+ const wrapperServiceRouter = wws.createWrapperService(
41
+ workflow,
42
+ path,
43
+ stateServiceApi
44
+ );
45
+
46
+ return {
47
+ api: { path } as restate.ServiceApi<WorkflowRestateRpcApi<R, T, U>>,
48
+ registerServices: (endpoint: restate.RestateEndpoint) => {
49
+ endpoint.bindKeyedRouter(stateServiceApi.path, stateServiceRouter);
50
+ endpoint.bindRouter(path, wrapperServiceRouter);
51
+ },
52
+ } satisfies WorkflowServices<R, T, U>;
53
+ }
54
+
55
+ /**
56
+ * The type signature of a workflow.
57
+ * A workflow must consist of
58
+ * - one run method: `run(ctx: WfContext, params: T) => Promise<R>`
59
+ * - an arbitrary number of interaction methods: `foo(ctx: SharedWfContext, params: T) => Promise<R>`
60
+ */
61
+ export type Workflow<R, T, U> = {
62
+ run: RunMethod<R, T>;
63
+ } & WorkflowMethods<R, T, U>;
64
+
65
+ type RunMethod<R, T> = (ctx: WfContext, params: T) => Promise<R>;
66
+ type InteractionMethod<R, T> = (ctx: SharedWfContext, params: T) => Promise<R>;
67
+
68
+ type WorkflowMethods<R, T, U> = {
69
+ [K in keyof U]: K extends "run"
70
+ ? U[K] extends RunMethod<R, T>
71
+ ? U[K]
72
+ : "The 'run' methods needs to follow the signature: (ctx: WfContext, params: any) => Promise<any> "
73
+ : // eslint-disable-next-line @typescript-eslint/no-explicit-any
74
+ U[K] extends InteractionMethod<any, any>
75
+ ? U[K]
76
+ : "Methods other than 'run' are interaction methods and need to follow the signature: (ctx: SharedWfContext, params: any) => Promise<any>";
77
+ };
78
+
79
+ /**
80
+ * The workflow service(s) and API.
81
+ *
82
+ * Register at a Restate endpoint (HTTP/2, Lambda, etc.) as follows:
83
+ * ```
84
+ * const myWorkflow = restate.workflows.workflow("org.acme.myworkflow", {
85
+ * // workflow implementation
86
+ * })
87
+ * restate.createServer().bind(myWorkflow)
88
+ * ```
89
+ *
90
+ * The {@link WorkflowServices.api} can be used to create typed clients, both
91
+ * from other Restate-backed serviced (e.g., `ctx.rpc(api).triggerMySignal()`)
92
+ * or from external clients (`clients.connectWorkflows(restateUri).connectToWorkflow(api, id);`).
93
+ */
94
+ export interface WorkflowServices<R, T, U> extends restate.ServiceBundle {
95
+ readonly api: restate.ServiceApi<WorkflowRestateRpcApi<R, T, U>>;
96
+ }
97
+
98
+ // ----------------------------------------------------------------------------
99
+ // workflow-specific types (promises, contexts)
100
+ // ----------------------------------------------------------------------------
101
+
102
+ export type DurablePromise<T> = restate.CombineablePromise<T> & {
103
+ peek(): Promise<T | null>;
104
+
105
+ resolve(value?: T): void;
106
+ reject(errorMsg: string): void;
107
+ };
108
+
109
+ /**
110
+ * The context for the workflow's interaction methods, which are all methods
111
+ * other than the 'run()' method.
112
+ *
113
+ * This gives primarily access to state reads and promises.
114
+ */
115
+ export interface SharedWfContext {
116
+ workflowId(): string;
117
+
118
+ get<T>(stateName: string): Promise<T | null>;
119
+
120
+ promise<T = void>(name: string): DurablePromise<T>;
121
+ }
122
+
123
+ /**
124
+ * The context for the workflow's 'run()' function.
125
+ *
126
+ * This is a full context as for stateful durable keyed services, plus the
127
+ * workflow-specific bits, like workflowID and durable promises.
128
+ */
129
+ export interface WfContext extends SharedWfContext, restate.KeyedContext {}
130
+
131
+ export enum LifecycleStatus {
132
+ NOT_STARTED = "NOT_STARTED",
133
+ RUNNING = "RUNNING",
134
+ FINISHED = "FINISHED",
135
+ FAILED = "FAILED",
136
+ }
137
+
138
+ export enum WorkflowStartResult {
139
+ STARTED = "STARTED",
140
+ ALREADY_STARTED = "ALREADY_STARTED",
141
+ ALREADY_FINISHED = "ALREADY_FINISHED",
142
+ }
143
+
144
+ // ----------------------------------------------------------------------------
145
+ // types and signatures for typed clients
146
+ // ----------------------------------------------------------------------------
147
+
148
+ /**
149
+ * The type of requests accepted by the workflow service.
150
+ * Must contain the 'workflowId' property.
151
+ */
152
+ export type WorkflowRequest<T> = T & { workflowId: string };
153
+
154
+ /**
155
+ * The API signature of the workflow for use with RPC operations from Restate services.
156
+ */
157
+ export type WorkflowRestateRpcApi<R, T, U> = {
158
+ start: (param: WorkflowRequest<T>) => Promise<WorkflowStartResult>;
159
+ waitForResult: (request: WorkflowRequest<unknown>) => Promise<R>;
160
+ status: (request: WorkflowRequest<unknown>) => Promise<LifecycleStatus>;
161
+ } & {
162
+ [K in keyof Omit<U, "run">]: U[K] extends InteractionMethod<infer R, infer T>
163
+ ? (request: WorkflowRequest<T>) => Promise<R>
164
+ : never;
165
+ };
166
+
167
+ /**
168
+ * The API signature of the workflow for external clients.
169
+ */
170
+ export type WorkflowClientApi<U> = {
171
+ [K in keyof Omit<U, "run">]: U[K] extends (
172
+ ctx: SharedWfContext
173
+ ) => Promise<infer R>
174
+ ? () => Promise<R>
175
+ : U[K] extends (ctx: SharedWfContext, params: infer T) => Promise<infer R>
176
+ ? (request: T) => Promise<R>
177
+ : never;
178
+ };