@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
@@ -1197,6 +1197,9 @@ module.exports = __toCommonJS(migrate_exports);
1197
1197
  var import_neko_context = require("@devbro/neko-context");
1198
1198
  var import_errors = require("@devbro/neko-http/errors");
1199
1199
 
1200
+ // ../neko-router/dist/CompiledRoute.mjs
1201
+ var import_stream = require("stream");
1202
+
1200
1203
  // ../neko-router/dist/Middleware.mjs
1201
1204
  var Middleware = class {
1202
1205
  static {
@@ -1274,6 +1277,9 @@ var CompiledRoute = class {
1274
1277
  if (typeof value.toJson === "function") {
1275
1278
  return traverse(value.toJson());
1276
1279
  }
1280
+ if (typeof value.toJSON === "function") {
1281
+ return traverse(value.toJSON());
1282
+ }
1277
1283
  if (Array.isArray(value)) {
1278
1284
  return value.map(traverse);
1279
1285
  }
@@ -1298,7 +1304,7 @@ var CompiledRoute = class {
1298
1304
  }
1299
1305
  return String(obj);
1300
1306
  }
1301
- processResponseBody(res, controller_rc) {
1307
+ async processResponseBody(res, controller_rc) {
1302
1308
  if (controller_rc && res.writableEnded) {
1303
1309
  throw new Error("cannot write to response, response has already ended");
1304
1310
  }
@@ -1307,18 +1313,36 @@ var CompiledRoute = class {
1307
1313
  }
1308
1314
  if (controller_rc) {
1309
1315
  const header_content_type = res.getHeader("Content-Type");
1310
- if (!header_content_type && typeof controller_rc === "object") {
1316
+ if (controller_rc instanceof import_stream.Stream || Buffer.isBuffer(controller_rc)) {
1317
+ await this.writeAsync(res, controller_rc);
1318
+ res.end();
1319
+ } else if (!header_content_type && typeof controller_rc === "object") {
1311
1320
  res.setHeader("Content-Type", "application/json");
1321
+ res.end(this.convertToString(controller_rc));
1312
1322
  } else if (!header_content_type) {
1313
1323
  res.setHeader("Content-Type", "text/plain");
1324
+ res.end(this.convertToString(controller_rc));
1325
+ } else {
1326
+ res.end(this.convertToString(controller_rc));
1314
1327
  }
1315
- res.end(this.convertToString(controller_rc));
1316
1328
  return;
1317
1329
  } else {
1318
1330
  res.statusCode = [200].includes(res.statusCode) ? 204 : res.statusCode;
1319
1331
  res.end();
1320
1332
  }
1321
1333
  }
1334
+ async writeAsync(res, chunk) {
1335
+ return new Promise((resolve, reject) => {
1336
+ const ok = res.write(chunk, (err) => {
1337
+ if (err) reject(err);
1338
+ });
1339
+ if (ok) {
1340
+ resolve(0);
1341
+ } else {
1342
+ res.once("drain", resolve);
1343
+ }
1344
+ });
1345
+ }
1322
1346
  async runMiddlewares(middlewares, req, res) {
1323
1347
  let index = 0;
1324
1348
  const me = this;
@@ -1376,7 +1400,7 @@ var Route = class {
1376
1400
  i = start;
1377
1401
  } else if (char === "*") {
1378
1402
  let start = i + 1;
1379
- while (start < path5.length && /[a-zA-Z0-9_]/.test(path5[start])) {
1403
+ while (start < path5.length && /[a-zA-Z0-9_\.]/.test(path5[start])) {
1380
1404
  start++;
1381
1405
  }
1382
1406
  tokens.push({ type: "WILDCARD", value: path5.slice(i + 1, start) });
@@ -1446,6 +1470,10 @@ var Route = class {
1446
1470
  params: r.groups || {}
1447
1471
  };
1448
1472
  }
1473
+ prependMiddleware(middlewares) {
1474
+ this.middlewares = [].concat(middlewares, this.middlewares);
1475
+ return this;
1476
+ }
1449
1477
  addMiddleware(middlewares) {
1450
1478
  this.middlewares = this.middlewares.concat(middlewares);
1451
1479
  return this;
@@ -1460,6 +1488,62 @@ var Route = class {
1460
1488
 
1461
1489
  // ../neko-router/dist/Router.mjs
1462
1490
  var import_path = __toESM(require("path"), 1);
1491
+
1492
+ // ../node_modules/url-join/lib/url-join.js
1493
+ function normalize(strArray) {
1494
+ var resultArray = [];
1495
+ if (strArray.length === 0) {
1496
+ return "";
1497
+ }
1498
+ if (typeof strArray[0] !== "string") {
1499
+ throw new TypeError("Url must be a string. Received " + strArray[0]);
1500
+ }
1501
+ if (strArray[0].match(/^[^/:]+:\/*$/) && strArray.length > 1) {
1502
+ var first = strArray.shift();
1503
+ strArray[0] = first + strArray[0];
1504
+ }
1505
+ if (strArray[0].match(/^file:\/\/\//)) {
1506
+ strArray[0] = strArray[0].replace(/^([^/:]+):\/*/, "$1:///");
1507
+ } else {
1508
+ strArray[0] = strArray[0].replace(/^([^/:]+):\/*/, "$1://");
1509
+ }
1510
+ for (var i = 0; i < strArray.length; i++) {
1511
+ var component = strArray[i];
1512
+ if (typeof component !== "string") {
1513
+ throw new TypeError("Url must be a string. Received " + component);
1514
+ }
1515
+ if (component === "") {
1516
+ continue;
1517
+ }
1518
+ if (i > 0) {
1519
+ component = component.replace(/^[\/]+/, "");
1520
+ }
1521
+ if (i < strArray.length - 1) {
1522
+ component = component.replace(/[\/]+$/, "");
1523
+ } else {
1524
+ component = component.replace(/[\/]+$/, "/");
1525
+ }
1526
+ resultArray.push(component);
1527
+ }
1528
+ var str = resultArray.join("/");
1529
+ str = str.replace(/\/(\?|&|#[^!])/g, "$1");
1530
+ var parts = str.split("?");
1531
+ str = parts.shift() + (parts.length > 0 ? "?" : "") + parts.join("&");
1532
+ return str;
1533
+ }
1534
+ __name(normalize, "normalize");
1535
+ function urlJoin() {
1536
+ var input;
1537
+ if (typeof arguments[0] === "object") {
1538
+ input = arguments[0];
1539
+ } else {
1540
+ input = [].slice.call(arguments);
1541
+ }
1542
+ return normalize(input);
1543
+ }
1544
+ __name(urlJoin, "urlJoin");
1545
+
1546
+ // ../neko-router/dist/Router.mjs
1463
1547
  var Router = class {
1464
1548
  static {
1465
1549
  __name(this, "Router");
@@ -1484,6 +1568,12 @@ var Router = class {
1484
1568
  }).addMiddleware([...controller.baseMiddlewares, ...route.middlewares]);
1485
1569
  }
1486
1570
  }
1571
+ addRouter(path22, router2) {
1572
+ for (const route of router2.routes) {
1573
+ let path222 = urlJoin("/", path22, route.path);
1574
+ this.addRoute(route.methods, path222, route.handler).addMiddleware(router2.getMiddlewares()).addMiddleware(route.getMiddlewares());
1575
+ }
1576
+ }
1487
1577
  addGlobalMiddleware(middlewares) {
1488
1578
  this.middlewares = this.middlewares.concat(middlewares);
1489
1579
  }
@@ -1524,7 +1614,7 @@ var import_neko_scheduler = require("@devbro/neko-scheduler");
1524
1614
  var import_neko_helper = require("@devbro/neko-helper");
1525
1615
  var import_neko_context2 = require("@devbro/neko-context");
1526
1616
  var import_neko_storage = require("@devbro/neko-storage");
1527
- var import_neko_mailer = require("@devbro/neko-mailer");
1617
+ var import_neko_mailer2 = require("@devbro/neko-mailer");
1528
1618
  var import_neko_config = require("@devbro/neko-config");
1529
1619
  var import_clipanion = require("clipanion");
1530
1620
 
@@ -1535,6 +1625,153 @@ __reExport(http_exports, require("@devbro/neko-http"));
1535
1625
  // src/facades.mts
1536
1626
  var yup = __toESM(require("yup"), 1);
1537
1627
  var import_neko_logger = require("@devbro/neko-logger");
1628
+
1629
+ // src/factories.mts
1630
+ var import_neko_mailer = require("@devbro/neko-mailer");
1631
+ var import_neko_queue = require("@devbro/neko-queue");
1632
+ var import_neko_queue2 = require("@devbro/neko-queue");
1633
+
1634
+ // src/queue.mts
1635
+ var queue_exports = {};
1636
+ __export(queue_exports, {
1637
+ DatabaseTransport: () => DatabaseTransport
1638
+ });
1639
+ __reExport(queue_exports, require("@devbro/neko-queue"));
1640
+ var import_neko_sql = require("@devbro/neko-sql");
1641
+ var DatabaseTransport = class {
1642
+ // default to 100 messages per fetch
1643
+ constructor(db_config) {
1644
+ this.db_config = db_config;
1645
+ }
1646
+ static {
1647
+ __name(this, "DatabaseTransport");
1648
+ }
1649
+ listenInterval = 6e4;
1650
+ // default to 1 minute
1651
+ messageLimit = 100;
1652
+ setListenInterval(interval) {
1653
+ this.listenInterval = interval;
1654
+ }
1655
+ setMessageLimit(limit) {
1656
+ this.messageLimit = limit;
1657
+ }
1658
+ async dispatch(channel, message) {
1659
+ const conn = new import_neko_sql.PostgresqlConnection(this.db_config);
1660
+ try {
1661
+ await conn.connect();
1662
+ let q = conn.getQuery();
1663
+ await q.table("queue_messages").insert({
1664
+ channel,
1665
+ message,
1666
+ processed: false,
1667
+ created_at: /* @__PURE__ */ new Date(),
1668
+ updated_at: /* @__PURE__ */ new Date(),
1669
+ last_tried_at: null,
1670
+ process_message: ""
1671
+ });
1672
+ } finally {
1673
+ await conn.disconnect();
1674
+ }
1675
+ }
1676
+ async listen(channel, callback) {
1677
+ return new Promise(async (resolve, reject) => {
1678
+ setInterval(async () => {
1679
+ const conn = new import_neko_sql.PostgresqlConnection(this.db_config);
1680
+ try {
1681
+ await conn.connect();
1682
+ let q = conn.getQuery();
1683
+ let messages = await q.table("queue_messages").whereOp("channel", "=", channel).whereOp("processed", "=", false).limit(this.messageLimit).orderBy("last_tried_at", "asc").get();
1684
+ for (let msg of messages) {
1685
+ try {
1686
+ await callback(msg.message);
1687
+ await q.table("queue_messages").whereOp("id", "=", msg.id).update({
1688
+ processed: true,
1689
+ updated_at: /* @__PURE__ */ new Date()
1690
+ });
1691
+ } catch (error) {
1692
+ await q.table("queue_messages").whereOp("id", "=", msg.id).update({
1693
+ processed: false,
1694
+ last_tried_at: /* @__PURE__ */ new Date(),
1695
+ process_message: error.message || "Error processing message"
1696
+ });
1697
+ }
1698
+ }
1699
+ } finally {
1700
+ await conn.disconnect();
1701
+ }
1702
+ }, this.listenInterval);
1703
+ });
1704
+ }
1705
+ };
1706
+
1707
+ // src/factories.mts
1708
+ var FlexibleFactory = class {
1709
+ static {
1710
+ __name(this, "FlexibleFactory");
1711
+ }
1712
+ registry = /* @__PURE__ */ new Map();
1713
+ register(key, ctor) {
1714
+ this.registry.set(key, ctor);
1715
+ }
1716
+ create(key, ...args) {
1717
+ const ctor = this.registry.get(key);
1718
+ if (!ctor) {
1719
+ throw new Error(`No factory registered for key: ${key}`);
1720
+ }
1721
+ return new ctor(...args);
1722
+ }
1723
+ };
1724
+ var MailerFactory = class _MailerFactory {
1725
+ static {
1726
+ __name(this, "MailerFactory");
1727
+ }
1728
+ static instance = new FlexibleFactory();
1729
+ static register(key, factory) {
1730
+ _MailerFactory.instance.register(key, factory);
1731
+ }
1732
+ static create(key, ...args) {
1733
+ return _MailerFactory.instance.create(key, ...args);
1734
+ }
1735
+ };
1736
+ MailerFactory.register("logger", (opt) => {
1737
+ return new import_neko_mailer.FunctionProvider((mail) => {
1738
+ logger().info({
1739
+ msg: "Sending email",
1740
+ mail
1741
+ });
1742
+ });
1743
+ });
1744
+ MailerFactory.register("SES", (opt) => {
1745
+ return new import_neko_mailer.SESProvider(opt);
1746
+ });
1747
+ MailerFactory.register("SMTP", (opt) => {
1748
+ return new import_neko_mailer.SMTPProvider(opt);
1749
+ });
1750
+ MailerFactory.register("MEMORY", (opt) => {
1751
+ return new import_neko_mailer.MemoryProvider();
1752
+ });
1753
+ var QueueFactory = class _QueueFactory {
1754
+ static {
1755
+ __name(this, "QueueFactory");
1756
+ }
1757
+ static instance = new FlexibleFactory();
1758
+ static register(key, factory) {
1759
+ _QueueFactory.instance.register(key, factory);
1760
+ }
1761
+ static create(key, ...args) {
1762
+ return _QueueFactory.instance.create(key, ...args);
1763
+ }
1764
+ };
1765
+ QueueFactory.register("database", (opt) => {
1766
+ let transport = new DatabaseTransport(opt);
1767
+ return new import_neko_queue.QueueConnection(transport);
1768
+ });
1769
+ QueueFactory.register("memory", (opt) => {
1770
+ let transport = new import_neko_queue2.MemoryTransport(opt);
1771
+ return new import_neko_queue.QueueConnection(transport);
1772
+ });
1773
+
1774
+ // src/facades.mts
1538
1775
  var router = (0, import_neko_helper.createSingleton)(() => new Router());
1539
1776
  var scheduler = (0, import_neko_helper.createSingleton)(() => {
1540
1777
  const rc = new import_neko_scheduler.Scheduler();
@@ -1605,27 +1842,19 @@ var logger = (0, import_neko_helper.createSingleton)((label) => {
1605
1842
  });
1606
1843
  var mailer = (0, import_neko_helper.createSingleton)((label) => {
1607
1844
  const mailer_config = import_neko_config.config.get(["mailer", label].join("."));
1608
- let provider;
1609
- if (mailer_config.provider === "logger") {
1610
- provider = new import_neko_mailer.FunctionProvider((mail) => {
1611
- logger().info({
1612
- msg: "Sending email",
1613
- mail
1614
- });
1615
- });
1616
- } else if (mailer_config.provider === "SES") {
1617
- provider = new import_neko_mailer.SESProvider(mailer_config.config);
1618
- } else if (mailer_config.provider === "SMTP") {
1619
- provider = new import_neko_mailer.SMTPProvider(mailer_config.config);
1620
- } else if (mailer_config.provider === "MEMORY") {
1621
- provider = new import_neko_mailer.MemoryProvider();
1622
- }
1623
- if (!provider) {
1624
- throw new Error(
1625
- `cannot initiate mailer provider: ${mailer_config?.provider}`
1626
- );
1845
+ let provider = MailerFactory.create(
1846
+ mailer_config.provider,
1847
+ mailer_config.config
1848
+ );
1849
+ const rc = new import_neko_mailer2.Mailer(provider);
1850
+ return rc;
1851
+ });
1852
+ var queue = (0, import_neko_helper.createSingleton)(async (label) => {
1853
+ const queue_config = import_neko_config.config.get(["queues", label].join("."));
1854
+ if (!queue_config) {
1855
+ throw new Error(`Queue configuration for '${label}' not found`);
1627
1856
  }
1628
- const rc = new import_neko_mailer.Mailer(provider);
1857
+ const rc = await QueueFactory.create(queue_config.type, queue_config);
1629
1858
  return rc;
1630
1859
  });
1631
1860
 
@@ -1640,29 +1869,46 @@ var MigrateCommand = class extends import_clipanion2.Command {
1640
1869
  __name(this, "MigrateCommand");
1641
1870
  }
1642
1871
  static paths = [[`migrate`]];
1643
- fresh = import_clipanion2.Option.Boolean("--fresh", false);
1872
+ fresh = import_clipanion2.Option.Boolean(`--fresh`, false, {
1873
+ description: `whether to delete and recreate database`
1874
+ });
1875
+ refresh = import_clipanion2.Option.Boolean(`--refresh`, false, {
1876
+ description: `whether to drop all tables before running migrations by using rollback function`
1877
+ });
1644
1878
  async execute() {
1645
1879
  await import_neko_context3.context_provider.run(async () => {
1646
1880
  const db2 = db();
1647
1881
  const schema = db2.getSchema();
1648
1882
  if (this.fresh) {
1649
- logger().info("dropping all tables!!");
1650
- let retry = true;
1651
- let retry_count = 0;
1652
- while (retry && retry_count < 10) {
1653
- retry = false;
1654
- retry_count++;
1655
- const tables = await schema.tables();
1656
- for (const table of tables) {
1657
- logger().info(`dropping table ${table.name}`);
1658
- try {
1659
- await schema.dropTable(table.name);
1660
- } catch {
1661
- logger().info(`failed to drop ${table.name}`);
1662
- retry = true;
1663
- }
1883
+ throw new Error("not implemented");
1884
+ }
1885
+ if (this.refresh) {
1886
+ logger().info("reverting all migrations!!");
1887
+ const existing_migrations = await db2.runQuery({
1888
+ sql: "select * from migrations order by created_at DESC",
1889
+ bindings: []
1890
+ });
1891
+ const migrationsDir2 = import_neko_config2.config.get("migration.path");
1892
+ for (const migration_record of existing_migrations) {
1893
+ logger().info(`rolling back ${migration_record.filename}`);
1894
+ try {
1895
+ const MigrationClass = (await import(import_path2.default.join(migrationsDir2, migration_record.filename))).default;
1896
+ const migrationInstance = new MigrationClass();
1897
+ await migrationInstance.down(db2.getSchema());
1898
+ await db2.runQuery({
1899
+ sql: "delete from migrations where filename = $1",
1900
+ bindings: [migration_record.filename]
1901
+ });
1902
+ } catch (error) {
1903
+ logger().error(
1904
+ `Failed to rollback migration ${migration_record.filename}: ${error}`
1905
+ );
1906
+ throw error;
1664
1907
  }
1665
1908
  }
1909
+ logger().info(
1910
+ `rolled back ${existing_migrations.length} migrations successfully!`
1911
+ );
1666
1912
  }
1667
1913
  if (!await schema.tableExists("migrations")) {
1668
1914
  await schema.createTable("migrations", (blueprint) => {
@@ -1774,7 +2020,7 @@ var MigrateRollbackCommand = class extends import_clipanion4.Command {
1774
2020
  __name(this, "MigrateRollbackCommand");
1775
2021
  }
1776
2022
  static paths = [[`migrate`, "rollback"]];
1777
- steps = import_clipanion4.Option.String(`--steps`, {
2023
+ steps = import_clipanion4.Option.String(`--steps`, "1", {
1778
2024
  description: `how many migrations to rollback`,
1779
2025
  validator: t.isNumber()
1780
2026
  });
@@ -1787,13 +2033,12 @@ var MigrateRollbackCommand = class extends import_clipanion4.Command {
1787
2033
  const dirEntries = await import_promises2.default.readdir(migrationsDir);
1788
2034
  files = dirEntries.filter((entry) => entry.endsWith(".ts")).sort();
1789
2035
  const migrations = await db2.runQuery({
1790
- sql: "select * from migrations order by created_at DESC",
1791
- bindings: []
2036
+ sql: "select * from migrations order by created_at DESC limit $1",
2037
+ bindings: [this.steps]
1792
2038
  });
1793
- const count = 0;
1794
2039
  for (const migration of migrations) {
1795
2040
  const class_to_migrate = migration.filename;
1796
- this.context.stdout.write(`rolling back ${class_to_migrate}`);
2041
+ logger().info(`rolling back ${class_to_migrate}`);
1797
2042
  const ClassToMigrate = (await import(import_path4.default.join(migrationsDir, class_to_migrate))).default;
1798
2043
  const c = new ClassToMigrate();
1799
2044
  await c.down(db2.getSchema());