@faremeter/middleware 0.8.0 → 0.9.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.
@@ -0,0 +1,16 @@
1
+ export type AgedLRUCacheOpts = {
2
+ capacity?: number;
3
+ maxAge?: number;
4
+ now?: () => number;
5
+ };
6
+ export declare class AgedLRUCache<K, V> {
7
+ private maxAge;
8
+ private capacity;
9
+ private cache;
10
+ private now;
11
+ constructor(opts?: AgedLRUCacheOpts);
12
+ get(key: K): V | undefined;
13
+ put(key: K, value: V): void;
14
+ get size(): number;
15
+ }
16
+ //# sourceMappingURL=cache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../src/cache.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,gBAAgB,GAAG;IAC7B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACpB,CAAC;AAEF,qBAAa,YAAY,CAAC,CAAC,EAAE,CAAC;IAC5B,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,KAAK,CAAsC;IACnD,OAAO,CAAC,GAAG,CAAe;gBAEd,IAAI,GAAE,gBAAqB;IAOvC,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,GAAG,SAAS;IAiB1B,GAAG,CAAC,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,IAAI;IAY3B,IAAW,IAAI,WAEd;CACF"}
@@ -0,0 +1,39 @@
1
+ export class AgedLRUCache {
2
+ maxAge;
3
+ capacity;
4
+ cache;
5
+ now;
6
+ constructor(opts = {}) {
7
+ this.capacity = opts.capacity ?? 256;
8
+ this.maxAge = opts.maxAge ?? 30 * 1000;
9
+ this.now = opts.now ?? Date.now;
10
+ this.cache = new Map();
11
+ }
12
+ get(key) {
13
+ const value = this.cache.get(key);
14
+ if (value === undefined) {
15
+ return undefined;
16
+ }
17
+ this.cache.delete(key);
18
+ if (this.now() - value.birth >= this.maxAge) {
19
+ return undefined;
20
+ }
21
+ this.cache.set(key, value);
22
+ return value.value;
23
+ }
24
+ put(key, value) {
25
+ if (this.cache.has(key)) {
26
+ this.cache.delete(key);
27
+ }
28
+ else if (this.cache.size >= this.capacity) {
29
+ const lruKey = this.cache.keys().next().value;
30
+ if (lruKey !== undefined) {
31
+ this.cache.delete(lruKey);
32
+ }
33
+ }
34
+ this.cache.set(key, { birth: this.now(), value });
35
+ }
36
+ get size() {
37
+ return this.cache.size;
38
+ }
39
+ }
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env pnpm tsx
2
+ export {};
3
+ //# sourceMappingURL=cache.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache.test.d.ts","sourceRoot":"","sources":["../../src/cache.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,43 @@
1
+ #!/usr/bin/env pnpm tsx
2
+ import t from "tap";
3
+ import { AgedLRUCache } from "./cache.js";
4
+ await t.test("checkBasicCaching", async (t) => {
5
+ let theTime = 0;
6
+ const now = () => theTime;
7
+ const cache = new AgedLRUCache({
8
+ capacity: 3,
9
+ maxAge: 1000,
10
+ now,
11
+ });
12
+ t.equal(cache.size, 0);
13
+ t.matchOnly(cache.get("somekey"), undefined);
14
+ cache.put("somekey", 42);
15
+ theTime += 500;
16
+ t.equal(cache.size, 1);
17
+ t.matchOnly(cache.get("somekey"), 42);
18
+ theTime += 1000;
19
+ t.matchOnly(cache.get("somekey"), undefined);
20
+ t.matchOnly(cache.size, 0);
21
+ cache.put("0key", 0);
22
+ cache.put("1key", 1);
23
+ cache.put("2key", 2);
24
+ t.equal(cache.size, 3);
25
+ t.matchOnly(cache.get("0key"), 0);
26
+ cache.put("3key", 3);
27
+ t.equal(cache.size, 3);
28
+ t.matchOnly(cache.get("1key"), undefined);
29
+ t.matchOnly(cache.get("2key"), 2);
30
+ cache.put("4key", 4);
31
+ t.equal(cache.size, 3);
32
+ t.matchOnly(cache.get("0key"), undefined);
33
+ t.matchOnly(cache.get("2key"), 2);
34
+ t.matchOnly(cache.get("3key"), 3);
35
+ t.matchOnly(cache.get("4key"), 4);
36
+ theTime += 1000;
37
+ t.matchOnly(cache.get("2key"), undefined);
38
+ t.matchOnly(cache.get("3key"), undefined);
39
+ t.matchOnly(cache.get("4key"), undefined);
40
+ t.equal(cache.size, 0);
41
+ t.pass();
42
+ t.end();
43
+ });
@@ -1,4 +1,5 @@
1
1
  import { type x402PaymentRequirements, type x402PaymentPayload } from "@faremeter/types/x402";
2
+ import { type AgedLRUCacheOpts } from "./cache.js";
2
3
  export declare function findMatchingPaymentRequirements(accepts: x402PaymentRequirements[], payload: x402PaymentPayload): {
3
4
  scheme: string;
4
5
  network: string;
@@ -41,12 +42,20 @@ type PossibleJSONResponse = object;
41
42
  export type CommonMiddlewareArgs = {
42
43
  facilitatorURL: string;
43
44
  accepts: RelaxedRequirements[];
45
+ cacheConfig?: createPaymentRequiredResponseCacheOpts;
44
46
  };
45
47
  export type HandleMiddlewareRequestArgs<MiddlewareResponse = unknown> = CommonMiddlewareArgs & {
46
48
  resource: string;
47
49
  getHeader: (key: string) => string | undefined;
50
+ getPaymentRequiredResponse: typeof getPaymentRequiredResponse;
48
51
  sendJSONResponse: (status: PossibleStatusCodes, obj: PossibleJSONResponse) => MiddlewareResponse;
49
52
  };
50
53
  export declare function handleMiddlewareRequest<MiddlewareResponse>(args: HandleMiddlewareRequestArgs<MiddlewareResponse>): Promise<MiddlewareResponse | undefined>;
54
+ export type createPaymentRequiredResponseCacheOpts = AgedLRUCacheOpts & {
55
+ disable?: boolean;
56
+ };
57
+ export declare function createPaymentRequiredResponseCache(opts?: createPaymentRequiredResponseCacheOpts): {
58
+ getPaymentRequiredResponse: typeof getPaymentRequiredResponse;
59
+ };
51
60
  export {};
52
61
  //# sourceMappingURL=common.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"common.d.ts","sourceRoot":"","sources":["../../src/common.ts"],"names":[],"mappings":"AACA,OAAO,EACL,KAAK,uBAAuB,EAC5B,KAAK,kBAAkB,EAKxB,MAAM,uBAAuB,CAAC;AAI/B,wBAAgB,+BAA+B,CAC7C,OAAO,EAAE,uBAAuB,EAAE,EAClC,OAAO,EAAE,kBAAkB;;;;;;;;;;;;cA6B5B;AAED,wBAAgB,8BAA8B,CAAC,GAAG,EAAE,QAAQ,QAS3D;AAED,MAAM,MAAM,mBAAmB,GAAG,OAAO,CAAC,uBAAuB,CAAC,CAAC;AAEnE,KAAK,8BAA8B,GAAG;IACpC,cAAc,EAAE,MAAM,CAAC;IACvB,OAAO,EAAE,mBAAmB,EAAE,CAAC;IAC/B,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,wBAAsB,0BAA0B,CAC9C,IAAI,EAAE,8BAA8B;;;;;;;;;;;;oBApCkC,CAAC;aAIhE,CAAC;;;GA8DT;AAED,KAAK,mBAAmB,GAAG,GAAG,CAAC;AAC/B,KAAK,oBAAoB,GAAG,MAAM,CAAC;AAEnC,MAAM,MAAM,oBAAoB,GAAG;IACjC,cAAc,EAAE,MAAM,CAAC;IACvB,OAAO,EAAE,mBAAmB,EAAE,CAAC;CAChC,CAAC;AAEF,MAAM,MAAM,2BAA2B,CAAC,kBAAkB,GAAG,OAAO,IAClE,oBAAoB,GAAG;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;IAC/C,gBAAgB,EAAE,CAChB,MAAM,EAAE,mBAAmB,EAC3B,GAAG,EAAE,oBAAoB,KACtB,kBAAkB,CAAC;CACzB,CAAC;AAEJ,wBAAsB,uBAAuB,CAAC,kBAAkB,EAC9D,IAAI,EAAE,2BAA2B,CAAC,kBAAkB,CAAC,2CA6DtD"}
1
+ {"version":3,"file":"common.d.ts","sourceRoot":"","sources":["../../src/common.ts"],"names":[],"mappings":"AACA,OAAO,EACL,KAAK,uBAAuB,EAC5B,KAAK,kBAAkB,EAKxB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,KAAK,gBAAgB,EAAgB,MAAM,SAAS,CAAC;AAI9D,wBAAgB,+BAA+B,CAC7C,OAAO,EAAE,uBAAuB,EAAE,EAClC,OAAO,EAAE,kBAAkB;;;;;;;;;;;;cA6B5B;AAED,wBAAgB,8BAA8B,CAAC,GAAG,EAAE,QAAQ,QAS3D;AAED,MAAM,MAAM,mBAAmB,GAAG,OAAO,CAAC,uBAAuB,CAAC,CAAC;AAEnE,KAAK,8BAA8B,GAAG;IACpC,cAAc,EAAE,MAAM,CAAC;IACvB,OAAO,EAAE,mBAAmB,EAAE,CAAC;IAC/B,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,wBAAsB,0BAA0B,CAC9C,IAAI,EAAE,8BAA8B;;;;;;;;;;;;oBApC5B,CAAA;aAAuB,CAAC;;;GAkEjC;AAED,KAAK,mBAAmB,GAAG,GAAG,CAAC;AAC/B,KAAK,oBAAoB,GAAG,MAAM,CAAC;AAEnC,MAAM,MAAM,oBAAoB,GAAG;IACjC,cAAc,EAAE,MAAM,CAAC;IACvB,OAAO,EAAE,mBAAmB,EAAE,CAAC;IAC/B,WAAW,CAAC,EAAE,sCAAsC,CAAC;CACtD,CAAC;AAEF,MAAM,MAAM,2BAA2B,CAAC,kBAAkB,GAAG,OAAO,IAClE,oBAAoB,GAAG;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;IAC/C,0BAA0B,EAAE,OAAO,0BAA0B,CAAC;IAC9D,gBAAgB,EAAE,CAChB,MAAM,EAAE,mBAAmB,EAC3B,GAAG,EAAE,oBAAoB,KACtB,kBAAkB,CAAC;CACzB,CAAC;AAEJ,wBAAsB,uBAAuB,CAAC,kBAAkB,EAC9D,IAAI,EAAE,2BAA2B,CAAC,kBAAkB,CAAC,2CA2DtD;AAED,MAAM,MAAM,sCAAsC,GAAG,gBAAgB,GAAG;IACtE,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC;AACF,wBAAgB,kCAAkC,CAChD,IAAI,GAAE,sCAA2C;;EA8BlD"}
@@ -1,5 +1,6 @@
1
1
  import { isValidationError } from "@faremeter/types";
2
2
  import { x402PaymentRequiredResponse, x402PaymentHeaderToPayload, x402SettleRequest, x402SettleResponse, } from "@faremeter/types/x402";
3
+ import { AgedLRUCache } from "./cache.js";
3
4
  import { logger } from "./logger.js";
4
5
  export function findMatchingPaymentRequirements(accepts, payload) {
5
6
  let possible;
@@ -52,9 +53,7 @@ export async function getPaymentRequiredResponse(args) {
52
53
  return response;
53
54
  }
54
55
  export async function handleMiddlewareRequest(args) {
55
- // XXX - Temporarily request this every time. This will be
56
- // cached in future.
57
- const paymentRequiredResponse = await getPaymentRequiredResponse(args);
56
+ const paymentRequiredResponse = await args.getPaymentRequiredResponse(args);
58
57
  const sendPaymentRequired = () => args.sendJSONResponse(402, paymentRequiredResponse);
59
58
  const paymentHeader = args.getHeader("X-PAYMENT");
60
59
  if (!paymentHeader) {
@@ -94,3 +93,22 @@ export async function handleMiddlewareRequest(args) {
94
93
  return sendPaymentRequired();
95
94
  }
96
95
  }
96
+ export function createPaymentRequiredResponseCache(opts = {}) {
97
+ if (opts.disable) {
98
+ logger.warning("payment required response cache disabled");
99
+ return {
100
+ getPaymentRequiredResponse,
101
+ };
102
+ }
103
+ const cache = new AgedLRUCache(opts);
104
+ return {
105
+ getPaymentRequiredResponse: async (args) => {
106
+ let response = cache.get(args.accepts);
107
+ if (response === undefined) {
108
+ response = await getPaymentRequiredResponse(args);
109
+ cache.put(args.accepts, response);
110
+ }
111
+ return response;
112
+ },
113
+ };
114
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"express.d.ts","sourceRoot":"","sources":["../../src/express.ts"],"names":[],"mappings":"AAAA,OAAO,EAA2B,KAAK,oBAAoB,EAAE,MAAM,UAAU,CAAC;AAC9E,OAAO,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAE/D,KAAK,oBAAoB,GAAG,oBAAoB,CAAC;AAEjD,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,oBAAoB,iBAC5C,OAAO,OAAO,QAAQ,QAAQ,YAAY,oBAc9D"}
1
+ {"version":3,"file":"express.d.ts","sourceRoot":"","sources":["../../src/express.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,oBAAoB,EAE1B,MAAM,UAAU,CAAC;AAClB,OAAO,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAE/D,KAAK,oBAAoB,GAAG,oBAAoB,CAAC;AAEjD,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,oBAAoB,iBAK5C,OAAO,OAAO,QAAQ,QAAQ,YAAY,oBAe9D"}
@@ -1,9 +1,11 @@
1
- import { handleMiddlewareRequest } from "./common.js";
1
+ import { handleMiddlewareRequest, createPaymentRequiredResponseCache, } from "./common.js";
2
2
  export async function createMiddleware(args) {
3
+ const { getPaymentRequiredResponse } = createPaymentRequiredResponseCache(args.cacheConfig);
3
4
  return async (req, res, next) => {
4
5
  const response = await handleMiddlewareRequest({
5
6
  ...args,
6
7
  resource: `${req.protocol}://${req.headers.host}${req.path}`,
8
+ getPaymentRequiredResponse,
7
9
  getHeader: (key) => req.header(key),
8
10
  sendJSONResponse: (status, body) => res.status(status).json(body),
9
11
  });
@@ -1 +1 @@
1
- {"version":3,"file":"hono.d.ts","sourceRoot":"","sources":["../../src/hono.ts"],"names":[],"mappings":"AAAA,OAAO,EAA2B,KAAK,oBAAoB,EAAE,MAAM,UAAU,CAAC;AAC9E,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,MAAM,CAAC;AAE9C,KAAK,oBAAoB,GAAG,oBAAoB,CAAC;AAEjD,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,oBAAoB,GACzB,OAAO,CAAC,iBAAiB,CAAC,CAkB5B"}
1
+ {"version":3,"file":"hono.d.ts","sourceRoot":"","sources":["../../src/hono.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,oBAAoB,EAE1B,MAAM,UAAU,CAAC;AAClB,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,MAAM,CAAC;AAE9C,KAAK,oBAAoB,GAAG,oBAAoB,CAAC;AAEjD,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,oBAAoB,GACzB,OAAO,CAAC,iBAAiB,CAAC,CAuB5B"}
package/dist/src/hono.js CHANGED
@@ -1,10 +1,12 @@
1
- import { handleMiddlewareRequest } from "./common.js";
1
+ import { handleMiddlewareRequest, createPaymentRequiredResponseCache, } from "./common.js";
2
2
  export async function createMiddleware(args) {
3
+ const { getPaymentRequiredResponse } = createPaymentRequiredResponseCache(args.cacheConfig);
3
4
  return async (c, next) => {
4
5
  const response = await handleMiddlewareRequest({
5
6
  ...args,
6
7
  resource: c.req.url,
7
8
  getHeader: (key) => c.req.header(key),
9
+ getPaymentRequiredResponse,
8
10
  sendJSONResponse: (status, body) => {
9
11
  c.status(status);
10
12
  return c.json(body);
@@ -1,4 +1,5 @@
1
1
  export * as express from "./express.js";
2
2
  export * as hono from "./hono.js";
3
3
  export * as common from "./common.js";
4
+ export * as cache from "./cache.js";
4
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,OAAO,MAAM,WAAW,CAAC;AACrC,OAAO,KAAK,IAAI,MAAM,QAAQ,CAAC;AAC/B,OAAO,KAAK,MAAM,MAAM,UAAU,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,OAAO,MAAM,WAAW,CAAC;AACrC,OAAO,KAAK,IAAI,MAAM,QAAQ,CAAC;AAC/B,OAAO,KAAK,MAAM,MAAM,UAAU,CAAC;AACnC,OAAO,KAAK,KAAK,MAAM,SAAS,CAAC"}
package/dist/src/index.js CHANGED
@@ -1,3 +1,4 @@
1
1
  export * as express from "./express.js";
2
2
  export * as hono from "./hono.js";
3
3
  export * as common from "./common.js";
4
+ export * as cache from "./cache.js";