# Migration Guide

## Upgrading v2 → v3.0.0

v3 narrows the plugin to a single, well-supported backend contract: the
hosted Native Update SaaS at `https://nativeupdate.aoneahsan.com`
(reference implementation: Laravel 11 + Nova 5 in `backend/`). The
direct-Firestore branch — `backendType: 'firestore'` plus the
`firestore: { … }` config — is removed, along with the
`src/firestore/` schema/client/manifest-reader/rules/indexes and the
`example-apps/firebase-backend/` example.

### Why

The Firestore branch existed so apps could run without any server. In
practice it pushed a lot of operational work (security rules, indexes,
billing, Drive token storage) onto every consumer. The SaaS handles all
of it server-side, so the SDK only needs to know how to call one
backend.

### Required action

#### 1. Drop `backendType` and `firestore` from your config

```diff
 await NativeUpdate.initialize({
-  backendType: 'firestore',
-  firestore: { projectId: '…', appId: 'com.your.app', channel: 'production' },
+  serverUrl: 'https://nativeupdatebe.aoneahsan.com/api',
+  apiKey:    import.meta.env.VITE_NATIVE_UPDATE_API_KEY,
+  publicKey: import.meta.env.VITE_NATIVE_UPDATE_PUBLIC_KEY,
   appId:     'com.your.app',
   channel:   'production',
 });
```

If you were running your own Firestore project for manifests, register
your app in the Native Update dashboard, generate an API key, and
switch to the HTTP config above. The dashboard mirrors every field
that previously lived in your Firestore manifest.

#### 2. Delete any direct Firestore reads from your app code

```diff
- import { FirestoreConfig } from 'native-update';
```

The plugin no longer exposes `FirestoreConfig`, `firestore-client`, or
`manifest-reader`. Update checks happen via
`NativeUpdate.checkForUpdate()` / `NativeUpdate.sync()`, which now
always speak HTTP to your configured `serverUrl`.

#### 3. Replace `firestore/schema` type imports

If you were importing rollout / device / update-check types, switch to
the new backend-neutral exports:

```diff
- import type {
-   RolloutConfig,
-   RolloutTargetSegments,
-   DeviceInfo,
-   UpdateCheckResponse,
- } from 'native-update/firestore';
+ import type {
+   RolloutConfig,
+   RolloutTargetSegments,
+   DeviceInfo,
+   UpdateCheckResponse,
+ } from 'native-update';
```

`FirestoreTimestamp` is replaced by `RolloutTimestamp` (`number |
string | Date`); a `toEpochMs()` helper handles all three forms.

### Not affected

- Bundle download, verification, install, rollback, boot-time
  re-verify, and crash-loop auto-rollback are unchanged.
- Native Android / iOS plugin code is unchanged.
- App store update checks (`AppUpdate`) and in-app review prompts
  (`AppReview`) are unchanged.

---

## Upgrading v1 → v2.0.0

v2 is a security-focused hardening release with a handful of breaking
changes. Every change is either (a) a credential-hardening tightening
that removes a silent-pass path, or (b) a feature removal for an option
that was documented but never actually implemented. Typical integrations
need only 2–3 config edits.

### Required action

#### 1. Re-pass `apiKey` / `publicKey` on every `initialize()`

v1 persisted config (including `apiKey` and `publicKey`) to
`localStorage` / `SharedPreferences` / `UserDefaults`. v2 strips these
fields before persistence on every platform. Your app must provide them
on every `initialize()` call instead of relying on the cached copy.

```ts
await NativeUpdate.initialize({
  serverUrl: import.meta.env.VITE_NATIVE_UPDATE_API_URL,
  apiKey:    import.meta.env.VITE_NATIVE_UPDATE_API_KEY,
  publicKey: import.meta.env.VITE_NATIVE_UPDATE_PUBLIC_KEY,
  appId:     'com.your.app',
  channel:   'production',
});
```

If you relied on a "call initialize once, then continue" pattern without
re-passing the key, sync + download calls will fail with
"API key not configured" after the first cold start.

#### 2. Remove `enableEncryption`, `encryptionKey`, `encryptionSalt`

These options claimed AES-256-GCM bundle encryption but were only wired
on the web platform and never on Android/iOS — they shipped a false
guarantee. v2 removes them from the public API.

#### 3. Remove `allowDowngrade` from `UpdateOptions`

Downgrades are a debug / recovery operation, not a production flag.
v2 removes the option and always blocks downgrades against the
persisted high-water mark per channel.

#### 4. Stop calling `setUpdateUrl()` at runtime

`setUpdateUrl()` is deprecated and now a no-op. Server URL is fixed at
`initialize()` to close an XSS-repoint attack path. Re-call
`initialize()` with the new config if you legitimately need to switch
servers.

#### 5. iOS host-app Info.plist for background updates

If you use `enableBackgroundUpdates` on iOS, add to your app's own
`Info.plist`:

```xml
<key>UIBackgroundModes</key>
<array><string>background-app-refresh</string></array>
<key>BGTaskSchedulerPermittedIdentifiers</key>
<array><string>com.aoneahsan.nativeupdate.background</string></array>
```

Without these, iOS silently refuses to register the background task.
v2 surfaces an actionable error in `backgroundUpdateStatus.lastError`.

### Behaviour that changed (no action needed)

- Signature verification fails closed when a `publicKey` is configured.
- Active bundle is re-hashed + re-signature-verified on every cold start.
- 2 failed cold starts without `notifyAppReady()` trigger auto-rollback.
- 3 most recent bundles kept by default (pass `cleanupOldBundles: false`
  to opt out).
- Jittered exponential backoff on retries.
- Android WorkManager background jobs cap at 5 retries.

### Backend changes (if self-hosting)

- Stripe → PayPal swap via `srmklive/paypal`. Run `composer update`,
  apply the new migration, set `PAYPAL_*` env vars. See
  `/docs/deployment/HOSTINGER_DEPLOY.md`.
- Signed URL TTL moved 5min → 30min, configurable via
  `NATIVE_UPDATE_DOWNLOAD_TTL_MIN`.
- Device IDs HMAC-SHA256 hashed at DB write. New rows only — existing
  rows are not backfilled.

---

## Migrating from Other Update Solutions

### From Capacitor Live Updates (Official)

If you're migrating from the official Capacitor Live Updates plugin:

1. **Update imports**:

```typescript
// Before
import { LiveUpdates } from '@capacitor/live-updates';

// After
import { NativeUpdate } from 'native-update';
```

2. **Update configuration**:

```typescript
// Before
await LiveUpdates.configure({
  appId: 'your-app-id',
  channel: 'production',
  autoUpdateMethod: 'background',
});

// After
await NativeUpdate.configure({
  liveUpdate: {
    appId: 'your-app-id',
    serverUrl: 'https://your-server.com',
    channel: 'production',
    updateStrategy: 'background',
    publicKey: 'your-public-key',
  },
});
```

3. **Update method calls**:

```typescript
// Before
const result = await LiveUpdates.sync();

// After
const result = await NativeUpdate.sync();
```

### From Capgo Capacitor Updater

If you're migrating from @capgo/capacitor-updater:

1. **Bundle management differences**:
   - Our plugin uses a different bundle storage mechanism
   - You'll need to re-download any active bundles
   - Bundle IDs are generated differently

2. **API differences**:

```typescript
// Capgo
import { CapacitorUpdater } from '@capgo/capacitor-updater';
await CapacitorUpdater.download({ url, version });

// Capacitor Native Update
import { NativeUpdate } from 'native-update';
await NativeUpdate.download({
  url,
  version,
  checksum: 'required-checksum',
});
```

3. **Security enhancements**:
   - Checksum is now required for all downloads
   - HTTPS is enforced by default
   - Public key verification is built-in

### From Ionic Appflow

If you're migrating from Ionic Appflow:

1. **Self-hosted server required**:
   - Unlike Appflow, this plugin requires your own update server
   - See our server implementation guide for details

2. **Channel management**:

```typescript
// Similar channel concept
await NativeUpdate.setChannel('production');
```

3. **Additional features**:
   - Native app update checks
   - In-app review integration
   - More granular security controls

## Breaking Changes

### Version 1.0.0

This is the initial release, but here are key differences from similar plugins:

1. **Required parameters**:
   - `checksum` is required for all bundle downloads
   - `serverUrl` must use HTTPS (unless explicitly disabled)

2. **Security by default**:
   - HTTPS enforcement is on by default
   - Signature verification is recommended
   - Input validation cannot be disabled

3. **Unified API**:
   - All update types (live, native, reviews) in one plugin
   - Single configuration object
   - Consistent error handling

## Data Migration

If you have existing bundles from another solution:

```typescript
// Clear old data and start fresh
await NativeUpdate.reset();

// Or manually migrate bundles
const oldBundles = getOldBundles(); // Your migration logic
for (const bundle of oldBundles) {
  // Re-download with new security requirements
  await NativeUpdate.download({
    url: bundle.url,
    version: bundle.version,
    checksum: await calculateChecksum(bundle.url),
  });
}
```

## Configuration Migration

### Old Configuration Patterns

```typescript
// CodePush style
codePush.sync({
  deploymentKey: 'key',
  installMode: codePush.InstallMode.IMMEDIATE,
});

// Convert to:
await NativeUpdate.sync({
  updateMode: 'immediate',
});
```

### Environment-Specific Config

```typescript
const config = {
  liveUpdate: {
    serverUrl: process.env.UPDATE_SERVER_URL,
    channel: process.env.UPDATE_CHANNEL || 'production',
    publicKey: process.env.UPDATE_PUBLIC_KEY,
  },
};

await NativeUpdate.configure(config);
```

## Troubleshooting Migration

### Common Issues

1. **"Checksum required" error**:
   - Calculate SHA-256 checksum of your bundles
   - Include in download request

2. **"HTTPS required" error**:
   - Update server URLs to use HTTPS
   - Or disable enforcement (not recommended)

3. **Bundle compatibility**:
   - Bundles from other systems won't work directly
   - Re-package and sign your bundles

### Getting Help

- Check our example app in the `/example` directory for implementation patterns
- Review the [API documentation](https://nativeupdate.aoneahsan.com/docs/api/live-update-api)
- File issues on GitHub for migration problems
