@goodie-ts/kysely 0.4.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/README.md +87 -0
- package/dist/abstract-migration.d.ts +29 -0
- package/dist/abstract-migration.d.ts.map +1 -0
- package/dist/abstract-migration.js +26 -0
- package/dist/abstract-migration.js.map +1 -0
- package/dist/crud-repository.d.ts +40 -0
- package/dist/crud-repository.d.ts.map +1 -0
- package/dist/crud-repository.js +64 -0
- package/dist/crud-repository.js.map +1 -0
- package/dist/decorators/migration.d.ts +18 -0
- package/dist/decorators/migration.d.ts.map +1 -0
- package/dist/decorators/migration.js +24 -0
- package/dist/decorators/migration.js.map +1 -0
- package/dist/decorators/transactional.d.ts +16 -0
- package/dist/decorators/transactional.d.ts.map +1 -0
- package/dist/decorators/transactional.js +14 -0
- package/dist/decorators/transactional.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/kysely-transformer-plugin.d.ts +32 -0
- package/dist/kysely-transformer-plugin.d.ts.map +1 -0
- package/dist/kysely-transformer-plugin.js +315 -0
- package/dist/kysely-transformer-plugin.js.map +1 -0
- package/dist/migration-runner.d.ts +16 -0
- package/dist/migration-runner.d.ts.map +1 -0
- package/dist/migration-runner.js +45 -0
- package/dist/migration-runner.js.map +1 -0
- package/dist/transaction-manager.d.ts +59 -0
- package/dist/transaction-manager.d.ts.map +1 -0
- package/dist/transaction-manager.js +132 -0
- package/dist/transaction-manager.js.map +1 -0
- package/dist/transactional-interceptor.d.ts +14 -0
- package/dist/transactional-interceptor.d.ts.map +1 -0
- package/dist/transactional-interceptor.js +24 -0
- package/dist/transactional-interceptor.js.map +1 -0
- package/package.json +51 -0
package/README.md
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# @goodie-ts/kysely
|
|
2
|
+
|
|
3
|
+
[Kysely](https://kysely.dev/) integration for [goodie-ts](https://github.com/GOOD-Code-ApS/goodie) — declarative transactions, auto-wired migrations, and a CRUD repository base class.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm add @goodie-ts/kysely kysely
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Overview
|
|
12
|
+
|
|
13
|
+
Provides `@Transactional` for declarative transaction management, `@Migration` for auto-discovered database migrations, and `CrudRepository<T>` for common CRUD operations. All backed by `TransactionManager` which uses `AsyncLocalStorage` for transaction propagation.
|
|
14
|
+
|
|
15
|
+
## Decorators
|
|
16
|
+
|
|
17
|
+
| Decorator | Target | Description |
|
|
18
|
+
|-----------|--------|-------------|
|
|
19
|
+
| `@Transactional({ propagation? })` | method | Wraps method in a database transaction |
|
|
20
|
+
| `@Migration('name')` | class | Marks a class as a migration (sorted by name) |
|
|
21
|
+
|
|
22
|
+
## Usage
|
|
23
|
+
|
|
24
|
+
```typescript
|
|
25
|
+
import { Singleton } from '@goodie-ts/decorators';
|
|
26
|
+
import { Transactional, CrudRepository, TransactionManager } from '@goodie-ts/kysely';
|
|
27
|
+
|
|
28
|
+
@Singleton()
|
|
29
|
+
class TodoRepository extends CrudRepository<Todo> {
|
|
30
|
+
constructor(transactionManager: TransactionManager) {
|
|
31
|
+
super('todos', transactionManager);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
@Singleton()
|
|
36
|
+
class TodoService {
|
|
37
|
+
constructor(private repo: TodoRepository) {}
|
|
38
|
+
|
|
39
|
+
@Transactional()
|
|
40
|
+
async createMany(titles: string[]) {
|
|
41
|
+
for (const title of titles) {
|
|
42
|
+
await this.repo.save({ title, completed: false });
|
|
43
|
+
}
|
|
44
|
+
// All-or-nothing: rolls back on error
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Migrations
|
|
50
|
+
|
|
51
|
+
```typescript
|
|
52
|
+
import { Migration, AbstractMigration } from '@goodie-ts/kysely';
|
|
53
|
+
import type { Kysely } from 'kysely';
|
|
54
|
+
|
|
55
|
+
@Migration('001_create_todos')
|
|
56
|
+
class CreateTodosTable extends AbstractMigration {
|
|
57
|
+
async up(db: Kysely<any>) {
|
|
58
|
+
await db.schema.createTable('todos')
|
|
59
|
+
.addColumn('id', 'uuid', c => c.primaryKey().defaultTo(sql`gen_random_uuid()`))
|
|
60
|
+
.addColumn('title', 'text', c => c.notNull())
|
|
61
|
+
.execute();
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Migrations run automatically at startup via `MigrationRunner` (`@PostConstruct`), sorted by name.
|
|
67
|
+
|
|
68
|
+
## Vite Plugin Setup
|
|
69
|
+
|
|
70
|
+
```typescript
|
|
71
|
+
import { diPlugin } from '@goodie-ts/vite-plugin';
|
|
72
|
+
import { createKyselyPlugin } from '@goodie-ts/kysely';
|
|
73
|
+
|
|
74
|
+
export default defineConfig({
|
|
75
|
+
plugins: [
|
|
76
|
+
diPlugin({
|
|
77
|
+
plugins: [createKyselyPlugin({ database: 'Database' })],
|
|
78
|
+
}),
|
|
79
|
+
],
|
|
80
|
+
});
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
The `database` option specifies the class name of your Kysely wrapper (a `@Singleton` with a `.kysely` property).
|
|
84
|
+
|
|
85
|
+
## License
|
|
86
|
+
|
|
87
|
+
[MIT](https://github.com/GOOD-Code-ApS/goodie/blob/main/LICENSE)
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { Kysely } from 'kysely';
|
|
2
|
+
/**
|
|
3
|
+
* Abstract base class for Kysely migrations.
|
|
4
|
+
*
|
|
5
|
+
* Extend this class and decorate with `@Migration('name')` to define a
|
|
6
|
+
* migration that is auto-discovered and executed by the MigrationRunner
|
|
7
|
+
* at application startup.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* @Migration('001_create_users')
|
|
12
|
+
* export class CreateUsersTable extends AbstractMigration {
|
|
13
|
+
* async up(db: Kysely<any>) {
|
|
14
|
+
* await db.schema.createTable('users')
|
|
15
|
+
* .addColumn('id', 'uuid', c => c.primaryKey())
|
|
16
|
+
* .execute();
|
|
17
|
+
* }
|
|
18
|
+
*
|
|
19
|
+
* async down(db: Kysely<any>) {
|
|
20
|
+
* await db.schema.dropTable('users').execute();
|
|
21
|
+
* }
|
|
22
|
+
* }
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
export declare abstract class AbstractMigration {
|
|
26
|
+
abstract up(db: Kysely<any>): Promise<void>;
|
|
27
|
+
down?(db: Kysely<any>): Promise<void>;
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=abstract-migration.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"abstract-migration.d.ts","sourceRoot":"","sources":["../src/abstract-migration.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAErC;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,8BAAsB,iBAAiB;IACrC,QAAQ,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAC3C,IAAI,CAAC,CAAC,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;CACtC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Abstract base class for Kysely migrations.
|
|
3
|
+
*
|
|
4
|
+
* Extend this class and decorate with `@Migration('name')` to define a
|
|
5
|
+
* migration that is auto-discovered and executed by the MigrationRunner
|
|
6
|
+
* at application startup.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```typescript
|
|
10
|
+
* @Migration('001_create_users')
|
|
11
|
+
* export class CreateUsersTable extends AbstractMigration {
|
|
12
|
+
* async up(db: Kysely<any>) {
|
|
13
|
+
* await db.schema.createTable('users')
|
|
14
|
+
* .addColumn('id', 'uuid', c => c.primaryKey())
|
|
15
|
+
* .execute();
|
|
16
|
+
* }
|
|
17
|
+
*
|
|
18
|
+
* async down(db: Kysely<any>) {
|
|
19
|
+
* await db.schema.dropTable('users').execute();
|
|
20
|
+
* }
|
|
21
|
+
* }
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
export class AbstractMigration {
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=abstract-migration.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"abstract-migration.js","sourceRoot":"","sources":["../src/abstract-migration.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,OAAgB,iBAAiB;CAGtC"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { Kysely } from 'kysely';
|
|
2
|
+
import type { TransactionManager } from './transaction-manager.js';
|
|
3
|
+
/**
|
|
4
|
+
* Abstract base class for CRUD repositories backed by Kysely.
|
|
5
|
+
*
|
|
6
|
+
* **PostgreSQL-only:** Uses `RETURNING` clauses in `save()` and `deleteById()`.
|
|
7
|
+
* MySQL and SQLite do not support `RETURNING`. For other dialects, override
|
|
8
|
+
* these methods in your subclass.
|
|
9
|
+
*
|
|
10
|
+
* Provides standard `findAll`, `findById`, `save`, and `deleteById` operations.
|
|
11
|
+
* All queries use `TransactionManager.getConnection()` to be transaction-aware.
|
|
12
|
+
*
|
|
13
|
+
* The `db` getter returns `Kysely<any>` — typed table access is intentionally
|
|
14
|
+
* erased because the transformer cannot generate clean tokens for `Kysely<DB>`
|
|
15
|
+
* generic types. Subclasses can cast as needed for custom queries.
|
|
16
|
+
*
|
|
17
|
+
* @typeParam T - The entity/row type returned by queries.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```typescript
|
|
21
|
+
* class TodoRepository extends CrudRepository<Todo> {
|
|
22
|
+
* constructor(transactionManager: TransactionManager) {
|
|
23
|
+
* super('todos', transactionManager);
|
|
24
|
+
* }
|
|
25
|
+
* }
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export declare abstract class CrudRepository<T extends Record<string, unknown>> {
|
|
29
|
+
protected readonly tableName: string;
|
|
30
|
+
protected readonly transactionManager: TransactionManager;
|
|
31
|
+
protected readonly idColumn: string;
|
|
32
|
+
constructor(tableName: string, transactionManager: TransactionManager, idColumn?: string);
|
|
33
|
+
/** Get the current transaction-aware Kysely connection. */
|
|
34
|
+
protected get db(): Kysely<any>;
|
|
35
|
+
findAll(): Promise<T[]>;
|
|
36
|
+
findById(id: unknown): Promise<T | undefined>;
|
|
37
|
+
save(entity: Record<string, unknown>): Promise<T>;
|
|
38
|
+
deleteById(id: unknown): Promise<T | undefined>;
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=crud-repository.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"crud-repository.d.ts","sourceRoot":"","sources":["../src/crud-repository.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACrC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAEnE;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,8BAAsB,cAAc,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAElE,SAAS,CAAC,QAAQ,CAAC,SAAS,EAAE,MAAM;IACpC,SAAS,CAAC,QAAQ,CAAC,kBAAkB,EAAE,kBAAkB;IACzD,SAAS,CAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM;gBAFhB,SAAS,EAAE,MAAM,EACjB,kBAAkB,EAAE,kBAAkB,EACtC,QAAQ,GAAE,MAAa;IAG5C,2DAA2D;IAC3D,SAAS,KAAK,EAAE,IAAI,MAAM,CAAC,GAAG,CAAC,CAE9B;IAEK,OAAO,IAAI,OAAO,CAAC,CAAC,EAAE,CAAC;IAMvB,QAAQ,CAAC,EAAE,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;IAQ7C,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAQjD,UAAU,CAAC,EAAE,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;CAOtD"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Abstract base class for CRUD repositories backed by Kysely.
|
|
3
|
+
*
|
|
4
|
+
* **PostgreSQL-only:** Uses `RETURNING` clauses in `save()` and `deleteById()`.
|
|
5
|
+
* MySQL and SQLite do not support `RETURNING`. For other dialects, override
|
|
6
|
+
* these methods in your subclass.
|
|
7
|
+
*
|
|
8
|
+
* Provides standard `findAll`, `findById`, `save`, and `deleteById` operations.
|
|
9
|
+
* All queries use `TransactionManager.getConnection()` to be transaction-aware.
|
|
10
|
+
*
|
|
11
|
+
* The `db` getter returns `Kysely<any>` — typed table access is intentionally
|
|
12
|
+
* erased because the transformer cannot generate clean tokens for `Kysely<DB>`
|
|
13
|
+
* generic types. Subclasses can cast as needed for custom queries.
|
|
14
|
+
*
|
|
15
|
+
* @typeParam T - The entity/row type returned by queries.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```typescript
|
|
19
|
+
* class TodoRepository extends CrudRepository<Todo> {
|
|
20
|
+
* constructor(transactionManager: TransactionManager) {
|
|
21
|
+
* super('todos', transactionManager);
|
|
22
|
+
* }
|
|
23
|
+
* }
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
export class CrudRepository {
|
|
27
|
+
tableName;
|
|
28
|
+
transactionManager;
|
|
29
|
+
idColumn;
|
|
30
|
+
constructor(tableName, transactionManager, idColumn = 'id') {
|
|
31
|
+
this.tableName = tableName;
|
|
32
|
+
this.transactionManager = transactionManager;
|
|
33
|
+
this.idColumn = idColumn;
|
|
34
|
+
}
|
|
35
|
+
/** Get the current transaction-aware Kysely connection. */
|
|
36
|
+
get db() {
|
|
37
|
+
return this.transactionManager.getConnection();
|
|
38
|
+
}
|
|
39
|
+
async findAll() {
|
|
40
|
+
return this.db.selectFrom(this.tableName).selectAll().execute();
|
|
41
|
+
}
|
|
42
|
+
async findById(id) {
|
|
43
|
+
return this.db
|
|
44
|
+
.selectFrom(this.tableName)
|
|
45
|
+
.selectAll()
|
|
46
|
+
.where(this.idColumn, '=', id)
|
|
47
|
+
.executeTakeFirst();
|
|
48
|
+
}
|
|
49
|
+
async save(entity) {
|
|
50
|
+
return this.db
|
|
51
|
+
.insertInto(this.tableName)
|
|
52
|
+
.values(entity)
|
|
53
|
+
.returningAll()
|
|
54
|
+
.executeTakeFirstOrThrow();
|
|
55
|
+
}
|
|
56
|
+
async deleteById(id) {
|
|
57
|
+
return this.db
|
|
58
|
+
.deleteFrom(this.tableName)
|
|
59
|
+
.where(this.idColumn, '=', id)
|
|
60
|
+
.returningAll()
|
|
61
|
+
.executeTakeFirst();
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=crud-repository.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"crud-repository.js","sourceRoot":"","sources":["../src/crud-repository.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,OAAgB,cAAc;IAEb;IACA;IACA;IAHrB,YACqB,SAAiB,EACjB,kBAAsC,EACtC,WAAmB,IAAI;QAFvB,cAAS,GAAT,SAAS,CAAQ;QACjB,uBAAkB,GAAlB,kBAAkB,CAAoB;QACtC,aAAQ,GAAR,QAAQ,CAAe;IACzC,CAAC;IAEJ,2DAA2D;IAC3D,IAAc,EAAE;QACd,OAAO,IAAI,CAAC,kBAAkB,CAAC,aAAa,EAAiB,CAAC;IAChE,CAAC;IAED,KAAK,CAAC,OAAO;QACX,OAAO,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,SAAS,EAAE,CAAC,OAAO,EAE5D,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,EAAW;QACxB,OAAO,IAAI,CAAC,EAAE;aACX,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC;aAC1B,SAAS,EAAE;aACX,KAAK,CAAC,IAAI,CAAC,QAAQ,EAAE,GAAG,EAAE,EAAE,CAAC;aAC7B,gBAAgB,EAA4B,CAAC;IAClD,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,MAA+B;QACxC,OAAO,IAAI,CAAC,EAAE;aACX,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC;aAC1B,MAAM,CAAC,MAAM,CAAC;aACd,YAAY,EAAE;aACd,uBAAuB,EAAgB,CAAC;IAC7C,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,EAAW;QAC1B,OAAO,IAAI,CAAC,EAAE;aACX,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC;aAC1B,KAAK,CAAC,IAAI,CAAC,QAAQ,EAAE,GAAG,EAAE,EAAE,CAAC;aAC7B,YAAY,EAAE;aACd,gBAAgB,EAA4B,CAAC;IAClD,CAAC;CACF"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
type ClassDecorator_Stage3 = (target: new (...args: never) => unknown, context: ClassDecoratorContext) => void;
|
|
2
|
+
/**
|
|
3
|
+
* Mark a class as a Kysely migration with a unique name.
|
|
4
|
+
*
|
|
5
|
+
* At compile time, the Kysely transformer plugin discovers @Migration classes
|
|
6
|
+
* and wires them into an auto-managed MigrationRunner.
|
|
7
|
+
*
|
|
8
|
+
* Classes should extend {@link AbstractMigration} which enforces the required
|
|
9
|
+
* `up()` method and optional `down()` method at compile time.
|
|
10
|
+
*
|
|
11
|
+
* @param name - Unique migration key, e.g. '001_create_todos'. Migrations
|
|
12
|
+
* are executed in lexicographic order by name.
|
|
13
|
+
*/
|
|
14
|
+
export declare function Migration(name: string): ClassDecorator_Stage3;
|
|
15
|
+
/** Read the migration name from a migration instance's Symbol.metadata. */
|
|
16
|
+
export declare function getMigrationName(instance: object): string | undefined;
|
|
17
|
+
export {};
|
|
18
|
+
//# sourceMappingURL=migration.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migration.d.ts","sourceRoot":"","sources":["../../src/decorators/migration.ts"],"names":[],"mappings":"AAEA,KAAK,qBAAqB,GAAG,CAC3B,MAAM,EAAE,KAAK,GAAG,IAAI,EAAE,KAAK,KAAK,OAAO,EACvC,OAAO,EAAE,qBAAqB,KAC3B,IAAI,CAAC;AAEV;;;;;;;;;;;GAWG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,qBAAqB,CAI7D;AAED,2EAA2E;AAC3E,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAGrE"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
const MIGRATION_NAME = Symbol('goodie:migration-name');
|
|
2
|
+
/**
|
|
3
|
+
* Mark a class as a Kysely migration with a unique name.
|
|
4
|
+
*
|
|
5
|
+
* At compile time, the Kysely transformer plugin discovers @Migration classes
|
|
6
|
+
* and wires them into an auto-managed MigrationRunner.
|
|
7
|
+
*
|
|
8
|
+
* Classes should extend {@link AbstractMigration} which enforces the required
|
|
9
|
+
* `up()` method and optional `down()` method at compile time.
|
|
10
|
+
*
|
|
11
|
+
* @param name - Unique migration key, e.g. '001_create_todos'. Migrations
|
|
12
|
+
* are executed in lexicographic order by name.
|
|
13
|
+
*/
|
|
14
|
+
export function Migration(name) {
|
|
15
|
+
return (_target, context) => {
|
|
16
|
+
context.metadata[MIGRATION_NAME] = name;
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
/** Read the migration name from a migration instance's Symbol.metadata. */
|
|
20
|
+
export function getMigrationName(instance) {
|
|
21
|
+
const meta = instance.constructor[Symbol.metadata];
|
|
22
|
+
return meta?.[MIGRATION_NAME];
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=migration.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migration.js","sourceRoot":"","sources":["../../src/decorators/migration.ts"],"names":[],"mappings":"AAAA,MAAM,cAAc,GAAG,MAAM,CAAC,uBAAuB,CAAC,CAAC;AAOvD;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,SAAS,CAAC,IAAY;IACpC,OAAO,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE;QAC1B,OAAO,CAAC,QAAS,CAAC,cAAc,CAAC,GAAG,IAAI,CAAC;IAC3C,CAAC,CAAC;AACJ,CAAC;AAED,2EAA2E;AAC3E,MAAM,UAAU,gBAAgB,CAAC,QAAgB;IAC/C,MAAM,IAAI,GAAI,QAAQ,CAAC,WAAmB,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAC5D,OAAO,IAAI,EAAE,CAAC,cAAc,CAAC,CAAC;AAChC,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export interface TransactionalOptions {
|
|
2
|
+
/** Transaction propagation strategy (default: 'REQUIRED'). */
|
|
3
|
+
propagation?: 'REQUIRED' | 'REQUIRES_NEW';
|
|
4
|
+
}
|
|
5
|
+
type MethodDecorator_Stage3 = (target: (...args: never) => unknown, context: ClassMethodDecoratorContext) => void;
|
|
6
|
+
/**
|
|
7
|
+
* Mark a method to run inside a database transaction.
|
|
8
|
+
*
|
|
9
|
+
* At compile time, the kysely transformer plugin reads this decorator
|
|
10
|
+
* and wires the `TransactionalInterceptor` via AOP.
|
|
11
|
+
*
|
|
12
|
+
* @param opts - Optional configuration (e.g. propagation strategy).
|
|
13
|
+
*/
|
|
14
|
+
export declare function Transactional(_opts?: TransactionalOptions): MethodDecorator_Stage3;
|
|
15
|
+
export {};
|
|
16
|
+
//# sourceMappingURL=transactional.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transactional.d.ts","sourceRoot":"","sources":["../../src/decorators/transactional.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,oBAAoB;IACnC,8DAA8D;IAC9D,WAAW,CAAC,EAAE,UAAU,GAAG,cAAc,CAAC;CAC3C;AAED,KAAK,sBAAsB,GAAG,CAC5B,MAAM,EAAE,CAAC,GAAG,IAAI,EAAE,KAAK,KAAK,OAAO,EACnC,OAAO,EAAE,2BAA2B,KACjC,IAAI,CAAC;AAEV;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAC3B,KAAK,CAAC,EAAE,oBAAoB,GAC3B,sBAAsB,CAIxB"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mark a method to run inside a database transaction.
|
|
3
|
+
*
|
|
4
|
+
* At compile time, the kysely transformer plugin reads this decorator
|
|
5
|
+
* and wires the `TransactionalInterceptor` via AOP.
|
|
6
|
+
*
|
|
7
|
+
* @param opts - Optional configuration (e.g. propagation strategy).
|
|
8
|
+
*/
|
|
9
|
+
export function Transactional(_opts) {
|
|
10
|
+
return (_target, _context) => {
|
|
11
|
+
// No-op: read at compile time by the kysely transformer plugin
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=transactional.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transactional.js","sourceRoot":"","sources":["../../src/decorators/transactional.ts"],"names":[],"mappings":"AAUA;;;;;;;GAOG;AACH,MAAM,UAAU,aAAa,CAC3B,KAA4B;IAE5B,OAAO,CAAC,OAAO,EAAE,QAAQ,EAAE,EAAE;QAC3B,+DAA+D;IACjE,CAAC,CAAC;AACJ,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export { AbstractMigration } from './abstract-migration.js';
|
|
2
|
+
export { CrudRepository } from './crud-repository.js';
|
|
3
|
+
export { getMigrationName, Migration } from './decorators/migration.js';
|
|
4
|
+
export { Transactional } from './decorators/transactional.js';
|
|
5
|
+
export type { KyselyPluginOptions } from './kysely-transformer-plugin.js';
|
|
6
|
+
export { createKyselyPlugin } from './kysely-transformer-plugin.js';
|
|
7
|
+
export { MigrationRunner } from './migration-runner.js';
|
|
8
|
+
export type { KyselyProvider } from './transaction-manager.js';
|
|
9
|
+
export { TransactionManager } from './transaction-manager.js';
|
|
10
|
+
export { TransactionalInterceptor } from './transactional-interceptor.js';
|
|
11
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AACxE,OAAO,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAC9D,YAAY,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AAC1E,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AACpE,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,YAAY,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC/D,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAC9D,OAAO,EAAE,wBAAwB,EAAE,MAAM,gCAAgC,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { AbstractMigration } from './abstract-migration.js';
|
|
2
|
+
export { CrudRepository } from './crud-repository.js';
|
|
3
|
+
export { getMigrationName, Migration } from './decorators/migration.js';
|
|
4
|
+
export { Transactional } from './decorators/transactional.js';
|
|
5
|
+
export { createKyselyPlugin } from './kysely-transformer-plugin.js';
|
|
6
|
+
export { MigrationRunner } from './migration-runner.js';
|
|
7
|
+
export { TransactionManager } from './transaction-manager.js';
|
|
8
|
+
export { TransactionalInterceptor } from './transactional-interceptor.js';
|
|
9
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AACxE,OAAO,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAE9D,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AACpE,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAExD,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAC9D,OAAO,EAAE,wBAAwB,EAAE,MAAM,gCAAgC,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { TransformerPlugin } from '@goodie-ts/transformer';
|
|
2
|
+
/** Options for the Kysely transformer plugin. */
|
|
3
|
+
export interface KyselyPluginOptions {
|
|
4
|
+
/**
|
|
5
|
+
* Explicit class name of the bean that provides a `Kysely<...>` property.
|
|
6
|
+
* Overrides auto-detection. Use this to disambiguate when multiple classes
|
|
7
|
+
* expose a `Kysely` property.
|
|
8
|
+
*/
|
|
9
|
+
database?: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Create the Kysely transformer plugin.
|
|
13
|
+
*
|
|
14
|
+
* Scans @Transactional decorators on methods and wires
|
|
15
|
+
* TransactionalInterceptor as an AOP interceptor dependency at compile time.
|
|
16
|
+
*
|
|
17
|
+
* Scans @Migration decorated classes and wires a MigrationRunner that
|
|
18
|
+
* executes all migrations at startup via @PostConstruct.
|
|
19
|
+
*
|
|
20
|
+
* **Auto-detection:** The plugin scans decorated classes for properties typed
|
|
21
|
+
* as `Kysely<...>` and auto-wires the owning class as a TransactionManager
|
|
22
|
+
* and/or MigrationRunner constructor dependency. Use `options.database` to
|
|
23
|
+
* override when multiple classes expose a `Kysely` property.
|
|
24
|
+
*
|
|
25
|
+
* **Limitation:** Propagation is detected via AST text matching
|
|
26
|
+
* (`text.includes('REQUIRES_NEW')`). Only string literal values in the
|
|
27
|
+
* decorator argument are supported — const references or computed values
|
|
28
|
+
* will fall back to `'REQUIRED'` silently.
|
|
29
|
+
*/
|
|
30
|
+
export declare function createKyselyPlugin(options?: KyselyPluginOptions): TransformerPlugin;
|
|
31
|
+
export default createKyselyPlugin;
|
|
32
|
+
//# sourceMappingURL=kysely-transformer-plugin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kysely-transformer-plugin.d.ts","sourceRoot":"","sources":["../src/kysely-transformer-plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAKV,iBAAiB,EAClB,MAAM,wBAAwB,CAAC;AAEhC,iDAAiD;AACjD,MAAM,WAAW,mBAAmB;IAClC;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAeD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,CAAC,EAAE,mBAAmB,GAC5B,iBAAiB,CAqSnB;AAmED,eAAe,kBAAkB,CAAC"}
|
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Create the Kysely transformer plugin.
|
|
3
|
+
*
|
|
4
|
+
* Scans @Transactional decorators on methods and wires
|
|
5
|
+
* TransactionalInterceptor as an AOP interceptor dependency at compile time.
|
|
6
|
+
*
|
|
7
|
+
* Scans @Migration decorated classes and wires a MigrationRunner that
|
|
8
|
+
* executes all migrations at startup via @PostConstruct.
|
|
9
|
+
*
|
|
10
|
+
* **Auto-detection:** The plugin scans decorated classes for properties typed
|
|
11
|
+
* as `Kysely<...>` and auto-wires the owning class as a TransactionManager
|
|
12
|
+
* and/or MigrationRunner constructor dependency. Use `options.database` to
|
|
13
|
+
* override when multiple classes expose a `Kysely` property.
|
|
14
|
+
*
|
|
15
|
+
* **Limitation:** Propagation is detected via AST text matching
|
|
16
|
+
* (`text.includes('REQUIRES_NEW')`). Only string literal values in the
|
|
17
|
+
* decorator argument are supported — const references or computed values
|
|
18
|
+
* will fall back to `'REQUIRED'` silently.
|
|
19
|
+
*/
|
|
20
|
+
export function createKyselyPlugin(options) {
|
|
21
|
+
const classTransactionalInfo = new Map();
|
|
22
|
+
/** Classes discovered with a `Kysely<...>` property. Keyed by filePath:className. */
|
|
23
|
+
const kyselyProviders = [];
|
|
24
|
+
/** @Migration decorated classes discovered during visitClass. */
|
|
25
|
+
const migrationClasses = [];
|
|
26
|
+
return {
|
|
27
|
+
name: 'kysely',
|
|
28
|
+
visitClass(ctx) {
|
|
29
|
+
// Detect Kysely<...> properties for auto-wiring
|
|
30
|
+
for (const prop of ctx.classDeclaration.getProperties()) {
|
|
31
|
+
// Check the type annotation text (AST-level), not the resolved type.
|
|
32
|
+
// This avoids requiring the 'kysely' package to be resolvable at transform time.
|
|
33
|
+
const typeAnnotation = prop.getTypeNode()?.getText();
|
|
34
|
+
if (typeAnnotation?.startsWith('Kysely<')) {
|
|
35
|
+
kyselyProviders.push({
|
|
36
|
+
className: ctx.className,
|
|
37
|
+
filePath: ctx.filePath,
|
|
38
|
+
});
|
|
39
|
+
break; // one match per class is enough
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
// Detect @Migration('name') class decorator
|
|
43
|
+
for (const decorator of ctx.classDeclaration.getDecorators()) {
|
|
44
|
+
if (decorator.getName() !== 'Migration')
|
|
45
|
+
continue;
|
|
46
|
+
const args = decorator.getArguments();
|
|
47
|
+
const name = args[0]?.getText().replace(/['"]/g, '') ?? ctx.className;
|
|
48
|
+
migrationClasses.push({
|
|
49
|
+
className: ctx.className,
|
|
50
|
+
filePath: ctx.filePath,
|
|
51
|
+
migrationName: name,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
visitMethod(ctx) {
|
|
56
|
+
const decorators = ctx.methodDeclaration.getDecorators();
|
|
57
|
+
for (const decorator of decorators) {
|
|
58
|
+
if (decorator.getName() !== 'Transactional')
|
|
59
|
+
continue;
|
|
60
|
+
// Parse propagation from decorator arguments
|
|
61
|
+
let propagation = 'REQUIRED';
|
|
62
|
+
const args = decorator.getArguments();
|
|
63
|
+
if (args.length > 0) {
|
|
64
|
+
const text = args[0].getText();
|
|
65
|
+
if (text.includes('REQUIRES_NEW')) {
|
|
66
|
+
propagation = 'REQUIRES_NEW';
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
const key = `${ctx.filePath}:${ctx.className}`;
|
|
70
|
+
const existing = classTransactionalInfo.get(key) ?? [];
|
|
71
|
+
existing.push({ methodName: ctx.methodName, propagation });
|
|
72
|
+
classTransactionalInfo.set(key, existing);
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
afterResolve(beans) {
|
|
76
|
+
const syntheticBeans = [];
|
|
77
|
+
// --- @Transactional interception ---
|
|
78
|
+
let needsInterceptor = false;
|
|
79
|
+
for (const bean of beans) {
|
|
80
|
+
const className = bean.tokenRef.kind === 'class' ? bean.tokenRef.className : undefined;
|
|
81
|
+
if (!className)
|
|
82
|
+
continue;
|
|
83
|
+
const key = `${bean.tokenRef.importPath}:${className}`;
|
|
84
|
+
const infos = classTransactionalInfo.get(key);
|
|
85
|
+
if (!infos || infos.length === 0)
|
|
86
|
+
continue;
|
|
87
|
+
needsInterceptor = true;
|
|
88
|
+
const existing = (bean.metadata.interceptedMethods ?? []);
|
|
89
|
+
for (const info of infos) {
|
|
90
|
+
const methodEntry = existing.find((m) => m.methodName === info.methodName);
|
|
91
|
+
const interceptorRef = {
|
|
92
|
+
className: 'TransactionalInterceptor',
|
|
93
|
+
importPath: '@goodie-ts/kysely',
|
|
94
|
+
adviceType: 'around',
|
|
95
|
+
order: -40, // Runs after logging (-100), before cache (-50), before resilience
|
|
96
|
+
metadata: { propagation: info.propagation },
|
|
97
|
+
};
|
|
98
|
+
if (methodEntry) {
|
|
99
|
+
methodEntry.interceptors.push(interceptorRef);
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
existing.push({
|
|
103
|
+
methodName: info.methodName,
|
|
104
|
+
interceptors: [interceptorRef],
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
bean.metadata.interceptedMethods = existing;
|
|
109
|
+
}
|
|
110
|
+
const hasMigrations = migrationClasses.length > 0;
|
|
111
|
+
if (!needsInterceptor && !hasMigrations)
|
|
112
|
+
return beans;
|
|
113
|
+
// Resolve the database bean for auto-wiring.
|
|
114
|
+
// Priority: explicit `database` option > auto-detected Kysely provider.
|
|
115
|
+
const kyselyProviderDep = resolveKyselyProvider(beans, options?.database, kyselyProviders);
|
|
116
|
+
// --- Transactional synthetic beans ---
|
|
117
|
+
if (needsInterceptor) {
|
|
118
|
+
syntheticBeans.push({
|
|
119
|
+
tokenRef: {
|
|
120
|
+
kind: 'class',
|
|
121
|
+
className: 'TransactionManager',
|
|
122
|
+
importPath: '@goodie-ts/kysely',
|
|
123
|
+
},
|
|
124
|
+
scope: 'singleton',
|
|
125
|
+
eager: false,
|
|
126
|
+
name: undefined,
|
|
127
|
+
constructorDeps: kyselyProviderDep,
|
|
128
|
+
fieldDeps: [],
|
|
129
|
+
factoryKind: 'constructor',
|
|
130
|
+
providesSource: undefined,
|
|
131
|
+
metadata: {},
|
|
132
|
+
sourceLocation: {
|
|
133
|
+
filePath: '@goodie-ts/kysely',
|
|
134
|
+
line: 0,
|
|
135
|
+
column: 0,
|
|
136
|
+
},
|
|
137
|
+
});
|
|
138
|
+
syntheticBeans.push({
|
|
139
|
+
tokenRef: {
|
|
140
|
+
kind: 'class',
|
|
141
|
+
className: 'TransactionalInterceptor',
|
|
142
|
+
importPath: '@goodie-ts/kysely',
|
|
143
|
+
},
|
|
144
|
+
scope: 'singleton',
|
|
145
|
+
eager: false,
|
|
146
|
+
name: undefined,
|
|
147
|
+
constructorDeps: [
|
|
148
|
+
{
|
|
149
|
+
tokenRef: {
|
|
150
|
+
kind: 'class',
|
|
151
|
+
className: 'TransactionManager',
|
|
152
|
+
importPath: '@goodie-ts/kysely',
|
|
153
|
+
},
|
|
154
|
+
optional: false,
|
|
155
|
+
collection: false,
|
|
156
|
+
sourceLocation: {
|
|
157
|
+
filePath: '@goodie-ts/kysely',
|
|
158
|
+
line: 0,
|
|
159
|
+
column: 0,
|
|
160
|
+
},
|
|
161
|
+
},
|
|
162
|
+
],
|
|
163
|
+
fieldDeps: [],
|
|
164
|
+
factoryKind: 'constructor',
|
|
165
|
+
providesSource: undefined,
|
|
166
|
+
metadata: {},
|
|
167
|
+
sourceLocation: {
|
|
168
|
+
filePath: '@goodie-ts/kysely',
|
|
169
|
+
line: 0,
|
|
170
|
+
column: 0,
|
|
171
|
+
},
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
// --- @Migration synthetic beans ---
|
|
175
|
+
if (hasMigrations) {
|
|
176
|
+
if (kyselyProviderDep.length === 0) {
|
|
177
|
+
console.warn('[@goodie-ts/kysely] @Migration classes found but no Kysely provider detected. ' +
|
|
178
|
+
'MigrationRunner will not be created.');
|
|
179
|
+
}
|
|
180
|
+
else {
|
|
181
|
+
// Sort migrations by name for deterministic execution order
|
|
182
|
+
migrationClasses.sort((a, b) => a.migrationName.localeCompare(b.migrationName));
|
|
183
|
+
// Synthetic bean per @Migration class (no baseTokenRefs — wired as individual deps)
|
|
184
|
+
const migrationDeps = [];
|
|
185
|
+
for (const m of migrationClasses) {
|
|
186
|
+
syntheticBeans.push({
|
|
187
|
+
tokenRef: {
|
|
188
|
+
kind: 'class',
|
|
189
|
+
className: m.className,
|
|
190
|
+
importPath: m.filePath,
|
|
191
|
+
},
|
|
192
|
+
scope: 'singleton',
|
|
193
|
+
eager: false,
|
|
194
|
+
name: undefined,
|
|
195
|
+
constructorDeps: [],
|
|
196
|
+
fieldDeps: [],
|
|
197
|
+
factoryKind: 'constructor',
|
|
198
|
+
providesSource: undefined,
|
|
199
|
+
metadata: {},
|
|
200
|
+
sourceLocation: {
|
|
201
|
+
filePath: m.filePath,
|
|
202
|
+
line: 0,
|
|
203
|
+
column: 0,
|
|
204
|
+
},
|
|
205
|
+
});
|
|
206
|
+
migrationDeps.push({
|
|
207
|
+
tokenRef: {
|
|
208
|
+
kind: 'class',
|
|
209
|
+
className: m.className,
|
|
210
|
+
importPath: m.filePath,
|
|
211
|
+
},
|
|
212
|
+
optional: false,
|
|
213
|
+
collection: false,
|
|
214
|
+
sourceLocation: {
|
|
215
|
+
filePath: '@goodie-ts/kysely',
|
|
216
|
+
line: 0,
|
|
217
|
+
column: 0,
|
|
218
|
+
},
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
// MigrationRunner: eager singleton, depends on KyselyProvider + individual migration deps
|
|
222
|
+
syntheticBeans.push({
|
|
223
|
+
tokenRef: {
|
|
224
|
+
kind: 'class',
|
|
225
|
+
className: 'MigrationRunner',
|
|
226
|
+
importPath: '@goodie-ts/kysely',
|
|
227
|
+
},
|
|
228
|
+
scope: 'singleton',
|
|
229
|
+
eager: true,
|
|
230
|
+
name: undefined,
|
|
231
|
+
constructorDeps: [kyselyProviderDep[0], ...migrationDeps],
|
|
232
|
+
fieldDeps: [],
|
|
233
|
+
factoryKind: 'constructor',
|
|
234
|
+
providesSource: undefined,
|
|
235
|
+
metadata: { postConstructMethods: ['migrate'] },
|
|
236
|
+
sourceLocation: {
|
|
237
|
+
filePath: '@goodie-ts/kysely',
|
|
238
|
+
line: 0,
|
|
239
|
+
column: 0,
|
|
240
|
+
},
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
return [...beans, ...syntheticBeans];
|
|
245
|
+
},
|
|
246
|
+
codegen(beans) {
|
|
247
|
+
const hasTransactional = beans.some((b) => {
|
|
248
|
+
const methods = b.metadata.interceptedMethods;
|
|
249
|
+
return methods?.some((m) => m.interceptors.some((i) => i.className === 'TransactionalInterceptor'));
|
|
250
|
+
});
|
|
251
|
+
if (!hasTransactional)
|
|
252
|
+
return {};
|
|
253
|
+
// TransactionManager and TransactionalInterceptor imports are already
|
|
254
|
+
// generated by collectClassImports (they are synthetic bean tokens).
|
|
255
|
+
// Only need to contribute the buildInterceptorChain import.
|
|
256
|
+
return {
|
|
257
|
+
imports: ["import { buildInterceptorChain } from '@goodie-ts/aop'"],
|
|
258
|
+
};
|
|
259
|
+
},
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Resolve which bean provides the Kysely instance for TransactionManager auto-wiring.
|
|
264
|
+
*
|
|
265
|
+
* Priority:
|
|
266
|
+
* 1. Explicit `database` option (string class name)
|
|
267
|
+
* 2. Auto-detected class with a `Kysely<...>` property (single match)
|
|
268
|
+
* 3. No wiring (zero matches, or multiple matches without explicit option)
|
|
269
|
+
*/
|
|
270
|
+
function resolveKyselyProvider(beans, explicitDatabase, autoDetected) {
|
|
271
|
+
// Determine target class name
|
|
272
|
+
let targetClassName;
|
|
273
|
+
if (explicitDatabase) {
|
|
274
|
+
targetClassName = explicitDatabase;
|
|
275
|
+
}
|
|
276
|
+
else if (autoDetected.length === 1) {
|
|
277
|
+
targetClassName = autoDetected[0].className;
|
|
278
|
+
}
|
|
279
|
+
else if (autoDetected.length > 1) {
|
|
280
|
+
const names = autoDetected.map((p) => p.className).join(', ');
|
|
281
|
+
console.warn(`[@goodie-ts/kysely] Multiple Kysely providers detected: ${names}. ` +
|
|
282
|
+
"Use createKyselyPlugin({ database: 'ClassName' }) to disambiguate. " +
|
|
283
|
+
'TransactionManager will require manual configure().');
|
|
284
|
+
return [];
|
|
285
|
+
}
|
|
286
|
+
else {
|
|
287
|
+
// Zero providers — no auto-wiring
|
|
288
|
+
return [];
|
|
289
|
+
}
|
|
290
|
+
// Find the bean matching the target class name
|
|
291
|
+
const databaseBean = beans.find((b) => b.tokenRef.kind === 'class' && b.tokenRef.className === targetClassName);
|
|
292
|
+
if (databaseBean && databaseBean.tokenRef.kind === 'class') {
|
|
293
|
+
return [
|
|
294
|
+
{
|
|
295
|
+
tokenRef: {
|
|
296
|
+
kind: 'class',
|
|
297
|
+
className: databaseBean.tokenRef.className,
|
|
298
|
+
importPath: databaseBean.tokenRef.importPath,
|
|
299
|
+
},
|
|
300
|
+
optional: false,
|
|
301
|
+
collection: false,
|
|
302
|
+
sourceLocation: {
|
|
303
|
+
filePath: '@goodie-ts/kysely',
|
|
304
|
+
line: 0,
|
|
305
|
+
column: 0,
|
|
306
|
+
},
|
|
307
|
+
},
|
|
308
|
+
];
|
|
309
|
+
}
|
|
310
|
+
console.warn(`[@goodie-ts/kysely] Database class '${targetClassName}' not found in beans. ` +
|
|
311
|
+
'TransactionManager will require manual configure().');
|
|
312
|
+
return [];
|
|
313
|
+
}
|
|
314
|
+
export default createKyselyPlugin;
|
|
315
|
+
//# sourceMappingURL=kysely-transformer-plugin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kysely-transformer-plugin.js","sourceRoot":"","sources":["../src/kysely-transformer-plugin.ts"],"names":[],"mappings":"AA+BA;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,UAAU,kBAAkB,CAChC,OAA6B;IAE7B,MAAM,sBAAsB,GAAG,IAAI,GAAG,EAAqC,CAAC;IAE5E,qFAAqF;IACrF,MAAM,eAAe,GAAmD,EAAE,CAAC;IAE3E,iEAAiE;IACjE,MAAM,gBAAgB,GAAyB,EAAE,CAAC;IAElD,OAAO;QACL,IAAI,EAAE,QAAQ;QAEd,UAAU,CAAC,GAAwB;YACjC,gDAAgD;YAChD,KAAK,MAAM,IAAI,IAAI,GAAG,CAAC,gBAAgB,CAAC,aAAa,EAAE,EAAE,CAAC;gBACxD,qEAAqE;gBACrE,iFAAiF;gBACjF,MAAM,cAAc,GAAG,IAAI,CAAC,WAAW,EAAE,EAAE,OAAO,EAAE,CAAC;gBACrD,IAAI,cAAc,EAAE,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;oBAC1C,eAAe,CAAC,IAAI,CAAC;wBACnB,SAAS,EAAE,GAAG,CAAC,SAAS;wBACxB,QAAQ,EAAE,GAAG,CAAC,QAAQ;qBACvB,CAAC,CAAC;oBACH,MAAM,CAAC,gCAAgC;gBACzC,CAAC;YACH,CAAC;YAED,4CAA4C;YAC5C,KAAK,MAAM,SAAS,IAAI,GAAG,CAAC,gBAAgB,CAAC,aAAa,EAAE,EAAE,CAAC;gBAC7D,IAAI,SAAS,CAAC,OAAO,EAAE,KAAK,WAAW;oBAAE,SAAS;gBAClD,MAAM,IAAI,GAAG,SAAS,CAAC,YAAY,EAAE,CAAC;gBACtC,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,IAAI,GAAG,CAAC,SAAS,CAAC;gBACtE,gBAAgB,CAAC,IAAI,CAAC;oBACpB,SAAS,EAAE,GAAG,CAAC,SAAS;oBACxB,QAAQ,EAAE,GAAG,CAAC,QAAQ;oBACtB,aAAa,EAAE,IAAI;iBACpB,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,WAAW,CAAC,GAAyB;YACnC,MAAM,UAAU,GAAG,GAAG,CAAC,iBAAiB,CAAC,aAAa,EAAE,CAAC;YAEzD,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;gBACnC,IAAI,SAAS,CAAC,OAAO,EAAE,KAAK,eAAe;oBAAE,SAAS;gBAEtD,6CAA6C;gBAC7C,IAAI,WAAW,GAAgC,UAAU,CAAC;gBAC1D,MAAM,IAAI,GAAG,SAAS,CAAC,YAAY,EAAE,CAAC;gBACtC,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;oBACpB,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;oBAC/B,IAAI,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,EAAE,CAAC;wBAClC,WAAW,GAAG,cAAc,CAAC;oBAC/B,CAAC;gBACH,CAAC;gBAED,MAAM,GAAG,GAAG,GAAG,GAAG,CAAC,QAAQ,IAAI,GAAG,CAAC,SAAS,EAAE,CAAC;gBAC/C,MAAM,QAAQ,GAAG,sBAAsB,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;gBACvD,QAAQ,CAAC,IAAI,CAAC,EAAE,UAAU,EAAE,GAAG,CAAC,UAAU,EAAE,WAAW,EAAE,CAAC,CAAC;gBAC3D,sBAAsB,CAAC,GAAG,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;YAC5C,CAAC;QACH,CAAC;QAED,YAAY,CAAC,KAAyB;YACpC,MAAM,cAAc,GAAuB,EAAE,CAAC;YAE9C,sCAAsC;YACtC,IAAI,gBAAgB,GAAG,KAAK,CAAC;YAE7B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;gBACzB,MAAM,SAAS,GACb,IAAI,CAAC,QAAQ,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC;gBACvE,IAAI,CAAC,SAAS;oBAAE,SAAS;gBAEzB,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,UAAU,IAAI,SAAS,EAAE,CAAC;gBACvD,MAAM,KAAK,GAAG,sBAAsB,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;gBAC9C,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;oBAAE,SAAS;gBAE3C,gBAAgB,GAAG,IAAI,CAAC;gBAExB,MAAM,QAAQ,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,kBAAkB,IAAI,EAAE,CAStD,CAAC;gBAEH,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;oBACzB,MAAM,WAAW,GAAG,QAAQ,CAAC,IAAI,CAC/B,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,KAAK,IAAI,CAAC,UAAU,CACxC,CAAC;oBAEF,MAAM,cAAc,GAAG;wBACrB,SAAS,EAAE,0BAA0B;wBACrC,UAAU,EAAE,mBAAmB;wBAC/B,UAAU,EAAE,QAAiB;wBAC7B,KAAK,EAAE,CAAC,EAAE,EAAE,mEAAmE;wBAC/E,QAAQ,EAAE,EAAE,WAAW,EAAE,IAAI,CAAC,WAAW,EAAE;qBAC5C,CAAC;oBAEF,IAAI,WAAW,EAAE,CAAC;wBAChB,WAAW,CAAC,YAAY,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;oBAChD,CAAC;yBAAM,CAAC;wBACN,QAAQ,CAAC,IAAI,CAAC;4BACZ,UAAU,EAAE,IAAI,CAAC,UAAU;4BAC3B,YAAY,EAAE,CAAC,cAAc,CAAC;yBAC/B,CAAC,CAAC;oBACL,CAAC;gBACH,CAAC;gBAED,IAAI,CAAC,QAAQ,CAAC,kBAAkB,GAAG,QAAQ,CAAC;YAC9C,CAAC;YAED,MAAM,aAAa,GAAG,gBAAgB,CAAC,MAAM,GAAG,CAAC,CAAC;YAElD,IAAI,CAAC,gBAAgB,IAAI,CAAC,aAAa;gBAAE,OAAO,KAAK,CAAC;YAEtD,6CAA6C;YAC7C,wEAAwE;YACxE,MAAM,iBAAiB,GAAG,qBAAqB,CAC7C,KAAK,EACL,OAAO,EAAE,QAAQ,EACjB,eAAe,CAChB,CAAC;YAEF,wCAAwC;YACxC,IAAI,gBAAgB,EAAE,CAAC;gBACrB,cAAc,CAAC,IAAI,CAAC;oBAClB,QAAQ,EAAE;wBACR,IAAI,EAAE,OAAO;wBACb,SAAS,EAAE,oBAAoB;wBAC/B,UAAU,EAAE,mBAAmB;qBAChC;oBACD,KAAK,EAAE,WAAW;oBAClB,KAAK,EAAE,KAAK;oBACZ,IAAI,EAAE,SAAS;oBACf,eAAe,EAAE,iBAAiB;oBAClC,SAAS,EAAE,EAAE;oBACb,WAAW,EAAE,aAAa;oBAC1B,cAAc,EAAE,SAAS;oBACzB,QAAQ,EAAE,EAAE;oBACZ,cAAc,EAAE;wBACd,QAAQ,EAAE,mBAAmB;wBAC7B,IAAI,EAAE,CAAC;wBACP,MAAM,EAAE,CAAC;qBACV;iBACF,CAAC,CAAC;gBAEH,cAAc,CAAC,IAAI,CAAC;oBAClB,QAAQ,EAAE;wBACR,IAAI,EAAE,OAAO;wBACb,SAAS,EAAE,0BAA0B;wBACrC,UAAU,EAAE,mBAAmB;qBAChC;oBACD,KAAK,EAAE,WAAW;oBAClB,KAAK,EAAE,KAAK;oBACZ,IAAI,EAAE,SAAS;oBACf,eAAe,EAAE;wBACf;4BACE,QAAQ,EAAE;gCACR,IAAI,EAAE,OAAO;gCACb,SAAS,EAAE,oBAAoB;gCAC/B,UAAU,EAAE,mBAAmB;6BAChC;4BACD,QAAQ,EAAE,KAAK;4BACf,UAAU,EAAE,KAAK;4BACjB,cAAc,EAAE;gCACd,QAAQ,EAAE,mBAAmB;gCAC7B,IAAI,EAAE,CAAC;gCACP,MAAM,EAAE,CAAC;6BACV;yBACF;qBACF;oBACD,SAAS,EAAE,EAAE;oBACb,WAAW,EAAE,aAAa;oBAC1B,cAAc,EAAE,SAAS;oBACzB,QAAQ,EAAE,EAAE;oBACZ,cAAc,EAAE;wBACd,QAAQ,EAAE,mBAAmB;wBAC7B,IAAI,EAAE,CAAC;wBACP,MAAM,EAAE,CAAC;qBACV;iBACF,CAAC,CAAC;YACL,CAAC;YAED,qCAAqC;YACrC,IAAI,aAAa,EAAE,CAAC;gBAClB,IAAI,iBAAiB,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;oBACnC,OAAO,CAAC,IAAI,CACV,gFAAgF;wBAC9E,sCAAsC,CACzC,CAAC;gBACJ,CAAC;qBAAM,CAAC;oBACN,4DAA4D;oBAC5D,gBAAgB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAC7B,CAAC,CAAC,aAAa,CAAC,aAAa,CAAC,CAAC,CAAC,aAAa,CAAC,CAC/C,CAAC;oBAEF,oFAAoF;oBACpF,MAAM,aAAa,GAAwC,EAAE,CAAC;oBAC9D,KAAK,MAAM,CAAC,IAAI,gBAAgB,EAAE,CAAC;wBACjC,cAAc,CAAC,IAAI,CAAC;4BAClB,QAAQ,EAAE;gCACR,IAAI,EAAE,OAAO;gCACb,SAAS,EAAE,CAAC,CAAC,SAAS;gCACtB,UAAU,EAAE,CAAC,CAAC,QAAQ;6BACvB;4BACD,KAAK,EAAE,WAAW;4BAClB,KAAK,EAAE,KAAK;4BACZ,IAAI,EAAE,SAAS;4BACf,eAAe,EAAE,EAAE;4BACnB,SAAS,EAAE,EAAE;4BACb,WAAW,EAAE,aAAa;4BAC1B,cAAc,EAAE,SAAS;4BACzB,QAAQ,EAAE,EAAE;4BACZ,cAAc,EAAE;gCACd,QAAQ,EAAE,CAAC,CAAC,QAAQ;gCACpB,IAAI,EAAE,CAAC;gCACP,MAAM,EAAE,CAAC;6BACV;yBACF,CAAC,CAAC;wBAEH,aAAa,CAAC,IAAI,CAAC;4BACjB,QAAQ,EAAE;gCACR,IAAI,EAAE,OAAO;gCACb,SAAS,EAAE,CAAC,CAAC,SAAS;gCACtB,UAAU,EAAE,CAAC,CAAC,QAAQ;6BACvB;4BACD,QAAQ,EAAE,KAAK;4BACf,UAAU,EAAE,KAAK;4BACjB,cAAc,EAAE;gCACd,QAAQ,EAAE,mBAAmB;gCAC7B,IAAI,EAAE,CAAC;gCACP,MAAM,EAAE,CAAC;6BACV;yBACF,CAAC,CAAC;oBACL,CAAC;oBAED,0FAA0F;oBAC1F,cAAc,CAAC,IAAI,CAAC;wBAClB,QAAQ,EAAE;4BACR,IAAI,EAAE,OAAO;4BACb,SAAS,EAAE,iBAAiB;4BAC5B,UAAU,EAAE,mBAAmB;yBAChC;wBACD,KAAK,EAAE,WAAW;wBAClB,KAAK,EAAE,IAAI;wBACX,IAAI,EAAE,SAAS;wBACf,eAAe,EAAE,CAAC,iBAAiB,CAAC,CAAC,CAAC,EAAE,GAAG,aAAa,CAAC;wBACzD,SAAS,EAAE,EAAE;wBACb,WAAW,EAAE,aAAa;wBAC1B,cAAc,EAAE,SAAS;wBACzB,QAAQ,EAAE,EAAE,oBAAoB,EAAE,CAAC,SAAS,CAAC,EAAE;wBAC/C,cAAc,EAAE;4BACd,QAAQ,EAAE,mBAAmB;4BAC7B,IAAI,EAAE,CAAC;4BACP,MAAM,EAAE,CAAC;yBACV;qBACF,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;YAED,OAAO,CAAC,GAAG,KAAK,EAAE,GAAG,cAAc,CAAC,CAAC;QACvC,CAAC;QAED,OAAO,CAAC,KAAyB;YAC/B,MAAM,gBAAgB,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE;gBACxC,MAAM,OAAO,GAAG,CAAC,CAAC,QAAQ,CAAC,kBAId,CAAC;gBACd,OAAO,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CACzB,CAAC,CAAC,YAAY,CAAC,IAAI,CACjB,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,KAAK,0BAA0B,CAClD,CACF,CAAC;YACJ,CAAC,CAAC,CAAC;YAEH,IAAI,CAAC,gBAAgB;gBAAE,OAAO,EAAE,CAAC;YAEjC,sEAAsE;YACtE,qEAAqE;YACrE,4DAA4D;YAC5D,OAAO;gBACL,OAAO,EAAE,CAAC,wDAAwD,CAAC;aACpE,CAAC;QACJ,CAAC;KACF,CAAC;AACJ,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,qBAAqB,CAC5B,KAAyB,EACzB,gBAAoC,EACpC,YAA4D;IAE5D,8BAA8B;IAC9B,IAAI,eAAmC,CAAC;IAExC,IAAI,gBAAgB,EAAE,CAAC;QACrB,eAAe,GAAG,gBAAgB,CAAC;IACrC,CAAC;SAAM,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACrC,eAAe,GAAG,YAAY,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAC9C,CAAC;SAAM,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACnC,MAAM,KAAK,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC9D,OAAO,CAAC,IAAI,CACV,2DAA2D,KAAK,IAAI;YAClE,qEAAqE;YACrE,qDAAqD,CACxD,CAAC;QACF,OAAO,EAAE,CAAC;IACZ,CAAC;SAAM,CAAC;QACN,kCAAkC;QAClC,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,+CAA+C;IAC/C,MAAM,YAAY,GAAG,KAAK,CAAC,IAAI,CAC7B,CAAC,CAAC,EAAE,EAAE,CACJ,CAAC,CAAC,QAAQ,CAAC,IAAI,KAAK,OAAO,IAAI,CAAC,CAAC,QAAQ,CAAC,SAAS,KAAK,eAAe,CAC1E,CAAC;IAEF,IAAI,YAAY,IAAI,YAAY,CAAC,QAAQ,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;QAC3D,OAAO;YACL;gBACE,QAAQ,EAAE;oBACR,IAAI,EAAE,OAAO;oBACb,SAAS,EAAE,YAAY,CAAC,QAAQ,CAAC,SAAS;oBAC1C,UAAU,EAAE,YAAY,CAAC,QAAQ,CAAC,UAAU;iBAC7C;gBACD,QAAQ,EAAE,KAAK;gBACf,UAAU,EAAE,KAAK;gBACjB,cAAc,EAAE;oBACd,QAAQ,EAAE,mBAAmB;oBAC7B,IAAI,EAAE,CAAC;oBACP,MAAM,EAAE,CAAC;iBACV;aACF;SACF,CAAC;IACJ,CAAC;IAED,OAAO,CAAC,IAAI,CACV,uCAAuC,eAAe,wBAAwB;QAC5E,qDAAqD,CACxD,CAAC;IACF,OAAO,EAAE,CAAC;AACZ,CAAC;AAED,eAAe,kBAAkB,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { AbstractMigration } from './abstract-migration.js';
|
|
2
|
+
import type { KyselyProvider } from './transaction-manager.js';
|
|
3
|
+
/**
|
|
4
|
+
* Runs Kysely migrations at application startup.
|
|
5
|
+
*
|
|
6
|
+
* Auto-wired by the Kysely transformer plugin as an eager singleton
|
|
7
|
+
* with `@PostConstruct` on `migrate()`. Constructor receives the
|
|
8
|
+
* KyselyProvider and all discovered @Migration instances.
|
|
9
|
+
*/
|
|
10
|
+
export declare class MigrationRunner {
|
|
11
|
+
private readonly kyselyProvider;
|
|
12
|
+
private readonly migrations;
|
|
13
|
+
constructor(kyselyProvider: KyselyProvider, ...migrations: AbstractMigration[]);
|
|
14
|
+
migrate(): Promise<void>;
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=migration-runner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migration-runner.d.ts","sourceRoot":"","sources":["../src/migration-runner.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAEjE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAE/D;;;;;;GAMG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAiB;IAChD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAsB;gBAG/C,cAAc,EAAE,cAAc,EAC9B,GAAG,UAAU,EAAE,iBAAiB,EAAE;IAM9B,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;CAgC/B"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { Migrator } from 'kysely';
|
|
2
|
+
import { getMigrationName } from './decorators/migration.js';
|
|
3
|
+
/**
|
|
4
|
+
* Runs Kysely migrations at application startup.
|
|
5
|
+
*
|
|
6
|
+
* Auto-wired by the Kysely transformer plugin as an eager singleton
|
|
7
|
+
* with `@PostConstruct` on `migrate()`. Constructor receives the
|
|
8
|
+
* KyselyProvider and all discovered @Migration instances.
|
|
9
|
+
*/
|
|
10
|
+
export class MigrationRunner {
|
|
11
|
+
kyselyProvider;
|
|
12
|
+
migrations;
|
|
13
|
+
constructor(kyselyProvider, ...migrations) {
|
|
14
|
+
this.kyselyProvider = kyselyProvider;
|
|
15
|
+
this.migrations = migrations;
|
|
16
|
+
}
|
|
17
|
+
async migrate() {
|
|
18
|
+
const migrationMap = {};
|
|
19
|
+
for (const m of this.migrations) {
|
|
20
|
+
const name = getMigrationName(m);
|
|
21
|
+
if (!name) {
|
|
22
|
+
throw new Error(`MigrationRunner received an object without @Migration metadata: ${m.constructor.name}`);
|
|
23
|
+
}
|
|
24
|
+
// Kysely's Migrator spreads migration objects (...migration), which
|
|
25
|
+
// drops prototype methods from class instances. Bind explicitly.
|
|
26
|
+
migrationMap[name] = {
|
|
27
|
+
up: m.up.bind(m),
|
|
28
|
+
down: m.down?.bind(m),
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
const migrator = new Migrator({
|
|
32
|
+
db: this.kyselyProvider.kysely,
|
|
33
|
+
provider: { getMigrations: async () => migrationMap },
|
|
34
|
+
});
|
|
35
|
+
const { error, results } = await migrator.migrateToLatest();
|
|
36
|
+
for (const r of results ?? []) {
|
|
37
|
+
if (r.status === 'Error') {
|
|
38
|
+
console.error(`Migration "${r.migrationName}" failed`);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
if (error)
|
|
42
|
+
throw error;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=migration-runner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migration-runner.js","sourceRoot":"","sources":["../src/migration-runner.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAElC,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAG7D;;;;;;GAMG;AACH,MAAM,OAAO,eAAe;IACT,cAAc,CAAiB;IAC/B,UAAU,CAAsB;IAEjD,YACE,cAA8B,EAC9B,GAAG,UAA+B;QAElC,IAAI,CAAC,cAAc,GAAG,cAAc,CAAC;QACrC,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;IAC/B,CAAC;IAED,KAAK,CAAC,OAAO;QACX,MAAM,YAAY,GAAoC,EAAE,CAAC;QACzD,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YAChC,MAAM,IAAI,GAAG,gBAAgB,CAAC,CAAC,CAAC,CAAC;YACjC,IAAI,CAAC,IAAI,EAAE,CAAC;gBACV,MAAM,IAAI,KAAK,CACb,mEAAmE,CAAC,CAAC,WAAW,CAAC,IAAI,EAAE,CACxF,CAAC;YACJ,CAAC;YACD,oEAAoE;YACpE,iEAAiE;YACjE,YAAY,CAAC,IAAI,CAAC,GAAG;gBACnB,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;gBAChB,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC;aACtB,CAAC;QACJ,CAAC;QAED,MAAM,QAAQ,GAAG,IAAI,QAAQ,CAAC;YAC5B,EAAE,EAAE,IAAI,CAAC,cAAc,CAAC,MAAM;YAC9B,QAAQ,EAAE,EAAE,aAAa,EAAE,KAAK,IAAI,EAAE,CAAC,YAAY,EAAE;SACtD,CAAC,CAAC;QAEH,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,MAAM,QAAQ,CAAC,eAAe,EAAE,CAAC;QAE5D,KAAK,MAAM,CAAC,IAAI,OAAO,IAAI,EAAE,EAAE,CAAC;YAC9B,IAAI,CAAC,CAAC,MAAM,KAAK,OAAO,EAAE,CAAC;gBACzB,OAAO,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,aAAa,UAAU,CAAC,CAAC;YACzD,CAAC;QACH,CAAC;QAED,IAAI,KAAK;YAAE,MAAM,KAAK,CAAC;IACzB,CAAC;CACF"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { Kysely, Transaction } from 'kysely';
|
|
2
|
+
/**
|
|
3
|
+
* An object that exposes a `.kysely` property — e.g. a Database wrapper class.
|
|
4
|
+
* Used for duck-type detection in the TransactionManager constructor.
|
|
5
|
+
*/
|
|
6
|
+
export interface KyselyProvider {
|
|
7
|
+
kysely: Kysely<any>;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Manages database transactions using AsyncLocalStorage.
|
|
11
|
+
*
|
|
12
|
+
* Provides transaction propagation across async call chains without
|
|
13
|
+
* explicitly threading a transaction object through every method.
|
|
14
|
+
*
|
|
15
|
+
* When auto-wired via `createKyselyPlugin({ database: 'Database' })`,
|
|
16
|
+
* the constructor receives the Database bean and reads its `.kysely` property.
|
|
17
|
+
* Manual `configure()` is still supported for backward compatibility.
|
|
18
|
+
*/
|
|
19
|
+
export declare class TransactionManager {
|
|
20
|
+
private readonly storage;
|
|
21
|
+
private kyselyRef?;
|
|
22
|
+
private testTransactionActive;
|
|
23
|
+
constructor(kyselyOrProvider?: Kysely<any> | KyselyProvider);
|
|
24
|
+
/**
|
|
25
|
+
* Configure the Kysely instance used for transactions.
|
|
26
|
+
* Unnecessary when auto-wired via `createKyselyPlugin({ database: '...' })`.
|
|
27
|
+
*/
|
|
28
|
+
configure(kysely: Kysely<any>): void;
|
|
29
|
+
private get kysely();
|
|
30
|
+
/**
|
|
31
|
+
* Run a function inside a transaction.
|
|
32
|
+
*
|
|
33
|
+
* - If already in a transaction (REQUIRED propagation), reuses it.
|
|
34
|
+
* - If `requiresNew` is true, always starts a fresh transaction.
|
|
35
|
+
*/
|
|
36
|
+
runInTransaction<T>(fn: () => Promise<T>, requiresNew?: boolean): Promise<T>;
|
|
37
|
+
/** Get the current transaction, or undefined if not in one. */
|
|
38
|
+
currentTransaction(): Transaction<any> | undefined;
|
|
39
|
+
/**
|
|
40
|
+
* Get the current database connection — transaction-aware.
|
|
41
|
+
* Returns the active transaction if inside one, otherwise the raw Kysely instance.
|
|
42
|
+
*/
|
|
43
|
+
getConnection(): Kysely<any>;
|
|
44
|
+
/**
|
|
45
|
+
* Start a test-scoped transaction that ALL queries will use.
|
|
46
|
+
*
|
|
47
|
+
* Replaces the internal Kysely reference with the transaction object,
|
|
48
|
+
* so any code calling `getConnection()` or `database.kysely` (via the
|
|
49
|
+
* provider proxy) will automatically use this transaction.
|
|
50
|
+
*
|
|
51
|
+
* Designed for test frameworks (e.g. vitest fixtures) where
|
|
52
|
+
* AsyncLocalStorage context may not propagate through `use()`.
|
|
53
|
+
*
|
|
54
|
+
* @returns A rollback function — call it to roll back the transaction
|
|
55
|
+
* and restore the original Kysely instance.
|
|
56
|
+
*/
|
|
57
|
+
startTestTransaction(): Promise<() => Promise<void>>;
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=transaction-manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transaction-manager.d.ts","sourceRoot":"","sources":["../src/transaction-manager.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAC;AAElD;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC;CACrB;AAED;;;;;;;;;GASG;AACH,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA6C;IACrE,OAAO,CAAC,SAAS,CAAC,CAAc;IAChC,OAAO,CAAC,qBAAqB,CAAS;gBAE1B,gBAAgB,CAAC,EAAE,MAAM,CAAC,GAAG,CAAC,GAAG,cAAc;IAoB3D;;;OAGG;IACH,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,GAAG,IAAI;IAIpC,OAAO,KAAK,MAAM,GAOjB;IAED;;;;;OAKG;IACG,gBAAgB,CAAC,CAAC,EACtB,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACpB,WAAW,UAAQ,GAClB,OAAO,CAAC,CAAC,CAAC;IAiBb,+DAA+D;IAC/D,kBAAkB,IAAI,WAAW,CAAC,GAAG,CAAC,GAAG,SAAS;IAIlD;;;OAGG;IACH,aAAa,IAAI,MAAM,CAAC,GAAG,CAAC;IAI5B;;;;;;;;;;;;OAYG;IACG,oBAAoB,IAAI,OAAO,CAAC,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAmC3D"}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
2
|
+
/**
|
|
3
|
+
* Manages database transactions using AsyncLocalStorage.
|
|
4
|
+
*
|
|
5
|
+
* Provides transaction propagation across async call chains without
|
|
6
|
+
* explicitly threading a transaction object through every method.
|
|
7
|
+
*
|
|
8
|
+
* When auto-wired via `createKyselyPlugin({ database: 'Database' })`,
|
|
9
|
+
* the constructor receives the Database bean and reads its `.kysely` property.
|
|
10
|
+
* Manual `configure()` is still supported for backward compatibility.
|
|
11
|
+
*/
|
|
12
|
+
export class TransactionManager {
|
|
13
|
+
storage = new AsyncLocalStorage();
|
|
14
|
+
kyselyRef;
|
|
15
|
+
testTransactionActive = false;
|
|
16
|
+
constructor(kyselyOrProvider) {
|
|
17
|
+
if (kyselyOrProvider) {
|
|
18
|
+
if ('kysely' in kyselyOrProvider) {
|
|
19
|
+
this.kyselyRef = kyselyOrProvider.kysely;
|
|
20
|
+
// Make the provider's .kysely property transaction-aware.
|
|
21
|
+
// Any code accessing provider.kysely (e.g. database.kysely) will
|
|
22
|
+
// automatically use the active transaction when inside one.
|
|
23
|
+
const tm = this;
|
|
24
|
+
Object.defineProperty(kyselyOrProvider, 'kysely', {
|
|
25
|
+
get() {
|
|
26
|
+
return tm.getConnection();
|
|
27
|
+
},
|
|
28
|
+
configurable: true,
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
this.kyselyRef = kyselyOrProvider;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Configure the Kysely instance used for transactions.
|
|
38
|
+
* Unnecessary when auto-wired via `createKyselyPlugin({ database: '...' })`.
|
|
39
|
+
*/
|
|
40
|
+
configure(kysely) {
|
|
41
|
+
this.kyselyRef = kysely;
|
|
42
|
+
}
|
|
43
|
+
get kysely() {
|
|
44
|
+
if (!this.kyselyRef) {
|
|
45
|
+
throw new Error('TransactionManager not configured. Call transactionManager.configure(kysely) or use createKyselyPlugin({ database: "YourDatabase" }).');
|
|
46
|
+
}
|
|
47
|
+
return this.kyselyRef;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Run a function inside a transaction.
|
|
51
|
+
*
|
|
52
|
+
* - If already in a transaction (REQUIRED propagation), reuses it.
|
|
53
|
+
* - If `requiresNew` is true, always starts a fresh transaction.
|
|
54
|
+
*/
|
|
55
|
+
async runInTransaction(fn, requiresNew = false) {
|
|
56
|
+
// Inside a test transaction, all queries already use the test transaction.
|
|
57
|
+
// Skip creating new transactions to avoid Kysely's nested transaction error.
|
|
58
|
+
if (this.testTransactionActive) {
|
|
59
|
+
return fn();
|
|
60
|
+
}
|
|
61
|
+
const existing = this.storage.getStore();
|
|
62
|
+
if (existing && !requiresNew) {
|
|
63
|
+
return fn();
|
|
64
|
+
}
|
|
65
|
+
return this.kysely.transaction().execute(async (trx) => {
|
|
66
|
+
return this.storage.run(trx, fn);
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
/** Get the current transaction, or undefined if not in one. */
|
|
70
|
+
currentTransaction() {
|
|
71
|
+
return this.storage.getStore();
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Get the current database connection — transaction-aware.
|
|
75
|
+
* Returns the active transaction if inside one, otherwise the raw Kysely instance.
|
|
76
|
+
*/
|
|
77
|
+
getConnection() {
|
|
78
|
+
return this.currentTransaction() ?? this.kysely;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Start a test-scoped transaction that ALL queries will use.
|
|
82
|
+
*
|
|
83
|
+
* Replaces the internal Kysely reference with the transaction object,
|
|
84
|
+
* so any code calling `getConnection()` or `database.kysely` (via the
|
|
85
|
+
* provider proxy) will automatically use this transaction.
|
|
86
|
+
*
|
|
87
|
+
* Designed for test frameworks (e.g. vitest fixtures) where
|
|
88
|
+
* AsyncLocalStorage context may not propagate through `use()`.
|
|
89
|
+
*
|
|
90
|
+
* @returns A rollback function — call it to roll back the transaction
|
|
91
|
+
* and restore the original Kysely instance.
|
|
92
|
+
*/
|
|
93
|
+
async startTestTransaction() {
|
|
94
|
+
const original = this.kyselyRef;
|
|
95
|
+
let triggerRollback;
|
|
96
|
+
let transactionReady;
|
|
97
|
+
const readyPromise = new Promise((resolve) => {
|
|
98
|
+
transactionReady = resolve;
|
|
99
|
+
});
|
|
100
|
+
const rollbackPromise = this.kysely
|
|
101
|
+
.transaction()
|
|
102
|
+
.execute(async (trx) => {
|
|
103
|
+
this.kyselyRef = trx;
|
|
104
|
+
this.testTransactionActive = true;
|
|
105
|
+
transactionReady();
|
|
106
|
+
await new Promise((_resolve, reject) => {
|
|
107
|
+
triggerRollback = () => reject(new TestRollbackSignal());
|
|
108
|
+
});
|
|
109
|
+
})
|
|
110
|
+
.catch((e) => {
|
|
111
|
+
if (!(e instanceof TestRollbackSignal))
|
|
112
|
+
throw e;
|
|
113
|
+
})
|
|
114
|
+
.finally(() => {
|
|
115
|
+
this.testTransactionActive = false;
|
|
116
|
+
this.kyselyRef = original;
|
|
117
|
+
});
|
|
118
|
+
await readyPromise;
|
|
119
|
+
return async () => {
|
|
120
|
+
triggerRollback();
|
|
121
|
+
await rollbackPromise;
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
/** @internal Sentinel error for test transaction rollback. */
|
|
126
|
+
class TestRollbackSignal extends Error {
|
|
127
|
+
constructor() {
|
|
128
|
+
super('TestRollbackSignal');
|
|
129
|
+
this.name = 'TestRollbackSignal';
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
//# sourceMappingURL=transaction-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transaction-manager.js","sourceRoot":"","sources":["../src/transaction-manager.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { InvocationContext, MethodInterceptor } from '@goodie-ts/aop';
|
|
2
|
+
import type { TransactionManager } from './transaction-manager.js';
|
|
3
|
+
/**
|
|
4
|
+
* AOP interceptor that wraps method execution in a database transaction.
|
|
5
|
+
*
|
|
6
|
+
* Reads propagation strategy from `ctx.metadata` (set by the kysely
|
|
7
|
+
* transformer plugin).
|
|
8
|
+
*/
|
|
9
|
+
export declare class TransactionalInterceptor implements MethodInterceptor {
|
|
10
|
+
private readonly transactionManager;
|
|
11
|
+
constructor(transactionManager: TransactionManager);
|
|
12
|
+
intercept(ctx: InvocationContext): unknown;
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=transactional-interceptor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transactional-interceptor.d.ts","sourceRoot":"","sources":["../src/transactional-interceptor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAC3E,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAOnE;;;;;GAKG;AACH,qBAAa,wBAAyB,YAAW,iBAAiB;IACpD,OAAO,CAAC,QAAQ,CAAC,kBAAkB;gBAAlB,kBAAkB,EAAE,kBAAkB;IAEnE,SAAS,CAAC,GAAG,EAAE,iBAAiB,GAAG,OAAO;CAY3C"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AOP interceptor that wraps method execution in a database transaction.
|
|
3
|
+
*
|
|
4
|
+
* Reads propagation strategy from `ctx.metadata` (set by the kysely
|
|
5
|
+
* transformer plugin).
|
|
6
|
+
*/
|
|
7
|
+
export class TransactionalInterceptor {
|
|
8
|
+
transactionManager;
|
|
9
|
+
constructor(transactionManager) {
|
|
10
|
+
this.transactionManager = transactionManager;
|
|
11
|
+
}
|
|
12
|
+
intercept(ctx) {
|
|
13
|
+
const meta = ctx.metadata;
|
|
14
|
+
if (!meta)
|
|
15
|
+
return ctx.proceed();
|
|
16
|
+
const requiresNew = meta.propagation === 'REQUIRES_NEW';
|
|
17
|
+
return this.transactionManager.runInTransaction(async () => {
|
|
18
|
+
// Must await so rejected promises propagate within the transaction scope,
|
|
19
|
+
// ensuring Kysely rolls back on failure.
|
|
20
|
+
return await ctx.proceed();
|
|
21
|
+
}, requiresNew);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=transactional-interceptor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transactional-interceptor.js","sourceRoot":"","sources":["../src/transactional-interceptor.ts"],"names":[],"mappings":"AAQA;;;;;GAKG;AACH,MAAM,OAAO,wBAAwB;IACN;IAA7B,YAA6B,kBAAsC;QAAtC,uBAAkB,GAAlB,kBAAkB,CAAoB;IAAG,CAAC;IAEvE,SAAS,CAAC,GAAsB;QAC9B,MAAM,IAAI,GAAG,GAAG,CAAC,QAA6C,CAAC;QAC/D,IAAI,CAAC,IAAI;YAAE,OAAO,GAAG,CAAC,OAAO,EAAE,CAAC;QAEhC,MAAM,WAAW,GAAG,IAAI,CAAC,WAAW,KAAK,cAAc,CAAC;QAExD,OAAO,IAAI,CAAC,kBAAkB,CAAC,gBAAgB,CAAC,KAAK,IAAI,EAAE;YACzD,0EAA0E;YAC1E,yCAAyC;YACzC,OAAO,MAAM,GAAG,CAAC,OAAO,EAAE,CAAC;QAC7B,CAAC,EAAE,WAAW,CAAC,CAAC;IAClB,CAAC;CACF"}
|
package/package.json
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@goodie-ts/kysely",
|
|
3
|
+
"version": "0.4.0",
|
|
4
|
+
"description": "Kysely integration for goodie-ts — @Transactional decorator, TransactionManager, CrudRepository",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/GOOD-Code-ApS/goodie.git",
|
|
10
|
+
"directory": "packages/kysely"
|
|
11
|
+
},
|
|
12
|
+
"publishConfig": {
|
|
13
|
+
"access": "public"
|
|
14
|
+
},
|
|
15
|
+
"goodie": {
|
|
16
|
+
"plugin": "./dist/kysely-transformer-plugin.js"
|
|
17
|
+
},
|
|
18
|
+
"main": "dist/index.js",
|
|
19
|
+
"types": "dist/index.d.ts",
|
|
20
|
+
"exports": {
|
|
21
|
+
".": {
|
|
22
|
+
"types": "./dist/index.d.ts",
|
|
23
|
+
"import": "./dist/index.js"
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"scripts": {
|
|
27
|
+
"build": "tsc",
|
|
28
|
+
"clean": "rm -rf dist *.tsbuildinfo"
|
|
29
|
+
},
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"@goodie-ts/aop": "workspace:*"
|
|
32
|
+
},
|
|
33
|
+
"peerDependencies": {
|
|
34
|
+
"@goodie-ts/transformer": "workspace:*",
|
|
35
|
+
"kysely": ">=0.27.0"
|
|
36
|
+
},
|
|
37
|
+
"peerDependenciesMeta": {
|
|
38
|
+
"@goodie-ts/transformer": {
|
|
39
|
+
"optional": true
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"@goodie-ts/transformer": "workspace:*",
|
|
44
|
+
"@types/node": "^22.0.0",
|
|
45
|
+
"kysely": "^0.27.0",
|
|
46
|
+
"ts-morph": "^24.0.0"
|
|
47
|
+
},
|
|
48
|
+
"files": [
|
|
49
|
+
"dist"
|
|
50
|
+
]
|
|
51
|
+
}
|