{"version":3,"file":"errors.cjs","names":["errorConstructors","NonRetriableError","target: unknown","z","requiredFields: (keyof SerializedError)[]"],"sources":["../../src/helpers/errors.ts"],"sourcesContent":["import stringify from \"json-stringify-safe\";\nimport {\n  type SerializedError as CjsSerializedError,\n  deserializeError as cjsDeserializeError,\n  serializeError as cjsSerializeError,\n  errorConstructors,\n} from \"serialize-error-cjs\";\nimport { z } from \"zod/v3\";\nimport { NonRetriableError } from \"../components/NonRetriableError.ts\";\nimport type { ClientOptions, OutgoingOp } from \"../types.ts\";\n\nconst SERIALIZED_KEY = \"__serialized\";\nconst SERIALIZED_VALUE = true;\n\n/**\n * Add first-class support for certain errors that we control, in addition to\n * built-in errors such as `TypeError`.\n *\n * Adding these allows these non-standard errors to be correctly serialized,\n * sent to Inngest, then deserialized back into the correct error type for users\n * to react to correctly.\n *\n * Note that these errors only support `message?: string | undefined` as the\n * input; more custom errors are not supported with this current strategy.\n */\nerrorConstructors.set(\n  \"NonRetriableError\",\n  NonRetriableError as ErrorConstructor,\n);\n\nexport interface SerializedError extends Readonly<CjsSerializedError> {\n  readonly [SERIALIZED_KEY]: typeof SERIALIZED_VALUE;\n}\n\n/**\n * Serialise an error to a serialized JSON string.\n *\n * Errors do not serialise nicely to JSON, so we use this function to convert\n * them to a serialized JSON string. Doing this is also non-trivial for some\n * errors, so we use the `serialize-error` package to do it for us.\n *\n * See {@link https://www.npmjs.com/package/serialize-error}\n *\n * This function is a small wrapper around that package to also add a `type`\n * property to the serialised error, so that we can distinguish between\n * serialised errors and other objects.\n *\n * Will not reserialise existing serialised errors.\n */\nexport const serializeError = <\n  TAllowUnknown extends boolean = false,\n  TOutput extends TAllowUnknown extends true\n    ? unknown\n    : SerializedError = TAllowUnknown extends true ? unknown : SerializedError,\n>(\n  /**\n   * The suspected error to serialize.\n   */\n  subject: unknown,\n\n  /**\n   * If `true` and the error is not serializable, will return the original value\n   * as `unknown` instead of coercing it to a serialized error.\n   */\n  allowUnknown: TAllowUnknown = false as TAllowUnknown,\n): TOutput => {\n  try {\n    // Try to understand if this is already done.\n    // Will handle stringified errors.\n    const existingSerializedError = isSerializedError(subject);\n\n    if (existingSerializedError) {\n      return existingSerializedError as TOutput;\n    }\n\n    if (typeof subject === \"object\" && subject !== null) {\n      // Is an object, so let's try and serialize it.\n      const serializedErr = cjsSerializeError(subject as Error);\n\n      // Not a proper error was caught, so give us a chance to return `unknown`.\n      if (!serializedErr.name && allowUnknown) {\n        return subject as TOutput;\n      }\n\n      // Serialization can succeed but assign no name or message, so we'll\n      // map over the result here to ensure we have everything.\n      // We'll just stringify the entire subject for the message, as this at\n      // least provides some context for the user.\n      const ret = {\n        // Ensure we spread to also capture additional properties such as\n        // `cause`.\n        ...serializedErr,\n\n        name: serializedErr.name || \"Error\",\n        message:\n          serializedErr.message ||\n          stringify(subject) ||\n          \"Unknown error; error serialization could not find a message.\",\n        stack: serializedErr.stack || \"\",\n        [SERIALIZED_KEY]: SERIALIZED_VALUE,\n      } as const;\n\n      // If we have a cause, make sure we recursively serialize them too. We are\n      // lighter with causes though; attempt to recursively serialize them, but\n      // stop if we find something that doesn't work and just return `unknown`.\n      let target: unknown = ret;\n      const maxDepth = 5;\n      for (let i = 0; i < maxDepth; i++) {\n        if (\n          typeof target === \"object\" &&\n          target !== null &&\n          \"cause\" in target &&\n          target.cause\n        ) {\n          target = target.cause = serializeError(target.cause, true);\n          continue;\n        }\n\n        break;\n      }\n\n      return ret as TOutput;\n    }\n\n    // If it's not an object, it's hard to parse this as an Error. In this case,\n    // we'll throw an error to start attempting backup strategies.\n    throw new Error(\"Error is not an object; strange throw value.\");\n  } catch {\n    if (allowUnknown) {\n      // If we are allowed to return unknown, we'll just return the original\n      // value.\n      return subject as TOutput;\n    }\n\n    try {\n      // If serialization fails, fall back to a regular Error and use the\n      // original object as the message for an Error. We don't know what this\n      // object looks like, so we can't do anything else with it.\n      return {\n        ...serializeError(\n          new Error(typeof subject === \"string\" ? subject : stringify(subject)),\n          false,\n        ),\n        // Remove the stack; it's not relevant here\n        stack: \"\",\n        [SERIALIZED_KEY]: SERIALIZED_VALUE,\n      } as TOutput;\n    } catch {\n      // If this failed, then stringifying the object also failed, so we'll just\n      // return a completely generic error.\n      // Failing to stringify the object is very unlikely.\n      return {\n        name: \"Could not serialize source error\",\n        message: \"Serializing the source error failed.\",\n        stack: \"\",\n        [SERIALIZED_KEY]: SERIALIZED_VALUE,\n      } as TOutput;\n    }\n  }\n};\n\n/**\n * Check if an object or a string is a serialised error created by\n * {@link serializeError}.\n */\nexport const isSerializedError = (\n  value: unknown,\n): SerializedError | undefined => {\n  try {\n    if (typeof value === \"string\") {\n      const parsed = z\n        .object({\n          [SERIALIZED_KEY]: z.literal(SERIALIZED_VALUE),\n          name: z.enum([...Array.from(errorConstructors.keys())] as [\n            string,\n            ...string[],\n          ]),\n          message: z.string(),\n          stack: z.string(),\n        })\n        .passthrough()\n        .safeParse(JSON.parse(value));\n\n      if (parsed.success) {\n        return parsed.data as SerializedError;\n      }\n    }\n\n    if (typeof value === \"object\" && value !== null) {\n      const objIsSerializedErr =\n        Object.hasOwn(value, SERIALIZED_KEY) &&\n        (value as { [SERIALIZED_KEY]: unknown })[SERIALIZED_KEY] ===\n          SERIALIZED_VALUE;\n\n      if (objIsSerializedErr) {\n        return value as SerializedError;\n      }\n    }\n  } catch {\n    // no-op; we'll return undefined if parsing failed, as it isn't a serialized\n    // error\n  }\n\n  return;\n};\n\n/**\n * Deserialise an error created by {@link serializeError}.\n *\n * Ensures we only deserialise errors that meet a minimum level of\n * applicability, inclusive of error handling to ensure that badly serialized\n * errors are still handled.\n */\nexport const deserializeError = <\n  TAllowUnknown extends boolean = false,\n  TOutput extends TAllowUnknown extends true\n    ? unknown\n    : Error = TAllowUnknown extends true ? unknown : Error,\n>(\n  subject: Partial<SerializedError>,\n  allowUnknown: TAllowUnknown = false as TAllowUnknown,\n): TOutput => {\n  const requiredFields: (keyof SerializedError)[] = [\"name\", \"message\"];\n\n  try {\n    const hasRequiredFields = requiredFields.every((field) => {\n      return Object.hasOwn(subject, field);\n    });\n\n    if (!hasRequiredFields) {\n      throw new Error();\n    }\n\n    const deserializedErr = cjsDeserializeError(subject as SerializedError);\n\n    if (\"cause\" in deserializedErr) {\n      deserializedErr.cause = deserializeError(\n        deserializedErr.cause as Partial<SerializedError>,\n        true,\n      );\n    }\n\n    return deserializedErr as TOutput;\n  } catch {\n    if (allowUnknown) {\n      // If we are allowed to return unknown, we'll just return the original\n      // value.\n      return subject as TOutput;\n    }\n\n    const err = new Error(\"Unknown error; could not reserialize\");\n\n    /**\n     * Remove the stack so that it's not misleadingly shown as the Inngest\n     * internals.\n     */\n    err.stack = undefined;\n\n    return err as TOutput;\n  }\n};\n\nexport enum ErrCode {\n  NESTING_STEPS = \"NESTING_STEPS\",\n\n  /**\n   * Legacy v0 execution error code for when a function has changed and no\n   * longer matches its in-progress state.\n   *\n   * @deprecated Not for use in latest execution method.\n   */\n  NON_DETERMINISTIC_FUNCTION = \"NON_DETERMINISTIC_FUNCTION\",\n\n  /**\n   * Legacy v0 execution error code for when a function is found to be using\n   * async actions after memoziation has occurred, which v0 doesn't support.\n   *\n   * @deprecated Not for use in latest execution method.\n   */\n  ASYNC_DETECTED_AFTER_MEMOIZATION = \"ASYNC_DETECTED_AFTER_MEMOIZATION\",\n\n  /**\n   * Legacy v0 execution error code for when a function is found to be using\n   * steps after a non-step async action has occurred.\n   *\n   * @deprecated Not for use in latest execution method.\n   */\n  STEP_USED_AFTER_ASYNC = \"STEP_USED_AFTER_ASYNC\",\n\n  AUTOMATIC_PARALLEL_INDEXING = \"AUTOMATIC_PARALLEL_INDEXING\",\n\n  NONDETERMINISTIC_STEPS = \"NONDETERMINISTIC_STEPS\",\n}\n\n/**\n * Given an `unknown` object, retrieve the `message` property from it, or fall\n * back to the `fallback` string if it doesn't exist or is empty.\n */\nexport const getErrorMessage = (err: unknown, fallback: string): string => {\n  const { message } = z\n    .object({ message: z.string().min(1) })\n    .catch({ message: fallback })\n    .parse(err);\n\n  return message;\n};\n\nexport const fixEventKeyMissingSteps = [\n  \"Set the `INNGEST_EVENT_KEY` environment variable\",\n  `Pass a key to the \\`new Inngest()\\` constructor using the \\`${\n    \"eventKey\" satisfies keyof ClientOptions\n  }\\` option`,\n];\n\n/**\n * An error that, when thrown, indicates internally that an outgoing operation\n * contains an error.\n *\n * We use this because serialized `data` sent back to Inngest may differ from\n * the error instance itself due to middleware.\n *\n * @internal\n */\nexport class OutgoingResultError extends Error {\n  public readonly result: Pick<OutgoingOp, \"data\" | \"error\">;\n\n  constructor(result: Pick<OutgoingOp, \"data\" | \"error\">) {\n    super(\"OutgoingOpError\");\n    this.result = result;\n  }\n}\n\n/**\n * Create a function that will rethrow an error with a prefix added to the\n * message.\n *\n * Useful for adding context to errors that are rethrown.\n *\n * @example\n * ```ts\n * await doSomeAction().catch(rethrowError(\"Failed to do some action\"));\n * ```\n */\n// biome-ignore lint/suspicious/noExplicitAny: intentional\nexport const rethrowError = (prefix: string): ((err: any) => never) => {\n  return (err) => {\n    try {\n      err.message &&= `${prefix}; ${err.message}`;\n    } catch (_noopErr) {\n      // no-op\n    } finally {\n      // biome-ignore lint/correctness/noUnsafeFinally: intentional\n      throw err;\n    }\n  };\n};\n"],"mappings":"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"}