From 1101d7434ad5ae8f4a5a974d0cc13ec3fabb8acb Mon Sep 17 00:00:00 2001 From: Bradley Erickson Date: Thu, 5 Feb 2026 16:57:29 -0500 Subject: [PATCH] added url listener to lockfields --- VERSION | 2 +- modules/lo_event/lo_event/lo_event.js | 10 +- modules/lo_event/lo_event/urlLockFields.js | 115 +++++++++++++++++++++ 3 files changed, 121 insertions(+), 6 deletions(-) create mode 100644 modules/lo_event/lo_event/urlLockFields.js diff --git a/VERSION b/VERSION index 82977005b..5c2af96d1 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.1.0+2026.02.03T15.21.57.253Z.1310c89e.berickson.20260130.execution.dag.single.value +0.1.0+2026.02.05T21.57.29.413Z.5f75e49b.berickson.20260205.add.url.to.lockfields diff --git a/modules/lo_event/lo_event/lo_event.js b/modules/lo_event/lo_event/lo_event.js index 6328f8d5c..7b0ab93d0 100644 --- a/modules/lo_event/lo_event/lo_event.js +++ b/modules/lo_event/lo_event/lo_event.js @@ -8,6 +8,7 @@ import * as Queue from './queue.js'; import * as disabler from './disabler.js'; import * as debug from './debugLog.js'; import * as util from './util.js'; +import { initializeUrlLockFields } from './urlLockFields.js'; export const QueueType = Queue.QueueType; @@ -23,7 +24,6 @@ const INIT_STATES = { let initialized = INIT_STATES.NOT_STARTED; // Current FSM state let currentState = new Promise((resolve, reject) => { resolve(); }); // promise pipeline to ensure we handle all initialization - let loggersEnabled = []; // A list of all loggers which should receive events. let queue; @@ -59,7 +59,7 @@ async function initializeLoggers () { * When initializing `lo_event`, clients can set which metadata items * they wish to include. */ -export async function compileMetadata(metadataTasks) { +export async function compileMetadata (metadataTasks) { const taskPromises = metadataTasks.map(async task => { try { const result = await Promise.resolve(task.func()); @@ -75,7 +75,6 @@ export async function compileMetadata(metadataTasks) { return results.filter(Boolean); } - /** * Set specific key/value pairs using the `lock_fields` * event. We use this to set specific fields that we want @@ -145,10 +144,11 @@ export function init ( .then(initializeLoggers) .then(() => setFieldSet([{ source, version }])) .then(() => compileMetadata(metadata)); - if(sendBrowserInfo) { + initializeUrlLockFields(setFieldSet); + if (sendBrowserInfo) { // In the future, some or all of this might be sent on every // reconnect - logEvent("BROWSER_INFO", getBrowserInfo()); + logEvent('BROWSER_INFO', getBrowserInfo()); } } diff --git a/modules/lo_event/lo_event/urlLockFields.js b/modules/lo_event/lo_event/urlLockFields.js new file mode 100644 index 000000000..63760e97b --- /dev/null +++ b/modules/lo_event/lo_event/urlLockFields.js @@ -0,0 +1,115 @@ +let urlListenerInitialized = false; +let lastUrl = null; + +function parseUrlFields (url, baseUrl) { + if (!url) { + return null; + } + + try { + const parsed = new URL(url, baseUrl); + return { + url: parsed.href, + url_path: parsed.pathname + }; + } catch (error) { + return null; + } +} + +function updateUrlLockFields (setFieldSet, url) { + const baseUrl = (typeof window !== 'undefined' && window.location) ? window.location.href : undefined; + const fields = parseUrlFields(url, baseUrl); + if (!fields) { + return; + } + + if (fields.url === lastUrl) { + return; + } + + lastUrl = fields.url; + setFieldSet([fields]); +} + +function initializeWindowListeners (setFieldSet) { + if (typeof window === 'undefined' || !window.location) { + return; + } + + updateUrlLockFields(setFieldSet, window.location.href); + + if (!window.addEventListener) { + return; + } + + const handleLocationChange = () => updateUrlLockFields(setFieldSet, window.location.href); + window.addEventListener('popstate', handleLocationChange); + window.addEventListener('hashchange', handleLocationChange); + + if (window.history) { + const originalPushState = window.history.pushState; + const originalReplaceState = window.history.replaceState; + + window.history.pushState = function (...args) { + const result = originalPushState.apply(this, args); + handleLocationChange(); + return result; + }; + + window.history.replaceState = function (...args) { + const result = originalReplaceState.apply(this, args); + handleLocationChange(); + return result; + }; + } +} + +function initializeChromeListeners (setFieldSet) { + if (typeof chrome === 'undefined' || !chrome.tabs) { + return; + } + + const handleTab = (tab) => { + if (tab && tab.url) { + updateUrlLockFields(setFieldSet, tab.url); + } + }; + + if (chrome.tabs.onUpdated && chrome.tabs.onUpdated.addListener) { + chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => { + if (changeInfo && changeInfo.url) { + updateUrlLockFields(setFieldSet, changeInfo.url); + return; + } + handleTab(tab); + }); + } + + if (chrome.tabs.onActivated && chrome.tabs.onActivated.addListener) { + chrome.tabs.onActivated.addListener((activeInfo) => { + if (!chrome.tabs.query) { + return; + } + chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => { + handleTab(tabs && tabs[0]); + }); + }); + } + + if (chrome.tabs.query) { + chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => { + handleTab(tabs && tabs[0]); + }); + } +} + +export function initializeUrlLockFields (setFieldSet) { + if (urlListenerInitialized) { + return; + } + urlListenerInitialized = true; + + initializeWindowListeners(setFieldSet); + initializeChromeListeners(setFieldSet); +}