@hexaijs/sqlite 0.1.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/LICENSE +21 -0
- package/README.md +248 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -0
- package/dist/sqlite-unit-of-work.d.ts +10 -0
- package/dist/sqlite-unit-of-work.d.ts.map +1 -0
- package/dist/sqlite-unit-of-work.js +50 -0
- package/dist/sqlite-unit-of-work.js.map +1 -0
- package/dist/test/index.d.ts +3 -0
- package/dist/test/index.d.ts.map +1 -0
- package/dist/test/index.js +19 -0
- package/dist/test/index.js.map +1 -0
- package/dist/test/sqlite-repository-for-test.d.ts +19 -0
- package/dist/test/sqlite-repository-for-test.d.ts.map +1 -0
- package/dist/test/sqlite-repository-for-test.js +63 -0
- package/dist/test/sqlite-repository-for-test.js.map +1 -0
- package/dist/test/utils.d.ts +3 -0
- package/dist/test/utils.d.ts.map +1 -0
- package/dist/test/utils.js +12 -0
- package/dist/test/utils.js.map +1 -0
- package/package.json +62 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023 Sangwoo Hyun
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
# @hexaijs/sqlite
|
|
2
|
+
|
|
3
|
+
> SQLite transaction management for testing and lightweight use cases
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
`@hexaijs/sqlite` provides an SQLite implementation of the `UnitOfWork` interface from `@hexaijs/core`. It enables transaction management against SQLite databases, making it particularly useful for fast, isolated integration tests.
|
|
8
|
+
|
|
9
|
+
The package centers around `SqliteUnitOfWork`, which wraps operations in SQLite transactions. Unlike `PostgresUnitOfWork`, it uses a simpler architecture without `AsyncLocalStorage` or propagation modes—a deliberate trade-off that favors simplicity for scenarios where a single database connection is sufficient.
|
|
10
|
+
|
|
11
|
+
The test utilities export makes it easy to spin up in-memory databases and use generic repositories for test fixtures. In-memory SQLite databases are ephemeral: they're created instantly, run entirely in RAM, and disappear when the connection closes. This makes them ideal for integration tests that need database behavior without the overhead of a real PostgreSQL server.
|
|
12
|
+
|
|
13
|
+
## When to Use SQLite vs PostgreSQL
|
|
14
|
+
|
|
15
|
+
| Scenario | Recommendation |
|
|
16
|
+
|----------|----------------|
|
|
17
|
+
| Unit/integration tests | **SQLite** - Fast, no external dependencies |
|
|
18
|
+
| Production database | **PostgreSQL** - Full ACID, scalability, advanced features |
|
|
19
|
+
| CI/CD pipelines | **SQLite** - No database setup required |
|
|
20
|
+
| Local development | Either - SQLite for speed, PostgreSQL for parity with production |
|
|
21
|
+
|
|
22
|
+
## Installation
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npm install @hexaijs/sqlite
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
**Peer dependencies:**
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
npm install @hexaijs/core sqlite sqlite3
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Core Concepts
|
|
35
|
+
|
|
36
|
+
### SqliteUnitOfWork
|
|
37
|
+
|
|
38
|
+
The `SqliteUnitOfWork` implements `UnitOfWork<Database>` from `@hexaijs/core`. It manages transaction lifecycle for a given SQLite database connection.
|
|
39
|
+
|
|
40
|
+
```typescript
|
|
41
|
+
import { open } from "sqlite";
|
|
42
|
+
import sqlite3 from "sqlite3";
|
|
43
|
+
import { SqliteUnitOfWork } from "@hexaijs/sqlite";
|
|
44
|
+
|
|
45
|
+
// Create an in-memory database
|
|
46
|
+
const db = await open({
|
|
47
|
+
filename: ":memory:",
|
|
48
|
+
driver: sqlite3.Database,
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
// Create unit of work
|
|
52
|
+
const unitOfWork = new SqliteUnitOfWork(db);
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Unlike PostgreSQL's unit of work which accepts a client factory, `SqliteUnitOfWork` takes a pre-connected `Database` instance. This simpler model works well for SQLite's single-writer architecture.
|
|
56
|
+
|
|
57
|
+
### Transaction Execution
|
|
58
|
+
|
|
59
|
+
Use `wrap()` to execute operations within a transaction:
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
const result = await unitOfWork.wrap(async (db) => {
|
|
63
|
+
await db.run("INSERT INTO orders (id, status) VALUES (?, ?)", [orderId, "pending"]);
|
|
64
|
+
await db.run("INSERT INTO order_items (order_id, product_id) VALUES (?, ?)", [orderId, productId]);
|
|
65
|
+
return { orderId };
|
|
66
|
+
});
|
|
67
|
+
// Transaction commits if successful
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
If an error is thrown, the transaction rolls back:
|
|
71
|
+
|
|
72
|
+
```typescript
|
|
73
|
+
try {
|
|
74
|
+
await unitOfWork.wrap(async (db) => {
|
|
75
|
+
await db.run("INSERT INTO orders (id, status) VALUES (?, ?)", [orderId, "pending"]);
|
|
76
|
+
throw new Error("Something went wrong");
|
|
77
|
+
});
|
|
78
|
+
} catch (error) {
|
|
79
|
+
// Transaction rolled back - no order was inserted
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Accessing the Client
|
|
84
|
+
|
|
85
|
+
Within a transaction, access the database through `getClient()`:
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
// Inside a command handler
|
|
89
|
+
const db = ctx.getUnitOfWork().getClient();
|
|
90
|
+
await db.run("UPDATE orders SET status = ? WHERE id = ?", ["confirmed", orderId]);
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Note: `getClient()` throws an error if called outside of a `wrap()` call.
|
|
94
|
+
|
|
95
|
+
### Nested Transactions
|
|
96
|
+
|
|
97
|
+
Nested `wrap()` calls participate in the same transaction:
|
|
98
|
+
|
|
99
|
+
```typescript
|
|
100
|
+
await unitOfWork.wrap(async (db) => {
|
|
101
|
+
await db.run("INSERT INTO orders (id) VALUES (?)", ["order-1"]);
|
|
102
|
+
|
|
103
|
+
await unitOfWork.wrap(async (db) => {
|
|
104
|
+
await db.run("INSERT INTO order_items (order_id) VALUES (?)", ["order-1"]);
|
|
105
|
+
});
|
|
106
|
+
// Both inserts are in the same transaction
|
|
107
|
+
});
|
|
108
|
+
// Single COMMIT at the end
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
If any nested call throws, the entire transaction rolls back:
|
|
112
|
+
|
|
113
|
+
```typescript
|
|
114
|
+
try {
|
|
115
|
+
await unitOfWork.wrap(async (db) => {
|
|
116
|
+
await db.run("INSERT INTO orders (id) VALUES (?)", ["order-1"]);
|
|
117
|
+
|
|
118
|
+
await unitOfWork.wrap(async (db) => {
|
|
119
|
+
await db.run("INSERT INTO order_items (order_id) VALUES (?)", ["order-1"]);
|
|
120
|
+
throw new Error("Nested failure");
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
} catch {
|
|
124
|
+
// Both inserts rolled back
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Usage
|
|
129
|
+
|
|
130
|
+
### Test Setup
|
|
131
|
+
|
|
132
|
+
Use the test utilities for fast, isolated integration tests:
|
|
133
|
+
|
|
134
|
+
```typescript
|
|
135
|
+
import type { Database } from "sqlite";
|
|
136
|
+
import { SqliteUnitOfWork } from "@hexaijs/sqlite";
|
|
137
|
+
import { getSqliteConnection } from "@hexaijs/sqlite/test";
|
|
138
|
+
|
|
139
|
+
describe("OrderRepository", () => {
|
|
140
|
+
let db: Database;
|
|
141
|
+
let unitOfWork: SqliteUnitOfWork;
|
|
142
|
+
|
|
143
|
+
beforeEach(async () => {
|
|
144
|
+
// Create fresh in-memory database
|
|
145
|
+
db = await getSqliteConnection();
|
|
146
|
+
|
|
147
|
+
// Create schema
|
|
148
|
+
await db.run(`
|
|
149
|
+
CREATE TABLE orders (
|
|
150
|
+
id TEXT PRIMARY KEY,
|
|
151
|
+
status TEXT NOT NULL
|
|
152
|
+
)
|
|
153
|
+
`);
|
|
154
|
+
|
|
155
|
+
unitOfWork = new SqliteUnitOfWork(db);
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
afterEach(async () => {
|
|
159
|
+
await db.close();
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
it("should persist orders", async () => {
|
|
163
|
+
await unitOfWork.wrap(async (db) => {
|
|
164
|
+
await db.run("INSERT INTO orders (id, status) VALUES (?, ?)", ["order-1", "pending"]);
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
const result = await db.get("SELECT * FROM orders WHERE id = ?", ["order-1"]);
|
|
168
|
+
expect(result.status).toBe("pending");
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### SqliteRepositoryForTest
|
|
174
|
+
|
|
175
|
+
The `SqliteRepositoryForTest` provides a generic repository implementation for test fixtures. It implements the `Repository<E>` interface from `@hexaijs/core`.
|
|
176
|
+
|
|
177
|
+
```typescript
|
|
178
|
+
import { SqliteRepositoryForTest, getSqliteConnection } from "@hexaijs/sqlite/test";
|
|
179
|
+
import { Identifiable, IdOf } from "@hexaijs/core";
|
|
180
|
+
|
|
181
|
+
// Define your entity
|
|
182
|
+
class Order implements Identifiable<OrderId> {
|
|
183
|
+
constructor(
|
|
184
|
+
private id: OrderId,
|
|
185
|
+
private status: string
|
|
186
|
+
) {}
|
|
187
|
+
|
|
188
|
+
getId(): OrderId {
|
|
189
|
+
return this.id;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
getStatus(): string {
|
|
193
|
+
return this.status;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Define memento for serialization
|
|
198
|
+
interface OrderMemento {
|
|
199
|
+
id: string;
|
|
200
|
+
status: string;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Create repository
|
|
204
|
+
const db = await getSqliteConnection();
|
|
205
|
+
const orderRepository = new SqliteRepositoryForTest<Order, OrderMemento>(db, {
|
|
206
|
+
namespace: "orders",
|
|
207
|
+
hydrate: (m) => new Order(new OrderId(m.id), m.status),
|
|
208
|
+
dehydrate: (e) => ({ id: e.getId().getValue(), status: e.getStatus() }),
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
// Use repository
|
|
212
|
+
await orderRepository.add(new Order(new OrderId("order-1"), "pending"));
|
|
213
|
+
const order = await orderRepository.get(new OrderId("order-1"));
|
|
214
|
+
await orderRepository.update(order);
|
|
215
|
+
const count = await orderRepository.count();
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
The repository automatically creates its table on first use. Each repository uses a separate table identified by its namespace.
|
|
219
|
+
|
|
220
|
+
### File-Based Database
|
|
221
|
+
|
|
222
|
+
For scenarios requiring persistence across test runs or debugging:
|
|
223
|
+
|
|
224
|
+
```typescript
|
|
225
|
+
import { getSqliteConnection } from "@hexaijs/sqlite/test";
|
|
226
|
+
|
|
227
|
+
// File-based database instead of in-memory
|
|
228
|
+
const db = await getSqliteConnection("./test-database.sqlite");
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
## API Highlights
|
|
232
|
+
|
|
233
|
+
| Export | Description |
|
|
234
|
+
|--------|-------------|
|
|
235
|
+
| `SqliteUnitOfWork` | Transaction management implementing `UnitOfWork<Database>` |
|
|
236
|
+
|
|
237
|
+
**From `@hexaijs/sqlite/test`:**
|
|
238
|
+
|
|
239
|
+
| Export | Description |
|
|
240
|
+
|--------|-------------|
|
|
241
|
+
| `getSqliteConnection` | Creates SQLite connection (in-memory by default) |
|
|
242
|
+
| `SqliteRepositoryForTest` | Generic repository for test fixtures |
|
|
243
|
+
|
|
244
|
+
## See Also
|
|
245
|
+
|
|
246
|
+
- [@hexaijs/core](../core/README.md) - Core interfaces (`UnitOfWork`, `Repository`)
|
|
247
|
+
- [@hexaijs/postgres](../postgres/README.md) - PostgreSQL implementation for production
|
|
248
|
+
- [@hexaijs/application](../application/README.md) - Application context that provides `getUnitOfWork()`
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,uBAAuB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./sqlite-unit-of-work"), exports);
|
|
18
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,wDAAsC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Database } from "sqlite";
|
|
2
|
+
import { UnitOfWork } from "@hexaijs/core";
|
|
3
|
+
export declare class SqliteUnitOfWork implements UnitOfWork<Database> {
|
|
4
|
+
private db;
|
|
5
|
+
private static transactions;
|
|
6
|
+
constructor(db: Database);
|
|
7
|
+
getClient(): Database;
|
|
8
|
+
wrap<T>(fn: (client: Database) => Promise<T>): Promise<T>;
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=sqlite-unit-of-work.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sqlite-unit-of-work.d.ts","sourceRoot":"","sources":["../src/sqlite-unit-of-work.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAEvC,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAE3C,qBAAa,gBAAiB,YAAW,UAAU,CAAC,QAAQ,CAAC;IAS7C,OAAO,CAAC,EAAE;IARtB,OAAO,CAAC,MAAM,CAAC,YAAY,CAMvB;gBAEgB,EAAE,EAAE,QAAQ;IAShC,SAAS,IAAI,QAAQ;IAQf,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,MAAM,EAAE,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;CAwBlE"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SqliteUnitOfWork = void 0;
|
|
4
|
+
class SqliteUnitOfWork {
|
|
5
|
+
db;
|
|
6
|
+
static transactions = new WeakMap();
|
|
7
|
+
constructor(db) {
|
|
8
|
+
this.db = db;
|
|
9
|
+
if (!SqliteUnitOfWork.transactions.has(db)) {
|
|
10
|
+
SqliteUnitOfWork.transactions.set(db, {
|
|
11
|
+
level: 0,
|
|
12
|
+
aborted: false,
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
getClient() {
|
|
17
|
+
const current = SqliteUnitOfWork.transactions.get(this.db);
|
|
18
|
+
if (!current || current.level === 0) {
|
|
19
|
+
throw new Error("No transaction is active");
|
|
20
|
+
}
|
|
21
|
+
return this.db;
|
|
22
|
+
}
|
|
23
|
+
async wrap(fn) {
|
|
24
|
+
const current = SqliteUnitOfWork.transactions.get(this.db);
|
|
25
|
+
if (++current.level === 1) {
|
|
26
|
+
await this.db.run("BEGIN TRANSACTION");
|
|
27
|
+
}
|
|
28
|
+
try {
|
|
29
|
+
return await fn(this.db);
|
|
30
|
+
}
|
|
31
|
+
catch (e) {
|
|
32
|
+
if (!current.aborted) {
|
|
33
|
+
current.aborted = true;
|
|
34
|
+
}
|
|
35
|
+
throw e;
|
|
36
|
+
}
|
|
37
|
+
finally {
|
|
38
|
+
if (--current.level === 0) {
|
|
39
|
+
if (current.aborted) {
|
|
40
|
+
await this.db.run("ROLLBACK");
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
await this.db.run("COMMIT");
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
exports.SqliteUnitOfWork = SqliteUnitOfWork;
|
|
50
|
+
//# sourceMappingURL=sqlite-unit-of-work.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sqlite-unit-of-work.js","sourceRoot":"","sources":["../src/sqlite-unit-of-work.ts"],"names":[],"mappings":";;;AAIA,MAAa,gBAAgB;IASL;IARZ,MAAM,CAAC,YAAY,GAAG,IAAI,OAAO,EAMtC,CAAC;IAEJ,YAAoB,EAAY;QAAZ,OAAE,GAAF,EAAE,CAAU;QAC5B,IAAI,CAAC,gBAAgB,CAAC,YAAY,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC;YACzC,gBAAgB,CAAC,YAAY,CAAC,GAAG,CAAC,EAAE,EAAE;gBAClC,KAAK,EAAE,CAAC;gBACR,OAAO,EAAE,KAAK;aACjB,CAAC,CAAC;QACP,CAAC;IACL,CAAC;IAED,SAAS;QACL,MAAM,OAAO,GAAG,gBAAgB,CAAC,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC3D,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,KAAK,KAAK,CAAC,EAAE,CAAC;YAClC,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAC;QAChD,CAAC;QACD,OAAO,IAAI,CAAC,EAAE,CAAC;IACnB,CAAC;IAED,KAAK,CAAC,IAAI,CAAI,EAAoC;QAC9C,MAAM,OAAO,GAAG,gBAAgB,CAAC,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAE,CAAC;QAC5D,IAAI,EAAE,OAAO,CAAC,KAAK,KAAK,CAAC,EAAE,CAAC;YACxB,MAAM,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAC;QAC3C,CAAC;QAED,IAAI,CAAC;YACD,OAAO,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC7B,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACT,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;gBACnB,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC;YAC3B,CAAC;YAED,MAAM,CAAC,CAAC;QACZ,CAAC;gBAAS,CAAC;YACP,IAAI,EAAE,OAAO,CAAC,KAAK,KAAK,CAAC,EAAE,CAAC;gBACxB,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;oBAClB,MAAM,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;gBAClC,CAAC;qBAAM,CAAC;oBACJ,MAAM,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;gBAChC,CAAC;YACL,CAAC;QACL,CAAC;IACL,CAAC;;AAjDL,4CAkDC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/test/index.ts"],"names":[],"mappings":"AAAA,cAAc,8BAA8B,CAAC;AAC7C,cAAc,SAAS,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./sqlite-repository-for-test"), exports);
|
|
18
|
+
__exportStar(require("./utils"), exports);
|
|
19
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/test/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,+DAA6C;AAC7C,0CAAwB"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { Database } from "sqlite";
|
|
2
|
+
import { Identifiable, IdOf, Repository } from "@hexaijs/core";
|
|
3
|
+
export declare class SqliteRepositoryForTest<E extends Identifiable<any>, M> implements Repository<E> {
|
|
4
|
+
protected db: Database;
|
|
5
|
+
protected namespace: string;
|
|
6
|
+
protected hydrate: (memento: M) => E;
|
|
7
|
+
protected dehydrate: (entity: E) => M;
|
|
8
|
+
constructor(db: Database, { namespace, hydrate, dehydrate, }: {
|
|
9
|
+
namespace: string;
|
|
10
|
+
hydrate: (memento: M) => E;
|
|
11
|
+
dehydrate: (entity: E) => M;
|
|
12
|
+
});
|
|
13
|
+
get(id: IdOf<E>): Promise<E>;
|
|
14
|
+
add(entity: E): Promise<void>;
|
|
15
|
+
update(entity: E): Promise<void>;
|
|
16
|
+
count(): Promise<number>;
|
|
17
|
+
protected ensureTableExists(): Promise<void>;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=sqlite-repository-for-test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sqlite-repository-for-test.d.ts","sourceRoot":"","sources":["../../src/test/sqlite-repository-for-test.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAEvC,OAAO,EAEH,YAAY,EACZ,IAAI,EAEJ,UAAU,EACb,MAAM,eAAe,CAAC;AAEvB,qBAAa,uBAAuB,CAChC,CAAC,SAAS,YAAY,CAAC,GAAG,CAAC,EAC3B,CAAC,CACH,YAAW,UAAU,CAAC,CAAC,CAAC;IAMlB,SAAS,CAAC,EAAE,EAAE,QAAQ;IAL1B,SAAS,CAAC,SAAS,EAAE,MAAM,CAAC;IAC5B,SAAS,CAAC,OAAO,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,CAAC;IACrC,SAAS,CAAC,SAAS,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,CAAC;gBAGxB,EAAE,EAAE,QAAQ,EACtB,EACI,SAAS,EACT,OAAO,EACP,SAAS,GACZ,EAAE;QACC,SAAS,EAAE,MAAM,CAAC;QAClB,OAAO,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,CAAC;QAC3B,SAAS,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,CAAC;KAC/B;IAOC,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAgB5B,GAAG,CAAC,MAAM,EAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAuB7B,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAkBhC,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC;cAUd,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC;CAQrD"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SqliteRepositoryForTest = void 0;
|
|
4
|
+
const core_1 = require("@hexaijs/core");
|
|
5
|
+
class SqliteRepositoryForTest {
|
|
6
|
+
db;
|
|
7
|
+
namespace;
|
|
8
|
+
hydrate;
|
|
9
|
+
dehydrate;
|
|
10
|
+
constructor(db, { namespace, hydrate, dehydrate, }) {
|
|
11
|
+
this.db = db;
|
|
12
|
+
this.namespace = namespace;
|
|
13
|
+
this.hydrate = hydrate;
|
|
14
|
+
this.dehydrate = dehydrate;
|
|
15
|
+
}
|
|
16
|
+
async get(id) {
|
|
17
|
+
await this.ensureTableExists();
|
|
18
|
+
const row = await this.db.get(`SELECT * FROM ${this.namespace} WHERE id = ?`, id.getValue());
|
|
19
|
+
if (!row) {
|
|
20
|
+
throw new core_1.ObjectNotFoundError(`entity with id '${id.getValue()}' not found`);
|
|
21
|
+
}
|
|
22
|
+
return this.hydrate(JSON.parse(row.data));
|
|
23
|
+
}
|
|
24
|
+
async add(entity) {
|
|
25
|
+
await this.ensureTableExists();
|
|
26
|
+
try {
|
|
27
|
+
await this.db.run(`INSERT INTO ${this.namespace} (id, data)
|
|
28
|
+
VALUES (?, ?)`, entity.getId().getValue(), JSON.stringify(this.dehydrate(entity)));
|
|
29
|
+
}
|
|
30
|
+
catch (e) {
|
|
31
|
+
if (e.message.includes("UNIQUE constraint failed")) {
|
|
32
|
+
throw new core_1.DuplicateObjectError(`entity with id '${entity
|
|
33
|
+
.getId()
|
|
34
|
+
.getValue()}' already exists`);
|
|
35
|
+
}
|
|
36
|
+
throw e;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
async update(entity) {
|
|
40
|
+
await this.ensureTableExists();
|
|
41
|
+
const result = await this.db.run(`UPDATE ${this.namespace}
|
|
42
|
+
SET data = ?
|
|
43
|
+
WHERE id = ?`, JSON.stringify(this.dehydrate(entity)), entity.getId().getValue());
|
|
44
|
+
if (result.changes === 0) {
|
|
45
|
+
throw new core_1.ObjectNotFoundError(`entity with id '${entity.getId().getValue()}' not found`);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
async count() {
|
|
49
|
+
await this.ensureTableExists();
|
|
50
|
+
const result = await this.db.get(`SELECT COUNT(*) AS count FROM ${this.namespace}`);
|
|
51
|
+
return result.count;
|
|
52
|
+
}
|
|
53
|
+
async ensureTableExists() {
|
|
54
|
+
await this.db.run(`
|
|
55
|
+
CREATE TABLE IF NOT EXISTS ${this.namespace} (
|
|
56
|
+
id TEXT NOT NULL PRIMARY KEY UNIQUE,
|
|
57
|
+
data TEXT NOT NULL
|
|
58
|
+
)
|
|
59
|
+
`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
exports.SqliteRepositoryForTest = SqliteRepositoryForTest;
|
|
63
|
+
//# sourceMappingURL=sqlite-repository-for-test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sqlite-repository-for-test.js","sourceRoot":"","sources":["../../src/test/sqlite-repository-for-test.ts"],"names":[],"mappings":";;;AAEA,wCAMuB;AAEvB,MAAa,uBAAuB;IASlB;IALJ,SAAS,CAAS;IAClB,OAAO,CAAoB;IAC3B,SAAS,CAAmB;IAEtC,YACc,EAAY,EACtB,EACI,SAAS,EACT,OAAO,EACP,SAAS,GAKZ;QATS,OAAE,GAAF,EAAE,CAAU;QAWtB,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;QAC3B,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;IAC/B,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,EAAW;QACjB,MAAM,IAAI,CAAC,iBAAiB,EAAE,CAAC;QAE/B,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,EAAE,CAAC,GAAG,CACzB,iBAAiB,IAAI,CAAC,SAAS,eAAe,EAC9C,EAAE,CAAC,QAAQ,EAAE,CAChB,CAAC;QACF,IAAI,CAAC,GAAG,EAAE,CAAC;YACP,MAAM,IAAI,0BAAmB,CACzB,mBAAmB,EAAE,CAAC,QAAQ,EAAE,aAAa,CAChD,CAAC;QACN,CAAC;QAED,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC;IAC9C,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,MAAS;QACf,MAAM,IAAI,CAAC,iBAAiB,EAAE,CAAC;QAE/B,IAAI,CAAC;YACD,MAAM,IAAI,CAAC,EAAE,CAAC,GAAG,CACb,eAAe,IAAI,CAAC,SAAS;+BACd,EACf,MAAM,CAAC,KAAK,EAAE,CAAC,QAAQ,EAAE,EACzB,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CACzC,CAAC;QACN,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACT,IAAK,CAAW,CAAC,OAAO,CAAC,QAAQ,CAAC,0BAA0B,CAAC,EAAE,CAAC;gBAC5D,MAAM,IAAI,2BAAoB,CAC1B,mBAAmB,MAAM;qBACpB,KAAK,EAAE;qBACP,QAAQ,EAAE,kBAAkB,CACpC,CAAC;YACN,CAAC;YAED,MAAM,CAAC,CAAC;QACZ,CAAC;IACL,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,MAAS;QAClB,MAAM,IAAI,CAAC,iBAAiB,EAAE,CAAC;QAE/B,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,EAAE,CAAC,GAAG,CAC5B,UAAU,IAAI,CAAC,SAAS;;8BAEN,EAClB,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,EACtC,MAAM,CAAC,KAAK,EAAE,CAAC,QAAQ,EAAE,CAC5B,CAAC;QAEF,IAAI,MAAM,CAAC,OAAO,KAAK,CAAC,EAAE,CAAC;YACvB,MAAM,IAAI,0BAAmB,CACzB,mBAAmB,MAAM,CAAC,KAAK,EAAE,CAAC,QAAQ,EAAE,aAAa,CAC5D,CAAC;QACN,CAAC;IACL,CAAC;IAED,KAAK,CAAC,KAAK;QACP,MAAM,IAAI,CAAC,iBAAiB,EAAE,CAAC;QAE/B,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,EAAE,CAAC,GAAG,CAC5B,iCAAiC,IAAI,CAAC,SAAS,EAAE,CACpD,CAAC;QAEF,OAAO,MAAM,CAAC,KAAK,CAAC;IACxB,CAAC;IAES,KAAK,CAAC,iBAAiB;QAC7B,MAAM,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC;yCACe,IAAI,CAAC,SAAS;;;;SAI9C,CAAC,CAAC;IACP,CAAC;CACJ;AApGD,0DAoGC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/test/utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAEvC,wBAAsB,mBAAmB,CACrC,QAAQ,SAAa,GACtB,OAAO,CAAC,QAAQ,CAAC,CAOnB"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getSqliteConnection = getSqliteConnection;
|
|
4
|
+
async function getSqliteConnection(filename = ":memory:") {
|
|
5
|
+
const sqlite = await import("sqlite");
|
|
6
|
+
const sqlite3 = await import("sqlite3");
|
|
7
|
+
return await sqlite.open({
|
|
8
|
+
filename,
|
|
9
|
+
driver: sqlite3.default.Database,
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/test/utils.ts"],"names":[],"mappings":";;AAEA,kDASC;AATM,KAAK,UAAU,mBAAmB,CACrC,QAAQ,GAAG,UAAU;IAErB,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,QAAQ,CAAC,CAAC;IACtC,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,SAAS,CAAC,CAAC;IACxC,OAAO,MAAM,MAAM,CAAC,IAAI,CAAC;QACrB,QAAQ;QACR,MAAM,EAAE,OAAO,CAAC,OAAO,CAAC,QAAQ;KACnC,CAAC,CAAC;AACP,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@hexaijs/sqlite",
|
|
3
|
+
"publishConfig": {
|
|
4
|
+
"access": "public"
|
|
5
|
+
},
|
|
6
|
+
"version": "0.1.0",
|
|
7
|
+
"description": "SQLite support for hexai",
|
|
8
|
+
"license": "MIT",
|
|
9
|
+
"author": "Sangwoo Hyun <wkdny.hyun@gmail.com>",
|
|
10
|
+
"contributors": [
|
|
11
|
+
"Seungcheol Baek <victoryiron.baek@gmail.com>",
|
|
12
|
+
"Donghyun Lee <edonghyun@naver.com>"
|
|
13
|
+
],
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "git+https://github.com/workingdanny911/hexai.git",
|
|
17
|
+
"directory": "packages/sqlite"
|
|
18
|
+
},
|
|
19
|
+
"homepage": "https://github.com/workingdanny911/hexai#readme",
|
|
20
|
+
"bugs": {
|
|
21
|
+
"url": "https://github.com/workingdanny911/hexai/issues"
|
|
22
|
+
},
|
|
23
|
+
"keywords": [
|
|
24
|
+
"hexai",
|
|
25
|
+
"hexagonal",
|
|
26
|
+
"clean-architecture",
|
|
27
|
+
"sqlite",
|
|
28
|
+
"database",
|
|
29
|
+
"typescript"
|
|
30
|
+
],
|
|
31
|
+
"files": [
|
|
32
|
+
"dist",
|
|
33
|
+
"LICENSE"
|
|
34
|
+
],
|
|
35
|
+
"exports": {
|
|
36
|
+
".": {
|
|
37
|
+
"types": "./dist/index.d.ts",
|
|
38
|
+
"import": "./dist/index.js",
|
|
39
|
+
"require": "./dist/index.js"
|
|
40
|
+
},
|
|
41
|
+
"./test": {
|
|
42
|
+
"types": "./dist/test/index.d.ts",
|
|
43
|
+
"import": "./dist/test/index.js",
|
|
44
|
+
"require": "./dist/test/index.js"
|
|
45
|
+
},
|
|
46
|
+
"./package.json": "./package.json"
|
|
47
|
+
},
|
|
48
|
+
"dependencies": {},
|
|
49
|
+
"peerDependencies": {
|
|
50
|
+
"sqlite": "^5.1.1",
|
|
51
|
+
"sqlite3": "^5.1.7",
|
|
52
|
+
"@hexaijs/core": "^0.2.0"
|
|
53
|
+
},
|
|
54
|
+
"devDependencies": {
|
|
55
|
+
"sqlite": "^5.1.1",
|
|
56
|
+
"sqlite3": "^5.1.7"
|
|
57
|
+
},
|
|
58
|
+
"scripts": {
|
|
59
|
+
"test": "vitest run",
|
|
60
|
+
"build": "tsc -p ./tsconfig.build.json && tsc-alias"
|
|
61
|
+
}
|
|
62
|
+
}
|