@olane/o-server 0.8.2 → 0.8.4

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 (2) hide show
  1. package/README.md +136 -37
  2. package/package.json +3 -3
package/README.md CHANGED
@@ -7,7 +7,7 @@ HTTP server entrypoint for Olane OS nodes. Exposes a node's `use` functionality
7
7
  ## Installation
8
8
 
9
9
  ```bash
10
- npm install @olane/o-server
10
+ pnpm install @olane/o-server
11
11
  ```
12
12
 
13
13
  ## Quick Start
@@ -219,11 +219,19 @@ const server = oServer({
219
219
  credentials: true
220
220
  },
221
221
 
222
- // Optional: Authentication middleware
223
- authenticate: async (req) => {
224
- const token = req.headers.authorization;
225
- return validateToken(token);
222
+ // Optional: JWT authentication (recommended)
223
+ jwtAuth: {
224
+ method: 'secret',
225
+ secret: 'your-jwt-secret',
226
+ issuer: 'https://auth.example.com',
227
+ audience: 'https://api.example.com'
226
228
  },
229
+
230
+ // DEPRECATED: Use jwtAuth instead. Will be removed in a future version.
231
+ // authenticate: async (req) => {
232
+ // const token = req.headers.authorization;
233
+ // return validateToken(token);
234
+ // },
227
235
 
228
236
  // Optional: Enable debug logging (default: false)
229
237
  debug: true
@@ -232,28 +240,71 @@ const server = oServer({
232
240
  await server.start();
233
241
  ```
234
242
 
235
- ### Authentication
243
+ ### JWT Authentication (Recommended)
244
+
245
+ Protect your endpoints with built-in JWT verification. All routes except `/health` require a valid token when `jwtAuth` is configured.
246
+
247
+ **RS256 with public key:**
248
+ ```typescript
249
+ const server = oServer({
250
+ node: myNode,
251
+ port: 3000,
252
+ jwtAuth: {
253
+ method: 'publicKey',
254
+ publicKeyPath: '/path/to/public-key.pem',
255
+ issuer: 'https://auth.example.com',
256
+ audience: 'https://api.example.com',
257
+ algorithms: ['RS256']
258
+ }
259
+ });
260
+ ```
261
+
262
+ **HS256 with shared secret:**
263
+ ```typescript
264
+ const server = oServer({
265
+ node: myNode,
266
+ port: 3000,
267
+ jwtAuth: {
268
+ method: 'secret',
269
+ secret: process.env.JWT_SECRET!,
270
+ clockTolerance: 5 // seconds of tolerance for exp/nbf
271
+ }
272
+ });
273
+ ```
274
+
275
+ **`jwtAuth` configuration options:**
276
+
277
+ | Option | Type | Required | Description |
278
+ |--------|------|----------|-------------|
279
+ | `method` | `'publicKey' \| 'secret'` | Yes | Verification method |
280
+ | `publicKeyPath` | `string` | If `method='publicKey'` | Path to PEM public key file |
281
+ | `secret` | `string` | If `method='secret'` | Shared secret for HS256 |
282
+ | `issuer` | `string` | No | Expected `iss` claim |
283
+ | `audience` | `string` | No | Expected `aud` claim |
284
+ | `algorithms` | `Algorithm[]` | No | Allowed algorithms (defaults based on method) |
285
+ | `clockTolerance` | `number` | No | Seconds of clock tolerance (default: 0) |
236
286
 
237
- Protect your endpoints with authentication:
287
+ ### Legacy Authentication (Deprecated)
288
+
289
+ > **Deprecated**: The `authenticate` option is deprecated and will be removed in a future version. Migrate to `jwtAuth` instead.
238
290
 
239
291
  ```typescript
292
+ // @deprecated - use jwtAuth instead
240
293
  const server = oServer({
241
294
  node: myNode,
242
295
  port: 3000,
296
+ // @deprecated - use jwtAuth instead
243
297
  authenticate: async (req) => {
244
- // Validate JWT token
245
298
  const token = req.headers.authorization?.split(' ')[1];
246
-
299
+
247
300
  if (!token) {
248
301
  throw new Error('No token provided');
249
302
  }
250
-
303
+
251
304
  const user = await verifyJWT(token);
252
305
  return { userId: user.id, roles: user.roles };
253
306
  }
254
307
  });
255
-
256
- // Now all requests require valid authentication
257
308
  ```
258
309
 
259
310
  ### CORS Configuration
@@ -323,6 +374,8 @@ await server.start();
323
374
 
324
375
  ```typescript
325
376
  // frontend/src/api.ts
377
+ // Note: HTTP responses from o-server use the flat { success, data, error } format.
378
+ // This is different from internal node.use() calls which return response.result.success.
326
379
  export async function analyzeRevenue(startDate: string, endDate: string) {
327
380
  const response = await fetch('http://localhost:3000/api/v1/use', {
328
381
  method: 'POST',
@@ -333,8 +386,13 @@ export async function analyzeRevenue(startDate: string, endDate: string) {
333
386
  params: { startDate, endDate }
334
387
  })
335
388
  });
336
-
389
+
337
390
  const result = await response.json();
391
+
392
+ if (!result.success) {
393
+ throw new Error(result.error?.message || 'Request failed');
394
+ }
395
+
338
396
  return result.data;
339
397
  }
340
398
  ```
@@ -349,11 +407,10 @@ import { oServer } from '@olane/o-server';
349
407
  const server = oServer({
350
408
  node: myNode,
351
409
  port: 8080,
352
- authenticate: async (req) => {
353
- // Validate Firebase auth token
354
- const token = req.headers.authorization?.split(' ')[1];
355
- const decodedToken = await admin.auth().verifyIdToken(token);
356
- return { userId: decodedToken.uid };
410
+ jwtAuth: {
411
+ method: 'secret',
412
+ secret: process.env.JWT_SECRET!,
413
+ issuer: 'https://your-firebase-project.firebaseapp.com'
357
414
  }
358
415
  });
359
416
 
@@ -373,17 +430,22 @@ const server = oServer({ node: myNode, port: 3000 });
373
430
  // Stripe webhook
374
431
  server.app.post('/webhooks/stripe', async (req, res) => {
375
432
  const event = req.body;
376
-
377
- // Process via your node
378
- await myNode.use(
433
+
434
+ // Process via your node (internal call uses response.result pattern)
435
+ const response = await myNode.use(
379
436
  new oAddress('o://payments/processor'),
380
437
  {
381
438
  method: 'handle_payment',
382
439
  params: { event }
383
440
  }
384
441
  );
385
-
386
- res.json({ received: true });
442
+
443
+ if (!response.result.success) {
444
+ res.status(500).json({ error: response.result.error });
445
+ return;
446
+ }
447
+
448
+ res.json({ received: true, data: response.result.data });
387
449
  });
388
450
 
389
451
  await server.start();
@@ -420,18 +482,20 @@ await server.start();
420
482
  // -d '{"address": "o://any/node/in/network", "method": "...", "params": {...}}'
421
483
  ```
422
484
 
423
- ## Error Handling
485
+ ## Response Structure
424
486
 
425
- `o-server` provides consistent error responses:
487
+ ### HTTP Response (from o-server)
426
488
 
427
- ```typescript
428
- // Success response
489
+ `o-server` flattens the internal node response into a simple HTTP-friendly format:
490
+
491
+ ```json
492
+ // Success response (HTTP)
429
493
  {
430
494
  "success": true,
431
495
  "data": { ... }
432
496
  }
433
497
 
434
- // Error response
498
+ // Error response (HTTP)
435
499
  {
436
500
  "success": false,
437
501
  "error": {
@@ -442,6 +506,38 @@ await server.start();
442
506
  }
443
507
  ```
444
508
 
509
+ ### Internal Node Response (from node.use())
510
+
511
+ Within the Olane node ecosystem, `node.use()` returns a JSON-RPC wrapped response. When calling nodes programmatically (not via HTTP), always access data through the `result` property:
512
+
513
+ ```typescript
514
+ const response = await node.use(address, { method: 'my_method', params: {} });
515
+
516
+ // Access the response correctly:
517
+ if (response.result.success) {
518
+ const data = response.result.data; // Your method's return value
519
+ } else {
520
+ const error = response.result.error; // Error message string
521
+ }
522
+
523
+ // Full internal response shape:
524
+ // {
525
+ // jsonrpc: "2.0",
526
+ // id: "request-id",
527
+ // result: {
528
+ // success: boolean,
529
+ // data: any, // Present on success
530
+ // error?: string // Present on failure
531
+ // }
532
+ // }
533
+ ```
534
+
535
+ > **Important**: The `o-server` translates the internal `response.result.success` / `response.result.data` / `response.result.error` structure into the flat HTTP `{ success, data }` / `{ success, error }` format. When writing code that runs inside the node ecosystem (not behind o-server), always use `response.result.success`, `response.result.data`, and `response.result.error`.
536
+
537
+ ## Error Handling
538
+
539
+ `o-server` provides consistent error responses:
540
+
445
541
  **Common error codes:**
446
542
  - `INVALID_PARAMS` - Missing or invalid request parameters
447
543
  - `NODE_NOT_FOUND` - Target node address not found
@@ -540,8 +636,9 @@ const config: ServerConfig = {
540
636
  cors: {
541
637
  origin: 'https://example.com'
542
638
  },
543
- authenticate: async (req) => {
544
- return { userId: '123' };
639
+ jwtAuth: {
640
+ method: 'secret',
641
+ secret: process.env.JWT_SECRET!,
545
642
  },
546
643
  debug: true
547
644
  };
@@ -568,13 +665,15 @@ LOG_LEVEL=info
568
665
  # Dockerfile
569
666
  FROM node:20-alpine
570
667
 
668
+ RUN corepack enable && corepack prepare pnpm@latest --activate
669
+
571
670
  WORKDIR /app
572
671
 
573
- COPY package*.json ./
574
- RUN npm ci --only=production
672
+ COPY package.json pnpm-lock.yaml ./
673
+ RUN pnpm install --frozen-lockfile --prod
575
674
 
576
675
  COPY . .
577
- RUN npm run build
676
+ RUN pnpm run build
578
677
 
579
678
  EXPOSE 8080
580
679
 
@@ -686,13 +785,13 @@ Complete examples:
686
785
  const server = oServer({ node: myNode, port: 3000 });
687
786
  await server.start();
688
787
 
689
- // With authentication
788
+ // With JWT authentication
690
789
  const server = oServer({
691
790
  node: myNode,
692
791
  port: 3000,
693
- authenticate: async (req) => {
694
- const token = req.headers.authorization;
695
- return await validateJWT(token);
792
+ jwtAuth: {
793
+ method: 'secret',
794
+ secret: process.env.JWT_SECRET!,
696
795
  }
697
796
  });
698
797
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@olane/o-server",
3
- "version": "0.8.2",
3
+ "version": "0.8.4",
4
4
  "type": "module",
5
5
  "main": "dist/src/index.js",
6
6
  "types": "dist/src/index.d.ts",
@@ -61,7 +61,7 @@
61
61
  "typescript": "5.4.5"
62
62
  },
63
63
  "dependencies": {
64
- "@olane/o-core": "0.8.2",
64
+ "@olane/o-core": "0.8.4",
65
65
  "cors": "^2.8.5",
66
66
  "debug": "^4.4.1",
67
67
  "dotenv": "^16.5.0",
@@ -69,5 +69,5 @@
69
69
  "jsonwebtoken": "^9.0.3",
70
70
  "zod": "^3.25.76"
71
71
  },
72
- "gitHead": "9e35c874d849d051bcffe483fd2a8c2b3ecf68cc"
72
+ "gitHead": "b53623b1ad4365133911722f80d5597a72b65bf2"
73
73
  }