@zapier/kitcore 0.18.0 → 0.19.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # @zapier/kitcore
2
2
 
3
+ ## 0.19.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 1ec9410: Added `RetryHttpRequestOptions.onRetry` and exported `RetryHttpRequestAttempt` for observing retries scheduled by `retryHttpRequestPlugin`. The callback runs before the retry wait and receives the request, operation ID, failed attempt number, selected delay, and response or error. Promise results are not awaited, and thrown errors or rejected promises do not cancel the retry. When a response is present, read only its status and headers.
8
+
9
+ ### Patch Changes
10
+
11
+ - 1ec9410: `retryHttpRequestPlugin` now waits at least 100 milliseconds before retrying when generated backoff is capped by a zero or near-zero `maxDelayMilliseconds`. Use `maxAttempts: 1` to disable retries.
12
+
3
13
  ## 0.18.0
4
14
 
5
15
  ### Minor Changes
package/dist/index.cjs CHANGED
@@ -775,6 +775,11 @@ function toIterable(source) {
775
775
  return { [Symbol.asyncIterator]: () => source[Symbol.asyncIterator]() };
776
776
  }
777
777
 
778
+ // src/utils/promise-utils.ts
779
+ function isPromiseLike(value) {
780
+ return value !== null && typeof value === "object" && typeof value.then === "function";
781
+ }
782
+
778
783
  // src/utils/validation.ts
779
784
  var parseOrThrow = (schema, input, { adaptError } = {}) => {
780
785
  const result = schema.safeParse(input);
@@ -1241,7 +1246,7 @@ function createRawFunction(coreFn, options) {
1241
1246
  try {
1242
1247
  const parsed = schema ? validateOptions(schema, input, { adaptError }) : input;
1243
1248
  const result = coreFn(parsed, context);
1244
- if (result !== null && typeof result === "object" && typeof result.then === "function") {
1249
+ if (isPromiseLike(result)) {
1245
1250
  return result.then(
1246
1251
  (value) => {
1247
1252
  fireEnd();
@@ -2573,9 +2578,6 @@ function applyListOutputPolicy(page, policy) {
2573
2578
  var FRAMEWORK_CONFIGURATION_IDS = /* @__PURE__ */ new Set([
2574
2579
  CORE_OPTIONS_ID
2575
2580
  ]);
2576
- function isPromiseLike(value) {
2577
- return value !== null && typeof value === "object" && typeof value.then === "function";
2578
- }
2579
2581
  function normalizeOutput(output) {
2580
2582
  if (output === void 0) return { type: "raw" };
2581
2583
  if (typeof output === "string") return { type: output };
@@ -5062,6 +5064,7 @@ var DEFAULT_IDEMPOTENT_METHODS = [
5062
5064
  "TRACE"
5063
5065
  ];
5064
5066
  var BASE_BACKOFF_MILLISECONDS = 1e3;
5067
+ var MIN_BACKOFF_MILLISECONDS = 100;
5065
5068
  var JITTER_FACTOR = 0.5;
5066
5069
  var EPOCH_THRESHOLD_SECONDS = 1e9;
5067
5070
  function directedDelayMilliseconds(response) {
@@ -5086,6 +5089,12 @@ function backoffMilliseconds(attemptNumber) {
5086
5089
  const base = BASE_BACKOFF_MILLISECONDS * 2 ** (attemptNumber - 1);
5087
5090
  return base + Math.random() * JITTER_FACTOR * base;
5088
5091
  }
5092
+ function clampBackoffMilliseconds(attemptNumber, maxDelayMilliseconds) {
5093
+ return Math.max(
5094
+ Math.min(backoffMilliseconds(attemptNumber), maxDelayMilliseconds),
5095
+ MIN_BACKOFF_MILLISECONDS
5096
+ );
5097
+ }
5089
5098
  function abortReason(signal) {
5090
5099
  const reason = signal?.reason;
5091
5100
  return reason ?? new Error("The request was aborted.");
@@ -5120,6 +5129,20 @@ var retryHttpRequestPlugin = defineHook({
5120
5129
  const maxDelayMilliseconds = options.maxDelayMilliseconds ?? DEFAULT_MAX_DELAY_MILLISECONDS;
5121
5130
  const idempotentMethods = options.idempotentMethods ?? DEFAULT_IDEMPOTENT_METHODS;
5122
5131
  const statuses = isIdempotent(input.request, idempotentMethods) ? options.retryStatuses ?? DEFAULT_RETRY_STATUSES : options.nonIdempotentRetryStatuses ?? DEFAULT_NON_IDEMPOTENT_RETRY_STATUSES;
5132
+ const notifyRetry = (notice) => {
5133
+ try {
5134
+ const observed = options.onRetry?.({
5135
+ ...notice,
5136
+ request: input.attempt.operation.request,
5137
+ operationId: input.attempt.operation.operationId
5138
+ });
5139
+ if (isPromiseLike(observed)) {
5140
+ void observed.then(void 0, () => {
5141
+ });
5142
+ }
5143
+ } catch {
5144
+ }
5145
+ };
5123
5146
  for (let attemptNumber = 1; ; attemptNumber++) {
5124
5147
  const attempt = {
5125
5148
  ...input.attempt,
@@ -5141,10 +5164,16 @@ var retryHttpRequestPlugin = defineHook({
5141
5164
  )) {
5142
5165
  throw error;
5143
5166
  }
5144
- await sleep(
5145
- Math.min(backoffMilliseconds(attemptNumber), maxDelayMilliseconds),
5146
- attempt.signal
5167
+ const errorDelay = clampBackoffMilliseconds(
5168
+ attemptNumber,
5169
+ maxDelayMilliseconds
5147
5170
  );
5171
+ notifyRetry({
5172
+ attemptNumber,
5173
+ delayMilliseconds: errorDelay,
5174
+ error
5175
+ });
5176
+ await sleep(errorDelay, attempt.signal);
5148
5177
  continue;
5149
5178
  }
5150
5179
  if (!statuses.includes(response.status) || !canRetry(
@@ -5157,10 +5186,8 @@ var retryHttpRequestPlugin = defineHook({
5157
5186
  const directed = directedDelayMilliseconds(response);
5158
5187
  if (directed != null && directed > maxDelayMilliseconds)
5159
5188
  return response;
5160
- const delay = Math.min(
5161
- directed ?? backoffMilliseconds(attemptNumber),
5162
- maxDelayMilliseconds
5163
- );
5189
+ const delay = directed ?? clampBackoffMilliseconds(attemptNumber, maxDelayMilliseconds);
5190
+ notifyRetry({ attemptNumber, delayMilliseconds: delay, response });
5164
5191
  await response.body?.cancel().catch(() => {
5165
5192
  });
5166
5193
  await sleep(delay, attempt.signal);