meadow-connection-sqlite 1.0.10

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.
package/docs/api.md ADDED
@@ -0,0 +1,256 @@
1
+ # API Reference
2
+
3
+ ## Class: MeadowConnectionSQLite
4
+
5
+ Extends `fable-serviceproviderbase`. Manages a connection to a SQLite database file through the better-sqlite3 library.
6
+
7
+ ### Constructor
8
+
9
+ ```javascript
10
+ new MeadowConnectionSQLite(pFable, pManifest, pServiceHash)
11
+ ```
12
+
13
+ | Parameter | Type | Description |
14
+ |-----------|------|-------------|
15
+ | `pFable` | object | A Fable instance |
16
+ | `pManifest` | object | Service manifest / options (optional) |
17
+ | `pServiceHash` | string | Service identifier |
18
+
19
+ On construction:
20
+
21
+ - Sets `serviceType` to `'MeadowConnectionSQLite'`
22
+ - Sets `connected` to `false`
23
+ - Reads `SQLiteFilePath` from `fable.settings.SQLite` if available
24
+
25
+ The provider is not yet connected after construction — call `connectAsync()` to open the database.
26
+
27
+ ---
28
+
29
+ ## Properties
30
+
31
+ ### connected
32
+
33
+ Whether the database connection is open.
34
+
35
+ **Type:** `boolean`
36
+
37
+ ### db
38
+
39
+ The raw `better-sqlite3` `Database` instance. Use this for all query operations. Returns `false` before `connectAsync()` is called.
40
+
41
+ **Type:** `Database | false`
42
+
43
+ ```javascript
44
+ let tmpDB = _Fable.MeadowSQLiteProvider.db;
45
+
46
+ // Synchronous query methods from better-sqlite3:
47
+ tmpDB.prepare(sql).run(params); // INSERT, UPDATE, DELETE
48
+ tmpDB.prepare(sql).get(params); // SELECT single row
49
+ tmpDB.prepare(sql).all(params); // SELECT all rows
50
+ tmpDB.exec(sql); // Execute raw SQL (multi-statement)
51
+ tmpDB.transaction(fn); // Wrap a function in a transaction
52
+ tmpDB.pragma(string); // Run a PRAGMA command
53
+ tmpDB.close(); // Close the database
54
+ ```
55
+
56
+ ### SQLite
57
+
58
+ Reference to the `better-sqlite3` module constructor. Useful for accessing type constants or creating additional database instances.
59
+
60
+ **Type:** `function`
61
+
62
+ ```javascript
63
+ let tmpSQLiteConstructor = _Fable.MeadowSQLiteProvider.SQLite;
64
+ ```
65
+
66
+ ### serviceType
67
+
68
+ Always `'MeadowConnectionSQLite'`.
69
+
70
+ **Type:** `string`
71
+
72
+ ---
73
+
74
+ ## Methods
75
+
76
+ ### connectAsync(fCallback)
77
+
78
+ Open a connection to the SQLite database file specified in configuration.
79
+
80
+ | Parameter | Type | Description |
81
+ |-----------|------|-------------|
82
+ | `fCallback` | function | Callback: `(error, database)` |
83
+
84
+ **Behavior:**
85
+
86
+ - If `SQLiteFilePath` is not configured, calls back with an error
87
+ - If already connected, calls back immediately with the existing database (idempotent)
88
+ - Creates the database file if it does not exist
89
+ - Enables WAL (Write-Ahead Logging) journal mode for performance
90
+ - Sets `this.connected = true` on success
91
+
92
+ ```javascript
93
+ _Fable.MeadowSQLiteProvider.connectAsync(
94
+ (pError, pDatabase) =>
95
+ {
96
+ if (pError)
97
+ {
98
+ _Fable.log.error(`Connection failed: ${pError}`);
99
+ return;
100
+ }
101
+
102
+ // pDatabase is the same as _Fable.MeadowSQLiteProvider.db
103
+ pDatabase.exec('CREATE TABLE IF NOT EXISTS Test (id INTEGER PRIMARY KEY)');
104
+ });
105
+ ```
106
+
107
+ ### connect()
108
+
109
+ Synchronous wrapper that calls `connectAsync()` without a callback. Logs a warning about potential race conditions. Prefer `connectAsync()`.
110
+
111
+ ---
112
+
113
+ ## Configuration
114
+
115
+ ### Fable Settings
116
+
117
+ ```json
118
+ {
119
+ "SQLite":
120
+ {
121
+ "SQLiteFilePath": "./data/myapp.db"
122
+ }
123
+ }
124
+ ```
125
+
126
+ | Setting | Type | Required | Description |
127
+ |---------|------|----------|-------------|
128
+ | `SQLiteFilePath` | string | Yes | File path or `:memory:` for in-memory databases |
129
+
130
+ Additional properties are passed through to the `better-sqlite3` constructor:
131
+
132
+ | Option | Type | Default | Description |
133
+ |--------|------|---------|-------------|
134
+ | `readonly` | boolean | `false` | Open the database in read-only mode |
135
+ | `fileMustExist` | boolean | `false` | Error if the file does not exist |
136
+ | `timeout` | number | `5000` | Milliseconds to wait when the database is locked |
137
+
138
+ ---
139
+
140
+ ## Service Registration
141
+
142
+ The provider integrates with Fable's service manager:
143
+
144
+ ```javascript
145
+ const libMeadowConnectionSQLite = require('meadow-connection-sqlite');
146
+
147
+ // Register the service type
148
+ _Fable.serviceManager.addServiceType('MeadowSQLiteProvider', libMeadowConnectionSQLite);
149
+
150
+ // Instantiate (optionally with per-instance options)
151
+ _Fable.serviceManager.instantiateServiceProvider('MeadowSQLiteProvider',
152
+ {
153
+ SQLiteFilePath: './data/alternate.db',
154
+ readonly: true
155
+ });
156
+
157
+ // Access the provider
158
+ _Fable.MeadowSQLiteProvider.connectAsync((pError) => { /* ... */ });
159
+ ```
160
+
161
+ Constructor options override Fable settings, allowing multiple providers with different configurations.
162
+
163
+ ---
164
+
165
+ ## better-sqlite3 Query API
166
+
167
+ After connecting, all queries go through the `db` getter. Here is a quick reference for the better-sqlite3 methods you will use most:
168
+
169
+ ### Execute Raw SQL
170
+
171
+ ```javascript
172
+ tmpDB.exec(`
173
+ CREATE TABLE IF NOT EXISTS Book (
174
+ IDBook INTEGER PRIMARY KEY AUTOINCREMENT,
175
+ Title TEXT DEFAULT ''
176
+ );
177
+ CREATE INDEX IF NOT EXISTS idx_book_title ON Book(Title);
178
+ `);
179
+ ```
180
+
181
+ `exec()` runs one or more SQL statements. It does not return data.
182
+
183
+ ### Prepared Statements
184
+
185
+ ```javascript
186
+ let tmpStmt = tmpDB.prepare('SELECT * FROM Book WHERE Author = ?');
187
+
188
+ // Single row
189
+ let tmpRow = tmpStmt.get('Frank Herbert');
190
+
191
+ // All rows
192
+ let tmpRows = tmpStmt.all('Frank Herbert');
193
+
194
+ // Modification
195
+ let tmpInsert = tmpDB.prepare('INSERT INTO Book (Title, Author) VALUES (?, ?)');
196
+ let tmpInfo = tmpInsert.run('Dune', 'Frank Herbert');
197
+ // tmpInfo.changes = 1, tmpInfo.lastInsertRowid = 1
198
+ ```
199
+
200
+ ### Named Parameters
201
+
202
+ ```javascript
203
+ let tmpStmt = tmpDB.prepare('SELECT * FROM Book WHERE Author = @author AND YearPublished > @year');
204
+ let tmpRows = tmpStmt.all({ author: 'Isaac Asimov', year: 1950 });
205
+ ```
206
+
207
+ ### Transactions
208
+
209
+ ```javascript
210
+ let tmpBulkInsert = tmpDB.transaction(
211
+ (pBooks) =>
212
+ {
213
+ let tmpStmt = tmpDB.prepare('INSERT INTO Book (Title, Author) VALUES (?, ?)');
214
+ for (let tmpBook of pBooks)
215
+ {
216
+ tmpStmt.run(tmpBook.Title, tmpBook.Author);
217
+ }
218
+ });
219
+
220
+ // The entire array is inserted atomically
221
+ tmpBulkInsert([
222
+ { Title: 'Foundation', Author: 'Isaac Asimov' },
223
+ { Title: 'Snow Crash', Author: 'Neal Stephenson' }
224
+ ]);
225
+ ```
226
+
227
+ If any statement inside the transaction throws, the entire transaction is rolled back automatically.
228
+
229
+ ### Close
230
+
231
+ ```javascript
232
+ tmpDB.close();
233
+ ```
234
+
235
+ ---
236
+
237
+ ## Logging
238
+
239
+ The provider logs connection events through the Fable logging system:
240
+
241
+ | Event | Level | Message |
242
+ |-------|-------|---------|
243
+ | Connecting | `info` | `Meadow-Connection-SQLite connecting to file [path].` |
244
+ | Connected | `info` | `Meadow-Connection-SQLite successfully connected to SQLite file [path].` |
245
+ | Already connected | `error` | `...is already connected - skipping the second connect call.` |
246
+ | Missing path | `error` | `...database file path is invalid; SQLiteFilePath must be in either...` |
247
+ | Connection error | `error` | `...error connecting to SQLite file [path]: [error]` |
248
+ | No callback | `error` | `...connect() called without a callback...` |
249
+
250
+ ---
251
+
252
+ ## Known Limitations
253
+
254
+ - **Table generation** — The `generateCreateTableStatement()` and `createTable()` methods produce MSSQL-syntax SQL that is not compatible with SQLite. Use `db.exec()` with SQLite-native `CREATE TABLE` statements instead.
255
+ - **Prepared statement getter** — The `preparedStatement` property references an uninitialized connection pool. Use `db.prepare()` directly for prepared statements.
256
+ - **No async queries** — better-sqlite3 is synchronous by design. For CPU-bound workloads, consider running queries in a worker thread.
package/docs/cover.md ADDED
@@ -0,0 +1,15 @@
1
+ # Meadow Connection SQLite
2
+
3
+ > SQLite database provider for the Meadow data layer
4
+
5
+ Connect any Fable application to a local SQLite database through the service provider pattern. Built on better-sqlite3 for fast synchronous access with WAL journaling, automatic file creation, and in-memory database support.
6
+
7
+ - **Zero Config Server** -- No daemon, no Docker — just a file path and you have a database
8
+ - **Synchronous API** -- All queries run synchronously through better-sqlite3's native bindings
9
+ - **WAL Journaling** -- Write-Ahead Logging enabled automatically for concurrent read/write performance
10
+ - **In-Memory Mode** -- Use `:memory:` as the file path for ephemeral databases in tests and prototypes
11
+ - **Service Integration** -- Registers as a Fable service with dependency injection and lifecycle logging
12
+
13
+ [Quick Start](README.md)
14
+ [API Reference](api.md)
15
+ [GitHub](https://github.com/stevenvelozo/meadow-connection-sqlite)
@@ -0,0 +1,73 @@
1
+ /* ============================================================================
2
+ Pict Docuserve - Base Styles
3
+ ============================================================================ */
4
+
5
+ /* Reset and base */
6
+ *, *::before, *::after {
7
+ box-sizing: border-box;
8
+ }
9
+
10
+ html, body {
11
+ margin: 0;
12
+ padding: 0;
13
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
14
+ font-size: 16px;
15
+ line-height: 1.5;
16
+ color: #423D37;
17
+ background-color: #fff;
18
+ -webkit-font-smoothing: antialiased;
19
+ -moz-osx-font-smoothing: grayscale;
20
+ }
21
+
22
+ /* Typography */
23
+ h1, h2, h3, h4, h5, h6 {
24
+ margin-top: 0;
25
+ line-height: 1.3;
26
+ }
27
+
28
+ a {
29
+ color: #2E7D74;
30
+ text-decoration: none;
31
+ }
32
+
33
+ a:hover {
34
+ color: #256861;
35
+ }
36
+
37
+ /* Application container */
38
+ #Docuserve-Application-Container {
39
+ min-height: 100vh;
40
+ }
41
+
42
+ /* Utility: scrollbar styling */
43
+ ::-webkit-scrollbar {
44
+ width: 8px;
45
+ }
46
+
47
+ ::-webkit-scrollbar-track {
48
+ background: #F5F0E8;
49
+ }
50
+
51
+ ::-webkit-scrollbar-thumb {
52
+ background: #D4CCBE;
53
+ border-radius: 4px;
54
+ }
55
+
56
+ ::-webkit-scrollbar-thumb:hover {
57
+ background: #B5AA9A;
58
+ }
59
+
60
+ /* Responsive adjustments */
61
+ @media (max-width: 768px) {
62
+ html {
63
+ font-size: 14px;
64
+ }
65
+
66
+ #Docuserve-Sidebar-Container {
67
+ display: none;
68
+ }
69
+
70
+ .docuserve-body {
71
+ flex-direction: column;
72
+ }
73
+ }
@@ -0,0 +1,331 @@
1
+ # Full Pipeline Example
2
+
3
+ This walkthrough builds a complete CRUD pipeline from scratch: configure Fable, connect to SQLite, create a schema, insert rows, query, update, delete, run a transaction, and compute aggregates. Every snippet below has been executed and verified against meadow-connection-sqlite.
4
+
5
+ > The full script is available at `debug/Pipeline-Test.js` in the module repository.
6
+
7
+ ---
8
+
9
+ ## Setup
10
+
11
+ ```javascript
12
+ const libFable = require('fable');
13
+ const libMeadowConnectionSQLite = require('meadow-connection-sqlite');
14
+
15
+ let _Fable = new libFable(
16
+ {
17
+ "Product": "BookstoreExample",
18
+ "ProductVersion": "1.0.0",
19
+ "UUID": { "DataCenter": 0, "Worker": 0 },
20
+ "LogStreams": [{ "streamtype": "console" }],
21
+ "SQLite": { "SQLiteFilePath": "./dist/Bookstore.db" }
22
+ });
23
+
24
+ _Fable.serviceManager.addServiceType('MeadowSQLiteProvider', libMeadowConnectionSQLite);
25
+ _Fable.serviceManager.instantiateServiceProvider('MeadowSQLiteProvider');
26
+ ```
27
+
28
+ ---
29
+
30
+ ## Step 1: Connect
31
+
32
+ ```javascript
33
+ _Fable.MeadowSQLiteProvider.connectAsync(
34
+ (pError, pDatabase) =>
35
+ {
36
+ if (pError)
37
+ {
38
+ _Fable.log.error(`Connection failed: ${pError}`);
39
+ return;
40
+ }
41
+
42
+ _Fable.log.info(`Connected: ${_Fable.MeadowSQLiteProvider.connected}`);
43
+ // => Connected: true
44
+
45
+ let tmpDB = _Fable.MeadowSQLiteProvider.db;
46
+
47
+ // ... all remaining steps use tmpDB ...
48
+ });
49
+ ```
50
+
51
+ The database file is created automatically if it does not exist. WAL journal mode is enabled on connection for better performance.
52
+
53
+ ---
54
+
55
+ ## Step 2: Create a Table
56
+
57
+ Use SQLite-native `CREATE TABLE` syntax through `db.exec()`:
58
+
59
+ ```javascript
60
+ tmpDB.exec(`
61
+ CREATE TABLE IF NOT EXISTS Book (
62
+ IDBook INTEGER PRIMARY KEY AUTOINCREMENT,
63
+ GUIDBook TEXT DEFAULT '00000000-0000-0000-0000-000000000000',
64
+ Title TEXT DEFAULT '',
65
+ Author TEXT DEFAULT '',
66
+ YearPublished INTEGER DEFAULT 0,
67
+ Price REAL DEFAULT 0.0,
68
+ CreateDate TEXT DEFAULT (datetime('now')),
69
+ UpdateDate TEXT DEFAULT (datetime('now'))
70
+ )
71
+ `);
72
+ ```
73
+
74
+ **SQLite type mapping:**
75
+
76
+ | Meadow Type | SQLite Type | Notes |
77
+ |-------------|-------------|-------|
78
+ | ID | `INTEGER PRIMARY KEY AUTOINCREMENT` | Auto-incrementing primary key |
79
+ | GUID | `TEXT` | Store as 36-character UUID string |
80
+ | String | `TEXT` | SQLite does not enforce VARCHAR length |
81
+ | Numeric / ForeignKey | `INTEGER` | 64-bit signed integer |
82
+ | Decimal | `REAL` | 64-bit IEEE floating point |
83
+ | Boolean | `INTEGER` | 0 = false, 1 = true |
84
+ | DateTime | `TEXT` | ISO-8601 string, e.g. `datetime('now')` |
85
+ | Text | `TEXT` | Unlimited length |
86
+
87
+ ---
88
+
89
+ ## Step 3: Insert Rows
90
+
91
+ Prepared statements with positional parameters:
92
+
93
+ ```javascript
94
+ let tmpInsert = tmpDB.prepare(
95
+ `INSERT INTO Book (GUIDBook, Title, Author, YearPublished, Price) VALUES (?, ?, ?, ?, ?)`
96
+ );
97
+
98
+ let tmpResult1 = tmpInsert.run(_Fable.fable.getUUID(), 'The Hobbit', 'J.R.R. Tolkien', 1937, 12.99);
99
+ // tmpResult1.lastInsertRowid => 1
100
+
101
+ let tmpResult2 = tmpInsert.run(_Fable.fable.getUUID(), 'Dune', 'Frank Herbert', 1965, 14.99);
102
+ // tmpResult2.lastInsertRowid => 2
103
+
104
+ let tmpResult3 = tmpInsert.run(_Fable.fable.getUUID(), 'Neuromancer', 'William Gibson', 1984, 11.50);
105
+ // tmpResult3.lastInsertRowid => 3
106
+ ```
107
+
108
+ The `run()` method returns an object with two properties:
109
+
110
+ | Property | Type | Description |
111
+ |----------|------|-------------|
112
+ | `changes` | number | Number of rows affected |
113
+ | `lastInsertRowid` | number | Auto-generated ID of the inserted row |
114
+
115
+ Fable's `getUUID()` generates unique identifiers for the GUID column.
116
+
117
+ ---
118
+
119
+ ## Step 4: Read All Rows
120
+
121
+ ```javascript
122
+ let tmpAllBooks = tmpDB.prepare(`SELECT * FROM Book`).all();
123
+ // tmpAllBooks.length => 3
124
+
125
+ for (let i = 0; i < tmpAllBooks.length; i++)
126
+ {
127
+ let tmpBook = tmpAllBooks[i];
128
+ console.log(`[${tmpBook.IDBook}] "${tmpBook.Title}" by ${tmpBook.Author} ($${tmpBook.Price})`);
129
+ }
130
+ ```
131
+
132
+ Output:
133
+
134
+ ```
135
+ [1] "The Hobbit" by J.R.R. Tolkien ($12.99)
136
+ [2] "Dune" by Frank Herbert ($14.99)
137
+ [3] "Neuromancer" by William Gibson ($11.5)
138
+ ```
139
+
140
+ The `all()` method returns an array of plain JavaScript objects. Column names become property keys.
141
+
142
+ ---
143
+
144
+ ## Step 5: Read a Single Row
145
+
146
+ ```javascript
147
+ let tmpOneBook = tmpDB.prepare(`SELECT * FROM Book WHERE IDBook = ?`).get(2);
148
+ // tmpOneBook => { IDBook: 2, Title: 'Dune', Author: 'Frank Herbert', ... }
149
+ ```
150
+
151
+ The `get()` method returns a single object, or `undefined` if no row matches.
152
+
153
+ ---
154
+
155
+ ## Step 6: Update
156
+
157
+ ```javascript
158
+ let tmpUpdate = tmpDB.prepare(
159
+ `UPDATE Book SET Price = ?, UpdateDate = datetime('now') WHERE IDBook = ?`
160
+ );
161
+ let tmpUpdateResult = tmpUpdate.run(16.99, 2);
162
+ // tmpUpdateResult.changes => 1
163
+
164
+ let tmpVerify = tmpDB.prepare(`SELECT * FROM Book WHERE IDBook = ?`).get(2);
165
+ // tmpVerify.Price => 16.99
166
+ ```
167
+
168
+ ---
169
+
170
+ ## Step 7: Delete
171
+
172
+ ```javascript
173
+ let tmpDelete = tmpDB.prepare(`DELETE FROM Book WHERE IDBook = ?`);
174
+ let tmpDeleteResult = tmpDelete.run(3);
175
+ // tmpDeleteResult.changes => 1
176
+
177
+ let tmpRemaining = tmpDB.prepare(`SELECT * FROM Book`).all();
178
+ // tmpRemaining.length => 2
179
+ ```
180
+
181
+ ---
182
+
183
+ ## Step 8: Transactions
184
+
185
+ Transactions in better-sqlite3 wrap a function. If anything inside the function throws, the entire transaction is rolled back. Otherwise it commits atomically.
186
+
187
+ ```javascript
188
+ let tmpBulkInsert = tmpDB.transaction(
189
+ (pBooks) =>
190
+ {
191
+ let tmpStmt = tmpDB.prepare(
192
+ `INSERT INTO Book (GUIDBook, Title, Author, YearPublished, Price) VALUES (?, ?, ?, ?, ?)`
193
+ );
194
+ for (let tmpBook of pBooks)
195
+ {
196
+ tmpStmt.run(tmpBook.GUID, tmpBook.Title, tmpBook.Author, tmpBook.Year, tmpBook.Price);
197
+ }
198
+ });
199
+
200
+ tmpBulkInsert(
201
+ [
202
+ { GUID: _Fable.fable.getUUID(), Title: 'Snow Crash', Author: 'Neal Stephenson', Year: 1992, Price: 13.99 },
203
+ { GUID: _Fable.fable.getUUID(), Title: 'Hyperion', Author: 'Dan Simmons', Year: 1989, Price: 15.50 },
204
+ { GUID: _Fable.fable.getUUID(), Title: 'Foundation', Author: 'Isaac Asimov', Year: 1951, Price: 10.99 }
205
+ ]);
206
+ ```
207
+
208
+ After the transaction, querying shows all five books:
209
+
210
+ ```
211
+ [1] "The Hobbit" by J.R.R. Tolkien ($12.99)
212
+ [2] "Dune" by Frank Herbert ($16.99)
213
+ [4] "Snow Crash" by Neal Stephenson ($13.99)
214
+ [5] "Hyperion" by Dan Simmons ($15.5)
215
+ [6] "Foundation" by Isaac Asimov ($10.99)
216
+ ```
217
+
218
+ Note that IDBook 3 is absent (deleted in Step 7) and the sequence skips to 4.
219
+
220
+ Transactions are significantly faster than individual inserts for bulk operations. Inserting 1,000 rows in a transaction can be 50-100x faster than 1,000 separate `run()` calls.
221
+
222
+ ---
223
+
224
+ ## Step 9: Aggregates
225
+
226
+ ```javascript
227
+ let tmpStats = tmpDB.prepare(
228
+ `SELECT COUNT(*) as BookCount, AVG(Price) as AvgPrice, SUM(Price) as TotalValue FROM Book`
229
+ ).get();
230
+
231
+ console.log(`Books: ${tmpStats.BookCount}`);
232
+ // => Books: 5
233
+
234
+ console.log(`Average price: $${tmpStats.AvgPrice.toFixed(2)}`);
235
+ // => Average price: $14.09
236
+
237
+ console.log(`Total value: $${tmpStats.TotalValue.toFixed(2)}`);
238
+ // => Total value: $70.46
239
+ ```
240
+
241
+ ---
242
+
243
+ ## Step 10: Close
244
+
245
+ ```javascript
246
+ tmpDB.close();
247
+ ```
248
+
249
+ After closing, further queries on this database instance will throw. The provider's `connected` property remains `true` — if you need to reconnect, create a new provider instance.
250
+
251
+ ---
252
+
253
+ ## Named Parameters
254
+
255
+ better-sqlite3 supports named parameters prefixed with `@`, `$`, or `:`:
256
+
257
+ ```javascript
258
+ let tmpStmt = tmpDB.prepare(
259
+ `SELECT * FROM Book WHERE Author = @author AND YearPublished > @minYear`
260
+ );
261
+
262
+ let tmpBooks = tmpStmt.all({ author: 'Isaac Asimov', minYear: 1950 });
263
+ ```
264
+
265
+ Named parameters make complex queries more readable and less error-prone than positional `?` placeholders.
266
+
267
+ ---
268
+
269
+ ## In-Memory Databases
270
+
271
+ For test suites and ephemeral workflows, use `:memory:` instead of a file path:
272
+
273
+ ```javascript
274
+ let _Fable = new libFable(
275
+ {
276
+ "Product": "TestSuite",
277
+ "SQLite": { "SQLiteFilePath": ":memory:" }
278
+ });
279
+
280
+ // Register, connect, create tables, run tests...
281
+ // Database is discarded when the process exits or the connection closes.
282
+ ```
283
+
284
+ In-memory databases are faster than file-backed databases and leave no cleanup artifacts.
285
+
286
+ ---
287
+
288
+ ## Error Handling Pattern
289
+
290
+ ```javascript
291
+ _Fable.MeadowSQLiteProvider.connectAsync(
292
+ (pError) =>
293
+ {
294
+ if (pError)
295
+ {
296
+ _Fable.log.error(`Connection error: ${pError.message}`);
297
+ return;
298
+ }
299
+
300
+ let tmpDB = _Fable.MeadowSQLiteProvider.db;
301
+
302
+ try
303
+ {
304
+ tmpDB.exec(`CREATE TABLE Test (id INTEGER PRIMARY KEY)`);
305
+ tmpDB.prepare(`INSERT INTO Test (id) VALUES (?)`).run(1);
306
+ }
307
+ catch (pQueryError)
308
+ {
309
+ _Fable.log.error(`Query error: ${pQueryError.message}`);
310
+ }
311
+ });
312
+ ```
313
+
314
+ better-sqlite3 throws `SqliteError` exceptions for all query failures (syntax errors, constraint violations, missing tables). Wrap query blocks in try/catch for graceful handling.
315
+
316
+ ---
317
+
318
+ ## Pipeline Summary
319
+
320
+ | Step | Method | What It Does |
321
+ |------|--------|-------------|
322
+ | Connect | `connectAsync(cb)` | Opens the database file, enables WAL |
323
+ | Create | `db.exec(sql)` | Runs DDL statements |
324
+ | Insert | `db.prepare(sql).run(...)` | Returns `{ changes, lastInsertRowid }` |
325
+ | Read all | `db.prepare(sql).all(...)` | Returns array of row objects |
326
+ | Read one | `db.prepare(sql).get(...)` | Returns single object or `undefined` |
327
+ | Update | `db.prepare(sql).run(...)` | Returns `{ changes }` |
328
+ | Delete | `db.prepare(sql).run(...)` | Returns `{ changes }` |
329
+ | Transaction | `db.transaction(fn)(args)` | Atomic batch; auto-rollback on throw |
330
+ | Aggregate | `db.prepare(sql).get()` | Returns computed values |
331
+ | Close | `db.close()` | Releases file handle |
@@ -0,0 +1,39 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
7
+ <meta name="description" content="Documentation powered by pict-docuserve">
8
+
9
+ <title>Documentation</title>
10
+
11
+ <!-- Application Stylesheet -->
12
+ <link href="css/docuserve.css" rel="stylesheet">
13
+ <!-- KaTeX stylesheet for LaTeX equation rendering -->
14
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.21/dist/katex.min.css">
15
+ <!-- PICT Dynamic View CSS Container -->
16
+ <style id="PICT-CSS"></style>
17
+
18
+ <!-- Load the PICT library from jsDelivr CDN -->
19
+ <script src="https://cdn.jsdelivr.net/npm/pict@1/dist/pict.min.js" type="text/javascript"></script>
20
+ <!-- Bootstrap the Application -->
21
+ <script type="text/javascript">
22
+ //<![CDATA[
23
+ Pict.safeOnDocumentReady(() => { Pict.safeLoadPictApplication(PictDocuserve, 2)});
24
+ //]]>
25
+ </script>
26
+ </head>
27
+ <body>
28
+ <!-- The root container for the Pict application -->
29
+ <div id="Docuserve-Application-Container"></div>
30
+
31
+ <!-- Mermaid diagram rendering -->
32
+ <script src="https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.min.js"></script>
33
+ <script>mermaid.initialize({ startOnLoad: false, theme: 'default' });</script>
34
+ <!-- KaTeX for LaTeX equation rendering -->
35
+ <script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.21/dist/katex.min.js"></script>
36
+ <!-- Load the Docuserve PICT Application Bundle from jsDelivr CDN -->
37
+ <script src="https://cdn.jsdelivr.net/npm/pict-docuserve@0/dist/pict-docuserve.min.js" type="text/javascript"></script>
38
+ </body>
39
+ </html>