Files
hapi/cli/src/api/versionedUpdate.ts
T
weishu f2f46e9383 refactor(api): extract versioned update handling to shared utility
Extract duplicate version acknowledgment logic from apiMachine and
apiSession into a reusable applyVersionedAck utility. This handles
success, version-mismatch, and error responses consistently with
customizable parsing and error handling via options object.
2026-01-03 22:23:29 +08:00

66 lines
2.1 KiB
TypeScript

export type AckResult = 'success' | 'version-mismatch'
export type VersionedAckResult<ValueKey extends string> =
| ({ result: 'success'; version: number } & Record<ValueKey, unknown | null>)
| ({ result: 'version-mismatch'; version: number } & Record<ValueKey, unknown | null>)
| { result: 'error'; reason?: string }
export type VersionedAckOptions<TValue, ValueKey extends string> = {
valueKey: ValueKey
parseValue: (value: unknown) => TValue | null
applyValue: (value: TValue | null) => void
applyVersion: (version: number) => void
logInvalidValue: (context: AckResult, version: number) => void
invalidResponseMessage: string
errorMessage: string
versionMismatchMessage: string
}
const isRecord = (value: unknown): value is Record<string, unknown> => {
return typeof value === 'object' && value !== null
}
export const applyVersionedAck = <TValue, ValueKey extends string>(
ack: unknown,
options: VersionedAckOptions<TValue, ValueKey>
): void => {
if (!isRecord(ack)) {
throw new Error(options.invalidResponseMessage)
}
const result = ack.result
if (result === 'success' || result === 'version-mismatch') {
const version = ack.version
if (typeof version !== 'number') {
throw new Error(options.invalidResponseMessage)
}
const rawValue = ack[options.valueKey]
if (rawValue == null) {
options.applyValue(null)
} else {
const parsed = options.parseValue(rawValue)
if (parsed === null) {
options.logInvalidValue(result, version)
} else {
options.applyValue(parsed)
}
}
options.applyVersion(version)
if (result === 'version-mismatch') {
throw new Error(options.versionMismatchMessage)
}
return
}
if (result === 'error') {
const reason = typeof ack.reason === 'string' ? ack.reason : 'unknown'
throw new Error(`${options.errorMessage} (${reason})`)
}
throw new Error(options.invalidResponseMessage)
}