@uvrn/api 1.0.1 → 1.0.3

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.
package/README.md CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  UVRN REST API — HTTP server for Delta Engine bundle processing. Exposes run, validate, and verify over HTTP so any client (browser, script, or service) can call the engine without installing the core or SDK.
4
4
 
5
+ **Disclaimer:** UVRN is in Alpha testing. The engine measures whether your sources agree with each other — not whether they’re correct. Final trust of output rests with the user. Use at your own risk. Have fun.
6
+
5
7
  ## Install
6
8
 
7
9
  ```bash
@@ -27,16 +29,20 @@ Or from your app:
27
29
  ```typescript
28
30
  import { startServer, createServer } from '@uvrn/api';
29
31
 
30
- const server = await createServer();
31
- await startServer(server);
32
+ // Default: start with default config (port 3000)
33
+ await startServer();
34
+
35
+ // Or create server with custom config, then listen
36
+ const server = await createServer({ port: 4000 });
37
+ await server.listen({ port: 4000, host: '0.0.0.0' });
32
38
  ```
33
39
 
34
- 2. **Send a bundle** (e.g. POST to `/delta/run`) and get a receipt in the response. Use `/delta/validate` and `/delta/verify` for validation and verification.
40
+ 2. **Send a bundle** (e.g. POST to `/api/v1/delta/run`) and get a receipt in the response. Use `/api/v1/delta/validate` and `/api/v1/delta/verify` for validation and verification.
35
41
 
36
42
  Example with curl:
37
43
 
38
44
  ```bash
39
- curl -X POST http://localhost:3000/delta/run \
45
+ curl -X POST http://localhost:3000/api/v1/delta/run \
40
46
  -H "Content-Type: application/json" \
41
47
  -d '{"bundleId":"example-001","claim":"Compare sources","thresholdPct":0.1,"dataSpecs":[...]}'
42
48
  ```
@@ -49,6 +55,8 @@ curl -X POST http://localhost:3000/delta/run \
49
55
 
50
56
  ## Links
51
57
 
58
+ **Open source:** Source code and issues: [GitHub (uvrn-packages)](https://github.com/UVRN-org/uvrn-packages). Project landing: [UVRN](https://github.com/UVRN-org/uvrn).
59
+
52
60
  - [Repository](https://github.com/UVRN-org/uvrn-packages) — monorepo (this package: `uvrn-api`)
53
61
  - [@uvrn/core](https://www.npmjs.com/package/@uvrn/core) — Delta Engine core used by this server
54
62
  - [@uvrn/cli](https://www.npmjs.com/package/@uvrn/cli) — run the engine from the command line instead of HTTP
package/dist/server.js CHANGED
File without changes
package/package.json CHANGED
@@ -1,9 +1,15 @@
1
1
  {
2
2
  "name": "@uvrn/api",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
+ "publishConfig": {
5
+ "access": "public"
6
+ },
4
7
  "description": "UVRN REST API — HTTP access to bundle processing",
5
8
  "main": "dist/index.js",
6
9
  "types": "dist/index.d.ts",
10
+ "bin": {
11
+ "uvrn-api": "dist/server.js"
12
+ },
7
13
  "keywords": [
8
14
  "uvrn",
9
15
  "rest-api",
@@ -18,11 +24,11 @@
18
24
  },
19
25
  "homepage": "https://github.com/UVRN-org/uvrn-packages#readme",
20
26
  "dependencies": {
21
- "fastify": "^4.25.0",
22
- "@fastify/cors": "^8.5.0",
23
- "@fastify/rate-limit": "^9.1.0",
24
- "@fastify/helmet": "^11.1.0",
25
- "@uvrn/core": "1.0.1"
27
+ "fastify": "^5.7.3",
28
+ "@fastify/cors": "^10.0.0",
29
+ "@fastify/rate-limit": "^10.0.0",
30
+ "@fastify/helmet": "^12.0.0",
31
+ "@uvrn/core": "^1.0.3"
26
32
  },
27
33
  "devDependencies": {
28
34
  "@types/node": "^20.0.0",
@@ -37,8 +43,12 @@
37
43
  "typescript": "^5.3.0"
38
44
  },
39
45
  "engines": {
40
- "node": ">=18.0.0"
46
+ "node": ">=20.0.0"
41
47
  },
48
+ "files": [
49
+ "dist",
50
+ "README.md"
51
+ ],
42
52
  "scripts": {
43
53
  "dev": "tsx watch src/server.ts",
44
54
  "build": "tsc",
package/TESTING_GUIDE.md DELETED
@@ -1,495 +0,0 @@
1
- # Delta Engine API - Testing Guide
2
-
3
- ## Quick Start
4
-
5
- ### 1. Start the Development Server
6
-
7
- ```bash
8
- cd packages/uvrn-api
9
- npm run dev
10
- ```
11
-
12
- **What to look for:**
13
- - ✅ Server starts without errors
14
- - ✅ You see log messages like:
15
- ```
16
- 🚀 Delta Engine API server running at http://0.0.0.0:3000
17
- 📊 Health check: http://0.0.0.0:3000/api/v1/health
18
- 📦 Environment: development
19
- 🔒 Rate limit: 100 requests per 1 minute
20
- ```
21
-
22
- ### 2. Run Automated Tests
23
-
24
- From the repository root:
25
-
26
- ```bash
27
- ./test-api.sh
28
- ```
29
-
30
- This will test all 5 endpoints plus error scenarios.
31
-
32
- ---
33
-
34
- ## Manual Testing
35
-
36
- ### Test 1: Health Check ✅
37
-
38
- **Purpose:** Verify the server is running and the engine is available
39
-
40
- ```bash
41
- curl http://localhost:3000/api/v1/health
42
- ```
43
-
44
- **Expected Response (200 OK):**
45
- ```json
46
- {
47
- "status": "healthy",
48
- "uptime": 12345,
49
- "version": "1.0.0",
50
- "engine": {
51
- "available": true
52
- },
53
- "timestamp": "2026-01-15T14:55:00.000Z"
54
- }
55
- ```
56
-
57
- **What to look for:**
58
- - ✅ `status: "healthy"`
59
- - ✅ `engine.available: true`
60
- - ✅ HTTP status code `200`
61
-
62
- ---
63
-
64
- ### Test 2: Version Information ✅
65
-
66
- **Purpose:** Check API, engine, and protocol versions
67
-
68
- ```bash
69
- curl http://localhost:3000/api/v1/version
70
- ```
71
-
72
- **Expected Response (200 OK):**
73
- ```json
74
- {
75
- "apiVersion": "1.0.0",
76
- "engineVersion": "1.0.0",
77
- "protocolVersion": "1.0"
78
- }
79
- ```
80
-
81
- **What to look for:**
82
- - ✅ All version fields present
83
- - ✅ HTTP status code `200`
84
-
85
- ---
86
-
87
- ### Test 3: Validate Bundle Schema ✅
88
-
89
- **Purpose:** Validate a bundle without executing it
90
-
91
- ```bash
92
- curl -X POST http://localhost:3000/api/v1/delta/validate \
93
- -H "Content-Type: application/json" \
94
- -d '{
95
- "bundleId": "test-bundle-001",
96
- "claim": "Revenue reconciliation test",
97
- "dataSpecs": [
98
- {
99
- "id": "spec-1",
100
- "label": "Q4 Revenue Report",
101
- "sourceKind": "report",
102
- "originDocIds": ["doc-123"],
103
- "metrics": [
104
- {"key": "revenue", "value": 100000, "unit": "USD"}
105
- ]
106
- }
107
- ],
108
- "thresholdPct": 0.1
109
- }'
110
- ```
111
-
112
- **Expected Response (200 OK):**
113
- ```json
114
- {
115
- "valid": true
116
- }
117
- ```
118
-
119
- **What to look for:**
120
- - ✅ `valid: true` for correct bundles
121
- - ✅ HTTP status code `200`
122
-
123
- ---
124
-
125
- ### Test 4: Run Engine on Bundle 🚀
126
-
127
- **Purpose:** Execute the delta engine and get a receipt
128
-
129
- ```bash
130
- curl -X POST http://localhost:3000/api/v1/delta/run \
131
- -H "Content-Type: application/json" \
132
- -d '{
133
- "bundleId": "revenue-q4-2025",
134
- "claim": "Q4 2025 revenue: $500K",
135
- "dataSpecs": [
136
- {
137
- "id": "spec-1",
138
- "label": "Accounting System",
139
- "sourceKind": "report",
140
- "originDocIds": ["acc-report-q4"],
141
- "metrics": [
142
- {"key": "revenue", "value": 500000, "unit": "USD"}
143
- ]
144
- },
145
- {
146
- "id": "spec-2",
147
- "label": "Sales Database",
148
- "sourceKind": "report",
149
- "originDocIds": ["sales-db-q4"],
150
- "metrics": [
151
- {"key": "revenue", "value": 502000, "unit": "USD"}
152
- ]
153
- }
154
- ],
155
- "thresholdPct": 0.05
156
- }'
157
- ```
158
-
159
- **Expected Response (200 OK):**
160
- ```json
161
- {
162
- "bundleId": "revenue-q4-2025",
163
- "deltaFinal": 0.004,
164
- "sources": ["Accounting System", "Sales Database"],
165
- "rounds": [
166
- {
167
- "round": 1,
168
- "deltasByMetric": {"revenue": 0.004},
169
- "withinThreshold": true,
170
- "witnessRequired": false
171
- }
172
- ],
173
- "suggestedFixes": [],
174
- "outcome": "consensus",
175
- "hash": "abc123..."
176
- }
177
- ```
178
-
179
- **What to look for:**
180
- - ✅ Receipt contains all fields: `bundleId`, `deltaFinal`, `sources`, `rounds`, `outcome`, `hash`
181
- - ✅ `outcome` is either `"consensus"` or `"indeterminate"`
182
- - ✅ `hash` is a SHA-256 hash string
183
- - ✅ HTTP status code `200`
184
-
185
- ---
186
-
187
- ### Test 5: Verify Receipt 🔐
188
-
189
- **Purpose:** Verify receipt integrity by recomputing the hash
190
-
191
- ```bash
192
- curl -X POST http://localhost:3000/api/v1/delta/verify \
193
- -H "Content-Type: application/json" \
194
- -d '{
195
- "bundleId": "test-001",
196
- "deltaFinal": 0.02,
197
- "sources": ["Source A", "Source B"],
198
- "rounds": [
199
- {
200
- "round": 1,
201
- "deltasByMetric": {"metric1": 0.02},
202
- "withinThreshold": true,
203
- "witnessRequired": false
204
- }
205
- ],
206
- "suggestedFixes": [],
207
- "outcome": "consensus",
208
- "hash": "abc123def456..."
209
- }'
210
- ```
211
-
212
- **Expected Response (200 OK):**
213
- ```json
214
- {
215
- "verified": true,
216
- "recomputedHash": "abc123def456..."
217
- }
218
- ```
219
-
220
- **What to look for:**
221
- - ✅ `verified: true` if hash matches
222
- - ✅ `verified: false` if hash doesn't match
223
- - ✅ `recomputedHash` is provided
224
- - ✅ HTTP status code `200`
225
-
226
- ---
227
-
228
- ## Error Scenario Testing
229
-
230
- ### Test 6: Invalid Bundle Schema ❌
231
-
232
- ```bash
233
- curl -X POST http://localhost:3000/api/v1/delta/validate \
234
- -H "Content-Type: application/json" \
235
- -d '{
236
- "bundleId": "incomplete",
237
- "claim": "Missing dataSpecs field"
238
- }'
239
- ```
240
-
241
- **Expected Response (200 OK with validation errors):**
242
- ```json
243
- {
244
- "valid": false,
245
- "errors": [
246
- {
247
- "field": "bundle",
248
- "message": "Missing required field: dataSpecs"
249
- }
250
- ]
251
- }
252
- ```
253
-
254
- **What to look for:**
255
- - ✅ `valid: false`
256
- - ✅ `errors` array with details
257
- - ✅ HTTP status code `200` (validation succeeded, bundle failed)
258
-
259
- ---
260
-
261
- ### Test 7: Wrong Content-Type ❌
262
-
263
- ```bash
264
- curl -X POST http://localhost:3000/api/v1/delta/run \
265
- -H "Content-Type: text/plain" \
266
- -d 'not json'
267
- ```
268
-
269
- **Expected Response (415 Unsupported Media Type):**
270
- ```json
271
- {
272
- "error": {
273
- "code": "UNSUPPORTED_MEDIA_TYPE",
274
- "message": "Content-Type must be application/json",
275
- "details": {
276
- "receivedContentType": "text/plain"
277
- }
278
- }
279
- }
280
- ```
281
-
282
- **What to look for:**
283
- - ✅ HTTP status code `415`
284
- - ✅ Error message explains the issue
285
-
286
- ---
287
-
288
- ### Test 8: Malformed JSON ❌
289
-
290
- ```bash
291
- curl -X POST http://localhost:3000/api/v1/delta/run \
292
- -H "Content-Type: application/json" \
293
- -d '{invalid json'
294
- ```
295
-
296
- **Expected Response (400 Bad Request):**
297
- ```json
298
- {
299
- "error": {
300
- "code": "BAD_REQUEST",
301
- "message": "Invalid JSON",
302
- "details": {...}
303
- }
304
- }
305
- ```
306
-
307
- **What to look for:**
308
- - ✅ HTTP status code `400`
309
- - ✅ Clear error message
310
-
311
- ---
312
-
313
- ### Test 9: Rate Limiting 🚦
314
-
315
- ```bash
316
- # Run this in a loop to exceed rate limit
317
- for i in {1..105}; do
318
- curl -s http://localhost:3000/api/v1/health > /dev/null
319
- done
320
- curl http://localhost:3000/api/v1/health
321
- ```
322
-
323
- **Expected Response (429 Too Many Requests):**
324
- ```json
325
- {
326
- "error": {
327
- "code": "RATE_LIMIT_EXCEEDED",
328
- "message": "Too many requests, please try again later",
329
- "details": {
330
- "rateLimitMax": 100,
331
- "timeWindow": "1 minute"
332
- }
333
- }
334
- }
335
- ```
336
-
337
- **What to look for:**
338
- - ✅ HTTP status code `429`
339
- - ✅ Rate limit details in response
340
-
341
- ---
342
-
343
- ### Test 10: 404 Not Found ❌
344
-
345
- ```bash
346
- curl http://localhost:3000/api/v1/nonexistent
347
- ```
348
-
349
- **Expected Response (404 Not Found):**
350
- ```json
351
- {
352
- "error": {
353
- "code": "NOT_FOUND",
354
- "message": "Route GET /api/v1/nonexistent not found",
355
- "details": {
356
- "method": "GET",
357
- "url": "/api/v1/nonexistent"
358
- }
359
- }
360
- }
361
- ```
362
-
363
- **What to look for:**
364
- - ✅ HTTP status code `404`
365
- - ✅ Clear error message with URL
366
-
367
- ---
368
-
369
- ## Testing with Real Bundles
370
-
371
- You can test with existing demo bundles from the CLI:
372
-
373
- ```bash
374
- # Find demo bundles
375
- ls ../../examples/*.json
376
-
377
- # Test with a real bundle
378
- curl -X POST http://localhost:3000/api/v1/delta/run \
379
- -H "Content-Type: application/json" \
380
- -d @../../examples/revenue-bundle.json
381
- ```
382
-
383
- ---
384
-
385
- ## Server Logs
386
-
387
- When running in development mode (`npm run dev`), you'll see detailed logs:
388
-
389
- **Good logs to look for:**
390
- ```
391
- [INFO] Incoming request - GET /api/v1/health
392
- [INFO] Request completed - Status: 200, Time: 5ms
393
- ```
394
-
395
- **Error logs:**
396
- ```
397
- [ERROR] Engine execution failed - Invalid bundle structure
398
- [WARN] Rate limit exceeded for IP 127.0.0.1
399
- ```
400
-
401
- ---
402
-
403
- ## Checklist: Server is Working ✅
404
-
405
- After running all tests, verify:
406
-
407
- - [ ] Health endpoint returns `healthy` status
408
- - [ ] Version endpoint returns correct versions
409
- - [ ] Validate endpoint accepts valid bundles
410
- - [ ] Validate endpoint rejects invalid bundles
411
- - [ ] Run endpoint executes engine and returns receipt
412
- - [ ] Verify endpoint checks receipt integrity
413
- - [ ] Rate limiting kicks in after 100 requests
414
- - [ ] Wrong content-type returns 415 error
415
- - [ ] Malformed JSON returns 400 error
416
- - [ ] Non-existent routes return 404 error
417
- - [ ] All responses have correct error structure
418
- - [ ] Server logs requests and responses
419
- - [ ] No crashes or unhandled exceptions
420
-
421
- ---
422
-
423
- ## Performance Testing
424
-
425
- To test performance and concurrent requests:
426
-
427
- ```bash
428
- # Install autocannon if not already installed
429
- npm install -g autocannon
430
-
431
- # Run load test (100 requests, 10 concurrent)
432
- autocannon -c 10 -d 10 http://localhost:3000/api/v1/health
433
- ```
434
-
435
- **What to look for:**
436
- - ✅ Response times < 100ms for health endpoint
437
- - ✅ No 5xx errors under load
438
- - ✅ Rate limiting works correctly
439
-
440
- ---
441
-
442
- ## Troubleshooting
443
-
444
- ### Server won't start
445
-
446
- 1. Check if port 3000 is already in use:
447
- ```bash
448
- lsof -i :3000
449
- ```
450
-
451
- 2. Use a different port:
452
- ```bash
453
- PORT=3001 npm run dev
454
- ```
455
-
456
- ### Engine not available
457
-
458
- If health check shows `engine.available: false`:
459
-
460
- 1. Ensure `@uvrn/core` is installed:
461
- ```bash
462
- npm list @uvrn/core
463
- ```
464
-
465
- 2. Build the engine package:
466
- ```bash
467
- cd ../uvrn-core && npm run build
468
- ```
469
-
470
- ### CORS errors (in browser)
471
-
472
- If testing from a browser and seeing CORS errors:
473
-
474
- 1. Set allowed origins in `.env`:
475
- ```
476
- CORS_ORIGINS=http://localhost:3000,http://localhost:8080
477
- ```
478
-
479
- 2. Restart the server
480
-
481
- ---
482
-
483
- ## Next Steps
484
-
485
- Once all tests pass:
486
- 1. ✅ Proceed to Task A.2.3 (OpenAPI Specification)
487
- 2. ✅ Add integration tests with Jest
488
- 3. ✅ Deploy to staging environment
489
- 4. ✅ Run load tests with realistic traffic
490
-
491
- ---
492
-
493
- **Created:** 2026-01-15
494
- **API Version:** 1.0.0
495
- **Last Updated:** 2026-01-15
package/jest.config.js DELETED
@@ -1,13 +0,0 @@
1
- module.exports = {
2
- preset: 'ts-jest',
3
- testEnvironment: 'node',
4
- roots: ['<rootDir>/tests'],
5
- testMatch: ['**/*.test.ts'],
6
- collectCoverageFrom: [
7
- 'src/**/*.ts',
8
- '!src/**/*.d.ts',
9
- '!src/index.ts',
10
- ],
11
- coverageDirectory: 'coverage',
12
- coverageReporters: ['text', 'lcov', 'html'],
13
- };
@@ -1,60 +0,0 @@
1
- /**
2
- * Configuration Loader
3
- * Loads and validates server configuration from environment variables
4
- */
5
-
6
- import { ServerConfig, ConfigValidationError } from './types';
7
-
8
- /**
9
- * Load configuration from environment variables with defaults
10
- */
11
- export function loadConfig(): ServerConfig {
12
- const config: ServerConfig = {
13
- port: parseInt(process.env.PORT || '3000', 10),
14
- host: process.env.HOST || '0.0.0.0',
15
- corsOrigins: process.env.CORS_ORIGINS?.split(',').map(o => o.trim()) || ['*'],
16
- rateLimitMax: parseInt(process.env.RATE_LIMIT_MAX || '100', 10),
17
- rateLimitTimeWindow: process.env.RATE_LIMIT_TIME_WINDOW || '1 minute',
18
- logLevel: (process.env.LOG_LEVEL as ServerConfig['logLevel']) || 'info',
19
- nodeEnv: (process.env.NODE_ENV as ServerConfig['nodeEnv']) || 'development'
20
- };
21
-
22
- const errors = validateConfig(config);
23
- if (errors.length > 0) {
24
- const errorMessages = errors.map(e => `${e.field}: ${e.message}`).join(', ');
25
- throw new Error(`Configuration validation failed: ${errorMessages}`);
26
- }
27
-
28
- return config;
29
- }
30
-
31
- /**
32
- * Validate configuration values
33
- */
34
- function validateConfig(config: ServerConfig): ConfigValidationError[] {
35
- const errors: ConfigValidationError[] = [];
36
-
37
- if (config.port < 1 || config.port > 65535) {
38
- errors.push({ field: 'port', message: 'Must be between 1 and 65535' });
39
- }
40
-
41
- if (!config.host) {
42
- errors.push({ field: 'host', message: 'Host cannot be empty' });
43
- }
44
-
45
- if (config.rateLimitMax < 1) {
46
- errors.push({ field: 'rateLimitMax', message: 'Must be at least 1' });
47
- }
48
-
49
- const validLogLevels = ['trace', 'debug', 'info', 'warn', 'error', 'fatal'];
50
- if (!validLogLevels.includes(config.logLevel)) {
51
- errors.push({ field: 'logLevel', message: `Must be one of: ${validLogLevels.join(', ')}` });
52
- }
53
-
54
- const validNodeEnvs = ['development', 'production', 'test'];
55
- if (!validNodeEnvs.includes(config.nodeEnv)) {
56
- errors.push({ field: 'nodeEnv', message: `Must be one of: ${validNodeEnvs.join(', ')}` });
57
- }
58
-
59
- return errors;
60
- }
@@ -1,18 +0,0 @@
1
- /**
2
- * Server Configuration Types
3
- */
4
-
5
- export interface ServerConfig {
6
- port: number;
7
- host: string;
8
- corsOrigins: string[];
9
- rateLimitMax: number;
10
- rateLimitTimeWindow: string;
11
- logLevel: 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'fatal';
12
- nodeEnv: 'development' | 'production' | 'test';
13
- }
14
-
15
- export interface ConfigValidationError {
16
- field: string;
17
- message: string;
18
- }
package/src/index.ts DELETED
@@ -1,13 +0,0 @@
1
- /**
2
- * Delta Engine API - Entry Point
3
- * Exports for programmatic usage
4
- */
5
-
6
- export { startServer, createServer } from './server';
7
- export type { ServerConfig } from './config/types';
8
- export type {
9
- ErrorResponse,
10
- HealthResponse,
11
- VersionResponse,
12
- ValidationResponse
13
- } from './types/api';
@@ -1,69 +0,0 @@
1
- /**
2
- * Global Error Handler
3
- * Handles uncaught errors and formats error responses
4
- */
5
-
6
- import { FastifyInstance, FastifyError, FastifyRequest, FastifyReply } from 'fastify';
7
- import { ErrorResponse } from '../types/api';
8
-
9
- /**
10
- * Register global error handler
11
- */
12
- export function registerErrorHandler(server: FastifyInstance): void {
13
- server.setErrorHandler((error: FastifyError, request: FastifyRequest, reply: FastifyReply) => {
14
- // Log the error
15
- request.log.error({
16
- err: error,
17
- url: request.url,
18
- method: request.method
19
- }, 'Request error');
20
-
21
- // Determine status code
22
- const statusCode = error.statusCode || 500;
23
-
24
- // Map error to code
25
- let errorCode = 'INTERNAL_ERROR';
26
- if (statusCode === 400) errorCode = 'BAD_REQUEST';
27
- else if (statusCode === 404) errorCode = 'NOT_FOUND';
28
- else if (statusCode === 415) errorCode = 'UNSUPPORTED_MEDIA_TYPE';
29
- else if (statusCode === 429) errorCode = 'RATE_LIMIT_EXCEEDED';
30
- else if (statusCode === 503) errorCode = 'SERVICE_UNAVAILABLE';
31
-
32
- // Build error response
33
- const errorResponse: ErrorResponse = {
34
- error: {
35
- code: errorCode,
36
- message: error.message || 'An unexpected error occurred',
37
- details: {
38
- statusCode
39
- }
40
- }
41
- };
42
-
43
- // Don't expose internal error details in production
44
- if (process.env.NODE_ENV !== 'production' && error.stack) {
45
- errorResponse.error.details = {
46
- ...errorResponse.error.details,
47
- stack: error.stack
48
- };
49
- }
50
-
51
- reply.code(statusCode).send(errorResponse);
52
- });
53
-
54
- // Handle 404 errors
55
- server.setNotFoundHandler((request: FastifyRequest, reply: FastifyReply) => {
56
- const errorResponse: ErrorResponse = {
57
- error: {
58
- code: 'NOT_FOUND',
59
- message: `Route ${request.method} ${request.url} not found`,
60
- details: {
61
- method: request.method,
62
- url: request.url
63
- }
64
- }
65
- };
66
-
67
- reply.code(404).send(errorResponse);
68
- });
69
- }
@@ -1,120 +0,0 @@
1
- /**
2
- * Delta Engine API Routes
3
- * Endpoints for bundle processing, validation, and verification
4
- */
5
-
6
- import { FastifyInstance, FastifyRequest, FastifyReply } from 'fastify';
7
- import {
8
- DeltaBundle,
9
- DeltaReceipt,
10
- runDeltaEngine,
11
- validateBundle,
12
- verifyReceipt
13
- } from '@uvrn/core';
14
- import { ErrorResponse, ValidationResponse } from '../types/api';
15
-
16
- /**
17
- * Register delta engine routes
18
- */
19
- export async function registerDeltaRoutes(server: FastifyInstance): Promise<void> {
20
- // POST /api/v1/delta/run - Execute engine on bundle
21
- server.post<{
22
- Body: DeltaBundle;
23
- Reply: DeltaReceipt | ErrorResponse;
24
- }>('/api/v1/delta/run', async (request: FastifyRequest<{ Body: DeltaBundle }>, reply: FastifyReply) => {
25
- try {
26
- const bundle = request.body;
27
-
28
- // Validate bundle schema
29
- const validation = validateBundle(bundle);
30
- if (!validation.valid) {
31
- return reply.code(400).send({
32
- error: {
33
- code: 'INVALID_BUNDLE',
34
- message: 'Bundle validation failed',
35
- details: { validationError: validation.error }
36
- }
37
- });
38
- }
39
-
40
- // Execute engine
41
- const receipt = runDeltaEngine(bundle);
42
-
43
- return reply.code(200).send(receipt);
44
- } catch (error) {
45
- const errorMessage = error instanceof Error ? error.message : 'Unknown error';
46
- request.log.error({ error: errorMessage }, 'Engine execution failed');
47
-
48
- return reply.code(500).send({
49
- error: {
50
- code: 'ENGINE_ERROR',
51
- message: 'Failed to process bundle',
52
- details: { error: errorMessage }
53
- }
54
- });
55
- }
56
- });
57
-
58
- // POST /api/v1/delta/validate - Validate bundle schema
59
- server.post<{
60
- Body: DeltaBundle;
61
- Reply: ValidationResponse | ErrorResponse;
62
- }>('/api/v1/delta/validate', async (request: FastifyRequest<{ Body: DeltaBundle }>, reply: FastifyReply) => {
63
- try {
64
- const bundle = request.body;
65
- const validation = validateBundle(bundle);
66
-
67
- if (!validation.valid) {
68
- return reply.code(200).send({
69
- valid: false,
70
- errors: [
71
- {
72
- field: 'bundle',
73
- message: validation.error || 'Validation failed'
74
- }
75
- ]
76
- });
77
- }
78
-
79
- return reply.code(200).send({ valid: true });
80
- } catch (error) {
81
- const errorMessage = error instanceof Error ? error.message : 'Unknown error';
82
- request.log.error({ error: errorMessage }, 'Validation failed');
83
-
84
- return reply.code(500).send({
85
- error: {
86
- code: 'VALIDATION_ERROR',
87
- message: 'Failed to validate bundle',
88
- details: { error: errorMessage }
89
- }
90
- });
91
- }
92
- });
93
-
94
- // POST /api/v1/delta/verify - Verify receipt replay
95
- server.post<{
96
- Body: DeltaReceipt;
97
- Reply: { verified: boolean; recomputedHash?: string } | ErrorResponse;
98
- }>('/api/v1/delta/verify', async (request: FastifyRequest<{ Body: DeltaReceipt }>, reply: FastifyReply) => {
99
- try {
100
- const receipt = request.body;
101
- const verifyResult = verifyReceipt(receipt);
102
-
103
- return reply.code(200).send({
104
- verified: verifyResult.verified,
105
- recomputedHash: verifyResult.recomputedHash
106
- });
107
- } catch (error) {
108
- const errorMessage = error instanceof Error ? error.message : 'Unknown error';
109
- request.log.error({ error: errorMessage }, 'Verification failed');
110
-
111
- return reply.code(500).send({
112
- error: {
113
- code: 'VERIFICATION_ERROR',
114
- message: 'Failed to verify receipt',
115
- details: { error: errorMessage }
116
- }
117
- });
118
- }
119
- });
120
- }
@@ -1,65 +0,0 @@
1
- /**
2
- * Health and Version Routes
3
- * System status and version information endpoints
4
- */
5
-
6
- import { FastifyInstance, FastifyRequest, FastifyReply } from 'fastify';
7
- import { HealthResponse, VersionResponse } from '../types/api';
8
-
9
- const SERVER_START_TIME = Date.now();
10
- const API_VERSION = '1.0.0';
11
- const PROTOCOL_VERSION = '1.0';
12
-
13
- /**
14
- * Register health and version routes
15
- */
16
- export async function registerHealthRoutes(server: FastifyInstance): Promise<void> {
17
- // GET /api/v1/health - Health check endpoint
18
- server.get<{
19
- Reply: HealthResponse;
20
- }>('/api/v1/health', async (_request: FastifyRequest, reply: FastifyReply) => {
21
- const uptime = Date.now() - SERVER_START_TIME;
22
-
23
- // Check if engine is available by attempting to import it
24
- let engineAvailable = true;
25
- try {
26
- require('@uvrn/core');
27
- } catch {
28
- engineAvailable = false;
29
- }
30
-
31
- const health: HealthResponse = {
32
- status: engineAvailable ? 'healthy' : 'unhealthy',
33
- uptime,
34
- version: API_VERSION,
35
- engine: {
36
- available: engineAvailable
37
- },
38
- timestamp: new Date().toISOString()
39
- };
40
-
41
- const statusCode = engineAvailable ? 200 : 503;
42
- return reply.code(statusCode).send(health);
43
- });
44
-
45
- // GET /api/v1/version - Version information
46
- server.get<{
47
- Reply: VersionResponse;
48
- }>('/api/v1/version', async (_request: FastifyRequest, reply: FastifyReply) => {
49
- let engineVersion = 'unknown';
50
- try {
51
- const enginePkg = require('@uvrn/core/package.json');
52
- engineVersion = enginePkg.version;
53
- } catch {
54
- // Engine version not available
55
- }
56
-
57
- const version: VersionResponse = {
58
- apiVersion: API_VERSION,
59
- engineVersion,
60
- protocolVersion: PROTOCOL_VERSION
61
- };
62
-
63
- return reply.code(200).send(version);
64
- });
65
- }
package/src/server.ts DELETED
@@ -1,139 +0,0 @@
1
- /**
2
- * Delta Engine API Server
3
- * Fastify-based REST API for bundle processing
4
- */
5
-
6
- import Fastify, { FastifyInstance } from 'fastify';
7
- import cors from '@fastify/cors';
8
- import helmet from '@fastify/helmet';
9
- import rateLimit from '@fastify/rate-limit';
10
- import { loadConfig } from './config/loader';
11
- import { ServerConfig } from './config/types';
12
- import { registerDeltaRoutes } from './routes/delta';
13
- import { registerHealthRoutes } from './routes/health';
14
- import { registerErrorHandler } from './middleware/errorHandler';
15
-
16
- /**
17
- * Create and configure Fastify server instance
18
- */
19
- export async function createServer(config?: ServerConfig): Promise<FastifyInstance> {
20
- // Load configuration
21
- const serverConfig = config || loadConfig();
22
-
23
- // Create Fastify instance with logging
24
- const server = Fastify({
25
- logger: {
26
- level: serverConfig.logLevel,
27
- transport: serverConfig.nodeEnv === 'development' ? {
28
- target: 'pino-pretty',
29
- options: {
30
- translateTime: 'HH:MM:ss Z',
31
- ignore: 'pid,hostname'
32
- }
33
- } : undefined
34
- }
35
- });
36
-
37
- // Register plugins
38
- await server.register(helmet, {
39
- contentSecurityPolicy: serverConfig.nodeEnv === 'production' ? undefined : false
40
- });
41
-
42
- await server.register(cors, {
43
- origin: serverConfig.corsOrigins,
44
- credentials: true
45
- });
46
-
47
- await server.register(rateLimit, {
48
- max: serverConfig.rateLimitMax,
49
- timeWindow: serverConfig.rateLimitTimeWindow,
50
- errorResponseBuilder: () => ({
51
- error: {
52
- code: 'RATE_LIMIT_EXCEEDED',
53
- message: 'Too many requests, please try again later',
54
- details: {
55
- rateLimitMax: serverConfig.rateLimitMax,
56
- timeWindow: serverConfig.rateLimitTimeWindow
57
- }
58
- }
59
- })
60
- });
61
-
62
- // Add request logging hook
63
- server.addHook('onRequest', async (request, _reply) => {
64
- request.log.info({
65
- url: request.url,
66
- method: request.method,
67
- ip: request.ip
68
- }, 'Incoming request');
69
- });
70
-
71
- server.addHook('onResponse', async (request, reply) => {
72
- request.log.info({
73
- url: request.url,
74
- method: request.method,
75
- statusCode: reply.statusCode,
76
- responseTime: reply.elapsedTime
77
- }, 'Request completed');
78
- });
79
-
80
- // Validate content-type for POST requests
81
- server.addHook('preHandler', async (request, reply) => {
82
- if (request.method === 'POST' && request.url.startsWith('/api/v1/delta/')) {
83
- const contentType = request.headers['content-type'];
84
- if (!contentType || !contentType.includes('application/json')) {
85
- return reply.code(415).send({
86
- error: {
87
- code: 'UNSUPPORTED_MEDIA_TYPE',
88
- message: 'Content-Type must be application/json',
89
- details: {
90
- receivedContentType: contentType || 'none'
91
- }
92
- }
93
- });
94
- }
95
- }
96
- });
97
-
98
- // Register routes
99
- await registerHealthRoutes(server);
100
- await registerDeltaRoutes(server);
101
-
102
- // Register error handler (must be last)
103
- registerErrorHandler(server);
104
-
105
- return server;
106
- }
107
-
108
- /**
109
- * Start the server
110
- */
111
- export async function startServer(config?: ServerConfig): Promise<FastifyInstance> {
112
- const serverConfig = config || loadConfig();
113
- const server = await createServer(serverConfig);
114
-
115
- try {
116
- await server.listen({
117
- port: serverConfig.port,
118
- host: serverConfig.host
119
- });
120
-
121
- server.log.info(`🚀 Delta Engine API server running at http://${serverConfig.host}:${serverConfig.port}`);
122
- server.log.info(`📊 Health check: http://${serverConfig.host}:${serverConfig.port}/api/v1/health`);
123
- server.log.info(`📦 Environment: ${serverConfig.nodeEnv}`);
124
- server.log.info(`🔒 Rate limit: ${serverConfig.rateLimitMax} requests per ${serverConfig.rateLimitTimeWindow}`);
125
-
126
- return server;
127
- } catch (error) {
128
- server.log.error(error);
129
- throw error;
130
- }
131
- }
132
-
133
- // Start server if run directly
134
- if (require.main === module) {
135
- startServer().catch((error) => {
136
- console.error('Failed to start server:', error);
137
- process.exit(1);
138
- });
139
- }
package/src/types/api.ts DELETED
@@ -1,35 +0,0 @@
1
- /**
2
- * API-specific type definitions
3
- */
4
-
5
- export interface ErrorResponse {
6
- error: {
7
- code: string;
8
- message: string;
9
- details?: Record<string, unknown>;
10
- };
11
- }
12
-
13
- export interface HealthResponse {
14
- status: 'healthy' | 'unhealthy';
15
- uptime: number;
16
- version: string;
17
- engine: {
18
- available: boolean;
19
- };
20
- timestamp: string;
21
- }
22
-
23
- export interface VersionResponse {
24
- apiVersion: string;
25
- engineVersion: string;
26
- protocolVersion: string;
27
- }
28
-
29
- export interface ValidationResponse {
30
- valid: boolean;
31
- errors?: Array<{
32
- field: string;
33
- message: string;
34
- }>;
35
- }
package/tsconfig.json DELETED
@@ -1,9 +0,0 @@
1
- {
2
- "extends": "../tsconfig.json",
3
- "compilerOptions": {
4
- "outDir": "./dist",
5
- "rootDir": "./src"
6
- },
7
- "include": ["src/**/*"],
8
- "exclude": ["node_modules", "dist", "tests"]
9
- }