> ## Documentation Index
> Fetch the complete documentation index at: https://cindel.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Cindel Backup: Export, Import, and Low-Level Helpers

> Export a Cindel database to a JSONL archive with CindelBackup, restore it into an empty database, and use documentIdsPage for large-collection tooling.

Cindel provides `CindelBackup` for exporting and importing full typed database archives, plus a set of low-level helpers for maintenance flows and tooling. Normal app reads and writes should always go through the generated typed collections (`db.users`, `db.todos`, etc.) — the APIs on this page are for archival, migration tooling, and controlled maintenance scenarios.

## `CindelBackup`

`CindelBackup` streams a full archive of your database as a JSONL file. The archive includes a header, schema records, document records for every collection you specify, and a footer with a document count and checksum. On native Dart the archive is gzip-compressed by default; pass `CindelBackupCompression.none` for Web or anywhere you need uncompressed JSONL bytes.

### Exporting a Database

Pass your database instance, a list of `CindelBackupCollection` entries (one per schema), and an output `StreamConsumer<List<int>>` such as a file sink:

```dart theme={null}
final report = await CindelBackup.exportDatabase(
  database: db,
  collections: [
    CindelBackupCollection(UserSchema),
    CindelBackupCollection(OrderSchema),
  ],
  output: File('backup.cindelbak').openWrite(),
);

print('Exported ${report.documentCount} documents');
print('Archive size: ${report.archiveSizeBytes} bytes');
```

For Web or any context that requires uncompressed output, set `compression` explicitly:

```dart theme={null}
final report = await CindelBackup.exportDatabase(
  database: db,
  collections: [
    CindelBackupCollection(UserSchema),
  ],
  compression: CindelBackupCompression.none,
  output: output,
);
```

The returned `CindelBackupReport` carries:

| Field              | Description                              |
| ------------------ | ---------------------------------------- |
| `documentCount`    | Total number of exported documents       |
| `archiveSizeBytes` | Size of the written archive in bytes     |
| `checksum`         | Integrity checksum of the archive        |
| `compression`      | Compression mode used (`gzip` or `none`) |

### Importing a Database

Use `CindelBackup.importDatabase` to restore an archive into an empty database. Open the target database with the same schemas that were used during export, then pass the archive as a `Stream<List<int>>`:

```dart theme={null}
final restoredDb = await Cindel.open(
  directory: restoredDirectory.path,
  schemas: [UserSchema, OrderSchema],
);

await CindelBackup.importDatabase(
  database: restoredDb,
  collections: [
    CindelBackupCollection(UserSchema),
    CindelBackupCollection(OrderSchema),
  ],
  input: File('backup.cindelbak').openRead(),
);
```

<Warning>
  Always import into an **empty** database. Importing into a database that already contains data is not supported and will produce unpredictable results. Open a fresh database in a new directory before calling `importDatabase`.
</Warning>

### Supporting Types

| Type                     | Purpose                                                                           |
| ------------------------ | --------------------------------------------------------------------------------- |
| `CindelBackupCollection` | Wraps a generated schema to keep typing intact during backup                      |
| `CindelBackupProgress`   | Reports the current phase, collection, and document count during a long operation |
| `CindelBackupReport`     | Final summary returned by `exportDatabase`                                        |

## Low-Level Helpers

The following helpers are intended for tooling, maintenance services, and migration code. Keep them out of normal application feature code.

### `documentIds`

Returns every id in a collection, ordered ascending:

```dart theme={null}
final ids = await db.documentIds('todos');
final todos = await db.todos.getAll(ids);
```

Use this only when the collection is known to be small and bounded. For collections of unknown or potentially large size, use `documentIdsPage` instead.

### `documentIdsPage`

Returns a bounded page of ids, ordered ascending, starting after an optional cursor id:

```dart theme={null}
int? afterId;

while (true) {
  final ids = await db.documentIdsPage(
    'todos',
    afterId: afterId,
    limit: 1000,
  );

  if (ids.isEmpty) {
    break;
  }

  final todos = await db.todos.getAll(ids);
  // Export, verify, or process this page.

  afterId = ids.last;
}
```

`afterId` is an exclusive cursor — pass the last id from the previous page to advance. `limit` must be greater than zero. `documentIdsPage` works identically across SQLite native, MDBX, and Web SQLite backends.

Prefer `documentIdsPage` over `documentIds` whenever the collection size is unknown or potentially large.

### `allocateId`

Allocates the next id for a collection without writing a document:

```dart theme={null}
final id = await db.allocateId('todos');
```

You rarely need this directly — typed `put` and `putAll` allocate ids automatically when a field is marked `autoIncrement`. Use `allocateId` only in tooling that needs to reserve ids before constructing documents.

Always pass the persisted collection name (the string identifier, not the Dart class name):

```dart theme={null}
final id = await db.allocateId('users'); // ✅ persisted name
```

### `compact`

Asks the storage backend to reclaim space freed by previous maintenance work, such as a completed migration. `compact` must be called inside a `writeTxn`:

```dart theme={null}
await db.writeTxn(() async {
  await db.compact();
});
```

<Note>
  For post-migration compaction, set `compactOnSuccess: true` on your `CindelMigrationPlan` instead of calling `compact()` directly. That option handles compaction automatically after a successful migration without any extra code.
</Note>

Call `compact` directly only from maintenance code that already controls when cleanup is safe.

## Practical Guidance

* **Use generated typed collections for app features.** `db.todos.all().findAll()` is faster to write, easier to read, and benefits from query compilation and indexing.
* **Use `documentIdsPage` over `documentIds`** whenever you don't control the upper bound of collection size. Fetching millions of ids at once can stall the UI or exhaust memory.
* **Use persisted collection names** — the string identifiers — when calling any low-level helper. These are stable across renames of your Dart classes.
* **Isolate advanced helpers.** Keep `documentIdsPage`, `allocateId`, and `compact` inside dedicated tooling, backup, or maintenance services. Normal feature code should never call them directly.
