Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions react/src/js/app.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import ReactDOM, { render } from 'react-dom';
import { DOMRegistry } from 'react-dom-components';
import { parseToPrimitive } from './components/Consonant/Helpers/general';
import { loadLana } from './components/Consonant/Helpers/lana';
import { initBeacon, beaconPageLoad } from './components/Consonant/Helpers/beacon';
import { initQaHooks } from './components/Consonant/Helpers/qa-hooks';
import Container from './components/Consonant/Container/Container';
import consonantPageRDC from './components/Consonant/Page/ConsonantPageDOM';

Expand Down Expand Up @@ -36,6 +38,17 @@ try {
}
}

// Initialize canary telemetry (consent-gated; idempotent).
try {
initBeacon();
} catch (e) { /* never block boot on telemetry init */ }

// QA introspection API for headless agents. No side effects, idempotent.
// Exposes window.caas.{version, dump, waitForReady} and the caas:ready event.
try {
initQaHooks();
} catch (e) { /* never block boot on QA hooks init */ }

// Must be constructible: Northstar uses bind/apply + new on this callback.
function initReact(element, registry) {
if (registry === undefined) {
Expand All @@ -46,6 +59,11 @@ function initReact(element, registry) {

initReact(document);

// Fire the page_load beacon after React is mounted.
try {
beaconPageLoad();
} catch (e) { /* swallow */ }

function collectionLoadedThroughXf(el) {
if (!el) return false; // Ensure el is not null or undefined
const container = el.firstElementChild;
Expand Down
44 changes: 44 additions & 0 deletions react/src/js/components/Consonant/Container/Container.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,13 @@ import classNames from 'classnames';
import { shape } from 'prop-types';
// import 'whatwg-fetch'; // Removed: fetch is native in modern browsers
import { logLana } from '../Helpers/lana';
import {
beaconCardsRendered,
beaconTargetMissing,
beaconFetchFail,
scheduleAssertions,
} from '../Helpers/beacon';
import { markCaasReady } from '../Helpers/qa-hooks';
import Popup from '../Sort/Popup';
import Search from '../Search/Search';
import Loader from '../Loader/Loader';
Expand Down Expand Up @@ -1061,11 +1068,19 @@ const Container = (props) => {
if (validData) return json;

logLana({ message: `no valid response data from ${endPoint}`, tags: 'collection' });
beaconTargetMissing({ reason: 'empty_collection', endpointUsed: endPoint });
/* istanbul ignore next */
return Promise.reject(new Error('no valid reponse data'));
});
}
logLana({ message: `failure for call to ${url}`, tags: 'collection', errorMessage: `${status}: ${statusText}` });
beaconFetchFail({
url,
method: 'GET',
httpStatus: status,
responseTimeMs: Date.now() - start,
errorMessage: statusText,
});
return Promise.reject(new Error(`${status}: ${statusText}, failure for call to ${url}`));
})
.then((payload) => {
Expand All @@ -1074,6 +1089,7 @@ const Container = (props) => {
setIsFirstLoad(true);
if (!getByPath(payload, 'cards.length')) {
logLana({ message: `no cards return by query to this endpoint: ${endPoint}`, tags: 'collection' });
beaconTargetMissing({ reason: 'no_cards_returned', endpointUsed: endPoint });
if (originSelection === 'events' && box.current) {
removeCollectionFromPage();
}
Expand Down Expand Up @@ -1214,6 +1230,33 @@ const Container = (props) => {

setCards(processedCards);

// Canary telemetry: cards rendered successfully.
try {
beaconCardsRendered({
cardCount: processedCards.length,
totalCountFromApi: payload.totalCount,
fetchDurationMs: Date.now() - start,
endpointUsed: endPoint,
});
} catch (e) { /* never block render on telemetry */ }

// QA ready signal: fires window.__caasReady + caas:ready event.
// Headless agents wait on this instead of guessing on networkidle.
try {
markCaasReady({
cardCount: processedCards.length,
totalCountFromApi: payload.totalCount,
fetchDurationMs: Date.now() - start,
endpointUsed: endPoint,
});
} catch (e) { /* never block render on QA hooks */ }

// Schedule the assertions beacon. Debounced: if multiple fetches
// (partial + full load) each call this, only the last one fires.
try {
scheduleAssertions(800);
} catch (e) { /* swallow */ }

// check if the current page is greater than the last page
const lastPage = Math.ceil(processedCards.length / resultsPerPage);
if (currentPage > lastPage) {
Expand All @@ -1240,6 +1283,7 @@ const Container = (props) => {
return;
}
logLana({ message: 'failed to return processed cards', tags: 'collection' });
beaconTargetMissing({ reason: 'processed_cards_empty', endpointUsed: endPoint });
setLoading(false);
setApiFailure(true);
});
Expand Down
Loading
Loading