Navigácia
ELOWEN · Dokumentácia

Posledná aktualizácia: 20. októbra 2026

Prechádzať dokumentáciu · onedrive implementation

Tento článok zatiaľ nie je preložený. Je dostupný v angličtine.

Developer reference

onedrive implementation

Project removal

This participant requires core 0.30.11 or later for durable removal acknowledgement and transaction-scoped active-Project admission. Update core before installing this plugin version.

Mirror creation/reconnection calls tx.host.projects.requireActive(id) in the same transaction as the link upsert, after remote-folder lookup has finished. Core's Project row lock serializes this durable binding with deletion across processes. Existing cleanup markers and cycle claims continue to fence baseline writes; remote work is not performed inside the admission transaction.

The plugin declares provides.projectRemoval: "services". Its registerProjectRemoved callback awaits SyncEngine.removeProject: the store first marks every matching link for removal, disables it and invalidates its claim, then drains baselines and removes links. A failed drain leaves every target fenced for retry. The engine then waits for already-admitted in-process account cycles and root-lock work before acknowledging completion. This drain currently includes unrelated active cycles because those existing owners are account/root scoped. Cleanup does not delete Microsoft credentials or remote OneDrive folders/files. API and directory/worktree resolution reject Projects with lifecycle deleting, including a second connect check after remote-folder lookup.

Migration entry

The manifest entry exports migrate(db: PluginDb<never>): Promise<void> beside register. The daemon loader always awaits it before registration for this reads: ['db'] plugin; the DB CLI calls the same export without registering tools, routes, timers or services. The export runs only the existing transactional plugin SQL through db.migrate. A failed step rolls back its SQL and ledger entry and prevents registration. Repeated calls skip applied versions. Runtime store construction never invokes migrations. Tests that bypass the loader explicitly await this export before using their fixture.

Problem audiences

Account, mirror and file-operation logs preserve the typed Microsoft/core cause, user filesystem errno and native network causes before selecting publisher reporting. Missing remote folders and unavailable managed environments carry the core instance-condition type; direct chunk HTTP refusals retain that type for access, conflict, quota and availability. Response-contract defects and persistence failures remain reportable. Retry counters and diagnostic text are unchanged. Microsoft Teams supplies typed delegated Graph causes; both plugins require the matching core's public elowen-plugin-shared/errors seam.

Retry reporting

SyncEngine uses ConsecutiveFailures from elowen-plugin-shared/consecutiveFailures, requiring core 0.30.7 and shared API 14. Account startup, mirror cycles and file operations count independently by account, link, and link plus relative path. Managed read/upload and local file failures share the file counter across cycles. Only failure three exports the existing code; subsequent failures stay local until success resets that counter. A successfully planned file resets its counter only when that cycle recorded no failure for that path. Bulk-deletion refusal remains local and does not consume a failure counter.

A managed mirror whose environment is unprovisioned, starting or stopped is not a failure: managedFor throws EnvironmentNotRunningError, and the cycle defers that link for the same 60-second backoff as environment_pending, keeps the start-it explanation on the link and logs at info without consuming the mirror counter. A failed, deleting or deleted environment still counts towards onedrive.mirror_failed.

Bounded baseline persistence

Database deletion uses core-owned drainDbRows from elowen-plugin-shared/dbBatches, which requires core 0.30.7 and shared API 14. It selects identities and payload sizes, commits at most 64 rows / 256 KiB per batch, processes one oversized row alone and yields between commits. Other bounded loops use the same module's yieldDbBatch for their event-loop yield.

The PostgreSQL schema plugin_onedrive contains links and items. The four async migrations create the link, baseline and durable cleanup marker. The host awaits the entry export migrate(db) before registration; OneDriveStore.open(await ctx.db()) only constructs the runtime store. PostgreSQL steps 1 through 4 create the current schema; installed ledgers already record them, so they are never renumbered. Every SQL object is explicitly schema-qualified, every value uses positional parameters, and the host supplies the async PluginDb. Link IDs and baseline row_id values are bigint identities generated by PostgreSQL. The item primary key remains (link_id, rel_path); relative paths use C collation for bytewise keyset paging. Stored instants use timestamptz and project back to epoch milliseconds. Local file mtimes remain double precision milliseconds so fractional filesystem times survive.

Reset and disconnect invalidate the current claim before draining baseline rows in transactions of at most 64 rows and 256 KiB of stored text plus overhead, with setImmediate between batches. Claims and live link projections treat cleanup as disabled, so a sync cannot read a partly erased baseline. Reconnecting a marked link fails until cleanup completes. Boot reconciliation starts reset/removal marker drains in the background, logs rejected drains and releases predecessor claims without waiting for the full drain. Markers keep claims fenced until completion, so long cleanup does not consume the host's boot-reconcile budget. API and account/Project cleanup callers await the complete operation; remote files remain untouched.

Every OneDriveStore method returns a promise. items(linkId) builds its Map in indexed, 64-row relative-path keyset pages. The shared deletion helper receives the raw schema, items table and explicit row_id identity, with octet_length measuring UTF-8 payload bytes. SyncEngine awaits both inventory reads and rechecks its existing CycleFence after the final read before writing status. The baseline remains per link and uses the existing primary key; no second database or core API is introduced.

A reconnect uses one conflict-aware INSERT with RETURNING. Its conflict predicate checks the cleanup marker while PostgreSQL owns the existing row lock, so a concurrent reset cannot reconnect a partial baseline. A changed drive, folder or local subpath marks the link for reset and invalidates the claim in that same write, then drains the old baseline before returning the new link.

CycleFence.commit(write) renews/checks the claim, then calls withCurrentLink to lock the link row and revalidate its owner, expiry, enabled state, cleanup marker and destination before executing the supplied async SQL writes on that same transaction. SyncEngine uses it for baseline, conflict and status writes. The SQL-only mutation methods accept either the root PluginDb or a PluginDbTx; only the latter exposes the transaction-bound host facade. CycleFence always passes its locked transaction handle. The store owns CycleStoppedError, shared by the fence and SyncEngine so claim rejection has one error identity without a store-to-fence dependency. Pause, reset, remove and reconnect serialize with that row lock; a superseded cycle rejects without publishing stale baseline data. Remote and filesystem operations stay outside database transactions. Host project reads and access checks are awaited at their existing resolution boundaries. MicrosoftIdentityControl.identityFor(accountUserId) is also asynchronous under shared API 13. The overview route awaits its identity object before serializing the response; panel visibility awaits the same account lookup before deciding whether to expose the project panel. Missing control means an unlinked identity and a hidden panel. Lookup failures propagate to the host without becoming a successful unlinked result. Neither consumer makes a Graph request.

Sync implementation boundaries

Maintainer section: the content below describes the code that owns each rule. It is not needed to use the plugin.

src/cycle.ts owns the shared asynchronous CycleFence.check() and deletion-valve decision used by both sync planners. Call the fence before the next operation, after awaited work before a baseline or status write, and before finishing the cycle. It checks the current stored link and renews the existing worker claim by elapsed time; it needs one link read per check and a renewal only when due, with no Graph requests. An obsolete link or lost claim stops the cycle without publishing a replacement status. With no enabled mirror, no cycle runs. Each transport retains its own planner and Git execution.

src/paths.ts owns remote folder paths and conflictName(rel, when, taken). A synchronous occupancy callback returns a name immediately; a managed asynchronous callback returns a promise. Both use the same extension-preserving names and bounded collision search. src/trash.ts owns local and managed trash moves; local conflict-resolution routes reuse trashFile, while sync supplies its current-cycle check before the final rename. Internal helper imports move to their owning modules; the installed plugin entrypoint, controls and HTTP routes keep their contracts.

Managed command and browser contracts

Editor and OneDrive execute one-shot project commands with sandbox.runCommand(input, actor, options). The host authorizes the prepared target and owns byte capture, heartbeat, timeout, cancellation and lease release; the actor preserves the caller's account and allowed roots. Editor uses leaseKind: 'editor', a 30-second timeout and an 8 MiB combined output bound, returns sanitized stdout, and rejects nonzero results. OneDrive uses leaseKind: 'files', its Git timeout of 120 seconds by default, and a 64 MiB combined bound. It retains real Git exit codes and maps interrupted execution to code: null with sanitized partial output; cleanup and authorization errors remain exceptions. Missing Sandbox support makes the operation unavailable. Long-lived services retain raw prepareExecution sessions. No consumer timer or second capture loop is required.

OneDrive's browser runtime declarations describe the host components used by OneDriveProjectPanel. Every declared name is checked against the published UI kit, so a component the host does not publish fails the build instead of reaching React as undefined. The panel renders mirror values as SettingsRow inside SettingsGroup, each workspace as a SettingsItem row inside SettingsList, conflicts as a DataTable inside WorkspaceDetailRail and the disconnect confirmation as ConfirmDialog. With no active worktree no workspace list is mounted. Workspace rows carry no open action: every state answers through its own buttons. This declaration is type-only, adds no runtime work and does not extend the host component API or change host styling.

OneDrive requires UI API 62 and uses the host's interpolate and formatBytes signatures; interpolate(template, values) handles named literal substitutions and formatBytes(bytes, viewerLocale) formats the mirror's byte count using the displayed language. Missing runtime support prevents loading rather than selecting a local fallback.

Managed file writes

ManagedMirror uses elowen-plugin-shared/guestFiles for all guest file I/O. For example, writeGuestFile(operation => this.call(operation), path, bytes, { expectedVersion }) selects one write up to 512 KiB and chunked upload above it. The callback forwards operations to the existing projectFiles binding, preserving its acting account, Project, workspace, expected generation and startIfNeeded: false.

Core owns chunk boundaries, acknowledgements, the resolved destination, missing parent directories and atomic version-checked commit. It aborts a failed upload and reports cleanup failure alongside the original error; begin and commit refusals retain their codes. The same client owns every folder-list page, bounded walk, stat, version-pinned range reads, streamed hashing, mkdir and rename. OneDrive retains mirror conflicts, size limits and sync decisions, and constructs no guest operation literals. Requires core 0.30.7 and shared API 14; ship together with Editor and LSP in the same registry snapshot as the core. A missing or stopped environment is still unavailable, never started by a write. This adds no extra file copy or whole-file buffer beyond the downloaded bytes already held by the mirror.

Conflict resolution and bounded scans

A Project can be mirrored directly, and each active managed Sandbox worktree can be connected separately. OneDrive obtains worktree IDs and labels through sandbox.managedWorktrees with the acting account and startIfNeeded: false. Host Projects have no Sandbox worktree list. Without Sandbox support a managed file operation is unavailable; it never falls back to a host directory. Stored paused links from retired local workspaces remain until an explicit retention decision.

POST conflicts/resolve takes { id, rel, keep: 'local' | 'remote' } and rechecks mirror ownership and Project access. SyncEngine.resolveConflict uses the existing mirror claim, base-directory lock and CycleFence, reads the current conflict under that lock, and verifies the Microsoft drive ID and remote ETag before selecting the existing host or guest transport. Keeping local uploads conditionally on that ETag. Keeping remote promotes the already downloaded conflict copy and preserves the superseded local file in sync trash. Managed reads and renames use guest version checks; rename requires an absent destination. Missing copies, stale versions, a running claim, a paused or removed link return 409 without recording a successful baseline. A transfer already sent may finish; the next operation boundary stops further work. A later remote ETag reoffers the conflict through the normal cycle while preserving earlier copies. Without an enabled mirror no resolution runs. OneDriveProjectPanel is the real consumer; no general filesystem adapter is introduced.

scanManaged passes exact mandatory directory names to ManagedMirror.walk, which forwards them to Sandbox's bounded guest walk. The same name list derives the hard-floor patterns for .git, node_modules, .elowen-trash, .ssh and .aws. These directories are pruned at every depth before consuming the walk budget. Git membership and the full ignore predicate still filter returned files. Arbitrary extraIgnore globs are not translated into directory skips. The 20,000-entry budget, one sentinel entry and the guest depth/deadline bounds remain; an incomplete scan cannot authorize deletion. ScanResult exposes files, skippedPaths, fromGit, complete and isIgnored. skippedPaths is the sole skipped-path projection and is consulted by both sync planners before deciding absence; it is not a user-facing skip report.

The OneDrive settings reader in src/index.ts normalizes extraIgnore once into string tokens, preserving commas inside brace globs and already-tokenized array entries. SyncEngine, registerApi and buildIgnore consume that normalized array through SyncSettings. rootFolder is trimmed and stripped of surrounding slashes before the Elowen default is applied; remoteRootFor consumes it without another fallback. registerApi uses one directory resolver for folders and connect, and reuses the fetched workspace label. OneDrive imports errorText from elowen-plugin-shared/errors and declares requiresSharedApi: 14 with core 0.30.7, matching the coordinated shared contract. The helper preserves Error.message and stringifies other thrown values without introducing retries or success defaults. registerUserRemoved is required and always wires account cleanup, leaving remote files untouched. Only the injected lease timing override is internal to tests; production lease durations are unchanged. DriveItem exposes only id, etag and size while validating raw Graph id and name. MirrorLink no longer projects createdAt; its stored SQL column remains pending a separate migration decision.