@devbro/pashmak 0.1.10 → 0.1.12

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.
Files changed (40) hide show
  1. package/dist/app/console/migrate/MigrateCommand.d.mts +1 -0
  2. package/dist/app/console/migrate/MigrateCommand.mjs +33 -16
  3. package/dist/app/console/migrate/MigrateCommand.mjs.map +1 -1
  4. package/dist/app/console/migrate/MigrateRollbackCommand.d.mts +1 -1
  5. package/dist/app/console/migrate/MigrateRollbackCommand.mjs +5 -6
  6. package/dist/app/console/migrate/MigrateRollbackCommand.mjs.map +1 -1
  7. package/dist/app/console/migrate/make_migration.tpl +5 -5
  8. package/dist/app/console/queue/GenerateMigrateCommand.d.mts +9 -0
  9. package/dist/app/console/queue/GenerateMigrateCommand.mjs +51 -0
  10. package/dist/app/console/queue/GenerateMigrateCommand.mjs.map +1 -0
  11. package/dist/app/console/queue/queue_migration.tpl +19 -0
  12. package/dist/bin/app/console/DefaultCommand.cjs +254 -25
  13. package/dist/bin/app/console/KeyGenerateCommand.cjs +254 -25
  14. package/dist/bin/app/console/StartCommand.cjs +257 -28
  15. package/dist/bin/app/console/generate/GenerateControllerCommand.cjs +254 -25
  16. package/dist/bin/app/console/generate/index.cjs +254 -25
  17. package/dist/bin/app/console/index.cjs +293 -48
  18. package/dist/bin/app/console/migrate/GenerateMigrateCommand.cjs +254 -25
  19. package/dist/bin/app/console/migrate/MigrateCommand.cjs +288 -42
  20. package/dist/bin/app/console/migrate/MigrateRollbackCommand.cjs +258 -30
  21. package/dist/bin/app/console/migrate/index.cjs +291 -46
  22. package/dist/bin/app/console/queue/GenerateMigrateCommand.cjs +752 -0
  23. package/dist/bin/facades.cjs +257 -26
  24. package/dist/bin/factories.cjs +707 -0
  25. package/dist/bin/index.cjs +312 -54
  26. package/dist/bin/middlewares.cjs +255 -26
  27. package/dist/bin/queue.cjs +99 -0
  28. package/dist/bin/router.cjs +95 -5
  29. package/dist/facades.d.mts +3 -1
  30. package/dist/facades.mjs +15 -27
  31. package/dist/facades.mjs.map +1 -1
  32. package/dist/factories.d.mts +20 -0
  33. package/dist/factories.mjs +83 -0
  34. package/dist/factories.mjs.map +1 -0
  35. package/dist/queue.d.mts +15 -0
  36. package/dist/queue.mjs +73 -0
  37. package/dist/queue.mjs.map +1 -0
  38. package/dist/router.mjs +1 -1
  39. package/dist/router.mjs.map +1 -1
  40. package/package.json +10 -3
@@ -1202,6 +1202,9 @@ module.exports = __toCommonJS(console_exports);
1202
1202
  var import_neko_context = require("@devbro/neko-context");
1203
1203
  var import_errors = require("@devbro/neko-http/errors");
1204
1204
 
1205
+ // ../neko-router/dist/CompiledRoute.mjs
1206
+ var import_stream = require("stream");
1207
+
1205
1208
  // ../neko-router/dist/Middleware.mjs
1206
1209
  var Middleware = class {
1207
1210
  static {
@@ -1279,6 +1282,9 @@ var CompiledRoute = class {
1279
1282
  if (typeof value.toJson === "function") {
1280
1283
  return traverse(value.toJson());
1281
1284
  }
1285
+ if (typeof value.toJSON === "function") {
1286
+ return traverse(value.toJSON());
1287
+ }
1282
1288
  if (Array.isArray(value)) {
1283
1289
  return value.map(traverse);
1284
1290
  }
@@ -1303,7 +1309,7 @@ var CompiledRoute = class {
1303
1309
  }
1304
1310
  return String(obj);
1305
1311
  }
1306
- processResponseBody(res, controller_rc) {
1312
+ async processResponseBody(res, controller_rc) {
1307
1313
  if (controller_rc && res.writableEnded) {
1308
1314
  throw new Error("cannot write to response, response has already ended");
1309
1315
  }
@@ -1312,18 +1318,36 @@ var CompiledRoute = class {
1312
1318
  }
1313
1319
  if (controller_rc) {
1314
1320
  const header_content_type = res.getHeader("Content-Type");
1315
- if (!header_content_type && typeof controller_rc === "object") {
1321
+ if (controller_rc instanceof import_stream.Stream || Buffer.isBuffer(controller_rc)) {
1322
+ await this.writeAsync(res, controller_rc);
1323
+ res.end();
1324
+ } else if (!header_content_type && typeof controller_rc === "object") {
1316
1325
  res.setHeader("Content-Type", "application/json");
1326
+ res.end(this.convertToString(controller_rc));
1317
1327
  } else if (!header_content_type) {
1318
1328
  res.setHeader("Content-Type", "text/plain");
1329
+ res.end(this.convertToString(controller_rc));
1330
+ } else {
1331
+ res.end(this.convertToString(controller_rc));
1319
1332
  }
1320
- res.end(this.convertToString(controller_rc));
1321
1333
  return;
1322
1334
  } else {
1323
1335
  res.statusCode = [200].includes(res.statusCode) ? 204 : res.statusCode;
1324
1336
  res.end();
1325
1337
  }
1326
1338
  }
1339
+ async writeAsync(res, chunk) {
1340
+ return new Promise((resolve, reject) => {
1341
+ const ok = res.write(chunk, (err) => {
1342
+ if (err) reject(err);
1343
+ });
1344
+ if (ok) {
1345
+ resolve(0);
1346
+ } else {
1347
+ res.once("drain", resolve);
1348
+ }
1349
+ });
1350
+ }
1327
1351
  async runMiddlewares(middlewares, req, res) {
1328
1352
  let index = 0;
1329
1353
  const me = this;
@@ -1381,7 +1405,7 @@ var Route = class {
1381
1405
  i = start;
1382
1406
  } else if (char === "*") {
1383
1407
  let start = i + 1;
1384
- while (start < path8.length && /[a-zA-Z0-9_]/.test(path8[start])) {
1408
+ while (start < path8.length && /[a-zA-Z0-9_\.]/.test(path8[start])) {
1385
1409
  start++;
1386
1410
  }
1387
1411
  tokens.push({ type: "WILDCARD", value: path8.slice(i + 1, start) });
@@ -1451,6 +1475,10 @@ var Route = class {
1451
1475
  params: r.groups || {}
1452
1476
  };
1453
1477
  }
1478
+ prependMiddleware(middlewares) {
1479
+ this.middlewares = [].concat(middlewares, this.middlewares);
1480
+ return this;
1481
+ }
1454
1482
  addMiddleware(middlewares) {
1455
1483
  this.middlewares = this.middlewares.concat(middlewares);
1456
1484
  return this;
@@ -1465,6 +1493,62 @@ var Route = class {
1465
1493
 
1466
1494
  // ../neko-router/dist/Router.mjs
1467
1495
  var import_path = __toESM(require("path"), 1);
1496
+
1497
+ // ../node_modules/url-join/lib/url-join.js
1498
+ function normalize(strArray) {
1499
+ var resultArray = [];
1500
+ if (strArray.length === 0) {
1501
+ return "";
1502
+ }
1503
+ if (typeof strArray[0] !== "string") {
1504
+ throw new TypeError("Url must be a string. Received " + strArray[0]);
1505
+ }
1506
+ if (strArray[0].match(/^[^/:]+:\/*$/) && strArray.length > 1) {
1507
+ var first = strArray.shift();
1508
+ strArray[0] = first + strArray[0];
1509
+ }
1510
+ if (strArray[0].match(/^file:\/\/\//)) {
1511
+ strArray[0] = strArray[0].replace(/^([^/:]+):\/*/, "$1:///");
1512
+ } else {
1513
+ strArray[0] = strArray[0].replace(/^([^/:]+):\/*/, "$1://");
1514
+ }
1515
+ for (var i = 0; i < strArray.length; i++) {
1516
+ var component = strArray[i];
1517
+ if (typeof component !== "string") {
1518
+ throw new TypeError("Url must be a string. Received " + component);
1519
+ }
1520
+ if (component === "") {
1521
+ continue;
1522
+ }
1523
+ if (i > 0) {
1524
+ component = component.replace(/^[\/]+/, "");
1525
+ }
1526
+ if (i < strArray.length - 1) {
1527
+ component = component.replace(/[\/]+$/, "");
1528
+ } else {
1529
+ component = component.replace(/[\/]+$/, "/");
1530
+ }
1531
+ resultArray.push(component);
1532
+ }
1533
+ var str = resultArray.join("/");
1534
+ str = str.replace(/\/(\?|&|#[^!])/g, "$1");
1535
+ var parts = str.split("?");
1536
+ str = parts.shift() + (parts.length > 0 ? "?" : "") + parts.join("&");
1537
+ return str;
1538
+ }
1539
+ __name(normalize, "normalize");
1540
+ function urlJoin() {
1541
+ var input;
1542
+ if (typeof arguments[0] === "object") {
1543
+ input = arguments[0];
1544
+ } else {
1545
+ input = [].slice.call(arguments);
1546
+ }
1547
+ return normalize(input);
1548
+ }
1549
+ __name(urlJoin, "urlJoin");
1550
+
1551
+ // ../neko-router/dist/Router.mjs
1468
1552
  var Router = class {
1469
1553
  static {
1470
1554
  __name(this, "Router");
@@ -1489,6 +1573,12 @@ var Router = class {
1489
1573
  }).addMiddleware([...controller.baseMiddlewares, ...route.middlewares]);
1490
1574
  }
1491
1575
  }
1576
+ addRouter(path22, router2) {
1577
+ for (const route of router2.routes) {
1578
+ let path222 = urlJoin("/", path22, route.path);
1579
+ this.addRoute(route.methods, path222, route.handler).addMiddleware(router2.getMiddlewares()).addMiddleware(route.getMiddlewares());
1580
+ }
1581
+ }
1492
1582
  addGlobalMiddleware(middlewares) {
1493
1583
  this.middlewares = this.middlewares.concat(middlewares);
1494
1584
  }
@@ -1529,7 +1619,7 @@ var import_neko_scheduler = require("@devbro/neko-scheduler");
1529
1619
  var import_neko_helper = require("@devbro/neko-helper");
1530
1620
  var import_neko_context2 = require("@devbro/neko-context");
1531
1621
  var import_neko_storage = require("@devbro/neko-storage");
1532
- var import_neko_mailer = require("@devbro/neko-mailer");
1622
+ var import_neko_mailer2 = require("@devbro/neko-mailer");
1533
1623
  var import_neko_config = require("@devbro/neko-config");
1534
1624
  var import_clipanion = require("clipanion");
1535
1625
 
@@ -1540,6 +1630,153 @@ __reExport(http_exports, require("@devbro/neko-http"));
1540
1630
  // src/facades.mts
1541
1631
  var yup = __toESM(require("yup"), 1);
1542
1632
  var import_neko_logger = require("@devbro/neko-logger");
1633
+
1634
+ // src/factories.mts
1635
+ var import_neko_mailer = require("@devbro/neko-mailer");
1636
+ var import_neko_queue = require("@devbro/neko-queue");
1637
+ var import_neko_queue2 = require("@devbro/neko-queue");
1638
+
1639
+ // src/queue.mts
1640
+ var queue_exports = {};
1641
+ __export(queue_exports, {
1642
+ DatabaseTransport: () => DatabaseTransport
1643
+ });
1644
+ __reExport(queue_exports, require("@devbro/neko-queue"));
1645
+ var import_neko_sql = require("@devbro/neko-sql");
1646
+ var DatabaseTransport = class {
1647
+ // default to 100 messages per fetch
1648
+ constructor(db_config) {
1649
+ this.db_config = db_config;
1650
+ }
1651
+ static {
1652
+ __name(this, "DatabaseTransport");
1653
+ }
1654
+ listenInterval = 6e4;
1655
+ // default to 1 minute
1656
+ messageLimit = 100;
1657
+ setListenInterval(interval) {
1658
+ this.listenInterval = interval;
1659
+ }
1660
+ setMessageLimit(limit) {
1661
+ this.messageLimit = limit;
1662
+ }
1663
+ async dispatch(channel, message) {
1664
+ const conn = new import_neko_sql.PostgresqlConnection(this.db_config);
1665
+ try {
1666
+ await conn.connect();
1667
+ let q = conn.getQuery();
1668
+ await q.table("queue_messages").insert({
1669
+ channel,
1670
+ message,
1671
+ processed: false,
1672
+ created_at: /* @__PURE__ */ new Date(),
1673
+ updated_at: /* @__PURE__ */ new Date(),
1674
+ last_tried_at: null,
1675
+ process_message: ""
1676
+ });
1677
+ } finally {
1678
+ await conn.disconnect();
1679
+ }
1680
+ }
1681
+ async listen(channel, callback) {
1682
+ return new Promise(async (resolve, reject) => {
1683
+ setInterval(async () => {
1684
+ const conn = new import_neko_sql.PostgresqlConnection(this.db_config);
1685
+ try {
1686
+ await conn.connect();
1687
+ let q = conn.getQuery();
1688
+ let messages = await q.table("queue_messages").whereOp("channel", "=", channel).whereOp("processed", "=", false).limit(this.messageLimit).orderBy("last_tried_at", "asc").get();
1689
+ for (let msg of messages) {
1690
+ try {
1691
+ await callback(msg.message);
1692
+ await q.table("queue_messages").whereOp("id", "=", msg.id).update({
1693
+ processed: true,
1694
+ updated_at: /* @__PURE__ */ new Date()
1695
+ });
1696
+ } catch (error) {
1697
+ await q.table("queue_messages").whereOp("id", "=", msg.id).update({
1698
+ processed: false,
1699
+ last_tried_at: /* @__PURE__ */ new Date(),
1700
+ process_message: error.message || "Error processing message"
1701
+ });
1702
+ }
1703
+ }
1704
+ } finally {
1705
+ await conn.disconnect();
1706
+ }
1707
+ }, this.listenInterval);
1708
+ });
1709
+ }
1710
+ };
1711
+
1712
+ // src/factories.mts
1713
+ var FlexibleFactory = class {
1714
+ static {
1715
+ __name(this, "FlexibleFactory");
1716
+ }
1717
+ registry = /* @__PURE__ */ new Map();
1718
+ register(key, ctor) {
1719
+ this.registry.set(key, ctor);
1720
+ }
1721
+ create(key, ...args) {
1722
+ const ctor = this.registry.get(key);
1723
+ if (!ctor) {
1724
+ throw new Error(`No factory registered for key: ${key}`);
1725
+ }
1726
+ return new ctor(...args);
1727
+ }
1728
+ };
1729
+ var MailerFactory = class _MailerFactory {
1730
+ static {
1731
+ __name(this, "MailerFactory");
1732
+ }
1733
+ static instance = new FlexibleFactory();
1734
+ static register(key, factory) {
1735
+ _MailerFactory.instance.register(key, factory);
1736
+ }
1737
+ static create(key, ...args) {
1738
+ return _MailerFactory.instance.create(key, ...args);
1739
+ }
1740
+ };
1741
+ MailerFactory.register("logger", (opt) => {
1742
+ return new import_neko_mailer.FunctionProvider((mail) => {
1743
+ logger().info({
1744
+ msg: "Sending email",
1745
+ mail
1746
+ });
1747
+ });
1748
+ });
1749
+ MailerFactory.register("SES", (opt) => {
1750
+ return new import_neko_mailer.SESProvider(opt);
1751
+ });
1752
+ MailerFactory.register("SMTP", (opt) => {
1753
+ return new import_neko_mailer.SMTPProvider(opt);
1754
+ });
1755
+ MailerFactory.register("MEMORY", (opt) => {
1756
+ return new import_neko_mailer.MemoryProvider();
1757
+ });
1758
+ var QueueFactory = class _QueueFactory {
1759
+ static {
1760
+ __name(this, "QueueFactory");
1761
+ }
1762
+ static instance = new FlexibleFactory();
1763
+ static register(key, factory) {
1764
+ _QueueFactory.instance.register(key, factory);
1765
+ }
1766
+ static create(key, ...args) {
1767
+ return _QueueFactory.instance.create(key, ...args);
1768
+ }
1769
+ };
1770
+ QueueFactory.register("database", (opt) => {
1771
+ let transport = new DatabaseTransport(opt);
1772
+ return new import_neko_queue.QueueConnection(transport);
1773
+ });
1774
+ QueueFactory.register("memory", (opt) => {
1775
+ let transport = new import_neko_queue2.MemoryTransport(opt);
1776
+ return new import_neko_queue.QueueConnection(transport);
1777
+ });
1778
+
1779
+ // src/facades.mts
1543
1780
  var router = (0, import_neko_helper.createSingleton)(() => new Router());
1544
1781
  var scheduler = (0, import_neko_helper.createSingleton)(() => {
1545
1782
  const rc = new import_neko_scheduler.Scheduler();
@@ -1610,27 +1847,19 @@ var logger = (0, import_neko_helper.createSingleton)((label) => {
1610
1847
  });
1611
1848
  var mailer = (0, import_neko_helper.createSingleton)((label) => {
1612
1849
  const mailer_config = import_neko_config.config.get(["mailer", label].join("."));
1613
- let provider;
1614
- if (mailer_config.provider === "logger") {
1615
- provider = new import_neko_mailer.FunctionProvider((mail) => {
1616
- logger().info({
1617
- msg: "Sending email",
1618
- mail
1619
- });
1620
- });
1621
- } else if (mailer_config.provider === "SES") {
1622
- provider = new import_neko_mailer.SESProvider(mailer_config.config);
1623
- } else if (mailer_config.provider === "SMTP") {
1624
- provider = new import_neko_mailer.SMTPProvider(mailer_config.config);
1625
- } else if (mailer_config.provider === "MEMORY") {
1626
- provider = new import_neko_mailer.MemoryProvider();
1627
- }
1628
- if (!provider) {
1629
- throw new Error(
1630
- `cannot initiate mailer provider: ${mailer_config?.provider}`
1631
- );
1850
+ let provider = MailerFactory.create(
1851
+ mailer_config.provider,
1852
+ mailer_config.config
1853
+ );
1854
+ const rc = new import_neko_mailer2.Mailer(provider);
1855
+ return rc;
1856
+ });
1857
+ var queue = (0, import_neko_helper.createSingleton)(async (label) => {
1858
+ const queue_config = import_neko_config.config.get(["queues", label].join("."));
1859
+ if (!queue_config) {
1860
+ throw new Error(`Queue configuration for '${label}' not found`);
1632
1861
  }
1633
- const rc = new import_neko_mailer.Mailer(provider);
1862
+ const rc = await QueueFactory.create(queue_config.type, queue_config);
1634
1863
  return rc;
1635
1864
  });
1636
1865
 
@@ -1645,29 +1874,46 @@ var MigrateCommand = class extends import_clipanion2.Command {
1645
1874
  __name(this, "MigrateCommand");
1646
1875
  }
1647
1876
  static paths = [[`migrate`]];
1648
- fresh = import_clipanion2.Option.Boolean("--fresh", false);
1877
+ fresh = import_clipanion2.Option.Boolean(`--fresh`, false, {
1878
+ description: `whether to delete and recreate database`
1879
+ });
1880
+ refresh = import_clipanion2.Option.Boolean(`--refresh`, false, {
1881
+ description: `whether to drop all tables before running migrations by using rollback function`
1882
+ });
1649
1883
  async execute() {
1650
1884
  await import_neko_context3.context_provider.run(async () => {
1651
1885
  const db2 = db();
1652
1886
  const schema = db2.getSchema();
1653
1887
  if (this.fresh) {
1654
- logger().info("dropping all tables!!");
1655
- let retry = true;
1656
- let retry_count = 0;
1657
- while (retry && retry_count < 10) {
1658
- retry = false;
1659
- retry_count++;
1660
- const tables = await schema.tables();
1661
- for (const table of tables) {
1662
- logger().info(`dropping table ${table.name}`);
1663
- try {
1664
- await schema.dropTable(table.name);
1665
- } catch {
1666
- logger().info(`failed to drop ${table.name}`);
1667
- retry = true;
1668
- }
1888
+ throw new Error("not implemented");
1889
+ }
1890
+ if (this.refresh) {
1891
+ logger().info("reverting all migrations!!");
1892
+ const existing_migrations = await db2.runQuery({
1893
+ sql: "select * from migrations order by created_at DESC",
1894
+ bindings: []
1895
+ });
1896
+ const migrationsDir2 = import_neko_config2.config.get("migration.path");
1897
+ for (const migration_record of existing_migrations) {
1898
+ logger().info(`rolling back ${migration_record.filename}`);
1899
+ try {
1900
+ const MigrationClass = (await import(import_path2.default.join(migrationsDir2, migration_record.filename))).default;
1901
+ const migrationInstance = new MigrationClass();
1902
+ await migrationInstance.down(db2.getSchema());
1903
+ await db2.runQuery({
1904
+ sql: "delete from migrations where filename = $1",
1905
+ bindings: [migration_record.filename]
1906
+ });
1907
+ } catch (error) {
1908
+ logger().error(
1909
+ `Failed to rollback migration ${migration_record.filename}: ${error}`
1910
+ );
1911
+ throw error;
1669
1912
  }
1670
1913
  }
1914
+ logger().info(
1915
+ `rolled back ${existing_migrations.length} migrations successfully!`
1916
+ );
1671
1917
  }
1672
1918
  if (!await schema.tableExists("migrations")) {
1673
1919
  await schema.createTable("migrations", (blueprint) => {
@@ -1779,7 +2025,7 @@ var MigrateRollbackCommand = class extends import_clipanion4.Command {
1779
2025
  __name(this, "MigrateRollbackCommand");
1780
2026
  }
1781
2027
  static paths = [[`migrate`, "rollback"]];
1782
- steps = import_clipanion4.Option.String(`--steps`, {
2028
+ steps = import_clipanion4.Option.String(`--steps`, "1", {
1783
2029
  description: `how many migrations to rollback`,
1784
2030
  validator: t.isNumber()
1785
2031
  });
@@ -1792,13 +2038,12 @@ var MigrateRollbackCommand = class extends import_clipanion4.Command {
1792
2038
  const dirEntries = await import_promises2.default.readdir(migrationsDir);
1793
2039
  files = dirEntries.filter((entry) => entry.endsWith(".ts")).sort();
1794
2040
  const migrations = await db2.runQuery({
1795
- sql: "select * from migrations order by created_at DESC",
1796
- bindings: []
2041
+ sql: "select * from migrations order by created_at DESC limit $1",
2042
+ bindings: [this.steps]
1797
2043
  });
1798
- const count = 0;
1799
2044
  for (const migration of migrations) {
1800
2045
  const class_to_migrate = migration.filename;
1801
- this.context.stdout.write(`rolling back ${class_to_migrate}`);
2046
+ logger().info(`rolling back ${class_to_migrate}`);
1802
2047
  const ClassToMigrate = (await import(import_path4.default.join(migrationsDir, class_to_migrate))).default;
1803
2048
  const c = new ClassToMigrate();
1804
2049
  await c.down(db2.getSchema());
@@ -1815,7 +2060,7 @@ cli().register(MigrateRollbackCommand);
1815
2060
  // src/app/console/StartCommand.mts
1816
2061
  var import_clipanion5 = require("clipanion");
1817
2062
  var import_neko_config5 = require("@devbro/neko-config");
1818
- var import_neko_sql = require("@devbro/neko-sql");
2063
+ var import_neko_sql2 = require("@devbro/neko-sql");
1819
2064
  var StartCommand = class extends import_clipanion5.Command {
1820
2065
  static {
1821
2066
  __name(this, "StartCommand");
@@ -1834,7 +2079,7 @@ var StartCommand = class extends import_clipanion5.Command {
1834
2079
  }
1835
2080
  logger().info(`Starting Server
1836
2081
  `);
1837
- import_neko_sql.PostgresqlConnection.defaults.idleTimeoutMillis = 1e4;
2082
+ import_neko_sql2.PostgresqlConnection.defaults.idleTimeoutMillis = 1e4;
1838
2083
  if (this.scheduler || this.all) {
1839
2084
  logger().info(`starting scheduler
1840
2085
  `);