Skip to content

feat(storage): add copy, move, and setObjectAccess; deprecate updateObject#97

Merged
designcode merged 5 commits into
mainfrom
feat/copy-and-move
May 11, 2026
Merged

feat(storage): add copy, move, and setObjectAccess; deprecate updateObject#97
designcode merged 5 commits into
mainfrom
feat/copy-and-move

Conversation

@designcode
Copy link
Copy Markdown
Collaborator

@designcode designcode commented May 9, 2026

Summary

  • Adds copy(src, dest, options?) and move(src, dest, options?) for copying/moving objects within or across buckets, sharing a single copyOrMove implementation (move sets the X-Tigris-Rename header). Cross-bucket via srcBucket / destBucket (default to config.bucket); response returns full bucket/key paths for both src and dest.
  • Adds setObjectAccess(path, { access }) extracted from updateObject, mirroring the bucket/set/* layout under lib/object/set/.
  • Marks updateObject @deprecated (still exported for backward compatibility) — use setObjectAccess for ACL changes and move for renames.
  • Replaces the deprecated updateObject integration test block with focused setObjectAccess, copy, and move tests.
  • Includes a changeset (minor bump for @tigrisdata/storage).

Test plan

  • pnpm --filter @tigrisdata/storage exec tsc --noEmit — clean
  • pnpm test — 148 tests pass (8 new integration tests against a real bucket)
  • biome check — clean (runs in pre-commit hook)
  • Verify deprecation warnings show up in consumer IDEs after publish
  • Optional: add cross-bucket integration tests once a second test bucket is configured in CI env

🤖 Generated with Claude Code


Note

Medium Risk
Adds new object mutation APIs (copy/move and ACL updates) that can affect data placement and permissions; behavior is validated by integration tests but relies on correct header/path handling.

Overview
Adds new object operations to @tigrisdata/storage: copy(src, dest, options?) and move(src, dest, options?), including optional cross-bucket support via srcBucket/destBucket and a shared implementation that drives CopyObject requests (with rename via X-Tigris-Rename).

Introduces setObjectAccess(path, { access }) for ACL changes and marks updateObject as @deprecated, steering renames to move and ACL updates to setObjectAccess. Updates public exports and replaces updateObject integration coverage with targeted tests for the new APIs, and includes a changeset for a minor release.

Reviewed by Cursor Bugbot for commit 8390715. Bugbot is set up for automated code reviews on this repo. Configure here.

designcode added 4 commits May 9, 2026 19:39
Splits the ACL-flip behavior out of `updateObject` into a dedicated
`setObjectAccess` helper under `lib/object/set/`, mirroring the
`bucket/set/*` layout. `updateObject` is marked `@deprecated` and will
be removed in a future major version.

Assisted-by: Claude Opus 4.7 via Claude Code
`copy(src, dest, options?)` and `move(src, dest, options?)` both share a
single `copyOrMove` implementation in `lib/object/copy.ts`; `move` is a
thin wrapper that flips the `X-Tigris-Rename` header. Cross-bucket is
supported via `srcBucket` / `destBucket` options (default to
`config.bucket`). Response includes full `src` and `dest` paths
(`bucket/key`) so cross-bucket results are unambiguous.

Replaces `updateObject`'s rename branch (still exported for backward
compatibility, marked `@deprecated`).

Integration tests cover same-bucket copy/move, identical-key rejection,
and `setObjectAccess` public/private (replacing the deprecated
`updateObject` test block).

Assisted-by: Claude Opus 4.7 via Claude Code
Assisted-by: Claude Opus 4.7 via Claude Code
@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented May 9, 2026

Greptile Summary

This PR adds copy, move, and setObjectAccess to the storage package, backed by a shared copyOrMove implementation, and marks updateObject as deprecated in favour of the new focused helpers.

  • copy and move share a single copyOrMove core that sets the X-Tigris-Rename header for moves; both validate inputs and support cross-bucket operations via srcBucket/destBucket options.
  • setObjectAccess extracts the ACL-setting logic from updateObject into its own module under lib/object/set/, matching the existing bucket-level set/ layout.
  • Integration tests replace the old updateObject suite with three focused suites covering setObjectAccess, copy, and move, all cleanly backed by beforeEach/afterEach lifecycle hooks.

Confidence Score: 3/5

Safe to merge after addressing the missing path guard in setObjectAccess.

setObjectAccess skips the empty-path check that copy and move both perform — an empty string passed as path bypasses all local validation and reaches PutObjectAclCommand with Key: '', producing an opaque API error instead of a clear local one. All other logic is sound and the public API surface in server.ts is correctly bounded.

packages/storage/src/lib/object/set/access.ts needs a !path guard before the !options?.access check.

Important Files Changed

Filename Overview
packages/storage/src/lib/object/set/access.ts Extracted ACL setter; missing an empty-path guard present in sibling functions, which lets an empty string reach the AWS SDK instead of returning a clear local error.
packages/storage/src/lib/object/copy.ts New file implementing copy and the shared copyOrMove helper; logic is correct but copyOrMove is exported unnecessarily, making the internal rename flag part of the reachable API surface.
packages/storage/src/lib/object/move.ts Thin wrapper over copyOrMove that sets rename: true; correct and minimal.
packages/storage/src/lib/object/update.ts Adds @deprecated JSDoc to updateObject; no logic changes, minor wording imprecision in the comment.
packages/storage/src/server.ts Exports copy, move, and setObjectAccess through the public API; copyOrMove is correctly omitted from the public surface.
packages/storage/src/test/integration.test.ts Replaces updateObject integration tests with focused setObjectAccess, copy, and move suites; cleanup logic handles post-move source removal safely since S3 DeleteObject is idempotent.

Reviews (1): Last reviewed commit: "chore(storage): add changeset for copy/m..." | Re-trigger Greptile

Comment thread packages/storage/src/lib/object/set/access.ts
Comment thread packages/storage/src/lib/object/update.ts
Comment thread packages/storage/src/lib/object/copy.ts
Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit d2e363a. Configure here.

return missingConfigError('bucket');
}

const destBucket = options?.destBucket ?? srcBucket;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Empty destBucket string bypasses bucket validation

Low Severity

The srcBucket value is validated for emptiness with if (!srcBucket), but destBucket has no equivalent check. If a caller explicitly passes destBucket: '', the nullish coalescing operator (??) won't fall through to srcBucket because empty string isn't nullish. This results in a malformed request path like //${encodeURIComponent(dest)}?x-id=CopyObject instead of a clear validation error.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit d2e363a. Configure here.

Adds an Agent Behavior section clarifying that AI agents must not run
`git commit`, `git push`, or open a PR without an explicit in-the-moment
user instruction; approval for one such action does not extend to the
next.

Assisted-by: Claude Opus 4.7 via Claude Code
@designcode designcode merged commit 024f9e0 into main May 11, 2026
2 checks passed
@designcode designcode deleted the feat/copy-and-move branch May 11, 2026 06:57
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.

2 participants