File size: 6,004 Bytes
25d91a4 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 | // Builds lazy native approval runtime adapters.
import { createLazyRuntimeModule } from "../shared/lazy-runtime.js";
import type {
ChannelApprovalNativeAvailabilityAdapter,
ChannelApprovalNativeRuntimeAdapter,
} from "./approval-handler-runtime-types.js";
import type { ChannelApprovalKind } from "./approval-types.js";
/** Runtime-context capability key used by channels to register native approval resources. */
export const CHANNEL_APPROVAL_NATIVE_RUNTIME_CONTEXT_CAPABILITY = "approval.native";
/** Creates an approval runtime adapter that loads heavy channel code only when delivery hooks run. */
export function createLazyChannelApprovalNativeRuntimeAdapter<
TPendingPayload = unknown,
TPreparedTarget = unknown,
TPendingEntry = unknown,
TBinding = unknown,
TFinalPayload = unknown,
TCapabilityBoundary extends boolean = false,
>(params: {
load: () => Promise<
ChannelApprovalNativeRuntimeAdapter<
TPendingPayload,
TPreparedTarget,
TPendingEntry,
TBinding,
TFinalPayload
>
>;
isConfigured: ChannelApprovalNativeAvailabilityAdapter["isConfigured"];
shouldHandle: ChannelApprovalNativeAvailabilityAdapter["shouldHandle"];
eventKinds?: readonly ChannelApprovalKind[];
/** Erases payload types only when registering with the non-generic channel capability. */
capabilityBoundary?: TCapabilityBoundary;
/** @deprecated Trusted compatibility override; omit to derive ownership from the payload. */
resolveApprovalKind?: ChannelApprovalNativeRuntimeAdapter["resolveApprovalKind"];
}): TCapabilityBoundary extends true
? ChannelApprovalNativeRuntimeAdapter
: ChannelApprovalNativeRuntimeAdapter<
TPendingPayload,
TPreparedTarget,
TPendingEntry,
TBinding,
TFinalPayload
> {
const loadRuntime = createLazyRuntimeModule(params.load);
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<
TPendingPayload,
TPreparedTarget,
TPendingEntry,
TBinding,
TFinalPayload
>,
) => TResult,
): Promise<TResult> => select(await loadResolvedRuntime());
const loadOptional = async <TResult>(
select: (
runtime: ChannelApprovalNativeRuntimeAdapter<
TPendingPayload,
TPreparedTarget,
TPendingEntry,
TBinding,
TFinalPayload
>,
) => TResult | undefined,
): Promise<TResult | undefined> => select(await loadResolvedRuntime());
return {
...(params.eventKinds ? { eventKinds: params.eventKinds } : {}),
...(params.resolveApprovalKind ? { resolveApprovalKind: params.resolveApprovalKind } : {}),
availability: {
isConfigured: params.isConfigured,
shouldHandle: params.shouldHandle,
},
presentation: {
buildPendingPayload: async (runtimeParams) =>
(await loadRequired((runtime) => runtime.presentation.buildPendingPayload))(runtimeParams),
buildResolvedResult: async (runtimeParams) =>
(await loadRequired((runtime) => runtime.presentation.buildResolvedResult))(runtimeParams),
buildExpiredResult: async (runtimeParams) =>
(await loadRequired((runtime) => runtime.presentation.buildExpiredResult))(runtimeParams),
},
transport: {
prepareTarget: async (runtimeParams) =>
(await loadRequired((runtime) => runtime.transport.prepareTarget))(runtimeParams),
deliverPending: async (runtimeParams) =>
(await loadRequired((runtime) => runtime.transport.deliverPending))(runtimeParams),
updateEntry: async (runtimeParams) =>
await (
await loadOptional((runtime) => runtime.transport.updateEntry)
)?.(runtimeParams),
deleteEntry: async (runtimeParams) =>
await (
await loadOptional((runtime) => runtime.transport.deleteEntry)
)?.(runtimeParams),
},
interactions: {
bindPending: async (runtimeParams) =>
(await loadOptional((runtime) => runtime.interactions?.bindPending))?.(runtimeParams) ??
null,
unbindPending: async (runtimeParams) =>
await (
await loadOptional((runtime) => runtime.interactions?.unbindPending)
)?.(runtimeParams),
clearPendingActions: async (runtimeParams) =>
await (
await loadOptional((runtime) => runtime.interactions?.clearPendingActions)
)?.(runtimeParams),
cancelDelivered: async (runtimeParams) =>
await (
await loadOptional((runtime) => runtime.interactions?.cancelDelivered)
)?.(runtimeParams),
},
observe: {
// Observe hooks are fire-and-forget at call sites. Reuse the already
// loaded runtime instead of introducing unawaited lazy-load promises.
onDeliveryError: (runtimeParams) => loadedRuntime?.observe?.onDeliveryError?.(runtimeParams),
onDuplicateSkipped: (runtimeParams) =>
loadedRuntime?.observe?.onDuplicateSkipped?.(runtimeParams),
onDelivered: (runtimeParams) => loadedRuntime?.observe?.onDelivered?.(runtimeParams),
},
// `capabilityBoundary` opts into the non-generic registration contract;
// otherwise this object preserves every type inferred from `load`.
// SAFETY: the conditional return type selects exactly those two representations.
} satisfies ChannelApprovalNativeRuntimeAdapter<
TPendingPayload,
TPreparedTarget,
TPendingEntry,
TBinding,
TFinalPayload
> as never; // SAFETY: the conditional return selects typed or capability-boundary form.
}
|