@peac/middleware-core 0.10.9
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/LICENSE +190 -0
- package/README.md +86 -0
- package/dist/config.d.ts +66 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +226 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +42 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +59 -0
- package/dist/index.js.map +1 -0
- package/dist/rate-limit.d.ts +49 -0
- package/dist/rate-limit.d.ts.map +1 -0
- package/dist/rate-limit.js +67 -0
- package/dist/rate-limit.js.map +1 -0
- package/dist/receipt.d.ts +58 -0
- package/dist/receipt.d.ts.map +1 -0
- package/dist/receipt.js +256 -0
- package/dist/receipt.js.map +1 -0
- package/dist/transport.d.ts +89 -0
- package/dist/transport.d.ts.map +1 -0
- package/dist/transport.js +173 -0
- package/dist/transport.js.map +1 -0
- package/dist/types.d.ts +131 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +10 -0
- package/dist/types.js.map +1 -0
- package/package.json +52 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* PEAC Middleware Core
|
|
4
|
+
*
|
|
5
|
+
* Framework-agnostic middleware primitives for PEAC receipt issuance.
|
|
6
|
+
*
|
|
7
|
+
* This package provides the core building blocks for integrating PEAC
|
|
8
|
+
* receipt issuance into any HTTP framework. For framework-specific
|
|
9
|
+
* implementations, see:
|
|
10
|
+
* - `@peac/middleware-express` for Express.js
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```typescript
|
|
14
|
+
* import { createReceipt, validateConfig, wrapResponse } from '@peac/middleware-core';
|
|
15
|
+
*
|
|
16
|
+
* // Validate configuration at startup
|
|
17
|
+
* validateConfig(config);
|
|
18
|
+
*
|
|
19
|
+
* // In request handler
|
|
20
|
+
* const result = await createReceipt(config, requestCtx, responseCtx);
|
|
21
|
+
*
|
|
22
|
+
* // Add headers
|
|
23
|
+
* for (const [key, value] of Object.entries(result.headers)) {
|
|
24
|
+
* res.setHeader(key, value);
|
|
25
|
+
* }
|
|
26
|
+
*
|
|
27
|
+
* // Handle body transport
|
|
28
|
+
* if (result.bodyWrapper) {
|
|
29
|
+
* res.json(result.bodyWrapper);
|
|
30
|
+
* } else {
|
|
31
|
+
* res.json(originalBody);
|
|
32
|
+
* }
|
|
33
|
+
* ```
|
|
34
|
+
*
|
|
35
|
+
* @packageDocumentation
|
|
36
|
+
*/
|
|
37
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
38
|
+
exports.MemoryRateLimitStore = exports.createReceiptWithClaims = exports.createReceipt = exports.buildReceiptResult = exports.buildResponseHeaders = exports.wrapResponse = exports.selectTransport = exports.applyDefaults = exports.MAX_PATH_LENGTH = exports.CONFIG_DEFAULTS = exports.ConfigError = exports.validateConfigAsync = exports.validateConfig = void 0;
|
|
39
|
+
// Configuration
|
|
40
|
+
var config_js_1 = require("./config.js");
|
|
41
|
+
Object.defineProperty(exports, "validateConfig", { enumerable: true, get: function () { return config_js_1.validateConfig; } });
|
|
42
|
+
Object.defineProperty(exports, "validateConfigAsync", { enumerable: true, get: function () { return config_js_1.validateConfigAsync; } });
|
|
43
|
+
Object.defineProperty(exports, "ConfigError", { enumerable: true, get: function () { return config_js_1.ConfigError; } });
|
|
44
|
+
Object.defineProperty(exports, "CONFIG_DEFAULTS", { enumerable: true, get: function () { return config_js_1.CONFIG_DEFAULTS; } });
|
|
45
|
+
Object.defineProperty(exports, "MAX_PATH_LENGTH", { enumerable: true, get: function () { return config_js_1.MAX_PATH_LENGTH; } });
|
|
46
|
+
Object.defineProperty(exports, "applyDefaults", { enumerable: true, get: function () { return config_js_1.applyDefaults; } });
|
|
47
|
+
// Transport
|
|
48
|
+
var transport_js_1 = require("./transport.js");
|
|
49
|
+
Object.defineProperty(exports, "selectTransport", { enumerable: true, get: function () { return transport_js_1.selectTransport; } });
|
|
50
|
+
Object.defineProperty(exports, "wrapResponse", { enumerable: true, get: function () { return transport_js_1.wrapResponse; } });
|
|
51
|
+
Object.defineProperty(exports, "buildResponseHeaders", { enumerable: true, get: function () { return transport_js_1.buildResponseHeaders; } });
|
|
52
|
+
Object.defineProperty(exports, "buildReceiptResult", { enumerable: true, get: function () { return transport_js_1.buildReceiptResult; } });
|
|
53
|
+
// Receipt generation
|
|
54
|
+
var receipt_js_1 = require("./receipt.js");
|
|
55
|
+
Object.defineProperty(exports, "createReceipt", { enumerable: true, get: function () { return receipt_js_1.createReceipt; } });
|
|
56
|
+
Object.defineProperty(exports, "createReceiptWithClaims", { enumerable: true, get: function () { return receipt_js_1.createReceiptWithClaims; } });
|
|
57
|
+
var rate_limit_js_1 = require("./rate-limit.js");
|
|
58
|
+
Object.defineProperty(exports, "MemoryRateLimitStore", { enumerable: true, get: function () { return rate_limit_js_1.MemoryRateLimitStore; } });
|
|
59
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;;;AAeH,gBAAgB;AAChB,yCAOqB;AANnB,2GAAA,cAAc,OAAA;AACd,gHAAA,mBAAmB,OAAA;AACnB,wGAAA,WAAW,OAAA;AACX,4GAAA,eAAe,OAAA;AACf,4GAAA,eAAe,OAAA;AACf,0GAAA,aAAa,OAAA;AAGf,YAAY;AACZ,+CAKwB;AAJtB,+GAAA,eAAe,OAAA;AACf,4GAAA,YAAY,OAAA;AACZ,oHAAA,oBAAoB,OAAA;AACpB,kHAAA,kBAAkB,OAAA;AAGpB,qBAAqB;AACrB,2CAAsE;AAA7D,2GAAA,aAAa,OAAA;AAAE,qHAAA,uBAAuB,OAAA;AAI/C,iDAAuD;AAA9C,qHAAA,oBAAoB,OAAA"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rate-limit store interface and bounded in-memory implementation.
|
|
3
|
+
*
|
|
4
|
+
* Provides a pluggable rate-limit store abstraction. The default
|
|
5
|
+
* MemoryRateLimitStore uses LRU eviction to bound memory usage.
|
|
6
|
+
*
|
|
7
|
+
* For production multi-instance deployments, implement RateLimitStore
|
|
8
|
+
* backed by Redis or similar shared storage.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Pluggable rate-limit store interface.
|
|
12
|
+
*
|
|
13
|
+
* Increment returns the current count and window reset time.
|
|
14
|
+
* Implementations must handle window expiry and cleanup.
|
|
15
|
+
*/
|
|
16
|
+
export interface RateLimitStore {
|
|
17
|
+
increment(key: string, windowMs: number): Promise<{
|
|
18
|
+
count: number;
|
|
19
|
+
resetAt: number;
|
|
20
|
+
}>;
|
|
21
|
+
reset(key: string): Promise<void>;
|
|
22
|
+
}
|
|
23
|
+
export interface MemoryRateLimitStoreOptions {
|
|
24
|
+
/** Maximum number of tracked keys before LRU eviction (default: 10000) */
|
|
25
|
+
maxKeys?: number;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Bounded in-memory rate-limit store with LRU eviction.
|
|
29
|
+
*
|
|
30
|
+
* - Expired windows are lazily cleaned on access
|
|
31
|
+
* - When maxKeys is exceeded, the least-recently-accessed entry is evicted
|
|
32
|
+
* - Suitable for single-instance deployments (state lost on restart)
|
|
33
|
+
*/
|
|
34
|
+
export declare class MemoryRateLimitStore implements RateLimitStore {
|
|
35
|
+
private readonly store;
|
|
36
|
+
private readonly maxKeys;
|
|
37
|
+
constructor(options?: MemoryRateLimitStoreOptions);
|
|
38
|
+
increment(key: string, windowMs: number): Promise<{
|
|
39
|
+
count: number;
|
|
40
|
+
resetAt: number;
|
|
41
|
+
}>;
|
|
42
|
+
reset(key: string): Promise<void>;
|
|
43
|
+
/** Number of tracked keys */
|
|
44
|
+
get size(): number;
|
|
45
|
+
/** Remove all entries */
|
|
46
|
+
clear(): void;
|
|
47
|
+
private evictIfNeeded;
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=rate-limit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rate-limit.d.ts","sourceRoot":"","sources":["../src/rate-limit.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH;;;;;GAKG;AACH,MAAM,WAAW,cAAc;IAC7B,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACtF,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACnC;AASD,MAAM,WAAW,2BAA2B;IAC1C,0EAA0E;IAC1E,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;GAMG;AACH,qBAAa,oBAAqB,YAAW,cAAc;IACzD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAkC;IACxD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;gBAErB,OAAO,CAAC,EAAE,2BAA2B;IAI3C,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAsBrF,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIvC,6BAA6B;IAC7B,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,yBAAyB;IACzB,KAAK,IAAI,IAAI;IAIb,OAAO,CAAC,aAAa;CAWtB"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Rate-limit store interface and bounded in-memory implementation.
|
|
4
|
+
*
|
|
5
|
+
* Provides a pluggable rate-limit store abstraction. The default
|
|
6
|
+
* MemoryRateLimitStore uses LRU eviction to bound memory usage.
|
|
7
|
+
*
|
|
8
|
+
* For production multi-instance deployments, implement RateLimitStore
|
|
9
|
+
* backed by Redis or similar shared storage.
|
|
10
|
+
*/
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.MemoryRateLimitStore = void 0;
|
|
13
|
+
/**
|
|
14
|
+
* Bounded in-memory rate-limit store with LRU eviction.
|
|
15
|
+
*
|
|
16
|
+
* - Expired windows are lazily cleaned on access
|
|
17
|
+
* - When maxKeys is exceeded, the least-recently-accessed entry is evicted
|
|
18
|
+
* - Suitable for single-instance deployments (state lost on restart)
|
|
19
|
+
*/
|
|
20
|
+
class MemoryRateLimitStore {
|
|
21
|
+
store = new Map();
|
|
22
|
+
maxKeys;
|
|
23
|
+
constructor(options) {
|
|
24
|
+
this.maxKeys = options?.maxKeys ?? 10_000;
|
|
25
|
+
}
|
|
26
|
+
async increment(key, windowMs) {
|
|
27
|
+
const now = Date.now();
|
|
28
|
+
let entry = this.store.get(key);
|
|
29
|
+
if (!entry || now >= entry.resetAt) {
|
|
30
|
+
// Expired or new -- start fresh window
|
|
31
|
+
entry = { count: 0, resetAt: now + windowMs, lastAccess: now };
|
|
32
|
+
}
|
|
33
|
+
entry.count++;
|
|
34
|
+
entry.lastAccess = now;
|
|
35
|
+
// Re-set to maintain Map insertion order (most recent last)
|
|
36
|
+
this.store.delete(key);
|
|
37
|
+
this.store.set(key, entry);
|
|
38
|
+
// Evict oldest entries if over capacity
|
|
39
|
+
this.evictIfNeeded();
|
|
40
|
+
return { count: entry.count, resetAt: entry.resetAt };
|
|
41
|
+
}
|
|
42
|
+
async reset(key) {
|
|
43
|
+
this.store.delete(key);
|
|
44
|
+
}
|
|
45
|
+
/** Number of tracked keys */
|
|
46
|
+
get size() {
|
|
47
|
+
return this.store.size;
|
|
48
|
+
}
|
|
49
|
+
/** Remove all entries */
|
|
50
|
+
clear() {
|
|
51
|
+
this.store.clear();
|
|
52
|
+
}
|
|
53
|
+
evictIfNeeded() {
|
|
54
|
+
while (this.store.size > this.maxKeys) {
|
|
55
|
+
// Map iterates in insertion order -- first key is oldest
|
|
56
|
+
const oldestKey = this.store.keys().next().value;
|
|
57
|
+
if (oldestKey !== undefined) {
|
|
58
|
+
this.store.delete(oldestKey);
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
break;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
exports.MemoryRateLimitStore = MemoryRateLimitStore;
|
|
67
|
+
//# sourceMappingURL=rate-limit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rate-limit.js","sourceRoot":"","sources":["../src/rate-limit.ts"],"names":[],"mappings":";AAAA;;;;;;;;GAQG;;;AAyBH;;;;;;GAMG;AACH,MAAa,oBAAoB;IACd,KAAK,GAAG,IAAI,GAAG,EAAuB,CAAC;IACvC,OAAO,CAAS;IAEjC,YAAY,OAAqC;QAC/C,IAAI,CAAC,OAAO,GAAG,OAAO,EAAE,OAAO,IAAI,MAAM,CAAC;IAC5C,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,GAAW,EAAE,QAAgB;QAC3C,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,IAAI,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAEhC,IAAI,CAAC,KAAK,IAAI,GAAG,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;YACnC,uCAAuC;YACvC,KAAK,GAAG,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,GAAG,GAAG,QAAQ,EAAE,UAAU,EAAE,GAAG,EAAE,CAAC;QACjE,CAAC;QAED,KAAK,CAAC,KAAK,EAAE,CAAC;QACd,KAAK,CAAC,UAAU,GAAG,GAAG,CAAC;QAEvB,4DAA4D;QAC5D,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACvB,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QAE3B,wCAAwC;QACxC,IAAI,CAAC,aAAa,EAAE,CAAC;QAErB,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC;IACxD,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,GAAW;QACrB,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IACzB,CAAC;IAED,6BAA6B;IAC7B,IAAI,IAAI;QACN,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;IACzB,CAAC;IAED,yBAAyB;IACzB,KAAK;QACH,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;IACrB,CAAC;IAEO,aAAa;QACnB,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC;YACtC,yDAAyD;YACzD,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC;YACjD,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;gBAC5B,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;YAC/B,CAAC;iBAAM,CAAC;gBACN,MAAM;YACR,CAAC;QACH,CAAC;IACH,CAAC;CACF;AAvDD,oDAuDC"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Receipt Generation
|
|
3
|
+
*
|
|
4
|
+
* Creates PEAC receipts from request/response context.
|
|
5
|
+
*
|
|
6
|
+
* This module produces **attestation receipts** - lightweight signed tokens
|
|
7
|
+
* that attest to API interactions. For full payment receipts with amt/cur/payment
|
|
8
|
+
* fields, use @peac/protocol directly.
|
|
9
|
+
*
|
|
10
|
+
* @packageDocumentation
|
|
11
|
+
*/
|
|
12
|
+
import type { MiddlewareConfig, RequestContext, ResponseContext, ReceiptResult, ReceiptClaimsInput } from './types.js';
|
|
13
|
+
/**
|
|
14
|
+
* Create a receipt for a request/response pair
|
|
15
|
+
*
|
|
16
|
+
* This is the main function for middleware receipt generation.
|
|
17
|
+
* It validates configuration, builds claims, signs the receipt,
|
|
18
|
+
* and determines the appropriate transport profile.
|
|
19
|
+
*
|
|
20
|
+
* By default, includes minimal interaction binding (method, path, status)
|
|
21
|
+
* in the `ext[MIDDLEWARE_INTERACTION_KEY]` field for evidentiary value.
|
|
22
|
+
*
|
|
23
|
+
* @param config - Middleware configuration
|
|
24
|
+
* @param request - Request context
|
|
25
|
+
* @param response - Response context
|
|
26
|
+
* @returns Receipt result with JWS, headers, and optional body wrapper
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```typescript
|
|
30
|
+
* const result = await createReceipt(config, requestCtx, responseCtx);
|
|
31
|
+
*
|
|
32
|
+
* // Add headers to response
|
|
33
|
+
* for (const [key, value] of Object.entries(result.headers)) {
|
|
34
|
+
* res.setHeader(key, value);
|
|
35
|
+
* }
|
|
36
|
+
*
|
|
37
|
+
* // If body transport, use wrapped body
|
|
38
|
+
* if (result.bodyWrapper) {
|
|
39
|
+
* res.json(result.bodyWrapper);
|
|
40
|
+
* }
|
|
41
|
+
* ```
|
|
42
|
+
*/
|
|
43
|
+
export declare function createReceipt(config: MiddlewareConfig, request: RequestContext, response: ResponseContext): Promise<ReceiptResult>;
|
|
44
|
+
/**
|
|
45
|
+
* Create a receipt with explicit claims (bypasses context extraction)
|
|
46
|
+
*
|
|
47
|
+
* Use this when you have explicit claims and don't need context extraction.
|
|
48
|
+
* Does NOT include automatic interaction binding.
|
|
49
|
+
*
|
|
50
|
+
* @param config - Middleware configuration
|
|
51
|
+
* @param claims - Explicit claims to include
|
|
52
|
+
* @param responseBody - Optional response body for body transport
|
|
53
|
+
* @returns Receipt result
|
|
54
|
+
*/
|
|
55
|
+
export declare function createReceiptWithClaims(config: MiddlewareConfig, claims: ReceiptClaimsInput & {
|
|
56
|
+
aud: string;
|
|
57
|
+
}, responseBody?: unknown): Promise<ReceiptResult>;
|
|
58
|
+
//# sourceMappingURL=receipt.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"receipt.d.ts","sourceRoot":"","sources":["../src/receipt.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAKH,OAAO,KAAK,EACV,gBAAgB,EAChB,cAAc,EACd,eAAe,EACf,aAAa,EACb,kBAAkB,EACnB,MAAM,YAAY,CAAC;AAiJpB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAsB,aAAa,CACjC,MAAM,EAAE,gBAAgB,EACxB,OAAO,EAAE,cAAc,EACvB,QAAQ,EAAE,eAAe,GACxB,OAAO,CAAC,aAAa,CAAC,CAsFxB;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,uBAAuB,CAC3C,MAAM,EAAE,gBAAgB,EACxB,MAAM,EAAE,kBAAkB,GAAG;IAAE,GAAG,EAAE,MAAM,CAAA;CAAE,EAC5C,YAAY,CAAC,EAAE,OAAO,GACrB,OAAO,CAAC,aAAa,CAAC,CAgDxB"}
|
package/dist/receipt.js
ADDED
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Receipt Generation
|
|
4
|
+
*
|
|
5
|
+
* Creates PEAC receipts from request/response context.
|
|
6
|
+
*
|
|
7
|
+
* This module produces **attestation receipts** - lightweight signed tokens
|
|
8
|
+
* that attest to API interactions. For full payment receipts with amt/cur/payment
|
|
9
|
+
* fields, use @peac/protocol directly.
|
|
10
|
+
*
|
|
11
|
+
* @packageDocumentation
|
|
12
|
+
*/
|
|
13
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
+
exports.createReceipt = createReceipt;
|
|
15
|
+
exports.createReceiptWithClaims = createReceiptWithClaims;
|
|
16
|
+
const uuidv7_1 = require("uuidv7");
|
|
17
|
+
const crypto_1 = require("@peac/crypto");
|
|
18
|
+
const schema_1 = require("@peac/schema");
|
|
19
|
+
const config_js_1 = require("./config.js");
|
|
20
|
+
const transport_js_1 = require("./transport.js");
|
|
21
|
+
/**
|
|
22
|
+
* Create a lowercase header lookup map for case-insensitive access
|
|
23
|
+
*
|
|
24
|
+
* HTTP headers are case-insensitive per RFC 7230, but different frameworks
|
|
25
|
+
* may provide them with different casing. This normalizes to lowercase.
|
|
26
|
+
*/
|
|
27
|
+
function normalizeHeaders(headers) {
|
|
28
|
+
const normalized = {};
|
|
29
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
30
|
+
normalized[key.toLowerCase()] = value;
|
|
31
|
+
}
|
|
32
|
+
return normalized;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Get a header value (case-insensitive)
|
|
36
|
+
*/
|
|
37
|
+
function getHeader(headers, name) {
|
|
38
|
+
const value = headers[name.toLowerCase()];
|
|
39
|
+
if (value === undefined)
|
|
40
|
+
return undefined;
|
|
41
|
+
return Array.isArray(value) ? value[0] : value;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Normalize issuer URL (remove trailing slashes for consistency)
|
|
45
|
+
*
|
|
46
|
+
* Uses explicit loop instead of regex to avoid ReDoS with quantifiers.
|
|
47
|
+
*/
|
|
48
|
+
function normalizeIssuer(issuer) {
|
|
49
|
+
let end = issuer.length;
|
|
50
|
+
while (end > 0 && issuer[end - 1] === '/') {
|
|
51
|
+
end--;
|
|
52
|
+
}
|
|
53
|
+
return end === issuer.length ? issuer : issuer.slice(0, end);
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Process path for interaction binding based on mode
|
|
57
|
+
*
|
|
58
|
+
* - 'minimal': Strip query string, truncate to MAX_PATH_LENGTH
|
|
59
|
+
* - 'full': Keep full path with query string, truncate to MAX_PATH_LENGTH
|
|
60
|
+
*
|
|
61
|
+
* @param path - Original request path (may include query string)
|
|
62
|
+
* @param mode - Interaction binding mode
|
|
63
|
+
* @returns Processed path
|
|
64
|
+
*/
|
|
65
|
+
function processPath(path, mode) {
|
|
66
|
+
let processedPath = path;
|
|
67
|
+
// Strip query string in minimal mode (privacy-safe default)
|
|
68
|
+
if (mode === 'minimal') {
|
|
69
|
+
const queryIndex = path.indexOf('?');
|
|
70
|
+
if (queryIndex !== -1) {
|
|
71
|
+
processedPath = path.substring(0, queryIndex);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
// Truncate to maximum length (DoS protection)
|
|
75
|
+
if (processedPath.length > config_js_1.MAX_PATH_LENGTH) {
|
|
76
|
+
processedPath = processedPath.substring(0, config_js_1.MAX_PATH_LENGTH);
|
|
77
|
+
}
|
|
78
|
+
return processedPath;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Extract audience from request context
|
|
82
|
+
*
|
|
83
|
+
* Derives audience from the request host or origin header (case-insensitive).
|
|
84
|
+
*/
|
|
85
|
+
function extractAudience(normalizedHeaders) {
|
|
86
|
+
// Try to get from Host header (case-insensitive)
|
|
87
|
+
const host = getHeader(normalizedHeaders, 'host');
|
|
88
|
+
if (host) {
|
|
89
|
+
// Assume HTTPS for audience
|
|
90
|
+
return `https://${host}`;
|
|
91
|
+
}
|
|
92
|
+
// Fallback to origin header
|
|
93
|
+
const origin = getHeader(normalizedHeaders, 'origin');
|
|
94
|
+
if (origin) {
|
|
95
|
+
return origin;
|
|
96
|
+
}
|
|
97
|
+
// Should not happen with proper request context
|
|
98
|
+
return 'https://localhost';
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Convert JWK private key to raw bytes
|
|
102
|
+
*/
|
|
103
|
+
function jwkToPrivateKeyBytes(jwk) {
|
|
104
|
+
return (0, crypto_1.base64urlDecode)(jwk.d);
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Create a receipt for a request/response pair
|
|
108
|
+
*
|
|
109
|
+
* This is the main function for middleware receipt generation.
|
|
110
|
+
* It validates configuration, builds claims, signs the receipt,
|
|
111
|
+
* and determines the appropriate transport profile.
|
|
112
|
+
*
|
|
113
|
+
* By default, includes minimal interaction binding (method, path, status)
|
|
114
|
+
* in the `ext[MIDDLEWARE_INTERACTION_KEY]` field for evidentiary value.
|
|
115
|
+
*
|
|
116
|
+
* @param config - Middleware configuration
|
|
117
|
+
* @param request - Request context
|
|
118
|
+
* @param response - Response context
|
|
119
|
+
* @returns Receipt result with JWS, headers, and optional body wrapper
|
|
120
|
+
*
|
|
121
|
+
* @example
|
|
122
|
+
* ```typescript
|
|
123
|
+
* const result = await createReceipt(config, requestCtx, responseCtx);
|
|
124
|
+
*
|
|
125
|
+
* // Add headers to response
|
|
126
|
+
* for (const [key, value] of Object.entries(result.headers)) {
|
|
127
|
+
* res.setHeader(key, value);
|
|
128
|
+
* }
|
|
129
|
+
*
|
|
130
|
+
* // If body transport, use wrapped body
|
|
131
|
+
* if (result.bodyWrapper) {
|
|
132
|
+
* res.json(result.bodyWrapper);
|
|
133
|
+
* }
|
|
134
|
+
* ```
|
|
135
|
+
*/
|
|
136
|
+
async function createReceipt(config, request, response) {
|
|
137
|
+
// Validate configuration
|
|
138
|
+
(0, config_js_1.validateConfig)(config);
|
|
139
|
+
// Apply defaults
|
|
140
|
+
const fullConfig = (0, config_js_1.applyDefaults)(config);
|
|
141
|
+
// Normalize headers for case-insensitive access
|
|
142
|
+
const normalizedHeaders = normalizeHeaders(request.headers);
|
|
143
|
+
// Generate receipt ID (UUIDv7 for time-ordering)
|
|
144
|
+
const rid = (0, uuidv7_1.uuidv7)();
|
|
145
|
+
// Get current timestamp
|
|
146
|
+
const iat = Math.floor(Date.now() / 1000);
|
|
147
|
+
const exp = iat + fullConfig.expiresIn;
|
|
148
|
+
// Normalize issuer and extract audience
|
|
149
|
+
const normalizedIssuer = normalizeIssuer(config.issuer);
|
|
150
|
+
const audience = extractAudience(normalizedHeaders);
|
|
151
|
+
// Build base claims
|
|
152
|
+
const claims = {
|
|
153
|
+
iss: normalizedIssuer,
|
|
154
|
+
aud: audience,
|
|
155
|
+
iat,
|
|
156
|
+
exp,
|
|
157
|
+
rid,
|
|
158
|
+
};
|
|
159
|
+
// Add interaction binding unless disabled
|
|
160
|
+
if (fullConfig.interactionBinding !== 'off') {
|
|
161
|
+
const processedPath = processPath(request.path, fullConfig.interactionBinding);
|
|
162
|
+
const interactionBinding = {
|
|
163
|
+
method: request.method.toUpperCase(),
|
|
164
|
+
path: processedPath,
|
|
165
|
+
status: response.statusCode,
|
|
166
|
+
};
|
|
167
|
+
claims.ext = {
|
|
168
|
+
[schema_1.MIDDLEWARE_INTERACTION_KEY]: interactionBinding,
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
// Apply custom claims if generator is provided
|
|
172
|
+
if (config.claimsGenerator) {
|
|
173
|
+
const customClaims = await config.claimsGenerator(request);
|
|
174
|
+
// Override audience if provided
|
|
175
|
+
if (customClaims.aud) {
|
|
176
|
+
claims.aud = customClaims.aud;
|
|
177
|
+
}
|
|
178
|
+
// Add subject if provided
|
|
179
|
+
if (customClaims.sub) {
|
|
180
|
+
claims.sub = customClaims.sub;
|
|
181
|
+
}
|
|
182
|
+
// Merge extensions (custom claims override defaults)
|
|
183
|
+
if (customClaims.ext) {
|
|
184
|
+
claims.ext = { ...claims.ext, ...customClaims.ext };
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
// Sign the receipt
|
|
188
|
+
const privateKeyBytes = jwkToPrivateKeyBytes(config.signingKey);
|
|
189
|
+
const receipt = await (0, crypto_1.sign)(claims, privateKeyBytes, config.keyId);
|
|
190
|
+
// Determine transport profile
|
|
191
|
+
const transport = (0, transport_js_1.selectTransport)(receipt, fullConfig);
|
|
192
|
+
// Generate pointer URL if needed
|
|
193
|
+
let pointerUrl;
|
|
194
|
+
if (transport === 'pointer' && config.pointerUrlGenerator) {
|
|
195
|
+
pointerUrl = await config.pointerUrlGenerator(receipt);
|
|
196
|
+
}
|
|
197
|
+
// Build complete result
|
|
198
|
+
return (0, transport_js_1.buildReceiptResult)({
|
|
199
|
+
receipt,
|
|
200
|
+
transport,
|
|
201
|
+
pointerUrl,
|
|
202
|
+
originalBody: response.body,
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Create a receipt with explicit claims (bypasses context extraction)
|
|
207
|
+
*
|
|
208
|
+
* Use this when you have explicit claims and don't need context extraction.
|
|
209
|
+
* Does NOT include automatic interaction binding.
|
|
210
|
+
*
|
|
211
|
+
* @param config - Middleware configuration
|
|
212
|
+
* @param claims - Explicit claims to include
|
|
213
|
+
* @param responseBody - Optional response body for body transport
|
|
214
|
+
* @returns Receipt result
|
|
215
|
+
*/
|
|
216
|
+
async function createReceiptWithClaims(config, claims, responseBody) {
|
|
217
|
+
// Validate configuration
|
|
218
|
+
(0, config_js_1.validateConfig)(config);
|
|
219
|
+
// Apply defaults
|
|
220
|
+
const fullConfig = (0, config_js_1.applyDefaults)(config);
|
|
221
|
+
// Generate receipt ID
|
|
222
|
+
const rid = (0, uuidv7_1.uuidv7)();
|
|
223
|
+
// Get current timestamp
|
|
224
|
+
const iat = Math.floor(Date.now() / 1000);
|
|
225
|
+
const exp = iat + fullConfig.expiresIn;
|
|
226
|
+
// Normalize issuer
|
|
227
|
+
const normalizedIssuer = normalizeIssuer(config.issuer);
|
|
228
|
+
// Build claims
|
|
229
|
+
const receiptClaims = {
|
|
230
|
+
iss: normalizedIssuer,
|
|
231
|
+
aud: claims.aud,
|
|
232
|
+
iat,
|
|
233
|
+
exp,
|
|
234
|
+
rid,
|
|
235
|
+
...(claims.sub && { sub: claims.sub }),
|
|
236
|
+
...(claims.ext && { ext: claims.ext }),
|
|
237
|
+
};
|
|
238
|
+
// Sign the receipt
|
|
239
|
+
const privateKeyBytes = jwkToPrivateKeyBytes(config.signingKey);
|
|
240
|
+
const receipt = await (0, crypto_1.sign)(receiptClaims, privateKeyBytes, config.keyId);
|
|
241
|
+
// Determine transport profile
|
|
242
|
+
const transport = (0, transport_js_1.selectTransport)(receipt, fullConfig);
|
|
243
|
+
// Generate pointer URL if needed
|
|
244
|
+
let pointerUrl;
|
|
245
|
+
if (transport === 'pointer' && config.pointerUrlGenerator) {
|
|
246
|
+
pointerUrl = await config.pointerUrlGenerator(receipt);
|
|
247
|
+
}
|
|
248
|
+
// Build complete result
|
|
249
|
+
return (0, transport_js_1.buildReceiptResult)({
|
|
250
|
+
receipt,
|
|
251
|
+
transport,
|
|
252
|
+
pointerUrl,
|
|
253
|
+
originalBody: responseBody,
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
//# sourceMappingURL=receipt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"receipt.js","sourceRoot":"","sources":["../src/receipt.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transport Profile Selection and Response Wrapping
|
|
3
|
+
*
|
|
4
|
+
* Implements transport profile selection and body wrapping per TRANSPORT-PROFILES.md.
|
|
5
|
+
*
|
|
6
|
+
* @packageDocumentation
|
|
7
|
+
*/
|
|
8
|
+
import type { MiddlewareConfig } from './types.js';
|
|
9
|
+
/**
|
|
10
|
+
* Calculate receipt size and determine appropriate transport
|
|
11
|
+
*
|
|
12
|
+
* Falls back from header to body if receipt exceeds maxHeaderSize.
|
|
13
|
+
* Pointer transport is only used if explicitly configured (never auto-selected).
|
|
14
|
+
*
|
|
15
|
+
* @param receipt - JWS compact serialization
|
|
16
|
+
* @param config - Transport configuration (optional fields)
|
|
17
|
+
* @returns Selected transport profile
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```typescript
|
|
21
|
+
* const transport = selectTransport(receipt, { maxHeaderSize: 4096 });
|
|
22
|
+
* // Returns 'header' if receipt fits, 'body' otherwise
|
|
23
|
+
*
|
|
24
|
+
* // Also accepts empty config for defaults
|
|
25
|
+
* const transport = selectTransport(receipt, {});
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export declare function selectTransport(receipt: string, config?: Partial<Pick<MiddlewareConfig, 'transport' | 'maxHeaderSize'>>): 'header' | 'body' | 'pointer';
|
|
29
|
+
/**
|
|
30
|
+
* Wrap a response body with a PEAC receipt (for body transport profile)
|
|
31
|
+
*
|
|
32
|
+
* Creates a wrapper object containing the original data and the receipt.
|
|
33
|
+
* Per TRANSPORT-PROFILES.md, the body format is:
|
|
34
|
+
* `{ "data": <original>, "peac_receipt": "<jws>" }`
|
|
35
|
+
*
|
|
36
|
+
* @param data - Original response body
|
|
37
|
+
* @param receipt - JWS compact serialization
|
|
38
|
+
* @returns Wrapped response body
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* ```typescript
|
|
42
|
+
* const original = { items: [1, 2, 3] };
|
|
43
|
+
* const wrapped = wrapResponse(original, receipt);
|
|
44
|
+
* // { data: { items: [1, 2, 3] }, peac_receipt: "eyJ..." }
|
|
45
|
+
* res.json(wrapped);
|
|
46
|
+
* ```
|
|
47
|
+
*/
|
|
48
|
+
export declare function wrapResponse<T>(data: T, receipt: string): {
|
|
49
|
+
data: T;
|
|
50
|
+
peac_receipt: string;
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* Build response headers for a receipt based on transport profile
|
|
54
|
+
*
|
|
55
|
+
* For header profile: adds PEAC-Receipt header
|
|
56
|
+
* For pointer profile: adds PEAC-Receipt-Pointer header
|
|
57
|
+
* For body profile: returns empty headers (receipt is in body)
|
|
58
|
+
*
|
|
59
|
+
* @param receipt - JWS compact serialization
|
|
60
|
+
* @param transport - Transport profile to use
|
|
61
|
+
* @param pointerUrl - URL for pointer profile (required if transport is 'pointer')
|
|
62
|
+
* @returns Headers to add to response
|
|
63
|
+
*/
|
|
64
|
+
export declare function buildResponseHeaders(receipt: string, transport: 'header' | 'body' | 'pointer', pointerUrl?: string): Promise<Record<string, string>>;
|
|
65
|
+
/**
|
|
66
|
+
* Input for building complete receipt result
|
|
67
|
+
*/
|
|
68
|
+
export interface BuildReceiptResultInput {
|
|
69
|
+
receipt: string;
|
|
70
|
+
transport: 'header' | 'body' | 'pointer';
|
|
71
|
+
pointerUrl?: string;
|
|
72
|
+
originalBody?: unknown;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Build complete receipt result with headers and optional body wrapper
|
|
76
|
+
*
|
|
77
|
+
* @param input - Receipt and transport information
|
|
78
|
+
* @returns Complete receipt result ready for response
|
|
79
|
+
*/
|
|
80
|
+
export declare function buildReceiptResult(input: BuildReceiptResultInput): Promise<{
|
|
81
|
+
receipt: string;
|
|
82
|
+
transport: 'header' | 'body' | 'pointer';
|
|
83
|
+
headers: Record<string, string>;
|
|
84
|
+
bodyWrapper?: {
|
|
85
|
+
data: unknown;
|
|
86
|
+
peac_receipt: string;
|
|
87
|
+
};
|
|
88
|
+
}>;
|
|
89
|
+
//# sourceMappingURL=transport.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../src/transport.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAKH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AA2CnD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,MAAM,EACf,MAAM,GAAE,OAAO,CAAC,IAAI,CAAC,gBAAgB,EAAE,WAAW,GAAG,eAAe,CAAC,CAAM,GAC1E,QAAQ,GAAG,MAAM,GAAG,SAAS,CAsB/B;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,OAAO,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,CAAC,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,CAK3F;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,QAAQ,GAAG,MAAM,GAAG,SAAS,EACxC,UAAU,CAAC,EAAE,MAAM,GAClB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CA4BjC;AAED;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACtC,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,QAAQ,GAAG,MAAM,GAAG,SAAS,CAAC;IACzC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAED;;;;;GAKG;AACH,wBAAsB,kBAAkB,CAAC,KAAK,EAAE,uBAAuB,GAAG,OAAO,CAAC;IAChF,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,QAAQ,GAAG,MAAM,GAAG,SAAS,CAAC;IACzC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,WAAW,CAAC,EAAE;QAAE,IAAI,EAAE,OAAO,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE,CAAC;CACvD,CAAC,CAoBD"}
|