supalite 0.5.1 β†’ 0.5.3

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/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.5.2] - 2025-10-16
4
+
5
+ ### 🐞 Fixed
6
+ - `select()` λ©”μ„œλ“œμ—μ„œ `count: 'exact'` μ˜΅μ…˜ μ‚¬μš© μ‹œ `limit()` λ˜λŠ” `range()`와 ν•¨κ»˜ 호좜될 λ•Œ 전체 개수 λŒ€μ‹  νŽ˜μ΄μ§€λ„€μ΄μ…˜λœ 개수λ₯Ό λ°˜ν™˜ν•˜λŠ” 버그λ₯Ό μˆ˜μ •ν–ˆμŠ΅λ‹ˆλ‹€. 이제 항상 μ •ν™•ν•œ 전체 개수λ₯Ό λ°˜ν™˜ν•©λ‹ˆλ‹€.
7
+ - `select()` λ©”μ„œλ“œμ—μ„œ `count: 'exact'`와 `head: true` μ˜΅μ…˜μ„ ν•¨κ»˜ μ‚¬μš©ν•  λ•Œ `count`κ°€ `null`둜 λ°˜ν™˜λ˜λŠ” 버그λ₯Ό μˆ˜μ •ν–ˆμŠ΅λ‹ˆλ‹€.
8
+
9
+ ## [0.5.1] - 2025-10-16
10
+
11
+ ### 🐞 Fixed
12
+ - `select()` λ©”μ„œλ“œμ—μ„œ `count: 'exact'` μ˜΅μ…˜ μ‚¬μš© μ‹œ `limit()` λ˜λŠ” `range()`와 ν•¨κ»˜ 호좜될 λ•Œ 전체 개수 λŒ€μ‹  νŽ˜μ΄μ§€λ„€μ΄μ…˜λœ 개수λ₯Ό λ°˜ν™˜ν•˜λŠ” 버그λ₯Ό μˆ˜μ •ν–ˆμŠ΅λ‹ˆλ‹€. 이제 항상 μ •ν™•ν•œ 전체 개수λ₯Ό λ°˜ν™˜ν•©λ‹ˆλ‹€.
13
+
3
14
  ## [0.5.0] - 2025-07-01
4
15
 
5
16
  ### ✨ Added
package/README.md CHANGED
@@ -268,6 +268,8 @@ const { data, error } = await client
268
268
  ### 쿼리 λ©”μ†Œλ“œ
269
269
 
270
270
  - `select(columns?: string, options?: { count?: 'exact' | 'planned' | 'estimated', head?: boolean })`: μ‘°νšŒν•  컬럼 μ§€μ •
271
+ - `options.count`: `'exact'`둜 μ„€μ •ν•˜λ©΄ `limit`의 영ν–₯을 λ°›μ§€ μ•ŠλŠ” 전체 결과의 개수λ₯Ό `count` μ†μ„±μœΌλ‘œ λ°˜ν™˜ν•©λ‹ˆλ‹€.
272
+ - `options.head`: `true`둜 μ„€μ •ν•˜λ©΄ 데이터 없이 `count`만 κ°€μ Έμ˜΅λ‹ˆλ‹€. `count` μ˜΅μ…˜κ³Ό ν•¨κ»˜ μ‚¬μš©ν•˜λ©΄ 효율적으둜 전체 개수만 μ‘°νšŒν•  수 μžˆμŠ΅λ‹ˆλ‹€.
271
273
  - `insert(data: T['Tables'][K]['Insert'] | T['Tables'][K]['Insert'][])`: 단일 λ˜λŠ” 닀쀑 λ ˆμ½”λ“œ μ‚½μž…
272
274
  - `update(data: T['Tables'][K]['Update'])`: λ ˆμ½”λ“œ μ—…λ°μ΄νŠΈ
273
275
  - `delete()`: λ ˆμ½”λ“œ μ‚­μ œ
@@ -41,7 +41,9 @@ export declare class QueryBuilder<T extends DatabaseSchema, S extends SchemaName
41
41
  not(column: string, operator: string, value: any): this;
42
42
  contains(column: string, value: any): this;
43
43
  in(column: string, values: any[]): this;
44
+ gt(column: string, value: any): this;
44
45
  gte(column: string, value: any): this;
46
+ lt(column: string, value: any): this;
45
47
  lte(column: string, value: any): this;
46
48
  order(column: string, options?: {
47
49
  ascending?: boolean;
@@ -122,11 +122,21 @@ class QueryBuilder {
122
122
  this.whereValues.push(...values);
123
123
  return this;
124
124
  }
125
+ gt(column, value) {
126
+ this.whereConditions.push(`"${column}" > $${this.whereValues.length + 1}`);
127
+ this.whereValues.push(value);
128
+ return this;
129
+ }
125
130
  gte(column, value) {
126
131
  this.whereConditions.push(`"${column}" >= $${this.whereValues.length + 1}`);
127
132
  this.whereValues.push(value);
128
133
  return this;
129
134
  }
135
+ lt(column, value) {
136
+ this.whereConditions.push(`"${column}" < $${this.whereValues.length + 1}`);
137
+ this.whereValues.push(value);
138
+ return this;
139
+ }
130
140
  lte(column, value) {
131
141
  this.whereConditions.push(`"${column}" <= $${this.whereValues.length + 1}`);
132
142
  this.whereValues.push(value);
@@ -245,6 +255,7 @@ class QueryBuilder {
245
255
  case 'SELECT': {
246
256
  if (this.headOption) {
247
257
  query = `SELECT COUNT(*) FROM ${schemaTable}`;
258
+ query += this.buildWhereClause();
248
259
  values = [...this.whereValues];
249
260
  break;
250
261
  }
@@ -258,7 +269,6 @@ class QueryBuilder {
258
269
  const joinSubqueries = await Promise.all(this.joinClauses.map(async (join) => {
259
270
  const fk = await this.client.getForeignKey(String(this.schema), String(this.table), join.foreignTable);
260
271
  if (!fk) {
261
- // In a real scenario, you might want to throw an error or handle this case
262
272
  console.warn(`[SupaLite WARNING] No foreign key found from ${join.foreignTable} to ${String(this.table)}`);
263
273
  return null;
264
274
  }
@@ -276,12 +286,15 @@ class QueryBuilder {
276
286
  if (validSubqueries) {
277
287
  selectClause += `, ${validSubqueries}`;
278
288
  }
279
- query = `SELECT ${selectClause} FROM ${schemaTable}`;
289
+ let baseQuery = `SELECT ${selectClause} FROM ${schemaTable}`;
290
+ baseQuery += this.buildWhereClause();
291
+ values = [...this.whereValues];
280
292
  if (this.countOption === 'exact') {
281
- // This part might need adjustment if subqueries are complex
282
- query = `SELECT *, COUNT(*) OVER() as exact_count FROM (${query}) subquery`;
293
+ query = `SELECT *, COUNT(*) OVER() as exact_count FROM (${baseQuery}) subquery`;
294
+ }
295
+ else {
296
+ query = baseQuery;
283
297
  }
284
- values = [...this.whereValues];
285
298
  break;
286
299
  }
287
300
  case 'INSERT':
@@ -388,17 +401,19 @@ class QueryBuilder {
388
401
  break;
389
402
  }
390
403
  }
404
+ // Append clauses that apply to the outermost query
391
405
  if (this.queryType === 'SELECT') {
392
- query += this.buildWhereClause();
393
- }
394
- if (this.orderByColumns.length > 0 && this.queryType === 'SELECT') {
395
- query += ` ORDER BY ${this.orderByColumns.join(', ')}`;
396
- }
397
- if (this.limitValue !== undefined && this.queryType === 'SELECT') {
398
- query += ` LIMIT ${this.limitValue}`;
399
- }
400
- if (this.offsetValue !== undefined && this.queryType === 'SELECT') {
401
- query += ` OFFSET ${this.offsetValue}`;
406
+ // WHERE is already in the query or subquery
407
+ // ORDER BY, LIMIT, and OFFSET always apply to the outer query
408
+ if (this.orderByColumns.length > 0) {
409
+ query += ` ORDER BY ${this.orderByColumns.join(', ')}`;
410
+ }
411
+ if (this.limitValue !== undefined) {
412
+ query += ` LIMIT ${this.limitValue}`;
413
+ }
414
+ if (this.offsetValue !== undefined) {
415
+ query += ` OFFSET ${this.offsetValue}`;
416
+ }
402
417
  }
403
418
  return { query, values };
404
419
  }
@@ -437,27 +452,49 @@ class QueryBuilder {
437
452
  statusText: 'Created',
438
453
  };
439
454
  }
455
+ let countResult = null;
456
+ let dataResult = result.rows;
457
+ if (this.headOption) {
458
+ countResult = Number(result.rows[0].count);
459
+ dataResult = [];
460
+ }
461
+ else if (this.countOption === 'exact') {
462
+ if (result.rows.length > 0) {
463
+ countResult = Number(result.rows[0].exact_count);
464
+ // exact_count 열을 λͺ¨λ“  데이터 κ°μ²΄μ—μ„œ 제거
465
+ dataResult = result.rows.map(row => {
466
+ const newRow = { ...row };
467
+ delete newRow.exact_count;
468
+ return newRow;
469
+ });
470
+ }
471
+ else {
472
+ countResult = 0;
473
+ }
474
+ }
475
+ else {
476
+ countResult = result.rowCount;
477
+ }
440
478
  if (this.singleMode) {
441
- if (result.rows.length > 1) {
479
+ if (dataResult.length > 1) {
442
480
  return {
443
481
  data: null,
444
- error: new errors_1.PostgresError('PGRST114: Multiple rows returned'), // PGRST114: More than one row was returned
445
- count: result.rowCount,
446
- status: 406, // Not Acceptable
482
+ error: new errors_1.PostgresError('PGRST114: Multiple rows returned'),
483
+ count: countResult,
484
+ status: 406,
447
485
  statusText: 'Not Acceptable. Expected a single row but found multiple.',
448
486
  };
449
487
  }
450
- if (result.rows.length === 0) {
488
+ if (dataResult.length === 0) {
451
489
  if (this.singleMode === 'strict') {
452
490
  return {
453
491
  data: null,
454
- error: new errors_1.PostgresError('PGRST116: No rows found'), // PGRST116: Not found
492
+ error: new errors_1.PostgresError('PGRST116: No rows found'),
455
493
  count: 0,
456
- status: 404, // Not Found (more appropriate than 200 for strict single when not found)
494
+ status: 404,
457
495
  statusText: 'Not Found. Expected a single row but found no rows.',
458
496
  };
459
497
  }
460
- // this.singleMode === 'maybe'
461
498
  return {
462
499
  data: null,
463
500
  error: null,
@@ -466,9 +503,8 @@ class QueryBuilder {
466
503
  statusText: 'OK',
467
504
  };
468
505
  }
469
- // result.rows.length === 1
470
506
  return {
471
- data: result.rows[0],
507
+ data: dataResult[0],
472
508
  error: null,
473
509
  count: 1,
474
510
  status: 200,
@@ -476,9 +512,9 @@ class QueryBuilder {
476
512
  };
477
513
  }
478
514
  return {
479
- data: result.rows.length > 0 ? result.rows : [],
515
+ data: dataResult,
480
516
  error: null,
481
- count: result.rowCount,
517
+ count: countResult,
482
518
  status: 200,
483
519
  statusText: 'OK',
484
520
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "supalite",
3
- "version": "0.5.1",
3
+ "version": "0.5.3",
4
4
  "description": "A lightweight TypeScript PostgreSQL client with Supabase-style API",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",