Skip to content

feat(storage): add vector bucket CRUD (alpha) - #1153

Merged
grdsdev merged 5 commits into
mainfrom
feat-storage-vector-buckets
Aug 7, 2026
Merged

feat(storage): add vector bucket CRUD (alpha)#1153
grdsdev merged 5 commits into
mainfrom
feat-storage-vector-buckets

Conversation

@grdsdev

@grdsdev grdsdev commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Adds storage.vectors.{createBucket,getBucket,listBuckets,deleteBucket} for Supabase Storage's alpha "vector buckets" feature, mirroring supabase-js's supabase.storage.vectors client. Indexes and vector data operations (put/get/query/delete vectors) are out of scope for this pass — start small.
  • Implemented by hand on the existing StorageApi/StorageHTTPSession HTTP stack used by the rest of the Storage module (StorageBucketApi, StorageFileApi), not via a generated OpenAPI client — no new dependency on tools/openapi-codegen or the HTTPRuntime transport bridge.
  • Rebased onto current main (previously based on an older main snapshot).
  • VectorBucket.creationTime is TimeInterval?, not Int?: the wire value is a raw UNIX timestamp (seconds), not an ISO8601 string like this module's other Date fields expect, so TimeInterval round-trips directly without custom Codable logic. Matches Session.expiresAt's existing convention for UNIX timestamps elsewhere in this codebase. (Required changing this file's import Foundation to public import Foundation, since TimeInterval is now exposed in a public property.)
  • StorageVectorsClient is a struct holding a StorageApi dependency (composition), not a StorageApi subclass — calls go through api.execute(...)/api.configuration instead of inherited members. SupabaseStorageClient.vectors passes self as that dependency, so custom headers set via setHeader(...) on the main storage client are shared with .vectors rather than each getting an independent StorageApi instance.

Vector buckets are a public alpha feature upstream; the new API surface is gated behind @_spi(Experimental) and documented as such.

Test plan

  • swift build
  • swift test (full suite, 1062 tests)
  • ./scripts/format.sh

@grdsdev
grdsdev requested a review from a team as a code owner July 27, 2026 21:54
@coderabbitai

coderabbitai Bot commented Jul 27, 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: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 2ceb1fa7-59aa-4646-a093-fefff232dd4e

📥 Commits

Reviewing files that changed from the base of the PR and between 887df65 and 1faa2d5.

📒 Files selected for processing (3)
  • Sources/Storage/StorageVectorsClient.swift
  • Sources/Storage/SupabaseStorage.swift
  • Tests/StorageTests/StorageVectorsClientTests.swift
🚧 Files skipped from review as they are similar to previous changes (2)
  • Sources/Storage/SupabaseStorage.swift
  • Tests/StorageTests/StorageVectorsClientTests.swift

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added experimental support for managing Supabase Storage vector buckets.
    • Create, retrieve, list, and delete vector buckets.
    • Filter bucket listings by prefix and navigate results with pagination tokens.
    • View bucket names and creation timestamps through the storage client’s experimental vectors interface.
  • Tests

    • Added coverage for vector bucket operations, filtering, pagination, and error handling.

Walkthrough

Added an experimental StorageVectorsClient for vector bucket creation, retrieval, listing, and deletion. Added public bucket and pagination response models. Exposed the client through SupabaseStorageClient.vectors. Added mocked unit tests and environment-gated integration tests for CRUD operations, filtering, pagination, cleanup, and forbidden-response errors.

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant SupabaseStorageClient
  participant StorageVectorsClient
  participant VectorBucketAPI
  Caller->>SupabaseStorageClient: Access vectors
  SupabaseStorageClient-->>Caller: Return StorageVectorsClient
  Caller->>StorageVectorsClient: Invoke bucket operation
  StorageVectorsClient->>VectorBucketAPI: Send vector bucket request
  VectorBucketAPI-->>StorageVectorsClient: Return bucket data or error
  StorageVectorsClient-->>Caller: Return decoded result or StorageError
Loading

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.

@grdsdev
grdsdev changed the base branch from claude/httpruntime-target-085a05 to claude/sad-poincare-c48d88 July 27, 2026 22:58
@grdsdev
grdsdev force-pushed the feat-storage-vector-buckets branch from 0dfb362 to f67bd81 Compare July 28, 2026 08:07
@github-actions

Copy link
Copy Markdown
Contributor

⚠️ Capability matrix drift detected

The following capabilities are marked implemented in swift but have no registered symbols to verify:

  • auth.passkey.register_passkey (no symbols list — cannot confirm implementation exists)
  • auth.passkey.sign_in_with_passkey (no symbols list — cannot confirm implementation exists)
  • client.authentication_integration.third_party_auth (no symbols list — cannot confirm implementation exists)
  • client.authentication_integration.cross_client_token_sync (no symbols list — cannot confirm implementation exists)
  • client.authentication_integration.oauth_flow_type (no symbols list — cannot confirm implementation exists)
  • client.authentication_integration.session_url_detection (no symbols list — cannot confirm implementation exists)
  • client.session_management.custom_storage (no symbols list — cannot confirm implementation exists)
  • client.session_management.persist_session (no symbols list — cannot confirm implementation exists)
  • client.request_configuration.global_headers (no symbols list — cannot confirm implementation exists)
  • client.observability.trace_propagation (no symbols list — cannot confirm implementation exists)
  • database.query.from_table (no symbols list — cannot confirm implementation exists)
  • database.query.select (no symbols list — cannot confirm implementation exists)
  • database.query.schema_selection (no symbols list — cannot confirm implementation exists)
  • database.query.rpc (no symbols list — cannot confirm implementation exists)
  • database.mutate.insert (no symbols list — cannot confirm implementation exists)
  • database.mutate.update (no symbols list — cannot confirm implementation exists)
  • database.mutate.upsert (no symbols list — cannot confirm implementation exists)
  • database.mutate.delete (no symbols list — cannot confirm implementation exists)
  • database.mutate.select_after_mutation (no symbols list — cannot confirm implementation exists)
  • database.using_filters.eq (no symbols list — cannot confirm implementation exists)
  • database.using_filters.neq (no symbols list — cannot confirm implementation exists)
  • database.using_filters.gt (no symbols list — cannot confirm implementation exists)
  • database.using_filters.gte (no symbols list — cannot confirm implementation exists)
  • database.using_filters.lt (no symbols list — cannot confirm implementation exists)
  • database.using_filters.lte (no symbols list — cannot confirm implementation exists)
  • database.using_filters.like (no symbols list — cannot confirm implementation exists)
  • database.using_filters.ilike (no symbols list — cannot confirm implementation exists)
  • database.using_filters.is (no symbols list — cannot confirm implementation exists)
  • database.using_filters.in (no symbols list — cannot confirm implementation exists)
  • database.using_filters.contains (no symbols list — cannot confirm implementation exists)
  • database.using_filters.contained_by (no symbols list — cannot confirm implementation exists)
  • database.using_filters.range_gt (no symbols list — cannot confirm implementation exists)
  • database.using_filters.range_gte (no symbols list — cannot confirm implementation exists)
  • database.using_filters.range_lt (no symbols list — cannot confirm implementation exists)
  • database.using_filters.range_lte (no symbols list — cannot confirm implementation exists)
  • database.using_filters.range_adjacent (no symbols list — cannot confirm implementation exists)
  • database.using_filters.overlaps (no symbols list — cannot confirm implementation exists)
  • database.using_filters.text_search (no symbols list — cannot confirm implementation exists)
  • database.using_filters.match (no symbols list — cannot confirm implementation exists)
  • database.using_filters.not (no symbols list — cannot confirm implementation exists)
  • database.using_filters.or (no symbols list — cannot confirm implementation exists)
  • database.using_filters.raw (no symbols list — cannot confirm implementation exists)
  • database.using_filters.regex (no symbols list — cannot confirm implementation exists)
  • database.using_filters.regex_icase (no symbols list — cannot confirm implementation exists)
  • database.using_filters.is_distinct (no symbols list — cannot confirm implementation exists)
  • database.using_filters.like_all (no symbols list — cannot confirm implementation exists)
  • database.using_filters.like_any (no symbols list — cannot confirm implementation exists)
  • database.using_filters.ilike_all (no symbols list — cannot confirm implementation exists)
  • database.using_filters.ilike_any (no symbols list — cannot confirm implementation exists)
  • database.using_modifiers.order (no symbols list — cannot confirm implementation exists)
  • database.using_modifiers.limit (no symbols list — cannot confirm implementation exists)
  • database.using_modifiers.range (no symbols list — cannot confirm implementation exists)
  • database.using_modifiers.single_row (no symbols list — cannot confirm implementation exists)
  • database.using_modifiers.strip_nulls (no symbols list — cannot confirm implementation exists)
  • database.using_modifiers.format_csv (no symbols list — cannot confirm implementation exists)
  • database.using_modifiers.format_geojson (no symbols list — cannot confirm implementation exists)
  • database.using_modifiers.max_affected_rows (no symbols list — cannot confirm implementation exists)
  • database.using_modifiers.request_cancellation (no symbols list — cannot confirm implementation exists)
  • database.configuration.auto_retry (no symbols list — cannot confirm implementation exists)
  • functions.invocation.invoke (no symbols list — cannot confirm implementation exists)
  • functions.invocation.set_auth_token (no symbols list — cannot confirm implementation exists)
  • functions.invocation.method_override (no symbols list — cannot confirm implementation exists)
  • functions.invocation.streaming_response (no symbols list — cannot confirm implementation exists)
  • functions.invocation.request_cancellation (no symbols list — cannot confirm implementation exists)
  • realtime.client.connect (no symbols list — cannot confirm implementation exists)
  • realtime.client.disconnect (no symbols list — cannot confirm implementation exists)
  • realtime.client.get_channels (no symbols list — cannot confirm implementation exists)
  • realtime.client.remove_channel (no symbols list — cannot confirm implementation exists)
  • realtime.client.remove_all_channels (no symbols list — cannot confirm implementation exists)
  • realtime.client.connection_state (no symbols list — cannot confirm implementation exists)
  • realtime.client.listen_heartbeats (no symbols list — cannot confirm implementation exists)
  • realtime.client.set_auth_token (no symbols list — cannot confirm implementation exists)
  • realtime.client.channel (no symbols list — cannot confirm implementation exists)
  • realtime.channel.subscribe (no symbols list — cannot confirm implementation exists)
  • realtime.channel.unsubscribe (no symbols list — cannot confirm implementation exists)
  • realtime.channel.send (no symbols list — cannot confirm implementation exists)
  • realtime.channel.broadcast_http (no symbols list — cannot confirm implementation exists)
  • realtime.subscriptions.postgres_changes (no symbols list — cannot confirm implementation exists)
  • realtime.subscriptions.subscribe_presence (no symbols list — cannot confirm implementation exists)
  • realtime.subscriptions.private_channel (no symbols list — cannot confirm implementation exists)
  • realtime.subscriptions.broadcast_self (no symbols list — cannot confirm implementation exists)
  • realtime.subscriptions.broadcast_ack (no symbols list — cannot confirm implementation exists)
  • realtime.subscriptions.broadcast_replay (no symbols list — cannot confirm implementation exists)
  • realtime.presence.track (no symbols list — cannot confirm implementation exists)
  • realtime.presence.untrack (no symbols list — cannot confirm implementation exists)
  • realtime.presence.presence_key (no symbols list — cannot confirm implementation exists)
  • realtime.configuration.custom_websocket_transport (no symbols list — cannot confirm implementation exists)
  • realtime.configuration.reconnect_backoff (no symbols list — cannot confirm implementation exists)
  • realtime.configuration.heartbeat_interval (no symbols list — cannot confirm implementation exists)
  • realtime.configuration.access_token_callback (no symbols list — cannot confirm implementation exists)
  • realtime.configuration.deferred_disconnect (no symbols list — cannot confirm implementation exists)
  • realtime.configuration.custom_logger (no symbols list — cannot confirm implementation exists)
  • realtime.configuration.binary_protocol (no symbols list — cannot confirm implementation exists)
  • storage.file_buckets.get_bucket (no symbols list — cannot confirm implementation exists)
  • storage.file_buckets.list_file_buckets (no symbols list — cannot confirm implementation exists)
  • storage.file_buckets.update_bucket (no symbols list — cannot confirm implementation exists)
  • storage.file_buckets.delete_file_bucket (no symbols list — cannot confirm implementation exists)
  • storage.file_buckets.empty_bucket (no symbols list — cannot confirm implementation exists)
  • storage.file_buckets.access_bucket (no symbols list — cannot confirm implementation exists)
  • storage.file_buckets.upload (no symbols list — cannot confirm implementation exists)
  • storage.file_buckets.download (no symbols list — cannot confirm implementation exists)
  • storage.file_buckets.move (no symbols list — cannot confirm implementation exists)
  • storage.file_buckets.copy (no symbols list — cannot confirm implementation exists)
  • storage.file_buckets.remove (no symbols list — cannot confirm implementation exists)
  • storage.file_buckets.create_signed_url (no symbols list — cannot confirm implementation exists)
  • storage.file_buckets.create_signed_urls (no symbols list — cannot confirm implementation exists)
  • storage.file_buckets.create_signed_upload_url (no symbols list — cannot confirm implementation exists)
  • storage.file_buckets.upload_with_signed_url (no symbols list — cannot confirm implementation exists)
  • storage.file_buckets.update_file (no symbols list — cannot confirm implementation exists)
  • storage.file_buckets.file_exists (no symbols list — cannot confirm implementation exists)
  • storage.file_buckets.file_info (no symbols list — cannot confirm implementation exists)
  • storage.file_buckets.list_files_paginated (no symbols list — cannot confirm implementation exists)
  • storage.file_buckets.copy_cross_bucket (no symbols list — cannot confirm implementation exists)
  • storage.file_buckets.move_cross_bucket (no symbols list — cannot confirm implementation exists)
  • storage.file_buckets.upload_with_metadata (no symbols list — cannot confirm implementation exists)
  • storage.file_buckets.url_cache_nonce (no symbols list — cannot confirm implementation exists)

These may have been renamed, removed, or never registered. Please update the capability matrix.
See: https://github.com/supabase/sdk/blob/main/docs/capability-matrix.md

Implements the storage.vectors client by hand, using the same
StorageApi/StorageHTTPSession HTTP stack as the rest of the Storage
module, instead of a generated OpenAPI client.
@grdsdev
grdsdev force-pushed the feat-storage-vector-buckets branch from f67bd81 to 4fd5e10 Compare August 6, 2026 20:41
@grdsdev
grdsdev changed the base branch from claude/sad-poincare-c48d88 to main August 6, 2026 20:41
Verified against a local Supabase stack (supabase start): create,
get, list (incl. prefix filtering), and delete, plus the 404 error
shape for a missing bucket.

@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: 1

🤖 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 `@Tests/IntegrationTests/StorageVectorsClientIntegrationTests.swift`:
- Around line 13-14: Mark the StorageVectorsClientIntegrationTests suite as
serialized so vectorBucket_CRUD and listBucketsWithPrefix cannot run
concurrently against the shared test-vector-bucket resource. Keep the existing
INTEGRATION_TESTS enablement condition unchanged.
🪄 Autofix

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: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 9097a4da-d49c-488a-b3cc-86b6a8c48999

📥 Commits

Reviewing files that changed from the base of the PR and between 0826b28 and eae7b06.

📒 Files selected for processing (4)
  • Sources/Storage/StorageVectorsClient.swift
  • Sources/Storage/SupabaseStorage.swift
  • Tests/IntegrationTests/StorageVectorsClientIntegrationTests.swift
  • Tests/StorageTests/StorageVectorsClientTests.swift

Comment on lines +13 to +14
@Suite(.enabled(if: ProcessInfo.processInfo.environment["INTEGRATION_TESTS"] != nil))
struct StorageVectorsClientIntegrationTests {

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.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf 'Files matching StorageVectorsClientIntegrationTests.swift:\n'
fd -a 'StorageVectorsClientIntegrationTests.swift' . || true

printf '\nRelevant file contents:\n'
f="$(fd 'StorageVectorsClientIntegrationTests.swift' . | head -n 1)"
if [ -n "${f:-}" ]; then
  wc -l "$f"
  sed -n '1,140p' "$f" | cat -n
fi

printf '\nSearch for test-vector-bucket usages:\n'
rg -n 'test-vector-bucket|vectorBucket_CRUD|listBucketsWithPrefix|`@Suite`|`@Test`' .

Repository: supabase/supabase-swift

Length of output: 50379


🌐 Web query:

Swift Testing @suite .serialized trait documentation

💡 Result:

The.serialized trait in the Swift Testing framework is a trait used to control the execution order of tests by enforcing sequential (serial) rather than parallel execution [1][2]. Key characteristics and usage include: Application: It can be applied to both individual @Test functions and entire @Suite types [2][3]. Parameterized Tests: When applied to a parameterized test function, it ensures that all cases of that test run serially [2][4]. Suite Scope: When applied to a @Suite, the trait is applied recursively; all test functions and sub-suites contained within that suite will be executed serially [2][5]. Non-parameterized Tests: When applied to a non-parameterized, single test function, it generally has no effect, as those tests are naturally serial with respect to their internal code [2][4]. Scope of Effect: This trait only affects the serialization of the tests to which it is applied (or its children); it does not affect the execution of a test relative to its peers in unrelated suites or the global test execution [2][5]. Global Context: This trait is ignored if test parallelization is disabled globally (e.g., by passing the --no-parallel flag to the swift test command) [2][5]. The trait is available as a public API in the Testing framework and is documented as part of the ParallelizationTrait documentation [1][2][3].

Citations:


Serialize test-vector-bucket integration tests.

vectorBucket_CRUD and listBucketsWithPrefix share the same bucket name. Swift Testing can run suite tests concurrently unless the suite is serialized, so one test can delete or create the bucket while another test reads it.

Proposed fix
-@Suite(.enabled(if: ProcessInfo.processInfo.environment["INTEGRATION_TESTS"] != nil))
+@Suite(.enabled(if: ProcessInfo.processInfo.environment["INTEGRATION_TESTS"] != nil), .serialized)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@Suite(.enabled(if: ProcessInfo.processInfo.environment["INTEGRATION_TESTS"] != nil))
struct StorageVectorsClientIntegrationTests {
`@Suite`(.enabled(if: ProcessInfo.processInfo.environment["INTEGRATION_TESTS"] != nil), .serialized)
struct StorageVectorsClientIntegrationTests {
🤖 Prompt for 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.

In `@Tests/IntegrationTests/StorageVectorsClientIntegrationTests.swift` around
lines 13 - 14, Mark the StorageVectorsClientIntegrationTests suite as serialized
so vectorBucket_CRUD and listBucketsWithPrefix cannot run concurrently against
the shared test-vector-bucket resource. Keep the existing INTEGRATION_TESTS
enablement condition unchanged.

The wire value is a raw UNIX timestamp (seconds), not an ISO8601
string, so Int obscured the semantics. Matches Session.expiresAt's
existing TimeInterval convention for UNIX timestamps in this codebase.
Composition instead of subclassing StorageApi: the client now holds a
StorageApi dependency passed in at init, rather than inheriting execute()
and configuration directly. SupabaseStorageClient.vectors now passes
itself as that dependency, so custom headers set via setHeader() are
shared between the main client and .vectors.
@coveralls

coveralls commented Aug 6, 2026

Copy link
Copy Markdown

Coverage Report for CI Build 31160573331

Warning

No base build found for commit 0826b28 on main.
Coverage changes can't be calculated without a base build.
If a base build is processing, this comment will update automatically when it completes.

Coverage: 83.944%

Details

  • Patch coverage: 3 uncovered changes across 1 file (48 of 51 lines covered, 94.12%).

Uncovered Changes

File Changed Covered %
Sources/Storage/SupabaseStorage.swift 3 0 0.0%
Total (2 files) 51 48 94.12%

Coverage Regressions

Requires a base build to compare against. How to fix this →


Coverage Stats

Coverage Status
Relevant Lines: 10121
Covered Lines: 8496
Line Coverage: 83.94%
Coverage Strength: 40.04 hits per line

💛 - Coveralls

@grdsdev
grdsdev merged commit a4ad8af into main Aug 7, 2026
32 checks passed
@grdsdev
grdsdev deleted the feat-storage-vector-buckets branch August 7, 2026 09:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants