@coffre/db 0.14.4 → 0.15.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/README.md +2 -1
- package/dist/change-records-_fKvahvs.js +55 -0
- package/dist/connect.d.ts +22 -10
- package/dist/connect.js +74 -12
- package/dist/database-CVteLhp1.d.ts +410 -0
- package/dist/{database-BadNeeN-.js → database-D55ENPX-.js} +46 -25
- package/dist/dialect.d.ts +4 -3
- package/dist/dialect.js +14 -5
- package/dist/grants.d.ts +4 -4
- package/dist/grants.js +4 -4
- package/dist/hyperdrive.d.ts +48 -23
- package/dist/hyperdrive.js +168 -91
- package/dist/index.d.ts +3 -3
- package/dist/index.js +1 -1
- package/dist/log-DaD7fyUq.d.ts +722 -0
- package/dist/log.d.ts +2 -596
- package/dist/log.js +224 -10
- package/dist/migrate.d.ts +43 -3
- package/dist/migrate.js +149 -29
- package/dist/migrations/postgres/0021_prune_vault_releases.sql +19 -0
- package/dist/migrations/postgres/0022_log_015.sql +672 -0
- package/dist/migrations/postgres/0023_change_path.sql +153 -0
- package/dist/migrations/postgres/0024_use_015.sql +556 -0
- package/dist/migrations/postgres/0025_app_change.sql +221 -0
- package/dist/migrations/postgres/0026_issue_lock.sql +106 -0
- package/dist/migrations/postgres/0027_retention.sql +118 -0
- package/dist/migrations/postgres/0028_use_stalls.sql +594 -0
- package/dist/migrations/postgres/0029_use_objects.sql +887 -0
- package/dist/migrations/postgres/0030_unarchive_restores.sql +259 -0
- package/dist/migrations/postgres/0031_app_read.sql +65 -0
- package/dist/migrations/postgres/0032_secrets_live_index.sql +4 -0
- package/dist/migrations/postgres/0033_use_generations.sql +582 -0
- package/dist/migrations/postgres/0034_vault_credentials.sql +153 -0
- package/dist/migrations/postgres/0035_first_days.sql +3 -0
- package/dist/migrations/postgres/0036_vault_issues.sql +91 -0
- package/dist/migrations/postgres/0037_vault_bindings.sql +271 -0
- package/dist/migrations/postgres/0040_binding_origin.sql +159 -0
- package/dist/migrations/postgres/0041_cutover.sql +132 -0
- package/dist/migrations/postgres/0042_sign_in_index.sql +7 -0
- package/dist/migrations/postgres/0043_issue_revoke_index.sql +4 -0
- package/dist/migrations/postgres/0044_use_by_name.sql +705 -0
- package/dist/migrations/postgres/0045_decide_first.sql +878 -0
- package/dist/migrations/postgres/0046_way_in_uses.sql +1149 -0
- package/dist/migrations/postgres/0047_cutover_finish.sql +268 -0
- package/dist/migrations/postgres/0048_use_batch.sql +59 -0
- package/dist/migrations/postgres/0049_reads.sql +171 -0
- package/dist/migrations/postgres/meta/0021_snapshot.json +3334 -0
- package/dist/migrations/postgres/meta/0022_snapshot.json +3831 -0
- package/dist/migrations/postgres/meta/0023_snapshot.json +3831 -0
- package/dist/migrations/postgres/meta/0024_snapshot.json +3831 -0
- package/dist/migrations/postgres/meta/0025_snapshot.json +3831 -0
- package/dist/migrations/postgres/meta/0026_snapshot.json +3831 -0
- package/dist/migrations/postgres/meta/0027_snapshot.json +3853 -0
- package/dist/migrations/postgres/meta/0028_snapshot.json +3853 -0
- package/dist/migrations/postgres/meta/0029_snapshot.json +3859 -0
- package/dist/migrations/postgres/meta/0030_snapshot.json +3859 -0
- package/dist/migrations/postgres/meta/0031_snapshot.json +3859 -0
- package/dist/migrations/postgres/meta/0032_snapshot.json +3875 -0
- package/dist/migrations/postgres/meta/0033_snapshot.json +3875 -0
- package/dist/migrations/postgres/meta/0034_snapshot.json +3875 -0
- package/dist/migrations/postgres/meta/0035_snapshot.json +3875 -0
- package/dist/migrations/postgres/meta/0036_snapshot.json +3875 -0
- package/dist/migrations/postgres/meta/0037_snapshot.json +3875 -0
- package/dist/migrations/postgres/meta/0038_snapshot.json +3881 -0
- package/dist/migrations/postgres/meta/0039_snapshot.json +3881 -0
- package/dist/migrations/postgres/meta/0040_snapshot.json +3931 -0
- package/dist/migrations/postgres/meta/0041_snapshot.json +3981 -0
- package/dist/migrations/postgres/meta/0042_snapshot.json +3981 -0
- package/dist/migrations/postgres/meta/0043_snapshot.json +3981 -0
- package/dist/migrations/postgres/meta/0044_snapshot.json +3991 -0
- package/dist/migrations/postgres/meta/0045_snapshot.json +3997 -0
- package/dist/migrations/postgres/meta/0046_snapshot.json +3997 -0
- package/dist/migrations/postgres/meta/0047_snapshot.json +4234 -0
- package/dist/migrations/postgres/meta/_journal.json +189 -0
- package/dist/migrations/sqlite/0018_log_015.sql +84 -0
- package/dist/migrations/sqlite/0019_retention.sql +2 -0
- package/dist/migrations/sqlite/0020_ended_references.sql +1 -0
- package/dist/migrations/sqlite/0021_secrets_live_index.sql +1 -0
- package/dist/migrations/sqlite/0022_binding_origin.sql +1 -0
- package/dist/migrations/sqlite/0023_sign_in_index.sql +2 -0
- package/dist/migrations/sqlite/0024_issue_revoke_index.sql +2 -0
- package/dist/migrations/sqlite/0025_way_in_uses.sql +42 -0
- package/dist/migrations/sqlite/0026_cutover_finish.sql +11 -0
- package/dist/migrations/sqlite/0027_reads.sql +38 -0
- package/dist/migrations/sqlite/meta/0018_snapshot.json +3529 -0
- package/dist/migrations/sqlite/meta/0019_snapshot.json +3538 -0
- package/dist/migrations/sqlite/meta/0020_snapshot.json +3545 -0
- package/dist/migrations/sqlite/meta/0021_snapshot.json +3553 -0
- package/dist/migrations/sqlite/meta/0022_snapshot.json +3560 -0
- package/dist/migrations/sqlite/meta/0023_snapshot.json +3579 -0
- package/dist/migrations/sqlite/meta/0024_snapshot.json +3598 -0
- package/dist/migrations/sqlite/meta/0025_snapshot.json +3609 -0
- package/dist/migrations/sqlite/meta/0026_snapshot.json +3616 -0
- package/dist/migrations/sqlite/meta/0027_snapshot.json +3834 -0
- package/dist/migrations/sqlite/meta/_journal.json +70 -0
- package/dist/platform-Y3gICK6n.d.ts +58 -0
- package/dist/portable.d.ts +2 -2
- package/dist/postgres-BI3xTnyw.js +1718 -0
- package/dist/postgres-platform-node.d.ts +14 -0
- package/dist/postgres-platform-node.js +21 -0
- package/dist/postgres-platform-workerd.d.ts +61 -0
- package/dist/postgres-platform-workerd.js +163 -0
- package/dist/{schema-z8GcN_Xu.d.ts → schema-DgoPqqsI.d.ts} +1329 -9
- package/dist/schema-sqlite.d.ts +1308 -8
- package/dist/schema-sqlite.js +156 -20
- package/dist/schema-version.d.ts +1 -1
- package/dist/schema-version.js +261 -2
- package/dist/schema.d.ts +2 -2
- package/dist/schema.js +229 -22
- package/package.json +13 -5
- package/src/active-subject.ts +9 -0
- package/src/change-records.ts +38 -0
- package/src/connect.ts +67 -13
- package/src/database.ts +67 -51
- package/src/dialect.ts +17 -8
- package/src/grants.ts +3 -3
- package/src/hyperdrive.ts +181 -104
- package/src/log.ts +244 -11
- package/src/migrate.ts +157 -30
- package/src/migrations/postgres/0021_prune_vault_releases.sql +19 -0
- package/src/migrations/postgres/0022_log_015.sql +672 -0
- package/src/migrations/postgres/0023_change_path.sql +153 -0
- package/src/migrations/postgres/0024_use_015.sql +556 -0
- package/src/migrations/postgres/0025_app_change.sql +221 -0
- package/src/migrations/postgres/0026_issue_lock.sql +106 -0
- package/src/migrations/postgres/0027_retention.sql +118 -0
- package/src/migrations/postgres/0028_use_stalls.sql +594 -0
- package/src/migrations/postgres/0029_use_objects.sql +887 -0
- package/src/migrations/postgres/0030_unarchive_restores.sql +259 -0
- package/src/migrations/postgres/0031_app_read.sql +65 -0
- package/src/migrations/postgres/0032_secrets_live_index.sql +4 -0
- package/src/migrations/postgres/0033_use_generations.sql +582 -0
- package/src/migrations/postgres/0034_vault_credentials.sql +153 -0
- package/src/migrations/postgres/0035_first_days.sql +3 -0
- package/src/migrations/postgres/0036_vault_issues.sql +91 -0
- package/src/migrations/postgres/0037_vault_bindings.sql +271 -0
- package/src/migrations/postgres/0040_binding_origin.sql +159 -0
- package/src/migrations/postgres/0041_cutover.sql +132 -0
- package/src/migrations/postgres/0042_sign_in_index.sql +7 -0
- package/src/migrations/postgres/0043_issue_revoke_index.sql +4 -0
- package/src/migrations/postgres/0044_use_by_name.sql +705 -0
- package/src/migrations/postgres/0045_decide_first.sql +878 -0
- package/src/migrations/postgres/0046_way_in_uses.sql +1149 -0
- package/src/migrations/postgres/0047_cutover_finish.sql +268 -0
- package/src/migrations/postgres/0048_use_batch.sql +59 -0
- package/src/migrations/postgres/0049_reads.sql +171 -0
- package/src/migrations/postgres/meta/0021_snapshot.json +3334 -0
- package/src/migrations/postgres/meta/0022_snapshot.json +3831 -0
- package/src/migrations/postgres/meta/0023_snapshot.json +3831 -0
- package/src/migrations/postgres/meta/0024_snapshot.json +3831 -0
- package/src/migrations/postgres/meta/0025_snapshot.json +3831 -0
- package/src/migrations/postgres/meta/0026_snapshot.json +3831 -0
- package/src/migrations/postgres/meta/0027_snapshot.json +3853 -0
- package/src/migrations/postgres/meta/0028_snapshot.json +3853 -0
- package/src/migrations/postgres/meta/0029_snapshot.json +3859 -0
- package/src/migrations/postgres/meta/0030_snapshot.json +3859 -0
- package/src/migrations/postgres/meta/0031_snapshot.json +3859 -0
- package/src/migrations/postgres/meta/0032_snapshot.json +3875 -0
- package/src/migrations/postgres/meta/0033_snapshot.json +3875 -0
- package/src/migrations/postgres/meta/0034_snapshot.json +3875 -0
- package/src/migrations/postgres/meta/0035_snapshot.json +3875 -0
- package/src/migrations/postgres/meta/0036_snapshot.json +3875 -0
- package/src/migrations/postgres/meta/0037_snapshot.json +3875 -0
- package/src/migrations/postgres/meta/0038_snapshot.json +3881 -0
- package/src/migrations/postgres/meta/0039_snapshot.json +3881 -0
- package/src/migrations/postgres/meta/0040_snapshot.json +3931 -0
- package/src/migrations/postgres/meta/0041_snapshot.json +3981 -0
- package/src/migrations/postgres/meta/0042_snapshot.json +3981 -0
- package/src/migrations/postgres/meta/0043_snapshot.json +3981 -0
- package/src/migrations/postgres/meta/0044_snapshot.json +3991 -0
- package/src/migrations/postgres/meta/0045_snapshot.json +3997 -0
- package/src/migrations/postgres/meta/0046_snapshot.json +3997 -0
- package/src/migrations/postgres/meta/0047_snapshot.json +4234 -0
- package/src/migrations/postgres/meta/_journal.json +189 -0
- package/src/migrations/sqlite/0018_log_015.sql +84 -0
- package/src/migrations/sqlite/0019_retention.sql +2 -0
- package/src/migrations/sqlite/0020_ended_references.sql +1 -0
- package/src/migrations/sqlite/0021_secrets_live_index.sql +1 -0
- package/src/migrations/sqlite/0022_binding_origin.sql +1 -0
- package/src/migrations/sqlite/0023_sign_in_index.sql +2 -0
- package/src/migrations/sqlite/0024_issue_revoke_index.sql +2 -0
- package/src/migrations/sqlite/0025_way_in_uses.sql +42 -0
- package/src/migrations/sqlite/0026_cutover_finish.sql +11 -0
- package/src/migrations/sqlite/0027_reads.sql +38 -0
- package/src/migrations/sqlite/meta/0018_snapshot.json +3529 -0
- package/src/migrations/sqlite/meta/0019_snapshot.json +3538 -0
- package/src/migrations/sqlite/meta/0020_snapshot.json +3545 -0
- package/src/migrations/sqlite/meta/0021_snapshot.json +3553 -0
- package/src/migrations/sqlite/meta/0022_snapshot.json +3560 -0
- package/src/migrations/sqlite/meta/0023_snapshot.json +3579 -0
- package/src/migrations/sqlite/meta/0024_snapshot.json +3598 -0
- package/src/migrations/sqlite/meta/0025_snapshot.json +3609 -0
- package/src/migrations/sqlite/meta/0026_snapshot.json +3616 -0
- package/src/migrations/sqlite/meta/0027_snapshot.json +3834 -0
- package/src/migrations/sqlite/meta/_journal.json +70 -0
- package/src/postgres-js/README.md +183 -0
- package/src/postgres-js/UNLICENSE +24 -0
- package/src/postgres-js/bytes.ts +89 -0
- package/src/postgres-js/connection.ts +882 -0
- package/src/postgres-js/errors.ts +84 -0
- package/src/postgres-js/index.ts +516 -0
- package/src/postgres-js/platform.node.ts +29 -0
- package/src/postgres-js/platform.ts +60 -0
- package/src/postgres-js/platform.workerd.ts +225 -0
- package/src/postgres-js/query.ts +123 -0
- package/src/postgres-js/queue.ts +42 -0
- package/src/postgres-js/result.ts +46 -0
- package/src/postgres-js/types.ts +52 -0
- package/src/postgres.ts +375 -26
- package/src/schema-version.ts +2 -2
- package/src/schema.sqlite.ts +200 -9
- package/src/schema.ts +294 -8
- package/src/verify-runtime.ts +18 -29
- package/LICENSE +0 -21
- package/dist/database-CFhTnnzM.d.ts +0 -73
- package/dist/postgres-Bmar8y0q.js +0 -29
- package/dist/relations-BfR3JjT1.js +0 -38
- package/src/relations.ts +0 -40
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// What the driver needs of where it runs, and nothing else: a TCP socket, its
|
|
2
|
+
// TLS upgrade, a few hashes, and a timer. Two modules give it, with exactly
|
|
3
|
+
// these exports: platform.node.ts (Node's own modules) and platform.workerd.ts
|
|
4
|
+
// (`cloudflare:sockets` and WebCrypto). `@coffre/db`'s `#postgres/platform`
|
|
5
|
+
// import picks one where the package runs: workerd, as wrangler and Vite
|
|
6
|
+
// resolve it for a Worker, or Node, the default. The driver imports
|
|
7
|
+
// `#postgres/platform` alone; this file holds the types both satisfy.
|
|
8
|
+
import type { Buffer } from 'node:buffer';
|
|
9
|
+
import type { ConnectionOptions } from 'node:tls';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* What the connection speaks Postgres on: a TCP socket, a TLS one, or a
|
|
13
|
+
* stream the caller made (`socket`, a tunnel's). A Node `Duplex` is one.
|
|
14
|
+
*/
|
|
15
|
+
export interface Socket {
|
|
16
|
+
/** Node's states for a socket; a stream that has none never says it is open, so no Terminate is written to it. */
|
|
17
|
+
readonly readyState?: string;
|
|
18
|
+
on(event: 'data', listener: (chunk: Buffer) => void): unknown;
|
|
19
|
+
on(event: 'error', listener: (error: Error) => void): unknown;
|
|
20
|
+
on(event: 'close', listener: (hadError?: boolean) => void): unknown;
|
|
21
|
+
on(event: 'connect' | 'secureConnect' | 'drain', listener: () => void): unknown;
|
|
22
|
+
once(event: 'data', listener: (chunk: Buffer) => void): unknown;
|
|
23
|
+
once(event: 'close', listener: () => void): unknown;
|
|
24
|
+
removeListener(event: 'data', listener: (chunk: Buffer) => void): unknown;
|
|
25
|
+
removeListener(event: 'connect', listener: () => void): unknown;
|
|
26
|
+
removeAllListeners(): unknown;
|
|
27
|
+
write(data: Uint8Array, callback?: () => void): boolean;
|
|
28
|
+
/** Write `data`, the last, and close. */
|
|
29
|
+
end(data: Uint8Array): unknown;
|
|
30
|
+
destroy(): unknown;
|
|
31
|
+
setKeepAlive?(enable: boolean, delay: number): unknown;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Where a socket connects: a host and port, TLS to follow or not (workerd asks so up front), or a Unix socket's path. */
|
|
35
|
+
export type Address = { readonly host: string; readonly port: number; readonly tls: boolean } | { readonly path: string };
|
|
36
|
+
|
|
37
|
+
/** The caller's TLS options, as Node's `tls.connect` takes them; workerd reads `servername` alone. */
|
|
38
|
+
export type TlsOptions = Omit<ConnectionOptions, 'socket'>;
|
|
39
|
+
|
|
40
|
+
/** What `#postgres/platform` exports, from either module. */
|
|
41
|
+
export interface Platform {
|
|
42
|
+
/** A TCP socket to `to`, connecting: `connected` is called once it is, heard before it starts; or it says `error` and `close`. */
|
|
43
|
+
tcp(to: Address, connected: () => void): Socket;
|
|
44
|
+
/** `socket`, now speaking TLS: it says `secureConnect` once the handshake is done. */
|
|
45
|
+
startTls(socket: Socket, options: TlsOptions): Socket;
|
|
46
|
+
/** 4 or 6 for an IP address, 0 for a name. */
|
|
47
|
+
isIP(host: string): number;
|
|
48
|
+
/** Run `fn` once what is running now is done, before any timer; and cancel it. */
|
|
49
|
+
setImmediate(fn: () => void): unknown;
|
|
50
|
+
clearImmediate(handle: unknown): void;
|
|
51
|
+
/** Milliseconds, monotonic. */
|
|
52
|
+
now(): number;
|
|
53
|
+
randomBytes(length: number): Buffer;
|
|
54
|
+
/** PBKDF2 with HMAC-SHA-256, as SCRAM-SHA-256 salts a password. */
|
|
55
|
+
pbkdf2(password: string, salt: Buffer, iterations: number, length: number): Promise<Buffer>;
|
|
56
|
+
hmac(key: Uint8Array, text: string): Promise<Buffer>;
|
|
57
|
+
sha256(data: Uint8Array): Promise<Buffer>;
|
|
58
|
+
/** MD5, in hex. */
|
|
59
|
+
md5(data: string | Uint8Array): Promise<string>;
|
|
60
|
+
}
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
// The driver on workerd (platform.ts): TCP through `cloudflare:sockets`,
|
|
2
|
+
// shaped as the Node socket the connection expects, and WebCrypto, whose
|
|
3
|
+
// hashes are all asynchronous. Upstream's `cf/polyfills.js`, as it was.
|
|
4
|
+
import { Buffer } from 'node:buffer';
|
|
5
|
+
import { EventEmitter } from 'node:events';
|
|
6
|
+
|
|
7
|
+
import type { Address, Platform, Socket } from './platform.ts';
|
|
8
|
+
|
|
9
|
+
/** A socket of `cloudflare:sockets`, what of it this uses. */
|
|
10
|
+
interface RawSocket {
|
|
11
|
+
readonly readable: ReadableStream<Uint8Array>;
|
|
12
|
+
readonly writable: WritableStream<Uint8Array>;
|
|
13
|
+
readonly closed: Promise<void>;
|
|
14
|
+
close(): Promise<void>;
|
|
15
|
+
startTls(options: { servername?: string }): RawSocket;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
type Connect = (address: string, options: { secureTransport?: 'starttls' }) => RawSocket;
|
|
19
|
+
|
|
20
|
+
const v4Seg = '(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])';
|
|
21
|
+
const v4Str = `(${v4Seg}[.]){3}${v4Seg}`;
|
|
22
|
+
const IPv4Reg = new RegExp(`^${v4Str}$`);
|
|
23
|
+
|
|
24
|
+
const v6Seg = '(?:[0-9a-fA-F]{1,4})';
|
|
25
|
+
const IPv6Reg = new RegExp(
|
|
26
|
+
'^(' +
|
|
27
|
+
`(?:${v6Seg}:){7}(?:${v6Seg}|:)|` +
|
|
28
|
+
`(?:${v6Seg}:){6}(?:${v4Str}|:${v6Seg}|:)|` +
|
|
29
|
+
`(?:${v6Seg}:){5}(?::${v4Str}|(:${v6Seg}){1,2}|:)|` +
|
|
30
|
+
`(?:${v6Seg}:){4}(?:(:${v6Seg}){0,1}:${v4Str}|(:${v6Seg}){1,3}|:)|` +
|
|
31
|
+
`(?:${v6Seg}:){3}(?:(:${v6Seg}){0,2}:${v4Str}|(:${v6Seg}){1,4}|:)|` +
|
|
32
|
+
`(?:${v6Seg}:){2}(?:(:${v6Seg}){0,3}:${v4Str}|(:${v6Seg}){1,5}|:)|` +
|
|
33
|
+
`(?:${v6Seg}:){1}(?:(:${v6Seg}){0,4}:${v4Str}|(:${v6Seg}){1,6}|:)|` +
|
|
34
|
+
`(?::((?::${v6Seg}){0,5}:${v4Str}|(?::${v6Seg}){1,7}|:))` +
|
|
35
|
+
')(%[0-9a-zA-Z-.:]{1,})?$',
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* A `cloudflare:sockets` socket as an event emitter: `connect` once it can
|
|
40
|
+
* write, `data` for each chunk read, `error` then `close` when it fails.
|
|
41
|
+
* With TLS to follow, it reads one chunk alone, the server's answer to the
|
|
42
|
+
* SSLRequest, so that the reader is free for `startTls`.
|
|
43
|
+
*/
|
|
44
|
+
export class WorkerdSocket extends EventEmitter {
|
|
45
|
+
readyState = 'open';
|
|
46
|
+
raw: RawSocket | null = null;
|
|
47
|
+
/** Destroyed before `connect` made its socket: closed as soon as it has. */
|
|
48
|
+
destroyed = false;
|
|
49
|
+
writer: WritableStreamDefaultWriter<Uint8Array> | null = null;
|
|
50
|
+
reader: ReadableStreamDefaultReader<Uint8Array> | null = null;
|
|
51
|
+
|
|
52
|
+
async connect(port: number, host: string, tls: boolean): Promise<void> {
|
|
53
|
+
try {
|
|
54
|
+
this.readyState = 'opening';
|
|
55
|
+
const { connect } = (await import('cloudflare:sockets' as string)) as { connect: Connect };
|
|
56
|
+
const raw = (this.raw = connect(host + ':' + port, tls ? { secureTransport: 'starttls' } : {}));
|
|
57
|
+
if (this.destroyed) return this.abort();
|
|
58
|
+
raw.closed.then(
|
|
59
|
+
() => {
|
|
60
|
+
if (this.readyState !== 'upgrade') return this.close();
|
|
61
|
+
this.readyState = 'open';
|
|
62
|
+
this.emit('secureConnect');
|
|
63
|
+
},
|
|
64
|
+
(error: unknown) => this.error(error),
|
|
65
|
+
);
|
|
66
|
+
this.writer = raw.writable.getWriter();
|
|
67
|
+
this.reader = raw.readable.getReader();
|
|
68
|
+
|
|
69
|
+
void (tls ? this.readFirst() : this.read());
|
|
70
|
+
void this.writer.ready.then(() => {
|
|
71
|
+
this.readyState = 'open';
|
|
72
|
+
this.emit('connect');
|
|
73
|
+
});
|
|
74
|
+
} catch (error) {
|
|
75
|
+
this.error(error);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** Upgraded to TLS, the same emitter: `secureConnect` once the plain socket under it closes. */
|
|
80
|
+
upgrade(servername: string | undefined): this {
|
|
81
|
+
this.writer!.releaseLock();
|
|
82
|
+
this.reader!.releaseLock();
|
|
83
|
+
this.readyState = 'upgrading';
|
|
84
|
+
// Upstream's option, which workerd reads as `expectedServerHostname`: the name is not checked here.
|
|
85
|
+
const raw = (this.raw = this.raw!.startTls({ servername }));
|
|
86
|
+
raw.closed.then(
|
|
87
|
+
() => this.close(),
|
|
88
|
+
(error: unknown) => this.error(error),
|
|
89
|
+
);
|
|
90
|
+
this.writer = raw.writable.getWriter();
|
|
91
|
+
this.reader = raw.readable.getReader();
|
|
92
|
+
void this.writer.ready.then(() => {
|
|
93
|
+
void this.read();
|
|
94
|
+
this.readyState = 'upgrade';
|
|
95
|
+
});
|
|
96
|
+
return this;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
write(data: Uint8Array, callback?: () => void): boolean {
|
|
100
|
+
this.writer!.write(data).then(callback, (error: unknown) => this.error(error));
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
end(data?: Uint8Array): unknown {
|
|
105
|
+
return data ? this.write(data, () => void this.raw!.close()) : this.raw!.close();
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Closed at once, as Node's `destroy`, whatever it is still writing or opening: its writes aborted and its reads
|
|
110
|
+
* cancelled, so that nothing buffered is sent, then the raw socket closed, which on its own waits until the socket
|
|
111
|
+
* has opened and every write has gone (workerd's `Socket::close`). Upstream ended it, which waits for a write stuck
|
|
112
|
+
* under backpressure too (reviews r2 and r3 of #438). Its TCP connection workerd closes with the request, whose I/O
|
|
113
|
+
* it is: locally, not before (packages/vault/test/workers-socket.test.ts). Before `connect` has made the raw socket,
|
|
114
|
+
* it is made only to be closed. Says `close` once, a task later, as Node's does.
|
|
115
|
+
*/
|
|
116
|
+
destroy(): void {
|
|
117
|
+
if (this.destroyed) return;
|
|
118
|
+
this.destroyed = true;
|
|
119
|
+
if (this.raw !== null) this.abort();
|
|
120
|
+
setTimeout(() => this.close(), 0);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** The raw socket's streams torn down, then the socket closed (`destroy`). */
|
|
124
|
+
abort(): void {
|
|
125
|
+
const raw = this.raw!;
|
|
126
|
+
void (this.writer ?? raw.writable).abort().catch(() => {});
|
|
127
|
+
void (this.reader ?? raw.readable).cancel().catch(() => {});
|
|
128
|
+
void raw.close().catch(() => {});
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
async read(): Promise<void> {
|
|
132
|
+
try {
|
|
133
|
+
for (;;) {
|
|
134
|
+
const { done, value } = await this.reader!.read();
|
|
135
|
+
if (done) return;
|
|
136
|
+
this.emit('data', Buffer.from(value));
|
|
137
|
+
}
|
|
138
|
+
} catch (error) {
|
|
139
|
+
this.error(error);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/** The server's answer to the SSLRequest; failed, or the socket closed or destroyed first, said as any read's end. */
|
|
144
|
+
async readFirst(): Promise<void> {
|
|
145
|
+
try {
|
|
146
|
+
const { done, value } = await this.reader!.read();
|
|
147
|
+
if (done) return this.close();
|
|
148
|
+
this.emit('data', Buffer.from(value));
|
|
149
|
+
} catch (error) {
|
|
150
|
+
this.error(error);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
close(): void {
|
|
155
|
+
if (this.readyState === 'closed') return;
|
|
156
|
+
this.readyState = 'closed';
|
|
157
|
+
this.emit('close');
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/** Said once: past `close` (a `destroy`'s, whose aborted streams fail what was under way), nothing more. */
|
|
161
|
+
error(error: unknown): void {
|
|
162
|
+
if (this.readyState === 'closed' || this.destroyed) return;
|
|
163
|
+
this.emit('error', error);
|
|
164
|
+
this.close();
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export const tcp: Platform['tcp'] = (to: Address, connected: () => void) => {
|
|
169
|
+
if ('path' in to) throw new Error('a Unix socket cannot be reached from workerd');
|
|
170
|
+
const socket = new WorkerdSocket();
|
|
171
|
+
socket.once('connect', connected);
|
|
172
|
+
void socket.connect(to.port, to.host, to.tls);
|
|
173
|
+
return socket as Socket;
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
export const startTls: Platform['startTls'] = (socket, { servername }) => (socket as WorkerdSocket).upgrade(servername) as Socket;
|
|
177
|
+
|
|
178
|
+
export const isIP: Platform['isIP'] = (host) => (IPv4Reg.test(host) ? 4 : IPv6Reg.test(host) ? 6 : 0);
|
|
179
|
+
|
|
180
|
+
// A microtask, as upstream's polyfill: once the writes of this turn are buffered, they go together.
|
|
181
|
+
let ids = 1;
|
|
182
|
+
const tasks = new Set<number>();
|
|
183
|
+
|
|
184
|
+
export const setImmediate: Platform['setImmediate'] = (fn) => {
|
|
185
|
+
const id = ids++;
|
|
186
|
+
tasks.add(id);
|
|
187
|
+
queueMicrotask(() => {
|
|
188
|
+
if (tasks.has(id)) {
|
|
189
|
+
fn();
|
|
190
|
+
tasks.delete(id);
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
return id;
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
export const clearImmediate: Platform['clearImmediate'] = (handle) => {
|
|
197
|
+
tasks.delete(handle as number);
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
export const now: Platform['now'] = () => performance.now();
|
|
201
|
+
|
|
202
|
+
export const randomBytes: Platform['randomBytes'] = (length) => crypto.getRandomValues(Buffer.alloc(length));
|
|
203
|
+
|
|
204
|
+
const encoder = new TextEncoder();
|
|
205
|
+
|
|
206
|
+
/** `x`, as WebCrypto types the bytes it takes: a Buffer's memory is never shared. */
|
|
207
|
+
const own = (x: Uint8Array) => x as Uint8Array<ArrayBuffer>;
|
|
208
|
+
|
|
209
|
+
export const pbkdf2: Platform['pbkdf2'] = async (password, salt, iterations, length) =>
|
|
210
|
+
Buffer.from(
|
|
211
|
+
await crypto.subtle.deriveBits(
|
|
212
|
+
{ name: 'PBKDF2', hash: 'SHA-256', salt: own(salt), iterations },
|
|
213
|
+
await crypto.subtle.importKey('raw', encoder.encode(password), 'PBKDF2', false, ['deriveBits']),
|
|
214
|
+
length * 8,
|
|
215
|
+
),
|
|
216
|
+
);
|
|
217
|
+
|
|
218
|
+
export const hmac: Platform['hmac'] = async (key, text) =>
|
|
219
|
+
Buffer.from(await crypto.subtle.sign('HMAC', await crypto.subtle.importKey('raw', own(key), { name: 'HMAC', hash: 'SHA-256' }, false, ['sign']), encoder.encode(text)));
|
|
220
|
+
|
|
221
|
+
export const sha256: Platform['sha256'] = async (data) => Buffer.from(await crypto.subtle.digest('SHA-256', own(data)));
|
|
222
|
+
|
|
223
|
+
// workerd's WebCrypto has MD5, which the standard leaves out.
|
|
224
|
+
export const md5: Platform['md5'] = async (data) =>
|
|
225
|
+
Buffer.from(await crypto.subtle.digest('MD5', typeof data === 'string' ? encoder.encode(data) : own(data))).toString('hex');
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import type { RowList } from './index.ts';
|
|
2
|
+
import type { Column } from './result.ts';
|
|
3
|
+
|
|
4
|
+
/** A statement as sent: its text, its parameters' types (0 for the server to say), and its columns once described. */
|
|
5
|
+
export interface Statement {
|
|
6
|
+
string: string;
|
|
7
|
+
types: number[];
|
|
8
|
+
/** Once described: by a RowDescription, or NoData's none. */
|
|
9
|
+
columns: Column[] | null;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface QueryOptions {
|
|
13
|
+
/** Sent with the simple protocol, one Query message: what a statement with no parameters is, by default. */
|
|
14
|
+
simple: boolean;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* One statement of a group (`group`, index.ts): its text and values, whether
|
|
19
|
+
* its rows come as arrays, and what the connection works out as it sends it.
|
|
20
|
+
*/
|
|
21
|
+
export interface Member {
|
|
22
|
+
text: string;
|
|
23
|
+
args: unknown[];
|
|
24
|
+
arrays: boolean;
|
|
25
|
+
parameters: unknown[];
|
|
26
|
+
statement: Statement | null;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* A statement, sent once awaited (`then`, `catch`, `finally`) or `execute`d,
|
|
31
|
+
* a task later, through the pool or the transaction that made it: its
|
|
32
|
+
* `handler`. A promise of its result, its rows as objects or, after
|
|
33
|
+
* `values()`, as arrays.
|
|
34
|
+
*/
|
|
35
|
+
export class Query<T = unknown> extends Promise<T> {
|
|
36
|
+
readonly text: string;
|
|
37
|
+
readonly args: unknown[];
|
|
38
|
+
readonly options: QueryOptions;
|
|
39
|
+
readonly #handler: (query: Query) => void;
|
|
40
|
+
/** Where it was made, when the client debugs. */
|
|
41
|
+
readonly #origin: Error | null;
|
|
42
|
+
/** Its rows as arrays of values rather than objects. */
|
|
43
|
+
arrays = false;
|
|
44
|
+
executed = false;
|
|
45
|
+
|
|
46
|
+
// What the connection works out as it sends it.
|
|
47
|
+
parameters: unknown[] = [];
|
|
48
|
+
statement: Statement | null = null;
|
|
49
|
+
/** Whether its parameters' types are asked of the server before its values go (`typedByServer`, connection.ts). */
|
|
50
|
+
describeFirst = false;
|
|
51
|
+
/** A transaction's first statement: its BEGIN, sent ahead of it under its Sync (`begin`, index.ts); and whether it took. */
|
|
52
|
+
begin: string | null = null;
|
|
53
|
+
began = false;
|
|
54
|
+
/**
|
|
55
|
+
* Coffre: a group's statements, sent under one Sync and answered together, each its own result (`group`, index.ts),
|
|
56
|
+
* and the one answering now. Null for a statement of its own.
|
|
57
|
+
*/
|
|
58
|
+
group: Member[] | null = null;
|
|
59
|
+
member = 0;
|
|
60
|
+
|
|
61
|
+
readonly resolve: (value: unknown) => void;
|
|
62
|
+
readonly reject: (error: unknown) => void;
|
|
63
|
+
|
|
64
|
+
constructor(text: string, args: unknown[], handler: (query: Query) => void, options: QueryOptions, debug: boolean) {
|
|
65
|
+
let resolve!: (value: T) => void;
|
|
66
|
+
let reject!: (error: unknown) => void;
|
|
67
|
+
super((a, b) => {
|
|
68
|
+
resolve = a;
|
|
69
|
+
reject = b;
|
|
70
|
+
});
|
|
71
|
+
this.text = text;
|
|
72
|
+
this.args = args;
|
|
73
|
+
this.options = options;
|
|
74
|
+
this.#handler = handler;
|
|
75
|
+
this.#origin = debug ? new Error() : null;
|
|
76
|
+
this.resolve = resolve as (value: unknown) => void;
|
|
77
|
+
this.reject = reject;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** Where it was made, for its error's stack: nothing unless the client debugs. */
|
|
81
|
+
get origin(): string {
|
|
82
|
+
return this.#origin?.stack ?? '';
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** What `then` and the like make: a plain promise, not another statement. */
|
|
86
|
+
static get [Symbol.species](): PromiseConstructor {
|
|
87
|
+
return Promise;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
values(): Query<RowList<unknown[][]>> {
|
|
91
|
+
this.arrays = true;
|
|
92
|
+
return this as never;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
execute(): this {
|
|
96
|
+
void this.#handle();
|
|
97
|
+
return this;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// `any` for what it fails with, as Promise's own `then` and `catch` take it.
|
|
101
|
+
override then<A = T, B = never>(onfulfilled?: ((value: T) => A | PromiseLike<A>) | null, onrejected?: ((reason: any) => B | PromiseLike<B>) | null): Promise<A | B> {
|
|
102
|
+
void this.#handle();
|
|
103
|
+
return super.then(onfulfilled, onrejected);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
override catch<B = never>(onrejected?: ((reason: any) => B | PromiseLike<B>) | null): Promise<T | B> {
|
|
107
|
+
void this.#handle();
|
|
108
|
+
return super.catch(onrejected);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
override finally(onfinally?: (() => void) | null): Promise<T> {
|
|
112
|
+
void this.#handle();
|
|
113
|
+
return super.finally(onfinally);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** Sent once, a task after it is first asked for. */
|
|
117
|
+
async #handle(): Promise<void> {
|
|
118
|
+
if (this.executed) return;
|
|
119
|
+
this.executed = true;
|
|
120
|
+
await null;
|
|
121
|
+
this.#handler(this);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/** A queue that takes from either end, and lets go of what it took. */
|
|
2
|
+
export class Queue<T> {
|
|
3
|
+
#xs: (T | undefined)[] = [];
|
|
4
|
+
#index = 0;
|
|
5
|
+
|
|
6
|
+
get length(): number {
|
|
7
|
+
return this.#xs.length - this.#index;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
push(x: T): T {
|
|
11
|
+
this.#xs.push(x);
|
|
12
|
+
return x;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
remove(x: T): T | null {
|
|
16
|
+
const index = this.#xs.indexOf(x);
|
|
17
|
+
if (index === -1) return null;
|
|
18
|
+
this.#xs.splice(index, 1);
|
|
19
|
+
return x;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Coffre: the newest, for the pool to take the connection idle the shortest, as pg-pool does: the others stay
|
|
23
|
+
// idle, and close once idle_timeout passes, and the backends used stay few and warm.
|
|
24
|
+
pop(): T | undefined {
|
|
25
|
+
if (this.#xs.length === this.#index) return undefined;
|
|
26
|
+
const out = this.#xs.pop();
|
|
27
|
+
if (this.#index === this.#xs.length) this.#clear();
|
|
28
|
+
return out;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
shift(): T | undefined {
|
|
32
|
+
const out = this.#xs[this.#index++];
|
|
33
|
+
if (this.#index === this.#xs.length) this.#clear();
|
|
34
|
+
else this.#xs[this.#index - 1] = undefined;
|
|
35
|
+
return out;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
#clear(): void {
|
|
39
|
+
this.#index = 0;
|
|
40
|
+
this.#xs = [];
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { Statement } from './query.ts';
|
|
2
|
+
|
|
3
|
+
/** A backend's process id and secret key, as BackendKeyData gave them: what a CancelRequest names. */
|
|
4
|
+
export interface Backend {
|
|
5
|
+
pid: number | null;
|
|
6
|
+
secret: number | null;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/** What Postgres said of a column, in a RowDescription. */
|
|
10
|
+
export interface Column {
|
|
11
|
+
name: string;
|
|
12
|
+
/** How its text is read, by its type; as text when there is none. */
|
|
13
|
+
parser: ((text: string) => unknown) | undefined;
|
|
14
|
+
table: number;
|
|
15
|
+
number: number;
|
|
16
|
+
type: number;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* A statement's rows, with what its CommandComplete said beside them: `count`
|
|
21
|
+
* (rows inserted, updated, deleted or returned), `command`, and `state`, the
|
|
22
|
+
* backend that ran it. Not enumerable, so that a result spreads as its rows.
|
|
23
|
+
*/
|
|
24
|
+
export class Result extends Array<unknown> {
|
|
25
|
+
declare count: number | null;
|
|
26
|
+
declare state: Backend | null;
|
|
27
|
+
declare command: string | null;
|
|
28
|
+
declare columns: Column[] | null;
|
|
29
|
+
declare statement: Statement | null;
|
|
30
|
+
|
|
31
|
+
constructor() {
|
|
32
|
+
super();
|
|
33
|
+
Object.defineProperties(this, {
|
|
34
|
+
count: { value: null, writable: true },
|
|
35
|
+
state: { value: null, writable: true },
|
|
36
|
+
command: { value: null, writable: true },
|
|
37
|
+
columns: { value: null, writable: true },
|
|
38
|
+
statement: { value: null, writable: true },
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** What `map`, `filter` and the like make: a plain array. */
|
|
43
|
+
static get [Symbol.species](): ArrayConstructor {
|
|
44
|
+
return Array;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { Buffer } from 'node:buffer';
|
|
2
|
+
|
|
3
|
+
/** How a column's text is read, by its type's oid. */
|
|
4
|
+
export type Parsers = Record<number, ((text: string) => unknown) | undefined>;
|
|
5
|
+
/** How a value is written as text, by its parameter's type's oid. */
|
|
6
|
+
export type Serializers = Record<number, ((value: unknown) => unknown) | undefined>;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* The types read and written by default, as upstream's `types.js`: each one's
|
|
10
|
+
* oid when sent (`to`), the oids read as it (`from`), and how.
|
|
11
|
+
*/
|
|
12
|
+
const TYPES: { to: number; from: number[]; serialize: (x: unknown) => string; parse?: (x: string) => unknown }[] = [
|
|
13
|
+
// string: read as the text it is.
|
|
14
|
+
{ to: 25, from: [], serialize: (x) => String(x) },
|
|
15
|
+
// number
|
|
16
|
+
{ to: 0, from: [21, 23, 26, 700, 701], serialize: (x) => String(x), parse: (x) => +x },
|
|
17
|
+
// json
|
|
18
|
+
{ to: 114, from: [114, 3802], serialize: (x) => JSON.stringify(x), parse: (x) => JSON.parse(x) as unknown },
|
|
19
|
+
// boolean
|
|
20
|
+
{ to: 16, from: [16], serialize: (x) => (x === true ? 't' : 'f'), parse: (x) => x === 't' },
|
|
21
|
+
// date
|
|
22
|
+
{ to: 1184, from: [1082, 1114, 1184], serialize: (x) => (x instanceof Date ? x : new Date(x as string)).toISOString(), parse: (x) => new Date(x) },
|
|
23
|
+
// bytea
|
|
24
|
+
{ to: 17, from: [17], serialize: (x) => '\\x' + Buffer.from(x as Uint8Array).toString('hex'), parse: (x) => Buffer.from(x.slice(2), 'hex') },
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
/** A client's own parsers and serializers, made from the defaults: it may change them (`options.parsers[oid] = …`). */
|
|
28
|
+
export function defaultTypes(): { parsers: Parsers; serializers: Serializers } {
|
|
29
|
+
const parsers: Parsers = {};
|
|
30
|
+
const serializers: Serializers = {};
|
|
31
|
+
for (const type of TYPES) {
|
|
32
|
+
for (const oid of type.from) parsers[oid] = type.parse;
|
|
33
|
+
serializers[type.to] = type.serialize;
|
|
34
|
+
for (const oid of type.from) serializers[oid] = type.serialize;
|
|
35
|
+
}
|
|
36
|
+
return { parsers, serializers };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* The type a value is sent as, which a parameter's serializer goes by: 0,
|
|
41
|
+
* none, for the server to type its text, but for a date, bytes, a boolean or
|
|
42
|
+
* a bigint. An array goes by its first element (porsager/postgres#1225; coffre
|
|
43
|
+
* sends arrays as their text, `asPgSends` in postgres.ts).
|
|
44
|
+
*/
|
|
45
|
+
export function inferType(x: unknown): number {
|
|
46
|
+
return x instanceof Date ? 1184
|
|
47
|
+
: x instanceof Uint8Array ? 17
|
|
48
|
+
: x === true || x === false ? 16
|
|
49
|
+
: typeof x === 'bigint' ? 20
|
|
50
|
+
: Array.isArray(x) ? inferType(x[0])
|
|
51
|
+
: 0;
|
|
52
|
+
}
|