@rebasepro/server-postgres 0.18.0 → 0.18.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/dist/BranchService-ByLfM_Tt.js +360 -0
- package/dist/BranchService-ByLfM_Tt.js.map +1 -0
- package/dist/__vite-browser-external-BnuHet1e.js +46 -0
- package/dist/__vite-browser-external-BnuHet1e.js.map +1 -0
- package/dist/arg-Dni7MzLB.js +87 -0
- package/dist/arg-Dni7MzLB.js.map +1 -0
- package/dist/auth-users-columns-D2LBFrMH.js +116 -0
- package/dist/auth-users-columns-D2LBFrMH.js.map +1 -0
- package/dist/backup-cli-oM9WQVlV.js +408 -0
- package/dist/backup-cli-oM9WQVlV.js.map +1 -0
- package/dist/backup-service-Br3tqwsy.js +715 -0
- package/dist/backup-service-Br3tqwsy.js.map +1 -0
- package/dist/cli-errors-DxN4yzdH.js +657 -0
- package/dist/cli-errors-DxN4yzdH.js.map +1 -0
- package/dist/cli-output-CNdMql-L.js +47 -0
- package/dist/cli-output-CNdMql-L.js.map +1 -0
- package/dist/cli.js +2020 -0
- package/dist/cli.js.map +1 -0
- package/dist/{collection-index-C7LopMyX.js → collection-index-BHgT_TKd.js} +32 -3
- package/dist/{collection-index-C7LopMyX.js.map → collection-index-BHgT_TKd.js.map} +1 -1
- package/dist/{collections-schema-version-BMeu3cgv.js → collections-schema-version-T6cGvrDi.js} +1 -2
- package/dist/{collections-schema-version-BMeu3cgv.js.map → collections-schema-version-T6cGvrDi.js.map} +1 -1
- package/dist/databasePoolManager-Bj5FbeAs.js +349 -0
- package/dist/databasePoolManager-Bj5FbeAs.js.map +1 -0
- package/dist/doctor-D7HWmGzs.js +974 -0
- package/dist/doctor-D7HWmGzs.js.map +1 -0
- package/dist/{ensure-collection-policies-CagOzEPS.js → ensure-collection-policies-2c2NV8zX.js} +5 -25
- package/dist/ensure-collection-policies-2c2NV8zX.js.map +1 -0
- package/dist/ensure-collection-tables-D4wJzM4y.js +820 -0
- package/dist/ensure-collection-tables-D4wJzM4y.js.map +1 -0
- package/dist/ensure-tables-CIojK-Rx.js +650 -0
- package/dist/ensure-tables-CIojK-Rx.js.map +1 -0
- package/dist/generate-drizzle-schema-CwEgVyjO.js +123 -0
- package/dist/generate-drizzle-schema-CwEgVyjO.js.map +1 -0
- package/dist/generate-drizzle-schema-logic-so2Z_lyC.js +528 -0
- package/dist/generate-drizzle-schema-logic-so2Z_lyC.js.map +1 -0
- package/dist/generate-postgres-ddl-logic-CtnpIBVv.js +1312 -0
- package/dist/generate-postgres-ddl-logic-CtnpIBVv.js.map +1 -0
- package/dist/generated-schema-staleness-DQdfJl2A.js +157 -0
- package/dist/generated-schema-staleness-DQdfJl2A.js.map +1 -0
- package/dist/index.es.js +849 -10807
- package/dist/index.es.js.map +1 -1
- package/dist/introspect-db-logic-C6LQdTxj.js +1378 -0
- package/dist/introspect-db-logic-C6LQdTxj.js.map +1 -0
- package/dist/policy-drift-kNJ6X2Uj.js +313 -0
- package/dist/policy-drift-kNJ6X2Uj.js.map +1 -0
- package/dist/rls-bootstrap-sql-DLDnUjtx.js +7092 -0
- package/dist/rls-bootstrap-sql-DLDnUjtx.js.map +1 -0
- package/dist/{rls-enforcement-ClJoTboT.js → rls-enforcement-DeYEjYLt.js} +14 -3
- package/dist/{rls-enforcement-ClJoTboT.js.map → rls-enforcement-DeYEjYLt.js.map} +1 -1
- package/dist/schema/bin/generate-drizzle-schema.d.ts +1 -0
- package/dist/schema/bin/generate-postgres-ddl.d.ts +1 -0
- package/dist/schema/doctor-cli.js +129 -0
- package/dist/schema/doctor-cli.js.map +1 -0
- package/dist/schema/generate-drizzle-schema.d.ts +1 -1
- package/dist/schema/generate-drizzle-schema.js +24 -0
- package/dist/schema/generate-drizzle-schema.js.map +1 -0
- package/dist/schema/generate-postgres-ddl.d.ts +1 -1
- package/dist/schema/generate-postgres-ddl.js +138 -0
- package/dist/schema/generate-postgres-ddl.js.map +1 -0
- package/dist/schema/introspect-db.js +866 -0
- package/dist/schema/introspect-db.js.map +1 -0
- package/dist/source-Br7L7GOI.js +410 -0
- package/dist/source-Br7L7GOI.js.map +1 -0
- package/dist/vector-index-DkWxm2de.js +309 -0
- package/dist/vector-index-DkWxm2de.js.map +1 -0
- package/package.json +7 -7
- package/dist/ensure-collection-policies-CagOzEPS.js.map +0 -1
- package/dist/ensure-collection-tables-DbAhrjnG.js +0 -2732
- package/dist/ensure-collection-tables-DbAhrjnG.js.map +0 -1
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
import { createRequire as __createRequire } from "module";
|
|
2
|
+
__createRequire(import.meta.url);
|
|
3
|
+
import { r as __exportAll } from "./__vite-browser-external-BnuHet1e.js";
|
|
4
|
+
import { l as extractCauseMessage, u as extractPgError } from "./cli-errors-DxN4yzdH.js";
|
|
5
|
+
import { sql } from "drizzle-orm";
|
|
6
|
+
import { revokeInternalTableSql } from "@rebasepro/common";
|
|
7
|
+
//#region src/services/BranchService.ts
|
|
8
|
+
/**
|
|
9
|
+
* BranchService
|
|
10
|
+
*
|
|
11
|
+
* Manages database branching by creating/deleting PostgreSQL databases
|
|
12
|
+
* using `CREATE DATABASE ... TEMPLATE`. Branch metadata is stored in the
|
|
13
|
+
* `rebase.branches` table in the default (main) database, following the
|
|
14
|
+
* same `rebase` schema convention used by entity_history, auth, etc.
|
|
15
|
+
*/
|
|
16
|
+
var BranchService_exports = /* @__PURE__ */ __exportAll({
|
|
17
|
+
BranchService: () => BranchService,
|
|
18
|
+
BranchingUnsupportedError: () => BranchingUnsupportedError
|
|
19
|
+
});
|
|
20
|
+
/** Internal prefix applied to branch database names to avoid collisions. */
|
|
21
|
+
var BRANCH_DB_PREFIX = "rb_";
|
|
22
|
+
/** `duplicate_database` — the target database name is already taken. */
|
|
23
|
+
var PG_DUPLICATE_DATABASE = "42P04";
|
|
24
|
+
/** `object_in_use` — the database still has connections attached. */
|
|
25
|
+
var PG_OBJECT_IN_USE = "55006";
|
|
26
|
+
/**
|
|
27
|
+
* Describe a failed branch DDL statement in terms a user can act on.
|
|
28
|
+
*
|
|
29
|
+
* Drizzle reports failures as `Failed query: <sql> params:` and hides the real
|
|
30
|
+
* PostgreSQL error in the `cause` chain, so matching on `err.message` never sees
|
|
31
|
+
* the actual problem. Match on the PG error code instead — it survives wrapping
|
|
32
|
+
* and, unlike the message text, is not locale-dependent.
|
|
33
|
+
*/
|
|
34
|
+
function describeBranchDdlError(err, fallbackContext) {
|
|
35
|
+
const pgError = extractPgError(err);
|
|
36
|
+
if (pgError?.code === PG_DUPLICATE_DATABASE) return /* @__PURE__ */ new Error(`Database "${fallbackContext}" already exists on the server. Choose a different branch name.`);
|
|
37
|
+
if (pgError?.code === PG_OBJECT_IN_USE) return /* @__PURE__ */ new Error(`Cannot complete the operation: the database "${fallbackContext}" has active connections. Close other clients or connections and try again.`);
|
|
38
|
+
const detail = pgError?.message ?? extractCauseMessage(err);
|
|
39
|
+
if (detail) return new Error(detail);
|
|
40
|
+
return err instanceof Error ? err : new Error(String(err));
|
|
41
|
+
}
|
|
42
|
+
/** Fully-qualified metadata table in the rebase schema. */
|
|
43
|
+
var BRANCHES_TABLE = "rebase.branches";
|
|
44
|
+
/**
|
|
45
|
+
* The connected server cannot branch, so nothing was done.
|
|
46
|
+
*
|
|
47
|
+
* A refusal, not a fault: typed so the transports can forward it intact.
|
|
48
|
+
* `websocket.ts` answers it with its own code and message rather than letting
|
|
49
|
+
* it fall into the generic handler, which suppresses the text in production —
|
|
50
|
+
* and the text is the only part that says what to do instead.
|
|
51
|
+
*/
|
|
52
|
+
var BranchingUnsupportedError = class extends Error {
|
|
53
|
+
/** Wire code, forwarded verbatim to the client. */
|
|
54
|
+
code = "BRANCHING_UNSUPPORTED";
|
|
55
|
+
constructor(message) {
|
|
56
|
+
super(message);
|
|
57
|
+
this.name = "BranchingUnsupportedError";
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* What to say when the server serves one database.
|
|
62
|
+
*
|
|
63
|
+
* Deliberately the same argument the CLI's `refuseBranchOnManagedDatabase`
|
|
64
|
+
* makes, because it is the same refusal — it just used to be available only to
|
|
65
|
+
* whoever came in through argv. One paragraph, because this one has to fit in a
|
|
66
|
+
* toast as well as a terminal.
|
|
67
|
+
*/
|
|
68
|
+
var BRANCHING_UNSUPPORTED_MESSAGE = "Branching does not work on the managed development database. It copies a database with CREATE DATABASE ... TEMPLATE, and the managed database is PGlite, which serves exactly one — the copy would be the original, and every write you meant to sandbox would land in it. Branching needs a real Postgres: run `rebase dev --docker`, or uncomment DATABASE_URL in .env to point at your own.";
|
|
69
|
+
/**
|
|
70
|
+
* Validate that a user-provided identifier only contains safe characters.
|
|
71
|
+
* Throws if the value contains characters outside [a-zA-Z0-9_-].
|
|
72
|
+
*/
|
|
73
|
+
function validateIdentifier(value, label) {
|
|
74
|
+
if (!/^[a-zA-Z0-9_-]+$/.test(value)) throw new Error(`Invalid ${label}: only letters, digits, underscores, and hyphens are allowed.`);
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Postgres truncates identifiers at NAMEDATALEN-1 = 63 bytes, and does it
|
|
78
|
+
* silently. The prefix comes out of the same budget.
|
|
79
|
+
*/
|
|
80
|
+
var MAX_BRANCH_NAME_LENGTH = 60;
|
|
81
|
+
/**
|
|
82
|
+
* Check a user-provided branch name, and otherwise leave it exactly as given.
|
|
83
|
+
*
|
|
84
|
+
* This used to strip everything outside [a-zA-Z0-9_], so `my-feature` was
|
|
85
|
+
* quietly created as `myfeature`: the name you typed was not the name `list`
|
|
86
|
+
* gave back. Nothing ever needed that. Every identifier this service builds is
|
|
87
|
+
* double-quoted (see `CREATE DATABASE` below), which is what makes a hyphen
|
|
88
|
+
* safe, and `validateIdentifier` has always accepted hyphens for the `--from`
|
|
89
|
+
* source database — the two disagreed about the same character class.
|
|
90
|
+
*
|
|
91
|
+
* Refusing a name we cannot represent is better than representing a different
|
|
92
|
+
* one, which is also why the length is checked here rather than left to
|
|
93
|
+
* Postgres, whose answer to an over-long identifier is a silent rename.
|
|
94
|
+
*/
|
|
95
|
+
function assertValidBranchName(name) {
|
|
96
|
+
validateIdentifier(name, "branch name");
|
|
97
|
+
if (name.length > MAX_BRANCH_NAME_LENGTH) throw new Error(`Branch name "${name}" is too long: ${name.length} characters, maximum ${MAX_BRANCH_NAME_LENGTH}. Postgres truncates identifiers past 63 bytes, which would give the branch a name you did not choose.`);
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Convert a user-facing branch name to the actual PostgreSQL database name.
|
|
101
|
+
*/
|
|
102
|
+
function toBranchDbName(name) {
|
|
103
|
+
assertValidBranchName(name);
|
|
104
|
+
return `${BRANCH_DB_PREFIX}${name}`;
|
|
105
|
+
}
|
|
106
|
+
var BranchService = class {
|
|
107
|
+
db;
|
|
108
|
+
poolManager;
|
|
109
|
+
constructor(db, poolManager) {
|
|
110
|
+
this.db = db;
|
|
111
|
+
this.poolManager = poolManager;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Refuse a branch mutation the connected server cannot honour.
|
|
115
|
+
*
|
|
116
|
+
* The check lives here rather than in the CLI's argv parsing because the
|
|
117
|
+
* CLI is not the only caller: Studio reaches `createBranch` over the
|
|
118
|
+
* websocket, and before this it got "Branch created successfully" and a
|
|
119
|
+
* detail pane promising isolation for a database that is the parent.
|
|
120
|
+
*
|
|
121
|
+
* Reads only, and only mutations are guarded: `listBranches` on a managed
|
|
122
|
+
* database is an honest empty list, and refusing it would turn the Branches
|
|
123
|
+
* pane into a failure with a "Try again" that can never succeed.
|
|
124
|
+
*/
|
|
125
|
+
async assertBranchingSupported() {
|
|
126
|
+
if (await this.poolManager.servesOneDatabase()) throw new BranchingUnsupportedError(BRANCHING_UNSUPPORTED_MESSAGE);
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Ensure the `rebase.branches` metadata table exists in the default database.
|
|
130
|
+
* Idempotent — safe to call on every startup.
|
|
131
|
+
*/
|
|
132
|
+
async ensureBranchMetadataTable() {
|
|
133
|
+
await this.db.execute(sql`CREATE SCHEMA IF NOT EXISTS rebase`);
|
|
134
|
+
await this.db.execute(sql.raw(`
|
|
135
|
+
CREATE TABLE IF NOT EXISTS ${BRANCHES_TABLE} (
|
|
136
|
+
name TEXT PRIMARY KEY,
|
|
137
|
+
db_name TEXT NOT NULL UNIQUE,
|
|
138
|
+
parent_db TEXT NOT NULL,
|
|
139
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
140
|
+
metadata JSONB DEFAULT '{}'
|
|
141
|
+
);
|
|
142
|
+
`));
|
|
143
|
+
await this.db.execute(sql.raw(revokeInternalTableSql("rebase", "branches")));
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Create a new branch database by templating the source database.
|
|
147
|
+
*
|
|
148
|
+
* Uses `CREATE DATABASE ... TEMPLATE` for an instant, full-fidelity copy
|
|
149
|
+
* of both schema and data.
|
|
150
|
+
*
|
|
151
|
+
* @param name User-facing branch name (e.g., "feature_auth")
|
|
152
|
+
* @param options.source Source database to clone; defaults to the main database.
|
|
153
|
+
*/
|
|
154
|
+
async createBranch(name, options) {
|
|
155
|
+
await this.assertBranchingSupported();
|
|
156
|
+
if (options?.source) validateIdentifier(options.source, "source database name");
|
|
157
|
+
const dbName = toBranchDbName(name);
|
|
158
|
+
const sourceDb = options?.source || this.poolManager.defaultDatabaseName;
|
|
159
|
+
if ((await this.db.execute(sql`SELECT name FROM rebase.branches WHERE name = ${name} OR db_name = ${dbName}`)).rows.length > 0) throw new Error(`Branch "${name}" already exists.`);
|
|
160
|
+
await this.poolManager.disconnectDatabase(sourceDb);
|
|
161
|
+
if (options?.force) {
|
|
162
|
+
if (await this.terminateConnections(sourceDb) > 0) await new Promise((resolve) => setTimeout(resolve, 250));
|
|
163
|
+
}
|
|
164
|
+
const safeDbName = dbName.replace(/"/g, "\"\"");
|
|
165
|
+
const safeSourceDb = sourceDb.replace(/"/g, "\"\"");
|
|
166
|
+
try {
|
|
167
|
+
await this.db.execute(sql.raw(`CREATE DATABASE "${safeDbName}" TEMPLATE "${safeSourceDb}"`));
|
|
168
|
+
} catch (err) {
|
|
169
|
+
if (extractPgError(err)?.code === PG_OBJECT_IN_USE) throw new Error(`Cannot create branch: the source database "${sourceDb}" has active connections.\n` + await this.describeBlockingConnections(sourceDb));
|
|
170
|
+
throw describeBranchDdlError(err, dbName);
|
|
171
|
+
}
|
|
172
|
+
const now = /* @__PURE__ */ new Date();
|
|
173
|
+
await this.db.execute(sql`INSERT INTO rebase.branches (name, db_name, parent_db, created_at)
|
|
174
|
+
VALUES (${name}, ${dbName}, ${sourceDb}, ${now.toISOString()})`);
|
|
175
|
+
return {
|
|
176
|
+
name,
|
|
177
|
+
parentDatabase: sourceDb,
|
|
178
|
+
createdAt: now
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Delete a branch database and remove its metadata.
|
|
183
|
+
* Cannot delete the main/default database.
|
|
184
|
+
*/
|
|
185
|
+
async deleteBranch(name, options) {
|
|
186
|
+
await this.assertBranchingSupported();
|
|
187
|
+
assertValidBranchName(name);
|
|
188
|
+
if (toBranchDbName(name) === this.poolManager.defaultDatabaseName) throw new Error("Cannot delete the main database.");
|
|
189
|
+
const existingRows = (await this.db.execute(sql`SELECT db_name FROM rebase.branches WHERE name = ${name}`)).rows;
|
|
190
|
+
if (existingRows.length === 0) throw new Error(`Branch "${name}" not found.`);
|
|
191
|
+
const dbName = existingRows[0].db_name;
|
|
192
|
+
if (dbName === this.poolManager.defaultDatabaseName) throw new Error("Cannot delete the main database.");
|
|
193
|
+
await this.poolManager.disconnectDatabase(dbName);
|
|
194
|
+
if (options?.force) {
|
|
195
|
+
if (await this.terminateConnections(dbName) > 0) await new Promise((resolve) => setTimeout(resolve, 250));
|
|
196
|
+
}
|
|
197
|
+
const safeDbName = dbName.replace(/"/g, "\"\"");
|
|
198
|
+
try {
|
|
199
|
+
await this.db.execute(sql.raw(`DROP DATABASE "${safeDbName}"`));
|
|
200
|
+
} catch (err) {
|
|
201
|
+
if (extractPgError(err)?.code === PG_OBJECT_IN_USE) throw new Error(`Cannot delete branch "${name}": the database has active connections.\n` + await this.describeBlockingConnections(dbName));
|
|
202
|
+
throw describeBranchDdlError(err, dbName);
|
|
203
|
+
}
|
|
204
|
+
await this.db.execute(sql`DELETE FROM rebase.branches WHERE name = ${name}`);
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Who is holding the database open, in words a developer can act on.
|
|
208
|
+
*
|
|
209
|
+
* `CREATE DATABASE ... TEMPLATE` and `DROP DATABASE` both require that no
|
|
210
|
+
* other session is connected, and the one thing the old message could not
|
|
211
|
+
* say is the only thing worth knowing: *which* session. "Close other
|
|
212
|
+
* clients or connections and try again" is advice you cannot follow when
|
|
213
|
+
* you do not know there is a `rebase dev` in another terminal — and that is
|
|
214
|
+
* the common case, because wanting a branch and running the app are the
|
|
215
|
+
* same moment.
|
|
216
|
+
*
|
|
217
|
+
* `application_name` is the useful column: node-postgres sends none by
|
|
218
|
+
* default, so a Rebase process shows as `(unnamed)` while DBeaver, pgAdmin
|
|
219
|
+
* and psql all name themselves. Counting by it separates "my own dev
|
|
220
|
+
* server" from "the GUI I forgot about" without any guessing.
|
|
221
|
+
*
|
|
222
|
+
* Best-effort by construction: this runs while reporting a failure, and a
|
|
223
|
+
* diagnostic that throws would replace a real error with its own.
|
|
224
|
+
*/
|
|
225
|
+
async describeBlockingConnections(dbName) {
|
|
226
|
+
try {
|
|
227
|
+
const rows = (await this.db.execute(sql`
|
|
228
|
+
SELECT coalesce(nullif(application_name, ''), '(unnamed)') AS app,
|
|
229
|
+
count(*)::int AS n
|
|
230
|
+
FROM pg_stat_activity
|
|
231
|
+
WHERE datname = ${dbName}
|
|
232
|
+
AND pid <> pg_backend_pid()
|
|
233
|
+
GROUP BY 1
|
|
234
|
+
ORDER BY 2 DESC, 1
|
|
235
|
+
`)).rows;
|
|
236
|
+
if (rows.length === 0) return " Retry — whatever was connected has since disconnected.";
|
|
237
|
+
return ` Connected right now:\n${rows.map((row) => ` ${row.n} × ${String(row.app)}`).join("\n")}\n A running \`rebase dev\` is the usual one — stop it, or re-run with --force to
|
|
238
|
+
disconnect them for you.`;
|
|
239
|
+
} catch {
|
|
240
|
+
return " Close other clients and connections, or re-run with --force.";
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Disconnect every other session on a database, and say how many.
|
|
245
|
+
*
|
|
246
|
+
* `pg_terminate_backend` rather than `pg_cancel_backend`: cancelling a
|
|
247
|
+
* query leaves the session connected, and a connected session is exactly
|
|
248
|
+
* what blocks the operation.
|
|
249
|
+
*
|
|
250
|
+
* The current backend is excluded — terminating the connection running this
|
|
251
|
+
* statement would abort the command that asked for it.
|
|
252
|
+
*/
|
|
253
|
+
async terminateConnections(dbName) {
|
|
254
|
+
return (await this.db.execute(sql`
|
|
255
|
+
SELECT pg_terminate_backend(pid)
|
|
256
|
+
FROM pg_stat_activity
|
|
257
|
+
WHERE datname = ${dbName}
|
|
258
|
+
AND pid <> pg_backend_pid()
|
|
259
|
+
`)).rows.length;
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Everything prune needs to decide, in two queries.
|
|
263
|
+
*
|
|
264
|
+
* Reads the rows and the server's database list rather than joining them,
|
|
265
|
+
* so the *disagreements* between the two are visible — a row whose database
|
|
266
|
+
* is gone and a database with no row are the two things prune exists to
|
|
267
|
+
* find, and a join hides both.
|
|
268
|
+
*/
|
|
269
|
+
async pruneCandidates() {
|
|
270
|
+
return {
|
|
271
|
+
rows: (await this.db.execute(sql.raw(`
|
|
272
|
+
SELECT name, db_name, created_at FROM ${BRANCHES_TABLE} ORDER BY created_at
|
|
273
|
+
`))).rows.map((row) => ({
|
|
274
|
+
name: row.name,
|
|
275
|
+
dbName: row.db_name,
|
|
276
|
+
createdAt: new Date(row.created_at)
|
|
277
|
+
})),
|
|
278
|
+
databases: (await this.db.execute(sql`
|
|
279
|
+
SELECT datname FROM pg_database WHERE NOT datistemplate
|
|
280
|
+
`)).rows.map((row) => String(row.datname))
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
/** Remove a metadata row whose database is already gone. */
|
|
284
|
+
async forgetBranchRow(name) {
|
|
285
|
+
await this.db.execute(sql`DELETE FROM rebase.branches WHERE name = ${name}`);
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* Drop a database by name, with no metadata row required.
|
|
289
|
+
*
|
|
290
|
+
* `deleteBranch` deliberately takes its database name from the row, because
|
|
291
|
+
* the row is the only value that is true by construction. Prune has the
|
|
292
|
+
* opposite job — the cases it handles are exactly the ones where row and
|
|
293
|
+
* database disagree — so it needs to name a database directly. Guarded the
|
|
294
|
+
* same way regardless: the main database is never droppable.
|
|
295
|
+
*/
|
|
296
|
+
async dropDatabase(dbName) {
|
|
297
|
+
validateIdentifier(dbName, "database name");
|
|
298
|
+
if (dbName === this.poolManager.defaultDatabaseName) throw new Error("Cannot delete the main database.");
|
|
299
|
+
await this.poolManager.disconnectDatabase(dbName);
|
|
300
|
+
try {
|
|
301
|
+
await this.db.execute(sql.raw(`DROP DATABASE "${dbName.replace(/"/g, "\"\"")}"`));
|
|
302
|
+
} catch (err) {
|
|
303
|
+
throw describeBranchDdlError(err, dbName);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* List all branches recorded in the metadata table.
|
|
308
|
+
* Optionally fetches database sizes from pg_database.
|
|
309
|
+
*/
|
|
310
|
+
async listBranches() {
|
|
311
|
+
return (await this.db.execute(sql.raw(`
|
|
312
|
+
SELECT
|
|
313
|
+
b.name,
|
|
314
|
+
b.parent_db,
|
|
315
|
+
b.created_at,
|
|
316
|
+
pg_database_size(b.db_name) as size_bytes
|
|
317
|
+
FROM ${BRANCHES_TABLE} b
|
|
318
|
+
JOIN pg_database d ON d.datname = b.db_name
|
|
319
|
+
ORDER BY b.created_at DESC
|
|
320
|
+
`))).rows.map((row) => ({
|
|
321
|
+
name: row.name,
|
|
322
|
+
parentDatabase: row.parent_db,
|
|
323
|
+
createdAt: new Date(row.created_at),
|
|
324
|
+
sizeBytes: row.size_bytes != null ? Number(row.size_bytes) : void 0
|
|
325
|
+
}));
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* Get info about a specific branch.
|
|
329
|
+
*/
|
|
330
|
+
async getBranchInfo(name) {
|
|
331
|
+
assertValidBranchName(name);
|
|
332
|
+
const rows = (await this.db.execute(sql`
|
|
333
|
+
SELECT
|
|
334
|
+
b.name,
|
|
335
|
+
b.db_name,
|
|
336
|
+
b.parent_db,
|
|
337
|
+
b.created_at
|
|
338
|
+
FROM rebase.branches b
|
|
339
|
+
WHERE b.name = ${name}
|
|
340
|
+
`)).rows;
|
|
341
|
+
if (rows.length === 0) return void 0;
|
|
342
|
+
const row = rows[0];
|
|
343
|
+
let sizeBytes;
|
|
344
|
+
try {
|
|
345
|
+
const dbName = row.db_name;
|
|
346
|
+
const sizeRows = (await this.db.execute(sql`SELECT pg_database_size(${dbName}) as size_bytes`)).rows;
|
|
347
|
+
if (sizeRows.length > 0 && sizeRows[0].size_bytes != null) sizeBytes = Number(sizeRows[0].size_bytes);
|
|
348
|
+
} catch {}
|
|
349
|
+
return {
|
|
350
|
+
name: row.name,
|
|
351
|
+
parentDatabase: row.parent_db,
|
|
352
|
+
createdAt: new Date(row.created_at),
|
|
353
|
+
sizeBytes
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
};
|
|
357
|
+
//#endregion
|
|
358
|
+
export { BranchService_exports as n, BranchingUnsupportedError as r, BranchService as t };
|
|
359
|
+
|
|
360
|
+
//# sourceMappingURL=BranchService-ByLfM_Tt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BranchService-ByLfM_Tt.js","names":[],"sources":["../src/services/BranchService.ts"],"sourcesContent":["/**\n * BranchService\n *\n * Manages database branching by creating/deleting PostgreSQL databases\n * using `CREATE DATABASE ... TEMPLATE`. Branch metadata is stored in the\n * `rebase.branches` table in the default (main) database, following the\n * same `rebase` schema convention used by entity_history, auth, etc.\n */\n\nimport { sql } from \"drizzle-orm\";\nimport { BranchInfo } from \"@rebasepro/types\";\nimport { revokeInternalTableSql } from \"@rebasepro/common\";\nimport { DrizzleClient } from \"../interfaces\";\nimport { BranchRow } from \"../branch-prune\";\nimport { DatabasePoolManager } from \"../databasePoolManager\";\nimport { extractPgError, extractCauseMessage } from \"../utils/pg-error-utils\";\n\n/** Internal prefix applied to branch database names to avoid collisions. */\nconst BRANCH_DB_PREFIX = \"rb_\";\n\n/** `duplicate_database` — the target database name is already taken. */\nconst PG_DUPLICATE_DATABASE = \"42P04\";\n\n/** `object_in_use` — the database still has connections attached. */\nconst PG_OBJECT_IN_USE = \"55006\";\n\n/**\n * Describe a failed branch DDL statement in terms a user can act on.\n *\n * Drizzle reports failures as `Failed query: <sql> params:` and hides the real\n * PostgreSQL error in the `cause` chain, so matching on `err.message` never sees\n * the actual problem. Match on the PG error code instead — it survives wrapping\n * and, unlike the message text, is not locale-dependent.\n */\nfunction describeBranchDdlError(err: unknown, fallbackContext: string): Error {\n const pgError = extractPgError(err);\n\n if (pgError?.code === PG_DUPLICATE_DATABASE) {\n return new Error(`Database \"${fallbackContext}\" already exists on the server. Choose a different branch name.`);\n }\n if (pgError?.code === PG_OBJECT_IN_USE) {\n return new Error(\n `Cannot complete the operation: the database \"${fallbackContext}\" has active connections. ` +\n \"Close other clients or connections and try again.\"\n );\n }\n\n // Unknown failure: surface the real PG message rather than the Drizzle\n // wrapper, which would otherwise show the raw SQL and no reason at all.\n const detail = pgError?.message ?? extractCauseMessage(err);\n if (detail) return new Error(detail);\n return err instanceof Error ? err : new Error(String(err));\n}\n\n/** Fully-qualified metadata table in the rebase schema. */\nconst BRANCHES_TABLE = \"rebase.branches\";\n\n/**\n * The connected server cannot branch, so nothing was done.\n *\n * A refusal, not a fault: typed so the transports can forward it intact.\n * `websocket.ts` answers it with its own code and message rather than letting\n * it fall into the generic handler, which suppresses the text in production —\n * and the text is the only part that says what to do instead.\n */\nexport class BranchingUnsupportedError extends Error {\n /** Wire code, forwarded verbatim to the client. */\n readonly code = \"BRANCHING_UNSUPPORTED\";\n\n constructor(message: string) {\n super(message);\n this.name = \"BranchingUnsupportedError\";\n }\n}\n\n/**\n * What to say when the server serves one database.\n *\n * Deliberately the same argument the CLI's `refuseBranchOnManagedDatabase`\n * makes, because it is the same refusal — it just used to be available only to\n * whoever came in through argv. One paragraph, because this one has to fit in a\n * toast as well as a terminal.\n */\nconst BRANCHING_UNSUPPORTED_MESSAGE =\n \"Branching does not work on the managed development database. It copies a database with \"\n + \"CREATE DATABASE ... TEMPLATE, and the managed database is PGlite, which serves exactly one — \"\n + \"the copy would be the original, and every write you meant to sandbox would land in it. \"\n + \"Branching needs a real Postgres: run `rebase dev --docker`, or uncomment DATABASE_URL in .env \"\n + \"to point at your own.\";\n\n/**\n * Validate that a user-provided identifier only contains safe characters.\n * Throws if the value contains characters outside [a-zA-Z0-9_-].\n */\nfunction validateIdentifier(value: string, label: string): void {\n if (!/^[a-zA-Z0-9_-]+$/.test(value)) {\n throw new Error(`Invalid ${label}: only letters, digits, underscores, and hyphens are allowed.`);\n }\n}\n\n/**\n * Postgres truncates identifiers at NAMEDATALEN-1 = 63 bytes, and does it\n * silently. The prefix comes out of the same budget.\n */\nconst MAX_BRANCH_NAME_LENGTH = 63 - BRANCH_DB_PREFIX.length;\n\n/**\n * Check a user-provided branch name, and otherwise leave it exactly as given.\n *\n * This used to strip everything outside [a-zA-Z0-9_], so `my-feature` was\n * quietly created as `myfeature`: the name you typed was not the name `list`\n * gave back. Nothing ever needed that. Every identifier this service builds is\n * double-quoted (see `CREATE DATABASE` below), which is what makes a hyphen\n * safe, and `validateIdentifier` has always accepted hyphens for the `--from`\n * source database — the two disagreed about the same character class.\n *\n * Refusing a name we cannot represent is better than representing a different\n * one, which is also why the length is checked here rather than left to\n * Postgres, whose answer to an over-long identifier is a silent rename.\n */\nfunction assertValidBranchName(name: string): void {\n validateIdentifier(name, \"branch name\");\n if (name.length > MAX_BRANCH_NAME_LENGTH) {\n throw new Error(\n `Branch name \"${name}\" is too long: ${name.length} characters, maximum ${MAX_BRANCH_NAME_LENGTH}. ` +\n \"Postgres truncates identifiers past 63 bytes, which would give the branch a name you did not choose.\"\n );\n }\n}\n\n/**\n * Convert a user-facing branch name to the actual PostgreSQL database name.\n */\nfunction toBranchDbName(name: string): string {\n assertValidBranchName(name);\n return `${BRANCH_DB_PREFIX}${name}`;\n}\n\nexport class BranchService {\n constructor(\n private db: DrizzleClient,\n private poolManager: DatabasePoolManager\n ) {}\n\n /**\n * Refuse a branch mutation the connected server cannot honour.\n *\n * The check lives here rather than in the CLI's argv parsing because the\n * CLI is not the only caller: Studio reaches `createBranch` over the\n * websocket, and before this it got \"Branch created successfully\" and a\n * detail pane promising isolation for a database that is the parent.\n *\n * Reads only, and only mutations are guarded: `listBranches` on a managed\n * database is an honest empty list, and refusing it would turn the Branches\n * pane into a failure with a \"Try again\" that can never succeed.\n */\n private async assertBranchingSupported(): Promise<void> {\n if (await this.poolManager.servesOneDatabase()) {\n throw new BranchingUnsupportedError(BRANCHING_UNSUPPORTED_MESSAGE);\n }\n }\n\n /**\n * Ensure the `rebase.branches` metadata table exists in the default database.\n * Idempotent — safe to call on every startup.\n */\n async ensureBranchMetadataTable(): Promise<void> {\n // Create the rebase schema (idempotent — may already exist from auth/history init)\n await this.db.execute(sql`CREATE SCHEMA IF NOT EXISTS rebase`);\n\n await this.db.execute(sql.raw(`\n CREATE TABLE IF NOT EXISTS ${BRANCHES_TABLE} (\n name TEXT PRIMARY KEY,\n db_name TEXT NOT NULL UNIQUE,\n parent_db TEXT NOT NULL,\n created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),\n metadata JSONB DEFAULT '{}'\n );\n `));\n\n // Not a collection, so no RLS — and it names every branch database on\n // this server. The driver's schema-wide grant reaches it, so revoke.\n await this.db.execute(sql.raw(revokeInternalTableSql(\"rebase\", \"branches\")));\n }\n\n /**\n * Create a new branch database by templating the source database.\n *\n * Uses `CREATE DATABASE ... TEMPLATE` for an instant, full-fidelity copy\n * of both schema and data.\n *\n * @param name User-facing branch name (e.g., \"feature_auth\")\n * @param options.source Source database to clone; defaults to the main database.\n */\n async createBranch(\n name: string,\n options?: { source?: string; force?: boolean }\n ): Promise<BranchInfo> {\n await this.assertBranchingSupported();\n\n if (options?.source) {\n validateIdentifier(options.source, \"source database name\");\n }\n\n const dbName = toBranchDbName(name);\n const sourceDb = options?.source || this.poolManager.defaultDatabaseName;\n\n // Check if branch already exists\n const existing = await this.db.execute(\n sql`SELECT name FROM rebase.branches WHERE name = ${name} OR db_name = ${dbName}`\n );\n if ((existing.rows as unknown[]).length > 0) {\n throw new Error(`Branch \"${name}\" already exists.`);\n }\n\n // Disconnect any idle pools to the source DB so TEMPLATE works.\n // CREATE DATABASE ... TEMPLATE requires no other connections to the template.\n //\n // This only reaches pools inside *this* process. The CLI runs as its own\n // process, so a `rebase dev` holding the database open is untouched by\n // it — which is why the failure below has to name what is connected, and\n // why `--force` exists.\n await this.poolManager.disconnectDatabase(sourceDb);\n\n if (options?.force) {\n const closed = await this.terminateConnections(sourceDb);\n if (closed > 0) {\n // Postgres reports the backend as gone before its slot is\n // actually free, and CREATE DATABASE then fails on a connection\n // that no longer exists. One short settle beats a confusing retry.\n await new Promise((resolve) => setTimeout(resolve, 250));\n }\n }\n\n // Create the database using the source as a template.\n // Note: Identifiers must be double-quoted, not parameterized.\n const safeDbName = dbName.replace(/\"/g, '\"\"');\n const safeSourceDb = sourceDb.replace(/\"/g, '\"\"');\n try {\n await this.db.execute(\n sql.raw(`CREATE DATABASE \"${safeDbName}\" TEMPLATE \"${safeSourceDb}\"`)\n );\n } catch (err) {\n const pgError = extractPgError(err);\n if (pgError?.code === PG_OBJECT_IN_USE) {\n // The template — not the new database — is the one still in use.\n throw new Error(\n `Cannot create branch: the source database \"${sourceDb}\" has active connections.\\n`\n + await this.describeBlockingConnections(sourceDb)\n );\n }\n throw describeBranchDdlError(err, dbName);\n }\n\n // Record metadata in the default database\n const now = new Date();\n await this.db.execute(\n sql`INSERT INTO rebase.branches (name, db_name, parent_db, created_at)\n VALUES (${name}, ${dbName}, ${sourceDb}, ${now.toISOString()})`\n );\n\n return {\n name,\n parentDatabase: sourceDb,\n createdAt: now\n };\n }\n\n /**\n * Delete a branch database and remove its metadata.\n * Cannot delete the main/default database.\n */\n async deleteBranch(name: string, options?: { force?: boolean }): Promise<void> {\n await this.assertBranchingSupported();\n\n assertValidBranchName(name);\n\n // Safety, first pass: a request that would target the default database\n // is refused before any metadata is read, so the answer costs nothing\n // and cannot depend on what a row happens to say.\n if (toBranchDbName(name) === this.poolManager.defaultDatabaseName) {\n throw new Error(\"Cannot delete the main database.\");\n }\n\n // Verify the branch exists, and take the database name from the row\n // rather than deriving it again. A branch created before names stopped\n // being stripped is recorded as `rb_myfeature` while `my-feature` now\n // derives `rb_my-feature`; re-deriving would drop a database this row\n // never named — or, if that name happened to exist, somebody else's.\n // The stored value is the only one that is true by construction.\n const existing = await this.db.execute(\n sql`SELECT db_name FROM rebase.branches WHERE name = ${name}`\n );\n const existingRows = existing.rows as Record<string, unknown>[];\n if (existingRows.length === 0) {\n throw new Error(`Branch \"${name}\" not found.`);\n }\n const dbName = existingRows[0].db_name as string;\n\n // Safety, second pass: the first checked a name we derived, this checks\n // the one we are about to drop. They differ for any row written under\n // the old scheme, and it is this one that governs.\n if (dbName === this.poolManager.defaultDatabaseName) {\n throw new Error(\"Cannot delete the main database.\");\n }\n\n // Disconnect any pools to this branch before dropping. Same limit as\n // `createBranch`: in-process pools only.\n await this.poolManager.disconnectDatabase(dbName);\n\n if (options?.force) {\n const closed = await this.terminateConnections(dbName);\n if (closed > 0) await new Promise((resolve) => setTimeout(resolve, 250));\n }\n\n // Drop the database\n const safeDbName = dbName.replace(/\"/g, '\"\"');\n try {\n await this.db.execute(sql.raw(`DROP DATABASE \"${safeDbName}\"`));\n } catch (err) {\n const pgError = extractPgError(err);\n if (pgError?.code === PG_OBJECT_IN_USE) {\n throw new Error(\n `Cannot delete branch \"${name}\": the database has active connections.\\n`\n + await this.describeBlockingConnections(dbName)\n );\n }\n throw describeBranchDdlError(err, dbName);\n }\n\n // Remove metadata\n await this.db.execute(\n sql`DELETE FROM rebase.branches WHERE name = ${name}`\n );\n }\n\n /**\n * Who is holding the database open, in words a developer can act on.\n *\n * `CREATE DATABASE ... TEMPLATE` and `DROP DATABASE` both require that no\n * other session is connected, and the one thing the old message could not\n * say is the only thing worth knowing: *which* session. \"Close other\n * clients or connections and try again\" is advice you cannot follow when\n * you do not know there is a `rebase dev` in another terminal — and that is\n * the common case, because wanting a branch and running the app are the\n * same moment.\n *\n * `application_name` is the useful column: node-postgres sends none by\n * default, so a Rebase process shows as `(unnamed)` while DBeaver, pgAdmin\n * and psql all name themselves. Counting by it separates \"my own dev\n * server\" from \"the GUI I forgot about\" without any guessing.\n *\n * Best-effort by construction: this runs while reporting a failure, and a\n * diagnostic that throws would replace a real error with its own.\n */\n private async describeBlockingConnections(dbName: string): Promise<string> {\n try {\n const result = await this.db.execute(sql`\n SELECT coalesce(nullif(application_name, ''), '(unnamed)') AS app,\n count(*)::int AS n\n FROM pg_stat_activity\n WHERE datname = ${dbName}\n AND pid <> pg_backend_pid()\n GROUP BY 1\n ORDER BY 2 DESC, 1\n `);\n const rows = result.rows as Record<string, unknown>[];\n if (rows.length === 0) {\n // The blocker went away between the failure and this query, or\n // it is a session Postgres does not attribute to this database.\n return \" Retry — whatever was connected has since disconnected.\";\n }\n\n const listed = rows\n .map((row) => ` ${row.n} × ${String(row.app)}`)\n .join(\"\\n\");\n\n return ` Connected right now:\\n${listed}\\n`\n + \" A running `rebase dev` is the usual one — stop it, or re-run with --force to\\n\"\n + \" disconnect them for you.\";\n } catch {\n return \" Close other clients and connections, or re-run with --force.\";\n }\n }\n\n /**\n * Disconnect every other session on a database, and say how many.\n *\n * `pg_terminate_backend` rather than `pg_cancel_backend`: cancelling a\n * query leaves the session connected, and a connected session is exactly\n * what blocks the operation.\n *\n * The current backend is excluded — terminating the connection running this\n * statement would abort the command that asked for it.\n */\n private async terminateConnections(dbName: string): Promise<number> {\n const result = await this.db.execute(sql`\n SELECT pg_terminate_backend(pid)\n FROM pg_stat_activity\n WHERE datname = ${dbName}\n AND pid <> pg_backend_pid()\n `);\n\n return (result.rows as unknown[]).length;\n }\n\n /**\n * Everything prune needs to decide, in two queries.\n *\n * Reads the rows and the server's database list rather than joining them,\n * so the *disagreements* between the two are visible — a row whose database\n * is gone and a database with no row are the two things prune exists to\n * find, and a join hides both.\n */\n async pruneCandidates(): Promise<{ rows: BranchRow[]; databases: string[] }> {\n const rowResult = await this.db.execute(sql.raw(`\n SELECT name, db_name, created_at FROM ${BRANCHES_TABLE} ORDER BY created_at\n `));\n const rows = (rowResult.rows as Record<string, unknown>[]).map((row) => ({\n name: row.name as string,\n dbName: row.db_name as string,\n createdAt: new Date(row.created_at as string)\n }));\n\n const dbResult = await this.db.execute(sql`\n SELECT datname FROM pg_database WHERE NOT datistemplate\n `);\n const databases = (dbResult.rows as Record<string, unknown>[]).map((row) => String(row.datname));\n\n return { rows, databases };\n }\n\n /** Remove a metadata row whose database is already gone. */\n async forgetBranchRow(name: string): Promise<void> {\n await this.db.execute(sql`DELETE FROM rebase.branches WHERE name = ${name}`);\n }\n\n /**\n * Drop a database by name, with no metadata row required.\n *\n * `deleteBranch` deliberately takes its database name from the row, because\n * the row is the only value that is true by construction. Prune has the\n * opposite job — the cases it handles are exactly the ones where row and\n * database disagree — so it needs to name a database directly. Guarded the\n * same way regardless: the main database is never droppable.\n */\n async dropDatabase(dbName: string): Promise<void> {\n validateIdentifier(dbName, \"database name\");\n if (dbName === this.poolManager.defaultDatabaseName) {\n throw new Error(\"Cannot delete the main database.\");\n }\n\n await this.poolManager.disconnectDatabase(dbName);\n try {\n await this.db.execute(sql.raw(`DROP DATABASE \"${dbName.replace(/\"/g, '\"\"')}\"`));\n } catch (err) {\n throw describeBranchDdlError(err, dbName);\n }\n }\n\n /**\n * List all branches recorded in the metadata table.\n * Optionally fetches database sizes from pg_database.\n */\n async listBranches(): Promise<BranchInfo[]> {\n const result = await this.db.execute(sql.raw(`\n SELECT \n b.name,\n b.parent_db,\n b.created_at,\n pg_database_size(b.db_name) as size_bytes\n FROM ${BRANCHES_TABLE} b\n JOIN pg_database d ON d.datname = b.db_name\n ORDER BY b.created_at DESC\n `));\n\n return (result.rows as Record<string, unknown>[]).map((row) => ({\n name: row.name as string,\n parentDatabase: row.parent_db as string,\n createdAt: new Date(row.created_at as string),\n sizeBytes: row.size_bytes != null ? Number(row.size_bytes) : undefined\n }));\n }\n\n /**\n * Get info about a specific branch.\n */\n async getBranchInfo(name: string): Promise<BranchInfo | undefined> {\n assertValidBranchName(name);\n\n const result = await this.db.execute(sql`\n SELECT\n b.name,\n b.db_name,\n b.parent_db,\n b.created_at\n FROM rebase.branches b\n WHERE b.name = ${name}\n `);\n\n const rows = result.rows as Record<string, unknown>[];\n if (rows.length === 0) return undefined;\n\n const row = rows[0];\n\n // Attempt to get size — may fail if the DB was externally dropped.\n // Same reason as `deleteBranch`: the size of a re-derived name is the\n // size of some other database, or of nothing.\n let sizeBytes: number | undefined;\n try {\n const dbName = row.db_name as string;\n const sizeResult = await this.db.execute(\n sql`SELECT pg_database_size(${dbName}) as size_bytes`\n );\n const sizeRows = sizeResult.rows as Record<string, unknown>[];\n if (sizeRows.length > 0 && sizeRows[0].size_bytes != null) {\n sizeBytes = Number(sizeRows[0].size_bytes);\n }\n } catch {\n // Database might not exist anymore\n }\n\n return {\n name: row.name as string,\n parentDatabase: row.parent_db as string,\n createdAt: new Date(row.created_at as string),\n sizeBytes\n };\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { createRequire as __createRequire } from "module";
|
|
2
|
+
const require = __createRequire(import.meta.url);
|
|
3
|
+
//#region \0rolldown/runtime.js
|
|
4
|
+
var __create = Object.create;
|
|
5
|
+
var __defProp = Object.defineProperty;
|
|
6
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
7
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
8
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
9
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
10
|
+
var __commonJSMin = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).exports, mod), cb = null), mod.exports);
|
|
11
|
+
var __exportAll = (all, no_symbols) => {
|
|
12
|
+
let target = {};
|
|
13
|
+
for (var name in all) __defProp(target, name, {
|
|
14
|
+
get: all[name],
|
|
15
|
+
enumerable: true
|
|
16
|
+
});
|
|
17
|
+
if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
|
|
18
|
+
return target;
|
|
19
|
+
};
|
|
20
|
+
var __copyProps = (to, from, except, desc) => {
|
|
21
|
+
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
22
|
+
key = keys[i];
|
|
23
|
+
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
24
|
+
get: ((k) => from[k]).bind(null, key),
|
|
25
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
return to;
|
|
29
|
+
};
|
|
30
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
31
|
+
value: mod,
|
|
32
|
+
enumerable: true
|
|
33
|
+
}) : target, mod));
|
|
34
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, { get: (a, b) => (typeof require !== "undefined" ? require : a)[b] }) : x)(function(x) {
|
|
35
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
36
|
+
throw Error("Calling `require` for \"" + x + "\" in an environment that doesn't expose the `require` function. See https://rolldown.rs/in-depth/bundling-cjs#require-external-modules for more details.");
|
|
37
|
+
});
|
|
38
|
+
//#endregion
|
|
39
|
+
//#region __vite-browser-external
|
|
40
|
+
var require___vite_browser_external = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
41
|
+
module.exports = {};
|
|
42
|
+
}));
|
|
43
|
+
//#endregion
|
|
44
|
+
export { __toESM as a, __require as i, __commonJSMin as n, __exportAll as r, require___vite_browser_external as t };
|
|
45
|
+
|
|
46
|
+
//# sourceMappingURL=__vite-browser-external-BnuHet1e.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"__vite-browser-external-BnuHet1e.js","names":[],"sources":["../__vite-browser-external"],"sourcesContent":["module.exports = {}"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAAA,OAAO,UAAU,CAAC"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { createRequire as __createRequire } from "module";
|
|
2
|
+
__createRequire(import.meta.url);
|
|
3
|
+
import { n as __commonJSMin } from "./__vite-browser-external-BnuHet1e.js";
|
|
4
|
+
//#region ../../node_modules/.pnpm/arg@5.0.2/node_modules/arg/index.js
|
|
5
|
+
var require_arg = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
6
|
+
var flagSymbol = Symbol("arg flag");
|
|
7
|
+
var ArgError = class ArgError extends Error {
|
|
8
|
+
constructor(msg, code) {
|
|
9
|
+
super(msg);
|
|
10
|
+
this.name = "ArgError";
|
|
11
|
+
this.code = code;
|
|
12
|
+
Object.setPrototypeOf(this, ArgError.prototype);
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
function arg(opts, { argv = process.argv.slice(2), permissive = false, stopAtPositional = false } = {}) {
|
|
16
|
+
if (!opts) throw new ArgError("argument specification object is required", "ARG_CONFIG_NO_SPEC");
|
|
17
|
+
const result = { _: [] };
|
|
18
|
+
const aliases = {};
|
|
19
|
+
const handlers = {};
|
|
20
|
+
for (const key of Object.keys(opts)) {
|
|
21
|
+
if (!key) throw new ArgError("argument key cannot be an empty string", "ARG_CONFIG_EMPTY_KEY");
|
|
22
|
+
if (key[0] !== "-") throw new ArgError(`argument key must start with '-' but found: '${key}'`, "ARG_CONFIG_NONOPT_KEY");
|
|
23
|
+
if (key.length === 1) throw new ArgError(`argument key must have a name; singular '-' keys are not allowed: ${key}`, "ARG_CONFIG_NONAME_KEY");
|
|
24
|
+
if (typeof opts[key] === "string") {
|
|
25
|
+
aliases[key] = opts[key];
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
let type = opts[key];
|
|
29
|
+
let isFlag = false;
|
|
30
|
+
if (Array.isArray(type) && type.length === 1 && typeof type[0] === "function") {
|
|
31
|
+
const [fn] = type;
|
|
32
|
+
type = (value, name, prev = []) => {
|
|
33
|
+
prev.push(fn(value, name, prev[prev.length - 1]));
|
|
34
|
+
return prev;
|
|
35
|
+
};
|
|
36
|
+
isFlag = fn === Boolean || fn[flagSymbol] === true;
|
|
37
|
+
} else if (typeof type === "function") isFlag = type === Boolean || type[flagSymbol] === true;
|
|
38
|
+
else throw new ArgError(`type missing or not a function or valid array type: ${key}`, "ARG_CONFIG_VAD_TYPE");
|
|
39
|
+
if (key[1] !== "-" && key.length > 2) throw new ArgError(`short argument keys (with a single hyphen) must have only one character: ${key}`, "ARG_CONFIG_SHORTOPT_TOOLONG");
|
|
40
|
+
handlers[key] = [type, isFlag];
|
|
41
|
+
}
|
|
42
|
+
for (let i = 0, len = argv.length; i < len; i++) {
|
|
43
|
+
const wholeArg = argv[i];
|
|
44
|
+
if (stopAtPositional && result._.length > 0) {
|
|
45
|
+
result._ = result._.concat(argv.slice(i));
|
|
46
|
+
break;
|
|
47
|
+
}
|
|
48
|
+
if (wholeArg === "--") {
|
|
49
|
+
result._ = result._.concat(argv.slice(i + 1));
|
|
50
|
+
break;
|
|
51
|
+
}
|
|
52
|
+
if (wholeArg.length > 1 && wholeArg[0] === "-") {
|
|
53
|
+
const separatedArguments = wholeArg[1] === "-" || wholeArg.length === 2 ? [wholeArg] : wholeArg.slice(1).split("").map((a) => `-${a}`);
|
|
54
|
+
for (let j = 0; j < separatedArguments.length; j++) {
|
|
55
|
+
const arg = separatedArguments[j];
|
|
56
|
+
const [originalArgName, argStr] = arg[1] === "-" ? arg.split(/=(.*)/, 2) : [arg, void 0];
|
|
57
|
+
let argName = originalArgName;
|
|
58
|
+
while (argName in aliases) argName = aliases[argName];
|
|
59
|
+
if (!(argName in handlers)) if (permissive) {
|
|
60
|
+
result._.push(arg);
|
|
61
|
+
continue;
|
|
62
|
+
} else throw new ArgError(`unknown or unexpected option: ${originalArgName}`, "ARG_UNKNOWN_OPTION");
|
|
63
|
+
const [type, isFlag] = handlers[argName];
|
|
64
|
+
if (!isFlag && j + 1 < separatedArguments.length) throw new ArgError(`option requires argument (but was followed by another short argument): ${originalArgName}`, "ARG_MISSING_REQUIRED_SHORTARG");
|
|
65
|
+
if (isFlag) result[argName] = type(true, argName, result[argName]);
|
|
66
|
+
else if (argStr === void 0) {
|
|
67
|
+
if (argv.length < i + 2 || argv[i + 1].length > 1 && argv[i + 1][0] === "-" && !(argv[i + 1].match(/^-?\d*(\.(?=\d))?\d*$/) && (type === Number || typeof BigInt !== "undefined" && type === BigInt))) throw new ArgError(`option requires argument: ${originalArgName}${originalArgName === argName ? "" : ` (alias for ${argName})`}`, "ARG_MISSING_REQUIRED_LONGARG");
|
|
68
|
+
result[argName] = type(argv[i + 1], argName, result[argName]);
|
|
69
|
+
++i;
|
|
70
|
+
} else result[argName] = type(argStr, argName, result[argName]);
|
|
71
|
+
}
|
|
72
|
+
} else result._.push(wholeArg);
|
|
73
|
+
}
|
|
74
|
+
return result;
|
|
75
|
+
}
|
|
76
|
+
arg.flag = (fn) => {
|
|
77
|
+
fn[flagSymbol] = true;
|
|
78
|
+
return fn;
|
|
79
|
+
};
|
|
80
|
+
arg.COUNT = arg.flag((v, name, existingCount) => (existingCount || 0) + 1);
|
|
81
|
+
arg.ArgError = ArgError;
|
|
82
|
+
module.exports = arg;
|
|
83
|
+
}));
|
|
84
|
+
//#endregion
|
|
85
|
+
export { require_arg as t };
|
|
86
|
+
|
|
87
|
+
//# sourceMappingURL=arg-Dni7MzLB.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"arg-Dni7MzLB.js","names":[],"sources":["../../../node_modules/.pnpm/arg@5.0.2/node_modules/arg/index.js"],"sourcesContent":["const flagSymbol = Symbol('arg flag');\n\nclass ArgError extends Error {\n\tconstructor(msg, code) {\n\t\tsuper(msg);\n\t\tthis.name = 'ArgError';\n\t\tthis.code = code;\n\n\t\tObject.setPrototypeOf(this, ArgError.prototype);\n\t}\n}\n\nfunction arg(\n\topts,\n\t{\n\t\targv = process.argv.slice(2),\n\t\tpermissive = false,\n\t\tstopAtPositional = false\n\t} = {}\n) {\n\tif (!opts) {\n\t\tthrow new ArgError(\n\t\t\t'argument specification object is required',\n\t\t\t'ARG_CONFIG_NO_SPEC'\n\t\t);\n\t}\n\n\tconst result = { _: [] };\n\n\tconst aliases = {};\n\tconst handlers = {};\n\n\tfor (const key of Object.keys(opts)) {\n\t\tif (!key) {\n\t\t\tthrow new ArgError(\n\t\t\t\t'argument key cannot be an empty string',\n\t\t\t\t'ARG_CONFIG_EMPTY_KEY'\n\t\t\t);\n\t\t}\n\n\t\tif (key[0] !== '-') {\n\t\t\tthrow new ArgError(\n\t\t\t\t`argument key must start with '-' but found: '${key}'`,\n\t\t\t\t'ARG_CONFIG_NONOPT_KEY'\n\t\t\t);\n\t\t}\n\n\t\tif (key.length === 1) {\n\t\t\tthrow new ArgError(\n\t\t\t\t`argument key must have a name; singular '-' keys are not allowed: ${key}`,\n\t\t\t\t'ARG_CONFIG_NONAME_KEY'\n\t\t\t);\n\t\t}\n\n\t\tif (typeof opts[key] === 'string') {\n\t\t\taliases[key] = opts[key];\n\t\t\tcontinue;\n\t\t}\n\n\t\tlet type = opts[key];\n\t\tlet isFlag = false;\n\n\t\tif (\n\t\t\tArray.isArray(type) &&\n\t\t\ttype.length === 1 &&\n\t\t\ttypeof type[0] === 'function'\n\t\t) {\n\t\t\tconst [fn] = type;\n\t\t\ttype = (value, name, prev = []) => {\n\t\t\t\tprev.push(fn(value, name, prev[prev.length - 1]));\n\t\t\t\treturn prev;\n\t\t\t};\n\t\t\tisFlag = fn === Boolean || fn[flagSymbol] === true;\n\t\t} else if (typeof type === 'function') {\n\t\t\tisFlag = type === Boolean || type[flagSymbol] === true;\n\t\t} else {\n\t\t\tthrow new ArgError(\n\t\t\t\t`type missing or not a function or valid array type: ${key}`,\n\t\t\t\t'ARG_CONFIG_VAD_TYPE'\n\t\t\t);\n\t\t}\n\n\t\tif (key[1] !== '-' && key.length > 2) {\n\t\t\tthrow new ArgError(\n\t\t\t\t`short argument keys (with a single hyphen) must have only one character: ${key}`,\n\t\t\t\t'ARG_CONFIG_SHORTOPT_TOOLONG'\n\t\t\t);\n\t\t}\n\n\t\thandlers[key] = [type, isFlag];\n\t}\n\n\tfor (let i = 0, len = argv.length; i < len; i++) {\n\t\tconst wholeArg = argv[i];\n\n\t\tif (stopAtPositional && result._.length > 0) {\n\t\t\tresult._ = result._.concat(argv.slice(i));\n\t\t\tbreak;\n\t\t}\n\n\t\tif (wholeArg === '--') {\n\t\t\tresult._ = result._.concat(argv.slice(i + 1));\n\t\t\tbreak;\n\t\t}\n\n\t\tif (wholeArg.length > 1 && wholeArg[0] === '-') {\n\t\t\t/* eslint-disable operator-linebreak */\n\t\t\tconst separatedArguments =\n\t\t\t\twholeArg[1] === '-' || wholeArg.length === 2\n\t\t\t\t\t? [wholeArg]\n\t\t\t\t\t: wholeArg\n\t\t\t\t\t\t\t.slice(1)\n\t\t\t\t\t\t\t.split('')\n\t\t\t\t\t\t\t.map((a) => `-${a}`);\n\t\t\t/* eslint-enable operator-linebreak */\n\n\t\t\tfor (let j = 0; j < separatedArguments.length; j++) {\n\t\t\t\tconst arg = separatedArguments[j];\n\t\t\t\tconst [originalArgName, argStr] =\n\t\t\t\t\targ[1] === '-' ? arg.split(/=(.*)/, 2) : [arg, undefined];\n\n\t\t\t\tlet argName = originalArgName;\n\t\t\t\twhile (argName in aliases) {\n\t\t\t\t\targName = aliases[argName];\n\t\t\t\t}\n\n\t\t\t\tif (!(argName in handlers)) {\n\t\t\t\t\tif (permissive) {\n\t\t\t\t\t\tresult._.push(arg);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthrow new ArgError(\n\t\t\t\t\t\t\t`unknown or unexpected option: ${originalArgName}`,\n\t\t\t\t\t\t\t'ARG_UNKNOWN_OPTION'\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tconst [type, isFlag] = handlers[argName];\n\n\t\t\t\tif (!isFlag && j + 1 < separatedArguments.length) {\n\t\t\t\t\tthrow new ArgError(\n\t\t\t\t\t\t`option requires argument (but was followed by another short argument): ${originalArgName}`,\n\t\t\t\t\t\t'ARG_MISSING_REQUIRED_SHORTARG'\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tif (isFlag) {\n\t\t\t\t\tresult[argName] = type(true, argName, result[argName]);\n\t\t\t\t} else if (argStr === undefined) {\n\t\t\t\t\tif (\n\t\t\t\t\t\targv.length < i + 2 ||\n\t\t\t\t\t\t(argv[i + 1].length > 1 &&\n\t\t\t\t\t\t\targv[i + 1][0] === '-' &&\n\t\t\t\t\t\t\t!(\n\t\t\t\t\t\t\t\targv[i + 1].match(/^-?\\d*(\\.(?=\\d))?\\d*$/) &&\n\t\t\t\t\t\t\t\t(type === Number ||\n\t\t\t\t\t\t\t\t\t// eslint-disable-next-line no-undef\n\t\t\t\t\t\t\t\t\t(typeof BigInt !== 'undefined' && type === BigInt))\n\t\t\t\t\t\t\t))\n\t\t\t\t\t) {\n\t\t\t\t\t\tconst extended =\n\t\t\t\t\t\t\toriginalArgName === argName ? '' : ` (alias for ${argName})`;\n\t\t\t\t\t\tthrow new ArgError(\n\t\t\t\t\t\t\t`option requires argument: ${originalArgName}${extended}`,\n\t\t\t\t\t\t\t'ARG_MISSING_REQUIRED_LONGARG'\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\n\t\t\t\t\tresult[argName] = type(argv[i + 1], argName, result[argName]);\n\t\t\t\t\t++i;\n\t\t\t\t} else {\n\t\t\t\t\tresult[argName] = type(argStr, argName, result[argName]);\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tresult._.push(wholeArg);\n\t\t}\n\t}\n\n\treturn result;\n}\n\narg.flag = (fn) => {\n\tfn[flagSymbol] = true;\n\treturn fn;\n};\n\n// Utility types\narg.COUNT = arg.flag((v, name, existingCount) => (existingCount || 0) + 1);\n\n// Expose error class\narg.ArgError = ArgError;\n\nmodule.exports = arg;\n"],"x_google_ignoreList":[0],"mappings":"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"}
|