sails-sqlite 0.0.0 → 0.2.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 (48) 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 +1104 -0
  10. package/lib/private/build-std-adapter-method.js +69 -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/begin-transaction.js +51 -0
  19. package/lib/private/machines/commit-transaction.js +50 -0
  20. package/lib/private/machines/count-records.js +78 -0
  21. package/lib/private/machines/create-each-record.js +163 -0
  22. package/lib/private/machines/create-manager.js +174 -0
  23. package/lib/private/machines/create-record.js +126 -0
  24. package/lib/private/machines/define-physical-model.js +111 -0
  25. package/lib/private/machines/destroy-manager.js +87 -0
  26. package/lib/private/machines/destroy-records.js +114 -0
  27. package/lib/private/machines/drop-physical-model.js +51 -0
  28. package/lib/private/machines/find-records.js +120 -0
  29. package/lib/private/machines/get-connection.js +54 -0
  30. package/lib/private/machines/join.js +259 -0
  31. package/lib/private/machines/lease-connection.js +58 -0
  32. package/lib/private/machines/private/build-sqlite-where-clause.js +91 -0
  33. package/lib/private/machines/private/compile-statement.js +334 -0
  34. package/lib/private/machines/private/generate-join-sql-query.js +385 -0
  35. package/lib/private/machines/private/process-each-record.js +106 -0
  36. package/lib/private/machines/private/process-native-error.js +104 -0
  37. package/lib/private/machines/private/process-native-record.js +104 -0
  38. package/lib/private/machines/private/reify-values-to-set.js +83 -0
  39. package/lib/private/machines/release-connection.js +70 -0
  40. package/lib/private/machines/rollback-transaction.js +50 -0
  41. package/lib/private/machines/set-physical-sequence.js +77 -0
  42. package/lib/private/machines/sum-records.js +75 -0
  43. package/lib/private/machines/update-records.js +162 -0
  44. package/lib/private/machines/verify-model-def.js +38 -0
  45. package/package.json +58 -5
  46. package/tests/index.js +88 -0
  47. package/tests/runner.js +99 -0
  48. package/tests/transaction.test.js +562 -0
@@ -0,0 +1,83 @@
1
+ const assert = require('assert')
2
+
3
+ /**
4
+ * reifyValuesToSet()
5
+ *
6
+ * Prepare a dictionary of values to be used in a SQLite database operation.
7
+ * > The provided `valuesToSet` will be mutated in-place.
8
+ *
9
+ * @param {Object} valuesToSet
10
+ * @param {Object} WLModel
11
+ * @param {Object?} meta [`meta` query key from the s3q]
12
+ */
13
+ module.exports = function reifyValuesToSet(valuesToSet, WLModel, meta) {
14
+ assert(valuesToSet !== undefined, '1st argument is required')
15
+ assert(
16
+ typeof valuesToSet === 'object' &&
17
+ valuesToSet !== null &&
18
+ !Array.isArray(valuesToSet) &&
19
+ typeof valuesToSet !== 'function',
20
+ '1st argument must be a dictionary'
21
+ )
22
+ assert(WLModel !== undefined, '2nd argument is required')
23
+ assert(
24
+ typeof WLModel === 'object' &&
25
+ WLModel !== null &&
26
+ !Array.isArray(WLModel) &&
27
+ typeof WLModel !== 'function',
28
+ '2nd argument must be a WLModel, and it has to have a `definition` property for this utility to work.'
29
+ )
30
+
31
+ // Handle primary key safely
32
+ if (
33
+ WLModel.primaryKey &&
34
+ WLModel.attributes &&
35
+ WLModel.attributes[WLModel.primaryKey]
36
+ ) {
37
+ const primaryKeyAttrName = WLModel.primaryKey
38
+ const primaryKeyAttrDef = WLModel.attributes[WLModel.primaryKey]
39
+ const primaryKeyColumnName =
40
+ primaryKeyAttrDef.columnName || primaryKeyAttrName
41
+
42
+ if (valuesToSet[primaryKeyColumnName] === null) {
43
+ delete valuesToSet[primaryKeyColumnName]
44
+ } else if (valuesToSet[primaryKeyColumnName] !== undefined) {
45
+ // Ensure primary key is a number or string
46
+ if (
47
+ typeof valuesToSet[primaryKeyColumnName] !== 'number' &&
48
+ typeof valuesToSet[primaryKeyColumnName] !== 'string'
49
+ ) {
50
+ throw new Error(
51
+ `Invalid primary key value provided for \`${primaryKeyAttrName}\`. Must be a number or string.`
52
+ )
53
+ }
54
+ }
55
+ }
56
+
57
+ // Handle other attributes safely
58
+ if (WLModel.attributes && typeof WLModel.attributes === 'object') {
59
+ Object.entries(WLModel.attributes).forEach(([attrName, attrDef]) => {
60
+ if (!attrDef || typeof attrDef !== 'object') return
61
+
62
+ const columnName = attrDef.columnName || attrName
63
+ if (valuesToSet[columnName] === undefined) return
64
+
65
+ // Handle JSON type
66
+ if (attrDef.type === 'json' && valuesToSet[columnName] !== null) {
67
+ valuesToSet[columnName] = JSON.stringify(valuesToSet[columnName])
68
+ }
69
+
70
+ // Handle date type
71
+ if (attrDef.type === 'ref' && valuesToSet[columnName] instanceof Date) {
72
+ valuesToSet[columnName] = valuesToSet[columnName].toISOString()
73
+ }
74
+
75
+ // Handle boolean type
76
+ if (attrDef.type === 'boolean') {
77
+ valuesToSet[columnName] = valuesToSet[columnName] ? 1 : 0
78
+ }
79
+ })
80
+ }
81
+
82
+ return valuesToSet
83
+ }
@@ -0,0 +1,70 @@
1
+ module.exports = {
2
+ friendlyName: 'Release connection',
3
+
4
+ description: 'Release an active SQLite database connection.',
5
+
6
+ extendedDescription:
7
+ "For SQLite, this is typically a no-op as there is no connection pooling. However, it's included for consistency with other database adapters.",
8
+
9
+ sync: true,
10
+
11
+ inputs: {
12
+ connection: {
13
+ description: 'An active SQLite database connection.',
14
+ extendedDescription:
15
+ 'The provided database connection instance must still be active. Only database connection instances created by the `getConnection()` function in this driver are supported.',
16
+ example: '===',
17
+ required: true
18
+ },
19
+
20
+ meta: {
21
+ friendlyName: 'Meta (custom)',
22
+ description: 'Additional stuff to pass to the driver.',
23
+ extendedDescription:
24
+ 'This is reserved for custom driver-specific extensions. Please refer to the documentation for better-sqlite3 for more specific information.',
25
+ example: '==='
26
+ }
27
+ },
28
+
29
+ exits: {
30
+ success: {
31
+ description: 'The connection was released (no-op for SQLite).',
32
+ extendedDescription:
33
+ "For SQLite, this is typically a no-op, but it's included for consistency.",
34
+ outputFriendlyName: 'Report',
35
+ outputDescription:
36
+ 'The `meta` property is reserved for custom driver-specific extensions.',
37
+ outputExample: '==='
38
+ },
39
+
40
+ badConnection: {
41
+ description: 'The provided connection is not a valid SQLite connection.',
42
+ extendedDescription:
43
+ 'This might occur if the connection was already closed or if an invalid object was passed as the connection.',
44
+ outputFriendlyName: 'Report',
45
+ outputDescription:
46
+ 'The `meta` property is reserved for custom driver-specific extensions.',
47
+ outputExample: '==='
48
+ }
49
+ },
50
+
51
+ fn: ({ connection, meta }, exits) => {
52
+ // Check if the connection is a valid SQLite database instance
53
+ if (
54
+ typeof connection !== 'object' ||
55
+ connection === null ||
56
+ typeof connection.close !== 'function'
57
+ ) {
58
+ return exits.badConnection({
59
+ meta
60
+ })
61
+ }
62
+
63
+ // For SQLite, releasing a connection is typically a no-op
64
+ // We don't actually close the connection here because SQLite connections
65
+ // are meant to be long-lived and are automatically closed when the database is closed
66
+ return exits.success({
67
+ meta
68
+ })
69
+ }
70
+ }
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Module dependencies
3
+ */
4
+
5
+ /**
6
+ * Rollback Transaction
7
+ *
8
+ * Rollback the current database transaction on the provided connection.
9
+ */
10
+
11
+ module.exports = {
12
+ friendlyName: 'Rollback transaction',
13
+
14
+ description:
15
+ 'Rollback the current database transaction on the provided connection.',
16
+
17
+ moreInfoUrl:
18
+ 'https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#transactionfunction---function',
19
+
20
+ inputs: {
21
+ connection: {
22
+ description:
23
+ 'An active database connection that was acquired from a manager.',
24
+ example: '===',
25
+ required: true
26
+ },
27
+
28
+ meta: {
29
+ description: 'Additional options for this query.',
30
+ example: '==='
31
+ }
32
+ },
33
+
34
+ fn: function rollbackTransaction(inputs, exits) {
35
+ const db = inputs.connection
36
+ const meta = inputs.meta || {}
37
+
38
+ try {
39
+ if (!db.inTransaction) {
40
+ return exits.error(new Error('No active transaction to rollback.'))
41
+ }
42
+
43
+ db.prepare('ROLLBACK TRANSACTION').run()
44
+
45
+ return exits.success()
46
+ } catch (err) {
47
+ return exits.error(err)
48
+ }
49
+ }
50
+ }
@@ -0,0 +1,77 @@
1
+ module.exports = {
2
+ friendlyName: 'Set physical sequence',
3
+
4
+ description: 'Reset an auto-incrementing sequence to the specified value.',
5
+
6
+ sideEffects: 'idempotent',
7
+
8
+ inputs: {
9
+ connection: require('../constants/connection.input'),
10
+ sequenceName: { example: 'users', required: true },
11
+ sequenceValue: { example: 1, required: true },
12
+ meta: require('../constants/meta.input')
13
+ },
14
+
15
+ exits: {
16
+ success: {
17
+ description: 'The sequence was successfully reset.'
18
+ },
19
+ notFound: {
20
+ description: 'Could not find a sequence with the specified name.'
21
+ }
22
+ },
23
+
24
+ fn: function (inputs, exits) {
25
+ const db = inputs.connection
26
+ const sequenceName = inputs.sequenceName
27
+ const newSequenceValue = inputs.sequenceValue
28
+
29
+ // Parse the sequence name to get the actual table name
30
+ // PostgreSQL-style sequences are often named like 'user_id_seq', 'users_id_seq', etc.
31
+ // The table name should be the first string before the first underscore
32
+ let tableName = sequenceName
33
+
34
+ // Handle PostgreSQL-style sequence names
35
+ if (sequenceName.includes('_')) {
36
+ // Extract the table name as the first part before the first underscore
37
+ tableName = sequenceName.split('_')[0]
38
+ }
39
+
40
+ try {
41
+ // First, check if the table exists
42
+ const tableExists = db
43
+ .prepare(
44
+ "SELECT name FROM sqlite_master WHERE type='table' AND name = ?"
45
+ )
46
+ .get(tableName)
47
+
48
+ if (!tableExists) {
49
+ return exits.notFound(new Error(`Table '${tableName}' not found.`))
50
+ }
51
+
52
+ // If the table exists, update the sequence
53
+ const updateStmt = db.prepare(
54
+ 'UPDATE sqlite_sequence SET seq = ? WHERE name = ?'
55
+ )
56
+ const updateResult = updateStmt.run(newSequenceValue - 1, tableName)
57
+
58
+ if (updateResult.changes === 0) {
59
+ // If no rows were updated, it means the table doesn't have an autoincrement column
60
+ // We'll insert a new row in this case
61
+ const insertStmt = db.prepare(
62
+ 'INSERT INTO sqlite_sequence (name, seq) VALUES (?, ?)'
63
+ )
64
+ insertStmt.run(tableName, newSequenceValue - 1)
65
+ }
66
+
67
+ return exits.success()
68
+ } catch (error) {
69
+ // Handle the case where sqlite_sequence doesn't exist
70
+ if (error.message.includes('no such table: sqlite_sequence')) {
71
+ // This is not an error condition - it just means no tables with AUTOINCREMENT have been created yet
72
+ return exits.success()
73
+ }
74
+ return exits.error(error)
75
+ }
76
+ }
77
+ }
@@ -0,0 +1,75 @@
1
+ const buildSqliteWhereClause = require('./private/build-sqlite-where-clause')
2
+
3
+ module.exports = {
4
+ friendlyName: 'Sum (records)',
5
+
6
+ description:
7
+ 'Return the cumulative sum (∑) of a particular property over matching records.',
8
+
9
+ inputs: {
10
+ query: require('../constants/query.input'),
11
+ connection: require('../constants/connection.input'),
12
+ dryOrm: require('../constants/dry-orm.input')
13
+ },
14
+
15
+ exits: {
16
+ success: {
17
+ outputFriendlyName: 'Total (sum)',
18
+ outputDescription:
19
+ 'The sum of the given property across all matching records.',
20
+ outputExample: 999.99
21
+ }
22
+ },
23
+
24
+ fn: function sum(inputs, exits) {
25
+ const s3q = inputs.query
26
+
27
+ const tableName = s3q.using
28
+ const numericFieldName = s3q.numericAttrName
29
+
30
+ // Grab the model definition
31
+ // Find model by tableName since models is an object, not an array
32
+ let WLModel = null
33
+ for (const modelIdentity in inputs.dryOrm.models) {
34
+ if (inputs.dryOrm.models[modelIdentity].tableName === tableName) {
35
+ WLModel = inputs.dryOrm.models[modelIdentity]
36
+ break
37
+ }
38
+ }
39
+ if (!WLModel) {
40
+ return exits.error(
41
+ new Error(
42
+ `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.)`
43
+ )
44
+ )
45
+ }
46
+
47
+ // Build a SQLite WHERE clause from the `where` clause.
48
+ let whereClause
49
+ try {
50
+ whereClause = buildSqliteWhereClause(
51
+ s3q.criteria.where,
52
+ WLModel,
53
+ s3q.meta
54
+ )
55
+ } catch (e) {
56
+ return exits.error(e)
57
+ }
58
+
59
+ const db = inputs.connection
60
+
61
+ try {
62
+ let sumQuery = `SELECT COALESCE(SUM(\`${numericFieldName}\`), 0) as total FROM \`${tableName}\``
63
+ if (whereClause) {
64
+ sumQuery += ` WHERE ${whereClause}`
65
+ }
66
+
67
+ const stmt = db.prepare(sumQuery)
68
+ const result = stmt.get()
69
+
70
+ return exits.success(result.total)
71
+ } catch (err) {
72
+ return exits.error(err)
73
+ }
74
+ }
75
+ }
@@ -0,0 +1,162 @@
1
+ const util = require('util')
2
+ const processNativeRecord = require('./private/process-native-record')
3
+ const processNativeError = require('./private/process-native-error')
4
+ const reifyValuesToSet = require('./private/reify-values-to-set')
5
+ const buildSqliteWhereClause = require('./private/build-sqlite-where-clause')
6
+
7
+ module.exports = {
8
+ friendlyName: 'Update (records)',
9
+
10
+ description:
11
+ 'Update record(s) in the SQLite database based on a query criteria.',
12
+
13
+ inputs: {
14
+ query: require('../constants/query.input'),
15
+ connection: require('../constants/connection.input'),
16
+ dryOrm: require('../constants/dry-orm.input')
17
+ },
18
+
19
+ exits: {
20
+ success: {
21
+ outputFriendlyName: 'Records (maybe)',
22
+ outputDescription:
23
+ 'Either `null` OR (if `fetch:true`) an array of physical records that were updated.',
24
+ outputExample: '==='
25
+ },
26
+ notUnique: require('../constants/not-unique.exit')
27
+ },
28
+
29
+ fn: async function (inputs, exits) {
30
+ const s3q = inputs.query
31
+ if (s3q.meta && s3q.meta.logSqliteS3Qs) {
32
+ console.log(
33
+ '* * * * * *\nADAPTER (UPDATE RECORDS):',
34
+ util.inspect(s3q, { depth: 5 }),
35
+ '\n'
36
+ )
37
+ }
38
+
39
+ const tableName = s3q.using
40
+ // Find model by tableName since models is an object, not an array
41
+ let WLModel = null
42
+ for (const modelIdentity in inputs.dryOrm.models) {
43
+ if (inputs.dryOrm.models[modelIdentity].tableName === tableName) {
44
+ WLModel = inputs.dryOrm.models[modelIdentity]
45
+ break
46
+ }
47
+ }
48
+
49
+ if (!WLModel) {
50
+ return exits.error(
51
+ new Error(
52
+ `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.)`
53
+ )
54
+ )
55
+ }
56
+
57
+ const pkColumnName = WLModel.attributes[WLModel.primaryKey].columnName
58
+
59
+ const isFetchEnabled = !!(s3q.meta && s3q.meta.fetch)
60
+
61
+ try {
62
+ reifyValuesToSet(s3q.valuesToSet, WLModel, s3q.meta)
63
+ } catch (e) {
64
+ return exits.error(e)
65
+ }
66
+
67
+ const sqliteWhere = buildSqliteWhereClause(
68
+ s3q.criteria.where,
69
+ WLModel,
70
+ s3q.meta
71
+ )
72
+
73
+ const db = inputs.connection
74
+
75
+ // Check if we're already in a transaction
76
+ const wasInTransaction = db.inTransaction
77
+
78
+ try {
79
+ // Start a transaction only if we're not already in one
80
+ if (!wasInTransaction) {
81
+ db.exec('BEGIN TRANSACTION')
82
+ }
83
+
84
+ let affectedIds = []
85
+
86
+ if (isFetchEnabled) {
87
+ // Get the IDs of records which match this criteria
88
+ const selectSql = sqliteWhere
89
+ ? `SELECT \`${pkColumnName}\` FROM \`${tableName}\` WHERE ${sqliteWhere}`
90
+ : `SELECT \`${pkColumnName}\` FROM \`${tableName}\``
91
+ const selectStmt = db.prepare(selectSql)
92
+ affectedIds = selectStmt.all().map((row) => row[pkColumnName])
93
+ }
94
+
95
+ // Prepare the UPDATE statement
96
+ const setClauses = Object.entries(s3q.valuesToSet)
97
+ .map(([column, value]) => `\`${column}\` = ?`)
98
+ .join(', ')
99
+ const updateSql = sqliteWhere
100
+ ? `UPDATE \`${tableName}\` SET ${setClauses} WHERE ${sqliteWhere}`
101
+ : `UPDATE \`${tableName}\` SET ${setClauses}`
102
+ const updateStmt = db.prepare(updateSql)
103
+
104
+ // Execute the UPDATE
105
+ const updateInfo = updateStmt.run(...Object.values(s3q.valuesToSet))
106
+
107
+ // Handle case where pk value was changed
108
+ if (
109
+ s3q.valuesToSet[pkColumnName] !== undefined &&
110
+ affectedIds.length === 1
111
+ ) {
112
+ const oldPkValue = affectedIds[0]
113
+ const newPkValue = s3q.valuesToSet[pkColumnName]
114
+ affectedIds = [newPkValue]
115
+ } else if (
116
+ s3q.valuesToSet[pkColumnName] !== undefined &&
117
+ affectedIds.length > 1
118
+ ) {
119
+ if (!wasInTransaction) {
120
+ db.exec('ROLLBACK')
121
+ }
122
+ return exits.error(
123
+ new Error(
124
+ 'Consistency violation: Updated multiple records to have the same primary key value. (PK values should be unique!)'
125
+ )
126
+ )
127
+ }
128
+
129
+ // If fetch is not enabled, we're done
130
+ if (!isFetchEnabled) {
131
+ if (!wasInTransaction) {
132
+ db.exec('COMMIT')
133
+ }
134
+ return exits.success()
135
+ }
136
+
137
+ // Fetch the updated records
138
+ const fetchSql = `SELECT * FROM \`${tableName}\` WHERE \`${pkColumnName}\` IN (${affectedIds.map(() => '?').join(', ')})`
139
+ const fetchStmt = db.prepare(fetchSql)
140
+ const phRecords = fetchStmt.all(affectedIds)
141
+
142
+ // Process records
143
+ phRecords.forEach((phRecord) => {
144
+ processNativeRecord(phRecord, WLModel, s3q.meta)
145
+ })
146
+
147
+ if (!wasInTransaction) {
148
+ db.exec('COMMIT')
149
+ }
150
+ return exits.success(phRecords)
151
+ } catch (err) {
152
+ if (!wasInTransaction) {
153
+ db.exec('ROLLBACK')
154
+ }
155
+ err = processNativeError(err)
156
+ if (err.footprint && err.footprint.identity === 'notUnique') {
157
+ return exits.notUnique(err)
158
+ }
159
+ return exits.error(err)
160
+ }
161
+ }
162
+ }
@@ -0,0 +1,38 @@
1
+ module.exports = {
2
+ friendlyName: 'Verify model def',
3
+
4
+ description:
5
+ 'Verify that the specified model definition is compatible with this adapter.',
6
+
7
+ extendedDescription:
8
+ 'This assumes that the provided model def has already undergone adapter-agnostic normalization, and is considered generally valid.',
9
+
10
+ sideEffects: 'cacheable',
11
+
12
+ sync: true,
13
+
14
+ inputs: {
15
+ modelDef: {
16
+ description: 'A Waterline model definition.',
17
+ extendedDescription:
18
+ 'This model definition should already be fully-formed (i.e. it should have undergone generic normalization/validation already).',
19
+ moreInfoUrl:
20
+ 'http://sailsjs.com/documentation/concepts/models-and-orm/models',
21
+ example: '===', // {}
22
+ readOnly: true,
23
+ required: true
24
+ }
25
+ },
26
+
27
+ exits: {
28
+ invalid: {
29
+ description: 'The provided model definition was invalid.',
30
+ outputFriendlyName: 'Error',
31
+ outputExample: '===' // e.g. new Error('Primary key attribute should have `columnName: \'_id\'`.')
32
+ }
33
+ },
34
+
35
+ fn: function (inputs, exits) {
36
+ return exits.success()
37
+ }
38
+ }
package/package.json CHANGED
@@ -1,14 +1,39 @@
1
1
  {
2
2
  "name": "sails-sqlite",
3
- "version": "0.0.0",
3
+ "version": "0.2.0",
4
4
  "description": "SQLite adapter for Sails/Waterline",
5
- "main": "./lib",
5
+ "main": "lib",
6
+ "directories": {
7
+ "lib": "./lib"
8
+ },
6
9
  "scripts": {
7
- "test": "npm run lint && npm run custom-tests"
10
+ "test": "npm run lint && npm run custom-tests && npm run adapter-tests",
11
+ "custom-tests": "node tests/",
12
+ "adapter-tests": "rm -rf .tmp && node tests/runner.js",
13
+ "lint": "prettier --check .",
14
+ "lint:fix": "prettier --write .",
15
+ "prepare": "husky"
8
16
  },
9
17
  "repository": {
10
18
  "type": "git",
11
- "url": "github.com/sailscastshq/sails-sqlite"
19
+ "url": "git://github.com/sailscastshq/sails-sqlite.git"
20
+ },
21
+ "bugs": "https://github.com/sailscastshq/sails-sqlite/issues",
22
+ "waterlineAdapter": {
23
+ "waterlineVersion": "~0.13.0",
24
+ "interfaces": [
25
+ "semantic",
26
+ "queryable",
27
+ "migratable",
28
+ "associations",
29
+ "sql"
30
+ ],
31
+ "features": [
32
+ "crossAdapter",
33
+ "unique",
34
+ "autoIncrement",
35
+ "schemas"
36
+ ]
12
37
  },
13
38
  "keywords": [
14
39
  "sqlite",
@@ -21,5 +46,33 @@
21
46
  "database-adapter"
22
47
  ],
23
48
  "author": "Kelvin Omereshone <kelvin@sailscasts.com>",
24
- "license": "MIT"
49
+ "license": "MIT",
50
+ "devDependencies": {
51
+ "husky": "^9.0.11",
52
+ "lint-staged": "^15.2.2",
53
+ "mocha": "^11.7.2",
54
+ "prettier": "3.2.5",
55
+ "waterline-adapter-tests": "^1.0.1"
56
+ },
57
+ "lint-staged": {
58
+ "**/*": "prettier --write --ignore-unknown"
59
+ },
60
+ "dependencies": {
61
+ "better-sqlite3": "^12.2.0",
62
+ "flaverr": "^1.10.0",
63
+ "machine": "^15.2.3",
64
+ "waterline-utils": "^1.4.5"
65
+ },
66
+ "machinepack": {
67
+ "friendlyName": "SQLite",
68
+ "extendedDescription": "Uses the Node.js better-sqlite3 driver located at https://npmjs.com/package/better-sqlite3",
69
+ "moreInfoUrl": "https://github.com/WiseLibs/better-sqlite3",
70
+ "implements": [
71
+ "connectable",
72
+ "modeled",
73
+ "migratable"
74
+ ],
75
+ "machineDir": "lib/private/machines/",
76
+ "exportStyle": "dryDictionary"
77
+ }
25
78
  }