postgresai 0.14.0-dev.8 → 0.14.0-dev.80
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/README.md +161 -61
- package/bin/postgres-ai.ts +2596 -428
- package/bun.lock +258 -0
- package/bunfig.toml +20 -0
- package/dist/bin/postgres-ai.js +31218 -1575
- package/dist/sql/01.role.sql +16 -0
- package/dist/sql/02.extensions.sql +8 -0
- package/dist/sql/03.permissions.sql +38 -0
- package/dist/sql/04.optional_rds.sql +6 -0
- package/dist/sql/05.optional_self_managed.sql +8 -0
- package/dist/sql/06.helpers.sql +439 -0
- package/dist/sql/sql/01.role.sql +16 -0
- package/dist/sql/sql/02.extensions.sql +8 -0
- package/dist/sql/sql/03.permissions.sql +38 -0
- package/dist/sql/sql/04.optional_rds.sql +6 -0
- package/dist/sql/sql/05.optional_self_managed.sql +8 -0
- package/dist/sql/sql/06.helpers.sql +439 -0
- package/dist/sql/sql/uninit/01.helpers.sql +5 -0
- package/dist/sql/sql/uninit/02.permissions.sql +30 -0
- package/dist/sql/sql/uninit/03.role.sql +27 -0
- package/dist/sql/uninit/01.helpers.sql +5 -0
- package/dist/sql/uninit/02.permissions.sql +30 -0
- package/dist/sql/uninit/03.role.sql +27 -0
- package/lib/auth-server.ts +124 -106
- package/lib/checkup-api.ts +386 -0
- package/lib/checkup-dictionary.ts +113 -0
- package/lib/checkup.ts +1435 -0
- package/lib/config.ts +6 -3
- package/lib/init.ts +655 -189
- package/lib/issues.ts +848 -193
- package/lib/mcp-server.ts +391 -91
- package/lib/metrics-loader.ts +127 -0
- package/lib/supabase.ts +824 -0
- package/lib/util.ts +61 -0
- package/package.json +22 -10
- package/packages/postgres-ai/README.md +26 -0
- package/packages/postgres-ai/bin/postgres-ai.js +27 -0
- package/packages/postgres-ai/package.json +27 -0
- package/scripts/embed-checkup-dictionary.ts +106 -0
- package/scripts/embed-metrics.ts +154 -0
- package/sql/01.role.sql +16 -0
- package/sql/02.extensions.sql +8 -0
- package/sql/03.permissions.sql +38 -0
- package/sql/04.optional_rds.sql +6 -0
- package/sql/05.optional_self_managed.sql +8 -0
- package/sql/06.helpers.sql +439 -0
- package/sql/uninit/01.helpers.sql +5 -0
- package/sql/uninit/02.permissions.sql +30 -0
- package/sql/uninit/03.role.sql +27 -0
- package/test/auth.test.ts +258 -0
- package/test/checkup.integration.test.ts +321 -0
- package/test/checkup.test.ts +1116 -0
- package/test/config-consistency.test.ts +36 -0
- package/test/init.integration.test.ts +508 -0
- package/test/init.test.ts +916 -0
- package/test/issues.cli.test.ts +538 -0
- package/test/issues.test.ts +456 -0
- package/test/mcp-server.test.ts +1527 -0
- package/test/schema-validation.test.ts +81 -0
- package/test/supabase.test.ts +568 -0
- package/test/test-utils.ts +128 -0
- package/tsconfig.json +12 -20
- package/dist/bin/postgres-ai.d.ts +0 -3
- package/dist/bin/postgres-ai.d.ts.map +0 -1
- package/dist/bin/postgres-ai.js.map +0 -1
- package/dist/lib/auth-server.d.ts +0 -31
- package/dist/lib/auth-server.d.ts.map +0 -1
- package/dist/lib/auth-server.js +0 -263
- package/dist/lib/auth-server.js.map +0 -1
- package/dist/lib/config.d.ts +0 -45
- package/dist/lib/config.d.ts.map +0 -1
- package/dist/lib/config.js +0 -181
- package/dist/lib/config.js.map +0 -1
- package/dist/lib/init.d.ts +0 -64
- package/dist/lib/init.d.ts.map +0 -1
- package/dist/lib/init.js +0 -399
- package/dist/lib/init.js.map +0 -1
- package/dist/lib/issues.d.ts +0 -75
- package/dist/lib/issues.d.ts.map +0 -1
- package/dist/lib/issues.js +0 -336
- package/dist/lib/issues.js.map +0 -1
- package/dist/lib/mcp-server.d.ts +0 -9
- package/dist/lib/mcp-server.d.ts.map +0 -1
- package/dist/lib/mcp-server.js +0 -168
- package/dist/lib/mcp-server.js.map +0 -1
- package/dist/lib/pkce.d.ts +0 -32
- package/dist/lib/pkce.d.ts.map +0 -1
- package/dist/lib/pkce.js +0 -101
- package/dist/lib/pkce.js.map +0 -1
- package/dist/lib/util.d.ts +0 -27
- package/dist/lib/util.d.ts.map +0 -1
- package/dist/lib/util.js +0 -46
- package/dist/lib/util.js.map +0 -1
- package/dist/package.json +0 -46
- package/test/init.integration.test.cjs +0 -269
- package/test/init.test.cjs +0 -76
package/lib/auth-server.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import * as http from "http";
|
|
2
|
-
import { URL } from "url";
|
|
3
2
|
|
|
4
3
|
/**
|
|
5
4
|
* OAuth callback result
|
|
@@ -13,8 +12,9 @@ export interface CallbackResult {
|
|
|
13
12
|
* Callback server structure
|
|
14
13
|
*/
|
|
15
14
|
export interface CallbackServer {
|
|
16
|
-
server:
|
|
15
|
+
server: { stop: () => void };
|
|
17
16
|
promise: Promise<CallbackResult>;
|
|
17
|
+
ready: Promise<number>; // Resolves with actual port when server is listening
|
|
18
18
|
getPort: () => number;
|
|
19
19
|
}
|
|
20
20
|
|
|
@@ -34,11 +34,21 @@ function escapeHtml(str: string | null): string {
|
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
/**
|
|
37
|
-
* Create and start callback server
|
|
37
|
+
* Create and start callback server using Node.js http module
|
|
38
|
+
*
|
|
38
39
|
* @param port - Port to listen on (0 for random available port)
|
|
39
40
|
* @param expectedState - Expected state parameter for CSRF protection
|
|
40
41
|
* @param timeoutMs - Timeout in milliseconds
|
|
41
|
-
* @returns Server object with promise and getPort function
|
|
42
|
+
* @returns Server object with promise, ready promise, and getPort function
|
|
43
|
+
*
|
|
44
|
+
* @remarks
|
|
45
|
+
* The `ready` promise resolves with the actual port once the server is listening.
|
|
46
|
+
* Callers should await `ready` before using `getPort()` when using port 0.
|
|
47
|
+
*
|
|
48
|
+
* The server stops asynchronously ~100ms after the callback resolves/rejects.
|
|
49
|
+
* This delay ensures the HTTP response is fully sent before closing the connection.
|
|
50
|
+
* Callers should not attempt to reuse the same port immediately after the promise
|
|
51
|
+
* resolves - wait at least 200ms or use a different port.
|
|
42
52
|
*/
|
|
43
53
|
export function createCallbackServer(
|
|
44
54
|
port: number = 0,
|
|
@@ -46,54 +56,81 @@ export function createCallbackServer(
|
|
|
46
56
|
timeoutMs: number = 300000
|
|
47
57
|
): CallbackServer {
|
|
48
58
|
let resolved = false;
|
|
49
|
-
let server: http.Server | null = null;
|
|
50
59
|
let actualPort = port;
|
|
51
60
|
let resolveCallback: (value: CallbackResult) => void;
|
|
52
61
|
let rejectCallback: (reason: Error) => void;
|
|
53
|
-
|
|
62
|
+
let resolveReady: (port: number) => void;
|
|
63
|
+
let rejectReady: (reason: Error) => void;
|
|
64
|
+
let serverInstance: http.Server | null = null;
|
|
65
|
+
|
|
54
66
|
const promise = new Promise<CallbackResult>((resolve, reject) => {
|
|
55
67
|
resolveCallback = resolve;
|
|
56
68
|
rejectCallback = reject;
|
|
57
69
|
});
|
|
58
|
-
|
|
70
|
+
|
|
71
|
+
const ready = new Promise<number>((resolve, reject) => {
|
|
72
|
+
resolveReady = resolve;
|
|
73
|
+
rejectReady = reject;
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
let timeoutId: ReturnType<typeof setTimeout> | null = null;
|
|
77
|
+
|
|
78
|
+
const stopServer = () => {
|
|
79
|
+
// Clear timeout to prevent it firing after manual stop
|
|
80
|
+
if (timeoutId) {
|
|
81
|
+
clearTimeout(timeoutId);
|
|
82
|
+
timeoutId = null;
|
|
83
|
+
}
|
|
84
|
+
if (serverInstance) {
|
|
85
|
+
serverInstance.close();
|
|
86
|
+
serverInstance = null;
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
|
|
59
90
|
// Timeout handler
|
|
60
|
-
|
|
91
|
+
timeoutId = setTimeout(() => {
|
|
61
92
|
if (!resolved) {
|
|
62
93
|
resolved = true;
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
}
|
|
94
|
+
timeoutId = null; // Already fired, clear reference
|
|
95
|
+
stopServer();
|
|
66
96
|
rejectCallback(new Error("Authentication timeout. Please try again."));
|
|
67
97
|
}
|
|
68
98
|
}, timeoutMs);
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
const requestHandler = (req: http.IncomingMessage, res: http.ServerResponse): void => {
|
|
99
|
+
|
|
100
|
+
serverInstance = http.createServer((req, res) => {
|
|
72
101
|
if (resolved) {
|
|
102
|
+
res.writeHead(200, { "Content-Type": "text/plain" });
|
|
103
|
+
res.end("Already handled");
|
|
73
104
|
return;
|
|
74
105
|
}
|
|
75
106
|
|
|
107
|
+
const url = new URL(req.url || "/", `http://127.0.0.1:${actualPort}`);
|
|
108
|
+
|
|
76
109
|
// Only handle /callback path
|
|
77
|
-
if (!
|
|
110
|
+
if (!url.pathname.startsWith("/callback")) {
|
|
78
111
|
res.writeHead(404, { "Content-Type": "text/plain" });
|
|
79
112
|
res.end("Not Found");
|
|
80
113
|
return;
|
|
81
114
|
}
|
|
82
115
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
116
|
+
const code = url.searchParams.get("code");
|
|
117
|
+
const state = url.searchParams.get("state");
|
|
118
|
+
const error = url.searchParams.get("error");
|
|
119
|
+
const errorDescription = url.searchParams.get("error_description");
|
|
120
|
+
|
|
121
|
+
// Handle OAuth error
|
|
122
|
+
if (error) {
|
|
123
|
+
resolved = true;
|
|
124
|
+
if (timeoutId) {
|
|
125
|
+
clearTimeout(timeoutId);
|
|
126
|
+
timeoutId = null;
|
|
127
|
+
}
|
|
89
128
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
res.writeHead(400, { "Content-Type": "text/html" });
|
|
96
|
-
res.end(`
|
|
129
|
+
setTimeout(() => stopServer(), 100);
|
|
130
|
+
rejectCallback(new Error(`OAuth error: ${error}${errorDescription ? ` - ${errorDescription}` : ""}`));
|
|
131
|
+
|
|
132
|
+
res.writeHead(400, { "Content-Type": "text/html" });
|
|
133
|
+
res.end(`
|
|
97
134
|
<!DOCTYPE html>
|
|
98
135
|
<html>
|
|
99
136
|
<head>
|
|
@@ -114,19 +151,14 @@ export function createCallbackServer(
|
|
|
114
151
|
</div>
|
|
115
152
|
</body>
|
|
116
153
|
</html>
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
server.close();
|
|
121
|
-
}
|
|
122
|
-
rejectCallback(new Error(`OAuth error: ${error}${errorDescription ? ` - ${errorDescription}` : ""}`));
|
|
123
|
-
return;
|
|
124
|
-
}
|
|
154
|
+
`);
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
125
157
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
158
|
+
// Validate required parameters
|
|
159
|
+
if (!code || !state) {
|
|
160
|
+
res.writeHead(400, { "Content-Type": "text/html" });
|
|
161
|
+
res.end(`
|
|
130
162
|
<!DOCTYPE html>
|
|
131
163
|
<html>
|
|
132
164
|
<head>
|
|
@@ -145,17 +177,23 @@ export function createCallbackServer(
|
|
|
145
177
|
</div>
|
|
146
178
|
</body>
|
|
147
179
|
</html>
|
|
148
|
-
|
|
149
|
-
|
|
180
|
+
`);
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Validate state (CSRF protection)
|
|
185
|
+
if (expectedState && state !== expectedState) {
|
|
186
|
+
resolved = true;
|
|
187
|
+
if (timeoutId) {
|
|
188
|
+
clearTimeout(timeoutId);
|
|
189
|
+
timeoutId = null;
|
|
150
190
|
}
|
|
151
191
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
res.writeHead(400, { "Content-Type": "text/html" });
|
|
158
|
-
res.end(`
|
|
192
|
+
setTimeout(() => stopServer(), 100);
|
|
193
|
+
rejectCallback(new Error("State mismatch (possible CSRF attack)"));
|
|
194
|
+
|
|
195
|
+
res.writeHead(400, { "Content-Type": "text/html" });
|
|
196
|
+
res.end(`
|
|
159
197
|
<!DOCTYPE html>
|
|
160
198
|
<html>
|
|
161
199
|
<head>
|
|
@@ -174,21 +212,24 @@ export function createCallbackServer(
|
|
|
174
212
|
</div>
|
|
175
213
|
</body>
|
|
176
214
|
</html>
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
server.close();
|
|
181
|
-
}
|
|
182
|
-
rejectCallback(new Error("State mismatch (possible CSRF attack)"));
|
|
183
|
-
return;
|
|
184
|
-
}
|
|
215
|
+
`);
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
185
218
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
219
|
+
// Success!
|
|
220
|
+
resolved = true;
|
|
221
|
+
if (timeoutId) {
|
|
222
|
+
clearTimeout(timeoutId);
|
|
223
|
+
timeoutId = null;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Resolve first, then stop server asynchronously after response is sent.
|
|
227
|
+
// The 100ms delay ensures the HTTP response is fully written before closing.
|
|
228
|
+
resolveCallback({ code, state });
|
|
229
|
+
setTimeout(() => stopServer(), 100);
|
|
230
|
+
|
|
231
|
+
res.writeHead(200, { "Content-Type": "text/html" });
|
|
232
|
+
res.end(`
|
|
192
233
|
<!DOCTYPE html>
|
|
193
234
|
<html>
|
|
194
235
|
<head>
|
|
@@ -207,61 +248,38 @@ export function createCallbackServer(
|
|
|
207
248
|
</div>
|
|
208
249
|
</body>
|
|
209
250
|
</html>
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
if (server) {
|
|
213
|
-
server.close();
|
|
214
|
-
}
|
|
215
|
-
resolveCallback({ code, state });
|
|
216
|
-
} catch (err) {
|
|
217
|
-
if (!resolved) {
|
|
218
|
-
resolved = true;
|
|
219
|
-
clearTimeout(timeout);
|
|
220
|
-
res.writeHead(500, { "Content-Type": "text/plain" });
|
|
221
|
-
res.end("Internal Server Error");
|
|
222
|
-
if (server) {
|
|
223
|
-
server.close();
|
|
224
|
-
}
|
|
225
|
-
rejectCallback(err instanceof Error ? err : new Error(String(err)));
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
};
|
|
251
|
+
`);
|
|
252
|
+
});
|
|
229
253
|
|
|
230
|
-
//
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
254
|
+
// Handle server errors (e.g., EADDRINUSE)
|
|
255
|
+
serverInstance.on("error", (err: NodeJS.ErrnoException) => {
|
|
256
|
+
if (timeoutId) {
|
|
257
|
+
clearTimeout(timeoutId);
|
|
258
|
+
timeoutId = null;
|
|
259
|
+
}
|
|
260
|
+
if (err.code === "EADDRINUSE") {
|
|
261
|
+
rejectReady(new Error(`Port ${port} is already in use`));
|
|
262
|
+
} else {
|
|
263
|
+
rejectReady(new Error(`Server error: ${err.message}`));
|
|
264
|
+
}
|
|
234
265
|
if (!resolved) {
|
|
235
266
|
resolved = true;
|
|
236
|
-
clearTimeout(timeout);
|
|
237
267
|
rejectCallback(err);
|
|
238
268
|
}
|
|
239
269
|
});
|
|
240
270
|
|
|
241
|
-
|
|
242
|
-
const address =
|
|
271
|
+
serverInstance.listen(port, "127.0.0.1", () => {
|
|
272
|
+
const address = serverInstance?.address();
|
|
243
273
|
if (address && typeof address === "object") {
|
|
244
274
|
actualPort = address.port;
|
|
245
275
|
}
|
|
276
|
+
resolveReady(actualPort);
|
|
246
277
|
});
|
|
247
|
-
|
|
278
|
+
|
|
248
279
|
return {
|
|
249
|
-
server,
|
|
280
|
+
server: { stop: stopServer },
|
|
250
281
|
promise,
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
return address && typeof address === "object" ? address.port : 0;
|
|
254
|
-
},
|
|
282
|
+
ready,
|
|
283
|
+
getPort: () => actualPort,
|
|
255
284
|
};
|
|
256
285
|
}
|
|
257
|
-
|
|
258
|
-
/**
|
|
259
|
-
* Get the actual port the server is listening on
|
|
260
|
-
* @param server - HTTP server instance
|
|
261
|
-
* @returns Port number
|
|
262
|
-
*/
|
|
263
|
-
export function getServerPort(server: http.Server): number {
|
|
264
|
-
const address = server.address();
|
|
265
|
-
return address && typeof address === "object" ? address.port : 0;
|
|
266
|
-
}
|
|
267
|
-
|