var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __decorateClass = (decorators, target, key, kind) => {
  var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
  for (var i = decorators.length - 1, decorator; i >= 0; i--)
    if (decorator = decorators[i])
      result = (kind ? decorator(target, key, result) : decorator(result)) || result;
  if (kind && result) __defProp(target, key, result);
  return result;
};
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);

// src/modules/rest-block-viewer/chainLayout.ts
import {
  asChainManifest,
  CHAIN_LAYOUT_VERSION,
  ChainManifestSchema,
  ChainManifestZod,
  createChainManifest,
  isChainManifest,
  toChainManifest
} from "@xyo-network/xl1-protocol/network-model";

// src/modules/rest-block-viewer/indexLayout.ts
import {
  asIndexHead,
  asIndexManifest,
  createIndexHead,
  createIndexManifest,
  createIndexManifestFamilies,
  INDEX_LAYOUT_VERSION,
  IndexHeadSchema,
  IndexHeadZod,
  IndexManifestSchema,
  IndexManifestZod,
  isIndexHead,
  isIndexManifest,
  toIndexHead,
  toIndexManifest
} from "@xyo-network/xl1-protocol/network-model";

// src/modules/rest-block-viewer/paths.ts
import {
  blockHashPath,
  blockNumberPath,
  headPath,
  indexHeadPath,
  indexManifestPath,
  indexSummaryPath,
  manifestPath,
  payloadPath
} from "@xyo-network/xl1-protocol/network-model";

// src/modules/rest-block-viewer/RestBlockViewer.ts
import {
  assertEx,
  exists,
  isDefined
} from "@ariestools/sdk";
import { WithHashMetaPayloadZodLoose } from "@xyo-network/sdk";
import {
  asBlocksStepSummary,
  asSignedHydratedBlockWithHashMeta,
  asXL1BlockNumber,
  asXL1BlockRange,
  BlockViewerMoniker,
  ChainStateViewerMoniker,
  IndexViewerMoniker,
  StepSizes
} from "@xyo-network/xl1-protocol/protocol-lib";
import { LruCacheMap } from "#driver-memory";
import {
  AbstractCreatableProvider,
  blocksMaxStep,
  calculateBlockRate,
  calculateStepSizeRate,
  calculateTimeRate,
  creatableProvider,
  deepCalculateFramesFromRange,
  resolveRestConnectionEndpoint
} from "#protocol-sdk";

// src/modules/rest-block-viewer/stepSummaryCacheLimits.ts
var REST_SUMMARY_CACHE_MAX_BY_STEP = {
  balances: [512, 128, 32, 8, 2, 1],
  blocks: [512, 128, 16, 2],
  schemas: [512, 128, 32, 8, 2, 1],
  transfers: [512, 128, 32, 8, 2]
};

// src/modules/rest-block-viewer/RestBlockViewer.ts
var MIN_HEAD_POLL_INTERVAL_MS = 5e3;
var CURRENT_BLOCK_CACHE_TTL_MS = 1e3;
var CURRENT_BLOCK_CACHE_KEY = "head";
var BLOCKS_BY_NUMBER_STEP_ACCEL_MIN_LIMIT = StepSizes[1];
var PayloadFileZod = WithHashMetaPayloadZodLoose;
var RestBlockViewer = class extends AbstractCreatableProvider {
  /** Moniker implemented by this provider instance. */
  moniker = RestBlockViewer.defaultMoniker;
  blockByHashCache = new LruCacheMap({ max: 2e3 });
  // single-flight maps: concurrent identical requests share one in-flight fetch instead of each
  // missing the value cache and fetching separately; entries clear as soon as the fetch settles
  blockByHashInFlight = /* @__PURE__ */ new Map();
  blockByNumberCache = new LruCacheMap({ max: 2e3 });
  blockByNumberInFlight = /* @__PURE__ */ new Map();
  currentBlockCache = new LruCacheMap({ max: 1, ttl: CURRENT_BLOCK_CACHE_TTL_MS });
  payloadCache = new LruCacheMap({ max: 1e4 });
  stepCaches = REST_SUMMARY_CACHE_MAX_BY_STEP.blocks.map((max) => new LruCacheMap({ max }));
  _chainStateViewer;
  _headPollHash;
  _headPollInProgress = false;
  _headPollTimer = null;
  _indexViewer;
  /** Finalizes  base url. */
  get finalizedBaseUrl() {
    return this.params.finalizedBaseUrl;
  }
  get fetchFn() {
    return this.params.fetchFn ?? this.context.fetchFn ?? fetch.bind(globalThis);
  }
  get headPollIntervalMs() {
    return this.params.headPollIntervalMs;
  }
  /** Returns the params handler result exposed by this provider. */
  static async paramsHandler(params) {
    const headPollIntervalMs = params.headPollIntervalMs;
    if (headPollIntervalMs !== void 0) {
      assertEx(headPollIntervalMs >= MIN_HEAD_POLL_INTERVAL_MS, () => `headPollIntervalMs must be at least ${MIN_HEAD_POLL_INTERVAL_MS}ms`);
    }
    const moniker = this.defaultMoniker;
    const finalizedBaseUrl = isDefined(params.finalizedBaseUrl) && params.finalizedBaseUrl !== "" ? params.finalizedBaseUrl : resolveRestConnectionEndpoint(
      assertEx(params.context?.config, () => "Context config is required to resolve REST block endpoint"),
      moniker
    );
    return {
      ...await super.paramsHandler(params),
      fetchFn: params.fetchFn,
      finalizedBaseUrl: finalizedBaseUrl.replace(/\/+$/, ""),
      headPollIntervalMs
    };
  }
  /** Returns the block by hash result exposed by this provider. */
  async blockByHash(hash) {
    return await this.spanAsync("blockByHash", async () => {
      const cached = this.blockByHashCache.get(hash);
      if (cached) return cached;
      const inFlight = this.blockByHashInFlight.get(hash);
      if (inFlight) return await inFlight;
      const promise = (async () => {
        const parsed = await this.fetchJson(blockHashPath(hash));
        return parsed === void 0 ? null : this.cacheBlock(parsed);
      })();
      this.blockByHashInFlight.set(hash, promise);
      try {
        return await promise;
      } finally {
        this.blockByHashInFlight.delete(hash);
      }
    }, { ...this.context, timeBudgetLimit: 100 });
  }
  /** Returns the block by number result exposed by this provider. */
  async blockByNumber(blockNumber) {
    return await this.spanAsync("blockByNumber", async () => {
      const cached = this.blockByNumberCache.get(blockNumber);
      if (cached) return cached;
      const inFlight = this.blockByNumberInFlight.get(blockNumber);
      if (inFlight) return await inFlight;
      const promise = (async () => {
        const parsed = await this.fetchJson(blockNumberPath(blockNumber));
        return parsed === void 0 ? null : this.cacheBlock(parsed);
      })();
      this.blockByNumberInFlight.set(blockNumber, promise);
      try {
        return await promise;
      } finally {
        this.blockByNumberInFlight.delete(blockNumber);
      }
    }, { ...this.context, timeBudgetLimit: 100 });
  }
  /** Returns the blocks by hash result exposed by this provider. */
  async blocksByHash(hash, limit = 50) {
    return await this.spanAsync("blocksByHash", async () => {
      this.assertBlocksByRangeLimit(limit);
      const head = await this.blockByHash(hash);
      if (head === null) return [];
      const blocks = await this.blocksByNumber(head[0].block, limit);
      if (blocks.length === 0 || blocks[0][0]._hash !== hash) {
        return [head];
      }
      return this.contiguousPreviousLinkedPrefix(blocks);
    }, { ...this.context, timeBudgetLimit: 300 });
  }
  /** Returns the blocks by number result exposed by this provider. */
  async blocksByNumber(blockNumber, limit = 50) {
    return await this.spanAsync("blocksByNumber", async () => {
      this.assertBlocksByRangeLimit(limit);
      const count = Math.min(limit, blockNumber + 1);
      if (count <= 0) return [];
      if (count >= BLOCKS_BY_NUMBER_STEP_ACCEL_MIN_LIMIT) {
        const viaFrames = await this.blocksByNumberViaDeepFrames(blockNumber, count);
        if (viaFrames !== void 0) return viaFrames;
      }
      return await this.blocksByNumberParallel(blockNumber, count);
    }, this.context);
  }
  /** Returns the blocks by step result exposed by this provider. */
  async blocksByStep(stepLevel, stepIndex) {
    return await this.spanAsync("blocksByStep", async () => {
      const cache = this.stepCaches[stepLevel];
      const cached = cache?.get(stepIndex);
      if (cached) return cached;
      const parsed = await this._indexViewer.stepSummary("blocks", stepLevel, stepIndex);
      if (parsed === null) return [];
      const summary = asBlocksStepSummary(parsed, { required: true });
      const blocks = summary.blocks.map((b) => asSignedHydratedBlockWithHashMeta(b, true)).toReversed();
      cache?.set(stepIndex, blocks);
      return blocks;
    }, this.context);
  }
  /** Returns the chain id result exposed by this provider. */
  async chainId(blockNumber = "latest") {
    return await this.spanAsync("chainId", async () => {
      if (blockNumber === "latest") {
        const block2 = await this.currentBlock();
        return block2[0].chain;
      }
      const block = assertEx(await this.blockByNumber(blockNumber), () => `Block not found [${blockNumber}]`);
      return block[0].chain;
    }, this.context);
  }
  /** Creates the provider instance after its dependencies and configuration have resolved. */
  async createHandler() {
    await super.createHandler();
    this._chainStateViewer = await this.locator.getInstance(ChainStateViewerMoniker);
    this._indexViewer = await this.locator.getInstance(IndexViewerMoniker);
  }
  /** Returns block. */
  async currentBlock() {
    return await this.spanAsync("currentBlock", async () => {
      const cached = this.currentBlockCache.get(CURRENT_BLOCK_CACHE_KEY);
      if (cached) return await cached;
      const promise = this.fetchCurrentBlock();
      this.currentBlockCache.set(CURRENT_BLOCK_CACHE_KEY, promise);
      try {
        return await promise;
      } catch (error) {
        this.currentBlockCache.delete(CURRENT_BLOCK_CACHE_KEY);
        throw error;
      }
    }, this.context);
  }
  /** Returns block hash. */
  async currentBlockHash() {
    const block = await this.currentBlock();
    return block[0]._hash;
  }
  /** Returns block number. */
  async currentBlockNumber() {
    const block = await this.currentBlock();
    return block[0].block;
  }
  /** Returns the payload by hash result exposed by this provider. */
  async payloadByHash(hash) {
    const [payload] = await this.payloadsByHash([hash]);
    return payload ?? null;
  }
  /** Returns the payloads by hash result exposed by this provider. */
  async payloadsByHash(hashes) {
    return await this.spanAsync("payloadsByHash", async () => {
      const results = await Promise.all(hashes.map(async (hash) => {
        const cached = this.payloadCache.get(hash);
        if (cached) return cached;
        const parsed = await this.fetchJson(payloadPath(hash));
        if (parsed === void 0) return;
        const payload = PayloadFileZod.parse(parsed);
        this.payloadCache.set(hash, payload);
        return payload;
      }));
      return results.filter(exists);
    }, this.context);
  }
  /** Returns the rate result exposed by this provider. */
  async rate(range, timeUnit) {
    return await calculateBlockRate(this, range, timeUnit);
  }
  /** Returns the step size rate result exposed by this provider. */
  async stepSizeRate(start, stepIndex, count = 1, timeUnit) {
    return await calculateStepSizeRate(this, start, stepIndex, count, timeUnit);
  }
  /** Returns the time duration rate result exposed by this provider. */
  async timeDurationRate(timeConfig, startBlockNumber, timeUnit, toleranceMs, maxAttempts) {
    return await calculateTimeRate(this, timeConfig, startBlockNumber, timeUnit, toleranceMs, maxAttempts);
  }
  async startHandler() {
    await super.startHandler();
    if (this.headPollIntervalMs === void 0) return;
    await this.pollHead(false);
    this.startHeadPolling();
  }
  async stopHandler() {
    this.stopHeadPolling();
    this._headPollHash = void 0;
    await super.stopHandler();
  }
  assertBlocksByRangeLimit(limit) {
    assertEx(Number.isSafeInteger(limit) && limit > 0, () => "limit must be a positive integer");
  }
  /**
   * Fan-out per-block GETs for `[high - count + 1, high]`, assemble newest-first, and stop at
   * the first missing/broken link. Fetches exactly `count` candidates (no off-by-one past the
   * requested window).
   */
  async blocksByNumberParallel(high, count) {
    const numbers = Array.from(
      { length: count },
      (_, offset) => asXL1BlockNumber(high - offset, true)
    );
    const fetched = await Promise.all(numbers.map(async (n) => await this.blockByNumber(n)));
    return this.contiguousPreviousLinkedPrefix(this.contiguousNonNullPrefix(fetched));
  }
  /**
   * Large-range path: tile `[high - count + 1, high]` with `deepCalculateFramesFromRange`
   * (largest complete step frames first, capped at `blocksMaxStep`), load each multi-block
   * frame via `blocksByStep`, and fill only single-block leftovers or unpublished frames with
   * per-block GETs. Returns `undefined` when no frame produced blocks so the caller can fall
   * back to a pure parallel fan-out.
   */
  async blocksByNumberViaDeepFrames(high, count) {
    const low = high - count + 1;
    const range = asXL1BlockRange(
      [asXL1BlockNumber(low, true), high],
      { name: "blocksByNumberViaDeepFrames" }
    );
    const frames = deepCalculateFramesFromRange(range, blocksMaxStep);
    if (frames.length === 0) return void 0;
    const byNumber = /* @__PURE__ */ new Map();
    await Promise.all(frames.map(async (frame) => await this.loadFrameIntoMap(frame, byNumber)));
    if (byNumber.size === 0) return void 0;
    const missing = Array.from({ length: count }, (_, offset) => high - offset).filter((n) => !byNumber.has(n));
    if (missing.length > 0) {
      const filled = await Promise.all(
        missing.map(async (n) => await this.blockByNumber(asXL1BlockNumber(n, true)))
      );
      for (const block of filled) {
        if (block) byNumber.set(block[0].block, block);
      }
    }
    const assembled = [];
    for (let n = high; n >= low; n--) {
      const block = byNumber.get(n);
      if (!block) break;
      assembled.push(block);
    }
    return this.contiguousPreviousLinkedPrefix(assembled);
  }
  /** Validates a parsed block file and populates both block caches. */
  cacheBlock(parsed) {
    const block = asSignedHydratedBlockWithHashMeta(parsed, true);
    this.blockByHashCache.set(block[0]._hash, block);
    this.blockByNumberCache.set(block[0].block, block);
    return block;
  }
  /** Newest-first contiguous non-null prefix (stops at the first hole). */
  contiguousNonNullPrefix(fetched) {
    const blocks = [];
    for (const block of fetched) {
      if (block === null) break;
      blocks.push(block);
    }
    return blocks;
  }
  /**
   * Keeps the longest newest-first prefix where each block's `previous` matches the next
   * block's hash and block numbers decrease by one. Truncates on the first break so callers
   * never return a silently forked/gapped list from a parallel fan-out.
   */
  contiguousPreviousLinkedPrefix(blocks) {
    if (blocks.length <= 1) return blocks;
    const verified = [blocks[0]];
    for (let i = 1; i < blocks.length; i++) {
      const newer = verified[i - 1];
      const older = blocks[i];
      if (newer[0].previous !== older[0]._hash) break;
      if (newer[0].block - 1 !== older[0].block) break;
      verified.push(older);
    }
    return verified;
  }
  /** The head is ChainStateViewer's question; delegate, then seed the immutable block caches. */
  async fetchCurrentBlock() {
    return this.cacheBlock(await this._chainStateViewer.currentBlock());
  }
  async fetchJson(path) {
    const response = await this.fetchFn(`${this.finalizedBaseUrl}/${path}`);
    if (response.status === 404) return void 0;
    assertEx(response.ok, () => `Request failed [${response.status}] for ${path}`);
    return await response.json();
  }
  /** Parallel per-block fill for `[start, end]` inclusive (skips numbers already present). */
  async loadBlockNumbersIntoMap(start, end, byNumber) {
    const numbers = Array.from(
      { length: end - start + 1 },
      (_, offset) => start + offset
    ).filter((n) => !byNumber.has(n));
    if (numbers.length === 0) return;
    const fetched = await Promise.all(
      numbers.map(async (n) => await this.blockByNumber(asXL1BlockNumber(n, true)))
    );
    for (const block of fetched) {
      if (block) byNumber.set(block[0].block, block);
    }
  }
  /**
   * Loads one deep-calculated frame into the block map: multi-block frames map to a published
   * `blocksByStep` file when available; singles and unpublished frames fall back to per-block GETs.
   */
  async loadFrameIntoMap(frame, byNumber) {
    const [start, end] = frame;
    const size = end - start + 1;
    if (size === 1) {
      const block = await this.blockByNumber(asXL1BlockNumber(start, true));
      if (block) byNumber.set(start, block);
      return;
    }
    const level = StepSizes.indexOf(asXL1BlockNumber(size, true));
    const stepIndex = start / size;
    if (level === -1 || level > blocksMaxStep || !Number.isInteger(stepIndex)) {
      await this.loadBlockNumbersIntoMap(start, end, byNumber);
      return;
    }
    const blocks = await this.blocksByStep(level, stepIndex);
    if (blocks.length === 0) {
      await this.loadBlockNumbersIntoMap(start, end, byNumber);
      return;
    }
    for (const block of blocks) {
      const n = block[0].block;
      if (n >= start && n <= end) byNumber.set(n, block);
    }
  }
  async pollHead(emitOnChange) {
    if (this._headPollInProgress) return;
    this._headPollInProgress = true;
    try {
      const block = await this.currentBlock();
      const hash = block[0]._hash;
      if (this._headPollHash === void 0) {
        this._headPollHash = hash;
        return;
      }
      if (hash !== this._headPollHash) {
        this._headPollHash = hash;
        if (emitOnChange) {
          await this.emit("headUpdated", { block });
        }
      }
    } catch (ex) {
      this.logger?.error("Error polling block head", ex);
    } finally {
      this._headPollInProgress = false;
    }
  }
  startHeadPolling() {
    if (this.headPollIntervalMs === void 0) return;
    this.stopHeadPolling();
    this._headPollTimer = setInterval(() => {
      void this.pollHead(true);
    }, this.headPollIntervalMs);
  }
  stopHeadPolling() {
    if (!this._headPollTimer) {
      return;
    }
    clearInterval(this._headPollTimer);
    this._headPollTimer = null;
  }
};
/** Connection kinds supported by this provider. */
__publicField(RestBlockViewer, "connectionTypes", ["rest"]);
/** Primary moniker used to resolve this provider. */
__publicField(RestBlockViewer, "defaultMoniker", BlockViewerMoniker);
/** Provider monikers that must resolve before this provider can be created. */
__publicField(RestBlockViewer, "dependencies", [ChainStateViewerMoniker, IndexViewerMoniker]);
/** Provider monikers implemented by this provider class. */
__publicField(RestBlockViewer, "monikers", [BlockViewerMoniker]);
/** Read, write, or mixed capability surface exposed by the provider. */
__publicField(RestBlockViewer, "surface", "node");
RestBlockViewer = __decorateClass([
  creatableProvider()
], RestBlockViewer);

// src/modules/rest-block-viewer/RestChainContractViewer.ts
import {
  assertEx as assertEx2,
  isDefined as isDefined2
} from "@ariestools/sdk";
import { asChainContractManifest, chainContractByIdPath } from "@xyo-network/xl1-protocol/network-model";
import { ChainContractViewerMoniker, FinalizationViewerMoniker } from "@xyo-network/xl1-protocol/protocol-lib";
import {
  AbstractCreatableProvider as AbstractCreatableProvider2,
  creatableProvider as creatableProvider2,
  resolveRestConnectionEndpoint as resolveRestConnectionEndpoint2
} from "#protocol-sdk";
var RestChainContractViewer = class extends AbstractCreatableProvider2 {
  /** Moniker implemented by this provider instance. */
  moniker = RestChainContractViewer.defaultMoniker;
  _finalizationViewer;
  _forkedChainContractViewer;
  _manifestPromise;
  /** Returns the contract base url result exposed by this provider. */
  get contractBaseUrl() {
    return this.params.contractBaseUrl;
  }
  get fetchFn() {
    return this.params.fetchFn ?? this.context.fetchFn ?? fetch.bind(globalThis);
  }
  /** Returns the params handler result exposed by this provider. */
  static async paramsHandler(params) {
    const moniker = this.defaultMoniker;
    const contractBaseUrl = isDefined2(params.contractBaseUrl) && params.contractBaseUrl !== "" ? params.contractBaseUrl : resolveRestConnectionEndpoint2(
      assertEx2(params.context?.config, () => "Context config is required to resolve REST chain-contract endpoint"),
      moniker
    );
    return {
      ...await super.paramsHandler(params),
      chainId: params.chainId,
      contractBaseUrl: contractBaseUrl.replace(/\/+$/, ""),
      fetchFn: params.fetchFn
    };
  }
  /** Returns the chain id result exposed by this provider. */
  async chainId() {
    if (isDefined2(this.params.chainId)) return this.params.chainId;
    return await this._finalizationViewer.chainId();
  }
  /** Returns the chain id at block number result exposed by this provider. */
  async chainIdAtBlockNumber(blockNumber) {
    if (blockNumber === "latest") return await this.chainId();
    let chainId = await this.chainId();
    let contractViewer = this;
    let forkedAtBlockNumber = await contractViewer.forkedAtBlockNumber();
    while (forkedAtBlockNumber !== null && blockNumber <= forkedAtBlockNumber) {
      contractViewer = assertEx2(
        await contractViewer.forkedChainContractViewer(),
        () => "Block predates a fork boundary but no forked chain-contract viewer is available"
      );
      forkedAtBlockNumber = await contractViewer.forkedAtBlockNumber();
      chainId = await contractViewer.chainId();
    }
    return chainId;
  }
  /** Creates the provider instance after its dependencies and configuration have resolved. */
  async createHandler() {
    await super.createHandler();
    this._finalizationViewer = await this.locator.getInstance(FinalizationViewerMoniker);
  }
  /** Returns the forked at block number result exposed by this provider. */
  async forkedAtBlockNumber() {
    return (await this.manifest()).forkedAtBlockNumber ?? null;
  }
  /** Returns the forked at hash result exposed by this provider. */
  async forkedAtHash() {
    return (await this.manifest()).forkedAtHash ?? null;
  }
  /** Returns the forked chain contract viewer result exposed by this provider. */
  async forkedChainContractViewer() {
    this._forkedChainContractViewer ??= (async () => {
      const forkedChainId = await this.forkedChainId();
      if (forkedChainId === null) return null;
      return await RestChainContractViewer.create({
        ...this.params,
        chainId: forkedChainId
      });
    })();
    return await this._forkedChainContractViewer;
  }
  /** Returns the forked chain id result exposed by this provider. */
  async forkedChainId() {
    return (await this.manifest()).forkedChainId ?? null;
  }
  /** Returns the min withdrawal blocks result exposed by this provider. */
  async minWithdrawalBlocks() {
    return (await this.manifest()).minWithdrawalBlocks;
  }
  /** Returns the rewards contract result exposed by this provider. */
  async rewardsContract() {
    return (await this.manifest()).rewardsContract;
  }
  /** Returns the staking token address result exposed by this provider. */
  async stakingTokenAddress() {
    return (await this.manifest()).stakingTokenAddress;
  }
  async fetchManifest() {
    const chainId = await this.chainId();
    const path = chainContractByIdPath(chainId);
    const response = await this.fetchFn(`${this.contractBaseUrl}/${path}`);
    assertEx2(response.ok, () => `Chain-contract manifest not found for chain ${chainId} [${response.status}]`);
    const parsed = await response.json();
    return asChainContractManifest(parsed, true);
  }
  async manifest() {
    this._manifestPromise ??= this.fetchManifest();
    return await this._manifestPromise;
  }
};
/** Connection kinds supported by this provider. */
__publicField(RestChainContractViewer, "connectionTypes", ["rest"]);
/** Primary moniker used to resolve this provider. */
__publicField(RestChainContractViewer, "defaultMoniker", ChainContractViewerMoniker);
/** Provider monikers that must resolve before this provider can be created. */
__publicField(RestChainContractViewer, "dependencies", [FinalizationViewerMoniker]);
/** Provider monikers implemented by this provider class. */
__publicField(RestChainContractViewer, "monikers", [ChainContractViewerMoniker]);
/** Read, write, or mixed capability surface exposed by the provider. */
__publicField(RestChainContractViewer, "surface", "node");
RestChainContractViewer = __decorateClass([
  creatableProvider2()
], RestChainContractViewer);

// src/modules/rest-block-viewer/RestChainStateViewer.ts
import { assertEx as assertEx3, isDefined as isDefined3 } from "@ariestools/sdk";
import { asSignedHydratedBlockWithHashMeta as asSignedHydratedBlockWithHashMeta2, ChainStateViewerMoniker as ChainStateViewerMoniker2 } from "@xyo-network/xl1-protocol/protocol-lib";
import {
  AbstractCreatableProvider as AbstractCreatableProvider3,
  creatableProvider as creatableProvider3,
  resolveRestConnectionEndpoint as resolveRestConnectionEndpoint3
} from "#protocol-sdk";
var MIN_HEAD_POLL_INTERVAL_MS2 = 5e3;
var RestChainStateViewer = class extends AbstractCreatableProvider3 {
  /** Moniker implemented by this provider instance. */
  moniker = RestChainStateViewer.defaultMoniker;
  _headPollHash;
  _headPollInProgress = false;
  _headPollTimer = null;
  /** Returns the chain state base url result exposed by this provider. */
  get chainStateBaseUrl() {
    return this.params.chainStateBaseUrl;
  }
  get fetchFn() {
    return this.params.fetchFn ?? this.context.fetchFn ?? fetch.bind(globalThis);
  }
  get headPollIntervalMs() {
    return this.params.headPollIntervalMs;
  }
  /** Returns the params handler result exposed by this provider. */
  static async paramsHandler(params) {
    const headPollIntervalMs = params.headPollIntervalMs;
    if (headPollIntervalMs !== void 0) {
      assertEx3(headPollIntervalMs >= MIN_HEAD_POLL_INTERVAL_MS2, () => `headPollIntervalMs must be at least ${MIN_HEAD_POLL_INTERVAL_MS2}ms`);
    }
    const moniker = this.defaultMoniker;
    const chainStateBaseUrl = isDefined3(params.chainStateBaseUrl) && params.chainStateBaseUrl !== "" ? params.chainStateBaseUrl : resolveRestConnectionEndpoint3(
      assertEx3(params.context?.config, () => "Context config is required to resolve REST chain-state endpoint"),
      moniker
    );
    return {
      ...await super.paramsHandler(params),
      chainStateBaseUrl: chainStateBaseUrl.replace(/\/+$/, ""),
      fetchFn: params.fetchFn,
      headPollIntervalMs
    };
  }
  /** Returns block. */
  async currentBlock() {
    return await this.spanAsync("currentBlock", async () => {
      const response = await this.fetchFn(`${this.chainStateBaseUrl}/${headPath()}`);
      assertEx3(response.ok, () => `Head not found [${response.status}]`);
      const body = await response.json();
      return asSignedHydratedBlockWithHashMeta2(body, true);
    }, this.context);
  }
  /** Returns block hash. */
  async currentBlockHash() {
    const block = await this.currentBlock();
    return block[0]._hash;
  }
  /** Returns block number. */
  async currentBlockNumber() {
    const block = await this.currentBlock();
    return block[0].block;
  }
  /** The published head, or undefined when no head pointer has been published yet. */
  async tryCurrentBlock() {
    const response = await this.fetchFn(`${this.chainStateBaseUrl}/${headPath()}`);
    if (response.status === 404) return void 0;
    assertEx3(response.ok, () => `Head request failed [${response.status}]`);
    const body = await response.json();
    return asSignedHydratedBlockWithHashMeta2(body, true);
  }
  async startHandler() {
    await super.startHandler();
    if (this.headPollIntervalMs === void 0) return;
    await this.pollHead(false);
    this.startHeadPolling();
  }
  async stopHandler() {
    this.stopHeadPolling();
    this._headPollHash = void 0;
    await super.stopHandler();
  }
  async pollHead(emitOnChange) {
    if (this._headPollInProgress) return;
    this._headPollInProgress = true;
    try {
      const block = await this.currentBlock();
      const hash = block[0]._hash;
      if (this._headPollHash === void 0) {
        this._headPollHash = hash;
        return;
      }
      if (hash !== this._headPollHash) {
        this._headPollHash = hash;
        if (emitOnChange) {
          await this.emit("headUpdated", { block });
        }
      }
    } catch (ex) {
      this.logger?.error("Error polling block head", ex);
    } finally {
      this._headPollInProgress = false;
    }
  }
  startHeadPolling() {
    if (this.headPollIntervalMs === void 0) return;
    this.stopHeadPolling();
    this._headPollTimer = setInterval(() => {
      void this.pollHead(true);
    }, this.headPollIntervalMs);
  }
  stopHeadPolling() {
    if (!this._headPollTimer) {
      return;
    }
    clearInterval(this._headPollTimer);
    this._headPollTimer = null;
  }
};
/** Connection kinds supported by this provider. */
__publicField(RestChainStateViewer, "connectionTypes", ["rest"]);
/** Primary moniker used to resolve this provider. */
__publicField(RestChainStateViewer, "defaultMoniker", ChainStateViewerMoniker2);
/** Provider monikers that must resolve before this provider can be created. */
__publicField(RestChainStateViewer, "dependencies", []);
/** Provider monikers implemented by this provider class. */
__publicField(RestChainStateViewer, "monikers", [ChainStateViewerMoniker2]);
/** Read, write, or mixed capability surface exposed by the provider. */
__publicField(RestChainStateViewer, "surface", "node");
RestChainStateViewer = __decorateClass([
  creatableProvider3()
], RestChainStateViewer);

// src/modules/rest-block-viewer/RestFinalizationViewer.ts
import { ChainStateViewerMoniker as ChainStateViewerMoniker3, FinalizationViewerMoniker as FinalizationViewerMoniker2 } from "@xyo-network/xl1-protocol/protocol-lib";
import { AbstractCreatableProvider as AbstractCreatableProvider4, creatableProvider as creatableProvider4 } from "#protocol-sdk";
var RestFinalizationViewer = class extends AbstractCreatableProvider4 {
  /** Moniker implemented by this provider instance. */
  moniker = RestFinalizationViewer.defaultMoniker;
  _chainStateViewer;
  /** Returns the chain id result exposed by this provider. */
  async chainId() {
    const headBlock = await this.headBlock();
    return headBlock.chain;
  }
  /** Creates the provider instance after its dependencies and configuration have resolved. */
  async createHandler() {
    await super.createHandler();
    this._chainStateViewer = await this.locator.getInstance(ChainStateViewerMoniker3);
  }
  /** Returns the head result exposed by this provider. */
  async head() {
    return await this._chainStateViewer.currentBlock();
  }
  /** Returns the head block result exposed by this provider. */
  async headBlock() {
    const head = await this.head();
    return head[0];
  }
  /** Returns the head hash result exposed by this provider. */
  async headHash() {
    const headBlock = await this.headBlock();
    return headBlock._hash;
  }
  /** Returns the head number result exposed by this provider. */
  async headNumber() {
    const headBlock = await this.headBlock();
    return headBlock.block;
  }
};
/** Connection kinds supported by this provider. */
__publicField(RestFinalizationViewer, "connectionTypes", ["rest"]);
/** Primary moniker used to resolve this provider. */
__publicField(RestFinalizationViewer, "defaultMoniker", FinalizationViewerMoniker2);
/** Provider monikers that must resolve before this provider can be created. */
__publicField(RestFinalizationViewer, "dependencies", [ChainStateViewerMoniker3]);
/** Provider monikers implemented by this provider class. */
__publicField(RestFinalizationViewer, "monikers", [FinalizationViewerMoniker2]);
/** Read, write, or mixed capability surface exposed by the provider. */
__publicField(RestFinalizationViewer, "surface", "node");
RestFinalizationViewer = __decorateClass([
  creatableProvider4()
], RestFinalizationViewer);

// src/modules/rest-block-viewer/RestIndexViewer.ts
import { assertEx as assertEx4, isDefined as isDefined4 } from "@ariestools/sdk";
import { IndexViewerMoniker as IndexViewerMoniker2, stepSize } from "@xyo-network/xl1-protocol/protocol-lib";
import { LruCacheMap as LruCacheMap2 } from "#driver-memory";
import {
  AbstractCreatableProvider as AbstractCreatableProvider5,
  balancesMaxStep,
  blocksMaxStep as blocksMaxStep2,
  creatableProvider as creatableProvider5,
  resolveRestConnectionEndpoint as resolveRestConnectionEndpoint4,
  schemasMaxStep,
  transfersMaxStep
} from "#protocol-sdk";
var MAX_STEP_BY_FAMILY = {
  balances: balancesMaxStep,
  blocks: blocksMaxStep2,
  schemas: schemasMaxStep,
  transfers: transfersMaxStep
};
var RestIndexViewer = class extends AbstractCreatableProvider5 {
  /** Moniker implemented by this provider instance. */
  moniker = RestIndexViewer.defaultMoniker;
  summaryCaches = {
    balances: REST_SUMMARY_CACHE_MAX_BY_STEP.balances.map((max) => new LruCacheMap2({ max })),
    blocks: REST_SUMMARY_CACHE_MAX_BY_STEP.blocks.map((max) => new LruCacheMap2({ max })),
    schemas: REST_SUMMARY_CACHE_MAX_BY_STEP.schemas.map((max) => new LruCacheMap2({ max })),
    transfers: REST_SUMMARY_CACHE_MAX_BY_STEP.transfers.map((max) => new LruCacheMap2({ max }))
  };
  /** Returns the index base url result exposed by this provider. */
  get indexBaseUrl() {
    return this.params.indexBaseUrl;
  }
  get fetchFn() {
    return this.params.fetchFn ?? this.context.fetchFn ?? fetch.bind(globalThis);
  }
  /** Returns the params handler result exposed by this provider. */
  static async paramsHandler(params) {
    const moniker = this.defaultMoniker;
    const indexBaseUrl = isDefined4(params.indexBaseUrl) && params.indexBaseUrl !== "" ? params.indexBaseUrl : resolveRestConnectionEndpoint4(
      assertEx4(params.context?.config, () => "Context config is required to resolve REST index endpoint"),
      moniker
    );
    return {
      ...await super.paramsHandler(params),
      fetchFn: params.fetchFn,
      indexBaseUrl: indexBaseUrl.replace(/\/+$/, "")
    };
  }
  /** The published summary for a completed step in the given family, or null when not (yet) published. */
  async stepSummary(family, stepLevel, stepIndex) {
    return await this.spanAsync("stepSummary", async () => {
      assertEx4(Number.isSafeInteger(stepIndex) && stepIndex >= 0, () => "stepIndex must be a non-negative integer");
      stepSize(stepLevel);
      const maxStep = MAX_STEP_BY_FAMILY[family];
      assertEx4(
        stepLevel <= maxStep,
        () => `stepSummary does not support step levels above ${maxStep} for family "${family}" (requested ${stepLevel})`
      );
      const cache = assertEx4(this.summaryCaches[family][stepLevel], () => `Missing ${family} cache for step level ${stepLevel}`);
      const cached = cache.get(stepIndex);
      if (cached !== void 0) return cached;
      const response = await this.fetchFn(`${this.indexBaseUrl}/${indexSummaryPath(family, stepLevel, stepIndex)}`);
      if (response.status === 404) return null;
      assertEx4(response.ok, () => `Request failed [${response.status}] for ${family} step ${stepLevel}/${stepIndex}`);
      const summary = await response.json();
      cache.set(stepIndex, summary);
      return summary;
    }, this.context);
  }
};
/** Connection kinds supported by this provider. */
__publicField(RestIndexViewer, "connectionTypes", ["rest"]);
/** Primary moniker used to resolve this provider. */
__publicField(RestIndexViewer, "defaultMoniker", IndexViewerMoniker2);
/** Provider monikers that must resolve before this provider can be created. */
__publicField(RestIndexViewer, "dependencies", []);
/** Provider monikers implemented by this provider class. */
__publicField(RestIndexViewer, "monikers", [IndexViewerMoniker2]);
RestIndexViewer = __decorateClass([
  creatableProvider5()
], RestIndexViewer);
export {
  CHAIN_LAYOUT_VERSION,
  ChainManifestSchema,
  ChainManifestZod,
  INDEX_LAYOUT_VERSION,
  IndexHeadSchema,
  IndexHeadZod,
  IndexManifestSchema,
  IndexManifestZod,
  RestBlockViewer,
  RestChainContractViewer,
  RestChainStateViewer,
  RestFinalizationViewer,
  RestIndexViewer,
  asChainManifest,
  asIndexHead,
  asIndexManifest,
  blockHashPath,
  blockNumberPath,
  createChainManifest,
  createIndexHead,
  createIndexManifest,
  createIndexManifestFamilies,
  headPath,
  indexHeadPath,
  indexManifestPath,
  indexSummaryPath,
  isChainManifest,
  isIndexHead,
  isIndexManifest,
  manifestPath,
  payloadPath,
  toChainManifest,
  toIndexHead,
  toIndexManifest
};
//# sourceMappingURL=rest-block-viewer.mjs.map
