@frontmcp/testing 1.2.1 → 1.4.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@frontmcp/testing",
3
- "version": "1.2.1",
3
+ "version": "1.4.0",
4
4
  "description": "E2E testing framework for FrontMCP servers - MCP client, auth mocks, Playwright integration",
5
5
  "author": "AgentFront <info@agentfront.dev>",
6
6
  "homepage": "https://docs.agentfront.dev",
@@ -87,12 +87,12 @@
87
87
  "./esm": null
88
88
  },
89
89
  "peerDependencies": {
90
- "@frontmcp/sdk": "1.2.1",
91
- "@frontmcp/ui": "1.2.1",
90
+ "@frontmcp/sdk": "1.4.0",
91
+ "@frontmcp/ui": "1.4.0",
92
92
  "@playwright/test": "^1.40.0",
93
93
  "jest": "^29.0.0 || ^30.0.0",
94
94
  "@jest/globals": "^29.0.0 || ^30.0.0",
95
- "@frontmcp/lazy-zod": "1.2.1"
95
+ "@frontmcp/lazy-zod": "1.4.0"
96
96
  },
97
97
  "peerDependenciesMeta": {
98
98
  "@frontmcp/ui": {
@@ -112,8 +112,8 @@
112
112
  "node": ">=24.0.0"
113
113
  },
114
114
  "dependencies": {
115
- "@frontmcp/utils": "1.2.1",
116
- "@frontmcp/protocol": "1.2.1",
115
+ "@frontmcp/utils": "1.4.0",
116
+ "@frontmcp/protocol": "1.4.0",
117
117
  "jose": "^6.0.11",
118
118
  "tslib": "^2.3.0"
119
119
  },
package/perf/index.js CHANGED
@@ -2911,15 +2911,39 @@ var TestTokenFactory = class {
2911
2911
  publicKey = null;
2912
2912
  jwk = null;
2913
2913
  keyId;
2914
+ /** HS256 shared secret (gateway mode). Null → RS256 + JWKS (default). */
2915
+ hmacSecret;
2914
2916
  constructor(options = {}) {
2915
2917
  this.issuer = options.issuer ?? "https://test.frontmcp.local";
2916
2918
  this.audience = options.audience ?? "frontmcp-test";
2917
2919
  this.keyId = `test-key-${Date.now()}`;
2920
+ this.hmacSecret = options.hmacSecret ? new TextEncoder().encode(options.hmacSecret) : null;
2921
+ }
2922
+ /** True when this factory signs HS256 with a shared secret (gateway mode). */
2923
+ get isHmac() {
2924
+ return this.hmacSecret !== null;
2925
+ }
2926
+ /** The JWS `alg` this factory signs with. */
2927
+ get alg() {
2928
+ return this.isHmac ? "HS256" : "RS256";
2929
+ }
2930
+ /**
2931
+ * The signing key: the shared HMAC secret in gateway mode, otherwise the
2932
+ * generated RSA private key (lazily created via ensureKeys()).
2933
+ */
2934
+ signingKey() {
2935
+ if (this.hmacSecret) return this.hmacSecret;
2936
+ if (!this.privateKey) {
2937
+ throw new Error("Private key not initialized");
2938
+ }
2939
+ return this.privateKey;
2918
2940
  }
2919
2941
  /**
2920
- * Initialize the key pair (called automatically on first use)
2942
+ * Initialize the key pair (called automatically on first use). No-op in HS256
2943
+ * mode — there is no asymmetric keypair, just the shared secret.
2921
2944
  */
2922
2945
  async ensureKeys() {
2946
+ if (this.isHmac) return;
2923
2947
  if (this.privateKey && this.publicKey) return;
2924
2948
  const { publicKey, privateKey } = await (0, import_jose.generateKeyPair)("RS256", {
2925
2949
  extractable: true
@@ -2947,10 +2971,8 @@ var TestTokenFactory = class {
2947
2971
  scope: options.scopes?.join(" "),
2948
2972
  ...options.claims
2949
2973
  };
2950
- if (!this.privateKey) {
2951
- throw new Error("Private key not initialized");
2952
- }
2953
- const token = await new import_jose.SignJWT(payload).setProtectedHeader({ alg: "RS256", kid: this.keyId }).sign(this.privateKey);
2974
+ const header = this.isHmac ? { alg: this.alg, typ: "JWT" } : { alg: this.alg, kid: this.keyId };
2975
+ const token = await new import_jose.SignJWT(payload).setProtectedHeader(header).sign(this.signingKey());
2954
2976
  return token;
2955
2977
  }
2956
2978
  /**
@@ -3009,10 +3031,8 @@ var TestTokenFactory = class {
3009
3031
  exp: now - 3600
3010
3032
  // Expired 1 hour ago
3011
3033
  };
3012
- if (!this.privateKey) {
3013
- throw new Error("Private key not initialized");
3014
- }
3015
- const token = await new import_jose.SignJWT(payload).setProtectedHeader({ alg: "RS256", kid: this.keyId }).sign(this.privateKey);
3034
+ const header = this.isHmac ? { alg: this.alg, typ: "JWT" } : { alg: this.alg, kid: this.keyId };
3035
+ const token = await new import_jose.SignJWT(payload).setProtectedHeader(header).sign(this.signingKey());
3016
3036
  return token;
3017
3037
  }
3018
3038
  /**
@@ -3020,7 +3040,8 @@ var TestTokenFactory = class {
3020
3040
  */
3021
3041
  createTokenWithInvalidSignature(options) {
3022
3042
  const now = Math.floor(Date.now() / 1e3);
3023
- const header = Buffer.from(JSON.stringify({ alg: "RS256", kid: this.keyId })).toString("base64url");
3043
+ const headerObj = this.isHmac ? { alg: this.alg, typ: "JWT" } : { alg: this.alg, kid: this.keyId };
3044
+ const header = Buffer.from(JSON.stringify(headerObj)).toString("base64url");
3024
3045
  const payload = Buffer.from(
3025
3046
  JSON.stringify({
3026
3047
  iss: this.issuer,
@@ -3034,9 +3055,15 @@ var TestTokenFactory = class {
3034
3055
  return `${header}.${payload}.${signature}`;
3035
3056
  }
3036
3057
  /**
3037
- * Get the public JWKS for verifying tokens
3058
+ * Get the public JWKS for verifying tokens.
3059
+ *
3060
+ * Only meaningful in RS256 mode — HS256 (gateway) tokens are verified against
3061
+ * the shared secret, not a published public key, so there is no JWKS to serve.
3038
3062
  */
3039
3063
  async getPublicJwks() {
3064
+ if (this.isHmac) {
3065
+ throw new Error("getPublicJwks() is not available in HS256 mode (gateway tokens use a shared secret)");
3066
+ }
3040
3067
  await this.ensureKeys();
3041
3068
  if (!this.jwk) {
3042
3069
  throw new Error("JWK not initialized");
@@ -3098,6 +3125,7 @@ var E2E_PORT_RANGES = {
3098
3125
  "demo-e2e-orchestrated": { start: 50100, size: 10 },
3099
3126
  "demo-e2e-transparent": { start: 50110, size: 10 },
3100
3127
  "demo-e2e-cimd": { start: 50120, size: 10 },
3128
+ "demo-e2e-remote-proxy": { start: 50130, size: 10 },
3101
3129
  // Feature E2E tests (50200-50299)
3102
3130
  "demo-e2e-skills": { start: 50200, size: 10 },
3103
3131
  "demo-e2e-remote": { start: 50210, size: 10 },
@@ -4,4 +4,5 @@
4
4
  */
5
5
  export { Client as McpClient } from '@frontmcp/protocol';
6
6
  export { StdioClientTransport as McpStdioClientTransport } from '@frontmcp/protocol';
7
+ export { isTcpPortListening } from './port-probe';
7
8
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/raw-client/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,MAAM,IAAI,SAAS,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,oBAAoB,IAAI,uBAAuB,EAAE,MAAM,oBAAoB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/raw-client/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,MAAM,IAAI,SAAS,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,oBAAoB,IAAI,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AACrF,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Resolve `true` iff a TCP server is currently accepting connections on
3
+ * `host:port`, otherwise `false`.
4
+ *
5
+ * A refused connection (`ECONNREFUSED`) or a timeout resolves to `false`; a
6
+ * successful connect resolves to `true` and the probe socket is closed
7
+ * immediately. Probing loopback makes "nothing listening" fail fast, so the
8
+ * timeout only guards against a silently-dropped connection.
9
+ *
10
+ * @param port - TCP port to probe
11
+ * @param host - Host/interface to probe (default `127.0.0.1`)
12
+ * @param timeoutMs - Max time to wait for a connection (default `1500`)
13
+ */
14
+ export declare function isTcpPortListening(port: number, host?: string, timeoutMs?: number): Promise<boolean>;
15
+ //# sourceMappingURL=port-probe.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"port-probe.d.ts","sourceRoot":"","sources":["../../src/raw-client/port-probe.ts"],"names":[],"mappings":"AAQA;;;;;;;;;;;;GAYG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,SAAc,EAAE,SAAS,SAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAoBvG"}
@@ -67,6 +67,10 @@ export declare const E2E_PORT_RANGES: {
67
67
  readonly start: 50120;
68
68
  readonly size: 10;
69
69
  };
70
+ readonly 'demo-e2e-remote-proxy': {
71
+ readonly start: 50130;
72
+ readonly size: 10;
73
+ };
70
74
  readonly 'demo-e2e-skills': {
71
75
  readonly start: 50200;
72
76
  readonly size: 10;
@@ -1 +1 @@
1
- {"version":3,"file":"port-registry.d.ts","sourceRoot":"","sources":["../../src/server/port-registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAQH;;;;;;GAMG;AACH,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0DlB,CAAC;AAEX,MAAM,MAAM,UAAU,GAAG,MAAM,OAAO,eAAe,CAAC;AAuBtD;;GAEG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAM7E;AAED;;;;;;;;;GASG;AACH,wBAAsB,WAAW,CAC/B,OAAO,EAAE,MAAM,EACf,aAAa,CAAC,EAAE,MAAM,GACrB,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,CAAC,CAiEzD;AAkFD;;GAEG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAGtD;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAWxE;AAED;;GAEG;AACH,wBAAsB,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC,CAGrD;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAAC,CAM/F"}
1
+ {"version":3,"file":"port-registry.d.ts","sourceRoot":"","sources":["../../src/server/port-registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAQH;;;;;;GAMG;AACH,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2DlB,CAAC;AAEX,MAAM,MAAM,UAAU,GAAG,MAAM,OAAO,eAAe,CAAC;AAuBtD;;GAEG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAM7E;AAED;;;;;;;;;GASG;AACH,wBAAsB,WAAW,CAC/B,OAAO,EAAE,MAAM,EACf,aAAa,CAAC,EAAE,MAAM,GACrB,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,CAAC,CAiEzD;AAkFD;;GAEG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAGtD;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAWxE;AAED;;GAEG;AACH,wBAAsB,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC,CAGrD;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAAC,CAM/F"}