Some notes and observations from debugging theatre ticket website / web app frontends; explicitly for learning purposes and better understanding web app development / debugging.
These notes and observations are purely for learning purposes, and understanding web app development / debugging.
- https://developer.chrome.com/docs/devtools
-
Chrome DevTools is a set of web developer tools built directly into the Google Chrome browser. DevTools lets you edit pages on-the-fly and diagnose problems quickly, which helps you build better websites, faster.
- https://developer.chrome.com/docs/devtools/search
-
Search: Find text across all loaded resources
-
Use the Search panel to find text across all loaded resources.
-
The Search panel lets you find text across all loaded resources with the option to use regular expressions or case-sensitivity. Once you find the text, you can view its source in the Sources panel.
-
- https://developer.chrome.com/docs/devtools/console
-
Console overview
-
Use the Console to test and debug Javascript web applications.
- https://developer.chrome.com/docs/devtools/console/javascript
-
Run JavaScript in the Console
-
This interactive tutorial shows you how to run JavaScript in the Chrome DevTools Console.
-
- https://developer.chrome.com/docs/devtools/console/live-expressions
-
Watch JavaScript values in real time with Live Expressions
-
If you find yourself typing the same JavaScript expression in the Console repeatedly, you might find it easier to create a Live Expression. With Live Expressions, you type an expression once and then pin it to the top of your Console. The value of the expression updates in near real time.
-
- https://developer.chrome.com/docs/devtools/console/reference
-
Console features reference
-
This page is a reference of features related to the Chrome DevTools Console.
-
If you're looking for the API reference on functions like
console.log()see 'Console API Reference'. -
For the reference on functions like
monitorEvents()see 'Console Utilities API Reference'.
-
- https://developer.chrome.com/docs/devtools/console/api
-
Console API reference
-
Use the Console API to write messages to the Console from your JavaScript.
-
- https://developer.chrome.com/docs/devtools/console/utilities
-
Console Utilities API reference
-
The Console Utilities API contains a collection of convenience functions for performing common tasks: selecting and inspecting DOM elements, querying objects, displaying data in readable format, stopping and starting the profiler, monitoring DOM events and function calls, and more.
-
-
- https://developer.chrome.com/docs/devtools/sources
-
Sources panel overview
-
Use the Sources panel to view and edit your website's resources, such as stylesheets, Javascript files, and images.
- https://developer.chrome.com/docs/devtools/javascript
-
Debug JavaScript
-
This tutorial teaches you the basic workflow for debugging any JavaScript issue in DevTools.
-
- https://developer.chrome.com/docs/devtools/javascript/breakpoints
-
Pause your code with breakpoints
-
Use breakpoints to pause your JavaScript code. This guide explains each type of breakpoint that's available in DevTools, as well as when to use and how to set each type.
- https://developer.chrome.com/docs/devtools/javascript/breakpoints#overview
-
Overview of when to use each breakpoint type
-
The most well-known type of breakpoint is line-of-code. But line-of-code breakpoints can be inefficient to set, especially if you don't know exactly where to look, or if you are working with a large codebase. You can save yourself time when debugging by knowing how and when to use the other types of breakpoints.
-
-
- https://developer.chrome.com/docs/devtools/javascript/snippets
-
Run snippets of JavaScript
-
If you find yourself running the same code in the Console repeatedly, consider saving the code as a snippet instead. Snippets have access to the page's JavaScript context. They are an alternative to bookmarklets.
-
You can author snippets in the Sources panel and run them on any page and in incognito mode.
-
- https://developer.chrome.com/docs/devtools/overrides
-
Override web content and HTTP response headers locally
-
With local overrides, you can unblock your workflow by prototyping and testing changes and fixes without waiting for the backend, third-parties, or APIs to support them.
-
Use local overrides to mock remote resources even if you don't have access to them. You can mock responses to requests and various files, for example, HTTP response headers and web content, including XHR and fetch requests.
-
- https://developer.chrome.com/docs/devtools/javascript/reference
-
JavaScript debugging reference
-
Discover new debugging workflows with this comprehensive reference of Chrome DevTools debugging features.
-
See Get Started With Debugging JavaScript In Chrome DevTools to learn the basics of debugging.
-
-
- https://developer.chrome.com/docs/devtools/network/overview
-
Network panel: Analyze network load and resources
-
Use the Network panel to analyze page load and inspect network resources.
- https://developer.chrome.com/docs/devtools/network
-
Inspect network activity
-
This is a hands-on tutorial of some of the most commonly-used DevTools features related to inspecting a page's network activity.
-
- https://developer.chrome.com/docs/devtools/network/reference
-
Network features reference
-
Discover new ways to analyze how your page loads in this comprehensive reference of Chrome DevTools network analysis features.
-
- https://developer.chrome.com/docs/devtools/resources
-
View page resources
-
This guide teaches you how to use Chrome DevTools to view a web page's resources. Resources are the files that a page needs in order to display correctly. Examples of resources include CSS, JavaScript, and HTML files, as well as images.
-
-
- https://developer.chrome.com/docs/devtools/application
-
Application panel overview
-
Use the Application panel to inspect, modify, and debug many aspects of your web app including its manifest, service workers, storage, and cache data.
- https://developer.chrome.com/docs/devtools/storage/localstorage
-
View and edit local storage
-
This guide shows you how to use Chrome DevTools to view, edit, and delete localStorage key-value pairs. Local storage saves data across browser sessions.
-
- https://developer.chrome.com/docs/devtools/storage/sessionstorage
-
View and edit session storage
-
This guide shows you how to use Chrome DevTools to view, edit, and delete sessionStorage key-value pairs. Session storage clears when the page session ends.
-
- https://developer.chrome.com/docs/devtools/storage/extensionstorage
-
View and edit extension storage
-
This guide shows you how to use Chrome DevTools to view and change data stored by extensions using the
chrome.storageAPI.
-
- https://developer.chrome.com/docs/devtools/application/cookies
-
View, add, edit, and delete cookies
-
HTTP Cookies are mainly used to manage user sessions, store user personalization preferences, and track user behavior. They are also the cause of all of those annoying "this page uses cookies" consent forms that you see across the web. This guide teaches you how to view, add, edit, and delete a page's cookies with Chrome DevTools.
-
- https://developer.chrome.com/docs/devtools/javascript/background-services
-
Debug background services
-
The Background services section of Chrome DevTools is a collection of tools for the JavaScript APIs that enables your website to send and receive updates even when a user does not have your website open.
-
- https://developer.chrome.com/docs/devtools/application/webmcp
-
Debug WebMCP tools
-
Use the WebMCP panel in Chrome DevTools to inspect, debug, and test the client-side AI tools your website exposes to AI agents. The panel provides full end-to-end visibility into the tool lifecycle, including registration, schema validation, and invocation history.
-
-
- https://developer.chrome.com/docs/devtools/ai-assistance
-
AI assistance
-
Supercharge your development workflow with Gemini integrated directly into Chrome DevTools. Streamline debugging with AI assistance for styling, performance, network and sources.
- https://developer.chrome.com/docs/devtools/ai-assistance/get-started
-
Enable AI assistance in DevTools
-
Use the AI assistance panel to learn more about how your website works with the help of AI.
-
- https://developer.chrome.com/docs/devtools/ai-assistance/code-generation
-
Generate code in Console and Sources
-
Use Gemini to generate code in the Console and Sources panels.
-
From Chrome 147, this feature is available for all text-based resources for a web page. The priority language for code generation is JavaScript.
-
-
-
- https://react.dev/learn/react-developer-tools
-
React Developer Tools
-
Use React Developer Tools to inspect React components, edit props and state, and identify performance problems.
- https://chromewebstore.google.com/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi?hl=en
-
React Developer Tools
-
Adds React debugging tools to the Chrome Developer Tools.
-
React Developer Tools is a Chrome DevTools extension for the open-source React JavaScript library. It allows you to inspect the React component hierarchies in the Chrome Developer Tools.
You will get two new tabs in your Chrome DevTools: "Components ⚛" and "Profiler ⚛".
The Components tab shows you the root React components that were rendered on the page, as well as the subcomponents that they ended up rendering.
By selecting one of the components in the tree, you can inspect and edit its current props and state in the panel on the right. In the breadcrumbs you can inspect the selected component, the component that created it, the component that created that one, and so on.
If you inspect a React element on the page using the regular Elements tab, then switch over to the React tab, that element will be automatically selected in the React tree.
The Profiler tab allows you to record performance information.
-
- https://github.com/react/react/tree/main/packages
-
- https://chromewebstore.google.com/detail/redux-devtools/lmhkpmbekcpmknklioeibfkpmmfibljd?hl=en
-
Redux DevTools
-
Redux DevTools for debugging application's state changes.
The extension provides power-ups for your Redux development workflow. Apart from Redux, it can be used with any other architectures which handle the state.
- https://github.com/reduxjs/redux-devtools
-
Redux DevTools
-
DevTools for Redux with hot reloading, action replay, and customizable UI
-
Developer Tools to power-up Redux development workflow or any other architecture which handles the state change (see integrations).
- https://github.com/reduxjs/redux-devtools/tree/main/extension#redux-devtools-extension
-
Redux DevTools Extension
-
- https://github.com/reduxjs/redux-devtools/blob/main/extension/docs/API/Arguments.md
-
Options
-
Use with:
window.__REDUX_DEVTOOLS_EXTENSION__([options])window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__([options])()window.__REDUX_DEVTOOLS_EXTENSION__.connect([options])@redux-devtools/extensionnpm package
-
- https://github.com/reduxjs/redux-devtools/blob/main/extension/docs/API/Methods.md
-
Communicate with the extension directly
-
Note this is advanced API, which you usually don't need to use with Redux enhancer.
-
Use the following methods of
window.__REDUX_DEVTOOLS_EXTENSION__:- connect
- disconnect
- send
- listen
- open
- notifyErrors
-
- https://github.com/reduxjs/redux-devtools/blob/main/extension/docs/Integrations.md
-
Integrations for js and non-js frameworks
-
-
-
- https://github.com/jqlang/jq
-
jq
-
Command-line JSON processor
-
jqis a lightweight and flexible command-line JSON processor akin tosed,awk,grep, and friends for JSON data. It's written in portable C and has zero runtime dependencies, allowing you to easily slice, filter, map, and transform structured data.
-
- https://github.com/tomnomnom/gron
-
gron
-
Make JSON greppable!
-
gron transforms JSON into discrete assignments to make it easier to grep for what you want and see the absolute 'path' to it. It eases the exploration of APIs that return large blobs of JSON but have terrible documentation.
- Quck usage examples:
gron "URL" | fgrep "foo.bar"pbpaste | gron | subl -n
-
Extract Next.JS's __NEXT_DATA__ from initial page HTML:
// Raw stringified JSON
nextDataJson = $('#__NEXT_DATA__').innerText
// Copy it to the clipboard
copy(nextDataJson)
// Parse it into a JS Object if that is useful
nextDataObject = JSON.parse(nextDataJson)Search for a known key or value in a JavaScript object, and get it's path:
First define these helper functions:
function* walkObject(value, path = []) {
if (value === null || typeof value !== "object") return;
for (const [rawKey, child] of Object.entries(value)) {
const key = Array.isArray(value) ? Number(rawKey) : rawKey;
const childPath = [...path, key];
yield { path: childPath, key, value: child };
yield* walkObject(child, childPath);
}
}
const findInObject = (object, ...filters) =>
[...walkObject(object)].flatMap(entry => {
const matches = [];
for (const [label, test] of filters) {
if (test(entry)) matches.push(label);
}
return matches.length
? [{
path: entry.path,
value: entry.value,
...(filters.length > 1 && {
matched: matches.join(" and "),
}),
}]
: [];
});
const jsonEquals = (a, b) =>
a === b ||
(
a !== null &&
b !== null &&
typeof a === "object" &&
typeof b === "object" &&
Array.isArray(a) === Array.isArray(b) &&
Object.keys(a).length === Object.keys(b).length &&
Object.keys(a).every(
key => Object.hasOwn(b, key) && jsonEquals(a[key], b[key])
)
);And then you can use them like:
// Assume the parsed JSON is stored in `nextDataObject`:
// const nextDataObject = JSON.parse(nextDataJson);
// Search by key:
// Replace "targetKey" with the exact key name to find.
findInObject(
nextDataObject,
["key", ({ key }) => key === "targetKey"],
);
// Search by string value:
// Replace "targetValue" with the exact string value to find.
findInObject(
nextDataObject,
["value", ({ value }) => value === "targetValue"],
);
// Search by non-string JSON value:
// Replace 123 with a number, boolean, null, array, or object.
// Examples: 123, true, null, [1, 2, 3], { enabled: true }
findInObject(
nextDataObject,
["value", ({ value }) => jsonEquals(value, 123)],
);
// Search for an exact string match in either a key name or a value:
// Replace "target" with the key or string value to find.
findInObject(
nextDataObject,
["key", ({ key }) => key === "target"],
["value", ({ value }) => value === "target"],
);
// Search for a partial string match in either a key name or string value:
// Replace "target" with the substring to find.
// Matching is case-sensitive.
findInObject(
nextDataObject,
[
"key",
({ key }) =>
typeof key === "string" &&
key.includes("target"),
],
[
"value",
({ value }) =>
typeof value === "string" &&
value.includes("target"),
],
);
// Search for a partial string match in either a key name or string value:
// Replace "target" with the substring to find.
// Matching is case-insensitive.
{
const search = "target".toLowerCase();
findInObject(
nextDataObject,
[
"key",
({ key }) =>
typeof key === "string" &&
key.toLowerCase().includes(search),
],
[
"value",
({ value }) =>
typeof value === "string" &&
value.toLowerCase().includes(search),
],
);
}Search for a known key or value in a JSON blob via gron, and get it's path:
# Search for a partial string match in either a key name or string value:
# Replace "target" with the substring to find.
# A matching key may appear anywhere within the nested path.
# Matching is case-sensitive.
pbpaste | gron | grep -F 'target'
# Alternatives:
# pbpaste | gron | rg -F 'target'
# pbpaste | gron | fgrep 'target' # Legacy alias for grep -F
# Search for a partial string match in either a key name or string value:
# Replace "target" with the substring to find.
# A matching key may appear anywhere within the nested path.
# Matching is case-insensitive.
pbpaste | gron | grep -Fi 'target'
# Alternatives:
# pbpaste | gron | rg -Fi 'target'
# pbpaste | gron | fgrep -i 'target' # Legacy alias for grep -F
# Search by key:
# Replace "targetKey" with the exact key name to find anywhere in the path.
# If the key contains nested content, all descendant lines beneath it will match.
pbpaste | gron | grep -E '(\.targetKey|\["targetKey"\])(\.|\[| =)'
# Alternatives:
# pbpaste | gron | rg '(\.targetKey|\["targetKey"\])(\.|\[| =)'
# Search by string value:
# Replace "targetValue" with the exact string value to find.
pbpaste | gron | grep -F '= "targetValue";'
# Alternatives:
# pbpaste | gron | rg -F '= "targetValue";'
# pbpaste | gron | fgrep '= "targetValue";' # Legacy alias for grep -F
# Search by non-string JSON value:
# Replace '123' with the exact number, boolean, or null value to find.
# Examples: '123', 'true', 'false', 'null'
pbpaste | gron | grep -E '= 123;$'
# Alternatives:
# pbpaste | gron | rg '= 123;$'
# Search for an exact string match in either a key name or a value:
# Replace "target" with the exact key or string value to find.
# A matching key may appear anywhere within the nested path.
pbpaste | gron | grep -E '(\.target|\["target"\])(\.|\[| =)|= "target";$'
# Alternatives:
# pbpaste | gron | rg '(\.target|\["target"\])(\.|\[| =)|= "target";$'Search for a known key or value in a JSON blob via jq, and get it's path:
# Search by key:
# Replace "targetKey" with the exact key name to find.
pbpaste | jq -c --arg key "targetKey" '
paths as $path
| select($path[-1] == $key)
| {
path: $path,
value: getpath($path)
}
'
# Search by string value:
# Replace "targetValue" with the exact string value to find.
pbpaste | jq -c --arg value "targetValue" '
paths(scalars) as $path
| select(getpath($path) == $value)
| {
path: $path,
value: getpath($path)
}
'
# Search by non-string JSON value:
# Replace '123' with a number, boolean, null, array, or object.
# Examples: '123', 'true', 'null', '[1,2,3]', '{"enabled":true}'
pbpaste | jq -c --argjson value '123' '
paths as $path
| select(getpath($path) == $value)
| {
path: $path,
value: getpath($path)
}
'
# Search for an exact string match in either a key name or a value:
# Replace "target" with the key or string value to find.
pbpaste | jq -c --arg search "target" '
paths as $path
| getpath($path) as $value
| select(
($path[-1] == $search)
or
($value == $search)
)
| {
path: $path,
value: $value,
matched: (
if ($path[-1] == $search) and ($value == $search) then "key and value"
elif $path[-1] == $search then "key"
else "value"
end
)
}
'
# Search for a partial string match in either a key name or string value:
# Replace "target" with the substring to find.
# Matching is case-sensitive.
pbpaste | jq -c --arg search "target" '
paths as $path
| getpath($path) as $value
| select(
(($path[-1] | type) == "string" and ($path[-1] | contains($search)))
or
(($value | type) == "string" and ($value | contains($search)))
)
| {
path: $path,
value: $value,
matched: (
if (($path[-1] | type) == "string" and ($path[-1] | contains($search)))
and (($value | type) == "string" and ($value | contains($search)))
then "key and value"
elif (($path[-1] | type) == "string" and ($path[-1] | contains($search)))
then "key"
else "value"
end
)
}
'
# Search for a partial string match in either a key name or string value:
# Replace "target" with the substring to find.
# Matching is case-insensitive.
pbpaste | jq -c --arg search "target" '
($search | ascii_downcase) as $needle
| paths as $path
| getpath($path) as $value
| (($path[-1] | type) == "string"
and ($path[-1] | ascii_downcase | contains($needle))) as $key_match
| (($value | type) == "string"
and ($value | ascii_downcase | contains($needle))) as $value_match
| select($key_match or $value_match)
| {
path: $path,
value: $value,
matched: (
if $key_match and $value_match then "key and value"
elif $key_match then "key"
else "value"
end
)
}
'It seems to be a Next.JS web app:
$('#__NEXT_DATA__').innerText
There also seem to be some interesting globals such as:
window.__INITIAL_STATE__window.__STATE__window.__STATE__.apiwindow.__STATE__.basketwindow.__STATE__.eventInfowindow.__STATE__.eventInfo.discoveryUrlwindow.__STATE__.eventInfo.namewindow.__STATE__.eventInfo.dateswindow.__STATE__.eventInfo.venuewindow.__STATE__.eventInfo.secnameswindow.__STATE__.eventInfo.restrictSingleSeats- etc
window.__STATE__.offerswindow.__STATE__.quickpickswindow.__STATE__.ticketSelectionwindow.__STATE__.ticketSelection.sectionswindow.__STATE__.ticketSelection.priceToSectionswindow.__STATE__.ticketSelection.ticketTypes- etc
window.__STATE__.unlockTokenwindow.__STATE__.smartQueue- etc
window.__REDUX_STORE__window.__REDUX_STORE__.getState()
- etc
Some potentially interesting JS source files for further reading:
https://{SUBDOMAIN}.{STATIC-CONTENT-DOMAIN}.{TLD}/{BUILD-ID}/_next/static/chunks/seatmap.{HASH}.js-
s.jsx)(e0.$t, { onClick: () => p(e.ticketTypeId, e.offerId), __: i })
-
https://{SUBDOMAIN}.{STATIC-CONTENT-DOMAIN}.{TLD}/{BUILD-ID}/_next/static/chunks/pages/event/%5BeventId%5D-{HASH}.jslet s = l(){config: u, eventInfo: m, brand: B} = sif (m.restrictSingleSeats &&
https://checkout.{DOMAIN}.{TLD}/js/sdk.jsacceptSplit: Boolean("true" === e.acceptNonAdjacent || c),const E = x.tickets- Among likely many other things, it seems to include some GraphQL API things such as:
mutation:$totalReservationAmount:
- https://gist.github.com/0xdevalias
- https://github.com/0xdevalias/chatgpt-source-watch : Analyzing the evolution of ChatGPT's codebase through time with curated archives and scripts.
- Deobfuscating / Unminifying Obfuscated Web App Code (0xdevalias' gist)
- Fingerprinting Minified JavaScript Libraries / AST Fingerprinting / Source Code Similarity / Etc (0xdevalias' gist)
- Reverse Engineering Webpack Apps (0xdevalias' gist)
- Bypassing Cloudflare, Akamai, etc (0xdevalias' gist)
- Debugging Electron Apps (and related memory issues) (0xdevalias' gist)
- devalias' Beeper CSS Hacks (0xdevalias' gist)
- Reverse Engineering Golang (0xdevalias' gist)
- Reverse Engineering on macOS (0xdevalias' gist)