text: import { Alias } from '../nodes/Alias.js';
  import { isEmptyPath, collectionFromPath } from '../nodes/Collection.js';
  import { NODE_TYPE, DOC, isNode, isCollection, isScalar } from '../nodes/Node.js';
  import { Pair } from '../nodes/Pair.js';
  import { toJS } from '../nodes/toJS.js';
  import { Schema } from '../schema/Schema.js';
  import { stringify } from '../stringify/stringify.js';
  import { stringifyDocument } from '../stringify/stringifyDocument.js';
  import { anchorNames, findNewAnchor, createNodeAnchors } from './anchors.js';
  import { applyReviver } from './applyReviver.js';
  import { createNode } from './createNode.js';
  import { Directives } from './directives.js';
  
  class Document {
      constructor(value, replacer, options) {
          /** A comment before this Document */
          this.commentBefore = null;
          /** A comment immediately after this Document */
          this.comment = null;
          /** Errors encountered during parsing. */
          this.errors = [];
          /** Warnings encountered during parsing. */
          this.warnings = [];
          Object.defineProperty(this, NODE_TYPE, { value: DOC });
          let _replacer = null;
          if (typeof replacer === 'function' || Array.isArray(replacer)) {
              _replacer = replacer;
          }
          else if (options === undefined && replacer) {
              options = replacer;
              replacer = undefined;
          }
          const opt = Object.assign({
              intAsBigInt: false,
              keepSourceTokens: false,
              logLevel: 'warn',
              prettyErrors: true,
              strict: true,
              uniqueKeys: true,
              version: '1.2'
          }, options);
          this.options = opt;
          let { version } = opt;
          if (options === null || options === void 0 ? void 0 : options.directives) {
              this.directives = options.directives.atDocument();
              if (this.directives.yaml.explicit)
                  version = this.directives.yaml.version;
          }
          else
              this.directives = new Directives({ version });
          this.setSchema(version, options);
          if (value === undefined)
              this.contents = null;
          else {
              this.contents = this.createNode(value, _replacer, options);
          }
      }
      /**
       * Create a deep copy of this Document and its contents.
       *
       * Custom Node values that inherit from `Object` still refer to their original instances.
       */
      clone() {
          const copy = Object.create(Document.prototype, {
              [NODE_TYPE]: { value: DOC }
          });
          copy.commentBefore = this.commentBefore;
          copy.comment = this.comment;
          copy.errors = this.errors.slice();
          copy.warnings = this.warnings.slice();
          copy.options = Object.assign({}, this.options);
          if (this.directives)
              copy.directives = this.directives.clone();
          copy.schema = this.schema.clone();
          copy.contents = isNode(this.contents)
              ? this.contents.clone(copy.schema)
              : this.contents;
          if (this.range)
              copy.range = this.range.slice();
          return copy;
      }
      /** Adds a value to the document. */
      add(value) {
          if (assertCollection(this.contents))
              this.contents.add(value);
      }
      /** Adds a value to the document. */
      addIn(path, value) {
          if (assertCollection(this.contents))
              this.contents.addIn(path, value);
      }
      /**
       * Create a new `Alias` node, ensuring that the target `node` has the required anchor.
       *
       * If `node` already has an anchor, `name` is ignored.
       * Otherwise, the `node.anchor` value will be set to `name`,
       * or if an anchor with that name is already present in the document,
       * `name` will be used as a prefix for a new unique anchor.
       * If `name` is undefined, the generated anchor will use 'a' as a prefix.
       */
      createAlias(node, name) {
          if (!node.anchor) {
              const prev = anchorNames(this);
              node.anchor =
                  // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
                  !name || prev.has(name) ? findNewAnchor(name || 'a', prev) : name;
          }
          return new Alias(node.anchor);
      }
      createNode(value, replacer, options) {
          let _replacer = undefined;
          if (typeof replacer === 'function') {
              value = replacer.call({ '': value }, '', value);
              _replacer = replacer;
          }
          else if (Array.isArray(replacer)) {
              const keyToStr = (v) => typeof v === 'number' || v instanceof String || v instanceof Number;
              const asStr = replacer.filter(keyToStr).map(String);
              if (asStr.length > 0)
                  replacer = replacer.concat(asStr);
              _replacer = replacer;
          }
          else if (options === undefined && replacer) {
              options = replacer;
              replacer = undefined;
          }
          const { aliasDuplicateObjects, anchorPrefix, flow, keepUndefined, onTagObj, tag } = options !== null && options !== void 0 ? options : {};
          const { onAnchor, setAnchors, sourceObjects } = createNodeAnchors(this, 
          // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
          anchorPrefix || 'a');
          const ctx = {
              aliasDuplicateObjects: aliasDuplicateObjects !== null && aliasDuplicateObjects !== void 0 ? aliasDuplicateObjects : true,
              keepUndefined: keepUndefined !== null && keepUndefined !== void 0 ? keepUndefined : false,
              onAnchor,
              onTagObj,
              replacer: _replacer,
              schema: this.schema,
              sourceObjects
          };
          const node = createNode(value, tag, ctx);
          if (flow && isCollection(node))
              node.flow = true;
          setAnchors();
          return node;
      }
      /**
       * Convert a key and a value into a `Pair` using the current schema,
       * recursively wrapping all values as `Scalar` or `Collection` nodes.
       */
      createPair(key, value, options = {}) {
          const k = this.createNode(key, null, options);
          const v = this.createNode(value, null, options);
          return new Pair(k, v);
      }
      /**
       * Removes a value from the document.
       * @returns `true` if the item was found and removed.
       */
      delete(key) {
          return assertCollection(this.contents) ? this.contents.delete(key) : false;
      }
      /**
       * Removes a value from the document.
       * @returns `true` if the item was found and removed.
       */
      deleteIn(path) {
          if (isEmptyPath(path)) {
              if (this.contents == null)
                  return false;
              this.contents = null;
              return true;
          }
          return assertCollection(this.contents)
              ? this.contents.deleteIn(path)
              : false;
      }
      /**
       * Returns item at `key`, or `undefined` if not found. By default unwraps
       * scalar values from their surrounding node; to disable set `keepScalar` to
       * `true` (collections are always returned intact).
       */
      get(key, keepScalar) {
          return isCollection(this.contents)
              ? this.contents.get(key, keepScalar)
              : undefined;
      }
      /**
       * Returns item at `path`, or `undefined` if not found. By default unwraps
       * scalar values from their surrounding node; to disable set `keepScalar` to
       * `true` (collections are always returned intact).
       */
      getIn(path, keepScalar) {
          if (isEmptyPath(path))
              return !keepScalar && isScalar(this.contents)
                  ? this.contents.value
                  : this.contents;
          return isCollection(this.contents)
              ? this.contents.getIn(path, keepScalar)
              : undefined;
      }
      /**
       * Checks if the document includes a value with the key `key`.
       */
      has(key) {
          return isCollection(this.contents) ? this.contents.has(key) : false;
      }
      /**
       * Checks if the document includes a value at `path`.
       */
      hasIn(path) {
          if (isEmptyPath(path))
              return this.contents !== undefined;
          return isCollection(this.contents) ? this.contents.hasIn(path) : false;
      }
      /**
       * Sets a value in this document. For `!!set`, `value` needs to be a
       * boolean to add/remove the item from the set.
       */
      set(key, value) {
          if (this.contents == null) {
              this.contents = collectionFromPath(this.schema, [key], value);
          }
          else if (assertCollection(this.contents)) {
              this.contents.set(key, value);
          }
      }
      /**
       * Sets a value in this document. For `!!set`, `value` needs to be a
       * boolean to add/remove the item from the set.
       */
      setIn(path, value) {
          if (isEmptyPath(path))
              this.contents = value;
          else if (this.contents == null) {
              this.contents = collectionFromPath(this.schema, Array.from(path), value);
          }
          else if (assertCollection(this.contents)) {
              this.contents.setIn(path, value);
          }
      }
      /**
       * Change the YAML version and schema used by the document.
       * A `null` version disables support for directives, explicit tags, anchors, and aliases.
       * It also requires the `schema` option to be given as a `Schema` instance value.
       *
       * Overrides all previously set schema options.
       */
      setSchema(version, options = {}) {
          if (typeof version === 'number')
              version = String(version);
          let opt;
          switch (version) {
              case '1.1':
                  if (this.directives)
                      this.directives.yaml.version = '1.1';
                  else
                      this.directives = new Directives({ version: '1.1' });
                  opt = { merge: true, resolveKnownTags: false, schema: 'yaml-1.1' };
                  break;
              case '1.2':
              case 'next':
                  if (this.directives)
                      this.directives.yaml.version = version;
                  else
                      this.directives = new Directives({ version });
                  opt = { merge: false, resolveKnownTags: true, schema: 'core' };
                  break;
              case null:
                  if (this.directives)
                      delete this.directives;
                  opt = null;
                  break;
              default: {
                  const sv = JSON.stringify(version);
                  throw new Error(`Expected '1.1', '1.2' or null as first argument, but found: ${sv}`);
              }
          }
          // Not using `instanceof Schema` to allow for duck typing
          if (options.schema instanceof Object)
              this.schema = options.schema;
          else if (opt)
              this.schema = new Schema(Object.assign(opt, options));
          else
              throw new Error(`With a null YAML version, the { schema: Schema } option is required`);
      }
      // json & jsonArg are only used from toJSON()
      toJS({ json, jsonArg, mapAsMap, maxAliasCount, onAnchor, reviver } = {}) {
          const ctx = {
              anchors: new Map(),
              doc: this,
              keep: !json,
              mapAsMap: mapAsMap === true,
              mapKeyWarned: false,
              maxAliasCount: typeof maxAliasCount === 'number' ? maxAliasCount : 100,
              stringify
          };
          const res = toJS(this.contents, jsonArg !== null && jsonArg !== void 0 ? jsonArg : '', ctx);
          if (typeof onAnchor === 'function')
              for (const { count, res } of ctx.anchors.values())
                  onAnchor(res, count);
          return typeof reviver === 'function'
              ? applyReviver(reviver, { '': res }, '', res)
              : res;
      }
      /**
       * A JSON representation of the document `contents`.
       *
       * @param jsonArg Used by `JSON.stringify` to indicate the array index or
       *   property name.
       */
      toJSON(jsonArg, onAnchor) {
          return this.toJS({ json: true, jsonArg, mapAsMap: false, onAnchor });
      }
      /** A YAML representation of the document. */
      toString(options = {}) {
          if (this.errors.length > 0)
              throw new Error('Document with errors cannot be stringified');
          if ('indent' in options &&
              (!Number.isInteger(options.indent) || Number(options.indent) <= 0)) {
              const s = JSON.stringify(options.indent);
              throw new Error(`"indent" option must be a positive integer, not ${s}`);
          }
          return stringifyDocument(this, options);
      }
  }
  function assertCollection(contents) {
      if (isCollection(contents))
          return true;
      throw new Error('Expected a YAML collection as document contents');
  }
  
  export { Document };
  