"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __knownSymbol = (name, symbol) => (symbol = Symbol[name]) ? symbol : /* @__PURE__ */ Symbol.for("Symbol." + name);
var __typeError = (msg) => {
  throw TypeError(msg);
};
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
var __export = (target, all) => {
  for (var name in all)
    __defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
  if (from && typeof from === "object" || typeof from === "function") {
    for (let key of __getOwnPropNames(from))
      if (!__hasOwnProp.call(to, key) && key !== except)
        __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
  }
  return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var __decoratorStart = (base) => [, , , __create(base?.[__knownSymbol("metadata")] ?? null)];
var __decoratorStrings = ["class", "method", "getter", "setter", "accessor", "field", "value", "get", "set"];
var __expectFn = (fn) => fn !== void 0 && typeof fn !== "function" ? __typeError("Function expected") : fn;
var __decoratorContext = (kind, name, done, metadata, fns) => ({ kind: __decoratorStrings[kind], name, metadata, addInitializer: (fn) => done._ ? __typeError("Already initialized") : fns.push(__expectFn(fn || null)) });
var __decoratorMetadata = (array, target) => __defNormalProp(target, __knownSymbol("metadata"), array[3]);
var __runInitializers = (array, flags, self, value) => {
  for (var i = 0, fns = array[flags >> 1], n = fns && fns.length; i < n; i++) flags & 1 ? fns[i].call(self) : value = fns[i].call(self, value);
  return value;
};
var __decorateElement = (array, flags, name, decorators, target, extra) => {
  var fn, it, done, ctx, access, k = flags & 7, s = !!(flags & 8), p = !!(flags & 16);
  var j = k > 3 ? array.length + 1 : k ? s ? 1 : 2 : 0, key = __decoratorStrings[k + 5];
  var initializers = k > 3 && (array[j - 1] = []), extraInitializers = array[j] || (array[j] = []);
  var desc = k && (!p && !s && (target = target.prototype), k < 5 && (k > 3 || !p) && __getOwnPropDesc(k < 4 ? target : { get [name]() {
    return __privateGet(this, extra);
  }, set [name](x) {
    return __privateSet(this, extra, x);
  } }, name));
  k ? p && k < 4 && __name(extra, (k > 2 ? "set " : k > 1 ? "get " : "") + name) : __name(target, name);
  for (var i = decorators.length - 1; i >= 0; i--) {
    ctx = __decoratorContext(k, name, done = {}, array[3], extraInitializers);
    if (k) {
      ctx.static = s, ctx.private = p, access = ctx.access = { has: p ? (x) => __privateIn(target, x) : (x) => name in x };
      if (k ^ 3) access.get = p ? (x) => (k ^ 1 ? __privateGet : __privateMethod)(x, target, k ^ 4 ? extra : desc.get) : (x) => x[name];
      if (k > 2) access.set = p ? (x, y) => __privateSet(x, target, y, k ^ 4 ? extra : desc.set) : (x, y) => x[name] = y;
    }
    it = (0, decorators[i])(k ? k < 4 ? p ? extra : desc[key] : k > 4 ? void 0 : { get: desc.get, set: desc.set } : target, ctx), done._ = 1;
    if (k ^ 4 || it === void 0) __expectFn(it) && (k > 4 ? initializers.unshift(it) : k ? p ? extra = it : desc[key] = it : target = it);
    else if (typeof it !== "object" || it === null) __typeError("Object expected");
    else __expectFn(fn = it.get) && (desc.get = fn), __expectFn(fn = it.set) && (desc.set = fn), __expectFn(fn = it.init) && initializers.unshift(fn);
  }
  return k || __decoratorMetadata(array, target), desc && __defProp(target, name, desc), p ? k ^ 4 ? extra : desc : target;
};
var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
var __privateIn = (member, obj) => Object(obj) !== obj ? __typeError('Cannot use the "in" operator on this value') : member.has(obj);
var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);

// src/core/toc/index.ts
var index_exports = {};
__export(index_exports, {
  IncludeMode: () => IncludeMode,
  TocService: () => TocService,
  getHooks: () => getHooks
});
module.exports = __toCommonJS(index_exports);

// src/core/toc/hooks.ts
var import_tapable = require("tapable");
var import_utils = require("@diplodoc/cli/lib/utils");
function hooks(name) {
  return {
    /**
     * Called before item data processing (but after data interpolation)
     */
    Item: new import_tapable.AsyncSeriesWaterfallHook(
      ["TocItem", "TocPath"],
      `${name}.Item`
    ),
    /**
     * AsyncSeriesWaterfall HookMap called for each includer name detected in toc.<br>
     * Expects RawToc as result of waterfall.
     */
    Includer: new import_tapable.HookMap(
      (type) => new import_tapable.AsyncSeriesWaterfallHook(
        ["Toc", "options", "from"],
        `${name}.Includer(${type})`
      )
    ),
    Loaded: new import_tapable.AsyncParallelHook(["Toc"], `${name}.Loaded`),
    Resolved: new import_tapable.AsyncParallelHook(["Toc"], `${name}.Resolved`),
    Included: new import_tapable.AsyncParallelHook(
      ["Toc", "IncludeInfo"],
      `${name}.Included`
    ),
    Dump: new import_tapable.AsyncSeriesHook(["vfile"], `${name}.Dump`),
    Filtered: new import_tapable.SyncHook(["path"], `${name}.Filtered`)
  };
}
var [getHooks, withHooks] = (0, import_utils.generateHooksAccess)("Toc", hooks);

// src/core/toc/loader.ts
var import_node_assert = require("node:assert");
var import_node_path = require("node:path");
var import_lodash = require("lodash");
var import_liquid2 = require("@diplodoc/liquid");
var import_utils3 = require("@diplodoc/cli/lib/utils");

// src/core/toc/utils.ts
var import_liquid = require("@diplodoc/liquid");
var import_utils2 = require("@diplodoc/cli/lib/utils");
function isRelative(path) {
  return /^\.{1,2}\//.test(path) || !/^(\w{0,7}:)?\/\//.test(path);
}
function isEntryItem(item) {
  return (0, import_utils2.own)(item, "href") && !(0, import_utils2.isExternalHref)(item.href);
}
function getFirstValuable(items, vars, fallback) {
  if (typeof items === "string") {
    items = [{ text: items, when: true }];
  }
  if (!Array.isArray(items)) {
    items = [];
  }
  for (const item of items) {
    let { when = true } = item;
    delete item.when;
    if (typeof when === "string") {
      when = Boolean((0, import_liquid.evaluate)(when, vars));
    }
    if (when) {
      return item.text;
    }
  }
  return fallback;
}
function isTocLabel(item) {
  return "title" in item;
}
function resolveSingleLabel(item, vars) {
  let { when = true } = item;
  if (typeof when === "string") {
    when = Boolean((0, import_liquid.evaluate)(when, vars));
  }
  if (!when) {
    return void 0;
  }
  if (isTocLabel(item)) {
    const result = { title: item.title };
    if (item.description) {
      result.description = item.description;
    }
    if (item.theme) {
      result.theme = item.theme;
    }
    return result;
  }
  return { title: item.text };
}
function resolveLabel(value, vars) {
  if (typeof value === "string") {
    return { title: value };
  }
  if (!Array.isArray(value)) {
    if (typeof value === "object" && value !== null) {
      return resolveSingleLabel(value, vars);
    }
    return void 0;
  }
  for (const item of value) {
    const result = resolveSingleLabel(item, vars);
    if (result) {
      return result;
    }
  }
  return void 0;
}

// src/core/toc/loader.ts
var IncludeMode = /* @__PURE__ */ ((IncludeMode2) => {
  IncludeMode2["RootMerge"] = "root_merge";
  IncludeMode2["Merge"] = "merge";
  IncludeMode2["Link"] = "link";
  return IncludeMode2;
})(IncludeMode || {});
function isLinkMode(include) {
  return "link" /* Link */ === include.mode;
}
function isMergeMode(include) {
  return "root_merge" /* RootMerge */ === include.mode || "merge" /* Merge */ === include.mode;
}
async function loader(toc) {
  toc = await resolveFields.call(this, toc);
  toc = await resolveNavigation.call(this, toc);
  toc = await validateToc.call(this, toc);
  toc = await resolveItems.call(this, toc);
  toc = await templateFields.call(this, toc);
  toc = await rebaseIncludes.call(this, toc);
  toc = await processItems.call(this, toc);
  toc = await rebaseItems.call(this, toc);
  toc = await normalizeItems.call(this, toc);
  toc = await removeEmptyItems.call(this, toc);
  return toc;
}
async function resolveFields(toc) {
  const titleValue = toc.title;
  if (titleValue) {
    toc.title = getFirstValuable(titleValue, this.vars);
  }
  if (toc.label) {
    toc.label = resolveLabel(toc.label, this.vars);
  }
  return toc;
}
async function resolveNavigation(toc) {
  const { skipMissingVars } = this.options;
  const { conditions } = this.settings;
  if (!conditions || !toc.navigation || typeof toc.navigation !== "object") {
    return toc;
  }
  const navigation = toc.navigation;
  if (!navigation.header) {
    return toc;
  }
  const filterItems = (items) => {
    if (!items || !Array.isArray(items)) {
      return [];
    }
    return items.filter((item) => {
      if (typeof item.when === "string") {
        const result = (0, import_utils3.evaluateWhen)(item.when, this.vars, skipMissingVars);
        delete item.when;
        return result;
      }
      if (item.when === false) {
        delete item.when;
        return false;
      }
      delete item.when;
      return true;
    });
  };
  if (navigation.header.leftItems) {
    navigation.header.leftItems = filterItems(navigation.header.leftItems);
  }
  if (navigation.header.rightItems) {
    navigation.header.rightItems = filterItems(navigation.header.rightItems);
  }
  return toc;
}
function checkTocItems(items, path = "items") {
  const errors = [];
  const CHECK_FIELDS = ["name", "href", "title", "label", "navigation"];
  for (let i = 0; i < items.length; i++) {
    const item = items[i];
    const currentPath = `${path}[${i}]`;
    if (item && typeof item === "object" && !Array.isArray(item)) {
      for (const field of CHECK_FIELDS) {
        if (item[field] !== void 0 && item[field]?.toString() === "[object Object]") {
          errors.push(`${currentPath}.${field}`);
        }
      }
      if (item.items && Array.isArray(item.items)) {
        const nestedErrors = checkTocItems(item.items, `${currentPath}.items`);
        errors.push(...nestedErrors);
      }
    }
  }
  return errors;
}
async function validateToc(toc) {
  if (toc.items && Array.isArray(toc.items)) {
    const errors = checkTocItems(toc.items);
    const path = this.from ? this.from + " -> " + this.path : this.path;
    for (const error of errors) {
      this.logger.error(
        `Invalid toc structure in ${path.toString()} at ${error}: found [object Object] value`
      );
    }
  }
  return toc;
}
async function templateFields(toc) {
  const { conditions, substitutions } = this.settings;
  if (!conditions && !substitutions) {
    return toc;
  }
  const interpolate = (box, field) => {
    const value = box[field];
    if (typeof value !== "string") {
      return;
    }
    box[field] = import_liquid2.liquidSnippet.call(this, value, this.vars);
  };
  const interpolateDeep = (obj) => {
    for (const key of Object.keys(obj)) {
      const value = obj[key];
      if (typeof value === "string") {
        interpolate(obj, key);
      } else if (Array.isArray(value)) {
        for (let i = 0; i < value.length; i++) {
          const item = value[i];
          if (typeof item === "string") {
            value[i] = import_liquid2.liquidSnippet.call(this, item, this.vars);
          } else if (item && typeof item === "object") {
            interpolateDeep(item);
          }
        }
      } else if (value && typeof value === "object") {
        interpolateDeep(value);
      }
    }
  };
  for (const field of ["href", "title", "label", "navigation"]) {
    const value = toc[field];
    if (typeof value === "string") {
      interpolate(toc, field);
    } else if (value && typeof value === "object") {
      interpolateDeep(value);
    }
  }
  toc.items = await this.toc.walkItems(toc.items, (item) => {
    for (const field of ["name", "href"]) {
      interpolate(item, field);
    }
    return item;
  });
  return toc;
}
async function resolveItems(toc) {
  const { removeHiddenItems, skipMissingVars } = this.options;
  const { conditions, substitutions } = this.settings;
  if (!conditions && !substitutions) {
    return toc;
  }
  toc.items = await this.toc.walkItems(toc.items, (item) => {
    let when = true;
    if (conditions) {
      if (typeof item.when === "string") {
        when = (0, import_utils3.evaluateWhen)(item.when, this.vars, skipMissingVars);
      } else {
        when = item.when !== false;
      }
      delete item.when;
    }
    if (removeHiddenItems) {
      when = when && !item.hidden;
      delete item.hidden;
    }
    if (when) {
      return item;
    }
    if ("href" in item && item["href"] === null) {
      this.logger.warn(
        `Empty href property in item with name: ${item.name} in toc: ${this.path}`
      );
    }
    return void 0;
  });
  return toc;
}
async function processItems(toc) {
  const rawToc = toc;
  const isTranslateMode = this.options.mode === "translate";
  toc.items = await this.toc.walkItems(toc.items, async (item) => {
    item = await getHooks(this.toc).Item.promise(item, this.path);
    if (!item || !(0, import_utils3.own)(item, "include") || isTranslateMode && item.include.path === "openapi") {
      return item;
    }
    const { include } = item;
    (0, import_node_assert.ok)(include.path, "Invalid value for include path.");
    let toc2;
    if ((0, import_utils3.own)(include, "includers")) {
      (0, import_node_assert.ok)(
        include.mode === "link" /* Link */ || !include.mode,
        "Invalid mode value for include with includers."
      );
      (0, import_node_assert.ok)(Array.isArray(include.includers), "Includers should be an array.");
      const tocPath = include.path.endsWith("toc.yaml") ? (0, import_utils3.normalizePath)(include.path) : (0, import_utils3.normalizePath)((0, import_node_path.join)(include.path, "toc.yaml"));
      toc2 = { path: tocPath };
      for (const includer of include.includers) {
        const hook = getHooks(this.toc).Includer.get(includer.name);
        (0, import_node_assert.ok)(includer.name, "Includer name should be a string.");
        (0, import_node_assert.ok)(hook, `Includer with name '${includer.name}' is not registered.`);
        const options = {
          ...includer,
          path: tocPath,
          rawToc,
          include
        };
        toc2 = await hook.promise(toc2, options, this.path);
      }
      toc2 = await this.include(tocPath, {
        from: this.path,
        mode: "link" /* Link */,
        content: toc2
      });
    } else {
      const includeInfo = {
        from: this.path,
        mode: include.mode
      };
      if (isMergeMode(includeInfo)) {
        includeInfo.base = this.base || this.path;
      }
      toc2 = await this.include(include.path, includeInfo);
    }
    item = (0, import_lodash.omit)(item, ["include"]);
    if (!toc2) {
      return null;
    }
    if (item.name) {
      item.items = (item.items || []).concat(toc2.items || []);
      return item;
    } else {
      return toc2.items;
    }
  });
  return toc;
}
async function rebaseIncludes(toc) {
  const rebaseIncludes2 = (item) => {
    if (!(0, import_utils3.own)(item, "include")) {
      return item;
    }
    if (!item.include.mode) {
      item.include.mode = (0, import_utils3.own)(item.include, "includers") ? "link" /* Link */ : "root_merge" /* RootMerge */;
    }
    if (item.include.mode === "root_merge" /* RootMerge */) {
      return item;
    }
    if (isLinkMode(this)) {
      item.include.path = (0, import_node_path.join)((0, import_node_path.dirname)(this.path), item.include.path);
    } else {
      item.include.path = (0, import_node_path.join)((0, import_node_path.dirname)(this.base || this.path), item.include.path);
    }
    return item;
  };
  await this.toc.walkItems([toc], rebaseIncludes2);
  return toc;
}
async function rebaseItems(toc) {
  const rebaseHrefs = (item) => {
    if ((0, import_utils3.own)(item, "href") && isRelative(item.href)) {
      const absBase = (0, import_node_path.dirname)(this.from);
      const absPath = (0, import_node_path.join)((0, import_node_path.dirname)(this.base || this.path), item.href);
      item.href = (0, import_node_path.relative)(absBase, absPath);
    }
    return item;
  };
  if (isLinkMode(this)) {
    await this.toc.walkItems([toc], rebaseHrefs);
  }
  return toc;
}
async function normalizeItems(toc) {
  await this.toc.walkEntries([toc], (item) => {
    if (!item.href) {
      delete item["href"];
      return item;
    }
    let href = (0, import_utils3.normalizePath)(item.href);
    if (href.endsWith("/")) {
      href += "index.yaml";
    }
    if (!href.endsWith(".md") && !href.endsWith(".yaml")) {
      href += ".md";
    }
    item.href = href;
    return item;
  });
  return toc;
}
async function removeEmptyItems(toc) {
  const { removeEmptyItems: removeEmptyItems2 } = this.options;
  if (!removeEmptyItems2) {
    return toc;
  }
  const removeEmpty = (item) => {
    const hasChildren = item.items && item.items.length > 0;
    if (!hasChildren && !(0, import_utils3.own)(item, "href")) {
      return null;
    }
    return item;
  };
  toc.items = await this.toc.walkItems(toc.items, removeEmpty);
  return toc;
}

// src/core/toc/TocService.ts
var import_node_path2 = require("node:path");
var import_js_yaml = require("js-yaml");
var import_ts_dedent = require("ts-dedent");
var import_utils5 = require("@diplodoc/cli/lib/utils");
var _include_dec, __dump_dec, _isGenerator_dec, _isEntry_dec, _isToc_dec, _for_dec, _TocService_decorators, _init;
_TocService_decorators = [withHooks], _for_dec = [import_utils5.bounded], _isToc_dec = [import_utils5.bounded], _isEntry_dec = [import_utils5.bounded], _isGenerator_dec = [import_utils5.bounded], __dump_dec = [(0, import_utils5.memoize)("path")], _include_dec = [import_utils5.bounded];
var TocService = class {
  constructor(run, options) {
    __runInitializers(_init, 5, this);
    this.name = "Toc";
    this.relations = new import_utils5.Graph();
    this.run = void 0;
    this.logger = void 0;
    this.config = void 0;
    this.options = void 0;
    this.run = run;
    this.logger = run.logger;
    this.config = run.config;
    this.options = {
      skipMissingVars: false,
      mode: "build",
      pdfDebug: false,
      ...options
    };
  }
  get tocs() {
    return this.relations.overallOrder().filter(this.isToc).map(this.for);
  }
  get entries() {
    const allEntries = this.relations.overallOrder().filter(this.isEntry);
    const allTocPaths = this.relations.overallOrder().filter(this.isToc);
    if (allTocPaths.length === 0) {
      return allEntries;
    }
    const nestingLevels = allTocPaths.map((tocPath) => tocPath.split("/").length);
    const minNestingLevel = Math.min(...nestingLevels);
    const filteredEntries = allEntries.filter((entry) => {
      const tocPaths = this.relations.dependantsOf(entry).filter(
        this.isToc
      );
      return tocPaths.some((tocPath) => {
        const nestingLevel = tocPath.split("/").length;
        const isRootToc = nestingLevel === minNestingLevel;
        const isReferenced = this.relations.dependenciesOf(tocPath).length > 0;
        return isRootToc || isReferenced;
      });
    });
    return filteredEntries;
  }
  get vars() {
    return this.run.vars;
  }
  get meta() {
    return this.run.meta;
  }
  async init(paths) {
    for (const path of paths) {
      await this.load(path);
    }
    return this.tocs.filter((toc) => paths.includes(toc.path));
  }
  async pushAdditionalEntries(path, toc) {
    await this.addEntries(path, toc);
  }
  async dump(file, toc) {
    toc = toc || this.for(file);
    return this._dump(toc.path, toc);
  }
  /**
   * Visits items which will be project entries. Applies actor to each item.
   * Then applies actor to each item in actor result.items.
   * Returns actor results.
   */
  async walkEntries(items, actor) {
    return this.walkItems(items, actor, { accept: isEntryItem });
  }
  /**
   * Visits all passed items. Applies actor to each item.
   * Then applies actor to each item in actor result.items.
   * Returns actor results.
   */
  async walkItems(items, actor, options = { accept: () => true }) {
    return this._walkItems(items, actor, options);
  }
  for(path) {
    const file = (0, import_utils5.normalizePath)(path);
    if (this.isToc(file)) {
      return this.relations.getNodeData(file).data;
    }
    const tocPaths = this.relations.dependantsOf(file).filter(this.isToc);
    if (!tocPaths.length) {
      throw new Error("Error while finding toc dir.");
    }
    if (tocPaths.length === 1) {
      return this.relations.getNodeData(tocPaths[0]).data;
    }
    const fileParts = (0, import_utils5.normalizePath)((0, import_node_path2.join)((0, import_node_path2.dirname)(file), "toc.yaml")).split("/");
    const toc = tocPaths.reduce(
      (result, path2) => {
        const tocParts = path2.split("/");
        let index = 0;
        let score = 0;
        while (tocParts.length > index && fileParts[index] === tocParts[index]) {
          index++;
          score++;
        }
        if (score > result.score) {
          return { score, path: path2 };
        }
        return result;
      },
      { score: 0, path: null }
    );
    if (toc.path === null) {
      throw new Error("Error while finding toc dir.");
    }
    return this.relations.getNodeData(toc.path).data;
  }
  release(path) {
    import_utils5.memoize.release(this._dump, path);
  }
  isToc(path) {
    if (!this.relations.hasNode(path)) {
      return false;
    }
    const data = this.relations.getNodeData(path);
    return data.type === "toc" && data.data;
  }
  isEntry(path) {
    if (!this.relations.hasNode(path)) {
      return false;
    }
    const data = this.relations.getNodeData(path);
    return data.type === "entry";
  }
  isGenerator(path) {
    if (!this.relations.hasNode(path)) {
      return false;
    }
    const data = this.relations.getNodeData(path);
    return data.type === "generator";
  }
  async _dump(file, toc) {
    const vfile = new import_utils5.VFile(file, (0, import_utils5.copyJson)(toc), import_js_yaml.dump);
    await getHooks(this).Dump.promise(vfile);
    return vfile;
  }
  async load(file) {
    if (this.relations.hasNode(file)) {
      return this.relations.getNodeData(file).data;
    }
    this.logger.proc(file);
    const defer = new import_utils5.Defer();
    this.relations.addNode(file, { type: "toc", data: defer.promise });
    defer.promise.then((result) => {
      if (this.relations.hasNode(file)) {
        this.relations.setNodeData(file, { type: "toc", data: result });
      }
    });
    const context = this.loaderContext(file);
    const content = await read(this.run, file);
    if (Array.isArray(content)) {
      throw this.logError(
        `Invalid TOC format in ${file}: root value must be an object, got array. Wrap items into an object with the items field.`
      );
    }
    if (!content || typeof content !== "object") {
      throw this.logError(
        `Invalid TOC format in ${file}: root value must be an object, got ${typeof content}.`
      );
    }
    content.path = file;
    if (this.shouldSkip(content)) {
      defer.resolve(void 0);
      return void 0;
    }
    const toc = await loader.call(context, content);
    await getHooks(this).Loaded.promise(toc);
    if (toc.href || toc.items?.length) {
      await this.addEntries(file, toc);
      await this.restrictAccess(file, toc);
    }
    const pdfStartPages = toc?.pdf?.startPages;
    const pdfEndPages = toc?.pdf?.endPages;
    const { pdfDebug } = this.options;
    if (pdfStartPages && pdfDebug) {
      const tocLikeEntries = pdfStartPages.map((page) => ({ href: page }));
      this.addEntries(file, { items: tocLikeEntries, path: toc.path });
    }
    if (pdfEndPages && pdfDebug) {
      const tocLikeEntries = pdfEndPages.map((page) => ({ href: page }));
      this.addEntries(file, { items: tocLikeEntries, path: toc.path });
    }
    defer.resolve(toc);
    return defer.promise;
  }
  async include(path, include) {
    const file = (0, import_utils5.normalizePath)(path);
    this.relations.addNode(file);
    this.relations.setNodeData(file, { type: "source", data: void 0 });
    this.relations.addNode(include.from);
    if (this.config.watch) {
      this.relations.addDependency(include.from, file);
    }
    this.logger.proc(file);
    const context = this.loaderContext(file, include);
    const content = include.content || await read(this.run, file, include.from);
    if (this.shouldSkip(content)) {
      return void 0;
    }
    if (isMergeMode(include)) {
      const from = (0, import_utils5.normalizePath)((0, import_node_path2.dirname)(file));
      const to = (0, import_utils5.normalizePath)((0, import_node_path2.dirname)(include.base));
      context.vars = this.vars.for(include.base);
      context.path = context.path.replace(from, to);
      context.from = include.from;
      const files = await this.run.copy(
        (0, import_utils5.normalizePath)((0, import_node_path2.join)(this.run.input, from)),
        (0, import_utils5.normalizePath)((0, import_node_path2.join)(this.run.input, to)),
        [(0, import_node_path2.basename)(file), "**/toc.yaml"]
      );
      for (const pair of files) {
        const [from2, to2] = pair.map(
          (path2) => (0, import_utils5.normalizePath)((0, import_node_path2.relative)(this.run.input, path2))
        );
        const { sourcePath, vcsPath } = this.meta.get(from2);
        this.meta.add(to2, { sourcePath: vcsPath || sourcePath || from2 });
        this.logger.copy(pair[0], pair[1]);
      }
    }
    const toc = await loader.call(context, content);
    await getHooks(this).Included.promise(toc, include);
    return toc;
  }
  /**
   * Visits all passed items. Applies actor to each item.
   * Then applies actor to each item in actor result.items.
   * Returns actor results.
   * DFS
   */
  async _walkItems(items, actor, options, context = {}) {
    const { accept } = options;
    if (!items || !items.length) {
      return items;
    }
    const results = [];
    for (const item of items) {
      const itemContext = { ...context };
      const result = accept(item) ? await actor(item, itemContext) : item;
      if (result) {
        results.push(...[].concat(result));
        if ((0, import_utils5.own)(result, "items")) {
          if (!Array.isArray(result.items) && result.items) {
            result.items = [].concat(result.items);
          }
          if (result.items?.length) {
            result.items = await this._walkItems(
              result.items,
              actor,
              options,
              itemContext
            );
          }
        }
      }
    }
    return results;
  }
  shouldSkip(toc) {
    const { ignoreStage } = this.config;
    if (toc.stage && ignoreStage.length && ignoreStage.includes(toc.stage)) {
      return true;
    }
    return false;
  }
  async addEntries(path, toc) {
    await this.walkEntries([toc], (item) => {
      if (typeof item.href !== "string" || !item.href) {
        const itemName = typeof item.name === "string" ? item.name : "<unnamed>";
        throw this.logError(
          `Invalid TOC entry href in ${toc.path}: expected non-empty string, got ${String(item.href)} for item ${itemName}`
        );
      }
      const entryPath = (0, import_utils5.normalizePath)((0, import_node_path2.join)((0, import_node_path2.dirname)(path), item.href));
      this.relations.addNode(entryPath, { type: "entry", data: void 0 });
      this.relations.addDependency(toc.path, entryPath);
      return item;
    });
  }
  async restrictAccess(path, toc) {
    await this.walkItems(
      [toc],
      (item, context) => {
        if ((0, import_utils5.own)(item, "restricted-access")) {
          let itemAccess = [].concat(item["restricted-access"] || []);
          const contextAccess = context["restricted-access"] ?? [];
          if (contextAccess.some(isEqualAccess(itemAccess.sort().join(",")))) {
            itemAccess = [];
          }
          if (itemAccess.length > 0) {
            context["restricted-access"] = [...contextAccess, itemAccess];
          }
        }
        if (context["restricted-access"]?.length && isEntryItem(item)) {
          const href = (0, import_utils5.normalizePath)((0, import_node_path2.join)((0, import_node_path2.dirname)(path), item.href));
          this.meta.add(href, {
            "restricted-access": context["restricted-access"]
          });
        }
        return item;
      }
    );
    return toc;
  }
  loaderContext(path, { from, mode, base } = {}) {
    return {
      path,
      from: from || path,
      mode,
      base,
      vars: this.vars.for(path),
      toc: this,
      logger: this.logger,
      include: this.include,
      settings: {
        conditions: typeof this.config.template.features.conditions === "string" ? this.config.template.features.conditions : Boolean(this.config.template.features.conditions),
        substitutions: this.config.template.features.substitutions,
        cycles: this.config.template.features.cycles ?? true
      },
      options: {
        removeHiddenItems: this.config.removeHiddenTocItems,
        removeEmptyItems: this.config.removeEmptyTocItems,
        skipMissingVars: this.options.skipMissingVars,
        mode: this.options.mode
      }
    };
  }
  logError(message) {
    const error = new Error(message);
    this.logger.error(error.message);
    error.logged = true;
    return error;
  }
};
_init = __decoratorStart(null);
__decorateElement(_init, 1, "for", _for_dec, TocService);
__decorateElement(_init, 1, "isToc", _isToc_dec, TocService);
__decorateElement(_init, 1, "isEntry", _isEntry_dec, TocService);
__decorateElement(_init, 1, "isGenerator", _isGenerator_dec, TocService);
__decorateElement(_init, 1, "_dump", __dump_dec, TocService);
__decorateElement(_init, 1, "include", _include_dec, TocService);
TocService = __decorateElement(_init, 0, "TocService", _TocService_decorators, TocService);
__runInitializers(_init, 1, TocService);
async function read(run, path, from) {
  try {
    const source = (0, import_utils5.normalizePath)((0, import_node_path2.join)(run.input, path));
    return (0, import_js_yaml.load)(await run.read(source) || "{}");
  } catch (error) {
    throw new Error(import_ts_dedent.dedent`
            Unable to resolve ${path}${from ? " from " + from : ""}.
            Original error:
                ${(0, import_utils5.errorMessage)(error)}
        `);
  }
}
function isEqualAccess(match) {
  return function(access) {
    return access.sort().join() === match;
  };
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
  IncludeMode,
  TocService,
  getHooks
});
//# sourceMappingURL=index.js.map
