@ferricstore/ferricstore 0.13.0 → 0.13.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 (74) hide show
  1. package/README.md +43 -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 +30 -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 +12 -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 +12 -1
  74. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -30,9 +30,16 @@ var FerricStoreError = class extends Error {
30
30
  };
31
31
  var HTTPTransportError = class extends FerricStoreError {
32
32
  code = "http_transport";
33
+ requestDisposition;
33
34
  statusCode;
34
35
  constructor(message, options = {}) {
35
- super(message, options);
36
+ const disposition = options.requestDisposition;
37
+ super(message, {
38
+ ...options,
39
+ retryable: options.retryable ?? (disposition === "unsent" ? false : void 0),
40
+ safeToRetry: options.safeToRetry ?? (disposition === "unsent" ? true : disposition === "possibly_sent" ? false : void 0)
41
+ });
42
+ this.requestDisposition = disposition;
36
43
  this.statusCode = options.statusCode;
37
44
  }
38
45
  };
@@ -57,6 +64,20 @@ var RequestTimeoutError = class extends FerricStoreError {
57
64
  this.timeoutMs = timeoutMs;
58
65
  }
59
66
  };
67
+ var RequestNotSentError = class extends FerricStoreError {
68
+ code = "request_not_sent";
69
+ requestDisposition = "unsent";
70
+ constructor(message, options = {}) {
71
+ super(message, { ...options, retryable: false, safeToRetry: true });
72
+ }
73
+ };
74
+ function requestNotSentError(error, message) {
75
+ if (error instanceof RequestNotSentError) return error;
76
+ return new RequestNotSentError(
77
+ message ?? (error instanceof Error ? error.message : String(error)),
78
+ { cause: error, raw: error }
79
+ );
80
+ }
60
81
  var FlowNotFoundError = class extends FerricStoreError {
61
82
  code = "flow_not_found";
62
83
  };
@@ -6315,7 +6336,10 @@ function decodeCompactPipeline(data, budget, claimModes) {
6315
6336
  );
6316
6337
  offset = read.offset;
6317
6338
  } else {
6318
- throw new FerricStoreError("unknown compact pipeline status");
6339
+ throw new FerricStoreError(`unknown compact pipeline status ${status}`, {
6340
+ retryable: false,
6341
+ safeToRetry: false
6342
+ });
6319
6343
  }
6320
6344
  }
6321
6345
  if (offset !== data.byteLength) throw new FerricStoreError("trailing compact pipeline bytes");
@@ -6485,6 +6509,7 @@ var COUNTED_COMPACT_TAGS = /* @__PURE__ */ new Set([
6485
6509
  ]);
6486
6510
 
6487
6511
  // src/protocol-response.ts
6512
+ var KNOWN_NATIVE_RESPONSE_STATUSES = /* @__PURE__ */ new Set([0, 1, 2, 3, 4, 5, 6]);
6488
6513
  function decodeResponse(frame, expectedOpcode, hints = {}) {
6489
6514
  if (frame.opcode !== expectedOpcode) {
6490
6515
  throw new FerricStoreError(
@@ -6509,6 +6534,13 @@ function decodeResponse(frame, expectedOpcode, hints = {}) {
6509
6534
  customPayload
6510
6535
  );
6511
6536
  if (status === STATUS_OK) return value;
6537
+ if (!KNOWN_NATIVE_RESPONSE_STATUSES.has(status)) {
6538
+ throw new FerricStoreError(`unknown native response status ${status}`, {
6539
+ raw: value,
6540
+ retryable: false,
6541
+ safeToRetry: false
6542
+ });
6543
+ }
6512
6544
  const message = protocolErrorMessage(status, value);
6513
6545
  throw classifyServerError(message, value, void 0, status);
6514
6546
  }
@@ -6548,7 +6580,7 @@ function unwrapPipelineResponse(value, options = {}, expectedItems) {
6548
6580
  continue;
6549
6581
  }
6550
6582
  const error = classifyServerError(
6551
- protocolErrorMessage(status === "busy" ? 4 : 1, payload),
6583
+ protocolErrorMessage(pipelineStatusCode(status), payload),
6552
6584
  payload,
6553
6585
  void 0,
6554
6586
  status
@@ -6557,15 +6589,46 @@ function unwrapPipelineResponse(value, options = {}, expectedItems) {
6557
6589
  out[index] = error;
6558
6590
  continue;
6559
6591
  }
6592
+ if (pipelineStatusToken(item[0]) != null) {
6593
+ const unknown = pipelineStatusToken(item[0]) ?? "unknown";
6594
+ const error = new FerricStoreError(`unknown native pipeline status ${JSON.stringify(unknown)}`, {
6595
+ raw: item,
6596
+ retryable: false,
6597
+ safeToRetry: false
6598
+ });
6599
+ if (options.throwOnItemError !== false) throw error;
6600
+ out[index] = error;
6601
+ continue;
6602
+ }
6560
6603
  }
6561
6604
  out[index] = item;
6562
6605
  }
6563
6606
  return out;
6564
6607
  }
6565
6608
  function pipelineStatus(value) {
6609
+ const status = pipelineStatusToken(value);
6610
+ if (status == null) return null;
6611
+ return status === "ok" || status === "error" || status === "auth" || status === "noperm" || status === "busy" || status === "reroute" || status === "bad_request" ? status : null;
6612
+ }
6613
+ function pipelineStatusToken(value) {
6566
6614
  if (typeof value !== "string" && !Buffer26.isBuffer(value) && !(value instanceof Uint8Array)) return null;
6567
- const status = asText(value).toLowerCase();
6568
- return status === "ok" || status === "busy" || status === "error" ? status : null;
6615
+ return asText(value).toLowerCase();
6616
+ }
6617
+ function pipelineStatusCode(status) {
6618
+ switch (status) {
6619
+ case "error":
6620
+ return 1;
6621
+ case "auth":
6622
+ return 2;
6623
+ case "noperm":
6624
+ return 3;
6625
+ case "busy":
6626
+ return 4;
6627
+ case "reroute":
6628
+ return 5;
6629
+ case "bad_request":
6630
+ return 6;
6631
+ }
6569
6632
  }
6570
6633
  function decodeResponseValue(opcode, body, hints, allowCompact, customPayload) {
6571
6634
  if (customPayload && allowCompact) {
@@ -8225,6 +8288,22 @@ function plainRecord(value) {
8225
8288
  return value;
8226
8289
  }
8227
8290
 
8291
+ // src/native-command-preparation.ts
8292
+ function prepareNativeCommand(args, maxFrameBytes) {
8293
+ try {
8294
+ return buildProtocolCommand(args, maxFrameBytes);
8295
+ } catch (error) {
8296
+ throw requestNotSentError(error);
8297
+ }
8298
+ }
8299
+ function encodeNativeRequest(command, requestId, maxFrameBytes) {
8300
+ try {
8301
+ return encodeRequest(command, requestId, maxFrameBytes);
8302
+ } catch (error) {
8303
+ throw requestNotSentError(error);
8304
+ }
8305
+ }
8306
+
8228
8307
  // src/native-adapter.ts
8229
8308
  var NativeAdapter = class _NativeAdapter {
8230
8309
  socket;
@@ -8332,15 +8411,13 @@ var NativeAdapter = class _NativeAdapter {
8332
8411
  return await this.executeCommandArgs(args);
8333
8412
  }
8334
8413
  async executeCommandArgs(args) {
8335
- const command = buildProtocolCommand(args, this.capabilities.requestFrameBytes);
8414
+ const command = prepareNativeCommand(args, this.capabilities.requestFrameBytes);
8336
8415
  return await this.request(command);
8337
8416
  }
8338
8417
  /** @internal Execute raw command arguments on a topology-selected lane. */
8339
8418
  async executeCommandOnLane(args, laneId) {
8340
- return await this.executeProtocolCommand(
8341
- buildProtocolCommand(args, this.capabilities.requestFrameBytes),
8342
- laneId
8343
- );
8419
+ const command = prepareNativeCommand(args, this.capabilities.requestFrameBytes);
8420
+ return await this.executeProtocolCommand(command, laneId);
8344
8421
  }
8345
8422
  async executeProtocolCommand(command, laneId) {
8346
8423
  return await this.request(laneId == null ? command : { ...command, laneId });
@@ -8434,14 +8511,14 @@ var NativeAdapter = class _NativeAdapter {
8434
8511
  let frame;
8435
8512
  try {
8436
8513
  requestId = this.requestScheduler.nextRequestId();
8437
- frame = encodeRequest(
8514
+ frame = encodeNativeRequest(
8438
8515
  withFlowQueryDeadline(command, timeoutMs),
8439
8516
  requestId,
8440
8517
  this.capabilities.requestFrameBytes
8441
8518
  );
8442
8519
  } catch (error) {
8443
8520
  if (hasFlowControlCredit) this.flowControl.release(laneId);
8444
- return Promise.reject(error instanceof Error ? error : new FerricStoreError(String(error), { raw: error }));
8521
+ return Promise.reject(error instanceof Error ? error : requestNotSentError(error));
8445
8522
  }
8446
8523
  return new Promise((resolve, reject) => {
8447
8524
  const timer = timeoutMs == null ? void 0 : setLongTimeout(() => {
@@ -9016,7 +9093,9 @@ var HTTPTransport = class {
9016
9093
  #requests = /* @__PURE__ */ new Set();
9017
9094
  #closed = false;
9018
9095
  async post(body, timeoutMs) {
9019
- if (this.#closed) throw new HTTPTransportError("HTTP transport is closed");
9096
+ if (this.#closed) {
9097
+ throw new HTTPTransportError("HTTP transport is closed", { requestDisposition: "unsent" });
9098
+ }
9020
9099
  const controller = new AbortController();
9021
9100
  this.#requests.add(controller);
9022
9101
  const timer = timeoutMs == null ? void 0 : setLongTimeout(() => controller.abort(requestTimeoutReason), timeoutMs);
@@ -9033,9 +9112,22 @@ var HTTPTransport = class {
9033
9112
  raw: { retryable: true, safe_to_retry: false }
9034
9113
  });
9035
9114
  }
9036
- if (error instanceof HTTPTransportError || error instanceof RequestTimeoutError) throw error;
9115
+ if (error instanceof RequestTimeoutError) throw error;
9116
+ if (error instanceof HTTPTransportError) {
9117
+ if (error.requestDisposition != null) throw error;
9118
+ throw new HTTPTransportError(error.message, {
9119
+ cause: error.cause ?? error,
9120
+ raw: error.raw,
9121
+ requestDisposition: "possibly_sent",
9122
+ retryAfterMs: error.retryAfterMs,
9123
+ retryable: error.retryable,
9124
+ safeToRetry: false,
9125
+ statusCode: error.statusCode
9126
+ });
9127
+ }
9037
9128
  throw new HTTPTransportError("HTTP command request failed", {
9038
9129
  cause: error,
9130
+ requestDisposition: "possibly_sent",
9039
9131
  retryable: true,
9040
9132
  safeToRetry: false
9041
9133
  });
@@ -9047,7 +9139,9 @@ var HTTPTransport = class {
9047
9139
  async close() {
9048
9140
  if (this.#closed) return;
9049
9141
  this.#closed = true;
9050
- const error = new HTTPTransportError("HTTP transport is closed");
9142
+ const error = new HTTPTransportError("HTTP transport is closed", {
9143
+ requestDisposition: "possibly_sent"
9144
+ });
9051
9145
  for (const controller of this.#requests) controller.abort(error);
9052
9146
  this.#httpAgent.destroy();
9053
9147
  this.#httpsAgent.destroy();
@@ -9290,16 +9384,22 @@ var HTTPAdapter = class _HTTPAdapter {
9290
9384
  await this.#transport.close();
9291
9385
  }
9292
9386
  async executeBatch(commands) {
9293
- if (commands.length > this.#config.maxBatchItems) {
9294
- throw new HTTPTransportError("HTTP command batch exceeds maxBatchItems");
9295
- }
9296
- for (const command of commands) assertHTTPCommandSupported(command[0]);
9297
- const prepared = commands.map((command) => prepareHTTPCommand(command, this.#config.maxRequestBytes));
9298
- const body = encodeHTTPCommands(prepared.map((command) => command.encoded), this.#config.maxRequestBytes);
9299
- if (body.byteLength > this.#config.maxRequestBytes) {
9300
- throw new HTTPTransportError("HTTP command request exceeds maxRequestBytes");
9387
+ let body;
9388
+ let serverBlockMs;
9389
+ try {
9390
+ if (commands.length > this.#config.maxBatchItems) {
9391
+ throw new HTTPTransportError("HTTP command batch exceeds maxBatchItems");
9392
+ }
9393
+ for (const command of commands) assertHTTPCommandSupported(command[0]);
9394
+ const prepared = commands.map((command) => prepareHTTPCommand(command, this.#config.maxRequestBytes));
9395
+ body = encodeHTTPCommands(prepared.map((command) => command.encoded), this.#config.maxRequestBytes);
9396
+ if (body.byteLength > this.#config.maxRequestBytes) {
9397
+ throw new HTTPTransportError("HTTP command request exceeds maxRequestBytes");
9398
+ }
9399
+ serverBlockMs = combinedServerBlockMs(prepared.map((command) => command.serverBlockMs));
9400
+ } catch (error) {
9401
+ throw unsentHTTPError(error);
9301
9402
  }
9302
- const serverBlockMs = combinedServerBlockMs(prepared.map((command) => command.serverBlockMs));
9303
9403
  const response = await this.#transport.post(
9304
9404
  body,
9305
9405
  serverResponseTimeoutMs(this.#config.timeoutMs, serverBlockMs)
@@ -9356,16 +9456,32 @@ function commandError(value) {
9356
9456
  }
9357
9457
  function topLevelError(status, envelope, retryAfter) {
9358
9458
  const details = isRecord2(envelope.error) ? envelope.error : {};
9459
+ const code = typeof details.code === "string" ? details.code.toLowerCase() : void 0;
9359
9460
  const message = typeof details.message === "string" ? details.message : `HTTP command request failed with status ${status}`;
9360
9461
  const retryAfterMs2 = retryAfterMilliseconds(retryAfter);
9462
+ const ambiguousTimeout = status === 408 || code === "request_timeout";
9463
+ const safeToRetry = !ambiguousTimeout && (details.safe_to_retry === true || definitelyRejectedHTTPStatus(status));
9361
9464
  return new HTTPTransportError(message, {
9362
9465
  raw: details,
9363
9466
  retryable: status === 408 || status === 425 || status === 429 || status >= 500,
9364
9467
  retryAfterMs: retryAfterMs2,
9365
- safeToRetry: false,
9468
+ requestDisposition: "possibly_sent",
9469
+ safeToRetry,
9366
9470
  statusCode: status
9367
9471
  });
9368
9472
  }
9473
+ function definitelyRejectedHTTPStatus(status) {
9474
+ 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;
9475
+ }
9476
+ function unsentHTTPError(error) {
9477
+ if (error instanceof HTTPTransportError && error.requestDisposition === "unsent") return error;
9478
+ const message = error instanceof Error ? error.message : String(error);
9479
+ return new HTTPTransportError(message, {
9480
+ cause: error,
9481
+ raw: error instanceof FerricStoreError ? error.raw ?? error : error,
9482
+ requestDisposition: "unsent"
9483
+ });
9484
+ }
9369
9485
  function retryAfterMilliseconds(value) {
9370
9486
  if (value == null) return void 0;
9371
9487
  if (/^\d+$/u.test(value)) {
@@ -9458,9 +9574,9 @@ var ReconnectingExecutor = class {
9458
9574
  const snapshot = snapshotPipelineCommands(commands);
9459
9575
  const snapshotOptions = snapshotPipelineOptions(options) ?? {};
9460
9576
  for (const command of snapshot) assertCommandHasStableConnectionState(command);
9461
- if (this.closed) throw new FerricStoreError("FerricStore client is closed");
9577
+ if (this.closed) throw new RequestNotSentError("FerricStore client is closed");
9462
9578
  const initialExecutor = await this.executorPromise;
9463
- if (this.closed) throw new FerricStoreError("FerricStore client is closed");
9579
+ if (this.closed) throw new RequestNotSentError("FerricStore client is closed");
9464
9580
  if (initialExecutor.executePipeline == null) {
9465
9581
  return await executeCommandArraysIndividually(
9466
9582
  async (command) => await this.executeCommandArgs(command),
@@ -9481,9 +9597,9 @@ var ReconnectingExecutor = class {
9481
9597
  const snapshot = snapshotPipelineCommands(commands);
9482
9598
  const snapshotOptions = snapshotPipelineOptions(options) ?? {};
9483
9599
  for (const command of snapshot) assertCommandHasStableConnectionState(command);
9484
- if (this.closed) throw new FerricStoreError("FerricStore client is closed");
9600
+ if (this.closed) throw new RequestNotSentError("FerricStore client is closed");
9485
9601
  const initialExecutor = await this.executorPromise;
9486
- if (this.closed) throw new FerricStoreError("FerricStore client is closed");
9602
+ if (this.closed) throw new RequestNotSentError("FerricStore client is closed");
9487
9603
  if (initialExecutor.executeFusedPipeline == null) return void 0;
9488
9604
  try {
9489
9605
  return await initialExecutor.executeFusedPipeline(snapshot, snapshotOptions);
@@ -9516,7 +9632,7 @@ var ReconnectingExecutor = class {
9516
9632
  return;
9517
9633
  }
9518
9634
  this.closed = true;
9519
- this.retryAbortController.abort(new FerricStoreError("FerricStore client is closed"));
9635
+ this.retryAbortController.abort(new RequestNotSentError("FerricStore client is closed"));
9520
9636
  const executorPromise = this.executorPromise;
9521
9637
  const reconnectPromise = this.reconnectPromise;
9522
9638
  this.closePromise = (async () => {
@@ -9538,7 +9654,7 @@ var ReconnectingExecutor = class {
9538
9654
  let executor = await this.executorPromise;
9539
9655
  let retries = 0;
9540
9656
  for (; ; ) {
9541
- if (this.closed) throw new FerricStoreError("FerricStore client is closed");
9657
+ if (this.closed) throw new RequestNotSentError("FerricStore client is closed");
9542
9658
  try {
9543
9659
  return await operation(executor);
9544
9660
  } catch (error) {
@@ -9567,17 +9683,17 @@ var ReconnectingExecutor = class {
9567
9683
  try {
9568
9684
  await sleep(Math.min(this.maxDelayMs, base + jitter), this.retryAbortController.signal);
9569
9685
  } catch (error) {
9570
- if (this.closed) throw new FerricStoreError("FerricStore client is closed", { cause: error });
9686
+ if (this.closed) throw new RequestNotSentError("FerricStore client is closed", { cause: error });
9571
9687
  throw error;
9572
9688
  }
9573
9689
  }
9574
9690
  async reconnect(staleExecutor) {
9575
- if (this.closed) throw new FerricStoreError("FerricStore client is closed");
9691
+ if (this.closed) throw new RequestNotSentError("FerricStore client is closed");
9576
9692
  const competingReconnect = this.reconnectPromise;
9577
9693
  if (competingReconnect != null) return await competingReconnect;
9578
9694
  const observedExecutorPromise = this.executorPromise;
9579
9695
  const currentExecutor = await observedExecutorPromise.catch(() => void 0);
9580
- if (this.closed) throw new FerricStoreError("FerricStore client is closed");
9696
+ if (this.closed) throw new RequestNotSentError("FerricStore client is closed");
9581
9697
  if (this.reconnectPromise != null) return await this.reconnectPromise;
9582
9698
  if (this.executorPromise !== observedExecutorPromise) return await this.executorPromise;
9583
9699
  if (currentExecutor != null && currentExecutor !== staleExecutor) return currentExecutor;
@@ -9586,7 +9702,7 @@ var ReconnectingExecutor = class {
9586
9702
  const nextExecutor = await this.createExecutor(this.retryAbortController.signal);
9587
9703
  if (this.closed) {
9588
9704
  await Promise.resolve(nextExecutor.close?.()).catch(() => void 0);
9589
- throw new FerricStoreError("FerricStore client is closed");
9705
+ throw new RequestNotSentError("FerricStore client is closed");
9590
9706
  }
9591
9707
  this.executorPromise = Promise.resolve(nextExecutor);
9592
9708
  return nextExecutor;
@@ -10996,7 +11112,7 @@ var TopologyNativeAdapterPool = class _TopologyNativeAdapterPool {
10996
11112
  );
10997
11113
  }
10998
11114
  assertOpen() {
10999
- if (this.closed) throw new FerricStoreError("FerricStore topology adapter pool is closed");
11115
+ if (this.closed) throw new RequestNotSentError("FerricStore topology adapter pool is closed");
11000
11116
  }
11001
11117
  };
11002
11118
 
@@ -12122,7 +12238,7 @@ var AutoBatchExecutor = class {
12122
12238
  }
12123
12239
  async executeCommandArgs(args) {
12124
12240
  if (this.closed) {
12125
- throw new FerricStoreError("FerricStore client is closed");
12241
+ throw new RequestNotSentError("FerricStore client is closed");
12126
12242
  }
12127
12243
  const command = snapshotCommandArguments(args);
12128
12244
  if (this.executor.executePipeline == null) {
@@ -12142,7 +12258,7 @@ var AutoBatchExecutor = class {
12142
12258
  if (orderingBarrier != null) {
12143
12259
  await orderingBarrier;
12144
12260
  if (this.closed) {
12145
- throw new FerricStoreError("FerricStore client is closed");
12261
+ throw new RequestNotSentError("FerricStore client is closed");
12146
12262
  }
12147
12263
  }
12148
12264
  return await new Promise((resolve, reject) => {
@@ -12156,7 +12272,7 @@ var AutoBatchExecutor = class {
12156
12272
  }
12157
12273
  async executePipeline(commands, options) {
12158
12274
  if (this.closed) {
12159
- throw new FerricStoreError("FerricStore client is closed");
12275
+ throw new RequestNotSentError("FerricStore client is closed");
12160
12276
  }
12161
12277
  const snapshot = snapshotPipelineCommands(commands);
12162
12278
  const snapshotOptions = snapshotPipelineOptions(options);
@@ -12173,7 +12289,7 @@ var AutoBatchExecutor = class {
12173
12289
  }
12174
12290
  async executeFusedPipeline(commands, options) {
12175
12291
  if (this.closed) {
12176
- throw new FerricStoreError("FerricStore client is closed");
12292
+ throw new RequestNotSentError("FerricStore client is closed");
12177
12293
  }
12178
12294
  if (this.executor.executeFusedPipeline == null) return void 0;
12179
12295
  const snapshot = snapshotPipelineCommands(commands);
@@ -12184,7 +12300,7 @@ var AutoBatchExecutor = class {
12184
12300
  }
12185
12301
  async refreshTopology() {
12186
12302
  if (this.closed) {
12187
- throw new FerricStoreError("FerricStore client is closed");
12303
+ throw new RequestNotSentError("FerricStore client is closed");
12188
12304
  }
12189
12305
  const refreshTopology = this.executor.refreshTopology?.bind(this.executor);
12190
12306
  if (refreshTopology == null) {
@@ -12194,7 +12310,7 @@ var AutoBatchExecutor = class {
12194
12310
  }
12195
12311
  async route(key) {
12196
12312
  if (this.closed) {
12197
- throw new FerricStoreError("FerricStore client is closed");
12313
+ throw new RequestNotSentError("FerricStore client is closed");
12198
12314
  }
12199
12315
  const route = this.executor.route?.bind(this.executor);
12200
12316
  if (route == null) {
@@ -12214,7 +12330,7 @@ var AutoBatchExecutor = class {
12214
12330
  this.timer.cancel();
12215
12331
  this.timer = void 0;
12216
12332
  }
12217
- this.failPending(new Error("FerricStore client closed before auto-batch flush"));
12333
+ this.failPending(new RequestNotSentError("FerricStore client closed before auto-batch flush"));
12218
12334
  await this.orderingBarrier;
12219
12335
  await this.waitForInFlightBatches();
12220
12336
  await Promise.allSettled([...this.inFlightHelpers]);
@@ -12224,7 +12340,7 @@ var AutoBatchExecutor = class {
12224
12340
  }
12225
12341
  async executeAfterPendingBatches(operation) {
12226
12342
  if (this.closed) {
12227
- throw new FerricStoreError("FerricStore client is closed");
12343
+ throw new RequestNotSentError("FerricStore client is closed");
12228
12344
  }
12229
12345
  const previousBarrier = this.orderingBarrier;
12230
12346
  let releaseBarrier;
@@ -12245,7 +12361,7 @@ var AutoBatchExecutor = class {
12245
12361
  }
12246
12362
  executeHelper(operation) {
12247
12363
  if (this.closed) {
12248
- return Promise.reject(new FerricStoreError("FerricStore client is closed"));
12364
+ return Promise.reject(new RequestNotSentError("FerricStore client is closed"));
12249
12365
  }
12250
12366
  const task = Promise.resolve().then(operation);
12251
12367
  this.inFlightHelpers.add(task);
@@ -12267,7 +12383,7 @@ var AutoBatchExecutor = class {
12267
12383
  await previousBarrier;
12268
12384
  await this.flushNow();
12269
12385
  if (this.closed) {
12270
- throw new FerricStoreError("FerricStore client is closed");
12386
+ throw new RequestNotSentError("FerricStore client is closed");
12271
12387
  }
12272
12388
  dispatched = operation();
12273
12389
  } finally {
@@ -14606,7 +14722,7 @@ var FerricStoreClientBase = class {
14606
14722
  return validatePipelineResponse(results, snapshot.length);
14607
14723
  }
14608
14724
  async close() {
14609
- this.closeAbortController.abort(new FerricStoreError("FerricStore client is closed"));
14725
+ this.closeAbortController.abort(new RequestNotSentError("FerricStore client is closed"));
14610
14726
  this.closePromise ??= (async () => await this.executor.close?.())();
14611
14727
  await this.closePromise;
14612
14728
  }
@@ -17682,7 +17798,7 @@ async function executeProducerWriteWithBackpressure(execute, policy, signal) {
17682
17798
  function throwIfClosed(signal) {
17683
17799
  if (!signal.aborted) return;
17684
17800
  if (signal.reason instanceof Error) throw signal.reason;
17685
- throw new FerricStoreError("FerricStore client is closed", { raw: signal.reason });
17801
+ throw new RequestNotSentError("FerricStore client is closed", { raw: signal.reason });
17686
17802
  }
17687
17803
 
17688
17804
  // src/flow-policy.ts
@@ -18999,6 +19115,8 @@ async function runDurableStep(client, job, options, hooks = {}) {
18999
19115
  const run2 = options.run;
19000
19116
  const continuation = snapshotAdvanceOptions(options);
19001
19117
  assertNonEmptyString(name, "name");
19118
+ if (name.trim().length === 0) throw new TypeError("name must not be blank");
19119
+ if (!name.isWellFormed()) throw new TypeError("name must contain valid Unicode");
19002
19120
  assertNonEmptyString(continuation.toState, "toState");
19003
19121
  if (typeof run2 !== "function") throw new TypeError("run must be a function");
19004
19122
  const resultName = durableStepValueName(name);
@@ -19026,7 +19144,7 @@ async function runDurableStep(client, job, options, hooks = {}) {
19026
19144
  throw new FerricStoreError("committed durable step result is missing");
19027
19145
  }
19028
19146
  const replayedJob = claimedItemFromRecord(validated);
19029
- hooks.replayed?.(replayedJob);
19147
+ notifyWithoutReplacingOutcome(hooks.replayed, replayedJob);
19030
19148
  return { job: replayedJob, result: entry.value };
19031
19149
  }
19032
19150
  const result = normalizeStepResult(client, await run2());
@@ -19039,12 +19157,18 @@ async function runDurableStep(client, job, options, hooks = {}) {
19039
19157
  values: { ...continuation.values, [resultName]: result }
19040
19158
  });
19041
19159
  } catch (error) {
19042
- hooks.commitFailed?.(error);
19160
+ notifyWithoutReplacingOutcome(hooks.commitFailed, error);
19043
19161
  throw error;
19044
19162
  }
19045
- hooks.committed?.(refreshed);
19163
+ notifyWithoutReplacingOutcome(hooks.committed, refreshed);
19046
19164
  return { job: refreshed, result };
19047
19165
  }
19166
+ function notifyWithoutReplacingOutcome(callback, value) {
19167
+ try {
19168
+ callback?.(value);
19169
+ } catch {
19170
+ }
19171
+ }
19048
19172
  function normalizeStepResult(client, value) {
19049
19173
  return client.codec.decode(client.codec.encode(value));
19050
19174
  }
@@ -19165,6 +19289,35 @@ function claimedItemFromRecord(record) {
19165
19289
  };
19166
19290
  }
19167
19291
 
19292
+ // src/client-step-continue.ts
19293
+ function stepContinueArguments(id, options, codec) {
19294
+ try {
19295
+ const args = [
19296
+ "FLOW.STEP_CONTINUE",
19297
+ id,
19298
+ options.leaseToken,
19299
+ options.fromState,
19300
+ options.toState,
19301
+ "FENCING",
19302
+ options.fencingToken,
19303
+ "LEASE_MS",
19304
+ options.leaseMs ?? 3e4,
19305
+ "NOW",
19306
+ options.nowMs ?? nowMs()
19307
+ ];
19308
+ append(args, "PARTITION", options.partitionKey);
19309
+ append(args, "WORKER", options.worker);
19310
+ appendEncoded(args, "PAYLOAD", codec, options.payload);
19311
+ if (options.returnJob === true) args.push("RETURN", "JOBS_COMPACT");
19312
+ appendStateMeta(args, options.stateMeta);
19313
+ appendNamedValues(args, codec, options);
19314
+ appendAttributeMutations(args, options);
19315
+ return args;
19316
+ } catch (error) {
19317
+ throw requestNotSentError(error);
19318
+ }
19319
+ }
19320
+
19168
19321
  // src/client.ts
19169
19322
  var FerricStoreClient = class _FerricStoreClient extends FerricStoreProducerClient {
19170
19323
  static async fromUrl(url, options = {}) {
@@ -19287,26 +19440,7 @@ var FerricStoreClient = class _FerricStoreClient extends FerricStoreProducerClie
19287
19440
  const partitionKey = options.partitionKey;
19288
19441
  const returnJob = options.returnJob === true;
19289
19442
  const type = options.type;
19290
- const args = [
19291
- "FLOW.STEP_CONTINUE",
19292
- id,
19293
- options.leaseToken,
19294
- options.fromState,
19295
- options.toState,
19296
- "FENCING",
19297
- options.fencingToken,
19298
- "LEASE_MS",
19299
- options.leaseMs ?? 3e4,
19300
- "NOW",
19301
- options.nowMs ?? nowMs()
19302
- ];
19303
- append(args, "PARTITION", partitionKey);
19304
- append(args, "WORKER", options.worker);
19305
- appendEncoded(args, "PAYLOAD", this.codec, options.payload);
19306
- if (returnJob) args.push("RETURN", "JOBS_COMPACT");
19307
- appendStateMeta(args, options.stateMeta);
19308
- appendNamedValues(args, this.codec, options);
19309
- appendAttributeMutations(args, options);
19443
+ const args = stepContinueArguments(id, options, this.codec);
19310
19444
  const response = await this.commandArgs(args);
19311
19445
  return returnJob ? claimedItemFromResp(response, this.codec, { type }) : await this.recordOrGet(response, id, partitionKey);
19312
19446
  }
@@ -20945,15 +21079,6 @@ function createWorkflowStateRegistration(name, handler, options, defaultValueMax
20945
21079
  // src/workflow-context.ts
20946
21080
  var MISSING_VALUE = /* @__PURE__ */ Symbol("ferricstore.missingValue");
20947
21081
  var WorkflowContext = class {
20948
- constructor(workflow, job, stateName, leaseJob = job, mutationCoordinator) {
20949
- this.mutationCoordinator = mutationCoordinator;
20950
- this.workflow = workflow;
20951
- this.job = job;
20952
- this.leaseJob = leaseJob;
20953
- this.currentStateName = stateName;
20954
- this.flow = new WorkflowFlowCommands(this);
20955
- }
20956
- mutationCoordinator;
20957
21082
  workflow;
20958
21083
  job;
20959
21084
  flow;
@@ -20962,7 +21087,20 @@ var WorkflowContext = class {
20962
21087
  leaseJob;
20963
21088
  mutationFailure;
20964
21089
  mutationPhase = "idle";
21090
+ mutationCoordinator;
20965
21091
  valueCache = /* @__PURE__ */ new Map();
21092
+ constructor(workflow, job, stateName) {
21093
+ this.workflow = workflow;
21094
+ this.job = job;
21095
+ this.leaseJob = job;
21096
+ this.currentStateName = stateName;
21097
+ this.flow = new WorkflowFlowCommands(this);
21098
+ }
21099
+ /** @internal */
21100
+ configureWorkerMutation(leaseJob, coordinator) {
21101
+ this.leaseJob = leaseJob;
21102
+ this.mutationCoordinator = coordinator;
21103
+ }
20966
21104
  get client() {
20967
21105
  return this.workflow.client;
20968
21106
  }
@@ -21386,10 +21524,18 @@ async function applyFail(client, ctx, outcome, returnRecord) {
21386
21524
  });
21387
21525
  }
21388
21526
 
21527
+ // src/workflow-worker-context.ts
21528
+ var WorkerWorkflowContext = class extends WorkflowContext {
21529
+ constructor(workflow, job, stateName, leaseJob, mutationCoordinator) {
21530
+ super(workflow, job, stateName);
21531
+ this.configureWorkerMutation(leaseJob, mutationCoordinator);
21532
+ }
21533
+ };
21534
+
21389
21535
  // src/workflow-job-execution.ts
21390
21536
  async function executeWorkflowJob(workflow, workerOptions, job, registration, guard) {
21391
21537
  guard.assertActive();
21392
- const ctx = new WorkflowContext(workflow, job, registration.name, guard.job, {
21538
+ const ctx = new WorkerWorkflowContext(workflow, job, registration.name, guard.job, {
21393
21539
  pause: async () => await guard.pauseForLeaseMutation(),
21394
21540
  resume: (refreshed) => guard.resumeWith(refreshed)
21395
21541
  });
@@ -21734,7 +21880,7 @@ function throwError(value) {
21734
21880
  }
21735
21881
 
21736
21882
  // src/version.ts
21737
- var FERRICSTORE_SDK_VERSION = "0.12.0";
21883
+ var FERRICSTORE_SDK_VERSION = "0.13.1";
21738
21884
  var FERRICSTORE_MINIMUM_SERVER_VERSION = "0.11.4";
21739
21885
  var FERRICSTORE_NATIVE_PROTOCOL_VERSION = 1;
21740
21886
  export {
@@ -21777,6 +21923,7 @@ export {
21777
21923
  QueueWorker,
21778
21924
  RawCodec,
21779
21925
  ReconnectingExecutor,
21926
+ RequestNotSentError,
21780
21927
  RequestTimeoutError,
21781
21928
  RerouteError,
21782
21929
  RoutingTopology,