@restura/core 0.1.0-alpha.20 → 0.1.0-alpha.21

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