-
Notifications
You must be signed in to change notification settings - Fork 53
Optimize webhooks JSON with compact structure and string interning #232
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -64,3 +64,33 @@ jobs: | |
| echo "" | ||
| exit 1 | ||
| fi | ||
|
|
||
| validate-webhooks: | ||
| runs-on: ubuntu-latest | ||
| name: Validate webhook optimization | ||
|
|
||
| steps: | ||
| - uses: actions/checkout@v4 | ||
| - name: Use Node.js 22.x | ||
| uses: actions/setup-node@v4 | ||
| with: | ||
| node-version: 22.x | ||
| cache: 'npm' | ||
| registry-url: 'https://npm.pkg.github.com' | ||
| - run: npm ci | ||
| env: | ||
| NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
| - name: Build workspaces | ||
| run: npm run build -ws | ||
| - name: Generate full webhooks file | ||
| run: cd languageservice && npm run update-webhooks | ||
| - name: Run optimization validation tests | ||
| run: cd languageservice && npm test -- --testPathPattern=eventPayloads | ||
| - name: Verify validation tests ran | ||
| run: | | ||
| if [ ! -f languageservice/src/context-providers/events/webhooks.full.validation-complete ]; then | ||
|
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since the test is normally skipped when the full webhooks file doesn't exist, I made the test write a marker file. With the marker file, we can be sure the test actually ran. The full webhooks file (unoptimized) is written above by the script |
||
| echo "ERROR: Validation tests did not run!" | ||
| echo "The webhooks.full.validation-complete marker file was not created." | ||
| exit 1 | ||
| fi | ||
| echo "Validation tests completed at: $(cat languageservice/src/context-providers/events/webhooks.full.validation-complete)" | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -6,8 +6,9 @@ This document describes the JSON data files used by the language service package | |||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| The language service uses several JSON files containing schema definitions, webhook payloads, and other metadata. To reduce bundle size, these files are: | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| 1. **Optimized at generation time** — unused events are dropped, unused fields are stripped | ||||||||||||||||||||||||||||||||||
| 2. **Minified at build time** — whitespace is removed to produce `.min.json` files | ||||||||||||||||||||||||||||||||||
| 1. **Optimized at generation time** — unused events are dropped, unused fields are stripped, shared objects are deduplicated, property names are interned | ||||||||||||||||||||||||||||||||||
| 2. **Compacted using a space-efficient format** — params use type-based dispatch arrays instead of objects | ||||||||||||||||||||||||||||||||||
| 3. **Minified at build time** — whitespace is removed to produce `.min.json` files | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| The source `.json` files are human-readable and checked into the repository. The `.min.json` files are generated during build and gitignored. | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
|
|
@@ -19,6 +20,7 @@ The source `.json` files are human-readable and checked into the repository. The | |||||||||||||||||||||||||||||||||
| |------|-------------| | ||||||||||||||||||||||||||||||||||
| | `src/context-providers/events/webhooks.json` | Webhook event payload schemas for autocompletion | | ||||||||||||||||||||||||||||||||||
| | `src/context-providers/events/objects.json` | Deduplicated shared object definitions referenced by webhooks | | ||||||||||||||||||||||||||||||||||
| | `src/context-providers/events/strings.json` | Interned property names shared by webhooks and objects | | ||||||||||||||||||||||||||||||||||
| | `src/context-providers/events/schedule.json` | Schedule event context data | | ||||||||||||||||||||||||||||||||||
| | `src/context-providers/events/workflow_call.json` | Reusable workflow call context data | | ||||||||||||||||||||||||||||||||||
| | `src/context-providers/descriptions.json` | Context variable descriptions for hover | | ||||||||||||||||||||||||||||||||||
|
|
@@ -33,7 +35,7 @@ The source `.json` files are human-readable and checked into the repository. The | |||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| ### Webhooks and Objects | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| The `webhooks.json` and `objects.json` files are generated from the [GitHub REST API description](https://github.com/github/rest-api-description): | ||||||||||||||||||||||||||||||||||
| The `webhooks.json`, `objects.json`, and `strings.json` files are generated from the [GitHub REST API description](https://github.com/github/rest-api-description): | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| ```bash | ||||||||||||||||||||||||||||||||||
| cd languageservice | ||||||||||||||||||||||||||||||||||
|
|
@@ -44,9 +46,10 @@ This script: | |||||||||||||||||||||||||||||||||
| 1. Fetches webhook schemas from the GitHub API description | ||||||||||||||||||||||||||||||||||
| 2. **Validates** all events are categorized (fails if new events are found) | ||||||||||||||||||||||||||||||||||
| 3. **Drops** events that aren't valid workflow triggers (see [Dropped Events](#dropped-events)) | ||||||||||||||||||||||||||||||||||
| 4. **Strips** unused fields like `description` and `summary` (see [Stripped Fields](#stripped-fields)) | ||||||||||||||||||||||||||||||||||
| 4. **Compacts** params into a space-efficient array format, keeping only `name`, `description`, and `childParamsGroups` (see [Compact Format](#compact-format)) | ||||||||||||||||||||||||||||||||||
| 5. **Deduplicates** shared object definitions into `objects.json` | ||||||||||||||||||||||||||||||||||
| 6. Writes the optimized, pretty-printed JSON files | ||||||||||||||||||||||||||||||||||
| 6. **Interns** duplicate property names into `strings.json` (see [String Interning](#string-interning)) | ||||||||||||||||||||||||||||||||||
| 7. Writes the optimized, pretty-printed JSON files | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| ### Handling New Webhook Events | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
|
|
@@ -67,9 +70,9 @@ Action required: | |||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| 1. Check [Events that trigger workflows](https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows) | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| 2. Edit `languageservice/script/webhooks/index.ts`: | ||||||||||||||||||||||||||||||||||
| - Add to `KEPT_EVENTS` if it's a valid workflow trigger | ||||||||||||||||||||||||||||||||||
| - Add to `DROPPED_EVENTS` if it's GitHub App or API-only | ||||||||||||||||||||||||||||||||||
| 2. Edit `languageservice/src/context-providers/events/event-filters.json`: | ||||||||||||||||||||||||||||||||||
| - Add to `kept` array if it's a valid workflow trigger | ||||||||||||||||||||||||||||||||||
| - Add to `dropped` array if it's GitHub App or API-only | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| 3. Run `npm run update-webhooks` and commit the changes | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
|
|
@@ -101,13 +104,15 @@ The code imports the minified versions: | |||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| ```ts | ||||||||||||||||||||||||||||||||||
| import webhooks from "./events/webhooks.min.json" | ||||||||||||||||||||||||||||||||||
| import objects from "./events/objects.min.json" | ||||||||||||||||||||||||||||||||||
| import strings from "./events/strings.min.json" | ||||||||||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| ## CI Verification | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| CI verifies that generated source files are up-to-date: | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| 1. Runs `npm run update-webhooks` to regenerate webhooks.json and objects.json | ||||||||||||||||||||||||||||||||||
| 1. Runs `npm run update-webhooks` to regenerate webhooks.json, objects.json, and strings.json | ||||||||||||||||||||||||||||||||||
| 2. Checks for uncommitted changes with `git diff --exit-code` | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| The `.min.json` files are generated at build time and are not committed to the repository. | ||||||||||||||||||||||||||||||||||
|
|
@@ -118,33 +123,95 @@ If the build fails, run `cd languageservice && npm run update-webhooks` locally | |||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| Webhook events that aren't valid workflow `on:` triggers are dropped (e.g., `installation`, `ping`, `member`, etc.). These are GitHub App or API-only events. | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| See `DROPPED_EVENTS` in `script/webhooks/index.ts` for the full list. | ||||||||||||||||||||||||||||||||||
| See `dropped` array in `src/context-providers/events/event-filters.json` for the full list. | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| ## Stripped Fields | ||||||||||||||||||||||||||||||||||
| ## Compact Format | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| Unused fields are stripped to reduce bundle size. For example: | ||||||||||||||||||||||||||||||||||
| Params are converted from verbose objects into compact arrays, keeping only the fields needed for autocompletion and hover docs (`name`, `description`, `childParamsGroups`). Unused fields like `type`, `in`, `isRequired`, `enum`, and `default` are discarded. | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| | Format | Meaning | | ||||||||||||||||||||||||||||||||||
| |--------|---------| | ||||||||||||||||||||||||||||||||||
| | `"name"` | Name only (no description, no children) | | ||||||||||||||||||||||||||||||||||
| | `[name, desc]` | Name + description (arr[1] is a string) | | ||||||||||||||||||||||||||||||||||
| | `[name, children]` | Name + children (arr[1] is an array) | | ||||||||||||||||||||||||||||||||||
| | `[name, desc, children]` | Name + description + children | | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| The reader uses `typeof arr[1]` to determine the format: if it's a string, it's a description; if it's an array, it's children. | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| **Example:** | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| ```json | ||||||||||||||||||||||||||||||||||
| // Before (from webhooks.all.json) | ||||||||||||||||||||||||||||||||||
| // Before (object format) | ||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||
| "type": "object", | ||||||||||||||||||||||||||||||||||
| "name": "issue", | ||||||||||||||||||||||||||||||||||
| "in": "body", | ||||||||||||||||||||||||||||||||||
| "description": "The issue itself.", | ||||||||||||||||||||||||||||||||||
| "isRequired": true, | ||||||||||||||||||||||||||||||||||
| "childParamsGroups": [...] | ||||||||||||||||||||||||||||||||||
| "childParamsGroups": [ | ||||||||||||||||||||||||||||||||||
| { "name": "id" }, | ||||||||||||||||||||||||||||||||||
| { "name": "title", "description": "Issue title" } | ||||||||||||||||||||||||||||||||||
| ] | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| // After (webhooks.json) | ||||||||||||||||||||||||||||||||||
| // After (compact format) | ||||||||||||||||||||||||||||||||||
| ["issue", "The issue itself.", [ | ||||||||||||||||||||||||||||||||||
| "id", | ||||||||||||||||||||||||||||||||||
| ["title", "Issue title"] | ||||||||||||||||||||||||||||||||||
| ]] | ||||||||||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| ## String Interning | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| Property names that appear 2+ times are "interned" into a shared string table (`strings.json`). In the compact arrays, these names are replaced with numeric indices: | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| ```json | ||||||||||||||||||||||||||||||||||
| // strings.json | ||||||||||||||||||||||||||||||||||
| ["url", "id", "name", ...] // Index 0 = "url", 1 = "id", 2 = "name" | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| // webhooks.json - uses indices instead of strings | ||||||||||||||||||||||||||||||||||
| ["push", [ | ||||||||||||||||||||||||||||||||||
| [0, "The URL..."], // 0 = "url" from string table | ||||||||||||||||||||||||||||||||||
| [1, "Unique ID"], // 1 = "id" | ||||||||||||||||||||||||||||||||||
| 2 // 2 = "name" (name-only, no description) | ||||||||||||||||||||||||||||||||||
| ]] | ||||||||||||||||||||||||||||||||||
|
Comment on lines
+170
to
+174
|
||||||||||||||||||||||||||||||||||
| ["push", [ | |
| [0, "The URL..."], // 0 = "url" from string table | |
| [1, "Unique ID"], // 1 = "id" | |
| 2 // 2 = "name" (name-only, no description) | |
| ]] | |
| { | |
| "push": { | |
| "default": { | |
| "p": [ | |
| [0, "The URL..."], // 0 = "url" from string table | |
| [1, "Unique ID"], // 1 = "id" | |
| 2 // 2 = "name" (name-only, no description) | |
| ] | |
| } | |
| } | |
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added this job to validate the optimized webhooks JSON is equivalent to the full webhooks JSON file.
Refer changes to the file eventPayloads.test.ts in this PR