zero-http 0.2.4 → 0.3.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 (89) hide show
  1. package/README.md +1250 -283
  2. package/documentation/config/db.js +25 -0
  3. package/documentation/config/middleware.js +44 -0
  4. package/documentation/config/tls.js +12 -0
  5. package/documentation/controllers/cookies.js +34 -0
  6. package/documentation/controllers/tasks.js +108 -0
  7. package/documentation/full-server.js +28 -177
  8. package/documentation/models/Task.js +21 -0
  9. package/documentation/public/data/api.json +404 -24
  10. package/documentation/public/data/docs.json +1139 -0
  11. package/documentation/public/data/examples.json +80 -2
  12. package/documentation/public/data/options.json +23 -8
  13. package/documentation/public/index.html +138 -99
  14. package/documentation/public/scripts/app.js +1 -3
  15. package/documentation/public/scripts/custom-select.js +189 -0
  16. package/documentation/public/scripts/data-sections.js +233 -250
  17. package/documentation/public/scripts/playground.js +270 -0
  18. package/documentation/public/scripts/ui.js +4 -3
  19. package/documentation/public/styles.css +56 -5
  20. package/documentation/public/vendor/icons/compress.svg +17 -17
  21. package/documentation/public/vendor/icons/database.svg +21 -0
  22. package/documentation/public/vendor/icons/env.svg +21 -0
  23. package/documentation/public/vendor/icons/fetch.svg +11 -14
  24. package/documentation/public/vendor/icons/security.svg +15 -0
  25. package/documentation/public/vendor/icons/sse.svg +12 -13
  26. package/documentation/public/vendor/icons/static.svg +12 -26
  27. package/documentation/public/vendor/icons/stream.svg +7 -13
  28. package/documentation/public/vendor/icons/validate.svg +17 -0
  29. package/documentation/routes/api.js +41 -0
  30. package/documentation/routes/core.js +20 -0
  31. package/documentation/routes/playground.js +29 -0
  32. package/documentation/routes/realtime.js +49 -0
  33. package/documentation/routes/uploads.js +71 -0
  34. package/index.js +62 -1
  35. package/lib/app.js +200 -8
  36. package/lib/body/json.js +28 -5
  37. package/lib/body/multipart.js +29 -1
  38. package/lib/body/raw.js +1 -1
  39. package/lib/body/sendError.js +1 -0
  40. package/lib/body/text.js +1 -1
  41. package/lib/body/typeMatch.js +6 -2
  42. package/lib/body/urlencoded.js +5 -2
  43. package/lib/debug.js +345 -0
  44. package/lib/env/index.js +440 -0
  45. package/lib/errors.js +231 -0
  46. package/lib/http/request.js +219 -1
  47. package/lib/http/response.js +410 -6
  48. package/lib/middleware/compress.js +39 -6
  49. package/lib/middleware/cookieParser.js +237 -0
  50. package/lib/middleware/cors.js +13 -2
  51. package/lib/middleware/csrf.js +135 -0
  52. package/lib/middleware/errorHandler.js +90 -0
  53. package/lib/middleware/helmet.js +176 -0
  54. package/lib/middleware/index.js +7 -2
  55. package/lib/middleware/rateLimit.js +12 -1
  56. package/lib/middleware/requestId.js +54 -0
  57. package/lib/middleware/static.js +95 -11
  58. package/lib/middleware/timeout.js +72 -0
  59. package/lib/middleware/validator.js +257 -0
  60. package/lib/orm/adapters/json.js +215 -0
  61. package/lib/orm/adapters/memory.js +383 -0
  62. package/lib/orm/adapters/mongo.js +444 -0
  63. package/lib/orm/adapters/mysql.js +272 -0
  64. package/lib/orm/adapters/postgres.js +394 -0
  65. package/lib/orm/adapters/sql-base.js +142 -0
  66. package/lib/orm/adapters/sqlite.js +311 -0
  67. package/lib/orm/index.js +276 -0
  68. package/lib/orm/model.js +895 -0
  69. package/lib/orm/query.js +807 -0
  70. package/lib/orm/schema.js +172 -0
  71. package/lib/router/index.js +136 -47
  72. package/lib/sse/stream.js +15 -3
  73. package/lib/ws/connection.js +19 -3
  74. package/lib/ws/handshake.js +3 -0
  75. package/lib/ws/index.js +3 -1
  76. package/lib/ws/room.js +222 -0
  77. package/package.json +15 -5
  78. package/types/app.d.ts +120 -0
  79. package/types/env.d.ts +80 -0
  80. package/types/errors.d.ts +147 -0
  81. package/types/fetch.d.ts +43 -0
  82. package/types/index.d.ts +135 -0
  83. package/types/middleware.d.ts +292 -0
  84. package/types/orm.d.ts +610 -0
  85. package/types/request.d.ts +99 -0
  86. package/types/response.d.ts +142 -0
  87. package/types/router.d.ts +78 -0
  88. package/types/sse.d.ts +78 -0
  89. package/types/websocket.d.ts +119 -0
@@ -0,0 +1,394 @@
1
+ /**
2
+ * @module orm/adapters/postgres
3
+ * @description PostgreSQL adapter using the optional `pg` driver.
4
+ * Requires: `npm install pg`
5
+ *
6
+ * @example
7
+ * const db = Database.connect('postgres', {
8
+ * host: '127.0.0.1', user: 'postgres', password: '', database: 'myapp',
9
+ * });
10
+ */
11
+ const BaseSqlAdapter = require('./sql-base');
12
+
13
+ class PostgresAdapter extends BaseSqlAdapter
14
+ {
15
+ /**
16
+ * @param {object} options
17
+ * @param {string} [options.host='localhost'] - Server hostname.
18
+ * @param {number} [options.port=5432] - Server port.
19
+ * @param {string} [options.user] - Database user.
20
+ * @param {string} [options.password] - Database password.
21
+ * @param {string} options.database - Database name.
22
+ * @param {number} [options.max=10] - Max pool size.
23
+ * @param {number} [options.idleTimeoutMillis=10000] - Idle client timeout.
24
+ * @param {number} [options.connectionTimeoutMillis=0] - Connection timeout (0 = no limit).
25
+ * @param {boolean|object} [options.ssl] - SSL mode or TLS options.
26
+ * @param {string} [options.connectionString] - Full connection URI (overrides individual settings).
27
+ * @param {string} [options.application_name] - Identify the app in pg_stat_activity.
28
+ * @param {number} [options.statement_timeout] - Statement timeout in ms.
29
+ */
30
+ constructor(options = {})
31
+ {
32
+ super();
33
+ let pg;
34
+ try { pg = require('pg'); }
35
+ catch (e)
36
+ {
37
+ throw new Error(
38
+ 'PostgreSQL adapter requires "pg" package.\n' +
39
+ 'Install it with: npm install pg'
40
+ );
41
+ }
42
+ this._pool = new pg.Pool({ max: 10, ...options });
43
+ this._options = options;
44
+ }
45
+
46
+ _typeMap(colDef)
47
+ {
48
+ const map = {
49
+ string: `VARCHAR(${colDef.maxLength || 255})`, text: 'TEXT',
50
+ integer: 'INTEGER', float: 'DOUBLE PRECISION', boolean: 'BOOLEAN',
51
+ date: 'DATE', datetime: 'TIMESTAMPTZ', json: 'JSONB', blob: 'BYTEA',
52
+ uuid: 'UUID',
53
+ };
54
+ return map[colDef.type] || 'TEXT';
55
+ }
56
+
57
+ /**
58
+ * PostgreSQL uses $1, $2, ... style parameters.
59
+ * Override the base class WHERE builders.
60
+ */
61
+
62
+ _buildWherePg(conditions, startIdx = 1)
63
+ {
64
+ if (!conditions || Object.keys(conditions).length === 0)
65
+ return { clause: '', values: [], nextIdx: startIdx };
66
+ const parts = [];
67
+ const values = [];
68
+ let idx = startIdx;
69
+ for (const [k, v] of Object.entries(conditions))
70
+ {
71
+ if (v === null) { parts.push(`"${k}" IS NULL`); }
72
+ else { parts.push(`"${k}" = $${idx++}`); values.push(this._toSqlValue(v)); }
73
+ }
74
+ return { clause: ' WHERE ' + parts.join(' AND '), values, nextIdx: idx };
75
+ }
76
+
77
+ _buildWhereFromChainPg(where, startIdx = 1)
78
+ {
79
+ if (!where || where.length === 0) return { clause: '', values: [], nextIdx: startIdx };
80
+ const parts = [];
81
+ const values = [];
82
+ let idx = startIdx;
83
+
84
+ for (let i = 0; i < where.length; i++)
85
+ {
86
+ const w = where[i];
87
+
88
+ // Handle raw WHERE clauses (from whereRaw) — convert ? to $N
89
+ if (w.raw)
90
+ {
91
+ let rawExpr = w.raw;
92
+ if (w.params)
93
+ {
94
+ for (const p of w.params)
95
+ {
96
+ rawExpr = rawExpr.replace('?', `$${idx++}`);
97
+ values.push(p);
98
+ }
99
+ }
100
+ if (i === 0) parts.push(rawExpr);
101
+ else parts.push(`${w.logic} ${rawExpr}`);
102
+ continue;
103
+ }
104
+
105
+ const { field, op, value, logic } = w;
106
+ let expr;
107
+
108
+ if (op === 'IS NULL') expr = `"${field}" IS NULL`;
109
+ else if (op === 'IS NOT NULL') expr = `"${field}" IS NOT NULL`;
110
+ else if (op === 'IN' || op === 'NOT IN')
111
+ {
112
+ if (!Array.isArray(value) || value.length === 0)
113
+ expr = op === 'IN' ? '0=1' : '1=1';
114
+ else
115
+ {
116
+ const placeholders = value.map(() => `$${idx++}`).join(', ');
117
+ expr = `"${field}" ${op} (${placeholders})`;
118
+ values.push(...value.map(v => this._toSqlValue(v)));
119
+ }
120
+ }
121
+ else if (op === 'BETWEEN')
122
+ {
123
+ expr = `"${field}" BETWEEN $${idx++} AND $${idx++}`;
124
+ values.push(this._toSqlValue(value[0]), this._toSqlValue(value[1]));
125
+ }
126
+ else
127
+ {
128
+ expr = `"${field}" ${op} $${idx++}`;
129
+ values.push(this._toSqlValue(value));
130
+ }
131
+
132
+ if (i === 0) parts.push(expr);
133
+ else parts.push(`${logic} ${expr}`);
134
+ }
135
+
136
+ return { clause: ' WHERE ' + parts.join(' '), values, nextIdx: idx };
137
+ }
138
+
139
+ async createTable(table, schema)
140
+ {
141
+ const cols = [];
142
+ for (const [name, def] of Object.entries(schema))
143
+ {
144
+ let line = `"${name}" ${this._typeMap(def)}`;
145
+ if (def.primaryKey && def.autoIncrement)
146
+ {
147
+ line = `"${name}" SERIAL PRIMARY KEY`;
148
+ }
149
+ else
150
+ {
151
+ if (def.primaryKey) line += ' PRIMARY KEY';
152
+ if (def.required && !def.primaryKey) line += ' NOT NULL';
153
+ if (def.unique) line += ' UNIQUE';
154
+ if (def.default !== undefined && typeof def.default !== 'function')
155
+ line += ` DEFAULT ${this._sqlDefault(def.default)}`;
156
+ }
157
+ cols.push(line);
158
+ }
159
+ await this._pool.query(`CREATE TABLE IF NOT EXISTS "${table}" (${cols.join(', ')})`);
160
+ }
161
+
162
+ async dropTable(table)
163
+ {
164
+ await this._pool.query(`DROP TABLE IF EXISTS "${table}"`);
165
+ }
166
+
167
+ async insert(table, data)
168
+ {
169
+ const keys = Object.keys(data);
170
+ const values = keys.map(k => this._toSqlValue(data[k]));
171
+ const placeholders = keys.map((_, i) => `$${i + 1}`).join(', ');
172
+ const sql = `INSERT INTO "${table}" (${keys.map(k => `"${k}"`).join(', ')}) VALUES (${placeholders}) RETURNING *`;
173
+ const { rows } = await this._pool.query(sql, values);
174
+ return rows[0] || { ...data };
175
+ }
176
+
177
+ async update(table, pk, pkVal, data)
178
+ {
179
+ const keys = Object.keys(data);
180
+ const values = keys.map(k => this._toSqlValue(data[k]));
181
+ const sets = keys.map((k, i) => `"${k}" = $${i + 1}`).join(', ');
182
+ values.push(pkVal);
183
+ await this._pool.query(`UPDATE "${table}" SET ${sets} WHERE "${pk}" = $${values.length}`, values);
184
+ }
185
+
186
+ async updateWhere(table, conditions, data)
187
+ {
188
+ const keys = Object.keys(data);
189
+ const values = keys.map(k => this._toSqlValue(data[k]));
190
+ const sets = keys.map((k, i) => `"${k}" = $${i + 1}`).join(', ');
191
+ const { clause, values: whereVals } = this._buildWherePg(conditions, keys.length + 1);
192
+ values.push(...whereVals);
193
+ const { rowCount } = await this._pool.query(`UPDATE "${table}" SET ${sets}${clause}`, values);
194
+ return rowCount;
195
+ }
196
+
197
+ async remove(table, pk, pkVal)
198
+ {
199
+ await this._pool.query(`DELETE FROM "${table}" WHERE "${pk}" = $1`, [pkVal]);
200
+ }
201
+
202
+ async deleteWhere(table, conditions)
203
+ {
204
+ const { clause, values } = this._buildWherePg(conditions);
205
+ const { rowCount } = await this._pool.query(`DELETE FROM "${table}"${clause}`, values);
206
+ return rowCount;
207
+ }
208
+
209
+ async execute(descriptor)
210
+ {
211
+ const { action, table, fields, where, orderBy, limit, offset, distinct } = descriptor;
212
+
213
+ if (action === 'count')
214
+ {
215
+ const { clause, values } = this._buildWhereFromChainPg(where);
216
+ const { rows } = await this._pool.query(`SELECT COUNT(*) as count FROM "${table}"${clause}`, values);
217
+ return parseInt(rows[0].count, 10);
218
+ }
219
+
220
+ const selectFields = fields && fields.length ? fields.map(f => `"${f}"`).join(', ') : '*';
221
+ const distinctStr = distinct ? 'DISTINCT ' : '';
222
+ let sql = `SELECT ${distinctStr}${selectFields} FROM "${table}"`;
223
+ const values = [];
224
+ let paramIdx = 1;
225
+
226
+ if (where && where.length)
227
+ {
228
+ const { clause, values: wv, nextIdx } = this._buildWhereFromChainPg(where, paramIdx);
229
+ sql += clause;
230
+ values.push(...wv);
231
+ paramIdx = nextIdx;
232
+ }
233
+
234
+ if (orderBy && orderBy.length)
235
+ sql += ' ORDER BY ' + orderBy.map(o => `"${o.field}" ${o.dir}`).join(', ');
236
+ if (limit !== null && limit !== undefined)
237
+ {
238
+ sql += ` LIMIT $${paramIdx++}`;
239
+ values.push(limit);
240
+ }
241
+ if (offset !== null && offset !== undefined)
242
+ {
243
+ sql += ` OFFSET $${paramIdx++}`;
244
+ values.push(offset);
245
+ }
246
+
247
+ const { rows } = await this._pool.query(sql, values);
248
+ return rows;
249
+ }
250
+
251
+ async close() { await this._pool.end(); }
252
+ async raw(sql, ...params) { const { rows } = await this._pool.query(sql, params); return rows; }
253
+
254
+ async transaction(fn)
255
+ {
256
+ const client = await this._pool.connect();
257
+ try
258
+ {
259
+ await client.query('BEGIN');
260
+ const result = await fn(client);
261
+ await client.query('COMMIT');
262
+ return result;
263
+ }
264
+ catch (e) { await client.query('ROLLBACK'); throw e; }
265
+ finally { client.release(); }
266
+ }
267
+
268
+ // -- PostgreSQL Utilities ----------------------------
269
+
270
+ /**
271
+ * List all user-created tables in the current schema.
272
+ * @param {string} [schema='public'] - Schema name.
273
+ * @returns {Promise<string[]>}
274
+ */
275
+ async tables(schema = 'public')
276
+ {
277
+ const { rows } = await this._pool.query(
278
+ `SELECT tablename FROM pg_catalog.pg_tables WHERE schemaname = $1 ORDER BY tablename`,
279
+ [schema]
280
+ );
281
+ return rows.map(r => r.tablename);
282
+ }
283
+
284
+ /**
285
+ * Get column information for a table.
286
+ * @param {string} table
287
+ * @param {string} [schema='public']
288
+ * @returns {Promise<Array<{ column_name: string, data_type: string, is_nullable: string, column_default: string }>>}
289
+ */
290
+ async columns(table, schema = 'public')
291
+ {
292
+ const { rows } = await this._pool.query(
293
+ `SELECT column_name, data_type, is_nullable, column_default
294
+ FROM information_schema.columns
295
+ WHERE table_schema = $1 AND table_name = $2
296
+ ORDER BY ordinal_position`,
297
+ [schema, table]
298
+ );
299
+ return rows;
300
+ }
301
+
302
+ /**
303
+ * Get the current database size in bytes.
304
+ * @returns {Promise<number>}
305
+ */
306
+ async databaseSize()
307
+ {
308
+ const { rows } = await this._pool.query('SELECT pg_database_size(current_database()) AS size');
309
+ return Number(rows[0].size) || 0;
310
+ }
311
+
312
+ /**
313
+ * Get the row count for a table (estimated for large tables, exact for small ones).
314
+ * @param {string} table
315
+ * @returns {Promise<number>}
316
+ */
317
+ async tableSize(table)
318
+ {
319
+ const { rows } = await this._pool.query(
320
+ `SELECT pg_total_relation_size($1) AS size`, [table]
321
+ );
322
+ return Number(rows[0].size) || 0;
323
+ }
324
+
325
+ /**
326
+ * Get connection pool status.
327
+ * @returns {{ total: number, idle: number, waiting: number }}
328
+ */
329
+ poolStatus()
330
+ {
331
+ return {
332
+ total: this._pool.totalCount,
333
+ idle: this._pool.idleCount,
334
+ waiting: this._pool.waitingCount,
335
+ };
336
+ }
337
+
338
+ /**
339
+ * Get the PostgreSQL server version string.
340
+ * @returns {Promise<string>}
341
+ */
342
+ async version()
343
+ {
344
+ const { rows } = await this._pool.query('SELECT version() AS ver');
345
+ return rows[0].ver;
346
+ }
347
+
348
+ /**
349
+ * Ping the database to check connectivity.
350
+ * @returns {Promise<boolean>}
351
+ */
352
+ async ping()
353
+ {
354
+ try
355
+ {
356
+ await this._pool.query('SELECT 1');
357
+ return true;
358
+ }
359
+ catch { return false; }
360
+ }
361
+
362
+ /**
363
+ * Execute a raw statement that doesn't return rows (INSERT, UPDATE, DDL).
364
+ * @param {string} sql
365
+ * @param {...*} params
366
+ * @returns {Promise<{ rowCount: number }>}
367
+ */
368
+ async exec(sql, ...params)
369
+ {
370
+ const { rowCount } = await this._pool.query(sql, params);
371
+ return { rowCount: rowCount || 0 };
372
+ }
373
+
374
+ /**
375
+ * Run a LISTEN/NOTIFY style query. Useful for subscribing to PG notifications.
376
+ * @param {string} channel
377
+ * @param {Function} callback - Receives { channel, payload }.
378
+ * @returns {Promise<Function>} Unlisten function.
379
+ */
380
+ async listen(channel, callback)
381
+ {
382
+ const client = await this._pool.connect();
383
+ await client.query(`LISTEN ${channel}`);
384
+ client.on('notification', callback);
385
+ return async () =>
386
+ {
387
+ await client.query(`UNLISTEN ${channel}`);
388
+ client.removeListener('notification', callback);
389
+ client.release();
390
+ };
391
+ }
392
+ }
393
+
394
+ module.exports = PostgresAdapter;
@@ -0,0 +1,142 @@
1
+ /**
2
+ * @module orm/adapters/sql-base
3
+ * @description Base class for SQL adapters. Provides shared query-building
4
+ * utilities, parameterised queries (SQL injection safe), and
5
+ * type mapping helpers.
6
+ */
7
+
8
+ class BaseSqlAdapter
9
+ {
10
+ /**
11
+ * Build a WHERE clause from simple { key: value } conditions.
12
+ * Uses parameterised queries to prevent SQL injection.
13
+ *
14
+ * @param {object} conditions
15
+ * @returns {{ clause: string, values: Array }}
16
+ * @protected
17
+ */
18
+ _buildWhere(conditions)
19
+ {
20
+ if (!conditions || Object.keys(conditions).length === 0)
21
+ {
22
+ return { clause: '', values: [] };
23
+ }
24
+ const parts = [];
25
+ const values = [];
26
+ for (const [k, v] of Object.entries(conditions))
27
+ {
28
+ if (v === null)
29
+ {
30
+ parts.push(`"${k}" IS NULL`);
31
+ }
32
+ else
33
+ {
34
+ parts.push(`"${k}" = ?`);
35
+ values.push(this._toSqlValue(v));
36
+ }
37
+ }
38
+ return { clause: ' WHERE ' + parts.join(' AND '), values };
39
+ }
40
+
41
+ /**
42
+ * Build a WHERE clause from the Query builder's where chain.
43
+ * Supports operators: =, !=, >, <, >=, <=, LIKE, IN, NOT IN, BETWEEN, IS NULL, IS NOT NULL.
44
+ *
45
+ * @param {Array} where - Array of { field, op, value, logic } objects.
46
+ * @returns {{ clause: string, values: Array }}
47
+ * @protected
48
+ */
49
+ _buildWhereFromChain(where)
50
+ {
51
+ if (!where || where.length === 0) return { clause: '', values: [] };
52
+
53
+ const parts = [];
54
+ const values = [];
55
+
56
+ for (let i = 0; i < where.length; i++)
57
+ {
58
+ const w = where[i];
59
+
60
+ // Handle raw WHERE clauses (from whereRaw)
61
+ if (w.raw)
62
+ {
63
+ const expr = w.raw;
64
+ if (w.params) values.push(...w.params);
65
+ if (i === 0) parts.push(expr);
66
+ else parts.push(`${w.logic} ${expr}`);
67
+ continue;
68
+ }
69
+
70
+ const { field, op, value, logic } = w;
71
+
72
+ let expr;
73
+ if (op === 'IS NULL')
74
+ {
75
+ expr = `"${field}" IS NULL`;
76
+ }
77
+ else if (op === 'IS NOT NULL')
78
+ {
79
+ expr = `"${field}" IS NOT NULL`;
80
+ }
81
+ else if (op === 'IN' || op === 'NOT IN')
82
+ {
83
+ if (!Array.isArray(value) || value.length === 0)
84
+ {
85
+ expr = op === 'IN' ? '0' : '1'; // IN () → false, NOT IN () → true
86
+ }
87
+ else
88
+ {
89
+ const placeholders = value.map(() => '?').join(', ');
90
+ expr = `"${field}" ${op} (${placeholders})`;
91
+ values.push(...value.map(v => this._toSqlValue(v)));
92
+ }
93
+ }
94
+ else if (op === 'BETWEEN')
95
+ {
96
+ expr = `"${field}" BETWEEN ? AND ?`;
97
+ values.push(this._toSqlValue(value[0]), this._toSqlValue(value[1]));
98
+ }
99
+ else
100
+ {
101
+ expr = `"${field}" ${op} ?`;
102
+ values.push(this._toSqlValue(value));
103
+ }
104
+
105
+ if (i === 0) parts.push(expr);
106
+ else parts.push(`${logic} ${expr}`);
107
+ }
108
+
109
+ return { clause: ' WHERE ' + parts.join(' '), values };
110
+ }
111
+
112
+ /**
113
+ * Convert a JS value to a SQL-safe value.
114
+ * @param {*} value
115
+ * @returns {*}
116
+ * @protected
117
+ */
118
+ _toSqlValue(value)
119
+ {
120
+ if (value instanceof Date) return value.toISOString();
121
+ if (typeof value === 'boolean') return value ? 1 : 0;
122
+ if (typeof value === 'object' && value !== null) return JSON.stringify(value);
123
+ return value;
124
+ }
125
+
126
+ /**
127
+ * Format a default value for SQL DDL.
128
+ * @param {*} val
129
+ * @returns {string}
130
+ * @protected
131
+ */
132
+ _sqlDefault(val)
133
+ {
134
+ if (val === null) return 'NULL';
135
+ if (typeof val === 'number') return String(val);
136
+ if (typeof val === 'boolean') return val ? '1' : '0';
137
+ if (typeof val === 'string') return `'${val.replace(/'/g, "''")}'`;
138
+ return 'NULL';
139
+ }
140
+ }
141
+
142
+ module.exports = BaseSqlAdapter;