Skip to content

SK-3053: Split SDK into skyflow-android-sdk (v1) + skyflow-flowvault-android-sdk (v2) sharing a common core- #194 - #195

Merged
saileshwar-skyflow merged 12 commits into
release/26.8.0from
saileshwar/SK-3053-package-split
Aug 17, 2026
Merged

SK-3053: Split SDK into skyflow-android-sdk (v1) + skyflow-flowvault-android-sdk (v2) sharing a common core- #194#195
saileshwar-skyflow merged 12 commits into
release/26.8.0from
saileshwar/SK-3053-package-split

Conversation

@saileshwar-skyflow

@saileshwar-skyflow saileshwar-skyflow commented Aug 14, 2026

Copy link
Copy Markdown
Collaborator

What & why

Splits the single skyflow-android codebase into two independently publishable AAR SDKs that share one common core, with the variant fixed by which artifact an app installs — no build flags, no runtime switch.

Gradle module Published artifact Contract Sourced from
:skyvault skyflow-android-sdk (unchanged) v1 — per-token redaction, /v1/vaults/, untyped Callback, table/fields/skyflow_id main (1.27.0)
:flowvault skyflow-flowvault-android-sdk (new, 1.0.0) v2tokenGroupRedactions, /v2/records|tokens/, typed callbacks, tableName/tokens/skyflowId beta-release/26.7.0 (1.28.0-beta.1)
common/ (shared source folder, not a module) compiled into each AAR contract-agnostic core + base types

common/ is a plain source folder added to both modules' source sets, so Kotlin internal works natively (no @RestrictTo leakage) and each AAR is self-contained. FlowVault is implemented by extending shared base types (BaseSkyflowClient, BaseConfiguration, BaseCollectElementInput, BaseRevealElementInput), not by branching inside shared code.

Backward compatibility: the legacy v1 public surface is unchanged (branch cut from main; Configuration keeps its /v1/vaults/ suffix via subclass; v1 wire keys preserved).

Key mechanics

  • base+extend for contract-divergent types; typealias reconciles the error name (SkyflowError = core SkyflowInternalError in v1; a typed data class in v2).
  • JWTUtils promoted to common/core; neutral okHttpClient superset on BaseConfiguration; Element.skyflowId read alias.
  • Per-product versioning (bump_version.sh <legacy|flowvault> …), per-product release workflows keyed to tag namespaces (x.y.z / *-beta.* → skyvault; flowvault/* → flowvault), PR CI builds+tests both.

Verification (all green)

  • :skyvault + :flowvault compile, lint, test, and assembleRelease (runOnGitHub → BUILD SUCCESSFUL).
  • skyvault v1 suite green; flowvault 93-test v2 suite green.
  • publishToMavenLocalskyflow-android-sdk:1.27.0 and skyflow-flowvault-android-sdk:1.0.0; flowvault POM has no common module dependency (shared source compiled into the AAR).
  • v1 and v2 source-file coverage audited — zero files unaccounted for; old Skyflow/ module removed with no stale references.

Notes / deliberate deviations

  • Boundary guard = the dual-module compile (common shares the Skyflow package with each layer, so a leak is an unqualified reference, not an import). CI builds both modules, so any violation breaks the build. A Konsist AST test is left as an optional future enhancement rather than adding a network dependency.
  • samples/ remains the v1 reference app (points at :skyvault, standalone as on main). No dedicated FlowVault sample in this PR.

saileshwar-skyflow and others added 12 commits August 10, 2026 23:18
…les — foundation

Restructure the single SDK into two independently publishable modules sharing a
contract-agnostic core, per docs/sdk-split-plan.md. Published artifact ids are
unchanged (skyflow-android-sdk; new skyflow-flowvault-android-sdk).

- Rename module Skyflow -> skyvault (legacy/v1); add flowvault module (v1.0.0)
- Extract shared sources into common/ (compiled into each module via source set,
  so Kotlin `internal` visibility is preserved without a separate :core module)
- Container marker classes -> common; per-product container ops stay per-module
- Base+extend for divergent types: BaseSkyflowClient / BaseConfiguration /
  BaseCollectElementInput / BaseRevealElementInput in common; legacy subclasses
  keep v1 shapes (table / skyflowID / redaction / /v1/vaults/ URL)
- Error model: common SkyflowInternalError (exception) + legacy typealias
  SkyflowError (flowvault will bind SkyflowError to its v2 data class)
- SdkInfo identity in common, stamped by each product's init()
- Split Utils: neutral helpers -> common; v1-only helpers -> LegacyUtils
- Add docs/sdk-split-plan.md

Verified: skyvault v1 test suite passes; common compiles standalone inside the
flowvault module (proving self-containment). FlowVault v2 port is in progress.

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

Builds on the split foundation to finish the two-SDK architecture from one codebase:

FlowVault (v2) module — port the beta v2 contract onto shared common/:
- base+extend subclasses: CollectElementInput(tableName/skyflowId),
  RevealElementInput (no redaction), Configuration (no /v1/vaults/ suffix),
  Client (wires FlowDBAPIClient, no standalone client methods), Init (stamps SdkInfo)
- v2 types + FlowDB* API layer, MockCVV, typed callbacks, container extensions
  (client.apiClient reached via (client as Client) since Container.client is BaseSkyflowClient)
- 93-test v2 suite green (MockCVV/Response/Composable/InputFormatting/Utils/Validation)

Common (kept contract-neutral):
- promote JWTUtils to common/core (shared by both api clients)
- add neutral okHttpClient superset to BaseConfiguration; add Element.skyflowId read alias

Versioning + CI:
- bump_version.sh takes a leading product arg (legacy|flowvault)
- runOnGitHub + pr.yml build/test both modules
- legacy release workflows retargeted to :skyvault; new mirrored flowvault_* workflows
  keyed to flowvault/* tags

Both modules compile, lint, test, assembleRelease, and publishToMavenLocal
(skyflow-android-sdk:1.27.0, skyflow-flowvault-android-sdk:1.0.0); flowvault POM
carries no common module dependency. v1/v2 source coverage verified complete.
docs/sdk-split-plan.md finalized to match the implementation.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Pure file relocation (git mv, 58 files) across common/skyvault/flowvault: move the
files that sat flat in src/main/kotlin/Skyflow/ into logical subfolders — client/,
config/, container/, element/, options/, response/, error/, logging/, auth/, style/,
connection/.

No content changes: every file keeps its `package Skyflow` declaration, so
fully-qualified names, consumer imports, samples, and the published AAR API are all
unchanged (all 58 changes are 100%-similarity renames). Kotlin allows package != folder,
so kotlinc/Gradle/CI are unaffected; only the IDE shows a cosmetic folder-vs-package hint.

Pre-existing package anomalies (com.Skyflow.* validations, ContainerProtocol, State.kt),
test sources, and build/CI config are intentionally left untouched.

Verified: both modules compile, lint, test (skyvault v1 + flowvault 93-test v2), and
assembleRelease — all green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
APIClient (v1) and FlowDBAPIClient (v2) had byte-identical token handling
(isValidToken + getAccessToken) — the only difference was SkyflowError vs
SkyflowInternalError, which are the same type (skyvault aliases SkyflowError to
SkyflowInternalError). Pull that lifecycle into a new internal
common/core/BaseApiClient; both api clients now extend it and keep only their
contract-specific request methods and endpoints.

Same base-class pattern as BaseSkyflowClient/BaseConfiguration. Both api clients
are internal, so no public API change; behavior is unchanged.

Verified: both modules compile; skyvault v1 suite + flowvault 93-test v2 suite green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…or binary compat)

Previously the thrown exception was defined once in common as SkyflowInternalError,
with skyvault aliasing `SkyflowError` onto it. That changed the legacy artifact's JVM
class from `Skyflow.SkyflowError` (1.27.0) to `Skyflow.SkyflowInternalError` — a source
break for Java consumers and a binary break for precompiled/reflection consumers.

Now each product defines its own error class; common only *references* the neutral
throw-name `SkyflowInternalError` (shared source, resolved per module):
- common: no longer defines the exception (deleted SkyflowInternalError.kt).
- skyvault: restores the real v1 `class SkyflowError` (byte-identical to 1.27.0) and adds
  `internal typealias SkyflowInternalError = SkyflowError`, so common's throws produce real
  `Skyflow.SkyflowError` instances again.
- flowvault: adds its own `internal class SkyflowInternalError` (plumbing only) and keeps the
  public `SkyflowError` data class unchanged.

Verified against the clean AARs: skyvault exposes `Skyflow.SkyflowError` (no SkyflowInternalError
class); flowvault has the SkyflowError data class + internal SkyflowInternalError. Both modules
compile, assemble, and pass tests (skyvault v1 + flowvault 93-test v2).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ta/internal)

Collapse the six release workflows into three. Each now triggers on both products'
refs and derives the product from a single "Determine product" step:
- release.yml         : tags x.y.z (legacy) | flowvault/x.y.z (flowvault)
- beta_release.yml    : tags *.*.*-beta.* | flowvault/*-beta.*
- internal_release.yml: branches release/* | flowvault-release/*

The step sets product / module / version / gradle_file / label as step outputs; the
shared bump / commit / publish steps read them, so the logic lives once. Deleted the
three flowvault_*.yml duplicates. Also modernized the deprecated `::set-output` to
$GITHUB_OUTPUT. Publish routing is unchanged (skyvault vs flowvault by ref namespace).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…client (v1-safe)

Mirror the JS/Java SDK restructure on Android, as far as Kotlin allows, keeping the
v1 (skyvault) public API byte-compatible — every change is additive or internal.

Shared into common (written once):
- ISkyflowClient — public client contract the base implements (analogue of JS ISkyflow).
- initClient<T : BaseSkyflowClient> — the shared init body with a generic return
  (analogue of JS `static init<T>`); each product's `init(Configuration): Client` is a
  one-line delegate that supplies the concrete Client + its BuildConfig.
- BaseCollectContainer / BaseRevealContainer — the byte-identical `create` body
  (createElement / createLabel) and element validation (validateElements/validateElement),
  reused by both products.

Stays per-product (the genuine divergence):
- the public `create(CollectElementInput)` / `create(RevealElementInput)` — thin delegates
  that keep each product's own input type and unchanged public/binary signature.
- collect/reveal/post/get/update — contract-specific (v1 APIClient `/v1/vaults/` vs
  v2 FlowDBAPIClient `/v2/…`).
- the concrete Client + init entry point (Kotlin can't inherit/specialize a static factory).

Also tightened the element-input bases (BaseCollectElementInput/BaseRevealElementInput)
from open to abstract (new in the split, never instantiated directly).

Not done (flagged infeasible in Kotlin): container as an interface / member methods
(ops are extension functions — would break v1 binary), CRTP self-type (no fluent client
API), element inputs as interfaces (they hold state).

Verified: both modules compile, lint, test (skyvault 430 + flowvault 93), assembleRelease;
skyvault public-API surface unchanged except the additive ISkyflowClient superinterface
(Client/Container/SkyflowError/CollectElementInput/Configuration byte-identical).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Cosmetic rename of the shared generic init helper (and the two per-module init
delegates' calls + KDoc references). No behavior or signature change; the public
`init(Configuration): Client` entry points are unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…scope of change

Rework internal_release.yml so one release/* branch (one PR, no separate
flowvault-release branch) publishes both dev SDKs based on what changed vs main —
mirroring the skyflow-js internal workflow:
  - common/ or a shared root (scripts, root build.gradle, settings.gradle, gradle
    wrapper, .github/workflows) changed -> publish BOTH skyvault + flowvault
  - only skyvault/ changed  -> publish skyvault
  - only flowvault/ changed -> publish flowvault

Scope = git diff against merge-base with main (idempotent across re-pushes). So
merging the split PR into release/* (which touches common/) publishes both dev
artifacts in one run. Public/beta workflows are unchanged (still per-product by
tag namespace) — scope logic is internal-only.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- README.md -> slim product index (SkyVault v1 / FlowVault v2) linking each package guide + sample
- skyvault/README.md: v1 guide (moved from root); sample links -> samples/skyvault/
- flowvault/README.md: v2 guide (from beta-release/26.7.0) with install coordinates
  repointed to the NEW separate package (com.skyflowapi.android:skyflow-flowvault-android-sdk:1.0.0)
- samples/skyvault/: v1 sample (-> :skyvault); samples/flowvault/: v2 sample (-> :flowvault, distinct applicationId)
- samples/README.md: two-sample index + shared vault-setup prerequisites

Samples stay reference-only (not wired into settings.gradle), mirroring the iOS layout.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replace RevealRecord.metadata (Map<String, Any?>) with a typed RevealMetadata(tableName, skyflowId),
matching the iOS RevealResponse. Update toJson/fromJson accordingly (fromJson tolerates the raw
'skyflowID' key as well as the normalized 'skyflowId') and add a metadata-parsing test.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
FlowVault is a new package versioned from 1.0.0 that targets the v2 API - it is
not 'SDK v2'. Match the skyflow-js package-split README: distinguish the two SDKs by
vault type + API version (skyflow-android-sdk = PDB vault (v1 API);
skyflow-flowvault-android-sdk = Flow vault (v2 API)) and add the backward-compat guidance.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@saileshwar-skyflow
saileshwar-skyflow changed the base branch from flowvault-release/26.8.0 to release/26.8.0 August 17, 2026 16:57
@saileshwar-skyflow
saileshwar-skyflow merged commit 4073dff into release/26.8.0 Aug 17, 2026
6 checks passed
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