This repository has been archived on 2026-04-03. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
core/docs/JS-DEPENDENCY-AUDIT.md
2026-03-14 12:30:57 +00:00

349 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Frontend JS Dependency Audit
**Version:** v0.28.3 cs2
**Date:** 2026-03-13
**Purpose:** Map every global, cross-file dependency, and init sequence
to prepare for module extraction.
---
## Summary
| Metric | Value |
|--------|-------|
| JS files | 47 |
| Total lines | 25,056 |
| Total bytes | 1,103,572 (~1.1 MB) |
| Top-level globals (original) | 431 |
| IIFE-wrapped files | 47 (all) |
| Explicit `window.*` exports | ~215 |
| Functions privatized | ~168 |
| Implicit globals remaining | 0 |
| Vendor libs | 3 (marked.min.js, purify.min.js, codemirror.bundle.js) |
| Inline `onclick` handlers (templates) | ~50 unique functions |
| Dynamic `onclick` in JS (innerHTML) | 143 total occurrences |
---
## Architecture: IIFE Modules (Post-Decomposition)
No module system (no ES modules, no bundler, no AMD). Every file is a
`<script>` tag loaded in dependency order. All 47 files are now wrapped
in IIFEs with explicit `window.*` exports:
```js
(function() {
'use strict';
// ... all code ...
window.MyGlobal = MyGlobal;
})();
```
**Phase 1 complete.** Zero implicit globals remain. ~168 functions
privatized. The next phase is onclick → addEventListener migration
(143 dynamic handlers in JS innerHTML + ~50 in Go templates), which
is the prerequisite for ES module conversion.
### IIFE Files
| File | Clean? | Leaks |
|------|--------|-------|
| `admin-handlers.js` | ✅ | None (all via inline onclick from globals) |
| `admin-scaffold.js` | ✅ | None |
| `editor-surface.js` | ✅ | None |
| `knowledge-ui.js` | ✅ | None |
| `pages-splash.js` | ✅ | None |
| `task-admin.js` | ❌ | `window._loadAdminTasks` |
| `task-settings.js` | ❌ | `window._createFromTemplate`, `window._loadSettingsTasks` |
| `task-sidebar.js` | ❌ | `window.TaskSidebar` |
| `tools-toggle.js` | ✅ | None |
| `workflow-admin.js` | ❌ | 5 functions (`_loadAdminWorkflows`, `_wfCreate`, `_wfOpen`, `_wfEditStage`, `_wfDeleteStage`) |
| `workflow-api.js` | ✅ | None (registers on App) |
| `workflow-queue.js` | ❌ | `window.WorkflowQueue` |
---
## Script Load Order
### base.html (shared — every surface)
```
1. app-state.js → const App = { ... }
2. api.js → const API = { ... }
3. (inline) → window.__BASE__, auth bootstrap
4. events.js → const Events = { ... }
5. ui-primitives.js → const UI = { ... }
6. vendor/marked.min.js
7. vendor/purify.min.js
8. ui-format.js → formatMessage(), toggleCodeCollapse(), etc.
9. ui-core.js → renderChatList(), renderMessages(), etc.
10. pages.js → const Pages = { ... }
11. user-menu.js → const UserMenu = { ... }
12. model-selector.js → const ModelSelector = { ... }
13. file-tree.js → const FileTree = { ... }
14. code-editor.js → const CodeEditor = { ... }
15. note-editor.js → const NoteEditor = { ... }
16. drag-resize.js → const DragResize = { ... }
17. pane-container.js → const PaneContainer = { ... }
18. (inline) → Theme.init()
19. surfaces/{surface}/js/main.js → surface CSS + scaffold
```
### chat.html (25 additional scripts + 1 vendor)
```
vendor/codemirror/codemirror.bundle.js → window.CM (optional, onerror fallback)
20. extensions.js → const Extensions = { ... }
21. panels.js → const PanelRegistry = { ... }
22. ui-settings.js → settings rendering (bare globals)
23. ui-admin.js → admin rendering (bare globals)
24. tokens.js → token counting (bare globals)
25. notes.js → notes UI (bare globals)
26. note-graph.js → graph visualization (bare globals)
27. files.js → file upload/management (bare globals)
28. tools-toggle.js → tool enable/disable (IIFE)
29. knowledge-ui.js → KB picker (IIFE)
30. memory-ui.js → memory panel (bare globals)
31. persona-kb.js → persona KB bindings (bare globals)
32. projects-ui.js → project management (bare globals, 78 top-level decls)
33. channel-models.js → const ChannelModels = { ... }
34. notification-prefs.js → notification preferences (bare globals)
35. notifications.js → const Notifications = { ... }
36. chat.js → const ChatInput = { ... } + 29 top-level functions
37. settings-handlers.js → settings form handlers (bare globals, 24 decls)
38. admin-handlers.js → admin form handlers (IIFE, 40 internal functions)
39. workflow-api.js → workflow REST helpers (IIFE)
40. workflow-admin.js → workflow admin UI (IIFE, 5 window.* leaks)
41. workflow-queue.js → workflow queue sidebar (IIFE, window.WorkflowQueue)
42. task-sidebar.js → task sidebar (IIFE, window.TaskSidebar)
43. app.js → init(), startApp(), event listeners
44. debug.js → debug console (bare globals)
45. repl.js → REPL (bare globals)
```
### admin.html (14 additional)
Loads: ui-settings, ui-admin, admin-handlers, settings-handlers,
knowledge-ui, persona-kb, memory-ui, notifications, files,
admin-scaffold, workflow-api, workflow-admin, task-admin,
admin-surfaces.
### notes.html (14 additional)
Loads: chat, files, channel-models, tokens, extensions, notes,
note-graph, knowledge-ui, persona-kb, memory-ui, notifications,
panels, projects-ui, app.
### settings.html (9 additional)
Loads: ui-settings, settings-handlers, task-settings, knowledge-ui,
persona-kb, memory-ui, notifications, notification-prefs,
channel-models.
### editor.html (4 additional + 1 vendor)
Loads: vendor/codemirror, chat-pane, editor-surface, app.
Lightest surface — fewest dependencies.
---
## Core Globals (Dependency Roots)
These are referenced by 10+ other files. They form the "root set" that
must be extracted first.
| Global | Defined in | Referenced by | Role |
|--------|-----------|---------------|------|
| `App` | app-state.js | 20 files | State container (chats, models, user, settings, presence) |
| `API` | api.js | 36 files | REST client (all HTTP calls) |
| `UI` | ui-core.js | 32 files | DOM helpers (toast, confirm, modal, sidebar, theme) |
| `Events` | events.js | 9 files | Pub/sub event bus (WebSocket bridge) |
### Secondary Globals (39 references)
| Global | Defined in | Referenced by | Role |
|--------|-----------|---------------|------|
| `Pages` | pages.js | Templates + 3 files | Page-level actions (login, admin forms) |
| `ChannelModels` | channel-models.js | 4 files | Model roster per channel |
| `ChatInput` | chat.js | 3 files | Message input state + send |
| `Notifications` | notifications.js | Templates + 2 files | Bell dropdown, badge count |
| `ChatPane` | chat-pane.js | 3 files | Embedded chat component (editor) |
| `CM` | vendor/codemirror.bundle.js | 5 files | CodeMirror 6 editor (optional, graceful fallback) |
| `PanelRegistry` | panels.js | Templates + 1 file | Side panel management |
### Leaf Globals (12 references, mostly self-contained)
ModelSelector, FileTree, CodeEditor, NoteEditor, DragResize,
PaneContainer, UserMenu, WorkflowQueue, TaskSidebar, Extensions.
---
## Inline Handler Problem
143 `onclick=` patterns in JS files (innerHTML-generated HTML) plus
~50 in Go templates. These reference bare globals that **must remain
on `window`** or the handlers break. This is the primary blocker for
ES module migration.
**Top offenders (dynamic onclick in JS):**
| File | Count | Examples |
|------|-------|---------|
| ui-admin.js | 28 | Admin list row actions |
| projects-ui.js | 26 | Project CRUD, channel/KB/note associations |
| ui-core.js | 25 | Chat list, sidebar, context menus |
| ui-format.js | 10 | Code block copy/preview/download |
| ui-settings.js | 8 | Settings form toggles |
| notifications.js | 8 | Notification row actions |
**Template onclick (must remain global):**
UI.toggleSidebar, UI.toggleSidebarSection, UI.switchTeamTab,
Notifications.toggleDropdown, PanelRegistry.toggle, PanelRegistry.closeAll,
Pages.doLogin, Pages.saveSettings, Pages.saveProvider, Pages.saveTeam,
handleLogin, handleRegister, switchAuthTab, newChat, newFolder,
newChannelOrDM, sendMessage, createProject, closeLightbox,
toggleSidePanelFullscreen, plus ~15 more Pages.* admin form handlers.
---
## Cross-File Call Graph (Simplified)
```
app-state.js (App)
↑ read/write by 20 files
api.js (API)
↑ called by 36 files
events.js (Events)
↑ subscribed by 9 files
├── chat.js (typing, message.created, presence)
├── notifications.js (notification.new/read)
├── extensions.js (tool.call/result)
├── repl.js (debug events)
└── app.js (role.fallback banner)
ui-primitives.js (UI)
↑ called by 32 files
├── toast(), confirm(), modal()
├── toggleSidebar(), toggleSidebarSection()
└── theme, kbd shortcuts
ui-format.js (formatMessage, etc.)
↑ called by ui-core.js, chat.js, notes.js, projects-ui.js
├── depends on: marked.min.js, purify.min.js
ui-core.js (renderChatList, renderMessages, etc.)
↑ called by chat.js, app.js, projects-ui.js
├── depends on: App, API, UI, ui-format
chat.js (ChatInput, sendMessage, selectChat, loadChats)
↑ called by app.js, templates
├── depends on: App, API, Events, UI, ui-core, ui-format,
│ ChannelModels, tokens, files
app.js (init, startApp)
↑ entry point — called by inline <script> in base.html
├── depends on: everything above + surface-specific modules
```
---
## Decomposition Strategy
### Phase 1: Extract Core Four (no bundler required)
Move `App`, `API`, `Events`, `UI` to a pattern where they're defined
as proper singletons with explicit exports. No ES modules yet — just
clean up the global registration so each file has a single `window.X`
export with a documented interface.
**Why no bundler yet:** 143 dynamic onclick handlers + 50 template
onclick handlers require globals on `window`. A bundler would need
either (a) a global shim layer or (b) converting all handlers to
`addEventListener`. Option (b) is the right answer but it's ~200
call sites across 16 files + 10 templates — too much churn for one
changeset.
### Phase 2: onclick → addEventListener migration
Convert dynamic HTML generation from:
```js
`<button onclick="deleteItem('${id}')">Delete</button>`
```
to:
```js
const btn = document.createElement('button');
btn.textContent = 'Delete';
btn.addEventListener('click', () => deleteItem(id));
```
This eliminates the global requirement. Target the top 6 files first
(ui-admin, projects-ui, ui-core, ui-format, ui-settings, notifications)
which account for 105 of 143 occurrences.
Template onclick handlers stay — Go templates can't do addEventListener.
These become the only remaining reason for `window.*` exports.
### Phase 3: ES Module conversion
Once onclick handlers are gone from JS, files can become ES modules:
```html
<script type="module" src="js/app.js"></script>
```
Import graph follows the dependency order already established by
`<script>` tag ordering. Vendor libs (marked, purify, codemirror) stay
as classic scripts since they self-register on `window`.
### Phase 4: Template handler shim
Create a thin `window.handlers = {}` object that ES modules register
into. Template onclick handlers call `handlers.doLogin()` etc. This
is the final bridge — once templates move to client-side rendering
(extension surface SDK), this shim disappears.
---
## Files by Decomposition Priority
### Tier 1 — Core (extract first, most dependents)
| File | Lines | Globals | Dependents | Notes |
|------|-------|---------|------------|-------|
| app-state.js | 96 | 3 | 20 | Clean singleton, easy extract |
| api.js | 1,159 | 3 | 36 | Large but self-contained |
| events.js | 261 | 1 | 9 | Clean singleton |
| ui-primitives.js | 1,316 | 18 | 32 | Large, many utility functions |
### Tier 2 — Rendering (depends on Tier 1)
| File | Lines | Globals | Dependents | Notes |
|------|-------|---------|------------|-------|
| ui-format.js | 573 | 20 | 4 | Markdown + code blocks |
| ui-core.js | 1,644 | 6 | 3 | Chat list, messages, sidebar |
| pages.js | 382 | 8 | Templates | Page-level orchestration |
### Tier 3 — Feature modules (leaf nodes, mostly self-contained)
| File | Lines | Notes |
|------|-------|-------|
| channel-models.js | 425 | Clean object literal |
| chat-pane.js | 143 | Clean object literal |
| notifications.js | 390 | Clean singleton, IIFE candidate |
| model-selector.js | 191 | Clean object literal |
| file-tree.js | 295 | Clean object literal |
| tokens.js | 143 | 4 globals, minimal deps |
### Tier 4 — Heavy pages (most onclick handlers, refactor last)
| File | Lines | onclick | Notes |
|------|-------|---------|-------|
| ui-admin.js | 1,922 | 28 | Admin rendering, deeply coupled |
| projects-ui.js | 1,794 | 26 | 78 top-level globals (!), biggest mess |
| ui-core.js | 1,644 | 25 | Already in Tier 2, but onclick heavy |
| ui-settings.js | 1,011 | 8 | Settings rendering |
| settings-handlers.js | 1,064 | 0 | But 24 globals called by ui-settings |
| admin-handlers.js | 952 | 2 | IIFE but 40 internal functions |