@amerged/ohmyhost-cli 0.1.37 → 0.1.39
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 +1 -1
- package/THIRD_PARTY_NOTICES.md +27 -1
- package/dist/bin.js +1 -1
- package/dist/{chunk-IHXNADOQ.js → chunk-R2BC42EW.js} +4501 -134
- package/dist/index.js +1 -1
- package/dist/licenses/pgcf-bridge-LICENSE +201 -0
- package/dist/licenses/pgcf-bridge-NOTICE +1 -0
- package/package.json +1 -1
- package/src/apps/product-cli/package.json +4 -3
- package/src/apps/product-cli/src/cli.ts +7 -5
- package/src/apps/product-cli/src/command.ts +18 -5
- package/src/apps/product-cli/src/product-api.ts +22 -15
- package/src/apps/product-cli/src/psql-session.ts +54 -19
- package/src/apps/product-cli/src/repository-init.ts +181 -9
- package/src/packages/contracts/package.json +6 -1
- package/src/packages/contracts/src/credit-pricing.ts +7 -1
- package/src/packages/contracts/src/database-access.ts +53 -12
- package/src/packages/contracts/src/database-compute.ts +66 -11
- package/src/packages/contracts/src/database-sizes.ts +114 -0
- package/src/packages/contracts/src/framework-admission-corpus.ts +15 -18
- package/src/packages/contracts/src/framework-build-script.mjs +10 -3
- package/src/packages/contracts/src/index.ts +3 -0
- package/src/packages/contracts/src/operation-failure.ts +2 -2
- package/src/packages/contracts/src/platform-origins.ts +8 -0
- package/src/packages/contracts/src/problem-policy.ts +16 -2
- package/src/packages/contracts/src/storage-gateway-origin.ts +93 -0
- package/src/packages/postgres-bridge/LICENSE +201 -0
- package/src/packages/postgres-bridge/NOTICE +1 -0
- package/src/packages/postgres-bridge/package.json +26 -0
- package/src/packages/postgres-bridge/src/auth.ts +159 -0
- package/src/packages/postgres-bridge/src/bridge.ts +168 -0
- package/src/packages/postgres-bridge/src/index.ts +2 -0
- package/src/packages/postgres-bridge/src/options.ts +49 -0
- package/src/packages/sdk-ts/package.json +2 -2
- package/src/packages/sdk-ts/src/generated/index.ts +1 -0
- package/src/packages/sdk-ts/src/generated/sdk.gen.ts +9 -8
- package/src/packages/sdk-ts/src/generated/types.gen.ts +91 -24
- package/src/packages/sdk-ts/src/index.ts +1 -0
- package/src/packages/sdk-ts/src/managed-mail-attachment.ts +107 -0
- package/src/packages/workos-auth-contracts/package.json +1 -1
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
// Adapted from cloudflare-postgres packages/cli at e05d9102c840e283602272ed0e16a95be3e3b8ea.
|
|
3
|
+
import { Transform } from "node:stream";
|
|
4
|
+
import type { TransformCallback } from "node:stream";
|
|
5
|
+
|
|
6
|
+
export const AUTH_BYTES = 64 * 1024;
|
|
7
|
+
const SSL_REQUEST = 80877103;
|
|
8
|
+
const GSS_REQUEST = 80877104;
|
|
9
|
+
|
|
10
|
+
export class BackendAuthentication extends Transform {
|
|
11
|
+
private frame = Buffer.alloc(AUTH_BYTES);
|
|
12
|
+
private length = 0;
|
|
13
|
+
private target = 5;
|
|
14
|
+
private offered = false;
|
|
15
|
+
private typed = false;
|
|
16
|
+
private preludes = 0;
|
|
17
|
+
authenticated = false;
|
|
18
|
+
|
|
19
|
+
constructor() {
|
|
20
|
+
super({ highWaterMark: AUTH_BYTES });
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
expectPrelude(): void {
|
|
24
|
+
if (this.typed || this.authenticated || this.preludes >= 2)
|
|
25
|
+
throw new Error("invalid_auth_prelude");
|
|
26
|
+
this.preludes++;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
private authentication(frame: Buffer): Buffer {
|
|
30
|
+
if (frame.length < 9) throw new Error("invalid_auth_frame");
|
|
31
|
+
const code = frame.readUInt32BE(5);
|
|
32
|
+
if (code === 3) throw new Error("cleartext_auth_refused");
|
|
33
|
+
if (code !== 0 && code !== 10 && code !== 11 && code !== 12)
|
|
34
|
+
throw new Error("unsupported_authentication");
|
|
35
|
+
if (code === 0) {
|
|
36
|
+
if (frame.length !== 9) throw new Error("invalid_auth_frame");
|
|
37
|
+
this.authenticated = true;
|
|
38
|
+
return frame;
|
|
39
|
+
}
|
|
40
|
+
if (code !== 10) return frame;
|
|
41
|
+
if (this.offered) throw new Error("duplicate_sasl_offer");
|
|
42
|
+
this.offered = true;
|
|
43
|
+
const bytes = frame.subarray(9);
|
|
44
|
+
if (bytes.length < 2 || bytes.at(-1) !== 0 || bytes.at(-2) !== 0)
|
|
45
|
+
throw new Error("invalid_sasl_offer");
|
|
46
|
+
const names = bytes.subarray(0, -2).toString("ascii").split("\0");
|
|
47
|
+
if (
|
|
48
|
+
bytes.some((value) => value > 127) ||
|
|
49
|
+
names.length > 16 ||
|
|
50
|
+
names.some((name) => !/^[A-Z0-9_-]{1,128}$/.test(name)) ||
|
|
51
|
+
new Set(names).size !== names.length ||
|
|
52
|
+
!names.includes("SCRAM-SHA-256")
|
|
53
|
+
)
|
|
54
|
+
throw new Error("invalid_sasl_offer");
|
|
55
|
+
const retained = names.filter((name) => name !== "SCRAM-SHA-256-PLUS");
|
|
56
|
+
if (retained.length === names.length) return frame;
|
|
57
|
+
const mechanisms = Buffer.from(retained.join("\0") + "\0\0");
|
|
58
|
+
const rewritten = Buffer.alloc(9 + mechanisms.length);
|
|
59
|
+
rewritten[0] = 0x52;
|
|
60
|
+
rewritten.writeUInt32BE(8 + mechanisms.length, 1);
|
|
61
|
+
rewritten.writeUInt32BE(10, 5);
|
|
62
|
+
mechanisms.copy(rewritten, 9);
|
|
63
|
+
return rewritten;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
override _transform(chunk: Buffer, _encoding: BufferEncoding, callback: TransformCallback): void {
|
|
67
|
+
try {
|
|
68
|
+
let offset = 0;
|
|
69
|
+
while (offset < chunk.length) {
|
|
70
|
+
if (this.authenticated) {
|
|
71
|
+
this.push(chunk.subarray(offset));
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
74
|
+
if (this.preludes && this.length === 0) {
|
|
75
|
+
if (chunk[offset] !== 0x4e) throw new Error("invalid_auth_prelude");
|
|
76
|
+
this.preludes--;
|
|
77
|
+
this.push(chunk.subarray(offset, ++offset));
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
this.typed = true;
|
|
81
|
+
const count = Math.min(this.target - this.length, chunk.length - offset);
|
|
82
|
+
chunk.copy(this.frame, this.length, offset, offset + count);
|
|
83
|
+
this.length += count;
|
|
84
|
+
offset += count;
|
|
85
|
+
if (this.length !== this.target) continue;
|
|
86
|
+
if (this.target === 5) {
|
|
87
|
+
const length = this.frame.readUInt32BE(1);
|
|
88
|
+
if (length < 4 || length + 1 > AUTH_BYTES) throw new Error("invalid_auth_frame");
|
|
89
|
+
this.target = length + 1;
|
|
90
|
+
if (this.length !== this.target) continue;
|
|
91
|
+
}
|
|
92
|
+
const frame = Buffer.from(this.frame.subarray(0, this.target));
|
|
93
|
+
this.push(frame[0] === 0x52 ? this.authentication(frame) : frame);
|
|
94
|
+
this.length = 0;
|
|
95
|
+
this.target = 5;
|
|
96
|
+
}
|
|
97
|
+
callback();
|
|
98
|
+
} catch (error) {
|
|
99
|
+
callback(error instanceof Error ? error : new Error("invalid_auth_frame"));
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
override _flush(callback: TransformCallback): void {
|
|
104
|
+
callback(this.length || this.preludes ? new Error("truncated_auth_frame") : undefined);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export class FrontendPrelude extends Transform {
|
|
109
|
+
private header = Buffer.alloc(8);
|
|
110
|
+
private length = 0;
|
|
111
|
+
private startup = false;
|
|
112
|
+
private count = 0;
|
|
113
|
+
private backend: BackendAuthentication;
|
|
114
|
+
|
|
115
|
+
constructor(backend: BackendAuthentication) {
|
|
116
|
+
super({ highWaterMark: AUTH_BYTES });
|
|
117
|
+
this.backend = backend;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
override _transform(chunk: Buffer, _encoding: BufferEncoding, callback: TransformCallback): void {
|
|
121
|
+
try {
|
|
122
|
+
let offset = 0;
|
|
123
|
+
while (offset < chunk.length) {
|
|
124
|
+
if (this.startup || this.backend.authenticated) {
|
|
125
|
+
if (this.length) {
|
|
126
|
+
this.push(Buffer.from(this.header.subarray(0, this.length)));
|
|
127
|
+
this.length = 0;
|
|
128
|
+
}
|
|
129
|
+
this.push(chunk.subarray(offset));
|
|
130
|
+
break;
|
|
131
|
+
}
|
|
132
|
+
const count = Math.min(8 - this.length, chunk.length - offset);
|
|
133
|
+
chunk.copy(this.header, this.length, offset, offset + count);
|
|
134
|
+
this.length += count;
|
|
135
|
+
offset += count;
|
|
136
|
+
if (this.length !== 8) continue;
|
|
137
|
+
const code = this.header.readUInt32BE(4);
|
|
138
|
+
if (code === SSL_REQUEST || code === GSS_REQUEST) {
|
|
139
|
+
if (this.header.readUInt32BE(0) !== 8 || ++this.count > 2)
|
|
140
|
+
throw new Error("invalid_frontend_prelude");
|
|
141
|
+
this.backend.expectPrelude();
|
|
142
|
+
} else this.startup = true;
|
|
143
|
+
this.push(Buffer.from(this.header));
|
|
144
|
+
this.length = 0;
|
|
145
|
+
}
|
|
146
|
+
callback();
|
|
147
|
+
} catch (error) {
|
|
148
|
+
callback(error instanceof Error ? error : new Error("invalid_frontend_prelude"));
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
override _flush(callback: TransformCallback): void {
|
|
153
|
+
if (this.backend.authenticated && this.length) {
|
|
154
|
+
this.push(Buffer.from(this.header.subarray(0, this.length)));
|
|
155
|
+
this.length = 0;
|
|
156
|
+
}
|
|
157
|
+
callback(this.length ? new Error("truncated_frontend_prelude") : undefined);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
// Adapted from cloudflare-postgres packages/cli at e05d9102c840e283602272ed0e16a95be3e3b8ea.
|
|
3
|
+
import { createServer } from "node:net";
|
|
4
|
+
import type { AddressInfo, Socket } from "node:net";
|
|
5
|
+
import type { Duplex } from "node:stream";
|
|
6
|
+
import WebSocket, { createWebSocketStream } from "ws";
|
|
7
|
+
import { upstreamUrl, validateOptions } from "./options.js";
|
|
8
|
+
import type { ConnectOptions } from "./options.js";
|
|
9
|
+
import { BackendAuthentication, FrontendPrelude } from "./auth.js";
|
|
10
|
+
|
|
11
|
+
export const STREAM_BYTES = 64 * 1024;
|
|
12
|
+
export const MESSAGE_BYTES = 1024 * 1024;
|
|
13
|
+
export const CLIENT_LIMIT = 16;
|
|
14
|
+
// The edge answers the upgrade only after a sleeping database is admitted, within its 60 s
|
|
15
|
+
// admission deadline (ADMISSION_DEADLINE_MS in @pgcf/contracts); the handshake waits for that plus margin.
|
|
16
|
+
export const HANDSHAKE_MS = 65_000;
|
|
17
|
+
export const FRAGMENT_LIMIT = 128;
|
|
18
|
+
export const HALF_CLOSE_MS = 10_000;
|
|
19
|
+
|
|
20
|
+
export interface Session {
|
|
21
|
+
local: Socket;
|
|
22
|
+
upstream: WebSocket;
|
|
23
|
+
stream: Duplex;
|
|
24
|
+
authentication: BackendAuthentication;
|
|
25
|
+
frontend: FrontendPrelude;
|
|
26
|
+
}
|
|
27
|
+
export type Connector = (url: string) => WebSocket;
|
|
28
|
+
export const UPSTREAM_OPTIONS: Readonly<WebSocket.ClientOptions> = Object.freeze({
|
|
29
|
+
rejectUnauthorized: true,
|
|
30
|
+
followRedirects: false,
|
|
31
|
+
perMessageDeflate: false,
|
|
32
|
+
autoPong: false,
|
|
33
|
+
allowSynchronousEvents: false,
|
|
34
|
+
maxFragments: FRAGMENT_LIMIT,
|
|
35
|
+
maxBufferedChunks: 256,
|
|
36
|
+
maxPayload: MESSAGE_BYTES,
|
|
37
|
+
handshakeTimeout: HANDSHAKE_MS,
|
|
38
|
+
});
|
|
39
|
+
export const connectUpstream: Connector = (url) => {
|
|
40
|
+
if (new URL(url).protocol !== "wss:") throw new Error("invalid_endpoint");
|
|
41
|
+
return new WebSocket(url, UPSTREAM_OPTIONS);
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export interface Bridge {
|
|
45
|
+
readonly host: string;
|
|
46
|
+
readonly port: number;
|
|
47
|
+
readonly clients: number;
|
|
48
|
+
close(): Promise<void>;
|
|
49
|
+
}
|
|
50
|
+
export interface BridgeHooks {
|
|
51
|
+
connect?: Connector;
|
|
52
|
+
session?: (value: Session) => void;
|
|
53
|
+
disconnected?: () => void;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export async function startBridge(raw: ConnectOptions, hooks: BridgeHooks = {}): Promise<Bridge> {
|
|
57
|
+
const options = validateOptions(raw);
|
|
58
|
+
const sessions = new Set<Session>();
|
|
59
|
+
const endings = new Set<Promise<void>>();
|
|
60
|
+
let closing: Promise<void> | undefined;
|
|
61
|
+
const server = createServer({ highWaterMark: STREAM_BYTES, allowHalfOpen: true }, (local) => {
|
|
62
|
+
local.on("error", () => {});
|
|
63
|
+
if (closing || sessions.size >= CLIENT_LIMIT) {
|
|
64
|
+
local.destroy();
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
let upstream: WebSocket;
|
|
68
|
+
try {
|
|
69
|
+
upstream = (hooks.connect ?? connectUpstream)(upstreamUrl(options));
|
|
70
|
+
} catch {
|
|
71
|
+
local.destroy();
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
const stream = createWebSocketStream(upstream, {
|
|
75
|
+
highWaterMark: STREAM_BYTES,
|
|
76
|
+
});
|
|
77
|
+
const authentication = new BackendAuthentication();
|
|
78
|
+
const frontend = new FrontendPrelude(authentication);
|
|
79
|
+
const session = { local, upstream, stream, authentication, frontend };
|
|
80
|
+
sessions.add(session);
|
|
81
|
+
const ended = new Promise<void>((resolve) => upstream.once("close", () => resolve()));
|
|
82
|
+
endings.add(ended);
|
|
83
|
+
void ended.then(() => endings.delete(ended));
|
|
84
|
+
let finished = false;
|
|
85
|
+
let halfCloseTimer: ReturnType<typeof setTimeout> | undefined;
|
|
86
|
+
const finish = () => {
|
|
87
|
+
if (finished) return;
|
|
88
|
+
finished = true;
|
|
89
|
+
if (halfCloseTimer) clearTimeout(halfCloseTimer);
|
|
90
|
+
local.unpipe(frontend);
|
|
91
|
+
frontend.unpipe(stream);
|
|
92
|
+
stream.unpipe(authentication);
|
|
93
|
+
authentication.unpipe(local);
|
|
94
|
+
local.destroy();
|
|
95
|
+
frontend.destroy();
|
|
96
|
+
authentication.destroy();
|
|
97
|
+
stream.destroy();
|
|
98
|
+
upstream.terminate();
|
|
99
|
+
sessions.delete(session);
|
|
100
|
+
hooks.disconnected?.();
|
|
101
|
+
};
|
|
102
|
+
stream.on("error", finish);
|
|
103
|
+
authentication.on("error", finish);
|
|
104
|
+
frontend.on("error", finish);
|
|
105
|
+
upstream.on("error", finish);
|
|
106
|
+
local.once("error", finish);
|
|
107
|
+
local.once("close", finish);
|
|
108
|
+
local.once("end", () => {
|
|
109
|
+
if (finished) return;
|
|
110
|
+
// WebSocket has no half-close: retain its readable side while queued bytes drain.
|
|
111
|
+
halfCloseTimer = setTimeout(
|
|
112
|
+
finish,
|
|
113
|
+
HALF_CLOSE_MS + (upstream.readyState === WebSocket.CONNECTING ? HANDSHAKE_MS : 0),
|
|
114
|
+
);
|
|
115
|
+
});
|
|
116
|
+
stream.once("close", finish);
|
|
117
|
+
upstream.once("close", () => {
|
|
118
|
+
// Let the readable stream flush admitted bytes before ending local TCP.
|
|
119
|
+
if (local.destroyed) finish();
|
|
120
|
+
});
|
|
121
|
+
authentication.once("end", () => local.end());
|
|
122
|
+
upstream.prependListener("message", (_data, binary) => {
|
|
123
|
+
if (!binary) finish();
|
|
124
|
+
});
|
|
125
|
+
upstream.on("ping", (data) => {
|
|
126
|
+
if (upstream.bufferedAmount >= STREAM_BYTES) {
|
|
127
|
+
finish();
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
upstream.pong(data, true, (error) => {
|
|
131
|
+
if (error) finish();
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
local.setNoDelay(true);
|
|
135
|
+
hooks.session?.(session);
|
|
136
|
+
local.pipe(frontend);
|
|
137
|
+
frontend.pipe(stream, { end: false });
|
|
138
|
+
stream.pipe(authentication).pipe(local);
|
|
139
|
+
});
|
|
140
|
+
server.maxConnections = CLIENT_LIMIT;
|
|
141
|
+
await new Promise<void>((resolve, reject) => {
|
|
142
|
+
const failed = () => reject(new Error("listener_failed"));
|
|
143
|
+
server.once("error", failed);
|
|
144
|
+
server.listen(options.port, [127, 0, 0, 1].join("."), () => {
|
|
145
|
+
server.removeListener("error", failed);
|
|
146
|
+
resolve();
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
const address = server.address() as AddressInfo;
|
|
150
|
+
const bridge: Bridge = {
|
|
151
|
+
host: address.address,
|
|
152
|
+
port: address.port,
|
|
153
|
+
get clients() {
|
|
154
|
+
return sessions.size;
|
|
155
|
+
},
|
|
156
|
+
close() {
|
|
157
|
+
closing ??= (async () => {
|
|
158
|
+
const listener = new Promise<void>((resolve) => server.close(() => resolve()));
|
|
159
|
+
for (const session of [...sessions]) session.local.destroy();
|
|
160
|
+
await listener;
|
|
161
|
+
await Promise.all([...endings]);
|
|
162
|
+
})();
|
|
163
|
+
return closing;
|
|
164
|
+
},
|
|
165
|
+
};
|
|
166
|
+
server.on("error", bridge.close);
|
|
167
|
+
return bridge;
|
|
168
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
const isDatabaseId = (value: string) => /^[a-z][a-z0-9]{19}$/u.test(value);
|
|
3
|
+
const isRoleName = (value: string) => /^[a-z][a-z0-9_]{0,62}$/u.test(value);
|
|
4
|
+
|
|
5
|
+
export interface ConnectOptions {
|
|
6
|
+
endpoint: string;
|
|
7
|
+
database: string;
|
|
8
|
+
user: string;
|
|
9
|
+
port: number;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function validateOptions(value: ConnectOptions): ConnectOptions {
|
|
13
|
+
let endpoint: URL;
|
|
14
|
+
try {
|
|
15
|
+
endpoint = new URL(value.endpoint);
|
|
16
|
+
} catch {
|
|
17
|
+
throw new Error("invalid_endpoint");
|
|
18
|
+
}
|
|
19
|
+
if (
|
|
20
|
+
endpoint.protocol !== "wss:" ||
|
|
21
|
+
!endpoint.hostname ||
|
|
22
|
+
endpoint.username ||
|
|
23
|
+
endpoint.password ||
|
|
24
|
+
endpoint.search ||
|
|
25
|
+
endpoint.hash ||
|
|
26
|
+
endpoint.pathname !== "/" ||
|
|
27
|
+
value.endpoint.length > 2048 ||
|
|
28
|
+
/[\s\\?#@]/.test(value.endpoint)
|
|
29
|
+
)
|
|
30
|
+
throw new Error("invalid_endpoint");
|
|
31
|
+
if (!isDatabaseId(value.database)) throw new Error("invalid_database");
|
|
32
|
+
if (!isRoleName(value.user)) throw new Error("invalid_user");
|
|
33
|
+
if (!Number.isInteger(value.port) || value.port < 0 || value.port > 65535)
|
|
34
|
+
throw new Error("invalid_port");
|
|
35
|
+
return {
|
|
36
|
+
endpoint: endpoint.href,
|
|
37
|
+
database: value.database,
|
|
38
|
+
user: value.user,
|
|
39
|
+
port: value.port,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function upstreamUrl(options: ConnectOptions): string {
|
|
44
|
+
const url = new URL(options.endpoint);
|
|
45
|
+
url.pathname = "/v2";
|
|
46
|
+
url.searchParams.set("database", options.database);
|
|
47
|
+
url.searchParams.set("user", options.user);
|
|
48
|
+
return url.href;
|
|
49
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ohmyhost/sdk-ts",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.39",
|
|
4
4
|
"private": true,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"build": "tsc --project tsconfig.build.json",
|
|
15
15
|
"check": "pnpm --dir ../.. sdk-ts:check",
|
|
16
16
|
"generate": "pnpm --dir ../.. sdk-ts:generate",
|
|
17
|
-
"typecheck": "tsc --project tsconfig.json --noEmit",
|
|
17
|
+
"typecheck": "tsc --project tsconfig.json --noEmit --incremental --tsBuildInfoFile typecheck.tsbuildinfo",
|
|
18
18
|
"test": "pnpm run check && vitest run --root ../.. packages/sdk-ts"
|
|
19
19
|
},
|
|
20
20
|
"license": "Apache-2.0"
|
|
@@ -4441,18 +4441,21 @@ export const getDatabaseCompute = <ThrowOnError extends boolean = true>(
|
|
|
4441
4441
|
};
|
|
4442
4442
|
|
|
4443
4443
|
/**
|
|
4444
|
-
* Select
|
|
4444
|
+
* Select database memory and included storage
|
|
4445
4445
|
*
|
|
4446
|
-
* Requires project-write access and explicit confirmation.
|
|
4446
|
+
* Requires project-write access and explicit confirmation. Select 256, 512, 1024, 2048 or 4096 MiB RAM; Free permits only 256 MiB. Included total storage is respectively 500, 1024, 2048, 4096 or 8192 MiB, including PostgreSQL and WAL. Seven-day backups are included. Each 256 MiB costs 10 credits per active hour, prorated by time; sleeping costs no active RAM credits. Shared Dev/Prod data changes both environments. A smaller volume requires a separate data migration and is refused here. A brief connection interruption is possible. Poll the original operation until observed completion. The legacy profile input is accepted only for databases still on the transitional provider; it does not imply PGCF autoscaling.
|
|
4447
4447
|
*/
|
|
4448
4448
|
export const changeDatabaseCompute = <ThrowOnError extends boolean = true>(
|
|
4449
4449
|
parameters: {
|
|
4450
4450
|
"X-Request-Id"?: string;
|
|
4451
4451
|
"Idempotency-Key": string;
|
|
4452
4452
|
project_id: Ulid;
|
|
4453
|
-
|
|
4454
|
-
|
|
4455
|
-
|
|
4453
|
+
body: unknown & {
|
|
4454
|
+
environment: "dev" | "prod";
|
|
4455
|
+
profile?: "standard" | "performance";
|
|
4456
|
+
memory_mib?: 256 | 512 | 1024 | 2048 | 4096;
|
|
4457
|
+
confirm: true;
|
|
4458
|
+
};
|
|
4456
4459
|
},
|
|
4457
4460
|
options?: Options<never, ThrowOnError>,
|
|
4458
4461
|
): RequestResult<
|
|
@@ -4469,9 +4472,7 @@ export const changeDatabaseCompute = <ThrowOnError extends boolean = true>(
|
|
|
4469
4472
|
{ in: "headers", key: "X-Request-Id" },
|
|
4470
4473
|
{ in: "headers", key: "Idempotency-Key" },
|
|
4471
4474
|
{ in: "path", key: "project_id" },
|
|
4472
|
-
{
|
|
4473
|
-
{ in: "body", key: "profile" },
|
|
4474
|
-
{ in: "body", key: "confirm" },
|
|
4475
|
+
{ key: "body", map: "body" },
|
|
4475
4476
|
],
|
|
4476
4477
|
},
|
|
4477
4478
|
],
|
|
@@ -376,9 +376,9 @@ export type PublishedCreditRateCard = {
|
|
|
376
376
|
unit: string;
|
|
377
377
|
units_per_charge: string;
|
|
378
378
|
/**
|
|
379
|
-
* Provider list-cost basis for units_per_charge, in USD micro-units (1000000 equals 1 USD), before shared allowances and discounts.
|
|
379
|
+
* Provider list-cost basis for units_per_charge, in USD micro-units (1000000 equals 1 USD), before shared allowances and discounts. Null for the PGCF RAM tariff means node infrastructure costs are tracked separately and are not allocated to this customer rate; null never means zero cost.
|
|
380
380
|
*/
|
|
381
|
-
provider_cost_micros: string;
|
|
381
|
+
provider_cost_micros: string | null;
|
|
382
382
|
credit_micros: CreditMicros;
|
|
383
383
|
}>;
|
|
384
384
|
};
|
|
@@ -894,13 +894,29 @@ export type ProjectDatabaseAccessRequest = {
|
|
|
894
894
|
label?: string | null;
|
|
895
895
|
};
|
|
896
896
|
|
|
897
|
-
export type ProjectDatabaseAccess =
|
|
897
|
+
export type ProjectDatabaseAccess = (
|
|
898
|
+
| {
|
|
899
|
+
database?: "neondb";
|
|
900
|
+
}
|
|
901
|
+
| {
|
|
902
|
+
database?: unknown;
|
|
903
|
+
}
|
|
904
|
+
) & {
|
|
898
905
|
access_id: Ulid;
|
|
899
906
|
environment: "dev" | "prod";
|
|
900
907
|
mode: "read" | "write";
|
|
901
908
|
role_name: string;
|
|
902
909
|
host: string;
|
|
903
|
-
|
|
910
|
+
/**
|
|
911
|
+
* neondb for a transitional Neon credential; the exact 20-character PGCF database identity for WSS.
|
|
912
|
+
*/
|
|
913
|
+
database: string;
|
|
914
|
+
provider?: "pgcf";
|
|
915
|
+
transport?: "wss";
|
|
916
|
+
/**
|
|
917
|
+
* Verified WSS origin ending in / and matching host. The local bridge adds /v2 and the bound database and user; no public PostgreSQL TCP endpoint.
|
|
918
|
+
*/
|
|
919
|
+
websocket_url?: string;
|
|
904
920
|
label: string | null;
|
|
905
921
|
state: "active" | "expired" | "revoked";
|
|
906
922
|
issued_at: string;
|
|
@@ -909,13 +925,41 @@ export type ProjectDatabaseAccess = {
|
|
|
909
925
|
revocation_reason: "principal" | "expired" | "budget" | "creation_failed" | "data_change" | null;
|
|
910
926
|
};
|
|
911
927
|
|
|
912
|
-
export type ProjectDatabaseAccessCredential =
|
|
928
|
+
export type ProjectDatabaseAccessCredential = (
|
|
929
|
+
| {
|
|
930
|
+
database?: "neondb";
|
|
931
|
+
}
|
|
932
|
+
| {
|
|
933
|
+
database?: unknown;
|
|
934
|
+
}
|
|
935
|
+
) & {
|
|
936
|
+
access_id: Ulid;
|
|
937
|
+
environment: "dev" | "prod";
|
|
938
|
+
mode: "read" | "write";
|
|
939
|
+
role_name: string;
|
|
940
|
+
host: string;
|
|
913
941
|
/**
|
|
914
|
-
*
|
|
942
|
+
* neondb for a transitional Neon credential; the exact 20-character PGCF database identity for WSS.
|
|
943
|
+
*/
|
|
944
|
+
database: string;
|
|
945
|
+
provider?: "pgcf";
|
|
946
|
+
transport?: "wss";
|
|
947
|
+
/**
|
|
948
|
+
* Verified WSS origin ending in / and matching host. The local bridge adds /v2 and the bound database and user; no public PostgreSQL TCP endpoint.
|
|
949
|
+
*/
|
|
950
|
+
websocket_url?: string;
|
|
951
|
+
label: string | null;
|
|
952
|
+
state: "active" | "expired" | "revoked";
|
|
953
|
+
issued_at: string;
|
|
954
|
+
expires_at: string;
|
|
955
|
+
revoked_at: string | null;
|
|
956
|
+
revocation_reason: "principal" | "expired" | "budget" | "creation_failed" | "data_change" | null;
|
|
957
|
+
/**
|
|
958
|
+
* Returned once as credential material. For PGCF this identifies a WSS transport, never direct public TCP. Never place it in command arguments, files, notes or source.
|
|
915
959
|
*/
|
|
916
960
|
connection_uri: string;
|
|
917
961
|
/**
|
|
918
|
-
*
|
|
962
|
+
* For PGCF, a secret-free ohmyhost database psql invocation that obtains and revokes its own session credential. Transitional Neon responses retain their original direct invocation.
|
|
919
963
|
*/
|
|
920
964
|
psql_command: string;
|
|
921
965
|
};
|
|
@@ -1102,7 +1146,7 @@ export type Sha256Digest = string;
|
|
|
1102
1146
|
export type ProjectDataMode = "shared" | "isolated";
|
|
1103
1147
|
|
|
1104
1148
|
/**
|
|
1105
|
-
* Chosen
|
|
1149
|
+
* Chosen at project creation and immutable through customer operations. us (default) or eu places new build resources and, when no storage is already assigned, new file buckets. Existing buckets, source repositories and historical artifacts retain their recorded locations independently of a platform-authorized project-region migration. New PGCF application databases follow the selected project region; existing owner-authorized migrations target US1. Read database compute for the actual database region. storage.jurisdiction must match the project's recorded storage location, or this region when no storage custody exists. Project context reports the authoritative storage jurisdiction. Prices are identical in both project regions.
|
|
1106
1150
|
*/
|
|
1107
1151
|
export type HostingRegion = "us" | "eu";
|
|
1108
1152
|
|
|
@@ -1362,6 +1406,8 @@ export type ProblemDetails = {
|
|
|
1362
1406
|
| "paid_plan_required"
|
|
1363
1407
|
| "project_budget_exceeded"
|
|
1364
1408
|
| "compute_performance_paid_required"
|
|
1409
|
+
| "compute_paid_required"
|
|
1410
|
+
| "compute_storage_shrink_requires_migration"
|
|
1365
1411
|
| "compute_performance_unavailable"
|
|
1366
1412
|
| "database_write_pending"
|
|
1367
1413
|
| "database_access_limit"
|
|
@@ -1486,20 +1532,40 @@ export type DatabaseCompute = {
|
|
|
1486
1532
|
environment: "dev" | "prod";
|
|
1487
1533
|
data_mode: "shared" | "isolated";
|
|
1488
1534
|
observed_at: string;
|
|
1489
|
-
database:
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1535
|
+
database:
|
|
1536
|
+
| null
|
|
1537
|
+
| FixedDatabaseCompute
|
|
1538
|
+
| {
|
|
1539
|
+
min_cu: number;
|
|
1540
|
+
max_cu: number;
|
|
1541
|
+
min_memory_gb: number;
|
|
1542
|
+
max_memory_gb: number;
|
|
1543
|
+
/**
|
|
1544
|
+
* Configured timeout: 0 is provider default; -1 disables suspension.
|
|
1545
|
+
*/
|
|
1546
|
+
suspend_timeout_seconds: number;
|
|
1547
|
+
state: "init" | "active" | "idle";
|
|
1548
|
+
pending_state: "init" | "active" | "idle" | null;
|
|
1549
|
+
disabled: boolean;
|
|
1550
|
+
region: string;
|
|
1551
|
+
};
|
|
1552
|
+
};
|
|
1553
|
+
|
|
1554
|
+
/**
|
|
1555
|
+
* Fixed resources; storage includes engine and WAL overhead. First provisioning is distinct from a sleeping database.
|
|
1556
|
+
*/
|
|
1557
|
+
export type FixedDatabaseCompute = {
|
|
1558
|
+
provider: "pgcf";
|
|
1559
|
+
size_class_id: "r256-s500m" | "r512-s1g" | "r1024-s2g" | "r2048-s4g" | "r4096-s8g";
|
|
1560
|
+
memory_mib: 256 | 512 | 1024 | 2048 | 4096;
|
|
1561
|
+
cpu_millicores: 250 | 500 | 1000 | 2000;
|
|
1562
|
+
storage_mib: 500 | 1024 | 2048 | 4096 | 8192;
|
|
1563
|
+
max_connections: 20 | 40 | 80 | 160 | 200;
|
|
1564
|
+
suspend_timeout_seconds: number;
|
|
1565
|
+
state: "provisioning" | "cold" | "warm" | "suspended" | "error";
|
|
1566
|
+
pending_state: "provisioning" | "cold" | "warm" | "suspended" | "error" | null;
|
|
1567
|
+
disabled: boolean;
|
|
1568
|
+
region: "us" | "eu";
|
|
1503
1569
|
};
|
|
1504
1570
|
|
|
1505
1571
|
export type ProjectContext = {
|
|
@@ -7457,9 +7523,10 @@ export type GetDatabaseComputeResponse =
|
|
|
7457
7523
|
GetDatabaseComputeResponses[keyof GetDatabaseComputeResponses];
|
|
7458
7524
|
|
|
7459
7525
|
export type ChangeDatabaseComputeData = {
|
|
7460
|
-
body: {
|
|
7526
|
+
body: unknown & {
|
|
7461
7527
|
environment: "dev" | "prod";
|
|
7462
|
-
profile
|
|
7528
|
+
profile?: "standard" | "performance";
|
|
7529
|
+
memory_mib?: 256 | 512 | 1024 | 2048 | 4096;
|
|
7463
7530
|
confirm: true;
|
|
7464
7531
|
};
|
|
7465
7532
|
headers: {
|