@dotdo/postgres 0.1.2 → 0.1.3

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 (103) hide show
  1. package/README.md +73 -1
  2. package/dist/client/index.d.ts +47 -0
  3. package/dist/client/index.d.ts.map +1 -0
  4. package/dist/client/index.js +47 -0
  5. package/dist/client/index.js.map +1 -0
  6. package/dist/client/postgres-client.d.ts +273 -0
  7. package/dist/client/postgres-client.d.ts.map +1 -0
  8. package/dist/client/postgres-client.js +389 -0
  9. package/dist/client/postgres-client.js.map +1 -0
  10. package/dist/client/types.d.ts +167 -0
  11. package/dist/client/types.d.ts.map +1 -0
  12. package/dist/client/types.js +7 -0
  13. package/dist/client/types.js.map +1 -0
  14. package/dist/do/index.d.ts +18 -0
  15. package/dist/do/index.d.ts.map +1 -0
  16. package/dist/do/index.js +18 -0
  17. package/dist/do/index.js.map +1 -0
  18. package/dist/do/postgres.d.ts +110 -0
  19. package/dist/do/postgres.d.ts.map +1 -0
  20. package/dist/do/postgres.js +266 -0
  21. package/dist/do/postgres.js.map +1 -0
  22. package/dist/do/sql.d.ts +92 -0
  23. package/dist/do/sql.d.ts.map +1 -0
  24. package/dist/do/sql.js +204 -0
  25. package/dist/do/sql.js.map +1 -0
  26. package/dist/index.d.ts +25 -30
  27. package/dist/index.d.ts.map +1 -1
  28. package/dist/index.js +29 -30
  29. package/dist/index.js.map +1 -1
  30. package/dist/mcp/binding.d.ts +47 -0
  31. package/dist/mcp/binding.d.ts.map +1 -0
  32. package/dist/mcp/binding.js +183 -0
  33. package/dist/mcp/binding.js.map +1 -0
  34. package/dist/mcp/index.d.ts +92 -0
  35. package/dist/mcp/index.d.ts.map +1 -0
  36. package/dist/mcp/index.js +91 -0
  37. package/dist/mcp/index.js.map +1 -0
  38. package/dist/mcp/server.d.ts +62 -0
  39. package/dist/mcp/server.d.ts.map +1 -0
  40. package/dist/mcp/server.js +278 -0
  41. package/dist/mcp/server.js.map +1 -0
  42. package/dist/mcp/tools.d.ts +58 -0
  43. package/dist/mcp/tools.d.ts.map +1 -0
  44. package/dist/mcp/tools.js +356 -0
  45. package/dist/mcp/tools.js.map +1 -0
  46. package/dist/mcp/types.d.ts +139 -0
  47. package/dist/mcp/types.d.ts.map +1 -0
  48. package/dist/mcp/types.js +7 -0
  49. package/dist/mcp/types.js.map +1 -0
  50. package/dist/pglite/workers-pglite.d.ts +13 -4
  51. package/dist/pglite/workers-pglite.d.ts.map +1 -1
  52. package/dist/pglite/workers-pglite.js +110 -5
  53. package/dist/pglite/workers-pglite.js.map +1 -1
  54. package/dist/pglite-assets/pglite.data +0 -0
  55. package/dist/pglite-assets/pglite.wasm +0 -0
  56. package/dist/worker/background-pglite-manager.d.ts +243 -0
  57. package/dist/worker/background-pglite-manager.d.ts.map +1 -0
  58. package/dist/worker/background-pglite-manager.js +528 -0
  59. package/dist/worker/background-pglite-manager.js.map +1 -0
  60. package/dist/worker/do-pglite-manager.d.ts +77 -0
  61. package/dist/worker/do-pglite-manager.d.ts.map +1 -1
  62. package/dist/worker/do-pglite-manager.js +189 -12
  63. package/dist/worker/do-pglite-manager.js.map +1 -1
  64. package/dist/worker/index.d.ts +7 -1
  65. package/dist/worker/index.d.ts.map +1 -1
  66. package/dist/worker/index.js +19 -1
  67. package/dist/worker/index.js.map +1 -1
  68. package/dist/worker/lazy-pglite-manager.d.ts +242 -0
  69. package/dist/worker/lazy-pglite-manager.d.ts.map +1 -0
  70. package/dist/worker/lazy-pglite-manager.js +463 -0
  71. package/dist/worker/lazy-pglite-manager.js.map +1 -0
  72. package/package.json +16 -3
  73. package/src/client/index.ts +61 -0
  74. package/src/client/postgres-client.ts +442 -0
  75. package/src/client/types.ts +211 -0
  76. package/src/do/index.ts +18 -0
  77. package/src/do/postgres.ts +367 -0
  78. package/src/do/sql.ts +280 -0
  79. package/src/index.ts +50 -30
  80. package/src/mcp/binding.ts +236 -0
  81. package/src/mcp/index.ts +122 -0
  82. package/src/mcp/server.ts +361 -0
  83. package/src/mcp/tools.ts +464 -0
  84. package/src/mcp/types.ts +148 -0
  85. package/src/pglite/workers-pglite.ts +141 -12
  86. package/src/pglite-assets/pglite.data +0 -0
  87. package/src/pglite-assets/pglite.wasm +0 -0
  88. package/src/worker/background-pglite-manager.ts +680 -0
  89. package/src/worker/do-pglite-manager.ts +235 -19
  90. package/src/worker/index.ts +71 -1
  91. package/src/worker/lazy-pglite-manager.ts +595 -0
  92. package/dist/iceberg/duckdb-wasm.d.ts +0 -447
  93. package/dist/iceberg/duckdb-wasm.d.ts.map +0 -1
  94. package/dist/iceberg/duckdb-wasm.js +0 -600
  95. package/dist/iceberg/duckdb-wasm.js.map +0 -1
  96. package/dist/iceberg/test-fixtures.d.ts +0 -151
  97. package/dist/iceberg/test-fixtures.d.ts.map +0 -1
  98. package/dist/iceberg/test-fixtures.js +0 -446
  99. package/dist/iceberg/test-fixtures.js.map +0 -1
  100. package/dist/worker/__mocks__/cloudflare-workers.d.ts +0 -31
  101. package/dist/worker/__mocks__/cloudflare-workers.d.ts.map +0 -1
  102. package/dist/worker/__mocks__/cloudflare-workers.js +0 -33
  103. package/dist/worker/__mocks__/cloudflare-workers.js.map +0 -1
package/src/index.ts CHANGED
@@ -1,48 +1,46 @@
1
1
  /**
2
2
  * @module @dotdo/postgres
3
3
  *
4
- * PostgreSQL server for Cloudflare Workers/DOs with PGLite WASM and tiered storage.
4
+ * PostgreSQL for Cloudflare Workers - Simple, Persistent, Powerful.
5
5
  *
6
- * This package provides a complete PostgreSQL-compatible database solution
7
- * optimized for Cloudflare's edge infrastructure:
8
- *
9
- * - **Worker Module**: PostgresDO Durable Object with HTTP/WebSocket APIs
10
- * - **PGLite Module**: Tiered storage system (Cache/DO/R2) for PGLite
11
- * - **Extensions Module**: PGLite extension management and pgvector support
12
- * - **Routing Module**: Intelligent query routing based on CPU cost estimation
13
- * - **Read-Only Module**: Security-focused read-only deployment mode
14
- * - **Middleware Module**: Rate limiting with sliding window and token bucket algorithms
15
- *
16
- * @example
6
+ * @example Minimal API
17
7
  * ```typescript
18
- * import {
19
- * PostgresDO,
20
- * createRoutes,
21
- * WebSocketHandler,
22
- * } from '@dotdo/postgres'
23
- *
24
- * // Export the Durable Object
25
- * export { PostgresDO }
26
- *
27
- * // Create Hono routes
28
- * const app = new Hono()
29
- * app.route('/api/sql', createRoutes(env))
8
+ * import { sql, Postgres } from '@dotdo/postgres'
9
+ * export { Postgres }
30
10
  *
31
11
  * export default {
32
- * fetch: app.fetch,
12
+ * fetch: () => Response.json(await sql`SELECT * FROM posts`)
33
13
  * }
34
14
  * ```
35
15
  *
36
- * @example
16
+ * @example With Hono
37
17
  * ```typescript
38
- * // Use tiered storage for PGLite
39
- * import { createDOVFS, TieredVFS, CacheLayer } from '@dotdo/postgres/pglite'
18
+ * import { Hono } from 'hono'
19
+ * import { sql, Postgres } from '@dotdo/postgres'
20
+ * export { Postgres }
40
21
  *
41
- * const vfs = createDOVFS(ctx.storage)
42
- * const pglite = await PGlite.create({ vfs })
22
+ * const app = new Hono()
23
+ * app.get('/posts', c => sql`SELECT * FROM posts`.then(r => c.json(r)))
24
+ * app.get('/posts/:id', c => sql`SELECT * FROM posts WHERE id = ${c.req.param('id')}`.then(r => c.json(r)))
25
+ *
26
+ * export default app
43
27
  * ```
28
+ *
29
+ * The Postgres Durable Object provides:
30
+ * - Full PostgreSQL via PGLite WASM
31
+ * - Automatic persistence via DO SQLite
32
+ * - Safe parameterized queries via tagged templates
33
+ *
34
+ * For advanced features (tiered storage, migrations, observability), see submodules.
44
35
  */
45
36
 
37
+ // ============================================================================
38
+ // Core API - The Minimal Interface
39
+ // ============================================================================
40
+
41
+ export { sql, db, createSql, createDb, Postgres } from './do'
42
+ export type { SqlConfig, SqlResult, PostgresEnv, QueryResult } from './do'
43
+
46
44
  // ============================================================================
47
45
  // Configuration Module - Centralized Constants
48
46
  // ============================================================================
@@ -210,3 +208,25 @@ export type {
210
208
  // Discriminated union
211
209
  DiscriminatedMessage,
212
210
  } from './types/utilities'
211
+
212
+ // ============================================================================
213
+ // Client Module - WebSocket client with rpc.do integration
214
+ // ============================================================================
215
+
216
+ export {
217
+ PostgresClient,
218
+ createPostgresClient,
219
+ } from './client'
220
+
221
+ export type {
222
+ PostgresClientConfig,
223
+ PostgresDORpcApi,
224
+ RpcQueryResult,
225
+ RpcBatchQuery,
226
+ RpcBatchResult,
227
+ RpcTransactionOptions,
228
+ TransactionApi,
229
+ ColumnInfo,
230
+ DatabaseStats,
231
+ ConnectionState,
232
+ } from './client'
@@ -0,0 +1,236 @@
1
+ /**
2
+ * PostgreSQL Binding for MCP Do Tool
3
+ *
4
+ * Creates the `pg` binding that is available in the sandboxed code execution
5
+ * environment. Wraps PostgresDO RPC calls in a clean API.
6
+ */
7
+
8
+ import type {
9
+ PGBinding,
10
+ PGTransaction,
11
+ QueryResult,
12
+ TableInfo,
13
+ ColumnInfo,
14
+ } from './types.js'
15
+
16
+ /**
17
+ * Interface for the query executor (PostgresDO stub)
18
+ */
19
+ export interface QueryExecutor {
20
+ rpcQuery(sql: string, params?: unknown[]): Promise<{
21
+ rows: Record<string, unknown>[]
22
+ fields: Array<{ name: string; dataTypeID: number }>
23
+ rowCount: number
24
+ }>
25
+ rpcBatchTransaction(
26
+ queries: Array<{ sql: string; params?: unknown[] }>
27
+ ): Promise<{
28
+ results: Array<{
29
+ rows: Record<string, unknown>[]
30
+ fields: Array<{ name: string; dataTypeID: number }>
31
+ rowCount: number
32
+ }>
33
+ success: boolean
34
+ }>
35
+ }
36
+
37
+ /**
38
+ * Create the pg binding for the do tool
39
+ *
40
+ * @param executor - Query executor (PostgresDO stub)
41
+ * @returns The pg binding object
42
+ */
43
+ export function createPGBinding(executor: QueryExecutor): PGBinding {
44
+ return {
45
+ async query<T = Record<string, unknown>>(
46
+ sql: string,
47
+ params?: unknown[]
48
+ ): Promise<QueryResult<T>> {
49
+ const result = await executor.rpcQuery(sql, params)
50
+ return {
51
+ rows: result.rows as T[],
52
+ rowCount: result.rowCount,
53
+ fields: result.fields,
54
+ }
55
+ },
56
+
57
+ async execute(
58
+ sql: string,
59
+ params?: unknown[]
60
+ ): Promise<{ rowCount: number }> {
61
+ const result = await executor.rpcQuery(sql, params)
62
+ return { rowCount: result.rowCount }
63
+ },
64
+
65
+ async transaction<T>(fn: (tx: PGTransaction) => Promise<T>): Promise<T> {
66
+ // Execute BEGIN, run function with transaction context, execute COMMIT/ROLLBACK
67
+ await executor.rpcQuery('BEGIN')
68
+
69
+ try {
70
+ const result = await fn({
71
+ async query<R = Record<string, unknown>>(
72
+ sql: string,
73
+ params?: unknown[]
74
+ ): Promise<QueryResult<R>> {
75
+ const res = await executor.rpcQuery(sql, params)
76
+ return {
77
+ rows: res.rows as R[],
78
+ rowCount: res.rowCount,
79
+ fields: res.fields,
80
+ }
81
+ },
82
+ async execute(
83
+ sql: string,
84
+ params?: unknown[]
85
+ ): Promise<{ rowCount: number }> {
86
+ const res = await executor.rpcQuery(sql, params)
87
+ return { rowCount: res.rowCount }
88
+ },
89
+ })
90
+
91
+ await executor.rpcQuery('COMMIT')
92
+ return result
93
+ } catch (error) {
94
+ await executor.rpcQuery('ROLLBACK')
95
+ throw error
96
+ }
97
+ },
98
+
99
+ async tables(): Promise<TableInfo[]> {
100
+ const result = await executor.rpcQuery(`
101
+ SELECT
102
+ table_name as name,
103
+ table_schema as schema,
104
+ CASE table_type
105
+ WHEN 'BASE TABLE' THEN 'table'
106
+ WHEN 'VIEW' THEN 'view'
107
+ ELSE 'table'
108
+ END as type
109
+ FROM information_schema.tables
110
+ WHERE table_schema NOT IN ('pg_catalog', 'information_schema')
111
+ ORDER BY table_schema, table_name
112
+ `)
113
+
114
+ return result.rows.map((row) => ({
115
+ name: row.name as string,
116
+ schema: row.schema as string,
117
+ type: row.type as 'table' | 'view' | 'materialized_view',
118
+ }))
119
+ },
120
+
121
+ async schema(tableName: string): Promise<ColumnInfo[]> {
122
+ // Validate table name to prevent SQL injection
123
+ if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(tableName)) {
124
+ throw new Error('Invalid table name')
125
+ }
126
+
127
+ const result = await executor.rpcQuery(
128
+ `
129
+ SELECT
130
+ c.column_name as name,
131
+ c.data_type as type,
132
+ c.is_nullable = 'YES' as nullable,
133
+ c.column_default as "default",
134
+ COALESCE(
135
+ (SELECT true FROM information_schema.key_column_usage kcu
136
+ JOIN information_schema.table_constraints tc
137
+ ON kcu.constraint_name = tc.constraint_name
138
+ WHERE tc.constraint_type = 'PRIMARY KEY'
139
+ AND kcu.table_name = c.table_name
140
+ AND kcu.column_name = c.column_name
141
+ LIMIT 1),
142
+ false
143
+ ) as "primaryKey"
144
+ FROM information_schema.columns c
145
+ WHERE c.table_name = $1
146
+ AND c.table_schema NOT IN ('pg_catalog', 'information_schema')
147
+ ORDER BY c.ordinal_position
148
+ `,
149
+ [tableName]
150
+ )
151
+
152
+ return result.rows.map((row) => ({
153
+ name: row.name as string,
154
+ type: row.type as string,
155
+ nullable: Boolean(row.nullable),
156
+ ...(row.default !== null && { default: row.default as string }),
157
+ primaryKey: Boolean(row.primaryKey),
158
+ }))
159
+ },
160
+ }
161
+ }
162
+
163
+ /**
164
+ * TypeScript type definitions for the pg binding
165
+ * Used by the do tool to provide type information to LLMs
166
+ */
167
+ export const PG_BINDING_TYPES = `
168
+ interface QueryResult<T = Record<string, unknown>> {
169
+ rows: T[]
170
+ rowCount: number
171
+ fields: Array<{ name: string; dataTypeID: number }>
172
+ }
173
+
174
+ interface TableInfo {
175
+ name: string
176
+ schema: string
177
+ type: 'table' | 'view' | 'materialized_view'
178
+ }
179
+
180
+ interface ColumnInfo {
181
+ name: string
182
+ type: string
183
+ nullable: boolean
184
+ default?: string
185
+ primaryKey: boolean
186
+ }
187
+
188
+ interface PGTransaction {
189
+ query<T = Record<string, unknown>>(sql: string, params?: unknown[]): Promise<QueryResult<T>>
190
+ execute(sql: string, params?: unknown[]): Promise<{ rowCount: number }>
191
+ }
192
+
193
+ declare const pg: {
194
+ /**
195
+ * Execute a SQL query with optional parameters
196
+ * @example
197
+ * const users = await pg.query('SELECT * FROM users WHERE age > $1', [18])
198
+ * console.log(users.rows)
199
+ */
200
+ query<T = Record<string, unknown>>(sql: string, params?: unknown[]): Promise<QueryResult<T>>
201
+
202
+ /**
203
+ * Execute a SQL statement (INSERT, UPDATE, DELETE)
204
+ * @example
205
+ * const result = await pg.execute('UPDATE users SET active = true WHERE id = $1', [1])
206
+ * console.log(result.rowCount)
207
+ */
208
+ execute(sql: string, params?: unknown[]): Promise<{ rowCount: number }>
209
+
210
+ /**
211
+ * Execute multiple statements in a transaction
212
+ * @example
213
+ * await pg.transaction(async (tx) => {
214
+ * await tx.execute('INSERT INTO orders (user_id) VALUES ($1)', [1])
215
+ * await tx.execute('UPDATE users SET order_count = order_count + 1 WHERE id = $1', [1])
216
+ * })
217
+ */
218
+ transaction<T>(fn: (tx: PGTransaction) => Promise<T>): Promise<T>
219
+
220
+ /**
221
+ * List all tables in the database
222
+ * @example
223
+ * const tables = await pg.tables()
224
+ * tables.forEach(t => console.log(t.name))
225
+ */
226
+ tables(): Promise<TableInfo[]>
227
+
228
+ /**
229
+ * Get schema information for a table
230
+ * @example
231
+ * const columns = await pg.schema('users')
232
+ * columns.forEach(c => console.log(\`\${c.name}: \${c.type}\`))
233
+ */
234
+ schema(tableName: string): Promise<ColumnInfo[]>
235
+ }
236
+ `
@@ -0,0 +1,122 @@
1
+ /**
2
+ * MCP Server Module for PostgresDO
3
+ *
4
+ * Provides MCP (Model Context Protocol) server implementation for PostgreSQL
5
+ * databases running in Cloudflare Durable Objects.
6
+ *
7
+ * ## Overview
8
+ *
9
+ * This module implements the three core MCP primitives:
10
+ *
11
+ * 1. **search** - Query the database with SQL, list tables, or get schema
12
+ * 2. **fetch** - Get a specific row by table/id or table schema
13
+ * 3. **do** - Execute code with the `pg` binding for full database access
14
+ *
15
+ * ## Usage
16
+ *
17
+ * @example Basic MCP server setup
18
+ * ```typescript
19
+ * import { createMCPServer } from '@dotdo/postgres/mcp'
20
+ * import { PostgresDO } from '@dotdo/postgres/worker'
21
+ *
22
+ * // In your worker
23
+ * const stub = env.POSTGRES_DO.get(doId) as DurableObjectStub<PostgresDO>
24
+ * const mcpServer = createMCPServer(stub, { databaseId: 'mydb' })
25
+ *
26
+ * // Mount routes
27
+ * app.route('/mcp', mcpServer.routes())
28
+ * ```
29
+ *
30
+ * @example With auth context from oauth.do JWT
31
+ * ```typescript
32
+ * const mcpServer = createMCPServer(stub, {
33
+ * databaseId: 'mydb',
34
+ * authContext: {
35
+ * userId: user.id,
36
+ * email: user.email,
37
+ * scopes: ['read', 'write'],
38
+ * },
39
+ * })
40
+ * ```
41
+ *
42
+ * @example Using tools directly
43
+ * ```typescript
44
+ * // Search for tables
45
+ * const result = await mcpServer.handleRequest({
46
+ * jsonrpc: '2.0',
47
+ * id: 1,
48
+ * method: 'tools/call',
49
+ * params: {
50
+ * name: 'search',
51
+ * arguments: { query: 'tables' }
52
+ * }
53
+ * })
54
+ *
55
+ * // Fetch a row
56
+ * const row = await mcpServer.handleRequest({
57
+ * jsonrpc: '2.0',
58
+ * id: 2,
59
+ * method: 'tools/call',
60
+ * params: {
61
+ * name: 'fetch',
62
+ * arguments: { resource: 'users/123' }
63
+ * }
64
+ * })
65
+ *
66
+ * // Execute code
67
+ * const code = await mcpServer.handleRequest({
68
+ * jsonrpc: '2.0',
69
+ * id: 3,
70
+ * method: 'tools/call',
71
+ * params: {
72
+ * name: 'do',
73
+ * arguments: {
74
+ * code: `
75
+ * const users = await pg.query('SELECT * FROM users LIMIT 5')
76
+ * return users.rows
77
+ * `
78
+ * }
79
+ * }
80
+ * })
81
+ * ```
82
+ *
83
+ * @module mcp
84
+ */
85
+
86
+ // Types
87
+ export type {
88
+ QueryResult,
89
+ TableInfo,
90
+ ColumnInfo,
91
+ MCPSearchResult,
92
+ MCPFetchResult,
93
+ PGBinding,
94
+ PGTransaction,
95
+ ToolResponse,
96
+ SearchInput,
97
+ FetchInput,
98
+ DoInput,
99
+ MCPAuthContext,
100
+ MCPServerConfig,
101
+ } from './types.js'
102
+
103
+ // Binding
104
+ export { createPGBinding, PG_BINDING_TYPES } from './binding.js'
105
+ export type { QueryExecutor } from './binding.js'
106
+
107
+ // Tools
108
+ export {
109
+ searchTool,
110
+ fetchTool,
111
+ doTool,
112
+ getToolDefinitions,
113
+ createToolHandlers,
114
+ createSearchHandler,
115
+ createFetchHandler,
116
+ createDoHandler,
117
+ } from './tools.js'
118
+ export type { Tool } from './tools.js'
119
+
120
+ // Server
121
+ export { createMCPServer, createMCPRoutes } from './server.js'
122
+ export type { MCPServer } from './server.js'