element => {
    const cssFnPropCollection = getJSXAttributes(j, element, 'cssFn');

    // no cssProp usage for this element
    if (!cssFnPropCollection.length) return;

    const cssFnProp = cssFnPropCollection.get();

    const cssFnExpression = cssFnPropCollection
      .find(j.JSXExpressionContainer)
      .find(j.Expression)
      .get();

    if (cssFnExpression) {
      // just remove the state styles param
      try {
        const [stylePropName] = cssFnExpression!.value.params;
        j(cssFnExpression)
          .find(j.SpreadElement)
          .forEach(n => {
            // discerns whether there are multiple identifiers here
            const isComplexIdentifier = j(n).find(j.Identifier).length > 1;

            if (isComplexIdentifier) {
              throw new Error(
                'CSSFn Prop codemod: Unable to parse spread element',
              );
            }

            const hasStyleProp = !!j(n)
              .find(j.Identifier)
              .filter(node => node.value.name === stylePropName.name).length;
            if (hasStyleProp) {
              j(n).remove();
            }
          });

        cssFnExpression!.value.params.shift();
      } catch (e) {
        addCommentBeforeJSX(
          j,
          cssFnProp,
          `
        The usage of the 'cssFn' prop in this component could not be transformed and requires manual intervention.
        The 'cssFn' prop has been simplified so that users no longer need to merge the inherited styles with their own overrides.
        For more info please reach out to #help-design-system-code.
        `,
        );
      }
    }
  }


n => {
            // discerns whether there are multiple identifiers here
            const isComplexIdentifier = j(n).find(j.Identifier).length > 1;

            if (isComplexIdentifier) {
              throw new Error(
                'CSSFn Prop codemod: Unable to parse spread element',
              );
            }

            const hasStyleProp = !!j(n)
              .find(j.Identifier)
              .filter(node => node.value.name === stylePropName.name).length;
            if (hasStyleProp) {
              j(n).remove();
            }
          }


node => node.value.name === stylePropName.name


pkg => {
    const importSpecifier = getImportSpecifierName(
      j,
      source,
      pkg,
      '@atlaskit/menu',
    );

    if (importSpecifier != null) {
      updateCssFnProp(j, source, importSpecifier);
    }
  }


(files: File[]) => (_: any, headers: Headers) => {
    // Allow the browser to set the content type based on the form data - manually setting to multipart mangles
    // the request for some reason so lets let the browser set based on the form data.
    headers.delete('Content-Type');
    const formData = new FormData();

    // Add the files
    files.forEach((curFile: File) => {
      formData.append('file', curFile, curFile.name);
    });

    return { body: formData, headers };
  }


(_: any, headers: Headers) => {
    // Allow the browser to set the content type based on the form data - manually setting to multipart mangles
    // the request for some reason so lets let the browser set based on the form data.
    headers.delete('Content-Type');
    const formData = new FormData();

    // Add the files
    files.forEach((curFile: File) => {
      formData.append('file', curFile, curFile.name);
    });

    return { body: formData, headers };
  }


(curFile: File) => {
      formData.append('file', curFile, curFile.name);
    }


// tslint:disable-next-line no-any
(files: File[]) => (data: KycApplicationInput, headers: Headers) => {
    headers.delete('Content-Type');
    const formData = new FormData();

    formData.append('kycApplicantInput', JSON.stringify(data));
    // Add the files
    files.forEach((curFile: File) => {
      // tslint:disable-next-line no-string-literal
      formData.append(curFile['uid'], curFile, curFile.name);
    });

    return { body: formData, headers };
  }


(data: KycApplicationInput, headers: Headers) => {
    headers.delete('Content-Type');
    const formData = new FormData();

    formData.append('kycApplicantInput', JSON.stringify(data));
    // Add the files
    files.forEach((curFile: File) => {
      // tslint:disable-next-line no-string-literal
      formData.append(curFile['uid'], curFile, curFile.name);
    });

    return { body: formData, headers };
  }


(curFile: File) => {
      // tslint:disable-next-line no-string-literal
      formData.append(curFile['uid'], curFile, curFile.name);
    }


export default class Match {
    character1: CharacterAsset
    character2: CharacterAsset
    constructor(
        character1: CharacterAsset,
        character2: CharacterAsset
        ) {
            this.character1 = character1
            this.character2 = character2
        }
}


export function ZoomOutButton(props: ToolbarButtonProps) {
  const {t} = useTranslation()
  const dispatch = useDispatch()
  const graphGetter = useGraph()
  const {disabled} = props
  const available = !disabled && graphGetter()

  return (
    <ToolbarButton
      name={t("panels.actions.view-zoomOut.label", "zoom-out")}
      icon={<Icon/>}


t("panels.actions.view-zoomOut.label", "zoom-out")


() => {
    return (
        <div style={{ position: "relative", width: 100, height: 100 }}


(content: string, name: string, mimetype: string) =>
  new Promise<string>((resolve, reject) => {
    const fileUpload = bucket.file(name)
    const blobStream = fileUpload.createWriteStream({
      metadata: {
        contentType: mimetype,
      },
    })

    blobStream.on('error', (error) => {
      console.error(error)
      reject('Something is wrong! Unable to upload at the moment.')
    })

    blobStream.on('finish', () => {
      resolve(
        `https://storage.googleapis.com/${bucket.name}/${fileUpload.name}`
      )
    })

    blobStream.end(content)
  })


(resolve, reject) => {
    const fileUpload = bucket.file(name)
    const blobStream = fileUpload.createWriteStream({
      metadata: {
        contentType: mimetype,
      },
    })

    blobStream.on('error', (error) => {
      console.error(error)
      reject('Something is wrong! Unable to upload at the moment.')
    })

    blobStream.on('finish', () => {
      resolve(
        `https://storage.googleapis.com/${bucket.name}/${fileUpload.name}`
      )
    })

    blobStream.end(content)
  }


(error) => {
      console.error(error)
      reject('Something is wrong! Unable to upload at the moment.')
    }


() => {
      resolve(
        `https://storage.googleapis.com/${bucket.name}/${fileUpload.name}`
      )
    }


async (req: NextApiRequest, res: NextApiResponse) => {
  const { body } = req
  const ref = binsRef.doc()

  const filename = `${body.files[0].filename}.${body.files[0].lang.fileExtension}`
  const fileUrl = await uploadBinToStorage(
    body.files[0].code,
    `bins/${ref.id}/${filename}`,
    'text/plain'
  ).catch((e) => {
    console.error(e)
    res.status(500).json({
      message: 'An internal error ocurred',
    })
  })

  const bin = {
    id: ref.id,
    name: body.name,
    author: body.author,
    timestamp: admin.firestore.FieldValue.serverTimestamp(),
    files: [
      {
        id: uniqid(),
        name: filename,
        url: fileUrl,
        lang: body.files[0].lang,
      },
    ],
  }

  await ref.set(bin).catch((e) => {
    console.error(e)
    res.status(500).json({
      message: 'An internal error ocurred',
    })
  })

  res.status(200).json(bin)
}


(e) => {
    console.error(e)
    res.status(500).json({
      message: 'An internal error ocurred',
    })
  }


async (
  req: NextApiRequestWithFormData,
  res: NextApiResponse
) => {
  await multerAny(req, res)

  // This operation expects a single file upload.
  if (!req.files?.length || req.files.length > 1) {
    res.status(400).json({ message: 'A .zip file expected' })
    return
  }

  const blob: BlobCorrected = req.files[0]

  var zip = new AdmZip(blob.buffer)
  var zipEntries = zip.getEntries()

  const ref = binsRef.doc()

  const promises = zipEntries.map(async (zipEntry) => {
    if (
      !zipEntry.isDirectory &&
      !zipEntry.entryName.endsWith('.class') &&
      !zipEntry.entryName.split('/').some((folder) => folder.startsWith('.'))
    ) {
      const fileBuffer = zipEntry.getData()
      const fileType = await FileType.fromBuffer(fileBuffer)

      if (!fileType || fileType.mime === 'application/xml') {
        const fileUrl = await uploadBinToStorage(
          fileBuffer.toString('utf8'),
          `bins/${ref.id}/${zipEntry.entryName}`,
          'plain/text'
        ).catch((e) => {
          console.error(e)
          res.status(500).json({
            message: 'An internal error ocurred',
          })
        })

        const fileExtension = zipEntry.entryName.split('.')

        return {
          id: uniqid(),
          name: zipEntry.entryName,
          url: fileUrl,
          lang: {
            name:
              fileExtension.length > 1
                ? fileExtension[fileExtension.length - 1]
                : 'plaintext',
          },
        }
      }
    }
  })

  const files = await Promise.all(promises)

  const bin = {
    id: ref.id,
    files: files.filter((file) => file !== undefined),
    timestamp: admin.firestore.FieldValue.serverTimestamp(),
    name: req.body.name,
    author: {
      name: req.body['author.name'],
      ...(req.body['author.email'] &&
      req.body['author.uid'] &&
      req.body['author.photoUrl']
        ? {
            email: req.body['author.email'],
            uid: req.body['author.uid'],
            photoUrl: req.body['author.photoUrl'],
          }
        : {}),
    },
  }

  await ref.set(bin).catch((e) => {
    console.error(e)
    res.status(500).json({
      message: 'An internal error ocurred',
    })
  })

  res.status(201).json({ ...bin, timestamp: undefined })
}


async (zipEntry) => {
    if (
      !zipEntry.isDirectory &&
      !zipEntry.entryName.endsWith('.class') &&
      !zipEntry.entryName.split('/').some((folder) => folder.startsWith('.'))
    ) {
      const fileBuffer = zipEntry.getData()
      const fileType = await FileType.fromBuffer(fileBuffer)

      if (!fileType || fileType.mime === 'application/xml') {
        const fileUrl = await uploadBinToStorage(
          fileBuffer.toString('utf8'),
          `bins/${ref.id}/${zipEntry.entryName}`,
          'plain/text'
        ).catch((e) => {
          console.error(e)
          res.status(500).json({
            message: 'An internal error ocurred',
          })
        })

        const fileExtension = zipEntry.entryName.split('.')

        return {
          id: uniqid(),
          name: zipEntry.entryName,
          url: fileUrl,
          lang: {
            name:
              fileExtension.length > 1
                ? fileExtension[fileExtension.length - 1]
                : 'plaintext',
          },
        }
      }
    }
  }


(folder) => folder.startsWith('.')


(e) => {
          console.error(e)
          res.status(500).json({
            message: 'An internal error ocurred',
          })
        }


(file) => file !== undefined


async (req: NextApiRequest, res: NextApiResponse) => {
  const { query } = req
  const bin = await binsRef.doc(query.binId as string).get()
  if (!bin.exists) {
    res.status(404).json({
      message: "This bin doesn't exists",
    })
  } else {
    res.status(200).json({
      ...bin.data(),
      timestamp: bin.data()?.timestamp.toMillis(),
    })
  }
}


export type NextApiRequestWithFormData = NextApiRequest & {
  files: any[]
}


type BlobCorrected = Blob & {
  buffer: Buffer
}


// compare two token amounts with highest one coming first
function balanceComparator(balanceA?: TokenAmount, balanceB?: TokenAmount) {
  if (balanceA && balanceB) {
    return balanceA.greaterThan(balanceB) ? -1 : balanceA.equalTo(balanceB) ? 0 : 1
  }
  if (balanceA && balanceA.greaterThan('0')) {
    return -1
  }
  if (balanceB && balanceB.greaterThan('0')) {
    return 1
  }
  return 0
}


function getTokenComparator(balances: {
  [tokenAddress: string]: TokenAmount | undefined
}): (tokenA: Token, tokenB: Token) => number {
  return function sortTokens(tokenA: Token, tokenB: Token): number {
    // -1 = a is first
    // 1 = b is first

    // sort by balances
    const balanceA = balances[tokenA.address]
    const balanceB = balances[tokenB.address]

    const balanceComp = balanceComparator(balanceA, balanceB)
    if (balanceComp !== 0) return balanceComp

    if (tokenA.symbol && tokenB.symbol) {
      // sort by symbol
      return tokenA.symbol.toLowerCase() < tokenB.symbol.toLowerCase() ? -1 : 1
    }
    return tokenA.symbol ? -1 : tokenB.symbol ? -1 : 0
  }
}


function useTokenComparator(inverted: boolean): (tokenA: Token, tokenB: Token) => number {
  const balances = useAllTokenBalances()
  const comparator = useMemo(() => getTokenComparator(balances ?? {}), [balances])
  return useMemo(() => {
    if (inverted) {
      return (tokenA: Token, tokenB: Token) => comparator(tokenA, tokenB) * -1
    }
    return comparator
  }, [inverted, comparator])
}


() => getTokenComparator(balances ?? {})


() => {
    if (inverted) {
      return (tokenA: Token, tokenB: Token) => comparator(tokenA, tokenB) * -1
    }
    return comparator
  }


(tokenA: Token, tokenB: Token) => comparator(tokenA, tokenB) * -1


() => jest.requireActual('../../index.ts')


() => {
  test('full project', async () => {
    const basedir = path.join(__dirname, 'fixtures', 'hoist');
    const outputPath = findCacheDir({
      name: `test-nohoist`,
      cwd: __dirname,
    });
    // eslint-disable-next-line import/no-extraneous-dependencies, global-require
    const { getWebpackConfig } = require('@goji/cli/dist/cjs/config/webpack.config');
    // eslint-disable-next-line import/no-extraneous-dependencies, global-require
    const babelConfig = require('@goji/cli/dist/cjs/config/babel.config');
    const webpackConfig = getWebpackConfig({
      basedir,
      outputPath,
      target: 'wechat',
      nodeEnv: 'production',
      babelConfig,
      nohoist: {
        enable: true,
        maxPackages: Infinity,
      },
    });
    const compiler = webpack(webpackConfig);
    await new Promise<webpack.Stats | undefined>((resolve, reject) => {
      compiler.run((err, stats) => {
        if (err) {
          reject(err);
          return;
        }
        if (stats?.hasErrors()) {
          reject(new Error(stats.compilation.errors[0].toString()));
          return;
        }
        resolve(stats);
      });
    });
    await new Promise<undefined>(resolve => {
      compiler.close(() => resolve(undefined));
    });
    // checks
    const findAllFiles = (content: string) =>
      new Promise<Array<string>>((resolve, reject) => {
        execFile(rgPath, ['-l', content, '.'], { cwd: outputPath }, (err, stdout) => {
          if (err) {
            reject(err);
          }
          resolve(
            stdout
              .trim()
              .split('\n')
              .filter(_ => _.endsWith('.js'))
              .sort()
              // remove leading `./` or `.\\`
              .map(_ => path.join(_)),
          );
        });
      });

    // internal
    expect(await findAllFiles('this is internalA')).toEqual([
      path.join('packageA', '_goji_commons.js'),
    ]);

    // independent should works
    expect(await findAllFiles('this is sharedTop')).toEqual([
      path.join('_goji_commons.js'),
      path.join('packageIndependent', '_goji_commons.js'),
    ]);

    // can nohoist
    expect(await findAllFiles('this is sharedA')).toEqual([
      path.join('packageA', '_goji_nohoist_ef694a33ef79da4db885e0bae648d1b0.js'),
      path.join('packageB', '_goji_nohoist_ef694a33ef79da4db885e0bae648d1b0.js'),
    ]);

    // nohoist can work with independent
    expect(await findAllFiles('this is sharedB')).toEqual([
      path.join('packageA', '_goji_nohoist_d1f973b3a1e95adb52e3162147042c71.js'),
      path.join('packageB', '_goji_nohoist_d1f973b3a1e95adb52e3162147042c71.js'),
      path.join('packageIndependent', '_goji_nohoist_d1f973b3a1e95adb52e3162147042c71.js'),
    ]);
  });
}


async () => {
    const basedir = path.join(__dirname, 'fixtures', 'hoist');
    const outputPath = findCacheDir({
      name: `test-nohoist`,
      cwd: __dirname,
    });
    // eslint-disable-next-line import/no-extraneous-dependencies, global-require
    const { getWebpackConfig } = require('@goji/cli/dist/cjs/config/webpack.config');
    // eslint-disable-next-line import/no-extraneous-dependencies, global-require
    const babelConfig = require('@goji/cli/dist/cjs/config/babel.config');
    const webpackConfig = getWebpackConfig({
      basedir,
      outputPath,
      target: 'wechat',
      nodeEnv: 'production',
      babelConfig,
      nohoist: {
        enable: true,
        maxPackages: Infinity,
      },
    });
    const compiler = webpack(webpackConfig);
    await new Promise<webpack.Stats | undefined>((resolve, reject) => {
      compiler.run((err, stats) => {
        if (err) {
          reject(err);
          return;
        }
        if (stats?.hasErrors()) {
          reject(new Error(stats.compilation.errors[0].toString()));
          return;
        }
        resolve(stats);
      });
    });
    await new Promise<undefined>(resolve => {
      compiler.close(() => resolve(undefined));
    });
    // checks
    const findAllFiles = (content: string) =>
      new Promise<Array<string>>((resolve, reject) => {
        execFile(rgPath, ['-l', content, '.'], { cwd: outputPath }, (err, stdout) => {
          if (err) {
            reject(err);
          }
          resolve(
            stdout
              .trim()
              .split('\n')
              .filter(_ => _.endsWith('.js'))
              .sort()
              // remove leading `./` or `.\\`
              .map(_ => path.join(_)),
          );
        });
      });

    // internal
    expect(await findAllFiles('this is internalA')).toEqual([
      path.join('packageA', '_goji_commons.js'),
    ]);

    // independent should works
    expect(await findAllFiles('this is sharedTop')).toEqual([
      path.join('_goji_commons.js'),
      path.join('packageIndependent', '_goji_commons.js'),
    ]);

    // can nohoist
    expect(await findAllFiles('this is sharedA')).toEqual([
      path.join('packageA', '_goji_nohoist_ef694a33ef79da4db885e0bae648d1b0.js'),
      path.join('packageB', '_goji_nohoist_ef694a33ef79da4db885e0bae648d1b0.js'),
    ]);

    // nohoist can work with independent
    expect(await findAllFiles('this is sharedB')).toEqual([
      path.join('packageA', '_goji_nohoist_d1f973b3a1e95adb52e3162147042c71.js'),
      path.join('packageB', '_goji_nohoist_d1f973b3a1e95adb52e3162147042c71.js'),
      path.join('packageIndependent', '_goji_nohoist_d1f973b3a1e95adb52e3162147042c71.js'),
    ]);
  }


(resolve, reject) => {
      compiler.run((err, stats) => {
        if (err) {
          reject(err);
          return;
        }
        if (stats?.hasErrors()) {
          reject(new Error(stats.compilation.errors[0].toString()));
          return;
        }
        resolve(stats);
      });
    }


(err, stats) => {
        if (err) {
          reject(err);
          return;
        }
        if (stats?.hasErrors()) {
          reject(new Error(stats.compilation.errors[0].toString()));
          return;
        }
        resolve(stats);
      }


resolve => {
      compiler.close(() => resolve(undefined));
    }


() => resolve(undefined)


(content: string) =>
      new Promise<Array<string>>((resolve, reject) => {
        execFile(rgPath, ['-l', content, '.'], { cwd: outputPath }, (err, stdout) => {
          if (err) {
            reject(err);
          }
          resolve(
            stdout
              .trim()
              .split('\n')
              .filter(_ => _.endsWith('.js'))
              .sort()
              // remove leading `./` or `.\\`
              .map(_ => path.join(_)),
          );
        });
      })


(resolve, reject) => {
        execFile(rgPath, ['-l', content, '.'], { cwd: outputPath }, (err, stdout) => {
          if (err) {
            reject(err);
          }
          resolve(
            stdout
              .trim()
              .split('\n')
              .filter(_ => _.endsWith('.js'))
              .sort()
              // remove leading `./` or `.\\`
              .map(_ => path.join(_)),
          );
        });
      }


(err, stdout) => {
          if (err) {
            reject(err);
          }
          resolve(
            stdout
              .trim()
              .split('\n')
              .filter(_ => _.endsWith('.js'))
              .sort()
              // remove leading `./` or `.\\`
              .map(_ => path.join(_)),
          );
        }


_ => _.endsWith('.js')


_ => path.join(_)


(
    nodes: any[] | null,
    identifyParentIsTreeNode: () => boolean
): void => {
    const { level, informParentOfChildNodes } = useContext(TreeNodeBranchContext);
    useEffect(() => {
        if (level > 0 && identifyParentIsTreeNode()) {
            informParentOfChildNodes(nodes?.length);
        }
    }, [nodes, level, informParentOfChildNodes, identifyParentIsTreeNode]);
}


() => {
        if (level > 0 && identifyParentIsTreeNode()) {
            informParentOfChildNodes(nodes?.length);
        }
    }


export interface TreeNodeBranchContextProps {
    level: number;
    informParentOfChildNodes: (numberOfNodes: number | undefined) => void;
}


export function SortDescendingOutlined(props: Omit<DIconProps, 'dIcon'>) {
  return <DIcon {...props} dIcon={AntIcon} />;
}


(event: KeyboardEvent) => {
    const { onSelect, selectedIndex, values } = this.props;
    const indicators = Array.from(this.tablist.children);

    // bail if the target isn't an indicator
    if (!indicators.includes(event.target as HTMLElement)) return;

    // bail if not valid arrow key
    const isLeft = event.key === 'ArrowLeft';
    const isRight = event.key === 'ArrowRight';
    if (!isLeft && !isRight) return;

    // bail if at either end of the values
    const isAlpha = isLeft && selectedIndex === 0;
    const isOmega = isRight && selectedIndex === values.length - 1;
    if (isAlpha || isOmega) return;

    const index = isLeft ? selectedIndex - 1 : selectedIndex + 1;

    // call the consumer's select method and focus the applicable indicator
    if (onSelect) {
      onSelect({
        event: (event as unknown) as React.MouseEvent<HTMLButtonElement>,
        index,
      });
    }

    if (typeof indicators[index].focus === 'function') {
      indicators[index].focus();
    }
  }


(val, index) => {
          const selected = selectedIndex === index;
          const common = {
            appearance: appearance!,
            key: index,
            selected,
            size: size!,
            gutter: gutter!,
          };
          const tabId = `${ariaLabel}${index}`;
          const panelId = `${ariaControls}${index}`;

          return onSelect ? (
            <IndicatorButton
              {...common}
              aria-controls={panelId}
              aria-label={tabId}
              aria-selected={selected}
              id={tabId}
              onClick={event => onSelect({ event, index })}
              role="tab"
              tabIndex={selected ? 0 : -1}
              type="button"
            />


class ProgressDots extends Component<Props, {}> {
  tablist: { children: HTMLElement[] } = { children: [] };

  static defaultProps = {
    appearance: 'default',
    ariaControls: 'panel',
    ariaLabel: 'tab',
    size: 'default',
    spacing: 'comfortable',
  };

  componentDidMount() {
    if (this.props.onSelect) {
      document.addEventListener('keydown', this.handleKeyDown, false);
    }
  }

  componentWillUnmount() {
    if (this.props.onSelect) {
      document.removeEventListener('keydown', this.handleKeyDown);
    }
  }

  handleKeyDown = (event: KeyboardEvent) => {
    const { onSelect, selectedIndex, values } = this.props;
    const indicators = Array.from(this.tablist.children);

    // bail if the target isn't an indicator
    if (!indicators.includes(event.target as HTMLElement)) return;

    // bail if not valid arrow key
    const isLeft = event.key === 'ArrowLeft';
    const isRight = event.key === 'ArrowRight';
    if (!isLeft && !isRight) return;

    // bail if at either end of the values
    const isAlpha = isLeft && selectedIndex === 0;
    const isOmega = isRight && selectedIndex === values.length - 1;
    if (isAlpha || isOmega) return;

    const index = isLeft ? selectedIndex - 1 : selectedIndex + 1;

    // call the consumer's select method and focus the applicable indicator
    if (onSelect) {
      onSelect({
        event: (event as unknown) as React.MouseEvent<HTMLButtonElement>,
        index,
      });
    }

    if (typeof indicators[index].focus === 'function') {
      indicators[index].focus();
    }
  };

  render() {
    // NOTE: `spacing` is a reserved HTML attribute and will be added to the
    // element, replaced with `gutter`.
    const {
      appearance,
      ariaControls,
      ariaLabel,
      onSelect,
      selectedIndex,
      size,
      spacing: gutter,
      values,
    } = this.props;

    return (
      <Container
        innerRef={r => {
          this.tablist = r;
        }}
        role="tablist"
      >
        {values.map((val, index) => {
          const selected = selectedIndex === index;
          const common = {
            appearance: appearance!,
            key: index,
            selected,
            size: size!,
            gutter: gutter!,
          };
          const tabId = `${ariaLabel}${index}`;
          const panelId = `${ariaControls}${index}`;

          return onSelect ? (
            <IndicatorButton
              {...common}
              aria-controls={panelId}
              aria-label={tabId}
              aria-selected={selected}
              id={tabId}
              onClick={event => onSelect({ event, index })}
              role="tab"
              tabIndex={selected ? 0 : -1}
              type="button"
            />
          ) : (
            <IndicatorDiv {...common} role="presentation" />
          );
        }


interface Props extends WithAnalyticsEventsProps {
  /** The color of the indicators */
  appearance?: DotsAppearance;
  /** The aria-controls text applied to each indicator, appended by the index */
  ariaControls?: string;
  /** The aria-label text applied to each indicator, appended by the index */
  ariaLabel?: string;
  /** Function called when an indicator is selected */
  onSelect?: (eventData: {
    event: React.MouseEvent<HTMLButtonElement>;
    index: number;
  }) => void;
  /** Which indicator is currently selected */
  selectedIndex: number;
  /** Corresponds to the width & height of each indicator */
  size?: Size;
  /** How much of a gutter is desired between indicators */
  spacing?: Spacing;
  /** An array of values mapped over to create the indicators */
  values: Array<any>;
}


export type DotsAppearance = 'default' | 'help' | 'inverted' | 'primary';


export type Spacing = 'comfortable' | 'cozy' | 'compact';


export type Size = 'small' | 'default' | 'large';


componentDidMount() {
    if (this.props.onSelect) {
      document.addEventListener('keydown', this.handleKeyDown, false);
    }
  }


componentWillUnmount() {
    if (this.props.onSelect) {
      document.removeEventListener('keydown', this.handleKeyDown);
    }
  }


render() {
    // NOTE: `spacing` is a reserved HTML attribute and will be added to the
    // element, replaced with `gutter`.
    const {
      appearance,
      ariaControls,
      ariaLabel,
      onSelect,
      selectedIndex,
      size,
      spacing: gutter,
      values,
    } = this.props;

    return (
      <Container
        innerRef={r => {
          this.tablist = r;
        }}


onSelect({ event, index })


(props) => {

	const history = useHistory()

	const searchMode = useSelector((state: Store) => state.SearchMode)

	const dispatch = useDispatch()

	let authorsDom

	if (props.authors) {
		const { authors } = props
		if (Array.isArray(authors)) {
			authorsDom = authors.map((value, index, array)=> {
				if(index === array.length - 1) {
					return <a href="" key={index} onClick={(e)


(state: Store) => state.SearchMode


(value, index, array)=> {
				if(index === array.length - 1) {
					return <a href="" key={index} onClick={(e)


(e: React.MouseEvent<HTMLAnchorElement>, authors: string) => {
		e.preventDefault()
		searchMode || dispatch(changeSearchMode(true))
		dispatch(setSearchWord(authors))
		dispatch(setSearchSelectPage(1))
		window.scroll(0, 150)
	}


@Module({
  imports: [ArticleModule],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}


export class Util {
	private constructor() {}

	static removeEle(ele, arr: any[]) {
		let index = arr.indexOf(ele);
		if (index > -1) {
			arr.splice(index, 1);
		} else {
			throw ele + " not found in arr " + arr;
		}
	}
}


static removeEle(ele, arr: any[]) {
		let index = arr.indexOf(ele);
		if (index > -1) {
			arr.splice(index, 1);
		} else {
			throw ele + " not found in arr " + arr;
		}
	}


async function dummyFunction(): Promise<void> { }


export async function ensureLog(event: MoonbeamEvent): Promise<Log> {
  const transaction = await ensureTransaction(event.transactionHash,
    event.blockNumber.toString())
  const transactionIndex = event.transactionIndex
  const logIndex = event.logIndex
  const recordId = `${transaction.id}-${logIndex}`
  let entity = await Log.get(recordId)
  if (!entity) {
    entity = Log.create({
      id: recordId,
      transactionId: transaction.id,
      transactionIndex: transactionIndex,
      logIndex: logIndex
    })
    await entity.save()
  }
  return entity
}


export async function createLog(event: MoonbeamEvent): Promise<void> {
  const entity = await ensureLog(event)
  const block = await ensureBlock(event.blockNumber.toString())
  const address = await ensureAccount(event.address)
  entity.blockNumber = block.number
  entity.timestamp = event.blockTimestamp
  //null address probably means internalTransaction (So far, not true. No internalTransaction)
  entity.address = address.id
  entity.removed = event.removed
  //data, topics, arguments
  await entity.save()
  await dispatch.dispatch(event.topics[0].toString(), {
    event: entity,
    rawEvent: event,
  });
}


async (pendingInvitation) => {
          const planningRef = pendingInvitation.data().planning
          const invitationSenderId = pendingInvitation.data().user_id
          const invitationSender = await authServices.getUser(invitationSenderId)
          // if invitation sender doesn't exists...

          // TODO check not already exists ?
          // TODO Add transaction
          await firestoreServices.createUserSharing(user, planningRef, false, invitationSender.displayName)
          await firestoreServices.createPlanningSharing(user, planningRef)
          const planningPendingSharings = await firestoreServices.findPendingPlanningSharing(planningRef, user.email)
          if (!planningPendingSharings.empty) {
            await firestoreServices.deletePendingInvitation(pendingInvitation.ref)
            await firestoreServices.deletePendingPlanningSharings(planningPendingSharings.docs.map((x) => x.ref))
          }
        }


(x) => x.ref


/**
   * Send invatation email
   * @param sender User who send invitation
   * @param receiver Email to send invitation
   * @param inviteToRegister [Optional] Add invation link in the email
   */
async sendInvitation(sender: UserRecord, receiver: string, inviteToRegister: boolean = false) {
    const view = {
      sender,
      displayApplicationUrl: config.app.url,
      baseUrl: `${config.app.url}/`,
      inviteToRegister,
    }
    const html = Mustache.render(invitationTemplateString, view)
    const subject = `${sender.displayName} a partagé ses menus`
    const to = {
      name: `${sender.displayName} (via ${config.app.name})`,
      address: sender.email,
    }
    return emailServices.sendEmail(to, receiver, subject, html)
  }


/**
   * Store a pending invitation in firestore
   * @param userId User id who send invitation
   * @param guestEmail Invited user email
   * @param planningRef Planning to share with the invited user
   */
async savePendingInvitation(userId: string, guestEmail: string, planningRef: DocumentReference<IPlanning>) {
    console.log('savePendingInvitation', { userId, guestEmail, planningRef: planningRef.path })
    return firestoreServices.buildNewPendingInvitationReference().create({
      user_id: userId,
      guest_email: removeDotsInEmail(guestEmail),
      planning: planningRef,
      created_date: new Date(),
    })
  }


async findPendingInvitations(guestEmail: string) {
    console.log('findPendingInvitations', { guestEmail })
    return firestoreServices.findPendingInvitations(guestEmail)
  }


/**
   * Find pending invitation from the user email and convert it to shared planning
   * @param user New created user
   */
async acceptPendingInvationIfExists(user) {
    const pendingInvitations = await invitationServices.findPendingInvitations(user.email)
    if (!pendingInvitations.empty) {
      await Promise.all(
        pendingInvitations.docs.map(async (pendingInvitation) => {
          const planningRef = pendingInvitation.data().planning
          const invitationSenderId = pendingInvitation.data().user_id
          const invitationSender = await authServices.getUser(invitationSenderId)
          // if invitation sender doesn't exists...

          // TODO check not already exists ?
          // TODO Add transaction
          await firestoreServices.createUserSharing(user, planningRef, false, invitationSender.displayName)
          await firestoreServices.createPlanningSharing(user, planningRef)
          const planningPendingSharings = await firestoreServices.findPendingPlanningSharing(planningRef, user.email)
          if (!planningPendingSharings.empty) {
            await firestoreServices.deletePendingInvitation(pendingInvitation.ref)
            await firestoreServices.deletePendingPlanningSharings(planningPendingSharings.docs.map((x) => x.ref))
          }
        }),
      )
      if (pendingInvitations.size === 1) {
        await firestoreServices.setPrimaryPlanning(user.uid, pendingInvitations.docs[0].data().planning)
      }
    }
  }


(props) => (
    <G>
        <Path d="M21.7923 11.8009C22.4856 12.3791 22.5266 13.4721 21.895 14.1049L20.2357 15.7642C19.97 16.0352 19.6149 16.2006 19.2367 16.2294C18.1615 16.3114 16.1037 14.9979 15.5768 14.1847C15.1654 13.5497 15.3268 12.6563 15.3275 11.9399C14.5632 11.7305 13.7839 11.5951 13 11.5337"


(props) => (
    <G>
        <Path fillRule="evenodd"


(props) => (
    <G>
        <Path opacity="0.15"


(props) => (
    <G>
        <Path d="M2.10863 14.1079L3.76461 15.7639C4.02858 16.0413 4.38552 16.2119 4.76752 16.2431C5.84479 16.3311 7.91395 15.0073 8.44327 14.1917C8.8559 13.5559 8.69631 12.6629 8.69702 11.9465C10.8675 11.3476 13.1582 11.3453 15.3275 11.9399C15.3268 12.6563 15.1654 13.5497 15.5768 14.1847C16.1037 14.9979 18.1615 16.3114 19.2367 16.2294C19.6149 16.2006 19.97 16.0352 20.2357 15.7642L21.895 14.1049C22.5266 13.4721 22.4856 12.3791 21.7923 11.8009C16.3175 6.31749 7.7222 6.27776 2.21038 11.7982C1.51362 12.3797 1.47304 13.4775 2.10863 14.1079Z"


(props) => {
    switch (props.variant) {
        case 'broken':
            return <Broken {...props} />;
        case 'curved':
            return <Curved {...props} />;
        case 'duotone':
            return <Duotone {...props} />;
        case 'outline':
            return <Outline {...props} />;
    }
}


type BrokenProps = { color: Color, strokeWidth: number, };


type CurvedProps = { color: Color, strokeWidth: number, };


type DuotoneProps = { color: Color, strokeWidth: number, };


type OutlineProps = { color: Color, strokeWidth: number, };


type PhoneDownProps =
    { variant: 'broken' } & BrokenProps |
    { variant: 'curved' } & CurvedProps |
    { variant: 'duotone' } & DuotoneProps |
    { variant: 'outline' } & OutlineProps;


async function promptForRolloutPercentAsync({
  promptMessage,
}: {
  promptMessage: string;
}): Promise<number> {
  const { name: rolloutPercent } = await promptAsync({
    type: 'text',
    name: 'name',
    format: value => {
      return parseInt(value, 10);
    },
    message: promptMessage,
    initial: 0,
    validate: (rolloutPercent: string): true | string => {
      const floatValue = parseFloat(rolloutPercent);
      return Number.isInteger(floatValue) && floatValue >= 0 && floatValue <= 100
        ? true
        : 'The rollout percentage must be an integer between 0 and 100 inclusive.';
    },
  });
  return rolloutPercent;
}


function getRolloutInfo(getUpdateChannelByNameForAppResult: GetChannelByNameForAppQuery): {
  newBranch: Pick<UpdateBranch, 'name' | 'id'>;
  oldBranch: Pick<UpdateBranch, 'name' | 'id'>;
  currentPercent: number;
} {
  const { branchMapping } = getBranchMapping(
    getUpdateChannelByNameForAppResult.app?.byId.updateChannelByName?.branchMapping
  );
  const [newBranchId, oldBranchId] = branchMapping.data.map(d => d.branchId);
  const newBranch =
    getUpdateChannelByNameForAppResult.app?.byId.updateChannelByName?.updateBranches.filter(
      branch => branch.id === newBranchId
    )[0];
  const oldBranch =
    getUpdateChannelByNameForAppResult.app?.byId.updateChannelByName?.updateBranches.filter(
      branch => branch.id === oldBranchId
    )[0];

  if (!newBranch || !oldBranch) {
    throw new Error(
      `Branch mapping rollout is missing a branch for channel "${getUpdateChannelByNameForAppResult.app?.byId.updateChannelByName?.name}".`
    );
  }

  const currentPercent = 100 * branchMapping.data[0].branchMappingLogic.operand;
  return { newBranch, oldBranch, currentPercent };
}


async function startRolloutAsync({
  channelName,
  branchName,
  percent,
  jsonFlag,
  projectId,
  fullName,
  currentBranchMapping,
  getUpdateChannelByNameForAppResult,
}: {
  channelName?: string;
  branchName?: string;
  percent?: number;
  jsonFlag: boolean;
  projectId: string;
  fullName: string;
  currentBranchMapping: BranchMapping;
  getUpdateChannelByNameForAppResult: GetChannelByNameForAppQuery;
}): Promise<{
  newChannelInfo: {
    id: string;
    name: string;
    branchMapping: string;
  };
  logMessage: string;
}> {
  if (!branchName) {
    const validationMessage = 'A branch must be specified.';
    if (jsonFlag) {
      throw new Error(validationMessage);
    }
    ({ name: branchName } = await promptAsync({
      type: 'text',
      name: 'name',
      message: `Select a branch to rollout onto ${channelName}`,
      validate: value => (value ? true : validationMessage),
    }));
  }
  const branch = await getBranchByNameAsync({ appId: projectId, name: branchName! });
  const oldBranchId = currentBranchMapping.data[0].branchId;
  if (branch.id === oldBranchId) {
    throw new Error(
      `channel "${channelName}" is already pointing at branch "${branchName}". Rollouts must be done with distinct branches.`
    );
  }

  if (percent == null) {
    if (jsonFlag) {
      throw new Error(
        'You must specify a percent with the --percent flag when initiating a rollout with the --json flag.'
      );
    }
    const promptMessage = `What percent of users should be directed to the branch "${branchName}"?`;
    percent = await promptForRolloutPercentAsync({ promptMessage });
  }

  const newBranchMapping = {
    version: 0,
    data: [
      {
        branchId: branch.id,
        branchMappingLogic: {
          operand: percent / 100,
          clientKey: 'rolloutToken',
          branchMappingOperator: 'hash_lt',
        },
      },
      currentBranchMapping.data[0],
    ],
  };
  const newChannelInfo = await updateChannelBranchMappingAsync({
    channelId: getUpdateChannelByNameForAppResult.app?.byId.updateChannelByName?.id!,
    branchMapping: JSON.stringify(newBranchMapping),
  });

  const oldBranch =
    getUpdateChannelByNameForAppResult.app?.byId.updateChannelByName?.updateBranches.filter(
      branch => branch.id === oldBranchId
    )[0];
  if (!oldBranch) {
    throw new Error(
      `Branch mapping is missing its only branch for channel "${channelName}" on app "${fullName}"`
    );
  }

  const logMessage = `Started a rollout of branch ${chalk.bold(
    branchName
  )} onto channel ${chalk.bold(channelName!)}! ${chalk.bold(
    percent
  )}% of users will be directed to branch ${chalk.bold(branchName)}, ${chalk.bold(
    100 - percent
  )}% to branch ${chalk.bold(oldBranch.name)}.`;

  return { newChannelInfo, logMessage };
}


async function editRolloutAsync({
  channelName,
  percent,
  jsonFlag,
  currentBranchMapping,
  getUpdateChannelByNameForAppResult,
}: {
  channelName?: string;
  percent?: number;
  jsonFlag: boolean;
  currentBranchMapping: BranchMapping;
  getUpdateChannelByNameForAppResult: GetChannelByNameForAppQuery;
}): Promise<{
  newChannelInfo: {
    id: string;
    name: string;
    branchMapping: string;
  };
  logMessage: string;
}> {
  const { newBranch, oldBranch, currentPercent } = getRolloutInfo(
    getUpdateChannelByNameForAppResult
  );

  if (percent == null) {
    if (jsonFlag) {
      throw new Error(
        'A rollout is already in progress. If you wish to modify it you must use specify the new rollout percentage with the --percent flag.'
      );
    }
    const promptMessage = `Currently ${currentPercent}% of all users are routed to branch ${
      newBranch.name
    } and ${100 - currentPercent}% of all users are routed to branch ${
      oldBranch.name
    }. What percent of users should be directed to the branch ${newBranch.name}?`;
    percent = await promptForRolloutPercentAsync({ promptMessage });
  }

  const newBranchMapping = { ...currentBranchMapping };
  newBranchMapping.data[0].branchMappingLogic.operand = percent / 100;

  const newChannelInfo = await updateChannelBranchMappingAsync({
    channelId: getUpdateChannelByNameForAppResult.app?.byId.updateChannelByName?.id!,
    branchMapping: JSON.stringify(newBranchMapping),
  });

  const logMessage = `Rollout of branch ${chalk.bold(newBranch.name)} onto channel ${chalk.bold(
    channelName!
  )} updated from ${chalk.bold(currentPercent)}% to ${chalk.bold(percent)}%. ${chalk.bold(
    percent
  )}% of users will be directed to branch ${chalk.bold(newBranch.name)}, ${chalk.bold(
    100 - percent
  )}% to branch ${chalk.bold(oldBranch.name)}.`;

  return { newChannelInfo, logMessage };
}


async function endRolloutAsync({
  channelName,
  branchName,
  jsonFlag,
  projectId,
  getUpdateChannelByNameForAppResult,
}: {
  channelName?: string;
  branchName?: string;
  jsonFlag: boolean;
  projectId: string;
  getUpdateChannelByNameForAppResult: GetChannelByNameForAppQuery;
}): Promise<{
  newChannelInfo: {
    id: string;
    name: string;
    branchMapping: string;
  };
  logMessage: string;
}> {
  // end rollout
  const { newBranch, oldBranch, currentPercent } = getRolloutInfo(
    getUpdateChannelByNameForAppResult
  );

  let endOnNewBranch;
  if (branchName) {
    const branch = await getBranchByNameAsync({ appId: projectId, name: branchName! });
    switch (branch.id) {
      case newBranch.id:
        endOnNewBranch = true;
        break;
      case oldBranch.id:
        endOnNewBranch = false;
        break;
      default:
        throw new Error(
          `The branch "${branchName}" specified by --branch must be one of the branches involved in the rollout: "${newBranch.name}" or "${oldBranch.name}".`
        );
    }
  } else {
    if (jsonFlag) {
      throw new Error(
        'Branch name must be specified with the --branch flag when both the --end and --json flag are true.'
      );
    }
    endOnNewBranch = await selectAsync<boolean>(
      'Ending the rollout will send all traffic to a single branch. Which one should that be?',
      [
        {
          title: `${newBranch.name} ${chalk.grey(`- current percent: ${100 - currentPercent}%`)}`,
          value: true,
        },
        {
          title: `${oldBranch.name} ${chalk.grey(`- current percent: ${currentPercent}%`)}`,
          value: false,
        },
      ]
    );
  }
  if (endOnNewBranch == null) {
    throw new Error('Branch to end on is undefined.');
  }

  const newBranchMapping = {
    version: 0,
    data: [
      {
        branchId: endOnNewBranch ? newBranch.id : oldBranch.id,
        branchMappingLogic: 'true',
      },
    ],
  };

  const newChannelInfo = await updateChannelBranchMappingAsync({
    channelId: getUpdateChannelByNameForAppResult.app?.byId.updateChannelByName?.id!,
    branchMapping: JSON.stringify(newBranchMapping),
  });
  const logMessage = `Rollout on channel ${chalk.bold(
    channelName
  )} ended. All traffic is now sent to branch ${chalk.bold(
    endOnNewBranch ? newBranch.name : oldBranch.name
  )}`;

  return { newChannelInfo, logMessage };
}


value => {
      return parseInt(value, 10);
    }


(rolloutPercent: string): true | string => {
      const floatValue = parseFloat(rolloutPercent);
      return Number.isInteger(floatValue) && floatValue >= 0 && floatValue <= 100
        ? true
        : 'The rollout percentage must be an integer between 0 and 100 inclusive.';
    }


d => d.branchId


branch => branch.id === newBranchId


branch => branch.id === oldBranchId


value => (value ? true : validationMessage)


export default class ChannelRollout extends EasCommand {
  static hidden = true;
  static description = 'Rollout a new branch out to a channel incrementally.';

  static args = [
    {
      name: 'channel',
      required: true,
      description: 'rollout that the channel is on',
    },
  ];

  static flags = {
    branch: flags.string({
      description: 'branch to rollout',
      required: false,
    }),
    percent: flags.integer({
      description: 'percent of traffic to redirect to the new branch',
      required: false,
    }),
    end: flags.boolean({
      description: 'end the rollout',
      default: false,
    }),
    json: flags.boolean({
      description: 'print output as a JSON object with the new channel ID, name and branch mapping',
      default: false,
    }),
  };

  async runAsync(): Promise<void> {
    const {
      args: { channel: channelName },
      flags: { json: jsonFlag, end: endFlag },
    } = this.parse(ChannelRollout);
    const {
      flags: { branch: branchName, percent },
    } = this.parse(ChannelRollout);

    const projectDir = await findProjectRootAsync(process.cwd());
    if (!projectDir) {
      throw new Error('Please run this command inside a project directory.');
    }
    const { exp } = getConfig(projectDir, { skipSDKVersionRequirement: true });
    const fullName = await getProjectFullNameAsync(exp);
    const projectId = await getProjectIdAsync(exp);

    const getUpdateChannelByNameForAppResult = await getUpdateChannelByNameForAppAsync({
      appId: projectId,
      channelName: channelName!,
    });
    const { branchMapping: currentBranchMapping, isRollout } = getBranchMapping(
      getUpdateChannelByNameForAppResult.app?.byId.updateChannelByName?.branchMapping
    );

    if (currentBranchMapping.data.length === 0) {
      throw new Error('The channel is not pointing at any branches.');
    }
    if (currentBranchMapping.data.length > 2) {
      throw new Error('"channel:rollout" cannot handle branch mappings with more than 2 branches.');
    }
    // This combination doesn't make sense. Throw an error explaining the options.
    if (isRollout && branchName && !endFlag) {
      throw new Error(
        `There is a rollout in progress. You can only either edit the rollout percent or 'end' it.`
      );
    }

    /**
     * This if/else block has three branches:
     *  1. The branch mapping is not a rollout, i.e. it is pointing to a single branch.
     *  2. The branch mapping is a rollout.
     *    a. increase/decrease the rollout percentage.
     *    b. end the rollout.
     */
    let rolloutMutationResult: {
      newChannelInfo: {
        id: string;
        name: string;
        branchMapping: string;
      };
      logMessage: string;
    };
    if (!isRollout) {
      rolloutMutationResult = await startRolloutAsync({
        channelName,
        branchName,
        percent,
        jsonFlag,
        projectId,
        fullName,
        currentBranchMapping,
        getUpdateChannelByNameForAppResult,
      });
    } else if (endFlag) {
      rolloutMutationResult = await endRolloutAsync({
        channelName,
        branchName,
        jsonFlag,
        projectId,
        getUpdateChannelByNameForAppResult,
      });
    } else {
      rolloutMutationResult = await editRolloutAsync({
        channelName,
        percent,
        jsonFlag,
        currentBranchMapping,
        getUpdateChannelByNameForAppResult,
      });
    }
    if (!rolloutMutationResult) {
      throw new Error('rollout result is empty');
    }
    const { newChannelInfo, logMessage } = rolloutMutationResult;
    if (jsonFlag) {
      Log.log(JSON.stringify(newChannelInfo));
      return;
    }
    Log.withTick(logMessage);
  }
}


async runAsync(): Promise<void> {
    const {
      args: { channel: channelName },
      flags: { json: jsonFlag, end: endFlag },
    } = this.parse(ChannelRollout);
    const {
      flags: { branch: branchName, percent },
    } = this.parse(ChannelRollout);

    const projectDir = await findProjectRootAsync(process.cwd());
    if (!projectDir) {
      throw new Error('Please run this command inside a project directory.');
    }
    const { exp } = getConfig(projectDir, { skipSDKVersionRequirement: true });
    const fullName = await getProjectFullNameAsync(exp);
    const projectId = await getProjectIdAsync(exp);

    const getUpdateChannelByNameForAppResult = await getUpdateChannelByNameForAppAsync({
      appId: projectId,
      channelName: channelName!,
    });
    const { branchMapping: currentBranchMapping, isRollout } = getBranchMapping(
      getUpdateChannelByNameForAppResult.app?.byId.updateChannelByName?.branchMapping
    );

    if (currentBranchMapping.data.length === 0) {
      throw new Error('The channel is not pointing at any branches.');
    }
    if (currentBranchMapping.data.length > 2) {
      throw new Error('"channel:rollout" cannot handle branch mappings with more than 2 branches.');
    }
    // This combination doesn't make sense. Throw an error explaining the options.
    if (isRollout && branchName && !endFlag) {
      throw new Error(
        `There is a rollout in progress. You can only either edit the rollout percent or 'end' it.`
      );
    }

    /**
     * This if/else block has three branches:
     *  1. The branch mapping is not a rollout, i.e. it is pointing to a single branch.
     *  2. The branch mapping is a rollout.
     *    a. increase/decrease the rollout percentage.
     *    b. end the rollout.
     */
    let rolloutMutationResult: {
      newChannelInfo: {
        id: string;
        name: string;
        branchMapping: string;
      };
      logMessage: string;
    };
    if (!isRollout) {
      rolloutMutationResult = await startRolloutAsync({
        channelName,
        branchName,
        percent,
        jsonFlag,
        projectId,
        fullName,
        currentBranchMapping,
        getUpdateChannelByNameForAppResult,
      });
    } else if (endFlag) {
      rolloutMutationResult = await endRolloutAsync({
        channelName,
        branchName,
        jsonFlag,
        projectId,
        getUpdateChannelByNameForAppResult,
      });
    } else {
      rolloutMutationResult = await editRolloutAsync({
        channelName,
        percent,
        jsonFlag,
        currentBranchMapping,
        getUpdateChannelByNameForAppResult,
      });
    }
    if (!rolloutMutationResult) {
      throw new Error('rollout result is empty');
    }
    const { newChannelInfo, logMessage } = rolloutMutationResult;
    if (jsonFlag) {
      Log.log(JSON.stringify(newChannelInfo));
      return;
    }
    Log.withTick(logMessage);
  }


export declare type LabelPosition = 'top' | 'side';


/**
* Represents a {@link https://www.terraform.io/docs/providers/aws/r/networkfirewall_resource_policy.html aws_networkfirewall_resource_policy}
*/
export class NetworkfirewallResourcePolicy extends cdktf.TerraformResource {

  // =================
  // STATIC PROPERTIES
  // =================
  public static readonly tfResourceType: string = "aws_networkfirewall_resource_policy";

  // ===========
  // INITIALIZER
  // ===========

  /**
  * Create a new {@link https://www.terraform.io/docs/providers/aws/r/networkfirewall_resource_policy.html aws_networkfirewall_resource_policy} Resource
  *
  * @param scope The scope in which to define this construct
  * @param id The scoped construct ID. Must be unique amongst siblings in the same scope
  * @param options NetworkfirewallResourcePolicyConfig
  */
  public constructor(scope: Construct, id: string, config: NetworkfirewallResourcePolicyConfig) {
    super(scope, id, {
      terraformResourceType: 'aws_networkfirewall_resource_policy',
      terraformGeneratorMetadata: {
        providerName: 'aws'
      },
      provider: config.provider,
      dependsOn: config.dependsOn,
      count: config.count,
      lifecycle: config.lifecycle
    });
    this._policy = config.policy;
    this._resourceArn = config.resourceArn;
  }

  // ==========
  // ATTRIBUTES
  // ==========

  // id - computed: true, optional: true, required: false
  public get id() {
    return this.getStringAttribute('id');
  }

  // policy - computed: false, optional: false, required: true
  private _policy?: string;
  public get policy() {
    return this.getStringAttribute('policy');
  }
  public set policy(value: string) {
    this._policy = value;
  }
  // Temporarily expose input value. Use with caution.
  public get policyInput() {
    return this._policy;
  }

  // resource_arn - computed: false, optional: false, required: true
  private _resourceArn?: string;
  public get resourceArn() {
    return this.getStringAttribute('resource_arn');
  }
  public set resourceArn(value: string) {
    this._resourceArn = value;
  }
  // Temporarily expose input value. Use with caution.
  public get resourceArnInput() {
    return this._resourceArn;
  }

  // =========
  // SYNTHESIS
  // =========

  protected synthesizeAttributes(): { [name: string]: any } {
    return {
      policy: cdktf.stringToTerraform(this._policy),
      resource_arn: cdktf.stringToTerraform(this._resourceArn),
    };
  }
}


/**
* AWS Network Firewall
*/
export interface NetworkfirewallResourcePolicyConfig extends cdktf.TerraformMetaArguments {
  /**
  * Docs at Terraform Registry: {@link https://www.terraform.io/docs/providers/aws/r/networkfirewall_resource_policy.html#policy NetworkfirewallResourcePolicy#policy}
  */
  readonly policy: string;
  /**
  * Docs at Terraform Registry: {@link https://www.terraform.io/docs/providers/aws/r/networkfirewall_resource_policy.html#resource_arn NetworkfirewallResourcePolicy#resource_arn}
  */
  readonly resourceArn: string;
}


// =========
// SYNTHESIS
// =========
protected synthesizeAttributes(): { [name: string]: any } {
    return {
      policy: cdktf.stringToTerraform(this._policy),
      resource_arn: cdktf.stringToTerraform(this._resourceArn),
    };
  }


function ConfirmOrCancel({
  disabled = false,
  loading = false,
  label = "Confirm",
  onConfirm,
  onCancel,
}: Props) {
  return (

        <Button
          onClick={onConfirm}
          label="Confirm"
          fullWidth
          primary
          disabled={disabled}
          loading={loading}
        />

        Only connect with sites you trust.

        Cancel

  );
}


type Props = {
  disabled?: boolean;
  loading?: boolean;
  label?: string;
  onConfirm: MouseEventHandler;
  onCancel: MouseEventHandler;
};


(resolve) => {
    setTimeout(resolve, ms)
  }


interface FormUrlArgs {
    create?: string;      // 新增url
    retrieve?: string;    // 查询url
    update?: string;      // 更新url
    delete?: string;      // 删除url
    cou?: string;         // 新增或更新url
    upload?: string;      // 文件上传
}


export interface FormArgs {
    urls?: FormUrlArgs;   // crud操作urls
    method?: string;      // http请求方式，GET/POST
    entity?: any;         // 存放待提交数据的实体，用于与模版进行数据绑定
    params?: any;         // 请求参数
    key?: string;         // 后台接受参数的属性所在的对象路径
    uploadKey?: string;   // 后台接受file文件的对象路径
}


export function ThemePropertyPane() {
  const themingStack = useSelector(getAppThemingStack);
  const themingMode = last(themingStack);

  /**
   * renders the theming property pane:
   *
   * 1. if THEME_EDIT -> ThemeEditor
   * 2. if THEME_SELECTION -> ThemeSelector
   */
  const propertyPane = useMemo(() => {
    switch (true) {
      case themingMode === AppThemingMode.APP_THEME_EDIT:
        return <ThemeEditor />;
      case themingMode === AppThemingMode.APP_THEME_SELECTION:
        return <ThemeSelector />;
      default:
        return <ThemeEditor />;
    }
  }, [themingMode]);

  return
{propertyPane}
;
}


() => {
    switch (true) {
      case themingMode === AppThemingMode.APP_THEME_EDIT:
        return <ThemeEditor />;
      case themingMode === AppThemingMode.APP_THEME_SELECTION:
        return <ThemeSelector />;
      default:
        return <ThemeEditor />;
    }
  }


export class AdventarClient {
  constructor (hostname: string,
               credentials: null | { [index: string]: string; },
               options: null | { [index: string]: string; });

  listCalendars(
    request: ListCalendarsRequest,
    metadata: grpcWeb.Metadata,
    callback: (err: grpcWeb.Error,
               response: ListCalendarsResponse) => void
  ): grpcWeb.ClientReadableStream<ListCalendarsResponse>;

  getCalendar(
    request: GetCalendarRequest,
    metadata: grpcWeb.Metadata,
    callback: (err: grpcWeb.Error,
               response: GetCalendarResponse) => void
  ): grpcWeb.ClientReadableStream<GetCalendarResponse>;

  createCalendar(
    request: CreateCalendarRequest,
    metadata: grpcWeb.Metadata,
    callback: (err: grpcWeb.Error,
               response: Calendar) => void
  ): grpcWeb.ClientReadableStream<Calendar>;

  updateCalendar(
    request: UpdateCalendarRequest,
    metadata: grpcWeb.Metadata,
    callback: (err: grpcWeb.Error,
               response: Calendar) => void
  ): grpcWeb.ClientReadableStream<Calendar>;

  deleteCalendar(
    request: DeleteCalendarRequest,
    metadata: grpcWeb.Metadata,
    callback: (err: grpcWeb.Error,
               response: Empty) => void
  ): grpcWeb.ClientReadableStream<Empty>;

  listCalendarStats(
    request: ListCalendarStatsRequest,
    metadata: grpcWeb.Metadata,
    callback: (err: grpcWeb.Error,
               response: ListCalendarStatsResponse) => void
  ): grpcWeb.ClientReadableStream<ListCalendarStatsResponse>;

  listEntries(
    request: ListEntriesRequest,
    metadata: grpcWeb.Metadata,
    callback: (err: grpcWeb.Error,
               response: ListEntriesResponse) => void
  ): grpcWeb.ClientReadableStream<ListEntriesResponse>;

  createEntry(
    request: CreateEntryRequest,
    metadata: grpcWeb.Metadata,
    callback: (err: grpcWeb.Error,
               response: Entry) => void
  ): grpcWeb.ClientReadableStream<Entry>;

  updateEntry(
    request: UpdateEntryRequest,
    metadata: grpcWeb.Metadata,
    callback: (err: grpcWeb.Error,
               response: Entry) => void
  ): grpcWeb.ClientReadableStream<Entry>;

  deleteEntry(
    request: DeleteEntryRequest,
    metadata: grpcWeb.Metadata,
    callback: (err: grpcWeb.Error,
               response: Empty) => void
  ): grpcWeb.ClientReadableStream<Empty>;

  signIn(
    request: SignInRequest,
    metadata: grpcWeb.Metadata,
    callback: (err: grpcWeb.Error,
               response: User) => void
  ): grpcWeb.ClientReadableStream<User>;

  getUser(
    request: GetUserRequest,
    metadata: grpcWeb.Metadata,
    callback: (err: grpcWeb.Error,
               response: User) => void
  ): grpcWeb.ClientReadableStream<User>;

  updateUser(
    request: UpdateUserRequest,
    metadata: grpcWeb.Metadata,
    callback: (err: grpcWeb.Error,
               response: User) => void
  ): grpcWeb.ClientReadableStream<User>;

  healthCheck(
    request: HealthCheckRequest,
    metadata: grpcWeb.Metadata,
    callback: (err: grpcWeb.Error,
               response: HealthCheckResponse) => void
  ): grpcWeb.ClientReadableStream<HealthCheckResponse>;

}


export class AdventarPromiseClient {
  constructor (hostname: string,
               credentials: null | { [index: string]: string; },
               options: null | { [index: string]: string; });

  listCalendars(
    request: ListCalendarsRequest,
    metadata: grpcWeb.Metadata
  ): Promise<ListCalendarsResponse>;

  getCalendar(
    request: GetCalendarRequest,
    metadata: grpcWeb.Metadata
  ): Promise<GetCalendarResponse>;

  createCalendar(
    request: CreateCalendarRequest,
    metadata: grpcWeb.Metadata
  ): Promise<Calendar>;

  updateCalendar(
    request: UpdateCalendarRequest,
    metadata: grpcWeb.Metadata
  ): Promise<Calendar>;

  deleteCalendar(
    request: DeleteCalendarRequest,
    metadata: grpcWeb.Metadata
  ): Promise<Empty>;

  listCalendarStats(
    request: ListCalendarStatsRequest,
    metadata: grpcWeb.Metadata
  ): Promise<ListCalendarStatsResponse>;

  listEntries(
    request: ListEntriesRequest,
    metadata: grpcWeb.Metadata
  ): Promise<ListEntriesResponse>;

  createEntry(
    request: CreateEntryRequest,
    metadata: grpcWeb.Metadata
  ): Promise<Entry>;

  updateEntry(
    request: UpdateEntryRequest,
    metadata: grpcWeb.Metadata
  ): Promise<Entry>;

  deleteEntry(
    request: DeleteEntryRequest,
    metadata: grpcWeb.Metadata
  ): Promise<Empty>;

  signIn(
    request: SignInRequest,
    metadata: grpcWeb.Metadata
  ): Promise<User>;

  getUser(
    request: GetUserRequest,
    metadata: grpcWeb.Metadata
  ): Promise<User>;

  updateUser(
    request: UpdateUserRequest,
    metadata: grpcWeb.Metadata
  ): Promise<User>;

  healthCheck(
    request: HealthCheckRequest,
    metadata: grpcWeb.Metadata
  ): Promise<HealthCheckResponse>;

}


listCalendars(
    request: ListCalendarsRequest,
    metadata: grpcWeb.Metadata,
    callback: (err: grpcWeb.Error,
               response: ListCalendarsResponse) => void
  ): grpcWeb.ClientReadableStream<ListCalendarsResponse>;


getCalendar(
    request: GetCalendarRequest,
    metadata: grpcWeb.Metadata,
    callback: (err: grpcWeb.Error,
               response: GetCalendarResponse) => void
  ): grpcWeb.ClientReadableStream<GetCalendarResponse>;


createCalendar(
    request: CreateCalendarRequest,
    metadata: grpcWeb.Metadata,
    callback: (err: grpcWeb.Error,
               response: Calendar) => void
  ): grpcWeb.ClientReadableStream<Calendar>;


updateCalendar(
    request: UpdateCalendarRequest,
    metadata: grpcWeb.Metadata,
    callback: (err: grpcWeb.Error,
               response: Calendar) => void
  ): grpcWeb.ClientReadableStream<Calendar>;


deleteCalendar(
    request: DeleteCalendarRequest,
    metadata: grpcWeb.Metadata,
    callback: (err: grpcWeb.Error,
               response: Empty) => void
  ): grpcWeb.ClientReadableStream<Empty>;


listCalendarStats(
    request: ListCalendarStatsRequest,
    metadata: grpcWeb.Metadata,
    callback: (err: grpcWeb.Error,
               response: ListCalendarStatsResponse) => void
  ): grpcWeb.ClientReadableStream<ListCalendarStatsResponse>;


listEntries(
    request: ListEntriesRequest,
    metadata: grpcWeb.Metadata,
    callback: (err: grpcWeb.Error,
               response: ListEntriesResponse) => void
  ): grpcWeb.ClientReadableStream<ListEntriesResponse>;


createEntry(
    request: CreateEntryRequest,
    metadata: grpcWeb.Metadata,
    callback: (err: grpcWeb.Error,
               response: Entry) => void
  ): grpcWeb.ClientReadableStream<Entry>;


updateEntry(
    request: UpdateEntryRequest,
    metadata: grpcWeb.Metadata,
    callback: (err: grpcWeb.Error,
               response: Entry) => void
  ): grpcWeb.ClientReadableStream<Entry>;


deleteEntry(
    request: DeleteEntryRequest,
    metadata: grpcWeb.Metadata,
    callback: (err: grpcWeb.Error,
               response: Empty) => void
  ): grpcWeb.ClientReadableStream<Empty>;


signIn(
    request: SignInRequest,
    metadata: grpcWeb.Metadata,
    callback: (err: grpcWeb.Error,
               response: User) => void
  ): grpcWeb.ClientReadableStream<User>;


getUser(
    request: GetUserRequest,
    metadata: grpcWeb.Metadata,
    callback: (err: grpcWeb.Error,
               response: User) => void
  ): grpcWeb.ClientReadableStream<User>;


updateUser(
    request: UpdateUserRequest,
    metadata: grpcWeb.Metadata,
    callback: (err: grpcWeb.Error,
               response: User) => void
  ): grpcWeb.ClientReadableStream<User>;


healthCheck(
    request: HealthCheckRequest,
    metadata: grpcWeb.Metadata,
    callback: (err: grpcWeb.Error,
               response: HealthCheckResponse) => void
  ): grpcWeb.ClientReadableStream<HealthCheckResponse>;


listCalendars(
    request: ListCalendarsRequest,
    metadata: grpcWeb.Metadata
  ): Promise<ListCalendarsResponse>;


getCalendar(
    request: GetCalendarRequest,
    metadata: grpcWeb.Metadata
  ): Promise<GetCalendarResponse>;


createCalendar(
    request: CreateCalendarRequest,
    metadata: grpcWeb.Metadata
  ): Promise<Calendar>;


updateCalendar(
    request: UpdateCalendarRequest,
    metadata: grpcWeb.Metadata
  ): Promise<Calendar>;


deleteCalendar(
    request: DeleteCalendarRequest,
    metadata: grpcWeb.Metadata
  ): Promise<Empty>;


listCalendarStats(
    request: ListCalendarStatsRequest,
    metadata: grpcWeb.Metadata
  ): Promise<ListCalendarStatsResponse>;


listEntries(
    request: ListEntriesRequest,
    metadata: grpcWeb.Metadata
  ): Promise<ListEntriesResponse>;


createEntry(
    request: CreateEntryRequest,
    metadata: grpcWeb.Metadata
  ): Promise<Entry>;


updateEntry(
    request: UpdateEntryRequest,
    metadata: grpcWeb.Metadata
  ): Promise<Entry>;


deleteEntry(
    request: DeleteEntryRequest,
    metadata: grpcWeb.Metadata
  ): Promise<Empty>;


signIn(
    request: SignInRequest,
    metadata: grpcWeb.Metadata
  ): Promise<User>;


getUser(
    request: GetUserRequest,
    metadata: grpcWeb.Metadata
  ): Promise<User>;


updateUser(
    request: UpdateUserRequest,
    metadata: grpcWeb.Metadata
  ): Promise<User>;


healthCheck(
    request: HealthCheckRequest,
    metadata: grpcWeb.Metadata
  ): Promise<HealthCheckResponse>;


export default function Form(props: FormProps): JSX.Element {
  const { submitButtonText = 'submit', errorMessages = [], onSubmit, className, children, ...formProps } = props;
  const [loading, setLoading] = React.useState(false);

  function handleSubmit(e) {
    setLoading(true);
    Promise.resolve(onSubmit(e)).finally(() => setLoading(false));
    e.preventDefault();
  }

  return (
    <form className={classnames('form', className)} onSubmit={handleSubmit} {...formProps}>
      {children}
      {errorMessages.map((errorMessage, index) => (
        <div className="error-message" key={index} data-testid="formErrorMessage">
          {errorMessage}
        </div>
      ))}
      <PrimaryButton className="submit-button" disabled={loading} data-testid="formSubmitButton">
        {submitButtonText}
      </PrimaryButton>
    </form>
  );
}


function handleSubmit(e) {
    setLoading(true);
    Promise.resolve(onSubmit(e)).finally(() => setLoading(false));
    e.preventDefault();
  }


(errorMessage, index) => (
        <div className="error-message"


interface FormProps extends ComponentPropsWithoutRef<'form'> {
  errorMessages?: string[];
  submitButtonText?: string;
}


classnames('form', className)


export function EnjoyNotification({
  notifications,
  toggleNotifications,
}: EnjoyNotificationReduxProps & EnjoyNotificationReduxDispatch): JSX.Element {
  const navigation = useNavigation()
  const { correct } = useVibrations()
  const { bottom } = useSafeAreaWithPadding()
  const { colors } = useTheme()

  function onExit(): void {
    navigation.goBack()
  }

  function onHold(): void {
    requestReview()
    navigation.goBack()
  }

  return (
    <Container>
      <Header onPress={onExit} />
      <Content>
        <InfoScreenWithButton
          emoji={'🎉'}
          heading={'Having fun?'}
          description={'Maybe give a rating and setup weekly notifications'}
        />
        <Toggle
          text={'Notifications'}
          description={'Setup weekly notifications for practice.'}
          enabled={notifications}
          onValueChange={toggleNotifications}
        />
      </Content>
      <View style={[{ paddingBottom: bottom }, styles.ButtonContainer]}>
        <CourseButton
          text={'Hold to rate'}
          marker={'🤩'}
          finalColor={colors.primary.buttonSuccessColor}
          vibrationMethod={correct}
          onHold={onHold}
        />
      </View>
    </Container>
  )
}


function onExit(): void {
    navigation.goBack()
  }


function onHold(): void {
    requestReview()
    navigation.goBack()
  }


export interface EnjoyNotificationReduxProps {
  readonly notifications: boolean
}


export interface EnjoyNotificationReduxDispatch {
  readonly toggleNotifications: (value: boolean) => void
}


(entry) => {
      const [key, value] = entry
      const val: { answer: string, questions: string[]}[] = Object.entries(value).map((ans) => {
        return { answer: ans[0], questions: ans[1] }
      })
      val.sort((a, b) => b.questions.length - a.questions.length)
      return [key, val]
    }


(ans) => {
        return { answer: ans[0], questions: ans[1] }
      }


(a, b) => b.questions.length - a.questions.length


(e) => !this.excludeFilter.length || this.exclude !== !this.excludeFilter.includes(e.answer)


(question) => question.answer


export class PracticeModule extends VuexModule {
  constructor () {
    super()

    this.frequencyLists = Object.fromEntries(Object.entries(freqLists).map((entry) => {
      const [key, value] = entry
      const val: { answer: string, questions: string[]}[] = Object.entries(value).map((ans) => {
        return { answer: ans[0], questions: ans[1] }
      })
      val.sort((a, b) => b.questions.length - a.questions.length)
      return [key, val]
    })) as { [k in keyof typeof freqLists]: { answer: string, questions: string[] }[] }
  }

  frequencyLists: { [k in keyof typeof freqLists]: { answer: string, questions: string[] }[] }

  frequencyListFilter: keyof typeof freqLists | '' = ''
  lowerBoundFilter = 0
  upperBoundFilter = 50
  excludeFilter: string[] = []
  exclude = false

  get validQuestions (): { answer: string, questions: string[]}[] {
    if (this.frequencyListFilter === '' || !this.frequencyListFilter) {
      return []
    }
    return this.frequencyLists[this.frequencyListFilter]
      .slice(this.lowerBoundFilter, this.upperBoundFilter)
      .filter((e) => !this.excludeFilter.length || this.exclude !== !this.excludeFilter.includes(e.answer))
  }

  get validAnswers (): string[] {
    return this.validQuestions.map((question) => question.answer)
  }
}


@Injectable()
export class UserService extends UserData {

  private time: Date = new Date;

  private users = {
    nick: { name: 'Utilisateur', picture: 'assets/user.svg' },
    eva: { name: 'Eva Moor', picture: 'assets/images/eva.png' },
    jack: { name: 'Jack Williams', picture: 'assets/images/jack.png' },
    lee: { name: 'Lee Wong', picture: 'assets/images/lee.png' },
    alan: { name: 'Alan Thompson', picture: 'assets/images/alan.png' },
    kate: { name: 'Kate Martinez', picture: 'assets/images/kate.png' },
  };
  private types = {
    mobile: 'mobile',
    home: 'home',
    work: 'work',
  };
  private contacts: Contacts[] = [
    { user: this.users.nick, type: this.types.mobile },
    { user: this.users.eva, type: this.types.home },
    { user: this.users.jack, type: this.types.mobile },
    { user: this.users.lee, type: this.types.mobile },
    { user: this.users.alan, type: this.types.home },
    { user: this.users.kate, type: this.types.work },
  ];
  private recentUsers: RecentUsers[]  = [
    { user: this.users.alan, type: this.types.home, time: this.time.setHours(21, 12)},
    { user: this.users.eva, type: this.types.home, time: this.time.setHours(17, 45)},
    { user: this.users.nick, type: this.types.mobile, time: this.time.setHours(5, 29)},
    { user: this.users.lee, type: this.types.mobile, time: this.time.setHours(11, 24)},
    { user: this.users.jack, type: this.types.mobile, time: this.time.setHours(10, 45)},
    { user: this.users.kate, type: this.types.work, time: this.time.setHours(9, 42)},
    { user: this.users.kate, type: this.types.work, time: this.time.setHours(9, 31)},
    { user: this.users.jack, type: this.types.mobile, time: this.time.setHours(8, 0)},
  ];

  getUsers(): Observable<any> {
    return observableOf(this.users);
  }

  getContacts(): Observable<Contacts[]> {
    return observableOf(this.contacts);
  }

  getRecentUsers(): Observable<RecentUsers[]> {
    return observableOf(this.recentUsers);
  }
}


getUsers(): Observable<any> {
    return observableOf(this.users);
  }


getContacts(): Observable<Contacts[]> {
    return observableOf(this.contacts);
  }


getRecentUsers(): Observable<RecentUsers[]> {
    return observableOf(this.recentUsers);
  }


() => CasPortionType


(casPortionType) => casPortionType.casPayOffPortions


(casPayOffPortion: CasPayOffPortion) => casPayOffPortion.fkfPortionType


@Index('PK_casPayOffPortion', ['pkfPayOffPortion'], { unique: true })
@Entity('casPayOffPortion', { schema: 'dbo' })
export class CasPayOffPortion {
  @PrimaryGeneratedColumn({ type: 'int', name: 'pkfPayOffPortion' })
  pkfPayOffPortion: number;

  @Column('bigint', { name: 'payOff' })
  payOff: string;

  @Column('nvarchar', { name: 'memo', length: 255 })
  memo: string;

  @Column('int', { name: 'FkfPortion' })
  fkfPortion: number;

  @Column('int', { name: 'FKF' })
  fkf: number;

  @ManyToOne(
    () => CasPortionType,
    (casPortionType) => casPortionType.casPayOffPortions,
  )
  @JoinColumn([
    { name: 'FkfPortionType', referencedColumnName: 'pkfPortionType' },
  ])
  fkfPortionType: CasPortionType;

  @RelationId(
    (casPayOffPortion: CasPayOffPortion) => casPayOffPortion.fkfPortionType,
  )
  fkfPortionType2: number;
}


(user) => user.username === input || user.addresses.includes(input)


({ pair, orders }) =>
        orders
          .map((order) => ({
            id: order.id,
            owner: order.creator,
            pair,
            price: order.price,
            filled: order.originalQuantity - order.quantity,
            quantity: order.originalQuantity,
          }))
          .filter(({ owner }) => owner === address)


(order) => ({
            id: order.id,
            owner: order.creator,
            pair,
            price: order.price,
            filled: order.originalQuantity - order.quantity,
            quantity: order.originalQuantity,
          })


({ owner }) => owner === address


(tx) => tx.node.id === after


({ id }) => id === node.id


(tag) => tag.name === "Input"


(a, b) =>
          (b.timestamp ||
            parseFloat(new Date().getTime().toString().substring(0, 10))) -
          (a.timestamp ||
            parseFloat(new Date().getTime().toString().substring(0, 10)))


/**
   * Fetches the user info for a given wallet address or username.
   * @param input User wallet address or username.
   * @returns The user's data such as name & image, or undefined.
   */
async getUser(input: string): Promise<UserInterface | undefined> {
    let allUsers: CommunityContractPeople[];

    if (this.cache) allUsers = await fetchUsers();
    else {
      const contract = await this.utils.getState(this.utils.COMMUNITY_CONTRACT);
      allUsers = contract.people;
    }

    return allUsers.find(
      (user) => user.username === input || user.addresses.includes(input)
    );
  }


/**
   * Fetches the assets (listed on Verto) for a given wallet address.
   * @param address User wallet address.
   * @returns List of asset ids, balances, names, tickers, & logos.
   */
async getBalances(address: string): Promise<UserBalance[]> {
    if (!this.cache) {
      const balances: UserBalance[] = [];
      const listedTokens = await this.token.getTokens();

      for (const token of listedTokens) {
        const tokenState = await this.utils.getState(token.id);

        if (tokenState?.balances?.[address]) {
          balances.push({
            contractId: token.id,
            name: token.name,
            ticker: token.ticker,
            logo: this.utils.getPSTSettingValue("communityLogo", tokenState),
            balance: tokenState.balances[address],
            userAddress: address,
          });
        }
      }

      return balances;
    } else {
      return await fetchBalancesForAddress(address);
    }
  }


/**
   * Fetches the orders for a given wallet address
   * @param address User wallet address
   * @returns List of orders
   */
async getOrders(address: string): Promise<OrderInterface[]> {
    // get clob contract state
    const clobContractState: {
      [key: string]: any;
      pairs: {
        pair: [string, string];
        orders: {
          [key: string]: any;
        }[];
      }[];
    } = await this.utils.getState(this.utils.CLOB_CONTRACT);

    // map orders
    const allOrders: OrderInterface[][] = clobContractState.pairs.map(
      ({ pair, orders }) =>
        orders
          .map((order) => ({
            id: order.id,
            owner: order.creator,
            pair,
            price: order.price,
            filled: order.originalQuantity - order.quantity,
            quantity: order.originalQuantity,
          }))
          .filter(({ owner }) => owner === address)
    );

    // flatten orders
    return allOrders.flat();
  }


/**
   * Fetches the latest transactions for a given wallet address.
   * @param address User wallet address.
   * @param after Optional latest transaction id, used for pagination.
   * @returns List of transaction ids, statuses, amounts, & timestamps.
   */
async getTransactions(
    address: string,
    after?: string
  ): Promise<TransactionInterface[]> {
    let maxHeight: number | undefined = undefined;

    // get the block height for the after tx
    if (after) {
      const tx = await this.utils.arGQL(
        `
        query($id: ID!) {
          transaction(id: $id) {
            id
            block {
              height
            }
          }
        }
      `,
        { id: after }
      );

      if (tx) {
        maxHeight = tx.data.transaction.block.height;
      }
    }

    // get outgoing txs
    const outTxQuery = await this.utils.arGQL(
      `
      query ($addr: String!, $max: Int) {
        transactions (owners: [$addr], block: { max: $max }) {
          edges {
            node {
              id
              owner {
                address
              }
              recipient
              quantity {
                ar
                winston
              }
              tags {
                name
                value
              }
              block {
                timestamp
              }
            }
          }
        }
      }
    `,
      { addr: address, max: maxHeight }
    );

    // get incoming txs
    const inTxQuery = await this.utils.arGQL(
      `
      query ($addr: String!, $max: Int) {
        transactions (recipients: [$addr], block: { max: $max }) {
          edges {
            node {
              id
              owner {
                address
              }
              recipient
              quantity {
                ar
                winston
              }
              tags {
                name
                value
              }
              block {
                timestamp
              }
            }
          }
        }
      }
    `,
      { addr: address, max: maxHeight }
    );

    // parse data
    let inTxs = inTxQuery.data.transactions.edges;
    let outTxs = outTxQuery.data.transactions.edges;

    // if we are limiting to an "after" tx
    // remove the ones before that tx
    if (after) {
      const inIndex = inTxs.findIndex((tx) => tx.node.id === after);
      const outIndex = outTxs.findIndex((tx) => tx.node.id === after);

      if (inIndex > -1) inTxs = inTxs.slice(inIndex + 1);
      if (outIndex > -1) outTxs = outTxs.slice(outIndex + 1);
    }

    // we'll push the formatted txs here
    const res: TransactionInterface[] = [];

    for (const { node } of [...inTxs, ...outTxs]) {
      if (res.find(({ id }) => id === node.id)) continue;

      const appName = this.utils.getTagValue("App-Name", node.tags);

      let status;
      let amount;

      // do some checks to see if it is a token transfer
      // if it is, we format the amount
      if (appName && appName === "SmartWeaveAction") {
        const input = node.tags.find((tag) => tag.name === "Input");

        if (input) {
          const parsedInput = JSON.parse(input.value);
          const contractID = this.utils.getTagValue("Contract", node.tags);

          if (
            parsedInput.function === "transfer" &&
            parsedInput.qty &&
            contractID
          ) {
            const contract = await fetchContract(contractID, true);

            if (contract) {
              amount = `${parsedInput.qty} ${
                contract.state ? contract.state.ticker : "???"
              }`;

              if (contract.validity && contract.validity[node.id] === false)
                status = "error";
            }
          }
        }
      }

      res.push({
        id: node.id,
        // @ts-expect-error
        status: node.block ? status || "success" : "pending",
        type: node.owner.address === address ? "out" : "in",
        amount: amount || `${parseFloat(node.quantity.ar)} AR`,
        timestamp: node.block && node.block.timestamp,
      });
    }

    return res
      .sort(
        (a, b) =>
          (b.timestamp ||
            parseFloat(new Date().getTime().toString().substring(0, 10))) -
          (a.timestamp ||
            parseFloat(new Date().getTime().toString().substring(0, 10)))
      )
      .slice(0, 5);
  }


@Injectable()
export class Playlist {
  songs: Song[];
  constructor(songsService: SongsService) {
    this.songs = songsService.fetch();
  }
}


@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
    constructor(
        @InjectRepository(UserRepository)
        private userRepository: UserRepository,
    ) {
        super({
            jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
            secretOrKey: process.env.JWT_SECRET || config.get('jwt.secret'),
        });
    }

    async validate(payload: JwtPayload): Promise<User> {
        const { username } = payload;
        const user = await this.userRepository.findOne({ username });

        if (!user) {
            throw new UnauthorizedException();
        }

        return user;
    }
}


async validate(payload: JwtPayload): Promise<User> {
        const { username } = payload;
        const user = await this.userRepository.findOne({ username });

        if (!user) {
            throw new UnauthorizedException();
        }

        return user;
    }


function ConfirmPage(): ReactElement {
	return (
		<Layout title="Confirm Account">
			<h1>Cannot confirm user account, invalid token </h1>
		</Layout>
	);
}


async ({ apolloClient, query: { token }, ...ctx }: DefaultContext) => {
	try {
		const { data } = await apolloClient.mutate<ConfirmUserMutation, ConfirmUserMutationVariables>({
			mutation: confirmUserMutation,
			variables: { token: token as string },
		});

		if (data?.confirmUser) {
			redirect(ctx, "/user/login");
		}
		return { props: {} };
	} catch {
		return { props: {} };
	}
}


/** Callback to inform of a value updates. */
export type StoreSubscriber<T> = (value: T) => void;


/** Unsubscribes from value updates. */
export type StoreUnsubscriber = () => void;


/** Callback to update a value. */
export type StoreUpdater<T> = (value: T) => T;


/** Cleanup logic callback. */
export type StoreInvalidator<T> = (value?: T) => void;


/** Start and stop notification callbacks. */
export type StoreStartStopNotifier<T> = (set: StoreSubscriber<T>) => StoreUnsubscriber | void;


/** Readable interface for subscribing. */
export type ReadableStore<T> = {
  initialValue: T | undefined;

  /**
   * Subscribe on value changes.
   * @param run - subscription callback
   * @param invalidate - cleanup callback
   */
  subscribe(
    this: void,
    run: StoreSubscriber<T>,
    invalidate?: StoreInvalidator<T>,
  ): StoreUnsubscriber;
};


/** Writable interface for both updating and subscribing. */
export type WritableStore<T> = ReadableStore<T> & {
  /**
   * Set value and inform subscribers.
   * @param value - to set
   */
  set(this: void, value: T): void;

  /**
   * Update value using callback and inform subscribers.
   * @param updater - callback
   */
  update(this: void, updater: StoreUpdater<T>): void;
};


/** Pair of subscriber and invalidator. */
export type SubscribeInvalidateTuple<T> = [StoreSubscriber<T>, StoreInvalidator<T>];


export type ReadableStoreRecord = {
  [prop: string]: ReadableStore<any>;
};


export type WritableStoreRecord = {
  [prop: string]: WritableStore<any>;
};


export type StoreValue<T> = T extends ReadableStore<infer U> ? U : never;


/** One or more `Readable`s. */
export type Stores =
  | ReadableStore<any>
  | [ReadableStore<any>, ...Array<ReadableStore<any>>]
  | Array<ReadableStore<any>>;


/** One or more values from `Readable` stores. */
export type StoresValues<T> = T extends ReadableStore<infer U>
  ? U
  : { [K in keyof T]: T[K] extends ReadableStore<infer U> ? U : never };


function getGrantWrapperContentProps (grantType: PromotionType): GrantWrapperContentProps {
  if (grantType === 'ads') {
    return {
      title: getLocale('grantTitleAds'),
      text: getLocale('grantSubtitleAds')
    }
  }
  return {
    title: getLocale('grantTitleUGP'),
    text: getLocale('grantSubtitleUGP')
  }
}


function getGrantCompleteContentProps (grantType: PromotionType): GrantCompleteContentProps {
  if (grantType === 'ads') {
    return {
      amountTitleText: getLocale('grantAmountTitleAds'),
      dateTitleText: getLocale('grantDateTitleAds')
    }
  }
  return {
    amountTitleText: getLocale('grantAmountTitleUGP'),
    dateTitleText: getLocale('grantDateTitleUGP')
  }
}


(promotionId?: string) => {
    this.setState({ loading: true })
    this.actions.claimPromotion(promotionId)
    this.setState({ promotionStep: 'complete' })
  }


(promotionId: string) => {
    this.setState({
      promotionShow: false,
      promotionStep: '',
      loading: false
    })
    this.actions.deletePromotion(promotionId)
  }


(tokens?: string) => {
    const { promotion } = this.props
    const { safetyNetFailed } = this.props.rewardsData

    if (!tokens || !promotion) {
      return false
    }

    if (safetyNetFailed) {
      this.setState({ loading: false })
      return false
    }

    // Promotion types other than Rewards.PromotionTypes.ADS must have
    // a valid expiration
    if (!promotion.expiresAt && promotion.type !== 1) {
      return false
    }

    return tokens !== '0.000'
  }


(state: Rewards.ApplicationState) => ({
  rewardsData: state.rewardsData
})


(dispatch: Dispatch) => ({
  actions: bindActionCreators(rewardsActions, dispatch)
})


// TODO add local when we know what we will get from the server
class Promotion extends React.Component<Props, State> {
  constructor (props: Props) {
    super(props)
    this.state = {
      promotionShow: true,
      promotionStep: '',
      loading: false
    }
  }

  get actions () {
    return this.props.actions
  }

  onClaim = (promotionId?: string) => {
    this.setState({ loading: true })
    this.actions.claimPromotion(promotionId)
    this.setState({ promotionStep: 'complete' })
  }

  onSuccess = (promotionId: string) => {
    this.setState({
      promotionShow: false,
      promotionStep: '',
      loading: false
    })
    this.actions.deletePromotion(promotionId)
  }

  validPromotion = (tokens?: string) => {
    const { promotion } = this.props
    const { safetyNetFailed } = this.props.rewardsData

    if (!tokens || !promotion) {
      return false
    }

    if (safetyNetFailed) {
      this.setState({ loading: false })
      return false
    }

    // Promotion types other than Rewards.PromotionTypes.ADS must have
    // a valid expiration
    if (!promotion.expiresAt && promotion.type !== 1) {
      return false
    }

    return tokens !== '0.000'
  }

  render () {
    const { promotion } = this.props
    const { onlyAnonWallet } = this.props

    if (!promotion) {
      return null
    }

    // Handle that ugp type string can actually be 'android' on android
    let type: PromotionType = promotion.type === 1 ? 'ads' : 'ugp'
    let promoId
    let tokens = '0.000'
    let date = ''

    if (promotion.promotionId) {
      promoId = promotion.promotionId
    }

    if (promotion.amount) {
      tokens = promotion.amount.toFixed(3)
    }

    if (promotion.type !== 1) { // Rewards.PromotionTypes.ADS
      date = new Date(promotion.expiresAt).toLocaleDateString()
    }

    // Guard against null promotion statuses
    const validPromotion = this.validPromotion(tokens)

    return (
      <React.Fragment>
        {
          (this.state.promotionShow && type)
          ? <GrantClaim
            type={type}
            isMobile={true}
            onClaim={this.onClaim.bind(this, promoId)}
            loading={this.state.loading}
          />
          : null
        }


interface State {
  promotionShow: boolean
  promotionStep: Step
  loading: boolean
}


interface Props extends Rewards.ComponentProps {
  promotion: Rewards.Promotion
  onlyAnonWallet?: boolean
}


type Step = '' | 'complete'


type GrantWrapperContentProps = {
  title: string
  text: string
}


type GrantCompleteContentProps = {
  amountTitleText: string
  dateTitleText: string
}


render () {
    const { promotion } = this.props
    const { onlyAnonWallet } = this.props

    if (!promotion) {
      return null
    }

    // Handle that ugp type string can actually be 'android' on android
    let type: PromotionType = promotion.type === 1 ? 'ads' : 'ugp'
    let promoId
    let tokens = '0.000'
    let date = ''

    if (promotion.promotionId) {
      promoId = promotion.promotionId
    }

    if (promotion.amount) {
      tokens = promotion.amount.toFixed(3)
    }

    if (promotion.type !== 1) { // Rewards.PromotionTypes.ADS
      date = new Date(promotion.expiresAt).toLocaleDateString()
    }

    // Guard against null promotion statuses
    const validPromotion = this.validPromotion(tokens)

    return (
      <React.Fragment>
        {
          (this.state.promotionShow && type)
          ? <GrantClaim
            type={type}
            isMobile={true}
            onClaim={this.onClaim.bind(this, promoId)}
            loading={this.state.loading}
          />
          : null
        }


function getTestFiles(): FileInfo[] {
      return [{ path: relativePath, content: fileContent }];
    }


() => {
  const expectedFileList: string[] = [
    join('bundles', 'dx.light.scss'),
    join('bundles', 'dx.material.blue.light.scss'),
    join('widgets', 'generic', 'accordion', '_colors.scss'),
    join('widgets', 'generic', 'accordion', '_index.scss'),
    join('widgets', 'generic', 'accordion', '_sizes.scss'),
    join('widgets', 'generic', 'dateBox', '_index.scss'),
    join('widgets', 'generic', '_colors.scss'),
    join('widgets', 'generic', '_index.scss'),
    join('widgets', 'generic', '_sizes.scss'),
    join('widgets', 'generic', '_variables.scss'),
    join('widgets', 'material', '_index.scss'),
  ];

  promises.mkdir = jest.fn();
  promises.writeFile = jest.fn();

  beforeEach(() => {
    jest.clearAllMocks();
  });

  test('getFileList', async () => {
    const collector = new MetadataCollector();
    const fileList = await collector.getFileList(join(rootDir, 'tests', 'data', 'scss'));
    const expectedFullPaths = expectedFileList.map((file) => join(scssDir, file));

    expect(fileList.length).toBe(expectedFullPaths.length);

    fileList.forEach((file) => expect(expectedFullPaths).toContain(file));
  });

  test('readFiles', async () => {
    const collector = new MetadataCollector();
    const handler = (content: string): string => content;
    const filesInfo = await collector.readFiles(join(rootDir, 'tests', 'data', 'scss'), handler);

    expect(filesInfo.length).toBe(expectedFileList.length);

    filesInfo.forEach((file) => {
      expect(expectedFileList).toContain(file.path);
      expect(typeof file.content).toBe('string');
      expect(file.content.length).toBeGreaterThan(0);
    });
  });

  test('readFiles handle files with additional handler', async () => {
    const collector = new MetadataCollector();
    const handler = (content: string): string => `123${content}`;
    const fileList = await collector.readFiles(join(rootDir, 'tests', 'data', 'scss'), handler);

    fileList.forEach((file) => expect(file.content.slice(0, 3)).toBe('123'));
  });

  test('saveScssFile', async () => {
    const relativePath = join('path', 'filePath.scss');
    const fileContent = 'file content';
    const destinationPath = './scss';
    const expectedDestinationPath = resolve(join(destinationPath, relativePath));
    const expectedDestinationDir = dirname(expectedDestinationPath);

    function getTestFiles(): FileInfo[] {
      return [{ path: relativePath, content: fileContent }];
    }

    await MetadataCollector.saveScssFiles(Promise.resolve(getTestFiles()), destinationPath);

    expect(promises.mkdir).toHaveBeenCalledTimes(1);
    expect(promises.mkdir).toHaveBeenCalledWith(expectedDestinationDir, { recursive: true });
    expect(promises.writeFile).toHaveBeenCalledTimes(1);
    expect(promises.writeFile).toHaveBeenCalledWith(expectedDestinationPath, fileContent);
  });

  test('saveMetadata', async () => {
    const collector = new MetadataCollector();
    const version = '1.1.1';
    const fileName = join('metadata', 'dx-theme-builder-metadata.ts');
    const jsonFileName = join('metadata', 'dx-theme-builder-metadata.json');
    const expectedFileName = resolve(fileName);
    const expectedJsonFileName = resolve(jsonFileName);
    const expectedDirName = dirname(expectedFileName);
    const meta: ThemesMetadata = { generic: [{ Key: '$var', Value: '\'ON\'' }], material: [] };

    collector.generator.metadata = meta;

    let metaContent = 'export const metadata: ThemesMetadata = {\'generic\':[{\'Key\':\'$var\',\'Value\':\'"ON"\'}],\'material\':[]};\n';
    metaContent += `export const version: string = '${version}';\n`;
    metaContent += 'export const browsersList: Array<string> = [];\n';
    metaContent += 'export const dependencies: FlatStylesDependencies = {};\n';

    const jsonMetaContent = JSON.stringify(meta);

    await collector.saveMetadata(fileName, jsonFileName, version, [], {});

    expect(promises.mkdir).toHaveBeenCalledTimes(2);
    expect(promises.mkdir).toHaveBeenCalledWith(expectedDirName, { recursive: true });
    expect(promises.writeFile).toHaveBeenCalledTimes(2);
    expect(promises.writeFile).toHaveBeenCalledWith(expectedFileName, metaContent);
    expect(promises.writeFile).toHaveBeenCalledWith(expectedJsonFileName, jsonMetaContent);
  });
}


async () => {
    const collector = new MetadataCollector();
    const fileList = await collector.getFileList(join(rootDir, 'tests', 'data', 'scss'));
    const expectedFullPaths = expectedFileList.map((file) => join(scssDir, file));

    expect(fileList.length).toBe(expectedFullPaths.length);

    fileList.forEach((file) => expect(expectedFullPaths).toContain(file));
  }


(file) => join(scssDir, file)


(file) => expect(expectedFullPaths).toContain(file)


async () => {
    const collector = new MetadataCollector();
    const handler = (content: string): string => content;
    const filesInfo = await collector.readFiles(join(rootDir, 'tests', 'data', 'scss'), handler);

    expect(filesInfo.length).toBe(expectedFileList.length);

    filesInfo.forEach((file) => {
      expect(expectedFileList).toContain(file.path);
      expect(typeof file.content).toBe('string');
      expect(file.content.length).toBeGreaterThan(0);
    });
  }


(content: string): string => content


(file) => {
      expect(expectedFileList).toContain(file.path);
      expect(typeof file.content).toBe('string');
      expect(file.content.length).toBeGreaterThan(0);
    }


async () => {
    const collector = new MetadataCollector();
    const handler = (content: string): string => `123${content}`;
    const fileList = await collector.readFiles(join(rootDir, 'tests', 'data', 'scss'), handler);

    fileList.forEach((file) => expect(file.content.slice(0, 3)).toBe('123'));
  }


(content: string): string => `123${content}`


(file) => expect(file.content.slice(0, 3)).toBe('123')


async () => {
    const relativePath = join('path', 'filePath.scss');
    const fileContent = 'file content';
    const destinationPath = './scss';
    const expectedDestinationPath = resolve(join(destinationPath, relativePath));
    const expectedDestinationDir = dirname(expectedDestinationPath);

    function getTestFiles(): FileInfo[] {
      return [{ path: relativePath, content: fileContent }];
    }

    await MetadataCollector.saveScssFiles(Promise.resolve(getTestFiles()), destinationPath);

    expect(promises.mkdir).toHaveBeenCalledTimes(1);
    expect(promises.mkdir).toHaveBeenCalledWith(expectedDestinationDir, { recursive: true });
    expect(promises.writeFile).toHaveBeenCalledTimes(1);
    expect(promises.writeFile).toHaveBeenCalledWith(expectedDestinationPath, fileContent);
  }


async () => {
    const collector = new MetadataCollector();
    const version = '1.1.1';
    const fileName = join('metadata', 'dx-theme-builder-metadata.ts');
    const jsonFileName = join('metadata', 'dx-theme-builder-metadata.json');
    const expectedFileName = resolve(fileName);
    const expectedJsonFileName = resolve(jsonFileName);
    const expectedDirName = dirname(expectedFileName);
    const meta: ThemesMetadata = { generic: [{ Key: '$var', Value: '\'ON\'' }], material: [] };

    collector.generator.metadata = meta;

    let metaContent = 'export const metadata: ThemesMetadata = {\'generic\':[{\'Key\':\'$var\',\'Value\':\'"ON"\'}],\'material\':[]};\n';
    metaContent += `export const version: string = '${version}';\n`;
    metaContent += 'export const browsersList: Array<string> = [];\n';
    metaContent += 'export const dependencies: FlatStylesDependencies = {};\n';

    const jsonMetaContent = JSON.stringify(meta);

    await collector.saveMetadata(fileName, jsonFileName, version, [], {});

    expect(promises.mkdir).toHaveBeenCalledTimes(2);
    expect(promises.mkdir).toHaveBeenCalledWith(expectedDirName, { recursive: true });
    expect(promises.writeFile).toHaveBeenCalledTimes(2);
    expect(promises.writeFile).toHaveBeenCalledWith(expectedFileName, metaContent);
    expect(promises.writeFile).toHaveBeenCalledWith(expectedJsonFileName, jsonMetaContent);
  }


export enum BanType {
    FORCE,
    CLEAN,
    SOFT,
    NORMAL,
    MANUAL,
    TEMP
}