diff --git a/.Jules/bolt.md b/.Jules/bolt.md index f02512d4..94e8748d 100644 --- a/.Jules/bolt.md +++ b/.Jules/bolt.md @@ -41,3 +41,7 @@ Action: Updated `collectItems` in `src/utils/whats-new-glance.ts` to iterate ove 2026-09-08 - Zero-Allocation Reverse Array Scan for Chat Follow-up Prompts Learning: Dynamic shallow array copying and array reversal (`[...messages].reverse().find(...)`) on every UI update creates unnecessary temporary heap allocations and garbage collection overhead in client-side scripts. Replacing array copy/reverse with a reverse indexed `for` loop eliminates dynamic array allocations completely and stops scanning as soon as the first matching element is found. Action: Refactored `showFollowUps()` in `src/components/Chat.astro` to use an indexed reverse `for` loop. + +2026-09-19 - Pre-Computed Tabular Column Indexing & Zero-Allocation Date Slicing +Learning: Performing repeated `columns.indexOf(key)` scans (12 times per response) when parsing PostHog HogQL API tabular arrays creates unnecessary O(N) array iteration overhead on edge Workers. Pre-building a `Record` hashtable index map once converts column lookups into O(1) constant-time property reads. Additionally, replacing `date.toISOString().split('T')[0]` with `date.toISOString().slice(0, 10)` in date utilities extracts ISO date strings directly without intermediate string array allocation. +Action: Pre-build column index maps for tabular API response parsers and use `date.toISOString().slice(0, 10)` for YYYY-MM-DD extraction in edge utility functions. diff --git a/package-lock.json b/package-lock.json index b332bbda..02c8cc32 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5167,9 +5167,10 @@ } }, "node_modules/devalue": { - "version": "5.9.0", - "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.9.0.tgz", - "integrity": "sha512-RWrqdArjvPbsATEhOPUo6Wndc/iWnkWKlhIrdlF3zMMYo/c3CVtoaVAyLtWxz5h8nSlkHzxnzV2uLydPXmtF+A==" + "version": "5.9.1", + "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.9.1.tgz", + "integrity": "sha512-+17vil3EVQRzvtDJSFuTWEb8XJRvXqAiV3qZyQWD398QeXUa6CxsUyMdD1fxzEhUrd4FojitFz7lhIHBTlV4fw==", + "license": "MIT" }, "node_modules/devlop": { "version": "1.1.0", diff --git a/package.json b/package.json index 5070b267..ce5b4485 100644 --- a/package.json +++ b/package.json @@ -49,6 +49,7 @@ "vitest": "^4.1.2" }, "overrides": { + "devalue": "5.9.1", "brace-expansion": "5.0.9", "postcss": "8.5.26", "yaml": "2.8.3", diff --git a/src/utils/github-releases.ts b/src/utils/github-releases.ts index d2b6cb48..2a0dbb9f 100644 --- a/src/utils/github-releases.ts +++ b/src/utils/github-releases.ts @@ -176,7 +176,8 @@ export function formatReleaseDate(dateString: string | null): string { return 'Unknown date'; } - return date.toISOString().split('T')[0]; + // Extract YYYY-MM-DD date substring directly to avoid dynamic 2-element array allocation on edge runtimes + return date.toISOString().slice(0, 10); } /** diff --git a/src/utils/visit-stats.ts b/src/utils/visit-stats.ts index 17e005d0..e3767f81 100644 --- a/src/utils/visit-stats.ts +++ b/src/utils/visit-stats.ts @@ -45,18 +45,22 @@ function asIsoTimestamp(raw: unknown): string | null { return d.toISOString(); } +/** + * Extracts a column value from a row using either object property access or hashtable column index map. + * Replaces repeated O(N) array indexOf scans with O(1) constant-time property reads. + */ function valueFromRow( row: unknown, - columns: string[] | undefined, + columnMap: Record | undefined, key: string, index: number ): unknown { if (row && typeof row === 'object' && !Array.isArray(row) && key in row) { return (row as Record)[key]; } - if (Array.isArray(row) && columns) { - const colIndex = columns.indexOf(key); - if (colIndex >= 0) return row[colIndex]; + if (Array.isArray(row) && columnMap) { + const colIndex = columnMap[key]; + if (colIndex !== undefined) return row[colIndex]; } if (Array.isArray(row)) return row[index]; return undefined; @@ -76,26 +80,31 @@ export function parseVisitGlance(payload: unknown): VisitGlance | null { if (!Array.isArray(results) || results.length === 0) return null; const columns = (payload as { columns?: unknown }).columns; - const columnNames = Array.isArray(columns) - ? columns.filter((c): c is string => typeof c === 'string') - : undefined; + let columnMap: Record | undefined; + if (Array.isArray(columns)) { + columnMap = {}; + for (let i = 0; i < columns.length; i++) { + const col = columns[i]; + if (typeof col === 'string') { + columnMap[col] = i; + } + } + } const row = results[0]; - const pageviews = asFiniteNumber(valueFromRow(row, columnNames, 'pageviews', 0)); - const uniqueVisitors = asFiniteNumber(valueFromRow(row, columnNames, 'unique_visitors', 1)); - const firstSeen = asIsoTimestamp(valueFromRow(row, columnNames, 'first_seen', 2)); - const pageviews7d = asFiniteNumber(valueFromRow(row, columnNames, 'pageviews_7d', 3)); - const uniqueVisitors7d = asFiniteNumber(valueFromRow(row, columnNames, 'unique_visitors_7d', 4)); - const unique1d = asFiniteNumber(valueFromRow(row, columnNames, 'unique_visitors_1d', 5)); - const unique1dPrev = asFiniteNumber(valueFromRow(row, columnNames, 'unique_visitors_1d_prev', 6)); - const unique7dPrev = asFiniteNumber(valueFromRow(row, columnNames, 'unique_visitors_7d_prev', 7)); - const unique30d = asFiniteNumber(valueFromRow(row, columnNames, 'unique_visitors_30d', 8)); - const unique30dPrev = asFiniteNumber( - valueFromRow(row, columnNames, 'unique_visitors_30d_prev', 9) - ); - const unique365d = asFiniteNumber(valueFromRow(row, columnNames, 'unique_visitors_365d', 10)); + const pageviews = asFiniteNumber(valueFromRow(row, columnMap, 'pageviews', 0)); + const uniqueVisitors = asFiniteNumber(valueFromRow(row, columnMap, 'unique_visitors', 1)); + const firstSeen = asIsoTimestamp(valueFromRow(row, columnMap, 'first_seen', 2)); + const pageviews7d = asFiniteNumber(valueFromRow(row, columnMap, 'pageviews_7d', 3)); + const uniqueVisitors7d = asFiniteNumber(valueFromRow(row, columnMap, 'unique_visitors_7d', 4)); + const unique1d = asFiniteNumber(valueFromRow(row, columnMap, 'unique_visitors_1d', 5)); + const unique1dPrev = asFiniteNumber(valueFromRow(row, columnMap, 'unique_visitors_1d_prev', 6)); + const unique7dPrev = asFiniteNumber(valueFromRow(row, columnMap, 'unique_visitors_7d_prev', 7)); + const unique30d = asFiniteNumber(valueFromRow(row, columnMap, 'unique_visitors_30d', 8)); + const unique30dPrev = asFiniteNumber(valueFromRow(row, columnMap, 'unique_visitors_30d_prev', 9)); + const unique365d = asFiniteNumber(valueFromRow(row, columnMap, 'unique_visitors_365d', 10)); const unique365dPrev = asFiniteNumber( - valueFromRow(row, columnNames, 'unique_visitors_365d_prev', 11) + valueFromRow(row, columnMap, 'unique_visitors_365d_prev', 11) ); if (