@parsrun/database 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,651 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropNames = Object.getOwnPropertyNames;
3
+ var __esm = (fn, res) => function __init() {
4
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
5
+ };
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+
11
+ // src/types.ts
12
+ var DatabaseError, DatabaseErrorCodes;
13
+ var init_types = __esm({
14
+ "src/types.ts"() {
15
+ "use strict";
16
+ DatabaseError = class extends Error {
17
+ constructor(message, code, cause) {
18
+ super(message);
19
+ this.code = code;
20
+ this.cause = cause;
21
+ this.name = "DatabaseError";
22
+ }
23
+ };
24
+ DatabaseErrorCodes = {
25
+ CONNECTION_FAILED: "CONNECTION_FAILED",
26
+ QUERY_FAILED: "QUERY_FAILED",
27
+ TRANSACTION_FAILED: "TRANSACTION_FAILED",
28
+ MIGRATION_FAILED: "MIGRATION_FAILED",
29
+ INVALID_CONFIG: "INVALID_CONFIG",
30
+ ADAPTER_NOT_AVAILABLE: "ADAPTER_NOT_AVAILABLE",
31
+ TIMEOUT: "TIMEOUT",
32
+ CONSTRAINT_VIOLATION: "CONSTRAINT_VIOLATION",
33
+ NOT_FOUND: "NOT_FOUND"
34
+ };
35
+ }
36
+ });
37
+
38
+ // src/adapters/postgres.ts
39
+ var postgres_exports = {};
40
+ __export(postgres_exports, {
41
+ PostgresAdapter: () => PostgresAdapter,
42
+ createPostgresAdapter: () => createPostgresAdapter,
43
+ createPostgresFromUrl: () => createPostgresFromUrl
44
+ });
45
+ import { drizzle } from "drizzle-orm/postgres-js";
46
+ async function createPostgresAdapter(config) {
47
+ const adapter = new PostgresAdapter(config);
48
+ await adapter.connect();
49
+ return adapter;
50
+ }
51
+ async function createPostgresFromUrl(connectionString, options) {
52
+ const url = new URL(connectionString);
53
+ const config = {
54
+ type: "postgres",
55
+ host: url.hostname,
56
+ port: parseInt(url.port || "5432", 10),
57
+ user: url.username,
58
+ password: url.password,
59
+ database: url.pathname.slice(1),
60
+ ssl: url.searchParams.get("sslmode") === "require",
61
+ ...options
62
+ };
63
+ return createPostgresAdapter(config);
64
+ }
65
+ var PostgresAdapter;
66
+ var init_postgres = __esm({
67
+ "src/adapters/postgres.ts"() {
68
+ "use strict";
69
+ init_types();
70
+ PostgresAdapter = class {
71
+ type = "postgres";
72
+ client = null;
73
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
74
+ db = null;
75
+ config;
76
+ constructor(config) {
77
+ this.config = config;
78
+ }
79
+ /**
80
+ * Initialize the connection
81
+ */
82
+ async connect() {
83
+ if (this.client) return;
84
+ try {
85
+ const postgresModule = await import("postgres");
86
+ const postgres = postgresModule.default;
87
+ let sslConfig = false;
88
+ if (this.config.ssl === true) {
89
+ sslConfig = "require";
90
+ } else if (typeof this.config.ssl === "object") {
91
+ sslConfig = this.config.ssl;
92
+ }
93
+ this.client = postgres({
94
+ host: this.config.host,
95
+ port: this.config.port,
96
+ user: this.config.user,
97
+ password: this.config.password,
98
+ database: this.config.database,
99
+ ssl: sslConfig,
100
+ max: this.config.poolSize ?? 10
101
+ });
102
+ const drizzleOpts = {};
103
+ if (this.config.logging !== void 0) {
104
+ drizzleOpts.logger = this.config.logging;
105
+ }
106
+ this.db = drizzle(this.client, drizzleOpts);
107
+ } catch (err) {
108
+ throw new DatabaseError(
109
+ `Failed to connect to PostgreSQL: ${err instanceof Error ? err.message : "Unknown error"}`,
110
+ DatabaseErrorCodes.CONNECTION_FAILED,
111
+ err
112
+ );
113
+ }
114
+ }
115
+ async execute(sql) {
116
+ if (!this.client) {
117
+ await this.connect();
118
+ }
119
+ try {
120
+ const client = this.client;
121
+ const result = await client.unsafe(sql);
122
+ return result;
123
+ } catch (err) {
124
+ throw new DatabaseError(
125
+ `Query failed: ${err instanceof Error ? err.message : "Unknown error"}`,
126
+ DatabaseErrorCodes.QUERY_FAILED,
127
+ err
128
+ );
129
+ }
130
+ }
131
+ async ping() {
132
+ try {
133
+ await this.execute("SELECT 1");
134
+ return true;
135
+ } catch {
136
+ return false;
137
+ }
138
+ }
139
+ async health() {
140
+ const start = Date.now();
141
+ try {
142
+ if (!this.client) {
143
+ await this.connect();
144
+ }
145
+ const result = await this.execute("SELECT version()");
146
+ const latencyMs = Date.now() - start;
147
+ return {
148
+ healthy: true,
149
+ latencyMs,
150
+ version: result[0]?.version
151
+ };
152
+ } catch (err) {
153
+ return {
154
+ healthy: false,
155
+ latencyMs: Date.now() - start,
156
+ error: err instanceof Error ? err.message : "Unknown error"
157
+ };
158
+ }
159
+ }
160
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
161
+ drizzle() {
162
+ if (!this.db) {
163
+ throw new DatabaseError(
164
+ "Database not connected. Call connect() first.",
165
+ DatabaseErrorCodes.CONNECTION_FAILED
166
+ );
167
+ }
168
+ return this.db;
169
+ }
170
+ async close() {
171
+ if (this.client) {
172
+ const client = this.client;
173
+ await client.end();
174
+ this.client = null;
175
+ this.db = null;
176
+ }
177
+ }
178
+ /**
179
+ * Get the raw postgres client
180
+ */
181
+ getClient() {
182
+ return this.client;
183
+ }
184
+ };
185
+ }
186
+ });
187
+
188
+ // src/adapters/neon.ts
189
+ var neon_exports = {};
190
+ __export(neon_exports, {
191
+ NeonAdapter: () => NeonAdapter,
192
+ createNeonAdapter: () => createNeonAdapter,
193
+ createNeonFromUrl: () => createNeonFromUrl
194
+ });
195
+ import { drizzle as drizzle2 } from "drizzle-orm/neon-http";
196
+ async function createNeonAdapter(config) {
197
+ const adapter = new NeonAdapter(config);
198
+ await adapter.connect();
199
+ return adapter;
200
+ }
201
+ async function createNeonFromUrl(connectionString, options) {
202
+ return createNeonAdapter({
203
+ type: "neon",
204
+ connectionString,
205
+ ...options
206
+ });
207
+ }
208
+ var NeonAdapter;
209
+ var init_neon = __esm({
210
+ "src/adapters/neon.ts"() {
211
+ "use strict";
212
+ init_types();
213
+ NeonAdapter = class {
214
+ type = "neon";
215
+ client = null;
216
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
217
+ db = null;
218
+ config;
219
+ constructor(config) {
220
+ this.config = config;
221
+ }
222
+ /**
223
+ * Initialize the connection
224
+ */
225
+ async connect() {
226
+ if (this.client) return;
227
+ try {
228
+ const { neon } = await import("@neondatabase/serverless");
229
+ this.client = neon(this.config.connectionString, {
230
+ fetchOptions: {
231
+ cache: "no-store"
232
+ }
233
+ });
234
+ const drizzleOpts = {};
235
+ if (this.config.logging !== void 0) {
236
+ drizzleOpts.logger = this.config.logging;
237
+ }
238
+ this.db = drizzle2(this.client, drizzleOpts);
239
+ } catch (err) {
240
+ throw new DatabaseError(
241
+ `Failed to connect to Neon: ${err instanceof Error ? err.message : "Unknown error"}`,
242
+ DatabaseErrorCodes.CONNECTION_FAILED,
243
+ err
244
+ );
245
+ }
246
+ }
247
+ async execute(sql) {
248
+ if (!this.client) {
249
+ await this.connect();
250
+ }
251
+ try {
252
+ const result = await this.client(sql);
253
+ if (Array.isArray(result) && result.rows) {
254
+ return result.rows;
255
+ }
256
+ return result;
257
+ } catch (err) {
258
+ throw new DatabaseError(
259
+ `Query failed: ${err instanceof Error ? err.message : "Unknown error"}`,
260
+ DatabaseErrorCodes.QUERY_FAILED,
261
+ err
262
+ );
263
+ }
264
+ }
265
+ async ping() {
266
+ try {
267
+ await this.execute("SELECT 1");
268
+ return true;
269
+ } catch {
270
+ return false;
271
+ }
272
+ }
273
+ async health() {
274
+ const start = Date.now();
275
+ try {
276
+ if (!this.client) {
277
+ await this.connect();
278
+ }
279
+ const result = await this.execute("SELECT version()");
280
+ const latencyMs = Date.now() - start;
281
+ return {
282
+ healthy: true,
283
+ latencyMs,
284
+ version: result[0]?.version
285
+ };
286
+ } catch (err) {
287
+ return {
288
+ healthy: false,
289
+ latencyMs: Date.now() - start,
290
+ error: err instanceof Error ? err.message : "Unknown error"
291
+ };
292
+ }
293
+ }
294
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
295
+ drizzle() {
296
+ if (!this.db) {
297
+ throw new DatabaseError(
298
+ "Database not connected. Call connect() first.",
299
+ DatabaseErrorCodes.CONNECTION_FAILED
300
+ );
301
+ }
302
+ return this.db;
303
+ }
304
+ async close() {
305
+ this.client = null;
306
+ this.db = null;
307
+ }
308
+ /**
309
+ * Get the raw Neon client
310
+ */
311
+ getClient() {
312
+ return this.client;
313
+ }
314
+ };
315
+ }
316
+ });
317
+
318
+ // src/adapters/d1.ts
319
+ var d1_exports = {};
320
+ __export(d1_exports, {
321
+ D1Adapter: () => D1Adapter,
322
+ createD1Adapter: () => createD1Adapter
323
+ });
324
+ import { drizzle as drizzle3 } from "drizzle-orm/d1";
325
+ function createD1Adapter(config) {
326
+ return new D1Adapter(config);
327
+ }
328
+ var D1Adapter;
329
+ var init_d1 = __esm({
330
+ "src/adapters/d1.ts"() {
331
+ "use strict";
332
+ init_types();
333
+ D1Adapter = class {
334
+ type = "d1";
335
+ binding;
336
+ db;
337
+ constructor(config) {
338
+ this.binding = config.binding;
339
+ const drizzleConfig = {};
340
+ if (config.logging !== void 0) {
341
+ drizzleConfig.logger = config.logging;
342
+ }
343
+ this.db = drizzle3(this.binding, drizzleConfig);
344
+ }
345
+ async execute(sql) {
346
+ try {
347
+ const result = await this.binding.exec(sql);
348
+ return result.results ?? [];
349
+ } catch (err) {
350
+ throw new DatabaseError(
351
+ `Query failed: ${err instanceof Error ? err.message : "Unknown error"}`,
352
+ DatabaseErrorCodes.QUERY_FAILED,
353
+ err
354
+ );
355
+ }
356
+ }
357
+ async ping() {
358
+ try {
359
+ await this.binding.prepare("SELECT 1").first();
360
+ return true;
361
+ } catch {
362
+ return false;
363
+ }
364
+ }
365
+ async health() {
366
+ const start = Date.now();
367
+ try {
368
+ await this.binding.prepare("SELECT 1").first();
369
+ const latencyMs = Date.now() - start;
370
+ const versionResult = await this.binding.prepare("SELECT sqlite_version() as version").first();
371
+ return {
372
+ healthy: true,
373
+ latencyMs,
374
+ version: versionResult ? `SQLite ${versionResult.version}` : void 0
375
+ };
376
+ } catch (err) {
377
+ return {
378
+ healthy: false,
379
+ latencyMs: Date.now() - start,
380
+ error: err instanceof Error ? err.message : "Unknown error"
381
+ };
382
+ }
383
+ }
384
+ drizzle() {
385
+ return this.db;
386
+ }
387
+ async close() {
388
+ }
389
+ /**
390
+ * Get the raw D1 binding
391
+ */
392
+ getBinding() {
393
+ return this.binding;
394
+ }
395
+ /**
396
+ * Execute a batch of statements
397
+ */
398
+ async batch(statements) {
399
+ try {
400
+ const prepared = statements.map((stmt) => {
401
+ const p = this.binding.prepare(stmt.sql);
402
+ return stmt.params ? p.bind(...stmt.params) : p;
403
+ });
404
+ const results = await this.binding.batch(prepared);
405
+ return results.map((r) => r.results ?? []);
406
+ } catch (err) {
407
+ throw new DatabaseError(
408
+ `Batch execution failed: ${err instanceof Error ? err.message : "Unknown error"}`,
409
+ DatabaseErrorCodes.QUERY_FAILED,
410
+ err
411
+ );
412
+ }
413
+ }
414
+ };
415
+ }
416
+ });
417
+
418
+ // src/index.ts
419
+ init_types();
420
+ init_postgres();
421
+ init_neon();
422
+ init_d1();
423
+
424
+ // src/utils.ts
425
+ function sleep(ms) {
426
+ return new Promise((resolve) => setTimeout(resolve, ms));
427
+ }
428
+ async function retry(operation, options = {}) {
429
+ const {
430
+ maxAttempts = 3,
431
+ baseDelay = 100,
432
+ maxDelay = 5e3,
433
+ shouldRetry = isRetryableError
434
+ } = options;
435
+ let lastError;
436
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
437
+ try {
438
+ return await operation();
439
+ } catch (error) {
440
+ lastError = error;
441
+ if (attempt === maxAttempts || !shouldRetry(error)) {
442
+ throw error;
443
+ }
444
+ const delay = Math.min(baseDelay * Math.pow(2, attempt - 1), maxDelay);
445
+ await sleep(delay);
446
+ }
447
+ }
448
+ throw lastError;
449
+ }
450
+ function isRetryableError(error) {
451
+ if (error instanceof Error) {
452
+ const message = error.message.toLowerCase();
453
+ if (message.includes("connection") || message.includes("econnrefused") || message.includes("econnreset") || message.includes("etimedout")) {
454
+ return true;
455
+ }
456
+ if (message.includes("could not serialize") || message.includes("deadlock detected")) {
457
+ return true;
458
+ }
459
+ if (message.includes("too many clients")) {
460
+ return true;
461
+ }
462
+ }
463
+ return false;
464
+ }
465
+ function parseConnectionString(connectionString) {
466
+ const url = new URL(connectionString);
467
+ const params = {};
468
+ url.searchParams.forEach((value, key) => {
469
+ params[key] = value;
470
+ });
471
+ return {
472
+ host: url.hostname,
473
+ port: parseInt(url.port || "5432", 10),
474
+ user: decodeURIComponent(url.username),
475
+ password: decodeURIComponent(url.password),
476
+ database: url.pathname.slice(1),
477
+ ssl: url.searchParams.get("sslmode") === "require" || url.searchParams.get("ssl") === "true",
478
+ params
479
+ };
480
+ }
481
+ function buildConnectionString(config) {
482
+ const url = new URL(`postgresql://${config.host}`);
483
+ url.port = String(config.port ?? 5432);
484
+ url.username = encodeURIComponent(config.user);
485
+ url.password = encodeURIComponent(config.password);
486
+ url.pathname = `/${config.database}`;
487
+ if (config.ssl) {
488
+ url.searchParams.set("sslmode", "require");
489
+ }
490
+ if (config.params) {
491
+ for (const [key, value] of Object.entries(config.params)) {
492
+ url.searchParams.set(key, value);
493
+ }
494
+ }
495
+ return url.toString();
496
+ }
497
+ function snakeToCamel(str) {
498
+ return str.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
499
+ }
500
+ function camelToSnake(str) {
501
+ return str.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`);
502
+ }
503
+ function transformToCamelCase(obj) {
504
+ const result = {};
505
+ for (const [key, value] of Object.entries(obj)) {
506
+ const newKey = snakeToCamel(key);
507
+ if (value && typeof value === "object" && !Array.isArray(value)) {
508
+ result[newKey] = transformToCamelCase(value);
509
+ } else if (Array.isArray(value)) {
510
+ result[newKey] = value.map(
511
+ (item) => item && typeof item === "object" ? transformToCamelCase(item) : item
512
+ );
513
+ } else {
514
+ result[newKey] = value;
515
+ }
516
+ }
517
+ return result;
518
+ }
519
+ function transformToSnakeCase(obj) {
520
+ const result = {};
521
+ for (const [key, value] of Object.entries(obj)) {
522
+ const newKey = camelToSnake(key);
523
+ if (value && typeof value === "object" && !Array.isArray(value)) {
524
+ result[newKey] = transformToSnakeCase(value);
525
+ } else if (Array.isArray(value)) {
526
+ result[newKey] = value.map(
527
+ (item) => item && typeof item === "object" ? transformToSnakeCase(item) : item
528
+ );
529
+ } else {
530
+ result[newKey] = value;
531
+ }
532
+ }
533
+ return result;
534
+ }
535
+ function generateUUID() {
536
+ return crypto.randomUUID();
537
+ }
538
+ function generateShortId() {
539
+ return crypto.randomUUID().slice(0, 8);
540
+ }
541
+ function getPaginationOffset(options) {
542
+ const page = Math.max(1, options.page ?? 1);
543
+ const maxLimit = options.maxLimit ?? 100;
544
+ const limit = Math.min(Math.max(1, options.limit ?? 20), maxLimit);
545
+ const offset = (page - 1) * limit;
546
+ return { offset, limit };
547
+ }
548
+ function createPaginatedResult(data, total, options) {
549
+ const { offset, limit } = getPaginationOffset(options);
550
+ const page = Math.floor(offset / limit) + 1;
551
+ const totalPages = Math.ceil(total / limit);
552
+ return {
553
+ data,
554
+ pagination: {
555
+ page,
556
+ limit,
557
+ total,
558
+ totalPages,
559
+ hasMore: page < totalPages
560
+ }
561
+ };
562
+ }
563
+ function escapeLike(value) {
564
+ return value.replace(/[%_\\]/g, "\\$&");
565
+ }
566
+ function buildSearchPattern(value, mode = "contains") {
567
+ const escaped = escapeLike(value);
568
+ switch (mode) {
569
+ case "startsWith":
570
+ return `${escaped}%`;
571
+ case "endsWith":
572
+ return `%${escaped}`;
573
+ case "contains":
574
+ default:
575
+ return `%${escaped}%`;
576
+ }
577
+ }
578
+
579
+ // src/index.ts
580
+ init_types();
581
+ export * from "drizzle-orm";
582
+ async function createDatabase(config) {
583
+ switch (config.type) {
584
+ case "postgres": {
585
+ const { createPostgresAdapter: createPostgresAdapter2 } = await Promise.resolve().then(() => (init_postgres(), postgres_exports));
586
+ return createPostgresAdapter2(config);
587
+ }
588
+ case "neon": {
589
+ const { createNeonAdapter: createNeonAdapter2 } = await Promise.resolve().then(() => (init_neon(), neon_exports));
590
+ return createNeonAdapter2(config);
591
+ }
592
+ case "d1": {
593
+ const { createD1Adapter: createD1Adapter2 } = await Promise.resolve().then(() => (init_d1(), d1_exports));
594
+ return createD1Adapter2(config);
595
+ }
596
+ default:
597
+ throw new DatabaseError(
598
+ `Unknown database type: ${config.type}`,
599
+ DatabaseErrorCodes.INVALID_CONFIG
600
+ );
601
+ }
602
+ }
603
+ async function createDatabaseFromUrl(connectionString, options) {
604
+ const url = new URL(connectionString);
605
+ switch (url.protocol) {
606
+ case "postgresql:":
607
+ case "postgres:": {
608
+ if (url.hostname.includes("neon.tech")) {
609
+ const { createNeonFromUrl: createNeonFromUrl2 } = await Promise.resolve().then(() => (init_neon(), neon_exports));
610
+ return createNeonFromUrl2(connectionString, options);
611
+ }
612
+ const { createPostgresFromUrl: createPostgresFromUrl2 } = await Promise.resolve().then(() => (init_postgres(), postgres_exports));
613
+ return createPostgresFromUrl2(connectionString, options);
614
+ }
615
+ default:
616
+ throw new DatabaseError(
617
+ `Unsupported database URL scheme: ${url.protocol}`,
618
+ DatabaseErrorCodes.INVALID_CONFIG
619
+ );
620
+ }
621
+ }
622
+ export {
623
+ D1Adapter,
624
+ DatabaseError,
625
+ DatabaseErrorCodes,
626
+ NeonAdapter,
627
+ PostgresAdapter,
628
+ buildConnectionString,
629
+ buildSearchPattern,
630
+ camelToSnake,
631
+ createD1Adapter,
632
+ createDatabase,
633
+ createDatabaseFromUrl,
634
+ createNeonAdapter,
635
+ createNeonFromUrl,
636
+ createPaginatedResult,
637
+ createPostgresAdapter,
638
+ createPostgresFromUrl,
639
+ escapeLike,
640
+ generateShortId,
641
+ generateUUID,
642
+ getPaginationOffset,
643
+ isRetryableError,
644
+ parseConnectionString,
645
+ retry,
646
+ sleep,
647
+ snakeToCamel,
648
+ transformToCamelCase,
649
+ transformToSnakeCase
650
+ };
651
+ //# sourceMappingURL=index.js.map