supalite 0.5.1 β†’ 0.5.2

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()`: λ ˆμ½”λ“œ μ‚­μ œ
@@ -245,6 +245,7 @@ class QueryBuilder {
245
245
  case 'SELECT': {
246
246
  if (this.headOption) {
247
247
  query = `SELECT COUNT(*) FROM ${schemaTable}`;
248
+ query += this.buildWhereClause();
248
249
  values = [...this.whereValues];
249
250
  break;
250
251
  }
@@ -258,7 +259,6 @@ class QueryBuilder {
258
259
  const joinSubqueries = await Promise.all(this.joinClauses.map(async (join) => {
259
260
  const fk = await this.client.getForeignKey(String(this.schema), String(this.table), join.foreignTable);
260
261
  if (!fk) {
261
- // In a real scenario, you might want to throw an error or handle this case
262
262
  console.warn(`[SupaLite WARNING] No foreign key found from ${join.foreignTable} to ${String(this.table)}`);
263
263
  return null;
264
264
  }
@@ -276,12 +276,15 @@ class QueryBuilder {
276
276
  if (validSubqueries) {
277
277
  selectClause += `, ${validSubqueries}`;
278
278
  }
279
- query = `SELECT ${selectClause} FROM ${schemaTable}`;
279
+ let baseQuery = `SELECT ${selectClause} FROM ${schemaTable}`;
280
+ baseQuery += this.buildWhereClause();
281
+ values = [...this.whereValues];
280
282
  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`;
283
+ query = `SELECT *, COUNT(*) OVER() as exact_count FROM (${baseQuery}) subquery`;
284
+ }
285
+ else {
286
+ query = baseQuery;
283
287
  }
284
- values = [...this.whereValues];
285
288
  break;
286
289
  }
287
290
  case 'INSERT':
@@ -388,17 +391,19 @@ class QueryBuilder {
388
391
  break;
389
392
  }
390
393
  }
394
+ // Append clauses that apply to the outermost query
391
395
  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}`;
396
+ // WHERE is already in the query or subquery
397
+ // ORDER BY, LIMIT, and OFFSET always apply to the outer query
398
+ if (this.orderByColumns.length > 0) {
399
+ query += ` ORDER BY ${this.orderByColumns.join(', ')}`;
400
+ }
401
+ if (this.limitValue !== undefined) {
402
+ query += ` LIMIT ${this.limitValue}`;
403
+ }
404
+ if (this.offsetValue !== undefined) {
405
+ query += ` OFFSET ${this.offsetValue}`;
406
+ }
402
407
  }
403
408
  return { query, values };
404
409
  }
@@ -437,27 +442,49 @@ class QueryBuilder {
437
442
  statusText: 'Created',
438
443
  };
439
444
  }
445
+ let countResult = null;
446
+ let dataResult = result.rows;
447
+ if (this.headOption) {
448
+ countResult = Number(result.rows[0].count);
449
+ dataResult = [];
450
+ }
451
+ else if (this.countOption === 'exact') {
452
+ if (result.rows.length > 0) {
453
+ countResult = Number(result.rows[0].exact_count);
454
+ // exact_count 열을 λͺ¨λ“  데이터 κ°μ²΄μ—μ„œ 제거
455
+ dataResult = result.rows.map(row => {
456
+ const newRow = { ...row };
457
+ delete newRow.exact_count;
458
+ return newRow;
459
+ });
460
+ }
461
+ else {
462
+ countResult = 0;
463
+ }
464
+ }
465
+ else {
466
+ countResult = result.rowCount;
467
+ }
440
468
  if (this.singleMode) {
441
- if (result.rows.length > 1) {
469
+ if (dataResult.length > 1) {
442
470
  return {
443
471
  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
472
+ error: new errors_1.PostgresError('PGRST114: Multiple rows returned'),
473
+ count: countResult,
474
+ status: 406,
447
475
  statusText: 'Not Acceptable. Expected a single row but found multiple.',
448
476
  };
449
477
  }
450
- if (result.rows.length === 0) {
478
+ if (dataResult.length === 0) {
451
479
  if (this.singleMode === 'strict') {
452
480
  return {
453
481
  data: null,
454
- error: new errors_1.PostgresError('PGRST116: No rows found'), // PGRST116: Not found
482
+ error: new errors_1.PostgresError('PGRST116: No rows found'),
455
483
  count: 0,
456
- status: 404, // Not Found (more appropriate than 200 for strict single when not found)
484
+ status: 404,
457
485
  statusText: 'Not Found. Expected a single row but found no rows.',
458
486
  };
459
487
  }
460
- // this.singleMode === 'maybe'
461
488
  return {
462
489
  data: null,
463
490
  error: null,
@@ -466,9 +493,8 @@ class QueryBuilder {
466
493
  statusText: 'OK',
467
494
  };
468
495
  }
469
- // result.rows.length === 1
470
496
  return {
471
- data: result.rows[0],
497
+ data: dataResult[0],
472
498
  error: null,
473
499
  count: 1,
474
500
  status: 200,
@@ -476,9 +502,9 @@ class QueryBuilder {
476
502
  };
477
503
  }
478
504
  return {
479
- data: result.rows.length > 0 ? result.rows : [],
505
+ data: dataResult,
480
506
  error: null,
481
- count: result.rowCount,
507
+ count: countResult,
482
508
  status: 200,
483
509
  statusText: 'OK',
484
510
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "supalite",
3
- "version": "0.5.1",
3
+ "version": "0.5.2",
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",