@crvouga/mockingbird-service-postgres 1.4.0 → 1.5.1
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/CHANGELOG.md +37 -1
- package/README.md +39 -8
- package/dist/admin.d.ts +13 -0
- package/dist/admin.js +26638 -0
- package/dist/admin.js.map +7 -0
- package/dist/api/database.d.ts +37 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.js +279 -22
- package/dist/index.js.map +3 -3
- package/dist/runtime/options.d.ts +5 -0
- package/dist/wire/cli.js +338 -29
- package/dist/wire/cli.js.map +4 -4
- package/dist/wire/connection.d.ts +5 -0
- package/dist/wire/index.d.ts +18 -2
- package/dist/wire/index.js +332 -26
- package/dist/wire/index.js.map +4 -4
- package/package.json +8 -1
|
@@ -21,6 +21,11 @@ export type ConnectionOptions = {
|
|
|
21
21
|
parameters: Record<string, string>;
|
|
22
22
|
faults: ServerFaults;
|
|
23
23
|
onLog?: (entry: ServerLog) => void;
|
|
24
|
+
/**
|
|
25
|
+
* When set, a startup packet that names a different database is fatal `3D000`.
|
|
26
|
+
* Unset accepts any database name.
|
|
27
|
+
*/
|
|
28
|
+
database?: string;
|
|
24
29
|
};
|
|
25
30
|
/**
|
|
26
31
|
* One client connection: the protocol state machine over the shared {@link Cluster}.
|
package/dist/wire/index.d.ts
CHANGED
|
@@ -44,12 +44,27 @@ export type ServeOptions = {
|
|
|
44
44
|
parameters?: Record<string, string>;
|
|
45
45
|
/** Per-statement log sink, for tests and debugging. */
|
|
46
46
|
onLog?: (entry: ServerLog) => void;
|
|
47
|
+
/**
|
|
48
|
+
* `postgres://` or `postgresql://` URL. User, password, host, port, and database name
|
|
49
|
+
* fill the fields above when those fields are omitted. An omitted port means `5432`;
|
|
50
|
+
* pass port `0` in the URL (`postgres://postgres@127.0.0.1:0/app`) for an ephemeral port.
|
|
51
|
+
*/
|
|
52
|
+
url?: string;
|
|
53
|
+
/**
|
|
54
|
+
* Database name clients must request in the startup packet. A different name is
|
|
55
|
+
* SQLSTATE `3D000`. Set from `url` when the path is non-empty. Unset accepts any name.
|
|
56
|
+
*/
|
|
57
|
+
databaseName?: string;
|
|
58
|
+
/** Role shown in `connectionString`. A URI's username fills this. Default `postgres`. */
|
|
59
|
+
user?: string;
|
|
47
60
|
};
|
|
48
61
|
export type PostgresServer = {
|
|
49
62
|
/** The bound port. */
|
|
50
63
|
readonly port: number;
|
|
51
64
|
/** The bound host. */
|
|
52
65
|
readonly host: string;
|
|
66
|
+
/** `postgres://` URL for the bound address, including the database name. */
|
|
67
|
+
readonly connectionString: string;
|
|
53
68
|
/** The shared engine, for seeding, snapshotting or asserting from the test process. */
|
|
54
69
|
readonly database: Database;
|
|
55
70
|
/** The underlying `net.Server`. */
|
|
@@ -63,7 +78,8 @@ export type PostgresServer = {
|
|
|
63
78
|
/** Stop listening and close every connection. */
|
|
64
79
|
close(): Promise<void>;
|
|
65
80
|
};
|
|
66
|
-
|
|
67
|
-
|
|
81
|
+
export type ServeInput = ServeOptions | string;
|
|
82
|
+
/** Start a server and resolve once it is listening. A string argument is a `postgres://` URI. */
|
|
83
|
+
export declare const serve: (input?: ServeInput) => Promise<PostgresServer>;
|
|
68
84
|
export { Cluster } from "./cluster.js";
|
|
69
85
|
export type { ServerFaults, ServerLog } from "./connection.js";
|
package/dist/wire/index.js
CHANGED
|
@@ -1,6 +1,167 @@
|
|
|
1
1
|
// src/wire/index.ts
|
|
2
2
|
import { createServer } from "node:net";
|
|
3
3
|
|
|
4
|
+
// ../../core/dist/timeline.js
|
|
5
|
+
var BRANCH_PATTERN = /^[A-Za-z0-9_.-]{1,64}$/;
|
|
6
|
+
var Timeline = class {
|
|
7
|
+
maxCheckpoints;
|
|
8
|
+
now;
|
|
9
|
+
makeId;
|
|
10
|
+
nodes = /* @__PURE__ */ new Map();
|
|
11
|
+
heads = /* @__PURE__ */ new Map();
|
|
12
|
+
/** Unreferenced nodes in the exact order they became collectible. */
|
|
13
|
+
evictable = /* @__PURE__ */ new Set();
|
|
14
|
+
/** Branch heads plus explicit retainers. Absent means zero. */
|
|
15
|
+
references = /* @__PURE__ */ new Map();
|
|
16
|
+
explicitPins = /* @__PURE__ */ new Map();
|
|
17
|
+
sequence = 0;
|
|
18
|
+
constructor(options = {}) {
|
|
19
|
+
const max = options.maxCheckpoints ?? 1e3;
|
|
20
|
+
if (!Number.isSafeInteger(max) || max < 1)
|
|
21
|
+
throw new RangeError("maxCheckpoints must be a positive integer");
|
|
22
|
+
this.maxCheckpoints = max;
|
|
23
|
+
this.now = options.now ?? (() => this.sequence);
|
|
24
|
+
this.makeId = options.id ?? ((sequence) => `cp_${sequence.toString(36).padStart(8, "0")}`);
|
|
25
|
+
}
|
|
26
|
+
/** Capture a new immutable value and move `branch` to it. */
|
|
27
|
+
commit(value, options = {}) {
|
|
28
|
+
const branch = options.branch ?? "main";
|
|
29
|
+
this.assertBranch(branch);
|
|
30
|
+
const parent = options.parent === void 0 ? this.heads.get(branch) ?? null : options.parent;
|
|
31
|
+
if (parent !== null && !this.nodes.has(parent))
|
|
32
|
+
throw new RangeError(`no checkpoint ${parent}`);
|
|
33
|
+
const id = this.makeId(++this.sequence);
|
|
34
|
+
if (this.nodes.has(id))
|
|
35
|
+
throw new RangeError(`duplicate checkpoint id ${id}`);
|
|
36
|
+
const checkpoint = Object.freeze({ id, branch, parent, at: this.now(), value });
|
|
37
|
+
this.nodes.set(id, checkpoint);
|
|
38
|
+
this.moveHead(branch, id);
|
|
39
|
+
this.collect(this.maxCheckpoints);
|
|
40
|
+
return checkpoint;
|
|
41
|
+
}
|
|
42
|
+
/** Create a branch pointer without copying its checkpoint value. */
|
|
43
|
+
fork(branch, options = {}) {
|
|
44
|
+
this.assertBranch(branch);
|
|
45
|
+
if (this.heads.has(branch))
|
|
46
|
+
throw new RangeError(`branch already exists: ${branch}`);
|
|
47
|
+
const from = options.from ?? this.heads.get("main");
|
|
48
|
+
if (from === void 0)
|
|
49
|
+
return void 0;
|
|
50
|
+
const checkpoint = this.get(from);
|
|
51
|
+
this.moveHead(branch, checkpoint.id);
|
|
52
|
+
return checkpoint;
|
|
53
|
+
}
|
|
54
|
+
/** Move a branch pointer to an existing checkpoint. */
|
|
55
|
+
checkout(branch, id) {
|
|
56
|
+
this.assertBranch(branch);
|
|
57
|
+
const checkpoint = this.get(id);
|
|
58
|
+
this.moveHead(branch, checkpoint.id);
|
|
59
|
+
return checkpoint;
|
|
60
|
+
}
|
|
61
|
+
get(id) {
|
|
62
|
+
const checkpoint = this.nodes.get(id);
|
|
63
|
+
if (!checkpoint)
|
|
64
|
+
throw new RangeError(`no checkpoint ${id}`);
|
|
65
|
+
return checkpoint;
|
|
66
|
+
}
|
|
67
|
+
head(branch = "main") {
|
|
68
|
+
const id = this.heads.get(branch);
|
|
69
|
+
return id === void 0 ? void 0 : this.get(id);
|
|
70
|
+
}
|
|
71
|
+
hasBranch(branch) {
|
|
72
|
+
return this.heads.has(branch);
|
|
73
|
+
}
|
|
74
|
+
branches() {
|
|
75
|
+
return Object.freeze(Object.fromEntries([...this.heads].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0)));
|
|
76
|
+
}
|
|
77
|
+
checkpoints() {
|
|
78
|
+
return [...this.nodes.values()];
|
|
79
|
+
}
|
|
80
|
+
/** Number of retained checkpoints without allocating an array. */
|
|
81
|
+
get size() {
|
|
82
|
+
return this.nodes.size;
|
|
83
|
+
}
|
|
84
|
+
/** Pin a checkpoint independently of branch heads (used by compatibility snapshot handles). */
|
|
85
|
+
retain(id) {
|
|
86
|
+
const checkpoint = this.get(id);
|
|
87
|
+
this.explicitPins.set(id, (this.explicitPins.get(id) ?? 0) + 1);
|
|
88
|
+
this.addReference(id);
|
|
89
|
+
return checkpoint;
|
|
90
|
+
}
|
|
91
|
+
/** Release one explicit pin. Branch heads remain pinned until moved or deleted. */
|
|
92
|
+
release(id) {
|
|
93
|
+
if (!this.nodes.has(id))
|
|
94
|
+
return false;
|
|
95
|
+
const pins = this.explicitPins.get(id) ?? 0;
|
|
96
|
+
if (pins === 0)
|
|
97
|
+
return false;
|
|
98
|
+
if (pins === 1)
|
|
99
|
+
this.explicitPins.delete(id);
|
|
100
|
+
else
|
|
101
|
+
this.explicitPins.set(id, pins - 1);
|
|
102
|
+
this.removeReference(id);
|
|
103
|
+
this.collect(this.maxCheckpoints);
|
|
104
|
+
return true;
|
|
105
|
+
}
|
|
106
|
+
deleteBranch(branch) {
|
|
107
|
+
if (branch === "main")
|
|
108
|
+
throw new RangeError("cannot delete main branch");
|
|
109
|
+
const previous = this.heads.get(branch);
|
|
110
|
+
const deleted = this.heads.delete(branch);
|
|
111
|
+
if (previous !== void 0)
|
|
112
|
+
this.removeReference(previous);
|
|
113
|
+
this.collect(this.maxCheckpoints);
|
|
114
|
+
return deleted;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Deterministically discard oldest unpinned checkpoints. Collection is O(number removed):
|
|
118
|
+
* commits never scan pinned nodes or the retained history. Parents are metadata rather than a
|
|
119
|
+
* storage dependency, so a retained node remains usable after pruning.
|
|
120
|
+
*/
|
|
121
|
+
gc(max = this.maxCheckpoints) {
|
|
122
|
+
if (!Number.isSafeInteger(max) || max < 1)
|
|
123
|
+
throw new RangeError("max must be a positive integer");
|
|
124
|
+
const removed = [];
|
|
125
|
+
this.collect(max, removed);
|
|
126
|
+
return removed;
|
|
127
|
+
}
|
|
128
|
+
collect(max, removed) {
|
|
129
|
+
while (this.nodes.size > max && this.evictable.size > 0) {
|
|
130
|
+
const id = this.evictable.values().next().value;
|
|
131
|
+
this.evictable.delete(id);
|
|
132
|
+
this.nodes.delete(id);
|
|
133
|
+
removed?.push(id);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
moveHead(branch, id) {
|
|
137
|
+
const previous = this.heads.get(branch);
|
|
138
|
+
if (previous === id)
|
|
139
|
+
return;
|
|
140
|
+
if (previous !== void 0)
|
|
141
|
+
this.removeReference(previous);
|
|
142
|
+
this.heads.set(branch, id);
|
|
143
|
+
this.addReference(id);
|
|
144
|
+
}
|
|
145
|
+
addReference(id) {
|
|
146
|
+
this.references.set(id, (this.references.get(id) ?? 0) + 1);
|
|
147
|
+
this.evictable.delete(id);
|
|
148
|
+
}
|
|
149
|
+
removeReference(id) {
|
|
150
|
+
const next = (this.references.get(id) ?? 0) - 1;
|
|
151
|
+
if (next > 0)
|
|
152
|
+
this.references.set(id, next);
|
|
153
|
+
else {
|
|
154
|
+
this.references.delete(id);
|
|
155
|
+
if (this.nodes.has(id))
|
|
156
|
+
this.evictable.add(id);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
assertBranch(branch) {
|
|
160
|
+
if (!BRANCH_PATTERN.test(branch))
|
|
161
|
+
throw new RangeError(`branch must match ${BRANCH_PATTERN}`);
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
|
|
4
165
|
// src/errors/error.ts
|
|
5
166
|
var CATEGORY_TO_SQLSTATE = {
|
|
6
167
|
syntax: "42601",
|
|
@@ -18733,7 +18894,7 @@ function setOpRelation(env, op) {
|
|
|
18733
18894
|
return t === UNKNOWN ? "text" : t;
|
|
18734
18895
|
});
|
|
18735
18896
|
const columns = left.columns.map((c, i) => ({ name: c.name, type: types[i], table: null }));
|
|
18736
|
-
const
|
|
18897
|
+
const normalize2 = (rel2) => rel2.rows.map(
|
|
18737
18898
|
(row) => row.map((v, i) => {
|
|
18738
18899
|
if (v === null) return null;
|
|
18739
18900
|
const srcT = rel2.columns[i].type;
|
|
@@ -18741,8 +18902,8 @@ function setOpRelation(env, op) {
|
|
|
18741
18902
|
return castTo(ctx, tv(srcT === UNKNOWN ? "text" : srcT, v), types[i], {}).v;
|
|
18742
18903
|
})
|
|
18743
18904
|
);
|
|
18744
|
-
const lrows =
|
|
18745
|
-
const rrows =
|
|
18905
|
+
const lrows = normalize2(left);
|
|
18906
|
+
const rrows = normalize2(right);
|
|
18746
18907
|
const keyOf = (row) => row.map((v, i) => v === null ? "\0N" : datumKey(types[i], v)).join("");
|
|
18747
18908
|
let rows;
|
|
18748
18909
|
switch (op.op) {
|
|
@@ -23114,6 +23275,9 @@ var ADOPT = /* @__PURE__ */ Symbol("postgres-mem.adopt");
|
|
|
23114
23275
|
function isAdopted(value) {
|
|
23115
23276
|
return ADOPT in value;
|
|
23116
23277
|
}
|
|
23278
|
+
function project(point) {
|
|
23279
|
+
return { id: point.id, branch: point.branch, parent: point.parent, at: point.at };
|
|
23280
|
+
}
|
|
23117
23281
|
var Database = class {
|
|
23118
23282
|
/** @internal Engine catalog, tables, session settings. */
|
|
23119
23283
|
state;
|
|
@@ -23134,6 +23298,10 @@ var Database = class {
|
|
|
23134
23298
|
closed = false;
|
|
23135
23299
|
transactionSequence = 0;
|
|
23136
23300
|
apiTransactionDepth = 0;
|
|
23301
|
+
/** @internal Shared checkpoint DAG. Omitted from published declarations. */
|
|
23302
|
+
timeline;
|
|
23303
|
+
historyBranch = "main";
|
|
23304
|
+
originId = "";
|
|
23137
23305
|
constructor(options = {}) {
|
|
23138
23306
|
if (isAdopted(options)) {
|
|
23139
23307
|
this.seed = options.seed;
|
|
@@ -23146,16 +23314,23 @@ var Database = class {
|
|
|
23146
23314
|
this.state.prng = this.prng;
|
|
23147
23315
|
this.state.clock = () => this.now();
|
|
23148
23316
|
this.transactions = txManagerFor(this.state);
|
|
23149
|
-
|
|
23317
|
+
} else {
|
|
23318
|
+
this.seed = options.seed ?? DEFAULT_DATABASE_SEED;
|
|
23319
|
+
this.randomMode = options.random ?? "deterministic";
|
|
23320
|
+
this.systemClock = options.now === "system";
|
|
23321
|
+
this.int8Mode = options.int8 ?? "bigint";
|
|
23322
|
+
this.prng = this.randomMode === "os" ? new OsEntropy() : new Prng(this.seed);
|
|
23323
|
+
this.now = resolveClock(options.now);
|
|
23324
|
+
this.state = new DatabaseState(this.prng, () => this.now());
|
|
23325
|
+
this.transactions = txManagerFor(this.state);
|
|
23150
23326
|
}
|
|
23151
|
-
this.
|
|
23152
|
-
|
|
23153
|
-
|
|
23154
|
-
|
|
23155
|
-
|
|
23156
|
-
this.
|
|
23157
|
-
this.
|
|
23158
|
-
this.transactions = txManagerFor(this.state);
|
|
23327
|
+
this.timeline = new Timeline({
|
|
23328
|
+
now: () => this.now().getTime(),
|
|
23329
|
+
...options.maxCheckpoints !== void 0 ? { maxCheckpoints: options.maxCheckpoints } : {}
|
|
23330
|
+
});
|
|
23331
|
+
const origin = this.timeline.commit(this.captureLive());
|
|
23332
|
+
this.originId = origin.id;
|
|
23333
|
+
this.timeline.retain(origin.id);
|
|
23159
23334
|
}
|
|
23160
23335
|
/**
|
|
23161
23336
|
* Execute SQL for its side effects. Multiple semicolon-separated statements
|
|
@@ -23210,7 +23385,15 @@ var Database = class {
|
|
|
23210
23385
|
/** Execute a single-statement query and return all rows keyed by column name. */
|
|
23211
23386
|
query(sql, params = [], options = {}) {
|
|
23212
23387
|
this.assertOpen();
|
|
23213
|
-
|
|
23388
|
+
const at = options.at;
|
|
23389
|
+
if (typeof at === "string") {
|
|
23390
|
+
try {
|
|
23391
|
+
return this.timeline.get(at).value.open().query(sql, params);
|
|
23392
|
+
} catch (error) {
|
|
23393
|
+
this.historyError(error);
|
|
23394
|
+
}
|
|
23395
|
+
}
|
|
23396
|
+
if (at) return at.open().query(sql, params);
|
|
23214
23397
|
return this.prepareSingle(sql).all(...params);
|
|
23215
23398
|
}
|
|
23216
23399
|
/** Compile a single SQL statement into a reusable {@link Statement}. */
|
|
@@ -23258,23 +23441,77 @@ var Database = class {
|
|
|
23258
23441
|
* {@link Snapshot.open} for a copy-on-write fork.
|
|
23259
23442
|
*/
|
|
23260
23443
|
snapshot() {
|
|
23444
|
+
const point = this.record(this.historyBranch);
|
|
23445
|
+
return this.timeline.get(point.id).value;
|
|
23446
|
+
}
|
|
23447
|
+
/** Alias for {@link snapshot}, naming the value as a timeline checkpoint. */
|
|
23448
|
+
checkpoint() {
|
|
23449
|
+
return this.snapshot();
|
|
23450
|
+
}
|
|
23451
|
+
/**
|
|
23452
|
+
* Commit the live state onto `branch` (default: the branch this database follows).
|
|
23453
|
+
* Does not switch the live database. Illegal inside a transaction.
|
|
23454
|
+
*/
|
|
23455
|
+
record(branch = this.historyBranch) {
|
|
23261
23456
|
this.assertOpen();
|
|
23262
23457
|
if (this.transactions.inTransaction) {
|
|
23263
23458
|
throw pgError("transaction_state", "cannot snapshot during a transaction", "25P01");
|
|
23264
23459
|
}
|
|
23265
|
-
|
|
23266
|
-
this.
|
|
23267
|
-
|
|
23268
|
-
this.
|
|
23269
|
-
|
|
23270
|
-
this.randomMode,
|
|
23271
|
-
this.systemClock,
|
|
23272
|
-
this.int8Mode
|
|
23273
|
-
);
|
|
23460
|
+
try {
|
|
23461
|
+
return project(this.timeline.commit(this.captureLive(), { branch }));
|
|
23462
|
+
} catch (error) {
|
|
23463
|
+
this.historyError(error);
|
|
23464
|
+
}
|
|
23274
23465
|
}
|
|
23275
|
-
/**
|
|
23276
|
-
|
|
23277
|
-
|
|
23466
|
+
/** Point `name` at a checkpoint without copying rows or switching the live database. */
|
|
23467
|
+
fork(name, at) {
|
|
23468
|
+
this.assertOpen();
|
|
23469
|
+
try {
|
|
23470
|
+
const point = this.timeline.fork(name, at !== void 0 ? { from: at } : {});
|
|
23471
|
+
if (!point) throw pgError("misuse", "no checkpoint to fork from", "XX000");
|
|
23472
|
+
return project(point);
|
|
23473
|
+
} catch (error) {
|
|
23474
|
+
if (error instanceof PostgresError) throw error;
|
|
23475
|
+
this.historyError(error);
|
|
23476
|
+
}
|
|
23477
|
+
}
|
|
23478
|
+
/** Move `branch` to `id` and install that checkpoint as the live database. */
|
|
23479
|
+
checkout(id, branch = this.historyBranch) {
|
|
23480
|
+
this.assertOpen();
|
|
23481
|
+
if (this.transactions.inTransaction) {
|
|
23482
|
+
throw pgError("transaction_state", "cannot checkout during a transaction", "25P01");
|
|
23483
|
+
}
|
|
23484
|
+
try {
|
|
23485
|
+
const point = this.timeline.checkout(branch, id);
|
|
23486
|
+
this.historyBranch = branch;
|
|
23487
|
+
this.install(point.value);
|
|
23488
|
+
return project(point);
|
|
23489
|
+
} catch (error) {
|
|
23490
|
+
this.historyError(error);
|
|
23491
|
+
}
|
|
23492
|
+
}
|
|
23493
|
+
/** Return the live database to the origin checkpoint on `main`. */
|
|
23494
|
+
reset() {
|
|
23495
|
+
return this.checkout(this.originId, "main");
|
|
23496
|
+
}
|
|
23497
|
+
/** Checkpoint ids and branch heads. Values stay inside the timeline. */
|
|
23498
|
+
get history() {
|
|
23499
|
+
const timeline = this.timeline;
|
|
23500
|
+
return {
|
|
23501
|
+
get size() {
|
|
23502
|
+
return timeline.size;
|
|
23503
|
+
},
|
|
23504
|
+
head(branch) {
|
|
23505
|
+
const point = timeline.head(branch);
|
|
23506
|
+
return point ? project(point) : void 0;
|
|
23507
|
+
},
|
|
23508
|
+
branches() {
|
|
23509
|
+
return timeline.branches();
|
|
23510
|
+
},
|
|
23511
|
+
checkpoints() {
|
|
23512
|
+
return timeline.checkpoints().map(project);
|
|
23513
|
+
}
|
|
23514
|
+
};
|
|
23278
23515
|
}
|
|
23279
23516
|
/** Open a copy-on-write branch from `at`, or from the current state when omitted. */
|
|
23280
23517
|
branch(at = this.snapshot()) {
|
|
@@ -23319,6 +23556,26 @@ var Database = class {
|
|
|
23319
23556
|
assertOpen() {
|
|
23320
23557
|
if (this.closed) throw pgError("misuse", "Database is closed", "XX000");
|
|
23321
23558
|
}
|
|
23559
|
+
historyError(error) {
|
|
23560
|
+
const message2 = error instanceof Error ? error.message : String(error);
|
|
23561
|
+
throw pgError("misuse", message2, "XX000");
|
|
23562
|
+
}
|
|
23563
|
+
captureLive() {
|
|
23564
|
+
return captureSnapshot(
|
|
23565
|
+
this.state,
|
|
23566
|
+
this.prng,
|
|
23567
|
+
this.now,
|
|
23568
|
+
this.seed,
|
|
23569
|
+
this.randomMode,
|
|
23570
|
+
this.systemClock,
|
|
23571
|
+
this.int8Mode
|
|
23572
|
+
);
|
|
23573
|
+
}
|
|
23574
|
+
install(snapshot) {
|
|
23575
|
+
this.state.restoreFrom(snapshot.state.cloneShallow());
|
|
23576
|
+
this.prng.setState(snapshot.prngState);
|
|
23577
|
+
if (!this.systemClock) this.now = fixedClock(new Date(snapshot.nowMs));
|
|
23578
|
+
}
|
|
23322
23579
|
prepareSingle(sql) {
|
|
23323
23580
|
const statements = parse(sql);
|
|
23324
23581
|
if (statements.length === 0) {
|
|
@@ -24208,6 +24465,11 @@ var Connection = class {
|
|
|
24208
24465
|
return;
|
|
24209
24466
|
}
|
|
24210
24467
|
this.user = parameters.user ?? "postgres";
|
|
24468
|
+
const requested = parameters.database && parameters.database.length > 0 ? parameters.database : this.user;
|
|
24469
|
+
if (this.options.database !== void 0 && requested !== this.options.database) {
|
|
24470
|
+
this.fatal("3D000", `database "${requested}" does not exist`);
|
|
24471
|
+
return;
|
|
24472
|
+
}
|
|
24211
24473
|
if (this.options.password !== void 0) {
|
|
24212
24474
|
this.scram = new ScramServer(this.options.password);
|
|
24213
24475
|
this.write(backend.authenticationSASL(["SCRAM-SHA-256"]));
|
|
@@ -24649,13 +24911,53 @@ var ConnectionDropped = class extends Error {
|
|
|
24649
24911
|
};
|
|
24650
24912
|
|
|
24651
24913
|
// src/wire/index.ts
|
|
24914
|
+
var parseUri = (raw) => {
|
|
24915
|
+
let url;
|
|
24916
|
+
try {
|
|
24917
|
+
url = new URL(raw);
|
|
24918
|
+
} catch {
|
|
24919
|
+
throw new Error(`invalid postgres URI: ${raw}`);
|
|
24920
|
+
}
|
|
24921
|
+
if (url.protocol !== "postgres:" && url.protocol !== "postgresql:") {
|
|
24922
|
+
throw new Error(`unsupported postgres URI protocol ${url.protocol}`);
|
|
24923
|
+
}
|
|
24924
|
+
const databaseName = decodeURIComponent(url.pathname.replace(/^\//, ""));
|
|
24925
|
+
const password = url.password === "" ? void 0 : decodeURIComponent(url.password);
|
|
24926
|
+
const user = decodeURIComponent(url.username || "postgres");
|
|
24927
|
+
const port = url.port === "" ? 5432 : Number(url.port);
|
|
24928
|
+
if (!Number.isInteger(port) || port < 0 || port > 65535) throw new Error(`invalid postgres URI port: ${url.port}`);
|
|
24929
|
+
return {
|
|
24930
|
+
host: url.hostname || "127.0.0.1",
|
|
24931
|
+
port,
|
|
24932
|
+
user,
|
|
24933
|
+
...password !== void 0 ? { password } : {},
|
|
24934
|
+
...databaseName !== "" ? { databaseName } : {}
|
|
24935
|
+
};
|
|
24936
|
+
};
|
|
24937
|
+
var normalize = (input) => {
|
|
24938
|
+
if (typeof input === "string") return parseUri(input);
|
|
24939
|
+
if (input.url === void 0) return input;
|
|
24940
|
+
const parsed = parseUri(input.url);
|
|
24941
|
+
return {
|
|
24942
|
+
...parsed,
|
|
24943
|
+
...input,
|
|
24944
|
+
host: input.host ?? parsed.host,
|
|
24945
|
+
port: input.port ?? parsed.port,
|
|
24946
|
+
password: input.password ?? parsed.password,
|
|
24947
|
+
databaseName: input.databaseName ?? parsed.databaseName,
|
|
24948
|
+
user: input.user ?? parsed.user,
|
|
24949
|
+
parameters: input.parameters ?? parsed.parameters
|
|
24950
|
+
};
|
|
24951
|
+
};
|
|
24652
24952
|
var isSnapshot = (value) => typeof value === "object" && value !== null && "open" in value && typeof value.open === "function";
|
|
24653
|
-
var serve = (
|
|
24953
|
+
var serve = (input = {}) => {
|
|
24954
|
+
const options = normalize(input);
|
|
24654
24955
|
const database = isSnapshot(options.database) ? options.database.open() : options.database ?? new Database();
|
|
24655
24956
|
const cluster = new Cluster(database);
|
|
24656
24957
|
const faults = {};
|
|
24657
24958
|
const connections = /* @__PURE__ */ new Set();
|
|
24658
24959
|
const host = options.host ?? "127.0.0.1";
|
|
24960
|
+
const user = options.user || "postgres";
|
|
24659
24961
|
const server = createServer((socket) => {
|
|
24660
24962
|
socket.setNoDelay(true);
|
|
24661
24963
|
const connection = new Connection(socket, cluster, {
|
|
@@ -24663,6 +24965,7 @@ var serve = (options = {}) => {
|
|
|
24663
24965
|
serverVersion: options.serverVersion ?? "18.3",
|
|
24664
24966
|
parameters: options.parameters ?? {},
|
|
24665
24967
|
faults,
|
|
24968
|
+
...options.databaseName !== void 0 ? { database: options.databaseName } : {},
|
|
24666
24969
|
...options.onLog ? { onLog: options.onLog } : {}
|
|
24667
24970
|
});
|
|
24668
24971
|
connections.add(connection);
|
|
@@ -24674,9 +24977,12 @@ var serve = (options = {}) => {
|
|
|
24674
24977
|
server.removeListener("error", reject);
|
|
24675
24978
|
const address = server.address();
|
|
24676
24979
|
const port = typeof address === "object" && address !== null ? address.port : 0;
|
|
24980
|
+
const name = options.databaseName ?? "postgres";
|
|
24981
|
+
const auth = options.password ? `${encodeURIComponent(user)}:${encodeURIComponent(options.password)}@` : `${encodeURIComponent(user)}@`;
|
|
24677
24982
|
resolve({
|
|
24678
24983
|
port,
|
|
24679
24984
|
host,
|
|
24985
|
+
connectionString: `postgres://${auth}${host}:${port}/${encodeURIComponent(name)}`,
|
|
24680
24986
|
database,
|
|
24681
24987
|
server,
|
|
24682
24988
|
get connections() {
|