types: preserve approval runtime payload typing

This commit is contained in:
Gustavo Madeira Santana
2026-04-07 17:08:57 -04:00
parent 5fb6aeaf86
commit af4a2faa1d
10 changed files with 118 additions and 36 deletions

View File

@@ -7,29 +7,75 @@ import type { ExecApprovalChannelRuntimeEventKind } from "./exec-approval-channe
export const CHANNEL_APPROVAL_NATIVE_RUNTIME_CONTEXT_CAPABILITY = "approval.native";
type LazyChannelApprovalNativeRuntimeParams = {
load: () => Promise<ChannelApprovalNativeRuntimeAdapter>;
export function createLazyChannelApprovalNativeRuntimeAdapter<
TPendingPayload = unknown,
TPreparedTarget = unknown,
TPendingEntry = unknown,
TBinding = unknown,
TFinalPayload = unknown,
>(params: {
load: () => Promise<
ChannelApprovalNativeRuntimeAdapter<
TPendingPayload,
TPreparedTarget,
TPendingEntry,
TBinding,
TFinalPayload
>
>;
isConfigured: ChannelApprovalNativeAvailabilityAdapter["isConfigured"];
shouldHandle: ChannelApprovalNativeAvailabilityAdapter["shouldHandle"];
eventKinds?: readonly ExecApprovalChannelRuntimeEventKind[];
resolveApprovalKind?: ChannelApprovalNativeRuntimeAdapter["resolveApprovalKind"];
};
export function createLazyChannelApprovalNativeRuntimeAdapter(
params: LazyChannelApprovalNativeRuntimeParams,
): ChannelApprovalNativeRuntimeAdapter {
}): ChannelApprovalNativeRuntimeAdapter<
TPendingPayload,
TPreparedTarget,
TPendingEntry,
TBinding,
TFinalPayload
> {
const loadRuntime = createLazyRuntimeModule(params.load);
let loadedRuntime: ChannelApprovalNativeRuntimeAdapter | null = null;
const loadResolvedRuntime = async (): Promise<ChannelApprovalNativeRuntimeAdapter> => {
let loadedRuntime: ChannelApprovalNativeRuntimeAdapter<
TPendingPayload,
TPreparedTarget,
TPendingEntry,
TBinding,
TFinalPayload
> | null = null;
const loadResolvedRuntime = async (): Promise<
ChannelApprovalNativeRuntimeAdapter<
TPendingPayload,
TPreparedTarget,
TPendingEntry,
TBinding,
TFinalPayload
>
> => {
const runtime = await loadRuntime();
loadedRuntime = runtime;
return runtime;
};
const loadRequired = async <TResult>(
select: (runtime: ChannelApprovalNativeRuntimeAdapter) => TResult,
select: (
runtime: ChannelApprovalNativeRuntimeAdapter<
TPendingPayload,
TPreparedTarget,
TPendingEntry,
TBinding,
TFinalPayload
>,
) => TResult,
): Promise<TResult> => select(await loadResolvedRuntime());
const loadOptional = async <TResult>(
select: (runtime: ChannelApprovalNativeRuntimeAdapter) => TResult | undefined,
select: (
runtime: ChannelApprovalNativeRuntimeAdapter<
TPendingPayload,
TPreparedTarget,
TPendingEntry,
TBinding,
TFinalPayload
>,
) => TResult | undefined,
): Promise<TResult | undefined> => select(await loadResolvedRuntime());
return {
@@ -63,7 +109,8 @@ export function createLazyChannelApprovalNativeRuntimeAdapter(
},
interactions: {
bindPending: async (runtimeParams) =>
(await loadOptional((runtime) => runtime.interactions?.bindPending))?.(runtimeParams),
(await loadOptional((runtime) => runtime.interactions?.bindPending))?.(runtimeParams) ??
null,
unbindPending: async (runtimeParams) =>
await (
await loadOptional((runtime) => runtime.interactions?.unbindPending)

View File

@@ -222,13 +222,24 @@ export type ChannelApprovalNativeObserveAdapter<
) => void;
};
export type ChannelApprovalNativeRuntimeAdapter = {
export type ChannelApprovalNativeRuntimeAdapter<
TPendingPayload = unknown,
TPreparedTarget = unknown,
TPendingEntry = unknown,
TBinding = unknown,
TFinalPayload = unknown,
> = {
eventKinds?: readonly ExecApprovalChannelRuntimeEventKind[];
resolveApprovalKind?: (request: ApprovalRequest) => ChannelApprovalKind;
availability: ChannelApprovalNativeAvailabilityAdapter;
presentation: ChannelApprovalNativePresentationAdapter;
transport: ChannelApprovalNativeTransportAdapter;
interactions?: ChannelApprovalNativeInteractionAdapter;
presentation: ChannelApprovalNativePresentationAdapter<TPendingPayload, TFinalPayload>;
transport: ChannelApprovalNativeTransportAdapter<
TPreparedTarget,
TPendingEntry,
TPendingPayload,
TFinalPayload
>;
interactions?: ChannelApprovalNativeInteractionAdapter<TPendingEntry, TBinding>;
observe?: ChannelApprovalNativeObserveAdapter;
};
@@ -237,6 +248,7 @@ export type ChannelApprovalNativeRuntimeSpec<
TPreparedTarget,
TPendingEntry,
TBinding = unknown,
TFinalPayload = unknown,
TPendingView extends PendingApprovalView = PendingApprovalView,
TResolvedView extends ResolvedApprovalView = ResolvedApprovalView,
TExpiredView extends ExpiredApprovalView = ExpiredApprovalView,
@@ -261,8 +273,8 @@ export type ChannelApprovalNativeRuntimeSpec<
entry: TPendingEntry;
},
) =>
| ChannelApprovalNativeFinalAction<unknown>
| Promise<ChannelApprovalNativeFinalAction<unknown>>;
| ChannelApprovalNativeFinalAction<TFinalPayload>
| Promise<ChannelApprovalNativeFinalAction<TFinalPayload>>;
buildExpiredResult: (
params: ChannelApprovalCapabilityHandlerContext & {
request: ApprovalRequest;
@@ -270,8 +282,8 @@ export type ChannelApprovalNativeRuntimeSpec<
entry: TPendingEntry;
},
) =>
| ChannelApprovalNativeFinalAction<unknown>
| Promise<ChannelApprovalNativeFinalAction<unknown>>;
| ChannelApprovalNativeFinalAction<TFinalPayload>
| Promise<ChannelApprovalNativeFinalAction<TFinalPayload>>;
};
transport: {
prepareTarget: (
@@ -299,7 +311,7 @@ export type ChannelApprovalNativeRuntimeSpec<
updateEntry?: (
params: ChannelApprovalCapabilityHandlerContext & {
entry: TPendingEntry;
payload: unknown;
payload: TFinalPayload;
phase: "resolved" | "expired";
},
) => Promise<void>;
@@ -487,6 +499,7 @@ export function createChannelApprovalNativeRuntimeAdapter<
TPreparedTarget,
TPendingEntry,
TBinding = unknown,
TFinalPayload = unknown,
TPendingView extends PendingApprovalView = PendingApprovalView,
TResolvedView extends ResolvedApprovalView = ResolvedApprovalView,
TExpiredView extends ExpiredApprovalView = ExpiredApprovalView,
@@ -496,11 +509,18 @@ export function createChannelApprovalNativeRuntimeAdapter<
TPreparedTarget,
TPendingEntry,
TBinding,
TFinalPayload,
TPendingView,
TResolvedView,
TExpiredView
>,
): ChannelApprovalNativeRuntimeAdapter {
): ChannelApprovalNativeRuntimeAdapter<
TPendingPayload,
TPreparedTarget,
TPendingEntry,
TBinding,
TFinalPayload
> {
return {
...(spec.eventKinds ? { eventKinds: spec.eventKinds } : {}),
...(spec.resolveApprovalKind ? { resolveApprovalKind: spec.resolveApprovalKind } : {}),
@@ -547,7 +567,7 @@ export function createChannelApprovalNativeRuntimeAdapter<
...(spec.interactions.bindPending
? {
bindPending: async (params) =>
await spec.interactions?.bindPending?.(params as never),
(await spec.interactions!.bindPending!(params as never)) ?? null,
}
: {}),
...(spec.interactions.unbindPending

View File

@@ -978,7 +978,9 @@ describe("fetchWithSsrFGuard hardening", () => {
fetch: vi.fn(async () => okResponse()),
};
const lookupFn: LookupFn = vi.fn(async (hostname: string) => {
if (hostname === "localhost") return [{ address: "127.0.0.1", family: 4 }];
if (hostname === "localhost") {
return [{ address: "127.0.0.1", family: 4 }];
}
return [{ address: "149.154.167.220", family: 4 }];
}) as unknown as LookupFn;
const fetchImpl = vi.fn(async () => okResponse());
@@ -1008,7 +1010,9 @@ describe("fetchWithSsrFGuard hardening", () => {
fetch: vi.fn(async () => okResponse()),
};
const lookupFn: LookupFn = vi.fn(async (hostname: string) => {
if (hostname === "localhost") return [{ address: "127.0.0.1", family: 4 }];
if (hostname === "localhost") {
return [{ address: "127.0.0.1", family: 4 }];
}
return [{ address: "149.154.167.220", family: 4 }];
}) as unknown as LookupFn;
const fetchImpl = vi.fn();