@quilla-be-kit/messaging 0.2.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.
Files changed (88) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +620 -0
  3. package/dist/.tsbuildinfo +1 -0
  4. package/dist/event-bus/defaults.d.ts +5 -0
  5. package/dist/event-bus/defaults.d.ts.map +1 -0
  6. package/dist/event-bus/defaults.js +5 -0
  7. package/dist/event-bus/defaults.js.map +1 -0
  8. package/dist/event-bus/event-bus-consumer.interface.d.ts +8 -0
  9. package/dist/event-bus/event-bus-consumer.interface.d.ts.map +1 -0
  10. package/dist/event-bus/event-bus-consumer.interface.js +2 -0
  11. package/dist/event-bus/event-bus-consumer.interface.js.map +1 -0
  12. package/dist/event-bus/event-bus-entry.type.d.ts +19 -0
  13. package/dist/event-bus/event-bus-entry.type.d.ts.map +1 -0
  14. package/dist/event-bus/event-bus-entry.type.js +2 -0
  15. package/dist/event-bus/event-bus-entry.type.js.map +1 -0
  16. package/dist/event-bus/event-bus-publisher.interface.d.ts +14 -0
  17. package/dist/event-bus/event-bus-publisher.interface.d.ts.map +1 -0
  18. package/dist/event-bus/event-bus-publisher.interface.js +2 -0
  19. package/dist/event-bus/event-bus-publisher.interface.js.map +1 -0
  20. package/dist/event-bus/event-subscription.interface.d.ts +7 -0
  21. package/dist/event-bus/event-subscription.interface.d.ts.map +1 -0
  22. package/dist/event-bus/event-subscription.interface.js +2 -0
  23. package/dist/event-bus/event-subscription.interface.js.map +1 -0
  24. package/dist/event-bus/event.consumer.d.ts +66 -0
  25. package/dist/event-bus/event.consumer.d.ts.map +1 -0
  26. package/dist/event-bus/event.consumer.js +251 -0
  27. package/dist/event-bus/event.consumer.js.map +1 -0
  28. package/dist/event-bus/event.descriptor.d.ts +9 -0
  29. package/dist/event-bus/event.descriptor.d.ts.map +1 -0
  30. package/dist/event-bus/event.descriptor.js +7 -0
  31. package/dist/event-bus/event.descriptor.js.map +1 -0
  32. package/dist/event-bus/handler-entry.type.d.ts +8 -0
  33. package/dist/event-bus/handler-entry.type.d.ts.map +1 -0
  34. package/dist/event-bus/handler-entry.type.js +2 -0
  35. package/dist/event-bus/handler-entry.type.js.map +1 -0
  36. package/dist/event-bus/index.d.ts +10 -0
  37. package/dist/event-bus/index.d.ts.map +1 -0
  38. package/dist/event-bus/index.js +4 -0
  39. package/dist/event-bus/index.js.map +1 -0
  40. package/dist/event-bus/standard-schema.type.d.ts +33 -0
  41. package/dist/event-bus/standard-schema.type.d.ts.map +1 -0
  42. package/dist/event-bus/standard-schema.type.js +2 -0
  43. package/dist/event-bus/standard-schema.type.js.map +1 -0
  44. package/dist/index.d.ts +3 -0
  45. package/dist/index.d.ts.map +1 -0
  46. package/dist/index.js +3 -0
  47. package/dist/index.js.map +1 -0
  48. package/dist/local-outbox/index.d.ts +6 -0
  49. package/dist/local-outbox/index.d.ts.map +1 -0
  50. package/dist/local-outbox/index.js +2 -0
  51. package/dist/local-outbox/index.js.map +1 -0
  52. package/dist/local-outbox/local-outbox-entry.type.d.ts +28 -0
  53. package/dist/local-outbox/local-outbox-entry.type.d.ts.map +1 -0
  54. package/dist/local-outbox/local-outbox-entry.type.js +2 -0
  55. package/dist/local-outbox/local-outbox-entry.type.js.map +1 -0
  56. package/dist/local-outbox/local-outbox-reader.interface.d.ts +8 -0
  57. package/dist/local-outbox/local-outbox-reader.interface.d.ts.map +1 -0
  58. package/dist/local-outbox/local-outbox-reader.interface.js +2 -0
  59. package/dist/local-outbox/local-outbox-reader.interface.js.map +1 -0
  60. package/dist/local-outbox/local-outbox-writer.interface.d.ts +6 -0
  61. package/dist/local-outbox/local-outbox-writer.interface.d.ts.map +1 -0
  62. package/dist/local-outbox/local-outbox-writer.interface.js +2 -0
  63. package/dist/local-outbox/local-outbox-writer.interface.js.map +1 -0
  64. package/dist/local-outbox/outbox.forwarder.d.ts +34 -0
  65. package/dist/local-outbox/outbox.forwarder.d.ts.map +1 -0
  66. package/dist/local-outbox/outbox.forwarder.js +109 -0
  67. package/dist/local-outbox/outbox.forwarder.js.map +1 -0
  68. package/dist/local-outbox/transaction-handle.interface.d.ts +6 -0
  69. package/dist/local-outbox/transaction-handle.interface.d.ts.map +1 -0
  70. package/dist/local-outbox/transaction-handle.interface.js +2 -0
  71. package/dist/local-outbox/transaction-handle.interface.js.map +1 -0
  72. package/dist/postgres/get-postgres-schema.d.ts +6 -0
  73. package/dist/postgres/get-postgres-schema.d.ts.map +1 -0
  74. package/dist/postgres/get-postgres-schema.js +56 -0
  75. package/dist/postgres/get-postgres-schema.js.map +1 -0
  76. package/dist/postgres/index.d.ts +4 -0
  77. package/dist/postgres/index.d.ts.map +1 -0
  78. package/dist/postgres/index.js +4 -0
  79. package/dist/postgres/index.js.map +1 -0
  80. package/dist/postgres/pg-event-bus.d.ts +25 -0
  81. package/dist/postgres/pg-event-bus.d.ts.map +1 -0
  82. package/dist/postgres/pg-event-bus.js +115 -0
  83. package/dist/postgres/pg-event-bus.js.map +1 -0
  84. package/dist/postgres/pg-local-outbox.d.ts +26 -0
  85. package/dist/postgres/pg-local-outbox.d.ts.map +1 -0
  86. package/dist/postgres/pg-local-outbox.js +102 -0
  87. package/dist/postgres/pg-local-outbox.js.map +1 -0
  88. package/package.json +73 -0
@@ -0,0 +1,115 @@
1
+ const DEFAULT_EVENTS_TABLE = 'events';
2
+ const DEFAULT_MAX_RETRIES = 3;
3
+ export class PgEventBus {
4
+ pool;
5
+ eventsTable;
6
+ maxRetries;
7
+ constructor(options) {
8
+ this.pool = options.pool;
9
+ this.eventsTable = options.eventsTableName ?? DEFAULT_EVENTS_TABLE;
10
+ this.maxRetries = options.maxRetries ?? DEFAULT_MAX_RETRIES;
11
+ }
12
+ async publish(event) {
13
+ const publishedAt = new Date();
14
+ await this.pool.query(`INSERT INTO ${this.eventsTable}
15
+ (id, event_type, event_version, event_kind, payload, source_service,
16
+ aggregate_id, correlation_id, status, claimed_by, claimed_at,
17
+ retry_count, last_error, created_at, published_at)
18
+ VALUES ($1, $2, $3, $4, $5::jsonb, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15)`, [
19
+ event.id,
20
+ event.eventType,
21
+ event.eventVersion,
22
+ event.eventKind,
23
+ JSON.stringify(event.payload),
24
+ event.sourceService,
25
+ event.aggregateId ?? null,
26
+ event.correlationId ?? null,
27
+ 'PENDING',
28
+ null,
29
+ null,
30
+ 0,
31
+ null,
32
+ event.createdAt,
33
+ publishedAt,
34
+ ]);
35
+ }
36
+ // NOT EXISTS guards against claiming a second event for an aggregate while
37
+ // an earlier one is still CLAIMED. Advisory xact lock closes the race
38
+ // between NOT EXISTS and UPDATE across overlapping transactions.
39
+ async claim(instanceId, batchSize) {
40
+ const result = await this.pool.query(`WITH claimed AS (
41
+ SELECT id FROM ${this.eventsTable} e
42
+ WHERE e.status = 'PENDING'
43
+ AND NOT EXISTS (
44
+ SELECT 1 FROM ${this.eventsTable} e2
45
+ WHERE e2.aggregate_id IS NOT NULL
46
+ AND e2.aggregate_id = e.aggregate_id
47
+ AND e2.status = 'CLAIMED'
48
+ )
49
+ AND (e.aggregate_id IS NULL
50
+ OR pg_try_advisory_xact_lock(hashtext(e.aggregate_id)::bigint))
51
+ ORDER BY e.created_at
52
+ LIMIT $1
53
+ FOR UPDATE SKIP LOCKED
54
+ )
55
+ UPDATE ${this.eventsTable} t
56
+ SET status = 'CLAIMED', claimed_by = $2, claimed_at = $3
57
+ FROM claimed c
58
+ WHERE t.id = c.id
59
+ RETURNING t.*`, [batchSize, instanceId, new Date()]);
60
+ return result.rows.map((r) => this.rowToEntry(r));
61
+ }
62
+ async markDone(id) {
63
+ await this.pool.query(`DELETE FROM ${this.eventsTable} WHERE id = $1`, [id]);
64
+ }
65
+ async markFailed(id, reason) {
66
+ await this.pool.query(`UPDATE ${this.eventsTable}
67
+ SET retry_count = retry_count + 1,
68
+ last_error = $1,
69
+ status = CASE WHEN retry_count + 1 >= $3 THEN 'FAILED' ELSE 'PENDING' END,
70
+ claimed_by = NULL,
71
+ claimed_at = NULL
72
+ WHERE id = $2`, [reason, id, this.maxRetries]);
73
+ }
74
+ async resetStale(olderThan) {
75
+ const result = await this.pool.query(`UPDATE ${this.eventsTable}
76
+ SET status = 'PENDING', claimed_by = NULL, claimed_at = NULL
77
+ WHERE status = 'CLAIMED' AND claimed_at < $1`, [olderThan]);
78
+ return result.rowCount ?? 0;
79
+ }
80
+ async cleanupFailed(olderThan, options) {
81
+ const limit = options?.limit;
82
+ const result = limit
83
+ ? await this.pool.query(`WITH cte AS (
84
+ SELECT id FROM ${this.eventsTable}
85
+ WHERE status = 'FAILED' AND created_at < $1
86
+ ORDER BY created_at ASC
87
+ LIMIT $2
88
+ )
89
+ DELETE FROM ${this.eventsTable} t
90
+ USING cte
91
+ WHERE t.id = cte.id`, [olderThan, limit])
92
+ : await this.pool.query(`DELETE FROM ${this.eventsTable} WHERE status = 'FAILED' AND created_at < $1`, [olderThan]);
93
+ return result.rowCount ?? 0;
94
+ }
95
+ rowToEntry(row) {
96
+ return {
97
+ id: row.id,
98
+ eventType: row.event_type,
99
+ eventVersion: row.event_version,
100
+ eventKind: row.event_kind,
101
+ payload: row.payload,
102
+ sourceService: row.source_service,
103
+ ...(row.aggregate_id !== null ? { aggregateId: row.aggregate_id } : {}),
104
+ ...(row.correlation_id !== null ? { correlationId: row.correlation_id } : {}),
105
+ status: row.status,
106
+ ...(row.claimed_by !== null ? { claimedBy: row.claimed_by } : {}),
107
+ ...(row.claimed_at !== null ? { claimedAt: row.claimed_at } : {}),
108
+ retryCount: row.retry_count,
109
+ ...(row.last_error !== null ? { lastError: row.last_error } : {}),
110
+ createdAt: row.created_at,
111
+ publishedAt: row.published_at,
112
+ };
113
+ }
114
+ }
115
+ //# sourceMappingURL=pg-event-bus.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pg-event-bus.js","sourceRoot":"","sources":["../../src/postgres/pg-event-bus.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ import type { Pool } from 'pg';
2
+ import type { LocalOutboxEntry, LocalOutboxInsertInput } from '../local-outbox/local-outbox-entry.type.js';
3
+ import type { LocalOutboxReader } from '../local-outbox/local-outbox-reader.interface.js';
4
+ import type { LocalOutboxWriter } from '../local-outbox/local-outbox-writer.interface.js';
5
+ import type { TransactionHandle } from '../local-outbox/transaction-handle.interface.js';
6
+ export type PgLocalOutboxOptions = {
7
+ readonly pool: Pool;
8
+ readonly tableName?: string;
9
+ readonly maxRetries?: number;
10
+ };
11
+ export declare class PgLocalOutbox implements LocalOutboxWriter, LocalOutboxReader {
12
+ private readonly pool;
13
+ private readonly tableName;
14
+ private readonly maxRetries;
15
+ constructor(options: PgLocalOutboxOptions);
16
+ insert(entries: readonly LocalOutboxInsertInput[], trx: TransactionHandle): Promise<void>;
17
+ claim(instanceId: string, batchSize: number): Promise<readonly LocalOutboxEntry[]>;
18
+ markSent(id: string, publishedAt: Date): Promise<void>;
19
+ markFailed(id: string, reason: string): Promise<void>;
20
+ resetStale(olderThan: Date): Promise<number>;
21
+ cleanup(olderThan: Date, options?: {
22
+ readonly limit?: number;
23
+ }): Promise<number>;
24
+ private rowToEntry;
25
+ }
26
+ //# sourceMappingURL=pg-local-outbox.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pg-local-outbox.d.ts","sourceRoot":"","sources":["../../src/postgres/pg-local-outbox.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,IAAI,CAAC;AAC/B,OAAO,KAAK,EACV,gBAAgB,EAChB,sBAAsB,EAEvB,MAAM,4CAA4C,CAAC;AACpD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kDAAkD,CAAC;AAC1F,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kDAAkD,CAAC;AAC1F,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iDAAiD,CAAC;AAuBzF,MAAM,MAAM,oBAAoB,GAAG;IACjC,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC;IACpB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC9B,CAAC;AAEF,qBAAa,aAAc,YAAW,iBAAiB,EAAE,iBAAiB;IACxE,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAO;IAC5B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;gBAExB,OAAO,EAAE,oBAAoB;IAMnC,MAAM,CAAC,OAAO,EAAE,SAAS,sBAAsB,EAAE,EAAE,GAAG,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IAyCzF,KAAK,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,gBAAgB,EAAE,CAAC;IAmBlF,QAAQ,CAAC,EAAE,EAAE,MAAM,EAAE,WAAW,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAStD,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAarD,UAAU,CAAC,SAAS,EAAE,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC;IAU5C,OAAO,CAAC,SAAS,EAAE,IAAI,EAAE,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,MAAM,CAAC;IAuBtF,OAAO,CAAC,UAAU;CAkBnB"}
@@ -0,0 +1,102 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ const DEFAULT_TABLE_NAME = 'outbox_events';
3
+ const DEFAULT_MAX_RETRIES = 3;
4
+ const INSERT_COLUMN_COUNT = 14;
5
+ export class PgLocalOutbox {
6
+ pool;
7
+ tableName;
8
+ maxRetries;
9
+ constructor(options) {
10
+ this.pool = options.pool;
11
+ this.tableName = options.tableName ?? DEFAULT_TABLE_NAME;
12
+ this.maxRetries = options.maxRetries ?? DEFAULT_MAX_RETRIES;
13
+ }
14
+ async insert(entries, trx) {
15
+ if (entries.length === 0)
16
+ return;
17
+ const params = [];
18
+ const valueGroups = [];
19
+ for (const [i, entry] of entries.entries()) {
20
+ const base = i * INSERT_COLUMN_COUNT;
21
+ valueGroups.push(`($${base + 1}, $${base + 2}, $${base + 3}, $${base + 4}, $${base + 5}::jsonb, ` +
22
+ `$${base + 6}, $${base + 7}, $${base + 8}, $${base + 9}, $${base + 10}, ` +
23
+ `$${base + 11}, $${base + 12}, $${base + 13}, $${base + 14})`);
24
+ params.push(entry.id ?? randomUUID(), entry.eventType, entry.eventVersion ?? 1, entry.eventKind, JSON.stringify(entry.payload), entry.aggregateId ?? null, entry.correlationId ?? null, 'PENDING', null, null, 0, null, null, entry.createdAt ?? new Date());
25
+ }
26
+ await trx.query(`INSERT INTO ${this.tableName}
27
+ (id, event_type, event_version, event_kind, payload,
28
+ aggregate_id, correlation_id, status, claimed_by, claimed_at,
29
+ retry_count, last_error, published_at, created_at)
30
+ VALUES ${valueGroups.join(', ')}`, params);
31
+ }
32
+ async claim(instanceId, batchSize) {
33
+ const result = await this.pool.query(`WITH claimed AS (
34
+ SELECT id FROM ${this.tableName}
35
+ WHERE status = 'PENDING'
36
+ ORDER BY created_at
37
+ LIMIT $1
38
+ FOR UPDATE SKIP LOCKED
39
+ )
40
+ UPDATE ${this.tableName} t
41
+ SET status = 'CLAIMED', claimed_by = $2, claimed_at = $3
42
+ FROM claimed c
43
+ WHERE t.id = c.id
44
+ RETURNING t.*`, [batchSize, instanceId, new Date()]);
45
+ return result.rows.map((r) => this.rowToEntry(r));
46
+ }
47
+ async markSent(id, publishedAt) {
48
+ await this.pool.query(`UPDATE ${this.tableName}
49
+ SET status = 'SENT', published_at = $1, claimed_by = NULL, claimed_at = NULL
50
+ WHERE id = $2`, [publishedAt, id]);
51
+ }
52
+ async markFailed(id, reason) {
53
+ await this.pool.query(`UPDATE ${this.tableName}
54
+ SET retry_count = retry_count + 1,
55
+ last_error = $1,
56
+ status = CASE WHEN retry_count + 1 >= $3 THEN 'FAILED' ELSE 'PENDING' END,
57
+ claimed_by = NULL,
58
+ claimed_at = NULL
59
+ WHERE id = $2`, [reason, id, this.maxRetries]);
60
+ }
61
+ async resetStale(olderThan) {
62
+ const result = await this.pool.query(`UPDATE ${this.tableName}
63
+ SET status = 'PENDING', claimed_by = NULL, claimed_at = NULL
64
+ WHERE status = 'CLAIMED' AND claimed_at < $1`, [olderThan]);
65
+ return result.rowCount ?? 0;
66
+ }
67
+ async cleanup(olderThan, options) {
68
+ const limit = options?.limit;
69
+ const result = limit
70
+ ? await this.pool.query(`WITH cte AS (
71
+ SELECT id FROM ${this.tableName}
72
+ WHERE status = 'SENT' AND published_at < $1
73
+ ORDER BY published_at ASC
74
+ LIMIT $2
75
+ )
76
+ DELETE FROM ${this.tableName} t
77
+ USING cte
78
+ WHERE t.id = cte.id`, [olderThan, limit])
79
+ : await this.pool.query(`DELETE FROM ${this.tableName}
80
+ WHERE status = 'SENT' AND published_at < $1`, [olderThan]);
81
+ return result.rowCount ?? 0;
82
+ }
83
+ rowToEntry(row) {
84
+ return {
85
+ id: row.id,
86
+ eventType: row.event_type,
87
+ eventVersion: row.event_version,
88
+ eventKind: row.event_kind,
89
+ payload: row.payload,
90
+ ...(row.aggregate_id !== null ? { aggregateId: row.aggregate_id } : {}),
91
+ ...(row.correlation_id !== null ? { correlationId: row.correlation_id } : {}),
92
+ status: row.status,
93
+ ...(row.claimed_by !== null ? { claimedBy: row.claimed_by } : {}),
94
+ ...(row.claimed_at !== null ? { claimedAt: row.claimed_at } : {}),
95
+ retryCount: row.retry_count,
96
+ ...(row.last_error !== null ? { lastError: row.last_error } : {}),
97
+ ...(row.published_at !== null ? { publishedAt: row.published_at } : {}),
98
+ createdAt: row.created_at,
99
+ };
100
+ }
101
+ }
102
+ //# sourceMappingURL=pg-local-outbox.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pg-local-outbox.js","sourceRoot":"","sources":["../../src/postgres/pg-local-outbox.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,73 @@
1
+ {
2
+ "name": "@quilla-be-kit/messaging",
3
+ "version": "0.2.0",
4
+ "description": "Broker-agnostic messaging: event-bus + consumer interfaces, local-outbox pattern, outbox forwarder, Postgres reference implementation.",
5
+ "license": "MIT",
6
+ "author": "Max Martinez",
7
+ "homepage": "https://github.com/quilla-kit/quilla-be-kit/tree/main/packages/messaging",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/quilla-kit/quilla-be-kit.git",
11
+ "directory": "packages/messaging"
12
+ },
13
+ "bugs": {
14
+ "url": "https://github.com/quilla-kit/quilla-be-kit/issues"
15
+ },
16
+ "keywords": [
17
+ "messaging",
18
+ "event-bus",
19
+ "outbox",
20
+ "integration-events",
21
+ "typescript"
22
+ ],
23
+ "type": "module",
24
+ "engines": {
25
+ "node": ">=22"
26
+ },
27
+ "sideEffects": false,
28
+ "exports": {
29
+ ".": {
30
+ "types": "./dist/index.d.ts",
31
+ "import": "./dist/index.js"
32
+ },
33
+ "./postgres": {
34
+ "types": "./dist/postgres/index.d.ts",
35
+ "import": "./dist/postgres/index.js"
36
+ }
37
+ },
38
+ "main": "./dist/index.js",
39
+ "types": "./dist/index.d.ts",
40
+ "files": [
41
+ "dist",
42
+ "README.md",
43
+ "LICENSE"
44
+ ],
45
+ "publishConfig": {
46
+ "access": "public"
47
+ },
48
+ "dependencies": {
49
+ "@quilla-be-kit/errors": "^0.2.0",
50
+ "@quilla-be-kit/execution-context": "^0.2.0",
51
+ "@quilla-be-kit/observability": "^0.2.0",
52
+ "@quilla-be-kit/runtime": "^0.2.0"
53
+ },
54
+ "peerDependencies": {
55
+ "pg": ">=8.0"
56
+ },
57
+ "peerDependenciesMeta": {
58
+ "pg": {
59
+ "optional": true
60
+ }
61
+ },
62
+ "devDependencies": {
63
+ "@types/pg": "^8.15.5",
64
+ "pg": "^8.13.0",
65
+ "zod": "^4.0.0"
66
+ },
67
+ "scripts": {
68
+ "build": "tsc -b",
69
+ "typecheck": "tsc -p tsconfig.test.json",
70
+ "test": "vitest run --passWithNoTests",
71
+ "clean": "rm -rf dist *.tsbuildinfo"
72
+ }
73
+ }