← Public packages
@kentcdodds/dropbox
Upload, share, and manage Dropbox files via OAuth — small or large uploads and direct download URLs.
src/upload.ts
143 lines · 4.8 KB · TypeScript/**
* Upload UTF-8 text or base64 bytes to Dropbox. Pick this for small files
* (`text` or `bytesBase64` under 120 KiB binary). Larger `bytesBase64` payloads
* auto-delegate to `./upload-large` (upload session) in the same call. Put
* `bytesBase64` in execute `params`, never inline in `code`. Requires `confirm: true`.
*
* @param params.path - Destination Dropbox path starting with `/` (prefer `/Apps/Kody/...`).
* @param params.text - UTF-8 text body (mutually exclusive with `bytesBase64`).
* @param params.bytesBase64 - Base64 file bytes (mutually exclusive with `text`).
* @param params.mode - `add` (default) or `overwrite`.
* @param params.confirm - Required `true` to perform the mutating call.
* @param params.dryRun - When true, return the planned request without calling Dropbox.
* @returns Dropbox upload metadata, or a dry-run preview.
*
* @example
* import upload from 'kody:@kentcdodds/dropbox/upload'
* const result = await upload({
* path: '/Apps/Kody/notes/hello.txt',
* text: 'Hello',
* mode: 'overwrite',
* confirm: true,
* })
*/
import { decodeBase64, encodeTextBase64 } from './base64.ts'
import { UPLOAD_SINGLE_MAX_BYTES } from './paths.ts'
import { request } from './request.ts'
import { uploadLarge } from './upload-large.ts'
import type { DropboxRequestResult, JsonRecord } from './types.ts'
import {
optionalBoolean,
optionalString,
requireDropboxPath,
requireRecord,
} from './types.ts'
export type UploadInput = {
path: string
text?: string
bytesBase64?: string
mode?: 'add' | 'overwrite'
autorename?: boolean
mute?: boolean
strictConflict?: boolean
confirm?: boolean
dryRun?: boolean
}
/**
* Upload UTF-8 text or base64 bytes to Dropbox. Pick this for small files
* (`text` or `bytesBase64` under 120 KiB binary). Larger `bytesBase64` payloads
* auto-delegate to `./upload-large` (upload session) in the same call. Put
* `bytesBase64` in execute `params`, never inline in `code`. Requires `confirm: true`.
*
* @param params.path - Destination Dropbox path starting with `/` (prefer `/Apps/Kody/...`).
* @param params.text - UTF-8 text body (mutually exclusive with `bytesBase64`).
* @param params.bytesBase64 - Base64 file bytes (mutually exclusive with `text`).
* @param params.mode - `add` (default) or `overwrite`.
* @param params.confirm - Required `true` to perform the mutating call.
* @param params.dryRun - When true, return the planned request without calling Dropbox.
* @returns Dropbox upload metadata, or a dry-run preview.
*
* @example
* import upload from 'kody:@kentcdodds/dropbox/upload'
* const result = await upload({
* path: '/Apps/Kody/notes/hello.txt',
* text: 'Hello',
* mode: 'overwrite',
* confirm: true,
* })
*/
export async function upload(
input: UploadInput,
): Promise<DropboxRequestResult | JsonRecord> {
const path = requireDropboxPath(input.path)
const text = optionalString(input.text, 'text')
const suppliedBase64 = optionalString(input.bytesBase64, 'bytesBase64')
if ((text === undefined) === (suppliedBase64 === undefined)) {
throw new Error('Provide exactly one of text or bytesBase64.')
}
const mode = input.mode ?? 'add'
if (mode !== 'add' && mode !== 'overwrite') {
throw new Error('mode must be "add" or "overwrite".')
}
const bytesBase64 = suppliedBase64 ?? encodeTextBase64(text ?? '')
const byteLength = decodeBase64(bytesBase64).byteLength
if (byteLength > UPLOAD_SINGLE_MAX_BYTES) {
const delegated = await uploadLarge({
path,
bytesBase64,
mode,
autorename: input.autorename,
mute: input.mute,
strictConflict: input.strictConflict,
confirm: input.confirm,
dryRun: input.dryRun,
})
if (input.dryRun && delegated && typeof delegated === 'object') {
return {
...delegated,
delegatedFrom: './upload',
reason: `Decoded bytes (${byteLength}) exceed UPLOAD_SINGLE_MAX_BYTES (${UPLOAD_SINGLE_MAX_BYTES}); using upload session.`,
}
}
return delegated
}
return request({
endpoint: 'files/upload',
kind: 'upload',
apiArg: {
path,
mode,
autorename: optionalBoolean(input.autorename, 'autorename') ?? false,
mute: optionalBoolean(input.mute, 'mute') ?? false,
strict_conflict: optionalBoolean(input.strictConflict, 'strictConflict') ?? false,
},
bytesBase64,
confirm: input.confirm,
dryRun: input.dryRun,
})
}
/**
* Upload UTF-8 text or base64 bytes to Dropbox.
* Requires `confirm: true`; use `dryRun: true` to preview the request.
* Over-threshold binary payloads auto-delegate to `./upload-large`.
* @example
* import upload from 'kody:@kentcdodds/dropbox/upload'
* const result = await upload({
* path: '/Apps/Kody/notes/hello.txt',
* text: 'Hello',
* mode: 'overwrite',
* confirm: true,
* })
*/
export default async function uploadEntrypoint(
params: Partial<UploadInput> & Record<string, unknown> = {},
) {
return upload(requireRecord(params, 'upload') as UploadInput)
}