@laserdata/laser-sdk 0.5.3 → 0.6.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 (356) hide show
  1. package/README.md +105 -79
  2. package/dist/agent/agdx.d.ts +8 -3
  3. package/dist/agent/agdx.d.ts.map +1 -1
  4. package/dist/agent/agdx.js +39 -22
  5. package/dist/agent/agdx.js.map +1 -1
  6. package/dist/agent/builder.d.ts +25 -15
  7. package/dist/agent/builder.d.ts.map +1 -1
  8. package/dist/agent/builder.js +103 -59
  9. package/dist/agent/builder.js.map +1 -1
  10. package/dist/agent/context.d.ts +3 -6
  11. package/dist/agent/context.d.ts.map +1 -1
  12. package/dist/agent/context.js +17 -9
  13. package/dist/agent/context.js.map +1 -1
  14. package/dist/agent/contract.d.ts +2 -2
  15. package/dist/agent/contract.d.ts.map +1 -1
  16. package/dist/agent/contract.js +10 -6
  17. package/dist/agent/contract.js.map +1 -1
  18. package/dist/agent/memory-handler.d.ts +25 -0
  19. package/dist/agent/memory-handler.d.ts.map +1 -0
  20. package/dist/agent/memory-handler.js +43 -0
  21. package/dist/agent/memory-handler.js.map +1 -0
  22. package/dist/agent/registry.d.ts +3 -10
  23. package/dist/agent/registry.d.ts.map +1 -1
  24. package/dist/agent/registry.js +22 -19
  25. package/dist/agent/registry.js.map +1 -1
  26. package/dist/agent/reliable-consumer.d.ts +19 -9
  27. package/dist/agent/reliable-consumer.d.ts.map +1 -1
  28. package/dist/agent/reliable-consumer.js +193 -54
  29. package/dist/agent/reliable-consumer.js.map +1 -1
  30. package/dist/agent/replies.d.ts +6 -0
  31. package/dist/agent/replies.d.ts.map +1 -1
  32. package/dist/agent/replies.js +55 -4
  33. package/dist/agent/replies.js.map +1 -1
  34. package/dist/agent/router.d.ts +1 -1
  35. package/dist/agent/router.d.ts.map +1 -1
  36. package/dist/agent/router.js +5 -19
  37. package/dist/agent/router.js.map +1 -1
  38. package/dist/agent/scope.d.ts +7 -3
  39. package/dist/agent/scope.d.ts.map +1 -1
  40. package/dist/agent/scope.js +11 -1
  41. package/dist/agent/scope.js.map +1 -1
  42. package/dist/agent/timer.d.ts +6 -0
  43. package/dist/agent/timer.d.ts.map +1 -0
  44. package/dist/agent/timer.js +21 -0
  45. package/dist/agent/timer.js.map +1 -0
  46. package/dist/agent/workflow.d.ts +16 -19
  47. package/dist/agent/workflow.d.ts.map +1 -1
  48. package/dist/agent/workflow.js +17 -6
  49. package/dist/agent/workflow.js.map +1 -1
  50. package/dist/blob.d.ts +2 -3
  51. package/dist/blob.d.ts.map +1 -1
  52. package/dist/blob.js.map +1 -1
  53. package/dist/bridges/a2a.d.ts +34 -23
  54. package/dist/bridges/a2a.d.ts.map +1 -1
  55. package/dist/bridges/a2a.js +29 -19
  56. package/dist/bridges/a2a.js.map +1 -1
  57. package/dist/bridges/hops.d.ts +1 -1
  58. package/dist/bridges/hops.d.ts.map +1 -1
  59. package/dist/bridges/hops.js +1 -1
  60. package/dist/bridges/hops.js.map +1 -1
  61. package/dist/bridges/mcp.d.ts +27 -7
  62. package/dist/bridges/mcp.d.ts.map +1 -1
  63. package/dist/bridges/mcp.js +24 -9
  64. package/dist/bridges/mcp.js.map +1 -1
  65. package/dist/client/bytes.d.ts +5 -0
  66. package/dist/client/bytes.d.ts.map +1 -1
  67. package/dist/client/bytes.js +15 -0
  68. package/dist/client/bytes.js.map +1 -1
  69. package/dist/client/capabilities.d.ts +25 -13
  70. package/dist/client/capabilities.d.ts.map +1 -1
  71. package/dist/client/capabilities.js +145 -123
  72. package/dist/client/capabilities.js.map +1 -1
  73. package/dist/client/error-classify.d.ts +57 -0
  74. package/dist/client/error-classify.d.ts.map +1 -0
  75. package/dist/client/error-classify.js +347 -0
  76. package/dist/client/error-classify.js.map +1 -0
  77. package/dist/client/errors.d.ts +113 -40
  78. package/dist/client/errors.d.ts.map +1 -1
  79. package/dist/client/errors.js +191 -53
  80. package/dist/client/errors.js.map +1 -1
  81. package/dist/client/internals.d.ts +1 -0
  82. package/dist/client/internals.d.ts.map +1 -1
  83. package/dist/client/internals.js +1 -0
  84. package/dist/client/internals.js.map +1 -1
  85. package/dist/client/laser.d.ts +62 -46
  86. package/dist/client/laser.d.ts.map +1 -1
  87. package/dist/client/laser.js +206 -120
  88. package/dist/client/laser.js.map +1 -1
  89. package/dist/client/managed.d.ts +3 -0
  90. package/dist/client/managed.d.ts.map +1 -1
  91. package/dist/client/managed.js +70 -3
  92. package/dist/client/managed.js.map +1 -1
  93. package/dist/context-scope.d.ts +25 -9
  94. package/dist/context-scope.d.ts.map +1 -1
  95. package/dist/context-scope.js +36 -9
  96. package/dist/context-scope.js.map +1 -1
  97. package/dist/context.d.ts +33 -16
  98. package/dist/context.d.ts.map +1 -1
  99. package/dist/context.js +174 -95
  100. package/dist/context.js.map +1 -1
  101. package/dist/conversation-state.d.ts +1 -1
  102. package/dist/conversation-state.d.ts.map +1 -1
  103. package/dist/conversation-state.js +35 -16
  104. package/dist/conversation-state.js.map +1 -1
  105. package/dist/govern.d.ts +24 -5
  106. package/dist/govern.d.ts.map +1 -1
  107. package/dist/govern.js +116 -28
  108. package/dist/govern.js.map +1 -1
  109. package/dist/iggy/apache-iggy.d.ts +59 -56
  110. package/dist/iggy/apache-iggy.d.ts.map +1 -1
  111. package/dist/iggy/apache-iggy.js +300 -137
  112. package/dist/iggy/apache-iggy.js.map +1 -1
  113. package/dist/index.d.ts +97 -65
  114. package/dist/index.d.ts.map +1 -1
  115. package/dist/index.js +49 -30
  116. package/dist/index.js.map +1 -1
  117. package/dist/intent.d.ts +16 -2
  118. package/dist/intent.d.ts.map +1 -1
  119. package/dist/intent.js +50 -18
  120. package/dist/intent.js.map +1 -1
  121. package/dist/managed/coordination.d.ts +82 -0
  122. package/dist/managed/coordination.d.ts.map +1 -0
  123. package/dist/managed/coordination.js +367 -0
  124. package/dist/managed/coordination.js.map +1 -0
  125. package/dist/managed/destinations.d.ts +4 -3
  126. package/dist/managed/destinations.d.ts.map +1 -1
  127. package/dist/managed/destinations.js +29 -13
  128. package/dist/managed/destinations.js.map +1 -1
  129. package/dist/managed/filters.d.ts +5 -5
  130. package/dist/managed/filters.d.ts.map +1 -1
  131. package/dist/managed/filters.js +39 -22
  132. package/dist/managed/filters.js.map +1 -1
  133. package/dist/managed/forks.d.ts +8 -11
  134. package/dist/managed/forks.d.ts.map +1 -1
  135. package/dist/managed/forks.js +33 -13
  136. package/dist/managed/forks.js.map +1 -1
  137. package/dist/managed/graph.d.ts +1 -4
  138. package/dist/managed/graph.d.ts.map +1 -1
  139. package/dist/managed/graph.js +5 -3
  140. package/dist/managed/graph.js.map +1 -1
  141. package/dist/managed/kv.d.ts +26 -14
  142. package/dist/managed/kv.d.ts.map +1 -1
  143. package/dist/managed/kv.js +88 -61
  144. package/dist/managed/kv.js.map +1 -1
  145. package/dist/managed/projections.d.ts +5 -8
  146. package/dist/managed/projections.d.ts.map +1 -1
  147. package/dist/managed/projections.js +22 -2
  148. package/dist/managed/projections.js.map +1 -1
  149. package/dist/managed/query.d.ts +20 -6
  150. package/dist/managed/query.d.ts.map +1 -1
  151. package/dist/managed/query.js +97 -24
  152. package/dist/managed/query.js.map +1 -1
  153. package/dist/managed/runs.d.ts +2 -4
  154. package/dist/managed/runs.d.ts.map +1 -1
  155. package/dist/managed/runs.js +9 -1
  156. package/dist/managed/runs.js.map +1 -1
  157. package/dist/managed/watch.d.ts +2 -4
  158. package/dist/managed/watch.d.ts.map +1 -1
  159. package/dist/managed/watch.js +9 -1
  160. package/dist/managed/watch.js.map +1 -1
  161. package/dist/memory/handle.d.ts +75 -22
  162. package/dist/memory/handle.d.ts.map +1 -1
  163. package/dist/memory/handle.js +232 -58
  164. package/dist/memory/handle.js.map +1 -1
  165. package/dist/memory/log-memory.d.ts +24 -12
  166. package/dist/memory/log-memory.d.ts.map +1 -1
  167. package/dist/memory/log-memory.js +214 -100
  168. package/dist/memory/log-memory.js.map +1 -1
  169. package/dist/memory/topic.d.ts +2 -2
  170. package/dist/memory/topic.d.ts.map +1 -1
  171. package/dist/memory/topic.js +7 -5
  172. package/dist/memory/topic.js.map +1 -1
  173. package/dist/memory/types.d.ts +41 -5
  174. package/dist/memory/types.d.ts.map +1 -1
  175. package/dist/memory/types.js +44 -18
  176. package/dist/memory/types.js.map +1 -1
  177. package/dist/memory/vector-memory.d.ts +8 -6
  178. package/dist/memory/vector-memory.d.ts.map +1 -1
  179. package/dist/memory/vector-memory.js +65 -34
  180. package/dist/memory/vector-memory.js.map +1 -1
  181. package/dist/memory.d.ts +6 -5
  182. package/dist/memory.d.ts.map +1 -1
  183. package/dist/memory.js +4 -4
  184. package/dist/memory.js.map +1 -1
  185. package/dist/provenance/agent-topic.d.ts +3 -1
  186. package/dist/provenance/agent-topic.d.ts.map +1 -1
  187. package/dist/provenance/agent-topic.js +3 -1
  188. package/dist/provenance/agent-topic.js.map +1 -1
  189. package/dist/provenance/provenance.d.ts +3 -3
  190. package/dist/provenance/provenance.d.ts.map +1 -1
  191. package/dist/provenance/provenance.js +30 -18
  192. package/dist/provenance/provenance.js.map +1 -1
  193. package/dist/runtime/clock.d.ts +3 -0
  194. package/dist/runtime/clock.d.ts.map +1 -1
  195. package/dist/runtime/clock.js +16 -3
  196. package/dist/runtime/clock.js.map +1 -1
  197. package/dist/schema-codecs.d.ts +7 -3
  198. package/dist/schema-codecs.d.ts.map +1 -1
  199. package/dist/schema-codecs.js +80 -12
  200. package/dist/schema-codecs.js.map +1 -1
  201. package/dist/session.d.ts +27 -18
  202. package/dist/session.d.ts.map +1 -1
  203. package/dist/session.js +95 -37
  204. package/dist/session.js.map +1 -1
  205. package/dist/signing.d.ts +11 -5
  206. package/dist/signing.d.ts.map +1 -1
  207. package/dist/signing.js +64 -21
  208. package/dist/signing.js.map +1 -1
  209. package/dist/snapshot.d.ts +10 -2
  210. package/dist/snapshot.d.ts.map +1 -1
  211. package/dist/snapshot.js +62 -28
  212. package/dist/snapshot.js.map +1 -1
  213. package/dist/state-store.d.ts +1 -1
  214. package/dist/state-store.d.ts.map +1 -1
  215. package/dist/state-store.js.map +1 -1
  216. package/dist/stream/batching.d.ts +81 -0
  217. package/dist/stream/batching.d.ts.map +1 -0
  218. package/dist/stream/batching.js +210 -0
  219. package/dist/stream/batching.js.map +1 -0
  220. package/dist/stream/bson.d.ts +3 -0
  221. package/dist/stream/bson.d.ts.map +1 -0
  222. package/dist/stream/bson.js +245 -0
  223. package/dist/stream/bson.js.map +1 -0
  224. package/dist/stream/codecs.d.ts +58 -6
  225. package/dist/stream/codecs.d.ts.map +1 -1
  226. package/dist/stream/codecs.js +143 -59
  227. package/dist/stream/codecs.js.map +1 -1
  228. package/dist/stream/consumer-group.d.ts +13 -10
  229. package/dist/stream/consumer-group.d.ts.map +1 -1
  230. package/dist/stream/consumer-group.js +91 -31
  231. package/dist/stream/consumer-group.js.map +1 -1
  232. package/dist/stream/consumer-start.d.ts +17 -0
  233. package/dist/stream/consumer-start.d.ts.map +1 -0
  234. package/dist/stream/consumer-start.js +2 -0
  235. package/dist/stream/consumer-start.js.map +1 -0
  236. package/dist/stream/consumer.d.ts +166 -24
  237. package/dist/stream/consumer.d.ts.map +1 -1
  238. package/dist/stream/consumer.js +451 -68
  239. package/dist/stream/consumer.js.map +1 -1
  240. package/dist/stream/cursor.d.ts +5 -12
  241. package/dist/stream/cursor.d.ts.map +1 -1
  242. package/dist/stream/cursor.js +34 -8
  243. package/dist/stream/cursor.js.map +1 -1
  244. package/dist/stream/header-value.d.ts +61 -15
  245. package/dist/stream/header-value.d.ts.map +1 -1
  246. package/dist/stream/header-value.js.map +1 -1
  247. package/dist/stream/message.d.ts +17 -0
  248. package/dist/stream/message.d.ts.map +1 -0
  249. package/dist/stream/message.js +9 -0
  250. package/dist/stream/message.js.map +1 -0
  251. package/dist/stream/producer.d.ts +127 -11
  252. package/dist/stream/producer.d.ts.map +1 -1
  253. package/dist/stream/producer.js +401 -36
  254. package/dist/stream/producer.js.map +1 -1
  255. package/dist/stream/publish.d.ts +6 -15
  256. package/dist/stream/publish.d.ts.map +1 -1
  257. package/dist/stream/publish.js +18 -28
  258. package/dist/stream/publish.js.map +1 -1
  259. package/dist/stream/record.d.ts +4 -6
  260. package/dist/stream/record.d.ts.map +1 -1
  261. package/dist/stream/record.js +4 -3
  262. package/dist/stream/record.js.map +1 -1
  263. package/dist/stream/routing.d.ts +12 -0
  264. package/dist/stream/routing.d.ts.map +1 -1
  265. package/dist/stream/routing.js +6 -1
  266. package/dist/stream/routing.js.map +1 -1
  267. package/dist/stream/stream.d.ts +1 -4
  268. package/dist/stream/stream.d.ts.map +1 -1
  269. package/dist/stream/stream.js +5 -1
  270. package/dist/stream/stream.js.map +1 -1
  271. package/dist/stream/topic.d.ts +22 -23
  272. package/dist/stream/topic.d.ts.map +1 -1
  273. package/dist/stream/topic.js +90 -30
  274. package/dist/stream/topic.js.map +1 -1
  275. package/dist/stream/typed-topic.d.ts +9 -13
  276. package/dist/stream/typed-topic.d.ts.map +1 -1
  277. package/dist/stream/typed-topic.js +25 -10
  278. package/dist/stream/typed-topic.js.map +1 -1
  279. package/dist/swarm.d.ts +0 -1
  280. package/dist/swarm.d.ts.map +1 -1
  281. package/dist/swarm.js +1 -0
  282. package/dist/swarm.js.map +1 -1
  283. package/dist/testing.d.ts +3 -3
  284. package/dist/testing.d.ts.map +1 -1
  285. package/dist/testing.js +3 -3
  286. package/dist/testing.js.map +1 -1
  287. package/dist/types/ids.d.ts +17 -2
  288. package/dist/types/ids.d.ts.map +1 -1
  289. package/dist/types/ids.js +34 -14
  290. package/dist/types/ids.js.map +1 -1
  291. package/dist/wire/agent.d.ts +22 -0
  292. package/dist/wire/agent.d.ts.map +1 -1
  293. package/dist/wire/agent.js +80 -25
  294. package/dist/wire/agent.js.map +1 -1
  295. package/dist/wire/authz.d.ts +35 -1
  296. package/dist/wire/authz.d.ts.map +1 -1
  297. package/dist/wire/authz.js +45 -15
  298. package/dist/wire/authz.js.map +1 -1
  299. package/dist/wire/cbor.d.ts.map +1 -1
  300. package/dist/wire/cbor.js +13 -2
  301. package/dist/wire/cbor.js.map +1 -1
  302. package/dist/wire/checkpoint.d.ts +17 -12
  303. package/dist/wire/checkpoint.d.ts.map +1 -1
  304. package/dist/wire/checkpoint.js +28 -3
  305. package/dist/wire/checkpoint.js.map +1 -1
  306. package/dist/wire/control.d.ts +82 -0
  307. package/dist/wire/control.d.ts.map +1 -1
  308. package/dist/wire/control.js +219 -0
  309. package/dist/wire/control.js.map +1 -1
  310. package/dist/wire/filter-eval.d.ts +41 -7
  311. package/dist/wire/filter-eval.d.ts.map +1 -1
  312. package/dist/wire/filter-eval.js +26 -28
  313. package/dist/wire/filter-eval.js.map +1 -1
  314. package/dist/wire/filter.d.ts +51 -17
  315. package/dist/wire/filter.d.ts.map +1 -1
  316. package/dist/wire/filter.js +99 -10
  317. package/dist/wire/filter.js.map +1 -1
  318. package/dist/wire/fork.d.ts +14 -1
  319. package/dist/wire/fork.d.ts.map +1 -1
  320. package/dist/wire/fork.js +19 -8
  321. package/dist/wire/fork.js.map +1 -1
  322. package/dist/wire/graph.d.ts +3 -0
  323. package/dist/wire/graph.d.ts.map +1 -1
  324. package/dist/wire/graph.js +18 -4
  325. package/dist/wire/graph.js.map +1 -1
  326. package/dist/wire/hello.d.ts +4 -1
  327. package/dist/wire/hello.d.ts.map +1 -1
  328. package/dist/wire/hello.js +13 -3
  329. package/dist/wire/hello.js.map +1 -1
  330. package/dist/wire/index.d.ts +4 -1
  331. package/dist/wire/index.d.ts.map +1 -1
  332. package/dist/wire/index.js +4 -1
  333. package/dist/wire/index.js.map +1 -1
  334. package/dist/wire/kv.d.ts +37 -2
  335. package/dist/wire/kv.d.ts.map +1 -1
  336. package/dist/wire/kv.js +36 -18
  337. package/dist/wire/kv.js.map +1 -1
  338. package/dist/wire/query.d.ts +6 -0
  339. package/dist/wire/query.d.ts.map +1 -1
  340. package/dist/wire/query.js +38 -12
  341. package/dist/wire/query.js.map +1 -1
  342. package/dist/wire/schema.d.ts +29 -6
  343. package/dist/wire/schema.d.ts.map +1 -1
  344. package/dist/wire/schema.js +61 -26
  345. package/dist/wire/schema.js.map +1 -1
  346. package/dist/wire/snapshot.js +1 -1
  347. package/dist/wire/snapshot.js.map +1 -1
  348. package/dist/wire/value.d.ts +10 -2
  349. package/dist/wire/value.d.ts.map +1 -1
  350. package/dist/wire/value.js +29 -3
  351. package/dist/wire/value.js.map +1 -1
  352. package/package.json +2 -3
  353. package/dist/stream/polling-strategy.d.ts +0 -14
  354. package/dist/stream/polling-strategy.d.ts.map +0 -1
  355. package/dist/stream/polling-strategy.js +0 -2
  356. package/dist/stream/polling-strategy.js.map +0 -1
@@ -1,8 +1,11 @@
1
+ import { INTERNAL_RETURN_DELIVERY } from "../client/internals.js";
1
2
  import { mintUlidValue } from "../runtime/ulid.js";
2
- import { CancelledError, FilterExecutionError, InvalidError, TransportError, UnsupportedError } from "../client/errors.js";
3
- const DEFAULT_BATCH_LENGTH = 100;
4
- const DEFAULT_POLL_INTERVAL_MS = 250;
5
- const DEFAULT_START_FROM = { kind: "next" };
3
+ import { CancelledError, FilterExecutionError, InvalidError, TimeoutError, TransportError, UnsupportedError } from "../client/errors.js";
4
+ import { Json } from "./codecs.js";
5
+ const DEFAULT_BATCH_LENGTH = 1000;
6
+ const DEFAULT_POLL_INTERVAL_MS = 0;
7
+ const DEFAULT_POLLING_RETRY_INTERVAL_MS = 1_000;
8
+ const DEFAULT_START_AT = { kind: "next" };
6
9
  function throwIfAborted(signal, message) {
7
10
  if (signal?.aborted === true)
8
11
  throw new CancelledError(message, { cause: signal.reason });
@@ -24,22 +27,52 @@ function delay(ms, signal) {
24
27
  signal?.addEventListener("abort", onAbort, { once: true });
25
28
  });
26
29
  }
30
+ function commitInterval(policy) {
31
+ return "intervalMs" in policy ? policy.intervalMs : undefined;
32
+ }
33
+ function validateCommitPolicy(policy) {
34
+ const interval = commitInterval(policy);
35
+ if (interval !== undefined && (!Number.isFinite(interval) || interval <= 0)) {
36
+ throw new InvalidError("commit policy intervalMs must be a positive finite number");
37
+ }
38
+ if ("count" in policy && (!Number.isSafeInteger(policy.count) || policy.count < 1)) {
39
+ throw new InvalidError("commit policy count must be a positive safe integer");
40
+ }
41
+ }
27
42
  /** Apply the defaults and reject values no consumer can run with. */
28
43
  export function resolveConsumerOptions(options, anonymous) {
44
+ const commitPolicy = options.commitPolicy ?? (anonymous ? { kind: "disabled" } : { kind: "polling" });
45
+ validateCommitPolicy(commitPolicy);
46
+ const initRetries = options.initRetries ?? { retries: 0, intervalMs: 0 };
29
47
  const resolved = {
30
48
  batchLength: options.batchLength ?? DEFAULT_BATCH_LENGTH,
31
- autoCommit: options.autoCommit ?? !anonymous,
32
- startFrom: options.startFrom ?? DEFAULT_START_FROM,
33
- pollIntervalMs: options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS
49
+ autoCommit: commitPolicy.kind !== "disabled",
50
+ commitPolicy,
51
+ startAt: options.startAt ?? DEFAULT_START_AT,
52
+ pollIntervalMs: options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS,
53
+ autoJoinGroup: options.autoJoinGroup ?? true,
54
+ createGroup: options.createGroup ?? true,
55
+ pollingRetryIntervalMs: options.pollingRetryIntervalMs ?? DEFAULT_POLLING_RETRY_INTERVAL_MS,
56
+ initRetries,
57
+ allowReplay: options.allowReplay ?? false
34
58
  };
59
+ if (!Number.isFinite(resolved.pollingRetryIntervalMs) || resolved.pollingRetryIntervalMs <= 0) {
60
+ throw new InvalidError("consumer pollingRetryIntervalMs must be a positive finite number");
61
+ }
62
+ if (!Number.isSafeInteger(initRetries.retries) ||
63
+ initRetries.retries < 0 ||
64
+ !Number.isFinite(initRetries.intervalMs) ||
65
+ (options.initRetries === undefined ? initRetries.intervalMs < 0 : initRetries.intervalMs <= 0)) {
66
+ throw new InvalidError("consumer initRetries needs non-negative retries and a positive intervalMs");
67
+ }
35
68
  if (!Number.isSafeInteger(resolved.batchLength) || resolved.batchLength < 1) {
36
69
  throw new InvalidError("consumer batchLength must be a positive safe integer");
37
70
  }
38
71
  if (!Number.isFinite(resolved.pollIntervalMs) || resolved.pollIntervalMs < 0) {
39
72
  throw new InvalidError("consumer pollIntervalMs must be a non-negative finite number");
40
73
  }
41
- if ((resolved.startFrom.kind === "offset" || resolved.startFrom.kind === "timestamp") &&
42
- resolved.startFrom.value < 0n) {
74
+ if ((resolved.startAt.kind === "offset" || resolved.startAt.kind === "timestampMicros") &&
75
+ resolved.startAt.value < 0n) {
43
76
  throw new InvalidError("consumer start value must be non-negative");
44
77
  }
45
78
  return resolved;
@@ -59,20 +92,38 @@ export class Consumer {
59
92
  topicName;
60
93
  target;
61
94
  reader;
95
+ pollFailures;
62
96
  batchLength;
63
97
  autoCommit;
64
- startFrom;
98
+ commitPolicy;
99
+ startAt;
65
100
  pollIntervalMs;
101
+ pollingRetryIntervalMs;
102
+ allowReplay;
66
103
  buffer = [];
104
+ // Native deliveries handed back, yielded again before the buffer.
105
+ returned = [];
106
+ storedOffsets = new Map();
107
+ yieldedSinceStore = 0;
108
+ commitTimer;
109
+ latestDelivered;
67
110
  // Group reads: the records of the current page, the record each delivered
68
111
  // message came from, and whether a delivery waits to be stored.
69
112
  records = [];
70
113
  delivered = new WeakMap();
71
- handledSincePoll = false;
72
114
  lastDelivery;
115
+ // When a group consumer's commit policy last stored the handled prefix, the
116
+ // records yielded since, and the store decision a failed store left due.
117
+ yieldedSinceFlush = 0;
118
+ lastFlushAt = Date.now();
119
+ pendingCommit;
73
120
  moreScanned = false;
74
121
  consumedOffsets = new Map();
75
122
  explicitOffsets = new Map();
123
+ // The consumed offset a partition had when its server offset was deleted.
124
+ // No commit policy stores that offset again, so the delete stands until a
125
+ // newer record is delivered.
126
+ deletedAt = new Map();
76
127
  nextOffsets = new Map();
77
128
  assignedPartitions = new Set();
78
129
  assignmentSeen = false;
@@ -80,12 +131,16 @@ export class Consumer {
80
131
  gainedPartitions = new Set();
81
132
  started = false;
82
133
  shuttingDown = false;
83
- constructor(transport, streamName, topicName, target, options = {}, reader) {
134
+ polling;
135
+ // A native group consumer leaves at shutdown only the group it joined.
136
+ leavesGroup;
137
+ constructor(transport, streamName, topicName, target, options = {}, reader, pollFailures = "retry") {
84
138
  this.transport = transport;
85
139
  this.streamName = streamName;
86
140
  this.topicName = topicName;
87
141
  this.target = target;
88
142
  this.reader = reader;
143
+ this.pollFailures = pollFailures;
89
144
  const anonymous = target.kind === "single" && target.name === undefined;
90
145
  if (anonymous)
91
146
  this.target = { ...target, name: `anonymous-${mintUlidValue().toString(16)}` };
@@ -96,9 +151,22 @@ export class Consumer {
96
151
  const resolved = resolveConsumerOptions(options, anonymous);
97
152
  this.batchLength = resolved.batchLength;
98
153
  this.autoCommit = resolved.autoCommit;
99
- this.startFrom = resolved.startFrom;
154
+ this.commitPolicy = resolved.commitPolicy;
155
+ this.startAt = resolved.startAt;
100
156
  this.pollIntervalMs = resolved.pollIntervalMs;
157
+ this.pollingRetryIntervalMs = resolved.pollingRetryIntervalMs;
158
+ this.allowReplay = resolved.allowReplay;
159
+ this.leavesGroup = this.target.kind === "group" && resolved.autoJoinGroup;
101
160
  }
161
+ /** @internal */
162
+ static create(transport, streamName, topicName, target, options = {}, reader, pollFailures = "retry") {
163
+ return new Consumer(transport, streamName, topicName, target, options, reader, pollFailures);
164
+ }
165
+ /**
166
+ * Waits for the next record, bounding how long the caller sits idle. Fails
167
+ * with `TimeoutError` past `timeoutMs` and with `InvalidError` once the
168
+ * consumer was shut down, like the Rust `next_within`.
169
+ */
102
170
  async nextWithin(timeoutMs, options = {}) {
103
171
  if (!Number.isFinite(timeoutMs) || timeoutMs < 0) {
104
172
  throw new InvalidError("nextWithin timeout must be a non-negative finite number");
@@ -106,21 +174,23 @@ export class Consumer {
106
174
  const deadline = Date.now() + timeoutMs;
107
175
  for (;;) {
108
176
  if (this.stopped())
109
- return null;
177
+ throw new InvalidError("the live consumer stream ended");
110
178
  throwIfAborted(options.signal, "nextWithin aborted");
111
- this.handledPrevious();
179
+ await this.finishDelivery();
112
180
  const message = this.take();
113
- if (message !== undefined)
181
+ if (message !== undefined) {
182
+ await this.afterYield(message);
114
183
  return message;
115
- await this.fillBuffer(options.signal);
184
+ }
185
+ await this.pollOnce(options.signal, deadline);
116
186
  throwIfAborted(options.signal, "nextWithin aborted");
117
187
  if (this.stopped())
118
- return null;
188
+ throw new InvalidError("the live consumer stream ended");
119
189
  if (this.pending())
120
190
  continue;
121
191
  const remaining = deadline - Date.now();
122
192
  if (remaining <= 0)
123
- return null;
193
+ throw new TimeoutError("the live consumer to yield a record");
124
194
  await delay(this.moreScanned ? 0 : Math.min(this.pollIntervalMs, remaining), options.signal);
125
195
  }
126
196
  }
@@ -130,13 +200,14 @@ export class Consumer {
130
200
  async *stream(options = {}) {
131
201
  while (!this.stopped()) {
132
202
  throwIfAborted(options.signal, "consumer stream aborted");
133
- this.handledPrevious();
203
+ await this.finishDelivery();
134
204
  const message = this.take();
135
205
  if (message !== undefined) {
206
+ await this.afterYield(message);
136
207
  yield message;
137
208
  continue;
138
209
  }
139
- await this.fillBuffer(options.signal);
210
+ await this.pollOnce(options.signal);
140
211
  throwIfAborted(options.signal, "consumer stream aborted");
141
212
  if (this.stopped())
142
213
  return;
@@ -145,6 +216,29 @@ export class Consumer {
145
216
  }
146
217
  }
147
218
  }
219
+ /**
220
+ * Hands back the latest delivery that the caller could not process, so the
221
+ * next read yields it again. Only the most recent delivery can be returned.
222
+ */
223
+ [INTERNAL_RETURN_DELIVERY](message) {
224
+ if (this.stopped())
225
+ throw new InvalidError("consumer has been shut down");
226
+ if (message !== this.latestDelivered) {
227
+ throw new InvalidError("the delivery is not the latest one from this consumer");
228
+ }
229
+ this.latestDelivered = undefined;
230
+ if (this.reader !== undefined) {
231
+ const record = this.delivered.get(message);
232
+ if (record === undefined) {
233
+ throw new InvalidError("the record was not delivered by this group consumer");
234
+ }
235
+ if (this.lastDelivery?.record === record)
236
+ this.lastDelivery = undefined;
237
+ this.records.unshift(record);
238
+ return;
239
+ }
240
+ this.returned.unshift(message);
241
+ }
148
242
  /**
149
243
  * Store a handled message's offset on the server. A group consumer on a
150
244
  * server that resolves group policies stores the contiguous prefix of the
@@ -157,18 +251,65 @@ export class Consumer {
157
251
  throw new InvalidError("commit the message object this group consumer delivered");
158
252
  }
159
253
  await this.reader.ackThrough(record);
160
- this.explicitOffsets.set(message.partitionId, message.offset);
254
+ this.explicitOffsets.set(message.partitionId, message.position.offset);
255
+ this.storedOffsets.set(message.partitionId, message.position.offset);
161
256
  return;
162
257
  }
163
- await this.transport.storeOffset(this.streamName, this.topicName, this.target, message.partitionId, message.offset);
164
- this.explicitOffsets.set(message.partitionId, message.offset);
258
+ await this.transport.storeOffset(this.streamName, this.topicName, this.target, message.partitionId, message.position.offset);
259
+ this.explicitOffsets.set(message.partitionId, message.position.offset);
260
+ this.storedOffsets.set(message.partitionId, message.position.offset);
165
261
  }
166
262
  lastConsumedOffset(partitionId) {
167
263
  return this.consumedOffsets.get(partitionId);
168
264
  }
265
+ /** The last offset this consumer stored for `partitionId`, by `commit`,
266
+ * `storeOffset`, or its commit policy. Local bookkeeping: for server-side
267
+ * resume, start from `next`. */
268
+ lastStoredOffset(partitionId) {
269
+ return this.storedOffsets.get(partitionId);
270
+ }
271
+ /**
272
+ * Stores an explicit server offset for `partitionId`, or for the partition
273
+ * of the latest delivery when omitted. Native only: a policy-aware group
274
+ * consumer refuses it, because an arbitrary offset bypasses the group's
275
+ * acknowledgment contract. Commit a delivered record instead.
276
+ */
277
+ async storeOffset(offset, partitionId) {
278
+ this.requireNative("storeOffset");
279
+ if (offset < 0n)
280
+ throw new InvalidError("offset must be non-negative");
281
+ const partition = this.currentPartition(partitionId);
282
+ await this.transport.storeOffset(this.streamName, this.topicName, this.target, partition, offset);
283
+ this.explicitOffsets.set(partition, offset);
284
+ this.storedOffsets.set(partition, offset);
285
+ }
286
+ /** Deletes the server offset for `partitionId`, or for the partition of the
287
+ * latest delivery when omitted. Native only, like `storeOffset`. */
288
+ async deleteOffset(partitionId) {
289
+ this.requireNative("deleteOffset");
290
+ if (this.transport.deleteOffset === undefined) {
291
+ throw new UnsupportedError("the Apache Iggy client does not expose offset deletion", {
292
+ surface: "stream"
293
+ });
294
+ }
295
+ const partition = this.currentPartition(partitionId);
296
+ await this.transport.deleteOffset(this.streamName, this.topicName, this.target, partition);
297
+ this.explicitOffsets.delete(partition);
298
+ this.storedOffsets.delete(partition);
299
+ const consumed = this.consumedOffsets.get(partition);
300
+ if (consumed === undefined)
301
+ this.deletedAt.delete(partition);
302
+ else
303
+ this.deletedAt.set(partition, consumed);
304
+ }
305
+ /** The offset the server stores for this consumer, its group or its
306
+ * name, on `partitionId`, with the partition head. `undefined` when nothing
307
+ * is stored. */
169
308
  async storedOffset(partitionId) {
170
309
  if (this.transport.getConsumerOffset === undefined) {
171
- throw new UnsupportedError("the Apache Iggy client does not expose consumer offsets");
310
+ throw new UnsupportedError("the Apache Iggy client does not expose consumer offsets", {
311
+ surface: "stream"
312
+ });
172
313
  }
173
314
  const offsetTarget = this.target.kind === "group"
174
315
  ? { kind: "group", name: this.target.name }
@@ -188,24 +329,35 @@ export class Consumer {
188
329
  if (this.shuttingDown)
189
330
  return;
190
331
  this.shuttingDown = true;
332
+ if (this.commitTimer !== undefined) {
333
+ clearInterval(this.commitTimer);
334
+ this.commitTimer = undefined;
335
+ }
191
336
  if (this.reader !== undefined) {
192
- this.handledPrevious();
193
- this.records.length = 0;
194
- await this.reader.close();
337
+ try {
338
+ await this.finishDelivery();
339
+ }
340
+ finally {
341
+ this.records.length = 0;
342
+ await this.reader.close();
343
+ }
195
344
  return;
196
345
  }
197
346
  try {
198
347
  if (this.autoCommit) {
199
348
  for (const [partition, offset] of this.consumedOffsets) {
200
349
  const explicit = this.explicitOffsets.get(partition);
350
+ if (this.deletedAt.get(partition) === offset)
351
+ continue;
201
352
  if (explicit === undefined || explicit < offset) {
202
353
  await this.transport.storeOffset(this.streamName, this.topicName, this.target, partition, offset);
354
+ this.storedOffsets.set(partition, offset);
203
355
  }
204
356
  }
205
357
  }
206
358
  }
207
359
  finally {
208
- if (this.target.kind === "group") {
360
+ if (this.leavesGroup && this.target.kind === "group") {
209
361
  await this.transport.leaveConsumerGroup(this.streamName, this.topicName, this.target.name);
210
362
  }
211
363
  }
@@ -218,9 +370,17 @@ export class Consumer {
218
370
  // the prefix through it handled when the consumer commits on its own.
219
371
  take() {
220
372
  if (this.reader === undefined) {
221
- const message = this.buffer.shift();
222
- if (message !== undefined)
223
- this.consumedOffsets.set(message.partitionId, message.offset);
373
+ const returned = this.returned.shift();
374
+ if (returned !== undefined) {
375
+ this.latestDelivered = returned;
376
+ return returned;
377
+ }
378
+ const polled = this.buffer.shift();
379
+ if (polled === undefined)
380
+ return undefined;
381
+ this.consumedOffsets.set(polled.partitionId, polled.offset);
382
+ const message = withJson(polled, polled.headersMalformed !== undefined);
383
+ this.latestDelivered = message;
224
384
  return message;
225
385
  }
226
386
  let record = this.records.shift();
@@ -229,51 +389,213 @@ export class Consumer {
229
389
  if (record === undefined)
230
390
  return undefined;
231
391
  if (this.autoCommit)
232
- this.lastDelivery = record;
392
+ this.lastDelivery = { record, lastOfPage: this.records.length === 0 };
233
393
  this.consumedOffsets.set(record.partitionId, record.offset);
234
- const message = {
235
- payload: record.payload,
236
- partitionId: record.partitionId,
237
- offset: record.offset,
238
- ...(record.timestampMicros !== undefined ? { timestampMicros: record.timestampMicros } : {}),
239
- headers: record.headers
240
- };
394
+ const message = record.message;
241
395
  this.delivered.set(message, record);
396
+ this.latestDelivered = message;
242
397
  return message;
243
398
  }
399
+ // The native commit policies that store after yielding. `polling` stores on
400
+ // the server during the poll itself, and a group reader stores its handled
401
+ // prefix on its own. A failed store never costs the caller the record: the
402
+ // offset stays pending, and the next yield, timer tick, or shutdown stores
403
+ // it, like the Rust consumer.
404
+ async afterYield(message) {
405
+ if (this.reader !== undefined)
406
+ return;
407
+ const policy = this.commitPolicy;
408
+ this.yieldedSinceStore += 1;
409
+ const store = policy.kind === "each" ||
410
+ policy.kind === "intervalOrEach" ||
411
+ ((policy.kind === "all" || policy.kind === "intervalOrAll") && this.buffer.length === 0) ||
412
+ ((policy.kind === "every" || policy.kind === "intervalOrEvery") &&
413
+ this.yieldedSinceStore >= policy.count);
414
+ if (!store)
415
+ return;
416
+ try {
417
+ await this.storeConsumed(message.partitionId);
418
+ this.yieldedSinceStore = 0;
419
+ }
420
+ catch {
421
+ // Stored on a later attempt.
422
+ }
423
+ }
424
+ async storeConsumed(only) {
425
+ for (const [partition, offset] of this.consumedOffsets) {
426
+ if (only !== undefined && partition !== only)
427
+ continue;
428
+ if ((this.storedOffsets.get(partition) ?? -1n) >= offset)
429
+ continue;
430
+ if (this.deletedAt.get(partition) === offset)
431
+ continue;
432
+ await this.transport.storeOffset(this.streamName, this.topicName, this.target, partition, offset);
433
+ this.storedOffsets.set(partition, offset);
434
+ }
435
+ }
436
+ startCommitTimer() {
437
+ const interval = commitInterval(this.commitPolicy);
438
+ if (interval === undefined || this.commitTimer !== undefined || this.reader !== undefined) {
439
+ return;
440
+ }
441
+ this.commitTimer = setInterval(() => {
442
+ this.storeConsumed().catch(() => undefined);
443
+ }, interval);
444
+ this.commitTimer.unref();
445
+ }
446
+ requireNative(operation) {
447
+ if (this.reader !== undefined) {
448
+ throw new InvalidError(`${operation}() is native only: a policy-aware group consumer commits delivered records`);
449
+ }
450
+ }
451
+ currentPartition(partitionId) {
452
+ const partition = partitionId ??
453
+ (this.target.kind === "single" ? this.target.partitionId : undefined) ??
454
+ this.latestDelivered?.partitionId;
455
+ if (partition === undefined || !Number.isSafeInteger(partition) || partition < 0) {
456
+ throw new InvalidError("name a partition: this consumer has no current partition yet");
457
+ }
458
+ return partition;
459
+ }
460
+ // One poll. A retryable transport failure waits the polling retry interval
461
+ // and reports an empty poll, so the read loop keeps going after a server
462
+ // restart, like the Rust consumer. The wait never runs past `deadline`.
463
+ async pollOnce(signal, deadline) {
464
+ this.startCommitTimer();
465
+ try {
466
+ this.polling ??= this.fillBuffer();
467
+ await this.waitForPoll(this.polling, signal, deadline);
468
+ }
469
+ catch (error) {
470
+ if (this.pollFailures === "propagate" ||
471
+ !(error instanceof TransportError) ||
472
+ !error.retryable ||
473
+ this.stopped()) {
474
+ throw error;
475
+ }
476
+ const wait = deadline === undefined
477
+ ? this.pollingRetryIntervalMs
478
+ : Math.min(this.pollingRetryIntervalMs, Math.max(0, deadline - Date.now()));
479
+ await delay(wait, signal);
480
+ }
481
+ }
482
+ waitForPoll(poll, signal, deadline) {
483
+ return new Promise((resolve, reject) => {
484
+ let waiting = true;
485
+ let timer;
486
+ const finish = (complete, observed) => {
487
+ if (!waiting)
488
+ return;
489
+ waiting = false;
490
+ clearTimeout(timer);
491
+ signal?.removeEventListener("abort", onAbort);
492
+ if (observed && this.polling === poll)
493
+ this.polling = undefined;
494
+ complete();
495
+ };
496
+ const onAbort = () => {
497
+ finish(() => {
498
+ reject(new CancelledError("consumer poll aborted", { cause: signal?.reason }));
499
+ }, false);
500
+ };
501
+ poll.then(() => {
502
+ if (deadline !== undefined && Date.now() > deadline) {
503
+ finish(() => {
504
+ reject(new TimeoutError("the live consumer to yield a record"));
505
+ }, true);
506
+ }
507
+ else {
508
+ finish(resolve, true);
509
+ }
510
+ }, (error) => {
511
+ finish(() => {
512
+ reject(error instanceof Error
513
+ ? error
514
+ : new TransportError("consumer poll failed", true, { cause: error }));
515
+ }, true);
516
+ });
517
+ signal?.addEventListener("abort", onAbort, { once: true });
518
+ if (signal?.aborted === true) {
519
+ onAbort();
520
+ }
521
+ else if (deadline !== undefined) {
522
+ const tick = () => {
523
+ if (!waiting)
524
+ return;
525
+ const remaining = deadline - Date.now();
526
+ if (remaining <= 0) {
527
+ finish(() => {
528
+ reject(new TimeoutError("the live consumer to yield a record"));
529
+ }, false);
530
+ }
531
+ else {
532
+ timer = setTimeout(tick, Math.min(remaining, 2_147_483_647));
533
+ }
534
+ };
535
+ tick();
536
+ }
537
+ });
538
+ }
244
539
  pending() {
245
- return this.reader === undefined ? this.buffer.length > 0 : this.records.length > 0;
540
+ return this.reader === undefined
541
+ ? this.buffer.length > 0 || this.returned.length > 0
542
+ : this.records.length > 0;
246
543
  }
247
544
  stopped() {
248
545
  return this.shuttingDown;
249
546
  }
250
547
  // Asking for the next record or shutting down confirms that the previous
251
- // delivery reached the caller. A cancelled poll cannot complete a record
548
+ // delivery reached the caller, and the commit policy decides whether the
549
+ // handled prefix is stored now. A cancelled poll cannot complete a record
252
550
  // that the caller never received.
253
- handledPrevious() {
254
- if (this.lastDelivery === undefined || this.reader === undefined)
551
+ async finishDelivery() {
552
+ if (this.reader === undefined)
553
+ return;
554
+ const delivery = this.lastDelivery;
555
+ if (delivery !== undefined) {
556
+ this.lastDelivery = undefined;
557
+ this.reader.handled(delivery.record);
558
+ this.yieldedSinceFlush += 1;
559
+ this.pendingCommit = { kind: "delivered", lastOfPage: delivery.lastOfPage };
560
+ }
561
+ const point = this.pendingCommit;
562
+ if (point === undefined)
255
563
  return;
256
- this.reader.handled(this.lastDelivery);
257
- this.lastDelivery = undefined;
258
- this.handledSincePoll = true;
564
+ await this.flushIfDue(this.reader, point);
565
+ this.pendingCommit = undefined;
259
566
  }
260
- // One group poll. The delivered prefix is stored first, so a crash
261
- // redelivers the current batch instead of skipping it.
262
- async fillFromGroup(reader) {
263
- if (this.handledSincePoll) {
264
- try {
265
- await reader.flushCompleted();
266
- }
267
- catch (error) {
268
- // A policy change fences the prefix read under the old policy. Those
269
- // records are delivered again from the stored offset, so nothing is
270
- // lost and the consumer goes on without an error.
271
- if (!(error instanceof FilterExecutionError) || error.reason !== "conflict")
272
- throw error;
273
- this.records.length = 0;
274
- }
275
- this.handledSincePoll = false;
567
+ // Stores the handled prefix when the commit policy says so at this point.
568
+ async flushIfDue(reader, point) {
569
+ if (!commitDue(this.commitPolicy, this.yieldedSinceFlush, Date.now() - this.lastFlushAt, point)) {
570
+ return;
276
571
  }
572
+ try {
573
+ await reader.flushCompleted();
574
+ }
575
+ catch (error) {
576
+ const reason = error instanceof FilterExecutionError ? error.detail.reason : undefined;
577
+ if (reason !== "conflict" && reason !== "source_changed")
578
+ throw error;
579
+ const owned = this.records.filter((record) => reader.owns(record));
580
+ this.records.splice(0, this.records.length, ...owned);
581
+ this.pendingCommit = undefined;
582
+ this.yieldedSinceFlush = 0;
583
+ // A policy change fences the prefix read under the old policy. Those
584
+ // records are delivered again from the stored offset, so nothing is
585
+ // lost and the consumer goes on without an error.
586
+ if (reason !== "conflict")
587
+ throw error;
588
+ this.lastFlushAt = Date.now();
589
+ return;
590
+ }
591
+ this.yieldedSinceFlush = 0;
592
+ this.lastFlushAt = Date.now();
593
+ }
594
+ // One group poll. A policy that stores at the poll stores the delivered
595
+ // prefix first, so a crash redelivers the current batch instead of
596
+ // skipping it.
597
+ async fillFromGroup(reader) {
598
+ await this.flushIfDue(reader, { kind: "poll" });
277
599
  const [page, more] = await reader.readRound();
278
600
  this.moreScanned = more;
279
601
  if (page !== undefined)
@@ -290,7 +612,7 @@ export class Consumer {
290
612
  }
291
613
  let target = this.target;
292
614
  if (target.kind === "group" &&
293
- (this.startFrom.kind !== "next" || !this.autoCommit) &&
615
+ (this.startAt.kind !== "next" || !this.autoCommit) &&
294
616
  this.transport.syncConsumerGroup !== undefined) {
295
617
  const assignment = await this.transport.syncConsumerGroup(this.streamName, this.topicName, target.name);
296
618
  if (assignment === undefined)
@@ -328,15 +650,76 @@ export class Consumer {
328
650
  : partition !== undefined
329
651
  ? this.gainedPartitions.has(partition)
330
652
  ? { kind: "next" }
331
- : this.startFrom
653
+ : this.startAt
332
654
  : this.started
333
655
  ? { kind: "next" }
334
- : this.startFrom;
335
- const polled = await this.transport.pollMessages(this.streamName, this.topicName, target, strategy, this.batchLength, this.autoCommit);
656
+ : this.startAt;
657
+ const commitOnPoll = this.commitPolicy.kind === "polling" || this.commitPolicy.kind === "intervalOrPolling";
658
+ const polled = await this.transport.pollMessages(this.streamName, this.topicName, target, strategy, this.batchLength, commitOnPoll);
336
659
  this.started = true;
337
660
  for (const message of polled)
338
661
  this.nextOffsets.set(message.partitionId, message.offset + 1n);
339
- this.buffer.push(...polled);
662
+ const fresh = this.allowReplay
663
+ ? polled
664
+ : polled.filter((message) => {
665
+ const consumed = this.consumedOffsets.get(message.partitionId);
666
+ return consumed === undefined || message.offset > consumed;
667
+ });
668
+ if (commitOnPoll) {
669
+ for (const message of fresh)
670
+ this.storedOffsets.set(message.partitionId, message.offset);
671
+ }
672
+ this.buffer.push(...fresh);
673
+ }
674
+ }
675
+ function withJson(message, headersMalformed) {
676
+ const json = (decodeValue) => new Json(decodeValue).decode(message.payload);
677
+ const position = { partitionId: message.partitionId, offset: message.offset };
678
+ const delivered = {
679
+ payload: message.payload,
680
+ id: position,
681
+ partitionId: message.partitionId,
682
+ headers: message.headers,
683
+ position,
684
+ messageId: message.messageId ?? 0n,
685
+ checksum: message.checksum ?? 0n,
686
+ currentOffset: message.currentOffset ?? 0n,
687
+ timestampMicros: message.timestampMicros ?? 0n,
688
+ originTimestampMicros: message.originTimestampMicros ?? 0n,
689
+ ...(message.userHeaders !== undefined ? { userHeaders: message.userHeaders } : {}),
690
+ headersMalformed
691
+ };
692
+ // Not enumerable, so a delivered message still compares equal to its plain
693
+ // fields.
694
+ return Object.defineProperty(delivered, "json", { value: json });
695
+ }
696
+ // Whether an automatic commit policy stores the handled prefix now, like the
697
+ // Rust `commit_due`. `yielded` counts records delivered since the last store
698
+ // and `elapsedMs` the time since it.
699
+ function commitDue(policy, yielded, elapsedMs, point) {
700
+ const polling = point.kind === "poll" && yielded > 0;
701
+ const lastOfPage = point.kind === "delivered" && point.lastOfPage;
702
+ const delivered = point.kind === "delivered";
703
+ switch (policy.kind) {
704
+ case "disabled":
705
+ return false;
706
+ case "polling":
707
+ return polling;
708
+ case "interval":
709
+ return elapsedMs >= policy.intervalMs;
710
+ case "intervalOrPolling":
711
+ return polling || elapsedMs >= policy.intervalMs;
712
+ case "all":
713
+ return lastOfPage;
714
+ case "intervalOrAll":
715
+ return lastOfPage || elapsedMs >= policy.intervalMs;
716
+ case "each":
717
+ case "intervalOrEach":
718
+ return delivered && yielded > 0;
719
+ case "every":
720
+ return delivered && yielded >= policy.count;
721
+ case "intervalOrEvery":
722
+ return (delivered && yielded >= policy.count) || elapsedMs >= policy.intervalMs;
340
723
  }
341
724
  }
342
725
  //# sourceMappingURL=consumer.js.map