@faremeter/test-harness 0.16.0 → 0.17.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 (61) hide show
  1. package/dist/src/choosers.d.ts +74 -10
  2. package/dist/src/choosers.d.ts.map +1 -1
  3. package/dist/src/choosers.js +64 -0
  4. package/dist/src/harness/config.d.ts +19 -3
  5. package/dist/src/harness/config.d.ts.map +1 -1
  6. package/dist/src/harness/defaults.d.ts +10 -3
  7. package/dist/src/harness/defaults.d.ts.map +1 -1
  8. package/dist/src/harness/defaults.js +17 -1
  9. package/dist/src/harness/harness.d.ts +10 -4
  10. package/dist/src/harness/harness.d.ts.map +1 -1
  11. package/dist/src/harness/harness.js +124 -48
  12. package/dist/src/harness/harness.test.js +3 -2
  13. package/dist/src/harness/resource.d.ts +36 -2
  14. package/dist/src/harness/resource.d.ts.map +1 -1
  15. package/dist/src/harness/resource.js +33 -8
  16. package/dist/src/index.d.ts +12 -4
  17. package/dist/src/index.d.ts.map +1 -1
  18. package/dist/src/index.js +11 -3
  19. package/dist/src/interceptors/delay.d.ts +21 -0
  20. package/dist/src/interceptors/delay.d.ts.map +1 -1
  21. package/dist/src/interceptors/delay.js +21 -0
  22. package/dist/src/interceptors/failures.d.ts +42 -0
  23. package/dist/src/interceptors/failures.d.ts.map +1 -1
  24. package/dist/src/interceptors/failures.js +42 -0
  25. package/dist/src/interceptors/hooks.d.ts +27 -0
  26. package/dist/src/interceptors/hooks.d.ts.map +1 -1
  27. package/dist/src/interceptors/hooks.js +27 -0
  28. package/dist/src/interceptors/logging.d.ts +21 -0
  29. package/dist/src/interceptors/logging.d.ts.map +1 -1
  30. package/dist/src/interceptors/logging.js +18 -0
  31. package/dist/src/interceptors/matchers.d.ts +30 -0
  32. package/dist/src/interceptors/matchers.d.ts.map +1 -1
  33. package/dist/src/interceptors/matchers.js +30 -0
  34. package/dist/src/interceptors/responses.d.ts +75 -3
  35. package/dist/src/interceptors/responses.d.ts.map +1 -1
  36. package/dist/src/interceptors/responses.js +98 -8
  37. package/dist/src/interceptors/types.d.ts +11 -0
  38. package/dist/src/interceptors/types.d.ts.map +1 -1
  39. package/dist/src/interceptors/utils.d.ts +6 -0
  40. package/dist/src/interceptors/utils.d.ts.map +1 -1
  41. package/dist/src/interceptors/utils.js +6 -0
  42. package/dist/src/interceptors/v2.d.ts +15 -0
  43. package/dist/src/interceptors/v2.d.ts.map +1 -0
  44. package/dist/src/interceptors/v2.js +52 -0
  45. package/dist/src/scheme/client.d.ts +8 -2
  46. package/dist/src/scheme/client.d.ts.map +1 -1
  47. package/dist/src/scheme/client.js +1 -5
  48. package/dist/src/scheme/constants.d.ts +7 -0
  49. package/dist/src/scheme/constants.d.ts.map +1 -1
  50. package/dist/src/scheme/constants.js +7 -0
  51. package/dist/src/scheme/facilitator.d.ts +7 -1
  52. package/dist/src/scheme/facilitator.d.ts.map +1 -1
  53. package/dist/src/scheme/facilitator.js +35 -40
  54. package/dist/src/scheme/types.d.ts +8 -0
  55. package/dist/src/scheme/types.d.ts.map +1 -1
  56. package/dist/src/scheme/types.js +5 -0
  57. package/dist/src/test-handlers.d.ts +52 -0
  58. package/dist/src/test-handlers.d.ts.map +1 -0
  59. package/dist/src/test-handlers.js +110 -0
  60. package/dist/tsconfig.tsbuildinfo +1 -1
  61. package/package.json +7 -5
@@ -1,12 +1,76 @@
1
- import type { PaymentExecer } from "@faremeter/types/client";
2
- export declare function chooseFirst(execers: PaymentExecer[]): PaymentExecer;
3
- export declare function chooseCheapest(execers: PaymentExecer[]): PaymentExecer;
4
- export declare function chooseMostExpensive(execers: PaymentExecer[]): PaymentExecer;
5
- export declare function chooseByAsset(asset: string): (execers: PaymentExecer[]) => PaymentExecer;
6
- export declare function chooseByNetwork(network: string): (execers: PaymentExecer[]) => PaymentExecer;
7
- export declare function chooseByScheme(scheme: string): (execers: PaymentExecer[]) => PaymentExecer;
8
- export declare function chooseByIndex(index: number): (execers: PaymentExecer[]) => PaymentExecer;
1
+ import type { PaymentExecerV1 } from "@faremeter/types/client";
2
+ /**
3
+ * Chooser that selects the first available payment option.
4
+ *
5
+ * @param execers - Available payment execers.
6
+ * @returns The first execer in the list.
7
+ * @throws If no options are available.
8
+ */
9
+ export declare function chooseFirst(execers: PaymentExecerV1[]): PaymentExecerV1;
10
+ /**
11
+ * Chooser that selects the cheapest payment option by maxAmountRequired.
12
+ *
13
+ * @param execers - Available payment execers.
14
+ * @returns The execer with the lowest amount.
15
+ * @throws If no options are available.
16
+ */
17
+ export declare function chooseCheapest(execers: PaymentExecerV1[]): PaymentExecerV1;
18
+ /**
19
+ * Chooser that selects the most expensive payment option by maxAmountRequired.
20
+ *
21
+ * @param execers - Available payment execers.
22
+ * @returns The execer with the highest amount.
23
+ * @throws If no options are available.
24
+ */
25
+ export declare function chooseMostExpensive(execers: PaymentExecerV1[]): PaymentExecerV1;
26
+ /**
27
+ * Creates a chooser that selects by asset name.
28
+ *
29
+ * @param asset - Asset name to match (case-insensitive).
30
+ * @returns A chooser function.
31
+ */
32
+ export declare function chooseByAsset(asset: string): (execers: PaymentExecerV1[]) => PaymentExecerV1;
33
+ /**
34
+ * Creates a chooser that selects by network name.
35
+ *
36
+ * @param network - Network name to match (case-insensitive).
37
+ * @returns A chooser function.
38
+ */
39
+ export declare function chooseByNetwork(network: string): (execers: PaymentExecerV1[]) => PaymentExecerV1;
40
+ /**
41
+ * Creates a chooser that selects by payment scheme.
42
+ *
43
+ * @param scheme - Scheme name to match (case-insensitive).
44
+ * @returns A chooser function.
45
+ */
46
+ export declare function chooseByScheme(scheme: string): (execers: PaymentExecerV1[]) => PaymentExecerV1;
47
+ /**
48
+ * Creates a chooser that selects by array index.
49
+ *
50
+ * @param index - Zero-based index of the option to select.
51
+ * @returns A chooser function.
52
+ */
53
+ export declare function chooseByIndex(index: number): (execers: PaymentExecerV1[]) => PaymentExecerV1;
54
+ /**
55
+ * Chooser that always throws, useful for testing "no suitable option" paths.
56
+ *
57
+ * @throws Always throws "No suitable payment option".
58
+ */
9
59
  export declare function chooseNone(): never;
10
- export declare function chooseWithInspection(inspector: (execers: PaymentExecer[]) => void, inner: (execers: PaymentExecer[]) => PaymentExecer): (execers: PaymentExecer[]) => PaymentExecer;
11
- export declare function chooseWithFilter(filter: (execer: PaymentExecer) => boolean, inner: (execers: PaymentExecer[]) => PaymentExecer): (execers: PaymentExecer[]) => PaymentExecer;
60
+ /**
61
+ * Wraps a chooser to inspect options before choosing.
62
+ *
63
+ * @param inspector - Callback to inspect available options.
64
+ * @param inner - Chooser to delegate to after inspection.
65
+ * @returns A chooser that inspects then delegates.
66
+ */
67
+ export declare function chooseWithInspection(inspector: (execers: PaymentExecerV1[]) => void, inner: (execers: PaymentExecerV1[]) => PaymentExecerV1): (execers: PaymentExecerV1[]) => PaymentExecerV1;
68
+ /**
69
+ * Wraps a chooser to filter options before choosing.
70
+ *
71
+ * @param filter - Predicate to filter available options.
72
+ * @param inner - Chooser to delegate to after filtering.
73
+ * @returns A chooser that filters then delegates.
74
+ */
75
+ export declare function chooseWithFilter(filter: (execer: PaymentExecerV1) => boolean, inner: (execers: PaymentExecerV1[]) => PaymentExecerV1): (execers: PaymentExecerV1[]) => PaymentExecerV1;
12
76
  //# sourceMappingURL=choosers.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"choosers.d.ts","sourceRoot":"","sources":["../../src/choosers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAE7D,wBAAgB,WAAW,CAAC,OAAO,EAAE,aAAa,EAAE,GAAG,aAAa,CASnE;AAED,wBAAgB,cAAc,CAAC,OAAO,EAAE,aAAa,EAAE,GAAG,aAAa,CAUtE;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,aAAa,EAAE,GAAG,aAAa,CAU3E;AAED,wBAAgB,aAAa,CAC3B,KAAK,EAAE,MAAM,GACZ,CAAC,OAAO,EAAE,aAAa,EAAE,KAAK,aAAa,CAU7C;AAED,wBAAgB,eAAe,CAC7B,OAAO,EAAE,MAAM,GACd,CAAC,OAAO,EAAE,aAAa,EAAE,KAAK,aAAa,CAU7C;AAED,wBAAgB,cAAc,CAC5B,MAAM,EAAE,MAAM,GACb,CAAC,OAAO,EAAE,aAAa,EAAE,KAAK,aAAa,CAU7C;AAED,wBAAgB,aAAa,CAC3B,KAAK,EAAE,MAAM,GACZ,CAAC,OAAO,EAAE,aAAa,EAAE,KAAK,aAAa,CAU7C;AAED,wBAAgB,UAAU,IAAI,KAAK,CAElC;AAED,wBAAgB,oBAAoB,CAClC,SAAS,EAAE,CAAC,OAAO,EAAE,aAAa,EAAE,KAAK,IAAI,EAC7C,KAAK,EAAE,CAAC,OAAO,EAAE,aAAa,EAAE,KAAK,aAAa,GACjD,CAAC,OAAO,EAAE,aAAa,EAAE,KAAK,aAAa,CAK7C;AAED,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,CAAC,MAAM,EAAE,aAAa,KAAK,OAAO,EAC1C,KAAK,EAAE,CAAC,OAAO,EAAE,aAAa,EAAE,KAAK,aAAa,GACjD,CAAC,OAAO,EAAE,aAAa,EAAE,KAAK,aAAa,CAK7C"}
1
+ {"version":3,"file":"choosers.d.ts","sourceRoot":"","sources":["../../src/choosers.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAE/D;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,eAAe,EAAE,GAAG,eAAe,CASvE;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,eAAe,EAAE,GAAG,eAAe,CAU1E;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,eAAe,EAAE,GACzB,eAAe,CAUjB;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAC3B,KAAK,EAAE,MAAM,GACZ,CAAC,OAAO,EAAE,eAAe,EAAE,KAAK,eAAe,CAUjD;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,MAAM,GACd,CAAC,OAAO,EAAE,eAAe,EAAE,KAAK,eAAe,CAUjD;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE,MAAM,GACb,CAAC,OAAO,EAAE,eAAe,EAAE,KAAK,eAAe,CAUjD;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAC3B,KAAK,EAAE,MAAM,GACZ,CAAC,OAAO,EAAE,eAAe,EAAE,KAAK,eAAe,CAUjD;AAED;;;;GAIG;AACH,wBAAgB,UAAU,IAAI,KAAK,CAElC;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAClC,SAAS,EAAE,CAAC,OAAO,EAAE,eAAe,EAAE,KAAK,IAAI,EAC/C,KAAK,EAAE,CAAC,OAAO,EAAE,eAAe,EAAE,KAAK,eAAe,GACrD,CAAC,OAAO,EAAE,eAAe,EAAE,KAAK,eAAe,CAKjD;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,CAAC,MAAM,EAAE,eAAe,KAAK,OAAO,EAC5C,KAAK,EAAE,CAAC,OAAO,EAAE,eAAe,EAAE,KAAK,eAAe,GACrD,CAAC,OAAO,EAAE,eAAe,EAAE,KAAK,eAAe,CAKjD"}
@@ -1,3 +1,10 @@
1
+ /**
2
+ * Chooser that selects the first available payment option.
3
+ *
4
+ * @param execers - Available payment execers.
5
+ * @returns The first execer in the list.
6
+ * @throws If no options are available.
7
+ */
1
8
  export function chooseFirst(execers) {
2
9
  if (execers.length === 0) {
3
10
  throw new Error("No payment options available");
@@ -8,6 +15,13 @@ export function chooseFirst(execers) {
8
15
  }
9
16
  return first;
10
17
  }
18
+ /**
19
+ * Chooser that selects the cheapest payment option by maxAmountRequired.
20
+ *
21
+ * @param execers - Available payment execers.
22
+ * @returns The execer with the lowest amount.
23
+ * @throws If no options are available.
24
+ */
11
25
  export function chooseCheapest(execers) {
12
26
  if (execers.length === 0) {
13
27
  throw new Error("No payment options available");
@@ -17,6 +31,13 @@ export function chooseCheapest(execers) {
17
31
  ? current
18
32
  : cheapest);
19
33
  }
34
+ /**
35
+ * Chooser that selects the most expensive payment option by maxAmountRequired.
36
+ *
37
+ * @param execers - Available payment execers.
38
+ * @returns The execer with the highest amount.
39
+ * @throws If no options are available.
40
+ */
20
41
  export function chooseMostExpensive(execers) {
21
42
  if (execers.length === 0) {
22
43
  throw new Error("No payment options available");
@@ -26,6 +47,12 @@ export function chooseMostExpensive(execers) {
26
47
  ? current
27
48
  : expensive);
28
49
  }
50
+ /**
51
+ * Creates a chooser that selects by asset name.
52
+ *
53
+ * @param asset - Asset name to match (case-insensitive).
54
+ * @returns A chooser function.
55
+ */
29
56
  export function chooseByAsset(asset) {
30
57
  return (execers) => {
31
58
  const match = execers.find((e) => e.requirements.asset.toLowerCase() === asset.toLowerCase());
@@ -35,6 +62,12 @@ export function chooseByAsset(asset) {
35
62
  return match;
36
63
  };
37
64
  }
65
+ /**
66
+ * Creates a chooser that selects by network name.
67
+ *
68
+ * @param network - Network name to match (case-insensitive).
69
+ * @returns A chooser function.
70
+ */
38
71
  export function chooseByNetwork(network) {
39
72
  return (execers) => {
40
73
  const match = execers.find((e) => e.requirements.network.toLowerCase() === network.toLowerCase());
@@ -44,6 +77,12 @@ export function chooseByNetwork(network) {
44
77
  return match;
45
78
  };
46
79
  }
80
+ /**
81
+ * Creates a chooser that selects by payment scheme.
82
+ *
83
+ * @param scheme - Scheme name to match (case-insensitive).
84
+ * @returns A chooser function.
85
+ */
47
86
  export function chooseByScheme(scheme) {
48
87
  return (execers) => {
49
88
  const match = execers.find((e) => e.requirements.scheme.toLowerCase() === scheme.toLowerCase());
@@ -53,6 +92,12 @@ export function chooseByScheme(scheme) {
53
92
  return match;
54
93
  };
55
94
  }
95
+ /**
96
+ * Creates a chooser that selects by array index.
97
+ *
98
+ * @param index - Zero-based index of the option to select.
99
+ * @returns A chooser function.
100
+ */
56
101
  export function chooseByIndex(index) {
57
102
  return (execers) => {
58
103
  const execer = execers[index];
@@ -62,15 +107,34 @@ export function chooseByIndex(index) {
62
107
  return execer;
63
108
  };
64
109
  }
110
+ /**
111
+ * Chooser that always throws, useful for testing "no suitable option" paths.
112
+ *
113
+ * @throws Always throws "No suitable payment option".
114
+ */
65
115
  export function chooseNone() {
66
116
  throw new Error("No suitable payment option");
67
117
  }
118
+ /**
119
+ * Wraps a chooser to inspect options before choosing.
120
+ *
121
+ * @param inspector - Callback to inspect available options.
122
+ * @param inner - Chooser to delegate to after inspection.
123
+ * @returns A chooser that inspects then delegates.
124
+ */
68
125
  export function chooseWithInspection(inspector, inner) {
69
126
  return (execers) => {
70
127
  inspector(execers);
71
128
  return inner(execers);
72
129
  };
73
130
  }
131
+ /**
132
+ * Wraps a chooser to filter options before choosing.
133
+ *
134
+ * @param filter - Predicate to filter available options.
135
+ * @param inner - Chooser to delegate to after filtering.
136
+ * @returns A chooser that filters then delegates.
137
+ */
74
138
  export function chooseWithFilter(filter, inner) {
75
139
  return (execers) => {
76
140
  const filtered = execers.filter(filter);
@@ -1,8 +1,18 @@
1
1
  import type { FacilitatorHandler } from "@faremeter/types/facilitator";
2
- import type { PaymentHandler } from "@faremeter/types/client";
2
+ import type { PaymentHandlerV1 } from "@faremeter/types/client";
3
3
  import type { x402PaymentRequirements } from "@faremeter/types/x402";
4
+ import type { SupportedVersionsConfig } from "@faremeter/middleware/common";
4
5
  import type { Interceptor } from "../interceptors/types.js";
6
+ /**
7
+ * How the middleware handles payment verification and settlement.
8
+ *
9
+ * - `"settle-only"` - Skip verification, settle directly (faster tests).
10
+ * - `"verify-then-settle"` - Verify payment before settling (more realistic).
11
+ */
5
12
  export type SettleMode = "settle-only" | "verify-then-settle";
13
+ /**
14
+ * Configuration for {@link TestHarness}.
15
+ */
6
16
  export type TestHarnessConfig = {
7
17
  /**
8
18
  * Payment requirements the middleware accepts.
@@ -15,15 +25,21 @@ export type TestHarnessConfig = {
15
25
  */
16
26
  facilitatorHandlers: FacilitatorHandler[];
17
27
  /**
18
- * Client payment handlers.
28
+ * Client payment handlers (v1).
29
+ * Internally adapted to v2 for use with the fetch client.
19
30
  * Multiple handlers can be provided for different schemes.
20
31
  */
21
- clientHandlers: PaymentHandler[];
32
+ clientHandlers: PaymentHandlerV1[];
22
33
  /**
23
34
  * Settlement mode for the middleware.
24
35
  * @default "settle-only"
25
36
  */
26
37
  settleMode?: SettleMode;
38
+ /**
39
+ * Protocol versions the middleware should support.
40
+ * Passed through to middleware without modification.
41
+ */
42
+ supportedVersions?: SupportedVersionsConfig;
27
43
  /**
28
44
  * Interceptors between test code and middleware.
29
45
  * These see all requests from the wrapped fetch to the Hono app.
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../../src/harness/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AACvE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AACrE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAEzD,MAAM,MAAM,UAAU,GAAG,aAAa,GAAG,oBAAoB,CAAC;AAE9D,MAAM,MAAM,iBAAiB,GAAG;IAC9B;;;OAGG;IACH,OAAO,EAAE,OAAO,CAAC,uBAAuB,CAAC,EAAE,CAAC;IAE5C;;;OAGG;IACH,mBAAmB,EAAE,kBAAkB,EAAE,CAAC;IAE1C;;;OAGG;IACH,cAAc,EAAE,cAAc,EAAE,CAAC;IAEjC;;;OAGG;IACH,UAAU,CAAC,EAAE,UAAU,CAAC;IAExB;;;OAGG;IACH,kBAAkB,CAAC,EAAE,WAAW,EAAE,CAAC;IAEnC;;;OAGG;IACH,sBAAsB,CAAC,EAAE,WAAW,EAAE,CAAC;CACxC,CAAC"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../../src/harness/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AACvE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAChE,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AACrE,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AAC5E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAEzD;;;;;GAKG;AACH,MAAM,MAAM,UAAU,GAAG,aAAa,GAAG,oBAAoB,CAAC;AAE9D;;GAEG;AACH,MAAM,MAAM,iBAAiB,GAAG;IAC9B;;;OAGG;IACH,OAAO,EAAE,OAAO,CAAC,uBAAuB,CAAC,EAAE,CAAC;IAE5C;;;OAGG;IACH,mBAAmB,EAAE,kBAAkB,EAAE,CAAC;IAE1C;;;;OAIG;IACH,cAAc,EAAE,gBAAgB,EAAE,CAAC;IAEnC;;;OAGG;IACH,UAAU,CAAC,EAAE,UAAU,CAAC;IAExB;;;OAGG;IACH,iBAAiB,CAAC,EAAE,uBAAuB,CAAC;IAE5C;;;OAGG;IACH,kBAAkB,CAAC,EAAE,WAAW,EAAE,CAAC;IAEnC;;;OAGG;IACH,sBAAsB,CAAC,EAAE,WAAW,EAAE,CAAC;CACxC,CAAC"}
@@ -1,8 +1,15 @@
1
- import type { x402PaymentRequirements } from "@faremeter/types/x402";
1
+ import type { x402PaymentRequirements as x402PaymentRequirementsV1 } from "@faremeter/types/x402";
2
+ import type { x402PaymentRequirements } from "@faremeter/types/x402v2";
2
3
  /**
3
- * Creates a payment requirements object with test defaults.
4
+ * Creates a payment requirements object with test defaults (v1 format).
4
5
  * Override specific fields by passing a partial:
5
6
  * accepts({ maxAmountRequired: "500" })
6
7
  */
7
- export declare function accepts(overrides?: Partial<x402PaymentRequirements>): x402PaymentRequirements;
8
+ export declare function accepts(overrides?: Partial<x402PaymentRequirementsV1>): x402PaymentRequirementsV1;
9
+ /**
10
+ * Creates a payment requirements object with test defaults (v2 format).
11
+ * Override specific fields by passing a partial:
12
+ * acceptsV2({ amount: "500" })
13
+ */
14
+ export declare function acceptsV2(overrides?: Partial<x402PaymentRequirements>): x402PaymentRequirements;
8
15
  //# sourceMappingURL=defaults.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"defaults.d.ts","sourceRoot":"","sources":["../../../src/harness/defaults.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AAErE;;;;GAIG;AACH,wBAAgB,OAAO,CACrB,SAAS,CAAC,EAAE,OAAO,CAAC,uBAAuB,CAAC,GAC3C,uBAAuB,CAazB"}
1
+ {"version":3,"file":"defaults.d.ts","sourceRoot":"","sources":["../../../src/harness/defaults.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,uBAAuB,IAAI,yBAAyB,EAAE,MAAM,uBAAuB,CAAC;AAClG,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AAEvE;;;;GAIG;AACH,wBAAgB,OAAO,CACrB,SAAS,CAAC,EAAE,OAAO,CAAC,yBAAyB,CAAC,GAC7C,yBAAyB,CAa3B;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CACvB,SAAS,CAAC,EAAE,OAAO,CAAC,uBAAuB,CAAC,GAC3C,uBAAuB,CAUzB"}
@@ -1,6 +1,6 @@
1
1
  import { TEST_SCHEME, TEST_NETWORK, TEST_ASSET } from "../scheme/constants.js";
2
2
  /**
3
- * Creates a payment requirements object with test defaults.
3
+ * Creates a payment requirements object with test defaults (v1 format).
4
4
  * Override specific fields by passing a partial:
5
5
  * accepts({ maxAmountRequired: "500" })
6
6
  */
@@ -18,3 +18,19 @@ export function accepts(overrides) {
18
18
  ...overrides,
19
19
  };
20
20
  }
21
+ /**
22
+ * Creates a payment requirements object with test defaults (v2 format).
23
+ * Override specific fields by passing a partial:
24
+ * acceptsV2({ amount: "500" })
25
+ */
26
+ export function acceptsV2(overrides) {
27
+ return {
28
+ scheme: TEST_SCHEME,
29
+ network: TEST_NETWORK,
30
+ amount: "100",
31
+ payTo: "test-receiver",
32
+ maxTimeoutSeconds: 30,
33
+ asset: TEST_ASSET,
34
+ ...overrides,
35
+ };
36
+ }
@@ -1,5 +1,5 @@
1
1
  import { Hono } from "hono";
2
- import type { PaymentExecer } from "@faremeter/types/client";
2
+ import type { PaymentExecerV1 } from "@faremeter/types/client";
3
3
  import type { Interceptor } from "../interceptors/types.js";
4
4
  import type { TestHarnessConfig } from "./config.js";
5
5
  import type { ResourceHandler } from "./resource.js";
@@ -37,7 +37,7 @@ export declare class TestHarness {
37
37
  * Create a fetch function for client->middleware calls.
38
38
  * This applies client interceptors and routes to the Hono app.
39
39
  */
40
- private createClientFetch;
40
+ createClientFetch(): typeof fetch;
41
41
  /**
42
42
  * Set the resource handler that responds after successful payment.
43
43
  */
@@ -45,10 +45,12 @@ export declare class TestHarness {
45
45
  /**
46
46
  * Create a fetch function that handles the full x402 payment flow.
47
47
  *
48
- * @param opts.payerChooser - Function to choose which payment option to use
48
+ * @param opts.payerChooser - Function to choose which payment option to use.
49
+ * Receives v1 PaymentExecerV1[] for compatibility with v1 protocol tests.
50
+ * The chosen execer is converted back to v2 internally.
49
51
  */
50
52
  createFetch(opts?: {
51
- payerChooser?: ((execers: PaymentExecer[]) => PaymentExecer | Promise<PaymentExecer>) | undefined;
53
+ payerChooser?: ((execers: PaymentExecerV1[]) => PaymentExecerV1 | Promise<PaymentExecerV1>) | undefined;
52
54
  }): typeof fetch;
53
55
  /**
54
56
  * Add an interceptor to the client chain (between test code and middleware).
@@ -66,5 +68,9 @@ export declare class TestHarness {
66
68
  * Reset harness state (interceptors, resource handler).
67
69
  */
68
70
  reset(): void;
71
+ /**
72
+ * Handle protocol body callback for both v1 and v2.
73
+ */
74
+ private handleBody;
69
75
  }
70
76
  //# sourceMappingURL=harness.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"harness.d.ts","sourceRoot":"","sources":["../../../src/harness/harness.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAQ5B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAQ7D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAGzD,OAAO,KAAK,EAAE,iBAAiB,EAAc,MAAM,UAAU,CAAC;AAC9D,OAAO,KAAK,EAAE,eAAe,EAAmB,MAAM,YAAY,CAAC;AAanE;;;;;GAKG;AACH,qBAAa,WAAW;IACtB;;OAEG;IACH,QAAQ,CAAC,GAAG,EAAE,IAAI,CAAC;IAEnB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAoB;IAC3C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAa;IACxC,OAAO,CAAC,eAAe,CAA2C;IAClE,OAAO,CAAC,kBAAkB,CAAqB;IAC/C,OAAO,CAAC,sBAAsB,CAAqB;IAEnD;;OAEG;IACH,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAyB;gBAErC,MAAM,EAAE,iBAAiB;IASrC;;OAEG;IACH,OAAO,CAAC,SAAS;IAiGjB;;;OAGG;IACH,OAAO,CAAC,qBAAqB;IAe7B;;;OAGG;IACH,OAAO,CAAC,iBAAiB;IAmBzB;;OAEG;IACH,kBAAkB,CAAC,OAAO,EAAE,eAAe,GAAG,IAAI;IAIlD;;;;OAIG;IACH,WAAW,CAAC,IAAI,CAAC,EAAE;QACjB,YAAY,CAAC,EACT,CAAC,CAAC,OAAO,EAAE,aAAa,EAAE,KAAK,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC,GACtE,SAAS,CAAC;KACf,GAAG,OAAO,KAAK;IAgBhB;;OAEG;IACH,oBAAoB,CAAC,WAAW,EAAE,WAAW,GAAG,IAAI;IAIpD;;OAEG;IACH,wBAAwB,CAAC,WAAW,EAAE,WAAW,GAAG,IAAI;IAIxD;;OAEG;IACH,iBAAiB,IAAI,IAAI;IAOzB;;OAEG;IACH,KAAK,IAAI,IAAI;CAId"}
1
+ {"version":3,"file":"harness.d.ts","sourceRoot":"","sources":["../../../src/harness/harness.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,IAAI,EAAgB,MAAM,MAAM,CAAC;AAa1C,OAAO,KAAK,EAAiB,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAI9E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAGzD,OAAO,KAAK,EAAE,iBAAiB,EAAc,MAAM,UAAU,CAAC;AAC9D,OAAO,KAAK,EACV,eAAe,EAIhB,MAAM,YAAY,CAAC;AAYpB;;;;;GAKG;AACH,qBAAa,WAAW;IACtB;;OAEG;IACH,QAAQ,CAAC,GAAG,EAAE,IAAI,CAAC;IAEnB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAoB;IAC3C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAa;IACxC,OAAO,CAAC,eAAe,CAA2C;IAClE,OAAO,CAAC,kBAAkB,CAAqB;IAC/C,OAAO,CAAC,sBAAsB,CAAqB;IAEnD;;OAEG;IACH,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAyB;gBAErC,MAAM,EAAE,iBAAiB;IASrC;;OAEG;IACH,OAAO,CAAC,SAAS;IAgEjB;;;OAGG;IACH,OAAO,CAAC,qBAAqB;IAe7B;;;OAGG;IACH,iBAAiB,IAAI,OAAO,KAAK;IAmBjC;;OAEG;IACH,kBAAkB,CAAC,OAAO,EAAE,eAAe,GAAG,IAAI;IAIlD;;;;;;OAMG;IACH,WAAW,CAAC,IAAI,CAAC,EAAE;QACjB,YAAY,CAAC,EACT,CAAC,CACC,OAAO,EAAE,eAAe,EAAE,KACvB,eAAe,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC,GAChD,SAAS,CAAC;KACf,GAAG,OAAO,KAAK;IA+DhB;;OAEG;IACH,oBAAoB,CAAC,WAAW,EAAE,WAAW,GAAG,IAAI;IAIpD;;OAEG;IACH,wBAAwB,CAAC,WAAW,EAAE,WAAW,GAAG,IAAI;IAIxD;;OAEG;IACH,iBAAiB,IAAI,IAAI;IAOzB;;OAEG;IACH,KAAK,IAAI,IAAI;IAKb;;OAEG;YACW,UAAU;CA2DzB"}
@@ -1,10 +1,21 @@
1
+ /* eslint-disable @typescript-eslint/no-deprecated -- v1 test harness uses v1 types */
2
+ /* eslint-disable @typescript-eslint/unbound-method -- exec is a function property, not a method */
1
3
  import { Hono } from "hono";
2
4
  import { createFacilitatorRoutes } from "@faremeter/facilitator";
3
5
  import { wrap } from "@faremeter/fetch";
4
- import { handleMiddlewareRequest, getPaymentRequiredResponse, } from "@faremeter/middleware/common";
6
+ import { handleMiddlewareRequest, getPaymentRequiredResponse, getPaymentRequiredResponseV2, resolveSupportedVersions, } from "@faremeter/middleware/common";
7
+ import { adaptPaymentHandlerV1ToV2 } from "@faremeter/types/client";
8
+ import { adaptRequirementsV2ToV1 } from "@faremeter/types/x402-adapters";
9
+ import { normalizeNetworkId } from "@faremeter/info";
5
10
  import { composeInterceptors } from "../interceptors/types.js";
6
11
  import { getURLFromRequestInfo } from "../interceptors/utils.js";
7
12
  import { defaultResourceHandler } from "./resource.js";
13
+ /**
14
+ * Type guard to check if a middleware context is v1.
15
+ */
16
+ function isV1Context(context) {
17
+ return context.protocolVersion === 1;
18
+ }
8
19
  /**
9
20
  * TestHarness provides an in-process test environment for the x402 protocol.
10
21
  *
@@ -54,61 +65,30 @@ export class TestHarness {
54
65
  // 1. Applies middleware interceptors
55
66
  // 2. Routes to the Hono app
56
67
  const middlewareFetch = this.createMiddlewareFetch();
57
- // Track state for resource handler
58
- const state = {};
59
68
  const result = await handleMiddlewareRequest({
60
69
  facilitatorURL: `${this.baseUrl}/facilitator`,
61
70
  accepts: this.config.accepts,
71
+ supportedVersions: resolveSupportedVersions(this.config.supportedVersions),
62
72
  resource: c.req.url,
63
73
  fetch: middlewareFetch,
64
74
  getHeader: (key) => c.req.header(key),
75
+ setResponseHeader: (key, value) => c.header(key, value),
65
76
  getPaymentRequiredResponse,
66
- sendJSONResponse: (status, body) => {
77
+ getPaymentRequiredResponseV2,
78
+ sendJSONResponse: (status, body, headers) => {
67
79
  c.status(status);
68
- return c.json(body);
69
- },
70
- body: async (context) => {
71
- const { paymentRequirements, paymentPayload, settle, verify } = context;
72
- // Store for resource handler
73
- state.paymentRequirements = paymentRequirements;
74
- state.paymentPayload = paymentPayload;
75
- if (this.settleMode === "verify-then-settle") {
76
- const verifyResult = await verify();
77
- if (!verifyResult.success) {
78
- return verifyResult.errorResponse;
79
- }
80
- state.verifyResponse = verifyResult.facilitatorResponse;
81
- const settleResult = await settle();
82
- if (!settleResult.success) {
83
- return settleResult.errorResponse;
84
- }
85
- state.settleResponse = settleResult.facilitatorResponse;
86
- }
87
- else {
88
- const settleResult = await settle();
89
- if (!settleResult.success) {
90
- return settleResult.errorResponse;
91
- }
92
- state.settleResponse = settleResult.facilitatorResponse;
93
- }
94
- // Payment successful - call resource handler
95
- const ctx = {
96
- resource: c.req.url,
97
- request: c.req.raw,
98
- paymentRequirements,
99
- paymentPayload,
100
- settleResponse: state.settleResponse,
101
- verifyResponse: state.verifyResponse,
102
- };
103
- const resourceResult = await this.resourceHandler(ctx);
104
- // Set response headers
105
- if (resourceResult.headers) {
106
- for (const [key, value] of Object.entries(resourceResult.headers)) {
80
+ if (headers) {
81
+ for (const [key, value] of Object.entries(headers)) {
107
82
  c.header(key, value);
108
83
  }
109
84
  }
110
- c.status(resourceResult.status);
111
- return c.json(resourceResult.body);
85
+ if (body) {
86
+ return c.json(body);
87
+ }
88
+ return c.body(null);
89
+ },
90
+ body: async (context) => {
91
+ return this.handleBody(c, context);
112
92
  },
113
93
  });
114
94
  return result;
@@ -160,16 +140,54 @@ export class TestHarness {
160
140
  /**
161
141
  * Create a fetch function that handles the full x402 payment flow.
162
142
  *
163
- * @param opts.payerChooser - Function to choose which payment option to use
143
+ * @param opts.payerChooser - Function to choose which payment option to use.
144
+ * Receives v1 PaymentExecerV1[] for compatibility with v1 protocol tests.
145
+ * The chosen execer is converted back to v2 internally.
164
146
  */
165
147
  createFetch(opts) {
166
148
  const clientFetch = this.createClientFetch();
167
149
  const payerChooser = opts?.payerChooser;
150
+ // Adapt v1 handlers to v2 for use with the fetch client
151
+ const v2Handlers = this.config.clientHandlers.map((h) => adaptPaymentHandlerV1ToV2(h, normalizeNetworkId));
168
152
  return wrap(clientFetch, {
169
- handlers: this.config.clientHandlers,
153
+ handlers: v2Handlers,
170
154
  ...(payerChooser
171
155
  ? {
172
- payerChooser: async (execers) => payerChooser(execers),
156
+ payerChooser: async (v2Execers) => {
157
+ // Convert v2 execers to v1 for the callback
158
+ const v1Execers = v2Execers.map((e) => ({
159
+ requirements: adaptRequirementsV2ToV1(e.requirements, {
160
+ url: "",
161
+ }),
162
+ exec: e.exec,
163
+ }));
164
+ // Let the callback choose using v1 types
165
+ const chosenV1 = await payerChooser(v1Execers);
166
+ // Find the corresponding v2 execer by matching requirements
167
+ const chosenIndex = v1Execers.indexOf(chosenV1);
168
+ if (chosenIndex >= 0) {
169
+ const v2Execer = v2Execers[chosenIndex];
170
+ if (v2Execer) {
171
+ return v2Execer;
172
+ }
173
+ }
174
+ // Fallback: wrap the v1 execer's exec with the first matching v2 requirements
175
+ const matchingV2 = v2Execers.find((e) => e.requirements.scheme === chosenV1.requirements.scheme &&
176
+ e.requirements.network === chosenV1.requirements.network &&
177
+ e.requirements.asset === chosenV1.requirements.asset);
178
+ if (matchingV2) {
179
+ return {
180
+ requirements: matchingV2.requirements,
181
+ exec: chosenV1.exec,
182
+ };
183
+ }
184
+ // Last resort: return first v2 execer with the chosen exec
185
+ const first = v2Execers[0];
186
+ if (!first) {
187
+ throw new Error("No execers available");
188
+ }
189
+ return { requirements: first.requirements, exec: chosenV1.exec };
190
+ },
173
191
  }
174
192
  : {}),
175
193
  });
@@ -202,4 +220,62 @@ export class TestHarness {
202
220
  this.clearInterceptors();
203
221
  this.resourceHandler = defaultResourceHandler;
204
222
  }
223
+ /**
224
+ * Handle protocol body callback for both v1 and v2.
225
+ */
226
+ async handleBody(c, context) {
227
+ let ctx;
228
+ if (isV1Context(context)) {
229
+ let verifyResponse;
230
+ if (this.settleMode === "verify-then-settle") {
231
+ const verifyResult = await context.verify();
232
+ if (!verifyResult.success)
233
+ return verifyResult.errorResponse;
234
+ verifyResponse = verifyResult.facilitatorResponse;
235
+ }
236
+ const settleResult = await context.settle();
237
+ if (!settleResult.success)
238
+ return settleResult.errorResponse;
239
+ ctx = {
240
+ protocolVersion: 1,
241
+ resource: c.req.url,
242
+ request: c.req.raw,
243
+ paymentRequirements: context.paymentRequirements,
244
+ paymentPayload: context.paymentPayload,
245
+ settleResponse: settleResult.facilitatorResponse,
246
+ verifyResponse,
247
+ };
248
+ }
249
+ else {
250
+ let verifyResponse;
251
+ if (this.settleMode === "verify-then-settle") {
252
+ const verifyResult = await context.verify();
253
+ if (!verifyResult.success)
254
+ return verifyResult.errorResponse;
255
+ verifyResponse = verifyResult.facilitatorResponse;
256
+ }
257
+ const settleResult = await context.settle();
258
+ if (!settleResult.success)
259
+ return settleResult.errorResponse;
260
+ ctx = {
261
+ protocolVersion: 2,
262
+ resource: c.req.url,
263
+ request: c.req.raw,
264
+ paymentRequirements: context.paymentRequirements,
265
+ paymentPayload: context.paymentPayload,
266
+ settleResponse: settleResult.facilitatorResponse,
267
+ verifyResponse,
268
+ };
269
+ }
270
+ const resourceResult = await this.resourceHandler(ctx);
271
+ if (resourceResult.headers) {
272
+ for (const [key, value] of Object.entries(resourceResult.headers)) {
273
+ c.header(key, value);
274
+ }
275
+ }
276
+ // Hono's typed API requires literal status codes and typed json bodies.
277
+ // These assertions satisfy the framework's type constraints.
278
+ c.status(resourceResult.status);
279
+ return c.json(resourceResult.body);
280
+ }
205
281
  }
@@ -141,8 +141,9 @@ await t.test("interceptor composition", async (t) => {
141
141
  if (matchFacilitatorSettle(url, init)) {
142
142
  return new Response(JSON.stringify({
143
143
  success: true,
144
- txHash: "0xshortcircuit",
145
- networkId: TEST_NETWORK,
144
+ transaction: "0xshortcircuit",
145
+ network: TEST_NETWORK,
146
+ payer: "test-payer",
146
147
  }), {
147
148
  status: 200,
148
149
  headers: { "Content-Type": "application/json" },
@@ -1,17 +1,51 @@
1
- import type { x402PaymentRequirements, x402PaymentPayload, x402SettleResponse, x402VerifyResponse } from "@faremeter/types/x402";
2
- export type ResourceContext = {
1
+ import type { x402PaymentRequirements as x402PaymentRequirementsV1, x402PaymentPayload as x402PaymentPayloadV1, x402SettleResponse as x402SettleResponseV1, x402VerifyResponse as x402VerifyResponseV1 } from "@faremeter/types/x402";
2
+ import type { x402PaymentRequirements, x402PaymentPayload, x402SettleResponse, x402VerifyResponse } from "@faremeter/types/x402v2";
3
+ /**
4
+ * Common fields shared between v1 and v2 resource contexts.
5
+ */
6
+ type ResourceContextBase = {
3
7
  resource: string;
4
8
  request: Request;
9
+ };
10
+ /**
11
+ * Resource context for v1 protocol.
12
+ */
13
+ export type ResourceContextV1 = ResourceContextBase & {
14
+ protocolVersion: 1;
15
+ paymentRequirements: x402PaymentRequirementsV1;
16
+ paymentPayload: x402PaymentPayloadV1;
17
+ settleResponse: x402SettleResponseV1;
18
+ verifyResponse?: x402VerifyResponseV1 | undefined;
19
+ };
20
+ /**
21
+ * Resource context for v2 protocol.
22
+ */
23
+ export type ResourceContextV2 = ResourceContextBase & {
24
+ protocolVersion: 2;
5
25
  paymentRequirements: x402PaymentRequirements;
6
26
  paymentPayload: x402PaymentPayload;
7
27
  settleResponse: x402SettleResponse;
8
28
  verifyResponse?: x402VerifyResponse | undefined;
9
29
  };
30
+ /**
31
+ * Resource context passed to the resource handler after successful payment.
32
+ * Discriminated union based on protocolVersion.
33
+ */
34
+ export type ResourceContext = ResourceContextV1 | ResourceContextV2;
10
35
  export type ResourceResult = {
11
36
  status: number;
12
37
  body: unknown;
13
38
  headers?: Record<string, string>;
14
39
  };
15
40
  export type ResourceHandler = (ctx: ResourceContext) => ResourceResult | Promise<ResourceResult>;
41
+ /**
42
+ * Type guard to check if context is v1.
43
+ */
44
+ export declare function isResourceContextV1(ctx: ResourceContext): ctx is ResourceContextV1;
45
+ /**
46
+ * Type guard to check if context is v2.
47
+ */
48
+ export declare function isResourceContextV2(ctx: ResourceContext): ctx is ResourceContextV2;
16
49
  export declare const defaultResourceHandler: ResourceHandler;
50
+ export {};
17
51
  //# sourceMappingURL=resource.d.ts.map