apiforgejs 0.1.0 → 1.0.1

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 (3) hide show
  1. package/README.md +25 -34
  2. package/package.json +11 -3
  3. package/src/ui.html +9 -11
package/README.md CHANGED
@@ -1,9 +1,18 @@
1
1
  # apiforgejs
2
2
 
3
- API observability & intelligence SDK for Express.js — local-first, privacy-first.
3
+ **API observability & intelligence for Express.js — local-first, privacy-first.**
4
+
5
+ [![npm version](https://img.shields.io/npm/v/apiforgejs?color=0066FF)](https://www.npmjs.com/package/apiforgejs)
6
+ [![CI](https://img.shields.io/github/actions/workflow/status/APIForge-Organisation/sdk-nodejs/ci.yml?branch=main&label=CI)](https://github.com/APIForge-Organisation/sdk-nodejs/actions)
7
+ [![License: MIT](https://img.shields.io/badge/license-MIT-green)](LICENSE)
8
+ [![Node.js](https://img.shields.io/badge/node-%3E%3D22.5-brightgreen)](https://nodejs.org)
4
9
 
5
10
  > Track latency, error rates, and behavioral trends of your APIs. Everything stays on your machine.
6
11
 
12
+ **→ [Full documentation](https://apiforge-organisation.github.io/docs/)**
13
+
14
+ ---
15
+
7
16
  ## Install
8
17
 
9
18
  ```bash
@@ -18,13 +27,11 @@ const { apiforge } = require('apiforgejs');
18
27
 
19
28
  const app = express();
20
29
 
21
- // Add the middleware — one line, zero config required
22
30
  app.use(apiforge({ mode: 'local' }));
23
31
 
24
32
  app.get('/users/:id', (req, res) => res.json({ id: req.params.id }));
25
33
 
26
34
  app.listen(3000, () => {
27
- console.log('App running on :3000');
28
35
  // Dashboard auto-starts at http://localhost:4242
29
36
  });
30
37
  ```
@@ -45,15 +52,15 @@ Data is collected locally in `.apiforge.db` (SQLite). Nothing leaves your machin
45
52
 
46
53
  ```javascript
47
54
  app.use(apiforge({
48
- mode: 'local', // only supported mode in v0.x
49
- dbPath: '.apiforge.db', // SQLite file location
50
- dashboardPort: 4242, // set to 0 to disable dashboard
51
- flushInterval: 60_000, // flush to SQLite every 60s (ms)
52
- env: process.env.NODE_ENV, // 'production' | 'staging' | 'development'
53
- release: process.env.APP_VERSION, // enables release regression detection
54
- service: 'my-api', // label for multi-service setups
55
- sampling: 1.0, // 0.0–1.0 sample rate
56
- ignorePaths: ['/health', '/ping'], // paths to skip
55
+ mode: 'local',
56
+ dbPath: '.apiforge.db',
57
+ dashboardPort: 4242, // set to 0 to disable
58
+ flushInterval: 60_000, // flush to SQLite every 60s (ms)
59
+ env: process.env.NODE_ENV,
60
+ release: process.env.APP_VERSION,
61
+ service: 'my-api',
62
+ sampling: 1.0, // 0.0–1.0 sample rate
63
+ ignorePaths: ['/health', '/ping'],
57
64
  }));
58
65
  ```
59
66
 
@@ -64,11 +71,11 @@ Pass your release version to enable before/after deployment comparison:
64
71
  ```javascript
65
72
  app.use(apiforge({
66
73
  mode: 'local',
67
- release: process.env.npm_package_version, // or 'v1.4.0', git SHA, etc.
74
+ release: process.env.npm_package_version,
68
75
  }));
69
76
  ```
70
77
 
71
- When a new release is detected, APIForge automatically compares P90 latency before vs. after and surfaces regressions as insights.
78
+ When a new release is detected, APIForge compares P90 latency before vs. after and surfaces regressions as insights automatically.
72
79
 
73
80
  ## Graceful shutdown
74
81
 
@@ -77,7 +84,7 @@ const forge = apiforge({ mode: 'local' });
77
84
  app.use(forge);
78
85
 
79
86
  process.on('SIGTERM', () => {
80
- forge.shutdown(); // flush remaining buffer to SQLite
87
+ forge.shutdown();
81
88
  process.exit(0);
82
89
  });
83
90
  ```
@@ -91,27 +98,11 @@ The SDK **never** collects:
91
98
  - Route parameter values (`/users/12345` → only `/users/:id` is stored)
92
99
  - IP addresses or User-Agent strings
93
100
 
94
- Collected fields: route pattern, HTTP method, status code, latency (ms), timestamp, and optional env/release/service labels.
95
-
96
- ## Data collected
97
-
98
- ```json
99
- {
100
- "route": "GET /users/:id",
101
- "method": "GET",
102
- "status": 200,
103
- "duration_ms": 134.7,
104
- "timestamp": "2026-05-13T10:00:00.000Z",
105
- "env": "production",
106
- "release": "v1.4.0"
107
- }
108
- ```
109
-
110
101
  ## Requirements
111
102
 
112
- - Node.js >= 18
113
- - Express >= 4
103
+ - Node.js 22.5 (uses the built-in `node:sqlite` module)
104
+ - Express.js v4 or v5
114
105
 
115
106
  ## License
116
107
 
117
- MIT
108
+ MIT — [APIForge Organisation](https://github.com/APIForge-Organisation)
package/package.json CHANGED
@@ -1,11 +1,19 @@
1
1
  {
2
2
  "name": "apiforgejs",
3
- "version": "0.1.0",
3
+ "version": "1.0.1",
4
4
  "description": "API observability & intelligence SDK for Express.js — local-first, privacy-first",
5
5
  "main": "src/index.js",
6
6
  "keywords": [
7
- "api", "observability", "monitoring", "express", "metrics",
8
- "performance", "analytics", "middleware", "latency", "local-first"
7
+ "api",
8
+ "observability",
9
+ "monitoring",
10
+ "express",
11
+ "metrics",
12
+ "performance",
13
+ "analytics",
14
+ "middleware",
15
+ "latency",
16
+ "local-first"
9
17
  ],
10
18
  "author": "APIForge",
11
19
  "license": "MIT",
package/src/ui.html CHANGED
@@ -315,11 +315,9 @@ input.fld:focus { outline: 2px solid var(--accent-line); border-color: var(--acc
315
315
  <body>
316
316
  <div id="root"></div>
317
317
 
318
- <!-- React UMD served from node_modules by apiforgejs dashboard server -->
319
- <script src="/assets/react.js"></script>
320
- <script src="/assets/react-dom.js"></script>
321
- <!-- Babel standalone for in-browser JSX transpilation -->
322
- <script src="https://unpkg.com/@babel/standalone@7/babel.min.js" crossorigin></script>
318
+ <script src="https://cdn.jsdelivr.net/npm/react@18/umd/react.production.min.js" crossorigin></script>
319
+ <script src="https://cdn.jsdelivr.net/npm/react-dom@18/umd/react-dom.production.min.js" crossorigin></script>
320
+ <script src="https://cdn.jsdelivr.net/npm/@babel/standalone@7/babel.min.js" crossorigin></script>
323
321
 
324
322
  <script type="text/babel" data-presets="react">
325
323
  'use strict';
@@ -719,7 +717,7 @@ function StatusStackChart({ data, height = 200 }) {
719
717
  }
720
718
 
721
719
  // ─── Overview ─────────────────────────────────────────────────────────────────
722
- function Overview({ timeRange, setRoute, setParams }) {
720
+ function Overview({ timeRange, setRoute, setParams, lastUpdated }) {
723
721
  const { ENDPOINTS, RELEASES, INSIGHTS, SUMMARY } = window.AF_DATA;
724
722
  const [globalTs, setGlobalTs] = useState(null);
725
723
  const hours = TIME_HOURS[timeRange] || 24;
@@ -728,7 +726,7 @@ function Overview({ timeRange, setRoute, setParams }) {
728
726
  setGlobalTs(null);
729
727
  fetch(`/api/global-timeseries?hours=${hours}`)
730
728
  .then(r => r.json()).then(d => setGlobalTs(d)).catch(() => setGlobalTs([]));
731
- }, [hours]);
729
+ }, [hours, lastUpdated]);
732
730
 
733
731
  const chartData = globalTs ? tsBucketsToChart(globalTs, hours) : null;
734
732
  const points = Math.max(chartData?.p90?.length || 0, 2);
@@ -1050,7 +1048,7 @@ function Endpoints({ setRoute, setParams }) {
1050
1048
  }
1051
1049
 
1052
1050
  // ─── Endpoint detail ──────────────────────────────────────────────────────────
1053
- function EndpointDetail({ id, timeRange, setRoute, setParams }) {
1051
+ function EndpointDetail({ id, timeRange, setRoute, setParams, lastUpdated }) {
1054
1052
  const { ENDPOINTS, INSIGHTS } = window.AF_DATA;
1055
1053
  const ep = ENDPOINTS.find(e => e.id === id) || ENDPOINTS[0];
1056
1054
  const [tab, setTab] = useState('performance');
@@ -1065,7 +1063,7 @@ function EndpointDetail({ id, timeRange, setRoute, setParams }) {
1065
1063
  setTs(null);
1066
1064
  fetch(`/api/timeseries?route=${encodeURIComponent(route)}&method=${encodeURIComponent(method)}&hours=${hours}`)
1067
1065
  .then(r => r.json()).then(d => setTs(d)).catch(() => setTs([]));
1068
- }, [id, hours]);
1066
+ }, [id, hours, lastUpdated]);
1069
1067
 
1070
1068
  if (!ep) return <div className="empty-state">Endpoint not found.</div>;
1071
1069
 
@@ -1628,9 +1626,9 @@ function App() {
1628
1626
  lastUpdated={lastUpdated} onRefresh={() => fetchData.current()}/>
1629
1627
  <div className="content">
1630
1628
  <div className="content-inner">
1631
- {route === 'overview' && <Overview timeRange={timeRange} setRoute={setRoute} setParams={setParams}/>}
1629
+ {route === 'overview' && <Overview timeRange={timeRange} setRoute={setRoute} setParams={setParams} lastUpdated={lastUpdated}/>}
1632
1630
  {route === 'endpoints' && <Endpoints setRoute={setRoute} setParams={setParams}/>}
1633
- {route === 'endpoint' && <EndpointDetail id={params.id} timeRange={timeRange} setRoute={setRoute} setParams={setParams}/>}
1631
+ {route === 'endpoint' && <EndpointDetail id={params.id} timeRange={timeRange} setRoute={setRoute} setParams={setParams} lastUpdated={lastUpdated}/>}
1634
1632
  {route === 'insights' && <Insights setRoute={setRoute} setParams={setParams}/>}
1635
1633
  {route === 'releases' && <Releases/>}
1636
1634
  {route === 'settings' && <Settings/>}