export type JsonRecord = Record<string, unknown>
export type DropboxRequestKind = 'rpc' | 'upload' | 'download'
export type DropboxRequestInput = {
endpoint: string
kind?: DropboxRequestKind
body?: unknown
apiArg?: JsonRecord
bytesBase64?: string
headers?: Record<string, string>
confirm?: boolean
dryRun?: boolean
}
export type DropboxRequestResult = {
ok: boolean
status: number
statusText: string
requestId: string | null
headers: Record<string, string>
body: unknown
}
export function requireRecord(value: unknown, label: string): JsonRecord {
if (!value || typeof value !== 'object' || Array.isArray(value)) {
throw new Error(`${label} must be an object.`)
}
return value as JsonRecord
}
export function requireString(
value: unknown,
label: string,
options: { allowEmpty?: boolean } = {},
): string {
if (typeof value !== 'string' || (!options.allowEmpty && value.length === 0)) {
throw new Error(`${label} must be ${options.allowEmpty ? 'a string' : 'a non-empty string'}.`)
}
return value
}
export function optionalBoolean(value: unknown, label: string): boolean | undefined {
if (value === undefined) return undefined
if (typeof value !== 'boolean') throw new Error(`${label} must be a boolean.`)
return value
}
export function optionalNumber(value: unknown, label: string): number | undefined {
if (value === undefined) return undefined
if (typeof value !== 'number' || !Number.isFinite(value)) {
throw new Error(`${label} must be a finite number.`)
}
return value
}
export function optionalString(value: unknown, label: string): string | undefined {
if (value === undefined) return undefined
return requireString(value, label, { allowEmpty: true })
}
export function requireDropboxPath(
value: unknown,
label = 'path',
options: { allowRoot?: boolean } = {},
): string {
const path = requireString(value, label, { allowEmpty: options.allowRoot })
if (path !== '' && !path.startsWith('/')) {
throw new Error(`${label} must start with "/"${options.allowRoot ? ' or be empty for root' : ''}.`)
}
return path
}