@rebasepro/server-postgresql 0.6.1 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/PostgresBackendDriver.d.ts +8 -0
- package/dist/auth/services.d.ts +21 -1
- package/dist/cli-errors.d.ts +29 -0
- package/dist/cli-helpers.d.ts +7 -0
- package/dist/collections/PostgresCollectionRegistry.d.ts +2 -2
- package/dist/index.es.js +2987 -230
- package/dist/index.es.js.map +1 -1
- package/dist/schema/auth-default-policies.d.ts +12 -0
- package/dist/schema/auth-schema.d.ts +227 -0
- package/dist/schema/generate-postgres-ddl-logic.d.ts +5 -0
- package/dist/schema/generate-postgres-ddl.d.ts +1 -0
- package/dist/services/entityService.d.ts +1 -1
- package/dist/utils/pg-error-utils.d.ts +10 -0
- package/dist/utils/table-classification.d.ts +8 -0
- package/package.json +15 -9
- package/src/PostgresBackendDriver.ts +200 -68
- package/src/PostgresBootstrapper.ts +34 -2
- package/src/auth/ensure-tables.ts +56 -1
- package/src/auth/services.ts +94 -1
- package/src/cli-errors.ts +162 -0
- package/src/cli-helpers.ts +183 -0
- package/src/cli.ts +264 -245
- package/src/collections/PostgresCollectionRegistry.ts +6 -6
- package/src/data-transformer.ts +2 -2
- package/src/schema/auth-default-policies.ts +97 -0
- package/src/schema/auth-schema.ts +25 -2
- package/src/schema/doctor.ts +2 -4
- package/src/schema/generate-drizzle-schema-logic.ts +20 -82
- package/src/schema/generate-drizzle-schema.ts +3 -5
- package/src/schema/generate-postgres-ddl-logic.ts +487 -0
- package/src/schema/generate-postgres-ddl.ts +116 -0
- package/src/services/EntityPersistService.ts +10 -8
- package/src/services/entityService.ts +28 -3
- package/src/services/realtimeService.ts +26 -2
- package/src/utils/pg-error-utils.ts +16 -0
- package/src/utils/table-classification.ts +16 -0
- package/src/websocket.ts +9 -0
- package/test/auth-default-policies.test.ts +89 -0
- package/test/cli-helpers-extended.test.ts +324 -0
- package/test/cli-helpers.test.ts +59 -0
- package/test/connection.test.ts +292 -0
- package/test/databasePoolManager.test.ts +289 -0
- package/test/doctor-extended.test.ts +443 -0
- package/test/e2e/db-e2e.test.ts +293 -0
- package/test/e2e/pg-setup.ts +79 -0
- package/test/entity-callbacks-redaction.test.ts +86 -0
- package/test/entity-persist-composite-keys.test.ts +451 -0
- package/test/generate-postgres-ddl-edge-cases.test.ts +716 -0
- package/test/generate-postgres-ddl.test.ts +300 -0
- package/test/mfa-service.test.ts +544 -0
- package/test/pg-error-utils.test.ts +50 -1
- package/test/postgresDataDriver.test.ts +2 -1
- package/test/realtimeService-channels.test.ts +696 -0
- package/test/unmapped-tables-safety.test.ts +55 -342
- package/vitest.e2e.config.ts +10 -0
package/dist/index.es.js
CHANGED
|
@@ -152,15 +152,118 @@ var Vector = class {
|
|
|
152
152
|
}
|
|
153
153
|
};
|
|
154
154
|
//#endregion
|
|
155
|
+
//#region ../types/src/types/filter-operators.ts
|
|
156
|
+
/** Maps REST short-code operators to their canonical equivalents. */
|
|
157
|
+
var REST_TO_CANONICAL = {
|
|
158
|
+
"eq": "==",
|
|
159
|
+
"neq": "!=",
|
|
160
|
+
"gt": ">",
|
|
161
|
+
"gte": ">=",
|
|
162
|
+
"lt": "<",
|
|
163
|
+
"lte": "<=",
|
|
164
|
+
"in": "in",
|
|
165
|
+
"nin": "not-in",
|
|
166
|
+
"cs": "array-contains",
|
|
167
|
+
"csa": "array-contains-any"
|
|
168
|
+
};
|
|
169
|
+
/** All canonical operator strings for runtime validation. */
|
|
170
|
+
var CANONICAL_OPS = new Set([
|
|
171
|
+
"<",
|
|
172
|
+
"<=",
|
|
173
|
+
"==",
|
|
174
|
+
"!=",
|
|
175
|
+
">=",
|
|
176
|
+
">",
|
|
177
|
+
"in",
|
|
178
|
+
"not-in",
|
|
179
|
+
"array-contains",
|
|
180
|
+
"array-contains-any"
|
|
181
|
+
]);
|
|
182
|
+
/**
|
|
183
|
+
* Resolve any operator string (canonical or REST short-code) to its
|
|
184
|
+
* canonical `WhereFilterOp` form. Returns `undefined` for unknown operators.
|
|
185
|
+
*
|
|
186
|
+
* @example
|
|
187
|
+
* toCanonicalOp("==") // "=="
|
|
188
|
+
* toCanonicalOp("eq") // "=="
|
|
189
|
+
* toCanonicalOp("cs") // "array-contains"
|
|
190
|
+
* toCanonicalOp("xyz") // undefined
|
|
191
|
+
*/
|
|
192
|
+
function toCanonicalOp(op) {
|
|
193
|
+
if (CANONICAL_OPS.has(op)) return op;
|
|
194
|
+
return REST_TO_CANONICAL[op];
|
|
195
|
+
}
|
|
196
|
+
//#endregion
|
|
155
197
|
//#region ../types/src/types/collections.ts
|
|
156
198
|
/**
|
|
157
199
|
* Type guard for PostgreSQL collections.
|
|
158
|
-
* Returns true if the collection uses the Postgres
|
|
200
|
+
* Returns true if the collection uses the Postgres engine (or the default engine).
|
|
159
201
|
* @group Models
|
|
160
202
|
*/
|
|
161
203
|
function isPostgresCollection(collection) {
|
|
162
|
-
return !collection.
|
|
204
|
+
return !collection.engine || collection.engine === "postgres";
|
|
163
205
|
}
|
|
206
|
+
/**
|
|
207
|
+
* Reads a collection's driver-declared subcollections thunk (the `subcollections`
|
|
208
|
+
* field) independent of engine identity, so engine-agnostic code doesn't have to
|
|
209
|
+
* type-guard against a specific driver. Returns `undefined` when the collection
|
|
210
|
+
* declares none.
|
|
211
|
+
*
|
|
212
|
+
* Pair with `getDataSourceCapabilities(engine).supportsSubcollections` to decide
|
|
213
|
+
* whether the engine honours subcollections at all before reading them.
|
|
214
|
+
* @group Models
|
|
215
|
+
*/
|
|
216
|
+
function getDeclaredSubcollections(collection) {
|
|
217
|
+
return collection.subcollections;
|
|
218
|
+
}
|
|
219
|
+
//#endregion
|
|
220
|
+
//#region ../types/src/types/policy.ts
|
|
221
|
+
/** @group Models */
|
|
222
|
+
var policy = {
|
|
223
|
+
true: () => ({ kind: "true" }),
|
|
224
|
+
false: () => ({ kind: "false" }),
|
|
225
|
+
and: (...operands) => ({
|
|
226
|
+
kind: "and",
|
|
227
|
+
operands
|
|
228
|
+
}),
|
|
229
|
+
or: (...operands) => ({
|
|
230
|
+
kind: "or",
|
|
231
|
+
operands
|
|
232
|
+
}),
|
|
233
|
+
not: (operand) => ({
|
|
234
|
+
kind: "not",
|
|
235
|
+
operand
|
|
236
|
+
}),
|
|
237
|
+
compare: (left, op, right) => ({
|
|
238
|
+
kind: "compare",
|
|
239
|
+
op,
|
|
240
|
+
left,
|
|
241
|
+
right
|
|
242
|
+
}),
|
|
243
|
+
rolesOverlap: (roles) => ({
|
|
244
|
+
kind: "rolesOverlap",
|
|
245
|
+
roles
|
|
246
|
+
}),
|
|
247
|
+
rolesContain: (roles) => ({
|
|
248
|
+
kind: "rolesContain",
|
|
249
|
+
roles
|
|
250
|
+
}),
|
|
251
|
+
authenticated: () => ({ kind: "authenticated" }),
|
|
252
|
+
raw: (sql) => ({
|
|
253
|
+
kind: "raw",
|
|
254
|
+
sql
|
|
255
|
+
}),
|
|
256
|
+
field: (name) => ({
|
|
257
|
+
kind: "field",
|
|
258
|
+
name
|
|
259
|
+
}),
|
|
260
|
+
literal: (value) => ({
|
|
261
|
+
kind: "literal",
|
|
262
|
+
value
|
|
263
|
+
}),
|
|
264
|
+
authUid: () => ({ kind: "authUid" }),
|
|
265
|
+
authRoles: () => ({ kind: "authRoles" })
|
|
266
|
+
};
|
|
164
267
|
//#endregion
|
|
165
268
|
//#region ../types/src/types/backend.ts
|
|
166
269
|
/**
|
|
@@ -177,8 +280,6 @@ function isSQLAdmin(admin) {
|
|
|
177
280
|
function isSchemaAdmin(admin) {
|
|
178
281
|
return !!admin && (typeof admin.fetchUnmappedTables === "function" || typeof admin.fetchTableMetadata === "function");
|
|
179
282
|
}
|
|
180
|
-
//#endregion
|
|
181
|
-
//#region ../types/src/types/data_source.ts
|
|
182
283
|
/** @group Models */
|
|
183
284
|
var POSTGRES_CAPABILITIES = {
|
|
184
285
|
key: "postgres",
|
|
@@ -246,13 +347,13 @@ var CAPABILITIES_REGISTRY = {
|
|
|
246
347
|
"(default)": DEFAULT_CAPABILITIES
|
|
247
348
|
};
|
|
248
349
|
/**
|
|
249
|
-
* Look up capabilities for a given
|
|
250
|
-
* If `
|
|
350
|
+
* Look up capabilities for a given engine key.
|
|
351
|
+
* If `engine` is undefined or not found, returns `DEFAULT_CAPABILITIES`.
|
|
251
352
|
* @group Models
|
|
252
353
|
*/
|
|
253
|
-
function getDataSourceCapabilities(
|
|
254
|
-
if (!
|
|
255
|
-
return CAPABILITIES_REGISTRY[
|
|
354
|
+
function getDataSourceCapabilities(engine) {
|
|
355
|
+
if (!engine) return POSTGRES_CAPABILITIES;
|
|
356
|
+
return CAPABILITIES_REGISTRY[engine] ?? DEFAULT_CAPABILITIES;
|
|
256
357
|
}
|
|
257
358
|
var snakeCaseRegex = /[A-Z]{2,}(?=[A-Z][a-z]|\b)|[A-Z]?[a-z]+|[0-9]+(?:[a-z](?![a-z]))?|[A-Z]/g;
|
|
258
359
|
var toSnakeCase = (str) => {
|
|
@@ -1443,7 +1544,7 @@ function sanitizeRelation(relation, sourceCollection, resolveCollection) {
|
|
|
1443
1544
|
if (!newRelation.foreignKeyOnTarget) {
|
|
1444
1545
|
let foundForeignKey = false;
|
|
1445
1546
|
try {
|
|
1446
|
-
const targetRelations = getDataSourceCapabilities(targetCollection.
|
|
1547
|
+
const targetRelations = getDataSourceCapabilities(targetCollection.engine).supportsRelations ? targetCollection.relations || [] : [];
|
|
1447
1548
|
for (const targetRel of targetRelations) if (targetRel.direction === "owning" && targetRel.cardinality === "one" && targetRel.localKey) try {
|
|
1448
1549
|
if (targetRel.target().slug === sourceCollection.slug) {
|
|
1449
1550
|
newRelation.foreignKeyOnTarget = targetRel.localKey;
|
|
@@ -1459,7 +1560,7 @@ function sanitizeRelation(relation, sourceCollection, resolveCollection) {
|
|
|
1459
1560
|
} else if (newRelation.cardinality === "many" && newRelation.direction === "inverse") {
|
|
1460
1561
|
let isManyToManyInverse = false;
|
|
1461
1562
|
if (newRelation.inverseRelationName && !newRelation.foreignKeyOnTarget) try {
|
|
1462
|
-
const targetRelations = getDataSourceCapabilities(targetCollection.
|
|
1563
|
+
const targetRelations = getDataSourceCapabilities(targetCollection.engine).supportsRelations ? targetCollection.relations || [] : [];
|
|
1463
1564
|
for (const targetRel of targetRelations) if (targetRel.cardinality === "many" && (targetRel.direction === "owning" || !targetRel.direction) && targetRel.relationName === newRelation.inverseRelationName) {
|
|
1464
1565
|
isManyToManyInverse = true;
|
|
1465
1566
|
break;
|
|
@@ -1494,11 +1595,10 @@ var _resolvedRelationsCache = /* @__PURE__ */ new WeakMap();
|
|
|
1494
1595
|
function resolveCollectionRelations(collection) {
|
|
1495
1596
|
const cached = _resolvedRelationsCache.get(collection);
|
|
1496
1597
|
if (cached) return cached;
|
|
1497
|
-
if (!getDataSourceCapabilities(collection.
|
|
1498
|
-
const relCollection = collection;
|
|
1598
|
+
if (!getDataSourceCapabilities(collection.engine).supportsRelations) return {};
|
|
1499
1599
|
const relations = {};
|
|
1500
1600
|
const registeredRelationNames = /* @__PURE__ */ new Set();
|
|
1501
|
-
if (
|
|
1601
|
+
if (collection.relations) collection.relations.forEach((relation) => {
|
|
1502
1602
|
try {
|
|
1503
1603
|
const normalizedRelation = sanitizeRelation(relation, collection);
|
|
1504
1604
|
const relationKey = normalizedRelation.relationName;
|
|
@@ -1545,7 +1645,7 @@ function resolvePropertyRelation({ propertyKey, property, sourceCollection }) {
|
|
|
1545
1645
|
console.warn(`Unrecognized or missing relation target for property '${propertyKey}' in collection '${sourceCollection.slug}'`);
|
|
1546
1646
|
}
|
|
1547
1647
|
function getTableName$1(collection) {
|
|
1548
|
-
if (getDataSourceCapabilities(collection.
|
|
1648
|
+
if (getDataSourceCapabilities(collection.engine).supportsRelations) return collection.table ?? toSnakeCase(collection.slug) ?? toSnakeCase(collection.name);
|
|
1549
1649
|
return toSnakeCase(collection.slug) ?? toSnakeCase(collection.name);
|
|
1550
1650
|
}
|
|
1551
1651
|
function getTableVarName(tableName) {
|
|
@@ -1577,8 +1677,9 @@ function findRelation(resolvedRelations, key) {
|
|
|
1577
1677
|
//#region ../common/src/util/resolutions.ts
|
|
1578
1678
|
function getSubcollections(collection) {
|
|
1579
1679
|
if (collection.childCollections) return collection.childCollections() ?? [];
|
|
1580
|
-
|
|
1581
|
-
if (getDataSourceCapabilities(collection.
|
|
1680
|
+
const declaredSubcollections = getDeclaredSubcollections(collection);
|
|
1681
|
+
if (getDataSourceCapabilities(collection.engine).supportsSubcollections && declaredSubcollections) return declaredSubcollections() ?? [];
|
|
1682
|
+
if (getDataSourceCapabilities(collection.engine).supportsRelations) {
|
|
1582
1683
|
const resolvedRelations = resolveCollectionRelations(collection);
|
|
1583
1684
|
return Object.values(resolvedRelations).filter((r) => r.cardinality === "many").map((r) => {
|
|
1584
1685
|
const target = r.target();
|
|
@@ -1604,6 +1705,164 @@ function getSubcollections(collection) {
|
|
|
1604
1705
|
return [];
|
|
1605
1706
|
}
|
|
1606
1707
|
//#endregion
|
|
1708
|
+
//#region ../common/src/util/policy/sqlToPolicy.ts
|
|
1709
|
+
/**
|
|
1710
|
+
* A tiny, regex-based SQL "parser" for security rules.
|
|
1711
|
+
*
|
|
1712
|
+
* This is NOT a full SQL parser. It is designed to handle the subset of SQL
|
|
1713
|
+
* commonly used in `USING` and `WITH CHECK` clauses, enough to drive the
|
|
1714
|
+
* optimistic client-side UI decision.
|
|
1715
|
+
*
|
|
1716
|
+
* It handles:
|
|
1717
|
+
* - `field = 'literal'`
|
|
1718
|
+
* - `field != 'literal'`
|
|
1719
|
+
* - `field = current_setting('app.user_id')`
|
|
1720
|
+
* - `A AND B`
|
|
1721
|
+
* - `true`
|
|
1722
|
+
* - `IN (...)` (as optimistic true)
|
|
1723
|
+
*
|
|
1724
|
+
* For anything it doesn't understand, it returns a `raw` expression, which
|
|
1725
|
+
* the evaluator treats as "unknown" (and usually optimistic true).
|
|
1726
|
+
*/
|
|
1727
|
+
function sqlToPolicy(sql) {
|
|
1728
|
+
const trimmed = sql.trim();
|
|
1729
|
+
if (trimmed.toLowerCase() === "true") return policy.true();
|
|
1730
|
+
if (trimmed.toLowerCase() === "false") return policy.false();
|
|
1731
|
+
const overlapMatch = trimmed.match(/^string_to_array\s*\(\s*auth\.roles\(\)\s*,\s*','\s*\)\s*&&\s*ARRAY\s*\[(.+)\]$/i);
|
|
1732
|
+
if (overlapMatch) {
|
|
1733
|
+
const roles = overlapMatch[1].split(",").map((s) => s.trim().replace(/^'|'$/g, ""));
|
|
1734
|
+
return policy.rolesOverlap(roles);
|
|
1735
|
+
}
|
|
1736
|
+
const containMatch = trimmed.match(/^string_to_array\s*\(\s*auth\.roles\(\)\s*,\s*','\s*\)\s*@>\s*ARRAY\s*\[(.+)\]$/i);
|
|
1737
|
+
if (containMatch) {
|
|
1738
|
+
const roles = containMatch[1].split(",").map((s) => s.trim().replace(/^'|'$/g, ""));
|
|
1739
|
+
return policy.rolesContain(roles);
|
|
1740
|
+
}
|
|
1741
|
+
if (trimmed.toUpperCase().includes(" OR ")) {
|
|
1742
|
+
const parts = trimmed.split(/ OR /i);
|
|
1743
|
+
return policy.or(...parts.map(sqlToPolicy));
|
|
1744
|
+
}
|
|
1745
|
+
if (trimmed.toUpperCase().includes(" AND ")) {
|
|
1746
|
+
const parts = trimmed.split(/ AND /i);
|
|
1747
|
+
return policy.and(...parts.map(sqlToPolicy));
|
|
1748
|
+
}
|
|
1749
|
+
const match = trimmed.match(/^(.+?)\s*(!?=)\s*(.+)$/);
|
|
1750
|
+
if (match) {
|
|
1751
|
+
const [, leftStr, op, rightStr] = match;
|
|
1752
|
+
const left = parseOperand(leftStr.trim());
|
|
1753
|
+
const right = parseOperand(rightStr.trim());
|
|
1754
|
+
if (left && right) return policy.compare(left, op === "=" ? "eq" : "neq", right);
|
|
1755
|
+
}
|
|
1756
|
+
return policy.raw(sql);
|
|
1757
|
+
}
|
|
1758
|
+
function parseOperand(str) {
|
|
1759
|
+
if (/current_setting\s*\(\s*'app\.user_id'\s*\)/i.test(str) || /auth\.uid\(\)/i.test(str)) return policy.authUid();
|
|
1760
|
+
const stringMatch = str.match(/^'(.+)'$/);
|
|
1761
|
+
if (stringMatch) return policy.literal(stringMatch[1]);
|
|
1762
|
+
if (/^\w+$/.test(str)) return policy.field(str);
|
|
1763
|
+
return null;
|
|
1764
|
+
}
|
|
1765
|
+
//#endregion
|
|
1766
|
+
//#region ../common/src/util/policy/securityRuleToConditions.ts
|
|
1767
|
+
/**
|
|
1768
|
+
* Desugars a {@link SecurityRule} — its `access`/`ownerField`/`roles` shortcuts,
|
|
1769
|
+
* structured `condition`/`check`, and raw `using`/`withCheck` — into a single
|
|
1770
|
+
* normalized {@link PolicyExpression} pair.
|
|
1771
|
+
*
|
|
1772
|
+
* **This is the linchpin against drift:** both the Postgres DDL generators and
|
|
1773
|
+
* the client-side evaluator consume this one function, so there is exactly one
|
|
1774
|
+
* definition of what a rule means. In particular, application `roles` are folded
|
|
1775
|
+
* into the expression here (AND'd with the base condition, matching how Postgres
|
|
1776
|
+
* generates the clause) rather than being handled separately by each consumer.
|
|
1777
|
+
*/
|
|
1778
|
+
function securityRuleToConditions(rule) {
|
|
1779
|
+
return {
|
|
1780
|
+
usingExpr: withRoles(baseUsing(rule), rule),
|
|
1781
|
+
withCheckExpr: withRoles(baseWithCheck(rule), rule)
|
|
1782
|
+
};
|
|
1783
|
+
}
|
|
1784
|
+
function baseUsing(rule) {
|
|
1785
|
+
if (rule.condition) return rule.condition;
|
|
1786
|
+
if (rule.using != null) return sqlToPolicy(rule.using);
|
|
1787
|
+
if (rule.access === "public") return policy.true();
|
|
1788
|
+
if (rule.ownerField) return policy.compare(policy.field(rule.ownerField), "eq", policy.authUid());
|
|
1789
|
+
return null;
|
|
1790
|
+
}
|
|
1791
|
+
function baseWithCheck(rule) {
|
|
1792
|
+
if (rule.check) return rule.check;
|
|
1793
|
+
if (rule.withCheck != null) return sqlToPolicy(rule.withCheck);
|
|
1794
|
+
return baseUsing(rule);
|
|
1795
|
+
}
|
|
1796
|
+
/**
|
|
1797
|
+
* AND the base condition with an application-role check, or produce a roles-only
|
|
1798
|
+
* condition when there is no base. Mirrors the Postgres generator so that a
|
|
1799
|
+
* role-scoped restrictive rule denies exactly the same set of users on both
|
|
1800
|
+
* sides.
|
|
1801
|
+
*/
|
|
1802
|
+
function withRoles(base, rule) {
|
|
1803
|
+
if (!rule.roles || rule.roles.length === 0) return base;
|
|
1804
|
+
const rolesExpr = policy.rolesOverlap(rule.roles);
|
|
1805
|
+
if (rule.mode === "restrictive") return base ? policy.or(policy.not(rolesExpr), base) : policy.not(rolesExpr);
|
|
1806
|
+
return base ? policy.and(base, rolesExpr) : rolesExpr;
|
|
1807
|
+
}
|
|
1808
|
+
//#endregion
|
|
1809
|
+
//#region ../common/src/util/policy/policyToPostgres.ts
|
|
1810
|
+
/**
|
|
1811
|
+
* Compiles a {@link PolicyExpression} to a PostgreSQL boolean SQL string,
|
|
1812
|
+
* suitable for a `USING (...)` / `WITH CHECK (...)` clause.
|
|
1813
|
+
*
|
|
1814
|
+
* This is one of the two consumers of the shared policy model (the other being
|
|
1815
|
+
* {@link evaluatePolicy}); the Postgres schema generators call it so that DDL
|
|
1816
|
+
* and the admin UI derive from the exact same expression.
|
|
1817
|
+
*/
|
|
1818
|
+
function policyToPostgres(expr, collection) {
|
|
1819
|
+
switch (expr.kind) {
|
|
1820
|
+
case "true": return "true";
|
|
1821
|
+
case "false": return "false";
|
|
1822
|
+
case "and": return expr.operands.length === 0 ? "true" : expr.operands.map((o) => `(${policyToPostgres(o, collection)})`).join(" AND ");
|
|
1823
|
+
case "or": return expr.operands.length === 0 ? "false" : expr.operands.map((o) => `(${policyToPostgres(o, collection)})`).join(" OR ");
|
|
1824
|
+
case "not":
|
|
1825
|
+
if (expr.operand.kind === "authenticated") return "auth.uid() IS NULL";
|
|
1826
|
+
return `NOT (${policyToPostgres(expr.operand, collection)})`;
|
|
1827
|
+
case "compare": return `${operandToSql(expr.left, collection)} ${COMPARE_SQL[expr.op]} ${operandToSql(expr.right, collection)}`;
|
|
1828
|
+
case "rolesOverlap": return `string_to_array(auth.roles(), ',') && ${rolesArraySql(expr.roles)}`;
|
|
1829
|
+
case "rolesContain": return `string_to_array(auth.roles(), ',') @> ${rolesArraySql(expr.roles)}`;
|
|
1830
|
+
case "authenticated": return "auth.uid() IS NOT NULL";
|
|
1831
|
+
case "raw": return expr.sql.replace(/\{(\w+)\}/g, (_, col) => col);
|
|
1832
|
+
}
|
|
1833
|
+
}
|
|
1834
|
+
var COMPARE_SQL = {
|
|
1835
|
+
eq: "=",
|
|
1836
|
+
neq: "!=",
|
|
1837
|
+
lt: "<",
|
|
1838
|
+
lte: "<=",
|
|
1839
|
+
gt: ">",
|
|
1840
|
+
gte: ">="
|
|
1841
|
+
};
|
|
1842
|
+
function operandToSql(operand, collection) {
|
|
1843
|
+
switch (operand.kind) {
|
|
1844
|
+
case "field": return resolveColumnName$1(operand.name, collection);
|
|
1845
|
+
case "literal": return quoteLiteral(operand.value);
|
|
1846
|
+
case "authUid": return "auth.uid()";
|
|
1847
|
+
case "authRoles": return "string_to_array(auth.roles(), ',')";
|
|
1848
|
+
}
|
|
1849
|
+
}
|
|
1850
|
+
function resolveColumnName$1(propName, collection) {
|
|
1851
|
+
const prop = collection?.properties?.[propName];
|
|
1852
|
+
if (prop && "columnName" in prop && typeof prop.columnName === "string") return prop.columnName;
|
|
1853
|
+
return toSnakeCase(propName);
|
|
1854
|
+
}
|
|
1855
|
+
function quoteLiteral(value) {
|
|
1856
|
+
if (value === null) return "NULL";
|
|
1857
|
+
if (typeof value === "boolean") return value ? "true" : "false";
|
|
1858
|
+
if (typeof value === "number") return String(value);
|
|
1859
|
+
return `'${value.replace(/'/g, "''")}'`;
|
|
1860
|
+
}
|
|
1861
|
+
/** Sorted, single-quoted `ARRAY['a','b']` — matches the generators' output. */
|
|
1862
|
+
function rolesArraySql(roles) {
|
|
1863
|
+
return `ARRAY[${[...roles].sort().map((r) => `'${r}'`).join(",")}]`;
|
|
1864
|
+
}
|
|
1865
|
+
//#endregion
|
|
1607
1866
|
//#region ../common/src/util/callbacks.ts
|
|
1608
1867
|
/**
|
|
1609
1868
|
* Helper function to recursively check if there are any callbacks in the properties.
|
|
@@ -2366,8 +2625,67 @@ function createCustomEqual(options = {}) {
|
|
|
2366
2625
|
});
|
|
2367
2626
|
}
|
|
2368
2627
|
//#endregion
|
|
2628
|
+
//#region ../common/src/data/resolveDataSource.ts
|
|
2629
|
+
/**
|
|
2630
|
+
* Resolve the effective data source for a collection — the single source of
|
|
2631
|
+
* truth shared by the frontend router, the backend driver registry, and the
|
|
2632
|
+
* editor's capability lookups.
|
|
2633
|
+
*
|
|
2634
|
+
* Resolution order:
|
|
2635
|
+
* 1. The routing **key** is `collection.dataSource`, else
|
|
2636
|
+
* {@link DEFAULT_DATA_SOURCE_KEY}.
|
|
2637
|
+
* 2. If a definition is registered for that key, it provides `engine`,
|
|
2638
|
+
* `transport`, and `databaseId`.
|
|
2639
|
+
* 3. Otherwise values are synthesized: `engine` from `collection.engine`
|
|
2640
|
+
* (or the key, or `"postgres"`), `transport` defaults to `"server"`,
|
|
2641
|
+
* and `databaseId` from the collection.
|
|
2642
|
+
*
|
|
2643
|
+
* `capabilities` are always derived from the resolved `engine`, so two
|
|
2644
|
+
* data sources sharing an engine share capabilities.
|
|
2645
|
+
*
|
|
2646
|
+
* @param collection the collection (or any object carrying the routing fields)
|
|
2647
|
+
* @param registry optional registry of declared data sources
|
|
2648
|
+
*/
|
|
2649
|
+
function resolveDataSource(collection, registry) {
|
|
2650
|
+
const key = collection?.dataSource ?? "(default)";
|
|
2651
|
+
const def = registry?.[key];
|
|
2652
|
+
const engine = def?.engine ?? collection?.engine ?? (key !== "(default)" ? key : "postgres");
|
|
2653
|
+
return {
|
|
2654
|
+
key,
|
|
2655
|
+
engine,
|
|
2656
|
+
transport: def?.transport ?? "server",
|
|
2657
|
+
databaseId: collection?.databaseId ?? def?.databaseId,
|
|
2658
|
+
capabilities: getDataSourceCapabilities(engine)
|
|
2659
|
+
};
|
|
2660
|
+
}
|
|
2661
|
+
//#endregion
|
|
2369
2662
|
//#region ../common/src/collections/CollectionRegistry.ts
|
|
2370
2663
|
var CollectionRegistry = class {
|
|
2664
|
+
/**
|
|
2665
|
+
* Declared data sources, used during normalization to resolve each
|
|
2666
|
+
* collection's engine (so `dataSource`-only collections get the right
|
|
2667
|
+
* capabilities). Empty by default.
|
|
2668
|
+
*/
|
|
2669
|
+
dataSources = {};
|
|
2670
|
+
/**
|
|
2671
|
+
* Global lifecycle callbacks applied to every collection.
|
|
2672
|
+
* Runs on all data paths (REST, WebSocket, `rebase.data`).
|
|
2673
|
+
* Execution order: global → collection → property callbacks.
|
|
2674
|
+
*/
|
|
2675
|
+
_globalCallbacks;
|
|
2676
|
+
/**
|
|
2677
|
+
* Set global lifecycle callbacks that apply to every collection.
|
|
2678
|
+
* Typically called once during backend initialization.
|
|
2679
|
+
*/
|
|
2680
|
+
setGlobalCallbacks(callbacks) {
|
|
2681
|
+
this._globalCallbacks = callbacks;
|
|
2682
|
+
}
|
|
2683
|
+
/**
|
|
2684
|
+
* Get the currently registered global callbacks, if any.
|
|
2685
|
+
*/
|
|
2686
|
+
getGlobalCallbacks() {
|
|
2687
|
+
return this._globalCallbacks;
|
|
2688
|
+
}
|
|
2371
2689
|
collectionsByTableName = /* @__PURE__ */ new Map();
|
|
2372
2690
|
collectionsBySlug = /* @__PURE__ */ new Map();
|
|
2373
2691
|
rootCollections = [];
|
|
@@ -2377,9 +2695,20 @@ var CollectionRegistry = class {
|
|
|
2377
2695
|
rawRootCollections = [];
|
|
2378
2696
|
cachedRawCollectionsList = null;
|
|
2379
2697
|
lastRawInputSnapshot = null;
|
|
2380
|
-
constructor(collections) {
|
|
2698
|
+
constructor(collections, dataSources) {
|
|
2699
|
+
if (dataSources) this.dataSources = dataSources;
|
|
2381
2700
|
if (collections) this.registerMultiple(collections);
|
|
2382
2701
|
}
|
|
2702
|
+
/**
|
|
2703
|
+
* Provide the declared data sources used to resolve each collection's
|
|
2704
|
+
* engine during normalization. Set this before registering collections.
|
|
2705
|
+
* Returns true if the registry changed (callers may re-register).
|
|
2706
|
+
*/
|
|
2707
|
+
setDataSources(dataSources) {
|
|
2708
|
+
if (deepEqual$1(this.dataSources, dataSources)) return false;
|
|
2709
|
+
this.dataSources = dataSources ?? {};
|
|
2710
|
+
return true;
|
|
2711
|
+
}
|
|
2383
2712
|
reset() {
|
|
2384
2713
|
this.collectionsByTableName.clear();
|
|
2385
2714
|
this.collectionsBySlug.clear();
|
|
@@ -2448,9 +2777,14 @@ var CollectionRegistry = class {
|
|
|
2448
2777
|
}
|
|
2449
2778
|
normalizeCollection(collection) {
|
|
2450
2779
|
const result = { ...collection };
|
|
2780
|
+
{
|
|
2781
|
+
const resolved = resolveDataSource(result, this.dataSources);
|
|
2782
|
+
if (!result.dataSource) result.dataSource = resolved.key;
|
|
2783
|
+
if (!result.engine) result.engine = resolved.engine;
|
|
2784
|
+
}
|
|
2451
2785
|
const extractedRelations = this.extractRelationsFromProperties(result.properties);
|
|
2452
2786
|
const relResult = result;
|
|
2453
|
-
const manualRelations = getDataSourceCapabilities(result.
|
|
2787
|
+
const manualRelations = getDataSourceCapabilities(result.engine).supportsRelations ? relResult.relations ?? [] : [];
|
|
2454
2788
|
const mergedRelationsRaw = [...extractedRelations];
|
|
2455
2789
|
for (const manual of manualRelations) {
|
|
2456
2790
|
const name = manual.relationName;
|
|
@@ -2465,7 +2799,7 @@ var CollectionRegistry = class {
|
|
|
2465
2799
|
}
|
|
2466
2800
|
}
|
|
2467
2801
|
let mergedRelations = mergedRelationsRaw;
|
|
2468
|
-
if (getDataSourceCapabilities(result.
|
|
2802
|
+
if (getDataSourceCapabilities(result.engine).supportsRelations) {
|
|
2469
2803
|
mergedRelations = mergedRelationsRaw.map((r) => {
|
|
2470
2804
|
try {
|
|
2471
2805
|
return sanitizeRelation(r, result, (slug) => this.get(slug));
|
|
@@ -2477,8 +2811,10 @@ var CollectionRegistry = class {
|
|
|
2477
2811
|
}
|
|
2478
2812
|
result.properties = this.normalizeProperties(result.properties, mergedRelations);
|
|
2479
2813
|
if (!result.childCollections) {
|
|
2480
|
-
|
|
2481
|
-
|
|
2814
|
+
const capabilities = getDataSourceCapabilities(result.engine);
|
|
2815
|
+
const declaredSubcollections = getDeclaredSubcollections(result);
|
|
2816
|
+
if (capabilities.supportsSubcollections && declaredSubcollections) result.childCollections = declaredSubcollections;
|
|
2817
|
+
else if (capabilities.supportsRelations && relResult.relations) {
|
|
2482
2818
|
const manyRelations = relResult.relations.filter((r) => r.cardinality === "many");
|
|
2483
2819
|
if (manyRelations.length > 0) result.childCollections = () => manyRelations.map((r) => {
|
|
2484
2820
|
const target = r.target();
|
|
@@ -2580,7 +2916,7 @@ var CollectionRegistry = class {
|
|
|
2580
2916
|
if (!currentCollection) throw new Error(`Root collection not found: ${rootCollectionPath}`);
|
|
2581
2917
|
for (let i = 2; i < pathSegments.length; i += 2) {
|
|
2582
2918
|
const relationKey = pathSegments[i];
|
|
2583
|
-
if (!getDataSourceCapabilities(currentCollection.
|
|
2919
|
+
if (!getDataSourceCapabilities(currentCollection.engine).supportsRelations) throw new Error(`Relation path navigation requires a collection that supports relations, but '${currentCollection.slug}' uses engine '${currentCollection.engine}'`);
|
|
2584
2920
|
const relation = findRelation(resolveCollectionRelations(currentCollection), relationKey);
|
|
2585
2921
|
if (!relation) throw new Error(`Relation '${relationKey}' not found in collection '${currentCollection.slug}'`);
|
|
2586
2922
|
const target = relation.target();
|
|
@@ -2664,8 +3000,8 @@ var QueryBuilder = class {
|
|
|
2664
3000
|
* @example
|
|
2665
3001
|
* client.collection('users').orderBy('createdAt', 'desc').find()
|
|
2666
3002
|
*/
|
|
2667
|
-
orderBy(column,
|
|
2668
|
-
this.params.orderBy = `${column}:${
|
|
3003
|
+
orderBy(column, direction = "asc") {
|
|
3004
|
+
this.params.orderBy = `${column}:${direction}`;
|
|
2669
3005
|
return this;
|
|
2670
3006
|
}
|
|
2671
3007
|
/**
|
|
@@ -2720,80 +3056,83 @@ var QueryBuilder = class {
|
|
|
2720
3056
|
}
|
|
2721
3057
|
};
|
|
2722
3058
|
//#endregion
|
|
2723
|
-
//#region ../common/src/data/
|
|
3059
|
+
//#region ../common/src/data/filter-dialect.ts
|
|
2724
3060
|
/**
|
|
2725
|
-
*
|
|
3061
|
+
* REST wire-format adapter for the unified filter system.
|
|
2726
3062
|
*
|
|
2727
|
-
*
|
|
2728
|
-
* -
|
|
2729
|
-
*
|
|
2730
|
-
* - Tuple syntax: { age: [">=", 18], role: ["in", ["admin", "editor"]] }
|
|
3063
|
+
* This module is the ONLY code in the entire codebase that knows about
|
|
3064
|
+
* PostgREST-style dot-syntax strings (`eq.active`, `gt.18`, `in.(a,b)`).
|
|
3065
|
+
* Everything else speaks `FilterValues` exclusively.
|
|
2731
3066
|
*
|
|
2732
|
-
*
|
|
3067
|
+
* @module
|
|
2733
3068
|
*/
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
|
|
2754
|
-
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
const
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
3069
|
+
/**
|
|
3070
|
+
* Coerce a raw querystring value to its natural JS type.
|
|
3071
|
+
* - `"true"` / `"false"` → boolean
|
|
3072
|
+
* - `"null"` → null
|
|
3073
|
+
* - Numeric strings → number
|
|
3074
|
+
* - Everything else → string (unchanged)
|
|
3075
|
+
*/
|
|
3076
|
+
function coerceValue(raw) {
|
|
3077
|
+
if (raw === "true") return true;
|
|
3078
|
+
if (raw === "false") return false;
|
|
3079
|
+
if (raw === "null") return null;
|
|
3080
|
+
if (raw !== "" && !isNaN(Number(raw))) return Number(raw);
|
|
3081
|
+
return raw;
|
|
3082
|
+
}
|
|
3083
|
+
/**
|
|
3084
|
+
* Parse a single PostgREST dot-string into a `[WhereFilterOp, unknown]` tuple.
|
|
3085
|
+
*
|
|
3086
|
+
* If the string doesn't match a known operator prefix, falls back to
|
|
3087
|
+
* `["==", originalString]` (treating the whole string as an equality value).
|
|
3088
|
+
*/
|
|
3089
|
+
function deserializeSingle(raw) {
|
|
3090
|
+
const dotIndex = raw.indexOf(".");
|
|
3091
|
+
if (dotIndex === -1) return ["==", coerceValue(raw)];
|
|
3092
|
+
const prefix = raw.substring(0, dotIndex);
|
|
3093
|
+
const rest = raw.substring(dotIndex + 1);
|
|
3094
|
+
const canonicalOp = REST_TO_CANONICAL[prefix];
|
|
3095
|
+
if (!canonicalOp) return ["==", raw];
|
|
3096
|
+
if (rest.startsWith("(") && rest.endsWith(")")) return [canonicalOp, rest.slice(1, -1).split(",").map((s) => coerceValue(s.trim()))];
|
|
3097
|
+
return [canonicalOp, coerceValue(rest)];
|
|
3098
|
+
}
|
|
3099
|
+
/**
|
|
3100
|
+
* Convert a PostgREST-style querystring record to `FilterValues`.
|
|
3101
|
+
*
|
|
3102
|
+
* - String values are parsed as single conditions.
|
|
3103
|
+
* - String arrays (repeated query params) become multiple conditions on the same field.
|
|
3104
|
+
*
|
|
3105
|
+
* @example
|
|
3106
|
+
* deserializeFilter({ status: "eq.active" })
|
|
3107
|
+
* // → { status: ["==", "active"] }
|
|
3108
|
+
*
|
|
3109
|
+
* deserializeFilter({ age: ["gte.18", "lt.65"] })
|
|
3110
|
+
* // → { age: [[">=", 18], ["<", 65]] }
|
|
3111
|
+
*/
|
|
3112
|
+
function deserializeFilter(query) {
|
|
3113
|
+
const result = {};
|
|
3114
|
+
for (const [field, raw] of Object.entries(query)) {
|
|
3115
|
+
if (raw === void 0) continue;
|
|
3116
|
+
if (Array.isArray(raw) && raw.length === 2 && typeof raw[0] === "string" && toCanonicalOp(raw[0]) === raw[0]) {
|
|
3117
|
+
result[field] = raw;
|
|
2776
3118
|
continue;
|
|
2777
3119
|
}
|
|
2778
|
-
if (
|
|
2779
|
-
|
|
2780
|
-
if (
|
|
2781
|
-
|
|
3120
|
+
if (Array.isArray(raw)) {
|
|
3121
|
+
if (raw.length === 0) continue;
|
|
3122
|
+
if (Array.isArray(raw[0]) && raw[0].length === 2 && typeof raw[0][0] === "string" && toCanonicalOp(raw[0][0]) === raw[0][0]) {
|
|
3123
|
+
result[field] = raw;
|
|
2782
3124
|
continue;
|
|
2783
3125
|
}
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
else if (value === "false") value = false;
|
|
2790
|
-
else if (typeof value === "string" && !isNaN(Number(value)) && value.trim() !== "") value = Number(value);
|
|
2791
|
-
const mappedOp = operatorMap[op];
|
|
2792
|
-
if (mappedOp) filter[field] = [mappedOp, value];
|
|
2793
|
-
}
|
|
3126
|
+
if (raw.length === 1) result[field] = typeof raw[0] === "string" ? deserializeSingle(raw[0]) : ["==", raw[0]];
|
|
3127
|
+
else if (typeof raw[0] === "string" && raw[0].includes(".")) result[field] = raw.map((r) => typeof r === "string" ? deserializeSingle(r) : ["==", r]);
|
|
3128
|
+
else result[field] = ["in", raw];
|
|
3129
|
+
} else if (typeof raw === "string") result[field] = deserializeSingle(raw);
|
|
3130
|
+
else result[field] = ["==", raw];
|
|
2794
3131
|
}
|
|
2795
|
-
return
|
|
3132
|
+
return result;
|
|
2796
3133
|
}
|
|
3134
|
+
//#endregion
|
|
3135
|
+
//#region ../common/src/data/buildRebaseData.ts
|
|
2797
3136
|
/**
|
|
2798
3137
|
* Parse an orderBy string like "created_at:desc" into [field, direction].
|
|
2799
3138
|
*/
|
|
@@ -2806,11 +3145,12 @@ function createDriverAccessor(driver, slug) {
|
|
|
2806
3145
|
const accessor = {
|
|
2807
3146
|
async find(params) {
|
|
2808
3147
|
const orderParsed = parseOrderBy(params?.orderBy);
|
|
3148
|
+
const filter = params?.where ? deserializeFilter(params.where) : void 0;
|
|
2809
3149
|
const entities = await driver.fetchCollection({
|
|
2810
3150
|
path: slug,
|
|
2811
3151
|
limit: params?.limit,
|
|
2812
3152
|
offset: params?.offset,
|
|
2813
|
-
filter
|
|
3153
|
+
filter,
|
|
2814
3154
|
orderBy: orderParsed?.[0],
|
|
2815
3155
|
order: orderParsed?.[1],
|
|
2816
3156
|
searchString: params?.searchString
|
|
@@ -2860,9 +3200,10 @@ function createDriverAccessor(driver, slug) {
|
|
|
2860
3200
|
return driver.deleteAll(slug);
|
|
2861
3201
|
} : void 0,
|
|
2862
3202
|
count: driver.countEntities ? async (params) => {
|
|
3203
|
+
const filter = params?.where ? deserializeFilter(params.where) : void 0;
|
|
2863
3204
|
return driver.countEntities({
|
|
2864
3205
|
path: slug,
|
|
2865
|
-
filter
|
|
3206
|
+
filter
|
|
2866
3207
|
});
|
|
2867
3208
|
} : void 0,
|
|
2868
3209
|
listen: driver.listenCollection ? (params, onUpdate, onError) => {
|
|
@@ -2873,7 +3214,7 @@ function createDriverAccessor(driver, slug) {
|
|
|
2873
3214
|
path: slug,
|
|
2874
3215
|
limit: params?.limit,
|
|
2875
3216
|
offset: params?.offset,
|
|
2876
|
-
filter:
|
|
3217
|
+
filter: params?.where,
|
|
2877
3218
|
orderBy: orderParsed?.[0],
|
|
2878
3219
|
order: orderParsed?.[1],
|
|
2879
3220
|
searchString: params?.searchString,
|
|
@@ -2952,6 +3293,72 @@ function buildRebaseData(driver) {
|
|
|
2952
3293
|
} });
|
|
2953
3294
|
}
|
|
2954
3295
|
//#endregion
|
|
3296
|
+
//#region ../common/src/table-classification.ts
|
|
3297
|
+
/** Schemas that are always considered Rebase-internal. */
|
|
3298
|
+
var REBASE_INTERNAL_SCHEMAS = ["rebase", "auth"];
|
|
3299
|
+
/** Table-name prefixes that mark a table as Rebase-internal regardless of schema. */
|
|
3300
|
+
var REBASE_INTERNAL_PREFIXES = [
|
|
3301
|
+
"_rebase_",
|
|
3302
|
+
"_auth_",
|
|
3303
|
+
"drizzle_"
|
|
3304
|
+
];
|
|
3305
|
+
/**
|
|
3306
|
+
* Synchronously classify a table based on naming conventions.
|
|
3307
|
+
*
|
|
3308
|
+
* @param tableName - The unqualified name of the table.
|
|
3309
|
+
* @param schemaName - The schema the table belongs to (e.g. `"public"`, `"rebase"`).
|
|
3310
|
+
* @returns `"rebase-internal"` when the table belongs to a reserved schema or
|
|
3311
|
+
* carries a reserved prefix; `"user"` otherwise.
|
|
3312
|
+
*
|
|
3313
|
+
* @remarks
|
|
3314
|
+
* Junction-table detection requires an async database query and is therefore
|
|
3315
|
+
* **not** handled by this function. Use {@link detectJunctionTables} to obtain
|
|
3316
|
+
* the set of junction tables, then reclassify as needed.
|
|
3317
|
+
*/
|
|
3318
|
+
function classifyTable(tableName, schemaName) {
|
|
3319
|
+
if (REBASE_INTERNAL_SCHEMAS.includes(schemaName) || REBASE_INTERNAL_PREFIXES.some((prefix) => tableName.startsWith(prefix))) return "rebase-internal";
|
|
3320
|
+
return "user";
|
|
3321
|
+
}
|
|
3322
|
+
/** SQL query that detects junction tables in the `public` schema. */
|
|
3323
|
+
var JUNCTION_TABLES_SQL = `
|
|
3324
|
+
SELECT t.table_name
|
|
3325
|
+
FROM information_schema.tables t
|
|
3326
|
+
WHERE t.table_schema = 'public'
|
|
3327
|
+
AND t.table_type = 'BASE TABLE'
|
|
3328
|
+
AND NOT EXISTS (
|
|
3329
|
+
SELECT 1
|
|
3330
|
+
FROM information_schema.columns c
|
|
3331
|
+
WHERE c.table_schema = t.table_schema
|
|
3332
|
+
AND c.table_name = t.table_name
|
|
3333
|
+
AND c.column_name NOT IN (
|
|
3334
|
+
SELECT kcu.column_name
|
|
3335
|
+
FROM information_schema.key_column_usage kcu
|
|
3336
|
+
JOIN information_schema.table_constraints tc
|
|
3337
|
+
ON tc.constraint_name = kcu.constraint_name
|
|
3338
|
+
AND tc.table_schema = kcu.table_schema
|
|
3339
|
+
WHERE tc.constraint_type = 'FOREIGN KEY'
|
|
3340
|
+
AND kcu.table_schema = t.table_schema
|
|
3341
|
+
AND kcu.table_name = t.table_name
|
|
3342
|
+
)
|
|
3343
|
+
)
|
|
3344
|
+
`;
|
|
3345
|
+
/**
|
|
3346
|
+
* Asynchronously detect junction (link) tables in the `public` schema.
|
|
3347
|
+
*
|
|
3348
|
+
* A junction table is defined as a table where **every** column participates in
|
|
3349
|
+
* at least one foreign-key constraint.
|
|
3350
|
+
*
|
|
3351
|
+
* @param executeSql - A callback that executes a raw SQL string and returns the
|
|
3352
|
+
* resulting rows.
|
|
3353
|
+
* @returns A `Set` containing the names of all detected junction tables.
|
|
3354
|
+
*/
|
|
3355
|
+
async function detectJunctionTables(executeSql) {
|
|
3356
|
+
const rows = await executeSql(JUNCTION_TABLES_SQL);
|
|
3357
|
+
const junctionTables = /* @__PURE__ */ new Set();
|
|
3358
|
+
for (const row of rows) if (typeof row.table_name === "string") junctionTables.add(row.table_name);
|
|
3359
|
+
return junctionTables;
|
|
3360
|
+
}
|
|
3361
|
+
//#endregion
|
|
2955
3362
|
//#region src/services/entity-helpers.ts
|
|
2956
3363
|
/** Safely extract Drizzle column metadata from a column object. */
|
|
2957
3364
|
function getColumnMeta(col) {
|
|
@@ -5700,6 +6107,21 @@ function extractCauseMessage(error) {
|
|
|
5700
6107
|
return null;
|
|
5701
6108
|
}
|
|
5702
6109
|
/**
|
|
6110
|
+
* Detect whether an error is specifically a role-switching permission failure
|
|
6111
|
+
* (e.g. "permission denied to set role" or "must be member of role"),
|
|
6112
|
+
* as opposed to a table-level permission denial.
|
|
6113
|
+
*
|
|
6114
|
+
* This is used by the backend driver to auto-disable role switching when the
|
|
6115
|
+
* connection user lacks SET ROLE privileges, rather than surfacing a confusing
|
|
6116
|
+
* error to the Studio SQL Editor user.
|
|
6117
|
+
*/
|
|
6118
|
+
function isRoleSwitchingPermissionError(error) {
|
|
6119
|
+
const pgError = extractPgError(error);
|
|
6120
|
+
if (!pgError || pgError.code !== "42501") return false;
|
|
6121
|
+
const msg = pgError.message.toLowerCase();
|
|
6122
|
+
return msg.includes("set role") || msg.includes("member of role");
|
|
6123
|
+
}
|
|
6124
|
+
/**
|
|
5703
6125
|
* Translate a raw PostgreSQL error into a user-friendly message.
|
|
5704
6126
|
*
|
|
5705
6127
|
* @param pgError - The extracted PostgreSQL error (from {@link extractPgError})
|
|
@@ -5835,11 +6257,14 @@ var EntityPersistService = class {
|
|
|
5835
6257
|
const collection = getCollectionByPath(collectionPath, this.registry);
|
|
5836
6258
|
const table = getTableForCollection(collection, this.registry);
|
|
5837
6259
|
const idInfoArray = getPrimaryKeys(collection, this.registry);
|
|
5838
|
-
const
|
|
5839
|
-
const
|
|
5840
|
-
|
|
5841
|
-
|
|
5842
|
-
|
|
6260
|
+
const parsedIdObj = parseIdValues(entityId, idInfoArray);
|
|
6261
|
+
const conditions = [];
|
|
6262
|
+
for (const info of idInfoArray) {
|
|
6263
|
+
const field = table[info.fieldName];
|
|
6264
|
+
if (!field) throw new Error(`ID field '${info.fieldName}' not found in table for collection '${collectionPath}'`);
|
|
6265
|
+
conditions.push(eq(field, parsedIdObj[info.fieldName]));
|
|
6266
|
+
}
|
|
6267
|
+
await this.db.delete(table).where(and(...conditions));
|
|
5843
6268
|
}
|
|
5844
6269
|
/**
|
|
5845
6270
|
* Delete all entities from a collection
|
|
@@ -6082,10 +6507,23 @@ var EntityService = class {
|
|
|
6082
6507
|
/**
|
|
6083
6508
|
* Execute raw SQL
|
|
6084
6509
|
*/
|
|
6085
|
-
async executeSql(sqlText) {
|
|
6086
|
-
if (process.env.NODE_ENV !== "production") console.debug("Executing raw SQL:", sqlText);
|
|
6510
|
+
async executeSql(sqlText, params) {
|
|
6511
|
+
if (process.env.NODE_ENV !== "production") console.debug("Executing raw SQL:", sqlText, params?.length ? `with ${params.length} params` : "");
|
|
6087
6512
|
const { sql } = await import("drizzle-orm");
|
|
6088
|
-
|
|
6513
|
+
let result;
|
|
6514
|
+
if (params && params.length > 0) {
|
|
6515
|
+
const parts = sqlText.split(/\$(\d+)/);
|
|
6516
|
+
const chunks = [];
|
|
6517
|
+
for (let i = 0; i < parts.length; i++) if (i % 2 === 0) {
|
|
6518
|
+
if (parts[i].length > 0) chunks.push(sql.raw(parts[i]));
|
|
6519
|
+
} else {
|
|
6520
|
+
const paramIndex = Number(parts[i]) - 1;
|
|
6521
|
+
chunks.push(sql.param(params[paramIndex]));
|
|
6522
|
+
}
|
|
6523
|
+
const query = sql.join(chunks, sql.raw(""));
|
|
6524
|
+
result = await this.db.execute(query);
|
|
6525
|
+
} else result = await this.db.execute(sql.raw(sqlText));
|
|
6526
|
+
const rows = result.rows;
|
|
6089
6527
|
if (process.env.NODE_ENV !== "production") console.debug(`SQL executed successfully. Returned ${Array.isArray(rows) ? rows.length : "non-array"} rows.`);
|
|
6090
6528
|
return rows;
|
|
6091
6529
|
}
|
|
@@ -6287,6 +6725,13 @@ var PostgresBackendDriver = class {
|
|
|
6287
6725
|
data;
|
|
6288
6726
|
client;
|
|
6289
6727
|
/**
|
|
6728
|
+
* Auto-set to `true` when a SET LOCAL ROLE fails with insufficient
|
|
6729
|
+
* privileges, so subsequent queries skip the doomed attempt.
|
|
6730
|
+
* Mirrors the static `DISABLE_DB_ROLE_SWITCHING` env var but is
|
|
6731
|
+
* learned at runtime.
|
|
6732
|
+
*/
|
|
6733
|
+
_roleSwitchingDisabled = false;
|
|
6734
|
+
/**
|
|
6290
6735
|
* When true, realtime notifications are deferred until after the
|
|
6291
6736
|
* wrapping transaction commits. Set by `withAuth` → `withTransaction`.
|
|
6292
6737
|
*/
|
|
@@ -6345,6 +6790,7 @@ var PostgresBackendDriver = class {
|
|
|
6345
6790
|
if (!collection && !path) return {
|
|
6346
6791
|
collection: void 0,
|
|
6347
6792
|
callbacks: void 0,
|
|
6793
|
+
globalCallbacks: void 0,
|
|
6348
6794
|
propertyCallbacks: void 0
|
|
6349
6795
|
};
|
|
6350
6796
|
const registryCollection = this.registry?.getCollectionByPath(path);
|
|
@@ -6353,12 +6799,14 @@ var PostgresBackendDriver = class {
|
|
|
6353
6799
|
...registryCollection
|
|
6354
6800
|
} : collection;
|
|
6355
6801
|
const callbacks = resolvedCollection?.callbacks;
|
|
6802
|
+
const globalCallbacks = this.registry?.getGlobalCallbacks();
|
|
6356
6803
|
const properties = resolvedCollection?.properties;
|
|
6357
6804
|
let propertyCallbacks;
|
|
6358
6805
|
if (properties) propertyCallbacks = buildPropertyCallbacks(properties);
|
|
6359
6806
|
return {
|
|
6360
6807
|
collection: resolvedCollection,
|
|
6361
6808
|
callbacks,
|
|
6809
|
+
globalCallbacks,
|
|
6362
6810
|
propertyCallbacks
|
|
6363
6811
|
};
|
|
6364
6812
|
}
|
|
@@ -6374,11 +6822,17 @@ var PostgresBackendDriver = class {
|
|
|
6374
6822
|
searchString,
|
|
6375
6823
|
vectorSearch
|
|
6376
6824
|
});
|
|
6377
|
-
const { collection: resolvedCollection, callbacks, propertyCallbacks } = this.resolveCollectionCallbacks(collection, path);
|
|
6378
|
-
if (callbacks?.afterRead || propertyCallbacks?.afterRead) {
|
|
6825
|
+
const { collection: resolvedCollection, callbacks, globalCallbacks, propertyCallbacks } = this.resolveCollectionCallbacks(collection, path);
|
|
6826
|
+
if (globalCallbacks?.afterRead || callbacks?.afterRead || propertyCallbacks?.afterRead) {
|
|
6379
6827
|
const contextForCallback = this.buildCallContext();
|
|
6380
6828
|
return Promise.all(entities.map(async (entity) => {
|
|
6381
6829
|
let fetched = entity;
|
|
6830
|
+
if (globalCallbacks?.afterRead) fetched = await globalCallbacks.afterRead({
|
|
6831
|
+
collection: resolvedCollection,
|
|
6832
|
+
path,
|
|
6833
|
+
entity: fetched,
|
|
6834
|
+
context: contextForCallback
|
|
6835
|
+
});
|
|
6382
6836
|
if (callbacks?.afterRead) fetched = await callbacks.afterRead({
|
|
6383
6837
|
collection: resolvedCollection,
|
|
6384
6838
|
path,
|
|
@@ -6390,7 +6844,7 @@ var PostgresBackendDriver = class {
|
|
|
6390
6844
|
path,
|
|
6391
6845
|
entity: fetched,
|
|
6392
6846
|
context: contextForCallback
|
|
6393
|
-
})
|
|
6847
|
+
});
|
|
6394
6848
|
return fetched;
|
|
6395
6849
|
}));
|
|
6396
6850
|
}
|
|
@@ -6439,21 +6893,27 @@ var PostgresBackendDriver = class {
|
|
|
6439
6893
|
}
|
|
6440
6894
|
async fetchEntity({ path, entityId, databaseId, collection }) {
|
|
6441
6895
|
let entity = await this.entityService.fetchEntity(path, entityId, databaseId || collection?.databaseId);
|
|
6442
|
-
const { collection: resolvedCollection, callbacks, propertyCallbacks } = this.resolveCollectionCallbacks(collection, path);
|
|
6443
|
-
if (entity && (callbacks?.afterRead || propertyCallbacks?.afterRead)) {
|
|
6896
|
+
const { collection: resolvedCollection, callbacks, globalCallbacks, propertyCallbacks } = this.resolveCollectionCallbacks(collection, path);
|
|
6897
|
+
if (entity && (globalCallbacks?.afterRead || callbacks?.afterRead || propertyCallbacks?.afterRead)) {
|
|
6444
6898
|
const contextForCallback = this.buildCallContext();
|
|
6899
|
+
if (globalCallbacks?.afterRead) entity = await globalCallbacks.afterRead({
|
|
6900
|
+
collection: resolvedCollection,
|
|
6901
|
+
path,
|
|
6902
|
+
entity,
|
|
6903
|
+
context: contextForCallback
|
|
6904
|
+
});
|
|
6445
6905
|
if (callbacks?.afterRead) entity = await callbacks.afterRead({
|
|
6446
6906
|
collection: resolvedCollection,
|
|
6447
6907
|
path,
|
|
6448
6908
|
entity,
|
|
6449
6909
|
context: contextForCallback
|
|
6450
|
-
})
|
|
6910
|
+
});
|
|
6451
6911
|
if (propertyCallbacks?.afterRead) entity = await propertyCallbacks.afterRead({
|
|
6452
6912
|
collection: resolvedCollection,
|
|
6453
6913
|
path,
|
|
6454
6914
|
entity,
|
|
6455
6915
|
context: contextForCallback
|
|
6456
|
-
})
|
|
6916
|
+
});
|
|
6457
6917
|
}
|
|
6458
6918
|
return entity;
|
|
6459
6919
|
}
|
|
@@ -6484,7 +6944,7 @@ var PostgresBackendDriver = class {
|
|
|
6484
6944
|
};
|
|
6485
6945
|
}
|
|
6486
6946
|
async saveEntity({ path, entityId, values, collection, status }) {
|
|
6487
|
-
const { collection: resolvedCollection, callbacks, propertyCallbacks } = this.resolveCollectionCallbacks(collection, path);
|
|
6947
|
+
const { collection: resolvedCollection, callbacks, globalCallbacks, propertyCallbacks } = this.resolveCollectionCallbacks(collection, path);
|
|
6488
6948
|
let updatedValues = values;
|
|
6489
6949
|
const contextForCallback = this.buildCallContext();
|
|
6490
6950
|
let previousValuesForHistory;
|
|
@@ -6492,7 +6952,19 @@ var PostgresBackendDriver = class {
|
|
|
6492
6952
|
const existing = await this.entityService.fetchEntity(path, entityId, resolvedCollection?.databaseId);
|
|
6493
6953
|
if (existing) previousValuesForHistory = existing.values;
|
|
6494
6954
|
}
|
|
6495
|
-
if (callbacks?.beforeSave || propertyCallbacks?.beforeSave) {
|
|
6955
|
+
if (globalCallbacks?.beforeSave || callbacks?.beforeSave || propertyCallbacks?.beforeSave) {
|
|
6956
|
+
if (globalCallbacks?.beforeSave) {
|
|
6957
|
+
const result = await globalCallbacks.beforeSave({
|
|
6958
|
+
collection: resolvedCollection,
|
|
6959
|
+
path,
|
|
6960
|
+
entityId,
|
|
6961
|
+
values: updatedValues,
|
|
6962
|
+
previousValues: previousValuesForHistory,
|
|
6963
|
+
status,
|
|
6964
|
+
context: contextForCallback
|
|
6965
|
+
});
|
|
6966
|
+
if (result) updatedValues = mergeDeep(updatedValues, result);
|
|
6967
|
+
}
|
|
6496
6968
|
if (callbacks?.beforeSave) {
|
|
6497
6969
|
const result = await callbacks.beforeSave({
|
|
6498
6970
|
collection: resolvedCollection,
|
|
@@ -6526,7 +6998,13 @@ var PostgresBackendDriver = class {
|
|
|
6526
6998
|
});
|
|
6527
6999
|
try {
|
|
6528
7000
|
let savedEntity = await this.entityService.saveEntity(path, updatedValues, entityId, resolvedCollection?.databaseId);
|
|
6529
|
-
if (savedEntity && (callbacks?.afterRead || propertyCallbacks?.afterRead)) {
|
|
7001
|
+
if (savedEntity && (globalCallbacks?.afterRead || callbacks?.afterRead || propertyCallbacks?.afterRead)) {
|
|
7002
|
+
if (globalCallbacks?.afterRead) savedEntity = await globalCallbacks.afterRead({
|
|
7003
|
+
collection: resolvedCollection,
|
|
7004
|
+
path,
|
|
7005
|
+
entity: savedEntity,
|
|
7006
|
+
context: contextForCallback
|
|
7007
|
+
});
|
|
6530
7008
|
if (callbacks?.afterRead) savedEntity = await callbacks.afterRead({
|
|
6531
7009
|
collection: resolvedCollection,
|
|
6532
7010
|
path,
|
|
@@ -6538,9 +7016,18 @@ var PostgresBackendDriver = class {
|
|
|
6538
7016
|
path,
|
|
6539
7017
|
entity: savedEntity,
|
|
6540
7018
|
context: contextForCallback
|
|
6541
|
-
})
|
|
7019
|
+
});
|
|
6542
7020
|
}
|
|
6543
|
-
if (callbacks?.afterSave || propertyCallbacks?.afterSave) {
|
|
7021
|
+
if (globalCallbacks?.afterSave || callbacks?.afterSave || propertyCallbacks?.afterSave) {
|
|
7022
|
+
if (globalCallbacks?.afterSave) await globalCallbacks.afterSave({
|
|
7023
|
+
collection: resolvedCollection,
|
|
7024
|
+
path,
|
|
7025
|
+
entityId: savedEntity.id,
|
|
7026
|
+
values: savedEntity.values,
|
|
7027
|
+
previousValues: previousValuesForHistory,
|
|
7028
|
+
status,
|
|
7029
|
+
context: contextForCallback
|
|
7030
|
+
});
|
|
6544
7031
|
if (callbacks?.afterSave) await callbacks.afterSave({
|
|
6545
7032
|
collection: resolvedCollection,
|
|
6546
7033
|
path,
|
|
@@ -6577,7 +7064,16 @@ var PostgresBackendDriver = class {
|
|
|
6577
7064
|
else await this.realtimeService.notifyEntityUpdate(path, savedEntity.id.toString(), savedEntity, resolvedCollection?.databaseId);
|
|
6578
7065
|
return savedEntity;
|
|
6579
7066
|
} catch (error) {
|
|
6580
|
-
if (callbacks?.afterSaveError || propertyCallbacks?.afterSaveError) {
|
|
7067
|
+
if (globalCallbacks?.afterSaveError || callbacks?.afterSaveError || propertyCallbacks?.afterSaveError) {
|
|
7068
|
+
if (globalCallbacks?.afterSaveError) await globalCallbacks.afterSaveError({
|
|
7069
|
+
collection: resolvedCollection,
|
|
7070
|
+
path,
|
|
7071
|
+
entityId: entityId || "unknown",
|
|
7072
|
+
values: updatedValues,
|
|
7073
|
+
previousValues: void 0,
|
|
7074
|
+
status,
|
|
7075
|
+
context: contextForCallback
|
|
7076
|
+
});
|
|
6581
7077
|
if (callbacks?.afterSaveError) await callbacks.afterSaveError({
|
|
6582
7078
|
collection: resolvedCollection,
|
|
6583
7079
|
path,
|
|
@@ -6601,10 +7097,19 @@ var PostgresBackendDriver = class {
|
|
|
6601
7097
|
}
|
|
6602
7098
|
}
|
|
6603
7099
|
async deleteEntity({ entity, collection }) {
|
|
6604
|
-
const { collection: resolvedCollection, callbacks, propertyCallbacks } = this.resolveCollectionCallbacks(collection, entity.path);
|
|
7100
|
+
const { collection: resolvedCollection, callbacks, globalCallbacks, propertyCallbacks } = this.resolveCollectionCallbacks(collection, entity.path);
|
|
6605
7101
|
const contextForCallback = this.buildCallContext();
|
|
6606
|
-
if (callbacks?.beforeDelete || propertyCallbacks?.beforeDelete) {
|
|
7102
|
+
if (globalCallbacks?.beforeDelete || callbacks?.beforeDelete || propertyCallbacks?.beforeDelete) {
|
|
6607
7103
|
let preventDefault = false;
|
|
7104
|
+
if (globalCallbacks?.beforeDelete) {
|
|
7105
|
+
if (await globalCallbacks.beforeDelete({
|
|
7106
|
+
collection: resolvedCollection,
|
|
7107
|
+
path: entity.path,
|
|
7108
|
+
entityId: entity.id,
|
|
7109
|
+
entity,
|
|
7110
|
+
context: contextForCallback
|
|
7111
|
+
}) === false) preventDefault = true;
|
|
7112
|
+
}
|
|
6608
7113
|
if (callbacks?.beforeDelete) {
|
|
6609
7114
|
if (await callbacks.beforeDelete({
|
|
6610
7115
|
collection: resolvedCollection,
|
|
@@ -6626,7 +7131,14 @@ var PostgresBackendDriver = class {
|
|
|
6626
7131
|
if (preventDefault) return;
|
|
6627
7132
|
}
|
|
6628
7133
|
await this.entityService.deleteEntity(entity.path, entity.id, entity.databaseId || resolvedCollection?.databaseId);
|
|
6629
|
-
if (callbacks?.afterDelete || propertyCallbacks?.afterDelete) {
|
|
7134
|
+
if (globalCallbacks?.afterDelete || callbacks?.afterDelete || propertyCallbacks?.afterDelete) {
|
|
7135
|
+
if (globalCallbacks?.afterDelete) await globalCallbacks.afterDelete({
|
|
7136
|
+
collection: resolvedCollection,
|
|
7137
|
+
path: entity.path,
|
|
7138
|
+
entityId: entity.id,
|
|
7139
|
+
entity,
|
|
7140
|
+
context: contextForCallback
|
|
7141
|
+
});
|
|
6630
7142
|
if (callbacks?.afterDelete) await callbacks.afterDelete({
|
|
6631
7143
|
collection: resolvedCollection,
|
|
6632
7144
|
path: entity.path,
|
|
@@ -6676,11 +7188,11 @@ var PostgresBackendDriver = class {
|
|
|
6676
7188
|
return this.poolManager.getDrizzle(databaseName);
|
|
6677
7189
|
}
|
|
6678
7190
|
async executeSql(sqlText, options) {
|
|
6679
|
-
if (!options?.database && !options?.role) return this.entityService.executeSql(sqlText);
|
|
7191
|
+
if (!options?.database && !options?.role) return this.entityService.executeSql(sqlText, options?.params);
|
|
6680
7192
|
const targetDb = this.getTargetDb(options?.database);
|
|
6681
7193
|
try {
|
|
6682
7194
|
let needsRoleSwitch = false;
|
|
6683
|
-
if (options?.role && process.env.DISABLE_DB_ROLE_SWITCHING !== "true") try {
|
|
7195
|
+
if (options?.role && process.env.DISABLE_DB_ROLE_SWITCHING !== "true" && !this._roleSwitchingDisabled) try {
|
|
6684
7196
|
const currentRole = ((await targetDb.execute(sql.raw("SELECT current_user AS role"))).rows?.[0])?.role;
|
|
6685
7197
|
needsRoleSwitch = !!currentRole && currentRole !== options.role;
|
|
6686
7198
|
} catch {
|
|
@@ -6688,12 +7200,37 @@ var PostgresBackendDriver = class {
|
|
|
6688
7200
|
}
|
|
6689
7201
|
if (needsRoleSwitch && options?.role) {
|
|
6690
7202
|
const safeRole = options.role.replace(/"/g, "\"\"");
|
|
6691
|
-
|
|
6692
|
-
await
|
|
6693
|
-
|
|
6694
|
-
|
|
7203
|
+
try {
|
|
7204
|
+
return await targetDb.transaction(async (tx) => {
|
|
7205
|
+
await tx.execute(sql.raw(`SET LOCAL ROLE "${safeRole}"`));
|
|
7206
|
+
let result;
|
|
7207
|
+
if (options?.params && options.params.length > 0) {
|
|
7208
|
+
const parts = sqlText.split(/\$(\d+)/);
|
|
7209
|
+
const chunks = [];
|
|
7210
|
+
for (let i = 0; i < parts.length; i++) if (i % 2 === 0) {
|
|
7211
|
+
if (parts[i].length > 0) chunks.push(sql.raw(parts[i]));
|
|
7212
|
+
} else chunks.push(sql.param(options.params[Number(parts[i]) - 1]));
|
|
7213
|
+
result = await tx.execute(sql.join(chunks, sql.raw("")));
|
|
7214
|
+
} else result = await tx.execute(sql.raw(sqlText));
|
|
7215
|
+
return result.rows;
|
|
7216
|
+
});
|
|
7217
|
+
} catch (roleError) {
|
|
7218
|
+
if (isRoleSwitchingPermissionError(roleError)) {
|
|
7219
|
+
logger.warn(`[PostgresBackendDriver] SET LOCAL ROLE "${safeRole}" failed — the connection user lacks permission. Falling back to executing without role switching. To suppress this warning, set DISABLE_DB_ROLE_SWITCHING=true in your .env file.`);
|
|
7220
|
+
this._roleSwitchingDisabled = true;
|
|
7221
|
+
} else throw roleError;
|
|
7222
|
+
}
|
|
6695
7223
|
}
|
|
6696
|
-
|
|
7224
|
+
let result;
|
|
7225
|
+
if (options?.params && options.params.length > 0) {
|
|
7226
|
+
const parts = sqlText.split(/\$(\d+)/);
|
|
7227
|
+
const chunks = [];
|
|
7228
|
+
for (let i = 0; i < parts.length; i++) if (i % 2 === 0) {
|
|
7229
|
+
if (parts[i].length > 0) chunks.push(sql.raw(parts[i]));
|
|
7230
|
+
} else chunks.push(sql.param(options.params[Number(parts[i]) - 1]));
|
|
7231
|
+
result = await targetDb.execute(sql.join(chunks, sql.raw("")));
|
|
7232
|
+
} else result = await targetDb.execute(sql.raw(sqlText));
|
|
7233
|
+
return result.rows;
|
|
6697
7234
|
} catch (error) {
|
|
6698
7235
|
const msg = error instanceof Error ? error.message : String(error);
|
|
6699
7236
|
if (msg.includes("pg_hba.conf") || msg.includes("no encryption") || msg.includes("connection refused")) {
|
|
@@ -6731,53 +7268,16 @@ var PostgresBackendDriver = class {
|
|
|
6731
7268
|
* and junction/connection tables used for many-to-many relations.
|
|
6732
7269
|
*/
|
|
6733
7270
|
async fetchUnmappedTables(mappedPaths) {
|
|
6734
|
-
const
|
|
7271
|
+
const allTables = (await this.executeSql(`
|
|
6735
7272
|
SELECT table_name
|
|
6736
7273
|
FROM information_schema.tables
|
|
6737
7274
|
WHERE table_schema = 'public'
|
|
6738
7275
|
AND table_type = 'BASE TABLE'
|
|
6739
7276
|
ORDER BY table_name;
|
|
6740
|
-
`);
|
|
6741
|
-
const internalPrefixes = ["_rebase_", "_auth_"];
|
|
6742
|
-
const internalExact = [
|
|
6743
|
-
"users",
|
|
6744
|
-
"roles",
|
|
6745
|
-
"user_roles",
|
|
6746
|
-
"refresh_tokens",
|
|
6747
|
-
"password_reset_tokens",
|
|
6748
|
-
"email_verification_tokens"
|
|
6749
|
-
];
|
|
6750
|
-
const allTables = result.map((r) => r.table_name).filter((name) => {
|
|
6751
|
-
if (internalPrefixes.some((prefix) => name.startsWith(prefix))) return false;
|
|
6752
|
-
if (internalExact.includes(name)) return false;
|
|
6753
|
-
return true;
|
|
6754
|
-
});
|
|
7277
|
+
`)).map((r) => r.table_name).filter((name) => classifyTable(name, "public") !== "rebase-internal");
|
|
6755
7278
|
let junctionTables = /* @__PURE__ */ new Set();
|
|
6756
7279
|
try {
|
|
6757
|
-
|
|
6758
|
-
SELECT t.table_name
|
|
6759
|
-
FROM information_schema.tables t
|
|
6760
|
-
WHERE t.table_schema = 'public'
|
|
6761
|
-
AND t.table_type = 'BASE TABLE'
|
|
6762
|
-
AND NOT EXISTS (
|
|
6763
|
-
-- Find columns that are NOT part of any foreign key
|
|
6764
|
-
SELECT 1
|
|
6765
|
-
FROM information_schema.columns c
|
|
6766
|
-
WHERE c.table_schema = t.table_schema
|
|
6767
|
-
AND c.table_name = t.table_name
|
|
6768
|
-
AND c.column_name NOT IN (
|
|
6769
|
-
SELECT kcu.column_name
|
|
6770
|
-
FROM information_schema.key_column_usage kcu
|
|
6771
|
-
JOIN information_schema.table_constraints tc
|
|
6772
|
-
ON tc.constraint_name = kcu.constraint_name
|
|
6773
|
-
AND tc.table_schema = kcu.table_schema
|
|
6774
|
-
WHERE tc.constraint_type = 'FOREIGN KEY'
|
|
6775
|
-
AND kcu.table_schema = t.table_schema
|
|
6776
|
-
AND kcu.table_name = t.table_name
|
|
6777
|
-
)
|
|
6778
|
-
);
|
|
6779
|
-
`);
|
|
6780
|
-
junctionTables = new Set(junctionResult.map((r) => r.table_name));
|
|
7280
|
+
junctionTables = await detectJunctionTables(this.executeSql.bind(this));
|
|
6781
7281
|
} catch (e) {
|
|
6782
7282
|
logger.warn("Could not detect junction tables", { error: e });
|
|
6783
7283
|
}
|
|
@@ -7146,6 +7646,14 @@ function createAuthSchema(usersSchemaName = "rebase") {
|
|
|
7146
7646
|
codeHash: varchar("code_hash", { length: 255 }).notNull(),
|
|
7147
7647
|
usedAt: timestamp("used_at"),
|
|
7148
7648
|
createdAt: timestamp("created_at").defaultNow().notNull()
|
|
7649
|
+
}),
|
|
7650
|
+
magicLinkTokens: tableCreator("magic_link_tokens", {
|
|
7651
|
+
id: uuid("id").defaultRandom().primaryKey(),
|
|
7652
|
+
userId: uuid("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
|
|
7653
|
+
tokenHash: varchar("token_hash", { length: 255 }).notNull().unique(),
|
|
7654
|
+
expiresAt: timestamp("expires_at").notNull(),
|
|
7655
|
+
usedAt: timestamp("used_at"),
|
|
7656
|
+
createdAt: timestamp("created_at").defaultNow().notNull()
|
|
7149
7657
|
})
|
|
7150
7658
|
};
|
|
7151
7659
|
}
|
|
@@ -7159,12 +7667,14 @@ var userIdentities = defaultAuthSchema.userIdentities;
|
|
|
7159
7667
|
var mfaFactors = defaultAuthSchema.mfaFactors;
|
|
7160
7668
|
var mfaChallenges = defaultAuthSchema.mfaChallenges;
|
|
7161
7669
|
var recoveryCodes = defaultAuthSchema.recoveryCodes;
|
|
7670
|
+
var magicLinkTokens = defaultAuthSchema.magicLinkTokens;
|
|
7162
7671
|
var usersRelations = relations(users, ({ many }) => ({
|
|
7163
7672
|
refreshTokens: many(refreshTokens),
|
|
7164
7673
|
passwordResetTokens: many(passwordResetTokens),
|
|
7165
7674
|
userIdentities: many(userIdentities),
|
|
7166
7675
|
mfaFactors: many(mfaFactors),
|
|
7167
|
-
recoveryCodes: many(recoveryCodes)
|
|
7676
|
+
recoveryCodes: many(recoveryCodes),
|
|
7677
|
+
magicLinkTokens: many(magicLinkTokens)
|
|
7168
7678
|
}));
|
|
7169
7679
|
var refreshTokensRelations = relations(refreshTokens, ({ one }) => ({ user: one(users, {
|
|
7170
7680
|
fields: [refreshTokens.userId],
|
|
@@ -7193,6 +7703,93 @@ var recoveryCodesRelations = relations(recoveryCodes, ({ one }) => ({ user: one(
|
|
|
7193
7703
|
fields: [recoveryCodes.userId],
|
|
7194
7704
|
references: [users.id]
|
|
7195
7705
|
}) }));
|
|
7706
|
+
var magicLinkTokensRelations = relations(magicLinkTokens, ({ one }) => ({ user: one(users, {
|
|
7707
|
+
fields: [magicLinkTokens.userId],
|
|
7708
|
+
references: [users.id]
|
|
7709
|
+
}) }));
|
|
7710
|
+
//#endregion
|
|
7711
|
+
//#region src/schema/auth-default-policies.ts
|
|
7712
|
+
/**
|
|
7713
|
+
* Default RLS for authentication collections.
|
|
7714
|
+
*
|
|
7715
|
+
* Users are persisted through the same write path as any other collection —
|
|
7716
|
+
* the only thing that distinguishes them is the `auth` flag. That means
|
|
7717
|
+
* privileged columns (most importantly `roles`) would be editable by any
|
|
7718
|
+
* authenticated user unless the collection author remembers to write a correct
|
|
7719
|
+
* admin-only write policy.
|
|
7720
|
+
*
|
|
7721
|
+
* To make this safe by default, the schema generator injects two policies for
|
|
7722
|
+
* every write operation on an auth collection. Enforcement lives in the
|
|
7723
|
+
* database (RLS), so it applies to every write path — REST, WebSocket, and any
|
|
7724
|
+
* internal caller — not just one entry point:
|
|
7725
|
+
*
|
|
7726
|
+
* 1. A **restrictive** admin gate. Restrictive policies are AND'd with every
|
|
7727
|
+
* other policy, so this guarantees a write is rejected unless the caller is
|
|
7728
|
+
* an admin (or the trusted server context) — even if the author also wrote
|
|
7729
|
+
* a permissive rule such as "a user may edit their own row". Without this,
|
|
7730
|
+
* a permissive owner rule would let a user change their own `roles`.
|
|
7731
|
+
*
|
|
7732
|
+
* 2. A **permissive** admin grant. Restrictive policies only constrain; at
|
|
7733
|
+
* least one permissive policy must also pass for the write to be allowed.
|
|
7734
|
+
* This grants the baseline write so admins (and the server context) can
|
|
7735
|
+
* manage users even when the collection has no other permissive write rule.
|
|
7736
|
+
*
|
|
7737
|
+
* Both mirror the framework's trusted-server convention: the write is allowed
|
|
7738
|
+
* when there is no authenticated user context (`auth.uid() IS NULL`, used by
|
|
7739
|
+
* the built-in auth flows that run as the service role) OR the caller holds the
|
|
7740
|
+
* `admin` role. Net effect: only admins (or the server) can write the row, and
|
|
7741
|
+
* therefore only admins can change privileged columns like `roles`.
|
|
7742
|
+
*
|
|
7743
|
+
* Author-provided write rules are preserved but cannot loosen this guarantee —
|
|
7744
|
+
* a non-admin write is always blocked by the restrictive gate. To take full
|
|
7745
|
+
* control of an auth collection's write authorization, set
|
|
7746
|
+
* `disableDefaultAuthPolicies: true` on the collection.
|
|
7747
|
+
*/
|
|
7748
|
+
var ADMIN_WRITE_EXPR = policy.or(policy.not(policy.authenticated()), policy.rolesOverlap(["admin"]));
|
|
7749
|
+
/** Write operations that must be admin-gated by default on auth collections. */
|
|
7750
|
+
var DEFAULT_GUARDED_OPS = [
|
|
7751
|
+
"insert",
|
|
7752
|
+
"update",
|
|
7753
|
+
"delete"
|
|
7754
|
+
];
|
|
7755
|
+
/** Whether a collection is flagged as an authentication collection. */
|
|
7756
|
+
function isAuthCollection(collection) {
|
|
7757
|
+
const auth = collection.auth;
|
|
7758
|
+
return auth === true || typeof auth === "object" && auth?.enabled === true;
|
|
7759
|
+
}
|
|
7760
|
+
/**
|
|
7761
|
+
* Returns the security rules that should be applied to a collection: the
|
|
7762
|
+
* author's explicit `securityRules`, plus, for auth collections, an
|
|
7763
|
+
* auto-injected restrictive admin gate and permissive admin grant on every
|
|
7764
|
+
* write operation. Together these guarantee only admins (or the trusted server
|
|
7765
|
+
* context) can write the row.
|
|
7766
|
+
*
|
|
7767
|
+
* Non-auth collections, and auth collections that opt out via
|
|
7768
|
+
* `disableDefaultAuthPolicies`, are returned unchanged.
|
|
7769
|
+
*/
|
|
7770
|
+
function getEffectiveSecurityRules(collection) {
|
|
7771
|
+
const explicit = (isPostgresCollection(collection) ? collection.securityRules : void 0) ?? [];
|
|
7772
|
+
if (!isAuthCollection(collection) || collection.disableDefaultAuthPolicies) return explicit;
|
|
7773
|
+
const tableName = getTableName$1(collection);
|
|
7774
|
+
const requireAdminGate = {
|
|
7775
|
+
name: `${tableName}_require_admin_write`,
|
|
7776
|
+
mode: "restrictive",
|
|
7777
|
+
operations: [...DEFAULT_GUARDED_OPS],
|
|
7778
|
+
condition: ADMIN_WRITE_EXPR,
|
|
7779
|
+
check: ADMIN_WRITE_EXPR
|
|
7780
|
+
};
|
|
7781
|
+
const allowAdminWrite = {
|
|
7782
|
+
name: `${tableName}_default_admin_write`,
|
|
7783
|
+
operations: [...DEFAULT_GUARDED_OPS],
|
|
7784
|
+
condition: ADMIN_WRITE_EXPR,
|
|
7785
|
+
check: ADMIN_WRITE_EXPR
|
|
7786
|
+
};
|
|
7787
|
+
return [
|
|
7788
|
+
...explicit,
|
|
7789
|
+
allowAdminWrite,
|
|
7790
|
+
requireAdminGate
|
|
7791
|
+
];
|
|
7792
|
+
}
|
|
7196
7793
|
//#endregion
|
|
7197
7794
|
//#region src/schema/generate-drizzle-schema-logic.ts
|
|
7198
7795
|
/**
|
|
@@ -7269,7 +7866,7 @@ var getDrizzleColumn = (propName, prop, collection, collections) => {
|
|
|
7269
7866
|
if (stringProp.enum) columnDefinition = `${getEnumVarName(getTableName$1(collection), propName)}("${colName}")`;
|
|
7270
7867
|
else if ("isId" in stringProp && stringProp.isId === "uuid") columnDefinition = `uuid("${colName}")`;
|
|
7271
7868
|
else if (stringProp.columnType === "uuid") columnDefinition = `uuid("${colName}")`;
|
|
7272
|
-
else if (stringProp.columnType === "text") columnDefinition = `text("${colName}")`;
|
|
7869
|
+
else if (stringProp.columnType === "text" || stringProp.ui?.markdown || stringProp.ui?.multiline) columnDefinition = `text("${colName}")`;
|
|
7273
7870
|
else if (stringProp.columnType === "char") columnDefinition = `char("${colName}")`;
|
|
7274
7871
|
else columnDefinition = `varchar("${colName}")`;
|
|
7275
7872
|
if (isIdProperty(propName, prop, collection)) columnDefinition += ".primaryKey()";
|
|
@@ -7388,47 +7985,9 @@ var getDrizzleColumn = (propName, prop, collection, collections) => {
|
|
|
7388
7985
|
return ` ${propName}: ${columnDefinition}`;
|
|
7389
7986
|
};
|
|
7390
7987
|
/**
|
|
7391
|
-
*
|
|
7392
|
-
* The result is wrapped in a Drizzle sql`` template literal.
|
|
7393
|
-
*/
|
|
7394
|
-
var resolveRawSql = (expression) => {
|
|
7395
|
-
return `sql\`${expression.replace(/\{(\w+)\}/g, (_, col) => col)}\``;
|
|
7396
|
-
};
|
|
7397
|
-
/**
|
|
7398
|
-
* Wraps a SQL clause with a role check using AND.
|
|
7399
|
-
* Generates: `(<clause>) AND (string_to_array(auth.roles(), ',') && ARRAY['<role1>','<role2>'])`
|
|
7400
|
-
*/
|
|
7401
|
-
var wrapWithRoleCheck = (clause, roles) => {
|
|
7402
|
-
const roleCondition = `string_to_array(auth.roles(), ',') && ${`ARRAY[${roles.map((r) => `'${r}'`).join(",")}]`}`;
|
|
7403
|
-
return `sql\`(${unwrapSql(clause)}) AND (${roleCondition})\``;
|
|
7404
|
-
};
|
|
7405
|
-
/**
|
|
7406
|
-
* Extracts the inner expression from a `sql\`...\`` wrapper.
|
|
7407
|
-
*/
|
|
7408
|
-
var unwrapSql = (sqlExpr) => {
|
|
7409
|
-
const match = sqlExpr.match(/^sql`(.*)`$/s);
|
|
7410
|
-
return match ? match[1] : sqlExpr;
|
|
7411
|
-
};
|
|
7412
|
-
/**
|
|
7413
|
-
* Builds the USING clause for a policy based on shortcuts or raw SQL.
|
|
7414
|
-
*/
|
|
7415
|
-
var buildUsingClause = (rule, collection) => {
|
|
7416
|
-
if (rule.using) return resolveRawSql(rule.using);
|
|
7417
|
-
if (rule.access === "public") return "sql`true`";
|
|
7418
|
-
if (rule.ownerField) {
|
|
7419
|
-
const prop = collection.properties?.[rule.ownerField];
|
|
7420
|
-
return `sql\`${resolveColumnName(rule.ownerField, prop)} = auth.uid()\``;
|
|
7421
|
-
}
|
|
7422
|
-
return null;
|
|
7423
|
-
};
|
|
7424
|
-
/**
|
|
7425
|
-
* Builds the WITH CHECK clause for a policy based on shortcuts or raw SQL.
|
|
7426
|
-
* Falls back to the USING clause if not explicitly provided.
|
|
7988
|
+
* Wraps a compiled SQL clause in a Drizzle `sql\`...\`` template literal.
|
|
7427
7989
|
*/
|
|
7428
|
-
var
|
|
7429
|
-
if (rule.withCheck) return resolveRawSql(rule.withCheck);
|
|
7430
|
-
return buildUsingClause(rule, collection);
|
|
7431
|
-
};
|
|
7990
|
+
var wrapSql = (clause) => `sql\`${clause}\``;
|
|
7432
7991
|
/**
|
|
7433
7992
|
* Generates a deterministic hash based on the rule configuration.
|
|
7434
7993
|
*/
|
|
@@ -7442,7 +8001,9 @@ var getPolicyNameHash = (rule) => {
|
|
|
7442
8001
|
rol: rule.roles?.slice().sort(),
|
|
7443
8002
|
pg: rule.pgRoles?.slice().sort(),
|
|
7444
8003
|
u: rule.using,
|
|
7445
|
-
w: rule.withCheck
|
|
8004
|
+
w: rule.withCheck,
|
|
8005
|
+
c: rule.condition,
|
|
8006
|
+
ch: rule.check
|
|
7446
8007
|
});
|
|
7447
8008
|
return createHash("sha1").update(data).digest("hex").substring(0, 7);
|
|
7448
8009
|
};
|
|
@@ -7469,17 +8030,11 @@ var generatePolicyCode = (collection, rule, index) => {
|
|
|
7469
8030
|
*/
|
|
7470
8031
|
var generateSinglePolicyCode = (collection, rule, operation, policyName) => {
|
|
7471
8032
|
const mode = rule.mode ?? "permissive";
|
|
7472
|
-
const roles = rule.roles ? [...rule.roles].sort() : void 0;
|
|
7473
8033
|
const needsUsing = operation !== "insert";
|
|
7474
8034
|
const needsWithCheck = operation !== "select" && operation !== "delete";
|
|
7475
|
-
|
|
7476
|
-
let
|
|
7477
|
-
|
|
7478
|
-
if (usingClause) usingClause = wrapWithRoleCheck(usingClause, roles);
|
|
7479
|
-
else if (needsUsing) usingClause = `sql\`string_to_array(auth.roles(), ',') && ${`ARRAY[${roles.map((r) => `'${r}'`).join(",")}]`}\``;
|
|
7480
|
-
if (withCheckClause) withCheckClause = wrapWithRoleCheck(withCheckClause, roles);
|
|
7481
|
-
else if (needsWithCheck) withCheckClause = `sql\`string_to_array(auth.roles(), ',') && ${`ARRAY[${roles.map((r) => `'${r}'`).join(",")}]`}\``;
|
|
7482
|
-
}
|
|
8035
|
+
const { usingExpr, withCheckExpr } = securityRuleToConditions(rule);
|
|
8036
|
+
let usingClause = needsUsing && usingExpr ? wrapSql(policyToPostgres(usingExpr, collection)) : null;
|
|
8037
|
+
let withCheckClause = needsWithCheck && withCheckExpr ? wrapSql(policyToPostgres(withCheckExpr, collection)) : null;
|
|
7483
8038
|
if (!usingClause && needsUsing) usingClause = "sql`false`";
|
|
7484
8039
|
if (!withCheckClause && needsWithCheck) withCheckClause = "sql`false`";
|
|
7485
8040
|
const parts = [];
|
|
@@ -7587,7 +8142,7 @@ var generateSchema = async (collections, stripPolicies = false) => {
|
|
|
7587
8142
|
if ("enum" in prop && (prop.type === "string" || prop.type === "number") && prop.enum) {
|
|
7588
8143
|
const enumVarName = getEnumVarName(collectionPath, propName);
|
|
7589
8144
|
const enumDbName = `${collectionPath}_${resolveColumnName(propName, prop)}`;
|
|
7590
|
-
const values = Array.isArray(prop.enum) ? prop.enum.map((v) => String(v.id
|
|
8145
|
+
const values = Array.isArray(prop.enum) ? prop.enum.map((v) => String(typeof v === "object" && v !== null && "id" in v ? v.id : v)) : Object.keys(prop.enum);
|
|
7591
8146
|
if (values.length > 0) {
|
|
7592
8147
|
schemaContent += `export const ${enumVarName} = pgEnum(\"${enumDbName}\", [${values.map((v) => `'${v}'`).join(", ")}]);\n`;
|
|
7593
8148
|
if (!exportedEnumVars.includes(enumVarName)) exportedEnumVars.push(enumVarName);
|
|
@@ -7644,8 +8199,8 @@ var generateSchema = async (collections, stripPolicies = false) => {
|
|
|
7644
8199
|
});
|
|
7645
8200
|
if (!Array.from(columns).some((col) => col.includes(".primaryKey()"))) columns.add(" id: varchar(\"id\").primaryKey()");
|
|
7646
8201
|
schemaContent += `${Array.from(columns).join(",\n")}`;
|
|
7647
|
-
const securityRules =
|
|
7648
|
-
if (!stripPolicies && securityRules
|
|
8202
|
+
const securityRules = getEffectiveSecurityRules(collection);
|
|
8203
|
+
if (!stripPolicies && securityRules.length > 0) {
|
|
7649
8204
|
schemaContent += "\n}, (table) => ([\n";
|
|
7650
8205
|
securityRules.forEach((rule, idx) => {
|
|
7651
8206
|
schemaContent += generatePolicyCode(collection, rule, idx);
|
|
@@ -7781,8 +8336,7 @@ var runGeneration = async (collectionsFilePath, outputPath) => {
|
|
|
7781
8336
|
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") {
|
|
7782
8337
|
const filePath = path.join(resolvedPath, file);
|
|
7783
8338
|
try {
|
|
7784
|
-
const
|
|
7785
|
-
const module = await new Function("url", "return import(url)")(fileUrl);
|
|
8339
|
+
const module = await import(pathToFileURL(filePath).href);
|
|
7786
8340
|
if (module && module.default) collections.push(module.default);
|
|
7787
8341
|
} catch (err) {
|
|
7788
8342
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -7790,8 +8344,7 @@ var runGeneration = async (collectionsFilePath, outputPath) => {
|
|
|
7790
8344
|
}
|
|
7791
8345
|
}
|
|
7792
8346
|
} else {
|
|
7793
|
-
const
|
|
7794
|
-
const imported = await new Function("url", "return import(url)")(fileUrl);
|
|
8347
|
+
const imported = await import(pathToFileURL(resolvedPath).href + `?t=${Date.now()}`);
|
|
7795
8348
|
collections = imported.backendCollections || imported.collections;
|
|
7796
8349
|
}
|
|
7797
8350
|
if (!collections || !Array.isArray(collections)) collections = [];
|
|
@@ -7801,7 +8354,7 @@ var runGeneration = async (collectionsFilePath, outputPath) => {
|
|
|
7801
8354
|
const outputDir = path.dirname(outputPath);
|
|
7802
8355
|
await promises.mkdir(outputDir, { recursive: true });
|
|
7803
8356
|
await promises.writeFile(outputPath, schemaContent);
|
|
7804
|
-
logger.info(
|
|
8357
|
+
logger.info(`✅ Drizzle schema generated successfully at ${outputPath}`);
|
|
7805
8358
|
} else {
|
|
7806
8359
|
logger.info("✅ Drizzle schema generated successfully.");
|
|
7807
8360
|
logger.info(String(schemaContent));
|
|
@@ -8236,8 +8789,9 @@ var RealtimeService = class RealtimeService extends EventEmitter {
|
|
|
8236
8789
|
...registryCollection
|
|
8237
8790
|
} : registryCollection;
|
|
8238
8791
|
const callbacks = resolvedCollection?.callbacks;
|
|
8792
|
+
const globalCallbacks = this.registry?.getGlobalCallbacks();
|
|
8239
8793
|
const propertyCallbacks = resolvedCollection?.properties ? buildPropertyCallbacks(resolvedCollection.properties) : void 0;
|
|
8240
|
-
if (callbacks?.afterRead || propertyCallbacks?.afterRead) {
|
|
8794
|
+
if (globalCallbacks?.afterRead || callbacks?.afterRead || propertyCallbacks?.afterRead) {
|
|
8241
8795
|
const contextForCallback = {
|
|
8242
8796
|
user: {
|
|
8243
8797
|
uid: activeAuth.userId,
|
|
@@ -8248,6 +8802,12 @@ var RealtimeService = class RealtimeService extends EventEmitter {
|
|
|
8248
8802
|
};
|
|
8249
8803
|
return await Promise.all(fetchedEntities.map(async (entity) => {
|
|
8250
8804
|
let processedEntity = entity;
|
|
8805
|
+
if (globalCallbacks?.afterRead) processedEntity = await globalCallbacks.afterRead({
|
|
8806
|
+
collection: resolvedCollection,
|
|
8807
|
+
path: notifyPath,
|
|
8808
|
+
entity: processedEntity,
|
|
8809
|
+
context: contextForCallback
|
|
8810
|
+
}) ?? processedEntity;
|
|
8251
8811
|
if (callbacks?.afterRead) processedEntity = await callbacks.afterRead({
|
|
8252
8812
|
collection: resolvedCollection,
|
|
8253
8813
|
path: notifyPath,
|
|
@@ -8344,8 +8904,9 @@ var RealtimeService = class RealtimeService extends EventEmitter {
|
|
|
8344
8904
|
...registryCollection
|
|
8345
8905
|
} : registryCollection;
|
|
8346
8906
|
const callbacks = resolvedCollection?.callbacks;
|
|
8907
|
+
const globalCallbacks = this.registry?.getGlobalCallbacks();
|
|
8347
8908
|
const propertyCallbacks = resolvedCollection?.properties ? buildPropertyCallbacks(resolvedCollection.properties) : void 0;
|
|
8348
|
-
if (callbacks?.afterRead || propertyCallbacks?.afterRead) {
|
|
8909
|
+
if (globalCallbacks?.afterRead || callbacks?.afterRead || propertyCallbacks?.afterRead) {
|
|
8349
8910
|
const contextForCallback = {
|
|
8350
8911
|
user: {
|
|
8351
8912
|
uid: activeAuth.userId,
|
|
@@ -8354,6 +8915,12 @@ var RealtimeService = class RealtimeService extends EventEmitter {
|
|
|
8354
8915
|
driver: this.driver,
|
|
8355
8916
|
data: this.driver && "data" in this.driver ? this.driver.data : void 0
|
|
8356
8917
|
};
|
|
8918
|
+
if (globalCallbacks?.afterRead) processedEntity = await globalCallbacks.afterRead({
|
|
8919
|
+
collection: resolvedCollection,
|
|
8920
|
+
path: notifyPath,
|
|
8921
|
+
entity: processedEntity,
|
|
8922
|
+
context: contextForCallback
|
|
8923
|
+
}) ?? processedEntity;
|
|
8357
8924
|
if (callbacks?.afterRead) processedEntity = await callbacks.afterRead({
|
|
8358
8925
|
collection: resolvedCollection,
|
|
8359
8926
|
path: notifyPath,
|
|
@@ -8967,6 +9534,15 @@ function createPostgresWebSocket(server, realtimeService, driver, authConfig, au
|
|
|
8967
9534
|
}
|
|
8968
9535
|
const result = await admin.executeSql(sql, options);
|
|
8969
9536
|
if (process.env.NODE_ENV !== "production") wsDebug(`⚡ [WebSocket Server] SQL executed. Returned ${Array.isArray(result) ? result.length : "non-array"} rows.`);
|
|
9537
|
+
const auditSession = clientSessions.get(clientId);
|
|
9538
|
+
console.log("[SQL Audit] WebSocket SQL execution", JSON.stringify({
|
|
9539
|
+
sql: typeof sql === "string" ? sql.substring(0, 500) : sql,
|
|
9540
|
+
options,
|
|
9541
|
+
resultRows: Array.isArray(result) ? result.length : "unknown",
|
|
9542
|
+
userId: auditSession?.user?.userId ?? "unknown",
|
|
9543
|
+
roles: auditSession?.user?.roles ?? [],
|
|
9544
|
+
isAdmin: auditSession?.user?.isAdmin ?? false
|
|
9545
|
+
}));
|
|
8970
9546
|
const response = {
|
|
8971
9547
|
type: "EXECUTE_SQL_SUCCESS",
|
|
8972
9548
|
payload: { result },
|
|
@@ -9181,10 +9757,10 @@ var PostgresCollectionRegistry = class extends CollectionRegistry {
|
|
|
9181
9757
|
return Array.from(this.tables.keys());
|
|
9182
9758
|
}
|
|
9183
9759
|
/**
|
|
9184
|
-
* Finds collections assigned to a specific
|
|
9760
|
+
* Finds collections assigned to a specific data source that do not have a registered table.
|
|
9185
9761
|
*/
|
|
9186
|
-
getCollectionsWithoutTables(
|
|
9187
|
-
return this.getCollections().filter((c) => c.
|
|
9762
|
+
getCollectionsWithoutTables(dataSourceKey = "(default)") {
|
|
9763
|
+
return this.getCollections().filter((c) => c.dataSource === dataSourceKey || !c.dataSource && dataSourceKey === "(default)").filter((c) => !this.tables.has(getTableName$1(c)));
|
|
9188
9764
|
}
|
|
9189
9765
|
registerEnums(enums) {
|
|
9190
9766
|
Object.entries(enums).forEach(([name, value]) => this.enums.set(name, value));
|
|
@@ -9221,7 +9797,7 @@ var PostgresCollectionRegistry = class extends CollectionRegistry {
|
|
|
9221
9797
|
*/
|
|
9222
9798
|
getRelationKeysForCollection(collectionPath) {
|
|
9223
9799
|
const collection = this.getCollectionByPath(collectionPath);
|
|
9224
|
-
if (!collection || !getDataSourceCapabilities(collection.
|
|
9800
|
+
if (!collection || !getDataSourceCapabilities(collection.engine).supportsRelations || !collection.relations) return [];
|
|
9225
9801
|
return collection.relations.map((r) => r.relationName || r.localKey || "").filter(Boolean);
|
|
9226
9802
|
}
|
|
9227
9803
|
};
|
|
@@ -9277,6 +9853,24 @@ async function ensureAuthTablesExist(db, collection) {
|
|
|
9277
9853
|
const refreshTokensTableName = `"${authSchema}"."refresh_tokens"`;
|
|
9278
9854
|
const passwordResetTokensTableName = `"${authSchema}"."password_reset_tokens"`;
|
|
9279
9855
|
const appConfigTableName = `"${authSchema}"."app_config"`;
|
|
9856
|
+
const idDefault = userIdType === "UUID" ? "DEFAULT gen_random_uuid()" : userIdType === "INTEGER" ? "GENERATED ALWAYS AS IDENTITY" : "DEFAULT gen_random_uuid()::text";
|
|
9857
|
+
await db.execute(sql`
|
|
9858
|
+
CREATE TABLE IF NOT EXISTS ${sql.raw(usersTableName)} (
|
|
9859
|
+
id ${sql.raw(userIdType)} PRIMARY KEY ${sql.raw(idDefault)},
|
|
9860
|
+
email VARCHAR(255) UNIQUE NOT NULL,
|
|
9861
|
+
display_name VARCHAR(255),
|
|
9862
|
+
photo_url VARCHAR(500),
|
|
9863
|
+
roles TEXT[] DEFAULT '{}' NOT NULL,
|
|
9864
|
+
password_hash VARCHAR(255),
|
|
9865
|
+
email_verified BOOLEAN DEFAULT FALSE NOT NULL,
|
|
9866
|
+
email_verification_token VARCHAR(255),
|
|
9867
|
+
email_verification_sent_at TIMESTAMP WITH TIME ZONE,
|
|
9868
|
+
is_anonymous BOOLEAN DEFAULT FALSE NOT NULL,
|
|
9869
|
+
metadata JSONB DEFAULT '{}' NOT NULL,
|
|
9870
|
+
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() NOT NULL,
|
|
9871
|
+
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() NOT NULL
|
|
9872
|
+
)
|
|
9873
|
+
`);
|
|
9280
9874
|
await db.execute(sql`
|
|
9281
9875
|
CREATE TABLE IF NOT EXISTS ${sql.raw(userIdentitiesTable)} (
|
|
9282
9876
|
id TEXT PRIMARY KEY DEFAULT gen_random_uuid()::text,
|
|
@@ -9330,6 +9924,25 @@ async function ensureAuthTablesExist(db, collection) {
|
|
|
9330
9924
|
await db.execute(sql`
|
|
9331
9925
|
CREATE INDEX IF NOT EXISTS idx_password_reset_tokens_user
|
|
9332
9926
|
ON ${sql.raw(passwordResetTokensTableName)}(user_id)
|
|
9927
|
+
`);
|
|
9928
|
+
const magicLinkTokensTableName = `"${authSchema}"."magic_link_tokens"`;
|
|
9929
|
+
await db.execute(sql`
|
|
9930
|
+
CREATE TABLE IF NOT EXISTS ${sql.raw(magicLinkTokensTableName)} (
|
|
9931
|
+
id TEXT PRIMARY KEY DEFAULT gen_random_uuid()::text,
|
|
9932
|
+
user_id ${sql.raw(userIdType)} NOT NULL REFERENCES ${sql.raw(usersTableName)}(id) ON DELETE CASCADE,
|
|
9933
|
+
token_hash TEXT NOT NULL UNIQUE,
|
|
9934
|
+
expires_at TIMESTAMP WITH TIME ZONE NOT NULL,
|
|
9935
|
+
used_at TIMESTAMP WITH TIME ZONE,
|
|
9936
|
+
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
|
9937
|
+
)
|
|
9938
|
+
`);
|
|
9939
|
+
await db.execute(sql`
|
|
9940
|
+
CREATE INDEX IF NOT EXISTS idx_magic_link_tokens_hash
|
|
9941
|
+
ON ${sql.raw(magicLinkTokensTableName)}(token_hash)
|
|
9942
|
+
`);
|
|
9943
|
+
await db.execute(sql`
|
|
9944
|
+
CREATE INDEX IF NOT EXISTS idx_magic_link_tokens_user
|
|
9945
|
+
ON ${sql.raw(magicLinkTokensTableName)}(user_id)
|
|
9333
9946
|
`);
|
|
9334
9947
|
await db.execute(sql`
|
|
9335
9948
|
CREATE TABLE IF NOT EXISTS ${sql.raw(appConfigTableName)} (
|
|
@@ -9942,6 +10555,53 @@ var PasswordResetTokenService = class {
|
|
|
9942
10555
|
}
|
|
9943
10556
|
};
|
|
9944
10557
|
/**
|
|
10558
|
+
* Magic link token service.
|
|
10559
|
+
* Handles magic link token storage for passwordless email login.
|
|
10560
|
+
*/
|
|
10561
|
+
var MagicLinkTokenService = class {
|
|
10562
|
+
db;
|
|
10563
|
+
magicLinkTokensTable;
|
|
10564
|
+
constructor(db, tableOrTables) {
|
|
10565
|
+
this.db = db;
|
|
10566
|
+
this.magicLinkTokensTable = magicLinkTokens;
|
|
10567
|
+
}
|
|
10568
|
+
getQualifiedTableName() {
|
|
10569
|
+
const name = getTableName(this.magicLinkTokensTable);
|
|
10570
|
+
return `"${getTableConfig(this.magicLinkTokensTable).schema || "public"}"."${name}"`;
|
|
10571
|
+
}
|
|
10572
|
+
async createToken(userId, tokenHash, expiresAt) {
|
|
10573
|
+
const tableName = this.getQualifiedTableName();
|
|
10574
|
+
await this.db.execute(sql`
|
|
10575
|
+
DELETE FROM ${sql.raw(tableName)}
|
|
10576
|
+
WHERE user_id = ${userId} AND used_at IS NULL
|
|
10577
|
+
`);
|
|
10578
|
+
await this.db.insert(this.magicLinkTokensTable).values({
|
|
10579
|
+
userId,
|
|
10580
|
+
tokenHash,
|
|
10581
|
+
expiresAt
|
|
10582
|
+
});
|
|
10583
|
+
}
|
|
10584
|
+
async findValidByHash(tokenHash) {
|
|
10585
|
+
const tableName = this.getQualifiedTableName();
|
|
10586
|
+
const result = await this.db.execute(sql`
|
|
10587
|
+
SELECT user_id, expires_at
|
|
10588
|
+
FROM ${sql.raw(tableName)}
|
|
10589
|
+
WHERE token_hash = ${tokenHash}
|
|
10590
|
+
AND used_at IS NULL
|
|
10591
|
+
AND expires_at > NOW()
|
|
10592
|
+
`);
|
|
10593
|
+
if (result.rows.length === 0) return null;
|
|
10594
|
+
const row = result.rows[0];
|
|
10595
|
+
return {
|
|
10596
|
+
userId: row.user_id,
|
|
10597
|
+
expiresAt: new Date(row.expires_at)
|
|
10598
|
+
};
|
|
10599
|
+
}
|
|
10600
|
+
async markAsUsed(tokenHash) {
|
|
10601
|
+
await this.db.update(this.magicLinkTokensTable).set({ usedAt: /* @__PURE__ */ new Date() }).where(eq(this.magicLinkTokensTable.tokenHash, tokenHash));
|
|
10602
|
+
}
|
|
10603
|
+
};
|
|
10604
|
+
/**
|
|
9945
10605
|
* PostgreSQL implementation of TokenRepository.
|
|
9946
10606
|
* Combines refresh token and password reset token operations.
|
|
9947
10607
|
*/
|
|
@@ -9949,10 +10609,12 @@ var PostgresTokenRepository = class {
|
|
|
9949
10609
|
db;
|
|
9950
10610
|
refreshTokenService;
|
|
9951
10611
|
passwordResetTokenService;
|
|
10612
|
+
magicLinkTokenService;
|
|
9952
10613
|
constructor(db, tableOrTables) {
|
|
9953
10614
|
this.db = db;
|
|
9954
10615
|
this.refreshTokenService = new RefreshTokenService(db, tableOrTables);
|
|
9955
10616
|
this.passwordResetTokenService = new PasswordResetTokenService(db, tableOrTables);
|
|
10617
|
+
this.magicLinkTokenService = new MagicLinkTokenService(db, tableOrTables);
|
|
9956
10618
|
}
|
|
9957
10619
|
async createRefreshToken(userId, tokenHash, expiresAt, userAgent, ipAddress) {
|
|
9958
10620
|
await this.refreshTokenService.createToken(userId, tokenHash, expiresAt, userAgent, ipAddress);
|
|
@@ -9987,6 +10649,15 @@ var PostgresTokenRepository = class {
|
|
|
9987
10649
|
async deleteExpiredTokens() {
|
|
9988
10650
|
await this.passwordResetTokenService.deleteExpired();
|
|
9989
10651
|
}
|
|
10652
|
+
async createMagicLinkToken(userId, tokenHash, expiresAt) {
|
|
10653
|
+
await this.magicLinkTokenService.createToken(userId, tokenHash, expiresAt);
|
|
10654
|
+
}
|
|
10655
|
+
async findValidMagicLinkToken(tokenHash) {
|
|
10656
|
+
return this.magicLinkTokenService.findValidByHash(tokenHash);
|
|
10657
|
+
}
|
|
10658
|
+
async markMagicLinkTokenUsed(tokenHash) {
|
|
10659
|
+
await this.magicLinkTokenService.markAsUsed(tokenHash);
|
|
10660
|
+
}
|
|
9990
10661
|
};
|
|
9991
10662
|
/**
|
|
9992
10663
|
* PostgreSQL implementation of AuthRepository.
|
|
@@ -10145,6 +10816,15 @@ var PostgresAuthRepository = class {
|
|
|
10145
10816
|
async deleteExpiredTokens() {
|
|
10146
10817
|
await this.tokenRepository.deleteExpiredTokens();
|
|
10147
10818
|
}
|
|
10819
|
+
async createMagicLinkToken(userId, tokenHash, expiresAt) {
|
|
10820
|
+
await this.tokenRepository.createMagicLinkToken(userId, tokenHash, expiresAt);
|
|
10821
|
+
}
|
|
10822
|
+
async findValidMagicLinkToken(tokenHash) {
|
|
10823
|
+
return this.tokenRepository.findValidMagicLinkToken(tokenHash);
|
|
10824
|
+
}
|
|
10825
|
+
async markMagicLinkTokenUsed(tokenHash) {
|
|
10826
|
+
await this.tokenRepository.markMagicLinkTokenUsed(tokenHash);
|
|
10827
|
+
}
|
|
10148
10828
|
_mfaService = null;
|
|
10149
10829
|
getMfaService() {
|
|
10150
10830
|
if (!this._mfaService) this._mfaService = new MfaService(this.db);
|
|
@@ -10590,6 +11270,2071 @@ function patchPgArrayNullSafety(tables) {
|
|
|
10590
11270
|
if (patchedCount > 0) logger.debug(`[PgArray] Patched ${patchedCount} array column(s) for null-safety`);
|
|
10591
11271
|
}
|
|
10592
11272
|
//#endregion
|
|
11273
|
+
//#region ../../node_modules/.pnpm/color-name@1.1.4/node_modules/color-name/index.js
|
|
11274
|
+
var require_color_name = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
11275
|
+
module.exports = {
|
|
11276
|
+
"aliceblue": [
|
|
11277
|
+
240,
|
|
11278
|
+
248,
|
|
11279
|
+
255
|
|
11280
|
+
],
|
|
11281
|
+
"antiquewhite": [
|
|
11282
|
+
250,
|
|
11283
|
+
235,
|
|
11284
|
+
215
|
|
11285
|
+
],
|
|
11286
|
+
"aqua": [
|
|
11287
|
+
0,
|
|
11288
|
+
255,
|
|
11289
|
+
255
|
|
11290
|
+
],
|
|
11291
|
+
"aquamarine": [
|
|
11292
|
+
127,
|
|
11293
|
+
255,
|
|
11294
|
+
212
|
|
11295
|
+
],
|
|
11296
|
+
"azure": [
|
|
11297
|
+
240,
|
|
11298
|
+
255,
|
|
11299
|
+
255
|
|
11300
|
+
],
|
|
11301
|
+
"beige": [
|
|
11302
|
+
245,
|
|
11303
|
+
245,
|
|
11304
|
+
220
|
|
11305
|
+
],
|
|
11306
|
+
"bisque": [
|
|
11307
|
+
255,
|
|
11308
|
+
228,
|
|
11309
|
+
196
|
|
11310
|
+
],
|
|
11311
|
+
"black": [
|
|
11312
|
+
0,
|
|
11313
|
+
0,
|
|
11314
|
+
0
|
|
11315
|
+
],
|
|
11316
|
+
"blanchedalmond": [
|
|
11317
|
+
255,
|
|
11318
|
+
235,
|
|
11319
|
+
205
|
|
11320
|
+
],
|
|
11321
|
+
"blue": [
|
|
11322
|
+
0,
|
|
11323
|
+
0,
|
|
11324
|
+
255
|
|
11325
|
+
],
|
|
11326
|
+
"blueviolet": [
|
|
11327
|
+
138,
|
|
11328
|
+
43,
|
|
11329
|
+
226
|
|
11330
|
+
],
|
|
11331
|
+
"brown": [
|
|
11332
|
+
165,
|
|
11333
|
+
42,
|
|
11334
|
+
42
|
|
11335
|
+
],
|
|
11336
|
+
"burlywood": [
|
|
11337
|
+
222,
|
|
11338
|
+
184,
|
|
11339
|
+
135
|
|
11340
|
+
],
|
|
11341
|
+
"cadetblue": [
|
|
11342
|
+
95,
|
|
11343
|
+
158,
|
|
11344
|
+
160
|
|
11345
|
+
],
|
|
11346
|
+
"chartreuse": [
|
|
11347
|
+
127,
|
|
11348
|
+
255,
|
|
11349
|
+
0
|
|
11350
|
+
],
|
|
11351
|
+
"chocolate": [
|
|
11352
|
+
210,
|
|
11353
|
+
105,
|
|
11354
|
+
30
|
|
11355
|
+
],
|
|
11356
|
+
"coral": [
|
|
11357
|
+
255,
|
|
11358
|
+
127,
|
|
11359
|
+
80
|
|
11360
|
+
],
|
|
11361
|
+
"cornflowerblue": [
|
|
11362
|
+
100,
|
|
11363
|
+
149,
|
|
11364
|
+
237
|
|
11365
|
+
],
|
|
11366
|
+
"cornsilk": [
|
|
11367
|
+
255,
|
|
11368
|
+
248,
|
|
11369
|
+
220
|
|
11370
|
+
],
|
|
11371
|
+
"crimson": [
|
|
11372
|
+
220,
|
|
11373
|
+
20,
|
|
11374
|
+
60
|
|
11375
|
+
],
|
|
11376
|
+
"cyan": [
|
|
11377
|
+
0,
|
|
11378
|
+
255,
|
|
11379
|
+
255
|
|
11380
|
+
],
|
|
11381
|
+
"darkblue": [
|
|
11382
|
+
0,
|
|
11383
|
+
0,
|
|
11384
|
+
139
|
|
11385
|
+
],
|
|
11386
|
+
"darkcyan": [
|
|
11387
|
+
0,
|
|
11388
|
+
139,
|
|
11389
|
+
139
|
|
11390
|
+
],
|
|
11391
|
+
"darkgoldenrod": [
|
|
11392
|
+
184,
|
|
11393
|
+
134,
|
|
11394
|
+
11
|
|
11395
|
+
],
|
|
11396
|
+
"darkgray": [
|
|
11397
|
+
169,
|
|
11398
|
+
169,
|
|
11399
|
+
169
|
|
11400
|
+
],
|
|
11401
|
+
"darkgreen": [
|
|
11402
|
+
0,
|
|
11403
|
+
100,
|
|
11404
|
+
0
|
|
11405
|
+
],
|
|
11406
|
+
"darkgrey": [
|
|
11407
|
+
169,
|
|
11408
|
+
169,
|
|
11409
|
+
169
|
|
11410
|
+
],
|
|
11411
|
+
"darkkhaki": [
|
|
11412
|
+
189,
|
|
11413
|
+
183,
|
|
11414
|
+
107
|
|
11415
|
+
],
|
|
11416
|
+
"darkmagenta": [
|
|
11417
|
+
139,
|
|
11418
|
+
0,
|
|
11419
|
+
139
|
|
11420
|
+
],
|
|
11421
|
+
"darkolivegreen": [
|
|
11422
|
+
85,
|
|
11423
|
+
107,
|
|
11424
|
+
47
|
|
11425
|
+
],
|
|
11426
|
+
"darkorange": [
|
|
11427
|
+
255,
|
|
11428
|
+
140,
|
|
11429
|
+
0
|
|
11430
|
+
],
|
|
11431
|
+
"darkorchid": [
|
|
11432
|
+
153,
|
|
11433
|
+
50,
|
|
11434
|
+
204
|
|
11435
|
+
],
|
|
11436
|
+
"darkred": [
|
|
11437
|
+
139,
|
|
11438
|
+
0,
|
|
11439
|
+
0
|
|
11440
|
+
],
|
|
11441
|
+
"darksalmon": [
|
|
11442
|
+
233,
|
|
11443
|
+
150,
|
|
11444
|
+
122
|
|
11445
|
+
],
|
|
11446
|
+
"darkseagreen": [
|
|
11447
|
+
143,
|
|
11448
|
+
188,
|
|
11449
|
+
143
|
|
11450
|
+
],
|
|
11451
|
+
"darkslateblue": [
|
|
11452
|
+
72,
|
|
11453
|
+
61,
|
|
11454
|
+
139
|
|
11455
|
+
],
|
|
11456
|
+
"darkslategray": [
|
|
11457
|
+
47,
|
|
11458
|
+
79,
|
|
11459
|
+
79
|
|
11460
|
+
],
|
|
11461
|
+
"darkslategrey": [
|
|
11462
|
+
47,
|
|
11463
|
+
79,
|
|
11464
|
+
79
|
|
11465
|
+
],
|
|
11466
|
+
"darkturquoise": [
|
|
11467
|
+
0,
|
|
11468
|
+
206,
|
|
11469
|
+
209
|
|
11470
|
+
],
|
|
11471
|
+
"darkviolet": [
|
|
11472
|
+
148,
|
|
11473
|
+
0,
|
|
11474
|
+
211
|
|
11475
|
+
],
|
|
11476
|
+
"deeppink": [
|
|
11477
|
+
255,
|
|
11478
|
+
20,
|
|
11479
|
+
147
|
|
11480
|
+
],
|
|
11481
|
+
"deepskyblue": [
|
|
11482
|
+
0,
|
|
11483
|
+
191,
|
|
11484
|
+
255
|
|
11485
|
+
],
|
|
11486
|
+
"dimgray": [
|
|
11487
|
+
105,
|
|
11488
|
+
105,
|
|
11489
|
+
105
|
|
11490
|
+
],
|
|
11491
|
+
"dimgrey": [
|
|
11492
|
+
105,
|
|
11493
|
+
105,
|
|
11494
|
+
105
|
|
11495
|
+
],
|
|
11496
|
+
"dodgerblue": [
|
|
11497
|
+
30,
|
|
11498
|
+
144,
|
|
11499
|
+
255
|
|
11500
|
+
],
|
|
11501
|
+
"firebrick": [
|
|
11502
|
+
178,
|
|
11503
|
+
34,
|
|
11504
|
+
34
|
|
11505
|
+
],
|
|
11506
|
+
"floralwhite": [
|
|
11507
|
+
255,
|
|
11508
|
+
250,
|
|
11509
|
+
240
|
|
11510
|
+
],
|
|
11511
|
+
"forestgreen": [
|
|
11512
|
+
34,
|
|
11513
|
+
139,
|
|
11514
|
+
34
|
|
11515
|
+
],
|
|
11516
|
+
"fuchsia": [
|
|
11517
|
+
255,
|
|
11518
|
+
0,
|
|
11519
|
+
255
|
|
11520
|
+
],
|
|
11521
|
+
"gainsboro": [
|
|
11522
|
+
220,
|
|
11523
|
+
220,
|
|
11524
|
+
220
|
|
11525
|
+
],
|
|
11526
|
+
"ghostwhite": [
|
|
11527
|
+
248,
|
|
11528
|
+
248,
|
|
11529
|
+
255
|
|
11530
|
+
],
|
|
11531
|
+
"gold": [
|
|
11532
|
+
255,
|
|
11533
|
+
215,
|
|
11534
|
+
0
|
|
11535
|
+
],
|
|
11536
|
+
"goldenrod": [
|
|
11537
|
+
218,
|
|
11538
|
+
165,
|
|
11539
|
+
32
|
|
11540
|
+
],
|
|
11541
|
+
"gray": [
|
|
11542
|
+
128,
|
|
11543
|
+
128,
|
|
11544
|
+
128
|
|
11545
|
+
],
|
|
11546
|
+
"green": [
|
|
11547
|
+
0,
|
|
11548
|
+
128,
|
|
11549
|
+
0
|
|
11550
|
+
],
|
|
11551
|
+
"greenyellow": [
|
|
11552
|
+
173,
|
|
11553
|
+
255,
|
|
11554
|
+
47
|
|
11555
|
+
],
|
|
11556
|
+
"grey": [
|
|
11557
|
+
128,
|
|
11558
|
+
128,
|
|
11559
|
+
128
|
|
11560
|
+
],
|
|
11561
|
+
"honeydew": [
|
|
11562
|
+
240,
|
|
11563
|
+
255,
|
|
11564
|
+
240
|
|
11565
|
+
],
|
|
11566
|
+
"hotpink": [
|
|
11567
|
+
255,
|
|
11568
|
+
105,
|
|
11569
|
+
180
|
|
11570
|
+
],
|
|
11571
|
+
"indianred": [
|
|
11572
|
+
205,
|
|
11573
|
+
92,
|
|
11574
|
+
92
|
|
11575
|
+
],
|
|
11576
|
+
"indigo": [
|
|
11577
|
+
75,
|
|
11578
|
+
0,
|
|
11579
|
+
130
|
|
11580
|
+
],
|
|
11581
|
+
"ivory": [
|
|
11582
|
+
255,
|
|
11583
|
+
255,
|
|
11584
|
+
240
|
|
11585
|
+
],
|
|
11586
|
+
"khaki": [
|
|
11587
|
+
240,
|
|
11588
|
+
230,
|
|
11589
|
+
140
|
|
11590
|
+
],
|
|
11591
|
+
"lavender": [
|
|
11592
|
+
230,
|
|
11593
|
+
230,
|
|
11594
|
+
250
|
|
11595
|
+
],
|
|
11596
|
+
"lavenderblush": [
|
|
11597
|
+
255,
|
|
11598
|
+
240,
|
|
11599
|
+
245
|
|
11600
|
+
],
|
|
11601
|
+
"lawngreen": [
|
|
11602
|
+
124,
|
|
11603
|
+
252,
|
|
11604
|
+
0
|
|
11605
|
+
],
|
|
11606
|
+
"lemonchiffon": [
|
|
11607
|
+
255,
|
|
11608
|
+
250,
|
|
11609
|
+
205
|
|
11610
|
+
],
|
|
11611
|
+
"lightblue": [
|
|
11612
|
+
173,
|
|
11613
|
+
216,
|
|
11614
|
+
230
|
|
11615
|
+
],
|
|
11616
|
+
"lightcoral": [
|
|
11617
|
+
240,
|
|
11618
|
+
128,
|
|
11619
|
+
128
|
|
11620
|
+
],
|
|
11621
|
+
"lightcyan": [
|
|
11622
|
+
224,
|
|
11623
|
+
255,
|
|
11624
|
+
255
|
|
11625
|
+
],
|
|
11626
|
+
"lightgoldenrodyellow": [
|
|
11627
|
+
250,
|
|
11628
|
+
250,
|
|
11629
|
+
210
|
|
11630
|
+
],
|
|
11631
|
+
"lightgray": [
|
|
11632
|
+
211,
|
|
11633
|
+
211,
|
|
11634
|
+
211
|
|
11635
|
+
],
|
|
11636
|
+
"lightgreen": [
|
|
11637
|
+
144,
|
|
11638
|
+
238,
|
|
11639
|
+
144
|
|
11640
|
+
],
|
|
11641
|
+
"lightgrey": [
|
|
11642
|
+
211,
|
|
11643
|
+
211,
|
|
11644
|
+
211
|
|
11645
|
+
],
|
|
11646
|
+
"lightpink": [
|
|
11647
|
+
255,
|
|
11648
|
+
182,
|
|
11649
|
+
193
|
|
11650
|
+
],
|
|
11651
|
+
"lightsalmon": [
|
|
11652
|
+
255,
|
|
11653
|
+
160,
|
|
11654
|
+
122
|
|
11655
|
+
],
|
|
11656
|
+
"lightseagreen": [
|
|
11657
|
+
32,
|
|
11658
|
+
178,
|
|
11659
|
+
170
|
|
11660
|
+
],
|
|
11661
|
+
"lightskyblue": [
|
|
11662
|
+
135,
|
|
11663
|
+
206,
|
|
11664
|
+
250
|
|
11665
|
+
],
|
|
11666
|
+
"lightslategray": [
|
|
11667
|
+
119,
|
|
11668
|
+
136,
|
|
11669
|
+
153
|
|
11670
|
+
],
|
|
11671
|
+
"lightslategrey": [
|
|
11672
|
+
119,
|
|
11673
|
+
136,
|
|
11674
|
+
153
|
|
11675
|
+
],
|
|
11676
|
+
"lightsteelblue": [
|
|
11677
|
+
176,
|
|
11678
|
+
196,
|
|
11679
|
+
222
|
|
11680
|
+
],
|
|
11681
|
+
"lightyellow": [
|
|
11682
|
+
255,
|
|
11683
|
+
255,
|
|
11684
|
+
224
|
|
11685
|
+
],
|
|
11686
|
+
"lime": [
|
|
11687
|
+
0,
|
|
11688
|
+
255,
|
|
11689
|
+
0
|
|
11690
|
+
],
|
|
11691
|
+
"limegreen": [
|
|
11692
|
+
50,
|
|
11693
|
+
205,
|
|
11694
|
+
50
|
|
11695
|
+
],
|
|
11696
|
+
"linen": [
|
|
11697
|
+
250,
|
|
11698
|
+
240,
|
|
11699
|
+
230
|
|
11700
|
+
],
|
|
11701
|
+
"magenta": [
|
|
11702
|
+
255,
|
|
11703
|
+
0,
|
|
11704
|
+
255
|
|
11705
|
+
],
|
|
11706
|
+
"maroon": [
|
|
11707
|
+
128,
|
|
11708
|
+
0,
|
|
11709
|
+
0
|
|
11710
|
+
],
|
|
11711
|
+
"mediumaquamarine": [
|
|
11712
|
+
102,
|
|
11713
|
+
205,
|
|
11714
|
+
170
|
|
11715
|
+
],
|
|
11716
|
+
"mediumblue": [
|
|
11717
|
+
0,
|
|
11718
|
+
0,
|
|
11719
|
+
205
|
|
11720
|
+
],
|
|
11721
|
+
"mediumorchid": [
|
|
11722
|
+
186,
|
|
11723
|
+
85,
|
|
11724
|
+
211
|
|
11725
|
+
],
|
|
11726
|
+
"mediumpurple": [
|
|
11727
|
+
147,
|
|
11728
|
+
112,
|
|
11729
|
+
219
|
|
11730
|
+
],
|
|
11731
|
+
"mediumseagreen": [
|
|
11732
|
+
60,
|
|
11733
|
+
179,
|
|
11734
|
+
113
|
|
11735
|
+
],
|
|
11736
|
+
"mediumslateblue": [
|
|
11737
|
+
123,
|
|
11738
|
+
104,
|
|
11739
|
+
238
|
|
11740
|
+
],
|
|
11741
|
+
"mediumspringgreen": [
|
|
11742
|
+
0,
|
|
11743
|
+
250,
|
|
11744
|
+
154
|
|
11745
|
+
],
|
|
11746
|
+
"mediumturquoise": [
|
|
11747
|
+
72,
|
|
11748
|
+
209,
|
|
11749
|
+
204
|
|
11750
|
+
],
|
|
11751
|
+
"mediumvioletred": [
|
|
11752
|
+
199,
|
|
11753
|
+
21,
|
|
11754
|
+
133
|
|
11755
|
+
],
|
|
11756
|
+
"midnightblue": [
|
|
11757
|
+
25,
|
|
11758
|
+
25,
|
|
11759
|
+
112
|
|
11760
|
+
],
|
|
11761
|
+
"mintcream": [
|
|
11762
|
+
245,
|
|
11763
|
+
255,
|
|
11764
|
+
250
|
|
11765
|
+
],
|
|
11766
|
+
"mistyrose": [
|
|
11767
|
+
255,
|
|
11768
|
+
228,
|
|
11769
|
+
225
|
|
11770
|
+
],
|
|
11771
|
+
"moccasin": [
|
|
11772
|
+
255,
|
|
11773
|
+
228,
|
|
11774
|
+
181
|
|
11775
|
+
],
|
|
11776
|
+
"navajowhite": [
|
|
11777
|
+
255,
|
|
11778
|
+
222,
|
|
11779
|
+
173
|
|
11780
|
+
],
|
|
11781
|
+
"navy": [
|
|
11782
|
+
0,
|
|
11783
|
+
0,
|
|
11784
|
+
128
|
|
11785
|
+
],
|
|
11786
|
+
"oldlace": [
|
|
11787
|
+
253,
|
|
11788
|
+
245,
|
|
11789
|
+
230
|
|
11790
|
+
],
|
|
11791
|
+
"olive": [
|
|
11792
|
+
128,
|
|
11793
|
+
128,
|
|
11794
|
+
0
|
|
11795
|
+
],
|
|
11796
|
+
"olivedrab": [
|
|
11797
|
+
107,
|
|
11798
|
+
142,
|
|
11799
|
+
35
|
|
11800
|
+
],
|
|
11801
|
+
"orange": [
|
|
11802
|
+
255,
|
|
11803
|
+
165,
|
|
11804
|
+
0
|
|
11805
|
+
],
|
|
11806
|
+
"orangered": [
|
|
11807
|
+
255,
|
|
11808
|
+
69,
|
|
11809
|
+
0
|
|
11810
|
+
],
|
|
11811
|
+
"orchid": [
|
|
11812
|
+
218,
|
|
11813
|
+
112,
|
|
11814
|
+
214
|
|
11815
|
+
],
|
|
11816
|
+
"palegoldenrod": [
|
|
11817
|
+
238,
|
|
11818
|
+
232,
|
|
11819
|
+
170
|
|
11820
|
+
],
|
|
11821
|
+
"palegreen": [
|
|
11822
|
+
152,
|
|
11823
|
+
251,
|
|
11824
|
+
152
|
|
11825
|
+
],
|
|
11826
|
+
"paleturquoise": [
|
|
11827
|
+
175,
|
|
11828
|
+
238,
|
|
11829
|
+
238
|
|
11830
|
+
],
|
|
11831
|
+
"palevioletred": [
|
|
11832
|
+
219,
|
|
11833
|
+
112,
|
|
11834
|
+
147
|
|
11835
|
+
],
|
|
11836
|
+
"papayawhip": [
|
|
11837
|
+
255,
|
|
11838
|
+
239,
|
|
11839
|
+
213
|
|
11840
|
+
],
|
|
11841
|
+
"peachpuff": [
|
|
11842
|
+
255,
|
|
11843
|
+
218,
|
|
11844
|
+
185
|
|
11845
|
+
],
|
|
11846
|
+
"peru": [
|
|
11847
|
+
205,
|
|
11848
|
+
133,
|
|
11849
|
+
63
|
|
11850
|
+
],
|
|
11851
|
+
"pink": [
|
|
11852
|
+
255,
|
|
11853
|
+
192,
|
|
11854
|
+
203
|
|
11855
|
+
],
|
|
11856
|
+
"plum": [
|
|
11857
|
+
221,
|
|
11858
|
+
160,
|
|
11859
|
+
221
|
|
11860
|
+
],
|
|
11861
|
+
"powderblue": [
|
|
11862
|
+
176,
|
|
11863
|
+
224,
|
|
11864
|
+
230
|
|
11865
|
+
],
|
|
11866
|
+
"purple": [
|
|
11867
|
+
128,
|
|
11868
|
+
0,
|
|
11869
|
+
128
|
|
11870
|
+
],
|
|
11871
|
+
"rebeccapurple": [
|
|
11872
|
+
102,
|
|
11873
|
+
51,
|
|
11874
|
+
153
|
|
11875
|
+
],
|
|
11876
|
+
"red": [
|
|
11877
|
+
255,
|
|
11878
|
+
0,
|
|
11879
|
+
0
|
|
11880
|
+
],
|
|
11881
|
+
"rosybrown": [
|
|
11882
|
+
188,
|
|
11883
|
+
143,
|
|
11884
|
+
143
|
|
11885
|
+
],
|
|
11886
|
+
"royalblue": [
|
|
11887
|
+
65,
|
|
11888
|
+
105,
|
|
11889
|
+
225
|
|
11890
|
+
],
|
|
11891
|
+
"saddlebrown": [
|
|
11892
|
+
139,
|
|
11893
|
+
69,
|
|
11894
|
+
19
|
|
11895
|
+
],
|
|
11896
|
+
"salmon": [
|
|
11897
|
+
250,
|
|
11898
|
+
128,
|
|
11899
|
+
114
|
|
11900
|
+
],
|
|
11901
|
+
"sandybrown": [
|
|
11902
|
+
244,
|
|
11903
|
+
164,
|
|
11904
|
+
96
|
|
11905
|
+
],
|
|
11906
|
+
"seagreen": [
|
|
11907
|
+
46,
|
|
11908
|
+
139,
|
|
11909
|
+
87
|
|
11910
|
+
],
|
|
11911
|
+
"seashell": [
|
|
11912
|
+
255,
|
|
11913
|
+
245,
|
|
11914
|
+
238
|
|
11915
|
+
],
|
|
11916
|
+
"sienna": [
|
|
11917
|
+
160,
|
|
11918
|
+
82,
|
|
11919
|
+
45
|
|
11920
|
+
],
|
|
11921
|
+
"silver": [
|
|
11922
|
+
192,
|
|
11923
|
+
192,
|
|
11924
|
+
192
|
|
11925
|
+
],
|
|
11926
|
+
"skyblue": [
|
|
11927
|
+
135,
|
|
11928
|
+
206,
|
|
11929
|
+
235
|
|
11930
|
+
],
|
|
11931
|
+
"slateblue": [
|
|
11932
|
+
106,
|
|
11933
|
+
90,
|
|
11934
|
+
205
|
|
11935
|
+
],
|
|
11936
|
+
"slategray": [
|
|
11937
|
+
112,
|
|
11938
|
+
128,
|
|
11939
|
+
144
|
|
11940
|
+
],
|
|
11941
|
+
"slategrey": [
|
|
11942
|
+
112,
|
|
11943
|
+
128,
|
|
11944
|
+
144
|
|
11945
|
+
],
|
|
11946
|
+
"snow": [
|
|
11947
|
+
255,
|
|
11948
|
+
250,
|
|
11949
|
+
250
|
|
11950
|
+
],
|
|
11951
|
+
"springgreen": [
|
|
11952
|
+
0,
|
|
11953
|
+
255,
|
|
11954
|
+
127
|
|
11955
|
+
],
|
|
11956
|
+
"steelblue": [
|
|
11957
|
+
70,
|
|
11958
|
+
130,
|
|
11959
|
+
180
|
|
11960
|
+
],
|
|
11961
|
+
"tan": [
|
|
11962
|
+
210,
|
|
11963
|
+
180,
|
|
11964
|
+
140
|
|
11965
|
+
],
|
|
11966
|
+
"teal": [
|
|
11967
|
+
0,
|
|
11968
|
+
128,
|
|
11969
|
+
128
|
|
11970
|
+
],
|
|
11971
|
+
"thistle": [
|
|
11972
|
+
216,
|
|
11973
|
+
191,
|
|
11974
|
+
216
|
|
11975
|
+
],
|
|
11976
|
+
"tomato": [
|
|
11977
|
+
255,
|
|
11978
|
+
99,
|
|
11979
|
+
71
|
|
11980
|
+
],
|
|
11981
|
+
"turquoise": [
|
|
11982
|
+
64,
|
|
11983
|
+
224,
|
|
11984
|
+
208
|
|
11985
|
+
],
|
|
11986
|
+
"violet": [
|
|
11987
|
+
238,
|
|
11988
|
+
130,
|
|
11989
|
+
238
|
|
11990
|
+
],
|
|
11991
|
+
"wheat": [
|
|
11992
|
+
245,
|
|
11993
|
+
222,
|
|
11994
|
+
179
|
|
11995
|
+
],
|
|
11996
|
+
"white": [
|
|
11997
|
+
255,
|
|
11998
|
+
255,
|
|
11999
|
+
255
|
|
12000
|
+
],
|
|
12001
|
+
"whitesmoke": [
|
|
12002
|
+
245,
|
|
12003
|
+
245,
|
|
12004
|
+
245
|
|
12005
|
+
],
|
|
12006
|
+
"yellow": [
|
|
12007
|
+
255,
|
|
12008
|
+
255,
|
|
12009
|
+
0
|
|
12010
|
+
],
|
|
12011
|
+
"yellowgreen": [
|
|
12012
|
+
154,
|
|
12013
|
+
205,
|
|
12014
|
+
50
|
|
12015
|
+
]
|
|
12016
|
+
};
|
|
12017
|
+
}));
|
|
12018
|
+
//#endregion
|
|
12019
|
+
//#region ../../node_modules/.pnpm/color-convert@2.0.1/node_modules/color-convert/conversions.js
|
|
12020
|
+
var require_conversions = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
12021
|
+
var cssKeywords = require_color_name();
|
|
12022
|
+
var reverseKeywords = {};
|
|
12023
|
+
for (const key of Object.keys(cssKeywords)) reverseKeywords[cssKeywords[key]] = key;
|
|
12024
|
+
var convert = {
|
|
12025
|
+
rgb: {
|
|
12026
|
+
channels: 3,
|
|
12027
|
+
labels: "rgb"
|
|
12028
|
+
},
|
|
12029
|
+
hsl: {
|
|
12030
|
+
channels: 3,
|
|
12031
|
+
labels: "hsl"
|
|
12032
|
+
},
|
|
12033
|
+
hsv: {
|
|
12034
|
+
channels: 3,
|
|
12035
|
+
labels: "hsv"
|
|
12036
|
+
},
|
|
12037
|
+
hwb: {
|
|
12038
|
+
channels: 3,
|
|
12039
|
+
labels: "hwb"
|
|
12040
|
+
},
|
|
12041
|
+
cmyk: {
|
|
12042
|
+
channels: 4,
|
|
12043
|
+
labels: "cmyk"
|
|
12044
|
+
},
|
|
12045
|
+
xyz: {
|
|
12046
|
+
channels: 3,
|
|
12047
|
+
labels: "xyz"
|
|
12048
|
+
},
|
|
12049
|
+
lab: {
|
|
12050
|
+
channels: 3,
|
|
12051
|
+
labels: "lab"
|
|
12052
|
+
},
|
|
12053
|
+
lch: {
|
|
12054
|
+
channels: 3,
|
|
12055
|
+
labels: "lch"
|
|
12056
|
+
},
|
|
12057
|
+
hex: {
|
|
12058
|
+
channels: 1,
|
|
12059
|
+
labels: ["hex"]
|
|
12060
|
+
},
|
|
12061
|
+
keyword: {
|
|
12062
|
+
channels: 1,
|
|
12063
|
+
labels: ["keyword"]
|
|
12064
|
+
},
|
|
12065
|
+
ansi16: {
|
|
12066
|
+
channels: 1,
|
|
12067
|
+
labels: ["ansi16"]
|
|
12068
|
+
},
|
|
12069
|
+
ansi256: {
|
|
12070
|
+
channels: 1,
|
|
12071
|
+
labels: ["ansi256"]
|
|
12072
|
+
},
|
|
12073
|
+
hcg: {
|
|
12074
|
+
channels: 3,
|
|
12075
|
+
labels: [
|
|
12076
|
+
"h",
|
|
12077
|
+
"c",
|
|
12078
|
+
"g"
|
|
12079
|
+
]
|
|
12080
|
+
},
|
|
12081
|
+
apple: {
|
|
12082
|
+
channels: 3,
|
|
12083
|
+
labels: [
|
|
12084
|
+
"r16",
|
|
12085
|
+
"g16",
|
|
12086
|
+
"b16"
|
|
12087
|
+
]
|
|
12088
|
+
},
|
|
12089
|
+
gray: {
|
|
12090
|
+
channels: 1,
|
|
12091
|
+
labels: ["gray"]
|
|
12092
|
+
}
|
|
12093
|
+
};
|
|
12094
|
+
module.exports = convert;
|
|
12095
|
+
for (const model of Object.keys(convert)) {
|
|
12096
|
+
if (!("channels" in convert[model])) throw new Error("missing channels property: " + model);
|
|
12097
|
+
if (!("labels" in convert[model])) throw new Error("missing channel labels property: " + model);
|
|
12098
|
+
if (convert[model].labels.length !== convert[model].channels) throw new Error("channel and label counts mismatch: " + model);
|
|
12099
|
+
const { channels, labels } = convert[model];
|
|
12100
|
+
delete convert[model].channels;
|
|
12101
|
+
delete convert[model].labels;
|
|
12102
|
+
Object.defineProperty(convert[model], "channels", { value: channels });
|
|
12103
|
+
Object.defineProperty(convert[model], "labels", { value: labels });
|
|
12104
|
+
}
|
|
12105
|
+
convert.rgb.hsl = function(rgb) {
|
|
12106
|
+
const r = rgb[0] / 255;
|
|
12107
|
+
const g = rgb[1] / 255;
|
|
12108
|
+
const b = rgb[2] / 255;
|
|
12109
|
+
const min = Math.min(r, g, b);
|
|
12110
|
+
const max = Math.max(r, g, b);
|
|
12111
|
+
const delta = max - min;
|
|
12112
|
+
let h;
|
|
12113
|
+
let s;
|
|
12114
|
+
if (max === min) h = 0;
|
|
12115
|
+
else if (r === max) h = (g - b) / delta;
|
|
12116
|
+
else if (g === max) h = 2 + (b - r) / delta;
|
|
12117
|
+
else if (b === max) h = 4 + (r - g) / delta;
|
|
12118
|
+
h = Math.min(h * 60, 360);
|
|
12119
|
+
if (h < 0) h += 360;
|
|
12120
|
+
const l = (min + max) / 2;
|
|
12121
|
+
if (max === min) s = 0;
|
|
12122
|
+
else if (l <= .5) s = delta / (max + min);
|
|
12123
|
+
else s = delta / (2 - max - min);
|
|
12124
|
+
return [
|
|
12125
|
+
h,
|
|
12126
|
+
s * 100,
|
|
12127
|
+
l * 100
|
|
12128
|
+
];
|
|
12129
|
+
};
|
|
12130
|
+
convert.rgb.hsv = function(rgb) {
|
|
12131
|
+
let rdif;
|
|
12132
|
+
let gdif;
|
|
12133
|
+
let bdif;
|
|
12134
|
+
let h;
|
|
12135
|
+
let s;
|
|
12136
|
+
const r = rgb[0] / 255;
|
|
12137
|
+
const g = rgb[1] / 255;
|
|
12138
|
+
const b = rgb[2] / 255;
|
|
12139
|
+
const v = Math.max(r, g, b);
|
|
12140
|
+
const diff = v - Math.min(r, g, b);
|
|
12141
|
+
const diffc = function(c) {
|
|
12142
|
+
return (v - c) / 6 / diff + 1 / 2;
|
|
12143
|
+
};
|
|
12144
|
+
if (diff === 0) {
|
|
12145
|
+
h = 0;
|
|
12146
|
+
s = 0;
|
|
12147
|
+
} else {
|
|
12148
|
+
s = diff / v;
|
|
12149
|
+
rdif = diffc(r);
|
|
12150
|
+
gdif = diffc(g);
|
|
12151
|
+
bdif = diffc(b);
|
|
12152
|
+
if (r === v) h = bdif - gdif;
|
|
12153
|
+
else if (g === v) h = 1 / 3 + rdif - bdif;
|
|
12154
|
+
else if (b === v) h = 2 / 3 + gdif - rdif;
|
|
12155
|
+
if (h < 0) h += 1;
|
|
12156
|
+
else if (h > 1) h -= 1;
|
|
12157
|
+
}
|
|
12158
|
+
return [
|
|
12159
|
+
h * 360,
|
|
12160
|
+
s * 100,
|
|
12161
|
+
v * 100
|
|
12162
|
+
];
|
|
12163
|
+
};
|
|
12164
|
+
convert.rgb.hwb = function(rgb) {
|
|
12165
|
+
const r = rgb[0];
|
|
12166
|
+
const g = rgb[1];
|
|
12167
|
+
let b = rgb[2];
|
|
12168
|
+
const h = convert.rgb.hsl(rgb)[0];
|
|
12169
|
+
const w = 1 / 255 * Math.min(r, Math.min(g, b));
|
|
12170
|
+
b = 1 - 1 / 255 * Math.max(r, Math.max(g, b));
|
|
12171
|
+
return [
|
|
12172
|
+
h,
|
|
12173
|
+
w * 100,
|
|
12174
|
+
b * 100
|
|
12175
|
+
];
|
|
12176
|
+
};
|
|
12177
|
+
convert.rgb.cmyk = function(rgb) {
|
|
12178
|
+
const r = rgb[0] / 255;
|
|
12179
|
+
const g = rgb[1] / 255;
|
|
12180
|
+
const b = rgb[2] / 255;
|
|
12181
|
+
const k = Math.min(1 - r, 1 - g, 1 - b);
|
|
12182
|
+
const c = (1 - r - k) / (1 - k) || 0;
|
|
12183
|
+
const m = (1 - g - k) / (1 - k) || 0;
|
|
12184
|
+
const y = (1 - b - k) / (1 - k) || 0;
|
|
12185
|
+
return [
|
|
12186
|
+
c * 100,
|
|
12187
|
+
m * 100,
|
|
12188
|
+
y * 100,
|
|
12189
|
+
k * 100
|
|
12190
|
+
];
|
|
12191
|
+
};
|
|
12192
|
+
function comparativeDistance(x, y) {
|
|
12193
|
+
return (x[0] - y[0]) ** 2 + (x[1] - y[1]) ** 2 + (x[2] - y[2]) ** 2;
|
|
12194
|
+
}
|
|
12195
|
+
convert.rgb.keyword = function(rgb) {
|
|
12196
|
+
const reversed = reverseKeywords[rgb];
|
|
12197
|
+
if (reversed) return reversed;
|
|
12198
|
+
let currentClosestDistance = Infinity;
|
|
12199
|
+
let currentClosestKeyword;
|
|
12200
|
+
for (const keyword of Object.keys(cssKeywords)) {
|
|
12201
|
+
const value = cssKeywords[keyword];
|
|
12202
|
+
const distance = comparativeDistance(rgb, value);
|
|
12203
|
+
if (distance < currentClosestDistance) {
|
|
12204
|
+
currentClosestDistance = distance;
|
|
12205
|
+
currentClosestKeyword = keyword;
|
|
12206
|
+
}
|
|
12207
|
+
}
|
|
12208
|
+
return currentClosestKeyword;
|
|
12209
|
+
};
|
|
12210
|
+
convert.keyword.rgb = function(keyword) {
|
|
12211
|
+
return cssKeywords[keyword];
|
|
12212
|
+
};
|
|
12213
|
+
convert.rgb.xyz = function(rgb) {
|
|
12214
|
+
let r = rgb[0] / 255;
|
|
12215
|
+
let g = rgb[1] / 255;
|
|
12216
|
+
let b = rgb[2] / 255;
|
|
12217
|
+
r = r > .04045 ? ((r + .055) / 1.055) ** 2.4 : r / 12.92;
|
|
12218
|
+
g = g > .04045 ? ((g + .055) / 1.055) ** 2.4 : g / 12.92;
|
|
12219
|
+
b = b > .04045 ? ((b + .055) / 1.055) ** 2.4 : b / 12.92;
|
|
12220
|
+
const x = r * .4124 + g * .3576 + b * .1805;
|
|
12221
|
+
const y = r * .2126 + g * .7152 + b * .0722;
|
|
12222
|
+
const z = r * .0193 + g * .1192 + b * .9505;
|
|
12223
|
+
return [
|
|
12224
|
+
x * 100,
|
|
12225
|
+
y * 100,
|
|
12226
|
+
z * 100
|
|
12227
|
+
];
|
|
12228
|
+
};
|
|
12229
|
+
convert.rgb.lab = function(rgb) {
|
|
12230
|
+
const xyz = convert.rgb.xyz(rgb);
|
|
12231
|
+
let x = xyz[0];
|
|
12232
|
+
let y = xyz[1];
|
|
12233
|
+
let z = xyz[2];
|
|
12234
|
+
x /= 95.047;
|
|
12235
|
+
y /= 100;
|
|
12236
|
+
z /= 108.883;
|
|
12237
|
+
x = x > .008856 ? x ** (1 / 3) : 7.787 * x + 16 / 116;
|
|
12238
|
+
y = y > .008856 ? y ** (1 / 3) : 7.787 * y + 16 / 116;
|
|
12239
|
+
z = z > .008856 ? z ** (1 / 3) : 7.787 * z + 16 / 116;
|
|
12240
|
+
return [
|
|
12241
|
+
116 * y - 16,
|
|
12242
|
+
500 * (x - y),
|
|
12243
|
+
200 * (y - z)
|
|
12244
|
+
];
|
|
12245
|
+
};
|
|
12246
|
+
convert.hsl.rgb = function(hsl) {
|
|
12247
|
+
const h = hsl[0] / 360;
|
|
12248
|
+
const s = hsl[1] / 100;
|
|
12249
|
+
const l = hsl[2] / 100;
|
|
12250
|
+
let t2;
|
|
12251
|
+
let t3;
|
|
12252
|
+
let val;
|
|
12253
|
+
if (s === 0) {
|
|
12254
|
+
val = l * 255;
|
|
12255
|
+
return [
|
|
12256
|
+
val,
|
|
12257
|
+
val,
|
|
12258
|
+
val
|
|
12259
|
+
];
|
|
12260
|
+
}
|
|
12261
|
+
if (l < .5) t2 = l * (1 + s);
|
|
12262
|
+
else t2 = l + s - l * s;
|
|
12263
|
+
const t1 = 2 * l - t2;
|
|
12264
|
+
const rgb = [
|
|
12265
|
+
0,
|
|
12266
|
+
0,
|
|
12267
|
+
0
|
|
12268
|
+
];
|
|
12269
|
+
for (let i = 0; i < 3; i++) {
|
|
12270
|
+
t3 = h + 1 / 3 * -(i - 1);
|
|
12271
|
+
if (t3 < 0) t3++;
|
|
12272
|
+
if (t3 > 1) t3--;
|
|
12273
|
+
if (6 * t3 < 1) val = t1 + (t2 - t1) * 6 * t3;
|
|
12274
|
+
else if (2 * t3 < 1) val = t2;
|
|
12275
|
+
else if (3 * t3 < 2) val = t1 + (t2 - t1) * (2 / 3 - t3) * 6;
|
|
12276
|
+
else val = t1;
|
|
12277
|
+
rgb[i] = val * 255;
|
|
12278
|
+
}
|
|
12279
|
+
return rgb;
|
|
12280
|
+
};
|
|
12281
|
+
convert.hsl.hsv = function(hsl) {
|
|
12282
|
+
const h = hsl[0];
|
|
12283
|
+
let s = hsl[1] / 100;
|
|
12284
|
+
let l = hsl[2] / 100;
|
|
12285
|
+
let smin = s;
|
|
12286
|
+
const lmin = Math.max(l, .01);
|
|
12287
|
+
l *= 2;
|
|
12288
|
+
s *= l <= 1 ? l : 2 - l;
|
|
12289
|
+
smin *= lmin <= 1 ? lmin : 2 - lmin;
|
|
12290
|
+
const v = (l + s) / 2;
|
|
12291
|
+
return [
|
|
12292
|
+
h,
|
|
12293
|
+
(l === 0 ? 2 * smin / (lmin + smin) : 2 * s / (l + s)) * 100,
|
|
12294
|
+
v * 100
|
|
12295
|
+
];
|
|
12296
|
+
};
|
|
12297
|
+
convert.hsv.rgb = function(hsv) {
|
|
12298
|
+
const h = hsv[0] / 60;
|
|
12299
|
+
const s = hsv[1] / 100;
|
|
12300
|
+
let v = hsv[2] / 100;
|
|
12301
|
+
const hi = Math.floor(h) % 6;
|
|
12302
|
+
const f = h - Math.floor(h);
|
|
12303
|
+
const p = 255 * v * (1 - s);
|
|
12304
|
+
const q = 255 * v * (1 - s * f);
|
|
12305
|
+
const t = 255 * v * (1 - s * (1 - f));
|
|
12306
|
+
v *= 255;
|
|
12307
|
+
switch (hi) {
|
|
12308
|
+
case 0: return [
|
|
12309
|
+
v,
|
|
12310
|
+
t,
|
|
12311
|
+
p
|
|
12312
|
+
];
|
|
12313
|
+
case 1: return [
|
|
12314
|
+
q,
|
|
12315
|
+
v,
|
|
12316
|
+
p
|
|
12317
|
+
];
|
|
12318
|
+
case 2: return [
|
|
12319
|
+
p,
|
|
12320
|
+
v,
|
|
12321
|
+
t
|
|
12322
|
+
];
|
|
12323
|
+
case 3: return [
|
|
12324
|
+
p,
|
|
12325
|
+
q,
|
|
12326
|
+
v
|
|
12327
|
+
];
|
|
12328
|
+
case 4: return [
|
|
12329
|
+
t,
|
|
12330
|
+
p,
|
|
12331
|
+
v
|
|
12332
|
+
];
|
|
12333
|
+
case 5: return [
|
|
12334
|
+
v,
|
|
12335
|
+
p,
|
|
12336
|
+
q
|
|
12337
|
+
];
|
|
12338
|
+
}
|
|
12339
|
+
};
|
|
12340
|
+
convert.hsv.hsl = function(hsv) {
|
|
12341
|
+
const h = hsv[0];
|
|
12342
|
+
const s = hsv[1] / 100;
|
|
12343
|
+
const v = hsv[2] / 100;
|
|
12344
|
+
const vmin = Math.max(v, .01);
|
|
12345
|
+
let sl;
|
|
12346
|
+
let l;
|
|
12347
|
+
l = (2 - s) * v;
|
|
12348
|
+
const lmin = (2 - s) * vmin;
|
|
12349
|
+
sl = s * vmin;
|
|
12350
|
+
sl /= lmin <= 1 ? lmin : 2 - lmin;
|
|
12351
|
+
sl = sl || 0;
|
|
12352
|
+
l /= 2;
|
|
12353
|
+
return [
|
|
12354
|
+
h,
|
|
12355
|
+
sl * 100,
|
|
12356
|
+
l * 100
|
|
12357
|
+
];
|
|
12358
|
+
};
|
|
12359
|
+
convert.hwb.rgb = function(hwb) {
|
|
12360
|
+
const h = hwb[0] / 360;
|
|
12361
|
+
let wh = hwb[1] / 100;
|
|
12362
|
+
let bl = hwb[2] / 100;
|
|
12363
|
+
const ratio = wh + bl;
|
|
12364
|
+
let f;
|
|
12365
|
+
if (ratio > 1) {
|
|
12366
|
+
wh /= ratio;
|
|
12367
|
+
bl /= ratio;
|
|
12368
|
+
}
|
|
12369
|
+
const i = Math.floor(6 * h);
|
|
12370
|
+
const v = 1 - bl;
|
|
12371
|
+
f = 6 * h - i;
|
|
12372
|
+
if ((i & 1) !== 0) f = 1 - f;
|
|
12373
|
+
const n = wh + f * (v - wh);
|
|
12374
|
+
let r;
|
|
12375
|
+
let g;
|
|
12376
|
+
let b;
|
|
12377
|
+
switch (i) {
|
|
12378
|
+
default:
|
|
12379
|
+
case 6:
|
|
12380
|
+
case 0:
|
|
12381
|
+
r = v;
|
|
12382
|
+
g = n;
|
|
12383
|
+
b = wh;
|
|
12384
|
+
break;
|
|
12385
|
+
case 1:
|
|
12386
|
+
r = n;
|
|
12387
|
+
g = v;
|
|
12388
|
+
b = wh;
|
|
12389
|
+
break;
|
|
12390
|
+
case 2:
|
|
12391
|
+
r = wh;
|
|
12392
|
+
g = v;
|
|
12393
|
+
b = n;
|
|
12394
|
+
break;
|
|
12395
|
+
case 3:
|
|
12396
|
+
r = wh;
|
|
12397
|
+
g = n;
|
|
12398
|
+
b = v;
|
|
12399
|
+
break;
|
|
12400
|
+
case 4:
|
|
12401
|
+
r = n;
|
|
12402
|
+
g = wh;
|
|
12403
|
+
b = v;
|
|
12404
|
+
break;
|
|
12405
|
+
case 5:
|
|
12406
|
+
r = v;
|
|
12407
|
+
g = wh;
|
|
12408
|
+
b = n;
|
|
12409
|
+
break;
|
|
12410
|
+
}
|
|
12411
|
+
return [
|
|
12412
|
+
r * 255,
|
|
12413
|
+
g * 255,
|
|
12414
|
+
b * 255
|
|
12415
|
+
];
|
|
12416
|
+
};
|
|
12417
|
+
convert.cmyk.rgb = function(cmyk) {
|
|
12418
|
+
const c = cmyk[0] / 100;
|
|
12419
|
+
const m = cmyk[1] / 100;
|
|
12420
|
+
const y = cmyk[2] / 100;
|
|
12421
|
+
const k = cmyk[3] / 100;
|
|
12422
|
+
const r = 1 - Math.min(1, c * (1 - k) + k);
|
|
12423
|
+
const g = 1 - Math.min(1, m * (1 - k) + k);
|
|
12424
|
+
const b = 1 - Math.min(1, y * (1 - k) + k);
|
|
12425
|
+
return [
|
|
12426
|
+
r * 255,
|
|
12427
|
+
g * 255,
|
|
12428
|
+
b * 255
|
|
12429
|
+
];
|
|
12430
|
+
};
|
|
12431
|
+
convert.xyz.rgb = function(xyz) {
|
|
12432
|
+
const x = xyz[0] / 100;
|
|
12433
|
+
const y = xyz[1] / 100;
|
|
12434
|
+
const z = xyz[2] / 100;
|
|
12435
|
+
let r;
|
|
12436
|
+
let g;
|
|
12437
|
+
let b;
|
|
12438
|
+
r = x * 3.2406 + y * -1.5372 + z * -.4986;
|
|
12439
|
+
g = x * -.9689 + y * 1.8758 + z * .0415;
|
|
12440
|
+
b = x * .0557 + y * -.204 + z * 1.057;
|
|
12441
|
+
r = r > .0031308 ? 1.055 * r ** (1 / 2.4) - .055 : r * 12.92;
|
|
12442
|
+
g = g > .0031308 ? 1.055 * g ** (1 / 2.4) - .055 : g * 12.92;
|
|
12443
|
+
b = b > .0031308 ? 1.055 * b ** (1 / 2.4) - .055 : b * 12.92;
|
|
12444
|
+
r = Math.min(Math.max(0, r), 1);
|
|
12445
|
+
g = Math.min(Math.max(0, g), 1);
|
|
12446
|
+
b = Math.min(Math.max(0, b), 1);
|
|
12447
|
+
return [
|
|
12448
|
+
r * 255,
|
|
12449
|
+
g * 255,
|
|
12450
|
+
b * 255
|
|
12451
|
+
];
|
|
12452
|
+
};
|
|
12453
|
+
convert.xyz.lab = function(xyz) {
|
|
12454
|
+
let x = xyz[0];
|
|
12455
|
+
let y = xyz[1];
|
|
12456
|
+
let z = xyz[2];
|
|
12457
|
+
x /= 95.047;
|
|
12458
|
+
y /= 100;
|
|
12459
|
+
z /= 108.883;
|
|
12460
|
+
x = x > .008856 ? x ** (1 / 3) : 7.787 * x + 16 / 116;
|
|
12461
|
+
y = y > .008856 ? y ** (1 / 3) : 7.787 * y + 16 / 116;
|
|
12462
|
+
z = z > .008856 ? z ** (1 / 3) : 7.787 * z + 16 / 116;
|
|
12463
|
+
return [
|
|
12464
|
+
116 * y - 16,
|
|
12465
|
+
500 * (x - y),
|
|
12466
|
+
200 * (y - z)
|
|
12467
|
+
];
|
|
12468
|
+
};
|
|
12469
|
+
convert.lab.xyz = function(lab) {
|
|
12470
|
+
const l = lab[0];
|
|
12471
|
+
const a = lab[1];
|
|
12472
|
+
const b = lab[2];
|
|
12473
|
+
let x;
|
|
12474
|
+
let y;
|
|
12475
|
+
let z;
|
|
12476
|
+
y = (l + 16) / 116;
|
|
12477
|
+
x = a / 500 + y;
|
|
12478
|
+
z = y - b / 200;
|
|
12479
|
+
const y2 = y ** 3;
|
|
12480
|
+
const x2 = x ** 3;
|
|
12481
|
+
const z2 = z ** 3;
|
|
12482
|
+
y = y2 > .008856 ? y2 : (y - 16 / 116) / 7.787;
|
|
12483
|
+
x = x2 > .008856 ? x2 : (x - 16 / 116) / 7.787;
|
|
12484
|
+
z = z2 > .008856 ? z2 : (z - 16 / 116) / 7.787;
|
|
12485
|
+
x *= 95.047;
|
|
12486
|
+
y *= 100;
|
|
12487
|
+
z *= 108.883;
|
|
12488
|
+
return [
|
|
12489
|
+
x,
|
|
12490
|
+
y,
|
|
12491
|
+
z
|
|
12492
|
+
];
|
|
12493
|
+
};
|
|
12494
|
+
convert.lab.lch = function(lab) {
|
|
12495
|
+
const l = lab[0];
|
|
12496
|
+
const a = lab[1];
|
|
12497
|
+
const b = lab[2];
|
|
12498
|
+
let h;
|
|
12499
|
+
h = Math.atan2(b, a) * 360 / 2 / Math.PI;
|
|
12500
|
+
if (h < 0) h += 360;
|
|
12501
|
+
return [
|
|
12502
|
+
l,
|
|
12503
|
+
Math.sqrt(a * a + b * b),
|
|
12504
|
+
h
|
|
12505
|
+
];
|
|
12506
|
+
};
|
|
12507
|
+
convert.lch.lab = function(lch) {
|
|
12508
|
+
const l = lch[0];
|
|
12509
|
+
const c = lch[1];
|
|
12510
|
+
const hr = lch[2] / 360 * 2 * Math.PI;
|
|
12511
|
+
return [
|
|
12512
|
+
l,
|
|
12513
|
+
c * Math.cos(hr),
|
|
12514
|
+
c * Math.sin(hr)
|
|
12515
|
+
];
|
|
12516
|
+
};
|
|
12517
|
+
convert.rgb.ansi16 = function(args, saturation = null) {
|
|
12518
|
+
const [r, g, b] = args;
|
|
12519
|
+
let value = saturation === null ? convert.rgb.hsv(args)[2] : saturation;
|
|
12520
|
+
value = Math.round(value / 50);
|
|
12521
|
+
if (value === 0) return 30;
|
|
12522
|
+
let ansi = 30 + (Math.round(b / 255) << 2 | Math.round(g / 255) << 1 | Math.round(r / 255));
|
|
12523
|
+
if (value === 2) ansi += 60;
|
|
12524
|
+
return ansi;
|
|
12525
|
+
};
|
|
12526
|
+
convert.hsv.ansi16 = function(args) {
|
|
12527
|
+
return convert.rgb.ansi16(convert.hsv.rgb(args), args[2]);
|
|
12528
|
+
};
|
|
12529
|
+
convert.rgb.ansi256 = function(args) {
|
|
12530
|
+
const r = args[0];
|
|
12531
|
+
const g = args[1];
|
|
12532
|
+
const b = args[2];
|
|
12533
|
+
if (r === g && g === b) {
|
|
12534
|
+
if (r < 8) return 16;
|
|
12535
|
+
if (r > 248) return 231;
|
|
12536
|
+
return Math.round((r - 8) / 247 * 24) + 232;
|
|
12537
|
+
}
|
|
12538
|
+
return 16 + 36 * Math.round(r / 255 * 5) + 6 * Math.round(g / 255 * 5) + Math.round(b / 255 * 5);
|
|
12539
|
+
};
|
|
12540
|
+
convert.ansi16.rgb = function(args) {
|
|
12541
|
+
let color = args % 10;
|
|
12542
|
+
if (color === 0 || color === 7) {
|
|
12543
|
+
if (args > 50) color += 3.5;
|
|
12544
|
+
color = color / 10.5 * 255;
|
|
12545
|
+
return [
|
|
12546
|
+
color,
|
|
12547
|
+
color,
|
|
12548
|
+
color
|
|
12549
|
+
];
|
|
12550
|
+
}
|
|
12551
|
+
const mult = (~~(args > 50) + 1) * .5;
|
|
12552
|
+
return [
|
|
12553
|
+
(color & 1) * mult * 255,
|
|
12554
|
+
(color >> 1 & 1) * mult * 255,
|
|
12555
|
+
(color >> 2 & 1) * mult * 255
|
|
12556
|
+
];
|
|
12557
|
+
};
|
|
12558
|
+
convert.ansi256.rgb = function(args) {
|
|
12559
|
+
if (args >= 232) {
|
|
12560
|
+
const c = (args - 232) * 10 + 8;
|
|
12561
|
+
return [
|
|
12562
|
+
c,
|
|
12563
|
+
c,
|
|
12564
|
+
c
|
|
12565
|
+
];
|
|
12566
|
+
}
|
|
12567
|
+
args -= 16;
|
|
12568
|
+
let rem;
|
|
12569
|
+
return [
|
|
12570
|
+
Math.floor(args / 36) / 5 * 255,
|
|
12571
|
+
Math.floor((rem = args % 36) / 6) / 5 * 255,
|
|
12572
|
+
rem % 6 / 5 * 255
|
|
12573
|
+
];
|
|
12574
|
+
};
|
|
12575
|
+
convert.rgb.hex = function(args) {
|
|
12576
|
+
const string = (((Math.round(args[0]) & 255) << 16) + ((Math.round(args[1]) & 255) << 8) + (Math.round(args[2]) & 255)).toString(16).toUpperCase();
|
|
12577
|
+
return "000000".substring(string.length) + string;
|
|
12578
|
+
};
|
|
12579
|
+
convert.hex.rgb = function(args) {
|
|
12580
|
+
const match = args.toString(16).match(/[a-f0-9]{6}|[a-f0-9]{3}/i);
|
|
12581
|
+
if (!match) return [
|
|
12582
|
+
0,
|
|
12583
|
+
0,
|
|
12584
|
+
0
|
|
12585
|
+
];
|
|
12586
|
+
let colorString = match[0];
|
|
12587
|
+
if (match[0].length === 3) colorString = colorString.split("").map((char) => {
|
|
12588
|
+
return char + char;
|
|
12589
|
+
}).join("");
|
|
12590
|
+
const integer = parseInt(colorString, 16);
|
|
12591
|
+
return [
|
|
12592
|
+
integer >> 16 & 255,
|
|
12593
|
+
integer >> 8 & 255,
|
|
12594
|
+
integer & 255
|
|
12595
|
+
];
|
|
12596
|
+
};
|
|
12597
|
+
convert.rgb.hcg = function(rgb) {
|
|
12598
|
+
const r = rgb[0] / 255;
|
|
12599
|
+
const g = rgb[1] / 255;
|
|
12600
|
+
const b = rgb[2] / 255;
|
|
12601
|
+
const max = Math.max(Math.max(r, g), b);
|
|
12602
|
+
const min = Math.min(Math.min(r, g), b);
|
|
12603
|
+
const chroma = max - min;
|
|
12604
|
+
let grayscale;
|
|
12605
|
+
let hue;
|
|
12606
|
+
if (chroma < 1) grayscale = min / (1 - chroma);
|
|
12607
|
+
else grayscale = 0;
|
|
12608
|
+
if (chroma <= 0) hue = 0;
|
|
12609
|
+
else if (max === r) hue = (g - b) / chroma % 6;
|
|
12610
|
+
else if (max === g) hue = 2 + (b - r) / chroma;
|
|
12611
|
+
else hue = 4 + (r - g) / chroma;
|
|
12612
|
+
hue /= 6;
|
|
12613
|
+
hue %= 1;
|
|
12614
|
+
return [
|
|
12615
|
+
hue * 360,
|
|
12616
|
+
chroma * 100,
|
|
12617
|
+
grayscale * 100
|
|
12618
|
+
];
|
|
12619
|
+
};
|
|
12620
|
+
convert.hsl.hcg = function(hsl) {
|
|
12621
|
+
const s = hsl[1] / 100;
|
|
12622
|
+
const l = hsl[2] / 100;
|
|
12623
|
+
const c = l < .5 ? 2 * s * l : 2 * s * (1 - l);
|
|
12624
|
+
let f = 0;
|
|
12625
|
+
if (c < 1) f = (l - .5 * c) / (1 - c);
|
|
12626
|
+
return [
|
|
12627
|
+
hsl[0],
|
|
12628
|
+
c * 100,
|
|
12629
|
+
f * 100
|
|
12630
|
+
];
|
|
12631
|
+
};
|
|
12632
|
+
convert.hsv.hcg = function(hsv) {
|
|
12633
|
+
const s = hsv[1] / 100;
|
|
12634
|
+
const v = hsv[2] / 100;
|
|
12635
|
+
const c = s * v;
|
|
12636
|
+
let f = 0;
|
|
12637
|
+
if (c < 1) f = (v - c) / (1 - c);
|
|
12638
|
+
return [
|
|
12639
|
+
hsv[0],
|
|
12640
|
+
c * 100,
|
|
12641
|
+
f * 100
|
|
12642
|
+
];
|
|
12643
|
+
};
|
|
12644
|
+
convert.hcg.rgb = function(hcg) {
|
|
12645
|
+
const h = hcg[0] / 360;
|
|
12646
|
+
const c = hcg[1] / 100;
|
|
12647
|
+
const g = hcg[2] / 100;
|
|
12648
|
+
if (c === 0) return [
|
|
12649
|
+
g * 255,
|
|
12650
|
+
g * 255,
|
|
12651
|
+
g * 255
|
|
12652
|
+
];
|
|
12653
|
+
const pure = [
|
|
12654
|
+
0,
|
|
12655
|
+
0,
|
|
12656
|
+
0
|
|
12657
|
+
];
|
|
12658
|
+
const hi = h % 1 * 6;
|
|
12659
|
+
const v = hi % 1;
|
|
12660
|
+
const w = 1 - v;
|
|
12661
|
+
let mg = 0;
|
|
12662
|
+
switch (Math.floor(hi)) {
|
|
12663
|
+
case 0:
|
|
12664
|
+
pure[0] = 1;
|
|
12665
|
+
pure[1] = v;
|
|
12666
|
+
pure[2] = 0;
|
|
12667
|
+
break;
|
|
12668
|
+
case 1:
|
|
12669
|
+
pure[0] = w;
|
|
12670
|
+
pure[1] = 1;
|
|
12671
|
+
pure[2] = 0;
|
|
12672
|
+
break;
|
|
12673
|
+
case 2:
|
|
12674
|
+
pure[0] = 0;
|
|
12675
|
+
pure[1] = 1;
|
|
12676
|
+
pure[2] = v;
|
|
12677
|
+
break;
|
|
12678
|
+
case 3:
|
|
12679
|
+
pure[0] = 0;
|
|
12680
|
+
pure[1] = w;
|
|
12681
|
+
pure[2] = 1;
|
|
12682
|
+
break;
|
|
12683
|
+
case 4:
|
|
12684
|
+
pure[0] = v;
|
|
12685
|
+
pure[1] = 0;
|
|
12686
|
+
pure[2] = 1;
|
|
12687
|
+
break;
|
|
12688
|
+
default:
|
|
12689
|
+
pure[0] = 1;
|
|
12690
|
+
pure[1] = 0;
|
|
12691
|
+
pure[2] = w;
|
|
12692
|
+
}
|
|
12693
|
+
mg = (1 - c) * g;
|
|
12694
|
+
return [
|
|
12695
|
+
(c * pure[0] + mg) * 255,
|
|
12696
|
+
(c * pure[1] + mg) * 255,
|
|
12697
|
+
(c * pure[2] + mg) * 255
|
|
12698
|
+
];
|
|
12699
|
+
};
|
|
12700
|
+
convert.hcg.hsv = function(hcg) {
|
|
12701
|
+
const c = hcg[1] / 100;
|
|
12702
|
+
const v = c + hcg[2] / 100 * (1 - c);
|
|
12703
|
+
let f = 0;
|
|
12704
|
+
if (v > 0) f = c / v;
|
|
12705
|
+
return [
|
|
12706
|
+
hcg[0],
|
|
12707
|
+
f * 100,
|
|
12708
|
+
v * 100
|
|
12709
|
+
];
|
|
12710
|
+
};
|
|
12711
|
+
convert.hcg.hsl = function(hcg) {
|
|
12712
|
+
const c = hcg[1] / 100;
|
|
12713
|
+
const l = hcg[2] / 100 * (1 - c) + .5 * c;
|
|
12714
|
+
let s = 0;
|
|
12715
|
+
if (l > 0 && l < .5) s = c / (2 * l);
|
|
12716
|
+
else if (l >= .5 && l < 1) s = c / (2 * (1 - l));
|
|
12717
|
+
return [
|
|
12718
|
+
hcg[0],
|
|
12719
|
+
s * 100,
|
|
12720
|
+
l * 100
|
|
12721
|
+
];
|
|
12722
|
+
};
|
|
12723
|
+
convert.hcg.hwb = function(hcg) {
|
|
12724
|
+
const c = hcg[1] / 100;
|
|
12725
|
+
const v = c + hcg[2] / 100 * (1 - c);
|
|
12726
|
+
return [
|
|
12727
|
+
hcg[0],
|
|
12728
|
+
(v - c) * 100,
|
|
12729
|
+
(1 - v) * 100
|
|
12730
|
+
];
|
|
12731
|
+
};
|
|
12732
|
+
convert.hwb.hcg = function(hwb) {
|
|
12733
|
+
const w = hwb[1] / 100;
|
|
12734
|
+
const v = 1 - hwb[2] / 100;
|
|
12735
|
+
const c = v - w;
|
|
12736
|
+
let g = 0;
|
|
12737
|
+
if (c < 1) g = (v - c) / (1 - c);
|
|
12738
|
+
return [
|
|
12739
|
+
hwb[0],
|
|
12740
|
+
c * 100,
|
|
12741
|
+
g * 100
|
|
12742
|
+
];
|
|
12743
|
+
};
|
|
12744
|
+
convert.apple.rgb = function(apple) {
|
|
12745
|
+
return [
|
|
12746
|
+
apple[0] / 65535 * 255,
|
|
12747
|
+
apple[1] / 65535 * 255,
|
|
12748
|
+
apple[2] / 65535 * 255
|
|
12749
|
+
];
|
|
12750
|
+
};
|
|
12751
|
+
convert.rgb.apple = function(rgb) {
|
|
12752
|
+
return [
|
|
12753
|
+
rgb[0] / 255 * 65535,
|
|
12754
|
+
rgb[1] / 255 * 65535,
|
|
12755
|
+
rgb[2] / 255 * 65535
|
|
12756
|
+
];
|
|
12757
|
+
};
|
|
12758
|
+
convert.gray.rgb = function(args) {
|
|
12759
|
+
return [
|
|
12760
|
+
args[0] / 100 * 255,
|
|
12761
|
+
args[0] / 100 * 255,
|
|
12762
|
+
args[0] / 100 * 255
|
|
12763
|
+
];
|
|
12764
|
+
};
|
|
12765
|
+
convert.gray.hsl = function(args) {
|
|
12766
|
+
return [
|
|
12767
|
+
0,
|
|
12768
|
+
0,
|
|
12769
|
+
args[0]
|
|
12770
|
+
];
|
|
12771
|
+
};
|
|
12772
|
+
convert.gray.hsv = convert.gray.hsl;
|
|
12773
|
+
convert.gray.hwb = function(gray) {
|
|
12774
|
+
return [
|
|
12775
|
+
0,
|
|
12776
|
+
100,
|
|
12777
|
+
gray[0]
|
|
12778
|
+
];
|
|
12779
|
+
};
|
|
12780
|
+
convert.gray.cmyk = function(gray) {
|
|
12781
|
+
return [
|
|
12782
|
+
0,
|
|
12783
|
+
0,
|
|
12784
|
+
0,
|
|
12785
|
+
gray[0]
|
|
12786
|
+
];
|
|
12787
|
+
};
|
|
12788
|
+
convert.gray.lab = function(gray) {
|
|
12789
|
+
return [
|
|
12790
|
+
gray[0],
|
|
12791
|
+
0,
|
|
12792
|
+
0
|
|
12793
|
+
];
|
|
12794
|
+
};
|
|
12795
|
+
convert.gray.hex = function(gray) {
|
|
12796
|
+
const val = Math.round(gray[0] / 100 * 255) & 255;
|
|
12797
|
+
const string = ((val << 16) + (val << 8) + val).toString(16).toUpperCase();
|
|
12798
|
+
return "000000".substring(string.length) + string;
|
|
12799
|
+
};
|
|
12800
|
+
convert.rgb.gray = function(rgb) {
|
|
12801
|
+
return [(rgb[0] + rgb[1] + rgb[2]) / 3 / 255 * 100];
|
|
12802
|
+
};
|
|
12803
|
+
}));
|
|
12804
|
+
//#endregion
|
|
12805
|
+
//#region ../../node_modules/.pnpm/color-convert@2.0.1/node_modules/color-convert/route.js
|
|
12806
|
+
var require_route = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
12807
|
+
var conversions = require_conversions();
|
|
12808
|
+
function buildGraph() {
|
|
12809
|
+
const graph = {};
|
|
12810
|
+
const models = Object.keys(conversions);
|
|
12811
|
+
for (let len = models.length, i = 0; i < len; i++) graph[models[i]] = {
|
|
12812
|
+
distance: -1,
|
|
12813
|
+
parent: null
|
|
12814
|
+
};
|
|
12815
|
+
return graph;
|
|
12816
|
+
}
|
|
12817
|
+
function deriveBFS(fromModel) {
|
|
12818
|
+
const graph = buildGraph();
|
|
12819
|
+
const queue = [fromModel];
|
|
12820
|
+
graph[fromModel].distance = 0;
|
|
12821
|
+
while (queue.length) {
|
|
12822
|
+
const current = queue.pop();
|
|
12823
|
+
const adjacents = Object.keys(conversions[current]);
|
|
12824
|
+
for (let len = adjacents.length, i = 0; i < len; i++) {
|
|
12825
|
+
const adjacent = adjacents[i];
|
|
12826
|
+
const node = graph[adjacent];
|
|
12827
|
+
if (node.distance === -1) {
|
|
12828
|
+
node.distance = graph[current].distance + 1;
|
|
12829
|
+
node.parent = current;
|
|
12830
|
+
queue.unshift(adjacent);
|
|
12831
|
+
}
|
|
12832
|
+
}
|
|
12833
|
+
}
|
|
12834
|
+
return graph;
|
|
12835
|
+
}
|
|
12836
|
+
function link(from, to) {
|
|
12837
|
+
return function(args) {
|
|
12838
|
+
return to(from(args));
|
|
12839
|
+
};
|
|
12840
|
+
}
|
|
12841
|
+
function wrapConversion(toModel, graph) {
|
|
12842
|
+
const path = [graph[toModel].parent, toModel];
|
|
12843
|
+
let fn = conversions[graph[toModel].parent][toModel];
|
|
12844
|
+
let cur = graph[toModel].parent;
|
|
12845
|
+
while (graph[cur].parent) {
|
|
12846
|
+
path.unshift(graph[cur].parent);
|
|
12847
|
+
fn = link(conversions[graph[cur].parent][cur], fn);
|
|
12848
|
+
cur = graph[cur].parent;
|
|
12849
|
+
}
|
|
12850
|
+
fn.conversion = path;
|
|
12851
|
+
return fn;
|
|
12852
|
+
}
|
|
12853
|
+
module.exports = function(fromModel) {
|
|
12854
|
+
const graph = deriveBFS(fromModel);
|
|
12855
|
+
const conversion = {};
|
|
12856
|
+
const models = Object.keys(graph);
|
|
12857
|
+
for (let len = models.length, i = 0; i < len; i++) {
|
|
12858
|
+
const toModel = models[i];
|
|
12859
|
+
if (graph[toModel].parent === null) continue;
|
|
12860
|
+
conversion[toModel] = wrapConversion(toModel, graph);
|
|
12861
|
+
}
|
|
12862
|
+
return conversion;
|
|
12863
|
+
};
|
|
12864
|
+
}));
|
|
12865
|
+
//#endregion
|
|
12866
|
+
//#region ../../node_modules/.pnpm/color-convert@2.0.1/node_modules/color-convert/index.js
|
|
12867
|
+
var require_color_convert = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
12868
|
+
var conversions = require_conversions();
|
|
12869
|
+
var route = require_route();
|
|
12870
|
+
var convert = {};
|
|
12871
|
+
var models = Object.keys(conversions);
|
|
12872
|
+
function wrapRaw(fn) {
|
|
12873
|
+
const wrappedFn = function(...args) {
|
|
12874
|
+
const arg0 = args[0];
|
|
12875
|
+
if (arg0 === void 0 || arg0 === null) return arg0;
|
|
12876
|
+
if (arg0.length > 1) args = arg0;
|
|
12877
|
+
return fn(args);
|
|
12878
|
+
};
|
|
12879
|
+
if ("conversion" in fn) wrappedFn.conversion = fn.conversion;
|
|
12880
|
+
return wrappedFn;
|
|
12881
|
+
}
|
|
12882
|
+
function wrapRounded(fn) {
|
|
12883
|
+
const wrappedFn = function(...args) {
|
|
12884
|
+
const arg0 = args[0];
|
|
12885
|
+
if (arg0 === void 0 || arg0 === null) return arg0;
|
|
12886
|
+
if (arg0.length > 1) args = arg0;
|
|
12887
|
+
const result = fn(args);
|
|
12888
|
+
if (typeof result === "object") for (let len = result.length, i = 0; i < len; i++) result[i] = Math.round(result[i]);
|
|
12889
|
+
return result;
|
|
12890
|
+
};
|
|
12891
|
+
if ("conversion" in fn) wrappedFn.conversion = fn.conversion;
|
|
12892
|
+
return wrappedFn;
|
|
12893
|
+
}
|
|
12894
|
+
models.forEach((fromModel) => {
|
|
12895
|
+
convert[fromModel] = {};
|
|
12896
|
+
Object.defineProperty(convert[fromModel], "channels", { value: conversions[fromModel].channels });
|
|
12897
|
+
Object.defineProperty(convert[fromModel], "labels", { value: conversions[fromModel].labels });
|
|
12898
|
+
const routes = route(fromModel);
|
|
12899
|
+
Object.keys(routes).forEach((toModel) => {
|
|
12900
|
+
const fn = routes[toModel];
|
|
12901
|
+
convert[fromModel][toModel] = wrapRounded(fn);
|
|
12902
|
+
convert[fromModel][toModel].raw = wrapRaw(fn);
|
|
12903
|
+
});
|
|
12904
|
+
});
|
|
12905
|
+
module.exports = convert;
|
|
12906
|
+
}));
|
|
12907
|
+
//#endregion
|
|
12908
|
+
//#region ../../node_modules/.pnpm/ansi-styles@4.3.0/node_modules/ansi-styles/index.js
|
|
12909
|
+
var require_ansi_styles = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
12910
|
+
var wrapAnsi16 = (fn, offset) => (...args) => {
|
|
12911
|
+
return `\u001B[${fn(...args) + offset}m`;
|
|
12912
|
+
};
|
|
12913
|
+
var wrapAnsi256 = (fn, offset) => (...args) => {
|
|
12914
|
+
const code = fn(...args);
|
|
12915
|
+
return `\u001B[${38 + offset};5;${code}m`;
|
|
12916
|
+
};
|
|
12917
|
+
var wrapAnsi16m = (fn, offset) => (...args) => {
|
|
12918
|
+
const rgb = fn(...args);
|
|
12919
|
+
return `\u001B[${38 + offset};2;${rgb[0]};${rgb[1]};${rgb[2]}m`;
|
|
12920
|
+
};
|
|
12921
|
+
var ansi2ansi = (n) => n;
|
|
12922
|
+
var rgb2rgb = (r, g, b) => [
|
|
12923
|
+
r,
|
|
12924
|
+
g,
|
|
12925
|
+
b
|
|
12926
|
+
];
|
|
12927
|
+
var setLazyProperty = (object, property, get) => {
|
|
12928
|
+
Object.defineProperty(object, property, {
|
|
12929
|
+
get: () => {
|
|
12930
|
+
const value = get();
|
|
12931
|
+
Object.defineProperty(object, property, {
|
|
12932
|
+
value,
|
|
12933
|
+
enumerable: true,
|
|
12934
|
+
configurable: true
|
|
12935
|
+
});
|
|
12936
|
+
return value;
|
|
12937
|
+
},
|
|
12938
|
+
enumerable: true,
|
|
12939
|
+
configurable: true
|
|
12940
|
+
});
|
|
12941
|
+
};
|
|
12942
|
+
/** @type {typeof import('color-convert')} */
|
|
12943
|
+
var colorConvert;
|
|
12944
|
+
var makeDynamicStyles = (wrap, targetSpace, identity, isBackground) => {
|
|
12945
|
+
if (colorConvert === void 0) colorConvert = require_color_convert();
|
|
12946
|
+
const offset = isBackground ? 10 : 0;
|
|
12947
|
+
const styles = {};
|
|
12948
|
+
for (const [sourceSpace, suite] of Object.entries(colorConvert)) {
|
|
12949
|
+
const name = sourceSpace === "ansi16" ? "ansi" : sourceSpace;
|
|
12950
|
+
if (sourceSpace === targetSpace) styles[name] = wrap(identity, offset);
|
|
12951
|
+
else if (typeof suite === "object") styles[name] = wrap(suite[targetSpace], offset);
|
|
12952
|
+
}
|
|
12953
|
+
return styles;
|
|
12954
|
+
};
|
|
12955
|
+
function assembleStyles() {
|
|
12956
|
+
const codes = /* @__PURE__ */ new Map();
|
|
12957
|
+
const styles = {
|
|
12958
|
+
modifier: {
|
|
12959
|
+
reset: [0, 0],
|
|
12960
|
+
bold: [1, 22],
|
|
12961
|
+
dim: [2, 22],
|
|
12962
|
+
italic: [3, 23],
|
|
12963
|
+
underline: [4, 24],
|
|
12964
|
+
inverse: [7, 27],
|
|
12965
|
+
hidden: [8, 28],
|
|
12966
|
+
strikethrough: [9, 29]
|
|
12967
|
+
},
|
|
12968
|
+
color: {
|
|
12969
|
+
black: [30, 39],
|
|
12970
|
+
red: [31, 39],
|
|
12971
|
+
green: [32, 39],
|
|
12972
|
+
yellow: [33, 39],
|
|
12973
|
+
blue: [34, 39],
|
|
12974
|
+
magenta: [35, 39],
|
|
12975
|
+
cyan: [36, 39],
|
|
12976
|
+
white: [37, 39],
|
|
12977
|
+
blackBright: [90, 39],
|
|
12978
|
+
redBright: [91, 39],
|
|
12979
|
+
greenBright: [92, 39],
|
|
12980
|
+
yellowBright: [93, 39],
|
|
12981
|
+
blueBright: [94, 39],
|
|
12982
|
+
magentaBright: [95, 39],
|
|
12983
|
+
cyanBright: [96, 39],
|
|
12984
|
+
whiteBright: [97, 39]
|
|
12985
|
+
},
|
|
12986
|
+
bgColor: {
|
|
12987
|
+
bgBlack: [40, 49],
|
|
12988
|
+
bgRed: [41, 49],
|
|
12989
|
+
bgGreen: [42, 49],
|
|
12990
|
+
bgYellow: [43, 49],
|
|
12991
|
+
bgBlue: [44, 49],
|
|
12992
|
+
bgMagenta: [45, 49],
|
|
12993
|
+
bgCyan: [46, 49],
|
|
12994
|
+
bgWhite: [47, 49],
|
|
12995
|
+
bgBlackBright: [100, 49],
|
|
12996
|
+
bgRedBright: [101, 49],
|
|
12997
|
+
bgGreenBright: [102, 49],
|
|
12998
|
+
bgYellowBright: [103, 49],
|
|
12999
|
+
bgBlueBright: [104, 49],
|
|
13000
|
+
bgMagentaBright: [105, 49],
|
|
13001
|
+
bgCyanBright: [106, 49],
|
|
13002
|
+
bgWhiteBright: [107, 49]
|
|
13003
|
+
}
|
|
13004
|
+
};
|
|
13005
|
+
styles.color.gray = styles.color.blackBright;
|
|
13006
|
+
styles.bgColor.bgGray = styles.bgColor.bgBlackBright;
|
|
13007
|
+
styles.color.grey = styles.color.blackBright;
|
|
13008
|
+
styles.bgColor.bgGrey = styles.bgColor.bgBlackBright;
|
|
13009
|
+
for (const [groupName, group] of Object.entries(styles)) {
|
|
13010
|
+
for (const [styleName, style] of Object.entries(group)) {
|
|
13011
|
+
styles[styleName] = {
|
|
13012
|
+
open: `\u001B[${style[0]}m`,
|
|
13013
|
+
close: `\u001B[${style[1]}m`
|
|
13014
|
+
};
|
|
13015
|
+
group[styleName] = styles[styleName];
|
|
13016
|
+
codes.set(style[0], style[1]);
|
|
13017
|
+
}
|
|
13018
|
+
Object.defineProperty(styles, groupName, {
|
|
13019
|
+
value: group,
|
|
13020
|
+
enumerable: false
|
|
13021
|
+
});
|
|
13022
|
+
}
|
|
13023
|
+
Object.defineProperty(styles, "codes", {
|
|
13024
|
+
value: codes,
|
|
13025
|
+
enumerable: false
|
|
13026
|
+
});
|
|
13027
|
+
styles.color.close = "\x1B[39m";
|
|
13028
|
+
styles.bgColor.close = "\x1B[49m";
|
|
13029
|
+
setLazyProperty(styles.color, "ansi", () => makeDynamicStyles(wrapAnsi16, "ansi16", ansi2ansi, false));
|
|
13030
|
+
setLazyProperty(styles.color, "ansi256", () => makeDynamicStyles(wrapAnsi256, "ansi256", ansi2ansi, false));
|
|
13031
|
+
setLazyProperty(styles.color, "ansi16m", () => makeDynamicStyles(wrapAnsi16m, "rgb", rgb2rgb, false));
|
|
13032
|
+
setLazyProperty(styles.bgColor, "ansi", () => makeDynamicStyles(wrapAnsi16, "ansi16", ansi2ansi, true));
|
|
13033
|
+
setLazyProperty(styles.bgColor, "ansi256", () => makeDynamicStyles(wrapAnsi256, "ansi256", ansi2ansi, true));
|
|
13034
|
+
setLazyProperty(styles.bgColor, "ansi16m", () => makeDynamicStyles(wrapAnsi16m, "rgb", rgb2rgb, true));
|
|
13035
|
+
return styles;
|
|
13036
|
+
}
|
|
13037
|
+
Object.defineProperty(module, "exports", {
|
|
13038
|
+
enumerable: true,
|
|
13039
|
+
get: assembleStyles
|
|
13040
|
+
});
|
|
13041
|
+
}));
|
|
13042
|
+
//#endregion
|
|
13043
|
+
//#region ../../node_modules/.pnpm/supports-color@7.2.0/node_modules/supports-color/browser.js
|
|
13044
|
+
var require_browser = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
13045
|
+
module.exports = {
|
|
13046
|
+
stdout: false,
|
|
13047
|
+
stderr: false
|
|
13048
|
+
};
|
|
13049
|
+
}));
|
|
13050
|
+
//#endregion
|
|
13051
|
+
//#region ../../node_modules/.pnpm/chalk@4.1.2/node_modules/chalk/source/util.js
|
|
13052
|
+
var require_util = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
13053
|
+
var stringReplaceAll = (string, substring, replacer) => {
|
|
13054
|
+
let index = string.indexOf(substring);
|
|
13055
|
+
if (index === -1) return string;
|
|
13056
|
+
const substringLength = substring.length;
|
|
13057
|
+
let endIndex = 0;
|
|
13058
|
+
let returnValue = "";
|
|
13059
|
+
do {
|
|
13060
|
+
returnValue += string.substr(endIndex, index - endIndex) + substring + replacer;
|
|
13061
|
+
endIndex = index + substringLength;
|
|
13062
|
+
index = string.indexOf(substring, endIndex);
|
|
13063
|
+
} while (index !== -1);
|
|
13064
|
+
returnValue += string.substr(endIndex);
|
|
13065
|
+
return returnValue;
|
|
13066
|
+
};
|
|
13067
|
+
var stringEncaseCRLFWithFirstIndex = (string, prefix, postfix, index) => {
|
|
13068
|
+
let endIndex = 0;
|
|
13069
|
+
let returnValue = "";
|
|
13070
|
+
do {
|
|
13071
|
+
const gotCR = string[index - 1] === "\r";
|
|
13072
|
+
returnValue += string.substr(endIndex, (gotCR ? index - 1 : index) - endIndex) + prefix + (gotCR ? "\r\n" : "\n") + postfix;
|
|
13073
|
+
endIndex = index + 1;
|
|
13074
|
+
index = string.indexOf("\n", endIndex);
|
|
13075
|
+
} while (index !== -1);
|
|
13076
|
+
returnValue += string.substr(endIndex);
|
|
13077
|
+
return returnValue;
|
|
13078
|
+
};
|
|
13079
|
+
module.exports = {
|
|
13080
|
+
stringReplaceAll,
|
|
13081
|
+
stringEncaseCRLFWithFirstIndex
|
|
13082
|
+
};
|
|
13083
|
+
}));
|
|
13084
|
+
//#endregion
|
|
13085
|
+
//#region ../../node_modules/.pnpm/chalk@4.1.2/node_modules/chalk/source/templates.js
|
|
13086
|
+
var require_templates = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
13087
|
+
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;
|
|
13088
|
+
var STYLE_REGEX = /(?:^|\.)(\w+)(?:\(([^)]*)\))?/g;
|
|
13089
|
+
var STRING_REGEX = /^(['"])((?:\\.|(?!\1)[^\\])*)\1$/;
|
|
13090
|
+
var ESCAPE_REGEX = /\\(u(?:[a-f\d]{4}|{[a-f\d]{1,6}})|x[a-f\d]{2}|.)|([^\\])/gi;
|
|
13091
|
+
var ESCAPES = new Map([
|
|
13092
|
+
["n", "\n"],
|
|
13093
|
+
["r", "\r"],
|
|
13094
|
+
["t", " "],
|
|
13095
|
+
["b", "\b"],
|
|
13096
|
+
["f", "\f"],
|
|
13097
|
+
["v", "\v"],
|
|
13098
|
+
["0", "\0"],
|
|
13099
|
+
["\\", "\\"],
|
|
13100
|
+
["e", "\x1B"],
|
|
13101
|
+
["a", "\x07"]
|
|
13102
|
+
]);
|
|
13103
|
+
function unescape(c) {
|
|
13104
|
+
const u = c[0] === "u";
|
|
13105
|
+
const bracket = c[1] === "{";
|
|
13106
|
+
if (u && !bracket && c.length === 5 || c[0] === "x" && c.length === 3) return String.fromCharCode(parseInt(c.slice(1), 16));
|
|
13107
|
+
if (u && bracket) return String.fromCodePoint(parseInt(c.slice(2, -1), 16));
|
|
13108
|
+
return ESCAPES.get(c) || c;
|
|
13109
|
+
}
|
|
13110
|
+
function parseArguments(name, arguments_) {
|
|
13111
|
+
const results = [];
|
|
13112
|
+
const chunks = arguments_.trim().split(/\s*,\s*/g);
|
|
13113
|
+
let matches;
|
|
13114
|
+
for (const chunk of chunks) {
|
|
13115
|
+
const number = Number(chunk);
|
|
13116
|
+
if (!Number.isNaN(number)) results.push(number);
|
|
13117
|
+
else if (matches = chunk.match(STRING_REGEX)) results.push(matches[2].replace(ESCAPE_REGEX, (m, escape, character) => escape ? unescape(escape) : character));
|
|
13118
|
+
else throw new Error(`Invalid Chalk template style argument: ${chunk} (in style '${name}')`);
|
|
13119
|
+
}
|
|
13120
|
+
return results;
|
|
13121
|
+
}
|
|
13122
|
+
function parseStyle(style) {
|
|
13123
|
+
STYLE_REGEX.lastIndex = 0;
|
|
13124
|
+
const results = [];
|
|
13125
|
+
let matches;
|
|
13126
|
+
while ((matches = STYLE_REGEX.exec(style)) !== null) {
|
|
13127
|
+
const name = matches[1];
|
|
13128
|
+
if (matches[2]) {
|
|
13129
|
+
const args = parseArguments(name, matches[2]);
|
|
13130
|
+
results.push([name].concat(args));
|
|
13131
|
+
} else results.push([name]);
|
|
13132
|
+
}
|
|
13133
|
+
return results;
|
|
13134
|
+
}
|
|
13135
|
+
function buildStyle(chalk, styles) {
|
|
13136
|
+
const enabled = {};
|
|
13137
|
+
for (const layer of styles) for (const style of layer.styles) enabled[style[0]] = layer.inverse ? null : style.slice(1);
|
|
13138
|
+
let current = chalk;
|
|
13139
|
+
for (const [styleName, styles] of Object.entries(enabled)) {
|
|
13140
|
+
if (!Array.isArray(styles)) continue;
|
|
13141
|
+
if (!(styleName in current)) throw new Error(`Unknown Chalk style: ${styleName}`);
|
|
13142
|
+
current = styles.length > 0 ? current[styleName](...styles) : current[styleName];
|
|
13143
|
+
}
|
|
13144
|
+
return current;
|
|
13145
|
+
}
|
|
13146
|
+
module.exports = (chalk, temporary) => {
|
|
13147
|
+
const styles = [];
|
|
13148
|
+
const chunks = [];
|
|
13149
|
+
let chunk = [];
|
|
13150
|
+
temporary.replace(TEMPLATE_REGEX, (m, escapeCharacter, inverse, style, close, character) => {
|
|
13151
|
+
if (escapeCharacter) chunk.push(unescape(escapeCharacter));
|
|
13152
|
+
else if (style) {
|
|
13153
|
+
const string = chunk.join("");
|
|
13154
|
+
chunk = [];
|
|
13155
|
+
chunks.push(styles.length === 0 ? string : buildStyle(chalk, styles)(string));
|
|
13156
|
+
styles.push({
|
|
13157
|
+
inverse,
|
|
13158
|
+
styles: parseStyle(style)
|
|
13159
|
+
});
|
|
13160
|
+
} else if (close) {
|
|
13161
|
+
if (styles.length === 0) throw new Error("Found extraneous } in Chalk template literal");
|
|
13162
|
+
chunks.push(buildStyle(chalk, styles)(chunk.join("")));
|
|
13163
|
+
chunk = [];
|
|
13164
|
+
styles.pop();
|
|
13165
|
+
} else chunk.push(character);
|
|
13166
|
+
});
|
|
13167
|
+
chunks.push(chunk.join(""));
|
|
13168
|
+
if (styles.length > 0) {
|
|
13169
|
+
const errMessage = `Chalk template literal is missing ${styles.length} closing bracket${styles.length === 1 ? "" : "s"} (\`}\`)`;
|
|
13170
|
+
throw new Error(errMessage);
|
|
13171
|
+
}
|
|
13172
|
+
return chunks.join("");
|
|
13173
|
+
};
|
|
13174
|
+
}));
|
|
13175
|
+
(/* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
13176
|
+
var ansiStyles = require_ansi_styles();
|
|
13177
|
+
var { stdout: stdoutColor, stderr: stderrColor } = require_browser();
|
|
13178
|
+
var { stringReplaceAll, stringEncaseCRLFWithFirstIndex } = require_util();
|
|
13179
|
+
var { isArray } = Array;
|
|
13180
|
+
var levelMapping = [
|
|
13181
|
+
"ansi",
|
|
13182
|
+
"ansi",
|
|
13183
|
+
"ansi256",
|
|
13184
|
+
"ansi16m"
|
|
13185
|
+
];
|
|
13186
|
+
var styles = Object.create(null);
|
|
13187
|
+
var applyOptions = (object, options = {}) => {
|
|
13188
|
+
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");
|
|
13189
|
+
const colorLevel = stdoutColor ? stdoutColor.level : 0;
|
|
13190
|
+
object.level = options.level === void 0 ? colorLevel : options.level;
|
|
13191
|
+
};
|
|
13192
|
+
var ChalkClass = class {
|
|
13193
|
+
constructor(options) {
|
|
13194
|
+
return chalkFactory(options);
|
|
13195
|
+
}
|
|
13196
|
+
};
|
|
13197
|
+
var chalkFactory = (options) => {
|
|
13198
|
+
const chalk = {};
|
|
13199
|
+
applyOptions(chalk, options);
|
|
13200
|
+
chalk.template = (...arguments_) => chalkTag(chalk.template, ...arguments_);
|
|
13201
|
+
Object.setPrototypeOf(chalk, Chalk.prototype);
|
|
13202
|
+
Object.setPrototypeOf(chalk.template, chalk);
|
|
13203
|
+
chalk.template.constructor = () => {
|
|
13204
|
+
throw new Error("`chalk.constructor()` is deprecated. Use `new chalk.Instance()` instead.");
|
|
13205
|
+
};
|
|
13206
|
+
chalk.template.Instance = ChalkClass;
|
|
13207
|
+
return chalk.template;
|
|
13208
|
+
};
|
|
13209
|
+
function Chalk(options) {
|
|
13210
|
+
return chalkFactory(options);
|
|
13211
|
+
}
|
|
13212
|
+
for (const [styleName, style] of Object.entries(ansiStyles)) styles[styleName] = { get() {
|
|
13213
|
+
const builder = createBuilder(this, createStyler(style.open, style.close, this._styler), this._isEmpty);
|
|
13214
|
+
Object.defineProperty(this, styleName, { value: builder });
|
|
13215
|
+
return builder;
|
|
13216
|
+
} };
|
|
13217
|
+
styles.visible = { get() {
|
|
13218
|
+
const builder = createBuilder(this, this._styler, true);
|
|
13219
|
+
Object.defineProperty(this, "visible", { value: builder });
|
|
13220
|
+
return builder;
|
|
13221
|
+
} };
|
|
13222
|
+
var usedModels = [
|
|
13223
|
+
"rgb",
|
|
13224
|
+
"hex",
|
|
13225
|
+
"keyword",
|
|
13226
|
+
"hsl",
|
|
13227
|
+
"hsv",
|
|
13228
|
+
"hwb",
|
|
13229
|
+
"ansi",
|
|
13230
|
+
"ansi256"
|
|
13231
|
+
];
|
|
13232
|
+
for (const model of usedModels) styles[model] = { get() {
|
|
13233
|
+
const { level } = this;
|
|
13234
|
+
return function(...arguments_) {
|
|
13235
|
+
const styler = createStyler(ansiStyles.color[levelMapping[level]][model](...arguments_), ansiStyles.color.close, this._styler);
|
|
13236
|
+
return createBuilder(this, styler, this._isEmpty);
|
|
13237
|
+
};
|
|
13238
|
+
} };
|
|
13239
|
+
for (const model of usedModels) {
|
|
13240
|
+
const bgModel = "bg" + model[0].toUpperCase() + model.slice(1);
|
|
13241
|
+
styles[bgModel] = { get() {
|
|
13242
|
+
const { level } = this;
|
|
13243
|
+
return function(...arguments_) {
|
|
13244
|
+
const styler = createStyler(ansiStyles.bgColor[levelMapping[level]][model](...arguments_), ansiStyles.bgColor.close, this._styler);
|
|
13245
|
+
return createBuilder(this, styler, this._isEmpty);
|
|
13246
|
+
};
|
|
13247
|
+
} };
|
|
13248
|
+
}
|
|
13249
|
+
var proto = Object.defineProperties(() => {}, {
|
|
13250
|
+
...styles,
|
|
13251
|
+
level: {
|
|
13252
|
+
enumerable: true,
|
|
13253
|
+
get() {
|
|
13254
|
+
return this._generator.level;
|
|
13255
|
+
},
|
|
13256
|
+
set(level) {
|
|
13257
|
+
this._generator.level = level;
|
|
13258
|
+
}
|
|
13259
|
+
}
|
|
13260
|
+
});
|
|
13261
|
+
var createStyler = (open, close, parent) => {
|
|
13262
|
+
let openAll;
|
|
13263
|
+
let closeAll;
|
|
13264
|
+
if (parent === void 0) {
|
|
13265
|
+
openAll = open;
|
|
13266
|
+
closeAll = close;
|
|
13267
|
+
} else {
|
|
13268
|
+
openAll = parent.openAll + open;
|
|
13269
|
+
closeAll = close + parent.closeAll;
|
|
13270
|
+
}
|
|
13271
|
+
return {
|
|
13272
|
+
open,
|
|
13273
|
+
close,
|
|
13274
|
+
openAll,
|
|
13275
|
+
closeAll,
|
|
13276
|
+
parent
|
|
13277
|
+
};
|
|
13278
|
+
};
|
|
13279
|
+
var createBuilder = (self, _styler, _isEmpty) => {
|
|
13280
|
+
const builder = (...arguments_) => {
|
|
13281
|
+
if (isArray(arguments_[0]) && isArray(arguments_[0].raw)) return applyStyle(builder, chalkTag(builder, ...arguments_));
|
|
13282
|
+
return applyStyle(builder, arguments_.length === 1 ? "" + arguments_[0] : arguments_.join(" "));
|
|
13283
|
+
};
|
|
13284
|
+
Object.setPrototypeOf(builder, proto);
|
|
13285
|
+
builder._generator = self;
|
|
13286
|
+
builder._styler = _styler;
|
|
13287
|
+
builder._isEmpty = _isEmpty;
|
|
13288
|
+
return builder;
|
|
13289
|
+
};
|
|
13290
|
+
var applyStyle = (self, string) => {
|
|
13291
|
+
if (self.level <= 0 || !string) return self._isEmpty ? "" : string;
|
|
13292
|
+
let styler = self._styler;
|
|
13293
|
+
if (styler === void 0) return string;
|
|
13294
|
+
const { openAll, closeAll } = styler;
|
|
13295
|
+
if (string.indexOf("\x1B") !== -1) while (styler !== void 0) {
|
|
13296
|
+
string = stringReplaceAll(string, styler.close, styler.open);
|
|
13297
|
+
styler = styler.parent;
|
|
13298
|
+
}
|
|
13299
|
+
const lfIndex = string.indexOf("\n");
|
|
13300
|
+
if (lfIndex !== -1) string = stringEncaseCRLFWithFirstIndex(string, closeAll, openAll, lfIndex);
|
|
13301
|
+
return openAll + string + closeAll;
|
|
13302
|
+
};
|
|
13303
|
+
var template;
|
|
13304
|
+
var chalkTag = (chalk, ...strings) => {
|
|
13305
|
+
const [firstString] = strings;
|
|
13306
|
+
if (!isArray(firstString) || !isArray(firstString.raw)) return strings.join(" ");
|
|
13307
|
+
const arguments_ = strings.slice(1);
|
|
13308
|
+
const parts = [firstString.raw[0]];
|
|
13309
|
+
for (let i = 1; i < firstString.length; i++) parts.push(String(arguments_[i - 1]).replace(/[{}\\]/g, "\\$&"), String(firstString.raw[i]));
|
|
13310
|
+
if (template === void 0) template = require_templates();
|
|
13311
|
+
return template(chalk, parts.join(""));
|
|
13312
|
+
};
|
|
13313
|
+
Object.defineProperties(Chalk.prototype, styles);
|
|
13314
|
+
var chalk = Chalk();
|
|
13315
|
+
chalk.supportsColor = stdoutColor;
|
|
13316
|
+
chalk.stderr = Chalk({ level: stderrColor ? stderrColor.level : 0 });
|
|
13317
|
+
chalk.stderr.supportsColor = stderrColor;
|
|
13318
|
+
module.exports = chalk;
|
|
13319
|
+
})))();
|
|
13320
|
+
/**
|
|
13321
|
+
* Detect whether an error (or AggregateError wrapping multiple attempts)
|
|
13322
|
+
* represents an ECONNREFUSED — i.e. the database is simply not running.
|
|
13323
|
+
*
|
|
13324
|
+
* Handles:
|
|
13325
|
+
* - Direct `{ code: "ECONNREFUSED" }` errors from Node `net`
|
|
13326
|
+
* - `AggregateError` from dual-stack IPv4+IPv6 connection attempts
|
|
13327
|
+
* - Drizzle's `cause`-wrapped pg errors
|
|
13328
|
+
*/
|
|
13329
|
+
function isEconnrefused(err) {
|
|
13330
|
+
if (!err || typeof err !== "object") return false;
|
|
13331
|
+
const e = err;
|
|
13332
|
+
if (e.code === "ECONNREFUSED") return true;
|
|
13333
|
+
if (Array.isArray(e.errors)) return e.errors.some((inner) => inner && typeof inner === "object" && inner.code === "ECONNREFUSED");
|
|
13334
|
+
if (e.cause && typeof e.cause === "object") return isEconnrefused(e.cause);
|
|
13335
|
+
return false;
|
|
13336
|
+
}
|
|
13337
|
+
//#endregion
|
|
10593
13338
|
//#region src/PostgresBootstrapper.ts
|
|
10594
13339
|
/**
|
|
10595
13340
|
* PostgresBootstrapper
|
|
@@ -10636,6 +13381,18 @@ function createPostgresBootstrapper(pgConfig) {
|
|
|
10636
13381
|
try {
|
|
10637
13382
|
await schemaAwareDb.execute(sql`SELECT 1`);
|
|
10638
13383
|
} catch (err) {
|
|
13384
|
+
if (isEconnrefused(err)) {
|
|
13385
|
+
let hostInfo = pgConfig.connectionString || "unknown";
|
|
13386
|
+
try {
|
|
13387
|
+
const parsed = new URL(pgConfig.connectionString || "");
|
|
13388
|
+
hostInfo = `${parsed.hostname}:${parsed.port || 5432}`;
|
|
13389
|
+
} catch {}
|
|
13390
|
+
const message = `
|
|
13391
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
13392
|
+
❌ Cannot connect to PostgreSQL at ${hostInfo}\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n The database server is not running or is not accepting\n connections. Common fixes:\n\n • brew services start postgresql@18\n • docker compose up -d postgres\n • Verify DATABASE_URL in your .env file\n\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n`;
|
|
13393
|
+
logger.error(message);
|
|
13394
|
+
throw new Error(`Cannot connect to PostgreSQL at ${hostInfo}: connection refused. Is the database running?`);
|
|
13395
|
+
}
|
|
10639
13396
|
logger.error("❌ Failed to connect to PostgreSQL", { error: err });
|
|
10640
13397
|
logger.warn("⚠️ Continuing without initial database verification. Drizzle/PG will attempt to connect on subsequent queries.");
|
|
10641
13398
|
}
|
|
@@ -10798,6 +13555,6 @@ function createPostgresAdapter(pgConfig) {
|
|
|
10798
13555
|
};
|
|
10799
13556
|
}
|
|
10800
13557
|
//#endregion
|
|
10801
|
-
export { AuthenticatedPostgresBackendDriver, BranchService, DatabasePoolManager, DrizzleConditionBuilder, PostgresBackendDriver, PostgresCollectionRegistry, PostgresConditionBuilder, PostgresRealtimeProvider, RealtimeService, appConfig, createAuthSchema, createDirectDatabaseConnection, createPostgresAdapter, createPostgresBootstrapper, createPostgresDatabaseConnection, createPostgresWebSocket, createReadReplicaConnection, generateSchema, mfaChallenges, mfaChallengesRelations, mfaFactors, mfaFactorsRelations, passwordResetTokens, passwordResetTokensRelations, recoveryCodes, recoveryCodesRelations, refreshTokens, refreshTokensRelations, userIdentities, userIdentitiesRelations, users, usersRelations, usersSchema };
|
|
13558
|
+
export { AuthenticatedPostgresBackendDriver, BranchService, DatabasePoolManager, DrizzleConditionBuilder, PostgresBackendDriver, PostgresCollectionRegistry, PostgresConditionBuilder, PostgresRealtimeProvider, RealtimeService, appConfig, createAuthSchema, createDirectDatabaseConnection, createPostgresAdapter, createPostgresBootstrapper, createPostgresDatabaseConnection, createPostgresWebSocket, createReadReplicaConnection, generateSchema, magicLinkTokens, magicLinkTokensRelations, mfaChallenges, mfaChallengesRelations, mfaFactors, mfaFactorsRelations, passwordResetTokens, passwordResetTokensRelations, recoveryCodes, recoveryCodesRelations, refreshTokens, refreshTokensRelations, userIdentities, userIdentitiesRelations, users, usersRelations, usersSchema };
|
|
10802
13559
|
|
|
10803
13560
|
//# sourceMappingURL=index.es.js.map
|