Skip to content

Sync with InsForge-sdk-js v1.5.0#30

Open
Fermionic-Lyu wants to merge 1 commit into
mainfrom
sdk-sync/v1.5.0
Open

Sync with InsForge-sdk-js v1.5.0#30
Fermionic-Lyu wants to merge 1 commit into
mainfrom
sdk-sync/v1.5.0

Conversation

@Fermionic-Lyu

@Fermionic-Lyu Fermionic-Lyu commented Jul 21, 2026

Copy link
Copy Markdown
Member

Summary

Ports the storage changes from InsForge-sdk-js v1.5.0 (diff v1.4.5..v1.5.0) — Storage: standard PUT create-or-replace semantics, paired with the backend change InsForge/InsForge#1760.

What was ported

  • upload(path:data:options:) / upload(path:fileURL:options:) — uploading to a key that already exists now replaces the object in place (previously the server silently auto-renamed, e.g. photo.pngphoto (1).png). This is a server-side behavior change; method signatures are unchanged. Doc comments updated to state the new semantics (mirrors the JS SDK, which only updated docs on upload).
  • upload(data:fileName:options:) (the Swift equivalent of JS uploadAuto) — now mints a unique, collision-free key client-side (<sanitized-base>-<timestamp>-<random><ext>, base sanitized to [a-zA-Z0-9-_], truncated to 32 chars, file fallback — same algorithm as the JS generateObjectKey) and delegates to the standard upload(path:data:options:) path. Without this, the raw filename would be treated as the literal key by the new backend and repeated auto-key uploads would silently overwrite each other.
  • Tests — new offline StorageObjectKeyTests class (7 tests: ext/base preservation, sanitization, 32-char truncation, no-extension, dotfile, empty-name fallback, uniqueness), placed in a separate class so it runs in the deterministic scripts/test-unit.sh suite (the existing InsForgeStorageTests class is live-integration and skipped in CI).
  • Docs — CHANGELOG [Unreleased] entry; README storage examples annotated with the new semantics.

What was skipped and why

  • JS storage.test.ts mock-fetch tests for getPublicUrl / createSignedUrl / createSignedUrls and the legacy-POST fallback — these cover JS-SDK methods that have no Swift counterpart and pre-existing JS behavior, not part of the v1.5.0 API contract change.
  • package.json / lockfile / CI workflow / SDK-REFERENCE plumbing — JS-repo concerns.
  • Version bump — this repo's version is bumped by scripts/release.sh at release time, so the changelog entry sits under [Unreleased].

Test results

  • swift buildpasses (Swift 6.3.3, macOS arm64).
  • ./scripts/test-unit.sh (XCTest) — could not run locally: this machine has only CommandLineTools (no Xcode), so the XCTest module is unavailable. This is a pre-existing toolchain limitation, not caused by these changes; the repo's CI (macos-14 + full Xcode) will run the suite.
  • As a substitute, the new test file was parse-checked with swiftc -parse, and a standalone harness was compiled against the actual built InsForgeStorage module (@testable import) exercising generateObjectKey with all 7 assertions from the new test class — all pass (e.g. report.pdfreport-1784608265685-hfp8nn.pdf, matching the JS /^report-\d+-[a-z0-9]+\.pdf$/ contract).
  • SwiftLint not installed locally; changes follow the repo's .swiftlint.yml conventions.

Notes

🤖 Generated with Claude Code


Summary by cubic

Syncs storage behavior with InsForge-sdk-js v1.5.0. Uploads now use standard PUT (create-or-replace), and auto-key uploads mint a unique client-side key to avoid overwrites. Requires backend with standard PUT storage (InsForge/InsForge#1760).

  • Refactors

    • upload(path:data:...) and upload(path:fileURL:...) now replace existing objects in place (previously auto-renamed).
    • upload(data:fileName:...) now generates a unique key client-side (<sanitized-base>-<timestamp>-<random><ext>) and delegates to path-based upload.
    • Added offline unit tests for key generation; updated README and CHANGELOG.
  • Migration

Written for commit f2854f6. Summary will update on new commits.

Review in cubic

… SDK v1.5.0)

Port InsForge/InsForge-sdk-js v1.5.0 storage changes (paired with the
backend change InsForge/InsForge#1760):

- upload(path:data:options:) / upload(path:fileURL:options:): uploading
  to an existing key now replaces the object in place (previously the
  server silently auto-renamed, e.g. photo.png -> photo (1).png).
  Signatures unchanged; docs updated.
- upload(data:fileName:options:): now mints a unique, collision-free
  key client-side (sanitized base + timestamp + random suffix) and
  delegates to the standard upload(path:data:options:) path, so
  repeated uploads of the same file never overwrite each other. The
  backend no longer mints keys server-side.
- Add offline unit tests for key generation in a new
  StorageObjectKeyTests class so they run in the deterministic CI
  suite (InsForgeStorageTests remains skipped as live-integration).
- Update CHANGELOG (Unreleased) and README storage examples.

Requires an InsForge backend that includes InsForge/InsForge#1760.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@jwfing jwfing left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Review: Sync with InsForge-sdk-js v1.5.0

Summary: A faithful, well-tested port of the JS SDK v1.5.0 storage change (standard PUT create-or-replace + client-side collision-free key minting for auto-upload); no blocking issues found.

Requirements context

No /docs/superpowers/ (or docs/specs/) directory exists in this repo — assessing against the PR description, the linked JS release (InsForge-sdk-js v1.5.0), and the backend contract (InsForge/InsForge#1760). I fetched the actual JS reference implementation (src/modules/storage.ts @ v1.5.0) to verify port parity directly rather than taking the parity claim on faith.


Findings

Critical

(none)

Suggestion

(none blocking — see Information.)

Information

Functionality / parity — algorithm matches JS byte-for-byte (confirmed). I diffed generateObjectKey (Sources/Storage/StorageClient.swift:375-403) against the JS generateObjectKey at v1.5.0. All branches line up:

  • hasExt (JS dotIndex > 0) ≡ Swift if let dotIndex …, dotIndex != filename.startIndex — leading-dot/dotfile handling identical.
  • base sanitize [^a-zA-Z0-9-_] → '-', slice(0,32) / .prefix(32), and || 'file' empty-fallback all match (per-char map means map-then-truncate ≡ truncate-then-map).
  • timestamp = ms epoch in both. Good parity; nothing to change.

Functionality — extension is copied verbatim, not sanitized or length-bounded (StorageClient.swift:378-379, 402). A crafted fileName (e.g. y.png/../a or a very long post-dot segment) injects /, whitespace, or unbounded length into the object key via ext. This is intentional parity — JS v1.5.0 does the exact same (ext = filename.slice(dotIndex)), so deviating here would break the sync and is correctly out of scope for this PR. Flagging only as a latent cross-SDK robustness note worth tracking upstream in the JS SDK first; the impact is low (object-storage key namespacing, not filesystem traversal), and the auto-key path's contract is collision-avoidance, not access control.

Software engineering — test coverage is solid and actually runs in CI. StorageObjectKeyTests (Tests/InsForgeStorageTests/StorageObjectKeyTests.swift) is a separate class from the live-integration InsForgeStorageTests, which is the class scripts/test-unit.sh skips — so the 7 new offline tests execute deterministically. @testable import InsForgeStorage correctly reaches the static internal generateObjectKey, and the target wiring in Package.swift:149-152 picks the file up. Coverage hits the meaningful edges (ext/base preservation, sanitization, 32-char truncation, no-ext, dotfile, empty→file, uniqueness). Regex assertions verified correct by hand (e.g. my photo (1)my-photo--1- → matches ^my-photo--1--\d+…).

Functionality — auto-upload consolidation is clean. upload(data:fileName:options:) (StorageClient.swift:485-495) now delegates to upload(path:data:options:), which already handles both the confirmRequired and direct-upload/list fallback paths — so the removed bespoke logic is not a regression, and no methods were orphaned (getUploadStrategy/uploadToStrategy/confirmUpload/list remain used). Content-type inference is preserved: the generated key keeps the original extension, so inferContentType(from: path) (StorageClient.swift:1011-1012, keyed on pathExtension) yields the same result as the old inferContentType(from: fileName).

Software engineering — minor random-suffix divergence (cosmetic). Swift emits exactly 6 chars from [a-z0-9]; JS Math.random().toString(36).slice(2,8) can emit fewer than 6 (trailing-zero stripping). Both satisfy the [a-z0-9]+ contract and both are non-cryptographic; collision probability is negligible given the ms timestamp prefix. No action needed.

Security — no regressions. No new secret logging (debug logs only echo the path/key), no auth/authorization change, no new dependencies. The behavior change is server-driven (backend #1760); the SDK diff is client-side key derivation only.

Performance — no concerns. generateObjectKey is O(len(filename)); no new network round-trips (in fact the auto path is now a single delegated upload call rather than a separate strategy fetch).


Verdict: approved (informational)

Zero Critical findings. Faithful parity with JS v1.5.0 (verified against source), tests present and CI-visible, scope discipline is good (JS-only concerns explicitly skipped and justified). Posting as a comment per bot policy — the green-checkmark approval remains a separate human action.

One caveat to double-check before/at release, already called out in the PR body: this SDK now hard-requires a backend carrying InsForge/InsForge#1760 (standard-PUT semantics); running against a pre-change backend will silently auto-rename keys, defeating the create-or-replace contract.

@jwfing jwfing left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM - approved.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

No issues found across 4 files

Re-trigger cubic

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.

2 participants