[
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "content": "---\nname: Bug report\nabout: Report an issue with a specific sample\ntitle: '[BUG] in sample: '\nlabels: 'type: bug'\nassignees: ''\n---\n\n### Which sample has a bug?\n\n**Sample name or URL where you found the bug**\n\n### How to reproduce the issue\n\n**Failing Function code used (if you modified the sample)**\n\n**Steps to set up and reproduce**\n\n<!-- Help us diagnose the issue. Please provide detailed instructions to run your minimal repro or to recreate the environment -->\n\n### Debug output\n\n<!-- Provide any error messages or screenshots of unexpected behavior -->\n\n**Errors in the\n[console logs](https://console.firebase.google.com/project/_/functions/logs?search=&severity=DEBUG)**\n\n**Screenshots**\n\n### Expected behavior\n\n<!-- What is the expected behavior? -->\n\n### Actual behavior\n\n<!-- What is the actual behavior? -->\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "content": "# Copyright 2023 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# https://docs.github.com/en/free-pro-team@latest/github/building-a-strong-community/configuring-issue-templates-for-your-repository#configuring-the-template-chooser\n\nblank_issues_enabled: false\ncontact_links:\n  - name: Propose a new sample, or get help setting up a sample\n    url: https://github.com/firebase/functions-samples/discussions\n    about: Informal help and discussion about the samples in this repository.\n  - name: Issues with the Firebase platform\n    url: https://firebase.google.com/support\n    about: Report issues with Firebase services to Firebase support.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/docs.md",
    "content": "---\nname: Documentation issue\nabout: Are a sample's docs incorrect or unclear?\ntitle: '[DOCS] for sample: '\nlabels: ''\nassignees: ''\n---\n\n<!-- This is only for issues with the documentation in this repository. -->\n\n### Which sample?\n\n**Sample name or URL** **Link to closest heading**\n\n### What is the issue with this sample's docs?\n\n<!-- Broken link, unclear step, outdated step, etc... -->\n"
  },
  {
    "path": ".github/workflows/test_node.yml",
    "content": "# Copyright 2023 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nname: 2nd Gen CI Tests\n\non:\n  pull_request:\n    paths:\n      - 'Node/**'\n      - 'package.json'\n      - 'package-lock.json'\n      - 'pnpm-workspace.yaml'\n      - 'pnpm-lock.yaml'\n      - 'tsconfig.template.json'\n  push:\n    branches:\n      - main\n    paths:\n      - 'Node/**'\n      - 'package.json'\n      - 'package-lock.json'\n      - 'pnpm-workspace.yaml'\n      - 'pnpm-lock.yaml'\n      - 'tsconfig.template.json'\n\nenv:\n  CI: true\n\njobs:\n  unit:\n    runs-on: ubuntu-latest\n    strategy:\n      matrix:\n        node-version:\n          # The latest Node version for 2nd gen\n          # https://cloud.google.com/functions/docs/runtime-support#node.js\n          - 22.x\n    steps:\n      - uses: actions/checkout@v4\n      - uses: actions/setup-node@v4\n        with:\n          node-version: ${{ matrix.node-version }}\n\n      - name: Cache npm\n        uses: actions/cache@v4\n        with:\n          path: ~/.npm\n          key: ${{ runner.os }}-node-${{ matrix.node-version }}-${{ hashFiles('**/package-lock.json') }}\n\n      - run: npm install\n      - run: npm run bootstrap-2nd-gen\n      - run: npm run lint-2nd-gen\n      - run: npm run compile-2nd-gen\n      - run: npm run test-2nd-gen\n"
  },
  {
    "path": ".github/workflows/test_node_1st_gen.yml",
    "content": "# Copyright 2023 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nname: 1st Gen CI Tests\n\non:\n  pull_request:\n    paths:\n      - 'Node-1st-gen/**'\n      - 'package.json'\n      - 'package-lock.json'\n      - 'pnpm-workspace.yaml'\n      - 'pnpm-lock.yaml'\n      - 'tsconfig.template.json'\n  push:\n    branches:\n      - main\n    paths:\n      - 'Node-1st-gen/**'\n      - 'package.json'\n      - 'package-lock.json'\n      - 'pnpm-workspace.yaml'\n      - 'pnpm-lock.yaml'\n      - 'tsconfig.template.json'\n\nenv:\n  CI: true\n\njobs:\n  unit:\n    runs-on: ubuntu-latest\n    strategy:\n      matrix:\n        node-version:\n          # The latest Node version for 1st gen\n          # https://cloud.google.com/functions/docs/runtime-support#node.js\n          - 20.x\n    steps:\n      - uses: actions/checkout@v4\n      - uses: actions/setup-node@v4\n        with:\n          node-version: ${{ matrix.node-version }}\n\n      - name: Cache npm\n        uses: actions/cache@v4\n        with:\n          path: ~/.npm\n          key: ${{ runner.os }}-node-${{ matrix.node-version }}-${{ hashFiles('**/package-lock.json') }}\n\n      - run: npm install\n      - run: npm run bootstrap-1st-gen\n      - run: npm run lint-1st-gen\n      - run: npm run compile-1st-gen\n      - run: npm run test-1st-gen\n"
  },
  {
    "path": ".github/workflows/test_python.yml",
    "content": "# Copyright 2023 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nname: CI Tests\n\non:\n  pull_request:\n    paths:\n      - 'Python/**'\n\n  push:\n    branches:\n      - main\n    paths:\n      - 'Python/**'\n\nenv:\n  CI: true\n\njobs:\n  unit:\n    runs-on: ubuntu-latest\n    strategy:\n      matrix:\n        python-version:\n          - \"3.10\"\n          - \"3.11\"\n    steps:\n      - uses: actions/checkout@v3\n      - name: Set up Python ${{ matrix.python-version }}\n        uses: actions/setup-python@v4\n        with:\n            python-version: ${{ matrix.python-version }}\n      - name: Install dependencies\n        working-directory: ./Python\n        run: pip install -r requirements.txt\n      - name: Lint\n        working-directory: ./Python\n        run: python pyfmt.py --check_only --exclude \"**/venv/**/*.py\" **/*.py\n"
  },
  {
    "path": ".gitignore",
    "content": ".idea\n**/node_modules/*\n**/.firebase\n**/.firebaserc\n**/.runtimeconfig.json\n*/npm-debug.log\nlerna-debug.log\n*~\nservice-account-credentials.json\n**/package-lock.json\n# keep the root package-lock so that tools are consistent\n!/package-lock.json\n**/ui-debug.log\n**/database-debug.log\n**/firebase-debug.log\n**/pubsub-debug.log\n**/tsconfig-compile.json\nyarn.lock\n**/.DS_Store\n"
  },
  {
    "path": ".opensource/project.json",
    "content": "{\n    \"name\": \"Cloud Functions Samples\",\n    \"type\": \"sample\",\n    \"platforms\": [\n        \"Admin\",\n        \"Node\"\n    ],\n    \"content\": \"README.md\",\n    \"pages\": {\n        \"quickstarts/big-ben/README.md\": \"Quickstart: HTTP Trigger + Hosting\",\n        \"quickstarts/email-users/README.md\": \"Quickstart: Auth Trigger\",\n        \"quickstarts/pubsub-helloworld/README.md\": \"Quickstart: PubSub Trigger\",\n        \"quickstarts/thumbnails/README.md\": \"Quickstart: Cloud Storage Trigger\",\n        \"quickstarts/time-server/README.md\": \"Quickstart: HTTP Trigger\",\n        \"quickstarts/uppercase/README.md\": \"Quickstart: RTDB Trigger\",\n        \"quickstarts/uppercase-firestore/README.md\": \"Quickstart: Firestore Trigger\"\n    },\n    \"related\": [\n        \"firebase/firebase-functions\"\n    ]\n}\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing to the Firebase Cloud Functions Templates Library\n\nWe'd love for you to contribute to our source code and to make the Firebase\nCloud Functions Templates Library even better than it is today! Here are the\nguidelines we'd like you to follow:\n\n- [Code of Conduct](#coc)\n- [Question or Problem?](#question)\n- [Issues and Bugs](#issue)\n- [Feature Requests](#feature)\n- [Submission Guidelines](#submit)\n- [Signing the CLA](#cla)\n\n## <a name=\"coc\"></a> Code of Conduct\n\nAs contributors and maintainers of the Firebase functions-samples project, we\npledge to respect everyone who contributes by posting issues, updating\ndocumentation, submitting pull requests, providing feedback in comments, and any\nother activities.\n\nCommunication through any of Firebase's channels (GitHub, StackOverflow,\nTwitter, etc.) must be constructive and never resort to personal attacks,\ntrolling, public or private harassment, insults, or other unprofessional\nconduct.\n\nWe promise to extend courtesy and respect to everyone involved in this project\nregardless of gender, gender identity, sexual orientation, disability, age,\nrace, ethnicity, religion, or level of experience. We expect anyone contributing\nto the project to do the same.\n\nIf any member of the community violates this code of conduct, the maintainers of\nthis project may take action, removing issues, comments, and PRs or blocking\naccounts as deemed appropriate.\n\nIf you are subject to or witness unacceptable behavior, or have any other\nconcerns, please drop us a line at\n[Firebase support](https://firebase.google.com/support).\n\n## <a name=\"question\"></a> Got a Question or Problem?\n\n- To report specific problems with the samples in this repository, file an\n  [issue](#issue).\n- If you have questions about a sample in this repository, try opening a\n  [discussion](https://github.com/firebase/functions-samples/discussions).\n- For general Cloud Functions for Firebase questions, please ask on\n  [StackOverflow][stackoverflow] and use the `firebase` tag.\n- If you have a problem specific to your Firebase project, or would like to\n  report an issue with Firebase itself, contact\n  [Firebase support](https://firebase.google.com/support).\n\n## <a name=\"issue\"></a> Found an Issue?\n\nIf you find a bug in the source code or a mistake in the documentation, you can\nhelp us by submitting an issue to our [GitHub Repository][github]. Even better,\nyou can submit a Pull Request with a fix.\n\nSee [below](#submit) for some guidelines.\n\n## <a name=\"submit\"></a> Submission Guidelines\n\n### Submitting an Issue\n\nBefore you submit your issue search the archive in case your question was\nalready answered.\n\nHelp us to maximize the effort we can spend fixing issues and adding new\nfeatures by not reporting duplicate issues.\n\nThe more information you can provide in an issue, the faster we can resolve it.\nIssues that don't make an attempt to follow the issue template will likely be\nclosed.\n\n### New samples\n\nGiven the maintenance cost of the large number of samples that are already in\nthis repository, we are unlikely to add new samples. However, if you have an\nidea for a sample and would like to discuss it with others, take a look at the\n[\"ideas\" section of this repository's discussions](https://github.com/firebase/functions-samples/discussions?discussions_q=category%3AIdeas).\n\nPull Requests that contain new samples will only be considered if they meet the\nfollowing criteria:\n\n- Solve a common use-case for Cloud Functions for Firebase with minimal\n  non-Firebase setup required\n- Thoroughly documented\n- Thoroughly tested\n- Simple enough to be maintained alongside all other samples in the repository\n\nIf your Pull Request isn't accepted, consider hosting it in your own GitHub\nrepository, and adding it to our\n[list of community-built extensions](./community.md)!\n\n## <a name=\"cla\"></a> Signing the CLA\n\nContributions to this project must be accompanied by a Contributor License\nAgreement (CLA). You (or your employer) retain the copyright to your\ncontribution; this simply gives us permission to use and redistribute your\ncontributions as part of the project. Head over to\n<https://cla.developers.google.com/> to see your current agreements on file or\nto sign a new one.\n\nYou generally only need to submit a CLA once, so if you've already submitted one\n(even if it was for a different project), you probably don't need to do it\nagain.\n\nPlease sign our [Contributor License Agreement][google-cla] (CLA) before sending\npull requests. For any code changes to be accepted, the CLA must be signed. It's\na quick process, we promise!\n\n[github]: https://github.com/firebase/functions-samples\n[google-cla]: https://cla.developers.google.com\n[js-style-guide]: http://google.github.io/styleguide/javascriptguide.xml\n[py-style-guide]: http://google.github.io/styleguide/pyguide.html\n[jsbin]: http://jsbin.com/\n[stackoverflow]: http://stackoverflow.com/questions/tagged/firebase\n[global-gitignore]:\n  https://help.github.com/articles/ignoring-files/#create-a-global-gitignore\n"
  },
  {
    "path": "LICENSE",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright 2016 Google Inc.\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "Node/README.md",
    "content": "# Node.js (2nd gen) samples\n\nThis folder contains examples for 2nd gen Node.js functions. See a description of all samples in this folder in the [root README](../README.md)."
  },
  {
    "path": "Node/alerts-to-discord/.gitignore",
    "content": "# Don't accidentally push Discord Webhook URL to prod\nfunctions/.env"
  },
  {
    "path": "Node/alerts-to-discord/README.md",
    "content": "# Quickstart: Send Firebase Alerts to Discord\n\nThis quickstart demonstrates how to trigger a function based on a Firebase Alert, and send information about the alert to a channel in a Discord server.\n\n<img width=\"639\" alt=\"Screen Shot 2022-04-29 at 11 12 12 AM\" src=\"https://user-images.githubusercontent.com/3759507/165973290-2f6e6937-7c07-4006-b52d-813aa195e7cf.png\">\n\n## Set up and Deploy\n\n### Discord Webhook URL\n\nThe sample uses [Discord Webhooks](https://support.discord.com/hc/en-us/articles/228383668-Intro-to-Webhooks) to send alerts to a Discord channel. You'll need to create a Webhook and hook it up the function by [creating an environment variable](https://firebase.google.com/docs/functions/config-env#env-variables):\n\n1. Follow the \"MAKING A WEBHOOK\" instructions in the [Discord docs](https://support.discord.com/hc/en-us/articles/228383668-Intro-to-Webhooks). \n1. Copy the Webhook URL\n1. Create a `.env` file in the `functions` directory\n1. Add the `DISCORD_WEBHOOK_URL` variable and set it to your Webhook URL:\n    ```bash\n    DISCORD_WEBHOOK_URL=\"<your webhook url>\"\n    ```\n\n### Deploy\nDeploy functions using the Firebase CLI:\n\n```bash\n$ firebase deploy\n```\n\n## License\n\n© Google, 2022. Licensed under an [Apache-2](../../../LICENSE) license.\n\n"
  },
  {
    "path": "Node/alerts-to-discord/firebase.json",
    "content": "{\n  \"functions\": {\n    \"codebase\": \"alerts-to-discord\",\n    \"predeploy\": [\n      \"npm --prefix \\\"$RESOURCE_DIR\\\" run lint\"\n    ]\n  }\n}\n"
  },
  {
    "path": "Node/alerts-to-discord/functions/.eslintrc.js",
    "content": "/**\n * Copyright 2023 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nmodule.exports = {\n  root: true,\n  env: {\n    es2017: true,\n    node: true,\n  },\n  extends: [\n    \"eslint:recommended\",\n    \"google\",\n  ],\n  rules: {\n    quotes: [\"error\", \"double\"],\n  },\n};\n"
  },
  {
    "path": "Node/alerts-to-discord/functions/index.js",
    "content": "/**\n * Copyright 2022 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n// [START v2import]\n\nconst {\n  onNewFatalIssuePublished,\n} = require(\"firebase-functions/alerts/crashlytics\");\nconst {\n  onNewTesterIosDevicePublished,\n} = require(\"firebase-functions/alerts/appDistribution\");\nconst {\n  onThresholdAlertPublished,\n} = require(\"firebase-functions/alerts/performance\");\nconst logger = require(\"firebase-functions/logger\");\n// [END v2import]\n\n/**\n * Posts a message to Discord with Discord's Webhook API\n *\n * @param {string} botName - The bot username to display\n * @param {string} messageBody - The message to post (Discord MarkDown)\n */\nasync function postMessageToDiscord(botName, messageBody) {\n  const webhookUrl = process.env.DISCORD_WEBHOOK_URL;\n  if (!webhookUrl) {\n    throw new Error(\n        \"No webhook URL found. Set the Discord Webhook URL before deploying. Learn more about Discord webhooks here: https://support.discord.com/hc/en-us/articles/228383668-Intro-to-Webhooks\",\n    );\n  }\n\n  return fetch(webhookUrl, {\n    method: \"POST\",\n    headers: {\"Content-Type\": \"application/json\"},\n    body: JSON.stringify(\n        // Here's what the Discord API supports in the payload:\n        // https://discord.com/developers/docs/resources/webhook#execute-webhook-jsonform-params\n        {\n          content: messageBody,\n          username: botName,\n        },\n    ),\n  });\n}\n\n// [START v2Alerts]\n/**\n * function triggered by Crashlytics that publishes a message\n * to Discord whenever a new fatal issue occurs.\n */\n// [START v2CrashlyticsAlertTrigger]\nexports.postfatalissuetodiscord = onNewFatalIssuePublished(async (event) => {\n// [END v2CrashlyticsAlertTrigger]\n  // [START v2CrashlyticsEventPayload]\n  // construct a helpful message to send to Discord\n  const appId = event.appId;\n  const {id, title, subtitle, appVersion} = event.data.payload.issue;\n  const message = `\n🚨 New fatal issue for ${appId} in version ${appVersion} 🚨\n\n**${title}**\n\n${subtitle}\n\nid: \\`${id}\\`\n`;\n  // [END v2CrashlyticsEventPayload]\n\n  try {\n    // [START v2SendToDiscord]\n    const response = await postMessageToDiscord(\"Crashlytics Bot\", message);\n    if (response.ok) {\n      logger.info(\n          `Posted fatal Crashlytics alert ${id} for ${appId} to Discord`,\n          event.data.payload,\n      );\n    } else {\n      throw new Error(`Discord returned status code ${response.status}`);\n    }\n    // [END v2SendToDiscord]\n  } catch (error) {\n    logger.error(\n        `Unable to post fatal Crashlytics alert ${id} for ${appId} to Discord`,\n        error,\n    );\n  }\n});\n\n/**\n * function triggered by App Distribution that publishes a message\n * to Discord whenever a new iOS tester device is registered.\n */\n// [START v2AppDistributionAlertTrigger]\nexports.postnewduuidtodiscord = onNewTesterIosDevicePublished(async (event) => {\n// [END v2AppDistributionAlertTrigger]\n  // [START v2AppDistributionEventPayload]\n  // construct a helpful message to send to Discord\n  const appId = event.appId;\n  const {\n    testerDeviceIdentifier,\n    testerDeviceModelName,\n    testerEmail,\n    testerName,\n  } = event.data.payload;\n  const message = `\n📱 New iOS device registered by ${testerName} <${testerEmail}> for ${appId}\n\nUDID **${testerDeviceIdentifier}** for ${testerDeviceModelName}\n`;\n  // [END v2AppDistributionEventPayload]\n\n  try {\n    // [START v2SendNewTesterIosDeviceToDiscord]\n    const response = await postMessageToDiscord(\"AppDistribution Bot\", message);\n    if (response.ok) {\n      logger.info(\n          `Posted iOS device registration alert for ${testerEmail} to Discord`,\n      );\n    } else {\n      throw new Error(`Discord returned status code ${response.status}`);\n    }\n    // [END v2SendNewTesterIosDeviceToDiscord]\n  } catch (error) {\n    logger.error(\n        `Unable to post iOS device registration for ${testerEmail} to Discord`,\n        error,\n    );\n  }\n});\n// [END v2Alerts]\n\n/**\n * Function triggered by Firebase Performance Monitoring that publishes\n * a message to Discord whenever a performance threshold alert is fired.\n */\n// [START v2PerformanceAlertTrigger]\nexports.postperformancealerttodiscord = onThresholdAlertPublished(\n    async (event) => {\n      // [END v2PerformanceAlertTrigger]\n      // [START v2PerformanceEventPayload]\n      // construct a helpful message to send to Discord\n      const appId = event.appId;\n      const {\n        eventName,\n        metricType,\n        eventType,\n        numSamples,\n        thresholdValue,\n        thresholdUnit,\n        conditionPercentile,\n        appVersion,\n        violationValue,\n        violationUnit,\n        investigateUri,\n      } = event.data.payload;\n      const message = `\n    ⚠️ Performance Alert for ${metricType} of ${eventType}: **${eventName}** ⚠️\n    \n    App id: ${appId}\n    Alert condition: ${thresholdValue} ${thresholdUnit}\n    Percentile (if applicable): ${conditionPercentile}\n    App version (if applicable): ${appVersion}\n    \n    Violation: ${violationValue} ${violationUnit}\n    Number of samples checked: ${numSamples}\n    \n    **Investigate more:** ${investigateUri}\n    `;\n      // [END v2PerformanceEventPayload]\n\n      try {\n        // [START v2SendPerformanceAlertToDiscord]\n        const response = await postMessageToDiscord(\n            \"Firebase Performance Bot\", message);\n        if (response.ok) {\n          logger.info(\n              `Posted Firebase Performance alert ${eventName} to Discord`,\n              event.data.payload,\n          );\n        } else {\n          throw new Error(`Discord returned status code ${response.status}`);\n        }\n        // [END v2SendPerformanceAlertToDiscord]\n      } catch (error) {\n        logger.error(\n            `Unable to post Firebase Performance alert ${eventName} to Discord`,\n            error,\n        );\n      }\n    });\n// [END v2Alerts]\n"
  },
  {
    "path": "Node/alerts-to-discord/functions/package.json",
    "content": "{\n  \"name\": \"alerts-to-discord\",\n  \"description\": \"Send a message to Discord when an alert is received from Firebase\",\n  \"scripts\": {\n    \"lint\": \"eslint .\",\n    \"serve\": \"firebase emulators:start --only functions\",\n    \"shell\": \"firebase functions:shell\",\n    \"start\": \"npm run shell\",\n    \"deploy\": \"firebase deploy --only functions\",\n    \"logs\": \"firebase functions:log\",\n    \"compile\": \"cp ../../../tsconfig.template.json ./tsconfig-compile.json && tsc --project tsconfig-compile.json\"\n  },\n  \"engines\": {\n    \"node\": \"22\"\n  },\n  \"main\": \"index.js\",\n  \"dependencies\": {\n    \"firebase-admin\": \"^13.0.2\",\n    \"firebase-functions\": \"7.0.0\"\n  },\n  \"devDependencies\": {\n    \"@types/node\": \"^17.0.45\",\n    \"eslint\": \"^8.57.1\",\n    \"eslint-config-google\": \"^0.14.0\",\n    \"firebase-functions-test\": \"^3.4.0\"\n  },\n  \"private\": true\n}\n"
  },
  {
    "path": "Node/app-distribution-feedback-to-jira/.eslintrc",
    "content": "module.exports = {\n  root: true,\n  env: {\n    es2017: true,\n    node: true,\n  },\n  extends: [\n    \"eslint:recommended\",\n    \"google\",\n  ],\n  rules: {\n    quotes: [\"error\", \"double\"],\n  },\n};\n"
  },
  {
    "path": "Node/app-distribution-feedback-to-jira/.gitignore",
    "content": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\nfirebase-debug.log*\nfirebase-debug.*.log*\n\n# Firebase cache\n.firebase/\n\n# Firebase config\n\n# Uncomment this if you'd like others to create their own Firebase project.\n# For a team working on the same Firebase project(s), it is recommended to leave\n# it commented so all members can deploy to the same project(s) in .firebaserc.\n# .firebaserc\n\n# Runtime data\npids\n*.pid\n*.seed\n*.pid.lock\n\n# Directory for instrumented libs generated by jscoverage/JSCover\nlib-cov\n\n# Coverage directory used by tools like istanbul\ncoverage\n\n# nyc test coverage\n.nyc_output\n\n# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)\n.grunt\n\n# Bower dependency directory (https://bower.io/)\nbower_components\n\n# node-waf configuration\n.lock-wscript\n\n# Compiled binary addons (http://nodejs.org/api/addons.html)\nbuild/Release\n\n# Dependency directories\nnode_modules/\n\n# Optional npm cache directory\n.npm\n\n# Optional eslint cache\n.eslintcache\n\n# Optional REPL history\n.node_repl_history\n\n# Output of 'npm pack'\n*.tgz\n\n# Yarn Integrity file\n.yarn-integrity\n\n# dotenv environment variables file\n.env\n"
  },
  {
    "path": "Node/app-distribution-feedback-to-jira/README.md",
    "content": "# Send in-app feedback to Jira\n \nThis [code](functions/index.js) demonstrates how to use a Firebase Cloud Function triggered by an \n[in-app feedback Firebase Alert from App Distribution](https://firebase.google.com/docs/functions/beta/reference/firebase-functions.alerts.appdistribution.inappfeedbackpayload),\nand stores the feedback details (including screenshot) in Jira.\n\nYou can customize this code to work with your own Jira configuration (eg on-premise support, custom issue types, etc). \n\n## Quickstart\n\nThis sample code uses Jira's built-in APIs to create issues for in-app tester feedback. For simplicity it uses [basic authorization](https://developer.atlassian.com/cloud/jira/platform/basic-auth-for-rest-apis/).\n\n1. [Generate an API token](https://id.atlassian.com/manage-profile/security/api-tokens) via your Jira profile.\n\n\n   Note: If the tester who files feedback does not have a Jira account, the user who generates this token will be marked as the issue's reporter.\n2. This [code](functions/index.js) uses [parameterized configuration](https://firebase.google.com/docs/functions/config-env#params) to prompt for the required configuratio. To start the process, run:\n   ```bash\n   $ firebase deploy\n   ```\n   This will store the `API_TOKEN` using [Google Cloud Secret Manager](https://cloud.google.com/secret-manager) and the remaining settings in an `.env` file which will contain the following variables, customized to your Jira project:\n    ```bash\n    JIRA_URI=\"<your JIRA instance's URI, e.g. 'https://mysite.atlassian.net'>\"\n    PROJECT_KEY=\"<your project's key, e.g. 'DEV'>\"\n    ISSUE_TYPE_ID=\"<issue type ID; defaults to '10001' (Improvement)>\"\n    ISSUE_LABEL=\"<label applied to the Jira issues created; defaults to 'in-app'>\"\n    API_TOKEN_OWNER=\"<creator of the token; default reporter of issues>\"\n    ```\n\n## License\n© Google, 2022. Licensed under an [Apache-2 license](../../LICENSE)."
  },
  {
    "path": "Node/app-distribution-feedback-to-jira/firebase.json",
    "content": "{\n  \"functions\": [\n    {\n      \"source\": \"functions\",\n      \"codebase\": \"app-distribution-feedback-to-jira\",\n      \"ignore\": [\n        \"node_modules\",\n        \".git\",\n        \"firebase-debug.log\",\n        \"firebase-debug.*.log\"\n      ],\n      \"predeploy\": [\n        \"npm --prefix \\\"$RESOURCE_DIR\\\" run lint\"\n      ]\n    }\n  ]\n}\n"
  },
  {
    "path": "Node/app-distribution-feedback-to-jira/functions/.eslintrc.cjs",
    "content": "/**\n * Copyright 2023 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nmodule.exports = {\n  root: true,\n  env: {\n    es6: true,\n    node: true,\n  },\n  extends: [\n    \"eslint:recommended\",\n    \"google\",\n  ],\n  rules: {\n    quotes: [\"error\", \"double\"],\n  },\n  \"parserOptions\": {\n    \"sourceType\": \"module\",\n    \"ecmaVersion\": 2022\n  }\n};\n"
  },
  {
    "path": "Node/app-distribution-feedback-to-jira/functions/.gitignore",
    "content": "node_modules/"
  },
  {
    "path": "Node/app-distribution-feedback-to-jira/functions/index.js",
    "content": "/**\n * Copyright 2023 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n  onInAppFeedbackPublished} from \"firebase-functions/alerts/appDistribution\";\nimport {defineInt, defineSecret, defineString} from \"firebase-functions/params\";\nimport logger from \"firebase-functions/logger\";\nimport {FormData} from \"formdata-polyfill/esm.min.js\";\n\n// The keys are either defined in .env or they are created\n// via prompt in the CLI before deploying\nconst jiraUriConfig = defineString(\"JIRA_URI\", {\n  description: \"URI of your Jira instance (e.g. 'https://mysite.atlassian.net')\",\n  input: {\n    text: {\n      validationRegex: /^https:\\/\\/.*/,\n      validationErrorMessage: \"Please enter an 'https://' URI\",\n    },\n  },\n});\nconst projectKeyConfig = defineString(\"PROJECT_KEY\", {\n  description: \"Project key of your Jira instance (e.g. 'XY')\",\n});\nconst issueTypeIdConfig = defineInt(\"ISSUE_TYPE_ID\", {\n  description: \"Issue type ID for the Jira issues being created\",\n  default: 10001,\n});\nconst issueLabelConfig = defineString(\"ISSUE_LABEL\", {\n  description: \"Label for the Jira issues being created\",\n  default: \"in-app\",\n});\nconst apiTokenOwnerConfig = defineString(\"API_TOKEN_OWNER\", {\n  description: \"Owner of the Jira API token\",\n  input: {\n    text: {\n      validationRegex:\n      /^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\\.[a-zA-Z0-9-]+)*$/,\n      validationErrorMessage: \"Please enter a valid email address\",\n    },\n  },\n});\nconst apiTokenConfig = defineSecret(\"API_TOKEN\", {\n  description: \"Jira API token. Created using \" +\n      \"https://id.atlassian.com/manage-profile/security/api-tokens\",\n});\n\nexport const handleInAppFeedback = async (event) => {\n  const issueUri = await createIssue(event);\n  if (event.data.payload.screenshotUri) {\n    await uploadScreenshot(issueUri, event.data.payload.screenshotUri);\n  }\n  return undefined; // returns 204 (no content) which is ignored\n};\n\nexport const feedbacktojira =\n    onInAppFeedbackPublished({secrets: [apiTokenConfig]}, handleInAppFeedback);\n\n/**\n * Creates \"Authorization\" header value.\n * @return {string} Basic, base64-encoded \"Authorization\" header value\n */\nfunction authHeader() {\n  return \"Basic \" + Buffer\n      .from(apiTokenOwnerConfig.value() + \":\" + apiTokenConfig.value())\n      .toString(\"base64\");\n}\n\n/**\n * Creates new issue in Jira.\n * @param {AlertEvent<InAppFeedbackPayload>} event\n */\nasync function createIssue(event) {\n  const requestJson = await buildCreateIssueRequest(event);\n  const requestBody = JSON.stringify(requestJson);\n  const response =\n        await fetch(`${jiraUriConfig.value()}/rest/api/3/issue`, {\n          method: \"POST\",\n          headers: {\n            \"Authorization\": authHeader(),\n            \"Accept\": \"application/json\",\n            \"Content-Type\": \"application/json\",\n          },\n          body: requestBody,\n        });\n  if (!response.ok) {\n    throw new Error(\"Issue creation failed: \" +\n                    `${response.status} ${response.statusText} for ` +\n                    requestBody);\n  }\n  const json = await response.json();\n  return json.self; // issueUri\n}\n\n/**\n * Uploads screenshot to Jira (after downloading it from Firebase).\n * @param {string} issueUri URI of the Jira issue\n * @param {string} screenshotUri URI of the screenshot hosted by Firebase\n */\nasync function uploadScreenshot(issueUri, screenshotUri) {\n  const dlResonse = await fetch(screenshotUri);\n  if (!dlResonse.ok) {\n    throw new Error(\"Screenshot download failed: \" +\n                    `${dlResonse.status} ${dlResonse.statusText}`);\n  }\n  const blob = await dlResonse.blob();\n\n  const form = new FormData();\n  form.append(\"file\", blob, \"screenshot.png\");\n  const ulResponse = await fetch(issueUri + \"/attachments\", {\n    method: \"POST\",\n    body: form,\n    headers: {\n      \"Authorization\": authHeader(),\n      \"Accept\": \"application/json\",\n      \"X-Atlassian-Token\": \"no-check\",\n    },\n  });\n  if (!ulResponse.ok) {\n    throw new Error(\"Screenshot upload failed: \" +\n                    `${ulResponse.status} ${ulResponse.statusText}`);\n  }\n}\n\n/**\n * Looks up Jira user ID.\n * @param {string} testerEmail Email address of tester who filed feedback\n */\nasync function lookupReporter(testerEmail) {\n  const response =\n        await fetch(\n            `${jiraUriConfig.value()}/rest/api/3/user/search` +\n              `?query=${testerEmail}`, {\n              method: \"GET\",\n              headers: {\n                \"Authorization\": authHeader(),\n                \"Accept\": \"application/json\",\n              }});\n  if (!response.ok) {\n    logger.info(`Failed to find Jira user for '${testerEmail}':` +\n                `${response.status} ${response.statusText}`);\n  }\n  const json = await response.json();\n  return json.length > 0 ? json[0].accountId : undefined;\n}\n\n/**\n * Builds payload for creating a Jira issue.\n * @param {AlertEvent<InAppFeedbackPayload>} event\n */\nasync function buildCreateIssueRequest(event) {\n  let summary = \"In-app feedback: \" + event.data.payload.text;\n  summary = summary.replace(/[\\n\\r].*/g, \"\");\n  if (summary.length > 40) {\n    summary = summary.substring(0, 39) + \"…\";\n  }\n  const json = {\n    \"update\": {},\n    \"fields\": {\n      \"summary\": summary,\n      \"issuetype\": {\n        \"id\": issueTypeIdConfig.value(),\n      },\n      \"project\": {\n        \"key\": projectKeyConfig.value(),\n      },\n      \"description\": {\n        \"type\": \"doc\",\n        \"version\": 1,\n        \"content\": [\n          {\n            \"type\": \"paragraph\",\n            \"content\": [\n              {\n                \"text\": \"Firebase App ID: \",\n                \"type\": \"text\",\n                \"marks\": [\n                  {\n                    \"type\": \"strong\",\n                  },\n                ],\n              },\n              {\n                \"text\": event.appId,\n                \"type\": \"text\",\n              },\n            ],\n          },\n          {\n            \"type\": \"paragraph\",\n            \"content\": [\n              {\n                \"text\": \"App Version: \",\n                \"type\": \"text\",\n                \"marks\": [\n                  {\n                    \"type\": \"strong\",\n                  },\n                ],\n              },\n              {\n                \"text\": event.data.payload.appVersion,\n                \"type\": \"text\",\n              },\n            ],\n          },\n          {\n            \"type\": \"paragraph\",\n            \"content\": [\n              {\n                \"text\": \"Tester Email: \",\n                \"type\": \"text\",\n                \"marks\": [\n                  {\n                    \"type\": \"strong\",\n                  },\n                ],\n              },\n              {\n                \"text\": event.data.payload.testerEmail,\n                \"type\": \"text\",\n              },\n            ],\n          },\n          {\n            \"type\": \"paragraph\",\n            \"content\": [\n              {\n                \"text\": \"Tester Name: \",\n                \"type\": \"text\",\n                \"marks\": [\n                  {\n                    \"type\": \"strong\",\n                  },\n                ],\n              },\n              {\n                \"text\": event.data.payload.testerName || \"None\",\n                \"type\": \"text\",\n              },\n            ],\n          },\n          {\n            \"type\": \"paragraph\",\n            \"content\": [\n              {\n                \"text\": \"Feedback text: \",\n                \"type\": \"text\",\n                \"marks\": [\n                  {\n                    \"type\": \"strong\",\n                  },\n                ],\n              },\n              {\n                \"text\": event.data.payload.text,\n                \"type\": \"text\",\n              },\n            ],\n          },\n          {\n            \"type\": \"paragraph\",\n            \"content\": [\n              {\n                \"text\": \"Console link\",\n                \"type\": \"text\",\n                \"marks\": [\n                  {\n                    \"type\": \"link\",\n                    \"attrs\": {\n                      \"href\": event.data.payload.feedbackConsoleUri,\n                      \"title\": \"Firebase console\",\n                    },\n                  },\n                ],\n              },\n            ],\n          },\n        ],\n      },\n      \"labels\": [issueLabelConfig.value()],\n    },\n  };\n  const reporter = await lookupReporter(event.data.payload.testerEmail);\n  if (reporter) {\n    json.fields.reporter = {\"id\": reporter};\n  }\n  return json;\n}\n"
  },
  {
    "path": "Node/app-distribution-feedback-to-jira/functions/package.json",
    "content": "{\n  \"name\": \"functions\",\n  \"description\": \"File new issue in Jira when receiving in-app feedback facilitated by Firebase App Distribution\",\n  \"scripts\": {\n    \"lint\": \"eslint .\",\n    \"serve\": \"firebase emulators:start --only functions\",\n    \"shell\": \"firebase functions:shell\",\n    \"start\": \"npm run shell\",\n    \"deploy\": \"firebase deploy --only functions\",\n    \"logs\": \"firebase functions:log\"\n  },\n  \"engines\": {\n    \"node\": \"22\"\n  },\n  \"main\": \"index.js\",\n  \"type\": \"module\",\n  \"dependencies\": {\n    \"firebase-functions\": \"7.0.0\",\n    \"formdata-polyfill\": \"^4.0.10\"\n  },\n  \"devDependencies\": {\n    \"eslint\": \"^8.57.1\",\n    \"eslint-config-google\": \"^0.14.0\"\n  },\n  \"private\": true\n}\n"
  },
  {
    "path": "Node/call-vertex-remote-config-server/README.md",
    "content": "Call the Vertex AI Gemini API with Remote Config and App Check\n==============================================================\n\nIntroduction\n------------\n\nThis is a sample callable function that authenticates clients with App\nCheck and then sends queries to Gemini using the Vertex AI Gemini API. Vertex\nAI model parameters (including the model itself) are controlled by\nRemote Config server features included in the Firebase Admin SDK for\nNode.js.\n\nUse the web client provided in `client/` to test the function.\n\n- [Read more about Remote Config for servers](https://firebase.google.com/docs/remote-config/server).\n- [Read more about App Check](https://firebase.google.com/docs/app-check).\n- [Read more about the Vertex AI Node.js Client library](https://cloud.google.com/nodejs/docs/reference/aiplatform/latest).\n\nImportant:  Vertex AI and Cloud Functions require a billing account. Review\n[Vertex AI pricing](https://cloud.google.com/vertex-ai/pricing) and\n[Firebase pricing](https://firebase.google.com/pricing) before running\nthis function. If you're new to Firebase and Google Cloud, check to see if\nyou're eligible for a\n[$300 credit](https://firebase.google.com/support/faq#pricing-free-trial) and\na Free Trial Cloud Billing account.\n\nGet Started\n---------------\n\n 1. Follow the instructions in client/README.md to create a Firebase project,\n    enable ReCAPTCHA Enterprise, enable and enforce Firebase App Check, and add\n    your Firebase config and ReCAPTCHA Enterprise key to the client config.\n \n 2. Enable [recommended Vertex AI APIs](https://console.cloud.google.com/vertex-ai).\n \n 3. Configure a Remote Config server template on the Firebase console. Use the template\n    described in\n    [Use server side Remote Config with Cloud Functions and Vertex\n    AI](https://firebase.google.com/docs/remote-config/solution-server#implementation-create-template),\n    which contains all of the parameters used in this function sample.\n \n 4. Install dependencies: `cd functions && npm install`\n \n 5. If you haven't already done so, install firebase-tools:\n  \n    `npm i firebase-tools@latest`\n \n 6. Log into Firebase:\n \n    `firebase login`\n \n 7. Deploy the function. We recommend testing in the\n    [Firebase emulator](https://firebase.google.com/docs/remote-config/solution-server#implementation-deploy-and-test-in-emulator):\n \n    `firebase emulators:start`\n \n 8. If testing in the emulator, verify that `testMode` is set to `true` in\n     `client/main.ts`, then start the client:\n\n    `cd client && npm run dev`\n\n    TIP: If you're using the emulator, you can deploy both the function and hosting\n         to the emulator. From the `client` directory, run `npm run build`.\n         Then, from the parent directory, run `firebase server --only functions,hosting`.\n         Open http://localhost:5000 to access and test the web client's connection\n         to the `callVertexWithRC` function.\n    \n 0. Open the [client app in a browser](http://localhost:5173) and enter a\n    prompt. To access the Vertex AI Gemini API, make sure that you have\n    set the `is_vertex_enabled` boolean parameter in your Remote Config\n    server template to `true`.\n\nSupport\n-------\n\n- [Firebase Support](https://firebase.google.com/support/)\n\nLicense\n-------\n\n© Google, 2024. Licensed under an [Apache-2](../../LICENSE) license.\n"
  },
  {
    "path": "Node/call-vertex-remote-config-server/client/README.md",
    "content": "Test client for call-vertex-remote-config-server\n================================================\n\nIntroduction\n------------\n\nThis is a basic web app that calls the `callVertexWithRC` function. The\nfunction uses values stored in Remote Config server templates with\nthe Firebase Admin SDK to dynamically update Vertex AI Gemini API\nparameters. Access is controlled using Firebase App Check.\n\n- [Read more about Remote Config for servers](https://firebase.google.com/docs/remote-config/server).\n- [Read more about App Check](https://firebase.google.com/docs/app-check).\n- [Read more about the Vertex AI Node.js Client library](https://cloud.google.com/nodejs/docs/reference/aiplatform/latest).\n\nImportant:  Vertex AI and Cloud Functions require a billing account. Review\n[Vertex AI pricing](https://cloud.google.com/vertex-ai/pricing) and\n[Firebase pricing](https://firebase.google.com/pricing) before running\nthis function. If you're new to Firebase and Google Cloud, check to see if\nyou're eligible for a\n[$300 credit](https://firebase.google.com/support/faq#pricing-free-trial) and\na Free Trial Cloud Billing account.\n\nGet started\n---------------\n\n 1. Create a [Firebase project and register a web app](https://firebase.google.com/docs/web/setup#create-firebase-project-and-app).\n 2. [Create a ReCAPTCHA Enterprise key](https://firebase.google.com/docs/app-check/web/recaptcha-enterprise-provider#project-setup)\n    in the same project.\n 3. [Enable App Check](https://firebase.google.com/docs/app-check/web/recaptcha-enterprise-provider)\n    in the Firebase console with the ReCAPTCHA Enterprise site key you created.\n 4. Copy your Firebase project config and your ReCAPTCHA Enterprise site key\n    into the appropriate places in `config.ts` in this directory.\n 5. In this directory, run `npm install`.\n 6. Set up and deploy the function as described in [../README.md](../README.md).\n 7. In this directory, run `npm run dev` to run the client.\n\nTo run this app against the `callVertexWithRC` function running in the Firebase\nemulator, ensure that `testMode`in `main.ts` is set to `true`. Before testing\nwith a deployed (i.e., not emulated) function, set `testMode` to `false`.\n\nTIP: You can build the client and deploy to Firebase Hosting by running\n     `npm run build` from the `client` directory. Hosting deliverables are\n     generated and saved in `client/dist` and you can then deploy to\n     the emulator or Firebase Hosting from the parent directory. We recommend\n     deploying to the emulator first--you can use the following command to\n     deploy the function and web client simultaneously:\n   \n     firebase serve --only functions,hosting \n\nSupport\n-------\n\n- [Firebase Support](https://firebase.google.com/support/)\n\nLicense\n-------\n\n© Google, 2024. Licensed under an [Apache-2](../../../LICENSE) license.\n"
  },
  {
    "path": "Node/call-vertex-remote-config-server/client/config.ts",
    "content": "export const firebaseConfig = {\n  YOUR_FIREBASE_CONFIG\n};\n\n  // Your ReCAPTCHA Enterprise site key (must be from the same project\n  // as the Firebase config above).\nexport const RECAPTCHA_ENTERPRISE_SITE_KEY =\n  \"YOUR_RECAPTCHA_KEY\";\n"
  },
  {
    "path": "Node/call-vertex-remote-config-server/client/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"UTF-8\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n  <title>Client app for Remote Config server function with Vertex AI and App Check</title>\n  <style>\n    body {\n      font-family: Arial, sans-serif;\n      background-color: #f2f2f2;\n      justify-content: center;\n      align-items: center;\n      height: 100vh;\n      margin: 70px;\n    }\n\n    h1 {\n      margin-bottom: 20px;\n    }\n\n    .container {\n      background-color: #ffffff;\n      border-radius: 8px;\n      box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.1);\n      padding: 20px;\n      max-width: 400px;\n      width: 100%;\n      text-align: center;\n    }\n\n    input[type=\"text\"],\n    input[type=\"submit\"] {\n      width: 35%;\n      padding: 10px;\n      margin-bottom: 10px;\n      border: 1px solid #ccc;\n      border-radius: 4px;\n      box-sizing: border-box;\n    }\n\n    input[type=\"submit\"] {\n      background-color: #FFA500;\n      color: white;\n      border: none;\n      cursor: pointer;\n      font-size: 16px;\n      font-weight: bold;\n    }\n\n    input[type=\"submit\"]:hover {\n      background-color: #45a049;\n    }\n\n    #waitingMessage,\n    #generatedText,\n    #errorMessage {\n      margin-top: 20px;\n      text-align: left;\n    }\n  </style>\n</head>\n\n<body>\n  <div>\n    <h1>Client app for Remote Config server function with Vertex AI and App Check</h1>\n    <br />\n  </div>\n  <script type=\"module\" src=\"main.ts\"></script>\n</body>\n\n</html>"
  },
  {
    "path": "Node/call-vertex-remote-config-server/client/main.ts",
    "content": "/**\n * @license\n * Copyright 2024 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { initializeApp } from \"firebase/app\";\nimport { firebaseConfig, RECAPTCHA_ENTERPRISE_SITE_KEY } from \"./config\";\nimport {\n  initializeAppCheck,\n  ReCaptchaEnterpriseProvider,\n} from \"firebase/app-check\";\nimport {\n  getFunctions,\n  httpsCallable,\n  connectFunctionsEmulator,\n} from \"firebase/functions\";\n\n// Set to true to test in emulator.\nconst testMode = true;\n\n// Use showdown to convert Gemini-provided Markdown to HTML\nimport { Converter } from \"showdown\";\nconst converter = new Converter();\n\n// Set up output elements.\nconst outputDiv = document.createElement(\"div\");\ndocument.body.appendChild(outputDiv);\n\n// Initialize Firebase app.\nconst app = initializeApp(firebaseConfig);\n\n// Initialize App Check.\ninitializeAppCheck(app, {\n  provider: new ReCaptchaEnterpriseProvider(RECAPTCHA_ENTERPRISE_SITE_KEY),\n});\n\n// Define callVertexWithRC as a call to the callVertexWithRC function.\nconst callVertexWithRC = httpsCallable(getFunctions(), \"callVertexWithRC\", {\n  limitedUseAppCheckTokens: true,\n});\n\n// Enable emulator so that it can be used in test mode.\nconst functions = getFunctions(app, \"us-central1\"); // Replace with your region\n\nif (testMode) {\n  connectFunctionsEmulator(functions, \"localhost\", 5001);\n}\n\n// Generate body for index.html.\ndocument.body.innerHTML += `\n    \n    <div id=\"waitingMessage\"></div>\n    <div id=\"generatedText\"></div>\n    <div id=\"errorMessage\"></div>\n    <br/>\n    <form id=\"promptForm\">\n        <label for=\"promptInput\">Ask Gemini a question!</label><br>\n        <input type=\"text\" id=\"promptInput\" name=\"prompt\"><br><br>\n        <input type=\"submit\" value=\"Submit\">\n    </form>\n`;\n\nconst promptForm = document.getElementById(\"promptForm\") as HTMLFormElement;\n\npromptForm.addEventListener(\"submit\", async (event) => {\n  event.preventDefault();\n  const promptInput = document.getElementById(\n    \"promptInput\"\n  ) as HTMLInputElement;\n  const prompt = promptInput.value;\n\n  const waitingMessageElement = document.getElementById(\"waitingMessage\");\n\n  // Define a variable to keep track of the number of dots\n  let dotCount = 0;\n\n  // Set interval to add dots every second\n  const intervalId = setInterval(() => {\n    // Increment dotCount\n    dotCount = (dotCount + 1) % 7;\n    const dots = \".\".repeat(dotCount);\n    waitingMessageElement.textContent = \"Waiting for response\" + dots;\n   }, 1000);\n\n  const errorMessageElement = document.getElementById(\"errorMessage\");\n  errorMessageElement.textContent = \"\";\n\n  try {\n    const { data } = await callVertexWithRC({ prompt });\n    const generatedTextElement = document.getElementById(\"generatedText\"); // Access the element\n    const htmlContent = converter.makeHtml(data);\n    if (!generatedTextElement) {\n      throw new Error(\"Missing generated text.\");\n    }\n    generatedTextElement.innerHTML = htmlContent; // Set the element's content\n    waitingMessageElement.textContent = \"\";\n    errorMessageElement.textContent = \"\";\n\n  } catch (error) {\n    errorMessageElement.textContent = \"Error calling function: \" + error.message;\n    waitingMessageElement.textContent = \"\";\n  }\n  // Clear welcome dots.\n  clearInterval(intervalId);\n});\n"
  },
  {
    "path": "Node/call-vertex-remote-config-server/client/package.json",
    "content": "{\n  \"name\": \"call-vertex-remote-config-server-client\",\n  \"version\": \"1.0.0\",\n  \"description\": \"JavaScript quickstart for Vertex AI, Firebase Remote Config server, and App Check.\",\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/firebase/functions-samples.git\"\n  },\n  \"author\": \"\",\n  \"license\": \"Apache-2.0\",\n  \"bugs\": {\n    \"url\": \"https://github.com/firebase/functions-samples/issues\"\n  },\n  \"engines\": {\n    \"npm\": \">=9.0.0 <10.0.0\",\n    \"node\": \">=18.0.0 <=20.0.0\"\n  },\n  \"homepage\": \"https://github.com/firebase/functions-samples#readme\",\n  \"devDependencies\": {\n    \"eslint-config-google\": \"^0.14.0\",\n    \"typescript\": \"^5.1.6\",\n    \"vite\": \"^4.4.9\"\n  },\n  \"scripts\": {\n    \"dev\": \"vite\",\n    \"build\": \"vite build\",\n    \"format\": \"prettier --write .\"\n  },\n  \"dependencies\": {\n    \"@firebase/functions\": \"^0.11.5\",\n    \"eslint\": \"8\",\n    \"firebase\": \"^10.12.1\",\n    \"showdown\": \"^2.1.0\"\n  }\n}\n"
  },
  {
    "path": "Node/call-vertex-remote-config-server/client/vite-env.d.ts",
    "content": "/// <reference types=\"vite/client\" />\n"
  },
  {
    "path": "Node/call-vertex-remote-config-server/client/vite.config.js",
    "content": "import { defineConfig } from 'vite';\n\nexport default defineConfig({\n  base: '',\n  build: {\n    rollupOptions: {\n      input: ['index.html','main.ts'],\n    },\n  },\n  logLevel: 'info',\n});\n"
  },
  {
    "path": "Node/call-vertex-remote-config-server/firebase.json",
    "content": "{\n  \"functions\": [\n    {\n      \"source\": \"functions\",\n      \"codebase\": \"call-vertex-remote-config-server\",\n      \"ignore\": [\n        \"node_modules\",\n        \".git\",\n        \"firebase-debug.log\",\n        \"firebase-debug.*.log\",\n        \"*.local\",\n        \"*.bak\"\n      ]\n    }\n  ],\n  \"emulators\": {\n    \"functions\": {\n      \"port\": 5001\n    },\n    \"ui\": {\n      \"enabled\": true\n    },\n    \"singleProjectMode\": true\n  },\n  \"hosting\": {\n    \"public\": \"client/dist\",\n    \"ignore\": [\n      \"firebase.json\",\n      \"**/node_modules/**\"\n    ]\n  }\n}\n"
  },
  {
    "path": "Node/call-vertex-remote-config-server/functions/index.js",
    "content": "/**\n * @license\n * Copyright 2024 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *   http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// [START remote_config_server_vertex_init]\nconst { onCall, HttpsError } = require(\"firebase-functions/https\");\nconst logger = require(\"firebase-functions/logger\");\n\nconst { initializeApp } = require(\"firebase-admin/app\");\nconst { VertexAI } = require(\"@google-cloud/vertexai\");\nconst { getRemoteConfig } = require(\"firebase-admin/remote-config\");\n\n// Allow all origins. Set origin to restrict domain access.\nconst cors = require(\"cors\")({ origin: true });\n\n// Set and check environment variables.\nconst project = process.env.GCLOUD_PROJECT;\n\n// Enable App Check\nconst appCheckRequired = true;\n\n// Initialize Firebase.\nconst app = initializeApp();\n// [END remote_config_server_vertex_init]\n\n// [START remote_config_server_vertex_default_values]\n// Define default (fallback) parameter values for Remote Config.\nconst defaultConfig = {\n  // Default values for Vertex AI.\n  model_name: \"gemini-1.5-flash-preview-0514\",\n  generation_config: [\n    {\n      stopSequences: [],\n      temperature: 0.7,\n      maxOutputTokens: 64,\n      topP: 0.1,\n      topK: 20,\n    },\n  ],\n  prompt:\n    \"I'm a developer who wants to learn about Firebase and you are a \\\n    helpful assistant who knows everything there is to know about Firebase!\",\n  safety_settings: [\n    {\n      category: \"HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT\",\n      threshold: \"HarmBlockThreshold.BLOCK_MEDIUM_AND_ABOVE\",\n    },\n  ],\n\n  // Vertex AI location.\n  location: \"us-central1\",\n\n  // Disable Vertex AI Gemini API access for testing.\n  vertex_enabled: false,\n};\n// [END remote_config_server_vertex_default_values]\n\n// [START remote_config_server_vertex_create_function]\n// Export the function.\nexports.callVertexWithRC = onCall(\n  {\n    enforceAppCheck: appCheckRequired, // Enable App Check enforcement\n    consumeAppCheckToken: false, // Don't consume the token (optional)\n  },\n  async (data, context) => {\n    try {\n      // Set up Remote Config.\n      const rc = getRemoteConfig(app);\n\n      // Get the Remote Config template and assign default values.\n      const template = await rc.getServerTemplate({\n        defaultConfig: defaultConfig,\n      });\n\n      // Add the template evaluation to a constant.\n      const config = template.evaluate();\n\n      // Obtain values from Remote Config.\n      const textModel =\n        config.getString(\"model_name\") || defaultConfig.model_name;\n      const textPrompt = config.getString(\"prompt\") || defaultConfig.prompt;\n      const generationConfig =\n        config.getString(\"generation_config\") ||\n        defaultConfig.generation_config;\n      const safetySettings =\n        config.getString(\"safety_settings\") || defaultConfig.safety_settings;\n      const location = config.getString(\"location\") || defaultConfig.location;\n      const vertexEnabled =\n        config.getBoolean(\"is_vertex_enabled\") || defaultConfig.vertex_enabled;\n      // [END remote_config_server_vertex_create_function]\n\n      // [START remote_config_server_vertex_function_logic]\n      // Allow user input.\n      const userInput = data.data.prompt || \"\";\n\n      // Instantiate Vertex AI.\n      const vertex_ai = new VertexAI({ project: project, location: location });\n      const generativeModel = vertex_ai.getGenerativeModel({\n        model: textModel,\n        safety_settings: safetySettings,\n        generation_config: generationConfig,\n      });\n\n      // Combine prompt from Remote Config with optional user input.\n      const chatInput = textPrompt + \" \" + userInput;\n\n      if (!chatInput) {\n        throw new HttpsError(\"invalid-argument\", \"Missing text prompt\");\n      }\n\n      // Check if Vertex AI is enabled\n      if (vertexEnabled !== true) {\n        logger.log(\"Vertex AI is not enabled\");\n        return;\n      }\n      logger.log(\n        \"\\nRunning with model \",\n        textModel,\n        \", prompt: \",\n        textPrompt,\n        \", generationConfig: \",\n        generationConfig,\n        \", safetySettings: \",\n        safetySettings,\n        \" in \",\n        location,\n        \"\\n\"\n      );\n\n      const result = await generativeModel.generateContentStream(chatInput);\n\n      const chunks = [];\n      for await (const item of result.stream) {\n        const chunk = item.candidates[0].content.parts[0].text;\n        logger.log(\"Received chunk:\", chunk);\n        chunks.push(chunk);\n      }\n\n      return chunks.join(\"\"); // Return the concatenated chunks\n    } catch (error) {\n      logger.error(error);\n      throw new HttpsError(\"internal\", \"Internal server error\");\n    }\n  }\n);\n// [END remote_config_server_vertex_function_logic]\n"
  },
  {
    "path": "Node/call-vertex-remote-config-server/functions/package.json",
    "content": "{\n  \"name\": \"call-vertex-remote-config-server\",\n  \"description\": \"An example of a callable function that uses the Admin SDK with server-side Remote Config, App Check, and Vertex AI\",\n  \"scripts\": {\n    \"serve\": \"firebase emulators:start --only functions\",\n    \"shell\": \"firebase functions:shell\",\n    \"start\": \"npm run shell\",\n    \"deploy\": \"firebase deploy --only functions\",\n    \"logs\": \"firebase functions:log\"\n  },\n  \"engines\": {\n    \"node\": \"22\"\n  },\n  \"main\": \"index.js\",\n  \"dependencies\": {\n    \"@google-cloud/vertexai\": \"^1.2.0\",\n    \"cors\": \"^2.8.5\",\n    \"eslint\": \"8\",\n    \"firebase-admin\": \"^13.0.2\",\n    \"firebase-functions\": \"7.0.0\"\n  },\n  \"devDependencies\": {\n    \"eslint-config-google\": \"^0.14.0\",\n    \"firebase-functions-test\": \"^3.4.0\"\n  },\n  \"private\": true\n}\n"
  },
  {
    "path": "Node/delete-unused-accounts-cron/README.md",
    "content": "# Periodically delete unused accounts\n\nThis sample demonstrates how to delete the accounts of users who have not signed-in in the last month.\n\n\n## Functions Code\n\nSee the file [functions/index.js](functions/index.js) for the code.\n\n**Note:** This function uses Cloud Scheduler, which can have associated costs. Your project must be on the Blaze payment plan as these features require billing information. See the [Cloud Scheduler pricing page](https://cloud.google.com/scheduler/pricing) for more information.\n\nThe dependencies are listed in [functions/package.json](functions/package.json).\n\n## Deploy and test\n\nTo set up the sample:\n\n - Create a Firebase Project using the [Firebase Developer Console](https://console.firebase.google.com)\n - Download this sample e.g. `git clone https://github.com/firebase/functions-samples`\n - Enter the sample directory `cd functions-samples/2nd-gen/delete-unused-accounts-cron`\n - Setup the sample with your project `firebase use --add` and follow the instructions.\n - Install node dependencies of your Functions `cd functions; npm install; cd -`\n - Deploy your project using `firebase deploy`.\n - The Cloud Scheduler job should then run once a day and delete any inactive users. You can manually run the task by [navigating to Cloud Scheduler in the Google Cloud Platform Console](https://console.cloud.google.com/cloudscheduler)."
  },
  {
    "path": "Node/delete-unused-accounts-cron/firebase.json",
    "content": "{\n    \"functions\": {\n        \"codebase\": \"delete-unused-accounts-cron\"\n    }\n}\n"
  },
  {
    "path": "Node/delete-unused-accounts-cron/functions/.eslintrc.js",
    "content": "/**\n * Copyright 2023 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nmodule.exports = {\n  root: true,\n  env: {\n    es2020: true,\n    node: true,\n  },\n  extends: [\n    \"eslint:recommended\",\n    \"google\",\n  ],\n  rules: {\n    quotes: [\"error\", \"double\"],\n  },\n};\n"
  },
  {
    "path": "Node/delete-unused-accounts-cron/functions/index.js",
    "content": "/**\n * Copyright 2022 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\"use strict\";\n\n// [START all]\n// [START import]\n// The Cloud Functions for Firebase SDK to set up triggers and logging.\nconst {onSchedule} = require(\"firebase-functions/scheduler\");\nconst {logger} = require(\"firebase-functions\");\n\n// The Firebase Admin SDK to delete inactive users.\nconst admin = require(\"firebase-admin\");\nadmin.initializeApp();\n\n// The es6-promise-pool to limit the concurrency of promises.\nconst PromisePool = require(\"es6-promise-pool\").default;\n// Maximum concurrent account deletions.\nconst MAX_CONCURRENT = 3;\n// [END import]\n\n// [START accountcleanup]\n// Run once a day at midnight, to clean up the users\n// Manually run the task here https://console.cloud.google.com/cloudscheduler\nexports.accountcleanup = onSchedule(\"every day 00:00\", async (event) => {\n  // Fetch all user details.\n  const inactiveUsers = await getInactiveUsers();\n\n  // Use a pool so that we delete maximum `MAX_CONCURRENT` users in parallel.\n  const promisePool = new PromisePool(\n      () => deleteInactiveUser(inactiveUsers),\n      MAX_CONCURRENT,\n  );\n  await promisePool.start();\n\n  logger.log(\"User cleanup finished\");\n});\n// [END accountcleanup]\n\n// [START deleteInactiveUser]\n/**\n * Deletes one inactive user from the list.\n * @param {admin.auth.UserRecord[]} inactiveUsers\n * @return {null | Promise<void>}\n */\nfunction deleteInactiveUser(inactiveUsers) {\n  if (inactiveUsers.length > 0) {\n    const userToDelete = inactiveUsers.pop();\n\n    // Delete the inactive user.\n    return admin.auth().deleteUser(userToDelete.uid).then(() => {\n      return logger.log(\n          \"Deleted user account\",\n          userToDelete.uid,\n          \"because of inactivity\",\n      );\n    }).catch((error) => {\n      return logger.error(\n          \"Deletion of inactive user account\",\n          userToDelete.uid,\n          \"failed:\",\n          error,\n      );\n    });\n  } else {\n    return null;\n  }\n}\n// [END deleteInactiveUser]\n\n// [START getInactiveUsers]\n// Returns the list of all inactive users.\n/**\n *\n * @param {admin.auth.UserRecord[]} [users] the current list of inactive users\n * @param {string} [nextPageToken]\n * @return {Promise<admin.auth.UserRecord[]>}\n */\nasync function getInactiveUsers(users = [], nextPageToken) {\n  const result = await admin.auth().listUsers(1000, nextPageToken);\n  // Find users that have not signed in in the last 30 days.\n  const inactiveUsers = result.users.filter(\n      (user) =>\n        Date.parse(\n            user.metadata.lastRefreshTime || user.metadata.lastSignInTime,\n        ) <\n      Date.now() - 30 * 24 * 60 * 60 * 1000,\n  );\n\n  // Add to the list of previously found inactive users.\n  users = users.concat(inactiveUsers);\n\n  // If there are more users to fetch we fetch them.\n  if (result.pageToken) {\n    return getInactiveUsers(users, result.pageToken);\n  }\n\n  return users;\n}\n// [END getInactiveUsers]\n// [END all]\n"
  },
  {
    "path": "Node/delete-unused-accounts-cron/functions/package.json",
    "content": "{\n  \"name\": \"delete-unused-accounts-cron-functions\",\n  \"description\": \"Periodically delete unused Firebase accounts\",\n  \"dependencies\": {\n    \"es6-promise-pool\": \"^2.5.0\",\n    \"firebase-admin\": \"^13.0.2\",\n    \"firebase-functions\": \"7.0.0\"\n  },\n  \"devDependencies\": {\n    \"eslint\": \"^8.57.1\",\n    \"eslint-config-google\": \"^0.14.0\"\n  },\n  \"scripts\": {\n    \"lint\": \"./node_modules/.bin/eslint --max-warnings=0 .\",\n    \"serve\": \"firebase emulators:start --only functions\",\n    \"shell\": \"firebase functions:shell\",\n    \"start\": \"npm run shell\",\n    \"deploy\": \"firebase deploy --only functions\",\n    \"logs\": \"firebase functions:log\",\n    \"compile\": \"cp ../../../tsconfig.template.json ./tsconfig-compile.json && tsc --project tsconfig-compile.json\"\n  },\n  \"engines\": {\n    \"node\": \"22\"\n  },\n  \"private\": true\n}\n"
  },
  {
    "path": "Node/fcm-notifications/.gitignore",
    "content": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\nfirebase-debug.log*\nfirebase-debug.*.log*\n\n# Firebase cache\n.firebase/\n\n# Firebase config\n\n# Uncomment this if you'd like others to create their own Firebase project.\n# For a team working on the same Firebase project(s), it is recommended to leave\n# it commented so all members can deploy to the same project(s) in .firebaserc.\n# .firebaserc\n\n# Runtime data\npids\n*.pid\n*.seed\n*.pid.lock\n\n# Directory for instrumented libs generated by jscoverage/JSCover\nlib-cov\n\n# Coverage directory used by tools like istanbul\ncoverage\n\n# nyc test coverage\n.nyc_output\n\n# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)\n.grunt\n\n# Bower dependency directory (https://bower.io/)\nbower_components\n\n# node-waf configuration\n.lock-wscript\n\n# Compiled binary addons (http://nodejs.org/api/addons.html)\nbuild/Release\n\n# Dependency directories\nnode_modules/\n\n# Optional npm cache directory\n.npm\n\n# Optional eslint cache\n.eslintcache\n\n# Optional REPL history\n.node_repl_history\n\n# Output of 'npm pack'\n*.tgz\n\n# Yarn Integrity file\n.yarn-integrity\n\n# dotenv environment variables file\n.env\n"
  },
  {
    "path": "Node/fcm-notifications/README.md",
    "content": "# Send Firebase Cloud Messaging notifications for new followers.\n\nThis sample demonstrates how to send a Firebase Cloud Messaging (FCM) notification from a Realtime Database triggered Function. The sample also features a Web UI to experience the FCM notification.\n\n\n## Functions Code\n\nSee file [functions/index.js](functions/index.js) for the code.\n\nSending the notification is done using the [Firebase Admin SDK](https://www.npmjs.com/package/firebase-admin). The Web client writes the individual device tokens to the realtime database which the Function uses to send the notification.\n\nThe dependencies are listed in [functions/package.json](functions/package.json).\n\n\n## Sample Database Structure\n\nUsers sign into the app and are requested to enable notifications on their browsers. If they successfully enable notifications the device token is saved into the datastore under `/users/$uid/notificationTokens`.:\n\n```\n/functions-project-12345\n    /users\n        /Uid-12345\n            displayName: \"Bob Dole\"\n            /notificationTokens\n                1234567890: true\n            photoURL: \"https://lh3.googleusercontent.com/...\"\n\n```\n\nIf a user starts following another user we'll write to `/followers/$followedUid/$followerUid`:\n\n```\n/functions-project-12345\n    /followers\n        /followedUid-12345\n            followerUid-67890: true\n    /users\n        /Uid-12345\n            displayName: \"Bob Dole\"\n            /notificationTokens\n                1234567890: true\n            photoURL: \"https://lh3.googleusercontent.com/...\"\n\n```\n\n\n## Trigger rules\n\nThe function triggers every time the value of a follow flag changes at `/followers/$followedUid/$followerUid`.\n\n\n## Deploy and test\n\nThis sample comes with a web-based UI for testing the function. To test it out:\n\n 1. Set up your Firebase project:\n     1. [Create a Firebase project](https://firebase.google.com/docs/web/setup/#create-firebase-project)\n     1. [Register your web app with Firebase](https://firebase.google.com/docs/web/setup/#register-app)\n 1. Enable **Google Provider** in the [Auth section](https://console.firebase.google.com/project/_/authentication/providers)\n 1. Clone or download this repo and open the `fcm-notification` directory.\n 1. You must have the Firebase CLI installed. If you don't have it install it with `npm install -g firebase-tools` and then configure it with `firebase login`.\n 1. Configure the CLI locally by using `firebase use --add` and select your project in the list.\n 1. Install dependencies locally by running: `cd functions; npm install; cd -`\n 1. Deploy your project using `firebase deploy`\n 1. Open the app using `firebase open hosting:site`, this will open a browser.\n 1. Start following a user, this will send a notification to them.\n"
  },
  {
    "path": "Node/fcm-notifications/database.rules.json",
    "content": "{\n  \"rules\": {\n    \"users\": {\n      \".read\": true,\n      \"$uid\": {\n        \".write\": \"auth.uid === $uid\"\n      }\n    },\n    \"followers\": {\n      \"$followedUid\": {\n        \"$followerUid\": {\n          \".read\": \"auth.uid === $followerUid\",\n          \".write\": \"auth.uid === $followerUid\"\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "Node/fcm-notifications/firebase.json",
    "content": "{\n  \"database\": {\n    \"rules\": \"database.rules.json\"\n  },\n  \"hosting\": {\n    \"public\": \"public\"\n  },\n  \"functions\": [\n    {\n      \"source\": \"functions\",\n      \"codebase\": \"fcm-notifications\",\n      \"ignore\": [\n        \"node_modules\",\n        \".git\",\n        \"firebase-debug.log\",\n        \"firebase-debug.*.log\"\n      ],\n      \"predeploy\": [\n        \"npm --prefix \\\"$RESOURCE_DIR\\\" run lint\"\n      ]\n    }\n  ]\n}\n"
  },
  {
    "path": "Node/fcm-notifications/functions/.eslintrc.cjs",
    "content": "module.exports = {\n  env: {\n    es2022: true,\n    node: true,\n  },\n  parserOptions: {\n    \"ecmaVersion\": 2022,\n    \"sourceType\": \"module\",\n  },\n  extends: [\n    \"eslint:recommended\",\n    \"google\",\n  ],\n  rules: {\n    \"no-restricted-globals\": [\"error\", \"name\", \"length\"],\n    \"prefer-arrow-callback\": \"error\",\n    \"quotes\": [\"error\", \"double\", {\"allowTemplateLiterals\": true}],\n  },\n  overrides: [\n    {\n      files: [\"**/*.spec.*\"],\n      env: {\n        mocha: true,\n      },\n      rules: {},\n    },\n  ],\n  globals: {},\n};\n"
  },
  {
    "path": "Node/fcm-notifications/functions/.gitignore",
    "content": "node_modules/"
  },
  {
    "path": "Node/fcm-notifications/functions/index.js",
    "content": "/**\n * Copyright 2023 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {initializeApp} from \"firebase-admin/app\";\nimport {getAuth} from \"firebase-admin/auth\";\nimport {getDatabase} from \"firebase-admin/database\";\nimport {getMessaging} from \"firebase-admin/messaging\";\nimport {log, warn} from \"firebase-functions/logger\";\nimport {onValueWritten} from \"firebase-functions/database\";\n\ninitializeApp();\nconst auth = getAuth();\nconst db = getDatabase();\nconst messaging = getMessaging();\n\n/**\n * Triggers when a user gets a new follower and sends a notification. Followers\n * add a flag to `/followers/{followedUid}/{followerUid}`. Users save their\n * device notification tokens to\n * `/users/{followedUid}/notificationTokens/{notificationToken}`.\n */\nexport const sendFollowerNotification = onValueWritten(\n    \"/followers/{followedUid}/{followerUid}\",\n    async (event) => {\n      // If un-follow we exit the function.\n      if (!event.data.after.val()) {\n        log(`User ${event.params.followerUid} unfollowed` +\n            ` user ${event.params.followedUid} :(`);\n        return;\n      }\n\n      log(`User ${event.params.followerUid} is now following` +\n         ` user ${event.params.followedUid}`);\n      const tokensRef =\n        db.ref(`/users/${event.params.followedUid}/notificationTokens`);\n      const notificationTokens = await tokensRef.get();\n      if (!notificationTokens.hasChildren()) {\n        log(\"There are no tokens to send notifications to.\");\n        return;\n      }\n\n      log(`There are ${notificationTokens.numChildren()} tokens` +\n          \" to send notifications to.\");\n      const followerProfile = await auth.getUser(event.params.followerUid);\n\n      // Notification details.\n      const notification = {\n        title: \"You have a new follower!\",\n        body: (followerProfile.displayName ?? \"Someone\") +\n                \" is now following you.\",\n        image: followerProfile.photoURL ?? \"\",\n      };\n\n      // Send notifications to all tokens.\n      const messages = [];\n      notificationTokens.forEach((child) => {\n        messages.push({\n          token: child.key,\n          notification: notification,\n        });\n      });\n      const batchResponse = await messaging.sendEach(messages);\n\n      if (batchResponse.failureCount < 1) {\n        // Messages sent sucessfully. We're done!\n        log(\"Messages sent.\");\n        return;\n      }\n      warn(`${batchResponse.failureCount} messages weren't sent.`,\n          batchResponse);\n\n      // Clean up the tokens that are not registered any more.\n      for (let i = 0; i < batchResponse.responses.length; i++) {\n        const errorCode = batchResponse.responses[i].error?.code;\n        const errorMessage = batchResponse.responses[i].error?.message;\n        if ((errorCode === \"messaging/invalid-registration-token\") ||\n            (errorCode === \"messaging/registration-token-not-registered\") ||\n            (errorCode === \"messaging/invalid-argument\" &&\n              errorMessage ===\n              \"The registration token is not a valid FCM registration token\")) {\n          log(`Removing invalid token: ${messages[i].token}`);\n          await tokensRef.child(messages[i].token).remove();\n        }\n      }\n    });\n"
  },
  {
    "path": "Node/fcm-notifications/functions/package.json",
    "content": "{\n  \"name\": \"functions\",\n  \"description\": \"Cloud Functions for Firebase\",\n  \"scripts\": {\n    \"lint\": \"eslint .\",\n    \"serve\": \"firebase emulators:start --only functions\",\n    \"shell\": \"firebase functions:shell\",\n    \"start\": \"npm run shell\",\n    \"deploy\": \"firebase deploy --only functions\",\n    \"logs\": \"firebase functions:log\"\n  },\n  \"engines\": {\n    \"node\": \"22\"\n  },\n  \"main\": \"index.js\",\n  \"type\": \"module\",\n  \"dependencies\": {\n    \"firebase-admin\": \"^13.0.2\",\n    \"firebase-functions\": \"7.0.0\"\n  },\n  \"devDependencies\": {\n    \"eslint\": \"^8.57.1\",\n    \"eslint-config-google\": \"^0.14.0\",\n    \"firebase-functions-test\": \"^3.4.0\"\n  },\n  \"private\": true\n}\n"
  },
  {
    "path": "Node/fcm-notifications/public/firebase-messaging-sw.js",
    "content": "/**\n * Copyright 2023 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// Import and configure the Firebase SDK\n// These scripts are made available when the app is served or deployed on Firebase Hosting\n// If you do not serve/host your project using Firebase Hosting see https://firebase.google.com/docs/web/setup\nimportScripts('/__/firebase/10.0.0/firebase-app-compat.js');\nimportScripts('/__/firebase/10.0.0/firebase-messaging-compat.js');\nimportScripts('/__/firebase/init.js');\n\nfirebase.messaging();\n"
  },
  {
    "path": "Node/fcm-notifications/public/index.html",
    "content": "<!doctype html>\n<!--\n  Copyright 2016 Google Inc. All rights reserved.\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n      https://www.apache.org/licenses/LICENSE-2.0\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License\n-->\n<html lang=\"en\">\n<head>\n  <meta charset=\"utf-8\">\n  <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n  <meta name=\"description\" content=\"Demonstrates of to authorize Firebase with Instagram Auth using Firebase Functions\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n  <title>Firebase Functions demo send FCM notifications</title>\n\n  <!-- Material Design Lite -->\n  <link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/icon?family=Material+Icons\">\n  <link rel=\"stylesheet\" href=\"https://code.getmdl.io/1.1.3/material.blue_grey-orange.min.css\">\n  <script defer src=\"https://code.getmdl.io/1.1.3/material.min.js\"></script>\n\n  <link rel=\"stylesheet\" href=\"main.css\">\n</head>\n<body>\n<div class=\"demo-layout mdl-layout mdl-js-layout mdl-layout--fixed-header\">\n\n  <!-- Header section containing title -->\n  <header class=\"mdl-layout__header mdl-color-text--white mdl-color--light-blue-700\">\n    <div class=\"mdl-cell mdl-cell--12-col mdl-cell--12-col-tablet mdl-grid\">\n      <div class=\"mdl-layout__header-row mdl-cell mdl-cell--12-col mdl-cell--12-col-tablet mdl-cell--8-col-desktop\">\n        <h3>Send FCM notifications demo</h3>\n      </div>\n    </div>\n  </header>\n  <main class=\"mdl-layout__content mdl-color--grey-100\">\n    <div class=\"mdl-cell--12-col mdl-cell--12-col-tablet mdl-grid\">\n\n      <!-- Card containing the sign-in UI -->\n      <div id=\"demo-signed-out-card\" class=\"mdl-card mdl-shadow--2dp mdl-cell\">\n        <div class=\"mdl-card__supporting-text mdl-color-text--grey-600\">\n          <p>\n            This web application demonstrates how you can send FCM notifications using Functions and a web client.\n            <strong>Now sign in and activate notifications for your user!</strong>\n          </p>\n          <button id=\"demo-sign-in-button\" class=\"mdl-color-text--grey-700 mdl-button--raised mdl-button mdl-js-button mdl-js-ripple-effect\"><i class=\"material-icons\">account_circle</i> Sign in with Google</button>\n        </div>\n      </div>\n\n      <!-- Card containing the signed-in UI -->\n      <div id=\"demo-signed-in-card\" class=\"mdl-card mdl-shadow--2dp mdl-cell\">\n        <div class=\"mdl-card__supporting-text mdl-color-text--grey-600\">\n          <p>\n            Welcome <span id=\"demo-name-container\"></span>\n          </p>\n          <div id=\"demo-fcm-error-container\"></div>\n          <button id=\"demo-sign-out-button\" class=\"mdl-color-text--grey-700 mdl-button--raised mdl-button mdl-js-button mdl-js-ripple-effect\">Sign out</button>\n          <button id=\"demo-delete-button\" class=\"mdl-color-text--grey-700 mdl-button--raised mdl-button mdl-js-button mdl-js-ripple-effect\">Delete account</button>\n        </div>\n      </div>\n\n      <!-- Card containing the users to follow -->\n      <div id=\"demo-all-users-card\" class=\"mdl-card mdl-shadow--2dp mdl-cell mdl-cell--9-col\">\n        <div class=\"mdl-card__supporting-text mdl-color-text--grey-600\">\n          <p>\n            Here are all the users. If they have enabled notifications they will get a notification when you start following them.\n          </p>\n        </div>\n        <div id=\"demo-all-users-list\"></div>\n      </div>\n    </div>\n\n    <!-- Snackbar -->\n    <div id=\"demo-snackbar\" aria-live=\"assertive\" aria-atomic=\"true\" aria-relevant=\"text\" class=\"mdl-snackbar mdl-js-snackbar\">\n      <div class=\"mdl-snackbar__text\"></div>\n      <button type=\"button\" class=\"mdl-snackbar__action\"></button>\n    </div>\n  </main>\n</div>\n\n\n<!-- Import and configure the Firebase SDK -->\n<!-- These scripts are made available when the app is served or deployed on Firebase Hosting -->\n<!-- If you do not serve/host your project using Firebase Hosting see https://firebase.google.com/docs/web/setup -->\n<script src=\"/__/firebase/10.0.0/firebase-app-compat.js\"></script>\n<script src=\"/__/firebase/10.0.0/firebase-auth-compat.js\"></script>\n<script src=\"/__/firebase/10.0.0/firebase-messaging-compat.js\"></script>\n<script src=\"/__/firebase/10.0.0/firebase-database-compat.js\"></script>\n<script src=\"/__/firebase/init.js\"></script>\n\n<script src=\"main.js\"></script>\n</body>\n</html>\n"
  },
  {
    "path": "Node/fcm-notifications/public/main.css",
    "content": "/**\n * Copyright 2016 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nhtml, body {\n  font-family: 'Roboto', 'Helvetica', sans-serif;\n}\n.mdl-grid {\n  max-width: 1024px;\n  margin: auto;\n}\n.mdl-card {\n  min-height: 0;\n  padding-bottom: 5px;\n}\n.mdl-layout__header-row {\n  padding: 0;\n}\nh3 {\n  background: url('firebase-logo.png') no-repeat;\n  background-size: 40px;\n  padding-left: 50px;\n}\n#demo-signed-out-card,\n#demo-signed-in-card {\n  display: none;\n}\n#demo-profile-pic {\n  height: 60px;\n  width: 60px;\n  border-radius: 30px;\n  margin-left: calc(50% - 30px);\n  margin-top: 10px;\n}\n#demo-name-container {\n  font-weight: bold;\n}\n#demo-fcm-error-container {\n  margin-bottom: 20px;\n}\n#demo-delete-button {\n  margin-left: 20px;\n}\n#demo-all-users-list {\n  margin-bottom: -5px;\n}\n.demo-user-container {\n  position: relative;\n}\n.demo-user-container:HOVER {\n  background-color: #eee;\n}\n.demo-profile-pic {\n  height: 50px;\n  width: 50px;\n}\n.demo-name {\n  margin-left: 15px;\n}\n.mdl-switch {\n  display: inline-block;\n  right: 0;\n  position: absolute;\n  width: auto;\n  padding-right: 40px;\n  top: 13px;\n}\n.demo-notifications-enabled {\n  color: #aaa;\n  font-size: 80%;\n  display: none;\n}\n"
  },
  {
    "path": "Node/fcm-notifications/public/main.js",
    "content": "/**\n * Copyright 2016 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use strict';\n\n// Initializes the Demo.\nfunction Demo() {\n  document.addEventListener('DOMContentLoaded', function() {\n    // Shortcuts to DOM Elements.\n    this.signInButton = document.getElementById('demo-sign-in-button');\n    this.signOutButton = document.getElementById('demo-sign-out-button');\n    this.nameContainer = document.getElementById('demo-name-container');\n    this.fcmErrorContainer = document.getElementById('demo-fcm-error-container');\n    this.deleteButton = document.getElementById('demo-delete-button');\n    this.signedOutCard = document.getElementById('demo-signed-out-card');\n    this.signedInCard = document.getElementById('demo-signed-in-card');\n    this.usersContainer = document.getElementById('demo-all-users-list');\n    this.usersCard = document.getElementById('demo-all-users-card');\n    this.snackbar = document.getElementById('demo-snackbar');\n\n    // Bind events.\n    this.signInButton.addEventListener('click', this.signIn.bind(this));\n    this.signOutButton.addEventListener('click', this.signOut.bind(this));\n    this.deleteButton.addEventListener('click', this.deleteAccount.bind(this));\n    firebase.auth().onAuthStateChanged(this.onAuthStateChanged.bind(this));\n    firebase.messaging().onMessage(this.onMessage.bind(this));\n  }.bind(this));\n}\n\n// Triggered on Firebase auth state change.\nDemo.prototype.onAuthStateChanged = function(user) {\n  // If this is just an ID token refresh we exit.\n  if (user && this.currentUid === user.uid) {\n    return;\n  }\n\n  // Remove all Firebase realtime database listeners.\n  if (this.listeners) {\n    this.listeners.forEach(function(ref) {\n      ref.off();\n    });\n  }\n  this.listeners = [];\n\n  // Adjust UI depending on user state.\n  if (user) {\n    this.nameContainer.innerText = user.displayName;\n    this.signedOutCard.style.display = 'none';\n    this.signedInCard.style.display = 'block';\n    this.usersCard.style.display = 'block';\n    firebase.database().ref(`users/${user.uid}`).update({\n      displayName: user.displayName,\n      photoURL: user.photoURL\n    });\n    this.saveToken();\n    this.displayAllUsers();\n    this.currentUid = user.uid;\n  } else {\n    this.signedOutCard.style.display = 'block';\n    this.signedInCard.style.display = 'none';\n    this.usersCard.style.display = 'none';\n    this.usersContainer.innerHTML = '';\n    this.currentUid = null;\n  }\n};\n\n// Display all users so that they can be followed.\nDemo.prototype.displayAllUsers = function() {\n  var usersRef = firebase.database().ref('users');\n  usersRef.on('child_added', function(snapshot) {\n    // Create the HTML for a user.\n    var photoURL = snapshot.val().photoURL;\n    var displayName = snapshot.val().displayName;\n    var uid = snapshot.key;\n    var userTemplate =\n        '<div class=\"demo-user-container\">' +\n        '  <img class=\"demo-profile-pic\" src=\"' + photoURL + '\">' +\n        '  <span class=\"demo-name\">' + displayName + '</span>' +\n        '  <span class=\"demo-notifications-enabled\">(notifications enabled)</span>' +\n        '  <label class=\"mdl-switch mdl-js-switch mdl-js-ripple-effect\" for=\"demo-follow-switch-' + uid + '\">' +\n        '    <input type=\"checkbox\" id=\"demo-follow-switch-' + uid + '\" class=\"mdl-switch__input\">' +\n        '    <span class=\"mdl-switch__label\">Follow</span>' +\n        '  </label>' +\n        '</div>';\n\n    // Create the DOM element from the HTML.\n    var div = document.createElement('div');\n    div.innerHTML = userTemplate;\n    var userElement = div.firstChild;\n    this.usersContainer.appendChild(userElement);\n\n    // Activate the Material Design Lite Switch element.\n    var materialSwitchContainer = userElement.getElementsByClassName('mdl-switch')[0];\n    if (componentHandler) {\n      componentHandler.upgradeElement(materialSwitchContainer);\n    }\n\n    // Check if the user has notifications enabled and show a flag if he has.\n    var notificationEnabledElement = userElement.getElementsByClassName('demo-notifications-enabled')[0];\n    var notificationsEnabledRef = snapshot.ref.child('notificationTokens');\n    notificationsEnabledRef.on('value', function(notificationsEnabledSnapshot) {\n      notificationEnabledElement.style.display = notificationsEnabledSnapshot.hasChildren() ? 'inline' : 'none';\n    });\n    this.listeners.push(notificationsEnabledRef);\n\n    // Listen for the Switch state from the Realtime database.\n    var switchElement = document.getElementById('demo-follow-switch-' + uid);\n    var followUserRef = firebase.database().ref('followers/' + uid + '/' + this.currentUid);\n    this.listeners.push(followUserRef);\n    followUserRef.on('value', function(followSnapshot) {\n      switchElement.checked = !!followSnapshot.val();\n      if (materialSwitchContainer.MaterialSwitch) {\n        materialSwitchContainer.MaterialSwitch.checkDisabled();\n        materialSwitchContainer.MaterialSwitch.checkToggleState();\n      }\n    });\n\n    // Listen for switch state changes from the user.\n    switchElement.addEventListener('change', function() {\n      followUserRef.set(!!switchElement.checked);\n    });\n  }.bind(this));\n  this.listeners.push(usersRef);\n};\n\n// Initiates the sign-in flow using LinkedIn sign in in a popup.\nDemo.prototype.signIn = function() {\n  var google = new firebase.auth.GoogleAuthProvider();\n  firebase.auth().signInWithPopup(google);\n};\n\n// Signs-out of Firebase.\nDemo.prototype.signOut = function() {\n  firebase.auth().signOut();\n};\n\n// Deletes the user's account.\nDemo.prototype.deleteAccount = function() {\n  return firebase.database().ref('users/' + this.currentUid).remove().then(function() {\n    return firebase.auth().currentUser.delete().then(function() {\n      window.alert('Account deleted');\n    }).catch(function(error) {\n      if (error.code === 'auth/requires-recent-login') {\n        window.alert('You need to have recently signed-in to delete your account. Please sign-in and try again.');\n        firebase.auth().signOut();\n      }\n    });\n  });\n};\n\n// This is called when a notification is received while the app is in focus.\n// When the app is not in focus or if the tab is closed, this function is not called and the FCM notifications is\n// handled by the Service worker which displays a browser popup notification if the browser supports it.\nDemo.prototype.onMessage = function(payload) {\n  console.log('Notifications received.', payload);\n\n  // Normally our Function sends a notification payload, we check just in case.\n  if (payload.notification) {\n    // If notifications are supported on this browser we display one.\n    // Note: This is for demo purposes only. For a good user experience it is not recommended to display browser\n    // notifications while the app is in focus. In a production app you probably want to only display some form of\n    // in-app notifications like the snackbar (see below).\n    if (window.Notification instanceof Function) {\n      // This displays a notification if notifications have been granted.\n      new Notification(payload.notification.title, payload.notification);\n    }\n    // Display the notification content in the Snackbar too.\n    this.snackbar.MaterialSnackbar.showSnackbar({message: payload.notification.body});\n  }\n};\n\n// Saves the token to the database if available. If not request permissions.\nDemo.prototype.saveToken = function() {\n  firebase.messaging().getToken().then(function(currentToken) {\n    if (currentToken) {\n      firebase.database().ref('users/' + this.currentUid + '/notificationTokens/' + currentToken).set(true);\n    } else {\n      this.requestPermission();\n    }\n  }.bind(this)).catch(function(err) {\n    console.error('Unable to get messaging token.', err);\n    if (err.code === 'messaging/permission-default') {\n      this.fcmErrorContainer.innerText = 'You have not enabled notifications on this browser. To enable notifications reload the page and allow notifications using the permission dialog.';\n    } else if (err.code === 'messaging/notifications-blocked') {\n      this.fcmErrorContainer.innerHTML = 'You have blocked notifications on this browser. To enable notifications follow these instructions: <a href=\"https://support.google.com/chrome/answer/114662?visit_id=1-636150657126357237-2267048771&rd=1&co=GENIE.Platform%3DAndroid&oco=1\">Android Chrome Instructions</a><a href=\"https://support.google.com/chrome/answer/6148059\">Desktop Chrome Instructions</a>';\n    }\n  }.bind(this));\n};\n\n// Requests permission to send notifications on this browser.\nDemo.prototype.requestPermission = function() {\n  console.log('Requesting permission...');\n  firebase.messaging().requestPermission().then(function() {\n    console.log('Notification permission granted.');\n    this.saveToken();\n  }.bind(this)).catch(function(err) {\n    console.error('Unable to get permission to notify.', err);\n  });\n};\n\n// Load the demo.\nwindow.demo = new Demo();\n"
  },
  {
    "path": "Node/instrument-with-opentelemetry/README.md",
    "content": "# Instrumenting Cloud Functions for Firebase with Open Telemetry\nThis sample demonstrates instrumenting your Cloud Functions for Firebase using [OpenTelemetry](https://opentelemetry.io).\n\nSee Firebase Summit 2022 Talk \"Observability in Cloud Functions for Firebase\" for motivations and context.\n\nOpen Telemetry SDK provides both automatic and manual instrumentation, both of which are demonstrated here. See [OpenTelemetry JS documentations](https://opentelemetry.io/docs/instrumentation/js/) for more information about how to use and configure OpenTelemetry for your javascript project.\n\n## Notable Files\n* `./tracing.js`: Initializes OpenTelemetry SDK to automatically instrument HTTP/GRPC/Express modules and export the generated traces to Google Cloud Trace.\n\n* `./.env`: Configures `NODE_OPTIONS` to preload the `tracing.js` module. This is important because OpenTelemtry SDK works by monkey-patching instrumented modules and must run first before other module is loaded.\n\n* `./index.js`: Includes sample code for generating custom spans using the OpenTelemetry API. e.g.:\n```js\nconst opentelemetry = require('@opentelemetry/api');\n\nconst tracer = opentelemetry.trace.getTracer();\nawait tracer.startActiveSpan(\"calculatePrice\", async (span) => {\n    totalUsd = await calculatePrice(productIds);\n    span.end();\n});\n```\n\n## Deploy and test\n1. Deploy your function using firebase deploy --only functions\n2. Seed Firestore with mock data.\n3. Send callable request to the deployed function, e.g.:\n```\n$ curl -X POST -H \"content-type: application/json\" https:// -d '{ \"data\": ... }'\n```\n"
  },
  {
    "path": "Node/instrument-with-opentelemetry/firebase.json",
    "content": "{\n  \"functions\": {\n    \"source\": \"functions\",\n    \"codebase\": \"instrument-with-opentelemetry\"\n  },\n  \"emulators\": {\n    \"functions\": {\n      \"port\": 5001\n    },\n    \"firestore\": {\n      \"port\": 8080\n    },\n    \"ui\": {\n      \"enabled\": true\n    },\n    \"singleProjectMode\": true\n  }\n}\n"
  },
  {
    "path": "Node/instrument-with-opentelemetry/functions/.eslintrc.js",
    "content": "/**\n * Copyright 2023 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nmodule.exports = {\n  root: true,\n  env: {\n    es2020: true,\n    node: true,\n  },\n  extends: [\n    \"eslint:recommended\",\n    \"google\",\n  ],\n  rules: {\n    quotes: [\"error\", \"double\"],\n  },\n};\n"
  },
  {
    "path": "Node/instrument-with-opentelemetry/functions/index.js",
    "content": "/**\n * Copyright 2023 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nconst {onCall} = require(\"firebase-functions/https\");\nconst logger = require(\"firebase-functions/logger\");\nconst {initializeApp} = require(\"firebase-admin/app\");\nconst {getFirestore} = require(\"firebase-admin/firestore\");\nconst opentelemetry = require(\"@opentelemetry/api\");\nconst {Timer} = require(\"./timer\");\n\ninitializeApp();\nconst db = getFirestore();\n\n/**\n * Divide an array into chunks of `chunkSize`\n * @param {any[]} arr\n * @param {Number} chunkSize\n * @return {Array<Array<any>>}\n */\nfunction sliceIntoChunks(arr, chunkSize) {\n  const res = [];\n  for (let i = 0; i < arr.length; i += chunkSize) {\n    const chunk = arr.slice(i, i + chunkSize);\n    res.push(chunk);\n  }\n  return res;\n}\n\n/**\n * Get prices for all products in `productIds` array\n * @param {string[]} productIds\n * @return {number}\n */\nasync function calculatePrice(productIds) {\n  const timer = new Timer();\n  let totalUsd = 0;\n  const products = await db.getAll(\n      ...productIds.map((id) => db.doc(`products/${id}`)),\n  );\n  for (const product of products) {\n    totalUsd += product.data()?.usd || 0;\n  }\n  logger.info(\"calculatePrice\", {calcPriceMs: timer.measureMs()});\n  return totalUsd;\n}\n\n/**\n * Sum discounts of all `productIds`\n * @param {string[]} productIds\n * @return {number}\n */\nasync function calculateDiscount(productIds) {\n  const timer = new Timer();\n\n\n  let discountUsd = 0;\n  const processConcurrently = sliceIntoChunks(productIds, 10)\n      .map(async (productIds) => {\n        const discounts = await db.collection(\"discounts\")\n            .where(\"products\", \"array-contains-any\", productIds)\n            .get();\n        for (const discount of discounts.docs) {\n          discountUsd += discount.data().usd || 0;\n        }\n      });\n  await Promise.all(processConcurrently);\n  logger.info(\"calculateDiscount\", {calcDiscountMs: timer.measureMs()});\n  return discountUsd;\n}\n\nexports.calculatetotal = onCall(async (req) => {\n  const {productIds} = req.data;\n\n  let totalUsd = 0;\n  const tracer = opentelemetry.trace.getTracer();\n  await tracer.startActiveSpan(\"calculatePrice\", async (span) => {\n    totalUsd = await calculatePrice(productIds);\n    span.end();\n  });\n  await tracer.startActiveSpan(\"calculateDiscount\", async (span) => {\n    totalUsd -= await calculateDiscount(productIds);\n    span.end();\n  });\n  return {totalUsd};\n});\n"
  },
  {
    "path": "Node/instrument-with-opentelemetry/functions/package.json",
    "content": "{\n  \"name\": \"functions\",\n  \"description\": \"Cloud Functions for Firebase\",\n  \"scripts\": {\n    \"lint\": \"eslint .\",\n    \"serve\": \"firebase emulators:start --only functions\",\n    \"shell\": \"firebase functions:shell\",\n    \"start\": \"npm run shell\",\n    \"deploy\": \"firebase deploy --only functions\",\n    \"logs\": \"firebase functions:log\"\n  },\n  \"engines\": {\n    \"node\": \"22\"\n  },\n  \"dependencies\": {\n    \"@google-cloud/opentelemetry-cloud-trace-exporter\": \"^1.1.0\",\n    \"@google-cloud/opentelemetry-cloud-trace-propagator\": \"^0.14.0\",\n    \"@opentelemetry/api\": \"^1.2.0\",\n    \"@opentelemetry/instrumentation\": \"^0.33.0\",\n    \"@opentelemetry/instrumentation-grpc\": \"^0.33.0\",\n    \"@opentelemetry/instrumentation-http\": \"^0.33.0\",\n    \"@opentelemetry/resource-detector-gcp\": \"^0.27.3\",\n    \"@opentelemetry/sdk-node\": \"^0.33.0\",\n    \"firebase-admin\": \"^13.0.2\",\n    \"firebase-functions\": \"7.0.0\",\n    \"opentelemetry-instrumentation-express\": \"^0.29.0\"\n  },\n  \"private\": true,\n  \"devDependencies\": {\n    \"eslint\": \"^8.57.1\",\n    \"eslint-config-google\": \"^0.14.0\"\n  }\n}\n"
  },
  {
    "path": "Node/instrument-with-opentelemetry/functions/timer.js",
    "content": "/**\n * Copyright 2023 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nexports.Timer = class {\n  /**\n     *\n     */\n  constructor() {\n    this.start = process.hrtime.bigint();\n  }\n\n  /**\n     * Get the time since this timer was constructed\n     * @return {string}\n     */\n  measureMs() {\n    const duration = process.hrtime.bigint() - this.start;\n    return (duration / 1000000).toString();\n  }\n};\n"
  },
  {
    "path": "Node/instrument-with-opentelemetry/functions/tracing.js",
    "content": "/**\n * Copyright 2023 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nconst opentelemetry = require(\"@opentelemetry/sdk-node\");\nconst {\n  TraceExporter,\n} = require(\"@google-cloud/opentelemetry-cloud-trace-exporter\");\nconst {HttpInstrumentation} = require(\"@opentelemetry/instrumentation-http\");\nconst {GrpcInstrumentation} = require(\"@opentelemetry/instrumentation-grpc\");\nconst {\n  ExpressInstrumentation,\n} = require(\"opentelemetry-instrumentation-express\");\nconst {gcpDetector} = require(\"@opentelemetry/resource-detector-gcp\");\nconst {\n  CloudPropagator,\n} = require(\"@google-cloud/opentelemetry-cloud-trace-propagator\");\n\n\n// Only enable OpenTelemetry if the function is actually deployed.\n// Emulators don't reflect real-world latency\"\nif (!process.env.FUNCTIONS_EMULATOR) {\n  const sdk = new opentelemetry.NodeSDK({\n    // Setup automatic instrumentation for\n    //   http, grpc, and express modules.\n    instrumentations: [\n      new HttpInstrumentation(),\n      new GrpcInstrumentation(),\n      new ExpressInstrumentation(),\n    ],\n    // Make sure opentelemetry know about Cloud Trace http headers\n    //   i.e. 'X-Cloud-Trace-Context'\n    textMapPropagator: new CloudPropagator(),\n    // Automatically detect and include span metadata when running\n    //   in GCP, e.g. region of the function.\n    resourceDetectors: [gcpDetector],\n    // Export generated traces to Cloud Trace.\n    traceExporter: new TraceExporter(),\n  });\n\n  sdk.start();\n\n  // Ensure that generated traces are exported when the container is\n  //   shutdown.\n  process.on(\"SIGTERM\", async () => {\n    await sdk.shutdown();\n  });\n}\n"
  },
  {
    "path": "Node/pnpm-workspace.yaml",
    "content": "packages:\n  - \"./**\""
  },
  {
    "path": "Node/quickstarts/auth-blocking-functions/.gitignore",
    "content": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\nfirebase-debug.log*\nfirebase-debug.*.log*\n\n# Firebase cache\n.firebase/\n\n# Firebase config\n\n# Uncomment this if you'd like others to create their own Firebase project.\n# For a team working on the same Firebase project(s), it is recommended to leave\n# it commented so all members can deploy to the same project(s) in .firebaserc.\n# .firebaserc\n\n# Runtime data\npids\n*.pid\n*.seed\n*.pid.lock\n\n# Directory for instrumented libs generated by jscoverage/JSCover\nlib-cov\n\n# Coverage directory used by tools like istanbul\ncoverage\n\n# nyc test coverage\n.nyc_output\n\n# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)\n.grunt\n\n# Bower dependency directory (https://bower.io/)\nbower_components\n\n# node-waf configuration\n.lock-wscript\n\n# Compiled binary addons (http://nodejs.org/api/addons.html)\nbuild/Release\n\n# Dependency directories\nnode_modules/\n\n# Optional npm cache directory\n.npm\n\n# Optional eslint cache\n.eslintcache\n\n# Optional REPL history\n.node_repl_history\n\n# Output of 'npm pack'\n*.tgz\n\n# Yarn Integrity file\n.yarn-integrity\n\n# dotenv environment variables file\n.env\n"
  },
  {
    "path": "Node/quickstarts/auth-blocking-functions/README.md",
    "content": "# Firebase SDK for Cloud Functions 2nd Gen Quickstart - Auth Blocking Functions\n================================================\n\nThe Auth Blocking functions Quickstart demonstrates how to block account sign in and creation when using Firebase Auth or Google Cloud Identity Platform in a Firebase App.\n\n\n- [Read more about auth blocking functions](https://firebase.google.com/docs/auth/extend-with-blocking-functions)\n- [Read more about Cloud Functions for Firebase](https://firebase.google.com/docs/functions/)\n\n\nGetting Started\n---------------\n\nTo try this sample, you need a test app with Firebase Auth and Cloud Firestore enabled. Don't use a live app with real users!\n\n1. Install dependencies with `npm install`\n2. Deploy the functions with `firebase deploy --only functions`\n3. Try to create an account using an email address with a domain _other than_ `@acme.com`. It should fail.\n4. Add an existing user's email address to the `banned` collection in Cloud Firestore. Then, try to sign in as that user. It should fail.\n\n\nLicense\n-------\n\n© Google, 2022. Licensed under an [Apache-2](../../../LICENSE) license.\n"
  },
  {
    "path": "Node/quickstarts/auth-blocking-functions/firebase.json",
    "content": "{\n  \"functions\": {\n    \"codebase\": \"auth-blocking-functions\",\n    \"predeploy\": [\n      \"npm --prefix \\\"$RESOURCE_DIR\\\" run lint\"\n    ]\n  }\n}\n"
  },
  {
    "path": "Node/quickstarts/auth-blocking-functions/functions/.eslintrc.js",
    "content": "/**\n * Copyright 2023 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nmodule.exports = {\n  root: true,\n  env: {\n    es2020: true,\n    node: true,\n  },\n  extends: [\n    \"eslint:recommended\",\n    \"google\",\n  ],\n  rules: {\n    quotes: [\"error\", \"double\"],\n  },\n};\n"
  },
  {
    "path": "Node/quickstarts/auth-blocking-functions/functions/.gitignore",
    "content": "node_modules/"
  },
  {
    "path": "Node/quickstarts/auth-blocking-functions/functions/index.js",
    "content": "/**\n * Copyright 2022 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nconst {\n  beforeUserCreated,\n  beforeUserSignedIn,\n  HttpsError,\n} = require(\"firebase-functions/identity\");\nconst {admin} = require(\"firebase-admin\");\n\nadmin.initializeApp();\nconst db = admin.firestore();\n\n// [START v2ValidateNewUser]\n// [START v2beforeCreateFunctionTrigger]\n// Block account creation with any non-acme email address.\nexports.validatenewuser = beforeUserCreated((event) => {\n  // [END v2beforeCreateFunctionTrigger]\n  // [START v2readUserData]\n  // User data passed in from the CloudEvent.\n  const user = event.data;\n  // [END v2readUserData]\n\n  // [START v2domainHttpsError]\n  // Only users of a specific domain can sign up.\n  if (!user?.email?.includes(\"@acme.com\")) {\n    // Throw an HttpsError so that Firebase Auth rejects the account creation.\n    throw new HttpsError(\"invalid-argument\", \"Unauthorized email\");\n  }\n  // [END v2domainHttpsError]\n});\n// [END v2ValidateNewUser]\n\n// [START v2CheckForBan]\n// [START v2beforeSignInFunctionTrigger]\n// Block account sign in with any banned account.\nexports.checkforban = beforeUserSignedIn(async (event) => {\n  // [END v2beforeSignInFunctionTrigger]\n  // [START v2readEmailData]\n  // Email passed from the CloudEvent.\n  const email = event.data.email || \"\";\n  // [END v2readEmailData]\n\n  // [START v2documentGet]\n  // Obtain a document in Firestore of the banned email address.\n  const doc = await db.collection(\"banned\").doc(email).get();\n  // [END v2documentGet]\n\n  // [START v2bannedHttpsError]\n  // Checking that the document exists for the email address.\n  if (doc.exists) {\n    // Throw an HttpsError so that Firebase Auth rejects the account sign in.\n    throw new HttpsError(\"invalid-argument\", \"Unauthorized email\");\n  }\n  // [END v2bannedHttpsError]\n});\n// [START v2CheckForBan]\n"
  },
  {
    "path": "Node/quickstarts/auth-blocking-functions/functions/package.json",
    "content": "{\n  \"name\": \"functions\",\n  \"description\": \"Cloud Functions for Firebase\",\n  \"scripts\": {\n    \"lint\": \"eslint .\",\n    \"serve\": \"firebase emulators:start --only functions\",\n    \"shell\": \"firebase functions:shell\",\n    \"start\": \"npm run shell\",\n    \"deploy\": \"firebase deploy --only functions\",\n    \"logs\": \"firebase functions:log\"\n  },\n  \"engines\": {\n    \"node\": \"22\"\n  },\n  \"main\": \"index.js\",\n  \"dependencies\": {\n    \"firebase-admin\": \"^13.0.2\",\n    \"firebase-functions\": \"7.0.0\"\n  },\n  \"devDependencies\": {\n    \"eslint\": \"^8.57.1\",\n    \"eslint-config-google\": \"^0.14.0\",\n    \"firebase-functions-test\": \"^3.4.0\"\n  },\n  \"private\": true\n}\n"
  },
  {
    "path": "Node/quickstarts/callable-functions/.gitignore",
    "content": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\nfirebase-debug.log*\nfirebase-debug.*.log*\n\n# Firebase cache\n.firebase/\n\n# Firebase config\n\n# Uncomment this if you'd like others to create their own Firebase project.\n# For a team working on the same Firebase project(s), it is recommended to leave\n# it commented so all members can deploy to the same project(s) in .firebaserc.\n# .firebaserc\n\n# Runtime data\npids\n*.pid\n*.seed\n*.pid.lock\n\n# Directory for instrumented libs generated by jscoverage/JSCover\nlib-cov\n\n# Coverage directory used by tools like istanbul\ncoverage\n\n# nyc test coverage\n.nyc_output\n\n# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)\n.grunt\n\n# Bower dependency directory (https://bower.io/)\nbower_components\n\n# node-waf configuration\n.lock-wscript\n\n# Compiled binary addons (http://nodejs.org/api/addons.html)\nbuild/Release\n\n# Dependency directories\nnode_modules/\n\n# Optional npm cache directory\n.npm\n\n# Optional eslint cache\n.eslintcache\n\n# Optional REPL history\n.node_repl_history\n\n# Output of 'npm pack'\n*.tgz\n\n# Yarn Integrity file\n.yarn-integrity\n\n# dotenv environment variables file\n.env\n"
  },
  {
    "path": "Node/quickstarts/callable-functions/README.md",
    "content": "Firebase HTTPS Callable functions Quickstart\n================================================\n\nThe HTTPS Callable functions Quickstart demonstrates how to send requests to a server-side function and get a response back using one of the Client SDKs. It interoperates with the Web, iOS and Android quickstarts.\n\n\n[Read more about Cloud Functions for Firebase](https://firebase.google.com/docs/functions/)\n\n\nGetting Started\n---------------\n\n1. Install dependencies with `npm install` and deploy with `firebase deploy --only functions`\n1. Set up a client (import the functions client SDK, initializeApp, init the functions sdk)\n2. Call the function\n\n\nLicense\n-------\n\n© Google, 2022. Licensed under an [Apache-2](../../../LICENSE) license.\n"
  },
  {
    "path": "Node/quickstarts/callable-functions/firebase.json",
    "content": "{\n  \"functions\": {\n    \"codebase\": \"callable-functions\",\n    \"predeploy\": [\n      \"npm --prefix \\\"$RESOURCE_DIR\\\" run lint\"\n    ]\n  }\n}\n"
  },
  {
    "path": "Node/quickstarts/callable-functions/functions/.eslintrc.js",
    "content": "/**\n * Copyright 2023 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nmodule.exports = {\n  root: true,\n  env: {\n    es2020: true,\n    node: true,\n  },\n  extends: [\n    \"eslint:recommended\",\n    \"google\",\n  ],\n  rules: {\n    quotes: [\"error\", \"double\"],\n  },\n};\n"
  },
  {
    "path": "Node/quickstarts/callable-functions/functions/.gitignore",
    "content": "node_modules/"
  },
  {
    "path": "Node/quickstarts/callable-functions/functions/index.js",
    "content": "/**\n * Copyright 2022 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// [START imports]\n// Dependencies for callable functions.\nconst {onCall, HttpsError} = require(\"firebase-functions/https\");\nconst {logger} = require(\"firebase-functions\");\n\n// Dependencies for the addMessage function.\nconst {getDatabase} = require(\"firebase-admin/database\");\nconst sanitizer = require(\"./sanitizer\");\n// [END imports]\n\n// [START v2allAdd]\n// [START v2addFunctionTrigger]\n// Adds two numbers to each other.\nexports.addnumbers = onCall((request) => {\n  // [END v2addFunctionTrigger]\n  // [START v2readAddData]\n  // Numbers passed from the client.\n  const firstNumber = request.data.firstNumber;\n  const secondNumber = request.data.secondNumber;\n  // [END v2readAddData]\n\n  // [START v2addHttpsError]\n  // Checking that attributes are present and are numbers.\n  if (!Number.isFinite(firstNumber) || !Number.isFinite(secondNumber)) {\n    // Throwing an HttpsError so that the client gets the error details.\n    throw new HttpsError(\"invalid-argument\", \"The function must be called \" +\n            \"with two arguments \\\"firstNumber\\\" and \\\"secondNumber\\\" which \" +\n            \"must both be numbers.\");\n  }\n  // [END v2addHttpsError]\n\n  // [START v2returnAddData]\n  // returning result.\n  return {\n    firstNumber: firstNumber,\n    secondNumber: secondNumber,\n    operator: \"+\",\n    operationResult: firstNumber + secondNumber,\n  };\n  // [END v2returnAddData]\n});\n// [END v2allAdd]\n\n// [START v2messageFunctionTrigger]\n// Saves a message to the Firebase Realtime Database but sanitizes the\n// text by removing swearwords.\nexports.addmessage = onCall((request) => {\n  // [START_EXCLUDE]\n  // [START v2readMessageData]\n  // Message text passed from the client.\n  const text = request.data.text;\n  // [END v2readMessageData]\n  // [START v2messageHttpsErrors]\n  // Checking attribute.\n  if (!(typeof text === \"string\") || text.length === 0) {\n    // Throwing an HttpsError so that the client gets the error details.\n    throw new HttpsError(\"invalid-argument\", \"The function must be called \" +\n            \"with one arguments \\\"text\\\" containing the message text to add.\");\n  }\n  // Checking that the user is authenticated.\n  if (!request.auth) {\n    // Throwing an HttpsError so that the client gets the error details.\n    throw new HttpsError(\"failed-precondition\", \"The function must be \" +\n            \"called while authenticated.\");\n  }\n  // [END v2messageHttpsErrors]\n\n  // [START v2authIntegration]\n  // Authentication / user information is automatically added to the request.\n  const uid = request.auth.uid;\n  const name = request.auth.token.name || null;\n  const picture = request.auth.token.picture || null;\n  const email = request.auth.token.email || null;\n  // [END v2authIntegration]\n\n  // [START v2returnMessageAsync]\n  // Saving the new message to the Realtime Database.\n  const sanitizedMessage = sanitizer.sanitizeText(text); // Sanitize message.\n\n  return getDatabase().ref(\"/messages\").push({\n    text: sanitizedMessage,\n    author: {uid, name, picture, email},\n  }).then(() => {\n    logger.info(\"New Message written\");\n    // Returning the sanitized message to the client.\n    return {text: sanitizedMessage};\n  })\n  // [END v2returnMessageAsync]\n      .catch((error) => {\n        // Re-throwing the error as an HttpsError so that the client gets\n        // the error details.\n        throw new HttpsError(\"unknown\", error.message, error);\n      });\n  // [END_EXCLUDE]\n});\n// [END v2messageFunctionTrigger]\n"
  },
  {
    "path": "Node/quickstarts/callable-functions/functions/package.json",
    "content": "{\n  \"name\": \"functions\",\n  \"description\": \"Cloud Functions for Firebase\",\n  \"scripts\": {\n    \"lint\": \"eslint .\",\n    \"lintfix\": \"eslint . --fix\",\n    \"serve\": \"firebase emulators:start --only functions\",\n    \"shell\": \"firebase functions:shell\",\n    \"start\": \"npm run shell\",\n    \"deploy\": \"firebase deploy --only functions\",\n    \"logs\": \"firebase functions:log\",\n    \"compile\": \"cp ../../../../tsconfig.template.json ./tsconfig-compile.json && tsc --project tsconfig-compile.json\"\n  },\n  \"engines\": {\n    \"node\": \"22\"\n  },\n  \"main\": \"index.js\",\n  \"dependencies\": {\n    \"bad-words\": \"^3.0.4\",\n    \"firebase-admin\": \"^13.0.2\",\n    \"firebase-functions\": \"7.0.0\"\n  },\n  \"devDependencies\": {\n    \"eslint\": \"^8.57.1\",\n    \"eslint-config-google\": \"^0.14.0\",\n    \"firebase-functions-test\": \"^3.4.0\"\n  },\n  \"private\": true\n}\n"
  },
  {
    "path": "Node/quickstarts/callable-functions/functions/sanitizer.js",
    "content": "/**\n * Copyright 2022 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\"use strict\";\n\nconst Filter = require(\"bad-words\");\nconst badWordsFilter = new Filter();\n\n// Sanitizes the given text if needed by replacing bad words with '*'.\nexports.sanitizeText = (text) => {\n  // Re-capitalize if the user is Shouting.\n  if (isShouting(text)) {\n    console.log(\"User is shouting. Fixing sentence case...\");\n    text = stopShouting(text);\n  }\n\n  // Moderate if the user uses SwearWords.\n  if (containsSwearwords(text)) {\n    console.log(\"User is swearing. moderating...\");\n    text = replaceSwearwords(text);\n  }\n\n  return text;\n};\n\n/**\n * Returns true if the string contains swearwords.\n * @param {string} message\n * @return {boolean}\n */\nfunction containsSwearwords(message) {\n  return message !== badWordsFilter.clean(message);\n}\n\n/**\n * Hide all swearwords. e.g: Crap => ****.\n * @param {string} message\n * @return {string}\n */\nfunction replaceSwearwords(message) {\n  return badWordsFilter.clean(message);\n}\n\n/**\n * Detect if the current message is shouting. i.e. there are too many Uppercase\n * characters or exclamation points.\n * @param {string} message message to be analyzed\n * @return {boolean}\n */\nfunction isShouting(message) {\n  return message.replace(/[^A-Z]/g, \"\").length > message.length / 2 ||\n   message.replace(/[^!]/g, \"\").length >= 3;\n}\n\n/**\n * Correctly capitalize the string as a sentence (e.g. uppercase after dots)\n * and remove exclamation ppints.\n * @param {string} message message to capitalize\n * @return {string} capitalized string\n */\nfunction stopShouting(message) {\n  const sentenceCaseRegex = /(:?\\.\\s?|^)([A-Za-z\\u00C0-\\u1FFF\\u2800-\\uFFFD])/gi;\n  const noExclamationsRegex = /!+/g;\n\n  return message\n      .toLowerCase()\n      .replace(sentenceCaseRegex, (match) => match.toUpperCase())\n      .replace(noExclamationsRegex, \".\");\n}\n"
  },
  {
    "path": "Node/quickstarts/callable-functions-streaming/.gitignore",
    "content": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\nfirebase-debug.log*\nfirebase-debug.*.log*\n\n# Firebase cache\n.firebase/\n\n# Firebase config\n\n# Uncomment this if you'd like others to create their own Firebase project.\n# For a team working on the same Firebase project(s), it is recommended to leave\n# it commented so all members can deploy to the same project(s) in .firebaserc.\n# .firebaserc\n\n# Runtime data\npids\n*.pid\n*.seed\n*.pid.lock\n\n# Directory for instrumented libs generated by jscoverage/JSCover\nlib-cov\n\n# Coverage directory used by tools like istanbul\ncoverage\n\n# nyc test coverage\n.nyc_output\n\n# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)\n.grunt\n\n# Bower dependency directory (https://bower.io/)\nbower_components\n\n# node-waf configuration\n.lock-wscript\n\n# Compiled binary addons (http://nodejs.org/api/addons.html)\nbuild/Release\n\n# Dependency directories\nnode_modules/\n\n# Optional npm cache directory\n.npm\n\n# Optional eslint cache\n.eslintcache\n\n# Optional REPL history\n.node_repl_history\n\n# Output of 'npm pack'\n*.tgz\n\n# Yarn Integrity file\n.yarn-integrity\n\n# dotenv environment variables file\n.env\n"
  },
  {
    "path": "Node/quickstarts/callable-functions-streaming/README.md",
    "content": "Firebase HTTPS Callable functions streaming quickstart\n================================================\n\nThis quickstart demonstrates how to send requests to a server-side function and _stream_ a response to a client SDK.\n\n[Read more about Cloud Functions for Firebase](https://firebase.google.com/docs/functions/)\n\n\nGetting Started\n---------------\n\n1. Install dependencies with `npm install`\n1. Start the hosting and functions emulators with `firebase emulators:start --only functions,hosting`\n1. Visit the url of the emulated Hosting site, and click \"Get forecasts\"\n\nLicense\n-------\n\n© Google, 2025. Licensed under an [Apache-2](../../../LICENSE) license.\n"
  },
  {
    "path": "Node/quickstarts/callable-functions-streaming/firebase.json",
    "content": "{\n  \"functions\": {\n    \"codebase\": \"callable-functions\",\n    \"predeploy\": [\n      \"npm --prefix \\\"$RESOURCE_DIR\\\" run lint\"\n    ]\n  },\n  \"hosting\": {\n    \"public\": \"website\"\n  }\n}\n"
  },
  {
    "path": "Node/quickstarts/callable-functions-streaming/functions/.eslintrc.js",
    "content": "/**\n * Copyright 2023 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nmodule.exports = {\n  root: true,\n  env: {\n    es2020: true,\n    node: true,\n  },\n  extends: [\n    \"eslint:recommended\",\n    \"google\",\n  ],\n  rules: {\n    quotes: [\"error\", \"double\"],\n  },\n};\n"
  },
  {
    "path": "Node/quickstarts/callable-functions-streaming/functions/.gitignore",
    "content": "node_modules/"
  },
  {
    "path": "Node/quickstarts/callable-functions-streaming/functions/index.js",
    "content": "/**\n * Copyright 2022 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// [START full-sample]\n// Dependencies for callable functions.\nconst {onCall, HttpsError} = require(\"firebase-functions/https\");\n\n/**\n * Gets the weather from the national weather service\n * https://www.weather.gov/documentation/services-web-api\n *\n * @param {number} lat\n * @param {number} lng\n */\nasync function weatherForecastApi(lat, lng) {\n  const resp = await fetch(`https://api.weather.gov/points/${lat},${lng}`);\n\n  if (!resp.ok) {\n    return `error: ${resp.status}`;\n  }\n\n  const forecastUrl = (await resp.json()).properties.forecast;\n  const forecastResp = await fetch(forecastUrl);\n\n  if (!forecastResp.ok) {\n    return `error: ${forecastResp.status}`;\n  }\n\n  // add an artificial wait to emphasize stream-iness\n  await new Promise((resolve) => setTimeout(resolve, Math.random() * 1500));\n\n  return forecastResp.json();\n}\n\n// [START streaming-callable]\nexports.getForecast = onCall(async (request, response) => {\n  if (request.data?.locations?.length < 1) {\n    throw new HttpsError(\"invalid-argument\", \"Missing locations to forecast\");\n  }\n\n  // fetch forecast data for all requested locations\n  const allRequests = request.data.locations.map(\n      async ({latitude, longitude}) => {\n        const forecast = await weatherForecastApi(latitude, longitude);\n        const result = {latitude, longitude, forecast};\n\n        // clients that support streaming will have each\n        // forecast streamed to them as they complete\n        if (request.acceptsStreaming) {\n          response.sendChunk(result);\n        }\n\n        return result;\n      },\n  );\n\n  // Return the full set of data to all clients\n  return Promise.all(allRequests);\n});\n// [END streaming-callable]\n// [END full-sample]\n"
  },
  {
    "path": "Node/quickstarts/callable-functions-streaming/functions/package.json",
    "content": "{\n  \"name\": \"callable-functions-streaming\",\n  \"description\": \"Cloud Functions for Firebase\",\n  \"scripts\": {\n    \"lint\": \"eslint .\",\n    \"lintfix\": \"eslint . --fix\",\n    \"serve\": \"firebase emulators:start --only functions\",\n    \"shell\": \"firebase functions:shell\",\n    \"start\": \"npm run shell\",\n    \"deploy\": \"firebase deploy --only functions\",\n    \"logs\": \"firebase functions:log\",\n    \"compile\": \"cp ../../../../tsconfig.template.json ./tsconfig-compile.json && tsc --project tsconfig-compile.json\"\n  },\n  \"engines\": {\n    \"node\": \"22\"\n  },\n  \"main\": \"index.js\",\n  \"dependencies\": {\n    \"firebase-admin\": \"^13.0.2\",\n    \"firebase-functions\": \"7.0.0\"\n  },\n  \"devDependencies\": {\n    \"eslint\": \"^8.57.1\",\n    \"eslint-config-google\": \"^0.14.0\",\n    \"firebase-functions-test\": \"^3.4.0\"\n  },\n  \"private\": true\n}\n"
  },
  {
    "path": "Node/quickstarts/callable-functions-streaming/website/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf-8\" />\n    <title>title</title>\n  </head>\n  <body>\n    <h1>Many forecasts</h1>\n    <p>Click the button to get the forecast for all your favorite locations.</p>\n    <button id=\"btn-call-func\">Get forecasts</button>\n    <div id=\"forecasts\"></div>\n    <script type=\"module\">\n      import { initializeApp } from \"https://www.gstatic.com/firebasejs/11.2.0/firebase-app.js\";\n      import {\n        getFunctions,\n        httpsCallable,\n        connectFunctionsEmulator,\n      } from \"https://www.gstatic.com/firebasejs/11.2.0/firebase-functions.js\";\n\n      const callableButton = document.getElementById(\"btn-call-func\");\n      const resultElement = document.getElementById(\"forecasts\");\n\n      callableButton.onclick = handleClick;\n\n      // https://firebase.google.com/docs/hosting/reserved-urls#sdk_auto-configuration\n      const firebaseConfig = await fetch(\"/__/firebase/init.json\").then(\n        (response) => {\n          return response.json();\n        }\n      );\n      const app = initializeApp(firebaseConfig);\n      const functions = getFunctions(app);\n      connectFunctionsEmulator(functions, \"127.0.0.1\", 5001);\n\n      const favoriteLocations = [\n        {\n          name: \"The Googleplex\",\n          latitude: 37.4220199895279,\n          longitude: -122.08531347325561,\n        },\n        {\n          name: \"Yosemite Valley\",\n          latitude: 37.745192257741984,\n          longitude: -119.5945133017153,\n        },\n        {\n          name: \"Old Faithful\",\n          latitude: 44.46037818049411,\n          longitude: -110.82802255265777,\n        },\n      ];\n\n      async function handleClick() {\n        // reset result\n        initializeUi();\n\n        // [START stream_data_client]\n        // Get the callable by passing an initialized functions SDK.\n        const getForecast = httpsCallable(functions, \"getForecast\");\n\n        // Call the function with the `.stream()` method to start streaming.\n        const { stream, data } = await getForecast.stream({\n          locations: favoriteLocations,\n        });\n\n        // The `stream` async iterable returned by `.stream()`\n        // will yield a new value every time the callable\n        // function calls `sendChunk()`.\n        for await (const forecastDataChunk of stream) {\n          // update the UI every time a new chunk is received\n          // from the callable function\n          updateUi(forecastDataChunk);\n        }\n\n        // The `data` promise resolves when the callable\n        // function completes.\n        const allWeatherForecasts = await data;\n        finalizeUi(allWeatherForecasts);\n        // [END stream_data_client]\n      }\n\n      function initializeUi() {\n        resultElement.innerHTML = \"\";\n        callableButton.disabled = true;\n        callableButton.innerText = \"Streaming forecasts...\";\n      }\n\n      function finalizeUi() {\n        callableButton.disabled = false;\n        callableButton.innerText = \"Get forecasts\";\n      }\n\n      function updateUi(forecastData) {\n        const newWeatherCard = document.createElement(\"div\");\n\n        const locationName = document.createElement(\"h2\");\n        locationName.innerHTML = favoriteLocations.find(\n          (v) => v.latitude === forecastData.latitude\n        ).name;\n\n        const forecast = document.createElement(\"p\");\n        console.log(forecastData);\n        forecast.innerHTML =\n          forecastData.forecast.properties.periods[0].detailedForecast;\n\n        newWeatherCard.append(locationName, forecast);\n        resultElement.appendChild(newWeatherCard);\n      }\n    </script>\n  </body>\n</html>\n"
  },
  {
    "path": "Node/quickstarts/custom-events/ README.md",
    "content": "Firebase Custom Events sample\n================================================\n\nA custom event trigger function that handles `firebase.extensions.storage-resize-images.v1.complete` and adds custom workflow.\n\n\nGetting Started\n---------------\n\n1. Install the [Resize Images\n](https://firebase.google.com/products/extensions/firebase-storage-resize-images)\n1. Install dependencies with `npm install` and deploy with `firebase deploy --only functions`\n1. Upload a test image to the storage bucket configured for the extension.\n\nLicense\n-------\n\n© Google, 2022. Licensed under an [Apache-2](../../../LICENSE) license."
  },
  {
    "path": "Node/quickstarts/custom-events/firebase.json",
    "content": "{\n  \"functions\": {\n    \"codebase\": \"custom-events\",\n    \"predeploy\": [\n      \"npm --prefix \\\"$RESOURCE_DIR\\\" run lint\"\n    ]\n  }\n}\n"
  },
  {
    "path": "Node/quickstarts/custom-events/functions/.eslintrc.js",
    "content": "/**\n * Copyright 2023 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nmodule.exports = {\n  root: true,\n  env: {\n    es2020: true,\n    node: true,\n  },\n  extends: [\n    \"eslint:recommended\",\n    \"google\",\n  ],\n  rules: {\n    quotes: [\"error\", \"double\"],\n  },\n};\n"
  },
  {
    "path": "Node/quickstarts/custom-events/functions/index.js",
    "content": "/**\n * Copyright 2022 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// [START import]\nconst {onCustomEventPublished} = require(\"firebase-functions/eventarc\");\nconst logger = require(\"firebase-functions/logger\");\nconst {initializeApp} = require(\"firebase-admin/app\");\nconst {getFirestore} = require(\"firebase-admin/firestore\");\n\n// [END import]\n\ninitializeApp();\n\n// [START imageresizedEvent]\nexports.onimageresized = onCustomEventPublished(\n    \"firebase.extensions.storage-resize-images.v1.complete\",\n    (event) => {\n      logger.info(\"Received image resize completed event\", event);\n      // For example, write resized image details into Firestore.\n      return getFirestore()\n          .collection(\"images\")\n          .doc(event.subject.replace(\"/\", \"_\")) // original file path\n          .set(event.data); // resized images paths and sizes\n    });\n// [END imageresizedEvent]\n"
  },
  {
    "path": "Node/quickstarts/custom-events/functions/package.json",
    "content": "{\n  \"name\": \"functions-custom-events\",\n  \"description\": \"Custom event handler for Image Resizer extension.\",\n  \"scripts\": {\n    \"lint\": \"eslint .\",\n    \"lint:fix\": \"eslint . --fix\",\n    \"serve\": \"firebase emulators:start --only functions\",\n    \"shell\": \"firebase functions:shell\",\n    \"start\": \"npm run shell\",\n    \"deploy\": \"firebase deploy --only functions\",\n    \"logs\": \"firebase functions:log\",\n    \"compile\": \"cp ../../../../tsconfig.template.json ./tsconfig-compile.json && tsc --project tsconfig-compile.json\"\n  },\n  \"engines\": {\n    \"node\": \"22\"\n  },\n  \"main\": \"index.js\",\n  \"dependencies\": {\n    \"firebase-admin\": \"^13.0.2\",\n    \"firebase-functions\": \"7.0.0\"\n  },\n  \"devDependencies\": {\n    \"eslint\": \"^8.57.1\",\n    \"eslint-config-google\": \"^0.14.0\"\n  },\n  \"private\": true\n}\n"
  },
  {
    "path": "Node/quickstarts/firestore-sync-auth/.gitignore",
    "content": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\nfirebase-debug.log*\n\n# Firebase cache\n.firebase/\n\n# Firebase config\n\n# Uncomment this if you'd like others to create their own Firebase project.\n# For a team working on the same Firebase project(s), it is recommended to leave\n# it commented so all members can deploy to the same project(s) in .firebaserc.\n# .firebaserc\n\n# Runtime data\npids\n*.pid\n*.seed\n*.pid.lock\n\n# Directory for instrumented libs generated by jscoverage/JSCover\nlib-cov\n\n# Coverage directory used by tools like istanbul\ncoverage\n\n# nyc test coverage\n.nyc_output\n\n# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)\n.grunt\n\n# Bower dependency directory (https://bower.io/)\nbower_components\n\n# node-waf configuration\n.lock-wscript\n\n# Compiled binary addons (http://nodejs.org/api/addons.html)\nbuild/Release\n\n# Dependency directories\nnode_modules/\n\n# Optional npm cache directory\n.npm\n\n# Optional eslint cache\n.eslintcache\n\n# Optional REPL history\n.node_repl_history\n\n# Output of 'npm pack'\n*.tgz\n\n# Yarn Integrity file\n.yarn-integrity\n\n# dotenv environment variables file\n.env\n"
  },
  {
    "path": "Node/quickstarts/firestore-sync-auth/README.md",
    "content": "# Firebase SDK for Cloud Functions Quickstart - Firestore with auth context\n\nThis quickstart demonstrates using the **Firebase SDK for Cloud Functions** with\n**Firestore** with authentication context.\n\n## Introduction\n\nThis sample app adds auth metadata to all documents written to a collection.\n\n## Set up the sample\n\nBefore you can test the functions locally or deploy to a Firebase project,\nyou'll need to run `npm install` in the `functions` directory.\n\n## Run locally with the Firebase Emulator suite\n\nThe\n[Firebase Local Emulator Suite](https://firebase.google.com/docs/emulator-suite)\nallows you to build and test apps on your local machine instead of deploying to\na Firebase project.\n\n1. Create a Firebase project in the\n   [Firebase Console](https://console.firebase.google.com)\n   > _Wondering why this step is needed?_ Even though the emulator will run this\n   > sample on your local machine, it needs to interact with a Firebase project\n   > to retrieve some configuration values.\n1. [Set up or update the Firebase CLI](https://firebase.google.com/docs/cli#setup_update_cli)\n1. Run `firebase emulators:start`\n1. Open the Emulator Suite UI\n   1. Look in the output of the `firebase emulators:start` command for the URL\n      of the Emulator Suite UI. It defaults to\n      [localhost:4000](http://localhost:4000), but may be hosted on a different\n      port on your machine.\n   1. Enter that URL in your browser to open the UI.\n1. Trigger the functions\n   1. Look in the output of the `firebase emulators:start` command for the URL\n      of the http function \"verifyComment\". It will look similar to:\n      `http://localhost:5001/MY_PROJECT/us-central1/verifyComment`\n      1. `MY_PROJECT` will be replaced with your project ID\n      1. The port may be different on your local machine\n   1. Create a new document in the `comments` collection in Firestore in the emulator UI.\n1. View the effects of the functions in the Emulator Suite UI\n\n   1. In the \"Logs\" tab, you should see new logs indicating that the functions\n      \"verifyComment\" and \"makeuppercase\" ran:\n\n      > `functions: Beginning execution of \"verifyComment\"`\n\n   1. In the \"Firestore\" tab, you should see the document containing your original\n      message updated to include auth context.\n\n## Deploy and test on a live Firebase project\n\nTo deploy and test the sample:\n\n1. Create a Firebase project on the\n   [Firebase Console](https://console.firebase.google.com)\n1. Deploy your project's code using `firebase deploy`\n1. Create a new document in the `comments` collection in Firestore in the Firebase console.\n\nYou should see the document containing your original message updated to include auth context.\n\n## Contributing\n\nWe'd love that you contribute to the project. Before doing so please read our\n[Contributor guide](../../CONTRIBUTING.md).\n\n## License\n\n© Google, 2023. Licensed under an [Apache-2](../../LICENSE) license.\n"
  },
  {
    "path": "Node/quickstarts/firestore-sync-auth/firebase.json",
    "content": "{\n  \"functions\": {\n    \"codebase\": \"firestore-sync-auth\"\n  },\n  \"firestore\": {\n    \"rules\": \"firestore.rules\",\n    \"indexes\": \"firestore.indexes.json\"\n  },\n  \"emulators\": {\n    \"functions\": {\n      \"port\": 5001\n    },\n    \"firestore\": {\n      \"port\": 8080\n    },\n    \"ui\": {\n      \"enabled\": true\n    }\n  }\n}\n"
  },
  {
    "path": "Node/quickstarts/firestore-sync-auth/firestore.indexes.json",
    "content": "{\n  \"indexes\": [],\n  \"fieldOverrides\": []\n}\n"
  },
  {
    "path": "Node/quickstarts/firestore-sync-auth/firestore.rules",
    "content": "service cloud.firestore {\n  match /databases/{database}/documents {\n    match /comments/{comment} {\n      // Allow authenticated users to read/write the comments collection\n      allow read, write: if request.auth != null;\n    }\n  }\n}\n"
  },
  {
    "path": "Node/quickstarts/firestore-sync-auth/functions/.eslintrc.js",
    "content": "/**\n * Copyright 2023 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nmodule.exports = {\n  root: true,\n  env: {\n    es2020: true,\n    node: true,\n  },\n  extends: [\n    \"eslint:recommended\",\n    \"google\",\n  ],\n  rules: {\n    quotes: [\"error\", \"double\"],\n  },\n};\n"
  },
  {
    "path": "Node/quickstarts/firestore-sync-auth/functions/.gitignore",
    "content": "node_modules/"
  },
  {
    "path": "Node/quickstarts/firestore-sync-auth/functions/index.js",
    "content": "/**\n * Copyright 2023 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the 'License');\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an 'AS IS' BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\"use strict\";\n\n// [START all]\n// [START import]\n// The Cloud Functions for Firebase SDK to create Cloud Functions and triggers.\nconst {\n  onDocumentWrittenWithAuthContext,\n} = require(\"firebase-functions/firestore\");\nconst {logger} = require(\"firebase-functions\");\n\nexports.verifyComment = onDocumentWrittenWithAuthContext(\n    \"comments/{commentId}\",\n    (event) => {\n      const snapshot = event.data.after;\n      if (!snapshot) {\n        logger.log(\"No data associated with the event\");\n        return;\n      }\n\n      // retrieve auth context from event\n      const {authType, authId} = event;\n\n      let verified = false;\n      if (authType === \"system\") {\n      // system-generated users are automatically verified\n        verified = true;\n      } else if (authType === \"unknown\" || authType === \"unauthenticated\") {\n      // admin users from a specific domain are verified\n        if (authId.endsWith(\"@example.com\")) {\n          verified = true;\n        }\n      }\n\n      // add auth medadata to the document\n      return snapshot.ref.set(\n          {\n            created_by: authId ?? \"undefined\",\n            verified,\n          },\n          {merge: true},\n      );\n    },\n);\n\n// [END all]\n"
  },
  {
    "path": "Node/quickstarts/firestore-sync-auth/functions/package.json",
    "content": "{\n  \"name\": \"firestore-sync-auth\",\n  \"description\": \"Cloud Functions for Firebase\",\n  \"dependencies\": {\n    \"firebase-admin\": \"^13.0.2\",\n    \"firebase-functions\": \"7.0.0\"\n  },\n  \"devDependencies\": {\n    \"chai\": \"^4.3.6\",\n    \"chai-as-promised\": \"^7.1.1\",\n    \"eslint\": \"^8.57.1\",\n    \"eslint-config-google\": \"^0.14.0\",\n    \"mocha\": \"^7.2.0\",\n    \"sinon\": \"^9.2.4\"\n  },\n  \"scripts\": {\n    \"lint\": \"./node_modules/.bin/eslint --max-warnings=0 .\",\n    \"serve\": \"firebase emulators:start --only functions\",\n    \"shell\": \"firebase functions:shell\",\n    \"start\": \"npm run shell\",\n    \"deploy\": \"firebase deploy --only functions\",\n    \"logs\": \"firebase functions:log\",\n    \"compile\": \"cp ../../../../tsconfig.template.json ./tsconfig-compile.json && tsc --project tsconfig-compile.json\"\n  },\n  \"engines\": {\n    \"node\": \"22\"\n  },\n  \"private\": true\n}\n"
  },
  {
    "path": "Node/quickstarts/genkit-helloworld/.gitignore",
    "content": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\nfirebase-debug.log*\nfirebase-debug.*.log*\n\n# Firebase cache\n.firebase/\n\n# Firebase config\n\n# Uncomment this if you'd like others to create their own Firebase project.\n# For a team working on the same Firebase project(s), it is recommended to leave\n# it commented so all members can deploy to the same project(s) in .firebaserc.\n# .firebaserc\n\n# Runtime data\npids\n*.pid\n*.seed\n*.pid.lock\n\n# Directory for instrumented libs generated by jscoverage/JSCover\nlib-cov\n\n# Coverage directory used by tools like istanbul\ncoverage\n\n# nyc test coverage\n.nyc_output\n\n# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)\n.grunt\n\n# Bower dependency directory (https://bower.io/)\nbower_components\n\n# node-waf configuration\n.lock-wscript\n\n# Compiled binary addons (http://nodejs.org/api/addons.html)\nbuild/Release\n\n# Dependency directories\nnode_modules/\n\n# Optional npm cache directory\n.npm\n\n# Optional eslint cache\n.eslintcache\n\n# Optional REPL history\n.node_repl_history\n\n# Output of 'npm pack'\n*.tgz\n\n# Yarn Integrity file\n.yarn-integrity\n\n# dotenv environment variables file\n.env\n"
  },
  {
    "path": "Node/quickstarts/genkit-helloworld/README.md",
    "content": "Genkit quickstart\n================================================\n\nThis quickstart demonstrates how to initialize a [Genkit flow](https://firebase.google.com/docs/genkit/flows) and serve it with Cloud Functions for Firebase.\n\n[Read more about Cloud Functions for Firebase](https://firebase.google.com/docs/functions/)\n\n\nGetting Started\n---------------\n\n1. Install dependencies with `npm install`\n1. Start the functions emulator with `firebase emulators:start --only functions`\n    1. The emulator will output the function URL. It is usually of the form:\n\n    ```\n    https://127.0.0.1:5001/{$PROJECT}/us-central1/tellJoke\n    ```\n\n1. Call the function from a terminal, replacing the `url` argument with your function's URL:\n\n    ```bash\n    $ curl -X POST \\\n        --url https://127.0.0.1:5001/{$PROJECT}/us-central1/tellJoke \\\n        --header \"Content-Type: application/json\" \\\n        --header \"Accept: text/event-stream\" \\\n        --data '{\"data\": \"Observational comedy\"}'\n    ```\n\nLicense\n-------\n\n© Google, 2025. Licensed under an [Apache-2](../../../LICENSE) license."
  },
  {
    "path": "Node/quickstarts/genkit-helloworld/firebase.json",
    "content": "{\n  \"functions\": {\n    \"codebase\": \"oncallgenkit-helloworld\",\n    \"predeploy\": [\n      \"npm --prefix \\\"$RESOURCE_DIR\\\" run lint\"\n    ]\n  }\n}\n"
  },
  {
    "path": "Node/quickstarts/genkit-helloworld/functions/.eslintrc.js",
    "content": "/**\n * Copyright 2025 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nmodule.exports = {\n  root: true,\n  env: {\n    es2020: true,\n    node: true,\n  },\n  extends: [\n    \"eslint:recommended\",\n    \"google\",\n  ],\n  rules: {\n    quotes: [\"error\", \"double\"],\n  },\n};\n"
  },
  {
    "path": "Node/quickstarts/genkit-helloworld/functions/.gitignore",
    "content": "node_modules/"
  },
  {
    "path": "Node/quickstarts/genkit-helloworld/functions/index.js",
    "content": "/**\n * Copyright 2025 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// [START complete-example]\n// [START imports]\n// [START import-trigger]\nconst {onCallGenkit} = require(\"firebase-functions/https\");\n// [END import-trigger]\n// [START import-params]\nconst {defineSecret} = require(\"firebase-functions/params\");\n// [END import-params]\n\n// [START import-genkit]\n// Dependencies for Genkit.\nconst {gemini15Flash, googleAI} = require(\"@genkit-ai/googleai\");\nconst {genkit, z} = require(\"genkit\");\n// [END import-genkit]\n// [END imports]\n\n// [START define-secret]\n// Store the Gemini API key in Cloud Secret Manager.\nconst apiKey = defineSecret(\"GOOGLE_GENAI_API_KEY\");\n// [END define-secret]\n\n// [START flow]\nconst ai = genkit({\n  plugins: [googleAI()],\n  model: gemini15Flash,\n});\n\nconst jokeTeller = ai.defineFlow({\n  name: \"jokeTeller\",\n  inputSchema: z.string().nullable(),\n  outputSchema: z.string(),\n  streamSchema: z.string(),\n}, async (jokeType = \"knock-knock\", {sendChunk}) => {\n  const prompt = `Tell me a ${jokeType} joke.`;\n\n  // Call the `generateStream()` method to\n  // receive the `stream` async iterable.\n  const {stream, response: aiResponse} = ai.generateStream(prompt);\n\n  // Send new words of the generative AI response\n  // to the client as they are generated.\n  for await (const chunk of stream) {\n    sendChunk(chunk.text);\n  }\n\n  // Return the full generative AI response\n  // to clients that may not support streaming.\n  return (await aiResponse).text;\n},\n);\n// [END flow]\n\n// [START trigger]\nexports.tellJoke = onCallGenkit({\n  // [START bind-secrets]\n  // Bind the Gemini API key secret parameter to the function.\n  secrets: [apiKey],\n  // [END bind-secrets]\n},\n// Pass in the genkit flow.\njokeTeller,\n);\n// [END trigger]\n// [END complete-example]\n"
  },
  {
    "path": "Node/quickstarts/genkit-helloworld/functions/package.json",
    "content": "{\n  \"name\": \"functions\",\n  \"description\": \"Cloud Functions for Firebase\",\n  \"scripts\": {\n    \"lint\": \"eslint .\",\n    \"lintfix\": \"eslint . --fix\",\n    \"serve\": \"firebase emulators:start --only functions\",\n    \"shell\": \"firebase functions:shell\",\n    \"start\": \"npm run shell\",\n    \"deploy\": \"firebase deploy --only functions\",\n    \"logs\": \"firebase functions:log\",\n    \"compile\": \"cp ../../../../tsconfig.template.json ./tsconfig-compile.json && tsc --project tsconfig-compile.json\"\n  },\n  \"engines\": {\n    \"node\": \"22\"\n  },\n  \"main\": \"index.js\",\n  \"dependencies\": {\n    \"@genkit-ai/googleai\": \"1.0.0-rc.12\",\n    \"firebase-admin\": \"^13.0.2\",\n    \"firebase-functions\": \"7.0.0\",\n    \"genkit\": \"1.0.0-rc.12\"\n  },\n  \"devDependencies\": {\n    \"eslint\": \"^8.57.1\",\n    \"eslint-config-google\": \"^0.14.0\",\n    \"firebase-functions-test\": \"^3.4.0\"\n  },\n  \"private\": true\n}"
  },
  {
    "path": "Node/quickstarts/https-time-server/README.md",
    "content": "# Firebase SDK for Cloud Functions Quickstart - HTTPS trigger\n\nThis quickstart demonstrates using the **Firebase SDK for Cloud Functions** with an HTTPS trigger through building an endpoint returning the current time.\n\n\n## Introduction\n\nThe function `date` returns the current server date. You can pass it a `format` URL Query parameter to format the date.\n\nFurther reading:\n\n - [Read more about the Firebase SDK for Cloud Functions](https://firebase.google.com/docs/functions)\n\n\n## Initial setup, build tools and dependencies\n\n### 1. Clone this repo\n\nClone or download this repo and open the `quickstarts/time-server` directory.\n\n\n### 2. Create a Firebase project and configure the quickstart\n\nCreate a Firebase Project on the [Firebase Console](https://console.firebase.google.com).\n\nSet up your Firebase project by running `firebase use --add`, select your Project ID and follow the instructions.\n\n\n### 3. Install the Firebase CLI and enable Functions on your Firebase CLI\n\nYou need to have installed the Firebase CLI. If you haven't run:\n\n```bash\nnpm install -g firebase-tools\n```\n\n> Doesn't work? You may need to [change npm permissions](https://docs.npmjs.com/getting-started/fixing-npm-permissions).\n\n\n## Deploy the app to prod\n\nFirst you need to install the `npm` dependencies of the functions:\n\n```bash\ncd functions && npm install; cd ..\n```\n\nThis installs locally:\n - The Firebase SDK and the Firebase Functions SDK.\n - The [moment](https://www.npmjs.com/package/moment) npm package to format time.\n\nDeploy to Firebase using the following command:\n\n```bash\nfirebase deploy\n```\n\nThis deploys and activates the date Function.\n\n> The first time you call `firebase deploy` on a new project with Functions will take longer than usual.\n\n\nAlteratively, you can call `firebase emulators:start` to test the functions on the local emulator suite.\n\n\n## Try the sample\n\nAfter deploying the function, check the CLI's output to see the URL for your function.\n\nIt will look something like: `https://<function-name>-<random-hash>-<region>.a.run.app`\n\nYou can also send the format in the request body. For instance using cURL in the command line:\n\n```bash\ncurl -H 'Content-Type: application/json' /\n     -d '{\"format\": \"MMMM Do YYYY, h:mm:ss a\"}' /\n     <function url>/date\n```\nFormatted dates should be displayed.\n\nWe are responding with a 403 error in case of PUT requests:\n\n```bash\ncurl -X PUT -d '{\"format\": \"MMMM Do YYYY, h:mm:ss a\"}' <function-url>/date\n```\n\n\n## Contributing\n\nWe'd love that you contribute to the project. Before doing so please read our [Contributor guide](../../CONTRIBUTING.md).\n\n\n## License\n\n© Google, 2022. Licensed under an [Apache-2](../../../LICENSE) license.\n"
  },
  {
    "path": "Node/quickstarts/https-time-server/firebase.json",
    "content": "{\n  \"functions\": {\n    \"codebase\": \"https-time-server\",\n    \"predeploy\": [\n      \"npm --prefix \\\"$RESOURCE_DIR\\\" run lint\"\n    ]\n  }\n}\n"
  },
  {
    "path": "Node/quickstarts/https-time-server/functions/.eslintrc.js",
    "content": "/**\n * Copyright 2023 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nmodule.exports = {\n  root: true,\n  env: {\n    es2020: true,\n    node: true,\n  },\n  extends: [\n    \"eslint:recommended\",\n    \"google\",\n  ],\n  rules: {\n    quotes: [\"error\", \"double\"],\n  },\n};\n"
  },
  {
    "path": "Node/quickstarts/https-time-server/functions/index.js",
    "content": "/**\n * Copyright 2022 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\"use strict\";\n\n// [START v2httpImport]\nconst {onRequest} = require(\"firebase-functions/https\");\n// [END v2httpImport]\n\n// [START v2httpAdditionalImports]\nconst logger = require(\"firebase-functions/logger\");\n// Moments library to format dates.\nconst moment = require(\"moment\");\n// [END v2httpAdditionalImports]\n\n// [START v2httpAll]\n/**\n * Returns the server's date.\n * Options `timeoutSeconds` and `region` are optional.\n *\n * You must provide a `format` URL query parameter or `format` value in\n * the request body with which we'll try to format the date.\n *\n * Format must follow the Node moment library. See: http://momentjs.com/\n *\n * Example format: \"MMMM Do YYYY, h:mm:ss a\".\n * Example request using URL query parameters:\n *   https://us-central1-<project-id>.cloudfunctions.net/date?format=MMMM%20Do%20YYYY%2C%20h%3Amm%3Ass%20a\n * Example request using request body with cURL:\n *   curl -H 'Content-Type: application/json' /\n *        -d '{\"format\": \"MMMM Do YYYY, h:mm:ss a\"}' /\n *        https://us-central1-<project-id>.cloudfunctions.net/date\n */\n// [START v2httpTrigger]\nexports.date = onRequest(\n    {timeoutSeconds: 1200, region: [\"us-west1\", \"us-east1\"]},\n    (req, res) => {\n    // [END v2httpTrigger]\n\n      // [START v2httpSendError]\n      // Forbidding PUT requests.\n      if (req.method === \"PUT\") {\n        res.status(403).send(\"Forbidden!\");\n        return;\n      }\n      // [END v2httpSendError]\n\n      // Reading date format from URL query parameter.\n      // [START v2httpReadQueryParam]\n      let format = req.query.format;\n      // [END v2httpReadQueryParam]\n\n      // Reading date format from request body query parameter\n      if (!format) {\n      // [START v2httpReadBodyParam]\n        format = req.body.format;\n      // [END v2httpReadBodyParam]\n      }\n\n      // [START v2httpSendResponse]\n      const formattedDate = moment().format(`${format}`);\n      logger.log(\"Sending formatted date:\", formattedDate);\n      res.status(200).send(formattedDate);\n    // [END v2httpSendResponse]\n    },\n);\n// [END v2httpAll]\n"
  },
  {
    "path": "Node/quickstarts/https-time-server/functions/package.json",
    "content": "{\n  \"name\": \"time-server-functions\",\n  \"description\": \"A simple time server using HTTPS Cloud Function\",\n  \"dependencies\": {\n    \"firebase-admin\": \"^13.0.2\",\n    \"firebase-functions\": \"7.0.0\",\n    \"moment\": \"^2.29.4\"\n  },\n  \"devDependencies\": {\n    \"eslint\": \"^8.57.1\",\n    \"eslint-config-google\": \"^0.14.0\"\n  },\n  \"scripts\": {\n    \"lint\": \"./node_modules/.bin/eslint --max-warnings=0 .\",\n    \"serve\": \"firebase emulators:start --only functions\",\n    \"shell\": \"firebase functions:shell\",\n    \"start\": \"npm run shell\",\n    \"deploy\": \"firebase deploy --only functions\",\n    \"logs\": \"firebase functions:log\",\n    \"compile\": \"cp ../../../../tsconfig.template.json ./tsconfig-compile.json && tsc --project tsconfig-compile.json\"\n  },\n  \"engines\": {\n    \"node\": \"22\"\n  },\n  \"private\": true\n}\n"
  },
  {
    "path": "Node/quickstarts/monitor-cloud-logging/README.md",
    "content": "# Firebase SDK for Cloud Functions Quickstart - Structured Logs\n\nThis quickstart demonstrates using the **`logger`** subpackage to write structured logs to Cloud Logging.\n\nLearn more about logging in Cloud Functions for Firebase [in the docs](https://firebase.google.com/docs/functions/writing-and-viewing-logs).\n\n## License\n\n© Google, 2023. Licensed under an [Apache-2](../../../LICENSE) license."
  },
  {
    "path": "Node/quickstarts/monitor-cloud-logging/firebase.json",
    "content": "{\n  \"functions\": [\n    {\n      \"source\": \"functions\",\n      \"codebase\": \"monitor-cloud-logging\",\n      \"ignore\": [\n        \"node_modules\",\n        \".git\",\n        \"firebase-debug.log\",\n        \"firebase-debug.*.log\"\n      ],\n      \"predeploy\": [\n        \"npm --prefix \\\"$RESOURCE_DIR\\\" run lint\"\n      ]\n    }\n  ]\n}\n"
  },
  {
    "path": "Node/quickstarts/monitor-cloud-logging/functions/.eslintrc.js",
    "content": "/**\n * Copyright 2023 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nmodule.exports = {\n  env: {\n    es6: true,\n    node: true,\n  },\n  parserOptions: {\n    \"ecmaVersion\": 2018,\n  },\n  extends: [\n    \"eslint:recommended\",\n    \"google\",\n  ],\n  rules: {\n    \"no-restricted-globals\": [\"error\", \"name\", \"length\"],\n    \"prefer-arrow-callback\": \"error\",\n    \"quotes\": [\"error\", \"double\", {\"allowTemplateLiterals\": true}],\n  },\n  overrides: [\n    {\n      files: [\"**/*.spec.*\"],\n      env: {\n        mocha: true,\n      },\n      rules: {},\n    },\n  ],\n  globals: {},\n};\n"
  },
  {
    "path": "Node/quickstarts/monitor-cloud-logging/functions/index.js",
    "content": "/**\n * Copyright 2023 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nconst {onRequest} = require(\"firebase-functions/https\");\nconst {\n  onRegressionAlertPublished,\n} = require(\"firebase-functions/alerts/crashlytics\");\n\n// [START loggerImport]\n// All available logging functions\nconst {\n  log,\n  info,\n  debug,\n  warn,\n  error,\n  write,\n} = require(\"firebase-functions/logger\");\n// [END loggerImport]\n\nconst {initializeApp} = require(\"firebase-admin/app\");\nconst {getFirestore} = require(\"firebase-admin/firestore\");\n// Initialize admin sdk for Firestore read in getInspirationalQuote\ninitializeApp();\n\n// [START helloLogs]\nexports.helloWorld = onRequest((request, response) => {\n  // sends a log to Cloud Logging\n  log(\"Hello logs!\");\n\n  response.send(\"Hello from Firebase!\");\n});\n// [END helloLogs]\n\n// [START logsKitchenSink]\nexports.getInspirationalQuote = onRequest(async (request, response) => {\n  const db = getFirestore();\n  const today = new Date();\n  const quoteOfTheMonthRef = db\n      .collection(\"quotes\")\n      .doc(`${today.getFullYear()}`)\n      .collection(\"months\")\n      .doc(`${today.getMonth()}`);\n\n  const DEFAULT_QUOTE =\n      \"You miss 100% of the shots you don't take. -Wayne Gretzky\";\n  let quote;\n  try {\n    const quoteOfTheMonthDocSnap = await quoteOfTheMonthRef.get();\n\n    // Attach relevant debugging information with debug()\n    debug(\"Monthly quote fetch result\", {\n      docRef: quoteOfTheMonthRef.path,\n      exists: quoteOfTheMonthDocSnap.exists,\n      createTime: quoteOfTheMonthDocSnap.createTime,\n    });\n\n    if (quoteOfTheMonthDocSnap.exists) {\n      quote = quoteOfTheMonthDocSnap.data().text;\n    } else {\n      // Use warn() for lower-severity issues than error()\n      warn(\"Quote not found for month, sending default instead\", {\n        docRef: quoteOfTheMonthRef.path,\n        dateRequested: today.toLocaleDateString(\"en-US\"),\n      });\n\n      quote = DEFAULT_QUOTE;\n    }\n    // [START logError]\n  } catch (err) {\n    // Attach an error object as the second argument\n    error(\"Unable to read quote from Firestore, sending default instead\",\n        err);\n    // [END logError]\n\n    quote = DEFAULT_QUOTE;\n  }\n\n  // Attach relevant structured data to any log\n  info(\"Sending a quote!\", {quote: quote});\n  response.json({inspirationalQuote: quote});\n});\n// [END logsKitchenSink]\n\n// [START customLogWrite]\nexports.appHasARegression = onRegressionAlertPublished((event) => {\n  write({\n    // write() lets you set additional severity levels\n    // beyond the built-in logger functions\n    severity: \"EMERGENCY\",\n    message: \"Regression in production app\",\n    issue: event.data.payload.issue,\n    lastOccurred: event.data.payload.resolveTime,\n  });\n});\n// [END customLogWrite]\n"
  },
  {
    "path": "Node/quickstarts/monitor-cloud-logging/functions/package.json",
    "content": "{\n  \"name\": \"functions\",\n  \"description\": \"Cloud Functions for Firebase\",\n  \"scripts\": {\n    \"lint\": \"eslint .\",\n    \"serve\": \"firebase emulators:start --only functions\",\n    \"shell\": \"firebase functions:shell\",\n    \"start\": \"npm run shell\",\n    \"deploy\": \"firebase deploy --only functions\",\n    \"logs\": \"firebase functions:log\",\n    \"compile\": \"cp ../../../../tsconfig.template.json ./tsconfig-compile.json && tsc --project tsconfig-compile.json\"\n  },\n  \"engines\": {\n    \"node\": \"22\"\n  },\n  \"main\": \"index.js\",\n  \"dependencies\": {\n    \"firebase-admin\": \"^13.0.2\",\n    \"firebase-functions\": \"7.0.0\"\n  },\n  \"devDependencies\": {\n    \"eslint\": \"^8.57.1\",\n    \"eslint-config-google\": \"^0.14.0\",\n    \"firebase-functions-test\": \"^3.4.0\"\n  },\n  \"private\": true\n}\n"
  },
  {
    "path": "Node/quickstarts/pubsub-helloworld/.gitignore",
    "content": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\nfirebase-debug.log*\nfirebase-debug.*.log*\n\n# Firebase cache\n.firebase/\n\n# Firebase config\n\n# Uncomment this if you'd like others to create their own Firebase project.\n# For a team working on the same Firebase project(s), it is recommended to leave\n# it commented so all members can deploy to the same project(s) in .firebaserc.\n# .firebaserc\n\n# Runtime data\npids\n*.pid\n*.seed\n*.pid.lock\n\n# Directory for instrumented libs generated by jscoverage/JSCover\nlib-cov\n\n# Coverage directory used by tools like istanbul\ncoverage\n\n# nyc test coverage\n.nyc_output\n\n# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)\n.grunt\n\n# Bower dependency directory (https://bower.io/)\nbower_components\n\n# node-waf configuration\n.lock-wscript\n\n# Compiled binary addons (http://nodejs.org/api/addons.html)\nbuild/Release\n\n# Dependency directories\nnode_modules/\n\n# Optional npm cache directory\n.npm\n\n# Optional eslint cache\n.eslintcache\n\n# Optional REPL history\n.node_repl_history\n\n# Output of 'npm pack'\n*.tgz\n\n# Yarn Integrity file\n.yarn-integrity\n\n# dotenv environment variables file\n.env\n"
  },
  {
    "path": "Node/quickstarts/pubsub-helloworld/README.md",
    "content": "# Firebase SDK for Cloud Functions Quickstart - PubSub trigger\n\nThis quickstart demonstrates how to setup a PubSub triggered Cloud Function using the **Firebase SDK for Cloud Functions**.\n\n\n## Introduction\n\nWe'll deploy a PubSub triggered Functions that prints out a Hello World message to the Cloud Logs.\n\nFurther reading:\n\n - [Read more about the Firebase SDK for Cloud Functions](https://firebase.google.com/docs/functions/)\n\n\n## Initial setup, build tools and dependencies\n\n### 1. Clone this repo\n\nClone or download this repo and open the `2nd-gen/pubsub-helloworld` directory.\n\n\n### 2. Create a Firebase project and configure the quickstart\n\nCreate a Firebase Project on the [Firebase Console](https://console.firebase.google.com).\n\nSet up your Firebase project by running `firebase use --add`, select your Project ID and follow the instructions.\n\n\n### 3. Install the Firebase CLI and enable Functions on your Firebase CLI\n\nYou need to have installed the Firebase CLI. If you haven't run:\n\n```bash\nnpm install -g firebase-tools\n```\n\n> Doesn't work? You may need to [change npm permissions](https://docs.npmjs.com/getting-started/fixing-npm-permissions).\n\n\n## Deploy the app to prod\n\nFirst you need to install the `npm` dependencies of the functions:\n\n```bash\ncd functions && npm install; cd ..\n```\n\nThis installs locally the Firebase Admin SDK and the Firebase SDK for Cloud Functions.\n\nDeploy to Firebase using the following command:\n\n```bash\nfirebase deploy\n```\n\nThis deploys and activate the PubSub hello World Functions.\n\n> The first time you call `firebase deploy` on a new project with Functions will take longer than usual.\n\n\n## Try the sample\n\nOnce deployed, to try the sample use the `gcloud` CLI to publish a message to the `topic-name` topic:\n\n```\ngcloud pubsub topics publish topic-name --message='YourName'\n```\n\nOpen the Functions logs in the Firebase Console, you should see a messages that reads \"Hello YourName\".\n\nThen you can also publish a message to the `another-topic-name` topic using JSON data:\n\n```\ngcloud pubsub topics publish another-topic-name --message='{\"name\":\"YourName\"}'\n```\n\nOpen the Functions logs in the Firebase Console, you should see a messages that reads \"Hello YourName\".\n\nLast you can also publish a message to the `yet-another-topic-name` topic using JSON data:\n\n```\ngcloud pubsub topics publish yet-another-topic-name --attribute name=YourName\n```\n\nOpen the Functions logs in the Firebase Console, you should see a messages that reads \"Hello YourName\".\n\n\n## Contributing\n\nWe'd love that you contribute to the project. Before doing so please read our [Contributor guide](../../../CONTRIBUTING.md).\n\n\n## License\n\n© Google, 2022. Licensed under an [Apache-2](../../../LICENSE) license."
  },
  {
    "path": "Node/quickstarts/pubsub-helloworld/firebase.json",
    "content": "{\n  \"functions\": {\n    \"codebase\": \"pubsub-helloworld\",\n    \"predeploy\": [\n      \"npm --prefix \\\"$RESOURCE_DIR\\\" run lint\"\n    ]\n  }\n}\n"
  },
  {
    "path": "Node/quickstarts/pubsub-helloworld/functions/.eslintrc.js",
    "content": "/**\n * Copyright 2023 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nmodule.exports = {\n  root: true,\n  env: {\n    es2020: true,\n    node: true,\n  },\n  extends: [\n    \"eslint:recommended\",\n    \"google\",\n  ],\n  rules: {\n    quotes: [\"error\", \"double\"],\n  },\n};\n"
  },
  {
    "path": "Node/quickstarts/pubsub-helloworld/functions/.gitignore",
    "content": "node_modules/"
  },
  {
    "path": "Node/quickstarts/pubsub-helloworld/functions/index.js",
    "content": "/**\n * Copyright 2022 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\"use strict\";\n\n// [START v2import]\nconst {onMessagePublished} = require(\"firebase-functions/pubsub\");\nconst logger = require(\"firebase-functions/logger\");\n// [END v2import]\n\n// [START v2helloWorld]\n/**\n * Cloud Function to be triggered by Pub/Sub that logs a message using the\n *  data published to the topic.\n */\n// [START v2trigger]\nexports.hellopubsub = onMessagePublished(\"topic-name\", (event) => {\n  // [END v2trigger]\n  // [START v2readBase64]\n  // Decode the PubSub Message body.\n  const message = event.data.message;\n  const messageBody = message.data ?\n        Buffer.from(message.data, \"base64\").toString() :\n        null;\n  // [END v2readBase64]\n  // Print the message in the logs.\n  logger.log(`Hello ${messageBody || \"World\"}!`);\n  return null;\n});\n// [END v2helloWorld]\n\n\n/**\n * Cloud Function to be triggered by Pub/Sub that logs a message using the\n *  data published to the topic as JSON.\n */\nexports.hellopubsubjson = onMessagePublished(\"another-topic-name\", (event) => {\n  // [START v2readJson]\n  // Get the `name` attribute of the PubSub message JSON body.\n  let name = null;\n  try {\n    name = event.data.message.json.name;\n  } catch (e) {\n    logger.error(\"PubSub message was not JSON\", e);\n  }\n  // [END v2readJson]\n  // Print the message in the logs.\n  logger.log(`Hello ${name || \"World\"}!`);\n  return null;\n});\n\n/**\n * Cloud Function to be triggered by Pub/Sub that logs a message using the\n *  data attributes published to the topic.\n */\nexports.hellopubsubattributes = onMessagePublished(\"yet-another-topic-name\",\n    (event) => {\n      // [START v2readAttributes]\n      // Get the `name` attribute of the message.\n      const name = event.data.message.attributes.name;\n      // [END v2readAttributes]\n      // Print the message in the logs.\n      logger.log(`Hello ${name || \"World\"}!`);\n      return null;\n    });\n"
  },
  {
    "path": "Node/quickstarts/pubsub-helloworld/functions/package.json",
    "content": "{\n  \"name\": \"functions\",\n  \"description\": \"Cloud Functions for Firebase\",\n  \"scripts\": {\n    \"lint\": \"eslint .\",\n    \"serve\": \"firebase emulators:start --only functions\",\n    \"shell\": \"firebase functions:shell\",\n    \"start\": \"npm run shell\",\n    \"deploy\": \"firebase deploy --only functions\",\n    \"logs\": \"firebase functions:log\"\n  },\n  \"engines\": {\n    \"node\": \"22\"\n  },\n  \"main\": \"index.js\",\n  \"dependencies\": {\n    \"firebase-admin\": \"^13.0.2\",\n    \"firebase-functions\": \"7.0.0\"\n  },\n  \"devDependencies\": {\n    \"eslint\": \"^8.57.1\",\n    \"eslint-config-google\": \"^0.14.0\",\n    \"firebase-functions-test\": \"^3.4.0\"\n  },\n  \"private\": true\n}\n"
  },
  {
    "path": "Node/quickstarts/testlab-matrix-completed/README.md",
    "content": "# Log test complete\n\nThis sample demonstrates how to trigger a function in response to the\ncompletion of a Test Matrix in Firebase Test Lab.\n\n## Setting up the sample\n\n1. Clone or download this repo and open the `2nd-gen/test-complete`\n   directory.\n1. You must have the Firebase CLI installed. If you don't have it install it\n   with `npm install -g firebase-tools` and then configure it with\n   `firebase login`.\n1. Configure the CLI locally by using `firebase use --add` and select your\n   project in the list.\n1. Install Cloud Functions dependencies locally by running:\n   `cd functions; npm install; cd -`\n\n## Deploy and test\n\n1.  Deploy your function using `firebase deploy --only functions`\n1.  Navigate to the\n    [Test Lab](https://console.firebase.google.com/u/0/project/_/testlab/histories)\n    section of the Firebase Console and start a test.\n1.  Once the test finishes running,\n    [view the functions logs](https://console.firebase.google.com/u/0/project/_/functions/logs?severity=DEBUG)\n    for your project, and check that the test run status was logged.\n\n## Next Steps\n\nTo see how to post to Slack instead of just `console.log`-ing, check out\n[this sample](https://github.com/firebase/functions-samples/tree/main/2nd-gen/testlab-to-slack).\n"
  },
  {
    "path": "Node/quickstarts/testlab-matrix-completed/firebase.json",
    "content": "{\n  \"functions\": {\n    \"codebase\": \"testlab-matrix-completed\"\n  }\n}\n"
  },
  {
    "path": "Node/quickstarts/testlab-matrix-completed/functions/.eslintrc.js",
    "content": "/**\n * Copyright 2023 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nmodule.exports = {\n  root: true,\n  env: {\n    es2020: true,\n    node: true,\n  },\n  extends: [\n    \"eslint:recommended\",\n    \"google\",\n  ],\n  rules: {\n    quotes: [\"error\", \"double\"],\n  },\n};\n"
  },
  {
    "path": "Node/quickstarts/testlab-matrix-completed/functions/index.js",
    "content": "/**\n * Copyright 2022 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\"use strict\";\n\n// [START all]\n// [START import]\n// The Cloud Functions for Firebase SDK to set up triggers and logging.\nconst {onTestMatrixCompleted} = require(\"firebase-functions/testLab\");\nconst {logger} = require(\"firebase-functions\");\n// [END import]\n\n// [START logtestcomplete]\nexports.logtestcomplete = onTestMatrixCompleted((event) => {\n  // Obtain Test Matrix properties from the CloudEvent\n  const {testMatrixId, createTime, state, outcomeSummary} = event.data;\n\n  // Log the properties of the completed Test Matrix\n  logger.log(\n      `TEST ${testMatrixId} (created at ${createTime}): ${state}. ${\n        outcomeSummary || \"\"\n      }`,\n  );\n});\n// [END logtestcomplete]\n// [END all]\n"
  },
  {
    "path": "Node/quickstarts/testlab-matrix-completed/functions/package.json",
    "content": "{\n  \"name\": \"functions\",\n  \"description\": \"Cloud Functions for Firebase\",\n  \"scripts\": {\n    \"lint\": \"eslint .\",\n    \"serve\": \"firebase emulators:start --only functions\",\n    \"shell\": \"firebase functions:shell\",\n    \"start\": \"npm run shell\",\n    \"deploy\": \"firebase deploy --only functions\",\n    \"logs\": \"firebase functions:log\",\n    \"compile\": \"cp ../../../../tsconfig.template.json ./tsconfig-compile.json && tsc --project tsconfig-compile.json\"\n  },\n  \"engines\": {\n    \"node\": \"22\"\n  },\n  \"dependencies\": {\n    \"firebase-admin\": \"^13.0.2\",\n    \"firebase-functions\": \"7.0.0\"\n  },\n  \"devDependencies\": {\n    \"eslint\": \"^8.57.1\",\n    \"eslint-config-google\": \"^0.14.0\",\n    \"firebase-functions-test\": \"^3.4.0\"\n  },\n  \"private\": true\n}\n"
  },
  {
    "path": "Node/quickstarts/thumbnails/README.md",
    "content": "# Firebase SDK for Cloud Functions Quickstart - Cloud Storage trigger\n\nThis quickstart demonstrates using **Firebase SDK for Cloud Functions** setup with a Cloud Storage trigger.\n\n## Introduction\n\nThis sample automatically generates thumbnails for images that are uploaded to Cloud Storage.\n\n\n## Functions Code\n\nSee file [functions/index.js](functions/index.js) for the thumbnail generation code.\n\nThe thumbnail generation is performed using [sharp](https://www.npmjs.com/package/sharp).\n\nThe dependencies are listed in [functions/package.json](functions/package.json).\n\n\n## Trigger rules\n\nThe function triggers on upload of any file to your Firebase project's default Cloud Storage bucket.\n\n\n## Deploy and test\n\nTo deploy and test the sample:\n\n 1. Create a Firebase project on the [Firebase Console](https://console.firebase.google.com) and visit the **Storage** tab.\n 1. Get the code, for instance using `git clone https://github.com/firebase/functions-samples`\n 1. Enter the correct directory `cd functions-samples/quickstarts/thumbnails`\n 1. Setup the CLI to use your Firebase project using `firebase use --add` and select your Firebase project\n 1. Deploy your project's code using `firebase deploy`\n 1. Go to the Firebase Console **Storage** tab and upload an image. After a short time a thumbnail image with the same name but a `thumb_` prefix will be created in the same folder (make sure you refresh the UI to see the new file).\n\n\n## Contributing\n\nWe'd love that you contribute to the project. Before doing so please read our [Contributor guide](../../CONTRIBUTING.md).\n\n\n## License\n\n© Google, 2022. Licensed under an [Apache-2](../../LICENSE) license.\n"
  },
  {
    "path": "Node/quickstarts/thumbnails/firebase.json",
    "content": "{\n  \"functions\": {\n    \"codebase\": \"thumbnails\",\n    \"predeploy\": [\n      \"npm --prefix \\\"$RESOURCE_DIR\\\" run lint\"\n    ]\n  }\n}\n"
  },
  {
    "path": "Node/quickstarts/thumbnails/functions/.eslintrc.js",
    "content": "/**\n * Copyright 2023 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nmodule.exports = {\n  root: true,\n  env: {\n    es2017: true,\n    node: true,\n  },\n  extends: [\n    \"eslint:recommended\",\n    \"google\",\n  ],\n  rules: {\n    quotes: [\"error\", \"double\"],\n  },\n};\n"
  },
  {
    "path": "Node/quickstarts/thumbnails/functions/index.js",
    "content": "/**\n * Copyright 2022 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\"use strict\";\n\n// [START v2storageImports]\n// [START v2storageSDKImport]\nconst {onObjectFinalized} = require(\"firebase-functions/storage\");\n// [END v2storageSDKImport]\n\n// [START v2storageAdditionalImports]\nconst {initializeApp} = require(\"firebase-admin/app\");\nconst {getStorage} = require(\"firebase-admin/storage\");\nconst logger = require(\"firebase-functions/logger\");\nconst path = require(\"path\");\n\n// library for image resizing\nconst sharp = require(\"sharp\");\n\ninitializeApp();\n// [END v2storageAdditionalImports]\n// [END v2storageImports]\n\n// [START v2storageGenerateThumbnail]\n/**\n * When an image is uploaded in the Storage bucket,\n * generate a thumbnail automatically using sharp.\n */\n// [START v2storageGenerateThumbnailTrigger]\nexports.generateThumbnail = onObjectFinalized({cpu: 2}, async (event) => {\n// [END v2storageGenerateThumbnailTrigger]\n\n  // [START v2storageEventAttributes]\n  const fileBucket = event.data.bucket; // Storage bucket containing the file.\n  const filePath = event.data.name; // File path in the bucket.\n  const contentType = event.data.contentType; // File content type.\n  // [END v2storageEventAttributes]\n\n  // [START v2storageStopConditions]\n  // Exit if this is triggered on a file that is not an image.\n  if (!contentType.startsWith(\"image/\")) {\n    return logger.log(\"This is not an image.\");\n  }\n  // Exit if the image is already a thumbnail.\n  const fileName = path.basename(filePath);\n  if (fileName.startsWith(\"thumb_\")) {\n    return logger.log(\"Already a Thumbnail.\");\n  }\n  // [END v2storageStopConditions]\n\n  // [START v2storageThumbnailGeneration]\n  // Download file into memory from bucket.\n  const bucket = getStorage().bucket(fileBucket);\n  const downloadResponse = await bucket.file(filePath).download();\n  const imageBuffer = downloadResponse[0];\n  logger.log(\"Image downloaded!\");\n\n  // Generate a thumbnail using sharp.\n  const thumbnailBuffer = await sharp(imageBuffer).resize({\n    width: 200,\n    height: 200,\n    withoutEnlargement: true,\n  }).toBuffer();\n  logger.log(\"Thumbnail created\");\n\n  // Prefix 'thumb_' to file name.\n  const thumbFileName = `thumb_${fileName}`;\n  const thumbFilePath = path.join(path.dirname(filePath), thumbFileName);\n\n  // Upload the thumbnail.\n  const metadata = {contentType: contentType};\n  await bucket.file(thumbFilePath).save(thumbnailBuffer, {\n    metadata: metadata,\n  });\n  return logger.log(\"Thumbnail uploaded!\");\n  // [END v2storageThumbnailGeneration]\n});\n// [END v2storageGenerateThumbnail]\n"
  },
  {
    "path": "Node/quickstarts/thumbnails/functions/package.json",
    "content": "{\n  \"name\": \"generate-thumbnail-functions-quickstart\",\n  \"description\": \"Generate Thumbnail Firebase Functions sample\",\n  \"dependencies\": {\n    \"firebase-admin\": \"^13.0.2\",\n    \"firebase-functions\": \"7.0.0\",\n    \"sharp\": \"^0.32.1\"\n  },\n  \"devDependencies\": {\n    \"eslint\": \"^8.57.1\",\n    \"eslint-config-google\": \"^0.14.0\"\n  },\n  \"scripts\": {\n    \"lint\": \"./node_modules/.bin/eslint --max-warnings=0 .\",\n    \"serve\": \"firebase emulators:start --only functions\",\n    \"shell\": \"firebase functions:shell\",\n    \"start\": \"npm run shell\",\n    \"deploy\": \"firebase deploy --only functions\",\n    \"logs\": \"firebase functions:log\",\n    \"compile\": \"cp ../../../../tsconfig.template.json ./tsconfig-compile.json && tsc --project tsconfig-compile.json\"\n  },\n  \"engines\": {\n    \"node\": \"22\"\n  },\n  \"private\": true\n}\n"
  },
  {
    "path": "Node/quickstarts/uppercase-firestore/.gitignore",
    "content": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\nfirebase-debug.log*\n\n# Firebase cache\n.firebase/\n\n# Firebase config\n\n# Uncomment this if you'd like others to create their own Firebase project.\n# For a team working on the same Firebase project(s), it is recommended to leave\n# it commented so all members can deploy to the same project(s) in .firebaserc.\n# .firebaserc\n\n# Runtime data\npids\n*.pid\n*.seed\n*.pid.lock\n\n# Directory for instrumented libs generated by jscoverage/JSCover\nlib-cov\n\n# Coverage directory used by tools like istanbul\ncoverage\n\n# nyc test coverage\n.nyc_output\n\n# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)\n.grunt\n\n# Bower dependency directory (https://bower.io/)\nbower_components\n\n# node-waf configuration\n.lock-wscript\n\n# Compiled binary addons (http://nodejs.org/api/addons.html)\nbuild/Release\n\n# Dependency directories\nnode_modules/\n\n# Optional npm cache directory\n.npm\n\n# Optional eslint cache\n.eslintcache\n\n# Optional REPL history\n.node_repl_history\n\n# Output of 'npm pack'\n*.tgz\n\n# Yarn Integrity file\n.yarn-integrity\n\n# dotenv environment variables file\n.env\n"
  },
  {
    "path": "Node/quickstarts/uppercase-firestore/README.md",
    "content": "# Firebase SDK for Cloud Functions Quickstart - Firestore\n\nThis quickstart demonstrates using the **Firebase SDK for Cloud Functions** with\n**Firestore**.\n\n## Introduction\n\nThis sample app does two things:\n\n- Creates messages in Firestore using a simple HTTPS request which is\n  handled by an HTTP function. Writing to Firestore is done using the\n  Firebase Admin SDK.\n- When a message gets added in Firestore, a function triggers and\n  automatically makes these messages all uppercase.\n\n## Set up the sample\n\nBefore you can test the functions locally or deploy to a Firebase project,\nyou'll need to run `npm install` in the `functions` directory.\n\n## Run locally with the Firebase Emulator suite\n\nThe\n[Firebase Local Emulator Suite](https://firebase.google.com/docs/emulator-suite)\nallows you to build and test apps on your local machine instead of deploying to\na Firebase project.\n\n1. Create a Firebase project in the\n   [Firebase Console](https://console.firebase.google.com)\n   > _Wondering why this step is needed?_ Even though the emulator will run this\n   > sample on your local machine, it needs to interact with a Firebase project\n   > to retrieve some configuration values.\n1. [Set up or update the Firebase CLI](https://firebase.google.com/docs/cli#setup_update_cli)\n1. Run `firebase emulators:start`\n1. Open the Emulator Suite UI\n   1. Look in the output of the `firebase emulators:start` command for the URL\n      of the Emulator Suite UI. It defaults to\n      [localhost:4000](http://localhost:4000), but may be hosted on a different\n      port on your machine.\n   1. Enter that URL in your browser to open the UI.\n1. Trigger the functions\n   1. Look in the output of the `firebase emulators:start` command for the URL\n      of the http function \"addmessage\". It will look similar to:\n      `http://localhost:5001/MY_PROJECT/us-central1/addmessage`\n      1. `MY_PROJECT` will be replaced with your project ID\n      1. The port may be different on your local machine\n   1. Add the query string `?text=uppercaseme` to the end of the function's URL.\n      It should now look something like:\n      `http://localhost:5001/MY_PROJECT/us-central1/addmessage?text=uppercaseme`\n      1. Optionally, you can change the message \"uppercaseme\" to a custom\n         message\n   1. Create a new message by opening the URL in a new tab in your browser\n1. View the effects of the functions in the Emulator Suite UI\n\n   1. In the \"Logs\" tab, you should see new logs indicating that the functions\n      \"addmessage\" and \"makeuppercase\" ran:\n\n      > `functions: Beginning execution of \"addmessage\"`\n\n      > `functions: Beginning execution of \"makeuppercase\"`\n\n   1. In the \"Firestore\" tab, you should see a document containing your original\n      message as well as the uppercased version of your message (if it was\n      originally \"uppercaseme\", you'll see \"UPPERCASEME\")\n\n## Deploy and test on a live Firebase project\n\nTo deploy and test the sample:\n\n1. Create a Firebase project on the\n   [Firebase Console](https://console.firebase.google.com)\n1. Deploy your project's code using `firebase deploy`\n1. Create a message by opening the function URL in your browser.\n\nYou should see your text value displayed in the console and uppercase.\n\n## Contributing\n\nWe'd love that you contribute to the project. Before doing so please read our\n[Contributor guide](../../CONTRIBUTING.md).\n\n## License\n\n© Google, 2023. Licensed under an [Apache-2](../../LICENSE) license.\n"
  },
  {
    "path": "Node/quickstarts/uppercase-firestore/firebase.json",
    "content": "{\n  \"functions\": {\n    \"codebase\": \"uppercase-firestore\"\n  },\n  \"firestore\": {\n    \"rules\": \"firestore.rules\",\n    \"indexes\": \"firestore.indexes.json\"\n  },\n  \"emulators\": {\n    \"functions\": {\n      \"port\": 5001\n    },\n    \"firestore\": {\n      \"port\": 8080\n    },\n    \"ui\": {\n      \"enabled\": true\n    }\n  }\n}\n"
  },
  {
    "path": "Node/quickstarts/uppercase-firestore/firestore.indexes.json",
    "content": "{\n  \"indexes\": [],\n  \"fieldOverrides\": []\n}\n"
  },
  {
    "path": "Node/quickstarts/uppercase-firestore/firestore.rules",
    "content": "service cloud.firestore {\n  match /databases/{database}/documents {\n    match /messages/{message} {\n      // Allow authenticated users to read/write the messages collection\n      allow read, write: if request.auth != null;\n    }\n  }\n}\n"
  },
  {
    "path": "Node/quickstarts/uppercase-firestore/functions/.eslintrc.js",
    "content": "/**\n * Copyright 2023 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nmodule.exports = {\n  root: true,\n  env: {\n    es2020: true,\n    node: true,\n  },\n  extends: [\n    \"eslint:recommended\",\n    \"google\",\n  ],\n  rules: {\n    quotes: [\"error\", \"double\"],\n  },\n};\n"
  },
  {
    "path": "Node/quickstarts/uppercase-firestore/functions/.gitignore",
    "content": "node_modules/"
  },
  {
    "path": "Node/quickstarts/uppercase-firestore/functions/index.js",
    "content": "/**\n * Copyright 2023 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the 'License');\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an 'AS IS' BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\"use strict\";\n\n// [START all]\n// [START import]\n// The Cloud Functions for Firebase SDK to create Cloud Functions and triggers.\nconst {logger} = require(\"firebase-functions\");\nconst {onRequest} = require(\"firebase-functions/https\");\nconst {onDocumentCreated} = require(\"firebase-functions/firestore\");\n\n// The Firebase Admin SDK to access Firestore.\nconst {initializeApp} = require(\"firebase-admin/app\");\nconst {getFirestore} = require(\"firebase-admin/firestore\");\n\ninitializeApp();\n// [END import]\n\n// [START addmessage]\n// Take the text parameter passed to this HTTP endpoint and insert it into\n// Firestore under the path /messages/:documentId/original\n// [START addmessageTrigger]\nexports.addmessage = onRequest(async (req, res) => {\n  // [END addmessageTrigger]\n  // Grab the text parameter.\n  const original = req.query.text;\n  // [START adminSdkAdd]\n  // Push the new message into Firestore using the Firebase Admin SDK.\n  const writeResult = await getFirestore()\n      .collection(\"messages\")\n      .add({original: original});\n  // Send back a message that we've successfully written the message\n  res.json({result: `Message with ID: ${writeResult.id} added.`});\n  // [END adminSdkAdd]\n});\n// [END addmessage]\n\n// [START makeuppercase]\n// Listens for new messages added to /messages/:documentId/original\n// and saves an uppercased version of the message\n// to /messages/:documentId/uppercase\n// [START makeuppercaseTrigger]\nexports.makeuppercase = onDocumentCreated(\"/messages/{documentId}\", (event) => {\n  // [END makeuppercaseTrigger]\n  // [START makeUppercaseBody]\n  // Grab the current value of what was written to Firestore.\n  const original = event.data.data().original;\n\n  // Access the parameter `{documentId}` with `event.params`\n  logger.log(\"Uppercasing\", event.params.documentId, original);\n\n  const uppercase = original.toUpperCase();\n\n  // You must return a Promise when performing\n  // asynchronous tasks inside a function\n  // such as writing to Firestore.\n  // Setting an 'uppercase' field in Firestore document returns a Promise.\n  return event.data.ref.set({uppercase}, {merge: true});\n  // [END makeUppercaseBody]\n});\n// [END makeuppercase]\n// [END all]\n"
  },
  {
    "path": "Node/quickstarts/uppercase-firestore/functions/package.json",
    "content": "{\n  \"name\": \"functions\",\n  \"description\": \"Cloud Functions for Firebase\",\n  \"dependencies\": {\n    \"firebase-admin\": \"^13.0.2\",\n    \"firebase-functions\": \"7.0.0\"\n  },\n  \"devDependencies\": {\n    \"chai\": \"^4.3.6\",\n    \"chai-as-promised\": \"^7.1.1\",\n    \"eslint\": \"^8.57.1\",\n    \"eslint-config-google\": \"^0.14.0\",\n    \"mocha\": \"^7.2.0\",\n    \"sinon\": \"^9.2.4\"\n  },\n  \"scripts\": {\n    \"lint\": \"./node_modules/.bin/eslint --max-warnings=0 .\",\n    \"serve\": \"firebase emulators:start --only functions\",\n    \"shell\": \"firebase functions:shell\",\n    \"start\": \"npm run shell\",\n    \"deploy\": \"firebase deploy --only functions\",\n    \"logs\": \"firebase functions:log\",\n    \"compile\": \"cp ../../../../tsconfig.template.json ./tsconfig-compile.json && tsc --project tsconfig-compile.json\"\n  },\n  \"engines\": {\n    \"node\": \"22\"\n  },\n  \"private\": true\n}\n"
  },
  {
    "path": "Node/quickstarts/uppercase-rtdb/README.md",
    "content": "# Firebase SDK for Cloud Functions 2nd Gen Quickstart - Realtime Database\n\nThis quickstart demonstrates using **Firebase SDK for Cloud Functions** with **Firebase Realtime Database**.\n\n\n## Introduction\n\nThis sample app does two things:\n - Create messages in the Firebase Realtime Database using a simple HTTPS request which is handled by a v2 HTTPS function. Writing to the Realtime Database is done using the Firebase Admin SDK. \n - When a message gets added in the Realtime Database, a v2 function triggers and automatically makes these messages all uppercase.\n\n## Deploy and try out\n\nTo deploy and try out the sample:\n\n - Create a Firebase project using the [Firebase console](https://console.firebase.google.com)\n - Install the required dependencies by running `npm install` in the `functions` directory\n - Deploy your project's code using `firebase deploy`\n - Create a message by opening the querying the functions URL from your browser.\n\n The function executes and redirects the browser to the Firebase console at the database location where the text string was stored. You should see your text value displayed in the console and in uppercase.\n\n## Contributing\n\nWe'd love you to contribute to the project. Before doing so please read our [Contributor guide](../../CONTRIBUTING.md).\n\n\n## License\n\n© Google, 2022. Licensed under an [Apache-2](../../LICENSE) license.\n"
  },
  {
    "path": "Node/quickstarts/uppercase-rtdb/database.rules.json",
    "content": "{\n  \"rules\": {\n    \".read\": true,\n    \".write\": true\n  }\n}\n"
  },
  {
    "path": "Node/quickstarts/uppercase-rtdb/firebase.json",
    "content": "{\n  \"functions\": {\n    \"codebase\": \"uppercase-rtdb\"\n  },\n  \"rulesFile\": \"database.rules.json\",\n  \"emulators\": {\n    \"functions\": {\n      \"port\": 5001\n    },\n    \"database\": {\n      \"port\": 9000\n    },\n    \"ui\": {\n      \"enabled\": true\n    }\n  }\n}\n"
  },
  {
    "path": "Node/quickstarts/uppercase-rtdb/functions/.eslintrc.js",
    "content": "/**\n * Copyright 2023 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nmodule.exports = {\n  root: true,\n  env: {\n    es2020: true,\n    node: true,\n  },\n  extends: [\n    \"eslint:recommended\",\n    \"google\",\n  ],\n  rules: {\n    quotes: [\"error\", \"double\"],\n  },\n};\n"
  },
  {
    "path": "Node/quickstarts/uppercase-rtdb/functions/index.js",
    "content": "/**\n * Copyright 2022 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\"use strict\";\n\n// [START all]\n// [START import]\n// The Cloud Functions for Firebase SDK to setup triggers and logging.\nconst {onRequest} = require(\"firebase-functions/https\");\nconst {onValueCreated} = require(\"firebase-functions/database\");\nconst {logger} = require(\"firebase-functions\");\n\n// The Firebase Admin SDK to access the Firebase Realtime Database.\nconst admin = require(\"firebase-admin\");\nadmin.initializeApp();\n// [END import]\n\n// [START addmessage]\n// Take the text parameter passed to this HTTP endpoint and insert it into the\n// Realtime Database under the path /messages/:pushId/original\n// [START addMessageTrigger]\nexports.addmessage = onRequest(async (req, resp) => {\n// [END addMessageTrigger]\n  // Grab the text parameter.\n  const original = req.query.text;\n  // [START adminSdkPush]\n  // Push the new message into the Realtime Database\n  // using the Firebase Admin SDK.\n  const snapshot = await admin\n      .database()\n      .ref(\"/messages\")\n      .push({original: original});\n\n  // Redirect with 303 SEE OTHER to the URL\n  // of the pushed object in the Firebase console.\n  resp.redirect(303, snapshot.ref.toString());\n  // [END adminSdkPush]\n});\n// [END addmessage]\n\n// [START makeuppercase]\n// Listens for new messages added to /messages/:pushId/original and creates an\n// uppercase version of the message to /messages/:pushId/uppercase\n// for all databases in 'us-central1'\nexports.makeuppercase = onValueCreated(\n    \"/messages/{pushId}/original\",\n    (event) => {\n    // Grab the current value of what was written to the Realtime Database.\n      const original = event.data.val();\n      logger.log(\"Uppercasing\", event.params.pushId, original);\n      const uppercase = original.toUpperCase();\n      // You must return a Promise when performing\n      // asynchronous tasks inside a function, such as\n      // writing to the Firebase Realtime Database.\n      // Setting an \"uppercase\" sibling in the\n      // Realtime Database returns a Promise.\n      return event.data.ref.parent.child(\"uppercase\").set(uppercase);\n    },\n);\n// [END makeuppercase]\n// [END all]\n"
  },
  {
    "path": "Node/quickstarts/uppercase-rtdb/functions/package.json",
    "content": "{\n  \"name\": \"functions\",\n  \"description\": \"Cloud Functions for Firebase\",\n  \"scripts\": {\n    \"lint\": \"eslint .\",\n    \"serve\": \"firebase emulators:start --only functions\",\n    \"shell\": \"firebase functions:shell\",\n    \"start\": \"npm run shell\",\n    \"deploy\": \"firebase deploy --only functions\",\n    \"logs\": \"firebase functions:log\"\n  },\n  \"engines\": {\n    \"node\": \"22\"\n  },\n  \"main\": \"index.js\",\n  \"dependencies\": {\n    \"firebase-admin\": \"^13.0.2\",\n    \"firebase-functions\": \"7.0.0\"\n  },\n  \"devDependencies\": {\n    \"eslint\": \"^8.57.1\",\n    \"eslint-config-google\": \"^0.14.0\",\n    \"firebase-functions-test\": \"^3.4.0\"\n  },\n  \"private\": true\n}\n"
  },
  {
    "path": "Node/remote-config-diff/README.md",
    "content": "## Get the difference between the previous and current Remote Config templates.\n\nUse the Firebase Remote Config Cloud Function trigger to take action when your template is updated.\n\nThis sample demonstrates how to use the Remote Config Cloud Function trigger to log the difference between the previous and current Remote Config templates.\n\n* See [eBay's real world implementation](https://github.com/eBay/firebase-remote-config-monitor).\n\n* See the [docs](https://firebase.google.com/docs/functions/rc-events) for more on Remote Config Triggers.\n"
  },
  {
    "path": "Node/remote-config-diff/firebase.json",
    "content": "{\n  \"functions\": {\n    \"codebase\": \"remote-config-diff\"\n  }\n}\n"
  },
  {
    "path": "Node/remote-config-diff/functions/.eslintrc.js",
    "content": "/**\n * Copyright 2023 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nmodule.exports = {\n  root: true,\n  env: {\n    es2020: true,\n    node: true,\n  },\n  extends: [\n    \"eslint:recommended\",\n    \"google\",\n  ],\n  rules: {\n    quotes: [\"error\", \"double\"],\n  },\n};\n"
  },
  {
    "path": "Node/remote-config-diff/functions/index.js",
    "content": "/**\n * Copyright 2022 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\"use strict\";\n\n// [START all]\n// [START import]\n// The Cloud Functions for Firebase SDK to set up triggers and logging.\nconst {onConfigUpdated} = require(\"firebase-functions/remoteConfig\");\nconst logger = require(\"firebase-functions/logger\");\n// The Firebase Admin SDK to obtain access tokens.\nconst admin = require(\"firebase-admin\");\nconst app = admin.initializeApp();\nconst jsonDiff = require(\"json-diff\");\n// [END import]\n\n// [START showconfigdiff]\nexports.showconfigdiff = onConfigUpdated(async (event) => {\n  try {\n    // Obtain the access token from the Admin SDK\n    const accessTokenObj = await admin.credential.applicationDefault()\n        .getAccessToken();\n    const accessToken = accessTokenObj.access_token;\n\n    // Get the version number from the event object\n    const remoteConfigApi = \"https://firebaseremoteconfig.googleapis.com/v1/\" +\n        `projects/${app.options.projectId}/remoteConfig`;\n    const currentVersion = event.data.versionNumber;\n    const prevVersion = currentVersion - 1;\n    const templatePromises = [];\n    templatePromises.push(fetch(\n        remoteConfigApi,\n        {\n          method: \"POST\",\n          body: new URLSearchParams([[\"versionNumber\", currentVersion + \"\"]]),\n          headers: {Authorization: \"Bearer \" + accessToken},\n        },\n    ));\n    templatePromises.push(fetch(\n        remoteConfigApi,\n        {\n          method: \"POST\",\n          body: new URLSearchParams([[\"versionNumber\", prevVersion + \"\"]]),\n          headers: {Authorization: \"Bearer \" + accessToken},\n        },\n    ));\n\n    // Get the templates\n    const responses = await Promise.all(templatePromises);\n    const results = responses.map((r) => r.json());\n    const currentTemplate = results[0];\n    const previousTemplate = results[1];\n    // Figure out the differences of the templates\n    const diff = jsonDiff.diffString(previousTemplate, currentTemplate);\n    // Log the difference\n    logger.log(diff);\n  } catch (error) {\n    logger.error(error);\n  }\n});\n// [END showconfigdiff]\n// [END all]\n"
  },
  {
    "path": "Node/remote-config-diff/functions/package.json",
    "content": "{\n  \"name\": \"remote-config-diff\",\n  \"description\": \"Get the difference between the previous and current Remote Config template\",\n  \"scripts\": {\n    \"lint\": \"eslint .\",\n    \"serve\": \"firebase emulators:start --only functions\",\n    \"shell\": \"firebase functions:shell\",\n    \"start\": \"npm run shell\",\n    \"deploy\": \"firebase deploy --only functions\",\n    \"logs\": \"firebase functions:log\",\n    \"compile\": \"cp ../../../tsconfig.template.json ./tsconfig-compile.json && tsc --project tsconfig-compile.json\"\n  },\n  \"dependencies\": {\n    \"firebase-admin\": \"^13.0.2\",\n    \"firebase-functions\": \"7.0.0\",\n    \"json-diff\": \"^0.5.5\"\n  },\n  \"devDependencies\": {\n    \"eslint\": \"^8.57.1\",\n    \"eslint-config-google\": \"^0.14.0\"\n  },\n  \"engines\": {\n    \"node\": \"22\"\n  },\n  \"private\": true\n}\n"
  },
  {
    "path": "Node/remote-config-server-with-vertex/README.md",
    "content": "# Use server-side Remote Config with Cloud Functions and Vertex AI\n\nThis Cloud Function (`generateWithVertex`) demonstrates how to generate text\nusing Google's Vertex AI Gemini API. It uses\nthe Firebase Admin SDK for Node.js and Remote Config to manage model parameters,\nsafety settings, and feature flags.\n\nTo protect your API endpoints, function is intended to run with a service\naccount with unauthenticated access blocked as described in\n[Use server-side Remote Config with Cloud Functions and Vertex\nAI](https://firebase.google.com/docs/remote-config/solution-server).\n\nFor a callable function with App Check enabled, try the\n[Call the Vertex AI Gemini API with Remote Config and App Check](../call-vertex-remote-config-server)\nsample function and client.\n\n## Set up the sample\n\nFollow the [User server-side Remote Config with Cloud Functions and Vertex AI\nguide](https://firebase.google.com/docs/remote-config/solution-server) to:\n\n* Set up your Firebase project.\n* Enable required APIs and SDKs.\n* Configure IAM permissions.\n* Test your function in the Firebase emulator.\n* Deploy your function.\n\nImportant:  Vertex AI and Cloud Functions require a billing account. Review\n[Vertex AI pricing](https://cloud.google.com/vertex-ai/pricing) and\n[Cloud Functions pricing](https://firebase.google.com/pricing) before running\nthis function. If you're new to Firebase and Google Cloud, check to see if\nyou're eligible for a\n[$300 credit](https://firebase.google.com/support/faq#pricing-free-trial) and\na Free Trial Cloud Billing account.\n\n## Next steps\n\nLearn more about Remote Config server implementations at\n[Use Remote Config in server\nenvironments](https://firebase.google.com/docs/remote-config/server).\n\nSupport\n-------\n\n- [Firebase Support](https://firebase.google.com/support/)\n\nLicense\n-------\n\n© Google, 2024. Licensed under an [Apache-2](../../LICENSE) license.\n\n"
  },
  {
    "path": "Node/remote-config-server-with-vertex/firebase.json",
    "content": "{\n  \"functions\": {\n    \"codebase\": \"remote-config-server-with-vertex\"\n  }\n}\n"
  },
  {
    "path": "Node/remote-config-server-with-vertex/functions/index.js",
    "content": "// [START remote_config_server_vertex_init]\nconst { onRequest } = require(\"firebase-functions/https\");\nconst logger = require(\"firebase-functions/logger\");\n\nconst { initializeApp } = require(\"firebase-admin/app\");\nconst { VertexAI } = require('@google-cloud/vertexai');\nconst { getRemoteConfig } = require(\"firebase-admin/remote-config\");\n\n// Set and check environment variables.\nconst project = process.env.GCLOUD_PROJECT;\n\n// Initialize Firebase.\nconst app = initializeApp();\n// [END remote_config_server_vertex_init]\n\n// [START remote_config_server_vertex_default_values]\n// Define default (fallback) parameter values for Remote Config.\nconst defaultConfig = {\n\n  // Default values for Vertex AI.\n  model_name: \"gemini-1.5-flash-002\",\n  generation_config: [{\n    \"stopSequences\": [], \"temperature\": 0.7,\n    \"maxOutputTokens\": 64, \"topP\": 0.1, \"topK\": 20\n  }],\n  prompt: \"I'm a developer who wants to learn about Firebase and you are a \\\n    helpful assistant who knows everything there is to know about Firebase!\",\n  safety_settings: [{\n    \"category\":\n      \"HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT\",\n    \"threshold\": \"HarmBlockThreshold.BLOCK_MEDIUM_AND_ABOVE\"\n  }],\n  location: 'us-central1',\n\n  // Disable Vertex AI Gemini API access for testing.\n  vertex_enabled: false\n};\n// [END remote_config_server_vertex_default_values]\n\n// [START remote_config_server_vertex_create_function]\n// Export the function.\nexports.generateWithVertex = onRequest(async (request, response) => {\n\n  try {\n\n    // Set up Remote Config.\n    const rc = getRemoteConfig(app);\n\n    // Get the Remote Config template and assign default values.\n    const template = await rc.getServerTemplate({\n      defaultConfig: defaultConfig\n    });\n\n    // Add the template evaluation to a constant.\n    const config = template.evaluate();\n\n    // Obtain values from Remote Config.\n    const textModel = config.getString(\"model_name\") ||\n        defaultConfig.model_name;\n    const textPrompt = config.getString(\"prompt\") || defaultConfig.prompt;\n    const generationConfig = config.getString(\"generation_config\") ||\n        defaultConfig.generation_config;\n    const safetySettings = config.getString(\"safety_settings\") ||\n        defaultConfig.safety_settings;\n    const location = config.getString(\"location\") ||\n        defaultConfig.location;\n    const vertexEnabled = config.getBoolean(\"is_vertex_enabled\") ||\n        defaultConfig.vertex_enabled;\n// [END remote_config_server_vertex_create_function]\n\n// [START remote_config_server_vertex_function_logic]\n  // Allow user input.\n  const userInput = request.query.prompt || '';\n\n  // Instantiate Vertex AI.\n    const vertex_ai = new VertexAI({ project: project, location: location });\n    const generativeModel = vertex_ai.getGenerativeModel({\n      model: textModel,\n      safety_settings: safetySettings,\n      generation_config: generationConfig,\n    });\n\n    // Combine prompt from Remote Config with optional user input.\n    const chatInput = textPrompt + \" \" + userInput;\n\n    if (!chatInput) {\n      return res.status(400).send('Missing text prompt');\n    }\n    // If vertexEnabled isn't true, do not send queries to Vertex AI.\n    if (vertexEnabled !== true) {\n      response.status(200).send({\n        message: \"Vertex AI call skipped. Vertex is not enabled.\"\n      });\n      return;\n    }\n\n    logger.log(\"\\nRunning with model \", textModel, \", prompt: \", textPrompt,\n      \", generationConfig: \", generationConfig, \", safetySettings: \",\n      safetySettings, \" in \", location, \"\\n\");\n\n    const result = await generativeModel.generateContentStream(chatInput); \n    response.writeHead(200, { 'Content-Type': 'text/plain' });\n\n    for await (const item of result.stream) {\n      const chunk = item.candidates[0].content.parts[0].text;\n      logger.log(\"Received chunk:\", chunk);\n      response.write(chunk);\n    }\n\n    response.end();\n\n  } catch (error) {\n    logger.error(error);\n    response.status(500).send('Internal server error');\n  }\n});\n// [END remote_config_server_vertex_function_logic]\n\n"
  },
  {
    "path": "Node/remote-config-server-with-vertex/functions/package.json",
    "content": "{\n  \"name\": \"remote-config-server-with-vertex\",\n  \"description\": \"Cloud Functions for Firebase\",\n  \"scripts\": {\n    \"serve\": \"firebase emulators:start --only functions\",\n    \"shell\": \"firebase functions:shell\",\n    \"start\": \"npm run shell\",\n    \"deploy\": \"firebase deploy --only functions\",\n    \"logs\": \"firebase functions:log\"\n  },\n  \"engines\": {\n    \"node\": \"22\"\n  },\n  \"main\": \"index.js\",\n  \"dependencies\": {\n    \"@google-cloud/vertexai\": \"^1.1.0\",\n    \"eslint\": \"8\",\n    \"firebase-admin\": \"^13.0.2\",\n    \"firebase-functions\": \"7.0.0\"\n  },\n  \"devDependencies\": {\n    \"eslint-config-google\": \"^0.14.0\",\n    \"firebase-functions-test\": \"^3.4.0\"\n  },\n  \"private\": true\n}\n"
  },
  {
    "path": "Node/taskqueues-backup-images/README.md",
    "content": "# Back up images using a Task Queue Function (v2)\nThis quickstart demonstrates how to setup a Task Queue Function using the Firebase SDK for Cloud Functions (v2).\n\n## Introduction\n\nTask queue functions make it easy to manage the execution, dispatch, and delivery of a large number of distributed tasks.\n\nWe leverage its power here to set up a service that backs up all images from NASA's [Astronomy Picture of the Day](https://apod.nasa.gov/apod/astropix.html).\n\nTask queue functions are powered by [Google Cloud Tasks](https://cloud.google.com/tasks). Learn more about [task queue functions](https://firebase.google.com/docs/functions/beta/task-functions).\n\n## Functions\nThe sample code consists of 2 functions:\n\n### 1. `backupapod`\nA task queue function responsible for processing the logic for backing up the Astronomy Picture of the Day (\"apod\") for the given date. This function will be triggered for every task enqueued on the corresponding queue created in Cloud Tasks.\n\nTask queue functions come with a powerful set of configuration to precisely control rate limits and retry behavior of a task queue. [See the documentation](https://cloud.google.com/tasks/docs/creating-queues) to learn more about configuring task queue functions.\n\nOur sample make use of following configurations:\n\n1) `retryConfig.maxAttempts=5` - Each task in our task queue will be automatically retried upto 5 times. This helps us mitigate transient errors like network errors or temporary service disruption of a dependent, external service.\n2) `retryConfig.minBackoffSeconds=60` - Each task will be retried at least 60 seconds apart from each attempt. This gives us a large buffer between each attempt so we don't rush to exhaust our 5 retry attempts too quickly.\n3) `rateLimits.maxConcurrentDispatch` - At most 6 tasks will be dispatched at a given time. At most 6 tasks will be dispatched at a given time. This helps ensure a steady stream of requests to the underlying function and helps reduce the number of active instances and cold starts.\n\nYou can further configure this function with following [environment variables](https://firebase.google.com/docs/functions/config-env):\n\n* `BACKUP_BUCKET`: Name of the bucket to back up \"apod\" images. Defaults to default Cloud Storage bucket.\n\n### 2. `enqueuebackuptasks`\nAn HTTP function responsible for enqueuing tasks to our task queue. The function uses the Firebase Admin SDK to create and enqueue a task for each day we want to backup an \"apod\" image.\n\nNote that we explicitly query for and specify the `uri` of the task queue function when enqueueing task.\n\nWe are required to do this because v2 task queue functions doesn't come with a deterministic url.\n\nYou can configure this function with following [environment variables](https://firebase.google.com/docs/functions/config-env):\n\n* `BACKUP_COUNT`: Number of days to back up Astronomy Picture of the Day, starting from 1995-06-17 (the first day of publication). Defaults to 100.\n\n* `HOURLY_BATCH_SIZE`: Number of tasks to enqueue at each hour. Note that NASA API imposes a limit of 1000 reqs/hour. Defaults to 500.\n\n## Setup and Deploy\n\n### NASA Open API Key\nThe sample uses [NASA Open APIs](https://api.nasa.gov/) to retrieve Astronomy Picture of the Day  images. You need to register for an account to get your API Key and hook it up the task queue function by [creating a secret](https://firebase.google.com/docs/functions/config-env#secret-manager):\n\n```bash\n$ firebase functions:secrets:set NASA_API_KEY\n? Enter a value for NASA_API_KEY [input is hidden]\n✔  Created a new secret version projects/XXX/secrets/NASA_API_KEY/versions/1\n```\n\n### Deploy\nDeploy functions using Firebase CLI:\n\n```bash\n$ firebase deploy\n```\n\n## IAM Policy\nYou may see `PERMISSION DENIED` errors when enqueueing tasks or when Cloud Task tries to invoke your task queue functions. Ensure that your project has following IAM bindings:\n\n* Identity used to enqueue tasks to Cloud Tasks needs `cloudtasks.tasks.create` IAM permission\n  * In our sample, this is the [Compute Engine default service account](https://cloud.google.com/compute/docs/access/service-accounts).\n\n```\ngcloud projects add-iam-policy-binding $PROJECT_ID \\\n  --member=serviceAccount:${PROJECT_NUMBER}-compute@developer.gserviceaccount.com \\\n  --role=roles/cloudtasks.enqueuer\n```\n\n* Identity used to enqueue tasks to Cloud Task needs permission to use the service account associated with a task in Cloud Tasks.\n  * In our sample, this is the [Compute Engine default service account](https://cloud.google.com/compute/docs/access/service-accounts).\n\n```\nPlease follow Google Cloud IAM documentation to add App Engine default service account as user of App Engine default service account.\n```\n\n* Identity used to trigger the Task Queue function needs `cloudfunctions.functions.invoke` permission.\n  * In our sample, this is the [Compute Engine default service account](https://cloud.google.com/compute/docs/access/service-accounts).\n\n```\ngcloud functions add-iam-policy-binding backupapod \\\n  --region=us-central1 \\\n  --member=serviceAccount:${PROJECT_NUMBER}-compute@developer.gserviceaccount.com \\\n  --role=roles/cloudfunctions.invoker\n```"
  },
  {
    "path": "Node/taskqueues-backup-images/firebase.json",
    "content": "{\n  \"functions\": {\n    \"codebase\": \"taskqueues-backup-images\"\n  }\n}\n"
  },
  {
    "path": "Node/taskqueues-backup-images/functions/.eslintrc.js",
    "content": "/**\n * Copyright 2023 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nmodule.exports = {\n  root: true,\n  env: {\n    es2022: true,\n    node: true,\n  },\n  extends: [\n    \"eslint:recommended\",\n    \"google\",\n  ],\n  rules: {\n    quotes: [\"error\", \"double\"],\n  },\n};\n"
  },
  {
    "path": "Node/taskqueues-backup-images/functions/index.js",
    "content": "/**\n * Copyright 2022 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for t`he specific language governing permissions and\n * limitations under the License.\n */\n\"use strict\";\n// [START imports]\n// Dependencies for task queue functions.\nconst {onTaskDispatched} = require(\"firebase-functions/tasks\");\nconst {onRequest, HttpsError} = require(\"firebase-functions/https\");\nconst {getFunctions} = require(\"firebase-admin/functions\");\nconst {logger} = require(\"firebase-functions\");\n\n// Dependencies for image backup.\nconst path = require(\"path\");\nconst {initializeApp} = require(\"firebase-admin/app\");\nconst {getStorage} = require(\"firebase-admin/storage\");\nconst {GoogleAuth} = require(\"google-auth-library\");\n// [END imports]\ninitializeApp();\n\nconst BACKUP_START_DATE = new Date(\"1995-06-17\");\nconst BACKUP_COUNT = parseInt(process.env.BACKUP_COUNT) || 100;\nconst HOURLY_BATCH_SIZE = parseInt(process.env.HOURLY_BATCH_SIZE) || 500;\nconst BACKUP_BUCKET = process.env.BACKUP_BUCKET;\n\n/**\n * Grabs Astronomy Photo of the Day (APOD) using NASA's API.\n *\n */\n// [START v2TaskFunctionSetup]\nexports.backupapod = onTaskDispatched(\n    {\n      retryConfig: {\n        maxAttempts: 5,\n        minBackoffSeconds: 60,\n      },\n      rateLimits: {\n        maxConcurrentDispatches: 6,\n      },\n    }, async (req) => {\n      // [END v2TaskFunctionSetup]\n      const date = req.data.date;\n      if (!date) {\n        logger.warn(\"Invalid payload. Must include date.\");\n        throw new HttpsError(\n            \"invalid-argument\",\n            \"Invalid payload. Must include date.\",\n        );\n      }\n      logger.info(`Requesting data from apod api for date ${date}`);\n      let url = \"https://api.nasa.gov/planetary/apod\";\n      url += `?date=${date}`;\n      url += `&api_key=${process.env.NASA_API_KEY}`;\n      const apiResp = await fetch(url);\n      if (!apiResp.ok) {\n        logger.warn(\n            `request to NASA APOD API failed with reponse ${apiResp.status}`,\n        );\n        if (apiResp.status === 404) {\n          // APOD not published for the day. This is fine!\n          return;\n        }\n        if (apiResp.status >= 500) {\n          throw new HttpsError(\n              \"unavailable\",\n              \"APOD API temporarily not available.\",\n          );\n        }\n        throw new HttpsError(\"internal\", \"Uh-oh. Something broke.\");\n      }\n      const apod = await apiResp.json();\n      const picUrl = apod.hdurl;\n      logger.info(`Fetched ${picUrl} from NASA API for date ${date}.`);\n\n      const picResp = await fetch(picUrl);\n      const imageBuffer = await picResp.arrayBuffer();\n      const buffer = Buffer.from(imageBuffer);\n      const dest = getStorage()\n          .bucket(BACKUP_BUCKET)\n          .file(`apod/${date}${path.extname(picUrl)}`);\n      try {\n        await dest.save(buffer);\n      } catch (err) {\n        logger.error(`Failed to upload ${picUrl} to ${dest.name}`, err);\n        throw new HttpsError(\"internal\", \"Uh-oh. Something broke.\");\n      }\n    });\n\n\nlet auth;\n\n// [START v2GetFunctionUri]\n/**\n * Get the URL of a given v2 cloud function.\n *\n * @param {string} name the function's name\n * @param {string} location the function's location\n * @return {Promise<string>} The URL of the function\n */\nasync function getFunctionUrl(name, location=\"us-central1\") {\n  if (!auth) {\n    auth = new GoogleAuth({\n      scopes: \"https://www.googleapis.com/auth/cloud-platform\",\n    });\n  }\n  const projectId = await auth.getProjectId();\n  const url = \"https://cloudfunctions.googleapis.com/v2beta/\" +\n    `projects/${projectId}/locations/${location}/functions/${name}`;\n\n  const client = await auth.getClient();\n  const res = await client.request({url});\n  const uri = res.data?.serviceConfig?.uri;\n  if (!uri) {\n    throw new Error(`Unable to retreive uri for function at ${url}`);\n  }\n  return uri;\n}\n// [END v2GetFunctionUri]\n\n// [START v2EnqueueTasks]\nexports.enqueuebackuptasks = onRequest(\n    async (_request, response) => {\n      const queue = getFunctions().taskQueue(\"backupapod\");\n      const targetUri = await getFunctionUrl(\"backupapod\");\n\n      const enqueues = [];\n      for (let i = 0; i <= BACKUP_COUNT; i += 1) {\n        const iteration = Math.floor(i / HOURLY_BATCH_SIZE);\n        // Delay each batch by N * hour\n        const scheduleDelaySeconds = iteration * (60 * 60);\n\n        const backupDate = new Date(BACKUP_START_DATE);\n        backupDate.setDate(BACKUP_START_DATE.getDate() + i);\n        // Extract just the date portion (YYYY-MM-DD) as string.\n        const date = backupDate.toISOString().substring(0, 10);\n        enqueues.push(\n            queue.enqueue({date}, {\n              scheduleDelaySeconds,\n              dispatchDeadlineSeconds: 60 * 5, // 5 minutes\n              uri: targetUri,\n            }),\n        );\n      }\n      await Promise.all(enqueues);\n      response.sendStatus(200);\n    });\n// [END v2EnqueueTasks]\n"
  },
  {
    "path": "Node/taskqueues-backup-images/functions/package.json",
    "content": "{\n  \"name\": \"functions-taskqueues-backup-images\",\n  \"description\": \"Back up images from NASA's Astronomy Picture of the Day using Cloud Tasks.\",\n  \"scripts\": {\n    \"lint\": \"eslint .\",\n    \"lint:fix\": \"eslint . --fix\",\n    \"serve\": \"firebase emulators:start --only functions\",\n    \"shell\": \"firebase functions:shell\",\n    \"start\": \"npm run shell\",\n    \"deploy\": \"firebase deploy --only functions\",\n    \"logs\": \"firebase functions:log\",\n    \"compile\": \"cp ../../../tsconfig.template.json ./tsconfig-compile.json && tsc --project tsconfig-compile.json\"\n  },\n  \"engines\": {\n    \"node\": \"22\"\n  },\n  \"main\": \"index.js\",\n  \"dependencies\": {\n    \"firebase-admin\": \"^13.0.2\",\n    \"firebase-functions\": \"7.0.0\",\n    \"google-auth-library\": \"^8.6.0\"\n  },\n  \"devDependencies\": {\n    \"eslint\": \"^8.57.1\",\n    \"eslint-config-google\": \"^0.14.0\"\n  },\n  \"private\": true\n}\n"
  },
  {
    "path": "Node/test-functions-jest/.gitignore",
    "content": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\nfirebase-debug.log*\nfirebase-debug.*.log*\n\n# Firebase cache\n.firebase/\n\n# Firebase config\n\n# Uncomment this if you'd like others to create their own Firebase project.\n# For a team working on the same Firebase project(s), it is recommended to leave\n# it commented so all members can deploy to the same project(s) in .firebaserc.\n# .firebaserc\n\n# Runtime data\npids\n*.pid\n*.seed\n*.pid.lock\n\n# Directory for instrumented libs generated by jscoverage/JSCover\nlib-cov\n\n# Coverage directory used by tools like istanbul\ncoverage\n\n# nyc test coverage\n.nyc_output\n\n# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)\n.grunt\n\n# Bower dependency directory (https://bower.io/)\nbower_components\n\n# node-waf configuration\n.lock-wscript\n\n# Compiled binary addons (http://nodejs.org/api/addons.html)\nbuild/Release\n\n# Dependency directories\nnode_modules/\n\n# Optional npm cache directory\n.npm\n\n# Optional eslint cache\n.eslintcache\n\n# Optional REPL history\n.node_repl_history\n\n# Output of 'npm pack'\n*.tgz\n\n# Yarn Integrity file\n.yarn-integrity\n\n# dotenv environment variables file\n.env\n"
  },
  {
    "path": "Node/test-functions-jest/README.md",
    "content": "# Firebase Functions Test (with jest) - Quickstart\n\nThis quickstart demonstrates how to run unit tests on gen-2 functions.\n\n## Setting up the sample\n\n1. Clone or download this repo and open the `Node/test-functions-jest`\n   directory.\n1. Install Cloud Functions dependencies locally by running:\n   `cd functions; npm install; cd -`\n\n## Test\n\n1. Run your unit test with `npm test`.\n\n## Next Steps\n\nWrite your own functions using a testing-framework that best fits your use case.\n"
  },
  {
    "path": "Node/test-functions-jest/firebase.json",
    "content": "{\n  \"functions\": {\n    \"codebase\": \"test-functions-jest\",\n    \"predeploy\": [\n      \"npm --prefix \\\"$RESOURCE_DIR\\\" run lint\"\n    ],\n    \"source\": \"functions\"\n  }\n}\n"
  },
  {
    "path": "Node/test-functions-jest/functions/.eslintrc.js",
    "content": "/**\n * Copyright 2023 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nmodule.exports = {\n  root: true,\n  env: {\n    es2017: true,\n    node: true,\n    jest: true,\n  },\n  extends: [\n    \"eslint:recommended\",\n    \"google\",\n  ],\n  rules: {\n    quotes: [\"error\", \"double\"],\n  },\n};\n"
  },
  {
    "path": "Node/test-functions-jest/functions/.gitignore",
    "content": "node_modules/"
  },
  {
    "path": "Node/test-functions-jest/functions/index.js",
    "content": "/**\n * Copyright 2023 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nconst {logger} = require(\"firebase-functions\");\nconst {onObjectFinalized} = require(\"firebase-functions/storage\");\n\nexports.logstore = onObjectFinalized(\"my-bucket\", (cloudEvent) => {\n  logger.log(cloudEvent);\n});\n"
  },
  {
    "path": "Node/test-functions-jest/functions/index.test.js",
    "content": "/**\n * Copyright 2023 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nconst {logger} = require(\"firebase-functions\");\nconst test = require(\"firebase-functions-test\");\nconst {logstore} = require(\"./index\");\n\nconst {wrap} = test();\n\ndescribe(\"firebase-functions-test\", () => {\n  describe(\"#logstore\", () => {\n    it(\"will log when the v2 cloud function is invoked\", () => {\n      const logSpy = jest.spyOn(logger, \"log\");\n\n      const wrappedFunction = wrap(logstore);\n      wrappedFunction();\n      expect(logSpy).toHaveBeenCalled();\n    });\n  });\n});\n"
  },
  {
    "path": "Node/test-functions-jest/functions/package.json",
    "content": "{\n  \"name\": \"test-functions\",\n  \"scripts\": {\n    \"lint\": \"eslint --ext .js,.ts .\",\n    \"build\": \"tsc\",\n    \"serve\": \"npm run build && firebase emulators:start --only functions\",\n    \"shell\": \"npm run build && firebase functions:shell\",\n    \"start\": \"npm run shell\",\n    \"test\": \"jest\",\n    \"deploy\": \"firebase deploy --only functions\",\n    \"logs\": \"firebase functions:log\",\n    \"compile\": \"cp ../../../tsconfig.template.json ./tsconfig-compile.json && tsc --project tsconfig-compile.json\"\n  },\n  \"engines\": {\n    \"node\": \"22\"\n  },\n  \"main\": \"index.js\",\n  \"dependencies\": {\n    \"firebase-admin\": \"^13.0.2\",\n    \"firebase-functions\": \"7.0.0\"\n  },\n  \"devDependencies\": {\n    \"@types/jest\": \"^27.5.2\",\n    \"eslint\": \"^8.57.1\",\n    \"eslint-config-google\": \"^0.14.0\",\n    \"firebase-functions-test\": \"^3.4.0\",\n    \"jest\": \"^28.1.3\"\n  },\n  \"private\": true\n}\n"
  },
  {
    "path": "Node/test-functions-jest-ts/.gitignore",
    "content": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\nfirebase-debug.log*\nfirebase-debug.*.log*\n\n# Firebase cache\n.firebase/\n\n# Firebase config\n\n# Uncomment this if you'd like others to create their own Firebase project.\n# For a team working on the same Firebase project(s), it is recommended to leave\n# it commented so all members can deploy to the same project(s) in .firebaserc.\n# .firebaserc\n\n# Runtime data\npids\n*.pid\n*.seed\n*.pid.lock\n\n# Directory for instrumented libs generated by jscoverage/JSCover\nlib-cov\n\n# Coverage directory used by tools like istanbul\ncoverage\n\n# nyc test coverage\n.nyc_output\n\n# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)\n.grunt\n\n# Bower dependency directory (https://bower.io/)\nbower_components\n\n# node-waf configuration\n.lock-wscript\n\n# Compiled binary addons (http://nodejs.org/api/addons.html)\nbuild/Release\n\n# Dependency directories\nnode_modules/\n\n# Optional npm cache directory\n.npm\n\n# Optional eslint cache\n.eslintcache\n\n# Optional REPL history\n.node_repl_history\n\n# Output of 'npm pack'\n*.tgz\n\n# Yarn Integrity file\n.yarn-integrity\n\n# dotenv environment variables file\n.env\n"
  },
  {
    "path": "Node/test-functions-jest-ts/README.md",
    "content": "# Firebase Functions Test (with jest-ts) - Quickstart\n\nThis quickstart demonstrates how to run unit tests on gen-2 functions.\n\n## Setting up the sample\n\n1. Clone or download this repo and open the `2nd-gen/test-functions-jest-ts`\n   directory.\n1. Install Cloud Functions dependencies locally by running:\n   `cd functions; npm install; cd -`\n\n## Test\n\n1. Run your unit test with `npm test`.\n\n## Next Steps\n\nWrite your own functions using a testing-framework that best fits your use case.\n"
  },
  {
    "path": "Node/test-functions-jest-ts/firebase.json",
    "content": "{\n  \"functions\": {\n    \"codebase\": \"test-functions-jest-ts\",\n    \"predeploy\": [\n      \"npm --prefix \\\"$RESOURCE_DIR\\\" run lint\",\n      \"npm --prefix \\\"$RESOURCE_DIR\\\" run build\"\n    ]\n  }\n}\n"
  },
  {
    "path": "Node/test-functions-jest-ts/functions/.eslintrc.js",
    "content": "/**\n * Copyright 2023 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nmodule.exports = {\n  root: true,\n  env: {\n    es2020: true,\n    node: true,\n  },\n  extends: [\n    \"eslint:recommended\",\n    \"plugin:import/errors\",\n    \"plugin:import/warnings\",\n    \"plugin:import/typescript\",\n    \"google\",\n    \"plugin:@typescript-eslint/recommended\",\n  ],\n  parser: \"@typescript-eslint/parser\",\n  parserOptions: {\n    project: [\"tsconfig.json\", \"tsconfig.dev.json\"],\n    sourceType: \"module\",\n  },\n  ignorePatterns: [\n    \"jest.config.js\",\n    \"/lib/**/*\", // Ignore built files.\n  ],\n  plugins: [\n    \"@typescript-eslint\",\n    \"import\",\n  ],\n  rules: {\n    \"quotes\": [\"error\", \"double\"],\n    \"import/no-unresolved\": 0,\n  },\n};\n"
  },
  {
    "path": "Node/test-functions-jest-ts/functions/.gitignore",
    "content": "# Compiled JavaScript files\nlib/**/*.js\nlib/**/*.js.map\n\n# TypeScript v1 declaration files\ntypings/\n\n# Node.js dependency directory\nnode_modules/\n"
  },
  {
    "path": "Node/test-functions-jest-ts/functions/jest.config.js",
    "content": "/**\n * Copyright 2023 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */\nmodule.exports = {\n  preset: 'ts-jest',\n  testEnvironment: 'node',\n};"
  },
  {
    "path": "Node/test-functions-jest-ts/functions/package.json",
    "content": "{\n  \"name\": \"test-functions\",\n  \"scripts\": {\n    \"lint\": \"eslint --ext .js,.ts .\",\n    \"build\": \"tsc\",\n    \"serve\": \"npm run build && firebase emulators:start --only functions\",\n    \"shell\": \"npm run build && firebase functions:shell\",\n    \"start\": \"npm run shell\",\n    \"test\": \"jest\",\n    \"deploy\": \"firebase deploy --only functions\",\n    \"logs\": \"firebase functions:log\",\n    \"compile\": \"cp ../../../tsconfig.template.json ./tsconfig-compile.json && tsc --project tsconfig-compile.json\"\n  },\n  \"engines\": {\n    \"node\": \"22\"\n  },\n  \"main\": \"lib/index.js\",\n  \"dependencies\": {\n    \"firebase-admin\": \"^13.0.2\",\n    \"firebase-functions\": \"7.0.0\"\n  },\n  \"devDependencies\": {\n    \"@jest/globals\": \"^28.1.3\",\n    \"@types/jest\": \"^27.5.2\",\n    \"@typescript-eslint/eslint-plugin\": \"^5.58.0\",\n    \"@typescript-eslint/parser\": \"^5.58.0\",\n    \"eslint\": \"^8.57.1\",\n    \"eslint-config-google\": \"^0.14.0\",\n    \"eslint-plugin-import\": \"^2.26.0\",\n    \"firebase-functions\": \"^4.3.1\",\n    \"firebase-functions-test\": \"^3.4.0\",\n    \"jest\": \"^28.1.3\",\n    \"ts-jest\": \"^28.0.8\",\n    \"typescript\": \"^5.0.4\"\n  },\n  \"private\": true\n}\n"
  },
  {
    "path": "Node/test-functions-jest-ts/functions/src/__test__/index.test.ts",
    "content": "/**\n * Copyright 2023 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {logger} from \"firebase-functions\";\nimport {expect, jest, test} from \"@jest/globals\";\nimport firebaseFunctionsTest from \"firebase-functions-test\";\nimport {logstore} from \"../index\";\n\nconst {wrap} = firebaseFunctionsTest();\n\ntest(\"logstore\", () => {\n  const mockLog = jest.spyOn(logger, \"log\");\n  const wrappedLogStore = wrap(logstore);\n\n  /**\n   * Invoke the function once using default {@link CloudEvent}.\n   */\n  wrappedLogStore();\n  expect(mockLog).toBeCalledTimes(1);\n\n  /**\n   * Invoke the function once using {@link Partial<CloudEvent>}.\n   */\n  const cloudEventPartial = {data: {bucket: \"my-other-bucket\"}};\n  wrappedLogStore(cloudEventPartial);\n  expect(mockLog).toBeCalledTimes(2);\n});\n"
  },
  {
    "path": "Node/test-functions-jest-ts/functions/src/index.ts",
    "content": "/**\n * Copyright 2023 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {logger} from \"firebase-functions\";\nimport {onObjectFinalized} from \"firebase-functions/storage\";\n\nexport const logstore = onObjectFinalized(\"my-bucket\", (cloudEvent) => {\n  logger.log(cloudEvent);\n});\n"
  },
  {
    "path": "Node/test-functions-jest-ts/functions/tsconfig.dev.json",
    "content": "{\n  \"include\": [\n    \".eslintrc.js\"\n  ]\n}\n"
  },
  {
    "path": "Node/test-functions-jest-ts/functions/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"module\": \"commonjs\",\n    \"esModuleInterop\": true,\n    \"noImplicitReturns\": true,\n    \"noUnusedLocals\": true,\n    \"outDir\": \"lib\",\n    \"sourceMap\": true,\n    \"strict\": true,\n    \"target\": \"es2017\"\n  },\n  \"compileOnSave\": true,\n  \"include\": [\n    \"src\"\n  ]\n}\n"
  },
  {
    "path": "Node/test-functions-mocha/.gitignore",
    "content": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\nfirebase-debug.log*\nfirebase-debug.*.log*\n\n# Firebase cache\n.firebase/\n\n# Firebase config\n\n# Uncomment this if you'd like others to create their own Firebase project.\n# For a team working on the same Firebase project(s), it is recommended to leave\n# it commented so all members can deploy to the same project(s) in .firebaserc.\n# .firebaserc\n\n# Runtime data\npids\n*.pid\n*.seed\n*.pid.lock\n\n# Directory for instrumented libs generated by jscoverage/JSCover\nlib-cov\n\n# Coverage directory used by tools like istanbul\ncoverage\n\n# nyc test coverage\n.nyc_output\n\n# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)\n.grunt\n\n# Bower dependency directory (https://bower.io/)\nbower_components\n\n# node-waf configuration\n.lock-wscript\n\n# Compiled binary addons (http://nodejs.org/api/addons.html)\nbuild/Release\n\n# Dependency directories\nnode_modules/\n\n# Optional npm cache directory\n.npm\n\n# Optional eslint cache\n.eslintcache\n\n# Optional REPL history\n.node_repl_history\n\n# Output of 'npm pack'\n*.tgz\n\n# Yarn Integrity file\n.yarn-integrity\n\n# dotenv environment variables file\n.env\n"
  },
  {
    "path": "Node/test-functions-mocha/README.md",
    "content": "# Firebase Functions Test (with mocha) - Quickstart\n\nThis quickstart demonstrates how to run unit tests on Cloud Functions (2nd gen).\n\n## Setting up the sample\n\n1. Clone or download this repo and open the `2nd-gen/test-functions-mocha`\n   directory.\n1. Install Cloud Functions dependencies locally by running:\n   `cd functions; npm install; cd -`\n\n## Test\n\n1. Run your unit test with `npm test`.\n\n## Next Steps\n\nWrite your own functions using a testing-framework that best fits your use case.\n"
  },
  {
    "path": "Node/test-functions-mocha/firebase.json",
    "content": "{\n  \"functions\": {\n    \"codebase\": \"test-functions-mocha\",\n    \"predeploy\": [\n      \"npm --prefix \\\"$RESOURCE_DIR\\\" run lint\"\n    ],\n    \"source\": \"functions\"\n  }\n}\n"
  },
  {
    "path": "Node/test-functions-mocha/functions/.eslintrc.js",
    "content": "/**\n * Copyright 2023 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nmodule.exports = {\n  root: true,\n  env: {\n    es2017: true,\n    node: true,\n    jest: true,\n  },\n  extends: [\n    \"eslint:recommended\",\n    \"google\",\n  ],\n  rules: {\n    quotes: [\"error\", \"double\"],\n  },\n};\n"
  },
  {
    "path": "Node/test-functions-mocha/functions/.gitignore",
    "content": "node_modules/"
  },
  {
    "path": "Node/test-functions-mocha/functions/index.js",
    "content": "/**\n * Copyright 2023 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nconst {logger} = require(\"firebase-functions\");\nconst {onObjectFinalized} = require(\"firebase-functions/storage\");\n\nexports.logstore = onObjectFinalized(\"my-bucket\", (cloudEvent) => {\n  logger.log(cloudEvent);\n});\n"
  },
  {
    "path": "Node/test-functions-mocha/functions/index.test.js",
    "content": "/**\n * Copyright 2023 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nconst {logger} = require(\"firebase-functions\");\nconst test = require(\"firebase-functions-test\");\nconst {assert} = require(\"chai\");\nconst {spy} = require(\"sinon\");\nconst {logstore} = require(\"./index\");\n\nconst {wrap} = test();\n\ndescribe(\"firebase-functions-test\", () => {\n  describe(\"#logstore\", () => {\n    it(\"will log when the v2 cloud function is invoked\", () => {\n      const logSpy = spy(logger, \"log\");\n\n      const wrappedFunction = wrap(logstore);\n      wrappedFunction();\n      assert.isTrue(logSpy.calledOnce);\n    });\n  });\n});\n"
  },
  {
    "path": "Node/test-functions-mocha/functions/package.json",
    "content": "{\n  \"name\": \"functions\",\n  \"scripts\": {\n    \"lint\": \"eslint --ext .js,.ts .\",\n    \"build\": \"\",\n    \"serve\": \"npm run build && firebase emulators:start --only functions\",\n    \"shell\": \"npm run build && firebase functions:shell\",\n    \"start\": \"npm run shell\",\n    \"test\": \"mocha *.test.js\",\n    \"deploy\": \"firebase deploy --only functions\",\n    \"logs\": \"firebase functions:log\",\n    \"compile\": \"cp ../../../tsconfig.template.json ./tsconfig-compile.json && tsc --project tsconfig-compile.json\"\n  },\n  \"engines\": {\n    \"node\": \"22\"\n  },\n  \"main\": \"index.js\",\n  \"dependencies\": {\n    \"firebase-admin\": \"^13.0.2\",\n    \"firebase-functions\": \"7.0.0\"\n  },\n  \"devDependencies\": {\n    \"@types/mocha\": \"^9.1.1\",\n    \"chai\": \"^4.3.6\",\n    \"eslint\": \"^8.57.1\",\n    \"eslint-config-google\": \"^0.14.0\",\n    \"eslint-plugin-import\": \"^2.26.0\",\n    \"firebase-functions-test\": \"^3.4.0\",\n    \"mocha\": \"^10.1.0\",\n    \"sinon\": \"^13.0.2\"\n  },\n  \"private\": true\n}\n"
  },
  {
    "path": "Node/testlab-to-slack/README.md",
    "content": "# Post Test Lab Results to Slack channel\n\nThis sample demonstrates how to post to a Slack channel in response to the\ncompletion of a Test Matrix in Firebase Test Lab. The message will look like\nthis:\n\n![example Slack message with Test Lab status](https://i.imgur.com/9DTL19x.png)\n\n## Setting up the sample\n\n1.  [Add an **Incoming Webhook**](https://my.slack.com/services/new/incoming-webhook/) to your Slack channel and take note of the **Webhook URL**.\n1.  Clone or download this repo and open this directory in a terminal:\n\n    ```shell\n    cd 2nd-gen/testlab-to-slack\n    ```\n\n1.  You must have the latest Firebase CLI installed. If you don't have it,\n    install it with `npm install -g firebase-tools` and then sign in with\n    `firebase login`.\n1.  Configure the CLI locally by using `firebase use --add` and select your\n    project in the list.\n1.  Install Cloud Functions dependencies locally by running:\n    `cd functions; npm install; cd -`\n1.  Set the following environment variables so that the function can\n    authenticate with Slack and post to the correct room:\n\n    ```bash\n    firebase functions:secrets:set SLACK_WEBHOOK_URL\n    ```\n    \n    Enter the value of your Slack url to save it into Secret Manager.\n\n## Deploy and test\n\nThis sample comes with a web-based UI for testing the function. To test it out:\n\n1.  Deploy your function using `firebase deploy --only functions`\n1.  Navigate to the\n    [Test Lab](https://console.firebase.google.com/u/0/project/_/testlab/histories)\n    section of the Firebase Console and start a test.\n1.  Once the test finishes running, check your Slack channel and view the new\n    post!\n"
  },
  {
    "path": "Node/testlab-to-slack/firebase.json",
    "content": "{\n  \"functions\": {\n    \"codebase\": \"testlab-to-slack\"\n  }\n}\n"
  },
  {
    "path": "Node/testlab-to-slack/functions/.eslintrc.js",
    "content": "/**\n * Copyright 2023 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nmodule.exports = {\n  root: true,\n  env: {\n    es2020: true,\n    node: true,\n  },\n  extends: [\n    \"eslint:recommended\",\n    \"google\",\n  ],\n  rules: {\n    quotes: [\"error\", \"double\"],\n  },\n};\n"
  },
  {
    "path": "Node/testlab-to-slack/functions/index.js",
    "content": "/**\n * Copyright 2022 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\"use strict\";\n\n// [START all]\n// [START import]\n// The Cloud Functions for Firebase SDK to set up triggers and logging.\nconst {onTestMatrixCompleted} = require(\"firebase-functions/testLab\");\nconst {logger} = require(\"firebase-functions\");\n// [END import]\n\n// [START postToSlack]\n/**\n * Posts a message to Slack via a Webhook\n * @param {string} title\n * @param {string} details\n * @return {Promise<string>}\n */\nasync function postToSlack(title, details) {\n  const response = await fetch(process.env.SLACK_WEBHOOK_URL, {\n    method: \"post\",\n    body: JSON.stringify({\n      blocks: [\n        {\n          type: \"section\",\n          text: {\n            type: \"mrkdwn\",\n            text: title,\n          },\n        },\n        {\n          type: \"divider\",\n        },\n        {\n          type: \"section\",\n          text: {\n            type: \"mrkdwn\",\n            text: details,\n          },\n        },\n      ],\n    }),\n    headers: {\"Content-Type\": \"application/json\"},\n  });\n  return response.json();\n}\n// [END postToSlack]\n\n// [START getSlackmoji]\n/**\n * Convert a test result status into a Slackmoji\n * @param {string} term\n * @return {string}\n */\nfunction getSlackmoji(term) {\n  switch (term) {\n    case \"SUCCESS\":\n      return \":tada:\";\n    case \"FAILURE\":\n      return \":broken_heart:\";\n    case \"INCONCLUSIVE\":\n      return \":question:\";\n    case \"SKIPPED\":\n      return \":arrow_heading_down:\";\n    case \"VALIDATING\":\n      return \":thought_balloon:\";\n    case \"PENDING\":\n      return \":soon:\";\n    case \"FINISHED\":\n      return \":white_check_mark:\";\n    case \"ERROR\":\n      return \":red_circle:\";\n    case \"INVALID\":\n      return \":large_orange_diamond:\";\n    default:\n      return \"\";\n  }\n}\n// [END getSlackmoji]\n\n// [START posttestresultstoslack]\nexports.posttestresultstoslack = onTestMatrixCompleted(\n    {secrets: [\"SLACK_WEBHOOK_URL\"]},\n    async (event) => {\n    // Obtain Test Matrix properties from the CloudEvent\n      const {testMatrixId, state, outcomeSummary} = event.data;\n\n      // Create the title of the message\n      const title = `${getSlackmoji(state)} ${getSlackmoji(\n          outcomeSummary,\n      )} ${testMatrixId}`;\n\n      // Create the details of the message\n      const details = `Status: *${state}* ${getSlackmoji(\n          state,\n      )}\\nOutcome: *${outcomeSummary}* ${getSlackmoji(outcomeSummary)}\n    `;\n\n      // Post the message to slack\n      const slackResponse = await postToSlack(title, details);\n\n      // Log the response\n      logger.log(slackResponse);\n    });\n// [END posttestresultstoslack]\n// [END all]\n"
  },
  {
    "path": "Node/testlab-to-slack/functions/package.json",
    "content": "{\n  \"name\": \"functions\",\n  \"description\": \"Cloud Functions for Firebase\",\n  \"scripts\": {\n    \"lint\": \"eslint .\",\n    \"serve\": \"firebase emulators:start --only functions\",\n    \"shell\": \"firebase functions:shell\",\n    \"start\": \"npm run shell\",\n    \"deploy\": \"firebase deploy --only functions\",\n    \"logs\": \"firebase functions:log\",\n    \"compile\": \"cp ../../../tsconfig.template.json ./tsconfig-compile.json && tsc --project tsconfig-compile.json\"\n  },\n  \"engines\": {\n    \"node\": \"22\"\n  },\n  \"dependencies\": {\n    \"firebase-admin\": \"^13.0.2\",\n    \"firebase-functions\": \"7.0.0\"\n  },\n  \"devDependencies\": {\n    \"eslint\": \"^8.57.1\",\n    \"eslint-config-google\": \"^0.14.0\",\n    \"firebase-functions-test\": \"^3.4.0\"\n  },\n  \"private\": true\n}\n"
  },
  {
    "path": "Node/youtube/README.md",
    "content": "# YouTube: Get information about a YouTube channel (2nd Gen)\n\nThis quickstart demonstrates how to query the\n[YouTube Data API](https://developers.google.com/youtube/v3) using **Cloud\nFunctions for Firebase (2nd Gen)** with an HTTPS trigger.\n\n## Introduction\n\nThe function `getChannelInfo` returns information about a Youtube channel. By\ndefault it will return information about the\n[Firebase YouTube channel](https://www.youtube.com/user/Firebase), but you can pass it a\n`channelId` URL Query parameter to query any channel you'd like.\n\n## Setup\n\n### Get a YouTube API Key\n\n1. Create a Firebase Project on the\n   [Firebase Console](https://console.firebase.google.com) if you don't already have a project you want to use.\n   1. Upgrade your Firebase project to the\n      [Blaze \"pay as you go\" plan](https://firebase.google.com/pricing)\n1. Enable the Youtube API by visiting the\n   [API console](http://console.cloud.google.com/marketplace/product/google/youtube.googleapis.com),\n   selecting your Firebase project, and clicking \"ENABLE\".\n   1. Once the API is enabled, visit the\n      [credentials tab](http.console.cloud.google.com/apis/api/youtube.googleapis.com/credentials)\n      and click \"CREATE CREDENTIALS\" to create a YouTube API key.\n\n### Clone and configure the function\n\n1. Install the Firebase CLI and log in:\n   ```\n   npm install --global firebase-tools\n\n   firebase login\n   ```\n1. Clone or download this repo and open the `Node/youtube` directory.\n1. `cd` into the `functions` directory and install dependencies with `pnpm install`\n1. Set up your Firebase project by running `firebase use --add` with the\n   Firebase CLI, select your Project ID and follow the instructions.\n1. Set the YouTube API key as a secret:\n    ```bash\n    firebase functions:secrets:set YOUTUBE_API_KEY\n    ```\n   When prompted, enter the API key you created.\n\n### Run your function locally with the Firebase Emulator Suite\n\n1. Start the emulator:\n    ```bash\n    firebase emulators:start --only functions\n    ```\n1. Check the emulator output to find the URL of the `getChannelInfo` function. It will looks something like `http://localhost:5001/my-project-id/us-central1/getChannelInfo`\n1. Via CURL or in your browser, visit the URL that the function is running at. Optionally, add a query string `?channelId=SOME_CHANNEL_ID` to the end of the URL.\n1. You should get a JSON response with information about the YouTube channel!\n\n\n## Deploy the app to prod\n\nDeploy to Firebase using the following command:\n\n```bash\nfirebase deploy\n```\n\nThis deploys and activates the `getChannelInfo` function.\n\n> The first time you call `firebase deploy` on a new project with Functions will take longer than usual.\n\n## Modify it to your needs\n\nNow that you've got this sample working, modify it to work for your use case! Some ideas:\n\n- Check out the other things you can query with the [YouTube Data API](https://developers.google.com/youtube/v3/docs)\n- Convert `getChannelInfo` function to a scheduled function, and write the new latest videos for a channel into Firestore or Realtime Database\n- ...anything else you can think of!\n"
  },
  {
    "path": "Node/youtube/firebase.json",
    "content": "{\n  \"functions\": {\n    \"source\": \"functions\",\n    \"predeploy\": [\n      \"pnpm --prefix \\\"$RESOURCE_DIR\\\" run compile\"\n    ]\n  }\n}\n"
  },
  {
    "path": "Node/youtube/functions/index.ts",
    "content": "/**\n * Copyright 2024 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { onCall, CallableRequest } from \"firebase-functions/https\";\nimport { defineSecret } from \"firebase-functions/params\";\nimport { google } from \"googleapis\";\n\nconst youtubeApiKey = defineSecret(\"YOUTUBE_API_KEY\");\n\nconst FIREBASE_YOUTUBE_CHANNEL_ID = \"UCP4bf6IHJJQehibu6ai__cg\";\n\nexport const getChannelInfo = onCall({ secrets: [youtubeApiKey] }, async (request: CallableRequest) => {\n  const youtube = google.youtube({\n    version: \"v3\",\n    auth: youtubeApiKey.value(),\n  });\n\n  const channelId = request.data.channelId || FIREBASE_YOUTUBE_CHANNEL_ID;\n\n  // Fetch channel information\n  // https://developers.google.com/youtube/v3/docs/channels/list\n  const { data: channelData } = await youtube.channels.list({\n    part: [\"snippet\", \"statistics\"],\n    id: [channelId],\n    maxResults: 1,\n  });\n\n  if (!channelData.items || channelData.items.length !== 1) {\n    return { error: `Channel with ID ${channelId} not found.` };\n  }\n\n  const channel = channelData.items[0];\n\n  // Fetch the channel's latest videos\n  // https://developers.google.com/youtube/v3/docs/search/list\n  const { data: videoData } = await youtube.search.list({\n    part: [\"id\", \"snippet\"],\n    order: \"date\",\n    channelId,\n    maxResults: 3,\n  });\n\n  const videos = videoData.items || [];\n\n  return {\n    id: channelId,\n    channelTitle: channel.snippet!.title,\n    channelDescription: channel.snippet!.description,\n    subscriberCount: channel.statistics!.subscriberCount,\n    recentVideos: videos.map((video: any) => {\n      return {\n        videoTitle: video.snippet!.title,\n        videoUrl: `https://www.youtube.com/watch?v=${video.id!.videoId}`,\n        videoDescription: video.snippet!.description,\n      };\n    }),\n  };\n});\n"
  },
  {
    "path": "Node/youtube/functions/package.json",
    "content": "{\n  \"name\": \"youtube-2nd-gen\",\n  \"description\": \"Cloud Functions for Firebase 2nd Gen YouTube Sample\",\n  \"main\": \"lib/index.js\",\n  \"type\": \"module\",\n  \"engines\": {\n    \"node\": \"22\"\n  },\n  \"scripts\": {\n    \"serve\": \"firebase emulators:start --only functions\",\n    \"compile\": \"tsc\",\n    \"deploy\": \"firebase deploy --only functions\",\n    \"logs\": \"firebase functions:log\"\n  },\n  \"dependencies\": {\n    \"firebase-admin\": \"^13.0.2\",\n    \"firebase-functions\": \"7.0.0\",\n    \"googleapis\": \"^133.0.0\"\n  },\n  \"devDependencies\": {\n    \"@typescript-eslint/eslint-plugin\": \"^7.8.0\",\n    \"@typescript-eslint/parser\": \"^7.8.0\",\n    \"eslint\": \"^8.57.0\",\n    \"eslint-config-google\": \"^0.14.0\",\n    \"typescript\": \"^5.4.5\"\n  },\n  \"private\": true\n}\n"
  },
  {
    "path": "Node/youtube/functions/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"moduleResolution\": \"node\",\n    \"noEmit\": true,\n    \"sourceMap\": true,\n    \"strict\": true,\n    \"esModuleInterop\": true,\n    \"skipLibCheck\": true\n  },\n  \"include\": [\n    \"index.ts\"\n  ],\n  \"exclude\": [\n    \"node_modules\"\n  ]\n}\n"
  },
  {
    "path": "Node-1st-gen/README.md",
    "content": "# Node.js (1st gen) samples\n\nThis folder contains examples for 1st gen functions. Note that 2nd gen functions are available and the recommended platform for Cloud Functions for Firebase.\n\nSee a description of all samples in this folder in the [root README](../README.md)."
  },
  {
    "path": "Node-1st-gen/assistant-say-number/README.md",
    "content": "# Google Assistant action that reads the ordinal of a number.\n\nThis sample shows how to create an action for the Google Home/Assistant using the Actions SDK hosted on Cloud Functions. The sample action asks users to say a number and reads out the ordinal of that number.\n\ne.g. If the user says \"Twelve\" the action will say \"The ordinal of twelve is twelfth\".\n\nFurther reading:\n - Actions SDK: https://developers.google.com/actions/develop/sdk/getting-started#getting-started.\n - Firebase SDK: https://firebase.google.com/docs/functions\n\n## Functions Code\n\nSee file [functions/index.js](functions/index.js) for the code.\n\nHandling the Google Actions requests is done using the [Google Actions SDK](https://www.npmjs.com/package/actions-on-google).\n\nThe dependencies are listed in [functions/package.json](functions/package.json).\n\n\n## Deploy and test\n\nTo test this sample action:\n\n - Create a Firebase Project using the [Firebase Developer Console](https://console.firebase.google.com)\n - Configure this sample to use your project using `firebase use --add` and select your project.\n - Deploy your project using `firebase deploy`\n - In the `action.json` file, update the two `<YOUR_PROJECT_ID>` placeholders with your Firebase project ID. The URL should match the `Function URL (sayNumber):` that was printed out by `firebase deploy`.\n - [Download](https://developers.google.com/actions/tools/gactions-cli) the `gaction` CLI\n - Make your action available for testing using the `gactions preview action.json`\n - Test your Action on the [Google Home Web Simulator](https://g.co/actionswebsim) by saying \"Talk to My Action\"\n"
  },
  {
    "path": "Node-1st-gen/assistant-say-number/action.json",
    "content": "{\n  \"versionLabel\": \"1.0.0\",\n  \"agentInfo\": {\n    \"languageCode\": \"en-US\",\n    \"projectId\": \"<YOUR_PROJECT_ID>\",\n    \"voiceName\": \"male_1\"\n  },\n  \"actions\": [\n    {\n      \"initialTrigger\": {\n        \"intent\": \"assistant.intent.action.MAIN\"\n      },\n      \"httpExecution\": {\n        \"url\": \"https://us-central1-<YOUR_PROJECT_ID>.cloudfunctions.net/sayNumber\"\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "Node-1st-gen/assistant-say-number/firebase.json",
    "content": "{\n    \"functions\": {\n        \"codebase\": \"assistant-say-number\"\n    }\n}\n"
  },
  {
    "path": "Node-1st-gen/assistant-say-number/functions/.eslintrc.json",
    "content": "{\n  \"parserOptions\": {\n    // Required for certain syntax usages\n    \"ecmaVersion\": 2022\n  },\n  \"plugins\": [\n    \"promise\"\n  ],\n  \"extends\": \"eslint:recommended\",\n  \"rules\": {\n    // Removed rule \"disallow the use of console\" from recommended eslint rules\n    \"no-console\": \"off\",\n\n    // Removed rule \"disallow multiple spaces in regular expressions\" from recommended eslint rules\n    \"no-regex-spaces\": \"off\",\n\n    // Removed rule \"disallow the use of debugger\" from recommended eslint rules\n    \"no-debugger\": \"off\",\n\n    // Removed rule \"disallow unused variables\" from recommended eslint rules\n    \"no-unused-vars\": \"off\",\n\n    // Removed rule \"disallow mixed spaces and tabs for indentation\" from recommended eslint rules\n    \"no-mixed-spaces-and-tabs\": \"off\",\n\n    // Removed rule \"disallow the use of undeclared variables unless mentioned in /*global */ comments\" from recommended eslint rules\n    \"no-undef\": \"off\",\n\n    // Warn against template literal placeholder syntax in regular strings\n    \"no-template-curly-in-string\": 1,\n\n    // Warn if return statements do not either always or never specify values\n    \"consistent-return\": 1,\n\n    // Warn if no return statements in callbacks of array methods\n    \"array-callback-return\": 1,\n\n    // Requre the use of === and !==\n    \"eqeqeq\": 2,\n\n    // Disallow the use of alert, confirm, and prompt\n    \"no-alert\": 2,\n\n    // Disallow the use of arguments.caller or arguments.callee\n    \"no-caller\": 2,\n\n    // Disallow null comparisons without type-checking operators\n    \"no-eq-null\": 2,\n\n    // Disallow the use of eval()\n    \"no-eval\": 2,\n\n    // Warn against extending native types\n    \"no-extend-native\": 1,\n\n    // Warn against unnecessary calls to .bind()\n    \"no-extra-bind\": 1,\n\n    // Warn against unnecessary labels    \n    \"no-extra-label\": 1,\n\n    // Disallow leading or trailing decimal points in numeric literals\n    \"no-floating-decimal\": 2,\n\n    // Warn against shorthand type conversions\n    \"no-implicit-coercion\": 1,\n\n    // Warn against function declarations and expressions inside loop statements\n    \"no-loop-func\": 1,\n\n    // Disallow new operators with the Function object\n    \"no-new-func\": 2,\n\n    // Warn against new operators with the String, Number, and Boolean objects\n    \"no-new-wrappers\": 1,\n\n    // Disallow throwing literals as exceptions\n    \"no-throw-literal\": 2,\n\n    // Require using Error objects as Promise rejection reasons\n    \"prefer-promise-reject-errors\": 2,\n\n    // Enforce “for” loop update clause moving the counter in the right direction\n    \"for-direction\": 2,\n\n    // Enforce return statements in getters\n    \"getter-return\": 2,\n\n    // Disallow await inside of loops\n    \"no-await-in-loop\": 2,\n\n    // Disallow comparing against -0\n    \"no-compare-neg-zero\": 2,\n\n    // Warn against catch clause parameters from shadowing variables in the outer scope\n    \"no-catch-shadow\": 1,\n\n    // Disallow identifiers from shadowing restricted names\n    \"no-shadow-restricted-names\": 2,\n\n    // Enforce return statements in callbacks of array methods\n    \"callback-return\": 2,\n\n    // Require error handling in callbacks\n    \"handle-callback-err\": 2,\n\n    // Warn against string concatenation with __dirname and __filename\n    \"no-path-concat\": 1,\n\n    // Prefer using arrow functions for callbacks\n    \"prefer-arrow-callback\": 1,\n\n    // Return inside each then() to create readable and reusable Promise chains.\n    \"promise/always-return\": 2,\n\n    //Enforces the use of catch() on un-returned promises\n    \"promise/catch-or-return\": 2,\n\n    // Warn against nested then() or catch() statements\n    \"promise/no-nesting\": 1\n  }\n}\n"
  },
  {
    "path": "Node-1st-gen/assistant-say-number/functions/index.js",
    "content": "/**\n * Copyright 2016 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use strict';\n\nconst functions = require('firebase-functions/v1');\nconst { actionssdk } = require('actions-on-google');\n\nconst app = actionssdk();\napp.intent('actions.intent.MAIN', (conv) => {\n    conv.ask(`<speak>\n      Hi! <break time=\"1\"/>\n      I can read out an ordinal number like <say-as interpret-as=\"ordinal\">123</say-as>.\n      Say a number.\n    </speak>`);\n});\n\napp.intent('actions.intent.TEXT', (conv) => {\n  const rawInput = conv.input.raw;\n  if (rawInput === 'bye') {\n    conv.ask('Goodbye!');\n  } else if (isNaN(parseInt(rawInput, 10))) {\n    conv.ask('I didn\\'t quite get that, what was the number?');\n  } else {\n    conv.ask(`<speak>\n      The ordinal of <say-as interpret-as=\"cardinal\">${rawInput}</say-as> is\n      <say-as interpret-as=\"ordinal\">${rawInput}</say-as>\n    </speak>`);\n  }\n});\n\n/**\n * Endpoint which handles requests for a Google Assistant action which asks users to say a number\n * and read out the ordinal of that number.\n * e.g. If the user says \"Twelve\" the action will say \"The ordinal of twelve is twelfth\".\n */\nexports.sayNumber = functions.https.onRequest(app);\n"
  },
  {
    "path": "Node-1st-gen/assistant-say-number/functions/package.json",
    "content": "{\n  \"name\": \"assistant-say-numer-functions\",\n  \"description\": \"Firebase Functions\",\n  \"dependencies\": {\n    \"actions-on-google\": \"^2.14.0\",\n    \"firebase-admin\": \"^13.0.2\",\n    \"firebase-functions\": \"7.0.0\"\n  },\n  \"devDependencies\": {\n    \"eslint\": \"^8.57.1\",\n    \"eslint-plugin-promise\": \"^7.2.1\"\n  },\n  \"scripts\": {\n    \"lint\": \"./node_modules/.bin/eslint --max-warnings=0 .\",\n    \"serve\": \"firebase emulators:start --only functions\",\n    \"shell\": \"firebase functions:shell\",\n    \"start\": \"npm run shell\",\n    \"deploy\": \"firebase deploy --only functions\",\n    \"logs\": \"firebase functions:log\",\n    \"compile\": \"cp ../../../tsconfig.template.json ./tsconfig-compile.json && tsc --project tsconfig-compile.json\"\n  },\n  \"engines\": {\n    \"node\": \"20\"\n  },\n  \"private\": true\n}\n"
  },
  {
    "path": "Node-1st-gen/authenticated-json-api/README.md",
    "content": "# Authenticated JSON API\n\nThis sample shows how to authenticate access to a JSON API to only allow\naccess to data for a specific Firebase user.\n\nOnly users who pass a valid Firebase ID token as a Bearer token in the\n`Authorization` header of the HTTP request are authorized to use the API.\n\nThis sample comes with a web-based API explorer UI whose code is in the [public](public) directory.\nIt lets you sign in to Firebase with your Google account, and create messages whose sentiments are\ndetected by the [Cloud Natural Language API](https://cloud.google.com/natural-language/).\n\n## Setting up the sample\n\n 1. Create a Firebase Project using the [Firebase Console](https://console.firebase.google.com).\n 1. Enable the **Google** Provider in the **Auth** section.\n 1. Enable Billing on your project (to connect to the Natural Language API) by switching to the Blaze plan.\n 1. Clone or download this repo and open the `authenticated-json-api` directory.\n 1. You must have the Firebase CLI installed. If you don't have it install it with `npm install -g firebase-tools` and then configure it with `firebase login`.\n 1. Configure the CLI locally by using `firebase use --add` and select your project in the list.\n 1. Install dependencies locally by running: `cd functions; npm install; cd -`\n 1. [Enable the Google Cloud Natural Language API](https://console.cloud.google.com/apis/api/language.googleapis.com/overview?project=_)\n\n## Deploy and test\n\nThis sample comes with a web-based UI for testing the function.\nTo test locally do:\n\n  1. [Set up admin credentials](https://firebase.google.com/docs/functions/local-emulator#set_up_admin_credentials_optional) so that the emulator can talk to the Natural Language API\n  1. Start serving your project locally using `firebase serve --only hosting,functions`\n  1. Open the app in a browser at `https://localhost:5000`.\n  1. Sign in to the web app in the browser using Google Sign-In\n  1. Create messages and explore them using the List and Detail sections.\n  1. Sign out. You should no longer be able to access the API.\n\nTo deploy and test on prod do:\n\n 1. Deploy your project using `firebase deploy`\n 1. Open the app using `firebase open hosting:site`, this will open a browser.\n 1. Sign in to the web app in the browser using Google Sign-In\n 1. Create messages and explore them using the List and Detail sections.\n 1. Sign out. You should no longer be able to access the API.\n\n## Contributing\n\nWe'd love that you contribute to the project. Before doing so please read our [Contributor guide](../CONTRIBUTING.md).\n\n## License\n\n© Google, 2017. Licensed under an [Apache-2](../LICENSE) license.\n"
  },
  {
    "path": "Node-1st-gen/authenticated-json-api/database.rules.json",
    "content": "{\n  \"rules\": {\n    \"users\": {\n      \"$uid\": {\n        \".read\": \"auth.uid === $uid\",\n        \".write\": \"auth.uid === $uid\",\n        \"messages\": {\n          \".indexOn\": [\"category\"]\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "Node-1st-gen/authenticated-json-api/firebase.json",
    "content": "{\n  \"functions\": {\n    \"codebase\": \"authenticated-json-api\"\n  },\n  \"hosting\": {\n    \"public\": \"public\",\n    \"rewrites\": [\n      { \"source\": \"/api/**\", \"function\": \"api\" }\n    ]\n  },\n  \"database\": {\n    \"rules\": \"database.rules.json\"\n  }\n}\n"
  },
  {
    "path": "Node-1st-gen/authenticated-json-api/functions/.eslintrc.json",
    "content": "{\n  \"parserOptions\": {\n    // Required for certain syntax usages\n    \"ecmaVersion\": 2022\n  },\n  \"plugins\": [\n    \"promise\"\n  ],\n  \"extends\": \"eslint:recommended\",\n  \"rules\": {\n    // Removed rule \"disallow the use of console\" from recommended eslint rules\n    \"no-console\": \"off\",\n\n    // Removed rule \"disallow multiple spaces in regular expressions\" from recommended eslint rules\n    \"no-regex-spaces\": \"off\",\n\n    // Removed rule \"disallow the use of debugger\" from recommended eslint rules\n    \"no-debugger\": \"off\",\n\n    // Removed rule \"disallow unused variables\" from recommended eslint rules\n    \"no-unused-vars\": \"off\",\n\n    // Removed rule \"disallow mixed spaces and tabs for indentation\" from recommended eslint rules\n    \"no-mixed-spaces-and-tabs\": \"off\",\n\n    // Removed rule \"disallow the use of undeclared variables unless mentioned in /*global */ comments\" from recommended eslint rules\n    \"no-undef\": \"off\",\n\n    // Warn against template literal placeholder syntax in regular strings\n    \"no-template-curly-in-string\": 1,\n\n    // Warn if return statements do not either always or never specify values\n    \"consistent-return\": 1,\n\n    // Warn if no return statements in callbacks of array methods\n    \"array-callback-return\": 1,\n\n    // Requre the use of === and !==\n    \"eqeqeq\": 2,\n\n    // Disallow the use of alert, confirm, and prompt\n    \"no-alert\": 2,\n\n    // Disallow the use of arguments.caller or arguments.callee\n    \"no-caller\": 2,\n\n    // Disallow null comparisons without type-checking operators\n    \"no-eq-null\": 2,\n\n    // Disallow the use of eval()\n    \"no-eval\": 2,\n\n    // Warn against extending native types\n    \"no-extend-native\": 1,\n\n    // Warn against unnecessary calls to .bind()\n    \"no-extra-bind\": 1,\n\n    // Warn against unnecessary labels    \n    \"no-extra-label\": 1,\n\n    // Disallow leading or trailing decimal points in numeric literals\n    \"no-floating-decimal\": 2,\n\n    // Warn against shorthand type conversions\n    \"no-implicit-coercion\": 1,\n\n    // Warn against function declarations and expressions inside loop statements\n    \"no-loop-func\": 1,\n\n    // Disallow new operators with the Function object\n    \"no-new-func\": 2,\n\n    // Warn against new operators with the String, Number, and Boolean objects\n    \"no-new-wrappers\": 1,\n\n    // Disallow throwing literals as exceptions\n    \"no-throw-literal\": 2,\n\n    // Require using Error objects as Promise rejection reasons\n    \"prefer-promise-reject-errors\": 2,\n\n    // Enforce “for” loop update clause moving the counter in the right direction\n    \"for-direction\": 2,\n\n    // Enforce return statements in getters\n    \"getter-return\": 2,\n\n    // Disallow await inside of loops\n    \"no-await-in-loop\": 2,\n\n    // Disallow comparing against -0\n    \"no-compare-neg-zero\": 2,\n\n    // Warn against catch clause parameters from shadowing variables in the outer scope\n    \"no-catch-shadow\": 1,\n\n    // Disallow identifiers from shadowing restricted names\n    \"no-shadow-restricted-names\": 2,\n\n    // Enforce return statements in callbacks of array methods\n    \"callback-return\": 2,\n\n    // Require error handling in callbacks\n    \"handle-callback-err\": 2,\n\n    // Warn against string concatenation with __dirname and __filename\n    \"no-path-concat\": 1,\n\n    // Prefer using arrow functions for callbacks\n    \"prefer-arrow-callback\": 1,\n\n    // Return inside each then() to create readable and reusable Promise chains.\n    \"promise/always-return\": 2,\n\n    //Enforces the use of catch() on un-returned promises\n    \"promise/catch-or-return\": 2,\n\n    // Warn against nested then() or catch() statements\n    \"promise/no-nesting\": 1\n  }\n}\n"
  },
  {
    "path": "Node-1st-gen/authenticated-json-api/functions/index.js",
    "content": "/**\n * Copyright 2017 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n'use strict';\n\nconst functions = require('firebase-functions/v1');\nconst admin = require('firebase-admin');\n\n// Follow instructions to set up admin credentials:\n// https://firebase.google.com/docs/functions/local-emulator#set_up_admin_credentials_optional\nadmin.initializeApp({\n  credential: admin.credential.applicationDefault(),\n  // TODO: ADD YOUR DATABASE URL\n  databaseURL: undefined\n});\n\nconst language = require('@google-cloud/language');\nconst client = new language.LanguageServiceClient();\nconst express = require('express');\nconst app = express();\n\n\n// Express middleware that validates Firebase ID Tokens passed in the Authorization HTTP header.\n// The Firebase ID token needs to be passed as a Bearer token in the Authorization HTTP header like this:\n// `Authorization: Bearer <Firebase ID Token>`.\n// when decoded successfully, the ID Token content will be added as `req.user`.\nconst authenticate = async (req, res, next) => {\n  if (!req.headers.authorization || !req.headers.authorization.startsWith('Bearer ')) {\n    res.status(403).send('Unauthorized');\n    return;\n  }\n  const idToken = req.headers.authorization.split('Bearer ')[1];\n  try {\n    const decodedIdToken = await admin.auth().verifyIdToken(idToken);\n    req.user = decodedIdToken;\n    next();\n    return;\n  } catch(e) {\n    res.status(403).send('Unauthorized');\n    return;\n  }\n};\n\napp.use(authenticate);\n\n// POST /api/messages\n// Create a new message, get its sentiment using Google Cloud NLP,\n// and categorize the sentiment before saving.\napp.post('/api/messages', async (req, res) => {\n  const message = req.body.message;\n\n  functions.logger.log(`ANALYZING MESSAGE: \"${message}\"`);\n\n  try {\n    const results = await client.analyzeSentiment({\n      document: { content: message, type: 'PLAIN_TEXT' }\n    });\n\n    const category = categorizeScore(results[0].documentSentiment.score);\n    const data = {message: message, sentiment: results[0], category: category};\n\n    // @ts-ignore\n    const uid = req.user.uid;\n    await admin.database().ref(`/users/${uid}/messages`).push(data);\n\n    res.status(201).json({message, category});\n  } catch(error) {\n    functions.logger.log(\n      'Error detecting sentiment or saving message',\n      // @ts-ignore\n      error.message\n    );\n    res.sendStatus(500);\n  }\n});\n\n// GET /api/messages?category={category}\n// Get all messages, optionally specifying a category to filter on\napp.get('/api/messages', async (req, res) => {\n  // @ts-ignore\n  const uid = req.user.uid;\n  const category = `${req.query?.category ?? ''}`;\n\n  /** @type admin.database.Query */\n  let query = admin.database().ref(`/users/${uid}/messages`);\n\n  if (category && ['positive', 'negative', 'neutral'].indexOf(category) > -1) {\n    // Update the query with the valid category\n    query = query.orderByChild('category').equalTo(category);\n  } else if (category) {\n    res.status(404).json({errorCode: 404, errorMessage: `category '${category}' not found`});\n    return;\n  }\n  try {\n    const snapshot = await query.once('value');\n    let messages = [];\n    snapshot.forEach((childSnapshot) => {\n      messages.push({key: childSnapshot.key, message: childSnapshot.val().message});\n    });\n\n    res.status(200).json(messages);\n  } catch(error) {\n    // @ts-ignore\n    functions.logger.log('Error getting messages', error.message);\n    res.sendStatus(500);\n  }\n});\n\n// GET /api/message/{messageId}\n// Get details about a message\napp.get('/api/message/:messageId', async (req, res) => {\n  const messageId = req.params.messageId;\n\n  functions.logger.log(`LOOKING UP MESSAGE \"${messageId}\"`);\n\n  try {\n    // @ts-ignore\n    const uid = req.user.uid;\n    const snapshot = await admin.database().ref(`/users/${uid}/messages/${messageId}`).once('value');\n\n    if (!snapshot.exists()) {\n      return res.status(404).json({errorCode: 404, errorMessage: `message '${messageId}' not found`});\n    }\n    res.set('Cache-Control', 'private, max-age=300');\n    return res.status(200).json(snapshot.val());\n  } catch(error) {\n    functions.logger.log(\n      'Error getting message details',\n      messageId,\n      // @ts-ignore\n      error.message\n    );\n    return res.sendStatus(500);\n  }\n});\n\n// Expose the API as a function\nexports.api = functions.https.onRequest(app);\n\n// Helper function to categorize a sentiment score as positive, negative, or neutral\nconst categorizeScore = (score) => {\n  if (score > 0.25) {\n    return 'positive';\n  } else if (score < -0.25) {\n    return 'negative';\n  }\n  return 'neutral';\n};\n"
  },
  {
    "path": "Node-1st-gen/authenticated-json-api/functions/package.json",
    "content": "{\n  \"name\": \"authenticated-json-api-functions\",\n  \"description\": \"Cloud Functions for Firebase\",\n  \"dependencies\": {\n    \"@google-cloud/language\": \"^3.8.0\",\n    \"express\": \"^4.18.2\",\n    \"firebase-admin\": \"^13.0.2\",\n    \"firebase-functions\": \"7.0.0\"\n  },\n  \"devDependencies\": {\n    \"eslint\": \"^8.57.1\",\n    \"eslint-plugin-promise\": \"^7.2.1\"\n  },\n  \"scripts\": {\n    \"lint\": \"./node_modules/.bin/eslint --max-warnings=0 .\",\n    \"serve\": \"firebase emulators:start --only functions\",\n    \"shell\": \"firebase functions:shell\",\n    \"start\": \"npm run shell\",\n    \"deploy\": \"firebase deploy --only functions\",\n    \"logs\": \"firebase functions:log\",\n    \"compile\": \"cp ../../../tsconfig.template.json ./tsconfig-compile.json && tsc --project tsconfig-compile.json\"\n  },\n  \"engines\": {\n    \"node\": \"20\"\n  },\n  \"private\": true\n}"
  },
  {
    "path": "Node-1st-gen/authenticated-json-api/public/index.html",
    "content": "<!DOCTYPE html>\n<!--\n  Copyright 2017 Google Inc. All rights reserved.\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n      https://www.apache.org/licenses/LICENSE-2.0\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License\n-->\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n    <title>Authenticated JSON API</title>\n\n    <!-- Material Design Lite -->\n    <link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/icon?family=Material+Icons\">\n    <link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/icon?family=Material+Icons\">\n    <link rel=\"stylesheet\" href=\"https://code.getmdl.io/1.1.3/material.light_blue-pink.min.css\">\n\n    <link rel=\"stylesheet\" href=\"main.css\">\n  </head>\n  <body>\n    <header class=\"mdl-layout__header mdl-color-text--white mdl-color--light-blue-700\">\n      <div class=\"mdl-cell mdl-cell--12-col mdl-cell--12-col-tablet mdl-grid\">\n        <div class=\"mdl-layout__header-row mdl-cell mdl-cell--12-col mdl-cell--12-col-tablet mdl-cell--8-col-desktop\">\n          <h3>Authenticated JSON API Demo</h3>\n        </div>\n      </div>\n    </header>\n\n    <div class=\"mdl-grid\">\n      <div class=\"mdl-cell mdl-cell--2-offset--desktop mdl-cell--4-col mdl-shadow--2dp\">\n        <div id=\"demo-sign-in-card\" class=\"mdl-card\">\n          <div class=\"mdl-card__title mdl-color--light-blue-100\">\n            <h2 class=\"mdl-card__title-text\">Sign in with Google</h2>\n          </div>\n          <div class=\"mdl-card__supporting-text mdl-color-text--grey-600\">\n            This web application demonstrates how to expose an authenticated JSON API via Firebase Hosting and\n            Cloud Functions for Firebase.\n          </div>\n          <div class=\"mdl-card__actions mdl-card--border\">\n            <button id=\"demo-sign-in-button\" class=\"mdl-button mdl-button--raised mdl-js-button mdl-js-ripple-effect\">\n              <i class=\"material-icons\">account_circle</i>\n              Sign in with Google\n            </button>\n            <button id=\"demo-sign-out-button\" class=\"mdl-button mdl-button--raised mdl-js-button mdl-js-ripple-effect\">\n              Sign out\n            </button>\n          </div>\n        </div>\n      </div>\n    </div>\n\n    <div class=\"mdl-grid\">\n      <div class=\"mdl-cell\">\n        <h4>Explore the API <small>Requires Sign In</small></h4>\n      </div>\n    </div>\n\n    <div class=\"mdl-grid\">\n      <div class=\"mdl-cell mdl-cell--8-col mdl-shadow--2dp demo-create-message\">\n        <h5>New Message</h5>\n        <div class=\"mdl-textfield mdl-js-textfield\">\n          <textarea class=\"mdl-textfield__input\" type=\"text\" rows=\"1\" id=\"demo-message\" ></textarea>\n          <label class=\"mdl-textfield__label\" for=\"demo-message\">Type a message to analyze...</label>\n        </div>\n        <a id=\"demo-create-message\" class=\"mdl-button mdl-button--raised mdl-js-button mdl-js-ripple-effect\">\n          Save and Analyze\n        </a>\n        <span id=\"demo-create-message-result\"></span>\n      </div>\n    </div>\n\n    <div class=\"mdl-grid\">\n      <div class=\"mdl-cell mdl-cell--4-col mdl-shadow--2dp\">\n        <div class=\"demo-message-list\">\n          <h5>List Messages</h5>\n          <button id=\"message-list-button-all\" class=\"mdl-button mdl-button--raised mdl-js-button mdl-js-ripple-effect message-list-button\">All</button>\n          <button class=\"mdl-button mdl-button--raised mdl-js-button mdl-js-ripple-effect message-list-button\">Positive</button>\n          <button class=\"mdl-button mdl-button--raised mdl-js-button mdl-js-ripple-effect message-list-button\">Negative</button>\n          <button class=\"mdl-button mdl-button--raised mdl-js-button mdl-js-ripple-effect message-list-button\">Neutral</button>\n\n          <ul id=\"demo-message-list\" class=\"mdl-list\"></ul>\n        </div>\n      </div>\n\n      <div class=\"mdl-cell mdl-cell--4-col mdl-shadow--2dp\">\n        <div class=\"demo-message-details\">\n          <h5>Message Details</h5>\n          <pre id=\"demo-message-details\"></pre>\n        </div>\n      </div>\n    </div>\n\n    <script src=\"https://code.getmdl.io/1.1.3/material.min.js\"></script>\n    <script src=\"/__/firebase/10.0.0/firebase-app-compat.js\"></script>\n    <script src=\"/__/firebase/10.0.0/firebase-auth-compat.js\"></script>\n    <script src=\"/__/firebase/10.0.0/firebase-database-compat.js\"></script>\n    <script src=\"/__/firebase/init.js\"></script>\n    <script src=\"https://ajax.googleapis.com/ajax/libs/jquery/3.2.0/jquery.min.js\"></script>\n    <script src=\"main.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "Node-1st-gen/authenticated-json-api/public/main.css",
    "content": "/**\n * Copyright 2017 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n.mdl-card {\n  width: 100%;\n}\n\n#demo-sign-in-card > .mdl-card__title {\n  height: 125px;\n}\n\n#demo-create-message-result {\n  margin-left: 10px;\n}\n\n.mdl-textfield, .mdl-textfield > textarea {\n  width: 100%;\n  resize: none;\n}\n\nh5 {\n  margin: 20px 5px;\n}\n\nbutton:not(:first-child) {\n  margin-left: 5px;\n}\n\nli:hover {\n  background-color: rgba(158,158,158,.2);\n  cursor: pointer;\n}\n\nli.selected {\n  background-color: rgba(158,158,158,.4);\n}\n\n.demo-create-message {\n  padding: 10px;\n}\n\n.demo-message-list,\n.demo-message-details {\n  padding-left: 10px;\n}\n\n.demo-message-list > ul {\n  height: 335px;\n  overflow: auto;\n}\n\n.demo-message-details > pre {\n  height: 400px;\n  overflow: auto;\n}\n"
  },
  {
    "path": "Node-1st-gen/authenticated-json-api/public/main.js",
    "content": "/**\n * Copyright 2017 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n'use strict';\n\nfunction Demo() {\n  $(function() {\n    this.$signInButton = $('#demo-sign-in-button');\n    this.$signOutButton = $('#demo-sign-out-button');\n    this.$messageTextarea = $('#demo-message');\n    this.$createMessageButton = $('#demo-create-message');\n    this.$createMessageResult = $('#demo-create-message-result');\n    this.$messageListButtons = $('.message-list-button');\n    this.$messageList = $('#demo-message-list');\n    this.$messageDetails = $('#demo-message-details');\n\n    this.$signInButton.on('click', this.signIn.bind(this));\n    this.$signOutButton.on('click', this.signOut.bind(this));\n    this.$createMessageButton.on('click', this.createMessage.bind(this));\n    this.$messageListButtons.on('click', this.listMessages.bind(this));\n    firebase.auth().onAuthStateChanged(this.onAuthStateChanged.bind(this));\n  }.bind(this));\n}\n\nDemo.prototype.signIn = function() {\n  firebase.auth().signInWithPopup(new firebase.auth.GoogleAuthProvider());\n};\n\nDemo.prototype.signOut = function() {\n  firebase.auth().signOut();\n};\n\nDemo.prototype.onAuthStateChanged = function(user) {\n  if (user) {\n    // If we have a user, simulate a click to get all their messages.\n    // Material Design Lite will create a <span> child that we'll expect to be clicked\n    $('#message-list-button-all > span').click();\n    this.$messageTextarea.removeAttr('disabled');\n    this.$createMessageButton.removeAttr('disabled');\n  } else {\n    this.$messageTextarea.attr('disabled', true);\n    this.$createMessageButton.attr('disabled', true);\n    this.$createMessageResult.html('');\n    this.$messageList.html('');\n    this.$messageDetails.html('');\n  }\n};\n\nDemo.prototype.createMessage = function() {\n  var message = this.$messageTextarea.val();\n\n  if (message === '') return;\n\n  // Make an authenticated POST request to create a new message\n  this.authenticatedRequest('POST', '/api/messages', {message: message}).then(function(response) {\n    this.$messageTextarea.val('');\n    this.$messageTextarea.parent().removeClass('is-dirty');\n\n    this.$createMessageResult.html('Created <b>' + response.category + '</b> message: ' + response.message);\n  }.bind(this)).catch(function(error) {\n    console.log('Error creating message:', message);\n    throw error;\n  });\n};\n\nDemo.prototype.listMessages = function(event) {\n  this.$messageListButtons.removeClass('mdl-button--accent');\n  $(event.target).parent().addClass('mdl-button--accent');\n  this.$messageList.html('');\n  this.$messageDetails.html('');\n\n  // Make an authenticated GET request for a list of messages\n  // Optionally specifying a category (positive, negative, neutral)\n  var label = $(event.target).parent().text().toLowerCase();\n  var category = label === 'all' ? '' : label;\n  var url = category ? '/api/messages?category=' + category : '/api/messages';\n  this.authenticatedRequest('GET', url).then(function(response) {\n    var elements = response.map(function(message) {\n      return $('<li>')\n        .text(message.message)\n        .addClass('mdl-list__item')\n        .data('key', message.key)\n        .on('click', this.messageDetails.bind(this));\n    }.bind(this));\n\n    // Append items to the list and simulate a click to fetch the first message's details\n    this.$messageList.append(elements);\n\n    if (elements.length > 0) {\n      elements[0].click();\n    }\n  }.bind(this)).catch(function(error) {\n    console.log('Error listing messages.');\n    throw error;\n  });\n};\n\nDemo.prototype.messageDetails = function(event) {\n  $('li').removeClass('selected');\n  $(event.target).addClass('selected');\n\n  var key = $(event.target).data('key');\n  this.authenticatedRequest('GET', '/api/message/' + key).then(function(response) {\n    this.$messageDetails.text(JSON.stringify(response, null, 2));\n  }.bind(this)).catch(function(error) {\n    console.log('Error getting message details.');\n    throw error;\n  });\n};\n\nDemo.prototype.authenticatedRequest = function(method, url, body) {\n  if (!firebase.auth().currentUser) {\n    throw new Error('Not authenticated. Make sure you\\'re signed in!');\n  }\n\n  // Get the Firebase auth token to authenticate the request\n  return firebase.auth().currentUser.getIdToken().then(function(token) {\n    var request = {\n      method: method,\n      url: url,\n      dataType: 'json',\n      beforeSend: function(xhr) { xhr.setRequestHeader('Authorization', 'Bearer ' + token); }\n    };\n\n    if (method === 'POST') {\n      request.contentType = 'application/json';\n      request.data = JSON.stringify(body);\n    }\n\n    console.log('Making authenticated request:', method, url);\n    return $.ajax(request).catch(function() {\n      throw new Error('Request error: ' + method + ' ' + url);\n    });\n  });\n};\n\nwindow.demo = new Demo();\n"
  },
  {
    "path": "Node-1st-gen/authorized-https-endpoint/README.md",
    "content": "# Authorized HTTPS Endpoint\n\nThis samples shows how to restrict an HTTPS Function to only the Firebase users of your app.\n\nOnly users who pass a valid Firebase ID token as a Bearer token in the `Authorization` header of the HTTP request or in a `__session` cookie are authorized to use the function.\n\nChecking the ID token is done with an ExpressJs middleware that also passes the decoded ID token in the Express request object.\n\nOnce authorized the function respond with `Hello <username>`.\n\nThis sample comes with a simple web-based UI whose code is in [public](public) directory that lets you sign-in Firebase and initiates an authorized XHR to the Function.\n\n\n## Setting up the sample\n\n 1. Create a Firebase Project using the [Firebase Console](https://console.firebase.google.com).\n 1. Enable the **Google** Provider in the **Auth** section.\n 1. Clone or download this repo and open the `authorized-https-endpoint` directory.\n 1. You must have the Firebase CLI installed. If you don't have it install it with `npm install -g firebase-tools` and then configure it with `firebase login`.\n 1. Configure the CLI locally by using `firebase use --add` and select your project in the list.\n 1. Install dependencies locally by running: `cd functions; npm install; cd -`\n\n\n## Deploy and test\n\nThis sample comes with a web-based UI for testing the function.\nTo test locally do:\n\n 1. Start serving your project locally using `firebase serve --only hosting,functions`\n 1. Open the app in a browser at `http://localhost:5000`.\n 1. Sign in the web app in the browser using Google Sign-In and two authenticated requests will be performed from the client and the result will be displayed on the page, normally \"Hello <user displayname>\".\n\n\nTo deploy and test on prod do:\n\n 1. Deploy your project using `firebase deploy`\n 1. Open the app using `firebase open hosting:site`, this will open a browser.\n 1. Sign in the web app in the browser using Google Sign-In and two authenticated requests will be performed from the client and the result will be displayed on the page, normally \"Hello <user displayname>\".\n\n\n## Contributing\n\nWe'd love that you contribute to the project. Before doing so please read our [Contributor guide](../CONTRIBUTING.md).\n\n\n## License\n\n© Google, 2017. Licensed under an [Apache-2](../LICENSE) license.\n"
  },
  {
    "path": "Node-1st-gen/authorized-https-endpoint/firebase.json",
    "content": "{\n  \"functions\": {\n    \"codebase\": \"authorized-https-endpoint\"\n  },\n  \"hosting\": {\n    \"public\": \"public\",\n    \"rewrites\": [\n      {\n        \"source\":\"**\",\n        \"function\":\"app\"\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "Node-1st-gen/authorized-https-endpoint/functions/.eslintrc.json",
    "content": "{\n  \"parserOptions\": {\n    // Required for certain syntax usages\n    \"ecmaVersion\": 2022\n  },\n  \"plugins\": [\n    \"promise\"\n  ],\n  \"extends\": \"eslint:recommended\",\n  \"rules\": {\n    // Removed rule \"disallow the use of console\" from recommended eslint rules\n    \"no-console\": \"off\",\n\n    // Removed rule \"disallow multiple spaces in regular expressions\" from recommended eslint rules\n    \"no-regex-spaces\": \"off\",\n\n    // Removed rule \"disallow the use of debugger\" from recommended eslint rules\n    \"no-debugger\": \"off\",\n\n    // Removed rule \"disallow unused variables\" from recommended eslint rules\n    \"no-unused-vars\": \"off\",\n\n    // Removed rule \"disallow mixed spaces and tabs for indentation\" from recommended eslint rules\n    \"no-mixed-spaces-and-tabs\": \"off\",\n\n    // Removed rule \"disallow the use of undeclared variables unless mentioned in /*global */ comments\" from recommended eslint rules\n    \"no-undef\": \"off\",\n\n    // Warn against template literal placeholder syntax in regular strings\n    \"no-template-curly-in-string\": 1,\n\n    // Warn if return statements do not either always or never specify values\n    \"consistent-return\": 1,\n\n    // Warn if no return statements in callbacks of array methods\n    \"array-callback-return\": 1,\n\n    // Requre the use of === and !==\n    \"eqeqeq\": 2,\n\n    // Disallow the use of alert, confirm, and prompt\n    \"no-alert\": 2,\n\n    // Disallow the use of arguments.caller or arguments.callee\n    \"no-caller\": 2,\n\n    // Disallow null comparisons without type-checking operators\n    \"no-eq-null\": 2,\n\n    // Disallow the use of eval()\n    \"no-eval\": 2,\n\n    // Warn against extending native types\n    \"no-extend-native\": 1,\n\n    // Warn against unnecessary calls to .bind()\n    \"no-extra-bind\": 1,\n\n    // Warn against unnecessary labels    \n    \"no-extra-label\": 1,\n\n    // Disallow leading or trailing decimal points in numeric literals\n    \"no-floating-decimal\": 2,\n\n    // Warn against shorthand type conversions\n    \"no-implicit-coercion\": 1,\n\n    // Warn against function declarations and expressions inside loop statements\n    \"no-loop-func\": 1,\n\n    // Disallow new operators with the Function object\n    \"no-new-func\": 2,\n\n    // Warn against new operators with the String, Number, and Boolean objects\n    \"no-new-wrappers\": 1,\n\n    // Disallow throwing literals as exceptions\n    \"no-throw-literal\": 2,\n\n    // Require using Error objects as Promise rejection reasons\n    \"prefer-promise-reject-errors\": 2,\n\n    // Enforce “for” loop update clause moving the counter in the right direction\n    \"for-direction\": 2,\n\n    // Enforce return statements in getters\n    \"getter-return\": 2,\n\n    // Disallow await inside of loops\n    \"no-await-in-loop\": 2,\n\n    // Disallow comparing against -0\n    \"no-compare-neg-zero\": 2,\n\n    // Warn against catch clause parameters from shadowing variables in the outer scope\n    \"no-catch-shadow\": 1,\n\n    // Disallow identifiers from shadowing restricted names\n    \"no-shadow-restricted-names\": 2,\n\n    // Enforce return statements in callbacks of array methods\n    \"callback-return\": 2,\n\n    // Require error handling in callbacks\n    \"handle-callback-err\": 2,\n\n    // Warn against string concatenation with __dirname and __filename\n    \"no-path-concat\": 1,\n\n    // Prefer using arrow functions for callbacks\n    \"prefer-arrow-callback\": 1,\n\n    // Return inside each then() to create readable and reusable Promise chains.\n    \"promise/always-return\": 2,\n\n    //Enforces the use of catch() on un-returned promises\n    \"promise/catch-or-return\": 2,\n\n    // Warn against nested then() or catch() statements\n    \"promise/no-nesting\": 1\n  }\n}\n"
  },
  {
    "path": "Node-1st-gen/authorized-https-endpoint/functions/index.js",
    "content": "/**\n * Copyright 2016 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use strict';\n\nconst functions = require('firebase-functions/v1');\nconst admin = require('firebase-admin');\nadmin.initializeApp();\nconst express = require('express');\nconst cookieParser = require('cookie-parser')();\nconst cors = require('cors')({origin: true});\nconst app = express();\n\n// Express middleware that validates Firebase ID Tokens passed in the Authorization HTTP header.\n// The Firebase ID token needs to be passed as a Bearer token in the Authorization HTTP header like this:\n// `Authorization: Bearer <Firebase ID Token>`.\n// when decoded successfully, the ID Token content will be added as `req.user`.\nconst validateFirebaseIdToken = async (req, res, next) => {\n  functions.logger.log('Check if request is authorized with Firebase ID token');\n\n  if ((!req.headers.authorization || !req.headers.authorization.startsWith('Bearer ')) &&\n      !(req.cookies && req.cookies.__session)) {\n    functions.logger.error(\n      'No Firebase ID token was passed as a Bearer token in the Authorization header.',\n      'Make sure you authorize your request by providing the following HTTP header:',\n      'Authorization: Bearer <Firebase ID Token>',\n      'or by passing a \"__session\" cookie.'\n    );\n    res.status(403).send('Unauthorized');\n    return;\n  }\n\n  let idToken;\n  if (req.headers.authorization && req.headers.authorization.startsWith('Bearer ')) {\n    functions.logger.log('Found \"Authorization\" header');\n    // Read the ID Token from the Authorization header.\n    idToken = req.headers.authorization.split('Bearer ')[1];\n  } else if(req.cookies) {\n    functions.logger.log('Found \"__session\" cookie');\n    // Read the ID Token from cookie.\n    idToken = req.cookies.__session;\n  } else {\n    // No cookie\n    res.status(403).send('Unauthorized');\n    return;\n  }\n\n  try {\n    const decodedIdToken = await admin.auth().verifyIdToken(idToken);\n    functions.logger.log('ID Token correctly decoded', decodedIdToken);\n    req.user = decodedIdToken;\n    next();\n    return;\n  } catch (error) {\n    functions.logger.error('Error while verifying Firebase ID token:', error);\n    res.status(403).send('Unauthorized');\n    return;\n  }\n};\n\napp.use(cors);\napp.use(cookieParser);\napp.use(validateFirebaseIdToken);\napp.get('/hello', (req, res) => {\n  // @ts-ignore\n  res.send(`Hello ${req.user.name}`);\n});\n\n// This HTTPS endpoint can only be accessed by your Firebase Users.\n// Requests need to be authorized by providing an `Authorization` HTTP header\n// with value `Bearer <Firebase ID Token>`.\nexports.app = functions.https.onRequest(app);\n"
  },
  {
    "path": "Node-1st-gen/authorized-https-endpoint/functions/package.json",
    "content": "{\n  \"name\": \"authorized-https-endpoint-functions\",\n  \"description\": \"restrict HTTPS functions to Firebase users\",\n  \"dependencies\": {\n    \"cookie-parser\": \"^1.4.6\",\n    \"cors\": \"^2.8.5\",\n    \"express\": \"^4.18.2\",\n    \"firebase-admin\": \"^13.0.2\",\n    \"firebase-functions\": \"7.0.0\"\n  },\n  \"devDependencies\": {\n    \"eslint\": \"^8.57.1\",\n    \"eslint-plugin-promise\": \"^7.2.1\"\n  },\n  \"scripts\": {\n    \"lint\": \"./node_modules/.bin/eslint --max-warnings=0 .\",\n    \"serve\": \"firebase emulators:start --only functions\",\n    \"shell\": \"firebase functions:shell\",\n    \"start\": \"npm run shell\",\n    \"deploy\": \"firebase deploy --only functions\",\n    \"logs\": \"firebase functions:log\",\n    \"compile\": \"cp ../../../tsconfig.template.json ./tsconfig-compile.json && tsc --project tsconfig-compile.json\"\n  },\n  \"engines\": {\n    \"node\": \"20\"\n  },\n  \"private\": true\n}"
  },
  {
    "path": "Node-1st-gen/authorized-https-endpoint/public/index.html",
    "content": "<!doctype html>\n<!--\n  Copyright 2016 Google Inc. All rights reserved.\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n      https://www.apache.org/licenses/LICENSE-2.0\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License\n-->\n<html lang=\"en\">\n<head>\n  <meta charset=\"utf-8\">\n  <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n  <meta name=\"description\" content=\"Demonstrates how to send a welcome email using Firebase Functions.\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n  <title>Welcome email demo</title>\n\n  <!-- Material Design Lite -->\n  <link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/icon?family=Material+Icons\">\n  <link rel=\"stylesheet\" href=\"https://code.getmdl.io/1.1.3/material.blue_grey-orange.min.css\">\n  <script defer src=\"https://code.getmdl.io/1.1.3/material.min.js\"></script>\n\n  <link rel=\"stylesheet\" href=\"main.css\">\n</head>\n<body>\n<div class=\"demo-layout mdl-layout mdl-js-layout mdl-layout--fixed-header\">\n\n  <!-- Header section containing title -->\n  <header class=\"mdl-layout__header mdl-color-text--white mdl-color--light-blue-700\">\n    <div class=\"mdl-cell mdl-cell--12-col mdl-cell--12-col-tablet mdl-grid\">\n      <div class=\"mdl-layout__header-row mdl-cell mdl-cell--12-col mdl-cell--12-col-tablet mdl-cell--8-col-desktop\">\n        <h3>Authorized HTTP Function demo</h3>\n      </div>\n    </div>\n  </header>\n  <main class=\"mdl-layout__content mdl-color--grey-100\">\n    <div class=\"mdl-cell--12-col mdl-cell--12-col-tablet mdl-grid\">\n\n      <!-- Card containing the sign-in UI -->\n      <div id=\"demo-signed-out-card\" class=\"mdl-card mdl-shadow--2dp mdl-cell\">\n        <div class=\"mdl-card__supporting-text mdl-color-text--grey-600\">\n          <p>\n            This web application demonstrates how you can send an authorized XHR Request to an HTTPS Function.\n            <strong>Now sign in!</strong>\n          </p>\n          <button id=\"demo-sign-in-button\" class=\"mdl-color-text--grey-700 mdl-button--raised mdl-button mdl-js-button mdl-js-ripple-effect\"><i class=\"material-icons\">account_circle</i> Sign in with Google</button>\n        </div>\n      </div>\n\n      <!-- Card containing the signed-in UI -->\n      <div id=\"demo-signed-in-card\" class=\"mdl-card mdl-shadow--2dp mdl-cell\">\n        <div class=\"mdl-card__supporting-text mdl-color-text--grey-600\">\n          <p>\n            Sending XHR with \"Authorization\" header to:<br>\n            <span id=\"demo-url\"></span><br><br>\n            Response: <span id=\"demo-response\">...</span>\n          </p>\n\n          <hr />\n\n          <p>\n            Sending XHR with \"__session\" cookie to:<br>\n            <span id=\"demo-url-cookie\"></span><br><br>\n            Response: <span id=\"demo-response-cookie\">...</span>\n          </p>\n\n          <hr />\n\n          <button id=\"demo-sign-out-button\" class=\"mdl-color-text--grey-700 mdl-button--raised mdl-button mdl-js-button mdl-js-ripple-effect\">Sign out</button>\n        </div>\n      </div>\n    </div>\n  </main>\n</div>\n\n<!-- Import and configure the Firebase SDK -->\n<!-- These scripts are made available when the app is served or deployed on Firebase Hosting -->\n<!-- If you do not serve/host your project using Firebase Hosting see https://firebase.google.com/docs/web/setup -->\n<script src=\"/__/firebase/10.0.0/firebase-app-compat.js\"></script>\n<script src=\"/__/firebase/10.0.0/firebase-auth-compat.js\"></script>\n<script src=\"/__/firebase/init.js\"></script>\n\n<script src=\"main.js\"></script>\n</body>\n</html>\n"
  },
  {
    "path": "Node-1st-gen/authorized-https-endpoint/public/main.css",
    "content": "/**\n * Copyright 2016 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nhtml, body {\n  font-family: 'Roboto', 'Helvetica', sans-serif;\n}\n.mdl-grid {\n  max-width: 1024px;\n  margin: auto;\n}\n.mdl-card {\n  min-height: 0;\n  padding-bottom: 5px;\n}\n.mdl-layout__header-row {\n  padding: 0;\n}\nh3 {\n  background: url('firebase-logo.png') no-repeat;\n  background-size: 40px;\n  padding-left: 50px;\n}\n#demo-signed-out-card,\n#demo-signed-in-card {\n  display: none;\n}\n#demo-url,\n#demo-response,\n#demo-url-cookie,\n#demo-response-cookie {\n  font-weight: bold;\n}\n#demo-signed-in-card {\n  width: 600px;\n}"
  },
  {
    "path": "Node-1st-gen/authorized-https-endpoint/public/main.js",
    "content": "/**\n * Copyright 2016 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use strict';\n\n// Initializes the Demo.\nfunction Demo() {\n  document.addEventListener('DOMContentLoaded', function() {\n    // Shortcuts to DOM Elements.\n    this.signInButton = document.getElementById('demo-sign-in-button');\n    this.signOutButton = document.getElementById('demo-sign-out-button');\n    this.responseContainer = document.getElementById('demo-response');\n    this.responseContainerCookie = document.getElementById('demo-response-cookie');\n    this.urlContainer = document.getElementById('demo-url');\n    this.urlContainerCookie = document.getElementById('demo-url-cookie');\n    this.helloUserUrl = window.location.href + 'hello';\n    this.signedOutCard = document.getElementById('demo-signed-out-card');\n    this.signedInCard = document.getElementById('demo-signed-in-card');\n\n    // Bind events.\n    this.signInButton.addEventListener('click', this.signIn.bind(this));\n    this.signOutButton.addEventListener('click', this.signOut.bind(this));\n    firebase.auth().onAuthStateChanged(this.onAuthStateChanged.bind(this));\n  }.bind(this));\n}\n\n// Triggered on Firebase auth state change.\nDemo.prototype.onAuthStateChanged = function(user) {\n  if (user) {\n    this.urlContainer.textContent = this.helloUserUrl;\n    this.urlContainerCookie.textContent = this.helloUserUrl;\n    this.signedOutCard.style.display = 'none';\n    this.signedInCard.style.display = 'block';\n    this.startFunctionsRequest();\n    this.startFunctionsCookieRequest();\n  } else {\n    this.signedOutCard.style.display = 'block';\n    this.signedInCard.style.display = 'none';\n  }\n};\n\n// Initiates the sign-in flow using GoogleAuthProvider sign in in a popup.\nDemo.prototype.signIn = function() {\n  firebase.auth().signInWithPopup(new firebase.auth.GoogleAuthProvider());\n};\n\n// Signs-out of Firebase.\nDemo.prototype.signOut = function() {\n  firebase.auth().signOut();\n  // clear the __session cookie\n  document.cookie = '__session=';\n};\n\n// Does an authenticated request to a Firebase Functions endpoint using an Authorization header.\nDemo.prototype.startFunctionsRequest = function() {\n  firebase.auth().currentUser.getIdToken().then(function(token) {\n    console.log('Sending request to', this.helloUserUrl, 'with ID token in Authorization header.');\n    var req = new XMLHttpRequest();\n    req.onload = function() {\n      this.responseContainer.innerText = req.responseText;\n    }.bind(this);\n    req.onerror = function() {\n      this.responseContainer.innerText = 'There was an error';\n    }.bind(this);\n    req.open('GET', this.helloUserUrl, true);\n    req.setRequestHeader('Authorization', 'Bearer ' + token);\n    req.send();\n  }.bind(this));\n};\n\n// Does an authenticated request to a Firebase Functions endpoint using a __session cookie.\nDemo.prototype.startFunctionsCookieRequest = function() {\n  // Set the __session cookie.\n  firebase.auth().currentUser.getIdToken(true).then(function(token) {\n    // set the __session cookie\n    document.cookie = '__session=' + token + ';max-age=3600';\n\n    console.log('Sending request to', this.helloUserUrl, 'with ID token in __session cookie.');\n    var req = new XMLHttpRequest();\n    req.onload = function() {\n      this.responseContainerCookie.innerText = req.responseText;\n    }.bind(this);\n    req.onerror = function() {\n      this.responseContainerCookie.innerText = 'There was an error';\n    }.bind(this);\n    req.open('GET', this.helloUserUrl, true);\n    req.send();\n  }.bind(this));\n};\n\n// Load the demo.\nwindow.demo = new Demo();\n"
  },
  {
    "path": "Node-1st-gen/bigquery-import/README.md",
    "content": "# Import Data to Big Query\n\nThis template shows how to copy data from the Realtime Database (such as logs being written there) to Google Cloud's BigQuery.\n\n## Function Code\n\nSee file [functions/index.js](functions/index.js) for the code.\n\nThe dependencies are listed in [functions/package.json](functions/package.json).\n\n## Sample Database Structure\n\nAs an example we'll be using a simple logs database structure:\n\n```\n/functions-project-12345\n    /logs\n        /key-123456\n            text: \"User signed in.\"\n        /key-123457\n            text: \"Error: Could not connect to Database\"\n```\n\n## Setting up the sample\n\nSet the `bigquery.datasetName` and `bigquery.tableName` Google Cloud environment variables to match the Dataset name and the Table name where you want the logs written to. For this use:\n\n```bash\nAdd the following configuration to your `.env` file:\n```\nBIGQUERY_DATASETNAME=\"bar\"\nBIGQUERY_TABLENAME=\"baz\"\n```\n```\n"
  },
  {
    "path": "Node-1st-gen/bigquery-import/firebase.json",
    "content": "{\n    \"functions\": {\n        \"codebase\": \"bigquery-import\"\n    }\n}"
  },
  {
    "path": "Node-1st-gen/bigquery-import/functions/.eslintrc.json",
    "content": "{\n  \"parserOptions\": {\n    // Required for certain syntax usages\n    \"ecmaVersion\": 2022\n  },\n  \"plugins\": [\n    \"promise\"\n  ],\n  \"extends\": \"eslint:recommended\",\n  \"rules\": {\n    // Removed rule \"disallow the use of console\" from recommended eslint rules\n    \"no-console\": \"off\",\n\n    // Removed rule \"disallow multiple spaces in regular expressions\" from recommended eslint rules\n    \"no-regex-spaces\": \"off\",\n\n    // Removed rule \"disallow the use of debugger\" from recommended eslint rules\n    \"no-debugger\": \"off\",\n\n    // Removed rule \"disallow unused variables\" from recommended eslint rules\n    \"no-unused-vars\": \"off\",\n\n    // Removed rule \"disallow mixed spaces and tabs for indentation\" from recommended eslint rules\n    \"no-mixed-spaces-and-tabs\": \"off\",\n\n    // Removed rule \"disallow the use of undeclared variables unless mentioned in /*global */ comments\" from recommended eslint rules\n    \"no-undef\": \"off\",\n\n    // Warn against template literal placeholder syntax in regular strings\n    \"no-template-curly-in-string\": 1,\n\n    // Warn if return statements do not either always or never specify values\n    \"consistent-return\": 1,\n\n    // Warn if no return statements in callbacks of array methods\n    \"array-callback-return\": 1,\n\n    // Requre the use of === and !==\n    \"eqeqeq\": 2,\n\n    // Disallow the use of alert, confirm, and prompt\n    \"no-alert\": 2,\n\n    // Disallow the use of arguments.caller or arguments.callee\n    \"no-caller\": 2,\n\n    // Disallow null comparisons without type-checking operators\n    \"no-eq-null\": 2,\n\n    // Disallow the use of eval()\n    \"no-eval\": 2,\n\n    // Warn against extending native types\n    \"no-extend-native\": 1,\n\n    // Warn against unnecessary calls to .bind()\n    \"no-extra-bind\": 1,\n\n    // Warn against unnecessary labels    \n    \"no-extra-label\": 1,\n\n    // Disallow leading or trailing decimal points in numeric literals\n    \"no-floating-decimal\": 2,\n\n    // Warn against shorthand type conversions\n    \"no-implicit-coercion\": 1,\n\n    // Warn against function declarations and expressions inside loop statements\n    \"no-loop-func\": 1,\n\n    // Disallow new operators with the Function object\n    \"no-new-func\": 2,\n\n    // Warn against new operators with the String, Number, and Boolean objects\n    \"no-new-wrappers\": 1,\n\n    // Disallow throwing literals as exceptions\n    \"no-throw-literal\": 2,\n\n    // Require using Error objects as Promise rejection reasons\n    \"prefer-promise-reject-errors\": 2,\n\n    // Enforce “for” loop update clause moving the counter in the right direction\n    \"for-direction\": 2,\n\n    // Enforce return statements in getters\n    \"getter-return\": 2,\n\n    // Disallow await inside of loops\n    \"no-await-in-loop\": 2,\n\n    // Disallow comparing against -0\n    \"no-compare-neg-zero\": 2,\n\n    // Warn against catch clause parameters from shadowing variables in the outer scope\n    \"no-catch-shadow\": 1,\n\n    // Disallow identifiers from shadowing restricted names\n    \"no-shadow-restricted-names\": 2,\n\n    // Enforce return statements in callbacks of array methods\n    \"callback-return\": 2,\n\n    // Require error handling in callbacks\n    \"handle-callback-err\": 2,\n\n    // Warn against string concatenation with __dirname and __filename\n    \"no-path-concat\": 1,\n\n    // Prefer using arrow functions for callbacks\n    \"prefer-arrow-callback\": 1,\n\n    // Return inside each then() to create readable and reusable Promise chains.\n    \"promise/always-return\": 2,\n\n    //Enforces the use of catch() on un-returned promises\n    \"promise/catch-or-return\": 2,\n\n    // Warn against nested then() or catch() statements\n    \"promise/no-nesting\": 1\n  }\n}\n"
  },
  {
    "path": "Node-1st-gen/bigquery-import/functions/index.js",
    "content": "/**\n * Copyright 2016 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use strict';\n\nconst functions = require('firebase-functions/v1');\nconst {defineString} = require('firebase-functions/params');\nconst { BigQuery } = require('@google-cloud/bigquery');\n\nconst bigquery = new BigQuery();\n\nconst bigqueryDatasetname = defineString('BIGQUERY_DATASETNAME');\nconst bigqueryTablename = defineString('BIGQUERY_TABLENAME');\n\n/**\n * Writes all logs from the Realtime Database into bigquery.\n */\nexports.addtobigquery = functions.database.ref('/logs/{logid}').onCreate((snapshot) => {\n  // TODO: Make sure you set the `BIGQUERY_DATASETNAME` environment variable.\n  const dataset = bigquery.dataset(bigqueryDatasetname.value());\n  // TODO: Make sure you set the `BIGQUERY_TABLENAME` environment variable.\n  const table = dataset.table(bigqueryTablename.value());\n\n  return table.insert({\n    ID: snapshot.key,\n    MESSAGE: snapshot.val().message,\n    NUMBER: snapshot.val().number,\n  });\n});\n"
  },
  {
    "path": "Node-1st-gen/bigquery-import/functions/package.json",
    "content": "{\n  \"name\": \"bigquery-import-functions\",\n  \"description\": \"Import data to BigQuery Firebase Functions sample\",\n  \"dependencies\": {\n    \"@google-cloud/bigquery\": \"^4.7.0\",\n    \"eslint-plugin-promise\": \"^7.2.1\",\n    \"firebase-admin\": \"^13.0.2\",\n    \"firebase-functions\": \"7.0.0\"\n  },\n  \"devDependencies\": {\n    \"eslint\": \"^8.57.1\"\n  },\n  \"scripts\": {\n    \"lint\": \"./node_modules/.bin/eslint --max-warnings=0 .\",\n    \"serve\": \"firebase emulators:start --only functions\",\n    \"shell\": \"firebase functions:shell\",\n    \"start\": \"npm run shell\",\n    \"deploy\": \"firebase deploy --only functions\",\n    \"logs\": \"firebase functions:log\",\n    \"compile\": \"cp ../../../tsconfig.template.json ./tsconfig-compile.json && tsc --project tsconfig-compile.json\"\n  },\n  \"engines\": {\n    \"node\": \"20\"\n  },\n  \"private\": true\n}\n"
  },
  {
    "path": "Node-1st-gen/child-count/README.md",
    "content": "# Tracking the number of elements in a list\n\nThis template shows how to keep track of the number of elements in a Firebase Database list. For instance this can be useful to keep track of the number of \"likes\" or \"followers\" of something shared on a social platform.\n\n## Functions Code\n\nSee file [functions/index.js](functions/index.js) for the code.\n\nThis is done by updating a `likes_count` property on the parent of the list node which is tracked.\n\nThis counting is done in two cases:\n\n1. When a like is added or deleted, the `likes_count` is incremented or decremented.\n2. When the `likes_count` is deleted, all likes are recounted.\n\nThe dependencies are listed in [functions/package.json](functions/package.json).\n\n## Sample Database Structure\n\nAs an example we'll be using the database structure shown below. It keeps tracks of the list of users who liked a post and the count of these likes:\n\n```\n/functions-project-12345\n    /posts\n        /key-123456\n            likes_count: 32\n            /likes \n                user123456: true\n                user456789: true\n                user786245: true\n                ...\n```\n"
  },
  {
    "path": "Node-1st-gen/child-count/firebase.json",
    "content": "{\n    \"functions\": {\n        \"codebase\": \"child-count\"\n    }\n}"
  },
  {
    "path": "Node-1st-gen/child-count/functions/.eslintrc.json",
    "content": "{\n  \"parserOptions\": {\n    // Required for certain syntax usages\n    \"ecmaVersion\": 2022\n  },\n  \"plugins\": [\n    \"promise\"\n  ],\n  \"extends\": \"eslint:recommended\",\n  \"rules\": {\n    // Removed rule \"disallow the use of console\" from recommended eslint rules\n    \"no-console\": \"off\",\n\n    // Removed rule \"disallow multiple spaces in regular expressions\" from recommended eslint rules\n    \"no-regex-spaces\": \"off\",\n\n    // Removed rule \"disallow the use of debugger\" from recommended eslint rules\n    \"no-debugger\": \"off\",\n\n    // Removed rule \"disallow unused variables\" from recommended eslint rules\n    \"no-unused-vars\": \"off\",\n\n    // Removed rule \"disallow mixed spaces and tabs for indentation\" from recommended eslint rules\n    \"no-mixed-spaces-and-tabs\": \"off\",\n\n    // Removed rule \"disallow the use of undeclared variables unless mentioned in /*global */ comments\" from recommended eslint rules\n    \"no-undef\": \"off\",\n\n    // Warn against template literal placeholder syntax in regular strings\n    \"no-template-curly-in-string\": 1,\n\n    // Warn if return statements do not either always or never specify values\n    \"consistent-return\": 1,\n\n    // Warn if no return statements in callbacks of array methods\n    \"array-callback-return\": 1,\n\n    // Requre the use of === and !==\n    \"eqeqeq\": 2,\n\n    // Disallow the use of alert, confirm, and prompt\n    \"no-alert\": 2,\n\n    // Disallow the use of arguments.caller or arguments.callee\n    \"no-caller\": 2,\n\n    // Disallow null comparisons without type-checking operators\n    \"no-eq-null\": 2,\n\n    // Disallow the use of eval()\n    \"no-eval\": 2,\n\n    // Warn against extending native types\n    \"no-extend-native\": 1,\n\n    // Warn against unnecessary calls to .bind()\n    \"no-extra-bind\": 1,\n\n    // Warn against unnecessary labels    \n    \"no-extra-label\": 1,\n\n    // Disallow leading or trailing decimal points in numeric literals\n    \"no-floating-decimal\": 2,\n\n    // Warn against shorthand type conversions\n    \"no-implicit-coercion\": 1,\n\n    // Warn against function declarations and expressions inside loop statements\n    \"no-loop-func\": 1,\n\n    // Disallow new operators with the Function object\n    \"no-new-func\": 2,\n\n    // Warn against new operators with the String, Number, and Boolean objects\n    \"no-new-wrappers\": 1,\n\n    // Disallow throwing literals as exceptions\n    \"no-throw-literal\": 2,\n\n    // Require using Error objects as Promise rejection reasons\n    \"prefer-promise-reject-errors\": 2,\n\n    // Enforce “for” loop update clause moving the counter in the right direction\n    \"for-direction\": 2,\n\n    // Enforce return statements in getters\n    \"getter-return\": 2,\n\n    // Disallow await inside of loops\n    \"no-await-in-loop\": 2,\n\n    // Disallow comparing against -0\n    \"no-compare-neg-zero\": 2,\n\n    // Warn against catch clause parameters from shadowing variables in the outer scope\n    \"no-catch-shadow\": 1,\n\n    // Disallow identifiers from shadowing restricted names\n    \"no-shadow-restricted-names\": 2,\n\n    // Enforce return statements in callbacks of array methods\n    \"callback-return\": 2,\n\n    // Require error handling in callbacks\n    \"handle-callback-err\": 2,\n\n    // Warn against string concatenation with __dirname and __filename\n    \"no-path-concat\": 1,\n\n    // Prefer using arrow functions for callbacks\n    \"prefer-arrow-callback\": 1,\n\n    // Return inside each then() to create readable and reusable Promise chains.\n    \"promise/always-return\": 2,\n\n    //Enforces the use of catch() on un-returned promises\n    \"promise/catch-or-return\": 2,\n\n    // Warn against nested then() or catch() statements\n    \"promise/no-nesting\": 1\n  }\n}\n"
  },
  {
    "path": "Node-1st-gen/child-count/functions/index.js",
    "content": "/**\n * Copyright 2016 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use strict';\n\nconst functions = require('firebase-functions/v1');\nconst admin = require('firebase-admin');\nadmin.initializeApp();\n\n// Keeps track of the length of the 'likes' child list in a separate property.\nexports.countlikechange = functions.database.ref('/posts/{postid}/likes/{likeid}').onWrite(\n    async (change) => {\n      const collectionRef = change.after.ref.parent;\n      const countRef = collectionRef.parent.child('likes_count');\n\n      let increment;\n      if (change.after.exists() && !change.before.exists()) {\n        increment = 1;\n      } else if (!change.after.exists() && change.before.exists()) {\n        increment = -1;\n      } else {\n        return null;\n      }\n\n      // Return the promise from countRef.transaction() so our function\n      // waits for this async event to complete before it exits.\n      await countRef.transaction((current) => {\n        return (current || 0) + increment;\n      });\n      functions.logger.log('Counter updated.');\n      return null;\n    });\n\n// If the number of likes gets deleted, recount the number of likes\nexports.recountlikes = functions.database.ref('/posts/{postid}/likes_count').onDelete(async (snap) => {\n  const counterRef = snap.ref;\n  const collectionRef = counterRef.parent.child('likes');\n\n  // Return the promise from counterRef.set() so our function\n  // waits for this async event to complete before it exits.\n  const messagesData = await collectionRef.once('value');\n  return await counterRef.set(messagesData.numChildren());\n});\n"
  },
  {
    "path": "Node-1st-gen/child-count/functions/package.json",
    "content": "{\n  \"name\": \"child-count-functions\",\n  \"description\": \"Count Child nodes Firebase Functions sample\",\n  \"dependencies\": {\n    \"eslint-plugin-promise\": \"^7.2.1\",\n    \"firebase-admin\": \"^13.0.2\",\n    \"firebase-functions\": \"7.0.0\"\n  },\n  \"devDependencies\": {\n    \"eslint\": \"^8.57.1\"\n  },\n  \"scripts\": {\n    \"lint\": \"./node_modules/.bin/eslint --max-warnings=0 .\",\n    \"serve\": \"firebase emulators:start --only functions\",\n    \"shell\": \"firebase functions:shell\",\n    \"start\": \"npm run shell\",\n    \"deploy\": \"firebase deploy --only functions\",\n    \"logs\": \"firebase functions:log\",\n    \"compile\": \"cp ../../../tsconfig.template.json ./tsconfig-compile.json && tsc --project tsconfig-compile.json\"\n  },\n  \"engines\": {\n    \"node\": \"20\"\n  },\n  \"private\": true\n}\n"
  },
  {
    "path": "Node-1st-gen/convert-images/README.md",
    "content": "# Automatically Convert Images\n\nThis sample demonstrates how to automatically convert images that are uploaded to Firebase Storage to JPEG using ImageMagick.\n\n\n## Functions Code\n\nSee file [functions/index.js](functions/index.js) for the image conversion code.\n\nThe image conversion is performed using ImagMagick which is installed by default on all Cloud Functions instances. This is a CLI so we execute the command from node using the [child-process-promise](https://www.npmjs.com/package/child-process-promise) package. The image is first downloaded locally from the Firebase Storage bucket to the `tmp` folder using the [google-cloud](https://github.com/GoogleCloudPlatform/google-cloud-node) SDK.\n\nThe dependencies are listed in [functions/package.json](functions/package.json).\n\n\n## Trigger rules\n\nThe function triggers on upload of any file to your Firebase project's default Cloud Storage bucket.\n\n\n## Deploy and test\n\nTo deploy and test the sample:\n\n - Create a Firebase project on the [Firebase Console](https://console.firebase.google.com) and visit the **Storage** tab.\n - Deploy your project using `firebase deploy`\n - Go to the Firebase Console **Storage** tab and upload an image that is not a JPEG, for instance a PNG. After a short time an image with the same base name but the `.jpg` extension will be created in the same folder (make sure you refresh the UI to see the new file).\n"
  },
  {
    "path": "Node-1st-gen/convert-images/firebase.json",
    "content": "{\n    \"functions\": {\n        \"codebase\": \"convert-images\"\n    }\n}"
  },
  {
    "path": "Node-1st-gen/convert-images/functions/.eslintrc.json",
    "content": "{\n  \"parserOptions\": {\n    // Required for certain syntax usages\n    \"ecmaVersion\": 2022\n  },\n  \"plugins\": [\n    \"promise\"\n  ],\n  \"extends\": \"eslint:recommended\",\n  \"rules\": {\n    // Removed rule \"disallow the use of console\" from recommended eslint rules\n    \"no-console\": \"off\",\n\n    // Removed rule \"disallow multiple spaces in regular expressions\" from recommended eslint rules\n    \"no-regex-spaces\": \"off\",\n\n    // Removed rule \"disallow the use of debugger\" from recommended eslint rules\n    \"no-debugger\": \"off\",\n\n    // Removed rule \"disallow unused variables\" from recommended eslint rules\n    \"no-unused-vars\": \"off\",\n\n    // Removed rule \"disallow mixed spaces and tabs for indentation\" from recommended eslint rules\n    \"no-mixed-spaces-and-tabs\": \"off\",\n\n    // Removed rule \"disallow the use of undeclared variables unless mentioned in /*global */ comments\" from recommended eslint rules\n    \"no-undef\": \"off\",\n\n    // Warn against template literal placeholder syntax in regular strings\n    \"no-template-curly-in-string\": 1,\n\n    // Warn if return statements do not either always or never specify values\n    \"consistent-return\": 1,\n\n    // Warn if no return statements in callbacks of array methods\n    \"array-callback-return\": 1,\n\n    // Requre the use of === and !==\n    \"eqeqeq\": 2,\n\n    // Disallow the use of alert, confirm, and prompt\n    \"no-alert\": 2,\n\n    // Disallow the use of arguments.caller or arguments.callee\n    \"no-caller\": 2,\n\n    // Disallow null comparisons without type-checking operators\n    \"no-eq-null\": 2,\n\n    // Disallow the use of eval()\n    \"no-eval\": 2,\n\n    // Warn against extending native types\n    \"no-extend-native\": 1,\n\n    // Warn against unnecessary calls to .bind()\n    \"no-extra-bind\": 1,\n\n    // Warn against unnecessary labels    \n    \"no-extra-label\": 1,\n\n    // Disallow leading or trailing decimal points in numeric literals\n    \"no-floating-decimal\": 2,\n\n    // Warn against shorthand type conversions\n    \"no-implicit-coercion\": 1,\n\n    // Warn against function declarations and expressions inside loop statements\n    \"no-loop-func\": 1,\n\n    // Disallow new operators with the Function object\n    \"no-new-func\": 2,\n\n    // Warn against new operators with the String, Number, and Boolean objects\n    \"no-new-wrappers\": 1,\n\n    // Disallow throwing literals as exceptions\n    \"no-throw-literal\": 2,\n\n    // Require using Error objects as Promise rejection reasons\n    \"prefer-promise-reject-errors\": 2,\n\n    // Enforce “for” loop update clause moving the counter in the right direction\n    \"for-direction\": 2,\n\n    // Enforce return statements in getters\n    \"getter-return\": 2,\n\n    // Disallow await inside of loops\n    \"no-await-in-loop\": 2,\n\n    // Disallow comparing against -0\n    \"no-compare-neg-zero\": 2,\n\n    // Warn against catch clause parameters from shadowing variables in the outer scope\n    \"no-catch-shadow\": 1,\n\n    // Disallow identifiers from shadowing restricted names\n    \"no-shadow-restricted-names\": 2,\n\n    // Enforce return statements in callbacks of array methods\n    \"callback-return\": 2,\n\n    // Require error handling in callbacks\n    \"handle-callback-err\": 2,\n\n    // Warn against string concatenation with __dirname and __filename\n    \"no-path-concat\": 1,\n\n    // Prefer using arrow functions for callbacks\n    \"prefer-arrow-callback\": 1,\n\n    // Return inside each then() to create readable and reusable Promise chains.\n    \"promise/always-return\": 2,\n\n    //Enforces the use of catch() on un-returned promises\n    \"promise/catch-or-return\": 2,\n\n    // Warn against nested then() or catch() statements\n    \"promise/no-nesting\": 1\n  }\n}\n"
  },
  {
    "path": "Node-1st-gen/convert-images/functions/index.js",
    "content": "/**\n * Copyright 2016 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use strict';\n\nconst functions = require('firebase-functions/v1');\nconst admin = require('firebase-admin');\nconst mkdirp = require('mkdirp');\nconst spawn = require('child-process-promise').spawn;\nconst path = require('path');\nconst os = require('os');\nconst fs = require('fs');\n\nadmin.initializeApp();\n\n// File extension for the created JPEG files.\nconst JPEG_EXTENSION = '.jpg';\n\n/**\n * When an image is uploaded in the Storage bucket it is converted to JPEG automatically using\n * ImageMagick.\n */\nexports.imageToJPG = functions.storage.object().onFinalize(async (object) => {\n  const filePath = object.name;\n  const baseFileName = path.basename(filePath, path.extname(filePath));\n  const fileDir = path.dirname(filePath);\n  const JPEGFilePath = path.normalize(path.format({dir: fileDir, name: baseFileName, ext: JPEG_EXTENSION}));\n  const tempLocalFile = path.join(os.tmpdir(), filePath);\n  const tempLocalDir = path.dirname(tempLocalFile);\n  const tempLocalJPEGFile = path.join(os.tmpdir(), JPEGFilePath);\n\n  // Exit if this is triggered on a file that is not an image.\n  if (!object.contentType.startsWith('image/')) {\n    functions.logger.log('This is not an image.');\n    return null;\n  }\n\n  // Exit if the image is already a JPEG.\n  if (object.contentType.startsWith('image/jpeg')) {\n    functions.logger.log('Already a JPEG.');\n    return null;\n  }\n\n  const bucket = admin.storage().bucket(object.bucket);\n  // Create the temp directory where the storage file will be downloaded.\n  await mkdirp(tempLocalDir);\n  // Download file from bucket.\n  await bucket.file(filePath).download({destination: tempLocalFile});\n  functions.logger.log('The file has been downloaded to', tempLocalFile);\n  // Convert the image to JPEG using ImageMagick.\n  await spawn('convert', [tempLocalFile, tempLocalJPEGFile]);\n  functions.logger.log('JPEG image created at', tempLocalJPEGFile);\n  // Uploading the JPEG image.\n  await bucket.upload(tempLocalJPEGFile, {destination: JPEGFilePath});\n  functions.logger.log('JPEG image uploaded to Storage at', JPEGFilePath);\n  // Once the image has been converted delete the local files to free up disk space.\n  fs.unlinkSync(tempLocalJPEGFile);\n  fs.unlinkSync(tempLocalFile);\n  return null;\n});\n"
  },
  {
    "path": "Node-1st-gen/convert-images/functions/package.json",
    "content": "{\n  \"name\": \"convert-images-functions\",\n  \"description\": \"Convert images to JPEG Firebase Functions sample\",\n  \"dependencies\": {\n    \"child-process-promise\": \"^2.2.1\",\n    \"eslint-plugin-promise\": \"^7.2.1\",\n    \"firebase-admin\": \"^13.0.2\",\n    \"firebase-functions\": \"7.0.0\",\n    \"mkdirp\": \"^1.0.4\"\n  },\n  \"devDependencies\": {\n    \"eslint\": \"^8.57.1\"\n  },\n  \"scripts\": {\n    \"lint\": \"./node_modules/.bin/eslint --max-warnings=0 .\",\n    \"serve\": \"firebase emulators:start --only functions\",\n    \"shell\": \"firebase functions:shell\",\n    \"start\": \"npm run shell\",\n    \"deploy\": \"firebase deploy --only functions\",\n    \"logs\": \"firebase functions:log\",\n    \"compile\": \"cp ../../../tsconfig.template.json ./tsconfig-compile.json && tsc --project tsconfig-compile.json\"\n  },\n  \"engines\": {\n    \"node\": \"20\"\n  },\n  \"private\": true\n}\n"
  },
  {
    "path": "Node-1st-gen/coupon-on-purchase/README.md",
    "content": "# Send a coupon via FCM to users who have completed a purchase\n\nThis sample shows how to send a coupon to your users who have just purchased something. For instance: 10% off on your next purchase!\n\n\n## Functions Code\n\nSee file [functions/index.js](functions/index.js) for the trigger and the FCM code.\n\nThe function assumes that you are setting the Firebase Analytics User ID to be the same as the Firebase Auth uid using the setUserId API. Also it is assumed that the FCM Device Tokens need to be saved in the Firebase Realtime Database under `/users/$uid/tokens`.\n\nThe dependencies are listed in [functions/package.json](functions/package.json).\n\n\n## Trigger rules\n\nThe function triggers on changes to `in_app_purchase` Firebase Analytics events. For other automatically logged events see: https://support.google.com/firebase/answer/6317485\n\n\n## Deploy and test\n\nThis sample can be tested on your Android and iOS app. To test it out:\n\n - Set the project to your Firebase project using `firebase use --add` then select your projec tin the list.\n - Deploy your project using `firebase deploy`\n - Make your app trigger the `in_app_purchase` event (somehow).\n - Within a few hours the coupon will be sent by an FCM notification.\n"
  },
  {
    "path": "Node-1st-gen/coupon-on-purchase/firebase.json",
    "content": "{\n    \"functions\": {\n        \"codebase\": \"coupon-on-purchase\"\n    }\n}\n"
  },
  {
    "path": "Node-1st-gen/coupon-on-purchase/functions/.eslintrc.json",
    "content": "{\n  \"parserOptions\": {\n    // Required for certain syntax usages\n    \"ecmaVersion\": 2022\n  },\n  \"plugins\": [\n    \"promise\"\n  ],\n  \"extends\": \"eslint:recommended\",\n  \"rules\": {\n    // Removed rule \"disallow the use of console\" from recommended eslint rules\n    \"no-console\": \"off\",\n\n    // Removed rule \"disallow multiple spaces in regular expressions\" from recommended eslint rules\n    \"no-regex-spaces\": \"off\",\n\n    // Removed rule \"disallow the use of debugger\" from recommended eslint rules\n    \"no-debugger\": \"off\",\n\n    // Removed rule \"disallow unused variables\" from recommended eslint rules\n    \"no-unused-vars\": \"off\",\n\n    // Removed rule \"disallow mixed spaces and tabs for indentation\" from recommended eslint rules\n    \"no-mixed-spaces-and-tabs\": \"off\",\n\n    // Removed rule \"disallow the use of undeclared variables unless mentioned in /*global */ comments\" from recommended eslint rules\n    \"no-undef\": \"off\",\n\n    // Warn against template literal placeholder syntax in regular strings\n    \"no-template-curly-in-string\": 1,\n\n    // Warn if return statements do not either always or never specify values\n    \"consistent-return\": 1,\n\n    // Warn if no return statements in callbacks of array methods\n    \"array-callback-return\": 1,\n\n    // Requre the use of === and !==\n    \"eqeqeq\": 2,\n\n    // Disallow the use of alert, confirm, and prompt\n    \"no-alert\": 2,\n\n    // Disallow the use of arguments.caller or arguments.callee\n    \"no-caller\": 2,\n\n    // Disallow null comparisons without type-checking operators\n    \"no-eq-null\": 2,\n\n    // Disallow the use of eval()\n    \"no-eval\": 2,\n\n    // Warn against extending native types\n    \"no-extend-native\": 1,\n\n    // Warn against unnecessary calls to .bind()\n    \"no-extra-bind\": 1,\n\n    // Warn against unnecessary labels    \n    \"no-extra-label\": 1,\n\n    // Disallow leading or trailing decimal points in numeric literals\n    \"no-floating-decimal\": 2,\n\n    // Warn against shorthand type conversions\n    \"no-implicit-coercion\": 1,\n\n    // Warn against function declarations and expressions inside loop statements\n    \"no-loop-func\": 1,\n\n    // Disallow new operators with the Function object\n    \"no-new-func\": 2,\n\n    // Warn against new operators with the String, Number, and Boolean objects\n    \"no-new-wrappers\": 1,\n\n    // Disallow throwing literals as exceptions\n    \"no-throw-literal\": 2,\n\n    // Require using Error objects as Promise rejection reasons\n    \"prefer-promise-reject-errors\": 2,\n\n    // Enforce “for” loop update clause moving the counter in the right direction\n    \"for-direction\": 2,\n\n    // Enforce return statements in getters\n    \"getter-return\": 2,\n\n    // Disallow await inside of loops\n    \"no-await-in-loop\": 2,\n\n    // Disallow comparing against -0\n    \"no-compare-neg-zero\": 2,\n\n    // Warn against catch clause parameters from shadowing variables in the outer scope\n    \"no-catch-shadow\": 1,\n\n    // Disallow identifiers from shadowing restricted names\n    \"no-shadow-restricted-names\": 2,\n\n    // Enforce return statements in callbacks of array methods\n    \"callback-return\": 2,\n\n    // Require error handling in callbacks\n    \"handle-callback-err\": 2,\n\n    // Warn against string concatenation with __dirname and __filename\n    \"no-path-concat\": 1,\n\n    // Prefer using arrow functions for callbacks\n    \"prefer-arrow-callback\": 1,\n\n    // Return inside each then() to create readable and reusable Promise chains.\n    \"promise/always-return\": 2,\n\n    //Enforces the use of catch() on un-returned promises\n    \"promise/catch-or-return\": 2,\n\n    // Warn against nested then() or catch() statements\n    \"promise/no-nesting\": 1\n  }\n}\n"
  },
  {
    "path": "Node-1st-gen/coupon-on-purchase/functions/index.js",
    "content": "/**\n * Copyright 2016 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use strict';\n\nconst functions = require('firebase-functions/v1');\nconst admin = require('firebase-admin');\nadmin.initializeApp();\n\n// [START all]\n/**\n * After a user has completed a purchase, send them a coupon via FCM valid on their next purchase.\n */\n// [START trigger]\nexports.sendCouponOnPurchase = functions.analytics.event('in_app_purchase').onLog((event) => {\n// [END trigger]\n  // [START attributes]\n  const user = event.user;\n  const uid = user.userId; // The user ID set via the setUserId API.\n  const purchaseValue = event.valueInUSD; // Amount of the purchase in USD.\n  const userLanguage = user.deviceInfo.userDefaultLanguage; // The user language in language-country format.\n  // [END attributes]\n\n  // For purchases above 500 USD, we send a coupon of higher value.\n  if (purchaseValue > 500) {\n    return sendHighValueCouponViaFCM(uid, userLanguage);\n  }\n  return sendCouponViaFCM(uid, userLanguage);\n});\n// [END all]\n\n/**\n * Sends a coupon code via FCM to the given user.\n *\n * @param {string} uid The UID of the user.\n * @param {string} userLanguage The user language in language-country format.\n */\nasync function sendCouponViaFCM(uid, userLanguage) {\n  // Fetching all the user's device tokens.\n  const tokens = await getDeviceTokens(uid);\n  if (tokens.length > 0) {\n    // Notification details.\n    let payload = {\n      notification: {\n        title: 'Thanks for your Purchase!',\n        body: 'Get 10% off your next purchase with \"COMEBACK10\".',\n      },\n    };\n\n    // Notification in French.\n    if (userLanguage.split('-')[0] === 'fr') {\n      payload = {\n        notification: {\n          title: 'Merci pour votre achat!',\n          body: 'Obtenez 10% de réduction sur votre prochain achat avec \"COMEBACK10\".',\n        },\n      };\n    }\n\n    // Send notifications to all tokens.\n    return admin.messaging().sendEachForMulticast({\n      notification: payload.notification,\n      tokens\n    });\n  }\n  return null;\n}\n\n/**\n * Sends a high value coupon vode via FCM to the given user.\n *\n * @param {string} uid The UID of the user.\n * @param {string} userLanguage The user language in language-country format.\n */\nasync function sendHighValueCouponViaFCM(uid, userLanguage) {\n  // Fetching all the user's device tokens.\n  const tokens = await getDeviceTokens(uid);\n  if (tokens.length > 0) {\n    // Notification details.\n    let payload = {\n      notification: {\n        title: 'Thanks for your Purchase!',\n        body: 'Get 30% off your next purchase with \"COMEBACK30\".',\n      },\n    };\n\n    // Notification in French.\n    if (userLanguage.split('-')[0] === 'fr') {\n      payload = {\n        notification: {\n          title: 'Merci pour votre achat!',\n          body: 'Obtenez 30% de réduction sur votre prochain achat avec \"COMEBACK30\".',\n        },\n      };\n    }\n\n    // Send notifications to all tokens.\n    return admin.messaging().sendEachForMulticast({\n      notification: payload.notification,\n      tokens\n    });\n  }\n  return null;\n}\n\n/**\n * Get the Device Tokens for the given user.\n *\n * @param {string} uid The UID of the user.\n */\nasync function getDeviceTokens(uid) {\n  const snap = await admin.database().ref(`/users/${uid}/tokens`).once('value');\n  if (snap.exists()) {\n    return Object.keys(snap.val());\n  }\n  return [];\n}\n"
  },
  {
    "path": "Node-1st-gen/coupon-on-purchase/functions/package.json",
    "content": "{\n  \"name\": \"coupon-on-crash-functions\",\n  \"description\": \"Send a coupon via FCM to your users who have experienced a crash.\",\n  \"dependencies\": {\n    \"eslint-plugin-promise\": \"^7.2.1\",\n    \"firebase-admin\": \"^13.0.2\",\n    \"firebase-functions\": \"7.0.0\"\n  },\n  \"devDependencies\": {\n    \"eslint\": \"^8.57.1\"\n  },\n  \"scripts\": {\n    \"lint\": \"./node_modules/.bin/eslint --max-warnings=0 .\",\n    \"serve\": \"firebase emulators:start --only functions\",\n    \"shell\": \"firebase functions:shell\",\n    \"start\": \"npm run shell\",\n    \"deploy\": \"firebase deploy --only functions\",\n    \"logs\": \"firebase functions:log\",\n    \"compile\": \"cp ../../../tsconfig.template.json ./tsconfig-compile.json && tsc --project tsconfig-compile.json\"\n  },\n  \"engines\": {\n    \"node\": \"20\"\n  },\n  \"private\": true\n}\n"
  },
  {
    "path": "Node-1st-gen/delete-old-child-nodes/README.md",
    "content": "# Removing old items from a list\n\nThis sample shows how to remove child nodes older than 2 hours from a Firebase Database list. This can be useful for removing outdated items from a collection.\n\n## Functions Code\n\nSee file [functions/index.js](functions/index.js) for the code.\n\nNote that this code only runs when triggered by a write to the list. This means that if nothing gets written for a while, the list may still contain outdated items. If that does not match with your use-case, you should modify the code to trigger based on a time-interval as shown in [this sample](../delete-unused-accounts-cron).\n\nThe dependencies are listed in [functions/package.json](functions/package.json).\n\n## Sample Database Structure\n\nThe example uses the database structure shown below: \n\n```\n/functions-project-12345\n    /path\n        /to\n            /items\n                item1: {\n                    timestamp: 1497911193083\n                },\n                item2: {\n                    timestamp: 1597911193083                    \n                }\n                ...\n```\n\nThe default cutoff interval is 2 hours, meaning that items older than 2 hours are deleted. You can modify the code to match your database structure and cutoff requirements.\n"
  },
  {
    "path": "Node-1st-gen/delete-old-child-nodes/firebase.json",
    "content": "{\n    \"functions\": {\n        \"codebase\": \"delete-old-child-nodes\"\n    }\n}"
  },
  {
    "path": "Node-1st-gen/delete-old-child-nodes/functions/.eslintrc.json",
    "content": "{\n  \"parserOptions\": {\n    // Required for certain syntax usages\n    \"ecmaVersion\": 2022\n  },\n  \"plugins\": [\n    \"promise\"\n  ],\n  \"extends\": \"eslint:recommended\",\n  \"rules\": {\n    // Removed rule \"disallow the use of console\" from recommended eslint rules\n    \"no-console\": \"off\",\n\n    // Removed rule \"disallow multiple spaces in regular expressions\" from recommended eslint rules\n    \"no-regex-spaces\": \"off\",\n\n    // Removed rule \"disallow the use of debugger\" from recommended eslint rules\n    \"no-debugger\": \"off\",\n\n    // Removed rule \"disallow unused variables\" from recommended eslint rules\n    \"no-unused-vars\": \"off\",\n\n    // Removed rule \"disallow mixed spaces and tabs for indentation\" from recommended eslint rules\n    \"no-mixed-spaces-and-tabs\": \"off\",\n\n    // Removed rule \"disallow the use of undeclared variables unless mentioned in /*global */ comments\" from recommended eslint rules\n    \"no-undef\": \"off\",\n\n    // Warn against template literal placeholder syntax in regular strings\n    \"no-template-curly-in-string\": 1,\n\n    // Warn if return statements do not either always or never specify values\n    \"consistent-return\": 1,\n\n    // Warn if no return statements in callbacks of array methods\n    \"array-callback-return\": 1,\n\n    // Requre the use of === and !==\n    \"eqeqeq\": 2,\n\n    // Disallow the use of alert, confirm, and prompt\n    \"no-alert\": 2,\n\n    // Disallow the use of arguments.caller or arguments.callee\n    \"no-caller\": 2,\n\n    // Disallow null comparisons without type-checking operators\n    \"no-eq-null\": 2,\n\n    // Disallow the use of eval()\n    \"no-eval\": 2,\n\n    // Warn against extending native types\n    \"no-extend-native\": 1,\n\n    // Warn against unnecessary calls to .bind()\n    \"no-extra-bind\": 1,\n\n    // Warn against unnecessary labels    \n    \"no-extra-label\": 1,\n\n    // Disallow leading or trailing decimal points in numeric literals\n    \"no-floating-decimal\": 2,\n\n    // Warn against shorthand type conversions\n    \"no-implicit-coercion\": 1,\n\n    // Warn against function declarations and expressions inside loop statements\n    \"no-loop-func\": 1,\n\n    // Disallow new operators with the Function object\n    \"no-new-func\": 2,\n\n    // Warn against new operators with the String, Number, and Boolean objects\n    \"no-new-wrappers\": 1,\n\n    // Disallow throwing literals as exceptions\n    \"no-throw-literal\": 2,\n\n    // Require using Error objects as Promise rejection reasons\n    \"prefer-promise-reject-errors\": 2,\n\n    // Enforce “for” loop update clause moving the counter in the right direction\n    \"for-direction\": 2,\n\n    // Enforce return statements in getters\n    \"getter-return\": 2,\n\n    // Disallow await inside of loops\n    \"no-await-in-loop\": 2,\n\n    // Disallow comparing against -0\n    \"no-compare-neg-zero\": 2,\n\n    // Warn against catch clause parameters from shadowing variables in the outer scope\n    \"no-catch-shadow\": 1,\n\n    // Disallow identifiers from shadowing restricted names\n    \"no-shadow-restricted-names\": 2,\n\n    // Enforce return statements in callbacks of array methods\n    \"callback-return\": 2,\n\n    // Require error handling in callbacks\n    \"handle-callback-err\": 2,\n\n    // Warn against string concatenation with __dirname and __filename\n    \"no-path-concat\": 1,\n\n    // Prefer using arrow functions for callbacks\n    \"prefer-arrow-callback\": 1,\n\n    // Return inside each then() to create readable and reusable Promise chains.\n    \"promise/always-return\": 2,\n\n    //Enforces the use of catch() on un-returned promises\n    \"promise/catch-or-return\": 2,\n\n    // Warn against nested then() or catch() statements\n    \"promise/no-nesting\": 1\n  }\n}\n"
  },
  {
    "path": "Node-1st-gen/delete-old-child-nodes/functions/index.js",
    "content": "/**\n * Copyright 2017 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use strict';\n\nconst functions = require('firebase-functions/v1');\nconst admin = require('firebase-admin');\nadmin.initializeApp();\n\n// Cut off time. Child nodes older than this will be deleted.\nconst CUT_OFF_TIME = 2 * 60 * 60 * 1000; // 2 Hours in milliseconds.\n\n/**\n * This database triggered function will check for child nodes that are older than the\n * cut-off time. Each child needs to have a `timestamp` attribute.\n */\nexports.deleteOldItems = functions.database.ref('/path/to/items/{pushId}').onWrite(async (change) => {\n  const ref = change.after.ref.parent; // reference to the parent\n  const now = Date.now();\n  const cutoff = now - CUT_OFF_TIME;\n  const oldItemsQuery = ref.orderByChild('timestamp').endAt(cutoff);\n  const snapshot = await oldItemsQuery.once('value');\n  // create a map with all children that need to be removed\n  const updates = {};\n  snapshot.forEach(child => {\n    updates[child.key] = null;\n  });\n  // execute all updates in one go and return the result to end the function\n  return ref.update(updates);\n});\n"
  },
  {
    "path": "Node-1st-gen/delete-old-child-nodes/functions/package.json",
    "content": "{\n  \"name\": \"delete-old-child-nodes\",\n  \"description\": \"Delete nodes of a Realtime Database collection that are older than a given threshold.\",\n  \"dependencies\": {\n    \"eslint\": \"8\",\n    \"eslint-plugin-promise\": \"^7.2.1\",\n    \"firebase-admin\": \"^13.0.2\",\n    \"firebase-functions\": \"7.0.0\"\n  },\n  \"engines\": {\n    \"node\": \"20\"\n  },\n  \"scripts\": {\n    \"compile\": \"cp ../../../tsconfig.template.json ./tsconfig-compile.json && tsc --project tsconfig-compile.json\"\n  },\n  \"private\": true\n}\n"
  },
  {
    "path": "Node-1st-gen/delete-unused-accounts-cron/README.md",
    "content": "# Periodically delete unused accounts\n\nThis sample demonstrates how to delete the accounts of users who have not signed-in in the last month.\n\n\n## Functions Code\n\nSee the file [functions/index.js](functions/index.js) for the code.\n\n**Note:** This function uses Cloud Scheduler and Pub/Sub, which can have associated costs. Your project must be on the Blaze payment plan as these features require billing information. See the [Cloud Scheduler pricing page](https://cloud.google.com/scheduler/pricing) for more information.\n\nThe dependencies are listed in [functions/package.json](functions/package.json).\n\n## Deploy and test\n\nTo set up the sample:\n\n - Create a Firebase Project using the [Firebase Developer Console](https://console.firebase.google.com)\n - Download this sample e.g. `git clone https://github.com/firebase/functions-samples`\n - Enter the sample directory `cd functions-samples/delete-unused-accounts-cron`\n - Setup the sample with your project `firebase use --add` and follow the instructions.\n - Install node dependencies of your Functions `cd functions; npm install; cd -`\n - Deploy your project using `firebase deploy`.\n - The pubsub task should then run once a day and delete any inactive users. You can manually run the task by [navigating to Cloud Scheduler in the Google Cloud Platform Console](https://console.cloud.google.com/cloudscheduler)."
  },
  {
    "path": "Node-1st-gen/delete-unused-accounts-cron/firebase.json",
    "content": "{\n    \"functions\": {\n        \"codebase\": \"delete-unused-accounts\"\n    }\n}"
  },
  {
    "path": "Node-1st-gen/delete-unused-accounts-cron/functions/.eslintrc.json",
    "content": "{\n  \"parserOptions\": {\n    // Required for certain syntax usages\n    \"ecmaVersion\": 2022\n  },\n  \"plugins\": [\n    \"promise\"\n  ],\n  \"extends\": \"eslint:recommended\",\n  \"rules\": {\n    // Removed rule \"disallow the use of console\" from recommended eslint rules\n    \"no-console\": \"off\",\n\n    // Removed rule \"disallow multiple spaces in regular expressions\" from recommended eslint rules\n    \"no-regex-spaces\": \"off\",\n\n    // Removed rule \"disallow the use of debugger\" from recommended eslint rules\n    \"no-debugger\": \"off\",\n\n    // Removed rule \"disallow unused variables\" from recommended eslint rules\n    \"no-unused-vars\": \"off\",\n\n    // Removed rule \"disallow mixed spaces and tabs for indentation\" from recommended eslint rules\n    \"no-mixed-spaces-and-tabs\": \"off\",\n\n    // Removed rule \"disallow the use of undeclared variables unless mentioned in /*global */ comments\" from recommended eslint rules\n    \"no-undef\": \"off\",\n\n    // Warn against template literal placeholder syntax in regular strings\n    \"no-template-curly-in-string\": 1,\n\n    // Warn if return statements do not either always or never specify values\n    \"consistent-return\": 1,\n\n    // Warn if no return statements in callbacks of array methods\n    \"array-callback-return\": 1,\n\n    // Requre the use of === and !==\n    \"eqeqeq\": 2,\n\n    // Disallow the use of alert, confirm, and prompt\n    \"no-alert\": 2,\n\n    // Disallow the use of arguments.caller or arguments.callee\n    \"no-caller\": 2,\n\n    // Disallow null comparisons without type-checking operators\n    \"no-eq-null\": 2,\n\n    // Disallow the use of eval()\n    \"no-eval\": 2,\n\n    // Warn against extending native types\n    \"no-extend-native\": 1,\n\n    // Warn against unnecessary calls to .bind()\n    \"no-extra-bind\": 1,\n\n    // Warn against unnecessary labels    \n    \"no-extra-label\": 1,\n\n    // Disallow leading or trailing decimal points in numeric literals\n    \"no-floating-decimal\": 2,\n\n    // Warn against shorthand type conversions\n    \"no-implicit-coercion\": 1,\n\n    // Warn against function declarations and expressions inside loop statements\n    \"no-loop-func\": 1,\n\n    // Disallow new operators with the Function object\n    \"no-new-func\": 2,\n\n    // Warn against new operators with the String, Number, and Boolean objects\n    \"no-new-wrappers\": 1,\n\n    // Disallow throwing literals as exceptions\n    \"no-throw-literal\": 2,\n\n    // Require using Error objects as Promise rejection reasons\n    \"prefer-promise-reject-errors\": 2,\n\n    // Enforce “for” loop update clause moving the counter in the right direction\n    \"for-direction\": 2,\n\n    // Enforce return statements in getters\n    \"getter-return\": 2,\n\n    // Disallow await inside of loops\n    \"no-await-in-loop\": 2,\n\n    // Disallow comparing against -0\n    \"no-compare-neg-zero\": 2,\n\n    // Warn against catch clause parameters from shadowing variables in the outer scope\n    \"no-catch-shadow\": 1,\n\n    // Disallow identifiers from shadowing restricted names\n    \"no-shadow-restricted-names\": 2,\n\n    // Enforce return statements in callbacks of array methods\n    \"callback-return\": 2,\n\n    // Require error handling in callbacks\n    \"handle-callback-err\": 2,\n\n    // Warn against string concatenation with __dirname and __filename\n    \"no-path-concat\": 1,\n\n    // Prefer using arrow functions for callbacks\n    \"prefer-arrow-callback\": 1,\n\n    // Return inside each then() to create readable and reusable Promise chains.\n    \"promise/always-return\": 2,\n\n    //Enforces the use of catch() on un-returned promises\n    \"promise/catch-or-return\": 2,\n\n    // Warn against nested then() or catch() statements\n    \"promise/no-nesting\": 1\n  }\n}\n"
  },
  {
    "path": "Node-1st-gen/delete-unused-accounts-cron/functions/index.js",
    "content": "/**\n * Copyright 2016 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use strict';\n\nconst functions = require('firebase-functions/v1');\nconst admin = require('firebase-admin');\nadmin.initializeApp();\nconst PromisePool = require('es6-promise-pool').default;\n// Maximum concurrent account deletions.\nconst MAX_CONCURRENT = 3;\n\n/**\n * Run once a day at midnight, to cleanup the users\n * Manually run the task here https://console.cloud.google.com/cloudscheduler\n */\nexports.accountcleanup = functions.pubsub.schedule('every day 00:00').onRun(async context => {\n  // Fetch all user details.\n  const inactiveUsers = await getInactiveUsers();\n\n  // Use a pool so that we delete maximum `MAX_CONCURRENT` users in parallel.\n  const promisePool = new PromisePool(() => deleteInactiveUser(inactiveUsers), MAX_CONCURRENT);\n  await promisePool.start();\n\n  functions.logger.log('User cleanup finished');\n});\n\n/**\n * Deletes one inactive user from the list.\n */\nfunction deleteInactiveUser(inactiveUsers) {\n  if (inactiveUsers.length > 0) {\n    const userToDelete = inactiveUsers.pop();\n    \n    // Delete the inactive user.\n    return admin.auth().deleteUser(userToDelete.uid).then(() => {\n      return functions.logger.log(\n        'Deleted user account',\n        userToDelete.uid,\n        'because of inactivity'\n      );\n    }).catch((error) => {\n      return functions.logger.error(\n        'Deletion of inactive user account',\n        userToDelete.uid,\n        'failed:',\n        error\n      );\n    });\n  } else {\n    return null;\n  }\n}\n\n/**\n * Returns the list of all inactive users.\n */\nasync function getInactiveUsers(users = [], nextPageToken) {\n  const result = await admin.auth().listUsers(1000, nextPageToken);\n  // Find users that have not signed in in the last 30 days.\n  const inactiveUsers = result.users.filter(\n      user => Date.parse(user.metadata.lastRefreshTime || user.metadata.lastSignInTime) < (Date.now() - 30 * 24 * 60 * 60 * 1000));\n  \n  // Concat with list of previously found inactive users if there was more than 1000 users.\n  users = users.concat(inactiveUsers);\n  \n  // If there are more users to fetch we fetch them.\n  if (result.pageToken) {\n    return getInactiveUsers(users, result.pageToken);\n  }\n  \n  return users;\n}\n"
  },
  {
    "path": "Node-1st-gen/delete-unused-accounts-cron/functions/package.json",
    "content": "{\n  \"name\": \"delete-unused-accounts-cron-functions\",\n  \"description\": \"Periodically delete unused Firebase accounts\",\n  \"dependencies\": {\n    \"es6-promise-pool\": \"^2.5.0\",\n    \"eslint-plugin-promise\": \"^7.2.1\",\n    \"firebase-admin\": \"^13.0.2\",\n    \"firebase-functions\": \"7.0.0\"\n  },\n  \"devDependencies\": {\n    \"eslint\": \"^8.57.1\"\n  },\n  \"scripts\": {\n    \"lint\": \"./node_modules/.bin/eslint --max-warnings=0 .\",\n    \"serve\": \"firebase emulators:start --only functions\",\n    \"shell\": \"firebase functions:shell\",\n    \"start\": \"npm run shell\",\n    \"deploy\": \"firebase deploy --only functions\",\n    \"logs\": \"firebase functions:log\",\n    \"compile\": \"cp ../../../tsconfig.template.json ./tsconfig-compile.json && tsc --project tsconfig-compile.json\"\n  },\n  \"engines\": {\n    \"node\": \"20\"\n  },\n  \"private\": true\n}\n"
  },
  {
    "path": "Node-1st-gen/developer-motivator/README.md",
    "content": "# Get notified each time a new user opens your app the first time or removes your app from their device.\n\nThis sample demonstrates how to send a Firebase Cloud Messaging (FCM) notification from a Analytics triggered Function.\n\n\n## Functions Code\n\nSee file [functions/index.js](functions/index.js) for the code.\n\nSending the notification is done using the [Firebase Admin SDK](https://www.npmjs.com/package/firebase-admin).\n\nThe dependencies are listed in [functions/package.json](functions/package.json).\n\n\n## Trigger rules\n\nThe functions triggers every time a new user opens your app the first time or removes your app from their device.\n\n\n## Setup and test this sample section\n\nTo deploy and test the sample:\n\n - Create a Firebase project on the [Firebase Console](https://console.firebase.google.com)\n - In the Firebase Console, under `Analytics`, in the `Events` tab, mark the `app_remove` event a conversion event by switching the toggle. The `first_open` event should already be marked as such.\n - Install the required dependencies by running `npm install` in the `functions` directory\n - Add this log to your android project:\n\n    ```bash\n    Log.d(\"Firebase\", \"token \"+ FirebaseInstanceId.getInstance().getToken());\n    ```\n - Run your app on your device and copy the device token from the android logcat\n - Set the `dev_motivator.device_token` Google Cloud environment variables. For this use:\n\n    ```bash\n    firebase functions:secrets:set DEV_MOTIVATOR_DEVICE_TOKEN\n    ```\n - Deploy your project's code using `firebase deploy`\n - You'll now get a notification on your mobile when a user opens your app for the first time and when they uninstall your app.\n\n"
  },
  {
    "path": "Node-1st-gen/developer-motivator/firebase.json",
    "content": "{\n    \"functions\": {\n        \"codebase\": \"developer-motivator\"\n    }\n}\n"
  },
  {
    "path": "Node-1st-gen/developer-motivator/functions/.eslintrc.json",
    "content": "{\n  \"parserOptions\": {\n    // Required for certain syntax usages\n    \"ecmaVersion\": 2022\n  },\n  \"plugins\": [\n    \"promise\"\n  ],\n  \"extends\": \"eslint:recommended\",\n  \"rules\": {\n    // Removed rule \"disallow the use of console\" from recommended eslint rules\n    \"no-console\": \"off\",\n\n    // Removed rule \"disallow multiple spaces in regular expressions\" from recommended eslint rules\n    \"no-regex-spaces\": \"off\",\n\n    // Removed rule \"disallow the use of debugger\" from recommended eslint rules\n    \"no-debugger\": \"off\",\n\n    // Removed rule \"disallow unused variables\" from recommended eslint rules\n    \"no-unused-vars\": \"off\",\n\n    // Removed rule \"disallow mixed spaces and tabs for indentation\" from recommended eslint rules\n    \"no-mixed-spaces-and-tabs\": \"off\",\n\n    // Removed rule \"disallow the use of undeclared variables unless mentioned in /*global */ comments\" from recommended eslint rules\n    \"no-undef\": \"off\",\n\n    // Warn against template literal placeholder syntax in regular strings\n    \"no-template-curly-in-string\": 1,\n\n    // Warn if return statements do not either always or never specify values\n    \"consistent-return\": 1,\n\n    // Warn if no return statements in callbacks of array methods\n    \"array-callback-return\": 1,\n\n    // Requre the use of === and !==\n    \"eqeqeq\": 2,\n\n    // Disallow the use of alert, confirm, and prompt\n    \"no-alert\": 2,\n\n    // Disallow the use of arguments.caller or arguments.callee\n    \"no-caller\": 2,\n\n    // Disallow null comparisons without type-checking operators\n    \"no-eq-null\": 2,\n\n    // Disallow the use of eval()\n    \"no-eval\": 2,\n\n    // Warn against extending native types\n    \"no-extend-native\": 1,\n\n    // Warn against unnecessary calls to .bind()\n    \"no-extra-bind\": 1,\n\n    // Warn against unnecessary labels    \n    \"no-extra-label\": 1,\n\n    // Disallow leading or trailing decimal points in numeric literals\n    \"no-floating-decimal\": 2,\n\n    // Warn against shorthand type conversions\n    \"no-implicit-coercion\": 1,\n\n    // Warn against function declarations and expressions inside loop statements\n    \"no-loop-func\": 1,\n\n    // Disallow new operators with the Function object\n    \"no-new-func\": 2,\n\n    // Warn against new operators with the String, Number, and Boolean objects\n    \"no-new-wrappers\": 1,\n\n    // Disallow throwing literals as exceptions\n    \"no-throw-literal\": 2,\n\n    // Require using Error objects as Promise rejection reasons\n    \"prefer-promise-reject-errors\": 2,\n\n    // Enforce “for” loop update clause moving the counter in the right direction\n    \"for-direction\": 2,\n\n    // Enforce return statements in getters\n    \"getter-return\": 2,\n\n    // Disallow await inside of loops\n    \"no-await-in-loop\": 2,\n\n    // Disallow comparing against -0\n    \"no-compare-neg-zero\": 2,\n\n    // Warn against catch clause parameters from shadowing variables in the outer scope\n    \"no-catch-shadow\": 1,\n\n    // Disallow identifiers from shadowing restricted names\n    \"no-shadow-restricted-names\": 2,\n\n    // Enforce return statements in callbacks of array methods\n    \"callback-return\": 2,\n\n    // Require error handling in callbacks\n    \"handle-callback-err\": 2,\n\n    // Warn against string concatenation with __dirname and __filename\n    \"no-path-concat\": 1,\n\n    // Prefer using arrow functions for callbacks\n    \"prefer-arrow-callback\": 1,\n\n    // Return inside each then() to create readable and reusable Promise chains.\n    \"promise/always-return\": 2,\n\n    //Enforces the use of catch() on un-returned promises\n    \"promise/catch-or-return\": 2,\n\n    // Warn against nested then() or catch() statements\n    \"promise/no-nesting\": 1\n  }\n}\n"
  },
  {
    "path": "Node-1st-gen/developer-motivator/functions/index.js",
    "content": "/**\n * Copyright 2017 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use strict';\n\nconst admin = require('firebase-admin');\nconst functions = require('firebase-functions/v1');\nconst {defineSecret} = require('firebase-functions/params');\nadmin.initializeApp();\n\n// TODO: Make sure you configure the 'DEV_MOTIVATOR_DEVICE_TOKEN' secret.\nconst devMotivatorDeviceToken = defineSecret('DEV_MOTIVATOR_DEVICE_TOKEN');\n\n/**\n * Triggers when the app is opened the first time in a user device and sends a notification to your developer device.\n *\n * The device model name, the city and the country of the user are sent in the notification message\n */\nexports.appinstalled = functions.runWith({secrets: [devMotivatorDeviceToken]}).analytics.event('first_open').onLog((event) => {\n  const user = event.user;\n  const payload = {\n    notification: {\n      title: 'You have a new user \\uD83D\\uDE43',\n      body: `${user.deviceInfo.mobileModelName} from ${user.geoInfo.city}, ${user.geoInfo.country}`,\n    }\n  };\n\n  return admin.messaging().send({token: devMotivatorDeviceToken.value(), notification: payload.notification});\n});\n\n/**\n * Triggers when the app is removed from the user device and sends a notification to your developer device.\n * NOTE: for this trigger to  work, you must mark the `app_remove` event as a conversion event in Firebase's\n * Analytics dashboard.\n *\n * The device model name, the city and the country of the user are sent in the notification message\n */\nexports.appremoved = functions.runWith({secrets: [devMotivatorDeviceToken]}).analytics.event('app_remove').onLog((event) => {\n  const user = event.user;\n  const payload = {\n    notification: {\n      title: 'You lost a user \\uD83D\\uDE1E',\n      body: `${user.deviceInfo.mobileModelName} from ${user.geoInfo.city}, ${user.geoInfo.country}`,\n    }\n  };\n\n  return admin.messaging().send({token: devMotivatorDeviceToken.value(), notification: payload.notification});\n});\n"
  },
  {
    "path": "Node-1st-gen/developer-motivator/functions/package.json",
    "content": "{\n  \"name\": \"developer-motivator-functions\",\n  \"description\": \"A simple developer motivator using Cloud Function and firebase analytics\",\n  \"dependencies\": {\n    \"eslint-plugin-promise\": \"^7.2.1\",\n    \"firebase-admin\": \"^13.0.2\",\n    \"firebase-functions\": \"7.0.0\"\n  },\n  \"devDependencies\": {\n    \"eslint\": \"^8.57.1\"\n  },\n  \"scripts\": {\n    \"lint\": \"./node_modules/.bin/eslint --max-warnings=0 .\",\n    \"serve\": \"firebase emulators:start --only functions\",\n    \"shell\": \"firebase functions:shell\",\n    \"start\": \"npm run shell\",\n    \"deploy\": \"firebase deploy --only functions\",\n    \"logs\": \"firebase functions:log\",\n    \"compile\": \"cp ../../../tsconfig.template.json ./tsconfig-compile.json && tsc --project tsconfig-compile.json\"\n  },\n  \"engines\": {\n    \"node\": \"20\"\n  },\n  \"private\": true\n}\n"
  },
  {
    "path": "Node-1st-gen/email-confirmation/README.md",
    "content": "# Send Confirmation Emails with Cloud Functions\n\nThis sample shows how to send a confirmation emails to users who are subscribing/un-subscribing to a newsletter.\n\n\n## Functions Code\n\nSee file [functions/index.js](functions/index.js) for the email sending code.\n\nSending emails is performed using [nodemailer](https://www.npmjs.com/package/nodemailer) a node based Email client with comprehensive EMail server setup. For simplicity, in this sample we're showing how to send email through SMTP using a Gmail account. Be aware that Gmail has an [email sending quota](https://support.google.com/mail/answer/22839). If you are planning on sending a large number of emails you should use a professional email sending platform such as [Sendgrid](https://console.cloud.google.com/launcher/details/sendgrid-app/sendgrid-email), [Mailjet](https://www.mailjet.com/google) or [Mailgun](http://www.mailgun.com/google).\n\n>If switching to Sendgrid, Mailjet or Mailgun make sure you enable billing on your Firebase project as this is required to send requests to non-Google services.\n\nThe dependencies are listed in [functions/package.json](functions/package.json).\n\n\n## Sample Database Structure\n\nWhen a signed-in user subscribes or unsubscribes to the mailing list we change the `subscribedToMailingList` boolean:\n\n```\n/functions-project-12345\n    /users\n        /$uid\n            subscribedToMailingList: true,\n            email: \"user@domain.com\"\n```\n\nThen the email stored here is used by the function to send the email.\n\n\n## Trigger rules\n\nThe function triggers on changes to `/users/$uid` and exits if there are no changes to `subscribedToMailingList`.\n\n\n## Setting up the sample\n\n 1. Create a Firebase Project using the [Firebase Console](https://console.firebase.google.com).\n 1. Enable the **Google** Provider in the **Auth** section.\n 1. Clone or download this repo and open the `email-confirmation` directory.\n 1. You must have the Firebase CLI installed. If you don't have it install it with `npm install -g firebase-tools` and then configure it with `firebase login`.\n 1. Configure the CLI locally by using `firebase use --add` and select your project in the list.\n 1. Install dependencies locally by running: `cd functions; npm install; cd -`\n 1. To be able to send emails with your Gmail account: enable access to [Less Secure Apps](https://www.google.com/settings/security/lesssecureapps) and [Display Unlock Captcha](https://accounts.google.com/DisplayUnlockCaptcha). For accounts with 2-step verification enabled [Generate an App Password](https://support.google.com/accounts/answer/185833).\n 1. Set the `gmail.email` and `gmail.password` Google Cloud environment variables to match the email and password of the Gmail account used to send emails (or the app password if your account has 2-step verification enabled). For this use:\n    ```bash\n    Add the following configuration to your `.env` file:\n    ```\nGMAIL_EMAIL=\"myusername@gmail.com\"\n```\nThen, set the `GMAIL_PASSWORD` secret:\n```\nfirebase functions:secrets:set GMAIL_PASSWORD\n```\n    ```\n\n## Deploy and test\n\nThis sample comes with a web-based UI for testing the function. To test it out:\n\n 1. Deploy your project using `firebase deploy`\n 1. Open the app using `firebase open hosting:site`, this will open a browser.\n 1. Sign in the web app in the browser using Google Sign-In and delete your account using the button on the web app. You should receive email confirmations for each actions.\n"
  },
  {
    "path": "Node-1st-gen/email-confirmation/database.rules.json",
    "content": "{\n  \"rules\": {\n    \"users\": {\n      \"$uid\": {\n        \".read\": \"auth.uid === $uid\",\n        \".write\": \"auth.uid === $uid\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "Node-1st-gen/email-confirmation/firebase.json",
    "content": "{\n  \"functions\": {\n    \"codebase\": \"email-confirmation\"\n  },\n  \"database\": {\n    \"rules\": \"database.rules.json\"\n  },\n  \"hosting\": {\n    \"public\": \"public\"\n  }\n}\n"
  },
  {
    "path": "Node-1st-gen/email-confirmation/functions/.eslintrc.json",
    "content": "{\n  \"parserOptions\": {\n    // Required for certain syntax usages\n    \"ecmaVersion\": 2022\n  },\n  \"plugins\": [\n    \"promise\"\n  ],\n  \"extends\": \"eslint:recommended\",\n  \"rules\": {\n    // Removed rule \"disallow the use of console\" from recommended eslint rules\n    \"no-console\": \"off\",\n\n    // Removed rule \"disallow multiple spaces in regular expressions\" from recommended eslint rules\n    \"no-regex-spaces\": \"off\",\n\n    // Removed rule \"disallow the use of debugger\" from recommended eslint rules\n    \"no-debugger\": \"off\",\n\n    // Removed rule \"disallow unused variables\" from recommended eslint rules\n    \"no-unused-vars\": \"off\",\n\n    // Removed rule \"disallow mixed spaces and tabs for indentation\" from recommended eslint rules\n    \"no-mixed-spaces-and-tabs\": \"off\",\n\n    // Removed rule \"disallow the use of undeclared variables unless mentioned in /*global */ comments\" from recommended eslint rules\n    \"no-undef\": \"off\",\n\n    // Warn against template literal placeholder syntax in regular strings\n    \"no-template-curly-in-string\": 1,\n\n    // Warn if return statements do not either always or never specify values\n    \"consistent-return\": 1,\n\n    // Warn if no return statements in callbacks of array methods\n    \"array-callback-return\": 1,\n\n    // Requre the use of === and !==\n    \"eqeqeq\": 2,\n\n    // Disallow the use of alert, confirm, and prompt\n    \"no-alert\": 2,\n\n    // Disallow the use of arguments.caller or arguments.callee\n    \"no-caller\": 2,\n\n    // Disallow null comparisons without type-checking operators\n    \"no-eq-null\": 2,\n\n    // Disallow the use of eval()\n    \"no-eval\": 2,\n\n    // Warn against extending native types\n    \"no-extend-native\": 1,\n\n    // Warn against unnecessary calls to .bind()\n    \"no-extra-bind\": 1,\n\n    // Warn against unnecessary labels    \n    \"no-extra-label\": 1,\n\n    // Disallow leading or trailing decimal points in numeric literals\n    \"no-floating-decimal\": 2,\n\n    // Warn against shorthand type conversions\n    \"no-implicit-coercion\": 1,\n\n    // Warn against function declarations and expressions inside loop statements\n    \"no-loop-func\": 1,\n\n    // Disallow new operators with the Function object\n    \"no-new-func\": 2,\n\n    // Warn against new operators with the String, Number, and Boolean objects\n    \"no-new-wrappers\": 1,\n\n    // Disallow throwing literals as exceptions\n    \"no-throw-literal\": 2,\n\n    // Require using Error objects as Promise rejection reasons\n    \"prefer-promise-reject-errors\": 2,\n\n    // Enforce “for” loop update clause moving the counter in the right direction\n    \"for-direction\": 2,\n\n    // Enforce return statements in getters\n    \"getter-return\": 2,\n\n    // Disallow await inside of loops\n    \"no-await-in-loop\": 2,\n\n    // Disallow comparing against -0\n    \"no-compare-neg-zero\": 2,\n\n    // Warn against catch clause parameters from shadowing variables in the outer scope\n    \"no-catch-shadow\": 1,\n\n    // Disallow identifiers from shadowing restricted names\n    \"no-shadow-restricted-names\": 2,\n\n    // Enforce return statements in callbacks of array methods\n    \"callback-return\": 2,\n\n    // Require error handling in callbacks\n    \"handle-callback-err\": 2,\n\n    // Warn against string concatenation with __dirname and __filename\n    \"no-path-concat\": 1,\n\n    // Prefer using arrow functions for callbacks\n    \"prefer-arrow-callback\": 1,\n\n    // Return inside each then() to create readable and reusable Promise chains.\n    \"promise/always-return\": 2,\n\n    //Enforces the use of catch() on un-returned promises\n    \"promise/catch-or-return\": 2,\n\n    // Warn against nested then() or catch() statements\n    \"promise/no-nesting\": 1\n  }\n}\n"
  },
  {
    "path": "Node-1st-gen/email-confirmation/functions/index.js",
    "content": "/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use strict';\n\nconst functions = require('firebase-functions/v1');\nconst {onInit} = require('firebase-functions/v1/init');\nconst {defineString, defineSecret} = require('firebase-functions/params');\nconst nodemailer = require('nodemailer');\n// Configure the email transport using the default SMTP transport and a GMail account.\n// For other types of transports such as Sendgrid see https://nodemailer.com/transports/\n// TODO: Configure the `GMAIL_EMAIL` environment variable and the `GMAIL_PASSWORD` secret.\nconst gmailEmail = defineString('GMAIL_EMAIL');\nconst gmailPassword = defineSecret('GMAIL_PASSWORD');\n\nlet mailTransport;\nonInit(() => {\n  mailTransport = nodemailer.createTransport({\n    service: 'gmail',\n    auth: {\n      user: gmailEmail.value(),\n      pass: gmailPassword.value(),\n    },\n  });\n});\n\n// Sends an email confirmation when a user changes his mailing list subscription.\nexports.sendEmailConfirmation = functions.runWith({secrets: [gmailPassword]}).database.ref('/users/{uid}').onWrite(async (change) => {\n  // Early exit if the 'subscribedToMailingList' field has not changed\n  if (change.after.child('subscribedToMailingList').val() === change.before.child('subscribedToMailingList').val()) {\n    return null;\n  }\n\n  const val = change.after.val();\n\n  const mailOptions = {\n    from: '\"Spammy Corp.\" <noreply@firebase.com>',\n    to: val.email,\n  };\n\n  const subscribed = val.subscribedToMailingList;\n\n  // Building Email message.\n  mailOptions.subject = subscribed ? 'Thanks and Welcome!' : 'Sad to see you go :`(';\n  mailOptions.text = subscribed ?\n      'Thanks you for subscribing to our newsletter. You will receive our next weekly newsletter.' :\n      'I hereby confirm that I will stop sending you the newsletter.';\n  \n  try {\n    await mailTransport.sendMail(mailOptions);\n    functions.logger.log(\n      `New ${subscribed ? '' : 'un'}subscription confirmation email sent to:`,\n      val.email\n    );\n  } catch(error) {\n    functions.logger.error(\n      'There was an error while sending the email:',\n      error\n    );\n  }\n  return null;\n});\n"
  },
  {
    "path": "Node-1st-gen/email-confirmation/functions/package.json",
    "content": "{\n  \"name\": \"email-confirmation-functions\",\n  \"description\": \"Send Email confirmation upon sign up to a Mailing list Firebase Functions sample\",\n  \"dependencies\": {\n    \"eslint-plugin-promise\": \"^7.2.1\",\n    \"firebase-admin\": \"^13.0.2\",\n    \"firebase-functions\": \"7.0.0\",\n    \"nodemailer\": \"^6.8.0\"\n  },\n  \"devDependencies\": {\n    \"eslint\": \"^8.57.1\"\n  },\n  \"scripts\": {\n    \"lint\": \"./node_modules/.bin/eslint --max-warnings=0 .\",\n    \"serve\": \"firebase emulators:start --only functions\",\n    \"shell\": \"firebase functions:shell\",\n    \"start\": \"npm run shell\",\n    \"deploy\": \"firebase deploy --only functions\",\n    \"logs\": \"firebase functions:log\",\n    \"compile\": \"cp ../../../tsconfig.template.json ./tsconfig-compile.json && tsc --project tsconfig-compile.json\"\n  },\n  \"engines\": {\n    \"node\": \"20\"\n  },\n  \"private\": true\n}\n"
  },
  {
    "path": "Node-1st-gen/email-confirmation/public/index.html",
    "content": "<!doctype html>\n<!--\n  Copyright 2015 Google Inc. All rights reserved.\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n      https://www.apache.org/licenses/LICENSE-2.0\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License\n-->\n<html lang=\"en\">\n<head>\n  <meta charset=\"utf-8\">\n  <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n  <meta name=\"description\" content=\"Demonstrates the use of Google Cloud Functions with a Firebase DB\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n  <title>Functions Email Confirmation Demo</title>\n\n  <!-- Disable tap highlight on IE -->\n  <meta name=\"msapplication-tap-highlight\" content=\"no\">\n\n  <!-- Add to homescreen for Chrome on Android -->\n  <meta name=\"mobile-web-app-capable\" content=\"yes\">\n  <meta name=\"application-name\" content=\"Firebase Functions Quickstart\">\n  <meta name=\"theme-color\" content=\"#303F9F\">\n\n  <!-- Add to homescreen for Safari on iOS -->\n  <meta name=\"apple-mobile-web-app-capable\" content=\"yes\">\n  <meta name=\"apple-mobile-web-app-status-bar-style\" content=\"black-translucent\">\n  <meta name=\"apple-mobile-web-app-title\" content=\"Firebase Functions Quickstart\">\n  <meta name=\"apple-mobile-web-app-status-bar-style\" content=\"#303F9F\">\n\n  <!-- Tile icon for Win8 -->\n  <meta name=\"msapplication-TileColor\" content=\"#3372DF\">\n  <meta name=\"msapplication-navbutton-color\" content=\"#303F9F\">\n\n  <!-- Material Design Lite -->\n  <link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/icon?family=Material+Icons\">\n  <link rel=\"stylesheet\" href=\"https://code.getmdl.io/1.1.3/material.blue_grey-orange.min.css\">\n  <script defer src=\"https://code.getmdl.io/1.1.3/material.min.js\"></script>\n\n  <link rel=\"stylesheet\" href=\"main.css\">\n</head>\n<body>\n<div class=\"demo-layout mdl-layout mdl-js-layout mdl-layout--fixed-header\">\n\n  <!-- Header section containing title -->\n  <header class=\"mdl-layout__header mdl-color-text--white mdl-color--light-blue-700\">\n    <div class=\"mdl-cell mdl-cell--12-col mdl-cell--12-col-tablet mdl-grid\">\n      <div class=\"mdl-layout__header-row mdl-cell mdl-cell--12-col mdl-cell--12-col-tablet mdl-cell--8-col-desktop\">\n        <h3>Very interesting website with Newsletter</h3>\n      </div>\n    </div>\n  </header>\n\n  <main class=\"mdl-layout__content mdl-color--grey-100\">\n    <div class=\"mdl-cell mdl-cell--12-col mdl-cell--12-col-tablet mdl-grid\">\n\n      <!-- Card containing the sign-in UI -->\n      <div id=\"demo-signed-out-card\" class=\"mdl-card mdl-shadow--2dp mdl-cell\">\n        <div class=\"mdl-card__supporting-text mdl-color-text--grey-600\">\n          <p>\n          Welcome to my very interesting website. <strong>Would you like to sign up to my Newsletter?</strong>\n          </p>\n          <p>\n          To subscribe just:\n          </p>\n          <button id=\"demo-sign-in-button\" class=\"mdl-color-text--grey-700 mdl-button--raised mdl-button mdl-js-button mdl-js-ripple-effect\"><i class=\"material-icons\">account_circle</i> Sign in with Google</button>\n        </div>\n      </div>\n\n      <!-- Card containing the signed-in UI -->\n      <div id=\"demo-signed-in-card\" class=\"mdl-card mdl-shadow--2dp mdl-cell\">\n        <div class=\"mdl-card__supporting-text mdl-color-text--grey-600\">\n          <p>\n            Welcome <span id=\"demo-name-container\"></span>.\n          </p>\n          <p id=\"demo-subscribed-text-container\">\n            You are currently subscribed to the Mailing list.\n          </p>\n          <p id=\"demo-unsubscribed-text-container\">\n            You are <strong>not</strong> subscribed to the Mailing list. Click the button below to subscribe.\n          </p>\n          <p>\n            You will receive emails to <strong><span id=\"demo-email-container\"></span></strong>.\n          </p>\n          <button id=\"demo-subscribe-button\" class=\"mdl-color-text--grey-700 mdl-button--raised mdl-button mdl-js-button mdl-js-ripple-effect\">Subscribe</button>\n          <button id=\"demo-unsubscribe-button\" class=\"mdl-color-text--grey-700 mdl-button--raised mdl-button mdl-js-button mdl-js-ripple-effect\">Unsubscribe</button>\n          <button id=\"demo-sign-out-button\" class=\"mdl-color-text--grey-700 mdl-button--raised mdl-button mdl-js-button mdl-js-ripple-effect\">Sign out</button>\n        </div>\n      </div>\n    </div>\n  </main>\n</div>\n\n\n<!-- Import and configure the Firebase SDK -->\n<!-- These scripts are made available when the app is served or deployed on Firebase Hosting -->\n<!-- If you do not serve/host your project using Firebase Hosting see https://firebase.google.com/docs/web/setup -->\n<script src=\"/__/firebase/10.0.0/firebase-app-compat.js\"></script>\n<script src=\"/__/firebase/10.0.0/firebase-auth-compat.js\"></script>\n<script src=\"/__/firebase/10.0.0/firebase-database-compat.js\"></script>\n<script src=\"/__/firebase/init.js\"></script>\n\n<script src=\"main.js\"></script>\n</body>\n</html>\n"
  },
  {
    "path": "Node-1st-gen/email-confirmation/public/main.css",
    "content": "/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nhtml, body {\n  font-family: 'Roboto', 'Helvetica', sans-serif;\n}\n.mdl-grid {\n  max-width: 1024px;\n  margin: auto;\n}\n.mdl-card {\n  min-height: 0;\n  padding-bottom: 5px;\n}\n.mdl-layout__header-row {\n  padding: 0;\n}\n#message-form {\n  display: flex;\n  flex-direction: column;\n}\n#message-form button {\n  max-width: 300px;\n}\n#message-list {\n  padding: 0;\n  width: 100%;\n}\n#message-list > div {\n  padding: 15px;\n  border-bottom: 1px #f1f1f1 solid;\n}\nh3 {\n  background: url('firebase-logo.png') no-repeat;\n  background-size: 40px;\n  padding-left: 50px;\n}\n#demo-signed-out-card,\n#demo-signed-in-card,\n#demo-subscribe-button,\n#demo-unsubscribe-button,\n#demo-subscribed-text-container,\n#demo-unsubscribed-text-container {\n  display: none;\n}\n#demo-subscribe-button,\n#demo-unsubscribe-button {\n  margin-right: 20px;\n}"
  },
  {
    "path": "Node-1st-gen/email-confirmation/public/main.js",
    "content": "/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use strict';\n\n// Initializes the Demo.\nfunction Demo() {\n  // Shortcuts to DOM Elements.\n  this.signInButton = document.getElementById('demo-sign-in-button');\n  this.signOutButton = document.getElementById('demo-sign-out-button');\n  this.subscribeButton = document.getElementById('demo-subscribe-button');\n  this.unsubscribeButton = document.getElementById('demo-unsubscribe-button');\n  this.emailContainer = document.getElementById('demo-email-container');\n  this.subscribedTextContainer = document.getElementById('demo-subscribed-text-container');\n  this.unsubscribedTextContainer = document.getElementById('demo-unsubscribed-text-container');\n  this.nameContainer = document.getElementById('demo-name-container');\n  this.signedOutCard = document.getElementById('demo-signed-out-card');\n  this.signedInCard = document.getElementById('demo-signed-in-card');\n\n  // Bind events.\n  this.signInButton.addEventListener('click', this.signIn.bind(this));\n  this.signOutButton.addEventListener('click', this.signOut.bind(this));\n  this.subscribeButton.addEventListener('click', this.subscribe.bind(this));\n  this.unsubscribeButton.addEventListener('click', this.unsubscribe.bind(this));\n  firebase.auth().onAuthStateChanged(this.onAuthStateChanged.bind(this));\n}\n\nDemo.prototype.onAuthStateChanged = function(user) {\n  if (user) {\n    this.nameContainer.innerText = user.displayName;\n    this.emailContainer.innerText = user.email;\n    this.signedOutCard.style.display = 'none';\n    this.signedInCard.style.display = 'block';\n    this.userRef = firebase.database().ref('users/' + user.uid);\n    this.userRef.on('value', function(data) {\n      if (data.val() && data.val().subscribedToMailingList) {\n        this.subscribedTextContainer.style.display = 'block';\n        this.unsubscribedTextContainer.style.display = 'none';\n        this.subscribeButton.style.display = 'none';\n        this.unsubscribeButton.style.display = 'inline-block';\n      } else {\n        this.subscribedTextContainer.style.display = 'none';\n        this.unsubscribedTextContainer.style.display = 'block';\n        this.subscribeButton.style.display = 'inline-block';\n        this.unsubscribeButton.style.display = 'none';\n      }\n    }.bind(this));\n  } else {\n    if (this.userRef) {\n      this.userRef.off();\n    }\n    this.signedOutCard.style.display = 'block';\n    this.signedInCard.style.display = 'none';\n  }\n};\n\n// Signs-in Firebase.\nDemo.prototype.signIn = function() {\n  firebase.auth().signInWithPopup(new firebase.auth.GoogleAuthProvider()).then(function(result) {\n    // If the user signs-in we automatically signs-him up for the newsletter.\n    this.onAuthStateChanged(result.user);\n    this.subscribe();\n  }.bind(this));\n};\n\n// Signs-out of Firebase.\nDemo.prototype.signOut = function() {\n  firebase.auth().signOut();\n};\n\n// Subscribe to the newsletter.\nDemo.prototype.subscribe = function() {\n  return firebase.database().ref('users/' + firebase.auth().currentUser.uid).set({\n    subscribedToMailingList: true,\n    email: firebase.auth().currentUser.email\n  });\n};\n\n// Unsubscribe to the newsletter.\nDemo.prototype.unsubscribe = function() {\n  return firebase.database().ref('users/' + firebase.auth().currentUser.uid + '/subscribedToMailingList').set(false);\n};\n\n// Bindings on load.\nwindow.addEventListener('load', function() {\n  window.demo = new Demo();\n});\n"
  },
  {
    "path": "Node-1st-gen/exif-images/README.md",
    "content": "# Automatically Extract Images Metadata\n\nThis sample demonstrates how to automatically extract images metadata that are uploaded to Firebase Storage ImageMagick.\n\n\n## Functions Code\n\nSee file [functions/index.js](functions/index.js) for the code.\n\nThe image metadata is provided using ImagMagick `identify` tool which is installed by default on all Cloud Functions instances. This is a CLI for which we use a NodeJS wrapper. The image is first downloaded locally from the Firebase Storage bucket to the `tmp` folder using the [google-cloud](https://github.com/GoogleCloudPlatform/google-cloud-node) SDK.\n\nThe dependencies are listed in [functions/package.json](functions/package.json).\n\n\n## Trigger rules\n\nThe function triggers on upload of any file to your Firebase project default Cloud Storage bucket.\n\n\n## Storage and Database Structure\n\nUsers upload an image to Firebase Storage to the path `/<timestamp>/<filename>` and in return the Function will write to the `/<timestamp>/<filename>` path in the database. The filename typically contains illegal characters for a Firebase Realtime Database keys (such as `.`) so we're replacing all these by the `*` character.\n\nFor example the metadata for the file at path `/1477402116302/mypic.jpg` will be written to the corresponding Database path `/1477402116302/mypic*jpg`\n\n\n## Setting up the sample\n\nThis sample comes with a Function and web-based UI for testing the function. To configure it:\n\n 1. Create a Firebase Project using the [Firebase Console](https://console.firebase.google.com).\n 1. Enable the **Anonymous** sign-in in the **Auth** section.\n 1. Clone or download this repo and open the `exif-image` directory.\n 1. You must have the Firebase CLI installed. If you don't have it install it with `npm install -g firebase-tools` and then configure it with `firebase login`.\n 1. Configure the CLI locally by using `firebase use --add` and select your project in the list.\n 1. Install dependencies locally by running: `cd functions; npm install; cd -`\n\n\n## Deploy and test\n\nTo test the sample:\n\n 1. Deploy your project using `firebase deploy`\n 1. Open the app using `firebase open hosting:site`, this will open a browser.\n 1. Upload an image using the Web UI.\n 1. You should see the metadata displayed below after a bit.\n"
  },
  {
    "path": "Node-1st-gen/exif-images/database.rules.json",
    "content": "{\n  \"rules\": {\n    \".read\": true,\n    \".write\": false\n  }\n}\n"
  },
  {
    "path": "Node-1st-gen/exif-images/firebase.json",
    "content": "{\n  \"functions\": {\n    \"codebase\": \"exif-images\"\n  },\n  \"database\": {\n    \"rules\": \"database.rules.json\"\n  },\n  \"hosting\": {\n    \"public\": \"public\"\n  }\n}\n"
  },
  {
    "path": "Node-1st-gen/exif-images/functions/.eslintrc.json",
    "content": "{\n  \"parserOptions\": {\n    // Required for certain syntax usages\n    \"ecmaVersion\": 2022\n  },\n  \"plugins\": [\n    \"promise\"\n  ],\n  \"extends\": \"eslint:recommended\",\n  \"rules\": {\n    // Removed rule \"disallow the use of console\" from recommended eslint rules\n    \"no-console\": \"off\",\n\n    // Removed rule \"disallow multiple spaces in regular expressions\" from recommended eslint rules\n    \"no-regex-spaces\": \"off\",\n\n    // Removed rule \"disallow the use of debugger\" from recommended eslint rules\n    \"no-debugger\": \"off\",\n\n    // Removed rule \"disallow unused variables\" from recommended eslint rules\n    \"no-unused-vars\": \"off\",\n\n    // Removed rule \"disallow mixed spaces and tabs for indentation\" from recommended eslint rules\n    \"no-mixed-spaces-and-tabs\": \"off\",\n\n    // Removed rule \"disallow the use of undeclared variables unless mentioned in /*global */ comments\" from recommended eslint rules\n    \"no-undef\": \"off\",\n\n    // Warn against template literal placeholder syntax in regular strings\n    \"no-template-curly-in-string\": 1,\n\n    // Warn if return statements do not either always or never specify values\n    \"consistent-return\": 1,\n\n    // Warn if no return statements in callbacks of array methods\n    \"array-callback-return\": 1,\n\n    // Requre the use of === and !==\n    \"eqeqeq\": 2,\n\n    // Disallow the use of alert, confirm, and prompt\n    \"no-alert\": 2,\n\n    // Disallow the use of arguments.caller or arguments.callee\n    \"no-caller\": 2,\n\n    // Disallow null comparisons without type-checking operators\n    \"no-eq-null\": 2,\n\n    // Disallow the use of eval()\n    \"no-eval\": 2,\n\n    // Warn against extending native types\n    \"no-extend-native\": 1,\n\n    // Warn against unnecessary calls to .bind()\n    \"no-extra-bind\": 1,\n\n    // Warn against unnecessary labels    \n    \"no-extra-label\": 1,\n\n    // Disallow leading or trailing decimal points in numeric literals\n    \"no-floating-decimal\": 2,\n\n    // Warn against shorthand type conversions\n    \"no-implicit-coercion\": 1,\n\n    // Warn against function declarations and expressions inside loop statements\n    \"no-loop-func\": 1,\n\n    // Disallow new operators with the Function object\n    \"no-new-func\": 2,\n\n    // Warn against new operators with the String, Number, and Boolean objects\n    \"no-new-wrappers\": 1,\n\n    // Disallow throwing literals as exceptions\n    \"no-throw-literal\": 2,\n\n    // Require using Error objects as Promise rejection reasons\n    \"prefer-promise-reject-errors\": 2,\n\n    // Enforce “for” loop update clause moving the counter in the right direction\n    \"for-direction\": 2,\n\n    // Enforce return statements in getters\n    \"getter-return\": 2,\n\n    // Disallow await inside of loops\n    \"no-await-in-loop\": 2,\n\n    // Disallow comparing against -0\n    \"no-compare-neg-zero\": 2,\n\n    // Warn against catch clause parameters from shadowing variables in the outer scope\n    \"no-catch-shadow\": 1,\n\n    // Disallow identifiers from shadowing restricted names\n    \"no-shadow-restricted-names\": 2,\n\n    // Enforce return statements in callbacks of array methods\n    \"callback-return\": 2,\n\n    // Require error handling in callbacks\n    \"handle-callback-err\": 2,\n\n    // Warn against string concatenation with __dirname and __filename\n    \"no-path-concat\": 1,\n\n    // Prefer using arrow functions for callbacks\n    \"prefer-arrow-callback\": 1,\n\n    // Return inside each then() to create readable and reusable Promise chains.\n    \"promise/always-return\": 2,\n\n    //Enforces the use of catch() on un-returned promises\n    \"promise/catch-or-return\": 2,\n\n    // Warn against nested then() or catch() statements\n    \"promise/no-nesting\": 1\n  }\n}\n"
  },
  {
    "path": "Node-1st-gen/exif-images/functions/index.js",
    "content": "/**\n * Copyright 2016 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for t`he specific language governing permissions and\n * limitations under the License.\n */\n'use strict';\n\nconst functions = require('firebase-functions/v1');\nconst fs = require('fs');\nconst crypto = require('crypto');\nconst path = require('path');\nconst os = require('os');\n\nconst admin = require('firebase-admin');\nadmin.initializeApp();\nconst { Storage } = require('@google-cloud/storage');\nconst spawn = require('child-process-promise').spawn;\n\nconst gcs = new Storage();\n\n/**\n * When an image is uploaded in the Storage bucket the information and metadata of the image (the\n * output of ImageMagick's `identify -verbose`) is saved in the Realtime Database.\n */\nexports.metadata = functions.storage.object().onFinalize(async (object) => {\n  const filePath = object.name;\n\n  // Create random filename with same extension as uploaded file.\n  const randomFileName = crypto.randomBytes(20).toString('hex') + path.extname(filePath);\n  const tempLocalFile = path.join(os.tmpdir(), randomFileName);\n\n  // Exit if this is triggered on a file that is not an image.\n  if (!object.contentType.startsWith('image/')) {\n    functions.logger.log('This is not an image.');\n    return null;\n  }\n\n  let metadata;\n  // Download file from bucket.\n  const bucket = gcs.bucket(object.bucket);\n  await bucket.file(filePath).download({destination: tempLocalFile});\n  // Get Metadata from image.\n  const result = await spawn('identify', ['-verbose', tempLocalFile], {capture: ['stdout', 'stderr']});\n  // Save metadata to realtime datastore.\n  metadata = imageMagickOutputToObject(result.stdout);\n  const safeKey = makeKeyFirebaseCompatible(filePath);\n  await admin.database().ref(safeKey).set(metadata);\n  functions.logger.log('Wrote to:', filePath, 'data:', metadata);\n  // Cleanup temp directory after metadata is extracted\n  // Remove the file from temp directory\n  await fs.unlinkSync(tempLocalFile)\n  return functions.logger.log('cleanup successful!');\n});\n\n/**\n * Convert the output of ImageMagick's `identify -verbose` command to a JavaScript Object.\n */\nfunction imageMagickOutputToObject(output) {\n  let previousLineIndent = 0;\n  const lines = output.match(/[^\\r\\n]+/g);\n  lines.shift(); // Remove First line\n  lines.forEach((line, index) => {\n    const currentIdent = line.search(/\\S/);\n    line = line.trim();\n    if (line.endsWith(':')) {\n      lines[index] = makeKeyFirebaseCompatible(`\"${line.replace(':', '\":{')}`);\n    } else {\n      const split = line.replace('\"', '\\\\\"').split(': ');\n      split[0] = makeKeyFirebaseCompatible(split[0]);\n      lines[index] = `\"${split.join('\":\"')}\",`;\n    }\n    if (currentIdent < previousLineIndent) {\n      lines[index - 1] = lines[index - 1].substring(0, lines[index - 1].length - 1);\n      lines[index] = new Array(1 + (previousLineIndent - currentIdent) / 2).join('}') + ',' + lines[index];\n    }\n    previousLineIndent = currentIdent;\n  });\n  output = lines.join('');\n  output = '{' + output.substring(0, output.length - 1) + '}'; // remove trailing comma.\n  output = JSON.parse(output);\n  functions.logger.log('Metadata extracted from image', output);\n  return output;\n}\n\n/**\n * Makes sure the given string does not contain characters that can't be used as Firebase\n * Realtime Database keys such as '.' and replaces them by '*'.\n */\nfunction makeKeyFirebaseCompatible(key) {\n  return key.replace(/\\./g, '*');\n}\n"
  },
  {
    "path": "Node-1st-gen/exif-images/functions/package.json",
    "content": "{\n  \"name\": \"exif-images-functions\",\n  \"description\": \"Extract EXIF metadata from images Firebase Functions sample\",\n  \"dependencies\": {\n    \"@google-cloud/storage\": \"^4.7.2\",\n    \"child-process-promise\": \"^2.2.1\",\n    \"eslint-plugin-promise\": \"^7.2.1\",\n    \"firebase-admin\": \"^13.0.2\",\n    \"firebase-functions\": \"7.0.0\"\n  },\n  \"devDependencies\": {\n    \"eslint\": \"^8.57.1\"\n  },\n  \"scripts\": {\n    \"lint\": \"./node_modules/.bin/eslint --max-warnings=0 .\",\n    \"serve\": \"firebase emulators:start --only functions\",\n    \"shell\": \"firebase functions:shell\",\n    \"start\": \"npm run shell\",\n    \"deploy\": \"firebase deploy --only functions\",\n    \"logs\": \"firebase functions:log\",\n    \"compile\": \"cp ../../../tsconfig.template.json ./tsconfig-compile.json && tsc --project tsconfig-compile.json\"\n  },\n  \"engines\": {\n    \"node\": \"20\"\n  },\n  \"private\": true\n}\n"
  },
  {
    "path": "Node-1st-gen/exif-images/public/index.html",
    "content": "<!DOCTYPE html>\n<!--\nCopyright (c) 2016 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\nhttp://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n-->\n<html>\n<head>\n  <meta charset=utf-8 />\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n  <title>Image Metadata Extractor</title>\n\n  <!-- Material Design Theming -->\n  <link rel=\"stylesheet\" href=\"https://code.getmdl.io/1.1.3/material.orange-indigo.min.css\">\n  <link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/icon?family=Material+Icons\">\n  <script defer src=\"https://code.getmdl.io/1.1.3/material.min.js\"></script>\n\n  <link rel=\"stylesheet\" href=\"main.css\">\n</head>\n<body>\n  <div class=\"demo-layout mdl-layout mdl-js-layout mdl-layout--fixed-header\">\n\n    <!-- Header section containing title -->\n    <header class=\"mdl-layout__header mdl-color-text--white mdl-color--light-blue-700\">\n      <div class=\"mdl-cell mdl-cell--12-col mdl-cell--12-col-tablet mdl-grid\">\n        <div class=\"mdl-layout__header-row mdl-cell mdl-cell--12-col mdl-cell--12-col-tablet mdl-cell--8-col-desktop\">\n          <h3>Image Metadata Extractor</h3>\n        </div>\n      </div>\n    </header>\n\n    <main class=\"mdl-layout__content mdl-color--grey-100\">\n      <div class=\"mdl-cell mdl-cell--12-col mdl-cell--12-col-tablet mdl-grid\">\n\n        <!-- Container for the demo -->\n        <div class=\"mdl-card mdl-shadow--2dp mdl-cell mdl-cell--12-col mdl-cell--12-col-tablet mdl-cell--12-col-desktop\">\n          <div class=\"mdl-card__title mdl-color--light-blue-600 mdl-color-text--white\">\n            <h2 class=\"mdl-card__title-text\">Upload an image</h2>\n          </div>\n          <div class=\"mdl-card__supporting-text mdl-color-text--grey-600\" id=\"messagesDiv\">\n            <p>Select an image below. When it is uploaded, a shareable link to the file and the image's metadata will be displayed.</p>\n            <h6>Choose File</h6>\n            <input type=\"file\" disabled id=\"demo-file\" name=\"demo-file\" accept=\"image/*;capture=camera\"/>\n            <h6>Image URL:</h6>\n            <span id=\"demo-link\"></span>\n            <h6>Image Metadata:</h6>\n            <pre><code id=\"demo-metadata\"></code></pre>\n          </div>\n        </div>\n      </div>\n    </main>\n  </div>\n\n\n  <!-- Import and configure the Firebase SDK -->\n  <!-- These scripts are made available when the app is served or deployed on Firebase Hosting -->\n  <!-- If you do not serve/host your project using Firebase Hosting see https://firebase.google.com/docs/web/setup -->\n  <script src=\"/__/firebase/10.0.0/firebase-app-compat.js\"></script>\n  <script src=\"/__/firebase/10.0.0/firebase-auth-compat.js\"></script>\n  <script src=\"/__/firebase/10.0.0/firebase-storage-compat.js\"></script>\n  <script src=\"/__/firebase/10.0.0/firebase-database-compat.js\"></script>\n  <script src=\"/__/firebase/init.js\"></script>\n\n  <script src=\"main.js\"></script>\n</body>\n</html>\n"
  },
  {
    "path": "Node-1st-gen/exif-images/public/main.css",
    "content": "/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nhtml, body {\n  font-family: 'Roboto', 'Helvetica', sans-serif;\n}\n.mdl-grid {\n  max-width: 1024px;\n  margin: auto;\n}\n.mdl-layout__header-row {\n  padding: 0;\n}\nh3 {\n  background: url('firebase-logo.png') no-repeat;\n  background-size: 40px;\n  padding-left: 50px;\n}\npre {\n  overflow-x: scroll;\n  line-height: 18px;\n}\ncode {\n  white-space: pre-wrap;\n  word-break: break-all;\n}\n"
  },
  {
    "path": "Node-1st-gen/exif-images/public/main.js",
    "content": "/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use strict';\n\n// Initializes the Demo.\nfunction Demo() {\n  // Shortcuts to DOM Elements.\n  this.fileInput = document.getElementById('demo-file');\n  this.linkContainer = document.getElementById('demo-link');\n  this.metadataContainer = document.getElementById('demo-metadata');\n\n  // Other attributes\n  this.metadataRef = undefined;\n\n  // Event bindings.\n  this.fileInput.addEventListener('change', this.handleFileSelect.bind(this));\n  firebase.auth().onAuthStateChanged(this.onAuthStateChanged.bind(this));\n}\n\n/**\n * Triggered when the Firebase auth state changes.\n */\nDemo.prototype.onAuthStateChanged = function(user) {\n  if (user) {\n    console.log('Anonymous user signed-in.', user);\n    this.fileInput.disabled = false;\n  } else {\n    console.log('There was no anonymous session. Creating a new anonymous user.');\n    // Sign the user in anonymously since accessing Storage requires the user to be authenticated.\n    firebase.auth().signInAnonymously();\n  }\n};\n\n/**\n * Triggered when a user selects a file using the file picker.\n */\nDemo.prototype.handleFileSelect = function(e) {\n  this.fileInput.true = false;\n  e.stopPropagation();\n  e.preventDefault();\n  var file = e.target.files[0];\n\n  // If we were already listening for metadata from a previously uploaded file we stop listening.\n  if (this.metadataRef) {\n    this.metadataRef.off();\n  }\n\n  var metadata = {\n    contentType: file.type\n  };\n\n  // Save the image on Cloud Storage.\n  var filePath = String(Date.now()) + '/' + file.name;\n  firebase.storage().ref(filePath).put(file, metadata).then(function(snapshot) {\n    console.log('Uploaded', snapshot.totalBytes, 'bytes.');\n    var url = snapshot.metadata.downloadURLs[0];\n    console.log('File available at', url);\n    this.linkContainer.innerHTML = '<a href=\"' + url + '\">/' + filePath + '</a>';\n    this.fileInput.disabled = false;\n  }.bind(this)).catch(function(error) {\n    console.error('Upload failed:', error);\n    this.linkContainer.innerHTML = '';\n    this.fileInput.disabled = false;\n  }.bind(this));\n\n  // Start listening for the metadata which will be added to the Realtime DB.\n  this.metadataRef = firebase.database().ref(Demo.makeKeyFirebaseCompatible(filePath));\n  this.metadataRef.on('value', function(snapshot) {\n    var metadata = snapshot.val();\n    this.metadataContainer.innerHTML = metadata ? JSON.stringify(metadata, null, '  ') : '';\n  }.bind(this));\n};\n\n/**\n * Makes sure the given string does not contain characters that can't be used as Firebase\n * Realtime Database keys such as '.' and replaces them by '*'.\n */\nDemo.makeKeyFirebaseCompatible = function(key) {\n  return key.replace(/\\./g, '*');\n};\n\n// Bindings on load.\nwindow.addEventListener('load', function() {\n  window.demo = new Demo();\n});\n"
  },
  {
    "path": "Node-1st-gen/fcm-notifications/README.md",
    "content": "# Send Firebase Cloud Messaging notifications for new followers.\n\nThis sample demonstrates how to send a Firebase Cloud Messaging (FCM) notification from a Realtime Database triggered Function. The sample also features a Web UI to experience the FCM notification.\n\n\n## Functions Code\n\nSee file [functions/index.js](functions/index.js) for the code.\n\nSending the notification is done using the [Firebase Admin SDK](https://www.npmjs.com/package/firebase-admin). The Web client writes the individual device tokens to the realtime database which the Function uses to send the notification.\n\nThe dependencies are listed in [functions/package.json](functions/package.json).\n\n\n## Sample Database Structure\n\nUsers sign into the app and are requested to enable notifications on their browsers. If they successfully enable notifications the device token is saved into the datastore under `/users/$uid/notificationTokens`.:\n\n```\n/functions-project-12345\n    /users\n        /Uid-12345\n            displayName: \"Bob Dole\"\n            /notificationTokens\n                1234567890: true\n            photoURL: \"https://lh3.googleusercontent.com/...\"\n\n```\n\nIf a user starts following another user we'll write to `/followers/$followedUid/$followerUid`:\n\n```\n/functions-project-12345\n    /followers\n        /followedUid-12345\n            followerUid-67890: true\n    /users\n        /Uid-12345\n            displayName: \"Bob Dole\"\n            /notificationTokens\n                1234567890: true\n            photoURL: \"https://lh3.googleusercontent.com/...\"\n\n```\n\n\n## Trigger rules\n\nThe function triggers every time the value of a follow flag changes at `/followers/$followedUid/$followerUid`.\n\n\n## Deploy and test\n\nThis sample comes with a web-based UI for testing the function. To test it out:\n\n 1. Set up your Firebase project:\n     1. [Create a Firebase project](https://firebase.google.com/docs/web/setup/#create-firebase-project)\n     1. [Register your web app with Firebase](https://firebase.google.com/docs/web/setup/#register-app)\n 1. Enable **Google Provider** in the [Auth section](https://console.firebase.google.com/project/_/authentication/providers)\n 1. Clone or download this repo and open the `fcm-notification` directory.\n 1. You must have the Firebase CLI installed. If you don't have it install it with `npm install -g firebase-tools` and then configure it with `firebase login`.\n 1. Configure the CLI locally by using `firebase use --add` and select your project in the list.\n 1. Install dependencies locally by running: `cd functions; npm install; cd -`\n 1. Deploy your project using `firebase deploy`\n 1. Open the app using `firebase open hosting:site`, this will open a browser.\n 1. Start following a user, this will send a notification to them.\n"
  },
  {
    "path": "Node-1st-gen/fcm-notifications/database.rules.json",
    "content": "{\n  \"rules\": {\n    \"users\": {\n      \".read\": true,\n      \"$uid\": {\n        \".write\": \"auth.uid === $uid\"\n      }\n    },\n    \"followers\": {\n      \"$followedUid\": {\n        \"$followerUid\": {\n          \".read\": \"auth.uid === $followerUid\",\n          \".write\": \"auth.uid === $followerUid\"\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "Node-1st-gen/fcm-notifications/firebase.json",
    "content": "{\n  \"functions\": {\n    \"codebase\": \"fcm-notifications\"\n  },\n  \"database\": {\n    \"rules\": \"database.rules.json\"\n  },\n  \"hosting\": {\n    \"public\": \"public\"\n  }\n}\n"
  },
  {
    "path": "Node-1st-gen/fcm-notifications/functions/.eslintrc.json",
    "content": "{\n  \"parserOptions\": {\n    // Required for certain syntax usages\n    \"ecmaVersion\": 2022\n  },\n  \"plugins\": [\n    \"promise\"\n  ],\n  \"extends\": \"eslint:recommended\",\n  \"rules\": {\n    // Removed rule \"disallow the use of console\" from recommended eslint rules\n    \"no-console\": \"off\",\n\n    // Removed rule \"disallow multiple spaces in regular expressions\" from recommended eslint rules\n    \"no-regex-spaces\": \"off\",\n\n    // Removed rule \"disallow the use of debugger\" from recommended eslint rules\n    \"no-debugger\": \"off\",\n\n    // Removed rule \"disallow unused variables\" from recommended eslint rules\n    \"no-unused-vars\": \"off\",\n\n    // Removed rule \"disallow mixed spaces and tabs for indentation\" from recommended eslint rules\n    \"no-mixed-spaces-and-tabs\": \"off\",\n\n    // Removed rule \"disallow the use of undeclared variables unless mentioned in /*global */ comments\" from recommended eslint rules\n    \"no-undef\": \"off\",\n\n    // Warn against template literal placeholder syntax in regular strings\n    \"no-template-curly-in-string\": 1,\n\n    // Warn if return statements do not either always or never specify values\n    \"consistent-return\": 1,\n\n    // Warn if no return statements in callbacks of array methods\n    \"array-callback-return\": 1,\n\n    // Requre the use of === and !==\n    \"eqeqeq\": 2,\n\n    // Disallow the use of alert, confirm, and prompt\n    \"no-alert\": 2,\n\n    // Disallow the use of arguments.caller or arguments.callee\n    \"no-caller\": 2,\n\n    // Disallow null comparisons without type-checking operators\n    \"no-eq-null\": 2,\n\n    // Disallow the use of eval()\n    \"no-eval\": 2,\n\n    // Warn against extending native types\n    \"no-extend-native\": 1,\n\n    // Warn against unnecessary calls to .bind()\n    \"no-extra-bind\": 1,\n\n    // Warn against unnecessary labels    \n    \"no-extra-label\": 1,\n\n    // Disallow leading or trailing decimal points in numeric literals\n    \"no-floating-decimal\": 2,\n\n    // Warn against shorthand type conversions\n    \"no-implicit-coercion\": 1,\n\n    // Warn against function declarations and expressions inside loop statements\n    \"no-loop-func\": 1,\n\n    // Disallow new operators with the Function object\n    \"no-new-func\": 2,\n\n    // Warn against new operators with the String, Number, and Boolean objects\n    \"no-new-wrappers\": 1,\n\n    // Disallow throwing literals as exceptions\n    \"no-throw-literal\": 2,\n\n    // Require using Error objects as Promise rejection reasons\n    \"prefer-promise-reject-errors\": 2,\n\n    // Enforce “for” loop update clause moving the counter in the right direction\n    \"for-direction\": 2,\n\n    // Enforce return statements in getters\n    \"getter-return\": 2,\n\n    // Disallow await inside of loops\n    \"no-await-in-loop\": 2,\n\n    // Disallow comparing against -0\n    \"no-compare-neg-zero\": 2,\n\n    // Warn against catch clause parameters from shadowing variables in the outer scope\n    \"no-catch-shadow\": 1,\n\n    // Disallow identifiers from shadowing restricted names\n    \"no-shadow-restricted-names\": 2,\n\n    // Enforce return statements in callbacks of array methods\n    \"callback-return\": 2,\n\n    // Require error handling in callbacks\n    \"handle-callback-err\": 2,\n\n    // Warn against string concatenation with __dirname and __filename\n    \"no-path-concat\": 1,\n\n    // Prefer using arrow functions for callbacks\n    \"prefer-arrow-callback\": 1,\n\n    // Return inside each then() to create readable and reusable Promise chains.\n    \"promise/always-return\": 2,\n\n    //Enforces the use of catch() on un-returned promises\n    \"promise/catch-or-return\": 2,\n\n    // Warn against nested then() or catch() statements\n    \"promise/no-nesting\": 1\n  }\n}\n"
  },
  {
    "path": "Node-1st-gen/fcm-notifications/functions/index.js",
    "content": "/**\n * Copyright 2016 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use strict';\n\nconst functions = require('firebase-functions/v1');\nconst admin = require('firebase-admin');\nadmin.initializeApp();\n\n/**\n * Triggers when a user gets a new follower and sends a notification.\n *\n * Followers add a flag to `/followers/{followedUid}/{followerUid}`.\n * Users save their device notification tokens to `/users/{followedUid}/notificationTokens/{notificationToken}`.\n */\nexports.sendFollowerNotification = functions.database.ref('/followers/{followedUid}/{followerUid}')\n    .onWrite(async (change, context) => {\n      const followerUid = context.params.followerUid;\n      const followedUid = context.params.followedUid;\n      // If un-follow we exit the function.\n      if (!change.after.val()) {\n        return functions.logger.log(\n          'User ',\n          followerUid,\n          'un-followed user',\n          followedUid\n        );\n      }\n      functions.logger.log(\n        'We have a new follower UID:',\n        followerUid,\n        'for user:',\n        followedUid\n      );\n\n      // Get the list of device notification tokens.\n      const getDeviceTokensPromise = admin.database()\n          .ref(`/users/${followedUid}/notificationTokens`).once('value');\n\n      // Get the follower profile.\n      const getFollowerProfilePromise = admin.auth().getUser(followerUid);\n\n      // The snapshot to the user's tokens.\n      let tokensSnapshot;\n\n      // The array containing all the user's tokens.\n      let tokens;\n\n      const results = await Promise.all([getDeviceTokensPromise, getFollowerProfilePromise]);\n      tokensSnapshot = results[0];\n      const follower = results[1];\n\n      // Check if there are any device tokens.\n      if (!tokensSnapshot.hasChildren()) {\n        return functions.logger.log(\n          'There are no notification tokens to send to.'\n        );\n      }\n      functions.logger.log(\n        'There are',\n        tokensSnapshot.numChildren(),\n        'tokens to send notifications to.'\n      );\n      functions.logger.log('Fetched follower profile', follower);\n\n      // Notification details.\n      const payload = {\n        notification: {\n          title: 'You have a new follower!',\n          body: `${follower.displayName} is now following you.`,\n          icon: follower.photoURL\n        }\n      };\n\n      // Listing all tokens as an array.\n      tokens = Object.keys(tokensSnapshot.val());\n      // Send notifications to all tokens.\n      const {responses} = await admin.messaging().sendEachForMulticast({\n        notification: payload.notification,\n        tokens,\n      });\n      // For each message check if there was an error.\n      const tokensToRemove = [];\n      responses.forEach((result, index) => {\n        const error = result.error;\n        if (error) {\n          functions.logger.error(\n            'Failure sending notification to',\n            tokens[index],\n            error\n          );\n          // Cleanup the tokens who are not registered anymore.\n          if (error.code === 'messaging/invalid-registration-token' ||\n              error.code === 'messaging/registration-token-not-registered') {\n            tokensToRemove.push(tokensSnapshot.ref.child(tokens[index]).remove());\n          }\n        }\n      });\n      return Promise.all(tokensToRemove);\n    });\n"
  },
  {
    "path": "Node-1st-gen/fcm-notifications/functions/package.json",
    "content": "{\n  \"name\": \"fcm-notifications-functions\",\n  \"description\": \"Send FCM notifications Firebase Functions sample\",\n  \"dependencies\": {\n    \"eslint-plugin-promise\": \"^7.2.1\",\n    \"firebase-admin\": \"^13.0.2\",\n    \"firebase-functions\": \"7.0.0\"\n  },\n  \"devDependencies\": {\n    \"eslint\": \"^8.57.1\"\n  },\n  \"scripts\": {\n    \"lint\": \"./node_modules/.bin/eslint --max-warnings=0 .\",\n    \"serve\": \"firebase emulators:start --only functions\",\n    \"shell\": \"firebase functions:shell\",\n    \"start\": \"npm run shell\",\n    \"deploy\": \"firebase deploy --only functions\",\n    \"logs\": \"firebase functions:log\",\n    \"compile\": \"cp ../../../tsconfig.template.json ./tsconfig-compile.json && tsc --project tsconfig-compile.json\"\n  },\n  \"engines\": {\n    \"node\": \"20\"\n  },\n  \"private\": true\n}\n"
  },
  {
    "path": "Node-1st-gen/fcm-notifications/public/firebase-messaging-sw.js",
    "content": "/**\n * Copyright 2023 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// Import and configure the Firebase SDK\n// These scripts are made available when the app is served or deployed on Firebase Hosting\n// If you do not serve/host your project using Firebase Hosting see https://firebase.google.com/docs/web/setup\nimportScripts('/__/firebase/9.2.0/firebase-app-compat.js');\nimportScripts('/__/firebase/9.2.0/firebase-messaging-compat.js');\nimportScripts('/__/firebase/init.js');\n\nfirebase.messaging();\n"
  },
  {
    "path": "Node-1st-gen/fcm-notifications/public/index.html",
    "content": "<!doctype html>\n<!--\n  Copyright 2016 Google Inc. All rights reserved.\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n      https://www.apache.org/licenses/LICENSE-2.0\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License\n-->\n<html lang=\"en\">\n<head>\n  <meta charset=\"utf-8\">\n  <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n  <meta name=\"description\" content=\"Demonstrates of to authorize Firebase with Instagram Auth using Firebase Functions\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n  <title>Firebase Functions demo send FCM notifications</title>\n\n  <!-- Material Design Lite -->\n  <link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/icon?family=Material+Icons\">\n  <link rel=\"stylesheet\" href=\"https://code.getmdl.io/1.1.3/material.blue_grey-orange.min.css\">\n  <script defer src=\"https://code.getmdl.io/1.1.3/material.min.js\"></script>\n\n  <link rel=\"stylesheet\" href=\"main.css\">\n</head>\n<body>\n<div class=\"demo-layout mdl-layout mdl-js-layout mdl-layout--fixed-header\">\n\n  <!-- Header section containing title -->\n  <header class=\"mdl-layout__header mdl-color-text--white mdl-color--light-blue-700\">\n    <div class=\"mdl-cell mdl-cell--12-col mdl-cell--12-col-tablet mdl-grid\">\n      <div class=\"mdl-layout__header-row mdl-cell mdl-cell--12-col mdl-cell--12-col-tablet mdl-cell--8-col-desktop\">\n        <h3>Send FCM notifications demo</h3>\n      </div>\n    </div>\n  </header>\n  <main class=\"mdl-layout__content mdl-color--grey-100\">\n    <div class=\"mdl-cell--12-col mdl-cell--12-col-tablet mdl-grid\">\n\n      <!-- Card containing the sign-in UI -->\n      <div id=\"demo-signed-out-card\" class=\"mdl-card mdl-shadow--2dp mdl-cell\">\n        <div class=\"mdl-card__supporting-text mdl-color-text--grey-600\">\n          <p>\n            This web application demonstrates how you can send FCM notifications using Functions and a web client.\n            <strong>Now sign in and activate notifications for your user!</strong>\n          </p>\n          <button id=\"demo-sign-in-button\" class=\"mdl-color-text--grey-700 mdl-button--raised mdl-button mdl-js-button mdl-js-ripple-effect\"><i class=\"material-icons\">account_circle</i> Sign in with Google</button>\n        </div>\n      </div>\n\n      <!-- Card containing the signed-in UI -->\n      <div id=\"demo-signed-in-card\" class=\"mdl-card mdl-shadow--2dp mdl-cell\">\n        <div class=\"mdl-card__supporting-text mdl-color-text--grey-600\">\n          <p>\n            Welcome <span id=\"demo-name-container\"></span>\n          </p>\n          <div id=\"demo-fcm-error-container\"></div>\n          <button id=\"demo-sign-out-button\" class=\"mdl-color-text--grey-700 mdl-button--raised mdl-button mdl-js-button mdl-js-ripple-effect\">Sign out</button>\n          <button id=\"demo-delete-button\" class=\"mdl-color-text--grey-700 mdl-button--raised mdl-button mdl-js-button mdl-js-ripple-effect\">Delete account</button>\n        </div>\n      </div>\n\n      <!-- Card containing the users to follow -->\n      <div id=\"demo-all-users-card\" class=\"mdl-card mdl-shadow--2dp mdl-cell mdl-cell--9-col\">\n        <div class=\"mdl-card__supporting-text mdl-color-text--grey-600\">\n          <p>\n            Here are all the users. If they have enabled notifications they will get a notification when you start following them.\n          </p>\n        </div>\n        <div id=\"demo-all-users-list\"></div>\n      </div>\n    </div>\n\n    <!-- Snackbar -->\n    <div id=\"demo-snackbar\" aria-live=\"assertive\" aria-atomic=\"true\" aria-relevant=\"text\" class=\"mdl-snackbar mdl-js-snackbar\">\n      <div class=\"mdl-snackbar__text\"></div>\n      <button type=\"button\" class=\"mdl-snackbar__action\"></button>\n    </div>\n  </main>\n</div>\n\n\n<!-- Import and configure the Firebase SDK -->\n<!-- These scripts are made available when the app is served or deployed on Firebase Hosting -->\n<!-- If you do not serve/host your project using Firebase Hosting see https://firebase.google.com/docs/web/setup -->\n<script src=\"/__/firebase/10.0.0/firebase-app-compat.js\"></script>\n<script src=\"/__/firebase/10.0.0/firebase-auth-compat.js\"></script>\n<script src=\"/__/firebase/10.0.0/firebase-messaging-compat.js\"></script>\n<script src=\"/__/firebase/10.0.0/firebase-database-compat.js\"></script>\n<script src=\"/__/firebase/init.js\"></script>\n\n<script src=\"main.js\"></script>\n</body>\n</html>\n"
  },
  {
    "path": "Node-1st-gen/fcm-notifications/public/main.css",
    "content": "/**\n * Copyright 2016 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nhtml, body {\n  font-family: 'Roboto', 'Helvetica', sans-serif;\n}\n.mdl-grid {\n  max-width: 1024px;\n  margin: auto;\n}\n.mdl-card {\n  min-height: 0;\n  padding-bottom: 5px;\n}\n.mdl-layout__header-row {\n  padding: 0;\n}\nh3 {\n  background: url('firebase-logo.png') no-repeat;\n  background-size: 40px;\n  padding-left: 50px;\n}\n#demo-signed-out-card,\n#demo-signed-in-card {\n  display: none;\n}\n#demo-profile-pic {\n  height: 60px;\n  width: 60px;\n  border-radius: 30px;\n  margin-left: calc(50% - 30px);\n  margin-top: 10px;\n}\n#demo-name-container {\n  font-weight: bold;\n}\n#demo-fcm-error-container {\n  margin-bottom: 20px;\n}\n#demo-delete-button {\n  margin-left: 20px;\n}\n#demo-all-users-list {\n  margin-bottom: -5px;\n}\n.demo-user-container {\n  position: relative;\n}\n.demo-user-container:HOVER {\n  background-color: #eee;\n}\n.demo-profile-pic {\n  height: 50px;\n  width: 50px;\n}\n.demo-name {\n  margin-left: 15px;\n}\n.mdl-switch {\n  display: inline-block;\n  right: 0;\n  position: absolute;\n  width: auto;\n  padding-right: 40px;\n  top: 13px;\n}\n.demo-notifications-enabled {\n  color: #aaa;\n  font-size: 80%;\n  display: none;\n}\n"
  },
  {
    "path": "Node-1st-gen/fcm-notifications/public/main.js",
    "content": "/**\n * Copyright 2016 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use strict';\n\n// Initializes the Demo.\nfunction Demo() {\n  document.addEventListener('DOMContentLoaded', function() {\n    // Shortcuts to DOM Elements.\n    this.signInButton = document.getElementById('demo-sign-in-button');\n    this.signOutButton = document.getElementById('demo-sign-out-button');\n    this.nameContainer = document.getElementById('demo-name-container');\n    this.fcmErrorContainer = document.getElementById('demo-fcm-error-container');\n    this.deleteButton = document.getElementById('demo-delete-button');\n    this.signedOutCard = document.getElementById('demo-signed-out-card');\n    this.signedInCard = document.getElementById('demo-signed-in-card');\n    this.usersContainer = document.getElementById('demo-all-users-list');\n    this.usersCard = document.getElementById('demo-all-users-card');\n    this.snackbar = document.getElementById('demo-snackbar');\n\n    // Bind events.\n    this.signInButton.addEventListener('click', this.signIn.bind(this));\n    this.signOutButton.addEventListener('click', this.signOut.bind(this));\n    this.deleteButton.addEventListener('click', this.deleteAccount.bind(this));\n    firebase.auth().onAuthStateChanged(this.onAuthStateChanged.bind(this));\n    firebase.messaging().onMessage(this.onMessage.bind(this));\n  }.bind(this));\n}\n\n// Triggered on Firebase auth state change.\nDemo.prototype.onAuthStateChanged = function(user) {\n  // If this is just an ID token refresh we exit.\n  if (user && this.currentUid === user.uid) {\n    return;\n  }\n\n  // Remove all Firebase realtime database listeners.\n  if (this.listeners) {\n    this.listeners.forEach(function(ref) {\n      ref.off();\n    });\n  }\n  this.listeners = [];\n\n  // Adjust UI depending on user state.\n  if (user) {\n    this.nameContainer.innerText = user.displayName;\n    this.signedOutCard.style.display = 'none';\n    this.signedInCard.style.display = 'block';\n    this.usersCard.style.display = 'block';\n    firebase.database().ref(`users/${user.uid}`).update({\n      displayName: user.displayName,\n      photoURL: user.photoURL\n    });\n    this.saveToken();\n    this.displayAllUsers();\n    this.currentUid = user.uid;\n  } else {\n    this.signedOutCard.style.display = 'block';\n    this.signedInCard.style.display = 'none';\n    this.usersCard.style.display = 'none';\n    this.usersContainer.innerHTML = '';\n    this.currentUid = null;\n  }\n};\n\n// Display all users so that they can be followed.\nDemo.prototype.displayAllUsers = function() {\n  var usersRef = firebase.database().ref('users');\n  usersRef.on('child_added', function(snapshot) {\n    // Create the HTML for a user.\n    var photoURL = snapshot.val().photoURL;\n    var displayName = snapshot.val().displayName;\n    var uid = snapshot.key;\n    var userTemplate =\n        '<div class=\"demo-user-container\">' +\n        '  <img class=\"demo-profile-pic\" src=\"' + photoURL + '\">' +\n        '  <span class=\"demo-name\">' + displayName + '</span>' +\n        '  <span class=\"demo-notifications-enabled\">(notifications enabled)</span>' +\n        '  <label class=\"mdl-switch mdl-js-switch mdl-js-ripple-effect\" for=\"demo-follow-switch-' + uid + '\">' +\n        '    <input type=\"checkbox\" id=\"demo-follow-switch-' + uid + '\" class=\"mdl-switch__input\">' +\n        '    <span class=\"mdl-switch__label\">Follow</span>' +\n        '  </label>' +\n        '</div>';\n\n    // Create the DOM element from the HTML.\n    var div = document.createElement('div');\n    div.innerHTML = userTemplate;\n    var userElement = div.firstChild;\n    this.usersContainer.appendChild(userElement);\n\n    // Activate the Material Design Lite Switch element.\n    var materialSwitchContainer = userElement.getElementsByClassName('mdl-switch')[0];\n    if (componentHandler) {\n      componentHandler.upgradeElement(materialSwitchContainer);\n    }\n\n    // Check if the user has notifications enabled and show a flag if he has.\n    var notificationEnabledElement = userElement.getElementsByClassName('demo-notifications-enabled')[0];\n    var notificationsEnabledRef = snapshot.ref.child('notificationTokens');\n    notificationsEnabledRef.on('value', function(notificationsEnabledSnapshot) {\n      notificationEnabledElement.style.display = notificationsEnabledSnapshot.hasChildren() ? 'inline' : 'none';\n    });\n    this.listeners.push(notificationsEnabledRef);\n\n    // Listen for the Switch state from the Realtime database.\n    var switchElement = document.getElementById('demo-follow-switch-' + uid);\n    var followUserRef = firebase.database().ref('followers/' + uid + '/' + this.currentUid);\n    this.listeners.push(followUserRef);\n    followUserRef.on('value', function(followSnapshot) {\n      switchElement.checked = !!followSnapshot.val();\n      if (materialSwitchContainer.MaterialSwitch) {\n        materialSwitchContainer.MaterialSwitch.checkDisabled();\n        materialSwitchContainer.MaterialSwitch.checkToggleState();\n      }\n    });\n\n    // Listen for switch state changes from the user.\n    switchElement.addEventListener('change', function() {\n      followUserRef.set(!!switchElement.checked);\n    });\n  }.bind(this));\n  this.listeners.push(usersRef);\n};\n\n// Initiates the sign-in flow using LinkedIn sign in in a popup.\nDemo.prototype.signIn = function() {\n  var google = new firebase.auth.GoogleAuthProvider();\n  firebase.auth().signInWithPopup(google);\n};\n\n// Signs-out of Firebase.\nDemo.prototype.signOut = function() {\n  firebase.auth().signOut();\n};\n\n// Deletes the user's account.\nDemo.prototype.deleteAccount = function() {\n  return firebase.database().ref('users/' + this.currentUid).remove().then(function() {\n    return firebase.auth().currentUser.delete().then(function() {\n      window.alert('Account deleted');\n    }).catch(function(error) {\n      if (error.code === 'auth/requires-recent-login') {\n        window.alert('You need to have recently signed-in to delete your account. Please sign-in and try again.');\n        firebase.auth().signOut();\n      }\n    });\n  });\n};\n\n// This is called when a notification is received while the app is in focus.\n// When the app is not in focus or if the tab is closed, this function is not called and the FCM notifications is\n// handled by the Service worker which displays a browser popup notification if the browser supports it.\nDemo.prototype.onMessage = function(payload) {\n  console.log('Notifications received.', payload);\n\n  // Normally our Function sends a notification payload, we check just in case.\n  if (payload.notification) {\n    // If notifications are supported on this browser we display one.\n    // Note: This is for demo purposes only. For a good user experience it is not recommended to display browser\n    // notifications while the app is in focus. In a production app you probably want to only display some form of\n    // in-app notifications like the snackbar (see below).\n    if (window.Notification instanceof Function) {\n      // This displays a notification if notifications have been granted.\n      new Notification(payload.notification.title, payload.notification);\n    }\n    // Display the notification content in the Snackbar too.\n    this.snackbar.MaterialSnackbar.showSnackbar({message: payload.notification.body});\n  }\n};\n\n// Saves the token to the database if available. If not request permissions.\nDemo.prototype.saveToken = function() {\n  firebase.messaging().getToken().then(function(currentToken) {\n    if (currentToken) {\n      firebase.database().ref('users/' + this.currentUid + '/notificationTokens/' + currentToken).set(true);\n    } else {\n      this.requestPermission();\n    }\n  }.bind(this)).catch(function(err) {\n    console.error('Unable to get messaging token.', err);\n    if (err.code === 'messaging/permission-default') {\n      this.fcmErrorContainer.innerText = 'You have not enabled notifications on this browser. To enable notifications reload the page and allow notifications using the permission dialog.';\n    } else if (err.code === 'messaging/notifications-blocked') {\n      this.fcmErrorContainer.innerHTML = 'You have blocked notifications on this browser. To enable notifications follow these instructions: <a href=\"https://support.google.com/chrome/answer/114662?visit_id=1-636150657126357237-2267048771&rd=1&co=GENIE.Platform%3DAndroid&oco=1\">Android Chrome Instructions</a><a href=\"https://support.google.com/chrome/answer/6148059\">Desktop Chrome Instructions</a>';\n    }\n  }.bind(this));\n};\n\n// Requests permission to send notifications on this browser.\nDemo.prototype.requestPermission = function() {\n  console.log('Requesting permission...');\n  firebase.messaging().requestPermission().then(function() {\n    console.log('Notification permission granted.');\n    this.saveToken();\n  }.bind(this)).catch(function(err) {\n    console.error('Unable to get permission to notify.', err);\n  });\n};\n\n// Load the demo.\nwindow.demo = new Demo();\n"
  },
  {
    "path": "Node-1st-gen/ffmpeg-convert-audio/README.md",
    "content": "# Automatically convert audio files using ffmpeg\n\nThis sample uses ffmpeg / fluent-ffmpeg and automatically converts audio files that are uploaded to Cloud Storage to FLAC file format with mono-channel audio @ 16000hz.\n\n## Functions Code\n\nSee file [functions/index.js](functions/index.js) for the audio conversion code.\n\nThe audio conversion is performed using ffmpeg. The audio is first downloaded locally from the Cloud Storage bucket to the `tmp` folder using the [google-cloud](https://github.com/GoogleCloudPlatform/google-cloud-node) SDK.\n\nThe dependencies are listed in [functions/package.json](functions/package.json).\n\n## Trigger rules\n\nThe function triggers on upload of any file to your Firebase project's default Cloud Storage bucket.\n\n## Deploy and test\n\nTo deploy and test the sample:\n\n- Create a Firebase project on the [Firebase Console](https://console.firebase.google.com) and visit the **Storage** tab.\n- Get the code, for instance using `git clone https://github.com/firebase/functions-samples`\n- Enter the correct directory `cd functions-samples/ffmpeg-convert-audio`\n- Set up the CLI to use your Firebase project using `firebase use --add` and select your Firebase project\n- Deploy your project's code using `firebase deploy`\n- Go to the Firebase Console **Storage** tab and upload an audio. After a short time a converted audio with the same name but a `_output.flac` suffix will be created in the same folder (make sure you refresh the UI to see the new file).\n\n## Notes\n\n- Take into account that the audio files produced should not exceed the size of the memory of your function.\n- The audio conversion could take a certain amount of time, increase the timeout of your function using the cloud functions webgui so the function can run for a longer time.\n"
  },
  {
    "path": "Node-1st-gen/ffmpeg-convert-audio/firebase.json",
    "content": "{\n    \"functions\": {\n        \"codebase\": \"ffmpeg-convert-audio\"\n    }\n}"
  },
  {
    "path": "Node-1st-gen/ffmpeg-convert-audio/functions/.eslintrc.json",
    "content": "{\n  \"parserOptions\": {\n    // Required for certain syntax usages\n    \"ecmaVersion\": 2022\n  },\n  \"plugins\": [\n    \"promise\"\n  ],\n  \"extends\": \"eslint:recommended\",\n  \"rules\": {\n    // Removed rule \"disallow the use of console\" from recommended eslint rules\n    \"no-console\": \"off\",\n\n    // Removed rule \"disallow multiple spaces in regular expressions\" from recommended eslint rules\n    \"no-regex-spaces\": \"off\",\n\n    // Removed rule \"disallow the use of debugger\" from recommended eslint rules\n    \"no-debugger\": \"off\",\n\n    // Removed rule \"disallow unused variables\" from recommended eslint rules\n    \"no-unused-vars\": \"off\",\n\n    // Removed rule \"disallow mixed spaces and tabs for indentation\" from recommended eslint rules\n    \"no-mixed-spaces-and-tabs\": \"off\",\n\n    // Removed rule \"disallow the use of undeclared variables unless mentioned in /*global */ comments\" from recommended eslint rules\n    \"no-undef\": \"off\",\n\n    // Warn against template literal placeholder syntax in regular strings\n    \"no-template-curly-in-string\": 1,\n\n    // Warn if return statements do not either always or never specify values\n    \"consistent-return\": 1,\n\n    // Warn if no return statements in callbacks of array methods\n    \"array-callback-return\": 1,\n\n    // Requre the use of === and !==\n    \"eqeqeq\": 2,\n\n    // Disallow the use of alert, confirm, and prompt\n    \"no-alert\": 2,\n\n    // Disallow the use of arguments.caller or arguments.callee\n    \"no-caller\": 2,\n\n    // Disallow null comparisons without type-checking operators\n    \"no-eq-null\": 2,\n\n    // Disallow the use of eval()\n    \"no-eval\": 2,\n\n    // Warn against extending native types\n    \"no-extend-native\": 1,\n\n    // Warn against unnecessary calls to .bind()\n    \"no-extra-bind\": 1,\n\n    // Warn against unnecessary labels    \n    \"no-extra-label\": 1,\n\n    // Disallow leading or trailing decimal points in numeric literals\n    \"no-floating-decimal\": 2,\n\n    // Warn against shorthand type conversions\n    \"no-implicit-coercion\": 1,\n\n    // Warn against function declarations and expressions inside loop statements\n    \"no-loop-func\": 1,\n\n    // Disallow new operators with the Function object\n    \"no-new-func\": 2,\n\n    // Warn against new operators with the String, Number, and Boolean objects\n    \"no-new-wrappers\": 1,\n\n    // Disallow throwing literals as exceptions\n    \"no-throw-literal\": 2,\n\n    // Require using Error objects as Promise rejection reasons\n    \"prefer-promise-reject-errors\": 2,\n\n    // Enforce “for” loop update clause moving the counter in the right direction\n    \"for-direction\": 2,\n\n    // Enforce return statements in getters\n    \"getter-return\": 2,\n\n    // Disallow await inside of loops\n    \"no-await-in-loop\": 2,\n\n    // Disallow comparing against -0\n    \"no-compare-neg-zero\": 2,\n\n    // Warn against catch clause parameters from shadowing variables in the outer scope\n    \"no-catch-shadow\": 1,\n\n    // Disallow identifiers from shadowing restricted names\n    \"no-shadow-restricted-names\": 2,\n\n    // Enforce return statements in callbacks of array methods\n    \"callback-return\": 2,\n\n    // Require error handling in callbacks\n    \"handle-callback-err\": 2,\n\n    // Warn against string concatenation with __dirname and __filename\n    \"no-path-concat\": 1,\n\n    // Prefer using arrow functions for callbacks\n    \"prefer-arrow-callback\": 1,\n\n    // Return inside each then() to create readable and reusable Promise chains.\n    \"promise/always-return\": 2,\n\n    //Enforces the use of catch() on un-returned promises\n    \"promise/catch-or-return\": 2,\n\n    // Warn against nested then() or catch() statements\n    \"promise/no-nesting\": 1\n  }\n}\n"
  },
  {
    "path": "Node-1st-gen/ffmpeg-convert-audio/functions/index.js",
    "content": "/**\n * Copyright 2017 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use strict';\n\nconst functions = require('firebase-functions/v1');\nconst { Storage } = require('@google-cloud/storage');\nconst path = require('path');\nconst os = require('os');\nconst fs = require('fs');\nconst ffmpeg = require('fluent-ffmpeg');\nconst ffmpeg_static = require('ffmpeg-static');\n\nconst gcs = new Storage();\n\n// Makes an ffmpeg command return a promise.\nfunction promisifyCommand(command) {\n  return new Promise((resolve, reject) => {\n    command.on('end', resolve).on('error', reject).run();\n  });\n}\n\n/**\n * When an audio is uploaded in the Storage bucket We generate a mono channel audio automatically using\n * node-fluent-ffmpeg.\n */\nexports.generateMonoAudio = functions.storage.object().onFinalize(async (object) => {\n  const fileBucket = object.bucket; // The Storage bucket that contains the file.\n  const filePath = object.name; // File path in the bucket.\n  const contentType = object.contentType; // File content type.\n\n  // Exit if this is triggered on a file that is not an audio.\n  if (!contentType.startsWith('audio/')) {\n    functions.logger.log('This is not an audio.');\n    return null;\n  }\n\n  // Get the file name.\n  const fileName = path.basename(filePath);\n  // Exit if the audio is already converted.\n  if (fileName.endsWith('_output.flac')) {\n    functions.logger.log('Already a converted audio.');\n    return null;\n  }\n\n  // Download file from bucket.\n  const bucket = gcs.bucket(fileBucket);\n  const tempFilePath = path.join(os.tmpdir(), fileName);\n  // We add a '_output.flac' suffix to target audio file name. That's where we'll upload the converted audio.\n  const targetTempFileName = fileName.replace(/\\.[^/.]+$/, '') + '_output.flac';\n  const targetTempFilePath = path.join(os.tmpdir(), targetTempFileName);\n  const targetStorageFilePath = path.join(path.dirname(filePath), targetTempFileName);\n\n  await bucket.file(filePath).download({destination: tempFilePath});\n  functions.logger.log('Audio downloaded locally to', tempFilePath);\n  // Convert the audio to mono channel using FFMPEG.\n\n  let command = ffmpeg(tempFilePath)\n      .setFfmpegPath(ffmpeg_static)\n      .audioChannels(1)\n      .audioFrequency(16000)\n      .format('flac')\n      .output(targetTempFilePath);\n\n  await promisifyCommand(command);\n  functions.logger.log('Output audio created at', targetTempFilePath);\n  // Uploading the audio.\n  await bucket.upload(targetTempFilePath, {destination: targetStorageFilePath});\n  functions.logger.log('Output audio uploaded to', targetStorageFilePath);\n\n  // Once the audio has been uploaded delete the local file to free up disk space.\n  fs.unlinkSync(tempFilePath);\n  fs.unlinkSync(targetTempFilePath);\n\n  return functions.logger.log('Temporary files removed.', targetTempFilePath);\n});\n"
  },
  {
    "path": "Node-1st-gen/ffmpeg-convert-audio/functions/package.json",
    "content": "{\n  \"name\": \"ffmpeg-convert-audio\",\n  \"description\": \"FFMPEG Convert Audio Firebase Functions sample\",\n  \"dependencies\": {\n    \"@google-cloud/storage\": \"^4.7.2\",\n    \"eslint-plugin-promise\": \"^7.2.1\",\n    \"ffmpeg-static\": \"^4.4.1\",\n    \"firebase-admin\": \"^13.0.2\",\n    \"firebase-functions\": \"7.0.0\",\n    \"fluent-ffmpeg\": \"^2.1.2\"\n  },\n  \"devDependencies\": {\n    \"eslint\": \"^8.57.1\"\n  },\n  \"scripts\": {\n    \"lint\": \"./node_modules/.bin/eslint --max-warnings=0 .\",\n    \"serve\": \"firebase emulators:start --only functions\",\n    \"shell\": \"firebase functions:shell\",\n    \"start\": \"npm run shell\",\n    \"deploy\": \"firebase deploy --only functions\",\n    \"logs\": \"firebase functions:log\",\n    \"compile\": \"cp ../../../tsconfig.template.json ./tsconfig-compile.json && tsc --project tsconfig-compile.json\"\n  },\n  \"engines\": {\n    \"node\": \"20\"\n  },\n  \"private\": true\n}\n"
  },
  {
    "path": "Node-1st-gen/fulltext-search/README.md",
    "content": "# Full-text search via Algolia\n\nThis template shows how to enable full text search on firebase database elements by using an Algolia hosted search service.\n\n## Functions Code\n\nSee file [functions/index.js](functions/index.js) for the code.\n\nThe dependencies are listed in [functions/package.json](functions/package.json).\n\n## Sample Database Structure\n\nAs an example we'll be using a simple blog structure:\n\n```\n/functions-project-12345\n    /blog-posts\n        /key-123456\n            text: \"This is my first blog entry...\"\n            last_index_timestamp: 1234567890\n        /key-123457\n            text: \"This is my second blog entry...\"\n            last_index_timestamp: 1234567891\n    /search\n        /queries\n            /key-546789\n                query: \"first entry\"\n            /key-078234\n                query: \"second entry\"\n        /results\n            /key-546789\n                hits: [...\n            /key-078234\n                hits: [...\n        /last_query_timestamp: 1234567892\n```\n\nWhenever a new blog post is created or modified a Function sends the content to be indexed to the Algolia instance.\nTo perform new searches clients add the search query to the realtime database under `/search/queries/` which triggers a\nFirebase function which performs the search on the Algolia instance. The results are written under the `/search/results/`\ntree.\n\n\n## Setting up the sample\n\nCreate an Algolia account at [www.algolia.com](https://www.algolia.com/).\n\nEnable Billing on your Firebase project by switching to the Blaze plan. You need billing enabled to allow external requests. For more information have a look at the [pricing page](https://firebase.google.com/pricing/).\n\nSet the `algolia.app_id` and `algolia.api_key` Google Cloud environment variables to match the Algolia application ID and API key of your account. For this use:\n\n```bash\nfirebase functions:secrets:set ALGOLIA_APP_ID\nfirebase functions:secrets:set ALGOLIA_API_KEY\n```\n"
  },
  {
    "path": "Node-1st-gen/fulltext-search/firebase.json",
    "content": "{\n    \"functions\": {\n        \"codebase\": \"fulltext-search\"\n    }\n}"
  },
  {
    "path": "Node-1st-gen/fulltext-search/functions/.eslintrc.json",
    "content": "{\n  \"parserOptions\": {\n    // Required for certain syntax usages\n    \"ecmaVersion\": 2022\n  },\n  \"plugins\": [\n    \"promise\"\n  ],\n  \"extends\": \"eslint:recommended\",\n  \"rules\": {\n    // Removed rule \"disallow the use of console\" from recommended eslint rules\n    \"no-console\": \"off\",\n\n    // Removed rule \"disallow multiple spaces in regular expressions\" from recommended eslint rules\n    \"no-regex-spaces\": \"off\",\n\n    // Removed rule \"disallow the use of debugger\" from recommended eslint rules\n    \"no-debugger\": \"off\",\n\n    // Removed rule \"disallow unused variables\" from recommended eslint rules\n    \"no-unused-vars\": \"off\",\n\n    // Removed rule \"disallow mixed spaces and tabs for indentation\" from recommended eslint rules\n    \"no-mixed-spaces-and-tabs\": \"off\",\n\n    // Removed rule \"disallow the use of undeclared variables unless mentioned in /*global */ comments\" from recommended eslint rules\n    \"no-undef\": \"off\",\n\n    // Warn against template literal placeholder syntax in regular strings\n    \"no-template-curly-in-string\": 1,\n\n    // Warn if return statements do not either always or never specify values\n    \"consistent-return\": 1,\n\n    // Warn if no return statements in callbacks of array methods\n    \"array-callback-return\": 1,\n\n    // Requre the use of === and !==\n    \"eqeqeq\": 2,\n\n    // Disallow the use of alert, confirm, and prompt\n    \"no-alert\": 2,\n\n    // Disallow the use of arguments.caller or arguments.callee\n    \"no-caller\": 2,\n\n    // Disallow null comparisons without type-checking operators\n    \"no-eq-null\": 2,\n\n    // Disallow the use of eval()\n    \"no-eval\": 2,\n\n    // Warn against extending native types\n    \"no-extend-native\": 1,\n\n    // Warn against unnecessary calls to .bind()\n    \"no-extra-bind\": 1,\n\n    // Warn against unnecessary labels    \n    \"no-extra-label\": 1,\n\n    // Disallow leading or trailing decimal points in numeric literals\n    \"no-floating-decimal\": 2,\n\n    // Warn against shorthand type conversions\n    \"no-implicit-coercion\": 1,\n\n    // Warn against function declarations and expressions inside loop statements\n    \"no-loop-func\": 1,\n\n    // Disallow new operators with the Function object\n    \"no-new-func\": 2,\n\n    // Warn against new operators with the String, Number, and Boolean objects\n    \"no-new-wrappers\": 1,\n\n    // Disallow throwing literals as exceptions\n    \"no-throw-literal\": 2,\n\n    // Require using Error objects as Promise rejection reasons\n    \"prefer-promise-reject-errors\": 2,\n\n    // Enforce “for” loop update clause moving the counter in the right direction\n    \"for-direction\": 2,\n\n    // Enforce return statements in getters\n    \"getter-return\": 2,\n\n    // Disallow await inside of loops\n    \"no-await-in-loop\": 2,\n\n    // Disallow comparing against -0\n    \"no-compare-neg-zero\": 2,\n\n    // Warn against catch clause parameters from shadowing variables in the outer scope\n    \"no-catch-shadow\": 1,\n\n    // Disallow identifiers from shadowing restricted names\n    \"no-shadow-restricted-names\": 2,\n\n    // Enforce return statements in callbacks of array methods\n    \"callback-return\": 2,\n\n    // Require error handling in callbacks\n    \"handle-callback-err\": 2,\n\n    // Warn against string concatenation with __dirname and __filename\n    \"no-path-concat\": 1,\n\n    // Prefer using arrow functions for callbacks\n    \"prefer-arrow-callback\": 1,\n\n    // Return inside each then() to create readable and reusable Promise chains.\n    \"promise/always-return\": 2,\n\n    //Enforces the use of catch() on un-returned promises\n    \"promise/catch-or-return\": 2,\n\n    // Warn against nested then() or catch() statements\n    \"promise/no-nesting\": 1\n  }\n}\n"
  },
  {
    "path": "Node-1st-gen/fulltext-search/functions/index.js",
    "content": "/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use strict';\n\nconst functions = require('firebase-functions/v1');\nconst {onInit} = require('firebase-functions/v1/init');\nconst {defineSecret} = require('firebase-functions/params');\nconst admin = require('firebase-admin');\nadmin.initializeApp();\n\n// Authenticate to Algolia Database.\n// TODO: Make sure you configure the `ALGOLIA_APP_ID` and `ALGOLIA_API_KEY` secrets.\nconst algoliasearch = require('algoliasearch').default;\nconst algoliaAppId = defineSecret('ALGOLIA_APP_ID');\nconst algoliaApiKey = defineSecret('ALGOLIA_API_KEY');\n\nlet client;\nonInit(() => {\n  client = algoliasearch(algoliaAppId.value(), algoliaApiKey.value());\n});\n\n// Name fo the algolia index for Blog posts content.\nconst ALGOLIA_POSTS_INDEX_NAME = 'blogposts';\n\n// Updates the search index when new blog entries are created or updated.\nexports.indexentry = functions.runWith({secrets: [algoliaAppId, algoliaApiKey]}).database.ref('/blog-posts/{blogid}/text').onWrite(\n    async (data, context) => {\n      const index = client.initIndex(ALGOLIA_POSTS_INDEX_NAME);\n      const firebaseObject = {\n        text: data.after.val(),\n        objectID: context.params.blogid\n      };\n\n      await index.saveObject(firebaseObject);\n      return data.after.ref.parent.child('last_index_timestamp').set(Date.parse(context.timestamp));\n    });\n\n// Starts a search query whenever a query is requested (by adding one to the `/search/queries`\n// element. Search results are then written under `/search/results`.\nexports.searchentry = functions.runWith({secrets: [algoliaAppId, algoliaApiKey]}).database.ref('/search/queries/{queryid}').onCreate(\n    async (snap, context) => {\n      const index = client.initIndex(ALGOLIA_POSTS_INDEX_NAME);\n\n      const query = snap.val().query;\n      const key = snap.key;\n\n      const content = await index.search(query);\n      const updates = {\n        '/search/last_query_timestamp': Date.parse(context.timestamp),\n      };\n      updates[`/search/results/${key}`] = content;\n      return admin.database().ref().update(updates);\n    });\n"
  },
  {
    "path": "Node-1st-gen/fulltext-search/functions/package.json",
    "content": "{\n  \"name\": \"fulltext-search-functions\",\n  \"description\": \"Full Text Search with Algolia Firebase Functions sample\",\n  \"dependencies\": {\n    \"algoliasearch\": \"^4.14.2\",\n    \"eslint-plugin-promise\": \"^7.2.1\",\n    \"firebase-admin\": \"^13.0.2\",\n    \"firebase-functions\": \"7.0.0\"\n  },\n  \"devDependencies\": {\n    \"eslint\": \"^8.57.1\"\n  },\n  \"scripts\": {\n    \"lint\": \"./node_modules/.bin/eslint --max-warnings=0 .\",\n    \"serve\": \"firebase emulators:start --only functions\",\n    \"shell\": \"firebase functions:shell\",\n    \"start\": \"npm run shell\",\n    \"deploy\": \"firebase deploy --only functions\",\n    \"logs\": \"firebase functions:log\",\n    \"compile\": \"cp ../../../tsconfig.template.json ./tsconfig-compile.json && tsc --project tsconfig-compile.json\"\n  },\n  \"engines\": {\n    \"node\": \"20\"\n  },\n  \"private\": true\n}\n"
  },
  {
    "path": "Node-1st-gen/fulltext-search-firestore/README.md",
    "content": "# Full Text search\n\nThis template shows how to enable full text search on Firestore documents by using one of the followning hosted search services:\n\n  * [Algolia](https://algolia.com)\n  * [Elastic](https://elastic.co)\n  * [Typesense](https://typesense.org)\n\n## Functions Code\n\nSee file [functions/index.js](functions/index.js) for the code.\n\nThe dependencies are listed in [functions/package.json](functions/package.json).\n\n## Sample Firestore Structure\n\nAs an example we'll be using a secure note structure:\n\n```\n/notes\n    /note-123456\n        text: \"This is my first note...\",\n        owner: \"FIREBASE_USER_ID\"\n    /note-123457\n        text: \"This is my second note entry...\",\n        owner: \"FIREBASE_USER_ID\"\n        tags: [\"some_category\"]\n```\n\nWhenever a new note is created or modified a Function sends the content to be indexed.\n\n## Setting up the sample\n\nFor setup and overview, please see the [Full Text Search Solution](https://firebase.google.com/docs/firestore/solutions/search) in the\nFirestore documentation.\n"
  },
  {
    "path": "Node-1st-gen/fulltext-search-firestore/firebase.json",
    "content": "{\n    \"functions\": {\n        \"codebase\": \"fulltext-search-firestore\"\n    },\n    \"hosting\": {\n        \"public\": \"public\"\n    }\n}\n"
  },
  {
    "path": "Node-1st-gen/fulltext-search-firestore/functions/.eslintrc.json",
    "content": "{\n  \"parserOptions\": {\n    // Required for certain syntax usages\n    \"ecmaVersion\": 2022\n  },\n  \"plugins\": [\n    \"promise\"\n  ],\n  \"extends\": \"eslint:recommended\",\n  \"rules\": {\n    // Removed rule \"disallow the use of console\" from recommended eslint rules\n    \"no-console\": \"off\",\n\n    // Removed rule \"disallow multiple spaces in regular expressions\" from recommended eslint rules\n    \"no-regex-spaces\": \"off\",\n\n    // Removed rule \"disallow the use of debugger\" from recommended eslint rules\n    \"no-debugger\": \"off\",\n\n    // Removed rule \"disallow unused variables\" from recommended eslint rules\n    \"no-unused-vars\": \"off\",\n\n    // Removed rule \"disallow mixed spaces and tabs for indentation\" from recommended eslint rules\n    \"no-mixed-spaces-and-tabs\": \"off\",\n\n    // Removed rule \"disallow the use of undeclared variables unless mentioned in /*global */ comments\" from recommended eslint rules\n    \"no-undef\": \"off\",\n\n    // Warn against template literal placeholder syntax in regular strings\n    \"no-template-curly-in-string\": 1,\n\n    // Warn if return statements do not either always or never specify values\n    \"consistent-return\": 1,\n\n    // Warn if no return statements in callbacks of array methods\n    \"array-callback-return\": 1,\n\n    // Requre the use of === and !==\n    \"eqeqeq\": 2,\n\n    // Disallow the use of alert, confirm, and prompt\n    \"no-alert\": 2,\n\n    // Disallow the use of arguments.caller or arguments.callee\n    \"no-caller\": 2,\n\n    // Disallow null comparisons without type-checking operators\n    \"no-eq-null\": 2,\n\n    // Disallow the use of eval()\n    \"no-eval\": 2,\n\n    // Warn against extending native types\n    \"no-extend-native\": 1,\n\n    // Warn against unnecessary calls to .bind()\n    \"no-extra-bind\": 1,\n\n    // Warn against unnecessary labels    \n    \"no-extra-label\": 1,\n\n    // Disallow leading or trailing decimal points in numeric literals\n    \"no-floating-decimal\": 2,\n\n    // Warn against shorthand type conversions\n    \"no-implicit-coercion\": 1,\n\n    // Warn against function declarations and expressions inside loop statements\n    \"no-loop-func\": 1,\n\n    // Disallow new operators with the Function object\n    \"no-new-func\": 2,\n\n    // Warn against new operators with the String, Number, and Boolean objects\n    \"no-new-wrappers\": 1,\n\n    // Disallow throwing literals as exceptions\n    \"no-throw-literal\": 2,\n\n    // Require using Error objects as Promise rejection reasons\n    \"prefer-promise-reject-errors\": 2,\n\n    // Enforce “for” loop update clause moving the counter in the right direction\n    \"for-direction\": 2,\n\n    // Enforce return statements in getters\n    \"getter-return\": 2,\n\n    // Disallow await inside of loops\n    \"no-await-in-loop\": 2,\n\n    // Disallow comparing against -0\n    \"no-compare-neg-zero\": 2,\n\n    // Warn against catch clause parameters from shadowing variables in the outer scope\n    \"no-catch-shadow\": 1,\n\n    // Disallow identifiers from shadowing restricted names\n    \"no-shadow-restricted-names\": 2,\n\n    // Enforce return statements in callbacks of array methods\n    \"callback-return\": 2,\n\n    // Require error handling in callbacks\n    \"handle-callback-err\": 2,\n\n    // Warn against string concatenation with __dirname and __filename\n    \"no-path-concat\": 1,\n\n    // Prefer using arrow functions for callbacks\n    \"prefer-arrow-callback\": 1,\n\n    // Return inside each then() to create readable and reusable Promise chains.\n    \"promise/always-return\": 2,\n\n    //Enforces the use of catch() on un-returned promises\n    \"promise/catch-or-return\": 2,\n\n    // Warn against nested then() or catch() statements\n    \"promise/no-nesting\": 1\n  }\n}\n"
  },
  {
    "path": "Node-1st-gen/fulltext-search-firestore/functions/.gitignore",
    "content": "test.js\n"
  },
  {
    "path": "Node-1st-gen/fulltext-search-firestore/functions/elastic.js",
    "content": "/**\n * Copyright 2021 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the 'License');\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an 'AS IS' BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nconst functions = require('firebase-functions/v1');\nconst {onInit} = require('firebase-functions/v1/init');\nconst {defineString, defineSecret} = require('firebase-functions/params');\n\n// [START init_elastic]\nconst { Client } = require(\"@elastic/elasticsearch\");\n\n// Initialize Elastic, requires installing Elastic dependencies:\n// https://github.com/elastic/elasticsearch-js\n//\n// ID, username, and password are stored in functions config variables\nconst elasticId = defineString('ELASTIC_ID');\nconst elasticUsername = defineString('ELASTIC_USERNAME');\nconst elasticPassword = defineSecret('ELASTIC_PASSWORD');\n\nlet client;\nonInit(() => {\n  client = new Client({\n    cloud: {\n      id: elasticId.value(),\n      username: elasticUsername.value(),\n      password: elasticPassword.value(),\n    }\n  });\n});\n// [END init_elastic]\n\n// [START update_index_function_elastic]\n// Update the search index every time a blog post is written.\nexports.onNoteCreated = functions.runWith({secrets: [elasticPassword]}).firestore.document('notes/{noteId}').onCreate(async (snap, context) => {\n  // Get the note document\n  const note = snap.data();\n\n  // Use the 'nodeId' path segment as the identifier for Elastic\n  const id = context.params.noteId;\n\n  // Write to the Elastic index\n  client.index({\n    index: \"notes\",\n    id,\n    body: note,\n  });\n});\n// [END update_index_function_elastic]\n\n// [START search_function_elastic]\nexports.searchNotes = functions.runWith({secrets: [elasticPassword]}).https.onCall(async (data, context) => {\n  const query = data.query;\n\n  // Search for any notes where the text field contains the query text.\n  // For more search examples see:\n  // https://www.elastic.co/guide/en/elasticsearch/client/javascript-api/current/search_examples.html\n  const searchRes = await client.search({\n    index: \"notes\",\n    body: {\n      query: {\n        query_string: {\n          query: `*${query}*`,\n          fields: [\n            \"text\"\n          ]\n        }\n      }\n    }\n  });\n\n  // Each entry will have the following properties:\n  //   _score: a score for how well the item matches the search\n  //   _source: the original item data\n  const hits = searchRes.body.hits.hits;\n\n  const notes = hits.map(h => h[\"_source\"]);\n  return {\n    notes: notes\n  };\n});\n// [END search_function_elastic]\n"
  },
  {
    "path": "Node-1st-gen/fulltext-search-firestore/functions/index.js",
    "content": "/**\n * Copyright 2017 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the 'License');\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an 'AS IS' BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nconst functions = require('firebase-functions/v1');\nconst {onInit} = require('firebase-functions/v1/init');\nconst {defineSecret} = require('firebase-functions/params');\nconst algoliasearch = require('algoliasearch').default;\n\n// [START init_algolia]\n// Initialize Algolia, requires installing Algolia dependencies:\n// https://www.algolia.com/doc/api-client/javascript/getting-started/#install\n//\n// App ID and API Key are stored in functions config variables\nconst algoliaId = defineSecret('ALGOLIA_ID');\nconst algoliaAdminKey = defineSecret('ALGOLIA_ADMIN_KEY');\nconst algoliaSearchKey = defineSecret('ALGOLIA_SEARCH_KEY');\n\nconst ALGOLIA_INDEX_NAME = 'notes';\n\nlet client;\nonInit(() => {\n  client = algoliasearch(algoliaId.value(), algoliaAdminKey.value());\n});\n// [END init_algolia]\n\n// [START update_index_function]\n// Update the search index every time a blog post is written.\nexports.onNoteCreated = functions.runWith({secrets: [algoliaId, algoliaAdminKey]}).firestore.document('notes/{noteId}').onCreate((snap, context) => {\n  // Get the note document\n  const note = snap.data();\n\n  // Add an 'objectID' field which Algolia requires\n  note.objectID = context.params.noteId;\n\n  // Write to the algolia index\n  const index = client.initIndex(ALGOLIA_INDEX_NAME);\n  return index.saveObject(note);\n});\n// [END update_index_function]\n\n// [START get_firebase_user]\nconst admin = require('firebase-admin');\nadmin.initializeApp();\n\nasync function getFirebaseUser(req, res, next) {\n  functions.logger.log('Check if request is authorized with Firebase ID token');\n\n  if (!req.headers.authorization || !req.headers.authorization.startsWith('Bearer ')) {\n    functions.logger.error(\n      'No Firebase ID token was passed as a Bearer token in the Authorization header.',\n      'Make sure you authorize your request by providing the following HTTP header:',\n      'Authorization: Bearer <Firebase ID Token>'\n    );\n    return res.sendStatus(403);\n  }\n\n  let idToken;\n  if (req.headers.authorization && req.headers.authorization.startsWith('Bearer ')) {\n    functions.logger.log(\"Found 'Authorization' header\");\n    idToken = req.headers.authorization.split('Bearer ')[1];\n  }\n\n  try {\n    const decodedIdToken = await admin.auth().verifyIdToken(idToken);\n    functions.logger.log('ID Token correctly decoded', decodedIdToken);\n    req.user = decodedIdToken;\n    return next();\n  } catch(error) {\n    functions.logger.error('Error while verifying Firebase ID token:', error);\n    return res.status(403).send('Unauthorized');\n  }\n}\n// [END get_firebase_user]\n\n// [START get_algolia_user_token]\n// This complex HTTP function will be created as an ExpressJS app:\n// https://expressjs.com/en/4x/api.html\nconst app = require('express')();\n\n// We'll enable CORS support to allow the function to be invoked\n// from our app client-side.\napp.use(require('cors')({origin: true}));\n\n// Then we'll also use a special 'getFirebaseUser' middleware which\n// verifies the Authorization header and adds a `user` field to the\n// incoming request:\n// https://gist.github.com/abeisgoat/832d6f8665454d0cd99ef08c229afb42\napp.use(getFirebaseUser);\n\n// Add a route handler to the app to generate the secured key\napp.get('/', (req, res) => {\n  // @ts-ignore\n  const uid = req.user.uid;\n\n  // Create the params object as described in the Algolia documentation:\n  // https://www.algolia.com/doc/guides/security/api-keys/#generating-api-keys\n  const params = {\n    // This filter ensures that only documents where owner == uid will be readable\n    filters: `owner:${uid}`,\n    // We also proxy the uid as a unique token for this key.\n    userToken: uid,\n  };\n\n  // Call the Algolia API to generate a unique key based on our search key\n  const key = client.generateSecuredApiKey(algoliaSearchKey.value(), params);\n\n  // Then return this key as {key: '...key'}\n  res.json({key});\n});\n\n// Finally, pass our ExpressJS app to Cloud Functions as a function\n// called 'getSearchKey';\nexports.getSearchKey = functions.runWith({secrets: [algoliaId, algoliaAdminKey, algoliaSearchKey]}).https.onRequest(app);\n// [END get_algolia_user_token]\n"
  },
  {
    "path": "Node-1st-gen/fulltext-search-firestore/functions/package.json",
    "content": "{\n  \"name\": \"full-text-search\",\n  \"description\": \"Full text search for Firestore\",\n  \"dependencies\": {\n    \"@babel/runtime\": \"^7.20.1\",\n    \"@elastic/elasticsearch\": \"^7.17.0\",\n    \"algoliasearch\": \"^4.14.2\",\n    \"cors\": \"^2.8.5\",\n    \"eslint-plugin-promise\": \"^7.2.1\",\n    \"express\": \"^4.18.2\",\n    \"firebase-admin\": \"^13.0.2\",\n    \"firebase-functions\": \"7.0.0\",\n    \"typesense\": \"^0.13.0\"\n  },\n  \"devDependencies\": {\n    \"eslint\": \"^8.57.1\"\n  },\n  \"scripts\": {\n    \"lint\": \"./node_modules/.bin/eslint --max-warnings=0 .\",\n    \"serve\": \"firebase emulators:start --only functions\",\n    \"shell\": \"firebase functions:shell\",\n    \"start\": \"npm run shell\",\n    \"deploy\": \"firebase deploy --only functions\",\n    \"logs\": \"firebase functions:log\",\n    \"compile\": \"cp ../../../tsconfig.template.json ./tsconfig-compile.json && tsc --project tsconfig-compile.json\"\n  },\n  \"engines\": {\n    \"node\": \"20\"\n  },\n  \"private\": true\n}\n"
  },
  {
    "path": "Node-1st-gen/fulltext-search-firestore/functions/typesense.js",
    "content": "/**\n * Copyright 2021 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the 'License');\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an 'AS IS' BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nconst functions = require('firebase-functions/v1');\nconst {onInit} = require('firebase-functions/v1/init');\nconst {defineSecret} = require('firebase-functions/params');\n\n// [START init_typesense]\n// Initialize Typesense, requires installing Typesense dependencies:\n// https://github.com/typesense/typesense-js\nconst Typesense = require(\"typesense\");\n\n// Typesense API keys are stored in functions config variables\nconst typesenseAdminApiKey = defineSecret('TYPESENSE_ADMIN_API_KEY');\nconst typesenseSearchApiKey = defineSecret('TYPESENSE_SEARCH_API_KEY');\n\nlet client;\nonInit(() => {\n  client = new Typesense.Client({\n    'nodes': [{\n      'host': 'xxx.a1.typesense.net', // where xxx is the ClusterID of your Typesense Cloud cluster\n      'port': '443',\n      'protocol': 'https'\n    }],\n    'apiKey': typesenseAdminApiKey.value(),\n    'connectionTimeoutSeconds': 2\n  });\n});\n// [END init_typesense]\n\n// [START update_index_function_typesense]\n// Update the search index every time a blog post is written.\nexports.onNoteWritten = functions.runWith({secrets: [typesenseAdminApiKey]}).firestore.document('notes/{noteId}').onWrite(async (snap, context) => {\n  // Use the 'nodeId' path segment as the identifier for Typesense\n  const id = context.params.noteId;\n\n  // If the note is deleted, delete the note from the Typesense index\n  if (!snap.after.exists) {\n    await client.collections('notes').documents(id).delete();\n    return;\n  }\n\n  // Otherwise, create/update the note in the the Typesense index\n  const note = snap.after.data();\n  await client.collections('notes').documents().upsert({\n    id,\n    owner: note.owner,\n    text: note.text\n  });\n});\n// [END update_index_function_typesense]\n\n// [START api_key_function_typesense]\nexports.getScopedApiKey = functions.runWith({secrets: [typesenseAdminApiKey, typesenseSearchApiKey]}).https.onCall(async (data, context) => {\n  // Ensure that the user is authenticated with Firebase Auth\n  if (!(context.auth && context.auth.uid)) {\n    throw new functions.https.HttpsError('permission-denied', 'Must be signed in!');\n  }\n\n  // Generate a scoped API key which allows the user to search ONLY\n  // documents which belong to them (based on the 'owner' field).\n  const scopedApiKey = client.keys().generateScopedSearchKey(\n    typesenseSearchApiKey.value(),\n    { \n      'filter_by': `owner:${context.auth.uid}`\n    }\n  );\n\n  return {\n    key: scopedApiKey\n  };\n});\n// [END api_key_function_typesense]\n"
  },
  {
    "path": "Node-1st-gen/fulltext-search-firestore/public/index.html",
    "content": "<!doctype html>\n<!--\n  Copyright 2016 Google Inc. All rights reserved.\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n      https://www.apache.org/licenses/LICENSE-2.0\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License\n-->\n<html> \n    <head>\n        <title>Full Text Search via Algolia and Firestore</title>\n        <style>\n            #content > * {\n                width: 600px;\n                clear: both;\n            }\n\n            #results {\n                min-height: 20px;\n                border: 1px solid #ccc;\n            }\n        </style>\n    </head>\n    <body>\n        <div id=\"content\">\n            <textarea id=\"note-text\"></textarea>\n            <button id=\"do-add-note\">Add Note to Firestore / Algolia</button>\n            <br /><br />\n            <input type=\"text\" id=\"query-text\" placeholder=\"Search your notes...\" />\n            <button id=\"do-query\">Search</button>\n            <br />\n            <h3>Results</h3>\n            <pre id=\"results\">\n\n            </pre>\n            <br />\n            <b>Make sure to follow setup steps in the \n                <a href=\"https://firebase.google.com/docs/firestore/solutions/search\">documentation</a>\n            </b><br /><br />\n            <i>\n                Open your JavaScript console for helpful errors.\n                <br /><br />\n                Indexing can take several seconds and may require a page refresh for new<br />\n                notes to appear due to Algolia's search client caching results.\n            </i>\n        </div>\n\n        <!-- Import the Algolia SDK -->\n        <script src=\"https://cdn.jsdelivr.net/algoliasearch/3/algoliasearch.min.js\"></script>\n        \n        <!-- Import and configure the Firebase SDK -->\n        <!-- These scripts are made available when the app is served or deployed on Firebase Hosting -->\n        <!-- If you do not serve/host your project using Firebase Hosting see https://firebase.google.com/docs/web/setup -->\n        <script src=\"/__/firebase/10.0.0/firebase-app-compat.js\"></script>\n        <script src=\"/__/firebase/10.0.0/firebase-auth-compat.js\"></script>\n        <script src=\"/__/firebase/10.0.0/firebase-firestore-compat.js\"></script>\n        <script src=\"/__/firebase/10.0.0/firebase-firestore-compat.js\"></script>\n        \n        <script src=\"/__/firebase/init.js\"></script>\n\n        <script src=\"./index.js\"></script>\n    </body>\n</html>\n"
  },
  {
    "path": "Node-1st-gen/fulltext-search-firestore/public/index.js",
    "content": "/**\n * Copyright 2017 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the 'License');\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an 'AS IS' BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nconst PROJECT_ID = '...'          // Required - your Firebase project ID\n\nconst ALGOLIA_APP_ID = '...';     // Required - your Algolia app ID\nconst ALGOLIA_SEARCH_KEY = '...'; // Optional - Only used for unauthenticated search\n\n// A search-only API Typesense API key. NEVER use your Admin API Key in a\n// web app. You can generate API keys using either the TypeSense Cloud console\n// or the TypeSense API.\nconst TYPESENSE_SEARCH_API_KEY = '...';\n\nfunction searchAlgoliaUnauthenticated(query) {\n\n  // [START search_index_unsecure]\n  var client = algoliasearch(ALGOLIA_APP_ID, ALGOLIA_SEARCH_KEY);\n  var index = client.initIndex('notes');\n\n  // Perform an Algolia search:\n  // https://www.algolia.com/doc/api-reference/api-methods/search/\n  return index\n    .search({\n      query\n    })\n    .then(function(responses) {\n      // Response from Algolia:\n      // https://www.algolia.com/doc/api-reference/api-methods/search/#response-format\n      console.log(responses.hits);\n    });\n  // [END search_index_unsecure]\n}\n\nfunction searchAlgoliaAuthenticated(query) {\n  var client;\n  var index;\n  // [START search_index_secure]\n  // Use Firebase Authentication to request the underlying token\n  return firebase.auth().currentUser.getIdToken()\n    .then(function(token) {\n      // The token is then passed to our getSearchKey Cloud Function\n      return fetch('https://us-central1-' + PROJECT_ID + '.cloudfunctions.net/getSearchKey/', {\n          headers: { Authorization: 'Bearer ' + token }\n      });\n    })\n    .then(function(response) {\n      // The Fetch API returns a stream, which we convert into a JSON object.\n      return response.json();\n    })\n    .then(function(data) {\n      // Data will contain the restricted key in the `key` field.\n      client = algoliasearch(ALGOLIA_APP_ID, data.key);\n      index = client.initIndex('notes');\n\n      // Perform the search as usual.\n      return index.search({query});\n    })\n    .then(function(responses) {\n      // Finally, use the search 'hits' returned from Algolia.\n      return responses.hits;\n    });\n  // [END search_index_secure]\n}\n\nfunction search(query) {\n  if (!PROJECT_ID) {\n    console.warn('Please set PROJECT_ID in /index.js!');\n  } else if (!ALGOLIA_APP_ID) {\n    console.warn('Please set ALGOLIA_APP_ID in /index.js!');\n  } else if (ALGOLIA_SEARCH_KEY) {\n    console.log('Performing unauthenticated search...');\n    return searchAlgoliaUnauthenticated(query);\n  } else {\n    return firebase.auth().signInAnonymously()\n      .then(function() {\n        return searchAlgoliaAuthenticated(query).catch(function(err) {\n          console.warn(err);\n        });\n      }).catch(function(err) {\n        console.warn(err);\n        console.warn('Please enable Anonymous Authentication in your Firebase Project!');\n      });\n  }\n}\n\nfunction searchElastic(query) {\n  // [START search_elastic]\n  const searchNotes = firebase.functions().httpsCallable('searchNotes');\n  searchNotes({ query: query })\n    .then((result) => {\n      const notes = result.data.notes;\n      // ...\n    });\n  // [END search_elastic]\n}\n\nasync function searchTypesenseUnauthenticated(query) {\n  // [START search_typesense_unauthenticated]\n  // Create a Typesense Client using the search-only API key\n  const client = new Typesense.Client({\n    'nodes': [{\n      'host': 'xxx.a1.typesense.net', // where xxx is the ClusterID of your Typesense Cloud cluster\n      'port': '443',\n      'protocol': 'https'\n    }],\n    'apiKey': TYPESENSE_SEARCH_API_KEY,\n    'connectionTimeoutSeconds': 2\n  });\n\n  // Search for notes with matching text\n  const searchParameters = {\n    'q': query,\n    'query_by': 'text'\n  };\n  const searchResults = await client.collections('notes')\n    .documents()\n    .search(searchParameters);\n  // ...\n  // [END search_typesense_unauthenticated]\n}\n\nasync function searchTypesenseAuthenticated(query) {\n  // [START search_typesense_authenticated]\n  // Get a scoped TypeSense API key from our Callable Function\n  const getScopedApiKey = firebase.functions().httpsCallable('getScopedApiKey');\n  const scopedApiKeyResponse = await getScopedApiKey();\n  const apiKey = scopedApiKeyResponse.data.key;\n\n  // Create a Typesense Client\n  const client = new Typesense.Client({\n    'nodes': [{\n      'host': 'xxx.a1.typesense.net', // where xxx is the ClusterID of your Typesense Cloud cluster\n      'port': '443',\n      'protocol': 'https'\n    }],\n    'apiKey': apiKey,\n    'connectionTimeoutSeconds': 2\n  });\n\n  // Search for notes with matching text\n  const searchParameters = {\n    'q': query,\n    'query_by': 'text'\n  };\n  const searchResults = await client.collections('notes')\n    .documents()\n    .search(searchParameters);\n  // ...\n  // [END search_typesense_authenticated]\n}\n\n// Other code to wire up the buttons and textboxes.\n\ndocument.querySelector('#do-add-note').addEventListener('click', function() {\n  firebase.firestore().collection('notes').add({\n    owner: [firebase.auth().currentUser.uid],\n    text: document.querySelector('#note-text').value\n  }).then(function() {\n    document.querySelector('#note-text').value = '';\n  });\n});\n\ndocument.querySelector('#do-query').addEventListener('click', function() {\n  search(document.querySelector('#query-text').value).then(function(hits) {\n    document.querySelector('#results').innerHTML = JSON.stringify(hits, null, 2);\n  });\n});\n"
  },
  {
    "path": "Node-1st-gen/github-to-slack/README.md",
    "content": "# Post GitHub commits to Slack channel.\n\nThis sample shows how to automatically post GitHub commits to a Slack channel using an HTTPS Cloud Function.\n\nFurther reading:\n - Slack Webhooks API: https://api.slack.com/incoming-webhooks\n - Firebase SDK: https://firebase.google.com/docs/functions\n - GitHub Webhooks: https://developer.github.com/webhooks/creating/\n\n## Functions Code\n\nSee file [functions/index.js](functions/index.js) for the code.\n\nThe dependencies are listed in [functions/package.json](functions/package.json).\n\n\n## Deploy and test\n\nTo test this integration:\n\n - Create a Firebase Project using the [Firebase Developer Console](https://console.firebase.google.com)\n - Enable billing on your project by switching to the Blaze plan. See [pricing](https://firebase.google.com/pricing/) for more details. This is required to be able to do requests to non-Google services.\n - Configure this sample to use your project using `firebase use --add` and select your project.\n - Install dependencies locally by running: `cd functions; npm install; cd -`\n - [Add a WebHook to your GitHub repo](https://help.github.com/articles/about-webhooks/) with the following settings:\n   - Payload URL: `https://us-central1-<FIREBASE_PROJECT_ID>.cloudfunctions.net/githubWebhook`\n   - Content type: `application/json`\n   - Secret: `A_SECRET_YOU_DEFINE`\n   - Which events would you like to trigger this webhook? `Just the push event.`\n - [Add an **Incoming Webhook**](https://my.slack.com/services/new/incoming-webhook/) to your Slack channel and take note of the **Webhook URL**.\n - Set the `slack.webhook_url` and `github.secret` Google Cloud environment variables to match the email and password of the Gmail account used to send emails. For this use:\n   ```bash\n   firebase functions:secrets:set SLACK_WEBHOOK_URL\n   firebase functions:secrets:set GITHUB_SECRET\n   ```\n - Deploy your project using `firebase deploy`\n - Push a commit to your GitHub repo\n"
  },
  {
    "path": "Node-1st-gen/github-to-slack/firebase.json",
    "content": "{\n    \"functions\": {\n        \"codebase\": \"github-to-slack\"\n    }\n}"
  },
  {
    "path": "Node-1st-gen/github-to-slack/functions/.eslintrc.json",
    "content": "{\n  \"parserOptions\": {\n    // Required for certain syntax usages\n    \"ecmaVersion\": 2022\n  },\n  \"plugins\": [\n    \"promise\"\n  ],\n  \"extends\": \"eslint:recommended\",\n  \"rules\": {\n    // Removed rule \"disallow the use of console\" from recommended eslint rules\n    \"no-console\": \"off\",\n\n    // Removed rule \"disallow multiple spaces in regular expressions\" from recommended eslint rules\n    \"no-regex-spaces\": \"off\",\n\n    // Removed rule \"disallow the use of debugger\" from recommended eslint rules\n    \"no-debugger\": \"off\",\n\n    // Removed rule \"disallow unused variables\" from recommended eslint rules\n    \"no-unused-vars\": \"off\",\n\n    // Removed rule \"disallow mixed spaces and tabs for indentation\" from recommended eslint rules\n    \"no-mixed-spaces-and-tabs\": \"off\",\n\n    // Removed rule \"disallow the use of undeclared variables unless mentioned in /*global */ comments\" from recommended eslint rules\n    \"no-undef\": \"off\",\n\n    // Warn against template literal placeholder syntax in regular strings\n    \"no-template-curly-in-string\": 1,\n\n    // Warn if return statements do not either always or never specify values\n    \"consistent-return\": 1,\n\n    // Warn if no return statements in callbacks of array methods\n    \"array-callback-return\": 1,\n\n    // Requre the use of === and !==\n    \"eqeqeq\": 2,\n\n    // Disallow the use of alert, confirm, and prompt\n    \"no-alert\": 2,\n\n    // Disallow the use of arguments.caller or arguments.callee\n    \"no-caller\": 2,\n\n    // Disallow null comparisons without type-checking operators\n    \"no-eq-null\": 2,\n\n    // Disallow the use of eval()\n    \"no-eval\": 2,\n\n    // Warn against extending native types\n    \"no-extend-native\": 1,\n\n    // Warn against unnecessary calls to .bind()\n    \"no-extra-bind\": 1,\n\n    // Warn against unnecessary labels    \n    \"no-extra-label\": 1,\n\n    // Disallow leading or trailing decimal points in numeric literals\n    \"no-floating-decimal\": 2,\n\n    // Warn against shorthand type conversions\n    \"no-implicit-coercion\": 1,\n\n    // Warn against function declarations and expressions inside loop statements\n    \"no-loop-func\": 1,\n\n    // Disallow new operators with the Function object\n    \"no-new-func\": 2,\n\n    // Warn against new operators with the String, Number, and Boolean objects\n    \"no-new-wrappers\": 1,\n\n    // Disallow throwing literals as exceptions\n    \"no-throw-literal\": 2,\n\n    // Require using Error objects as Promise rejection reasons\n    \"prefer-promise-reject-errors\": 2,\n\n    // Enforce “for” loop update clause moving the counter in the right direction\n    \"for-direction\": 2,\n\n    // Enforce return statements in getters\n    \"getter-return\": 2,\n\n    // Disallow await inside of loops\n    \"no-await-in-loop\": 2,\n\n    // Disallow comparing against -0\n    \"no-compare-neg-zero\": 2,\n\n    // Warn against catch clause parameters from shadowing variables in the outer scope\n    \"no-catch-shadow\": 1,\n\n    // Disallow identifiers from shadowing restricted names\n    \"no-shadow-restricted-names\": 2,\n\n    // Enforce return statements in callbacks of array methods\n    \"callback-return\": 2,\n\n    // Require error handling in callbacks\n    \"handle-callback-err\": 2,\n\n    // Warn against string concatenation with __dirname and __filename\n    \"no-path-concat\": 1,\n\n    // Prefer using arrow functions for callbacks\n    \"prefer-arrow-callback\": 1,\n\n    // Return inside each then() to create readable and reusable Promise chains.\n    \"promise/always-return\": 2,\n\n    //Enforces the use of catch() on un-returned promises\n    \"promise/catch-or-return\": 2,\n\n    // Warn against nested then() or catch() statements\n    \"promise/no-nesting\": 1\n  }\n}\n"
  },
  {
    "path": "Node-1st-gen/github-to-slack/functions/index.js",
    "content": "/**\n * Copyright 2016 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use strict';\n\nconst functions = require('firebase-functions/v1');\nconst {defineSecret} = require('firebase-functions/params');\nconst crypto = require('node:crypto');\nconst secureCompare = require('secure-compare');\n\nconst githubSecret = defineSecret('GITHUB_SECRET');\nconst slackWebhookUrl = defineSecret('SLACK_WEBHOOK_URL');\n\n/**\n * Webhook that will be called each time there is a new GitHub commit and will post a message to\n * Slack.\n */\nexports.githubWebhook = functions.runWith({secrets: [githubSecret, slackWebhookUrl]}).https.onRequest(async (req, res) => {\n  const cipher = 'sha1';\n  const signature = req.headers['x-hub-signature'];\n\n  // TODO: Configure the `GITHUB_SECRET` secret.\n  const hmac = crypto.createHmac(cipher, githubSecret.value())\n      .update(req.rawBody)\n      .digest('hex');\n  const expectedSignature = `${cipher}=${hmac}`;\n\n  // Check that the body of the request has been signed with the GitHub Secret.\n  if (!secureCompare(signature, expectedSignature)) {\n    functions.logger.error(\n      'x-hub-signature',\n      signature,\n      'did not match',\n      expectedSignature\n    );\n    res.status(403).send('Your x-hub-signature\\'s bad and you should feel bad!');\n    return;\n  }\n  \n  try {\n    await postToSlack(req.body.compare, req.body.commits.length, req.body.repository);\n    res.end();\n  } catch(error) {\n    functions.logger.error(error);\n    res.status(500).send('Something went wrong while posting the message to Slack.');\n  }\n});\n\n/**\n * Post a message to Slack about the new GitHub commit.\n */\nasync function postToSlack(url, commits, repo) {\n  const response = await fetch(slackWebhookUrl.value(), {\n    method: \"POST\",\n    body: JSON.stringify({\n      text: `<${url}|${commits} new commit${\n        commits > 1 ? \"s\" : \"\"\n      }> pushed to <${repo.url}|${repo.full_name}>.`,\n    }),\n    headers: { \"Content-Type\": \"application/json\" },\n  });\n  return response.json();\n}\n"
  },
  {
    "path": "Node-1st-gen/github-to-slack/functions/package.json",
    "content": "{\n  \"name\": \"github-to-slack-functions\",\n  \"description\": \"Firebase Functions that posts new GitHub commits to a Slack channel.\",\n  \"dependencies\": {\n    \"eslint-plugin-promise\": \"^7.2.1\",\n    \"firebase-admin\": \"^13.0.2\",\n    \"firebase-functions\": \"7.0.0\",\n    \"secure-compare\": \"^3.0.1\"\n  },\n  \"devDependencies\": {\n    \"eslint\": \"^8.57.1\"\n  },\n  \"scripts\": {\n    \"lint\": \"./node_modules/.bin/eslint --max-warnings=0 .\",\n    \"serve\": \"firebase emulators:start --only functions\",\n    \"shell\": \"firebase functions:shell\",\n    \"start\": \"npm run shell\",\n    \"deploy\": \"firebase deploy --only functions\",\n    \"logs\": \"firebase functions:log\",\n    \"compile\": \"cp ../../../tsconfig.template.json ./tsconfig-compile.json && tsc --project tsconfig-compile.json\"\n  },\n  \"engines\": {\n    \"node\": \"20\"\n  },\n  \"private\": true\n}\n"
  },
  {
    "path": "Node-1st-gen/google-sheet-sync/README.md",
    "content": "# Automatically sync data to a Google Sheet\n\nThis sample demonstrates how to sync new data written to a Firebase database to a Google Sheet. It includes a method for obtaining, storing, and using Oauth2 tokens for Google API access.\n\nThis code is also described in this [blog post](https://medium.com/@elon.danziger/fast-flexible-and-free-visualizing-newborn-health-data-with-firebase-nodejs-and-google-sheets-1f73465a18bc).\n\n\n## Functions Code\n\nSee file [functions/index.js](functions/index.js) for the Google Sheet sync code.\n\nUse the HTTPS function `authGoogleAPI` to request Oauth2 tokens for the Google API.\n\nThe trigger function is `appendRecordToSpreadsheet`.\n\nTo test it out, use the HTTPS function `testSheetWrite`.\n\n\n## Trigger rules\n\nThe function triggers on when data is added to the DATA_PATH of the active Firebase database.  In this sample, objects written to `DATA_PATH/{ID}` in the form `{firstColumn: value, secondColumn: value, thirdColumn: value}` are appended to the sheet.\n\n\n## Deploy and test\n\nTo deploy and test the sample:\n\n 1. Create a Firebase project on the [Firebase Console](https://console.firebase.google.com) and visit the **Storage** tab.\n 1. Clone this repo: `git clone https://github.com/firebase/functions-samples`.\n 1. Open this sample's directory: `cd functions-samples/google-sheet-sync`\n 1. Setup your project by running `firebase use --add` and select the project you had created.\n 1. Install dependencies in the functions directory: `cd functions; npm install; cd -`\n 1. Using the Google APIs Console [create an OAuth Client ID](https://console.cloud.google.com/apis/credentials/oauthclient?project=_) Click this link, select your project and then choose **Web Application**. In **Authorized redirect URIs**, you’ll need to enter `https://{YOUR-PROJECT-ID}.firebaseapp.com/oauthcallback`.\n 1. Configure your Google API client ID and secret by running:\n    ```bash\n    firebase functions:secrets:set GOOGLEAPI_CLIENT_ID\n    firebase functions:secrets:set GOOGLEAPI_CLIENT_SECRET\n    ```\n 1. Create a new Google Sheet, and copy the long string in the middle of the Sheet URL. This is the Spreadsheet ID.\n 1. Configure your Google Spreadsheet ID by running:\n    ```bash\n    Add the following configuration to your `.env` file:\n    ```\nGOOGLEAPI_SHEET_ID=\"YOUR_SPREADSHEET_ID\"\nWATCHEDPATHS_DATA_PATH=\"THE_DATA_PATH_YOU_WANT\"\n```\n    ```\n 1. Deploy your project using `firebase deploy`\n 1. Configure the app once by opening the following URL and going through the auth flow `https://{YOUR-PROJET-ID}.firebaseapp.com/authgoogleapi`\n 1. To test, go to `{YOUR_PROJET_ID}.firebaseapp.com/testsheetwrite`. This will automatically add some test data in your Firebase Realtime Database in the data path that you set in `watchedpaths.data_path`.\n 1. Check your Google Sheet, to see these same values which have been appended via the trigger function.\n"
  },
  {
    "path": "Node-1st-gen/google-sheet-sync/firebase.json",
    "content": "{\n  \"functions\": {\n    \"source\": \"functions\",\n    \"codebase\": \"google-sheet-sync\"\n  },\n  \"hosting\": {\n    \"rewrites\": [\n      {\n        \"source\": \"/oauthcallback\",\n        \"function\": \"oauthcallback\"\n      },\n      {\n        \"source\": \"/authgoogleapi\",\n        \"function\": \"authgoogleapi\"\n      },\n      {\n        \"source\": \"/testsheetwrite\",\n        \"function\": \"testsheetwrite\"\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "Node-1st-gen/google-sheet-sync/functions/.eslintrc.json",
    "content": "{\n  \"parserOptions\": {\n    // Required for certain syntax usages\n    \"ecmaVersion\": 2022\n  },\n  \"plugins\": [\n    \"promise\"\n  ],\n  \"extends\": \"eslint:recommended\",\n  \"rules\": {\n    // Removed rule \"disallow the use of console\" from recommended eslint rules\n    \"no-console\": \"off\",\n\n    // Removed rule \"disallow multiple spaces in regular expressions\" from recommended eslint rules\n    \"no-regex-spaces\": \"off\",\n\n    // Removed rule \"disallow the use of debugger\" from recommended eslint rules\n    \"no-debugger\": \"off\",\n\n    // Removed rule \"disallow unused variables\" from recommended eslint rules\n    \"no-unused-vars\": \"off\",\n\n    // Removed rule \"disallow mixed spaces and tabs for indentation\" from recommended eslint rules\n    \"no-mixed-spaces-and-tabs\": \"off\",\n\n    // Removed rule \"disallow the use of undeclared variables unless mentioned in /*global */ comments\" from recommended eslint rules\n    \"no-undef\": \"off\",\n\n    // Warn against template literal placeholder syntax in regular strings\n    \"no-template-curly-in-string\": 1,\n\n    // Warn if return statements do not either always or never specify values\n    \"consistent-return\": 1,\n\n    // Warn if no return statements in callbacks of array methods\n    \"array-callback-return\": 1,\n\n    // Requre the use of === and !==\n    \"eqeqeq\": 2,\n\n    // Disallow the use of alert, confirm, and prompt\n    \"no-alert\": 2,\n\n    // Disallow the use of arguments.caller or arguments.callee\n    \"no-caller\": 2,\n\n    // Disallow null comparisons without type-checking operators\n    \"no-eq-null\": 2,\n\n    // Disallow the use of eval()\n    \"no-eval\": 2,\n\n    // Warn against extending native types\n    \"no-extend-native\": 1,\n\n    // Warn against unnecessary calls to .bind()\n    \"no-extra-bind\": 1,\n\n    // Warn against unnecessary labels    \n    \"no-extra-label\": 1,\n\n    // Disallow leading or trailing decimal points in numeric literals\n    \"no-floating-decimal\": 2,\n\n    // Warn against shorthand type conversions\n    \"no-implicit-coercion\": 1,\n\n    // Warn against function declarations and expressions inside loop statements\n    \"no-loop-func\": 1,\n\n    // Disallow new operators with the Function object\n    \"no-new-func\": 2,\n\n    // Warn against new operators with the String, Number, and Boolean objects\n    \"no-new-wrappers\": 1,\n\n    // Disallow throwing literals as exceptions\n    \"no-throw-literal\": 2,\n\n    // Require using Error objects as Promise rejection reasons\n    \"prefer-promise-reject-errors\": 2,\n\n    // Enforce “for” loop update clause moving the counter in the right direction\n    \"for-direction\": 2,\n\n    // Enforce return statements in getters\n    \"getter-return\": 2,\n\n    // Disallow await inside of loops\n    \"no-await-in-loop\": 2,\n\n    // Disallow comparing against -0\n    \"no-compare-neg-zero\": 2,\n\n    // Warn against catch clause parameters from shadowing variables in the outer scope\n    \"no-catch-shadow\": 1,\n\n    // Disallow identifiers from shadowing restricted names\n    \"no-shadow-restricted-names\": 2,\n\n    // Enforce return statements in callbacks of array methods\n    \"callback-return\": 2,\n\n    // Require error handling in callbacks\n    \"handle-callback-err\": 2,\n\n    // Warn against string concatenation with __dirname and __filename\n    \"no-path-concat\": 1,\n\n    // Prefer using arrow functions for callbacks\n    \"prefer-arrow-callback\": 1,\n\n    // Return inside each then() to create readable and reusable Promise chains.\n    \"promise/always-return\": 2,\n\n    //Enforces the use of catch() on un-returned promises\n    \"promise/catch-or-return\": 2,\n\n    // Warn against nested then() or catch() statements\n    \"promise/no-nesting\": 1\n  }\n}\n"
  },
  {
    "path": "Node-1st-gen/google-sheet-sync/functions/index.js",
    "content": "/**\n * Copyright 2017 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use strict';\n\n// Sample trigger function that copies new Firebase data to a Google Sheet\n\nconst functions = require('firebase-functions/v1');\nconst {onInit} = require('firebase-functions/v1/init');\nconst {defineString, defineSecret} = require('firebase-functions/params');\nconst admin = require('firebase-admin');\nadmin.initializeApp();\nconst {OAuth2Client} = require('google-auth-library');\nconst {google} = require('googleapis');\n\n// TODO: Configure the `GOOGLEAPI_CLIENT_ID` and `GOOGLEAPI_CLIENT_SECRET` secrets,\n// and the `GOOGLEAPI_SHEET_ID` environment variable.\nconst googleApiClientId = defineSecret('GOOGLEAPI_CLIENT_ID');\nconst googleApiClientSecret = defineSecret('GOOGLEAPI_CLIENT_SECRET');\nconst googleApiSheetId = defineString('GOOGLEAPI_SHEET_ID');\n\n// TODO: Configure the `WATCHEDPATHS_DATA_PATH` environment variable.\nconst watchedpathsDataPath = defineString('WATCHEDPATHS_DATA_PATH');\n\n// The OAuth Callback Redirect.\nconst FUNCTIONS_REDIRECT = `https://${process.env.GCLOUD_PROJECT}.firebaseapp.com/oauthcallback`;\n\n// setup for authGoogleAPI\nconst SCOPES = ['https://www.googleapis.com/auth/spreadsheets'];\n\nlet functionsOauthClient;\nonInit(() => {\n  functionsOauthClient = new OAuth2Client(googleApiClientId.value(), googleApiClientSecret.value(),\n    FUNCTIONS_REDIRECT);\n});\n\n// OAuth token cached locally.\nlet oauthTokens = null;\n\n// visit the URL for this Function to request tokens\nexports.authgoogleapi = functions.runWith({secrets: [googleApiClientId, googleApiClientSecret]}).https.onRequest((req, res) => {\n  res.set('Cache-Control', 'private, max-age=0, s-maxage=0');\n  res.redirect(functionsOauthClient.generateAuthUrl({\n    access_type: 'offline',\n    scope: SCOPES,\n    prompt: 'consent',\n  }));\n});\n\n// setup for OauthCallback\nconst DB_TOKEN_PATH = '/api_tokens';\n\n// after you grant access, you will be redirected to the URL for this Function\n// this Function stores the tokens to your Firebase database\nexports.oauthcallback = functions.runWith({secrets: [googleApiClientId, googleApiClientSecret]}).https.onRequest(async (req, res) => {\n  res.set('Cache-Control', 'private, max-age=0, s-maxage=0');\n  const code = `${req.query.code}`;\n  try {\n    const { tokens } = await functionsOauthClient.getToken(code);\n    // Now tokens contains an access_token and an optional refresh_token. Save them.\n    await admin.database().ref(DB_TOKEN_PATH).set(tokens);\n    res.status(200).send('App successfully configured with new Credentials. '\n        + 'You can now close this page.');\n  } catch (error) {\n    res.status(400).send(error);\n  }\n});\n\n// trigger function to write to Sheet when new data comes in on watchedpathsDataPath\nexports.appendrecordtospreadsheet = functions.runWith({secrets: [googleApiClientId, googleApiClientSecret]}).database.ref('/{path}/{ITEM}').onCreate(\n    (snap, context) => {\n      if (context.params.path !== watchedpathsDataPath.value()) {\n        return null;\n      }\n      const newRecord = snap.val();\n      return appendPromise({\n        spreadsheetId: googleApiSheetId.value(),\n        range: 'A:C',\n        valueInputOption: 'USER_ENTERED',\n        insertDataOption: 'INSERT_ROWS',\n        resource: {\n          values: [[newRecord.firstColumn, newRecord.secondColumn, newRecord.thirdColumn]],\n        },\n      });\n    });\n\n// accepts an append request, returns a Promise to append it, enriching it with auth\nfunction appendPromise(requestWithoutAuth) {\n  return new Promise((resolve, reject) => {\n    return getAuthorizedClient().then((client) => {\n      const sheets = google.sheets('v4');\n      const request = requestWithoutAuth;\n      request.auth = client;\n      return sheets.spreadsheets.values.append(request, (err, response) => {\n        if (err) {\n          functions.logger.log(`The API returned an error: ${err}`);\n          return reject(err);\n        }\n        return resolve(response.data);\n      });\n    });\n  });\n}\n\n// checks if oauthTokens have been loaded into memory, and if not, retrieves them\nasync function getAuthorizedClient() {\n  if (oauthTokens) {\n    functionsOauthClient.setCredentials(oauthTokens);\n    return functionsOauthClient;\n  }\n  const snapshot = await admin.database().ref(DB_TOKEN_PATH).once('value');\n  oauthTokens = snapshot.val();\n  functionsOauthClient.setCredentials(oauthTokens);\n  return functionsOauthClient;\n}\n\n// HTTPS function to write new data to watchedpathsDataPath, for testing\nexports.testsheetwrite = functions.https.onRequest(async (req, res) => {\n  const random1 = Math.floor(Math.random() * 100);\n  const random2 = Math.floor(Math.random() * 100);\n  const random3 = Math.floor(Math.random() * 100);\n  const ID = new Date().getUTCMilliseconds();\n  await admin.database().ref(`${watchedpathsDataPath.value()}/${ID}`).set({\n    firstColumn: random1,\n    secondColumn: random2,\n    thirdColumn: random3,\n  });\n  res.send(`Wrote ${random1}, ${random2}, ${random3} to DB, trigger should now update Sheet.`);\n});\n"
  },
  {
    "path": "Node-1st-gen/google-sheet-sync/functions/package.json",
    "content": "{\n  \"name\": \"google-sheet-sync\",\n  \"description\": \"Syncs new Firebase data to a Google Sheet\",\n  \"dependencies\": {\n    \"eslint-plugin-promise\": \"^7.2.1\",\n    \"firebase-admin\": \"^13.0.2\",\n    \"firebase-functions\": \"7.0.0\",\n    \"google-auth-library\": \"^5.10.1\",\n    \"googleapis\": \"^47.0.0\"\n  },\n  \"devDependencies\": {\n    \"eslint\": \"^8.57.1\"\n  },\n  \"scripts\": {\n    \"lint\": \"./node_modules/.bin/eslint --max-warnings=0 .\",\n    \"serve\": \"firebase emulators:start --only functions\",\n    \"shell\": \"firebase functions:shell\",\n    \"start\": \"npm run shell\",\n    \"deploy\": \"firebase deploy --only functions\",\n    \"logs\": \"firebase functions:log\",\n    \"compile\": \"cp ../../../tsconfig.template.json ./tsconfig-compile.json && tsc --project tsconfig-compile.json\"\n  },\n  \"engines\": {\n    \"node\": \"20\"\n  },\n  \"private\": true\n}\n"
  },
  {
    "path": "Node-1st-gen/image-maker/.gitignore",
    "content": ".firebaserc\nnode_modules\n"
  },
  {
    "path": "Node-1st-gen/image-maker/README.md",
    "content": "# Image Maker\n\nThis sample shows how to create various images through Functions and serve it to the client\n\nIt uses [node-canvas](https://github.com/Automattic/node-canvas) to create a canvas environment on Node. That canvas is then used to create either a clock, sparkline chart, or sphere(s) in png format. The images are then cached on the server and sent to the client in `image/png` format.\n\n## Setting up the sample\n\n 1. Create a Firebase Project using the [Firebase Console](https://console.firebase.google.com).\n 1. Clone or download this repo and open the `image-maker` directory.\n 1. You must have the Firebase CLI installed. If you don't have it install it with `npm install -g firebase-tools` and then configure it with `firebase login`.\n 1. Configure the CLI locally by using `firebase use --add` and select your project in the list.\n 1. Install dependencies locally by running: `cd functions; npm install;`\n\n## Deploy and test\n\nThis sample comes with a web-based UI for testing the function.\nTo test locally do:\n\n 1. Start serving your project locally using `firebase serve --only hosting,functions`\n 1. Open the app in a browser at `https://localhost:5000`.\n\nTo deploy and test on prod do:\n\n 1. Deploy your project using `firebase deploy`\n 1. Open the app using `firebase open hosting:site`, this will open a browser.\n\n## Contributing\n\nWe'd love that you contribute to the project. Before doing so please read our [Contributor guide](../CONTRIBUTING.md).\n\n## License\n\n© Google, 2017. Licensed under an [Apache-2](../LICENSE) license.\n"
  },
  {
    "path": "Node-1st-gen/image-maker/firebase.json",
    "content": "{\n  \"functions\": {\n    \"codebase\": \"image-maker\"\n  },\n  \"hosting\": {\n    \"public\": \"public\",\n    \"rewrites\": [\n      {\"source\":\"/api/**\", \"function\":\"app\" }\n    ]\n  }\n}\n"
  },
  {
    "path": "Node-1st-gen/image-maker/functions/.eslintrc.json",
    "content": "{\n  \"parserOptions\": {\n    // Required for certain syntax usages\n    \"ecmaVersion\": 2022\n  },\n  \"plugins\": [\n    \"promise\"\n  ],\n  \"extends\": \"eslint:recommended\",\n  \"rules\": {\n    // Removed rule \"disallow the use of console\" from recommended eslint rules\n    \"no-console\": \"off\",\n\n    // Removed rule \"disallow multiple spaces in regular expressions\" from recommended eslint rules\n    \"no-regex-spaces\": \"off\",\n\n    // Removed rule \"disallow the use of debugger\" from recommended eslint rules\n    \"no-debugger\": \"off\",\n\n    // Removed rule \"disallow unused variables\" from recommended eslint rules\n    \"no-unused-vars\": \"off\",\n\n    // Removed rule \"disallow mixed spaces and tabs for indentation\" from recommended eslint rules\n    \"no-mixed-spaces-and-tabs\": \"off\",\n\n    // Removed rule \"disallow the use of undeclared variables unless mentioned in /*global */ comments\" from recommended eslint rules\n    \"no-undef\": \"off\",\n\n    // Warn against template literal placeholder syntax in regular strings\n    \"no-template-curly-in-string\": 1,\n\n    // Warn if return statements do not either always or never specify values\n    \"consistent-return\": 1,\n\n    // Warn if no return statements in callbacks of array methods\n    \"array-callback-return\": 1,\n\n    // Requre the use of === and !==\n    \"eqeqeq\": 2,\n\n    // Disallow the use of alert, confirm, and prompt\n    \"no-alert\": 2,\n\n    // Disallow the use of arguments.caller or arguments.callee\n    \"no-caller\": 2,\n\n    // Disallow null comparisons without type-checking operators\n    \"no-eq-null\": 2,\n\n    // Disallow the use of eval()\n    \"no-eval\": 2,\n\n    // Warn against extending native types\n    \"no-extend-native\": 1,\n\n    // Warn against unnecessary calls to .bind()\n    \"no-extra-bind\": 1,\n\n    // Warn against unnecessary labels    \n    \"no-extra-label\": 1,\n\n    // Disallow leading or trailing decimal points in numeric literals\n    \"no-floating-decimal\": 2,\n\n    // Warn against shorthand type conversions\n    \"no-implicit-coercion\": 1,\n\n    // Warn against function declarations and expressions inside loop statements\n    \"no-loop-func\": 1,\n\n    // Disallow new operators with the Function object\n    \"no-new-func\": 2,\n\n    // Warn against new operators with the String, Number, and Boolean objects\n    \"no-new-wrappers\": 1,\n\n    // Disallow throwing literals as exceptions\n    \"no-throw-literal\": 2,\n\n    // Require using Error objects as Promise rejection reasons\n    \"prefer-promise-reject-errors\": 2,\n\n    // Enforce “for” loop update clause moving the counter in the right direction\n    \"for-direction\": 2,\n\n    // Enforce return statements in getters\n    \"getter-return\": 2,\n\n    // Disallow await inside of loops\n    \"no-await-in-loop\": 2,\n\n    // Disallow comparing against -0\n    \"no-compare-neg-zero\": 2,\n\n    // Warn against catch clause parameters from shadowing variables in the outer scope\n    \"no-catch-shadow\": 1,\n\n    // Disallow identifiers from shadowing restricted names\n    \"no-shadow-restricted-names\": 2,\n\n    // Enforce return statements in callbacks of array methods\n    \"callback-return\": 2,\n\n    // Require error handling in callbacks\n    \"handle-callback-err\": 2,\n\n    // Warn against string concatenation with __dirname and __filename\n    \"no-path-concat\": 1,\n\n    // Prefer using arrow functions for callbacks\n    \"prefer-arrow-callback\": 1,\n\n    // Return inside each then() to create readable and reusable Promise chains.\n    \"promise/always-return\": 2,\n\n    //Enforces the use of catch() on un-returned promises\n    \"promise/catch-or-return\": 2,\n\n    // Warn against nested then() or catch() statements\n    \"promise/no-nesting\": 1\n  }\n}\n"
  },
  {
    "path": "Node-1st-gen/image-maker/functions/clock.js",
    "content": "/**\n * Copyright 2016 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nconst _ = require('lodash');\n\nconst getDefaultOpts = () => ({\n  strokes: {\n    clock: '#325FA2',\n    hour: '#000000',\n    minute: '#000000',\n    seconds: '#D40000'\n  },\n  fills: {\n    clock: '#eeeeee',\n    tip: '#555555',\n    seconds: '#D40000'\n  }\n});\n\nconst getX = (angle) => {\n  return -Math.sin(angle + Math.PI)\n};\n\nconst getY = (angle) => {\n  return Math.cos(angle + Math.PI)\n};\n\nconst clock = (ctx, colorOpts) => {\n  const colors = _.merge({}, getDefaultOpts(), colorOpts);\n  let x, y, i;\n  const now = new Date();\n\n  ctx.clearRect(0, 0, 320, 320);\n  ctx.save();\n\n  // Clock background\n  ctx.translate(160, 160);\n  ctx.beginPath();\n  ctx.lineWidth = 14;\n  ctx.strokeStyle = colors.strokes.clock;\n  ctx.fillStyle = colors.fills.clock;\n  ctx.arc(0, 0, 142, 0, Math.PI * 2, true);\n  ctx.stroke();\n  ctx.fill();\n\n  // Hour marks\n  ctx.lineWidth = 8;\n  ctx.strokeStyle = colors.strokes.hour;\n  for (i = 0; i < 12; i++) {\n    x = getX(Math.PI / 6 * i);\n    y = getY(Math.PI / 6 * i);\n    ctx.beginPath();\n    ctx.moveTo(x * 100, y * 100);\n    ctx.lineTo(x * 125, y * 125);\n    ctx.stroke();\n  }\n\n  // Minute marks\n  ctx.lineWidth = 5;\n  ctx.strokeStyle = colors.strokes.minute;\n  for (i = 0; i < 60; i++) {\n    if (i % 5 !== 0) {\n      x = getX(Math.PI / 30 * i);\n      y = getY(Math.PI / 30 * i);\n      ctx.beginPath();\n      ctx.moveTo(x * 117, y * 117);\n      ctx.lineTo(x * 125, y * 125);\n      ctx.stroke();\n    }\n  }\n\n  const sec = now.getSeconds();\n  const min = now.getMinutes();\n  const hr = now.getHours() % 12;\n\n  ctx.fillStyle = 'black';\n\n  // Write hours\n  x = getX(hr * (Math.PI / 6) + (Math.PI / 360) * min + (Math.PI / 21600) * sec);\n  y = getY(hr * (Math.PI / 6) + (Math.PI / 360) * min + (Math.PI / 21600) * sec);\n  ctx.lineWidth = 14;\n  ctx.beginPath();\n  ctx.moveTo(x * -20, y * -20);\n  ctx.lineTo(x * 80, y * 80);\n  ctx.stroke();\n\n  // Write minutes\n  x = getX((Math.PI / 30) * min + (Math.PI / 1800) * sec);\n  y = getY((Math.PI / 30) * min + (Math.PI / 1800) * sec);\n\n  ctx.lineWidth = 10;\n  ctx.beginPath();\n  ctx.moveTo(x * -28, y * -28);\n  ctx.lineTo(x * 112, y * 112);\n  ctx.stroke();\n\n  // Write seconds\n  x = getX(sec * Math.PI / 30);\n  y = getY(sec * Math.PI / 30);\n  ctx.strokeStyle = colors.strokes.seconds;\n  ctx.fillStyle = colors.fills.seconds;\n  ctx.lineWidth = 6;\n  ctx.beginPath();\n  ctx.moveTo(x * -30, y * -30);\n  ctx.lineTo(x * 83, y * 83);\n  ctx.stroke();\n  ctx.beginPath();\n  ctx.arc(0, 0, 10, 0, Math.PI * 2, true);\n  ctx.fill();\n  ctx.beginPath();\n  ctx.arc(x * 95, y * 95, 10, 0, Math.PI * 2, true);\n  ctx.stroke();\n  ctx.fillStyle = colors.fills.tip;\n  ctx.arc(0, 0, 3, 0, Math.PI * 2, true);\n  ctx.fill();\n\n  ctx.restore();\n}\n\nmodule.exports = clock;\n"
  },
  {
    "path": "Node-1st-gen/image-maker/functions/index.js",
    "content": "/**\n * Copyright 2016 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nconst functions = require('firebase-functions/v1');\nconst app = require('express')();\nconst { Canvas } = require('canvas');\nconst _ = require('lodash');\n\nconst clock = require('./clock');\nconst spark = require('./sparkline');\nconst ray = require('./ray');\n\napp.get('/api/ray', (req, res) => {\n  const tracers = JSON.parse(`${req.query.tracers}`);\n  if (!_.isArray(tracers) ||\n    !_.every(tracers, (depth) => typeof depth === 'number')) {\n    // invalid format\n    res.status(422);\n    res.end();\n  }\n  const canvas = new Canvas(243 * tracers.length, 243);\n  const ctx = canvas.getContext('2d');\n  for (let i=0; i<tracers.length; i++) {\n    ray(Math.round(27/tracers[i]), 81, ctx, {x: 243, y: 0});\n  }\n  res.set('Cache-Control', 'public, max-age=60, s-maxage=31536000');\n  res.writeHead(200, {'Content-Type': 'image/png'});\n  canvas.createPNGStream().pipe(res);\n});\n\napp.get('/api/clock', (req, res) => {\n  const colorOpts = req.query;\n  const canvas = new Canvas(320, 320);\n  const ctx = canvas.getContext('2d');\n  clock(ctx, colorOpts);\n  res.set('Cache-Control', 'public, max-age=60, s-maxage=31536000');\n  res.writeHead(200, {'Content-Type': 'image/png'});\n  canvas.createPNGStream().pipe(res);\n});\n\napp.get('/api/spark', (req, res) => {\n  const dataSeries = JSON.parse(`${req.query.series}`);\n  const colorOpts = req.query.colorOpts || {};\n  if (!_.isArray(dataSeries) || !_.every(dataSeries, (num) => typeof num === 'number')) {\n    // invalid format\n    res.status(422);\n    res.end();\n  }\n  const canvas = new Canvas(320, 100);\n  let ctx = canvas.getContext('2d');\n  spark(ctx, dataSeries, colorOpts);\n  res.set('Cache-Control', 'public, max-age=60, s-maxage=31536000');\n  res.writeHead(200, {'Content-Type': 'image/png'});\n  canvas.createPNGStream().pipe(res);\n});\n\nexports.app = functions.https.onRequest(app);\n"
  },
  {
    "path": "Node-1st-gen/image-maker/functions/package.json",
    "content": "{\n  \"name\": \"functions-image-maker\",\n  \"description\": \"Sample functions that generate images on the backend\",\n  \"dependencies\": {\n    \"canvas\": \"^3.0.1\",\n    \"eslint-plugin-promise\": \"^7.2.1\",\n    \"express\": \"^4.18.2\",\n    \"firebase-admin\": \"^13.0.2\",\n    \"firebase-functions\": \"7.0.0\",\n    \"lodash\": \"^4.17.21\"\n  },\n  \"devDependencies\": {\n    \"eslint\": \"^8.57.1\"\n  },\n  \"scripts\": {\n    \"lint\": \"./node_modules/.bin/eslint --max-warnings=0 .\",\n    \"serve\": \"firebase emulators:start --only functions\",\n    \"shell\": \"firebase functions:shell\",\n    \"start\": \"npm run shell\",\n    \"deploy\": \"firebase deploy --only functions\",\n    \"logs\": \"firebase functions:log\",\n    \"compile\": \"cp ../../../tsconfig.template.json ./tsconfig-compile.json && tsc --project tsconfig-compile.json\"\n  },\n  \"engines\": {\n    \"node\": \"20\"\n  },\n  \"private\": true\n}\n"
  },
  {
    "path": "Node-1st-gen/image-maker/functions/ray.js",
    "content": "/**\n * Copyright 2016 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n const render = (min, max, ctx, posObject) => {\n  ctx.fillStyle = getPointColor(122, 122);\n  ctx.fillRect(0, 0, 240, 240);\n  renderLevel(min, max, 0, ctx);\n  ctx.translate(posObject.x, posObject.y);\n};\n\nconst renderLevel = (minimumLevel, level, y, ctx) => {\n  let x;\n\n  for (x = 0; x < 243 / level; ++x) {\n    drawBlock(x, y, level, ctx);\n  }\n  for (x = 0; x < 243 / level; x += 3) {\n    drawBlock(x, y + 1, level, ctx);\n    drawBlock(x + 2, y + 1, level, ctx);\n  }\n  for (x = 0; x < 243 / level; ++x) {\n    drawBlock(x, y + 2, level, ctx);\n  }\n  if ((y += 3) >= 243 / level) {\n    y = 0;\n    level /= 3;\n  }\n  if (level >= minimumLevel) {\n    renderLevel(minimumLevel, level, y, ctx);\n  }\n};\n\nconst drawBlock = (x, y, level, ctx) => {\n  ctx.fillStyle = getPointColor(\n    x * level + (level - 1) / 2,\n    y * level + (level - 1) / 2\n  );\n\n  ctx.fillRect(\n    x * level,\n    y * level,\n    level,\n    level\n  );\n};\n\nconst getPointColor = (x, y) => {\n  x = x / 121.5 - 1;\n  y = -y / 121.5 + 1;\n\n  const x2y2 = x * x + y * y;\n  if (x2y2 > 1) {\n    return '#000';\n  }\n\n  const root = Math.sqrt(1 - x2y2);\n  const x3d = x * 0.7071067812 + root / 2 - y / 2;\n  const y3d = x * 0.7071067812 - root / 2 + y / 2;\n  const z3d = 0.7071067812 * root + 0.7071067812 * y;\n  let brightness = -x / 2 + root * 0.7071067812 + y / 2;\n  if (brightness < 0) brightness = 0;\n\n  const r = Math.round(brightness * 127.5 * (1 - y3d));\n  const g = Math.round(brightness * 127.5 * (x3d + 1));\n  const b = Math.round(brightness * 127.5 * (z3d + 1));\n\n  return 'rgb(' + r + ', ' + g + ', ' + b + ')';\n}\n\nmodule.exports = render;\n"
  },
  {
    "path": "Node-1st-gen/image-maker/functions/sparkline.js",
    "content": "/**\n * Copyright 2016 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nconst spark = (ctx, data, opts) => {\n  const len = data.length;\n  const pad = 1;\n  const width = ctx.canvas.width;\n  const height = ctx.canvas.height;\n  const barWidth = width / len;\n  const max = Math.max.apply(null, data);\n  ctx.fillStyle = opts.barFill || 'rgba(0,0,255,0.5)';\n  ctx.strokeStyle = opts.lineStroke || 'red';\n  ctx.lineWidth = 1;\n\n  data.forEach((n, i) => {\n    const x = i * barWidth + pad;\n    const y = height * (n / max);\n\n    ctx.lineTo(x, height - y);\n    ctx.fillRect(x, height, barWidth - pad, -y);\n  })\n\n  ctx.stroke();\n};\n\nmodule.exports = spark;\n"
  },
  {
    "path": "Node-1st-gen/image-maker/public/index.html",
    "content": "<!DOCTYPE html>\n<!--\n Copyright 2023 Google LLC\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n     http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<html lang=\"en\">\n<head>\n  <meta charset=\"utf-8\">\n  <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n  <meta name=\"description\" content=\"Demonstrates how to dynamically generate images on the server\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n  <title>Image Maker Samples</title>\n\n  <!-- Material Design Lite -->\n  <link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/icon?family=Material+Icons\">\n  <link rel=\"stylesheet\" href=\"https://code.getmdl.io/1.1.3/material.blue_grey-orange.min.css\">\n  <script defer src=\"https://code.getmdl.io/1.1.3/material.min.js\"></script>\n<style>\n    .mdl-card {\n      width: 400px;\n      margin: 10px;\n    }\n    section.row {\n      display: flex;\n      flex-direction: row;\n      width: 100%;\n      justify-content: center;\n      margin: 10px 0;\n      align-items: baseline;\n    }\n    .inline {\n      display: inline-block;\n      margin: 0 5px;\n    }\n    .image-container {\n      text-align: center;\n      display: none;\n    }\n    #ray-tracing {\n      min-width: 400px;\n      width: auto;\n    }\n  </style>\n</head>\n<body>\n<div class=\"mdl-layout mdl-js-layout mdl-layout--fixed-header\">\n    <!-- Header section containing title -->\n    <header class=\"mdl-layout__header mdl-color-text--white mdl-color--light-blue-700\">\n      <div class=\"mdl-cell mdl-cell--12-col mdl-cell--12-col-tablet mdl-grid\">\n        <div class=\"mdl-layout__header-row mdl-cell mdl-cell--12-col mdl-cell--12-col-tablet mdl-cell--8-col-desktop\">\n          <h3>Image Maker Samples</h3>\n        </div>\n      </div>\n    </header>\n    <main class=\"mdl-layout__content mdl-color--grey-100\">\n      <div class=\"mdl-cell--12-col mdl-cell--12-col-tablet mdl-grid\" style=\"align-items: baseline;\">\n        <section class='row'>\n          <div class=\"mdl-card mdl-shadow--2dp\">\n            <div class=\"mdl-card__title\">\n              <h2 class=\"mdl-card__title-text\">Clock</h2>\n            </div>\n            <div class=\"mdl-card__supporting-text\" id='clock-directions'>Generates a PNG of a clock based on the following parameters.</div>\n            <div class=\"mdl-card__supporting-text image-container\" id='clock-png'></div>\n            <div class=\"mdl-card__supporting-text\">\n              <div class=\"mdl-textfield mdl-js-textfield mdl-textfield--floating-label inline\" style=\"width:46%\">\n                <input class=\"mdl-textfield__input\" type='color' id='clock-stroke' value='#325FA2' />\n                <label class=\"mdl-textfield__label\" for=\"clock-stroke\">Clock Stroke</label>\n              </div>\n              <div class=\"mdl-textfield mdl-js-textfield mdl-textfield--floating-label inline\" style=\"width:46%\">\n                <input class=\"mdl-textfield__input\" type='color' id='clock-fill' value='#eeeeee' />\n                <label class=\"mdl-textfield__label\" for=\"clock-fill\">Clock Fill</label>\n              </div>\n              <div class=\"mdl-textfield mdl-js-textfield mdl-textfield--floating-label inline\" style=\"width:46%\">\n                <input class=\"mdl-textfield__input\" type='color' id='hour-stroke' value='#000000' />\n                <label class=\"mdl-textfield__label\" for=\"hour-stroke\">Hour Hand Stroke</label>\n              </div>\n              <div class=\"mdl-textfield mdl-js-textfield mdl-textfield--floating-label inline\" style=\"width:46%\">\n                <input class=\"mdl-textfield__input\" type='color' id='minute-stroke' value='#000000' />\n                <label class=\"mdl-textfield__label\" for=\"minute-stroke\">Minute Hand Stroke</label>\n              </div>\n              <div class=\"mdl-textfield mdl-js-textfield mdl-textfield--floating-label inline\" style=\"width:46%\">\n                <input class=\"mdl-textfield__input\" type='color' id='seconds-stroke' value='#D40000' />\n                <label class=\"mdl-textfield__label\" for=\"seconds-stroke\">Seconds Hand Stroke</label>\n              </div>\n              <div class=\"mdl-textfield mdl-js-textfield mdl-textfield--floating-label inline\" style=\"width:46%\">\n                <input class=\"mdl-textfield__input\" type='color' id='seconds-fill' value='#D40000' />\n                <label class=\"mdl-textfield__label\" for=\"seconds-fill\">Seconds Hand Fill</label>\n              </div>\n              <div class=\"mdl-textfield mdl-js-textfield mdl-textfield--floating-label inline\" style=\"width:46%\">\n                <input class=\"mdl-textfield__input\" type='color' id='seconds-tip' value='#555555' />\n                <label class=\"mdl-textfield__label\" for=\"seconds-tip\">Seconds Tip Fill</label>\n              </div>\n            </div>\n            <div class=\"mdl-card__actions mdl-card--border\">\n              <a class=\"mdl-button mdl-button--colored mdl-js-button mdl-js-ripple-effect\" onClick={generateClock()}>\n                Generate Image\n              </a>\n            </div>\n          </div>\n          <div class=\"mdl-card mdl-shadow--2dp\">\n            <div class=\"mdl-card__title\">\n              <h2 class=\"mdl-card__title-text\">Sparkline Chart</h2>\n            </div>\n            <div class=\"mdl-card__supporting-text\" id='spark-directions'>Generates a PNG of a Sparkline Chart. Input an array of integers as a sample data series.</div>\n            <div class=\"mdl-card__supporting-text image-container\" id='spark-png'></div>\n            <div class=\"mdl-card__supporting-text\">\n              <div class=\"mdl-textfield mdl-js-textfield mdl-textfield--floating-label\" style=\"width:100%\">\n                <textarea class=\"mdl-textfield__input\" type=\"text\" rows= \"1\" id=\"sampleSeries\">[1, 2, 4, 5, 10, 4, 2, 5, 4, 3, 3, 2]</textarea>\n                <label class=\"mdl-textfield__label\" for='sampleSeries'>Sample Data Series</label>\n              </div>\n              <div class=\"mdl-textfield mdl-js-textfield mdl-textfield--floating-label inline\" style=\"width:46%\">\n                <input class=\"mdl-textfield__input\" type='color' id='line-stroke' value='#D40000' />\n                <label class=\"mdl-textfield__label\" for=\"line-stroke\">Line Stroke</label>\n              </div>\n              <div class=\"mdl-textfield mdl-js-textfield mdl-textfield--floating-label inline\" style=\"width:46%\">\n                <input class=\"mdl-textfield__input\" type='color' id='bar-fill' value='#EEEEEE' />\n                <label class=\"mdl-textfield__label\" for=\"bar-fill\">Bar Fill</label>\n              </div>\n            </div>\n            <div class=\"mdl-card__actions mdl-card--border\">\n              <a class=\"mdl-button mdl-button--colored mdl-js-button mdl-js-ripple-effect\" onClick={generateSparkChart()}>\n                Generate Image\n              </a>\n            </div>\n          </div>\n        </section>\n        <section class='row'>\n          <div class=\"mdl-card mdl-shadow--2dp\" id='ray-tracing'>\n            <div class=\"mdl-card__title\">\n              <h2 class=\"mdl-card__title-text\">Ray Tracing</h2>\n            </div>\n            <div class=\"mdl-card__supporting-text\" id='ray-directions'>Generates a PNG of a sphere using a recursive ray tracing algorithm. </div>\n            <div class='image-container' id='ray-png'></div>\n            <div id=\"ray-sections\">\n              <div class=\"mdl-card__supporting-text mdl-card--border raySection\">\n                <div class=\"mdl-textfield mdl-js-textfield mdl-textfield--floating-label\" style=\"width:100%\">\n                  <input class=\"mdl-textfield__input\" type=\"number\" value=\"27\" min=\"1\" max=\"27\" id=\"depth\" />\n                  <label class=\"mdl-textfield__label\" for=\"depth\">Min Depth Level</label>\n                </div>\n              </div>\n              <div class=\"mdl-card__supporting-text mdl-card--border raySection\">\n                <div class=\"mdl-textfield mdl-js-textfield mdl-textfield--floating-label\" style=\"width:100%\">\n                  <input class=\"mdl-textfield__input\" type=\"number\" value=\"1\" min=\"1\" max=\"27\" id=\"depth\" />\n                  <label class=\"mdl-textfield__label\" for=\"depth\">Min Depth Level</label>\n                </div>\n              </div>\n            </div>\n            <div class=\"mdl-card__actions mdl-card--border\">\n              <a class=\"mdl-button mdl-button--colored mdl-js-button mdl-js-ripple-effect\" onClick={generateTracers()}>\n                Generate Image\n              </a>\n              <a class=\"mdl-button mdl-button--colored mdl-js-button mdl-js-ripple-effect\" onClick={addTracer()}>\n                Add New Tracer\n              </a>\n              <a class=\"mdl-button mdl-button--colored mdl-js-button mdl-js-ripple-effect\" onClick={removeTracer()}>\n                Remove Last Tracer\n              </a>\n            </div>\n          </div>\n        </section>\n      </div>\n    </main>\n  </div>\n  <script\n    src=\"https://code.jquery.com/jquery-2.2.4.min.js\"\n    integrity=\"sha256-BbhdlvQf/xTY9gja0Dq3HiwQF8LaCRTXxZKRutelT44=\"\n    crossorigin=\"anonymous\">\n  </script>\n  <script>\n    function updateImage(divId, imageUrl) {\n      $(`#${divId}-png`).html(`<div class='loading'>generating...</div><img src='${imageUrl}'/>`);\n      $(`#${divId}-png img`).on('load', function() {\n        $(`#${divId}-png .loading`).remove();\n      });\n      $(`#${divId}-png`).css('display','block');\n      $(`#${divId}-directions`).css('display','hidden');\n    }\n    function addTracer() {\n      $('#ray-sections').append(`<div class=\"mdl-card__supporting-text mdl-card--border raySection\">\n        <div class=\"mdl-textfield mdl-js-textfield mdl-textfield--floating-label\" style=\"width:100%\">\n          <input class=\"mdl-textfield__input\" type=\"number\" value=\"13\" min=\"1\" max=\"27\" id=\"depth\" />\n          <label class=\"mdl-textfield__label\" for=\"depth\">Min Depth Level</label>\n        </div>\n      </div>`);\n      componentHandler.upgradeElements(document.getElementById('ray-sections'));\n    }\n    function removeTracer() {\n      if ($('#ray-sections').children().length > 1) {\n        $('#ray-sections').children().last().remove();\n      }\n    }\n    function generateTracers() {\n      const tracers = $('.raySection').map(function(ind, elm) {\n        return parseInt($(elm).find(\"input#depth\").val())\n      });\n      // construct data obj\n      const data = {\n        tracers: JSON.stringify(tracers.toArray())\n      };\n      // construct URL\n      const url = `/api/ray?${$.param(data)}`;\n      // update the img-container\n      updateImage('ray', url);\n    }\n    function generateSparkChart() {\n      // construct data obj\n      const data = {\n        series: $('#sampleSeries').val(),\n        colorOpts: {\n          lineStroke: $('#line-stroke').val(),\n          barFill: $('#bar-fill').val()\n        }\n      };\n      // construct URL\n      const url = `/api/spark?${$.param(data)}`;\n      // update the img-container\n      updateImage('spark', url);\n    }\n    function generateClock() {\n      // construct data obj\n      const data = {\n        strokes: {\n          clock: $('#clock-stroke').val(),\n          minute: $('#hour-stroke').val(),\n          hour: $('#minute-stroke').val(),\n          seconds: $('#seconds-stroke').val()\n        },\n        fills: {\n          clock: $('#clock-fill').val(),\n          tip: $('#seconds-tip').val(),\n          seconds: $('#seconds-fill').val()\n        }\n      };\n      // construct URL\n      const url = `/api/clock?${$.param(data)}`;\n      // update the img-container\n      updateImage('clock', url);\n    }\n  </script>\n</body>\n</html>\n"
  },
  {
    "path": "Node-1st-gen/instagram-auth/.gitignore",
    "content": "service-account.json\n"
  },
  {
    "path": "Node-1st-gen/instagram-auth/README.md",
    "content": "# Use Instagram Sign In with Firebase\n\nThis sample shows how to authenticate using Instagram Sign-In on Firebase. In this sample we use OAuth 2.0 based authentication to get Instagram user information then create a Firebase Custom Token (using the Instagram user ID).\n\n\n## Setup the sample\n\nCreate and setup the Firebase project:\n 1. Create a Firebase project using the [Firebase Developer Console](https://console.firebase.google.com).\n 1. Enable Billing on your Firebase the project by switching to the **Blaze** plan, this is currently needed to be able to perform HTTP requests to external services from a Cloud Function.\n\nCreate and provide a Service Account's credentials:\n 1. Create a Service Accounts file as described in the [Server SDK setup instructions](https://firebase.google.com/docs/server/setup#add_firebase_to_your_app).\n 1. Save the Service Account credential file as `./functions/service-account.json`\n\nCreate and setup your Instagram app:\n 1. Register an Instagram app on [Instagram for Developers](https://www.instagram.com/developer/). You'll need to **Register a New Client**.\n 1. Once Your app is created make sure you specify your app's callback URL in the list of **Valid redirect URIs** of your Instagram app. You should whitelist `https://localhost:5000/popup.html` for local development and if you deploy on App Engine (See Deploy section below) you should whitelist the URL `https://<application-id>.firebaseapp.com/popup.html`.\n 1. Copy the **Client ID** and **Client Secret** of your Instagram app and use them to set the `instagram.client_id` and `instagram.client_secret` Google Cloud environment variables. For this use:\n\n    ```bash\n    firebase functions:secrets:set INSTAGRAM_CLIENT_ID\n    firebase functions:secrets:set INSTAGRAM_CLIENT_SECRET\n    ```\n\n > Make sure the Instagram Client Secret is always kept secret. For instance do not save it in your version control system.\n\nDeploy your project:\n 1. Run `firebase use --add` and choose your Firebase project. This will configure the Firebase CLI to use the correct project locally.\n 1. Run `firebase deploy` to effectively deploy the sample. The first time the Functions are deployed the process can take several minutes.\n\n\n## Run the sample\n\nOpen the sample's website by using `firebase open hosting:site` or directly accessing `https://<project-id>.firebaseapp.com/`.\n\nClick on the **Sign in with Instagram** button and a popup window will appear that will show the Instagram In authentication consent screen. Sign In and/or authorize the authentication request.\n\nThe website should display your name and profile pic from Instagram. At this point you are authenticated in Firebase and can use the database/hosting etc...\n\n## Workflow and design\n\nWhen clicking the **Sign in with Instagram** button a popup is shown which redirects users to the `redirect` Function URL.\n\nThe `redirect` Function then redirects the user to the Instagram OAuth 2.0 consent screen where (the first time only) the user will have to grant approval. Also the `state` cookie is set on the client with the value of the `state` URL query parameter to check against later on.\n\nAfter the user has granted approval he is redirected back to the `./popup.html` page along with an OAuth 2.0 Auth Code as a URL parameter. This Auth code is then sent to the `token` Function using a JSONP Request. The `token` function then:\n - Checks that the value of the `state` URL query parameter is the same as the one in the `state` cookie.\n - Exchanges the auth code for an access token using the Instagram app credentials and gets the user identity (photoURL and full name).\n - Mints a Custom Auth token (which is why we need Service Accounts Credentials).\n - Returns the Custom Auth Token, photo URL, user display name and Instagram access token to the `./popup.html` page.\n\n The `./popup.html` receives the Custom Auth Token and other data back from the AJAX request to the `token` Function and uses it to update the user's profile, saves the access token to the database, authenticate the user in Firebase and then close the popup.\n At this point the main page will detect the sign-in through the Firebase Auth State observer and display the signed-In user information.\n"
  },
  {
    "path": "Node-1st-gen/instagram-auth/database.rules.json",
    "content": "{\n  \"rules\": {\n    \"instagramAccessToken\": {\n      \"$uid\": {\n        \".read\": \"auth.uid === $uid\",\n        \".write\": \"auth.uid === $uid\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "Node-1st-gen/instagram-auth/firebase.json",
    "content": "{\n  \"functions\": {\n    \"codebase\": \"instagram-auth\"\n  },\n  \"database\": {\n    \"rules\": \"database.rules.json\"\n  },\n  \"hosting\": {\n    \"public\": \"public\",\n    \"rewrites\": [\n      {\n        \"source\": \"/redirect\",\n        \"function\": \"redirect\"\n      },\n      {\n        \"source\": \"/token\",\n        \"function\": \"token\"\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "Node-1st-gen/instagram-auth/functions/.eslintrc.json",
    "content": "{\n  \"parserOptions\": {\n    // Required for certain syntax usages\n    \"ecmaVersion\": 2022\n  },\n  \"plugins\": [\n    \"promise\"\n  ],\n  \"extends\": \"eslint:recommended\",\n  \"rules\": {\n    // Removed rule \"disallow the use of console\" from recommended eslint rules\n    \"no-console\": \"off\",\n\n    // Removed rule \"disallow multiple spaces in regular expressions\" from recommended eslint rules\n    \"no-regex-spaces\": \"off\",\n\n    // Removed rule \"disallow the use of debugger\" from recommended eslint rules\n    \"no-debugger\": \"off\",\n\n    // Removed rule \"disallow unused variables\" from recommended eslint rules\n    \"no-unused-vars\": \"off\",\n\n    // Removed rule \"disallow mixed spaces and tabs for indentation\" from recommended eslint rules\n    \"no-mixed-spaces-and-tabs\": \"off\",\n\n    // Removed rule \"disallow the use of undeclared variables unless mentioned in /*global */ comments\" from recommended eslint rules\n    \"no-undef\": \"off\",\n\n    // Warn against template literal placeholder syntax in regular strings\n    \"no-template-curly-in-string\": 1,\n\n    // Warn if return statements do not either always or never specify values\n    \"consistent-return\": 1,\n\n    // Warn if no return statements in callbacks of array methods\n    \"array-callback-return\": 1,\n\n    // Requre the use of === and !==\n    \"eqeqeq\": 2,\n\n    // Disallow the use of alert, confirm, and prompt\n    \"no-alert\": 2,\n\n    // Disallow the use of arguments.caller or arguments.callee\n    \"no-caller\": 2,\n\n    // Disallow null comparisons without type-checking operators\n    \"no-eq-null\": 2,\n\n    // Disallow the use of eval()\n    \"no-eval\": 2,\n\n    // Warn against extending native types\n    \"no-extend-native\": 1,\n\n    // Warn against unnecessary calls to .bind()\n    \"no-extra-bind\": 1,\n\n    // Warn against unnecessary labels    \n    \"no-extra-label\": 1,\n\n    // Disallow leading or trailing decimal points in numeric literals\n    \"no-floating-decimal\": 2,\n\n    // Warn against shorthand type conversions\n    \"no-implicit-coercion\": 1,\n\n    // Warn against function declarations and expressions inside loop statements\n    \"no-loop-func\": 1,\n\n    // Disallow new operators with the Function object\n    \"no-new-func\": 2,\n\n    // Warn against new operators with the String, Number, and Boolean objects\n    \"no-new-wrappers\": 1,\n\n    // Disallow throwing literals as exceptions\n    \"no-throw-literal\": 2,\n\n    // Require using Error objects as Promise rejection reasons\n    \"prefer-promise-reject-errors\": 2,\n\n    // Enforce “for” loop update clause moving the counter in the right direction\n    \"for-direction\": 2,\n\n    // Enforce return statements in getters\n    \"getter-return\": 2,\n\n    // Disallow await inside of loops\n    \"no-await-in-loop\": 2,\n\n    // Disallow comparing against -0\n    \"no-compare-neg-zero\": 2,\n\n    // Warn against catch clause parameters from shadowing variables in the outer scope\n    \"no-catch-shadow\": 1,\n\n    // Disallow identifiers from shadowing restricted names\n    \"no-shadow-restricted-names\": 2,\n\n    // Enforce return statements in callbacks of array methods\n    \"callback-return\": 2,\n\n    // Require error handling in callbacks\n    \"handle-callback-err\": 2,\n\n    // Warn against string concatenation with __dirname and __filename\n    \"no-path-concat\": 1,\n\n    // Prefer using arrow functions for callbacks\n    \"prefer-arrow-callback\": 1,\n\n    // Return inside each then() to create readable and reusable Promise chains.\n    \"promise/always-return\": 2,\n\n    //Enforces the use of catch() on un-returned promises\n    \"promise/catch-or-return\": 2,\n\n    // Warn against nested then() or catch() statements\n    \"promise/no-nesting\": 1\n  }\n}\n"
  },
  {
    "path": "Node-1st-gen/instagram-auth/functions/index.js",
    "content": "/**\n * Copyright 2016 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use strict';\n\nconst functions = require('firebase-functions/v1');\nconst {onInit} = require('firebase-functions/v1/init');\nconst {defineSecret} = require('firebase-functions/params');\nconst cookieParser = require('cookie-parser');\nconst crypto = require('node:crypto');\n\n// Firebase Setup\nconst admin = require('firebase-admin');\n// @ts-ignore\nconst serviceAccount = require('./service-account.json');\nadmin.initializeApp({\n  credential: admin.credential.cert(serviceAccount),\n  databaseURL: `https://${process.env.GCLOUD_PROJECT}.firebaseio.com`,\n});\n\nconst OAUTH_REDIRECT_URI = `https://${process.env.GCLOUD_PROJECT}.firebaseapp.com/popup.html`;\nconst OAUTH_SCOPES = 'basic';\n\nconst instagramClientId = defineSecret('INSTAGRAM_CLIENT_ID');\nconst instagramClientSecret = defineSecret('INSTAGRAM_CLIENT_SECRET');\n\nlet oauth2;\nonInit(() => {\n  // Instagram OAuth 2 setup\n  // TODO: Configure the `INSTAGRAM_CLIENT_ID` and `INSTAGRAM_CLIENT_SECRET` secrets.\n  const credentials = {\n    client: {\n      id: instagramClientId.value(),\n      secret: instagramClientSecret.value(),\n    },\n    auth: {\n      tokenHost: 'https://api.instagram.com',\n      tokenPath: '/oauth/access_token',\n    },\n  };\n  oauth2 = require('simple-oauth2').create(credentials);\n});\n\n/**\n * Redirects the User to the Instagram authentication consent screen. Also the 'state' cookie is set for later state\n * verification.\n */\nexports.redirect = functions.runWith({secrets: [instagramClientId, instagramClientSecret]}).https.onRequest((req, res) => {\n  cookieParser()(req, res, () => {\n    const state = req.cookies.state || crypto.randomBytes(20).toString('hex');\n    functions.logger.log('Setting verification state:', state);\n    res.cookie('state', state.toString(), {\n      maxAge: 3600000,\n      secure: true,\n      httpOnly: true,\n    });\n    const redirectUri = oauth2.authorizationCode.authorizeURL({\n      redirect_uri: OAUTH_REDIRECT_URI,\n      scope: OAUTH_SCOPES,\n      state: state,\n    });\n    functions.logger.log('Redirecting to:', redirectUri);\n    res.redirect(redirectUri);\n  });\n});\n\n/**\n * Exchanges a given Instagram auth code passed in the 'code' URL query parameter for a Firebase auth token.\n * The request also needs to specify a 'state' query parameter which will be checked against the 'state' cookie.\n * The Firebase custom auth token, display name, photo URL and Instagram acces token are sent back in a JSONP callback\n * function with function name defined by the 'callback' query parameter.\n */\nexports.token = functions.runWith({secrets: [instagramClientId, instagramClientSecret]}).https.onRequest(async (req, res) => {\n  try {\n    return cookieParser()(req, res, async () => {\n      functions.logger.log('Received verification state:', req.cookies.state);\n      functions.logger.log('Received state:', req.query.state);\n      if (!req.cookies.state) {\n        throw new Error('State cookie not set or expired. Maybe you took too long to authorize. Please try again.');\n      } else if (req.cookies.state !== req.query.state) {\n        throw new Error('State validation failed');\n      }\n      functions.logger.log('Received auth code:', req.query.code);\n      const results = await oauth2.authorizationCode.getToken({\n        code: req.query.code,\n        redirect_uri: OAUTH_REDIRECT_URI,\n      });\n      functions.logger.log('Auth code exchange result received:', results);\n\n        // We have an Instagram access token and the user identity now.\n        const accessToken = results.access_token;\n        const instagramUserID = results.user.id;\n        const profilePic = results.user.profile_picture;\n        const userName = results.user.full_name;\n\n      // Create a Firebase account and get the Custom Auth Token.\n      const firebaseToken = await createFirebaseAccount(instagramUserID, userName, profilePic, accessToken);\n      // Serve an HTML page that signs the user in and updates the user profile.\n      return res.jsonp({ token: firebaseToken});\n    });\n  } catch(error) {\n    return res.jsonp({\n      error: error.toString(),\n    });\n  }\n});\n\n/**\n * Creates a Firebase account with the given user profile and returns a custom auth token allowing\n * signing-in this account.\n * Also saves the accessToken to the datastore at /instagramAccessToken/$uid\n *\n * @returns {Promise<string>} The Firebase custom auth token in a promise.\n */\nasync function createFirebaseAccount(instagramID, displayName, photoURL, accessToken) {\n  // The UID we'll assign to the user.\n  const uid = `instagram:${instagramID}`;\n\n  // Save the access token to the Firebase Realtime Database.\n  const databaseTask = admin.database().ref(`/instagramAccessToken/${uid}`).set(accessToken);\n\n  // Create or update the user account.\n  const userCreationTask = admin.auth().updateUser(uid, {\n    displayName: displayName,\n    photoURL: photoURL,\n  }).catch((error) => {\n    // If user does not exists we create it.\n    if (error.code === 'auth/user-not-found') {\n      return admin.auth().createUser({\n        uid: uid,\n        displayName: displayName,\n        photoURL: photoURL,\n      });\n    }\n    throw error;\n  });\n\n  // Wait for all async task to complete then generate and return a custom auth token.\n  await Promise.all([userCreationTask, databaseTask]);\n  // Create a Firebase custom auth token.\n  const token = await admin.auth().createCustomToken(uid);\n  functions.logger.log(\n    'Created Custom token for UID \"',\n    uid,\n    '\" Token:',\n    token\n  );\n  return token;\n}\n"
  },
  {
    "path": "Node-1st-gen/instagram-auth/functions/package.json",
    "content": "{\n  \"name\": \"instagram-auth-functions\",\n  \"description\": \"Authenticate with Instagram Firebase Functions sample\",\n  \"dependencies\": {\n    \"cookie-parser\": \"^1.4.6\",\n    \"eslint-plugin-promise\": \"^7.2.1\",\n    \"firebase-admin\": \"^13.0.2\",\n    \"firebase-functions\": \"7.0.0\",\n    \"simple-oauth2\": \"^3.4.0\"\n  },\n  \"devDependencies\": {\n    \"eslint\": \"^8.57.1\"\n  },\n  \"scripts\": {\n    \"lint\": \"./node_modules/.bin/eslint --max-warnings=0 .\",\n    \"serve\": \"firebase emulators:start --only functions\",\n    \"shell\": \"firebase functions:shell\",\n    \"start\": \"npm run shell\",\n    \"deploy\": \"firebase deploy --only functions\",\n    \"logs\": \"firebase functions:log\",\n    \"compile\": \"cp ../../../tsconfig.template.json ./tsconfig-compile.json && tsc --project tsconfig-compile.json\"\n  },\n  \"engines\": {\n    \"node\": \"20\"\n  },\n  \"private\": true\n}\n"
  },
  {
    "path": "Node-1st-gen/instagram-auth/main.css",
    "content": "/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nhtml, body {\n  font-family: 'Roboto', 'Helvetica', sans-serif;\n}\n.mdl-grid {\n  max-width: 1024px;\n  margin: auto;\n}\n.mdl-card {\n  min-height: 0;\n  padding-bottom: 5px;\n}\n.mdl-layout__header-row {\n  padding: 0;\n}\n#message-form {\n  display: flex;\n  flex-direction: column;\n}\n#message-form button {\n  max-width: 300px;\n}\n#message-list {\n  padding: 0;\n  width: 100%;\n}\n#message-list > div {\n  padding: 15px;\n  border-bottom: 1px #f1f1f1 solid;\n}\nh3 {\n  background: url('firebase-logo.png') no-repeat;\n  background-size: 40px;\n  padding-left: 50px;\n}\n#demo-signed-out-card,\n#demo-signed-in-card,\n#demo-subscribe-button,\n#demo-unsubscribe-button,\n#demo-subscribed-text-container,\n#demo-unsubscribed-text-container {\n  display: none;\n}\n#demo-subscribe-button,\n#demo-unsubscribe-button {\n  margin-right: 20px;\n}"
  },
  {
    "path": "Node-1st-gen/instagram-auth/public/index.html",
    "content": "<!doctype html>\n<!--\n  Copyright 2016 Google Inc. All rights reserved.\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n      https://www.apache.org/licenses/LICENSE-2.0\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License\n-->\n<html lang=\"en\">\n<head>\n  <meta charset=\"utf-8\">\n  <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n  <meta name=\"description\" content=\"Demonstrates of to authorize Firebase with Instagram Auth using Firebase Functions\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n  <title>Firebase Functions demo to Sign In with Instagram</title>\n\n  <!-- Material Design Lite -->\n  <link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/icon?family=Material+Icons\">\n  <link rel=\"stylesheet\" href=\"https://code.getmdl.io/1.1.3/material.blue_grey-orange.min.css\">\n  <script defer src=\"https://code.getmdl.io/1.1.3/material.min.js\"></script>\n\n  <link rel=\"stylesheet\" href=\"main.css\">\n</head>\n<body>\n<div class=\"demo-layout mdl-layout mdl-js-layout mdl-layout--fixed-header\">\n\n  <!-- Header section containing title -->\n  <header class=\"mdl-layout__header mdl-color-text--white mdl-color--light-blue-700\">\n    <div class=\"mdl-cell mdl-cell--12-col mdl-cell--12-col-tablet mdl-grid\">\n      <div class=\"mdl-layout__header-row mdl-cell mdl-cell--12-col mdl-cell--12-col-tablet mdl-cell--8-col-desktop\">\n        <h3>Sign in with Instagram demo</h3>\n      </div>\n    </div>\n  </header>\n  <main class=\"mdl-layout__content mdl-color--grey-100\">\n    <div class=\"mdl-cell--12-col mdl-cell--12-col-tablet mdl-grid\">\n\n      <!-- Card containing the sign-in UI -->\n      <div id=\"demo-signed-out-card\" class=\"mdl-card mdl-shadow--2dp mdl-cell\">\n        <div class=\"mdl-card__supporting-text mdl-color-text--grey-600\">\n          <p>\n            This web application demonstrates how you can Sign In with Instagram to Firebase Authentication.\n            <strong>Now sign in!</strong>\n          </p>\n          <button id=\"demo-sign-in-button\" class=\"mdl-color-text--grey-700 mdl-button--raised mdl-button mdl-js-button mdl-js-ripple-effect\"><i class=\"material-icons\">account_circle</i> Sign in with Instagram</button>\n        </div>\n      </div>\n\n      <!-- Card containing the signed-in UI -->\n      <div id=\"demo-signed-in-card\" class=\"mdl-card mdl-shadow--2dp mdl-cell\">\n        <div class=\"mdl-card__supporting-text mdl-color-text--grey-600\">\n          <p>\n            Welcome <span id=\"demo-name-container\"></span><br>\n            Your Firebase User ID is: <span id=\"demo-uid-container\"></span><br>\n            Your profile picture: <img id=\"demo-profile-pic\">\n          </p>\n          <button id=\"demo-sign-out-button\" class=\"mdl-color-text--grey-700 mdl-button--raised mdl-button mdl-js-button mdl-js-ripple-effect\">Sign out</button>\n          <button id=\"demo-delete-button\" class=\"mdl-color-text--grey-700 mdl-button--raised mdl-button mdl-js-button mdl-js-ripple-effect\">Delete account</button>\n        </div>\n      </div>\n    </div>\n  </main>\n</div>\n\n\n<!-- Import and configure the Firebase SDK -->\n<!-- These scripts are made available when the app is served or deployed on Firebase Hosting -->\n<!-- If you do not serve/host your project using Firebase Hosting see https://firebase.google.com/docs/web/setup -->\n<script src=\"/__/firebase/10.0.0/firebase-app-compat.js\"></script>\n<script src=\"/__/firebase/10.0.0/firebase-auth-compat.js\"></script>\n<script src=\"/__/firebase/init.js\"></script>\n\n<script src=\"main.js\"></script>\n</body>\n</html>\n"
  },
  {
    "path": "Node-1st-gen/instagram-auth/public/main.css",
    "content": "/**\n * Copyright 2016 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nhtml, body {\n  font-family: 'Roboto', 'Helvetica', sans-serif;\n}\n.mdl-grid {\n  max-width: 1024px;\n  margin: auto;\n}\n.mdl-card {\n  min-height: 0;\n  padding-bottom: 5px;\n}\n.mdl-layout__header-row {\n  padding: 0;\n}\nh3 {\n  background: url('firebase-logo.png') no-repeat;\n  background-size: 40px;\n  padding-left: 50px;\n}\n#demo-signed-out-card,\n#demo-signed-in-card {\n  display: none;\n}\n#demo-profile-pic {\n  height: 60px;\n  width: 60px;\n  border-radius: 30px;\n  margin-left: calc(50% - 30px);\n  margin-top: 10px;\n}\n#demo-name-container,\n#demo-email-container,\n#demo-uid-container {\n  font-weight: bold;\n}\n#demo-delete-button {\n  margin-left: 20px;\n}\n"
  },
  {
    "path": "Node-1st-gen/instagram-auth/public/main.js",
    "content": "/**\n * Copyright 2016 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use strict';\n\n// Initializes the Demo.\nfunction Demo() {\n  document.addEventListener('DOMContentLoaded', function() {\n    // Shortcuts to DOM Elements.\n    this.signInButton = document.getElementById('demo-sign-in-button');\n    this.signOutButton = document.getElementById('demo-sign-out-button');\n    this.nameContainer = document.getElementById('demo-name-container');\n    this.uidContainer = document.getElementById('demo-uid-container');\n    this.deleteButton = document.getElementById('demo-delete-button');\n    this.profilePic = document.getElementById('demo-profile-pic');\n    this.signedOutCard = document.getElementById('demo-signed-out-card');\n    this.signedInCard = document.getElementById('demo-signed-in-card');\n\n    // Bind events.\n    this.signInButton.addEventListener('click', this.signIn.bind(this));\n    this.signOutButton.addEventListener('click', this.signOut.bind(this));\n    this.deleteButton.addEventListener('click', this.deleteAccount.bind(this));\n    firebase.auth().onAuthStateChanged(this.onAuthStateChanged.bind(this));\n  }.bind(this));\n}\n\n// Triggered on Firebase auth state change.\nDemo.prototype.onAuthStateChanged = function(user) {\n  if (user) {\n    this.nameContainer.innerText = user.displayName;\n    this.uidContainer.innerText = user.uid;\n    this.profilePic.src = user.photoURL;\n    this.signedOutCard.style.display = 'none';\n    this.signedInCard.style.display = 'block';\n  } else {\n    this.signedOutCard.style.display = 'block';\n    this.signedInCard.style.display = 'none';\n  }\n};\n\n// Initiates the sign-in flow using LinkedIn sign in in a popup.\nDemo.prototype.signIn = function() {\n  // Open the popup that will start the auth flow.\n  window.open('popup.html', 'name', 'height=585,width=400');\n};\n\n// Signs-out of Firebase.\nDemo.prototype.signOut = function() {\n  firebase.auth().signOut();\n};\n\n// Deletes the user's account.\nDemo.prototype.deleteAccount = function() {\n  firebase.auth().currentUser.delete().then(function() {\n    window.alert('Account deleted');\n  }).catch(function(error) {\n    if (error.code === 'auth/requires-recent-login') {\n      window.alert('You need to have recently signed-in to delete your account. Please sign-in and try again.');\n      firebase.auth().signOut();\n    }\n  });\n};\n\n// Load the demo.\nnew Demo();\n"
  },
  {
    "path": "Node-1st-gen/instagram-auth/public/popup.html",
    "content": "<!doctype html>\n<!--\n  Copyright 2016 Google Inc. All rights reserved.\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n      https://www.apache.org/licenses/LICENSE-2.0\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License\n-->\n<html lang=\"en\">\n<head>\n  <meta charset=\"utf-8\">\n  <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n  <meta name=\"description\" content=\"Demonstrates how to authorize Firebase with Instagram auth using Firebase Functions\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n  <title>Authenticate with Instagram</title>\n</head>\n<body>\n\nPlease wait...\n\n\n<!-- Import and configure the Firebase SDK -->\n<!-- These scripts are made available when the app is served or deployed on Firebase Hosting -->\n<!-- If you do not serve/host your project using Firebase Hosting see https://firebase.google.com/docs/web/setup -->\n<script src=\"/__/firebase/10.0.0/firebase-app-compat.js\"></script>\n<script src=\"/__/firebase/10.0.0/firebase-auth-compat.js\"></script>\n<script src=\"/__/firebase/init.js\"></script>\n\n<script>\n  /**\n   * Returns the value of the given URL query parameter.\n   */\n  function getURLParameter(name) {\n    return decodeURIComponent((new RegExp('[?|&]' + name + '=' + '([^&;]+?)(&|#|;|$)').exec(location.search) ||\n        [null, ''])[1].replace(/\\+/g, '%20')) || null;\n  }\n\n  /**\n   * Returns the ID of the Firebase project.\n   */\n  function getFirebaseProjectId() {\n    return firebase.app().options.authDomain.split('.')[0];\n  }\n\n  /**\n   * This callback is called by the JSONP callback of the 'token' Firebase Function with the Firebase auth token.\n   */\n  function tokenReceived(data) {\n    if (data.token) {\n      firebase.auth().signInWithCustomToken(data.token).then(function() {\n        window.close();\n      });\n    } else {\n      console.error(data);\n      document.body.innerText = 'Error in the token Function: ' + data.error;\n    }\n  }\n\n  var code = getURLParameter('code');\n  var state = getURLParameter('state');\n  var error = getURLParameter('error');\n  if (error) {\n    document.body.innerText = 'Error back from the Instagram auth page: ' + error;\n  } else if(!code) {\n    // Start the auth flow.\n    window.location.href  = '/redirect';\n  } else {\n    // Use JSONP to load the 'token' Firebase Function to exchange the auth code against a Firebase custom token.\n    const script = document.createElement('script');\n    script.type = 'text/javascript';\n    // This is the URL to the HTTP triggered 'token' Firebase Function.\n    // See https://firebase.google.com/docs/functions.\n    var tokenFunctionURL = '/token';\n    script.src = tokenFunctionURL +\n        '?code=' + encodeURIComponent(code) +\n        '&state=' + encodeURIComponent(state) +\n        '&callback=' + tokenReceived.name;\n    document.head.appendChild(script);\n  }\n</script>\n</body>\n</html>\n"
  },
  {
    "path": "Node-1st-gen/lastmodified-tracking/README.md",
    "content": "# Tracking last modified Date of a Firebase Database\n\nThis template shows how to keep track of the date at which the Firebase Database or a subset of a firebase Database was last modified.\n\n## Functions Code\n\nSee file [functions/index.js](functions/index.js) for the code.\n\nThis is done by simply updating a `lastmodified` attribute on the parent of the node which is tracked (for instance at the root of the Database).\n\nThe dependencies are listed in [functions/package.json](functions/package.json).\n\n## Sample Database Structure\n\nAs an example we'll be using a simple chat database structure:\n\n```\n/functions-project-12345\n    /chat\n        /key-123456\n            username: \"Mat\"\n            text: \"Hey Bob!\"\n        /key-123457\n            username: \"Bob\"\n            text: \"Hey Mat\"\n```\n\nThe function will write to `/lastmodified`:\n\n\n```\n/functions-project-12345\n    /lastmodified: 1234567890\n    /chat\n        /key-123456\n            username: \"Mat\"\n            text: \"Hey Bob!\"\n        /key-123457\n            username: \"Bob\"\n            text: \"Hey Mat\"\n```\n\n## Security Rules\n\nThe following security rules ensures only a Function with admin access can update the `lastmodified` attribute.\n\n```\n{\n  \"rules\": {\n    \"lastmodified\": {\n        \".write\": \"false\",\n        \".read\": \"true\"\n    }\n  }\n}\n```\n\n\n"
  },
  {
    "path": "Node-1st-gen/lastmodified-tracking/database.rules.json",
    "content": "{\n  \"rules\": {\n    \"lastmodified\": {\n      \".write\": \"false\",\n      \".read\": \"true\"\n    }\n  }\n}\n"
  },
  {
    "path": "Node-1st-gen/lastmodified-tracking/firebase.json",
    "content": "{\n  \"functions\": {\n    \"codebase\": \"lastmodified-tracking\"\n  },\n  \"database\": {\n    \"rules\": \"database.rules.json\"\n  }\n}\n"
  },
  {
    "path": "Node-1st-gen/lastmodified-tracking/functions/.eslintrc.json",
    "content": "{\n  \"parserOptions\": {\n    // Required for certain syntax usages\n    \"ecmaVersion\": 2022\n  },\n  \"plugins\": [\n    \"promise\"\n  ],\n  \"extends\": \"eslint:recommended\",\n  \"rules\": {\n    // Removed rule \"disallow the use of console\" from recommended eslint rules\n    \"no-console\": \"off\",\n\n    // Removed rule \"disallow multiple spaces in regular expressions\" from recommended eslint rules\n    \"no-regex-spaces\": \"off\",\n\n    // Removed rule \"disallow the use of debugger\" from recommended eslint rules\n    \"no-debugger\": \"off\",\n\n    // Removed rule \"disallow unused variables\" from recommended eslint rules\n    \"no-unused-vars\": \"off\",\n\n    // Removed rule \"disallow mixed spaces and tabs for indentation\" from recommended eslint rules\n    \"no-mixed-spaces-and-tabs\": \"off\",\n\n    // Removed rule \"disallow the use of undeclared variables unless mentioned in /*global */ comments\" from recommended eslint rules\n    \"no-undef\": \"off\",\n\n    // Warn against template literal placeholder syntax in regular strings\n    \"no-template-curly-in-string\": 1,\n\n    // Warn if return statements do not either always or never specify values\n    \"consistent-return\": 1,\n\n    // Warn if no return statements in callbacks of array methods\n    \"array-callback-return\": 1,\n\n    // Requre the use of === and !==\n    \"eqeqeq\": 2,\n\n    // Disallow the use of alert, confirm, and prompt\n    \"no-alert\": 2,\n\n    // Disallow the use of arguments.caller or arguments.callee\n    \"no-caller\": 2,\n\n    // Disallow null comparisons without type-checking operators\n    \"no-eq-null\": 2,\n\n    // Disallow the use of eval()\n    \"no-eval\": 2,\n\n    // Warn against extending native types\n    \"no-extend-native\": 1,\n\n    // Warn against unnecessary calls to .bind()\n    \"no-extra-bind\": 1,\n\n    // Warn against unnecessary labels    \n    \"no-extra-label\": 1,\n\n    // Disallow leading or trailing decimal points in numeric literals\n    \"no-floating-decimal\": 2,\n\n    // Warn against shorthand type conversions\n    \"no-implicit-coercion\": 1,\n\n    // Warn against function declarations and expressions inside loop statements\n    \"no-loop-func\": 1,\n\n    // Disallow new operators with the Function object\n    \"no-new-func\": 2,\n\n    // Warn against new operators with the String, Number, and Boolean objects\n    \"no-new-wrappers\": 1,\n\n    // Disallow throwing literals as exceptions\n    \"no-throw-literal\": 2,\n\n    // Require using Error objects as Promise rejection reasons\n    \"prefer-promise-reject-errors\": 2,\n\n    // Enforce “for” loop update clause moving the counter in the right direction\n    \"for-direction\": 2,\n\n    // Enforce return statements in getters\n    \"getter-return\": 2,\n\n    // Disallow await inside of loops\n    \"no-await-in-loop\": 2,\n\n    // Disallow comparing against -0\n    \"no-compare-neg-zero\": 2,\n\n    // Warn against catch clause parameters from shadowing variables in the outer scope\n    \"no-catch-shadow\": 1,\n\n    // Disallow identifiers from shadowing restricted names\n    \"no-shadow-restricted-names\": 2,\n\n    // Enforce return statements in callbacks of array methods\n    \"callback-return\": 2,\n\n    // Require error handling in callbacks\n    \"handle-callback-err\": 2,\n\n    // Warn against string concatenation with __dirname and __filename\n    \"no-path-concat\": 1,\n\n    // Prefer using arrow functions for callbacks\n    \"prefer-arrow-callback\": 1,\n\n    // Return inside each then() to create readable and reusable Promise chains.\n    \"promise/always-return\": 2,\n\n    //Enforces the use of catch() on un-returned promises\n    \"promise/catch-or-return\": 2,\n\n    // Warn against nested then() or catch() statements\n    \"promise/no-nesting\": 1\n  }\n}\n"
  },
  {
    "path": "Node-1st-gen/lastmodified-tracking/functions/index.js",
    "content": "/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use strict';\n\nconst functions = require('firebase-functions/v1');\nconst admin = require('firebase-admin');\nadmin.initializeApp();\n\n/**\n * This Function updates the `/lastmodified` with the timestamp of the last write to `/chat/$message`.\n */\nexports.touch = functions.database.ref('/chat/{message}').onWrite(\n    (change, context) => admin.database().ref('/lastmodified').set(context.timestamp));\n"
  },
  {
    "path": "Node-1st-gen/lastmodified-tracking/functions/package.json",
    "content": "{\n  \"name\": \"lastmodified-tracking-functions\",\n  \"description\": \"Track Lastmodified date of nodes Firebase Functions sample\",\n  \"dependencies\": {\n    \"eslint-plugin-promise\": \"^7.2.1\",\n    \"firebase-admin\": \"^13.0.2\",\n    \"firebase-functions\": \"7.0.0\"\n  },\n  \"devDependencies\": {\n    \"eslint\": \"^8.57.1\"\n  },\n  \"scripts\": {\n    \"lint\": \"./node_modules/.bin/eslint --max-warnings=0 .\",\n    \"serve\": \"firebase emulators:start --only functions\",\n    \"shell\": \"firebase functions:shell\",\n    \"start\": \"npm run shell\",\n    \"deploy\": \"firebase deploy --only functions\",\n    \"logs\": \"firebase functions:log\",\n    \"compile\": \"cp ../../../tsconfig.template.json ./tsconfig-compile.json && tsc --project tsconfig-compile.json\"\n  },\n  \"engines\": {\n    \"node\": \"20\"\n  },\n  \"private\": true\n}\n"
  },
  {
    "path": "Node-1st-gen/limit-children/README.md",
    "content": "# Limit number of child nodes\n\nThis template shows how to keep the number of child nodes in a Firebase database below a given number. This can be used to limit the number of lines of a chat history or logs.\n\n## Functions Code\n\nSee file [functions/index.js](functions/index.js) for the code.\n\nThe dependencies are listed in [functions/package.json](functions/package.json).\n\n## Sample Database Structure\n\nAs an example we'll be using a simple chat database structure:\n\n```\n/functions-project-12345\n    /chat\n        /key-123456\n            user: \"Mat\",\n            text: \"Hey Bob!\"\n        /key-123457\n            user: \"Bob\",\n            text: \"Hey Mat! What's Up?\"\n```\n\nEvery time a new chat message is added the Function runs. It counts the number of chat messages and removes the old ones if there are too many.\n"
  },
  {
    "path": "Node-1st-gen/limit-children/firebase.json",
    "content": "{\n    \"functions\": {\n        \"codebase\": \"limit-children\"\n    }\n}"
  },
  {
    "path": "Node-1st-gen/limit-children/functions/.eslintrc.json",
    "content": "{\n  \"parserOptions\": {\n    // Required for certain syntax usages\n    \"ecmaVersion\": 2022\n  },\n  \"plugins\": [\n    \"promise\"\n  ],\n  \"extends\": \"eslint:recommended\",\n  \"rules\": {\n    // Removed rule \"disallow the use of console\" from recommended eslint rules\n    \"no-console\": \"off\",\n\n    // Removed rule \"disallow multiple spaces in regular expressions\" from recommended eslint rules\n    \"no-regex-spaces\": \"off\",\n\n    // Removed rule \"disallow the use of debugger\" from recommended eslint rules\n    \"no-debugger\": \"off\",\n\n    // Removed rule \"disallow unused variables\" from recommended eslint rules\n    \"no-unused-vars\": \"off\",\n\n    // Removed rule \"disallow mixed spaces and tabs for indentation\" from recommended eslint rules\n    \"no-mixed-spaces-and-tabs\": \"off\",\n\n    // Removed rule \"disallow the use of undeclared variables unless mentioned in /*global */ comments\" from recommended eslint rules\n    \"no-undef\": \"off\",\n\n    // Warn against template literal placeholder syntax in regular strings\n    \"no-template-curly-in-string\": 1,\n\n    // Warn if return statements do not either always or never specify values\n    \"consistent-return\": 1,\n\n    // Warn if no return statements in callbacks of array methods\n    \"array-callback-return\": 1,\n\n    // Requre the use of === and !==\n    \"eqeqeq\": 2,\n\n    // Disallow the use of alert, confirm, and prompt\n    \"no-alert\": 2,\n\n    // Disallow the use of arguments.caller or arguments.callee\n    \"no-caller\": 2,\n\n    // Disallow null comparisons without type-checking operators\n    \"no-eq-null\": 2,\n\n    // Disallow the use of eval()\n    \"no-eval\": 2,\n\n    // Warn against extending native types\n    \"no-extend-native\": 1,\n\n    // Warn against unnecessary calls to .bind()\n    \"no-extra-bind\": 1,\n\n    // Warn against unnecessary labels    \n    \"no-extra-label\": 1,\n\n    // Disallow leading or trailing decimal points in numeric literals\n    \"no-floating-decimal\": 2,\n\n    // Warn against shorthand type conversions\n    \"no-implicit-coercion\": 1,\n\n    // Warn against function declarations and expressions inside loop statements\n    \"no-loop-func\": 1,\n\n    // Disallow new operators with the Function object\n    \"no-new-func\": 2,\n\n    // Warn against new operators with the String, Number, and Boolean objects\n    \"no-new-wrappers\": 1,\n\n    // Disallow throwing literals as exceptions\n    \"no-throw-literal\": 2,\n\n    // Require using Error objects as Promise rejection reasons\n    \"prefer-promise-reject-errors\": 2,\n\n    // Enforce “for” loop update clause moving the counter in the right direction\n    \"for-direction\": 2,\n\n    // Enforce return statements in getters\n    \"getter-return\": 2,\n\n    // Disallow await inside of loops\n    \"no-await-in-loop\": 2,\n\n    // Disallow comparing against -0\n    \"no-compare-neg-zero\": 2,\n\n    // Warn against catch clause parameters from shadowing variables in the outer scope\n    \"no-catch-shadow\": 1,\n\n    // Disallow identifiers from shadowing restricted names\n    \"no-shadow-restricted-names\": 2,\n\n    // Enforce return statements in callbacks of array methods\n    \"callback-return\": 2,\n\n    // Require error handling in callbacks\n    \"handle-callback-err\": 2,\n\n    // Warn against string concatenation with __dirname and __filename\n    \"no-path-concat\": 1,\n\n    // Prefer using arrow functions for callbacks\n    \"prefer-arrow-callback\": 1,\n\n    // Return inside each then() to create readable and reusable Promise chains.\n    \"promise/always-return\": 2,\n\n    //Enforces the use of catch() on un-returned promises\n    \"promise/catch-or-return\": 2,\n\n    // Warn against nested then() or catch() statements\n    \"promise/no-nesting\": 1\n  }\n}\n"
  },
  {
    "path": "Node-1st-gen/limit-children/functions/index.js",
    "content": "/**\n * Copyright 2016 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use strict';\n\nconst functions = require('firebase-functions/v1');\n\n// Max number of lines of the chat history.\nconst MAX_LOG_COUNT = 5;\n\n// Removes siblings of the node that element that triggered the function if there are more than MAX_LOG_COUNT.\n// In this example we'll keep the max number of chat message history to MAX_LOG_COUNT.\nexports.truncate = functions.database.ref('/chat').onWrite((change) => {\n  const parentRef = change.after.ref;\n  const snapshot = change.after\n\n  if (snapshot.numChildren() >= MAX_LOG_COUNT) {\n    let childCount = 0;\n    const updates = {};\n    snapshot.forEach((child) => {\n      if (++childCount <= snapshot.numChildren() - MAX_LOG_COUNT) {\n        updates[child.key] = null;\n      }\n    });\n    // Update the parent. This effectively removes the extra children.\n    return parentRef.update(updates);\n  }\n  return null;\n});\n"
  },
  {
    "path": "Node-1st-gen/limit-children/functions/package.json",
    "content": "{\n  \"name\": \"limit-children-functions\",\n  \"description\": \"Limit number of child nodes Firebase Functions sample\",\n  \"dependencies\": {\n    \"eslint-plugin-promise\": \"^7.2.1\",\n    \"firebase-admin\": \"^13.0.2\",\n    \"firebase-functions\": \"7.0.0\"\n  },\n  \"devDependencies\": {\n    \"eslint\": \"^8.57.1\"\n  },\n  \"scripts\": {\n    \"lint\": \"./node_modules/.bin/eslint --max-warnings=0 .\",\n    \"serve\": \"firebase emulators:start --only functions\",\n    \"shell\": \"firebase functions:shell\",\n    \"start\": \"npm run shell\",\n    \"deploy\": \"firebase deploy --only functions\",\n    \"logs\": \"firebase functions:log\",\n    \"compile\": \"cp ../../../tsconfig.template.json ./tsconfig-compile.json && tsc --project tsconfig-compile.json\"\n  },\n  \"engines\": {\n    \"node\": \"20\"\n  },\n  \"private\": true\n}\n"
  },
  {
    "path": "Node-1st-gen/linkedin-auth/.gitignore",
    "content": "service-account.json\n"
  },
  {
    "path": "Node-1st-gen/linkedin-auth/README.md",
    "content": "# Use LinkedIn Sign In with Firebase\n\nThis sample shows how to authenticate using LinkedIn Sign-In on Firebase. In this sample we use OAuth 2.0 based authentication to get LinkedIn user information then create a Firebase Custom Token (using the LinkedIn user ID).\n\n\n## Setup the sample\n\nCreate and setup the Firebase project:\n 1. Create a Firebase project using the [Firebase Developer Console](https://console.firebase.google.com).\n 1. Enable Billing on your Firebase the project by switching to the **Blaze** plan, this is currently needed to be able to perform HTTP requests to external services from a Cloud Function.\n\nCreate and provide a Service Account's credentials:\n 1. Create a Service Accounts file as described in the [Server SDK setup instructions](https://firebase.google.com/docs/server/setup#add_firebase_to_your_app).\n 1. Save the Service Account credential file as `./functions/service-account.json`\n\nCreate and setup your LinkedIn app:\n 1. Create a LinkedIn app in the [LinkedIn Developers website](https://www.linkedin.com/developer/apps/).\n 1. Add the URL `https://<application-id>.firebaseapp.com/popup.html` to the\n    **OAuth 2.0** > **Authorized Redirect URLs** of your LinkedIn app.\n 1. Copy the **Client ID** and **Client Secret** of your LinkedIn app and use them to set the `linkedin.client_id` and `linkedin.client_secret` Google Cloud environment variables. For this use:\n\n    ```bash\n    firebase functions:secrets:set LINKEDIN_CLIENT_ID\n    firebase functions:secrets:set LINKEDIN_CLIENT_SECRET\n    ```\n\n > Make sure the LinkedIn Client Secret is always kept secret. For instance do not save this in your version control system.\n\nDeploy your project:\n 1. Run `firebase use --add` and choose your Firebase project. This will configure the Firebase CLI to use the correct project locally.\n 1. Run `firebase deploy` to effectively deploy the sample. The first time the Functions are deployed the process can take several minutes.\n\n\n## Run the sample\n\nOpen the sample's website by using `firebase open hosting:site` or directly accessing `https://<project-id>.firebaseapp.com/`.\n\nClick on the **Sign in with LinkedIn** button and a popup window will appear that will show the LinkedIn authentication consent screen. Sign In and/or authorize the authentication request.\n\nThe website should display your name, email and profile pic from LinkedIn. At this point you are authenticated in Firebase and can use the database/hosting etc...\n\n## Workflow and design\n\nWhen clicking the **Sign in with LinkedIn** button a popup is shown which redirects users to the `redirect` Function URL.\n\nThe `redirect` Function then redirects the user to the LinkedIn OAuth 2.0 consent screen where (the first time only) the user will have to grant approval. Also the `state` cookie is set on the client with the value of the `state` URL query parameter to check against later on.\n\nAfter the user has granted approval he is redirected back to the `./popup.html` page along with an OAuth 2.0 Auth Code as a URL parameter. This Auth code is then sent to the `token` Function using a JSONP Request. The `token` function then:\n - Checks that the value of the `state` URL query parameter is the same as the one in the `state` cookie.\n - Exchanges the auth code for an access token using the LinkedIn app credentials.\n - Fetches the user identity using the LinkedIn API.\n - Mints a Custom Auth token (which is why we need Service Accounts Credentials).\n - Returns the Custom Auth Token, email, photo URL, user display name and LinkedIn access token to the `./popup.html` page.\n\n  The `./popup.html` receives the Custom Auth Token and other data back from the AJAX request to the `token` Function and uses it to update the user's profile, saves the access token to the database, authenticate the user in Firebase and then close the popup.\n At this point the main page will detect the sign-in through the Firebase Auth State observer and display the signed-In user information.\n"
  },
  {
    "path": "Node-1st-gen/linkedin-auth/database.rules.json",
    "content": "{\n  \"rules\": {\n    \"linkedInAccessToken\": {\n      \"$uid\": {\n        \".read\": \"auth.uid === $uid\",\n        \".write\": \"auth.uid === $uid\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "Node-1st-gen/linkedin-auth/firebase.json",
    "content": "{\n  \"functions\": {\n    \"codebase\": \"linkedin-auth\"\n  },\n  \"database\": {\n    \"rules\": \"database.rules.json\"\n  },\n  \"hosting\": {\n    \"public\": \"public\",\n    \"rewrites\": [\n      {\n        \"source\": \"/redirect\",\n        \"function\": \"redirect\"\n      },\n      {\n        \"source\": \"/token\",\n        \"function\": \"token\"\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "Node-1st-gen/linkedin-auth/functions/.eslintrc.json",
    "content": "{\n  \"parserOptions\": {\n    // Required for certain syntax usages\n    \"ecmaVersion\": 2022\n  },\n  \"plugins\": [\n    \"promise\"\n  ],\n  \"extends\": \"eslint:recommended\",\n  \"rules\": {\n    // Removed rule \"disallow the use of console\" from recommended eslint rules\n    \"no-console\": \"off\",\n\n    // Removed rule \"disallow multiple spaces in regular expressions\" from recommended eslint rules\n    \"no-regex-spaces\": \"off\",\n\n    // Removed rule \"disallow the use of debugger\" from recommended eslint rules\n    \"no-debugger\": \"off\",\n\n    // Removed rule \"disallow unused variables\" from recommended eslint rules\n    \"no-unused-vars\": \"off\",\n\n    // Removed rule \"disallow mixed spaces and tabs for indentation\" from recommended eslint rules\n    \"no-mixed-spaces-and-tabs\": \"off\",\n\n    // Removed rule \"disallow the use of undeclared variables unless mentioned in /*global */ comments\" from recommended eslint rules\n    \"no-undef\": \"off\",\n\n    // Warn against template literal placeholder syntax in regular strings\n    \"no-template-curly-in-string\": 1,\n\n    // Warn if return statements do not either always or never specify values\n    \"consistent-return\": 1,\n\n    // Warn if no return statements in callbacks of array methods\n    \"array-callback-return\": 1,\n\n    // Requre the use of === and !==\n    \"eqeqeq\": 2,\n\n    // Disallow the use of alert, confirm, and prompt\n    \"no-alert\": 2,\n\n    // Disallow the use of arguments.caller or arguments.callee\n    \"no-caller\": 2,\n\n    // Disallow null comparisons without type-checking operators\n    \"no-eq-null\": 2,\n\n    // Disallow the use of eval()\n    \"no-eval\": 2,\n\n    // Warn against extending native types\n    \"no-extend-native\": 1,\n\n    // Warn against unnecessary calls to .bind()\n    \"no-extra-bind\": 1,\n\n    // Warn against unnecessary labels    \n    \"no-extra-label\": 1,\n\n    // Disallow leading or trailing decimal points in numeric literals\n    \"no-floating-decimal\": 2,\n\n    // Warn against shorthand type conversions\n    \"no-implicit-coercion\": 1,\n\n    // Warn against function declarations and expressions inside loop statements\n    \"no-loop-func\": 1,\n\n    // Disallow new operators with the Function object\n    \"no-new-func\": 2,\n\n    // Warn against new operators with the String, Number, and Boolean objects\n    \"no-new-wrappers\": 1,\n\n    // Disallow throwing literals as exceptions\n    \"no-throw-literal\": 2,\n\n    // Require using Error objects as Promise rejection reasons\n    \"prefer-promise-reject-errors\": 2,\n\n    // Enforce “for” loop update clause moving the counter in the right direction\n    \"for-direction\": 2,\n\n    // Enforce return statements in getters\n    \"getter-return\": 2,\n\n    // Disallow await inside of loops\n    \"no-await-in-loop\": 2,\n\n    // Disallow comparing against -0\n    \"no-compare-neg-zero\": 2,\n\n    // Warn against catch clause parameters from shadowing variables in the outer scope\n    \"no-catch-shadow\": 1,\n\n    // Disallow identifiers from shadowing restricted names\n    \"no-shadow-restricted-names\": 2,\n\n    // Enforce return statements in callbacks of array methods\n    \"callback-return\": 2,\n\n    // Require error handling in callbacks\n    \"handle-callback-err\": 2,\n\n    // Warn against string concatenation with __dirname and __filename\n    \"no-path-concat\": 1,\n\n    // Prefer using arrow functions for callbacks\n    \"prefer-arrow-callback\": 1,\n\n    // Return inside each then() to create readable and reusable Promise chains.\n    \"promise/always-return\": 2,\n\n    //Enforces the use of catch() on un-returned promises\n    \"promise/catch-or-return\": 2,\n\n    // Warn against nested then() or catch() statements\n    \"promise/no-nesting\": 1\n  }\n}\n"
  },
  {
    "path": "Node-1st-gen/linkedin-auth/functions/index.js",
    "content": "/**\n * Copyright 2016 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use strict';\n\nconst functions = require('firebase-functions/v1');\nconst {onInit} = require('firebase-functions/v1/init');\nconst {defineSecret} = require('firebase-functions/params');\nconst cookieParser = require('cookie-parser');\nconst crypto = require('crypto');\n\n// Firebase Setup\nconst admin = require('firebase-admin');\n// @ts-ignore\nconst serviceAccount = require('./service-account.json');\nadmin.initializeApp({\n  credential: admin.credential.cert(serviceAccount),\n  databaseURL: `https://${process.env.GCLOUD_PROJECT}.firebaseio.com`,\n});\n\nconst OAUTH_SCOPES = ['r_basicprofile', 'r_emailaddress'];\n\nconst linkedinClientId = defineSecret('LINKEDIN_CLIENT_ID');\nconst linkedinClientSecret = defineSecret('LINKEDIN_CLIENT_SECRET');\n\nlet Linkedin;\nonInit(() => {\n  Linkedin = require('node-linkedin')(\n    linkedinClientId.value(),\n    linkedinClientSecret.value(),\n    `https://${process.env.GCLOUD_PROJECT}.firebaseapp.com/popup.html`);\n});\n\n/**\n * Redirects the User to the LinkedIn authentication consent screen. ALso the 'state' cookie is set for later state\n * verification.\n */\nexports.redirect = functions.runWith({secrets: [linkedinClientId, linkedinClientSecret]}).https.onRequest((req, res) => {\n  cookieParser()(req, res, () => {\n    const state = req.cookies.state || crypto.randomBytes(20).toString('hex');\n    functions.logger.log('Setting verification state:', state);\n    res.cookie('state', state.toString(), {\n      maxAge: 3600000,\n      secure: true,\n      httpOnly: true,\n    });\n    Linkedin.auth.authorize(res, OAUTH_SCOPES, state.toString());\n  });\n});\n\n/**\n * Exchanges a given LinkedIn auth code passed in the 'code' URL query parameter for a Firebase auth token.\n * The request also needs to specify a 'state' query parameter which will be checked against the 'state' cookie.\n * The Firebase custom auth token is sent back in a JSONP callback function with function name defined by the\n * 'callback' query parameter.\n */\nexports.token = functions.runWith({secrets: [linkedinClientId, linkedinClientSecret]}).https.onRequest((req, res) => {\n  try {\n    return cookieParser()(req, res, () => {\n      if (!req.cookies.state) {\n        throw new Error('State cookie not set or expired. Maybe you took too long to authorize. Please try again.');\n      }\n      functions.logger.log('Received verification state:', req.cookies.state);\n      Linkedin.auth.authorize(OAUTH_SCOPES, req.cookies.state); // Makes sure the state parameter is set\n      functions.logger.log('Received auth code:', req.query.code);\n      functions.logger.log('Received state:', req.query.state);\n      Linkedin.auth.getAccessToken(res, req.query.code, req.query.state, (error, results) => {\n        if (error) {\n          throw error;\n        }\n        functions.logger.log('Received Access Token:', results.access_token);\n        const linkedin = Linkedin.init(results.access_token);\n        linkedin.people.me(async (error, userResults) => {\n          if (error) {\n            throw error;\n          }\n          functions.logger.log(\n            'Auth code exchange result received:',\n            userResults\n          );\n\n          // We have a LinkedIn access token and the user identity now.\n          const accessToken = results.access_token;\n          const linkedInUserID = userResults.id;\n          const profilePic = userResults.pictureUrl;\n          const userName = userResults.formattedName;\n          const email = userResults.emailAddress;\n\n          // Create a Firebase account and get the Custom Auth Token.\n          const firebaseToken = await createFirebaseAccount(linkedInUserID, userName, profilePic, email, accessToken);\n          // Serve an HTML page that signs the user in and updates the user profile.\n          res.jsonp({\n            token: firebaseToken,\n          });\n        });\n      });\n    });\n  } catch (error) {\n    return res.jsonp({ error: error.toString });\n  }\n});\n\n/**\n * Creates a Firebase account with the given user profile and returns a custom auth token allowing\n * signing-in this account.\n * Also saves the accessToken to the datastore at /linkedInAccessToken/$uid\n *\n * @returns {Promise<string>} The Firebase custom auth token in a promise.\n */\nasync function createFirebaseAccount(linkedinID, displayName, photoURL, email, accessToken) {\n  // The UID we'll assign to the user.\n  const uid = `linkedin:${linkedinID}`;\n\n  // Save the access token tot he Firebase Realtime Database.\n  const databaseTask = admin.database().ref(`/linkedInAccessToken/${uid}`).set(accessToken);\n\n  // Create or update the user account.\n  const userCreationTask = admin.auth().updateUser(uid, {\n    displayName: displayName,\n    photoURL: photoURL,\n    email: email,\n    emailVerified: true,\n  }).catch((error) => {\n    // If user does not exists we create it.\n    if (error.code === 'auth/user-not-found') {\n      return admin.auth().createUser({\n        uid: uid,\n        displayName: displayName,\n        photoURL: photoURL,\n        email: email,\n        emailVerified: true,\n      });\n    }\n    throw error;\n  });\n\n  // Wait for all async task to complete then generate and return a custom auth token.\n  await Promise.all([userCreationTask, databaseTask]);\n  // Create a Firebase custom auth token.\n  const token = await admin.auth().createCustomToken(uid);\n  functions.logger.log(\n    'Created Custom token for UID \"',\n    uid,\n    '\" Token:',\n    token\n  );\n  return token;\n}\n"
  },
  {
    "path": "Node-1st-gen/linkedin-auth/functions/package.json",
    "content": "{\n  \"name\": \"linkedin-auth-functions\",\n  \"description\": \"Authenticate with LinkedIn Firebase Functions sample\",\n  \"dependencies\": {\n    \"cookie-parser\": \"^1.4.6\",\n    \"eslint-plugin-promise\": \"^7.2.1\",\n    \"firebase-admin\": \"^13.0.2\",\n    \"firebase-functions\": \"7.0.0\",\n    \"node-linkedin\": \"^0.5.6\"\n  },\n  \"devDependencies\": {\n    \"eslint\": \"^8.57.1\"\n  },\n  \"scripts\": {\n    \"lint\": \"./node_modules/.bin/eslint --max-warnings=0 .\",\n    \"serve\": \"firebase emulators:start --only functions\",\n    \"shell\": \"firebase functions:shell\",\n    \"start\": \"npm run shell\",\n    \"deploy\": \"firebase deploy --only functions\",\n    \"logs\": \"firebase functions:log\",\n    \"compile\": \"cp ../../../tsconfig.template.json ./tsconfig-compile.json && tsc --project tsconfig-compile.json\"\n  },\n  \"engines\": {\n    \"node\": \"20\"\n  },\n  \"private\": true\n}\n"
  },
  {
    "path": "Node-1st-gen/linkedin-auth/main.css",
    "content": "/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nhtml, body {\n  font-family: 'Roboto', 'Helvetica', sans-serif;\n}\n.mdl-grid {\n  max-width: 1024px;\n  margin: auto;\n}\n.mdl-card {\n  min-height: 0;\n  padding-bottom: 5px;\n}\n.mdl-layout__header-row {\n  padding: 0;\n}\n#message-form {\n  display: flex;\n  flex-direction: column;\n}\n#message-form button {\n  max-width: 300px;\n}\n#message-list {\n  padding: 0;\n  width: 100%;\n}\n#message-list > div {\n  padding: 15px;\n  border-bottom: 1px #f1f1f1 solid;\n}\nh3 {\n  background: url('firebase-logo.png') no-repeat;\n  background-size: 40px;\n  padding-left: 50px;\n}\n#demo-signed-out-card,\n#demo-signed-in-card,\n#demo-subscribe-button,\n#demo-unsubscribe-button,\n#demo-subscribed-text-container,\n#demo-unsubscribed-text-container {\n  display: none;\n}\n#demo-subscribe-button,\n#demo-unsubscribe-button {\n  margin-right: 20px;\n}"
  },
  {
    "path": "Node-1st-gen/linkedin-auth/public/index.html",
    "content": "<!doctype html>\n<!--\n  Copyright 2016 Google Inc. All rights reserved.\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n      https://www.apache.org/licenses/LICENSE-2.0\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License\n-->\n<html lang=\"en\">\n<head>\n  <meta charset=\"utf-8\">\n  <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n  <meta name=\"description\" content=\"Demonstrates of to authorize Firebase with LinkedIn auth using Firebase Functions\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n  <title>Firebase Functions demo to Sign In with LinkedIn</title>\n\n  <!-- Material Design Lite -->\n  <link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/icon?family=Material+Icons\">\n  <link rel=\"stylesheet\" href=\"https://code.getmdl.io/1.1.3/material.blue_grey-orange.min.css\">\n  <script defer src=\"https://code.getmdl.io/1.1.3/material.min.js\"></script>\n\n  <link rel=\"stylesheet\" href=\"main.css\">\n</head>\n<body>\n<div class=\"demo-layout mdl-layout mdl-js-layout mdl-layout--fixed-header\">\n\n  <!-- Header section containing title -->\n  <header class=\"mdl-layout__header mdl-color-text--white mdl-color--light-blue-700\">\n    <div class=\"mdl-cell mdl-cell--12-col mdl-cell--12-col-tablet mdl-grid\">\n      <div class=\"mdl-layout__header-row mdl-cell mdl-cell--12-col mdl-cell--12-col-tablet mdl-cell--8-col-desktop\">\n        <h3>Sign in with LinkedIn demo</h3>\n      </div>\n    </div>\n  </header>\n  <main class=\"mdl-layout__content mdl-color--grey-100\">\n    <div class=\"mdl-cell--12-col mdl-cell--12-col-tablet mdl-grid\">\n\n      <!-- Card containing the sign-in UI -->\n      <div id=\"demo-signed-out-card\" class=\"mdl-card mdl-shadow--2dp mdl-cell\">\n        <div class=\"mdl-card__supporting-text mdl-color-text--grey-600\">\n          <p>\n            This web application demonstrates how you can Sign In with LinkedIn to Firebase Authentication.\n            <strong>Now sign in!</strong>\n          </p>\n          <button id=\"demo-sign-in-button\" class=\"mdl-color-text--grey-700 mdl-button--raised mdl-button mdl-js-button mdl-js-ripple-effect\"></button>\n        </div>\n      </div>\n\n      <!-- Card containing the signed-in UI -->\n      <div id=\"demo-signed-in-card\" class=\"mdl-card mdl-shadow--2dp mdl-cell\">\n        <div class=\"mdl-card__supporting-text mdl-color-text--grey-600\">\n          <p>\n            Welcome <span id=\"demo-name-container\"></span><br>\n            Your email is: <span id=\"demo-email-container\"></span><br>\n            Your Firebase User ID is: <span id=\"demo-uid-container\"></span><br>\n            Your profile picture: <img id=\"demo-profile-pic\">\n          </p>\n          <button id=\"demo-sign-out-button\" class=\"mdl-color-text--grey-700 mdl-button--raised mdl-button mdl-js-button mdl-js-ripple-effect\">Sign out</button>\n          <button id=\"demo-delete-button\" class=\"mdl-color-text--grey-700 mdl-button--raised mdl-button mdl-js-button mdl-js-ripple-effect\">Delete account</button>\n        </div>\n      </div>\n    </div>\n  </main>\n</div>\n\n\n<!-- Import and configure the Firebase SDK -->\n<!-- These scripts are made available when the app is served or deployed on Firebase Hosting -->\n<!-- If you do not serve/host your project using Firebase Hosting see https://firebase.google.com/docs/web/setup -->\n<script src=\"/__/firebase/10.0.0/firebase-app-compat.js\"></script>\n<script src=\"/__/firebase/10.0.0/firebase-auth-compat.js\"></script>\n<script src=\"/__/firebase/init.js\"></script>\n\n<script src=\"main.js\"></script>\n</body>\n</html>\n"
  },
  {
    "path": "Node-1st-gen/linkedin-auth/public/main.css",
    "content": "/**\n * Copyright 2016 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nhtml, body {\n  font-family: 'Roboto', 'Helvetica', sans-serif;\n}\n.mdl-grid {\n  max-width: 1024px;\n  margin: auto;\n}\n.mdl-card {\n  min-height: 0;\n  padding-bottom: 5px;\n}\n.mdl-layout__header-row {\n  padding: 0;\n}\nh3 {\n  background: url('firebase-logo.png') no-repeat;\n  background-size: 40px;\n  padding-left: 50px;\n}\n#demo-signed-out-card,\n#demo-signed-in-card {\n  display: none;\n}\n#demo-profile-pic {\n  height: 60px;\n  width: 60px;\n  border-radius: 30px;\n  margin-left: calc(50% - 30px);\n  margin-top: 10px;\n}\n#demo-name-container,\n#demo-email-container,\n#demo-uid-container {\n  font-weight: bold;\n}\n#demo-sign-in-button {\n  background-image: url('linkedIn-button.png');\n  background-size: 100% 100%;\n  width: 197px;\n  height: 27px;\n}\n#demo-delete-button {\n  margin-left: 20px;\n}\n"
  },
  {
    "path": "Node-1st-gen/linkedin-auth/public/main.js",
    "content": "/**\n * Copyright 2016 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use strict';\n\n// Initializes the Demo.\nfunction Demo() {\n  document.addEventListener('DOMContentLoaded', function() {\n    // Shortcuts to DOM Elements.\n    this.signInButton = document.getElementById('demo-sign-in-button');\n    this.signOutButton = document.getElementById('demo-sign-out-button');\n    this.emailContainer = document.getElementById('demo-email-container');\n    this.nameContainer = document.getElementById('demo-name-container');\n    this.deleteButton = document.getElementById('demo-delete-button');\n    this.uidContainer = document.getElementById('demo-uid-container');\n    this.profilePic = document.getElementById('demo-profile-pic');\n    this.signedOutCard = document.getElementById('demo-signed-out-card');\n    this.signedInCard = document.getElementById('demo-signed-in-card');\n\n    // Bind events.\n    this.signInButton.addEventListener('click', this.signIn.bind(this));\n    this.signOutButton.addEventListener('click', this.signOut.bind(this));\n    this.deleteButton.addEventListener('click', this.deleteAccount.bind(this));\n    firebase.auth().onAuthStateChanged(this.onAuthStateChanged.bind(this));\n  }.bind(this));\n}\n\n// Triggered on Firebase auth state change.\nDemo.prototype.onAuthStateChanged = function(user) {\n  if (user) {\n    this.nameContainer.innerText = user.displayName;\n    this.emailContainer.innerText = user.email;\n    this.uidContainer.innerText = user.uid;\n    this.profilePic.src = user.photoURL;\n    this.signedOutCard.style.display = 'none';\n    this.signedInCard.style.display = 'block';\n  } else {\n    this.signedOutCard.style.display = 'block';\n    this.signedInCard.style.display = 'none';\n  }\n};\n\n// Initiates the sign-in flow using LinkedIn sign in in a popup.\nDemo.prototype.signIn = function() {\n  // Open the popup that will start the auth flow.\n  window.open('popup.html', 'name', 'height=585,width=400');\n};\n\n// Signs-out of Firebase.\nDemo.prototype.signOut = function() {\n  firebase.auth().signOut();\n};\n\n// Deletes the user's account.\nDemo.prototype.deleteAccount = function() {\n  firebase.auth().currentUser.delete().then(function() {\n    window.alert('Account deleted');\n  }).catch(function(error) {\n    if (error.code === 'auth/requires-recent-login') {\n      window.alert('You need to have recently signed-in to delete your account. Please sign-in and try again.');\n      firebase.auth().signOut();\n    }\n  });\n};\n\n// Load the demo.\nnew Demo();\n"
  },
  {
    "path": "Node-1st-gen/linkedin-auth/public/popup.html",
    "content": "<!doctype html>\n<!--\n  Copyright 2016 Google Inc. All rights reserved.\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n      https://www.apache.org/licenses/LICENSE-2.0\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License\n-->\n<html lang=\"en\">\n<head>\n  <meta charset=\"utf-8\">\n  <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n  <meta name=\"description\" content=\"Demonstrates how to authorize Firebase with LinkedIn auth using Firebase Functions\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n  <title>Authenticate with LinkedIn</title>\n</head>\n<body>\n\nPlease wait...\n\n\n<!-- Import and configure the Firebase SDK -->\n<!-- These scripts are made available when the app is served or deployed on Firebase Hosting -->\n<!-- If you do not serve/host your project using Firebase Hosting see https://firebase.google.com/docs/web/setup -->\n<script src=\"/__/firebase/10.0.0/firebase-app-compat.js\"></script>\n<script src=\"/__/firebase/10.0.0/firebase-auth-compat.js\"></script>\n<script src=\"/__/firebase/init.js\"></script>\n\n<script>\n  /**\n   * Returns the value of the given URL query parameter.\n   */\n  function getURLParameter(name) {\n    return decodeURIComponent((new RegExp('[?|&]' + name + '=' + '([^&;]+?)(&|#|;|$)').exec(location.search) ||\n        [null, ''])[1].replace(/\\+/g, '%20')) || null;\n  }\n\n  /**\n   * Returns the ID of the Firebase project.\n   */\n  function getFirebaseProjectId() {\n    return firebase.app().options.authDomain.split('.')[0];\n  }\n\n  /**\n   * This callback is called by the JSONP callback of the 'token' Firebase Function with the Firebase auth token.\n   */\n  function tokenReceived(data) {\n    if (data.token) {\n      firebase.auth().signInWithCustomToken(data.token).then(function() {\n        window.close();\n      });\n    } else {\n      console.error(data);\n      document.body.innerText = 'Error in the token Function: ' + data.error;\n    }\n  }\n\n  var code = getURLParameter('code');\n  var state = getURLParameter('state');\n  var error = getURLParameter('error');\n  if (error) {\n    document.body.innerText = 'Error back from the LinkedIn auth page: ' + error;\n  } else if(!code) {\n    // Start the auth flow.\n    window.location.href  = '/redirect';\n  } else {\n    // Use JSONP to load the 'token' Firebase Function to exchange the auth code against a Firebase custom token.\n    const script = document.createElement('script');\n    script.type = 'text/javascript';\n    // This is the URL to the HTTP triggered 'token' Firebase Function.\n    // See https://firebase.google.com/docs/functions.\n    var tokenFunctionURL = '/token';\n    script.src = tokenFunctionURL +\n        '?code=' + encodeURIComponent(code) +\n        '&state=' + encodeURIComponent(state) +\n        '&callback=' + tokenReceived.name;\n    document.head.appendChild(script);\n  }\n</script>\n</body>\n</html>\n"
  },
  {
    "path": "Node-1st-gen/message-translation/README.md",
    "content": "# Automatic message translation using the Google Translate API.\n\nThis template shows how to translate a new message in a given language into multiple destination languages.\n\n## Functions Code\n\nSee file [functions/index.js](functions/index.js) for the code.\n\nThis is done by using the Google Translate API to translate the new message. The translated output is written into a fanned out structure using the langauge code as the key.\n\nThe dependencies are listed in [functions/package.json](functions/package.json).\n\n## Sample Database Structure\n\nAs an example we'll be using a simple database structure:\n\n```\n/functions-project-12345\n    /messages\n        /en\n            /key-123456\n                translated: false\n                text: \"Hey Bob! How Are you?\"\n            /key-123457\n                translated: false\n                text: \"Hey Mat! How Are you?\"\n```\n\nWhen a new message is received we lookup the language message and automatically translate in all other required languages:\n\n```\n/functions-project-12345\n    /messages\n        /en\n            /key-123456\n                translated: true\n                text: \"Hey Bob! How Are you?\"\n            /key-123457\n                translated: false\n                text: \"Hey Mat! How Are you?\"\n        /fr\n            /key-123456\n                translated: true\n                text: \"Salut Bob! Comment ca va?\"\n```\n"
  },
  {
    "path": "Node-1st-gen/message-translation/firebase.json",
    "content": "{\n    \"functions\": {\n      \"codebase\": \"message-translation\"\n    }\n}"
  },
  {
    "path": "Node-1st-gen/message-translation/functions/.eslintrc.json",
    "content": "{\n  \"parserOptions\": {\n    // Required for certain syntax usages\n    \"ecmaVersion\": 2022\n  },\n  \"plugins\": [\n    \"promise\"\n  ],\n  \"extends\": \"eslint:recommended\",\n  \"rules\": {\n    // Removed rule \"disallow the use of console\" from recommended eslint rules\n    \"no-console\": \"off\",\n\n    // Removed rule \"disallow multiple spaces in regular expressions\" from recommended eslint rules\n    \"no-regex-spaces\": \"off\",\n\n    // Removed rule \"disallow the use of debugger\" from recommended eslint rules\n    \"no-debugger\": \"off\",\n\n    // Removed rule \"disallow unused variables\" from recommended eslint rules\n    \"no-unused-vars\": \"off\",\n\n    // Removed rule \"disallow mixed spaces and tabs for indentation\" from recommended eslint rules\n    \"no-mixed-spaces-and-tabs\": \"off\",\n\n    // Removed rule \"disallow the use of undeclared variables unless mentioned in /*global */ comments\" from recommended eslint rules\n    \"no-undef\": \"off\",\n\n    // Warn against template literal placeholder syntax in regular strings\n    \"no-template-curly-in-string\": 1,\n\n    // Warn if return statements do not either always or never specify values\n    \"consistent-return\": 1,\n\n    // Warn if no return statements in callbacks of array methods\n    \"array-callback-return\": 1,\n\n    // Requre the use of === and !==\n    \"eqeqeq\": 2,\n\n    // Disallow the use of alert, confirm, and prompt\n    \"no-alert\": 2,\n\n    // Disallow the use of arguments.caller or arguments.callee\n    \"no-caller\": 2,\n\n    // Disallow null comparisons without type-checking operators\n    \"no-eq-null\": 2,\n\n    // Disallow the use of eval()\n    \"no-eval\": 2,\n\n    // Warn against extending native types\n    \"no-extend-native\": 1,\n\n    // Warn against unnecessary calls to .bind()\n    \"no-extra-bind\": 1,\n\n    // Warn against unnecessary labels    \n    \"no-extra-label\": 1,\n\n    // Disallow leading or trailing decimal points in numeric literals\n    \"no-floating-decimal\": 2,\n\n    // Warn against shorthand type conversions\n    \"no-implicit-coercion\": 1,\n\n    // Warn against function declarations and expressions inside loop statements\n    \"no-loop-func\": 1,\n\n    // Disallow new operators with the Function object\n    \"no-new-func\": 2,\n\n    // Warn against new operators with the String, Number, and Boolean objects\n    \"no-new-wrappers\": 1,\n\n    // Disallow throwing literals as exceptions\n    \"no-throw-literal\": 2,\n\n    // Require using Error objects as Promise rejection reasons\n    \"prefer-promise-reject-errors\": 2,\n\n    // Enforce “for” loop update clause moving the counter in the right direction\n    \"for-direction\": 2,\n\n    // Enforce return statements in getters\n    \"getter-return\": 2,\n\n    // Disallow await inside of loops\n    \"no-await-in-loop\": 2,\n\n    // Disallow comparing against -0\n    \"no-compare-neg-zero\": 2,\n\n    // Warn against catch clause parameters from shadowing variables in the outer scope\n    \"no-catch-shadow\": 1,\n\n    // Disallow identifiers from shadowing restricted names\n    \"no-shadow-restricted-names\": 2,\n\n    // Enforce return statements in callbacks of array methods\n    \"callback-return\": 2,\n\n    // Require error handling in callbacks\n    \"handle-callback-err\": 2,\n\n    // Warn against string concatenation with __dirname and __filename\n    \"no-path-concat\": 1,\n\n    // Prefer using arrow functions for callbacks\n    \"prefer-arrow-callback\": 1,\n\n    // Return inside each then() to create readable and reusable Promise chains.\n    \"promise/always-return\": 2,\n\n    //Enforces the use of catch() on un-returned promises\n    \"promise/catch-or-return\": 2,\n\n    // Warn against nested then() or catch() statements\n    \"promise/no-nesting\": 1\n  }\n}\n"
  },
  {
    "path": "Node-1st-gen/message-translation/functions/index.js",
    "content": "/**\n * Copyright 2016 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use strict';\n\nconst functions = require('firebase-functions/v1');\nconst admin = require('firebase-admin');\nadmin.initializeApp();\nconst { TranslationServiceClient } = require('@google-cloud/translate');\n\nconst translate = new TranslationServiceClient();\n\n// List of output languages.\nconst LANGUAGES = ['en', 'es', 'de', 'fr', 'sv', 'ga', 'it', 'jp'];\n\n// Translate an incoming message.\nexports.translate = functions.database.ref('/messages/{languageID}/{messageID}').onWrite(\n    (change, context) => {\n      const snapshot = change.after;\n      if (snapshot.val().translated) {\n        return null;\n      }\n      const promises = [];\n      for (let i = 0; i < LANGUAGES.length; i++) {\n        const language = LANGUAGES[i];\n        if (language !== context.params.languageID) {\n          promises.push(async () => {\n            const results = await translate.translateText({ \n              contents: [snapshot.val().message], \n              sourceLanguageCode: context.params.languageID, \n              targetLanguageCode: language \n            });\n            return admin.database().ref(`/messages/${language}/${snapshot.key}`).set({\n              message: results[0],\n              translated: true,\n            });\n          });\n        }\n      }\n      return Promise.all(promises);\n    });\n"
  },
  {
    "path": "Node-1st-gen/message-translation/functions/package.json",
    "content": "{\n  \"name\": \"message-translation-functions\",\n  \"description\": \"Transalte Messages Firebase Functions sample\",\n  \"dependencies\": {\n    \"@google-cloud/translate\": \"^5.3.0\",\n    \"eslint-plugin-promise\": \"^7.2.1\",\n    \"firebase-admin\": \"^13.0.2\",\n    \"firebase-functions\": \"7.0.0\"\n  },\n  \"devDependencies\": {\n    \"eslint\": \"^8.57.1\"\n  },\n  \"scripts\": {\n    \"lint\": \"./node_modules/.bin/eslint --max-warnings=0 .\",\n    \"serve\": \"firebase emulators:start --only functions\",\n    \"shell\": \"firebase functions:shell\",\n    \"start\": \"npm run shell\",\n    \"deploy\": \"firebase deploy --only functions\",\n    \"logs\": \"firebase functions:log\",\n    \"compile\": \"cp ../../../tsconfig.template.json ./tsconfig-compile.json && tsc --project tsconfig-compile.json\"\n  },\n  \"engines\": {\n    \"node\": \"20\"\n  },\n  \"private\": true\n}\n"
  },
  {
    "path": "Node-1st-gen/minimal-webhook/README.md",
    "content": "# Webhook upon Database writes\n\nThis Function shows how a Database write can trigger a request to a hardcoded callback URL (a Webhook). The content of the modified Data is sent to the Webhook.\n\n## Functions Code\n\nSee file [functions/index.js](functions/index.js) for the code.\n\nWe're sending a request to an external webhook. As a sample we're using a Request Bin from [requestb.in](http://requestb.in) that will receive the Data so you can visualize it easily. make sure you create your own Request Bin and update the sample with it.\n\nNote: You will need to enable billing on your Firebase the project by switching to the **Blaze** plan, this is currently needed to be able to perform HTTP requests to external services from a Cloud Function.\n\n## Sample Database Structure\n\nAs an example we'll be using a database structure where adding or updating an element under `/hooks` will trigger the Webhook:\n\n```\n/functions-project-12345\n    /hooks\n        /key-123456\n            stuff: \"Whatever\"\n            more_stuff: \"Cool\"\n        /key-123457\n            things: \"A car\"\n            more_things: \"A truck\"\n```\n"
  },
  {
    "path": "Node-1st-gen/minimal-webhook/firebase.json",
    "content": "{\n    \"functions\": {\n      \"codebase\": \"minimal-webhook\"\n    }\n}"
  },
  {
    "path": "Node-1st-gen/minimal-webhook/functions/.eslintrc.json",
    "content": "{\n  \"parserOptions\": {\n    // Required for certain syntax usages\n    \"ecmaVersion\": 2022\n  },\n  \"plugins\": [\n    \"promise\"\n  ],\n  \"extends\": \"eslint:recommended\",\n  \"rules\": {\n    // Removed rule \"disallow the use of console\" from recommended eslint rules\n    \"no-console\": \"off\",\n\n    // Removed rule \"disallow multiple spaces in regular expressions\" from recommended eslint rules\n    \"no-regex-spaces\": \"off\",\n\n    // Removed rule \"disallow the use of debugger\" from recommended eslint rules\n    \"no-debugger\": \"off\",\n\n    // Removed rule \"disallow unused variables\" from recommended eslint rules\n    \"no-unused-vars\": \"off\",\n\n    // Removed rule \"disallow mixed spaces and tabs for indentation\" from recommended eslint rules\n    \"no-mixed-spaces-and-tabs\": \"off\",\n\n    // Removed rule \"disallow the use of undeclared variables unless mentioned in /*global */ comments\" from recommended eslint rules\n    \"no-undef\": \"off\",\n\n    // Warn against template literal placeholder syntax in regular strings\n    \"no-template-curly-in-string\": 1,\n\n    // Warn if return statements do not either always or never specify values\n    \"consistent-return\": 1,\n\n    // Warn if no return statements in callbacks of array methods\n    \"array-callback-return\": 1,\n\n    // Requre the use of === and !==\n    \"eqeqeq\": 2,\n\n    // Disallow the use of alert, confirm, and prompt\n    \"no-alert\": 2,\n\n    // Disallow the use of arguments.caller or arguments.callee\n    \"no-caller\": 2,\n\n    // Disallow null comparisons without type-checking operators\n    \"no-eq-null\": 2,\n\n    // Disallow the use of eval()\n    \"no-eval\": 2,\n\n    // Warn against extending native types\n    \"no-extend-native\": 1,\n\n    // Warn against unnecessary calls to .bind()\n    \"no-extra-bind\": 1,\n\n    // Warn against unnecessary labels    \n    \"no-extra-label\": 1,\n\n    // Disallow leading or trailing decimal points in numeric literals\n    \"no-floating-decimal\": 2,\n\n    // Warn against shorthand type conversions\n    \"no-implicit-coercion\": 1,\n\n    // Warn against function declarations and expressions inside loop statements\n    \"no-loop-func\": 1,\n\n    // Disallow new operators with the Function object\n    \"no-new-func\": 2,\n\n    // Warn against new operators with the String, Number, and Boolean objects\n    \"no-new-wrappers\": 1,\n\n    // Disallow throwing literals as exceptions\n    \"no-throw-literal\": 2,\n\n    // Require using Error objects as Promise rejection reasons\n    \"prefer-promise-reject-errors\": 2,\n\n    // Enforce “for” loop update clause moving the counter in the right direction\n    \"for-direction\": 2,\n\n    // Enforce return statements in getters\n    \"getter-return\": 2,\n\n    // Disallow await inside of loops\n    \"no-await-in-loop\": 2,\n\n    // Disallow comparing against -0\n    \"no-compare-neg-zero\": 2,\n\n    // Warn against catch clause parameters from shadowing variables in the outer scope\n    \"no-catch-shadow\": 1,\n\n    // Disallow identifiers from shadowing restricted names\n    \"no-shadow-restricted-names\": 2,\n\n    // Enforce return statements in callbacks of array methods\n    \"callback-return\": 2,\n\n    // Require error handling in callbacks\n    \"handle-callback-err\": 2,\n\n    // Warn against string concatenation with __dirname and __filename\n    \"no-path-concat\": 1,\n\n    // Prefer using arrow functions for callbacks\n    \"prefer-arrow-callback\": 1,\n\n    // Return inside each then() to create readable and reusable Promise chains.\n    \"promise/always-return\": 2,\n\n    //Enforces the use of catch() on un-returned promises\n    \"promise/catch-or-return\": 2,\n\n    // Warn against nested then() or catch() statements\n    \"promise/no-nesting\": 1\n  }\n}\n"
  },
  {
    "path": "Node-1st-gen/minimal-webhook/functions/index.js",
    "content": "/**\n * Copyright 2016 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use strict';\n\nconst functions = require('firebase-functions/v1');\n\n// This is the URL that we will callback and send the content of the updated data node.\n// As an example we're using a Request Bin from http://requestb.in\n// TODO: Make sure you create your own Request Bin and change this URL to try this sample.\nconst WEBHOOK_URL = 'http://requestb.in/1mqw97l1';\n\n// Reads the content of the node that triggered the function and sends it to the registered Webhook\n// URL.\nexports.webhook = functions.database.ref('/hooks/{hookId}').onCreate(async (snap) => {\n  const response = await fetch(WEBHOOK_URL, {\n    method: 'post',\n    body: JSON.stringify(snap.val()),\n    headers: {'Content-Type': 'application/json'}\n  });\n\n  if (!response.ok) {\n    throw new Error(`HTTP Error: ${response.status}`);\n  }\n  functions.logger.log('SUCCESS! Posted', snap.ref);\n});\n"
  },
  {
    "path": "Node-1st-gen/minimal-webhook/functions/package.json",
    "content": "{\n  \"name\": \"minimal-webhook-functions\",\n  \"description\": \"Queries a Webhook Firebase Functions sample\",\n  \"dependencies\": {\n    \"eslint-plugin-promise\": \"^7.2.1\",\n    \"firebase-admin\": \"^13.0.2\",\n    \"firebase-functions\": \"7.0.0\"\n  },\n  \"devDependencies\": {\n    \"eslint\": \"^8.57.1\"\n  },\n  \"scripts\": {\n    \"lint\": \"./node_modules/.bin/eslint --max-warnings=0 .\",\n    \"serve\": \"firebase emulators:start --only functions\",\n    \"shell\": \"firebase functions:shell\",\n    \"start\": \"npm run shell\",\n    \"deploy\": \"firebase deploy --only functions\",\n    \"logs\": \"firebase functions:log\",\n    \"compile\": \"cp ../../../tsconfig.template.json ./tsconfig-compile.json && tsc --project tsconfig-compile.json\"\n  },\n  \"engines\": {\n    \"node\": \"20\"\n  },\n  \"private\": true\n}\n"
  },
  {
    "path": "Node-1st-gen/moderate-images/README.md",
    "content": "# Automatically Moderate Images\n\nThis sample demonstrates how to automatically moderate offensive images uploaded to Firebase Storage. It uses The Google Cloud Vision API to detect if the image contains adult or violent content and if so uses ImageMagick to blur the image.\n\n## Functions Code\n\nSee file [functions/index.js](functions/index.js) for the moderation code.\n\nThe detection of adult and violent content in an image is done using The [Google Cloud Vision API](https://cloud.google.com/vision/).\nThe image blurring is performed using ImageMagick which is installed by default on all Cloud Functions instances. The image is first downloaded locally from the Firebase Storage bucket to the `tmp` folder using the [google-cloud](https://github.com/GoogleCloudPlatform/google-cloud-node) SDK.\n\nThe dependencies are listed in [functions/package.json](functions/package.json).\n\n## Trigger rules\n\nThe function triggers on upload of any file to your Firebase project's default Cloud Storage bucket.\n\n## Setting up the sample\n\n 1. Create a Firebase project on the [Firebase Console](https://console.firebase.google.com).\n 1. In the Google Cloud Console [enable the **Google Cloud Vision API**](https://console.cloud.google.com/apis/api/vision.googleapis.com/overview?project=_). Note: Billing is required to enable the Cloud Vision API so enable Billing on your Firebase project by switching to the Blaze plan. For more information have a look at the [pricing page](https://firebase.google.com/pricing/).\n 1. Clone or download this repo and open the `moderate-image` directory.\n 1. You must have the Firebase CLI installed. If you don't have it install it with `npm install -g firebase-tools` and then configure it with `firebase login`.\n 1. Configure the CLI locally by using `firebase use --add` and select your project in the list.\n 1. Install dependencies locally by running: `cd functions; npm install; cd -`\n\n## Deploy and test\n\nTo test the sample:\n\n1. Deploy your Cloud Functions using `firebase deploy`\n1. Go to the Firebase Console **Storage** tab and upload an image that contains adult or violent content. After a short time, refresh the page. You'll see a new folder that contains a blurred version of your uploaded image.\n"
  },
  {
    "path": "Node-1st-gen/moderate-images/firebase.json",
    "content": "{\n    \"functions\": {\n      \"codebase\": \"moderate-images\"\n    }\n}\n"
  },
  {
    "path": "Node-1st-gen/moderate-images/functions/.eslintrc.json",
    "content": "{\n  \"parserOptions\": {\n    // Required for certain syntax usages\n    \"ecmaVersion\": 2022\n  },\n  \"plugins\": [\n    \"promise\"\n  ],\n  \"extends\": \"eslint:recommended\",\n  \"rules\": {\n    // Removed rule \"disallow the use of console\" from recommended eslint rules\n    \"no-console\": \"off\",\n\n    // Removed rule \"disallow multiple spaces in regular expressions\" from recommended eslint rules\n    \"no-regex-spaces\": \"off\",\n\n    // Removed rule \"disallow the use of debugger\" from recommended eslint rules\n    \"no-debugger\": \"off\",\n\n    // Removed rule \"disallow unused variables\" from recommended eslint rules\n    \"no-unused-vars\": \"off\",\n\n    // Removed rule \"disallow mixed spaces and tabs for indentation\" from recommended eslint rules\n    \"no-mixed-spaces-and-tabs\": \"off\",\n\n    // Removed rule \"disallow the use of undeclared variables unless mentioned in /*global */ comments\" from recommended eslint rules\n    \"no-undef\": \"off\",\n\n    // Warn against template literal placeholder syntax in regular strings\n    \"no-template-curly-in-string\": 1,\n\n    // Warn if return statements do not either always or never specify values\n    \"consistent-return\": 1,\n\n    // Warn if no return statements in callbacks of array methods\n    \"array-callback-return\": 1,\n\n    // Requre the use of === and !==\n    \"eqeqeq\": 2,\n\n    // Disallow the use of alert, confirm, and prompt\n    \"no-alert\": 2,\n\n    // Disallow the use of arguments.caller or arguments.callee\n    \"no-caller\": 2,\n\n    // Disallow null comparisons without type-checking operators\n    \"no-eq-null\": 2,\n\n    // Disallow the use of eval()\n    \"no-eval\": 2,\n\n    // Warn against extending native types\n    \"no-extend-native\": 1,\n\n    // Warn against unnecessary calls to .bind()\n    \"no-extra-bind\": 1,\n\n    // Warn against unnecessary labels    \n    \"no-extra-label\": 1,\n\n    // Disallow leading or trailing decimal points in numeric literals\n    \"no-floating-decimal\": 2,\n\n    // Warn against shorthand type conversions\n    \"no-implicit-coercion\": 1,\n\n    // Warn against function declarations and expressions inside loop statements\n    \"no-loop-func\": 1,\n\n    // Disallow new operators with the Function object\n    \"no-new-func\": 2,\n\n    // Warn against new operators with the String, Number, and Boolean objects\n    \"no-new-wrappers\": 1,\n\n    // Disallow throwing literals as exceptions\n    \"no-throw-literal\": 2,\n\n    // Require using Error objects as Promise rejection reasons\n    \"prefer-promise-reject-errors\": 2,\n\n    // Enforce “for” loop update clause moving the counter in the right direction\n    \"for-direction\": 2,\n\n    // Enforce return statements in getters\n    \"getter-return\": 2,\n\n    // Disallow await inside of loops\n    \"no-await-in-loop\": 2,\n\n    // Disallow comparing against -0\n    \"no-compare-neg-zero\": 2,\n\n    // Warn against catch clause parameters from shadowing variables in the outer scope\n    \"no-catch-shadow\": 1,\n\n    // Disallow identifiers from shadowing restricted names\n    \"no-shadow-restricted-names\": 2,\n\n    // Enforce return statements in callbacks of array methods\n    \"callback-return\": 2,\n\n    // Require error handling in callbacks\n    \"handle-callback-err\": 2,\n\n    // Warn against string concatenation with __dirname and __filename\n    \"no-path-concat\": 1,\n\n    // Prefer using arrow functions for callbacks\n    \"prefer-arrow-callback\": 1,\n\n    // Return inside each then() to create readable and reusable Promise chains.\n    \"promise/always-return\": 2,\n\n    //Enforces the use of catch() on un-returned promises\n    \"promise/catch-or-return\": 2,\n\n    // Warn against nested then() or catch() statements\n    \"promise/no-nesting\": 1\n  }\n}\n"
  },
  {
    "path": "Node-1st-gen/moderate-images/functions/index.js",
    "content": "/**\n * Copyright 2016 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use strict';\n\n// Firebase setup\nconst functions = require('firebase-functions/v1');\nconst admin = require('firebase-admin');\nadmin.initializeApp();\n\n// Node.js core modules\nconst fs = require('fs');\nconst mkdirp = fs.promises.mkdir;\nconst {promisify} = require('util');\nconst exec = promisify(require('child_process').exec);\nconst path = require('path');\nconst os = require('os');\n\n// Vision API\nconst vision = require('@google-cloud/vision');\n\n// Where we'll save blurred images\nconst BLURRED_FOLDER = 'blurred';\n\n/**\n * When an image is uploaded we check if it is flagged as Adult or Violence by the Cloud Vision\n * API and if it is we blur it using ImageMagick.\n */\nexports.blurOffensiveImages = functions.storage.object().onFinalize(async (object) => {\n  // Ignore things we've already blurred\n  if (object.name.startsWith(`${BLURRED_FOLDER}/`)) {\n    functions.logger.log(`Ignoring upload \"${object.name}\" because it was already blurred.`);\n    return null;\n  }\n  \n  // Check the image content using the Cloud Vision API.\n  const visionClient = new vision.ImageAnnotatorClient();\n  const data = await visionClient.safeSearchDetection(\n    `gs://${object.bucket}/${object.name}`\n  );\n  const safeSearchResult = data[0].safeSearchAnnotation;\n  functions.logger.log(`SafeSearch results on image \"${object.name}\"`, safeSearchResult);\n\n  // Tune these detection likelihoods to suit your app.\n  // The current settings show the most strict configuration\n  // Available likelihoods are defined in https://cloud.google.com/vision/docs/reference/rest/v1/AnnotateImageResponse#likelihood\n  if (\n    safeSearchResult.adult !== 'VERY_UNLIKELY' ||\n    safeSearchResult.spoof !== 'VERY_UNLIKELY' ||\n    safeSearchResult.medical !== 'VERY_UNLIKELY' ||\n    safeSearchResult.violence !== 'VERY_UNLIKELY' ||\n    safeSearchResult.racy !== 'VERY_UNLIKELY'\n  ) {\n    functions.logger.log('Offensive image found. Blurring.');\n    return blurImage(object.name, object.bucket, object.metadata);\n  }\n\n  return null;\n});\n\n/**\n * Blurs the given image located in the given bucket using ImageMagick.\n */\nasync function blurImage(filePath, bucketName, metadata) {\n  const tempLocalFile = path.join(os.tmpdir(), filePath);\n  const tempLocalDir = path.dirname(tempLocalFile);\n  const bucket = admin.storage().bucket(bucketName);\n\n  // Create the temp directory where the storage file will be downloaded.\n  await mkdirp(tempLocalDir, { recursive: true });\n  functions.logger.log('Temporary directory has been created', tempLocalDir);\n\n  // Download file from bucket.\n  await bucket.file(filePath).download({ destination: tempLocalFile });\n  functions.logger.log('The file has been downloaded to', tempLocalFile);\n\n  // Blur the image using ImageMagick.\n  await exec(`convert \"${tempLocalFile}\" -channel RGBA -blur 0x8 \"${tempLocalFile}\"`);\n  functions.logger.log('Blurred image created at', tempLocalFile);\n\n  // Uploading the Blurred image.\n  await bucket.upload(tempLocalFile, {\n    destination: `${BLURRED_FOLDER}/${filePath}`,\n    metadata: {metadata: metadata}, // Keeping custom metadata.\n  });\n  functions.logger.log('Blurred image uploaded to Storage at', filePath);\n\n  // Clean up the local file\n  fs.unlinkSync(tempLocalFile);\n  functions.logger.log('Deleted local file', filePath);\n}\n"
  },
  {
    "path": "Node-1st-gen/moderate-images/functions/package.json",
    "content": "{\n  \"name\": \"moderate-image-function\",\n  \"description\": \"Offensive Image blurring using Firebase Function\",\n  \"dependencies\": {\n    \"@google-cloud/vision\": \"^2.4.2\",\n    \"eslint-plugin-promise\": \"^7.2.1\",\n    \"firebase-admin\": \"^13.0.2\",\n    \"firebase-functions\": \"7.0.0\"\n  },\n  \"devDependencies\": {\n    \"eslint\": \"^8.57.1\"\n  },\n  \"scripts\": {\n    \"lint\": \"./node_modules/.bin/eslint --max-warnings=0 .\",\n    \"serve\": \"firebase emulators:start --only functions\",\n    \"shell\": \"firebase functions:shell\",\n    \"start\": \"npm run shell\",\n    \"deploy\": \"firebase deploy --only functions\",\n    \"logs\": \"firebase functions:log\",\n    \"compile\": \"cp ../../../tsconfig.template.json ./tsconfig-compile.json && tsc --project tsconfig-compile.json\"\n  },\n  \"engines\": {\n    \"node\": \"20\"\n  },\n  \"private\": true\n}\n"
  },
  {
    "path": "Node-1st-gen/okta-auth/.gitignore",
    "content": "service_account_private_key.json\n.runtimeconfig.json\nui-debug.log\npublic/okta-config.js\nfunctions/.env\n"
  },
  {
    "path": "Node-1st-gen/okta-auth/README.md",
    "content": "# Authenticate with Firebase using Okta\n\nSample app that demonstrates how to authenticate with Firebase using Okta.\n\n## Overview\n\nThis sample has two parts:\n\n- A Node.js backend that “exchanges” Okta access tokens for Firebase custom\n  authentication tokens. The backend is intended to be deployed as a Cloud\n  Function, but because its just an Express.js app, you can also run on it on\n  your own infrastructure.\n- A web frontend that signs users in with Okta, gets a Firebase custom\n  authentication token from your backend, and authenticates with Firebase using\n  the custom token.\n\n## Setup\n\nBefore you try the demo with the Firebase emulator suite or deploy it to\nFirebase Hosting and Cloud Functions, set up your Okta and Firebase projects,\nand install the Firebase CLI tool:\n\n1.  On the [Okta Developer site](https://developer.okta.com/):\n\n    1.  Sign in or sign up.\n    2.  Take note of your **Org URL** (top-right of the dashboard) for later.\n    3.  Create a user with a password in your Okta project. (This demo doesn't\n        have a sign-up flow.)\n    4.  On the Applications page, add a Single-Page App:\n        1.  Set the **Base URIs** to `http://localhost:5000`.\n        2.  Set the **Login redirect URIs**  to `http://localhost:5000`.\n        3.  Enable the **Authorization Code** grant type.\n        4.  Click **Done**. Take note of the app's **Client ID** for later.\n    5.  In **API > Trusted Origins**, confirm that `http://localhost:5000` is\n        listed, with **CORS** and **Redirect** enabled.\n\n2.  In the [Firebase console](https://console.firebase.google.com/):\n\n    1. Create a new Firebase project. Take note of your **project ID** for\n       later.\n    2. On the Project Overview page, add a new web app. Be sure **Also set up\n       Firebase Hosting for this app** is selected.\n    3. On the Project Settings page, open the [Service Accounts][svcacct]\n       section and take note of your Admin SDK service account ID (it looks\n       like an email address). If you plan to try the demo in the emulator,\n       also generate and download a service account key file.\n\n3.  If you don't already have a Node.js 10 (or newer) environment,\n    [install Node.js](https://nodejs.org/).\n\n4.  If you haven't already installed the Firebase CLI tool, do it now:\n\n    ```\n    $ npm install --global firebase-tools\n    ```\n\n[svcacct]: https://console.firebase.google.com/project/_/settings/serviceaccounts/adminsdk\n\n## Try the demo with the Firebase emulator suite\n\n1.  Make sure the Firebase CLI tool is set to use your Firebase project:\n\n    ```\n    $ cd functions-samples/okta-auth\n    okta-auth$ firebase login\n    okta-auth$ firebase use <YOUR_FIREBASE_PROJECT_ID>\n    ```\n\n2.  Run `setup.js` from the Firebase project directory:\n\n    ```\n    okta-auth$ node setup.js\n    ```\n\n    The script will prompt you for some of your Okta and Firebase configuration\n    values and create configurations files from them. The script won't\n    overwrite existing files.\n\n3.  Start the emulators:\n\n    ```\n    okta-auth$ firebase emulators:start\n    ```\n\n4.  Open the web app: [`http://localhost:5000`](http://localhost:5000).\n\n## Deploy the demo to Firebase Hosting and Cloud Functions\n\n1.  [Upgrade your Firebase project to the Blaze (pay as you go) plan](https://console.firebase.google.com/project/_/overview?purchaseBillingPlan=metered).\n    The Blaze plan is required to access external services (in this case, Okta)\n    from Cloud Functions.\n\n2.  Make sure the Firebase CLI tool is set to use your Firebase project:\n\n    ```\n    $ cd functions-samples/okta-auth\n    okta-auth$ firebase login\n    okta-auth$ firebase use <YOUR_FIREBASE_PROJECT_ID>\n    ```\n\n3.  Optional: If you have configuration files from local testing, delete them:\n\n    ```\n    okta-auth$ rm public/okta-config.js ; rm functions/.env ; rm .runtimeconfig.json\n    ```\n\n4.  Run `setup.js -d` from the Firebase project directory. The `-d` flag\n    configures the web app and backend for deployment.\n\n    ```\n    okta-auth$ node setup.js -d\n    ```\n\n    The script will prompt you for some of your Okta and Firebase configuration\n    values, create configurations files from them, and set some Cloud Funcions\n    environment settings. The script won't overwrite existing files or Cloud\n    Functions environment settings.\n\n5.  Deploy the project:\n\n    ```\n    okta-auth$ firebase deploy\n    ```\n\n6.  In the Google Cloud console:\n\n    1.  [Enable the IAM Service Account Credentials API](https://console.cloud.google.com/apis/api/iamcredentials.googleapis.com/overview?project=_).\n    2.  On the [Cloud Functions](https://console.cloud.google.com/functions/list?project=_)\n        page, \n        1.  Click the name of your Cloud Function (**`api`**) to open the\n            Function Details page.\n        2.  Click **Edit**.\n        3.  Set the service account to your Admin SDK service account.\n        4.  Click **Deploy** to redeploy your Cloud Function to run as the\n            updated service account.\n\n7.  Open the web app at: `https://<YOUR_FIREBASE_PROJECT_ID>.web.app`\n"
  },
  {
    "path": "Node-1st-gen/okta-auth/firebase.json",
    "content": "{\n  \"functions\": {\n    \"codebase\": \"okta-auth\",\n    \"predeploy\": [\n      \"npm --prefix \\\"$RESOURCE_DIR\\\" run lint\",\n      \"node setup.js -d\"\n    ]\n  },\n  \"hosting\": {\n    \"public\": \"public\",\n    \"ignore\": [\n      \"firebase.json\",\n      \"**/.*\",\n      \"**/node_modules/**\"\n    ],\n    \"predeploy\": [\n      \"node setup.js -d\"\n    ]\n  }\n}\n"
  },
  {
    "path": "Node-1st-gen/okta-auth/functions/.eslintrc.json",
    "content": "{\n  \"parserOptions\": {\n    // Required for certain syntax usages\n    \"ecmaVersion\": 2022\n  },\n  \"plugins\": [\n    \"promise\"\n  ],\n  \"extends\": \"eslint:recommended\",\n  \"rules\": {\n    // Removed rule \"disallow the use of console\" from recommended eslint rules\n    \"no-console\": \"off\",\n\n    // Removed rule \"disallow multiple spaces in regular expressions\" from recommended eslint rules\n    \"no-regex-spaces\": \"off\",\n\n    // Removed rule \"disallow the use of debugger\" from recommended eslint rules\n    \"no-debugger\": \"off\",\n\n    // Removed rule \"disallow unused variables\" from recommended eslint rules\n    \"no-unused-vars\": \"off\",\n\n    // Removed rule \"disallow mixed spaces and tabs for indentation\" from recommended eslint rules\n    \"no-mixed-spaces-and-tabs\": \"off\",\n\n    // Removed rule \"disallow the use of undeclared variables unless mentioned in /*global */ comments\" from recommended eslint rules\n    \"no-undef\": \"off\",\n\n    // Warn against template literal placeholder syntax in regular strings\n    \"no-template-curly-in-string\": 1,\n\n    // Warn if return statements do not either always or never specify values\n    \"consistent-return\": 1,\n\n    // Warn if no return statements in callbacks of array methods\n    \"array-callback-return\": 1,\n\n    // Require the use of === and !==\n    \"eqeqeq\": 2,\n\n    // Disallow the use of alert, confirm, and prompt\n    \"no-alert\": 2,\n\n    // Disallow the use of arguments.caller or arguments.callee\n    \"no-caller\": 2,\n\n    // Disallow null comparisons without type-checking operators\n    \"no-eq-null\": 2,\n\n    // Disallow the use of eval()\n    \"no-eval\": 2,\n\n    // Warn against extending native types\n    \"no-extend-native\": 1,\n\n    // Warn against unnecessary calls to .bind()\n    \"no-extra-bind\": 1,\n\n    // Warn against unnecessary labels    \n    \"no-extra-label\": 1,\n\n    // Disallow leading or trailing decimal points in numeric literals\n    \"no-floating-decimal\": 2,\n\n    // Warn against shorthand type conversions\n    \"no-implicit-coercion\": 1,\n\n    // Warn against function declarations and expressions inside loop statements\n    \"no-loop-func\": 1,\n\n    // Disallow new operators with the Function object\n    \"no-new-func\": 2,\n\n    // Warn against new operators with the String, Number, and Boolean objects\n    \"no-new-wrappers\": 1,\n\n    // Disallow throwing literals as exceptions\n    \"no-throw-literal\": 2,\n\n    // Require using Error objects as Promise rejection reasons\n    \"prefer-promise-reject-errors\": 2,\n\n    // Enforce “for” loop update clause moving the counter in the right direction\n    \"for-direction\": 2,\n\n    // Enforce return statements in getters\n    \"getter-return\": 2,\n\n    // Disallow await inside of loops\n    \"no-await-in-loop\": 2,\n\n    // Disallow comparing against -0\n    \"no-compare-neg-zero\": 2,\n\n    // Warn against catch clause parameters from shadowing variables in the outer scope\n    \"no-catch-shadow\": 1,\n\n    // Disallow identifiers from shadowing restricted names\n    \"no-shadow-restricted-names\": 2,\n\n    // Enforce return statements in callbacks of array methods\n    \"callback-return\": 2,\n\n    // Require error handling in callbacks\n    \"handle-callback-err\": 2,\n\n    // Warn against string concatenation with __dirname and __filename\n    \"no-path-concat\": 1,\n\n    // Prefer using arrow functions for callbacks\n    \"prefer-arrow-callback\": 1,\n\n    // Return inside each then() to create readable and reusable Promise chains.\n    // Forces developers to return console logs and http calls in promises. \n    \"promise/always-return\": 2,\n\n    //Enforces the use of catch() on un-returned promises\n    \"promise/catch-or-return\": 2,\n\n    // Warn against nested then() or catch() statements\n    \"promise/no-nesting\": 1\n  }\n}\n"
  },
  {
    "path": "Node-1st-gen/okta-auth/functions/index.js",
    "content": "/**\n * Copyright 2023 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Implements the `/api/firebaseCustomToken` endpoint, which is responsible for\n * validating Okta access tokens and minting Firebase custom authentication\n * tokens.\n */\nconst express = require('express');\nconst app = express();\n\n// For local testing, use GOOGLE_APPLICATION_CREDENTIALS from `.env` instead of\n// the value set by the emulator. Generate a .`env` file with `setup.js`, or\n// create it manually.\nconst envCfg = require('dotenv').config();\nif (envCfg.parsed && envCfg.parsed.GOOGLE_APPLICATION_CREDENTIALS) {\n    process.env.GOOGLE_APPLICATION_CREDENTIALS =\n            envCfg.parsed.GOOGLE_APPLICATION_CREDENTIALS;\n}\n\nconst functions = require('firebase-functions/v1');\nconst {onInit} = require('firebase-functions/v1/init');\nconst {defineString} = require('firebase-functions/params');\nconst firebaseAdmin = require('firebase-admin');\nconst firebaseApp = firebaseAdmin.initializeApp();\n\nconst oktaOrgUrl = defineString('OKTA_ORG_URL');\nconst OktaJwtVerifier = require('@okta/jwt-verifier');\n\nlet oktaJwtVerifier;\nonInit(() => {\n    oktaJwtVerifier = new OktaJwtVerifier({\n        issuer: `${oktaOrgUrl.value()}/oauth2/default`\n    });\n});\n\n// Update CORS_ORIGIN to the base URL of your web client before deploying or\n// using a non-standard emulator configuration.\nconst corsOrigin = defineString('CORS_ORIGIN');\n\n// Middleware to authenticate requests with an Okta access token.\n// https://developer.okta.com/docs/guides/protect-your-api/nodeexpress/require-authentication/\nconst oktaAuth = async (req, res, next) => {\n    const authHeader = req.headers.authorization || '';\n    const match = authHeader.match(/Bearer (.+)/);\n  \n    if (!match) {\n        res.status(401);\n        return next('Unauthorized');\n    }\n  \n    const accessToken = match[1];\n    try {\n        const jwt = await oktaJwtVerifier.verifyAccessToken(\n                accessToken, 'api://default');\n        req.jwt = jwt;\n        return next();\n    } catch (err) {\n        functions.logger.log('Unable to verify Okta access token', err);\n        res.status(401);\n        return next('Unauthorized');\n    }\n}\n\n// Get a Firebase custom auth token for the authenticated Okta user.\nlet cors;\nonInit(() => {\n    cors = require('cors')({ origin: corsOrigin.value() });\n});\n\napp.get('/firebaseCustomToken', [cors, oktaAuth], async (req, res) => {\n    const oktaUid = req.jwt.claims.uid;\n    try {\n        const firebaseToken =\n                await firebaseApp.auth().createCustomToken(oktaUid);\n        res.send(firebaseToken);\n    } catch (err) {\n        functions.logger.error('Error minting token.', err);\n        res.status(500).send('Error minting token.');\n    }\n});\n\n// Enable CORS pre-flight requests.\napp.options('/firebaseCustomToken', cors);\n\nexports.api = functions.https.onRequest(app);\n"
  },
  {
    "path": "Node-1st-gen/okta-auth/functions/package.json",
    "content": "{\n  \"name\": \"firebase-okta-auth\",\n  \"description\": \"Example of authenticating with Firebase using Okta\",\n  \"scripts\": {\n    \"lint\": \"eslint .\",\n    \"serve\": \"firebase emulators:start --only functions\",\n    \"shell\": \"firebase functions:shell\",\n    \"start\": \"npm run shell\",\n    \"deploy\": \"firebase deploy --only functions\",\n    \"logs\": \"firebase functions:log\",\n    \"compile\": \"cp ../../../tsconfig.template.json ./tsconfig-compile.json && tsc --project tsconfig-compile.json\"\n  },\n  \"engines\": {\n    \"node\": \"20\"\n  },\n  \"dependencies\": {\n    \"@okta/jwt-verifier\": \"^1.0.0\",\n    \"@okta/oidc-middleware\": \"^4.0.1\",\n    \"cors\": \"^2.8.5\",\n    \"dotenv\": \"^8.2.0\",\n    \"eslint-plugin-promise\": \"^7.2.1\",\n    \"express\": \"^4.17.1\",\n    \"firebase-admin\": \"^13.0.2\",\n    \"firebase-functions\": \"7.0.0\"\n  },\n  \"devDependencies\": {\n    \"eslint\": \"^8.57.1\",\n    \"firebase-functions-test\": \"^3.4.0\"\n  },\n  \"private\": true\n}\n"
  },
  {
    "path": "Node-1st-gen/okta-auth/public/index.html",
    "content": "<!doctype html>\n<!--\n Copyright 2023 Google LLC\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n     http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<!-- Example frontend that demonstrates signing in with Okta, exchanging    -->\n<!-- the Okta access token for a Firebase custom authentication token, and  -->\n<!-- authenticating with Firebase using the custom token.                   -->\n<html>\n<head>\n    <meta charset=\"utf-8\"/>\n    <title>Okta sign-in demo</title>\n    <script src=\"/__/firebase/10.0.0/firebase-app-compat.js\"></script>\n    <script src=\"/__/firebase/10.0.0/firebase-auth-compat.js\"></script>\n    <script src=\"/__/firebase/init.js\"></script>\n    <script src=\"https://global.oktacdn.com/okta-signin-widget/4.1.1/js/okta-sign-in.min.js\" type=\"text/javascript\"></script>\n    <link href=\"https://global.oktacdn.com/okta-signin-widget/4.1.1/css/okta-sign-in.min.css\" type=\"text/css\" rel=\"stylesheet\"/>\n\n    <!-- okta-config.js defines the following values:                       -->\n    <!--   OKTA_ORG_URL: Your Okta organization URL                         -->\n    <!--   OKTA_CLIENT_ID: Your Okta client ID                              -->\n    <!--   CUSTOM_TOKEN_ENDPOINT: The URL of your custom token endpoint     -->\n    <!-- Create this file with setup.js or manually.                        -->\n    <script src=\"/okta-config.js\"></script>\n</head>\n<body>\n<div id=\"authenticated-user-content\" hidden>\n    <h2>Authenticated with Firebase</h2>\n    <p id=\"user-info\"></p>\n    <button onclick=\"firebase.auth().signOut();\">Sign out</button>\n</div>\n<div id=\"signin-widget\" hidden></div>\n<script>\nwindow.onload = async () => {\n    const oktaSignIn = new OktaSignIn({\n        baseUrl: OKTA_ORG_URL,\n        redirectUri: window.location.url,\n        authParams: {\n            display: 'page',\n        },\n        el: '#signin-widget',\n    });\n\n    if (oktaSignIn.hasTokensInUrl()) {\n        // The user has been redirected back to your app after signing in with\n        // Okta. Get the Okta access token from the response and use it to\n        // authenticate with Firebase.\n\n        // Get the access token from Okta.\n        const oktaTokenResponse = await oktaSignIn.authClient.token.parseFromUrl();\n        const accessToken = oktaTokenResponse.tokens.accessToken.value;\n        const oktaUserName = oktaTokenResponse.tokens.idToken.claims.name || '';\n        const oktaUserEmail = oktaTokenResponse.tokens.idToken.claims.email || '';\n\n        // Use the access token to call the firebaseCustomToken endpoint.\n        const firebaseTokenResponse = await fetch(CUSTOM_TOKEN_ENDPOINT, {\n            headers: {\n                'Authorization': `Bearer ${accessToken}`,\n            }\n        });\n        const firebaseToken = await firebaseTokenResponse.text();\n\n        // Use the Firebase custom token to authenticate with Firebase.\n        try {\n            await firebase.auth().signInWithCustomToken(firebaseToken);\n\n            // Now that the user is authenticated, you can use auth-enabled\n            // Firebase services. In this example, we update the Firebase\n            // user profile with information from Okta:\n            const user = firebase.auth().currentUser;\n            if (user.displayName !== oktaUserName) {\n                await user.updateProfile({displayName: oktaUserName});\n            }\n            if (user.email !== oktaUserEmail) {\n                await user.updateEmail(oktaUserEmail);\n            }\n        } catch (err) {\n            console.error('Error signing in with custom token.');\n        }\n    }\n\n    firebase.auth().onAuthStateChanged((user) => {\n        if (user) {\n            // User is signed in. Display some user profile information.\n            document.getElementById('user-info').innerHTML = `\\\n                    <table>\n                        <tr>\n                            <th>Display name</th>\n                            <td>${user.displayName}</td>\n                        </tr>\n                        <tr>\n                            <th>Email address</th>\n                            <td>${user.email}</td>\n                        </tr>\n                        <tr>\n                            <th>Unique ID</th>\n                            <td>${user.uid}</td>\n                        </tr>\n                    </table>`;\n            document.getElementById('authenticated-user-content').hidden = false;\n            document.getElementById('signin-widget').hidden = true;\n        } else {\n            // User is signed out. Display the Okta sign-in widget.\n            oktaSignIn.showSignInToGetTokens({\n                clientId: OKTA_CLIENT_ID,\n                redirectUri: window.location.url,\n                getAccessToken: true,\n                getIdToken: true,\n                scope: 'openid profile email',\n            });\n            document.getElementById('authenticated-user-content').hidden = true;\n            document.getElementById('signin-widget').hidden = false;\n        }\n    });\n}\n</script>\n</body>\n</html>\n"
  },
  {
    "path": "Node-1st-gen/okta-auth/setup.js",
    "content": "#!/usr/bin/env node\n/**\n * Copyright 2023 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * This script helps you set up your environment to run this demo in the\n * emulator or deploy it to Cloud Functions and Hosting. Its use is optional;\n * see the README for manual setup hints.\n */\nconst { execSync } = require('child_process');\nconst fs = require('fs');\nconst process = require('process');\nconst readline = require('readline');\n\nconst DEPLOYING = process.argv.length >= 3 && process.argv[2] === '-d';\nconst GCLOUD_PROJECT = process.env.GCLOUD_PROJECT || '<YOUR_PROJECT_ID>';\nconst PROJECT_DIR = process.env.PROJECT_DIR || __dirname;\n\nconst OKTA_URL_PROMPT = `\\\nWhat is your Okta organization URL?\n\nYou can find it in the top-right corner of the Dashboard page of the Okta \\\ndeveloper console.\n> `;\n\nconst OKTA_CLIENT_ID_PROMPT = `\\\nWhat is your web app's Okta client ID?\n\nYou can find it on the Applications page of the Okta developer console.\n> `;\n\nconst CORS_ORIGIN = `\\\nWhat is the base URL where you'll be hosting your web app?\n\nFor example: https://${GCLOUD_PROJECT}.web.app\n> `;\n\nconst SERVICE_ACCOUNT_PATH_PROMPT = `\\\nWhat is the full path to your service account key file?\n\nYou can download your project's service account key from the Firebase console:\nhttps://console.firebase.google.com/project/_/settings/serviceaccounts/adminsdk\n\nNote that this value is required only when using the emulator.\n> `;\n\nconst EXAMPLE_ENDPOINT = (\n        DEPLOYING ? `\nWhen you deploy the Cloud Function, the endpoint looks like:\nhttps://<YOUR_PROJECT_AND_LOCATION>.cloudfunctions.net/api/firebaseCustomToken`\n        : `\nWhen using the emulator, the endpoint is the following:\nhttp://localhost:5001/${GCLOUD_PROJECT}/us-central1/api/firebaseCustomToken`\n);\n\nconst TOKEN_ENDPOINT_PROMPT = `\\\nWhat is the URL of your '/api/firebaseCustomToken' endpoint?\n${EXAMPLE_ENDPOINT}\n> `;\n\nconst rl = readline.createInterface({\n    input: process.stdin,\n    output: process.stdout\n});\n\nrl.on('SIGINT', () => {\n    process.exit(1);\n});\n\nconst ask = (question) => {\n    return new Promise((resolve) => {\n        rl.question(question, (answer) => {\n            console.log();\n            resolve(answer);\n        });\n    });\n};\n\n(async () => {\n    let oktaOrgUrl = null;\n    let oktaClientId = null;\n    let corsOrigin = null;\n    let tokenEndpoint = null;\n    let serviceAccountFile = null;\n\n    console.log(DEPLOYING ? 'Checking configuration for deployment...'\n                          : 'Checking configuration for emulation...');\n\n    const oktaConfigJs = 'public/okta-config.js';\n    try {\n        fs.accessSync(`${PROJECT_DIR}/${oktaConfigJs}`);\n        console.log(`Found ${oktaConfigJs}. Skipping setup.`);\n    } catch {\n        console.log(`Can't find ${oktaConfigJs}. Configuring...\\n`)\n        oktaOrgUrl = oktaOrgUrl || await ask(OKTA_URL_PROMPT);\n        oktaClientId = oktaClientId || await ask(OKTA_CLIENT_ID_PROMPT);\n        tokenEndpoint = tokenEndpoint || await ask(TOKEN_ENDPOINT_PROMPT);\n        const contents = `\\\n//\n// Configuration for Okta/Firebase demo client (index.html)\n//\n\n// Okta org URL, from the Dashboard page of the Okta developer console.\nconst OKTA_ORG_URL = '${oktaOrgUrl}';\n\n// Okta client ID, from the Application page of the Okta developer console.\nconst OKTA_CLIENT_ID = '${oktaClientId}';\n\n// The complete URL of your custom token endpoint. When using the emulator, the\n// endpoint is:\n// http://localhost:5001/<YOUR_FIREBASE_PROJECT_ID>/us-central1/api/firebaseCustomToken\nconst CUSTOM_TOKEN_ENDPOINT = '${tokenEndpoint}';\n\nif (OKTA_ORG_URL === ''\n    || OKTA_CLIENT_ID === ''\n    || CUSTOM_TOKEN_ENDPOINT === '') {\n    console.error('Okta configuration not set in okta-config.js.')\n}        \n`;\n        fs.writeFileSync(`${PROJECT_DIR}/${oktaConfigJs}`, contents);\n    }\n\n    if (DEPLOYING) {\n        try {\n            const envCfgJson = execSync('firebase functions:config:get');\n            const envCfg = JSON.parse(envCfgJson);\n            if (!envCfg.okta_auth\n                    || !envCfg.okta_auth.org_url\n                    || !envCfg.okta_auth.cors_origin) {\n                console.log('Cloud Functions environment not configured. '\n                            + 'Configuring...');\n                oktaOrgUrl = oktaOrgUrl || await ask(OKTA_URL_PROMPT);\n                corsOrigin = corsOrigin || await ask(CORS_ORIGIN);\n                execSync(`firebase functions:config:set\\\n                            okta_auth.org_url=${oktaOrgUrl}\\\n                            okta_auth.cors_origin=${corsOrigin}`);\n            } else {\n                console.log('Cloud Functions environment configuration found. '\n                            + 'Skipping setup.');\n            }\n        } catch {\n            console.error('Couldn\\'t set up the Cloud Functions environment. '\n                          + 'Try again or set okta_auth.org_url and '\n                          + 'okta_auth.cors_origin manually with \\'firebase '\n                          + 'functions:config:set\\'');\n        }\n\n        rl.close();\n        return;\n    }\n\n    // Skip the rest if we're deploying for real.\n\n    const runtimeconfigJson = '.runtimeconfig.json';\n    try {\n        fs.accessSync(`${PROJECT_DIR}/${runtimeconfigJson}`);\n        console.log(`Found ${runtimeconfigJson}. Skipping setup.`);\n    } catch {\n        console.log(`Can't find ${runtimeconfigJson}. Configuring...\\n`)\n        oktaOrgUrl = oktaOrgUrl || await ask(OKTA_URL_PROMPT);\n        const contents = `{\"okta_auth\": {\"org_url\": \"${oktaOrgUrl}\"}}`;\n        fs.writeFileSync(`${PROJECT_DIR}/${runtimeconfigJson}`, contents);\n    }\n\n    const dotEnv = 'functions/.env';\n    try {\n        fs.accessSync(`${PROJECT_DIR}/${dotEnv}`);\n        console.log(`Found ${dotEnv}. Skipping setup.`);\n    } catch {\n        console.log(`Can't find ${dotEnv}. Configuring...\\n`)\n        serviceAccountFile =\n                serviceAccountFile || await ask(SERVICE_ACCOUNT_PATH_PROMPT);\n        const contents =\n                `GOOGLE_APPLICATION_CREDENTIALS=\"${serviceAccountFile}\"\\n`;\n        fs.writeFileSync(`${PROJECT_DIR}/${dotEnv}`, contents);\n    }\n\n    rl.close();\n})();\n"
  },
  {
    "path": "Node-1st-gen/paypal/.gitignore",
    "content": "firebase-debug.log\nfunctions/eslintrc.js\n"
  },
  {
    "path": "Node-1st-gen/paypal/README.md",
    "content": "# Accept PayPal payment in Cloud Functions for Firebase\n\nThis sample demonstrates how to use the Paypal-rest-sdk with Google Cloud Functions.\n\n\n## Functions Code\n\nSee file [functions/index.js](functions/index.js) for the code.\n\nThe dependencies are listed in [functions/package.json](functions/package.json).\n\n\n## Process PayPal payments\n\n1. [Set up a payment information object](https://developer.paypal.com/docs/api/quickstart/payments/#set-up-the-payment-information-object) that contains details about the PayPal payment.\n\n2. [Initialize the payment and redirect the user](https://developer.paypal.com/docs/api/quickstart/payments/#initialize-the-payment-and-redirect-the-user). To do so, send the payment object to PayPal. This action provides a redirect URL to which to redirect the user. After the user confirms the payment, PayPal redirects the user to the return URLs specified in the payment object.\n\n3. [Complete the payment](https://developer.paypal.com/docs/api/quickstart/payments/#complete-the-payment). Use the payer and payment IDs provided in the query string following the redirect.\n\n\n## Setting up the sample\n\n 1. Create a Firebase project on the [Firebase application console](https://console.firebase.google.com).\n 1. Enable billing on your Firebase project by switching to the Blaze plan. See [pricing](https://firebase.google.com/pricing/) for more details. This is required to be able to do requests to non-Google services.\n 1. Clone or download this repo and open the `paypal` directory.\n 1. You must have the Firebase CLI installed. If you don't have it install it with `npm install -g firebase-tools` and then configure it with `firebase login`.\n 1. Configure the CLI locally by using `firebase use --add` and select your project in the list.\n 1. [Create a Paypal REST API app](https://developer.paypal.com/developer/applications/create) and note your **Client ID** and **Client Secret**.\n 1. Setup [your Paypal API Client ID and Secret](https://developer.paypal.com/developer/applications/) in your Cloud Function. Run in the command line:\n  \n    ```sh\n    firebase functions:secrets:set PAYPAL_CLIENT_ID\n    ```\n\n    ```sh\n    firebase functions:secrets:set PAYPAL_CLIENT_SECRET\n    ```\n 1. Install dependencies locally by running: `cd functions; npm install; cd -`\n\n\n## Deploy and test\n\nThis sample comes with a web-based UI.\nTo test locally do:\n\n 1. Start serving your project locally using `firebase serve --only hosting,functions`\n 1. Send a `POST` request with body `{price:5}` to `https://localhost:5000/pay`. You will get a 302 Redirect redirecting to the payment page.\n\n\nTo deploy and test on prod do:\n\n 1. Deploy your project using `firebase deploy`\n 1. Send a `POST` request with body `{price:5}` to `https://us-central1-<project-id>.cloudfunctions.net/pay`. You will get a 302 Redirect redirecting to the payment page.\n\n## Contributing\n\nWe'd love that you contribute to the project. Before doing so please read our [Contributor guide](../../CONTRIBUTING.md).\n\n\n## License\n\n© Google, 2017. Licensed under an [Apache-2](../../LICENSE) license.\n"
  },
  {
    "path": "Node-1st-gen/paypal/firebase.json",
    "content": "{\n  \"functions\": {\n    \"codebase\": \"paypal\",\n    \"source\": \"functions\"\n  },\n  \"hosting\": {\n    \"public\": \"public\",\n    \"rewrites\": [\n      {\n        \"source\": \"/process\",\n        \"function\": \"process\"\n      },\n      {\n        \"source\": \"/pay\",\n        \"function\": \"pay\"\n      },\n      {\n        \"source\":\"/error\",\n        \"destination\":\"/error.html\",\n        \"type\":301\n      },\n      {\n        \"source\":\"/cancel\",\n        \"destination\":\"/cancel.html\",\n        \"type\":301\n      },\n      {\n        \"source\":\"/success\",\n        \"destination\":\"/success.html\",\n        \"type\":301\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "Node-1st-gen/paypal/functions/.eslintrc.json",
    "content": "{\n  \"parserOptions\": {\n    // Required for certain syntax usages\n    \"ecmaVersion\": 2022\n  },\n  \"plugins\": [\n    \"promise\"\n  ],\n  \"extends\": \"eslint:recommended\",\n  \"rules\": {\n    // Removed rule \"disallow the use of console\" from recommended eslint rules\n    \"no-console\": \"off\",\n\n    // Removed rule \"disallow multiple spaces in regular expressions\" from recommended eslint rules\n    \"no-regex-spaces\": \"off\",\n\n    // Removed rule \"disallow the use of debugger\" from recommended eslint rules\n    \"no-debugger\": \"off\",\n\n    // Removed rule \"disallow unused variables\" from recommended eslint rules\n    \"no-unused-vars\": \"off\",\n\n    // Removed rule \"disallow mixed spaces and tabs for indentation\" from recommended eslint rules\n    \"no-mixed-spaces-and-tabs\": \"off\",\n\n    // Removed rule \"disallow the use of undeclared variables unless mentioned in /*global */ comments\" from recommended eslint rules\n    \"no-undef\": \"off\",\n\n    // Warn against template literal placeholder syntax in regular strings\n    \"no-template-curly-in-string\": 1,\n\n    // Warn if return statements do not either always or never specify values\n    \"consistent-return\": 1,\n\n    // Warn if no return statements in callbacks of array methods\n    \"array-callback-return\": 1,\n\n    // Requre the use of === and !==\n    \"eqeqeq\": 2,\n\n    // Disallow the use of alert, confirm, and prompt\n    \"no-alert\": 2,\n\n    // Disallow the use of arguments.caller or arguments.callee\n    \"no-caller\": 2,\n\n    // Disallow null comparisons without type-checking operators\n    \"no-eq-null\": 2,\n\n    // Disallow the use of eval()\n    \"no-eval\": 2,\n\n    // Warn against extending native types\n    \"no-extend-native\": 1,\n\n    // Warn against unnecessary calls to .bind()\n    \"no-extra-bind\": 1,\n\n    // Warn against unnecessary labels    \n    \"no-extra-label\": 1,\n\n    // Disallow leading or trailing decimal points in numeric literals\n    \"no-floating-decimal\": 2,\n\n    // Warn against shorthand type conversions\n    \"no-implicit-coercion\": 1,\n\n    // Warn against function declarations and expressions inside loop statements\n    \"no-loop-func\": 1,\n\n    // Disallow new operators with the Function object\n    \"no-new-func\": 2,\n\n    // Warn against new operators with the String, Number, and Boolean objects\n    \"no-new-wrappers\": 1,\n\n    // Disallow throwing literals as exceptions\n    \"no-throw-literal\": 2,\n\n    // Require using Error objects as Promise rejection reasons\n    \"prefer-promise-reject-errors\": 2,\n\n    // Enforce “for” loop update clause moving the counter in the right direction\n    \"for-direction\": 2,\n\n    // Enforce return statements in getters\n    \"getter-return\": 2,\n\n    // Disallow await inside of loops\n    \"no-await-in-loop\": 2,\n\n    // Disallow comparing against -0\n    \"no-compare-neg-zero\": 2,\n\n    // Warn against catch clause parameters from shadowing variables in the outer scope\n    \"no-catch-shadow\": 1,\n\n    // Disallow identifiers from shadowing restricted names\n    \"no-shadow-restricted-names\": 2,\n\n    // Enforce return statements in callbacks of array methods\n    \"callback-return\": 2,\n\n    // Require error handling in callbacks\n    \"handle-callback-err\": 2,\n\n    // Warn against string concatenation with __dirname and __filename\n    \"no-path-concat\": 1,\n\n    // Prefer using arrow functions for callbacks\n    \"prefer-arrow-callback\": 1,\n\n    // Return inside each then() to create readable and reusable Promise chains.\n    \"promise/always-return\": 2,\n\n    //Enforces the use of catch() on un-returned promises\n    \"promise/catch-or-return\": 2,\n\n    // Warn against nested then() or catch() statements\n    \"promise/no-nesting\": 1\n  }\n}\n"
  },
  {
    "path": "Node-1st-gen/paypal/functions/index.js",
    "content": "/**\n * Copyright 2016 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use strict';\n\nconst functions = require('firebase-functions/v1');\nconst {onInit} = require('firebase-functions/v1/init');\nconst {defineSecret} = require('firebase-functions/params');\nconst paypal = require('paypal-rest-sdk');\n// firebase-admin SDK init\nconst admin = require('firebase-admin');\nadmin.initializeApp();\n\nconst paypalClientId = defineSecret('PAYPAL_CLIENT_ID');\nconst paypalClientSecret = defineSecret('PAYPAL_CLIENT_SECRET');\n\nonInit(() => {\n  paypal.configure({\n    mode: 'sandbox', // sandbox or live\n    client_id: paypalClientId.value(),\n    client_secret: paypalClientSecret.value(),\n  });\n});\n\n/**\n * Expected in the body the amount\n * Set up the payment information object\n * Initialize the payment and redirect the user to the PayPal payment page\n */\nexports.pay = functions.runWith({secrets: [paypalClientId, paypalClientSecret]}).https.onRequest((req, res) => {\n  // 1.Set up a payment information object, Build PayPal payment request\n  const payReq = JSON.stringify({\n    intent: 'sale',\n    payer: {\n      payment_method: 'paypal'\n    },\n    redirect_urls: {\n      return_url: `${req.protocol}://${req.get('host')}/process`,\n      cancel_url: `${req.protocol}://${req.get('host')}/cancel`\n    },\n    transactions: [{\n      amount: {\n        total: req.body.price,\n        currency: 'USD'\n      },\n      // This is the payment transaction description. Maximum length: 127\n      description: req.body.uid, // req.body.id\n      // reference_id string .Optional. The merchant-provided ID for the purchase unit. Maximum length: 256.\n      // reference_id: req.body.uid,\n      custom: req.body.uid,\n      // soft_descriptor: req.body.uid\n      // \"invoice_number\": req.body.uid,A\n    }]\n  });\n  // 2.Initialize the payment and redirect the user.\n  paypal.payment.create(payReq, (error, payment) => {\n    const links = {};\n    if (error) {\n      functions.logger.error(error);\n      res.status(500).end();\n    } else {\n      // Capture HATEOAS links\n      payment.links.forEach((linkObj) => {\n        links[linkObj.rel] = {\n          href: linkObj.href,\n          method: linkObj.method\n        };\n      });\n      // If redirect url present, redirect user\n      if ( Object.prototype.hasOwnProperty.call(links, 'approval_url')) {\n        // REDIRECT USER TO links['approval_url'].href\n        functions.logger.info(links.approval_url.href);\n        // res.json({\"approval_url\":links.approval_url.href});\n        res.redirect(302, links.approval_url.href);\n      } else {\n        functions.logger.error('no redirect URI present');\n        res.status(500).end();\n      }\n    }\n  });\n});\n\n// 3.Complete the payment. Use the payer and payment IDs provided in the query string following the redirect.\nexports.process = functions.runWith({secrets: [paypalClientId, paypalClientSecret]}).https.onRequest(async (req, res) => {\n  const paymentId = req.query.paymentId;\n  const payerId = {\n    payer_id: req.query.PayerID\n  };\n  const r = await paypal.payment.execute(paymentId, payerId, (error, payment) => {\n    if (error) {\n      functions.logger.error(error);\n      res.redirect(`${req.protocol}://${req.get('host')}/error`); // replace with your url page error\n    } else {\n      if (payment.state === 'approved') {\n        functions.logger.info(\n          'payment completed successfully, description: ',\n          payment.transactions[0].description\n        );\n        // functions.logger.info('req.custom: : ', payment.transactions[0].custom);\n        // set paid status to True in RealTime Database\n        const date = Date.now();\n        const uid = payment.transactions[0].description;\n        const ref = admin.database().ref('users/' + uid + '/');\n        ref.push({\n          paid: true,\n          // 'description': description,\n          date: date,\n        });\n        res.redirect(`${req.protocol}://${req.get('host')}/success`); // replace with your url, page success\n      } else {\n        functions.logger.warn('payment.state: not approved ?');\n        // replace debug url\n        res.redirect(`https://console.firebase.google.com/project/${process.env.GCLOUD_PROJECT}/functions/logs?search=&severity=DEBUG`);\n      }\n    }\n  });\n  functions.logger.info('promise: ', r);\n});\n"
  },
  {
    "path": "Node-1st-gen/paypal/functions/package.json",
    "content": "{\n  \"name\": \"paypal-functions\",\n  \"description\": \"Paypal Firebase Functions\",\n  \"dependencies\": {\n    \"cors\": \"^2.8.5\",\n    \"eslint-plugin-promise\": \"^7.2.1\",\n    \"firebase-admin\": \"^13.0.2\",\n    \"firebase-functions\": \"7.0.0\",\n    \"paypal-rest-sdk\": \"^1.8.1\"\n  },\n  \"devDependencies\": {\n    \"eslint\": \"^8.57.1\",\n    \"eslint-config-google\": \"^0.14.0\",\n    \"jshint\": \"^2.13.5\"\n  },\n  \"scripts\": {\n    \"lint\": \"./node_modules/.bin/eslint --max-warnings=0 .\",\n    \"serve\": \"firebase emulators:start --only functions\",\n    \"shell\": \"firebase functions:shell\",\n    \"start\": \"npm run shell\",\n    \"deploy\": \"firebase deploy --only functions\",\n    \"logs\": \"firebase functions:log\",\n    \"compile\": \"cp ../../../tsconfig.template.json ./tsconfig-compile.json && tsc --project tsconfig-compile.json\"\n  },\n  \"engines\": {\n    \"node\": \"20\"\n  },\n  \"private\": true\n}\n"
  },
  {
    "path": "Node-1st-gen/paypal/public/cancel.html",
    "content": "<!DOCTYPE html>\n<!--\n Copyright 2023 Google LLC\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n     http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<html lang=\"fr\">\n  <head>\n    <meta charset=\"utf-8\">\n    <title>Cancel page</title>\n    <!-- On peut avoir d'autres méta-données ici -->\n  </head>\n  <body>\n <h1>Cancel Page</h1>\n     <a disabled class=\"mdl-button mdl-js-button mdl-button--raised\"  href=\"/\">Home</a>\n  </body>\n</html>\n"
  },
  {
    "path": "Node-1st-gen/paypal/public/error.html",
    "content": "<!DOCTYPE html>\n<!--\n Copyright 2023 Google LLC\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n     http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<html lang=\"fr\">\n  <head>\n    <meta charset=\"utf-8\">\n    <title>Error Page</title>\n    <!-- On peut avoir d'autres méta-données ici -->\n  </head>\n  <body>\n   <h1>Error Page</h1>\n       <a disabled class=\"mdl-button mdl-js-button mdl-button--raised\"  href=\"/\">Home</a>\n  </body>\n</html>\n"
  },
  {
    "path": "Node-1st-gen/paypal/public/index.html",
    "content": "<!DOCTYPE html>\n<!--\nCopyright (c) 2016 Google Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\nhttp://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n-->\n<html>\n<head>\n  <meta charset=utf-8 />\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n  <title>Anonymous Auth + PayPal Demo</title>\n\n  <!-- Material Design Theming -->\n  <link rel=\"stylesheet\" href=\"https://code.getmdl.io/1.3.0/material.blue-light_blue.min.css\" />\n  <link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/icon?family=Material+Icons\">\n  <script defer src=\"https://code.getmdl.io/1.1.3/material.min.js\"></script>\n\n  <link rel=\"stylesheet\" href=\"main.css\">\n\n  <!-- Import and configure the Firebase SDK -->\n  <!-- These scripts are made available when the app is served or deployed on Firebase Hosting -->\n  <!-- If you do not serve/host your project using Firebase Hosting see https://firebase.google.com/docs/web/setup -->\n  <script src=\"/__/firebase/10.0.0/firebase-app-compat.js\"></script>\n  <script src=\"/__/firebase/10.0.0/firebase-auth-compat.js\"></script>\n  <script src=\"/__/firebase/10.0.0/firebase-database-compat.js\"></script>\n  <script src=\"/__/firebase/init.js\"></script>\n\n  <script type=\"text/javascript\">\n    /**\n     * Handles the sign in button press.\n     */\n    function toggleSignIn() {\n      if (firebase.auth().currentUser) {\n        // [START signout]\n        firebase.auth().signOut();\n        // [END signout]\n      } else {\n        // [START authanon]\n        firebase.auth().signInAnonymously().catch(function(error) {\n          // Handle Errors here.\n          var errorCode = error.code;\n          var errorMessage = error.message;\n          // [START_EXCLUDE]\n          if (errorCode === 'auth/operation-not-allowed') {\n            alert('You must enable Anonymous auth in the Firebase Console.');\n          } else {\n            console.error(error);\n          }\n          // [END_EXCLUDE]\n        });\n        // [END authanon]\n      }\n      document.getElementById('quickstart-sign-in').disabled = true;\n    }\n\n\n    /**\n     * initApp handles setting up UI event listeners and registering Firebase auth listeners:\n     *  - firebase.auth().onAuthStateChanged: This listener is called when the user is signed in or\n     *    out, and that is where we update the UI.\n     */\n    function initApp() {\n      // Listening for auth state changes.\n      // [START authstatelistener]\n      firebase.auth().onAuthStateChanged(function (user) {\n        if (user) {\n          // User is signed in.\n          var isAnonymous = user.isAnonymous;\n          var uid = user.uid;\n          console.log('uid', uid);\n          // [START_EXCLUDE]\n          document.getElementById('quickstart-sign-in-status').textContent = 'Signed in';\n          document.getElementById('quickstart-sign-in').textContent = 'Sign out';\n          // firebase.database().ref('users/' + uid).set({ paypal: \"false\"});\n\n          firebase.database().ref('users/' + uid).once('value').then(function(snapshot) {\n            var paypal = snapshot.val();\n            document.getElementById('quickstart-account-details').textContent = JSON.stringify(paypal, null,\n              '  ');\n            document.getElementById('uid').value = uid;\n            document.getElementById('hidden-uid').value = uid;\n          });\n          // [END_EXCLUDE]\n        } else {\n          // User is signed out.\n          // [START_EXCLUDE]\n          document.getElementById('quickstart-sign-in-status').textContent = 'Signed out';\n          document.getElementById('quickstart-sign-in').textContent = 'Sign in';\n          document.getElementById('quickstart-account-details').textContent = 'null';\n          // [END_EXCLUDE]\n        }\n        // [START_EXCLUDE]\n        document.getElementById('quickstart-sign-in').disabled = false;\n        // [END_EXCLUDE]\n      });\n      // [END authstatelistener]\n\n      document.getElementById('quickstart-sign-in').addEventListener('click', toggleSignIn, false);\n    }\n\n    window.onload = function () {\n      initApp();\n    };\n  </script>\n</head>\n\n<body>\n  <div class=\"demo-layout mdl-layout mdl-js-layout mdl-layout--fixed-header\">\n\n    <!-- Header section containing title -->\n    <header class=\"mdl-layout__header mdl-color-text--white mdl-color--light-blue-700\">\n      <div class=\"mdl-cell mdl-cell--12-col mdl-cell--12-col-tablet mdl-grid\">\n        <div class=\"mdl-layout__header-row mdl-cell mdl-cell--12-col mdl-cell--12-col-tablet mdl-cell--8-col-desktop\">\n          <a href=\"/\">\n            <h3>Firebase Auth + PayPal </h3>\n          </a>\n        </div>\n      </div>\n    </header>\n\n    <main class=\"mdl-layout__content mdl-color--grey-100\">\n      <div class=\"mdl-cell mdl-cell--12-col mdl-cell--12-col-tablet mdl-grid\">\n\n        <!-- Container for the demo -->\n        <div class=\"mdl-card mdl-shadow--2dp mdl-cell mdl-cell--12-col mdl-cell--12-col-tablet mdl-cell--12-col-desktop\">\n          <div class=\"mdl-card__title mdl-color--light-blue-600 mdl-color-text--white\">\n            <h2 class=\"mdl-card__title-text\">Anonymous Auth</h2>\n          </div>\n          <div class=\"mdl-card__supporting-text mdl-color-text--grey-600\">\n            <p>Sign in anonymously.</p>\n            <button disabled class=\"mdl-button mdl-js-button mdl-button--raised\" id=\"quickstart-sign-in\" name=\"signin\">Sign in</button>\n            <hr>\n            <h4>Demo PayPal-rest-sdk</h4>\n            <!-- PayPal Form-->\n            <div class=\"mdl-grid\">\n              <div class=\"mdl-cell mdl-cell--4-col\">\n                <form action=\"/pay\" id=\"paypal-form\" method=\"POST\">\n                  <div class=\"mdl-textfield mdl-js-textfield mdl-textfield--floating-label\">\n                    <input class=\"mdl-textfield__input\" id=\"sample1\" type=\"text\" name=\"price\" value=\"5\" pattern=\"-?[0-9]*(\\.[0-9]+)?\" required>\n                    <label class=\"mdl-textfield__label\" for=\"sample1\">Price</label>\n                    <span class=\"mdl-textfield__error\">Input is not a number!</span>\n                  </div>\n                  <br>\n                  <div class=\"mdl-textfield mdl-js-textfield mdl-textfield--floating-label\">\n                    <input class=\"mdl-textfield__input\" type=\"text\" name=\"uid\" value=\" \" id=\"uid\" disabled><br>\n                    <label class=\"mdl-textfield__label\" for=\"uid\">Uid</label>\n                    <input class=\"mdl-textfield__input\" type=\"hidden\" name=\"uid\" value=\"\" id=\"hidden-uid\"><br>\n                    <button class=\"mdl-button mdl-js-button mdl-button--raised mdl-button--colored\" form=\"paypal-form\" type=\"submit\" value=\"Submit\">\n               Submit\n              </button>\n                  </div>\n                </form>\n              </div>\n            </div>\n          </div>\n          <!-- Container where we'll display the user details -->\n          <div class=\"quickstart-user-details-container\">\n            Firebase sign-in status: <span id=\"quickstart-sign-in-status\">Unknown</span>\n            <div>Firebase auth <code>currentUser</code> object value:</div>\n            <!-- Database PayPal info -->\n            <pre><code id=\"quickstart-account-details\">null</code></pre>\n          </div>\n        </div>\n      </div>\n\n  </div>\n  </main>\n  </div>\n</body>\n</html>\n"
  },
  {
    "path": "Node-1st-gen/paypal/public/main.css",
    "content": "/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nhtml, body {\n  font-family: 'Roboto', 'Helvetica', sans-serif;\n  background-color: #f5f5f5;\n}\na {\n  text-decoration: none;\n}\nli a {\n  text-decoration: underline;\n  color: #0288d1;\n}\n.mdl-card {\n  overflow: visible;\n}\n.grecaptcha-logo {\n  background-color: white;\n}\n.mdl-grid {\n  max-width: 1024px;\n  margin: auto;\n}\n.mdl-layout__header-row {\n  padding: 0;\n}\n.quickstart-user-details-container, .user-details-container {\n  margin-top: 20px;\n  line-height: 25px;\n}\n#quickstart-sign-in-status, #sign-in-status {\n  font-weight: bold;\n}\npre {\n  overflow-x: scroll;\n  line-height: 18px;\n}\ncode {\n  white-space: pre-wrap;\n  word-break: break-all;\n}\nh3 {\n  background: url('firebase-logo.png') no-repeat;\n  background-size: 40px;\n  padding-left: 50px;\n  color: white;\n}\n#verification-code-form {\n  display:none;\n}\n#recaptcha-container {\n  margin-top: 10px;\n  margin-bottom: 20px;\n}\n#verify-code-button, #cancel-verify-code-button {\n  margin-left: 20px;\n}\n#sign-out-button {\n  display: none;\n}\n#sign-in-card {\n  z-index: 2;\n}\n"
  },
  {
    "path": "Node-1st-gen/paypal/public/success.html",
    "content": "<!DOCTYPE html>\n<!--\n Copyright 2023 Google LLC\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n     http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<html lang=\"fr\">\n<head>\n    <meta charset=\"utf-8\">\n    <title>Successs Page</title>\n</head>\n<body>\n    <h1>Success Page</h1>\n    <a disabled class=\"mdl-button mdl-js-button mdl-button--raised\"  href=\"/\">Home</a>\n</body>\n</html>\n"
  },
  {
    "path": "Node-1st-gen/presence-firestore/README.md",
    "content": "# Presence in Firestore\n\nThis template shows you how to build a presence system (understanding which users are online / offline) in Firestore with some help from the Realtime Database. An explanation of why this is necessary, how it works, and what changes you need to implement in a client app can be found [in the docs](https://firebase.google.com/docs/firestore/solutions/presence).\n\n## Functions Code\n\nSee file [functions/index.js](functions/index.js) for the code.\n\nThe dependencies are listed in [functions/package.json](functions/package.json).\n\nTo install:\n\n```sh\ncd functions\nnpm install\n```\n\n## Sample Database Structure\n\nAs an example we'll be using a secure note structure:\n\n```\n/status\n    /UID_A\n        state: \"online\"\n    /UID_B\n        state: \"offline\"\n```\n\nWhenever a new note is created or modified a Function sends the content to be indexed to the Firestore instance.\n\n## Sample Client Code\n\nThe sample client app in [public/index.html](public/index.html) and [public/index.js](public/index.js) will anonymously log in the user, create `status/UID_A` with a `last_changed` and `state` in `Realtime Database`, then mirror that over to a `UID_A` document in the app's `Firestore` collection `status`.\n\nTo deploy the sample to your Firebase app,\n\n1. Run `npm install` to install dependencies for the server-side [functions](functions/) as detailed above.\n2. From this top-level sample directory, deploy the `Realtime Database` trigger defined in [functions](functions/) to `Firebase Functions` and the [public](public/) directory app to `Firebase Hosting`.\n\nAssumimg you've created a Firebase application called `firebase-example-123` (make sure it's upgraded to the Spark plan and that `Anonymous Authentication` are enabled).\n\n```sh\nfirebase use --add firebase-example-123\nfirebase deploy\n```\n\nThen visit `https://firebase-example-123.web.app` in your browser and you should see `User <UID_A>` is online written to the index file, as well as the associated data in `Realtime Database` and `Firestore`.\n"
  },
  {
    "path": "Node-1st-gen/presence-firestore/firebase.json",
    "content": "{\n  \"functions\": {\n    \"codebase\": \"presence-firestore\"\n  },\n  \"hosting\": {\"public\": \"public\"}\n}\n"
  },
  {
    "path": "Node-1st-gen/presence-firestore/functions/.eslintrc.json",
    "content": "{\n  \"parserOptions\": {\n    // Required for certain syntax usages\n    \"ecmaVersion\": 2022\n  },\n  \"plugins\": [\n    \"promise\"\n  ],\n  \"extends\": \"eslint:recommended\",\n  \"rules\": {\n    // Removed rule \"disallow the use of console\" from recommended eslint rules\n    \"no-console\": \"off\",\n\n    // Removed rule \"disallow multiple spaces in regular expressions\" from recommended eslint rules\n    \"no-regex-spaces\": \"off\",\n\n    // Removed rule \"disallow the use of debugger\" from recommended eslint rules\n    \"no-debugger\": \"off\",\n\n    // Removed rule \"disallow unused variables\" from recommended eslint rules\n    \"no-unused-vars\": \"off\",\n\n    // Removed rule \"disallow mixed spaces and tabs for indentation\" from recommended eslint rules\n    \"no-mixed-spaces-and-tabs\": \"off\",\n\n    // Removed rule \"disallow the use of undeclared variables unless mentioned in /*global */ comments\" from recommended eslint rules\n    \"no-undef\": \"off\",\n\n    // Warn against template literal placeholder syntax in regular strings\n    \"no-template-curly-in-string\": 1,\n\n    // Warn if return statements do not either always or never specify values\n    \"consistent-return\": 1,\n\n    // Warn if no return statements in callbacks of array methods\n    \"array-callback-return\": 1,\n\n    // Requre the use of === and !==\n    \"eqeqeq\": 2,\n\n    // Disallow the use of alert, confirm, and prompt\n    \"no-alert\": 2,\n\n    // Disallow the use of arguments.caller or arguments.callee\n    \"no-caller\": 2,\n\n    // Disallow null comparisons without type-checking operators\n    \"no-eq-null\": 2,\n\n    // Disallow the use of eval()\n    \"no-eval\": 2,\n\n    // Warn against extending native types\n    \"no-extend-native\": 1,\n\n    // Warn against unnecessary calls to .bind()\n    \"no-extra-bind\": 1,\n\n    // Warn against unnecessary labels    \n    \"no-extra-label\": 1,\n\n    // Disallow leading or trailing decimal points in numeric literals\n    \"no-floating-decimal\": 2,\n\n    // Warn against shorthand type conversions\n    \"no-implicit-coercion\": 1,\n\n    // Warn against function declarations and expressions inside loop statements\n    \"no-loop-func\": 1,\n\n    // Disallow new operators with the Function object\n    \"no-new-func\": 2,\n\n    // Warn against new operators with the String, Number, and Boolean objects\n    \"no-new-wrappers\": 1,\n\n    // Disallow throwing literals as exceptions\n    \"no-throw-literal\": 2,\n\n    // Require using Error objects as Promise rejection reasons\n    \"prefer-promise-reject-errors\": 2,\n\n    // Enforce “for” loop update clause moving the counter in the right direction\n    \"for-direction\": 2,\n\n    // Enforce return statements in getters\n    \"getter-return\": 2,\n\n    // Disallow await inside of loops\n    \"no-await-in-loop\": 2,\n\n    // Disallow comparing against -0\n    \"no-compare-neg-zero\": 2,\n\n    // Warn against catch clause parameters from shadowing variables in the outer scope\n    \"no-catch-shadow\": 1,\n\n    // Disallow identifiers from shadowing restricted names\n    \"no-shadow-restricted-names\": 2,\n\n    // Enforce return statements in callbacks of array methods\n    \"callback-return\": 2,\n\n    // Require error handling in callbacks\n    \"handle-callback-err\": 2,\n\n    // Warn against string concatenation with __dirname and __filename\n    \"no-path-concat\": 1,\n\n    // Prefer using arrow functions for callbacks\n    \"prefer-arrow-callback\": 1,\n\n    // Return inside each then() to create readable and reusable Promise chains.\n    \"promise/always-return\": 2,\n\n    //Enforces the use of catch() on un-returned promises\n    \"promise/catch-or-return\": 2,\n\n    // Warn against nested then() or catch() statements\n    \"promise/no-nesting\": 1\n  }\n}\n"
  },
  {
    "path": "Node-1st-gen/presence-firestore/functions/index.js",
    "content": "/**\n * Copyright 2017 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the 'License');\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an 'AS IS' BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// [START presence_sync_function]\nconst functions = require('firebase-functions/v1');\nconst admin = require('firebase-admin');\nadmin.initializeApp();\n\n// Since this code will be running in the Cloud Functions environment\n// we call initialize Firestore without any arguments because it\n// detects authentication from the environment.\nconst firestore = admin.firestore();\n\n// Create a new function which is triggered on changes to /status/{uid}\n// Note: This is a Realtime Database trigger, *not* Firestore.\nexports.onUserStatusChanged = functions.database.ref('/status/{uid}').onUpdate(\n    async (change, context) => {\n      // Get the data written to Realtime Database\n      const eventStatus = change.after.val();\n\n      // Then use other event data to create a reference to the\n      // corresponding Firestore document.\n      const userStatusFirestoreRef = firestore.doc(`status/${context.params.uid}`);\n\n      // It is likely that the Realtime Database change that triggered\n      // this event has already been overwritten by a fast change in\n      // online / offline status, so we'll re-read the current data\n      // and compare the timestamps.\n      const statusSnapshot = await change.after.ref.once('value');\n      const status = statusSnapshot.val();\n      functions.logger.log(status, eventStatus);\n      // If the current timestamp for this data is newer than\n      // the data that triggered this event, we exit this function.\n      if (status.last_changed > eventStatus.last_changed) {\n        return null;\n      }\n\n      // Otherwise, we convert the last_changed field to a Date\n      eventStatus.last_changed = new Date(eventStatus.last_changed);\n\n      // ... and write it to Firestore.\n      return userStatusFirestoreRef.set(eventStatus);\n    });\n// [END presence_sync_function]\n"
  },
  {
    "path": "Node-1st-gen/presence-firestore/functions/package.json",
    "content": "{\n  \"name\": \"presence-firestore\",\n  \"description\": \"Presence for Firestore\",\n  \"dependencies\": {\n    \"eslint-plugin-promise\": \"^7.2.1\",\n    \"firebase-admin\": \"^13.0.2\",\n    \"firebase-functions\": \"7.0.0\"\n  },\n  \"devDependencies\": {\n    \"eslint\": \"^8.57.1\"\n  },\n  \"scripts\": {\n    \"lint\": \"./node_modules/.bin/eslint --max-warnings=0 .\",\n    \"serve\": \"firebase emulators:start --only functions\",\n    \"shell\": \"firebase functions:shell\",\n    \"start\": \"npm run shell\",\n    \"deploy\": \"firebase deploy --only functions\",\n    \"logs\": \"firebase functions:log\",\n    \"compile\": \"cp ../../../tsconfig.template.json ./tsconfig-compile.json && tsc --project tsconfig-compile.json\"\n  },\n  \"engines\": {\n    \"node\": \"20\"\n  },\n  \"private\": true\n}\n"
  },
  {
    "path": "Node-1st-gen/presence-firestore/public/index.html",
    "content": "<!doctype html>\n<!--\n  Copyright 2016 Google Inc. All rights reserved.\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n      https://www.apache.org/licenses/LICENSE-2.0\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License\n-->\n<html> \n    <head>\n        <title>Presence in Firestore</title>\n    </head>\n    <body>\n        <h3>History of user presence (since this page was opened)</h3>\n        <div id=\"history\">\n\n        </div>\n        <!-- Import and configure the Firebase SDK -->\n        <!-- These scripts are made available when the app is served or deployed on Firebase Hosting -->\n        <!-- If you do not serve/host your project using Firebase Hosting see https://firebase.google.com/docs/web/setup -->\n        <script src=\"/__/firebase/10.0.0/firebase-app-compat.js\"></script>\n        <script src=\"/__/firebase/10.0.0/firebase-auth-compat.js\"></script>\n        <script src=\"/__/firebase/10.0.0/firebase-database-compat.js\"></script>\n        <script src=\"/__/firebase/10.0.0/firebase-firestore-compat.js\"></script>\n   \n        <script src=\"/__/firebase/init.js\"></script>\n\n        <script src=\"./index.js\"></script>\n    </body>\n</html>\n"
  },
  {
    "path": "Node-1st-gen/presence-firestore/public/index.js",
    "content": "/**\n * Copyright 2017 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the 'License');\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an 'AS IS' BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nfunction rtdb_presence() {\n    // [START rtdb_presence]\n    // Fetch the current user's ID from Firebase Authentication.\n    var uid = firebase.auth().currentUser.uid;\n\n    // Create a reference to this user's specific status node.\n    // This is where we will store data about being online/offline.\n    var userStatusDatabaseRef = firebase.database().ref('/status/' + uid);\n\n    // We'll create two constants which we will write to \n    // the Realtime database when this device is offline\n    // or online.\n    var isOfflineForDatabase = {\n        state: 'offline',\n        last_changed: firebase.database.ServerValue.TIMESTAMP,\n    };\n\n    var isOnlineForDatabase = {\n        state: 'online',\n        last_changed: firebase.database.ServerValue.TIMESTAMP,\n    };\n\n    // Create a reference to the special '.info/connected' path in \n    // Realtime Database. This path returns `true` when connected\n    // and `false` when disconnected.\n    firebase.database().ref('.info/connected').on('value', function(snapshot) {\n        // If we're not currently connected, don't do anything.\n        if (snapshot.val() == false) {\n            return;\n        };\n\n        // If we are currently connected, then use the 'onDisconnect()' \n        // method to add a set which will only trigger once this \n        // client has disconnected by closing the app, \n        // losing internet, or any other means.\n        userStatusDatabaseRef.onDisconnect().set(isOfflineForDatabase).then(function() {\n            // The promise returned from .onDisconnect().set() will\n            // resolve as soon as the server acknowledges the onDisconnect() \n            // request, NOT once we've actually disconnected:\n            // https://firebase.google.com/docs/reference/js/firebase.database.OnDisconnect\n\n            // We can now safely set ourselves as 'online' knowing that the\n            // server will mark us as offline once we lose connection.\n            userStatusDatabaseRef.set(isOnlineForDatabase);\n        });\n    });\n    // [END rtdb_presence]\n}\n\nfunction rtdb_and_local_fs_presence() {\n    // [START rtdb_and_local_fs_presence]\n    // [START_EXCLUDE]\n    var uid = firebase.auth().currentUser.uid;\n    var userStatusDatabaseRef = firebase.database().ref('/status/' + uid);\n\n    var isOfflineForDatabase = {\n        state: 'offline',\n        last_changed: firebase.database.ServerValue.TIMESTAMP,\n    };\n\n    var isOnlineForDatabase = {\n        state: 'online',\n        last_changed: firebase.database.ServerValue.TIMESTAMP,\n    };\n\n    // [END_EXCLUDE]\n    var userStatusFirestoreRef = firebase.firestore().doc('/status/' + uid);\n\n    // Firestore uses a different server timestamp value, so we'll \n    // create two more constants for Firestore state.\n    var isOfflineForFirestore = {\n        state: 'offline',\n        last_changed: firebase.firestore.FieldValue.serverTimestamp(),\n    };\n\n    var isOnlineForFirestore = {\n        state: 'online',\n        last_changed: firebase.firestore.FieldValue.serverTimestamp(),\n    };\n\n    firebase.database().ref('.info/connected').on('value', function(snapshot) {\n        if (snapshot.val() == false) {\n            // Instead of simply returning, we'll also set Firestore's state\n            // to 'offline'. This ensures that our Firestore cache is aware\n            // of the switch to 'offline.'\n            userStatusFirestoreRef.set(isOfflineForFirestore);\n            return;\n        };\n\n        userStatusDatabaseRef.onDisconnect().set(isOfflineForDatabase).then(function() {\n            userStatusDatabaseRef.set(isOnlineForDatabase);\n\n            // We'll also add Firestore set here for when we come online.\n            userStatusFirestoreRef.set(isOnlineForFirestore);\n        });\n    });\n    // [END rtdb_and_local_fs_presence]\n}\n\nfunction fs_listen() {\n    // [START fs_onsnapshot]\n    userStatusFirestoreRef.onSnapshot(function(doc) {\n        var isOnline = doc.data().state == 'online';\n        // ... use isOnline\n    });\n    // [END fs_onsnapshot]\n}\n\nfunction fs_listen_online() {\n    var history = document.querySelector('#history');\n    // [START fs_onsnapshot_online]\n    firebase.firestore().collection('status')\n        .where('state', '==', 'online')\n        .onSnapshot(function(snapshot) {\n            snapshot.docChanges().forEach(function(change) {\n                if (change.type === 'added') {\n                    var msg = 'User ' + change.doc.id + ' is online.';\n                    console.log(msg);\n                    // [START_EXCLUDE]\n                    history.innerHTML += msg + '<br />';\n                    // [END_EXCLUDE]\n                }\n                if (change.type === 'removed') {\n                    var msg = 'User ' + change.doc.id + ' is offline.';\n                    console.log(msg);\n                    // [START_EXCLUDE]\n                    history.innerHTML += msg + '<br />'\n                    // [END_EXCLUDE]\n                }\n            });\n        });\n    // [END fs_onsnapshot_online]\n}\n\nfirebase.auth().signInAnonymously().then(function() {\n    rtdb_and_local_fs_presence();\n    fs_listen_online();\n}).catch(function(err) {\n    console.warn(err);\n    console.warn('Please enable Anonymous Authentication in your Firebase project!');\n});\n"
  },
  {
    "path": "Node-1st-gen/publish-model/README.md",
    "content": "# Automatically Publish Models to Firebase ML\n\nThis sample demonstrates how to automatically publish models to Firebase ML for each TensorFlow Lite file that is uploaded to Firebase Storage.\n\n\n## Functions Code\n\nSee file [functions/index.js](functions/index.js) for the model publishing code.\n\nThe dependencies are listed in [functions/package.json](functions/package.json).\n\n\n## Trigger rules\n\nThe function triggers on upload of any file to your Firebase project's default Cloud Storage bucket.\n\n\n## Deploy and test\n\nTo deploy and test the sample:\n\n - Create a Firebase project on the [Firebase Console](https://console.firebase.google.com) and visit the **Storage** tab.\n - Clone this repo: `git clone https://github.com/firebase/functions-samples`.\n - Open this sample's directory: `cd functions-samples/publish-model`\n - Setup your project by running `firebase use --add` and select the project you had created.\n - Install dependencies in the functions directory: `cd functions; npm install; cd -`\n - Deploy your project using `firebase deploy`\n - Go to your project's [**Cloud Console > IAM & admin > IAM**](https://console.cloud.google.com/iam-admin/iam?project=_), Find the **App Engine default service account**.\n  - Add the **Service Account Token Creator** role to that member. This will allow your app to create signed public URLs to the models.\n  - Add the **Firebase ML Kit Admin** role to that member. This will allow your app to manage Firebase ML models.\n  - Add the **Firebase Admin SDK Administrator Service Agent** role to that member. This will allow your app to manage models with the Firebase Admin SDK.\n  - Go to the Firebase Console **Storage** tab and upload a Tensorflow Lite model (*.tflite). After a short time a model with same file name will be published to Firebase ML. \n - Go to the Firebase Console **ML Kit** (Custom) tab and see that a new model with file name has been created.\n"
  },
  {
    "path": "Node-1st-gen/publish-model/firebase.json",
    "content": "{\n  \"functions\": {\n    \"codebase\": \"publish-model\",\n    \"predeploy\": [\n      \"npm --prefix \\\"$RESOURCE_DIR\\\" run lint\"\n    ]\n  }\n}\n"
  },
  {
    "path": "Node-1st-gen/publish-model/functions/.eslintrc.json",
    "content": "{\n  \"parserOptions\": {\n    // Required for certain syntax usages\n    \"ecmaVersion\": 2022\n  },\n  \"plugins\": [\n    \"promise\"\n  ],\n  \"extends\": \"eslint:recommended\",\n  \"rules\": {\n    // Removed rule \"disallow the use of console\" from recommended eslint rules\n    \"no-console\": \"off\",\n\n    // Removed rule \"disallow multiple spaces in regular expressions\" from recommended eslint rules\n    \"no-regex-spaces\": \"off\",\n\n    // Removed rule \"disallow the use of debugger\" from recommended eslint rules\n    \"no-debugger\": \"off\",\n\n    // Removed rule \"disallow unused variables\" from recommended eslint rules\n    \"no-unused-vars\": \"off\",\n\n    // Removed rule \"disallow mixed spaces and tabs for indentation\" from recommended eslint rules\n    \"no-mixed-spaces-and-tabs\": \"off\",\n\n    // Removed rule \"disallow the use of undeclared variables unless mentioned in /*global */ comments\" from recommended eslint rules\n    \"no-undef\": \"off\",\n\n    // Warn against template literal placeholder syntax in regular strings\n    \"no-template-curly-in-string\": 1,\n\n    // Warn if return statements do not either always or never specify values\n    \"consistent-return\": 1,\n\n    // Warn if no return statements in callbacks of array methods\n    \"array-callback-return\": 1,\n\n    // Require the use of === and !==\n    \"eqeqeq\": 2,\n\n    // Disallow the use of alert, confirm, and prompt\n    \"no-alert\": 2,\n\n    // Disallow the use of arguments.caller or arguments.callee\n    \"no-caller\": 2,\n\n    // Disallow null comparisons without type-checking operators\n    \"no-eq-null\": 2,\n\n    // Disallow the use of eval()\n    \"no-eval\": 2,\n\n    // Warn against extending native types\n    \"no-extend-native\": 1,\n\n    // Warn against unnecessary calls to .bind()\n    \"no-extra-bind\": 1,\n\n    // Warn against unnecessary labels    \n    \"no-extra-label\": 1,\n\n    // Disallow leading or trailing decimal points in numeric literals\n    \"no-floating-decimal\": 2,\n\n    // Warn against shorthand type conversions\n    \"no-implicit-coercion\": 1,\n\n    // Warn against function declarations and expressions inside loop statements\n    \"no-loop-func\": 1,\n\n    // Disallow new operators with the Function object\n    \"no-new-func\": 2,\n\n    // Warn against new operators with the String, Number, and Boolean objects\n    \"no-new-wrappers\": 1,\n\n    // Disallow throwing literals as exceptions\n    \"no-throw-literal\": 2,\n\n    // Require using Error objects as Promise rejection reasons\n    \"prefer-promise-reject-errors\": 2,\n\n    // Enforce “for” loop update clause moving the counter in the right direction\n    \"for-direction\": 2,\n\n    // Enforce return statements in getters\n    \"getter-return\": 2,\n\n    // Disallow await inside of loops\n    \"no-await-in-loop\": 2,\n\n    // Disallow comparing against -0\n    \"no-compare-neg-zero\": 2,\n\n    // Warn against catch clause parameters from shadowing variables in the outer scope\n    \"no-catch-shadow\": 1,\n\n    // Disallow identifiers from shadowing restricted names\n    \"no-shadow-restricted-names\": 2,\n\n    // Enforce return statements in callbacks of array methods\n    \"callback-return\": 2,\n\n    // Require error handling in callbacks\n    \"handle-callback-err\": 2,\n\n    // Warn against string concatenation with __dirname and __filename\n    \"no-path-concat\": 1,\n\n    // Prefer using arrow functions for callbacks\n    \"prefer-arrow-callback\": 1,\n\n    // Return inside each then() to create readable and reusable Promise chains.\n    // Forces developers to return console logs and http calls in promises. \n    \"promise/always-return\": 2,\n\n    //Enforces the use of catch() on un-returned promises\n    \"promise/catch-or-return\": 2,\n\n    // Warn against nested then() or catch() statements\n    \"promise/no-nesting\": 1\n  }\n}\n"
  },
  {
    "path": "Node-1st-gen/publish-model/functions/index.js",
    "content": "/**\n * Copyright 2020 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use strict';\n\nconst functions = require('firebase-functions/v1');\nconst admin = require('firebase-admin');\nadmin.initializeApp();\n\nconst ml = admin.machineLearning();\nconst path = require('path');\n\n/**\n * When a .tflite file is uploaded into the GCS bucket, we automatically create\n * a Firebase Model based on it and publish the model.\n * The name of the model is the name of the file.\n *\n */\n\n// [START publishModelTrigger]\nexports.createFirebaseTFLiteModel = functions.storage.object().onFinalize(async (object) => {\n// [END publishModelTrigger]\n  // [START eventAttributes]\n  const fileBucket = object.bucket; // The Storage bucket that contains the file.\n  const filePath = object.name; // File path in the bucket.\n  const contentType = object.contentType; // File content type. E.g. application/octet-stream\n  const metageneration = object.metageneration; // Number of times metadata has been generated. New objects have a value of 1.\n  // [END eventAttributes]\n\n  // [START stopConditions]\n  // Exit if this is triggered on a file that is not a tflite file\n  if (!filePath.endsWith('.tflite')) {\n    return functions.logger.log(\n      `Content type is: ${contentType}. This is not a TFLite file.`\n    );\n  }\n  // [END stopConditions]\n\n  // Get the file name without the path or extension.\n  const modelName = path.basename(filePath, '.tflite');\n\n  // Make sure the file name matches model name requirements (regex).\n  const re = /^[A-Za-z\\d_-]{1,32}$/;\n  if (!re.test(modelName)) {\n    const errMsg = \"Model name must be 1 to 32 characters long, and may only consist of alphanumeric characters, underscores, and hyphens.\";\n    return functions.logger.log(`Not creating model '${modelName}'. ${errMsg}`);\n  }\n\n  const gcsUri = `gs://${fileBucket}/${filePath}`;\n\n  const modelOptions = {\n    displayName: modelName,\n    tfliteModel: { gcsTfliteUri: gcsUri }\n  };\n\n  // List models with same name. (Display name is unique - this returns exactly 0 or 1 models)\n  const modelsResult = await ml.listModels({filter: `display_name = ${modelName}`});\n\n  let model;\n  if (modelsResult.models.length > 0) {\n    // The model already exists.\n    // Check the source to see if it matches\n    const existingModel = modelsResult.models[0];\n    if (existingModel.tfliteModel.gcsTfliteUri === gcsUri) {\n\t  // It's ok to update this model since it's from the same source file.\n      model = await ml.updateModel(existingModel.modelId, modelOptions);\n    } else {\n      // For safety - don't overwrite (update) Firebase Models that were created a different way.\n      return functions.logger.log(\n        'Not updating existing model with same name. Existing model was created from a different source location.'\n      );\n    }\n  } else {\n\t  model = await ml.createModel(modelOptions);\n  }\n\n  if (model.validationError) {\n    // Can't publish models with validation errors\n    return functions.logger.log(\n      `Validation error: ${model.validationError}. Will not publish the model.`\n    );\n  }\n\n  const publishedModel = await ml.publishModel(model.modelId);\n  return functions.logger.log(\n    `Model ${modelName} published at ${publishedModel.updateTime}.`\n  );\n});\n// [END publishModel]\n"
  },
  {
    "path": "Node-1st-gen/publish-model/functions/package.json",
    "content": "{\n  \"name\": \"functions\",\n  \"description\": \"Cloud Functions for Firebase\",\n  \"scripts\": {\n    \"lint\": \"eslint .\",\n    \"serve\": \"firebase emulators:start --only functions\",\n    \"shell\": \"firebase functions:shell\",\n    \"start\": \"npm run shell\",\n    \"deploy\": \"firebase deploy --only functions\",\n    \"logs\": \"firebase functions:log\",\n    \"compile\": \"cp ../../../tsconfig.template.json ./tsconfig-compile.json && tsc --project tsconfig-compile.json\"\n  },\n  \"engines\": {\n    \"node\": \"20\"\n  },\n  \"dependencies\": {\n    \"eslint-plugin-promise\": \"^7.2.1\",\n    \"firebase-admin\": \"^13.0.2\",\n    \"firebase-functions\": \"7.0.0\"\n  },\n  \"devDependencies\": {\n    \"eslint\": \"^8.57.1\",\n    \"firebase-functions-test\": \"^3.4.0\"\n  },\n  \"private\": true\n}\n"
  },
  {
    "path": "Node-1st-gen/quickstarts/auth-blocking-functions/.gitignore",
    "content": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\nfirebase-debug.log*\nfirebase-debug.*.log*\n\n# Firebase cache\n.firebase/\n\n# Firebase config\n\n# Uncomment this if you'd like others to create their own Firebase project.\n# For a team working on the same Firebase project(s), it is recommended to leave\n# it commented so all members can deploy to the same project(s) in .firebaserc.\n# .firebaserc\n\n# Runtime data\npids\n*.pid\n*.seed\n*.pid.lock\n\n# Directory for instrumented libs generated by jscoverage/JSCover\nlib-cov\n\n# Coverage directory used by tools like istanbul\ncoverage\n\n# nyc test coverage\n.nyc_output\n\n# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)\n.grunt\n\n# Bower dependency directory (https://bower.io/)\nbower_components\n\n# node-waf configuration\n.lock-wscript\n\n# Compiled binary addons (http://nodejs.org/api/addons.html)\nbuild/Release\n\n# Dependency directories\nnode_modules/\n\n# Optional npm cache directory\n.npm\n\n# Optional eslint cache\n.eslintcache\n\n# Optional REPL history\n.node_repl_history\n\n# Output of 'npm pack'\n*.tgz\n\n# Yarn Integrity file\n.yarn-integrity\n\n# dotenv environment variables file\n.env\n"
  },
  {
    "path": "Node-1st-gen/quickstarts/auth-blocking-functions/README.md",
    "content": "# Firebase SDK for Cloud Functions Quickstart - Auth Blocking Functions\n================================================\n\nThe Auth Blocking functions Quickstart demonstrates how to block account sign in and creation when using Firebase Auth or Google Cloud Identity Platform in a Firebase App.\n\n\n- [Read more about auth blocking functions](https://firebase.google.com/docs/auth/extend-with-blocking-functions)\n- [Read more about Cloud Functions for Firebase](https://firebase.google.com/docs/functions/)\n\n\nGetting Started\n---------------\n\nTo try this sample, you need a test app with Firebase Auth and Cloud Firestore enabled. Don't use a live app with real users!\n\n1. Install dependencies with `npm install`\n2. Deploy the functions with `firebase deploy --only functions`\n3. Try to create an account using an email address with a domain _other than_ `@acme.com`. It should fail.\n4. Add an existing user's email address to the `banned` collection in Cloud Firestore. Then, try to sign in as that user. It should fail.\n\n\nLicense\n-------\n\n© Google, 2022. Licensed under an [Apache-2](../../../LICENSE) license.\n"
  },
  {
    "path": "Node-1st-gen/quickstarts/auth-blocking-functions/firebase.json",
    "content": "{\n  \"functions\": {\n    \"codebase\": \"auth-blocking-functions\",\n    \"predeploy\": [\n      \"npm --prefix \\\"$RESOURCE_DIR\\\" run lint\"\n    ]\n  }\n}\n"
  },
  {
    "path": "Node-1st-gen/quickstarts/auth-blocking-functions/functions/.eslintrc.js",
    "content": "/**\n * Copyright 2023 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nmodule.exports = {\n  root: true,\n  env: {\n    es2020: true,\n    node: true,\n  },\n  extends: [\n    \"eslint:recommended\",\n    \"google\",\n  ],\n  rules: {\n    quotes: [\"error\", \"double\"],\n  },\n};\n"
  },
  {
    "path": "Node-1st-gen/quickstarts/auth-blocking-functions/functions/.gitignore",
    "content": "node_modules/"
  },
  {
    "path": "Node-1st-gen/quickstarts/auth-blocking-functions/functions/index.js",
    "content": "/**\n * Copyright 2022 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nconst {functions} = require(\"firebase-functions/v1\");\nconst {admin} = require(\"firebase-admin\");\n\nadmin.initializeApp();\nconst db = admin.firestore();\n\n// [START v1ValidateNewUser]\n// [START v1beforeCreateFunctionTrigger]\n// Block account creation with any non-acme email address.\nexports.validateNewUser = functions.auth\n    .user()\n    .beforeCreate((user, context) => {\n    // [END v1beforeCreateFunctionTrigger]\n    // [START v1readEmailData]\n    // Email passed from the User object.\n      const email = user.email || \"\";\n      // [END v1readEmailData]\n\n      // [START v1domainHttpsError]\n      // Only users of a specific domain can sign up.\n      if (!email.includes(\"acme.com\")) {\n        // Throwing an HttpsError so that Auth rejects the account creation.\n        throw new functions.https.HttpsError(\n            \"invalid-argument\",\n            \"Unauthorized email\",\n        );\n      }\n    // [END v1domainHttpsError]\n    });\n// [END v1ValidateNewUser]\n\n// [START v1CheckForBan]\n// [START v1beforeSignInFunctionTrigger]\n// Block account sign in with any banned account.\nexports.checkForBan = functions.auth\n    .user()\n    .beforeSignIn(async (user, context) => {\n    // [END v1beforeSignInFunctionTrigger]\n    // [START v1readEmailData]\n    // Email passed from the User object.\n      const email = user.email || \"\";\n      // [END v1readEmailData]\n\n      // [START v1documentGet]\n      // Obtain a document in Firestore of the banned email address.\n      const doc = await db.collection(\"banned\").doc(email).get();\n      // [END v1documentGet]\n\n      // [START v1bannedHttpsError]\n      // Checking that the document exists for the email address.\n      if (doc.exists) {\n      // Throwing an HttpsError so that Auth rejects the account sign in.\n        throw new functions.https.HttpsError(\n            \"invalid-argument\",\n            \"Unauthorized email\",\n        );\n      }\n    // [END v1bannedHttpsError]\n    });\n// [START v1CheckForBan]\n"
  },
  {
    "path": "Node-1st-gen/quickstarts/auth-blocking-functions/functions/package.json",
    "content": "{\n  \"name\": \"functions\",\n  \"description\": \"Cloud Functions for Firebase\",\n  \"scripts\": {\n    \"lint\": \"eslint .\",\n    \"serve\": \"firebase emulators:start --only functions\",\n    \"shell\": \"firebase functions:shell\",\n    \"start\": \"npm run shell\",\n    \"deploy\": \"firebase deploy --only functions\",\n    \"logs\": \"firebase functions:log\"\n  },\n  \"engines\": {\n    \"node\": \"20\"\n  },\n  \"main\": \"index.js\",\n  \"dependencies\": {\n    \"eslint-plugin-promise\": \"^7.2.1\",\n    \"firebase-admin\": \"^13.0.2\",\n    \"firebase-functions\": \"7.0.0\"\n  },\n  \"devDependencies\": {\n    \"eslint\": \"^8.57.1\",\n    \"eslint-config-google\": \"^0.14.0\",\n    \"firebase-functions-test\": \"^3.4.0\"\n  },\n  \"private\": true\n}\n"
  },
  {
    "path": "Node-1st-gen/quickstarts/big-ben/README.md",
    "content": "# Firebase SDK for Cloud Functions Quickstart - HTTPS function with Firebase Hosting URL trigger\n\nThis quickstart demonstrates using the **Firebase SDK for Cloud Functions** with an HTTPS trigger that's triggered through a Firebase Hosting URL. The function will display a repeated number of \"BONG\" depending on the hour of the day.\n\n\n## Introduction\n\nThe function `bigben` returns an HTML page that display a repeated number of \"BONG\" depending on the hour of the day.\n\nFurther reading:\n\n - [Read more about the Firebase SDK for Cloud Functions](https://firebase.google.com/docs/functions)\n\n\n## Initial setup, build tools and dependencies\n\n### 1. Clone this repo\n\nClone or download this repo and open the `quickstarts/big-ben` directory.\n\n\n### 2. Create a Firebase project and configure the quickstart\n\nCreate a Firebase Project on the [Firebase Console](https://console.firebase.google.com).\n\nSet up your Firebase project by running `firebase use --add`, select your Project ID and follow the instructions.\n\n\n### 3. Install the Firebase CLI\n\nYou need to have installed the Firebase CLI, and it always helps to be on the latest version. Run:\n\n```bash\nnpm install -g firebase-tools\n```\n\n> Doesn't work? You may need to [change npm permissions](https://docs.npmjs.com/getting-started/fixing-npm-permissions).\n\n## Try the sample locally\n\nFirst you need to install the `npm` dependencies of the functions:\n\n```bash\ncd functions && npm install; cd ..\n```\n\nStart serving your project locally using `firebase serve`\n\nOpen the app in a browser at [https://localhost:5000/](https://localhost:5000/).\n\nA page containing a repeated number of \"BONG\" - One for each hour of the day - will be displayed.\n\nYou can click on the **Refresh** button which will call the API by doing an XHR to `/api` and refresh the \"BONG\" display on the page.\n\n\n## Deploy the app to prod\n\nFirst you need to install the `npm` dependencies of the functions:\n\n```bash\ncd functions && npm install; cd ..\n```\n\nThis installs locally the Firebase SDK and the Firebase Functions SDK.\n\nDeploy to Firebase using the following command:\n\n```bash\nfirebase deploy\n```\n\nThis deploys and activates the `bigben` Function.\n\n> The first time you call `firebase deploy` on a new project with Functions will take longer than usual.\n\n\n## Try the sample on prod\n\nAfter deploying the function you can open the following URL in your browser:\n\n```\nhttps://<your-project-id>.firebaseapp.com/bigben\n```\n\nA page containing a repeated number of \"BONG\" will be displayed.\n\n\n## Contributing\n\nWe'd love that you contribute to the project. Before doing so please read our [Contributor guide](../../CONTRIBUTING.md).\n\n\n## License\n\n© Google, 2016. Licensed under an [Apache-2](../../LICENSE) license.\n"
  },
  {
    "path": "Node-1st-gen/quickstarts/big-ben/firebase.json",
    "content": "// [START rewriterules]\n{\n  \"hosting\": {\n    \"public\": \"public\",\n\n    // Add the following rewrites section *within* \"hosting\".\n    \"rewrites\": [ {\n      \"source\": \"**\", \"function\": \"app\"\n    } ]\n  }\n}\n// [END rewriterules]\n"
  },
  {
    "path": "Node-1st-gen/quickstarts/big-ben/functions/.eslintrc.json",
    "content": "{\n  \"parserOptions\": {\n    // Required for certain syntax usages\n    \"ecmaVersion\": 2022\n  },\n  \"plugins\": [\n    \"promise\"\n  ],\n  \"extends\": \"eslint:recommended\",\n  \"rules\": {\n    // Removed rule \"disallow the use of console\" from recommended eslint rules\n    \"no-console\": \"off\",\n\n    // Removed rule \"disallow multiple spaces in regular expressions\" from recommended eslint rules\n    \"no-regex-spaces\": \"off\",\n\n    // Removed rule \"disallow the use of debugger\" from recommended eslint rules\n    \"no-debugger\": \"off\",\n\n    // Removed rule \"disallow unused variables\" from recommended eslint rules\n    \"no-unused-vars\": \"off\",\n\n    // Removed rule \"disallow mixed spaces and tabs for indentation\" from recommended eslint rules\n    \"no-mixed-spaces-and-tabs\": \"off\",\n\n    // Removed rule \"disallow the use of undeclared variables unless mentioned in /*global */ comments\" from recommended eslint rules\n    \"no-undef\": \"off\",\n\n    // Warn against template literal placeholder syntax in regular strings\n    \"no-template-curly-in-string\": 1,\n\n    // Warn if return statements do not either always or never specify values\n    \"consistent-return\": 1,\n\n    // Warn if no return statements in callbacks of array methods\n    \"array-callback-return\": 1,\n\n    // Requre the use of === and !==\n    \"eqeqeq\": 2,\n\n    // Disallow the use of alert, confirm, and prompt\n    \"no-alert\": 2,\n\n    // Disallow the use of arguments.caller or arguments.callee\n    \"no-caller\": 2,\n\n    // Disallow null comparisons without type-checking operators\n    \"no-eq-null\": 2,\n\n    // Disallow the use of eval()\n    \"no-eval\": 2,\n\n    // Warn against extending native types\n    \"no-extend-native\": 1,\n\n    // Warn against unnecessary calls to .bind()\n    \"no-extra-bind\": 1,\n\n    // Warn against unnecessary labels    \n    \"no-extra-label\": 1,\n\n    // Disallow leading or trailing decimal points in numeric literals\n    \"no-floating-decimal\": 2,\n\n    // Warn against shorthand type conversions\n    \"no-implicit-coercion\": 1,\n\n    // Warn against function declarations and expressions inside loop statements\n    \"no-loop-func\": 1,\n\n    // Disallow new operators with the Function object\n    \"no-new-func\": 2,\n\n    // Warn against new operators with the String, Number, and Boolean objects\n    \"no-new-wrappers\": 1,\n\n    // Disallow throwing literals as exceptions\n    \"no-throw-literal\": 2,\n\n    // Require using Error objects as Promise rejection reasons\n    \"prefer-promise-reject-errors\": 2,\n\n    // Enforce “for” loop update clause moving the counter in the right direction\n    \"for-direction\": 2,\n\n    // Enforce return statements in getters\n    \"getter-return\": 2,\n\n    // Disallow await inside of loops\n    \"no-await-in-loop\": 2,\n\n    // Disallow comparing against -0\n    \"no-compare-neg-zero\": 2,\n\n    // Warn against catch clause parameters from shadowing variables in the outer scope\n    \"no-catch-shadow\": 1,\n\n    // Disallow identifiers from shadowing restricted names\n    \"no-shadow-restricted-names\": 2,\n\n    // Enforce return statements in callbacks of array methods\n    \"callback-return\": 2,\n\n    // Require error handling in callbacks\n    \"handle-callback-err\": 2,\n\n    // Warn against string concatenation with __dirname and __filename\n    \"no-path-concat\": 1,\n\n    // Prefer using arrow functions for callbacks\n    \"prefer-arrow-callback\": 1,\n\n    // Return inside each then() to create readable and reusable Promise chains.\n    \"promise/always-return\": 2,\n\n    //Enforces the use of catch() on un-returned promises\n    \"promise/catch-or-return\": 2,\n\n    // Warn against nested then() or catch() statements\n    \"promise/no-nesting\": 1\n  }\n}\n"
  },
  {
    "path": "Node-1st-gen/quickstarts/big-ben/functions/index.js",
    "content": "/**\n * Copyright 2017 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use strict';\n\n// [START import]\nconst functions = require('firebase-functions/v1');\nconst express = require('express');\nconst app = express();\n// [END import]\n\n// [START middleware]\nconst cors = require('cors')({origin: true});\napp.use(cors);\n// [END middleware]\n\n// [START index]\n// This endpoint provides displays the index page.\napp.get('/', (req, res) => {\n  const date = new Date();\n  const hours = (date.getHours() % 12) + 1; // London is UTC + 1hr;\n  // [START_EXCLUDE silent]\n  res.set('Cache-Control', `public, max-age=${secondsLeftBeforeEndOfHour(date)}`);\n  // [END_EXCLUDE silent]\n  res.send(`\n  <!doctype html>\n    <head>\n      <title>Time</title>\n      <link rel=\"stylesheet\" href=\"/style.css\">\n      <script src=\"/script.js\"></script>\n    </head>\n    <body>\n      <p>In London, the clock strikes: <span id=\"bongs\">${'BONG '.repeat(hours)}</span></p>\n      <button onClick=\"refresh(this)\">Refresh</button>\n    </body>\n  </html>`);\n});\n// [END index]\n\n// [START api]\n// This endpoint is the BONG API. It returns the bongs as an API.\napp.get('/api', (req, res) => {\n  const date = new Date();\n  const hours = (date.getHours() % 12) + 1; // London is UTC + 1hr;\n  // [START_EXCLUDE silent]\n  // [START cache]\n  res.set('Cache-Control', `public, max-age=${secondsLeftBeforeEndOfHour(date)}`);\n  // [END cache]\n  // [END_EXCLUDE silent]\n  res.json({bongs: 'BONG '.repeat(hours)});\n});\n// [END api]\n\n// [START seconds_left]\n// Returns the number of seconds left before the next hour starts.\nfunction secondsLeftBeforeEndOfHour(date) {\n  const m = date.getMinutes();\n  const s = date.getSeconds();\n  return 3600 - (m*60) - s;\n}\n// [END seconds_left]\n\n// [START export]\n// Export the express app as an HTTP Cloud Function\nexports.app = functions.https.onRequest(app);\n// [END export]\n"
  },
  {
    "path": "Node-1st-gen/quickstarts/big-ben/functions/package.json",
    "content": "{\n  \"name\": \"big-ben-functions\",\n  \"description\": \"A simple endpoint that returns a number of 'BONG' based on the time of day\",\n  \"dependencies\": {\n    \"cors\": \"^2.8.5\",\n    \"eslint-plugin-promise\": \"^7.2.1\",\n    \"express\": \"^4.18.2\",\n    \"firebase-admin\": \"^13.0.2\",\n    \"firebase-functions\": \"7.0.0\"\n  },\n  \"devDependencies\": {\n    \"eslint\": \"^8.57.1\"\n  },\n  \"scripts\": {\n    \"lint\": \"./node_modules/.bin/eslint --max-warnings=0 .\",\n    \"serve\": \"firebase emulators:start --only functions\",\n    \"shell\": \"firebase functions:shell\",\n    \"start\": \"npm run shell\",\n    \"deploy\": \"firebase deploy --only functions\",\n    \"logs\": \"firebase functions:log\",\n    \"compile\": \"cp ../../../../tsconfig.template.json ./tsconfig-compile.json && tsc --project tsconfig-compile.json\"\n  },\n  \"engines\": {\n    \"node\": \"20\"\n  },\n  \"private\": true\n}\n"
  },
  {
    "path": "Node-1st-gen/quickstarts/big-ben/public/script.js",
    "content": "/**\n * Copyright 2018 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nfunction refresh(_this) {\n  // Disable the button and remove current BONGS text.\n  _this.disabled = true;\n  document.getElementById('bongs').innerHTML = '...';\n\n  // Prepare an ajax request to use the API to get the current BONGS from the API.\n  var request = new XMLHttpRequest();\n  request.onreadystatechange = function() {\n    if (request.readyState === 4) {\n      // Re-enable the button.\n      _this.disabled = false;\n      var bongsContainer = document.getElementById('bongs');\n      if (request.status === 200) {\n        // Replace the BONG text with the response from the API.\n        bongsContainer.innerHTML = JSON.parse(request.responseText).bongs;\n      } else {\n        bongsContainer.innerHTML = 'An error occurred during your request: ' +  request.status + ' ' + request.statusText;\n      }\n    }\n  }\n  request.open('Get', '/api');\n\n  // Start the ajax request.\n  request.send();\n}\n"
  },
  {
    "path": "Node-1st-gen/quickstarts/big-ben/public/style.css",
    "content": "/**\n * Copyright 2018 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n \n body {\n    font-family: sans-serif;\n    text-align: center;\n}\n\n#bongs {\n    display: block;\n    margin-top: 10px;\n    font-weight: bold;\n}\n\nbutton {\n    cursor: pointer;\n}"
  },
  {
    "path": "Node-1st-gen/quickstarts/email-users/README.md",
    "content": "# Firebase SDK for Cloud Functions Quickstart - Auth triggers\n\nThis quickstart demonstrates how to setup an Auth triggered Cloud Function using the **Firebase SDK for Cloud Functions**.\n\n\n## Introduction\n\nWe'll deploy Auth triggered Functions that send a welcome email when a new user signs up and a goodbye email when user accounts are deleted.\n\nFurther reading: [Firebase SDK for Cloud Functions](https://firebase.google.com/docs/functions/)\n\n\n## Functions Code\n\nSee file [functions/index.js](functions/index.js) for the Functions trigger and the email sending code.\n\nSending emails is performed using [nodemailer](https://www.npmjs.com/package/nodemailer) a node based Email client with comprehensive EMail server setup. For simplicity, in this sample we're showing how to send email through SMTP using a Gmail account. Be aware that Gmail has an [email sending quota](https://support.google.com/mail/answer/22839). If you are planning on sending a large number of emails you should use a professional email sending platform such as [Sendgrid](https://console.cloud.google.com/launcher/details/sendgrid-app/sendgrid-email), [Mailjet](https://www.mailjet.com/google) or [Mailgun](http://www.mailgun.com/google).\n\n>If switching to Sendgrid, Mailjet or Mailgun make sure you enable billing on your Firebase project as this is required to send requests to non-Google services.\n\nThe dependencies are listed in [functions/package.json](functions/package.json).\n\nThis sample comes with a simple web-based UI which code is in [public](public) directory that lets you easily sign-in Firebase and delete your account for purposes of testing the Functions.\n\n\n## Setting up the sample\n\n 1. Create a Firebase Project using the [Firebase Console](https://console.firebase.google.com).\n 1. Enable the **Google** Provider in the **Auth** section.\n 1. Clone or download this repo and open the `quickstarts/email-users` directory.\n 1. You must have the Firebase CLI installed. If you don't have it install it with `npm install -g firebase-tools` and then configure it with `firebase login`.\n 1. Configure the CLI locally by using `firebase use --add` and select your project in the list.\n 1. Install Cloud Functions dependencies locally by running: `cd functions; npm install; cd -`\n 1. To be able to send emails with your Gmail account: enable access to [Less Secure Apps](https://www.google.com/settings/security/lesssecureapps) and [Display Unlock Captcha](https://accounts.google.com/DisplayUnlockCaptcha). For accounts with 2-step verification enabled [Generate an App Password](https://support.google.com/accounts/answer/185833).\n 1. Set the `gmail.email` and `gmail.password` Google Cloud environment variables to match the email and password of the Gmail account used to send emails (or the app password if your account has 2-step verification enabled). For this use:\n\n    ```bash\n    firebase functions:config:set gmail.email=\"myusername@gmail.com\" gmail.password=\"secretpassword\"\n    ```\n \n\n\n## Deploy and test\n\nThis sample comes with a web-based UI for testing the function. To test it out:\n\n 1. Deploy your project using `firebase deploy`\n 1. Open the app using `firebase open hosting:site`, this will open a browser.\n 1. Sign in the web app in the browser using Google Sign-In and delete your account using the button on the web app. You should receive email confirmations for each actions.\n\n\n## Contributing\n\nWe'd love that you contribute to the project. Before doing so please read our [Contributor guide](../../CONTRIBUTING.md).\n\n\n## License\n\n© Google, 2016. Licensed under an [Apache-2](../../LICENSE) license.\n"
  },
  {
    "path": "Node-1st-gen/quickstarts/email-users/firebase.json",
    "content": "{\n  \"functions\": {\n    \"codebase\": \"email-users\"\n  },\n  \"hosting\": {\n    \"public\": \"./public\"\n  },\n  \"emulators\": {\n    \"auth\": {\n      \"port\": 9099\n    },\n    \"functions\": {\n      \"port\": 5001\n    },\n    \"hosting\": {\n      \"port\": 5000\n    },\n    \"ui\": {\n      \"enabled\": true\n    }\n  }\n}\n"
  },
  {
    "path": "Node-1st-gen/quickstarts/email-users/functions/.eslintrc.json",
    "content": "{\n  \"parserOptions\": {\n    // Required for certain syntax usages\n    \"ecmaVersion\": 2022\n  },\n  \"plugins\": [\n    \"promise\"\n  ],\n  \"extends\": \"eslint:recommended\",\n  \"rules\": {\n    // Removed rule \"disallow the use of console\" from recommended eslint rules\n    \"no-console\": \"off\",\n\n    // Removed rule \"disallow multiple spaces in regular expressions\" from recommended eslint rules\n    \"no-regex-spaces\": \"off\",\n\n    // Removed rule \"disallow the use of debugger\" from recommended eslint rules\n    \"no-debugger\": \"off\",\n\n    // Removed rule \"disallow unused variables\" from recommended eslint rules\n    \"no-unused-vars\": \"off\",\n\n    // Removed rule \"disallow mixed spaces and tabs for indentation\" from recommended eslint rules\n    \"no-mixed-spaces-and-tabs\": \"off\",\n\n    // Removed rule \"disallow the use of undeclared variables unless mentioned in /*global */ comments\" from recommended eslint rules\n    \"no-undef\": \"off\",\n\n    // Warn against template literal placeholder syntax in regular strings\n    \"no-template-curly-in-string\": 1,\n\n    // Warn if return statements do not either always or never specify values\n    \"consistent-return\": 1,\n\n    // Warn if no return statements in callbacks of array methods\n    \"array-callback-return\": 1,\n\n    // Requre the use of === and !==\n    \"eqeqeq\": 2,\n\n    // Disallow the use of alert, confirm, and prompt\n    \"no-alert\": 2,\n\n    // Disallow the use of arguments.caller or arguments.callee\n    \"no-caller\": 2,\n\n    // Disallow null comparisons without type-checking operators\n    \"no-eq-null\": 2,\n\n    // Disallow the use of eval()\n    \"no-eval\": 2,\n\n    // Warn against extending native types\n    \"no-extend-native\": 1,\n\n    // Warn against unnecessary calls to .bind()\n    \"no-extra-bind\": 1,\n\n    // Warn against unnecessary labels    \n    \"no-extra-label\": 1,\n\n    // Disallow leading or trailing decimal points in numeric literals\n    \"no-floating-decimal\": 2,\n\n    // Warn against shorthand type conversions\n    \"no-implicit-coercion\": 1,\n\n    // Warn against function declarations and expressions inside loop statements\n    \"no-loop-func\": 1,\n\n    // Disallow new operators with the Function object\n    \"no-new-func\": 2,\n\n    // Warn against new operators with the String, Number, and Boolean objects\n    \"no-new-wrappers\": 1,\n\n    // Disallow throwing literals as exceptions\n    \"no-throw-literal\": 2,\n\n    // Require using Error objects as Promise rejection reasons\n    \"prefer-promise-reject-errors\": 2,\n\n    // Enforce “for” loop update clause moving the counter in the right direction\n    \"for-direction\": 2,\n\n    // Enforce return statements in getters\n    \"getter-return\": 2,\n\n    // Disallow await inside of loops\n    \"no-await-in-loop\": 2,\n\n    // Disallow comparing against -0\n    \"no-compare-neg-zero\": 2,\n\n    // Warn against catch clause parameters from shadowing variables in the outer scope\n    \"no-catch-shadow\": 1,\n\n    // Disallow identifiers from shadowing restricted names\n    \"no-shadow-restricted-names\": 2,\n\n    // Enforce return statements in callbacks of array methods\n    \"callback-return\": 2,\n\n    // Require error handling in callbacks\n    \"handle-callback-err\": 2,\n\n    // Warn against string concatenation with __dirname and __filename\n    \"no-path-concat\": 1,\n\n    // Prefer using arrow functions for callbacks\n    \"prefer-arrow-callback\": 1,\n\n    // Return inside each then() to create readable and reusable Promise chains.\n    \"promise/always-return\": 2,\n\n    //Enforces the use of catch() on un-returned promises\n    \"promise/catch-or-return\": 2,\n\n    // Warn against nested then() or catch() statements\n    \"promise/no-nesting\": 1\n  }\n}\n"
  },
  {
    "path": "Node-1st-gen/quickstarts/email-users/functions/index.js",
    "content": "/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use strict';\n\nconst functions = require('firebase-functions/v1');\nconst {onInit} = require('firebase-functions/v1/init');\nconst {defineString, defineSecret} = require('firebase-functions/params');\nconst nodemailer = require('nodemailer');\n// Configure the email transport using the default SMTP transport and a GMail account.\n// For Gmail, enable these:\n// 1. https://www.google.com/settings/security/lesssecureapps\n// 2. https://accounts.google.com/DisplayUnlockCaptcha\n// For other types of transports such as Sendgrid see https://nodemailer.com/transports/\n// TODO: Configure the `gmail.email` and `gmail.password` Google Cloud environment variables.\nconst gmailEmail = defineString('GMAIL_EMAIL');\nconst gmailPassword = defineSecret('GMAIL_PASSWORD');\n\nlet mailTransport;\nonInit(() => {\n  mailTransport = nodemailer.createTransport({\n    service: 'gmail',\n    auth: {\n      user: gmailEmail.value(),\n      pass: gmailPassword.value(),\n    },\n  });\n});\n\n// Your company name to include in the emails\n// TODO: Change this to your app or company name to customize the email sent.\nconst APP_NAME = 'Cloud Storage for Firebase quickstart';\n\n// [START sendWelcomeEmail]\n/**\n * Sends a welcome email to new user.\n */\n// [START onCreateTrigger]\nexports.sendWelcomeEmail = functions.runWith({secrets: [gmailPassword]}).auth.user().onCreate((user) => {\n// [END onCreateTrigger]\n  // [START eventAttributes]\n  const email = user.email; // The email of the user.\n  const displayName = user.displayName; // The display name of the user.\n  // [END eventAttributes]\n\n  return sendWelcomeEmail(email, displayName);\n});\n// [END sendWelcomeEmail]\n\n// [START sendByeEmail]\n/**\n * Send an account deleted email confirmation to users who delete their accounts.\n */\n// [START onDeleteTrigger]\nexports.sendByeEmail = functions.runWith({secrets: [gmailPassword]}).auth.user().onDelete((user) => {\n// [END onDeleteTrigger]\n  const email = user.email;\n  const displayName = user.displayName;\n\n  return sendGoodbyeEmail(email, displayName);\n});\n// [END sendByeEmail]\n\n// Sends a welcome email to the given user.\nasync function sendWelcomeEmail(email, displayName) {\n  const mailOptions = {\n    from: `${APP_NAME} <noreply@firebase.com>`,\n    to: email,\n  };\n\n  // The user subscribed to the newsletter.\n  mailOptions.subject = `Welcome to ${APP_NAME}!`;\n  mailOptions.text = `Hey ${displayName || ''}! Welcome to ${APP_NAME}. I hope you will enjoy our service.`;\n  await mailTransport.sendMail(mailOptions);\n  functions.logger.log('New welcome email sent to:', email);\n  return null;\n}\n\n// Sends a goodbye email to the given user.\nasync function sendGoodbyeEmail(email, displayName) {\n  const mailOptions = {\n    from: `${APP_NAME} <noreply@firebase.com>`,\n    to: email,\n  };\n\n  // The user unsubscribed to the newsletter.\n  mailOptions.subject = `Bye!`;\n  mailOptions.text = `Hey ${displayName || ''}!, We confirm that we have deleted your ${APP_NAME} account.`;\n  await mailTransport.sendMail(mailOptions);\n  functions.logger.log('Account deletion confirmation email sent to:', email);\n  return null;\n}\n"
  },
  {
    "path": "Node-1st-gen/quickstarts/email-users/functions/package.json",
    "content": "{\n  \"name\": \"new-user-email-functions\",\n  \"description\": \"Send an email to new users and users who delete their account\",\n  \"dependencies\": {\n    \"eslint-plugin-promise\": \"^7.2.1\",\n    \"firebase-admin\": \"^13.0.2\",\n    \"firebase-functions\": \"7.0.0\",\n    \"nodemailer\": \"^6.8.0\"\n  },\n  \"devDependencies\": {\n    \"eslint\": \"^8.57.1\"\n  },\n  \"scripts\": {\n    \"lint\": \"./node_modules/.bin/eslint --max-warnings=0 .\",\n    \"serve\": \"firebase emulators:start --only functions\",\n    \"shell\": \"firebase functions:shell\",\n    \"start\": \"npm run shell\",\n    \"deploy\": \"firebase deploy --only functions\",\n    \"logs\": \"firebase functions:log\",\n    \"compile\": \"cp ../../../../tsconfig.template.json ./tsconfig-compile.json && tsc --project tsconfig-compile.json\"\n  },\n  \"engines\": {\n    \"node\": \"20\"\n  },\n  \"private\": true\n}\n"
  },
  {
    "path": "Node-1st-gen/quickstarts/email-users/public/index.html",
    "content": "<!doctype html>\n<!--\n  Copyright 2016 Google Inc. All rights reserved.\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n      https://www.apache.org/licenses/LICENSE-2.0\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License\n-->\n<html lang=\"en\">\n<head>\n  <meta charset=\"utf-8\">\n  <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n  <meta name=\"description\" content=\"Demonstrates how to send a welcome email using Firebase Functions.\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n  <title>Welcome email demo</title>\n\n  <!-- Material Design Lite -->\n  <link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/icon?family=Material+Icons\">\n  <link rel=\"stylesheet\" href=\"https://code.getmdl.io/1.1.3/material.blue_grey-orange.min.css\">\n  <script defer src=\"https://code.getmdl.io/1.1.3/material.min.js\"></script>\n\n  <link rel=\"stylesheet\" href=\"main.css\">\n</head>\n<body>\n<div class=\"demo-layout mdl-layout mdl-js-layout mdl-layout--fixed-header\">\n\n  <!-- Header section containing title -->\n  <header class=\"mdl-layout__header mdl-color-text--white mdl-color--light-blue-700\">\n    <div class=\"mdl-cell mdl-cell--12-col mdl-cell--12-col-tablet mdl-grid\">\n      <div class=\"mdl-layout__header-row mdl-cell mdl-cell--12-col mdl-cell--12-col-tablet mdl-cell--8-col-desktop\">\n        <h3>Welcome email demo</h3>\n      </div>\n    </div>\n  </header>\n  <main class=\"mdl-layout__content mdl-color--grey-100\">\n    <div class=\"mdl-cell--12-col mdl-cell--12-col-tablet mdl-grid\">\n\n      <!-- Card containing the sign-in UI -->\n      <div id=\"demo-signed-out-card\" class=\"mdl-card mdl-shadow--2dp mdl-cell\">\n        <div class=\"mdl-card__supporting-text mdl-color-text--grey-600\">\n          <p>\n            This web application demonstrates how you can send a welcome email for new accounts. After signing-in for the first time you will receive a welcome email.\n            <strong>Now sign in!</strong>\n          </p>\n          <button id=\"demo-sign-in-button\" class=\"mdl-color-text--grey-700 mdl-button--raised mdl-button mdl-js-button mdl-js-ripple-effect\"><i class=\"material-icons\">account_circle</i> Sign in with Google</button>\n        </div>\n      </div>\n\n      <!-- Card containing the signed-in UI -->\n      <div id=\"demo-signed-in-card\" class=\"mdl-card mdl-shadow--2dp mdl-cell\">\n        <div class=\"mdl-card__supporting-text mdl-color-text--grey-600\">\n          <p>\n            Welcome <span id=\"demo-name-container\"></span><br>\n            Your Firebase User ID is: <span id=\"demo-uid-container\"></span><br>\n            Your profile picture: <img id=\"demo-profile-pic\">\n          </p>\n          <button id=\"demo-sign-out-button\" class=\"mdl-color-text--grey-700 mdl-button--raised mdl-button mdl-js-button mdl-js-ripple-effect\">Sign out</button>\n          <button id=\"demo-delete-button\" class=\"mdl-color-text--grey-700 mdl-button--raised mdl-button mdl-js-button mdl-js-ripple-effect\">Delete account</button>\n        </div>\n      </div>\n    </div>\n  </main>\n</div>\n\n<!-- Firebase -->\n<script defer src=\"/__/firebase/10.0.0/firebase-app-compat.js\"></script>\n<script defer src=\"/__/firebase/10.0.0/firebase-auth-compat.js\"></script>\n<script defer src=\"/__/firebase/10.0.0/firebase-database-compat.js\"></script>\n<!-- initialize the SDK after all desired features are loaded -->\n<script defer src=\"/__/firebase/init.js\"></script>\n\n<script src=\"main.js\"></script>\n</body>\n</html>\n"
  },
  {
    "path": "Node-1st-gen/quickstarts/email-users/public/main.css",
    "content": "/**\n * Copyright 2016 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nhtml, body {\n  font-family: 'Roboto', 'Helvetica', sans-serif;\n}\n.mdl-grid {\n  max-width: 1024px;\n  margin: auto;\n}\n.mdl-card {\n  min-height: 0;\n  padding-bottom: 5px;\n}\n.mdl-layout__header-row {\n  padding: 0;\n}\nh3 {\n  background: url('firebase-logo.png') no-repeat;\n  background-size: 40px;\n  padding-left: 50px;\n}\n#demo-signed-out-card,\n#demo-signed-in-card {\n  display: none;\n}\n#demo-profile-pic {\n  height: 60px;\n  width: 60px;\n  border-radius: 30px;\n  margin-left: calc(50% - 30px);\n  margin-top: 10px;\n}\n#demo-name-container,\n#demo-email-container,\n#demo-uid-container {\n  font-weight: bold;\n}\n#demo-delete-button {\n  margin-left: 20px;\n}\n"
  },
  {
    "path": "Node-1st-gen/quickstarts/email-users/public/main.js",
    "content": "/**\n * Copyright 2016 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use strict';\n\n// Initializes the Demo.\nfunction Demo() {\n  document.addEventListener('DOMContentLoaded', function() {\n    // Shortcuts to DOM Elements.\n    this.signInButton = document.getElementById('demo-sign-in-button');\n    this.signOutButton = document.getElementById('demo-sign-out-button');\n    this.nameContainer = document.getElementById('demo-name-container');\n    this.uidContainer = document.getElementById('demo-uid-container');\n    this.deleteButton = document.getElementById('demo-delete-button');\n    this.profilePic = document.getElementById('demo-profile-pic');\n    this.signedOutCard = document.getElementById('demo-signed-out-card');\n    this.signedInCard = document.getElementById('demo-signed-in-card');\n\n    // Bind events.\n    this.signInButton.addEventListener('click', this.signIn.bind(this));\n    this.signOutButton.addEventListener('click', this.signOut.bind(this));\n    this.deleteButton.addEventListener('click', this.deleteAccount.bind(this));\n    firebase.auth().onAuthStateChanged(this.onAuthStateChanged.bind(this));\n  }.bind(this));\n}\n\n// Triggered on Firebase auth state change.\nDemo.prototype.onAuthStateChanged = function(user) {\n  if (user) {\n    this.nameContainer.innerText = user.displayName;\n    this.uidContainer.innerText = user.uid;\n    this.profilePic.src = user.photoURL;\n    this.signedOutCard.style.display = 'none';\n    this.signedInCard.style.display = 'block';\n  } else {\n    this.signedOutCard.style.display = 'block';\n    this.signedInCard.style.display = 'none';\n  }\n};\n\n// Initiates the sign-in flow using LinkedIn sign in in a popup.\nDemo.prototype.signIn = function() {\n  firebase.auth().signInWithPopup(new firebase.auth.GoogleAuthProvider());\n};\n\n// Signs-out of Firebase.\nDemo.prototype.signOut = function() {\n  firebase.auth().signOut();\n};\n\n// Deletes the user's account.\nDemo.prototype.deleteAccount = function() {\n  firebase.auth().currentUser.delete().then(function() {\n    window.alert('Account deleted. Check your email for a confirmation.');\n  }).catch(function(error) {\n    if (error.code === 'auth/requires-recent-login') {\n      window.alert('You need to have recently signed-in to delete your account. Please sign-in and try again.');\n      firebase.auth().signOut();\n    }\n  });\n};\n\n// Load the demo.\nwindow.demo = new Demo();\n"
  },
  {
    "path": "Node-1st-gen/quickstarts/https-time-server/README.md",
    "content": "# Firebase SDK for Cloud Functions Quickstart - HTTPS trigger\n\nThis quickstart demonstrates using the **Firebase SDK for Cloud Functions** with an HTTPS trigger through building an endpoint returning the current time.\n\n\n## Introduction\n\nThe function `date` returns the current server date. You can pass it a `format` URL Query parameter to format the date.\n\nFurther reading:\n\n - [Read more about the Firebase SDK for Cloud Functions](https://firebase.google.com/docs/functions)\n\n\n## Initial setup, build tools and dependencies\n\n### 1. Clone this repo\n\nClone or download this repo and open the `quickstarts/time-server` directory.\n\n\n### 2. Create a Firebase project and configure the quickstart\n\nCreate a Firebase Project on the [Firebase Console](https://console.firebase.google.com).\n\nSet up your Firebase project by running `firebase use --add`, select your Project ID and follow the instructions.\n\n\n### 3. Install the Firebase CLI and enable Functions on your Firebase CLI\n\nYou need to have installed the Firebase CLI. If you haven't run:\n\n```bash\nnpm install -g firebase-tools\n```\n\n> Doesn't work? You may need to [change npm permissions](https://docs.npmjs.com/getting-started/fixing-npm-permissions).\n\n\n## Deploy the app to prod\n\nFirst you need to install the `npm` dependencies of the functions:\n\n```bash\ncd functions && npm install; cd ..\n```\n\nThis installs locally:\n - The Firebase SDK and the Firebase Functions SDK.\n - The [moment](https://www.npmjs.com/package/moment) npm package to format time.\n - The [cors](https://www.npmjs.com/package/cors) npm package to allow Cross Origin AJAX requests on the endpoint.\n\nDeploy to Firebase using the following command:\n\n```bash\nfirebase deploy\n```\n\nThis deploys and activates the date Function.\n\n> The first time you call `firebase deploy` on a new project with Functions will take longer than usual.\n\n\n## Try the sample\n\nAfter deploying the function you can open the following URLs in your browser:\n\n```\nhttps://us-central1-<project-id>.cloudfunctions.net/date\n\nhttps://us-central1-<project-id>.cloudfunctions.net/date?format=MMMM%20Do%20YYYY%2C%20h%3Amm%3Ass%20a\n```\n\nYou can also send the format in the request body. For instance using cURL in the command line:\n\n```bash\ncurl -H 'Content-Type: application/json' /\n     -d '{\"format\": \"MMMM Do YYYY, h:mm:ss a\"}' /\n     https://us-central1-<project-id>.cloudfunctions.net/date\n```\nFormatted dates should be displayed.\n\nWe are responding with a 403 error in case of PUT requests:\n\n```bash\ncurl -X PUT -d '{\"format\": \"MMMM Do YYYY, h:mm:ss a\"}' https://us-central1-<project-id>.cloudfunctions.net/date\n```\n\n\n## Contributing\n\nWe'd love that you contribute to the project. Before doing so please read our [Contributor guide](../../CONTRIBUTING.md).\n\n\n## License\n\n© Google, 2016. Licensed under an [Apache-2](../../LICENSE) license.\n"
  },
  {
    "path": "Node-1st-gen/quickstarts/https-time-server/firebase.json",
    "content": "{\n    \"functions\": {\n        \"codebase\": \"https-time-server\"\n    }\n}"
  },
  {
    "path": "Node-1st-gen/quickstarts/https-time-server/functions/.eslintrc.json",
    "content": "{\n  \"parserOptions\": {\n    // Required for certain syntax usages\n    \"ecmaVersion\": 2022\n  },\n  \"plugins\": [\n    \"promise\"\n  ],\n  \"extends\": \"eslint:recommended\",\n  \"rules\": {\n    // Removed rule \"disallow the use of console\" from recommended eslint rules\n    \"no-console\": \"off\",\n\n    // Removed rule \"disallow multiple spaces in regular expressions\" from recommended eslint rules\n    \"no-regex-spaces\": \"off\",\n\n    // Removed rule \"disallow the use of debugger\" from recommended eslint rules\n    \"no-debugger\": \"off\",\n\n    // Removed rule \"disallow unused variables\" from recommended eslint rules\n    \"no-unused-vars\": \"off\",\n\n    // Removed rule \"disallow mixed spaces and tabs for indentation\" from recommended eslint rules\n    \"no-mixed-spaces-and-tabs\": \"off\",\n\n    // Removed rule \"disallow the use of undeclared variables unless mentioned in /*global */ comments\" from recommended eslint rules\n    \"no-undef\": \"off\",\n\n    // Warn against template literal placeholder syntax in regular strings\n    \"no-template-curly-in-string\": 1,\n\n    // Warn if return statements do not either always or never specify values\n    \"consistent-return\": 1,\n\n    // Warn if no return statements in callbacks of array methods\n    \"array-callback-return\": 1,\n\n    // Requre the use of === and !==\n    \"eqeqeq\": 2,\n\n    // Disallow the use of alert, confirm, and prompt\n    \"no-alert\": 2,\n\n    // Disallow the use of arguments.caller or arguments.callee\n    \"no-caller\": 2,\n\n    // Disallow null comparisons without type-checking operators\n    \"no-eq-null\": 2,\n\n    // Disallow the use of eval()\n    \"no-eval\": 2,\n\n    // Warn against extending native types\n    \"no-extend-native\": 1,\n\n    // Warn against unnecessary calls to .bind()\n    \"no-extra-bind\": 1,\n\n    // Warn against unnecessary labels    \n    \"no-extra-label\": 1,\n\n    // Disallow leading or trailing decimal points in numeric literals\n    \"no-floating-decimal\": 2,\n\n    // Warn against shorthand type conversions\n    \"no-implicit-coercion\": 1,\n\n    // Warn against function declarations and expressions inside loop statements\n    \"no-loop-func\": 1,\n\n    // Disallow new operators with the Function object\n    \"no-new-func\": 2,\n\n    // Warn against new operators with the String, Number, and Boolean objects\n    \"no-new-wrappers\": 1,\n\n    // Disallow throwing literals as exceptions\n    \"no-throw-literal\": 2,\n\n    // Require using Error objects as Promise rejection reasons\n    \"prefer-promise-reject-errors\": 2,\n\n    // Enforce “for” loop update clause moving the counter in the right direction\n    \"for-direction\": 2,\n\n    // Enforce return statements in getters\n    \"getter-return\": 2,\n\n    // Disallow await inside of loops\n    \"no-await-in-loop\": 2,\n\n    // Disallow comparing against -0\n    \"no-compare-neg-zero\": 2,\n\n    // Warn against catch clause parameters from shadowing variables in the outer scope\n    \"no-catch-shadow\": 1,\n\n    // Disallow identifiers from shadowing restricted names\n    \"no-shadow-restricted-names\": 2,\n\n    // Enforce return statements in callbacks of array methods\n    \"callback-return\": 2,\n\n    // Require error handling in callbacks\n    \"handle-callback-err\": 2,\n\n    // Warn against string concatenation with __dirname and __filename\n    \"no-path-concat\": 1,\n\n    // Prefer using arrow functions for callbacks\n    \"prefer-arrow-callback\": 1,\n\n    // Return inside each then() to create readable and reusable Promise chains.\n    \"promise/always-return\": 2,\n\n    //Enforces the use of catch() on un-returned promises\n    \"promise/catch-or-return\": 2,\n\n    // Warn against nested then() or catch() statements\n    \"promise/no-nesting\": 1\n  }\n}\n"
  },
  {
    "path": "Node-1st-gen/quickstarts/https-time-server/functions/index.js",
    "content": "/**\n * Copyright 2016 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use strict';\n\n// [START functionsimport]\nconst functions = require('firebase-functions/v1');\n// [END functionsimport]\n// [START additionalimports]\n// Moments library to format dates.\nconst moment = require('moment');\n// CORS Express middleware to enable CORS Requests.\nconst cors = require('cors')({\n  origin: true,\n});\n// [END additionalimports]\n\n// [START all]\n/**\n * Returns the server's date. You must provide a `format` URL query parameter or `format` value in\n * the request body with which we'll try to format the date.\n *\n * Format must follow the Node moment library. See: http://momentjs.com/\n *\n * Example format: \"MMMM Do YYYY, h:mm:ss a\".\n * Example request using URL query parameters:\n *   https://us-central1-<project-id>.cloudfunctions.net/date?format=MMMM%20Do%20YYYY%2C%20h%3Amm%3Ass%20a\n * Example request using request body with cURL:\n *   curl -H 'Content-Type: application/json' /\n *        -d '{\"format\": \"MMMM Do YYYY, h:mm:ss a\"}' /\n *        https://us-central1-<project-id>.cloudfunctions.net/date\n *\n * This endpoint supports CORS.\n */\n// [START trigger]\nexports.date = functions.https.onRequest((req, res) => {\n  // [END trigger]\n  // [START sendError]\n  // Forbidding PUT requests.\n  if (req.method === 'PUT') {\n    res.status(403).send('Forbidden!');\n    return;\n  }\n  // [END sendError]\n\n  // [START usingMiddleware]\n  // Enable CORS using the `cors` express middleware.\n  cors(req, res, () => {\n    // [END usingMiddleware]\n    // Reading date format from URL query parameter.\n    // [START readQueryParam]\n    let format = req.query.format;\n    // [END readQueryParam]\n    // Reading date format from request body query parameter\n    if (!format) {\n      // [START readBodyParam]\n      format = req.body.format;\n      // [END readBodyParam]\n    }\n    // [START sendResponse]\n    const formattedDate = moment().format(`${format}`);\n    functions.logger.log('Sending Formatted date:', formattedDate);\n    res.status(200).send(formattedDate);\n    // [END sendResponse]\n  });\n});\n// [END all]\n"
  },
  {
    "path": "Node-1st-gen/quickstarts/https-time-server/functions/package.json",
    "content": "{\n  \"name\": \"time-server-functions\",\n  \"description\": \"A simple time server using HTTPS Cloud Function\",\n  \"dependencies\": {\n    \"cors\": \"^2.8.5\",\n    \"eslint-plugin-promise\": \"^7.2.1\",\n    \"firebase-admin\": \"^13.0.2\",\n    \"firebase-functions\": \"7.0.0\",\n    \"moment\": \"^2.29.4\"\n  },\n  \"devDependencies\": {\n    \"eslint\": \"^8.57.1\"\n  },\n  \"scripts\": {\n    \"lint\": \"./node_modules/.bin/eslint --max-warnings=0 .\",\n    \"serve\": \"firebase emulators:start --only functions\",\n    \"shell\": \"firebase functions:shell\",\n    \"start\": \"npm run shell\",\n    \"deploy\": \"firebase deploy --only functions\",\n    \"logs\": \"firebase functions:log\",\n    \"compile\": \"cp ../../../../tsconfig.template.json ./tsconfig-compile.json && tsc --project tsconfig-compile.json\"\n  },\n  \"engines\": {\n    \"node\": \"20\"\n  },\n  \"private\": true\n}\n"
  },
  {
    "path": "Node-1st-gen/quickstarts/multicodebase-hellos/.gitignore",
    "content": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\nfirebase-debug.log*\nfirebase-debug.*.log*\n\n# Firebase cache\n.firebase/\n\n# Firebase config\n\n# Uncomment this if you'd like others to create their own Firebase project.\n# For a team working on the same Firebase project(s), it is recommended to leave\n# it commented so all members can deploy to the same project(s) in .firebaserc.\n# .firebaserc\n\n# Runtime data\npids\n*.pid\n*.seed\n*.pid.lock\n\n# Directory for instrumented libs generated by jscoverage/JSCover\nlib-cov\n\n# Coverage directory used by tools like istanbul\ncoverage\n\n# nyc test coverage\n.nyc_output\n\n# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)\n.grunt\n\n# Bower dependency directory (https://bower.io/)\nbower_components\n\n# node-waf configuration\n.lock-wscript\n\n# Compiled binary addons (http://nodejs.org/api/addons.html)\nbuild/Release\n\n# Dependency directories\nnode_modules/\n\n# Optional npm cache directory\n.npm\n\n# Optional eslint cache\n.eslintcache\n\n# Optional REPL history\n.node_repl_history\n\n# Output of 'npm pack'\n*.tgz\n\n# Yarn Integrity file\n.yarn-integrity\n\n# dotenv environment variables file\n.env\n"
  },
  {
    "path": "Node-1st-gen/quickstarts/multicodebase-hellos/README.md",
    "content": "# Firebase SDK for Cloud Functions Quickstart - Multi-codebases\n\nThis quickstart demonstrates how to set up a multi-codebase repository that allows you to setup multiple source directories to manage your function definitions. \n\n\n## Setup\n\nThis repository contains 2 different function sources:\n\n* `/js`: Contains a function sample written in JavaScript.\n* `/ts`: Contains a function sample written in Typescript.\n\nWe define function configuration for each source directory in `firebase.json`:\n\n```json\n{\n  \"functions\": [\n    {\n      \"source\": \"js\",\n      \"codebase\": \"javascript\"\n    },\n    {\n      \"source\": \"ts\",\n      \"codebase\": \"typescript\"\n    }\n  ]\n}\n```\n\n## Deploying\n\nMake sure you are using the latest Firebase CLI. Multi-codebase support is only available in Firebase CLI v10.7.0 and up.\n\n```bash\nnpm install -g firebase-tools@latest\n```\n\nDeploy the sample functions using the Firebase CLI deploy command:\n\n```bash\nfirebase deploy --only functions\n```\n\nYou can also deploy a specific codebase:\n\n```bash\nfirebase deploy --only functions:javascript\nfirebase deploy --only functions:typescript\n```"
  },
  {
    "path": "Node-1st-gen/quickstarts/multicodebase-hellos/firebase.json",
    "content": "{\n  \"functions\": [\n    {\n      \"source\": \"ts\",\n      \"codebase\": \"multicodebase-hellos\",\n      \"predeploy\": [\n        \"npm --prefix \\\"$RESOURCE_DIR\\\" run lint\",\n        \"npm --prefix \\\"$RESOURCE_DIR\\\" run build\"\n      ]\n    },\n    {\n      \"source\": \"js\",\n      \"codebase\": \"javascript\"\n    }\n  ],\n  \"emulators\": {\n    \"functions\": {\n      \"port\": 5001\n    },\n    \"ui\": {\n      \"enabled\": true\n    }\n  }\n}\n"
  },
  {
    "path": "Node-1st-gen/quickstarts/multicodebase-hellos/js/.eslintrc.js",
    "content": "/**\n * Copyright 2023 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nmodule.exports = {\n  root: true,\n  env: {\n    es2020: true,\n    node: true,\n  },\n  extends: [\n    \"eslint:recommended\",\n    \"google\",\n  ],\n  rules: {\n    quotes: [\"error\", \"double\"],\n  },\n};\n"
  },
  {
    "path": "Node-1st-gen/quickstarts/multicodebase-hellos/js/.gitignore",
    "content": "node_modules/"
  },
  {
    "path": "Node-1st-gen/quickstarts/multicodebase-hellos/js/index.js",
    "content": "/**\n * Copyright 2023 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nconst functions = require(\"firebase-functions/v1\");\n\nexports.helloJS = functions.https.onRequest((request, response) => {\n  functions.logger.info(\"Hello logs!\", {structuredData: true});\n  response.send(\"Hello from JS codebase!\");\n});\n"
  },
  {
    "path": "Node-1st-gen/quickstarts/multicodebase-hellos/js/package.json",
    "content": "{\n  \"name\": \"functions\",\n  \"description\": \"Cloud Functions for Firebase\",\n  \"scripts\": {\n    \"lint\": \"eslint .\",\n    \"serve\": \"firebase emulators:start --only functions\",\n    \"shell\": \"firebase functions:shell\",\n    \"start\": \"npm run shell\",\n    \"deploy\": \"firebase deploy --only functions\",\n    \"logs\": \"firebase functions:log\"\n  },\n  \"engines\": {\n    \"node\": \"20\"\n  },\n  \"main\": \"index.js\",\n  \"dependencies\": {\n    \"eslint-plugin-promise\": \"^7.2.1\",\n    \"firebase-admin\": \"^13.0.2\",\n    \"firebase-functions\": \"7.0.0\"\n  },\n  \"devDependencies\": {\n    \"eslint\": \"^8.57.1\",\n    \"eslint-config-google\": \"^0.14.0\",\n    \"firebase-functions-test\": \"^3.4.0\"\n  },\n  \"private\": true\n}\n"
  },
  {
    "path": "Node-1st-gen/quickstarts/multicodebase-hellos/ts/.eslintrc.js",
    "content": "/**\n * Copyright 2023 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nmodule.exports = {\n  root: true,\n  env: {\n    es2020: true,\n    node: true,\n  },\n  extends: [\n    \"eslint:recommended\",\n    \"plugin:import/errors\",\n    \"plugin:import/warnings\",\n    \"plugin:import/typescript\",\n    \"google\",\n    \"plugin:@typescript-eslint/recommended\",\n  ],\n  parser: \"@typescript-eslint/parser\",\n  parserOptions: {\n    project: [\"tsconfig.json\", \"tsconfig.dev.json\"],\n    sourceType: \"module\",\n  },\n  ignorePatterns: [\n    \"/lib/**/*\", // Ignore built files.\n  ],\n  plugins: [\n    \"@typescript-eslint\",\n    \"import\",\n  ],\n  rules: {\n    \"quotes\": [\"error\", \"double\"],\n    \"import/no-unresolved\": 0,\n  },\n};\n"
  },
  {
    "path": "Node-1st-gen/quickstarts/multicodebase-hellos/ts/.gitignore",
    "content": "# Compiled JavaScript files\nlib/**/*.js\nlib/**/*.js.map\n\n# TypeScript v1 declaration files\ntypings/\n\n# Node.js dependency directory\nnode_modules/\n"
  },
  {
    "path": "Node-1st-gen/quickstarts/multicodebase-hellos/ts/package.json",
    "content": "{\n  \"name\": \"functions\",\n  \"scripts\": {\n    \"lint\": \"eslint --ext .js,.ts .\",\n    \"build\": \"tsc\",\n    \"serve\": \"npm run build && firebase emulators:start --only functions\",\n    \"shell\": \"npm run build && firebase functions:shell\",\n    \"start\": \"npm run shell\",\n    \"deploy\": \"firebase deploy --only functions\",\n    \"logs\": \"firebase functions:log\"\n  },\n  \"engines\": {\n    \"node\": \"20\"\n  },\n  \"main\": \"lib/index.js\",\n  \"dependencies\": {\n    \"eslint-plugin-promise\": \"^7.2.1\",\n    \"firebase-admin\": \"^13.0.2\",\n    \"firebase-functions\": \"7.0.0\"\n  },\n  \"devDependencies\": {\n    \"@typescript-eslint/eslint-plugin\": \"^5.58.0\",\n    \"@typescript-eslint/parser\": \"^5.58.0\",\n    \"eslint\": \"^8.57.1\",\n    \"eslint-config-google\": \"^0.14.0\",\n    \"eslint-plugin-import\": \"^2.25.4\",\n    \"firebase-functions-test\": \"^3.4.0\",\n    \"typescript\": \"^5.0.4\"\n  },\n  \"private\": true\n}\n"
  },
  {
    "path": "Node-1st-gen/quickstarts/multicodebase-hellos/ts/src/index.ts",
    "content": "/**\n * Copyright 2023 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport * as functions from \"firebase-functions\";\n\nexport const helloTS = functions.https.onRequest((request, response) => {\n  functions.logger.info(\"Hello logs!\", {structuredData: true});\n  response.send(\"Hello from TS codebase!\");\n});\n"
  },
  {
    "path": "Node-1st-gen/quickstarts/multicodebase-hellos/ts/tsconfig.dev.json",
    "content": "{\n  \"include\": [\n    \".eslintrc.js\"\n  ]\n}\n"
  },
  {
    "path": "Node-1st-gen/quickstarts/multicodebase-hellos/ts/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"module\": \"commonjs\",\n    \"noImplicitReturns\": true,\n    \"noUnusedLocals\": true,\n    \"outDir\": \"lib\",\n    \"sourceMap\": true,\n    \"strict\": true,\n    \"target\": \"es2017\"\n  },\n  \"compileOnSave\": true,\n  \"include\": [\n    \"src\"\n  ]\n}\n"
  },
  {
    "path": "Node-1st-gen/quickstarts/pubsub-helloworld/README.md",
    "content": "# Firebase SDK for Cloud Functions Quickstart - PubSub trigger\n\nThis quickstart demonstrates how to setup a PubSub triggered Cloud Function using the **Firebase SDK for Cloud Functions**.\n\n\n## Introduction\n\nWe'll deploy a PubSub triggered Functions that prints out a Hello World message to the Cloud Logs.\n\nFurther reading:\n\n - [Read more about the Firebase SDK for Cloud Functions](https://firebase.google.com/docs/functions/)\n\n\n## Initial setup, build tools and dependencies\n\n### 1. Clone this repo\n\nClone or download this repo and open the `quickstarts/pubsub-helloworld` directory.\n\n\n### 2. Create a Firebase project and configure the quickstart\n\nCreate a Firebase Project on the [Firebase Console](https://console.firebase.google.com).\n\nSet up your Firebase project by running `firebase use --add`, select your Project ID and follow the instructions.\n\n\n### 3. Install the Firebase CLI and enable Functions on your Firebase CLI\n\nYou need to have installed the Firebase CLI. If you haven't run:\n\n```bash\nnpm install -g firebase-tools\n```\n\n> Doesn't work? You may need to [change npm permissions](https://docs.npmjs.com/getting-started/fixing-npm-permissions).\n\n\n## Deploy the app to prod\n\nFirst you need to install the `npm` dependencies of the functions:\n\n```bash\ncd functions && npm install; cd ..\n```\n\nThis installs locally the Firebase Admin SDK and the Firebase SDK for Cloud Functions.\n\nDeploy to Firebase using the following command:\n\n```bash\nfirebase deploy\n```\n\nThis deploys and activate the PubSub hello World Functions.\n\n> The first time you call `firebase deploy` on a new project with Functions will take longer than usual.\n\n\n## Try the sample\n\nOnce deployed, to try the sample use the `gcloud` CLI to publish a message to the `topic-name` topic:\n\n```\ngcloud alpha pubsub topics publish topic-name --message='YourName'\n```\n\nOpen the Functions logs in the Firebase Console, you should see a messages that reads \"Hello YourName\".\n\nThen you can also publish a message to the `another-topic-name` topic using JSON data:\n\n```\ngcloud alpha pubsub topics publish another-topic-name --message='{\"name\":\"YourName\"}'\n```\n\nOpen the Functions logs in the Firebase Console, you should see a messages that reads \"Hello YourName\".\n\nLast you can also publish a message to the `yet-another-topic-name` topic using JSON data:\n\n```\ngcloud alpha pubsub topics publish yet-another-topic-name --attribute name=YourName\n```\n\nOpen the Functions logs in the Firebase Console, you should see a messages that reads \"Hello YourName\".\n\n\n## Contributing\n\nWe'd love that you contribute to the project. Before doing so please read our [Contributor guide](../../CONTRIBUTING.md).\n\n\n## License\n\n© Google, 2016. Licensed under an [Apache-2](../../LICENSE) license.\n"
  },
  {
    "path": "Node-1st-gen/quickstarts/pubsub-helloworld/firebase.json",
    "content": "{\n  \"functions\": {\n    \"codebase\": \"pubsub-helloworld\"\n  },\n  \"emulators\": {\n    \"functions\": {\n      \"port\": 5001\n    },\n    \"pubsub\": {\n      \"port\": 8085\n    },\n    \"ui\": {\n      \"enabled\": true\n    }\n  }\n}\n"
  },
  {
    "path": "Node-1st-gen/quickstarts/pubsub-helloworld/functions/.eslintrc.json",
    "content": "{\n  \"parserOptions\": {\n    // Required for certain syntax usages\n    \"ecmaVersion\": 2022\n  },\n  \"plugins\": [\n    \"promise\"\n  ],\n  \"extends\": \"eslint:recommended\",\n  \"rules\": {\n    // Removed rule \"disallow the use of console\" from recommended eslint rules\n    \"no-console\": \"off\",\n\n    // Removed rule \"disallow multiple spaces in regular expressions\" from recommended eslint rules\n    \"no-regex-spaces\": \"off\",\n\n    // Removed rule \"disallow the use of debugger\" from recommended eslint rules\n    \"no-debugger\": \"off\",\n\n    // Removed rule \"disallow unused variables\" from recommended eslint rules\n    \"no-unused-vars\": \"off\",\n\n    // Removed rule \"disallow mixed spaces and tabs for indentation\" from recommended eslint rules\n    \"no-mixed-spaces-and-tabs\": \"off\",\n\n    // Removed rule \"disallow the use of undeclared variables unless mentioned in /*global */ comments\" from recommended eslint rules\n    \"no-undef\": \"off\",\n\n    // Warn against template literal placeholder syntax in regular strings\n    \"no-template-curly-in-string\": 1,\n\n    // Warn if return statements do not either always or never specify values\n    \"consistent-return\": 1,\n\n    // Warn if no return statements in callbacks of array methods\n    \"array-callback-return\": 1,\n\n    // Requre the use of === and !==\n    \"eqeqeq\": 2,\n\n    // Disallow the use of alert, confirm, and prompt\n    \"no-alert\": 2,\n\n    // Disallow the use of arguments.caller or arguments.callee\n    \"no-caller\": 2,\n\n    // Disallow null comparisons without type-checking operators\n    \"no-eq-null\": 2,\n\n    // Disallow the use of eval()\n    \"no-eval\": 2,\n\n    // Warn against extending native types\n    \"no-extend-native\": 1,\n\n    // Warn against unnecessary calls to .bind()\n    \"no-extra-bind\": 1,\n\n    // Warn against unnecessary labels    \n    \"no-extra-label\": 1,\n\n    // Disallow leading or trailing decimal points in numeric literals\n    \"no-floating-decimal\": 2,\n\n    // Warn against shorthand type conversions\n    \"no-implicit-coercion\": 1,\n\n    // Warn against function declarations and expressions inside loop statements\n    \"no-loop-func\": 1,\n\n    // Disallow new operators with the Function object\n    \"no-new-func\": 2,\n\n    // Warn against new operators with the String, Number, and Boolean objects\n    \"no-new-wrappers\": 1,\n\n    // Disallow throwing literals as exceptions\n    \"no-throw-literal\": 2,\n\n    // Require using Error objects as Promise rejection reasons\n    \"prefer-promise-reject-errors\": 2,\n\n    // Enforce “for” loop update clause moving the counter in the right direction\n    \"for-direction\": 2,\n\n    // Enforce return statements in getters\n    \"getter-return\": 2,\n\n    // Disallow await inside of loops\n    \"no-await-in-loop\": 2,\n\n    // Disallow comparing against -0\n    \"no-compare-neg-zero\": 2,\n\n    // Warn against catch clause parameters from shadowing variables in the outer scope\n    \"no-catch-shadow\": 1,\n\n    // Disallow identifiers from shadowing restricted names\n    \"no-shadow-restricted-names\": 2,\n\n    // Enforce return statements in callbacks of array methods\n    \"callback-return\": 2,\n\n    // Require error handling in callbacks\n    \"handle-callback-err\": 2,\n\n    // Warn against string concatenation with __dirname and __filename\n    \"no-path-concat\": 1,\n\n    // Prefer using arrow functions for callbacks\n    \"prefer-arrow-callback\": 1,\n\n    // Return inside each then() to create readable and reusable Promise chains.\n    \"promise/always-return\": 2,\n\n    //Enforces the use of catch() on un-returned promises\n    \"promise/catch-or-return\": 2,\n\n    // Warn against nested then() or catch() statements\n    \"promise/no-nesting\": 1\n  }\n}\n"
  },
  {
    "path": "Node-1st-gen/quickstarts/pubsub-helloworld/functions/index.js",
    "content": "/**\n * Copyright 2016 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use strict';\n\n// [START import]\nconst functions = require('firebase-functions/v1');\n// [END import]\n\n// [START helloWorld]\n/**\n * Cloud Function to be triggered by Pub/Sub that logs a message using the data published to the\n * topic.\n */\n// [START trigger]\nexports.helloPubSub = functions.pubsub.topic('topic-name').onPublish((message) => {\n// [END trigger]\n  // [START readBase64]\n  // Decode the PubSub Message body.\n  const messageBody = message.data ? Buffer.from(message.data, 'base64').toString() : null;\n  // [END readBase64]\n  // Print the message in the logs.\n  functions.logger.log(`Hello ${messageBody || 'World'}!`);\n  return null;\n});\n// [END helloWorld]\n\n/**\n * Cloud Function to be triggered by Pub/Sub that logs a message using the data published to the\n * topic as JSON.\n */\nexports.helloPubSubJson = functions.pubsub.topic('another-topic-name').onPublish((message) => {\n  // [START readJson]\n  // Get the `name` attribute of the PubSub message JSON body.\n  let name = null;\n  try {\n    name = message.json.name;\n  } catch (e) {\n    functions.logger.error('PubSub message was not JSON', e);\n  }\n  // [END readJson]\n  // Print the message in the logs.\n  functions.logger.log(`Hello ${name || 'World'}!`);\n  return null;\n});\n\n/**\n * Cloud Function to be triggered by Pub/Sub that logs a message using the data attributes\n * published to the topic.\n */\nexports.helloPubSubAttributes = functions.pubsub.topic('yet-another-topic-name').onPublish((message) => {\n  // [START readAttributes]\n  // Get the `name` attribute of the message.\n  const name = message.attributes.name;\n  // [END readAttributes]\n  // Print the message in the logs.\n  functions.logger.log(`Hello ${name || 'World'}!`);\n  return null;\n});\n"
  },
  {
    "path": "Node-1st-gen/quickstarts/pubsub-helloworld/functions/package.json",
    "content": "{\n  \"name\": \"pubsub-hello-functions\",\n  \"description\": \"Logs the data published to PubSub topics\",\n  \"dependencies\": {\n    \"eslint-plugin-promise\": \"^7.2.1\",\n    \"firebase-admin\": \"^13.0.2\",\n    \"firebase-functions\": \"7.0.0\"\n  },\n  \"devDependencies\": {\n    \"eslint\": \"^8.57.1\"\n  },\n  \"scripts\": {\n    \"lint\": \"./node_modules/.bin/eslint --max-warnings=0 .\",\n    \"serve\": \"firebase emulators:start --only functions\",\n    \"shell\": \"firebase functions:shell\",\n    \"start\": \"npm run shell\",\n    \"deploy\": \"firebase deploy --only functions\",\n    \"logs\": \"firebase functions:log\",\n    \"compile\": \"cp ../../../../tsconfig.template.json ./tsconfig-compile.json && tsc --project tsconfig-compile.json\"\n  },\n  \"engines\": {\n    \"node\": \"20\"\n  },\n  \"private\": true\n}\n"
  },
  {
    "path": "Node-1st-gen/quickstarts/runtime-options/.gitignore",
    "content": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\nfirebase-debug.log*\nfirebase-debug.*.log*\n\n# Firebase cache\n.firebase/\n\n# Firebase config\n\n# Uncomment this if you'd like others to create their own Firebase project.\n# For a team working on the same Firebase project(s), it is recommended to leave\n# it commented so all members can deploy to the same project(s) in .firebaserc.\n# .firebaserc\n\n# Runtime data\npids\n*.pid\n*.seed\n*.pid.lock\n\n# Directory for instrumented libs generated by jscoverage/JSCover\nlib-cov\n\n# Coverage directory used by tools like istanbul\ncoverage\n\n# nyc test coverage\n.nyc_output\n\n# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)\n.grunt\n\n# Bower dependency directory (https://bower.io/)\nbower_components\n\n# node-waf configuration\n.lock-wscript\n\n# Compiled binary addons (http://nodejs.org/api/addons.html)\nbuild/Release\n\n# Dependency directories\nnode_modules/\n\n# Optional npm cache directory\n.npm\n\n# Optional eslint cache\n.eslintcache\n\n# Optional REPL history\n.node_repl_history\n\n# Output of 'npm pack'\n*.tgz\n\n# Yarn Integrity file\n.yarn-integrity\n\n# dotenv environment variables file\n.env\n"
  },
  {
    "path": "Node-1st-gen/quickstarts/runtime-options/firebase.json",
    "content": "{\n  \"functions\": {\n    \"source\": \"functions\",\n    \"codebase\": \"runtime-options\",\n    \"predeploy\": [\n      \"npm --prefix \\\"$RESOURCE_DIR\\\" run lint\"\n    ]\n  }\n}\n"
  },
  {
    "path": "Node-1st-gen/quickstarts/runtime-options/functions/.eslintrc.js",
    "content": "/**\n * Copyright 2023 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nmodule.exports = {\n  root: true,\n  env: {\n    es2020: true,\n    node: true,\n  },\n  extends: [\n    \"eslint:recommended\",\n    \"google\",\n  ],\n  rules: {\n    quotes: [\"error\", \"double\"],\n  },\n};\n"
  },
  {
    "path": "Node-1st-gen/quickstarts/runtime-options/functions/.gitignore",
    "content": "node_modules/"
  },
  {
    "path": "Node-1st-gen/quickstarts/runtime-options/functions/index.js",
    "content": "/**\n * Copyright 2023 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nconst functions = require(\"firebase-functions/v1\");\n\n// [START runtimeMinInstances]\nexports.getAutocompleteResponse = functions\n    .runWith({\n      // Keep 5 instances warm for this latency-critical function\n      minInstances: 5,\n    })\n    .https.onCall((data, context) => {\n      // Autocomplete a user's search term\n    });\n// [END runtimeMinInstances]\n\n// [START runtimeMinInstancesDynamic]\n// Get Firebase project id from `FIREBASE_CONFIG` environment variable\nconst envProjectId = JSON.parse(process.env.FIREBASE_CONFIG).projectId;\n\nexports.renderProfilePage = functions\n    .runWith({\n      // Keep 5 instances warm for this latency-critical function\n      // in production only. Default to 0 for test projects.\n      minInstances: envProjectId === \"my-production-project\" ? 5 : 0,\n    })\n    .https.onRequest((req, res) => {\n      // render some html\n    });\n// [END runtimeMinInstancesDynamic]\n\n// [START runtimeMaxInstances]\nexports.mirrorOrdersToLegacyDatabase = functions\n    .runWith({\n      // Legacy database only supports 100 simultaneous connections\n      maxInstances: 100,\n    })\n    .firestore.document(\"orders/{orderId}\")\n    .onWrite((change, context) => {\n      // Connect to legacy database\n    });\n// [END runtimeMaxInstances]\n\n// [START runtimeTimeoutMemory]\nexports.convertLargeFile = functions\n    .runWith({\n      // Ensure the function has enough memory and time\n      // to process large files\n      timeoutSeconds: 300,\n      memory: \"1GB\",\n    })\n    .storage.object()\n    .onFinalize((object) => {\n      // Do some complicated things that take a lot of memory and time\n    });\n// [END runtimeTimeoutMemory]\n"
  },
  {
    "path": "Node-1st-gen/quickstarts/runtime-options/functions/package.json",
    "content": "{\n  \"name\": \"functions-runtime-options\",\n  \"description\": \"Cloud Functions for Firebase\",\n  \"scripts\": {\n    \"lint\": \"eslint .\",\n    \"serve\": \"firebase emulators:start --only functions\",\n    \"shell\": \"firebase functions:shell\",\n    \"start\": \"npm run shell\",\n    \"deploy\": \"firebase deploy --only functions\",\n    \"logs\": \"firebase functions:log\"\n  },\n  \"engines\": {\n    \"node\": \"20\"\n  },\n  \"main\": \"index.js\",\n  \"dependencies\": {\n    \"eslint-plugin-promise\": \"^7.2.1\",\n    \"firebase-admin\": \"^13.0.2\",\n    \"firebase-functions\": \"7.0.0\"\n  },\n  \"devDependencies\": {\n    \"eslint\": \"^8.57.1\",\n    \"eslint-config-google\": \"^0.14.0\",\n    \"firebase-functions-test\": \"^3.4.0\"\n  },\n  \"private\": true\n}\n"
  },
  {
    "path": "Node-1st-gen/quickstarts/taskqueues-backup-images/README.md",
    "content": "# Quickstart: Back up images using a Task Queue Function\nThis quickstart demonstrates how to setup a Task Queue Function using the Firebase SDK for Cloud Functions.\n\n## Introduction\n\nTask queue functions make it easy to manage the execution, dispatch, and delivery of a large number of distributed tasks.\n\nWe leverage its power here to set up a service that backs up all images from NASA's [Astronomy Picture of the Day](https://apod.nasa.gov/apod/astropix.html).\n\nTask queue functions are powered by [Google Cloud Tasks](https://cloud.google.com/tasks). Learn more about [task queue functions](https://firebase.google.com/docs/functions/task-functions).\n\n## Functions\nThe sample code consists of 2 functions:\n\n### 1. `backupApod`\nA task queue function responsible for processing the logic for backing up the Astronomy Picture of the Day (\"apod\") for the given date. This function will be triggered for every task enqueued on the corresponding queue created in Cloud Tasks.\n\nTask queue functions come with a powerful set of configuration to precisely control rate limits and retry behavior of a task queue. [See the documentation](https://cloud.google.com/tasks/docs/creating-queues) to learn more about configuring task queue functions.\n\nOur sample make use of following configurations:\n\n1) `retryConfig.maxAttempts=5` - Each task in our task queue will be automatically retried upto 5 times. This helps us mitigate transient errors like network errors or temporary service disruption of a dependent, external service.\n2) `retryConfig.minBackoffSeconds=60` - Each task will be retried at least 60 seconds apart from each attempt. This gives us a large buffer between each attempt so we don't rush to exhaust our 5 retry attempts too quickly.\n3) `rateLimits.maxConcurrentDispatch` - At most 6 tasks will be dispatched at a given time. At most 6 tasks will be dispatched at a given time. This helps ensure a steady stream of requests to the underlying function and helps reduce the number of active instances and cold starts.\n \nYou can further configure this function with following [environment variables](https://firebase.google.com/docs/functions/config-env):\n\n* `BACKUP_BUCKET`: Name of the bucket to back up \"apod\" images. Defaults to default Cloud Storage bucket.\n\n### 2. `enqueueBackupTasks`\nAn HTTP function responsible for enqueuing tasks to our task queue. The function uses the Firebase Admin SDK to create and enqueue a task for each day we want to backup an \"apod\" image.\n\nYou can configure this function with following [environment variables](https://firebase.google.com/docs/functions/config-env):\n\n* `BACKUP_COUNT`: Number of days to back up Astronomy Picture of the Day, starting from 1995-06-17 (the first day of publication). Defaults to 100.\n\n* `HOURLY_BATCH_SIZE`: Number of tasks to enqueue at each hour. Note that NASA API imposes a limit of 1000 reqs/hour. Defaults to 500.\n\n## Setup and Deploy\n\n### NASA Open APIs Key\nThe sample function uses [NASA Open APIs](https://api.nasa.gov/) to retrieve Astronomy Picture of the Day images. You need to register an account to get your API Key and hook it up the task queue function by [creating a secret](https://firebase.google.com/docs/functions/config-env#secret-manager):\n\n```bash\n$ firebase functions:secrets:set NASA_API_KEY\n? Enter a value for NASA_API_KEY [input is hidden]\n✔  Created a new secret version projects/XXX/secrets/NASA_API_KEY/versions/1\n```\n\n### Deploy\nDeploy functions using Firebase CLI:\n\n```bash\n$ firebase deploy\n```\n\n## IAM Policy\nYou may see `PERMISSION DENIED` errors when enqueueing tasks or when Cloud Task tries to invoke your task queue functions. Ensure that your project has the following IAM bindings:\n\n* Identity used to enqueue tasks to Cloud Tasks needs `cloudtasks.tasks.create` IAM permission\n  * In our sample, this is the [App Engine default service account](https://cloud.google.com/functions/docs/securing/function-identity#runtime_service_account)\n\n```\ngcloud projects add-iam-policy-binding $PROJECT_ID \\\n  --member=serviceAccount:${PROJECT_ID}@appspot.gserviceaccount.com \\\n  --role=roles/cloudtasks.enqueuer\n```\n\n* Identity used to enqueue tasks to Cloud Task needs permission to use the service account associated with a task in Cloud Tasks.\n  * In our sample, this is the [App Engine default service account](https://cloud.google.com/functions/docs/securing/function-identity#runtime_service_account).\n\n\n```\nPlease follow Google Cloud IAM documentation to add App Engine default service account as user of App Engine default service account.\nhttps://cloud.google.com/iam/docs/impersonating-service-accounts#impersonate-sa-level\n```\n\n* Identity used to trigger the Task Queue function needs `cloudfunctions.functions.invoke` permission.\n  * In our sample, this is [App Engine default service account](https://cloud.google.com/functions/docs/securing/function-identity#runtime_service_account)\n\n```\ngcloud functions add-iam-policy-binding $FUNCTION_NAME \\\n  --region=us-central1 \\\n  --member=serviceAccount:${PROJECT_ID}@appspot.gserviceaccount.com \\\n  --role=roles/cloudfunctions.invoker\n```"
  },
  {
    "path": "Node-1st-gen/quickstarts/taskqueues-backup-images/firebase.json",
    "content": "{\n    \"functions\": {\n        \"codebase\": \"taskqueues-backup-images\"\n    }\n}\n"
  },
  {
    "path": "Node-1st-gen/quickstarts/taskqueues-backup-images/functions/.eslintrc.json",
    "content": "{\n  \"parserOptions\": {\n    // Required for certain syntax usages\n    \"ecmaVersion\": 2022\n  },\n  \"plugins\": [\n    \"promise\"\n  ],\n  \"extends\": \"eslint:recommended\",\n  \"rules\": {\n    // Removed rule \"disallow the use of console\" from recommended eslint rules\n    \"no-console\": \"off\",\n\n    // Removed rule \"disallow multiple spaces in regular expressions\" from recommended eslint rules\n    \"no-regex-spaces\": \"off\",\n\n    // Removed rule \"disallow the use of debugger\" from recommended eslint rules\n    \"no-debugger\": \"off\",\n\n    // Removed rule \"disallow unused variables\" from recommended eslint rules\n    \"no-unused-vars\": \"off\",\n\n    // Removed rule \"disallow mixed spaces and tabs for indentation\" from recommended eslint rules\n    \"no-mixed-spaces-and-tabs\": \"off\",\n\n    // Removed rule \"disallow the use of undeclared variables unless mentioned in /*global */ comments\" from recommended eslint rules\n    \"no-undef\": \"off\",\n\n    // Warn against template literal placeholder syntax in regular strings\n    \"no-template-curly-in-string\": 1,\n\n    // Warn if return statements do not either always or never specify values\n    \"consistent-return\": 1,\n\n    // Warn if no return statements in callbacks of array methods\n    \"array-callback-return\": 1,\n\n    // Requre the use of === and !==\n    \"eqeqeq\": 2,\n\n    // Disallow the use of alert, confirm, and prompt\n    \"no-alert\": 2,\n\n    // Disallow the use of arguments.caller or arguments.callee\n    \"no-caller\": 2,\n\n    // Disallow null comparisons without type-checking operators\n    \"no-eq-null\": 2,\n\n    // Disallow the use of eval()\n    \"no-eval\": 2,\n\n    // Warn against extending native types\n    \"no-extend-native\": 1,\n\n    // Warn against unnecessary calls to .bind()\n    \"no-extra-bind\": 1,\n\n    // Warn against unnecessary labels    \n    \"no-extra-label\": 1,\n\n    // Disallow leading or trailing decimal points in numeric literals\n    \"no-floating-decimal\": 2,\n\n    // Warn against shorthand type conversions\n    \"no-implicit-coercion\": 1,\n\n    // Warn against function declarations and expressions inside loop statements\n    \"no-loop-func\": 1,\n\n    // Disallow new operators with the Function object\n    \"no-new-func\": 2,\n\n    // Warn against new operators with the String, Number, and Boolean objects\n    \"no-new-wrappers\": 1,\n\n    // Disallow throwing literals as exceptions\n    \"no-throw-literal\": 2,\n\n    // Require using Error objects as Promise rejection reasons\n    \"prefer-promise-reject-errors\": 2,\n\n    // Enforce “for” loop update clause moving the counter in the right direction\n    \"for-direction\": 2,\n\n    // Enforce return statements in getters\n    \"getter-return\": 2,\n\n    // Disallow await inside of loops\n    \"no-await-in-loop\": 2,\n\n    // Disallow comparing against -0\n    \"no-compare-neg-zero\": 2,\n\n    // Warn against catch clause parameters from shadowing variables in the outer scope\n    \"no-catch-shadow\": 1,\n\n    // Disallow identifiers from shadowing restricted names\n    \"no-shadow-restricted-names\": 2,\n\n    // Enforce return statements in callbacks of array methods\n    \"callback-return\": 2,\n\n    // Require error handling in callbacks\n    \"handle-callback-err\": 2,\n\n    // Warn against string concatenation with __dirname and __filename\n    \"no-path-concat\": 1,\n\n    // Prefer using arrow functions for callbacks\n    \"prefer-arrow-callback\": 1,\n\n    // Return inside each then() to create readable and reusable Promise chains.\n    \"promise/always-return\": 2,\n\n    //Enforces the use of catch() on un-returned promises\n    \"promise/catch-or-return\": 2,\n\n    // Warn against nested then() or catch() statements\n    \"promise/no-nesting\": 1\n  }\n}\n"
  },
  {
    "path": "Node-1st-gen/quickstarts/taskqueues-backup-images/functions/index.js",
    "content": "/**\n * Copyright 2022 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for t`he specific language governing permissions and\n * limitations under the License.\n */\n\"use strict\";\nconst path = require(\"path\");\nconst functions = require('firebase-functions/v1');\nconst {initializeApp} = require(\"firebase-admin/app\");\nconst {getFunctions} = require(\"firebase-admin/functions\");\nconst {getStorage} = require(\"firebase-admin/storage\");\nconst logger = functions.logger;\nconst HttpsError = functions.https.HttpsError;\ninitializeApp();\n\nconst BACKUP_START_DATE = new Date(\"1995-06-17\");\nconst BACKUP_COUNT = parseInt(process.env.BACKUP_COUNT) || 100;\nconst HOURLY_BATCH_SIZE = parseInt(process.env.HOURLY_BATCH_SIZE) || 500;\nconst BACKUP_BUCKET = process.env.BACKUP_BUCKET;\n\n/**\n * Grabs Astronomy Photo of the Day (APOD) using NASA's API.\n */\n// [START taskFunctionSetup]\nexports.backupApod = functions\n    .runWith( {secrets: [\"NASA_API_KEY\"]})\n    .tasks.taskQueue({\n      retryConfig: {\n        maxAttempts: 5,\n        minBackoffSeconds: 60,\n      },\n      rateLimits: {\n        maxConcurrentDispatches: 6,\n      },\n    }).onDispatch(async (data) => {\n// [END taskFunctionSetup]\n      const date = data.date;\n      if (!date) {\n        logger.warn(\"Invalid payload. Must include date.\");\n        throw new HttpsError(\n            \"invalid-argument\",\n            \"Invalid payload. Must include date.\",\n        );\n      }\n\n      logger.info(`Requesting data from apod api for date ${date}`);\n      let url = \"https://api.nasa.gov/planetary/apod\";\n      url += `?date=${date}`;\n      url += `&api_key=${process.env.NASA_API_KEY}`;\n      const apiResp = await fetch(url);\n      if (!apiResp.ok) {\n        logger.warn(\n            `request to NASA APOD API failed with reponse ${apiResp.status}`,\n        );\n        if (apiResp.status === 404) {\n          // APOD not published for the day. This is fine!\n          return;\n        }\n        if (apiResp.status >= 500) {\n          throw new HttpsError(\n              \"unavailable\",\n              \"APOD API temporarily not available.\",\n          );\n        }\n        throw new HttpsError(\"internal\", \"Uh-oh. Something broke.\");\n      }\n      const apod = await apiResp.json();\n      const picUrl = apod.hdurl;\n      logger.info(`Fetched ${picUrl} from NASA API for date ${date}.`);\n\n      const picResp = await fetch(picUrl);\n      const imageBuffer = await picResp.arrayBuffer();\n      const buffer = Buffer.from(imageBuffer);\n      const dest = getStorage()\n          .bucket(BACKUP_BUCKET)\n          .file(`apod/${date}${path.extname(picUrl)}`);\n      try {\n        await dest.save(buffer);\n      } catch (err) {\n        logger.error(`Failed to upload ${picUrl} to ${dest.name}`, err);\n        throw new HttpsError(\"internal\", \"Uh-oh. Something broke.\");\n      }\n    });\n\n// [START enqueueTasks]\nexports.enqueueBackupTasks = functions.https.onRequest(\n    async (_request, response) => {\n      const queue = getFunctions().taskQueue(\"backupApod\");\n\n      const enqueues = [];\n      for (let i = 0; i <= BACKUP_COUNT; i += 1) {\n        const iteration = Math.floor(i / HOURLY_BATCH_SIZE);\n        const scheduleDelaySeconds = iteration * (60 * 60); // Delay each batch by N * hour\n\n        const backupDate = new Date(BACKUP_START_DATE);\n        backupDate.setDate(BACKUP_START_DATE.getDate() + i);\n        // Extract just the date portion (YYYY-MM-DD) as string.\n        const date = backupDate.toISOString().substring(0, 10);\n          enqueues.push(\n              queue.enqueue({date}, {\n                  scheduleDelaySeconds,\n                  dispatchDeadlineSeconds: 60 * 5 // 5 minutes\n              }),\n          );\n      }\n      await Promise.all(enqueues);\n      response.sendStatus(200);\n    });\n// [END enqueueTasks]\n"
  },
  {
    "path": "Node-1st-gen/quickstarts/taskqueues-backup-images/functions/package.json",
    "content": "{\n  \"name\": \"functions-taskqueues-backup-images\",\n  \"description\": \"Back up images from NASA's Astronomy Picture of the Day using Cloud Tasks.\",\n  \"scripts\": {\n    \"lint\": \"eslint .\",\n    \"lint:fix\": \"eslint . --fix\",\n    \"serve\": \"firebase emulators:start --only functions\",\n    \"shell\": \"firebase functions:shell\",\n    \"start\": \"npm run shell\",\n    \"deploy\": \"firebase deploy --only functions\",\n    \"logs\": \"firebase functions:log\",\n    \"compile\": \"cp ../../../../tsconfig.template.json ./tsconfig-compile.json && tsc --project tsconfig-compile.json\"\n  },\n  \"engines\": {\n    \"node\": \"20\"\n  },\n  \"main\": \"index.js\",\n  \"dependencies\": {\n    \"eslint-plugin-promise\": \"^7.2.1\",\n    \"firebase-admin\": \"^13.0.2\",\n    \"firebase-functions\": \"7.0.0\"\n  },\n  \"devDependencies\": {\n    \"eslint\": \"^8.57.1\",\n    \"eslint-config-google\": \"^0.14.0\"\n  },\n  \"private\": true\n}\n"
  },
  {
    "path": "Node-1st-gen/quickstarts/testlab-matrix-completed/.gitignore",
    "content": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\nfirebase-debug.log*\n\n# Firebase cache\n.firebase/\n\n# Firebase config\n\n# Uncomment this if you'd like others to create their own Firebase project.\n# For a team working on the same Firebase project(s), it is recommended to leave\n# it commented so all members can deploy to the same project(s) in .firebaserc.\n# .firebaserc\n\n# Runtime data\npids\n*.pid\n*.seed\n*.pid.lock\n\n# Directory for instrumented libs generated by jscoverage/JSCover\nlib-cov\n\n# Coverage directory used by tools like istanbul\ncoverage\n\n# nyc test coverage\n.nyc_output\n\n# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)\n.grunt\n\n# Bower dependency directory (https://bower.io/)\nbower_components\n\n# node-waf configuration\n.lock-wscript\n\n# Compiled binary addons (http://nodejs.org/api/addons.html)\nbuild/Release\n\n# Dependency directories\nnode_modules/\n\n# Optional npm cache directory\n.npm\n\n# Optional eslint cache\n.eslintcache\n\n# Optional REPL history\n.node_repl_history\n\n# Output of 'npm pack'\n*.tgz\n\n# Yarn Integrity file\n.yarn-integrity\n\n# dotenv environment variables file\n.env\n"
  },
  {
    "path": "Node-1st-gen/quickstarts/testlab-matrix-completed/README.md",
    "content": "# Firebase Test Lab Trigger - Quickstart\n\nThis quickstart demonstrates how to trigger a function in response to the\ncompletion of a test matrix in **Firebase Test Lab**.\n\n## Setting up the sample\n\n1. Clone or download this repo and open the `quickstarts/test-complete`\n   directory.\n1. You must have the Firebase CLI installed. If you don't have it install it\n   with `npm install -g firebase-tools` and then configure it with\n   `firebase login`.\n1. Configure the CLI locally by using `firebase use --add` and select your\n   project in the list.\n1. Install Cloud Functions dependencies locally by running:\n   `cd functions; npm install; cd -`\n\n## Deploy and test\n\n1.  Deploy your function using `firebase deploy --only functions`\n1.  Navigate to the\n    [Test Lab](https://console.firebase.google.com/u/0/project/_/testlab/histories)\n    section of the Firebase Console and start a test.\n1.  Once the test finishes running,\n    [view the functions logs](https://console.firebase.google.com/u/0/project/_/functions/logs?severity=DEBUG)\n    for your project, and check that the test run status was logged.\n\n## Next Steps\n\nTo see how to post to Slack instead of just `console.log`-ing, check out\n[this sample](https://github.com/firebase/functions-samples/tree/main/testlab-to-slack).\n"
  },
  {
    "path": "Node-1st-gen/quickstarts/testlab-matrix-completed/firebase.json",
    "content": "{\n    \"functions\": {\n        \"codebase\": \"testlab-matrix-completed\"\n    }\n}\n"
  },
  {
    "path": "Node-1st-gen/quickstarts/testlab-matrix-completed/functions/.gitignore",
    "content": "node_modules/"
  },
  {
    "path": "Node-1st-gen/quickstarts/testlab-matrix-completed/functions/index.js",
    "content": "/**\n * Copyright 2023 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nconst functions = require('firebase-functions/v1');\n\nexports.logTestComplete = functions.testLab\n  .testMatrix()\n  .onComplete(testMatrix => {\n    const { testMatrixId, createTime, state, outcomeSummary } = testMatrix;\n\n    functions.logger.log(\n      `TEST ${testMatrixId} (created at ${createTime}): ${state}. ${\n        outcomeSummary || ''\n      }`\n    );\n  });\n"
  },
  {
    "path": "Node-1st-gen/quickstarts/testlab-matrix-completed/functions/package.json",
    "content": "{\n  \"name\": \"functions\",\n  \"description\": \"Cloud Functions for Firebase\",\n  \"scripts\": {\n    \"serve\": \"firebase emulators:start --only functions\",\n    \"shell\": \"firebase functions:shell\",\n    \"start\": \"npm run shell\",\n    \"deploy\": \"firebase deploy --only functions\",\n    \"logs\": \"firebase functions:log\",\n    \"compile\": \"cp ../../../../tsconfig.template.json ./tsconfig-compile.json && tsc --project tsconfig-compile.json\"\n  },\n  \"engines\": {\n    \"node\": \"20\"\n  },\n  \"dependencies\": {\n    \"eslint\": \"8\",\n    \"eslint-plugin-promise\": \"^7.2.1\",\n    \"firebase-admin\": \"^13.0.2\",\n    \"firebase-functions\": \"7.0.0\"\n  },\n  \"devDependencies\": {\n    \"firebase-functions-test\": \"^3.4.0\"\n  },\n  \"private\": true\n}\n"
  },
  {
    "path": "Node-1st-gen/quickstarts/thumbnails/README.md",
    "content": "# Firebase SDK for Cloud Functions Quickstart - Cloud Storage trigger\n\nThis quickstart demonstrates using **Firebase SDK for Cloud Functions** setup with a Cloud Storage trigger.\n\n## Introduction\n\nThis sample automatically generates thumbnails for images that are uploaded to Cloud Storage.\n\n\n## Functions Code\n\nSee file [functions/index.js](functions/index.js) for the thumbnail generation code.\n\nThe thumbnail generation is performed using [sharp](https://www.npmjs.com/package/sharp).\n\nThe dependencies are listed in [functions/package.json](functions/package.json).\n\n\n## Trigger rules\n\nThe function triggers on upload of any file to your Firebase project's default Cloud Storage bucket.\n\n\n## Deploy and test\n\nTo deploy and test the sample:\n\n - Create a Firebase project on the [Firebase Console](https://console.firebase.google.com) and visit the **Storage** tab.\n - Get the code, for instance using `git clone https://github.com/firebase/functions-samples`\n - Enter the correct directory `cd functions-samples/quickstarts/thumbnails`\n - Setup the CLI to use your Firebase project using `firebase use --add` and select your Firebase project\n - Deploy your project's code using `firebase deploy`\n - Go to the Firebase Console **Storage** tab and upload an image. After a short time a thumbnail image with the same name but a `thumb_` prefix will be created in the same folder (make sure you refresh the UI to see the new file).\n\n\n## Contributing\n\nWe'd love that you contribute to the project. Before doing so please read our [Contributor guide](../../CONTRIBUTING.md).\n\n\n## License\n\n© Google, 2016. Licensed under an [Apache-2](../../LICENSE) license.\n"
  },
  {
    "path": "Node-1st-gen/quickstarts/thumbnails/firebase.json",
    "content": "{\n    \"functions\": {\n        \"codebase\": \"thumbnails\"\n    }\n}\n"
  },
  {
    "path": "Node-1st-gen/quickstarts/thumbnails/functions/.eslintrc.json",
    "content": "{\n  \"parserOptions\": {\n    // Required for certain syntax usages\n    \"ecmaVersion\": 2022\n  },\n  \"plugins\": [\n    \"promise\"\n  ],\n  \"extends\": \"eslint:recommended\",\n  \"rules\": {\n    // Removed rule \"disallow the use of console\" from recommended eslint rules\n    \"no-console\": \"off\",\n\n    // Removed rule \"disallow multiple spaces in regular expressions\" from recommended eslint rules\n    \"no-regex-spaces\": \"off\",\n\n    // Removed rule \"disallow the use of debugger\" from recommended eslint rules\n    \"no-debugger\": \"off\",\n\n    // Removed rule \"disallow unused variables\" from recommended eslint rules\n    \"no-unused-vars\": \"off\",\n\n    // Removed rule \"disallow mixed spaces and tabs for indentation\" from recommended eslint rules\n    \"no-mixed-spaces-and-tabs\": \"off\",\n\n    // Removed rule \"disallow the use of undeclared variables unless mentioned in /*global */ comments\" from recommended eslint rules\n    \"no-undef\": \"off\",\n\n    // Warn against template literal placeholder syntax in regular strings\n    \"no-template-curly-in-string\": 1,\n\n    // Warn if return statements do not either always or never specify values\n    \"consistent-return\": 1,\n\n    // Warn if no return statements in callbacks of array methods\n    \"array-callback-return\": 1,\n\n    // Requre the use of === and !==\n    \"eqeqeq\": 2,\n\n    // Disallow the use of alert, confirm, and prompt\n    \"no-alert\": 2,\n\n    // Disallow the use of arguments.caller or arguments.callee\n    \"no-caller\": 2,\n\n    // Disallow null comparisons without type-checking operators\n    \"no-eq-null\": 2,\n\n    // Disallow the use of eval()\n    \"no-eval\": 2,\n\n    // Warn against extending native types\n    \"no-extend-native\": 1,\n\n    // Warn against unnecessary calls to .bind()\n    \"no-extra-bind\": 1,\n\n    // Warn against unnecessary labels    \n    \"no-extra-label\": 1,\n\n    // Disallow leading or trailing decimal points in numeric literals\n    \"no-floating-decimal\": 2,\n\n    // Warn against shorthand type conversions\n    \"no-implicit-coercion\": 1,\n\n    // Warn against function declarations and expressions inside loop statements\n    \"no-loop-func\": 1,\n\n    // Disallow new operators with the Function object\n    \"no-new-func\": 2,\n\n    // Warn against new operators with the String, Number, and Boolean objects\n    \"no-new-wrappers\": 1,\n\n    // Disallow throwing literals as exceptions\n    \"no-throw-literal\": 2,\n\n    // Require using Error objects as Promise rejection reasons\n    \"prefer-promise-reject-errors\": 2,\n\n    // Enforce “for” loop update clause moving the counter in the right direction\n    \"for-direction\": 2,\n\n    // Enforce return statements in getters\n    \"getter-return\": 2,\n\n    // Disallow await inside of loops\n    \"no-await-in-loop\": 2,\n\n    // Disallow comparing against -0\n    \"no-compare-neg-zero\": 2,\n\n    // Warn against catch clause parameters from shadowing variables in the outer scope\n    \"no-catch-shadow\": 1,\n\n    // Disallow identifiers from shadowing restricted names\n    \"no-shadow-restricted-names\": 2,\n\n    // Enforce return statements in callbacks of array methods\n    \"callback-return\": 2,\n\n    // Require error handling in callbacks\n    \"handle-callback-err\": 2,\n\n    // Warn against string concatenation with __dirname and __filename\n    \"no-path-concat\": 1,\n\n    // Prefer using arrow functions for callbacks\n    \"prefer-arrow-callback\": 1,\n\n    // Return inside each then() to create readable and reusable Promise chains.\n    \"promise/always-return\": 2,\n\n    //Enforces the use of catch() on un-returned promises\n    \"promise/catch-or-return\": 2,\n\n    // Warn against nested then() or catch() statements\n    \"promise/no-nesting\": 1\n  }\n}\n"
  },
  {
    "path": "Node-1st-gen/quickstarts/thumbnails/functions/index.js",
    "content": "/**\n * Copyright 2016 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use strict';\n\n// [START import]\nconst functions = require('firebase-functions/v1');\nconst admin = require('firebase-admin');\nadmin.initializeApp()\nconst path = require('path');\n\n//library for resizing images\nconst sharp = require('sharp');\n// [END import]\n\n// [START generateThumbnail]\n/**\n * When an image is uploaded in the Storage bucket,\n * generate a thumbnail automatically using sharp.\n */\n// [START generateThumbnailTrigger]\nexports.firstGenGenerateThumbnail = functions.storage.object().onFinalize(async (object) => {\n// [END generateThumbnailTrigger]\n  // [START eventAttributes]\n  const fileBucket = object.bucket; // The Storage bucket that contains the file.\n  const filePath = object.name; // File path in the bucket.\n  const contentType = object.contentType; // File content type.\n  // [END eventAttributes]\n\n  // [START stopConditions]\n  // Exit if this is triggered on a file that is not an image.\n  if (!contentType.startsWith('image/')) {\n    return functions.logger.log('This is not an image.');\n  }\n\n  // Get the file name.\n  const fileName = path.basename(filePath);\n  // Exit if the image is already a thumbnail.\n  if (fileName.startsWith('thumb_')) {\n    return functions.logger.log('Already a Thumbnail.');\n  }\n  // [END stopConditions]\n\n  // [START thumbnailGeneration]\n  // Download file from bucket.\n  const bucket = admin.storage().bucket(fileBucket);\n  const metadata = {\n    contentType: contentType,\n  };\n  const downloadResponse = await bucket.file(filePath).download();\n  const imageBuffer = downloadResponse[0];\n  functions.logger.log(\"Image downloaded!\");\n\n  // Generate a thumbnail using sharp.\n  const thumbnailBuffer = await sharp(imageBuffer).resize({\n    width: 200,\n    height: 200,\n    withoutEnlargement: true,\n  }).toBuffer();\n  functions.logger.log(\"Thumbnail created\");\n\n  // Upload the thumbnail with a 'thumb_' prefix.\n  const thumbFileName = `thumb_${fileName}`;\n  const thumbFilePath = path.join(path.dirname(filePath), thumbFileName);\n  await bucket.file(thumbFilePath).save(thumbnailBuffer, {\n    metadata: metadata,\n  });\n  return functions.logger.log(\"Thumbnail uploaded!\");\n  // [END thumbnailGeneration]\n});\n// [END generateThumbnail]\n"
  },
  {
    "path": "Node-1st-gen/quickstarts/thumbnails/functions/package.json",
    "content": "{\n  \"name\": \"generate-thumbnail-functions-quickstart\",\n  \"description\": \"Generate Thumbnail Firebase Functions sample\",\n  \"dependencies\": {\n    \"eslint-plugin-promise\": \"^7.2.1\",\n    \"firebase-admin\": \"^13.0.2\",\n    \"firebase-functions\": \"7.0.0\",\n    \"sharp\": \"^0.32.1\"\n  },\n  \"devDependencies\": {\n    \"eslint\": \"^8.57.1\"\n  },\n  \"scripts\": {\n    \"lint\": \"./node_modules/.bin/eslint --max-warnings=0 .\",\n    \"serve\": \"firebase emulators:start --only functions\",\n    \"shell\": \"firebase functions:shell\",\n    \"start\": \"npm run shell\",\n    \"deploy\": \"firebase deploy --only functions\",\n    \"logs\": \"firebase functions:log\",\n    \"compile\": \"cp ../../../../tsconfig.template.json ./tsconfig-compile.json && tsc --project tsconfig-compile.json\"\n  },\n  \"engines\": {\n    \"node\": \"20\"\n  },\n  \"private\": true\n}\n"
  },
  {
    "path": "Node-1st-gen/quickstarts/uppercase-firestore/.gitignore",
    "content": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\nfirebase-debug.log*\n\n# Firebase cache\n.firebase/\n\n# Firebase config\n\n# Uncomment this if you'd like others to create their own Firebase project.\n# For a team working on the same Firebase project(s), it is recommended to leave\n# it commented so all members can deploy to the same project(s) in .firebaserc.\n# .firebaserc\n\n# Runtime data\npids\n*.pid\n*.seed\n*.pid.lock\n\n# Directory for instrumented libs generated by jscoverage/JSCover\nlib-cov\n\n# Coverage directory used by tools like istanbul\ncoverage\n\n# nyc test coverage\n.nyc_output\n\n# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)\n.grunt\n\n# Bower dependency directory (https://bower.io/)\nbower_components\n\n# node-waf configuration\n.lock-wscript\n\n# Compiled binary addons (http://nodejs.org/api/addons.html)\nbuild/Release\n\n# Dependency directories\nnode_modules/\n\n# Optional npm cache directory\n.npm\n\n# Optional eslint cache\n.eslintcache\n\n# Optional REPL history\n.node_repl_history\n\n# Output of 'npm pack'\n*.tgz\n\n# Yarn Integrity file\n.yarn-integrity\n\n# dotenv environment variables file\n.env\n"
  },
  {
    "path": "Node-1st-gen/quickstarts/uppercase-firestore/README.md",
    "content": "# Firebase SDK for Cloud Functions Quickstart - Firestore\n\nThis quickstart demonstrates using the **Firebase SDK for Cloud Functions** with\n**Firestore**.\n\n## Introduction\n\nThis sample app does two things:\n\n- Creates messages in Firestore using a simple HTTPS request which is\n  handled by an HTTP function. Writing to Firestore is done using the\n  Firebase Admin SDK.\n- When a message gets added in Firestore, a function triggers and\n  automatically makes these messages all uppercase.\n\n## Set up the sample\n\nBefore you can test the functions locally or deploy to a Firebase project,\nyou'll need to run `npm install` in the `functions` directory.\n\n## Run locally with the Firebase Emulator suite\n\nThe\n[Firebase Local Emulator Suite](https://firebase.google.com/docs/emulator-suite)\nallows you to build and test apps on your local machine instead of deploying to\na Firebase project.\n\n1. Create a Firebase project in the\n   [Firebase Console](https://console.firebase.google.com)\n   > _Wondering why this step is needed?_ Even though the emulator will run this\n   > sample on your local machine, it needs to interact with a Firebase project\n   > to retrieve some configuration values.\n1. [Set up or update the Firebase CLI](https://firebase.google.com/docs/cli#setup_update_cli)\n1. Run `firebase emulators:start`\n1. Open the Emulator Suite UI\n   1. Look in the output of the `firebase emulators:start` command for the URL\n      of the Emulator Suite UI. It defaults to\n      [localhost:4000](http://localhost:4000), but may be hosted on a different\n      port on your machine.\n   1. Enter that URL in your browser to open the UI.\n1. Trigger the functions\n   1. Look in the output of the `firebase emulators:start` command for the URL\n      of the http function \"addMessage\". It will look similar to:\n      `http://localhost:5001/MY_PROJECT/us-central1/addMessage`\n      1. `MY_PROJECT` will be replaced with your project ID\n      1. The port may be different on your local machine\n   1. Add the query string `?text=uppercaseme` to the end of the function's URL.\n      It should now look something like:\n      `http://localhost:5001/MY_PROJECT/us-central1/addMessage?text=uppercaseme`\n      1. Optionally, you can change the message \"uppercaseme\" to a custom\n         message\n   1. Create a new message by opening the URL in a new tab in your browser\n1. View the effects of the functions in the Emulator Suite UI\n\n   1. In the \"Logs\" tab, you should see new logs indicating that the functions\n      \"addMessage\" and \"makeUppercase\" ran:\n\n      > `functions: Beginning execution of \"addMessage\"`\n\n      > `functions: Beginning execution of \"makeUppercase\"`\n\n   1. In the \"Firestore\" tab, you should see a document containing your original\n      message as well as the uppercased version of your message (if it was\n      originally \"uppercaseme\", you'll see \"UPPERCASEME\")\n\n## Deploy and test on a live Firebase project\n\nTo deploy and test the sample:\n\n1. Create a Firebase project on the\n   [Firebase Console](https://console.firebase.google.com)\n1. Deploy your project's code using `firebase deploy`\n1. Create a message by opening the following URL in your browser:\n   https://us-central1-[MY_PROJECT].cloudfunctions.net/addMessage?text=uppercaseme\n   (Replace [MY_PROJECT] by your project ID and you can change the message\n   \"uppercaseme\").\n\nYou should see your text value displayed in the console and uppercase.\n\n## Contributing\n\nWe'd love that you contribute to the project. Before doing so please read our\n[Contributor guide](../../CONTRIBUTING.md).\n\n## License\n\n© Google, 2016. Licensed under an [Apache-2](../../LICENSE) license.\n"
  },
  {
    "path": "Node-1st-gen/quickstarts/uppercase-firestore/firebase.json",
    "content": "{\n  \"functions\": {\n    \"codebase\": \"uppercase-firestore\"\n  },\n  \"firestore\": {\n    \"rules\": \"firestore.rules\",\n    \"indexes\": \"firestore.indexes.json\"\n  },\n  \"emulators\": {\n    \"functions\": {\n      \"port\": 5001\n    },\n    \"firestore\": {\n      \"port\": 8080\n    },\n    \"ui\": {\n      \"enabled\": true\n    }\n  }\n}\n"
  },
  {
    "path": "Node-1st-gen/quickstarts/uppercase-firestore/firestore.indexes.json",
    "content": "{\n  \"indexes\": [],\n  \"fieldOverrides\": []\n}\n"
  },
  {
    "path": "Node-1st-gen/quickstarts/uppercase-firestore/firestore.rules",
    "content": "service cloud.firestore {\n  match /databases/{database}/documents {\n    match /messages/{message} {\n      // Allow authenticated users to read/write the messages collection\n      allow read, write: if request.auth != null;\n    }\n  }\n}\n"
  },
  {
    "path": "Node-1st-gen/quickstarts/uppercase-firestore/functions/.eslintrc.json",
    "content": "{\n  \"parserOptions\": {\n    // Required for certain syntax usages\n    \"ecmaVersion\": 2022\n  },\n  \"plugins\": [\n    \"promise\"\n  ],\n  \"extends\": \"eslint:recommended\",\n  \"rules\": {\n    // Removed rule \"disallow the use of console\" from recommended eslint rules\n    \"no-console\": \"off\",\n\n    // Removed rule \"disallow multiple spaces in regular expressions\" from recommended eslint rules\n    \"no-regex-spaces\": \"off\",\n\n    // Removed rule \"disallow the use of debugger\" from recommended eslint rules\n    \"no-debugger\": \"off\",\n\n    // Removed rule \"disallow unused variables\" from recommended eslint rules\n    \"no-unused-vars\": \"off\",\n\n    // Removed rule \"disallow mixed spaces and tabs for indentation\" from recommended eslint rules\n    \"no-mixed-spaces-and-tabs\": \"off\",\n\n    // Removed rule \"disallow the use of undeclared variables unless mentioned in /*global */ comments\" from recommended eslint rules\n    \"no-undef\": \"off\",\n\n    // Warn against template literal placeholder syntax in regular strings\n    \"no-template-curly-in-string\": 1,\n\n    // Warn if return statements do not either always or never specify values\n    \"consistent-return\": 1,\n\n    // Warn if no return statements in callbacks of array methods\n    \"array-callback-return\": 1,\n\n    // Requre the use of === and !==\n    \"eqeqeq\": 2,\n\n    // Disallow the use of alert, confirm, and prompt\n    \"no-alert\": 2,\n\n    // Disallow the use of arguments.caller or arguments.callee\n    \"no-caller\": 2,\n\n    // Disallow null comparisons without type-checking operators\n    \"no-eq-null\": 2,\n\n    // Disallow the use of eval()\n    \"no-eval\": 2,\n\n    // Warn against extending native types\n    \"no-extend-native\": 1,\n\n    // Warn against unnecessary calls to .bind()\n    \"no-extra-bind\": 1,\n\n    // Warn against unnecessary labels    \n    \"no-extra-label\": 1,\n\n    // Disallow leading or trailing decimal points in numeric literals\n    \"no-floating-decimal\": 2,\n\n    // Warn against shorthand type conversions\n    \"no-implicit-coercion\": 1,\n\n    // Warn against function declarations and expressions inside loop statements\n    \"no-loop-func\": 1,\n\n    // Disallow new operators with the Function object\n    \"no-new-func\": 2,\n\n    // Warn against new operators with the String, Number, and Boolean objects\n    \"no-new-wrappers\": 1,\n\n    // Disallow throwing literals as exceptions\n    \"no-throw-literal\": 2,\n\n    // Require using Error objects as Promise rejection reasons\n    \"prefer-promise-reject-errors\": 2,\n\n    // Enforce “for” loop update clause moving the counter in the right direction\n    \"for-direction\": 2,\n\n    // Enforce return statements in getters\n    \"getter-return\": 2,\n\n    // Disallow await inside of loops\n    \"no-await-in-loop\": 2,\n\n    // Disallow comparing against -0\n    \"no-compare-neg-zero\": 2,\n\n    // Warn against catch clause parameters from shadowing variables in the outer scope\n    \"no-catch-shadow\": 1,\n\n    // Disallow identifiers from shadowing restricted names\n    \"no-shadow-restricted-names\": 2,\n\n    // Enforce return statements in callbacks of array methods\n    \"callback-return\": 2,\n\n    // Require error handling in callbacks\n    \"handle-callback-err\": 2,\n\n    // Warn against string concatenation with __dirname and __filename\n    \"no-path-concat\": 1,\n\n    // Prefer using arrow functions for callbacks\n    \"prefer-arrow-callback\": 1,\n\n    // Return inside each then() to create readable and reusable Promise chains.\n    \"promise/always-return\": 2,\n\n    //Enforces the use of catch() on un-returned promises\n    \"promise/catch-or-return\": 2,\n\n    // Warn against nested then() or catch() statements\n    \"promise/no-nesting\": 1\n  }\n}\n"
  },
  {
    "path": "Node-1st-gen/quickstarts/uppercase-firestore/functions/.gitignore",
    "content": "node_modules/"
  },
  {
    "path": "Node-1st-gen/quickstarts/uppercase-firestore/functions/index.js",
    "content": "/**\n * Copyright 2017 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the 'License');\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an 'AS IS' BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\"use strict\";\n\n// [START all]\n// [START import]\n// The Cloud Functions for Firebase SDK to create Cloud Functions and set up triggers.\nconst functions = require('firebase-functions/v1');\n\n// The Firebase Admin SDK to access Firestore.\nconst admin = require(\"firebase-admin\");\nadmin.initializeApp();\n// [END import]\n\n// [START addMessage]\n// Take the text parameter passed to this HTTP endpoint and insert it into\n// Firestore under the path /messages/:documentId/original\n// [START addMessageTrigger]\nexports.addMessage = functions.https.onRequest(async (req, res) => {\n  // [END addMessageTrigger]\n  // Grab the text parameter.\n  const original = req.query.text;\n  // [START adminSdkAdd]\n  // Push the new message into Firestore using the Firebase Admin SDK.\n  const writeResult = await admin\n    .firestore()\n    .collection(\"messages\")\n    .add({ original: original });\n  // Send back a message that we've successfully written the message\n  res.json({ result: `Message with ID: ${writeResult.id} added.` });\n  // [END adminSdkAdd]\n});\n// [END addMessage]\n\n// [START makeUppercase]\n// Listens for new messages added to /messages/:documentId/original and creates an\n// uppercase version of the message to /messages/:documentId/uppercase\n// [START makeUppercaseTrigger]\nexports.makeUppercase = functions.firestore\n  .document(\"/messages/{documentId}\")\n  .onCreate((snap, context) => {\n    // [END makeUppercaseTrigger]\n    // [START makeUppercaseBody]\n    // Grab the current value of what was written to Firestore.\n    const original = snap.data().original;\n\n    // Access the parameter `{documentId}` with `context.params`\n    functions.logger.log(\"Uppercasing\", context.params.documentId, original);\n\n    const uppercase = original.toUpperCase();\n\n    // You must return a Promise when performing asynchronous tasks inside a Functions such as\n    // writing to Firestore.\n    // Setting an 'uppercase' field in Firestore document returns a Promise.\n    return snap.ref.set({ uppercase }, { merge: true });\n    // [END makeUppercaseBody]\n  });\n// [END makeUppercase]\n// [END all]\n"
  },
  {
    "path": "Node-1st-gen/quickstarts/uppercase-firestore/functions/package.json",
    "content": "{\n  \"name\": \"uppercase-firestore-quickstart-functions\",\n  \"description\": \"Uppercaser Firebase Functions Quickstart sample for Firestore\",\n  \"dependencies\": {\n    \"eslint-plugin-promise\": \"^7.2.1\",\n    \"firebase-admin\": \"^13.0.2\",\n    \"firebase-functions\": \"7.0.0\"\n  },\n  \"devDependencies\": {\n    \"chai\": \"^4.3.6\",\n    \"chai-as-promised\": \"^7.1.1\",\n    \"eslint\": \"^8.57.1\",\n    \"mocha\": \"^7.2.0\",\n    \"sinon\": \"^9.2.4\"\n  },\n  \"scripts\": {\n    \"lint\": \"./node_modules/.bin/eslint --max-warnings=0 .\",\n    \"serve\": \"firebase emulators:start --only functions\",\n    \"shell\": \"firebase functions:shell\",\n    \"start\": \"npm run shell\",\n    \"deploy\": \"firebase deploy --only functions\",\n    \"logs\": \"firebase functions:log\",\n    \"compile\": \"cp ../../../../tsconfig.template.json ./tsconfig-compile.json && tsc --project tsconfig-compile.json\"\n  },\n  \"engines\": {\n    \"node\": \"20\"\n  },\n  \"private\": true\n}\n"
  },
  {
    "path": "Node-1st-gen/quickstarts/uppercase-rtdb/README.md",
    "content": "# Firebase SDK for Cloud Functions Quickstart - Realtime Database\n\nThis quickstart demonstrates using **Firebase SDK for Cloud Functions** setup with a **Firebase database**.\n\n\n## Introduction\n\nThis sample app does two things:\n - Create messages in the Firebase Realtime Database using a simple HTTPS request which is handled by an HTTPS Firebase Function. Writing to the Realtime Database is done using the Firebase Admin SDK. \n - When a message gets added in the Realtime Database, a Firebase Function triggers and automatically makes these messages all uppercase.\n\n## Deploy and try out\n\nTo deploy and try out the sample:\n\n - Create a Firebase project on the [Firebase Console](https://console.firebase.google.com)\n - Install the required dependencies by running `npm install` in the `functions` directory\n - Deploy your project's code using `firebase deploy`\n - Create a message by opening the following URL in your browser: https://us-central1-[MY_PROJECT].cloudfunctions.net/addMessage?text=uppercaseme (Replace [MY_PROJECT] by your project ID and you can change the message \"uppercaseme\").\n\n The function executes and redirects the browser to the Firebase console at the database location where the text string was stored. You should see your text value displayed in the console and uppercase.\n\n## Run unit tests\n\nThe test folder has unit tests written with [`firebase-functions-test`](https://github.com/Firebase/firebase-functions-test). There are 2 sets of tests: online and offline.\n\nTo run the offline tests: run `npm test` inside the functions folder.\n\nTo run the online tests:\n - Replace the `projectConfig` variable in `test/test.online.js` with configuration values from your project.\n - Download a service account key by opening the [Service Accounts pane](https://console.cloud.google.com/iam-admin/serviceaccounts) of the Google Cloud Console.\n - Select the App Engine default service account, and use the options menu at right to select Create key.\n - When prompted, select JSON for the key type, and click Create.\n - Save the file in the test folder, and name it `service-account-key.json`\n - Run `npm run test-online` inside the functions folder.\n\n## Contributing\n\nWe'd love that you contribute to the project. Before doing so please read our [Contributor guide](../../CONTRIBUTING.md).\n\n\n## License\n\n© Google, 2016. Licensed under an [Apache-2](../../LICENSE) license.\n"
  },
  {
    "path": "Node-1st-gen/quickstarts/uppercase-rtdb/database.rules.json",
    "content": "{\n  \"rules\": {\n    \".read\": false,\n    \".write\": false\n  }\n}\n"
  },
  {
    "path": "Node-1st-gen/quickstarts/uppercase-rtdb/firebase.json",
    "content": "{\n  \"functions\": {\n    \"codebase\": \"uppercase-rtdb\"\n  },\n  \"rulesFile\": \"database.rules.json\",\n  \"emulators\": {\n    \"functions\": {\n      \"port\": 5001\n    },\n    \"database\": {\n      \"port\": 9000\n    },\n    \"ui\": {\n      \"enabled\": true\n    }\n  }\n}\n"
  },
  {
    "path": "Node-1st-gen/quickstarts/uppercase-rtdb/functions/.eslintrc.json",
    "content": "{\n  \"parserOptions\": {\n    // Required for certain syntax usages\n    \"ecmaVersion\": 2022\n  },\n  \"plugins\": [\n    \"promise\"\n  ],\n  \"extends\": \"eslint:recommended\",\n  \"rules\": {\n    // Removed rule \"disallow the use of console\" from recommended eslint rules\n    \"no-console\": \"off\",\n\n    // Removed rule \"disallow multiple spaces in regular expressions\" from recommended eslint rules\n    \"no-regex-spaces\": \"off\",\n\n    // Removed rule \"disallow the use of debugger\" from recommended eslint rules\n    \"no-debugger\": \"off\",\n\n    // Removed rule \"disallow unused variables\" from recommended eslint rules\n    \"no-unused-vars\": \"off\",\n\n    // Removed rule \"disallow mixed spaces and tabs for indentation\" from recommended eslint rules\n    \"no-mixed-spaces-and-tabs\": \"off\",\n\n    // Removed rule \"disallow the use of undeclared variables unless mentioned in /*global */ comments\" from recommended eslint rules\n    \"no-undef\": \"off\",\n\n    // Warn against template literal placeholder syntax in regular strings\n    \"no-template-curly-in-string\": 1,\n\n    // Warn if return statements do not either always or never specify values\n    \"consistent-return\": 1,\n\n    // Warn if no return statements in callbacks of array methods\n    \"array-callback-return\": 1,\n\n    // Requre the use of === and !==\n    \"eqeqeq\": 2,\n\n    // Disallow the use of alert, confirm, and prompt\n    \"no-alert\": 2,\n\n    // Disallow the use of arguments.caller or arguments.callee\n    \"no-caller\": 2,\n\n    // Disallow null comparisons without type-checking operators\n    \"no-eq-null\": 2,\n\n    // Disallow the use of eval()\n    \"no-eval\": 2,\n\n    // Warn against extending native types\n    \"no-extend-native\": 1,\n\n    // Warn against unnecessary calls to .bind()\n    \"no-extra-bind\": 1,\n\n    // Warn against unnecessary labels    \n    \"no-extra-label\": 1,\n\n    // Disallow leading or trailing decimal points in numeric literals\n    \"no-floating-decimal\": 2,\n\n    // Warn against shorthand type conversions\n    \"no-implicit-coercion\": 1,\n\n    // Warn against function declarations and expressions inside loop statements\n    \"no-loop-func\": 1,\n\n    // Disallow new operators with the Function object\n    \"no-new-func\": 2,\n\n    // Warn against new operators with the String, Number, and Boolean objects\n    \"no-new-wrappers\": 1,\n\n    // Disallow throwing literals as exceptions\n    \"no-throw-literal\": 2,\n\n    // Require using Error objects as Promise rejection reasons\n    \"prefer-promise-reject-errors\": 2,\n\n    // Enforce “for” loop update clause moving the counter in the right direction\n    \"for-direction\": 2,\n\n    // Enforce return statements in getters\n    \"getter-return\": 2,\n\n    // Disallow await inside of loops\n    \"no-await-in-loop\": 2,\n\n    // Disallow comparing against -0\n    \"no-compare-neg-zero\": 2,\n\n    // Warn against catch clause parameters from shadowing variables in the outer scope\n    \"no-catch-shadow\": 1,\n\n    // Disallow identifiers from shadowing restricted names\n    \"no-shadow-restricted-names\": 2,\n\n    // Enforce return statements in callbacks of array methods\n    \"callback-return\": 2,\n\n    // Require error handling in callbacks\n    \"handle-callback-err\": 2,\n\n    // Warn against string concatenation with __dirname and __filename\n    \"no-path-concat\": 1,\n\n    // Prefer using arrow functions for callbacks\n    \"prefer-arrow-callback\": 1,\n\n    // Return inside each then() to create readable and reusable Promise chains.\n    \"promise/always-return\": 2,\n\n    //Enforces the use of catch() on un-returned promises\n    \"promise/catch-or-return\": 2,\n\n    // Warn against nested then() or catch() statements\n    \"promise/no-nesting\": 1\n  }\n}\n"
  },
  {
    "path": "Node-1st-gen/quickstarts/uppercase-rtdb/functions/index.js",
    "content": "/**\n * Copyright 2016 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use strict';\n\n// [START all]\n// [START import]\n// The Cloud Functions for Firebase SDK to create Cloud Functions and setup triggers.\nconst functions = require('firebase-functions/v1');\n\n// The Firebase Admin SDK to access the Firebase Realtime Database.\nconst admin = require('firebase-admin');\nadmin.initializeApp();\n// [END import]\n\n// [START addMessage]\n// Take the text parameter passed to this HTTP endpoint and insert it into the\n// Realtime Database under the path /messages/:pushId/original\n// [START addMessageTrigger]\nexports.addMessage = functions.https.onRequest(async (req, res) => {\n// [END addMessageTrigger]\n  // Grab the text parameter.\n  const original = req.query.text;\n  // [START adminSdkPush]\n  // Push the new message into the Realtime Database using the Firebase Admin SDK.\n  const snapshot = await admin.database().ref('/messages').push({original: original});\n  // Redirect with 303 SEE OTHER to the URL of the pushed object in the Firebase console.\n  res.redirect(303, snapshot.ref.toString());\n  // [END adminSdkPush]\n});\n// [END addMessage]\n\n// [START makeUppercase]\n// Listens for new messages added to /messages/:pushId/original and creates an\n// uppercase version of the message to /messages/:pushId/uppercase\nexports.makeUppercase = functions.database.ref('/messages/{pushId}/original')\n    .onCreate((snapshot, context) => {\n      // Grab the current value of what was written to the Realtime Database.\n      const original = snapshot.val();\n      functions.logger.log('Uppercasing', context.params.pushId, original);\n      const uppercase = original.toUpperCase();\n      // You must return a Promise when performing asynchronous tasks inside a Functions such as\n      // writing to the Firebase Realtime Database.\n      // Setting an \"uppercase\" sibling in the Realtime Database returns a Promise.\n      return snapshot.ref.parent.child('uppercase').set(uppercase);\n    });\n// [END makeUppercase]\n// [END all]\n"
  },
  {
    "path": "Node-1st-gen/quickstarts/uppercase-rtdb/functions/package.json",
    "content": "{\n  \"name\": \"uppercase-quickstart-functions\",\n  \"description\": \"Uppercaser Firebase Functions Quickstart sample\",\n  \"dependencies\": {\n    \"eslint\": \"8\",\n    \"eslint-plugin-promise\": \"^7.2.1\",\n    \"firebase-admin\": \"^13.0.2\",\n    \"firebase-functions\": \"7.0.0\"\n  },\n  \"devDependencies\": {\n    \"@types/mocha\": \"^8.2.3\",\n    \"chai\": \"^4.3.6\",\n    \"chai-as-promised\": \"^7.1.1\",\n    \"firebase-functions-test\": \"3.4.0\",\n    \"mocha\": \"^7.2.0\",\n    \"sinon\": \"^9.2.4\"\n  },\n  \"scripts\": {\n    \"ci-test\": \"npm install && npm run test\",\n    \"test\": \"npm run test-offline\",\n    \"test-online\": \"mocha --reporter spec test/test.online.js --exit\",\n    \"test-offline\": \"mocha --reporter spec test/test.offline.js --exit\",\n    \"serve\": \"firebase emulators:start --only functions\",\n    \"shell\": \"firebase functions:shell\",\n    \"start\": \"npm run shell\",\n    \"deploy\": \"firebase deploy --only functions\",\n    \"logs\": \"firebase functions:log\",\n    \"compile\": \"cp ../../../../tsconfig.template.json ./tsconfig-compile.json && tsc --project tsconfig-compile.json\"\n  },\n  \"engines\": {\n    \"node\": \"20\"\n  },\n  \"private\": true\n}\n"
  },
  {
    "path": "Node-1st-gen/quickstarts/uppercase-rtdb/functions/test/test.offline.js",
    "content": "/**\n * Copyright 2016 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// You can run these unit tests by running \"npm test\" inside the uppercase/functions directory.\n// Visit https://firebase.google.com/docs/functions/unit-testing to learn more\n// about using the `firebase-functions-test` SDK.\n\n// Chai is a commonly used library for creating unit test suites. It is easily extended with plugins.\nconst chai = require('chai');\nconst assert = chai.assert;\n\n// Sinon is a library used for mocking or verifying function calls in JavaScript.\nconst sinon = require('sinon');\n\n// Require firebase-admin so we can stub out some of its methods.\nconst admin = require('firebase-admin');\n// Require and initialize firebase-functions-test. Since we are not passing in any parameters, it will\n// be initialized in an \"offline mode\", which means we have to stub out all the methods that interact\n// with Firebase services.\nconst test = require('firebase-functions-test')();\n\ndescribe('Cloud Functions', () => {\n  let myFunctions, adminInitStub;\n\n  before(() => {\n    // [START stubAdminInit]\n    // If index.js calls admin.initializeApp at the top of the file,\n    // we need to stub it out before requiring index.js. This is because the\n    // functions will be executed as a part of the require process.\n    // Here we stub admin.initializeApp to be a dummy function that doesn't do anything.\n    adminInitStub = sinon.stub(admin, 'initializeApp');\n    // Now we can require index.js and save the exports inside a namespace called myFunctions.\n    myFunctions = require('../index');\n    // [END stubAdminInit]\n  });\n\n  after(() => {\n    // Restore admin.initializeApp() to its original method.\n    adminInitStub.restore();\n    // Do other cleanup tasks.\n    test.cleanup();\n  });\n\n  describe('makeUpperCase', () => {\n    // Test Case: setting messages/{pushId}/original to 'input' should cause 'INPUT' to be written to\n    // messages/{pushId}/uppercase\n    it('should upper case input and write it to /uppercase', () => {\n      // [START assertOffline]\n      const childParam = 'uppercase';\n      const setParam = 'INPUT';\n      // Stubs are objects that fake and/or record function calls.\n      // These are excellent for verifying that functions have been called and to validate the\n      // parameters passed to those functions.\n      const childStub = sinon.stub();\n      const setStub = sinon.stub();\n      // [START fakeSnap]\n      // The following lines creates a fake snapshot, 'snap', which returns 'input' when snap.val() is called,\n      // and returns true when snap.ref.parent.child('uppercase').set('INPUT') is called.\n      const snap = {\n        val: () => 'input',\n        ref: {\n          parent: {\n            child: childStub,\n          }\n        }\n      };\n      childStub.withArgs(childParam).returns({ set: setStub });\n      setStub.withArgs(setParam).returns(true);\n      // [END fakeSnap]\n      // Wrap the makeUppercase function.\n      const wrapped = test.wrap(myFunctions.makeUppercase);\n      // Since we've stubbed snap.ref.parent.child(childParam).set(setParam) to return true if it was\n      // called with the parameters we expect, we assert that it indeed returned true.\n      return wrapped(snap).then(makeUppercaseResult => {\n        return assert.equal(makeUppercaseResult, true);\n      });\n      // [END assertOffline]\n    })\n  });\n\n  describe('addMessage', () => {\n    let oldDatabase;\n    before(() => {\n      // Save the old database method so it can be restored after the test.\n      oldDatabase = admin.database;\n    });\n\n    after(() => {\n      // Restoring admin.database() to the original method.\n      admin.database = oldDatabase;\n    });\n\n    it('should return a 303 redirect', (done) => {\n      const refParam = '/messages';\n      const pushParam = { original: 'input' };\n      const databaseStub = sinon.stub();\n      const refStub = sinon.stub();\n      const pushStub = sinon.stub();\n\n      // The following lines override the behavior of admin.database().ref('/messages')\n      // .push({ original: 'input' }) to return a promise that resolves with { ref: 'new_ref' }.\n      // This mimics the behavior of a push to the database, which returns an object containing a\n      // ref property representing the URL of the newly pushed item.\n\n      Object.defineProperty(admin, 'database', { get: () => databaseStub });\n      databaseStub.returns({ ref: refStub });\n      refStub.withArgs(refParam).returns({ push: pushStub });\n      pushStub.withArgs(pushParam).returns(Promise.resolve({ ref: 'new_ref' }));\n\n      // [START assertHTTP]\n      // A fake request object, with req.query.text set to 'input'\n      const req = { query: {text: 'input'} };\n      // A fake response object, with a stubbed redirect function which asserts that it is called\n      // with parameters 303, 'new_ref'.\n      const res = {\n        redirect: (code, url) => {\n          assert.equal(code, 303);\n          assert.equal(url, 'new_ref');\n          done();\n        }\n      };\n\n      // Invoke addMessage with our fake request and response objects. This will cause the\n      // assertions in the response object to be evaluated.\n      myFunctions.addMessage(req, res);\n      // [END assertHTTP]\n    });\n  });\n})\n"
  },
  {
    "path": "Node-1st-gen/quickstarts/uppercase-rtdb/functions/test/test.online.js",
    "content": "/**\n * Copyright 2016 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// Follow the instructions in uppercase/README.md for running these tests.\n// Visit https://firebase.google.com/docs/functions/unit-testing to learn more\n// about using the `firebase-functions-test` SDK.\n\n// Chai is a commonly used library for creating unit test suites. It is easily extended with plugins.\nconst chai = require('chai');\nconst assert = chai.assert;\n\n// Sinon is a library used for mocking or verifying function calls in JavaScript.\nconst sinon = require('sinon');\n\nconst admin = require('firebase-admin');\n// Require and initialize firebase-functions-test in \"online mode\" with your project's\n// credentials and service account key.\nconst projectConfig = {\n  projectId: 'my-project',\n  databaseURL: 'https://my-project.firebaseio.com'\n};\nconst test = require('firebase-functions-test')(projectConfig, './service-account-key.json');\n\ndescribe('Cloud Functions', () => {\n  let myFunctions;\n\n  before(() => {\n    // Require index.js and save the exports inside a namespace called myFunctions.\n    // This includes our cloud functions, which can now be accessed at myFunctions.makeUppercase\n    // and myFunctions.addMessage\n    myFunctions = require('../index');\n  });\n\n  after(() => {\n    // Do cleanup tasks.\n    test.cleanup();\n    // Reset the database.\n    admin.database().ref('messages').remove();\n  });\n\n  describe('makeUpperCase', () => {\n    // Test Case: setting messages/11111/original to 'input' should cause 'INPUT' to be written to\n    // messages/11111/uppercase\n    it('should upper case input and write it to /uppercase', () => {\n      // [START assertOnline]\n      // Create a DataSnapshot with the value 'input' and the reference path 'messages/11111/original'.\n      const snap = test.database.makeDataSnapshot('input', 'messages/11111/original');\n\n      // Wrap the makeUppercase function\n      const wrapped = test.wrap(myFunctions.makeUppercase);\n      // Call the wrapped function with the snapshot you constructed.\n      return wrapped(snap).then(() => {\n        // Read the value of the data at messages/11111/uppercase. Because `admin.initializeApp()` is\n        // called in functions/index.js, there's already a Firebase app initialized. Otherwise, add\n        // `admin.initializeApp()` before this line.\n        return admin.database().ref('messages/11111/uppercase').once('value').then((createdSnap) => {\n          // Assert that the value is the uppercased version of our input.\n          assert.equal(createdSnap.val(), 'INPUT');\n        });\n      });\n      // [END assertOnline]\n    })\n  });\n\n  describe('addMessage', () => {\n    it('should return a 303 redirect', (done) => {\n      // A fake request object, with req.query.text set to 'input'\n      const req = { query: {text: 'input'} };\n      // A fake response object, with a stubbed redirect function which does some assertions\n      const res = {\n        redirect: (code, url) => {\n          // Assert code is 303\n          assert.equal(code, 303);\n          // If the database push is successful, then the URL sent back will have the following format:\n          const expectedRef = new RegExp(projectConfig.databaseURL + '/messages/');\n          assert.isTrue(expectedRef.test(url));\n          done();\n        }\n      };\n\n      // Invoke addMessage with our fake request and response objects. This will cause the\n      // assertions in the response object to be evaluated.\n      myFunctions.addMessage(req, res);\n    });\n  });\n})\n"
  },
  {
    "path": "Node-1st-gen/remote-config-diff/README.md",
    "content": "## Get the diff between the previous and current Remote Config templates.\n\nWith the Firebase Remote Config Cloud Function trigger you can take action when your template has been updated.\n\nIn this sample the diff between the previous and current templates is logged.\n\nSee [eBay's](https://github.com/eBay/firebase-remote-config-monitor) real world implementation.\n\nSee the [docs](https://firebase.google.com/docs/functions/rc-events) for more on Remote Config Triggers.\n"
  },
  {
    "path": "Node-1st-gen/remote-config-diff/firebase.json",
    "content": "{\n  \"functions\": {\n    \"codebase\": \"remote-config-diff\",\n    \"predeploy\": [\n      \"npm --prefix \\\"$RESOURCE_DIR\\\" run lint\"\n    ]\n  }\n}\n"
  },
  {
    "path": "Node-1st-gen/remote-config-diff/functions/.eslintrc.json",
    "content": "{\n  \"parserOptions\": {\n    // Required for certain syntax usages\n    \"ecmaVersion\": 2022\n  },\n  \"plugins\": [\n    \"promise\"\n  ],\n  \"extends\": \"eslint:recommended\",\n  \"rules\": {\n    // Removed rule \"disallow the use of console\" from recommended eslint rules\n    \"no-console\": \"off\",\n\n    // Removed rule \"disallow multiple spaces in regular expressions\" from recommended eslint rules\n    \"no-regex-spaces\": \"off\",\n\n    // Removed rule \"disallow the use of debugger\" from recommended eslint rules\n    \"no-debugger\": \"off\",\n\n    // Removed rule \"disallow unused variables\" from recommended eslint rules\n    \"no-unused-vars\": \"off\",\n\n    // Removed rule \"disallow mixed spaces and tabs for indentation\" from recommended eslint rules\n    \"no-mixed-spaces-and-tabs\": \"off\",\n\n    // Removed rule \"disallow the use of undeclared variables unless mentioned in /*global */ comments\" from recommended eslint rules\n    \"no-undef\": \"off\",\n\n    // Warn against template literal placeholder syntax in regular strings\n    \"no-template-curly-in-string\": 1,\n\n    // Warn if return statements do not either always or never specify values\n    \"consistent-return\": 1,\n\n    // Warn if no return statements in callbacks of array methods\n    \"array-callback-return\": 1,\n\n    // Require the use of === and !==\n    \"eqeqeq\": 2,\n\n    // Disallow the use of alert, confirm, and prompt\n    \"no-alert\": 2,\n\n    // Disallow the use of arguments.caller or arguments.callee\n    \"no-caller\": 2,\n\n    // Disallow null comparisons without type-checking operators\n    \"no-eq-null\": 2,\n\n    // Disallow the use of eval()\n    \"no-eval\": 2,\n\n    // Warn against extending native types\n    \"no-extend-native\": 1,\n\n    // Warn against unnecessary calls to .bind()\n    \"no-extra-bind\": 1,\n\n    // Warn against unnecessary labels    \n    \"no-extra-label\": 1,\n\n    // Disallow leading or trailing decimal points in numeric literals\n    \"no-floating-decimal\": 2,\n\n    // Warn against shorthand type conversions\n    \"no-implicit-coercion\": 1,\n\n    // Warn against function declarations and expressions inside loop statements\n    \"no-loop-func\": 1,\n\n    // Disallow new operators with the Function object\n    \"no-new-func\": 2,\n\n    // Warn against new operators with the String, Number, and Boolean objects\n    \"no-new-wrappers\": 1,\n\n    // Disallow throwing literals as exceptions\n    \"no-throw-literal\": 2,\n\n    // Require using Error objects as Promise rejection reasons\n    \"prefer-promise-reject-errors\": 2,\n\n    // Enforce “for” loop update clause moving the counter in the right direction\n    \"for-direction\": 2,\n\n    // Enforce return statements in getters\n    \"getter-return\": 2,\n\n    // Disallow await inside of loops\n    \"no-await-in-loop\": 2,\n\n    // Disallow comparing against -0\n    \"no-compare-neg-zero\": 2,\n\n    // Warn against catch clause parameters from shadowing variables in the outer scope\n    \"no-catch-shadow\": 1,\n\n    // Disallow identifiers from shadowing restricted names\n    \"no-shadow-restricted-names\": 2,\n\n    // Enforce return statements in callbacks of array methods\n    \"callback-return\": 2,\n\n    // Require error handling in callbacks\n    \"handle-callback-err\": 2,\n\n    // Warn against string concatenation with __dirname and __filename\n    \"no-path-concat\": 1,\n\n    // Prefer using arrow functions for callbacks\n    \"prefer-arrow-callback\": 1,\n\n    // Return inside each then() to create readable and reusable Promise chains.\n    // Forces developers to return console logs and http calls in promises. \n    \"promise/always-return\": 2,\n\n    //Enforces the use of catch() on un-returned promises\n    \"promise/catch-or-return\": 2,\n\n    // Warn against nested then() or catch() statements\n    \"promise/no-nesting\": 1\n  }\n}\n"
  },
  {
    "path": "Node-1st-gen/remote-config-diff/functions/index.js",
    "content": "/**\n * Copyright 2023 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nconst functions = require('firebase-functions/v1');\nconst admin = require('firebase-admin');\nconst jsonDiff = require('json-diff');\n\nadmin.initializeApp();\n\n// [START remote_config_function]\nexports.showConfigDiff = functions.remoteConfig.onUpdate(versionMetadata => {\n  return admin.credential.applicationDefault().getAccessToken()\n    .then(accessTokenObj => {\n      return accessTokenObj.access_token;\n    })\n    .then(accessToken => {\n      const currentVersion = versionMetadata.versionNumber;\n      const templatePromises = [];\n      templatePromises.push(getTemplate(currentVersion, accessToken));\n      templatePromises.push(getTemplate(currentVersion - 1, accessToken));\n\n      return Promise.all(templatePromises);\n    })\n    .then(results => {\n      const currentTemplate = results[0];\n      const previousTemplate = results[1];\n\n      const diff = jsonDiff.diffString(previousTemplate, currentTemplate);\n\n      functions.logger.log(diff);\n\n      return null;\n    }).catch(error => {\n      functions.logger.error(error);\n      return null;\n    });\n});\n// [END remote_config_function]\n\nasync function getTemplate(version, accessToken) {\n  const params = new URLSearchParams();\n  params.append(\"versionNumber\", version);\n  const response = await fetch(\n    \"https://firebaseremoteconfig.googleapis.com/v1/projects/remote-config-function/remoteConfig\",\n    {\n      method: \"POST\",\n      body: params,\n      headers: { Authorization: \"Bearer \" + accessToken },\n    }\n  );\n  return response.json();\n}\n\n"
  },
  {
    "path": "Node-1st-gen/remote-config-diff/functions/package.json",
    "content": "{\n  \"name\": \"functions\",\n  \"description\": \"Cloud Functions for Firebase\",\n  \"scripts\": {\n    \"lint\": \"eslint .\",\n    \"serve\": \"firebase emulators:start --only functions\",\n    \"shell\": \"firebase functions:shell\",\n    \"start\": \"npm run shell\",\n    \"deploy\": \"firebase deploy --only functions\",\n    \"logs\": \"firebase functions:log\",\n    \"compile\": \"cp ../../../tsconfig.template.json ./tsconfig-compile.json && tsc --project tsconfig-compile.json\"\n  },\n  \"dependencies\": {\n    \"eslint-plugin-promise\": \"^7.2.1\",\n    \"firebase-admin\": \"^13.0.2\",\n    \"firebase-functions\": \"7.0.0\",\n    \"json-diff\": \"^1.0.3\",\n    \"request\": \"^2.88.2\",\n    \"request-promise\": \"^4.2.5\"\n  },\n  \"devDependencies\": {\n    \"eslint\": \"^8.57.1\"\n  },\n  \"private\": true\n}\n"
  },
  {
    "path": "Node-1st-gen/spotify-auth/README.md",
    "content": "# Use Spotify Sign In with Firebase\n\nThis sample shows how to authenticate using Spotify Sign-In on Firebase. In this sample we use OAuth 2.0 based authentication to get Spotify user information then create a Firebase Custom Token (using the Spotify user ID).\n\n\n## Setup the sample\n\nCreate and setup the Firebase project:\n 1. Create a Firebase project using the [Firebase Developer Console](https://console.firebase.google.com).\n 1. Enable Billing on your Firebase project by switching to the **Blaze** plan, this is currently needed to be able to perform HTTP requests to external services from a Cloud Function. See the [pricing](https://firebase.google.com/pricing/) page for more details.\n\nCreate and provide a Service Account's credentials:\n 1. Create a Service Accounts file as described in the [Server SDK setup instructions](https://firebase.google.com/docs/server/setup#add_firebase_to_your_app).\n 1. Save the Service Account credential file as `./functions/service-account.json`\n\nCreate and setup your Spotify app:\n 1. Create a Spotify app in the [Spotify Developers website](https://developer.spotify.com/my-applications/).\n 1. Add the URL `https://<application-id>.firebaseapp.com/popup.html` to the\n    **Redirect URIs** of your Spotify app.\n 1. Copy the **Client ID** and **Client Secret** of your Spotify app and use them to set the `spotify.client_id` and `spotify.client_secret` Google Cloud environment variables. For this use:\n\n    ```bash\n    firebase functions:secrets:set SPOTIFY_CLIENT_ID\n    firebase functions:secrets:set SPOTIFY_CLIENT_SECRET\n    ```\n\n > Make sure the Spotify Client Secret is always kept secret. For instance do not save this in your version control system.\n\nDeploy your project:\n 1. Run `firebase use --add` and choose your Firebase project. This will configure the Firebase CLI to use the correct project locally.\n 1. Run `firebase deploy` to effectively deploy the sample. The first time the Functions are deployed the process can take several minutes.\n\n\n## Run the sample\n\nOpen the sample's website by using `firebase open hosting:site` or directly accessing `https://<project-id>.firebaseapp.com/`.\n\nClick on the **Sign in with Spotify** button and a popup window will appear that will show the Spotify authentication consent screen. Sign In and/or authorize the authentication request.\n\nThe website should display your display name, email and profile pic from Spotify. At this point you are authenticated in Firebase and can use the database/hosting etc...\n\n## Workflow and design\n\nWhen clicking the **Sign in with Spotify** button a popup is shown which redirects users to the `redirect` Function URL.\n\nThe `redirect` Function then redirects the user to the Spotify OAuth 2.0 consent screen where (the first time only) the user will have to grant approval. Also the `state` cookie is set on the client with the value of the `state` URL query parameter to check against later on.\n\nAfter the user has granted approval he is redirected back to the `./popup.html` page along with an OAuth 2.0 Auth Code as a URL parameter. This Auth code is then sent to the `token` Function using a JSONP Request. The `token` function then:\n - Checks that the value of the `state` URL query parameter is the same as the one in the `state` cookie.\n - Exchanges the auth code for an access token using the Spotify app credentials.\n - Fetches the user identity using the Spotify API.\n - Mints a Custom Auth token (which is why we need Service Accounts Credentials).\n - Returns the Custom Auth Token, email, photo URL, user display name and Spotify access token to the `./popup.html` page.\n\n  The `./popup.html` receives the Custom Auth Token and other data back from the AJAX request to the `token` Function and uses it to update the user's profile, saves the access token to the database, authenticate the user in Firebase and then close the popup.\n At this point the main page will detect the sign-in through the Firebase Auth State observer and display the signed-In user information.\n"
  },
  {
    "path": "Node-1st-gen/spotify-auth/database.rules.json",
    "content": "{\n  \"rules\": {\n    \"spotifyAccessToken\": {\n      \"$uid\": {\n        \".read\": \"auth.uid === $uid\",\n        \".write\": \"auth.uid === $uid\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "Node-1st-gen/spotify-auth/firebase.json",
    "content": "{\n  \"functions\": {\n    \"codebase\": \"spotify-auth\"\n  },\n  \"database\": {\n    \"rules\": \"database.rules.json\"\n  },\n  \"hosting\": {\n    \"public\": \"public\"\n  }\n}\n"
  },
  {
    "path": "Node-1st-gen/spotify-auth/functions/.eslintrc.json",
    "content": "{\n  \"parserOptions\": {\n    // Required for certain syntax usages\n    \"ecmaVersion\": 2022\n  },\n  \"plugins\": [\n    \"promise\"\n  ],\n  \"extends\": \"eslint:recommended\",\n  \"rules\": {\n    // Removed rule \"disallow the use of console\" from recommended eslint rules\n    \"no-console\": \"off\",\n\n    // Removed rule \"disallow multiple spaces in regular expressions\" from recommended eslint rules\n    \"no-regex-spaces\": \"off\",\n\n    // Removed rule \"disallow the use of debugger\" from recommended eslint rules\n    \"no-debugger\": \"off\",\n\n    // Removed rule \"disallow unused variables\" from recommended eslint rules\n    \"no-unused-vars\": \"off\",\n\n    // Removed rule \"disallow mixed spaces and tabs for indentation\" from recommended eslint rules\n    \"no-mixed-spaces-and-tabs\": \"off\",\n\n    // Removed rule \"disallow the use of undeclared variables unless mentioned in /*global */ comments\" from recommended eslint rules\n    \"no-undef\": \"off\",\n\n    // Warn against template literal placeholder syntax in regular strings\n    \"no-template-curly-in-string\": 1,\n\n    // Warn if return statements do not either always or never specify values\n    \"consistent-return\": 1,\n\n    // Warn if no return statements in callbacks of array methods\n    \"array-callback-return\": 1,\n\n    // Requre the use of === and !==\n    \"eqeqeq\": 2,\n\n    // Disallow the use of alert, confirm, and prompt\n    \"no-alert\": 2,\n\n    // Disallow the use of arguments.caller or arguments.callee\n    \"no-caller\": 2,\n\n    // Disallow null comparisons without type-checking operators\n    \"no-eq-null\": 2,\n\n    // Disallow the use of eval()\n    \"no-eval\": 2,\n\n    // Warn against extending native types\n    \"no-extend-native\": 1,\n\n    // Warn against unnecessary calls to .bind()\n    \"no-extra-bind\": 1,\n\n    // Warn against unnecessary labels    \n    \"no-extra-label\": 1,\n\n    // Disallow leading or trailing decimal points in numeric literals\n    \"no-floating-decimal\": 2,\n\n    // Warn against shorthand type conversions\n    \"no-implicit-coercion\": 1,\n\n    // Warn against function declarations and expressions inside loop statements\n    \"no-loop-func\": 1,\n\n    // Disallow new operators with the Function object\n    \"no-new-func\": 2,\n\n    // Warn against new operators with the String, Number, and Boolean objects\n    \"no-new-wrappers\": 1,\n\n    // Disallow throwing literals as exceptions\n    \"no-throw-literal\": 2,\n\n    // Require using Error objects as Promise rejection reasons\n    \"prefer-promise-reject-errors\": 2,\n\n    // Enforce “for” loop update clause moving the counter in the right direction\n    \"for-direction\": 2,\n\n    // Enforce return statements in getters\n    \"getter-return\": 2,\n\n    // Disallow await inside of loops\n    \"no-await-in-loop\": 2,\n\n    // Disallow comparing against -0\n    \"no-compare-neg-zero\": 2,\n\n    // Warn against catch clause parameters from shadowing variables in the outer scope\n    \"no-catch-shadow\": 1,\n\n    // Disallow identifiers from shadowing restricted names\n    \"no-shadow-restricted-names\": 2,\n\n    // Enforce return statements in callbacks of array methods\n    \"callback-return\": 2,\n\n    // Require error handling in callbacks\n    \"handle-callback-err\": 2,\n\n    // Warn against string concatenation with __dirname and __filename\n    \"no-path-concat\": 1,\n\n    // Prefer using arrow functions for callbacks\n    \"prefer-arrow-callback\": 1,\n\n    // Return inside each then() to create readable and reusable Promise chains.\n    \"promise/always-return\": 2,\n\n    //Enforces the use of catch() on un-returned promises\n    \"promise/catch-or-return\": 2,\n\n    // Warn against nested then() or catch() statements\n    \"promise/no-nesting\": 1\n  }\n}\n"
  },
  {
    "path": "Node-1st-gen/spotify-auth/functions/index.js",
    "content": "/**\n * Copyright 2016 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use strict';\n\nconst functions = require('firebase-functions/v1');\nconst {onInit} = require('firebase-functions/v1/init');\nconst {defineSecret} = require('firebase-functions/params');\nconst cookieParser = require('cookie-parser');\nconst crypto = require('node:crypto');\n\n// Firebase Setup\nconst admin = require('firebase-admin');\n// @ts-ignore\nconst serviceAccount = require('./service-account.json');\nadmin.initializeApp({\n  credential: admin.credential.cert(serviceAccount),\n  databaseURL: `https://${process.env.GCLOUD_PROJECT}.firebaseio.com`,\n});\n\nconst spotifyClientId = defineSecret('SPOTIFY_CLIENT_ID');\nconst spotifyClientSecret = defineSecret('SPOTIFY_CLIENT_SECRET');\n\n// Spotify OAuth 2 setup\n// TODO: Configure the `spotifyClientId` and `spotifyClientSecret` secrets.\nconst SpotifyWebApi = require('spotify-web-api-node');\n\nlet Spotify;\nonInit(() => {\n  Spotify = new SpotifyWebApi({\n    clientId: spotifyClientId.value(),\n    clientSecret: spotifyClientSecret.value(),\n    redirectUri: `https://${process.env.GCLOUD_PROJECT}.firebaseapp.com/popup.html`,\n  });\n});\n\n// Scopes to request.\nconst OAUTH_SCOPES = ['user-read-email'];\n\n/**\n * Redirects the User to the Spotify authentication consent screen. Also the 'state' cookie is set for later state\n * verification.\n */\nexports.redirect = functions.runWith({secrets: [spotifyClientId, spotifyClientSecret]}).https.onRequest((req, res) => {\n  cookieParser()(req, res, () => {\n    const state = req.cookies.state || crypto.randomBytes(20).toString('hex');\n    functions.logger.log('Setting verification state:', state);\n    res.cookie('state', state.toString(), {maxAge: 3600000, secure: true, httpOnly: true});\n    const authorizeURL = Spotify.createAuthorizeURL(OAUTH_SCOPES, state.toString());\n    res.redirect(authorizeURL);\n  });\n});\n\n/**\n * Exchanges a given Spotify auth code passed in the 'code' URL query parameter for a Firebase auth token.\n * The request also needs to specify a 'state' query parameter which will be checked against the 'state' cookie.\n * The Firebase custom auth token is sent back in a JSONP callback function with function name defined by the\n * 'callback' query parameter.\n */\nexports.token = functions.runWith({secrets: [spotifyClientId, spotifyClientSecret]}).https.onRequest((req, res) => {\n  try {\n    cookieParser()(req, res, () => {\n      functions.logger.log('Received verification state:', req.cookies.state);\n      functions.logger.log('Received state:', req.query.state);\n      if (!req.cookies.state) {\n        throw new Error('State cookie not set or expired. Maybe you took too long to authorize. Please try again.');\n      } else if (req.cookies.state !== req.query.state) {\n        throw new Error('State validation failed');\n      }\n      functions.logger.log('Received auth code:', req.query.code);\n      Spotify.authorizationCodeGrant(req.query.code, (error, data) => {\n        if (error) {\n          throw error;\n        }\n        functions.logger.log(\n          'Received Access Token:',\n          data.body['access_token']\n        );\n        Spotify.setAccessToken(data.body['access_token']);\n\n        Spotify.getMe(async (error, userResults) => {\n          if (error) {\n            throw error;\n          }\n          functions.logger.log(\n            'Auth code exchange result received:',\n            userResults\n          );\n          // We have a Spotify access token and the user identity now.\n          const accessToken = data.body['access_token'];\n          const spotifyUserID = userResults.body['id'];\n          const profilePic = userResults.body['images'][0]['url'];\n          const userName = userResults.body['display_name'];\n          const email = userResults.body['email'];\n\n          // Create a Firebase account and get the Custom Auth Token.\n          const firebaseToken = await createFirebaseAccount(spotifyUserID, userName, profilePic, email, accessToken);\n          // Serve an HTML page that signs the user in and updates the user profile.\n          res.jsonp({token: firebaseToken});\n        });\n      });\n    });\n  } catch (error) {\n    res.jsonp({error: error.toString()});\n  }\n  return null;\n});\n\n/**\n * Creates a Firebase account with the given user profile and returns a custom auth token allowing\n * signing-in this account.\n * Also saves the accessToken to the datastore at /spotifyAccessToken/$uid\n *\n * @returns {Promise<string>} The Firebase custom auth token in a promise.\n */\nasync function createFirebaseAccount(spotifyID, displayName, photoURL, email, accessToken) {\n  // The UID we'll assign to the user.\n  const uid = `spotify:${spotifyID}`;\n\n  // Save the access token to the Firebase Realtime Database.\n  const databaseTask = admin.database().ref(`/spotifyAccessToken/${uid}`).set(accessToken);\n\n  // Create or update the user account.\n  const userCreationTask = admin.auth().updateUser(uid, {\n    displayName: displayName,\n    photoURL: photoURL,\n    email: email,\n    emailVerified: true,\n  }).catch((error) => {\n    // If user does not exists we create it.\n    if (error.code === 'auth/user-not-found') {\n      return admin.auth().createUser({\n        uid: uid,\n        displayName: displayName,\n        photoURL: photoURL,\n        email: email,\n        emailVerified: true,\n      });\n    }\n    throw error;\n  });\n\n  // Wait for all async tasks to complete, then generate and return a custom auth token.\n  await Promise.all([userCreationTask, databaseTask]);\n  // Create a Firebase custom auth token.\n  const token = await admin.auth().createCustomToken(uid);\n  functions.logger.log(\n    'Created Custom token for UID \"',\n    uid,\n    '\" Token:',\n    token\n  );\n  return token;\n}\n"
  },
  {
    "path": "Node-1st-gen/spotify-auth/functions/package.json",
    "content": "{\n  \"name\": \"spotify-auth-functions\",\n  \"description\": \"Authenticate with Spotify Firebase Functions sample\",\n  \"dependencies\": {\n    \"cookie-parser\": \"^1.4.6\",\n    \"eslint-plugin-promise\": \"^7.2.1\",\n    \"firebase-admin\": \"^13.0.2\",\n    \"firebase-functions\": \"7.0.0\",\n    \"spotify-web-api-node\": \"^4.0.0\"\n  },\n  \"devDependencies\": {\n    \"eslint\": \"^8.57.1\"\n  },\n  \"scripts\": {\n    \"lint\": \"./node_modules/.bin/eslint --max-warnings=0 .\",\n    \"serve\": \"firebase emulators:start --only functions\",\n    \"shell\": \"firebase functions:shell\",\n    \"start\": \"npm run shell\",\n    \"deploy\": \"firebase deploy --only functions\",\n    \"logs\": \"firebase functions:log\",\n    \"compile\": \"cp ../../../tsconfig.template.json ./tsconfig-compile.json && tsc --project tsconfig-compile.json\"\n  },\n  \"engines\": {\n    \"node\": \"20\"\n  },\n  \"private\": true\n}\n"
  },
  {
    "path": "Node-1st-gen/spotify-auth/main.css",
    "content": "/**\n * Copyright 2023 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n\n"
  },
  {
    "path": "Node-1st-gen/spotify-auth/public/index.html",
    "content": "<!doctype html>\n<!--\n  Copyright 2016 Google Inc. All rights reserved.\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n      https://www.apache.org/licenses/LICENSE-2.0\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License\n-->\n<html lang=\"en\">\n<head>\n  <meta charset=\"utf-8\">\n  <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n  <meta name=\"description\" content=\"Demonstrates of to authorize Firebase with Spotify auth using Firebase Functions\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n  <title>Firebase Functions demo to Sign In with Spotify</title>\n\n  <!-- Material Design Lite -->\n  <link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/icon?family=Material+Icons\">\n  <link rel=\"stylesheet\" href=\"https://code.getmdl.io/1.1.3/material.blue_grey-orange.min.css\">\n  <script defer src=\"https://code.getmdl.io/1.1.3/material.min.js\"></script>\n\n  <link rel=\"stylesheet\" href=\"main.css\">\n</head>\n<body>\n<div class=\"demo-layout mdl-layout mdl-js-layout mdl-layout--fixed-header\">\n\n  <!-- Header section containing title -->\n  <header class=\"mdl-layout__header mdl-color-text--white mdl-color--light-blue-700\">\n    <div class=\"mdl-cell mdl-cell--12-col mdl-cell--12-col-tablet mdl-grid\">\n      <div class=\"mdl-layout__header-row mdl-cell mdl-cell--12-col mdl-cell--12-col-tablet mdl-cell--8-col-desktop\">\n        <h3>Sign in with Spotify demo</h3>\n      </div>\n    </div>\n  </header>\n  <main class=\"mdl-layout__content mdl-color--grey-100\">\n    <div class=\"mdl-cell--12-col mdl-cell--12-col-tablet mdl-grid\">\n\n      <!-- Card containing the sign-in UI -->\n      <div id=\"demo-signed-out-card\" class=\"mdl-card mdl-shadow--2dp mdl-cell\">\n        <div class=\"mdl-card__supporting-text mdl-color-text--grey-600\">\n          <p>\n            This web application demonstrates how you can Sign In with Spotify to Firebase Authentication.\n            <strong>Now sign in!</strong>\n          </p>\n          <button id=\"demo-sign-in-button\" class=\"mdl-color-text--grey-700 mdl-button--raised mdl-button mdl-js-button mdl-js-ripple-effect\"></button>\n        </div>\n      </div>\n\n      <!-- Card containing the signed-in UI -->\n      <div id=\"demo-signed-in-card\" class=\"mdl-card mdl-shadow--2dp mdl-cell\">\n        <div class=\"mdl-card__supporting-text mdl-color-text--grey-600\">\n          <p>\n            Welcome <span id=\"demo-name-container\"></span><br>\n            Your email is: <span id=\"demo-email-container\"></span><br>\n            Your Firebase User ID is: <span id=\"demo-uid-container\"></span><br>\n            Your profile picture: <img id=\"demo-profile-pic\">\n          </p>\n          <button id=\"demo-sign-out-button\" class=\"mdl-color-text--grey-700 mdl-button--raised mdl-button mdl-js-button mdl-js-ripple-effect\">Sign out</button>\n          <button id=\"demo-delete-button\" class=\"mdl-color-text--grey-700 mdl-button--raised mdl-button mdl-js-button mdl-js-ripple-effect\">Delete account</button>\n        </div>\n      </div>\n    </div>\n  </main>\n</div>\n\n<!-- Firebase -->\n<script src=\"/__/firebase/10.0.0/firebase-app-compat.js\"></script>\n<script src=\"/__/firebase/10.0.0/firebase-auth-compat.js\"></script>\n<script src=\"/__/firebase/init.js\"></script>\n\n<script src=\"main.js\"></script>\n</body>\n</html>\n"
  },
  {
    "path": "Node-1st-gen/spotify-auth/public/main.css",
    "content": "/**\n * Copyright 2016 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nhtml, body {\n  font-family: 'Roboto', 'Helvetica', sans-serif;\n}\n.mdl-grid {\n  max-width: 1024px;\n  margin: auto;\n}\n.mdl-card {\n  min-height: 0;\n  padding-bottom: 5px;\n}\n.mdl-layout__header-row {\n  padding: 0;\n}\nh3 {\n  background: url('firebase-logo.png') no-repeat;\n  background-size: 40px;\n  padding-left: 50px;\n}\n#demo-signed-out-card,\n#demo-signed-in-card {\n  display: none;\n}\n#demo-profile-pic {\n  height: 60px;\n  width: 60px;\n  border-radius: 30px;\n  margin-left: calc(50% - 30px);\n  margin-top: 10px;\n}\n#demo-name-container,\n#demo-email-container,\n#demo-uid-container {\n  font-weight: bold;\n}\n#demo-sign-in-button {\n  background-image: url('spotify-button.png');\n  background-size: 100% 100%;\n  width: 197px;\n  height: 27px;\n}\n#demo-delete-button {\n  margin-left: 20px;\n}\n"
  },
  {
    "path": "Node-1st-gen/spotify-auth/public/main.js",
    "content": "/**\n * Copyright 2016 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use strict';\n\n// Initializes the Demo.\nfunction Demo() {\n  document.addEventListener('DOMContentLoaded', function() {\n    // Shortcuts to DOM Elements.\n    this.signInButton = document.getElementById('demo-sign-in-button');\n    this.signOutButton = document.getElementById('demo-sign-out-button');\n    this.emailContainer = document.getElementById('demo-email-container');\n    this.nameContainer = document.getElementById('demo-name-container');\n    this.deleteButton = document.getElementById('demo-delete-button');\n    this.uidContainer = document.getElementById('demo-uid-container');\n    this.profilePic = document.getElementById('demo-profile-pic');\n    this.signedOutCard = document.getElementById('demo-signed-out-card');\n    this.signedInCard = document.getElementById('demo-signed-in-card');\n\n    // Bind events.\n    this.signInButton.addEventListener('click', this.signIn.bind(this));\n    this.signOutButton.addEventListener('click', this.signOut.bind(this));\n    this.deleteButton.addEventListener('click', this.deleteAccount.bind(this));\n    firebase.auth().onAuthStateChanged(this.onAuthStateChanged.bind(this));\n  }.bind(this));\n}\n\n// Triggered on Firebase auth state change.\nDemo.prototype.onAuthStateChanged = function(user) {\n  if (user) {\n    this.nameContainer.innerText = user.displayName;\n    this.emailContainer.innerText = user.email;\n    this.uidContainer.innerText = user.uid;\n    this.profilePic.src = user.photoURL;\n    this.signedOutCard.style.display = 'none';\n    this.signedInCard.style.display = 'block';\n  } else {\n    this.signedOutCard.style.display = 'block';\n    this.signedInCard.style.display = 'none';\n  }\n};\n\n// Initiates the sign-in flow using Spotify sign in in a popup.\nDemo.prototype.signIn = function() {\n  // Open the popup that will start the auth flow.\n  window.open('popup.html', 'name', 'height=585,width=400');\n};\n\n// Signs-out of Firebase.\nDemo.prototype.signOut = function() {\n  firebase.auth().signOut();\n};\n\n// Deletes the user's account.\nDemo.prototype.deleteAccount = function() {\n  firebase.auth().currentUser.delete().then(function() {\n    window.alert('Account deleted');\n  }).catch(function(error) {\n    if (error.code === 'auth/requires-recent-login') {\n      window.alert('You need to have recently signed-in to delete your account. Please sign-in and try again.');\n      firebase.auth().signOut();\n    }\n  });\n};\n\n// Load the demo.\nnew Demo();\n"
  },
  {
    "path": "Node-1st-gen/spotify-auth/public/popup.html",
    "content": "<!doctype html>\n<!--\n  Copyright 2016 Google Inc. All rights reserved.\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n      https://www.apache.org/licenses/LICENSE-2.0\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License\n-->\n<html lang=\"en\">\n<head>\n  <meta charset=\"utf-8\">\n  <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n  <meta name=\"description\" content=\"Demonstrates how to authorize Firebase with Spotify auth using Firebase Functions\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n  <title>Authenticate with Spotify</title>\n</head>\n<body>\n\nPlease wait...\n\n<!-- Firebase -->\n<script src=\"/__/firebase/10.0.0/firebase-app-compat.js\"></script>\n<script src=\"/__/firebase/10.0.0/firebase-auth-compat.js\"></script>\n<script src=\"/__/firebase/init.js\"></script>\n\n<script>\n  /**\n   * Returns the value of the given URL query parameter.\n   */\n  function getURLParameter(name) {\n    return decodeURIComponent((new RegExp('[?|&]' + name + '=' + '([^&;]+?)(&|#|;|$)').exec(location.search) ||\n        [null, ''])[1].replace(/\\+/g, '%20')) || null;\n  }\n\n  /**\n   * Returns the ID of the Firebase project.\n   */\n  function getFirebaseProjectId() {\n    return firebase.app().options.authDomain.split('.')[0];\n  }\n\n  /**\n   * This callback is called by the JSONP callback of the 'token' Firebase Function with the Firebase auth token.\n   */\n  function tokenReceived(data) {\n    if (data.token) {\n      firebase.auth().signInWithCustomToken(data.token).then(function() {\n        window.close();\n      });\n    } else {\n      console.error(data);\n      document.body.innerText = 'Error in the token Function: ' + data.error;\n    }\n  }\n\n  var code = getURLParameter('code');\n  var state = getURLParameter('state');\n  var error = getURLParameter('error');\n  if (error) {\n    document.body.innerText = 'Error back from the Spotify auth page: ' + error;\n  } else if(!code) {\n    // Start the auth flow.\n    window.location.href  = 'https://us-central1-' + getFirebaseProjectId() + '.cloudfunctions.net/redirect';\n  } else {\n    // Use JSONP to load the 'token' Firebase Function to exchange the auth code against a Firebase custom token.\n    const script = document.createElement('script');\n    script.type = 'text/javascript';\n    // This is the URL to the HTTP triggered 'token' Firebase Function.\n    // See https://firebase.google.com/docs/functions.\n    var tokenFunctionURL = 'https://us-central1-' + getFirebaseProjectId() + '.cloudfunctions.net/token';\n    script.src = tokenFunctionURL +\n        '?code=' + encodeURIComponent(code) +\n        '&state=' + encodeURIComponent(state) +\n        '&callback=' + tokenReceived.name;\n    document.head.appendChild(script);\n  }\n</script>\n</body>\n</html>\n"
  },
  {
    "path": "Node-1st-gen/stripe/README.md",
    "content": "# Create Stripe customers and charge them on Firestore write\n\nThis sample shows you how to create Stripe customers when your users sign up, securely collect and store their payment details, and charge them when a new document is written to your Firestore.\n\n### Features\n\n- Create a customer object in Stripe when a new user signs up. ([view code](./functions/index.js#L29)).\n- Securely collect a customers card details with Stripe Elements and set them up for future usage. ([view code](./public/javascript/app.js#L69)).\n- Create a payment on the customer's card when a new document is written to the `payments` collection. ([view code](./functions/index.js#L75)).\n  - **NOTE:** Note that this example creates the payment document on the client with an amount and currency inputted by the user. In a real application, you need to validate price details in your function, e.g. based on product information stored in your Firestore.\n- Handle 3D Secure authentication if required by the card issuer. Read more about 3D Secure and SCA [here](https://stripe.com/payments/strong-customer-authentication). ([view code](./functions/index.js#L114))\n\n#### Further reading:\n\n- Stripe docs: https://stripe.com/docs/payments/save-and-reuse\n- 3D Secure and SCA regulation: https://stripe.com/payments/strong-customer-authentication\n- Firebase docs: https://firebase.google.com/docs/functions\n\n## Demo\n\n- https://cloud-functions-stripe-sample.web.app/\n\n![Firebase Stripe demo gif](./demo.gif)\n\n## Deploy and test\n\n- Create a Firebase Project using the [Firebase Developer Console](https://console.firebase.google.com)\n- Enable billing on your project by switching to the Blaze plan. See [pricing](https://firebase.google.com/pricing/) for more details. This is required to be able to do requests to non-Google services.\n- Enable Google & Email sign-in in your [authentication provider settings](https://console.firebase.google.com/project/_/authentication/providers).\n- Install [Firebase CLI Tools](https://github.com/firebase/firebase-tools) if you have not already and log in with `firebase login`.\n- Configure this sample to use your project using `firebase use --add` and select your project.\n- Install dependencies locally by running: `cd functions; npm install; cd -`\n- [Add your Stripe API Secret Key](https://dashboard.stripe.com/account/apikeys) to firebase config:\n  ```bash\n  firebase functions:secrets:set STRIPE_SECRET\n  ```\n- Set your [Stripe publishable key](https://dashboard.stripe.com/account/apikeys) for the `STRIPE_PUBLISHABLE_KEY` const in [`/public/javascript/app.js`](./public/javascript/app.js#L16)\n- Deploy your project using `firebase deploy`\n- Test your Stripe integration by viewing your deployed site `firebase open hosting:site`\n\n### Run the client locally\n\nSince this project uses Firebase Auth triggers, the functions need to be deployed. However, when making changes to your client application in the `/public` folder, you can serve it locally to quickly preview changes.\n\n```\nfirebase deploy --only functions ## only needs to be run when you make changes to your functions\n\nfirebase serve --only hosting\n```\n\n## Accepting live payments\n\nOnce you’re ready to go live, you will need to exchange your test keys for your live keys. See the [Stripe docs](https://stripe.com/docs/keys) for further details.\n\n- Update your Stripe secret config:\n  ```bash\n  firebase functions:secrets:set STRIPE_SECRET\n  ```\n- Set your [live publishable key](https://dashboard.stripe.com/account/apikeys) for the `STRIPE_PUBLISHABLE_KEY` const in [`/public/javascript/app.js`](./public/javascript/app.js#L16).\n- Redeploy both functions and hosting for the changes to take effect `firebase deploy`.\n"
  },
  {
    "path": "Node-1st-gen/stripe/firebase.json",
    "content": "{\n  \"functions\": {\n    \"codebase\": \"stripe\"\n  },\n  \"firestore\": {\n    \"rules\": \"firestore.rules\"\n  },\n  \"hosting\": {\n    \"public\": \"public\",\n    \"ignore\": [\"firebase.json\", \"**/.*\", \"**/node_modules/**\"]\n  }\n}\n"
  },
  {
    "path": "Node-1st-gen/stripe/firestore.rules",
    "content": "rules_version = '2';\nservice cloud.firestore {\n  match /databases/{database}/documents {\n    match /stripe_customers/{uid} {\n      allow read, write: if request.auth.uid == uid;\n\n      match /payment_methods/{id} {\n        allow read,write: if request.auth.uid == uid;\n      }\n      match /payments/{id} {\n        allow read, write: if request.auth.uid == uid;\n      }\n\n    }\n  }\n}\n"
  },
  {
    "path": "Node-1st-gen/stripe/functions/.eslintrc.json",
    "content": "{\n  \"parserOptions\": {\n    // Required for certain syntax usages\n    \"ecmaVersion\": 2022\n  },\n  \"plugins\": [\n    \"promise\"\n  ],\n  \"extends\": \"eslint:recommended\",\n  \"rules\": {\n    // Removed rule \"disallow the use of console\" from recommended eslint rules\n    \"no-console\": \"off\",\n\n    // Removed rule \"disallow multiple spaces in regular expressions\" from recommended eslint rules\n    \"no-regex-spaces\": \"off\",\n\n    // Removed rule \"disallow the use of debugger\" from recommended eslint rules\n    \"no-debugger\": \"off\",\n\n    // Removed rule \"disallow unused variables\" from recommended eslint rules\n    \"no-unused-vars\": \"off\",\n\n    // Removed rule \"disallow mixed spaces and tabs for indentation\" from recommended eslint rules\n    \"no-mixed-spaces-and-tabs\": \"off\",\n\n    // Removed rule \"disallow the use of undeclared variables unless mentioned in /*global */ comments\" from recommended eslint rules\n    \"no-undef\": \"off\",\n\n    // Warn against template literal placeholder syntax in regular strings\n    \"no-template-curly-in-string\": 1,\n\n    // Warn if return statements do not either always or never specify values\n    \"consistent-return\": 1,\n\n    // Warn if no return statements in callbacks of array methods\n    \"array-callback-return\": 1,\n\n    // Requre the use of === and !==\n    \"eqeqeq\": 2,\n\n    // Disallow the use of alert, confirm, and prompt\n    \"no-alert\": 2,\n\n    // Disallow the use of arguments.caller or arguments.callee\n    \"no-caller\": 2,\n\n    // Disallow null comparisons without type-checking operators\n    \"no-eq-null\": 2,\n\n    // Disallow the use of eval()\n    \"no-eval\": 2,\n\n    // Warn against extending native types\n    \"no-extend-native\": 1,\n\n    // Warn against unnecessary calls to .bind()\n    \"no-extra-bind\": 1,\n\n    // Warn against unnecessary labels    \n    \"no-extra-label\": 1,\n\n    // Disallow leading or trailing decimal points in numeric literals\n    \"no-floating-decimal\": 2,\n\n    // Warn against shorthand type conversions\n    \"no-implicit-coercion\": 1,\n\n    // Warn against function declarations and expressions inside loop statements\n    \"no-loop-func\": 1,\n\n    // Disallow new operators with the Function object\n    \"no-new-func\": 2,\n\n    // Warn against new operators with the String, Number, and Boolean objects\n    \"no-new-wrappers\": 1,\n\n    // Disallow throwing literals as exceptions\n    \"no-throw-literal\": 2,\n\n    // Require using Error objects as Promise rejection reasons\n    \"prefer-promise-reject-errors\": 2,\n\n    // Enforce “for” loop update clause moving the counter in the right direction\n    \"for-direction\": 2,\n\n    // Enforce return statements in getters\n    \"getter-return\": 2,\n\n    // Disallow await inside of loops\n    \"no-await-in-loop\": 2,\n\n    // Disallow comparing against -0\n    \"no-compare-neg-zero\": 2,\n\n    // Warn against catch clause parameters from shadowing variables in the outer scope\n    \"no-catch-shadow\": 1,\n\n    // Disallow identifiers from shadowing restricted names\n    \"no-shadow-restricted-names\": 2,\n\n    // Enforce return statements in callbacks of array methods\n    \"callback-return\": 2,\n\n    // Require error handling in callbacks\n    \"handle-callback-err\": 2,\n\n    // Warn against string concatenation with __dirname and __filename\n    \"no-path-concat\": 1,\n\n    // Prefer using arrow functions for callbacks\n    \"prefer-arrow-callback\": 1,\n\n    // Return inside each then() to create readable and reusable Promise chains.\n    \"promise/always-return\": 2,\n\n    //Enforces the use of catch() on un-returned promises\n    \"promise/catch-or-return\": 2,\n\n    // Warn against nested then() or catch() statements\n    \"promise/no-nesting\": 1\n  }\n}\n"
  },
  {
    "path": "Node-1st-gen/stripe/functions/index.js",
    "content": "/**\n * Copyright 2020 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use strict';\n\nconst functions = require('firebase-functions/v1');\nconst {onInit} = require('firebase-functions/v1/init');\nconst {defineSecret} = require('firebase-functions/params');\nconst admin = require('firebase-admin');\nadmin.initializeApp();\nconst { Logging } = require('@google-cloud/logging');\nconst logging = new Logging({\n  projectId: process.env.GCLOUD_PROJECT,\n});\n\nconst { Stripe } = require('stripe');\nconst stripeSecret = defineSecret('STRIPE_SECRET');\n\nlet stripe;\nonInit(() => {\n  stripe = new Stripe(stripeSecret.value(), {\n    apiVersion: '2020-08-27',\n  });\n});\n\n/**\n * When a user is created, create a Stripe customer object for them.\n *\n * @see https://stripe.com/docs/payments/save-and-reuse#web-create-customer\n */\nexports.createStripeCustomer = functions.runWith({secrets: [stripeSecret]}).auth.user().onCreate(async (user) => {\n  const customer = await stripe.customers.create({ email: user.email });\n  const intent = await stripe.setupIntents.create({\n    customer: customer.id,\n  });\n  await admin.firestore().collection('stripe_customers').doc(user.uid).set({\n    customer_id: customer.id,\n    setup_secret: intent.client_secret,\n  });\n  return;\n});\n\n/**\n * When adding the payment method ID on the client,\n * this function is triggered to retrieve the payment method details.\n */\nexports.addPaymentMethodDetails = functions.runWith({secrets: [stripeSecret]}).firestore\n  .document('/stripe_customers/{userId}/payment_methods/{pushId}')\n  .onCreate(async (snap, context) => {\n    try {\n      const paymentMethodId = snap.data().id;\n      const paymentMethod = await stripe.paymentMethods.retrieve(\n        paymentMethodId\n      );\n      await snap.ref.set(paymentMethod);\n      // Create a new SetupIntent so the customer can add a new method next time.\n      const intent = await stripe.setupIntents.create({\n        customer: `${paymentMethod.customer}`,\n      });\n      await snap.ref.parent.parent.set(\n        {\n          setup_secret: intent.client_secret,\n        },\n        { merge: true }\n      );\n      return;\n    } catch (error) {\n      await snap.ref.set({ error: userFacingMessage(error) }, { merge: true });\n      await reportError(error, { user: context.params.userId });\n    }\n  });\n\n/**\n * When a payment document is written on the client,\n * this function is triggered to create the payment in Stripe.\n *\n * @see https://stripe.com/docs/payments/save-and-reuse#web-create-payment-intent-off-session\n */\n\n// [START chargecustomer]\n\nexports.createStripePayment = functions.runWith({secrets: [stripeSecret]}).firestore\n  .document('stripe_customers/{userId}/payments/{pushId}')\n  .onCreate(async (snap, context) => {\n    const { amount, currency, payment_method } = snap.data();\n    try {\n      // Look up the Stripe customer id.\n      const customer = (await snap.ref.parent.parent.get()).data().customer_id;\n      // Create a charge using the pushId as the idempotency key\n      // to protect against double charges.\n      const idempotencyKey = context.params.pushId;\n      const payment = await stripe.paymentIntents.create(\n        {\n          amount,\n          currency,\n          customer,\n          payment_method,\n          off_session: false,\n          confirm: true,\n          confirmation_method: 'manual',\n        },\n        { idempotencyKey }\n      );\n      // If the result is successful, write it back to the database.\n      await snap.ref.set(payment);\n    } catch (error) {\n      // We want to capture errors and render them in a user-friendly way, while\n      // still logging an exception to Error Reporting.\n      functions.logger.log(error);\n      await snap.ref.set({ error: userFacingMessage(error) }, { merge: true });\n      await reportError(error, { user: context.params.userId });\n    }\n  });\n\n// [END chargecustomer]\n\n/**\n * When 3D Secure is performed, we need to reconfirm the payment\n * after authentication has been performed.\n *\n * @see https://stripe.com/docs/payments/accept-a-payment-synchronously#web-confirm-payment\n */\nexports.confirmStripePayment = functions.runWith({secrets: [stripeSecret]}).firestore\n  .document('stripe_customers/{userId}/payments/{pushId}')\n  .onUpdate(async (change, context) => {\n    if (change.after.data().status === 'requires_confirmation') {\n      const payment = await stripe.paymentIntents.confirm(\n        change.after.data().id\n      );\n      change.after.ref.set(payment);\n    }\n  });\n\n/**\n * When a user deletes their account, clean up after them\n */\nexports.cleanupUser = functions.runWith({secrets: [stripeSecret]}).auth.user().onDelete(async (user) => {\n  const dbRef = admin.firestore().collection('stripe_customers');\n  const customer = (await dbRef.doc(user.uid).get()).data();\n  await stripe.customers.del(customer.customer_id);\n  // Delete the customers payments & payment methods in firestore.\n  const batch = admin.firestore().batch();\n  const paymetsMethodsSnapshot = await dbRef\n    .doc(user.uid)\n    .collection('payment_methods')\n    .get();\n  paymetsMethodsSnapshot.forEach((snap) => batch.delete(snap.ref));\n  const paymentsSnapshot = await dbRef\n    .doc(user.uid)\n    .collection('payments')\n    .get();\n  paymentsSnapshot.forEach((snap) => batch.delete(snap.ref));\n\n  await batch.commit();\n\n  await dbRef.doc(user.uid).delete();\n  return;\n});\n\n/**\n * To keep on top of errors, we should raise a verbose error report with Error Reporting rather\n * than simply relying on functions.logger.error. This will calculate users affected + send you email\n * alerts, if you've opted into receiving them.\n */\n\n// [START reporterror]\n\nfunction reportError(err, context = {}) {\n  // This is the name of the log stream that will receive the log\n  // entry. This name can be any valid log stream name, but must contain \"err\"\n  // in order for the error to be picked up by Error Reporting.\n  const logName = 'errors';\n  const log = logging.log(logName);\n\n  // https://cloud.google.com/logging/docs/api/ref_v2beta1/rest/v2beta1/MonitoredResource\n  const metadata = {\n    resource: {\n      type: 'cloud_function',\n      labels: { function_name: process.env.FUNCTION_NAME },\n    },\n  };\n\n  // https://cloud.google.com/error-reporting/reference/rest/v1beta1/ErrorEvent\n  const errorEvent = {\n    message: err.stack,\n    serviceContext: {\n      service: process.env.FUNCTION_NAME,\n      resourceType: 'cloud_function',\n    },\n    context: context,\n  };\n\n  // Write the error log entry\n  return new Promise((resolve, reject) => {\n    log.write(log.entry(metadata, errorEvent), (error) => {\n      if (error) {\n        return reject(error);\n      }\n      return resolve();\n    });\n  });\n}\n\n// [END reporterror]\n\n/**\n * Sanitize the error message for the user.\n */\nfunction userFacingMessage(error) {\n  return error.type\n    ? error.message\n    : 'An error occurred, developers have been alerted';\n}\n"
  },
  {
    "path": "Node-1st-gen/stripe/functions/package.json",
    "content": "{\n  \"name\": \"stripe-functions\",\n  \"description\": \"Stripe Firebase Functions\",\n  \"dependencies\": {\n    \"@google-cloud/logging\": \"^7.3.0\",\n    \"eslint-plugin-promise\": \"^7.2.1\",\n    \"firebase-admin\": \"^13.0.2\",\n    \"firebase-functions\": \"7.0.0\",\n    \"stripe\": \"^8.222.0\"\n  },\n  \"devDependencies\": {\n    \"eslint\": \"^8.57.1\"\n  },\n  \"scripts\": {\n    \"lint\": \"./node_modules/.bin/eslint --max-warnings=0 .\",\n    \"serve\": \"firebase emulators:start --only functions\",\n    \"shell\": \"firebase functions:shell\",\n    \"start\": \"npm run shell\",\n    \"deploy\": \"firebase deploy --only functions\",\n    \"logs\": \"firebase functions:log\",\n    \"compile\": \"cp ../../../tsconfig.template.json ./tsconfig-compile.json && tsc --project tsconfig-compile.json\"\n  },\n  \"engines\": {\n    \"node\": \"20\"\n  },\n  \"private\": true\n}\n"
  },
  {
    "path": "Node-1st-gen/stripe/public/index.html",
    "content": "<!DOCTYPE html>\n<!--\n Copyright 2023 Google LLC\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n     http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n-->\n\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n    <title>Cloud Functions for Firebase with Stripe Payments</title>\n    <link\n      type=\"text/css\"\n      rel=\"stylesheet\"\n      href=\"https://www.gstatic.com/firebasejs/ui/4.5.0/firebase-ui-auth.css\"\n    />\n    <link\n      rel=\"stylesheet\"\n      href=\"https://cdn.jsdelivr.net/npm/@exampledev/new.css@1/new.min.css\"\n    />\n  </head>\n  <style>\n    fieldset {\n      background-color: #f6f8fa;\n    }\n    #error-message {\n      color: red;\n      height: 2em;\n    }\n  </style>\n  <body>\n    <h1>Cloud Functions for Firebase with Stripe Payments</h1>\n    <section id=\"firebaseui-auth-container\"></section>\n    <div id=\"loader\">Loading &hellip;</div>\n    <section id=\"content\" style=\"display: none;\">\n      <button type=\"button\" id=\"signout\">\n        Sign out\n      </button>\n      <div>\n        <h2>Payment Methods</h2>\n        <details id=\"add-new-card\">\n          <summary>Add new</summary>\n          <p>\n            Use any of the\n            <a href=\"https://stripe.com/docs/testing#international-cards\"\n              >Stripe test cards</a\n            >\n            for this demo!\n          </p>\n          <form id=\"payment-method-form\">\n            <label>\n              Cardholder name\n              <input type=\"text\" name=\"name\" required />\n            </label>\n            <fieldset>\n              <div id=\"card-element\"></div>\n            </fieldset>\n            <div id=\"error-message\" role=\"alert\"></div>\n            <button>Save Card</button>\n          </form>\n        </details>\n        <hr />\n        <form id=\"payment-form\">\n          <div>\n            <label>\n              Card:\n              <select name=\"payment-method\" required></select>\n            </label>\n          </div>\n          <div>\n            <label>\n              Amount:\n              <input\n                name=\"amount\"\n                type=\"number\"\n                min=\"1\"\n                max=\"99999999\"\n                value=\"100\"\n                required\n              />\n            </label>\n            <label>\n              Currency:\n              <select name=\"currency\">\n                <option value=\"usd\">USD</option>\n                <option value=\"eur\">EUR</option>\n                <option value=\"gbp\">GBP</option>\n                <option value=\"jpy\">JPY</option>\n              </select>\n            </label>\n          </div>\n          <button>Charge selected card</button>\n        </form>\n      </div>\n      <div>\n        <h2>Payments</h2>\n        <ul id=\"payments-list\"></ul>\n      </div>\n    </section>\n    <hr />\n    <footer>\n      <a\n        href=\"https://github.com/firebase/functions-samples/tree/main/stripe\"\n      >\n        View source code\n      </a>\n    </footer>\n\n    <!-- Import and configure the Firebase SDK -->\n    <!-- These scripts are made available when the app is served or deployed on Firebase Hosting -->\n    <!-- If you do not serve/host your project using Firebase Hosting see https://firebase.google.com/docs/web/setup -->\n    <script src=\"/__/firebase/10.0.0/firebase-app-compat.js\"></script>\n    <script src=\"/__/firebase/10.0.0/firebase-auth-compat.js\"></script>\n    <script src=\"/__/firebase/10.0.0/firebase-firestore-compat.js\"></script>\n    <script src=\"/__/firebase/init.js\"></script>\n\n    <!-- Import Firebase UI -->\n    <script src=\"https://www.gstatic.com/firebasejs/ui/4.5.0/firebase-ui-auth.js\"></script>\n\n    <!-- Stripe's JS library -->\n    <script src=\"https://js.stripe.com/v3/\"></script>\n\n    <!-- Our application script -->\n    <script src=\"/javascript/app.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "Node-1st-gen/stripe/public/javascript/app.js",
    "content": "/**\n * Copyright 2020 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nconst STRIPE_PUBLISHABLE_KEY = '<YOUR STRIPE PUBLISHABLE KEY>';\nlet currentUser = {};\nlet customerData = {};\n\n/**\n * Firebase auth configuration\n */\nconst firebaseUI = new firebaseui.auth.AuthUI(firebase.auth());\nconst firebaseUiConfig = {\n  callbacks: {\n    signInSuccessWithAuthResult: function (authResult, redirectUrl) {\n      // User successfully signed in.\n      // Return type determines whether we continue the redirect automatically\n      // or whether we leave that to developer to handle.\n      return true;\n    },\n    uiShown: () => {\n      document.getElementById('loader').style.display = 'none';\n    },\n  },\n  signInFlow: 'popup',\n  signInSuccessUrl: '/',\n  signInOptions: [\n    firebase.auth.GoogleAuthProvider.PROVIDER_ID,\n    firebase.auth.EmailAuthProvider.PROVIDER_ID,\n  ],\n  credentialHelper: firebaseui.auth.CredentialHelper.NONE,\n  // Your terms of service url.\n  tosUrl: 'https://example.com/terms',\n  // Your privacy policy url.\n  privacyPolicyUrl: 'https://example.com/privacy',\n};\nfirebase.auth().onAuthStateChanged((firebaseUser) => {\n  if (firebaseUser) {\n    currentUser = firebaseUser;\n    firebase\n      .firestore()\n      .collection('stripe_customers')\n      .doc(currentUser.uid)\n      .onSnapshot((snapshot) => {\n        if (snapshot.data()) {\n          customerData = snapshot.data();\n          startDataListeners();\n          document.getElementById('loader').style.display = 'none';\n          document.getElementById('content').style.display = 'block';\n        } else {\n          console.warn(\n            `No Stripe customer found in Firestore for user: ${currentUser.uid}`\n          );\n        }\n      });\n  } else {\n    document.getElementById('content').style.display = 'none';\n    firebaseUI.start('#firebaseui-auth-container', firebaseUiConfig);\n  }\n});\n\n/**\n * Set up Stripe Elements\n */\nconst stripe = Stripe(STRIPE_PUBLISHABLE_KEY);\nconst elements = stripe.elements();\nconst cardElement = elements.create('card');\ncardElement.mount('#card-element');\ncardElement.on('change', ({ error }) => {\n  const displayError = document.getElementById('error-message');\n  if (error) {\n    displayError.textContent = error.message;\n  } else {\n    displayError.textContent = '';\n  }\n});\n\n/**\n * Set up Firestore data listeners\n */\nfunction startDataListeners() {\n  /**\n   * Get all payment methods for the logged in customer\n   */\n  firebase\n    .firestore()\n    .collection('stripe_customers')\n    .doc(currentUser.uid)\n    .collection('payment_methods')\n    .onSnapshot((snapshot) => {\n      if (snapshot.empty) {\n        document.querySelector('#add-new-card').open = true;\n      }\n      snapshot.forEach(function (doc) {\n        const paymentMethod = doc.data();\n        if (!paymentMethod.card) {\n          return;\n        }\n\n        const optionId = `card-${doc.id}`;\n        let optionElement = document.getElementById(optionId);\n\n        // Add a new option if one doesn't exist yet.\n        if (!optionElement) {\n          optionElement = document.createElement('option');\n          optionElement.id = optionId;\n          document\n            .querySelector('select[name=payment-method]')\n            .appendChild(optionElement);\n        }\n\n        optionElement.value = paymentMethod.id;\n        optionElement.text = `${paymentMethod.card.brand} •••• ${paymentMethod.card.last4} | Expires ${paymentMethod.card.exp_month}/${paymentMethod.card.exp_year}`;\n      });\n    });\n\n  /**\n   * Get all payments for the logged in customer\n   */\n  firebase\n    .firestore()\n    .collection('stripe_customers')\n    .doc(currentUser.uid)\n    .collection('payments')\n    .onSnapshot((snapshot) => {\n      snapshot.forEach((doc) => {\n        const payment = doc.data();\n\n        let liElement = document.getElementById(`payment-${doc.id}`);\n        if (!liElement) {\n          liElement = document.createElement('li');\n          liElement.id = `payment-${doc.id}`;\n        }\n\n        let content = '';\n        if (\n          payment.status === 'new' ||\n          payment.status === 'requires_confirmation'\n        ) {\n          content = `Creating Payment for ${formatAmount(\n            payment.amount,\n            payment.currency\n          )}`;\n        } else if (payment.status === 'succeeded') {\n          const card = payment.charges.data[0].payment_method_details.card;\n          content = `✅ Payment for ${formatAmount(\n            payment.amount,\n            payment.currency\n          )} on ${card.brand} card •••• ${card.last4}.`;\n        } else if (payment.status === 'requires_action') {\n          content = `🚨 Payment for ${formatAmount(\n            payment.amount,\n            payment.currency\n          )} ${payment.status}`;\n          handleCardAction(payment, doc.id);\n        } else {\n          content = `⚠️ Payment for ${formatAmount(\n            payment.amount,\n            payment.currency\n          )} ${payment.status}`;\n        }\n        liElement.innerText = content;\n        document.querySelector('#payments-list').appendChild(liElement);\n      });\n    });\n}\n\n/**\n * Event listeners\n */\n\n// Signout button\ndocument\n  .getElementById('signout')\n  .addEventListener('click', () => firebase.auth().signOut());\n\n// Add new card form\ndocument\n  .querySelector('#payment-method-form')\n  .addEventListener('submit', async (event) => {\n    event.preventDefault();\n    if (!event.target.reportValidity()) {\n      return;\n    }\n    document\n      .querySelectorAll('button')\n      .forEach((button) => (button.disabled = true));\n\n    const form = new FormData(event.target);\n    const cardholderName = form.get('name');\n\n    const { setupIntent, error } = await stripe.confirmCardSetup(\n      customerData.setup_secret,\n      {\n        payment_method: {\n          card: cardElement,\n          billing_details: {\n            name: cardholderName,\n          },\n        },\n      }\n    );\n\n    if (error) {\n      document.querySelector('#error-message').textContent = error.message;\n      document\n        .querySelectorAll('button')\n        .forEach((button) => (button.disabled = false));\n      return;\n    }\n\n    await firebase\n      .firestore()\n      .collection('stripe_customers')\n      .doc(currentUser.uid)\n      .collection('payment_methods')\n      .add({ id: setupIntent.payment_method });\n\n    document.querySelector('#add-new-card').open = false;\n    document\n      .querySelectorAll('button')\n      .forEach((button) => (button.disabled = false));\n  });\n\n// Create payment form\ndocument\n  .querySelector('#payment-form')\n  .addEventListener('submit', async (event) => {\n    event.preventDefault();\n    document\n      .querySelectorAll('button')\n      .forEach((button) => (button.disabled = true));\n\n    const form = new FormData(event.target);\n    const amount = Number(form.get('amount'));\n    const currency = form.get('currency');\n    const data = {\n      payment_method: form.get('payment-method'),\n      currency,\n      amount: formatAmountForStripe(amount, currency),\n      status: 'new',\n    };\n\n    await firebase\n      .firestore()\n      .collection('stripe_customers')\n      .doc(currentUser.uid)\n      .collection('payments')\n      .add(data);\n\n    document\n      .querySelectorAll('button')\n      .forEach((button) => (button.disabled = false));\n  });\n\n/**\n * Helper functions\n */\n\n// Format amount for diplay in the UI\nfunction formatAmount(amount, currency) {\n  amount = zeroDecimalCurrency(amount, currency)\n    ? amount\n    : (amount / 100).toFixed(2);\n  return new Intl.NumberFormat('en-US', {\n    style: 'currency',\n    currency,\n  }).format(amount);\n}\n\n// Format amount for Stripe\nfunction formatAmountForStripe(amount, currency) {\n  return zeroDecimalCurrency(amount, currency)\n    ? amount\n    : Math.round(amount * 100);\n}\n\n// Check if we have a zero decimal currency\n// https://stripe.com/docs/currencies#zero-decimal\nfunction zeroDecimalCurrency(amount, currency) {\n  let numberFormat = new Intl.NumberFormat(['en-US'], {\n    style: 'currency',\n    currency: currency,\n    currencyDisplay: 'symbol',\n  });\n  const parts = numberFormat.formatToParts(amount);\n  let zeroDecimalCurrency = true;\n  for (let part of parts) {\n    if (part.type === 'decimal') {\n      zeroDecimalCurrency = false;\n    }\n  }\n  return zeroDecimalCurrency;\n}\n\n// Handle card actions like 3D Secure\nasync function handleCardAction(payment, docId) {\n  const { error, paymentIntent } = await stripe.handleCardAction(\n    payment.client_secret\n  );\n  if (error) {\n    alert(error.message);\n    payment = error.payment_intent;\n  } else if (paymentIntent) {\n    payment = paymentIntent;\n  }\n\n  await firebase\n    .firestore()\n    .collection('stripe_customers')\n    .doc(currentUser.uid)\n    .collection('payments')\n    .doc(docId)\n    .set(payment, { merge: true });\n}\n"
  },
  {
    "path": "Node-1st-gen/survey-app-update/README.md",
    "content": "# Send a survey when users update your app\n\nThis sample shows how to send a survey to your users who have updated your app. App Update is detected using a Firebase Analytics event.\n\n\n## Functions Code\n\nSee file [functions/index.js](functions/index.js) for the trigger and the email sending code.\n\nSending emails is performed using [nodemailer](https://www.npmjs.com/package/nodemailer) a node based Email client with comprehensive EMail server setup. For simplicity, in this sample we're showing how to send email through SMTP using a Gmail account. Be aware that Gmail has an [email sending quota](https://support.google.com/mail/answer/22839). If you are planning on sending a large number of emails you should use a professional email sending platform such as [Sendgrid](https://console.cloud.google.com/launcher/details/sendgrid-app/sendgrid-email), [Mailjet](https://www.mailjet.com/google) or [Mailgun](http://www.mailgun.com/google).\n\nThe dependencies are listed in [functions/package.json](functions/package.json).\n\n\n## Trigger rules\n\nThe function triggers on changes to `app_update` Firebase Analytics events. For other automatically logged events see: https://support.google.com/firebase/answer/6317485\n\n\n## Setting up the sample\n\nSet the `gmail.email` and `gmail.password` Google Cloud environment variables to match the email and password of the Gmail account used to send emails. For this use:\n\n```bash\nAdd the following configuration to your `.env` file:\n```\nGMAIL_EMAIL=\"myusername@gmail.com\"\n```\nThen, set the `GMAIL_PASSWORD` secret:\n```\nfirebase functions:secrets:set GMAIL_PASSWORD\n```\n```\n\n\n## Deploy and test\n\nThis sample can be tested on your Android and iOS app. To test it out:\n\n - Make sure you set the `app_update` events as being a **Conversion event** in your project. You can do this on the Analytics section > Events tab.\n - Set the project to your Firebase project using `firebase use --add` then select your projec tin the list.\n - Deploy your project using `firebase deploy`\n - Have users update your app, for instance through the play store.\n - Within a few hours the emails to the survey will be sent.\n"
  },
  {
    "path": "Node-1st-gen/survey-app-update/firebase.json",
    "content": "{\n    \"functions\": {\n      \"codebase\": \"survey-app-update\"\n    }\n}\n  "
  },
  {
    "path": "Node-1st-gen/survey-app-update/functions/.eslintrc.json",
    "content": "{\n  \"parserOptions\": {\n    // Required for certain syntax usages\n    \"ecmaVersion\": 2022\n  },\n  \"plugins\": [\n    \"promise\"\n  ],\n  \"extends\": \"eslint:recommended\",\n  \"rules\": {\n    // Removed rule \"disallow the use of console\" from recommended eslint rules\n    \"no-console\": \"off\",\n\n    // Removed rule \"disallow multiple spaces in regular expressions\" from recommended eslint rules\n    \"no-regex-spaces\": \"off\",\n\n    // Removed rule \"disallow the use of debugger\" from recommended eslint rules\n    \"no-debugger\": \"off\",\n\n    // Removed rule \"disallow unused variables\" from recommended eslint rules\n    \"no-unused-vars\": \"off\",\n\n    // Removed rule \"disallow mixed spaces and tabs for indentation\" from recommended eslint rules\n    \"no-mixed-spaces-and-tabs\": \"off\",\n\n    // Removed rule \"disallow the use of undeclared variables unless mentioned in /*global */ comments\" from recommended eslint rules\n    \"no-undef\": \"off\",\n\n    // Warn against template literal placeholder syntax in regular strings\n    \"no-template-curly-in-string\": 1,\n\n    // Warn if return statements do not either always or never specify values\n    \"consistent-return\": 1,\n\n    // Warn if no return statements in callbacks of array methods\n    \"array-callback-return\": 1,\n\n    // Requre the use of === and !==\n    \"eqeqeq\": 2,\n\n    // Disallow the use of alert, confirm, and prompt\n    \"no-alert\": 2,\n\n    // Disallow the use of arguments.caller or arguments.callee\n    \"no-caller\": 2,\n\n    // Disallow null comparisons without type-checking operators\n    \"no-eq-null\": 2,\n\n    // Disallow the use of eval()\n    \"no-eval\": 2,\n\n    // Warn against extending native types\n    \"no-extend-native\": 1,\n\n    // Warn against unnecessary calls to .bind()\n    \"no-extra-bind\": 1,\n\n    // Warn against unnecessary labels    \n    \"no-extra-label\": 1,\n\n    // Disallow leading or trailing decimal points in numeric literals\n    \"no-floating-decimal\": 2,\n\n    // Warn against shorthand type conversions\n    \"no-implicit-coercion\": 1,\n\n    // Warn against function declarations and expressions inside loop statements\n    \"no-loop-func\": 1,\n\n    // Disallow new operators with the Function object\n    \"no-new-func\": 2,\n\n    // Warn against new operators with the String, Number, and Boolean objects\n    \"no-new-wrappers\": 1,\n\n    // Disallow throwing literals as exceptions\n    \"no-throw-literal\": 2,\n\n    // Require using Error objects as Promise rejection reasons\n    \"prefer-promise-reject-errors\": 2,\n\n    // Enforce “for” loop update clause moving the counter in the right direction\n    \"for-direction\": 2,\n\n    // Enforce return statements in getters\n    \"getter-return\": 2,\n\n    // Disallow await inside of loops\n    \"no-await-in-loop\": 2,\n\n    // Disallow comparing against -0\n    \"no-compare-neg-zero\": 2,\n\n    // Warn against catch clause parameters from shadowing variables in the outer scope\n    \"no-catch-shadow\": 1,\n\n    // Disallow identifiers from shadowing restricted names\n    \"no-shadow-restricted-names\": 2,\n\n    // Enforce return statements in callbacks of array methods\n    \"callback-return\": 2,\n\n    // Require error handling in callbacks\n    \"handle-callback-err\": 2,\n\n    // Warn against string concatenation with __dirname and __filename\n    \"no-path-concat\": 1,\n\n    // Prefer using arrow functions for callbacks\n    \"prefer-arrow-callback\": 1,\n\n    // Return inside each then() to create readable and reusable Promise chains.\n    \"promise/always-return\": 2,\n\n    //Enforces the use of catch() on un-returned promises\n    \"promise/catch-or-return\": 2,\n\n    // Warn against nested then() or catch() statements\n    \"promise/no-nesting\": 1\n  }\n}\n"
  },
  {
    "path": "Node-1st-gen/survey-app-update/functions/index.js",
    "content": "/**\n * Copyright 2016 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use strict';\n\nconst functions = require('firebase-functions/v1');\nconst {onInit} = require('firebase-functions/v1/init');\nconst {defineString, defineSecret} = require('firebase-functions/params');\nconst admin = require('firebase-admin');\nadmin.initializeApp();\nconst nodemailer = require('nodemailer');\n// Configure the email transport using the default SMTP transport and a GMail account.\n// For other types of transports such as Sendgrid see https://nodemailer.com/transports/\n// TODO: Configure the `GMAIL_EMAIL` environment variable and the `GMAIL_PASSWORD` secret.\nconst gmailEmail = defineString('GMAIL_EMAIL');\nconst gmailPassword = defineSecret('GMAIL_PASSWORD');\n\nlet mailTransport;\nonInit(() => {\n  mailTransport = nodemailer.createTransport(\n    `smtps://${encodeURIComponent(gmailEmail.value())}:${encodeURIComponent(gmailPassword.value())}@smtp.gmail.com`);\n});\n\n// TODO: Create yor own survey.\nconst LINK_TO_SURVEY = 'https://goo.gl/forms/IdurnOZ66h3FtlO33';\nconst LATEST_VERSION = '2.0';\n\n/**\n * After a user has updated the app. Send them a survey to compare the app with the old version.\n */\nexports.sendAppUpdateSurvey = functions.runWith({secrets: [gmailPassword]}).analytics.event('app_update').onLog(async (event) => {\n  const uid = event.user.userId;\n  const appVerion = event.user.appInfo.appVersion;\n\n  // Check that the user has indeed upgraded to the latest version.\n  if (appVerion === LATEST_VERSION) {\n    // Fetch the email of the user. In this sample we assume that the app is using Firebase Auth and\n    // has set the Firebase Analytics User ID to be the same as the Firebase Auth uid using the\n    // setUserId API.\n    const user = await admin.auth().getUser(uid);\n    const email = user.email;\n    const name = user.displayName;\n    return sendSurveyEmail(email, name);\n  }\n  return null;\n});\n\n/**\n * Sends an email pointing to the Upgraded App survey.\n */\nasync function sendSurveyEmail(email, name) {\n  const mailOptions = {\n    from: '\"MyCoolApp\" <noreply@firebase.com>',\n    to: email,\n    subject: 'How did you like our new app?',\n    text: `Hey ${name}, We've seen that you have upgraded to the new version of our app!\n           It would be awesome if you could tell us how you like it.\n           Fill out our survey: ${LINK_TO_SURVEY}`,\n  };\n\n  await mailTransport.sendMail(mailOptions);\n  functions.logger.log('Upgrade App Survey email sent to:', email);\n}\n"
  },
  {
    "path": "Node-1st-gen/survey-app-update/functions/package.json",
    "content": "{\n  \"name\": \"survey-app-update-function\",\n  \"description\": \"Sends a survey via email to users who upgrade your app\",\n  \"dependencies\": {\n    \"eslint-plugin-promise\": \"^7.2.1\",\n    \"firebase-admin\": \"^13.0.2\",\n    \"firebase-functions\": \"7.0.0\",\n    \"nodemailer\": \"^6.8.0\"\n  },\n  \"devDependencies\": {\n    \"eslint\": \"^8.57.1\"\n  },\n  \"scripts\": {\n    \"lint\": \"./node_modules/.bin/eslint --max-warnings=0 .\",\n    \"serve\": \"firebase emulators:start --only functions\",\n    \"shell\": \"firebase functions:shell\",\n    \"start\": \"npm run shell\",\n    \"deploy\": \"firebase deploy --only functions\",\n    \"logs\": \"firebase functions:log\",\n    \"compile\": \"cp ../../../tsconfig.template.json ./tsconfig-compile.json && tsc --project tsconfig-compile.json\"\n  },\n  \"engines\": {\n    \"node\": \"20\"\n  },\n  \"private\": true\n}\n"
  },
  {
    "path": "Node-1st-gen/template-handlebars/README.md",
    "content": "# Server-side generated pages w/ Handlebars templating and user sessions\n\nThis samples shows how to serve server-side generated HTML pages using the [HandlebarsJs](http://handlebarsjs.com/) templating system.\n\nIt also shows how to serve user specific content by passing the Firebase ID token of the signed-in user in a `__session` cookie.\n\nChecking and decoding the ID token passed in the `__session` cookie is done with an ExpressJs middleware.\n\nSome custom scripts in [functions/views/layouts/main.handlebars] maintain the Firebase ID token in the `__session` cookie.\n\n\n## Setting up the sample\n\n 1. Create a Firebase Project using the [Firebase Console](https://console.firebase.google.com).\n 1. Enable the **Google** Provider in the **Auth** section.\n 1. Clone or download this repo and open the `template-handlebars` directory.\n 1. You must have the Firebase CLI installed. If you don't have it install it with `npm install -g firebase-tools` and then configure it with `firebase login`.\n 1. Configure the CLI locally by using `firebase use --add` and select your project in the list.\n 1. Install dependencies locally by running: `cd functions; npm install; cd -`\n\n\n## Deploy and test\n\nThis sample comes with a web-based UI for testing the function.\nTo test locally do:\n\n 1. Start serving your project locally using `firebase serve --only hosting,functions`\n 1. Open the app in a browser at `https://localhost:5000`.\n 1. Sign in the web app in the browser using Google Sign-In and some user information will be displayed on a server-side generated page.\n\nTo deploy and test the app on prod do:\n\n 1. Deploy your project using `firebase deploy`\n 1. Open the app using `firebase open hosting:site`, this will open a browser.\n 1. Sign in the web app in the browser using Google Sign-In and some user information will be displayed on a server-side generated page.\n\n\n## Contributing\n\nWe'd love that you contribute to the project. Before doing so please read our [Contributor guide](../CONTRIBUTING.md).\n\n\n## License\n\n© Google, 2017. Licensed under an [Apache-2](../LICENSE) license.\n"
  },
  {
    "path": "Node-1st-gen/template-handlebars/firebase.json",
    "content": "{\n  \"functions\": {\n    \"codebase\": \"template-handlebars\"\n  },\n  \"hosting\": {\n    \"public\": \"public\",\n    \"rewrites\": [\n      {\n        \"source\":\"**\",\n        \"function\":\"app\"\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "Node-1st-gen/template-handlebars/functions/.eslintrc.json",
    "content": "{\n  \"parserOptions\": {\n    // Required for certain syntax usages\n    \"ecmaVersion\": 2022\n  },\n  \"plugins\": [\n    \"promise\"\n  ],\n  \"extends\": \"eslint:recommended\",\n  \"rules\": {\n    // Removed rule \"disallow the use of console\" from recommended eslint rules\n    \"no-console\": \"off\",\n\n    // Removed rule \"disallow multiple spaces in regular expressions\" from recommended eslint rules\n    \"no-regex-spaces\": \"off\",\n\n    // Removed rule \"disallow the use of debugger\" from recommended eslint rules\n    \"no-debugger\": \"off\",\n\n    // Removed rule \"disallow unused variables\" from recommended eslint rules\n    \"no-unused-vars\": \"off\",\n\n    // Removed rule \"disallow mixed spaces and tabs for indentation\" from recommended eslint rules\n    \"no-mixed-spaces-and-tabs\": \"off\",\n\n    // Removed rule \"disallow the use of undeclared variables unless mentioned in /*global */ comments\" from recommended eslint rules\n    \"no-undef\": \"off\",\n\n    // Warn against template literal placeholder syntax in regular strings\n    \"no-template-curly-in-string\": 1,\n\n    // Warn if return statements do not either always or never specify values\n    \"consistent-return\": 1,\n\n    // Warn if no return statements in callbacks of array methods\n    \"array-callback-return\": 1,\n\n    // Requre the use of === and !==\n    \"eqeqeq\": 2,\n\n    // Disallow the use of alert, confirm, and prompt\n    \"no-alert\": 2,\n\n    // Disallow the use of arguments.caller or arguments.callee\n    \"no-caller\": 2,\n\n    // Disallow null comparisons without type-checking operators\n    \"no-eq-null\": 2,\n\n    // Disallow the use of eval()\n    \"no-eval\": 2,\n\n    // Warn against extending native types\n    \"no-extend-native\": 1,\n\n    // Warn against unnecessary calls to .bind()\n    \"no-extra-bind\": 1,\n\n    // Warn against unnecessary labels    \n    \"no-extra-label\": 1,\n\n    // Disallow leading or trailing decimal points in numeric literals\n    \"no-floating-decimal\": 2,\n\n    // Warn against shorthand type conversions\n    \"no-implicit-coercion\": 1,\n\n    // Warn against function declarations and expressions inside loop statements\n    \"no-loop-func\": 1,\n\n    // Disallow new operators with the Function object\n    \"no-new-func\": 2,\n\n    // Warn against new operators with the String, Number, and Boolean objects\n    \"no-new-wrappers\": 1,\n\n    // Disallow throwing literals as exceptions\n    \"no-throw-literal\": 2,\n\n    // Require using Error objects as Promise rejection reasons\n    \"prefer-promise-reject-errors\": 2,\n\n    // Enforce “for” loop update clause moving the counter in the right direction\n    \"for-direction\": 2,\n\n    // Enforce return statements in getters\n    \"getter-return\": 2,\n\n    // Disallow await inside of loops\n    \"no-await-in-loop\": 2,\n\n    // Disallow comparing against -0\n    \"no-compare-neg-zero\": 2,\n\n    // Warn against catch clause parameters from shadowing variables in the outer scope\n    \"no-catch-shadow\": 1,\n\n    // Disallow identifiers from shadowing restricted names\n    \"no-shadow-restricted-names\": 2,\n\n    // Enforce return statements in callbacks of array methods\n    \"callback-return\": 2,\n\n    // Require error handling in callbacks\n    \"handle-callback-err\": 2,\n\n    // Warn against string concatenation with __dirname and __filename\n    \"no-path-concat\": 1,\n\n    // Prefer using arrow functions for callbacks\n    \"prefer-arrow-callback\": 1,\n\n    // Return inside each then() to create readable and reusable Promise chains.\n    \"promise/always-return\": 2,\n\n    //Enforces the use of catch() on un-returned promises\n    \"promise/catch-or-return\": 2,\n\n    // Warn against nested then() or catch() statements\n    \"promise/no-nesting\": 1\n  }\n}\n"
  },
  {
    "path": "Node-1st-gen/template-handlebars/functions/firebaseUser.js",
    "content": "/**\n * Copyright 2016 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use strict';\n\nconst admin = require('firebase-admin');\nconst cookieParser = require('cookie-parser')();\nconst functions = require('firebase-functions/v1');\n\n// Express middleware that checks if a Firebase ID Tokens is passed in the `Authorization` HTTP\n// header or the `__session` cookie and decodes it.\n// The Firebase ID token needs to be passed as a Bearer token in the Authorization HTTP header like this:\n// `Authorization: Bearer <Firebase ID Token>`.\n// When decoded successfully, the ID Token content will be added as `req.user`.\nasync function validateFirebaseIdToken (req, res, next) {\n  functions.logger.log('Check if request is authorized with Firebase ID token');\n\n  const idToken = await getIdTokenFromRequest(req, res);\n  if (idToken) {\n    await addDecodedIdTokenToRequest(idToken, req);\n  }\n  next();\n}\n\n/**\n * Returns a Promise with the Firebase ID Token if found in the Authorization or the __session cookie.\n */\nfunction getIdTokenFromRequest(req, res) {\n  if (req.headers.authorization && req.headers.authorization.startsWith('Bearer ')) {\n    functions.logger.log('Found \"Authorization\" header');\n    // Read the ID Token from the Authorization header.\n    return Promise.resolve(req.headers.authorization.split('Bearer ')[1]);\n  }\n  return new Promise((resolve, reject) => {\n    cookieParser(req, res, () => {\n      if (req.cookies && req.cookies.__session) {\n        functions.logger.log('Found \"__session\" cookie');\n        // Read the ID Token from cookie.\n        resolve(req.cookies.__session);\n      } else {\n        resolve();\n      }\n    });\n  });\n}\n\n/**\n * Returns a Promise with the Decoded ID Token and adds it to req.user.\n */\nasync function addDecodedIdTokenToRequest(idToken, req) {\n  try {\n    const decodedIdToken = await admin.auth().verifyIdToken(idToken);\n    req.user = decodedIdToken;\n    functions.logger.log('ID Token correctly decoded', decodedIdToken);\n  } catch (error) {\n    functions.logger.error('Error while verifying Firebase ID token:', error);\n  }\n}\n\nexports.validateFirebaseIdToken = validateFirebaseIdToken;"
  },
  {
    "path": "Node-1st-gen/template-handlebars/functions/index.js",
    "content": "/**\n * Copyright 2016 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use strict';\n\nconst functions = require('firebase-functions/v1');\nconst admin = require('firebase-admin');\nadmin.initializeApp();\n\nconst express = require('express');\nconst exphbs = require('express-handlebars');\nconst app = express();\nconst firebaseUser = require('./firebaseUser');\n\napp.engine('handlebars', exphbs({defaultLayout: 'main'}));\napp.set('view engine', 'handlebars');\napp.use(firebaseUser.validateFirebaseIdToken);\n\napp.get('/', (req, res) => {\n  // @ts-ignore\n  const user = req.user;\n\n  functions.logger.log('Signed-in user:', user);\n  return res.render('user', {\n    user: user,\n  });\n});\n\n// This HTTPS endpoint can only be accessed by your Firebase Users.\n// Requests need to be authorized by providing an `Authorization` HTTP header\n// with value `Bearer <Firebase ID Token>`.\nexports.app = functions.https.onRequest(app);\n"
  },
  {
    "path": "Node-1st-gen/template-handlebars/functions/package.json",
    "content": "{\n  \"name\": \"template-handlebars-functions\",\n  \"description\": \"Use handlebars templating engine on Firebase hosting with Cloud functions\",\n  \"dependencies\": {\n    \"cookie-parser\": \"^1.4.6\",\n    \"cors\": \"^2.8.5\",\n    \"eslint-plugin-promise\": \"^7.2.1\",\n    \"express\": \"^4.18.2\",\n    \"express-handlebars\": \"^3.1.0\",\n    \"firebase-admin\": \"^13.0.2\",\n    \"firebase-functions\": \"7.0.0\",\n    \"handlebars\": \"^4.7.7\"\n  },\n  \"devDependencies\": {\n    \"eslint\": \"^8.57.1\"\n  },\n  \"scripts\": {\n    \"lint\": \"./node_modules/.bin/eslint --max-warnings=0 .\",\n    \"serve\": \"firebase emulators:start --only functions\",\n    \"shell\": \"firebase functions:shell\",\n    \"start\": \"npm run shell\",\n    \"deploy\": \"firebase deploy --only functions\",\n    \"logs\": \"firebase functions:log\",\n    \"compile\": \"cp ../../../tsconfig.template.json ./tsconfig-compile.json && tsc --project tsconfig-compile.json\"\n  },\n  \"engines\": {\n    \"node\": \"20\"\n  },\n  \"private\": true\n}\n"
  },
  {
    "path": "Node-1st-gen/template-handlebars/functions/views/layouts/main.handlebars",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <meta charset=\"utf-8\">\n  <title>Example App</title>\n\n  <!-- Import and configure the Firebase SDK -->\n  <!-- These scripts are made available when the app is served or deployed on Firebase Hosting -->\n  <!-- If you do not serve/host your project using Firebase Hosting see https://firebase.google.com/docs/web/setup -->\n  <script defer src=\"/__/firebase/3.9.0/firebase-app-compat.js\"></script>\n  <script defer src=\"/__/firebase/3.9.0/firebase-auth-compat.js\"></script>\n  <script defer src=\"/__/firebase/init.js\"></script>\n\n  <script>\n    function checkCookie() {\n      // Checks if it's likely that there is a signed-in Firebase user and the session cookie expired.\n      // In that case we'll hide the body of the page until it will be reloaded after the cookie has been set.\n      var hasSessionCookie = document.cookie.indexOf('__session=') !== -1;\n      var isProbablySignedInFirebase = typeof Object.keys(localStorage).find(function (key) {\n            return key.startsWith('firebase:authUser')\n          }) !== 'undefined';\n      if (!hasSessionCookie && isProbablySignedInFirebase) {\n        var style = document.createElement('style');\n        style.id = '__bodyHider';\n        style.appendChild(document.createTextNode('body{display:none}'));\n        document.head.appendChild(style);\n      }\n    }\n    checkCookie();\n\n    document.addEventListener('DOMContentLoaded', function() {\n      // Make sure the Firebase ID Token is always passed as a cookie.\n      firebase.auth().addAuthTokenListener(function (idToken) {\n        var hadSessionCookie = document.cookie.indexOf('__session=') !== -1;\n        document.cookie = '__session=' + idToken + ';max-age=' + (idToken ? 3600 : 0);\n        // If there is a change in the auth state compared to what's in the session cookie we'll reload after setting the cookie.\n        if ((!hadSessionCookie && idToken) || (hadSessionCookie && !idToken)) {\n          window.location.reload(true);\n        } else {\n          // In the rare case where there was a user but it could not be signed in (for instance the account has been deleted).\n          // We un-hide the page body.\n          var style = document.getElementById('__bodyHider');\n          if (style) {\n            document.head.removeChild(style);\n          }\n        }\n      });\n    });\n  </script>\n\n  <link rel=\"stylesheet\" href=\"/main.css\">\n</head>\n<body>\n\n{{{body}}}\n\n</body>\n</html>"
  },
  {
    "path": "Node-1st-gen/template-handlebars/functions/views/user.handlebars",
    "content": "{{#if user}}\n  <h1>Hello {{user.name}}!</h1>\n  Here is your UID: {{user.uid}}\n  <br><br>\n  <button onclick=\"firebase.auth().signOut()\">Sign-out</button>\n{{^}}\n  <h1>Hey there!</h1>\n  Please sign-in!\n  <br><br>\n  <button onclick=\"firebase.auth().signInWithPopup(new firebase.auth.GoogleAuthProvider())\">Sign-in with Google</button>\n{{/if}}\n"
  },
  {
    "path": "Node-1st-gen/template-handlebars/public/main.css",
    "content": "/**\n * Copyright 2017 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nhtml, body {\n  font-family: 'Roboto', 'Helvetica', sans-serif;\n}\n"
  },
  {
    "path": "Node-1st-gen/testlab-to-slack/.gitignore",
    "content": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\nfirebase-debug.log*\n\n# Firebase cache\n.firebase/\n\n# Firebase config\n\n# Uncomment this if you'd like others to create their own Firebase project.\n# For a team working on the same Firebase project(s), it is recommended to leave\n# it commented so all members can deploy to the same project(s) in .firebaserc.\n.firebaserc\n\n# Runtime data\npids\n*.pid\n*.seed\n*.pid.lock\n\n# Directory for instrumented libs generated by jscoverage/JSCover\nlib-cov\n\n# Coverage directory used by tools like istanbul\ncoverage\n\n# nyc test coverage\n.nyc_output\n\n# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)\n.grunt\n\n# Bower dependency directory (https://bower.io/)\nbower_components\n\n# node-waf configuration\n.lock-wscript\n\n# Compiled binary addons (http://nodejs.org/api/addons.html)\nbuild/Release\n\n# Dependency directories\nnode_modules/\n\n# Optional npm cache directory\n.npm\n\n# Optional eslint cache\n.eslintcache\n\n# Optional REPL history\n.node_repl_history\n\n# Output of 'npm pack'\n*.tgz\n\n# Yarn Integrity file\n.yarn-integrity\n\n# dotenv environment variables file\n.env\n"
  },
  {
    "path": "Node-1st-gen/testlab-to-slack/README.md",
    "content": "# Post Test Lab Results to Slack channel\n\nThis sample demonstrates how to post to a Slack channel in response to the\ncompletion of a test matrix in **Firebase Test Lab**. The message will look like\nthis:\n\n![example Slack message with Test Lab status](https://i.imgur.com/9DTL19x.png)\n\n## Setting up the sample\n\n1.  [Add an **Incoming Webhook**](https://my.slack.com/services/new/incoming-webhook/) to your Slack channel and take note of the **Webhook URL**.\n1.  Clone or download this repo and open this directory in a terminal:\n\n    ```shell\n    cd testlab-to-slack\n    ```\n\n1.  You must have the latest Firebase CLI installed. If you don't have it,\n    install it with `npm install -g firebase-tools` and then sign in with\n    `firebase login`.\n1.  Configure the CLI locally by using `firebase use --add` and select your\n    project in the list.\n1.  Install Cloud Functions dependencies locally by running:\n    `cd functions; npm install; cd -`\n1.  Set the following environment variables so that the function can\n    authenticate with Slack and post to the correct room:\n\n    ```bash\n    firebase functions:secrets:set SLACK_WEBHOOK_URL\n    ```\n\n## Deploy and test\n\nThis sample comes with a web-based UI for testing the function. To test it out:\n\n1.  Deploy your function using `firebase deploy --only functions`\n1.  Navigate to the\n    [Test Lab](https://console.firebase.google.com/u/0/project/_/testlab/histories)\n    section of the Firebase Console and start a test.\n1.  Once the test finishes running, check your Slack channel and view the new\n    post!\n"
  },
  {
    "path": "Node-1st-gen/testlab-to-slack/firebase.json",
    "content": "{\n    \"functions\": {\n      \"codebase\": \"testlab-to-slack\"\n    }\n}"
  },
  {
    "path": "Node-1st-gen/testlab-to-slack/functions/.gitignore",
    "content": "node_modules/"
  },
  {
    "path": "Node-1st-gen/testlab-to-slack/functions/index.js",
    "content": "/**\n * Copyright 2023 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nconst functions = require('firebase-functions/v1');\nconst {defineSecret} = require('firebase-functions/params');\n\nconst slackWebhookUrl = defineSecret('SLACK_WEBHOOK_URL');\n\n/**\n * Posts a message to Slack via a Webhook\n * @param {string} title\n * @param {string} details\n * @return {Promise<string>}\n */\n async function postToSlack(title, details) {\n  const response = await fetch(slackWebhookUrl.value(), {\n    method: \"post\",\n    body: JSON.stringify({\n      blocks: [\n        {\n          type: \"section\",\n          text: {\n            type: \"mrkdwn\",\n            text: title,\n          },\n        },\n        {\n          type: \"divider\",\n        },\n        {\n          type: \"section\",\n          text: {\n            type: \"mrkdwn\",\n            text: details,\n          },\n        },\n      ],\n    }),\n    headers: {\"Content-Type\": \"application/json\"},\n  });\n  return response.json();\n}\n\nfunction getSlackmoji(term) {\n  switch (term) {\n    case 'SUCCESS':\n      return ':tada:';\n    case 'FAILURE':\n      return ':broken_heart:';\n    case 'INCONCLUSIVE':\n      return ':question:';\n    case 'SKIPPED':\n      return ':arrow_heading_down:';\n    case 'VALIDATING':\n      return ':thought_balloon:';\n    case 'PENDING':\n      return ':soon:';\n    case 'FINISHED':\n      return ':white_check_mark:';\n    case 'ERROR':\n      return ':red_circle:';\n    case 'INVALID':\n      return ':large_orange_diamond:';\n    default:\n      return '';\n  }\n}\n\nexports.postTestResultsToSlack = functions.runWith({secrets: [slackWebhookUrl]}).testLab\n  .testMatrix()\n  .onComplete(async testMatrix => {\n    const { testMatrixId, state, outcomeSummary } = testMatrix;\n\n    const title = `${getSlackmoji(state)} ${getSlackmoji(\n      outcomeSummary\n    )} ${testMatrixId}`;\n\n    const details = `Status: *${state}* ${getSlackmoji(\n      state\n    )}\\nOutcome: *${outcomeSummary}* ${getSlackmoji(outcomeSummary)}\n    `;\n\n    const slackResponse = await postToSlack(title, details);\n\n    functions.logger.log(slackResponse);\n  });\n"
  },
  {
    "path": "Node-1st-gen/testlab-to-slack/functions/package.json",
    "content": "{\n  \"name\": \"functions\",\n  \"description\": \"Cloud Functions for Firebase\",\n  \"scripts\": {\n    \"serve\": \"firebase emulators:start --only functions\",\n    \"shell\": \"firebase functions:shell\",\n    \"start\": \"npm run shell\",\n    \"deploy\": \"firebase deploy --only functions\",\n    \"logs\": \"firebase functions:log\",\n    \"compile\": \"cp ../../../tsconfig.template.json ./tsconfig-compile.json && tsc --project tsconfig-compile.json\"\n  },\n  \"engines\": {\n    \"node\": \"20\"\n  },\n  \"dependencies\": {\n    \"eslint\": \"8\",\n    \"eslint-plugin-promise\": \"^7.2.1\",\n    \"firebase-admin\": \"^13.0.2\",\n    \"firebase-functions\": \"7.0.0\"\n  },\n  \"devDependencies\": {\n    \"firebase-functions-test\": \"^3.4.0\"\n  },\n  \"private\": true\n}\n"
  },
  {
    "path": "Node-1st-gen/text-moderation/README.md",
    "content": "# Text Moderation with Cloud Functions\n\nThis template shows how to perform server side moderation of text written to a Firebase DB.\n\nFor instance if a user added the message \"I DON'T LIKE THIS APP!! This is POOP!!!\" this will get moderated to a - more civilized - non uppercase message: \"I don't like this app. This is ****\".\n\n\n## Functions Code\n\nSee file [functions/index.js](functions/index.js) for the moderation code.\n\nModeration of the messages is performed using [bad-words](https://www.npmjs.com/package/bad-words) a bad words remover that uses an external [list of bad-words](https://github.com/web-mech/badwords-list) and is currently mostly aimed at filtering english bad words. Also messages that contains mostly upper case characters are re-capitalized correctly using [capitalize-sentence](https://www.npmjs.com/package/capitalize-sentence).\n\nThe dependencies are listed in [functions/package.json](functions/package.json).\n\n\n## Sample Database Structure\n\nUsers anonymously add a message - an object with a `text` attribute - to the `/messages` list:\n\n```\n/functions-project-12345\n    /messages\n        /key-123456\n            text: \"This is my first message!\"\n        /key-123457\n            text: \"IN THIS MESSAGE I AM SHOUTING!!!\"\n```\n\nOnce the function has ran on the newly added messages it adds two attributes. `sanitized` which is `true` if message has been looked at and `moderated` which is `true` if it was detected that the message contained offensive content and was modified:\n\n```\n/functions-project-12345\n    /messages\n        /key-123456\n            text: \"This is my first message!\",\n            sanitized: true,\n            moderated: false\n        /key-123457\n            text: \"In this message I am shouting.\"\n            sanitized: true,\n            moderated: true\n```\n\n\n## Trigger rules\n\nThe function triggers every time a message is modified. It exits if the message has already been moderated.\n\n\n## Security Rules\n\nThe security rules only allow users to create message but not edit them afterwards. Also it does not allow users to set the `sanitized` value. Only the Cloud Function is allowed to modify `sanitized` by using an admin authorized reference.\n\n\n## Deploy and test\n\nThis sample comes with a Function and web-based UI for testing the function. To configure it:\n\n 1. Create a Firebase Project using the [Firebase Console](https://console.firebase.google.com).\n 1. Clone or download this repo and open the `text-moderation` directory.\n 1. You must have the Firebase CLI installed. If you don't have it install it with `npm install -g firebase-tools` and then configure it with `firebase login`.\n 1. Configure the CLI locally by using `firebase use --add` and select your project in the list.\n 1. Install dependencies locally by running: `cd functions; npm install; cd -`\n 1. Deploy your project using `firebase deploy`\n 1. Open the app using `firebase open hosting:site`, this will open a browser.\n 1. Open the app and add messages to the message board. Try to ad bad words into your message and they should get moderated.\n"
  },
  {
    "path": "Node-1st-gen/text-moderation/firebase.json",
    "content": "{\n  \"functions\": {\n    \"codebase\": \"text-moderation\"\n  },\n  \"database\": {\n    \"rules\": \"security.rules.json\"\n  },\n  \"hosting\": {\n    \"public\": \"public\"\n  }\n}\n"
  },
  {
    "path": "Node-1st-gen/text-moderation/functions/.eslintrc.json",
    "content": "{\n  \"parserOptions\": {\n    // Required for certain syntax usages\n    \"ecmaVersion\": 2022\n  },\n  \"plugins\": [\n    \"promise\"\n  ],\n  \"extends\": \"eslint:recommended\",\n  \"rules\": {\n    // Removed rule \"disallow the use of console\" from recommended eslint rules\n    \"no-console\": \"off\",\n\n    // Removed rule \"disallow multiple spaces in regular expressions\" from recommended eslint rules\n    \"no-regex-spaces\": \"off\",\n\n    // Removed rule \"disallow the use of debugger\" from recommended eslint rules\n    \"no-debugger\": \"off\",\n\n    // Removed rule \"disallow unused variables\" from recommended eslint rules\n    \"no-unused-vars\": \"off\",\n\n    // Removed rule \"disallow mixed spaces and tabs for indentation\" from recommended eslint rules\n    \"no-mixed-spaces-and-tabs\": \"off\",\n\n    // Removed rule \"disallow the use of undeclared variables unless mentioned in /*global */ comments\" from recommended eslint rules\n    \"no-undef\": \"off\",\n\n    // Warn against template literal placeholder syntax in regular strings\n    \"no-template-curly-in-string\": 1,\n\n    // Warn if return statements do not either always or never specify values\n    \"consistent-return\": 1,\n\n    // Warn if no return statements in callbacks of array methods\n    \"array-callback-return\": 1,\n\n    // Requre the use of === and !==\n    \"eqeqeq\": 2,\n\n    // Disallow the use of alert, confirm, and prompt\n    \"no-alert\": 2,\n\n    // Disallow the use of arguments.caller or arguments.callee\n    \"no-caller\": 2,\n\n    // Disallow null comparisons without type-checking operators\n    \"no-eq-null\": 2,\n\n    // Disallow the use of eval()\n    \"no-eval\": 2,\n\n    // Warn against extending native types\n    \"no-extend-native\": 1,\n\n    // Warn against unnecessary calls to .bind()\n    \"no-extra-bind\": 1,\n\n    // Warn against unnecessary labels    \n    \"no-extra-label\": 1,\n\n    // Disallow leading or trailing decimal points in numeric literals\n    \"no-floating-decimal\": 2,\n\n    // Warn against shorthand type conversions\n    \"no-implicit-coercion\": 1,\n\n    // Warn against function declarations and expressions inside loop statements\n    \"no-loop-func\": 1,\n\n    // Disallow new operators with the Function object\n    \"no-new-func\": 2,\n\n    // Warn against new operators with the String, Number, and Boolean objects\n    \"no-new-wrappers\": 1,\n\n    // Disallow throwing literals as exceptions\n    \"no-throw-literal\": 2,\n\n    // Require using Error objects as Promise rejection reasons\n    \"prefer-promise-reject-errors\": 2,\n\n    // Enforce “for” loop update clause moving the counter in the right direction\n    \"for-direction\": 2,\n\n    // Enforce return statements in getters\n    \"getter-return\": 2,\n\n    // Disallow await inside of loops\n    \"no-await-in-loop\": 2,\n\n    // Disallow comparing against -0\n    \"no-compare-neg-zero\": 2,\n\n    // Warn against catch clause parameters from shadowing variables in the outer scope\n    \"no-catch-shadow\": 1,\n\n    // Disallow identifiers from shadowing restricted names\n    \"no-shadow-restricted-names\": 2,\n\n    // Enforce return statements in callbacks of array methods\n    \"callback-return\": 2,\n\n    // Require error handling in callbacks\n    \"handle-callback-err\": 2,\n\n    // Warn against string concatenation with __dirname and __filename\n    \"no-path-concat\": 1,\n\n    // Prefer using arrow functions for callbacks\n    \"prefer-arrow-callback\": 1,\n\n    // Return inside each then() to create readable and reusable Promise chains.\n    \"promise/always-return\": 2,\n\n    //Enforces the use of catch() on un-returned promises\n    \"promise/catch-or-return\": 2,\n\n    // Warn against nested then() or catch() statements\n    \"promise/no-nesting\": 1\n  }\n}\n"
  },
  {
    "path": "Node-1st-gen/text-moderation/functions/index.js",
    "content": "/**\n * Copyright 2016 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use strict';\n\nconst functions = require('firebase-functions/v1');\nconst Filter = require('bad-words');\nconst badWordsFilter = new Filter();\n\n// Moderates messages by lowering all uppercase messages and removing swearwords.\nexports.moderator = functions.database.ref('/messages/{messageId}').onWrite((change) => {\n  const message = change.after.val();\n\n  if (message && !message.sanitized) {\n    // Retrieved the message values.\n    functions.logger.log('Retrieved message content: ', message);\n\n    // Run moderation checks on on the message and moderate if needed.\n    const moderatedMessage = moderateMessage(message.text);\n\n    // Update the Firebase DB with checked message.\n    functions.logger.log(\n      'Message has been moderated. Saving to DB: ',\n      moderatedMessage\n    );\n    return change.after.ref.update({\n      text: moderatedMessage,\n      sanitized: true,\n      moderated: message.text !== moderatedMessage,\n    });\n  }\n  return null;\n});\n\n// Moderates the given message if appropriate.\nfunction moderateMessage(message) {\n  // Re-capitalize if the user is Shouting.\n  if (isShouting(message)) {\n    functions.logger.log('User is shouting. Fixing sentence case...');\n    message = stopShouting(message);\n  }\n\n  // Moderate if the user uses SwearWords.\n  if (containsSwearwords(message)) {\n    functions.logger.log('User is swearing. moderating...');\n    message = moderateSwearwords(message);\n  }\n\n  return message;\n}\n\n// Returns true if the string contains swearwords.\nfunction containsSwearwords(message) {\n  return message !== badWordsFilter.clean(message);\n}\n\n// Hide all swearwords. e.g: Crap => ****.\nfunction moderateSwearwords(message) {\n  return badWordsFilter.clean(message);\n}\n\n// Detect if the current message is shouting. i.e. there are too many Uppercase\n// characters or exclamation points.\nfunction isShouting(message) {\n  return message.replace(/[^A-Z]/g, '').length > message.length / 2 || message.replace(/[^!]/g, '').length >= 3;\n}\n\n// Correctly capitalize the string as a sentence (e.g. uppercase after dots)\n// and remove exclamation points.\nfunction stopShouting(message) {\n  const sentenceCaseRegex = /(:?\\.\\s?|^)([A-Za-z\\u00C0-\\u1FFF\\u2800-\\uFFFD])/gi;\n  const noExclamationsRegex = /!+/g;\n\n  return message\n    .toLowerCase()\n    .replace(sentenceCaseRegex, (match) => match.toUpperCase())\n    .replace(noExclamationsRegex, \".\");\n}\n"
  },
  {
    "path": "Node-1st-gen/text-moderation/functions/package.json",
    "content": "{\n  \"name\": \"text-moderation-functions\",\n  \"description\": \"Moderate text using Firebase Functions\",\n  \"dependencies\": {\n    \"bad-words\": \"^3.0.4\",\n    \"eslint-plugin-promise\": \"^7.2.1\",\n    \"firebase-admin\": \"^13.0.2\",\n    \"firebase-functions\": \"7.0.0\"\n  },\n  \"devDependencies\": {\n    \"eslint\": \"^8.57.1\"\n  },\n  \"scripts\": {\n    \"lint\": \"./node_modules/.bin/eslint --max-warnings=0 .\",\n    \"serve\": \"firebase emulators:start --only functions\",\n    \"shell\": \"firebase functions:shell\",\n    \"start\": \"npm run shell\",\n    \"deploy\": \"firebase deploy --only functions\",\n    \"logs\": \"firebase functions:log\",\n    \"compile\": \"cp ../../../tsconfig.template.json ./tsconfig-compile.json && tsc --project tsconfig-compile.json\"\n  },\n  \"engines\": {\n    \"node\": \"20\"\n  },\n  \"private\": true\n}\n"
  },
  {
    "path": "Node-1st-gen/text-moderation/public/index.html",
    "content": "<!doctype html>\n<!--\n  Copyright 2016 Google Inc. All rights reserved.\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n      https://www.apache.org/licenses/LICENSE-2.0\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License\n-->\n<html lang=\"en\">\n<head>\n  <meta charset=\"utf-8\">\n  <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n  <meta name=\"description\" content=\"Demonstrates the use of Google Cloud Functions with a Firebase DB\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n  <title>A Guestbook. But moderated...</title>\n\n  <!-- Disable tap highlight on IE -->\n  <meta name=\"msapplication-tap-highlight\" content=\"no\">\n\n  <!-- Tile icon for Win8 -->\n  <meta name=\"msapplication-TileImage\" content=\"images/touch/touch-icon-144x144.png\">\n  <meta name=\"msapplication-TileColor\" content=\"#3372DF\">\n  <meta name=\"msapplication-navbutton-color\" content=\"#303F9F\">\n\n  <!-- Material Design Theming -->\n  <link rel=\"stylesheet\" href=\"https://code.getmdl.io/1.1.3/material.blue_grey-orange.min.css\">\n  <link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css?family=Roboto:regular,bold,italic,thin,light,bolditalic,black,medium&amp;lang=en\">\n  <link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/icon?family=Material+Icons\">\n  <script defer src=\"https://code.getmdl.io/1.1.3/material.min.js\"></script>\n\n  <link rel=\"stylesheet\" href=\"main.css\">\n</head>\n<body>\n<div class=\"demo-layout mdl-layout mdl-js-layout mdl-layout--fixed-header\">\n\n  <!-- Header section containing logo -->\n  <header class=\"mdl-layout__header mdl-color-text--white mdl-color--primary\">\n    <div class=\"mdl-cell mdl-cell--12-col mdl-cell--12-col-tablet mdl-grid\">\n      <div class=\"mdl-layout__header-row mdl-cell mdl-cell--12-col mdl-cell--12-col-tablet mdl-cell--12-col-desktop\">\n        <h3><i class=\"material-icons\">chrome_reader_mode</i> A Guestbook</h3><span>but moderated...</span>\n      </div>\n    </div>\n  </header>\n\n  <main class=\"mdl-layout__content mdl-color--grey-100\">\n    <div id=\"message-list\" class=\"mdl-cell mdl-cell--12-col mdl-cell--12-col-tablet mdl-grid\">\n\n      <!-- Explanation card -->\n      <div class=\"mdl-cell--4-col mdl-cell--4-col-tablet mdl-cell--8-col-desktop explanation mdl-card mdl-shadow--2dp mdl-cell mdl-card__supporting-text mdl-color-text--grey-600\">\n        Dear friends,<br><br>\n        Feel free to leave a message on this guestbook. Messages are moderated to prevent bad words or yelling and keep things civilized.\n        Try it out by leaving your own mean or nice (but mean is more fun) message.<br><br>\n        Love.\n      </div>\n\n      <!-- Card containing the inputs to add a new message to the Guestbook -->\n      <div class=\"mdl-card mdl-shadow--2dp mdl-cell\">\n        <div class=\"mdl-card__title mdl-color--orange-500\">\n          <h2 class=\"mdl-card__title-text\">Add new message</h2>\n        </div>\n        <div class=\"mdl-card__supporting-text mdl-color-text--grey-600\">\n          <form id=\"message-form\" action=\"#\">\n            <div class=\"mdl-textfield mdl-js-textfield mdl-textfield--floating-label\">\n              <input class=\"mdl-textfield__input\" type=\"text\" id=\"name\">\n              <label class=\"mdl-textfield__label\" for=\"name\">Name...</label>\n            </div>\n            <div class=\"mdl-textfield mdl-js-textfield mdl-textfield--floating-label\">\n              <textarea class=\"mdl-textfield__input\" rows= \"3\" id=\"message\"></textarea>\n              <label class=\"mdl-textfield__label\" for=\"message\">Message text...</label>\n            </div>\n            <button id=\"submit\" disabled type=\"submit\" class=\"mdl-button mdl-js-button mdl-button--raised mdl-js-ripple-effect mdl-button--accent\">\n              Add message\n            </button>\n          </form>\n        </div>\n      </div>\n\n      <!-- Container for the last Guestbook's Messages -->\n      <div id=\"message-title\" class=\"mdl-card mdl-shadow--2dp mdl-cell mdl-cell--12-col mdl-cell--12-col-tablet mdl-cell--12-col-desktop\">\n        <div class=\"mdl-card__title mdl-color--orange-200\">\n          <h2 class=\"mdl-card__title-text\">Last Guestbook's messages</h2>\n        </div>\n      </div>\n    </div>\n  </main>\n</div>\n\n\n<!-- Import and configure the Firebase SDK -->\n<!-- These scripts are made available when the app is served or deployed on Firebase Hosting -->\n<!-- If you do not serve/host your project using Firebase Hosting see https://firebase.google.com/docs/web/setup -->\n<script src=\"/__/firebase/10.0.0/firebase-app-compat.js\"></script>\n<script src=\"/__/firebase/10.0.0/firebase-database-compat.js\"></script>\n<script src=\"/__/firebase/init.js\"></script>\n\n<script src=\"main.js\"></script>\n</body>\n</html>\n"
  },
  {
    "path": "Node-1st-gen/text-moderation/public/main.css",
    "content": "/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nhtml, body {\n  font-family: 'Roboto', 'Helvetica', sans-serif;\n}\n.mdl-layout__header-row span {\n  margin-left: 15px;\n  margin-top: 17px;\n}\n.mdl-grid {\n  max-width: 1024px;\n  margin: auto;\n}\n.material-icons {\n  font-size: 36px;\n  top: 5px;\n  position: relative;\n}\n.mdl-layout__header-row {\n  padding: 0;\n  margin: 0;\n}\n#message-form {\n  display: flex;\n  flex-direction: column;\n}\n#message-form button {\n  max-width: 300px;\n}\n#message-list {\n  padding: 0;\n  margin-top: 20px;\n  margin-bottom: 20px;\n  width: 100%;\n}\n#message-title {\n  margin-top: 20px;\n}\n.mdl-card {\n  min-height: 0;\n}\n.message-card {\n  opacity: 0;\n  transition: opacity 0.5s ease-in-out;\n}\n.message-card.visible {\n  opacity: 1;\n}\n.message {\n  font-size: 28px;\n  line-height: 32px;\n  font-weight: 200;\n  padding-bottom: 30px;\n}\n.author {\n  position: absolute;\n  bottom: 10px;\n  right: 20px;\n  font-style: italic;\n}\n.moderated {\n  position: absolute;\n  bottom: 10px;\n  font-weight: 100;\n  font-size: 10px;\n}\n.explanation {\n  font-size: 20px;\n  font-weight: 100;\n  line-height: 25px;\n}\n.mdl-card {\n  background: linear-gradient(white, #f9f9f9);\n  justify-content: space-between;\n}\n"
  },
  {
    "path": "Node-1st-gen/text-moderation/public/main.js",
    "content": "/**\n * Copyright 2015 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use strict';\n\n// Initializes the Guestbook.\nfunction Guestbook() {\n\n  // Shortcuts to DOM Elements.\n  this.messageList = document.getElementById('message-list');\n  this.messageForm = document.getElementById('message-form');\n  this.messageInput = document.getElementById('message');\n  this.nameInput = document.getElementById('name');\n  this.submitButton = document.getElementById('submit');\n\n  // Saves message on form submit.\n  this.messageForm.addEventListener('submit', this.saveMessage.bind(this));\n\n  // Toggle for the button.\n  var buttonTogglingHandler = this.toggleButton.bind(this);\n  this.messageInput.addEventListener('keyup', buttonTogglingHandler);\n  this.nameInput.addEventListener('keyup', buttonTogglingHandler);\n  this.messageInput.addEventListener('change', buttonTogglingHandler);\n  this.nameInput.addEventListener('change', buttonTogglingHandler);\n\n  // Function calling displayMessage with correct attributes from Firebase data.\n  var callDisplayMessage = function (data) {\n    var val = data.val();\n    this.displayMessage(data.key, val.name, val.text, val.moderated);\n  }.bind(this);\n\n  // Loads the last 12 messages and listen for new ones.\n  Guestbook.fbMessagesRef.limitToLast(12).on('child_added', callDisplayMessage);\n  // Listen for messages updates.\n  Guestbook.fbMessagesRef.limitToLast(12).on('child_changed', callDisplayMessage);\n  // Listen for messages updates.\n  Guestbook.fbMessagesRef.limitToLast(12).on('child_removed', callDisplayMessage);\n}\n\n// Reference to the new messages feed in the Firebase DB.\nGuestbook.fbMessagesRef = firebase.database().ref('/messages');\n\n// Saves a new message on the Firebase DB.\nGuestbook.prototype.saveMessage = function(e) {\n  e.preventDefault();\n  if (this.messageInput.value && this.nameInput.value) {\n    Guestbook.fbMessagesRef.push({\n      name: this.nameInput.value,\n      text: this.messageInput.value,\n      timestamp: firebase.database.ServerValue.TIMESTAMP\n    }, function (error) {\n      if (error) {\n        console.log(error);\n      } else {\n        Guestbook.resetMaterialTextfield(this.messageInput);\n        Guestbook.resetMaterialTextfield(this.nameInput);\n        this.toggleButton();\n      }\n    }.bind(this));\n  }\n};\n\n// Resets the given MaterialTextField.\nGuestbook.resetMaterialTextfield = function(element) {\n  element.value = '';\n  element.parentNode.MaterialTextfield.boundUpdateClassesHandler();\n  element.blur();\n};\n\n// Template for message cards.\nGuestbook.MESSAGE_CARD_TEMPLATE =\n  '<div class=\"mdl-card mdl-cell mdl-cell--12-col mdl-card__supporting-text mdl-shadow--2dp ' +\n              'message-card mdl-cell--4-col-tablet mdl-cell--4-col-desktop\">' +\n      '<div class=\"message\"></div>' +\n      '<div class=\"author\"></div>' +\n      '<div class=\"moderated\">(This message has been moderated)</div>' +\n  '</div>';\n\n// Displays a Visitor's Book Message in the UI.\nGuestbook.prototype.displayMessage = function(key, name, message, moderated) {\n  var div = document.getElementById(key);\n  // If an element for that message does not exists yet we create it.\n  if (!div) {\n    var container = document.createElement('div');\n    container.innerHTML = Guestbook.MESSAGE_CARD_TEMPLATE;\n    div = container.firstChild;\n    div.setAttribute('id', key);\n    this.messageList.insertBefore(div, document.getElementById('message-title').nextSibling);\n  }\n  div.querySelector('.author').textContent = name;\n  div.querySelector('.moderated').style.visibility = moderated ? 'visible' : 'hidden';\n  var messageElement = div.querySelector('.message');\n  messageElement.textContent = message;\n  // Replace all line breaks by <br>.\n  messageElement.innerHTML = messageElement.innerHTML.replace(/\\n/g, '<br>');\n  // Show the card fading-in.\n  setTimeout(function() {div.classList.add('visible')}, 1);\n};\n\n// Enables or disables the submit button depending on the values of the input\n// fields.\nGuestbook.prototype.toggleButton = function() {\n  if (this.messageInput.value && this.nameInput.value) {\n    this.submitButton.removeAttribute('disabled');\n  } else {\n    this.submitButton.setAttribute('disabled', 'true');\n  }\n};\n\n// Bindings on load.\nwindow.addEventListener('load', function() {\n  new Guestbook();\n});\n"
  },
  {
    "path": "Node-1st-gen/text-moderation/security.rules.json",
    "content": "{\n  \"rules\": {\n    \"messages\": {\n      \".read\": true,\n      \"$message\": {\n        // Users can only add new messages to the review queue.\n        \".write\": \"!data.exists()\",\n        // Users can't set the sanitized boolean themselves.\n        \".validate\": \"!newData.hasChildren(['sanitized'])\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "Node-1st-gen/url-shortener/README.md",
    "content": "# Shortens URLs automatically using the Bit.ly Shortener API.\n\nThis template shows how to shorten URLs automatically as they are added.\n\n\n## Functions Code\n\nSee file [functions/index.js](functions/index.js) for the code.\n\nThis uses the [Bit.ly API](https://dev.bitly.com/).\n\nThe dependencies are listed in [functions/package.json](functions/package.json).\n\n## Setting up the sample\n\n - Create a Firebase project using the [Firebase Console](https://console.firebase.google.com).\n - Create a [Bit.ly application](https://bitly.com/a/oauth_apps) and generate an **Access Tokens**.\n - Set the sample to use your Firebase project using `firebase use --add` and select your new Firebase project.\n - Set your Bit.ly app's access token on your function by running:\n     ```bash\n     firebase functions:secrets:set BITLY_ACCESS_TOKEN\n     ```\n - Deploy the function using `firebase deploy`\n - Manually add an object to the Realtime Database following the structure described below.\n\n\n## Sample Database Structure\n\nWe'll be using a simple database structure:\n\n```\n/functions-project-12345\n    /links\n        link-123456: \"https://my.super.long-link.com/api/user/profile/-jEHitne10395-k3593085\"\n```\n\nWhen a new URL (string) is pushed to `/links`, it gets replaced with an object containing the original URL and a shortened one.\nThis way, you can display a clean URL by fetching `/links/$linkId/short`.\n\n```\n/functions-project-12345\n    /links\n        /link-123456\n            original: \"https://my.super.long-link.com/api/user/profile/-jEHitne10395-k3593085\",\n            short: \"https://goo.gl/EKDdza\"\n```\n"
  },
  {
    "path": "Node-1st-gen/url-shortener/firebase.json",
    "content": "{\n    \"functions\": {\n      \"codebase\": \"url-shortener\"\n    }\n}"
  },
  {
    "path": "Node-1st-gen/url-shortener/functions/.eslintrc.json",
    "content": "{\n  \"parserOptions\": {\n    // Required for certain syntax usages\n    \"ecmaVersion\": 2022\n  },\n  \"plugins\": [\n    \"promise\"\n  ],\n  \"extends\": \"eslint:recommended\",\n  \"rules\": {\n    // Removed rule \"disallow the use of console\" from recommended eslint rules\n    \"no-console\": \"off\",\n\n    // Removed rule \"disallow multiple spaces in regular expressions\" from recommended eslint rules\n    \"no-regex-spaces\": \"off\",\n\n    // Removed rule \"disallow the use of debugger\" from recommended eslint rules\n    \"no-debugger\": \"off\",\n\n    // Removed rule \"disallow unused variables\" from recommended eslint rules\n    \"no-unused-vars\": \"off\",\n\n    // Removed rule \"disallow mixed spaces and tabs for indentation\" from recommended eslint rules\n    \"no-mixed-spaces-and-tabs\": \"off\",\n\n    // Removed rule \"disallow the use of undeclared variables unless mentioned in /*global */ comments\" from recommended eslint rules\n    \"no-undef\": \"off\",\n\n    // Warn against template literal placeholder syntax in regular strings\n    \"no-template-curly-in-string\": 1,\n\n    // Warn if return statements do not either always or never specify values\n    \"consistent-return\": 1,\n\n    // Warn if no return statements in callbacks of array methods\n    \"array-callback-return\": 1,\n\n    // Requre the use of === and !==\n    \"eqeqeq\": 2,\n\n    // Disallow the use of alert, confirm, and prompt\n    \"no-alert\": 2,\n\n    // Disallow the use of arguments.caller or arguments.callee\n    \"no-caller\": 2,\n\n    // Disallow null comparisons without type-checking operators\n    \"no-eq-null\": 2,\n\n    // Disallow the use of eval()\n    \"no-eval\": 2,\n\n    // Warn against extending native types\n    \"no-extend-native\": 1,\n\n    // Warn against unnecessary calls to .bind()\n    \"no-extra-bind\": 1,\n\n    // Warn against unnecessary labels    \n    \"no-extra-label\": 1,\n\n    // Disallow leading or trailing decimal points in numeric literals\n    \"no-floating-decimal\": 2,\n\n    // Warn against shorthand type conversions\n    \"no-implicit-coercion\": 1,\n\n    // Warn against function declarations and expressions inside loop statements\n    \"no-loop-func\": 1,\n\n    // Disallow new operators with the Function object\n    \"no-new-func\": 2,\n\n    // Warn against new operators with the String, Number, and Boolean objects\n    \"no-new-wrappers\": 1,\n\n    // Disallow throwing literals as exceptions\n    \"no-throw-literal\": 2,\n\n    // Require using Error objects as Promise rejection reasons\n    \"prefer-promise-reject-errors\": 2,\n\n    // Enforce “for” loop update clause moving the counter in the right direction\n    \"for-direction\": 2,\n\n    // Enforce return statements in getters\n    \"getter-return\": 2,\n\n    // Disallow await inside of loops\n    \"no-await-in-loop\": 2,\n\n    // Disallow comparing against -0\n    \"no-compare-neg-zero\": 2,\n\n    // Warn against catch clause parameters from shadowing variables in the outer scope\n    \"no-catch-shadow\": 1,\n\n    // Disallow identifiers from shadowing restricted names\n    \"no-shadow-restricted-names\": 2,\n\n    // Enforce return statements in callbacks of array methods\n    \"callback-return\": 2,\n\n    // Require error handling in callbacks\n    \"handle-callback-err\": 2,\n\n    // Warn against string concatenation with __dirname and __filename\n    \"no-path-concat\": 1,\n\n    // Prefer using arrow functions for callbacks\n    \"prefer-arrow-callback\": 1,\n\n    // Return inside each then() to create readable and reusable Promise chains.\n    \"promise/always-return\": 2,\n\n    //Enforces the use of catch() on un-returned promises\n    \"promise/catch-or-return\": 2,\n\n    // Warn against nested then() or catch() statements\n    \"promise/no-nesting\": 1\n  }\n}\n"
  },
  {
    "path": "Node-1st-gen/url-shortener/functions/index.js",
    "content": "/**\n * Copyright 2017 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use strict';\n\nconst functions = require('firebase-functions/v1');\nconst {onInit} = require('firebase-functions/v1/init');\nconst {defineSecret} = require('firebase-functions/params');\nconst { BitlyClient } = require('bitly');\n// TODO: Make sure to set the `BITLY_ACCESS_TOKEN` secret using the CLI.\nconst bitlyAccessToken = defineSecret('BITLY_ACCESS_TOKEN');\n\nlet bitly;\nonInit(() => {\n  bitly = new BitlyClient(bitlyAccessToken.value());\n});\n\n// Shorten URL written to /links/{linkID}.\nexports.shortenUrl = functions.runWith({secrets: [bitlyAccessToken]}).database.ref('/links/{linkID}').onCreate(async (snap) => {\n  const originalUrl = snap.val();\n  const response = await bitly.shorten(originalUrl);\n  // @ts-ignore\n  const shortUrl = response.url;\n\n  return snap.ref.set({\n    original: originalUrl,\n    short: shortUrl,\n  })\n});\n"
  },
  {
    "path": "Node-1st-gen/url-shortener/functions/package.json",
    "content": "{\n  \"name\": \"url-shortener-functions\",\n  \"description\": \"URL Shortener Firebase Functions sample\",\n  \"dependencies\": {\n    \"bitly\": \"^6.1.0\",\n    \"eslint-plugin-promise\": \"^7.2.1\",\n    \"firebase-admin\": \"^13.0.2\",\n    \"firebase-functions\": \"7.0.0\"\n  },\n  \"devDependencies\": {\n    \"eslint\": \"^8.57.1\"\n  },\n  \"scripts\": {\n    \"lint\": \"./node_modules/.bin/eslint --max-warnings=0 .\",\n    \"serve\": \"firebase emulators:start --only functions\",\n    \"shell\": \"firebase functions:shell\",\n    \"start\": \"npm run shell\",\n    \"deploy\": \"firebase deploy --only functions\",\n    \"logs\": \"firebase functions:log\",\n    \"compile\": \"cp ../../../tsconfig.template.json ./tsconfig-compile.json && tsc --project tsconfig-compile.json\"\n  },\n  \"engines\": {\n    \"node\": \"20\"\n  },\n  \"private\": true\n}\n"
  },
  {
    "path": "Node-1st-gen/user-data-cleanup/README.md",
    "content": "# Wipeout user data when account deleted\n\n**This code has moved to its own repo at\nhttps://github.com/firebase/user-data-protection**\n\n(For archeologists, Git history prior to mid October 2017 is here, while\nsubsequent history is in the new repo.)\n"
  },
  {
    "path": "Node-1st-gen/user-data-cleanup/doc/auto_rules_extraction.md",
    "content": "**Moved to https://github.com/firebase/user-data-protection/blob/main/doc/auto_rules_extraction.md**\n\n"
  },
  {
    "path": "Node-1st-gen/user-data-cleanup/doc/design.md",
    "content": "**Moved to https://github.com/firebase/user-data-protection/blob/main/doc/design.md**"
  },
  {
    "path": "Node-1st-gen/username-password-auth/README.md",
    "content": "# Username/password sign in with Firebase\n\nThis sample shows how to authenticate with a username/password to Sign-In with Firebase. In this sample we use a basic authentication request with the supplied credentials and if successful create a Firebase Custom Token.\n\nNOTE: [Firebase Authentication](https://firebase.google.com/docs/auth/web/password-auth) can be used for email address/password sign in. This sample specifically addresses the case in which an email address is not used as the unique identifier for a user.\n\n## Set up the sample\n\nCreate and set up the Firebase project:\n 1. Create a Firebase project using the [Firebase Developer Console](https://console.firebase.google.com).\n 1. Enable Billing on your Firebase project by switching to the **Blaze** plan, this is currently needed to be able to perform HTTP requests to external services from a Cloud Function.\n 1. Copy the Web initialization snippet from **Firebase Console > Overview > Add Firebase to your web app** and paste it in `public/index.html` in lieu of the placeholder (where the `TODO(DEVELOPER)` is located).\n\nCreate and provide a Service Account's credentials:\n 1. Create a service account file as described in the [Server SDK setup instructions](https://firebase.google.com/docs/server/setup#add_firebase_to_your_app).\n 1. Save the service account credential file as `./functions/service-account.json`\n\nDeploy your project:\n 1. Run `firebase use --add` and choose your Firebase project. This will configure the Firebase CLI to use the correct project locally.\n 1. Run `firebase deploy` to effectively deploy the sample. The first time the Functions are deployed the process can take several minutes.\n\n\n## Run the sample\n\nOpen the sample's website by using `firebase open hosting:site` or directly accessing `https://<project-id>.firebaseapp.com/`.\n\nEnter credentials and click on the **Sign in** button. At this point you are authenticated in Firebase and can use the database/hosting etc...\n\n## Workflow and design\n\nWhen clicking the **Sign in** button the `auth` Cloud Function authenticates the username/password then Mints and returns a Firebase Custom Auth token (which is why we need service accounts credentials).\n\nThe [httpbin](https://httpbin.org) request & response service is used for the basic authentication request in this example.\n\nThe main page will detect the sign-in through the Firebase Auth State observer and display the signed-in user information.\n\nNOTE: In production you'll need to update the placeholder `authenticate` function in `./functions/index.js` so that it authenticates with your own credentials system.\n"
  },
  {
    "path": "Node-1st-gen/username-password-auth/database.rules.json",
    "content": "{\n  \"rules\": {\n      \"$uid\": {\n        \".read\": \"auth.uid === $uid\",\n        \".write\": \"auth.uid === $uid\"\n      }\n  }\n}\n"
  },
  {
    "path": "Node-1st-gen/username-password-auth/firebase.json",
    "content": "{\n  \"functions\": {\n    \"codebase\": \"username-password-auth\"\n  },\n  \"database\": {\n    \"rules\": \"database.rules.json\"\n  },\n  \"hosting\": {\n    \"public\": \"public\"\n  }\n}\n"
  },
  {
    "path": "Node-1st-gen/username-password-auth/functions/.eslintrc.json",
    "content": "{\n  \"parserOptions\": {\n    // Required for certain syntax usages\n    \"ecmaVersion\": 2022\n  },\n  \"plugins\": [\n    \"promise\"\n  ],\n  \"extends\": \"eslint:recommended\",\n  \"rules\": {\n    // Removed rule \"disallow the use of console\" from recommended eslint rules\n    \"no-console\": \"off\",\n\n    // Removed rule \"disallow multiple spaces in regular expressions\" from recommended eslint rules\n    \"no-regex-spaces\": \"off\",\n\n    // Removed rule \"disallow the use of debugger\" from recommended eslint rules\n    \"no-debugger\": \"off\",\n\n    // Removed rule \"disallow unused variables\" from recommended eslint rules\n    \"no-unused-vars\": \"off\",\n\n    // Removed rule \"disallow mixed spaces and tabs for indentation\" from recommended eslint rules\n    \"no-mixed-spaces-and-tabs\": \"off\",\n\n    // Removed rule \"disallow the use of undeclared variables unless mentioned in /*global */ comments\" from recommended eslint rules\n    \"no-undef\": \"off\",\n\n    // Warn against template literal placeholder syntax in regular strings\n    \"no-template-curly-in-string\": 1,\n\n    // Warn if return statements do not either always or never specify values\n    \"consistent-return\": 1,\n\n    // Warn if no return statements in callbacks of array methods\n    \"array-callback-return\": 1,\n\n    // Requre the use of === and !==\n    \"eqeqeq\": 2,\n\n    // Disallow the use of alert, confirm, and prompt\n    \"no-alert\": 2,\n\n    // Disallow the use of arguments.caller or arguments.callee\n    \"no-caller\": 2,\n\n    // Disallow null comparisons without type-checking operators\n    \"no-eq-null\": 2,\n\n    // Disallow the use of eval()\n    \"no-eval\": 2,\n\n    // Warn against extending native types\n    \"no-extend-native\": 1,\n\n    // Warn against unnecessary calls to .bind()\n    \"no-extra-bind\": 1,\n\n    // Warn against unnecessary labels    \n    \"no-extra-label\": 1,\n\n    // Disallow leading or trailing decimal points in numeric literals\n    \"no-floating-decimal\": 2,\n\n    // Warn against shorthand type conversions\n    \"no-implicit-coercion\": 1,\n\n    // Warn against function declarations and expressions inside loop statements\n    \"no-loop-func\": 1,\n\n    // Disallow new operators with the Function object\n    \"no-new-func\": 2,\n\n    // Warn against new operators with the String, Number, and Boolean objects\n    \"no-new-wrappers\": 1,\n\n    // Disallow throwing literals as exceptions\n    \"no-throw-literal\": 2,\n\n    // Require using Error objects as Promise rejection reasons\n    \"prefer-promise-reject-errors\": 2,\n\n    // Enforce “for” loop update clause moving the counter in the right direction\n    \"for-direction\": 2,\n\n    // Enforce return statements in getters\n    \"getter-return\": 2,\n\n    // Disallow await inside of loops\n    \"no-await-in-loop\": 2,\n\n    // Disallow comparing against -0\n    \"no-compare-neg-zero\": 2,\n\n    // Warn against catch clause parameters from shadowing variables in the outer scope\n    \"no-catch-shadow\": 1,\n\n    // Disallow identifiers from shadowing restricted names\n    \"no-shadow-restricted-names\": 2,\n\n    // Enforce return statements in callbacks of array methods\n    \"callback-return\": 2,\n\n    // Require error handling in callbacks\n    \"handle-callback-err\": 2,\n\n    // Warn against string concatenation with __dirname and __filename\n    \"no-path-concat\": 1,\n\n    // Prefer using arrow functions for callbacks\n    \"prefer-arrow-callback\": 1,\n\n    // Return inside each then() to create readable and reusable Promise chains.\n    \"promise/always-return\": 2,\n\n    //Enforces the use of catch() on un-returned promises\n    \"promise/catch-or-return\": 2,\n\n    // Warn against nested then() or catch() statements\n    \"promise/no-nesting\": 1\n  }\n}\n"
  },
  {
    "path": "Node-1st-gen/username-password-auth/functions/index.js",
    "content": "/**\n * Copyright 2017 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for t`he specific language governing permissions and\n * limitations under the License.\n */\n'use strict';\n\nconst functions = require('firebase-functions/v1');\n\n// CORS Express middleware to enable CORS Requests.\nconst cors = require('cors')({origin: true});\n\n// Firebase Setup\nconst admin = require('firebase-admin');\n// @ts-ignore\nconst serviceAccount = require('./service-account.json');\nadmin.initializeApp({\n  credential: admin.credential.cert(serviceAccount),\n  databaseURL: `https://${process.env.GCLOUD_PROJECT}.firebaseio.com`,\n});\n\n/**\n * Authenticate the provided credentials returning a Firebase custom auth token.\n * `username` and `password` values are expected in the body of the request.\n * If authentication fails return a 401 response.\n * If the request is badly formed return a 400 response.\n * If the request method is unsupported (not POST) return a 403 response.\n * If an error occurs log the details and return a 500 response.\n */\nexports.auth = functions.https.onRequest((req, res) => {\n  const handleError = (username, error) => {\n    functions.logger.error({ User: username }, error);\n    res.sendStatus(500);\n    return;\n  };\n\n  const handleResponse = (username, status, body) => {\n    functions.logger.log(\n      { User: username },\n      {\n        Response: {\n          Status: status,\n          Body: body,\n        },\n      }\n    );\n    if (body) {\n      return res.status(200).json(body);\n    }\n    return res.sendStatus(status);\n  };\n\n  let username = '';\n  try {\n    return cors(req, res, async () => {\n      // Authentication requests are POSTed, other requests are forbidden\n      if (req.method !== 'POST') {\n        return handleResponse(username, 403);\n      }\n      username = req.body.username;\n      if (!username) {\n        return handleResponse(username, 400);\n      }\n      const password = req.body.password;\n      if (!password) {\n        return handleResponse(username, 400);\n      }\n\n      // TODO(DEVELOPER): In production you'll need to update the `authenticate` function so that it authenticates with your own credentials system.\n      const valid = await authenticate(username, password)\n      if (!valid) {\n        return handleResponse(username, 401); // Invalid username/password\n      }\n\n      // On success return the Firebase Custom Auth Token.\n      const firebaseToken = await admin.auth().createCustomToken(username);\n      return handleResponse(username, 200, { token: firebaseToken });\n    });\n  } catch (error) {\n    return handleError(username, error);\n  }\n});\n\n/**\n * Authenticate the provided credentials.\n * TODO(DEVELOPER): In production you'll need to update this function so that it authenticates with your own credentials system.\n * @returns {Promise<boolean>} success or failure.\n */\nasync function authenticate(username, password) {\n  // For the purpose of this example use httpbin (https://httpbin.org) and send a basic authentication request.\n  // (Only a password of `Testing123` will succeed)\n  const authEndpoint = `https://httpbin.org/basic-auth/${username}/Testing123`;\n  const response = await fetch(authEndpoint, {\n    headers: {\n      Authorization: 'Basic ' + Buffer.from(username + \":\" + password).toString('base64')\n    }\n  });\n\n  if (response.status === 200) {\n    return true;\n  } else if (response.status === 401) {\n    return false\n  } else {\n    throw new Error(`invalid response returned from ${authEndpoint} status code ${response.status}`)\n  }\n}\n"
  },
  {
    "path": "Node-1st-gen/username-password-auth/functions/package.json",
    "content": "{\n  \"name\": \"username-password-auth-function\",\n  \"description\": \"Authenticate with username/password Firebase Function sample\",\n  \"dependencies\": {\n    \"cors\": \"^2.8.5\",\n    \"eslint-plugin-promise\": \"^7.2.1\",\n    \"firebase-admin\": \"^13.0.2\",\n    \"firebase-functions\": \"7.0.0\"\n  },\n  \"devDependencies\": {\n    \"eslint\": \"^8.57.1\"\n  },\n  \"scripts\": {\n    \"lint\": \"./node_modules/.bin/eslint --max-warnings=0 .\",\n    \"serve\": \"firebase emulators:start --only functions\",\n    \"shell\": \"firebase functions:shell\",\n    \"start\": \"npm run shell\",\n    \"deploy\": \"firebase deploy --only functions\",\n    \"logs\": \"firebase functions:log\",\n    \"compile\": \"cp ../../../tsconfig.template.json ./tsconfig-compile.json && tsc --project tsconfig-compile.json\"\n  },\n  \"engines\": {\n    \"node\": \"20\"\n  },\n  \"private\": true\n}\n"
  },
  {
    "path": "Node-1st-gen/username-password-auth/public/index.html",
    "content": "<!doctype html>\n<!--\n  Copyright 2016 Google Inc. All rights reserved.\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n      https://www.apache.org/licenses/LICENSE-2.0\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License\n-->\n<html lang=\"en\">\n<head>\n  <meta charset=\"utf-8\">\n  <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n  <meta name=\"description\" content=\"Demonstrates how to authorize Firebase with a username/password using Firebase Functions\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n  <title>Firebase Functions demo to Sign In with username/password</title>\n\n  <!-- Material Design Lite -->\n  <link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/icon?family=Material+Icons\">\n  <link rel=\"stylesheet\" href=\"https://code.getmdl.io/1.1.3/material.blue_grey-orange.min.css\">\n  <script defer src=\"https://code.getmdl.io/1.1.3/material.min.js\"></script>\n\n  <link rel=\"stylesheet\" href=\"main.css\">\n</head>\n<body>\n<div class=\"demo-layout mdl-layout mdl-js-layout mdl-layout--fixed-header\">\n\n  <!-- Header section containing title -->\n  <header class=\"mdl-layout__header mdl-color-text--white mdl-color--light-blue-700\">\n    <div class=\"mdl-cell mdl-cell--12-col mdl-cell--12-col-tablet mdl-grid\">\n      <div class=\"mdl-layout__header-row mdl-cell mdl-cell--12-col mdl-cell--12-col-tablet mdl-cell--8-col-desktop\">\n        <h3>Sign in with username/password demo</h3>\n      </div>\n    </div>\n  </header>\n  <main class=\"mdl-layout__content mdl-color--grey-100\">\n    <div class=\"mdl-cell--12-col mdl-cell--12-col-tablet mdl-grid\">\n\n      <!-- Card containing the sign-in UI -->\n      <div id=\"demo-signed-out-card\" class=\"mdl-card mdl-shadow--2dp mdl-cell\">\n        <div class=\"mdl-card__supporting-text mdl-color-text--grey-600\">\n          <p>\n            This web application demonstrates how you can Sign In with a username/password using Firebase Authentication.\n            For the purpose of this example only a password of `Testing123` will succeed.\n            <strong>Now sign in!</strong>\n          </p>\n          <div class=\"mdl-textfield mdl-js-textfield mdl-textfield--floating-label\">\n            <input class=\"mdl-textfield__input\" type=\"text\" id=\"demo-sign-in-username\">\n            <label class=\"mdl-textfield__label\" for=\"demo-sign-in-username\">Username</label>\n          </div>\n          <div class=\"mdl-textfield mdl-js-textfield mdl-textfield--floating-label\">\n            <input class=\"mdl-textfield__input\" type=\"password\" id=\"demo-sign-in-password\">\n            <label class=\"mdl-textfield__label\" for=\"demo-sign-in-password\">Password</label>\n          </div>\n          <button id=\"demo-sign-in-button\" class=\"mdl-color-text--grey-700 mdl-button--raised mdl-button mdl-js-button mdl-js-ripple-effect\"><i class=\"material-icons\">account_circle</i> Sign in</button>\n          <p>\n            <strong id=\"demo-sign-in-error\" class=\"mdl-color-text--orange-900\"></strong>\n          </p>\n        </div>\n      </div>\n\n      <!-- Card containing the signed-in UI -->\n      <div id=\"demo-signed-in-card\" class=\"mdl-card mdl-shadow--2dp mdl-cell\">\n        <div class=\"mdl-card__supporting-text mdl-color-text--grey-600\">\n          <p>\n            Welcome <span id=\"demo-name-container\"></span><br>\n            Your Firebase User ID is: <span id=\"demo-uid-container\"></span><br>\n          </p>\n          <button id=\"demo-sign-out-button\" class=\"mdl-color-text--grey-700 mdl-button--raised mdl-button mdl-js-button mdl-js-ripple-effect\">Sign out</button>\n          <button id=\"demo-delete-button\" class=\"mdl-color-text--grey-700 mdl-button--raised mdl-button mdl-js-button mdl-js-ripple-effect\">Delete account</button>\n        </div>\n      </div>\n    </div>\n  </main>\n</div>\n\n<!-- Firebase -->\n<!-- ***********************************************************************************************************************\n     * TODO(DEVELOPER): Paste the initialization snippet from: Firebase Console > Overview > Add Firebase to your web app. *\n     *********************************************************************************************************************** -->\n\n<script src=\"main.js\"></script>\n</body>\n</html>\n"
  },
  {
    "path": "Node-1st-gen/username-password-auth/public/main.css",
    "content": "/**\n * Copyright 2016 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nhtml, body {\n  font-family: 'Roboto', 'Helvetica', sans-serif;\n}\n.mdl-grid {\n  max-width: 1024px;\n  margin: auto;\n}\n.mdl-card {\n  min-height: 0;\n  padding-bottom: 5px;\n}\n.mdl-layout__header-row {\n  padding: 0;\n}\nh3 {\n  background: url('firebase-logo.png') no-repeat;\n  background-size: 40px;\n  padding-left: 50px;\n}\n#demo-signed-out-card,\n#demo-signed-in-card {\n  display: none;\n}\n\n#demo-name-container,\n#demo-email-container,\n#demo-uid-container {\n  font-weight: bold;\n}\n#demo-delete-button {\n  margin-left: 20px;\n}\n"
  },
  {
    "path": "Node-1st-gen/username-password-auth/public/main.js",
    "content": "/**\n * Copyright 2016 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use strict';\n\n/**\n * Returns the ID of the Firebase project.\n */\nfunction getFirebaseProjectId() {\n  return firebase.app().options.authDomain.split('.')[0];\n}\n\n// Initializes the Demo.\nfunction Demo() {\n  document.addEventListener('DOMContentLoaded', function() {\n    // Shortcuts to DOM Elements.\n    this.signInUsername = document.getElementById('demo-sign-in-username');\n    this.signInPassword = document.getElementById('demo-sign-in-password');\n    this.signInButton = document.getElementById('demo-sign-in-button');\n    this.signInError = document.getElementById('demo-sign-in-error');\n    this.signOutButton = document.getElementById('demo-sign-out-button');\n    this.nameContainer = document.getElementById('demo-name-container');\n    this.uidContainer = document.getElementById('demo-uid-container');\n    this.deleteButton = document.getElementById('demo-delete-button');\n    this.signedOutCard = document.getElementById('demo-signed-out-card');\n    this.signedInCard = document.getElementById('demo-signed-in-card');\n\n    // Bind events.\n    this.signInButton.addEventListener('click', this.signIn.bind(this));\n    this.signOutButton.addEventListener('click', this.signOut.bind(this));\n    this.deleteButton.addEventListener('click', this.deleteAccount.bind(this));\n    firebase.auth().onAuthStateChanged(this.onAuthStateChanged.bind(this));\n  }.bind(this));\n}\n\n// Triggered on Firebase auth state change.\nDemo.prototype.onAuthStateChanged = function(user) {\n  if (user) {\n    this.nameContainer.innerText = user.displayName;\n    this.uidContainer.innerText = user.uid;\n    this.signedOutCard.style.display = 'none';\n    this.signedInCard.style.display = 'block';\n  } else {\n    this.signedOutCard.style.display = 'block';\n    this.signedInCard.style.display = 'none';\n  }\n};\n\n// Initiates the sign-in flow.\nDemo.prototype.signIn = function() {\n  var err = this.signInError;\n  err.innerText = '';\n  var req = new XMLHttpRequest();\n  req.onload = function() {\n    if (req.status === 400 || req.status === 401) {\n      err.innerText = 'Invalid username or password';\n      return;\n    }\n    if (req.status !== 200) {\n      err.innerText = 'Invalid response from Firebase Cloud Function ' + req.status;\n      return;\n    }\n    var data = JSON.parse(req.responseText);\n    if (data.token) {\n      firebase.auth().signInWithCustomToken(data.token);\n    } else {\n      console.log('ERROR RESPONSE: ' + req.responseText);\n      err.innerText = 'Invalid response from Firebase Cloud Function see developer console for details';\n    }\n  };\n  req.onerror = function() {\n    err.innerText = 'Network error in Firebase Cloud Function call see developer console for details';\n  };\n  var url = 'https://us-central1-' + getFirebaseProjectId() + '.cloudfunctions.net/auth';\n  req.open('POST', url, true);\n  req.setRequestHeader('Content-Type', 'application/json');\n  req.send(JSON.stringify({\n    username: this.signInUsername.value,\n    password: this.signInPassword.value\n  }));\n};\n\n// Signs-out of Firebase.\nDemo.prototype.signOut = function() {\n  firebase.auth().signOut();\n};\n\n// Deletes the user's account.\nDemo.prototype.deleteAccount = function() {\n  firebase.auth().currentUser.delete().then(function() {\n    window.alert('Account deleted');\n  }).catch(function(error) {\n    if (error.code === 'auth/requires-recent-login') {\n      window.alert('You need to have recently signed-in to delete your account. Please sign-in and try again.');\n      firebase.auth().signOut();\n    }\n  });\n};\n\n// Load the demo.\nnew Demo();\n"
  },
  {
    "path": "Node-1st-gen/vision-annotate-images/README.md",
    "content": "# Vision Annotate Images\n\nThis function acts as a pass through to the [Cloud Vision JSON API](https://cloud.google.com/vision/docs/request). \n\nTo call this from an app, you must first sign-in using [Firebase Auth](https://firebase.google.com/docs/auth).\nOnly users who pass a valid Firebase ID token as a Bearer token in the `Authorization` header of the HTTP request are authorized to use the function.\n\nExplore the ready-to-use APIs: [text recognition](https://firebase.google.com/docs/ml/recognize-text), [image labeling](https://firebase.google.com/docs/ml/label-images), and [landmark recognition](https://firebase.google.com/docs/ml/recognize-landmarks)\n\n\n## Contributing\n\nWe'd love that you contribute to the project. Before doing so please read our [Contributor guide](../CONTRIBUTING.md).\n\n\n## License\n\n© Google, 2017. Licensed under an [Apache-2](../LICENSE) license.\n"
  },
  {
    "path": "Node-1st-gen/vision-annotate-images/firebase.json",
    "content": "{\n  \"functions\": {\n    \"codebase\": \"vision-annotate-images\",\n    \"predeploy\": [\n      \"npm --prefix \\\"$RESOURCE_DIR\\\" run lint\",\n      \"npm --prefix \\\"$RESOURCE_DIR\\\" run build\"\n    ]\n  }\n}\n"
  },
  {
    "path": "Node-1st-gen/vision-annotate-images/functions/.gitignore",
    "content": "lib/*.js\nlib/*.js.map"
  },
  {
    "path": "Node-1st-gen/vision-annotate-images/functions/eslint.config.js",
    "content": "/**\n * Copyright 2023 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nconst eslint = require('@eslint/js');\nconst tseslint = require('typescript-eslint');\n\nmodule.exports = tseslint.config(\n  eslint.configs.recommended,\n  tseslint.configs.recommended,\n);\n"
  },
  {
    "path": "Node-1st-gen/vision-annotate-images/functions/package.json",
    "content": "{\n  \"name\": \"functions\",\n  \"scripts\": {\n    \"lint\": \"eslint \\\"src/**/*\\\"\",\n    \"build\": \"tsc --project ./tsconfig.json\",\n    \"serve\": \"npm run build && firebase emulators:start --only functions\",\n    \"shell\": \"npm run build && firebase functions:shell\",\n    \"start\": \"npm run shell\",\n    \"deploy\": \"firebase deploy --only functions\",\n    \"logs\": \"firebase functions:log\",\n    \"compile\": \"npm run build\"\n  },\n  \"engines\": {\n    \"node\": \"20\"\n  },\n  \"main\": \"lib/index.js\",\n  \"dependencies\": {\n    \"@google-cloud/vision\": \"^2.1.2\",\n    \"firebase-admin\": \"^13.0.2\",\n    \"firebase-functions\": \"7.0.0\"\n  },\n  \"devDependencies\": {\n    \"@eslint/js\": \"^9.19.0\",\n    \"eslint\": \"^9.19.0\",\n    \"firebase-functions-test\": \"^3.4.0\",\n    \"typescript\": \"^5.7.3\",\n    \"typescript-eslint\": \"^8.22.0\"\n  },\n  \"private\": true\n}\n"
  },
  {
    "path": "Node-1st-gen/vision-annotate-images/functions/src/index.ts",
    "content": "/**\n * Copyright 2023 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport * as functions from \"firebase-functions/v1\";\nimport vision from \"@google-cloud/vision\";\n\nconst client = new vision.ImageAnnotatorClient();\n\n// This will allow only requests with an auth token to access the Vision\n// API, including anonymous ones.\n// It is highly recommended to limit access only to signed-in users. This may\n// be done by adding the following condition to the if statement:\n//    || context.auth.token?.firebase?.sign_in_provider === 'anonymous'\n// \n// For more fine-grained control, you may add additional failure checks, ie:\n//    || context.auth.token?.firebase?.email_verified === false\n// Also see: https://firebase.google.com/docs/auth/admin/custom-claims\nexport const annotateImage = functions.https.onCall(async (data, context) => {\n  if (!context?.auth) {\n    throw new functions.https.HttpsError(\n      \"unauthenticated\",\n      \"annotateImage must be called while authenticated.\"\n    );\n  }\n  try {\n    return await client.annotateImage(data);\n  } catch (e) {\n\n    throw new functions.https.HttpsError(\"internal\", (e as Error).message);\n  }\n});\n"
  },
  {
    "path": "Node-1st-gen/vision-annotate-images/functions/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"module\": \"commonjs\",\n    \"noImplicitReturns\": true,\n    \"noUnusedLocals\": true,\n    \"outDir\": \"lib\",\n    \"sourceMap\": true,\n    \"strict\": true,\n    \"target\": \"es2017\",\n    \"lib\" : [\n      \"esnext\"\n    ],\n    \"skipLibCheck\": true,\n  },\n  \"compileOnSave\": true,\n  \"include\": [\n    \"src\"\n  ]\n}\n"
  },
  {
    "path": "Node-1st-gen/youtube/README.md",
    "content": "# YouTube: Get information about a YouTube channel\n\nThis quickstart demonstrates how to query the\n[YouTube Data API](https://developers.google.com/youtube/v3) using **Cloud\nFunctions for Firebase** with an HTTPS trigger.\n\n## Introduction\n\nThe function `getChannelInfo` returns information about a Youtube channel. By\ndefault it will return information about the\n[Firebase YouTube channel](https://www.youtube.com/user/Firebase), but you can pass it a\n`channelId` URL Query parameter to query any channel you'd like.\n\n## Setup\n\n### Get a YouTube API Key\n\n1. Create a Firebase Project on the\n   [Firebase Console](https://console.firebase.google.com) if you don't already have a project you want to use.\n   1. Upgrade your Firebase project to the\n      [Blaze \"pay as you go\" plan](https://firebase.google.com/pricing)\n1. Enable the Youtube API by visiting the\n   [API console](http://console.cloud.google.com/marketplace/product/google/youtube.googleapis.com),\n   selecting your Firebase project, and clicking \"ENABLE\".\n   1. Once the API is enabled, visit the\n      [credentials tab](http://console.cloud.google.com/apis/api/youtube.googleapis.com/credentials)\n      and click \"CREATE CREDENTIALS\" to create a YouTube API key.\n\n### Clone and configure the function\n\n1. Install the Firebase CLI and log in:\n   ```\n   npm install --global firebase-tools\n\n   firebase login\n   ```\n1. Clone or download this repo and open the `youtube` directory.\n1. `cd` into the `functions` directory and install dependencies with `npm install`\n1. Set up your Firebase project by running `firebase use --add` with the\n   Firebase CLI, select your Project ID and follow the instructions.\n1. Set the YouTube API key as an environment variable:\n    ```bash\n    firebase functions:secrets:set YOUTUBE_KEY\n    ```\n\n### Run your function locally with the Firebase Emulator Suite\n\n1. Set up the Firebase emulators with your config ([docs](https://firebase.google.com/docs/functions/local-emulator#set_up_functions_configuration_optional)):\n    ```bash\n    cd functions\n    ```\n1. Run the following command to start the emulator:\n    ```bash\n    firebase emulators:start --only functions\n    ```\n1. Check the emulator output to find the URL of the `getChannelInfo` function. It will looks something like `http://localhost:5001/my-project-id/us-central1/getChannelInfo`\n1. Via CURL or in your browser, visit the URL that the function is running at. Optionally, add a query string `?channelId=SOME_CHANNEL_ID` to the end of the URL.\n1. You should get a JSON response with information about the YouTube channel!\n\n\n## Deploy the app to prod\n\nDeploy to Firebase using the following command:\n\n```bash\nfirebase deploy\n```\n\nThis deploys and activates the `getChannelInfo` function.\n\n> The first time you call `firebase deploy` on a new project with Functions will take longer than usual.\n\n## Modify it to your needs\n\nNow that you've got this sample working, modify it to work for your use case! Some ideas:\n\n- Check out the other things you can query with the [YouTube Data API](https://developers.google.com/youtube/v3/docs)\n- Convert `getChannelInfo` function to a scheduled function, and write the new latest videos for a channel into Firestore or Realtime Database\n- ...anything else you can think of!"
  },
  {
    "path": "Node-1st-gen/youtube/firebase.json",
    "content": "{\n  \"functions\": {\n    \"codebase\": \"youtube\",\n    \"predeploy\": [\n      \"npm --prefix \\\"$RESOURCE_DIR\\\" run lint\"\n    ],\n    \"source\": \"functions\"\n  },\n  \"emulators\": {\n    \"functions\": {\n      \"port\": 5001\n    },\n    \"ui\": {\n      \"enabled\": true\n    }\n  }\n}\n"
  },
  {
    "path": "Node-1st-gen/youtube/functions/.eslintrc.json",
    "content": "{\n  \"parserOptions\": {\n    // Required for certain syntax usages\n    \"ecmaVersion\": 2022\n  },\n  \"plugins\": [\n    \"promise\"\n  ],\n  \"extends\": \"eslint:recommended\",\n  \"rules\": {\n    // Removed rule \"disallow the use of console\" from recommended eslint rules\n    \"no-console\": \"off\",\n\n    // Removed rule \"disallow multiple spaces in regular expressions\" from recommended eslint rules\n    \"no-regex-spaces\": \"off\",\n\n    // Removed rule \"disallow the use of debugger\" from recommended eslint rules\n    \"no-debugger\": \"off\",\n\n    // Removed rule \"disallow unused variables\" from recommended eslint rules\n    \"no-unused-vars\": \"off\",\n\n    // Removed rule \"disallow mixed spaces and tabs for indentation\" from recommended eslint rules\n    \"no-mixed-spaces-and-tabs\": \"off\",\n\n    // Removed rule \"disallow the use of undeclared variables unless mentioned in /*global */ comments\" from recommended eslint rules\n    \"no-undef\": \"off\",\n\n    // Warn against template literal placeholder syntax in regular strings\n    \"no-template-curly-in-string\": 1,\n\n    // Warn if return statements do not either always or never specify values\n    \"consistent-return\": 1,\n\n    // Warn if no return statements in callbacks of array methods\n    \"array-callback-return\": 1,\n\n    // Require the use of === and !==\n    \"eqeqeq\": 2,\n\n    // Disallow the use of alert, confirm, and prompt\n    \"no-alert\": 2,\n\n    // Disallow the use of arguments.caller or arguments.callee\n    \"no-caller\": 2,\n\n    // Disallow null comparisons without type-checking operators\n    \"no-eq-null\": 2,\n\n    // Disallow the use of eval()\n    \"no-eval\": 2,\n\n    // Warn against extending native types\n    \"no-extend-native\": 1,\n\n    // Warn against unnecessary calls to .bind()\n    \"no-extra-bind\": 1,\n\n    // Warn against unnecessary labels    \n    \"no-extra-label\": 1,\n\n    // Disallow leading or trailing decimal points in numeric literals\n    \"no-floating-decimal\": 2,\n\n    // Warn against shorthand type conversions\n    \"no-implicit-coercion\": 1,\n\n    // Warn against function declarations and expressions inside loop statements\n    \"no-loop-func\": 1,\n\n    // Disallow new operators with the Function object\n    \"no-new-func\": 2,\n\n    // Warn against new operators with the String, Number, and Boolean objects\n    \"no-new-wrappers\": 1,\n\n    // Disallow throwing literals as exceptions\n    \"no-throw-literal\": 2,\n\n    // Require using Error objects as Promise rejection reasons\n    \"prefer-promise-reject-errors\": 2,\n\n    // Enforce “for” loop update clause moving the counter in the right direction\n    \"for-direction\": 2,\n\n    // Enforce return statements in getters\n    \"getter-return\": 2,\n\n    // Disallow await inside of loops\n    \"no-await-in-loop\": 2,\n\n    // Disallow comparing against -0\n    \"no-compare-neg-zero\": 2,\n\n    // Warn against catch clause parameters from shadowing variables in the outer scope\n    \"no-catch-shadow\": 1,\n\n    // Disallow identifiers from shadowing restricted names\n    \"no-shadow-restricted-names\": 2,\n\n    // Enforce return statements in callbacks of array methods\n    \"callback-return\": 2,\n\n    // Require error handling in callbacks\n    \"handle-callback-err\": 2,\n\n    // Warn against string concatenation with __dirname and __filename\n    \"no-path-concat\": 1,\n\n    // Prefer using arrow functions for callbacks\n    \"prefer-arrow-callback\": 1,\n\n    // Return inside each then() to create readable and reusable Promise chains.\n    // Forces developers to return console logs and http calls in promises. \n    \"promise/always-return\": 2,\n\n    //Enforces the use of catch() on un-returned promises\n    \"promise/catch-or-return\": 2,\n\n    // Warn against nested then() or catch() statements\n    \"promise/no-nesting\": 1\n  }\n}\n"
  },
  {
    "path": "Node-1st-gen/youtube/functions/index.js",
    "content": "/**\n * Copyright 2023 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nconst functions = require('firebase-functions/v1');\nconst {onInit} = require('firebase-functions/v1/init');\nconst {defineSecret} = require('firebase-functions/params');\nconst { google } = require('googleapis');\n\nconst youtubeKey = defineSecret('YOUTUBE_KEY');\n\nconst FIREBASE_YOUTUBE_CHANNEL_ID = 'UCP4bf6IHJJQehibu6ai__cg';\n\nlet youtube;\nonInit(() => {\n  youtube = google.youtube({\n    version: 'v3',\n    auth: youtubeKey.value(),\n  });\n});\n\nexports.getChannelInfo = functions.runWith({secrets: [youtubeKey]}).https.onRequest(\n  async (request, response) => {\n    const channelId = request.query.channelId || FIREBASE_YOUTUBE_CHANNEL_ID;\n\n    // Fetch channel information\n    // https://developers.google.com/youtube/v3/docs/channels/list\n    const { data: channelData } = await youtube.channels.list({\n      part: 'snippet,statistics',\n      id: channelId,\n      maxResults: 1,\n    });\n\n    if (!channelData.items || channelData.items.length !== 1) {\n      response.send(`Channel with ID ${channelId} not found.`);\n      return;\n    }\n\n    const channel = channelData.items[0];\n\n    // Fetch the channel's latest videos\n    // https://developers.google.com/youtube/v3/docs/search/list\n    const { data: videoData } = await youtube.search.list({\n      part: 'id, snippet',\n      order: 'date',\n      channelId,\n      maxResults: 3,\n    });\n\n    const videos = videoData.items || [];\n\n    const channelDetails = {\n      id: channelId,\n      channelTitle: channel.snippet.title,\n      channelDescription: channel.snippet.description,\n      subscriberCount: channel.statistics.subscriberCount,\n      recentVideos: videos.map((video) => {\n        return {\n          videoTitle: video.snippet.title,\n          videoUrl: `https://www.youtube.com/watch?v=${video.id.videoId}`,\n          videoDescription: video.snippet.description,\n        };\n      }),\n    };\n\n    // Respond with channel details as JSON\n    const channelJSON = JSON.stringify(channelDetails, null, 2);\n    response.writeHead(200, {\n      'Content-Length': channelJSON.length,\n      'Content-Type': 'application/json',\n    });\n    response.write(channelJSON);\n    response.end();\n  }\n);\n"
  },
  {
    "path": "Node-1st-gen/youtube/functions/package.json",
    "content": "{\n  \"name\": \"functions\",\n  \"description\": \"Cloud Functions for Firebase\",\n  \"scripts\": {\n    \"lint\": \"eslint .\",\n    \"serve\": \"firebase emulators:start --only functions\",\n    \"shell\": \"firebase functions:shell\",\n    \"start\": \"npm run shell\",\n    \"deploy\": \"firebase deploy --only functions\",\n    \"logs\": \"firebase functions:log\"\n  },\n  \"engines\": {\n    \"node\": \"20\"\n  },\n  \"main\": \"index.js\",\n  \"dependencies\": {\n    \"eslint-plugin-promise\": \"^7.2.1\",\n    \"firebase-admin\": \"^13.0.2\",\n    \"firebase-functions\": \"7.0.0\",\n    \"googleapis\": \"^66.0.0\"\n  },\n  \"devDependencies\": {\n    \"eslint\": \"^8.57.1\",\n    \"firebase-functions-test\": \"^3.4.0\"\n  },\n  \"private\": true\n}\n"
  },
  {
    "path": "Python/.gitignore",
    "content": "venv"
  },
  {
    "path": "Python/README.md",
    "content": "# Python (2nd gen) samples\n\n> Note: Python support in Cloud Functions for Firebase is a public preview. This means that the functionality might change in backward-incompatible ways. A preview release is not subject to any SLA or deprecation policy and may receive limited or no support.\n\nThis folder contains examples for Python functions. See a description of all samples in this folder in the [root README](../README.md)."
  },
  {
    "path": "Python/alerts-to-discord/README.md",
    "content": "# alerts-to-discord\n"
  },
  {
    "path": "Python/alerts-to-discord/firebase.json",
    "content": "{\n  \"functions\": {\n    \"codebase\": \"alerts-to-discord\"\n  }\n}\n"
  },
  {
    "path": "Python/alerts-to-discord/functions/main.py",
    "content": "# Copyright 2023 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport pprint\n\n# [START v2import]\nfrom firebase_functions import params\nfrom firebase_functions.alerts import app_distribution_fn, crashlytics_fn, performance_fn\n# [END v2import]\n\nimport requests\n\nDISCORD_WEBHOOK_URL = params.SecretParam(\"DISCORD_WEBHOOK_URL\")\n\n\ndef post_message_to_discord(bot_name: str, message_body: str,\n                            webhook_url: str) -> requests.Response:\n    \"\"\"Posts a message to Discord with Discord's Webhook API.\n\n    Params:\n        bot_name: The bot username to display\n        message_body: The message to post (Discord Markdown)\n    \"\"\"\n    if webhook_url == \"\":\n        raise EnvironmentError(\n            \"No webhook URL found. Set the Discord Webhook URL before deploying. \"\n            \"Learn more about Discord webhooks here: \"\n            \"https://support.discord.com/hc/en-us/articles/228383668-Intro-to-Webhooks\")\n\n    return requests.post(\n        url=webhook_url,\n        json={\n            # Here's what the Discord API supports in the payload:\n            # https://discord.com/developers/docs/resources/webhook#execute-webhook-jsonform-params\n            \"username\": bot_name,\n            \"content\": message_body\n        })\n\n\n# [START v2Alerts]\n# [START v2CrashlyticsAlertTrigger]\n@crashlytics_fn.on_new_fatal_issue_published(secrets=[\"DISCORD_WEBHOOK_URL\"])\ndef post_fatal_issue_to_discord(event: crashlytics_fn.CrashlyticsNewFatalIssueEvent) -> None:\n    \"\"\"Publishes a message to Discord whenever a new Crashlytics fatal issue occurs.\"\"\"\n# [END v2CrashlyticsAlertTrigger]\n    # [START v2CrashlyticsEventPayload]\n    # Construct a helpful message to send to Discord.\n    app_id = event.app_id\n    issue = event.data.payload.issue\n    message = f\"\"\"\n🚨 New fatal issue for {app_id} in version {issue.app_version} 🚨\n\n# {issue.title}\n\n{issue.subtitle}\n\nID: `{issue.id}`\n\"\"\".strip()\n    # [END v2CrashlyticsEventPayload]\n\n    try:\n        # [START v2SendToDiscord]\n        response = post_message_to_discord(\"Crashlytics Bot\", message, DISCORD_WEBHOOK_URL.value)\n        if response.ok:\n            print(f\"Posted fatal Crashlytics alert {issue.id} for {app_id} to Discord.\")\n            pprint.pp(event.data.payload)\n        else:\n            response.raise_for_status()\n        # [END v2SendToDiscord]\n    except (EnvironmentError, requests.HTTPError) as error:\n        print(f\"Unable to post fatal Crashlytics alert {issue.id} for {app_id} to Discord.\", error)\n\n\n# [START v2AppDistributionAlertTrigger]\n@app_distribution_fn.on_new_tester_ios_device_published(secrets=[\"DISCORD_WEBHOOK_URL\"])\ndef post_new_udid_to_discord(event: app_distribution_fn.NewTesterDeviceEvent) -> None:\n    \"\"\"Publishes a message to Discord whenever someone registers a new iOS test device.\"\"\"\n# [END v2AppDistributionAlertTrigger]\n    # [START v2AppDistributionEventPayload]\n    # Construct a helpful message to send to Discord.\n    app_id = event.app_id\n    app_dist = event.data.payload\n    message = f\"\"\"\n📱 New iOS device registered by {app_dist.tester_name} <{app_dist.tester_email}> for {app_id}\n\nUDID **{app_dist.tester_device_identifier}** for {app_dist.tester_device_model_name}\n\"\"\".strip()\n    # [END v2AppDistributionEventPayload]\n\n    try:\n        # [START v2SendNewTesterIosDeviceToDiscord]\n        response = post_message_to_discord(\"App Distro Bot\", message, DISCORD_WEBHOOK_URL.value)\n        if response.ok:\n            print(f\"Posted iOS device registration alert for {app_dist.tester_email} to Discord.\")\n            pprint.pp(event.data.payload)\n        else:\n            response.raise_for_status()\n        # [END v2SendNewTesterIosDeviceToDiscord]\n    except (EnvironmentError, requests.HTTPError) as error:\n        print(\n            f\"Unable to post iOS device registration alert for {app_dist.tester_email} to Discord.\",\n            error)\n\n\n# [START v2PerformanceAlertTrigger]\n@performance_fn.on_threshold_alert_published(secrets=[\"DISCORD_WEBHOOK_URL\"])\ndef post_performance_alert_to_discord(event: performance_fn.PerformanceThresholdAlertEvent) -> None:\n    \"\"\"Publishes a message to Discord whenever a performance threshold alert is fired.\"\"\"\n# [END v2PerformanceAlertTrigger]\n    # [START v2PerformanceEventPayload]\n    # Construct a helpful message to send to Discord.\n    app_id = event.app_id\n    perf = event.data.payload\n    message = f\"\"\"\n⚠️ Performance Alert for {perf.metric_type} of {perf.event_type}: **{perf.event_name}** ⚠️\n\nApp ID: {app_id}\nAlert condition: {perf.threshold_value} {perf.threshold_unit}\nPercentile (if applicable): {perf.condition_percentile}\nApp version (if applicable): {perf.app_version}\n\nViolation: {perf.violation_value} {perf.violation_unit}\nNumber of samples checked: {perf.num_samples}\n\n**Investigate more:** {perf.investigate_uri}\n\"\"\".strip()\n    # [END v2PerformanceEventPayload]\n\n    try:\n        # [START v2SendPerformanceAlertToDiscord]\n        response = post_message_to_discord(\"App Performance Bot\", message,\n                                           DISCORD_WEBHOOK_URL.value)\n        if response.ok:\n            print(f\"Posted Firebase Performance alert {perf.event_name} to Discord.\")\n            pprint.pp(event.data.payload)\n        else:\n            response.raise_for_status()\n        # [END v2SendPerformanceAlertToDiscord]\n    except (EnvironmentError, requests.HTTPError) as error:\n        print(f\"Unable to post Firebase Performance alert {perf.event_name} to Discord.\", error)\n# [END v2Alerts]\n"
  },
  {
    "path": "Python/alerts-to-discord/functions/requirements.txt",
    "content": "firebase-functions\nrequests\n"
  },
  {
    "path": "Python/delete-unused-accounts-cron/README.md",
    "content": "# delete-unused-accounts-cron\n"
  },
  {
    "path": "Python/delete-unused-accounts-cron/firebase.json",
    "content": "{\n  \"functions\": {\n    \"codebase\": \"delete-unused-accounts-cron\"\n  }\n}\n"
  },
  {
    "path": "Python/delete-unused-accounts-cron/functions/main.py",
    "content": "# Copyright 2023 Google Inc. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# [START all]\nfrom datetime import datetime, timedelta\n\n# [START import]\n# The Cloud Functions for Firebase SDK to set up triggers and logging.\nfrom firebase_functions import scheduler_fn\n\n# The Firebase Admin SDK to delete users.\nimport firebase_admin\nfrom firebase_admin import auth\n\nfirebase_admin.initialize_app()\n# [END import]\n\n\n# [START accountcleanup]\n# Run once a day at midnight, to clean up inactive users.\n# Manually run the task here https://console.cloud.google.com/cloudscheduler\n@scheduler_fn.on_schedule(schedule=\"every day 00:00\")\ndef accountcleanup(event: scheduler_fn.ScheduledEvent) -> None:\n    \"\"\"Delete users who've been inactive for 30 days or more.\"\"\"\n    user_page: auth.ListUsersPage | None = auth.list_users()\n    while user_page is not None:\n        inactive_uids = [\n            user.uid for user in user_page.users if is_inactive(user, timedelta(days=30))\n        ]\n        auth.delete_users(inactive_uids)\n        user_page = user_page.get_next_page()\n# [END accountcleanup]\n\n\ndef is_inactive(user: auth.UserRecord, inactive_limit: timedelta) -> bool:\n    if user.user_metadata.last_refresh_timestamp is not None:\n        last_seen_timestamp = user.user_metadata.last_refresh_timestamp / 1000\n    elif user.user_metadata.last_sign_in_timestamp is not None:\n        last_seen_timestamp = user.user_metadata.last_sign_in_timestamp / 1000\n    elif user.user_metadata.creation_timestamp is not None:\n        last_seen_timestamp = user.user_metadata.creation_timestamp / 1000\n    else:\n        raise ValueError\n    last_seen = datetime.fromtimestamp(last_seen_timestamp)\n    inactive_time = datetime.now() - last_seen\n    return inactive_time >= inactive_limit\n# [END all]\n"
  },
  {
    "path": "Python/delete-unused-accounts-cron/functions/requirements.txt",
    "content": "firebase-functions\nfirebase-admin\n"
  },
  {
    "path": "Python/fcm-notifications/.gitignore",
    "content": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\nfirebase-debug.log*\nfirebase-debug.*.log*\n\n# Firebase cache\n.firebase/\n\n# Firebase config\n\n# Uncomment this if you'd like others to create their own Firebase project.\n# For a team working on the same Firebase project(s), it is recommended to leave\n# it commented so all members can deploy to the same project(s) in .firebaserc.\n# .firebaserc\n\n# Runtime data\npids\n*.pid\n*.seed\n*.pid.lock\n\n# Directory for instrumented libs generated by jscoverage/JSCover\nlib-cov\n\n# Coverage directory used by tools like istanbul\ncoverage\n\n# nyc test coverage\n.nyc_output\n\n# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)\n.grunt\n\n# Bower dependency directory (https://bower.io/)\nbower_components\n\n# node-waf configuration\n.lock-wscript\n\n# Compiled binary addons (http://nodejs.org/api/addons.html)\nbuild/Release\n\n# Dependency directories\nnode_modules/\n\n# Optional npm cache directory\n.npm\n\n# Optional eslint cache\n.eslintcache\n\n# Optional REPL history\n.node_repl_history\n\n# Output of 'npm pack'\n*.tgz\n\n# Yarn Integrity file\n.yarn-integrity\n\n# dotenv environment variables file\n.env\n"
  },
  {
    "path": "Python/fcm-notifications/README.md",
    "content": "# Send Firebase Cloud Messaging notifications for new followers.\n\nThis sample demonstrates how to send a Firebase Cloud Messaging (FCM) notification from a Realtime Database triggered Function. The sample also features a Web UI to experience the FCM notification.\n\n\n## Functions Code\n\nSee file [functions/main.py](functions/main.py) for the code.\n\nSending the notification is done using the [Firebase Admin SDK](https://firebase.google.com/docs/admin/setup). The Web client writes the individual device tokens to the realtime database which the Function uses to send the notification.\n\nThe dependencies are listed in [functions/requirements.txt](functions/requirements.txt).\n\n\n## Sample Database Structure\n\nUsers sign into the app and are requested to enable notifications on their browsers. If they successfully enable notifications the device token is saved into the datastore under `/users/$uid/notificationTokens`.:\n\n```\n/functions-project-12345\n    /users\n        /Uid-12345\n            displayName: \"Bob Dole\"\n            /notificationTokens\n                1234567890: true\n            photoURL: \"https://lh3.googleusercontent.com/...\"\n\n```\n\nIf a user starts following another user we'll write to `/followers/$followedUid/$followerUid`:\n\n```\n/functions-project-12345\n    /followers\n        /followedUid-12345\n            followerUid-67890: true\n    /users\n        /Uid-12345\n            displayName: \"Bob Dole\"\n            /notificationTokens\n                1234567890: true\n            photoURL: \"https://lh3.googleusercontent.com/...\"\n\n```\n\n\n## Trigger rules\n\nThe function triggers every time the value of a follow flag changes at `/followers/$followedUid/$followerUid`.\n\n\n## Deploy and test\n\nThis sample comes with a web-based UI for testing the function. To test it out:\n\n 1. Set up your Firebase project:\n     1. [Create a Firebase project](https://firebase.google.com/docs/web/setup/#create-firebase-project)\n     1. [Register your web app with Firebase](https://firebase.google.com/docs/web/setup/#register-app)\n 1. Enable **Google Provider** in the [Auth section](https://console.firebase.google.com/project/_/authentication/providers)\n 1. Clone or download this repo and open the `fcm-notification` directory.\n 1. You must have the Firebase CLI installed. If you don't have it install it with `npm install -g firebase-tools` and then configure it with `firebase login`.\n 1. Configure the CLI locally by using `firebase use --add` and select your project in the list.\n 1. Install dependencies locally by running: `./functions/venv/bin/pip install -r functions/requirements.txt`\n 1. Deploy your project using `firebase deploy`\n 1. Open the app using `firebase open hosting:site`, this will open a browser.\n 1. Start following a user, this will send a notification to them.\n"
  },
  {
    "path": "Python/fcm-notifications/database.rules.json",
    "content": "{\n  \"rules\": {\n    \"users\": {\n      \".read\": true,\n      \"$uid\": {\n        \".write\": \"auth.uid === $uid\"\n      }\n    },\n    \"followers\": {\n      \"$followedUid\": {\n        \"$followerUid\": {\n          \".read\": \"auth.uid === $followerUid\",\n          \".write\": \"auth.uid === $followerUid\"\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "Python/fcm-notifications/firebase.json",
    "content": "{\n  \"database\": {\n    \"rules\": \"database.rules.json\"\n  },\n  \"hosting\": {\n    \"public\": \"public\"\n  },\n  \"functions\": [\n    {\n      \"source\": \"functions\",\n      \"codebase\": \"fcm-notifications\",\n      \"ignore\": [\n        \"venv\",\n        \".git\",\n        \"firebase-debug.log\",\n        \"firebase-debug.*.log\"\n      ]\n    }\n  ]\n}\n"
  },
  {
    "path": "Python/fcm-notifications/functions/.gitignore",
    "content": "__pycache__\nvenv"
  },
  {
    "path": "Python/fcm-notifications/functions/main.py",
    "content": "import firebase_admin\nfrom firebase_admin import auth, db, messaging, exceptions\nfrom firebase_functions import db_fn\n\nfirebase_admin.initialize_app()\n\n\n@db_fn.on_value_written(reference=r\"followers/{followedUid}/{followerUid}\")\ndef send_follower_notification(event: db_fn.Event[db_fn.Change]) -> None:\n    \"\"\"Triggers when a user gets a new follower and sends a notification.\n\n    Followers add a flag to `/followers/{followedUid}/{followerUid}`.\n    Users save their device notification tokens to\n    `/users/{followedUid}/notificationTokens/{notificationToken}`.\n    \"\"\"\n    follower_uid = event.params[\"followerUid\"]\n    followed_uid = event.params[\"followedUid\"]\n\n    # If un-follow we exit the function.\n    change = event.data\n    if not change.after:\n        print(f\"User {follower_uid} unfollowed user {followed_uid} :(\")\n        return\n\n    print(f\"User {follower_uid} is now following user {followed_uid}\")\n    tokens_ref = db.reference(f\"users/{followed_uid}/notificationTokens\")\n    notification_tokens = tokens_ref.get()\n    if (not isinstance(notification_tokens, dict) or len(notification_tokens) < 1):\n        print(\"There are no tokens to send notifications to.\")\n        return\n    print(f\"There are {len(notification_tokens)} tokens to send notifications to.\")\n\n    follower: auth.UserRecord = auth.get_user(follower_uid)\n    notification = messaging.Notification(\n        title=\"You have a new follower!\",\n        body=f\"{follower.display_name} is now following you.\",\n        image=follower.photo_url if follower.photo_url else \"\",\n    )\n\n    # Send notifications to all tokens.\n    msgs = [\n        messaging.Message(token=token, notification=notification) for token in notification_tokens\n    ]\n    batch_response: messaging.BatchResponse = messaging.send_each(msgs)\n    if batch_response.failure_count < 1:\n        # Messages sent sucessfully. We're done!\n        return\n\n    # Clean up the tokens that are not registered any more.\n    for i in range(len(batch_response.responses)):\n        exception = batch_response.responses[i].exception\n        if not isinstance(exception, exceptions.FirebaseError):\n            continue\n        message = exception.http_response.json()[\"error\"][\"message\"]\n        if (isinstance(exception, messaging.UnregisteredError) or\n                message == \"The registration token is not a valid FCM registration token\"):\n            tokens_ref.child(msgs[i].token).delete()\n"
  },
  {
    "path": "Python/fcm-notifications/functions/requirements.txt",
    "content": "firebase_functions~=0.1.0"
  },
  {
    "path": "Python/fcm-notifications/public/firebase-messaging-sw.js",
    "content": "/**\n * Copyright 2023 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// Import and configure the Firebase SDK\n// These scripts are made available when the app is served or deployed on Firebase Hosting\n// If you do not serve/host your project using Firebase Hosting see https://firebase.google.com/docs/web/setup\nimportScripts('/__/firebase/10.0.0/firebase-app-compat.js');\nimportScripts('/__/firebase/10.0.0/firebase-messaging-compat.js');\nimportScripts('/__/firebase/init.js');\n\nfirebase.messaging();\n"
  },
  {
    "path": "Python/fcm-notifications/public/index.html",
    "content": "<!doctype html>\n<!--\n  Copyright 2016 Google Inc. All rights reserved.\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n      https://www.apache.org/licenses/LICENSE-2.0\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License\n-->\n<html lang=\"en\">\n<head>\n  <meta charset=\"utf-8\">\n  <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n  <meta name=\"description\" content=\"Demonstrates of to authorize Firebase with Instagram Auth using Firebase Functions\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n  <title>Firebase Functions demo send FCM notifications</title>\n\n  <!-- Material Design Lite -->\n  <link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/icon?family=Material+Icons\">\n  <link rel=\"stylesheet\" href=\"https://code.getmdl.io/1.1.3/material.blue_grey-orange.min.css\">\n  <script defer src=\"https://code.getmdl.io/1.1.3/material.min.js\"></script>\n\n  <link rel=\"stylesheet\" href=\"main.css\">\n</head>\n<body>\n<div class=\"demo-layout mdl-layout mdl-js-layout mdl-layout--fixed-header\">\n\n  <!-- Header section containing title -->\n  <header class=\"mdl-layout__header mdl-color-text--white mdl-color--light-blue-700\">\n    <div class=\"mdl-cell mdl-cell--12-col mdl-cell--12-col-tablet mdl-grid\">\n      <div class=\"mdl-layout__header-row mdl-cell mdl-cell--12-col mdl-cell--12-col-tablet mdl-cell--8-col-desktop\">\n        <h3>Send FCM notifications demo</h3>\n      </div>\n    </div>\n  </header>\n  <main class=\"mdl-layout__content mdl-color--grey-100\">\n    <div class=\"mdl-cell--12-col mdl-cell--12-col-tablet mdl-grid\">\n\n      <!-- Card containing the sign-in UI -->\n      <div id=\"demo-signed-out-card\" class=\"mdl-card mdl-shadow--2dp mdl-cell\">\n        <div class=\"mdl-card__supporting-text mdl-color-text--grey-600\">\n          <p>\n            This web application demonstrates how you can send FCM notifications using Functions and a web client.\n            <strong>Now sign in and activate notifications for your user!</strong>\n          </p>\n          <button id=\"demo-sign-in-button\" class=\"mdl-color-text--grey-700 mdl-button--raised mdl-button mdl-js-button mdl-js-ripple-effect\"><i class=\"material-icons\">account_circle</i> Sign in with Google</button>\n        </div>\n      </div>\n\n      <!-- Card containing the signed-in UI -->\n      <div id=\"demo-signed-in-card\" class=\"mdl-card mdl-shadow--2dp mdl-cell\">\n        <div class=\"mdl-card__supporting-text mdl-color-text--grey-600\">\n          <p>\n            Welcome <span id=\"demo-name-container\"></span>\n          </p>\n          <div id=\"demo-fcm-error-container\"></div>\n          <button id=\"demo-sign-out-button\" class=\"mdl-color-text--grey-700 mdl-button--raised mdl-button mdl-js-button mdl-js-ripple-effect\">Sign out</button>\n          <button id=\"demo-delete-button\" class=\"mdl-color-text--grey-700 mdl-button--raised mdl-button mdl-js-button mdl-js-ripple-effect\">Delete account</button>\n        </div>\n      </div>\n\n      <!-- Card containing the users to follow -->\n      <div id=\"demo-all-users-card\" class=\"mdl-card mdl-shadow--2dp mdl-cell mdl-cell--9-col\">\n        <div class=\"mdl-card__supporting-text mdl-color-text--grey-600\">\n          <p>\n            Here are all the users. If they have enabled notifications they will get a notification when you start following them.\n          </p>\n        </div>\n        <div id=\"demo-all-users-list\"></div>\n      </div>\n    </div>\n\n    <!-- Snackbar -->\n    <div id=\"demo-snackbar\" aria-live=\"assertive\" aria-atomic=\"true\" aria-relevant=\"text\" class=\"mdl-snackbar mdl-js-snackbar\">\n      <div class=\"mdl-snackbar__text\"></div>\n      <button type=\"button\" class=\"mdl-snackbar__action\"></button>\n    </div>\n  </main>\n</div>\n\n\n<!-- Import and configure the Firebase SDK -->\n<!-- These scripts are made available when the app is served or deployed on Firebase Hosting -->\n<!-- If you do not serve/host your project using Firebase Hosting see https://firebase.google.com/docs/web/setup -->\n<script src=\"/__/firebase/10.0.0/firebase-app-compat.js\"></script>\n<script src=\"/__/firebase/10.0.0/firebase-auth-compat.js\"></script>\n<script src=\"/__/firebase/10.0.0/firebase-messaging-compat.js\"></script>\n<script src=\"/__/firebase/10.0.0/firebase-database-compat.js\"></script>\n<script src=\"/__/firebase/init.js\"></script>\n\n<script src=\"main.js\"></script>\n</body>\n</html>\n"
  },
  {
    "path": "Python/fcm-notifications/public/main.css",
    "content": "/**\n * Copyright 2016 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nhtml, body {\n  font-family: 'Roboto', 'Helvetica', sans-serif;\n}\n.mdl-grid {\n  max-width: 1024px;\n  margin: auto;\n}\n.mdl-card {\n  min-height: 0;\n  padding-bottom: 5px;\n}\n.mdl-layout__header-row {\n  padding: 0;\n}\nh3 {\n  background: url('firebase-logo.png') no-repeat;\n  background-size: 40px;\n  padding-left: 50px;\n}\n#demo-signed-out-card,\n#demo-signed-in-card {\n  display: none;\n}\n#demo-profile-pic {\n  height: 60px;\n  width: 60px;\n  border-radius: 30px;\n  margin-left: calc(50% - 30px);\n  margin-top: 10px;\n}\n#demo-name-container {\n  font-weight: bold;\n}\n#demo-fcm-error-container {\n  margin-bottom: 20px;\n}\n#demo-delete-button {\n  margin-left: 20px;\n}\n#demo-all-users-list {\n  margin-bottom: -5px;\n}\n.demo-user-container {\n  position: relative;\n}\n.demo-user-container:HOVER {\n  background-color: #eee;\n}\n.demo-profile-pic {\n  height: 50px;\n  width: 50px;\n}\n.demo-name {\n  margin-left: 15px;\n}\n.mdl-switch {\n  display: inline-block;\n  right: 0;\n  position: absolute;\n  width: auto;\n  padding-right: 40px;\n  top: 13px;\n}\n.demo-notifications-enabled {\n  color: #aaa;\n  font-size: 80%;\n  display: none;\n}\n"
  },
  {
    "path": "Python/fcm-notifications/public/main.js",
    "content": "/**\n * Copyright 2016 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use strict';\n\n// Initializes the Demo.\nfunction Demo() {\n  document.addEventListener('DOMContentLoaded', function() {\n    // Shortcuts to DOM Elements.\n    this.signInButton = document.getElementById('demo-sign-in-button');\n    this.signOutButton = document.getElementById('demo-sign-out-button');\n    this.nameContainer = document.getElementById('demo-name-container');\n    this.fcmErrorContainer = document.getElementById('demo-fcm-error-container');\n    this.deleteButton = document.getElementById('demo-delete-button');\n    this.signedOutCard = document.getElementById('demo-signed-out-card');\n    this.signedInCard = document.getElementById('demo-signed-in-card');\n    this.usersContainer = document.getElementById('demo-all-users-list');\n    this.usersCard = document.getElementById('demo-all-users-card');\n    this.snackbar = document.getElementById('demo-snackbar');\n\n    // Bind events.\n    this.signInButton.addEventListener('click', this.signIn.bind(this));\n    this.signOutButton.addEventListener('click', this.signOut.bind(this));\n    this.deleteButton.addEventListener('click', this.deleteAccount.bind(this));\n    firebase.auth().onAuthStateChanged(this.onAuthStateChanged.bind(this));\n    firebase.messaging().onMessage(this.onMessage.bind(this));\n  }.bind(this));\n}\n\n// Triggered on Firebase auth state change.\nDemo.prototype.onAuthStateChanged = function(user) {\n  // If this is just an ID token refresh we exit.\n  if (user && this.currentUid === user.uid) {\n    return;\n  }\n\n  // Remove all Firebase realtime database listeners.\n  if (this.listeners) {\n    this.listeners.forEach(function(ref) {\n      ref.off();\n    });\n  }\n  this.listeners = [];\n\n  // Adjust UI depending on user state.\n  if (user) {\n    this.nameContainer.innerText = user.displayName;\n    this.signedOutCard.style.display = 'none';\n    this.signedInCard.style.display = 'block';\n    this.usersCard.style.display = 'block';\n    firebase.database().ref(`users/${user.uid}`).update({\n      displayName: user.displayName,\n      photoURL: user.photoURL\n    });\n    this.saveToken();\n    this.displayAllUsers();\n    this.currentUid = user.uid;\n  } else {\n    this.signedOutCard.style.display = 'block';\n    this.signedInCard.style.display = 'none';\n    this.usersCard.style.display = 'none';\n    this.usersContainer.innerHTML = '';\n    this.currentUid = null;\n  }\n};\n\n// Display all users so that they can be followed.\nDemo.prototype.displayAllUsers = function() {\n  var usersRef = firebase.database().ref('users');\n  usersRef.on('child_added', function(snapshot) {\n    // Create the HTML for a user.\n    var photoURL = snapshot.val().photoURL;\n    var displayName = snapshot.val().displayName;\n    var uid = snapshot.key;\n    var userTemplate =\n        '<div class=\"demo-user-container\">' +\n        '  <img class=\"demo-profile-pic\" src=\"' + photoURL + '\">' +\n        '  <span class=\"demo-name\">' + displayName + '</span>' +\n        '  <span class=\"demo-notifications-enabled\">(notifications enabled)</span>' +\n        '  <label class=\"mdl-switch mdl-js-switch mdl-js-ripple-effect\" for=\"demo-follow-switch-' + uid + '\">' +\n        '    <input type=\"checkbox\" id=\"demo-follow-switch-' + uid + '\" class=\"mdl-switch__input\">' +\n        '    <span class=\"mdl-switch__label\">Follow</span>' +\n        '  </label>' +\n        '</div>';\n\n    // Create the DOM element from the HTML.\n    var div = document.createElement('div');\n    div.innerHTML = userTemplate;\n    var userElement = div.firstChild;\n    this.usersContainer.appendChild(userElement);\n\n    // Activate the Material Design Lite Switch element.\n    var materialSwitchContainer = userElement.getElementsByClassName('mdl-switch')[0];\n    if (componentHandler) {\n      componentHandler.upgradeElement(materialSwitchContainer);\n    }\n\n    // Check if the user has notifications enabled and show a flag if he has.\n    var notificationEnabledElement = userElement.getElementsByClassName('demo-notifications-enabled')[0];\n    var notificationsEnabledRef = snapshot.ref.child('notificationTokens');\n    notificationsEnabledRef.on('value', function(notificationsEnabledSnapshot) {\n      notificationEnabledElement.style.display = notificationsEnabledSnapshot.hasChildren() ? 'inline' : 'none';\n    });\n    this.listeners.push(notificationsEnabledRef);\n\n    // Listen for the Switch state from the Realtime database.\n    var switchElement = document.getElementById('demo-follow-switch-' + uid);\n    var followUserRef = firebase.database().ref('followers/' + uid + '/' + this.currentUid);\n    this.listeners.push(followUserRef);\n    followUserRef.on('value', function(followSnapshot) {\n      switchElement.checked = !!followSnapshot.val();\n      if (materialSwitchContainer.MaterialSwitch) {\n        materialSwitchContainer.MaterialSwitch.checkDisabled();\n        materialSwitchContainer.MaterialSwitch.checkToggleState();\n      }\n    });\n\n    // Listen for switch state changes from the user.\n    switchElement.addEventListener('change', function() {\n      followUserRef.set(!!switchElement.checked);\n    });\n  }.bind(this));\n  this.listeners.push(usersRef);\n};\n\n// Initiates the sign-in flow using LinkedIn sign in in a popup.\nDemo.prototype.signIn = function() {\n  var google = new firebase.auth.GoogleAuthProvider();\n  firebase.auth().signInWithPopup(google);\n};\n\n// Signs-out of Firebase.\nDemo.prototype.signOut = function() {\n  firebase.auth().signOut();\n};\n\n// Deletes the user's account.\nDemo.prototype.deleteAccount = function() {\n  return firebase.database().ref('users/' + this.currentUid).remove().then(function() {\n    return firebase.auth().currentUser.delete().then(function() {\n      window.alert('Account deleted');\n    }).catch(function(error) {\n      if (error.code === 'auth/requires-recent-login') {\n        window.alert('You need to have recently signed-in to delete your account. Please sign-in and try again.');\n        firebase.auth().signOut();\n      }\n    });\n  });\n};\n\n// This is called when a notification is received while the app is in focus.\n// When the app is not in focus or if the tab is closed, this function is not called and the FCM notifications is\n// handled by the Service worker which displays a browser popup notification if the browser supports it.\nDemo.prototype.onMessage = function(payload) {\n  console.log('Notifications received.', payload);\n\n  // Normally our Function sends a notification payload, we check just in case.\n  if (payload.notification) {\n    // If notifications are supported on this browser we display one.\n    // Note: This is for demo purposes only. For a good user experience it is not recommended to display browser\n    // notifications while the app is in focus. In a production app you probably want to only display some form of\n    // in-app notifications like the snackbar (see below).\n    if (window.Notification instanceof Function) {\n      // This displays a notification if notifications have been granted.\n      new Notification(payload.notification.title, payload.notification);\n    }\n    // Display the notification content in the Snackbar too.\n    this.snackbar.MaterialSnackbar.showSnackbar({message: payload.notification.body});\n  }\n};\n\n// Saves the token to the database if available. If not request permissions.\nDemo.prototype.saveToken = function() {\n  firebase.messaging().getToken().then(function(currentToken) {\n    if (currentToken) {\n      firebase.database().ref('users/' + this.currentUid + '/notificationTokens/' + currentToken).set(true);\n    } else {\n      this.requestPermission();\n    }\n  }.bind(this)).catch(function(err) {\n    console.error('Unable to get messaging token.', err);\n    if (err.code === 'messaging/permission-default') {\n      this.fcmErrorContainer.innerText = 'You have not enabled notifications on this browser. To enable notifications reload the page and allow notifications using the permission dialog.';\n    } else if (err.code === 'messaging/notifications-blocked') {\n      this.fcmErrorContainer.innerHTML = 'You have blocked notifications on this browser. To enable notifications follow these instructions: <a href=\"https://support.google.com/chrome/answer/114662?visit_id=1-636150657126357237-2267048771&rd=1&co=GENIE.Platform%3DAndroid&oco=1\">Android Chrome Instructions</a><a href=\"https://support.google.com/chrome/answer/6148059\">Desktop Chrome Instructions</a>';\n    }\n  }.bind(this));\n};\n\n// Requests permission to send notifications on this browser.\nDemo.prototype.requestPermission = function() {\n  console.log('Requesting permission...');\n  firebase.messaging().requestPermission().then(function() {\n    console.log('Notification permission granted.');\n    this.saveToken();\n  }.bind(this)).catch(function(err) {\n    console.error('Unable to get permission to notify.', err);\n  });\n};\n\n// Load the demo.\nwindow.demo = new Demo();\n"
  },
  {
    "path": "Python/http-flask/README.md",
    "content": "# HTTP Flask Example\n\nThis sample demonstrates how to expose a Flask app as a single Cloud Function. The Flask app provides a simple CRUD interface for \"widgets\" stored in the Realtime Database.\n\n## Functions Code\n\nSee file [functions/main.py](functions/main.py) for the code.\n\nThe sample creates a Flask app and defines two routes:\n\n- `GET /widgets` and `GET /widgets/<id>`: These routes retrieve a list of all widgets or a single widget by its ID from the Realtime Database.\n- `POST /widgets`: This route adds a new widget to the Realtime Database.\n\nThe entire Flask app is then exposed as a single Cloud Function called `httpsflaskexample` using `@https_fn.on_request()`.\n\n## Trigger rules\n\nThe `httpsflaskexample` function is triggered by an HTTP request.\n\n## Deploy and test\n\nTo deploy and test the sample:\n\n1. Create a Firebase project on the [Firebase Console](https://console.firebase.google.com).\n2. Get the code, for instance using `git clone https://github.com/firebase/functions-samples`\n3. Enter the correct directory `cd functions-samples/Python/http-flask`\n4. Set up the CLI to use your Firebase project using `firebase use --add` and select your Firebase project.\n5. Deploy your project's code using `firebase deploy`.\n6. Use a tool like `curl` to test the function:\n   - **Add a widget:** `curl -X POST -d \"My new widget\" https://us-central1-YOUR_PROJECT_ID.cloudfunctions.net/httpsflaskexample/widgets`\n   - **Get all widgets:** `curl https://us-central1-YOUR_PROJECT_ID.cloudfunctions.net/httpsflaskexample/widgets`\n   - **Get a specific widget:** `curl https://us-central1-YOUR_PROJECT_ID.cloudfunctions.net/httpsflaskexample/widgets/WIDGET_ID`\n"
  },
  {
    "path": "Python/http-flask/firebase.json",
    "content": "{\n  \"functions\": {\n    \"codebase\": \"http-flask\"\n  }\n}\n"
  },
  {
    "path": "Python/http-flask/functions/main.py",
    "content": "# Copyright 2023 Google Inc. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# [START httpflaskexample]\nfrom firebase_admin import initialize_app, db\nfrom firebase_functions import https_fn\nimport flask\n\ninitialize_app()\napp = flask.Flask(__name__)\n\n# Build multiple CRUD interfaces:\n\n\n@app.get(\"/widgets\")\n@app.get(\"/widgets/<id>\")\ndef get_widget(id=None):\n    if id is not None:\n        return db.reference(f\"/widgets/{id}\").get()\n    else:\n        return db.reference(\"/widgets\").get()\n\n\n@app.post(\"/widgets\")\ndef add_widget():\n    new_widget = flask.request.get_data(as_text=True)\n    db.reference(\"/widgets\").push(new_widget)\n    return flask.Response(status=201, response=\"Added widget\")\n\n\n# Expose Flask app as a single Cloud Function:\n\n\n@https_fn.on_request()\ndef httpsflaskexample(req: https_fn.Request) -> https_fn.Response:\n    with app.request_context(req.environ):\n        return app.full_dispatch_request()\n# [END httpflaskexample]\n"
  },
  {
    "path": "Python/http-flask/functions/requirements.txt",
    "content": "firebase-admin\nfirebase-functions\nflask\n"
  },
  {
    "path": "Python/post-signup-event/README.md",
    "content": "# Post Sign-up Event with Google Calendar\n\nThis sample demonstrates how to use an [Auth blocking function](https://firebase.google.com/docs/functions/auth-blocking-events) to save a user's Google OAuth2 access token to Firestore upon sign-up. It then uses a [task queue](https://firebase.google.com/docs/functions/task-functions) to schedule a Cloud Function that uses this token to create a Google Calendar event for the user.\n\n## Functions Code\n\nSee file [functions/main.py](functions/main.py) for the code.\n\nThe function `savegoogletoken` is an Auth blocking function that triggers before a user is created. If the user is signing up with Google, it saves their OAuth2 access token to a `user_info` collection in Firestore. It then creates a task to call the `scheduleonboarding` function.\n\nThe `scheduleonboarding` function is a task queue function that retrieves the user's access token from Firestore, creates a new event on their primary Google Calendar, and then deletes the access token from Firestore.\n\n## Trigger rules\n\n- The `savegoogletoken` function is triggered by `beforeUserCreated` Auth blocking event.\n- The `scheduleonboarding` function is triggered by a task queue.\n\n## Deploy and test\n\nTo deploy and test the sample:\n\n1. Create a Firebase project on the [Firebase Console](https://console.firebase.google.com).\n2. Enable the **Google Calendar API** for your project in the [Google Cloud Console](https://console.cloud.google.com/apis/library/calendar-json.googleapis.com).\n3. Enable blocking functions for your project in the Firebase console:\n   - Go to the **Authentication** > **Settings** page.\n   - In the **Blocking functions** section, select **Before user creation (beforeCreate)** from the dropdown menu.\n   - Ensure that **Enable credential pass-through** is checked.\n4. Get the code, for instance using `git clone https://github.com/firebase/functions-samples`\n5. Enter the correct directory `cd functions-samples/Python/post-signup-event`\n6. Set up the CLI to use your Firebase project using `firebase use --add` and select your Firebase project.\n7. Deploy your project's code using `firebase deploy`.\n8. Sign up for your app using a Google account.\n9. After a minute, check your Google Calendar for a new \"Onboarding with ExampleCo\" event.\n"
  },
  {
    "path": "Python/post-signup-event/firebase.json",
    "content": "{\n  \"functions\": {\n    \"codebase\": \"post-signup-event\"\n  }\n}\n"
  },
  {
    "path": "Python/post-signup-event/functions/main.py",
    "content": "# Copyright 2023 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom datetime import datetime, timedelta\nimport json\n\nfrom firebase_admin import auth, firestore, initialize_app\nfrom firebase_functions import https_fn, identity_fn, tasks_fn, options, params\n\nimport google.auth\nimport google.auth.transport.requests\nimport google.cloud.firestore\nimport google.cloud.tasks_v2\nimport google.oauth2.credentials\nimport googleapiclient.discovery\n\ninitialize_app()\n\n\n# [START savegoogletoken]\n@identity_fn.before_user_created()\ndef savegoogletoken(\n        event: identity_fn.AuthBlockingEvent) -> identity_fn.BeforeCreateResponse | None:\n    \"\"\"During sign-up, save the Google OAuth2 access token and queue up a task\n    to schedule an onboarding session on the user's Google Calendar.\n\n    You will only get an access token if you enabled it in your project's blocking\n    functions settings in the Firebase console:\n\n    https://console.firebase.google.com/project/_/authentication/settings\n    \"\"\"\n    if event.credential is not None and event.credential.provider_id == \"google.com\":\n        print(f\"Signed in with {event.credential.provider_id}. Saving access token.\")\n\n        firestore_client: google.cloud.firestore.Client = firestore.client()\n        doc_ref = firestore_client.collection(\"user_info\").document(event.data.uid)\n        doc_ref.set({\"calendar_access_token\": event.credential.access_token}, merge=True)\n\n        tasks_client = google.cloud.tasks_v2.CloudTasksClient()\n        task_queue = tasks_client.queue_path(params.PROJECT_ID.value,\n                                             options.SupportedRegion.US_CENTRAL1,\n                                             \"scheduleonboarding\")\n        target_uri = get_function_url(\"scheduleonboarding\")\n        calendar_task = google.cloud.tasks_v2.Task(http_request={\n            \"http_method\": google.cloud.tasks_v2.HttpMethod.POST,\n            \"url\": target_uri,\n            \"headers\": {\n                \"Content-type\": \"application/json\"\n            },\n            \"body\": json.dumps({\n                \"data\": {\n                    \"uid\": event.data.uid\n                }\n            }).encode()\n        },\n                                                   schedule_time=datetime.now() +\n                                                   timedelta(minutes=1))\n        tasks_client.create_task(parent=task_queue, task=calendar_task)\n# [END savegoogletoken]\n\n\n# [START scheduleonboarding]\n@tasks_fn.on_task_dispatched()\ndef scheduleonboarding(request: tasks_fn.CallableRequest) -> https_fn.Response:\n    \"\"\"Add an onboarding event to a user's Google Calendar.\n\n    Retrieves and deletes the access token that was saved to Cloud Firestore.\n    \"\"\"\n\n    if \"uid\" not in request.data:\n        return https_fn.Response(status=https_fn.FunctionsErrorCode.INVALID_ARGUMENT,\n                                 response=\"No user specified.\")\n    uid = request.data[\"uid\"]\n\n    user_record: auth.UserRecord = auth.get_user(uid)\n    if user_record.email is None:\n        return https_fn.Response(status=https_fn.FunctionsErrorCode.INVALID_ARGUMENT,\n                                 response=\"No email address on record.\")\n\n    firestore_client: google.cloud.firestore.Client = firestore.client()\n    user_info = firestore_client.collection(\"user_info\").document(uid).get().to_dict()\n    if not isinstance(user_info, dict) or \"calendar_access_token\" not in user_info:\n        return https_fn.Response(status=https_fn.FunctionsErrorCode.PERMISSION_DENIED,\n                                 response=\"No Google OAuth token found.\")\n    calendar_access_token = user_info[\"calendar_access_token\"]\n    firestore_client.collection(\"user_info\").document(uid).update(\n        {\"calendar_access_token\": google.cloud.firestore.DELETE_FIELD})\n\n    google_credentials = google.oauth2.credentials.Credentials(token=calendar_access_token)\n\n    calendar_client = googleapiclient.discovery.build(\"calendar\",\n                                                      \"v3\",\n                                                      credentials=google_credentials)\n    calendar_event = {\n        \"summary\": \"Onboarding with ExampleCo\",\n        \"location\": \"Video call\",\n        \"description\": \"Walk through onboarding tasks with an ExampleCo engineer.\",\n        \"start\": {\n            \"dateTime\": (datetime.now() + timedelta(days=3)).isoformat(),\n            \"timeZone\": \"America/Los_Angeles\"\n        },\n        \"end\": {\n            \"dateTime\": (datetime.now() + timedelta(days=3, hours=1)).isoformat(),\n            \"timeZone\": \"America/Los_Angeles\"\n        },\n        \"attendees\": [{\n            \"email\": user_record.email\n        }, {\n            \"email\": \"onboarding@example.com\"\n        }]\n    }\n    calendar_client.events().insert(calendarId=\"primary\", body=calendar_event).execute()\n\n    return https_fn.Response(\"Success\")\n# [END scheduleonboarding]\n\n\ndef get_function_url(name: str, location: str = options.SupportedRegion.US_CENTRAL1) -> str:\n    \"\"\"Get the URL of a given v2 cloud function.\n\n    Params:\n        name: the function's name\n        location: the function's location\n\n    Returns:\n        The URL of the function\n    \"\"\"\n    credentials, project_id = google.auth.default(\n        scopes=[\"https://www.googleapis.com/auth/cloud-platform\"])\n    authed_session = google.auth.transport.requests.AuthorizedSession(credentials)\n    url = (\"https://cloudfunctions.googleapis.com/v2beta/\" +\n           f\"projects/{project_id}/locations/{location}/functions/{name}\")\n    response = authed_session.get(url)\n    data = response.json()\n    function_url = data[\"serviceConfig\"][\"uri\"]\n    return function_url\n"
  },
  {
    "path": "Python/post-signup-event/functions/requirements.txt",
    "content": "firebase-functions\nfirebase-admin\ngoogle-auth\ngoogle-api-python-client\ngoogle-cloud-tasks\n"
  },
  {
    "path": "Python/pyfmt.py",
    "content": "\"\"\"Utility to format Python source files for Firebase docs.\"\"\"\n\n# Copyright 2023 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport difflib\nimport pathlib\nimport re\n\nfrom yapf.yapflib import yapf_api\n\nstart_tag_re = re.compile(r\"^([ \\t]*)#\\s*\\[START\\s+(\\w+).*\\]\\s*\\n\", flags=re.MULTILINE)\nend_tag_re = re.compile(r\"^\\s*#\\s*\\[END\\s+(\\w+).*\\][ \\t]*$\", flags=re.MULTILINE)\n\npyproject_toml = str(pathlib.Path(__file__).parent / \"pyproject.toml\")\n\n\ndef reformat_in_place(files: list[str]) -> None:\n    for file in files:\n        with open(file, \"rt\", encoding=\"utf-8\") as f:\n            src = format(f.read())\n        with open(file, \"wt\", encoding=\"utf-8\") as f:\n            f.write(src)\n\n\ndef check_and_diff(files: list[str]) -> int:\n    diff_count = 0\n    for file in files:\n        with open(file, \"rt\", encoding=\"utf-8\") as f:\n            orig = f.read()\n        fmt = format(orig)\n        diff = list(\n            difflib.unified_diff(orig.splitlines(),\n                                 fmt.splitlines(),\n                                 fromfile=file,\n                                 tofile=f\"{file} (reformatted)\",\n                                 lineterm=\"\"))\n        if len(diff) > 0:\n            diff_count += 1\n            print(\"\\n\".join(diff), end=\"\\n\\n\")\n    return diff_count\n\n\ndef format(src: str) -> str:\n    out, _ = yapf_api.FormatCode(src, style_config=pyproject_toml)\n    out = fix_region_tags(out)\n    return out\n\n\ndef fix_region_tags(src: str) -> str:\n    \"\"\"Fix formattiing of region tags.\n\n    - Remove extra blank lines after START tags.\n    - Remove extra blank lines before END tags.\n    - Matches indentation of END tags to their START tags.\n    \"\"\"\n    src = start_tag_re.sub(r\"\\1# [START \\2]\\n\", src)\n\n    tag_indentation = {m.group(2): m.group(1) for m in start_tag_re.finditer(src)}\n\n    def fix_end_tag(m: re.Match) -> str:\n        name = m.group(1)\n        indentation = tag_indentation[name]\n        return f\"{indentation}# [END {name}]\"\n\n    src = end_tag_re.sub(fix_end_tag, src)\n\n    return src\n\n\nif __name__ == \"__main__\":\n    import argparse\n\n    argparser = argparse.ArgumentParser()\n    argparser.add_argument(\"--check_only\",\n                           \"-c\",\n                           action=\"store_true\",\n                           help=\"check files and print diffs, but don't modify files\")\n    argparser.add_argument(\"--exclude\",\n                           \"-e\",\n                           action=\"append\",\n                           default=[],\n                           help=\"exclude file or glob (can specify multiple times)\")\n    argparser.add_argument(\"file_or_glob\", nargs=\"+\")\n    args = argparser.parse_args()\n\n    files = {str(f) for fs in [pathlib.Path(\".\").glob(fg) for fg in args.file_or_glob] for f in fs}\n    excludes = {str(f) for fs in [pathlib.Path(\".\").glob(fg) for fg in args.exclude] for f in fs}\n    files = files - excludes\n\n    if args.check_only:\n        diff_count = check_and_diff(files)\n        if diff_count != 0:\n            print(f\"{diff_count} files would be reformatted.\")\n            print(f\"Run {argparser.prog} to reformat in place.\")\n        exit(diff_count)\n    else:\n        reformat_in_place(files)\n"
  },
  {
    "path": "Python/pyproject.toml",
    "content": "# Copyright 2023 Google Inc. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n[project]\nname = \"functions_samples\"\nversion = \"1.0.0\"\n[tool.yapf]\nbased_on_style = \"google\"\ncolumn_limit = 100\n[tool.mypy]\npython_version = \"3.10\""
  },
  {
    "path": "Python/quickstarts/auth-blocking-functions/README.md",
    "content": "# auth-blocking-functions\n"
  },
  {
    "path": "Python/quickstarts/auth-blocking-functions/firebase.json",
    "content": "{\n  \"functions\": {\n    \"codebase\": \"auth-blocking-functions\"\n  }\n}\n"
  },
  {
    "path": "Python/quickstarts/auth-blocking-functions/functions/main.py",
    "content": "# Copyright 2023 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom firebase_admin import auth, firestore, initialize_app\nfrom firebase_functions import identity_fn, https_fn\n\nimport google.cloud.firestore\n\ninitialize_app()\n\n\n# [START created_noop]\n@identity_fn.before_user_created()\ndef created_noop(event: identity_fn.AuthBlockingEvent) -> identity_fn.BeforeCreateResponse | None:\n    return\n# [END created_noop]\n\n\n# [START signedin_noop]\n@identity_fn.before_user_signed_in()\ndef signedin_noop(event: identity_fn.AuthBlockingEvent) -> identity_fn.BeforeSignInResponse | None:\n    return\n# [END signedin_noop]\n\n\n# [START v2ValidateNewUser]\n# [START v2beforeCreateFunctionTrigger]\n# Block account creation with any non-acme email address.\n@identity_fn.before_user_created()\ndef validatenewuser(\n        event: identity_fn.AuthBlockingEvent) -> identity_fn.BeforeCreateResponse | None:\n# [END v2beforeCreateFunctionTrigger]\n    # [START v2readUserData]\n    # User data passed in from the CloudEvent.\n    user = event.data\n    # [END v2readUserData]\n\n    # [START v2domainHttpsError]\n    # Only users of a specific domain can sign up.\n    if user.email is None or \"@acme.com\" not in user.email:\n        # Return None so that Firebase Auth rejects the account creation.\n        raise https_fn.HttpsError(code=https_fn.FunctionsErrorCode.INVALID_ARGUMENT,\n                                  message=\"Unauthorized email\")\n    # [END v2domainHttpsError]\n# [END v2ValidateNewUser]\n\n\n# [START setdefaultname]\n@identity_fn.before_user_created()\ndef setdefaultname(event: identity_fn.AuthBlockingEvent) -> identity_fn.BeforeCreateResponse | None:\n    return identity_fn.BeforeCreateResponse(\n        # If no display name is provided, set it to \"Guest\".\n        display_name=event.data.display_name if event.data.display_name is not None else \"Guest\")\n# [END setdefaultname]\n\n\n# [START requireverified]\n@identity_fn.before_user_created()\ndef requireverified(\n        event: identity_fn.AuthBlockingEvent) -> identity_fn.BeforeCreateResponse | None:\n    if event.data.email is not None and not event.data.email_verified:\n        raise https_fn.HttpsError(code=https_fn.FunctionsErrorCode.INVALID_ARGUMENT,\n                                  message=\"You must register using a trusted provider.\")\n# [END requireverified]\n\n\ndef send_verification_email_using_your_smtp_server(email, link):\n    return\n\n\n# TODO: Should really be non-blocking or client-side call.\n# [START sendverification]\n@identity_fn.before_user_created()\ndef sendverification(\n        event: identity_fn.AuthBlockingEvent) -> identity_fn.BeforeCreateResponse | None:\n    if event.data.email is not None and not event.data.email_verified:\n        link = auth.generate_email_verification_link(event.data.email)\n        send_verification_email_using_your_smtp_server(event.data.email, link)\n# [END sendverification]\n\n\n# [START requireverifiedsignin]\n@identity_fn.before_user_signed_in()\ndef requireverifiedsignin(\n        event: identity_fn.AuthBlockingEvent) -> identity_fn.BeforeSignInResponse | None:\n    if event.data.email is not None and not event.data.email_verified:\n        raise https_fn.HttpsError(code=https_fn.FunctionsErrorCode.INVALID_ARGUMENT,\n                                  message=\"You must verify your email address before signing in.\")\n# [END requireverifiedsignin]\n\n\n# [START trustfacebook]\n@identity_fn.before_user_created()\ndef markverified(event: identity_fn.AuthBlockingEvent) -> identity_fn.BeforeCreateResponse | None:\n    if event.data.email is not None and \"@facebook.com\" in event.data.email:\n        return identity_fn.BeforeSignInResponse(email_verified=True)\n# [END trustfacebook]\n\n\ndef is_suspicious(ip_address):\n    return True\n\n\n# [START ipban]\n@identity_fn.before_user_signed_in()\ndef ipban(event: identity_fn.AuthBlockingEvent) -> identity_fn.BeforeSignInResponse | None:\n    if is_suspicious(event.ip_address):\n        raise https_fn.HttpsError(code=https_fn.FunctionsErrorCode.PERMISSION_DENIED,\n                                  message=\"IP banned.\")\n# [END ipban]\n\n\n# [START customclaims]\n@identity_fn.before_user_created()\ndef setemployeeid(event: identity_fn.AuthBlockingEvent) -> identity_fn.BeforeCreateResponse | None:\n    if (event.credential is not None and event.credential.claims is not None and\n            event.credential.provider_id == \"saml.my-provider-id\"):\n        return identity_fn.BeforeCreateResponse(\n            custom_claims={\"eid\": event.credential.claims[\"employeeid\"]})\n\n\n@identity_fn.before_user_signed_in()\ndef copyclaimstosession(\n        event: identity_fn.AuthBlockingEvent) -> identity_fn.BeforeSignInResponse | None:\n    if (event.credential is not None and event.credential.claims is not None and\n            event.credential.provider_id == \"saml.my-provider-id\"):\n        return identity_fn.BeforeSignInResponse(session_claims={\n            \"role\": event.credential.claims[\"role\"],\n            \"groups\": event.credential.claims[\"groups\"]\n        })\n# [END customclaims]\n\n\n# [START logip]\n@identity_fn.before_user_signed_in()\ndef logip(event: identity_fn.AuthBlockingEvent) -> identity_fn.BeforeSignInResponse | None:\n    return identity_fn.BeforeSignInResponse(session_claims={\"signInIpAddress\": event.ip_address})\n# [END logip]\n\n\ndef analyze_photo_with_ml(url):\n    return 0.42\n\n\nTHRESHOLD = 0.7\nPLACEHOLDER_URL = \"\"\n\n\n# [START sanitizeprofilephoto]\n@identity_fn.before_user_created()\ndef sanitizeprofilephoto(\n        event: identity_fn.AuthBlockingEvent) -> identity_fn.BeforeCreateResponse | None:\n    if event.data.photo_url is not None:\n        score = analyze_photo_with_ml(event.data.photo_url)\n        if score > THRESHOLD:\n            return identity_fn.BeforeCreateResponse(photo_url=PLACEHOLDER_URL)\n# [END sanitizeprofilephoto]\n\n\n# [START v2CheckForBan]\n# [START v2beforeSignInFunctionTrigger]\n# Block account sign in with any banned account.\n@identity_fn.before_user_signed_in()\ndef checkforban(event: identity_fn.AuthBlockingEvent) -> identity_fn.BeforeSignInResponse | None:\n# [END v2beforeSignInFunctionTrigger]\n    # [START v2readEmailData]\n    # Email passed from the CloudEvent.\n    email = event.data.email if event.data.email is not None else \"\"\n    # [END v2readEmailData]\n\n    # [START v2documentGet]\n    # Obtain a document in Firestore of the banned email address.\n    firestore_client: google.cloud.firestore.Client = firestore.client()\n    doc = firestore_client.collection(\"banned\").document(email).get()\n    # [END v2documentGet]\n\n    # [START v2bannedHttpsError]\n    # Checking that the document exists for the email address.\n    if doc.exists:\n        # Throw an HttpsError so that Firebase Auth rejects the account sign in.\n        raise https_fn.HttpsError(code=https_fn.FunctionsErrorCode.INVALID_ARGUMENT,\n                                  message=\"Unauthorized email\")\n    # [END v2bannedHttpsError]\n# [END v2CheckForBan]\n"
  },
  {
    "path": "Python/quickstarts/auth-blocking-functions/functions/requirements.txt",
    "content": "firebase-functions\nfirebase-admin\n"
  },
  {
    "path": "Python/quickstarts/callable-functions/README.md",
    "content": "# callable-functions\n"
  },
  {
    "path": "Python/quickstarts/callable-functions/firebase.json",
    "content": "{\n  \"functions\": {\n    \"codebase\": \"callable-functions\"\n  }\n}\n"
  },
  {
    "path": "Python/quickstarts/callable-functions/functions/main.py",
    "content": "# Copyright 2023 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     https:#www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport re\nfrom typing import Any\n\n# [START v2imports]\n# Dependencies for callable functions.\nfrom firebase_functions import https_fn, options\n\n# Dependencies for writing to Realtime Database.\nfrom firebase_admin import db, initialize_app\n# [END v2imports]\n\ninitialize_app()\n\n\n# [START v2allAdd]\n# [START v2addFunctionTrigger]\n@https_fn.on_call()\ndef addnumbers(req: https_fn.CallableRequest) -> Any:\n    \"\"\"Adds two numbers to each other.\"\"\"\n# [END v2addFunctionTrigger]\n    # [START v2addHttpsError]\n    # Checking that attributes are present and are numbers.\n    try:\n        # [START v2readAddData]\n        # Numbers passed from the client.\n        first_number_param = req.data[\"firstNumber\"]\n        second_number_param = req.data[\"secondNumber\"]\n        # [END v2readAddData]\n        first_number = int(first_number_param)\n        second_number = int(second_number_param)\n    except (ValueError, KeyError):\n        # Throwing an HttpsError so that the client gets the error details.\n        raise https_fn.HttpsError(\n            code=https_fn.FunctionsErrorCode.INVALID_ARGUMENT,\n            message=('The function must be called with two arguments, \"firstNumber\"'\n                     ' and \"secondNumber\", which must both be numbers.'))\n    # [END v2addHttpsError]\n\n    # [START v2returnAddData]\n    return {\n        \"firstNumber\": first_number,\n        \"secondNumber\": second_number,\n        \"operator\": \"+\",\n        \"operationResult\": first_number + second_number\n    }\n    # [END v2returnAddData]\n# [END v2allAdd]\n\n\n# [START v2messageFunctionTrigger]\n@https_fn.on_call()\ndef addmessage(req: https_fn.CallableRequest) -> Any:\n    \"\"\"Saves a message to the Firebase Realtime Database but sanitizes the text\n    by removing swear words.\"\"\"\n# [END v2messageFunctionTrigger]\n    try:\n        # [START v2readMessageData]\n        # Message text passed from the client.\n        text = req.data[\"text\"]\n        # [END v2readMessageData]\n    except KeyError:\n        # Throwing an HttpsError so that the client gets the error details.\n        raise https_fn.HttpsError(code=https_fn.FunctionsErrorCode.INVALID_ARGUMENT,\n                                  message=('The function must be called with one argument, \"text\",'\n                                           \" containing the message text to add.\"))\n\n    # [START v2messageHttpsErrors]\n    # Checking attribute.\n    if not isinstance(text, str) or len(text) < 1:\n        # Throwing an HttpsError so that the client gets the error details.\n        raise https_fn.HttpsError(code=https_fn.FunctionsErrorCode.INVALID_ARGUMENT,\n                                  message=('The function must be called with one argument, \"text\",'\n                                           \" containing the message text to add.\"))\n\n    # Checking that the user is authenticated.\n    if req.auth is None:\n        # Throwing an HttpsError so that the client gets the error details.\n        raise https_fn.HttpsError(code=https_fn.FunctionsErrorCode.FAILED_PRECONDITION,\n                                  message=\"The function must be called while authenticated.\")\n    # [END v2messageHttpsErrors]\n\n    # [START v2authIntegration]\n    # Authentication / user information is automatically added to the request.\n    uid = req.auth.uid\n    name = req.auth.token.get(\"name\", \"\")\n    picture = req.auth.token.get(\"picture\", \"\")\n    email = req.auth.token.get(\"email\", \"\")\n    # [END v2authIntegration]\n\n    try:\n        # [START v2returnMessage]\n        # Saving the new message to the Realtime Database.\n        sanitized_message = sanitize_text(text)  # Sanitize message.\n        db.reference(\"/messages\").push({  # type: ignore\n            \"text\": sanitized_message,\n            \"author\": {\n                \"uid\": uid,\n                \"name\": name,\n                \"picture\": picture,\n                \"email\": email\n            }\n        })\n        print(\"New message written\")\n\n        # Returning the sanitized message to the client.\n        return {\"text\": sanitized_message}\n        # [END v2returnMessage]\n    except Exception as e:\n        # Re-throwing the error as an HttpsError so that the client gets\n        # the error details.\n        raise https_fn.HttpsError(code=https_fn.FunctionsErrorCode.UNKNOWN,\n                                  message=\"Error\",\n                                  details=e)\n\n\ndef sanitize_text(text: str) -> str:\n    # Use indoor voice\n    if text.isupper():\n        text = text.capitalize()\n\n    # Censor bad words\n    swears = re.compile(r\"shoot|dang|heck\", re.IGNORECASE)\n    text = swears.sub(repl=lambda m: \"*\" * (m.end() - m.start()), string=text)\n\n    return text\n"
  },
  {
    "path": "Python/quickstarts/callable-functions/functions/requirements.txt",
    "content": "firebase-functions\nfirebase-admin\n"
  },
  {
    "path": "Python/quickstarts/custom-events/README.md",
    "content": "# custom-events\n"
  },
  {
    "path": "Python/quickstarts/custom-events/firebase.json",
    "content": "{\n  \"functions\": {\n    \"codebase\": \"custom-events\"\n  }\n}\n"
  },
  {
    "path": "Python/quickstarts/custom-events/functions/main.py",
    "content": "# Copyright 2023 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# [START import]\nfrom firebase_admin import firestore, initialize_app\nfrom firebase_functions import eventarc_fn\n# [END import]\nimport google.cloud.firestore\n\ninitialize_app()\n\n\n# [START imageresizedEvent]\n@eventarc_fn.on_custom_event_published(\n    event_type=\"firebase.extensions.storage-resize-images.v1.complete\")\ndef onimageresized(event: eventarc_fn.CloudEvent) -> None:\n    print(\"Received image resize completed event: \", event.type)\n\n    if not isinstance(event.subject, str):\n        print(\"No 'subject' data.\")\n        return\n\n    # For example, write resized image details into Firestore.\n    firestore_client: google.cloud.firestore.Client = firestore.client()\n    collection = firestore_client.collection(\"images\")\n    doc = collection.document(event.subject.replace(\"/\", \"_\"))  # original file path\n    doc.set(event.data)  # resized images paths and sizes\n# [END imageresizedEvent]\n\n\n# [START nondefaultchannel]\n@eventarc_fn.on_custom_event_published(\n    event_type=\"firebase.extensions.storage-resize-images.v1.complete\",\n    channel=\"locations/us-west1/channels/firebase\",\n    region=\"us-west1\")\ndef onimageresizedwest(event: eventarc_fn.CloudEvent) -> None:\n    print(\"Received image resize completed event: \", event.type)\n    # [START_EXCLUDE]\n    if not isinstance(event.subject, str):\n        print(\"No 'subject' data.\")\n        return\n\n    # For example, write resized image details into Firestore.\n    firestore_client: google.cloud.firestore.Client = firestore.client()\n    collection = firestore_client.collection(\"images\")\n    doc = collection.document(event.subject.replace(\"/\", \"_\"))  # original file path\n    doc.set(event.data)  # resized images paths and sizes\n    # [END_EXCLUDE]\n# [END nondefaultchannel]\n"
  },
  {
    "path": "Python/quickstarts/custom-events/functions/requirements.txt",
    "content": "firebase-functions\nfirebase-admin\n"
  },
  {
    "path": "Python/quickstarts/firestore-sync-auth/README.md",
    "content": "# uppercase-firestore\n"
  },
  {
    "path": "Python/quickstarts/firestore-sync-auth/firebase.json",
    "content": "{\n  \"functions\": {\n    \"codebase\": \"firestore-sync-auth\"\n  }\n}\n"
  },
  {
    "path": "Python/quickstarts/firestore-sync-auth/functions/main.py",
    "content": "# Copyright 2023 Google Inc. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# [START all]\nfrom firebase_functions import firestore_fn\n\n\n# [START verifyComment]\n@firestore_fn.on_document_updated_with_auth_context(document=\"comments/{comment_id}\")\ndef verify_comment(event: Event[Change[DocumentSnapshot]]) -> None:\n\n    # Get the current and previous document values.\n    new_value = event.data.after\n    prev_value = event.data.before\n\n    # Get the auth context from the event\n    user_auth_type = event.auth_type\n    user_auth_id = event.auth_id\n\n    verified = False\n    if user_auth_type == \"system\":\n        # system-generated users are automatically verified\n        verified = True\n    elif user_auth_type in (\"unknown\", \"unauthenticated\"):\n        if user_auth_id.endswith(\"@example.com\"):\n            # admin users from a specific domain are verified\n            verified = True\n\n    # add auth medadata to the document\n    new_value.reference.update({\"created_by\": user_auth_id, \"verified\": verified})\n# [END verifyComment]\n# [END all]\n"
  },
  {
    "path": "Python/quickstarts/firestore-sync-auth/functions/requirements.txt",
    "content": "firebase-functions\nfirebase-admin\n"
  },
  {
    "path": "Python/quickstarts/https-time-server/README.md",
    "content": "# https-time-server\n"
  },
  {
    "path": "Python/quickstarts/https-time-server/firebase.json",
    "content": "{\n  \"functions\": {\n    \"codebase\": \"https-time-server\"\n  }\n}\n"
  },
  {
    "path": "Python/quickstarts/https-time-server/functions/main.py",
    "content": "# Copyright 2023 Google Inc. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http:#www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# [START additionalimports]\nfrom datetime import datetime\n# [END additionalimports]\n\n# [START functionsimport]\nfrom firebase_functions import https_fn, options\n# [END functionsimport]\n\n\n# [START all]\n# Returns the server's date. You must provide a `format` URL query parameter or `format` value in\n# the request body with which we'll try to format the date.\n#\n# Format must follow the Python datetime library.\n# See: https://docs.python.org/3.10/library/datetime.html#strftime-and-strptime-behavior\n#\n# Example format: \"%B %d %Y, %I:%M:%S %p\".\n# Example request using URL query parameters:\n#   https://us-central1-<project-id>.cloudfunctions.net/date?format=%25B%20%25d%20%25Y%2C%20%25I%3A%25M%3A%25S%20%25p\n# Example request using request body with cURL:\n#   curl -H 'Content-Type: application/json' /\n#        -d '{\"format\": \"%B %d %Y, %I:%M:%S %p\"}' /\n#        https://us-central1-<project-id>.cloudfunctions.net/date\n#\n# This endpoint supports CORS.\n# [START trigger]\n# [START usingMiddleware]\n@https_fn.on_request(cors=options.CorsOptions(cors_origins=\"*\", cors_methods=[\"get\", \"post\"]))\ndef date(req: https_fn.Request) -> https_fn.Response:\n    \"\"\"Get the server's local date and time.\"\"\"\n# [END usingMiddleware]\n# [END trigger]\n    # [START sendError]\n    # Forbidding PUT requests.\n    if req.method == \"PUT\":\n        return https_fn.Response(status=403, response=\"Forbidden!\")\n    # [END sendError]\n\n    # Reading date format from URL query parameter.\n    # [START readQueryParam]\n    format = req.args[\"format\"] if \"format\" in req.args else None\n    # [END readQueryParam]\n    # Reading date format from request body query parameter\n    if format is None:\n        # [START readBodyParam]\n        body_data = req.get_json(silent=True)\n        if body_data is None or \"format\" not in body_data:\n            return https_fn.Response(status=400, response=\"Format string missing\")\n        format = body_data[\"format\"]\n        # [END readBodyParam]\n\n    # [START sendResponse]\n    formatted_date = datetime.now().strftime(format)\n    print(f\"Sending Formatted date: {formatted_date}\")\n    return https_fn.Response(formatted_date)\n    # [END sendResponse]\n# [END all]\n"
  },
  {
    "path": "Python/quickstarts/https-time-server/functions/requirements.txt",
    "content": "firebase-functions\n"
  },
  {
    "path": "Python/quickstarts/monitor-cloud-logging/firebase.json",
    "content": "{\n  \"functions\": [\n    {\n      \"source\": \"functions\",\n      \"codebase\": \"monitor-cloud-logging\",\n      \"ignore\": [\n        \"venv\",\n        \".git\",\n        \"firebase-debug.log\",\n        \"firebase-debug.*.log\"\n      ]\n    }\n  ]\n}\n"
  },
  {
    "path": "Python/quickstarts/monitor-cloud-logging/functions/main.py",
    "content": "from firebase_functions import https_fn\nfrom firebase_functions.alerts import crashlytics_fn\nfrom firebase_admin import initialize_app, firestore\nimport datetime\nimport sys\n\n# [START loggerImport]\nfrom firebase_functions import logger\n# [END loggerImport]\n\napp = initialize_app()\n\n\n# [START helloLogs]\n@https_fn.on_request()\ndef hello_world(req: https_fn.Request) -> https_fn.Response:\n    # sends a log to Cloud Logging\n    logger.log(\"Hello logs!\")\n\n    return https_fn.Response(\"Hello from Firebase!\")\n# [END helloLogs]\n\n\n# [START logsKitchenSink]\n@https_fn.on_request()\ndef get_inspirational_quote(req: https_fn.Request) -> https_fn.Response:\n    firestore_client = firestore.client()\n    today = datetime.date.today()\n    quote_of_the_month_ref = (firestore_client.collection(\"quotes\").doc(str(\n        today.year)).collection(\"months\").doc(str(today.month)))\n\n    default_quote = \"Python has been an important part of Google since the beginning, and remains so as the system grows and evolves.\"\n\n    quote = None\n    try:\n        quote_of_the_month = quote_of_the_month_ref.get()\n\n        # Attach relevant debugging information with debug()\n        logger.debug(\n            \"Monthly quote fetch result\",\n            docRef=quote_of_the_month.path,\n            exists=quote_of_the_month.exists,\n            createTime=quote_of_the_month.createTime,\n        )\n\n        if quote_of_the_month.exists:\n            quote = quote_of_the_month.to_dict()[\"text\"]\n        else:\n            # Use warn() for lower-severity issues than error()\n            logger.warn(\n                \"Quote not found for month, sending default instead\",\n                doc_reference=quote_of_the_month.path,\n                date_requested=today.strftime(\"%Y-%m-%d\"),\n            )\n            quote = default_quote\n    except:\n        e = sys.exc_info()[0]\n        # [START logError]\n        # Attach an error object as the second argument\n        logger.error(\"Unable to read quote from Firestore, sending default instead\", error=e)\n        # [END logError]\n        quote = default_quote\n\n    # Attach relevant structured data to any log\n    logger.info(\"Sending a quote!\", quote=quote)\n    return https_fn.Response(\"Hello from Firebase!\")\n# [END logsKitchenSink]\n\n\n# [START customLogWrite]\n@crashlytics_fn.on_regression_alert_published()\ndef app_has_regression(alert: crashlytics_fn.CrashlyticsRegressionAlertEvent) -> None:\n    logger.write(\n        severity=\"EMERGENCY\",\n        message=\"Regression in production app\",\n        issue=alert.data.payload.issue,\n        last_occurred=alert.data.payload.resolve_time,\n    )\n    print(alert)\n# [END customLogWrite]\n"
  },
  {
    "path": "Python/quickstarts/monitor-cloud-logging/functions/requirements.txt",
    "content": "firebase_functions~=0.1.2"
  },
  {
    "path": "Python/quickstarts/pubsub-helloworld/README.md",
    "content": "# pubsub-helloworld\n"
  },
  {
    "path": "Python/quickstarts/pubsub-helloworld/firebase.json",
    "content": "{\n  \"functions\": {\n    \"codebase\": \"pubsub-helloworld\"\n  }\n}\n"
  },
  {
    "path": "Python/quickstarts/pubsub-helloworld/functions/main.py",
    "content": "# Copyright 2023 Google Inc. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport base64\n\n# [START import]\nfrom firebase_functions import pubsub_fn\n# [END import]\n\n\n# [START helloWorld]\n# [START trigger]\n@pubsub_fn.on_message_published(topic=\"topic-name\")\ndef hellopubsub(event: pubsub_fn.CloudEvent[pubsub_fn.MessagePublishedData]) -> None:\n    \"\"\"Log a message using data published to a Pub/Sub topic.\"\"\"\n# [END trigger]\n    # [START readBase64]\n    # Decode the PubSub message body.\n    message_body = base64.b64decode(event.data.message.data)\n    # [END readBase64]\n\n    # Print the message.\n    print(f\"Hello, {message_body.decode('utf-8') if message_body else 'World'}\")\n# [END helloWorld]\n\n\n@pubsub_fn.on_message_published(topic=\"another-topic-name\")\ndef hellopubsubjson(event: pubsub_fn.CloudEvent[pubsub_fn.MessagePublishedData]) -> None:\n    \"\"\"Log a message using data published as JSON to a Pub/Sub topic.\"\"\"\n    # [START readJson]\n    # Get the `name` attribute of the PubSub message JSON body.\n    try:\n        data = event.data.message.json\n    except ValueError:\n        print(\"PubSub message was not JSON\")\n        return\n    if data is None:\n        return\n    if \"name\" not in data:\n        print(\"No 'name' key\")\n        return\n    name = data[\"name\"]\n    # [END readJson]\n\n    # Print the message in the logs.\n    print(f\"Hello, {name}\")\n\n\n@pubsub_fn.on_message_published(topic=\"yet-another-topic-name\")\ndef hellopubsubattributes(event: pubsub_fn.CloudEvent[pubsub_fn.MessagePublishedData]) -> None:\n    \"\"\"Log a message using data published to a Pub/Sub topic as an attribute.\"\"\"\n    # [START readAttributes]\n    # Get the `name` attribute of the message.\n    if \"name\" not in event.data.message.attributes:\n        print(\"No 'name' attribute\")\n        return\n    name = event.data.message.attributes[\"name\"]\n    # [END readAttributes]\n\n    # Print the message in the logs.\n    print(f\"Hello, {name}\")\n"
  },
  {
    "path": "Python/quickstarts/pubsub-helloworld/functions/requirements.txt",
    "content": "firebase-functions\n"
  },
  {
    "path": "Python/quickstarts/uppercase-firestore/README.md",
    "content": "# uppercase-firestore\n"
  },
  {
    "path": "Python/quickstarts/uppercase-firestore/firebase.json",
    "content": "{\n  \"functions\": {\n    \"codebase\": \"uppercase-firestore\"\n  }\n}\n"
  },
  {
    "path": "Python/quickstarts/uppercase-firestore/functions/main.py",
    "content": "# Copyright 2023 Google Inc. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# [START all]\n# [START import]\n# The Cloud Functions for Firebase SDK to create Cloud Functions and set up triggers.\nfrom firebase_functions import firestore_fn, https_fn\n\n# The Firebase Admin SDK to access Cloud Firestore.\nfrom firebase_admin import initialize_app, firestore\nimport google.cloud.firestore\n\napp = initialize_app()\n# [END import]\n\n\n# [START addMessage]\n# [START addMessageTrigger]\n@https_fn.on_request()\ndef addmessage(req: https_fn.Request) -> https_fn.Response:\n    \"\"\"Take the text parameter passed to this HTTP endpoint and insert it into\n    a new document in the messages collection.\"\"\"\n# [END addMessageTrigger]\n    # Grab the text parameter.\n    original = req.args.get(\"text\")\n    if original is None:\n        return https_fn.Response(\"No text parameter provided\", status=400)\n\n    # [START adminSdkPush]\n    firestore_client: google.cloud.firestore.Client = firestore.client()\n\n    # Push the new message into Cloud Firestore using the Firebase Admin SDK.\n    _, doc_ref = firestore_client.collection(\"messages\").add({\"original\": original})\n\n    # Send back a message that we've successfully written the message\n    return https_fn.Response(f\"Message with ID {doc_ref.id} added.\")\n    # [END adminSdkPush]\n# [END addMessage]\n\n\n# [START makeUppercase]\n@firestore_fn.on_document_created(document=\"messages/{pushId}\")\ndef makeuppercase(event: firestore_fn.Event[firestore_fn.DocumentSnapshot | None]) -> None:\n    \"\"\"Listens for new documents to be added to /messages. If the document has\n    an \"original\" field, creates an \"uppercase\" field containg the contents of\n    \"original\" in upper case.\"\"\"\n\n    # Get the value of \"original\" if it exists.\n    if event.data is None:\n        return\n    try:\n        original = event.data.get(\"original\")\n    except KeyError:\n        # No \"original\" field, so do nothing.\n        return\n\n    # Set the \"uppercase\" field.\n    print(f\"Uppercasing {event.params['pushId']}: {original}\")\n    upper = original.upper()\n    event.data.reference.update({\"uppercase\": upper})\n# [END makeUppercase]\n# [END all]\n\n\n# [START makeUppercase2]\n@firestore_fn.on_document_written(document=\"messages/{pushId}\")\ndef makeuppercase2(\n        event: firestore_fn.Event[firestore_fn.Change[firestore_fn.DocumentSnapshot | None]]\n) -> None:\n    \"\"\"Listens for new documents to be added to /messages. If the document has\n    an \"original\" field, creates an \"uppercase\" field containg the contents of\n    \"original\" in upper case.\"\"\"\n\n    # Only edit data when it is first created.\n    if event.data.before is not None:\n        return\n\n    # Exit when the data is deleted.\n    if event.data.after is None:\n        return\n\n    # Get the value of \"original\" if it exists.\n    try:\n        original = event.data.after.get(\"original\")\n    except KeyError:\n        # No \"original\" field, so do nothing.\n        return\n\n    # Set the \"uppercase\" field.\n    print(f\"Uppercasing {event.params['pushId']}: {original}\")\n    upper = original.upper()\n    event.data.after.reference.update({\"uppercase\": upper})\n# [END makeUppercase2]\n"
  },
  {
    "path": "Python/quickstarts/uppercase-firestore/functions/requirements.txt",
    "content": "firebase-functions\nfirebase-admin\n"
  },
  {
    "path": "Python/quickstarts/uppercase-rtdb/README.md",
    "content": "# uppercase-rtdb\n"
  },
  {
    "path": "Python/quickstarts/uppercase-rtdb/firebase.json",
    "content": "{\n  \"functions\": {\n    \"codebase\": \"uppercase-rtdb\"\n  }\n}\n"
  },
  {
    "path": "Python/quickstarts/uppercase-rtdb/functions/main.py",
    "content": "# Copyright 2023 Google Inc. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# [START all]\nfrom typing import Any\nfrom urllib import parse as urllib_parse\n\n# [START import]\n# The Cloud Functions for Firebase SDK to create Cloud Functions and set up triggers.\nfrom firebase_functions import db_fn, https_fn\n\n# The Firebase Admin SDK to access the Firebase Realtime Database.\nfrom firebase_admin import initialize_app, db\n\napp = initialize_app()\n# [END import]\n\n\n# [START addMessage]\n# [START addMessageTrigger]\n@https_fn.on_request()\ndef addmessage(req: https_fn.Request) -> https_fn.Response:\n    \"\"\"Take the text parameter passed to this HTTP endpoint and insert it into\n    the Realtime Database under the path /messages/{pushId}/original\"\"\"\n# [END addMessageTrigger]\n    # Grab the text parameter.\n    original = req.args.get(\"text\")\n    if original is None:\n        return https_fn.Response(\"No text parameter provided\", status=400)\n    # [START adminSdkPush]\n    # Push the new message into the Realtime Database using the Firebase Admin SDK.\n    ref = db.reference(\"/messages\").push({\"original\": original})  # type: ignore\n\n    # Redirect with 303 SEE OTHER to the URL of the pushed object.\n    scheme, location, path, query, fragment = (\n        b.decode() for b in urllib_parse.urlsplit(app.options.get(\"databaseURL\")))\n    path = f\"{ref.path}.json\"\n    return https_fn.Response(\n        status=303,\n        headers={\"Location\": urllib_parse.urlunsplit((scheme, location, path, query, fragment))})\n    # [END adminSdkPush]\n# [END addMessage]\n\n\n# [START makeUppercase]\n@db_fn.on_value_created(reference=\"/messages/{pushId}/original\")\ndef makeuppercase(event: db_fn.Event[Any]) -> None:\n    \"\"\"Listens for new messages added to /messages/{pushId}/original and\n    creates an uppercase version of the message to /messages/{pushId}/uppercase\n    \"\"\"\n\n    # Grab the value that was written to the Realtime Database.\n    original = event.data\n    if not isinstance(original, str):\n        print(f\"Not a string: {event.reference}\")\n        return\n\n    # Use the Admin SDK to set an \"uppercase\" sibling.\n    print(f\"Uppercasing {event.params['pushId']}: {original}\")\n    upper = original.upper()\n    parent = db.reference(event.reference).parent\n    if parent is None:\n        print(\"Message can't be root node.\")\n        return\n    parent.child(\"uppercase\").set(upper)\n# [END makeUppercase]\n\n\n# [START makeUppercase2]\n@db_fn.on_value_written(reference=\"/messages/{pushId}/original\")\ndef makeuppercase2(event: db_fn.Event[db_fn.Change]) -> None:\n    \"\"\"Listens for new messages added to /messages/{pushId}/original and\n    creates an uppercase version of the message to /messages/{pushId}/uppercase\n    \"\"\"\n\n    # Only edit data when it is first created.\n    if event.data.before is not None:\n        return\n\n    # Exit when the data is deleted.\n    if event.data.after is None:\n        return\n\n    # Grab the value that was written to the Realtime Database.\n    original = event.data.after\n    if not hasattr(original, \"upper\"):\n        print(f\"Not a string: {event.reference}\")\n        return\n\n    # Use the Admin SDK to set an \"uppercase\" sibling.\n    print(f\"Uppercasing {event.params['pushId']}: {original}\")\n    upper = original.upper()\n    parent = db.reference(event.reference).parent\n    if parent is None:\n        print(\"Message can't be root node.\")\n        return\n    parent.child(\"uppercase\").set(upper)\n# [END makeUppercase2]\n# [END all]\n"
  },
  {
    "path": "Python/quickstarts/uppercase-rtdb/functions/requirements.txt",
    "content": "firebase-functions\nfirebase-admin\n"
  },
  {
    "path": "Python/remote-config-diff/README.md",
    "content": "# remote-config-diff\n"
  },
  {
    "path": "Python/remote-config-diff/firebase.json",
    "content": "{\n  \"functions\": {\n    \"codebase\": \"remote-config-diff\"\n  }\n}\n"
  },
  {
    "path": "Python/remote-config-diff/functions/main.py",
    "content": "# Copyright 2023 Google Inc. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# [START all]\n# [START import]\n# The Cloud Functions for Firebase SDK to set up triggers and logging.\nfrom firebase_functions import remote_config_fn\n\n# The Firebase Admin SDK to obtain access tokens.\nimport firebase_admin\n\napp = firebase_admin.initialize_app()\n\nimport deepdiff\nimport requests\n# [END import]\n\n\n# [START showconfigdiff]\n@remote_config_fn.on_config_updated()\ndef showconfigdiff(event: remote_config_fn.CloudEvent[remote_config_fn.ConfigUpdateData]) -> None:\n    \"\"\"Log the diff of the most recent Remote Config template change.\"\"\"\n\n    # Obtain an access token from the Admin SDK\n    access_token = app.credential.get_access_token().access_token\n\n    # Get the version number from the event object\n    current_version = int(event.data.version_number)\n\n    # Figure out the differences between templates\n    remote_config_api = (\"https://firebaseremoteconfig.googleapis.com/v1/\"\n                         f\"projects/{app.project_id}/remoteConfig\")\n    current_template = requests.get(remote_config_api,\n                                    params={\"versionNumber\": current_version},\n                                    headers={\"Authorization\": f\"Bearer {access_token}\"})\n    previous_template = requests.get(remote_config_api,\n                                     params={\"versionNumber\": current_version - 1},\n                                     headers={\"Authorization\": f\"Bearer {access_token}\"})\n    diff = deepdiff.DeepDiff(previous_template, current_template)\n\n    # Log the difference\n    print(diff.pretty())\n# [END showconfigdiff]\n# [END all]\n"
  },
  {
    "path": "Python/remote-config-diff/functions/requirements.txt",
    "content": "firebase-functions\nfirebase-admin\ndeepdiff\nrequests\n"
  },
  {
    "path": "Python/requirements.txt",
    "content": "yapf~=0.40.2\n"
  },
  {
    "path": "Python/taskqueues-backup-images/README.md",
    "content": "# taskqueues-backup-images\n"
  },
  {
    "path": "Python/taskqueues-backup-images/firebase.json",
    "content": "{\n  \"functions\": {\n    \"codebase\": \"taskqueues-backup-images\"\n  }\n}\n"
  },
  {
    "path": "Python/taskqueues-backup-images/functions/main.py",
    "content": "# Copyright 2023 Google Inc. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# [START v2imports]\n# Dependencies for task queue functions.\nfrom google.cloud import tasks_v2\nimport requests\nfrom firebase_functions.options import RetryConfig, RateLimits, SupportedRegion\n\n# Dependencies for image backup.\nfrom datetime import datetime, timedelta\nimport json\nimport pathlib\nfrom urllib.parse import urlparse\nfrom firebase_admin import initialize_app, storage, functions\nfrom firebase_functions import https_fn, tasks_fn, params\nimport google.auth\nfrom google.auth.transport.requests import AuthorizedSession\n# [END v2imports]\n\napp = initialize_app()\n\nBACKUP_START_DATE = datetime(1995, 6, 17)\nBACKUP_COUNT = params.IntParam(\"BACKUP_COUNT\", default=100).value\nHOURLY_BATCH_SIZE = params.IntParam(\"HOURLY_BATCH_SIZE\", default=600).value\nBACKUP_BUCKET = params.StringParam(\n    \"BACKUP_BUCKET\", input=params.ResourceInput(type=params.ResourceType.STORAGE_BUCKET)).value\nNASA_API_KEY = params.StringParam(\"NASA_API_KEY\").value\n\n\n# [START v2TaskFunctionSetup]\n@tasks_fn.on_task_dispatched(retry_config=RetryConfig(max_attempts=5, min_backoff_seconds=60),\n                             rate_limits=RateLimits(max_concurrent_dispatches=10))\ndef backupapod(req: tasks_fn.CallableRequest) -> str:\n    \"\"\"Grabs Astronomy Photo of the Day (APOD) using NASA's API.\"\"\"\n# [END v2TaskFunctionSetup]\n    try:\n        date = req.data[\"date\"]\n    except KeyError:\n        raise https_fn.HttpsError(code=https_fn.FunctionsErrorCode.INVALID_ARGUMENT,\n                                  message=\"Invalid payload. Must include date.\")\n\n    print(f\"Requesting data from APOD API for date {date}\")\n    api_resp = requests.get(url=\"https://api.nasa.gov/planetary/apod\",\n                            params={\n                                \"date\": date,\n                                \"api_key\": NASA_API_KEY\n                            })\n    if not api_resp.ok:\n        print(f\"Request to NASA APOD API failed with reponse {api_resp.status_code}\")\n        match api_resp.status_code:\n            case 404:  # APOD not published for the day. This is fine!\n                print(\"No APOD today.\")\n                return \"No APOD today.\"\n            case 500:\n                raise https_fn.HttpsError(code=https_fn.FunctionsErrorCode.UNAVAILABLE,\n                                          message=\"APOD API temporarily not available.\")\n            case _:\n                raise https_fn.HttpsError(code=https_fn.FunctionsErrorCode.INTERNAL,\n                                          message=\"Uh-oh. Something broke.\")\n    apod = api_resp.json()\n    pic_url = apod[\"hdurl\"]\n\n    print(f\"Got URL {pic_url} from NASA API for date {date}. Fetching...\")\n    pic_resp = requests.get(pic_url)\n    pic_type = pic_resp.headers.get(\"Content-Type\")\n    if pic_type is None:\n        pic_type = \"image/jpeg\"\n\n    print(\"Uploading to Cloud Storage\")\n    bucket = storage.bucket(BACKUP_BUCKET)\n    ext = pathlib.PurePosixPath(urlparse(pic_url).path).suffix\n    pic_blob = bucket.blob(f\"apod/{date}{ext}\")\n    try:\n        pic_blob.upload_from_string(pic_resp.content, content_type=pic_type)\n    except:\n        raise https_fn.HttpsError(code=https_fn.FunctionsErrorCode.INTERNAL,\n                                  message=\"Uh-oh. Something broke.\")\n\n    print(f\"Saved {pic_url}\")\n    return f\"Saved {pic_url}\"\n\n\n# [START v2EnqueueTasks]\n@https_fn.on_request()\ndef enqueuebackuptasks(_: https_fn.Request) -> https_fn.Response:\n    \"\"\"Adds backup tasks to a Cloud Tasks queue.\"\"\"\n    task_queue = functions.task_queue(\"backupapod\")\n    target_uri = get_function_url(\"backupapod\")\n\n    for i in range(BACKUP_COUNT):\n        batch = i // HOURLY_BATCH_SIZE\n\n        # Delay each batch by N hours\n        schedule_delay = timedelta(hours=batch)\n        schedule_time = datetime.now() + schedule_delay\n\n        dispatch_deadline_seconds = 60 * 5  # 5 minutes\n\n        backup_date = BACKUP_START_DATE + timedelta(days=i)\n        body = {\"data\": {\"date\": backup_date.isoformat()[:10]}}\n        task_options = functions.TaskOptions(schedule_time=schedule_time,\n                                             dispatch_deadline_seconds=dispatch_deadline_seconds,\n                                             uri=target_uri)\n        task_queue.enqueue(body, task_options)\n    return https_fn.Response(status=200, response=f\"Enqueued {BACKUP_COUNT} tasks\")\n# [END v2EnqueueTasks]\n\n\n# [START v2GetFunctionUri]\ndef get_function_url(name: str, location: str = SupportedRegion.US_CENTRAL1) -> str:\n    \"\"\"Get the URL of a given v2 cloud function.\n\n    Params:\n        name: the function's name\n        location: the function's location\n\n    Returns: The URL of the function\n    \"\"\"\n    credentials, project_id = google.auth.default(\n        scopes=[\"https://www.googleapis.com/auth/cloud-platform\"])\n    authed_session = AuthorizedSession(credentials)\n    url = (\"https://cloudfunctions.googleapis.com/v2beta/\" +\n           f\"projects/{project_id}/locations/{location}/functions/{name}\")\n    response = authed_session.get(url)\n    data = response.json()\n    function_url = data[\"serviceConfig\"][\"uri\"]\n    return function_url\n# [END v2GetFunctionUri]\n"
  },
  {
    "path": "Python/taskqueues-backup-images/functions/requirements.txt",
    "content": "firebase-functions\nfirebase-admin\ngoogle-auth\ngoogle-cloud-tasks\nrequests\n"
  },
  {
    "path": "Python/testlab-to-slack/README.md",
    "content": "# testlab-to-slack\n"
  },
  {
    "path": "Python/testlab-to-slack/firebase.json",
    "content": "{\n  \"functions\": {\n    \"codebase\": \"testlab-to-slack\"\n  }\n}\n"
  },
  {
    "path": "Python/testlab-to-slack/functions/main.py",
    "content": "# Copyright 2022 Google Inc. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# [START all]\n# [START import]\n# The Cloud Functions for Firebase SDK to set up triggers and logging.\nfrom firebase_functions import test_lab_fn, params\n\n# The requests library to send web requests to Slack.\nimport requests\n# [END import]\n\n# [START postToSlack]\nSLACK_WEBHOOK_URL = params.SecretParam(\"SLACK_WEBHOOK_URL\")\n\n\ndef post_to_slack(title: str, details: str) -> requests.Response:\n    \"\"\"Posts a message to Slack via a Webhook.\"\"\"\n    return requests.post(SLACK_WEBHOOK_URL.value,\n                         json={\n                             \"blocks\": [{\n                                 \"type\": \"section\",\n                                 \"text\": {\n                                     \"type\": \"mrkdwn\",\n                                     \"text\": title\n                                 }\n                             }, {\n                                 \"type\": \"divider\"\n                             }, {\n                                 \"type\": \"section\",\n                                 \"text\": {\n                                     \"type\": \"mrkdwn\",\n                                     \"text\": details\n                                 }\n                             }]\n                         })\n# [END postToSlack]\n\n\n# [START getSlackmoji]\ndef slackmoji(status: test_lab_fn.TestState | test_lab_fn.OutcomeSummary) -> str:\n    \"\"\"Convert a test result status into a Slackmoji.\"\"\"\n    status_slackmoji: dict[test_lab_fn.TestState | test_lab_fn.OutcomeSummary, str] = {\n        test_lab_fn.OutcomeSummary.SUCCESS: \":tada:\",\n        test_lab_fn.OutcomeSummary.FAILURE: \":broken_heart:\",\n        test_lab_fn.OutcomeSummary.INCONCLUSIVE: \":question:\",\n        test_lab_fn.OutcomeSummary.SKIPPED: \":arrow_heading_down:\",\n        test_lab_fn.TestState.VALIDATING: \":thought_balloon:\",\n        test_lab_fn.TestState.PENDING: \":soon:\",\n        test_lab_fn.TestState.FINISHED: \":white_check_mark:\",\n        test_lab_fn.TestState.ERROR: \":red_circle:\",\n        test_lab_fn.TestState.INVALID: \":large_orange_diamond:\"\n    }\n    return status_slackmoji[status] if status in status_slackmoji else \"\"\n# [END getSlackmoji]\n\n\n# [START posttestresultstoslack]\n@test_lab_fn.on_test_matrix_completed(secrets=[\"SLACK_WEBHOOK_URL\"])\ndef posttestresultstoslack(\n        event: test_lab_fn.CloudEvent[test_lab_fn.TestMatrixCompletedData]) -> None:\n    \"\"\"Posts a test matrix result to Slack.\"\"\"\n\n    # Obtain Test Matrix properties from the CloudEvent\n    test_matrix_id = event.data.test_matrix_id\n    state = event.data.state\n    outcome_summary = event.data.outcome_summary\n\n    # Create the title of the message\n    title = f\"{slackmoji(state)} {slackmoji(outcome_summary)} {test_matrix_id}\"\n\n    # Create the details of the message\n    details = (f\"Status: *{state}* {slackmoji(state)}\\n\"\n               f\"Outcome: *{outcome_summary}* {slackmoji(outcome_summary)}\")\n\n    # Post the message to Slack\n    response = post_to_slack(title, details)\n\n    # Log the response\n    print(response.status_code, response.text)\n# [END posttestresultstoslack]\n# [END all]\n"
  },
  {
    "path": "Python/testlab-to-slack/functions/requirements.txt",
    "content": "firebase-functions\nrequests\n"
  },
  {
    "path": "Python/thumbnails/README.md",
    "content": "# thumbnails\n"
  },
  {
    "path": "Python/thumbnails/firebase.json",
    "content": "{\n  \"functions\": {\n    \"codebase\": \"thumbnails\"\n  }\n}\n"
  },
  {
    "path": "Python/thumbnails/functions/main.py",
    "content": "# Copyright 2023 Google Inc. All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# [START storageImports]\n# [START storageAdditionalImports]\nimport io\nimport pathlib\n\nfrom PIL import Image\n\nfrom firebase_admin import initialize_app\n\ninitialize_app()\nfrom firebase_admin import storage\n# [END storageAdditionalImports]\n\n# [START storageSDKImport]\nfrom firebase_functions import storage_fn\n# [END storageSDKImport]\n# [END storageImports]\n\n\n# [START storageGenerateThumbnail]\n# [START storageGenerateThumbnailTrigger]\n@storage_fn.on_object_finalized()\ndef generatethumbnail(event: storage_fn.CloudEvent[storage_fn.StorageObjectData]):\n    \"\"\"When an image is uploaded in the Storage bucket, generate a thumbnail\n    automatically using Pillow.\"\"\"\n# [END storageGenerateThumbnailTrigger]\n\n    # [START storageEventAttributes]\n    bucket_name = event.data.bucket\n    file_path = pathlib.PurePath(event.data.name)\n    content_type = event.data.content_type\n    # [END storageEventAttributes]\n\n    # [START storageStopConditions]\n    # Exit if this is triggered on a file that is not an image.\n    if not content_type or not content_type.startswith(\"image/\"):\n        print(f\"This is not an image. ({content_type})\")\n        return\n\n    # Exit if the image is already a thumbnail.\n    if file_path.name.startswith(\"thumb_\"):\n        print(\"Already a thumbnail.\")\n        return\n    # [END storageStopConditions]\n\n    # [START storageThumbnailGeneration]\n    bucket = storage.bucket(bucket_name)\n\n    image_blob = bucket.blob(str(file_path))\n    image_bytes = image_blob.download_as_bytes()\n    image = Image.open(io.BytesIO(image_bytes))\n\n    image.thumbnail((200, 200))\n    thumbnail_io = io.BytesIO()\n    image.save(thumbnail_io, format=\"png\")\n    thumbnail_path = file_path.parent / pathlib.PurePath(f\"thumb_{file_path.stem}.png\")\n    thumbnail_blob = bucket.blob(str(thumbnail_path))\n    thumbnail_blob.upload_from_string(thumbnail_io.getvalue(), content_type=\"image/png\")\n    # [END storageThumbnailGeneration]\n# [END storageGenerateThumbnail]\n"
  },
  {
    "path": "Python/thumbnails/functions/requirements.txt",
    "content": "firebase-functions\nfirebase-admin\npillow\n"
  },
  {
    "path": "README.md",
    "content": "# Cloud Functions for Firebase Sample Library\n\nThis repository contains a collection of samples showcasing some typical uses of [Cloud Functions for Firebase](https://firebase.google.com/features/functions).\n\nSamples are available for **Node** (1st and 2nd gen) and **Python** (2nd gen).\n\n> Note: Python support in Cloud Functions for Firebase is a public preview. This means that the functionality might change in backward-incompatible ways. A preview release is not subject to any SLA or deprecation policy and may receive limited or no support.\n\n### What's Cloud Functions for Firebase?\n\nCloud Functions is a hosted, private, and scalable Node.js environment where you can run JavaScript or Python code. [Cloud Functions for Firebase](https://firebase.google.com/features/functions) integrates the Firebase platform by letting you write code that responds to events and invokes functionality exposed by other Firebase features.\n\n## Prerequisites\n\n> All samples require the Blaze pay-as-you-go billing plan to deploy. Learn more about [pricing](https://firebase.google.com/pricing#cloud-functions).\n\nTo learn how to get started with Cloud Functions for Firebase by having a look at the [Getting Started Guide](https://firebase.google.com/docs/functions/get-started), trying the [quickstart samples](/Node/quickstarts) and looking at [the documentation](https://firebase.google.com/docs/functions).\n\n<a name=\"quickstarts\"></a>\n## Quickstarts\n\nMinimal samples for each Cloud Functions trigger type.\n\n### Quickstart: Uppercaser for Firestore\n\n- [Node 2nd gen](/Node/quickstarts/uppercase-firestore/)\n- [Python](/Python/quickstarts/uppercase-firestore/)\n- [Node 1st gen](/Node-1st-gen/quickstarts/uppercase-firestore/)\n\nThis quickstart sample demonstrates using **Cloud Functions** triggered by **Firestore events**. The function transforms message text written to Firestore to uppercase.\n\n### Quickstart: Add numbers and sanitize text with callable functions\n\n- [Node 2nd gen](/Node/quickstarts/callable-functions/)\n- [Python](/Python/quickstarts/callable-functions)\n- [Node 1st gen](/Node-1st-gen/quickstarts/callable-functions/)\n\n### HTTPS trigger quickstart: Time Server\n\n- [Node 2nd gen](/Node/quickstarts/https-time-server/)\n- [Python](/Python/quickstarts/https-time-server/)\n- [Node 1st gen](/Node-1st-gen/quickstarts/https-time-server/)\n\nThis quickstart sample demonstrates using **Cloud Functions** triggered by **HTTPS requests**. The function returns the current server time and allows for date time formatting.\n\n### Quickstart: Uppercaser for Realtime Database\n\n- [Node 2nd gen](/Node/quickstarts/uppercase-rtdb/)\n- [Python](/Python/quickstarts/uppercase-rtdb/)\n- [Node 1st gen](/Node-1st-gen/quickstarts/uppercase-rtdb/)\n\nThis quickstart sample demonstrates using **Cloud Functions** triggered by **Realtime Database events**. The function transforms message text written to Realtime Database to uppercase.\n\n### Hosting triggered HTTPS function quickstart: Big Ben\n\n- [Node 1st gen](/Node-1st-gen/quickstarts/big-ben/)\n\nThis quickstart demonstrates using **Cloud Functions** with an HTTPS trigger that's triggered by a Firebase Hosting URL. The function will display a repeated number of \"BONG\"s depending on the hour of the day.\n\n### Cloud Storage trigger quickstart: Thumbnail generator\n\n- [Node 2nd gen](/Node/quickstarts/thumbnails/)\n- [Python](/Python/quickstarts/thumbnails/)\n- [Node 1st gen](/Node-1st-gen/quickstarts/thumbnails/)\n\nThis quickstart sample demonstrates using **Cloud Functions** triggered by **Firebase Storage events**. The function generates a thumbnail of uploaded images.\n\n### Auth trigger quickstart: Welcome Email\n\n> Auth user create and delete triggers aren't yet supported by 2nd gen functions\n\n- [Node 1st gen](/Node-1st-gen/quickstarts/email-users/)\n\nThis quickstart sample demonstrates using **Cloud Functions** triggered by **Firebase Auth events**. The function sends a Welcome Email when user accounts are created (or when users sign-in using an Identity Provider for the first time) and sends a Goodbye Email when user accounts are deleted.\n\n### Auth blocking trigger quickstart: Validate and check user status\n\n- [Node 2nd gen](/Node/quickstarts/auth-blocking-functions/)\n- [Python](/Python/quickstarts/auth-blocking-functions/)\n- [Node 1st gen](/Node-1st-gen/quickstarts/auth-blocking-functions/)\n\nThis quickstart demonstrates using **Auth blocking functions** to validate a user's email before they are allowed to sign in, and to see if a user is part of a list of banned users in Firestore.\n\n### PubSub trigger quickstart: Hello World\n\n- [Node 2nd gen](/Node/quickstarts/pubsub-helloworld/)\n- [Python](/Python/quickstarts/pubsub-helloworld/)\n- [Node 1st gen](/Node-1st-gen/quickstarts/pubsub-helloworld/)\n\nThis quickstart sample demonstrates using **Cloud Functions** triggered by **PubSub events**. The functions log the PubSub payload in a Hello world message.\n\n### Test Lab trigger quickstart: Log when a matrix completes\n\n- [Node 2nd gen](/Node/quickstarts/testlab-matrix-completed/)\n- [Python](/Python/quickstarts/testlab-matrix-completed)\n\n### Firebase Alerts trigger quickstart: Send crash reports to Discord\n\n- [Node 2nd gen](/Node/alerts-to-discord/)\n- [Python](/Python/alerts-to-discord/)\n- Node 1st gen: not supported\n\nTrigger a function based on a Firebase Alert, and send information about the alert to a channel in a Discord server.\n\n### Custom Events: Save image metadata\n\n- [Node 2nd gen](/Node/quickstarts/custom-events/)\n- [Python](/Python/quickstarts/custom-events)\n- Node 1st gen: not supported\n\nLearn how to trigger a function based on an event sent by an extension\n\n### Unit testing\n\n- [Test with Jest](/Node/test-functions-jest/)\n    - [Test with Jest and TypeScript](/Node/test-functions-jest-ts/)\n- [Test with Mocha](/Node/test-functions-mocha/)\n\n<a name=\"environment\"></a>\n## Development Boilerplates\n\nThe Firebase CLI generates sample code for Cloud Functions using JavaScript or TypeScript.\n\n### Server-side generated pages w/ Handlebars templating and user sessions\n\n- [Node (1st gen)](/Node-1st-gen/template-handlebars)\n\nThis sample shows how to serve server-side generated HTML pages using the [HandlebarsJs](http://handlebarsjs.com/) templating system and serve user-specific content by always passing the Firebase ID token in a `__session` cookie.\n\n<a name=\"image\"></a>\n## Image Processing\n\nHere are a few samples that show how you can process or analyze images using Cloud Functions.\n\n### Image Maker\n\n- [Node (1st gen)](/Node-1st-gen/image-maker)\n\nThis sample demonstrates how to create various customized images such as sparkline or sphere charts through Cloud Functions and Hosting and serve it to the client.\nUses an HTTP trigger.\n\n### Convert images after upload\n\n- [Node (1st gen)](/Node-1st-gen/convert-images)\n\nDemonstrates how to automatically convert images that are uploaded to Firebase Storage to JPEG using ImageMagick.\nUses a Firebase Storage trigger.\n\n### Moderate offensive images\n\n- [Node (1st gen)](/Node-1st-gen/moderate-images/)\n\nDemonstrates how to automatically moderate offensive images that are uploaded to Firebase Storage by using the Google Cloud Vision API to detect offensive images and ImageMagick to blur these images.\nUses a Firebase Storage trigger.\n\n### Extract image metadata\n\n- [Node (1st gen)](/Node-1st-gen/exif-images/)\n\nDemonstrates how to automatically extract image's metadata using ImageMagick for images that are uploaded to Firebase Storage.\nUses a Firebase Storage trigger.\n\n### Task Queues: back up images from an API\n\n- [Node 2nd gen](/Node/taskqueues-backup-images/)\n- [Python](/Python/taskqueues-backup-images/)\n\nSee how to use Task Queues to meter traffic to a rate-limited API.\n\n\n<a name=\"rtdb\"></a>\n## Firebase Realtime Database Data Consistency\n\nThese samples show how to implement automatic data consistency such as keeping a count of children, having a max amount of node childs, cleaning up old data etc...\n\n### LastModified Firebase Realtime Database tracking\n\n- [Node (1st gen)](/Node-1st-gen/lastmodified-tracking)\n\nTracking when the Firebase Database (or a subset) was last modified.\nUses a Realtime Database trigger.\n\n### Firebase Database child nodes count\n\n- [Node (1st gen)](/Node-1st-gen/child-count)\n\nKeeps track of the number of child nodes of a Firebase Database element allowing clients to filter or order results using the child count.\nThis can be useful to keep track of the number of \"likes\" or \"followers\" of something shared through social media.\nUses a Realtime Database trigger.\n\n### Limit number of child nodes\n\n- [Node (1st gen)](/Node-1st-gen/limit-children)\n\nMakes sure that the number of child nodes stays below a certain threshold. This can be useful to limit the number of lines of logs or chat history below a given number.\nUses a Realtime Database trigger.\n\n### Removing old items from a list\n\n- [Node (1st gen)](/Node-1st-gen/delete-old-child-nodes)\n\nThis sample shows how to remove child nodes older than 2 hours from a Firebase Database list. This can be useful for removing outdated items from a collection.\nUses a Realtime Database trigger.\n\n<a name=\"other\"></a>\n## Solve other common use cases\n\n### Send FCM notifications\n\n- [Node (1st gen)](/Node-1st-gen/fcm-notifications)\n\nThis sample demonstrates how to send a Firebase Cloud Messaging (FCM) notification from a Realtime Database triggered Function when users get new followers. The sample also features a Web UI to experience the FCM notification.\nUses a Realtime Database trigger.\n\n### Google Assistant says ordinal of given number\n\n- [Node (1st gen)](/Node-1st-gen/assistant-say-number)\n\nThis sample shows how to create an action for the Google Home/Assistant using the Actions SDK hosted on Cloud Functions. The sample action asks users to say a number and reads out the ordinal of that number.\nUses an HTTP trigger.\n\n### Authenticated JSON API\n\n- [Node (1st gen)](/Node-1st-gen/authenticated-json-api)\n\nThis sample shows how to authenticate access to a JSON API to only allow access to data for a specific Firebase user.\nUses an HTTP trigger.\n\n### Authorized HTTP endpoint\n\n- [Node (1st gen)](/Node-1st-gen/authorized-https-endpoint)\n\nThis sample shows how to restrict an HTTPS Function to only the Firebase users of your app.\nOnly users who pass a valid Firebase ID token as a Bearer token in the `Authorization` header of the HTTP request or in a `__session` cookie are authorized to use the function.\nChecking the ID token is done with an ExpressJs middleware that also passes the decoded ID token in the Express request object.\nUses an HTTP trigger.\n\n### Authorize with 3rd-party authentication providers\n\n[Okta](/Node-1st-gen/okta-auth), [LinkedIn](/Node-1st-gen/linkedin-auth), [Spotify](/Node-1st-gen/spotify-auth), [Instagram](/Node-1st-gen/instagram-auth), or [Basic Auth](/Node-1st-gen/username-password-auth)\n\nDemonstrates how to authorize with a 3rd party sign-in mechanism, create a Firebase custom auth token, update the user's profile and authorize Firebase.\nUses an HTTP trigger.\n\n### Post GitHub commits to Slack channel\n\n- [Node (1st gen)](/Node-1st-gen/github-to-slack)\n\nDemonstrates how to automatically post GitHub commits to a Slack channel using an HTTPS triggered Function.\n\n### Create and charge customers with [Stripe](/Node-1st-gen/stripe) or [Paypal](/Node-1st-gen/paypal)\n\nDemonstrates hows to integrate Firebase Auth and the Realtime database with Stripe via the Stripe Node.js library and shows how to create HTTP endpoints to charge customers via Paypal.\n\n### Text moderation\n\n- [Node (1st gen)](/Node-1st-gen/text-moderation)\n\nDemonstrates how to moderate user input text for bad words. This can be used to moderate usernames, chat or forum messages.\nUses a Realtime Database trigger.\n\n### Email confirmation\n\n- [Node (1st gen)](/Node-1st-gen/email-confirmation)\n\nSends email confirmation after users subscribed to a mailing list.\nUses a Realtime Database trigger.\n\n### Automatic message translation\n\n- [Node (1st gen)](/Node-1st-gen/message-translation)\n\nIntegrates the Google Translate API to perform automatic text translation across any number of languages. Language codes can be stored in Firebase for on the fly changes.\nUses a Realtime Database trigger.\n\n### Automatic URL shortener\n\n- [Node (1st gen)](/Node-1st-gen/url-shortener)\n\nIntegrates the Bit.ly API to shorten URLs automatically as they are added to the database.\nUses a Realtime Database trigger.\n\n### Full-text search for [Realtime Database](/Node-1st-gen/fulltext-search) or [Firestore](/Node-1st-gen/fulltext-search-firestore)\n\nEnable full-text search on Firebase Database data or Firestore documents by using a hosted search service.\nUses a Realtime Database or Firestore trigger.\n\n### User data cleanup\n\n- [Node (1st gen)](/Node-1st-gen/user-data-cleanup)\n\nDeletes all associated user data in the Realtime database when a user deletes his Firebase account.\nUses an Auth trigger.\n**This code has moved to its own repo at\nhttps://github.com/firebase/user-data-protection**\n\n### Export your data to a Google Spreadsheet\n\n- [Node (1st gen)](/Node-1st-gen/google-sheet-sync)\n\nThis sample demonstrates how to sync new data written to a Firebase database to a Google Sheet. It includes a method for obtaining, storing, and using Oauth2 tokens for Google API access.\nUses HTTPS triggers and Realtime Database triggers.\n\n### Export your data to Big Query\n\n- [Node (1st gen)](/Node-1st-gen/bigquery-import)\n\nCopies Firebase Database elements into BigQuery automatically. This can be useful for instance for further logs analysis.\nUses a Realtime Database trigger.\n\n### Webhook upon Firebase Database writes\n\n- [Node (1st gen)](/Node-1st-gen/minimal-webhook)\n\nWriting to the Firebase Database triggers a request to a callback URL (a Webhook). The content of the modified Data is sent to the Webhook.\nUses a Realtime Database trigger.\n\n### Send a survey when users update your app\n\n- [Node (1st gen)](/Node-1st-gen/survey-app-update)\n\nThis sample shows how to send a survey to your users who have updated your app. App Update is detected using a Firebase Analytics event.\nUses an Analytics trigger.\n\n### Send a coupon to user who have completed a purchase\n\n- [Node (1st gen)](/Node-1st-gen/coupon-on-purchase)\n\nThis sample shows how to send a coupon to your users who have just purchased something. 10% off on your next purchase!\nUses an Analytics trigger.\n\n### Delete inactive users accounts via cron\n\n- [Node (1st gen)](/Node-1st-gen/delete-unused-accounts-cron)\n\nPeriodically deletes the accounts of users who have not signed in in the last month.\nUses an HTTPS trigger.\n\n### Developer Motivator\n\n- [Node (1st gen)](/Node-1st-gen/developer-motivator)\n\nThis sample demonstrates how to send a Firebase Cloud Messaging (FCM) notification to the developer device each time your app gains or loses a user.\nUses an Analytics trigger.\n\n### Audio Files Conversion\n\n- [Node (1st gen)](/Node-1st-gen/ffmpeg-convert-audio)\n\nThis sample uses ffmpeg / fluent-ffmpeg and automatically converts audio files that are uploaded to Cloud Storage to FLAC file format with mono-channel audio @ 16000hz.\nUses a Storage trigger.\n\n### Presence for Firestore\n\n- [Node (1st gen)](/Node-1st-gen/presence-firestore)\n\nBuild a simple online / offline status indicator for your users by leveraging Firestore and Realtime Database together.\nUses a Realtime Database trigger.\n\n### Publish Models to Firebase ML\n\n- [Node (1st gen)](/Node-1st-gen/publish-model)\n\nAutomatically publishes models to Firebase ML for each TensorFlow Lite file that is uploaded to Firebase Storage.\n\n### Get information about a YouTube channel\n\n- [Node (1st gen)](/Node-1st-gen/youtube)\n\nThis sample shows how to query the Youtube Data API.\nUses an HTTPS trigger.\n\n## Contributing\n\nWe'd love that you contribute to the project. Before doing so please read our [Contributor guide](CONTRIBUTING.md).\n\n## License\n\n© Google, 2015-2023. Licensed under an [Apache-2](LICENSE) license.\n\n## Build Status\n\n[![Actions Status][gh-actions-badge]][gh-actions]\n\n[gh-actions]: https://github.com/firebase/functions-samples/actions\n[gh-actions-badge]: https://github.com/firebase/functions-samples/workflows/CI%20Tests/badge.svg\n"
  },
  {
    "path": "community.md",
    "content": "# Community Samples\n\nCheck out these awesome samples built by the Firebase Community!\n\nTo add your own, use the following template:\n\n```md\n### [Emoji replacer](https://link-to-sample-source-code.com)\n\nUse Cloud Functions to replace common words with emojis!\n\n**Author:** @jhuleatt\n```\n"
  },
  {
    "path": "package-lock.json",
    "content": "{\n  \"name\": \"functions-samples\",\n  \"version\": \"1.0.0\",\n  \"lockfileVersion\": 3,\n  \"requires\": true,\n  \"packages\": {\n    \"\": {\n      \"name\": \"functions-samples\",\n      \"version\": \"1.0.0\",\n      \"license\": \"Apache-2.0\",\n      \"devDependencies\": {\n        \"eslint\": \"^8.57.1\",\n        \"pnpm\": \"^9.15.4\",\n        \"typescript\": \"^5.0.4\"\n      },\n      \"engines\": {\n        \"node\": \"22\"\n      }\n    },\n    \"node_modules/@aashutoshrathi/word-wrap\": {\n      \"version\": \"1.2.6\",\n      \"resolved\": \"https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz\",\n      \"integrity\": \"sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==\",\n      \"dev\": true,\n      \"engines\": {\n        \"node\": \">=0.10.0\"\n      }\n    },\n    \"node_modules/@eslint-community/eslint-utils\": {\n      \"version\": \"4.4.0\",\n      \"resolved\": \"https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz\",\n      \"integrity\": \"sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==\",\n      \"dev\": true,\n      \"dependencies\": {\n        \"eslint-visitor-keys\": \"^3.3.0\"\n      },\n      \"engines\": {\n        \"node\": \"^12.22.0 || ^14.17.0 || >=16.0.0\"\n      },\n      \"peerDependencies\": {\n        \"eslint\": \"^6.0.0 || ^7.0.0 || >=8.0.0\"\n      }\n    },\n    \"node_modules/@eslint-community/regexpp\": {\n      \"version\": \"4.10.0\",\n      \"resolved\": \"https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz\",\n      \"integrity\": \"sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==\",\n      \"dev\": true,\n      \"engines\": {\n        \"node\": \"^12.0.0 || ^14.0.0 || >=16.0.0\"\n      }\n    },\n    \"node_modules/@eslint/eslintrc\": {\n      \"version\": \"2.1.4\",\n      \"resolved\": \"https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz\",\n      \"integrity\": \"sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==\",\n      \"dev\": true,\n      \"dependencies\": {\n        \"ajv\": \"^6.12.4\",\n        \"debug\": \"^4.3.2\",\n        \"espree\": \"^9.6.0\",\n        \"globals\": \"^13.19.0\",\n        \"ignore\": \"^5.2.0\",\n        \"import-fresh\": \"^3.2.1\",\n        \"js-yaml\": \"^4.1.0\",\n        \"minimatch\": \"^3.1.2\",\n        \"strip-json-comments\": \"^3.1.1\"\n      },\n      \"engines\": {\n        \"node\": \"^12.22.0 || ^14.17.0 || >=16.0.0\"\n      },\n      \"funding\": {\n        \"url\": \"https://opencollective.com/eslint\"\n      }\n    },\n    \"node_modules/@eslint/js\": {\n      \"version\": \"8.57.1\",\n      \"resolved\": \"https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz\",\n      \"integrity\": \"sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"engines\": {\n        \"node\": \"^12.22.0 || ^14.17.0 || >=16.0.0\"\n      }\n    },\n    \"node_modules/@humanwhocodes/config-array\": {\n      \"version\": \"0.13.0\",\n      \"resolved\": \"https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz\",\n      \"integrity\": \"sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==\",\n      \"deprecated\": \"Use @eslint/config-array instead\",\n      \"dev\": true,\n      \"license\": \"Apache-2.0\",\n      \"dependencies\": {\n        \"@humanwhocodes/object-schema\": \"^2.0.3\",\n        \"debug\": \"^4.3.1\",\n        \"minimatch\": \"^3.0.5\"\n      },\n      \"engines\": {\n        \"node\": \">=10.10.0\"\n      }\n    },\n    \"node_modules/@humanwhocodes/module-importer\": {\n      \"version\": \"1.0.1\",\n      \"resolved\": \"https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz\",\n      \"integrity\": \"sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==\",\n      \"dev\": true,\n      \"engines\": {\n        \"node\": \">=12.22\"\n      },\n      \"funding\": {\n        \"type\": \"github\",\n        \"url\": \"https://github.com/sponsors/nzakas\"\n      }\n    },\n    \"node_modules/@humanwhocodes/object-schema\": {\n      \"version\": \"2.0.3\",\n      \"resolved\": \"https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz\",\n      \"integrity\": \"sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==\",\n      \"deprecated\": \"Use @eslint/object-schema instead\",\n      \"dev\": true,\n      \"license\": \"BSD-3-Clause\"\n    },\n    \"node_modules/@nodelib/fs.scandir\": {\n      \"version\": \"2.1.5\",\n      \"resolved\": \"https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz\",\n      \"integrity\": \"sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==\",\n      \"dev\": true,\n      \"dependencies\": {\n        \"@nodelib/fs.stat\": \"2.0.5\",\n        \"run-parallel\": \"^1.1.9\"\n      },\n      \"engines\": {\n        \"node\": \">= 8\"\n      }\n    },\n    \"node_modules/@nodelib/fs.stat\": {\n      \"version\": \"2.0.5\",\n      \"resolved\": \"https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz\",\n      \"integrity\": \"sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==\",\n      \"dev\": true,\n      \"engines\": {\n        \"node\": \">= 8\"\n      }\n    },\n    \"node_modules/@nodelib/fs.walk\": {\n      \"version\": \"1.2.8\",\n      \"resolved\": \"https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz\",\n      \"integrity\": \"sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==\",\n      \"dev\": true,\n      \"dependencies\": {\n        \"@nodelib/fs.scandir\": \"2.1.5\",\n        \"fastq\": \"^1.6.0\"\n      },\n      \"engines\": {\n        \"node\": \">= 8\"\n      }\n    },\n    \"node_modules/@ungap/structured-clone\": {\n      \"version\": \"1.2.0\",\n      \"resolved\": \"https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz\",\n      \"integrity\": \"sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==\",\n      \"dev\": true\n    },\n    \"node_modules/acorn\": {\n      \"version\": \"8.11.3\",\n      \"resolved\": \"https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz\",\n      \"integrity\": \"sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==\",\n      \"dev\": true,\n      \"bin\": {\n        \"acorn\": \"bin/acorn\"\n      },\n      \"engines\": {\n        \"node\": \">=0.4.0\"\n      }\n    },\n    \"node_modules/acorn-jsx\": {\n      \"version\": \"5.3.2\",\n      \"resolved\": \"https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz\",\n      \"integrity\": \"sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==\",\n      \"dev\": true,\n      \"peerDependencies\": {\n        \"acorn\": \"^6.0.0 || ^7.0.0 || ^8.0.0\"\n      }\n    },\n    \"node_modules/ajv\": {\n      \"version\": \"6.12.6\",\n      \"resolved\": \"https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz\",\n      \"integrity\": \"sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==\",\n      \"dev\": true,\n      \"dependencies\": {\n        \"fast-deep-equal\": \"^3.1.1\",\n        \"fast-json-stable-stringify\": \"^2.0.0\",\n        \"json-schema-traverse\": \"^0.4.1\",\n        \"uri-js\": \"^4.2.2\"\n      },\n      \"funding\": {\n        \"type\": \"github\",\n        \"url\": \"https://github.com/sponsors/epoberezkin\"\n      }\n    },\n    \"node_modules/ansi-regex\": {\n      \"version\": \"5.0.1\",\n      \"resolved\": \"https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz\",\n      \"integrity\": \"sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==\",\n      \"dev\": true,\n      \"engines\": {\n        \"node\": \">=8\"\n      }\n    },\n    \"node_modules/ansi-styles\": {\n      \"version\": \"4.3.0\",\n      \"resolved\": \"https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz\",\n      \"integrity\": \"sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==\",\n      \"dev\": true,\n      \"dependencies\": {\n        \"color-convert\": \"^2.0.1\"\n      },\n      \"engines\": {\n        \"node\": \">=8\"\n      },\n      \"funding\": {\n        \"url\": \"https://github.com/chalk/ansi-styles?sponsor=1\"\n      }\n    },\n    \"node_modules/argparse\": {\n      \"version\": \"2.0.1\",\n      \"resolved\": \"https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz\",\n      \"integrity\": \"sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==\",\n      \"dev\": true\n    },\n    \"node_modules/balanced-match\": {\n      \"version\": \"1.0.2\",\n      \"resolved\": \"https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz\",\n      \"integrity\": \"sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==\",\n      \"dev\": true\n    },\n    \"node_modules/brace-expansion\": {\n      \"version\": \"1.1.11\",\n      \"resolved\": \"https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz\",\n      \"integrity\": \"sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==\",\n      \"dev\": true,\n      \"dependencies\": {\n        \"balanced-match\": \"^1.0.0\",\n        \"concat-map\": \"0.0.1\"\n      }\n    },\n    \"node_modules/callsites\": {\n      \"version\": \"3.1.0\",\n      \"resolved\": \"https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz\",\n      \"integrity\": \"sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==\",\n      \"dev\": true,\n      \"engines\": {\n        \"node\": \">=6\"\n      }\n    },\n    \"node_modules/chalk\": {\n      \"version\": \"4.1.2\",\n      \"resolved\": \"https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz\",\n      \"integrity\": \"sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==\",\n      \"dev\": true,\n      \"dependencies\": {\n        \"ansi-styles\": \"^4.1.0\",\n        \"supports-color\": \"^7.1.0\"\n      },\n      \"engines\": {\n        \"node\": \">=10\"\n      },\n      \"funding\": {\n        \"url\": \"https://github.com/chalk/chalk?sponsor=1\"\n      }\n    },\n    \"node_modules/color-convert\": {\n      \"version\": \"2.0.1\",\n      \"resolved\": \"https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz\",\n      \"integrity\": \"sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==\",\n      \"dev\": true,\n      \"dependencies\": {\n        \"color-name\": \"~1.1.4\"\n      },\n      \"engines\": {\n        \"node\": \">=7.0.0\"\n      }\n    },\n    \"node_modules/color-name\": {\n      \"version\": \"1.1.4\",\n      \"resolved\": \"https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz\",\n      \"integrity\": \"sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==\",\n      \"dev\": true\n    },\n    \"node_modules/concat-map\": {\n      \"version\": \"0.0.1\",\n      \"resolved\": \"https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz\",\n      \"integrity\": \"sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==\",\n      \"dev\": true\n    },\n    \"node_modules/cross-spawn\": {\n      \"version\": \"7.0.3\",\n      \"resolved\": \"https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz\",\n      \"integrity\": \"sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==\",\n      \"dev\": true,\n      \"dependencies\": {\n        \"path-key\": \"^3.1.0\",\n        \"shebang-command\": \"^2.0.0\",\n        \"which\": \"^2.0.1\"\n      },\n      \"engines\": {\n        \"node\": \">= 8\"\n      }\n    },\n    \"node_modules/debug\": {\n      \"version\": \"4.3.4\",\n      \"resolved\": \"https://registry.npmjs.org/debug/-/debug-4.3.4.tgz\",\n      \"integrity\": \"sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==\",\n      \"dev\": true,\n      \"dependencies\": {\n        \"ms\": \"2.1.2\"\n      },\n      \"engines\": {\n        \"node\": \">=6.0\"\n      },\n      \"peerDependenciesMeta\": {\n        \"supports-color\": {\n          \"optional\": true\n        }\n      }\n    },\n    \"node_modules/deep-is\": {\n      \"version\": \"0.1.4\",\n      \"resolved\": \"https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz\",\n      \"integrity\": \"sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==\",\n      \"dev\": true\n    },\n    \"node_modules/doctrine\": {\n      \"version\": \"3.0.0\",\n      \"resolved\": \"https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz\",\n      \"integrity\": \"sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==\",\n      \"dev\": true,\n      \"dependencies\": {\n        \"esutils\": \"^2.0.2\"\n      },\n      \"engines\": {\n        \"node\": \">=6.0.0\"\n      }\n    },\n    \"node_modules/escape-string-regexp\": {\n      \"version\": \"4.0.0\",\n      \"resolved\": \"https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz\",\n      \"integrity\": \"sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==\",\n      \"dev\": true,\n      \"engines\": {\n        \"node\": \">=10\"\n      },\n      \"funding\": {\n        \"url\": \"https://github.com/sponsors/sindresorhus\"\n      }\n    },\n    \"node_modules/eslint\": {\n      \"version\": \"8.57.1\",\n      \"resolved\": \"https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz\",\n      \"integrity\": \"sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==\",\n      \"deprecated\": \"This version is no longer supported. Please see https://eslint.org/version-support for other options.\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"@eslint-community/eslint-utils\": \"^4.2.0\",\n        \"@eslint-community/regexpp\": \"^4.6.1\",\n        \"@eslint/eslintrc\": \"^2.1.4\",\n        \"@eslint/js\": \"8.57.1\",\n        \"@humanwhocodes/config-array\": \"^0.13.0\",\n        \"@humanwhocodes/module-importer\": \"^1.0.1\",\n        \"@nodelib/fs.walk\": \"^1.2.8\",\n        \"@ungap/structured-clone\": \"^1.2.0\",\n        \"ajv\": \"^6.12.4\",\n        \"chalk\": \"^4.0.0\",\n        \"cross-spawn\": \"^7.0.2\",\n        \"debug\": \"^4.3.2\",\n        \"doctrine\": \"^3.0.0\",\n        \"escape-string-regexp\": \"^4.0.0\",\n        \"eslint-scope\": \"^7.2.2\",\n        \"eslint-visitor-keys\": \"^3.4.3\",\n        \"espree\": \"^9.6.1\",\n        \"esquery\": \"^1.4.2\",\n        \"esutils\": \"^2.0.2\",\n        \"fast-deep-equal\": \"^3.1.3\",\n        \"file-entry-cache\": \"^6.0.1\",\n        \"find-up\": \"^5.0.0\",\n        \"glob-parent\": \"^6.0.2\",\n        \"globals\": \"^13.19.0\",\n        \"graphemer\": \"^1.4.0\",\n        \"ignore\": \"^5.2.0\",\n        \"imurmurhash\": \"^0.1.4\",\n        \"is-glob\": \"^4.0.0\",\n        \"is-path-inside\": \"^3.0.3\",\n        \"js-yaml\": \"^4.1.0\",\n        \"json-stable-stringify-without-jsonify\": \"^1.0.1\",\n        \"levn\": \"^0.4.1\",\n        \"lodash.merge\": \"^4.6.2\",\n        \"minimatch\": \"^3.1.2\",\n        \"natural-compare\": \"^1.4.0\",\n        \"optionator\": \"^0.9.3\",\n        \"strip-ansi\": \"^6.0.1\",\n        \"text-table\": \"^0.2.0\"\n      },\n      \"bin\": {\n        \"eslint\": \"bin/eslint.js\"\n      },\n      \"engines\": {\n        \"node\": \"^12.22.0 || ^14.17.0 || >=16.0.0\"\n      },\n      \"funding\": {\n        \"url\": \"https://opencollective.com/eslint\"\n      }\n    },\n    \"node_modules/eslint-scope\": {\n      \"version\": \"7.2.2\",\n      \"resolved\": \"https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz\",\n      \"integrity\": \"sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==\",\n      \"dev\": true,\n      \"dependencies\": {\n        \"esrecurse\": \"^4.3.0\",\n        \"estraverse\": \"^5.2.0\"\n      },\n      \"engines\": {\n        \"node\": \"^12.22.0 || ^14.17.0 || >=16.0.0\"\n      },\n      \"funding\": {\n        \"url\": \"https://opencollective.com/eslint\"\n      }\n    },\n    \"node_modules/eslint-visitor-keys\": {\n      \"version\": \"3.4.3\",\n      \"resolved\": \"https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz\",\n      \"integrity\": \"sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==\",\n      \"dev\": true,\n      \"engines\": {\n        \"node\": \"^12.22.0 || ^14.17.0 || >=16.0.0\"\n      },\n      \"funding\": {\n        \"url\": \"https://opencollective.com/eslint\"\n      }\n    },\n    \"node_modules/espree\": {\n      \"version\": \"9.6.1\",\n      \"resolved\": \"https://registry.npmjs.org/espree/-/espree-9.6.1.tgz\",\n      \"integrity\": \"sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==\",\n      \"dev\": true,\n      \"dependencies\": {\n        \"acorn\": \"^8.9.0\",\n        \"acorn-jsx\": \"^5.3.2\",\n        \"eslint-visitor-keys\": \"^3.4.1\"\n      },\n      \"engines\": {\n        \"node\": \"^12.22.0 || ^14.17.0 || >=16.0.0\"\n      },\n      \"funding\": {\n        \"url\": \"https://opencollective.com/eslint\"\n      }\n    },\n    \"node_modules/esquery\": {\n      \"version\": \"1.5.0\",\n      \"resolved\": \"https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz\",\n      \"integrity\": \"sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==\",\n      \"dev\": true,\n      \"dependencies\": {\n        \"estraverse\": \"^5.1.0\"\n      },\n      \"engines\": {\n        \"node\": \">=0.10\"\n      }\n    },\n    \"node_modules/esrecurse\": {\n      \"version\": \"4.3.0\",\n      \"resolved\": \"https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz\",\n      \"integrity\": \"sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==\",\n      \"dev\": true,\n      \"dependencies\": {\n        \"estraverse\": \"^5.2.0\"\n      },\n      \"engines\": {\n        \"node\": \">=4.0\"\n      }\n    },\n    \"node_modules/estraverse\": {\n      \"version\": \"5.3.0\",\n      \"resolved\": \"https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz\",\n      \"integrity\": \"sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==\",\n      \"dev\": true,\n      \"engines\": {\n        \"node\": \">=4.0\"\n      }\n    },\n    \"node_modules/esutils\": {\n      \"version\": \"2.0.3\",\n      \"resolved\": \"https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz\",\n      \"integrity\": \"sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==\",\n      \"dev\": true,\n      \"engines\": {\n        \"node\": \">=0.10.0\"\n      }\n    },\n    \"node_modules/fast-deep-equal\": {\n      \"version\": \"3.1.3\",\n      \"resolved\": \"https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz\",\n      \"integrity\": \"sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==\",\n      \"dev\": true\n    },\n    \"node_modules/fast-json-stable-stringify\": {\n      \"version\": \"2.1.0\",\n      \"resolved\": \"https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz\",\n      \"integrity\": \"sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==\",\n      \"dev\": true\n    },\n    \"node_modules/fast-levenshtein\": {\n      \"version\": \"2.0.6\",\n      \"resolved\": \"https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz\",\n      \"integrity\": \"sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==\",\n      \"dev\": true\n    },\n    \"node_modules/fastq\": {\n      \"version\": \"1.15.0\",\n      \"resolved\": \"https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz\",\n      \"integrity\": \"sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==\",\n      \"dev\": true,\n      \"dependencies\": {\n        \"reusify\": \"^1.0.4\"\n      }\n    },\n    \"node_modules/file-entry-cache\": {\n      \"version\": \"6.0.1\",\n      \"resolved\": \"https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz\",\n      \"integrity\": \"sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==\",\n      \"dev\": true,\n      \"dependencies\": {\n        \"flat-cache\": \"^3.0.4\"\n      },\n      \"engines\": {\n        \"node\": \"^10.12.0 || >=12.0.0\"\n      }\n    },\n    \"node_modules/find-up\": {\n      \"version\": \"5.0.0\",\n      \"resolved\": \"https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz\",\n      \"integrity\": \"sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==\",\n      \"dev\": true,\n      \"dependencies\": {\n        \"locate-path\": \"^6.0.0\",\n        \"path-exists\": \"^4.0.0\"\n      },\n      \"engines\": {\n        \"node\": \">=10\"\n      },\n      \"funding\": {\n        \"url\": \"https://github.com/sponsors/sindresorhus\"\n      }\n    },\n    \"node_modules/flat-cache\": {\n      \"version\": \"3.0.4\",\n      \"resolved\": \"https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz\",\n      \"integrity\": \"sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==\",\n      \"dev\": true,\n      \"dependencies\": {\n        \"flatted\": \"^3.1.0\",\n        \"rimraf\": \"^3.0.2\"\n      },\n      \"engines\": {\n        \"node\": \"^10.12.0 || >=12.0.0\"\n      }\n    },\n    \"node_modules/flatted\": {\n      \"version\": \"3.2.7\",\n      \"resolved\": \"https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz\",\n      \"integrity\": \"sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==\",\n      \"dev\": true\n    },\n    \"node_modules/fs.realpath\": {\n      \"version\": \"1.0.0\",\n      \"resolved\": \"https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz\",\n      \"integrity\": \"sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==\",\n      \"dev\": true\n    },\n    \"node_modules/glob\": {\n      \"version\": \"7.2.3\",\n      \"resolved\": \"https://registry.npmjs.org/glob/-/glob-7.2.3.tgz\",\n      \"integrity\": \"sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==\",\n      \"dev\": true,\n      \"dependencies\": {\n        \"fs.realpath\": \"^1.0.0\",\n        \"inflight\": \"^1.0.4\",\n        \"inherits\": \"2\",\n        \"minimatch\": \"^3.1.1\",\n        \"once\": \"^1.3.0\",\n        \"path-is-absolute\": \"^1.0.0\"\n      },\n      \"engines\": {\n        \"node\": \"*\"\n      },\n      \"funding\": {\n        \"url\": \"https://github.com/sponsors/isaacs\"\n      }\n    },\n    \"node_modules/glob-parent\": {\n      \"version\": \"6.0.2\",\n      \"resolved\": \"https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz\",\n      \"integrity\": \"sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==\",\n      \"dev\": true,\n      \"dependencies\": {\n        \"is-glob\": \"^4.0.3\"\n      },\n      \"engines\": {\n        \"node\": \">=10.13.0\"\n      }\n    },\n    \"node_modules/globals\": {\n      \"version\": \"13.24.0\",\n      \"resolved\": \"https://registry.npmjs.org/globals/-/globals-13.24.0.tgz\",\n      \"integrity\": \"sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==\",\n      \"dev\": true,\n      \"dependencies\": {\n        \"type-fest\": \"^0.20.2\"\n      },\n      \"engines\": {\n        \"node\": \">=8\"\n      },\n      \"funding\": {\n        \"url\": \"https://github.com/sponsors/sindresorhus\"\n      }\n    },\n    \"node_modules/graphemer\": {\n      \"version\": \"1.4.0\",\n      \"resolved\": \"https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz\",\n      \"integrity\": \"sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==\",\n      \"dev\": true\n    },\n    \"node_modules/has-flag\": {\n      \"version\": \"4.0.0\",\n      \"resolved\": \"https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz\",\n      \"integrity\": \"sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==\",\n      \"dev\": true,\n      \"engines\": {\n        \"node\": \">=8\"\n      }\n    },\n    \"node_modules/ignore\": {\n      \"version\": \"5.3.1\",\n      \"resolved\": \"https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz\",\n      \"integrity\": \"sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==\",\n      \"dev\": true,\n      \"engines\": {\n        \"node\": \">= 4\"\n      }\n    },\n    \"node_modules/import-fresh\": {\n      \"version\": \"3.3.0\",\n      \"resolved\": \"https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz\",\n      \"integrity\": \"sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==\",\n      \"dev\": true,\n      \"dependencies\": {\n        \"parent-module\": \"^1.0.0\",\n        \"resolve-from\": \"^4.0.0\"\n      },\n      \"engines\": {\n        \"node\": \">=6\"\n      },\n      \"funding\": {\n        \"url\": \"https://github.com/sponsors/sindresorhus\"\n      }\n    },\n    \"node_modules/imurmurhash\": {\n      \"version\": \"0.1.4\",\n      \"resolved\": \"https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz\",\n      \"integrity\": \"sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==\",\n      \"dev\": true,\n      \"engines\": {\n        \"node\": \">=0.8.19\"\n      }\n    },\n    \"node_modules/inflight\": {\n      \"version\": \"1.0.6\",\n      \"resolved\": \"https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz\",\n      \"integrity\": \"sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==\",\n      \"dev\": true,\n      \"dependencies\": {\n        \"once\": \"^1.3.0\",\n        \"wrappy\": \"1\"\n      }\n    },\n    \"node_modules/inherits\": {\n      \"version\": \"2.0.4\",\n      \"resolved\": \"https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz\",\n      \"integrity\": \"sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==\",\n      \"dev\": true\n    },\n    \"node_modules/is-extglob\": {\n      \"version\": \"2.1.1\",\n      \"resolved\": \"https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz\",\n      \"integrity\": \"sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==\",\n      \"dev\": true,\n      \"engines\": {\n        \"node\": \">=0.10.0\"\n      }\n    },\n    \"node_modules/is-glob\": {\n      \"version\": \"4.0.3\",\n      \"resolved\": \"https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz\",\n      \"integrity\": \"sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==\",\n      \"dev\": true,\n      \"dependencies\": {\n        \"is-extglob\": \"^2.1.1\"\n      },\n      \"engines\": {\n        \"node\": \">=0.10.0\"\n      }\n    },\n    \"node_modules/is-path-inside\": {\n      \"version\": \"3.0.3\",\n      \"resolved\": \"https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz\",\n      \"integrity\": \"sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==\",\n      \"dev\": true,\n      \"engines\": {\n        \"node\": \">=8\"\n      }\n    },\n    \"node_modules/isexe\": {\n      \"version\": \"2.0.0\",\n      \"resolved\": \"https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz\",\n      \"integrity\": \"sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==\",\n      \"dev\": true\n    },\n    \"node_modules/js-yaml\": {\n      \"version\": \"4.1.0\",\n      \"resolved\": \"https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz\",\n      \"integrity\": \"sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==\",\n      \"dev\": true,\n      \"dependencies\": {\n        \"argparse\": \"^2.0.1\"\n      },\n      \"bin\": {\n        \"js-yaml\": \"bin/js-yaml.js\"\n      }\n    },\n    \"node_modules/json-schema-traverse\": {\n      \"version\": \"0.4.1\",\n      \"resolved\": \"https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz\",\n      \"integrity\": \"sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==\",\n      \"dev\": true\n    },\n    \"node_modules/json-stable-stringify-without-jsonify\": {\n      \"version\": \"1.0.1\",\n      \"resolved\": \"https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz\",\n      \"integrity\": \"sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==\",\n      \"dev\": true\n    },\n    \"node_modules/levn\": {\n      \"version\": \"0.4.1\",\n      \"resolved\": \"https://registry.npmjs.org/levn/-/levn-0.4.1.tgz\",\n      \"integrity\": \"sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==\",\n      \"dev\": true,\n      \"dependencies\": {\n        \"prelude-ls\": \"^1.2.1\",\n        \"type-check\": \"~0.4.0\"\n      },\n      \"engines\": {\n        \"node\": \">= 0.8.0\"\n      }\n    },\n    \"node_modules/locate-path\": {\n      \"version\": \"6.0.0\",\n      \"resolved\": \"https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz\",\n      \"integrity\": \"sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==\",\n      \"dev\": true,\n      \"dependencies\": {\n        \"p-locate\": \"^5.0.0\"\n      },\n      \"engines\": {\n        \"node\": \">=10\"\n      },\n      \"funding\": {\n        \"url\": \"https://github.com/sponsors/sindresorhus\"\n      }\n    },\n    \"node_modules/lodash.merge\": {\n      \"version\": \"4.6.2\",\n      \"resolved\": \"https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz\",\n      \"integrity\": \"sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==\",\n      \"dev\": true\n    },\n    \"node_modules/minimatch\": {\n      \"version\": \"3.1.2\",\n      \"resolved\": \"https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz\",\n      \"integrity\": \"sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==\",\n      \"dev\": true,\n      \"dependencies\": {\n        \"brace-expansion\": \"^1.1.7\"\n      },\n      \"engines\": {\n        \"node\": \"*\"\n      }\n    },\n    \"node_modules/ms\": {\n      \"version\": \"2.1.2\",\n      \"resolved\": \"https://registry.npmjs.org/ms/-/ms-2.1.2.tgz\",\n      \"integrity\": \"sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==\",\n      \"dev\": true\n    },\n    \"node_modules/natural-compare\": {\n      \"version\": \"1.4.0\",\n      \"resolved\": \"https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz\",\n      \"integrity\": \"sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==\",\n      \"dev\": true\n    },\n    \"node_modules/once\": {\n      \"version\": \"1.4.0\",\n      \"resolved\": \"https://registry.npmjs.org/once/-/once-1.4.0.tgz\",\n      \"integrity\": \"sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==\",\n      \"dev\": true,\n      \"dependencies\": {\n        \"wrappy\": \"1\"\n      }\n    },\n    \"node_modules/optionator\": {\n      \"version\": \"0.9.3\",\n      \"resolved\": \"https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz\",\n      \"integrity\": \"sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==\",\n      \"dev\": true,\n      \"dependencies\": {\n        \"@aashutoshrathi/word-wrap\": \"^1.2.3\",\n        \"deep-is\": \"^0.1.3\",\n        \"fast-levenshtein\": \"^2.0.6\",\n        \"levn\": \"^0.4.1\",\n        \"prelude-ls\": \"^1.2.1\",\n        \"type-check\": \"^0.4.0\"\n      },\n      \"engines\": {\n        \"node\": \">= 0.8.0\"\n      }\n    },\n    \"node_modules/p-limit\": {\n      \"version\": \"3.1.0\",\n      \"resolved\": \"https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz\",\n      \"integrity\": \"sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==\",\n      \"dev\": true,\n      \"dependencies\": {\n        \"yocto-queue\": \"^0.1.0\"\n      },\n      \"engines\": {\n        \"node\": \">=10\"\n      },\n      \"funding\": {\n        \"url\": \"https://github.com/sponsors/sindresorhus\"\n      }\n    },\n    \"node_modules/p-locate\": {\n      \"version\": \"5.0.0\",\n      \"resolved\": \"https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz\",\n      \"integrity\": \"sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==\",\n      \"dev\": true,\n      \"dependencies\": {\n        \"p-limit\": \"^3.0.2\"\n      },\n      \"engines\": {\n        \"node\": \">=10\"\n      },\n      \"funding\": {\n        \"url\": \"https://github.com/sponsors/sindresorhus\"\n      }\n    },\n    \"node_modules/parent-module\": {\n      \"version\": \"1.0.1\",\n      \"resolved\": \"https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz\",\n      \"integrity\": \"sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==\",\n      \"dev\": true,\n      \"dependencies\": {\n        \"callsites\": \"^3.0.0\"\n      },\n      \"engines\": {\n        \"node\": \">=6\"\n      }\n    },\n    \"node_modules/path-exists\": {\n      \"version\": \"4.0.0\",\n      \"resolved\": \"https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz\",\n      \"integrity\": \"sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==\",\n      \"dev\": true,\n      \"engines\": {\n        \"node\": \">=8\"\n      }\n    },\n    \"node_modules/path-is-absolute\": {\n      \"version\": \"1.0.1\",\n      \"resolved\": \"https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz\",\n      \"integrity\": \"sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==\",\n      \"dev\": true,\n      \"engines\": {\n        \"node\": \">=0.10.0\"\n      }\n    },\n    \"node_modules/path-key\": {\n      \"version\": \"3.1.1\",\n      \"resolved\": \"https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz\",\n      \"integrity\": \"sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==\",\n      \"dev\": true,\n      \"engines\": {\n        \"node\": \">=8\"\n      }\n    },\n    \"node_modules/pnpm\": {\n      \"version\": \"9.15.4\",\n      \"resolved\": \"https://registry.npmjs.org/pnpm/-/pnpm-9.15.4.tgz\",\n      \"integrity\": \"sha512-stwg4vxys+GISEWbNzWaMgZGY+VielHkx0ssKd2OjgSRSDw6u0B4nP1Xi/Ni+2uoJhsF8Dh9dnku1uI+o7G2oA==\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"bin\": {\n        \"pnpm\": \"bin/pnpm.cjs\",\n        \"pnpx\": \"bin/pnpx.cjs\"\n      },\n      \"engines\": {\n        \"node\": \">=18.12\"\n      },\n      \"funding\": {\n        \"url\": \"https://opencollective.com/pnpm\"\n      }\n    },\n    \"node_modules/prelude-ls\": {\n      \"version\": \"1.2.1\",\n      \"resolved\": \"https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz\",\n      \"integrity\": \"sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==\",\n      \"dev\": true,\n      \"engines\": {\n        \"node\": \">= 0.8.0\"\n      }\n    },\n    \"node_modules/punycode\": {\n      \"version\": \"2.3.1\",\n      \"resolved\": \"https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz\",\n      \"integrity\": \"sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==\",\n      \"dev\": true,\n      \"engines\": {\n        \"node\": \">=6\"\n      }\n    },\n    \"node_modules/queue-microtask\": {\n      \"version\": \"1.2.3\",\n      \"resolved\": \"https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz\",\n      \"integrity\": \"sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==\",\n      \"dev\": true,\n      \"funding\": [\n        {\n          \"type\": \"github\",\n          \"url\": \"https://github.com/sponsors/feross\"\n        },\n        {\n          \"type\": \"patreon\",\n          \"url\": \"https://www.patreon.com/feross\"\n        },\n        {\n          \"type\": \"consulting\",\n          \"url\": \"https://feross.org/support\"\n        }\n      ]\n    },\n    \"node_modules/resolve-from\": {\n      \"version\": \"4.0.0\",\n      \"resolved\": \"https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz\",\n      \"integrity\": \"sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==\",\n      \"dev\": true,\n      \"engines\": {\n        \"node\": \">=4\"\n      }\n    },\n    \"node_modules/reusify\": {\n      \"version\": \"1.0.4\",\n      \"resolved\": \"https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz\",\n      \"integrity\": \"sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==\",\n      \"dev\": true,\n      \"engines\": {\n        \"iojs\": \">=1.0.0\",\n        \"node\": \">=0.10.0\"\n      }\n    },\n    \"node_modules/rimraf\": {\n      \"version\": \"3.0.2\",\n      \"resolved\": \"https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz\",\n      \"integrity\": \"sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==\",\n      \"dev\": true,\n      \"dependencies\": {\n        \"glob\": \"^7.1.3\"\n      },\n      \"bin\": {\n        \"rimraf\": \"bin.js\"\n      },\n      \"funding\": {\n        \"url\": \"https://github.com/sponsors/isaacs\"\n      }\n    },\n    \"node_modules/run-parallel\": {\n      \"version\": \"1.2.0\",\n      \"resolved\": \"https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz\",\n      \"integrity\": \"sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==\",\n      \"dev\": true,\n      \"funding\": [\n        {\n          \"type\": \"github\",\n          \"url\": \"https://github.com/sponsors/feross\"\n        },\n        {\n          \"type\": \"patreon\",\n          \"url\": \"https://www.patreon.com/feross\"\n        },\n        {\n          \"type\": \"consulting\",\n          \"url\": \"https://feross.org/support\"\n        }\n      ],\n      \"dependencies\": {\n        \"queue-microtask\": \"^1.2.2\"\n      }\n    },\n    \"node_modules/shebang-command\": {\n      \"version\": \"2.0.0\",\n      \"resolved\": \"https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz\",\n      \"integrity\": \"sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==\",\n      \"dev\": true,\n      \"dependencies\": {\n        \"shebang-regex\": \"^3.0.0\"\n      },\n      \"engines\": {\n        \"node\": \">=8\"\n      }\n    },\n    \"node_modules/shebang-regex\": {\n      \"version\": \"3.0.0\",\n      \"resolved\": \"https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz\",\n      \"integrity\": \"sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==\",\n      \"dev\": true,\n      \"engines\": {\n        \"node\": \">=8\"\n      }\n    },\n    \"node_modules/strip-ansi\": {\n      \"version\": \"6.0.1\",\n      \"resolved\": \"https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz\",\n      \"integrity\": \"sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==\",\n      \"dev\": true,\n      \"dependencies\": {\n        \"ansi-regex\": \"^5.0.1\"\n      },\n      \"engines\": {\n        \"node\": \">=8\"\n      }\n    },\n    \"node_modules/strip-json-comments\": {\n      \"version\": \"3.1.1\",\n      \"resolved\": \"https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz\",\n      \"integrity\": \"sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==\",\n      \"dev\": true,\n      \"engines\": {\n        \"node\": \">=8\"\n      },\n      \"funding\": {\n        \"url\": \"https://github.com/sponsors/sindresorhus\"\n      }\n    },\n    \"node_modules/supports-color\": {\n      \"version\": \"7.2.0\",\n      \"resolved\": \"https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz\",\n      \"integrity\": \"sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==\",\n      \"dev\": true,\n      \"dependencies\": {\n        \"has-flag\": \"^4.0.0\"\n      },\n      \"engines\": {\n        \"node\": \">=8\"\n      }\n    },\n    \"node_modules/text-table\": {\n      \"version\": \"0.2.0\",\n      \"resolved\": \"https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz\",\n      \"integrity\": \"sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==\",\n      \"dev\": true\n    },\n    \"node_modules/type-check\": {\n      \"version\": \"0.4.0\",\n      \"resolved\": \"https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz\",\n      \"integrity\": \"sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==\",\n      \"dev\": true,\n      \"dependencies\": {\n        \"prelude-ls\": \"^1.2.1\"\n      },\n      \"engines\": {\n        \"node\": \">= 0.8.0\"\n      }\n    },\n    \"node_modules/type-fest\": {\n      \"version\": \"0.20.2\",\n      \"resolved\": \"https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz\",\n      \"integrity\": \"sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==\",\n      \"dev\": true,\n      \"engines\": {\n        \"node\": \">=10\"\n      },\n      \"funding\": {\n        \"url\": \"https://github.com/sponsors/sindresorhus\"\n      }\n    },\n    \"node_modules/typescript\": {\n      \"version\": \"5.0.4\",\n      \"resolved\": \"https://registry.npmjs.org/typescript/-/typescript-5.0.4.tgz\",\n      \"integrity\": \"sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==\",\n      \"dev\": true,\n      \"bin\": {\n        \"tsc\": \"bin/tsc\",\n        \"tsserver\": \"bin/tsserver\"\n      },\n      \"engines\": {\n        \"node\": \">=12.20\"\n      }\n    },\n    \"node_modules/uri-js\": {\n      \"version\": \"4.4.1\",\n      \"resolved\": \"https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz\",\n      \"integrity\": \"sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==\",\n      \"dev\": true,\n      \"dependencies\": {\n        \"punycode\": \"^2.1.0\"\n      }\n    },\n    \"node_modules/which\": {\n      \"version\": \"2.0.2\",\n      \"resolved\": \"https://registry.npmjs.org/which/-/which-2.0.2.tgz\",\n      \"integrity\": \"sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==\",\n      \"dev\": true,\n      \"dependencies\": {\n        \"isexe\": \"^2.0.0\"\n      },\n      \"bin\": {\n        \"node-which\": \"bin/node-which\"\n      },\n      \"engines\": {\n        \"node\": \">= 8\"\n      }\n    },\n    \"node_modules/wrappy\": {\n      \"version\": \"1.0.2\",\n      \"resolved\": \"https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz\",\n      \"integrity\": \"sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==\",\n      \"dev\": true\n    },\n    \"node_modules/yocto-queue\": {\n      \"version\": \"0.1.0\",\n      \"resolved\": \"https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz\",\n      \"integrity\": \"sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==\",\n      \"dev\": true,\n      \"engines\": {\n        \"node\": \">=10\"\n      },\n      \"funding\": {\n        \"url\": \"https://github.com/sponsors/sindresorhus\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"functions-samples\",\n  \"version\": \"1.0.0\",\n  \"description\": \"Samples for Cloud Functions for Firebase\",\n  \"author\": \"Firebase (https://firebase.google.com/)\",\n  \"license\": \"Apache-2.0\",\n  \"scripts\": {\n    \"bootstrap\": \"pnpm --recursive install\",\n    \"bootstrap-1st-gen\": \"pnpm --recursive --filter  \\\"./Node-1st-gen/**\\\" install\",\n    \"bootstrap-2nd-gen\": \"pnpm --recursive --filter  \\\"./Node/**\\\" install\",\n    \"lint-1st-gen\": \"pnpm --recursive --filter  \\\"./Node-1st-gen/**\\\" run lint\",\n    \"lint-2nd-gen\": \"pnpm --recursive --filter  \\\"./Node/**\\\" run lint\",\n    \"test-1st-gen\": \"pnpm --recursive --filter  \\\"./Node-1st-gen/**\\\" run test \",\n    \"test-2nd-gen\": \"pnpm --recursive --filter  \\\"./Node/**\\\" run test \",\n    \"compile-1st-gen\": \"pnpm --recursive --filter  \\\"./Node-1st-gen/**\\\" run compile \",\n    \"compile-2nd-gen\": \"pnpm --recursive --filter  \\\"./Node/**\\\" run compile \"\n  },\n  \"devDependencies\": {\n    \"eslint\": \"^8.57.1\",\n    \"pnpm\": \"^9.15.4\",\n    \"typescript\": \"^5.0.4\"\n  },\n  \"engines\": {\n    \"node\": \"22\"\n  },\n  \"pnpm\": {\n    \"peerDependencyRules\": {\n      \"ignoreMissing\": [\n        \"@firebase/app-compat\",\n        \"@firebase/app-types\"\n      ]\n    }\n  }\n}\n"
  },
  {
    "path": "tsconfig.template.json",
    "content": "{\n  \"compilerOptions\": {\n    /* Basic Options */\n    \"target\": \"es2022\",                          /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */\n    \"module\": \"commonjs\",                     /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */\n    \"lib\": [\"es2022\", \"dom\", \"webworker\"],              /* Specify library files to be included in the compilation. */\n    \"allowJs\": true,                          /* Allow javascript files to be compiled. */\n    \"checkJs\": true,                          /* Report errors in .js files. */\n    \"outDir\": \"./dist\",                       /* Redirect output structure to the directory. */\n    \"noEmit\": true,                           /* Do not emit outputs. */\n    \"resolveJsonModule\": true,\n    \"typeRoots\": [\n      \"./node_modules/@types\"\n    ],\n    /* Strict Type-Checking Options */\n    \"strict\": true,                           /* Enable all strict type-checking options. */\n    \"noImplicitAny\": false,                   /* Raise error on expressions and declarations with an implied 'any' type. */\n    \"strictNullChecks\": false,                /* Enable strict null checks. */\n    // \"strictFunctionTypes\": true,           /* Enable strict checking of function types. */\n    // \"strictBindCallApply\": true,           /* Enable strict 'bind', 'call', and 'apply' methods on functions. */\n    // \"strictPropertyInitialization\": true,  /* Enable strict checking of property initialization in classes. */\n    // \"noImplicitThis\": true,                /* Raise error on 'this' expressions with an implied 'any' type. */\n    // \"alwaysStrict\": true,                  /* Parse in strict mode and emit \"use strict\" for each source file. */\n\n    // See: https://github.com/microsoft/TypeScript/issues/20595\n    \"skipLibCheck\": true,\n\n    /* Additional Checks */\n    \"noUnusedLocals\": false,                /* Report errors on unused locals. */\n    // \"noUnusedParameters\": true,            /* Report errors on unused parameters. */\n    // \"noImplicitReturns\": true,             /* Report error when not all code paths in function return a value. */\n    // \"noFallthroughCasesInSwitch\": true,    /* Report errors for fallthrough cases in switch statement. */\n\n    \"esModuleInterop\": true,                  /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */\n    \"forceConsistentCasingInFileNames\": true  /* Disallow inconsistently-cased references to the same file. */\n  }\n}\n"
  }
]