@platformatic/sql-mapper 0.0.23

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 (36) hide show
  1. package/.nyc_output/0208d41a-48bc-4675-a861-0475eb461a17.json +1 -0
  2. package/.nyc_output/588169a6-88e9-4949-af5a-631822d7dc42.json +1 -0
  3. package/.nyc_output/5bbdf331-cd01-4869-9d54-3d708610224a.json +1 -0
  4. package/.nyc_output/6d7c60ad-a404-4a1d-af86-8a334cff5f02.json +1 -0
  5. package/.nyc_output/8dae7e8c-5022-4a0c-a5b3-bf547f97961b.json +1 -0
  6. package/.nyc_output/f63bf7c5-4f58-4b46-a822-6f5ccf5a54a8.json +1 -0
  7. package/.nyc_output/processinfo/0208d41a-48bc-4675-a861-0475eb461a17.json +1 -0
  8. package/.nyc_output/processinfo/588169a6-88e9-4949-af5a-631822d7dc42.json +1 -0
  9. package/.nyc_output/processinfo/5bbdf331-cd01-4869-9d54-3d708610224a.json +1 -0
  10. package/.nyc_output/processinfo/6d7c60ad-a404-4a1d-af86-8a334cff5f02.json +1 -0
  11. package/.nyc_output/processinfo/8dae7e8c-5022-4a0c-a5b3-bf547f97961b.json +1 -0
  12. package/.nyc_output/processinfo/f63bf7c5-4f58-4b46-a822-6f5ccf5a54a8.json +1 -0
  13. package/.nyc_output/processinfo/index.json +1 -0
  14. package/.taprc +1 -0
  15. package/LICENSE +201 -0
  16. package/NOTICE +13 -0
  17. package/README.md +13 -0
  18. package/lib/entity.js +287 -0
  19. package/lib/queries/index.js +23 -0
  20. package/lib/queries/mariadb.js +11 -0
  21. package/lib/queries/mysql-shared.js +62 -0
  22. package/lib/queries/mysql.js +104 -0
  23. package/lib/queries/pg.js +79 -0
  24. package/lib/queries/shared.js +100 -0
  25. package/lib/queries/sqlite.js +169 -0
  26. package/lib/utils.js +14 -0
  27. package/mapper.d.ts +308 -0
  28. package/mapper.js +155 -0
  29. package/package.json +44 -0
  30. package/test/entity.test.js +344 -0
  31. package/test/helper.js +66 -0
  32. package/test/hooks.test.js +325 -0
  33. package/test/inserted_at_updated_at.test.js +132 -0
  34. package/test/mapper.test.js +288 -0
  35. package/test/types/mapper.test-d.ts +64 -0
  36. package/test/where.test.js +316 -0
@@ -0,0 +1,169 @@
1
+ 'use strict'
2
+
3
+ const { randomUUID } = require('crypto')
4
+
5
+ async function listTables (db, sql) {
6
+ const tables = await db.query(sql`
7
+ SELECT name FROM sqlite_master
8
+ WHERE type='table'
9
+ `)
10
+ return tables.map(t => t.name)
11
+ }
12
+
13
+ module.exports.listTables = listTables
14
+
15
+ async function listColumns (db, sql, table) {
16
+ const columns = await db.query(sql`
17
+ SELECT * FROM pragma_table_info(${table})
18
+ `)
19
+ for (const column of columns) {
20
+ column.column_name = column.name
21
+ // convert varchar(42) in varchar
22
+ column.udt_name = column.type.replace(/^([^(]+).*/, '$1').toLowerCase()
23
+ // convert is_nullable
24
+ column.is_nullable = column.notnull === 0 && column.pk === 0 ? 'YES' : 'NO'
25
+ }
26
+ return columns
27
+ }
28
+
29
+ module.exports.listColumns = listColumns
30
+
31
+ async function listConstraints (db, sql, table) {
32
+ const constraints = []
33
+ const pks = await db.query(sql`
34
+ SELECT *
35
+ FROM pragma_table_info(${table})
36
+ WHERE pk > 0
37
+ `)
38
+
39
+ if (pks.length > 1) {
40
+ throw new Error(`Table ${table} has ${pks.length} primary keys`)
41
+ }
42
+
43
+ if (pks.length === 1) {
44
+ constraints.push({
45
+ column_name: pks[0].name,
46
+ constraint_type: 'PRIMARY KEY'
47
+ })
48
+ }
49
+
50
+ const foreignKeys = await db.query(sql`
51
+ SELECT *
52
+ FROM pragma_foreign_key_list(${table})
53
+ `)
54
+
55
+ for (const foreignKey of foreignKeys) {
56
+ constraints.push({
57
+ table_name: table,
58
+ column_name: foreignKey.from,
59
+ constraint_type: 'FOREIGN KEY',
60
+ foreign_table_name: foreignKey.table,
61
+ foreign_column_name: foreignKey.to
62
+ })
63
+ }
64
+ return constraints
65
+ }
66
+
67
+ module.exports.listConstraints = listConstraints
68
+
69
+ async function insertOne (db, sql, table, input, primaryKey, useUUID, fieldsToRetrieve) {
70
+ const keysToSql = Object.keys(input).map((key) => sql.ident(key))
71
+ keysToSql.push(sql.ident(primaryKey))
72
+ const keys = sql.join(
73
+ keysToSql,
74
+ sql`, `
75
+ )
76
+
77
+ const valuesToSql = Object.keys(input).map((key) => {
78
+ return sql.value(input[key])
79
+ })
80
+ let primaryKeyValue
81
+ // TODO add test for this
82
+ if (useUUID) {
83
+ primaryKeyValue = randomUUID()
84
+ valuesToSql.push(sql.value(primaryKeyValue))
85
+ } else {
86
+ valuesToSql.push(sql.value(null))
87
+ }
88
+ const values = sql.join(
89
+ valuesToSql,
90
+ sql`, `
91
+ )
92
+
93
+ const insert = sql`
94
+ INSERT INTO ${sql.ident(table)} (${keys})
95
+ VALUES(${values})
96
+ `
97
+ await db.query(insert)
98
+
99
+ if (!useUUID) {
100
+ const res2 = await db.query(sql`
101
+ SELECT last_insert_rowid()
102
+ `)
103
+
104
+ primaryKeyValue = res2[0]['last_insert_rowid()']
105
+ }
106
+
107
+ const res = await db.query(sql`
108
+ SELECT ${sql.join(fieldsToRetrieve, sql`, `)}
109
+ FROM ${sql.ident(table)}
110
+ WHERE ${sql.ident(primaryKey)} = ${sql.value(primaryKeyValue)}
111
+ `)
112
+
113
+ return res[0]
114
+ }
115
+
116
+ module.exports.insertOne = insertOne
117
+
118
+ async function updateOne (db, sql, table, input, primaryKey, fieldsToRetrieve) {
119
+ const pairs = Object.keys(input).map((key) => {
120
+ const value = input[key]
121
+ return sql`${sql.ident(key)} = ${value}`
122
+ })
123
+
124
+ const update = sql`
125
+ UPDATE ${sql.ident(table)}
126
+ SET ${sql.join(pairs, sql`, `)}
127
+ WHERE ${sql.ident(primaryKey)} = ${sql.value(input[primaryKey])}
128
+ `
129
+ await db.query(update)
130
+
131
+ const select = sql`
132
+ SELECT ${sql.join(fieldsToRetrieve, sql`, `)}
133
+ FROM ${sql.ident(table)}
134
+ WHERE ${sql.ident(primaryKey)} = ${sql.value(input[primaryKey])}
135
+ `
136
+ const res = await db.query(select)
137
+ return res[0]
138
+ }
139
+
140
+ module.exports.updateOne = updateOne
141
+
142
+ async function deleteAll (db, sql, table, criteria, fieldsToRetrieve) {
143
+ let query = sql`
144
+ SELECT ${sql.join(fieldsToRetrieve, sql`, `)}
145
+ FROM ${sql.ident(table)}
146
+ `
147
+
148
+ /* istanbul ignore else */
149
+ if (criteria.length > 0) {
150
+ query = sql`${query} WHERE ${sql.join(criteria, sql` AND `)}`
151
+ }
152
+
153
+ const data = await db.query(query)
154
+
155
+ query = sql`
156
+ DELETE FROM ${sql.ident(table)}
157
+ `
158
+
159
+ /* istanbul ignore else */
160
+ if (criteria.length > 0) {
161
+ query = sql`${query} WHERE ${sql.join(criteria, sql` AND `)}`
162
+ }
163
+
164
+ await db.query(query)
165
+
166
+ return data
167
+ }
168
+
169
+ module.exports.deleteAll = deleteAll
package/lib/utils.js ADDED
@@ -0,0 +1,14 @@
1
+ 'use strict'
2
+
3
+ const { singularize } = require('inflected')
4
+ const camelcase = require('camelcase')
5
+
6
+ function toSingular (str) {
7
+ str = camelcase(singularize(str))
8
+ str = str[0].toUpperCase() + str.slice(1)
9
+ return str
10
+ }
11
+
12
+ module.exports = {
13
+ toSingular
14
+ }
package/mapper.d.ts ADDED
@@ -0,0 +1,308 @@
1
+ import { FastifyPluginAsync } from 'fastify'
2
+ import { SQL, SQLQuery } from '@databases/sql'
3
+
4
+ interface ILogger {
5
+ trace(): any,
6
+ error(): any
7
+ }
8
+
9
+ export interface Database {
10
+ /**
11
+ * An option that is true if a Postgres database is used.
12
+ */
13
+ isPg?: boolean,
14
+ /**
15
+ * An option that is true if a MariaDB database is used.
16
+ */
17
+ isMariaDB?: boolean,
18
+ /**
19
+ * An option that is true if a MySQL database is used.
20
+ */
21
+ isMySQL?: boolean,
22
+ /**
23
+ * An option that is true if a SQLite database is used.
24
+ */
25
+ isSQLite?: boolean,
26
+ /**
27
+ * Run an SQL Query and get a promise for an array of results. If your query contains multiple statements, only the results of the final statement are returned.
28
+ */
29
+ query(query: SQLQuery): Promise<any[]>,
30
+ /**
31
+ * Dispose the connection. Once this is called, any subsequent queries will fail.
32
+ */
33
+ dispose(): Promise<void>
34
+ }
35
+
36
+ export interface DBEntityField {
37
+ /**
38
+ * Field type in the database.
39
+ */
40
+ sqlType: string,
41
+ /**
42
+ * Camel cased field name.
43
+ */
44
+ camelcase: string,
45
+ /**
46
+ * An option that is true if field is a primary key.
47
+ */
48
+ primaryKey?: boolean,
49
+ /**
50
+ * An option that is true if field is a foreignKey key.
51
+ */
52
+ foreignKey?: boolean,
53
+ /**
54
+ * An option that is true if field is nullable.
55
+ */
56
+ isNullable: boolean,
57
+ /**
58
+ * An option that is true if auto timestamp enabled for this field.
59
+ */
60
+ autoTimestamp?: boolean
61
+ }
62
+
63
+ export interface WhereCondition {
64
+ [columnName: string]: {
65
+ /**
66
+ * Equal to value.
67
+ */
68
+ eq?: string,
69
+ /**
70
+ * Not equal to value.
71
+ */
72
+ neq?: string,
73
+ /**
74
+ * Greater than value.
75
+ */
76
+ gr?: any,
77
+ /**
78
+ * Greater than or equal to value.
79
+ */
80
+ gte?: any,
81
+ /**
82
+ * Less than value.
83
+ */
84
+ lt?: any,
85
+ /**
86
+ * Less than or equal to value.
87
+ */
88
+ lte?: any,
89
+ /**
90
+ * In values.
91
+ */
92
+ in?: any[],
93
+ /**
94
+ * Not in values.
95
+ */
96
+ nin?: any[]
97
+ }
98
+ }
99
+
100
+ interface Find<EntityFields> {
101
+ (options?: {
102
+ /**
103
+ * SQL where condition.
104
+ */
105
+ where?: WhereCondition,
106
+ /**
107
+ * List of fields to be returned for each object
108
+ */
109
+ fields?: string[],
110
+ /**
111
+ * Entity fields to order by.
112
+ */
113
+ orderBy?: Array<{ field: string, direction: 'asc' | 'desc' }>,
114
+ /**
115
+ * Number of entities to select.
116
+ */
117
+ limit?: number,
118
+ /**
119
+ * Number of entities to skip.
120
+ */
121
+ offset?: number,
122
+ }): Promise<Partial<EntityFields>[]>
123
+ }
124
+
125
+ interface Insert<EntityFields> {
126
+ (options: {
127
+ /**
128
+ * Entities to insert.
129
+ */
130
+ inputs: EntityFields[],
131
+ /**
132
+ * List of fields to be returned for each object
133
+ */
134
+ fields?: string[]
135
+ }): Promise<Partial<EntityFields>[]>
136
+ }
137
+
138
+ interface Save<EntityFields> {
139
+ (options: {
140
+ /**
141
+ * Entity to save.
142
+ */
143
+ input: EntityFields,
144
+ /**
145
+ * List of fields to be returned for each object
146
+ */
147
+ fields?: string[]
148
+ }): Promise<Partial<EntityFields>>
149
+ }
150
+
151
+ interface Delete<EntityFields> {
152
+ (options?: {
153
+ /**
154
+ * SQL where condition.
155
+ */
156
+ where: WhereCondition,
157
+ /**
158
+ * List of fields to be returned for each object
159
+ */
160
+ fields: string[]
161
+ }): Promise<Partial<EntityFields>[]>,
162
+ }
163
+
164
+ export interface Entity<EntityFields = any> {
165
+ /**
166
+ * The origin name of the database entity.
167
+ */
168
+ name: string,
169
+ /**
170
+ * The name of the database object in the singular.
171
+ */
172
+ singularName: string,
173
+ /**
174
+ * The plural name of the database entity.
175
+ */
176
+ pluralName: string,
177
+ /**
178
+ * The primary key of the database entity.
179
+ */
180
+ primaryKey: string,
181
+ /**
182
+ * The table of the database entity.
183
+ */
184
+ table: string,
185
+ /**
186
+ * Fields of the database entity.
187
+ */
188
+ fields: { [columnName: string]: DBEntityField },
189
+ /**
190
+ * Camel cased fields of the database entity.
191
+ */
192
+ camelCasedFields: { [columnName: string]: DBEntityField },
193
+ /**
194
+ * Relations with other database entities.
195
+ */
196
+ relations: any[],
197
+ /**
198
+ * Converts entities fields names to database column names.
199
+ */
200
+ fixInput(input: { [columnName: string]: any }): { [columnName: string]: any },
201
+ /**
202
+ * Converts database column names to entities fields names.
203
+ */
204
+ fixOutput(input: { [columnName: string]: any }): { [columnName: string]: any },
205
+ /**
206
+ * Selects matching entities from the database.
207
+ */
208
+ find: Find<EntityFields>,
209
+ /**
210
+ * Inserts entities to the database.
211
+ */
212
+ insert: Insert<EntityFields>,
213
+ /**
214
+ * Saves entity to the database.
215
+ */
216
+ save: Save<EntityFields>,
217
+ /**
218
+ * Deletes entities from the database.
219
+ */
220
+ delete: Delete<EntityFields>,
221
+ }
222
+
223
+
224
+ export interface EntityHooks<EntityFields = any> {
225
+ [entityName: string]: {
226
+ find?: Find<EntityFields>,
227
+ insert?: Insert<EntityFields>,
228
+ save?: Save<EntityFields>,
229
+ delete?: Delete<EntityFields>,
230
+ }
231
+ }
232
+
233
+ export interface SQLMapperPluginOptions {
234
+ /**
235
+ * Database connection string.
236
+ */
237
+ connectionString: string,
238
+ /**
239
+ * Set to true to enable auto timestamping for updated_at and inserted_at fields.
240
+ */
241
+ autoTimestamp?: boolean,
242
+ /**
243
+ * A logger object (like [Pino](https://getpino.io))
244
+ */
245
+ log?: ILogger,
246
+ /**
247
+ * Database table to ignore when mapping to entities.
248
+ */
249
+ ignore?: {
250
+ [tableName: string]: {
251
+ [columnName: string]: boolean
252
+ } | boolean
253
+ },
254
+ /**
255
+ * For each entity name (like `Page`) you can customize any of the entity API function. Your custom function will receive the original function as first parameter, and then all the other parameters passed to it.
256
+ */
257
+ hooks?: EntityHooks,
258
+ /**
259
+ * An async function that is called after the connection is established.
260
+ */
261
+ onDatabaseLoad?(db: Database, sql: SQL): any,
262
+ }
263
+
264
+ export interface Entities {
265
+ [entityName: string]: Entity
266
+ }
267
+
268
+ export interface SQLMapperPluginInterface {
269
+ /**
270
+ * A Database abstraction layer from [@Databases](https://www.atdatabases.org/)
271
+ */
272
+ db: Database,
273
+ /**
274
+ * The SQL builder from [@Databases](https://www.atdatabases.org/)
275
+ */
276
+ sql: SQL,
277
+ /**
278
+ * An object containing a key for each table found in the schema, with basic CRUD operations. See [entity.md](./entity.md) for details.
279
+ */
280
+ entities: Entities,
281
+ /**
282
+ * Adds hooks to the entity.
283
+ */
284
+ addEntityHooks(entityName: string, hooks: EntityHooks): any
285
+ }
286
+
287
+ declare module 'fastify' {
288
+ interface FastifyInstance {
289
+ platformatic: SQLMapperPluginInterface
290
+ }
291
+ }
292
+
293
+ /**
294
+ * Connects to the database and maps the tables to entities.
295
+ */
296
+ export function connect(options: SQLMapperPluginOptions): Promise<SQLMapperPluginInterface>
297
+ /**
298
+ * Fastify plugin that connects to the database and maps the tables to entities.
299
+ */
300
+ export const plugin: FastifyPluginAsync<SQLMapperPluginOptions>
301
+ export default plugin
302
+
303
+ /**
304
+ * An object that contains utility functions.
305
+ */
306
+ export module utils {
307
+ export function toSingular (str: string): string
308
+ }
package/mapper.js ADDED
@@ -0,0 +1,155 @@
1
+ 'use strict'
2
+
3
+ const buildEntity = require('./lib/entity')
4
+ const queriesFactory = require('./lib/queries')
5
+ const fp = require('fastify-plugin')
6
+
7
+ // Ignore the function as it is only used only for MySQL and PostgreSQL
8
+ /* istanbul ignore next */
9
+ async function buildConnection (log, createConnectionPool, connectionString) {
10
+ const db = await createConnectionPool({
11
+ connectionString,
12
+ bigIntMode: 'string',
13
+ onQueryStart: (_query, { text, values }) => {
14
+ log.trace({
15
+ query: {
16
+ text,
17
+ values
18
+ }
19
+ }, 'start query')
20
+ },
21
+ onQueryResults: (_query, { text }, results) => {
22
+ log.trace({
23
+ query: {
24
+ text,
25
+ results: results.length
26
+ }
27
+ }, 'end query')
28
+ },
29
+ onQueryError: (_query, { text }, err) => {
30
+ log.error({
31
+ query: {
32
+ text,
33
+ error: err.message
34
+ }
35
+ }, 'query error')
36
+ }
37
+ })
38
+ return db
39
+ }
40
+
41
+ async function connect ({ connectionString, log, onDatabaseLoad, ignore = {}, autoTimestamp = true, hooks = {} }) {
42
+ // TODO validate config using the schema
43
+ if (!connectionString) {
44
+ throw new Error('connectionString is required')
45
+ }
46
+
47
+ let queries
48
+ let sql
49
+ let db
50
+
51
+ /* istanbul ignore next */
52
+ if (connectionString.indexOf('postgres') === 0) {
53
+ const createConnectionPoolPg = require('@databases/pg')
54
+ db = await buildConnection(log, createConnectionPoolPg, connectionString)
55
+ sql = createConnectionPoolPg.sql
56
+ queries = queriesFactory.pg
57
+ db.isPg = true
58
+ } else if (connectionString.indexOf('mysql') === 0) {
59
+ const createConnectionPoolMysql = require('@databases/mysql')
60
+ db = await buildConnection(log, createConnectionPoolMysql, connectionString)
61
+ sql = createConnectionPoolMysql.sql
62
+ const version = (await db.query(sql`SELECT VERSION()`))[0]['VERSION()']
63
+ db.version = version
64
+ db.isMariaDB = version.indexOf('maria') !== -1
65
+ if (db.isMariaDB) {
66
+ queries = queriesFactory.mariadb
67
+ } else {
68
+ db.isMySQL = true
69
+ queries = queriesFactory.mysql
70
+ }
71
+ } else if (connectionString.indexOf('sqlite') === 0) {
72
+ const sqlite = require('@databases/sqlite')
73
+ const path = connectionString.replace('sqlite://', '')
74
+ db = sqlite(connectionString === 'sqlite://:memory:' ? undefined : path)
75
+ sql = sqlite.sql
76
+ queries = queriesFactory.sqlite
77
+ db.isSQLite = true
78
+ } else {
79
+ throw new Error('You must specify either postgres, mysql or sqlite as protocols')
80
+ }
81
+
82
+ const entities = {}
83
+
84
+ try {
85
+ /* istanbul ignore else */
86
+ if (typeof onDatabaseLoad === 'function') {
87
+ await onDatabaseLoad(db, sql)
88
+ }
89
+
90
+ const tables = await queries.listTables(db, sql)
91
+
92
+ for (const table of tables) {
93
+ // The following line is a safety net when developing this module,
94
+ // it should never happen.
95
+ /* istanbul ignore next */
96
+ if (typeof table !== 'string') {
97
+ throw new Error(`Table must be a string, got '${table}'`)
98
+ }
99
+ if (ignore[table] === true) {
100
+ continue
101
+ }
102
+
103
+ const entity = await buildEntity(db, sql, log, table, queries, autoTimestamp, ignore[table] || {})
104
+ // Check for primary key of all entities
105
+ if (!entity.primaryKey) {
106
+ throw new Error(`Cannot find primary key for ${entity.name} entity`)
107
+ }
108
+ entities[entity.singularName] = entity
109
+ if (hooks[entity.name]) {
110
+ addEntityHooks(entity.singularName, hooks[entity.name])
111
+ } else if (hooks[entity.singularName]) {
112
+ addEntityHooks(entity.singularName, hooks[entity.singularName])
113
+ }
114
+ }
115
+ } catch (err) /* istanbul ignore next */ {
116
+ db.dispose()
117
+ throw err
118
+ }
119
+
120
+ return {
121
+ db,
122
+ sql,
123
+ entities,
124
+ addEntityHooks
125
+ }
126
+
127
+ function addEntityHooks (entityName, hooks) {
128
+ const entity = entities[entityName]
129
+ if (!entity) {
130
+ throw new Error('Cannot find entity ' + entityName)
131
+ }
132
+ for (const key of Object.keys(hooks)) {
133
+ if (hooks[key] && entity[key]) {
134
+ entity[key] = hooks[key].bind(null, entity[key])
135
+ }
136
+ }
137
+ }
138
+ }
139
+
140
+ async function sqlMapper (app, opts) {
141
+ const mapper = await connect({
142
+ log: app.log,
143
+ ...opts
144
+ })
145
+
146
+ app.onClose(() => mapper.db.dispose())
147
+ // TODO this would need to be refactored as other plugins
148
+ // would need to use this same namespace
149
+ app.decorate('platformatic', mapper)
150
+ }
151
+
152
+ module.exports = fp(sqlMapper)
153
+ module.exports.connect = connect
154
+ module.exports.plugin = module.exports
155
+ module.exports.utils = require('./lib/utils')
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "@platformatic/sql-mapper",
3
+ "version": "0.0.23",
4
+ "description": "A data mapper utility for SQL databases",
5
+ "main": "mapper.js",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/platformatic/platformatic.git"
9
+ },
10
+ "author": "Matteo Collina <hello@matteocollina.com>",
11
+ "license": "Apache-2.0",
12
+ "bugs": {
13
+ "url": "https://github.com/platformatic/platformatic/issues"
14
+ },
15
+ "homepage": "https://github.com/platformatic/platformatic#readme",
16
+ "devDependencies": {
17
+ "fastify": "^4.5.3",
18
+ "snazzy": "^9.0.0",
19
+ "standard": "^17.0.0",
20
+ "tap": "^16.0.0",
21
+ "tsd": "^0.24.0"
22
+ },
23
+ "dependencies": {
24
+ "@databases/mysql": "^5.2.0",
25
+ "@databases/pg": "^5.3.0",
26
+ "@databases/sql": "^3.2.0",
27
+ "@databases/sqlite": "^4.0.0",
28
+ "camelcase": "^6.0.0",
29
+ "fastify-plugin": "^4.1.0",
30
+ "inflected": "^2.1.0"
31
+ },
32
+ "tsd": {
33
+ "directory": "test/types"
34
+ },
35
+ "scripts": {
36
+ "test": "standard | snazzy && npm run test:typescript && npm run test:postgresql && npm run test:mariadb && npm run test:mysql && npm run test:mysql8 && npm run test:sqlite",
37
+ "test:postgresql": "DB=postgresql tap test/*.test.js",
38
+ "test:mariadb": "DB=mariadb tap test/*.test.js",
39
+ "test:mysql": "DB=mysql tap test/*.test.js",
40
+ "test:mysql8": "DB=mysql8 tap test/*.test.js",
41
+ "test:sqlite": "DB=sqlite tap test/*.test.js",
42
+ "test:typescript": "tsd"
43
+ }
44
+ }