@payai/x402-evm 2.4.1 → 2.4.3
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/dist/cjs/batch-settlement/client/file-storage.d.ts +47 -0
- package/dist/cjs/batch-settlement/client/file-storage.js +116 -0
- package/dist/cjs/batch-settlement/client/file-storage.js.map +1 -0
- package/dist/cjs/batch-settlement/client/index.d.ts +111 -0
- package/dist/cjs/batch-settlement/client/index.js +1565 -0
- package/dist/cjs/batch-settlement/client/index.js.map +1 -0
- package/dist/cjs/batch-settlement/facilitator/index.d.ts +71 -0
- package/dist/cjs/batch-settlement/facilitator/index.js +2032 -0
- package/dist/cjs/batch-settlement/facilitator/index.js.map +1 -0
- package/dist/cjs/batch-settlement/server/file-storage.d.ts +53 -0
- package/dist/cjs/batch-settlement/server/file-storage.js +181 -0
- package/dist/cjs/batch-settlement/server/file-storage.js.map +1 -0
- package/dist/cjs/batch-settlement/server/index.d.ts +491 -0
- package/dist/cjs/batch-settlement/server/index.js +1960 -0
- package/dist/cjs/batch-settlement/server/index.js.map +1 -0
- package/dist/cjs/batch-settlement/server/redis-storage.d.ts +87 -0
- package/dist/cjs/batch-settlement/server/redis-storage.js +181 -0
- package/dist/cjs/batch-settlement/server/redis-storage.js.map +1 -0
- package/dist/cjs/exact/client/index.d.ts +6 -4
- package/dist/cjs/exact/client/index.js +7 -5
- package/dist/cjs/exact/client/index.js.map +1 -1
- package/dist/cjs/exact/facilitator/index.d.ts +16 -9
- package/dist/cjs/exact/facilitator/index.js +35 -7
- package/dist/cjs/exact/facilitator/index.js.map +1 -1
- package/dist/cjs/exact/server/index.js +40 -1
- package/dist/cjs/exact/server/index.js.map +1 -1
- package/dist/cjs/exact/v1/client/index.d.ts +2 -1
- package/dist/cjs/exact/v1/client/index.js.map +1 -1
- package/dist/cjs/exact/v1/facilitator/index.d.ts +11 -5
- package/dist/cjs/exact/v1/facilitator/index.js +16 -2
- package/dist/cjs/exact/v1/facilitator/index.js.map +1 -1
- package/dist/cjs/index.d.ts +113 -7
- package/dist/cjs/index.js +1353 -5
- package/dist/cjs/index.js.map +1 -1
- package/dist/{esm/permit2-CyZxwngN.d.mts → cjs/permit2-DhJRUcgY.d.ts} +1 -13
- package/dist/cjs/rpc-DULZzRne.d.ts +13 -0
- package/dist/cjs/scheme-CvkPJXBD.d.ts +307 -0
- package/dist/{esm/scheme-DCR7hsa3.d.mts → cjs/scheme-DTQFE9xp.d.ts} +2 -2
- package/dist/{esm/signer-D912R4mq.d.mts → cjs/signer-tYS6Y46X.d.ts} +3 -0
- package/dist/cjs/storage-6W5MO46W.d.ts +50 -0
- package/dist/cjs/storage-Bl6aD0Xg.d.ts +81 -0
- package/dist/cjs/types-CF8P2-NM.d.ts +180 -0
- package/dist/cjs/upto/client/index.d.ts +5 -3
- package/dist/cjs/upto/client/index.js +7 -5
- package/dist/cjs/upto/client/index.js.map +1 -1
- package/dist/cjs/upto/facilitator/index.d.ts +2 -1
- package/dist/cjs/upto/facilitator/index.js +2 -1
- package/dist/cjs/upto/facilitator/index.js.map +1 -1
- package/dist/cjs/upto/server/index.js +40 -1
- package/dist/cjs/upto/server/index.js.map +1 -1
- package/dist/cjs/v1/index.d.ts +2 -1
- package/dist/cjs/v1/index.js.map +1 -1
- package/dist/esm/batch-settlement/client/file-storage.d.mts +47 -0
- package/dist/esm/batch-settlement/client/file-storage.mjs +63 -0
- package/dist/esm/batch-settlement/client/file-storage.mjs.map +1 -0
- package/dist/esm/batch-settlement/client/index.d.mts +111 -0
- package/dist/esm/batch-settlement/client/index.mjs +59 -0
- package/dist/esm/batch-settlement/client/index.mjs.map +1 -0
- package/dist/esm/batch-settlement/facilitator/index.d.mts +71 -0
- package/dist/esm/batch-settlement/facilitator/index.mjs +1235 -0
- package/dist/esm/batch-settlement/facilitator/index.mjs.map +1 -0
- package/dist/esm/batch-settlement/server/file-storage.d.mts +53 -0
- package/dist/esm/batch-settlement/server/file-storage.mjs +128 -0
- package/dist/esm/batch-settlement/server/file-storage.mjs.map +1 -0
- package/dist/esm/batch-settlement/server/index.d.mts +491 -0
- package/dist/esm/batch-settlement/server/index.mjs +1645 -0
- package/dist/esm/batch-settlement/server/index.mjs.map +1 -0
- package/dist/esm/batch-settlement/server/redis-storage.d.mts +87 -0
- package/dist/esm/batch-settlement/server/redis-storage.mjs +156 -0
- package/dist/esm/batch-settlement/server/redis-storage.mjs.map +1 -0
- package/dist/esm/chunk-2EUQTNJO.mjs +38 -0
- package/dist/esm/chunk-2EUQTNJO.mjs.map +1 -0
- package/dist/esm/chunk-53USC5VE.mjs +47 -0
- package/dist/esm/chunk-53USC5VE.mjs.map +1 -0
- package/dist/esm/{chunk-GJ57SZGI.mjs → chunk-6WQOGWBE.mjs} +7 -5
- package/dist/esm/{chunk-GJ57SZGI.mjs.map → chunk-6WQOGWBE.mjs.map} +1 -1
- package/dist/esm/{chunk-F3OOHBAW.mjs → chunk-BTYNCDNS.mjs} +42 -2
- package/dist/esm/{chunk-F3OOHBAW.mjs.map → chunk-BTYNCDNS.mjs.map} +1 -1
- package/dist/esm/{chunk-ERK2ZPOY.mjs → chunk-CSQS7ZON.mjs} +27 -7
- package/dist/esm/chunk-CSQS7ZON.mjs.map +1 -0
- package/dist/esm/chunk-GD4MKCN7.mjs +57 -0
- package/dist/esm/chunk-GD4MKCN7.mjs.map +1 -0
- package/dist/esm/chunk-HYABYUBD.mjs +432 -0
- package/dist/esm/chunk-HYABYUBD.mjs.map +1 -0
- package/dist/esm/chunk-IN5YIT5C.mjs +159 -0
- package/dist/esm/chunk-IN5YIT5C.mjs.map +1 -0
- package/dist/esm/{chunk-JII456TS.mjs → chunk-JK7SLLF7.mjs} +1 -1
- package/dist/esm/chunk-JK7SLLF7.mjs.map +1 -0
- package/dist/esm/{chunk-C4ZQMS77.mjs → chunk-MACPBXCT.mjs} +2 -216
- package/dist/esm/chunk-MACPBXCT.mjs.map +1 -0
- package/dist/esm/chunk-NKYVYGRA.mjs +911 -0
- package/dist/esm/chunk-NKYVYGRA.mjs.map +1 -0
- package/dist/esm/{chunk-FQJR4RCF.mjs → chunk-R7I3RZFF.mjs} +10 -6
- package/dist/esm/{chunk-FQJR4RCF.mjs.map → chunk-R7I3RZFF.mjs.map} +1 -1
- package/dist/esm/{chunk-CRT6YNY5.mjs → chunk-RWLVVO3B.mjs} +21 -61
- package/dist/esm/chunk-RWLVVO3B.mjs.map +1 -0
- package/dist/esm/chunk-TGFAVNUD.mjs +111 -0
- package/dist/esm/chunk-TGFAVNUD.mjs.map +1 -0
- package/dist/esm/chunk-TW7Z65AO.mjs +34 -0
- package/dist/esm/chunk-TW7Z65AO.mjs.map +1 -0
- package/dist/esm/chunk-U4HCGTLU.mjs +35 -0
- package/dist/esm/chunk-U4HCGTLU.mjs.map +1 -0
- package/dist/esm/chunk-VS3RYAYE.mjs +80 -0
- package/dist/esm/chunk-VS3RYAYE.mjs.map +1 -0
- package/dist/esm/chunk-W6ON4LG2.mjs +39 -0
- package/dist/esm/chunk-W6ON4LG2.mjs.map +1 -0
- package/dist/esm/{chunk-WKBC5YMI.mjs → chunk-YMQCTKDU.mjs} +23 -55
- package/dist/esm/chunk-YMQCTKDU.mjs.map +1 -0
- package/dist/esm/exact/client/index.d.mts +6 -4
- package/dist/esm/exact/client/index.mjs +10 -5
- package/dist/esm/exact/facilitator/index.d.mts +16 -9
- package/dist/esm/exact/facilitator/index.mjs +36 -14
- package/dist/esm/exact/facilitator/index.mjs.map +1 -1
- package/dist/esm/exact/server/index.mjs +1 -1
- package/dist/esm/exact/v1/client/index.d.mts +2 -1
- package/dist/esm/exact/v1/client/index.mjs +5 -2
- package/dist/esm/exact/v1/facilitator/index.d.mts +11 -5
- package/dist/esm/exact/v1/facilitator/index.mjs +5 -2
- package/dist/esm/index.d.mts +113 -7
- package/dist/esm/index.mjs +53 -7
- package/dist/esm/index.mjs.map +1 -1
- package/dist/esm/permit2-DhJRUcgY.d.mts +729 -0
- package/dist/esm/rpc-DULZzRne.d.mts +13 -0
- package/dist/esm/scheme-DtbSS4Fk.d.mts +307 -0
- package/dist/esm/scheme-gtqAIYPJ.d.mts +47 -0
- package/dist/esm/signer-tYS6Y46X.d.mts +170 -0
- package/dist/esm/storage-6W5MO46W.d.mts +50 -0
- package/dist/esm/storage-sZ1CDS4P.d.mts +81 -0
- package/dist/esm/types-CF8P2-NM.d.mts +180 -0
- package/dist/esm/upto/client/index.d.mts +5 -3
- package/dist/esm/upto/client/index.mjs +9 -4
- package/dist/esm/upto/facilitator/index.d.mts +2 -1
- package/dist/esm/upto/facilitator/index.mjs +17 -9
- package/dist/esm/upto/facilitator/index.mjs.map +1 -1
- package/dist/esm/upto/server/index.mjs +1 -1
- package/dist/esm/v1/index.d.mts +2 -1
- package/dist/esm/v1/index.mjs +5 -2
- package/package.json +5 -5
- package/dist/esm/chunk-C4ZQMS77.mjs.map +0 -1
- package/dist/esm/chunk-CRT6YNY5.mjs.map +0 -1
- package/dist/esm/chunk-ERK2ZPOY.mjs.map +0 -1
- package/dist/esm/chunk-JII456TS.mjs.map +0 -1
- package/dist/esm/chunk-WKBC5YMI.mjs.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../src/batch-settlement/server/index.ts","../../../../src/batch-settlement/server/scheme.ts","../../../../src/batch-settlement/utils.ts","../../../../src/batch-settlement/constants.ts","../../../../src/utils.ts","../../../../src/batch-settlement/authorizerSigner.ts","../../../../src/batch-settlement/server/channelManager.ts","../../../../src/shared/defaultAssets.ts","../../../../src/batch-settlement/server/storage.ts","../../../../src/batch-settlement/server/verify.ts","../../../../src/batch-settlement/types.ts","../../../../src/batch-settlement/facilitator/utils.ts","../../../../src/multicall.ts","../../../../src/batch-settlement/errors.ts","../../../../src/batch-settlement/server/utils.ts","../../../../src/batch-settlement/server/settle.ts"],"sourcesContent":["export { BatchSettlementEvmScheme } from \"./scheme\";\nexport type { BatchSettlementEvmSchemeServerConfig, BatchSettlementRequestContext } from \"./scheme\";\nexport type { AuthorizerSigner } from \"../types\";\nexport { InMemoryChannelStorage } from \"./storage\";\nexport type { Channel, ChannelStorage, ChannelUpdateResult, PendingRequest } from \"./storage\";\nexport { BatchSettlementChannelManager } from \"./channelManager\";\nexport type {\n ChannelManagerConfig,\n AutoSettlementConfig,\n AutoSettlementContext,\n ClaimChannelSelector,\n ClaimOptions,\n ClaimResult,\n SettleResult,\n RefundResult,\n} from \"./channelManager\";\n","import {\n AssetAmount,\n Network,\n PaymentPayload,\n PaymentRequirements,\n Price,\n SchemeNetworkServer,\n SchemeServerHooks,\n MoneyParser,\n} from \"@payai/x402/types\";\nimport type { DeepReadonly } from \"@payai/x402/types\";\nimport type { SettleContext, SettleResultContext } from \"@payai/x402/server\";\nimport { convertToTokenAmount, numberToDecimalString } from \"@payai/x402/utils\";\nimport type { FacilitatorClient } from \"@payai/x402/server\";\nimport { getAddress } from \"viem\";\nimport { BatchSettlementChannelManager } from \"./channelManager\";\nimport { getDefaultAsset } from \"../../shared/defaultAssets\";\nimport type { AuthorizerSigner } from \"../types\";\nimport { BATCH_SETTLEMENT_SCHEME, MIN_WITHDRAW_DELAY } from \"../constants\";\nimport { InMemoryChannelStorage, ChannelStorage, type Channel } from \"./storage\";\nimport {\n handleAfterVerify,\n handleBeforeVerify,\n handleEnrichPaymentRequiredResponse,\n handleVerifyFailure,\n handleVerifiedPaymentCanceled,\n} from \"./verify\";\nimport {\n handleAfterSettle,\n handleBeforeSettle,\n handleEnrichSettlementPayload,\n handleEnrichSettlementResponse,\n handleSettleFailure,\n} from \"./settle\";\n\nexport interface BatchSettlementEvmSchemeServerConfig {\n storage?: ChannelStorage;\n receiverAuthorizerSigner?: AuthorizerSigner;\n withdrawDelay?: number;\n onchainStateTtlMs?: number;\n}\n\nexport interface BatchSettlementRequestContext {\n channelId?: string;\n pendingId?: string;\n channelSnapshot?: Channel;\n localVerify?: boolean;\n}\n\n/**\n * Server-side implementation of the `batch-settlement` scheme for EVM networks.\n */\nexport class BatchSettlementEvmScheme implements SchemeNetworkServer {\n readonly scheme = BATCH_SETTLEMENT_SCHEME;\n readonly schemeHooks: SchemeServerHooks;\n\n private readonly requestContexts = new WeakMap<\n DeepReadonly<PaymentPayload>,\n BatchSettlementRequestContext\n >();\n private moneyParsers: MoneyParser[] = [];\n private readonly storage: ChannelStorage;\n private readonly receiverAuthorizerSigner: AuthorizerSigner | undefined;\n private readonly receiverAddress: `0x${string}`;\n private readonly withdrawDelay: number;\n private readonly onchainStateTtlMs: number;\n\n /**\n * Constructs a batched server scheme.\n *\n * @param receiverAddress - The server's receiver address (payTo).\n * @param config - Optional configuration for storage, receiver-authorizer signer, and withdraw delay.\n */\n constructor(receiverAddress: `0x${string}`, config?: BatchSettlementEvmSchemeServerConfig) {\n this.receiverAddress = receiverAddress;\n this.storage = config?.storage ?? new InMemoryChannelStorage();\n this.receiverAuthorizerSigner = config?.receiverAuthorizerSigner;\n this.withdrawDelay = config?.withdrawDelay ?? MIN_WITHDRAW_DELAY;\n this.onchainStateTtlMs =\n config?.onchainStateTtlMs ?? defaultOnchainStateTtlMs(this.withdrawDelay);\n this.schemeHooks = {\n onBeforeVerify: ctx => handleBeforeVerify(this, ctx),\n onAfterVerify: ctx => handleAfterVerify(this, ctx),\n onBeforeSettle: ctx => handleBeforeSettle(this, ctx),\n onAfterSettle: ctx => handleAfterSettle(this, ctx),\n onVerifyFailure: ctx => handleVerifyFailure(this, ctx),\n onSettleFailure: ctx => handleSettleFailure(this, ctx),\n onVerifiedPaymentCanceled: ctx => handleVerifiedPaymentCanceled(this, ctx),\n };\n }\n\n /**\n * Adds server-owned settlement fields before facilitator settlement.\n *\n * @param ctx - Settlement context for the current payment.\n * @returns Additive payload fields, or nothing when no enrichment is needed.\n */\n enrichSettlementPayload = (ctx: SettleContext): Promise<Record<string, unknown> | void> =>\n handleEnrichSettlementPayload(this, ctx);\n\n /**\n * Adds corrective channel state to payment-required responses when available.\n *\n * @param ctx - Payment-required response context for the current request.\n * @returns Updated payment requirements, or nothing when no enrichment is needed.\n */\n enrichPaymentRequiredResponse = (\n ctx: Parameters<typeof handleEnrichPaymentRequiredResponse>[1],\n ): Promise<PaymentRequirements[] | void> => handleEnrichPaymentRequiredResponse(this, ctx);\n\n /**\n * Adds server-owned extra fields after facilitator settlement.\n *\n * @param ctx - Settlement result context for the current payment.\n * @returns Additive response extra fields, or nothing when no enrichment is needed.\n */\n enrichSettlementResponse = (ctx: SettleResultContext): Promise<Record<string, unknown> | void> =>\n handleEnrichSettlementResponse(this, ctx);\n\n /**\n * Merges batch-settlement state into the current request context.\n *\n * @param payload - Request-scoped payment payload object.\n * @param context - Partial context fields to merge.\n */\n mergeRequestContext(\n payload: DeepReadonly<PaymentPayload>,\n context: BatchSettlementRequestContext,\n ): void {\n this.requestContexts.set(payload, {\n ...this.requestContexts.get(payload),\n ...context,\n });\n }\n\n /**\n * Reads batch-settlement state for the current request without clearing it.\n *\n * @param payload - Request-scoped payment payload object.\n * @returns Request context, if one was recorded.\n */\n readRequestContext(\n payload: DeepReadonly<PaymentPayload>,\n ): BatchSettlementRequestContext | undefined {\n return this.requestContexts.get(payload);\n }\n\n /**\n * Reads and clears batch-settlement state for the current request.\n *\n * @param payload - Request-scoped payment payload object.\n * @returns Request context, if one was recorded.\n */\n takeRequestContext(\n payload: DeepReadonly<PaymentPayload>,\n ): BatchSettlementRequestContext | undefined {\n const context = this.requestContexts.get(payload);\n this.requestContexts.delete(payload);\n return context;\n }\n\n /**\n * Stores a channel snapshot for the current settlement request.\n *\n * @param payload - Request-scoped payment payload object.\n * @param channel - Channel state to use during response enrichment.\n */\n rememberChannelSnapshot(payload: DeepReadonly<PaymentPayload>, channel: Channel): void {\n this.mergeRequestContext(payload, {\n channelId: channel.channelId,\n channelSnapshot: channel,\n });\n }\n\n /**\n * Reads and clears a channel snapshot for the current settlement request.\n *\n * @param payload - Request-scoped payment payload object.\n * @returns Stored channel state, if one was recorded.\n */\n takeChannelSnapshot(payload: DeepReadonly<PaymentPayload>): Channel | undefined {\n return this.takeRequestContext(payload)?.channelSnapshot;\n }\n\n /**\n * Clears this request's pending reservation without touching newer reservations.\n *\n * @param payload - Request-scoped payment payload object.\n */\n async clearPendingRequest(payload: DeepReadonly<PaymentPayload>): Promise<void> {\n const context = this.takeRequestContext(payload);\n if (!context?.channelId || !context.pendingId) {\n return;\n }\n\n await this.storage.updateChannel(context.channelId, current => {\n if (!current || current.pendingRequest?.pendingId !== context.pendingId) {\n return current;\n }\n\n if (!context.channelSnapshot) {\n return undefined;\n }\n\n return {\n ...current,\n pendingRequest: undefined,\n };\n });\n }\n\n /**\n * Registers a custom money parser for converting price strings to token amounts.\n *\n * @param parser - A parser function to try before the default USD→token conversion.\n * @returns `this` for chaining.\n */\n registerMoneyParser(parser: MoneyParser): BatchSettlementEvmScheme {\n this.moneyParsers.push(parser);\n return this;\n }\n\n /**\n * Resolves a human-readable price (e.g. `\"$0.01\"`) into an onchain token amount.\n *\n * @param price - A price string, number, or explicit {@link AssetAmount}.\n * @param network - CAIP-2 network identifier for looking up the default asset.\n * @returns Token amount with asset address and metadata.\n */\n async parsePrice(price: Price, network: Network): Promise<AssetAmount> {\n if (typeof price === \"object\" && price !== null && \"amount\" in price) {\n if (!price.asset) {\n throw new Error(`Asset address must be specified for AssetAmount on network ${network}`);\n }\n return {\n amount: price.amount,\n asset: price.asset,\n extra: price.extra || {},\n };\n }\n\n const amount = this.parseMoneyToDecimal(price);\n\n for (const parser of this.moneyParsers) {\n const result = await parser(amount, network);\n if (result !== null) {\n return result;\n }\n }\n\n return this.defaultMoneyConversion(amount, network);\n }\n\n /**\n * Injects batched-specific fields into the payment requirements returned to\n * the client (receiverAuthorizer, withdrawDelay, EIP-712 domain info).\n *\n * @param paymentRequirements - Base payment requirements from the middleware.\n * @param supportedKind - Matched scheme/network kind (extra may contain overrides).\n * @param supportedKind.x402Version - Protocol version from the matched kind.\n * @param supportedKind.scheme - Scheme name from the matched kind.\n * @param supportedKind.network - Network identifier from the matched kind.\n * @param supportedKind.extra - Optional extra fields on the matched kind.\n * @param _extensionKeys - Extension keys (unused).\n * @returns Enhanced payment requirements with batched fields in `extra`.\n */\n async enhancePaymentRequirements(\n paymentRequirements: PaymentRequirements,\n supportedKind: {\n x402Version: number;\n scheme: string;\n network: Network;\n extra?: Record<string, unknown>;\n },\n _extensionKeys: string[],\n ): Promise<PaymentRequirements> {\n void _extensionKeys;\n\n const assetInfo = getDefaultAsset(paymentRequirements.network as Network);\n\n const receiverAuthorizer =\n this.receiverAuthorizerSigner?.address ??\n (typeof supportedKind.extra?.receiverAuthorizer === \"string\"\n ? supportedKind.extra.receiverAuthorizer\n : undefined);\n\n if (\n !receiverAuthorizer ||\n getAddress(receiverAuthorizer) === \"0x0000000000000000000000000000000000000000\"\n ) {\n throw new Error(\"Payment requirements must include a non-zero extra.receiverAuthorizer\");\n }\n\n return {\n ...paymentRequirements,\n extra: {\n ...paymentRequirements.extra,\n receiverAuthorizer: getAddress(receiverAuthorizer),\n withdrawDelay: this.withdrawDelay,\n name: assetInfo.name,\n version: assetInfo.version,\n assetTransferMethod:\n paymentRequirements.extra?.assetTransferMethod ?? assetInfo.assetTransferMethod,\n },\n };\n }\n\n /**\n * Returns the underlying channel storage instance.\n *\n * @returns The configured {@link ChannelStorage} backend.\n */\n getStorage(): ChannelStorage {\n return this.storage;\n }\n\n /**\n * Returns the server's receiver address.\n *\n * @returns Receiver wallet address for the payment channel.\n */\n getReceiverAddress(): `0x${string}` {\n return this.receiverAddress;\n }\n\n /**\n * Returns the configured withdraw delay (seconds).\n *\n * @returns Withdraw delay in seconds before uncooperative withdrawal is allowed.\n */\n getWithdrawDelay(): number {\n return this.withdrawDelay;\n }\n\n /**\n * Returns how long mirrored onchain channel state is trusted for local voucher verification.\n *\n * @returns Freshness window in milliseconds.\n */\n getOnchainStateTtlMs(): number {\n return this.onchainStateTtlMs;\n }\n\n /**\n * Returns the receiver-authorizer signer, if configured.\n *\n * @returns Receiver-authorizer signer, or `undefined` when not set.\n */\n getReceiverAuthorizerSigner(): AuthorizerSigner | undefined {\n return this.receiverAuthorizerSigner;\n }\n\n /**\n * Creates a {@link BatchSettlementChannelManager} pre-configured with this scheme's\n * receiver, default token for the given network, and the provided facilitator.\n *\n * @param facilitator - Facilitator client for submitting onchain claims/settlements.\n * @param network - CAIP-2 network identifier (e.g. `\"eip155:84532\"`).\n * @returns A ready-to-use channel manager.\n */\n createChannelManager(\n facilitator: FacilitatorClient,\n network: Network,\n ): BatchSettlementChannelManager {\n const token = getDefaultAsset(network).address as `0x${string}`;\n return new BatchSettlementChannelManager({\n scheme: this,\n facilitator,\n receiver: this.receiverAddress,\n token,\n network,\n });\n }\n\n /**\n * Parses a human-readable money string (e.g. `\"$1.50\"`) into a decimal number.\n *\n * @param money - Money string (may include `$`) or numeric amount.\n * @returns Parsed finite number.\n */\n private parseMoneyToDecimal(money: string | number): number {\n if (typeof money === \"number\") {\n return money;\n }\n\n const cleanMoney = money.replace(/^\\$/, \"\").trim();\n const amount = parseFloat(cleanMoney);\n\n if (isNaN(amount)) {\n throw new Error(`Invalid money format: ${money}`);\n }\n\n return amount;\n }\n\n /**\n * Converts a decimal dollar amount to the network's default token amount.\n *\n * @param amount - Decimal amount in display units.\n * @param network - Target chain/network for default asset resolution.\n * @returns {@link AssetAmount} with integer token amount, contract address, and metadata.\n */\n private defaultMoneyConversion(amount: number, network: Network): AssetAmount {\n const assetInfo = getDefaultAsset(network);\n const tokenAmount = convertToTokenAmount(numberToDecimalString(amount), assetInfo.decimals);\n\n return {\n amount: tokenAmount,\n asset: assetInfo.address,\n extra: {\n name: assetInfo.name,\n version: assetInfo.version,\n },\n };\n }\n}\n\n/**\n * Derives a reasonable onchain state freshness window from the channel withdraw delay.\n *\n * @param withdrawDelaySeconds - Onchain withdraw delay for the channel, in seconds.\n * @returns TTL in milliseconds, clamped between 30 seconds and 5 minutes.\n */\nfunction defaultOnchainStateTtlMs(withdrawDelaySeconds: number): number {\n const withdrawDelayMs = Math.max(0, withdrawDelaySeconds) * 1000;\n return Math.min(5 * 60 * 1000, Math.max(30 * 1000, Math.floor(withdrawDelayMs / 3)));\n}\n","import { getAddress, hashTypedData } from \"viem\";\nimport { BATCH_SETTLEMENT_ADDRESS, BATCH_SETTLEMENT_DOMAIN, channelConfigTypes } from \"./constants\";\nimport type { ChannelConfig } from \"./types\";\nimport { getEvmChainId } from \"../utils\";\n\n/**\n * Computes the chain-bound channel id from a {@link ChannelConfig} struct.\n *\n * @param config - The immutable channel configuration.\n * @param networkOrChainId - CAIP-2 network identifier or numeric EVM chain id.\n * @returns The `bytes32` channel id as a hex string.\n */\nexport function computeChannelId(\n config: ChannelConfig,\n networkOrChainId: string | number,\n): `0x${string}` {\n const chainId =\n typeof networkOrChainId === \"number\" ? networkOrChainId : getEvmChainId(networkOrChainId);\n return hashTypedData({\n domain: getBatchSettlementEip712Domain(chainId),\n types: channelConfigTypes,\n primaryType: \"ChannelConfig\",\n message: {\n payer: config.payer,\n payerAuthorizer: config.payerAuthorizer,\n receiver: config.receiver,\n receiverAuthorizer: config.receiverAuthorizer,\n token: config.token,\n withdrawDelay: config.withdrawDelay,\n salt: config.salt,\n },\n });\n}\n\n/**\n * Returns the full EIP-712 domain for the batch-settlement contract on the given chain.\n *\n * @param chainId - Numeric EVM chain id.\n * @returns EIP-712 domain with `name`, `version`, `chainId`, and checksummed `verifyingContract`.\n */\nexport function getBatchSettlementEip712Domain(chainId: number) {\n return {\n ...BATCH_SETTLEMENT_DOMAIN,\n chainId,\n verifyingContract: getAddress(BATCH_SETTLEMENT_ADDRESS),\n } as const;\n}\n","import { keccak256, toBytes } from \"viem\";\n\n/** Scheme identifier for the batch-settlement payment scheme. */\nexport const BATCH_SETTLEMENT_SCHEME = \"batch-settlement\" as const;\n\n/** Deployed address of the x402BatchSettlement contract. */\nexport const BATCH_SETTLEMENT_ADDRESS = \"0x4020074e9dF2ce1deE5A9C1b5c3f541D02a10003\" as const;\n\n/** Deployed address of the ERC3009DepositCollector contract. */\nexport const ERC3009_DEPOSIT_COLLECTOR_ADDRESS =\n \"0x4020806089470a89826cB9fB1f4059150b550004\" as const;\n\n/** Deployed address of the Permit2DepositCollector contract. */\nexport const PERMIT2_DEPOSIT_COLLECTOR_ADDRESS =\n \"0x4020425FAf3B746C082C2f942b4E5159887B0005\" as const;\n\n/** Minimum withdraw delay in seconds (15 minutes), matching the onchain constant. */\nexport const MIN_WITHDRAW_DELAY = 900;\n\n/** Maximum withdraw delay in seconds (30 days), matching the onchain constant. */\nexport const MAX_WITHDRAW_DELAY = 2_592_000;\n\n/** EIP-712 domain fields shared across all batch-settlement typed-data signatures. */\nexport const BATCH_SETTLEMENT_DOMAIN = {\n name: \"x402 Batch Settlement\",\n version: \"1\",\n} as const;\n\n/** EIP-712 type hash for channel identity. */\nexport const CHANNEL_CONFIG_TYPEHASH = keccak256(\n toBytes(\n \"ChannelConfig(address payer,address payerAuthorizer,address receiver,address receiverAuthorizer,address token,uint40 withdrawDelay,bytes32 salt)\",\n ),\n);\n\n/** EIP-712 type definition for a channel configuration. */\nexport const channelConfigTypes = {\n ChannelConfig: [\n { name: \"payer\", type: \"address\" },\n { name: \"payerAuthorizer\", type: \"address\" },\n { name: \"receiver\", type: \"address\" },\n { name: \"receiverAuthorizer\", type: \"address\" },\n { name: \"token\", type: \"address\" },\n { name: \"withdrawDelay\", type: \"uint40\" },\n { name: \"salt\", type: \"bytes32\" },\n ],\n} as const;\n\n/** EIP-712 type definition for a cumulative voucher: `Voucher(bytes32 channelId, uint128 maxClaimableAmount)`. */\nexport const voucherTypes = {\n Voucher: [\n { name: \"channelId\", type: \"bytes32\" },\n { name: \"maxClaimableAmount\", type: \"uint128\" },\n ],\n} as const;\n\n/** EIP-712 type definition for cooperative refund: `Refund(bytes32 channelId, uint256 nonce, uint128 amount)`. */\nexport const refundTypes = {\n Refund: [\n { name: \"channelId\", type: \"bytes32\" },\n { name: \"nonce\", type: \"uint256\" },\n { name: \"amount\", type: \"uint128\" },\n ],\n} as const;\n\n/** EIP-712 type definitions for a receiver-authorizer claim batch (nested ClaimEntry). */\nexport const claimBatchTypes = {\n ClaimBatch: [{ name: \"claims\", type: \"ClaimEntry[]\" }],\n ClaimEntry: [\n { name: \"channelId\", type: \"bytes32\" },\n { name: \"maxClaimableAmount\", type: \"uint128\" },\n { name: \"totalClaimed\", type: \"uint128\" },\n ],\n} as const;\n\n/** EIP-712 type definition for ERC-3009 `ReceiveWithAuthorization` (used for gasless deposits). */\nexport const receiveAuthorizationTypes = {\n ReceiveWithAuthorization: [\n { name: \"from\", type: \"address\" },\n { name: \"to\", type: \"address\" },\n { name: \"value\", type: \"uint256\" },\n { name: \"validAfter\", type: \"uint256\" },\n { name: \"validBefore\", type: \"uint256\" },\n { name: \"nonce\", type: \"bytes32\" },\n ],\n} as const;\n\n/** Permit2 typed data for channel-bound batch deposits. */\nexport const batchPermit2WitnessTypes = {\n PermitWitnessTransferFrom: [\n { name: \"permitted\", type: \"TokenPermissions\" },\n { name: \"spender\", type: \"address\" },\n { name: \"nonce\", type: \"uint256\" },\n { name: \"deadline\", type: \"uint256\" },\n { name: \"witness\", type: \"DepositWitness\" },\n ],\n TokenPermissions: [\n { name: \"token\", type: \"address\" },\n { name: \"amount\", type: \"uint256\" },\n ],\n DepositWitness: [{ name: \"channelId\", type: \"bytes32\" }],\n} as const;\n","import { toHex } from \"viem\";\n\n/**\n * Extract chain ID from a CAIP-2 network identifier (eip155:CHAIN_ID).\n *\n * @param network - The network identifier in CAIP-2 format (e.g., \"eip155:8453\")\n * @returns The numeric chain ID\n * @throws Error if the network format is invalid\n */\nexport function getEvmChainId(network: string): number {\n if (network.startsWith(\"eip155:\")) {\n const idStr = network.split(\":\")[1];\n const chainId = parseInt(idStr, 10);\n if (isNaN(chainId)) {\n throw new Error(`Invalid CAIP-2 chain ID: ${network}`);\n }\n return chainId;\n }\n\n throw new Error(`Unsupported network format: ${network} (expected eip155:CHAIN_ID)`);\n}\n\n/**\n * Get the crypto object from the global scope.\n *\n * @returns The crypto object\n * @throws Error if crypto API is not available\n */\nfunction getCrypto(): Crypto {\n const cryptoObj = globalThis.crypto as Crypto | undefined;\n if (!cryptoObj) {\n throw new Error(\"Crypto API not available\");\n }\n return cryptoObj;\n}\n\n/**\n * Create a random 32-byte nonce for EIP-3009 authorization.\n *\n * @returns A hex-encoded 32-byte nonce\n */\nexport function createNonce(): `0x${string}` {\n return toHex(getCrypto().getRandomValues(new Uint8Array(32)));\n}\n\n/**\n * Creates a random 256-bit nonce for Permit2.\n * Permit2 uses uint256 nonces (not bytes32 like EIP-3009).\n *\n * @returns A string representation of the random nonce\n */\nexport function createPermit2Nonce(): string {\n const randomBytes = getCrypto().getRandomValues(new Uint8Array(32));\n return BigInt(toHex(randomBytes)).toString();\n}\n","import type { AuthorizerSigner, BatchSettlementVoucherClaim } from \"./types\";\nimport { claimBatchTypes, refundTypes } from \"./constants\";\nimport { computeChannelId, getBatchSettlementEip712Domain } from \"./utils\";\nimport { getEvmChainId } from \"../utils\";\n\n/**\n * Signs a `ClaimBatch` EIP-712 digest for `claimWithSignature()`.\n *\n * @param signer - Authorizer signer holding the `receiverAuthorizer` key.\n * @param claims - Voucher claims to include in the batch.\n * @param network - CAIP-2 network identifier (e.g. `\"eip155:84532\"`).\n * @returns EIP-712 signature over `ClaimBatch(ClaimEntry[] claims)`.\n */\nexport async function signClaimBatch(\n signer: AuthorizerSigner,\n claims: BatchSettlementVoucherClaim[],\n network: string,\n): Promise<`0x${string}`> {\n const chainId = getEvmChainId(network);\n\n const claimEntries = claims.map(c => ({\n channelId: computeChannelId(c.voucher.channel, chainId),\n maxClaimableAmount: BigInt(c.voucher.maxClaimableAmount),\n totalClaimed: BigInt(c.totalClaimed),\n }));\n\n return signer.signTypedData({\n domain: getBatchSettlementEip712Domain(chainId),\n types: claimBatchTypes,\n primaryType: \"ClaimBatch\",\n message: {\n claims: claimEntries,\n },\n });\n}\n\n/**\n * Signs a `Refund` EIP-712 digest for `refundWithSignature()`.\n *\n * @param signer - Authorizer signer holding the `receiverAuthorizer` key.\n * @param channelId - Channel to authorize refund for.\n * @param amount - Refund amount (capped to unclaimed escrow onchain).\n * @param nonce - Must match onchain `refundNonce(channelId)`.\n * @param network - CAIP-2 network identifier (e.g. `\"eip155:84532\"`).\n * @returns EIP-712 signature over `Refund(channelId, nonce, amount)`.\n */\nexport async function signRefund(\n signer: AuthorizerSigner,\n channelId: `0x${string}`,\n amount: string,\n nonce: string,\n network: string,\n): Promise<`0x${string}`> {\n const chainId = getEvmChainId(network);\n\n return signer.signTypedData({\n domain: getBatchSettlementEip712Domain(chainId),\n types: refundTypes,\n primaryType: \"Refund\",\n message: {\n channelId,\n nonce: BigInt(nonce),\n amount: BigInt(amount),\n },\n });\n}\n","import type {\n Network,\n PaymentPayload,\n PaymentRequirements,\n SettleResponse,\n} from \"@payai/x402/types\";\nimport type { FacilitatorClient } from \"@payai/x402/server\";\nimport type { BatchSettlementVoucherClaim } from \"../types\";\nimport type { BatchSettlementEvmScheme } from \"./scheme\";\nimport { computeChannelId } from \"../utils\";\nimport { BATCH_SETTLEMENT_SCHEME } from \"../constants\";\nimport { signClaimBatch, signRefund } from \"../authorizerSigner\";\nimport type { Channel } from \"./storage\";\n\nexport interface ChannelManagerConfig {\n scheme: BatchSettlementEvmScheme;\n facilitator: FacilitatorClient;\n receiver: `0x${string}`;\n token: `0x${string}`;\n network: Network;\n}\n\nexport type ClaimChannelSelector = (\n channels: Channel[],\n context: AutoSettlementContext,\n) => Channel[] | Promise<Channel[]>;\n\nexport interface ClaimOptions {\n maxClaimsPerBatch?: number;\n idleSecs?: number;\n selectClaimChannels?: ClaimChannelSelector;\n}\n\nexport interface AutoSettlementConfig {\n claimIntervalSecs?: number;\n settleIntervalSecs?: number;\n refundIntervalSecs?: number;\n maxClaimsPerBatch?: number;\n selectClaimChannels?: ClaimChannelSelector;\n shouldSettle?: (context: AutoSettlementContext) => boolean | Promise<boolean>;\n selectRefundChannels?: (\n channels: Channel[],\n context: AutoSettlementContext,\n ) => Channel[] | Promise<Channel[]>;\n onClaim?: (result: ClaimResult) => void;\n onSettle?: (result: SettleResult) => void;\n onRefund?: (result: RefundResult) => void;\n onError?: (error: unknown) => void;\n}\n\nexport interface AutoSettlementContext {\n now: number;\n lastClaimTime: number;\n lastSettleTime: number;\n pendingSettle: boolean;\n}\n\nexport interface ClaimResult {\n vouchers: number;\n transaction: string;\n}\n\nexport interface SettleResult {\n transaction: string;\n}\n\nexport interface RefundResult {\n channel: string;\n transaction: string;\n}\n\ntype AutoJob = \"claim\" | \"settle\" | \"refund\";\n\nconst AUTO_JOB_PRIORITY: AutoJob[] = [\"claim\", \"settle\", \"refund\"];\n\n/**\n * Formats a `Facilitator.settle()` failure into a human-readable error message.\n *\n * @param operation - Operation label (e.g. `\"Claim\"`, `\"Settle\"`, `\"Refund\"`).\n * @param response - The failed settle response.\n * @returns Error message including reason and (when available) facilitator-provided detail.\n */\nfunction formatFacilitatorFailure(operation: string, response: SettleResponse): string {\n return `${operation} failed: ${response.errorReason ?? \"unknown\"} — ${response.errorMessage ?? \"\"}`;\n}\n\n/**\n * Checks whether a channel has a non-expired payer request reservation.\n *\n * @param channel - Channel state to inspect.\n * @param now - Current wall-clock time in milliseconds.\n * @returns Whether the channel is busy with a live pending request.\n */\nfunction hasLivePendingRequest(channel: Channel, now = Date.now()): boolean {\n return channel.pendingRequest !== undefined && channel.pendingRequest.expiresAt > now;\n}\n\n/**\n * Manages the server-side channel lifecycle for the `batch-settlement` scheme:\n * batch claiming of vouchers, settlement of claimed funds, and cooperative refund.\n *\n * Provides one-shot operations (`claim()`, `settle()`, `claimAndSettle()`,\n * `refundIdleChannels()`) and an optional interval runner.\n */\nexport class BatchSettlementChannelManager {\n private readonly scheme: BatchSettlementEvmScheme;\n private readonly facilitator: FacilitatorClient;\n private readonly receiver: `0x${string}`;\n private readonly token: `0x${string}`;\n private readonly network: Network;\n\n private timers: Partial<Record<AutoJob, ReturnType<typeof setInterval>>> = {};\n private lastClaimTime = 0;\n private lastSettleTime = 0;\n private pendingSettle = false;\n private running = false;\n private pendingJobs = new Set<AutoJob>();\n private drainingJobs = false;\n private autoSettleConfig: AutoSettlementConfig = {};\n\n /**\n * Creates a new channel manager.\n *\n * @param config - Manager configuration: scheme, facilitator, receiver, token, network.\n */\n constructor(config: ChannelManagerConfig) {\n this.scheme = config.scheme;\n this.facilitator = config.facilitator;\n this.receiver = config.receiver;\n this.token = config.token;\n this.network = config.network;\n }\n\n /**\n * Collects claimable vouchers and submits them in batches to the facilitator via `claim()`.\n *\n * @param opts - Optional claim execution and target selection options.\n * @param opts.maxClaimsPerBatch - Max vouchers per facilitator `claim` batch.\n * @param opts.idleSecs - When set, only include channels idle for at least this many seconds.\n * @param opts.selectClaimChannels - Optional selector for choosing channels before claimability checks.\n * @returns Array of claim results (one per batch).\n */\n async claim(opts?: ClaimOptions): Promise<ClaimResult[]> {\n const channels = await this.selectClaimTargets(opts);\n return this.claimFromChannels(channels, {\n maxClaimsPerBatch: opts?.maxClaimsPerBatch ?? 100,\n ...(opts?.idleSecs !== undefined ? { idleSecs: opts.idleSecs } : {}),\n });\n }\n\n /**\n * Transfers claimed (but unsettled) funds to the receiver by calling `settle(receiver, token)`.\n *\n * @returns Settle result with the transaction hash.\n */\n async settle(): Promise<SettleResult> {\n const paymentPayload = this.buildSettlePaymentPayload();\n const requirements = this.buildPaymentRequirements();\n\n const response = await this.facilitator.settle(paymentPayload, requirements);\n if (!response.success) {\n throw new Error(formatFacilitatorFailure(\"Settle\", response));\n }\n\n this.pendingSettle = false;\n return { transaction: response.transaction };\n }\n\n /**\n * Convenience: claims all eligible vouchers then settles in one call.\n *\n * @param opts - Optional claim execution and target selection options.\n * @param opts.maxClaimsPerBatch - Max vouchers per claim batch before settling.\n * @param opts.idleSecs - When set, only include channels idle for at least this many seconds.\n * @param opts.selectClaimChannels - Optional selector for choosing channels before claimability checks.\n * @returns Combined claim and settle results.\n */\n async claimAndSettle(\n opts?: ClaimOptions,\n ): Promise<{ claims: ClaimResult[]; settle?: SettleResult }> {\n const claims = await this.claim(opts);\n let settleResult: SettleResult | undefined;\n if (claims.length > 0) {\n settleResult = await this.settle();\n }\n return { claims, settle: settleResult };\n }\n\n /**\n * Initiates cooperative refunds for one or more channels.\n *\n * @param channelIds - Specific channels to refund; defaults to all sessions.\n * @returns One result per successfully refunded channel.\n */\n async refund(channelIds?: string[]): Promise<RefundResult[]> {\n const storage = this.scheme.getStorage();\n const channels = await storage.list();\n\n const now = Date.now();\n const targets = (\n channelIds\n ? channels.filter(s =>\n channelIds.some(id => id.toLowerCase() === s.channelId.toLowerCase()),\n )\n : channels\n ).filter(channel => !hasLivePendingRequest(channel, now));\n\n if (targets.length === 0) {\n return [];\n }\n\n return this.refundChannels(targets);\n }\n\n /**\n * Refunds idle channels with non-zero balances.\n *\n * @param opts - Idle refund options.\n * @param opts.idleSecs - Minimum seconds since the last request.\n * @returns One result per successfully refunded channel.\n */\n async refundIdleChannels(opts: { idleSecs: number }): Promise<RefundResult[]> {\n const channels = await this.getIdleChannelsForRefund(opts.idleSecs);\n return this.refundChannels(channels);\n }\n\n /**\n * Collects vouchers that are eligible for onchain claiming.\n *\n * A voucher is claimable when its `chargedCumulativeAmount` exceeds what has already\n * been claimed onchain. An optional idle filter skips sessions that received a\n * request within the last `idleSecs` seconds.\n *\n * @param opts - Optional filtering: `idleSecs` to only return idle channels.\n * @param opts.idleSecs - Minimum seconds since last request for a channel to be included.\n * @returns Array of {@link BatchSettlementVoucherClaim} entries for batch submission.\n */\n async getClaimableVouchers(opts?: { idleSecs?: number }): Promise<BatchSettlementVoucherClaim[]> {\n const channels = await this.scheme.getStorage().list();\n return this.getClaimableVouchersFromChannels(channels, opts);\n }\n\n /**\n * Returns channels that have a pending payer-initiated withdrawal.\n *\n * @returns All stored channel records with `withdrawRequestedAt` set.\n */\n async getWithdrawalPendingSessions(): Promise<Channel[]> {\n const channels = await this.scheme.getStorage().list();\n return channels.filter(s => s.withdrawRequestedAt > 0);\n }\n\n /**\n * Starts auto-settlement jobs for configured claim, settle, and refund intervals.\n *\n * @param config - Auto-settlement policy configuration.\n */\n start(config: AutoSettlementConfig = {}): void {\n if (this.running) {\n return;\n }\n\n const now = Date.now();\n this.lastClaimTime = now;\n this.lastSettleTime = now;\n this.running = true;\n this.autoSettleConfig = config;\n\n this.startAutoTimer(\"claim\", config.claimIntervalSecs);\n this.startAutoTimer(\"settle\", config.settleIntervalSecs);\n this.startAutoTimer(\"refund\", config.refundIntervalSecs);\n }\n\n /**\n * Stops the auto-settlement loop.\n *\n * @param opts - Stop options.\n * @param opts.flush - When true, run `claimAndSettle` before stopping.\n * @returns Resolves when the loop is stopped (and flush work completes, if requested).\n */\n async stop(opts?: { flush?: boolean }): Promise<void> {\n this.running = false;\n for (const timer of Object.values(this.timers)) {\n clearInterval(timer);\n }\n this.timers = {};\n this.pendingJobs.clear();\n\n if (opts?.flush) {\n await this.claimAndSettle({\n maxClaimsPerBatch: this.autoSettleConfig.maxClaimsPerBatch,\n selectClaimChannels: this.autoSettleConfig.selectClaimChannels,\n });\n }\n }\n\n /**\n * Refunds a single channel and removes it from storage after success.\n *\n * @param target - Channel to refund.\n * @returns Successful refund transaction.\n */\n private async refundChannel(target: Channel): Promise<RefundResult> {\n const authorizerSigner = this.scheme.getReceiverAuthorizerSigner();\n const claims = this.buildRefundClaims(target);\n\n const refundAmount = (\n BigInt(target.balance) - BigInt(target.chargedCumulativeAmount)\n ).toString();\n\n const nonce = String(target.refundNonce ?? 0);\n\n const refundAuthorizerSignature = authorizerSigner\n ? await signRefund(\n authorizerSigner,\n target.channelId as `0x${string}`,\n refundAmount,\n nonce,\n this.network,\n )\n : undefined;\n\n const claimAuthorizerSignature =\n authorizerSigner && claims.length > 0\n ? await signClaimBatch(authorizerSigner, claims, this.network)\n : undefined;\n\n const paymentPayload: PaymentPayload = {\n x402Version: 2,\n accepted: this.buildPaymentRequirements(),\n payload: {\n type: \"refund\",\n channelConfig: target.channelConfig,\n voucher: {\n channelId: target.channelId as `0x${string}`,\n maxClaimableAmount: target.signedMaxClaimable,\n signature: target.signature as `0x${string}`,\n },\n amount: refundAmount,\n refundNonce: nonce,\n claims,\n ...(refundAuthorizerSignature ? { refundAuthorizerSignature } : {}),\n ...(claimAuthorizerSignature ? { claimAuthorizerSignature } : {}),\n },\n };\n\n const response = await this.facilitator.settle(paymentPayload, this.buildPaymentRequirements());\n if (!response.success) {\n throw new Error(formatFacilitatorFailure(\"Refund\", response));\n }\n\n await this.scheme\n .getStorage()\n .updateChannel(target.channelId, current =>\n current && !hasLivePendingRequest(current) ? undefined : current,\n );\n\n return {\n channel: target.channelId,\n transaction: response.transaction,\n };\n }\n\n /**\n * Starts a recurring timer for one auto job.\n *\n * @param job - Job to enqueue when the interval fires.\n * @param intervalSecs - Timer interval in seconds.\n */\n private startAutoTimer(job: AutoJob, intervalSecs?: number): void {\n if (intervalSecs === undefined) {\n return;\n }\n\n this.timers[job] = setInterval(() => {\n this.enqueueJob(job);\n }, intervalSecs * 1000);\n }\n\n /**\n * Adds an auto job to the coalescing queue.\n *\n * @param job - Job to run.\n */\n private enqueueJob(job: AutoJob): void {\n if (!this.running) {\n return;\n }\n\n this.pendingJobs.add(job);\n if (!this.drainingJobs) {\n void this.drainJobs();\n }\n }\n\n /**\n * Drains queued auto jobs in priority order.\n */\n private async drainJobs(): Promise<void> {\n if (this.drainingJobs) {\n return;\n }\n\n this.drainingJobs = true;\n try {\n while (this.running && this.pendingJobs.size > 0) {\n const job = this.nextPendingJob();\n if (!job) {\n return;\n }\n this.pendingJobs.delete(job);\n await this.runAutoJob(job);\n }\n } finally {\n this.drainingJobs = false;\n }\n }\n\n /**\n * Returns the highest-priority queued auto job.\n *\n * @returns Next job to run.\n */\n private nextPendingJob(): AutoJob | undefined {\n return AUTO_JOB_PRIORITY.find(job => this.pendingJobs.has(job));\n }\n\n /**\n * Runs one auto job.\n *\n * @param job - Job to run.\n */\n private async runAutoJob(job: AutoJob): Promise<void> {\n switch (job) {\n case \"claim\":\n await this.runClaimJob();\n return;\n case \"settle\":\n await this.runSettleJob();\n return;\n case \"refund\":\n await this.runRefundJob();\n return;\n }\n }\n\n /**\n * Runs the claim auto job.\n */\n private async runClaimJob(): Promise<void> {\n const cfg = this.autoSettleConfig;\n try {\n const targets = await this.selectClaimTargets({\n selectClaimChannels: cfg.selectClaimChannels,\n });\n const results = await this.claimFromChannels(targets, {\n maxClaimsPerBatch: cfg.maxClaimsPerBatch ?? 100,\n });\n\n this.lastClaimTime = Date.now();\n for (const result of results) {\n cfg.onClaim?.(result);\n }\n } catch (err) {\n cfg.onError?.(err);\n }\n }\n\n /**\n * Runs the settlement auto job.\n */\n private async runSettleJob(): Promise<void> {\n const cfg = this.autoSettleConfig;\n const context = this.buildAutoSettlementContext(Date.now());\n if (!context.pendingSettle) {\n return;\n }\n\n try {\n if (cfg.shouldSettle && !(await cfg.shouldSettle(context))) {\n return;\n }\n\n const result = await this.settle();\n this.lastSettleTime = Date.now();\n cfg.onSettle?.(result);\n } catch (err) {\n cfg.onError?.(err);\n }\n }\n\n /**\n * Runs the refund auto job.\n */\n private async runRefundJob(): Promise<void> {\n const cfg = this.autoSettleConfig;\n if (!cfg.selectRefundChannels) {\n return;\n }\n\n try {\n const context = this.buildAutoSettlementContext(Date.now());\n const channels = await this.scheme.getStorage().list();\n const targets = await cfg.selectRefundChannels(channels, context);\n for (const result of await this.refundChannels(targets)) {\n cfg.onRefund?.(result);\n }\n } catch (err) {\n cfg.onError?.(err);\n }\n }\n\n /**\n * Claims vouchers from a provided channel snapshot.\n *\n * @param channels - Channels to inspect for claimable vouchers.\n * @param opts - Claim batching and filtering options.\n * @param opts.maxClaimsPerBatch - Max vouchers per facilitator claim transaction.\n * @param opts.idleSecs - Optional idle filter.\n * @returns Claim results, one per submitted batch.\n */\n private async claimFromChannels(\n channels: Channel[],\n opts: {\n maxClaimsPerBatch: number;\n idleSecs?: number;\n },\n ): Promise<ClaimResult[]> {\n const allClaims = this.getClaimableVouchersFromChannels(\n channels,\n opts.idleSecs !== undefined ? { idleSecs: opts.idleSecs } : undefined,\n );\n\n if (allClaims.length === 0) {\n return [];\n }\n\n const results: ClaimResult[] = [];\n for (let i = 0; i < allClaims.length; i += opts.maxClaimsPerBatch) {\n const batch = allClaims.slice(i, i + opts.maxClaimsPerBatch);\n const result = await this.submitClaim(batch);\n results.push(result);\n await this.updateClaimedSessions(batch);\n }\n\n if (results.length > 0) {\n this.pendingSettle = true;\n }\n\n return results;\n }\n\n /**\n * Loads stored channels and applies the configured claim selector, if any.\n *\n * @param opts - Claim options containing an optional target selector.\n * @returns The channel snapshot that should be inspected for claimable vouchers.\n */\n private async selectClaimTargets(\n opts?: Pick<ClaimOptions, \"selectClaimChannels\">,\n ): Promise<Channel[]> {\n const channels = await this.scheme.getStorage().list();\n if (!opts?.selectClaimChannels) {\n return channels;\n }\n\n const context = this.buildAutoSettlementContext(Date.now());\n return opts.selectClaimChannels(channels, context);\n }\n\n /**\n * Refunds each eligible channel independently.\n *\n * @param channels - Channels to refund.\n * @returns Successful refund results.\n */\n private async refundChannels(channels: Channel[]): Promise<RefundResult[]> {\n const results: RefundResult[] = [];\n for (const channel of channels) {\n if (hasLivePendingRequest(channel)) {\n continue;\n }\n results.push(await this.refundChannel(channel));\n }\n return results;\n }\n\n /**\n * Builds an outstanding voucher claim for a refund payload.\n *\n * @param channel - Channel being refunded.\n * @returns Claim payloads needed before refunding unclaimed balance.\n */\n private buildRefundClaims(channel: Channel): BatchSettlementVoucherClaim[] {\n if (BigInt(channel.chargedCumulativeAmount) <= BigInt(channel.totalClaimed)) {\n return [];\n }\n\n return [\n {\n voucher: {\n channel: channel.channelConfig,\n maxClaimableAmount: channel.signedMaxClaimable,\n },\n signature: channel.signature as `0x${string}`,\n totalClaimed: channel.chargedCumulativeAmount,\n },\n ];\n }\n\n /**\n * Builds the policy context passed to interval hooks.\n *\n * @param now - Current wall-clock time in milliseconds.\n * @returns Auto-settlement policy context.\n */\n private buildAutoSettlementContext(now: number): AutoSettlementContext {\n return {\n now,\n lastClaimTime: this.lastClaimTime,\n lastSettleTime: this.lastSettleTime,\n pendingSettle: this.pendingSettle,\n };\n }\n\n /**\n * Collects claimable vouchers from a provided channel snapshot.\n *\n * @param channels - Channels to inspect.\n * @param opts - Optional idle filter.\n * @param opts.idleSecs - Minimum seconds since last request.\n * @returns Claimable voucher payloads.\n */\n private getClaimableVouchersFromChannels(\n channels: Channel[],\n opts?: { idleSecs?: number },\n ): BatchSettlementVoucherClaim[] {\n const now = Date.now();\n const claims: BatchSettlementVoucherClaim[] = [];\n\n for (const c of channels) {\n if (BigInt(c.chargedCumulativeAmount) <= BigInt(c.totalClaimed)) {\n continue;\n }\n if (opts?.idleSecs !== undefined) {\n const idleMs = now - c.lastRequestTimestamp;\n if (idleMs < opts.idleSecs * 1000) {\n continue;\n }\n }\n claims.push({\n voucher: {\n channel: c.channelConfig,\n maxClaimableAmount: c.signedMaxClaimable,\n },\n signature: c.signature as `0x${string}`,\n totalClaimed: c.chargedCumulativeAmount,\n });\n }\n\n return claims;\n }\n\n /**\n * Filters idle channels that can be cooperatively refunded.\n *\n * @param channels - Channels to inspect.\n * @param idleSecs - Minimum seconds since the last request.\n * @returns Idle refundable channels.\n */\n private getIdleChannelsForRefundFromChannels(channels: Channel[], idleSecs: number): Channel[] {\n const now = Date.now();\n const idleMs = idleSecs * 1000;\n return channels.filter(c => {\n if (BigInt(c.balance) === 0n) return false;\n if (hasLivePendingRequest(c, now)) return false;\n return now - c.lastRequestTimestamp >= idleMs;\n });\n }\n\n /**\n * Returns channels that have been idle longer than `idleSecs` and still have\n * a non-zero balance (candidates for cooperative refund).\n *\n * @param idleSecs - Minimum seconds since last request for a session to count as idle.\n * @returns Channels meeting the idle and balance criteria.\n */\n private async getIdleChannelsForRefund(idleSecs: number): Promise<Channel[]> {\n const channels = await this.scheme.getStorage().list();\n return this.getIdleChannelsForRefundFromChannels(channels, idleSecs);\n }\n\n /**\n * Submits a batch of voucher claims to the facilitator.\n *\n * @param claims - Voucher claims to send in one `type: \"claim\"` payload.\n * @returns Per-batch claim summary (count and transaction hash).\n */\n private async submitClaim(claims: BatchSettlementVoucherClaim[]): Promise<ClaimResult> {\n const authorizerSigner = this.scheme.getReceiverAuthorizerSigner();\n\n const claimAuthorizerSignature = authorizerSigner\n ? await signClaimBatch(authorizerSigner, claims, this.network)\n : undefined;\n\n const paymentPayload: PaymentPayload = {\n x402Version: 2,\n accepted: this.buildPaymentRequirements(),\n payload: {\n type: \"claim\",\n claims,\n ...(claimAuthorizerSignature ? { claimAuthorizerSignature } : {}),\n },\n };\n\n const response: SettleResponse = await this.facilitator.settle(\n paymentPayload,\n this.buildPaymentRequirements(),\n );\n\n if (!response.success) {\n throw new Error(formatFacilitatorFailure(\"Claim\", response));\n }\n\n return { vouchers: claims.length, transaction: response.transaction };\n }\n\n /**\n * Builds a settlement payment payload for `settle(receiver, token)`.\n *\n * @returns Payload with `type: \"settle\"` and receiver/token fields.\n */\n private buildSettlePaymentPayload(): PaymentPayload {\n return {\n x402Version: 2,\n accepted: this.buildPaymentRequirements(),\n payload: {\n type: \"settle\",\n receiver: this.receiver,\n token: this.token,\n },\n };\n }\n\n /**\n * Builds a minimal {@link PaymentRequirements} for channel manager operations.\n *\n * @returns Requirements describing batched operations for this manager.\n */\n private buildPaymentRequirements(): PaymentRequirements {\n return {\n scheme: BATCH_SETTLEMENT_SCHEME,\n network: this.network,\n asset: this.token,\n amount: \"0\",\n payTo: this.receiver,\n maxTimeoutSeconds: 0,\n extra: {},\n };\n }\n\n /**\n * Updates session records after a successful claim submission so that\n * `getClaimableVouchers` no longer returns already-claimed vouchers.\n *\n * @param claims - Voucher claims that were included in the submitted settlement transaction.\n */\n private async updateClaimedSessions(claims: BatchSettlementVoucherClaim[]): Promise<void> {\n const storage = this.scheme.getStorage();\n for (const claim of claims) {\n const channelId = computeChannelId(claim.voucher.channel, this.network);\n const channel = await storage.get(channelId);\n if (!channel) {\n continue;\n }\n const claimedAmount = BigInt(claim.totalClaimed);\n if (claimedAmount <= BigInt(channel.totalClaimed)) {\n continue;\n }\n await storage.updateChannel(channelId, current => {\n if (!current || claimedAmount <= BigInt(current.totalClaimed)) {\n return current;\n }\n return {\n ...current,\n totalClaimed: claimedAmount.toString(),\n };\n });\n }\n }\n}\n","import type { Network } from \"@payai/x402/types\";\n\n/**\n * Base stablecoin asset configuration shared across all EVM payment schemes.\n * Contains the core fields needed to identify and convert tokens.\n */\nexport type DefaultAssetInfo = {\n /** Token contract address */\n address: string;\n /** EIP-712 domain name (must match the token's domain separator) */\n name: string;\n /** EIP-712 domain version (must match the token's domain separator) */\n version: string;\n /** Token decimal places (typically 6 for USDC) */\n decimals: number;\n};\n\n/**\n * Extended asset configuration for the exact scheme.\n * Includes transfer method hints that control client-side behaviour.\n */\nexport type ExactDefaultAssetInfo = DefaultAssetInfo & {\n /**\n * Transfer method override: `\"permit2\"` for tokens that don't support EIP-3009.\n * Omit for EIP-3009 tokens (default behaviour).\n */\n assetTransferMethod?: string;\n /**\n * Set to `true` for permit2 tokens that implement EIP-2612 `permit()`.\n * Controls whether name/version are included in `extra` so the client can\n * sign a gasless EIP-2612 permit for Permit2 approval.\n */\n supportsEip2612?: boolean;\n};\n\n/**\n * Default stablecoins indexed by CAIP-2 network identifier.\n *\n * Each network has the right to determine its own default stablecoin that can\n * be expressed as a USD string by calling servers. See DEFAULT_ASSET.md in\n * exact/server/ for how to add new chains.\n */\nexport const DEFAULT_STABLECOINS: Record<string, ExactDefaultAssetInfo> = {\n \"eip155:8453\": {\n address: \"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913\",\n name: \"USD Coin\",\n version: \"2\",\n decimals: 6,\n }, // Base mainnet USDC\n \"eip155:84532\": {\n address: \"0x036CbD53842c5426634e7929541eC2318f3dCF7e\",\n name: \"USDC\",\n version: \"2\",\n decimals: 6,\n }, // Base Sepolia USDC\n \"eip155:4326\": {\n address: \"0xFAfDdbb3FC7688494971a79cc65DCa3EF82079E7\",\n name: \"MegaUSD\",\n version: \"1\",\n decimals: 18,\n assetTransferMethod: \"permit2\",\n supportsEip2612: true,\n }, // MegaETH mainnet MegaUSD (no EIP-3009, supports EIP-2612)\n \"eip155:143\": {\n address: \"0x754704Bc059F8C67012fEd69BC8A327a5aafb603\",\n name: \"USD Coin\",\n version: \"2\",\n decimals: 6,\n }, // Monad mainnet USDC\n \"eip155:988\": {\n address: \"0x779Ded0c9e1022225f8E0630b35a9b54bE713736\",\n name: \"USDT0\",\n version: \"1\",\n decimals: 6,\n }, // Stable mainnet USDT0\n \"eip155:2201\": {\n address: \"0x78Cf24370174180738C5B8E352B6D14c83a6c9A9\",\n name: \"USDT0\",\n version: \"1\",\n decimals: 6,\n }, // Stable testnet USDT0\n \"eip155:137\": {\n address: \"0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359\",\n name: \"USD Coin\",\n version: \"2\",\n decimals: 6,\n }, // Polygon mainnet USDC\n \"eip155:42161\": {\n address: \"0xaf88d065e77c8cC2239327C5EDb3A432268e5831\",\n name: \"USD Coin\",\n version: \"2\",\n decimals: 6,\n }, // Arbitrum One USDC\n \"eip155:421614\": {\n address: \"0x75faf114eafb1BDbe2F0316DF893fd58CE46AA4d\",\n name: \"USD Coin\",\n version: \"2\",\n decimals: 6,\n }, // Arbitrum Sepolia USDC\n \"eip155:31611\": {\n address: \"0x118917a40FAF1CD7a13dB0Ef56C86De7973Ac503\",\n name: \"Mezo USD\",\n version: \"1\",\n decimals: 18,\n assetTransferMethod: \"permit2\",\n supportsEip2612: true,\n }, // Mezo Testnet mUSD (no EIP-3009, supports EIP-2612)\n \"eip155:723487\": {\n address: \"0x33ad9e4BD16B69B5BFdED37D8B5D9fF9aba014Fb\",\n name: \"Stable Coin\",\n version: \"1\",\n decimals: 6,\n assetTransferMethod: \"permit2\",\n supportsEip2612: true,\n }, // Radius Network SBC (no EIP-3009, supports EIP-2612)\n \"eip155:72344\": {\n address: \"0x33ad9e4BD16B69B5BFdED37D8B5D9fF9aba014Fb\",\n name: \"Stable Coin\",\n version: \"1\",\n decimals: 6,\n assetTransferMethod: \"permit2\",\n supportsEip2612: true,\n }, // Radius Testnet SBC (no EIP-3009, supports EIP-2612)\n \"eip155:36900\": {\n address: \"0x9cb8142aEBBcdc60AF7c97Af897A67A8f3CA71C2\",\n name: \"USDC.e\",\n version: \"2\",\n decimals: 6,\n }, // ADI Chain USDC.e (EIP-3009 supported)\n \"eip155:190415\": {\n address: \"0x401eCb1D350407f13ba348573E5630B83638E30D\",\n name: \"Bridged USDC\",\n version: \"2\",\n decimals: 6,\n }, // HPP mainnet USDC.e\n \"eip155:181228\": {\n address: \"0x401eCb1D350407f13ba348573E5630B83638E30D\",\n name: \"Bridged USDC\",\n version: \"2\",\n decimals: 6,\n }, // HPP Sepolia USDC.e\n};\n\n/**\n * Look up the default stablecoin for a network.\n *\n * @param network - CAIP-2 network identifier (e.g. \"eip155:8453\")\n * @returns The default asset info\n * @throws If no default asset is configured for the network\n */\nexport function getDefaultAsset(network: Network): ExactDefaultAssetInfo {\n const info = DEFAULT_STABLECOINS[network];\n if (!info) {\n throw new Error(`No default asset configured for network ${network}`);\n }\n return info;\n}\n","import type { ChannelConfig } from \"../types\";\n\nexport interface Channel {\n channelId: string;\n channelConfig: ChannelConfig;\n chargedCumulativeAmount: string;\n signedMaxClaimable: string;\n signature: string;\n balance: string;\n totalClaimed: string;\n withdrawRequestedAt: number;\n refundNonce: number;\n onchainSyncedAt?: number;\n lastRequestTimestamp: number;\n pendingRequest?: PendingRequest;\n}\n\nexport interface PendingRequest {\n pendingId: string;\n signedMaxClaimable: string;\n expiresAt: number;\n}\n\nexport interface ChannelUpdateResult {\n channel: Channel | undefined;\n status: \"updated\" | \"unchanged\" | \"deleted\";\n}\n\nexport interface ChannelStorage {\n get(channelId: string): Promise<Channel | undefined>;\n list(): Promise<Channel[]>;\n /**\n * Atomically inspects and mutates a channel record.\n *\n * Implementations must guarantee that no concurrent mutation can interleave between\n * reading `current` and writing the callback result for all application instances that\n * share the backend. The in-memory backend only provides this guarantee inside one JS\n * runtime; production multi-instance deployments need storage with backend-level atomic\n * conditional mutation, such as Redis/Valkey Lua scripts, SQL transactions, or Durable Objects.\n *\n * @param channelId - The channel identifier.\n * @param update - Mutation callback. Return `undefined` to delete, or `current` to leave unchanged.\n * @returns The final stored channel and whether storage updated, stayed unchanged, or deleted.\n */\n updateChannel(\n channelId: string,\n update: (current: Channel | undefined) => Channel | undefined,\n ): Promise<ChannelUpdateResult>;\n}\n\n/**\n * In-memory {@link ChannelStorage} backed by a Map keyed by `channelId`.\n */\nexport class InMemoryChannelStorage implements ChannelStorage {\n private readonly channels = new Map<string, Channel>();\n private readonly channelLocks = new Map<string, Promise<void>>();\n\n /**\n * Returns the channel record for a channel, if present.\n *\n * @param channelId - The channel identifier.\n * @returns The channel record or undefined when not found.\n */\n async get(channelId: string): Promise<Channel | undefined> {\n return this.channels.get(channelId.toLowerCase());\n }\n\n /**\n * Lists all stored channel records.\n *\n * @returns All channel records in storage.\n */\n async list(): Promise<Channel[]> {\n return [...this.channels.values()];\n }\n\n /**\n * Atomically inspects and mutates a channel record while holding a per-channel lock.\n *\n * @param channelId - The channel identifier.\n * @param update - Mutation callback. Return `undefined` to delete, or `current` to leave unchanged.\n * @returns The final stored channel and whether storage updated, stayed unchanged, or deleted.\n */\n async updateChannel(\n channelId: string,\n update: (current: Channel | undefined) => Channel | undefined,\n ): Promise<ChannelUpdateResult> {\n const key = channelId.toLowerCase();\n return this.withChannelLock(key, async () => {\n const current = this.channels.get(key);\n const next = update(current);\n\n if (next === current) {\n return { channel: current, status: \"unchanged\" };\n }\n\n if (!next) {\n this.channels.delete(key);\n return { channel: undefined, status: current ? \"deleted\" : \"unchanged\" };\n }\n\n this.channels.set(key, next);\n return { channel: next, status: \"updated\" };\n });\n }\n\n /**\n * Runs `fn` after any prior locked work for the same channel key has finished.\n *\n * @param key - Lowercased channel id used as the lock key.\n * @param fn - Async work to run while holding the logical per-channel lock.\n * @returns The resolved result of `fn`.\n */\n private async withChannelLock<T>(key: string, fn: () => Promise<T>): Promise<T> {\n const previous = this.channelLocks.get(key) ?? Promise.resolve();\n let release!: () => void;\n const current = new Promise<void>(resolve => {\n release = resolve;\n });\n const next = previous.catch(() => {}).then(() => current);\n this.channelLocks.set(key, next);\n\n await previous.catch(() => {});\n try {\n return await fn();\n } finally {\n release();\n if (this.channelLocks.get(key) === next) {\n this.channelLocks.delete(key);\n }\n }\n }\n}\n","import type {\n VerifiedPaymentCanceledContext,\n VerifyContext,\n VerifyFailureContext,\n VerifyResultContext,\n} from \"@payai/x402/server\";\nimport type { VerifyResponse } from \"@payai/x402/types\";\nimport type { SchemePaymentRequiredContext } from \"@payai/x402/types\";\nimport { getAddress, verifyTypedData } from \"viem\";\nimport {\n type BatchSettlementDepositPayload,\n type BatchSettlementRefundPayload,\n type BatchSettlementVoucherPayload,\n isBatchSettlementDepositPayload,\n isBatchSettlementRefundPayload,\n isBatchSettlementVoucherPayload,\n} from \"../types\";\nimport { BATCH_SETTLEMENT_SCHEME, voucherTypes } from \"../constants\";\nimport type { ChannelConfig } from \"../types\";\nimport { createNonce, getEvmChainId } from \"../../utils\";\nimport { computeChannelId, getBatchSettlementEip712Domain } from \"../utils\";\nimport { validateChannelConfig } from \"../facilitator/utils\";\nimport * as Errors from \"../errors\";\nimport type { BatchSettlementEvmScheme } from \"./scheme\";\nimport type { Channel, PendingRequest } from \"./storage\";\nimport { readExtraNumber, readExtraString } from \"./utils\";\n\n// Framework cleanup hooks clear pending reservations for normal failures\n// This bounded TTL releases channels when cleanup cannot run or complete\nconst MIN_PENDING_TTL_MS = 5_000; // 5 seconds\nconst MAX_PENDING_TTL_MS = 10 * 60 * 1000; // 600 seconds\n\n/**\n * Computes the bounded pending reservation expiry time.\n *\n * @param maxTimeoutSeconds - Resource timeout from payment requirements.\n * @param now - Current wall-clock time in milliseconds.\n * @returns Expiry timestamp in milliseconds.\n */\nfunction pendingExpiresAt(maxTimeoutSeconds: number | undefined, now: number): number {\n const requestedMs = Math.max(0, maxTimeoutSeconds ?? 0) * 1000;\n const ttlMs = Math.min(MAX_PENDING_TTL_MS, Math.max(MIN_PENDING_TTL_MS, requestedMs));\n return now + ttlMs;\n}\n\n/**\n * Checks whether a pending reservation still blocks same-channel work.\n *\n * @param pending - Pending reservation to inspect.\n * @param now - Current wall-clock time in milliseconds.\n * @returns Whether the reservation exists and has not expired.\n */\nfunction isPendingLive(pending: PendingRequest | undefined, now: number): boolean {\n return pending !== undefined && pending.expiresAt > now;\n}\n\n/**\n * Lifecycle hook: runs before the facilitator verifies a payment.\n *\n * For paid payloads, checks whether the client's cumulative amount matches server\n * state. If mismatched, aborts with `invalid_batch_settlement_evm_cumulative_amount_mismatch`.\n *\n * Refund vouchers are zero-charge: the expected `maxClaimableAmount` equals\n * the existing `chargedCumulativeAmount`.\n *\n * When no local channel record exists, verification is delegated to the facilitator (which checks onchain state);\n * `handleAfterVerify` then rebuilds the channel record from the verify response.\n *\n * @param scheme - Owning `BatchSettlementEvmScheme` instance for storage access.\n * @param ctx - Verify lifecycle context (payload, requirements, and related state).\n * @returns Nothing to continue verification; or an object with `abort` to fail with a reason.\n */\nexport async function handleBeforeVerify(\n scheme: BatchSettlementEvmScheme,\n ctx: VerifyContext,\n): Promise<\n void | { abort: true; reason: string; message?: string } | { skip: true; result: VerifyResponse }\n> {\n const { paymentPayload, requirements } = ctx;\n\n const raw = paymentPayload.payload;\n const isPaidPayload =\n isBatchSettlementVoucherPayload(raw) || isBatchSettlementDepositPayload(raw);\n const isZeroChargePayload = isBatchSettlementRefundPayload(raw);\n if (!isPaidPayload && !isZeroChargePayload) {\n return;\n }\n\n const channelId = raw.voucher.channelId;\n const now = Date.now();\n const pendingId = createNonce();\n let outcome:\n | { status: \"reserved\"; channelSnapshot?: Channel }\n | { status: \"busy\" }\n | { status: \"mismatch\"; channel: Channel }\n | undefined;\n\n await scheme.getStorage().updateChannel(channelId, current => {\n if (isPendingLive(current?.pendingRequest, now)) {\n outcome = { status: \"busy\" };\n return current;\n }\n\n const chargedCumulativeAmount =\n current?.chargedCumulativeAmount ??\n inferMissingLocalChargedAmount(\n raw.voucher.maxClaimableAmount,\n requirements.amount,\n isPaidPayload,\n );\n const expectedMaxClaimable = isZeroChargePayload\n ? BigInt(chargedCumulativeAmount)\n : BigInt(chargedCumulativeAmount) + BigInt(requirements.amount);\n\n if (BigInt(raw.voucher.maxClaimableAmount) !== expectedMaxClaimable) {\n if (current) {\n outcome = { status: \"mismatch\", channel: current };\n } else {\n outcome = {\n status: \"mismatch\",\n channel: buildProvisionalChannel(raw, chargedCumulativeAmount),\n };\n }\n return current;\n }\n\n const pendingRequest: PendingRequest = {\n pendingId,\n signedMaxClaimable: raw.voucher.maxClaimableAmount,\n expiresAt: pendingExpiresAt(requirements.maxTimeoutSeconds, now),\n };\n\n outcome = { status: \"reserved\", channelSnapshot: current };\n return {\n ...(current ?? buildProvisionalChannel(raw, chargedCumulativeAmount)),\n pendingRequest,\n lastRequestTimestamp: now,\n };\n });\n\n if (outcome?.status === \"busy\") {\n return {\n abort: true,\n reason: Errors.ErrChannelBusy,\n message: \"Channel is already processing a request\",\n };\n }\n\n if (outcome?.status === \"mismatch\") {\n scheme.rememberChannelSnapshot(paymentPayload, outcome.channel);\n return {\n abort: true,\n reason: Errors.ErrCumulativeAmountMismatch,\n message: \"Client voucher base does not match server state\",\n };\n }\n\n if (outcome?.status === \"reserved\") {\n scheme.mergeRequestContext(paymentPayload, {\n channelId,\n pendingId,\n channelSnapshot: outcome.channelSnapshot,\n });\n\n if (isBatchSettlementVoucherPayload(raw)) {\n const localResult = await verifyVoucherLocally(\n scheme,\n raw,\n requirements,\n outcome.channelSnapshot,\n now,\n );\n if (localResult) {\n scheme.mergeRequestContext(paymentPayload, { localVerify: true });\n return { skip: true, result: localResult };\n }\n }\n }\n}\n\n/**\n * Adds server channel state to corrective 402 responses for cumulative mismatches.\n *\n * @param scheme - Owning `BatchSettlementEvmScheme` instance for storage access.\n * @param ctx - Payment-required response context.\n */\nexport async function handleEnrichPaymentRequiredResponse(\n scheme: BatchSettlementEvmScheme,\n ctx: SchemePaymentRequiredContext,\n): Promise<void> {\n if (ctx.error !== Errors.ErrCumulativeAmountMismatch) {\n return;\n }\n\n const { paymentPayload } = ctx;\n if (!paymentPayload) {\n return;\n }\n\n const raw = paymentPayload.payload;\n if (\n !isBatchSettlementVoucherPayload(raw) &&\n !isBatchSettlementDepositPayload(raw) &&\n !isBatchSettlementRefundPayload(raw)\n ) {\n return;\n }\n\n const channel =\n scheme.takeChannelSnapshot(paymentPayload) ??\n (await scheme.getStorage().get(raw.voucher.channelId));\n if (!channel) {\n return;\n }\n\n const accept = ctx.requirements.find(\n req =>\n req.scheme === BATCH_SETTLEMENT_SCHEME && req.network === paymentPayload.accepted.network,\n );\n if (!accept) {\n return;\n }\n\n accept.extra = {\n ...accept.extra,\n channelState: {\n channelId: channel.channelId,\n balance: channel.balance,\n totalClaimed: channel.totalClaimed,\n withdrawRequestedAt: channel.withdrawRequestedAt,\n refundNonce: String(channel.refundNonce),\n chargedCumulativeAmount: channel.chargedCumulativeAmount,\n },\n voucherState: {\n signedMaxClaimable: channel.signedMaxClaimable,\n signature: channel.signature as `0x${string}`,\n },\n };\n}\n\n/**\n * Lifecycle hook: runs after the facilitator verifies a payment.\n *\n * Persists channel state (balance, totalClaimed, voucher info) so that\n * subsequent requests can correctly calculate cumulative amounts and detect stale state.\n *\n * For refund payloads, additionally returns a `skipHandler` directive so that\n * the resource server bypasses the application handler and settles inline.\n *\n * @param scheme - Owning `BatchSettlementEvmScheme` instance for storage access.\n * @param ctx - Post-verify lifecycle context.\n * @param ctx.paymentPayload - Incoming payment payload that was verified.\n * @param ctx.requirements - Requirements used for verification.\n * @param ctx.result - Facilitator verify response.\n * @returns Optional `skipHandler` directive when this is a refund voucher; otherwise void.\n */\nexport async function handleAfterVerify(\n scheme: BatchSettlementEvmScheme,\n ctx: VerifyResultContext,\n): Promise<void | { skipHandler: true; response?: { contentType?: string; body?: unknown } }> {\n const { paymentPayload, result } = ctx;\n if (!result.isValid || !result.payer) {\n await scheme.clearPendingRequest(paymentPayload);\n return;\n }\n\n const raw = paymentPayload.payload;\n let channelId: string;\n let signedMaxClaimable: string;\n let signature: `0x${string}`;\n let channelConfig: ChannelConfig;\n let isRefundVoucher = false;\n\n if (isBatchSettlementDepositPayload(raw)) {\n channelId = raw.voucher.channelId;\n signedMaxClaimable = raw.voucher.maxClaimableAmount;\n signature = raw.voucher.signature;\n channelConfig = raw.channelConfig;\n } else if (isBatchSettlementVoucherPayload(raw)) {\n channelId = raw.voucher.channelId;\n signedMaxClaimable = raw.voucher.maxClaimableAmount;\n signature = raw.voucher.signature;\n channelConfig = raw.channelConfig;\n } else if (isBatchSettlementRefundPayload(raw)) {\n channelId = raw.voucher.channelId;\n signedMaxClaimable = raw.voucher.maxClaimableAmount;\n signature = raw.voucher.signature;\n channelConfig = raw.channelConfig;\n isRefundVoucher = true;\n } else {\n return;\n }\n\n const ex = result.extra ?? {};\n const balance = readExtraString(ex, \"balance\", \"0\");\n const totalClaimed = readExtraString(ex, \"totalClaimed\", \"0\");\n const withdrawRequestedAt = readExtraNumber(ex, \"withdrawRequestedAt\", 0);\n const refundNonce = readExtraNumber(ex, \"refundNonce\", 0);\n const now = Date.now();\n\n const storage = scheme.getStorage();\n const requestContext = scheme.readRequestContext(paymentPayload);\n if (!requestContext?.pendingId) {\n return;\n }\n if (requestContext.localVerify && isBatchSettlementVoucherPayload(raw)) {\n return;\n }\n\n const updateResult = await storage.updateChannel(channelId, current => {\n if (!current || current.pendingRequest?.pendingId !== requestContext.pendingId) {\n return current;\n }\n\n const channel: Channel = {\n channelId,\n channelConfig,\n chargedCumulativeAmount: current.chargedCumulativeAmount,\n signedMaxClaimable,\n signature,\n balance,\n totalClaimed,\n withdrawRequestedAt,\n refundNonce,\n onchainSyncedAt: requestContext.localVerify ? current.onchainSyncedAt : now,\n lastRequestTimestamp: now,\n pendingRequest: current.pendingRequest,\n };\n return channel;\n });\n if (updateResult.status === \"updated\" && updateResult.channel) {\n scheme.rememberChannelSnapshot(paymentPayload, updateResult.channel);\n }\n\n if (isRefundVoucher && updateResult.status === \"updated\") {\n return {\n skipHandler: true,\n response: {\n contentType: \"application/json\",\n body: { message: \"Refund acknowledged\", channelId },\n },\n };\n }\n}\n\n/**\n * Cleanup hook: clears this request's reservation after verify throws.\n *\n * @param scheme - Owning `BatchSettlementEvmScheme` instance.\n * @param ctx - Verify failure context for the current payment.\n */\nexport async function handleVerifyFailure(\n scheme: BatchSettlementEvmScheme,\n ctx: VerifyFailureContext,\n): Promise<void> {\n await scheme.clearPendingRequest(ctx.paymentPayload);\n}\n\n/**\n * Cleanup hook: clears this request's reservation when handler work is canceled.\n *\n * @param scheme - Owning `BatchSettlementEvmScheme` instance.\n * @param ctx - Verified-payment cancellation context.\n */\nexport async function handleVerifiedPaymentCanceled(\n scheme: BatchSettlementEvmScheme,\n ctx: VerifiedPaymentCanceledContext,\n): Promise<void> {\n if (ctx.reason !== \"handler_threw\" && ctx.reason !== \"handler_failed\") {\n return;\n }\n await scheme.clearPendingRequest(ctx.paymentPayload);\n}\n\n/**\n * Verifies a voucher against locally cached channel state when that state is fresh.\n *\n * @param scheme - Batch settlement scheme (TTL for onchain sync freshness).\n * @param raw - Decoded batch-settlement voucher payload.\n * @param requirements - Payment requirements (network, etc.).\n * @param channel - Cached channel row, if any.\n * @param now - Current wall-clock time in milliseconds.\n * @returns A {@link VerifyResponse}, or `undefined` to fall back to facilitator verification.\n */\nasync function verifyVoucherLocally(\n scheme: BatchSettlementEvmScheme,\n raw: BatchSettlementVoucherPayload,\n requirements: VerifyContext[\"requirements\"],\n channel: Channel | undefined,\n now: number,\n): Promise<VerifyResponse | undefined> {\n if (!channel || !isOnchainStateFresh(channel, scheme.getOnchainStateTtlMs(), now)) {\n return;\n }\n\n if (raw.channelConfig.payerAuthorizer === \"0x0000000000000000000000000000000000000000\") {\n return;\n }\n\n const payer = raw.channelConfig.payer;\n const configErr = validateChannelConfig(\n raw.channelConfig,\n raw.voucher.channelId,\n requirements as Parameters<typeof validateChannelConfig>[2],\n );\n if (configErr) {\n return invalidVerifyResponse(payer, configErr);\n }\n\n if (\n computeChannelId(raw.channelConfig, requirements.network).toLowerCase() !==\n channel.channelId.toLowerCase()\n ) {\n return invalidVerifyResponse(payer, Errors.ErrChannelIdMismatch);\n }\n\n const signatureOk = await verifyLocalVoucherSignature(raw, requirements.network);\n if (!signatureOk) {\n return invalidVerifyResponse(payer, Errors.ErrInvalidVoucherSignature);\n }\n\n const maxClaimableAmount = BigInt(raw.voucher.maxClaimableAmount);\n if (maxClaimableAmount > BigInt(channel.balance)) {\n return invalidVerifyResponse(payer, Errors.ErrCumulativeExceedsBalance);\n }\n\n if (maxClaimableAmount <= BigInt(channel.totalClaimed)) {\n return invalidVerifyResponse(payer, Errors.ErrCumulativeAmountBelowClaimed);\n }\n\n return {\n isValid: true,\n payer,\n extra: {\n channelId: raw.voucher.channelId,\n balance: channel.balance,\n totalClaimed: channel.totalClaimed,\n withdrawRequestedAt: channel.withdrawRequestedAt,\n refundNonce: channel.refundNonce.toString(),\n },\n };\n}\n\n/**\n * Returns whether cached onchain fields for a channel are still within the freshness window.\n *\n * @param channel - Cached channel row.\n * @param ttlMs - Maximum age of `onchainSyncedAt` in milliseconds.\n * @param now - Current wall-clock time in milliseconds.\n * @returns `true` if onchain sync time is present and still within `ttlMs` of `now`.\n */\nfunction isOnchainStateFresh(channel: Channel, ttlMs: number, now: number): boolean {\n return channel.onchainSyncedAt !== undefined && now - channel.onchainSyncedAt <= ttlMs;\n}\n\n/**\n * Verifies the EIP-712 voucher signature against the payer authorizer.\n *\n * @param raw - Decoded batch-settlement voucher payload.\n * @param network - EVM network identifier for chain ID / domain.\n * @returns Whether the typed-data signature is valid.\n */\nasync function verifyLocalVoucherSignature(\n raw: BatchSettlementVoucherPayload,\n network: string,\n): Promise<boolean> {\n try {\n return await verifyTypedData({\n address: getAddress(raw.channelConfig.payerAuthorizer),\n domain: getBatchSettlementEip712Domain(getEvmChainId(network)),\n types: voucherTypes,\n primaryType: \"Voucher\",\n message: {\n channelId: raw.voucher.channelId,\n maxClaimableAmount: BigInt(raw.voucher.maxClaimableAmount),\n },\n signature: raw.voucher.signature,\n });\n } catch {\n return false;\n }\n}\n\n/**\n * Builds a failed verify response with the payer address preserved for reporting.\n *\n * @param payer - Payer address from the payload.\n * @param invalidReason - Machine-readable failure reason.\n * @returns Invalid {@link VerifyResponse} with `isValid: false`.\n */\nfunction invalidVerifyResponse(payer: `0x${string}`, invalidReason: string): VerifyResponse {\n return { isValid: false, invalidReason, payer };\n}\n\n/**\n * Builds the minimal local channel record needed to reserve missing state.\n *\n * @param raw - Batch-settlement payload containing channel config and voucher.\n * @param chargedCumulativeAmount - Local charged base inferred before facilitator verification.\n * @returns Provisional channel state.\n */\nfunction buildProvisionalChannel(\n raw: BatchSettlementVoucherPayload | BatchSettlementDepositPayload | BatchSettlementRefundPayload,\n chargedCumulativeAmount: string,\n): Channel {\n return {\n channelId: raw.voucher.channelId,\n channelConfig: raw.channelConfig,\n chargedCumulativeAmount,\n signedMaxClaimable: raw.voucher.maxClaimableAmount,\n signature: raw.voucher.signature,\n balance: \"0\",\n totalClaimed: \"0\",\n withdrawRequestedAt: 0,\n refundNonce: 0,\n lastRequestTimestamp: Date.now(),\n };\n}\n\n/**\n * Infers the local charged base when storage has no channel record.\n *\n * @param signedMaxClaimable - Client-signed cumulative voucher cap.\n * @param price - Current request amount.\n * @param isPaidPayload - Whether the payload should add `price` to the local base.\n * @returns Inferred charged base as a decimal string.\n */\nfunction inferMissingLocalChargedAmount(\n signedMaxClaimable: string,\n price: string,\n isPaidPayload: boolean,\n): string {\n if (!isPaidPayload) {\n return signedMaxClaimable;\n }\n\n const signed = BigInt(signedMaxClaimable);\n const amount = BigInt(price);\n if (signed < amount) {\n return \"0\";\n }\n return (signed - amount).toString();\n}\n","import type { TypedData } from \"viem\";\n\nexport interface AuthorizerSigner {\n address: `0x${string}`;\n signTypedData(params: {\n domain: Record<string, unknown>;\n types: TypedData;\n primaryType: string;\n message: Record<string, unknown>;\n }): Promise<`0x${string}`>;\n}\n\nexport type ChannelState = {\n balance: bigint;\n totalClaimed: bigint;\n withdrawRequestedAt: number;\n refundNonce: bigint;\n};\n\nexport type ChannelConfig = {\n payer: `0x${string}`;\n payerAuthorizer: `0x${string}`;\n receiver: `0x${string}`;\n receiverAuthorizer: `0x${string}`;\n token: `0x${string}`;\n withdrawDelay: number;\n salt: `0x${string}`;\n};\n\nexport type BatchSettlementErc3009Authorization = {\n validAfter: string;\n validBefore: string;\n salt: `0x${string}`;\n signature: `0x${string}`;\n};\n\nexport type BatchSettlementPermit2Authorization = {\n from: `0x${string}`;\n permitted: {\n token: `0x${string}`;\n amount: string;\n };\n spender: `0x${string}`;\n nonce: string;\n deadline: string;\n witness: {\n channelId: `0x${string}`;\n };\n signature: `0x${string}`;\n};\n\nexport type BatchSettlementAssetTransferMethod = \"eip3009\" | \"permit2\";\n\nexport type BatchSettlementDepositAuthorization =\n | {\n erc3009Authorization: BatchSettlementErc3009Authorization;\n permit2Authorization?: never;\n }\n | {\n erc3009Authorization?: never;\n permit2Authorization: BatchSettlementPermit2Authorization;\n };\n\nexport type BatchSettlementDepositPayload = {\n type: \"deposit\";\n channelConfig: ChannelConfig;\n voucher: BatchSettlementVoucherFields;\n deposit: {\n amount: string;\n authorization: BatchSettlementDepositAuthorization;\n };\n};\n\nexport type BatchSettlementVoucherPayload = {\n type: \"voucher\";\n channelConfig: ChannelConfig;\n voucher: BatchSettlementVoucherFields;\n};\n\nexport type BatchSettlementRefundPayload = {\n type: \"refund\";\n channelConfig: ChannelConfig;\n voucher: BatchSettlementVoucherFields;\n amount?: string;\n};\n\nexport type BatchSettlementVoucherFields = {\n channelId: `0x${string}`;\n maxClaimableAmount: string;\n signature: `0x${string}`;\n};\n\nexport type BatchSettlementVoucherClaim = {\n voucher: {\n channel: ChannelConfig;\n maxClaimableAmount: string;\n };\n signature: `0x${string}`;\n totalClaimed: string;\n};\n\nexport type BatchSettlementChannelStateExtra = {\n channelId: `0x${string}`;\n balance: string;\n totalClaimed: string;\n withdrawRequestedAt: number;\n refundNonce: string;\n chargedCumulativeAmount?: string;\n};\n\nexport type BatchSettlementVoucherStateExtra = {\n signedMaxClaimable?: string;\n signature?: `0x${string}`;\n};\n\nexport type BatchSettlementPaymentRequirementsExtra = {\n receiverAuthorizer: `0x${string}`;\n withdrawDelay: number;\n name: string;\n version: string;\n assetTransferMethod?: BatchSettlementAssetTransferMethod;\n channelState?: BatchSettlementChannelStateExtra;\n voucherState?: BatchSettlementVoucherStateExtra;\n};\n\nexport type FileChannelStorageOptions = {\n /** Root directory; channels are stored under `{directory}/{client|server}/{channelId}.json`. */\n directory: string;\n};\n\nexport type BatchSettlementPaymentResponseExtra = {\n chargedAmount?: string;\n channelState?: BatchSettlementChannelStateExtra;\n voucherState?: BatchSettlementVoucherStateExtra;\n};\n\nexport type BatchSettlementClaimPayload = {\n type: \"claim\";\n claims: BatchSettlementVoucherClaim[];\n claimAuthorizerSignature?: `0x${string}`;\n};\n\nexport type BatchSettlementSettlePayload = {\n type: \"settle\";\n receiver: `0x${string}`;\n token: `0x${string}`;\n};\n\nexport type BatchSettlementEnrichedRefundPayload = BatchSettlementRefundPayload & {\n amount: string;\n refundNonce: string;\n claims: BatchSettlementVoucherClaim[];\n refundAuthorizerSignature?: `0x${string}`;\n claimAuthorizerSignature?: `0x${string}`;\n};\n\nexport type BatchSettlementPayload =\n | BatchSettlementDepositPayload\n | BatchSettlementVoucherPayload\n | BatchSettlementRefundPayload;\n\nexport type BatchSettlementFacilitatorSettlePayload =\n | BatchSettlementDepositPayload\n | BatchSettlementClaimPayload\n | BatchSettlementSettlePayload\n | BatchSettlementEnrichedRefundPayload;\n\n/**\n * Returns true when the value is a non-null object (a usable record).\n *\n * @param payload - Value of unknown shape.\n * @returns True if `payload` is an object that can be indexed by string keys.\n */\nfunction isObject(payload: unknown): payload is Record<string, unknown> {\n return typeof payload === \"object\" && payload !== null;\n}\n\n/**\n * Type guard for internal voucher field shape (channel, amount, signature).\n *\n * @param payload - Unknown value to check.\n * @returns True if `payload` is an object with `channelId`, `maxClaimableAmount`, and `signature`.\n */\nfunction isVoucherFields(payload: unknown): payload is BatchSettlementVoucherFields {\n return (\n isObject(payload) &&\n \"channelId\" in payload &&\n \"maxClaimableAmount\" in payload &&\n \"signature\" in payload\n );\n}\n\n/**\n * Type guard for {@link BatchSettlementDepositPayload}.\n *\n * @param payload - Unknown payload to check.\n * @returns True if `payload` is a deposit payload (carries `deposit` and `voucher`).\n */\nexport function isBatchSettlementDepositPayload(\n payload: unknown,\n): payload is BatchSettlementDepositPayload {\n return (\n isObject(payload) &&\n payload.type === \"deposit\" &&\n \"channelConfig\" in payload &&\n isVoucherFields(payload.voucher) &&\n isObject(payload.deposit) &&\n typeof payload.deposit.amount === \"string\" &&\n isObject(payload.deposit.authorization)\n );\n}\n\n/**\n * Type guard for {@link BatchSettlementVoucherPayload}.\n *\n * @param payload - Unknown payload to check.\n * @returns True if `payload` is a voucher payload with channel and signature fields.\n */\nexport function isBatchSettlementVoucherPayload(\n payload: unknown,\n): payload is BatchSettlementVoucherPayload {\n return (\n isObject(payload) &&\n payload.type === \"voucher\" &&\n \"channelConfig\" in payload &&\n isVoucherFields(payload.voucher)\n );\n}\n\n/**\n * Type guard for {@link BatchSettlementRefundPayload}.\n *\n * @param payload - Unknown payload to check.\n * @returns True if `payload` is a refund payload with channel config and voucher fields.\n */\nexport function isBatchSettlementRefundPayload(\n payload: unknown,\n): payload is BatchSettlementRefundPayload {\n return (\n isObject(payload) &&\n payload.type === \"refund\" &&\n \"channelConfig\" in payload &&\n isVoucherFields(payload.voucher)\n );\n}\n\n/**\n * Type guard for {@link BatchSettlementClaimPayload}.\n *\n * @param payload - Unknown payload to check.\n * @returns True if `payload` is a settle-action `claimWithSignature` payload.\n */\nexport function isBatchSettlementClaimPayload(\n payload: unknown,\n): payload is BatchSettlementClaimPayload {\n return isObject(payload) && payload.type === \"claim\" && \"claims\" in payload;\n}\n\n/**\n * Type guard for {@link BatchSettlementSettlePayload}.\n *\n * @param payload - Unknown payload to check.\n * @returns True if `payload` is a settle-action `settle` payload.\n */\nexport function isBatchSettlementSettlePayload(\n payload: unknown,\n): payload is BatchSettlementSettlePayload {\n return (\n isObject(payload) && payload.type === \"settle\" && \"receiver\" in payload && \"token\" in payload\n );\n}\n\n/**\n * Type guard for {@link BatchSettlementEnrichedRefundPayload}.\n *\n * @param payload - Unknown payload to check.\n * @returns True if `payload` is a settle-action `refundWithSignature` payload.\n */\nexport function isBatchSettlementEnrichedRefundPayload(\n payload: unknown,\n): payload is BatchSettlementEnrichedRefundPayload {\n return (\n isBatchSettlementRefundPayload(payload) &&\n \"amount\" in payload &&\n \"refundNonce\" in payload &&\n \"claims\" in payload\n );\n}\n","import { getAddress, verifyTypedData as viemVerifyTypedData } from \"viem\";\nimport type { PaymentRequirements } from \"@payai/x402/types\";\nimport { FacilitatorEvmSigner } from \"../../signer\";\nimport { multicall } from \"../../multicall\";\nimport {\n BATCH_SETTLEMENT_ADDRESS,\n MIN_WITHDRAW_DELAY,\n MAX_WITHDRAW_DELAY,\n voucherTypes,\n} from \"../constants\";\nimport { batchSettlementABI } from \"../abi\";\nimport type {\n BatchSettlementPaymentRequirementsExtra,\n ChannelConfig,\n ChannelState,\n} from \"../types\";\nimport { computeChannelId, getBatchSettlementEip712Domain } from \"../utils\";\nimport * as Errors from \"../errors\";\n\n/**\n * Normalises a {@link ChannelConfig} into the checksummed-address tuple expected by the\n * batch-settlement contract's `deposit` / `refundWithSignature` / `claimWithSignature` calls.\n *\n * @param config - In-memory channel configuration.\n * @returns Channel config tuple with all address fields checksummed via `getAddress`.\n */\nexport function toContractChannelConfig(config: ChannelConfig) {\n return {\n payer: getAddress(config.payer),\n payerAuthorizer: getAddress(config.payerAuthorizer),\n receiver: getAddress(config.receiver),\n receiverAuthorizer: getAddress(config.receiverAuthorizer),\n token: getAddress(config.token),\n withdrawDelay: config.withdrawDelay,\n salt: config.salt,\n };\n}\n\n/**\n * Case-insensitive comparison of two channel id hex strings.\n *\n * @param a - First channel id.\n * @param b - Second channel id (may be any unknown value).\n * @returns `true` when both ids refer to the same channel.\n */\nexport function channelIdsEqual(a: `0x${string}`, b: unknown): boolean {\n if (typeof b !== \"string\" || b.length === 0) return false;\n const norm = (x: string) => {\n let s = x.toLowerCase();\n if (s.startsWith(\"0x\")) s = s.slice(2);\n return `0x${s}`;\n };\n return norm(a) === norm(b);\n}\n\n/**\n * Validates the time window of an ERC-3009 `ReceiveWithAuthorization`.\n *\n * @param validAfter - Earliest unix timestamp the authorization is valid (in seconds).\n * @param validBefore - Latest unix timestamp before which the authorization is valid.\n * @returns An error code string if the time window is invalid, otherwise `undefined`.\n */\nexport function erc3009AuthorizationTimeInvalidReason(\n validAfter: bigint,\n validBefore: bigint,\n): string | undefined {\n const now = Math.floor(Date.now() / 1000);\n if (validBefore < BigInt(now + 6)) return Errors.ErrValidBeforeExpired;\n if (validAfter > BigInt(now)) return Errors.ErrValidAfterInFuture;\n return undefined;\n}\n\n/**\n * Dual-path voucher signature verification.\n *\n * When `payerAuthorizer` is a non-zero address, the signature is verified off-chain via\n * ECDSA recovery against that address (no RPC call). When `payerAuthorizer` is `address(0)`,\n * verification falls back to an ERC-1271 `isValidSignature` call against the payer contract\n * (smart-wallet path).\n *\n * @param signer - Facilitator signer providing `verifyTypedData` (may issue RPC for ERC-1271).\n * @param params - Voucher fields and authorizer addresses needed for verification.\n * @param params.channelId - EIP-712 voucher channel id (`bytes32` hex).\n * @param params.maxClaimableAmount - Max cumulative claimable amount as a decimal string.\n * @param params.payerAuthorizer - Address that signed the voucher; zero address selects ERC-1271 verification.\n * @param params.payer - Payer contract address (used for ERC-1271).\n * @param params.signature - EIP-712 signature bytes over the voucher.\n * @param chainId - Numeric EVM chain id for the EIP-712 domain.\n * @returns `true` when the voucher signature is valid.\n */\nexport async function verifyBatchSettlementVoucherTypedData(\n signer: FacilitatorEvmSigner,\n params: {\n channelId: `0x${string}`;\n maxClaimableAmount: string;\n payerAuthorizer: `0x${string}`;\n payer: `0x${string}`;\n signature: `0x${string}`;\n },\n chainId: number,\n): Promise<boolean> {\n const domain = getBatchSettlementEip712Domain(chainId);\n const message = {\n channelId: params.channelId,\n maxClaimableAmount: BigInt(params.maxClaimableAmount),\n };\n\n const zeroAddress = \"0x0000000000000000000000000000000000000000\";\n\n if (params.payerAuthorizer !== zeroAddress) {\n return viemVerifyTypedData({\n address: getAddress(params.payerAuthorizer),\n domain,\n types: voucherTypes,\n primaryType: \"Voucher\",\n message,\n signature: params.signature,\n });\n }\n\n return signer.verifyTypedData({\n address: getAddress(params.payer),\n domain,\n types: voucherTypes,\n primaryType: \"Voucher\",\n message,\n signature: params.signature,\n });\n}\n\n/**\n * Validates that a {@link ChannelConfig} is consistent with the claimed `channelId` and\n * the server's {@link PaymentRequirements}.\n *\n * @param config - The channel configuration from the payload.\n * @param channelId - The `channelId` claimed in the payload.\n * @param requirements - Server payment requirements to cross-check against.\n * @returns An error code string if validation fails, otherwise `undefined`.\n */\nexport function validateChannelConfig(\n config: ChannelConfig,\n channelId: `0x${string}`,\n requirements: PaymentRequirements,\n): string | undefined {\n const computedId = computeChannelId(config, requirements.network);\n if (computedId.toLowerCase() !== channelId.toLowerCase()) {\n return Errors.ErrChannelIdMismatch;\n }\n\n if (getAddress(config.receiver) !== getAddress(requirements.payTo)) {\n return Errors.ErrReceiverMismatch;\n }\n\n const extra = requirements.extra as Partial<BatchSettlementPaymentRequirementsExtra> | undefined;\n const requiredReceiverAuthorizer = extra?.receiverAuthorizer;\n\n if (\n !requiredReceiverAuthorizer ||\n getAddress(requiredReceiverAuthorizer) === \"0x0000000000000000000000000000000000000000\" ||\n getAddress(config.receiverAuthorizer) !== getAddress(requiredReceiverAuthorizer)\n ) {\n return Errors.ErrReceiverAuthorizerMismatch;\n }\n\n if (getAddress(config.token) !== getAddress(requirements.asset)) {\n return Errors.ErrTokenMismatch;\n }\n\n if (extra?.withdrawDelay !== undefined && config.withdrawDelay !== Number(extra.withdrawDelay)) {\n return Errors.ErrWithdrawDelayMismatch;\n }\n\n if (config.withdrawDelay < MIN_WITHDRAW_DELAY || config.withdrawDelay > MAX_WITHDRAW_DELAY) {\n return Errors.ErrWithdrawDelayOutOfRange;\n }\n\n return undefined;\n}\n\n/**\n * Reads onchain channel state via a 3-call multicall:\n * `channels(channelId)`, `pendingWithdrawals(channelId)`, `refundNonce(channelId)`.\n *\n * Throws when any sub-call fails so callers can distinguish RPC failures\n * from missing channels (which return zero balance/totalClaimed/refundNonce).\n *\n * @param signer - Facilitator signer for onchain reads.\n * @param channelId - The `bytes32` channel id.\n * @returns Fresh {@link ChannelState}.\n */\nexport async function readChannelState(\n signer: FacilitatorEvmSigner,\n channelId: `0x${string}`,\n): Promise<ChannelState> {\n const target = getAddress(BATCH_SETTLEMENT_ADDRESS);\n const mcResults = await multicall(signer.readContract.bind(signer), [\n { address: target, abi: batchSettlementABI, functionName: \"channels\", args: [channelId] },\n {\n address: target,\n abi: batchSettlementABI,\n functionName: \"pendingWithdrawals\",\n args: [channelId],\n },\n { address: target, abi: batchSettlementABI, functionName: \"refundNonce\", args: [channelId] },\n ]);\n\n const [chRes, wdRes, rnRes] = mcResults;\n if (chRes.status === \"failure\" || wdRes.status === \"failure\" || rnRes.status === \"failure\") {\n throw new Error(`${Errors.ErrRpcReadFailed}: multicall returned failure for ${channelId}`);\n }\n\n const [balance, totalClaimed] = chRes.result as [bigint, bigint];\n const [, wdInitiatedAt] = wdRes.result as [bigint, bigint];\n const refundNonce = rnRes.result as bigint;\n\n return { balance, totalClaimed, withdrawRequestedAt: Number(wdInitiatedAt), refundNonce };\n}\n","import { encodeFunctionData, decodeFunctionResult } from \"viem\";\n\n/**\n * Multicall3 contract address.\n * Same address on all EVM chains via CREATE2 deployment.\n *\n * @see https://github.com/mds1/multicall\n */\nexport const MULTICALL3_ADDRESS = \"0xcA11bde05977b3631167028862bE2a173976CA11\" as const;\n\n/** Multicall3 getEthBalance ABI for querying native token balance. */\nexport const multicall3GetEthBalanceAbi = [\n {\n name: \"getEthBalance\",\n inputs: [{ name: \"addr\", type: \"address\" }],\n outputs: [{ name: \"balance\", type: \"uint256\" }],\n stateMutability: \"view\",\n type: \"function\",\n },\n] as const;\n\n/** Multicall3 tryAggregate ABI for batching calls. */\nconst multicall3ABI = [\n {\n inputs: [\n { name: \"requireSuccess\", type: \"bool\" },\n {\n name: \"calls\",\n type: \"tuple[]\",\n components: [\n { name: \"target\", type: \"address\" },\n { name: \"callData\", type: \"bytes\" },\n ],\n },\n ],\n name: \"tryAggregate\",\n outputs: [\n {\n name: \"returnData\",\n type: \"tuple[]\",\n components: [\n { name: \"success\", type: \"bool\" },\n { name: \"returnData\", type: \"bytes\" },\n ],\n },\n ],\n stateMutability: \"payable\",\n type: \"function\",\n },\n] as const;\n\nexport type ContractCall = {\n address: `0x${string}`;\n abi: readonly unknown[];\n functionName: string;\n args?: readonly unknown[];\n};\n\nexport type RawContractCall = {\n address: `0x${string}`;\n callData: `0x${string}`;\n};\n\nexport type MulticallSuccess = { status: \"success\"; result: unknown };\nexport type MulticallFailure = { status: \"failure\"; error: Error };\nexport type MulticallResult = MulticallSuccess | MulticallFailure;\n\n/**\n * Batches contract calls via Multicall3 `tryAggregate(false, ...)`.\n *\n * Accepts a mix of typed ContractCall (ABI-encoded + decoded) and\n * RawContractCall (pre-encoded calldata, no decoding) entries.\n * Raw calls are useful for the EIP-6492 factory deployment case\n * where calldata is pre-encoded with no ABI available.\n */\ntype ReadContractFn = (args: {\n address: `0x${string}`;\n abi: readonly unknown[];\n functionName: string;\n args?: readonly unknown[];\n}) => Promise<unknown>;\n\n/**\n * Executes multiple contract read calls in a single RPC round-trip using Multicall3.\n *\n * @param readContract - Function that performs a single contract read (e.g. viem readContract)\n * @param calls - Array of contract calls to batch (ContractCall or RawContractCall)\n * @returns A promise that resolves to an array of decoded results, one per call\n */\nexport async function multicall(\n readContract: ReadContractFn,\n calls: ReadonlyArray<ContractCall | RawContractCall>,\n): Promise<MulticallResult[]> {\n const aggregateCalls = calls.map(call => {\n if (\"callData\" in call) {\n return { target: call.address, callData: call.callData };\n }\n const callData = encodeFunctionData({\n abi: call.abi,\n functionName: call.functionName,\n args: call.args as unknown[],\n });\n return { target: call.address, callData };\n });\n\n const rawResults = (await readContract({\n address: MULTICALL3_ADDRESS,\n abi: multicall3ABI,\n functionName: \"tryAggregate\",\n args: [false, aggregateCalls],\n })) as { success: boolean; returnData: `0x${string}` }[];\n\n return rawResults.map((raw, i) => {\n if (!raw.success) {\n return {\n status: \"failure\" as const,\n error: new Error(`multicall: call reverted (returnData: ${raw.returnData})`),\n };\n }\n\n const call = calls[i];\n if (\"callData\" in call) {\n return { status: \"success\" as const, result: undefined };\n }\n\n try {\n const decoded = decodeFunctionResult({\n abi: call.abi,\n functionName: call.functionName,\n data: raw.returnData,\n });\n return { status: \"success\" as const, result: decoded };\n } catch (err) {\n return {\n status: \"failure\" as const,\n error: err instanceof Error ? err : new Error(String(err)),\n };\n }\n });\n}\n","/** Error codes for the batch-settlement EVM scheme (see scheme_batch_settlement_evm2.md). */\n\nexport const ErrChannelNotFound = \"invalid_batch_settlement_evm_channel_not_found\";\nexport const ErrTokenMismatch = \"invalid_batch_settlement_evm_token_mismatch\";\nexport const ErrInvalidVoucherSignature = \"invalid_batch_settlement_evm_voucher_signature\";\nexport const ErrCumulativeExceedsBalance =\n \"invalid_batch_settlement_evm_cumulative_exceeds_balance\";\nexport const ErrCumulativeAmountBelowClaimed =\n \"invalid_batch_settlement_evm_cumulative_below_claimed\";\nexport const ErrInsufficientBalance = \"invalid_batch_settlement_evm_insufficient_balance\";\nexport const ErrDepositTransactionFailed =\n \"invalid_batch_settlement_evm_deposit_transaction_failed\";\nexport const ErrClaimTransactionFailed = \"invalid_batch_settlement_evm_claim_transaction_failed\";\nexport const ErrSettleTransactionFailed = \"invalid_batch_settlement_evm_settle_transaction_failed\";\nexport const ErrInvalidScheme = \"invalid_batch_settlement_evm_scheme\";\nexport const ErrNetworkMismatch = \"invalid_batch_settlement_evm_network_mismatch\";\nexport const ErrMissingEip712Domain = \"invalid_batch_settlement_evm_missing_eip712_domain\";\nexport const ErrValidBeforeExpired =\n \"invalid_batch_settlement_evm_payload_authorization_valid_before\";\nexport const ErrValidAfterInFuture =\n \"invalid_batch_settlement_evm_payload_authorization_valid_after\";\nexport const ErrInvalidReceiveAuthorizationSignature =\n \"invalid_batch_settlement_evm_receive_authorization_signature\";\nexport const ErrErc3009AuthorizationRequired =\n \"invalid_batch_settlement_evm_erc3009_authorization_required\";\nexport const ErrRefundTransactionFailed = \"invalid_batch_settlement_evm_refund_transaction_failed\";\nexport const ErrInvalidPayloadType = \"invalid_batch_settlement_evm_payload_type\";\nexport const ErrWithdrawDelayOutOfRange =\n \"invalid_batch_settlement_evm_withdraw_delay_out_of_range\";\nexport const ErrChannelIdMismatch = \"invalid_batch_settlement_evm_channel_id_mismatch\";\nexport const ErrReceiverMismatch = \"invalid_batch_settlement_evm_receiver_mismatch\";\nexport const ErrReceiverAuthorizerMismatch =\n \"invalid_batch_settlement_evm_receiver_authorizer_mismatch\";\nexport const ErrWithdrawDelayMismatch = \"invalid_batch_settlement_evm_withdraw_delay_mismatch\";\nexport const ErrAuthorizerAddressMismatch =\n \"invalid_batch_settlement_evm_authorizer_address_mismatch\";\nexport const ErrDepositSimulationFailed = \"invalid_batch_settlement_evm_deposit_simulation_failed\";\nexport const ErrClaimSimulationFailed = \"invalid_batch_settlement_evm_claim_simulation_failed\";\nexport const ErrSettleSimulationFailed = \"invalid_batch_settlement_evm_settle_simulation_failed\";\nexport const ErrNothingToSettle = \"invalid_batch_settlement_evm_nothing_to_settle\";\nexport const ErrRefundPayload = \"invalid_batch_settlement_evm_refund_payload\";\nexport const ErrRefundSimulationFailed = \"invalid_batch_settlement_evm_refund_simulation_failed\";\nexport const ErrRpcReadFailed = \"invalid_batch_settlement_evm_rpc_read_failed\";\nexport const ErrPermit2AuthorizationRequired =\n \"invalid_batch_settlement_evm_permit2_authorization_required\";\nexport const ErrPermit2InvalidSpender = \"invalid_batch_settlement_evm_permit2_invalid_spender\";\nexport const ErrPermit2AmountMismatch = \"invalid_batch_settlement_evm_permit2_amount_mismatch\";\nexport const ErrPermit2DeadlineExpired = \"invalid_batch_settlement_evm_permit2_deadline_expired\";\nexport const ErrPermit2InvalidSignature = \"invalid_batch_settlement_evm_permit2_invalid_signature\";\nexport const ErrPermit2AllowanceRequired =\n \"invalid_batch_settlement_evm_permit2_allowance_required\";\nexport const ErrEip2612AmountMismatch = \"invalid_batch_settlement_evm_eip2612_amount_mismatch\";\nexport const ErrEip2612OwnerMismatch = \"invalid_batch_settlement_evm_eip2612_owner_mismatch\";\nexport const ErrEip2612AssetMismatch = \"invalid_batch_settlement_evm_eip2612_asset_mismatch\";\nexport const ErrEip2612SpenderMismatch = \"invalid_batch_settlement_evm_eip2612_spender_mismatch\";\nexport const ErrEip2612DeadlineExpired = \"invalid_batch_settlement_evm_eip2612_deadline_expired\";\nexport const ErrErc20ApprovalUnavailable =\n \"invalid_batch_settlement_evm_erc20_approval_unavailable\";\n\n/** Resource server: 402 `error` and lifecycle `reason` (same strings as the spec). */\nexport const ErrCumulativeAmountMismatch =\n \"invalid_batch_settlement_evm_cumulative_amount_mismatch\";\nexport const ErrChannelBusy = \"invalid_batch_settlement_evm_channel_busy\";\nexport const ErrChargeExceedsSignedCumulative =\n \"invalid_batch_settlement_evm_charge_exceeds_signed_cumulative\";\nexport const ErrMissingChannel = \"invalid_batch_settlement_evm_missing_channel\";\nexport const ErrRefundNoBalance = \"invalid_batch_settlement_evm_refund_no_balance\";\nexport const ErrRefundAmountInvalid = \"invalid_batch_settlement_evm_refund_amount_invalid\";\n","import type { BatchSettlementChannelStateExtra } from \"../types\";\nimport { ErrRefundPayload } from \"../errors\";\n\n/**\n * Reads the nested channel snapshot from payment response extra fields.\n *\n * @param extra - Payment response extra fields.\n * @returns Channel state object, or undefined when absent.\n */\nexport function readChannelStateExtra(\n extra: Record<string, unknown> | undefined,\n): Partial<BatchSettlementChannelStateExtra> | undefined {\n const value = extra?.channelState;\n if (typeof value !== \"object\" || value === null) {\n return undefined;\n }\n return value as Partial<BatchSettlementChannelStateExtra>;\n}\n\n/**\n * Reads a string value from optional payment `extra`, with a fallback when missing or invalid.\n *\n * @param extra - Optional payment extra record.\n * @param key - Key on `BatchSettlementPaymentResponseExtra` to read.\n * @param fallback - Value returned when the entry is absent or not coercible to string.\n * @returns String representation of the value, or `fallback`.\n */\nexport function readExtraString(\n extra: Partial<Record<keyof BatchSettlementChannelStateExtra, unknown>> | undefined,\n key: keyof BatchSettlementChannelStateExtra,\n fallback: string,\n): string {\n const value = extra?.[key];\n if (typeof value === \"string\") return value;\n if (typeof value === \"number\") return String(value);\n return fallback;\n}\n\n/**\n * Reads a numeric value from optional payment `extra`, with a fallback when missing or invalid.\n *\n * @param extra - Optional payment extra record.\n * @param key - Key on `BatchSettlementPaymentResponseExtra` to read.\n * @param fallback - Value returned when the entry is absent or not parseable as a number.\n * @returns Parsed number, or `fallback`.\n */\nexport function readExtraNumber(\n extra: Partial<Record<keyof BatchSettlementChannelStateExtra, unknown>> | undefined,\n key: keyof BatchSettlementChannelStateExtra,\n fallback: number,\n): number {\n const value = extra?.[key];\n if (typeof value === \"number\") return value;\n if (typeof value === \"string\") return parseInt(value, 10) || fallback;\n return fallback;\n}\n\nexport type RefundSettlementSnapshot = {\n balance: string;\n totalClaimed: string;\n withdrawRequestedAt: number;\n refundNonce: number;\n};\n\n/**\n * Parses the facilitator's post-refund channel snapshot.\n *\n * @param extra - Settlement response extra fields.\n * @returns Validated refund settlement snapshot.\n */\nexport function parseRefundSettlementSnapshot(\n extra: Record<string, unknown> | undefined,\n): RefundSettlementSnapshot {\n const channelState = readChannelStateExtra(extra);\n return {\n balance: parseUintStringExtra(channelState, \"balance\"),\n totalClaimed: parseUintStringExtra(channelState, \"totalClaimed\"),\n withdrawRequestedAt: parseUintNumberExtra(channelState, \"withdrawRequestedAt\"),\n refundNonce: parseUintNumberExtra(channelState, \"refundNonce\"),\n };\n}\n\n/**\n * Parses a non-negative integer as a decimal string from refund snapshot `extra`.\n *\n * @param extra - Settlement response extra fields from the facilitator.\n * @param key - Field name: `balance` or `totalClaimed`.\n * @returns Decimal string representation of the uint (digits only).\n */\nfunction parseUintStringExtra(\n extra: Partial<Record<keyof BatchSettlementChannelStateExtra, unknown>> | undefined,\n key: \"balance\" | \"totalClaimed\",\n): string {\n const value = extra?.[key];\n if (typeof value === \"string\" && /^\\d+$/.test(value)) return value;\n if (typeof value === \"number\" && Number.isInteger(value) && value >= 0) return String(value);\n throw new Error(ErrRefundPayload);\n}\n\n/**\n * Parses a non-negative integer from refund snapshot `extra`.\n *\n * @param extra - Settlement response extra fields from the facilitator.\n * @param key - Field name: `withdrawRequestedAt` or `refundNonce`.\n * @returns Parsed non-negative integer.\n */\nfunction parseUintNumberExtra(\n extra: Partial<Record<keyof BatchSettlementChannelStateExtra, unknown>> | undefined,\n key: \"withdrawRequestedAt\" | \"refundNonce\",\n): number {\n const value = extra?.[key];\n if (typeof value === \"number\" && Number.isInteger(value) && value >= 0) return value;\n if (typeof value === \"string\" && /^\\d+$/.test(value)) {\n const parsed = parseInt(value, 10);\n if (!Number.isNaN(parsed)) return parsed;\n }\n throw new Error(ErrRefundPayload);\n}\n","import type { SettleResponse } from \"@payai/x402/types\";\nimport type { SettleContext, SettleFailureContext, SettleResultContext } from \"@payai/x402/server\";\nimport { signClaimBatch, signRefund } from \"../authorizerSigner\";\nimport {\n isBatchSettlementDepositPayload,\n isBatchSettlementRefundPayload,\n isBatchSettlementVoucherPayload,\n} from \"../types\";\nimport type { BatchSettlementPaymentResponseExtra, BatchSettlementVoucherClaim } from \"../types\";\nimport { computeChannelId } from \"../utils\";\nimport * as Errors from \"../errors\";\nimport type { BatchSettlementEvmScheme } from \"./scheme\";\nimport type { Channel } from \"./storage\";\nimport {\n parseRefundSettlementSnapshot,\n readChannelStateExtra,\n readExtraNumber,\n readExtraString,\n} from \"./utils\";\n\n/**\n * Converts stored channel state into the public response snapshot shape.\n *\n * @param channel - Stored channel state.\n * @param chargedCumulativeAmount - Optional current charged cumulative amount.\n * @returns Response-ready channel snapshot.\n */\nfunction channelStateExtra(\n channel: Pick<\n Channel,\n \"channelId\" | \"balance\" | \"totalClaimed\" | \"withdrawRequestedAt\" | \"refundNonce\"\n >,\n chargedCumulativeAmount?: string,\n): NonNullable<BatchSettlementPaymentResponseExtra[\"channelState\"]> {\n return {\n channelId: channel.channelId as `0x${string}`,\n balance: channel.balance,\n totalClaimed: channel.totalClaimed,\n withdrawRequestedAt: channel.withdrawRequestedAt,\n refundNonce: String(channel.refundNonce),\n ...(chargedCumulativeAmount !== undefined ? { chargedCumulativeAmount } : {}),\n };\n}\n\n/**\n * Lifecycle hook: runs before the facilitator settles a payment.\n *\n * For voucher payloads the server does NOT trigger an onchain settle. Instead, it\n * increments the local `chargedCumulativeAmount` and returns a `skip` result so the\n * middleware responds immediately. Cooperative refund payloads proceed to settlement\n * enrichment before facilitator settlement.\n *\n * @param scheme - Owning `BatchSettlementEvmScheme` instance for storage access.\n * @param ctx - Settle lifecycle context (payload and requirements).\n * @returns Nothing to proceed; `abort` to fail; `skip` with a result to short-circuit settlement.\n */\nexport async function handleBeforeSettle(\n scheme: BatchSettlementEvmScheme,\n ctx: SettleContext,\n): Promise<\n void | { abort: true; reason: string; message?: string } | { skip: true; result: SettleResponse }\n> {\n const { paymentPayload, requirements } = ctx;\n\n const raw = paymentPayload.payload;\n const storage = scheme.getStorage();\n\n if (!isBatchSettlementVoucherPayload(raw)) {\n return;\n }\n\n const { voucher } = raw;\n const channelId = voucher.channelId;\n const pendingId = scheme.readRequestContext(paymentPayload)?.pendingId;\n\n const increment = BigInt(requirements.amount);\n const signedCap = BigInt(voucher.maxClaimableAmount);\n let outcome:\n | { status: \"missing\" }\n | { status: \"pending_mismatch\" }\n | { status: \"cap_exceeded\"; charged: string }\n | { status: \"committed\"; previous: Channel; current: Channel }\n | undefined;\n\n const updateResult = await storage.updateChannel(channelId, current => {\n if (!current) {\n outcome = { status: \"missing\" };\n return current;\n }\n\n if (!pendingId || current.pendingRequest?.pendingId !== pendingId) {\n outcome = { status: \"pending_mismatch\" };\n return current;\n }\n\n const newCharged = BigInt(current.chargedCumulativeAmount) + increment;\n if (newCharged > signedCap) {\n outcome = { status: \"cap_exceeded\", charged: newCharged.toString() };\n return {\n ...current,\n pendingRequest: undefined,\n };\n }\n\n const updatedChannel: Channel = {\n ...current,\n chargedCumulativeAmount: newCharged.toString(),\n signedMaxClaimable: voucher.maxClaimableAmount,\n signature: voucher.signature,\n lastRequestTimestamp: Date.now(),\n pendingRequest: undefined,\n };\n outcome = { status: \"committed\", previous: current, current: updatedChannel };\n return updatedChannel;\n });\n\n if (outcome?.status === \"missing\") {\n scheme.takeRequestContext(paymentPayload);\n return {\n abort: true,\n reason: Errors.ErrMissingChannel,\n message: \"No channel record\",\n };\n }\n\n if (outcome?.status === \"cap_exceeded\") {\n scheme.takeRequestContext(paymentPayload);\n return {\n abort: true,\n reason: Errors.ErrChargeExceedsSignedCumulative,\n message: `Charged ${outcome.charged} exceeds signed max ${signedCap.toString()}`,\n };\n }\n\n if (updateResult.status !== \"updated\" || outcome?.status !== \"committed\") {\n scheme.takeRequestContext(paymentPayload);\n return {\n abort: true,\n reason: Errors.ErrChannelBusy,\n message: \"Concurrent request modified channel state\",\n };\n }\n scheme.takeRequestContext(paymentPayload);\n\n const skipExtra: BatchSettlementPaymentResponseExtra = {\n channelState: channelStateExtra(outcome.previous, outcome.current.chargedCumulativeAmount),\n chargedAmount: requirements.amount,\n };\n\n return {\n skip: true,\n result: {\n success: true,\n payer: outcome.previous.channelConfig.payer.toLowerCase() as `0x${string}`,\n transaction: \"\",\n network: requirements.network,\n amount: \"\",\n extra: skipExtra,\n },\n };\n}\n\n/**\n * Enriches cooperative refund vouchers with facilitator settlement fields.\n *\n * @param scheme - Owning `BatchSettlementEvmScheme` instance for storage and signer access.\n * @param ctx - Settlement context for the current payment.\n * @returns Additive refund settlement fields, or nothing for non-refund payloads.\n */\nexport async function handleEnrichSettlementPayload(\n scheme: BatchSettlementEvmScheme,\n ctx: SettleContext,\n): Promise<Record<string, unknown> | void> {\n const { paymentPayload, requirements } = ctx;\n const raw = paymentPayload.payload;\n if (!isBatchSettlementRefundPayload(raw)) {\n return;\n }\n\n const channelId = computeChannelId(raw.channelConfig, requirements.network);\n if (raw.voucher.channelId !== channelId) {\n throw new Error(\"refund channelId does not match channelConfig\");\n }\n\n const channel = await scheme.getStorage().get(channelId);\n if (!channel) {\n throw new Error(Errors.ErrMissingChannel);\n }\n const pendingId = scheme.readRequestContext(paymentPayload)?.pendingId;\n if (!pendingId || channel.pendingRequest?.pendingId !== pendingId) {\n throw new Error(Errors.ErrChannelBusy);\n }\n if (BigInt(raw.voucher.maxClaimableAmount) !== BigInt(channel.chargedCumulativeAmount)) {\n throw new Error(Errors.ErrCumulativeAmountMismatch);\n }\n if (raw.voucher.signature !== channel.signature) {\n throw new Error(Errors.ErrInvalidVoucherSignature);\n }\n\n const config = raw.channelConfig;\n\n const claimEntry: BatchSettlementVoucherClaim = {\n voucher: {\n channel: config,\n maxClaimableAmount: raw.voucher.maxClaimableAmount,\n },\n signature: raw.voucher.signature,\n totalClaimed: channel.chargedCumulativeAmount,\n };\n\n const remainder = BigInt(channel.balance) - BigInt(channel.chargedCumulativeAmount);\n if (remainder <= 0n) {\n throw new Error(Errors.ErrRefundNoBalance);\n }\n\n let refundAmountBig = remainder;\n if (raw.amount !== undefined) {\n if (!/^\\d+$/.test(raw.amount)) {\n throw new Error(Errors.ErrRefundAmountInvalid);\n }\n const requested = BigInt(raw.amount);\n if (requested <= 0n) {\n throw new Error(Errors.ErrRefundAmountInvalid);\n }\n refundAmountBig = requested;\n }\n\n const refundAmount = refundAmountBig.toString();\n const nonce = String(channel.refundNonce ?? 0);\n\n const receiverAuthorizerSigner = scheme.getReceiverAuthorizerSigner();\n\n const refundAuthorizerSignature = receiverAuthorizerSigner\n ? await signRefund(\n receiverAuthorizerSigner,\n channelId as `0x${string}`,\n refundAmount,\n nonce,\n requirements.network,\n )\n : undefined;\n\n const claimAuthorizerSignature = receiverAuthorizerSigner\n ? await signClaimBatch(receiverAuthorizerSigner, [claimEntry], requirements.network)\n : undefined;\n\n scheme.rememberChannelSnapshot(paymentPayload, channel);\n\n return {\n ...(raw.amount === undefined ? { amount: refundAmount } : {}),\n refundNonce: nonce,\n claims: [claimEntry],\n refundAuthorizerSignature,\n claimAuthorizerSignature,\n };\n}\n\n/**\n * Lifecycle hook: runs after the facilitator settles a payment.\n *\n * Updates channel state to reflect the settlement outcome — adjusting charged amounts,\n * balances, and handling cooperative-refund cleanup (channel record deletion).\n *\n * @param scheme - Owning `BatchSettlementEvmScheme` instance for storage access.\n * @param ctx - Post-settle lifecycle context.\n * @param ctx.paymentPayload - Payment payload that was settled (possibly rewritten).\n * @param ctx.requirements - Requirements used for settlement.\n * @param ctx.result - Facilitator settle response.\n * @returns Resolves when session updates are complete (no return value).\n */\nexport async function handleAfterSettle(\n scheme: BatchSettlementEvmScheme,\n ctx: SettleResultContext,\n): Promise<void> {\n const { paymentPayload, requirements, result } = ctx;\n if (!result.success) {\n return;\n }\n\n const raw = paymentPayload.payload;\n const storage = scheme.getStorage();\n\n if (isBatchSettlementRefundPayload(raw)) {\n const channelId = computeChannelId(raw.channelConfig, requirements.network);\n const pendingId = scheme.readRequestContext(paymentPayload)?.pendingId;\n const now = Date.now();\n\n const snapshot = parseRefundSettlementSnapshot(result.extra);\n const updateResult = await storage.updateChannel(channelId, current => {\n if (!current) {\n return current;\n }\n if (!pendingId || current.pendingRequest?.pendingId !== pendingId) {\n return current;\n }\n if (BigInt(snapshot.balance) <= BigInt(current.chargedCumulativeAmount)) {\n return undefined;\n }\n return {\n ...current,\n ...snapshot,\n onchainSyncedAt: now,\n lastRequestTimestamp: now,\n pendingRequest: undefined,\n };\n });\n if (updateResult.status === \"unchanged\") {\n throw new Error(Errors.ErrChannelBusy);\n }\n if (!updateResult.channel) {\n return;\n }\n return;\n }\n\n if (isBatchSettlementVoucherPayload(raw)) {\n return;\n }\n\n if (isBatchSettlementDepositPayload(raw)) {\n const channelId = raw.voucher.channelId;\n const pendingId = scheme.readRequestContext(paymentPayload)?.pendingId;\n const ex = result.extra ?? {};\n const channelState = readChannelStateExtra(ex);\n const config = raw.channelConfig;\n const signedMaxClaimable = raw.voucher.maxClaimableAmount;\n const now = Date.now();\n\n const updateResult = await storage.updateChannel(channelId, current => {\n if (!current) {\n return current;\n }\n if (!pendingId || current.pendingRequest?.pendingId !== pendingId) {\n return current;\n }\n const chargedActual = (\n BigInt(current.chargedCumulativeAmount) + BigInt(requirements.amount)\n ).toString();\n return {\n channelId,\n channelConfig: config,\n chargedCumulativeAmount: chargedActual,\n signedMaxClaimable,\n signature: raw.voucher.signature,\n balance: readExtraString(channelState, \"balance\", current.balance),\n totalClaimed: readExtraString(channelState, \"totalClaimed\", current.totalClaimed),\n withdrawRequestedAt: readExtraNumber(\n channelState,\n \"withdrawRequestedAt\",\n current.withdrawRequestedAt,\n ),\n refundNonce: readExtraNumber(channelState, \"refundNonce\", current.refundNonce),\n onchainSyncedAt: now,\n lastRequestTimestamp: now,\n };\n });\n if (updateResult.status === \"updated\" && updateResult.channel) {\n scheme.rememberChannelSnapshot(paymentPayload, updateResult.channel);\n return;\n }\n scheme.takeRequestContext(paymentPayload);\n throw new Error(Errors.ErrChannelBusy);\n }\n}\n\n/**\n * Cleanup hook: clears this request's reservation after settlement throws.\n *\n * @param scheme - Owning `BatchSettlementEvmScheme` instance.\n * @param ctx - Settle failure context for the current payment.\n */\nexport async function handleSettleFailure(\n scheme: BatchSettlementEvmScheme,\n ctx: SettleFailureContext,\n): Promise<void> {\n await scheme.clearPendingRequest(ctx.paymentPayload);\n}\n\n/**\n * Supplies server-owned settlement response fields from the channel snapshot.\n *\n * @param scheme - Owning `BatchSettlementEvmScheme` instance for snapshot access.\n * @param ctx - Settlement result context for the current payment.\n * @returns Additive response extra fields, or nothing when no snapshot exists.\n */\nexport async function handleEnrichSettlementResponse(\n scheme: BatchSettlementEvmScheme,\n ctx: SettleResultContext,\n): Promise<Record<string, unknown> | void> {\n const raw = ctx.paymentPayload.payload;\n if (isBatchSettlementVoucherPayload(raw)) {\n return;\n }\n\n const channel = scheme.takeChannelSnapshot(ctx.paymentPayload);\n if (!channel) {\n return;\n }\n\n if (isBatchSettlementRefundPayload(raw)) {\n return {\n channelState: {\n chargedCumulativeAmount: channel.chargedCumulativeAmount,\n },\n };\n }\n\n if (isBatchSettlementDepositPayload(raw)) {\n return {\n channelState: {\n chargedCumulativeAmount: channel.chargedCumulativeAmount,\n },\n chargedAmount: ctx.requirements.amount,\n };\n }\n return {\n channelState: {\n chargedCumulativeAmount: channel.chargedCumulativeAmount,\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACYA,IAAAA,iBAA4D;AAE5D,IAAAC,eAA2B;;;ACd3B,IAAAC,eAA0C;;;ACA1C,kBAAmC;AAG5B,IAAM,0BAA0B;AAGhC,IAAM,2BAA2B;AAWjC,IAAM,qBAAqB;AAG3B,IAAM,qBAAqB;AAG3B,IAAM,0BAA0B;AAAA,EACrC,MAAM;AAAA,EACN,SAAS;AACX;AAGO,IAAM,8BAA0B;AAAA,MACrC;AAAA,IACE;AAAA,EACF;AACF;AAGO,IAAM,qBAAqB;AAAA,EAChC,eAAe;AAAA,IACb,EAAE,MAAM,SAAS,MAAM,UAAU;AAAA,IACjC,EAAE,MAAM,mBAAmB,MAAM,UAAU;AAAA,IAC3C,EAAE,MAAM,YAAY,MAAM,UAAU;AAAA,IACpC,EAAE,MAAM,sBAAsB,MAAM,UAAU;AAAA,IAC9C,EAAE,MAAM,SAAS,MAAM,UAAU;AAAA,IACjC,EAAE,MAAM,iBAAiB,MAAM,SAAS;AAAA,IACxC,EAAE,MAAM,QAAQ,MAAM,UAAU;AAAA,EAClC;AACF;AAGO,IAAM,eAAe;AAAA,EAC1B,SAAS;AAAA,IACP,EAAE,MAAM,aAAa,MAAM,UAAU;AAAA,IACrC,EAAE,MAAM,sBAAsB,MAAM,UAAU;AAAA,EAChD;AACF;AAGO,IAAM,cAAc;AAAA,EACzB,QAAQ;AAAA,IACN,EAAE,MAAM,aAAa,MAAM,UAAU;AAAA,IACrC,EAAE,MAAM,SAAS,MAAM,UAAU;AAAA,IACjC,EAAE,MAAM,UAAU,MAAM,UAAU;AAAA,EACpC;AACF;AAGO,IAAM,kBAAkB;AAAA,EAC7B,YAAY,CAAC,EAAE,MAAM,UAAU,MAAM,eAAe,CAAC;AAAA,EACrD,YAAY;AAAA,IACV,EAAE,MAAM,aAAa,MAAM,UAAU;AAAA,IACrC,EAAE,MAAM,sBAAsB,MAAM,UAAU;AAAA,IAC9C,EAAE,MAAM,gBAAgB,MAAM,UAAU;AAAA,EAC1C;AACF;;;ACzEA,IAAAC,eAAsB;AASf,SAAS,cAAc,SAAyB;AACrD,MAAI,QAAQ,WAAW,SAAS,GAAG;AACjC,UAAM,QAAQ,QAAQ,MAAM,GAAG,EAAE,CAAC;AAClC,UAAM,UAAU,SAAS,OAAO,EAAE;AAClC,QAAI,MAAM,OAAO,GAAG;AAClB,YAAM,IAAI,MAAM,4BAA4B,OAAO,EAAE;AAAA,IACvD;AACA,WAAO;AAAA,EACT;AAEA,QAAM,IAAI,MAAM,+BAA+B,OAAO,6BAA6B;AACrF;AAQA,SAAS,YAAoB;AAC3B,QAAM,YAAY,WAAW;AAC7B,MAAI,CAAC,WAAW;AACd,UAAM,IAAI,MAAM,0BAA0B;AAAA,EAC5C;AACA,SAAO;AACT;AAOO,SAAS,cAA6B;AAC3C,aAAO,oBAAM,UAAU,EAAE,gBAAgB,IAAI,WAAW,EAAE,CAAC,CAAC;AAC9D;;;AF/BO,SAAS,iBACd,QACA,kBACe;AACf,QAAM,UACJ,OAAO,qBAAqB,WAAW,mBAAmB,cAAc,gBAAgB;AAC1F,aAAO,4BAAc;AAAA,IACnB,QAAQ,+BAA+B,OAAO;AAAA,IAC9C,OAAO;AAAA,IACP,aAAa;AAAA,IACb,SAAS;AAAA,MACP,OAAO,OAAO;AAAA,MACd,iBAAiB,OAAO;AAAA,MACxB,UAAU,OAAO;AAAA,MACjB,oBAAoB,OAAO;AAAA,MAC3B,OAAO,OAAO;AAAA,MACd,eAAe,OAAO;AAAA,MACtB,MAAM,OAAO;AAAA,IACf;AAAA,EACF,CAAC;AACH;AAQO,SAAS,+BAA+B,SAAiB;AAC9D,SAAO;AAAA,IACL,GAAG;AAAA,IACH;AAAA,IACA,uBAAmB,yBAAW,wBAAwB;AAAA,EACxD;AACF;;;AGjCA,eAAsB,eACpB,QACA,QACA,SACwB;AACxB,QAAM,UAAU,cAAc,OAAO;AAErC,QAAM,eAAe,OAAO,IAAI,QAAM;AAAA,IACpC,WAAW,iBAAiB,EAAE,QAAQ,SAAS,OAAO;AAAA,IACtD,oBAAoB,OAAO,EAAE,QAAQ,kBAAkB;AAAA,IACvD,cAAc,OAAO,EAAE,YAAY;AAAA,EACrC,EAAE;AAEF,SAAO,OAAO,cAAc;AAAA,IAC1B,QAAQ,+BAA+B,OAAO;AAAA,IAC9C,OAAO;AAAA,IACP,aAAa;AAAA,IACb,SAAS;AAAA,MACP,QAAQ;AAAA,IACV;AAAA,EACF,CAAC;AACH;AAYA,eAAsB,WACpB,QACA,WACA,QACA,OACA,SACwB;AACxB,QAAM,UAAU,cAAc,OAAO;AAErC,SAAO,OAAO,cAAc;AAAA,IAC1B,QAAQ,+BAA+B,OAAO;AAAA,IAC9C,OAAO;AAAA,IACP,aAAa;AAAA,IACb,SAAS;AAAA,MACP;AAAA,MACA,OAAO,OAAO,KAAK;AAAA,MACnB,QAAQ,OAAO,MAAM;AAAA,IACvB;AAAA,EACF,CAAC;AACH;;;ACQA,IAAM,oBAA+B,CAAC,SAAS,UAAU,QAAQ;AASjE,SAAS,yBAAyB,WAAmB,UAAkC;AACrF,SAAO,GAAG,SAAS,YAAY,SAAS,eAAe,SAAS,WAAM,SAAS,gBAAgB,EAAE;AACnG;AASA,SAAS,sBAAsB,SAAkB,MAAM,KAAK,IAAI,GAAY;AAC1E,SAAO,QAAQ,mBAAmB,UAAa,QAAQ,eAAe,YAAY;AACpF;AASO,IAAM,gCAAN,MAAoC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAqBzC,YAAY,QAA8B;AAd1C,SAAQ,SAAmE,CAAC;AAC5E,SAAQ,gBAAgB;AACxB,SAAQ,iBAAiB;AACzB,SAAQ,gBAAgB;AACxB,SAAQ,UAAU;AAClB,SAAQ,cAAc,oBAAI,IAAa;AACvC,SAAQ,eAAe;AACvB,SAAQ,mBAAyC,CAAC;AAQhD,SAAK,SAAS,OAAO;AACrB,SAAK,cAAc,OAAO;AAC1B,SAAK,WAAW,OAAO;AACvB,SAAK,QAAQ,OAAO;AACpB,SAAK,UAAU,OAAO;AAAA,EACxB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,MAAM,MAAM,MAA6C;AACvD,UAAM,WAAW,MAAM,KAAK,mBAAmB,IAAI;AACnD,WAAO,KAAK,kBAAkB,UAAU;AAAA,MACtC,mBAAmB,MAAM,qBAAqB;AAAA,MAC9C,GAAI,MAAM,aAAa,SAAY,EAAE,UAAU,KAAK,SAAS,IAAI,CAAC;AAAA,IACpE,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,SAAgC;AACpC,UAAM,iBAAiB,KAAK,0BAA0B;AACtD,UAAM,eAAe,KAAK,yBAAyB;AAEnD,UAAM,WAAW,MAAM,KAAK,YAAY,OAAO,gBAAgB,YAAY;AAC3E,QAAI,CAAC,SAAS,SAAS;AACrB,YAAM,IAAI,MAAM,yBAAyB,UAAU,QAAQ,CAAC;AAAA,IAC9D;AAEA,SAAK,gBAAgB;AACrB,WAAO,EAAE,aAAa,SAAS,YAAY;AAAA,EAC7C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,MAAM,eACJ,MAC2D;AAC3D,UAAM,SAAS,MAAM,KAAK,MAAM,IAAI;AACpC,QAAI;AACJ,QAAI,OAAO,SAAS,GAAG;AACrB,qBAAe,MAAM,KAAK,OAAO;AAAA,IACnC;AACA,WAAO,EAAE,QAAQ,QAAQ,aAAa;AAAA,EACxC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,OAAO,YAAgD;AAC3D,UAAM,UAAU,KAAK,OAAO,WAAW;AACvC,UAAM,WAAW,MAAM,QAAQ,KAAK;AAEpC,UAAM,MAAM,KAAK,IAAI;AACrB,UAAM,WACJ,aACI,SAAS;AAAA,MAAO,OACd,WAAW,KAAK,QAAM,GAAG,YAAY,MAAM,EAAE,UAAU,YAAY,CAAC;AAAA,IACtE,IACA,UACJ,OAAO,aAAW,CAAC,sBAAsB,SAAS,GAAG,CAAC;AAExD,QAAI,QAAQ,WAAW,GAAG;AACxB,aAAO,CAAC;AAAA,IACV;AAEA,WAAO,KAAK,eAAe,OAAO;AAAA,EACpC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,mBAAmB,MAAqD;AAC5E,UAAM,WAAW,MAAM,KAAK,yBAAyB,KAAK,QAAQ;AAClE,WAAO,KAAK,eAAe,QAAQ;AAAA,EACrC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,MAAM,qBAAqB,MAAsE;AAC/F,UAAM,WAAW,MAAM,KAAK,OAAO,WAAW,EAAE,KAAK;AACrD,WAAO,KAAK,iCAAiC,UAAU,IAAI;AAAA,EAC7D;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,+BAAmD;AACvD,UAAM,WAAW,MAAM,KAAK,OAAO,WAAW,EAAE,KAAK;AACrD,WAAO,SAAS,OAAO,OAAK,EAAE,sBAAsB,CAAC;AAAA,EACvD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,SAA+B,CAAC,GAAS;AAC7C,QAAI,KAAK,SAAS;AAChB;AAAA,IACF;AAEA,UAAM,MAAM,KAAK,IAAI;AACrB,SAAK,gBAAgB;AACrB,SAAK,iBAAiB;AACtB,SAAK,UAAU;AACf,SAAK,mBAAmB;AAExB,SAAK,eAAe,SAAS,OAAO,iBAAiB;AACrD,SAAK,eAAe,UAAU,OAAO,kBAAkB;AACvD,SAAK,eAAe,UAAU,OAAO,kBAAkB;AAAA,EACzD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,KAAK,MAA2C;AACpD,SAAK,UAAU;AACf,eAAW,SAAS,OAAO,OAAO,KAAK,MAAM,GAAG;AAC9C,oBAAc,KAAK;AAAA,IACrB;AACA,SAAK,SAAS,CAAC;AACf,SAAK,YAAY,MAAM;AAEvB,QAAI,MAAM,OAAO;AACf,YAAM,KAAK,eAAe;AAAA,QACxB,mBAAmB,KAAK,iBAAiB;AAAA,QACzC,qBAAqB,KAAK,iBAAiB;AAAA,MAC7C,CAAC;AAAA,IACH;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAc,cAAc,QAAwC;AAClE,UAAM,mBAAmB,KAAK,OAAO,4BAA4B;AACjE,UAAM,SAAS,KAAK,kBAAkB,MAAM;AAE5C,UAAM,gBACJ,OAAO,OAAO,OAAO,IAAI,OAAO,OAAO,uBAAuB,GAC9D,SAAS;AAEX,UAAM,QAAQ,OAAO,OAAO,eAAe,CAAC;AAE5C,UAAM,4BAA4B,mBAC9B,MAAM;AAAA,MACJ;AAAA,MACA,OAAO;AAAA,MACP;AAAA,MACA;AAAA,MACA,KAAK;AAAA,IACP,IACA;AAEJ,UAAM,2BACJ,oBAAoB,OAAO,SAAS,IAChC,MAAM,eAAe,kBAAkB,QAAQ,KAAK,OAAO,IAC3D;AAEN,UAAM,iBAAiC;AAAA,MACrC,aAAa;AAAA,MACb,UAAU,KAAK,yBAAyB;AAAA,MACxC,SAAS;AAAA,QACP,MAAM;AAAA,QACN,eAAe,OAAO;AAAA,QACtB,SAAS;AAAA,UACP,WAAW,OAAO;AAAA,UAClB,oBAAoB,OAAO;AAAA,UAC3B,WAAW,OAAO;AAAA,QACpB;AAAA,QACA,QAAQ;AAAA,QACR,aAAa;AAAA,QACb;AAAA,QACA,GAAI,4BAA4B,EAAE,0BAA0B,IAAI,CAAC;AAAA,QACjE,GAAI,2BAA2B,EAAE,yBAAyB,IAAI,CAAC;AAAA,MACjE;AAAA,IACF;AAEA,UAAM,WAAW,MAAM,KAAK,YAAY,OAAO,gBAAgB,KAAK,yBAAyB,CAAC;AAC9F,QAAI,CAAC,SAAS,SAAS;AACrB,YAAM,IAAI,MAAM,yBAAyB,UAAU,QAAQ,CAAC;AAAA,IAC9D;AAEA,UAAM,KAAK,OACR,WAAW,EACX;AAAA,MAAc,OAAO;AAAA,MAAW,aAC/B,WAAW,CAAC,sBAAsB,OAAO,IAAI,SAAY;AAAA,IAC3D;AAEF,WAAO;AAAA,MACL,SAAS,OAAO;AAAA,MAChB,aAAa,SAAS;AAAA,IACxB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,eAAe,KAAc,cAA6B;AAChE,QAAI,iBAAiB,QAAW;AAC9B;AAAA,IACF;AAEA,SAAK,OAAO,GAAG,IAAI,YAAY,MAAM;AACnC,WAAK,WAAW,GAAG;AAAA,IACrB,GAAG,eAAe,GAAI;AAAA,EACxB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,WAAW,KAAoB;AACrC,QAAI,CAAC,KAAK,SAAS;AACjB;AAAA,IACF;AAEA,SAAK,YAAY,IAAI,GAAG;AACxB,QAAI,CAAC,KAAK,cAAc;AACtB,WAAK,KAAK,UAAU;AAAA,IACtB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,YAA2B;AACvC,QAAI,KAAK,cAAc;AACrB;AAAA,IACF;AAEA,SAAK,eAAe;AACpB,QAAI;AACF,aAAO,KAAK,WAAW,KAAK,YAAY,OAAO,GAAG;AAChD,cAAM,MAAM,KAAK,eAAe;AAChC,YAAI,CAAC,KAAK;AACR;AAAA,QACF;AACA,aAAK,YAAY,OAAO,GAAG;AAC3B,cAAM,KAAK,WAAW,GAAG;AAAA,MAC3B;AAAA,IACF,UAAE;AACA,WAAK,eAAe;AAAA,IACtB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,iBAAsC;AAC5C,WAAO,kBAAkB,KAAK,SAAO,KAAK,YAAY,IAAI,GAAG,CAAC;AAAA,EAChE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAc,WAAW,KAA6B;AACpD,YAAQ,KAAK;AAAA,MACX,KAAK;AACH,cAAM,KAAK,YAAY;AACvB;AAAA,MACF,KAAK;AACH,cAAM,KAAK,aAAa;AACxB;AAAA,MACF,KAAK;AACH,cAAM,KAAK,aAAa;AACxB;AAAA,IACJ;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,cAA6B;AACzC,UAAM,MAAM,KAAK;AACjB,QAAI;AACF,YAAM,UAAU,MAAM,KAAK,mBAAmB;AAAA,QAC5C,qBAAqB,IAAI;AAAA,MAC3B,CAAC;AACD,YAAM,UAAU,MAAM,KAAK,kBAAkB,SAAS;AAAA,QACpD,mBAAmB,IAAI,qBAAqB;AAAA,MAC9C,CAAC;AAED,WAAK,gBAAgB,KAAK,IAAI;AAC9B,iBAAW,UAAU,SAAS;AAC5B,YAAI,UAAU,MAAM;AAAA,MACtB;AAAA,IACF,SAAS,KAAK;AACZ,UAAI,UAAU,GAAG;AAAA,IACnB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,eAA8B;AAC1C,UAAM,MAAM,KAAK;AACjB,UAAM,UAAU,KAAK,2BAA2B,KAAK,IAAI,CAAC;AAC1D,QAAI,CAAC,QAAQ,eAAe;AAC1B;AAAA,IACF;AAEA,QAAI;AACF,UAAI,IAAI,gBAAgB,CAAE,MAAM,IAAI,aAAa,OAAO,GAAI;AAC1D;AAAA,MACF;AAEA,YAAM,SAAS,MAAM,KAAK,OAAO;AACjC,WAAK,iBAAiB,KAAK,IAAI;AAC/B,UAAI,WAAW,MAAM;AAAA,IACvB,SAAS,KAAK;AACZ,UAAI,UAAU,GAAG;AAAA,IACnB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,eAA8B;AAC1C,UAAM,MAAM,KAAK;AACjB,QAAI,CAAC,IAAI,sBAAsB;AAC7B;AAAA,IACF;AAEA,QAAI;AACF,YAAM,UAAU,KAAK,2BAA2B,KAAK,IAAI,CAAC;AAC1D,YAAM,WAAW,MAAM,KAAK,OAAO,WAAW,EAAE,KAAK;AACrD,YAAM,UAAU,MAAM,IAAI,qBAAqB,UAAU,OAAO;AAChE,iBAAW,UAAU,MAAM,KAAK,eAAe,OAAO,GAAG;AACvD,YAAI,WAAW,MAAM;AAAA,MACvB;AAAA,IACF,SAAS,KAAK;AACZ,UAAI,UAAU,GAAG;AAAA,IACnB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,MAAc,kBACZ,UACA,MAIwB;AACxB,UAAM,YAAY,KAAK;AAAA,MACrB;AAAA,MACA,KAAK,aAAa,SAAY,EAAE,UAAU,KAAK,SAAS,IAAI;AAAA,IAC9D;AAEA,QAAI,UAAU,WAAW,GAAG;AAC1B,aAAO,CAAC;AAAA,IACV;AAEA,UAAM,UAAyB,CAAC;AAChC,aAAS,IAAI,GAAG,IAAI,UAAU,QAAQ,KAAK,KAAK,mBAAmB;AACjE,YAAM,QAAQ,UAAU,MAAM,GAAG,IAAI,KAAK,iBAAiB;AAC3D,YAAM,SAAS,MAAM,KAAK,YAAY,KAAK;AAC3C,cAAQ,KAAK,MAAM;AACnB,YAAM,KAAK,sBAAsB,KAAK;AAAA,IACxC;AAEA,QAAI,QAAQ,SAAS,GAAG;AACtB,WAAK,gBAAgB;AAAA,IACvB;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAc,mBACZ,MACoB;AACpB,UAAM,WAAW,MAAM,KAAK,OAAO,WAAW,EAAE,KAAK;AACrD,QAAI,CAAC,MAAM,qBAAqB;AAC9B,aAAO;AAAA,IACT;AAEA,UAAM,UAAU,KAAK,2BAA2B,KAAK,IAAI,CAAC;AAC1D,WAAO,KAAK,oBAAoB,UAAU,OAAO;AAAA,EACnD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAc,eAAe,UAA8C;AACzE,UAAM,UAA0B,CAAC;AACjC,eAAW,WAAW,UAAU;AAC9B,UAAI,sBAAsB,OAAO,GAAG;AAClC;AAAA,MACF;AACA,cAAQ,KAAK,MAAM,KAAK,cAAc,OAAO,CAAC;AAAA,IAChD;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,kBAAkB,SAAiD;AACzE,QAAI,OAAO,QAAQ,uBAAuB,KAAK,OAAO,QAAQ,YAAY,GAAG;AAC3E,aAAO,CAAC;AAAA,IACV;AAEA,WAAO;AAAA,MACL;AAAA,QACE,SAAS;AAAA,UACP,SAAS,QAAQ;AAAA,UACjB,oBAAoB,QAAQ;AAAA,QAC9B;AAAA,QACA,WAAW,QAAQ;AAAA,QACnB,cAAc,QAAQ;AAAA,MACxB;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,2BAA2B,KAAoC;AACrE,WAAO;AAAA,MACL;AAAA,MACA,eAAe,KAAK;AAAA,MACpB,gBAAgB,KAAK;AAAA,MACrB,eAAe,KAAK;AAAA,IACtB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUQ,iCACN,UACA,MAC+B;AAC/B,UAAM,MAAM,KAAK,IAAI;AACrB,UAAM,SAAwC,CAAC;AAE/C,eAAW,KAAK,UAAU;AACxB,UAAI,OAAO,EAAE,uBAAuB,KAAK,OAAO,EAAE,YAAY,GAAG;AAC/D;AAAA,MACF;AACA,UAAI,MAAM,aAAa,QAAW;AAChC,cAAM,SAAS,MAAM,EAAE;AACvB,YAAI,SAAS,KAAK,WAAW,KAAM;AACjC;AAAA,QACF;AAAA,MACF;AACA,aAAO,KAAK;AAAA,QACV,SAAS;AAAA,UACP,SAAS,EAAE;AAAA,UACX,oBAAoB,EAAE;AAAA,QACxB;AAAA,QACA,WAAW,EAAE;AAAA,QACb,cAAc,EAAE;AAAA,MAClB,CAAC;AAAA,IACH;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASQ,qCAAqC,UAAqB,UAA6B;AAC7F,UAAM,MAAM,KAAK,IAAI;AACrB,UAAM,SAAS,WAAW;AAC1B,WAAO,SAAS,OAAO,OAAK;AAC1B,UAAI,OAAO,EAAE,OAAO,MAAM,GAAI,QAAO;AACrC,UAAI,sBAAsB,GAAG,GAAG,EAAG,QAAO;AAC1C,aAAO,MAAM,EAAE,wBAAwB;AAAA,IACzC,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAc,yBAAyB,UAAsC;AAC3E,UAAM,WAAW,MAAM,KAAK,OAAO,WAAW,EAAE,KAAK;AACrD,WAAO,KAAK,qCAAqC,UAAU,QAAQ;AAAA,EACrE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAc,YAAY,QAA6D;AACrF,UAAM,mBAAmB,KAAK,OAAO,4BAA4B;AAEjE,UAAM,2BAA2B,mBAC7B,MAAM,eAAe,kBAAkB,QAAQ,KAAK,OAAO,IAC3D;AAEJ,UAAM,iBAAiC;AAAA,MACrC,aAAa;AAAA,MACb,UAAU,KAAK,yBAAyB;AAAA,MACxC,SAAS;AAAA,QACP,MAAM;AAAA,QACN;AAAA,QACA,GAAI,2BAA2B,EAAE,yBAAyB,IAAI,CAAC;AAAA,MACjE;AAAA,IACF;AAEA,UAAM,WAA2B,MAAM,KAAK,YAAY;AAAA,MACtD;AAAA,MACA,KAAK,yBAAyB;AAAA,IAChC;AAEA,QAAI,CAAC,SAAS,SAAS;AACrB,YAAM,IAAI,MAAM,yBAAyB,SAAS,QAAQ,CAAC;AAAA,IAC7D;AAEA,WAAO,EAAE,UAAU,OAAO,QAAQ,aAAa,SAAS,YAAY;AAAA,EACtE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,4BAA4C;AAClD,WAAO;AAAA,MACL,aAAa;AAAA,MACb,UAAU,KAAK,yBAAyB;AAAA,MACxC,SAAS;AAAA,QACP,MAAM;AAAA,QACN,UAAU,KAAK;AAAA,QACf,OAAO,KAAK;AAAA,MACd;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,2BAAgD;AACtD,WAAO;AAAA,MACL,QAAQ;AAAA,MACR,SAAS,KAAK;AAAA,MACd,OAAO,KAAK;AAAA,MACZ,QAAQ;AAAA,MACR,OAAO,KAAK;AAAA,MACZ,mBAAmB;AAAA,MACnB,OAAO,CAAC;AAAA,IACV;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAc,sBAAsB,QAAsD;AACxF,UAAM,UAAU,KAAK,OAAO,WAAW;AACvC,eAAW,SAAS,QAAQ;AAC1B,YAAM,YAAY,iBAAiB,MAAM,QAAQ,SAAS,KAAK,OAAO;AACtE,YAAM,UAAU,MAAM,QAAQ,IAAI,SAAS;AAC3C,UAAI,CAAC,SAAS;AACZ;AAAA,MACF;AACA,YAAM,gBAAgB,OAAO,MAAM,YAAY;AAC/C,UAAI,iBAAiB,OAAO,QAAQ,YAAY,GAAG;AACjD;AAAA,MACF;AACA,YAAM,QAAQ,cAAc,WAAW,aAAW;AAChD,YAAI,CAAC,WAAW,iBAAiB,OAAO,QAAQ,YAAY,GAAG;AAC7D,iBAAO;AAAA,QACT;AACA,eAAO;AAAA,UACL,GAAG;AAAA,UACH,cAAc,cAAc,SAAS;AAAA,QACvC;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AACF;;;AC5uBO,IAAM,sBAA6D;AAAA,EACxE,eAAe;AAAA,IACb,SAAS;AAAA,IACT,MAAM;AAAA,IACN,SAAS;AAAA,IACT,UAAU;AAAA,EACZ;AAAA;AAAA,EACA,gBAAgB;AAAA,IACd,SAAS;AAAA,IACT,MAAM;AAAA,IACN,SAAS;AAAA,IACT,UAAU;AAAA,EACZ;AAAA;AAAA,EACA,eAAe;AAAA,IACb,SAAS;AAAA,IACT,MAAM;AAAA,IACN,SAAS;AAAA,IACT,UAAU;AAAA,IACV,qBAAqB;AAAA,IACrB,iBAAiB;AAAA,EACnB;AAAA;AAAA,EACA,cAAc;AAAA,IACZ,SAAS;AAAA,IACT,MAAM;AAAA,IACN,SAAS;AAAA,IACT,UAAU;AAAA,EACZ;AAAA;AAAA,EACA,cAAc;AAAA,IACZ,SAAS;AAAA,IACT,MAAM;AAAA,IACN,SAAS;AAAA,IACT,UAAU;AAAA,EACZ;AAAA;AAAA,EACA,eAAe;AAAA,IACb,SAAS;AAAA,IACT,MAAM;AAAA,IACN,SAAS;AAAA,IACT,UAAU;AAAA,EACZ;AAAA;AAAA,EACA,cAAc;AAAA,IACZ,SAAS;AAAA,IACT,MAAM;AAAA,IACN,SAAS;AAAA,IACT,UAAU;AAAA,EACZ;AAAA;AAAA,EACA,gBAAgB;AAAA,IACd,SAAS;AAAA,IACT,MAAM;AAAA,IACN,SAAS;AAAA,IACT,UAAU;AAAA,EACZ;AAAA;AAAA,EACA,iBAAiB;AAAA,IACf,SAAS;AAAA,IACT,MAAM;AAAA,IACN,SAAS;AAAA,IACT,UAAU;AAAA,EACZ;AAAA;AAAA,EACA,gBAAgB;AAAA,IACd,SAAS;AAAA,IACT,MAAM;AAAA,IACN,SAAS;AAAA,IACT,UAAU;AAAA,IACV,qBAAqB;AAAA,IACrB,iBAAiB;AAAA,EACnB;AAAA;AAAA,EACA,iBAAiB;AAAA,IACf,SAAS;AAAA,IACT,MAAM;AAAA,IACN,SAAS;AAAA,IACT,UAAU;AAAA,IACV,qBAAqB;AAAA,IACrB,iBAAiB;AAAA,EACnB;AAAA;AAAA,EACA,gBAAgB;AAAA,IACd,SAAS;AAAA,IACT,MAAM;AAAA,IACN,SAAS;AAAA,IACT,UAAU;AAAA,IACV,qBAAqB;AAAA,IACrB,iBAAiB;AAAA,EACnB;AAAA;AAAA,EACA,gBAAgB;AAAA,IACd,SAAS;AAAA,IACT,MAAM;AAAA,IACN,SAAS;AAAA,IACT,UAAU;AAAA,EACZ;AAAA;AAAA,EACA,iBAAiB;AAAA,IACf,SAAS;AAAA,IACT,MAAM;AAAA,IACN,SAAS;AAAA,IACT,UAAU;AAAA,EACZ;AAAA;AAAA,EACA,iBAAiB;AAAA,IACf,SAAS;AAAA,IACT,MAAM;AAAA,IACN,SAAS;AAAA,IACT,UAAU;AAAA,EACZ;AAAA;AACF;AASO,SAAS,gBAAgB,SAAyC;AACvE,QAAM,OAAO,oBAAoB,OAAO;AACxC,MAAI,CAAC,MAAM;AACT,UAAM,IAAI,MAAM,2CAA2C,OAAO,EAAE;AAAA,EACtE;AACA,SAAO;AACT;;;ACvGO,IAAM,yBAAN,MAAuD;AAAA,EAAvD;AACL,SAAiB,WAAW,oBAAI,IAAqB;AACrD,SAAiB,eAAe,oBAAI,IAA2B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQ/D,MAAM,IAAI,WAAiD;AACzD,WAAO,KAAK,SAAS,IAAI,UAAU,YAAY,CAAC;AAAA,EAClD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,OAA2B;AAC/B,WAAO,CAAC,GAAG,KAAK,SAAS,OAAO,CAAC;AAAA,EACnC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,cACJ,WACA,QAC8B;AAC9B,UAAM,MAAM,UAAU,YAAY;AAClC,WAAO,KAAK,gBAAgB,KAAK,YAAY;AAC3C,YAAM,UAAU,KAAK,SAAS,IAAI,GAAG;AACrC,YAAM,OAAO,OAAO,OAAO;AAE3B,UAAI,SAAS,SAAS;AACpB,eAAO,EAAE,SAAS,SAAS,QAAQ,YAAY;AAAA,MACjD;AAEA,UAAI,CAAC,MAAM;AACT,aAAK,SAAS,OAAO,GAAG;AACxB,eAAO,EAAE,SAAS,QAAW,QAAQ,UAAU,YAAY,YAAY;AAAA,MACzE;AAEA,WAAK,SAAS,IAAI,KAAK,IAAI;AAC3B,aAAO,EAAE,SAAS,MAAM,QAAQ,UAAU;AAAA,IAC5C,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAc,gBAAmB,KAAa,IAAkC;AAC9E,UAAM,WAAW,KAAK,aAAa,IAAI,GAAG,KAAK,QAAQ,QAAQ;AAC/D,QAAI;AACJ,UAAM,UAAU,IAAI,QAAc,aAAW;AAC3C,gBAAU;AAAA,IACZ,CAAC;AACD,UAAM,OAAO,SAAS,MAAM,MAAM;AAAA,IAAC,CAAC,EAAE,KAAK,MAAM,OAAO;AACxD,SAAK,aAAa,IAAI,KAAK,IAAI;AAE/B,UAAM,SAAS,MAAM,MAAM;AAAA,IAAC,CAAC;AAC7B,QAAI;AACF,aAAO,MAAM,GAAG;AAAA,IAClB,UAAE;AACA,cAAQ;AACR,UAAI,KAAK,aAAa,IAAI,GAAG,MAAM,MAAM;AACvC,aAAK,aAAa,OAAO,GAAG;AAAA,MAC9B;AAAA,IACF;AAAA,EACF;AACF;;;AC5HA,IAAAC,eAA4C;;;ACqK5C,SAAS,SAAS,SAAsD;AACtE,SAAO,OAAO,YAAY,YAAY,YAAY;AACpD;AAQA,SAAS,gBAAgB,SAA2D;AAClF,SACE,SAAS,OAAO,KAChB,eAAe,WACf,wBAAwB,WACxB,eAAe;AAEnB;AAQO,SAAS,gCACd,SAC0C;AAC1C,SACE,SAAS,OAAO,KAChB,QAAQ,SAAS,aACjB,mBAAmB,WACnB,gBAAgB,QAAQ,OAAO,KAC/B,SAAS,QAAQ,OAAO,KACxB,OAAO,QAAQ,QAAQ,WAAW,YAClC,SAAS,QAAQ,QAAQ,aAAa;AAE1C;AAQO,SAAS,gCACd,SAC0C;AAC1C,SACE,SAAS,OAAO,KAChB,QAAQ,SAAS,aACjB,mBAAmB,WACnB,gBAAgB,QAAQ,OAAO;AAEnC;AAQO,SAAS,+BACd,SACyC;AACzC,SACE,SAAS,OAAO,KAChB,QAAQ,SAAS,YACjB,mBAAmB,WACnB,gBAAgB,QAAQ,OAAO;AAEnC;;;ACpPA,IAAAC,eAAmE;;;ACAnE,IAAAC,eAAyD;;;ACGlD,IAAM,mBAAmB;AACzB,IAAM,6BAA6B;AACnC,IAAM,8BACX;AACK,IAAM,kCACX;AAmBK,IAAM,6BACX;AACK,IAAM,uBAAuB;AAC7B,IAAM,sBAAsB;AAC5B,IAAM,gCACX;AACK,IAAM,2BAA2B;AAOjC,IAAM,mBAAmB;AAoBzB,IAAM,8BACX;AACK,IAAM,iBAAiB;AACvB,IAAM,mCACX;AACK,IAAM,oBAAoB;AAC1B,IAAM,qBAAqB;AAC3B,IAAM,yBAAyB;;;AFwE/B,SAAS,sBACd,QACA,WACA,cACoB;AACpB,QAAM,aAAa,iBAAiB,QAAQ,aAAa,OAAO;AAChE,MAAI,WAAW,YAAY,MAAM,UAAU,YAAY,GAAG;AACxD,WAAc;AAAA,EAChB;AAEA,UAAI,yBAAW,OAAO,QAAQ,UAAM,yBAAW,aAAa,KAAK,GAAG;AAClE,WAAc;AAAA,EAChB;AAEA,QAAM,QAAQ,aAAa;AAC3B,QAAM,6BAA6B,OAAO;AAE1C,MACE,CAAC,kCACD,yBAAW,0BAA0B,MAAM,oDAC3C,yBAAW,OAAO,kBAAkB,UAAM,yBAAW,0BAA0B,GAC/E;AACA,WAAc;AAAA,EAChB;AAEA,UAAI,yBAAW,OAAO,KAAK,UAAM,yBAAW,aAAa,KAAK,GAAG;AAC/D,WAAc;AAAA,EAChB;AAEA,MAAI,OAAO,kBAAkB,UAAa,OAAO,kBAAkB,OAAO,MAAM,aAAa,GAAG;AAC9F,WAAc;AAAA,EAChB;AAEA,MAAI,OAAO,gBAAgB,sBAAsB,OAAO,gBAAgB,oBAAoB;AAC1F,WAAc;AAAA,EAChB;AAEA,SAAO;AACT;;;AGxKO,SAAS,sBACd,OACuD;AACvD,QAAM,QAAQ,OAAO;AACrB,MAAI,OAAO,UAAU,YAAY,UAAU,MAAM;AAC/C,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAUO,SAAS,gBACd,OACA,KACA,UACQ;AACR,QAAM,QAAQ,QAAQ,GAAG;AACzB,MAAI,OAAO,UAAU,SAAU,QAAO;AACtC,MAAI,OAAO,UAAU,SAAU,QAAO,OAAO,KAAK;AAClD,SAAO;AACT;AAUO,SAAS,gBACd,OACA,KACA,UACQ;AACR,QAAM,QAAQ,QAAQ,GAAG;AACzB,MAAI,OAAO,UAAU,SAAU,QAAO;AACtC,MAAI,OAAO,UAAU,SAAU,QAAO,SAAS,OAAO,EAAE,KAAK;AAC7D,SAAO;AACT;AAeO,SAAS,8BACd,OAC0B;AAC1B,QAAM,eAAe,sBAAsB,KAAK;AAChD,SAAO;AAAA,IACL,SAAS,qBAAqB,cAAc,SAAS;AAAA,IACrD,cAAc,qBAAqB,cAAc,cAAc;AAAA,IAC/D,qBAAqB,qBAAqB,cAAc,qBAAqB;AAAA,IAC7E,aAAa,qBAAqB,cAAc,aAAa;AAAA,EAC/D;AACF;AASA,SAAS,qBACP,OACA,KACQ;AACR,QAAM,QAAQ,QAAQ,GAAG;AACzB,MAAI,OAAO,UAAU,YAAY,QAAQ,KAAK,KAAK,EAAG,QAAO;AAC7D,MAAI,OAAO,UAAU,YAAY,OAAO,UAAU,KAAK,KAAK,SAAS,EAAG,QAAO,OAAO,KAAK;AAC3F,QAAM,IAAI,MAAM,gBAAgB;AAClC;AASA,SAAS,qBACP,OACA,KACQ;AACR,QAAM,QAAQ,QAAQ,GAAG;AACzB,MAAI,OAAO,UAAU,YAAY,OAAO,UAAU,KAAK,KAAK,SAAS,EAAG,QAAO;AAC/E,MAAI,OAAO,UAAU,YAAY,QAAQ,KAAK,KAAK,GAAG;AACpD,UAAM,SAAS,SAAS,OAAO,EAAE;AACjC,QAAI,CAAC,OAAO,MAAM,MAAM,EAAG,QAAO;AAAA,EACpC;AACA,QAAM,IAAI,MAAM,gBAAgB;AAClC;;;ALxFA,IAAM,qBAAqB;AAC3B,IAAM,qBAAqB,KAAK,KAAK;AASrC,SAAS,iBAAiB,mBAAuC,KAAqB;AACpF,QAAM,cAAc,KAAK,IAAI,GAAG,qBAAqB,CAAC,IAAI;AAC1D,QAAM,QAAQ,KAAK,IAAI,oBAAoB,KAAK,IAAI,oBAAoB,WAAW,CAAC;AACpF,SAAO,MAAM;AACf;AASA,SAAS,cAAc,SAAqC,KAAsB;AAChF,SAAO,YAAY,UAAa,QAAQ,YAAY;AACtD;AAkBA,eAAsB,mBACpB,QACA,KAGA;AACA,QAAM,EAAE,gBAAgB,aAAa,IAAI;AAEzC,QAAM,MAAM,eAAe;AAC3B,QAAM,gBACJ,gCAAgC,GAAG,KAAK,gCAAgC,GAAG;AAC7E,QAAM,sBAAsB,+BAA+B,GAAG;AAC9D,MAAI,CAAC,iBAAiB,CAAC,qBAAqB;AAC1C;AAAA,EACF;AAEA,QAAM,YAAY,IAAI,QAAQ;AAC9B,QAAM,MAAM,KAAK,IAAI;AACrB,QAAM,YAAY,YAAY;AAC9B,MAAI;AAMJ,QAAM,OAAO,WAAW,EAAE,cAAc,WAAW,aAAW;AAC5D,QAAI,cAAc,SAAS,gBAAgB,GAAG,GAAG;AAC/C,gBAAU,EAAE,QAAQ,OAAO;AAC3B,aAAO;AAAA,IACT;AAEA,UAAM,0BACJ,SAAS,2BACT;AAAA,MACE,IAAI,QAAQ;AAAA,MACZ,aAAa;AAAA,MACb;AAAA,IACF;AACF,UAAM,uBAAuB,sBACzB,OAAO,uBAAuB,IAC9B,OAAO,uBAAuB,IAAI,OAAO,aAAa,MAAM;AAEhE,QAAI,OAAO,IAAI,QAAQ,kBAAkB,MAAM,sBAAsB;AACnE,UAAI,SAAS;AACX,kBAAU,EAAE,QAAQ,YAAY,SAAS,QAAQ;AAAA,MACnD,OAAO;AACL,kBAAU;AAAA,UACR,QAAQ;AAAA,UACR,SAAS,wBAAwB,KAAK,uBAAuB;AAAA,QAC/D;AAAA,MACF;AACA,aAAO;AAAA,IACT;AAEA,UAAM,iBAAiC;AAAA,MACrC;AAAA,MACA,oBAAoB,IAAI,QAAQ;AAAA,MAChC,WAAW,iBAAiB,aAAa,mBAAmB,GAAG;AAAA,IACjE;AAEA,cAAU,EAAE,QAAQ,YAAY,iBAAiB,QAAQ;AACzD,WAAO;AAAA,MACL,GAAI,WAAW,wBAAwB,KAAK,uBAAuB;AAAA,MACnE;AAAA,MACA,sBAAsB;AAAA,IACxB;AAAA,EACF,CAAC;AAED,MAAI,SAAS,WAAW,QAAQ;AAC9B,WAAO;AAAA,MACL,OAAO;AAAA,MACP,QAAe;AAAA,MACf,SAAS;AAAA,IACX;AAAA,EACF;AAEA,MAAI,SAAS,WAAW,YAAY;AAClC,WAAO,wBAAwB,gBAAgB,QAAQ,OAAO;AAC9D,WAAO;AAAA,MACL,OAAO;AAAA,MACP,QAAe;AAAA,MACf,SAAS;AAAA,IACX;AAAA,EACF;AAEA,MAAI,SAAS,WAAW,YAAY;AAClC,WAAO,oBAAoB,gBAAgB;AAAA,MACzC;AAAA,MACA;AAAA,MACA,iBAAiB,QAAQ;AAAA,IAC3B,CAAC;AAED,QAAI,gCAAgC,GAAG,GAAG;AACxC,YAAM,cAAc,MAAM;AAAA,QACxB;AAAA,QACA;AAAA,QACA;AAAA,QACA,QAAQ;AAAA,QACR;AAAA,MACF;AACA,UAAI,aAAa;AACf,eAAO,oBAAoB,gBAAgB,EAAE,aAAa,KAAK,CAAC;AAChE,eAAO,EAAE,MAAM,MAAM,QAAQ,YAAY;AAAA,MAC3C;AAAA,IACF;AAAA,EACF;AACF;AAQA,eAAsB,oCACpB,QACA,KACe;AACf,MAAI,IAAI,UAAiB,6BAA6B;AACpD;AAAA,EACF;AAEA,QAAM,EAAE,eAAe,IAAI;AAC3B,MAAI,CAAC,gBAAgB;AACnB;AAAA,EACF;AAEA,QAAM,MAAM,eAAe;AAC3B,MACE,CAAC,gCAAgC,GAAG,KACpC,CAAC,gCAAgC,GAAG,KACpC,CAAC,+BAA+B,GAAG,GACnC;AACA;AAAA,EACF;AAEA,QAAM,UACJ,OAAO,oBAAoB,cAAc,KACxC,MAAM,OAAO,WAAW,EAAE,IAAI,IAAI,QAAQ,SAAS;AACtD,MAAI,CAAC,SAAS;AACZ;AAAA,EACF;AAEA,QAAM,SAAS,IAAI,aAAa;AAAA,IAC9B,SACE,IAAI,WAAW,2BAA2B,IAAI,YAAY,eAAe,SAAS;AAAA,EACtF;AACA,MAAI,CAAC,QAAQ;AACX;AAAA,EACF;AAEA,SAAO,QAAQ;AAAA,IACb,GAAG,OAAO;AAAA,IACV,cAAc;AAAA,MACZ,WAAW,QAAQ;AAAA,MACnB,SAAS,QAAQ;AAAA,MACjB,cAAc,QAAQ;AAAA,MACtB,qBAAqB,QAAQ;AAAA,MAC7B,aAAa,OAAO,QAAQ,WAAW;AAAA,MACvC,yBAAyB,QAAQ;AAAA,IACnC;AAAA,IACA,cAAc;AAAA,MACZ,oBAAoB,QAAQ;AAAA,MAC5B,WAAW,QAAQ;AAAA,IACrB;AAAA,EACF;AACF;AAkBA,eAAsB,kBACpB,QACA,KAC4F;AAC5F,QAAM,EAAE,gBAAgB,OAAO,IAAI;AACnC,MAAI,CAAC,OAAO,WAAW,CAAC,OAAO,OAAO;AACpC,UAAM,OAAO,oBAAoB,cAAc;AAC/C;AAAA,EACF;AAEA,QAAM,MAAM,eAAe;AAC3B,MAAI;AACJ,MAAI;AACJ,MAAI;AACJ,MAAI;AACJ,MAAI,kBAAkB;AAEtB,MAAI,gCAAgC,GAAG,GAAG;AACxC,gBAAY,IAAI,QAAQ;AACxB,yBAAqB,IAAI,QAAQ;AACjC,gBAAY,IAAI,QAAQ;AACxB,oBAAgB,IAAI;AAAA,EACtB,WAAW,gCAAgC,GAAG,GAAG;AAC/C,gBAAY,IAAI,QAAQ;AACxB,yBAAqB,IAAI,QAAQ;AACjC,gBAAY,IAAI,QAAQ;AACxB,oBAAgB,IAAI;AAAA,EACtB,WAAW,+BAA+B,GAAG,GAAG;AAC9C,gBAAY,IAAI,QAAQ;AACxB,yBAAqB,IAAI,QAAQ;AACjC,gBAAY,IAAI,QAAQ;AACxB,oBAAgB,IAAI;AACpB,sBAAkB;AAAA,EACpB,OAAO;AACL;AAAA,EACF;AAEA,QAAM,KAAK,OAAO,SAAS,CAAC;AAC5B,QAAM,UAAU,gBAAgB,IAAI,WAAW,GAAG;AAClD,QAAM,eAAe,gBAAgB,IAAI,gBAAgB,GAAG;AAC5D,QAAM,sBAAsB,gBAAgB,IAAI,uBAAuB,CAAC;AACxE,QAAM,cAAc,gBAAgB,IAAI,eAAe,CAAC;AACxD,QAAM,MAAM,KAAK,IAAI;AAErB,QAAM,UAAU,OAAO,WAAW;AAClC,QAAM,iBAAiB,OAAO,mBAAmB,cAAc;AAC/D,MAAI,CAAC,gBAAgB,WAAW;AAC9B;AAAA,EACF;AACA,MAAI,eAAe,eAAe,gCAAgC,GAAG,GAAG;AACtE;AAAA,EACF;AAEA,QAAM,eAAe,MAAM,QAAQ,cAAc,WAAW,aAAW;AACrE,QAAI,CAAC,WAAW,QAAQ,gBAAgB,cAAc,eAAe,WAAW;AAC9E,aAAO;AAAA,IACT;AAEA,UAAM,UAAmB;AAAA,MACvB;AAAA,MACA;AAAA,MACA,yBAAyB,QAAQ;AAAA,MACjC;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,iBAAiB,eAAe,cAAc,QAAQ,kBAAkB;AAAA,MACxE,sBAAsB;AAAA,MACtB,gBAAgB,QAAQ;AAAA,IAC1B;AACA,WAAO;AAAA,EACT,CAAC;AACD,MAAI,aAAa,WAAW,aAAa,aAAa,SAAS;AAC7D,WAAO,wBAAwB,gBAAgB,aAAa,OAAO;AAAA,EACrE;AAEA,MAAI,mBAAmB,aAAa,WAAW,WAAW;AACxD,WAAO;AAAA,MACL,aAAa;AAAA,MACb,UAAU;AAAA,QACR,aAAa;AAAA,QACb,MAAM,EAAE,SAAS,uBAAuB,UAAU;AAAA,MACpD;AAAA,IACF;AAAA,EACF;AACF;AAQA,eAAsB,oBACpB,QACA,KACe;AACf,QAAM,OAAO,oBAAoB,IAAI,cAAc;AACrD;AAQA,eAAsB,8BACpB,QACA,KACe;AACf,MAAI,IAAI,WAAW,mBAAmB,IAAI,WAAW,kBAAkB;AACrE;AAAA,EACF;AACA,QAAM,OAAO,oBAAoB,IAAI,cAAc;AACrD;AAYA,eAAe,qBACb,QACA,KACA,cACA,SACA,KACqC;AACrC,MAAI,CAAC,WAAW,CAAC,oBAAoB,SAAS,OAAO,qBAAqB,GAAG,GAAG,GAAG;AACjF;AAAA,EACF;AAEA,MAAI,IAAI,cAAc,oBAAoB,8CAA8C;AACtF;AAAA,EACF;AAEA,QAAM,QAAQ,IAAI,cAAc;AAChC,QAAM,YAAY;AAAA,IAChB,IAAI;AAAA,IACJ,IAAI,QAAQ;AAAA,IACZ;AAAA,EACF;AACA,MAAI,WAAW;AACb,WAAO,sBAAsB,OAAO,SAAS;AAAA,EAC/C;AAEA,MACE,iBAAiB,IAAI,eAAe,aAAa,OAAO,EAAE,YAAY,MACtE,QAAQ,UAAU,YAAY,GAC9B;AACA,WAAO,sBAAsB,OAAc,oBAAoB;AAAA,EACjE;AAEA,QAAM,cAAc,MAAM,4BAA4B,KAAK,aAAa,OAAO;AAC/E,MAAI,CAAC,aAAa;AAChB,WAAO,sBAAsB,OAAc,0BAA0B;AAAA,EACvE;AAEA,QAAM,qBAAqB,OAAO,IAAI,QAAQ,kBAAkB;AAChE,MAAI,qBAAqB,OAAO,QAAQ,OAAO,GAAG;AAChD,WAAO,sBAAsB,OAAc,2BAA2B;AAAA,EACxE;AAEA,MAAI,sBAAsB,OAAO,QAAQ,YAAY,GAAG;AACtD,WAAO,sBAAsB,OAAc,+BAA+B;AAAA,EAC5E;AAEA,SAAO;AAAA,IACL,SAAS;AAAA,IACT;AAAA,IACA,OAAO;AAAA,MACL,WAAW,IAAI,QAAQ;AAAA,MACvB,SAAS,QAAQ;AAAA,MACjB,cAAc,QAAQ;AAAA,MACtB,qBAAqB,QAAQ;AAAA,MAC7B,aAAa,QAAQ,YAAY,SAAS;AAAA,IAC5C;AAAA,EACF;AACF;AAUA,SAAS,oBAAoB,SAAkB,OAAe,KAAsB;AAClF,SAAO,QAAQ,oBAAoB,UAAa,MAAM,QAAQ,mBAAmB;AACnF;AASA,eAAe,4BACb,KACA,SACkB;AAClB,MAAI;AACF,WAAO,UAAM,8BAAgB;AAAA,MAC3B,aAAS,yBAAW,IAAI,cAAc,eAAe;AAAA,MACrD,QAAQ,+BAA+B,cAAc,OAAO,CAAC;AAAA,MAC7D,OAAO;AAAA,MACP,aAAa;AAAA,MACb,SAAS;AAAA,QACP,WAAW,IAAI,QAAQ;AAAA,QACvB,oBAAoB,OAAO,IAAI,QAAQ,kBAAkB;AAAA,MAC3D;AAAA,MACA,WAAW,IAAI,QAAQ;AAAA,IACzB,CAAC;AAAA,EACH,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AASA,SAAS,sBAAsB,OAAsB,eAAuC;AAC1F,SAAO,EAAE,SAAS,OAAO,eAAe,MAAM;AAChD;AASA,SAAS,wBACP,KACA,yBACS;AACT,SAAO;AAAA,IACL,WAAW,IAAI,QAAQ;AAAA,IACvB,eAAe,IAAI;AAAA,IACnB;AAAA,IACA,oBAAoB,IAAI,QAAQ;AAAA,IAChC,WAAW,IAAI,QAAQ;AAAA,IACvB,SAAS;AAAA,IACT,cAAc;AAAA,IACd,qBAAqB;AAAA,IACrB,aAAa;AAAA,IACb,sBAAsB,KAAK,IAAI;AAAA,EACjC;AACF;AAUA,SAAS,+BACP,oBACA,OACA,eACQ;AACR,MAAI,CAAC,eAAe;AAClB,WAAO;AAAA,EACT;AAEA,QAAM,SAAS,OAAO,kBAAkB;AACxC,QAAM,SAAS,OAAO,KAAK;AAC3B,MAAI,SAAS,QAAQ;AACnB,WAAO;AAAA,EACT;AACA,UAAQ,SAAS,QAAQ,SAAS;AACpC;;;AMngBA,SAAS,kBACP,SAIA,yBACkE;AAClE,SAAO;AAAA,IACL,WAAW,QAAQ;AAAA,IACnB,SAAS,QAAQ;AAAA,IACjB,cAAc,QAAQ;AAAA,IACtB,qBAAqB,QAAQ;AAAA,IAC7B,aAAa,OAAO,QAAQ,WAAW;AAAA,IACvC,GAAI,4BAA4B,SAAY,EAAE,wBAAwB,IAAI,CAAC;AAAA,EAC7E;AACF;AAcA,eAAsB,mBACpB,QACA,KAGA;AACA,QAAM,EAAE,gBAAgB,aAAa,IAAI;AAEzC,QAAM,MAAM,eAAe;AAC3B,QAAM,UAAU,OAAO,WAAW;AAElC,MAAI,CAAC,gCAAgC,GAAG,GAAG;AACzC;AAAA,EACF;AAEA,QAAM,EAAE,QAAQ,IAAI;AACpB,QAAM,YAAY,QAAQ;AAC1B,QAAM,YAAY,OAAO,mBAAmB,cAAc,GAAG;AAE7D,QAAM,YAAY,OAAO,aAAa,MAAM;AAC5C,QAAM,YAAY,OAAO,QAAQ,kBAAkB;AACnD,MAAI;AAOJ,QAAM,eAAe,MAAM,QAAQ,cAAc,WAAW,aAAW;AACrE,QAAI,CAAC,SAAS;AACZ,gBAAU,EAAE,QAAQ,UAAU;AAC9B,aAAO;AAAA,IACT;AAEA,QAAI,CAAC,aAAa,QAAQ,gBAAgB,cAAc,WAAW;AACjE,gBAAU,EAAE,QAAQ,mBAAmB;AACvC,aAAO;AAAA,IACT;AAEA,UAAM,aAAa,OAAO,QAAQ,uBAAuB,IAAI;AAC7D,QAAI,aAAa,WAAW;AAC1B,gBAAU,EAAE,QAAQ,gBAAgB,SAAS,WAAW,SAAS,EAAE;AACnE,aAAO;AAAA,QACL,GAAG;AAAA,QACH,gBAAgB;AAAA,MAClB;AAAA,IACF;AAEA,UAAM,iBAA0B;AAAA,MAC9B,GAAG;AAAA,MACH,yBAAyB,WAAW,SAAS;AAAA,MAC7C,oBAAoB,QAAQ;AAAA,MAC5B,WAAW,QAAQ;AAAA,MACnB,sBAAsB,KAAK,IAAI;AAAA,MAC/B,gBAAgB;AAAA,IAClB;AACA,cAAU,EAAE,QAAQ,aAAa,UAAU,SAAS,SAAS,eAAe;AAC5E,WAAO;AAAA,EACT,CAAC;AAED,MAAI,SAAS,WAAW,WAAW;AACjC,WAAO,mBAAmB,cAAc;AACxC,WAAO;AAAA,MACL,OAAO;AAAA,MACP,QAAe;AAAA,MACf,SAAS;AAAA,IACX;AAAA,EACF;AAEA,MAAI,SAAS,WAAW,gBAAgB;AACtC,WAAO,mBAAmB,cAAc;AACxC,WAAO;AAAA,MACL,OAAO;AAAA,MACP,QAAe;AAAA,MACf,SAAS,WAAW,QAAQ,OAAO,uBAAuB,UAAU,SAAS,CAAC;AAAA,IAChF;AAAA,EACF;AAEA,MAAI,aAAa,WAAW,aAAa,SAAS,WAAW,aAAa;AACxE,WAAO,mBAAmB,cAAc;AACxC,WAAO;AAAA,MACL,OAAO;AAAA,MACP,QAAe;AAAA,MACf,SAAS;AAAA,IACX;AAAA,EACF;AACA,SAAO,mBAAmB,cAAc;AAExC,QAAM,YAAiD;AAAA,IACrD,cAAc,kBAAkB,QAAQ,UAAU,QAAQ,QAAQ,uBAAuB;AAAA,IACzF,eAAe,aAAa;AAAA,EAC9B;AAEA,SAAO;AAAA,IACL,MAAM;AAAA,IACN,QAAQ;AAAA,MACN,SAAS;AAAA,MACT,OAAO,QAAQ,SAAS,cAAc,MAAM,YAAY;AAAA,MACxD,aAAa;AAAA,MACb,SAAS,aAAa;AAAA,MACtB,QAAQ;AAAA,MACR,OAAO;AAAA,IACT;AAAA,EACF;AACF;AASA,eAAsB,8BACpB,QACA,KACyC;AACzC,QAAM,EAAE,gBAAgB,aAAa,IAAI;AACzC,QAAM,MAAM,eAAe;AAC3B,MAAI,CAAC,+BAA+B,GAAG,GAAG;AACxC;AAAA,EACF;AAEA,QAAM,YAAY,iBAAiB,IAAI,eAAe,aAAa,OAAO;AAC1E,MAAI,IAAI,QAAQ,cAAc,WAAW;AACvC,UAAM,IAAI,MAAM,+CAA+C;AAAA,EACjE;AAEA,QAAM,UAAU,MAAM,OAAO,WAAW,EAAE,IAAI,SAAS;AACvD,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI,MAAa,iBAAiB;AAAA,EAC1C;AACA,QAAM,YAAY,OAAO,mBAAmB,cAAc,GAAG;AAC7D,MAAI,CAAC,aAAa,QAAQ,gBAAgB,cAAc,WAAW;AACjE,UAAM,IAAI,MAAa,cAAc;AAAA,EACvC;AACA,MAAI,OAAO,IAAI,QAAQ,kBAAkB,MAAM,OAAO,QAAQ,uBAAuB,GAAG;AACtF,UAAM,IAAI,MAAa,2BAA2B;AAAA,EACpD;AACA,MAAI,IAAI,QAAQ,cAAc,QAAQ,WAAW;AAC/C,UAAM,IAAI,MAAa,0BAA0B;AAAA,EACnD;AAEA,QAAM,SAAS,IAAI;AAEnB,QAAM,aAA0C;AAAA,IAC9C,SAAS;AAAA,MACP,SAAS;AAAA,MACT,oBAAoB,IAAI,QAAQ;AAAA,IAClC;AAAA,IACA,WAAW,IAAI,QAAQ;AAAA,IACvB,cAAc,QAAQ;AAAA,EACxB;AAEA,QAAM,YAAY,OAAO,QAAQ,OAAO,IAAI,OAAO,QAAQ,uBAAuB;AAClF,MAAI,aAAa,IAAI;AACnB,UAAM,IAAI,MAAa,kBAAkB;AAAA,EAC3C;AAEA,MAAI,kBAAkB;AACtB,MAAI,IAAI,WAAW,QAAW;AAC5B,QAAI,CAAC,QAAQ,KAAK,IAAI,MAAM,GAAG;AAC7B,YAAM,IAAI,MAAa,sBAAsB;AAAA,IAC/C;AACA,UAAM,YAAY,OAAO,IAAI,MAAM;AACnC,QAAI,aAAa,IAAI;AACnB,YAAM,IAAI,MAAa,sBAAsB;AAAA,IAC/C;AACA,sBAAkB;AAAA,EACpB;AAEA,QAAM,eAAe,gBAAgB,SAAS;AAC9C,QAAM,QAAQ,OAAO,QAAQ,eAAe,CAAC;AAE7C,QAAM,2BAA2B,OAAO,4BAA4B;AAEpE,QAAM,4BAA4B,2BAC9B,MAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,aAAa;AAAA,EACf,IACA;AAEJ,QAAM,2BAA2B,2BAC7B,MAAM,eAAe,0BAA0B,CAAC,UAAU,GAAG,aAAa,OAAO,IACjF;AAEJ,SAAO,wBAAwB,gBAAgB,OAAO;AAEtD,SAAO;AAAA,IACL,GAAI,IAAI,WAAW,SAAY,EAAE,QAAQ,aAAa,IAAI,CAAC;AAAA,IAC3D,aAAa;AAAA,IACb,QAAQ,CAAC,UAAU;AAAA,IACnB;AAAA,IACA;AAAA,EACF;AACF;AAeA,eAAsB,kBACpB,QACA,KACe;AACf,QAAM,EAAE,gBAAgB,cAAc,OAAO,IAAI;AACjD,MAAI,CAAC,OAAO,SAAS;AACnB;AAAA,EACF;AAEA,QAAM,MAAM,eAAe;AAC3B,QAAM,UAAU,OAAO,WAAW;AAElC,MAAI,+BAA+B,GAAG,GAAG;AACvC,UAAM,YAAY,iBAAiB,IAAI,eAAe,aAAa,OAAO;AAC1E,UAAM,YAAY,OAAO,mBAAmB,cAAc,GAAG;AAC7D,UAAM,MAAM,KAAK,IAAI;AAErB,UAAM,WAAW,8BAA8B,OAAO,KAAK;AAC3D,UAAM,eAAe,MAAM,QAAQ,cAAc,WAAW,aAAW;AACrE,UAAI,CAAC,SAAS;AACZ,eAAO;AAAA,MACT;AACA,UAAI,CAAC,aAAa,QAAQ,gBAAgB,cAAc,WAAW;AACjE,eAAO;AAAA,MACT;AACA,UAAI,OAAO,SAAS,OAAO,KAAK,OAAO,QAAQ,uBAAuB,GAAG;AACvE,eAAO;AAAA,MACT;AACA,aAAO;AAAA,QACL,GAAG;AAAA,QACH,GAAG;AAAA,QACH,iBAAiB;AAAA,QACjB,sBAAsB;AAAA,QACtB,gBAAgB;AAAA,MAClB;AAAA,IACF,CAAC;AACD,QAAI,aAAa,WAAW,aAAa;AACvC,YAAM,IAAI,MAAa,cAAc;AAAA,IACvC;AACA,QAAI,CAAC,aAAa,SAAS;AACzB;AAAA,IACF;AACA;AAAA,EACF;AAEA,MAAI,gCAAgC,GAAG,GAAG;AACxC;AAAA,EACF;AAEA,MAAI,gCAAgC,GAAG,GAAG;AACxC,UAAM,YAAY,IAAI,QAAQ;AAC9B,UAAM,YAAY,OAAO,mBAAmB,cAAc,GAAG;AAC7D,UAAM,KAAK,OAAO,SAAS,CAAC;AAC5B,UAAM,eAAe,sBAAsB,EAAE;AAC7C,UAAM,SAAS,IAAI;AACnB,UAAM,qBAAqB,IAAI,QAAQ;AACvC,UAAM,MAAM,KAAK,IAAI;AAErB,UAAM,eAAe,MAAM,QAAQ,cAAc,WAAW,aAAW;AACrE,UAAI,CAAC,SAAS;AACZ,eAAO;AAAA,MACT;AACA,UAAI,CAAC,aAAa,QAAQ,gBAAgB,cAAc,WAAW;AACjE,eAAO;AAAA,MACT;AACA,YAAM,iBACJ,OAAO,QAAQ,uBAAuB,IAAI,OAAO,aAAa,MAAM,GACpE,SAAS;AACX,aAAO;AAAA,QACL;AAAA,QACA,eAAe;AAAA,QACf,yBAAyB;AAAA,QACzB;AAAA,QACA,WAAW,IAAI,QAAQ;AAAA,QACvB,SAAS,gBAAgB,cAAc,WAAW,QAAQ,OAAO;AAAA,QACjE,cAAc,gBAAgB,cAAc,gBAAgB,QAAQ,YAAY;AAAA,QAChF,qBAAqB;AAAA,UACnB;AAAA,UACA;AAAA,UACA,QAAQ;AAAA,QACV;AAAA,QACA,aAAa,gBAAgB,cAAc,eAAe,QAAQ,WAAW;AAAA,QAC7E,iBAAiB;AAAA,QACjB,sBAAsB;AAAA,MACxB;AAAA,IACF,CAAC;AACD,QAAI,aAAa,WAAW,aAAa,aAAa,SAAS;AAC7D,aAAO,wBAAwB,gBAAgB,aAAa,OAAO;AACnE;AAAA,IACF;AACA,WAAO,mBAAmB,cAAc;AACxC,UAAM,IAAI,MAAa,cAAc;AAAA,EACvC;AACF;AAQA,eAAsB,oBACpB,QACA,KACe;AACf,QAAM,OAAO,oBAAoB,IAAI,cAAc;AACrD;AASA,eAAsB,+BACpB,QACA,KACyC;AACzC,QAAM,MAAM,IAAI,eAAe;AAC/B,MAAI,gCAAgC,GAAG,GAAG;AACxC;AAAA,EACF;AAEA,QAAM,UAAU,OAAO,oBAAoB,IAAI,cAAc;AAC7D,MAAI,CAAC,SAAS;AACZ;AAAA,EACF;AAEA,MAAI,+BAA+B,GAAG,GAAG;AACvC,WAAO;AAAA,MACL,cAAc;AAAA,QACZ,yBAAyB,QAAQ;AAAA,MACnC;AAAA,IACF;AAAA,EACF;AAEA,MAAI,gCAAgC,GAAG,GAAG;AACxC,WAAO;AAAA,MACL,cAAc;AAAA,QACZ,yBAAyB,QAAQ;AAAA,MACnC;AAAA,MACA,eAAe,IAAI,aAAa;AAAA,IAClC;AAAA,EACF;AACA,SAAO;AAAA,IACL,cAAc;AAAA,MACZ,yBAAyB,QAAQ;AAAA,IACnC;AAAA,EACF;AACF;;;AdhXO,IAAM,2BAAN,MAA8D;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAqBnE,YAAY,iBAAgC,QAA+C;AApB3F,SAAS,SAAS;AAGlB,SAAiB,kBAAkB,oBAAI,QAGrC;AACF,SAAQ,eAA8B,CAAC;AAqCvC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mCAA0B,CAAC,QACzB,8BAA8B,MAAM,GAAG;AAQzC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,yCAAgC,CAC9B,QAC0C,oCAAoC,MAAM,GAAG;AAQzF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,oCAA2B,CAAC,QAC1B,+BAA+B,MAAM,GAAG;AA3CxC,SAAK,kBAAkB;AACvB,SAAK,UAAU,QAAQ,WAAW,IAAI,uBAAuB;AAC7D,SAAK,2BAA2B,QAAQ;AACxC,SAAK,gBAAgB,QAAQ,iBAAiB;AAC9C,SAAK,oBACH,QAAQ,qBAAqB,yBAAyB,KAAK,aAAa;AAC1E,SAAK,cAAc;AAAA,MACjB,gBAAgB,SAAO,mBAAmB,MAAM,GAAG;AAAA,MACnD,eAAe,SAAO,kBAAkB,MAAM,GAAG;AAAA,MACjD,gBAAgB,SAAO,mBAAmB,MAAM,GAAG;AAAA,MACnD,eAAe,SAAO,kBAAkB,MAAM,GAAG;AAAA,MACjD,iBAAiB,SAAO,oBAAoB,MAAM,GAAG;AAAA,MACrD,iBAAiB,SAAO,oBAAoB,MAAM,GAAG;AAAA,MACrD,2BAA2B,SAAO,8BAA8B,MAAM,GAAG;AAAA,IAC3E;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAoCA,oBACE,SACA,SACM;AACN,SAAK,gBAAgB,IAAI,SAAS;AAAA,MAChC,GAAG,KAAK,gBAAgB,IAAI,OAAO;AAAA,MACnC,GAAG;AAAA,IACL,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,mBACE,SAC2C;AAC3C,WAAO,KAAK,gBAAgB,IAAI,OAAO;AAAA,EACzC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,mBACE,SAC2C;AAC3C,UAAM,UAAU,KAAK,gBAAgB,IAAI,OAAO;AAChD,SAAK,gBAAgB,OAAO,OAAO;AACnC,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,wBAAwB,SAAuC,SAAwB;AACrF,SAAK,oBAAoB,SAAS;AAAA,MAChC,WAAW,QAAQ;AAAA,MACnB,iBAAiB;AAAA,IACnB,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,oBAAoB,SAA4D;AAC9E,WAAO,KAAK,mBAAmB,OAAO,GAAG;AAAA,EAC3C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,oBAAoB,SAAsD;AAC9E,UAAM,UAAU,KAAK,mBAAmB,OAAO;AAC/C,QAAI,CAAC,SAAS,aAAa,CAAC,QAAQ,WAAW;AAC7C;AAAA,IACF;AAEA,UAAM,KAAK,QAAQ,cAAc,QAAQ,WAAW,aAAW;AAC7D,UAAI,CAAC,WAAW,QAAQ,gBAAgB,cAAc,QAAQ,WAAW;AACvE,eAAO;AAAA,MACT;AAEA,UAAI,CAAC,QAAQ,iBAAiB;AAC5B,eAAO;AAAA,MACT;AAEA,aAAO;AAAA,QACL,GAAG;AAAA,QACH,gBAAgB;AAAA,MAClB;AAAA,IACF,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,oBAAoB,QAA+C;AACjE,SAAK,aAAa,KAAK,MAAM;AAC7B,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,WAAW,OAAc,SAAwC;AACrE,QAAI,OAAO,UAAU,YAAY,UAAU,QAAQ,YAAY,OAAO;AACpE,UAAI,CAAC,MAAM,OAAO;AAChB,cAAM,IAAI,MAAM,8DAA8D,OAAO,EAAE;AAAA,MACzF;AACA,aAAO;AAAA,QACL,QAAQ,MAAM;AAAA,QACd,OAAO,MAAM;AAAA,QACb,OAAO,MAAM,SAAS,CAAC;AAAA,MACzB;AAAA,IACF;AAEA,UAAM,SAAS,KAAK,oBAAoB,KAAK;AAE7C,eAAW,UAAU,KAAK,cAAc;AACtC,YAAM,SAAS,MAAM,OAAO,QAAQ,OAAO;AAC3C,UAAI,WAAW,MAAM;AACnB,eAAO;AAAA,MACT;AAAA,IACF;AAEA,WAAO,KAAK,uBAAuB,QAAQ,OAAO;AAAA,EACpD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAeA,MAAM,2BACJ,qBACA,eAMA,gBAC8B;AAC9B,SAAK;AAEL,UAAM,YAAY,gBAAgB,oBAAoB,OAAkB;AAExE,UAAM,qBACJ,KAAK,0BAA0B,YAC9B,OAAO,cAAc,OAAO,uBAAuB,WAChD,cAAc,MAAM,qBACpB;AAEN,QACE,CAAC,0BACD,yBAAW,kBAAkB,MAAM,8CACnC;AACA,YAAM,IAAI,MAAM,uEAAuE;AAAA,IACzF;AAEA,WAAO;AAAA,MACL,GAAG;AAAA,MACH,OAAO;AAAA,QACL,GAAG,oBAAoB;AAAA,QACvB,wBAAoB,yBAAW,kBAAkB;AAAA,QACjD,eAAe,KAAK;AAAA,QACpB,MAAM,UAAU;AAAA,QAChB,SAAS,UAAU;AAAA,QACnB,qBACE,oBAAoB,OAAO,uBAAuB,UAAU;AAAA,MAChE;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,aAA6B;AAC3B,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,qBAAoC;AAClC,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,mBAA2B;AACzB,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,uBAA+B;AAC7B,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,8BAA4D;AAC1D,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,qBACE,aACA,SAC+B;AAC/B,UAAM,QAAQ,gBAAgB,OAAO,EAAE;AACvC,WAAO,IAAI,8BAA8B;AAAA,MACvC,QAAQ;AAAA,MACR;AAAA,MACA,UAAU,KAAK;AAAA,MACf;AAAA,MACA;AAAA,IACF,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,oBAAoB,OAAgC;AAC1D,QAAI,OAAO,UAAU,UAAU;AAC7B,aAAO;AAAA,IACT;AAEA,UAAM,aAAa,MAAM,QAAQ,OAAO,EAAE,EAAE,KAAK;AACjD,UAAM,SAAS,WAAW,UAAU;AAEpC,QAAI,MAAM,MAAM,GAAG;AACjB,YAAM,IAAI,MAAM,yBAAyB,KAAK,EAAE;AAAA,IAClD;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASQ,uBAAuB,QAAgB,SAA+B;AAC5E,UAAM,YAAY,gBAAgB,OAAO;AACzC,UAAM,kBAAc,yCAAqB,sCAAsB,MAAM,GAAG,UAAU,QAAQ;AAE1F,WAAO;AAAA,MACL,QAAQ;AAAA,MACR,OAAO,UAAU;AAAA,MACjB,OAAO;AAAA,QACL,MAAM,UAAU;AAAA,QAChB,SAAS,UAAU;AAAA,MACrB;AAAA,IACF;AAAA,EACF;AACF;AAQA,SAAS,yBAAyB,sBAAsC;AACtE,QAAM,kBAAkB,KAAK,IAAI,GAAG,oBAAoB,IAAI;AAC5D,SAAO,KAAK,IAAI,IAAI,KAAK,KAAM,KAAK,IAAI,KAAK,KAAM,KAAK,MAAM,kBAAkB,CAAC,CAAC,CAAC;AACrF;","names":["import_utils","import_viem","import_viem","import_viem","import_viem","import_viem","import_viem"]}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { C as ChannelStorage, a as Channel, b as ChannelUpdateResult } from '../../storage-Bl6aD0Xg.js';
|
|
2
|
+
import '../../types-CF8P2-NM.js';
|
|
3
|
+
import 'viem';
|
|
4
|
+
|
|
5
|
+
type RedisEvalOptions = {
|
|
6
|
+
keys: string[];
|
|
7
|
+
arguments: string[];
|
|
8
|
+
};
|
|
9
|
+
type RedisSetOptions = {
|
|
10
|
+
NX?: true;
|
|
11
|
+
PX?: number;
|
|
12
|
+
};
|
|
13
|
+
type RedisScanOptions = {
|
|
14
|
+
MATCH?: string;
|
|
15
|
+
COUNT?: number;
|
|
16
|
+
};
|
|
17
|
+
type RedisChannelStorageClient = {
|
|
18
|
+
get(key: string): Promise<string | null>;
|
|
19
|
+
set(key: string, value: string, options?: RedisSetOptions): Promise<string | null>;
|
|
20
|
+
del(key: string): Promise<number>;
|
|
21
|
+
eval(script: string, options: RedisEvalOptions): Promise<unknown>;
|
|
22
|
+
scanIterator(options: RedisScanOptions): AsyncIterable<string | string[]>;
|
|
23
|
+
};
|
|
24
|
+
type RedisChannelStorageOptions = {
|
|
25
|
+
client: RedisChannelStorageClient;
|
|
26
|
+
keyPrefix?: string;
|
|
27
|
+
lockTtlMs?: number;
|
|
28
|
+
lockRetryIntervalMs?: number;
|
|
29
|
+
lockRenewalIntervalMs?: number;
|
|
30
|
+
scanCount?: number;
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Redis-backed {@link ChannelStorage} with optimistic atomic updates.
|
|
34
|
+
*/
|
|
35
|
+
declare class RedisChannelStorage implements ChannelStorage {
|
|
36
|
+
private readonly client;
|
|
37
|
+
private readonly keyPrefix;
|
|
38
|
+
private readonly channelKeyPrefix;
|
|
39
|
+
private readonly lockRetryIntervalMs;
|
|
40
|
+
private readonly scanCount;
|
|
41
|
+
/**
|
|
42
|
+
* Creates Redis-backed server channel storage.
|
|
43
|
+
*
|
|
44
|
+
* @param options - Redis client and optional key/retry configuration.
|
|
45
|
+
*/
|
|
46
|
+
constructor(options: RedisChannelStorageOptions);
|
|
47
|
+
/**
|
|
48
|
+
* Loads a persisted channel record, if present.
|
|
49
|
+
*
|
|
50
|
+
* @param channelId - The channel identifier.
|
|
51
|
+
* @returns Parsed channel record or `undefined` when the key is missing.
|
|
52
|
+
*/
|
|
53
|
+
get(channelId: string): Promise<Channel | undefined>;
|
|
54
|
+
/**
|
|
55
|
+
* Lists all stored channel records by scanning Redis keys.
|
|
56
|
+
*
|
|
57
|
+
* @returns Channel records sorted by channelId.
|
|
58
|
+
*/
|
|
59
|
+
list(): Promise<Channel[]>;
|
|
60
|
+
/**
|
|
61
|
+
* Atomically inspects and mutates a channel record with Redis compare-and-write retries.
|
|
62
|
+
*
|
|
63
|
+
* @param channelId - The channel identifier.
|
|
64
|
+
* @param update - Mutation callback. Return `undefined` to delete, or `current` to leave unchanged.
|
|
65
|
+
* @returns The final stored channel and whether storage updated, stayed unchanged, or deleted.
|
|
66
|
+
*/
|
|
67
|
+
updateChannel(channelId: string, update: (current: Channel | undefined) => Channel | undefined): Promise<ChannelUpdateResult>;
|
|
68
|
+
/**
|
|
69
|
+
* Applies a channel mutation only if the key still contains the value that was inspected.
|
|
70
|
+
*
|
|
71
|
+
* @param key - Redis channel key to mutate.
|
|
72
|
+
* @param expectedRaw - Raw JSON value observed before running the update callback.
|
|
73
|
+
* @param operation - Mutation to apply when the observed value is still current.
|
|
74
|
+
* @param nextRaw - Raw JSON value to write for set operations.
|
|
75
|
+
* @returns Whether the mutation was applied.
|
|
76
|
+
*/
|
|
77
|
+
private commitUpdate;
|
|
78
|
+
/**
|
|
79
|
+
* Builds the Redis key for a stored channel record.
|
|
80
|
+
*
|
|
81
|
+
* @param channelId - The channel identifier.
|
|
82
|
+
* @returns Redis key for the channel JSON.
|
|
83
|
+
*/
|
|
84
|
+
private channelKey;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export { RedisChannelStorage, type RedisChannelStorageClient, type RedisChannelStorageOptions, type RedisEvalOptions, type RedisScanOptions, type RedisSetOptions };
|