@fiber-pay/runtime 0.1.0-rc.1 → 0.1.0-rc.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -9,6 +9,31 @@ fiber-pay runtime start --daemon --json
9
9
  fiber-pay runtime status --json
10
10
  ```
11
11
 
12
+ ## Single-instance low-CPU defaults
13
+
14
+ Runtime defaults are tuned for lower idle CPU on one node:
15
+
16
+ - `channelPollIntervalMs=5000`
17
+ - `invoicePollIntervalMs=3000`
18
+ - `paymentPollIntervalMs=2000`
19
+ - `peerPollIntervalMs=15000`
20
+ - `healthPollIntervalMs=10000`
21
+ - `includeClosedChannels=false`
22
+ - `jobs.schedulerIntervalMs=1000`
23
+
24
+ If your machine is still busy when idle, start with slower polling explicitly:
25
+
26
+ ```bash
27
+ fiber-pay runtime start \
28
+ --channel-poll-ms 8000 \
29
+ --invoice-poll-ms 5000 \
30
+ --payment-poll-ms 3000 \
31
+ --peer-poll-ms 20000 \
32
+ --health-poll-ms 15000 \
33
+ --include-closed false \
34
+ --json
35
+ ```
36
+
12
37
  ## Manual payment flow (runtime jobs)
13
38
 
14
39
  前置:`jq`、`curl`、两边 profile 已有资金(示例 `rt-a` / `rt-b`)。
package/dist/index.js CHANGED
@@ -375,12 +375,12 @@ function computeRetryDelay(retryCount, policy) {
375
375
  // src/config.ts
376
376
  var defaultRuntimeConfig = {
377
377
  fiberRpcUrl: "http://127.0.0.1:8227",
378
- channelPollIntervalMs: 3e3,
379
- invoicePollIntervalMs: 2e3,
380
- paymentPollIntervalMs: 1e3,
381
- peerPollIntervalMs: 1e4,
382
- healthPollIntervalMs: 5e3,
383
- includeClosedChannels: true,
378
+ channelPollIntervalMs: 5e3,
379
+ invoicePollIntervalMs: 3e3,
380
+ paymentPollIntervalMs: 2e3,
381
+ peerPollIntervalMs: 15e3,
382
+ healthPollIntervalMs: 1e4,
383
+ includeClosedChannels: false,
384
384
  completedItemTtlSeconds: 86400,
385
385
  requestTimeoutMs: 1e4,
386
386
  alerts: [{ type: "stdout" }],
@@ -397,28 +397,41 @@ var defaultRuntimeConfig = {
397
397
  enabled: true,
398
398
  dbPath: resolve(process.cwd(), ".fiber-pay-jobs.db"),
399
399
  maxConcurrentJobs: 5,
400
- schedulerIntervalMs: 500,
400
+ schedulerIntervalMs: 1e3,
401
401
  retryPolicy: defaultPaymentRetryPolicy
402
402
  }
403
403
  };
404
404
  function createRuntimeConfig(input = {}) {
405
405
  const config = {
406
- ...defaultRuntimeConfig,
407
- ...input,
406
+ fiberRpcUrl: input.fiberRpcUrl ?? defaultRuntimeConfig.fiberRpcUrl,
407
+ channelPollIntervalMs: input.channelPollIntervalMs ?? defaultRuntimeConfig.channelPollIntervalMs,
408
+ invoicePollIntervalMs: input.invoicePollIntervalMs ?? defaultRuntimeConfig.invoicePollIntervalMs,
409
+ paymentPollIntervalMs: input.paymentPollIntervalMs ?? defaultRuntimeConfig.paymentPollIntervalMs,
410
+ peerPollIntervalMs: input.peerPollIntervalMs ?? defaultRuntimeConfig.peerPollIntervalMs,
411
+ healthPollIntervalMs: input.healthPollIntervalMs ?? defaultRuntimeConfig.healthPollIntervalMs,
412
+ includeClosedChannels: input.includeClosedChannels ?? defaultRuntimeConfig.includeClosedChannels,
413
+ completedItemTtlSeconds: input.completedItemTtlSeconds ?? defaultRuntimeConfig.completedItemTtlSeconds,
414
+ requestTimeoutMs: input.requestTimeoutMs ?? defaultRuntimeConfig.requestTimeoutMs,
408
415
  proxy: {
409
- ...defaultRuntimeConfig.proxy,
410
- ...input.proxy
416
+ enabled: input.proxy?.enabled ?? defaultRuntimeConfig.proxy.enabled,
417
+ listen: input.proxy?.listen ?? defaultRuntimeConfig.proxy.listen
411
418
  },
412
419
  storage: {
413
- ...defaultRuntimeConfig.storage,
414
- ...input.storage
420
+ stateFilePath: input.storage?.stateFilePath ?? defaultRuntimeConfig.storage.stateFilePath,
421
+ flushIntervalMs: input.storage?.flushIntervalMs ?? defaultRuntimeConfig.storage.flushIntervalMs,
422
+ maxAlertHistory: input.storage?.maxAlertHistory ?? defaultRuntimeConfig.storage.maxAlertHistory
415
423
  },
416
424
  jobs: {
417
- ...defaultRuntimeConfig.jobs,
418
- ...input.jobs,
425
+ enabled: input.jobs?.enabled ?? defaultRuntimeConfig.jobs.enabled,
426
+ dbPath: input.jobs?.dbPath ?? defaultRuntimeConfig.jobs.dbPath,
427
+ maxConcurrentJobs: input.jobs?.maxConcurrentJobs ?? defaultRuntimeConfig.jobs.maxConcurrentJobs,
428
+ schedulerIntervalMs: input.jobs?.schedulerIntervalMs ?? defaultRuntimeConfig.jobs.schedulerIntervalMs,
419
429
  retryPolicy: {
420
- ...defaultRuntimeConfig.jobs.retryPolicy,
421
- ...input.jobs?.retryPolicy
430
+ maxRetries: input.jobs?.retryPolicy?.maxRetries ?? defaultRuntimeConfig.jobs.retryPolicy.maxRetries,
431
+ baseDelayMs: input.jobs?.retryPolicy?.baseDelayMs ?? defaultRuntimeConfig.jobs.retryPolicy.baseDelayMs,
432
+ maxDelayMs: input.jobs?.retryPolicy?.maxDelayMs ?? defaultRuntimeConfig.jobs.retryPolicy.maxDelayMs,
433
+ backoffMultiplier: input.jobs?.retryPolicy?.backoffMultiplier ?? defaultRuntimeConfig.jobs.retryPolicy.backoffMultiplier,
434
+ jitterMs: input.jobs?.retryPolicy?.jitterMs ?? defaultRuntimeConfig.jobs.retryPolicy.jitterMs
422
435
  }
423
436
  },
424
437
  alerts: input.alerts ?? defaultRuntimeConfig.alerts
@@ -757,6 +770,9 @@ var InvoiceTracker = class extends BaseMonitor {
757
770
  async poll() {
758
771
  const tracked = this.store.listTrackedInvoices();
759
772
  for (const invoice of tracked) {
773
+ if (isTerminalInvoiceStatusString(invoice.status)) {
774
+ continue;
775
+ }
760
776
  try {
761
777
  const next = await this.client.getInvoice({ payment_hash: invoice.paymentHash });
762
778
  const previousStatus = invoice.status;
@@ -813,6 +829,9 @@ var InvoiceTracker = class extends BaseMonitor {
813
829
  this.store.pruneCompleted(this.config.completedItemTtlSeconds * 1e3);
814
830
  }
815
831
  };
832
+ function isTerminalInvoiceStatusString(status) {
833
+ return status === "Cancelled" || status === "Expired" || status === "Paid";
834
+ }
816
835
 
817
836
  // src/monitors/payment-tracker.ts
818
837
  var PaymentTracker = class extends BaseMonitor {
@@ -833,6 +852,9 @@ var PaymentTracker = class extends BaseMonitor {
833
852
  async poll() {
834
853
  const tracked = this.store.listTrackedPayments();
835
854
  for (const payment of tracked) {
855
+ if (isTerminalPaymentStatus(payment.status)) {
856
+ continue;
857
+ }
836
858
  try {
837
859
  const next = await this.client.getPayment({ payment_hash: payment.paymentHash });
838
860
  const previousStatus = payment.status;
@@ -876,6 +898,9 @@ var PaymentTracker = class extends BaseMonitor {
876
898
  this.store.pruneCompleted(this.config.completedItemTtlSeconds * 1e3);
877
899
  }
878
900
  };
901
+ function isTerminalPaymentStatus(status) {
902
+ return status === "Success" || status === "Failed";
903
+ }
879
904
 
880
905
  // src/diff/peer-diff.ts
881
906
  function diffPeers(previous, current) {
@@ -1332,6 +1357,7 @@ var RpcMonitorProxy = class {
1332
1357
  if (this.server) {
1333
1358
  return;
1334
1359
  }
1360
+ assertNoProxySelfLoop(this.config.listen, this.config.targetUrl);
1335
1361
  this.server = http2.createServer((req, res) => {
1336
1362
  void this.handleRequest(req, res);
1337
1363
  });
@@ -1422,6 +1448,29 @@ var RpcMonitorProxy = class {
1422
1448
  res.end(responseText);
1423
1449
  }
1424
1450
  };
1451
+ function assertNoProxySelfLoop(listen, targetUrl) {
1452
+ const { host, port } = parseListenAddress(listen);
1453
+ let parsed;
1454
+ try {
1455
+ parsed = new URL(targetUrl);
1456
+ } catch {
1457
+ throw new Error(`Invalid proxy targetUrl: ${targetUrl}`);
1458
+ }
1459
+ const targetHost = normalizeHost(parsed.hostname);
1460
+ const listenHost = normalizeHost(host);
1461
+ const targetPort = parsed.port || (parsed.protocol === "https:" ? "443" : "80");
1462
+ if (targetHost === listenHost && targetPort === String(port)) {
1463
+ throw new Error(
1464
+ `Invalid proxy configuration: targetUrl (${targetUrl}) points to proxy listen address (${listen})`
1465
+ );
1466
+ }
1467
+ }
1468
+ function normalizeHost(host) {
1469
+ if (host === "localhost" || host === "::1") {
1470
+ return "127.0.0.1";
1471
+ }
1472
+ return host;
1473
+ }
1425
1474
 
1426
1475
  // src/storage/memory-store.ts
1427
1476
  import { mkdir, readFile, writeFile } from "fs/promises";
@@ -1566,13 +1615,13 @@ var MemoryStore = class {
1566
1615
  ...existing,
1567
1616
  status,
1568
1617
  updatedAt: now,
1569
- completedAt: isTerminalPaymentStatus(status) ? existing.completedAt ?? now : void 0
1618
+ completedAt: isTerminalPaymentStatus2(status) ? existing.completedAt ?? now : void 0
1570
1619
  } : {
1571
1620
  paymentHash: hash,
1572
1621
  status,
1573
1622
  trackedAt: now,
1574
1623
  updatedAt: now,
1575
- completedAt: isTerminalPaymentStatus(status) ? now : void 0
1624
+ completedAt: isTerminalPaymentStatus2(status) ? now : void 0
1576
1625
  };
1577
1626
  this.trackedPayments.set(hash, next);
1578
1627
  }
@@ -1607,7 +1656,7 @@ var MemoryStore = class {
1607
1656
  function isTerminalInvoiceStatus(status) {
1608
1657
  return status === "Paid" || status === "Cancelled" || status === "Expired";
1609
1658
  }
1610
- function isTerminalPaymentStatus(status) {
1659
+ function isTerminalPaymentStatus2(status) {
1611
1660
  return status === "Success" || status === "Failed";
1612
1661
  }
1613
1662
 
@@ -2237,6 +2286,15 @@ async function* runInvoiceJob(job, rpc, policy, signal) {
2237
2286
  yield current;
2238
2287
  }
2239
2288
  if (current.state === "waiting_retry") {
2289
+ const delay = current.nextRetryAt ? Math.max(0, current.nextRetryAt - Date.now()) : 0;
2290
+ if (delay > 0) {
2291
+ await sleep(delay, signal);
2292
+ if (signal.aborted) {
2293
+ current = transitionJobState(current, invoiceStateMachine, "cancel");
2294
+ yield current;
2295
+ return;
2296
+ }
2297
+ }
2240
2298
  current = transitionJobState(current, invoiceStateMachine, "retry_delay_elapsed", {
2241
2299
  patch: { nextRetryAt: void 0 }
2242
2300
  });
@@ -2443,6 +2501,15 @@ async function* runChannelJob(job, rpc, policy, signal) {
2443
2501
  yield current;
2444
2502
  }
2445
2503
  if (current.state === "waiting_retry") {
2504
+ const delay = current.nextRetryAt ? Math.max(0, current.nextRetryAt - Date.now()) : 0;
2505
+ if (delay > 0) {
2506
+ await sleep(delay, signal);
2507
+ if (signal.aborted) {
2508
+ current = transitionJobState(current, channelStateMachine, "cancel");
2509
+ yield current;
2510
+ return;
2511
+ }
2512
+ }
2446
2513
  current = transitionJobState(current, channelStateMachine, "retry_delay_elapsed", {
2447
2514
  patch: { nextRetryAt: void 0 }
2448
2515
  });
@@ -2710,7 +2777,7 @@ var JobManager = class extends EventEmitter {
2710
2777
  this.rpc = rpc;
2711
2778
  this.store = store;
2712
2779
  this.retryPolicy = config.retryPolicy ?? defaultPaymentRetryPolicy;
2713
- this.schedulerIntervalMs = config.schedulerIntervalMs ?? 500;
2780
+ this.schedulerIntervalMs = config.schedulerIntervalMs ?? 1e3;
2714
2781
  this.maxConcurrentJobs = config.maxConcurrentJobs ?? 5;
2715
2782
  }
2716
2783
  async ensurePayment(params, options = {}) {