@hmcts/opal-frontend-common-node 0.0.13 → 0.0.14

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,7 +1,7 @@
1
1
  {
2
2
  "name": "@hmcts/opal-frontend-common-node",
3
3
  "type": "module",
4
- "version": "0.0.13",
4
+ "version": "0.0.14",
5
5
  "license": "MIT",
6
6
  "description": "Common nodejs library components for opal",
7
7
  "main": "dist/index",
@@ -45,8 +45,8 @@
45
45
  "@types/luxon": "^3.4.2",
46
46
  "@types/node": "^22.0.0",
47
47
  "@types/session-file-store": "^1.2.5",
48
- "@typescript-eslint/eslint-plugin": "8.44.0",
49
- "@typescript-eslint/parser": "8.44.0",
48
+ "@typescript-eslint/eslint-plugin": "8.44.1",
49
+ "@typescript-eslint/parser": "8.44.1",
50
50
  "eslint": "^9.0.0",
51
51
  "eslint-plugin-prettier": "^5.2.6",
52
52
  "typescript": "~5.9.0",
@@ -1,13 +1,27 @@
1
1
  import { Request, Response } from 'express';
2
2
  /**
3
- * Express middleware to check if the user is authenticated via SSO.
3
+ * Express handler that verifies whether the current session has a valid (non-expired) access token.
4
4
  *
5
- * Sets appropriate cache control headers to prevent caching of sensitive authentication responses.
6
- * Reads the access token from the session and checks if it is present and not expired.
7
- * Responds with HTTP 401 and `false` if the token is missing or expired, otherwise responds with HTTP 200 and `true`.
5
+ * Behavior:
6
+ * - Reads the access token from `req.session.securityToken?.access_token`.
7
+ * - Prevents caching of the endpoint by setting the `Cache-Control: no-store, must-revalidate` header.
8
+ * - If the token is missing or expired (determined by `Jwt.isJwtExpired`), responds with HTTP 401 and sends `false`.
9
+ * - If the token is present and not expired, responds with HTTP 200 and sends `true`.
8
10
  *
9
- * @param req - Express request object, expected to have a session with a securityToken containing an access_token.
10
- * @param res - Express response object used to send the authentication status.
11
+ * @param req - Express Request; expected to contain `session.securityToken?.access_token` (string).
12
+ * @param res - Express Response used to set headers, status code, and send a boolean result.
13
+ * @returns void
14
+ *
15
+ * @remarks
16
+ * This endpoint is a lightweight authentication status check and has the side-effect of modifying response headers
17
+ * and sending an HTTP status and boolean payload. It relies on `Jwt.isJwtExpired` for token expiry checks.
18
+ *
19
+ * @example
20
+ * // GET /sso/authenticated
21
+ * // -> 200 true (valid token)
22
+ * // -> 401 false (missing or expired token)
23
+ *
24
+ * @see Jwt.isJwtExpired
11
25
  */
12
26
  declare const _default: (req: Request, res: Response) => void;
13
27
  export default _default;
@@ -1 +1 @@
1
- {"version":3,"file":"sso-authenticated.d.ts","sourceRoot":"","sources":["../../src/sso/sso-authenticated.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAG5C;;;;;;;;;GASG;yBACa,KAAK,OAAO,EAAE,KAAK,QAAQ;AAA3C,wBAcE"}
1
+ {"version":3,"file":"sso-authenticated.d.ts","sourceRoot":"","sources":["../../src/sso/sso-authenticated.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAG5C;;;;;;;;;;;;;;;;;;;;;;;GAuBG;yBACa,KAAK,OAAO,EAAE,KAAK,QAAQ;AAA3C,wBAWE"}
@@ -1,22 +1,33 @@
1
1
  import { Jwt } from '../utils';
2
2
  /**
3
- * Express middleware to check if the user is authenticated via SSO.
3
+ * Express handler that verifies whether the current session has a valid (non-expired) access token.
4
4
  *
5
- * Sets appropriate cache control headers to prevent caching of sensitive authentication responses.
6
- * Reads the access token from the session and checks if it is present and not expired.
7
- * Responds with HTTP 401 and `false` if the token is missing or expired, otherwise responds with HTTP 200 and `true`.
5
+ * Behavior:
6
+ * - Reads the access token from `req.session.securityToken?.access_token`.
7
+ * - Prevents caching of the endpoint by setting the `Cache-Control: no-store, must-revalidate` header.
8
+ * - If the token is missing or expired (determined by `Jwt.isJwtExpired`), responds with HTTP 401 and sends `false`.
9
+ * - If the token is present and not expired, responds with HTTP 200 and sends `true`.
8
10
  *
9
- * @param req - Express request object, expected to have a session with a securityToken containing an access_token.
10
- * @param res - Express response object used to send the authentication status.
11
+ * @param req - Express Request; expected to contain `session.securityToken?.access_token` (string).
12
+ * @param res - Express Response used to set headers, status code, and send a boolean result.
13
+ * @returns void
14
+ *
15
+ * @remarks
16
+ * This endpoint is a lightweight authentication status check and has the side-effect of modifying response headers
17
+ * and sending an HTTP status and boolean payload. It relies on `Jwt.isJwtExpired` for token expiry checks.
18
+ *
19
+ * @example
20
+ * // GET /sso/authenticated
21
+ * // -> 200 true (valid token)
22
+ * // -> 401 false (missing or expired token)
23
+ *
24
+ * @see Jwt.isJwtExpired
11
25
  */
12
26
  export default (req, res) => {
13
- res.set('Cache-Control', 'no-store, no-cache, private');
14
- res.set('Pragma', 'no-cache');
15
- // Read the access token from the session (set during the SSO login callback).
16
27
  const accessToken = req.session.securityToken?.access_token;
17
- // Validate expiry without decoding secrets; returns true when token is missing or expired.
18
- const isJwtExpired = Jwt.isJwtExpired(accessToken);
19
- if (!accessToken || isJwtExpired) {
28
+ // Don't allow caching of this endpoint
29
+ res.header('Cache-Control', 'no-store, must-revalidate');
30
+ if (!accessToken || Jwt.isJwtExpired(accessToken)) {
20
31
  res.status(401).send(false);
21
32
  }
22
33
  else {
@@ -1 +1 @@
1
- {"version":3,"file":"sso-authenticated.js","sourceRoot":"","sources":["../../src/sso/sso-authenticated.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAE/B;;;;;;;;;GASG;AACH,eAAe,CAAC,GAAY,EAAE,GAAa,EAAE,EAAE;IAC7C,GAAG,CAAC,GAAG,CAAC,eAAe,EAAE,6BAA6B,CAAC,CAAC;IACxD,GAAG,CAAC,GAAG,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;IAE9B,8EAA8E;IAC9E,MAAM,WAAW,GAAG,GAAG,CAAC,OAAO,CAAC,aAAa,EAAE,YAAY,CAAC;IAC5D,2FAA2F;IAC3F,MAAM,YAAY,GAAG,GAAG,CAAC,YAAY,CAAC,WAAW,CAAC,CAAC;IAEnD,IAAI,CAAC,WAAW,IAAI,YAAY,EAAE,CAAC;QACjC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC9B,CAAC;SAAM,CAAC;QACN,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC7B,CAAC;AACH,CAAC,CAAC"}
1
+ {"version":3,"file":"sso-authenticated.js","sourceRoot":"","sources":["../../src/sso/sso-authenticated.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAE/B;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,eAAe,CAAC,GAAY,EAAE,GAAa,EAAE,EAAE;IAC7C,MAAM,WAAW,GAAG,GAAG,CAAC,OAAO,CAAC,aAAa,EAAE,YAAY,CAAC;IAE5D,uCAAuC;IACvC,GAAG,CAAC,MAAM,CAAC,eAAe,EAAE,2BAA2B,CAAC,CAAC;IAEzD,IAAI,CAAC,WAAW,IAAI,GAAG,CAAC,YAAY,CAAC,WAAW,CAAC,EAAE,CAAC;QAClD,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC9B,CAAC;SAAM,CAAC;QACN,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC7B,CAAC;AACH,CAAC,CAAC"}
@@ -1,14 +1,21 @@
1
1
  import { Request, Response } from 'express';
2
2
  /**
3
- * Express middleware that checks if the current session has a valid, non-expired access token.
3
+ * Middleware stub that validates whether the current request is authenticated via a JWT stored on the session.
4
4
  *
5
- * - Sets the `Cache-Control` header to prevent caching of the endpoint.
6
- * - Retrieves the `access_token` from the session.
7
- * - If the token is missing or expired (as determined by `Jwt.isJwtExpired`), responds with HTTP 401 and `false`.
8
- * - Otherwise, responds with HTTP 200 and `true`.
5
+ * This handler:
6
+ * - Reads the access token from req.session.securityToken?.access_token.
7
+ * - Prevents caching of the response by setting the "Cache-Control" header to "no-store, must-revalidate".
8
+ * - If there is no access token or the token is expired (via Jwt.isJwtExpired), it responds with HTTP 401 and a body of false.
9
+ * - If the token exists and is valid, it responds with HTTP 200 and a body of true.
9
10
  *
10
- * @param req - The Express request object, expected to have a `session.securityToken.access_token` property.
11
- * @param res - The Express response object used to send the HTTP response.
11
+ * The function writes the response directly to the provided Express Response object and does not return a value.
12
+ *
13
+ * @param req - The incoming Express Request. Expects a session object with an optional securityToken containing access_token.
14
+ * @param res - The Express Response used to set headers, status, and send the boolean authentication result.
15
+ *
16
+ * @remarks
17
+ * - This is a simple stub intended for testing or service-mocking; it performs no additional authentication logic beyond token existence and expiry check.
18
+ * - Side effects: sets Cache-Control header and sends an HTTP response (status + boolean body).
12
19
  */
13
20
  declare const _default: (req: Request, res: Response) => void;
14
21
  export default _default;
@@ -1 +1 @@
1
- {"version":3,"file":"sso-authenticated.stub.d.ts","sourceRoot":"","sources":["../../../src/stubs/sso/sso-authenticated.stub.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAG5C;;;;;;;;;;GAUG;yBACa,KAAK,OAAO,EAAE,KAAK,QAAQ;AAA3C,wBAWE"}
1
+ {"version":3,"file":"sso-authenticated.stub.d.ts","sourceRoot":"","sources":["../../../src/stubs/sso/sso-authenticated.stub.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAG5C;;;;;;;;;;;;;;;;;GAiBG;yBACa,KAAK,OAAO,EAAE,KAAK,QAAQ;AAA3C,wBAWE"}
@@ -1,22 +1,31 @@
1
1
  import { Jwt } from '../../utils';
2
2
  /**
3
- * Express middleware that checks if the current session has a valid, non-expired access token.
3
+ * Middleware stub that validates whether the current request is authenticated via a JWT stored on the session.
4
4
  *
5
- * - Sets the `Cache-Control` header to prevent caching of the endpoint.
6
- * - Retrieves the `access_token` from the session.
7
- * - If the token is missing or expired (as determined by `Jwt.isJwtExpired`), responds with HTTP 401 and `false`.
8
- * - Otherwise, responds with HTTP 200 and `true`.
5
+ * This handler:
6
+ * - Reads the access token from req.session.securityToken?.access_token.
7
+ * - Prevents caching of the response by setting the "Cache-Control" header to "no-store, must-revalidate".
8
+ * - If there is no access token or the token is expired (via Jwt.isJwtExpired), it responds with HTTP 401 and a body of false.
9
+ * - If the token exists and is valid, it responds with HTTP 200 and a body of true.
9
10
  *
10
- * @param req - The Express request object, expected to have a `session.securityToken.access_token` property.
11
- * @param res - The Express response object used to send the HTTP response.
11
+ * The function writes the response directly to the provided Express Response object and does not return a value.
12
+ *
13
+ * @param req - The incoming Express Request. Expects a session object with an optional securityToken containing access_token.
14
+ * @param res - The Express Response used to set headers, status, and send the boolean authentication result.
15
+ *
16
+ * @remarks
17
+ * - This is a simple stub intended for testing or service-mocking; it performs no additional authentication logic beyond token existence and expiry check.
18
+ * - Side effects: sets Cache-Control header and sends an HTTP response (status + boolean body).
12
19
  */
13
20
  export default (req, res) => {
21
+ const accessToken = req.session.securityToken?.access_token;
14
22
  // Don't allow caching of this endpoint
15
23
  res.header('Cache-Control', 'no-store, must-revalidate');
16
- const accessToken = req.session.securityToken?.access_token;
17
24
  if (!accessToken || Jwt.isJwtExpired(accessToken)) {
18
25
  res.status(401).send(false);
19
26
  }
20
- res.status(200).send(true);
27
+ else {
28
+ res.status(200).send(true);
29
+ }
21
30
  };
22
31
  //# sourceMappingURL=sso-authenticated.stub.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"sso-authenticated.stub.js","sourceRoot":"","sources":["../../../src/stubs/sso/sso-authenticated.stub.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AAElC;;;;;;;;;;GAUG;AACH,eAAe,CAAC,GAAY,EAAE,GAAa,EAAE,EAAE;IAC7C,uCAAuC;IACvC,GAAG,CAAC,MAAM,CAAC,eAAe,EAAE,2BAA2B,CAAC,CAAC;IAEzD,MAAM,WAAW,GAAG,GAAG,CAAC,OAAO,CAAC,aAAa,EAAE,YAAY,CAAC;IAE5D,IAAI,CAAC,WAAW,IAAI,GAAG,CAAC,YAAY,CAAC,WAAW,CAAC,EAAE,CAAC;QAClD,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC9B,CAAC;IAED,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC7B,CAAC,CAAC"}
1
+ {"version":3,"file":"sso-authenticated.stub.js","sourceRoot":"","sources":["../../../src/stubs/sso/sso-authenticated.stub.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AAElC;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAe,CAAC,GAAY,EAAE,GAAa,EAAE,EAAE;IAC7C,MAAM,WAAW,GAAG,GAAG,CAAC,OAAO,CAAC,aAAa,EAAE,YAAY,CAAC;IAE5D,uCAAuC;IACvC,GAAG,CAAC,MAAM,CAAC,eAAe,EAAE,2BAA2B,CAAC,CAAC;IAEzD,IAAI,CAAC,WAAW,IAAI,GAAG,CAAC,YAAY,CAAC,WAAW,CAAC,EAAE,CAAC;QAClD,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC9B,CAAC;SAAM,CAAC;QACN,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC7B,CAAC;AACH,CAAC,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"sso-login-callback.stub.d.ts","sourceRoot":"","sources":["../../../src/stubs/sso/sso-login-callback.stub.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAI1D;;;;;;;;;;;;;;;;GAgBG;yBACmB,KAAK,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,EAAE,YAAY,MAAM;AAAzF,wBA2CE"}
1
+ {"version":3,"file":"sso-login-callback.stub.d.ts","sourceRoot":"","sources":["../../../src/stubs/sso/sso-login-callback.stub.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAI1D;;;;;;;;;;;;;;;;GAgBG;yBACmB,KAAK,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,EAAE,YAAY,MAAM;AAAzF,wBA0CE"}
@@ -46,7 +46,6 @@ export default async (req, res, next, opalApiUrl) => {
46
46
  logger.error('Error saving session', err);
47
47
  return next(err);
48
48
  }
49
- logger.info('Session saved (access token only)');
50
49
  return res.redirect('/');
51
50
  });
52
51
  }
@@ -1 +1 @@
1
- {"version":3,"file":"sso-login-callback.stub.js","sourceRoot":"","sources":["../../../src/stubs/sso/sso-login-callback.stub.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAC/C,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B;;;;;;;;;;;;;;;;GAgBG;AACH,eAAe,KAAK,EAAE,GAAY,EAAE,GAAa,EAAE,IAAkB,EAAE,UAAkB,EAAE,EAAE;IAC3F,MAAM,MAAM,GAAG,MAAM,CAAC,SAAS,CAAC,qBAAqB,CAAC,CAAC;IAEvD,IAAI,CAAC;QACH,MAAM,KAAK,GAAI,GAAG,CAAC,KAAK,CAAC,OAAO,CAAwB,EAAE,IAAI,EAAE,CAAC;QACjE,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;YAChE,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YACpB,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC;QACrB,CAAC;QAED,MAAM,cAAc,GAAG,GAAG,UAAU,6BAA6B,CAAC;QAElE,6DAA6D;QAC7D,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,cAAc,EAAE;YAC7C,OAAO,EAAE,EAAE,cAAc,EAAE,KAAK,EAAE;SACnC,CAAC,CAAC;QAEH,4EAA4E;QAC5E,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC;QACzB,MAAM,YAAY,GAAG,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,YAAY,CAAC;QAE1E,IAAI,CAAC,YAAY,EAAE,CAAC;YAClB,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,6CAA6C,CAAC,CAAC;YACvE,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;YAC9B,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC;QACrB,CAAC;QAED,iDAAiD;QACjD,GAAG,CAAC,OAAO,CAAC,aAAa,GAAG,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,CAAC;QAEpE,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE;YACvB,IAAI,GAAG,EAAE,CAAC;gBACR,MAAM,CAAC,KAAK,CAAC,sBAAsB,EAAE,GAAG,CAAC,CAAC;gBAC1C,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC;YACnB,CAAC;YACD,MAAM,CAAC,IAAI,CAAC,mCAAmC,CAAC,CAAC;YACjD,OAAO,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;QAC3B,CAAC,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,CAAC,KAAK,CAAC,8BAA8B,EAAE,KAAK,CAAC,CAAC;QACpD,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC;IACrB,CAAC;AACH,CAAC,CAAC"}
1
+ {"version":3,"file":"sso-login-callback.stub.js","sourceRoot":"","sources":["../../../src/stubs/sso/sso-login-callback.stub.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAC/C,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B;;;;;;;;;;;;;;;;GAgBG;AACH,eAAe,KAAK,EAAE,GAAY,EAAE,GAAa,EAAE,IAAkB,EAAE,UAAkB,EAAE,EAAE;IAC3F,MAAM,MAAM,GAAG,MAAM,CAAC,SAAS,CAAC,qBAAqB,CAAC,CAAC;IAEvD,IAAI,CAAC;QACH,MAAM,KAAK,GAAI,GAAG,CAAC,KAAK,CAAC,OAAO,CAAwB,EAAE,IAAI,EAAE,CAAC;QACjE,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;YAChE,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YACpB,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC;QACrB,CAAC;QAED,MAAM,cAAc,GAAG,GAAG,UAAU,6BAA6B,CAAC;QAElE,6DAA6D;QAC7D,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,cAAc,EAAE;YAC7C,OAAO,EAAE,EAAE,cAAc,EAAE,KAAK,EAAE;SACnC,CAAC,CAAC;QAEH,4EAA4E;QAC5E,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC;QACzB,MAAM,YAAY,GAAG,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,YAAY,CAAC;QAE1E,IAAI,CAAC,YAAY,EAAE,CAAC;YAClB,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,6CAA6C,CAAC,CAAC;YACvE,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;YAC9B,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC;QACrB,CAAC;QAED,iDAAiD;QACjD,GAAG,CAAC,OAAO,CAAC,aAAa,GAAG,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,CAAC;QAEpE,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE;YACvB,IAAI,GAAG,EAAE,CAAC;gBACR,MAAM,CAAC,KAAK,CAAC,sBAAsB,EAAE,GAAG,CAAC,CAAC;gBAC1C,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC;YACnB,CAAC;YACD,OAAO,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;QAC3B,CAAC,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,CAAC,KAAK,CAAC,8BAA8B,EAAE,KAAK,CAAC,CAAC;QACpD,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC;IACrB,CAAC;AACH,CAAC,CAAC"}