nosqueel 1.0.0-alpha copy "nosqueel: ^1.0.0-alpha" to clipboard
nosqueel: ^1.0.0-alpha copied to clipboard

Dart FFI bindings for NoSqueel, a lightweight embedded NoSQL database with queries, transactions, and optional AES-256-GCM encryption.

NoSqueel Dart FFI Bindings #

Dart FFI bindings for the NoSqueel embedded database. The package wraps the C library with idiomatic Dart classes for entities, queries, transactions, and optional encryption.

What this package provides #

  • FFI bindings — generated from nosqueel.h via ffigen
  • NSDatabase — open, close, get, put, wipe, shared multi-isolate handles
  • NSEntity / NSKey — create records with typed properties
  • NSQuery — filtered and ordered queries with Filter, AndFilter, and OrFilter
  • NSTransaction — optimistic transactions with commit and rollback
  • Encryption — optional AES-256-GCM at rest via encryptionKey: on open

The native library must be built separately and placed where the loader can find it (see Building).

Prerequisites #

  • Dart SDK 3.0 or later
  • CMake 3.28 or later
  • A C compiler with C23 support
  • POSIX threads (provided by the system toolchain)

Linux example:

sudo apt-get install cmake build-essential

Building #

From bindings/dart:

chmod +x build.sh
./build.sh

This will:

  1. Configure and build libnosqueel via CMake
  2. Copy the shared library into lib/ (e.g. lib/libnosqueel.so)
  3. Regenerate lib/impl/nosqueel.g.dart with dart run tool/ffigen.dart

On Windows, use build.bat instead.

Option B: repository root build #

From the repo root:

cmake -S . -B build
cmake --build build
cp build/libnosqueel.so bindings/dart/lib/libnosqueel.so   # Linux
dart run bindings/dart/tool/ffigen.dart

Running tests #

Tests load the native library from lib/libnosqueel.so (or platform equivalent). Build the library first, then:

cd bindings/dart
dart pub get
dart test

Adding to your project #

dependencies:
  nosqueel:
    path: path/to/bindings/dart
dart pub get
import 'package:nosqueel/nosqueel.dart';

Quick start #

import 'package:nosqueel/nosqueel.dart';

void main() {
  final db = NSDatabase.open('/path/to/app.nosqueel');

  final key = NSKey.integer('users', 1);
  final user = NSEntity(key);
  user.setProperty('name', 'Ada');
  user.setProperty('score', 42);
  db.put(user);

  final loaded = db.get(key);
  print(loaded?.getProperty<String>('name')); // Ada

  db.close();
}

API overview #

Opening and closing #

// Plain database
final db = NSDatabase.open('/path/to/data.nosqueel');

// Encrypted database (AES-256-GCM, passphrase hashed with SHA-256)
final secure = NSDatabase.open(
  '/path/to/secure.nosqueel',
  encryptionKey: 'my-secret-passphrase',
);

db.wipe();   // clear all data, keep file open
db.close();  // release shared handle; pair each open with one close

NSDatabase.open uses the native ns_database_acquire() registry. Multiple isolates can open the same path and receive the same underlying handle. Each open must be matched with close.

Wrong encryption keys fail to open (they do not return an empty database).

Keys #

final intKey = NSKey.integer('products', 42);
final nameKey = NSKey.name('users', 'alice');

Tables are created implicitly when the first entity is stored. Table and property names follow the limits documented in the root README.

Entities and properties #

Properties are set and read with a single typed API:

entity.setProperty('name', 'Alice');    // String
entity.setProperty('age', 28);          // int
entity.setProperty('rating', 4.5);      // double
entity.setProperty('active', true);     // bool
entity.setProperty('notes', null);      // null
entity.setProperty('tags', <int>[1, 2]); // indexed list (empty list: <int>[])
entity.setProperty('body', NSText('…')); // large opaque payload (up to 1 MiB)

final name = entity.getProperty<String>('name');
final age = entity.getProperty<int>('age');
final tags = entity.getProperty<List<int>>('tags');
final body = entity.getProperty<NSText>('body');
final kind = entity.propertyType('name'); // NSPropertyType
final tagKind = entity.listElementType('tags'); // NSPropertyType.int

Supported property types: int, double, bool, String, typed List values (List<int>, List<double>, List<bool>, List<String>), null, and [NSText]. An empty typed list is distinct from null. Indexed [String] values are capped at [maxIndexableStringBytes] UTF-8 bytes; use [NSText] (or [NSText.bytes]) for larger opaque payloads up to [maxTextBytes]. [NSText] properties are not indexed and cannot be used in query filters.

NSDatabase.get returns an owned NSEntity?. Query results return borrowed entities that are only valid while the NSQueryResults object is alive.

Queries #

final query = db.query('items', orderBy: ['-score', 'name']);

query.addFilter(Filter('score', Op.greaterThanOrEqual, 10));
query.addFilter(Filter('active', Op.equals, true));

final results = query.fetch(limit: 20, offset: 0);
for (final entity in results.entities) {
  print(entity.getProperty<int>('score'));
}
results.dispose(); // optional: release native memory early

final total = query.count();

Filter composition:

  • Consecutive addFilter(Filter(...)) calls are AND-ed on one branch
  • addFilter(AndFilter([...])) adds one OR branch with AND-ed filters
  • addFilter(OrFilter([...])) adds separate OR branches

Available operators (Op): equals, contains, greaterThan, greaterThanOrEqual, lessThan, lessThanOrEqual, overlap, isEmpty.

List properties support contains/equals (value in list), overlap (pass a typed list as the filter value), and isEmpty (pass true or false). Other operators on list properties match nothing.

If a filtered or ordered property lacks a suitable index, fetch() or count() throws NSMissingIndexException. Indexes are created automatically when data is written or when a query succeeds.

limit and offset are applied in Dart after the native query runs.

Transactions #

final txn = db.beginTransaction();

final entity = txn.get(NSKey.integer('items', 1));
if (entity != null) {
  entity.setProperty('score', 200);
  txn.put(entity);
}

txn.commit();   // or txn.rollback()
  • Read-your-writestxn.get sees uncommitted puts and deletes in the same transaction
  • Optimistic locking — updating or deleting an existing entity requires a prior txn.get in that transaction; commit fails if another writer changed a read key
  • Queries inside transactionstxn.query() runs against committed data only; uncommitted writes are not visible to transactional queries
  • Deletestxn.delete(key) is available; there is no non-transactional db.delete() wrapper yet

Multiple transactions may be active on the same database.

Encryption #

Pass encryptionKey when opening:

final db = NSDatabase.open(path, encryptionKey: 'strong-passphrase');

The binding:

  1. Derives a 32-byte AES-256 key with SHA-256 from the UTF-8 passphrase
  2. Registers per-isolate NativeCallable encrypt/decrypt callbacks
  3. Passes key and callbacks to ns_database_acquire() on every open

Each isolate that opens an encrypted database must do so with the correct key. Callbacks are bound to the isolate's thread in native code.

Limitations:

  • Protects data at rest on disk, not in memory while the database is open
  • SHA-256 key derivation has no salt or memory-hard stretching — use a strong passphrase
  • The 32-byte key size is required by AES-256, not an arbitrary cap
  • See the root README encryption section for the full threat model

Package layout #

lib/
  nosqueel.dart           # Public exports
  libnosqueel.so          # Built native library (not in git)
  impl/
    bindings.dart         # Library loader + re-exports generated bindings
    nosqueel.g.dart       # Generated FFI bindings (ffigen)
    ns_database.dart
    ns_entity.dart
    ns_key.dart
    ns_query.dart
    ns_transaction.dart
    ns_filter.dart
    ns_crypto.dart        # AES-GCM callbacks for encryption
tool/
  ffigen.dart             # ffigen configuration
test/                     # Integration tests (require built lib/)

Development #

Regenerate FFI bindings #

After changing nosqueel.h:

cd bindings/dart
dart run tool/ffigen.dart

Rebuild native library after C changes #

./build.sh

Or manually:

cmake --build build
cp build/lib/libnosqueel.so lib/

Troubleshooting #

Cannot load library / ns_database_acquire failed #

  1. Build the native library: ./build.sh
  2. Confirm the file exists: ls -la lib/libnosqueel.so
  3. For tests, ensure you run from bindings/dart so the default search path resolves

NSMissingIndexException #

The query filters or orders on a property that does not yet have an index. Store at least one entity with that property, or simplify the query.

Encryption open failures #

  • Encrypted files require the same encryptionKey used at creation
  • Wrong keys fail at open time with StateError
  • Opening without a key on an encrypted file also fails

CMake or compiler errors #

See docs/BUILD.md for additional build notes.

Platform support #

Platform Status
Linux Flutter FFI plugin + CLI tests via prebuilt lib/libnosqueel.so
macOS Flutter FFI plugin (nosqueel.framework)
Windows Flutter FFI plugin (nosqueel.dll)
Android Flutter FFI plugin (libnosqueel.so)
iOS Flutter FFI plugin (nosqueel.framework)
Web Not supported (dart:ffi unavailable)

This package is a Flutter FFI plugin. In a Flutter app, add it as a dependency and run flutter pub get; Flutter builds and bundles the native library automatically.

For CLI / unit tests on desktop, build the library with ./build.sh (or build.bat) so tests can load lib/libnosqueel.so before falling back to the bundled plugin name.

cd bindings/dart
flutter pub get   # or dart pub get when Flutter SDK is on PATH
./build.sh
dart test

Mobile-specific notes:

Limitations #

  • No NSDatabase.delete() — use NSTransaction.delete() inside a transaction
  • Query limit / offset are Dart-side only
  • Transactional queries do not see uncommitted writes
  • Key-typed properties are not readable from Dart yet (NSPropertyType.key)
  • Some C APIs (direct ns_database_open, memory databases, table iteration) are not wrapped
  • Mobile packaging is documented separately and is more involved than desktop FFI

Further reading #

0
likes
150
points
6
downloads

Documentation

API reference

Publisher

verified publisherakqaleap.com

Weekly Downloads

Dart FFI bindings for NoSqueel, a lightweight embedded NoSQL database with queries, transactions, and optional AES-256-GCM encryption.

Repository (GitLab)
View/report issues

Topics

#database #embedded #ffi #nosql #storage

License

MIT (license)

Dependencies

ffi, flutter, path, pointycastle

More

Packages that depend on nosqueel

Packages that implement nosqueel