npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@absolutejs/sync-capacitor

v0.9.3

Published

Transactional Capacitor SQLite local store and native lifecycle adapter for @absolutejs/sync

Readme

@absolutejs/sync-capacitor

Native persistence and lifecycle wiring for @absolutejs/sync applications running in Capacitor. It stores confirmed rows, cursors, installation identity, and the durable mutation outbox in one principal-partitioned SQLite database. It also exposes namespace-scoped collection discovery so finite native workers can resume safe id-keyed pulls without application-authored worker code.

AbsoluteJS provisions this automatically when Mobile, Sync, and Auth are enabled. Version 0.9.3 adds peer support tested against @absolutejs/[email protected] and @absolutejs/[email protected], while retaining the earlier declared peer ranges. Intermediate Devices release lines are not newly certified by this change. Keep the application and this adapter on one compatible Devices pair; do not use package-manager overrides to hide an incompatible peer range.

Direct Capacitor applications can opt in explicitly:

import { createSyncClient } from '@absolutejs/sync/client';
import { lifecycle, network } from '@absolutejs/devices';
import {
	createCapacitorSyncLocalStore,
	createCapacitorSyncProtection,
	installCapacitorSyncLifecycle
} from '@absolutejs/sync-capacitor';

const client = createSyncClient({
	url: 'wss://app.example.com/sync/ws',
	durable: {
		store: createCapacitorSyncLocalStore({
			protection: createCapacitorSyncProtection()
		}),
		namespace: authenticatedPrincipalNamespace
	}
});

const removeLifecycle = await installCapacitorSyncLifecycle({
	client,
	lifecycle,
	network
});

Resume and restored-connectivity events refresh the Auth-backed socket and ask the client to flush within a finite ten-second budget. Explicitly retryable failures obey the client's delivery ceiling; conflicts and permanent failures remain in the principal's dead-letter partition for explicit remediation rather than replaying forever.

Generated mutation conflict policies are captured inside each encrypted outbox record. The TypeScript foreground/headless runner and the Android WorkManager or iOS BGProcessingTask runner therefore make the same bounded decision: manual conflicts become dead letters, server-wins discards the rejected local intent, and client-wins retries the unchanged operation ID up to its declared ceiling. An explicit argument-changing rebase is left to the foreground remediation API and creates a new traceable intent.

The namespace must come from a verified Auth principal, never from an untrusted route or form value. AbsoluteJS derives an opaque namespace from the verified issuer, public client ID, and subject. Signing out locks that partition by removing it from the active runtime; it does not silently destroy offline data. Signing back in as the same verified principal unlocks the same partition.

Installed-data upgrades

storageSchema accepts the same generated SyncLocalStoreSchema or component bundle used by createIndexedDbSyncLocalStore. Before any foreground transaction begins, the adapter migrates every principal's collections, durable mutations, and logical schema marker inside one SQLite transaction. A transform failure or process death rolls back the entire upgrade; a runtime older than the stored schema fails closed.

Migration callbacks are synchronous and deterministic. They may replace or delete persisted records, but cannot change a mutation's stable operation ID. AbsoluteJS will generate and provision the plan for ordinary applications; direct Capacitor integrations can pass it explicitly:

const store = createCapacitorSyncLocalStore({
	storageSchema: {
		version: 2,
		migrations: [
			{
				toVersion: 2,
				migrateCollection(record) {
					return {
						...record,
						rows: record.rows.map((row) => ({
							...(row as object),
							archived: false
						}))
					};
				}
			}
		]
	}
});

Absolute composes the app schema and every installed Sync pack into a deterministic component bundle. SQLite tracks each component independently, keeps removed-pack ledgers as orphan diagnostics, and migrates all records and ledger updates in one transaction.

Protected local data and quotas

AbsoluteJS automatically installs createCapacitorSyncProtection(). It creates one random AES-256-GCM data key, seals that key in the existing iOS Keychain or Android Keystore vault, and writes only authenticated ciphertext envelopes to SQLite. Namespace, record kind, and collection/mutation name are authenticated as associated data. The foreground adapter and both finite native workers use the same versioned format; a missing key, changed identity, or modified record fails closed. Direct Capacitor integrations opt in as shown above.

The generated localData policy also enforces memory-only records, whole-projection expiry, deterministic eviction priority, and a logical per-principal byte ceiling. Pending mutations are never evicted. When protected data declares a memory-only fallback, browsers without an audited key provider remain usable without writing that data to disk.

Managed native background Sync

AbsoluteJS also configures AbsoluteBackgroundSync when the application uses both @absolutejs/auth and @absolutejs/sync. The native worker is deliberately finite: Android WorkManager or iOS BGProcessingTask wakes it, it refreshes a short-lived access token, pushes a bounded durable outbox batch, pulls the foreground client's persisted collection descriptors, commits the response to the same SQLite database, and exits. Foreground/resume Sync remains the correctness path because neither operating system guarantees when background work will run.

The worker does not run application JavaScript and does not expose Capacitor APIs to a background WebView. Its credential and network boundary is fixed:

  • the OAuth refresh token is read from the shared native Keychain/Keystore vault and sent only to the issuer-advertised HTTPS token endpoint;
  • the resulting bearer token, mutation arguments, and collection parameters are sent only to the configured same-origin AbsoluteJS endpoint;
  • redirects fail closed, and response bodies are bounded before parsing;
  • a rotated refresh token is written back to the vault, while returned Sync data is written only to the principal's SQLite partition.

Direct Capacitor applications can configure the worker after resolving a verified principal:

import { configureCapacitorBackgroundSync } from '@absolutejs/sync-capacitor';

await configureCapacitorBackgroundSync({
	issuer: 'https://app.example.com',
	clientId: 'mobile-public-client',
	endpoint: 'https://app.example.com/__absolute/sync/background',
	namespace: authenticatedPrincipalNamespace
});

Call AbsoluteBackgroundSync.clear() on sign-out. On iOS, register AbsoluteBackgroundSyncPlugin.registerBackgroundTask() during application launch and list <bundle-id>.absolutejs.background-sync in BGTaskSchedulerPermittedIdentifiers; the AbsoluteJS mobile CLI owns those generated regions. Android scheduling is registered by the plugin.

Platform notes

  • AbsoluteJS protects record payloads with AES-256-GCM rather than claiming the default SQLite connection is SQLCipher-encrypted. SQLite keys, namespaces, and ordering columns remain metadata; sensitive row and mutation payloads are ciphertext. Review platform encryption-export requirements before shipping.
  • Web/PWA builds should use createIndexedDbSyncLocalStore from @absolutejs/sync/client; they do not need the plugin's WASM/web component.
  • The adapter serializes transactions so an app cannot overlap two explicit transactions on the same Capacitor connection.
  • The native iOS worker currently targets the SQLite plugin's default Documents location. Applications that override CapacitorSQLite.iosDatabaseLocation must keep foreground and background database locations aligned before enabling managed background Sync.

License

Apache-2.0.