Skip to content

feat(PowerSync): add attachments support#1616

Open
Chriztiaan wants to merge 13 commits into
TanStack:mainfrom
powersync-ja:feat/powersync-attachments
Open

feat(PowerSync): add attachments support#1616
Chriztiaan wants to merge 13 commits into
TanStack:mainfrom
powersync-ja:feat/powersync-attachments

Conversation

@Chriztiaan

@Chriztiaan Chriztiaan commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

🎯 Changes

Derived from Steven's efforts in powersync-ja/powersync-js#983, and addresses #1563.

Problem

PowerSync ships an attachment helper for syncing files (photos, documents) between local and remote storage. It's separate from regular synced tables: a local-only attachments table tracks each file's lifecycle (QUEUED_UPLOAD, SYNCED, QUEUED_DELETE), and an AttachmentQueue drives uploads/downloads in the background.

TanStackDB, on the other hand, gives you an optimistic, reactive, joinable view over synced data. For users who want to use the attachment helper alongside the PowerSync+TanstackDB integration there are blockers. Saving a file (in the local-only attachments table) and associating it with a record (e.g. setting user.photo_id) are two independent writes which could make data races and fatal errors a problem for data consistency.

The original POC (powersync-js#983) proved this integration was viable. This PR productionises a a subset of it as reusable functionality.

Solution

A TanStackDBAttachmentQueue that extends the SDK's AttachmentQueue (for saving and deleting a file) and backs it with a TanStack DB collection.

The package owns the collection-backed saveFile/delete implementation and leaves the wiring to the application (covered in documentation).

✅ Checklist

  • I have tested this code locally with pnpm test.

🚀 Release Impact

  • This change affects published code, and I have generated a changeset.

Summary by CodeRabbit

  • New Features
    • Added attachment support for PowerSync DB collections, including a TanStackDB-backed attachment queue with atomic save/delete flows.
    • Supports optional per-operation update hooks to apply related data changes within the same transaction.
    • Re-exported the new attachment queue from the package entrypoint.
  • Documentation
    • Added an “Attachments” section with setup, saving, displaying, and removing guidance.
  • Tests
    • Added a dedicated test suite covering upload/delete lifecycle behavior and hook atomicity.
  • Bug Fixes
    • Improved reliability of keeping local files, queued attachment states, and related records in sync.

@coderabbitai

coderabbitai Bot commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: bb746259-b2d9-4df9-94d5-7bfb2e205237

📥 Commits

Reviewing files that changed from the base of the PR and between cd8b191 and 2250d35.

📒 Files selected for processing (1)
  • packages/powersync-db-collection/package.json
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/powersync-db-collection/package.json

📝 Walkthrough

Walkthrough

Adds a TanStack DB-backed attachment queue with public exports, setup documentation, and tests. Attachments can be saved and deleted inside collection transactions, with examples covering watching, syncing, detaching references, and reading cached URIs.

Changes

TanStackDB attachment queue

Layer / File(s) Summary
API surface and package wiring
packages/powersync-db-collection/src/attachments.ts, packages/powersync-db-collection/src/index.ts, packages/powersync-db-collection/package.json, .changeset/curly-planets-lead.md
Adds attachment queue types and implementation, exports them from the package entrypoint, updates PowerSync package versions, and records a patch release.
Queue setup and attachment watching
docs/collections/powersync-collection.md, packages/powersync-db-collection/tests/attachments.test.ts
Documents TanStack DB collection setup and watchAttachments, with shared test setup for attachment state tracking.
Atomic save flow and attachment readback
packages/powersync-db-collection/src/attachments.ts, docs/collections/powersync-collection.md, packages/powersync-db-collection/tests/attachments.test.ts
Implements local persistence, queued uploads, transactional update hooks, sync transitions, caller-supplied IDs, and live-query access to cached attachment URIs.
Atomic delete flow
packages/powersync-db-collection/src/attachments.ts, docs/collections/powersync-collection.md, packages/powersync-db-collection/tests/attachments.test.ts
Implements queued deletion and transactional foreign-key clearing, with tests for synchronization cleanup and unknown IDs.

Estimated code review effort: 4 (Complex) | ~60 minutes

Suggested reviewers: kyleamathews, samwillis

Sequence Diagram(s)

sequenceDiagram
  participant TanStackDBAttachmentQueue
  participant withAttachmentContext
  participant PowerSyncTransactor
  participant attachmentsCollection
  participant updateHook
  TanStackDBAttachmentQueue->>withAttachmentContext: delete()
  withAttachmentContext->>PowerSyncTransactor: open non-auto-committing transaction
  PowerSyncTransactor->>attachmentsCollection: load attachment by id
  PowerSyncTransactor->>attachmentsCollection: set QUEUED_DELETE and reset has_synced
  TanStackDBAttachmentQueue->>updateHook: clear linked foreign key
  PowerSyncTransactor-->>TanStackDBAttachmentQueue: commit
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately summarizes the main change: adding attachments support for PowerSync/TanStack DB.
Description check ✅ Passed The description covers changes, motivation, checklist, and release impact, matching the required template well enough.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 Checkov (3.3.8)
packages/powersync-db-collection/package.json

Traceback (most recent call last):
File "/usr/local/bin/checkov", line 2, in
from checkov.main import Checkov
ModuleNotFoundError: No module named 'checkov'

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install timed out. The project may have too many dependencies for the sandbox.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@socket-security

socket-security Bot commented Jun 25, 2026

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Addednpm/​@​powersync/​node@​0.19.27810010098100
Addednpm/​@​powersync/​common@​1.57.0981008098100

View full report

@Chriztiaan
Chriztiaan marked this pull request as ready for review June 25, 2026 07:45

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.changeset/curly-planets-lead.md:
- Around line 1-3: The changeset for `@tanstack/powersync-db-collection` is
labeled patch, but this release introduces a new public export and raises the
`@powersync/common` peer minimum, so update the changeset in curly-planets-lead.md
to at least minor (or the appropriate breaking level per your policy). Keep the
package name the same and change only the release type so the generated release
notes reflect the expanded surface area and peer dependency bump.

In `@docs/collections/powersync-collection.md`:
- Around line 1195-1207: The watcher example in the referenced attachment-ID
flow is fire-and-forgetting onUpdate, which can let overlapping updates resolve
out of order. Update the example around livePhotoIds.stateWhenReady,
livePhotoIds.subscribeChanges, and the onUpdate callback to serialize each async
invocation by awaiting the previous one (for example through a chained promise
or equivalent queue) so the initial state and later change notifications are
processed in order. Also mirror the same awaited pattern in the test helper that
currently reproduces this race.

In `@packages/powersync-db-collection/src/attachments.ts`:
- Around line 79-80: The `addAttachment` flow in `Attachments` writes the blob
with `localStorage.saveFile()` before the transaction, but failures in `insert`,
`updateHook`, or `commit()` leave the file orphaned on disk. Update the
attachment write path to track the just-saved local URI and delete it on every
exception path, including transaction rollback and any thrown hook/commit error,
while keeping successful saves intact. Also add a regression test around
`updateHook` that throws to verify the DB changes roll back and the local file
is cleaned up.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9f1f73be-698d-4312-886c-aeb36c8b4f13

📥 Commits

Reviewing files that changed from the base of the PR and between 45617c4 and cd8b191.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (6)
  • .changeset/curly-planets-lead.md
  • docs/collections/powersync-collection.md
  • packages/powersync-db-collection/package.json
  • packages/powersync-db-collection/src/attachments.ts
  • packages/powersync-db-collection/src/index.ts
  • packages/powersync-db-collection/tests/attachments.test.ts

Comment thread .changeset/curly-planets-lead.md
Comment thread docs/collections/powersync-collection.md
Comment thread packages/powersync-db-collection/src/attachments.ts
@Chriztiaan

Copy link
Copy Markdown
Contributor Author

@samwillis - could you review this one please?

@tannerlinsley

Copy link
Copy Markdown
Member

I ran the exact package suite locally and all 92 tests pass, but I still need a few correctness fixes before merging this:

  • saveFile() runs before the collection transaction, so a failed insert, hook, or commit leaves an orphaned local file. Please clean it up on failure and add a regression test.
  • The docs show async updateHook callbacks, but the hook is called inside synchronous transaction.mutate() and its promise is ignored. Any mutation after an await escapes the transaction. The contract and examples need to be explicitly synchronous, or the implementation needs an actually awaited atomic design.
  • watchAttachments calls need to be serialized so an older async update can't win after a newer one.
  • This adds public API and raises the PowerSync peer floor from 1.41 to 1.57, so the changeset should be minor, not patch.

The earlier review threads are marked resolved, but the current diff still has these behaviors.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants