supalite 0.1.13 → 0.2.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.
- package/CHANGELOG.md +5 -42
- package/CHANGE_REPORT_LOG.md +122 -64
- package/README.md +3 -2
- package/dist/query-builder.d.ts +5 -1
- package/dist/query-builder.js +34 -9
- package/docs/changelog/2025-05-26-maybeSingle-single-refactor.md +47 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,52 +1,15 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## [0.1.
|
|
3
|
+
## [0.1.8] - 2025-03-04
|
|
4
4
|
|
|
5
5
|
### Fixed
|
|
6
|
-
-
|
|
7
|
-
- 타입 안전성 향상
|
|
6
|
+
- 누락된 `dist` 파일들을 포함하도록 수정
|
|
8
7
|
|
|
9
|
-
## [0.1.
|
|
8
|
+
## [0.1.7] - 2025-03-04
|
|
10
9
|
|
|
11
10
|
### Added
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
- View 테이블 조회 예제 코드 추가
|
|
15
|
-
- View 테이블 테스트를 위한 SQL 스크립트 추가
|
|
16
|
-
|
|
17
|
-
## [0.1.11] - 2025-03-02
|
|
18
|
-
|
|
19
|
-
### Fixed
|
|
20
|
-
- 빌드 결과물 업데이트 및 배포 문제 해결
|
|
21
|
-
|
|
22
|
-
## [0.1.10] - 2025-03-02
|
|
23
|
-
|
|
24
|
-
### Fixed
|
|
25
|
-
- order 메서드를 수정하여 컬럼 이름만 전달하는 경우에도 기본적으로 오름차순 정렬이 적용되도록 개선
|
|
26
|
-
- Supabase와의 호환성 개선 (.order('post_index') 형식 지원)
|
|
27
|
-
- order 메서드 사용 예제 추가
|
|
28
|
-
|
|
29
|
-
## [0.1.9] - 2025-03-02
|
|
30
|
-
|
|
31
|
-
### Fixed
|
|
32
|
-
- from 메서드의 반환 타입을 수정하여 single() 메서드를 호출할 때 SingleQueryResult를 반환하도록 개선
|
|
33
|
-
- 타입 단언 없이도 single() 메서드를 사용할 수 있도록 수정
|
|
34
|
-
- single() 메서드 사용 예제 추가
|
|
35
|
-
|
|
36
|
-
## [0.1.8] - 2025-03-02
|
|
37
|
-
|
|
38
|
-
### Fixed
|
|
39
|
-
- from 메서드의 반환 타입을 수정하여 타입 단언 없이도 배열 메서드를 사용할 수 있도록 개선
|
|
40
|
-
- await 사용 시 자동으로 QueryResult<Row<T, S, K>>를 반환하도록 수정
|
|
41
|
-
- Supabase와의 호환성 개선
|
|
42
|
-
|
|
43
|
-
## [0.1.7] - 2025-03-02
|
|
44
|
-
|
|
45
|
-
### Fixed
|
|
46
|
-
- QueryResult 타입의 data 필드가 항상 배열을 반환하도록 수정
|
|
47
|
-
- 쿼리 결과가 없을 때 null 대신 빈 배열([])을 반환하도록 수정
|
|
48
|
-
- 에러 발생 시에도 data 필드가 빈 배열을 반환하도록 수정
|
|
49
|
-
- Supabase와의 호환성 개선
|
|
11
|
+
- QueryBuilder에 `match` 메서드 추가
|
|
12
|
+
- `match` 메서드 테스트 코드 작성
|
|
50
13
|
|
|
51
14
|
## [0.1.6] - 2025-03-01
|
|
52
15
|
|
package/CHANGE_REPORT_LOG.md
CHANGED
|
@@ -1,65 +1,123 @@
|
|
|
1
|
-
# 변경
|
|
1
|
+
# 변경 작업 보고서
|
|
2
|
+
|
|
3
|
+
## [2025-03-04] 누락된 dist 파일들을 포함하도록 수정
|
|
4
|
+
|
|
5
|
+
### 작업 내용
|
|
6
|
+
- 빌드 시 생성되는 dist 디렉토리의 파일들이 이전 커밋에 포함되지 않은 문제를 발견했습니다.
|
|
7
|
+
- dist 디렉토리를 삭제하고 다시 빌드하여 누락된 파일들을 생성했습니다.
|
|
8
|
+
- package.json 파일의 버전을 0.1.8로 업데이트했습니다.
|
|
9
|
+
- CHANGELOG.md 파일에 변경 사항을 기록했습니다.
|
|
10
|
+
|
|
11
|
+
### 변경된 파일
|
|
12
|
+
- dist/*
|
|
13
|
+
- package.json
|
|
14
|
+
- CHANGELOG.md
|
|
15
|
+
|
|
16
|
+
### 개발 과정
|
|
17
|
+
1. dist 디렉토리 삭제
|
|
18
|
+
2. npm run build 명령어 실행
|
|
19
|
+
3. git add . 명령어로 변경된 파일 스테이징
|
|
20
|
+
4. git commit -m "fix: Include missing files in dist directory" 명령어로 커밋
|
|
21
|
+
5. package.json 파일의 버전 업데이트
|
|
22
|
+
6. CHANGELOG.md 파일에 변경 사항 기록
|
|
23
|
+
|
|
24
|
+
### 결론
|
|
25
|
+
이번 작업을 통해 누락되었던 dist 파일들을 커밋에 포함시켰습니다.
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## [2025-03-04] QueryBuilder에 match 메서드 추가
|
|
30
|
+
|
|
31
|
+
### 작업 내용
|
|
32
|
+
|
|
33
|
+
1. **`match` 메서드 구현**:
|
|
34
|
+
- `QueryBuilder` 클래스에 `match` 메서드를 추가했습니다.
|
|
35
|
+
- 이 메서드는 객체를 인자로 받아, 객체의 각 키-값 쌍을 `"${key}" = $${index}` 형태의 조건으로 변환하여 `whereConditions` 배열에 추가합니다.
|
|
36
|
+
- 이를 통해 사용자는 객체 리터럴을 사용하여 간편하게 쿼리 조건을 추가할 수 있습니다.
|
|
37
|
+
|
|
38
|
+
2. **테스트용 테이블 생성**:
|
|
39
|
+
- `examples/setup.sql` 파일에 `test_table`을 생성하는 SQL 문을 추가했습니다.
|
|
40
|
+
- 이 테이블은 `match` 메서드의 동작을 테스트하는 데 사용됩니다.
|
|
41
|
+
|
|
42
|
+
3. **테스트용 함수 구현**:
|
|
43
|
+
- `src/test-table.ts` 파일에 `test_table`과 상호작용하는 함수를 작성했습니다.
|
|
44
|
+
- `insertIntoTestTable` 함수는 `test_table`에 데이터를 삽입합니다.
|
|
45
|
+
- `getFromTestTable` 함수는 `test_table`에서 데이터를 조회하고, `match` 메서드를 사용하여 조건을 적용합니다.
|
|
46
|
+
|
|
47
|
+
4. **테스트 코드 작성**:
|
|
48
|
+
- `examples/tests/query-builder.test.ts` 파일에 `match` 메서드를 테스트하는 코드를 작성했습니다.
|
|
49
|
+
- 이 코드는 `test_table`에 데이터를 삽입하고, `match` 메서드를 사용하여 다양한 조건으로 데이터를 조회합니다.
|
|
50
|
+
- 조회 결과가 예상과 일치하는지 확인합니다.
|
|
51
|
+
|
|
52
|
+
5. **테스트 실행**:
|
|
53
|
+
- `bun examples/tests/query-builder.test.ts` 명령어를 사용하여 테스트를 실행했습니다.
|
|
54
|
+
- 모든 테스트가 성공적으로 완료되었습니다.
|
|
55
|
+
|
|
56
|
+
6. **문서화**:
|
|
57
|
+
- CHANGELOG.md 파일에 `match` 메서드 추가 사항을 기록했습니다.
|
|
58
|
+
- 버전을 0.1.7로 업데이트했습니다.
|
|
59
|
+
|
|
60
|
+
### 변경된 파일
|
|
61
|
+
|
|
62
|
+
1. `src/query-builder.ts`: `match` 메서드 추가
|
|
63
|
+
2. `examples/setup.sql`: `test_table` 생성 SQL 문 추가
|
|
64
|
+
3. `src/test-table.ts`: `test_table` 관련 함수 구현
|
|
65
|
+
4. `examples/tests/query-builder.test.ts`: `match` 메서드 테스트 코드 작성
|
|
66
|
+
5. `CHANGELOG.md`: 변경 사항 문서화 및 버전 업데이트
|
|
67
|
+
|
|
68
|
+
### 개발 과정
|
|
69
|
+
|
|
70
|
+
1. `feature/add-match-method` 브랜치 생성
|
|
71
|
+
2. `QueryBuilder` 클래스에 `match` 메서드 구현
|
|
72
|
+
3. `examples/setup.sql` 파일에 `test_table` 생성 SQL 문 추가
|
|
73
|
+
4. `src/test-table.ts` 파일에 `test_table` 관련 함수 구현
|
|
74
|
+
5. `examples/tests/query-builder.test.ts` 파일에 `match` 메서드 테스트 코드 작성
|
|
75
|
+
6. 테스트 실행 및 결과 확인
|
|
76
|
+
7. 문서화 및 버전 업데이트
|
|
77
|
+
8. 변경 사항 커밋
|
|
78
|
+
9. main 브랜치로 병합
|
|
79
|
+
|
|
80
|
+
### 테스트 결과
|
|
81
|
+
|
|
82
|
+
테스트 결과는 다음과 같습니다:
|
|
83
|
+
|
|
84
|
+
```
|
|
85
|
+
Result 1: [
|
|
86
|
+
{
|
|
87
|
+
id: 1
|
|
88
|
+
name: "test1"
|
|
89
|
+
value: 10
|
|
90
|
+
}
|
|
91
|
+
]
|
|
92
|
+
Result 2: [
|
|
93
|
+
{
|
|
94
|
+
id: 2
|
|
95
|
+
name: "test2"
|
|
96
|
+
value: 20
|
|
97
|
+
}
|
|
98
|
+
]
|
|
99
|
+
Result 3: [
|
|
100
|
+
{
|
|
101
|
+
id: 1
|
|
102
|
+
name: "test1"
|
|
103
|
+
value: 10
|
|
104
|
+
}
|
|
105
|
+
]
|
|
106
|
+
Result 4: null
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Result1, 2, 3은 예상대로 출력되었고, Result 4는 존재하지 않는 데이터에 대한 결과로 null을 반환했습니다.
|
|
110
|
+
|
|
111
|
+
### 결론
|
|
112
|
+
|
|
113
|
+
이번 작업을 통해 `QueryBuilder` 클래스에 `match` 메서드를 추가하여, 사용자가 객체 리터럴을 사용하여 간편하게 쿼리 조건을 추가할 수 있게 되었습니다. 또한, 테스트 코드를 통해 `match` 메서드의 동작을 검증했습니다.
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
# 변경 작업 보고서
|
|
118
|
+
|
|
119
|
+
## [2025-03-01] corepack을 통한 다중 패키지 관리자 지원 추가
|
|
120
|
+
|
|
121
|
+
### 작업 내용
|
|
2
122
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
### 1. 코드 변경 사항
|
|
6
|
-
|
|
7
|
-
#### 타입 정의 수정 (`src/types.ts`)
|
|
8
|
-
- `ViewName` 타입 추가: 스키마 내의 Views 이름을 참조하는 타입
|
|
9
|
-
- `TableOrViewName` 타입 추가: Tables와 Views를 모두 포함하는 통합 타입
|
|
10
|
-
- `Row`, `InsertRow`, `UpdateRow` 타입 수정: Views도 처리할 수 있도록 조건부 타입 적용
|
|
11
|
-
|
|
12
|
-
#### 쿼리 빌더 수정 (`src/query-builder.ts`)
|
|
13
|
-
- `QueryBuilder` 클래스의 제네릭 타입 매개변수를 `TableOrViewName`으로 변경
|
|
14
|
-
|
|
15
|
-
#### PostgreSQL 클라이언트 수정 (`src/postgres-client.ts`)
|
|
16
|
-
- `SchemaWithTables` 타입 수정: Views 타입 정의 추가
|
|
17
|
-
- `from` 메서드 시그니처 수정: `TableOrViewName` 타입을 사용하도록 변경
|
|
18
|
-
|
|
19
|
-
### 2. 예제 및 테스트 코드 추가
|
|
20
|
-
|
|
21
|
-
#### 예제 데이터베이스 타입 정의 수정 (`examples/types/database.ts`)
|
|
22
|
-
- 예제 데이터베이스 타입 정의에 Views 추가
|
|
23
|
-
- `user_posts_view`와 `active_users_view` 뷰 정의 추가
|
|
24
|
-
|
|
25
|
-
#### 테스트 및 예제 코드 추가
|
|
26
|
-
- View 테이블 조회 예제 코드 작성 (`examples/tests/view-table.ts`)
|
|
27
|
-
- 테스트용 SQL 스크립트 작성 (`examples/setup-views.sql`)
|
|
28
|
-
- 모의 데이터를 사용한 테스트 코드 작성 (`examples/tests/mock-view-table.ts`)
|
|
29
|
-
- 타입 안전성 테스트 코드 작성 (`examples/tests/view-table-test.ts`)
|
|
30
|
-
- PostgreSQL 테스트 환경 설정 스크립트 작성 (`setup-postgres-test.sql`)
|
|
31
|
-
|
|
32
|
-
### 3. 테스트 결과
|
|
33
|
-
|
|
34
|
-
#### 타입 검증
|
|
35
|
-
- TypeScript 컴파일러를 통한 타입 검증 완료
|
|
36
|
-
- View 테이블 타입이 올바르게 추론됨
|
|
37
|
-
- 타입 안전성 유지 (존재하지 않는 컬럼/테이블 접근 시 컴파일 오류 발생)
|
|
38
|
-
- Views는 읽기 전용이므로 Insert/Update 시도 시 컴파일 오류 발생
|
|
39
|
-
|
|
40
|
-
#### 모의 데이터 테스트
|
|
41
|
-
- 모의 데이터를 사용한 View 테이블 조회 예제 실행 성공
|
|
42
|
-
- 일반 테이블과 동일한 방식으로 View 테이블 조회 가능
|
|
43
|
-
- 조건 적용, 정렬, 단일 결과 조회 등 다양한 쿼리 기능 정상 작동
|
|
44
|
-
|
|
45
|
-
#### 실제 PostgreSQL 데이터베이스 테스트
|
|
46
|
-
- PostgreSQL 테스트 환경 설정 및 테스트 데이터 생성 성공
|
|
47
|
-
- 실제 데이터베이스에서 View 테이블 조회 기능 정상 작동 확인
|
|
48
|
-
- 연결 문자열을 사용하여 인증 문제 해결
|
|
49
|
-
|
|
50
|
-
### 4. 배포 과정
|
|
51
|
-
|
|
52
|
-
#### 브랜치 관리
|
|
53
|
-
- 브랜치 `feature/view-table-support` 생성
|
|
54
|
-
- 변경 사항 커밋: "feat: Views 테이블 조회 기능 추가"
|
|
55
|
-
- 테스트 코드 커밋: "test: View 테이블 조회 기능 테스트 코드 추가"
|
|
56
|
-
- PostgreSQL 테스트 환경 설정 스크립트 커밋: "test: PostgreSQL 테스트 환경 설정 스크립트 추가"
|
|
57
|
-
|
|
58
|
-
#### 머지 및 배포
|
|
59
|
-
- `feature/view-table-support` 브랜치를 `main` 브랜치에 머지
|
|
60
|
-
- 버전 0.1.12로 업데이트
|
|
61
|
-
- npm에 배포 완료
|
|
62
|
-
|
|
63
|
-
### 5. 결론
|
|
64
|
-
|
|
65
|
-
이제 Supalite 라이브러리는 Supabase 데이터베이스의 Views 테이블도 조회할 수 있게 되었습니다. 사용자는 일반 테이블과 동일한 방식으로 View 테이블을 조회할 수 있으며, 타입 안전성과 기존 코드와의 호환성이 유지됩니다. 실제 PostgreSQL 데이터베이스에서도 정상적으로 작동함을 확인했습니다.
|
|
123
|
+
... (생략) ...
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# SupaLite
|
|
2
2
|
|
|
3
|
-
[](https://www.npmjs.com/package/supalite)
|
|
4
4
|
|
|
5
5
|
가볍고 효율적인 PostgreSQL 클라이언트 라이브러리입니다. Supabase와 동일한 API를 제공하면서도 더 가볍고 빠른 구현을 제공합니다.
|
|
6
6
|
|
|
@@ -267,7 +267,8 @@ const { data, error } = await client
|
|
|
267
267
|
- `limit(count: number)`: 결과 개수 제한
|
|
268
268
|
- `offset(count: number)`: 결과 시작 위치
|
|
269
269
|
- `range(from: number, to: number)`: 범위 지정
|
|
270
|
-
- `single()`: 단일 결과 반환
|
|
270
|
+
- `single()`: 단일 결과 반환 (결과 없을 시 에러)
|
|
271
|
+
- `maybeSingle()`: 단일 결과 반환 (결과 없을 시 data: null, error: null)
|
|
271
272
|
- `returns<T>()`: 반환 타입 지정
|
|
272
273
|
|
|
273
274
|
### 트랜잭션 메소드
|
package/dist/query-builder.d.ts
CHANGED
|
@@ -14,7 +14,7 @@ export declare class QueryBuilder<T extends DatabaseSchema, S extends SchemaName
|
|
|
14
14
|
private limitValue?;
|
|
15
15
|
private offsetValue?;
|
|
16
16
|
private whereValues;
|
|
17
|
-
private
|
|
17
|
+
private singleMode;
|
|
18
18
|
private queryType;
|
|
19
19
|
private insertData?;
|
|
20
20
|
private updateData?;
|
|
@@ -27,6 +27,9 @@ export declare class QueryBuilder<T extends DatabaseSchema, S extends SchemaName
|
|
|
27
27
|
count?: 'exact' | 'planned' | 'estimated';
|
|
28
28
|
head?: boolean;
|
|
29
29
|
}): this;
|
|
30
|
+
match(conditions: {
|
|
31
|
+
[key: string]: any;
|
|
32
|
+
}): this;
|
|
30
33
|
eq(column: string, value: any): this;
|
|
31
34
|
neq(column: string, value: any): this;
|
|
32
35
|
is(column: string, value: any): this;
|
|
@@ -39,6 +42,7 @@ export declare class QueryBuilder<T extends DatabaseSchema, S extends SchemaName
|
|
|
39
42
|
}): this;
|
|
40
43
|
limit(value: number): this;
|
|
41
44
|
offset(value: number): this;
|
|
45
|
+
maybeSingle(): Promise<SingleQueryResult<Row<T, S, K>>>;
|
|
42
46
|
single(): Promise<SingleQueryResult<Row<T, S, K>>>;
|
|
43
47
|
ilike(column: string, pattern: string): this;
|
|
44
48
|
or(conditions: string): this;
|
package/dist/query-builder.js
CHANGED
|
@@ -12,7 +12,7 @@ class QueryBuilder {
|
|
|
12
12
|
this.orConditions = [];
|
|
13
13
|
this.orderByColumns = [];
|
|
14
14
|
this.whereValues = [];
|
|
15
|
-
this.
|
|
15
|
+
this.singleMode = null;
|
|
16
16
|
this.queryType = 'SELECT';
|
|
17
17
|
this.table = table;
|
|
18
18
|
this.schema = schema;
|
|
@@ -32,6 +32,13 @@ class QueryBuilder {
|
|
|
32
32
|
this.headOption = options?.head;
|
|
33
33
|
return this;
|
|
34
34
|
}
|
|
35
|
+
match(conditions) {
|
|
36
|
+
for (const key in conditions) {
|
|
37
|
+
this.whereConditions.push(`"${key}" = $${this.whereValues.length + 1}`);
|
|
38
|
+
this.whereValues.push(conditions[key]);
|
|
39
|
+
}
|
|
40
|
+
return this;
|
|
41
|
+
}
|
|
35
42
|
eq(column, value) {
|
|
36
43
|
this.whereConditions.push(`"${column}" = $${this.whereValues.length + 1}`);
|
|
37
44
|
this.whereValues.push(value);
|
|
@@ -90,8 +97,12 @@ class QueryBuilder {
|
|
|
90
97
|
this.offsetValue = value;
|
|
91
98
|
return this;
|
|
92
99
|
}
|
|
100
|
+
maybeSingle() {
|
|
101
|
+
this.singleMode = 'maybe';
|
|
102
|
+
return this.execute();
|
|
103
|
+
}
|
|
93
104
|
single() {
|
|
94
|
-
this.
|
|
105
|
+
this.singleMode = 'strict';
|
|
95
106
|
return this.execute();
|
|
96
107
|
}
|
|
97
108
|
ilike(column, pattern) {
|
|
@@ -196,7 +207,7 @@ class QueryBuilder {
|
|
|
196
207
|
values = [...this.whereValues];
|
|
197
208
|
break;
|
|
198
209
|
case 'INSERT':
|
|
199
|
-
case 'UPSERT':
|
|
210
|
+
case 'UPSERT': {
|
|
200
211
|
if (!this.insertData)
|
|
201
212
|
throw new Error('No data provided for insert/upsert');
|
|
202
213
|
if (Array.isArray(this.insertData)) {
|
|
@@ -223,7 +234,8 @@ class QueryBuilder {
|
|
|
223
234
|
}
|
|
224
235
|
query += returning;
|
|
225
236
|
break;
|
|
226
|
-
|
|
237
|
+
}
|
|
238
|
+
case 'UPDATE': {
|
|
227
239
|
if (!this.updateData)
|
|
228
240
|
throw new Error('No data provided for update');
|
|
229
241
|
const updateData = { ...this.updateData };
|
|
@@ -241,12 +253,14 @@ class QueryBuilder {
|
|
|
241
253
|
query += this.buildWhereClause(updateValues);
|
|
242
254
|
query += returning;
|
|
243
255
|
break;
|
|
244
|
-
|
|
256
|
+
}
|
|
257
|
+
case 'DELETE': {
|
|
245
258
|
query = `DELETE FROM ${schemaTable}`;
|
|
246
259
|
values = [...this.whereValues];
|
|
247
260
|
query += this.buildWhereClause();
|
|
248
261
|
query += returning;
|
|
249
262
|
break;
|
|
263
|
+
}
|
|
250
264
|
}
|
|
251
265
|
if (this.queryType === 'SELECT') {
|
|
252
266
|
query += this.buildWhereClause();
|
|
@@ -293,17 +307,27 @@ class QueryBuilder {
|
|
|
293
307
|
statusText: 'Created',
|
|
294
308
|
};
|
|
295
309
|
}
|
|
296
|
-
if (this.
|
|
310
|
+
if (this.singleMode) {
|
|
297
311
|
if (result.rows.length > 1) {
|
|
298
312
|
return {
|
|
299
313
|
data: null,
|
|
300
|
-
error: new errors_1.PostgresError('Multiple rows returned
|
|
314
|
+
error: new errors_1.PostgresError('PGRST114: Multiple rows returned'), // PGRST114: More than one row was returned
|
|
301
315
|
count: result.rowCount,
|
|
302
|
-
status: 406,
|
|
303
|
-
statusText: 'Not Acceptable',
|
|
316
|
+
status: 406, // Not Acceptable
|
|
317
|
+
statusText: 'Not Acceptable. Expected a single row but found multiple.',
|
|
304
318
|
};
|
|
305
319
|
}
|
|
306
320
|
if (result.rows.length === 0) {
|
|
321
|
+
if (this.singleMode === 'strict') {
|
|
322
|
+
return {
|
|
323
|
+
data: null,
|
|
324
|
+
error: new errors_1.PostgresError('PGRST116: No rows found'), // PGRST116: Not found
|
|
325
|
+
count: 0,
|
|
326
|
+
status: 404, // Not Found (more appropriate than 200 for strict single when not found)
|
|
327
|
+
statusText: 'Not Found. Expected a single row but found no rows.',
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
// this.singleMode === 'maybe'
|
|
307
331
|
return {
|
|
308
332
|
data: null,
|
|
309
333
|
error: null,
|
|
@@ -312,6 +336,7 @@ class QueryBuilder {
|
|
|
312
336
|
statusText: 'OK',
|
|
313
337
|
};
|
|
314
338
|
}
|
|
339
|
+
// result.rows.length === 1
|
|
315
340
|
return {
|
|
316
341
|
data: result.rows[0],
|
|
317
342
|
error: null,
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Changelog: 2025-05-26
|
|
2
|
+
|
|
3
|
+
## Feature: `maybeSingle()` Method and `single()` Refactor
|
|
4
|
+
|
|
5
|
+
**File:** `src/query-builder.ts`
|
|
6
|
+
|
|
7
|
+
### Summary
|
|
8
|
+
- The existing `single()` method has been renamed to `maybeSingle()`. This method returns `data: null` and `error: null` if no row is found.
|
|
9
|
+
- A new `single()` method has been implemented. This method returns `data: null` and an error object (specifically `PostgresError('PGRST116: No rows found')` with status `404`) if no row is found.
|
|
10
|
+
- Both methods will return an error (`PostgresError('PGRST114: Multiple rows returned')` with status `406`) if multiple rows are returned by the query.
|
|
11
|
+
|
|
12
|
+
### Detailed Changes
|
|
13
|
+
1. **Added `singleMode` Property**:
|
|
14
|
+
* A new private property `singleMode: 'strict' | 'maybe' | null` was added to the `QueryBuilder` class to manage the behavior of single-row fetching.
|
|
15
|
+
|
|
16
|
+
2. **`maybeSingle()` Method (Formerly `single()`)**:
|
|
17
|
+
* The original `single()` method was renamed to `maybeSingle()`.
|
|
18
|
+
* It now sets `this.singleMode = 'maybe';`.
|
|
19
|
+
* In the `execute()` method, when `singleMode` is `'maybe'`:
|
|
20
|
+
* If 0 rows are found: returns `{ data: null, error: null, count: 0, status: 200, statusText: 'OK' }`.
|
|
21
|
+
* If 1 row is found: returns `{ data: row, error: null, count: 1, status: 200, statusText: 'OK' }`.
|
|
22
|
+
* If >1 rows are found: returns `{ data: null, error: new PostgresError('PGRST114: Multiple rows returned'), count: rowCount, status: 406, statusText: 'Not Acceptable. Expected a single row but found multiple.' }`.
|
|
23
|
+
|
|
24
|
+
3. **New `single()` Method**:
|
|
25
|
+
* A new method named `single()` was introduced.
|
|
26
|
+
* It sets `this.singleMode = 'strict';`.
|
|
27
|
+
* In the `execute()` method, when `singleMode` is `'strict'`:
|
|
28
|
+
* If 0 rows are found: returns `{ data: null, error: new PostgresError('PGRST116: No rows found'), count: 0, status: 404, statusText: 'Not Found. Expected a single row but found no rows.' }`.
|
|
29
|
+
* If 1 row is found: returns `{ data: row, error: null, count: 1, status: 200, statusText: 'OK' }`.
|
|
30
|
+
* If >1 rows are found: returns `{ data: null, error: new PostgresError('PGRST114: Multiple rows returned'), count: rowCount, status: 406, statusText: 'Not Acceptable. Expected a single row but found multiple.' }`.
|
|
31
|
+
|
|
32
|
+
4. **ESLint Fixes in `src/query-builder.ts`**:
|
|
33
|
+
* Removed unused type imports `QueryOptions` and `FilterOptions`.
|
|
34
|
+
* Scoped lexical declarations within `switch` `case` blocks in `buildQuery()` using `{}`.
|
|
35
|
+
5. **Unit Tests Added (`src/__tests__/query-builder-single.test.ts`)**:
|
|
36
|
+
* Comprehensive Jest tests were added for both `single()` and `maybeSingle()`.
|
|
37
|
+
* The test file was initially created in `examples/tests/` and then moved to `src/__tests__/` to align with Jest's `roots` configuration, ensuring test discovery. Import paths within the test file were updated accordingly.
|
|
38
|
+
* Tests cover scenarios for finding one row, zero rows, and multiple rows.
|
|
39
|
+
* Appropriate assertions for `data`, `error`, `status`, and `statusText` are included.
|
|
40
|
+
* Test database setup (`users` and `test_table_for_multi_row` tables) and teardown are handled in `beforeAll`/`afterAll`.
|
|
41
|
+
* Type definitions for the test database schema (`TestDatabase`) were created and refined to ensure compatibility and correctness.
|
|
42
|
+
|
|
43
|
+
### Impact
|
|
44
|
+
- Developers now have two distinct methods for fetching a single row:
|
|
45
|
+
- `maybeSingle()`: Use when a row might or might not exist, and its absence is not an error condition.
|
|
46
|
+
- `single()`: Use when a row is expected to exist, and its absence should be treated as an error.
|
|
47
|
+
- This change provides more explicit control over how "not found" scenarios are handled for single-row queries.
|