@dvmkit/sdk 0.11.0-rc.1 → 0.12.0

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.
@@ -2029,7 +2029,8 @@ var CreditLedger = class {
2029
2029
  /**
2030
2030
  * Grow an accepted job’s pending hold while preserving its original ledger_seq. The balance
2031
2031
  * tuple is updated at that sequence, so interleaved draws need not form a monotonic balance
2032
- * trajectory. No expiry check applies to an already accepted job.
2032
+ * trajectory. Existing rail top-ups retain accepted-job semantics; spending existing balance
2033
+ * opts into fresh-spend expiry and channel gates.
2033
2034
  *
2034
2035
  * Available balance excludes every pending hold, including this one. An earmark allocates
2035
2036
  * the incoming funding’s rail value; absent one, growth uses the pool.
@@ -2041,7 +2042,7 @@ var CreditLedger = class {
2041
2042
  */
2042
2043
  async growDraw(args) {
2043
2044
  assertAmount(args.addAmountMicro, { min: 1 });
2044
- const run = async (client, credit) => {
2045
+ const run = async (client, credit, tempoLifecycle) => {
2045
2046
  const existing = await this.readDraw(client, args.creditId, args.drawId);
2046
2047
  if (!existing) {
2047
2048
  throw new CreditLedgerError(
@@ -2064,6 +2065,14 @@ var CreditLedger = class {
2064
2065
  { expectedJobId: existing.job_id ?? null }
2065
2066
  );
2066
2067
  }
2068
+ if (args.spendExistingBalance && toSafeInt(credit.expiry_ms) <= (args.nowMs ?? Date.now())) {
2069
+ throw new CreditLedgerError("credit_expired", "Credit has expired");
2070
+ }
2071
+ if (args.spendExistingBalance && tempoLifecycle && (tempoLifecycle.finalized || tempoLifecycle.closeRequestedAt !== 0n)) {
2072
+ throw new CreditLedgerError("tempo_channel_closing", "Credit channel is closing");
2073
+ }
2074
+ const refund = args.spendExistingBalance ? await this.blockingX402Refund(credit.x402_channel_id, X402_SPEND_BLOCKING_SETTLEMENT_STATUSES) : void 0;
2075
+ if (refund) throw x402SettlementPending(args.creditId, refund);
2067
2076
  const priorGrown = toSafeInt(existing.grown_micro);
2068
2077
  const headroom = args.capMicro === void 0 ? args.addAmountMicro : Math.max(0, args.capMicro - priorGrown);
2069
2078
  const grantMicro = Math.min(args.addAmountMicro, headroom);
@@ -2111,6 +2120,12 @@ var CreditLedger = class {
2111
2120
  const balanceAfterMicro = available - grantMicro;
2112
2121
  const drawMsats = priorMsats + added.drawMsats;
2113
2122
  const drawNative = added.drawNative === null ? priorNative : (priorNative ?? 0) + added.drawNative;
2123
+ if (args.spendExistingBalance) {
2124
+ await this.hostingCleanupFence?.assertNewIntent(client, {
2125
+ jobId: args.jobId,
2126
+ paymentSeq: args.hostingPaymentSeq
2127
+ });
2128
+ }
2114
2129
  await client.query(
2115
2130
  `UPDATE credit_draws
2116
2131
  SET amount_micro = $3, balance_after_micro = $4, draw_msats = $5, draw_native = $6,
@@ -2141,6 +2156,18 @@ var CreditLedger = class {
2141
2156
  cappedMicro: args.addAmountMicro - grantMicro
2142
2157
  };
2143
2158
  };
2159
+ const query = args.tx ?? this.pool;
2160
+ const { rows } = await query.query(
2161
+ "SELECT tempo_channel_id FROM credits WHERE credit_id = $1",
2162
+ [args.creditId]
2163
+ );
2164
+ const channelId = rows[0]?.tempo_channel_id;
2165
+ if (args.spendExistingBalance && channelId && this.tempoSessionStore) {
2166
+ return this.tempoSessionStore.withDrawPlacement(channelId, async (tx, lifecycle) => {
2167
+ const credit = await this.lockCreditRow(tx, args.creditId);
2168
+ return run(tx, credit, lifecycle);
2169
+ }, args.tx);
2170
+ }
2144
2171
  if (args.tx) {
2145
2172
  const credit = await this.lockCreditRow(args.tx, args.creditId);
2146
2173
  return run(args.tx, credit);
@@ -18,7 +18,7 @@ import {
18
18
  refundAssetDelta,
19
19
  refundRequest,
20
20
  x402SettlementPending
21
- } from "./chunk-K2ZNIQZP.js";
21
+ } from "./chunk-2VGMZ7VF.js";
22
22
 
23
23
  // src/sdk/server/memory-credit-ledger.ts
24
24
  import { randomUUID } from "crypto";
@@ -157,8 +157,9 @@ var MemoryCreditLedger = class _MemoryCreditLedger {
157
157
  blockingRefundForCredit(creditId, statuses) {
158
158
  return this.blockingX402Refund(this.credits.get(creditId)?.x402ChannelId, statuses);
159
159
  }
160
- growDraw(args) {
161
- return run(() => this.growDrawSync(args));
160
+ async growDraw(args) {
161
+ const blocking = args.spendExistingBalance ? await this.blockingRefundForCredit(args.creditId, X402_SPEND_BLOCKING_SETTLEMENT_STATUSES) : void 0;
162
+ return run(() => this.growDrawSync(args, blocking));
162
163
  }
163
164
  settle(args) {
164
165
  return run(() => {
@@ -628,7 +629,7 @@ var MemoryCreditLedger = class _MemoryCreditLedger {
628
629
  credit.draws.set(args.drawId, draw);
629
630
  return { ...drawResult(draw, credit), replayed: false };
630
631
  }
631
- growDrawSync(args) {
632
+ growDrawSync(args, x402Refund) {
632
633
  assertAmount(args.addAmountMicro, { min: 1 });
633
634
  const credit = this.requireCredit(args.creditId);
634
635
  const draw = credit.draws.get(args.drawId);
@@ -653,6 +654,16 @@ var MemoryCreditLedger = class _MemoryCreditLedger {
653
654
  { expectedJobId: draw.jobId }
654
655
  );
655
656
  }
657
+ if (args.spendExistingBalance) {
658
+ if (credit.expiryMs <= (args.nowMs ?? Date.now())) {
659
+ throw new CreditLedgerError("credit_expired", `credit ${args.creditId} expired`, {
660
+ expiryMs: credit.expiryMs,
661
+ balanceMicro: credit.balanceMicro
662
+ });
663
+ }
664
+ if (x402Refund) throw x402SettlementPending(args.creditId, x402Refund);
665
+ if (credit.tempoChannelId) throw new CreditLedgerError("tempo_channel_closing", "Tempo balance growth requires a durable channel lifecycle");
666
+ }
656
667
  const pending = pendingSums(credit);
657
668
  const available = credit.balanceMicro - pending.micro;
658
669
  const headroom = args.capMicro === void 0 ? args.addAmountMicro : Math.max(0, args.capMicro - draw.grownMicro);