@ferricstore/ferricstore 0.13.0 → 0.13.2

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 (74) hide show
  1. package/README.md +53 -1
  2. package/dist/index.cjs +230 -82
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.d.cts +30 -6
  5. package/dist/index.d.ts +30 -6
  6. package/dist/index.js +229 -82
  7. package/dist/index.js.map +1 -1
  8. package/dist/langgraph.cjs.map +1 -1
  9. package/dist/langgraph.js.map +1 -1
  10. package/dist/openai-agents.cjs.map +1 -1
  11. package/dist/openai-agents.js.map +1 -1
  12. package/docs/api/assets/hierarchy.js +1 -1
  13. package/docs/api/assets/highlight.css +9 -2
  14. package/docs/api/assets/navigation.js +1 -1
  15. package/docs/api/assets/search.js +1 -1
  16. package/docs/api/classes/ConnectionClosedError.html +4 -4
  17. package/docs/api/classes/FerricStoreClient.html +13 -13
  18. package/docs/api/classes/FerricStoreError.html +1 -0
  19. package/docs/api/classes/FlowAlreadyExistsError.html +3 -3
  20. package/docs/api/classes/FlowNotFoundError.html +3 -3
  21. package/docs/api/classes/FlowWrongStateError.html +3 -3
  22. package/docs/api/classes/HTTPTransportError.html +13 -7
  23. package/docs/api/classes/InvalidCommandError.html +3 -3
  24. package/docs/api/classes/LeaseRenewalError.html +1 -1
  25. package/docs/api/classes/LockHeldError.html +3 -3
  26. package/docs/api/classes/LockNotOwnedError.html +3 -3
  27. package/docs/api/classes/NativeAdapter.html +3 -3
  28. package/docs/api/classes/OverloadedError.html +5 -5
  29. package/docs/api/classes/QueueCompletionError.html +1 -1
  30. package/docs/api/classes/RequestNotSentError.html +225 -0
  31. package/docs/api/classes/RequestTimeoutError.html +6 -6
  32. package/docs/api/classes/RerouteError.html +3 -3
  33. package/docs/api/classes/StaleLeaseError.html +3 -3
  34. package/docs/api/classes/StalePolicyGenerationError.html +3 -3
  35. package/docs/api/classes/WorkflowContext.html +7 -9
  36. package/docs/api/functions/classifyServerError.html +1 -1
  37. package/docs/api/functions/mapException.html +1 -1
  38. package/docs/api/hierarchy.html +21 -20
  39. package/docs/api/index.html +38 -2
  40. package/docs/api/interfaces/AdvanceOptions.html +9 -5
  41. package/docs/api/interfaces/AlreadyAppliedOutcome.html +1 -1
  42. package/docs/api/interfaces/AppliedStepOutcome.html +1 -1
  43. package/docs/api/interfaces/CancelOptions.html +12 -8
  44. package/docs/api/interfaces/CompleteOptions.html +12 -8
  45. package/docs/api/interfaces/CompleteOutcome.html +1 -1
  46. package/docs/api/interfaces/ExtendLeaseOptions.html +10 -8
  47. package/docs/api/interfaces/FailOptions.html +12 -8
  48. package/docs/api/interfaces/FailOutcome.html +1 -1
  49. package/docs/api/interfaces/HistoryOptions.html +16 -16
  50. package/docs/api/interfaces/LeaseMutationOptions.html +10 -6
  51. package/docs/api/interfaces/MutateOptions.html +7 -3
  52. package/docs/api/interfaces/NamedValueMutation.html +1 -1
  53. package/docs/api/interfaces/ReadOptions.html +11 -11
  54. package/docs/api/interfaces/RetryOptions.html +12 -8
  55. package/docs/api/interfaces/RetryOutcome.html +1 -1
  56. package/docs/api/interfaces/RunStepsItem.html +3 -3
  57. package/docs/api/interfaces/RunStepsManyOptions.html +11 -11
  58. package/docs/api/interfaces/SearchOptions.html +12 -12
  59. package/docs/api/interfaces/StepContinueOptions.html +15 -11
  60. package/docs/api/interfaces/StepOptions.html +15 -8
  61. package/docs/api/interfaces/StepResult.html +3 -3
  62. package/docs/api/interfaces/TDigestCreateOptions.html +1 -1
  63. package/docs/api/interfaces/TDigestMergeOptions.html +1 -1
  64. package/docs/api/interfaces/TransitionOptions.html +14 -10
  65. package/docs/api/interfaces/TransitionOutcome.html +1 -1
  66. package/docs/api/media/design.md +22 -1
  67. package/docs/api/modules.html +2 -2
  68. package/docs/api/types/ConnectionRequestDisposition.html +1 -1
  69. package/docs/api/types/ManagementPairs.html +1 -1
  70. package/docs/api/types/RequestDisposition.html +1 -1
  71. package/docs/api/types/SearchStateMeta.html +1 -1
  72. package/docs/api/variables/FERRICSTORE_SDK_VERSION.html +1 -1
  73. package/docs/design.md +22 -1
  74. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -69,6 +69,7 @@ __export(index_exports, {
69
69
  QueueWorker: () => QueueWorker,
70
70
  RawCodec: () => RawCodec,
71
71
  ReconnectingExecutor: () => ReconnectingExecutor,
72
+ RequestNotSentError: () => RequestNotSentError,
72
73
  RequestTimeoutError: () => RequestTimeoutError,
73
74
  RerouteError: () => RerouteError,
74
75
  RoutingTopology: () => RoutingTopology,
@@ -131,9 +132,16 @@ var FerricStoreError = class extends Error {
131
132
  };
132
133
  var HTTPTransportError = class extends FerricStoreError {
133
134
  code = "http_transport";
135
+ requestDisposition;
134
136
  statusCode;
135
137
  constructor(message, options = {}) {
136
- super(message, options);
138
+ const disposition = options.requestDisposition;
139
+ super(message, {
140
+ ...options,
141
+ retryable: options.retryable ?? (disposition === "unsent" ? false : void 0),
142
+ safeToRetry: options.safeToRetry ?? (disposition === "unsent" ? true : disposition === "possibly_sent" ? false : void 0)
143
+ });
144
+ this.requestDisposition = disposition;
137
145
  this.statusCode = options.statusCode;
138
146
  }
139
147
  };
@@ -158,6 +166,20 @@ var RequestTimeoutError = class extends FerricStoreError {
158
166
  this.timeoutMs = timeoutMs;
159
167
  }
160
168
  };
169
+ var RequestNotSentError = class extends FerricStoreError {
170
+ code = "request_not_sent";
171
+ requestDisposition = "unsent";
172
+ constructor(message, options = {}) {
173
+ super(message, { ...options, retryable: false, safeToRetry: true });
174
+ }
175
+ };
176
+ function requestNotSentError(error, message) {
177
+ if (error instanceof RequestNotSentError) return error;
178
+ return new RequestNotSentError(
179
+ message ?? (error instanceof Error ? error.message : String(error)),
180
+ { cause: error, raw: error }
181
+ );
182
+ }
161
183
  var FlowNotFoundError = class extends FerricStoreError {
162
184
  code = "flow_not_found";
163
185
  };
@@ -6416,7 +6438,10 @@ function decodeCompactPipeline(data, budget, claimModes) {
6416
6438
  );
6417
6439
  offset = read.offset;
6418
6440
  } else {
6419
- throw new FerricStoreError("unknown compact pipeline status");
6441
+ throw new FerricStoreError(`unknown compact pipeline status ${status}`, {
6442
+ retryable: false,
6443
+ safeToRetry: false
6444
+ });
6420
6445
  }
6421
6446
  }
6422
6447
  if (offset !== data.byteLength) throw new FerricStoreError("trailing compact pipeline bytes");
@@ -6586,6 +6611,7 @@ var COUNTED_COMPACT_TAGS = /* @__PURE__ */ new Set([
6586
6611
  ]);
6587
6612
 
6588
6613
  // src/protocol-response.ts
6614
+ var KNOWN_NATIVE_RESPONSE_STATUSES = /* @__PURE__ */ new Set([0, 1, 2, 3, 4, 5, 6]);
6589
6615
  function decodeResponse(frame, expectedOpcode, hints = {}) {
6590
6616
  if (frame.opcode !== expectedOpcode) {
6591
6617
  throw new FerricStoreError(
@@ -6610,6 +6636,13 @@ function decodeResponse(frame, expectedOpcode, hints = {}) {
6610
6636
  customPayload
6611
6637
  );
6612
6638
  if (status === STATUS_OK) return value;
6639
+ if (!KNOWN_NATIVE_RESPONSE_STATUSES.has(status)) {
6640
+ throw new FerricStoreError(`unknown native response status ${status}`, {
6641
+ raw: value,
6642
+ retryable: false,
6643
+ safeToRetry: false
6644
+ });
6645
+ }
6613
6646
  const message = protocolErrorMessage(status, value);
6614
6647
  throw classifyServerError(message, value, void 0, status);
6615
6648
  }
@@ -6649,7 +6682,7 @@ function unwrapPipelineResponse(value, options = {}, expectedItems) {
6649
6682
  continue;
6650
6683
  }
6651
6684
  const error = classifyServerError(
6652
- protocolErrorMessage(status === "busy" ? 4 : 1, payload),
6685
+ protocolErrorMessage(pipelineStatusCode(status), payload),
6653
6686
  payload,
6654
6687
  void 0,
6655
6688
  status
@@ -6658,15 +6691,46 @@ function unwrapPipelineResponse(value, options = {}, expectedItems) {
6658
6691
  out[index] = error;
6659
6692
  continue;
6660
6693
  }
6694
+ if (pipelineStatusToken(item[0]) != null) {
6695
+ const unknown = pipelineStatusToken(item[0]) ?? "unknown";
6696
+ const error = new FerricStoreError(`unknown native pipeline status ${JSON.stringify(unknown)}`, {
6697
+ raw: item,
6698
+ retryable: false,
6699
+ safeToRetry: false
6700
+ });
6701
+ if (options.throwOnItemError !== false) throw error;
6702
+ out[index] = error;
6703
+ continue;
6704
+ }
6661
6705
  }
6662
6706
  out[index] = item;
6663
6707
  }
6664
6708
  return out;
6665
6709
  }
6666
6710
  function pipelineStatus(value) {
6711
+ const status = pipelineStatusToken(value);
6712
+ if (status == null) return null;
6713
+ return status === "ok" || status === "error" || status === "auth" || status === "noperm" || status === "busy" || status === "reroute" || status === "bad_request" ? status : null;
6714
+ }
6715
+ function pipelineStatusToken(value) {
6667
6716
  if (typeof value !== "string" && !import_node_buffer26.Buffer.isBuffer(value) && !(value instanceof Uint8Array)) return null;
6668
- const status = asText(value).toLowerCase();
6669
- return status === "ok" || status === "busy" || status === "error" ? status : null;
6717
+ return asText(value).toLowerCase();
6718
+ }
6719
+ function pipelineStatusCode(status) {
6720
+ switch (status) {
6721
+ case "error":
6722
+ return 1;
6723
+ case "auth":
6724
+ return 2;
6725
+ case "noperm":
6726
+ return 3;
6727
+ case "busy":
6728
+ return 4;
6729
+ case "reroute":
6730
+ return 5;
6731
+ case "bad_request":
6732
+ return 6;
6733
+ }
6670
6734
  }
6671
6735
  function decodeResponseValue(opcode, body, hints, allowCompact, customPayload) {
6672
6736
  if (customPayload && allowCompact) {
@@ -8326,6 +8390,22 @@ function plainRecord(value) {
8326
8390
  return value;
8327
8391
  }
8328
8392
 
8393
+ // src/native-command-preparation.ts
8394
+ function prepareNativeCommand(args, maxFrameBytes) {
8395
+ try {
8396
+ return buildProtocolCommand(args, maxFrameBytes);
8397
+ } catch (error) {
8398
+ throw requestNotSentError(error);
8399
+ }
8400
+ }
8401
+ function encodeNativeRequest(command, requestId, maxFrameBytes) {
8402
+ try {
8403
+ return encodeRequest(command, requestId, maxFrameBytes);
8404
+ } catch (error) {
8405
+ throw requestNotSentError(error);
8406
+ }
8407
+ }
8408
+
8329
8409
  // src/native-adapter.ts
8330
8410
  var NativeAdapter = class _NativeAdapter {
8331
8411
  socket;
@@ -8433,15 +8513,13 @@ var NativeAdapter = class _NativeAdapter {
8433
8513
  return await this.executeCommandArgs(args);
8434
8514
  }
8435
8515
  async executeCommandArgs(args) {
8436
- const command = buildProtocolCommand(args, this.capabilities.requestFrameBytes);
8516
+ const command = prepareNativeCommand(args, this.capabilities.requestFrameBytes);
8437
8517
  return await this.request(command);
8438
8518
  }
8439
8519
  /** @internal Execute raw command arguments on a topology-selected lane. */
8440
8520
  async executeCommandOnLane(args, laneId) {
8441
- return await this.executeProtocolCommand(
8442
- buildProtocolCommand(args, this.capabilities.requestFrameBytes),
8443
- laneId
8444
- );
8521
+ const command = prepareNativeCommand(args, this.capabilities.requestFrameBytes);
8522
+ return await this.executeProtocolCommand(command, laneId);
8445
8523
  }
8446
8524
  async executeProtocolCommand(command, laneId) {
8447
8525
  return await this.request(laneId == null ? command : { ...command, laneId });
@@ -8535,14 +8613,14 @@ var NativeAdapter = class _NativeAdapter {
8535
8613
  let frame;
8536
8614
  try {
8537
8615
  requestId = this.requestScheduler.nextRequestId();
8538
- frame = encodeRequest(
8616
+ frame = encodeNativeRequest(
8539
8617
  withFlowQueryDeadline(command, timeoutMs),
8540
8618
  requestId,
8541
8619
  this.capabilities.requestFrameBytes
8542
8620
  );
8543
8621
  } catch (error) {
8544
8622
  if (hasFlowControlCredit) this.flowControl.release(laneId);
8545
- return Promise.reject(error instanceof Error ? error : new FerricStoreError(String(error), { raw: error }));
8623
+ return Promise.reject(error instanceof Error ? error : requestNotSentError(error));
8546
8624
  }
8547
8625
  return new Promise((resolve, reject) => {
8548
8626
  const timer = timeoutMs == null ? void 0 : setLongTimeout(() => {
@@ -9117,7 +9195,9 @@ var HTTPTransport = class {
9117
9195
  #requests = /* @__PURE__ */ new Set();
9118
9196
  #closed = false;
9119
9197
  async post(body, timeoutMs) {
9120
- if (this.#closed) throw new HTTPTransportError("HTTP transport is closed");
9198
+ if (this.#closed) {
9199
+ throw new HTTPTransportError("HTTP transport is closed", { requestDisposition: "unsent" });
9200
+ }
9121
9201
  const controller = new AbortController();
9122
9202
  this.#requests.add(controller);
9123
9203
  const timer = timeoutMs == null ? void 0 : setLongTimeout(() => controller.abort(requestTimeoutReason), timeoutMs);
@@ -9134,9 +9214,22 @@ var HTTPTransport = class {
9134
9214
  raw: { retryable: true, safe_to_retry: false }
9135
9215
  });
9136
9216
  }
9137
- if (error instanceof HTTPTransportError || error instanceof RequestTimeoutError) throw error;
9217
+ if (error instanceof RequestTimeoutError) throw error;
9218
+ if (error instanceof HTTPTransportError) {
9219
+ if (error.requestDisposition != null) throw error;
9220
+ throw new HTTPTransportError(error.message, {
9221
+ cause: error.cause ?? error,
9222
+ raw: error.raw,
9223
+ requestDisposition: "possibly_sent",
9224
+ retryAfterMs: error.retryAfterMs,
9225
+ retryable: error.retryable,
9226
+ safeToRetry: false,
9227
+ statusCode: error.statusCode
9228
+ });
9229
+ }
9138
9230
  throw new HTTPTransportError("HTTP command request failed", {
9139
9231
  cause: error,
9232
+ requestDisposition: "possibly_sent",
9140
9233
  retryable: true,
9141
9234
  safeToRetry: false
9142
9235
  });
@@ -9148,7 +9241,9 @@ var HTTPTransport = class {
9148
9241
  async close() {
9149
9242
  if (this.#closed) return;
9150
9243
  this.#closed = true;
9151
- const error = new HTTPTransportError("HTTP transport is closed");
9244
+ const error = new HTTPTransportError("HTTP transport is closed", {
9245
+ requestDisposition: "possibly_sent"
9246
+ });
9152
9247
  for (const controller of this.#requests) controller.abort(error);
9153
9248
  this.#httpAgent.destroy();
9154
9249
  this.#httpsAgent.destroy();
@@ -9391,16 +9486,22 @@ var HTTPAdapter = class _HTTPAdapter {
9391
9486
  await this.#transport.close();
9392
9487
  }
9393
9488
  async executeBatch(commands) {
9394
- if (commands.length > this.#config.maxBatchItems) {
9395
- throw new HTTPTransportError("HTTP command batch exceeds maxBatchItems");
9396
- }
9397
- for (const command of commands) assertHTTPCommandSupported(command[0]);
9398
- const prepared = commands.map((command) => prepareHTTPCommand(command, this.#config.maxRequestBytes));
9399
- const body = encodeHTTPCommands(prepared.map((command) => command.encoded), this.#config.maxRequestBytes);
9400
- if (body.byteLength > this.#config.maxRequestBytes) {
9401
- throw new HTTPTransportError("HTTP command request exceeds maxRequestBytes");
9489
+ let body;
9490
+ let serverBlockMs;
9491
+ try {
9492
+ if (commands.length > this.#config.maxBatchItems) {
9493
+ throw new HTTPTransportError("HTTP command batch exceeds maxBatchItems");
9494
+ }
9495
+ for (const command of commands) assertHTTPCommandSupported(command[0]);
9496
+ const prepared = commands.map((command) => prepareHTTPCommand(command, this.#config.maxRequestBytes));
9497
+ body = encodeHTTPCommands(prepared.map((command) => command.encoded), this.#config.maxRequestBytes);
9498
+ if (body.byteLength > this.#config.maxRequestBytes) {
9499
+ throw new HTTPTransportError("HTTP command request exceeds maxRequestBytes");
9500
+ }
9501
+ serverBlockMs = combinedServerBlockMs(prepared.map((command) => command.serverBlockMs));
9502
+ } catch (error) {
9503
+ throw unsentHTTPError(error);
9402
9504
  }
9403
- const serverBlockMs = combinedServerBlockMs(prepared.map((command) => command.serverBlockMs));
9404
9505
  const response = await this.#transport.post(
9405
9506
  body,
9406
9507
  serverResponseTimeoutMs(this.#config.timeoutMs, serverBlockMs)
@@ -9457,16 +9558,32 @@ function commandError(value) {
9457
9558
  }
9458
9559
  function topLevelError(status, envelope, retryAfter) {
9459
9560
  const details = isRecord2(envelope.error) ? envelope.error : {};
9561
+ const code = typeof details.code === "string" ? details.code.toLowerCase() : void 0;
9460
9562
  const message = typeof details.message === "string" ? details.message : `HTTP command request failed with status ${status}`;
9461
9563
  const retryAfterMs2 = retryAfterMilliseconds(retryAfter);
9564
+ const ambiguousTimeout = status === 408 || code === "request_timeout";
9565
+ const safeToRetry = !ambiguousTimeout && (details.safe_to_retry === true || definitelyRejectedHTTPStatus(status));
9462
9566
  return new HTTPTransportError(message, {
9463
9567
  raw: details,
9464
9568
  retryable: status === 408 || status === 425 || status === 429 || status >= 500,
9465
9569
  retryAfterMs: retryAfterMs2,
9466
- safeToRetry: false,
9570
+ requestDisposition: "possibly_sent",
9571
+ safeToRetry,
9467
9572
  statusCode: status
9468
9573
  });
9469
9574
  }
9575
+ function definitelyRejectedHTTPStatus(status) {
9576
+ return status === 400 || status === 401 || status === 403 || status === 404 || status === 405 || status === 406 || status === 411 || status === 413 || status === 414 || status === 415 || status === 422 || status === 426 || status === 431;
9577
+ }
9578
+ function unsentHTTPError(error) {
9579
+ if (error instanceof HTTPTransportError && error.requestDisposition === "unsent") return error;
9580
+ const message = error instanceof Error ? error.message : String(error);
9581
+ return new HTTPTransportError(message, {
9582
+ cause: error,
9583
+ raw: error instanceof FerricStoreError ? error.raw ?? error : error,
9584
+ requestDisposition: "unsent"
9585
+ });
9586
+ }
9470
9587
  function retryAfterMilliseconds(value) {
9471
9588
  if (value == null) return void 0;
9472
9589
  if (/^\d+$/u.test(value)) {
@@ -9559,9 +9676,9 @@ var ReconnectingExecutor = class {
9559
9676
  const snapshot = snapshotPipelineCommands(commands);
9560
9677
  const snapshotOptions = snapshotPipelineOptions(options) ?? {};
9561
9678
  for (const command of snapshot) assertCommandHasStableConnectionState(command);
9562
- if (this.closed) throw new FerricStoreError("FerricStore client is closed");
9679
+ if (this.closed) throw new RequestNotSentError("FerricStore client is closed");
9563
9680
  const initialExecutor = await this.executorPromise;
9564
- if (this.closed) throw new FerricStoreError("FerricStore client is closed");
9681
+ if (this.closed) throw new RequestNotSentError("FerricStore client is closed");
9565
9682
  if (initialExecutor.executePipeline == null) {
9566
9683
  return await executeCommandArraysIndividually(
9567
9684
  async (command) => await this.executeCommandArgs(command),
@@ -9582,9 +9699,9 @@ var ReconnectingExecutor = class {
9582
9699
  const snapshot = snapshotPipelineCommands(commands);
9583
9700
  const snapshotOptions = snapshotPipelineOptions(options) ?? {};
9584
9701
  for (const command of snapshot) assertCommandHasStableConnectionState(command);
9585
- if (this.closed) throw new FerricStoreError("FerricStore client is closed");
9702
+ if (this.closed) throw new RequestNotSentError("FerricStore client is closed");
9586
9703
  const initialExecutor = await this.executorPromise;
9587
- if (this.closed) throw new FerricStoreError("FerricStore client is closed");
9704
+ if (this.closed) throw new RequestNotSentError("FerricStore client is closed");
9588
9705
  if (initialExecutor.executeFusedPipeline == null) return void 0;
9589
9706
  try {
9590
9707
  return await initialExecutor.executeFusedPipeline(snapshot, snapshotOptions);
@@ -9617,7 +9734,7 @@ var ReconnectingExecutor = class {
9617
9734
  return;
9618
9735
  }
9619
9736
  this.closed = true;
9620
- this.retryAbortController.abort(new FerricStoreError("FerricStore client is closed"));
9737
+ this.retryAbortController.abort(new RequestNotSentError("FerricStore client is closed"));
9621
9738
  const executorPromise = this.executorPromise;
9622
9739
  const reconnectPromise = this.reconnectPromise;
9623
9740
  this.closePromise = (async () => {
@@ -9639,7 +9756,7 @@ var ReconnectingExecutor = class {
9639
9756
  let executor = await this.executorPromise;
9640
9757
  let retries = 0;
9641
9758
  for (; ; ) {
9642
- if (this.closed) throw new FerricStoreError("FerricStore client is closed");
9759
+ if (this.closed) throw new RequestNotSentError("FerricStore client is closed");
9643
9760
  try {
9644
9761
  return await operation(executor);
9645
9762
  } catch (error) {
@@ -9668,17 +9785,17 @@ var ReconnectingExecutor = class {
9668
9785
  try {
9669
9786
  await sleep(Math.min(this.maxDelayMs, base + jitter), this.retryAbortController.signal);
9670
9787
  } catch (error) {
9671
- if (this.closed) throw new FerricStoreError("FerricStore client is closed", { cause: error });
9788
+ if (this.closed) throw new RequestNotSentError("FerricStore client is closed", { cause: error });
9672
9789
  throw error;
9673
9790
  }
9674
9791
  }
9675
9792
  async reconnect(staleExecutor) {
9676
- if (this.closed) throw new FerricStoreError("FerricStore client is closed");
9793
+ if (this.closed) throw new RequestNotSentError("FerricStore client is closed");
9677
9794
  const competingReconnect = this.reconnectPromise;
9678
9795
  if (competingReconnect != null) return await competingReconnect;
9679
9796
  const observedExecutorPromise = this.executorPromise;
9680
9797
  const currentExecutor = await observedExecutorPromise.catch(() => void 0);
9681
- if (this.closed) throw new FerricStoreError("FerricStore client is closed");
9798
+ if (this.closed) throw new RequestNotSentError("FerricStore client is closed");
9682
9799
  if (this.reconnectPromise != null) return await this.reconnectPromise;
9683
9800
  if (this.executorPromise !== observedExecutorPromise) return await this.executorPromise;
9684
9801
  if (currentExecutor != null && currentExecutor !== staleExecutor) return currentExecutor;
@@ -9687,7 +9804,7 @@ var ReconnectingExecutor = class {
9687
9804
  const nextExecutor = await this.createExecutor(this.retryAbortController.signal);
9688
9805
  if (this.closed) {
9689
9806
  await Promise.resolve(nextExecutor.close?.()).catch(() => void 0);
9690
- throw new FerricStoreError("FerricStore client is closed");
9807
+ throw new RequestNotSentError("FerricStore client is closed");
9691
9808
  }
9692
9809
  this.executorPromise = Promise.resolve(nextExecutor);
9693
9810
  return nextExecutor;
@@ -11097,7 +11214,7 @@ var TopologyNativeAdapterPool = class _TopologyNativeAdapterPool {
11097
11214
  );
11098
11215
  }
11099
11216
  assertOpen() {
11100
- if (this.closed) throw new FerricStoreError("FerricStore topology adapter pool is closed");
11217
+ if (this.closed) throw new RequestNotSentError("FerricStore topology adapter pool is closed");
11101
11218
  }
11102
11219
  };
11103
11220
 
@@ -12223,7 +12340,7 @@ var AutoBatchExecutor = class {
12223
12340
  }
12224
12341
  async executeCommandArgs(args) {
12225
12342
  if (this.closed) {
12226
- throw new FerricStoreError("FerricStore client is closed");
12343
+ throw new RequestNotSentError("FerricStore client is closed");
12227
12344
  }
12228
12345
  const command = snapshotCommandArguments(args);
12229
12346
  if (this.executor.executePipeline == null) {
@@ -12243,7 +12360,7 @@ var AutoBatchExecutor = class {
12243
12360
  if (orderingBarrier != null) {
12244
12361
  await orderingBarrier;
12245
12362
  if (this.closed) {
12246
- throw new FerricStoreError("FerricStore client is closed");
12363
+ throw new RequestNotSentError("FerricStore client is closed");
12247
12364
  }
12248
12365
  }
12249
12366
  return await new Promise((resolve, reject) => {
@@ -12257,7 +12374,7 @@ var AutoBatchExecutor = class {
12257
12374
  }
12258
12375
  async executePipeline(commands, options) {
12259
12376
  if (this.closed) {
12260
- throw new FerricStoreError("FerricStore client is closed");
12377
+ throw new RequestNotSentError("FerricStore client is closed");
12261
12378
  }
12262
12379
  const snapshot = snapshotPipelineCommands(commands);
12263
12380
  const snapshotOptions = snapshotPipelineOptions(options);
@@ -12274,7 +12391,7 @@ var AutoBatchExecutor = class {
12274
12391
  }
12275
12392
  async executeFusedPipeline(commands, options) {
12276
12393
  if (this.closed) {
12277
- throw new FerricStoreError("FerricStore client is closed");
12394
+ throw new RequestNotSentError("FerricStore client is closed");
12278
12395
  }
12279
12396
  if (this.executor.executeFusedPipeline == null) return void 0;
12280
12397
  const snapshot = snapshotPipelineCommands(commands);
@@ -12285,7 +12402,7 @@ var AutoBatchExecutor = class {
12285
12402
  }
12286
12403
  async refreshTopology() {
12287
12404
  if (this.closed) {
12288
- throw new FerricStoreError("FerricStore client is closed");
12405
+ throw new RequestNotSentError("FerricStore client is closed");
12289
12406
  }
12290
12407
  const refreshTopology = this.executor.refreshTopology?.bind(this.executor);
12291
12408
  if (refreshTopology == null) {
@@ -12295,7 +12412,7 @@ var AutoBatchExecutor = class {
12295
12412
  }
12296
12413
  async route(key) {
12297
12414
  if (this.closed) {
12298
- throw new FerricStoreError("FerricStore client is closed");
12415
+ throw new RequestNotSentError("FerricStore client is closed");
12299
12416
  }
12300
12417
  const route = this.executor.route?.bind(this.executor);
12301
12418
  if (route == null) {
@@ -12315,7 +12432,7 @@ var AutoBatchExecutor = class {
12315
12432
  this.timer.cancel();
12316
12433
  this.timer = void 0;
12317
12434
  }
12318
- this.failPending(new Error("FerricStore client closed before auto-batch flush"));
12435
+ this.failPending(new RequestNotSentError("FerricStore client closed before auto-batch flush"));
12319
12436
  await this.orderingBarrier;
12320
12437
  await this.waitForInFlightBatches();
12321
12438
  await Promise.allSettled([...this.inFlightHelpers]);
@@ -12325,7 +12442,7 @@ var AutoBatchExecutor = class {
12325
12442
  }
12326
12443
  async executeAfterPendingBatches(operation) {
12327
12444
  if (this.closed) {
12328
- throw new FerricStoreError("FerricStore client is closed");
12445
+ throw new RequestNotSentError("FerricStore client is closed");
12329
12446
  }
12330
12447
  const previousBarrier = this.orderingBarrier;
12331
12448
  let releaseBarrier;
@@ -12346,7 +12463,7 @@ var AutoBatchExecutor = class {
12346
12463
  }
12347
12464
  executeHelper(operation) {
12348
12465
  if (this.closed) {
12349
- return Promise.reject(new FerricStoreError("FerricStore client is closed"));
12466
+ return Promise.reject(new RequestNotSentError("FerricStore client is closed"));
12350
12467
  }
12351
12468
  const task = Promise.resolve().then(operation);
12352
12469
  this.inFlightHelpers.add(task);
@@ -12368,7 +12485,7 @@ var AutoBatchExecutor = class {
12368
12485
  await previousBarrier;
12369
12486
  await this.flushNow();
12370
12487
  if (this.closed) {
12371
- throw new FerricStoreError("FerricStore client is closed");
12488
+ throw new RequestNotSentError("FerricStore client is closed");
12372
12489
  }
12373
12490
  dispatched = operation();
12374
12491
  } finally {
@@ -14707,7 +14824,7 @@ var FerricStoreClientBase = class {
14707
14824
  return validatePipelineResponse(results, snapshot.length);
14708
14825
  }
14709
14826
  async close() {
14710
- this.closeAbortController.abort(new FerricStoreError("FerricStore client is closed"));
14827
+ this.closeAbortController.abort(new RequestNotSentError("FerricStore client is closed"));
14711
14828
  this.closePromise ??= (async () => await this.executor.close?.())();
14712
14829
  await this.closePromise;
14713
14830
  }
@@ -17783,7 +17900,7 @@ async function executeProducerWriteWithBackpressure(execute, policy, signal) {
17783
17900
  function throwIfClosed(signal) {
17784
17901
  if (!signal.aborted) return;
17785
17902
  if (signal.reason instanceof Error) throw signal.reason;
17786
- throw new FerricStoreError("FerricStore client is closed", { raw: signal.reason });
17903
+ throw new RequestNotSentError("FerricStore client is closed", { raw: signal.reason });
17787
17904
  }
17788
17905
 
17789
17906
  // src/flow-policy.ts
@@ -19100,6 +19217,8 @@ async function runDurableStep(client, job, options, hooks = {}) {
19100
19217
  const run2 = options.run;
19101
19218
  const continuation = snapshotAdvanceOptions(options);
19102
19219
  assertNonEmptyString(name, "name");
19220
+ if (name.trim().length === 0) throw new TypeError("name must not be blank");
19221
+ if (!name.isWellFormed()) throw new TypeError("name must contain valid Unicode");
19103
19222
  assertNonEmptyString(continuation.toState, "toState");
19104
19223
  if (typeof run2 !== "function") throw new TypeError("run must be a function");
19105
19224
  const resultName = durableStepValueName(name);
@@ -19127,7 +19246,7 @@ async function runDurableStep(client, job, options, hooks = {}) {
19127
19246
  throw new FerricStoreError("committed durable step result is missing");
19128
19247
  }
19129
19248
  const replayedJob = claimedItemFromRecord(validated);
19130
- hooks.replayed?.(replayedJob);
19249
+ notifyWithoutReplacingOutcome(hooks.replayed, replayedJob);
19131
19250
  return { job: replayedJob, result: entry.value };
19132
19251
  }
19133
19252
  const result = normalizeStepResult(client, await run2());
@@ -19140,12 +19259,18 @@ async function runDurableStep(client, job, options, hooks = {}) {
19140
19259
  values: { ...continuation.values, [resultName]: result }
19141
19260
  });
19142
19261
  } catch (error) {
19143
- hooks.commitFailed?.(error);
19262
+ notifyWithoutReplacingOutcome(hooks.commitFailed, error);
19144
19263
  throw error;
19145
19264
  }
19146
- hooks.committed?.(refreshed);
19265
+ notifyWithoutReplacingOutcome(hooks.committed, refreshed);
19147
19266
  return { job: refreshed, result };
19148
19267
  }
19268
+ function notifyWithoutReplacingOutcome(callback, value) {
19269
+ try {
19270
+ callback?.(value);
19271
+ } catch {
19272
+ }
19273
+ }
19149
19274
  function normalizeStepResult(client, value) {
19150
19275
  return client.codec.decode(client.codec.encode(value));
19151
19276
  }
@@ -19266,6 +19391,35 @@ function claimedItemFromRecord(record) {
19266
19391
  };
19267
19392
  }
19268
19393
 
19394
+ // src/client-step-continue.ts
19395
+ function stepContinueArguments(id, options, codec) {
19396
+ try {
19397
+ const args = [
19398
+ "FLOW.STEP_CONTINUE",
19399
+ id,
19400
+ options.leaseToken,
19401
+ options.fromState,
19402
+ options.toState,
19403
+ "FENCING",
19404
+ options.fencingToken,
19405
+ "LEASE_MS",
19406
+ options.leaseMs ?? 3e4,
19407
+ "NOW",
19408
+ options.nowMs ?? nowMs()
19409
+ ];
19410
+ append(args, "PARTITION", options.partitionKey);
19411
+ append(args, "WORKER", options.worker);
19412
+ appendEncoded(args, "PAYLOAD", codec, options.payload);
19413
+ if (options.returnJob === true) args.push("RETURN", "JOBS_COMPACT");
19414
+ appendStateMeta(args, options.stateMeta);
19415
+ appendNamedValues(args, codec, options);
19416
+ appendAttributeMutations(args, options);
19417
+ return args;
19418
+ } catch (error) {
19419
+ throw requestNotSentError(error);
19420
+ }
19421
+ }
19422
+
19269
19423
  // src/client.ts
19270
19424
  var FerricStoreClient = class _FerricStoreClient extends FerricStoreProducerClient {
19271
19425
  static async fromUrl(url, options = {}) {
@@ -19388,26 +19542,7 @@ var FerricStoreClient = class _FerricStoreClient extends FerricStoreProducerClie
19388
19542
  const partitionKey = options.partitionKey;
19389
19543
  const returnJob = options.returnJob === true;
19390
19544
  const type = options.type;
19391
- const args = [
19392
- "FLOW.STEP_CONTINUE",
19393
- id,
19394
- options.leaseToken,
19395
- options.fromState,
19396
- options.toState,
19397
- "FENCING",
19398
- options.fencingToken,
19399
- "LEASE_MS",
19400
- options.leaseMs ?? 3e4,
19401
- "NOW",
19402
- options.nowMs ?? nowMs()
19403
- ];
19404
- append(args, "PARTITION", partitionKey);
19405
- append(args, "WORKER", options.worker);
19406
- appendEncoded(args, "PAYLOAD", this.codec, options.payload);
19407
- if (returnJob) args.push("RETURN", "JOBS_COMPACT");
19408
- appendStateMeta(args, options.stateMeta);
19409
- appendNamedValues(args, this.codec, options);
19410
- appendAttributeMutations(args, options);
19545
+ const args = stepContinueArguments(id, options, this.codec);
19411
19546
  const response = await this.commandArgs(args);
19412
19547
  return returnJob ? claimedItemFromResp(response, this.codec, { type }) : await this.recordOrGet(response, id, partitionKey);
19413
19548
  }
@@ -21046,15 +21181,6 @@ function createWorkflowStateRegistration(name, handler, options, defaultValueMax
21046
21181
  // src/workflow-context.ts
21047
21182
  var MISSING_VALUE = /* @__PURE__ */ Symbol("ferricstore.missingValue");
21048
21183
  var WorkflowContext = class {
21049
- constructor(workflow, job, stateName, leaseJob = job, mutationCoordinator) {
21050
- this.mutationCoordinator = mutationCoordinator;
21051
- this.workflow = workflow;
21052
- this.job = job;
21053
- this.leaseJob = leaseJob;
21054
- this.currentStateName = stateName;
21055
- this.flow = new WorkflowFlowCommands(this);
21056
- }
21057
- mutationCoordinator;
21058
21184
  workflow;
21059
21185
  job;
21060
21186
  flow;
@@ -21063,7 +21189,20 @@ var WorkflowContext = class {
21063
21189
  leaseJob;
21064
21190
  mutationFailure;
21065
21191
  mutationPhase = "idle";
21192
+ mutationCoordinator;
21066
21193
  valueCache = /* @__PURE__ */ new Map();
21194
+ constructor(workflow, job, stateName) {
21195
+ this.workflow = workflow;
21196
+ this.job = job;
21197
+ this.leaseJob = job;
21198
+ this.currentStateName = stateName;
21199
+ this.flow = new WorkflowFlowCommands(this);
21200
+ }
21201
+ /** @internal */
21202
+ configureWorkerMutation(leaseJob, coordinator) {
21203
+ this.leaseJob = leaseJob;
21204
+ this.mutationCoordinator = coordinator;
21205
+ }
21067
21206
  get client() {
21068
21207
  return this.workflow.client;
21069
21208
  }
@@ -21487,10 +21626,18 @@ async function applyFail(client, ctx, outcome, returnRecord) {
21487
21626
  });
21488
21627
  }
21489
21628
 
21629
+ // src/workflow-worker-context.ts
21630
+ var WorkerWorkflowContext = class extends WorkflowContext {
21631
+ constructor(workflow, job, stateName, leaseJob, mutationCoordinator) {
21632
+ super(workflow, job, stateName);
21633
+ this.configureWorkerMutation(leaseJob, mutationCoordinator);
21634
+ }
21635
+ };
21636
+
21490
21637
  // src/workflow-job-execution.ts
21491
21638
  async function executeWorkflowJob(workflow, workerOptions, job, registration, guard) {
21492
21639
  guard.assertActive();
21493
- const ctx = new WorkflowContext(workflow, job, registration.name, guard.job, {
21640
+ const ctx = new WorkerWorkflowContext(workflow, job, registration.name, guard.job, {
21494
21641
  pause: async () => await guard.pauseForLeaseMutation(),
21495
21642
  resume: (refreshed) => guard.resumeWith(refreshed)
21496
21643
  });
@@ -21835,7 +21982,7 @@ function throwError(value) {
21835
21982
  }
21836
21983
 
21837
21984
  // src/version.ts
21838
- var FERRICSTORE_SDK_VERSION = "0.12.0";
21985
+ var FERRICSTORE_SDK_VERSION = "0.13.2";
21839
21986
  var FERRICSTORE_MINIMUM_SERVER_VERSION = "0.11.4";
21840
21987
  var FERRICSTORE_NATIVE_PROTOCOL_VERSION = 1;
21841
21988
  // Annotate the CommonJS export names for ESM import in node:
@@ -21879,6 +22026,7 @@ var FERRICSTORE_NATIVE_PROTOCOL_VERSION = 1;
21879
22026
  QueueWorker,
21880
22027
  RawCodec,
21881
22028
  ReconnectingExecutor,
22029
+ RequestNotSentError,
21882
22030
  RequestTimeoutError,
21883
22031
  RerouteError,
21884
22032
  RoutingTopology,