{"version":3,"file":"base.cjs","names":["EmptyChannelError","getDeltaMaxSuperstepsSinceSnapshot","DeltaSnapshot"],"sources":["../../src/channels/base.ts"],"sourcesContent":["import {\n  ReadonlyCheckpoint,\n  uuid6,\n  Checkpoint,\n  DeltaSnapshot,\n  type BaseCheckpointSaver,\n  type DeltaChannelHistory,\n} from \"@langchain/langgraph-checkpoint\";\nimport type { RunnableConfig } from \"@langchain/core/runnables\";\nimport { EmptyChannelError } from \"../errors.js\";\nimport { getDeltaMaxSuperstepsSinceSnapshot } from \"../constants.js\";\n\n/**\n * Structural check for a {@link DeltaChannel} without importing it (avoids an\n * import cycle: `delta.ts` imports `base.ts`).\n */\nexport function isDeltaChannel(channel: BaseChannel): boolean {\n  return channel != null && channel.lc_graph_name === \"DeltaChannel\";\n}\n\nexport function isBaseChannel(obj: unknown): obj is BaseChannel {\n  return obj != null && (obj as BaseChannel).lg_is_channel === true;\n}\n\n/** @internal */\nexport abstract class BaseChannel<\n  ValueType = unknown,\n  UpdateType = unknown,\n  CheckpointType = unknown,\n> {\n  ValueType: ValueType;\n\n  UpdateType: UpdateType;\n\n  /**\n   * The name of the channel.\n   */\n  abstract lc_graph_name: string;\n\n  /** @ignore */\n  lg_is_channel = true;\n\n  /**\n   * Return a new identical channel, optionally initialized from a checkpoint.\n   * Can be thought of as a \"restoration\" from a checkpoint which is a \"snapshot\" of the channel's state.\n   *\n   * @param {CheckpointType | undefined} checkpoint\n   * @param {CheckpointType | undefined} initialValue\n   * @returns {this}\n   */\n  abstract fromCheckpoint(checkpoint?: CheckpointType): this;\n\n  /**\n   * Update the channel's value with the given sequence of updates.\n   * The order of the updates in the sequence is arbitrary.\n   * This method is called by Pregel for all channels at the end of each step.\n   * If there are no updates, it is called with an empty sequence.\n   *\n   * Raises InvalidUpdateError if the sequence of updates is invalid.\n   * Returns True if the channel was updated, False otherwise.\n   *\n   * @throws {InvalidUpdateError} if the sequence of updates is invalid.\n   * @param {Array<UpdateType>} values\n   * @returns {void}\n   */\n  abstract update(values: UpdateType[]): boolean;\n\n  /**\n   * Return the current value of the channel.\n   *\n   * @throws {EmptyChannelError} if the channel is empty (never updated yet).\n   * @returns {ValueType}\n   */\n  abstract get(): ValueType;\n\n  /**\n   * Return a string representation of the channel's current state.\n   *\n   * @throws {EmptyChannelError} if the channel is empty (never updated yet), or doesn't support checkpoints.\n   * @returns {CheckpointType | undefined}\n   */\n  abstract checkpoint(): CheckpointType | undefined;\n\n  /**\n   * Mark the current value of the channel as consumed. By default, no-op.\n   * A channel can use this method to modify its state, preventing the value\n   * from being consumed again.\n   *\n   * Returns True if the channel was updated, False otherwise.\n   */\n  consume(): boolean {\n    return false;\n  }\n\n  /**\n   * Notify the channel that the Pregel run is finishing. By default, no-op.\n   * A channel can use this method to modify its state, preventing finish.\n   *\n   * Returns True if the channel was updated, False otherwise.\n   */\n  finish(): boolean {\n    return false;\n  }\n\n  /**\n   * Return True if the channel is available (not empty), False otherwise.\n   * Subclasses should override this method to provide a more efficient\n   * implementation than calling get() and catching EmptyChannelError.\n   */\n  isAvailable(): boolean {\n    try {\n      this.get();\n      return true;\n      // eslint-disable-next-line @typescript-eslint/no-explicit-any\n    } catch (error: any) {\n      if (error.name === EmptyChannelError.unminifiable_name) {\n        return false;\n      }\n      throw error;\n    }\n  }\n\n  /**\n   * Compare this channel with another channel for equality.\n   * Used to determine if two channels with the same key are semantically equivalent.\n   * Subclasses should override this method to provide a meaningful comparison.\n   *\n   * @param {BaseChannel} other - The other channel to compare with.\n   * @returns {boolean} True if the channels are equal, false otherwise.\n   */\n  equals(other: BaseChannel): boolean {\n    return this === other;\n  }\n}\n\nconst IS_ONLY_BASE_CHANNEL = Symbol.for(\"LG_IS_ONLY_BASE_CHANNEL\");\nexport function getOnlyChannels(\n  channels: Record<string, BaseChannel>\n): Record<string, BaseChannel> {\n  // @ts-expect-error - we know it's a record of base channels\n  if (channels[IS_ONLY_BASE_CHANNEL] === true) return channels;\n\n  const newChannels = {} as Record<string, BaseChannel>;\n  for (const k in channels) {\n    if (!Object.prototype.hasOwnProperty.call(channels, k)) continue;\n    const value = channels[k];\n    if (isBaseChannel(value)) newChannels[k] = value;\n  }\n\n  Object.assign(newChannels, { [IS_ONLY_BASE_CHANNEL]: true });\n  return newChannels;\n}\n\nexport function emptyChannels<Cc extends Record<string, BaseChannel>>(\n  channels: Cc,\n  checkpoint: ReadonlyCheckpoint\n): Cc {\n  const filteredChannels = getOnlyChannels(channels) as Cc;\n\n  const newChannels = {} as Cc;\n  for (const k in filteredChannels) {\n    if (!Object.prototype.hasOwnProperty.call(filteredChannels, k)) continue;\n    const channelValue = checkpoint.channel_values[k];\n    newChannels[k] = filteredChannels[k].fromCheckpoint(channelValue);\n  }\n  Object.assign(newChannels, { [IS_ONLY_BASE_CHANNEL]: true });\n  return newChannels;\n}\n\n/**\n * Minimal structural view of a {@link DeltaChannel}, used by helpers in this\n * module that must not import the concrete class (import-cycle avoidance).\n */\ninterface DeltaChannelLike extends BaseChannel {\n  snapshotFrequency: number;\n  replayWrites(writes: DeltaChannelHistory[\"writes\"]): void;\n}\n\n/**\n * Return the set of {@link DeltaChannel} names that should snapshot now.\n *\n * A channel snapshots when EITHER its accumulated update count reaches\n * `snapshotFrequency` OR the total supersteps since its last snapshot reaches\n * `DELTA_MAX_SUPERSTEPS_SINCE_SNAPSHOT`. Pure predicate — no mutation.\n */\nexport function deltaChannelsToSnapshot(\n  channels: Record<string, BaseChannel>,\n  countersSinceDeltaSnapshot: Record<string, [number, number]>\n): Set<string> {\n  const result = new Set<string>();\n  const maxSupersteps = getDeltaMaxSuperstepsSinceSnapshot();\n  for (const name in channels) {\n    if (!Object.prototype.hasOwnProperty.call(channels, name)) continue;\n    const ch = channels[name];\n    if (!isDeltaChannel(ch) || !ch.isAvailable()) continue;\n    const [updates, supersteps] = countersSinceDeltaSnapshot[name] ?? [0, 0];\n    if (\n      updates >= (ch as DeltaChannelLike).snapshotFrequency ||\n      supersteps >= maxSupersteps\n    ) {\n      result.add(name);\n    }\n  }\n  return result;\n}\n\nexport function createCheckpoint<ValueType>(\n  checkpoint: ReadonlyCheckpoint,\n  channels: Record<string, BaseChannel<ValueType>> | undefined,\n  step: number,\n  options?: {\n    id?: string;\n    channelsToSnapshot?: Set<string>;\n    updatedChannels?: Set<string>;\n    getNextVersion?: (current: number | string | undefined) => number | string;\n  }\n): Checkpoint {\n  const channelsToSnapshot = options?.channelsToSnapshot ?? new Set<string>();\n  const { updatedChannels, getNextVersion } = options ?? {};\n  // eslint-disable-next-line @typescript-eslint/no-explicit-any\n  let values: Record<string, any>;\n  let channelVersions: Record<string, number | string> =\n    checkpoint.channel_versions;\n  if (channels === undefined) {\n    values = checkpoint.channel_values;\n  } else {\n    values = {};\n    channelVersions = { ...checkpoint.channel_versions };\n    for (const k in channels) {\n      if (!Object.prototype.hasOwnProperty.call(channels, k)) continue;\n      const channel = channels[k];\n      if (channelsToSnapshot.has(k)) {\n        // Snapshot a DeltaChannel: store the materialized value directly. In\n        // exit/deferred modes the channel may have reached its snapshot\n        // threshold over several supersteps without the LAST superstep\n        // writing to it, so its version wouldn't be bumped by applyWrites —\n        // bump it here so the saver includes the snapshot blob.\n        if (\n          getNextVersion !== undefined &&\n          (updatedChannels === undefined || !updatedChannels.has(k))\n        ) {\n          channelVersions[k] = getNextVersion(channelVersions[k]);\n        }\n        values[k] = new DeltaSnapshot(channel.get());\n        continue;\n      }\n      if (isDeltaChannel(channel)) {\n        // Omitted from channel_values; reconstructed from ancestor writes.\n        continue;\n      }\n      try {\n        values[k] = channel.checkpoint();\n        // eslint-disable-next-line @typescript-eslint/no-explicit-any\n      } catch (error: any) {\n        if (error.name === EmptyChannelError.unminifiable_name) {\n          // no-op\n        } else {\n          throw error; // Rethrow unexpected errors\n        }\n      }\n    }\n  }\n\n  return {\n    v: 4,\n    id: options?.id ?? uuid6(step),\n    ts: new Date().toISOString(),\n    channel_values: values,\n    channel_versions: channelVersions,\n    versions_seen: checkpoint.versions_seen,\n  };\n}\n\n/**\n * Hydrate channels from a checkpoint, reconstructing any {@link DeltaChannel}\n * whose value is absent from `channel_values` by replaying ancestor writes.\n *\n * For most channels (and for delta channels with a {@link DeltaSnapshot} or a\n * migrated plain value in `channel_values`), {@link emptyChannels} is\n * sufficient and no saver access is required. When a delta channel is absent\n * from `channel_values`, an ancestor walk via `saver.getDeltaChannelHistory`\n * finds the nearest seed and accumulates the writes between it and the\n * target. All delta channels needing replay are batched into a single saver\n * call.\n */\nexport async function channelsFromCheckpoint<\n  Cc extends Record<string, BaseChannel>,\n>(\n  specs: Cc,\n  checkpoint: ReadonlyCheckpoint,\n  options?: { saver?: BaseCheckpointSaver; config?: RunnableConfig }\n): Promise<Cc> {\n  const channels = emptyChannels(specs, checkpoint);\n  const { saver, config } = options ?? {};\n\n  const filteredSpecs = getOnlyChannels(specs);\n  const deltaKeys: string[] = [];\n  for (const k in filteredSpecs) {\n    if (!Object.prototype.hasOwnProperty.call(filteredSpecs, k)) continue;\n    if (\n      isDeltaChannel(filteredSpecs[k]) &&\n      !Object.prototype.hasOwnProperty.call(checkpoint.channel_values, k)\n    ) {\n      deltaKeys.push(k);\n    }\n  }\n\n  if (deltaKeys.length === 0 || saver === undefined || config === undefined) {\n    return channels;\n  }\n\n  const histories = await saver.getDeltaChannelHistory({\n    config,\n    channels: deltaKeys,\n  });\n  for (const k of deltaKeys) {\n    const history = histories[k];\n    if (history === undefined) continue;\n    const replayCh = filteredSpecs[k].fromCheckpoint(\n      history.seed\n    ) as unknown as DeltaChannelLike;\n    replayCh.replayWrites(history.writes);\n    (channels as Record<string, BaseChannel>)[k] = replayCh;\n  }\n  return channels;\n}\n"],"mappings":"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"}