miniledger 0.1.0 → 0.2.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.
@@ -1,5 +1,4 @@
1
1
  #!/usr/bin/env node
2
- #!/usr/bin/env node
3
2
  "use strict";
4
3
  var __create = Object.create;
5
4
  var __defProp = Object.defineProperty;
@@ -2631,7 +2630,7 @@ var MiniLedgerNode = class _MiniLedgerNode extends import_node_events3.EventEmit
2631
2630
  // src/api/server.ts
2632
2631
  var fs4 = __toESM(require("fs"), 1);
2633
2632
  var path4 = __toESM(require("path"), 1);
2634
- var import_hono8 = require("hono");
2633
+ var import_hono9 = require("hono");
2635
2634
  var import_cors = require("hono/cors");
2636
2635
 
2637
2636
  // src/api/middleware/logging.ts
@@ -2681,9 +2680,16 @@ function blockRoutes(node) {
2681
2680
  app.get("/blocks", async (c) => {
2682
2681
  const stores = node.getStores();
2683
2682
  const currentHeight = stores.blocks.getHeight();
2684
- const from = Math.max(0, currentHeight - 19);
2685
- const blocks = stores.blocks.getRange(from, currentHeight);
2686
- return c.json({ blocks, height: currentHeight });
2683
+ const page = Math.max(1, Number.parseInt(c.req.query("page") ?? "1", 10) || 1);
2684
+ const limit = Math.min(100, Math.max(1, Number.parseInt(c.req.query("limit") ?? "20", 10) || 20));
2685
+ const toHeight = currentHeight - (page - 1) * limit;
2686
+ const fromHeight = Math.max(0, toHeight - limit + 1);
2687
+ if (toHeight < 0) {
2688
+ return c.json({ blocks: [], height: currentHeight, page, limit, totalPages: Math.ceil((currentHeight + 1) / limit) });
2689
+ }
2690
+ const blocks = stores.blocks.getRange(fromHeight, toHeight).reverse();
2691
+ const totalPages = Math.ceil((currentHeight + 1) / limit);
2692
+ return c.json({ blocks, height: currentHeight, page, limit, totalPages });
2687
2693
  });
2688
2694
  return app;
2689
2695
  }
@@ -2717,6 +2723,49 @@ function transactionRoutes(node) {
2717
2723
  return c.json({ error: message }, 400);
2718
2724
  }
2719
2725
  });
2726
+ app.get("/tx/recent", async (c) => {
2727
+ const page = Math.max(1, Number.parseInt(c.req.query("page") ?? "1", 10) || 1);
2728
+ const limit = Math.min(100, Math.max(1, Number.parseInt(c.req.query("limit") ?? "20", 10) || 20));
2729
+ const typeFilter = c.req.query("type") ?? "";
2730
+ const offset = (page - 1) * limit;
2731
+ try {
2732
+ let countSql = "SELECT COUNT(*) as total FROM transactions WHERE status = 'confirmed'";
2733
+ let dataSql = "SELECT hash, type, sender, nonce, timestamp, payload, signature, block_height, position FROM transactions WHERE status = 'confirmed'";
2734
+ const params = [];
2735
+ const countParams = [];
2736
+ if (typeFilter) {
2737
+ countSql += " AND type = ?";
2738
+ dataSql += " AND type = ?";
2739
+ params.push(typeFilter);
2740
+ countParams.push(typeFilter);
2741
+ }
2742
+ dataSql += " ORDER BY block_height DESC, position DESC LIMIT ? OFFSET ?";
2743
+ params.push(limit, offset);
2744
+ const db = node.getDatabase().raw();
2745
+ const countRow = db.prepare(countSql).get(...countParams);
2746
+ const rows = db.prepare(dataSql).all(...params);
2747
+ const transactions = rows.map((r) => ({
2748
+ hash: r.hash,
2749
+ type: r.type,
2750
+ sender: r.sender,
2751
+ nonce: r.nonce,
2752
+ timestamp: r.timestamp,
2753
+ payload: JSON.parse(r.payload),
2754
+ signature: r.signature,
2755
+ blockHeight: r.block_height
2756
+ }));
2757
+ const totalPages = Math.ceil(countRow.total / limit);
2758
+ return c.json({ transactions, total: countRow.total, page, limit, totalPages });
2759
+ } catch (err) {
2760
+ const message = err instanceof Error ? err.message : "Query failed";
2761
+ return c.json({ error: message }, 500);
2762
+ }
2763
+ });
2764
+ app.get("/tx/sender/:pubkey", async (c) => {
2765
+ const pubkey = c.req.param("pubkey");
2766
+ const transactions = node.getStores().txs.getBySender(pubkey, 200);
2767
+ return c.json({ transactions, count: transactions.length });
2768
+ });
2720
2769
  app.get("/tx/:hash", async (c) => {
2721
2770
  const hash = c.req.param("hash");
2722
2771
  const tx = await node.getTransaction(hash);
@@ -2735,6 +2784,31 @@ function transactionRoutes(node) {
2735
2784
  var import_hono4 = require("hono");
2736
2785
  function stateRoutes(node) {
2737
2786
  const app = new import_hono4.Hono();
2787
+ app.get("/state", async (c) => {
2788
+ const page = Math.max(1, Number.parseInt(c.req.query("page") ?? "1", 10) || 1);
2789
+ const limit = Math.min(100, Math.max(1, Number.parseInt(c.req.query("limit") ?? "20", 10) || 20));
2790
+ const offset = (page - 1) * limit;
2791
+ try {
2792
+ const db = node.getDatabase().raw();
2793
+ const countRow = db.prepare("SELECT COUNT(*) as total FROM world_state WHERE key NOT LIKE '\\_%' ESCAPE '\\'").get();
2794
+ const rows = db.prepare(
2795
+ "SELECT key, value, version, updated_at, updated_by, block_height FROM world_state WHERE key NOT LIKE '\\_%' ESCAPE '\\' ORDER BY updated_at DESC LIMIT ? OFFSET ?"
2796
+ ).all(limit, offset);
2797
+ const entries = rows.map((r) => ({
2798
+ key: r.key,
2799
+ value: JSON.parse(r.value),
2800
+ version: r.version,
2801
+ updatedAt: r.updated_at,
2802
+ updatedBy: r.updated_by,
2803
+ blockHeight: r.block_height
2804
+ }));
2805
+ const totalPages = Math.ceil(countRow.total / limit);
2806
+ return c.json({ entries, total: countRow.total, page, limit, totalPages });
2807
+ } catch (err) {
2808
+ const message = err instanceof Error ? err.message : "Query failed";
2809
+ return c.json({ error: message }, 500);
2810
+ }
2811
+ });
2738
2812
  app.get("/state/:key", async (c) => {
2739
2813
  const key = c.req.param("key");
2740
2814
  const entry = await node.getState(key);
@@ -2824,6 +2898,48 @@ function governanceRoutes(node) {
2824
2898
  return app;
2825
2899
  }
2826
2900
 
2901
+ // src/api/routes/search.ts
2902
+ var import_hono8 = require("hono");
2903
+ function searchRoutes(node) {
2904
+ const app = new import_hono8.Hono();
2905
+ app.get("/search", async (c) => {
2906
+ const q = (c.req.query("q") ?? "").trim();
2907
+ if (!q) return c.json({ error: "Missing query parameter q" }, 400);
2908
+ const stores = node.getStores();
2909
+ if (/^\d+$/.test(q)) {
2910
+ const height = Number.parseInt(q, 10);
2911
+ const block = stores.blocks.getByHeight(height);
2912
+ if (block) {
2913
+ return c.json({ type: "block", height: block.height, hash: block.hash });
2914
+ }
2915
+ }
2916
+ if (/^[0-9a-fA-F]{16,}$/.test(q)) {
2917
+ const tx = stores.txs.getByHash(q);
2918
+ if (tx) {
2919
+ return c.json({ type: "transaction", hash: tx.hash });
2920
+ }
2921
+ const block = stores.blocks.getByHash(q);
2922
+ if (block) {
2923
+ return c.json({ type: "block", height: block.height, hash: block.hash });
2924
+ }
2925
+ const senderTxs = stores.txs.getBySender(q, 1);
2926
+ if (senderTxs.length > 0) {
2927
+ return c.json({ type: "address", pubkey: q });
2928
+ }
2929
+ }
2930
+ const stateEntry = stores.state.get(q);
2931
+ if (stateEntry) {
2932
+ return c.json({ type: "state", key: stateEntry.key });
2933
+ }
2934
+ const contract = node.getContractRegistry().getInstance(q);
2935
+ if (contract) {
2936
+ return c.json({ type: "contract", name: contract.name });
2937
+ }
2938
+ return c.json({ type: "not_found", query: q }, 404);
2939
+ });
2940
+ return app;
2941
+ }
2942
+
2827
2943
  // src/api/server.ts
2828
2944
  var import_meta = {};
2829
2945
  function findDashboardDir() {
@@ -2837,16 +2953,8 @@ function findDashboardDir() {
2837
2953
  }
2838
2954
  return null;
2839
2955
  }
2840
- var MIME_TYPES = {
2841
- ".html": "text/html",
2842
- ".css": "text/css",
2843
- ".js": "application/javascript",
2844
- ".json": "application/json",
2845
- ".png": "image/png",
2846
- ".svg": "image/svg+xml"
2847
- };
2848
2956
  function createApp(node) {
2849
- const app = new import_hono8.Hono();
2957
+ const app = new import_hono9.Hono();
2850
2958
  if (node.config.api.cors) {
2851
2959
  app.use("*", (0, import_cors.cors)());
2852
2960
  }
@@ -2858,20 +2966,24 @@ function createApp(node) {
2858
2966
  app.route("/", identityRoutes(node));
2859
2967
  app.route("/", networkRoutes(node));
2860
2968
  app.route("/", governanceRoutes(node));
2969
+ app.route("/", searchRoutes(node));
2861
2970
  const dashboardDir = findDashboardDir();
2862
2971
  if (dashboardDir) {
2863
- app.get("/dashboard", (c) => {
2972
+ app.get("/dashboard/app.js", (c) => {
2973
+ const content = fs4.readFileSync(path4.join(dashboardDir, "app.js"), "utf-8");
2974
+ return c.text(content, 200, { "Content-Type": "application/javascript" });
2975
+ });
2976
+ app.get("/dashboard/style.css", (c) => {
2977
+ const content = fs4.readFileSync(path4.join(dashboardDir, "style.css"), "utf-8");
2978
+ return c.text(content, 200, { "Content-Type": "text/css" });
2979
+ });
2980
+ app.get("/dashboard/*", (c) => {
2864
2981
  const html = fs4.readFileSync(path4.join(dashboardDir, "index.html"), "utf-8");
2865
2982
  return c.html(html);
2866
2983
  });
2867
- app.get("/dashboard/:file", (c) => {
2868
- const file = c.req.param("file");
2869
- const filePath = path4.join(dashboardDir, file);
2870
- if (!fs4.existsSync(filePath)) return c.notFound();
2871
- const ext = path4.extname(file);
2872
- const mime = MIME_TYPES[ext] ?? "application/octet-stream";
2873
- const content = fs4.readFileSync(filePath, "utf-8");
2874
- return c.text(content, 200, { "Content-Type": mime });
2984
+ app.get("/dashboard", (c) => {
2985
+ const html = fs4.readFileSync(path4.join(dashboardDir, "index.html"), "utf-8");
2986
+ return c.html(html);
2875
2987
  });
2876
2988
  }
2877
2989
  app.get("/", (c) => {