"use strict";

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.SequenceManagerDO = void 0;
exports.createDOStoreFunctions = createDOStoreFunctions;
var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
function _regenerator() { /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/babel/babel/blob/main/packages/babel-helpers/LICENSE */ var e, t, r = "function" == typeof Symbol ? Symbol : {}, n = r.iterator || "@@iterator", o = r.toStringTag || "@@toStringTag"; function i(r, n, o, i) { var c = n && n.prototype instanceof Generator ? n : Generator, u = Object.create(c.prototype); return _regeneratorDefine2(u, "_invoke", function (r, n, o) { var i, c, u, f = 0, p = o || [], y = !1, G = { p: 0, n: 0, v: e, a: d, f: d.bind(e, 4), d: function d(t, r) { return i = t, c = 0, u = e, G.n = r, a; } }; function d(r, n) { for (c = r, u = n, t = 0; !y && f && !o && t < p.length; t++) { var o, i = p[t], d = G.p, l = i[2]; r > 3 ? (o = l === n) && (u = i[(c = i[4]) ? 5 : (c = 3, 3)], i[4] = i[5] = e) : i[0] <= d && ((o = r < 2 && d < i[1]) ? (c = 0, G.v = n, G.n = i[1]) : d < l && (o = r < 3 || i[0] > n || n > l) && (i[4] = r, i[5] = n, G.n = l, c = 0)); } if (o || r > 1) return a; throw y = !0, n; } return function (o, p, l) { if (f > 1) throw TypeError("Generator is already running"); for (y && 1 === p && d(p, l), c = p, u = l; (t = c < 2 ? e : u) || !y;) { i || (c ? c < 3 ? (c > 1 && (G.n = -1), d(c, u)) : G.n = u : G.v = u); try { if (f = 2, i) { if (c || (o = "next"), t = i[o]) { if (!(t = t.call(i, u))) throw TypeError("iterator result is not an object"); if (!t.done) return t; u = t.value, c < 2 && (c = 0); } else 1 === c && (t = i["return"]) && t.call(i), c < 2 && (u = TypeError("The iterator does not provide a '" + o + "' method"), c = 1); i = e; } else if ((t = (y = G.n < 0) ? u : r.call(n, G)) !== a) break; } catch (t) { i = e, c = 1, u = t; } finally { f = 1; } } return { value: t, done: y }; }; }(r, o, i), !0), u; } var a = {}; function Generator() {} function GeneratorFunction() {} function GeneratorFunctionPrototype() {} t = Object.getPrototypeOf; var c = [][n] ? t(t([][n]())) : (_regeneratorDefine2(t = {}, n, function () { return this; }), t), u = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(c); function f(e) { return Object.setPrototypeOf ? Object.setPrototypeOf(e, GeneratorFunctionPrototype) : (e.__proto__ = GeneratorFunctionPrototype, _regeneratorDefine2(e, o, "GeneratorFunction")), e.prototype = Object.create(u), e; } return GeneratorFunction.prototype = GeneratorFunctionPrototype, _regeneratorDefine2(u, "constructor", GeneratorFunctionPrototype), _regeneratorDefine2(GeneratorFunctionPrototype, "constructor", GeneratorFunction), GeneratorFunction.displayName = "GeneratorFunction", _regeneratorDefine2(GeneratorFunctionPrototype, o, "GeneratorFunction"), _regeneratorDefine2(u), _regeneratorDefine2(u, o, "Generator"), _regeneratorDefine2(u, n, function () { return this; }), _regeneratorDefine2(u, "toString", function () { return "[object Generator]"; }), (_regenerator = function _regenerator() { return { w: i, m: f }; })(); }
function _regeneratorDefine2(e, r, n, t) { var i = Object.defineProperty; try { i({}, "", {}); } catch (e) { i = 0; } _regeneratorDefine2 = function _regeneratorDefine(e, r, n, t) { function o(r, n) { _regeneratorDefine2(e, r, function (e) { return this._invoke(r, n, e); }); } r ? i ? i(e, r, { value: n, enumerable: !t, configurable: !t, writable: !t }) : e[r] = n : (o("next", 0), o("throw", 1), o("return", 2)); }, _regeneratorDefine2(e, r, n, t); }
/**
 * Cloudflare Durable Object for atomic sequence management.
 *
 * This module provides a Durable Object class that coordinates blockchain
 * transaction sequence numbers across multiple Cloudflare Workers or Workflows.
 *
 * ## The Problem
 *
 * When multiple workers send transactions concurrently from the same blockchain
 * account, they compete for sequence numbers:
 *
 * 1. Worker A reads sequence 100 from chain
 * 2. Worker B reads sequence 100 from chain (same time)
 * 3. Worker A broadcasts with sequence 100 - succeeds
 * 4. Worker B broadcasts with sequence 100 - fails (chain expects 101)
 *
 * ## The Solution
 *
 * Use a Durable Object to atomically allocate sequence numbers:
 *
 * 1. All workers request their sequence from a single DO instance
 * 2. DO serializes requests and allocates consecutive sequences
 * 3. DO adds a stagger delay (400ms default) so chain can process transactions in order
 * 4. Workers broadcast with their allocated sequence
 *
 * ## Usage
 *
 * 1. Re-export the class from your worker (required by Cloudflare):
 *
 * ```typescript
 * // Use the /cloudflare subpath to avoid loading crypto at module load time
 * export { SequenceManagerDO } from '@ixo/impactxclient-sdk/cloudflare';
 * ```
 *
 * 2. Configure wrangler.jsonc:
 *
 * ```jsonc
 * {
 *   "durable_objects": {
 *     "bindings": [{
 *       "name": "SEQUENCE_MANAGER",
 *       "class_name": "SequenceManagerDO"
 *     }]
 *   },
 *   "migrations": [{
 *     "tag": "v1",
 *     "new_classes": ["SequenceManagerDO"]
 *   }]
 * }
 * ```
 *
 * 3. Use with createDOStoreFunctions:
 *
 * ```typescript
 * import { createSigningClient, createDOStoreFunctions } from '@ixo/impactxclient-sdk';
 *
 * const doId = env.SEQUENCE_MANAGER.idFromName('global');
 * const doStub = env.SEQUENCE_MANAGER.get(doId);
 * const storageFunctions = createDOStoreFunctions(doStub);
 *
 * const client = await createSigningClient(rpc, wallet, false, options, storageFunctions);
 * await client.signAndBroadcast(address, msgs, 'auto', memo);
 * ```
 *
 * ## Configuration
 *
 * Set `IXO_CLIENT_SEQUENCE_MIN_DELAY_MS` in your wrangler.jsonc vars to configure
 * the stagger delay (default: 400ms):
 *
 * ```jsonc
 * {
 *   "vars": {
 *     "IXO_CLIENT_SEQUENCE_MIN_DELAY_MS": "500"
 *   }
 * }
 * ```
 */
// ============================================================================
// Internal type definitions
// ============================================================================
// Cloudflare DO types (compatible with @cloudflare/workers-types)
// Sequence data stored per chain and account
// Request/response types for the DO endpoints
// ============================================================================
// SequenceManagerDO
// ============================================================================
/**
 * Durable Object for atomic blockchain transaction sequence management.
 *
 * This DO ensures that concurrent workers/workflows get unique, consecutive
 * sequence numbers for blockchain transactions. It implements:
 *
 * - **Atomic increment**: Each request gets a unique sequence number
 * - **Stagger delay**: Ensures chain has time to process transactions in order
 * - **Persistence**: Sequence state survives DO restarts
 * - **Smart sync**: Resets to chain sequence if stale (>7s) or chain is ahead
 *
 * ## How it works
 *
 * When a worker calls the DO:
 * 1. Request enters the mutex queue (serializes concurrent requests)
 * 2. If needed, waits for stagger delay since last allocation
 * 3. Compares chain sequence with stored sequence
 * 4. Returns the appropriate sequence and increments for next caller
 * 5. Persists state to durable storage
 *
 * ## Endpoints
 *
 * - `POST /getSignerData` - Get next sequence number (main endpoint)
 * - `POST /reset` - Reset all sequence data
 * - `GET /status` - Get current state (for debugging)
 *
 * ## Configuration
 *
 * Set `IXO_CLIENT_SEQUENCE_MIN_DELAY_MS` env var to configure stagger delay (default: 400ms).
 *
 * @example
 * ```typescript
 * // In your worker, re-export the class:
 * export { SequenceManagerDO } from '@ixo/impactxclient-sdk';
 *
 * // Then use it via createDOStoreFunctions:
 * const doStub = env.SEQUENCE_MANAGER.get(env.SEQUENCE_MANAGER.idFromName('global'));
 * const storageFunctions = createDOStoreFunctions(doStub);
 * const client = await createSigningClient(rpc, wallet, false, opts, storageFunctions);
 * ```
 */
var SequenceManagerDO = exports.SequenceManagerDO = /*#__PURE__*/function () {
  function SequenceManagerDO(state, env) {
    var _this = this;
    (0, _classCallCheck2["default"])(this, SequenceManagerDO);
    (0, _defineProperty2["default"])(this, "state", void 0);
    (0, _defineProperty2["default"])(this, "data", {});
    (0, _defineProperty2["default"])(this, "mutex", Promise.resolve());
    (0, _defineProperty2["default"])(this, "lastAllocationTime", 0);
    /**
     * Minimum delay between sequence allocations in milliseconds.
     * Configurable via IXO_CLIENT_SEQUENCE_MIN_DELAY_MS env var (default: 400ms).
     *
     * Why is this needed?
     * When Worker A gets sequence N and broadcasts, the chain needs time to
     * add it to the mempool before Worker B tries to simulate with sequence N+1.
     * If Worker B's simulation happens too soon, the chain rejects it.
     */
    (0, _defineProperty2["default"])(this, "minAllocationDelayMs", void 0);
    this.state = state;

    // Read configurable delay from env, with default fallback
    var envDelay = env === null || env === void 0 ? void 0 : env.IXO_CLIENT_SEQUENCE_MIN_DELAY_MS;
    this.minAllocationDelayMs = typeof envDelay === "string" ? parseInt(envDelay, 10) : typeof envDelay === "number" ? envDelay : SequenceManagerDO.DEFAULT_MIN_DELAY_MS;

    // Load persisted data before handling any requests
    this.state.blockConcurrencyWhile(/*#__PURE__*/(0, _asyncToGenerator2["default"])(/*#__PURE__*/_regenerator().m(function _callee() {
      var stored;
      return _regenerator().w(function (_context) {
        while (1) switch (_context.n) {
          case 0:
            _context.n = 1;
            return _this.state.storage.get("sequence-data");
          case 1:
            stored = _context.v;
            if (stored) {
              _this.data = stored;
            }
          case 2:
            return _context.a(2);
        }
      }, _callee);
    })));
  }

  /**
   * Mutex pattern for serializing async operations.
   * Ensures only one request modifies state at a time.
   */
  return (0, _createClass2["default"])(SequenceManagerDO, [{
    key: "withMutex",
    value: (function () {
      var _withMutex = (0, _asyncToGenerator2["default"])(/*#__PURE__*/_regenerator().m(function _callee2(fn) {
        var release, acquired, previousMutex;
        return _regenerator().w(function (_context2) {
          while (1) switch (_context2.p = _context2.n) {
            case 0:
              acquired = new Promise(function (resolve) {
                release = resolve;
              });
              previousMutex = this.mutex;
              this.mutex = acquired;
              _context2.n = 1;
              return previousMutex;
            case 1:
              _context2.p = 1;
              _context2.n = 2;
              return fn();
            case 2:
              return _context2.a(2, _context2.v);
            case 3:
              _context2.p = 3;
              release();
              return _context2.f(3);
            case 4:
              return _context2.a(2);
          }
        }, _callee2, this, [[1,, 3, 4]]);
      }));
      function withMutex(_x) {
        return _withMutex.apply(this, arguments);
      }
      return withMutex;
    }())
  }, {
    key: "fetch",
    value: function () {
      var _fetch = (0, _asyncToGenerator2["default"])(/*#__PURE__*/_regenerator().m(function _callee4(request) {
        var _this2 = this;
        var url, params, result, _t;
        return _regenerator().w(function (_context4) {
          while (1) switch (_context4.p = _context4.n) {
            case 0:
              url = new URL(request.url); // POST /getSignerData - Main endpoint: get next sequence atomically
              if (!(url.pathname === "/getSignerData" && request.method === "POST")) {
                _context4.n = 5;
                break;
              }
              _context4.p = 1;
              _context4.n = 2;
              return request.json();
            case 2:
              params = _context4.v;
              _context4.n = 3;
              return this.withMutex(/*#__PURE__*/(0, _asyncToGenerator2["default"])(/*#__PURE__*/_regenerator().m(function _callee3() {
                var now, timeSinceLast, delay, seq;
                return _regenerator().w(function (_context3) {
                  while (1) switch (_context3.n) {
                    case 0:
                      // Enforce stagger delay so chain can process previous tx
                      now = Date.now();
                      timeSinceLast = now - _this2.lastAllocationTime;
                      if (!(timeSinceLast < _this2.minAllocationDelayMs)) {
                        _context3.n = 1;
                        break;
                      }
                      delay = _this2.minAllocationDelayMs - timeSinceLast;
                      _context3.n = 1;
                      return new Promise(function (resolve) {
                        return setTimeout(resolve, delay);
                      });
                    case 1:
                      seq = _this2.allocateSequence(params);
                      _this2.lastAllocationTime = Date.now();

                      // Persist async (OK since in-memory state is already updated)
                      _this2.persistData()["catch"](console.error);
                      return _context3.a(2, seq);
                  }
                }, _callee3);
              })));
            case 3:
              result = _context4.v;
              return _context4.a(2, new Response(JSON.stringify(result), {
                headers: {
                  "Content-Type": "application/json"
                }
              }));
            case 4:
              _context4.p = 4;
              _t = _context4.v;
              return _context4.a(2, new Response(JSON.stringify({
                error: String(_t)
              }), {
                status: 500,
                headers: {
                  "Content-Type": "application/json"
                }
              }));
            case 5:
              if (!(url.pathname === "/reset" && request.method === "POST")) {
                _context4.n = 7;
                break;
              }
              this.data = {};
              this.lastAllocationTime = 0;
              _context4.n = 6;
              return this.state.storage.put("sequence-data", this.data);
            case 6:
              return _context4.a(2, new Response(JSON.stringify({
                message: "Sequence data reset"
              }), {
                headers: {
                  "Content-Type": "application/json"
                }
              }));
            case 7:
              if (!(url.pathname === "/status")) {
                _context4.n = 8;
                break;
              }
              return _context4.a(2, new Response(JSON.stringify({
                data: this.data,
                minDelayMs: this.minAllocationDelayMs
              }), {
                headers: {
                  "Content-Type": "application/json"
                }
              }));
            case 8:
              return _context4.a(2, new Response("Not found", {
                status: 404
              }));
          }
        }, _callee4, this, [[1, 4]]);
      }));
      function fetch(_x2) {
        return _fetch.apply(this, arguments);
      }
      return fetch;
    }()
    /**
     * Core sequence allocation logic.
     *
     * Algorithm (same as store.ts getSignerData):
     * 1. If no stored data, use chain sequence
     * 2. If data is stale (>7s), use chain sequence (tx may have failed)
     * 3. If chain is ahead, use chain sequence (external tx occurred)
     * 4. Otherwise, use stored sequence (rapid consecutive txs)
     * 5. Increment and store for next caller
     */
  }, {
    key: "allocateSequence",
    value: function allocateSequence(params) {
      var chainId = params.chainId,
        accountNumber = params.accountNumber,
        chainSequence = params.chainSequence;
      var now = new Date();

      // Initialize if needed
      if (!this.data[chainId]) this.data[chainId] = {};
      if (!this.data[chainId][accountNumber]) {
        this.data[chainId][accountNumber] = {
          sequence: chainSequence,
          updated: now.toISOString()
        };
      }
      var accountData = this.data[chainId][accountNumber];
      var timeDiff = now.getTime() - new Date(accountData.updated).getTime();

      // Decide which sequence to use
      var useSequence = timeDiff > 7000 || timeDiff === 0 || chainSequence > accountData.sequence ? chainSequence : accountData.sequence;

      // Update for next caller
      this.data[chainId][accountNumber] = {
        sequence: useSequence + 1,
        updated: now.toISOString()
      };
      return {
        accountNumber: accountNumber,
        sequence: useSequence,
        chainId: chainId
      };
    }
  }, {
    key: "persistData",
    value: function () {
      var _persistData = (0, _asyncToGenerator2["default"])(/*#__PURE__*/_regenerator().m(function _callee5() {
        return _regenerator().w(function (_context5) {
          while (1) switch (_context5.n) {
            case 0:
              _context5.n = 1;
              return this.state.storage.put("sequence-data", this.data);
            case 1:
              return _context5.a(2);
          }
        }, _callee5, this);
      }));
      function persistData() {
        return _persistData.apply(this, arguments);
      }
      return persistData;
    }()
  }]);
}();
/**
 * Type for objects that can be used as a Durable Object stub.
 * Any object with a fetch method that returns a Response works.
 */
/** Default stagger delay if not configured via env */
(0, _defineProperty2["default"])(SequenceManagerDO, "DEFAULT_MIN_DELAY_MS", 400);
/**
 * Creates LocalStoreFunctions that use a Durable Object for atomic sequence coordination.
 * The DO must implement a POST /getSignerData endpoint that accepts
 * { chainId, accountNumber, chainSequence } and returns { accountNumber, sequence, chainId }.
 *
 * @param doStub - A Durable Object stub (or any object with a compatible fetch method)
 * @returns LocalStoreFunctions with getAndIncrementSequence implemented
 *
 * @example
 * ```typescript
 * const doId = env.SEQUENCE_MANAGER.idFromName('global');
 * const doStub = env.SEQUENCE_MANAGER.get(doId);
 * const storageFunctions = createDOStoreFunctions(doStub);
 * const client = await createSigningClient(rpc, wallet, false, options, storageFunctions);
 * ```
 */
function createDOStoreFunctions(doStub) {
  return {
    // These are not used when getAndIncrementSequence is provided,
    // but we provide no-op implementations for interface completeness
    getLocalData: function () {
      var _getLocalData = (0, _asyncToGenerator2["default"])(/*#__PURE__*/_regenerator().m(function _callee6() {
        return _regenerator().w(function (_context6) {
          while (1) switch (_context6.n) {
            case 0:
              return _context6.a(2, {});
          }
        }, _callee6);
      }));
      function getLocalData() {
        return _getLocalData.apply(this, arguments);
      }
      return getLocalData;
    }(),
    setLocalData: function () {
      var _setLocalData = (0, _asyncToGenerator2["default"])(/*#__PURE__*/_regenerator().m(function _callee7() {
        return _regenerator().w(function (_context7) {
          while (1) switch (_context7.n) {
            case 0:
              return _context7.a(2);
          }
        }, _callee7);
      }));
      function setLocalData() {
        return _setLocalData.apply(this, arguments);
      }
      return setLocalData;
    }(),
    getAndIncrementSequence: function () {
      var _getAndIncrementSequence = (0, _asyncToGenerator2["default"])(/*#__PURE__*/_regenerator().m(function _callee8(params) {
        var response, error;
        return _regenerator().w(function (_context8) {
          while (1) switch (_context8.n) {
            case 0:
              _context8.n = 1;
              return doStub.fetch(new Request("http://do/getSignerData", {
                method: "POST",
                headers: {
                  "Content-Type": "application/json"
                },
                body: JSON.stringify(params)
              }));
            case 1:
              response = _context8.v;
              if (response.ok) {
                _context8.n = 3;
                break;
              }
              _context8.n = 2;
              return response.text();
            case 2:
              error = _context8.v;
              throw new Error("DO getSignerData failed: ".concat(error));
            case 3:
              return _context8.a(2, response.json());
          }
        }, _callee8);
      }));
      function getAndIncrementSequence(_x3) {
        return _getAndIncrementSequence.apply(this, arguments);
      }
      return getAndIncrementSequence;
    }()
  };
}