@kidus.dev/flowdb 1.0.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/README.md +360 -7
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -1,15 +1,368 @@
1
1
  # flowdb
2
2
 
3
- To install dependencies:
3
+ A lightweight, file-based local database for Dart and Flutter. flowdb stores data on disk as JSON, with collections for structured records, key-value stores, chunked blob storage, backups, and optional encryption. It also includes reactive state (FlowState) and a Flutter widget (FlowBuilder) for stream-driven UIs.
4
4
 
5
- ```bash
6
- bun install
5
+ ---
6
+
7
+ ## Features
8
+
9
+ - **File-based persistence** — no native drivers or SQL; data lives under a database directory on the filesystem
10
+ - **Collections** — document-style records with auto-generated CUID ids, CRUD, and rich querying
11
+ - **Key-value stores** — simple get / set / remove storage scoped to the database
12
+ - **Blob storage** — large binary files stored in configurable chunks with metadata in a collection
13
+ - **Query builder** — filter, sort, paginate, project fields, and run bulk updates/deletes
14
+ - **Relations** — define one-to-one, one-to-many, and many-to-many links between collections
15
+ - **Backups** — snapshot the entire database directory
16
+ - **Optional encryption** — encrypt collection and store payloads at rest
17
+ - **Sync and async APIs** — most operations offer both `foo()` and `fooSync()` variants
18
+ - **Reactive state** — `FlowState<T>` built on RxDart BehaviorSubject
19
+ - **Flutter integration** — `FlowBuilder` connects FlowState streams to widgets
20
+
21
+ ---
22
+
23
+ ## Requirements
24
+
25
+ - Dart SDK `^3.10.7`
26
+ - Flutter `>=1.17.0` (only if you use `package:flowdb/flutter.dart`)
27
+
28
+ ---
29
+
30
+ ## Installation
31
+
32
+ Add flowdb to your `pubspec.yaml`:
33
+
34
+ ```yaml
35
+ dependencies:
36
+ flowdb: ^1.0.0
37
+ ```
38
+
39
+ For Dart-only projects, import the core library:
40
+
41
+ ```dart
42
+ import 'package:flowdb/core.dart';
43
+ ```
44
+
45
+ For Flutter apps that need FlowBuilder, import:
46
+
47
+ ```dart
48
+ import 'package:flowdb/flutter.dart';
49
+ ```
50
+
51
+ ---
52
+
53
+ ## Quick start
54
+
55
+ ```dart
56
+ import 'package:flowdb/core.dart';
57
+
58
+ Future<void> main() async {
59
+ final db = openDatabase('my_app', path: './data/my_app');
60
+
61
+ final users = db.collection('users');
62
+
63
+ final alice = await users.add({'name': 'Alice', 'age': 30});
64
+ print(alice.id); // auto-generated CUID
65
+
66
+ final found = await users.where('name', eq: 'Alice').getFirst();
67
+ print(found?.data);
68
+
69
+ final settings = db.store('settings');
70
+ settings.set('theme', 'dark');
71
+ print(settings.get('theme'));
72
+ }
73
+ ```
74
+
75
+ ---
76
+
77
+ ## Core concepts
78
+
79
+ ### Database
80
+
81
+ Open a database with `openDatabase`. All collections, stores, blobs, and backups live under the database path.
82
+
83
+ ```dart
84
+ final db = openDatabase(
85
+ 'my_app',
86
+ path: './data/my_app', // optional; defaults to the database name
87
+ encrypted: false, // encrypt collection/store file contents
88
+ isWeb: false, // web platform hint
89
+ );
90
+ ```
91
+
92
+ | Method / property | Description |
93
+ |------------------------|-----------------------------------------------|
94
+ | `collection(name)` | Get or create a collection |
95
+ | `store(name)` | Get or create a key-value store |
96
+ | `blobStorage(name)` | Get or create chunked blob storage |
97
+ | `relation(...)` | Define a relation between collections |
98
+ | `backup(name)` | Create a filesystem backup |
99
+ | `reset() / clear()` | Delete and recreate the database directory |
100
+ | `drop()` | Delete the database directory entirely |
101
+ | `size / sizeFormatted` | Total on-disk size |
102
+
103
+ ---
104
+
105
+ ### Collections and records
106
+
107
+ A collection holds records — maps of fields keyed by string, each with a stable id.
108
+
109
+ ```dart
110
+ final posts = db.collection('posts');
111
+
112
+ // Create
113
+ final record = await posts.add({'title': 'Hello', 'published': true});
114
+ final batch = await posts.addMany([
115
+ {'title': 'First'},
116
+ {'title': 'Second'},
117
+ ]);
118
+
119
+ // Read by id
120
+ final one = await posts.getById(record.id);
121
+
122
+ // Read all
123
+ final all = await posts.getAll();
124
+ final total = await posts.count;
125
+ ```
126
+
127
+ Records support nested field access via dot notation:
128
+
129
+ ```dart
130
+ record.set('author.name', 'Alice');
131
+ final authorName = record.get('author.name');
132
+ ```
133
+
134
+ Export a collection as CSV:
135
+
136
+ ```dart
137
+ final csv = await posts.exportAsCSV();
138
+ ```
139
+
140
+ ---
141
+
142
+ ### Querying
143
+
144
+ Chain filters on `collection.where(...)`, `collection.builder`, or the query returned by `where`:
145
+
146
+ ```dart
147
+ final results = await posts
148
+ .where('published', eq: true)
149
+ .and('views', gte: 100)
150
+ .orderBy('title')
151
+ .skip(10)
152
+ .limit(20)
153
+ .get();
154
+
155
+ final first = await posts.where('title', startsWith: 'Hello').getFirst();
156
+ final matching = await posts.where('tags', contains: 'dart').get();
157
+ ```
158
+
159
+ **Comparison operators**
160
+
161
+ | Parameter | Meaning |
162
+ |-------------------------------------------|--------------------------------------|
163
+ | `eq` / `neq` | Equal / not equal |
164
+ | `gt` / `gte` / `lt` / `lte` | Numeric ordering |
165
+ | `contains` / `doesNotContain` | Substring or list membership |
166
+ | `startsWith` / `endsWith` | String prefix / suffix |
167
+ | `regexMatch` | Regular expression |
168
+ | `between`, `betweenStartInclusive`, etc. | Range checks |
169
+ | `isNull` / `isNotNull` | Null checks |
170
+ | `isEmpty` / `isNotEmpty` | Empty string or collection |
171
+ | `filter` | Custom `bool Function(dynamic value)`|
172
+
173
+ **Other query methods**
174
+
175
+ | Method | Description |
176
+ |-------------------|-----------------------------------------------------|
177
+ | `or(...)` | OR condition (after an initial where) |
178
+ | `pick / pickMany` | Include only selected fields |
179
+ | `omit / omitMany` | Exclude fields from results |
180
+ | `orderBy(field, desc: true)` | Sort results |
181
+ | `skip / limit / take` | Pagination |
182
+ | `include / includeMany` | Load related data (when relations configured) |
183
+ | `getShuffled / getRandom / getRandomMany` | Random access |
184
+ | `update / updateMany` | Update matching records |
185
+ | `delete / deleteMany` | Delete matching records |
186
+ | `upsert` | Update if matched, otherwise insert |
187
+ | `addIfAbsent` | Insert only when no match |
188
+
189
+ Sync variants mirror the async API: `getSync()`, `addSync()`, `countSync`, and so on.
190
+
191
+ ---
192
+
193
+ ### Key-value stores
194
+
195
+ Stores hold arbitrary JSON-serializable values under string keys.
196
+
197
+ ```dart
198
+ final cache = db.store('cache');
199
+
200
+ cache.set('lastSync', DateTime.now().toIso8601String());
201
+ cache.setFrom('counter', (v) => (v as int? ?? 0) + 1, 0);
202
+
203
+ if (cache.has('lastSync')) {
204
+ print(cache.get('lastSync'));
205
+ }
206
+
207
+ final all = cache.all(); // Map of all entries
208
+ cache.clear();
209
+ cache.drop(); // delete the store from disk
210
+ ```
211
+
212
+ ---
213
+
214
+ ### Blob storage
215
+
216
+ Store large binary data in chunked files. Metadata is kept in an internal collection; bytes are written in configurable chunk sizes (default 512 KB).
217
+
218
+ ```dart
219
+ import 'dart:typed_data';
220
+
221
+ final files = db.blobStorage('files', chunksSize: 1024 * 512);
222
+
223
+ await files.add(
224
+ metadata: {'filename': 'photo.png', 'mime': 'image/png'},
225
+ bytes: Uint8List.fromList([/* ... */]),
226
+ );
227
+
228
+ final blob = await files.get((meta) => meta.where('filename', eq: 'photo.png'));
229
+ final bytes = await blob?.bytes;
230
+ ```
231
+
232
+ ---
233
+
234
+ ### Relations
235
+
236
+ Define how collections link to each other. Relations are registered on the database and can be used with query `include`.
237
+
238
+ ```dart
239
+ db
240
+ .relation('users', 'id', name: 'posts')
241
+ .hasMany('posts', 'authorId');
242
+
243
+ // one-to-one
244
+ db.relation('users', 'profileId', name: 'profile').hasOne('profiles', 'id');
245
+
246
+ // many-to-many
247
+ db.relation('users', 'id', name: 'tags').manyToMany('tags', 'id');
248
+ ```
249
+
250
+ ---
251
+
252
+ ### Backups
253
+
254
+ Backups copy the database directory (excluding existing backup folders) into `backups/<name>/`.
255
+
256
+ ```dart
257
+ final snapshot = db.backup('before-migration');
258
+ print(db.backups); // list of Backup instances
259
+
260
+ db.removeBackup('before-migration');
261
+ db.removeAllBackups();
262
+ ```
263
+
264
+ ---
265
+
266
+ ### Reactive state (`FlowState`)
267
+
268
+ `FlowState<T>` wraps a seeded `BehaviorSubject` for synchronous reads and stream updates.
269
+
270
+ ```dart
271
+ final counter = FlowState<int>(0);
272
+
273
+ counter.listen((value) => print('count: $value'));
274
+ counter.update(1);
275
+ counter.updateFrom((v) => v + 1);
276
+
277
+ final combined = FlowState.combine2(
278
+ counter,
279
+ FlowState<String>(''),
280
+ (count, label) => '$label: $count',
281
+ );
282
+
283
+ await counter.close();
7
284
  ```
8
285
 
9
- To run:
286
+ `FlowState.combine2` through `combine9`, plus `FlowState.list`, merge multiple states into one.
287
+
288
+ ---
289
+
290
+ ### Flutter integration
10
291
 
11
- ```bash
12
- bun run index.ts
292
+ Use `FlowBuilder` to rebuild widgets when a FlowState changes:
293
+
294
+ ```dart
295
+ import 'package:flowdb/flutter.dart';
296
+ import 'package:flutter/material.dart';
297
+
298
+ class CounterView extends StatelessWidget {
299
+ const CounterView({super.key, required this.counter});
300
+
301
+ final FlowState<int> counter;
302
+
303
+ @override
304
+ Widget build(BuildContext context) {
305
+ return FlowBuilder<int>(
306
+ flow: counter,
307
+ builder: (value) => Text('Count: $value'),
308
+ onLoadingBuilder: () => const CircularProgressIndicator(),
309
+ onErrorBuilder: (e) => Text('Error: $e'),
310
+ onNoDataBuilder: () => const Text('No data'),
311
+ );
312
+ }
313
+ }
13
314
  ```
14
315
 
15
- This project was created using `bun init` in bun v1.3.5. [Bun](https://bun.com) is a fast all-in-one JavaScript runtime.
316
+ ---
317
+
318
+ ## Utilities
319
+
320
+ The core library also exports helpers used internally and available for app code:
321
+
322
+ | Export | Purpose |
323
+ |----------------|------------------------------------------------|
324
+ | `BGWorker` | Run a function in a separate isolate |
325
+ | `Json` | JSON encode/decode, nested get/update, CSV conversion |
326
+ | `Encryptor` | Encrypt/decrypt strings for at-rest storage |
327
+ | `Format` | Human-readable byte sizes |
328
+ | `Performance` | Timing utilities |
329
+ | `PrintColored` | ANSI-colored console output |
330
+ | `Random` | ID generation (CUID) |
331
+
332
+ ---
333
+
334
+ ## On-disk layout
335
+
336
+ A database directory typically looks like:
337
+
338
+ ```
339
+ my_app/
340
+ ├── collections/
341
+ │ └── users # newline-delimited JSON records
342
+ ├── stores/
343
+ │ └── settings # JSON key-value file
344
+ ├── blobs/
345
+ │ └── files/
346
+ │ ├── _metadata/ # blob metadata collection
347
+ │ └── chunks/ # binary chunks
348
+ └── backups/
349
+ └── snapshot_name/
350
+ ```
351
+
352
+ ---
353
+
354
+ ## Sync vs async
355
+
356
+ Most collection and query methods exist in pairs:
357
+
358
+ - `add` / `addSync`
359
+ - `getAll` / `getAllSync`
360
+ - `where(...).get()` / `where(...).getSync()`
361
+
362
+ Use async methods in UI code and isolates where blocking I/O is undesirable. Use sync methods in scripts, tests, or startup paths where simplicity matters.
363
+
364
+ ---
365
+
366
+ ## Encryption
367
+
368
+ Pass `encrypted: true` when opening a database to encrypt collection and store file contents. Encrypted databases propagate the setting to collections and stores created through `db.collection()` and `db.store()`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kidus.dev/flowdb",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "A lightweight, pluggable local database for TypeScript and JavaScript projects.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",