Skip to content

basalt / drives/src / DriveProvider

Interface: DriveProvider ​

Defined in: drives/src/provider.ts:405

A file-storage provider adapter.

Only name, allowedHosts, authorization, list and download are required. Everything optional degrades honestly: the engine reports DriveUnsupportedError rather than silently doing nothing, and falls back to a full listing when delta is absent.

Properties ​

allowedHosts ​

> readonly allowedHosts: readonly string[]

Defined in: drives/src/provider.ts:418

Every host this adapter is allowed to open a connection to.

The allowlist is the load-bearing SSRF control. Provider responses carry URLs the framework then fetches (Graph's @microsoft.graph.downloadUrl, Google's redirect to googleusercontent.com), and those are attacker- influenced data. An entry is either an exact host (api.dropboxapi.com) or a leading-dot suffix (.googleusercontent.com) that matches subdomains only — never the bare parent.


authorization ​

> readonly authorization: DriveAuthorization

Defined in: drives/src/provider.ts:419


deltaIncludesExisting? ​

> readonly optional deltaIncludesExisting?: boolean

Defined in: drives/src/provider.ts:469

Whether the cursor startDelta returns replays the items that already exist, or only changes from that moment on.

This is the difference the neutral cursor was flattening, and it decides whether a first sync imports a tenant's drive or silently imports nothing:

  • Dropbox — true. files/list_folder is the head of the feed: its first page enumerates the folder and /continue carries on into changes. Backfill and delta are one continuum.
  • Google Drive — false. changes.getStartPageToken is explicitly "from now"; the existing corpus never appears in changes.list.
  • Microsoft Graph — true. /delta with no token enumerates the drive first, then hands over a deltaLink.

Defaults to false, the safe direction: the engine runs one full listing pass before the first delta run, so an adapter that forgets to declare it costs extra metadata reads instead of losing a tenant's files.


name ​

> readonly name: string

Defined in: drives/src/provider.ts:407

Stable identifier, stored on every connection: google, microsoft, dropbox.

Methods ​

delta()? ​

> optional delta(session, cursor): Promise<DriveDelta>

Defined in: drives/src/provider.ts:449

Reads one page of changes since cursor.

Parameters ​

session ​

DriveSession

cursor ​

string

Returns ​

Promise<DriveDelta>


download() ​

> download(session, item): Promise<DriveContent>

Defined in: drives/src/provider.ts:425

Opens the item's bytes. Must not buffer them.

Parameters ​

session ​

DriveSession

item ​

DriveItem

Returns ​

Promise<DriveContent>


get()? ​

> optional get(session, externalId): Promise<DriveItem | null>

Defined in: drives/src/provider.ts:423

Fetches one item's metadata. null when it no longer exists.

Parameters ​

session ​

DriveSession

externalId ​

string

Returns ​

Promise<DriveItem | null>


list() ​

> list(session, options): Promise<DrivePage<DriveItem>>

Defined in: drives/src/provider.ts:421

Lists one page of a folder.

Parameters ​

session ​

DriveSession

options ​

DriveListOptions

Returns ​

Promise<DrivePage<DriveItem>>


retryAfterFromBody()? ​

> optional retryAfterFromBody(body): number | undefined

Defined in: drives/src/provider.ts:519

Reads a vendor-specific "slow down" hint out of a 429/503 body.

Retry-After is the interoperable answer and always wins, but Dropbox frequently answers 429 with no header at all and puts the number in the body instead ({"error":{".tag":"too_many_requests","retry_after":300}}). The guarded fetch destroys a rate-limited body before an adapter can see it — deliberately, so nothing unbounded is read on an error path — so the hint has to be declared here, where the engine can apply it under its own cap.

Returns milliseconds, or undefined when the body carries no hint. It must be pure: it runs on a hostile-ish path and gets at most a few kilobytes.

Parameters ​

body ​

string

Returns ​

number | undefined


startDelta()? ​

> optional startDelta(session, options): Promise<string>

Defined in: drives/src/provider.ts:447

Establishes the initial delta cursor. See deltaIncludesExisting.

Parameters ​

session ​

DriveSession

options ​
folderId? ​

string

Returns ​

Promise<string>


unwatch()? ​

> optional unwatch(session, watch): Promise<void>

Defined in: drives/src/provider.ts:473

Cancels a subscription.

Parameters ​

session ​

DriveSession

watch ​

DriveWatch

Returns ​

Promise<void>


upload()? ​

> optional upload(session, input): Promise<DriveItem>

Defined in: drives/src/provider.ts:445

Writes a file back to the provider. Optional — most apps only read.

Every adapter has a hard size ceiling, and it is low. A single-request upload is all any of them implements, because the alternative on all three vendors is a multi-call resumable session with its own chunking, its own 308-based resumption and its own failure modes:

AdapterCeilingWhat a larger file would need
@basaltkit/drives-microsoft4 MBcreateUploadSession
@basaltkit/drives-google5 MBuploadType=resumable
@basaltkit/drives-dropbox150 MBfiles/upload_session/*

Anything larger is refused with DriveContentTooLargeError — up front when DriveUploadInput.size is given, and mid-stream otherwise. Nothing here silently truncates, and nothing here promises large files; an app that needs them should upload to the provider itself for now.

Parameters ​

session ​

DriveSession

input ​

DriveUploadInput

Returns ​

Promise<DriveItem>


verifyNotification()? ​

> optional verifyNotification(input): DriveNotificationResult

Defined in: drives/src/provider.ts:504

Verifies an inbound notification. Pure and synchronous: it gets no session and no network, so verification cannot be turned into a request amplifier by an unauthenticated caller hammering the webhook route.

"Verified" is not one guarantee ​

The engine treats every result the same way, and an app that reads shouldSync: true cannot tell which of these produced it. They are not equivalent, and the difference is the vendors', not this contract's:

  • Dropbox — a real signature. X-Dropbox-Signature is an HMAC-SHA256 over the raw body under the app secret. It authenticates the message itself, which is what makes it safe for the engine to act on an DriveNotificationResult.accountIds lookup that necessarily spans tenants.
  • Google and Microsoft — a secret we chose, echoed back (X-Goog-Channel-Token, Graph's clientState). Neither vendor signs anything. This authenticates the subscription, not the bytes: anyone holding the secret can send any body, and a body is not covered at all. That is why the engine matches such a result only against a connection that holds the subscription, and never across tenants.

What makes the weaker one acceptable is not the secret, it is the blast radius: no vendor sends the changed data. A verified notification only ever causes the engine to go and ask the provider, with its own credentials, for its own tenant. A perfect forgery costs a wasted sync. Anything that changed that — a notification whose content was trusted — would need the guarantees to be equalised first.

Parameters ​

input ​

DriveNotificationInput

Returns ​

DriveNotificationResult


watch()? ​

> optional watch(session, input): Promise<DriveWatch>

Defined in: drives/src/provider.ts:471

Subscribes to push notifications.

Parameters ​

session ​

DriveSession

input ​

DriveWatchInput

Returns ​

Promise<DriveWatch>

Released under the MIT License.