"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.WireguardUapiConfig = exports.WireguardIniConfig = exports.WireguardConfig = void 0;
var ParseResult = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("@effect/schema/ParseResult"));
var Schema = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("@effect/schema/Schema"));
var Effect = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("effect/Effect"));
var Either = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("effect/Either"));
var Function = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("effect/Function"));
var Number = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("effect/Number"));
var Option = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("effect/Option"));
var Predicate = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("effect/Predicate"));
var ReadonlyArray = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("effect/ReadonlyArray"));
var Tuple = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("effect/Tuple"));
var ini = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("ini"));
var InternetSchemas = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("./InternetSchemas.js"));
var WireguardErrors = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("./WireguardErrors.js"));
var WireguardKey = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("./WireguardKey.js"));
var WireguardPeer = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("./WireguardPeer.js"));
var internal = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("./internal/wireguardConfig.js"));
function _getRequireWildcardCache(e) {
  if ("function" != typeof WeakMap) return null;
  var r = new WeakMap(),
    t = new WeakMap();
  return (_getRequireWildcardCache = function (e) {
    return e ? t : r;
  })(e);
}
function _interopRequireWildcard(e, r) {
  if (!r && e && e.__esModule) return e;
  if (null === e || "object" != typeof e && "function" != typeof e) return {
    default: e
  };
  var t = _getRequireWildcardCache(r);
  if (t && t.has(e)) return t.get(e);
  var n = {
      __proto__: null
    },
    a = Object.defineProperty && Object.getOwnPropertyDescriptor;
  for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) {
    var i = a ? Object.getOwnPropertyDescriptor(e, u) : null;
    i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u];
  }
  return n.default = e, t && t.set(e, n), n;
}
/**
 * Wireguard config schema definitions
 *
 * @since 1.0.0
 */

/**
 * A wireguard configuration.
 *
 * @since 1.0.0
 * @category Datatypes
 */
class WireguardConfig extends Schema.Class("WireguardIniConfig")({
  Address: InternetSchemas.CidrBlockFromString,
  /**
   * The value for this is a decimal-string integer corresponding to the
   * listening port of the interface.
   */
  ListenPort: Schema.union(InternetSchemas.Port, Schema.transformOrFail(Schema.string, InternetSchemas.Port, (str, _options, ast) => Either.fromOption(Number.parse(str), () => new ParseResult.Type(ast, str)), Function.flow(String, Effect.succeed))),
  /**
   * The value for this is a decimal-string integer corresponding to the
   * fwmark of the interface. The value may 0 in the case of a set operation,
   * in which case it indicates that the fwmark should be removed.
   */
  FirewallMark: Schema.optional(Schema.number, {
    nullable: true
  }),
  /**
   * The value for this key should be a lowercase hex-encoded private key of
   * the interface. The value may be an all zero string in the case of a set
   * operation, in which case it indicates that the private key should be
   * removed.
   */
  PrivateKey: WireguardKey.WireguardKey,
  /** List of peers to add. */
  Peers: Schema.optional(Schema.array(WireguardPeer.WireguardPeer), {
    default: () => [],
    nullable: true
  })
}) {
  /**
   * Generates two wireguard configurations, each with the other as a single
   * peer and shares their keys appropriately.
   *
   * @since 1.0.0
   * @category Constructors
   * @param aliceData - The data for the first peer.
   * @param bobData - The data for the second peer.
   */
  static generateP2PConfigs = internal.generateP2PConfigs;
  /**
   * Generates two wireguard configurations, each with the other as a single
   * peer and shares their keys appropriately.
   *
   * TODO: update docs
   *
   * @since 1.0.0
   * @category Constructors
   * @param aliceData - The data for the first peer.
   * @param bobData - The data for the second peer.
   */
  static generateStarConfigs = internal.generateStarConfigs;
  /**
   * Generates a collection of wireguard configurations for a star network
   * with a single central hub node and many peers all connected to it.
   *
   * @since 1.0.0
   * @category Constructors
   * @param hubData - The data for the hub node.
   * @param spokeData - The data for the spoke nodes.
   */
  static generateHubSpokeConfigs = internal.generateHubSpokeConfigs;
  /**
   * Loads a wireguard interface configuration from an INI file.
   *
   * @since 1.0.0
   * @category Constructors
   * @param file - The path to the INI file.
   */
  static fromConfigFile = internal.fromConfigFile;
  /**
   * Writes a wireguard interface configuration to an INI file.
   *
   * @since 1.0.0
   * @category Constructors
   * @param file - The path to the INI file.
   */
  writeToFile = internal.writeToFile(this);
  /**
   * Starts a wireguard tunnel that will continue to run and serve traffic
   * even after the nodejs process exits.
   *
   * @since 1.0.0
   * @category Wireguard
   * @param options - The options for bringing the interface up.
   * @param interfaceObject - The wireguard interface to bring this config up
   *   on, it not specified it will use the next available interface on the
   *   system according to the required naming conventions.
   */
  up = (options, interfaceObject) => internal.up(options, interfaceObject)(this);
  /**
   * Starts a wireguard tunnel that will be gracefully shutdown and stop
   * serving traffic once the scope is closed.
   *
   * @since 1.0.0
   * @category Wireguard
   * @param options - The options for bringing the interface up.
   * @param interfaceObject - The wireguard interface to bring this config up
   *   on, if not specified it will use the next available interface on the
   *   system according to the required naming conventions.
   */
  upScoped = (options, interfaceObject) => internal.upScoped(options, interfaceObject)(this);
}
/**
 * A wireguard configuration encoded in the INI format.
 *
 * TODO: Write an api interface type
 *
 * @since 1.0.0
 * @category Transformations
 * @see {@link WireguardConfig}
 */
exports.WireguardConfig = WireguardConfig;
const WireguardIniConfig = exports.WireguardIniConfig = /*#__PURE__*/Schema.transformOrFail(WireguardConfig, Schema.string,
// Encoding is non-trivial, as we need to handle all the peers individually.
(config, _options, _ast) => Effect.gen(function* (λ) {
  const listenPort = `ListenPort = ${config.ListenPort}\n`;
  const privateKey = `PrivateKey = ${config.PrivateKey}\n`;
  const address = `Address = ${config.Address.ip.ip}/${config.Address.mask}\n`;
  const fwmark = Predicate.isNotUndefined(config.FirewallMark) ? `FirewallMark = ${config.FirewallMark}\n` : "";
  const peersConfig = yield* λ(Function.pipe(config.Peers, ReadonlyArray.map(peer => Schema.encode(WireguardPeer.WireguardPeer)(peer)), ReadonlyArray.map(Effect.flatMap(Schema.decode(WireguardPeer.WireguardIniPeer))), Effect.allWith(), Effect.map(ReadonlyArray.join("\n\n"))));
  return `[Interface]\n${listenPort}${fwmark}${address}${privateKey}\n${peersConfig}`;
}).pipe(Effect.mapError(({
  error
}) => error)),
// Decoding is likewise non-trivial, as we need to parse all the peers from the ini config.
(iniConfig, _options, _ast) => Effect.gen(function* (λ) {
  const sections = iniConfig.split(/(?=\[Peer\])/g);
  const maybeInterfaceSection = ReadonlyArray.findFirst(sections, text => text.startsWith("[Interface]"));
  const interfaceSection = Option.getOrThrowWith(maybeInterfaceSection, () => new WireguardErrors.WireguardError({
    message: "No [Interface] section found"
  }));
  const peerSections = Function.pipe(sections, ReadonlyArray.filter(text => text.startsWith("[Peer]")), ReadonlyArray.map(text => text.replace("[Peer]", "")));
  const parsePeers = yield* λ(Function.pipe(peerSections, ReadonlyArray.map(peer => Schema.encode(WireguardPeer.WireguardIniPeer)(peer)), Effect.allWith()));
  const parseInterface = Function.pipe(interfaceSection, ini.parse, jsonConfig => ({
    ...jsonConfig["Interface"],
    Peers: parsePeers
  }), ({
    ListenPort,
    FirewallMark,
    Address,
    PrivateKey,
    Peers
  }) => ({
    Peers,
    Address,
    PrivateKey,
    ListenPort,
    FirewallMark: Number.parse(FirewallMark || "").pipe(Option.getOrUndefined)
  }), Schema.decode(WireguardConfig));
  return yield* λ(parseInterface);
}).pipe(Effect.mapError(({
  error
}) => error))).annotations({
  identifier: "WireguardIniConfig",
  description: "A wireguard ini configuration"
});
/**
 * A wireguard configuration encoded in the userspace api format.
 *
 * TODO: Write an api interface type
 *
 * @since 1.0.0
 * @category Transformations
 * @see {@link WireguardConfig}
 */
const WireguardUapiConfig = exports.WireguardUapiConfig = /*#__PURE__*/Schema.transformOrFail(WireguardConfig, Schema.tuple(Schema.string, InternetSchemas.CidrBlockFromString),
// Encoding is non trivial, as we need to handle all the peers in individually and
// we need to save the ini config address somewhere.
(config, _options, _ast) => Effect.gen(function* (λ) {
  const fwmark = `fwmark=${config.FirewallMark}\n`;
  const listenPort = `listen_port=${config.ListenPort}\n`;
  const privateKeyHex = Buffer.from(config.PrivateKey, "base64").toString("hex");
  const privateKey = `private_key=${privateKeyHex}\n`;
  const peers = yield* λ(Function.pipe(config.Peers, ReadonlyArray.map(peer => Schema.encode(WireguardPeer.WireguardPeer)(peer)), ReadonlyArray.map(Effect.flatMap(Schema.decode(WireguardPeer.WireguardUapiPeer))), Effect.allWith(), Effect.map(ReadonlyArray.join("\n"))));
  const out = `${fwmark}${listenPort}${privateKey}${peers}\n`;
  const address = yield* λ(Schema.encode(InternetSchemas.CidrBlockFromString)(config.Address));
  return Tuple.make(out, address);
}).pipe(Effect.mapError(({
  error
}) => error)),
// Decoding is non-trivial, as we need to parse all the peers from the uapi config.
([uapiConfig, address], _options, _ast) => Effect.gen(function* (λ) {
  const [interfaceConfig, ...peers] = uapiConfig.split("public_key=");
  const {
    fwmark,
    listen_port,
    private_key
  } = ini.decode(interfaceConfig);
  const peerConfigs = yield* λ(Function.pipe(peers, ReadonlyArray.map(peer => Schema.encode(WireguardPeer.WireguardUapiPeer)(peer)), Effect.allWith()));
  return yield* λ(Schema.decode(WireguardConfig)({
    Address: address,
    FirewallMark: fwmark,
    ListenPort: listen_port,
    PrivateKey: Buffer.from(private_key, "hex").toString("base64"),
    Peers: peerConfigs
  }));
}).pipe(Effect.mapError(({
  error
}) => error))).annotations({
  identifier: "WireguardUapiConfig",
  description: "A wireguard userspace api configuration"
});
//# sourceMappingURL=WireguardConfig.js.map