@rebasepro/server-postgres 0.17.3 → 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/LICENSE +0 -1
- package/README.md +31 -0
- 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-argv.d.ts +19 -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/branch-argv.d.ts +28 -0
- package/dist/branch-prune.d.ts +77 -0
- package/dist/cli-collections-path.d.ts +31 -0
- package/dist/cli-errors-DxN4yzdH.js +657 -0
- package/dist/cli-errors-DxN4yzdH.js.map +1 -0
- package/dist/cli-errors.d.ts +123 -0
- package/dist/cli-flags.d.ts +101 -0
- package/dist/cli-helpers.d.ts +94 -10
- 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-BHgT_TKd.js +276 -0
- package/dist/collection-index-BHgT_TKd.js.map +1 -0
- 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/{connection-GOKU3Hu5.js → databasePoolManager-Bj5FbeAs.js} +112 -40
- package/dist/databasePoolManager-Bj5FbeAs.js.map +1 -0
- package/dist/databasePoolManager.d.ts +27 -0
- package/dist/doctor-D7HWmGzs.js +974 -0
- package/dist/doctor-D7HWmGzs.js.map +1 -0
- package/dist/{ensure-collection-policies-UCqgv_8c.js → ensure-collection-policies-2c2NV8zX.js} +7 -27
- 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/{ensure-collection-tables-DgVixhX3.js → generate-postgres-ddl-logic-CtnpIBVv.js} +126 -984
- 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.d.ts +12 -0
- package/dist/index.es.js +7423 -1133
- 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/{backup-service-DCk7KhhL.js → rls-bootstrap-sql-DLDnUjtx.js} +737 -2689
- package/dist/rls-bootstrap-sql-DLDnUjtx.js.map +1 -0
- package/dist/{rls-enforcement-DvAbL9YJ.js → rls-enforcement-DeYEjYLt.js} +65 -190
- package/dist/rls-enforcement-DeYEjYLt.js.map +1 -0
- 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/column-type-drift.d.ts +79 -0
- package/dist/schema/doctor-cli.js +129 -0
- package/dist/schema/doctor-cli.js.map +1 -0
- package/dist/schema/doctor.d.ts +42 -0
- package/dist/schema/ensure-collection-tables.d.ts +90 -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-next-step.d.ts +30 -0
- package/dist/schema/generate-postgres-ddl-logic.d.ts +28 -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/generated-schema-staleness.d.ts +37 -0
- package/dist/schema/introspect-db-queries.d.ts +34 -0
- package/dist/schema/introspect-db.js +866 -0
- package/dist/schema/introspect-db.js.map +1 -0
- package/dist/security/rls-enforcement.d.ts +63 -2
- package/dist/services/BranchService.d.ts +86 -1
- package/dist/services/collection-helpers.d.ts +1 -1
- package/dist/services/realtimeService.d.ts +30 -2
- package/dist/services/row-pipeline.d.ts +12 -1
- package/dist/source-Br7L7GOI.js +410 -0
- package/dist/source-Br7L7GOI.js.map +1 -0
- package/dist/utils/pg-error-utils.d.ts +3 -1
- package/dist/utils/pg-numeric-number-patch.d.ts +31 -0
- package/dist/vector-index-DkWxm2de.js +309 -0
- package/dist/vector-index-DkWxm2de.js.map +1 -0
- package/package.json +32 -27
- package/dist/backup-service-DCk7KhhL.js.map +0 -1
- package/dist/collection-index-BRUg10H5.js +0 -5690
- package/dist/collection-index-BRUg10H5.js.map +0 -1
- package/dist/connection-GOKU3Hu5.js.map +0 -1
- package/dist/ensure-collection-policies-UCqgv_8c.js.map +0 -1
- package/dist/ensure-collection-tables-DgVixhX3.js.map +0 -1
- package/dist/rls-bootstrap-sql-B5C9LoJ6.js +0 -244
- package/dist/rls-bootstrap-sql-B5C9LoJ6.js.map +0 -1
- package/dist/rls-enforcement-DvAbL9YJ.js.map +0 -1
- package/dist/src-DiB5RP2Z.js +0 -541
- package/dist/src-DiB5RP2Z.js.map +0 -1
- package/dist/websocket-BZ4H5wUz.js +0 -8198
- package/dist/websocket-BZ4H5wUz.js.map +0 -1
- package/src/PostgresAdapter.ts +0 -91
- package/src/PostgresBackendDriver.ts +0 -1857
- package/src/PostgresBootstrapper.ts +0 -1157
- package/src/auth/ensure-tables.ts +0 -896
- package/src/auth/schema-version.ts +0 -260
- package/src/auth/services.ts +0 -1610
- package/src/backup/backup-cli.ts +0 -520
- package/src/backup/backup-cron.ts +0 -212
- package/src/backup/backup-logic.ts +0 -93
- package/src/backup/backup-service.ts +0 -485
- package/src/backup/index.ts +0 -12
- package/src/backup/pg-tools.ts +0 -403
- package/src/backup/retention.ts +0 -86
- package/src/cli-errors.ts +0 -265
- package/src/cli-helpers.ts +0 -569
- package/src/cli-output.ts +0 -43
- package/src/cli.ts +0 -1339
- package/src/collections/PostgresCollectionRegistry.ts +0 -108
- package/src/collections/buildRegistry.ts +0 -68
- package/src/collections/validate-relations.ts +0 -402
- package/src/connection.ts +0 -333
- package/src/data-transformer.ts +0 -845
- package/src/databasePoolManager.ts +0 -95
- package/src/history/HistoryService.ts +0 -254
- package/src/history/ensure-history-table.ts +0 -53
- package/src/index.ts +0 -19
- package/src/interfaces.ts +0 -60
- package/src/module-dir.ts +0 -7
- package/src/schema/atlas-argv.ts +0 -94
- package/src/schema/auth-schema.ts +0 -298
- package/src/schema/auth-users-columns.ts +0 -131
- package/src/schema/carved-out-migration.ts +0 -404
- package/src/schema/classify-change.ts +0 -436
- package/src/schema/collection-index.ts +0 -427
- package/src/schema/collections-schema-version.ts +0 -103
- package/src/schema/destructive-sql.ts +0 -94
- package/src/schema/doctor-cli.ts +0 -66
- package/src/schema/doctor-policy-checks.ts +0 -105
- package/src/schema/doctor.ts +0 -837
- package/src/schema/drizzle-ddl.ts +0 -33
- package/src/schema/dynamic-tables.test.ts +0 -302
- package/src/schema/dynamic-tables.ts +0 -293
- package/src/schema/ensure-collection-policies.ts +0 -198
- package/src/schema/ensure-collection-tables.test.ts +0 -518
- package/src/schema/ensure-collection-tables.ts +0 -1267
- package/src/schema/generate-drizzle-schema-logic.ts +0 -957
- package/src/schema/generate-drizzle-schema.ts +0 -143
- package/src/schema/generate-postgres-ddl-logic.ts +0 -1339
- package/src/schema/generate-postgres-ddl.ts +0 -180
- package/src/schema/generate-schema-commit.ts +0 -267
- package/src/schema/generated-schema-staleness.ts +0 -284
- package/src/schema/introspect-db-constraints.ts +0 -385
- package/src/schema/introspect-db-inference.ts +0 -255
- package/src/schema/introspect-db-logic.ts +0 -1387
- package/src/schema/introspect-db-naming.ts +0 -15
- package/src/schema/introspect-db-project.ts +0 -78
- package/src/schema/introspect-db-queries.ts +0 -326
- package/src/schema/introspect-db-structure.ts +0 -670
- package/src/schema/introspect-db-types.ts +0 -56
- package/src/schema/introspect-db.ts +0 -257
- package/src/schema/introspect-runtime.test.ts +0 -260
- package/src/schema/introspect-runtime.ts +0 -327
- package/src/schema/non-sql-collections.test.ts +0 -131
- package/src/schema/rls-bootstrap-sql.ts +0 -288
- package/src/schema/search-column.ts +0 -643
- package/src/schema/test-schema.ts +0 -11
- package/src/schema/vector-index.ts +0 -491
- package/src/security/anonymous-grants.test.ts +0 -73
- package/src/security/policy-drift.test.ts +0 -435
- package/src/security/policy-drift.ts +0 -473
- package/src/security/rls-enforcement.ts +0 -505
- package/src/services/BranchService.ts +0 -326
- package/src/services/FetchService.ts +0 -2115
- package/src/services/PersistService.ts +0 -557
- package/src/services/RelationService.ts +0 -978
- package/src/services/RelationWriteService.ts +0 -653
- package/src/services/cdc/CdcListener.ts +0 -103
- package/src/services/cdc/junction-tables.ts +0 -91
- package/src/services/cdc/trigger-cdc.ts +0 -173
- package/src/services/channel-bus/ChannelBus.ts +0 -44
- package/src/services/channel-bus/PostgresChannelBus.ts +0 -299
- package/src/services/channel-bus/index.ts +0 -114
- package/src/services/channel-history.ts +0 -424
- package/src/services/channel-presence.ts +0 -185
- package/src/services/collection-helpers.ts +0 -420
- package/src/services/dataService.ts +0 -256
- package/src/services/index.ts +0 -14
- package/src/services/junction-writes.ts +0 -295
- package/src/services/nested-path.ts +0 -145
- package/src/services/pg-notify-listener.ts +0 -151
- package/src/services/realtimeService.ts +0 -2545
- package/src/services/row-pipeline.ts +0 -240
- package/src/services/write-denial.ts +0 -55
- package/src/types.ts +0 -4
- package/src/utils/connection-string.ts +0 -58
- package/src/utils/drizzle-conditions.ts +0 -2743
- package/src/utils/pg-array-null-patch.ts +0 -42
- package/src/utils/pg-error-utils.ts +0 -391
- package/src/websocket.ts +0 -855
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { createRequire as __createRequire } from "module";
|
|
2
|
-
import process from "process";
|
|
3
2
|
__createRequire(import.meta.url);
|
|
4
|
-
import {
|
|
5
|
-
import "./collection-index-
|
|
6
|
-
import "./
|
|
3
|
+
import { a as __toESM, i as __require, n as __commonJSMin, r as __exportAll, t as require___vite_browser_external } from "./__vite-browser-external-BnuHet1e.js";
|
|
4
|
+
import { o as isRebaseIndexName } from "./collection-index-BHgT_TKd.js";
|
|
5
|
+
import { r as outWarn, t as out } from "./cli-output-CNdMql-L.js";
|
|
6
|
+
import { t as chalk } from "./source-Br7L7GOI.js";
|
|
7
|
+
import { LEGACY_RLS_SCHEMA, REBASE_SCHEMA, isManyToMany } from "@rebasepro/types";
|
|
7
8
|
import fs from "fs";
|
|
8
9
|
import path from "path";
|
|
9
|
-
import os from "os";
|
|
10
10
|
import { fileURLToPath } from "node:url";
|
|
11
11
|
import { ChildProcess, execFile, spawn, spawnSync } from "node:child_process";
|
|
12
12
|
import { StringDecoder } from "node:string_decoder";
|
|
@@ -23,8 +23,8 @@ import { appendFileSync, createReadStream, createWriteStream, readFileSync, stat
|
|
|
23
23
|
import { finished } from "node:stream/promises";
|
|
24
24
|
import { Duplex, PassThrough, Readable, Transform, Writable, getDefaultHighWaterMark } from "node:stream";
|
|
25
25
|
import { Buffer as Buffer$1 } from "node:buffer";
|
|
26
|
-
import "module";
|
|
27
|
-
import { fileURLToPath as fileURLToPath$1 } from "url";
|
|
26
|
+
import { createRequire } from "module";
|
|
27
|
+
import { fileURLToPath as fileURLToPath$1, pathToFileURL } from "url";
|
|
28
28
|
//#region src/utils/connection-string.ts
|
|
29
29
|
/**
|
|
30
30
|
* Connection-string rewrites for the tools Rebase shells out to.
|
|
@@ -80,321 +80,6 @@ function forLibpq(connectionString) {
|
|
|
80
80
|
return url.toString();
|
|
81
81
|
}
|
|
82
82
|
//#endregion
|
|
83
|
-
//#region src/backup/pg-tools.ts
|
|
84
|
-
/**
|
|
85
|
-
* Pure helpers for the backup/restore commands.
|
|
86
|
-
*
|
|
87
|
-
* Everything in this file is side-effect free so it can be unit-tested
|
|
88
|
-
* without a live Postgres server, matching the constraint that CI must
|
|
89
|
-
* not require a database.
|
|
90
|
-
*/
|
|
91
|
-
/**
|
|
92
|
-
* Extract the database name from a Postgres connection string.
|
|
93
|
-
* Returns `null` when the URL has no database path (e.g. bare host).
|
|
94
|
-
*/
|
|
95
|
-
function parseDbNameFromUrl(connectionString) {
|
|
96
|
-
try {
|
|
97
|
-
const name = new URL(connectionString).pathname.replace(/^\//, "").trim();
|
|
98
|
-
return name.length > 0 ? name : null;
|
|
99
|
-
} catch {
|
|
100
|
-
const match = connectionString.match(/\/([^/?]+)(\?|$)/);
|
|
101
|
-
return match && match[1] ? match[1] : null;
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
/**
|
|
105
|
-
* Swap the database name in a connection string, preserving credentials,
|
|
106
|
-
* host, port and query params. Used by `--target-db` / `--create-db` so a
|
|
107
|
-
* restore can target a fresh database instead of clobbering the live one.
|
|
108
|
-
*/
|
|
109
|
-
function withDatabaseName(connectionString, dbName) {
|
|
110
|
-
try {
|
|
111
|
-
const parsed = new URL(connectionString);
|
|
112
|
-
parsed.pathname = `/${dbName}`;
|
|
113
|
-
return parsed.toString();
|
|
114
|
-
} catch {
|
|
115
|
-
return connectionString.replace(/\/([^/?]+)(\?|$)/, `/${dbName}$2`);
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
/**
|
|
119
|
-
* Parse the major version out of `pg_dump --version` / `pg_restore --version`
|
|
120
|
-
* output, e.g. `"pg_dump (PostgreSQL) 16.2"` → `16`. Handles the pre-10
|
|
121
|
-
* `9.6.x` scheme (returns `9`) and beta strings like `"17beta1"`.
|
|
122
|
-
*/
|
|
123
|
-
function parsePgToolMajor(versionOutput) {
|
|
124
|
-
const match = versionOutput.match(/(\d+)(?:\.(\d+))?/);
|
|
125
|
-
if (!match) return null;
|
|
126
|
-
const major = Number(match[1]);
|
|
127
|
-
if (!Number.isFinite(major)) return null;
|
|
128
|
-
if (major === 9 && match[2] !== void 0) return 9;
|
|
129
|
-
return major;
|
|
130
|
-
}
|
|
131
|
-
/**
|
|
132
|
-
* Convert `SELECT current_setting('server_version_num')` (e.g. `160002`)
|
|
133
|
-
* into a major version (`16`). Also accepts pre-10 encodings like `90603`
|
|
134
|
-
* → `9`.
|
|
135
|
-
*/
|
|
136
|
-
function serverVersionNumToMajor(versionNum) {
|
|
137
|
-
const num = typeof versionNum === "string" ? Number(versionNum) : versionNum;
|
|
138
|
-
if (!Number.isFinite(num) || num <= 0) return null;
|
|
139
|
-
if (num < 1e5) return Math.floor(num / 1e4);
|
|
140
|
-
return Math.floor(num / 1e4);
|
|
141
|
-
}
|
|
142
|
-
/**
|
|
143
|
-
* pg_dump / pg_restore must be **at least** as new as the server they talk
|
|
144
|
-
* to. A newer client against an older server is supported; an older client
|
|
145
|
-
* against a newer server is not and produces corrupt or rejected output.
|
|
146
|
-
*/
|
|
147
|
-
function checkToolServerCompatibility(toolMajor, serverMajor) {
|
|
148
|
-
if (toolMajor === null) return {
|
|
149
|
-
compatible: false,
|
|
150
|
-
reason: "Could not determine the client tool version."
|
|
151
|
-
};
|
|
152
|
-
if (serverMajor === null) return {
|
|
153
|
-
compatible: false,
|
|
154
|
-
reason: "Could not determine the Postgres server version."
|
|
155
|
-
};
|
|
156
|
-
if (toolMajor < serverMajor) return {
|
|
157
|
-
compatible: false,
|
|
158
|
-
reason: `Client tool is Postgres ${toolMajor} but the server is Postgres ${serverMajor}. pg_dump/pg_restore must be the same major version as the server or newer. Install Postgres ${serverMajor} client tools.`
|
|
159
|
-
};
|
|
160
|
-
return { compatible: true };
|
|
161
|
-
}
|
|
162
|
-
/**
|
|
163
|
-
* Build a deterministic, sortable backup file name:
|
|
164
|
-
* `rebase-<db>-<YYYYMMDD>T<HHMMSS>Z.dump`
|
|
165
|
-
*
|
|
166
|
-
* The UTC timestamp is embedded so retention pruning can recover the
|
|
167
|
-
* creation time from the object key alone, without extra metadata.
|
|
168
|
-
*/
|
|
169
|
-
function buildBackupFilename(dbName, date = /* @__PURE__ */ new Date()) {
|
|
170
|
-
const stamp = date.toISOString().replace(/\.\d+Z$/, "Z").replace(/[-:]/g, "");
|
|
171
|
-
return `rebase-${dbName.replace(/[^a-zA-Z0-9_-]/g, "_")}-${stamp}.dump`;
|
|
172
|
-
}
|
|
173
|
-
/**
|
|
174
|
-
* Recover the creation timestamp encoded in a backup file name by
|
|
175
|
-
* {@link buildBackupFilename}. Returns `null` for names that don't match,
|
|
176
|
-
* so foreign objects in a shared prefix are never pruned.
|
|
177
|
-
*/
|
|
178
|
-
function parseBackupTimestamp(fileName) {
|
|
179
|
-
const match = (fileName.split("/").pop() ?? fileName).match(/-(\d{8})T(\d{6})Z\.dump$/);
|
|
180
|
-
if (!match) return null;
|
|
181
|
-
const [, ymd, hms] = match;
|
|
182
|
-
const iso = `${ymd.slice(0, 4)}-${ymd.slice(4, 6)}-${ymd.slice(6, 8)}T${hms.slice(0, 2)}:${hms.slice(2, 4)}:${hms.slice(4, 6)}Z`;
|
|
183
|
-
const date = new Date(iso);
|
|
184
|
-
return Number.isNaN(date.getTime()) ? null : date;
|
|
185
|
-
}
|
|
186
|
-
/**
|
|
187
|
-
* Parse a destination string into a structured {@link BackupDestination}.
|
|
188
|
-
* `s3://bucket/prefix` and `gs://bucket/prefix` map to object storage;
|
|
189
|
-
* anything else is treated as a local path.
|
|
190
|
-
*/
|
|
191
|
-
function parseBackupDestination(out) {
|
|
192
|
-
const s3 = out.match(/^s3:\/\/([^/]+)\/?(.*)$/);
|
|
193
|
-
if (s3) return {
|
|
194
|
-
kind: "s3",
|
|
195
|
-
bucket: s3[1],
|
|
196
|
-
prefix: stripTrailingSlash(s3[2])
|
|
197
|
-
};
|
|
198
|
-
const gcs = out.match(/^gs:\/\/([^/]+)\/?(.*)$/);
|
|
199
|
-
if (gcs) return {
|
|
200
|
-
kind: "gcs",
|
|
201
|
-
bucket: gcs[1],
|
|
202
|
-
prefix: stripTrailingSlash(gcs[2])
|
|
203
|
-
};
|
|
204
|
-
return {
|
|
205
|
-
kind: "local",
|
|
206
|
-
path: out
|
|
207
|
-
};
|
|
208
|
-
}
|
|
209
|
-
function stripTrailingSlash(s) {
|
|
210
|
-
return s.replace(/\/+$/, "");
|
|
211
|
-
}
|
|
212
|
-
/**
|
|
213
|
-
* Join a storage prefix and a file name without producing a leading or
|
|
214
|
-
* doubled slash.
|
|
215
|
-
*/
|
|
216
|
-
function joinStorageKey(prefix, fileName) {
|
|
217
|
-
const clean = prefix.replace(/^\/+|\/+$/g, "");
|
|
218
|
-
return clean.length > 0 ? `${clean}/${fileName}` : fileName;
|
|
219
|
-
}
|
|
220
|
-
/**
|
|
221
|
-
* `PGOPTIONS` carrying an identity, for a libpq tool that has no other way to
|
|
222
|
-
* set a GUC.
|
|
223
|
-
*
|
|
224
|
-
* A backslash escape rather than quoting, which is what libpq's `-c` parsing
|
|
225
|
-
* takes: a space inside a value ends the option otherwise, so a role list is
|
|
226
|
-
* comma-joined and never spaced.
|
|
227
|
-
*/
|
|
228
|
-
function buildRowSecurityPgOptions(identity) {
|
|
229
|
-
const escape = (value) => value.replace(/([\\ ])/g, "\\$1");
|
|
230
|
-
return [
|
|
231
|
-
`-c app.uid=${escape(identity.uid)}`,
|
|
232
|
-
`-c app.user_id=${escape(identity.uid)}`,
|
|
233
|
-
`-c app.user_roles=${escape(identity.roles.join(","))}`
|
|
234
|
-
].join(" ");
|
|
235
|
-
}
|
|
236
|
-
/**
|
|
237
|
-
* Assemble the `pg_dump` argument vector. Uses the custom format (`-Fc`),
|
|
238
|
-
* which is compressed and restorable selectively via `pg_restore`.
|
|
239
|
-
*/
|
|
240
|
-
function buildPgDumpArgs(opts) {
|
|
241
|
-
const args = [
|
|
242
|
-
"--format=custom",
|
|
243
|
-
"--no-password",
|
|
244
|
-
`--file=${opts.outFile}`
|
|
245
|
-
];
|
|
246
|
-
if (opts.noOwner) args.push("--no-owner");
|
|
247
|
-
if (opts.rowSecurity) args.push("--enable-row-security");
|
|
248
|
-
for (const schema of opts.excludeSchemas ?? []) args.push(`--exclude-schema=${schema}`);
|
|
249
|
-
args.push(forLibpq(opts.connectionString));
|
|
250
|
-
return args;
|
|
251
|
-
}
|
|
252
|
-
/**
|
|
253
|
-
* Whether a `pg_dump` failure is the row-security one, and what to do about it.
|
|
254
|
-
*
|
|
255
|
-
* The error text names the table and nothing else, so the first read of it is
|
|
256
|
-
* "why would a backup be affected by RLS at all?" — the answer being that the
|
|
257
|
-
* dumping role is not the tables' owner and has no `BYPASSRLS`, which is the
|
|
258
|
-
* normal state of the `postgres` user on Cloud SQL, RDS and every other managed
|
|
259
|
-
* Postgres. Nothing about that is visible from the message.
|
|
260
|
-
*
|
|
261
|
-
* Returns `null` for any other failure, so the caller reports it unchanged.
|
|
262
|
-
*/
|
|
263
|
-
function diagnoseRowSecurityDumpFailure(error) {
|
|
264
|
-
const text = [error?.stderr, error?.message].map((part) => typeof part === "string" ? part : "").join("\n");
|
|
265
|
-
if (!/row-level security policy/i.test(text)) return null;
|
|
266
|
-
const table = text.match(/for table "([^"]+)"/)?.[1];
|
|
267
|
-
return [
|
|
268
|
-
`pg_dump cannot read ${table ? `"${table}"` : "one of the tables"} because row-level security applies to it.`,
|
|
269
|
-
"",
|
|
270
|
-
" The dumping role is neither the table's owner nor `BYPASSRLS`, which is the normal",
|
|
271
|
-
" state of the `postgres` user on Cloud SQL, RDS and other managed Postgres — there is",
|
|
272
|
-
" no superuser to hand out.",
|
|
273
|
-
"",
|
|
274
|
-
" Two ways out:",
|
|
275
|
-
"",
|
|
276
|
-
" • Grant the dumping role BYPASSRLS, or make it the owner, and run this again. The",
|
|
277
|
-
" dump then contains every row, which is what a backup should mean.",
|
|
278
|
-
"",
|
|
279
|
-
" • Re-run with --enable-row-security to dump as an admin subject instead. Rebase",
|
|
280
|
-
" sets `app.uid`/`app.user_roles` so the generated `admin_full_access` policy",
|
|
281
|
-
" admits the dump. Read the warning it prints: the result contains exactly the",
|
|
282
|
-
" rows those policies admit, and any table whose policies do not include an",
|
|
283
|
-
" admin rule comes out short — with no error.",
|
|
284
|
-
"",
|
|
285
|
-
" Do not reach for a bare `pg_dump --enable-row-security` by hand. Without the",
|
|
286
|
-
" settings above it succeeds and silently omits rows."
|
|
287
|
-
].join("\n");
|
|
288
|
-
}
|
|
289
|
-
/**
|
|
290
|
-
* Assemble the `pg_restore` argument vector for a custom-format dump.
|
|
291
|
-
*/
|
|
292
|
-
function buildPgRestoreArgs(opts) {
|
|
293
|
-
const args = [
|
|
294
|
-
"--format=custom",
|
|
295
|
-
"--no-password",
|
|
296
|
-
`--dbname=${forLibpq(opts.connectionString)}`
|
|
297
|
-
];
|
|
298
|
-
if (opts.clean) args.push("--clean", "--if-exists");
|
|
299
|
-
if (opts.noOwner) args.push("--no-owner");
|
|
300
|
-
if (opts.exitOnError !== false) args.push("--exit-on-error");
|
|
301
|
-
args.push(opts.inputFile);
|
|
302
|
-
return args;
|
|
303
|
-
}
|
|
304
|
-
/**
|
|
305
|
-
* Assemble the `pg_restore --list` argument vector. Reading a dump's table
|
|
306
|
-
* of contents parses the whole archive without touching a database, so it is
|
|
307
|
-
* a cheap integrity check that the file isn't truncated or corrupt.
|
|
308
|
-
*/
|
|
309
|
-
function buildPgRestoreListArgs(inputFile) {
|
|
310
|
-
return ["--list", inputFile];
|
|
311
|
-
}
|
|
312
|
-
/**
|
|
313
|
-
* Assemble the `pg_dumpall --globals-only` argument vector. Roles (and other
|
|
314
|
-
* cluster-wide objects) live outside any single database, so a per-database
|
|
315
|
-
* `pg_dump` omits them. Without the `rebase_user` role the RLS GRANT
|
|
316
|
-
* statements in the main dump fail on restore and RLS is silently lost — so
|
|
317
|
-
* every backup captures the globals into a sidecar `.globals.sql`.
|
|
318
|
-
*
|
|
319
|
-
* `--no-role-passwords` keeps role secrets out of the artifact (backups may
|
|
320
|
-
* be shipped off-box); roles are recreated password-less and re-secured by
|
|
321
|
-
* the operator.
|
|
322
|
-
*/
|
|
323
|
-
function buildPgDumpallGlobalsArgs(opts) {
|
|
324
|
-
return [
|
|
325
|
-
"--globals-only",
|
|
326
|
-
"--no-role-passwords",
|
|
327
|
-
"--no-password",
|
|
328
|
-
`--file=${opts.outFile}`,
|
|
329
|
-
`--dbname=${forLibpq(opts.connectionString)}`
|
|
330
|
-
];
|
|
331
|
-
}
|
|
332
|
-
/**
|
|
333
|
-
* Derive the globals sidecar path/key for a given `.dump` file. Keeps the
|
|
334
|
-
* two artifacts adjacent so listing, uploading and pruning can find one from
|
|
335
|
-
* the other. A name that doesn't end in `.dump` is returned unchanged with a
|
|
336
|
-
* `.globals.sql` suffix appended.
|
|
337
|
-
*/
|
|
338
|
-
function globalsFileForDump(dumpPath) {
|
|
339
|
-
return dumpPath.endsWith(".dump") ? dumpPath.slice(0, -5) + ".globals.sql" : dumpPath + ".globals.sql";
|
|
340
|
-
}
|
|
341
|
-
/**
|
|
342
|
-
* Split a `pg_dumpall --globals-only` script into individual statements.
|
|
343
|
-
* Used when replaying globals on restore so each `CREATE ROLE` / `GRANT`
|
|
344
|
-
* can run independently and a benign "role already exists" on one doesn't
|
|
345
|
-
* abort the rest. Drops `--` comment lines and blank statements.
|
|
346
|
-
*/
|
|
347
|
-
function splitGlobalsStatements(sql) {
|
|
348
|
-
return sql.split("\n").filter((line) => {
|
|
349
|
-
const trimmed = line.trim();
|
|
350
|
-
return !trimmed.startsWith("--") && !trimmed.startsWith("\\");
|
|
351
|
-
}).join("\n").split(";").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
352
|
-
}
|
|
353
|
-
/**
|
|
354
|
-
* Resolve the Postgres connection string the backup commands should use,
|
|
355
|
-
* mirroring the precedence the branch command already relies on.
|
|
356
|
-
*/
|
|
357
|
-
function resolveConnectionString(env) {
|
|
358
|
-
return env.DATABASE_URL || env.ADMIN_CONNECTION_STRING || null;
|
|
359
|
-
}
|
|
360
|
-
//#endregion
|
|
361
|
-
//#region src/backup/retention.ts
|
|
362
|
-
/**
|
|
363
|
-
* Retention pruning for scheduled backups. Pure logic — no I/O — so it can
|
|
364
|
-
* be exhaustively unit-tested without touching a storage backend.
|
|
365
|
-
*/
|
|
366
|
-
/**
|
|
367
|
-
* Decide which backups to delete under the given retention policy.
|
|
368
|
-
*
|
|
369
|
-
* Rules, applied in order:
|
|
370
|
-
* 1. Objects whose key isn't a recognised rebase backup (no parseable
|
|
371
|
-
* timestamp and no `createdAt`) are never selected — foreign files in a
|
|
372
|
-
* shared bucket are left untouched.
|
|
373
|
-
* 2. The `keepMinimum` newest backups are always retained.
|
|
374
|
-
* 3. Of the remainder, any older than `retentionDays` are selected for
|
|
375
|
-
* deletion.
|
|
376
|
-
*
|
|
377
|
-
* Returns the keys to delete, oldest first.
|
|
378
|
-
*/
|
|
379
|
-
function selectBackupsToPrune(backups, options) {
|
|
380
|
-
const retentionDays = options.retentionDays ?? 0;
|
|
381
|
-
const keepMinimum = Math.max(0, options.keepMinimum ?? 0);
|
|
382
|
-
const now = options.now ?? /* @__PURE__ */ new Date();
|
|
383
|
-
if (retentionDays <= 0) return [];
|
|
384
|
-
const dated = backups.map((b) => {
|
|
385
|
-
const createdAt = b.createdAt ?? parseBackupTimestamp(b.key);
|
|
386
|
-
return createdAt ? {
|
|
387
|
-
key: b.key,
|
|
388
|
-
createdAt
|
|
389
|
-
} : null;
|
|
390
|
-
}).filter((b) => b !== null);
|
|
391
|
-
dated.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
|
|
392
|
-
const cutoff = now.getTime() - retentionDays * 24 * 60 * 60 * 1e3;
|
|
393
|
-
const toDelete = dated.slice(keepMinimum).filter((b) => b.createdAt.getTime() < cutoff);
|
|
394
|
-
toDelete.sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime());
|
|
395
|
-
return toDelete.map((b) => b.key);
|
|
396
|
-
}
|
|
397
|
-
//#endregion
|
|
398
83
|
//#region ../../node_modules/.pnpm/is-plain-obj@4.1.0/node_modules/is-plain-obj/index.js
|
|
399
84
|
function isPlainObject(value) {
|
|
400
85
|
if (typeof value !== "object" || value === null) return false;
|
|
@@ -6566,2065 +6251,149 @@ createExeca(mapNode);
|
|
|
6566
6251
|
createExeca(mapScriptAsync, {}, deepScriptOptions, setScriptSync);
|
|
6567
6252
|
var { sendMessage, getOneMessage, getEachMessage, getCancelSignal } = getIpcExport();
|
|
6568
6253
|
//#endregion
|
|
6569
|
-
//#region
|
|
6570
|
-
var
|
|
6571
|
-
|
|
6572
|
-
}));
|
|
6573
|
-
//#endregion
|
|
6574
|
-
//#region ../../node_modules/.pnpm/color-name@1.1.4/node_modules/color-name/index.js
|
|
6575
|
-
var require_color_name = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
6576
|
-
module.exports = {
|
|
6577
|
-
"aliceblue": [
|
|
6578
|
-
240,
|
|
6579
|
-
248,
|
|
6580
|
-
255
|
|
6581
|
-
],
|
|
6582
|
-
"antiquewhite": [
|
|
6583
|
-
250,
|
|
6584
|
-
235,
|
|
6585
|
-
215
|
|
6586
|
-
],
|
|
6587
|
-
"aqua": [
|
|
6588
|
-
0,
|
|
6589
|
-
255,
|
|
6590
|
-
255
|
|
6591
|
-
],
|
|
6592
|
-
"aquamarine": [
|
|
6593
|
-
127,
|
|
6594
|
-
255,
|
|
6595
|
-
212
|
|
6596
|
-
],
|
|
6597
|
-
"azure": [
|
|
6598
|
-
240,
|
|
6599
|
-
255,
|
|
6600
|
-
255
|
|
6601
|
-
],
|
|
6602
|
-
"beige": [
|
|
6603
|
-
245,
|
|
6604
|
-
245,
|
|
6605
|
-
220
|
|
6606
|
-
],
|
|
6607
|
-
"bisque": [
|
|
6608
|
-
255,
|
|
6609
|
-
228,
|
|
6610
|
-
196
|
|
6611
|
-
],
|
|
6612
|
-
"black": [
|
|
6613
|
-
0,
|
|
6614
|
-
0,
|
|
6615
|
-
0
|
|
6616
|
-
],
|
|
6617
|
-
"blanchedalmond": [
|
|
6618
|
-
255,
|
|
6619
|
-
235,
|
|
6620
|
-
205
|
|
6621
|
-
],
|
|
6622
|
-
"blue": [
|
|
6623
|
-
0,
|
|
6624
|
-
0,
|
|
6625
|
-
255
|
|
6626
|
-
],
|
|
6627
|
-
"blueviolet": [
|
|
6628
|
-
138,
|
|
6629
|
-
43,
|
|
6630
|
-
226
|
|
6631
|
-
],
|
|
6632
|
-
"brown": [
|
|
6633
|
-
165,
|
|
6634
|
-
42,
|
|
6635
|
-
42
|
|
6636
|
-
],
|
|
6637
|
-
"burlywood": [
|
|
6638
|
-
222,
|
|
6639
|
-
184,
|
|
6640
|
-
135
|
|
6641
|
-
],
|
|
6642
|
-
"cadetblue": [
|
|
6643
|
-
95,
|
|
6644
|
-
158,
|
|
6645
|
-
160
|
|
6646
|
-
],
|
|
6647
|
-
"chartreuse": [
|
|
6648
|
-
127,
|
|
6649
|
-
255,
|
|
6650
|
-
0
|
|
6651
|
-
],
|
|
6652
|
-
"chocolate": [
|
|
6653
|
-
210,
|
|
6654
|
-
105,
|
|
6655
|
-
30
|
|
6656
|
-
],
|
|
6657
|
-
"coral": [
|
|
6658
|
-
255,
|
|
6659
|
-
127,
|
|
6660
|
-
80
|
|
6661
|
-
],
|
|
6662
|
-
"cornflowerblue": [
|
|
6663
|
-
100,
|
|
6664
|
-
149,
|
|
6665
|
-
237
|
|
6666
|
-
],
|
|
6667
|
-
"cornsilk": [
|
|
6668
|
-
255,
|
|
6669
|
-
248,
|
|
6670
|
-
220
|
|
6671
|
-
],
|
|
6672
|
-
"crimson": [
|
|
6673
|
-
220,
|
|
6674
|
-
20,
|
|
6675
|
-
60
|
|
6676
|
-
],
|
|
6677
|
-
"cyan": [
|
|
6678
|
-
0,
|
|
6679
|
-
255,
|
|
6680
|
-
255
|
|
6681
|
-
],
|
|
6682
|
-
"darkblue": [
|
|
6683
|
-
0,
|
|
6684
|
-
0,
|
|
6685
|
-
139
|
|
6686
|
-
],
|
|
6687
|
-
"darkcyan": [
|
|
6688
|
-
0,
|
|
6689
|
-
139,
|
|
6690
|
-
139
|
|
6691
|
-
],
|
|
6692
|
-
"darkgoldenrod": [
|
|
6693
|
-
184,
|
|
6694
|
-
134,
|
|
6695
|
-
11
|
|
6696
|
-
],
|
|
6697
|
-
"darkgray": [
|
|
6698
|
-
169,
|
|
6699
|
-
169,
|
|
6700
|
-
169
|
|
6701
|
-
],
|
|
6702
|
-
"darkgreen": [
|
|
6703
|
-
0,
|
|
6704
|
-
100,
|
|
6705
|
-
0
|
|
6706
|
-
],
|
|
6707
|
-
"darkgrey": [
|
|
6708
|
-
169,
|
|
6709
|
-
169,
|
|
6710
|
-
169
|
|
6711
|
-
],
|
|
6712
|
-
"darkkhaki": [
|
|
6713
|
-
189,
|
|
6714
|
-
183,
|
|
6715
|
-
107
|
|
6716
|
-
],
|
|
6717
|
-
"darkmagenta": [
|
|
6718
|
-
139,
|
|
6719
|
-
0,
|
|
6720
|
-
139
|
|
6721
|
-
],
|
|
6722
|
-
"darkolivegreen": [
|
|
6723
|
-
85,
|
|
6724
|
-
107,
|
|
6725
|
-
47
|
|
6726
|
-
],
|
|
6727
|
-
"darkorange": [
|
|
6728
|
-
255,
|
|
6729
|
-
140,
|
|
6730
|
-
0
|
|
6731
|
-
],
|
|
6732
|
-
"darkorchid": [
|
|
6733
|
-
153,
|
|
6734
|
-
50,
|
|
6735
|
-
204
|
|
6736
|
-
],
|
|
6737
|
-
"darkred": [
|
|
6738
|
-
139,
|
|
6739
|
-
0,
|
|
6740
|
-
0
|
|
6741
|
-
],
|
|
6742
|
-
"darksalmon": [
|
|
6743
|
-
233,
|
|
6744
|
-
150,
|
|
6745
|
-
122
|
|
6746
|
-
],
|
|
6747
|
-
"darkseagreen": [
|
|
6748
|
-
143,
|
|
6749
|
-
188,
|
|
6750
|
-
143
|
|
6751
|
-
],
|
|
6752
|
-
"darkslateblue": [
|
|
6753
|
-
72,
|
|
6754
|
-
61,
|
|
6755
|
-
139
|
|
6756
|
-
],
|
|
6757
|
-
"darkslategray": [
|
|
6758
|
-
47,
|
|
6759
|
-
79,
|
|
6760
|
-
79
|
|
6761
|
-
],
|
|
6762
|
-
"darkslategrey": [
|
|
6763
|
-
47,
|
|
6764
|
-
79,
|
|
6765
|
-
79
|
|
6766
|
-
],
|
|
6767
|
-
"darkturquoise": [
|
|
6768
|
-
0,
|
|
6769
|
-
206,
|
|
6770
|
-
209
|
|
6771
|
-
],
|
|
6772
|
-
"darkviolet": [
|
|
6773
|
-
148,
|
|
6774
|
-
0,
|
|
6775
|
-
211
|
|
6776
|
-
],
|
|
6777
|
-
"deeppink": [
|
|
6778
|
-
255,
|
|
6779
|
-
20,
|
|
6780
|
-
147
|
|
6781
|
-
],
|
|
6782
|
-
"deepskyblue": [
|
|
6783
|
-
0,
|
|
6784
|
-
191,
|
|
6785
|
-
255
|
|
6786
|
-
],
|
|
6787
|
-
"dimgray": [
|
|
6788
|
-
105,
|
|
6789
|
-
105,
|
|
6790
|
-
105
|
|
6791
|
-
],
|
|
6792
|
-
"dimgrey": [
|
|
6793
|
-
105,
|
|
6794
|
-
105,
|
|
6795
|
-
105
|
|
6796
|
-
],
|
|
6797
|
-
"dodgerblue": [
|
|
6798
|
-
30,
|
|
6799
|
-
144,
|
|
6800
|
-
255
|
|
6801
|
-
],
|
|
6802
|
-
"firebrick": [
|
|
6803
|
-
178,
|
|
6804
|
-
34,
|
|
6805
|
-
34
|
|
6806
|
-
],
|
|
6807
|
-
"floralwhite": [
|
|
6808
|
-
255,
|
|
6809
|
-
250,
|
|
6810
|
-
240
|
|
6811
|
-
],
|
|
6812
|
-
"forestgreen": [
|
|
6813
|
-
34,
|
|
6814
|
-
139,
|
|
6815
|
-
34
|
|
6816
|
-
],
|
|
6817
|
-
"fuchsia": [
|
|
6818
|
-
255,
|
|
6819
|
-
0,
|
|
6820
|
-
255
|
|
6821
|
-
],
|
|
6822
|
-
"gainsboro": [
|
|
6823
|
-
220,
|
|
6824
|
-
220,
|
|
6825
|
-
220
|
|
6826
|
-
],
|
|
6827
|
-
"ghostwhite": [
|
|
6828
|
-
248,
|
|
6829
|
-
248,
|
|
6830
|
-
255
|
|
6831
|
-
],
|
|
6832
|
-
"gold": [
|
|
6833
|
-
255,
|
|
6834
|
-
215,
|
|
6835
|
-
0
|
|
6836
|
-
],
|
|
6837
|
-
"goldenrod": [
|
|
6838
|
-
218,
|
|
6839
|
-
165,
|
|
6840
|
-
32
|
|
6841
|
-
],
|
|
6842
|
-
"gray": [
|
|
6843
|
-
128,
|
|
6844
|
-
128,
|
|
6845
|
-
128
|
|
6846
|
-
],
|
|
6847
|
-
"green": [
|
|
6848
|
-
0,
|
|
6849
|
-
128,
|
|
6850
|
-
0
|
|
6851
|
-
],
|
|
6852
|
-
"greenyellow": [
|
|
6853
|
-
173,
|
|
6854
|
-
255,
|
|
6855
|
-
47
|
|
6856
|
-
],
|
|
6857
|
-
"grey": [
|
|
6858
|
-
128,
|
|
6859
|
-
128,
|
|
6860
|
-
128
|
|
6861
|
-
],
|
|
6862
|
-
"honeydew": [
|
|
6863
|
-
240,
|
|
6864
|
-
255,
|
|
6865
|
-
240
|
|
6866
|
-
],
|
|
6867
|
-
"hotpink": [
|
|
6868
|
-
255,
|
|
6869
|
-
105,
|
|
6870
|
-
180
|
|
6871
|
-
],
|
|
6872
|
-
"indianred": [
|
|
6873
|
-
205,
|
|
6874
|
-
92,
|
|
6875
|
-
92
|
|
6876
|
-
],
|
|
6877
|
-
"indigo": [
|
|
6878
|
-
75,
|
|
6879
|
-
0,
|
|
6880
|
-
130
|
|
6881
|
-
],
|
|
6882
|
-
"ivory": [
|
|
6883
|
-
255,
|
|
6884
|
-
255,
|
|
6885
|
-
240
|
|
6886
|
-
],
|
|
6887
|
-
"khaki": [
|
|
6888
|
-
240,
|
|
6889
|
-
230,
|
|
6890
|
-
140
|
|
6891
|
-
],
|
|
6892
|
-
"lavender": [
|
|
6893
|
-
230,
|
|
6894
|
-
230,
|
|
6895
|
-
250
|
|
6896
|
-
],
|
|
6897
|
-
"lavenderblush": [
|
|
6898
|
-
255,
|
|
6899
|
-
240,
|
|
6900
|
-
245
|
|
6901
|
-
],
|
|
6902
|
-
"lawngreen": [
|
|
6903
|
-
124,
|
|
6904
|
-
252,
|
|
6905
|
-
0
|
|
6906
|
-
],
|
|
6907
|
-
"lemonchiffon": [
|
|
6908
|
-
255,
|
|
6909
|
-
250,
|
|
6910
|
-
205
|
|
6911
|
-
],
|
|
6912
|
-
"lightblue": [
|
|
6913
|
-
173,
|
|
6914
|
-
216,
|
|
6915
|
-
230
|
|
6916
|
-
],
|
|
6917
|
-
"lightcoral": [
|
|
6918
|
-
240,
|
|
6919
|
-
128,
|
|
6920
|
-
128
|
|
6921
|
-
],
|
|
6922
|
-
"lightcyan": [
|
|
6923
|
-
224,
|
|
6924
|
-
255,
|
|
6925
|
-
255
|
|
6926
|
-
],
|
|
6927
|
-
"lightgoldenrodyellow": [
|
|
6928
|
-
250,
|
|
6929
|
-
250,
|
|
6930
|
-
210
|
|
6931
|
-
],
|
|
6932
|
-
"lightgray": [
|
|
6933
|
-
211,
|
|
6934
|
-
211,
|
|
6935
|
-
211
|
|
6936
|
-
],
|
|
6937
|
-
"lightgreen": [
|
|
6938
|
-
144,
|
|
6939
|
-
238,
|
|
6940
|
-
144
|
|
6941
|
-
],
|
|
6942
|
-
"lightgrey": [
|
|
6943
|
-
211,
|
|
6944
|
-
211,
|
|
6945
|
-
211
|
|
6946
|
-
],
|
|
6947
|
-
"lightpink": [
|
|
6948
|
-
255,
|
|
6949
|
-
182,
|
|
6950
|
-
193
|
|
6951
|
-
],
|
|
6952
|
-
"lightsalmon": [
|
|
6953
|
-
255,
|
|
6954
|
-
160,
|
|
6955
|
-
122
|
|
6956
|
-
],
|
|
6957
|
-
"lightseagreen": [
|
|
6958
|
-
32,
|
|
6959
|
-
178,
|
|
6960
|
-
170
|
|
6961
|
-
],
|
|
6962
|
-
"lightskyblue": [
|
|
6963
|
-
135,
|
|
6964
|
-
206,
|
|
6965
|
-
250
|
|
6966
|
-
],
|
|
6967
|
-
"lightslategray": [
|
|
6968
|
-
119,
|
|
6969
|
-
136,
|
|
6970
|
-
153
|
|
6971
|
-
],
|
|
6972
|
-
"lightslategrey": [
|
|
6973
|
-
119,
|
|
6974
|
-
136,
|
|
6975
|
-
153
|
|
6976
|
-
],
|
|
6977
|
-
"lightsteelblue": [
|
|
6978
|
-
176,
|
|
6979
|
-
196,
|
|
6980
|
-
222
|
|
6981
|
-
],
|
|
6982
|
-
"lightyellow": [
|
|
6983
|
-
255,
|
|
6984
|
-
255,
|
|
6985
|
-
224
|
|
6986
|
-
],
|
|
6987
|
-
"lime": [
|
|
6988
|
-
0,
|
|
6989
|
-
255,
|
|
6990
|
-
0
|
|
6991
|
-
],
|
|
6992
|
-
"limegreen": [
|
|
6993
|
-
50,
|
|
6994
|
-
205,
|
|
6995
|
-
50
|
|
6996
|
-
],
|
|
6997
|
-
"linen": [
|
|
6998
|
-
250,
|
|
6999
|
-
240,
|
|
7000
|
-
230
|
|
7001
|
-
],
|
|
7002
|
-
"magenta": [
|
|
7003
|
-
255,
|
|
7004
|
-
0,
|
|
7005
|
-
255
|
|
7006
|
-
],
|
|
7007
|
-
"maroon": [
|
|
7008
|
-
128,
|
|
7009
|
-
0,
|
|
7010
|
-
0
|
|
7011
|
-
],
|
|
7012
|
-
"mediumaquamarine": [
|
|
7013
|
-
102,
|
|
7014
|
-
205,
|
|
7015
|
-
170
|
|
7016
|
-
],
|
|
7017
|
-
"mediumblue": [
|
|
7018
|
-
0,
|
|
7019
|
-
0,
|
|
7020
|
-
205
|
|
7021
|
-
],
|
|
7022
|
-
"mediumorchid": [
|
|
7023
|
-
186,
|
|
7024
|
-
85,
|
|
7025
|
-
211
|
|
7026
|
-
],
|
|
7027
|
-
"mediumpurple": [
|
|
7028
|
-
147,
|
|
7029
|
-
112,
|
|
7030
|
-
219
|
|
7031
|
-
],
|
|
7032
|
-
"mediumseagreen": [
|
|
7033
|
-
60,
|
|
7034
|
-
179,
|
|
7035
|
-
113
|
|
7036
|
-
],
|
|
7037
|
-
"mediumslateblue": [
|
|
7038
|
-
123,
|
|
7039
|
-
104,
|
|
7040
|
-
238
|
|
7041
|
-
],
|
|
7042
|
-
"mediumspringgreen": [
|
|
7043
|
-
0,
|
|
7044
|
-
250,
|
|
7045
|
-
154
|
|
7046
|
-
],
|
|
7047
|
-
"mediumturquoise": [
|
|
7048
|
-
72,
|
|
7049
|
-
209,
|
|
7050
|
-
204
|
|
7051
|
-
],
|
|
7052
|
-
"mediumvioletred": [
|
|
7053
|
-
199,
|
|
7054
|
-
21,
|
|
7055
|
-
133
|
|
7056
|
-
],
|
|
7057
|
-
"midnightblue": [
|
|
7058
|
-
25,
|
|
7059
|
-
25,
|
|
7060
|
-
112
|
|
7061
|
-
],
|
|
7062
|
-
"mintcream": [
|
|
7063
|
-
245,
|
|
7064
|
-
255,
|
|
7065
|
-
250
|
|
7066
|
-
],
|
|
7067
|
-
"mistyrose": [
|
|
7068
|
-
255,
|
|
7069
|
-
228,
|
|
7070
|
-
225
|
|
7071
|
-
],
|
|
7072
|
-
"moccasin": [
|
|
7073
|
-
255,
|
|
7074
|
-
228,
|
|
7075
|
-
181
|
|
7076
|
-
],
|
|
7077
|
-
"navajowhite": [
|
|
7078
|
-
255,
|
|
7079
|
-
222,
|
|
7080
|
-
173
|
|
7081
|
-
],
|
|
7082
|
-
"navy": [
|
|
7083
|
-
0,
|
|
7084
|
-
0,
|
|
7085
|
-
128
|
|
7086
|
-
],
|
|
7087
|
-
"oldlace": [
|
|
7088
|
-
253,
|
|
7089
|
-
245,
|
|
7090
|
-
230
|
|
7091
|
-
],
|
|
7092
|
-
"olive": [
|
|
7093
|
-
128,
|
|
7094
|
-
128,
|
|
7095
|
-
0
|
|
7096
|
-
],
|
|
7097
|
-
"olivedrab": [
|
|
7098
|
-
107,
|
|
7099
|
-
142,
|
|
7100
|
-
35
|
|
7101
|
-
],
|
|
7102
|
-
"orange": [
|
|
7103
|
-
255,
|
|
7104
|
-
165,
|
|
7105
|
-
0
|
|
7106
|
-
],
|
|
7107
|
-
"orangered": [
|
|
7108
|
-
255,
|
|
7109
|
-
69,
|
|
7110
|
-
0
|
|
7111
|
-
],
|
|
7112
|
-
"orchid": [
|
|
7113
|
-
218,
|
|
7114
|
-
112,
|
|
7115
|
-
214
|
|
7116
|
-
],
|
|
7117
|
-
"palegoldenrod": [
|
|
7118
|
-
238,
|
|
7119
|
-
232,
|
|
7120
|
-
170
|
|
7121
|
-
],
|
|
7122
|
-
"palegreen": [
|
|
7123
|
-
152,
|
|
7124
|
-
251,
|
|
7125
|
-
152
|
|
7126
|
-
],
|
|
7127
|
-
"paleturquoise": [
|
|
7128
|
-
175,
|
|
7129
|
-
238,
|
|
7130
|
-
238
|
|
7131
|
-
],
|
|
7132
|
-
"palevioletred": [
|
|
7133
|
-
219,
|
|
7134
|
-
112,
|
|
7135
|
-
147
|
|
7136
|
-
],
|
|
7137
|
-
"papayawhip": [
|
|
7138
|
-
255,
|
|
7139
|
-
239,
|
|
7140
|
-
213
|
|
7141
|
-
],
|
|
7142
|
-
"peachpuff": [
|
|
7143
|
-
255,
|
|
7144
|
-
218,
|
|
7145
|
-
185
|
|
7146
|
-
],
|
|
7147
|
-
"peru": [
|
|
7148
|
-
205,
|
|
7149
|
-
133,
|
|
7150
|
-
63
|
|
7151
|
-
],
|
|
7152
|
-
"pink": [
|
|
7153
|
-
255,
|
|
7154
|
-
192,
|
|
7155
|
-
203
|
|
7156
|
-
],
|
|
7157
|
-
"plum": [
|
|
7158
|
-
221,
|
|
7159
|
-
160,
|
|
7160
|
-
221
|
|
7161
|
-
],
|
|
7162
|
-
"powderblue": [
|
|
7163
|
-
176,
|
|
7164
|
-
224,
|
|
7165
|
-
230
|
|
7166
|
-
],
|
|
7167
|
-
"purple": [
|
|
7168
|
-
128,
|
|
7169
|
-
0,
|
|
7170
|
-
128
|
|
7171
|
-
],
|
|
7172
|
-
"rebeccapurple": [
|
|
7173
|
-
102,
|
|
7174
|
-
51,
|
|
7175
|
-
153
|
|
7176
|
-
],
|
|
7177
|
-
"red": [
|
|
7178
|
-
255,
|
|
7179
|
-
0,
|
|
7180
|
-
0
|
|
7181
|
-
],
|
|
7182
|
-
"rosybrown": [
|
|
7183
|
-
188,
|
|
7184
|
-
143,
|
|
7185
|
-
143
|
|
7186
|
-
],
|
|
7187
|
-
"royalblue": [
|
|
7188
|
-
65,
|
|
7189
|
-
105,
|
|
7190
|
-
225
|
|
7191
|
-
],
|
|
7192
|
-
"saddlebrown": [
|
|
7193
|
-
139,
|
|
7194
|
-
69,
|
|
7195
|
-
19
|
|
7196
|
-
],
|
|
7197
|
-
"salmon": [
|
|
7198
|
-
250,
|
|
7199
|
-
128,
|
|
7200
|
-
114
|
|
7201
|
-
],
|
|
7202
|
-
"sandybrown": [
|
|
7203
|
-
244,
|
|
7204
|
-
164,
|
|
7205
|
-
96
|
|
7206
|
-
],
|
|
7207
|
-
"seagreen": [
|
|
7208
|
-
46,
|
|
7209
|
-
139,
|
|
7210
|
-
87
|
|
7211
|
-
],
|
|
7212
|
-
"seashell": [
|
|
7213
|
-
255,
|
|
7214
|
-
245,
|
|
7215
|
-
238
|
|
7216
|
-
],
|
|
7217
|
-
"sienna": [
|
|
7218
|
-
160,
|
|
7219
|
-
82,
|
|
7220
|
-
45
|
|
7221
|
-
],
|
|
7222
|
-
"silver": [
|
|
7223
|
-
192,
|
|
7224
|
-
192,
|
|
7225
|
-
192
|
|
7226
|
-
],
|
|
7227
|
-
"skyblue": [
|
|
7228
|
-
135,
|
|
7229
|
-
206,
|
|
7230
|
-
235
|
|
7231
|
-
],
|
|
7232
|
-
"slateblue": [
|
|
7233
|
-
106,
|
|
7234
|
-
90,
|
|
7235
|
-
205
|
|
7236
|
-
],
|
|
7237
|
-
"slategray": [
|
|
7238
|
-
112,
|
|
7239
|
-
128,
|
|
7240
|
-
144
|
|
7241
|
-
],
|
|
7242
|
-
"slategrey": [
|
|
7243
|
-
112,
|
|
7244
|
-
128,
|
|
7245
|
-
144
|
|
7246
|
-
],
|
|
7247
|
-
"snow": [
|
|
7248
|
-
255,
|
|
7249
|
-
250,
|
|
7250
|
-
250
|
|
7251
|
-
],
|
|
7252
|
-
"springgreen": [
|
|
7253
|
-
0,
|
|
7254
|
-
255,
|
|
7255
|
-
127
|
|
7256
|
-
],
|
|
7257
|
-
"steelblue": [
|
|
7258
|
-
70,
|
|
7259
|
-
130,
|
|
7260
|
-
180
|
|
7261
|
-
],
|
|
7262
|
-
"tan": [
|
|
7263
|
-
210,
|
|
7264
|
-
180,
|
|
7265
|
-
140
|
|
7266
|
-
],
|
|
7267
|
-
"teal": [
|
|
7268
|
-
0,
|
|
7269
|
-
128,
|
|
7270
|
-
128
|
|
7271
|
-
],
|
|
7272
|
-
"thistle": [
|
|
7273
|
-
216,
|
|
7274
|
-
191,
|
|
7275
|
-
216
|
|
7276
|
-
],
|
|
7277
|
-
"tomato": [
|
|
7278
|
-
255,
|
|
7279
|
-
99,
|
|
7280
|
-
71
|
|
7281
|
-
],
|
|
7282
|
-
"turquoise": [
|
|
7283
|
-
64,
|
|
7284
|
-
224,
|
|
7285
|
-
208
|
|
7286
|
-
],
|
|
7287
|
-
"violet": [
|
|
7288
|
-
238,
|
|
7289
|
-
130,
|
|
7290
|
-
238
|
|
7291
|
-
],
|
|
7292
|
-
"wheat": [
|
|
7293
|
-
245,
|
|
7294
|
-
222,
|
|
7295
|
-
179
|
|
7296
|
-
],
|
|
7297
|
-
"white": [
|
|
7298
|
-
255,
|
|
7299
|
-
255,
|
|
7300
|
-
255
|
|
7301
|
-
],
|
|
7302
|
-
"whitesmoke": [
|
|
7303
|
-
245,
|
|
7304
|
-
245,
|
|
7305
|
-
245
|
|
7306
|
-
],
|
|
7307
|
-
"yellow": [
|
|
7308
|
-
255,
|
|
7309
|
-
255,
|
|
7310
|
-
0
|
|
7311
|
-
],
|
|
7312
|
-
"yellowgreen": [
|
|
7313
|
-
154,
|
|
7314
|
-
205,
|
|
7315
|
-
50
|
|
7316
|
-
]
|
|
7317
|
-
};
|
|
7318
|
-
}));
|
|
6254
|
+
//#region src/module-dir.ts
|
|
6255
|
+
var import___vite_browser_external = /* @__PURE__ */ __toESM(require___vite_browser_external(), 1);
|
|
6256
|
+
var moduleDir = path.dirname(fileURLToPath$1(import.meta.url));
|
|
7319
6257
|
//#endregion
|
|
7320
|
-
//#region
|
|
7321
|
-
|
|
7322
|
-
|
|
7323
|
-
|
|
7324
|
-
|
|
7325
|
-
|
|
7326
|
-
|
|
7327
|
-
|
|
7328
|
-
|
|
7329
|
-
|
|
7330
|
-
|
|
7331
|
-
|
|
7332
|
-
|
|
7333
|
-
|
|
7334
|
-
|
|
7335
|
-
|
|
7336
|
-
|
|
7337
|
-
|
|
7338
|
-
|
|
7339
|
-
|
|
7340
|
-
|
|
7341
|
-
|
|
7342
|
-
|
|
7343
|
-
|
|
7344
|
-
|
|
7345
|
-
|
|
7346
|
-
|
|
7347
|
-
|
|
7348
|
-
|
|
7349
|
-
|
|
7350
|
-
|
|
7351
|
-
|
|
7352
|
-
|
|
7353
|
-
|
|
7354
|
-
|
|
7355
|
-
|
|
7356
|
-
|
|
7357
|
-
|
|
7358
|
-
|
|
7359
|
-
|
|
7360
|
-
|
|
7361
|
-
|
|
7362
|
-
|
|
7363
|
-
|
|
7364
|
-
|
|
7365
|
-
|
|
7366
|
-
|
|
7367
|
-
|
|
7368
|
-
|
|
7369
|
-
|
|
7370
|
-
|
|
7371
|
-
channels: 1,
|
|
7372
|
-
labels: ["ansi256"]
|
|
7373
|
-
},
|
|
7374
|
-
hcg: {
|
|
7375
|
-
channels: 3,
|
|
7376
|
-
labels: [
|
|
7377
|
-
"h",
|
|
7378
|
-
"c",
|
|
7379
|
-
"g"
|
|
7380
|
-
]
|
|
7381
|
-
},
|
|
7382
|
-
apple: {
|
|
7383
|
-
channels: 3,
|
|
7384
|
-
labels: [
|
|
7385
|
-
"r16",
|
|
7386
|
-
"g16",
|
|
7387
|
-
"b16"
|
|
7388
|
-
]
|
|
7389
|
-
},
|
|
7390
|
-
gray: {
|
|
7391
|
-
channels: 1,
|
|
7392
|
-
labels: ["gray"]
|
|
7393
|
-
}
|
|
7394
|
-
};
|
|
7395
|
-
module.exports = convert;
|
|
7396
|
-
for (const model of Object.keys(convert)) {
|
|
7397
|
-
if (!("channels" in convert[model])) throw new Error("missing channels property: " + model);
|
|
7398
|
-
if (!("labels" in convert[model])) throw new Error("missing channel labels property: " + model);
|
|
7399
|
-
if (convert[model].labels.length !== convert[model].channels) throw new Error("channel and label counts mismatch: " + model);
|
|
7400
|
-
const { channels, labels } = convert[model];
|
|
7401
|
-
delete convert[model].channels;
|
|
7402
|
-
delete convert[model].labels;
|
|
7403
|
-
Object.defineProperty(convert[model], "channels", { value: channels });
|
|
7404
|
-
Object.defineProperty(convert[model], "labels", { value: labels });
|
|
7405
|
-
}
|
|
7406
|
-
convert.rgb.hsl = function(rgb) {
|
|
7407
|
-
const r = rgb[0] / 255;
|
|
7408
|
-
const g = rgb[1] / 255;
|
|
7409
|
-
const b = rgb[2] / 255;
|
|
7410
|
-
const min = Math.min(r, g, b);
|
|
7411
|
-
const max = Math.max(r, g, b);
|
|
7412
|
-
const delta = max - min;
|
|
7413
|
-
let h;
|
|
7414
|
-
let s;
|
|
7415
|
-
if (max === min) h = 0;
|
|
7416
|
-
else if (r === max) h = (g - b) / delta;
|
|
7417
|
-
else if (g === max) h = 2 + (b - r) / delta;
|
|
7418
|
-
else if (b === max) h = 4 + (r - g) / delta;
|
|
7419
|
-
h = Math.min(h * 60, 360);
|
|
7420
|
-
if (h < 0) h += 360;
|
|
7421
|
-
const l = (min + max) / 2;
|
|
7422
|
-
if (max === min) s = 0;
|
|
7423
|
-
else if (l <= .5) s = delta / (max + min);
|
|
7424
|
-
else s = delta / (2 - max - min);
|
|
7425
|
-
return [
|
|
7426
|
-
h,
|
|
7427
|
-
s * 100,
|
|
7428
|
-
l * 100
|
|
7429
|
-
];
|
|
7430
|
-
};
|
|
7431
|
-
convert.rgb.hsv = function(rgb) {
|
|
7432
|
-
let rdif;
|
|
7433
|
-
let gdif;
|
|
7434
|
-
let bdif;
|
|
7435
|
-
let h;
|
|
7436
|
-
let s;
|
|
7437
|
-
const r = rgb[0] / 255;
|
|
7438
|
-
const g = rgb[1] / 255;
|
|
7439
|
-
const b = rgb[2] / 255;
|
|
7440
|
-
const v = Math.max(r, g, b);
|
|
7441
|
-
const diff = v - Math.min(r, g, b);
|
|
7442
|
-
const diffc = function(c) {
|
|
7443
|
-
return (v - c) / 6 / diff + 1 / 2;
|
|
7444
|
-
};
|
|
7445
|
-
if (diff === 0) {
|
|
7446
|
-
h = 0;
|
|
7447
|
-
s = 0;
|
|
7448
|
-
} else {
|
|
7449
|
-
s = diff / v;
|
|
7450
|
-
rdif = diffc(r);
|
|
7451
|
-
gdif = diffc(g);
|
|
7452
|
-
bdif = diffc(b);
|
|
7453
|
-
if (r === v) h = bdif - gdif;
|
|
7454
|
-
else if (g === v) h = 1 / 3 + rdif - bdif;
|
|
7455
|
-
else if (b === v) h = 2 / 3 + gdif - rdif;
|
|
7456
|
-
if (h < 0) h += 1;
|
|
7457
|
-
else if (h > 1) h -= 1;
|
|
7458
|
-
}
|
|
7459
|
-
return [
|
|
7460
|
-
h * 360,
|
|
7461
|
-
s * 100,
|
|
7462
|
-
v * 100
|
|
7463
|
-
];
|
|
7464
|
-
};
|
|
7465
|
-
convert.rgb.hwb = function(rgb) {
|
|
7466
|
-
const r = rgb[0];
|
|
7467
|
-
const g = rgb[1];
|
|
7468
|
-
let b = rgb[2];
|
|
7469
|
-
const h = convert.rgb.hsl(rgb)[0];
|
|
7470
|
-
const w = 1 / 255 * Math.min(r, Math.min(g, b));
|
|
7471
|
-
b = 1 - 1 / 255 * Math.max(r, Math.max(g, b));
|
|
7472
|
-
return [
|
|
7473
|
-
h,
|
|
7474
|
-
w * 100,
|
|
7475
|
-
b * 100
|
|
7476
|
-
];
|
|
7477
|
-
};
|
|
7478
|
-
convert.rgb.cmyk = function(rgb) {
|
|
7479
|
-
const r = rgb[0] / 255;
|
|
7480
|
-
const g = rgb[1] / 255;
|
|
7481
|
-
const b = rgb[2] / 255;
|
|
7482
|
-
const k = Math.min(1 - r, 1 - g, 1 - b);
|
|
7483
|
-
const c = (1 - r - k) / (1 - k) || 0;
|
|
7484
|
-
const m = (1 - g - k) / (1 - k) || 0;
|
|
7485
|
-
const y = (1 - b - k) / (1 - k) || 0;
|
|
7486
|
-
return [
|
|
7487
|
-
c * 100,
|
|
7488
|
-
m * 100,
|
|
7489
|
-
y * 100,
|
|
7490
|
-
k * 100
|
|
7491
|
-
];
|
|
7492
|
-
};
|
|
7493
|
-
function comparativeDistance(x, y) {
|
|
7494
|
-
return (x[0] - y[0]) ** 2 + (x[1] - y[1]) ** 2 + (x[2] - y[2]) ** 2;
|
|
7495
|
-
}
|
|
7496
|
-
convert.rgb.keyword = function(rgb) {
|
|
7497
|
-
const reversed = reverseKeywords[rgb];
|
|
7498
|
-
if (reversed) return reversed;
|
|
7499
|
-
let currentClosestDistance = Infinity;
|
|
7500
|
-
let currentClosestKeyword;
|
|
7501
|
-
for (const keyword of Object.keys(cssKeywords)) {
|
|
7502
|
-
const value = cssKeywords[keyword];
|
|
7503
|
-
const distance = comparativeDistance(rgb, value);
|
|
7504
|
-
if (distance < currentClosestDistance) {
|
|
7505
|
-
currentClosestDistance = distance;
|
|
7506
|
-
currentClosestKeyword = keyword;
|
|
7507
|
-
}
|
|
7508
|
-
}
|
|
7509
|
-
return currentClosestKeyword;
|
|
7510
|
-
};
|
|
7511
|
-
convert.keyword.rgb = function(keyword) {
|
|
7512
|
-
return cssKeywords[keyword];
|
|
7513
|
-
};
|
|
7514
|
-
convert.rgb.xyz = function(rgb) {
|
|
7515
|
-
let r = rgb[0] / 255;
|
|
7516
|
-
let g = rgb[1] / 255;
|
|
7517
|
-
let b = rgb[2] / 255;
|
|
7518
|
-
r = r > .04045 ? ((r + .055) / 1.055) ** 2.4 : r / 12.92;
|
|
7519
|
-
g = g > .04045 ? ((g + .055) / 1.055) ** 2.4 : g / 12.92;
|
|
7520
|
-
b = b > .04045 ? ((b + .055) / 1.055) ** 2.4 : b / 12.92;
|
|
7521
|
-
const x = r * .4124 + g * .3576 + b * .1805;
|
|
7522
|
-
const y = r * .2126 + g * .7152 + b * .0722;
|
|
7523
|
-
const z = r * .0193 + g * .1192 + b * .9505;
|
|
7524
|
-
return [
|
|
7525
|
-
x * 100,
|
|
7526
|
-
y * 100,
|
|
7527
|
-
z * 100
|
|
7528
|
-
];
|
|
7529
|
-
};
|
|
7530
|
-
convert.rgb.lab = function(rgb) {
|
|
7531
|
-
const xyz = convert.rgb.xyz(rgb);
|
|
7532
|
-
let x = xyz[0];
|
|
7533
|
-
let y = xyz[1];
|
|
7534
|
-
let z = xyz[2];
|
|
7535
|
-
x /= 95.047;
|
|
7536
|
-
y /= 100;
|
|
7537
|
-
z /= 108.883;
|
|
7538
|
-
x = x > .008856 ? x ** (1 / 3) : 7.787 * x + 16 / 116;
|
|
7539
|
-
y = y > .008856 ? y ** (1 / 3) : 7.787 * y + 16 / 116;
|
|
7540
|
-
z = z > .008856 ? z ** (1 / 3) : 7.787 * z + 16 / 116;
|
|
7541
|
-
return [
|
|
7542
|
-
116 * y - 16,
|
|
7543
|
-
500 * (x - y),
|
|
7544
|
-
200 * (y - z)
|
|
7545
|
-
];
|
|
7546
|
-
};
|
|
7547
|
-
convert.hsl.rgb = function(hsl) {
|
|
7548
|
-
const h = hsl[0] / 360;
|
|
7549
|
-
const s = hsl[1] / 100;
|
|
7550
|
-
const l = hsl[2] / 100;
|
|
7551
|
-
let t2;
|
|
7552
|
-
let t3;
|
|
7553
|
-
let val;
|
|
7554
|
-
if (s === 0) {
|
|
7555
|
-
val = l * 255;
|
|
7556
|
-
return [
|
|
7557
|
-
val,
|
|
7558
|
-
val,
|
|
7559
|
-
val
|
|
7560
|
-
];
|
|
7561
|
-
}
|
|
7562
|
-
if (l < .5) t2 = l * (1 + s);
|
|
7563
|
-
else t2 = l + s - l * s;
|
|
7564
|
-
const t1 = 2 * l - t2;
|
|
7565
|
-
const rgb = [
|
|
7566
|
-
0,
|
|
7567
|
-
0,
|
|
7568
|
-
0
|
|
7569
|
-
];
|
|
7570
|
-
for (let i = 0; i < 3; i++) {
|
|
7571
|
-
t3 = h + 1 / 3 * -(i - 1);
|
|
7572
|
-
if (t3 < 0) t3++;
|
|
7573
|
-
if (t3 > 1) t3--;
|
|
7574
|
-
if (6 * t3 < 1) val = t1 + (t2 - t1) * 6 * t3;
|
|
7575
|
-
else if (2 * t3 < 1) val = t2;
|
|
7576
|
-
else if (3 * t3 < 2) val = t1 + (t2 - t1) * (2 / 3 - t3) * 6;
|
|
7577
|
-
else val = t1;
|
|
7578
|
-
rgb[i] = val * 255;
|
|
7579
|
-
}
|
|
7580
|
-
return rgb;
|
|
7581
|
-
};
|
|
7582
|
-
convert.hsl.hsv = function(hsl) {
|
|
7583
|
-
const h = hsl[0];
|
|
7584
|
-
let s = hsl[1] / 100;
|
|
7585
|
-
let l = hsl[2] / 100;
|
|
7586
|
-
let smin = s;
|
|
7587
|
-
const lmin = Math.max(l, .01);
|
|
7588
|
-
l *= 2;
|
|
7589
|
-
s *= l <= 1 ? l : 2 - l;
|
|
7590
|
-
smin *= lmin <= 1 ? lmin : 2 - lmin;
|
|
7591
|
-
const v = (l + s) / 2;
|
|
7592
|
-
return [
|
|
7593
|
-
h,
|
|
7594
|
-
(l === 0 ? 2 * smin / (lmin + smin) : 2 * s / (l + s)) * 100,
|
|
7595
|
-
v * 100
|
|
7596
|
-
];
|
|
7597
|
-
};
|
|
7598
|
-
convert.hsv.rgb = function(hsv) {
|
|
7599
|
-
const h = hsv[0] / 60;
|
|
7600
|
-
const s = hsv[1] / 100;
|
|
7601
|
-
let v = hsv[2] / 100;
|
|
7602
|
-
const hi = Math.floor(h) % 6;
|
|
7603
|
-
const f = h - Math.floor(h);
|
|
7604
|
-
const p = 255 * v * (1 - s);
|
|
7605
|
-
const q = 255 * v * (1 - s * f);
|
|
7606
|
-
const t = 255 * v * (1 - s * (1 - f));
|
|
7607
|
-
v *= 255;
|
|
7608
|
-
switch (hi) {
|
|
7609
|
-
case 0: return [
|
|
7610
|
-
v,
|
|
7611
|
-
t,
|
|
7612
|
-
p
|
|
7613
|
-
];
|
|
7614
|
-
case 1: return [
|
|
7615
|
-
q,
|
|
7616
|
-
v,
|
|
7617
|
-
p
|
|
7618
|
-
];
|
|
7619
|
-
case 2: return [
|
|
7620
|
-
p,
|
|
7621
|
-
v,
|
|
7622
|
-
t
|
|
7623
|
-
];
|
|
7624
|
-
case 3: return [
|
|
7625
|
-
p,
|
|
7626
|
-
q,
|
|
7627
|
-
v
|
|
7628
|
-
];
|
|
7629
|
-
case 4: return [
|
|
7630
|
-
t,
|
|
7631
|
-
p,
|
|
7632
|
-
v
|
|
7633
|
-
];
|
|
7634
|
-
case 5: return [
|
|
7635
|
-
v,
|
|
7636
|
-
p,
|
|
7637
|
-
q
|
|
7638
|
-
];
|
|
7639
|
-
}
|
|
7640
|
-
};
|
|
7641
|
-
convert.hsv.hsl = function(hsv) {
|
|
7642
|
-
const h = hsv[0];
|
|
7643
|
-
const s = hsv[1] / 100;
|
|
7644
|
-
const v = hsv[2] / 100;
|
|
7645
|
-
const vmin = Math.max(v, .01);
|
|
7646
|
-
let sl;
|
|
7647
|
-
let l;
|
|
7648
|
-
l = (2 - s) * v;
|
|
7649
|
-
const lmin = (2 - s) * vmin;
|
|
7650
|
-
sl = s * vmin;
|
|
7651
|
-
sl /= lmin <= 1 ? lmin : 2 - lmin;
|
|
7652
|
-
sl = sl || 0;
|
|
7653
|
-
l /= 2;
|
|
7654
|
-
return [
|
|
7655
|
-
h,
|
|
7656
|
-
sl * 100,
|
|
7657
|
-
l * 100
|
|
7658
|
-
];
|
|
7659
|
-
};
|
|
7660
|
-
convert.hwb.rgb = function(hwb) {
|
|
7661
|
-
const h = hwb[0] / 360;
|
|
7662
|
-
let wh = hwb[1] / 100;
|
|
7663
|
-
let bl = hwb[2] / 100;
|
|
7664
|
-
const ratio = wh + bl;
|
|
7665
|
-
let f;
|
|
7666
|
-
if (ratio > 1) {
|
|
7667
|
-
wh /= ratio;
|
|
7668
|
-
bl /= ratio;
|
|
7669
|
-
}
|
|
7670
|
-
const i = Math.floor(6 * h);
|
|
7671
|
-
const v = 1 - bl;
|
|
7672
|
-
f = 6 * h - i;
|
|
7673
|
-
if ((i & 1) !== 0) f = 1 - f;
|
|
7674
|
-
const n = wh + f * (v - wh);
|
|
7675
|
-
let r;
|
|
7676
|
-
let g;
|
|
7677
|
-
let b;
|
|
7678
|
-
switch (i) {
|
|
7679
|
-
default:
|
|
7680
|
-
case 6:
|
|
7681
|
-
case 0:
|
|
7682
|
-
r = v;
|
|
7683
|
-
g = n;
|
|
7684
|
-
b = wh;
|
|
7685
|
-
break;
|
|
7686
|
-
case 1:
|
|
7687
|
-
r = n;
|
|
7688
|
-
g = v;
|
|
7689
|
-
b = wh;
|
|
7690
|
-
break;
|
|
7691
|
-
case 2:
|
|
7692
|
-
r = wh;
|
|
7693
|
-
g = v;
|
|
7694
|
-
b = n;
|
|
7695
|
-
break;
|
|
7696
|
-
case 3:
|
|
7697
|
-
r = wh;
|
|
7698
|
-
g = n;
|
|
7699
|
-
b = v;
|
|
7700
|
-
break;
|
|
7701
|
-
case 4:
|
|
7702
|
-
r = n;
|
|
7703
|
-
g = wh;
|
|
7704
|
-
b = v;
|
|
7705
|
-
break;
|
|
7706
|
-
case 5:
|
|
7707
|
-
r = v;
|
|
7708
|
-
g = wh;
|
|
7709
|
-
b = n;
|
|
7710
|
-
break;
|
|
7711
|
-
}
|
|
7712
|
-
return [
|
|
7713
|
-
r * 255,
|
|
7714
|
-
g * 255,
|
|
7715
|
-
b * 255
|
|
7716
|
-
];
|
|
7717
|
-
};
|
|
7718
|
-
convert.cmyk.rgb = function(cmyk) {
|
|
7719
|
-
const c = cmyk[0] / 100;
|
|
7720
|
-
const m = cmyk[1] / 100;
|
|
7721
|
-
const y = cmyk[2] / 100;
|
|
7722
|
-
const k = cmyk[3] / 100;
|
|
7723
|
-
const r = 1 - Math.min(1, c * (1 - k) + k);
|
|
7724
|
-
const g = 1 - Math.min(1, m * (1 - k) + k);
|
|
7725
|
-
const b = 1 - Math.min(1, y * (1 - k) + k);
|
|
7726
|
-
return [
|
|
7727
|
-
r * 255,
|
|
7728
|
-
g * 255,
|
|
7729
|
-
b * 255
|
|
7730
|
-
];
|
|
7731
|
-
};
|
|
7732
|
-
convert.xyz.rgb = function(xyz) {
|
|
7733
|
-
const x = xyz[0] / 100;
|
|
7734
|
-
const y = xyz[1] / 100;
|
|
7735
|
-
const z = xyz[2] / 100;
|
|
7736
|
-
let r;
|
|
7737
|
-
let g;
|
|
7738
|
-
let b;
|
|
7739
|
-
r = x * 3.2406 + y * -1.5372 + z * -.4986;
|
|
7740
|
-
g = x * -.9689 + y * 1.8758 + z * .0415;
|
|
7741
|
-
b = x * .0557 + y * -.204 + z * 1.057;
|
|
7742
|
-
r = r > .0031308 ? 1.055 * r ** (1 / 2.4) - .055 : r * 12.92;
|
|
7743
|
-
g = g > .0031308 ? 1.055 * g ** (1 / 2.4) - .055 : g * 12.92;
|
|
7744
|
-
b = b > .0031308 ? 1.055 * b ** (1 / 2.4) - .055 : b * 12.92;
|
|
7745
|
-
r = Math.min(Math.max(0, r), 1);
|
|
7746
|
-
g = Math.min(Math.max(0, g), 1);
|
|
7747
|
-
b = Math.min(Math.max(0, b), 1);
|
|
7748
|
-
return [
|
|
7749
|
-
r * 255,
|
|
7750
|
-
g * 255,
|
|
7751
|
-
b * 255
|
|
7752
|
-
];
|
|
7753
|
-
};
|
|
7754
|
-
convert.xyz.lab = function(xyz) {
|
|
7755
|
-
let x = xyz[0];
|
|
7756
|
-
let y = xyz[1];
|
|
7757
|
-
let z = xyz[2];
|
|
7758
|
-
x /= 95.047;
|
|
7759
|
-
y /= 100;
|
|
7760
|
-
z /= 108.883;
|
|
7761
|
-
x = x > .008856 ? x ** (1 / 3) : 7.787 * x + 16 / 116;
|
|
7762
|
-
y = y > .008856 ? y ** (1 / 3) : 7.787 * y + 16 / 116;
|
|
7763
|
-
z = z > .008856 ? z ** (1 / 3) : 7.787 * z + 16 / 116;
|
|
7764
|
-
return [
|
|
7765
|
-
116 * y - 16,
|
|
7766
|
-
500 * (x - y),
|
|
7767
|
-
200 * (y - z)
|
|
7768
|
-
];
|
|
7769
|
-
};
|
|
7770
|
-
convert.lab.xyz = function(lab) {
|
|
7771
|
-
const l = lab[0];
|
|
7772
|
-
const a = lab[1];
|
|
7773
|
-
const b = lab[2];
|
|
7774
|
-
let x;
|
|
7775
|
-
let y;
|
|
7776
|
-
let z;
|
|
7777
|
-
y = (l + 16) / 116;
|
|
7778
|
-
x = a / 500 + y;
|
|
7779
|
-
z = y - b / 200;
|
|
7780
|
-
const y2 = y ** 3;
|
|
7781
|
-
const x2 = x ** 3;
|
|
7782
|
-
const z2 = z ** 3;
|
|
7783
|
-
y = y2 > .008856 ? y2 : (y - 16 / 116) / 7.787;
|
|
7784
|
-
x = x2 > .008856 ? x2 : (x - 16 / 116) / 7.787;
|
|
7785
|
-
z = z2 > .008856 ? z2 : (z - 16 / 116) / 7.787;
|
|
7786
|
-
x *= 95.047;
|
|
7787
|
-
y *= 100;
|
|
7788
|
-
z *= 108.883;
|
|
7789
|
-
return [
|
|
7790
|
-
x,
|
|
7791
|
-
y,
|
|
7792
|
-
z
|
|
7793
|
-
];
|
|
7794
|
-
};
|
|
7795
|
-
convert.lab.lch = function(lab) {
|
|
7796
|
-
const l = lab[0];
|
|
7797
|
-
const a = lab[1];
|
|
7798
|
-
const b = lab[2];
|
|
7799
|
-
let h;
|
|
7800
|
-
h = Math.atan2(b, a) * 360 / 2 / Math.PI;
|
|
7801
|
-
if (h < 0) h += 360;
|
|
7802
|
-
return [
|
|
7803
|
-
l,
|
|
7804
|
-
Math.sqrt(a * a + b * b),
|
|
7805
|
-
h
|
|
7806
|
-
];
|
|
7807
|
-
};
|
|
7808
|
-
convert.lch.lab = function(lch) {
|
|
7809
|
-
const l = lch[0];
|
|
7810
|
-
const c = lch[1];
|
|
7811
|
-
const hr = lch[2] / 360 * 2 * Math.PI;
|
|
7812
|
-
return [
|
|
7813
|
-
l,
|
|
7814
|
-
c * Math.cos(hr),
|
|
7815
|
-
c * Math.sin(hr)
|
|
7816
|
-
];
|
|
7817
|
-
};
|
|
7818
|
-
convert.rgb.ansi16 = function(args, saturation = null) {
|
|
7819
|
-
const [r, g, b] = args;
|
|
7820
|
-
let value = saturation === null ? convert.rgb.hsv(args)[2] : saturation;
|
|
7821
|
-
value = Math.round(value / 50);
|
|
7822
|
-
if (value === 0) return 30;
|
|
7823
|
-
let ansi = 30 + (Math.round(b / 255) << 2 | Math.round(g / 255) << 1 | Math.round(r / 255));
|
|
7824
|
-
if (value === 2) ansi += 60;
|
|
7825
|
-
return ansi;
|
|
7826
|
-
};
|
|
7827
|
-
convert.hsv.ansi16 = function(args) {
|
|
7828
|
-
return convert.rgb.ansi16(convert.hsv.rgb(args), args[2]);
|
|
7829
|
-
};
|
|
7830
|
-
convert.rgb.ansi256 = function(args) {
|
|
7831
|
-
const r = args[0];
|
|
7832
|
-
const g = args[1];
|
|
7833
|
-
const b = args[2];
|
|
7834
|
-
if (r === g && g === b) {
|
|
7835
|
-
if (r < 8) return 16;
|
|
7836
|
-
if (r > 248) return 231;
|
|
7837
|
-
return Math.round((r - 8) / 247 * 24) + 232;
|
|
7838
|
-
}
|
|
7839
|
-
return 16 + 36 * Math.round(r / 255 * 5) + 6 * Math.round(g / 255 * 5) + Math.round(b / 255 * 5);
|
|
7840
|
-
};
|
|
7841
|
-
convert.ansi16.rgb = function(args) {
|
|
7842
|
-
let color = args % 10;
|
|
7843
|
-
if (color === 0 || color === 7) {
|
|
7844
|
-
if (args > 50) color += 3.5;
|
|
7845
|
-
color = color / 10.5 * 255;
|
|
7846
|
-
return [
|
|
7847
|
-
color,
|
|
7848
|
-
color,
|
|
7849
|
-
color
|
|
7850
|
-
];
|
|
7851
|
-
}
|
|
7852
|
-
const mult = (~~(args > 50) + 1) * .5;
|
|
7853
|
-
return [
|
|
7854
|
-
(color & 1) * mult * 255,
|
|
7855
|
-
(color >> 1 & 1) * mult * 255,
|
|
7856
|
-
(color >> 2 & 1) * mult * 255
|
|
7857
|
-
];
|
|
7858
|
-
};
|
|
7859
|
-
convert.ansi256.rgb = function(args) {
|
|
7860
|
-
if (args >= 232) {
|
|
7861
|
-
const c = (args - 232) * 10 + 8;
|
|
7862
|
-
return [
|
|
7863
|
-
c,
|
|
7864
|
-
c,
|
|
7865
|
-
c
|
|
7866
|
-
];
|
|
7867
|
-
}
|
|
7868
|
-
args -= 16;
|
|
7869
|
-
let rem;
|
|
7870
|
-
return [
|
|
7871
|
-
Math.floor(args / 36) / 5 * 255,
|
|
7872
|
-
Math.floor((rem = args % 36) / 6) / 5 * 255,
|
|
7873
|
-
rem % 6 / 5 * 255
|
|
7874
|
-
];
|
|
7875
|
-
};
|
|
7876
|
-
convert.rgb.hex = function(args) {
|
|
7877
|
-
const string = (((Math.round(args[0]) & 255) << 16) + ((Math.round(args[1]) & 255) << 8) + (Math.round(args[2]) & 255)).toString(16).toUpperCase();
|
|
7878
|
-
return "000000".substring(string.length) + string;
|
|
7879
|
-
};
|
|
7880
|
-
convert.hex.rgb = function(args) {
|
|
7881
|
-
const match = args.toString(16).match(/[a-f0-9]{6}|[a-f0-9]{3}/i);
|
|
7882
|
-
if (!match) return [
|
|
7883
|
-
0,
|
|
7884
|
-
0,
|
|
7885
|
-
0
|
|
7886
|
-
];
|
|
7887
|
-
let colorString = match[0];
|
|
7888
|
-
if (match[0].length === 3) colorString = colorString.split("").map((char) => {
|
|
7889
|
-
return char + char;
|
|
7890
|
-
}).join("");
|
|
7891
|
-
const integer = parseInt(colorString, 16);
|
|
7892
|
-
return [
|
|
7893
|
-
integer >> 16 & 255,
|
|
7894
|
-
integer >> 8 & 255,
|
|
7895
|
-
integer & 255
|
|
7896
|
-
];
|
|
7897
|
-
};
|
|
7898
|
-
convert.rgb.hcg = function(rgb) {
|
|
7899
|
-
const r = rgb[0] / 255;
|
|
7900
|
-
const g = rgb[1] / 255;
|
|
7901
|
-
const b = rgb[2] / 255;
|
|
7902
|
-
const max = Math.max(Math.max(r, g), b);
|
|
7903
|
-
const min = Math.min(Math.min(r, g), b);
|
|
7904
|
-
const chroma = max - min;
|
|
7905
|
-
let grayscale;
|
|
7906
|
-
let hue;
|
|
7907
|
-
if (chroma < 1) grayscale = min / (1 - chroma);
|
|
7908
|
-
else grayscale = 0;
|
|
7909
|
-
if (chroma <= 0) hue = 0;
|
|
7910
|
-
else if (max === r) hue = (g - b) / chroma % 6;
|
|
7911
|
-
else if (max === g) hue = 2 + (b - r) / chroma;
|
|
7912
|
-
else hue = 4 + (r - g) / chroma;
|
|
7913
|
-
hue /= 6;
|
|
7914
|
-
hue %= 1;
|
|
7915
|
-
return [
|
|
7916
|
-
hue * 360,
|
|
7917
|
-
chroma * 100,
|
|
7918
|
-
grayscale * 100
|
|
7919
|
-
];
|
|
7920
|
-
};
|
|
7921
|
-
convert.hsl.hcg = function(hsl) {
|
|
7922
|
-
const s = hsl[1] / 100;
|
|
7923
|
-
const l = hsl[2] / 100;
|
|
7924
|
-
const c = l < .5 ? 2 * s * l : 2 * s * (1 - l);
|
|
7925
|
-
let f = 0;
|
|
7926
|
-
if (c < 1) f = (l - .5 * c) / (1 - c);
|
|
7927
|
-
return [
|
|
7928
|
-
hsl[0],
|
|
7929
|
-
c * 100,
|
|
7930
|
-
f * 100
|
|
7931
|
-
];
|
|
7932
|
-
};
|
|
7933
|
-
convert.hsv.hcg = function(hsv) {
|
|
7934
|
-
const s = hsv[1] / 100;
|
|
7935
|
-
const v = hsv[2] / 100;
|
|
7936
|
-
const c = s * v;
|
|
7937
|
-
let f = 0;
|
|
7938
|
-
if (c < 1) f = (v - c) / (1 - c);
|
|
7939
|
-
return [
|
|
7940
|
-
hsv[0],
|
|
7941
|
-
c * 100,
|
|
7942
|
-
f * 100
|
|
7943
|
-
];
|
|
7944
|
-
};
|
|
7945
|
-
convert.hcg.rgb = function(hcg) {
|
|
7946
|
-
const h = hcg[0] / 360;
|
|
7947
|
-
const c = hcg[1] / 100;
|
|
7948
|
-
const g = hcg[2] / 100;
|
|
7949
|
-
if (c === 0) return [
|
|
7950
|
-
g * 255,
|
|
7951
|
-
g * 255,
|
|
7952
|
-
g * 255
|
|
7953
|
-
];
|
|
7954
|
-
const pure = [
|
|
7955
|
-
0,
|
|
7956
|
-
0,
|
|
7957
|
-
0
|
|
7958
|
-
];
|
|
7959
|
-
const hi = h % 1 * 6;
|
|
7960
|
-
const v = hi % 1;
|
|
7961
|
-
const w = 1 - v;
|
|
7962
|
-
let mg = 0;
|
|
7963
|
-
switch (Math.floor(hi)) {
|
|
7964
|
-
case 0:
|
|
7965
|
-
pure[0] = 1;
|
|
7966
|
-
pure[1] = v;
|
|
7967
|
-
pure[2] = 0;
|
|
7968
|
-
break;
|
|
7969
|
-
case 1:
|
|
7970
|
-
pure[0] = w;
|
|
7971
|
-
pure[1] = 1;
|
|
7972
|
-
pure[2] = 0;
|
|
7973
|
-
break;
|
|
7974
|
-
case 2:
|
|
7975
|
-
pure[0] = 0;
|
|
7976
|
-
pure[1] = 1;
|
|
7977
|
-
pure[2] = v;
|
|
7978
|
-
break;
|
|
7979
|
-
case 3:
|
|
7980
|
-
pure[0] = 0;
|
|
7981
|
-
pure[1] = w;
|
|
7982
|
-
pure[2] = 1;
|
|
7983
|
-
break;
|
|
7984
|
-
case 4:
|
|
7985
|
-
pure[0] = v;
|
|
7986
|
-
pure[1] = 0;
|
|
7987
|
-
pure[2] = 1;
|
|
7988
|
-
break;
|
|
7989
|
-
default:
|
|
7990
|
-
pure[0] = 1;
|
|
7991
|
-
pure[1] = 0;
|
|
7992
|
-
pure[2] = w;
|
|
7993
|
-
}
|
|
7994
|
-
mg = (1 - c) * g;
|
|
7995
|
-
return [
|
|
7996
|
-
(c * pure[0] + mg) * 255,
|
|
7997
|
-
(c * pure[1] + mg) * 255,
|
|
7998
|
-
(c * pure[2] + mg) * 255
|
|
7999
|
-
];
|
|
8000
|
-
};
|
|
8001
|
-
convert.hcg.hsv = function(hcg) {
|
|
8002
|
-
const c = hcg[1] / 100;
|
|
8003
|
-
const v = c + hcg[2] / 100 * (1 - c);
|
|
8004
|
-
let f = 0;
|
|
8005
|
-
if (v > 0) f = c / v;
|
|
8006
|
-
return [
|
|
8007
|
-
hcg[0],
|
|
8008
|
-
f * 100,
|
|
8009
|
-
v * 100
|
|
8010
|
-
];
|
|
8011
|
-
};
|
|
8012
|
-
convert.hcg.hsl = function(hcg) {
|
|
8013
|
-
const c = hcg[1] / 100;
|
|
8014
|
-
const l = hcg[2] / 100 * (1 - c) + .5 * c;
|
|
8015
|
-
let s = 0;
|
|
8016
|
-
if (l > 0 && l < .5) s = c / (2 * l);
|
|
8017
|
-
else if (l >= .5 && l < 1) s = c / (2 * (1 - l));
|
|
8018
|
-
return [
|
|
8019
|
-
hcg[0],
|
|
8020
|
-
s * 100,
|
|
8021
|
-
l * 100
|
|
8022
|
-
];
|
|
8023
|
-
};
|
|
8024
|
-
convert.hcg.hwb = function(hcg) {
|
|
8025
|
-
const c = hcg[1] / 100;
|
|
8026
|
-
const v = c + hcg[2] / 100 * (1 - c);
|
|
8027
|
-
return [
|
|
8028
|
-
hcg[0],
|
|
8029
|
-
(v - c) * 100,
|
|
8030
|
-
(1 - v) * 100
|
|
8031
|
-
];
|
|
8032
|
-
};
|
|
8033
|
-
convert.hwb.hcg = function(hwb) {
|
|
8034
|
-
const w = hwb[1] / 100;
|
|
8035
|
-
const v = 1 - hwb[2] / 100;
|
|
8036
|
-
const c = v - w;
|
|
8037
|
-
let g = 0;
|
|
8038
|
-
if (c < 1) g = (v - c) / (1 - c);
|
|
8039
|
-
return [
|
|
8040
|
-
hwb[0],
|
|
8041
|
-
c * 100,
|
|
8042
|
-
g * 100
|
|
8043
|
-
];
|
|
8044
|
-
};
|
|
8045
|
-
convert.apple.rgb = function(apple) {
|
|
8046
|
-
return [
|
|
8047
|
-
apple[0] / 65535 * 255,
|
|
8048
|
-
apple[1] / 65535 * 255,
|
|
8049
|
-
apple[2] / 65535 * 255
|
|
8050
|
-
];
|
|
8051
|
-
};
|
|
8052
|
-
convert.rgb.apple = function(rgb) {
|
|
8053
|
-
return [
|
|
8054
|
-
rgb[0] / 255 * 65535,
|
|
8055
|
-
rgb[1] / 255 * 65535,
|
|
8056
|
-
rgb[2] / 255 * 65535
|
|
8057
|
-
];
|
|
8058
|
-
};
|
|
8059
|
-
convert.gray.rgb = function(args) {
|
|
8060
|
-
return [
|
|
8061
|
-
args[0] / 100 * 255,
|
|
8062
|
-
args[0] / 100 * 255,
|
|
8063
|
-
args[0] / 100 * 255
|
|
8064
|
-
];
|
|
8065
|
-
};
|
|
8066
|
-
convert.gray.hsl = function(args) {
|
|
8067
|
-
return [
|
|
8068
|
-
0,
|
|
8069
|
-
0,
|
|
8070
|
-
args[0]
|
|
6258
|
+
//#region src/cli-helpers.ts
|
|
6259
|
+
/**
|
|
6260
|
+
* Which package manager installed this project, read off its lockfile.
|
|
6261
|
+
*
|
|
6262
|
+
* Every remedy for a blocked build script is package-manager specific, and
|
|
6263
|
+
* naming the wrong one is the same failure as naming none: `pnpm approve-builds`
|
|
6264
|
+
* is not a thing an npm user can run, and `pnpm.onlyBuiltDependencies` is a key
|
|
6265
|
+
* npm does not read. This existed as a pnpm-only message for long enough that
|
|
6266
|
+
* npm 12 — which blocks dependency lifecycle scripts by default, the way pnpm 10
|
|
6267
|
+
* does — could ship and leave every npm reader three unfollowable commands deep.
|
|
6268
|
+
*
|
|
6269
|
+
* The lockfile, not `npm_config_user_agent`: the question is how the project's
|
|
6270
|
+
* `node_modules` was built, not which binary happens to be invoking us. Walks up
|
|
6271
|
+
* because commands run from `backend/` in a scaffolded project, and the lockfile
|
|
6272
|
+
* is at the workspace root.
|
|
6273
|
+
*/
|
|
6274
|
+
function detectProjectPackageManager(startDir = process.cwd()) {
|
|
6275
|
+
const lockfiles = [
|
|
6276
|
+
["pnpm-lock.yaml", "pnpm"],
|
|
6277
|
+
["bun.lock", "bun"],
|
|
6278
|
+
["bun.lockb", "bun"],
|
|
6279
|
+
["yarn.lock", "yarn"],
|
|
6280
|
+
["package-lock.json", "npm"]
|
|
6281
|
+
];
|
|
6282
|
+
let dir = path.resolve(startDir);
|
|
6283
|
+
while (true) {
|
|
6284
|
+
for (const [file, manager] of lockfiles) if (fs.existsSync(path.join(dir, file))) return manager;
|
|
6285
|
+
const parent = path.dirname(dir);
|
|
6286
|
+
if (parent === dir) return "unknown";
|
|
6287
|
+
dir = parent;
|
|
6288
|
+
}
|
|
6289
|
+
}
|
|
6290
|
+
/**
|
|
6291
|
+
* How to let `@ariga/atlas` run its `preinstall`, in the reader's own package
|
|
6292
|
+
* manager.
|
|
6293
|
+
*
|
|
6294
|
+
* Returns the lines to print after "the binary is missing but the package is
|
|
6295
|
+
* installed". Kept next to {@link detectProjectPackageManager} so the two move
|
|
6296
|
+
* together, and returned rather than printed so it can be asserted on.
|
|
6297
|
+
*/
|
|
6298
|
+
function describeBuildScriptRemedy(packageName, manager = detectProjectPackageManager()) {
|
|
6299
|
+
switch (manager) {
|
|
6300
|
+
case "npm": return [
|
|
6301
|
+
` npm install-scripts approve ${packageName}`,
|
|
6302
|
+
"",
|
|
6303
|
+
" or, to record it in the project (what `rebase init` scaffolds):",
|
|
6304
|
+
"",
|
|
6305
|
+
" // package.json",
|
|
6306
|
+
` "allowScripts": { "${packageName}": true }`,
|
|
6307
|
+
"",
|
|
6308
|
+
" Then re-run `npm install`."
|
|
8071
6309
|
];
|
|
8072
|
-
|
|
8073
|
-
|
|
8074
|
-
|
|
8075
|
-
|
|
8076
|
-
|
|
8077
|
-
100,
|
|
8078
|
-
gray[0]
|
|
6310
|
+
case "yarn": return [
|
|
6311
|
+
" // .yarnrc.yml",
|
|
6312
|
+
" enableScripts: true",
|
|
6313
|
+
"",
|
|
6314
|
+
" Then re-run `yarn install`."
|
|
8079
6315
|
];
|
|
8080
|
-
|
|
8081
|
-
|
|
8082
|
-
|
|
8083
|
-
|
|
8084
|
-
|
|
8085
|
-
0,
|
|
8086
|
-
gray[0]
|
|
6316
|
+
case "bun": return [
|
|
6317
|
+
" // package.json",
|
|
6318
|
+
` "trustedDependencies": ["${packageName}"]`,
|
|
6319
|
+
"",
|
|
6320
|
+
" Then re-run `bun install`."
|
|
8087
6321
|
];
|
|
8088
|
-
|
|
8089
|
-
|
|
8090
|
-
|
|
8091
|
-
|
|
8092
|
-
|
|
8093
|
-
|
|
6322
|
+
default: return [
|
|
6323
|
+
" pnpm approve-builds",
|
|
6324
|
+
"",
|
|
6325
|
+
" or, to record it in the project (what `rebase init` scaffolds):",
|
|
6326
|
+
"",
|
|
6327
|
+
" // package.json",
|
|
6328
|
+
` "pnpm": { "onlyBuiltDependencies": ["${packageName}"] }`,
|
|
6329
|
+
"",
|
|
6330
|
+
" Then re-run `pnpm install`."
|
|
8094
6331
|
];
|
|
8095
|
-
};
|
|
8096
|
-
convert.gray.hex = function(gray) {
|
|
8097
|
-
const val = Math.round(gray[0] / 100 * 255) & 255;
|
|
8098
|
-
const string = ((val << 16) + (val << 8) + val).toString(16).toUpperCase();
|
|
8099
|
-
return "000000".substring(string.length) + string;
|
|
8100
|
-
};
|
|
8101
|
-
convert.rgb.gray = function(rgb) {
|
|
8102
|
-
return [(rgb[0] + rgb[1] + rgb[2]) / 3 / 255 * 100];
|
|
8103
|
-
};
|
|
8104
|
-
}));
|
|
8105
|
-
//#endregion
|
|
8106
|
-
//#region ../../node_modules/.pnpm/color-convert@2.0.1/node_modules/color-convert/route.js
|
|
8107
|
-
var require_route = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
8108
|
-
var conversions = require_conversions();
|
|
8109
|
-
function buildGraph() {
|
|
8110
|
-
const graph = {};
|
|
8111
|
-
const models = Object.keys(conversions);
|
|
8112
|
-
for (let len = models.length, i = 0; i < len; i++) graph[models[i]] = {
|
|
8113
|
-
distance: -1,
|
|
8114
|
-
parent: null
|
|
8115
|
-
};
|
|
8116
|
-
return graph;
|
|
8117
|
-
}
|
|
8118
|
-
function deriveBFS(fromModel) {
|
|
8119
|
-
const graph = buildGraph();
|
|
8120
|
-
const queue = [fromModel];
|
|
8121
|
-
graph[fromModel].distance = 0;
|
|
8122
|
-
while (queue.length) {
|
|
8123
|
-
const current = queue.pop();
|
|
8124
|
-
const adjacents = Object.keys(conversions[current]);
|
|
8125
|
-
for (let len = adjacents.length, i = 0; i < len; i++) {
|
|
8126
|
-
const adjacent = adjacents[i];
|
|
8127
|
-
const node = graph[adjacent];
|
|
8128
|
-
if (node.distance === -1) {
|
|
8129
|
-
node.distance = graph[current].distance + 1;
|
|
8130
|
-
node.parent = current;
|
|
8131
|
-
queue.unshift(adjacent);
|
|
8132
|
-
}
|
|
8133
|
-
}
|
|
8134
|
-
}
|
|
8135
|
-
return graph;
|
|
8136
6332
|
}
|
|
8137
|
-
|
|
8138
|
-
|
|
8139
|
-
|
|
8140
|
-
|
|
6333
|
+
}
|
|
6334
|
+
/**
|
|
6335
|
+
* "Install again, from scratch", in the reader's own package manager.
|
|
6336
|
+
*
|
|
6337
|
+
* For the `bin-link-missing` state: the tree is half-written, and re-resolving
|
|
6338
|
+
* it is what recreates the `.bin` shim. `--force` on pnpm is what makes it
|
|
6339
|
+
* relink rather than decide the tree is already up to date.
|
|
6340
|
+
*/
|
|
6341
|
+
function describeReinstallCommand(manager = detectProjectPackageManager()) {
|
|
6342
|
+
switch (manager) {
|
|
6343
|
+
case "npm": return "rm -rf node_modules && npm install";
|
|
6344
|
+
case "yarn": return "yarn install --force";
|
|
6345
|
+
case "bun": return "rm -rf node_modules && bun install";
|
|
6346
|
+
default: return "pnpm install --force";
|
|
8141
6347
|
}
|
|
8142
|
-
|
|
8143
|
-
|
|
8144
|
-
|
|
8145
|
-
|
|
8146
|
-
|
|
8147
|
-
|
|
8148
|
-
|
|
8149
|
-
|
|
8150
|
-
}
|
|
8151
|
-
fn.conversion = path;
|
|
8152
|
-
return fn;
|
|
8153
|
-
}
|
|
8154
|
-
module.exports = function(fromModel) {
|
|
8155
|
-
const graph = deriveBFS(fromModel);
|
|
8156
|
-
const conversion = {};
|
|
8157
|
-
const models = Object.keys(graph);
|
|
8158
|
-
for (let len = models.length, i = 0; i < len; i++) {
|
|
8159
|
-
const toModel = models[i];
|
|
8160
|
-
if (graph[toModel].parent === null) continue;
|
|
8161
|
-
conversion[toModel] = wrapConversion(toModel, graph);
|
|
8162
|
-
}
|
|
8163
|
-
return conversion;
|
|
8164
|
-
};
|
|
8165
|
-
}));
|
|
8166
|
-
//#endregion
|
|
8167
|
-
//#region ../../node_modules/.pnpm/color-convert@2.0.1/node_modules/color-convert/index.js
|
|
8168
|
-
var require_color_convert = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
8169
|
-
var conversions = require_conversions();
|
|
8170
|
-
var route = require_route();
|
|
8171
|
-
var convert = {};
|
|
8172
|
-
var models = Object.keys(conversions);
|
|
8173
|
-
function wrapRaw(fn) {
|
|
8174
|
-
const wrappedFn = function(...args) {
|
|
8175
|
-
const arg0 = args[0];
|
|
8176
|
-
if (arg0 === void 0 || arg0 === null) return arg0;
|
|
8177
|
-
if (arg0.length > 1) args = arg0;
|
|
8178
|
-
return fn(args);
|
|
8179
|
-
};
|
|
8180
|
-
if ("conversion" in fn) wrappedFn.conversion = fn.conversion;
|
|
8181
|
-
return wrappedFn;
|
|
8182
|
-
}
|
|
8183
|
-
function wrapRounded(fn) {
|
|
8184
|
-
const wrappedFn = function(...args) {
|
|
8185
|
-
const arg0 = args[0];
|
|
8186
|
-
if (arg0 === void 0 || arg0 === null) return arg0;
|
|
8187
|
-
if (arg0.length > 1) args = arg0;
|
|
8188
|
-
const result = fn(args);
|
|
8189
|
-
if (typeof result === "object") for (let len = result.length, i = 0; i < len; i++) result[i] = Math.round(result[i]);
|
|
8190
|
-
return result;
|
|
8191
|
-
};
|
|
8192
|
-
if ("conversion" in fn) wrappedFn.conversion = fn.conversion;
|
|
8193
|
-
return wrappedFn;
|
|
8194
|
-
}
|
|
8195
|
-
models.forEach((fromModel) => {
|
|
8196
|
-
convert[fromModel] = {};
|
|
8197
|
-
Object.defineProperty(convert[fromModel], "channels", { value: conversions[fromModel].channels });
|
|
8198
|
-
Object.defineProperty(convert[fromModel], "labels", { value: conversions[fromModel].labels });
|
|
8199
|
-
const routes = route(fromModel);
|
|
8200
|
-
Object.keys(routes).forEach((toModel) => {
|
|
8201
|
-
const fn = routes[toModel];
|
|
8202
|
-
convert[fromModel][toModel] = wrapRounded(fn);
|
|
8203
|
-
convert[fromModel][toModel].raw = wrapRaw(fn);
|
|
8204
|
-
});
|
|
8205
|
-
});
|
|
8206
|
-
module.exports = convert;
|
|
8207
|
-
}));
|
|
8208
|
-
//#endregion
|
|
8209
|
-
//#region ../../node_modules/.pnpm/ansi-styles@4.3.0/node_modules/ansi-styles/index.js
|
|
8210
|
-
var require_ansi_styles = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
8211
|
-
var wrapAnsi16 = (fn, offset) => (...args) => {
|
|
8212
|
-
return `\u001B[${fn(...args) + offset}m`;
|
|
8213
|
-
};
|
|
8214
|
-
var wrapAnsi256 = (fn, offset) => (...args) => {
|
|
8215
|
-
const code = fn(...args);
|
|
8216
|
-
return `\u001B[${38 + offset};5;${code}m`;
|
|
8217
|
-
};
|
|
8218
|
-
var wrapAnsi16m = (fn, offset) => (...args) => {
|
|
8219
|
-
const rgb = fn(...args);
|
|
8220
|
-
return `\u001B[${38 + offset};2;${rgb[0]};${rgb[1]};${rgb[2]}m`;
|
|
8221
|
-
};
|
|
8222
|
-
var ansi2ansi = (n) => n;
|
|
8223
|
-
var rgb2rgb = (r, g, b) => [
|
|
8224
|
-
r,
|
|
8225
|
-
g,
|
|
8226
|
-
b
|
|
8227
|
-
];
|
|
8228
|
-
var setLazyProperty = (object, property, get) => {
|
|
8229
|
-
Object.defineProperty(object, property, {
|
|
8230
|
-
get: () => {
|
|
8231
|
-
const value = get();
|
|
8232
|
-
Object.defineProperty(object, property, {
|
|
8233
|
-
value,
|
|
8234
|
-
enumerable: true,
|
|
8235
|
-
configurable: true
|
|
8236
|
-
});
|
|
8237
|
-
return value;
|
|
8238
|
-
},
|
|
8239
|
-
enumerable: true,
|
|
8240
|
-
configurable: true
|
|
8241
|
-
});
|
|
8242
|
-
};
|
|
8243
|
-
/** @type {typeof import('color-convert')} */
|
|
8244
|
-
var colorConvert;
|
|
8245
|
-
var makeDynamicStyles = (wrap, targetSpace, identity, isBackground) => {
|
|
8246
|
-
if (colorConvert === void 0) colorConvert = require_color_convert();
|
|
8247
|
-
const offset = isBackground ? 10 : 0;
|
|
8248
|
-
const styles = {};
|
|
8249
|
-
for (const [sourceSpace, suite] of Object.entries(colorConvert)) {
|
|
8250
|
-
const name = sourceSpace === "ansi16" ? "ansi" : sourceSpace;
|
|
8251
|
-
if (sourceSpace === targetSpace) styles[name] = wrap(identity, offset);
|
|
8252
|
-
else if (typeof suite === "object") styles[name] = wrap(suite[targetSpace], offset);
|
|
8253
|
-
}
|
|
8254
|
-
return styles;
|
|
8255
|
-
};
|
|
8256
|
-
function assembleStyles() {
|
|
8257
|
-
const codes = /* @__PURE__ */ new Map();
|
|
8258
|
-
const styles = {
|
|
8259
|
-
modifier: {
|
|
8260
|
-
reset: [0, 0],
|
|
8261
|
-
bold: [1, 22],
|
|
8262
|
-
dim: [2, 22],
|
|
8263
|
-
italic: [3, 23],
|
|
8264
|
-
underline: [4, 24],
|
|
8265
|
-
inverse: [7, 27],
|
|
8266
|
-
hidden: [8, 28],
|
|
8267
|
-
strikethrough: [9, 29]
|
|
8268
|
-
},
|
|
8269
|
-
color: {
|
|
8270
|
-
black: [30, 39],
|
|
8271
|
-
red: [31, 39],
|
|
8272
|
-
green: [32, 39],
|
|
8273
|
-
yellow: [33, 39],
|
|
8274
|
-
blue: [34, 39],
|
|
8275
|
-
magenta: [35, 39],
|
|
8276
|
-
cyan: [36, 39],
|
|
8277
|
-
white: [37, 39],
|
|
8278
|
-
blackBright: [90, 39],
|
|
8279
|
-
redBright: [91, 39],
|
|
8280
|
-
greenBright: [92, 39],
|
|
8281
|
-
yellowBright: [93, 39],
|
|
8282
|
-
blueBright: [94, 39],
|
|
8283
|
-
magentaBright: [95, 39],
|
|
8284
|
-
cyanBright: [96, 39],
|
|
8285
|
-
whiteBright: [97, 39]
|
|
8286
|
-
},
|
|
8287
|
-
bgColor: {
|
|
8288
|
-
bgBlack: [40, 49],
|
|
8289
|
-
bgRed: [41, 49],
|
|
8290
|
-
bgGreen: [42, 49],
|
|
8291
|
-
bgYellow: [43, 49],
|
|
8292
|
-
bgBlue: [44, 49],
|
|
8293
|
-
bgMagenta: [45, 49],
|
|
8294
|
-
bgCyan: [46, 49],
|
|
8295
|
-
bgWhite: [47, 49],
|
|
8296
|
-
bgBlackBright: [100, 49],
|
|
8297
|
-
bgRedBright: [101, 49],
|
|
8298
|
-
bgGreenBright: [102, 49],
|
|
8299
|
-
bgYellowBright: [103, 49],
|
|
8300
|
-
bgBlueBright: [104, 49],
|
|
8301
|
-
bgMagentaBright: [105, 49],
|
|
8302
|
-
bgCyanBright: [106, 49],
|
|
8303
|
-
bgWhiteBright: [107, 49]
|
|
8304
|
-
}
|
|
8305
|
-
};
|
|
8306
|
-
styles.color.gray = styles.color.blackBright;
|
|
8307
|
-
styles.bgColor.bgGray = styles.bgColor.bgBlackBright;
|
|
8308
|
-
styles.color.grey = styles.color.blackBright;
|
|
8309
|
-
styles.bgColor.bgGrey = styles.bgColor.bgBlackBright;
|
|
8310
|
-
for (const [groupName, group] of Object.entries(styles)) {
|
|
8311
|
-
for (const [styleName, style] of Object.entries(group)) {
|
|
8312
|
-
styles[styleName] = {
|
|
8313
|
-
open: `\u001B[${style[0]}m`,
|
|
8314
|
-
close: `\u001B[${style[1]}m`
|
|
8315
|
-
};
|
|
8316
|
-
group[styleName] = styles[styleName];
|
|
8317
|
-
codes.set(style[0], style[1]);
|
|
8318
|
-
}
|
|
8319
|
-
Object.defineProperty(styles, groupName, {
|
|
8320
|
-
value: group,
|
|
8321
|
-
enumerable: false
|
|
8322
|
-
});
|
|
8323
|
-
}
|
|
8324
|
-
Object.defineProperty(styles, "codes", {
|
|
8325
|
-
value: codes,
|
|
8326
|
-
enumerable: false
|
|
8327
|
-
});
|
|
8328
|
-
styles.color.close = "\x1B[39m";
|
|
8329
|
-
styles.bgColor.close = "\x1B[49m";
|
|
8330
|
-
setLazyProperty(styles.color, "ansi", () => makeDynamicStyles(wrapAnsi16, "ansi16", ansi2ansi, false));
|
|
8331
|
-
setLazyProperty(styles.color, "ansi256", () => makeDynamicStyles(wrapAnsi256, "ansi256", ansi2ansi, false));
|
|
8332
|
-
setLazyProperty(styles.color, "ansi16m", () => makeDynamicStyles(wrapAnsi16m, "rgb", rgb2rgb, false));
|
|
8333
|
-
setLazyProperty(styles.bgColor, "ansi", () => makeDynamicStyles(wrapAnsi16, "ansi16", ansi2ansi, true));
|
|
8334
|
-
setLazyProperty(styles.bgColor, "ansi256", () => makeDynamicStyles(wrapAnsi256, "ansi256", ansi2ansi, true));
|
|
8335
|
-
setLazyProperty(styles.bgColor, "ansi16m", () => makeDynamicStyles(wrapAnsi16m, "rgb", rgb2rgb, true));
|
|
8336
|
-
return styles;
|
|
8337
|
-
}
|
|
8338
|
-
Object.defineProperty(module, "exports", {
|
|
8339
|
-
enumerable: true,
|
|
8340
|
-
get: assembleStyles
|
|
8341
|
-
});
|
|
8342
|
-
}));
|
|
8343
|
-
//#endregion
|
|
8344
|
-
//#region ../../node_modules/.pnpm/supports-color@7.2.0/node_modules/supports-color/browser.js
|
|
8345
|
-
var require_browser = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
8346
|
-
module.exports = {
|
|
8347
|
-
stdout: false,
|
|
8348
|
-
stderr: false
|
|
8349
|
-
};
|
|
8350
|
-
}));
|
|
8351
|
-
//#endregion
|
|
8352
|
-
//#region ../../node_modules/.pnpm/chalk@4.1.2/node_modules/chalk/source/util.js
|
|
8353
|
-
var require_util = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
8354
|
-
var stringReplaceAll = (string, substring, replacer) => {
|
|
8355
|
-
let index = string.indexOf(substring);
|
|
8356
|
-
if (index === -1) return string;
|
|
8357
|
-
const substringLength = substring.length;
|
|
8358
|
-
let endIndex = 0;
|
|
8359
|
-
let returnValue = "";
|
|
8360
|
-
do {
|
|
8361
|
-
returnValue += string.substr(endIndex, index - endIndex) + substring + replacer;
|
|
8362
|
-
endIndex = index + substringLength;
|
|
8363
|
-
index = string.indexOf(substring, endIndex);
|
|
8364
|
-
} while (index !== -1);
|
|
8365
|
-
returnValue += string.substr(endIndex);
|
|
8366
|
-
return returnValue;
|
|
8367
|
-
};
|
|
8368
|
-
var stringEncaseCRLFWithFirstIndex = (string, prefix, postfix, index) => {
|
|
8369
|
-
let endIndex = 0;
|
|
8370
|
-
let returnValue = "";
|
|
8371
|
-
do {
|
|
8372
|
-
const gotCR = string[index - 1] === "\r";
|
|
8373
|
-
returnValue += string.substr(endIndex, (gotCR ? index - 1 : index) - endIndex) + prefix + (gotCR ? "\r\n" : "\n") + postfix;
|
|
8374
|
-
endIndex = index + 1;
|
|
8375
|
-
index = string.indexOf("\n", endIndex);
|
|
8376
|
-
} while (index !== -1);
|
|
8377
|
-
returnValue += string.substr(endIndex);
|
|
8378
|
-
return returnValue;
|
|
8379
|
-
};
|
|
8380
|
-
module.exports = {
|
|
8381
|
-
stringReplaceAll,
|
|
8382
|
-
stringEncaseCRLFWithFirstIndex
|
|
8383
|
-
};
|
|
8384
|
-
}));
|
|
8385
|
-
//#endregion
|
|
8386
|
-
//#region ../../node_modules/.pnpm/chalk@4.1.2/node_modules/chalk/source/templates.js
|
|
8387
|
-
var require_templates = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
8388
|
-
var TEMPLATE_REGEX = /(?:\\(u(?:[a-f\d]{4}|\{[a-f\d]{1,6}\})|x[a-f\d]{2}|.))|(?:\{(~)?(\w+(?:\([^)]*\))?(?:\.\w+(?:\([^)]*\))?)*)(?:[ \t]|(?=\r?\n)))|(\})|((?:.|[\r\n\f])+?)/gi;
|
|
8389
|
-
var STYLE_REGEX = /(?:^|\.)(\w+)(?:\(([^)]*)\))?/g;
|
|
8390
|
-
var STRING_REGEX = /^(['"])((?:\\.|(?!\1)[^\\])*)\1$/;
|
|
8391
|
-
var ESCAPE_REGEX = /\\(u(?:[a-f\d]{4}|{[a-f\d]{1,6}})|x[a-f\d]{2}|.)|([^\\])/gi;
|
|
8392
|
-
var ESCAPES = /* @__PURE__ */ new Map([
|
|
8393
|
-
["n", "\n"],
|
|
8394
|
-
["r", "\r"],
|
|
8395
|
-
["t", " "],
|
|
8396
|
-
["b", "\b"],
|
|
8397
|
-
["f", "\f"],
|
|
8398
|
-
["v", "\v"],
|
|
8399
|
-
["0", "\0"],
|
|
8400
|
-
["\\", "\\"],
|
|
8401
|
-
["e", "\x1B"],
|
|
8402
|
-
["a", "\x07"]
|
|
8403
|
-
]);
|
|
8404
|
-
function unescape(c) {
|
|
8405
|
-
const u = c[0] === "u";
|
|
8406
|
-
const bracket = c[1] === "{";
|
|
8407
|
-
if (u && !bracket && c.length === 5 || c[0] === "x" && c.length === 3) return String.fromCharCode(parseInt(c.slice(1), 16));
|
|
8408
|
-
if (u && bracket) return String.fromCodePoint(parseInt(c.slice(2, -1), 16));
|
|
8409
|
-
return ESCAPES.get(c) || c;
|
|
8410
|
-
}
|
|
8411
|
-
function parseArguments(name, arguments_) {
|
|
8412
|
-
const results = [];
|
|
8413
|
-
const chunks = arguments_.trim().split(/\s*,\s*/g);
|
|
8414
|
-
let matches;
|
|
8415
|
-
for (const chunk of chunks) {
|
|
8416
|
-
const number = Number(chunk);
|
|
8417
|
-
if (!Number.isNaN(number)) results.push(number);
|
|
8418
|
-
else if (matches = chunk.match(STRING_REGEX)) results.push(matches[2].replace(ESCAPE_REGEX, (m, escape, character) => escape ? unescape(escape) : character));
|
|
8419
|
-
else throw new Error(`Invalid Chalk template style argument: ${chunk} (in style '${name}')`);
|
|
8420
|
-
}
|
|
8421
|
-
return results;
|
|
8422
|
-
}
|
|
8423
|
-
function parseStyle(style) {
|
|
8424
|
-
STYLE_REGEX.lastIndex = 0;
|
|
8425
|
-
const results = [];
|
|
8426
|
-
let matches;
|
|
8427
|
-
while ((matches = STYLE_REGEX.exec(style)) !== null) {
|
|
8428
|
-
const name = matches[1];
|
|
8429
|
-
if (matches[2]) {
|
|
8430
|
-
const args = parseArguments(name, matches[2]);
|
|
8431
|
-
results.push([name].concat(args));
|
|
8432
|
-
} else results.push([name]);
|
|
8433
|
-
}
|
|
8434
|
-
return results;
|
|
8435
|
-
}
|
|
8436
|
-
function buildStyle(chalk, styles) {
|
|
8437
|
-
const enabled = {};
|
|
8438
|
-
for (const layer of styles) for (const style of layer.styles) enabled[style[0]] = layer.inverse ? null : style.slice(1);
|
|
8439
|
-
let current = chalk;
|
|
8440
|
-
for (const [styleName, styles] of Object.entries(enabled)) {
|
|
8441
|
-
if (!Array.isArray(styles)) continue;
|
|
8442
|
-
if (!(styleName in current)) throw new Error(`Unknown Chalk style: ${styleName}`);
|
|
8443
|
-
current = styles.length > 0 ? current[styleName](...styles) : current[styleName];
|
|
8444
|
-
}
|
|
8445
|
-
return current;
|
|
8446
|
-
}
|
|
8447
|
-
module.exports = (chalk, temporary) => {
|
|
8448
|
-
const styles = [];
|
|
8449
|
-
const chunks = [];
|
|
8450
|
-
let chunk = [];
|
|
8451
|
-
temporary.replace(TEMPLATE_REGEX, (m, escapeCharacter, inverse, style, close, character) => {
|
|
8452
|
-
if (escapeCharacter) chunk.push(unescape(escapeCharacter));
|
|
8453
|
-
else if (style) {
|
|
8454
|
-
const string = chunk.join("");
|
|
8455
|
-
chunk = [];
|
|
8456
|
-
chunks.push(styles.length === 0 ? string : buildStyle(chalk, styles)(string));
|
|
8457
|
-
styles.push({
|
|
8458
|
-
inverse,
|
|
8459
|
-
styles: parseStyle(style)
|
|
8460
|
-
});
|
|
8461
|
-
} else if (close) {
|
|
8462
|
-
if (styles.length === 0) throw new Error("Found extraneous } in Chalk template literal");
|
|
8463
|
-
chunks.push(buildStyle(chalk, styles)(chunk.join("")));
|
|
8464
|
-
chunk = [];
|
|
8465
|
-
styles.pop();
|
|
8466
|
-
} else chunk.push(character);
|
|
8467
|
-
});
|
|
8468
|
-
chunks.push(chunk.join(""));
|
|
8469
|
-
if (styles.length > 0) {
|
|
8470
|
-
const errMessage = `Chalk template literal is missing ${styles.length} closing bracket${styles.length === 1 ? "" : "s"} (\`}\`)`;
|
|
8471
|
-
throw new Error(errMessage);
|
|
8472
|
-
}
|
|
8473
|
-
return chunks.join("");
|
|
8474
|
-
};
|
|
8475
|
-
}));
|
|
8476
|
-
//#endregion
|
|
8477
|
-
//#region ../../node_modules/.pnpm/chalk@4.1.2/node_modules/chalk/source/index.js
|
|
8478
|
-
var require_source = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
8479
|
-
var ansiStyles = require_ansi_styles();
|
|
8480
|
-
var { stdout: stdoutColor, stderr: stderrColor } = require_browser();
|
|
8481
|
-
var { stringReplaceAll, stringEncaseCRLFWithFirstIndex } = require_util();
|
|
8482
|
-
var { isArray } = Array;
|
|
8483
|
-
var levelMapping = [
|
|
8484
|
-
"ansi",
|
|
8485
|
-
"ansi",
|
|
8486
|
-
"ansi256",
|
|
8487
|
-
"ansi16m"
|
|
8488
|
-
];
|
|
8489
|
-
var styles = Object.create(null);
|
|
8490
|
-
var applyOptions = (object, options = {}) => {
|
|
8491
|
-
if (options.level && !(Number.isInteger(options.level) && options.level >= 0 && options.level <= 3)) throw new Error("The `level` option should be an integer from 0 to 3");
|
|
8492
|
-
const colorLevel = stdoutColor ? stdoutColor.level : 0;
|
|
8493
|
-
object.level = options.level === void 0 ? colorLevel : options.level;
|
|
8494
|
-
};
|
|
8495
|
-
var ChalkClass = class {
|
|
8496
|
-
constructor(options) {
|
|
8497
|
-
return chalkFactory(options);
|
|
8498
|
-
}
|
|
8499
|
-
};
|
|
8500
|
-
var chalkFactory = (options) => {
|
|
8501
|
-
const chalk = {};
|
|
8502
|
-
applyOptions(chalk, options);
|
|
8503
|
-
chalk.template = (...arguments_) => chalkTag(chalk.template, ...arguments_);
|
|
8504
|
-
Object.setPrototypeOf(chalk, Chalk.prototype);
|
|
8505
|
-
Object.setPrototypeOf(chalk.template, chalk);
|
|
8506
|
-
chalk.template.constructor = () => {
|
|
8507
|
-
throw new Error("`chalk.constructor()` is deprecated. Use `new chalk.Instance()` instead.");
|
|
8508
|
-
};
|
|
8509
|
-
chalk.template.Instance = ChalkClass;
|
|
8510
|
-
return chalk.template;
|
|
8511
|
-
};
|
|
8512
|
-
function Chalk(options) {
|
|
8513
|
-
return chalkFactory(options);
|
|
6348
|
+
}
|
|
6349
|
+
/** `add <pkg>` as a dev dependency, in the reader's own package manager. */
|
|
6350
|
+
function describeDevAddCommand(packageName, manager = detectProjectPackageManager()) {
|
|
6351
|
+
switch (manager) {
|
|
6352
|
+
case "npm": return `npm install -D ${packageName}`;
|
|
6353
|
+
case "yarn": return `yarn add -D ${packageName}`;
|
|
6354
|
+
case "bun": return `bun add -d ${packageName}`;
|
|
6355
|
+
default: return `pnpm add -D ${packageName}`;
|
|
8514
6356
|
}
|
|
8515
|
-
|
|
8516
|
-
|
|
8517
|
-
|
|
8518
|
-
|
|
8519
|
-
|
|
8520
|
-
|
|
8521
|
-
|
|
8522
|
-
|
|
8523
|
-
|
|
8524
|
-
|
|
8525
|
-
|
|
8526
|
-
|
|
8527
|
-
|
|
8528
|
-
|
|
8529
|
-
|
|
8530
|
-
|
|
8531
|
-
|
|
8532
|
-
|
|
8533
|
-
|
|
8534
|
-
|
|
8535
|
-
|
|
8536
|
-
|
|
8537
|
-
|
|
8538
|
-
|
|
8539
|
-
|
|
8540
|
-
|
|
8541
|
-
|
|
8542
|
-
|
|
8543
|
-
|
|
8544
|
-
|
|
8545
|
-
|
|
8546
|
-
|
|
8547
|
-
|
|
8548
|
-
|
|
8549
|
-
|
|
8550
|
-
|
|
8551
|
-
|
|
8552
|
-
|
|
8553
|
-
|
|
8554
|
-
|
|
8555
|
-
enumerable: true,
|
|
8556
|
-
get() {
|
|
8557
|
-
return this._generator.level;
|
|
8558
|
-
},
|
|
8559
|
-
set(level) {
|
|
8560
|
-
this._generator.level = level;
|
|
8561
|
-
}
|
|
8562
|
-
}
|
|
8563
|
-
});
|
|
8564
|
-
var createStyler = (open, close, parent) => {
|
|
8565
|
-
let openAll;
|
|
8566
|
-
let closeAll;
|
|
8567
|
-
if (parent === void 0) {
|
|
8568
|
-
openAll = open;
|
|
8569
|
-
closeAll = close;
|
|
8570
|
-
} else {
|
|
8571
|
-
openAll = parent.openAll + open;
|
|
8572
|
-
closeAll = close + parent.closeAll;
|
|
8573
|
-
}
|
|
8574
|
-
return {
|
|
8575
|
-
open,
|
|
8576
|
-
close,
|
|
8577
|
-
openAll,
|
|
8578
|
-
closeAll,
|
|
8579
|
-
parent
|
|
8580
|
-
};
|
|
8581
|
-
};
|
|
8582
|
-
var createBuilder = (self, _styler, _isEmpty) => {
|
|
8583
|
-
const builder = (...arguments_) => {
|
|
8584
|
-
if (isArray(arguments_[0]) && isArray(arguments_[0].raw)) return applyStyle(builder, chalkTag(builder, ...arguments_));
|
|
8585
|
-
return applyStyle(builder, arguments_.length === 1 ? "" + arguments_[0] : arguments_.join(" "));
|
|
8586
|
-
};
|
|
8587
|
-
Object.setPrototypeOf(builder, proto);
|
|
8588
|
-
builder._generator = self;
|
|
8589
|
-
builder._styler = _styler;
|
|
8590
|
-
builder._isEmpty = _isEmpty;
|
|
8591
|
-
return builder;
|
|
8592
|
-
};
|
|
8593
|
-
var applyStyle = (self, string) => {
|
|
8594
|
-
if (self.level <= 0 || !string) return self._isEmpty ? "" : string;
|
|
8595
|
-
let styler = self._styler;
|
|
8596
|
-
if (styler === void 0) return string;
|
|
8597
|
-
const { openAll, closeAll } = styler;
|
|
8598
|
-
if (string.indexOf("\x1B") !== -1) while (styler !== void 0) {
|
|
8599
|
-
string = stringReplaceAll(string, styler.close, styler.open);
|
|
8600
|
-
styler = styler.parent;
|
|
8601
|
-
}
|
|
8602
|
-
const lfIndex = string.indexOf("\n");
|
|
8603
|
-
if (lfIndex !== -1) string = stringEncaseCRLFWithFirstIndex(string, closeAll, openAll, lfIndex);
|
|
8604
|
-
return openAll + string + closeAll;
|
|
8605
|
-
};
|
|
8606
|
-
var template;
|
|
8607
|
-
var chalkTag = (chalk, ...strings) => {
|
|
8608
|
-
const [firstString] = strings;
|
|
8609
|
-
if (!isArray(firstString) || !isArray(firstString.raw)) return strings.join(" ");
|
|
8610
|
-
const arguments_ = strings.slice(1);
|
|
8611
|
-
const parts = [firstString.raw[0]];
|
|
8612
|
-
for (let i = 1; i < firstString.length; i++) parts.push(String(arguments_[i - 1]).replace(/[{}\\]/g, "\\$&"), String(firstString.raw[i]));
|
|
8613
|
-
if (template === void 0) template = require_templates();
|
|
8614
|
-
return template(chalk, parts.join(""));
|
|
8615
|
-
};
|
|
8616
|
-
Object.defineProperties(Chalk.prototype, styles);
|
|
8617
|
-
var chalk = Chalk();
|
|
8618
|
-
chalk.supportsColor = stdoutColor;
|
|
8619
|
-
chalk.stderr = Chalk({ level: stderrColor ? stderrColor.level : 0 });
|
|
8620
|
-
chalk.stderr.supportsColor = stderrColor;
|
|
8621
|
-
module.exports = chalk;
|
|
8622
|
-
}));
|
|
8623
|
-
require___vite_browser_external();
|
|
8624
|
-
require_source();
|
|
8625
|
-
var moduleDir = path.dirname(fileURLToPath$1(import.meta.url));
|
|
8626
|
-
//#endregion
|
|
8627
|
-
//#region src/cli-helpers.ts
|
|
6357
|
+
}
|
|
6358
|
+
/**
|
|
6359
|
+
* Where a package's own manifest lives, or null if it is not installed here.
|
|
6360
|
+
*
|
|
6361
|
+
* `package.json` rather than the package root: a package with an `exports` map
|
|
6362
|
+
* that omits `.` is unresolvable by name even when it is perfectly installed,
|
|
6363
|
+
* which would misreport it as absent.
|
|
6364
|
+
*/
|
|
6365
|
+
function resolvePackageManifest(packageName) {
|
|
6366
|
+
const bases = [pathToFileURL(path.join(process.cwd(), "package.json")), pathToFileURL(path.join(moduleDir, "package.json"))];
|
|
6367
|
+
for (const base of bases) try {
|
|
6368
|
+
return createRequire(base).resolve(`${packageName}/package.json`);
|
|
6369
|
+
} catch {}
|
|
6370
|
+
return null;
|
|
6371
|
+
}
|
|
6372
|
+
/**
|
|
6373
|
+
* The binaries a package's own manifest declares, as absolute paths.
|
|
6374
|
+
*
|
|
6375
|
+
* `bin` is a string for a single binary named after the package, or a map.
|
|
6376
|
+
* Both forms appear in the wild and `@ariga/atlas` uses the map.
|
|
6377
|
+
*/
|
|
6378
|
+
function declaredBinaries(manifestPath) {
|
|
6379
|
+
let manifest;
|
|
6380
|
+
try {
|
|
6381
|
+
manifest = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
|
|
6382
|
+
} catch {
|
|
6383
|
+
return [];
|
|
6384
|
+
}
|
|
6385
|
+
const dir = path.dirname(manifestPath);
|
|
6386
|
+
if (typeof manifest.bin === "string") return [path.resolve(dir, manifest.bin)];
|
|
6387
|
+
if (manifest.bin && typeof manifest.bin === "object") return Object.values(manifest.bin).filter((target) => typeof target === "string").map((target) => path.resolve(dir, target));
|
|
6388
|
+
return [];
|
|
6389
|
+
}
|
|
6390
|
+
function diagnoseMissingBin(packageName) {
|
|
6391
|
+
const manifestPath = resolvePackageManifest(packageName);
|
|
6392
|
+
if (!manifestPath) return "not-installed";
|
|
6393
|
+
const binaries = declaredBinaries(manifestPath);
|
|
6394
|
+
if (binaries.length === 0) return "build-script-blocked";
|
|
6395
|
+
return binaries.some((file) => fs.existsSync(file)) ? "bin-link-missing" : "build-script-blocked";
|
|
6396
|
+
}
|
|
8628
6397
|
function resolveLocalBin(binName) {
|
|
8629
6398
|
let dir = moduleDir;
|
|
8630
6399
|
while (true) {
|
|
@@ -8648,397 +6417,676 @@ function resolveLocalBin(binName) {
|
|
|
8648
6417
|
} catch {}
|
|
8649
6418
|
return null;
|
|
8650
6419
|
}
|
|
8651
|
-
|
|
8652
|
-
|
|
6420
|
+
async function getTableIncludesFromCollections(allCollections) {
|
|
6421
|
+
const { getTableName, resolveCollectionRelations, relationalCollections } = await import("@rebasepro/common");
|
|
6422
|
+
const { isPostgresCollectionConfig } = await import("@rebasepro/types");
|
|
6423
|
+
const collections = relationalCollections(allCollections);
|
|
6424
|
+
const includes = [];
|
|
6425
|
+
for (const col of collections) {
|
|
6426
|
+
const tableName = getTableName(col);
|
|
6427
|
+
const schema = isPostgresCollectionConfig(col) && col.schema ? col.schema : "public";
|
|
6428
|
+
if (tableName) includes.push(`${schema}.${tableName}`);
|
|
6429
|
+
const resolvedRelations = resolveCollectionRelations(col);
|
|
6430
|
+
for (const relation of Object.values(resolvedRelations)) if (isManyToMany(relation)) {
|
|
6431
|
+
const junctionTableName = relation.through.table;
|
|
6432
|
+
const targetCollection = relation.target();
|
|
6433
|
+
const targetSchema = isPostgresCollectionConfig(targetCollection) && targetCollection.schema ? targetCollection.schema : "public";
|
|
6434
|
+
includes.push(`${targetSchema}.${junctionTableName}`);
|
|
6435
|
+
}
|
|
6436
|
+
}
|
|
6437
|
+
return Array.from(new Set(includes));
|
|
6438
|
+
}
|
|
6439
|
+
async function getTableIncludes(collectionsPath) {
|
|
6440
|
+
return getTableIncludesFromCollections(await loadCollectionsForCli(collectionsPath));
|
|
6441
|
+
}
|
|
8653
6442
|
/**
|
|
8654
|
-
*
|
|
8655
|
-
* and `pg` value imports so it can be unit-tested under jest (the impure
|
|
8656
|
-
* edges — spawning processes, opening connections — live in
|
|
8657
|
-
* `backup-service.ts`, which is vitest/runtime only).
|
|
6443
|
+
* Load a project's collections the way the Atlas-facing commands need them.
|
|
8658
6444
|
*
|
|
8659
|
-
*
|
|
8660
|
-
*
|
|
6445
|
+
* Deliberately forgiving — a file that fails to import is skipped rather than
|
|
6446
|
+
* fatal — because the callers use this to *narrow* what Atlas may touch, and a
|
|
6447
|
+
* hard failure here would block a push over an unrelated broken file. Callers
|
|
6448
|
+
* that cannot tolerate a partial answer (the table excludes, which fail closed)
|
|
6449
|
+
* check the result themselves.
|
|
8661
6450
|
*/
|
|
6451
|
+
/** Paths this process has already complained about. See {@link loadCollectionsForCli}. */
|
|
6452
|
+
var warnedMissingCollectionsPaths = /* @__PURE__ */ new Set();
|
|
6453
|
+
async function loadCollectionsForCli(collectionsPath) {
|
|
6454
|
+
const resolvedPath = path.resolve(collectionsPath);
|
|
6455
|
+
const collections = [];
|
|
6456
|
+
if (!fs.existsSync(resolvedPath) && !warnedMissingCollectionsPaths.has(resolvedPath)) {
|
|
6457
|
+
warnedMissingCollectionsPaths.add(resolvedPath);
|
|
6458
|
+
outWarn(chalk.yellow(` ⚠ Collections path not found: "${collectionsPath}"\n Resolved to: ${resolvedPath}\n (relative to cwd: ${process.cwd()})\n Pass an absolute path, or a path relative to where you run the command.`));
|
|
6459
|
+
}
|
|
6460
|
+
if (fs.existsSync(resolvedPath)) {
|
|
6461
|
+
if (fs.statSync(resolvedPath).isDirectory()) {
|
|
6462
|
+
const files = fs.readdirSync(resolvedPath);
|
|
6463
|
+
for (const file of files) if ((file.endsWith(".ts") || file.endsWith(".js")) && !file.includes(".test.") && !file.endsWith(".d.ts") && file !== "index.ts" && file !== "index.js") try {
|
|
6464
|
+
const module = await import(pathToFileURL(path.join(resolvedPath, file)).href);
|
|
6465
|
+
if (module && module.default) collections.push(module.default);
|
|
6466
|
+
} catch {}
|
|
6467
|
+
}
|
|
6468
|
+
}
|
|
6469
|
+
return collections;
|
|
6470
|
+
}
|
|
6471
|
+
function getDevDatabaseUrl(databaseUrl) {
|
|
6472
|
+
try {
|
|
6473
|
+
const parsed = new URL(databaseUrl);
|
|
6474
|
+
parsed.pathname = `/${parsed.pathname.slice(1)}_dev_diff`;
|
|
6475
|
+
return parsed.toString();
|
|
6476
|
+
} catch {
|
|
6477
|
+
return databaseUrl + "_dev_diff";
|
|
6478
|
+
}
|
|
6479
|
+
}
|
|
8662
6480
|
/**
|
|
8663
|
-
*
|
|
8664
|
-
* tolerating per-statement failures. On a same-cluster restore the roles
|
|
8665
|
-
* usually already exist (`CREATE ROLE` → "already exists") and on a managed
|
|
8666
|
-
* provider an `ALTER ROLE <superuser>` may be refused; neither should abort
|
|
8667
|
-
* recreation of the roles that *are* missing. Returns how many statements
|
|
8668
|
-
* applied vs were skipped.
|
|
6481
|
+
* Create the scratch database Atlas plans against, and say so when we cannot.
|
|
8669
6482
|
*
|
|
8670
|
-
* `
|
|
6483
|
+
* Atlas needs a `--dev-url` — an empty database it can build the desired state
|
|
6484
|
+
* in — and this makes `<db>_dev_diff` next to the real one. The whole body used
|
|
6485
|
+
* to be wrapped in an empty catch commented "Ignore, let Atlas handle
|
|
6486
|
+
* connection failures", and Atlas's version of handling it is `postgres:
|
|
6487
|
+
* querying system variables: pq: database "app_dev_diff" does not exist
|
|
6488
|
+
* (3D000)` — the symptom, four frames downstream of the cause, with the cause
|
|
6489
|
+
* thrown away.
|
|
6490
|
+
*
|
|
6491
|
+
* The cause is almost always `42501`: `CREATE DATABASE` is a privilege managed
|
|
6492
|
+
* providers do not grant, so this is the first thing a hosted user hits and the
|
|
6493
|
+
* one they can do least with.
|
|
6494
|
+
*
|
|
6495
|
+
* @returns whether the scratch database was created by *this* call — the only
|
|
6496
|
+
* case in which dropping it afterwards is ours to do.
|
|
8671
6497
|
*/
|
|
8672
|
-
async function
|
|
8673
|
-
let
|
|
8674
|
-
|
|
8675
|
-
|
|
8676
|
-
|
|
8677
|
-
|
|
6498
|
+
async function ensureDevDatabaseExists(databaseUrl, devDatabaseUrl) {
|
|
6499
|
+
let devDbName = "";
|
|
6500
|
+
try {
|
|
6501
|
+
const { Client } = await import("pg");
|
|
6502
|
+
const parsed = new URL(databaseUrl);
|
|
6503
|
+
devDbName = new URL(devDatabaseUrl).pathname.slice(1);
|
|
6504
|
+
parsed.pathname = "/postgres";
|
|
6505
|
+
const client = new Client({ connectionString: parsed.toString() });
|
|
6506
|
+
await client.connect();
|
|
6507
|
+
try {
|
|
6508
|
+
if ((await client.query("SELECT 1 FROM pg_database WHERE datname = $1", [devDbName])).rowCount === 0) {
|
|
6509
|
+
await client.query(`CREATE DATABASE "${devDbName}"`);
|
|
6510
|
+
out(chalk.gray(` ✓ Created validation database "${devDbName}"`));
|
|
6511
|
+
return true;
|
|
6512
|
+
}
|
|
6513
|
+
} finally {
|
|
6514
|
+
await client.end();
|
|
6515
|
+
}
|
|
8678
6516
|
} catch (err) {
|
|
8679
|
-
|
|
8680
|
-
log(` • Skipped global: ${statement.split("\n")[0].slice(0, 80)} (${err instanceof Error ? err.message : String(err)})`);
|
|
6517
|
+
outWarn(describeDevDatabaseFailure(err, devDbName || "<db>_dev_diff"));
|
|
8681
6518
|
}
|
|
8682
|
-
return
|
|
8683
|
-
applied,
|
|
8684
|
-
skipped
|
|
8685
|
-
};
|
|
6519
|
+
return false;
|
|
8686
6520
|
}
|
|
8687
6521
|
/**
|
|
8688
|
-
*
|
|
8689
|
-
* pruning never orphans the roles file. The sidecar is best-effort — older
|
|
8690
|
-
* backups predate it, so a missing-sidecar failure is swallowed while a
|
|
8691
|
-
* failure deleting the dump itself propagates.
|
|
6522
|
+
* What went wrong creating the scratch database, and what to do about it.
|
|
8692
6523
|
*
|
|
8693
|
-
*
|
|
6524
|
+
* Exported for its test: the `42501` branch is the one that matters and it
|
|
6525
|
+
* cannot be provoked without a non-superuser role.
|
|
8694
6526
|
*/
|
|
8695
|
-
|
|
8696
|
-
|
|
8697
|
-
|
|
8698
|
-
|
|
8699
|
-
|
|
8700
|
-
|
|
8701
|
-
|
|
6527
|
+
function describeDevDatabaseFailure(err, devDbName) {
|
|
6528
|
+
const code = err?.code;
|
|
6529
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
6530
|
+
const lines = [chalk.yellow(` ⚠ Could not create the Atlas scratch database "${devDbName}": ${message}`)];
|
|
6531
|
+
if (code === "42501") lines.push(chalk.gray(` Atlas plans every change in a throwaway copy of the schema, and CREATE DATABASE
|
|
6532
|
+
is a privilege most managed providers withhold. Two ways forward:
|
|
6533
|
+
• GRANT the role CREATEDB: ALTER ROLE "<role>" CREATEDB;
|
|
6534
|
+
• or create it once by hand: CREATE DATABASE "${devDbName}";`));
|
|
6535
|
+
else lines.push(chalk.gray(" Atlas will fail next with `database \"" + devDbName + "\" does not exist (3D000)`, which is this."));
|
|
6536
|
+
return lines.join("\n");
|
|
8702
6537
|
}
|
|
8703
6538
|
/**
|
|
8704
|
-
*
|
|
8705
|
-
*
|
|
8706
|
-
* `pg_dump` creates its `--file=` target before it finishes connecting, so any
|
|
8707
|
-
* failure — a URL libpq rejects, a dropped connection, a full disk — leaves a
|
|
8708
|
-
* 0-byte file behind. That corpse is not inert: `rebase db backups list` used
|
|
8709
|
-
* to show it as an ordinary entry, and `selectBackupsToPrune` ranks by
|
|
8710
|
-
* timestamp alone, so it occupies a protected `keepMinimum` slot and can push
|
|
8711
|
-
* a real backup out of retention.
|
|
8712
|
-
*
|
|
8713
|
-
* Best-effort on purpose: the caller is already throwing, and a failed unlink
|
|
8714
|
-
* must not replace the real diagnosis with an ENOENT/EPERM from the cleanup.
|
|
6539
|
+
* Drop the scratch database again, after the command that needed it succeeded.
|
|
8715
6540
|
*
|
|
8716
|
-
*
|
|
8717
|
-
*
|
|
8718
|
-
*
|
|
6541
|
+
* It is a full copy of the schema — no rows, but every table, type and index —
|
|
6542
|
+
* and it was left behind per target forever. The only notice anyone got was
|
|
6543
|
+
* `rebase db branch prune` reporting "3 Atlas scratch database(s) left over
|
|
6544
|
+
* from db push", and on a provider that bills per database it is a silent cost.
|
|
8719
6545
|
*
|
|
8720
|
-
*
|
|
6546
|
+
* Deliberately NOT called on the failure path: after a failed push the scratch
|
|
6547
|
+
* database holds the state Atlas was planning against, which is the one thing
|
|
6548
|
+
* worth inspecting.
|
|
8721
6549
|
*/
|
|
8722
|
-
function
|
|
6550
|
+
async function dropDevDatabase(databaseUrl, devDatabaseUrl) {
|
|
8723
6551
|
try {
|
|
8724
|
-
|
|
8725
|
-
|
|
6552
|
+
const { Client } = await import("pg");
|
|
6553
|
+
const parsed = new URL(databaseUrl);
|
|
6554
|
+
const devDbName = new URL(devDatabaseUrl).pathname.slice(1);
|
|
6555
|
+
parsed.pathname = "/postgres";
|
|
6556
|
+
const client = new Client({ connectionString: parsed.toString() });
|
|
6557
|
+
await client.connect();
|
|
6558
|
+
try {
|
|
6559
|
+
await client.query(`DROP DATABASE IF EXISTS "${devDbName}"`);
|
|
6560
|
+
out(chalk.gray(` ✓ Removed validation database "${devDbName}"`));
|
|
6561
|
+
} finally {
|
|
6562
|
+
await client.end();
|
|
6563
|
+
}
|
|
6564
|
+
} catch (err) {
|
|
6565
|
+
outWarn(chalk.gray(` ⚠ Could not remove the Atlas scratch database: ${err instanceof Error ? err.message : String(err)}\n \`rebase db branch prune --include-dev-diff\` clears them.`));
|
|
6566
|
+
}
|
|
8726
6567
|
}
|
|
8727
|
-
//#endregion
|
|
8728
|
-
//#region src/backup/backup-service.ts
|
|
8729
6568
|
/**
|
|
8730
|
-
*
|
|
8731
|
-
* `pg_dump` and `pg_restore`, plus binary detection, version pre-flight,
|
|
8732
|
-
* and upload/prune against a storage backend.
|
|
6569
|
+
* The generated SQL for the project's `search` blocks, if it has any.
|
|
8733
6570
|
*
|
|
8734
|
-
*
|
|
8735
|
-
*
|
|
8736
|
-
* to Postgres and storage) and is intentionally kept small.
|
|
6571
|
+
* @param drizzleDir directory holding the generated SQL. Defaults to `drizzle`
|
|
6572
|
+
* under the working directory.
|
|
8737
6573
|
*/
|
|
8738
|
-
|
|
8739
|
-
|
|
8740
|
-
|
|
8741
|
-
|
|
8742
|
-
|
|
8743
|
-
discardPartialDump: () => discardPartialDump,
|
|
8744
|
-
ensureDatabaseExists: () => ensureDatabaseExists,
|
|
8745
|
-
getServerVersionMajor: () => getServerVersionMajor,
|
|
8746
|
-
listBackups: () => listBackups,
|
|
8747
|
-
preflight: () => preflight,
|
|
8748
|
-
pruneBackups: () => pruneBackups,
|
|
8749
|
-
resolvePgBinary: () => resolvePgBinary,
|
|
8750
|
-
restoreDump: () => restoreDump,
|
|
8751
|
-
uploadBackup: () => uploadBackup,
|
|
8752
|
-
validateDump: () => validateDump
|
|
8753
|
-
});
|
|
6574
|
+
function readSearchDdl(drizzleDir = path.resolve(process.cwd(), "drizzle")) {
|
|
6575
|
+
const searchFile = path.join(drizzleDir, "search.sql");
|
|
6576
|
+
if (!fs.existsSync(searchFile)) return "";
|
|
6577
|
+
return fs.readFileSync(searchFile, "utf-8").trim();
|
|
6578
|
+
}
|
|
8754
6579
|
/**
|
|
8755
|
-
*
|
|
8756
|
-
*
|
|
8757
|
-
*
|
|
6580
|
+
* Bring the search column, its index and their helpers up to date.
|
|
6581
|
+
*
|
|
6582
|
+
* Runs *after* Atlas, not before: the statements are `ALTER TABLE ... ADD
|
|
6583
|
+
* COLUMN`, so the table has to exist. Atlas is told to ignore these objects
|
|
6584
|
+
* entirely (`getSearchExcludes`) — it cannot manage them, and left to itself it
|
|
6585
|
+
* would plan a `DROP COLUMN` for every one, since they are absent from the
|
|
6586
|
+
* desired state it was given.
|
|
6587
|
+
*
|
|
6588
|
+
* A no-op when no collection declared `search`. A failure is *not* swallowed:
|
|
6589
|
+
* silently pushing a schema whose search column never appeared is how a
|
|
6590
|
+
* collection ends up with search configured, no error anywhere, and no results.
|
|
8758
6591
|
*/
|
|
8759
|
-
function
|
|
8760
|
-
|
|
8761
|
-
|
|
8762
|
-
var BackupToolError = class extends Error {
|
|
8763
|
-
hint;
|
|
8764
|
-
constructor(message, hint) {
|
|
8765
|
-
super(message);
|
|
8766
|
-
this.hint = hint;
|
|
8767
|
-
this.name = "BackupToolError";
|
|
8768
|
-
}
|
|
8769
|
-
};
|
|
8770
|
-
/** Locate `pg_dump` / `pg_restore` / `pg_dumpall`, honouring an env override. */
|
|
8771
|
-
function resolvePgBinary(tool, env = process.env) {
|
|
8772
|
-
const overrideVar = tool === "pg_dump" ? env.PG_DUMP_PATH : tool === "pg_restore" ? env.PG_RESTORE_PATH : env.PG_DUMPALL_PATH;
|
|
8773
|
-
if (overrideVar && fs.existsSync(overrideVar)) return overrideVar;
|
|
8774
|
-
return resolveLocalBin(tool);
|
|
8775
|
-
}
|
|
8776
|
-
/** Run `<bin> --version` and extract the major version. */
|
|
8777
|
-
async function detectToolMajor(bin) {
|
|
8778
|
-
try {
|
|
8779
|
-
const { stdout } = await execa(bin, ["--version"]);
|
|
8780
|
-
return parsePgToolMajor(stdout);
|
|
8781
|
-
} catch {
|
|
8782
|
-
return null;
|
|
8783
|
-
}
|
|
8784
|
-
}
|
|
8785
|
-
/** Query the server for its major version via `server_version_num`. */
|
|
8786
|
-
async function getServerVersionMajor(connectionString) {
|
|
6592
|
+
async function applySearchDdl(databaseUrl, drizzleDir = path.resolve(process.cwd(), "drizzle")) {
|
|
6593
|
+
const sql = readSearchDdl(drizzleDir);
|
|
6594
|
+
if (!sql) return;
|
|
8787
6595
|
const { Client } = await import("pg");
|
|
8788
|
-
const client = new Client({ connectionString });
|
|
6596
|
+
const client = new Client({ connectionString: databaseUrl });
|
|
8789
6597
|
await client.connect();
|
|
8790
6598
|
try {
|
|
8791
|
-
|
|
8792
|
-
return serverVersionNumToMajor(raw);
|
|
6599
|
+
await client.query(sql);
|
|
8793
6600
|
} finally {
|
|
8794
6601
|
await client.end();
|
|
8795
6602
|
}
|
|
6603
|
+
out(chalk.gray(" ✓ Applied full-text search columns and indexes"));
|
|
8796
6604
|
}
|
|
8797
6605
|
/**
|
|
8798
|
-
*
|
|
8799
|
-
*
|
|
8800
|
-
*
|
|
6606
|
+
* Glob patterns keeping Atlas away from the search objects.
|
|
6607
|
+
*
|
|
6608
|
+
* Returns an empty list — and so changes nothing — for a project with no
|
|
6609
|
+
* `search` block, which is every project that has not opted in.
|
|
8801
6610
|
*/
|
|
8802
|
-
async function
|
|
8803
|
-
const
|
|
8804
|
-
|
|
8805
|
-
const [toolMajor, serverMajor] = await Promise.all([detectToolMajor(bin), getServerVersionMajor(connectionString)]);
|
|
8806
|
-
return {
|
|
8807
|
-
bin,
|
|
8808
|
-
toolMajor,
|
|
8809
|
-
serverMajor,
|
|
8810
|
-
...checkToolServerCompatibility(toolMajor, serverMajor)
|
|
8811
|
-
};
|
|
6611
|
+
async function getSearchExcludes(collectionsPath) {
|
|
6612
|
+
const { searchExcludePatterns } = await import("./generate-postgres-ddl-logic-CtnpIBVv.js").then((n) => n.o);
|
|
6613
|
+
return searchExcludePatterns(await loadCollectionsForCli(collectionsPath));
|
|
8812
6614
|
}
|
|
8813
6615
|
/**
|
|
8814
|
-
*
|
|
8815
|
-
* file is written to the OS temp directory (used by the upload path, which
|
|
8816
|
-
* cleans it up afterwards).
|
|
6616
|
+
* The generated SQL for the project's `vector` properties, if it has any.
|
|
8817
6617
|
*
|
|
8818
|
-
*
|
|
8819
|
-
*
|
|
8820
|
-
* depend on can be recreated on restore. Set `includeGlobals: false` to skip
|
|
8821
|
-
* it (e.g. when the caller has no privilege to read cluster globals).
|
|
6618
|
+
* @param drizzleDir directory holding the generated SQL. Defaults to `drizzle`
|
|
6619
|
+
* under the working directory.
|
|
8822
6620
|
*/
|
|
8823
|
-
|
|
8824
|
-
const
|
|
8825
|
-
|
|
8826
|
-
|
|
8827
|
-
const fileName = opts.fileName ?? buildBackupFilename(opts.dbName);
|
|
8828
|
-
const outDir = opts.outDir ?? fs.mkdtempSync(path.join(os.tmpdir(), "rebase-backup-"));
|
|
8829
|
-
fs.mkdirSync(outDir, { recursive: true });
|
|
8830
|
-
const localFile = path.join(outDir, fileName);
|
|
8831
|
-
const args = buildPgDumpArgs({
|
|
8832
|
-
connectionString: opts.connectionString,
|
|
8833
|
-
outFile: localFile,
|
|
8834
|
-
excludeSchemas: opts.excludeSchemas,
|
|
8835
|
-
noOwner: opts.noOwner,
|
|
8836
|
-
rowSecurity: opts.rowSecurity
|
|
8837
|
-
});
|
|
8838
|
-
const dumpEnv = { ...env };
|
|
8839
|
-
if (opts.rowSecurity) dumpEnv.PGOPTIONS = [env.PGOPTIONS, buildRowSecurityPgOptions(opts.rowSecurity)].filter(Boolean).join(" ");
|
|
8840
|
-
try {
|
|
8841
|
-
await execa(bin, args, {
|
|
8842
|
-
stdio: opts.inheritStdio ? "inherit" : "pipe",
|
|
8843
|
-
env: dumpEnv
|
|
8844
|
-
});
|
|
8845
|
-
} catch (error) {
|
|
8846
|
-
discardPartialDump(localFile);
|
|
8847
|
-
const diagnosis = diagnoseRowSecurityDumpFailure(error);
|
|
8848
|
-
if (!diagnosis) throw error;
|
|
8849
|
-
throw new BackupToolError(diagnosis, "Run `rebase db backup --help` for the flag, and read what it says about partial dumps.");
|
|
8850
|
-
}
|
|
8851
|
-
const result = {
|
|
8852
|
-
localFile,
|
|
8853
|
-
fileName,
|
|
8854
|
-
sizeBytes: fs.existsSync(localFile) ? fs.statSync(localFile).size : 0
|
|
8855
|
-
};
|
|
8856
|
-
if (opts.includeGlobals !== false) {
|
|
8857
|
-
const dumpallBin = resolvePgBinary("pg_dumpall", env);
|
|
8858
|
-
if (!dumpallBin) throw new BackupToolError("Could not find the 'pg_dumpall' binary needed to capture cluster roles.", "Install the PostgreSQL client tools or set PG_DUMPALL_PATH. To take a role-incomplete backup anyway, pass includeGlobals: false.");
|
|
8859
|
-
const globalsFile = globalsFileForDump(localFile);
|
|
8860
|
-
try {
|
|
8861
|
-
await execa(dumpallBin, buildPgDumpallGlobalsArgs({
|
|
8862
|
-
connectionString: opts.connectionString,
|
|
8863
|
-
outFile: globalsFile
|
|
8864
|
-
}), {
|
|
8865
|
-
stdio: opts.inheritStdio ? "inherit" : "pipe",
|
|
8866
|
-
env: { ...env }
|
|
8867
|
-
});
|
|
8868
|
-
} catch (error) {
|
|
8869
|
-
discardPartialDump(globalsFile);
|
|
8870
|
-
discardPartialDump(localFile);
|
|
8871
|
-
throw error;
|
|
8872
|
-
}
|
|
8873
|
-
result.globalsFile = globalsFile;
|
|
8874
|
-
result.globalsSizeBytes = fs.existsSync(globalsFile) ? fs.statSync(globalsFile).size : 0;
|
|
8875
|
-
}
|
|
8876
|
-
return result;
|
|
6621
|
+
function readVectorDdl(drizzleDir = path.resolve(process.cwd(), "drizzle")) {
|
|
6622
|
+
const vectorFile = path.join(drizzleDir, "vector.sql");
|
|
6623
|
+
if (!fs.existsSync(vectorFile)) return "";
|
|
6624
|
+
return fs.readFileSync(vectorFile, "utf-8").trim();
|
|
8877
6625
|
}
|
|
8878
6626
|
/**
|
|
8879
|
-
*
|
|
8880
|
-
*
|
|
8881
|
-
*
|
|
8882
|
-
*
|
|
6627
|
+
* Install pgvector and bring the vector columns and their ANN indexes up to
|
|
6628
|
+
* date.
|
|
6629
|
+
*
|
|
6630
|
+
* Runs *after* Atlas, like {@link applySearchDdl} and for the same reason: the
|
|
6631
|
+
* statements are `ALTER TABLE … ADD COLUMN`, so the table has to exist. Atlas
|
|
6632
|
+
* is told to ignore these objects entirely ({@link getVectorExcludes}) — it
|
|
6633
|
+
* cannot manage a type its dev database is structurally incapable of having,
|
|
6634
|
+
* and left to itself it would plan a `DROP COLUMN` for every one.
|
|
6635
|
+
*
|
|
6636
|
+
* A no-op when no collection declares a `vector` property. A failure is *not*
|
|
6637
|
+
* swallowed: a push that reported success while the embedding column never
|
|
6638
|
+
* appeared is how a project ends up with vector search configured, no error
|
|
6639
|
+
* anywhere, and no results.
|
|
8883
6640
|
*/
|
|
8884
|
-
async function
|
|
8885
|
-
|
|
8886
|
-
|
|
8887
|
-
|
|
8888
|
-
};
|
|
8889
|
-
|
|
8890
|
-
|
|
8891
|
-
reason: "Dump file is empty (0 bytes)."
|
|
8892
|
-
};
|
|
8893
|
-
const bin = resolvePgBinary("pg_restore", env);
|
|
8894
|
-
if (!bin) return {
|
|
8895
|
-
ok: false,
|
|
8896
|
-
reason: "Could not find 'pg_restore' to verify the dump."
|
|
8897
|
-
};
|
|
6641
|
+
async function applyVectorDdl(databaseUrl, drizzleDir = path.resolve(process.cwd(), "drizzle")) {
|
|
6642
|
+
const sql = readVectorDdl(drizzleDir);
|
|
6643
|
+
if (!sql) return;
|
|
6644
|
+
const { vectorExtensionHint } = await import("./vector-index-DkWxm2de.js").then((n) => n.f);
|
|
6645
|
+
const { Client } = await import("pg");
|
|
6646
|
+
const client = new Client({ connectionString: databaseUrl });
|
|
6647
|
+
await client.connect();
|
|
8898
6648
|
try {
|
|
8899
|
-
await
|
|
8900
|
-
stdio: "pipe",
|
|
8901
|
-
env: { ...env }
|
|
8902
|
-
});
|
|
8903
|
-
return { ok: true };
|
|
6649
|
+
await client.query(sql);
|
|
8904
6650
|
} catch (err) {
|
|
8905
|
-
|
|
8906
|
-
|
|
8907
|
-
|
|
8908
|
-
};
|
|
6651
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
6652
|
+
const hint = vectorExtensionHint(message);
|
|
6653
|
+
if (!hint) throw err;
|
|
6654
|
+
throw new Error(`${message}${hint}`, { cause: err });
|
|
6655
|
+
} finally {
|
|
6656
|
+
await client.end();
|
|
8909
6657
|
}
|
|
6658
|
+
out(chalk.gray(" ✓ Applied vector columns and ANN indexes"));
|
|
6659
|
+
}
|
|
6660
|
+
/**
|
|
6661
|
+
* Glob patterns keeping Atlas away from the vector objects.
|
|
6662
|
+
*
|
|
6663
|
+
* Returns an empty list — and so changes nothing — for a project with no
|
|
6664
|
+
* `vector` property, which is every project that has not opted in.
|
|
6665
|
+
*/
|
|
6666
|
+
async function getVectorExcludes(collectionsPath) {
|
|
6667
|
+
const { vectorExcludePatterns } = await import("./generate-postgres-ddl-logic-CtnpIBVv.js").then((n) => n.o);
|
|
6668
|
+
return vectorExcludePatterns(await loadCollectionsForCli(collectionsPath));
|
|
8910
6669
|
}
|
|
8911
6670
|
/**
|
|
8912
|
-
*
|
|
8913
|
-
*
|
|
8914
|
-
*
|
|
8915
|
-
|
|
8916
|
-
|
|
8917
|
-
*
|
|
8918
|
-
*
|
|
8919
|
-
*
|
|
6671
|
+
* Query the live database for every user table/view outside the system
|
|
6672
|
+
* catalogs. Separated from {@link getTableExcludes} so its failure mode can
|
|
6673
|
+
* be handled explicitly (fail closed) and so tests can inject a stub.
|
|
6674
|
+
*/
|
|
6675
|
+
/**
|
|
6676
|
+
* Every index Postgres holds on a table Rebase manages, as
|
|
6677
|
+
* `schema.table.index`.
|
|
6678
|
+
*
|
|
6679
|
+
* Constraint-backed indexes are left out: a `PRIMARY KEY` or a `UNIQUE` is a
|
|
6680
|
+
* *constraint* to Atlas, diffed from the constraint declaration, and naming its
|
|
6681
|
+
* index in an exclude would shield the constraint itself.
|
|
8920
6682
|
*/
|
|
8921
|
-
async function
|
|
6683
|
+
async function queryExistingIndexes(databaseUrl) {
|
|
8922
6684
|
const { Client } = await import("pg");
|
|
8923
|
-
const client = new Client({ connectionString });
|
|
6685
|
+
const client = new Client({ connectionString: databaseUrl });
|
|
8924
6686
|
await client.connect();
|
|
8925
6687
|
try {
|
|
8926
|
-
return await
|
|
8927
|
-
|
|
8928
|
-
|
|
6688
|
+
return (await client.query(`
|
|
6689
|
+
SELECT n.nspname || '.' || t.relname || '.' || i.relname AS full_name
|
|
6690
|
+
FROM pg_index x
|
|
6691
|
+
JOIN pg_class i ON i.oid = x.indexrelid
|
|
6692
|
+
JOIN pg_class t ON t.oid = x.indrelid
|
|
6693
|
+
JOIN pg_namespace n ON n.oid = t.relnamespace
|
|
6694
|
+
WHERE n.nspname NOT IN ('pg_catalog', 'information_schema')
|
|
6695
|
+
AND t.relkind IN ('r', 'p')
|
|
6696
|
+
AND NOT EXISTS (
|
|
6697
|
+
SELECT 1 FROM pg_constraint c WHERE c.conindid = i.oid
|
|
6698
|
+
);
|
|
6699
|
+
`)).rows.map((row) => row.full_name);
|
|
8929
6700
|
} finally {
|
|
8930
6701
|
await client.end();
|
|
8931
6702
|
}
|
|
8932
6703
|
}
|
|
8933
6704
|
/**
|
|
8934
|
-
*
|
|
8935
|
-
*
|
|
8936
|
-
*
|
|
8937
|
-
*
|
|
6705
|
+
* Glob patterns keeping Atlas away from indexes that are not Rebase's.
|
|
6706
|
+
*
|
|
6707
|
+
* The problem this solves is live and predates the `indexes:` block. `db push`
|
|
6708
|
+
* is declarative, so an index on a managed table that is absent from
|
|
6709
|
+
* `schema.sql` is drift — and Atlas plans `DROP INDEX` for it. Verified
|
|
6710
|
+
* against atlas v1.2.3: create one by hand, re-run an unchanged push, and the
|
|
6711
|
+
* plan is a bare drop. `DROP INDEX` is not in `DESTRUCTIVE_PATTERNS`, so the
|
|
6712
|
+
* auto-approved apply took it without asking. Until now the only way to have
|
|
6713
|
+
* an index at all was to write it by hand, which made that the *only* outcome.
|
|
8938
6714
|
*
|
|
8939
|
-
*
|
|
8940
|
-
*
|
|
8941
|
-
*
|
|
8942
|
-
*
|
|
6715
|
+
* Ownership rather than a prompt, because once indexes are declarable a drop
|
|
6716
|
+
* is usually correct: removing one from the config should remove it from the
|
|
6717
|
+
* database, quietly. What must never be dropped is an index Rebase never
|
|
6718
|
+
* created — a hand-written one, or one an introspected database arrived with.
|
|
6719
|
+
* {@link isRebaseIndexName} is the test, the same arrangement
|
|
6720
|
+
* `isGeneratedPolicyName` uses to let policy reconciliation drop only what it
|
|
6721
|
+
* generated.
|
|
6722
|
+
*
|
|
6723
|
+
* The named-in-the-desired-state check comes first and is what makes removal
|
|
6724
|
+
* work: a declared index that the author has just deleted still matches the
|
|
6725
|
+
* name pattern, is no longer in the plan, and so is *not* excluded — Atlas
|
|
6726
|
+
* drops it, as intended.
|
|
6727
|
+
*
|
|
6728
|
+
* Three-part `schema.table.index`, never two: a two-part pattern reads as a
|
|
6729
|
+
* *table* named `<index>` in a *schema* named `<table>`, matches nothing, and
|
|
6730
|
+
* reports no error — the trap already recorded for the search excludes.
|
|
8943
6731
|
*/
|
|
8944
|
-
async function
|
|
8945
|
-
const
|
|
8946
|
-
const
|
|
8947
|
-
|
|
8948
|
-
|
|
8949
|
-
|
|
8950
|
-
|
|
8951
|
-
|
|
8952
|
-
|
|
8953
|
-
|
|
8954
|
-
|
|
8955
|
-
|
|
8956
|
-
|
|
6732
|
+
async function getForeignIndexExcludes(databaseUrl, collectionsPath, deps = {}) {
|
|
6733
|
+
const queryIndexes = deps.queryExistingIndexes ?? queryExistingIndexes;
|
|
6734
|
+
const managed = await (deps.getManagedIndexNames ?? managedIndexNames)(collectionsPath);
|
|
6735
|
+
let existing;
|
|
6736
|
+
try {
|
|
6737
|
+
existing = await queryIndexes(databaseUrl);
|
|
6738
|
+
} catch (err) {
|
|
6739
|
+
throw new ExcludeIntrospectionError(`Failed to introspect the database for unmanaged indexes: ${err instanceof Error ? err.message : String(err)}`, err);
|
|
6740
|
+
}
|
|
6741
|
+
return existing.filter((full) => {
|
|
6742
|
+
const indexName = full.slice(full.lastIndexOf(".") + 1);
|
|
6743
|
+
if (managed.has(indexName)) return false;
|
|
6744
|
+
return !isRebaseIndexName(indexName);
|
|
8957
6745
|
});
|
|
8958
6746
|
}
|
|
8959
6747
|
/**
|
|
8960
|
-
*
|
|
8961
|
-
*
|
|
6748
|
+
* The index names the current collections would create — search, vector and
|
|
6749
|
+
* declared alike. Anything Atlas would emit is by definition not foreign.
|
|
8962
6750
|
*/
|
|
8963
|
-
async function
|
|
6751
|
+
async function managedIndexNames(collectionsPath) {
|
|
6752
|
+
const collections = await loadCollectionsForCli(collectionsPath);
|
|
6753
|
+
const { resolveColumnName, searchExcludePatterns, vectorExcludePatterns } = await import("./generate-postgres-ddl-logic-CtnpIBVv.js").then((n) => n.o);
|
|
6754
|
+
const { buildCollectionIndexPlan } = await import("./collection-index-BHgT_TKd.js").then((n) => n.a);
|
|
6755
|
+
const names = new Set(buildCollectionIndexPlan(collections, resolveColumnName).map((spec) => spec.indexName));
|
|
6756
|
+
for (const pattern of [...searchExcludePatterns(collections), ...vectorExcludePatterns(collections)]) names.add(pattern.slice(pattern.lastIndexOf(".") + 1));
|
|
6757
|
+
return names;
|
|
6758
|
+
}
|
|
6759
|
+
async function queryExistingTables(databaseUrl) {
|
|
8964
6760
|
const { Client } = await import("pg");
|
|
8965
|
-
const
|
|
8966
|
-
parsed.pathname = "/postgres";
|
|
8967
|
-
const client = new Client({ connectionString: parsed.toString() });
|
|
6761
|
+
const client = new Client({ connectionString: databaseUrl });
|
|
8968
6762
|
await client.connect();
|
|
8969
6763
|
try {
|
|
8970
|
-
|
|
8971
|
-
|
|
8972
|
-
|
|
8973
|
-
|
|
8974
|
-
|
|
6764
|
+
return (await client.query(`
|
|
6765
|
+
SELECT table_schema || '.' || table_name AS full_name
|
|
6766
|
+
FROM information_schema.tables
|
|
6767
|
+
WHERE table_schema NOT IN ('pg_catalog', 'information_schema')
|
|
6768
|
+
AND table_type IN ('BASE TABLE', 'VIEW');
|
|
6769
|
+
`)).rows.map((row) => row.full_name);
|
|
8975
6770
|
} finally {
|
|
8976
6771
|
await client.end();
|
|
8977
6772
|
}
|
|
8978
6773
|
}
|
|
8979
6774
|
/**
|
|
8980
|
-
*
|
|
8981
|
-
*
|
|
8982
|
-
*
|
|
6775
|
+
* Raised when the exclude list could not be built. `db push` MUST abort on
|
|
6776
|
+
* this rather than continue: the exclude list is the only thing shielding
|
|
6777
|
+
* non-collection (user/system) tables from the auto-approved declarative
|
|
6778
|
+
* apply. A partial list — the old fail-open behaviour — meant a transient
|
|
6779
|
+
* introspection hiccup dropped every table not present in `schema.sql`.
|
|
8983
6780
|
*/
|
|
8984
|
-
|
|
8985
|
-
|
|
8986
|
-
|
|
8987
|
-
|
|
8988
|
-
|
|
8989
|
-
|
|
8990
|
-
|
|
8991
|
-
|
|
8992
|
-
file,
|
|
8993
|
-
key,
|
|
8994
|
-
bucket: dest.bucket,
|
|
8995
|
-
metadata: { "rebase-backup": "1" }
|
|
8996
|
-
})).storageUrl
|
|
8997
|
-
};
|
|
8998
|
-
}
|
|
6781
|
+
var ExcludeIntrospectionError = class extends Error {
|
|
6782
|
+
cause;
|
|
6783
|
+
constructor(message, cause) {
|
|
6784
|
+
super(message);
|
|
6785
|
+
this.cause = cause;
|
|
6786
|
+
this.name = "ExcludeIntrospectionError";
|
|
6787
|
+
}
|
|
6788
|
+
};
|
|
8999
6789
|
/**
|
|
9000
|
-
*
|
|
9001
|
-
*
|
|
6790
|
+
* Build the `--exclude` list that protects tables Rebase doesn't manage from
|
|
6791
|
+
* the declarative apply. Anything not backing a collection (or its M2M
|
|
6792
|
+
* junctions) is excluded so Atlas never drops it.
|
|
6793
|
+
*
|
|
6794
|
+
* Fails **closed**: if the database can't be introspected we cannot know
|
|
6795
|
+
* which tables to protect, so we throw {@link ExcludeIntrospectionError}
|
|
6796
|
+
* instead of returning a near-empty list and letting the caller drop
|
|
6797
|
+
* everything.
|
|
6798
|
+
*
|
|
6799
|
+
* `deps` is injectable for tests; production uses the real pg-backed queries.
|
|
9002
6800
|
*/
|
|
9003
|
-
async function
|
|
9004
|
-
|
|
9005
|
-
|
|
9006
|
-
|
|
9007
|
-
|
|
9008
|
-
|
|
9009
|
-
|
|
9010
|
-
|
|
9011
|
-
|
|
9012
|
-
|
|
9013
|
-
|
|
9014
|
-
|
|
9015
|
-
|
|
9016
|
-
|
|
9017
|
-
|
|
9018
|
-
|
|
9019
|
-
|
|
9020
|
-
|
|
9021
|
-
|
|
9022
|
-
key,
|
|
9023
|
-
createdAt: parseBackupTimestamp(key) ?? void 0
|
|
9024
|
-
})).sort((a, b) => (b.createdAt?.getTime() ?? 0) - (a.createdAt?.getTime() ?? 0));
|
|
6801
|
+
async function getTableExcludes(databaseUrl, collectionsPath, deps = {}) {
|
|
6802
|
+
const getIncludes = deps.getIncludes ?? getTableIncludes;
|
|
6803
|
+
const queryTables = deps.queryExistingTables ?? queryExistingTables;
|
|
6804
|
+
const includes = await getIncludes(collectionsPath);
|
|
6805
|
+
const excludes = [
|
|
6806
|
+
"atlas_schema_revisions.*",
|
|
6807
|
+
"auth",
|
|
6808
|
+
"auth.*",
|
|
6809
|
+
"rebase",
|
|
6810
|
+
"rebase.*"
|
|
6811
|
+
];
|
|
6812
|
+
let existingTables;
|
|
6813
|
+
try {
|
|
6814
|
+
existingTables = await queryTables(databaseUrl);
|
|
6815
|
+
} catch (err) {
|
|
6816
|
+
throw new ExcludeIntrospectionError(`Failed to introspect the database for unmapped tables: ${err instanceof Error ? err.message : String(err)}`, err);
|
|
6817
|
+
}
|
|
6818
|
+
for (const table of existingTables) if (!includes.includes(table)) excludes.push(table);
|
|
6819
|
+
return excludes;
|
|
9025
6820
|
}
|
|
9026
6821
|
/**
|
|
9027
|
-
*
|
|
9028
|
-
*
|
|
6822
|
+
* Ask a yes/no question on an interactive terminal.
|
|
6823
|
+
*
|
|
6824
|
+
* The `isTTY` guard is the contract, not an optimisation: non-interactive
|
|
6825
|
+
* shells (CI, pipes, agents) can never answer, and `readline` on a
|
|
6826
|
+
* non-TTY stdin resolves with whatever the pipe happens to contain — or never
|
|
6827
|
+
* resolves at all. Returning false there is what makes an unattended
|
|
6828
|
+
* `db push` abort instead of auto-confirming a destructive change. Callers
|
|
6829
|
+
* should already have gone through {@link decidePushSafety}, which decides
|
|
6830
|
+
* whether interactive confirmation is even possible; this is the backstop.
|
|
6831
|
+
*
|
|
6832
|
+
* Lives here rather than in cli.ts so it is reachable from a test — cli.ts uses
|
|
6833
|
+
* `import.meta` and cannot be imported by the jest runner.
|
|
9029
6834
|
*/
|
|
9030
|
-
async function
|
|
9031
|
-
|
|
9032
|
-
|
|
9033
|
-
|
|
9034
|
-
|
|
9035
|
-
} : async (key) => {
|
|
9036
|
-
if (!storage) throw new BackupToolError("Storage backend required to prune object backups.");
|
|
9037
|
-
await storage.deleteObject(key, dest.bucket);
|
|
6835
|
+
async function promptConfirm(question) {
|
|
6836
|
+
if (!process.stdin.isTTY) return false;
|
|
6837
|
+
const rl = import___vite_browser_external.default.createInterface({
|
|
6838
|
+
input: process.stdin,
|
|
6839
|
+
output: process.stdout
|
|
9038
6840
|
});
|
|
9039
|
-
|
|
6841
|
+
try {
|
|
6842
|
+
const answer = await new Promise((resolve) => rl.question(question, resolve));
|
|
6843
|
+
return /^y(es)?$/i.test(answer.trim());
|
|
6844
|
+
} finally {
|
|
6845
|
+
rl.close();
|
|
6846
|
+
}
|
|
6847
|
+
}
|
|
6848
|
+
//#endregion
|
|
6849
|
+
//#region src/schema/rls-bootstrap-sql.ts
|
|
6850
|
+
var rls_bootstrap_sql_exports = /* @__PURE__ */ __exportAll({
|
|
6851
|
+
DROP_LEGACY_AUTH_SCHEMA_SQL: () => DROP_LEGACY_AUTH_SCHEMA_SQL,
|
|
6852
|
+
LEGACY_RLS_DEPENDENTS_SQL: () => LEGACY_RLS_DEPENDENTS_SQL,
|
|
6853
|
+
LEGACY_RLS_FUNCTION_DEPENDENTS_SQL: () => LEGACY_RLS_FUNCTION_DEPENDENTS_SQL,
|
|
6854
|
+
RLS_BOOTSTRAP_SQL: () => RLS_BOOTSTRAP_SQL,
|
|
6855
|
+
RLS_BOOTSTRAP_STATEMENTS: () => RLS_BOOTSTRAP_STATEMENTS,
|
|
6856
|
+
dropLegacyAuthSchema: () => dropLegacyAuthSchema
|
|
6857
|
+
});
|
|
6858
|
+
/**
|
|
6859
|
+
* Canonical SQL bootstrap for the RLS helper functions.
|
|
6860
|
+
*
|
|
6861
|
+
* Generated RLS policies reference `rebase.uid()` / `rebase.roles()` /
|
|
6862
|
+
* `rebase.jwt()`, so any SQL stream that can contain policies must be
|
|
6863
|
+
* self-contained: it has to (re)create these helpers first. This matters for
|
|
6864
|
+
* the migration directory in particular — Atlas replays migrations against a
|
|
6865
|
+
* clean dev database where no out-of-band bootstrap has ever run, so a
|
|
6866
|
+
* migration carrying policies without this preamble fails with
|
|
6867
|
+
* "function rebase.uid() does not exist".
|
|
6868
|
+
*
|
|
6869
|
+
* Idempotent (`IF NOT EXISTS` / `OR REPLACE`) so it can be prepended to every
|
|
6870
|
+
* policies block and re-applied freely. The runtime boot path
|
|
6871
|
+
* (`auth/ensure-tables.ts`) creates the same functions under an advisory lock
|
|
6872
|
+
* for HMR-safety; keep the definitions in sync.
|
|
6873
|
+
*
|
|
6874
|
+
* ## Creating the `rebase` schema here is now safe, and required
|
|
6875
|
+
*
|
|
6876
|
+
* It deliberately did not, once. These functions lived in a schema called
|
|
6877
|
+
* `auth`, and the note here read: creating `rebase` would leak it into Atlas's
|
|
6878
|
+
* replayed migration state, and — absent from the desired `schema.sql` — Atlas
|
|
6879
|
+
* would then plan `DROP SCHEMA "rebase" CASCADE`, taking the auth tables with
|
|
6880
|
+
* it. That reasoning still holds; what changed is the second half of it. The
|
|
6881
|
+
* DDL generator now emits `CREATE SCHEMA IF NOT EXISTS "rebase"`
|
|
6882
|
+
* unconditionally, so the schema is always in the desired state and the diff is
|
|
6883
|
+
* empty. (It used to appear only when some collection happened to declare
|
|
6884
|
+
* `schema: "rebase"` — true for the scaffold's users collection, and not a
|
|
6885
|
+
* property anything guaranteed.) `db push` additionally excludes the whole
|
|
6886
|
+
* schema from the declarative apply.
|
|
6887
|
+
*
|
|
6888
|
+
* See `@rebasepro/types`' `rls-functions` for why the functions moved out of
|
|
6889
|
+
* `auth` at all: the short version is that the name was Supabase's, and
|
|
6890
|
+
* `CREATE OR REPLACE FUNCTION auth.uid() RETURNS text` cannot be applied over
|
|
6891
|
+
* Supabase's `RETURNS uuid` — Postgres refuses, and the refusal used to be
|
|
6892
|
+
* swallowed.
|
|
6893
|
+
*/
|
|
6894
|
+
/**
|
|
6895
|
+
* The bootstrap as individual statements.
|
|
6896
|
+
*
|
|
6897
|
+
* Kept as an array because the two consumers need different shapes and only one
|
|
6898
|
+
* of them can take a multi-command string: the migration preamble is written to
|
|
6899
|
+
* a file and replayed by Atlas, but the boot path runs through drizzle, whose
|
|
6900
|
+
* node-postgres handle speaks the extended query protocol and rejects more than
|
|
6901
|
+
* one command per call. Splitting a joined string back apart on `$$;` would be
|
|
6902
|
+
* a parser for a problem that does not need one.
|
|
6903
|
+
*/
|
|
6904
|
+
var RLS_BOOTSTRAP_STATEMENTS = [
|
|
6905
|
+
`CREATE SCHEMA IF NOT EXISTS ${REBASE_SCHEMA}`,
|
|
6906
|
+
`CREATE OR REPLACE FUNCTION ${REBASE_SCHEMA}.uid() RETURNS text AS $$
|
|
6907
|
+
SELECT COALESCE(
|
|
6908
|
+
NULLIF(current_setting('app.uid', true), ''),
|
|
6909
|
+
NULLIF(current_setting('app.user_id', true), '')
|
|
6910
|
+
);
|
|
6911
|
+
$$ LANGUAGE sql STABLE`,
|
|
6912
|
+
`CREATE OR REPLACE FUNCTION ${REBASE_SCHEMA}.jwt() RETURNS jsonb AS $$
|
|
6913
|
+
SELECT COALESCE(
|
|
6914
|
+
NULLIF(current_setting('app.jwt', true), ''),
|
|
6915
|
+
'{}'
|
|
6916
|
+
)::jsonb;
|
|
6917
|
+
$$ LANGUAGE sql STABLE`,
|
|
6918
|
+
`CREATE OR REPLACE FUNCTION ${REBASE_SCHEMA}.roles() RETURNS text AS $$
|
|
6919
|
+
SELECT COALESCE(NULLIF(current_setting('app.user_roles', true), ''), '');
|
|
6920
|
+
$$ LANGUAGE sql STABLE`,
|
|
6921
|
+
`CREATE OR REPLACE FUNCTION ${REBASE_SCHEMA}.is_anonymous() RETURNS boolean AS $$
|
|
6922
|
+
SELECT COALESCE(NULLIF(current_setting('app.is_anonymous', true), ''), 'false')::boolean;
|
|
6923
|
+
$$ LANGUAGE sql STABLE`
|
|
6924
|
+
];
|
|
6925
|
+
/** The same statements as one script, for migration files and raw clients. */
|
|
6926
|
+
var RLS_BOOTSTRAP_SQL = "-- Rebase schema + RLS helper functions (required by the policies below)\n" + RLS_BOOTSTRAP_STATEMENTS.map((s) => `${s};`).join("\n\n") + "\n";
|
|
6927
|
+
/**
|
|
6928
|
+
* Removes the pre-1.0 `auth` schema, but only when Rebase is what put it there.
|
|
6929
|
+
*
|
|
6930
|
+
* ## Why this is safe against a Supabase database
|
|
6931
|
+
*
|
|
6932
|
+
* Two independent guards, and both have to pass:
|
|
6933
|
+
*
|
|
6934
|
+
* 1. **Each function is identified before it is dropped.** Ours returns `text`
|
|
6935
|
+
* and reads the `app.uid` GUC; Supabase's returns `uuid` and reads
|
|
6936
|
+
* `request.jwt.claims`. Nothing is dropped on a signature we did not write,
|
|
6937
|
+
* so a Supabase database — where our `CREATE OR REPLACE` could never have
|
|
6938
|
+
* succeeded in the first place, Postgres refusing to change a return type —
|
|
6939
|
+
* matches nothing and this is a no-op.
|
|
6940
|
+
* 2. **`DROP SCHEMA … RESTRICT`**, never CASCADE. If anything else at all still
|
|
6941
|
+
* lives in `auth` (Supabase's `users` table, its other helpers), the drop
|
|
6942
|
+
* fails and the schema stays. CASCADE here would be unrecoverable.
|
|
6943
|
+
*
|
|
6944
|
+
* ## Why it cannot run too early
|
|
6945
|
+
*
|
|
6946
|
+
* Postgres records a dependency from every RLS policy to the functions its body
|
|
6947
|
+
* calls, so `DROP FUNCTION auth.uid()` fails for as long as a single policy
|
|
6948
|
+
* still references it. That is the interlock, and it is load-bearing: the drop
|
|
6949
|
+
* can only succeed once every policy has been recompiled to \`rebase.uid()\`.
|
|
6950
|
+
* Callers therefore run this *after* applying policies, and treat a failure as
|
|
6951
|
+
* "not yet — try again next boot" rather than as an error.
|
|
6952
|
+
*/
|
|
6953
|
+
var DROP_LEGACY_AUTH_SCHEMA_SQL = `
|
|
6954
|
+
DO $rebase_drop_legacy$
|
|
6955
|
+
DECLARE
|
|
6956
|
+
dropped_any boolean := false;
|
|
6957
|
+
BEGIN
|
|
6958
|
+
-- Each function is matched on its own result type and body, so a schema
|
|
6959
|
+
-- that merely shares the name keeps everything it has.
|
|
6960
|
+
IF EXISTS (
|
|
6961
|
+
SELECT 1 FROM pg_proc p JOIN pg_namespace n ON n.oid = p.pronamespace
|
|
6962
|
+
WHERE n.nspname = 'auth' AND p.proname = 'uid'
|
|
6963
|
+
AND pg_get_function_result(p.oid) = 'text'
|
|
6964
|
+
AND p.prosrc LIKE '%app.uid%'
|
|
6965
|
+
) THEN
|
|
6966
|
+
DROP FUNCTION auth.uid();
|
|
6967
|
+
dropped_any := true;
|
|
6968
|
+
END IF;
|
|
6969
|
+
|
|
6970
|
+
IF EXISTS (
|
|
6971
|
+
SELECT 1 FROM pg_proc p JOIN pg_namespace n ON n.oid = p.pronamespace
|
|
6972
|
+
WHERE n.nspname = 'auth' AND p.proname = 'jwt'
|
|
6973
|
+
AND pg_get_function_result(p.oid) = 'jsonb'
|
|
6974
|
+
AND p.prosrc LIKE '%app.jwt%'
|
|
6975
|
+
) THEN
|
|
6976
|
+
DROP FUNCTION auth.jwt();
|
|
6977
|
+
dropped_any := true;
|
|
6978
|
+
END IF;
|
|
6979
|
+
|
|
6980
|
+
IF EXISTS (
|
|
6981
|
+
SELECT 1 FROM pg_proc p JOIN pg_namespace n ON n.oid = p.pronamespace
|
|
6982
|
+
WHERE n.nspname = 'auth' AND p.proname = 'roles'
|
|
6983
|
+
AND pg_get_function_result(p.oid) = 'text'
|
|
6984
|
+
AND p.prosrc LIKE '%app.user_roles%'
|
|
6985
|
+
) THEN
|
|
6986
|
+
DROP FUNCTION auth.roles();
|
|
6987
|
+
dropped_any := true;
|
|
6988
|
+
END IF;
|
|
6989
|
+
|
|
6990
|
+
-- RESTRICT: only an empty schema goes. Anything else in there — including a
|
|
6991
|
+
-- Supabase installation left untouched above — keeps it.
|
|
6992
|
+
IF dropped_any THEN
|
|
6993
|
+
BEGIN
|
|
6994
|
+
EXECUTE 'DROP SCHEMA auth RESTRICT';
|
|
6995
|
+
EXCEPTION WHEN OTHERS THEN
|
|
6996
|
+
NULL;
|
|
6997
|
+
END;
|
|
6998
|
+
END IF;
|
|
6999
|
+
END
|
|
7000
|
+
$rebase_drop_legacy$;
|
|
7001
|
+
`;
|
|
7002
|
+
/**
|
|
7003
|
+
* Policies whose body still calls `auth.uid()` / `auth.roles()` / `auth.jwt()`.
|
|
7004
|
+
*
|
|
7005
|
+
* Postgres will not drop a function a policy depends on, so this is exactly the
|
|
7006
|
+
* set standing between a database and losing the legacy schema. Rebase's own
|
|
7007
|
+
* policies leave the list on the next push or boot, when they are recompiled —
|
|
7008
|
+
* anything still here afterwards is hand-written, will never be recompiled by
|
|
7009
|
+
* anybody, and is the reason the drop keeps being skipped. Silence there would
|
|
7010
|
+
* leave an operator staring at a schema the release notes said would go.
|
|
7011
|
+
*/
|
|
7012
|
+
var LEGACY_RLS_DEPENDENTS_SQL = `
|
|
7013
|
+
SELECT n.nspname AS schema, c.relname AS "table", p.polname AS policy
|
|
7014
|
+
FROM pg_policy p
|
|
7015
|
+
JOIN pg_class c ON c.oid = p.polrelid
|
|
7016
|
+
JOIN pg_namespace n ON n.oid = c.relnamespace
|
|
7017
|
+
WHERE pg_get_expr(p.polqual, p.polrelid) ~* '\\m${LEGACY_RLS_SCHEMA}\\.(uid|jwt|roles)\\s*\\('
|
|
7018
|
+
OR pg_get_expr(p.polwithcheck, p.polrelid) ~* '\\m${LEGACY_RLS_SCHEMA}\\.(uid|jwt|roles)\\s*\\('
|
|
7019
|
+
ORDER BY 1, 2, 3
|
|
7020
|
+
`;
|
|
7021
|
+
/**
|
|
7022
|
+
* Functions whose body calls `auth.uid()` / `auth.roles()` / `auth.jwt()`.
|
|
7023
|
+
*
|
|
7024
|
+
* This is the half `DROP FUNCTION ... RESTRICT` cannot see, and the reason it
|
|
7025
|
+
* needs its own query. Postgres records a dependency for a *policy* that calls a
|
|
7026
|
+
* function, which is why the drop is safe against the policies above — but a
|
|
7027
|
+
* `LANGUAGE sql` function whose body is a **string literal** is not parsed when
|
|
7028
|
+
* it is created, so nothing is recorded and `RESTRICT` has nothing to refuse on.
|
|
7029
|
+
* The drop succeeds and the caller is left pointing at a function that no longer
|
|
7030
|
+
* exists, which fails at *query* time rather than at boot.
|
|
7031
|
+
*
|
|
7032
|
+
* A downstream project building on these helpers is not hypothetical: the Rebase
|
|
7033
|
+
* control plane defines `auth.is_org_member(uuid)` and `auth.is_org_admin(uuid)`
|
|
7034
|
+
* in this very schema, each calling `auth.uid()` in a string body, and eleven of
|
|
7035
|
+
* its row-level-security policies go through them. Every one of those would have
|
|
7036
|
+
* started failing the first time a recompile left no policy referencing
|
|
7037
|
+
* `auth.uid()` directly — the drop's own precondition.
|
|
7038
|
+
*
|
|
7039
|
+
* Matching on the body text is the only option available, and it is deliberately
|
|
7040
|
+
* broad: a false positive costs a schema that stays one release longer and says
|
|
7041
|
+
* why, while a false negative costs somebody their policies.
|
|
7042
|
+
*/
|
|
7043
|
+
var LEGACY_RLS_FUNCTION_DEPENDENTS_SQL = `
|
|
7044
|
+
SELECT n.nspname AS schema, p.proname AS function
|
|
7045
|
+
FROM pg_proc p
|
|
7046
|
+
JOIN pg_namespace n ON n.oid = p.pronamespace
|
|
7047
|
+
WHERE n.nspname NOT IN ('pg_catalog', 'information_schema')
|
|
7048
|
+
AND NOT (n.nspname = '${LEGACY_RLS_SCHEMA}' AND p.proname IN ('uid', 'jwt', 'roles'))
|
|
7049
|
+
AND p.prosrc ~* '\\m${LEGACY_RLS_SCHEMA}\\.(uid|jwt|roles)\\s*\\('
|
|
7050
|
+
ORDER BY 1, 2
|
|
7051
|
+
`;
|
|
7052
|
+
/**
|
|
7053
|
+
* Retire the pre-1.0 `auth` schema, reporting what is holding it back.
|
|
7054
|
+
*
|
|
7055
|
+
* The shared implementation behind the CLI's post-push step and the runtime's
|
|
7056
|
+
* post-policy step. Both used to just fire {@link DROP_LEGACY_AUTH_SCHEMA_SQL}
|
|
7057
|
+
* and swallow whatever came back, which is right for the ordinary case — a
|
|
7058
|
+
* table not recompiled *yet* — and wrong for the one that never resolves: a
|
|
7059
|
+
* hand-written policy nothing will ever rewrite. Then the schema stays forever
|
|
7060
|
+
* and nothing ever says why.
|
|
7061
|
+
*/
|
|
7062
|
+
async function dropLegacyAuthSchema(run, report) {
|
|
7063
|
+
let blockers;
|
|
7064
|
+
try {
|
|
7065
|
+
blockers = await run(LEGACY_RLS_DEPENDENTS_SQL);
|
|
7066
|
+
} catch {
|
|
7067
|
+
return;
|
|
7068
|
+
}
|
|
7069
|
+
if (blockers.length > 0) {
|
|
7070
|
+
report.warn(`The pre-1.0 \`${LEGACY_RLS_SCHEMA}\` schema cannot be removed yet: ${blockers.length} ${blockers.length === 1 ? "policy still calls" : "policies still call"} \`${LEGACY_RLS_SCHEMA}.uid()\` and friends, and Postgres will not drop a function a policy depends on. Rebase's own policies are rewritten automatically — anything listed here is hand-written SQL that has to be updated to \`${REBASE_SCHEMA}.uid()\` by hand, after which the schema goes on its own:\n` + blockers.map((b) => ` • ${b.schema}.${b.table} → "${b.policy}"`).join("\n"));
|
|
7071
|
+
return;
|
|
7072
|
+
}
|
|
7073
|
+
let borrowers;
|
|
7074
|
+
try {
|
|
7075
|
+
borrowers = await run(LEGACY_RLS_FUNCTION_DEPENDENTS_SQL);
|
|
7076
|
+
} catch {
|
|
7077
|
+
return;
|
|
7078
|
+
}
|
|
7079
|
+
if (borrowers.length > 0) {
|
|
7080
|
+
report.warn(`The pre-1.0 \`${LEGACY_RLS_SCHEMA}\` schema cannot be removed yet: ${borrowers.length} ${borrowers.length === 1 ? "function calls" : "functions call"} \`${LEGACY_RLS_SCHEMA}.uid()\` and friends from its own body. Postgres records no dependency for that — a SQL body written as a string literal is never parsed — so dropping the helpers would leave these pointing at nothing, and they would fail when a query reached them rather than now. Repoint them at \`${REBASE_SCHEMA}.uid()\`, after which the schema goes on its own:\n` + borrowers.map((b) => ` • ${b.schema}.${b.function}()`).join("\n"));
|
|
7081
|
+
return;
|
|
7082
|
+
}
|
|
7083
|
+
try {
|
|
7084
|
+
await run(DROP_LEGACY_AUTH_SCHEMA_SQL);
|
|
7085
|
+
} catch (err) {
|
|
7086
|
+
report.info(`Left the \`${LEGACY_RLS_SCHEMA}\` schema in place: ` + (err instanceof Error ? err.message : String(err)));
|
|
7087
|
+
}
|
|
9040
7088
|
}
|
|
9041
7089
|
//#endregion
|
|
9042
|
-
export {
|
|
7090
|
+
export { resolveLocalBin as C, readVectorDdl as S, forLibpq as T, getSearchExcludes as _, ExcludeIntrospectionError as a, promptConfirm as b, describeBuildScriptRemedy as c, detectProjectPackageManager as d, diagnoseMissingBin as f, getForeignIndexExcludes as g, getDevDatabaseUrl as h, rls_bootstrap_sql_exports as i, describeDevAddCommand as l, ensureDevDatabaseExists as m, RLS_BOOTSTRAP_STATEMENTS as n, applySearchDdl as o, dropDevDatabase as p, dropLegacyAuthSchema as r, applyVectorDdl as s, RLS_BOOTSTRAP_SQL as t, describeReinstallCommand as u, getTableExcludes as v, execa as w, readSearchDdl as x, getVectorExcludes as y };
|
|
9043
7091
|
|
|
9044
|
-
//# sourceMappingURL=
|
|
7092
|
+
//# sourceMappingURL=rls-bootstrap-sql-DLDnUjtx.js.map
|