"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = exports.WireguardInterface = exports.WindowsInterfaceNameRegExp = exports.SupportedPlatforms = exports.SupportedArchitectures = exports.LinuxInterfaceNameRegExp = exports.DarwinInterfaceNameRegExp = void 0;
var Array = _interopRequireWildcard(require("effect/Array"));
var Chunk = _interopRequireWildcard(require("effect/Chunk"));
var Effect = _interopRequireWildcard(require("effect/Effect"));
var Function = _interopRequireWildcard(require("effect/Function"));
var Match = _interopRequireWildcard(require("effect/Match"));
var Number = _interopRequireWildcard(require("effect/Number"));
var Option = _interopRequireWildcard(require("effect/Option"));
var ParseResult = _interopRequireWildcard(require("effect/ParseResult"));
var Predicate = _interopRequireWildcard(require("effect/Predicate"));
var Schema = _interopRequireWildcard(require("effect/Schema"));
var Stream = _interopRequireWildcard(require("effect/Stream"));
var String = _interopRequireWildcard(require("effect/String"));
var assert = _interopRequireWildcard(require("node:assert"));
var os = _interopRequireWildcard(require("node:os"));
var WireguardConfig = _interopRequireWildcard(require("./WireguardConfig.js"));
var WireguardControl = _interopRequireWildcard(require("./WireguardControl.js"));
var WireguardErrors = _interopRequireWildcard(require("./WireguardErrors.js"));
var WireguardPeer = _interopRequireWildcard(require("./WireguardPeer.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 interface helpers
 *
 * @since 1.0.0
 */

/** @internal */
const SupportedArchitectures = exports.SupportedArchitectures = ["x64", "arm64"];
/** @internal */
const SupportedPlatforms = exports.SupportedPlatforms = ["linux", "darwin", "win32"];
/** @internal */
const LinuxInterfaceNameRegExp = exports.LinuxInterfaceNameRegExp = /^wg\d+$/;
/** @internal */
const DarwinInterfaceNameRegExp = exports.DarwinInterfaceNameRegExp = /^utun\d+$/;
/** @internal */
const WindowsInterfaceNameRegExp = exports.WindowsInterfaceNameRegExp = /^eth\d+$/;
/**
 * A wireguard interface name.
 *
 * @since 1.0.0
 * @category Datatypes
 */
class WireguardInterface extends /*#__PURE__*/Schema.Class("WireguardInterface")({
  /**
   * Ensures the interface name matches the platform's interface name regex.
   * These functions need to be fully typed as we are accessing a static
   * method on this same class and otherwise typescript really complains about
   * inference.
   */
  Name: /*#__PURE__*/Schema.transformOrFail(Schema.String, Schema.String, {
    decode: (s, _options, ast) => Function.pipe(WireguardInterface.InterfaceRegExpForPlatform, Effect.mapError(error => new ParseResult.Type(ast, s, error.message)), Effect.flatMap(x => x.test(s) ? Effect.succeed(s) : Effect.fail(new ParseResult.Type(ast, s, `Expected interface name to match ${x}`)))),
    encode: s => Effect.succeed(s)
  })
}) {
  /**
   * @since 1.0.0
   * @category Constructors
   */
  static getNextAvailableInterface = /*#__PURE__*/Effect.gen(function* () {
    // Determine all the used interface indexes
    const regex = yield* WireguardInterface.InterfaceRegExpForPlatform;
    const usedInterfaceIndexes = Function.pipe(os.networkInterfaces(), Object.keys, Array.filter(name => regex.test(name)), Array.map(String.replaceAll(/\D/g, "")), Array.map(Number.parse), Array.filterMap(Function.identity));
    // Find the next available interface index
    const nextAvailableInterfaceIndex = yield* Function.pipe(Stream.iterate(0, x => x + 1), Stream.find(x => !Array.contains(usedInterfaceIndexes, x)), Stream.take(1), Stream.runCollect, Effect.map(Chunk.head), Effect.map(Option.getOrThrow));
    // We know this will be a supported platform now because otherwise
    // the WireguardInterface.InterfaceRegExpForPlatform would have failed
    const platform = Function.unsafeCoerce(process.platform);
    // Construct the next available interface name
    const fromString = Schema.decodeSync(WireguardInterface);
    switch (platform) {
      case "win32":
        return fromString({
          Name: `eth${nextAvailableInterfaceIndex}`
        });
      case "linux":
        return fromString({
          Name: `wg${nextAvailableInterfaceIndex}`
        });
      case "darwin":
        return fromString({
          Name: `utun${nextAvailableInterfaceIndex}`
        });
      default:
        return Function.absurd(platform);
    }
  });
  static InterfaceRegExpForPlatform = /*#__PURE__*/Function.pipe(/*#__PURE__*/Match.value(`${process.arch}:${process.platform}`), /*#__PURE__*/Match.not(/*#__PURE__*/Predicate.some(/*#__PURE__*/Array.map(SupportedArchitectures, arch => String.startsWith(`${arch}:`))), bad => Effect.fail(new WireguardErrors.WireguardError({
    message: `Unsupported architecture ${bad}`
  }))), /*#__PURE__*/Match.when(/*#__PURE__*/String.endsWith(":linux"), () => Effect.succeed(LinuxInterfaceNameRegExp)), /*#__PURE__*/Match.when(/*#__PURE__*/String.endsWith(":win32"), () => Effect.succeed(WindowsInterfaceNameRegExp)), /*#__PURE__*/Match.when(/*#__PURE__*/String.endsWith(":darwin"), () => Effect.succeed(DarwinInterfaceNameRegExp)), /*#__PURE__*/Match.orElse(bad => Effect.fail(new WireguardErrors.WireguardError({
    message: `Unsupported platform ${bad}`
  }))));
  /**
   * @since 1.0.0
   * @category Userspace api
   */
  SocketLocation = /*#__PURE__*/Function.pipe(Match.type(), Match.when("linux", () => `/var/run/wireguard/${this.Name}.sock`), Match.when("darwin", () => `/var/run/wireguard/${this.Name}.sock`), Match.when("win32", () => `\\\\.\\pipe\\ProtectedPrefix\\Administrators\\WireGuard\\${this.Name}`), Match.exhaustive)(/*#__PURE__*/Function.unsafeCoerce(process.platform));
  /**
   * Starts a wireguard tunnel that will be gracefully shutdown and stop
   * serving traffic once the scope is closed.
   *
   * @since 1.0.0
   * @category Wireguard control
   */
  upScoped = config => Effect.flatMap(WireguardControl.WireguardControl, control => control.upScoped(config, 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 control
   */
  up = config => Effect.flatMap(WireguardControl.WireguardControl, control => control.up(config, this));
  /**
   * Stops a previously started wireguard tunnel.
   *
   * @since 1.0.0
   * @category Wireguard control
   */
  down = config => Effect.flatMap(WireguardControl.WireguardControl, control => control.down(config, this));
  /**
   * Adds a peer to this interface.
   *
   * @since 1.0.0
   * @category Wireguard control
   */
  addPeer = peer => Effect.gen(this, function* () {
    const control = yield* WireguardControl.WireguardControl;
    const request = new WireguardConfig.WireguardGetConfigRequest({
      address: "0.0.0.0/0",
      wireguardInterface: this
    });
    // Get the config before adding this peer and ensure this peer is not present
    const configBefore = yield* Effect.request(request, control.getConfigRequestResolver);
    assert.ok(configBefore.Peers.find(p => p.PublicKey === peer.PublicKey) === undefined);
    // Add the peer to the interface
    const peerUApiRequest = WireguardPeer.makeWireguardUApiSetPeerRequest(peer);
    const updateRequest = `set=1\n${peerUApiRequest}`;
    yield* WireguardControl.userspaceContact(this, updateRequest);
    // Get the config after adding this peer and ensure this peer is present
    const configAfter = yield* Effect.request(request, control.getConfigRequestResolver);
    assert.ok(configAfter.Peers.find(p => p.PublicKey === peer.PublicKey) !== undefined);
  });
  /**
   * Removes a peer from this interface.
   *
   * @since 1.0.0
   * @category Wireguard control
   */
  removePeer = peer => Effect.gen(this, function* () {
    const control = yield* WireguardControl.WireguardControl;
    const request = new WireguardConfig.WireguardGetConfigRequest({
      address: "0.0.0.0/0",
      wireguardInterface: this
    });
    // Get the config before removing this peer and ensure this peer is present
    const configBefore = yield* Effect.request(request, control.getConfigRequestResolver);
    assert.ok(configBefore.Peers.find(p => p.PublicKey === peer.PublicKey) !== undefined);
    // Remove the peer from the interface
    const peerUApiRequest = WireguardPeer.makeWireguardUApiSetPeerRequest(peer);
    const updateRequest = `set=1\n${peerUApiRequest}remove=true\n`;
    yield* WireguardControl.userspaceContact(this, updateRequest);
    // Get the config after removing this peer and ensure this peer is not present
    const configAfter = yield* Effect.request(request, control.getConfigRequestResolver);
    assert.ok(configAfter.Peers.find(p => p.PublicKey === peer.PublicKey) === undefined);
  });
}
exports.WireguardInterface = WireguardInterface;
var _default = exports.default = WireguardInterface;
//# sourceMappingURL=WireguardInterface.js.map