@restura/core 0.1.0-alpha.20 → 0.1.0-alpha.22
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/index.d.mts +9 -3
- package/dist/index.d.ts +9 -3
- package/dist/index.js +92 -41
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +90 -39
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -711,6 +711,8 @@ function addApiResponseFunctions(req, res, next) {
|
|
|
711
711
|
function authenticateUser(applicationAuthenticateHandler) {
|
|
712
712
|
return (req, res, next) => {
|
|
713
713
|
applicationAuthenticateHandler(req, res, (userDetails) => {
|
|
714
|
+
req.requesterDetails.host = req.hostname;
|
|
715
|
+
req.requesterDetails.ipAddress = req.ip || "";
|
|
714
716
|
req.requesterDetails = __spreadValues(__spreadValues({}, req.requesterDetails), userDetails);
|
|
715
717
|
next();
|
|
716
718
|
});
|
|
@@ -1283,14 +1285,17 @@ function SQL(strings, ...values) {
|
|
|
1283
1285
|
}
|
|
1284
1286
|
|
|
1285
1287
|
// src/restura/sql/PsqlConnection.ts
|
|
1288
|
+
import crypto from "crypto";
|
|
1286
1289
|
var PsqlConnection = class {
|
|
1287
1290
|
constructor() {
|
|
1291
|
+
this.instanceId = crypto.randomUUID();
|
|
1288
1292
|
}
|
|
1289
1293
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1290
1294
|
async queryOne(query, options, requesterDetails) {
|
|
1291
1295
|
const formattedQuery = questionMarksToOrderedParams(query);
|
|
1292
|
-
this.
|
|
1293
|
-
|
|
1296
|
+
const meta = __spreadValues({ connectionInstanceId: this.instanceId }, requesterDetails);
|
|
1297
|
+
this.logSqlStatement(formattedQuery, options, meta);
|
|
1298
|
+
const queryMetadata = `--QUERY_METADATA(${JSON.stringify(meta)})
|
|
1294
1299
|
`;
|
|
1295
1300
|
try {
|
|
1296
1301
|
const response = await this.query(queryMetadata + formattedQuery, options);
|
|
@@ -1309,8 +1314,9 @@ var PsqlConnection = class {
|
|
|
1309
1314
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1310
1315
|
async runQuery(query, options, requesterDetails) {
|
|
1311
1316
|
const formattedQuery = questionMarksToOrderedParams(query);
|
|
1312
|
-
this.
|
|
1313
|
-
|
|
1317
|
+
const meta = __spreadValues({ connectionInstanceId: this.instanceId }, requesterDetails);
|
|
1318
|
+
this.logSqlStatement(formattedQuery, options, meta);
|
|
1319
|
+
const queryMetadata = `--QUERY_METADATA(${JSON.stringify(meta)})
|
|
1314
1320
|
`;
|
|
1315
1321
|
try {
|
|
1316
1322
|
const response = await this.query(queryMetadata + formattedQuery, options);
|
|
@@ -1323,7 +1329,7 @@ var PsqlConnection = class {
|
|
|
1323
1329
|
throw new RsError("DATABASE_ERROR", `${error.message}`);
|
|
1324
1330
|
}
|
|
1325
1331
|
}
|
|
1326
|
-
logSqlStatement(query, options,
|
|
1332
|
+
logSqlStatement(query, options, queryMetadata, prefix = "") {
|
|
1327
1333
|
if (logger.level !== "silly") return;
|
|
1328
1334
|
let sqlStatement = "";
|
|
1329
1335
|
if (options.length === 0) {
|
|
@@ -1337,9 +1343,9 @@ var PsqlConnection = class {
|
|
|
1337
1343
|
});
|
|
1338
1344
|
}
|
|
1339
1345
|
let initiator = "Anonymous";
|
|
1340
|
-
if ("userId" in
|
|
1341
|
-
initiator = `User Id (${
|
|
1342
|
-
if ("isSystemUser" in
|
|
1346
|
+
if ("userId" in queryMetadata && queryMetadata.userId)
|
|
1347
|
+
initiator = `User Id (${queryMetadata.userId.toString()})`;
|
|
1348
|
+
if ("isSystemUser" in queryMetadata && queryMetadata.isSystemUser) initiator = "SYSTEM";
|
|
1343
1349
|
logger.silly(`${prefix}query by ${initiator}, Query ->
|
|
1344
1350
|
${sqlStatement}`);
|
|
1345
1351
|
}
|
|
@@ -1521,13 +1527,15 @@ var filterSqlGrammar = `
|
|
|
1521
1527
|
|
|
1522
1528
|
start = expressionList
|
|
1523
1529
|
|
|
1530
|
+
_ = [ \\t\\r\\n]* // Matches spaces, tabs, and line breaks
|
|
1531
|
+
|
|
1524
1532
|
expressionList =
|
|
1525
|
-
|
|
1533
|
+
leftExpression:expression _ operator:operator _ rightExpression:expressionList
|
|
1526
1534
|
{ return \`\${leftExpression} \${operator} \${rightExpression}\`;}
|
|
1527
1535
|
/ expression
|
|
1528
1536
|
|
|
1529
1537
|
expression =
|
|
1530
|
-
negate:negate?"(" "column:"
|
|
1538
|
+
negate:negate? _ "(" _ "column" _ ":" column:column _ ","? _ value:value? ","? _ type:type? _ ")"_
|
|
1531
1539
|
{return \`\${negate? " NOT " : ""}(\${type? type(column, value) : \`\${column} = \${format.literal(value)}\`})\`;}
|
|
1532
1540
|
/
|
|
1533
1541
|
negate:negate?"("expression:expressionList")" { return \`\${negate? " NOT " : ""}(\${expression})\`; }
|
|
@@ -1542,9 +1550,9 @@ column = left:text "." right:text { return \`\${quoteSqlIdentity(left)}.\${quot
|
|
|
1542
1550
|
text:text { return quoteSqlIdentity(text); }
|
|
1543
1551
|
|
|
1544
1552
|
|
|
1545
|
-
text = text:[a-z0-9
|
|
1553
|
+
text = text:[a-z0-9 \\t\\r\\n\\-_:@]i+ { return text.join(""); }
|
|
1546
1554
|
|
|
1547
|
-
type = "type:" type:typeString { return type; }
|
|
1555
|
+
type = "type" _ ":" _ type:typeString { return type; }
|
|
1548
1556
|
typeString = text:"startsWith" { return function(column, value) { return \`\${column} ILIKE '\${format.literal(value).slice(1,-1)}%'\`; } } /
|
|
1549
1557
|
text:"endsWith" { return function(column, value) { return \`\${column} ILIKE '%\${format.literal(value).slice(1,-1)}'\`; } } /
|
|
1550
1558
|
text:"contains" { return function(column, value) { return \`\${column} ILIKE '%\${format.literal(value).slice(1,-1)}%'\`; } } /
|
|
@@ -1554,8 +1562,9 @@ typeString = text:"startsWith" { return function(column, value) { return \`\${co
|
|
|
1554
1562
|
text:"lessThanEqual" { return function(column, value) { return \`\${column} <= '\${format.literal(value).slice(1,-1)}'\`; } } /
|
|
1555
1563
|
text:"lessThan" { return function(column, value) { return \`\${column} < '\${format.literal(value).slice(1,-1)}'\`; } } /
|
|
1556
1564
|
text:"isNull" { return function(column, value) { return \`isNull(\${column})\`; } }
|
|
1557
|
-
|
|
1558
|
-
value = "value:" value:text { return value; }
|
|
1565
|
+
|
|
1566
|
+
value = "value" _ ":" value:text { return value; }
|
|
1567
|
+
|
|
1559
1568
|
|
|
1560
1569
|
`;
|
|
1561
1570
|
var filterPsqlParser = peg.generate(filterSqlGrammar, {
|
|
@@ -1610,9 +1619,9 @@ var EventManager = class {
|
|
|
1610
1619
|
tableName: triggerResult.table,
|
|
1611
1620
|
insertId: triggerResult.record.id,
|
|
1612
1621
|
insertObject: triggerResult.record,
|
|
1613
|
-
|
|
1622
|
+
queryMetadata: data.queryMetadata
|
|
1614
1623
|
};
|
|
1615
|
-
callback(insertData, data.
|
|
1624
|
+
callback(insertData, data.queryMetadata);
|
|
1616
1625
|
},
|
|
1617
1626
|
{ concurrency: 10 }
|
|
1618
1627
|
);
|
|
@@ -1625,9 +1634,9 @@ var EventManager = class {
|
|
|
1625
1634
|
const deleteData = {
|
|
1626
1635
|
tableName: triggerResult.table,
|
|
1627
1636
|
deletedRow: triggerResult.previousRecord,
|
|
1628
|
-
|
|
1637
|
+
queryMetadata: data.queryMetadata
|
|
1629
1638
|
};
|
|
1630
|
-
callback(deleteData, data.
|
|
1639
|
+
callback(deleteData, data.queryMetadata);
|
|
1631
1640
|
},
|
|
1632
1641
|
{ concurrency: 10 }
|
|
1633
1642
|
);
|
|
@@ -1642,9 +1651,9 @@ var EventManager = class {
|
|
|
1642
1651
|
rowId: triggerResult.record.id,
|
|
1643
1652
|
newData: triggerResult.record,
|
|
1644
1653
|
oldData: triggerResult.previousRecord,
|
|
1645
|
-
|
|
1654
|
+
queryMetadata: data.queryMetadata
|
|
1646
1655
|
};
|
|
1647
|
-
callback(columnChangeData, data.
|
|
1656
|
+
callback(columnChangeData, data.queryMetadata);
|
|
1648
1657
|
},
|
|
1649
1658
|
{ concurrency: 10 }
|
|
1650
1659
|
);
|
|
@@ -1659,6 +1668,13 @@ var EventManager = class {
|
|
|
1659
1668
|
case "DATABASE_COLUMN_UPDATE":
|
|
1660
1669
|
const filterColumnChange = filter;
|
|
1661
1670
|
if (filterColumnChange.tableName !== filter.tableName) return false;
|
|
1671
|
+
if (!filterColumnChange.columns.some((item) => {
|
|
1672
|
+
const updatedColumns = Object.keys(
|
|
1673
|
+
changedValues(triggerResult.record, triggerResult.previousRecord)
|
|
1674
|
+
);
|
|
1675
|
+
return updatedColumns.includes(item);
|
|
1676
|
+
}))
|
|
1677
|
+
return false;
|
|
1662
1678
|
break;
|
|
1663
1679
|
}
|
|
1664
1680
|
}
|
|
@@ -1667,6 +1683,22 @@ var EventManager = class {
|
|
|
1667
1683
|
};
|
|
1668
1684
|
var eventManager = new EventManager();
|
|
1669
1685
|
var eventManager_default = eventManager;
|
|
1686
|
+
function changedValues(record, previousRecord) {
|
|
1687
|
+
const changed = {};
|
|
1688
|
+
for (const i in previousRecord) {
|
|
1689
|
+
if (previousRecord[i] !== record[i]) {
|
|
1690
|
+
if (typeof previousRecord[i] === "object" && typeof record[i] === "object") {
|
|
1691
|
+
const nestedChanged = changedValues(record[i], previousRecord[i]);
|
|
1692
|
+
if (Object.keys(nestedChanged).length > 0) {
|
|
1693
|
+
changed[i] = record[i];
|
|
1694
|
+
}
|
|
1695
|
+
} else {
|
|
1696
|
+
changed[i] = record[i];
|
|
1697
|
+
}
|
|
1698
|
+
}
|
|
1699
|
+
}
|
|
1700
|
+
return changed;
|
|
1701
|
+
}
|
|
1670
1702
|
|
|
1671
1703
|
// src/restura/sql/PsqlEngine.ts
|
|
1672
1704
|
var { Client } = pg2;
|
|
@@ -1713,12 +1745,15 @@ var PsqlEngine = class extends SqlEngine {
|
|
|
1713
1745
|
}
|
|
1714
1746
|
async handleTrigger(payload, mutationType) {
|
|
1715
1747
|
const findRequesterDetailsRegex = /^--QUERY_METADATA\(\{.*\}\)/;
|
|
1716
|
-
let requesterDetails = {};
|
|
1717
1748
|
const match = payload.query.match(findRequesterDetailsRegex);
|
|
1718
1749
|
if (match) {
|
|
1719
1750
|
const jsonString = match[0].slice(match[0].indexOf("{"), match[0].lastIndexOf("}") + 1);
|
|
1720
|
-
|
|
1721
|
-
|
|
1751
|
+
const queryMetadata = ObjectUtils4.safeParse(jsonString);
|
|
1752
|
+
const triggerFromThisInstance = queryMetadata.connectionInstanceId === this.psqlConnectionPool.instanceId;
|
|
1753
|
+
if (!triggerFromThisInstance) {
|
|
1754
|
+
return;
|
|
1755
|
+
}
|
|
1756
|
+
await eventManager_default.fireActionFromDbTrigger({ queryMetadata, mutationType }, payload);
|
|
1722
1757
|
}
|
|
1723
1758
|
}
|
|
1724
1759
|
async createDatabaseFromSchema(schema, connection) {
|
|
@@ -1728,7 +1763,6 @@ var PsqlEngine = class extends SqlEngine {
|
|
|
1728
1763
|
}
|
|
1729
1764
|
generateDatabaseSchemaFromSchema(schema) {
|
|
1730
1765
|
const sqlStatements = [];
|
|
1731
|
-
const enums = [];
|
|
1732
1766
|
const indexes = [];
|
|
1733
1767
|
const triggers = [];
|
|
1734
1768
|
for (const table of schema.database) {
|
|
@@ -1740,10 +1774,7 @@ var PsqlEngine = class extends SqlEngine {
|
|
|
1740
1774
|
const tableColumns = [];
|
|
1741
1775
|
for (const column of table.columns) {
|
|
1742
1776
|
let columnSql = "";
|
|
1743
|
-
|
|
1744
|
-
enums.push(`CREATE TYPE ${schemaToPsqlType(column, table.name)} AS ENUM (${column.value});`);
|
|
1745
|
-
}
|
|
1746
|
-
columnSql += ` "${column.name}" ${schemaToPsqlType(column, table.name)}`;
|
|
1777
|
+
columnSql += ` "${column.name}" ${schemaToPsqlType(column)}`;
|
|
1747
1778
|
let value = column.value;
|
|
1748
1779
|
if (column.type === "JSON") value = "";
|
|
1749
1780
|
if (column.type === "JSONB") value = "";
|
|
@@ -1762,6 +1793,9 @@ var PsqlEngine = class extends SqlEngine {
|
|
|
1762
1793
|
if (column.isNullable) columnSql += " NULL";
|
|
1763
1794
|
else columnSql += " NOT NULL";
|
|
1764
1795
|
if (column.default) columnSql += ` DEFAULT ${column.default}`;
|
|
1796
|
+
if (value && column.type === "ENUM") {
|
|
1797
|
+
columnSql += ` CHECK ("${column.name}" IN (${value}))`;
|
|
1798
|
+
}
|
|
1765
1799
|
tableColumns.push(columnSql);
|
|
1766
1800
|
}
|
|
1767
1801
|
sql += tableColumns.join(", \n");
|
|
@@ -1804,16 +1838,18 @@ var PsqlEngine = class extends SqlEngine {
|
|
|
1804
1838
|
}
|
|
1805
1839
|
sqlStatements.push(indexes.join("\n"));
|
|
1806
1840
|
sqlStatements.push(triggers.join("\n"));
|
|
1807
|
-
return
|
|
1841
|
+
return sqlStatements.join("\n\n");
|
|
1808
1842
|
}
|
|
1809
1843
|
async getScratchPool() {
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1844
|
+
var _a2, _b;
|
|
1845
|
+
const scratchDbExists = await this.psqlConnectionPool.runQuery(
|
|
1846
|
+
`SELECT *
|
|
1847
|
+
FROM pg_database
|
|
1848
|
+
WHERE datname = '${this.psqlConnectionPool.poolConfig.database}_scratch';`,
|
|
1813
1849
|
[],
|
|
1814
1850
|
systemUser
|
|
1815
1851
|
);
|
|
1816
|
-
if (
|
|
1852
|
+
if (scratchDbExists.length === 0) {
|
|
1817
1853
|
await this.psqlConnectionPool.runQuery(
|
|
1818
1854
|
`CREATE DATABASE ${this.psqlConnectionPool.poolConfig.database}_scratch;`,
|
|
1819
1855
|
[],
|
|
@@ -1830,12 +1866,27 @@ var PsqlEngine = class extends SqlEngine {
|
|
|
1830
1866
|
idleTimeoutMillis: this.psqlConnectionPool.poolConfig.idleTimeoutMillis,
|
|
1831
1867
|
connectionTimeoutMillis: this.psqlConnectionPool.poolConfig.connectionTimeoutMillis
|
|
1832
1868
|
});
|
|
1833
|
-
await scratchPool.runQuery(`
|
|
1869
|
+
await scratchPool.runQuery(`DROP SCHEMA public CASCADE;`, [], systemUser);
|
|
1834
1870
|
await scratchPool.runQuery(
|
|
1835
|
-
`
|
|
1871
|
+
`CREATE SCHEMA public AUTHORIZATION ${this.psqlConnectionPool.poolConfig.user};`,
|
|
1872
|
+
[],
|
|
1873
|
+
systemUser
|
|
1874
|
+
);
|
|
1875
|
+
const schemaComment = await this.psqlConnectionPool.runQuery(
|
|
1876
|
+
`SELECT pg_description.description
|
|
1877
|
+
FROM pg_description
|
|
1878
|
+
JOIN pg_namespace ON pg_namespace.oid = pg_description.objoid
|
|
1879
|
+
WHERE pg_namespace.nspname = 'public';`,
|
|
1836
1880
|
[],
|
|
1837
1881
|
systemUser
|
|
1838
1882
|
);
|
|
1883
|
+
if ((_a2 = schemaComment[0]) == null ? void 0 : _a2.description) {
|
|
1884
|
+
await scratchPool.runQuery(
|
|
1885
|
+
`COMMENT ON SCHEMA public IS '${(_b = schemaComment[0]) == null ? void 0 : _b.description}';`,
|
|
1886
|
+
[],
|
|
1887
|
+
systemUser
|
|
1888
|
+
);
|
|
1889
|
+
}
|
|
1839
1890
|
return scratchPool;
|
|
1840
1891
|
}
|
|
1841
1892
|
async diffDatabaseToSchema(schema) {
|
|
@@ -2205,9 +2256,9 @@ __decorateClass([
|
|
|
2205
2256
|
__decorateClass([
|
|
2206
2257
|
boundMethod
|
|
2207
2258
|
], PsqlEngine.prototype, "createInsertTriggers", 1);
|
|
2208
|
-
function schemaToPsqlType(column
|
|
2259
|
+
function schemaToPsqlType(column) {
|
|
2209
2260
|
if (column.hasAutoIncrement) return "BIGSERIAL";
|
|
2210
|
-
if (column.type === "ENUM") return `
|
|
2261
|
+
if (column.type === "ENUM") return `TEXT`;
|
|
2211
2262
|
if (column.type === "DATETIME") return "TIMESTAMPTZ";
|
|
2212
2263
|
if (column.type === "MEDIUMINT") return "INT";
|
|
2213
2264
|
return column.type;
|
|
@@ -2750,7 +2801,7 @@ __decorateClass([
|
|
|
2750
2801
|
__decorateClass([
|
|
2751
2802
|
boundMethod
|
|
2752
2803
|
], ResturaEngine.prototype, "runCustomRouteLogic", 1);
|
|
2753
|
-
|
|
2804
|
+
function setupPgReturnTypes() {
|
|
2754
2805
|
const TIMESTAMPTZ_OID = 1184;
|
|
2755
2806
|
types.setTypeParser(TIMESTAMPTZ_OID, (val) => {
|
|
2756
2807
|
return val === null ? null : new Date(val).toISOString();
|
|
@@ -2759,7 +2810,7 @@ var setupPgReturnTypes = () => {
|
|
|
2759
2810
|
types.setTypeParser(BIGINT_OID, (val) => {
|
|
2760
2811
|
return val === null ? null : Number(val);
|
|
2761
2812
|
});
|
|
2762
|
-
}
|
|
2813
|
+
}
|
|
2763
2814
|
setupPgReturnTypes();
|
|
2764
2815
|
var restura = new ResturaEngine();
|
|
2765
2816
|
|