sails-sqlite 0.0.0 → 0.1.0

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 (44) hide show
  1. package/.github/FUNDING.yml +1 -0
  2. package/.github/workflows/prettier.yml +16 -0
  3. package/.github/workflows/test.yml +16 -0
  4. package/.husky/pre-commit +1 -0
  5. package/.prettierrc.js +5 -0
  6. package/CHANGELOG.md +161 -0
  7. package/LICENSE +21 -0
  8. package/README.md +247 -0
  9. package/lib/index.js +928 -0
  10. package/lib/private/build-std-adapter-method.js +65 -0
  11. package/lib/private/constants/connection.input.js +15 -0
  12. package/lib/private/constants/dry-orm.input.js +23 -0
  13. package/lib/private/constants/meta.input.js +14 -0
  14. package/lib/private/constants/not-unique.exit.js +16 -0
  15. package/lib/private/constants/query.input.js +15 -0
  16. package/lib/private/constants/table-name.input.js +12 -0
  17. package/lib/private/machines/avg-records.js +74 -0
  18. package/lib/private/machines/count-records.js +78 -0
  19. package/lib/private/machines/create-each-record.js +163 -0
  20. package/lib/private/machines/create-manager.js +174 -0
  21. package/lib/private/machines/create-record.js +126 -0
  22. package/lib/private/machines/define-physical-model.js +102 -0
  23. package/lib/private/machines/destroy-manager.js +87 -0
  24. package/lib/private/machines/destroy-records.js +101 -0
  25. package/lib/private/machines/drop-physical-model.js +51 -0
  26. package/lib/private/machines/find-records.js +120 -0
  27. package/lib/private/machines/get-connection.js +54 -0
  28. package/lib/private/machines/join.js +93 -0
  29. package/lib/private/machines/private/build-sqlite-where-clause.js +89 -0
  30. package/lib/private/machines/private/generate-join-sql-query.js +377 -0
  31. package/lib/private/machines/private/process-each-record.js +99 -0
  32. package/lib/private/machines/private/process-native-error.js +59 -0
  33. package/lib/private/machines/private/process-native-record.js +104 -0
  34. package/lib/private/machines/private/reify-values-to-set.js +83 -0
  35. package/lib/private/machines/release-connection.js +70 -0
  36. package/lib/private/machines/set-physical-sequence.js +77 -0
  37. package/lib/private/machines/sum-records.js +75 -0
  38. package/lib/private/machines/update-records.js +145 -0
  39. package/lib/private/machines/verify-model-def.js +38 -0
  40. package/package.json +53 -5
  41. package/tests/adapter.test.js +534 -0
  42. package/tests/datatypes.test.js +293 -0
  43. package/tests/index.js +88 -0
  44. package/tests/sequence.test.js +153 -0
@@ -0,0 +1,126 @@
1
+ module.exports = {
2
+ friendlyName: 'Create (record)',
3
+
4
+ description: 'Create a new physical record in the SQLite database.',
5
+
6
+ inputs: {
7
+ query: require('../constants/query.input'),
8
+ connection: require('../constants/connection.input'),
9
+ dryOrm: require('../constants/dry-orm.input')
10
+ },
11
+
12
+ exits: {
13
+ success: {
14
+ outputFriendlyName: 'Record (maybe)',
15
+ outputDescription:
16
+ 'Either `null` or (if `fetch:true`) a dictionary representing the new record that was created.',
17
+ outputExample: '==='
18
+ },
19
+ notUnique: require('../constants/not-unique.exit')
20
+ },
21
+
22
+ fn: function (inputs, exits) {
23
+ // Dependencies
24
+ const util = require('util')
25
+ const _ = require('@sailshq/lodash')
26
+ const processNativeRecord = require('./private/process-native-record')
27
+ const processNativeError = require('./private/process-native-error')
28
+ const reifyValuesToSet = require('./private/reify-values-to-set')
29
+
30
+ // Local var for the stage 3 query, for easier access.
31
+ const s3q = inputs.query
32
+ if (s3q.meta && s3q.meta.logSQLiteS3Qs) {
33
+ console.log(
34
+ '* * * * * *\nADAPTER (CREATE RECORD):',
35
+ util.inspect(s3q, { depth: 5 }),
36
+ '\n'
37
+ )
38
+ }
39
+
40
+ // Local var for the `tableName`, for clarity.
41
+ const tableName = s3q.using
42
+
43
+ // Grab the model definition
44
+ const WLModel = _.find(inputs.dryOrm.models, { tableName: tableName })
45
+ if (!WLModel) {
46
+ return exits.error(
47
+ new Error(
48
+ `No model with that tableName (\`${tableName}\`) has been registered with this adapter. Were any unexpected modifications made to the stage 3 query? Could the adapter's internal state have been corrupted? (This error is usually due to a bug in this adapter's implementation.)`
49
+ )
50
+ )
51
+ }
52
+
53
+ // Reify values to set
54
+ try {
55
+ reifyValuesToSet(s3q.newRecord, WLModel, s3q.meta)
56
+ } catch (e) {
57
+ return exits.error(e)
58
+ }
59
+
60
+ // Determine whether to fetch or not
61
+ const isFetchEnabled = !!(s3q.meta && s3q.meta.fetch)
62
+
63
+ // Create this new record in the SQLite database
64
+ const db = inputs.connection
65
+
66
+ try {
67
+ // Build column names and values arrays
68
+ const columnNames = Object.keys(s3q.newRecord)
69
+ const columnValues = Object.values(s3q.newRecord)
70
+
71
+ // Validate that we have data to insert
72
+ if (columnNames.length === 0) {
73
+ throw new Error('Cannot create record: no data provided')
74
+ }
75
+
76
+ // Prepare the INSERT statement with proper SQL escaping
77
+ const columns = columnNames.join(', ')
78
+ const placeholders = columnNames.map(() => '?').join(', ')
79
+ const sql = `INSERT INTO \`${tableName}\` (${columns}) VALUES (${placeholders})`
80
+
81
+ // Use prepared statement (optimized for repeated use)
82
+ const stmt = db.getPreparedStatement
83
+ ? db.getPreparedStatement(sql)
84
+ : db.prepare(sql)
85
+
86
+ // Execute the INSERT statement within a transaction for consistency
87
+ const info = db.runInTransaction
88
+ ? db.runInTransaction(() => stmt.run(columnValues))
89
+ : stmt.run(columnValues)
90
+
91
+ // If `fetch` is NOT enabled, we're done.
92
+ if (!isFetchEnabled) {
93
+ return exits.success()
94
+ }
95
+
96
+ // Otherwise, fetch the newly created record
97
+ const selectSql = `SELECT * FROM ${tableName} WHERE rowid = ?`
98
+ const selectStmt = db.prepare(selectSql)
99
+ const phRecord = selectStmt.get(info.lastInsertRowid)
100
+
101
+ if (!phRecord) {
102
+ return exits.error(
103
+ new Error(
104
+ 'Consistency violation: Unable to retrieve the inserted record. This might indicate a consistency violation.'
105
+ )
106
+ )
107
+ }
108
+
109
+ try {
110
+ // Process record (mutate in-place) to wash away adapter-specific eccentricities.
111
+ processNativeRecord(phRecord, WLModel, s3q.meta)
112
+ } catch (e) {
113
+ return exits.error(e)
114
+ }
115
+
116
+ // Send back the record
117
+ return exits.success(phRecord)
118
+ } catch (err) {
119
+ err = processNativeError(err)
120
+ if (err.footprint && err.footprint.identity === 'notUnique') {
121
+ return exits.notUnique(err)
122
+ }
123
+ return exits.error(err)
124
+ }
125
+ }
126
+ }
@@ -0,0 +1,102 @@
1
+ module.exports = {
2
+ friendlyName: 'Define (physical model)',
3
+
4
+ description:
5
+ 'Define a physical model (i.e. SQLite table) with the specified characteristics, creating indexes as needed.',
6
+
7
+ sideEffects: 'idempotent',
8
+
9
+ inputs: {
10
+ connection: require('../constants/connection.input'),
11
+ tableName: require('../constants/table-name.input'),
12
+ columns: {
13
+ description: 'An array of column definitions.',
14
+ required: true,
15
+ example: '==='
16
+ },
17
+ meta: require('../constants/meta.input')
18
+ },
19
+
20
+ exits: {
21
+ success: {
22
+ description:
23
+ 'New physical model (and any necessary indexes) were created successfully.'
24
+ }
25
+ },
26
+
27
+ fn: function (inputs, exits) {
28
+ const db = inputs.connection
29
+ function getSqliteType(columnType) {
30
+ if (!columnType || typeof columnType !== 'string') {
31
+ return 'TEXT' // Default fallback
32
+ }
33
+ switch (columnType.toLowerCase()) {
34
+ case '_string':
35
+ case '_text':
36
+ case '_mediumtext':
37
+ case '_longtext':
38
+ return 'TEXT'
39
+ case '_number':
40
+ case '_numberkey':
41
+ case '_numbertimestamp':
42
+ case 'integer':
43
+ case 'int':
44
+ return 'INTEGER'
45
+ case '_json':
46
+ return 'TEXT'
47
+ case 'float':
48
+ case 'double':
49
+ case 'real':
50
+ return 'REAL'
51
+ case 'boolean':
52
+ return 'INTEGER'
53
+ case 'date':
54
+ case 'datetime':
55
+ return 'TEXT'
56
+ case 'binary':
57
+ case 'blob':
58
+ return 'BLOB'
59
+ default:
60
+ return 'TEXT'
61
+ }
62
+ }
63
+
64
+ try {
65
+ // Start a transaction
66
+ db.prepare('BEGIN').run()
67
+
68
+ // Build and execute the CREATE TABLE statement
69
+ let createTableSQL = `CREATE TABLE IF NOT EXISTS ${inputs.tableName} (`
70
+ let columnDefs = inputs.columns.map((column) => {
71
+ const columnType = column.columnType ?? column.type
72
+ let def = `${column.columnName} ${column.autoIncrement ? 'INTEGER' : getSqliteType(columnType)}`
73
+ if (column.autoIncrement) {
74
+ def += ' PRIMARY KEY AUTOINCREMENT NOT NULL'
75
+ }
76
+ if (column.unique && !column.autoIncrement) def += ' UNIQUE'
77
+ return def
78
+ })
79
+ createTableSQL += columnDefs.join(', ') + ')'
80
+ db.prepare(createTableSQL).run()
81
+
82
+ // Create indexes
83
+ inputs.columns.forEach((column) => {
84
+ if (column.unique && !column.autoIncrement) {
85
+ const indexSQL = `CREATE UNIQUE INDEX IF NOT EXISTS idx_${inputs.tableName}_${column.columnName} ON ${inputs.tableName} (${column.columnName})`
86
+ db.prepare(indexSQL).run()
87
+ }
88
+ })
89
+
90
+ // Commit the transaction
91
+ db.prepare('COMMIT').run()
92
+
93
+ return exits.success()
94
+ } catch (error) {
95
+ // If there's an error, roll back the transaction
96
+ db.prepare('ROLLBACK').run()
97
+ return exits.error(
98
+ new Error(`Error defining table ${inputs.tableName}: ${error.message}`)
99
+ )
100
+ }
101
+ }
102
+ }
@@ -0,0 +1,87 @@
1
+ module.exports = {
2
+ friendlyName: 'Destroy manager',
3
+
4
+ description: 'Destroy the specified SQLite connection manager.',
5
+
6
+ extendedDescription:
7
+ 'For SQLite, this involves closing the database connection. Unlike other databases, SQLite does not use connection pools, so this operation is relatively straightforward.',
8
+
9
+ sync: true,
10
+
11
+ inputs: {
12
+ manager: {
13
+ description: 'The SQLite connection manager instance to destroy.',
14
+ extendedDescription:
15
+ 'Only managers built using the `createManager()` method of this driver are supported. The database connection manager instance provided must not have been destroyed previously.',
16
+ example: '===',
17
+ required: true
18
+ },
19
+
20
+ meta: {
21
+ friendlyName: 'Meta (custom)',
22
+ description: 'Additional options to pass to the SQLite driver.',
23
+ extendedDescription:
24
+ 'This is reserved for custom driver-specific extensions. Please refer to the better-sqlite3 documentation for more specific information.',
25
+ example: '==='
26
+ }
27
+ },
28
+
29
+ exits: {
30
+ success: {
31
+ description: 'The specified SQLite manager was successfully destroyed.',
32
+ outputFriendlyName: 'Report',
33
+ outputDescription:
34
+ 'The `meta` property is reserved for custom driver-specific extensions.',
35
+ outputExample: '==='
36
+ }
37
+ },
38
+
39
+ fn: ({ manager, meta }, exits) => {
40
+ try {
41
+ // Validate the manager
42
+ if (
43
+ typeof manager !== 'object' ||
44
+ manager === null ||
45
+ typeof manager.close !== 'function'
46
+ ) {
47
+ return exits.error(
48
+ new Error(
49
+ 'The provided `manager` is not a valid SQLite manager. It should be a better-sqlite3 Database instance with a `close` method.'
50
+ )
51
+ )
52
+ }
53
+
54
+ // Check if the database is already closed
55
+ if (!manager.open) {
56
+ console.warn(
57
+ 'SQLite manager appears to already be closed, skipping destruction'
58
+ )
59
+ return exits.success({ meta })
60
+ }
61
+
62
+ // Use graceful cleanup if available (from enhanced create-manager)
63
+ if (typeof manager.closeGracefully === 'function') {
64
+ manager.closeGracefully()
65
+ } else {
66
+ // Fallback to basic close
67
+ manager.close()
68
+ }
69
+
70
+ // Verify the connection is actually closed
71
+ if (manager.open) {
72
+ throw new Error('Failed to close SQLite database connection')
73
+ }
74
+
75
+ return exits.success({
76
+ meta: {
77
+ ...meta,
78
+ destroyedAt: new Date().toISOString()
79
+ }
80
+ })
81
+ } catch (error) {
82
+ return exits.error(
83
+ new Error(`Error destroying SQLite manager: ${error.message}`)
84
+ )
85
+ }
86
+ }
87
+ }
@@ -0,0 +1,101 @@
1
+ const util = require('util')
2
+ const processNativeRecord = require('./private/process-native-record')
3
+ const buildSqliteWhereClause = require('./private/build-sqlite-where-clause')
4
+
5
+ module.exports = {
6
+ friendlyName: 'Destroy (records)',
7
+
8
+ description:
9
+ 'Destroy record(s) in the SQLite database matching a query criteria.',
10
+
11
+ inputs: {
12
+ query: require('../constants/query.input'),
13
+ connection: require('../constants/connection.input'),
14
+ dryOrm: require('../constants/dry-orm.input')
15
+ },
16
+
17
+ exits: {
18
+ success: {
19
+ outputFriendlyName: 'Records (maybe)',
20
+ outputDescription:
21
+ 'Either `null` OR (if `fetch:true`) an array of physical records that were destroyed.',
22
+ outputExample: '==='
23
+ }
24
+ },
25
+
26
+ fn: async function (inputs, exits) {
27
+ const s3q = inputs.query
28
+ if (s3q.meta && s3q.meta.logSqliteS3Qs) {
29
+ console.log(
30
+ '* * * * * *\nADAPTER (DESTROY RECORDS):',
31
+ util.inspect(s3q, { depth: 5 }),
32
+ '\n'
33
+ )
34
+ }
35
+
36
+ const tableName = s3q.using
37
+ // Find model by tableName since models is an object, not an array
38
+ let WLModel = null
39
+ for (const modelIdentity in inputs.dryOrm.models) {
40
+ if (inputs.dryOrm.models[modelIdentity].tableName === tableName) {
41
+ WLModel = inputs.dryOrm.models[modelIdentity]
42
+ break
43
+ }
44
+ }
45
+
46
+ if (!WLModel) {
47
+ return exits.error(
48
+ new Error(
49
+ `No model with that tableName (\`${tableName}\`) has been registered with this adapter. Were any unexpected modifications made to the stage 3 query? Could the adapter's internal state have been corrupted? (This error is usually due to a bug in this adapter's implementation.)`
50
+ )
51
+ )
52
+ }
53
+
54
+ const pkColumnName = WLModel.attributes[WLModel.primaryKey].columnName
55
+ const isFetchEnabled = !!(s3q.meta && s3q.meta.fetch)
56
+
57
+ const sqliteWhere = buildSqliteWhereClause(
58
+ s3q.criteria.where,
59
+ WLModel,
60
+ s3q.meta
61
+ )
62
+
63
+ const db = inputs.connection
64
+
65
+ try {
66
+ // Start a transaction
67
+ db.exec('BEGIN TRANSACTION')
68
+
69
+ let phRecords
70
+ if (isFetchEnabled) {
71
+ // Fetch matching records before deletion
72
+ const selectSql = `SELECT * FROM ${tableName} WHERE ${sqliteWhere}`
73
+ const selectStmt = db.prepare(selectSql)
74
+ phRecords = selectStmt.all()
75
+ }
76
+
77
+ // Perform the deletion
78
+ const deleteSql = `DELETE FROM ${tableName} WHERE ${sqliteWhere}`
79
+ const deleteStmt = db.prepare(deleteSql)
80
+ const deleteInfo = deleteStmt.run()
81
+
82
+ // Commit the transaction
83
+ db.exec('COMMIT')
84
+
85
+ if (!isFetchEnabled) {
86
+ return exits.success()
87
+ }
88
+
89
+ // Process fetched records
90
+ phRecords.forEach((phRecord) => {
91
+ processNativeRecord(phRecord, WLModel, s3q.meta)
92
+ })
93
+
94
+ return exits.success(phRecords)
95
+ } catch (err) {
96
+ // Rollback the transaction in case of error
97
+ db.exec('ROLLBACK')
98
+ return exits.error(err)
99
+ }
100
+ }
101
+ }
@@ -0,0 +1,51 @@
1
+ module.exports = {
2
+ friendlyName: 'Drop (physical model)',
3
+
4
+ description:
5
+ 'Completely drop & destroy any traces of a particular physical model (i.e. SQLite table).',
6
+
7
+ sideEffects: 'idempotent',
8
+
9
+ inputs: {
10
+ connection: require('../constants/connection.input'),
11
+ tableName: require('../constants/table-name.input'),
12
+ meta: require('../constants/meta.input')
13
+ },
14
+
15
+ exits: {
16
+ success: {
17
+ description:
18
+ 'If such a physical model exists, it was dropped successfully.'
19
+ }
20
+ },
21
+
22
+ fn: function (inputs, exits) {
23
+ // Get the SQLite database connection
24
+ const db = inputs.connection
25
+
26
+ try {
27
+ // SQL to drop the table
28
+ const dropTableSQL = `DROP TABLE IF EXISTS ${inputs.tableName}`
29
+
30
+ // Execute the drop table operation
31
+ db.prepare(dropTableSQL).run()
32
+
33
+ // SQL to remove the table's entry from sqlite_sequence (if it exists)
34
+ const cleanSequenceSQL = `DELETE FROM sqlite_sequence WHERE name = ?`
35
+
36
+ // Clean up the sqlite_sequence
37
+ db.prepare(cleanSequenceSQL).run(inputs.tableName)
38
+
39
+ // Return success, as the main operation (dropping the table) was successful
40
+ return exits.success()
41
+ } catch (error) {
42
+ if (error.message.includes('no such table: sqlite_sequence')) {
43
+ // If sqlite_sequence doesn't exist, it's not an error - just means no autoincrement was used
44
+ return exits.success()
45
+ }
46
+ return exits.error(
47
+ new Error(`Error dropping table ${inputs.tableName}: ${error.message}`)
48
+ )
49
+ }
50
+ }
51
+ }
@@ -0,0 +1,120 @@
1
+ const assert = require('assert')
2
+ const util = require('util')
3
+ const processNativeRecord = require('./private/process-native-record')
4
+ const buildSqliteWhereClause = require('./private/build-sqlite-where-clause')
5
+
6
+ module.exports = {
7
+ friendlyName: 'Find (records)',
8
+
9
+ description: 'Find record(s) in the SQLite database.',
10
+
11
+ inputs: {
12
+ query: require('../constants/query.input'),
13
+ connection: require('../constants/connection.input'),
14
+ dryOrm: require('../constants/dry-orm.input')
15
+ },
16
+
17
+ exits: {
18
+ success: {
19
+ outputFriendlyName: 'Records',
20
+ outputDescription: 'An array of physical records.',
21
+ outputExample: '===' //[ {===} ]
22
+ }
23
+ },
24
+
25
+ fn: async function (inputs, exits) {
26
+ const s3q = inputs.query
27
+ if (s3q.meta && s3q.meta.logSqliteS3Qs) {
28
+ console.log(
29
+ '* * * * * *\nADAPTER (FIND RECORDS):',
30
+ util.inspect(s3q, { depth: 10 }),
31
+ '\n'
32
+ )
33
+ }
34
+
35
+ const tableName = s3q.using
36
+ // Find model by tableName since models is an object, not an array
37
+ let WLModel = null
38
+ for (const modelIdentity in inputs.dryOrm.models) {
39
+ if (inputs.dryOrm.models[modelIdentity].tableName === tableName) {
40
+ WLModel = inputs.dryOrm.models[modelIdentity]
41
+ break
42
+ }
43
+ }
44
+
45
+ if (!WLModel) {
46
+ return exits.error(
47
+ new Error(
48
+ `No model with that tableName (\`${tableName}\`) has been registered with this adapter. Were any unexpected modifications made to the stage 3 query? Could the adapter's internal state have been corrupted? (This error is usually due to a bug in this adapter's implementation.)`
49
+ )
50
+ )
51
+ }
52
+
53
+ const db = inputs.connection
54
+
55
+ try {
56
+ let sqlQuery = `SELECT `
57
+
58
+ // Handle SELECT clause
59
+ if (s3q.criteria.select) {
60
+ sqlQuery += s3q.criteria.select.join(', ')
61
+ } else {
62
+ sqlQuery += '*'
63
+ }
64
+
65
+ sqlQuery += ` FROM ${tableName}`
66
+
67
+ // Handle WHERE clause
68
+ const whereClause = buildSqliteWhereClause(
69
+ s3q.criteria.where,
70
+ WLModel,
71
+ s3q.meta
72
+ )
73
+ if (whereClause) {
74
+ sqlQuery += ` WHERE ${whereClause}`
75
+ }
76
+
77
+ // Handle SORT clause
78
+ if (s3q.criteria.sort && s3q.criteria.sort.length) {
79
+ const sortClauses = s3q.criteria.sort.map((sortObj) => {
80
+ const key = Object.keys(sortObj)[0]
81
+ const direction = sortObj[key] === 'ASC' ? 'ASC' : 'DESC'
82
+ return `${key} ${direction}`
83
+ })
84
+ sqlQuery += ` ORDER BY ${sortClauses.join(', ')}`
85
+ }
86
+
87
+ // Handle LIMIT clause
88
+ // If no limit is specified, don't add LIMIT clause (will return all matching records)
89
+ // This handles cases where Waterline doesn't provide a default limit
90
+ if (
91
+ s3q.criteria.limit !== undefined &&
92
+ Number.isFinite(s3q.criteria.limit)
93
+ ) {
94
+ sqlQuery += ` LIMIT ${s3q.criteria.limit}`
95
+ }
96
+
97
+ // Handle SKIP (OFFSET) clause
98
+ if (s3q.criteria.skip) {
99
+ sqlQuery += ` OFFSET ${s3q.criteria.skip}`
100
+ }
101
+
102
+ // Use prepared statement caching for better performance
103
+ const stmt = db.getPreparedStatement
104
+ ? db.getPreparedStatement(sqlQuery)
105
+ : db.prepare(sqlQuery)
106
+
107
+ const nativeResult = stmt.all()
108
+
109
+ // Process records
110
+ const phRecords = nativeResult.map((record) => {
111
+ processNativeRecord(record, WLModel, s3q.meta)
112
+ return record
113
+ })
114
+
115
+ return exits.success(phRecords)
116
+ } catch (err) {
117
+ return exits.error(err)
118
+ }
119
+ }
120
+ }
@@ -0,0 +1,54 @@
1
+ module.exports = {
2
+ friendlyName: 'Get connection',
3
+
4
+ description:
5
+ 'Get an active connection to the SQLite database (this is a no-op for SQLite).',
6
+
7
+ moreInfoUrl:
8
+ 'https://github.com/node-machine/driver-interface/blob/master/machines/get-connection.js',
9
+
10
+ sync: true,
11
+
12
+ inputs: {
13
+ manager: {
14
+ description: 'A SQLite database instance (from better-sqlite3).',
15
+ example: '===',
16
+ required: true
17
+ },
18
+
19
+ meta: {
20
+ friendlyName: 'Meta (unused)',
21
+ description: 'Additional stuff to pass to the driver.',
22
+ example: '==='
23
+ }
24
+ },
25
+
26
+ exits: {
27
+ success: {
28
+ outputFriendlyName: 'Report',
29
+ outputDescription:
30
+ 'The `connection` property is a SQLite database instance. The `meta` property is unused.',
31
+ outputExample: '==='
32
+ },
33
+
34
+ failed: {
35
+ friendlyName: 'Failed (unused)',
36
+ description:
37
+ 'Could not acquire a connection to the database via the provided connection manager. (This is unlikely to occur with SQLite)',
38
+ outputFriendlyName: 'Report',
39
+ outputExample: {
40
+ error: '===',
41
+ meta: '==='
42
+ }
43
+ }
44
+ },
45
+
46
+ fn: ({ manager, meta }, exits) => {
47
+ // This is a no-op that just sends back the manager and `meta` that were passed in.
48
+ // In SQLite, the "manager" and "connection" are the same thing: a Database instance from better-sqlite3.
49
+ return exits.success({
50
+ connection: manager,
51
+ meta
52
+ })
53
+ }
54
+ }