{"id":15392670,"url":"https://github.com/jackdbd/performance-audit","last_synced_at":"2025-07-12T19:39:06.315Z","repository":{"id":175587949,"uuid":"654175762","full_name":"jackdbd/performance-audit","owner":"jackdbd","description":null,"archived":false,"fork":false,"pushed_at":"2023-10-19T09:52:07.000Z","size":481,"stargazers_count":0,"open_issues_count":2,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-06-10T14:49:58.150Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jackdbd.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-06-15T14:41:24.000Z","updated_at":"2023-08-16T12:18:29.000Z","dependencies_parsed_at":null,"dependency_job_id":"e12831b5-b18a-4f41-8b85-ad34ebae6698","html_url":"https://github.com/jackdbd/performance-audit","commit_stats":{"total_commits":27,"total_committers":1,"mean_commits":27.0,"dds":0.0,"last_synced_commit":"a5bed879e22b17e6cff8eba403dcb048a38da6b5"},"previous_names":["jackdbd/performance-audit"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/jackdbd/performance-audit","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jackdbd%2Fperformance-audit","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jackdbd%2Fperformance-audit/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jackdbd%2Fperformance-audit/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jackdbd%2Fperformance-audit/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jackdbd","download_url":"https://codeload.github.com/jackdbd/performance-audit/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jackdbd%2Fperformance-audit/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":265044477,"owners_count":23702942,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-01T15:15:38.316Z","updated_at":"2025-07-12T19:39:06.290Z","avatar_url":"https://github.com/jackdbd.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Performance Audit\n\n[![clasp](https://img.shields.io/badge/built%20with-clasp-4285f4.svg)](https://github.com/google/clasp)\n[![CI workflow](https://github.com/jackdbd/performance-audit/actions/workflows/ci.yaml/badge.svg)](https://github.com/jackdbd/performance-audit/actions/workflows/ci.yaml)\n\nRetrieve field performance data from the [Chrome UX Report](https://developer.chrome.com/docs/crux/) and launch [WebPageTest](https://docs.webpagetest.org/api/reference/) without leaving Google Sheets.\n\n## What is this?\n\nThis project is a Google Apps Script [container-bound script](https://developers.google.com/apps-script/guides/bound) that I use for my [website audit consultation service](https://www.giacomodebidda.com/services/website-audit/). I use it to retrieve **field performance data** from the [CrUX BigQuery dataset](https://developer.chrome.com/docs/crux/bigquery/) and the [CrUX History API](https://developer.chrome.com/docs/crux/history-api/), and to invoke the [WebPageTest API](https://docs.webpagetest.org/api/reference/), which will provide me with some **lab data** for the analysis.\n\nYou can use it too! Simply copy [this spreadsheet](https://docs.google.com/spreadsheets/d/12Z3HBsRuuJp8yXTa9uaK2CzY6so_uIOrRGa8kaq8ZPk/) and read the instructions below to configure it and learn how to use it.\n\n### What are field data and lab data?\n\nRead [this article](https://web.dev/lab-and-field-data-differences/) to understand the difference between field data and lab data in the context of web performance.\n\n### What is a container-bound script?\n\nA container-bound script extends the functionality of the Google Sheets it is bound to. A bound script is effectively an unpublished [Apps Script Editor add-on](https://developers.google.com/apps-script/add-ons/concepts/types#editor-add-ons) that functions only for the Google Sheets file it is bound to.\n\n## How do I use this tool?\n\nIf you want to use this tool, you need to create a copy of [this spreadsheet](https://docs.google.com/spreadsheets/d/12Z3HBsRuuJp8yXTa9uaK2CzY6so_uIOrRGa8kaq8ZPk/) by clicking on `File` \u003e `Make a copy`. By doing so, Google Sheets will copy the entire Apps Script project into your spreadsheet. There is no need to clone this repository if you want to use the tool as it is.\n\nYou also need to copy your CrUX API key and your WebPageTest API key, and paste them in the [Script Properties](https://developers.google.com/apps-script/guides/properties). Only those users that have access to your Google Sheets will be able to view these properties. Properties are never shared between scripts.\n\n![Key-Value pairs in Script Properties](./assets/images/script-properties.png)\n\n\u003e :information_source: See [here](https://developer.chrome.com/docs/crux/api/#crux-api-key) to learn how to create an API key that you can use to call both the [CrUX API](https://developer.chrome.com/docs/crux/api/) and the [CrUX History API](https://developer.chrome.com/docs/crux/history-api/).\n\nWhen you create an Apps Script project, Apps Script creates a default Cloud project that operates in the background. However, this application needs to execute functions in your script project using the Apps Script API's `scripts.run` method, so it requires a standard Google Cloud Platform project. For details, see [here](https://developers.google.com/apps-script/guides/cloud-platform-projects#standard) and [here](https://stackoverflow.com/questions/66607729/how-to-programmatically-deploy-a-google-apps-script-as-a-standard-gcp-project).\n\n**TODO**: how to retrieve the GCP Project ID dynamically?\n\n![GCP project number](./assets/images/gcp-project.png)\n\nThe first time you run this project, you will need to authorize it.\n\n![script authorization](./assets/images/script-authorization.png)\n\nDown below you can watch a few videos that show how to use this tool.\n\nhttps://github.com/jackdbd/performance-audit/assets/5048090/05f3d619-5f53-4df2-b01e-b0edcfbb8112\n\nhttps://github.com/jackdbd/performance-audit/assets/5048090/67e9f06e-96a2-4217-81a8-7e48920516cf\n\n## How do I modify this project?\n\nIf you want to modify the Apps Script project tied to the Google Sheets preadsheet, you essentially have two options:\n\n- With [clasp](https://github.com/google/clasp): clone this repository, replace `scriptId` and `projectId` in the `.clasp.json` file with your own values, develop the project as any other software project (commit changes in source control, push to remote repo, deploy, make a PR, etc). This is my recommended approach.\n- Without clasp: make a copy of the spreadsheet, edit the code directly in Google Sheets, in `Extensions` \u003e `Apps Script`. I would not recommend this approach: you would have no version control, no automatic tests to run on the CI, and the frontend code would be impossible to read since it is a single JS bundle.\n\n## Tech stack and environments\n\nThis project consists of some backend code and several frontend components.\n\nIn the **production** environment, the backend code runs on the Apps Script servers, which host a [V8-based runtime](https://developers.google.com/apps-script/guides/v8-runtime) somewhat similar to Node.js. Each frontend component runs in the browser and it is sandboxed in an `\u003ciframe\u003e` by the **host application** (i.e. Google Sheets) for [security reasons](https://developers.google.com/apps-script/guides/html/restrictions).\n\nIn the **development** environment, the backend code runs on Node.js. Each frontend component runs in the browser as a standalone web app, each component on a different port. This makes developing each frontend component easy using one [vite](https://vitejs.dev/guide/) development server for each component.\n\nIn the **test** environment, the backend code runs on Node.js. Each frontend component runs in [Happy DOM](https://github.com/capricorn86/happy-dom). All tests are written with [Vitest](https://vitest.dev/).\n\n## Installation\n\nInstall all the necessary dependencies to build, test, deploy this project.\n\n```sh\nnpm install\n```\n\nI suggest you use [direnv](https://github.com/direnv/direnv) and a `.envrc` file to configure the Node.js version you want to use for this project, as well as all the required environment variables.\n\n## Test\n\nRun tests in watch mode with [vitest](https://vitest.dev/).\n\n```sh\nnpm run test\n```\n\nRun all tests once and generate all coverage reports.\n\n```sh\nnpm run test:coverage\n```\n\n## Development\n\nWatch the backend code and run a vite dev server for each frontend component.\n\n```sh\nnpm run dev\n```\n\nEach frontend component will be served as a standalone web app on a different port (e.g. 5173, 5174).\n\n## Deploy\n\nPush changes to the Google Apps Script server. The code pushed will be the [head deployment](https://developers.google.com/apps-script/concepts/deployments#head_deployments) of this [container-bound Apps Script](https://developers.google.com/apps-script/guides/bound) project.\n\n```sh\nnpm run deploy\n```\n\n\u003e :information_source: I prefer to deploy this script manually, because deploying it automatically from the CI workflow would require to store the `.clasprc.json` credentials in a GitHub secret.\n\nYou can double-check which files will be pushed to Apps Script using this command.\n\n```sh\nnpx clasp status\n```\n\nApps Script can't execute TypeScript files, but you don't have to worry about it: clasp will take care of transpiling TypeScript code into Google Apps Script code [when you push it](https://developers.google.com/apps-script/guides/typescript).\n\nIf you want to inspect the Apps Script project bound to your Google Sheets file, you can either go to `Extensions` \u003e `Apps Script` in Google Sheets, or visit `https://script.google.com/`, or even use this command.\n\n```sh\nnpx clasp open\n```\n\n## Scripts\n\nSee [scripts](./scripts/README.md).\n\n## Reference\n\n- Apps Script Manifest (i.e. `appsscript.json`): see [here](https://developers.google.com/apps-script/concepts/manifests) and [here](https://developers.google.com/apps-script/manifest).\n- Project Settings File (i.e. `.clasp.json`): see [here](https://github.com/google/clasp#project-settings-file-claspjson).\n- OAuth scopes: see [here](https://developers.google.com/apps-script/add-ons/concepts/workspace-scopes) and [here](https://developers.google.com/apps-script/add-ons/concepts/editor-scopes).\n- Invoke an Apps Script function remotely: see [here](https://github.com/google/clasp/blob/master/docs/run.md).\n\nCrUX datasets by Google are licensed under a [Creative Commons Attribution 4.0 International License](https://creativecommons.org/licenses/by/4.0/).\n\n## Credits\n\nThis project was inspired by Andy Davies' [WebPageTest Google Sheets Bulk Tester](https://github.com/WebPageTest/WebPageTest-Bulk-Tester).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjackdbd%2Fperformance-audit","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjackdbd%2Fperformance-audit","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjackdbd%2Fperformance-audit/lists"}