icloud
@noy-db/to-icloud·pnpm add @noy-db/to-icloudThe whole vault as a single iCloud Drive bundle (macOS).
| Shape | bundle |
| Atomic CAS | n/a |
| Locality | cloud |
| Speed | slow |
| Persistence | durable |
What it does
Treats each vault as a single .noydb bundle on iCloud Drive. Handles both shapes
iCloud uses when it offloads a file's contents to save local disk space, and raises
PodVersionConflictError on parallel-device conflict copies so you merge
deliberately instead of silently losing writes.
Eviction has two shapes
Which one you get depends on the macOS version, and the modern one is not the
.icloud stub most documentation describes:
| modern (verified on Darwin 24.6 / Sequoia) | legacy | |
|---|---|---|
| On disk | the canonical file, APFS-dataless — stat reports full logical size, du reports 0 bytes | a separate <name>.icloud placeholder |
| A plain read | blocks transparently while the OS rehydrates, then succeeds (~0.7 s for a 195 KB bundle) | fails; needs an explicit download trigger |
| Store path taken | the ordinary read path | stub detection → brctl download |
On current macOS the stub-detection sequence never fires: stat(path) succeeds, so
the store is correct here through its normal path. The stub branch and
triggerDownload are the legacy shape, kept for older macOS and other sync
surfaces — not the common case.
st_blocks-vs-logical-size comparison, which the duck-typed
ICloudFs facade does not expose — so there is no way to check first. Budget for a
slow first read rather than trying to detect one.Notes
- Bundle-only (
NoydbPodStore), not the six-methodNoydbStorecontract — wrap it withwrapPodStore()from@noy-db/hubto get the standard record-level surface. casAtomic: n/a — whole-vault file, no per-record CAS.- Node on macOS only. Factory:
toIcloud(options: ICloudStoreOptions).
Store-locator descriptor
See store-locator descriptors for the general model.
kind: 'icloud', class: 'local'. address is { folder } — required,
unlike most stores in its tier toIcloud() has no default. options is { suffix? }. binding.client is required (an ICloudFs) — this store
has no filesystem facade of its own; resolve() without it throws. Because
toIcloud() returns a NoydbPodStore, its factory's return type is
deliberately unnarrowed — wrap the resolved store with wrapPodStore()
if you need the six-method surface.
import { icloudStoreDescriptor, registerIcloudStore } from '@noy-db/to-icloud'
const descriptor = icloudStoreDescriptor({ folder: '/Users/me/Library/Mobile Documents/…' })