mppx 0.8.12 → 0.8.14
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/CHANGELOG.md +34 -0
- package/README.md +16 -1
- package/dist/Errors.d.ts +10 -0
- package/dist/Errors.d.ts.map +1 -1
- package/dist/Errors.js +11 -2
- package/dist/Errors.js.map +1 -1
- package/dist/Method.d.ts +105 -3
- package/dist/Method.d.ts.map +1 -1
- package/dist/Method.js +102 -2
- package/dist/Method.js.map +1 -1
- package/dist/cli/account.d.ts +88 -0
- package/dist/cli/account.d.ts.map +1 -1
- package/dist/cli/account.js +39 -9
- package/dist/cli/account.js.map +1 -1
- package/dist/cli/cli.d.ts +2 -1
- package/dist/cli/cli.d.ts.map +1 -1
- package/dist/cli/cli.js +59 -2
- package/dist/cli/cli.js.map +1 -1
- package/dist/cli/sessions/Manager.d.ts +31 -0
- package/dist/cli/sessions/Manager.d.ts.map +1 -0
- package/dist/cli/sessions/Manager.js +56 -0
- package/dist/cli/sessions/Manager.js.map +1 -0
- package/dist/cli/sessions/commands.d.ts +36 -0
- package/dist/cli/sessions/commands.d.ts.map +1 -0
- package/dist/cli/sessions/commands.js +407 -0
- package/dist/cli/sessions/commands.js.map +1 -0
- package/dist/cli/sessions/request.d.ts +35 -0
- package/dist/cli/sessions/request.d.ts.map +1 -0
- package/dist/cli/sessions/request.js +271 -0
- package/dist/cli/sessions/request.js.map +1 -0
- package/dist/cli/sessions/store.d.ts +134 -0
- package/dist/cli/sessions/store.d.ts.map +1 -0
- package/dist/cli/sessions/store.js +723 -0
- package/dist/cli/sessions/store.js.map +1 -0
- package/dist/cli/utils.d.ts +2 -0
- package/dist/cli/utils.d.ts.map +1 -1
- package/dist/cli/utils.js +12 -0
- package/dist/cli/utils.js.map +1 -1
- package/dist/cli/validate/index.d.ts.map +1 -1
- package/dist/cli/validate/index.js +12 -6
- package/dist/cli/validate/index.js.map +1 -1
- package/dist/cli/validate/messages.d.ts +2 -0
- package/dist/cli/validate/messages.d.ts.map +1 -0
- package/dist/cli/validate/messages.js +8 -0
- package/dist/cli/validate/messages.js.map +1 -0
- package/dist/client/internal/Fetch.d.ts.map +1 -1
- package/dist/client/internal/Fetch.js +12 -2
- package/dist/client/internal/Fetch.js.map +1 -1
- package/dist/client/internal/MethodChallenge.d.ts +17 -0
- package/dist/client/internal/MethodChallenge.d.ts.map +1 -0
- package/dist/client/internal/MethodChallenge.js +11 -0
- package/dist/client/internal/MethodChallenge.js.map +1 -0
- package/dist/client/node.d.ts +29 -0
- package/dist/client/node.d.ts.map +1 -0
- package/dist/client/node.js +177 -0
- package/dist/client/node.js.map +1 -0
- package/dist/internal/types.d.ts +24 -0
- package/dist/internal/types.d.ts.map +1 -1
- package/dist/server/Mppx.d.ts +23 -4
- package/dist/server/Mppx.d.ts.map +1 -1
- package/dist/server/Mppx.js +91 -82
- package/dist/server/Mppx.js.map +1 -1
- package/dist/stripe/server/Charge.d.ts +8 -4
- package/dist/stripe/server/Charge.d.ts.map +1 -1
- package/dist/stripe/server/Charge.js.map +1 -1
- package/dist/stripe/server/internal/html.gen.d.ts +1 -1
- package/dist/stripe/server/internal/html.gen.d.ts.map +1 -1
- package/dist/stripe/server/internal/html.gen.js +1 -1
- package/dist/stripe/server/internal/html.gen.js.map +1 -1
- package/dist/tempo/client/Methods.d.ts +2 -1
- package/dist/tempo/client/Methods.d.ts.map +1 -1
- package/dist/tempo/internal/fee-payer.d.ts +1 -0
- package/dist/tempo/internal/fee-payer.d.ts.map +1 -1
- package/dist/tempo/internal/fee-payer.js +7 -2
- package/dist/tempo/internal/fee-payer.js.map +1 -1
- package/dist/tempo/internal/types.d.ts +10 -1
- package/dist/tempo/internal/types.d.ts.map +1 -1
- package/dist/tempo/legacy/client/ChannelOps.d.ts.map +1 -1
- package/dist/tempo/legacy/client/ChannelOps.js +2 -1
- package/dist/tempo/legacy/client/ChannelOps.js.map +1 -1
- package/dist/tempo/legacy/client/Session.d.ts +2 -2
- package/dist/tempo/legacy/server/Session.d.ts +3 -3
- package/dist/tempo/legacy/server/Session.d.ts.map +1 -1
- package/dist/tempo/legacy/server/Session.js.map +1 -1
- package/dist/tempo/server/Charge.d.ts +40 -78
- package/dist/tempo/server/Charge.d.ts.map +1 -1
- package/dist/tempo/server/Charge.js +289 -209
- package/dist/tempo/server/Charge.js.map +1 -1
- package/dist/tempo/server/Methods.d.ts +13 -23
- package/dist/tempo/server/Methods.d.ts.map +1 -1
- package/dist/tempo/server/Methods.js +6 -2
- package/dist/tempo/server/Methods.js.map +1 -1
- package/dist/tempo/server/Relay.d.ts +48 -0
- package/dist/tempo/server/Relay.d.ts.map +1 -0
- package/dist/tempo/server/Relay.js +177 -0
- package/dist/tempo/server/Relay.js.map +1 -0
- package/dist/tempo/server/SponsorBudget.d.ts +59 -0
- package/dist/tempo/server/SponsorBudget.d.ts.map +1 -0
- package/dist/tempo/server/SponsorBudget.js +144 -0
- package/dist/tempo/server/SponsorBudget.js.map +1 -0
- package/dist/tempo/server/Subscription.d.ts +3 -2
- package/dist/tempo/server/Subscription.d.ts.map +1 -1
- package/dist/tempo/server/Subscription.js.map +1 -1
- package/dist/tempo/server/index.d.ts +1 -1
- package/dist/tempo/server/index.d.ts.map +1 -1
- package/dist/tempo/server/index.js.map +1 -1
- package/dist/tempo/server/internal/html.gen.d.ts +1 -1
- package/dist/tempo/server/internal/html.gen.d.ts.map +1 -1
- package/dist/tempo/server/internal/html.gen.js +1 -1
- package/dist/tempo/server/internal/html.gen.js.map +1 -1
- package/dist/tempo/session/Snapshot.d.ts +9 -0
- package/dist/tempo/session/Snapshot.d.ts.map +1 -1
- package/dist/tempo/session/Snapshot.js +6 -0
- package/dist/tempo/session/Snapshot.js.map +1 -1
- package/dist/tempo/session/client/ChannelOps.d.ts +7 -1
- package/dist/tempo/session/client/ChannelOps.d.ts.map +1 -1
- package/dist/tempo/session/client/ChannelOps.js +17 -3
- package/dist/tempo/session/client/ChannelOps.js.map +1 -1
- package/dist/tempo/session/client/CredentialState.d.ts +26 -2
- package/dist/tempo/session/client/CredentialState.d.ts.map +1 -1
- package/dist/tempo/session/client/CredentialState.js +99 -16
- package/dist/tempo/session/client/CredentialState.js.map +1 -1
- package/dist/tempo/session/client/Runtime.d.ts +8 -0
- package/dist/tempo/session/client/Runtime.d.ts.map +1 -1
- package/dist/tempo/session/client/Runtime.js +17 -0
- package/dist/tempo/session/client/Runtime.js.map +1 -1
- package/dist/tempo/session/client/Session.d.ts +16 -6
- package/dist/tempo/session/client/Session.d.ts.map +1 -1
- package/dist/tempo/session/client/Session.js +93 -36
- package/dist/tempo/session/client/Session.js.map +1 -1
- package/dist/tempo/session/client/SessionManager.d.ts +10 -3
- package/dist/tempo/session/client/SessionManager.d.ts.map +1 -1
- package/dist/tempo/session/client/SessionManager.js +176 -50
- package/dist/tempo/session/client/SessionManager.js.map +1 -1
- package/dist/tempo/session/client/Transports.d.ts +18 -7
- package/dist/tempo/session/client/Transports.d.ts.map +1 -1
- package/dist/tempo/session/client/Transports.js +81 -33
- package/dist/tempo/session/client/Transports.js.map +1 -1
- package/dist/tempo/session/client/internal/SessionManager.d.ts +19 -0
- package/dist/tempo/session/client/internal/SessionManager.d.ts.map +1 -0
- package/dist/tempo/session/client/internal/SessionManager.js +13 -0
- package/dist/tempo/session/client/internal/SessionManager.js.map +1 -0
- package/dist/tempo/session/precompile/Chain.d.ts.map +1 -1
- package/dist/tempo/session/precompile/Chain.js +109 -12
- package/dist/tempo/session/precompile/Chain.js.map +1 -1
- package/dist/tempo/session/precompile/Channel.d.ts +2 -0
- package/dist/tempo/session/precompile/Channel.d.ts.map +1 -1
- package/dist/tempo/session/precompile/Channel.js +4 -0
- package/dist/tempo/session/precompile/Channel.js.map +1 -1
- package/dist/tempo/session/server/CredentialVerification.d.ts +3 -0
- package/dist/tempo/session/server/CredentialVerification.d.ts.map +1 -1
- package/dist/tempo/session/server/CredentialVerification.js +14 -0
- package/dist/tempo/session/server/CredentialVerification.js.map +1 -1
- package/dist/tempo/session/server/MeteredStream.d.ts +7 -1
- package/dist/tempo/session/server/MeteredStream.d.ts.map +1 -1
- package/dist/tempo/session/server/MeteredStream.js +5 -4
- package/dist/tempo/session/server/MeteredStream.js.map +1 -1
- package/dist/tempo/session/server/RequestState.d.ts +5 -1
- package/dist/tempo/session/server/RequestState.d.ts.map +1 -1
- package/dist/tempo/session/server/RequestState.js +13 -5
- package/dist/tempo/session/server/RequestState.js.map +1 -1
- package/dist/tempo/session/server/Session.d.ts +21 -6
- package/dist/tempo/session/server/Session.d.ts.map +1 -1
- package/dist/tempo/session/server/Session.js +4 -1
- package/dist/tempo/session/server/Session.js.map +1 -1
- package/dist/tempo/session/server/Settlement.d.ts +20 -0
- package/dist/tempo/session/server/Settlement.d.ts.map +1 -1
- package/dist/tempo/session/server/Settlement.js +20 -0
- package/dist/tempo/session/server/Settlement.js.map +1 -1
- package/dist/tempo/session/server/Ws.d.ts +2 -0
- package/dist/tempo/session/server/Ws.d.ts.map +1 -1
- package/dist/tempo/session/server/Ws.js.map +1 -1
- package/dist/tempo/session/server/index.d.ts +1 -1
- package/dist/tempo/session/server/index.d.ts.map +1 -1
- package/dist/tempo/subscription/KeyAuthorization.d.ts +21 -21
- package/dist/validation/core.d.ts.map +1 -1
- package/dist/validation/core.js +6 -2
- package/dist/validation/core.js.map +1 -1
- package/dist/viem/Client.d.ts.map +1 -1
- package/dist/viem/Client.js +24 -17
- package/dist/viem/Client.js.map +1 -1
- package/package.json +6 -1
- package/src/Errors.test.ts +23 -0
- package/src/Errors.ts +21 -2
- package/src/Method.test.ts +102 -1
- package/src/Method.ts +241 -5
- package/src/cli/account.ts +45 -10
- package/src/cli/cli.test.ts +165 -70
- package/src/cli/cli.ts +67 -2
- package/src/cli/mcp.test.ts +11 -0
- package/src/cli/sessions/Manager.test.ts +249 -0
- package/src/cli/sessions/Manager.ts +93 -0
- package/src/cli/sessions/commands.ts +444 -0
- package/src/cli/sessions/request.test.ts +51 -0
- package/src/cli/sessions/request.ts +353 -0
- package/src/cli/sessions/store.test.ts +581 -0
- package/src/cli/sessions/store.ts +940 -0
- package/src/cli/utils.test.ts +23 -0
- package/src/cli/utils.ts +10 -0
- package/src/cli/validate/index.ts +12 -14
- package/src/cli/validate/messages.ts +7 -0
- package/src/cli/validate.test.ts +38 -5
- package/src/client/Mppx.test-d.ts +3 -3
- package/src/client/internal/Fetch.ts +16 -7
- package/src/client/internal/MethodChallenge.ts +30 -0
- package/src/client/node.test.ts +115 -0
- package/src/client/node.ts +247 -0
- package/src/internal/types.test-d.ts +21 -0
- package/src/internal/types.ts +33 -0
- package/src/server/Mppx.test-d.ts +44 -0
- package/src/server/Mppx.test.ts +205 -0
- package/src/server/Mppx.ts +167 -103
- package/src/stripe/Methods.test.ts +10 -0
- package/src/stripe/server/Charge.test-d.ts +66 -0
- package/src/stripe/server/Charge.ts +8 -5
- package/src/stripe/server/internal/html.gen.ts +1 -1
- package/src/tempo/PublicExports.test-d.ts +20 -0
- package/src/tempo/internal/fee-payer.ts +7 -3
- package/src/tempo/internal/types.ts +13 -2
- package/src/tempo/legacy/client/ChannelOps.test.ts +7 -0
- package/src/tempo/legacy/client/ChannelOps.ts +2 -1
- package/src/tempo/legacy/server/Defaults.test-d.ts +9 -0
- package/src/tempo/legacy/server/Session.ts +6 -5
- package/src/tempo/server/Charge.test.ts +337 -63
- package/src/tempo/server/Charge.ts +440 -270
- package/src/tempo/server/Methods.ts +16 -4
- package/src/tempo/server/Relay.test.ts +523 -0
- package/src/tempo/server/Relay.ts +288 -0
- package/src/tempo/server/SponsorBudget.test.ts +125 -0
- package/src/tempo/server/SponsorBudget.ts +213 -0
- package/src/tempo/server/Subscription.ts +6 -4
- package/src/tempo/server/index.ts +5 -1
- package/src/tempo/server/internal/html.gen.ts +1 -1
- package/src/tempo/session/README.md +201 -0
- package/src/tempo/session/Snapshot.ts +21 -0
- package/src/tempo/session/client/ChannelOps.test.ts +55 -0
- package/src/tempo/session/client/ChannelOps.ts +21 -3
- package/src/tempo/session/client/CredentialState.test.ts +2 -2
- package/src/tempo/session/client/CredentialState.ts +159 -16
- package/src/tempo/session/client/Runtime.test.ts +12 -0
- package/src/tempo/session/client/Runtime.ts +22 -0
- package/src/tempo/session/client/Session.test.ts +238 -5
- package/src/tempo/session/client/Session.ts +130 -41
- package/src/tempo/session/client/SessionManager.test.ts +239 -8
- package/src/tempo/session/client/SessionManager.ts +204 -54
- package/src/tempo/session/client/Transports.test.ts +181 -8
- package/src/tempo/session/client/Transports.ts +127 -45
- package/src/tempo/session/client/internal/SessionManager.ts +36 -0
- package/src/tempo/session/precompile/Chain.integration.test.ts +19 -1
- package/src/tempo/session/precompile/Chain.test.ts +223 -10
- package/src/tempo/session/precompile/Chain.ts +119 -12
- package/src/tempo/session/precompile/Channel.test.ts +9 -0
- package/src/tempo/session/precompile/Channel.ts +5 -0
- package/src/tempo/session/server/CredentialVerification.ts +18 -1
- package/src/tempo/session/server/MeteredStream.ts +12 -5
- package/src/tempo/session/server/RequestState.test.ts +47 -4
- package/src/tempo/session/server/RequestState.ts +15 -8
- package/src/tempo/session/server/Session.test.ts +461 -0
- package/src/tempo/session/server/Session.ts +31 -8
- package/src/tempo/session/server/Settlement.ts +45 -0
- package/src/tempo/session/server/Ws.test.ts +113 -0
- package/src/tempo/session/server/Ws.ts +2 -0
- package/src/tempo/session/server/index.ts +2 -0
- package/src/validation/core.ts +7 -1
- package/src/viem/Client.test.ts +44 -1
- package/src/viem/Client.ts +31 -17
|
@@ -44,10 +44,16 @@ import {
|
|
|
44
44
|
import { respondToSessionCredential } from './RequestState.js'
|
|
45
45
|
import { applyVerifiedHttpAccounting, chargeSessionChannel } from './Settlement.js'
|
|
46
46
|
import { maybeSettleScheduled } from './Settlement.js'
|
|
47
|
-
import {
|
|
47
|
+
import {
|
|
48
|
+
resolveSettlementSchedule,
|
|
49
|
+
type OnSessionSettlement,
|
|
50
|
+
type SettlementSchedule,
|
|
51
|
+
} from './Settlement.js'
|
|
48
52
|
|
|
49
53
|
/** Server-side automatic settlement schedule. */
|
|
50
54
|
export type { SettlementSchedule } from './Settlement.js'
|
|
55
|
+
/** Server-side settlement event hook types. */
|
|
56
|
+
export type { OnSessionSettlement, SessionSettlementContext } from './Settlement.js'
|
|
51
57
|
/** Server-side hook types for request-identity channel bootstrap. */
|
|
52
58
|
export type {
|
|
53
59
|
ResolveSessionChannelId,
|
|
@@ -312,6 +318,7 @@ export function session<const parameters extends session.Parameters>(
|
|
|
312
318
|
unitType,
|
|
313
319
|
} = parameters
|
|
314
320
|
const settlementSchedule = resolveSettlementSchedule(parameters.settlementSchedule, decimals)
|
|
321
|
+
const onSessionSettlement = parameters.onSessionSettlement
|
|
315
322
|
|
|
316
323
|
const store = ChannelStore.fromStore(rawStore)
|
|
317
324
|
const lastOnChainVerified = new Map<Hex, number>()
|
|
@@ -424,6 +431,7 @@ export function session<const parameters extends session.Parameters>(
|
|
|
424
431
|
feeToken: parameters.feeToken,
|
|
425
432
|
lastOnChainVerified,
|
|
426
433
|
minVoucherDelta: context.minVoucherDelta,
|
|
434
|
+
onSessionSettlement,
|
|
427
435
|
payload,
|
|
428
436
|
store,
|
|
429
437
|
})
|
|
@@ -444,6 +452,7 @@ export function session<const parameters extends session.Parameters>(
|
|
|
444
452
|
...(typeof context.feePayer === 'object' ? { feePayer: context.feePayer } : {}),
|
|
445
453
|
feePayerPolicy: parameters.feePayerPolicy,
|
|
446
454
|
feeToken: parameters.feeToken,
|
|
455
|
+
onSessionSettlement,
|
|
447
456
|
schedule: settlementSchedule,
|
|
448
457
|
store,
|
|
449
458
|
channel,
|
|
@@ -492,9 +501,20 @@ export namespace session {
|
|
|
492
501
|
feePayerPolicy?: FeePayerPolicy | undefined
|
|
493
502
|
/** Minimum voucher delta to accept (numeric string, default: "0"). */
|
|
494
503
|
minVoucherDelta?: string | undefined
|
|
495
|
-
/**
|
|
504
|
+
/**
|
|
505
|
+
* Maps authenticated application identity and payment scope to an existing channel ID.
|
|
506
|
+
* Called only when the request does not already supply a channel ID. MPPx then loads that
|
|
507
|
+
* ID from `store` and validates the channel before including its snapshot in a challenge or
|
|
508
|
+
* bootstrap response. The store is not searched automatically and does not need to implement
|
|
509
|
+
* a secondary-index format.
|
|
510
|
+
*/
|
|
496
511
|
resolveChannelId?: ResolveSessionChannelId | undefined
|
|
497
|
-
/**
|
|
512
|
+
/**
|
|
513
|
+
* Enables same-route `HEAD` recovery before a paid request. The server issues a zero-amount
|
|
514
|
+
* identity challenge, verifies the returned proof, passes its authenticated `source` to
|
|
515
|
+
* `resolveChannelId`, validates the loaded channel's payment scope, and returns a snapshot
|
|
516
|
+
* containing the signed highest voucher for client rehydration.
|
|
517
|
+
*/
|
|
498
518
|
bootstrap?: boolean | undefined
|
|
499
519
|
/**
|
|
500
520
|
* Atomic store backend for channel state.
|
|
@@ -510,6 +530,8 @@ export namespace session {
|
|
|
510
530
|
chainId?: number | undefined
|
|
511
531
|
/** TIP20EscrowChannel precompile address override. */
|
|
512
532
|
escrowContract?: Address | undefined
|
|
533
|
+
/** Callback invoked after any on-chain settlement or close transaction is confirmed. */
|
|
534
|
+
onSessionSettlement?: OnSessionSettlement | undefined
|
|
513
535
|
/** Server-owned automatic settlement cadence. Clients do not receive or control this schedule. */
|
|
514
536
|
settlementSchedule?: SettlementSchedule | undefined
|
|
515
537
|
|
|
@@ -522,11 +544,12 @@ export namespace session {
|
|
|
522
544
|
/** Defaults derived from `session()` parameters for handler type inference. */
|
|
523
545
|
export type DeriveDefaults<parameters extends Parameters> = types.DeriveDefaults<
|
|
524
546
|
parameters,
|
|
525
|
-
Defaults
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
547
|
+
Defaults,
|
|
548
|
+
{
|
|
549
|
+
currency: string
|
|
550
|
+
decimals: number
|
|
551
|
+
}
|
|
552
|
+
>
|
|
530
553
|
}
|
|
531
554
|
|
|
532
555
|
/**
|
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
InsufficientBalanceError,
|
|
17
17
|
VerificationFailedError,
|
|
18
18
|
} from '../../../Errors.js'
|
|
19
|
+
import type { MaybePromise } from '../../../internal/types.js'
|
|
19
20
|
import type * as Method from '../../../Method.js'
|
|
20
21
|
import * as Store from '../../../Store.js'
|
|
21
22
|
import type * as FeePayer from '../../internal/fee-payer.js'
|
|
@@ -146,6 +147,23 @@ export type SettlementProgress = {
|
|
|
146
147
|
units: number
|
|
147
148
|
}
|
|
148
149
|
|
|
150
|
+
/** Context emitted when an on-chain settlement or close transaction is confirmed. */
|
|
151
|
+
export type SessionSettlementContext = Readonly<{
|
|
152
|
+
/** On-chain transaction hash (or signature on Solana). */
|
|
153
|
+
txHash: Hex
|
|
154
|
+
/** Channel ID that was settled. */
|
|
155
|
+
channelId: Hex
|
|
156
|
+
/** The trigger that caused settlement. */
|
|
157
|
+
trigger: 'settle' | 'close' | 'scheduled'
|
|
158
|
+
/** Cumulative amount settled on-chain to the payee (raw token units). */
|
|
159
|
+
amount: bigint
|
|
160
|
+
/** Incremental amount settled in this transaction (raw token units). */
|
|
161
|
+
delta: bigint
|
|
162
|
+
}>
|
|
163
|
+
|
|
164
|
+
/** Callback invoked after an on-chain settlement or close transaction is confirmed. */
|
|
165
|
+
export type OnSessionSettlement = (context: SessionSettlementContext) => MaybePromise<void>
|
|
166
|
+
|
|
149
167
|
/** Inputs used to mark a channel after automatic scheduled settlement succeeds. */
|
|
150
168
|
export type MarkSettlementCompleteParameters = {
|
|
151
169
|
channelId: ChannelStore.State['channelId']
|
|
@@ -340,6 +358,8 @@ export type SettlementTransactionOptions = {
|
|
|
340
358
|
feePayerPolicy?: Partial<FeePayer.Policy> | undefined
|
|
341
359
|
/** Optional fee token override for settlement. */
|
|
342
360
|
feeToken?: Address | undefined
|
|
361
|
+
/** Callback invoked after the settlement transaction is confirmed. */
|
|
362
|
+
onSessionSettlement?: OnSessionSettlement | undefined
|
|
343
363
|
}
|
|
344
364
|
|
|
345
365
|
/** Inputs for applying a server-owned automatic settlement schedule. */
|
|
@@ -356,6 +376,8 @@ export type MaybeSettleScheduledParameters = {
|
|
|
356
376
|
feePayerPolicy?: Partial<FeePayer.Policy> | undefined
|
|
357
377
|
/** Optional fee token override for settlement. */
|
|
358
378
|
feeToken?: Address | undefined
|
|
379
|
+
/** Callback invoked after the scheduled settlement transaction is confirmed. */
|
|
380
|
+
onSessionSettlement?: OnSessionSettlement | undefined
|
|
359
381
|
/** Resolved server-owned settlement cadence. */
|
|
360
382
|
schedule: ResolvedSettlementSchedule | undefined
|
|
361
383
|
/** Server-side channel store. */
|
|
@@ -400,6 +422,9 @@ export async function maybeSettleScheduled(
|
|
|
400
422
|
...(parameters.feePayer ? { feePayer: parameters.feePayer } : {}),
|
|
401
423
|
...(parameters.feePayerPolicy ? { feePayerPolicy: parameters.feePayerPolicy } : {}),
|
|
402
424
|
...(parameters.feeToken ? { feeToken: parameters.feeToken } : {}),
|
|
425
|
+
onSessionSettlement: parameters.onSessionSettlement
|
|
426
|
+
? (ctx) => parameters.onSessionSettlement!({ ...ctx, trigger: 'scheduled' })
|
|
427
|
+
: undefined,
|
|
403
428
|
})
|
|
404
429
|
await markSettlementComplete({ channelId: channel.channelId, store })
|
|
405
430
|
return txHash
|
|
@@ -466,6 +491,15 @@ export async function settle(
|
|
|
466
491
|
}
|
|
467
492
|
: current,
|
|
468
493
|
)
|
|
494
|
+
if (options?.onSessionSettlement) {
|
|
495
|
+
await emitSessionSettlement(options.onSessionSettlement, {
|
|
496
|
+
txHash,
|
|
497
|
+
channelId,
|
|
498
|
+
trigger: 'settle',
|
|
499
|
+
amount: newSettled,
|
|
500
|
+
delta: newSettled - channel.settledOnChain,
|
|
501
|
+
})
|
|
502
|
+
}
|
|
469
503
|
return txHash
|
|
470
504
|
}
|
|
471
505
|
|
|
@@ -480,3 +514,14 @@ export async function settleBatch(
|
|
|
480
514
|
for (const channelId of channelIds) hashes.push(await settle(store, client, channelId, options))
|
|
481
515
|
return hashes
|
|
482
516
|
}
|
|
517
|
+
|
|
518
|
+
async function emitSessionSettlement(
|
|
519
|
+
onSessionSettlement: OnSessionSettlement,
|
|
520
|
+
context: SessionSettlementContext,
|
|
521
|
+
): Promise<void> {
|
|
522
|
+
try {
|
|
523
|
+
await onSessionSettlement(Object.freeze(context))
|
|
524
|
+
} catch {
|
|
525
|
+
// Errors are isolated — observers cannot break the settlement flow.
|
|
526
|
+
}
|
|
527
|
+
}
|
|
@@ -408,6 +408,119 @@ describe('isows', () => {
|
|
|
408
408
|
expect(channel?.units).toBe(0)
|
|
409
409
|
})
|
|
410
410
|
|
|
411
|
+
test('aborts a blocked application generator before sending close-ready', async () => {
|
|
412
|
+
const socket = new MockSocket()
|
|
413
|
+
const store = memoryChannelStore()
|
|
414
|
+
await seedChannel(store, 1n)
|
|
415
|
+
let generatorAborted = false
|
|
416
|
+
|
|
417
|
+
await Ws.serve({
|
|
418
|
+
socket,
|
|
419
|
+
store,
|
|
420
|
+
url: 'ws://example.test/stream',
|
|
421
|
+
route: async () => ({
|
|
422
|
+
status: 200,
|
|
423
|
+
withReceipt(response = new Response(null, { status: 204 })) {
|
|
424
|
+
response.headers.set(
|
|
425
|
+
'Payment-Receipt',
|
|
426
|
+
serializeSessionReceipt(
|
|
427
|
+
createSessionReceipt({
|
|
428
|
+
challengeId: challenge.id,
|
|
429
|
+
channelId,
|
|
430
|
+
acceptedCumulative: 1n,
|
|
431
|
+
spent: 0n,
|
|
432
|
+
units: 0,
|
|
433
|
+
}),
|
|
434
|
+
),
|
|
435
|
+
)
|
|
436
|
+
return response
|
|
437
|
+
},
|
|
438
|
+
}),
|
|
439
|
+
generate: async function* (stream) {
|
|
440
|
+
await new Promise<void>((resolve) => {
|
|
441
|
+
stream.signal.addEventListener('abort', () => resolve(), { once: true })
|
|
442
|
+
})
|
|
443
|
+
generatorAborted = true
|
|
444
|
+
yield* []
|
|
445
|
+
},
|
|
446
|
+
})
|
|
447
|
+
|
|
448
|
+
socket.receive(
|
|
449
|
+
Ws.formatAuthorizationMessage(
|
|
450
|
+
makeCredential({
|
|
451
|
+
action: 'open',
|
|
452
|
+
channelId,
|
|
453
|
+
cumulativeAmount: '1',
|
|
454
|
+
signature: `0x${'77'.repeat(65)}`,
|
|
455
|
+
transaction: '0x01',
|
|
456
|
+
type: 'transaction',
|
|
457
|
+
}),
|
|
458
|
+
),
|
|
459
|
+
)
|
|
460
|
+
await sleep(10)
|
|
461
|
+
socket.receive(Ws.formatCloseRequestMessage())
|
|
462
|
+
await sleep(10)
|
|
463
|
+
|
|
464
|
+
expect(generatorAborted).toBe(true)
|
|
465
|
+
expect(
|
|
466
|
+
socket.sent
|
|
467
|
+
.map((message) => Ws.parseMessage(message))
|
|
468
|
+
.some((message) => message?.mpp === 'payment-close-ready'),
|
|
469
|
+
).toBe(true)
|
|
470
|
+
})
|
|
471
|
+
|
|
472
|
+
test('meters an explicit per-message amount independently of the challenge tick cost', async () => {
|
|
473
|
+
const socket = new MockSocket()
|
|
474
|
+
const store = memoryChannelStore()
|
|
475
|
+
await seedChannel(store, 25n)
|
|
476
|
+
|
|
477
|
+
await Ws.serve({
|
|
478
|
+
socket,
|
|
479
|
+
store,
|
|
480
|
+
url: 'ws://example.test/stream',
|
|
481
|
+
route: async () => ({
|
|
482
|
+
status: 200,
|
|
483
|
+
withReceipt(response = new Response(null, { status: 204 })) {
|
|
484
|
+
response.headers.set(
|
|
485
|
+
'Payment-Receipt',
|
|
486
|
+
serializeSessionReceipt(
|
|
487
|
+
createSessionReceipt({
|
|
488
|
+
challengeId: challenge.id,
|
|
489
|
+
channelId,
|
|
490
|
+
acceptedCumulative: 25n,
|
|
491
|
+
spent: 0n,
|
|
492
|
+
units: 0,
|
|
493
|
+
}),
|
|
494
|
+
),
|
|
495
|
+
)
|
|
496
|
+
return response
|
|
497
|
+
},
|
|
498
|
+
}),
|
|
499
|
+
generate: async function* (stream) {
|
|
500
|
+
await stream.charge(7n)
|
|
501
|
+
yield 'priced-response'
|
|
502
|
+
},
|
|
503
|
+
})
|
|
504
|
+
|
|
505
|
+
socket.receive(
|
|
506
|
+
Ws.formatAuthorizationMessage(
|
|
507
|
+
makeCredential({
|
|
508
|
+
action: 'open',
|
|
509
|
+
channelId,
|
|
510
|
+
cumulativeAmount: '25',
|
|
511
|
+
signature: `0x${'77'.repeat(65)}`,
|
|
512
|
+
transaction: '0x01',
|
|
513
|
+
type: 'transaction',
|
|
514
|
+
}),
|
|
515
|
+
),
|
|
516
|
+
)
|
|
517
|
+
|
|
518
|
+
await sleep(10)
|
|
519
|
+
|
|
520
|
+
expect(await store.getChannel(channelId)).toMatchObject({ spent: 7n, units: 1 })
|
|
521
|
+
expect(socket.sent.some((message) => message.includes('priced-response'))).toBe(true)
|
|
522
|
+
})
|
|
523
|
+
|
|
411
524
|
test('does not meter or emit application messages after close is requested on-chain', async () => {
|
|
412
525
|
const socket = new MockSocket()
|
|
413
526
|
const store = memoryChannelStore()
|
|
@@ -322,6 +322,8 @@ export declare namespace serve {
|
|
|
322
322
|
* multiple offers — otherwise a client can select the cheapest offer
|
|
323
323
|
* and still receive the same stream. */
|
|
324
324
|
amount?: string | undefined
|
|
325
|
+
/** Application stream. A manual stream can call `charge(amount)` with a
|
|
326
|
+
* per-message raw-unit amount; omitting it uses the challenge tick cost. */
|
|
325
327
|
generate: AsyncIterable<string> | ((stream: SessionController) => AsyncIterable<string>)
|
|
326
328
|
pollIntervalMs?: number | undefined
|
|
327
329
|
/** Payment route handler. Receives synthetic `POST` requests with only
|
|
@@ -3,8 +3,10 @@ export { charge, session, settle, settleBatch } from './Session.js'
|
|
|
3
3
|
export * as Sse from './Sse.js'
|
|
4
4
|
/** Server-side automatic settlement schedule. */
|
|
5
5
|
export type {
|
|
6
|
+
OnSessionSettlement,
|
|
6
7
|
ResolveSessionChannelId,
|
|
7
8
|
ResolveSessionChannelIdParameters,
|
|
8
9
|
SessionChannelIdRequest,
|
|
10
|
+
SessionSettlementContext,
|
|
9
11
|
SettlementSchedule,
|
|
10
12
|
} from './Session.js'
|
package/src/validation/core.ts
CHANGED
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
parseEndpointArg,
|
|
15
15
|
resolveBodyForEndpoint,
|
|
16
16
|
} from '../cli/validate/helpers.js'
|
|
17
|
+
import { missingDiscoverySuggestion } from '../cli/validate/messages.js'
|
|
17
18
|
import { validatePaymentFlow } from '../cli/validate/payment.js'
|
|
18
19
|
import { validate as validateDiscoveryDoc } from '../discovery/Validate.js'
|
|
19
20
|
|
|
@@ -237,7 +238,7 @@ export async function validate(options: ValidateOptions): Promise<ValidateResult
|
|
|
237
238
|
|
|
238
239
|
if (!discovery) throw new Error('Discovery phase did not complete')
|
|
239
240
|
|
|
240
|
-
const suggestions = computeSuggestions(endpointResults, { sawTestnet, sawMainnet })
|
|
241
|
+
const suggestions = computeSuggestions(endpointResults, discovery, { sawTestnet, sawMainnet })
|
|
241
242
|
|
|
242
243
|
return {
|
|
243
244
|
url: baseUrl,
|
|
@@ -250,11 +251,16 @@ export async function validate(options: ValidateOptions): Promise<ValidateResult
|
|
|
250
251
|
|
|
251
252
|
function computeSuggestions(
|
|
252
253
|
endpoints: EndpointValidationResult[],
|
|
254
|
+
discovery: DiscoveryResult,
|
|
253
255
|
flags: { sawTestnet: boolean; sawMainnet: boolean },
|
|
254
256
|
): string[] {
|
|
255
257
|
const steps: string[] = []
|
|
256
258
|
const allResults = endpoints.flatMap((ep) => [...ep.challenge, ...ep.payment])
|
|
257
259
|
|
|
260
|
+
if (!discovery.found) {
|
|
261
|
+
steps.push(missingDiscoverySuggestion)
|
|
262
|
+
}
|
|
263
|
+
|
|
258
264
|
const sawCryptoMainnet = flags.sawMainnet
|
|
259
265
|
const sawCryptoTestnet = flags.sawTestnet
|
|
260
266
|
const sawStripeLive = allResults.some(
|
package/src/viem/Client.test.ts
CHANGED
|
@@ -3,12 +3,16 @@ import { privateKeyToAccount } from 'viem/accounts'
|
|
|
3
3
|
import { signTransaction } from 'viem/actions'
|
|
4
4
|
import { Account as TempoAccount, Transaction } from 'viem/tempo'
|
|
5
5
|
import { tempoLocalnet } from 'viem/tempo/chains'
|
|
6
|
-
import { describe, expect, test } from 'vp/test'
|
|
6
|
+
import { afterEach, describe, expect, test, vi } from 'vp/test'
|
|
7
7
|
|
|
8
8
|
import * as Client from './Client.js'
|
|
9
9
|
|
|
10
10
|
const rpcUrl = { 42: 'https://rpc.example.com', 99: 'https://rpc2.example.com' } as const
|
|
11
11
|
|
|
12
|
+
afterEach(() => {
|
|
13
|
+
vi.unstubAllGlobals()
|
|
14
|
+
})
|
|
15
|
+
|
|
12
16
|
describe('getResolver', () => {
|
|
13
17
|
test('behavior: creates client from rpcUrl for given chainId', async () => {
|
|
14
18
|
const getClient = Client.getResolver({
|
|
@@ -61,6 +65,45 @@ describe('getResolver', () => {
|
|
|
61
65
|
`[Error: No \`rpcUrl\` configured for \`chainId\` (99).]`,
|
|
62
66
|
)
|
|
63
67
|
})
|
|
68
|
+
|
|
69
|
+
test('behavior: wraps custom client transports with a hosted fee payer', async () => {
|
|
70
|
+
const defaultRequests: string[] = []
|
|
71
|
+
const client = createClient({
|
|
72
|
+
chain: tempoLocalnet,
|
|
73
|
+
transport: custom({
|
|
74
|
+
async request({ method }) {
|
|
75
|
+
defaultRequests.push(method)
|
|
76
|
+
return { source: 'default' }
|
|
77
|
+
},
|
|
78
|
+
}),
|
|
79
|
+
})
|
|
80
|
+
const fetchMock = vi.fn(async (_input: RequestInfo | URL, init?: RequestInit) => {
|
|
81
|
+
const request = JSON.parse(String(init?.body)) as { id: number; jsonrpc: string }
|
|
82
|
+
return Response.json({
|
|
83
|
+
id: request.id,
|
|
84
|
+
jsonrpc: request.jsonrpc,
|
|
85
|
+
result: { source: 'relay' },
|
|
86
|
+
})
|
|
87
|
+
})
|
|
88
|
+
vi.stubGlobal('fetch', fetchMock)
|
|
89
|
+
|
|
90
|
+
const getClient = Client.getResolver({
|
|
91
|
+
chain: tempoLocalnet,
|
|
92
|
+
feePayerUrl: 'https://sponsor.example',
|
|
93
|
+
getClient: () => client,
|
|
94
|
+
rpcUrl: { [tempoLocalnet.id]: 'https://rpc.example.com' },
|
|
95
|
+
})
|
|
96
|
+
const resolved = await getClient({ chainId: tempoLocalnet.id })
|
|
97
|
+
const result = await resolved.request({
|
|
98
|
+
method: 'eth_fillTransaction',
|
|
99
|
+
params: [{ feePayer: true }],
|
|
100
|
+
} as never)
|
|
101
|
+
|
|
102
|
+
expect(result).toEqual({ source: 'relay' })
|
|
103
|
+
expect(fetchMock).toHaveBeenCalledOnce()
|
|
104
|
+
expect(String(fetchMock.mock.calls[0]?.[0])).toBe('https://sponsor.example/')
|
|
105
|
+
expect(defaultRequests).toEqual([])
|
|
106
|
+
})
|
|
64
107
|
})
|
|
65
108
|
|
|
66
109
|
// ---------------------------------------------------------------------------
|
package/src/viem/Client.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type Chain, type Client, createClient, http } from 'viem'
|
|
1
|
+
import { type Chain, type Client, createClient, createTransport, custom, http } from 'viem'
|
|
2
2
|
import { withFeePayer } from 'viem/tempo'
|
|
3
3
|
|
|
4
4
|
import type { MaybePromise } from '../internal/types.js'
|
|
@@ -23,31 +23,45 @@ export function getResolver(
|
|
|
23
23
|
if (!chain?.serializers && !feePayerUrl) return getClient
|
|
24
24
|
return async (params) => {
|
|
25
25
|
const client = await getClient(params)
|
|
26
|
+
let resolvedClient = client
|
|
26
27
|
|
|
27
28
|
// Wrap the client's transport with `withFeePayer` when a fee payer URL is provided.
|
|
28
29
|
if (feePayerUrl && client.transport.key !== 'feePayer') {
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
})
|
|
35
|
-
|
|
36
|
-
|
|
30
|
+
const request = client.request.bind(client)
|
|
31
|
+
// The supplied client already owns retries. Keep the relay middleware retry-free so
|
|
32
|
+
// failures are not retried once per nested transport layer.
|
|
33
|
+
const feePayerTransport = withFeePayer(
|
|
34
|
+
custom({ request: (args) => request(args as never) }, { retryCount: 0 }),
|
|
35
|
+
http(feePayerUrl, { retryCount: client.transport.retryCount }),
|
|
36
|
+
)({
|
|
37
|
+
account: client.account,
|
|
38
|
+
chain: client.chain,
|
|
39
|
+
pollingInterval: client.pollingInterval,
|
|
40
|
+
retryCount: 0,
|
|
41
|
+
})
|
|
42
|
+
const wrapped = createTransport(
|
|
43
|
+
{ ...feePayerTransport.config, retryCount: 0 },
|
|
44
|
+
feePayerTransport.value,
|
|
45
|
+
)
|
|
46
|
+
resolvedClient = Object.assign({}, client, {
|
|
47
|
+
request: wrapped.request,
|
|
48
|
+
transport: { ...wrapped.config, ...wrapped.value },
|
|
49
|
+
})
|
|
37
50
|
}
|
|
38
51
|
|
|
39
|
-
if (!chain?.serializers ||
|
|
40
|
-
|
|
52
|
+
if (!chain?.serializers || resolvedClient.chain?.serializers?.transaction)
|
|
53
|
+
return resolvedClient
|
|
54
|
+
return Object.assign({}, resolvedClient, {
|
|
41
55
|
chain: {
|
|
42
56
|
...chain,
|
|
43
|
-
...
|
|
44
|
-
formatters:
|
|
57
|
+
...resolvedClient.chain,
|
|
58
|
+
formatters: resolvedClient.chain?.formatters ?? chain.formatters,
|
|
45
59
|
prepareTransactionRequest:
|
|
46
|
-
|
|
47
|
-
serializers:
|
|
48
|
-
?
|
|
60
|
+
resolvedClient.chain?.prepareTransactionRequest ?? chain.prepareTransactionRequest,
|
|
61
|
+
serializers: resolvedClient.chain?.serializers?.transaction
|
|
62
|
+
? resolvedClient.chain.serializers
|
|
49
63
|
: chain.serializers,
|
|
50
|
-
} as typeof
|
|
64
|
+
} as typeof resolvedClient.chain,
|
|
51
65
|
})
|
|
52
66
|
}
|
|
53
67
|
}
|