@veloxts/orm 0.6.55 → 0.6.57
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +32 -0
- package/dist/plugin.d.ts +13 -0
- package/dist/plugin.js +25 -2
- package/dist/providers.d.ts +157 -0
- package/dist/providers.js +230 -0
- package/dist/tokens.d.ts +130 -0
- package/dist/tokens.js +140 -0
- package/dist/types.d.ts +26 -0
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# @veloxts/orm
|
|
2
2
|
|
|
3
|
+
## 0.6.57
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- feat: add DI support to ecosystem packages and main packages
|
|
8
|
+
- Updated dependencies
|
|
9
|
+
- @veloxts/core@0.6.57
|
|
10
|
+
|
|
11
|
+
## 0.6.56
|
|
12
|
+
|
|
13
|
+
### Patch Changes
|
|
14
|
+
|
|
15
|
+
- fix(create): resolve TypeScript errors in RSC templates
|
|
16
|
+
- Updated dependencies
|
|
17
|
+
- @veloxts/core@0.6.56
|
|
18
|
+
|
|
3
19
|
## 0.6.55
|
|
4
20
|
|
|
5
21
|
### Patch Changes
|
package/dist/index.d.ts
CHANGED
|
@@ -111,3 +111,22 @@ export {
|
|
|
111
111
|
* ```
|
|
112
112
|
*/
|
|
113
113
|
databasePlugin, } from './plugin.js';
|
|
114
|
+
/**
|
|
115
|
+
* DI tokens and providers for @veloxts/orm
|
|
116
|
+
*
|
|
117
|
+
* Use these to integrate ORM services with the @veloxts/core DI container.
|
|
118
|
+
*
|
|
119
|
+
* @example
|
|
120
|
+
* ```typescript
|
|
121
|
+
* import { Container } from '@veloxts/core';
|
|
122
|
+
* import { registerOrmProviders, DATABASE, DATABASE_CLIENT } from '@veloxts/orm';
|
|
123
|
+
*
|
|
124
|
+
* const container = new Container();
|
|
125
|
+
* registerOrmProviders(container, { client: prisma });
|
|
126
|
+
*
|
|
127
|
+
* const db = container.resolve(DATABASE);
|
|
128
|
+
* const client = container.resolve(DATABASE_CLIENT);
|
|
129
|
+
* ```
|
|
130
|
+
*/
|
|
131
|
+
export { databaseProvider, registerOrmProviders, registerTenantProviders, tenantClientPoolProvider, tenantProvisionerProvider, tenantSchemaManagerProvider, } from './providers.js';
|
|
132
|
+
export { DATABASE, DATABASE_CLIENT, DATABASE_CONFIG, PUBLIC_DATABASE_CLIENT, TENANT_CLIENT_POOL, TENANT_CLIENT_POOL_CONFIG, TENANT_MIDDLEWARE_CONFIG, TENANT_PROVISIONER, TENANT_PROVISIONER_CONFIG, TENANT_SCHEMA_MANAGER, TENANT_SCHEMA_MANAGER_CONFIG, } from './tokens.js';
|
package/dist/index.js
CHANGED
|
@@ -87,3 +87,35 @@ export {
|
|
|
87
87
|
* ```
|
|
88
88
|
*/
|
|
89
89
|
databasePlugin, } from './plugin.js';
|
|
90
|
+
// ============================================================================
|
|
91
|
+
// Dependency Injection
|
|
92
|
+
// ============================================================================
|
|
93
|
+
/**
|
|
94
|
+
* DI tokens and providers for @veloxts/orm
|
|
95
|
+
*
|
|
96
|
+
* Use these to integrate ORM services with the @veloxts/core DI container.
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* ```typescript
|
|
100
|
+
* import { Container } from '@veloxts/core';
|
|
101
|
+
* import { registerOrmProviders, DATABASE, DATABASE_CLIENT } from '@veloxts/orm';
|
|
102
|
+
*
|
|
103
|
+
* const container = new Container();
|
|
104
|
+
* registerOrmProviders(container, { client: prisma });
|
|
105
|
+
*
|
|
106
|
+
* const db = container.resolve(DATABASE);
|
|
107
|
+
* const client = container.resolve(DATABASE_CLIENT);
|
|
108
|
+
* ```
|
|
109
|
+
*/
|
|
110
|
+
// Provider exports - factory functions for registering services
|
|
111
|
+
export { databaseProvider, registerOrmProviders, registerTenantProviders, tenantClientPoolProvider, tenantProvisionerProvider, tenantSchemaManagerProvider, } from './providers.js';
|
|
112
|
+
// Token exports - unique identifiers for DI resolution
|
|
113
|
+
export {
|
|
114
|
+
// Core database tokens
|
|
115
|
+
DATABASE, DATABASE_CLIENT, DATABASE_CONFIG,
|
|
116
|
+
// Public database token (multi-tenant)
|
|
117
|
+
PUBLIC_DATABASE_CLIENT,
|
|
118
|
+
// Tenant service tokens
|
|
119
|
+
TENANT_CLIENT_POOL,
|
|
120
|
+
// Tenant config tokens
|
|
121
|
+
TENANT_CLIENT_POOL_CONFIG, TENANT_MIDDLEWARE_CONFIG, TENANT_PROVISIONER, TENANT_PROVISIONER_CONFIG, TENANT_SCHEMA_MANAGER, TENANT_SCHEMA_MANAGER_CONFIG, } from './tokens.js';
|
package/dist/plugin.d.ts
CHANGED
|
@@ -90,6 +90,19 @@ declare module '@veloxts/core' {
|
|
|
90
90
|
*
|
|
91
91
|
* @example
|
|
92
92
|
* ```typescript
|
|
93
|
+
* // With DI container
|
|
94
|
+
* import { Container } from '@veloxts/core';
|
|
95
|
+
* import { databasePlugin, DATABASE } from '@veloxts/orm';
|
|
96
|
+
*
|
|
97
|
+
* const container = new Container();
|
|
98
|
+
* await app.use(databasePlugin({ client: prisma, container }));
|
|
99
|
+
*
|
|
100
|
+
* // Resolve from container
|
|
101
|
+
* const db = container.resolve(DATABASE);
|
|
102
|
+
* ```
|
|
103
|
+
*
|
|
104
|
+
* @example
|
|
105
|
+
* ```typescript
|
|
93
106
|
* // Using ctx.db in procedures
|
|
94
107
|
* import { defineProcedures, procedure } from '@veloxts/router';
|
|
95
108
|
* import { z } from '@veloxts/validation';
|
package/dist/plugin.js
CHANGED
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import { ConfigurationError, definePlugin } from '@veloxts/core';
|
|
10
10
|
import { createDatabase } from './client.js';
|
|
11
|
+
import { registerOrmProviders } from './providers.js';
|
|
12
|
+
import { DATABASE } from './tokens.js';
|
|
11
13
|
import { isDatabaseClient } from './types.js';
|
|
12
14
|
// ============================================================================
|
|
13
15
|
// Plugin Implementation
|
|
@@ -53,6 +55,19 @@ const DEFAULT_PLUGIN_NAME = '@veloxts/orm';
|
|
|
53
55
|
*
|
|
54
56
|
* @example
|
|
55
57
|
* ```typescript
|
|
58
|
+
* // With DI container
|
|
59
|
+
* import { Container } from '@veloxts/core';
|
|
60
|
+
* import { databasePlugin, DATABASE } from '@veloxts/orm';
|
|
61
|
+
*
|
|
62
|
+
* const container = new Container();
|
|
63
|
+
* await app.use(databasePlugin({ client: prisma, container }));
|
|
64
|
+
*
|
|
65
|
+
* // Resolve from container
|
|
66
|
+
* const db = container.resolve(DATABASE);
|
|
67
|
+
* ```
|
|
68
|
+
*
|
|
69
|
+
* @example
|
|
70
|
+
* ```typescript
|
|
56
71
|
* // Using ctx.db in procedures
|
|
57
72
|
* import { defineProcedures, procedure } from '@veloxts/router';
|
|
58
73
|
* import { z } from '@veloxts/validation';
|
|
@@ -89,6 +104,7 @@ export function databasePlugin(config) {
|
|
|
89
104
|
'Ensure you are passing a valid Prisma client instance.');
|
|
90
105
|
}
|
|
91
106
|
const pluginName = config.name ?? DEFAULT_PLUGIN_NAME;
|
|
107
|
+
const { container } = config;
|
|
92
108
|
// Plugin state - holds the database wrapper
|
|
93
109
|
const state = {
|
|
94
110
|
database: null,
|
|
@@ -97,8 +113,15 @@ export function databasePlugin(config) {
|
|
|
97
113
|
name: pluginName,
|
|
98
114
|
version: ORM_PLUGIN_VERSION,
|
|
99
115
|
async register(server) {
|
|
100
|
-
|
|
101
|
-
|
|
116
|
+
if (container) {
|
|
117
|
+
// DI-enabled path: Register providers and resolve from container
|
|
118
|
+
registerOrmProviders(container, config);
|
|
119
|
+
state.database = container.resolve(DATABASE);
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
// Legacy path: Direct instantiation (backward compatible)
|
|
123
|
+
state.database = createDatabase({ client: config.client });
|
|
124
|
+
}
|
|
102
125
|
// Connect to the database
|
|
103
126
|
await state.database.connect();
|
|
104
127
|
// Add database client to request context via onRequest hook
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DI Providers for @veloxts/orm
|
|
3
|
+
*
|
|
4
|
+
* Factory provider functions for registering ORM services with the DI container.
|
|
5
|
+
* These providers allow services to be managed by the container for testability and flexibility.
|
|
6
|
+
*
|
|
7
|
+
* @module orm/providers
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* import { Container } from '@veloxts/core';
|
|
12
|
+
* import { registerOrmProviders, DATABASE, DATABASE_CLIENT } from '@veloxts/orm';
|
|
13
|
+
*
|
|
14
|
+
* const container = new Container();
|
|
15
|
+
* registerOrmProviders(container, { client: prisma });
|
|
16
|
+
*
|
|
17
|
+
* const db = container.resolve(DATABASE);
|
|
18
|
+
* await db.connect();
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
import { type Container, type FactoryProvider } from '@veloxts/core';
|
|
22
|
+
import { type Database } from './client.js';
|
|
23
|
+
import type { TenantClientPool, TenantClientPoolConfig, TenantProvisioner, TenantProvisionerConfig, TenantSchemaManager, TenantSchemaManagerConfig } from './tenant/types.js';
|
|
24
|
+
import type { DatabaseClient, OrmPluginConfig } from './types.js';
|
|
25
|
+
/**
|
|
26
|
+
* Creates a factory provider for the database wrapper
|
|
27
|
+
*
|
|
28
|
+
* Requires DATABASE_CLIENT to be registered in the container.
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ```typescript
|
|
32
|
+
* container.register({ provide: DATABASE_CLIENT, useValue: prisma });
|
|
33
|
+
* container.register(databaseProvider());
|
|
34
|
+
* const db = container.resolve(DATABASE);
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
export declare function databaseProvider(): FactoryProvider<Database<DatabaseClient>>;
|
|
38
|
+
/**
|
|
39
|
+
* Creates a factory provider for the tenant client pool
|
|
40
|
+
*
|
|
41
|
+
* Requires TENANT_CLIENT_POOL_CONFIG to be registered in the container.
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* ```typescript
|
|
45
|
+
* container.register({
|
|
46
|
+
* provide: TENANT_CLIENT_POOL_CONFIG,
|
|
47
|
+
* useValue: {
|
|
48
|
+
* baseDatabaseUrl: process.env.DATABASE_URL,
|
|
49
|
+
* createClient: (schema) => new PrismaClient({ ... }),
|
|
50
|
+
* }
|
|
51
|
+
* });
|
|
52
|
+
* container.register(tenantClientPoolProvider());
|
|
53
|
+
* const pool = container.resolve(TENANT_CLIENT_POOL);
|
|
54
|
+
* ```
|
|
55
|
+
*/
|
|
56
|
+
export declare function tenantClientPoolProvider(): FactoryProvider<TenantClientPool<DatabaseClient>>;
|
|
57
|
+
/**
|
|
58
|
+
* Creates a factory provider for the tenant schema manager
|
|
59
|
+
*
|
|
60
|
+
* Requires TENANT_SCHEMA_MANAGER_CONFIG to be registered in the container.
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* ```typescript
|
|
64
|
+
* container.register({
|
|
65
|
+
* provide: TENANT_SCHEMA_MANAGER_CONFIG,
|
|
66
|
+
* useValue: { databaseUrl: process.env.DATABASE_URL }
|
|
67
|
+
* });
|
|
68
|
+
* container.register(tenantSchemaManagerProvider());
|
|
69
|
+
* const schemaManager = container.resolve(TENANT_SCHEMA_MANAGER);
|
|
70
|
+
* ```
|
|
71
|
+
*/
|
|
72
|
+
export declare function tenantSchemaManagerProvider(): FactoryProvider<TenantSchemaManager>;
|
|
73
|
+
/**
|
|
74
|
+
* Creates a factory provider for the tenant provisioner
|
|
75
|
+
*
|
|
76
|
+
* Requires TENANT_PROVISIONER_CONFIG to be registered in the container.
|
|
77
|
+
*
|
|
78
|
+
* @example
|
|
79
|
+
* ```typescript
|
|
80
|
+
* container.register({
|
|
81
|
+
* provide: TENANT_PROVISIONER_CONFIG,
|
|
82
|
+
* useValue: {
|
|
83
|
+
* schemaManager: container.resolve(TENANT_SCHEMA_MANAGER),
|
|
84
|
+
* publicClient: prisma,
|
|
85
|
+
* clientPool: container.resolve(TENANT_CLIENT_POOL),
|
|
86
|
+
* }
|
|
87
|
+
* });
|
|
88
|
+
* container.register(tenantProvisionerProvider());
|
|
89
|
+
* const provisioner = container.resolve(TENANT_PROVISIONER);
|
|
90
|
+
* ```
|
|
91
|
+
*/
|
|
92
|
+
export declare function tenantProvisionerProvider(): FactoryProvider<TenantProvisioner>;
|
|
93
|
+
/**
|
|
94
|
+
* Registers core ORM providers with a container
|
|
95
|
+
*
|
|
96
|
+
* This registers the database client and wrapper for basic ORM usage.
|
|
97
|
+
* For multi-tenant scenarios, use `registerTenantProviders` as well.
|
|
98
|
+
*
|
|
99
|
+
* @param container - The DI container to register providers with
|
|
100
|
+
* @param config - ORM plugin configuration with Prisma client
|
|
101
|
+
*
|
|
102
|
+
* @example
|
|
103
|
+
* ```typescript
|
|
104
|
+
* import { Container } from '@veloxts/core';
|
|
105
|
+
* import { registerOrmProviders, DATABASE, DATABASE_CLIENT } from '@veloxts/orm';
|
|
106
|
+
*
|
|
107
|
+
* const container = new Container();
|
|
108
|
+
* registerOrmProviders(container, { client: prisma });
|
|
109
|
+
*
|
|
110
|
+
* const db = container.resolve(DATABASE);
|
|
111
|
+
* const client = container.resolve(DATABASE_CLIENT);
|
|
112
|
+
* ```
|
|
113
|
+
*/
|
|
114
|
+
export declare function registerOrmProviders(container: Container, config: OrmPluginConfig<DatabaseClient>): void;
|
|
115
|
+
/**
|
|
116
|
+
* Registers tenant providers with a container
|
|
117
|
+
*
|
|
118
|
+
* Use this for multi-tenant scenarios after calling `registerOrmProviders`.
|
|
119
|
+
*
|
|
120
|
+
* @param container - The DI container to register providers with
|
|
121
|
+
* @param config - Tenant configuration options
|
|
122
|
+
*
|
|
123
|
+
* @example
|
|
124
|
+
* ```typescript
|
|
125
|
+
* import { Container } from '@veloxts/core';
|
|
126
|
+
* import {
|
|
127
|
+
* registerOrmProviders,
|
|
128
|
+
* registerTenantProviders,
|
|
129
|
+
* TENANT_CLIENT_POOL,
|
|
130
|
+
* TENANT_SCHEMA_MANAGER,
|
|
131
|
+
* } from '@veloxts/orm';
|
|
132
|
+
*
|
|
133
|
+
* const container = new Container();
|
|
134
|
+
*
|
|
135
|
+
* // Register core ORM
|
|
136
|
+
* registerOrmProviders(container, { client: prisma });
|
|
137
|
+
*
|
|
138
|
+
* // Register tenant providers
|
|
139
|
+
* registerTenantProviders(container, {
|
|
140
|
+
* clientPool: {
|
|
141
|
+
* baseDatabaseUrl: process.env.DATABASE_URL!,
|
|
142
|
+
* createClient: (schema) => createPrismaClient(schema),
|
|
143
|
+
* },
|
|
144
|
+
* schemaManager: {
|
|
145
|
+
* databaseUrl: process.env.DATABASE_URL!,
|
|
146
|
+
* },
|
|
147
|
+
* });
|
|
148
|
+
*
|
|
149
|
+
* const pool = container.resolve(TENANT_CLIENT_POOL);
|
|
150
|
+
* const schemaManager = container.resolve(TENANT_SCHEMA_MANAGER);
|
|
151
|
+
* ```
|
|
152
|
+
*/
|
|
153
|
+
export declare function registerTenantProviders(container: Container, config: {
|
|
154
|
+
clientPool?: TenantClientPoolConfig<DatabaseClient>;
|
|
155
|
+
schemaManager?: TenantSchemaManagerConfig;
|
|
156
|
+
provisioner?: TenantProvisionerConfig<DatabaseClient>;
|
|
157
|
+
}): void;
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DI Providers for @veloxts/orm
|
|
3
|
+
*
|
|
4
|
+
* Factory provider functions for registering ORM services with the DI container.
|
|
5
|
+
* These providers allow services to be managed by the container for testability and flexibility.
|
|
6
|
+
*
|
|
7
|
+
* @module orm/providers
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* import { Container } from '@veloxts/core';
|
|
12
|
+
* import { registerOrmProviders, DATABASE, DATABASE_CLIENT } from '@veloxts/orm';
|
|
13
|
+
*
|
|
14
|
+
* const container = new Container();
|
|
15
|
+
* registerOrmProviders(container, { client: prisma });
|
|
16
|
+
*
|
|
17
|
+
* const db = container.resolve(DATABASE);
|
|
18
|
+
* await db.connect();
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
import { Scope } from '@veloxts/core';
|
|
22
|
+
import { createDatabase } from './client.js';
|
|
23
|
+
import { createTenantClientPool } from './tenant/client-pool.js';
|
|
24
|
+
import { createTenantSchemaManager } from './tenant/schema/manager.js';
|
|
25
|
+
import { createTenantProvisioner } from './tenant/schema/provisioner.js';
|
|
26
|
+
import { DATABASE, DATABASE_CLIENT, DATABASE_CONFIG, TENANT_CLIENT_POOL, TENANT_CLIENT_POOL_CONFIG, TENANT_PROVISIONER, TENANT_PROVISIONER_CONFIG, TENANT_SCHEMA_MANAGER, TENANT_SCHEMA_MANAGER_CONFIG, } from './tokens.js';
|
|
27
|
+
// ============================================================================
|
|
28
|
+
// Core Database Providers
|
|
29
|
+
// ============================================================================
|
|
30
|
+
/**
|
|
31
|
+
* Creates a factory provider for the database wrapper
|
|
32
|
+
*
|
|
33
|
+
* Requires DATABASE_CLIENT to be registered in the container.
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* ```typescript
|
|
37
|
+
* container.register({ provide: DATABASE_CLIENT, useValue: prisma });
|
|
38
|
+
* container.register(databaseProvider());
|
|
39
|
+
* const db = container.resolve(DATABASE);
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
export function databaseProvider() {
|
|
43
|
+
return {
|
|
44
|
+
provide: DATABASE,
|
|
45
|
+
useFactory: (client) => createDatabase({ client }),
|
|
46
|
+
inject: [DATABASE_CLIENT],
|
|
47
|
+
scope: Scope.SINGLETON,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
// ============================================================================
|
|
51
|
+
// Tenant Providers
|
|
52
|
+
// ============================================================================
|
|
53
|
+
/**
|
|
54
|
+
* Creates a factory provider for the tenant client pool
|
|
55
|
+
*
|
|
56
|
+
* Requires TENANT_CLIENT_POOL_CONFIG to be registered in the container.
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* ```typescript
|
|
60
|
+
* container.register({
|
|
61
|
+
* provide: TENANT_CLIENT_POOL_CONFIG,
|
|
62
|
+
* useValue: {
|
|
63
|
+
* baseDatabaseUrl: process.env.DATABASE_URL,
|
|
64
|
+
* createClient: (schema) => new PrismaClient({ ... }),
|
|
65
|
+
* }
|
|
66
|
+
* });
|
|
67
|
+
* container.register(tenantClientPoolProvider());
|
|
68
|
+
* const pool = container.resolve(TENANT_CLIENT_POOL);
|
|
69
|
+
* ```
|
|
70
|
+
*/
|
|
71
|
+
export function tenantClientPoolProvider() {
|
|
72
|
+
return {
|
|
73
|
+
provide: TENANT_CLIENT_POOL,
|
|
74
|
+
useFactory: (config) => createTenantClientPool(config),
|
|
75
|
+
inject: [TENANT_CLIENT_POOL_CONFIG],
|
|
76
|
+
scope: Scope.SINGLETON,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Creates a factory provider for the tenant schema manager
|
|
81
|
+
*
|
|
82
|
+
* Requires TENANT_SCHEMA_MANAGER_CONFIG to be registered in the container.
|
|
83
|
+
*
|
|
84
|
+
* @example
|
|
85
|
+
* ```typescript
|
|
86
|
+
* container.register({
|
|
87
|
+
* provide: TENANT_SCHEMA_MANAGER_CONFIG,
|
|
88
|
+
* useValue: { databaseUrl: process.env.DATABASE_URL }
|
|
89
|
+
* });
|
|
90
|
+
* container.register(tenantSchemaManagerProvider());
|
|
91
|
+
* const schemaManager = container.resolve(TENANT_SCHEMA_MANAGER);
|
|
92
|
+
* ```
|
|
93
|
+
*/
|
|
94
|
+
export function tenantSchemaManagerProvider() {
|
|
95
|
+
return {
|
|
96
|
+
provide: TENANT_SCHEMA_MANAGER,
|
|
97
|
+
useFactory: (config) => createTenantSchemaManager(config),
|
|
98
|
+
inject: [TENANT_SCHEMA_MANAGER_CONFIG],
|
|
99
|
+
scope: Scope.SINGLETON,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Creates a factory provider for the tenant provisioner
|
|
104
|
+
*
|
|
105
|
+
* Requires TENANT_PROVISIONER_CONFIG to be registered in the container.
|
|
106
|
+
*
|
|
107
|
+
* @example
|
|
108
|
+
* ```typescript
|
|
109
|
+
* container.register({
|
|
110
|
+
* provide: TENANT_PROVISIONER_CONFIG,
|
|
111
|
+
* useValue: {
|
|
112
|
+
* schemaManager: container.resolve(TENANT_SCHEMA_MANAGER),
|
|
113
|
+
* publicClient: prisma,
|
|
114
|
+
* clientPool: container.resolve(TENANT_CLIENT_POOL),
|
|
115
|
+
* }
|
|
116
|
+
* });
|
|
117
|
+
* container.register(tenantProvisionerProvider());
|
|
118
|
+
* const provisioner = container.resolve(TENANT_PROVISIONER);
|
|
119
|
+
* ```
|
|
120
|
+
*/
|
|
121
|
+
export function tenantProvisionerProvider() {
|
|
122
|
+
return {
|
|
123
|
+
provide: TENANT_PROVISIONER,
|
|
124
|
+
useFactory: (config) => createTenantProvisioner(config),
|
|
125
|
+
inject: [TENANT_PROVISIONER_CONFIG],
|
|
126
|
+
scope: Scope.SINGLETON,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
// ============================================================================
|
|
130
|
+
// Bulk Registration Helpers
|
|
131
|
+
// ============================================================================
|
|
132
|
+
/**
|
|
133
|
+
* Registers core ORM providers with a container
|
|
134
|
+
*
|
|
135
|
+
* This registers the database client and wrapper for basic ORM usage.
|
|
136
|
+
* For multi-tenant scenarios, use `registerTenantProviders` as well.
|
|
137
|
+
*
|
|
138
|
+
* @param container - The DI container to register providers with
|
|
139
|
+
* @param config - ORM plugin configuration with Prisma client
|
|
140
|
+
*
|
|
141
|
+
* @example
|
|
142
|
+
* ```typescript
|
|
143
|
+
* import { Container } from '@veloxts/core';
|
|
144
|
+
* import { registerOrmProviders, DATABASE, DATABASE_CLIENT } from '@veloxts/orm';
|
|
145
|
+
*
|
|
146
|
+
* const container = new Container();
|
|
147
|
+
* registerOrmProviders(container, { client: prisma });
|
|
148
|
+
*
|
|
149
|
+
* const db = container.resolve(DATABASE);
|
|
150
|
+
* const client = container.resolve(DATABASE_CLIENT);
|
|
151
|
+
* ```
|
|
152
|
+
*/
|
|
153
|
+
export function registerOrmProviders(container, config) {
|
|
154
|
+
// Register config
|
|
155
|
+
container.register({
|
|
156
|
+
provide: DATABASE_CONFIG,
|
|
157
|
+
useValue: config,
|
|
158
|
+
});
|
|
159
|
+
// Register database client
|
|
160
|
+
container.register({
|
|
161
|
+
provide: DATABASE_CLIENT,
|
|
162
|
+
useValue: config.client,
|
|
163
|
+
});
|
|
164
|
+
// Register database wrapper provider
|
|
165
|
+
container.register(databaseProvider());
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Registers tenant providers with a container
|
|
169
|
+
*
|
|
170
|
+
* Use this for multi-tenant scenarios after calling `registerOrmProviders`.
|
|
171
|
+
*
|
|
172
|
+
* @param container - The DI container to register providers with
|
|
173
|
+
* @param config - Tenant configuration options
|
|
174
|
+
*
|
|
175
|
+
* @example
|
|
176
|
+
* ```typescript
|
|
177
|
+
* import { Container } from '@veloxts/core';
|
|
178
|
+
* import {
|
|
179
|
+
* registerOrmProviders,
|
|
180
|
+
* registerTenantProviders,
|
|
181
|
+
* TENANT_CLIENT_POOL,
|
|
182
|
+
* TENANT_SCHEMA_MANAGER,
|
|
183
|
+
* } from '@veloxts/orm';
|
|
184
|
+
*
|
|
185
|
+
* const container = new Container();
|
|
186
|
+
*
|
|
187
|
+
* // Register core ORM
|
|
188
|
+
* registerOrmProviders(container, { client: prisma });
|
|
189
|
+
*
|
|
190
|
+
* // Register tenant providers
|
|
191
|
+
* registerTenantProviders(container, {
|
|
192
|
+
* clientPool: {
|
|
193
|
+
* baseDatabaseUrl: process.env.DATABASE_URL!,
|
|
194
|
+
* createClient: (schema) => createPrismaClient(schema),
|
|
195
|
+
* },
|
|
196
|
+
* schemaManager: {
|
|
197
|
+
* databaseUrl: process.env.DATABASE_URL!,
|
|
198
|
+
* },
|
|
199
|
+
* });
|
|
200
|
+
*
|
|
201
|
+
* const pool = container.resolve(TENANT_CLIENT_POOL);
|
|
202
|
+
* const schemaManager = container.resolve(TENANT_SCHEMA_MANAGER);
|
|
203
|
+
* ```
|
|
204
|
+
*/
|
|
205
|
+
export function registerTenantProviders(container, config) {
|
|
206
|
+
// Register client pool if config provided
|
|
207
|
+
if (config.clientPool) {
|
|
208
|
+
container.register({
|
|
209
|
+
provide: TENANT_CLIENT_POOL_CONFIG,
|
|
210
|
+
useValue: config.clientPool,
|
|
211
|
+
});
|
|
212
|
+
container.register(tenantClientPoolProvider());
|
|
213
|
+
}
|
|
214
|
+
// Register schema manager if config provided
|
|
215
|
+
if (config.schemaManager) {
|
|
216
|
+
container.register({
|
|
217
|
+
provide: TENANT_SCHEMA_MANAGER_CONFIG,
|
|
218
|
+
useValue: config.schemaManager,
|
|
219
|
+
});
|
|
220
|
+
container.register(tenantSchemaManagerProvider());
|
|
221
|
+
}
|
|
222
|
+
// Register provisioner if config provided
|
|
223
|
+
if (config.provisioner) {
|
|
224
|
+
container.register({
|
|
225
|
+
provide: TENANT_PROVISIONER_CONFIG,
|
|
226
|
+
useValue: config.provisioner,
|
|
227
|
+
});
|
|
228
|
+
container.register(tenantProvisionerProvider());
|
|
229
|
+
}
|
|
230
|
+
}
|
package/dist/tokens.d.ts
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DI Tokens for @veloxts/orm
|
|
3
|
+
*
|
|
4
|
+
* Symbol-based tokens for type-safe dependency injection.
|
|
5
|
+
* These tokens allow services to be registered, resolved, and mocked via the DI container.
|
|
6
|
+
*
|
|
7
|
+
* @module orm/tokens
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* import { Container } from '@veloxts/core';
|
|
12
|
+
* import { DATABASE_CLIENT, DATABASE, registerOrmProviders } from '@veloxts/orm';
|
|
13
|
+
*
|
|
14
|
+
* const container = new Container();
|
|
15
|
+
* registerOrmProviders(container, { client: prisma });
|
|
16
|
+
*
|
|
17
|
+
* const db = container.resolve(DATABASE);
|
|
18
|
+
* const client = container.resolve(DATABASE_CLIENT);
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
import type { Database } from './client.js';
|
|
22
|
+
import type { TenantClientPool, TenantClientPoolConfig, TenantMiddlewareConfig, TenantProvisioner, TenantProvisionerConfig, TenantSchemaManager, TenantSchemaManagerConfig } from './tenant/types.js';
|
|
23
|
+
import type { DatabaseClient, OrmPluginConfig } from './types.js';
|
|
24
|
+
/**
|
|
25
|
+
* Database client token (raw Prisma client)
|
|
26
|
+
*
|
|
27
|
+
* The underlying Prisma client instance used for database queries.
|
|
28
|
+
* This is the client passed to `databasePlugin({ client: ... })`.
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ```typescript
|
|
32
|
+
* const client = container.resolve(DATABASE_CLIENT);
|
|
33
|
+
* const users = await client.user.findMany();
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
export declare const DATABASE_CLIENT: import("@veloxts/core").SymbolToken<DatabaseClient>;
|
|
37
|
+
/**
|
|
38
|
+
* Database wrapper token (with lifecycle management)
|
|
39
|
+
*
|
|
40
|
+
* The Database wrapper provides connection state tracking and
|
|
41
|
+
* controlled connect/disconnect methods.
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* ```typescript
|
|
45
|
+
* const db = container.resolve(DATABASE);
|
|
46
|
+
* await db.connect();
|
|
47
|
+
* console.log(db.isConnected); // true
|
|
48
|
+
* const users = await db.client.user.findMany();
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
export declare const DATABASE: import("@veloxts/core").SymbolToken<Database<DatabaseClient>>;
|
|
52
|
+
/**
|
|
53
|
+
* ORM plugin configuration token
|
|
54
|
+
*
|
|
55
|
+
* Contains the plugin configuration including the Prisma client.
|
|
56
|
+
*/
|
|
57
|
+
export declare const DATABASE_CONFIG: import("@veloxts/core").SymbolToken<OrmPluginConfig<DatabaseClient>>;
|
|
58
|
+
/**
|
|
59
|
+
* Tenant client pool configuration token
|
|
60
|
+
*
|
|
61
|
+
* Configuration for creating tenant-scoped database clients.
|
|
62
|
+
*/
|
|
63
|
+
export declare const TENANT_CLIENT_POOL_CONFIG: import("@veloxts/core").SymbolToken<TenantClientPoolConfig<DatabaseClient>>;
|
|
64
|
+
/**
|
|
65
|
+
* Tenant schema manager configuration token
|
|
66
|
+
*
|
|
67
|
+
* Configuration for PostgreSQL schema management operations.
|
|
68
|
+
*/
|
|
69
|
+
export declare const TENANT_SCHEMA_MANAGER_CONFIG: import("@veloxts/core").SymbolToken<TenantSchemaManagerConfig>;
|
|
70
|
+
/**
|
|
71
|
+
* Tenant provisioner configuration token
|
|
72
|
+
*
|
|
73
|
+
* Configuration for tenant provisioning operations.
|
|
74
|
+
*/
|
|
75
|
+
export declare const TENANT_PROVISIONER_CONFIG: import("@veloxts/core").SymbolToken<TenantProvisionerConfig<DatabaseClient>>;
|
|
76
|
+
/**
|
|
77
|
+
* Tenant middleware configuration token
|
|
78
|
+
*
|
|
79
|
+
* Configuration for tenant middleware including tenant loading and client pool.
|
|
80
|
+
*/
|
|
81
|
+
export declare const TENANT_MIDDLEWARE_CONFIG: import("@veloxts/core").SymbolToken<TenantMiddlewareConfig<DatabaseClient>>;
|
|
82
|
+
/**
|
|
83
|
+
* Tenant client pool token
|
|
84
|
+
*
|
|
85
|
+
* Pool of tenant-scoped database clients for schema-per-tenant isolation.
|
|
86
|
+
*
|
|
87
|
+
* @example
|
|
88
|
+
* ```typescript
|
|
89
|
+
* const pool = container.resolve(TENANT_CLIENT_POOL);
|
|
90
|
+
* const client = await pool.getClient('tenant_acme_corp');
|
|
91
|
+
* ```
|
|
92
|
+
*/
|
|
93
|
+
export declare const TENANT_CLIENT_POOL: import("@veloxts/core").SymbolToken<TenantClientPool<DatabaseClient>>;
|
|
94
|
+
/**
|
|
95
|
+
* Tenant schema manager token
|
|
96
|
+
*
|
|
97
|
+
* Manages PostgreSQL schemas for tenant isolation.
|
|
98
|
+
*
|
|
99
|
+
* @example
|
|
100
|
+
* ```typescript
|
|
101
|
+
* const schemaManager = container.resolve(TENANT_SCHEMA_MANAGER);
|
|
102
|
+
* await schemaManager.createSchema('acme-corp');
|
|
103
|
+
* ```
|
|
104
|
+
*/
|
|
105
|
+
export declare const TENANT_SCHEMA_MANAGER: import("@veloxts/core").SymbolToken<TenantSchemaManager>;
|
|
106
|
+
/**
|
|
107
|
+
* Tenant provisioner token
|
|
108
|
+
*
|
|
109
|
+
* Handles full tenant lifecycle: creation, migration, and deprovisioning.
|
|
110
|
+
*
|
|
111
|
+
* @example
|
|
112
|
+
* ```typescript
|
|
113
|
+
* const provisioner = container.resolve(TENANT_PROVISIONER);
|
|
114
|
+
* const result = await provisioner.provision({ slug: 'acme-corp', name: 'Acme Corp' });
|
|
115
|
+
* ```
|
|
116
|
+
*/
|
|
117
|
+
export declare const TENANT_PROVISIONER: import("@veloxts/core").SymbolToken<TenantProvisioner>;
|
|
118
|
+
/**
|
|
119
|
+
* Public database client token (shared schema)
|
|
120
|
+
*
|
|
121
|
+
* In multi-tenant scenarios, this token represents the public schema client
|
|
122
|
+
* used for shared data like tenant records, user lookups, etc.
|
|
123
|
+
*
|
|
124
|
+
* @example
|
|
125
|
+
* ```typescript
|
|
126
|
+
* const publicDb = container.resolve(PUBLIC_DATABASE_CLIENT);
|
|
127
|
+
* const tenant = await publicDb.tenant.findUnique({ where: { id } });
|
|
128
|
+
* ```
|
|
129
|
+
*/
|
|
130
|
+
export declare const PUBLIC_DATABASE_CLIENT: import("@veloxts/core").SymbolToken<DatabaseClient>;
|
package/dist/tokens.js
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DI Tokens for @veloxts/orm
|
|
3
|
+
*
|
|
4
|
+
* Symbol-based tokens for type-safe dependency injection.
|
|
5
|
+
* These tokens allow services to be registered, resolved, and mocked via the DI container.
|
|
6
|
+
*
|
|
7
|
+
* @module orm/tokens
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* import { Container } from '@veloxts/core';
|
|
12
|
+
* import { DATABASE_CLIENT, DATABASE, registerOrmProviders } from '@veloxts/orm';
|
|
13
|
+
*
|
|
14
|
+
* const container = new Container();
|
|
15
|
+
* registerOrmProviders(container, { client: prisma });
|
|
16
|
+
*
|
|
17
|
+
* const db = container.resolve(DATABASE);
|
|
18
|
+
* const client = container.resolve(DATABASE_CLIENT);
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
import { token } from '@veloxts/core';
|
|
22
|
+
// ============================================================================
|
|
23
|
+
// Core Database Tokens
|
|
24
|
+
// ============================================================================
|
|
25
|
+
/**
|
|
26
|
+
* Database client token (raw Prisma client)
|
|
27
|
+
*
|
|
28
|
+
* The underlying Prisma client instance used for database queries.
|
|
29
|
+
* This is the client passed to `databasePlugin({ client: ... })`.
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```typescript
|
|
33
|
+
* const client = container.resolve(DATABASE_CLIENT);
|
|
34
|
+
* const users = await client.user.findMany();
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
export const DATABASE_CLIENT = token.symbol('DATABASE_CLIENT');
|
|
38
|
+
/**
|
|
39
|
+
* Database wrapper token (with lifecycle management)
|
|
40
|
+
*
|
|
41
|
+
* The Database wrapper provides connection state tracking and
|
|
42
|
+
* controlled connect/disconnect methods.
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* ```typescript
|
|
46
|
+
* const db = container.resolve(DATABASE);
|
|
47
|
+
* await db.connect();
|
|
48
|
+
* console.log(db.isConnected); // true
|
|
49
|
+
* const users = await db.client.user.findMany();
|
|
50
|
+
* ```
|
|
51
|
+
*/
|
|
52
|
+
export const DATABASE = token.symbol('DATABASE');
|
|
53
|
+
/**
|
|
54
|
+
* ORM plugin configuration token
|
|
55
|
+
*
|
|
56
|
+
* Contains the plugin configuration including the Prisma client.
|
|
57
|
+
*/
|
|
58
|
+
export const DATABASE_CONFIG = token.symbol('DATABASE_CONFIG');
|
|
59
|
+
// ============================================================================
|
|
60
|
+
// Tenant Tokens - Configuration
|
|
61
|
+
// ============================================================================
|
|
62
|
+
/**
|
|
63
|
+
* Tenant client pool configuration token
|
|
64
|
+
*
|
|
65
|
+
* Configuration for creating tenant-scoped database clients.
|
|
66
|
+
*/
|
|
67
|
+
export const TENANT_CLIENT_POOL_CONFIG = token.symbol('TENANT_CLIENT_POOL_CONFIG');
|
|
68
|
+
/**
|
|
69
|
+
* Tenant schema manager configuration token
|
|
70
|
+
*
|
|
71
|
+
* Configuration for PostgreSQL schema management operations.
|
|
72
|
+
*/
|
|
73
|
+
export const TENANT_SCHEMA_MANAGER_CONFIG = token.symbol('TENANT_SCHEMA_MANAGER_CONFIG');
|
|
74
|
+
/**
|
|
75
|
+
* Tenant provisioner configuration token
|
|
76
|
+
*
|
|
77
|
+
* Configuration for tenant provisioning operations.
|
|
78
|
+
*/
|
|
79
|
+
export const TENANT_PROVISIONER_CONFIG = token.symbol('TENANT_PROVISIONER_CONFIG');
|
|
80
|
+
/**
|
|
81
|
+
* Tenant middleware configuration token
|
|
82
|
+
*
|
|
83
|
+
* Configuration for tenant middleware including tenant loading and client pool.
|
|
84
|
+
*/
|
|
85
|
+
export const TENANT_MIDDLEWARE_CONFIG = token.symbol('TENANT_MIDDLEWARE_CONFIG');
|
|
86
|
+
// ============================================================================
|
|
87
|
+
// Tenant Tokens - Services
|
|
88
|
+
// ============================================================================
|
|
89
|
+
/**
|
|
90
|
+
* Tenant client pool token
|
|
91
|
+
*
|
|
92
|
+
* Pool of tenant-scoped database clients for schema-per-tenant isolation.
|
|
93
|
+
*
|
|
94
|
+
* @example
|
|
95
|
+
* ```typescript
|
|
96
|
+
* const pool = container.resolve(TENANT_CLIENT_POOL);
|
|
97
|
+
* const client = await pool.getClient('tenant_acme_corp');
|
|
98
|
+
* ```
|
|
99
|
+
*/
|
|
100
|
+
export const TENANT_CLIENT_POOL = token.symbol('TENANT_CLIENT_POOL');
|
|
101
|
+
/**
|
|
102
|
+
* Tenant schema manager token
|
|
103
|
+
*
|
|
104
|
+
* Manages PostgreSQL schemas for tenant isolation.
|
|
105
|
+
*
|
|
106
|
+
* @example
|
|
107
|
+
* ```typescript
|
|
108
|
+
* const schemaManager = container.resolve(TENANT_SCHEMA_MANAGER);
|
|
109
|
+
* await schemaManager.createSchema('acme-corp');
|
|
110
|
+
* ```
|
|
111
|
+
*/
|
|
112
|
+
export const TENANT_SCHEMA_MANAGER = token.symbol('TENANT_SCHEMA_MANAGER');
|
|
113
|
+
/**
|
|
114
|
+
* Tenant provisioner token
|
|
115
|
+
*
|
|
116
|
+
* Handles full tenant lifecycle: creation, migration, and deprovisioning.
|
|
117
|
+
*
|
|
118
|
+
* @example
|
|
119
|
+
* ```typescript
|
|
120
|
+
* const provisioner = container.resolve(TENANT_PROVISIONER);
|
|
121
|
+
* const result = await provisioner.provision({ slug: 'acme-corp', name: 'Acme Corp' });
|
|
122
|
+
* ```
|
|
123
|
+
*/
|
|
124
|
+
export const TENANT_PROVISIONER = token.symbol('TENANT_PROVISIONER');
|
|
125
|
+
// ============================================================================
|
|
126
|
+
// Public Database Token (for multi-tenant scenarios)
|
|
127
|
+
// ============================================================================
|
|
128
|
+
/**
|
|
129
|
+
* Public database client token (shared schema)
|
|
130
|
+
*
|
|
131
|
+
* In multi-tenant scenarios, this token represents the public schema client
|
|
132
|
+
* used for shared data like tenant records, user lookups, etc.
|
|
133
|
+
*
|
|
134
|
+
* @example
|
|
135
|
+
* ```typescript
|
|
136
|
+
* const publicDb = container.resolve(PUBLIC_DATABASE_CLIENT);
|
|
137
|
+
* const tenant = await publicDb.tenant.findUnique({ where: { id } });
|
|
138
|
+
* ```
|
|
139
|
+
*/
|
|
140
|
+
export const PUBLIC_DATABASE_CLIENT = token.symbol('PUBLIC_DATABASE_CLIENT');
|
package/dist/types.d.ts
CHANGED
|
@@ -91,6 +91,32 @@ export interface OrmPluginConfig<TClient extends DatabaseClient> {
|
|
|
91
91
|
* @default '@veloxts/orm'
|
|
92
92
|
*/
|
|
93
93
|
name?: string;
|
|
94
|
+
/**
|
|
95
|
+
* DI container for service registration and resolution (optional)
|
|
96
|
+
*
|
|
97
|
+
* When provided, ORM services are registered with the container and can be:
|
|
98
|
+
* - Resolved from the container directly
|
|
99
|
+
* - Mocked in tests by overriding registrations
|
|
100
|
+
* - Managed alongside other application services
|
|
101
|
+
*
|
|
102
|
+
* When not provided, services are created directly (legacy behavior).
|
|
103
|
+
*
|
|
104
|
+
* @example
|
|
105
|
+
* ```typescript
|
|
106
|
+
* import { Container } from '@veloxts/core';
|
|
107
|
+
* import { databasePlugin, DATABASE } from '@veloxts/orm';
|
|
108
|
+
*
|
|
109
|
+
* const container = new Container();
|
|
110
|
+
* app.register(databasePlugin({
|
|
111
|
+
* client: prisma,
|
|
112
|
+
* container,
|
|
113
|
+
* }));
|
|
114
|
+
*
|
|
115
|
+
* // Services now available from container
|
|
116
|
+
* const db = container.resolve(DATABASE);
|
|
117
|
+
* ```
|
|
118
|
+
*/
|
|
119
|
+
container?: import('@veloxts/core').Container;
|
|
94
120
|
}
|
|
95
121
|
/**
|
|
96
122
|
* Configuration options for the database wrapper
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@veloxts/orm",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.57",
|
|
4
4
|
"description": "Prisma wrapper with enhanced DX for VeloxTS framework",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"fastify": "5.6.2",
|
|
20
20
|
"pg": "8.16.0",
|
|
21
21
|
"pg-format": "1.0.4",
|
|
22
|
-
"@veloxts/core": "0.6.
|
|
22
|
+
"@veloxts/core": "0.6.57"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
25
|
"@types/pg": "8.15.4",
|