@streetjs/repository 1.0.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/ARCHITECTURE.md +72 -0
- package/CHANGELOG.md +25 -0
- package/LICENSE +21 -0
- package/README.md +116 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +25 -0
- package/dist/index.js.map +1 -0
- package/dist/repository.d.ts +58 -0
- package/dist/repository.d.ts.map +1 -0
- package/dist/repository.js +127 -0
- package/dist/repository.js.map +1 -0
- package/package.json +89 -0
package/ARCHITECTURE.md
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# Architecture — @streetjs/repository
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
|
|
5
|
+
`@streetjs/repository` is the StreetJS data-access base layer: a generic,
|
|
6
|
+
strongly-typed CRUD repository over a PostgreSQL pool, plus a small atomic
|
|
7
|
+
"ledger" transaction helper. Domain repositories in an application extend
|
|
8
|
+
`StreetPostgresRepository<T>` and supply a table name and a row mapper.
|
|
9
|
+
|
|
10
|
+
## Dependencies
|
|
11
|
+
|
|
12
|
+
```
|
|
13
|
+
@streetjs/pool (PgPool — the query/transaction/stream surface)
|
|
14
|
+
@streetjs/postgres (PgConnection + StreetPostgresWireStream types)
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
No cyclic dependencies. Field-level encryption is decoupled via a structural
|
|
18
|
+
`FieldEncryptor` interface, so this package does **not** depend on the enterprise
|
|
19
|
+
data-policy module — any object with `encryptEntity`/`decryptEntity` qualifies
|
|
20
|
+
(the framework's `FieldEncryptor` class satisfies it structurally).
|
|
21
|
+
|
|
22
|
+
## Design
|
|
23
|
+
|
|
24
|
+
### Typed CRUD over parameterized SQL
|
|
25
|
+
|
|
26
|
+
`StreetPostgresRepository<T>` is abstract: subclasses declare `tableName` and
|
|
27
|
+
implement `mapRow(row)`. Every method issues **parameterized** queries
|
|
28
|
+
(`$1..$N`); values are never interpolated. `create`/`update` build column and
|
|
29
|
+
placeholder lists from the object's own keys, skipping `undefined` values.
|
|
30
|
+
`findAll` clamps `limit` into `[1, 1000]` and floors `offset` at `0` to bound
|
|
31
|
+
result sizes.
|
|
32
|
+
|
|
33
|
+
### Table-name safety
|
|
34
|
+
|
|
35
|
+
The only identifier that can't be parameterized is the table name. It is
|
|
36
|
+
validated against `^[a-zA-Z_][a-zA-Z0-9_.]*$` **lazily on first query** (the
|
|
37
|
+
abstract property isn't available in the base constructor), so a mis-declared
|
|
38
|
+
subclass fails fast with a clear error instead of emitting injectable SQL.
|
|
39
|
+
|
|
40
|
+
### Optional transparent encryption
|
|
41
|
+
|
|
42
|
+
When a subclass sets both `encryptor` (a `FieldEncryptor`) and `encryptedEntity`
|
|
43
|
+
(the annotated entity class), `create`/`update` run values through
|
|
44
|
+
`encryptEntity` before writing and `findById`/`findAll`/`create`/`update` run
|
|
45
|
+
returned rows through `decryptEntity`. When either is unset, both paths are
|
|
46
|
+
no-ops — zero overhead for repositories that don't opt in.
|
|
47
|
+
|
|
48
|
+
### Transactions and streaming
|
|
49
|
+
|
|
50
|
+
`withTransaction(fn)` delegates to `pool.transaction`, giving `fn` a single
|
|
51
|
+
connection with BEGIN/COMMIT (ROLLBACK on throw). `streamAll(sql)` delegates to
|
|
52
|
+
`pool.stream` for backpressured reads; parameterized streaming is not yet
|
|
53
|
+
supported and throws synchronously to steer callers to `pool.query`.
|
|
54
|
+
`LedgerTransactionService.execute(ops, onSuccess?)` runs a list of operations and
|
|
55
|
+
an optional success callback within one transaction.
|
|
56
|
+
|
|
57
|
+
## Testing
|
|
58
|
+
|
|
59
|
+
The suite runs with **no live database** using a fake pool that records queries
|
|
60
|
+
and returns scripted results. It covers all CRUD paths (including limit/offset
|
|
61
|
+
clamping, undefined-field skipping, empty-update fallthrough, and the
|
|
62
|
+
delete/insert edge cases), lazy table-name rejection, the encrypt-on-write /
|
|
63
|
+
decrypt-on-read cycle via a fake `FieldEncryptor`, streaming (both the
|
|
64
|
+
parameterized rejection and the delegation path), `withTransaction`, and the
|
|
65
|
+
ledger service with and without a success callback. Coverage is 100%.
|
|
66
|
+
|
|
67
|
+
## Non-goals
|
|
68
|
+
|
|
69
|
+
- No query builder, joins, or relations — this is a single-table CRUD base.
|
|
70
|
+
- No schema management (see `@streetjs/migrations`).
|
|
71
|
+
- No connection management (see `@streetjs/pool`).
|
|
72
|
+
- No encryption implementation — only the structural hook to plug one in.
|
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `@streetjs/repository` are documented here.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [1.0.0]
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- Initial standalone release of the StreetJS generic repository, extracted
|
|
13
|
+
verbatim from the `streetjs` core (`src/database/repository.ts`).
|
|
14
|
+
- `StreetPostgresRepository<T>` abstract base: typed `findById`, `findAll`
|
|
15
|
+
(with clamped pagination), `create`, `update`, `delete`, `count`,
|
|
16
|
+
`withTransaction`, and `streamAll`, all over parameterized SQL with lazy
|
|
17
|
+
table-name validation.
|
|
18
|
+
- Optional transparent field-level encryption via a structural `FieldEncryptor`
|
|
19
|
+
hook (`encryptor` + `encryptedEntity`), decoupled from the data-policy module.
|
|
20
|
+
- `LedgerTransactionService` for atomic multi-operation transactions.
|
|
21
|
+
- Public types: `IRepository<T>`, `FieldEncryptor`.
|
|
22
|
+
- Runs on `@streetjs/pool` and `@streetjs/postgres`; ESM. 19 tests (no live
|
|
23
|
+
database) with 100% coverage and a runnable example.
|
|
24
|
+
|
|
25
|
+
[1.0.0]: https://github.com/hassanmubiru/StreetJS/releases/tag/repository-v1.0.0
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 street contributors
|
|
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,116 @@
|
|
|
1
|
+
# @streetjs/repository
|
|
2
|
+
|
|
3
|
+
The StreetJS generic PostgreSQL repository: a typed CRUD base class over
|
|
4
|
+
[`@streetjs/pool`](https://www.npmjs.com/package/@streetjs/pool) with safe
|
|
5
|
+
identifier validation, pagination, streaming, transactions, and **optional
|
|
6
|
+
transparent field-level encryption**. Plus `LedgerTransactionService` for
|
|
7
|
+
running a sequence of operations atomically. ESM, strict-TypeScript.
|
|
8
|
+
|
|
9
|
+
This is the standalone home of the repository that also backs the
|
|
10
|
+
`streetjs/repository` subpath. The `streetjs` framework re-exports this package,
|
|
11
|
+
so there is a single source of truth.
|
|
12
|
+
|
|
13
|
+
## Install
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install @streetjs/repository @streetjs/pool
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
import { StreetPostgresRepository } from '@streetjs/repository';
|
|
23
|
+
|
|
24
|
+
interface User { id: string; name: string; email: string }
|
|
25
|
+
|
|
26
|
+
class UserRepository extends StreetPostgresRepository<User> {
|
|
27
|
+
protected readonly tableName = 'users';
|
|
28
|
+
protected mapRow(row: Record<string, string | null>): User {
|
|
29
|
+
return { id: row.id!, name: row.name!, email: row.email! };
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const users = new UserRepository(pool); // a PgPool
|
|
34
|
+
|
|
35
|
+
await users.create({ id: '1', name: 'Ada', email: 'ada@x.dev' });
|
|
36
|
+
await users.findById('1');
|
|
37
|
+
await users.findAll(20, 0); // limit clamped to [1, 1000], offset floored at 0
|
|
38
|
+
await users.update('1', { name: 'Ada L.' });
|
|
39
|
+
await users.count();
|
|
40
|
+
await users.delete('1');
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Subclasses provide a `tableName` and a `mapRow` that turns a raw row into your
|
|
44
|
+
entity. All queries are parameterized; the table name is validated against
|
|
45
|
+
`^[a-zA-Z_][a-zA-Z0-9_.]*$` on first use to prevent SQL injection through a
|
|
46
|
+
mis-declared subclass.
|
|
47
|
+
|
|
48
|
+
## Transactions & streaming
|
|
49
|
+
|
|
50
|
+
```ts
|
|
51
|
+
// Run arbitrary work on a single connection in a transaction:
|
|
52
|
+
await users.withTransaction(async (conn) => {
|
|
53
|
+
await conn.query('UPDATE users SET name = $1 WHERE id = $2', ['Neo', '1']);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
// Stream a large result set with backpressure:
|
|
57
|
+
const stream = await users.streamAll('SELECT * FROM users');
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
`LedgerTransactionService` runs a list of operations and an optional success
|
|
61
|
+
callback atomically:
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
import { LedgerTransactionService } from '@streetjs/repository';
|
|
65
|
+
|
|
66
|
+
await new LedgerTransactionService(pool).execute(
|
|
67
|
+
[
|
|
68
|
+
(conn) => conn.query('INSERT INTO ledger ...'),
|
|
69
|
+
(conn) => conn.query('UPDATE balances ...'),
|
|
70
|
+
],
|
|
71
|
+
async () => 'committed',
|
|
72
|
+
);
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Transparent field-level encryption
|
|
76
|
+
|
|
77
|
+
Set `encryptor` and `encryptedEntity` on a subclass to automatically encrypt
|
|
78
|
+
`@Encrypt()`-annotated fields on write and decrypt them on read. The `encryptor`
|
|
79
|
+
is any object satisfying the `FieldEncryptor` interface
|
|
80
|
+
(`encryptEntity`/`decryptEntity`) — the framework's data-policy `FieldEncryptor`
|
|
81
|
+
qualifies:
|
|
82
|
+
|
|
83
|
+
```ts
|
|
84
|
+
class SecureUserRepo extends StreetPostgresRepository<User> {
|
|
85
|
+
protected readonly tableName = 'users';
|
|
86
|
+
protected readonly encryptor = myFieldEncryptor;
|
|
87
|
+
protected readonly encryptedEntity = User;
|
|
88
|
+
protected mapRow(row) { /* ... */ }
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## API
|
|
93
|
+
|
|
94
|
+
| Member | Description |
|
|
95
|
+
| ------ | ----------- |
|
|
96
|
+
| `findById(id)` | Row by id, or `null`. |
|
|
97
|
+
| `findAll(limit?, offset?)` | Paginated rows (`ORDER BY created_at DESC`). |
|
|
98
|
+
| `create(data)` | Insert non-`undefined` fields, return the row. |
|
|
99
|
+
| `update(id, data)` | Patch fields; empty patch is a no-op read. |
|
|
100
|
+
| `delete(id)` | `true` if a row was deleted. |
|
|
101
|
+
| `count()` | Row count. |
|
|
102
|
+
| `withTransaction(fn)` | Run `fn(conn)` in a transaction. |
|
|
103
|
+
| `streamAll(sql, params?)` | Stream rows (non-parameterized only, for now). |
|
|
104
|
+
|
|
105
|
+
## Example
|
|
106
|
+
|
|
107
|
+
A complete runnable example lives in
|
|
108
|
+
[`src/examples/integration.ts`](./src/examples/integration.ts):
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
npm run example -w packages/repository
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## License
|
|
115
|
+
|
|
116
|
+
MIT — see [LICENSE](./LICENSE).
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @streetjs/repository — the StreetJS generic PostgreSQL repository.
|
|
3
|
+
*
|
|
4
|
+
* `StreetPostgresRepository<T>` is a typed CRUD base class over `@streetjs/pool`
|
|
5
|
+
* with safe identifier validation, pagination, streaming, transactions, and
|
|
6
|
+
* optional transparent field-level encryption. `LedgerTransactionService` runs a
|
|
7
|
+
* sequence of operations atomically. Public API only.
|
|
8
|
+
*
|
|
9
|
+
* ```ts
|
|
10
|
+
* import { StreetPostgresRepository } from '@streetjs/repository';
|
|
11
|
+
*
|
|
12
|
+
* class UserRepository extends StreetPostgresRepository<User> {
|
|
13
|
+
* protected readonly tableName = 'users';
|
|
14
|
+
* protected mapRow(row): User { return { id: row.id!, name: row.name! }; }
|
|
15
|
+
* }
|
|
16
|
+
* const users = new UserRepository(pool);
|
|
17
|
+
* await users.findById('42');
|
|
18
|
+
* ```
|
|
19
|
+
*
|
|
20
|
+
* > This is the standalone home of the repository that also backs the
|
|
21
|
+
* > `streetjs/repository` subpath; the `streetjs` framework re-exports it, so
|
|
22
|
+
* > there is a single implementation.
|
|
23
|
+
*/
|
|
24
|
+
export { StreetPostgresRepository, LedgerTransactionService, } from './repository.js';
|
|
25
|
+
export type { IRepository, FieldEncryptor } from './repository.js';
|
|
26
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EACL,wBAAwB,EACxB,wBAAwB,GACzB,MAAM,iBAAiB,CAAC;AACzB,YAAY,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @streetjs/repository — the StreetJS generic PostgreSQL repository.
|
|
3
|
+
*
|
|
4
|
+
* `StreetPostgresRepository<T>` is a typed CRUD base class over `@streetjs/pool`
|
|
5
|
+
* with safe identifier validation, pagination, streaming, transactions, and
|
|
6
|
+
* optional transparent field-level encryption. `LedgerTransactionService` runs a
|
|
7
|
+
* sequence of operations atomically. Public API only.
|
|
8
|
+
*
|
|
9
|
+
* ```ts
|
|
10
|
+
* import { StreetPostgresRepository } from '@streetjs/repository';
|
|
11
|
+
*
|
|
12
|
+
* class UserRepository extends StreetPostgresRepository<User> {
|
|
13
|
+
* protected readonly tableName = 'users';
|
|
14
|
+
* protected mapRow(row): User { return { id: row.id!, name: row.name! }; }
|
|
15
|
+
* }
|
|
16
|
+
* const users = new UserRepository(pool);
|
|
17
|
+
* await users.findById('42');
|
|
18
|
+
* ```
|
|
19
|
+
*
|
|
20
|
+
* > This is the standalone home of the repository that also backs the
|
|
21
|
+
* > `streetjs/repository` subpath; the `streetjs` framework re-exports it, so
|
|
22
|
+
* > there is a single implementation.
|
|
23
|
+
*/
|
|
24
|
+
export { StreetPostgresRepository, LedgerTransactionService, } from './repository.js';
|
|
25
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EACL,wBAAwB,EACxB,wBAAwB,GACzB,MAAM,iBAAiB,CAAC"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { PgPool } from '@streetjs/pool';
|
|
2
|
+
import type { PgConnection, StreetPostgresWireStream } from '@streetjs/postgres';
|
|
3
|
+
export interface IRepository<T> {
|
|
4
|
+
findById(id: string): Promise<T | null>;
|
|
5
|
+
findAll(limit: number, offset: number): Promise<T[]>;
|
|
6
|
+
create(data: Partial<T>): Promise<T>;
|
|
7
|
+
update(id: string, data: Partial<T>): Promise<T | null>;
|
|
8
|
+
delete(id: string): Promise<boolean>;
|
|
9
|
+
count(): Promise<number>;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Minimal structural interface for transparent field-level encryption. The
|
|
13
|
+
* framework's `FieldEncryptor` (in `@streetjs/*` enterprise data-policy) satisfies
|
|
14
|
+
* it; declaring it structurally keeps this package free of a data-policy
|
|
15
|
+
* dependency while preserving the optional-encryption feature.
|
|
16
|
+
*/
|
|
17
|
+
export interface FieldEncryptor {
|
|
18
|
+
encryptEntity<T extends Record<string, unknown>>(entityClass: new (...a: never[]) => unknown, obj: T): T;
|
|
19
|
+
decryptEntity<T extends Record<string, unknown>>(entityClass: new (...a: never[]) => unknown, obj: T): T;
|
|
20
|
+
}
|
|
21
|
+
export declare abstract class StreetPostgresRepository<T extends object> implements IRepository<T> {
|
|
22
|
+
protected readonly pool: PgPool;
|
|
23
|
+
protected abstract readonly tableName: string;
|
|
24
|
+
constructor(pool: PgPool);
|
|
25
|
+
/** Validate tableName on first use (abstract property not available in constructor) */
|
|
26
|
+
private _assertSafeTableName;
|
|
27
|
+
protected abstract mapRow(row: Record<string, string | null>): T;
|
|
28
|
+
/**
|
|
29
|
+
* Optional transparent field-level encryption. Subclasses that set both
|
|
30
|
+
* `encryptor` and `encryptedEntity` get automatic AES-256-GCM encryption of
|
|
31
|
+
* `@Encrypt()`-annotated fields on `create()`/`update()` and decryption on
|
|
32
|
+
* `findById()`/`findAll()`. Defaults to undefined (no encryption).
|
|
33
|
+
*/
|
|
34
|
+
protected readonly encryptor?: FieldEncryptor;
|
|
35
|
+
protected readonly encryptedEntity?: new (...a: never[]) => unknown;
|
|
36
|
+
private _encrypt;
|
|
37
|
+
private _decrypt;
|
|
38
|
+
findById(id: string): Promise<T | null>;
|
|
39
|
+
findAll(limit?: number, offset?: number): Promise<T[]>;
|
|
40
|
+
count(): Promise<number>;
|
|
41
|
+
create(data: Partial<T>): Promise<T>;
|
|
42
|
+
update(id: string, data: Partial<T>): Promise<T | null>;
|
|
43
|
+
delete(id: string): Promise<boolean>;
|
|
44
|
+
/** Execute raw SQL within a transaction */
|
|
45
|
+
withTransaction<R>(fn: (conn: PgConnection) => Promise<R>): Promise<R>;
|
|
46
|
+
/** Stream rows with backpressure.
|
|
47
|
+
* Finding 6 fix: accepts parameterized queries only — raw SQL without
|
|
48
|
+
* params is still possible but callers should always use $1..$N placeholders.
|
|
49
|
+
* The method signature now accepts params to discourage raw interpolation.
|
|
50
|
+
*/
|
|
51
|
+
streamAll(sql: string, params?: unknown[]): Promise<StreetPostgresWireStream>;
|
|
52
|
+
}
|
|
53
|
+
export declare class LedgerTransactionService {
|
|
54
|
+
private readonly pool;
|
|
55
|
+
constructor(pool: PgPool);
|
|
56
|
+
execute<T>(operations: Array<(conn: PgConnection) => Promise<void>>, onSuccess?: () => Promise<T>): Promise<T | void>;
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=repository.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"repository.d.ts","sourceRoot":"","sources":["../src/repository.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
// src/repository.ts
|
|
2
|
+
// Generic repository with typed CRUD, safe identifiers, streaming, transactions,
|
|
3
|
+
// and optional transparent field-level encryption.
|
|
4
|
+
// ─── Base repository ────────────────────────────────────────────────────────────
|
|
5
|
+
// Finding 6 fix: safe table/schema name pattern
|
|
6
|
+
const SAFE_TABLE_NAME_RE = /^[a-zA-Z_][a-zA-Z0-9_.]*$/;
|
|
7
|
+
export class StreetPostgresRepository {
|
|
8
|
+
pool;
|
|
9
|
+
constructor(pool) {
|
|
10
|
+
this.pool = pool;
|
|
11
|
+
// Finding 6 fix: validate tableName at construction time so a bad subclass
|
|
12
|
+
// fails immediately rather than silently injecting SQL at query time.
|
|
13
|
+
// We defer the check to the first query because abstract properties are
|
|
14
|
+
// not yet initialised in the base constructor — use a lazy validator instead.
|
|
15
|
+
}
|
|
16
|
+
/** Validate tableName on first use (abstract property not available in constructor) */
|
|
17
|
+
_assertSafeTableName() {
|
|
18
|
+
if (!SAFE_TABLE_NAME_RE.test(this.tableName)) {
|
|
19
|
+
throw new Error(`Repository tableName contains unsafe characters: "${this.tableName}". ` +
|
|
20
|
+
'Only letters, digits, underscores, and dots are allowed.');
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Optional transparent field-level encryption. Subclasses that set both
|
|
25
|
+
* `encryptor` and `encryptedEntity` get automatic AES-256-GCM encryption of
|
|
26
|
+
* `@Encrypt()`-annotated fields on `create()`/`update()` and decryption on
|
|
27
|
+
* `findById()`/`findAll()`. Defaults to undefined (no encryption).
|
|
28
|
+
*/
|
|
29
|
+
encryptor;
|
|
30
|
+
encryptedEntity;
|
|
31
|
+
_encrypt(data) {
|
|
32
|
+
if (!this.encryptor || !this.encryptedEntity)
|
|
33
|
+
return data;
|
|
34
|
+
return this.encryptor.encryptEntity(this.encryptedEntity, data);
|
|
35
|
+
}
|
|
36
|
+
_decrypt(entity) {
|
|
37
|
+
if (!this.encryptor || !this.encryptedEntity)
|
|
38
|
+
return entity;
|
|
39
|
+
return this.encryptor.decryptEntity(this.encryptedEntity, entity);
|
|
40
|
+
}
|
|
41
|
+
async findById(id) {
|
|
42
|
+
this._assertSafeTableName();
|
|
43
|
+
const result = await this.pool.query(`SELECT * FROM ${this.tableName} WHERE id = $1 LIMIT 1`, [id]);
|
|
44
|
+
if (result.rows.length === 0)
|
|
45
|
+
return null;
|
|
46
|
+
return this._decrypt(this.mapRow(result.rows[0]));
|
|
47
|
+
}
|
|
48
|
+
async findAll(limit = 20, offset = 0) {
|
|
49
|
+
this._assertSafeTableName();
|
|
50
|
+
const safeLimit = Math.min(Math.max(1, Math.floor(limit)), 1000);
|
|
51
|
+
const safeOffset = Math.max(0, Math.floor(offset));
|
|
52
|
+
const result = await this.pool.query(`SELECT * FROM ${this.tableName} ORDER BY created_at DESC LIMIT $1 OFFSET $2`, [safeLimit, safeOffset]);
|
|
53
|
+
return result.rows.map((r) => this._decrypt(this.mapRow(r)));
|
|
54
|
+
}
|
|
55
|
+
async count() {
|
|
56
|
+
this._assertSafeTableName();
|
|
57
|
+
const result = await this.pool.query(`SELECT COUNT(*) AS total FROM ${this.tableName}`);
|
|
58
|
+
return parseInt(result.rows[0]?.['total'] ?? '0', 10);
|
|
59
|
+
}
|
|
60
|
+
async create(data) {
|
|
61
|
+
this._assertSafeTableName();
|
|
62
|
+
data = this._encrypt(data);
|
|
63
|
+
const keys = Object.keys(data).filter((k) => data[k] !== undefined);
|
|
64
|
+
const columns = keys.map((k) => `"${k}"`).join(', ');
|
|
65
|
+
const placeholders = keys.map((_, i) => `$${i + 1}`).join(', ');
|
|
66
|
+
const params = keys.map((k) => data[k]);
|
|
67
|
+
const result = await this.pool.query(`INSERT INTO ${this.tableName} (${columns}) VALUES (${placeholders}) RETURNING *`, params);
|
|
68
|
+
const row = result.rows[0];
|
|
69
|
+
if (!row)
|
|
70
|
+
throw new Error('Insert returned no rows');
|
|
71
|
+
return this._decrypt(this.mapRow(row));
|
|
72
|
+
}
|
|
73
|
+
async update(id, data) {
|
|
74
|
+
this._assertSafeTableName();
|
|
75
|
+
if (Object.keys(data).length === 0)
|
|
76
|
+
return this.findById(id);
|
|
77
|
+
data = this._encrypt(data);
|
|
78
|
+
const entries = Object.entries(data).filter(([, v]) => v !== undefined);
|
|
79
|
+
const setClauses = entries.map(([k], i) => `"${k}" = $${i + 1}`).join(', ');
|
|
80
|
+
const params = entries.map(([, v]) => v);
|
|
81
|
+
params.push(id); // last parameter for WHERE id = $N
|
|
82
|
+
const result = await this.pool.query(`UPDATE ${this.tableName} SET ${setClauses} WHERE id = $${params.length} RETURNING *`, params);
|
|
83
|
+
if (result.rows.length === 0)
|
|
84
|
+
return null;
|
|
85
|
+
return this._decrypt(this.mapRow(result.rows[0]));
|
|
86
|
+
}
|
|
87
|
+
async delete(id) {
|
|
88
|
+
this._assertSafeTableName();
|
|
89
|
+
const result = await this.pool.query(`DELETE FROM ${this.tableName} WHERE id = $1`, [id]);
|
|
90
|
+
return result.command.startsWith('DELETE') && result.rowCount > 0;
|
|
91
|
+
}
|
|
92
|
+
/** Execute raw SQL within a transaction */
|
|
93
|
+
async withTransaction(fn) {
|
|
94
|
+
return this.pool.transaction(fn);
|
|
95
|
+
}
|
|
96
|
+
/** Stream rows with backpressure.
|
|
97
|
+
* Finding 6 fix: accepts parameterized queries only — raw SQL without
|
|
98
|
+
* params is still possible but callers should always use $1..$N placeholders.
|
|
99
|
+
* The method signature now accepts params to discourage raw interpolation.
|
|
100
|
+
*/
|
|
101
|
+
streamAll(sql, params) {
|
|
102
|
+
if (params && params.length > 0) {
|
|
103
|
+
// Note: parameterized streaming requires wire-protocol changes planned for v2.x.
|
|
104
|
+
// Until then, use non-parameterized SQL for streaming or pool.query() for parameterized queries.
|
|
105
|
+
throw new Error('streamAll does not yet support parameterized queries — use pool.query() instead');
|
|
106
|
+
}
|
|
107
|
+
return this.pool.stream(sql);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
// ─── ACID ledger service ────────────────────────────────────────────────────────
|
|
111
|
+
export class LedgerTransactionService {
|
|
112
|
+
pool;
|
|
113
|
+
constructor(pool) {
|
|
114
|
+
this.pool = pool;
|
|
115
|
+
}
|
|
116
|
+
async execute(operations, onSuccess) {
|
|
117
|
+
return this.pool.transaction(async (conn) => {
|
|
118
|
+
for (const op of operations) {
|
|
119
|
+
await op(conn);
|
|
120
|
+
}
|
|
121
|
+
if (onSuccess)
|
|
122
|
+
return onSuccess();
|
|
123
|
+
return undefined;
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=repository.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"repository.js","sourceRoot":"","sources":["../src/repository.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@streetjs/repository",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "StreetJS generic PostgreSQL repository: a typed CRUD base class with safe identifier handling, pagination, streaming, transactions, and optional transparent field-level encryption. Built on @streetjs/pool.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./dist/index.js",
|
|
11
|
+
"types": "./dist/index.d.ts"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist/**/*.js",
|
|
16
|
+
"dist/**/*.js.map",
|
|
17
|
+
"dist/**/*.d.ts",
|
|
18
|
+
"dist/**/*.d.ts.map",
|
|
19
|
+
"!dist/**/*.test.js",
|
|
20
|
+
"!dist/**/*.test.js.map",
|
|
21
|
+
"!dist/**/*.test.d.ts",
|
|
22
|
+
"!dist/**/*.test.d.ts.map",
|
|
23
|
+
"!dist/tests/**",
|
|
24
|
+
"!dist/examples/**",
|
|
25
|
+
"README.md",
|
|
26
|
+
"ARCHITECTURE.md",
|
|
27
|
+
"CHANGELOG.md",
|
|
28
|
+
"LICENSE"
|
|
29
|
+
],
|
|
30
|
+
"c8": {
|
|
31
|
+
"include": ["dist/**/*.js"],
|
|
32
|
+
"exclude": ["dist/tests/**", "dist/examples/**"],
|
|
33
|
+
"reporter": ["text", "lcov"],
|
|
34
|
+
"all": true,
|
|
35
|
+
"check-coverage": true,
|
|
36
|
+
"lines": 90,
|
|
37
|
+
"functions": 90,
|
|
38
|
+
"branches": 90,
|
|
39
|
+
"statements": 90
|
|
40
|
+
},
|
|
41
|
+
"scripts": {
|
|
42
|
+
"build": "tsc",
|
|
43
|
+
"test": "node --test dist/tests/*.test.js",
|
|
44
|
+
"coverage": "c8 node --test dist/tests/*.test.js",
|
|
45
|
+
"example": "node dist/examples/integration.js",
|
|
46
|
+
"lint": "tsc --noEmit",
|
|
47
|
+
"clean": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\""
|
|
48
|
+
},
|
|
49
|
+
"keywords": [
|
|
50
|
+
"streetjs",
|
|
51
|
+
"street-framework",
|
|
52
|
+
"repository",
|
|
53
|
+
"crud",
|
|
54
|
+
"postgresql",
|
|
55
|
+
"database",
|
|
56
|
+
"data-access",
|
|
57
|
+
"field-encryption",
|
|
58
|
+
"typescript",
|
|
59
|
+
"esm"
|
|
60
|
+
],
|
|
61
|
+
"author": "street contributors",
|
|
62
|
+
"license": "MIT",
|
|
63
|
+
"homepage": "https://hassanmubiru.github.io/StreetJS/",
|
|
64
|
+
"repository": {
|
|
65
|
+
"type": "git",
|
|
66
|
+
"url": "git+https://github.com/hassanmubiru/StreetJS.git"
|
|
67
|
+
},
|
|
68
|
+
"bugs": {
|
|
69
|
+
"url": "https://github.com/hassanmubiru/StreetJS/issues"
|
|
70
|
+
},
|
|
71
|
+
"dependencies": {
|
|
72
|
+
"@streetjs/pool": "^1.0.0",
|
|
73
|
+
"@streetjs/postgres": "^1.0.0"
|
|
74
|
+
},
|
|
75
|
+
"devDependencies": {
|
|
76
|
+
"@types/node": "^26.1.0",
|
|
77
|
+
"c8": "^11.0.0",
|
|
78
|
+
"typescript": "^6.0.3"
|
|
79
|
+
},
|
|
80
|
+
"peerDependencies": {
|
|
81
|
+
"typescript": ">=5.0.0"
|
|
82
|
+
},
|
|
83
|
+
"engines": {
|
|
84
|
+
"node": ">=22.0.0"
|
|
85
|
+
},
|
|
86
|
+
"publishConfig": {
|
|
87
|
+
"access": "public"
|
|
88
|
+
}
|
|
89
|
+
}
|