@arki/db 0.1.0 → 0.1.2
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/dist/client/ids.d.ts +1 -1
- package/dist/client/index.d.ts +1 -1
- package/dist/dot.d.ts +28 -12
- package/dist/dot.d.ts.map +1 -1
- package/dist/dot.js +49 -18
- package/dist/dot.js.map +1 -1
- package/dist/env.d.ts.map +1 -1
- package/dist/env.js +14 -10
- package/dist/env.js.map +1 -1
- package/package.json +12 -5
- package/src/builder.ts +359 -0
- package/src/bun.ts +58 -0
- package/src/client/README.md +5 -0
- package/src/client/ids.ts +11 -0
- package/src/client/index.ts +11 -0
- package/src/connection-options.ts +49 -0
- package/src/debug.ts +24 -0
- package/src/dot.ts +200 -0
- package/src/env.ts +92 -0
- package/src/factory.ts +47 -0
- package/src/id-factory.ts +172 -0
- package/src/id.ts +12 -0
- package/src/init.bun.ts +58 -0
- package/src/init.ts +48 -0
- package/src/orm.ts +9 -0
- package/src/pg.ts +1 -0
- package/src/runtime-local.ts +48 -0
package/src/builder.ts
ADDED
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
import type { AnyRelations } from 'drizzle-orm';
|
|
2
|
+
import { defineRelations } from 'drizzle-orm';
|
|
3
|
+
import type { NodePgDatabase } from 'drizzle-orm/node-postgres';
|
|
4
|
+
import type { PoolConfig } from 'pg';
|
|
5
|
+
|
|
6
|
+
import { debugProjection } from './debug.js';
|
|
7
|
+
import { initDbWithOptions } from './init.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Projection middleware context
|
|
11
|
+
*/
|
|
12
|
+
export type ProjectionMiddlewareContext = {
|
|
13
|
+
next: () => Promise<void>;
|
|
14
|
+
meta: {
|
|
15
|
+
name: string;
|
|
16
|
+
eventCount: number;
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Projection middleware function
|
|
22
|
+
*/
|
|
23
|
+
export type ProjectionMiddleware = (context: ProjectionMiddlewareContext) => Promise<void>;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Projection handler context
|
|
27
|
+
*/
|
|
28
|
+
export type ProjectionHandlerContext<TRepos, TRelations extends AnyRelations = AnyRelations> = {
|
|
29
|
+
repo: TRepos;
|
|
30
|
+
db: NodePgDatabase<TRelations>;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Projection handler function
|
|
35
|
+
*/
|
|
36
|
+
export type ProjectionHandler<TEvent, TRepos, TRelations extends AnyRelations = AnyRelations> = (
|
|
37
|
+
events: TEvent[],
|
|
38
|
+
context: ProjectionHandlerContext<TRepos, TRelations>,
|
|
39
|
+
) => Promise<void>;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Projection builder interface
|
|
43
|
+
*/
|
|
44
|
+
export type ProjectionBuilder<
|
|
45
|
+
TEvent = never,
|
|
46
|
+
TRepos = never,
|
|
47
|
+
TRelations extends AnyRelations = AnyRelations,
|
|
48
|
+
> = {
|
|
49
|
+
named<TName extends string>(name: TName): ProjectionBuilderWithName<TEvent, TRepos, TRelations, TName>;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export type ProjectionBuilderWithName<
|
|
53
|
+
_TEvent,
|
|
54
|
+
TRepos,
|
|
55
|
+
TRelations extends AnyRelations,
|
|
56
|
+
TName extends string,
|
|
57
|
+
> = {
|
|
58
|
+
on<TEventType extends { type: string }>(
|
|
59
|
+
eventTypes: readonly TEventType['type'][] | TEventType['type'][],
|
|
60
|
+
): ProjectionBuilderWithEvents<TEventType, TRepos, TRelations, TName>;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export type ProjectionBuilderWithEvents<
|
|
64
|
+
TEvent,
|
|
65
|
+
TRepos,
|
|
66
|
+
TRelations extends AnyRelations,
|
|
67
|
+
TName extends string,
|
|
68
|
+
> = {
|
|
69
|
+
handle(handler: ProjectionHandler<TEvent, TRepos, TRelations>): ProjectionDefinition<TName>;
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Emmett-compatible projection definition.
|
|
74
|
+
*/
|
|
75
|
+
export type ProjectionDefinition<TName extends string = string> = {
|
|
76
|
+
name: TName;
|
|
77
|
+
canHandle: string[];
|
|
78
|
+
handle: (events: { type: string }[]) => Promise<void>;
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Database builder with projection support
|
|
83
|
+
*/
|
|
84
|
+
export type DbBuilder<TRelations extends AnyRelations, TRepos extends Record<string, unknown>> = {
|
|
85
|
+
readonly db: NodePgDatabase<TRelations>;
|
|
86
|
+
readonly isDev: boolean;
|
|
87
|
+
|
|
88
|
+
registerRepository<K extends string, R>(
|
|
89
|
+
key: K,
|
|
90
|
+
factory: (db: NodePgDatabase<TRelations>) => R,
|
|
91
|
+
): DbBuilder<TRelations, TRepos & Record<K, R>>;
|
|
92
|
+
|
|
93
|
+
projectionMiddleware(fn: ProjectionMiddleware): ProjectionMiddleware;
|
|
94
|
+
|
|
95
|
+
readonly projection: ProjectionBuilderRoot<TRepos, TRelations>;
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Projection builder root that can have middleware applied
|
|
100
|
+
*/
|
|
101
|
+
export type ProjectionBuilderRoot<TRepos, TRelations extends AnyRelations = AnyRelations> = {
|
|
102
|
+
use(middleware: ProjectionMiddleware): ProjectionBuilderRoot<TRepos, TRelations>;
|
|
103
|
+
} & ProjectionBuilder<never, TRepos, TRelations>;
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Schema configuration step
|
|
107
|
+
*/
|
|
108
|
+
export type SchemaBuilder<TRelations extends AnyRelations> = {
|
|
109
|
+
create(options: {
|
|
110
|
+
connectionString: string;
|
|
111
|
+
isDev?: boolean;
|
|
112
|
+
poolConfig?: Partial<PoolConfig>;
|
|
113
|
+
}): DbBuilder<TRelations, Record<string, never>>;
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Initial builder step
|
|
118
|
+
*/
|
|
119
|
+
export type InitialBuilder = {
|
|
120
|
+
/**
|
|
121
|
+
* Set the database tables. Internally builds a relations config via
|
|
122
|
+
* `defineRelations()` so consumers keep passing a plain table record.
|
|
123
|
+
*
|
|
124
|
+
* Use this for backends that don't define explicit relations and rely on
|
|
125
|
+
* auto-derivation (no joins). For explicit RQBv2 relations, prefer
|
|
126
|
+
* `.relations(...)` with a `defineRelations(tables, cb)` result.
|
|
127
|
+
*/
|
|
128
|
+
schema<TSchema extends Record<string, unknown>>(
|
|
129
|
+
schema: TSchema,
|
|
130
|
+
): SchemaBuilder<ReturnType<typeof defineRelations<TSchema>>>;
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Pass a pre-built relations object (the result of
|
|
134
|
+
* `defineRelations(tables, cb)`) so the database knows about every table
|
|
135
|
+
* AND the explicit one/many relations declared by the application.
|
|
136
|
+
*/
|
|
137
|
+
relations<TRelations extends AnyRelations>(relations: TRelations): SchemaBuilder<TRelations>;
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Applies a middleware chain around a core handler function.
|
|
142
|
+
*/
|
|
143
|
+
function applyMiddleware(
|
|
144
|
+
middleware: ProjectionMiddleware[],
|
|
145
|
+
name: string,
|
|
146
|
+
eventCount: number,
|
|
147
|
+
coreHandler: () => Promise<void>,
|
|
148
|
+
): Promise<void> {
|
|
149
|
+
const meta = { name, eventCount };
|
|
150
|
+
|
|
151
|
+
let next = coreHandler;
|
|
152
|
+
for (let i = middleware.length - 1; i >= 0; i--) {
|
|
153
|
+
const mw = middleware[i]!;
|
|
154
|
+
const currentNext = next;
|
|
155
|
+
next = () => mw({ next: currentNext, meta });
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
return next();
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
class ProjectionBuilderImpl<
|
|
162
|
+
TEvent,
|
|
163
|
+
TRepos,
|
|
164
|
+
TRelations extends AnyRelations,
|
|
165
|
+
TName extends string = string,
|
|
166
|
+
>
|
|
167
|
+
implements
|
|
168
|
+
ProjectionBuilder<TEvent, TRepos, TRelations>,
|
|
169
|
+
ProjectionBuilderWithName<TEvent, TRepos, TRelations, TName>,
|
|
170
|
+
ProjectionBuilderWithEvents<TEvent, TRepos, TRelations, TName>,
|
|
171
|
+
ProjectionBuilderRoot<TRepos, TRelations>
|
|
172
|
+
{
|
|
173
|
+
private _name?: string;
|
|
174
|
+
private _eventTypes?: string[];
|
|
175
|
+
private _middleware: ProjectionMiddleware[] = [];
|
|
176
|
+
|
|
177
|
+
constructor(
|
|
178
|
+
private readonly repos: TRepos,
|
|
179
|
+
private readonly _db: NodePgDatabase<TRelations>,
|
|
180
|
+
middleware?: ProjectionMiddleware[],
|
|
181
|
+
) {
|
|
182
|
+
if (middleware) {
|
|
183
|
+
this._middleware = [...middleware];
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
named<N extends string>(name: N): ProjectionBuilderWithName<TEvent, TRepos, TRelations, N> {
|
|
188
|
+
debugProjection('[builder] Setting projection name: %s', name);
|
|
189
|
+
this._name = name;
|
|
190
|
+
return this as unknown as ProjectionBuilderImpl<TEvent, TRepos, TRelations, N>;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
on<TEventType extends { type: string }>(
|
|
194
|
+
eventTypes: readonly TEventType['type'][] | TEventType['type'][],
|
|
195
|
+
): ProjectionBuilderWithEvents<TEventType, TRepos, TRelations, TName> {
|
|
196
|
+
debugProjection('[builder] Registering event types: %o', eventTypes);
|
|
197
|
+
this._eventTypes = [...eventTypes];
|
|
198
|
+
return this as unknown as ProjectionBuilderImpl<TEventType, TRepos, TRelations, TName>;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
handle(handler: ProjectionHandler<TEvent, TRepos, TRelations>): ProjectionDefinition<TName> {
|
|
202
|
+
if (!this._name) {
|
|
203
|
+
debugProjection('[builder] Error: Projection name is required');
|
|
204
|
+
throw new Error('Projection name is required. Call .named() first.');
|
|
205
|
+
}
|
|
206
|
+
if (!this._eventTypes) {
|
|
207
|
+
debugProjection('[builder] Error: Event types are required');
|
|
208
|
+
throw new Error('Event types are required. Call .on() first.');
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const name = this._name as TName;
|
|
212
|
+
const eventTypes = this._eventTypes;
|
|
213
|
+
const middleware = this._middleware;
|
|
214
|
+
const repos = this.repos;
|
|
215
|
+
const db = this._db;
|
|
216
|
+
|
|
217
|
+
debugProjection('[builder] Creating projection definition (name: %s, eventTypes: %o, middlewareCount: %d)',
|
|
218
|
+
name, eventTypes, middleware.length);
|
|
219
|
+
|
|
220
|
+
return {
|
|
221
|
+
name,
|
|
222
|
+
canHandle: eventTypes,
|
|
223
|
+
handle: async (events: { type: string }[]) => {
|
|
224
|
+
const context: ProjectionHandlerContext<TRepos, TRelations> = { repo: repos, db };
|
|
225
|
+
const coreHandler = () => handler(events as TEvent[], context);
|
|
226
|
+
|
|
227
|
+
if (middleware.length > 0) {
|
|
228
|
+
await applyMiddleware(middleware, name, events.length, coreHandler);
|
|
229
|
+
} else {
|
|
230
|
+
await coreHandler();
|
|
231
|
+
}
|
|
232
|
+
},
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
use(middleware: ProjectionMiddleware): ProjectionBuilderRoot<TRepos, TRelations> {
|
|
237
|
+
debugProjection('[builder] Adding projection middleware (currentCount: %d)', this._middleware.length);
|
|
238
|
+
const newBuilder = new ProjectionBuilderImpl<TEvent, TRepos, TRelations, TName>(this.repos, this._db, [
|
|
239
|
+
...this._middleware,
|
|
240
|
+
middleware,
|
|
241
|
+
]);
|
|
242
|
+
debugProjection('[builder] Middleware added (newCount: %d)', this._middleware.length + 1);
|
|
243
|
+
return newBuilder;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
class DbBuilderImpl<TRelations extends AnyRelations, TRepos extends Record<string, unknown>>
|
|
248
|
+
implements DbBuilder<TRelations, TRepos>
|
|
249
|
+
{
|
|
250
|
+
readonly db: NodePgDatabase<TRelations>;
|
|
251
|
+
readonly isDev: boolean;
|
|
252
|
+
private _repos?: TRepos;
|
|
253
|
+
private readonly _repoFactories: Map<string, (db: NodePgDatabase<TRelations>) => unknown>;
|
|
254
|
+
|
|
255
|
+
constructor(
|
|
256
|
+
db: NodePgDatabase<TRelations>,
|
|
257
|
+
isDev: boolean,
|
|
258
|
+
repoFactories?: Map<string, (db: NodePgDatabase<TRelations>) => unknown>,
|
|
259
|
+
) {
|
|
260
|
+
this.db = db;
|
|
261
|
+
this.isDev = isDev;
|
|
262
|
+
this._repoFactories = repoFactories ?? new Map();
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
registerRepository<K extends string, R>(
|
|
266
|
+
key: K,
|
|
267
|
+
factory: (db: NodePgDatabase<TRelations>) => R,
|
|
268
|
+
): DbBuilder<TRelations, TRepos & Record<K, R>> {
|
|
269
|
+
debugProjection('[builder] Registering repository (key: %s, currentCount: %d)', key, this._repoFactories.size);
|
|
270
|
+
const newFactories = new Map(this._repoFactories);
|
|
271
|
+
newFactories.set(key, factory);
|
|
272
|
+
debugProjection('[builder] Repository registered (key: %s, newCount: %d)', key, newFactories.size);
|
|
273
|
+
return new DbBuilderImpl<TRelations, TRepos & Record<K, R>>(this.db, this.isDev, newFactories) as DbBuilder<
|
|
274
|
+
TRelations,
|
|
275
|
+
TRepos & Record<K, R>
|
|
276
|
+
>;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
projectionMiddleware(fn: ProjectionMiddleware): ProjectionMiddleware {
|
|
280
|
+
return fn;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
get projection(): ProjectionBuilderRoot<TRepos, TRelations> {
|
|
284
|
+
if (!this._repos) {
|
|
285
|
+
debugProjection('[builder] Initializing repositories from factories (count: %d)', this._repoFactories.size);
|
|
286
|
+
const repos: Record<string, unknown> = {};
|
|
287
|
+
for (const [key, factory] of this._repoFactories) {
|
|
288
|
+
debugProjection('[builder] Creating repository: %s', key);
|
|
289
|
+
repos[key] = factory(this.db);
|
|
290
|
+
}
|
|
291
|
+
this._repos = repos as TRepos;
|
|
292
|
+
debugProjection('[builder] All repositories initialized');
|
|
293
|
+
}
|
|
294
|
+
debugProjection('[builder] Creating projection builder');
|
|
295
|
+
return new ProjectionBuilderImpl<never, TRepos, TRelations>(this._repos, this.db);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
class SchemaBuilderImpl<TRelations extends AnyRelations> implements SchemaBuilder<TRelations> {
|
|
300
|
+
constructor(private readonly relations: TRelations) {}
|
|
301
|
+
|
|
302
|
+
create(options: {
|
|
303
|
+
connectionString: string;
|
|
304
|
+
isDev?: boolean;
|
|
305
|
+
poolConfig?: Partial<PoolConfig>;
|
|
306
|
+
}): DbBuilder<TRelations, Record<string, never>> {
|
|
307
|
+
debugProjection('[builder] Creating database builder (isDev: %s, hasPoolConfig: %s)',
|
|
308
|
+
options.isDev ?? false, !!options.poolConfig);
|
|
309
|
+
|
|
310
|
+
const poolConfig: PoolConfig = {
|
|
311
|
+
connectionString: options.connectionString,
|
|
312
|
+
...options.poolConfig,
|
|
313
|
+
};
|
|
314
|
+
|
|
315
|
+
debugProjection('[builder] Initializing database with pool config');
|
|
316
|
+
const db = initDbWithOptions<TRelations>(poolConfig, {
|
|
317
|
+
relations: this.relations,
|
|
318
|
+
logger: options.isDev,
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
debugProjection('[builder] Database builder created successfully');
|
|
322
|
+
return new DbBuilderImpl<TRelations, Record<string, never>>(db, options.isDev ?? false);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
class InitialBuilderImpl implements InitialBuilder {
|
|
327
|
+
schema<TSchema extends Record<string, unknown>>(
|
|
328
|
+
schema: TSchema,
|
|
329
|
+
): SchemaBuilder<ReturnType<typeof defineRelations<TSchema>>> {
|
|
330
|
+
debugProjection('[builder] Setting database schema (deriving relations via defineRelations)');
|
|
331
|
+
const relations = defineRelations(schema) as ReturnType<typeof defineRelations<TSchema>>;
|
|
332
|
+
return new SchemaBuilderImpl(relations);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
relations<TRelations extends AnyRelations>(relations: TRelations): SchemaBuilder<TRelations> {
|
|
336
|
+
debugProjection('[builder] Setting database relations (explicit RQBv2)');
|
|
337
|
+
return new SchemaBuilderImpl(relations);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Initialize the database builder.
|
|
343
|
+
*
|
|
344
|
+
* @example
|
|
345
|
+
* ```typescript
|
|
346
|
+
* const d = initDb
|
|
347
|
+
* .schema(schema)
|
|
348
|
+
* .create({
|
|
349
|
+
* connectionString: env.DB_URL,
|
|
350
|
+
* isDev: process.env.NODE_ENV === 'development',
|
|
351
|
+
* });
|
|
352
|
+
*
|
|
353
|
+
* const db = d.db;
|
|
354
|
+
* const projection = d.projection.named('my-proj').on(['EventA']).handle(async (events, ctx) => {
|
|
355
|
+
* // handler
|
|
356
|
+
* });
|
|
357
|
+
* ```
|
|
358
|
+
*/
|
|
359
|
+
export const initDb: InitialBuilder = new InitialBuilderImpl();
|
package/src/bun.ts
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { SQL } from 'bun';
|
|
2
|
+
import type { AnyRelations } from 'drizzle-orm';
|
|
3
|
+
import type { BunSQLDatabase } from 'drizzle-orm/bun-sql/postgres';
|
|
4
|
+
import { drizzle } from 'drizzle-orm/bun-sql/postgres';
|
|
5
|
+
import type { DrizzlePgConfig } from 'drizzle-orm/pg-core/utils';
|
|
6
|
+
|
|
7
|
+
import { debugInit } from './debug.js';
|
|
8
|
+
import { env } from './env.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Initialize database with Bun's native SQL driver.
|
|
12
|
+
* @param connectionString - PostgreSQL connection string.
|
|
13
|
+
* @param config - Drizzle pg configuration.
|
|
14
|
+
*/
|
|
15
|
+
export const initDb = <TRelations extends AnyRelations>(
|
|
16
|
+
connectionString: string,
|
|
17
|
+
config: Omit<DrizzlePgConfig<TRelations>, 'relations'> & { relations: TRelations },
|
|
18
|
+
): BunSQLDatabase<TRelations> => {
|
|
19
|
+
debugInit('[bun] Initializing database with connection string (logger: %s)', !!config.logger);
|
|
20
|
+
|
|
21
|
+
const client = new SQL(connectionString);
|
|
22
|
+
|
|
23
|
+
const db = drizzle<TRelations>({ ...config, client });
|
|
24
|
+
|
|
25
|
+
debugInit('[bun] Database initialized successfully with Bun SQL driver');
|
|
26
|
+
|
|
27
|
+
return db;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Create a database connection using environment configuration with Bun SQL.
|
|
32
|
+
* @param config - Drizzle pg configuration.
|
|
33
|
+
* @throws Error if DB_URL is not configured.
|
|
34
|
+
*/
|
|
35
|
+
export function createDb<TRelations extends AnyRelations>(
|
|
36
|
+
config: Omit<DrizzlePgConfig<TRelations>, 'relations'> & { relations: TRelations },
|
|
37
|
+
): BunSQLDatabase<TRelations> {
|
|
38
|
+
debugInit('[bun] Creating database from environment configuration');
|
|
39
|
+
|
|
40
|
+
const connectionUrl = env.DB_URL;
|
|
41
|
+
if (!connectionUrl) {
|
|
42
|
+
debugInit('[bun] Database creation failed: DB_URL not configured');
|
|
43
|
+
throw new Error('Database URL is not configured. Please set DB_URL environment variable.');
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const db = initDb(connectionUrl, {
|
|
47
|
+
...config,
|
|
48
|
+
logger: config.logger ?? (env.DB_LOGGING || env.NODE_ENV === 'development'),
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
debugInit('[bun] Database created successfully');
|
|
52
|
+
|
|
53
|
+
return db;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export { getDbConnectionOptions, getDbConnectionParams } from './connection-options.js';
|
|
57
|
+
|
|
58
|
+
export type { BunSQLDatabase } from 'drizzle-orm/bun-sql/postgres';
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
# @arki/db/client
|
|
2
|
+
|
|
3
|
+
Reserved client-side surface of `@arki/db`. V1 contains only type-level re-exports
|
|
4
|
+
(zero runtime cost). Future Drizzle-on-client / Tier-3 sync work (Electric / PowerSync /
|
|
5
|
+
Zero adapters) will land here. See [packages/offline/docs/2026-05-16-arki-offline-design.md](../../../offline/docs/2026-05-16-arki-offline-design.md).
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Branded ID type re-exports for the client surface.
|
|
3
|
+
*
|
|
4
|
+
* V1: @arki/contracts does not yet export named branded ID types.
|
|
5
|
+
* Future versions will re-export them here once they are defined, e.g.:
|
|
6
|
+
*
|
|
7
|
+
* export type { UserId, OrgId, AssetId } from '@arki/contracts';
|
|
8
|
+
*
|
|
9
|
+
* See packages/offline/docs/2026-05-16-arki-offline-design.md §3.3.
|
|
10
|
+
*/
|
|
11
|
+
export type {};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @arki/db/client — types-only client surface.
|
|
3
|
+
*
|
|
4
|
+
* V1 is intentionally empty: this subpath claims the namespace for future
|
|
5
|
+
* Drizzle-on-client / Tier-3 sync work (Electric / PowerSync / Zero adapters).
|
|
6
|
+
* No runtime imports are allowed here — this file must remain free of any
|
|
7
|
+
* server-only dependencies (postgres, drizzle-orm connection utilities, etc.).
|
|
8
|
+
*
|
|
9
|
+
* See packages/offline/docs/2026-05-16-arki-offline-design.md §3.3.
|
|
10
|
+
*/
|
|
11
|
+
export type * from './ids.js';
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { debugFactory } from './debug.js';
|
|
2
|
+
import { env } from './env.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Get database connection options from environment variables
|
|
6
|
+
* @returns Connection options object for pg Pool
|
|
7
|
+
*/
|
|
8
|
+
export function getDbConnectionOptions() {
|
|
9
|
+
debugFactory('[factory] Retrieving database connection options from environment');
|
|
10
|
+
|
|
11
|
+
const connectionUrl = env.DB_URL;
|
|
12
|
+
const options = {
|
|
13
|
+
connectionString: connectionUrl,
|
|
14
|
+
min: env.DB_POOL_MIN,
|
|
15
|
+
max: env.DB_POOL_MAX,
|
|
16
|
+
idleTimeoutMillis: env.DB_POOL_IDLE_TIMEOUT,
|
|
17
|
+
connectionTimeoutMillis: env.DB_POOL_CONNECTION_TIMEOUT,
|
|
18
|
+
statement_timeout: env.DB_STATEMENT_TIMEOUT,
|
|
19
|
+
query_timeout: env.DB_QUERY_TIMEOUT,
|
|
20
|
+
idle_in_transaction_session_timeout: env.DB_IDLE_IN_TRANSACTION_SESSION_TIMEOUT,
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
debugFactory('[factory] Connection options retrieved (hasUrl: %s)', !!connectionUrl);
|
|
24
|
+
|
|
25
|
+
return options;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Get individual database connection parameters from environment variables
|
|
30
|
+
* Useful when DB_URL is not available but individual parameters are set
|
|
31
|
+
* @returns Connection parameters object
|
|
32
|
+
*/
|
|
33
|
+
export function getDbConnectionParams() {
|
|
34
|
+
debugFactory('[factory] Retrieving database connection parameters from environment');
|
|
35
|
+
|
|
36
|
+
const params = {
|
|
37
|
+
user: env.PGUSER,
|
|
38
|
+
password: env.PGPASSWORD,
|
|
39
|
+
host: env.PGHOST,
|
|
40
|
+
port: env.PGPORT,
|
|
41
|
+
database: env.PGDATABASE,
|
|
42
|
+
ssl: env.PGSSLMODE === 'disable' ? false : { rejectUnauthorized: env.PGSSLMODE === 'verify-full' },
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
debugFactory('[factory] Connection parameters retrieved (host: %s, port: %d, database: %s, ssl: %s)',
|
|
46
|
+
params.host, params.port, params.database, env.PGSSLMODE);
|
|
47
|
+
|
|
48
|
+
return params;
|
|
49
|
+
}
|
package/src/debug.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { createDebugLogger } from '@arki/log/debug';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Debug logger for database initialization and connection operations
|
|
5
|
+
* Enable with: DEBUG=db:init
|
|
6
|
+
*/
|
|
7
|
+
export const debugInit = createDebugLogger('db:init');
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Debug logger for database factory operations
|
|
11
|
+
* Enable with: DEBUG=db:factory
|
|
12
|
+
*/
|
|
13
|
+
export const debugFactory = createDebugLogger('db:factory');
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Debug logger for projection builder operations
|
|
17
|
+
* Enable with: DEBUG=db:projection
|
|
18
|
+
*/
|
|
19
|
+
export const debugProjection = createDebugLogger('db:projection');
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Enable all database debug logs
|
|
23
|
+
* Enable with: DEBUG=db:*
|
|
24
|
+
*/
|