const require_constants = require("../constants.cjs");
const require_errors = require("../errors.cjs");
let _langchain_langgraph_checkpoint = require("@langchain/langgraph-checkpoint");
//#region src/channels/base.ts
/** Matches Postgres `uuid` / Python `uuid.UUID` (128-bit, 8-4-4-4-12 hex). */
const STRUCTURED_UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
/**
* Structural check for a {@link DeltaChannel} without importing it (avoids an
* import cycle: `delta.ts` imports `base.ts`).
*/
function isDeltaChannel(channel) {
	return channel != null && channel.lc_graph_name === "DeltaChannel";
}
function isBaseChannel(obj) {
	return obj != null && obj.lg_is_channel === true;
}
/** @internal */
var BaseChannel = class {
	ValueType;
	UpdateType;
	/** @ignore */
	lg_is_channel = true;
	/**
	* Mark the current value of the channel as consumed. By default, no-op.
	* A channel can use this method to modify its state, preventing the value
	* from being consumed again.
	*
	* Returns True if the channel was updated, False otherwise.
	*/
	consume() {
		return false;
	}
	/**
	* Notify the channel that the Pregel run is finishing. By default, no-op.
	* A channel can use this method to modify its state, preventing finish.
	*
	* Returns True if the channel was updated, False otherwise.
	*/
	finish() {
		return false;
	}
	/**
	* Return True if the channel is available (not empty), False otherwise.
	* Subclasses should override this method to provide a more efficient
	* implementation than calling get() and catching EmptyChannelError.
	*/
	isAvailable() {
		try {
			this.get();
			return true;
		} catch (error) {
			if (error.name === require_errors.EmptyChannelError.unminifiable_name) return false;
			throw error;
		}
	}
	/**
	* Compare this channel with another channel for equality.
	* Used to determine if two channels with the same key are semantically equivalent.
	* Subclasses should override this method to provide a meaningful comparison.
	*
	* @param {BaseChannel} other - The other channel to compare with.
	* @returns {boolean} True if the channels are equal, false otherwise.
	*/
	equals(other) {
		return this === other;
	}
};
const IS_ONLY_BASE_CHANNEL = Symbol.for("LG_IS_ONLY_BASE_CHANNEL");
function getOnlyChannels(channels) {
	if (channels[IS_ONLY_BASE_CHANNEL] === true) return channels;
	const newChannels = {};
	for (const k in channels) {
		if (!Object.prototype.hasOwnProperty.call(channels, k)) continue;
		const value = channels[k];
		if (isBaseChannel(value)) newChannels[k] = value;
	}
	Object.assign(newChannels, { [IS_ONLY_BASE_CHANNEL]: true });
	return newChannels;
}
function emptyChannels(channels, checkpoint) {
	const filteredChannels = getOnlyChannels(channels);
	const newChannels = {};
	for (const k in filteredChannels) {
		if (!Object.prototype.hasOwnProperty.call(filteredChannels, k)) continue;
		const channelValue = checkpoint.channel_values[k];
		newChannels[k] = filteredChannels[k].fromCheckpoint(channelValue);
	}
	Object.assign(newChannels, { [IS_ONLY_BASE_CHANNEL]: true });
	return newChannels;
}
/**
* Synthetic task id for exit-mode DeltaChannel writes.
*
* Embeds the superstep in the first UUID group so `ORDER BY task_id, idx`
* preserves chronological order while remaining a valid RFC UUID (required by
* Postgres `checkpoint_writes.task_id uuid` columns).
*/
function exitDeltaTaskId(step, taskId) {
	if (!STRUCTURED_UUID.test(taskId)) throw new TypeError(`Invalid task id for exit delta: ${taskId}`);
	const parts = taskId.toLowerCase().split("-");
	return `${String(step).padStart(8, "0")}-${parts[1]}-${parts[2]}-${parts[3]}-${parts[4]}`;
}
/**
* Return the set of {@link DeltaChannel} names that should snapshot now.
*
* A channel snapshots when EITHER its accumulated update count reaches
* `snapshotFrequency` OR the total supersteps since its last snapshot reaches
* `DELTA_MAX_SUPERSTEPS_SINCE_SNAPSHOT`. Pure predicate — no mutation.
*/
function deltaChannelsToSnapshot(channels, countersSinceDeltaSnapshot) {
	const result = /* @__PURE__ */ new Set();
	const maxSupersteps = require_constants.getDeltaMaxSuperstepsSinceSnapshot();
	for (const name in channels) {
		if (!Object.prototype.hasOwnProperty.call(channels, name)) continue;
		const ch = channels[name];
		if (!isDeltaChannel(ch) || !ch.isAvailable()) continue;
		const [updates, supersteps] = countersSinceDeltaSnapshot[name] ?? [0, 0];
		if (updates >= ch.snapshotFrequency || supersteps >= maxSupersteps) result.add(name);
	}
	return result;
}
function createCheckpoint(checkpoint, channels, step, options) {
	const channelsToSnapshot = options?.channelsToSnapshot ?? /* @__PURE__ */ new Set();
	const { updatedChannels, getNextVersion } = options ?? {};
	let values;
	let channelVersions = checkpoint.channel_versions;
	if (channels === void 0) values = checkpoint.channel_values;
	else {
		values = {};
		channelVersions = { ...checkpoint.channel_versions };
		for (const k in channels) {
			if (!Object.prototype.hasOwnProperty.call(channels, k)) continue;
			const channel = channels[k];
			if (channelsToSnapshot.has(k)) {
				if (getNextVersion !== void 0 && (updatedChannels === void 0 || !updatedChannels.has(k))) channelVersions[k] = getNextVersion(channelVersions[k]);
				values[k] = new _langchain_langgraph_checkpoint.DeltaSnapshot(channel.get());
				continue;
			}
			if (isDeltaChannel(channel)) continue;
			try {
				values[k] = channel.checkpoint();
			} catch (error) {
				if (error.name === require_errors.EmptyChannelError.unminifiable_name) {} else throw error;
			}
		}
	}
	return {
		v: 4,
		id: options?.id ?? (0, _langchain_langgraph_checkpoint.uuid6)(step),
		ts: (/* @__PURE__ */ new Date()).toISOString(),
		channel_values: values,
		channel_versions: channelVersions,
		versions_seen: checkpoint.versions_seen
	};
}
/**
* Hydrate channels from a checkpoint, reconstructing any {@link DeltaChannel}
* whose value is absent from `channel_values` by replaying ancestor writes.
*
* For most channels (and for delta channels with a {@link DeltaSnapshot} or a
* migrated plain value in `channel_values`), {@link emptyChannels} is
* sufficient and no saver access is required. When a delta channel is absent
* from `channel_values`, an ancestor walk via `saver.getDeltaChannelHistory`
* finds the nearest seed and accumulates the writes between it and the
* target. All delta channels needing replay are batched into a single saver
* call.
*/
async function channelsFromCheckpoint(specs, checkpoint, options) {
	const channels = emptyChannels(specs, checkpoint);
	const { saver, config } = options ?? {};
	const filteredSpecs = getOnlyChannels(specs);
	const deltaKeys = [];
	for (const k in filteredSpecs) {
		if (!Object.prototype.hasOwnProperty.call(filteredSpecs, k)) continue;
		if (isDeltaChannel(filteredSpecs[k]) && !Object.prototype.hasOwnProperty.call(checkpoint.channel_values, k)) deltaKeys.push(k);
	}
	if (deltaKeys.length === 0 || saver === void 0 || config === void 0) return channels;
	const histories = await saver.getDeltaChannelHistory({
		config,
		channels: deltaKeys
	});
	for (const k of deltaKeys) {
		const history = histories[k];
		if (history === void 0) continue;
		const replayCh = filteredSpecs[k].fromCheckpoint(history.seed);
		replayCh.replayWrites(history.writes);
		channels[k] = replayCh;
	}
	return channels;
}
//#endregion
exports.BaseChannel = BaseChannel;
exports.channelsFromCheckpoint = channelsFromCheckpoint;
exports.createCheckpoint = createCheckpoint;
exports.deltaChannelsToSnapshot = deltaChannelsToSnapshot;
exports.emptyChannels = emptyChannels;
exports.exitDeltaTaskId = exitDeltaTaskId;
exports.getOnlyChannels = getOnlyChannels;
exports.isBaseChannel = isBaseChannel;
exports.isDeltaChannel = isDeltaChannel;

//# sourceMappingURL=base.cjs.map