@remix-run/data-table-postgres 0.3.1 → 0.5.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.
- package/README.md +41 -17
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/lib/database.d.ts +35 -0
- package/dist/lib/database.d.ts.map +1 -0
- package/dist/lib/database.js +31 -0
- package/dist/lib/{adapter.d.ts → driver.d.ts} +50 -44
- package/dist/lib/driver.d.ts.map +1 -0
- package/dist/lib/driver.js +577 -0
- package/dist/lib/sql-compiler.d.ts +2 -1
- package/dist/lib/sql-compiler.d.ts.map +1 -1
- package/dist/lib/sql-compiler.js +179 -13
- package/package.json +12 -12
- package/src/index.ts +3 -1
- package/src/lib/database.ts +45 -0
- package/src/lib/driver.ts +760 -0
- package/src/lib/sql-compiler.ts +235 -15
- package/dist/lib/adapter.d.ts.map +0 -1
- package/dist/lib/adapter.js +0 -735
- package/src/lib/adapter.ts +0 -925
package/dist/lib/sql-compiler.js
CHANGED
|
@@ -20,8 +20,8 @@ export function compilePostgresOperation(operation) {
|
|
|
20
20
|
compileGroupByClause(operation.groupBy) +
|
|
21
21
|
compileHavingClause(operation.having, context) +
|
|
22
22
|
compileOrderByClause(operation.orderBy) +
|
|
23
|
-
compileLimitClause(operation.limit) +
|
|
24
|
-
compileOffsetClause(operation.offset);
|
|
23
|
+
compileLimitClause(operation.limit, context) +
|
|
24
|
+
compileOffsetClause(operation.offset, context);
|
|
25
25
|
return {
|
|
26
26
|
text,
|
|
27
27
|
values: context.values,
|
|
@@ -185,17 +185,183 @@ function compileRawOperation(statement) {
|
|
|
185
185
|
values: [...statement.values],
|
|
186
186
|
};
|
|
187
187
|
}
|
|
188
|
-
let index = 1;
|
|
189
|
-
let text = statement.text.replace(/\?/g, function replaceParameter() {
|
|
190
|
-
let placeholder = '$' + String(index);
|
|
191
|
-
index += 1;
|
|
192
|
-
return placeholder;
|
|
193
|
-
});
|
|
194
188
|
return {
|
|
195
|
-
text,
|
|
189
|
+
text: rewriteRawPlaceholders(statement.text),
|
|
196
190
|
values: [...statement.values],
|
|
197
191
|
};
|
|
198
192
|
}
|
|
193
|
+
function rewriteRawPlaceholders(text) {
|
|
194
|
+
let output = '';
|
|
195
|
+
let index = 0;
|
|
196
|
+
let placeholderIndex = 1;
|
|
197
|
+
while (index < text.length) {
|
|
198
|
+
let char = text[index];
|
|
199
|
+
if (char === '?') {
|
|
200
|
+
output += '$' + String(placeholderIndex);
|
|
201
|
+
placeholderIndex += 1;
|
|
202
|
+
index += 1;
|
|
203
|
+
continue;
|
|
204
|
+
}
|
|
205
|
+
if ((char === 'E' || char === 'e') && text[index + 1] === "'") {
|
|
206
|
+
let end = scanEscapeSingleQuotedString(text, index + 1);
|
|
207
|
+
output += text.slice(index, end);
|
|
208
|
+
index = end;
|
|
209
|
+
continue;
|
|
210
|
+
}
|
|
211
|
+
if (char === "'") {
|
|
212
|
+
let end = scanSingleQuotedString(text, index);
|
|
213
|
+
output += text.slice(index, end);
|
|
214
|
+
index = end;
|
|
215
|
+
continue;
|
|
216
|
+
}
|
|
217
|
+
if (char === '"') {
|
|
218
|
+
let end = scanDoubleQuotedIdentifier(text, index);
|
|
219
|
+
output += text.slice(index, end);
|
|
220
|
+
index = end;
|
|
221
|
+
continue;
|
|
222
|
+
}
|
|
223
|
+
if (text.startsWith('--', index)) {
|
|
224
|
+
let end = scanLineComment(text, index);
|
|
225
|
+
output += text.slice(index, end);
|
|
226
|
+
index = end;
|
|
227
|
+
continue;
|
|
228
|
+
}
|
|
229
|
+
if (text.startsWith('/*', index)) {
|
|
230
|
+
let end = scanBlockComment(text, index);
|
|
231
|
+
output += text.slice(index, end);
|
|
232
|
+
index = end;
|
|
233
|
+
continue;
|
|
234
|
+
}
|
|
235
|
+
let dollarQuotedStringEnd = scanDollarQuotedString(text, index);
|
|
236
|
+
if (dollarQuotedStringEnd !== undefined) {
|
|
237
|
+
output += text.slice(index, dollarQuotedStringEnd);
|
|
238
|
+
index = dollarQuotedStringEnd;
|
|
239
|
+
continue;
|
|
240
|
+
}
|
|
241
|
+
output += char;
|
|
242
|
+
index += 1;
|
|
243
|
+
}
|
|
244
|
+
return output;
|
|
245
|
+
}
|
|
246
|
+
function scanSingleQuotedString(text, start) {
|
|
247
|
+
let index = start + 1;
|
|
248
|
+
while (index < text.length) {
|
|
249
|
+
if (text[index] === "'") {
|
|
250
|
+
if (text[index + 1] === "'") {
|
|
251
|
+
index += 2;
|
|
252
|
+
continue;
|
|
253
|
+
}
|
|
254
|
+
return index + 1;
|
|
255
|
+
}
|
|
256
|
+
index += 1;
|
|
257
|
+
}
|
|
258
|
+
return text.length;
|
|
259
|
+
}
|
|
260
|
+
function scanEscapeSingleQuotedString(text, start) {
|
|
261
|
+
let index = start + 1;
|
|
262
|
+
while (index < text.length) {
|
|
263
|
+
if (text[index] === '\\') {
|
|
264
|
+
index += 2;
|
|
265
|
+
continue;
|
|
266
|
+
}
|
|
267
|
+
if (text[index] === "'") {
|
|
268
|
+
if (text[index + 1] === "'") {
|
|
269
|
+
index += 2;
|
|
270
|
+
continue;
|
|
271
|
+
}
|
|
272
|
+
return index + 1;
|
|
273
|
+
}
|
|
274
|
+
index += 1;
|
|
275
|
+
}
|
|
276
|
+
return text.length;
|
|
277
|
+
}
|
|
278
|
+
function scanDoubleQuotedIdentifier(text, start) {
|
|
279
|
+
let index = start + 1;
|
|
280
|
+
while (index < text.length) {
|
|
281
|
+
if (text[index] === '"') {
|
|
282
|
+
if (text[index + 1] === '"') {
|
|
283
|
+
index += 2;
|
|
284
|
+
continue;
|
|
285
|
+
}
|
|
286
|
+
return index + 1;
|
|
287
|
+
}
|
|
288
|
+
index += 1;
|
|
289
|
+
}
|
|
290
|
+
return text.length;
|
|
291
|
+
}
|
|
292
|
+
function scanLineComment(text, start) {
|
|
293
|
+
let index = start + 2;
|
|
294
|
+
while (index < text.length && text[index] !== '\n' && text[index] !== '\r') {
|
|
295
|
+
index += 1;
|
|
296
|
+
}
|
|
297
|
+
return index;
|
|
298
|
+
}
|
|
299
|
+
function scanBlockComment(text, start) {
|
|
300
|
+
let index = start + 2;
|
|
301
|
+
let depth = 1;
|
|
302
|
+
while (index < text.length && depth > 0) {
|
|
303
|
+
if (text.startsWith('/*', index)) {
|
|
304
|
+
depth += 1;
|
|
305
|
+
index += 2;
|
|
306
|
+
continue;
|
|
307
|
+
}
|
|
308
|
+
if (text.startsWith('*/', index)) {
|
|
309
|
+
depth -= 1;
|
|
310
|
+
index += 2;
|
|
311
|
+
continue;
|
|
312
|
+
}
|
|
313
|
+
index += 1;
|
|
314
|
+
}
|
|
315
|
+
return index;
|
|
316
|
+
}
|
|
317
|
+
function scanDollarQuotedString(text, start) {
|
|
318
|
+
let delimiter = getDollarQuoteDelimiter(text, start);
|
|
319
|
+
if (delimiter === undefined) {
|
|
320
|
+
return undefined;
|
|
321
|
+
}
|
|
322
|
+
let end = text.indexOf(delimiter, start + delimiter.length);
|
|
323
|
+
return end === -1 ? text.length : end + delimiter.length;
|
|
324
|
+
}
|
|
325
|
+
function getDollarQuoteDelimiter(text, start) {
|
|
326
|
+
if (text[start] !== '$') {
|
|
327
|
+
return undefined;
|
|
328
|
+
}
|
|
329
|
+
let index = start + 1;
|
|
330
|
+
if (text[index] === '$') {
|
|
331
|
+
return '$$';
|
|
332
|
+
}
|
|
333
|
+
if (!isDollarQuoteTagStart(text[index])) {
|
|
334
|
+
return undefined;
|
|
335
|
+
}
|
|
336
|
+
index += 1;
|
|
337
|
+
while (index < text.length && isDollarQuoteTagPart(text[index])) {
|
|
338
|
+
index += 1;
|
|
339
|
+
}
|
|
340
|
+
if (text[index] !== '$') {
|
|
341
|
+
return undefined;
|
|
342
|
+
}
|
|
343
|
+
return text.slice(start, index + 1);
|
|
344
|
+
}
|
|
345
|
+
function isDollarQuoteTagStart(char) {
|
|
346
|
+
if (char === undefined) {
|
|
347
|
+
return false;
|
|
348
|
+
}
|
|
349
|
+
return isAsciiLetter(char) || char === '_';
|
|
350
|
+
}
|
|
351
|
+
function isDollarQuoteTagPart(char) {
|
|
352
|
+
if (char === undefined) {
|
|
353
|
+
return false;
|
|
354
|
+
}
|
|
355
|
+
return isDollarQuoteTagStart(char) || isAsciiDigit(char);
|
|
356
|
+
}
|
|
357
|
+
function isAsciiLetter(char) {
|
|
358
|
+
let code = char.charCodeAt(0);
|
|
359
|
+
return (code >= 65 && code <= 90) || (code >= 97 && code <= 122);
|
|
360
|
+
}
|
|
361
|
+
function isAsciiDigit(char) {
|
|
362
|
+
let code = char.charCodeAt(0);
|
|
363
|
+
return code >= 48 && code <= 57;
|
|
364
|
+
}
|
|
199
365
|
function compileFromClause(table, joins, context) {
|
|
200
366
|
let output = ' from ' + quotePath(getTableName(table));
|
|
201
367
|
for (let join of joins) {
|
|
@@ -242,17 +408,17 @@ function compileOrderByClause(orderBy) {
|
|
|
242
408
|
.map((clause) => quotePath(clause.column) + ' ' + clause.direction.toUpperCase())
|
|
243
409
|
.join(', '));
|
|
244
410
|
}
|
|
245
|
-
function compileLimitClause(limit) {
|
|
411
|
+
function compileLimitClause(limit, context) {
|
|
246
412
|
if (limit === undefined) {
|
|
247
413
|
return '';
|
|
248
414
|
}
|
|
249
|
-
return ' limit ' +
|
|
415
|
+
return ' limit ' + pushValue(context, limit);
|
|
250
416
|
}
|
|
251
|
-
function compileOffsetClause(offset) {
|
|
417
|
+
function compileOffsetClause(offset, context) {
|
|
252
418
|
if (offset === undefined) {
|
|
253
419
|
return '';
|
|
254
420
|
}
|
|
255
|
-
return ' offset ' +
|
|
421
|
+
return ' offset ' + pushValue(context, offset);
|
|
256
422
|
}
|
|
257
423
|
function compileReturningClause(returning) {
|
|
258
424
|
if (!returning) {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@remix-run/data-table-postgres",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "PostgreSQL
|
|
3
|
+
"version": "0.5.0",
|
|
4
|
+
"description": "PostgreSQL database implementation for remix/data-table",
|
|
5
5
|
"author": "Michael Jackson <mjijackson@gmail.com>",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"repository": {
|
|
@@ -29,14 +29,14 @@
|
|
|
29
29
|
"devDependencies": {
|
|
30
30
|
"@types/node": "^24.6.0",
|
|
31
31
|
"@types/pg": "^8.15.6",
|
|
32
|
-
"
|
|
32
|
+
"typescript": "^7.0.2",
|
|
33
33
|
"pg": "^8.16.3",
|
|
34
|
-
"@remix-run/assert": "0.
|
|
35
|
-
"@remix-run/data-table": "0.
|
|
36
|
-
"@remix-run/test": "0.
|
|
34
|
+
"@remix-run/assert": "0.3.0",
|
|
35
|
+
"@remix-run/data-table": "0.4.0",
|
|
36
|
+
"@remix-run/test": "0.6.0"
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@remix-run/data-table": "^0.
|
|
39
|
+
"@remix-run/data-table": "^0.4.0"
|
|
40
40
|
},
|
|
41
41
|
"peerDependencies": {
|
|
42
42
|
"pg": "^8.16.3"
|
|
@@ -54,11 +54,11 @@
|
|
|
54
54
|
"sql"
|
|
55
55
|
],
|
|
56
56
|
"scripts": {
|
|
57
|
-
"build": "
|
|
57
|
+
"build": "tsc -p tsconfig.build.json",
|
|
58
58
|
"clean": "git clean -fdX",
|
|
59
|
-
"test": "remix
|
|
60
|
-
"test:bun": "bun x --bun remix
|
|
61
|
-
"test:coverage": "remix
|
|
62
|
-
"typecheck": "
|
|
59
|
+
"test": "remix test",
|
|
60
|
+
"test:bun": "bun x --bun remix test",
|
|
61
|
+
"test:coverage": "remix test --coverage",
|
|
62
|
+
"typecheck": "tsc --noEmit"
|
|
63
63
|
}
|
|
64
64
|
}
|
package/src/index.ts
CHANGED
|
@@ -1 +1,3 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { createPostgresDatabase, PostgresDatabase } from './lib/database.ts'
|
|
2
|
+
export type { PostgresDatabaseOptions } from './lib/database.ts'
|
|
3
|
+
export type { PostgresDatabaseInput } from './lib/driver.ts'
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { Database, type DatabaseOptions } from '@remix-run/data-table'
|
|
2
|
+
|
|
3
|
+
import { PostgresDatabaseDriver, type PostgresDatabaseInput } from './driver.ts'
|
|
4
|
+
|
|
5
|
+
/** Options for creating a PostgreSQL database. */
|
|
6
|
+
export interface PostgresDatabaseOptions extends DatabaseOptions {
|
|
7
|
+
/** Database used while dropping and recreating the configured database (`postgres` by default). */
|
|
8
|
+
maintenanceDatabase?: string
|
|
9
|
+
/** Template used to recreate the configured database (`template0` by default). */
|
|
10
|
+
template?: string
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/** A {@link Database} backed by PostgreSQL. */
|
|
14
|
+
export class PostgresDatabase extends Database<'postgres'> {
|
|
15
|
+
/**
|
|
16
|
+
* Creates a PostgreSQL-backed database.
|
|
17
|
+
* @param input PostgreSQL pool configuration, pool, or client.
|
|
18
|
+
* @param options Database runtime and recreation options.
|
|
19
|
+
*/
|
|
20
|
+
constructor(input: PostgresDatabaseInput, options: PostgresDatabaseOptions = {}) {
|
|
21
|
+
super(new PostgresDatabaseDriver(input, options), options)
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Creates a PostgreSQL-backed database.
|
|
27
|
+
*
|
|
28
|
+
* @param input PostgreSQL pool configuration, pool, or client.
|
|
29
|
+
* @param options Database runtime and recreation options.
|
|
30
|
+
* @returns A PostgreSQL database.
|
|
31
|
+
* @example
|
|
32
|
+
* ```ts
|
|
33
|
+
* import { createPostgresDatabase } from 'remix/data-table/postgres'
|
|
34
|
+
*
|
|
35
|
+
* let db = createPostgresDatabase({
|
|
36
|
+
* connectionString: process.env.DATABASE_URL,
|
|
37
|
+
* })
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
export function createPostgresDatabase(
|
|
41
|
+
input: PostgresDatabaseInput,
|
|
42
|
+
options: PostgresDatabaseOptions = {},
|
|
43
|
+
): PostgresDatabase {
|
|
44
|
+
return new PostgresDatabase(input, options)
|
|
45
|
+
}
|