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