import { type FlowActionSucceededObservationSeed, toFlowActionSucceededObservationSeed, } from "./flow-action-succeeded-observation.js"; import { isBrowserNavigationUrl } from "./navigation-rpc.js"; export const POST_SUBMIT_HOST_ACTION_REQUEST_RPC_METHOD = "host-action.postSubmit.request"; export const POST_SUBMIT_HOST_ACTION_REQUEST_TIMEOUT_MS = 20_000; export const HOST_ACTION_EXECUTE_RPC_METHOD = "host-action.execute"; export const HOST_ACTION_EXECUTE_LINK_RPC_METHOD = "host-action.execute.link"; export const HOST_ACTION_SETTLEMENT_AVAILABILITY_RPC_METHOD = "host-action.settlement.availability"; export const HOST_ACTION_LINK_SETTLEMENT_TIMEOUT_MS = 10_000; export const HOST_ACTION_LINK_RPC_TIMEOUT_MS = 15_000; /** Settlements supported by the current host action executor. */ export type HostActionSettlementAvailabilityResult = { close: true; link: boolean; remain: true; }; export const toHostActionSettlementAvailabilityResult = ( value: unknown, ): HostActionSettlementAvailabilityResult | null => { if (typeof value !== "object" || value === null) { return null; } const result = value as Record; if ( !Object.hasOwn(result, "remain") || result.remain !== true || !Object.hasOwn(result, "close") || result.close !== true || !Object.hasOwn(result, "link") || typeof result.link !== "boolean" ) { return null; } return { close: true, link: result.link, remain: true }; }; export type HostActionExecuteParams = { viewId: string; generation: number; instanceId: string; /** Omitted only by protocol-v3 Core versions predating invocation identity. */ invocationId?: string; definition: { key: string; version: number }; /** * Optional Core-authored facts for a later host-owned terminal-success * observer. `invocationId` and `definition` above are the cross-binding * authorities and are intentionally not repeated in this seed. */ flowActionSucceededSeed?: FlowActionSucceededObservationSeed; }; export type HostActionExecuteLinkParams = HostActionExecuteParams & { settlementDeadline: number; url: string; }; export const toHostActionExecuteParams = ( value: unknown, ): HostActionExecuteParams | null => { if (typeof value !== "object" || value === null) return null; const valueRecord = value as Record; const definition = valueRecord.definition; const flowActionSucceededSeed = toFlowActionSucceededObservationSeed( valueRecord.flowActionSucceededSeed, "runtime-action", ); if ( typeof valueRecord.viewId !== "string" || typeof valueRecord.instanceId !== "string" || typeof valueRecord.generation !== "number" || !Number.isInteger(valueRecord.generation) || typeof definition !== "object" || definition === null ) return null; const definitionRecord = definition as Record; if ( [valueRecord.viewId, valueRecord.instanceId, definitionRecord.key].some( (value) => typeof value !== "string" || value.trim().length === 0, ) || typeof definitionRecord.version !== "number" || !Number.isSafeInteger(definitionRecord.version) || definitionRecord.version <= 0 ) return null; if ( valueRecord.invocationId !== undefined && (typeof valueRecord.invocationId !== "string" || valueRecord.invocationId.trim().length === 0) ) return null; return { viewId: valueRecord.viewId, generation: valueRecord.generation, instanceId: valueRecord.instanceId, ...(typeof valueRecord.invocationId === "string" ? { invocationId: valueRecord.invocationId } : {}), ...(flowActionSucceededSeed && typeof valueRecord.invocationId === "string" ? { flowActionSucceededSeed } : {}), definition: { key: definitionRecord.key as string, version: definitionRecord.version, }, }; }; export const toHostActionExecuteLinkParams = ( value: unknown, ): HostActionExecuteLinkParams | null => { const params = toHostActionExecuteParams(value); if ( !params || typeof value !== "object" || value === null || !("settlementDeadline" in value) || typeof value.settlementDeadline !== "number" || !Number.isSafeInteger(value.settlementDeadline) || value.settlementDeadline <= 0 || !("url" in value) || typeof value.url !== "string" || !isBrowserNavigationUrl(value.url) ) { return null; } return { ...params, settlementDeadline: value.settlementDeadline, url: value.url, }; };