Encrypted Pouch - v0.3.0
    Preparing search index...

    Class EncryptedPouch

    Encrypted document store with change detection and sync capabilities.

    This class provides a simple API for storing encrypted documents in PouchDB with real-time change detection and optional sync to CouchDB servers.

    const db = new PouchDB('myapp');
    const store = new EncryptedPouch(db, 'my-password', {
    onChange: (changes) => {
    changes.forEach(({ table, docs }) => {
    console.log(`${docs.length} docs changed in ${table}`);
    });
    },
    onDelete: (deletions) => console.log('Deleted:', deletions)
    });

    await store.loadAll();
    await store.put('expenses', { _id: 'lunch', amount: 15 });
    const doc = await store.get('expenses', 'lunch');
    Index

    Constructors

    Methods

    • Loads all existing documents from the database and starts change detection.

      This should be called once after creating the EncryptedStore instance. It will decrypt all documents, trigger onChange callbacks (batched by table), and set up real-time change listeners.

      Returns Promise<void>

      If documents fail to decrypt (reported via onError callback)

      const store = new EncryptedPouch(db, 'password', { onChange, onDelete });
      await store.loadAll(); // Loads existing docs and starts listening
    • Creates or updates a document in the specified table.

      If the document has no _id, one will be auto-generated. If the document has an _id and _rev, it will be updated. If the _rev doesn't match the current revision, a conflict error is thrown.

      Parameters

      • table: string

        Document type/table (e.g., "expenses", "tasks")

      • doc: NewDoc

        Document to store. Include _rev for updates.

      Returns Promise<Doc>

      The saved document with _id and _rev populated

      If there's a revision conflict

      // Create new document
      const doc = await store.put('expenses', { amount: 15, desc: 'Lunch' });

      // Update existing document
      const updated = await store.put('expenses', {
      _id: doc._id,
      _rev: doc._rev,
      amount: 20
      });
    • Writes multiple documents to a table in a single bulk operation.

      Documents without an _id get one auto-generated. Documents with an _id are upserted: include _rev to update an existing document, omit it to create a new one (a stale or missing _rev on an existing document surfaces as a write error via PouchListener.onError with kind: "write", while the rest of the batch still succeeds).

      Successful writes flow through the existing changes feed and are reported via PouchListener.onChange, batched per table.

      Parameters

      • table: string

        Document table name

      • docs: NewDoc[]

        Documents to write

      Returns Promise<void>

      await store.putAll('expenses', [
      { _id: 'lunch', amount: 15 },
      { _id: 'dinner', amount: 25 },
      { amount: 5 }, // _id auto-generated
      ]);
    • Retrieves a document by table and ID.

      Parameters

      • table: string

        Document table name

      • id: string

        Document ID within the table

      Returns Promise<Doc | null>

      The decrypted document, or null if not found

      const expense = await store.get('expenses', 'lunch');
      if (expense) {
      console.log(expense.amount);
      }
    • Deletes a document from the specified table.

      Parameters

      • table: string

        Document table name

      • id: string

        Document ID within the table

      Returns Promise<void>

      await store.delete('expenses', 'lunch');
      
    • Deletes all documents from the local database only.

      Automatically disconnects sync first to prevent deletions from propagating to remote. Use this when you want to clear local data without affecting the remote server.

      Returns Promise<void>

      await store.deleteAllLocal(); // Clear local data only
      
    • Deletes all documents locally AND propagates deletions to remote server.

      Waits for sync to complete before returning. The remote connection must be established first with connectRemote().

      Returns Promise<void>

      If sync is not connected

      await store.connectRemote({ url: 'http://localhost:5984/mydb' });
      await store.deleteAllAndSync(); // Delete everything locally and remotely
    • Retrieves all documents, optionally filtered by table.

      Parameters

      • Optionaltable: string

        Optional table name to filter by

      Returns Promise<Doc[]>

      Array of decrypted documents

      const allExpenses = await store.getAll('expenses');
      const allDocs = await store.getAll(); // All tables
    • Export every document (or a subset of tables) as a plaintext, re-loadable BackupDump — decrypted, grouped by table, with _rev stripped. The basis of a full backup; pair it with loadFromJSONBackup to restore.

      Tables are discovered from the stored documents themselves (their table_id ids), so a dump is complete without the caller enumerating table names — the key reason backup belongs in the library. Design documents are skipped. Decryption failures surface via onError (like getAll) and the offending document is omitted.

      Parameters

      • Optionalopts: { tables?: string[] }
        • Optionaltables?: string[]

          Restrict the dump to these tables (default: all).

      Returns Promise<BackupDump>

    • Load a BackupDump into THIS store, which must be empty (a freshly created database). Each table is written in one bulk putAll, then every table's document count is re-read and compared against the dump — a mismatch throws, so a per-document putAll failure can never silently lose data.

      Restore is deliberately "create a fresh database, then load", never "wipe an existing database, then load": deleteAllLocal leaves tombstones, and re-putting a document with the same id but no _rev would 409 against the tombstone. A pristine store makes that whole class of conflict impossible.

      On a thrown count check the store may be left partially populated — the caller should destroy the fresh database rather than reuse it.

      Parameters

      Returns Promise<void>

      if dump.version is newer than this build understands, if the store is non-empty, or if any document fails to write.

    • Permanently delete the underlying database and stop change detection.

      Unlike deleteAllLocal (which tombstones every document, leaving deleted leaves that could conflict with a later re-put), this removes the database outright — no tombstones remain. Use it to discard a throwaway / dry-run database, or to clean up the old database after restoring into a new one. The instance is unusable afterward.

      Returns Promise<void>

    • Check whether password can decrypt an existing database, without opening a persistent store or attaching a change feed — it reads at most one stored document and attempts to decrypt it.

      Returns true if a document decrypts, false if decryption fails (wrong password). A database with no encrypted documents returns true: a passphrase cannot be disproven against zero ciphertext. Intended as a "confirm your passphrase before a destructive action" gate — the caller opens a handle to the current database and passes it in.

      Parameters

      • db: Database
      • password: string
      • Optionaloptions: EncryptedPouchOptions

        Options for configuring the EncryptedPouch

        • OptionalpassphraseMode?: "derive" | "raw"

          Key derivation mode for the passphrase.

          • "derive" (default): Use PBKDF2 with 100k iterations for user passphrases. Recommended for production use. Provides strong protection against brute-force and dictionary attacks. First unlock will take ~50-100ms.

          • "raw": Use SHA-256 only. For pre-derived keys or advanced users who handle key derivation themselves. Allows full control over KDF algorithm, iterations, and progress UI.

          "derive"
          

      Returns Promise<boolean>

    • Connects to a remote CouchDB server for bidirectional sync.

      Parameters

      Returns Promise<void>

      // Continuous sync (live updates)
      await store.connectRemote({
      url: 'http://localhost:5984/mydb',
      live: true,
      retry: true
      });

      // One-time sync only (manual control)
      await store.connectRemote({
      url: 'http://localhost:5984/mydb',
      live: false,
      retry: false
      });
      await store.syncNow(); // Manually trigger sync
    • Disconnects from the remote sync server.

      Stops continuous sync if it was enabled.

      Returns void

    • Trigger an immediate one-time sync with the remote. Requires that connectRemote() has been called first. Returns a promise that resolves when the sync completes.

      Returns Promise<void>

    • Manually resolves a document conflict by choosing the winning version.

      Parameters

      • table: string

        Document table name

      • id: string

        Document ID within the table

      • winningDoc: Doc

        The document version to keep (must include _rev)

      Returns Promise<void>

      // In onConflict callback
      onConflict: async (conflicts) => {
      for (const conflict of conflicts) {
      // Pick the version with the latest timestamp
      const latest = [conflict.winner, ...conflict.losers]
      .sort((a, b) => b.timestamp - a.timestamp)[0];

      await store.resolveConflict(conflict.table, conflict.id, latest);
      }
      }
    • Retrieves conflict information for a document without triggering the callback.

      Parameters

      • table: string

        Document table name

      • id: string

        Document ID within the table

      Returns Promise<ConflictInfo | null>

      Conflict information if conflicts exist, null otherwise

      const conflict = await store.getConflictInfo('expenses', 'lunch');
      if (conflict) {
      console.log('Winner:', conflict.winner);
      console.log('Losers:', conflict.losers);
      // Manually resolve the conflict
      await store.resolveConflict('expenses', 'lunch', conflict.winner);
      }
    • Re-subscribes to the PouchDB changes feed.

      Useful after disconnect/reconnect scenarios or if the change feed needs to be restarted.

      Returns void

      store.reconnect(); // Restart change detection