Conversation
…fresh
- Store selected backend URL in localStorage when connection is established
- Check stored URL before configured backendUrl in auto-reconnect logic
- Ensures local launcher connections persist across page refreshes
- Prevents forced reconnection to remote backend when user chose local
Modified:
- connect/src/core.ts: Added setForVersion('ad4murl', url) in setUrl()
- connect/src/web.ts: Check getForVersion("ad4murl") before this.backendUrl in connectedCallback()
Add automatic detection and handling of embedded mode (iframe) in ad4m-connect to eliminate need for apps to implement their own postMessage logic. Changes: - core.ts: Detect embedded mode via window.self !== window.top check - core.ts: Add initializeEmbeddedMode() to handle postMessage protocol - core.ts: Listen for AD4M_CONFIG from parent and build client with received credentials - core.ts: Ignore localStorage tokens in embedded mode to prevent stale token issues - core.ts: Update connect() method to wait for postMessage in embedded mode - web.ts: Prevent UI from rendering when running in embedded mode Benefits: - Apps no longer need embedded vs standalone branching logic - Single unified getAd4mClient() API works in both modes - No modal shown in embedded mode - Prevents InvalidToken errors from stale localStorage in dev hot reloads - Follows established postMessage request-response pattern from WE launcher Modified files: - connect/src/core.ts - connect/src/web.ts
…emove redundant hooks BREAKING CHANGES: - Main export changed from './web.js' to './index.js' in package.json - getAd4mClient() now returns Promise<Ad4mClient> instead of sync client retrieval - Removed DOM-based getAd4mClient() from utils.ts (now re-exports from index.ts) ad4m-connect: - Created new index.ts entry point with simplified getAd4mClient(options) function - Handles both embedded (iframe) and standalone modes automatically - Returns Promise that resolves when authenticated - Fixed connect() to return Promise resolving on authentication in standalone mode - Added EventTarget inheritance to Ad4mConnect for standard DOM events - Persists localStorage fallback for port/url/token in standalone mode - Re-exports getAd4mClient from utils.ts for backwards compatibility ad4m-hooks: - Removed useClient hooks from React and Vue packages (redundant with centralized init) - Updated exports to remove useClient from both packages - Hooks-helpers getProfile reverted to old signature (will be updated when packages are published) Build: - Added build:index script to build new index.ts entry point - Updated main export path in package.json Note: The getProfile function in hooks-helpers still uses the old API for now to avoid breaking builds. This should be updated to require client parameter when publishing new versions.
- Change getAd4mConnect to return core synchronously and client as Promise - Enables event listener attachment before authentication completes - Update getAd4mClient to use non-async destructuring - Add TypeScript type declarations to package.json exports - Simplify JSDoc comments with clear Simple/Advanced API distinction - Add @example tags showing usage patterns for both APIs BREAKING CHANGE: getAd4mConnect now returns { core, client } synchronously instead of Promise<{ core, client }>. Apps using getAd4mConnect need to await the client property: const { core, client } = getAd4mConnect(options); const ad4mClient = await client;
- Add isEmbedded() utility function in utils.ts - Remove duplicate isEmbedded() method from Ad4mConnect class - Update core.ts to import and use isEmbedded() utility - Update web.ts to use standalone isEmbedded() function - Export isEmbedded from index.ts for external use Single source of truth for embedded mode detection across codebase.
- Import and use isEmbedded() utility instead of inline check - In standalone mode, return Promise that resolves on auth completion - Don't auto-call core.connect() - let UI handle local vs remote choice - Remove auth timeout (apps can implement their own if needed) This fixes the issue where standalone apps would auto-connect to local AD4M without showing the connection overview UI where users can choose between local executor or remote multi-user mode.
These props need to be set on the element for proper rendering of multi-user authentication flows and remote connection configuration.
Set display:none on element when stored token exists, only showing UI if authentication fails. Prevents brief flash of connection UI on page refresh when user is already authenticated.
- Created ConnectState class using Lit's ReactiveController pattern - Migrated 20+ @State() decorators to centralized state management - Added type-safe state interfaces (FormState, ErrorState, LoadingState, DetectionState) - Implemented computed properties (canSubmitMultiUser, showLocalOption, etc.) - Added type-safe update methods (updateForm, setError, setLoading, etc.) - Updated all component methods to use new state API - Improved code maintainability and state flow clarity - Removed duplicate changeCode method Benefits: - Single source of truth for all UI state - Better type safety with generic update methods - Clearer state flow with explicit update calls - Computed properties eliminate duplicate logic - Easier to test and reason about state changes
Centralize duplicated code patterns in both core and web components to improve maintainability and reduce code duplication by ~140 lines. Core (src/core.ts): - Add createApolloClient() helper to centralize Apollo configuration - Eliminates duplicate InMemoryCache and defaultOptions setup - Used in both buildClient() and buildTempClient() - Add notifyDisconnected() helper for common error/disconnection pattern - Replaces 6 instances of paired notifyConnectionChange + notifyAuthChange calls - Supports optional connection state parameter - Remove incorrect deprecation warning from connectMultiUser() - Method is still valid for programmatic multi-user authentication - Clarify documentation for UI-driven vs programmatic flows Web (src/web.ts): - Add initializeClient() to consolidate client initialization logic - Eliminates duplicate client creation in initializeWithoutUI() and connectedCallback() - Add setupEventListeners() to centralize event listener registration - Ensures consistent event handling across initialization paths - Add buildAppInfo() to create authentication info object - Replaces 3 duplicate object constructions - Add completeAuthentication() for post-auth flow - Consolidates token storage, client rebuild, and UI state updates - Replaces 3 nearly-identical blocks (~12 lines each) - Add clearCodeSubmitTimeout() for verification code timeout management - Replaces 3 duplicate timeout clearing patterns Benefits: - Reduced code duplication by ~140 lines total - Single source of truth for Apollo configuration - Consistent error handling and state notification - Easier to maintain and test isolated helper methods - Clear separation of concerns with well-named methods Breaking changes: None (all changes are internal refactoring)
- Reorganize component structure into views/, states/, and shared/ folders - Create unified ErrorState component replacing 4 separate error components - Convert 8 view components from functional templates to @CustomElement web components: * ConnectionOverview, RemoteConnection, ScanQRCode, RequestCapability * Settings, Start, Hosting, MultiUserAuth - Replace custom HTML/CSS with Flux UI components (j-flex, j-text, j-button, j-input, j-box, j-spinner) - Update web.ts to instantiate Lit components with CustomEvent-based communication - Remove 6 redundant components (AgentLocked, Disconnected, CouldNotMakeRequest, InvalidToken, Loading, MobileAppLogoButton) - Component count reduced from 19 to 12 (37% reduction) This migration improves code maintainability, reduces duplication, and provides consistent styling through the Flux UI design system while preserving all existing functionality.
- Reverted all 8 view components from Flux UI to native HTML elements - Replaced j-flex → div with flexbox - Replaced j-text → h1/h2/h3/p elements - Replaced j-button → button with CSS classes - Replaced j-box → div - Replaced j-input → input - Replaced j-spinner → custom CSS spinner animation - Created shared-styles.ts for centralized styling - Typography (h1, h2, h3, p, label) - Button variants (primary, secondary, ghost, link) - Form inputs with focus/disabled states - Common layout utilities (container, button-row, center) - Error and info boxes - Updated components to import and use shared styles - ConnectionOverview, RemoteConnection, ScanQRCode - RequestCapability, Settings, Start - Hosting, MultiUserAuth - Removed @coasys/flux-ui peer dependency from package.json Rationale: Shadow DOM boundaries prevented CSS variable inheritance for component-level theming. Native HTML approach provides full control over styling and is better suited for reusable package architecture.
- Rename mobileView() to settingsButton() for clarity - describes both purpose and behavior - Remove CSS variable definitions from shared-styles.ts to fix shadow DOM inheritance issues - Update shared-styles.ts to consume CSS vars from web.ts instead of defining its own - Add standardized CSS variables to web.ts :host block: - --ac-background-color for modal background - --ac-border-color-dark and --ac-border-color-light for consistent borders - Update modal styling to use new CSS variable system - Remove outdated TODO comments about localStorage (functionality works correctly) - Rename AppLogo import to Ad4mLogo for consistency This fixes the issue where each component was defining its own CSS variables due to :host in shared-styles, preventing proper theming from the parent component.
…ture - Change Ad4mConnectUI factory to inject core instance instead of creating internally - Make core property public to allow factory injection - Implement new ConnectionOptions and LocalAuthentication view components - Add reactive property updates with requestUpdate() for non-reactive core mutations - Set up event-driven communication between parent and child components using CustomEvents - Remove redundant checkAuth() call from buildClient connected callback - Add willUpdate() lifecycle for syncing parent props to child state - Implement port/URL change handlers with proper event bubbling (bubbles: true, composed: true) - Add 6-digit numeric security code input with monospace styling - Separate input state (newPort) from committed state (port) for better UX Breaking changes: - Ad4mConnectUI now requires Ad4mConnect instance as parameter - Core property is now public instead of private
- Add back button to LocalAuthentication view with ArrowLeftIcon component - Implement auto-submit when 6-digit security code is entered - Add verification error handling and display - Refactor input validation to block non-numeric characters immediately - Fix infinite reconnection loop by implementing requestedRestart flag - Add state indicators for success and error states - Consolidate CSS state styles in shared-styles - Clean up commented code and improve code organization - Update verifyCode to use connect() instead of buildClient() directly
…tence Major refactoring of AD4M Connect authentication system with improved architecture and UX: **Core Features Added:** - Complete multi-user authentication flow with smart detection - Email submission with server-side routing - Passwordless verification via email code (for existing users) - Password-based login (for returning users requiring password) - Account creation flow (for new users) - Embedded mode support with postMessage protocol - Parent-child iframe communication - AD4M config injection from host application - localStorage persistence for connection state - Port, URL, and token storage - Auto-reconnect with stored credentials - UI visibility management based on auth state **Components:** - New RemoteAuthentication component for multi-user flows - Enhanced ConnectionOptions with remote node validation - Improved LocalAuthentication with auto-submit on 6-digit code entry - Consolidated shared styles with back button and state indicators **Architecture Improvements:** - Business logic separated into core.ts - View orchestration handled by web.ts - Event-driven communication between components - MutationObserver pattern for reactive UI visibility - Proper error handling with try-catch-finally blocks - requestedRestart flag to prevent reconnection loops **Bug Fixes:** - Fixed infinite reconnection loop after authentication - Input validation for security codes (numeric only) - Auth listener setup before connection attempt - Token expiry handling with automatic UI display - API validation with proper error propagation - Clear error messages for each authentication step **Code Quality:** - Reduced codebase from 2000+ to ~1000 lines - TypeScript strict mode compliance - Consistent error handling patterns - Improved state management across components
…tency Refactored AD4M Connect codebase for better maintainability and user experience: **Code Organization (core.ts):** - Reordered methods by logical grouping (connection flow, embedded mode, local auth, remote auth) - Moved private helper methods (notifyX) to bottom of class - Grouped related methods together (buildClient with createApolloClient) - Added section comments for better code navigation - Simplified constructor logic (unified embedded/standalone initialization) **Loading State Improvements (web.ts):** - Added remoteAuthLoading state to verifyEmailCode, passwordLogin, and createAccount - Ensured consistent loading feedback across all remote authentication methods - Added finally blocks to guarantee loading state cleanup **Other Improvements:** - Renamed notify function parameters from `val` to `value` for consistency - Removed unnecessary code comments and whitespace - Maintained identical functionality - pure refactoring with no behavior changes No functional changes - purely organizational improvements for better code readability and maintainability.
Improvements to ConnectionOptions: - Add keyboard support (Enter key) for port and remote URL inputs - Refactor port refresh logic into dedicated method Improvements to RemoteAuthentication: - Add email validation with real-time button state - Add password length validation for login/signup - Enhance visual feedback with success states - Improve styling consistency and spacing - Clear auth state when navigating back - Remove stale comments Core improvements: - Refactor temp client creation into withTempClient helper with proper resource cleanup - Extract buildAppInfo helper to reduce duplication - Simplify remote auth methods using new helper - Improve error handling throughout Minor cleanup: - Better state organization with comments in web.ts - Remove extra whitespace in utils.ts
- Remove @coasys/ad4m-connect dependency from helpers and react hooks packages - Update getProfile to accept AgentClient parameter instead of internally fetching client - Update useAgent hook to pass client to getProfile calls - Fix import organization and remove unused imports across hooks packages - Update workspace dependencies to use workspace:* protocol - Add @types/uuid as devDependency to helpers package - Clean up circular import references in connect utils - Update comment from "TODO: remove" to "Re-export isEmbedded utility" This removes the circular dependency between ad4m-connect and hooks packages, allowing views to use hooks without bundling ad4m-connect multiple times.
📝 WalkthroughWalkthroughReworks Ad4m connect into an embedded, event-driven core with postMessage AD4M_CONFIG intake and local persistence; replaces legacy UI with LitElement views and shared styles; removes many legacy UI components and Electron bridge; updates hooks (removing useClient/useEntries) and helper APIs (getProfile signature); updates package manifests and build scripts. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant Parent as ParentWindow
participant Core as Ad4mConnect
participant Client as Ad4mClient
participant UI as Ad4mConnectUI
Note over Parent,Core: Embedded host posts AD4M_CONFIG
Parent->>Core: postMessage(AD4M_CONFIG {url, port, token})
Core->>Core: validate origin & persist config (setLocal)
Core->>Client: buildClient() (ws/graphql)
Client-->>Core: client ready
Core->>UI: dispatch authstatechange / connection events
UI->>Core: user triggers auth flows (submitEmail / verifyEmailCode / login / createAccount)
Core->>Core: withTempClient(...) for verification
Core->>Client: perform auth action
Client-->>Core: auth result / token
Core->>Core: persist token (setLocal) and resolve connect()
Estimated code review effort🎯 5 (Critical) | ⏱️ ~120 minutes Poem
🚥 Pre-merge checks | ✅ 1 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 13
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (6)
ad4m-hooks/vue/src/usePerspectives.ts (1)
37-42: Pre-existing bug:removeListenerused instead ofaddListener, and wrong callback array referenced.This appears to be a copy-paste error from the "link-added" handler above:
removeListenershould likely beaddListenerto register for "link-removed" eventsonAddedLinkCbsshould beonRemovedLinkCbsThis is not introduced by this PR, but worth fixing while you're in this file.
Proposed fix
- p.removeListener("link-removed", (link) => { - onAddedLinkCbs.value.forEach((cb) => { + p.addListener("link-removed", (link) => { + onRemovedLinkCbs.value.forEach((cb) => { cb(p, link); }); return null; });ad4m-hooks/vue/src/useMe.ts (1)
27-39: Inconsistent reference betweenagent.valueandnewAgentin watch handler.The outer condition checks
agent.value?.perspectivewhile the inner code extracts fromnewAgent?.perspective. SincenewAgentis the callback parameter representing the new value, the outer condition should also usenewAgentfor consistency:Proposed fix
watch( () => agent.value, (newAgent) => { - if (agent.value?.perspective) { + if (newAgent?.perspective) { const perspective = newAgent?.perspective; if (!perspective) return; profile.value = formatter(perspective.links); } else { profile.value = null; } }, { immediate: true } )With this change, the inner null-check on line 32 becomes redundant and can be removed since the outer condition already guards for it.
ad4m-hooks/react/src/useAgent.ts (2)
27-41: Missing null check and error handling forgetProfilecall.The
getProfilefunction can returnundefinedif no agent perspective is found (per the function signature ingetProfile.ts). Accessingprofile.perspective.linkswithout a null check will throw a runtime error.Proposed fix with null safety and error handling
const getData = useCallback(() => { if (didRef) { if (props.formatter) { - getProfile(props.client, didRef).then(profile => setProfile(props.formatter(profile.perspective.links))) + getProfile(props.client, didRef) + .then(profile => { + if (profile?.perspective?.links) { + setProfile(props.formatter(profile.perspective.links)); + } + }) + .catch((error) => setError(error.toString())); } props.client
41-41: Incomplete dependency array inuseCallback.The
getDatacallback referencesdidRef,props.formatter,props.client, andmutate, but the dependency array only includescacheKey. This can cause stale closures if these values change.Proposed fix
- }, [cacheKey]); + }, [cacheKey, didRef, props.client, props.formatter, mutate]);ad4m-hooks/helpers/src/getProfile.ts (1)
14-18: Non-null assertions may cause runtime errors.Line 15 uses
agentPerspective!.perspective!.linkswith non-null assertions, butperspectivecould benullorundefinedbased on theAgenttype from thebyDIDAPI. This would cause a runtime error.Consider adding a null check for
perspectivebefore accessinglinks.Suggested fix
if (agentPerspective) { - const links = agentPerspective!.perspective!.links; + const links = agentPerspective.perspective?.links; + if (!links) return agentPerspective; if (formatter) { return formatter(links); } return agentPerspective }ad4m-hooks/react/src/useMe.ts (1)
58-77: Memory leak: Listeners are never removed.The
addAgentStatusChangedListenerandaddUpdatedListenersubscriptions are set up but never cleaned up. The TODO on line 75 acknowledges this, but this will cause memory leaks and stale callback invocations when the component unmounts or whenagentchanges.Additionally, the dependency array
[agent]is incomplete—dataandmutateare used inside the callback but not listed, which can cause stale closure issues.If the AD4M client API supports removing listeners, the cleanup should be added. Would you like me to help implement the cleanup once the listener removal API is available?
#!/bin/bash # Description: Check if AgentClient has methods to remove listeners # Expected: Find removeListener or unsubscribe methods ast-grep --pattern $'class AgentClient { $$$ remove$_Listener($_) { $$$ } $$$ }'
🤖 Fix all issues with AI agents
In `@ad4m-hooks/helpers/src/getProfile.ts`:
- Around line 4-7: Remove the unused exported interface Payload by deleting the
declaration "export interface Payload { url: string; perspectiveUuid: string; }"
from the file; ensure no other references to the symbol Payload remain in the
codebase (if found, either import/use it or remove those references) and run a
quick build/typecheck to confirm no breakages.
In `@ad4m-hooks/react/src/useEntries.ts`:
- Around line 1-3: Delete the duplicate module useEntries.ts (which currently
mirrors useMe.ts) to avoid redundancy; remove the file and any imports/exports
referencing useEntries.ts so only useMe.ts remains exported (update index.ts if
it re-exports useEntries) and ensure no code references functions or hooks from
useEntries.ts remain in the codebase.
In `@connect/package.json`:
- Around line 13-17: The root package export object currently specifies "types",
"import", and "default" but omits an explicit "require" condition; update the
root export in package.json (the object containing "types", "import", "default")
to add a "require" field that points to the CJS build (e.g., "./dist/index.cjs"
or the project's CJS entry) so it matches the other exports that explicitly
define "require" and ensures consistent CJS resolution.
In `@connect/scripts/esbuild_index.js`:
- Line 11: Replace the deprecated boolean watch option with the esbuild context
API: create a build context via esbuild.context(...) using the existing build
options (entryPoints, bundle, format, minify, sourcemap, outfile), then if
process.env.NODE_ENV === "dev" call await ctx.watch() to enable watching,
otherwise call await ctx.dispose() to run a one-time build; update references in
connect/scripts/esbuild_index.js where the watch property was used and ensure
ctx is awaited and disposed appropriately.
In `@connect/src/components/views/ConnectionOptions.ts`:
- Around line 10-11: The port property is declared with `@property`({ type: String
}) which coerces attributes to strings and can break numeric logic; update the
decorator on the port field (symbol: port in the ConnectionOptions
component/class) to use `@property`({ type: Number }) so attribute values are
converted to numbers, and ensure the TypeScript type remains number (port:
number) and any code that reads this property expects a numeric value.
- Around line 160-167: The input handler is assigning NaN into the component
state (this.newPort) when the field is empty/invalid; update the handler in the
ConnectionOptions component so you parse the value into a const (e.g., const
parsed = parseInt(input.value, 10)), check Number.isNaN(parsed) and only assign
this.newPort = parsed when valid, otherwise set this.newPort to undefined/null
(or leave previous value) and update the .value binding for the input to render
an empty string when newPort is undefined (e.g., .value=${this.newPort != null ?
this.newPort.toString() : ''}) to prevent "NaN" from appearing and avoid
ws://localhost:NaN.
In `@connect/src/components/views/RemoteAuthentication.ts`:
- Around line 75-89: The dispatched events in emailLogin, verifyEmailCode,
passwordLogin, and createAccount use this.email raw (which may include
trailing/leading spaces); normalize by trimming once (e.g., const email =
this.email?.trim()) before building event detail and use that trimmed email in
the CustomEvent payloads (keep other fields like password/password code
unchanged) and ensure any call-sites relying on isValidEmail remain compatible.
In `@connect/src/core.ts`:
- Around line 250-254: In requestCapability(invalidateToken = false) the code
sets this.token = null but doesn't clear persisted storage and uses null (not a
string); change it to set this.token = '' (empty string), call
localStorage.removeItem('token') (or localStorage.setItem('token', '') if your
persistence expects a key) to clear the persisted token, and then call
this.notifyConfigChange("token", this.token) so in-memory and persisted state
stay consistent; locate this in the requestCapability method and update the
token handling there.
- Around line 199-231: Guard the AD4M_CONFIG message handler by verifying the
sender: inside the window.addEventListener('message', ...) callback for
AD4M_CONFIG, first check that event.source === window.parent and that
event.origin is present in the allowlist provided via the instance options
(reference Ad4mConnectOptions or this.options.allowedOrigins); if either check
fails, return early and optionally log/ignore the message. Only after passing
these validations proceed to extract { port, token }, set
this.port/this.token/this.url, persist to localStorage, buildClient() and
checkAuth(); if the message is rejected and an embeddedReject exists, call it
with an appropriate error to surface the failure.
In `@connect/src/styles/shared-styles.ts`:
- Around line 55-121: The stylesheet currently removes the default focus
indicator with "outline: none" on the base button rule; add an accessible
replacement by adding a :focus-visible (and :focus-visible:not(:disabled)) rule
that provides a visible focus indicator (e.g., a clear outline or high-contrast
box-shadow and outline-offset) for all button variants (base button, and ensure
it applies to button.primary, button.secondary, button.ghost, button.link); keep
"outline: none" if you prefer but ensure the :focus-visible rule visually
indicates focus and matches the existing hover/disabled logic so keyboard users
regain a clear focus state.
In `@connect/src/web.ts`:
- Around line 152-158: Wrap the await this.core.connect() call in
connectLocalNode in a try/catch to prevent unhandled promise rejections and
surface UI errors the same way connectedCallback does; on catch, run the same
error-handling path used by connectedCallback (e.g., log the error and update
this.currentView to the error UI or invoke the connectedCallback error handler)
so failures are displayed to the user and not left unhandled.
- Around line 87-92: Replace the non-semantic clickable <div> that renders the
settings control (the element using class "settings-button") with a native
<button type="button">, add an accessible aria-label (e.g., aria-label="Open
settings"), and remove any role="button" or manual keyboard handlers that
duplicate native behavior; keep the existing onClick handler. Also update the
.settings-button CSS to include button-reset styles (e.g., appearance: none;
border: none; background: transparent; padding: 0; cursor: pointer;) while
preserving the existing positioning and color so visual layout is unchanged.
- Around line 101-103: The MutationObserver watching attributes (created in the
factory function) never sees Lit `@state`() changes like modalOpen, so remove/stop
using that observer and instead use the existing authstatechange event listener
to reveal the modal: in the authstatechange handler set this.modalOpen = true
(and set this.currentView = "connection-options" or the appropriate view) when
auto‑connect fails, and ensure the component listens for and handles
authstatechange on the element (or re-dispatches it from the auth logic) so the
modal toggles via the event rather than relying on attribute mutations; keep
modalOpen as `@state`() and do not try to detect it via MutationObserver.
🧹 Nitpick comments (12)
ad4m-hooks/helpers/src/getProfile.ts (2)
1-2: Consolidate imports from the same module.Both imports are from
@coasys/ad4mand can be combined into a single import statement for cleaner code.Suggested fix
-import { AgentClient } from "@coasys/ad4m"; -import { LinkExpression } from "@coasys/ad4m"; +import { AgentClient, LinkExpression } from "@coasys/ad4m";
9-9: Tighten return type and handle the implicitundefinedcase.The return type
Promise<T | any>undermines the generic's purpose sinceanyabsorbs all types. Additionally, whenagentPerspectiveis falsy, the function returnsundefinedimplicitly, but the signature doesn't reflect this.Consider:
- Replacing
anywith the actual return type (likelyAgentor a perspective type)- Adding
| undefinedto the return type to indicate the function may not return a valuead4m-hooks/react/src/useMe.ts (1)
83-88: Unnecessary conditional check forformatter.The
formatterparameter is required in the function signature (line 19), so theif (formatter)check on line 84 is always truthy and can be removed.Suggested simplification
if (perspective) { - if (formatter) { - profile = formatter(perspective.links) - } - + profile = formatter(perspective.links); }connect/scripts/esbuild_index.js (1)
13-13: Consider logging the build error before exiting.The
.catch(() => process.exit(1))swallows errors silently, making debugging difficult when builds fail.🔧 Suggested improvement
-.catch(() => process.exit(1)); +.catch((error) => { + console.error("Build failed:", error); + process.exit(1); +});connect/src/components/icons/LocalIcon.ts (1)
5-8: Consider adding explicitstroke-widthfor consistent rendering.The outer circle relies on the browser's default stroke-width, which may vary. Adding an explicit value ensures consistent appearance.
🔧 Suggested improvement
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor"> - <circle cx="12" cy="12" r="10"></circle> + <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> + <circle cx="12" cy="12" r="10"></circle>connect/src/components/icons/RemoteIcon.ts (1)
5-9: Add explicitstroke-widthfor consistency with other icons.Similar to
LocalIcon, this SVG lacks an explicit stroke-width. For visual consistency across the icon set, consider addingstroke-width="2".🔧 Suggested improvement
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor"> + <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">connect/src/components/icons/RefreshIcon.ts (1)
5-8: Consider removing hardcodedwidthandheightfor flexible sizing.Unlike the other icon components that rely solely on
viewBox, this icon has hardcodedwidth="16" height="16". This can interfere with CSS-based resizing. Let the parent container control dimensions.🔧 Suggested improvement
- <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-arrow-clockwise" viewBox="0 0 16 16"> + <svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 16 16">connect/src/components/views/LocalAuthentication.ts (1)
8-10: Aligncapabilitiestype withcapSentenceinput
capSentenceexpects a capability object (not a plain string). TypingcapabilitiesasCapabilityInput[]keeps TS aligned with runtime usage and avoids accidental misuse.🔧 Proposed fix
-import { capSentence } from "@coasys/ad4m"; +import { capSentence, CapabilityInput } from "@coasys/ad4m"; @@ - `@property`({ type: Array }) capabilities: string[] = []; + `@property`({ type: Array }) capabilities: CapabilityInput[] = [];Also applies to: 162-164
connect/src/components/views/ConnectionOptions.ts (1)
240-262: Remove commented-out UI blocksThe commented sections at the end add noise and tend to rot. Consider deleting or moving them to an issue/ADR.
connect/src/utils.ts (1)
31-31: Type theconnectWebSocketAPIThe
urlparameter is implicitany. Adding explicit types improves TS strictness and API clarity.🔧 Proposed fix
-export async function connectWebSocket(url, timeout = 10000) { +export async function connectWebSocket(url: string, timeout = 10000): Promise<WebSocket> {connect/src/core.ts (1)
76-88: Avoid async Promise executor inconnect()
new Promise(async ...)is discouraged and can mask errors. Sinceconnect()is alreadyasync, a direct try/catch is clearer and matches idiomatic JS.🔧 Proposed fix
- return new Promise(async (resolve, reject) => { - try { - await connectWebSocket(this.url); - setLocal("ad4m-url", this.url); - this.ad4mClient = await this.buildClient(); - await this.checkAuth(); - resolve(this.ad4mClient); - } catch (error) { - console.error('[Ad4m Connect] Connection failed:', error); - this.notifyConnectionChange("error"); - reject(error); - } - }); + try { + await connectWebSocket(this.url); + setLocal("ad4m-url", this.url); + this.ad4mClient = await this.buildClient(); + await this.checkAuth(); + return this.ad4mClient; + } catch (error) { + console.error('[Ad4m Connect] Connection failed:', error); + this.notifyConnectionChange("error"); + throw error; + }connect/src/components/icons/ArrowLeftRightIcon.ts (1)
5-6: Consider marking the SVG as decorative (or labeling it).
If this icon is purely decorative, addaria-hidden="true"andfocusable="false"to prevent screen readers from announcing it. If it conveys meaning, consider exposing anaria-labelprop.♿️ Suggested tweak
- <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-arrow-left-right" viewBox="0 0 16 16"> + <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-arrow-left-right" viewBox="0 0 16 16" aria-hidden="true" focusable="false">
- Remove unused Payload interface from getProfile.ts - Delete duplicate useEntries.ts file - Add CJS support to package.json exports with require field - Update esbuild script to use modern context API for dev/prod builds - Fix ConnectionOptions port property type (String → Number) - Add NaN validation to port input with Number.isFinite check - Trim email addresses in all RemoteAuthentication event dispatchers - Add postMessage origin validation for embedded mode security - Verify event source is parent window - Check allowedOrigins array when configured - Fix token invalidation to use empty string and clear localStorage - Add :focus-visible indicators for all button variants (accessibility) - Replace non-semantic div with semantic button for settings control - Add error handling to connectLocalNode method - Remove broken MutationObserver watching @State() changes - Update visibility handling via authstatechange events - Add allowedOrigins security option to Ad4mConnectOptions type - Update pnpm-lock.yaml with workspace links for monorepo consistency
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Fix all issues with AI agents
In `@connect/src/components/views/RemoteAuthentication.ts`:
- Around line 70-73: The back() method resets the view but leaves sensitive
fields populated; update the RemoteAuthentication class's back() method to also
clear local sensitive state by setting password and emailSecurityCode to
null/empty (or their initial values) before or after setting remoteAuthState to
null, ensuring both fields are reset when the user navigates back so the
password step does not repopulate stale data.
In `@connect/src/core.ts`:
- Around line 385-395: The notifyAuthChange method only resolves the embedded
connect promise on "authenticated"; update it to also reject immediately when
auth transitions to failing states by calling embeddedReject with a clear Error
(e.g., message indicating "unauthenticated" or "locked") and then clearing
embeddedResolve and embeddedReject; modify notifyAuthChange to check for value
=== "unauthenticated" || value === "locked" (or other failing AuthStates) and if
embeddedReject exists invoke it and clear both fields so embedded connect() does
not wait for the 30s timeout; keep the existing authenticated-resolution
behavior intact.
🧹 Nitpick comments (2)
connect/src/core.ts (1)
76-89: Avoid async Promise executors inconnect()Line 76–89 uses
new Promise(async ...), which is a common JS anti-pattern and flagged by lint. Sinceconnect()is alreadyasync, you can return directly and lettry/catchhandle errors.♻️ Suggested refactor
- return new Promise(async (resolve, reject) => { - try { - await connectWebSocket(this.url); - setLocal("ad4m-url", this.url); - this.ad4mClient = await this.buildClient(); - await this.checkAuth(); - resolve(this.ad4mClient); - } catch (error) { - console.error('[Ad4m Connect] Connection failed:', error); - this.notifyConnectionChange("error"); - reject(error); - } - }); + try { + await connectWebSocket(this.url); + setLocal("ad4m-url", this.url); + this.ad4mClient = await this.buildClient(); + await this.checkAuth(); + return this.ad4mClient; + } catch (error) { + console.error('[Ad4m Connect] Connection failed:', error); + this.notifyConnectionChange("error"); + throw error; + }connect/src/web.ts (1)
11-11: Avoid deep import ofVerificationRequestResultLine 11 imports from
@coasys/ad4m/lib/src/..., which is brittle against package export changes. Prefer the public entry (as used inRemoteAuthentication.ts) if available.♻️ Suggested change
-import { VerificationRequestResult } from "@coasys/ad4m/lib/src/runtime/RuntimeResolver"; +import { VerificationRequestResult } from "@coasys/ad4m";
…lows Security improvements: - Clear sensitive fields (password, emailSecurityCode) in RemoteAuthentication.back() to prevent data persistence when navigating back - Add immediate promise rejection in embedded mode for failed auth states (unauthenticated/locked) to avoid 30-second timeout wait Authentication flow fixes: - Reject embedded connect() promise immediately on auth failure with clear error messages - Clean up promise handlers (embeddedResolve/embeddedReject) after rejection - Preserve existing authenticated-resolution behavior in embedded mode
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Fix all issues with AI agents
In `@connect/src/components/views/RemoteAuthentication.ts`:
- Around line 94-106: The onEmailSecurityCodeChange handler should avoid
triggering verifyEmailCode while a verification request is already in flight:
add a guard that checks the component state/flag remoteAuthLoading and only
calls verifyEmailCode() when remoteAuthLoading is false (e.g., if
(this.emailSecurityCode.length === 6 && !this.remoteAuthLoading)
this.verifyEmailCode()); update any related logic around emailSecurityCode and
remoteAuthLoading to ensure the flag is set before/after the async
verifyEmailCode call so duplicates are prevented.
In `@connect/src/core.ts`:
- Around line 116-128: The closed handler can get an already-authenticated but
dead embedded client because this.connect() may return the existing ad4mClient
without verifying the underlying WS is alive; change the logic so when the
CloseEvent indicates an unexpected close (event.wasClean false and
!this.requestedRestart) you verify the returned/client's socket liveness before
accepting it: either update connect(clientOptions) to accept a
forceFresh/forceReconnect flag and call connect({forceFresh:true}) from the
closed handler, or after getting a client check a liveness property/method
(e.g., client.socket.readyState or client.isConnected()) and if it's not open,
discard it and create a new client (rebuild) so the closed WS is replaced; keep
notifyConnectionChange("error") behavior if reconnect fails.
- Around line 43-89: The connect() method currently uses a new Promise with an
async executor for the standalone path which triggers the noAsyncPromiseExecutor
lint; refactor the standalone branch to use the async function's natural flow:
remove new Promise(async (resolve, reject) => { ... }), perform the operations
sequentially (await connectWebSocket(this.url); setLocal("ad4m-url", this.url);
this.ad4mClient = await this.buildClient(); await this.checkAuth();) inside a
try block and return this.ad4mClient, and in the catch block call
this.notifyConnectionChange("error"), console.error the error and rethrow it;
keep the existing embedded-mode Promise logic (with
embeddedResolve/embeddedReject and ad4mClient/authState check) unchanged.
🧹 Nitpick comments (1)
connect/src/components/views/RemoteAuthentication.ts (1)
135-139: Add autocomplete hints to improve UX and password manager support.✨ Suggested additions
<input type="email" + autocomplete="email" placeholder="email@example.com" .value=${this.email || ""} @@ <input type="text" maxlength="6" inputmode="numeric" pattern="[0-9]{6}" + autocomplete="one-time-code" placeholder="000000" .value=${this.emailSecurityCode || ""} @@ <input type="password" + autocomplete="current-password" placeholder="Password..." .value=${this.password || ""} @@ <input type="password" + autocomplete="new-password" placeholder="Password..." .value=${this.password || ""}Also applies to: 181-187, 215-218, 255-258
…nditions Connection improvements: - Refactor connect() standalone path to use natural async/await flow instead of async promise executor (fixes noAsyncPromiseExecutor lint) - Fix WebSocket reconnection to force fresh client rebuild via buildClient() instead of calling connect() which could return stale embedded client - Add proper error handling and logging to reconnection attempts Race condition fixes: - Add guard to prevent duplicate email verification requests when code is auto-submitted (check !remoteAuthLoading before calling verifyEmailCode) - Prevents multiple concurrent verification attempts during rapid input Code quality: - Remove Promise constructor anti-pattern in standalone connect() - Use direct return/throw instead of resolve/reject for cleaner flow - Preserve all existing embedded mode Promise logic unchanged
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@connect/src/core.ts`:
- Line 23: The activeSocket property is declared as type WebSocket but
initialized to null; update its declaration so its type explicitly allows null
(make activeSocket nullable) to match its initialization and other nullable
properties (e.g., ad4mClient?, wsClient?), then run type checks; locate the
activeSocket symbol in the class in connect/src/core.ts and change its type
annotation to permit null and keep the initializer as null.
🧹 Nitpick comments (5)
connect/src/components/views/RemoteAuthentication.ts (3)
32-34: Consider responsive widths for smaller viewports.Fixed widths (
width: 280pxandwidth: 194px) may cause horizontal overflow on narrow screens. If this component is expected to render in mobile contexts, consider usingmax-widthwith a percentage fallback.Also applies to: 48-56
217-226: Consider addingautocompleteattributes to password inputs.Adding
autocomplete="current-password"(line 218) andautocomplete="new-password"(line 258) helps password managers distinguish between login and signup flows, improving UX and accessibility.♻️ Suggested enhancement
<input type="password" + autocomplete="current-password" placeholder="Password..." .value=${this.password || ""}<input type="password" + autocomplete="new-password" placeholder="Password..." .value=${this.password || ""}Also applies to: 257-266
137-145: Consider addingautocomplete="email"for better autofill support.This helps browsers and password managers auto-populate the email field.
♻️ Suggested enhancement
<input type="email" + autocomplete="email" placeholder="email@example.com"connect/src/core.ts (2)
154-165: Ensure Apollo client is stopped inwithTempClient.
wsClient.dispose()alone can leave Apollo watchers hanging. Stop the Apollo client infinally.♻️ Proposed refactor
- } finally { - wsClient.dispose(); - } + } finally { + apolloClient.stop(); + wsClient.dispose(); + }
196-258: Prefer scopedtargetOriginwhen requesting config.If
allowedOriginsis provided, avoid broadcasting to"*"and send to those origins instead.🔒 Proposed refinement
- window.parent.postMessage({ type: 'REQUEST_AD4M_CONFIG' }, '*'); + const targets = (this.options.allowedOrigins && this.options.allowedOrigins.length > 0) + ? this.options.allowedOrigins + : ['*']; + for (const origin of targets) { + window.parent.postMessage({ type: 'REQUEST_AD4M_CONFIG' }, origin); + }
Change activeSocket property type from `WebSocket = null` to `WebSocket | null = null` to properly reflect its nullable nature and align with TypeScript strict null checking. This matches the pattern used for other optional properties (ad4mClient?, wsClient?, apolloClient?) and fixes the type mismatch error.
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Fix all issues with AI agents
In `@connect/src/core.ts`:
- Around line 290-301: verifyLocalAd4mCode currently uses this.requestId! which
can be undefined if requestCapability wasn't called; add a guard at the start of
verifyLocalAd4mCode to check that this.requestId is present (and not use the
non-null assertion), return false or throw a clear error and log a message if
it's missing, and only call ad4mClient.agent.generateJwt when requestId exists;
reference the verifyLocalAd4mCode method, the requestId property, and the
requestCapability flow to ensure callers must obtain a requestId before
verifying.
- Around line 196-258: The initializeEmbeddedMode listener currently uses
event.data.port and event.data.token directly; validate and normalize these
before persisting or using them: inside the AD4M_CONFIG handler (in
initializeEmbeddedMode) ensure event.data.port is present and can be parsed to a
positive integer (e.g., parseInt and isFinite check) and that token, if present,
is a string (or treat missing token as undefined), and if validation fails call
this.embeddedReject(new Error(...)) and return; only call
setLocal('ad4m-port'...), setLocal('ad4m-token'...), setLocal('ad4m-url'...) and
set this.port/this.token/this.url after successful normalization, then proceed
to await this.buildClient() and this.checkAuth(); also avoid storing the literal
string "undefined" by gating setLocal calls on validated values.
- Add validation and normalization for AD4M_CONFIG port and token fields - Validate port is present and can be parsed to positive integer - Normalize token as string or empty string, avoid persisting undefined - Reject embedded promise with clear errors on validation failure - Add guard to verifyLocalAd4mCode requiring requestId before use - Remove non-null assertion operator for type safety - Return false with descriptive error if requestCapability() not called - Extract embedded promise handlers into reusable helper methods - Add resolveEmbedded() and rejectEmbedded() to eliminate repetition - Consolidate cleanup logic (clear both resolve/reject handlers) - Simplify 6 call sites throughout initializeEmbeddedMode flow
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@connect/src/core.ts`:
- Around line 196-272: In initializeEmbeddedMode(): when processing AD4M_CONFIG,
detect when rawToken is absent or invalid (normalizedToken === '') and
explicitly clear any persisted token instead of leaving a stale value; replace
or augment the current conditional that only writes setLocal('ad4m-token',
normalizedToken) with logic that removes the stored token (e.g., call
removeLocal('ad4m-token') or delete the key) when normalizedToken is empty, and
only setLocal when a non-empty normalizedToken exists so reloads cannot reuse
stale credentials.
When processing AD4M_CONFIG with absent or invalid token, explicitly remove any persisted token from localStorage to prevent stale credentials from being reused on reload. Previously only stored valid tokens but left old values in place when normalizedToken was empty.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@connect/src/core.ts`:
- Around line 43-73: The embedded connect() path currently only resolves
immediately when this.ad4mClient exists and this.authState === 'authenticated',
but if this.ad4mClient exists and auth has already failed (this.authState is
'unauthenticated' or 'locked') the promise stays waiting until timeout; fix by
adding an early rejection branch: after the existing check for this.ad4mClient
&& this.authState === 'authenticated', add an else-if that checks
this.ad4mClient && this.authState !== 'authenticated' and immediately
clearTimeout(timeout) and invoke this.embeddedReject(new Error(`Embedded auth
state: ${this.authState}`)) (or call reject(...) directly) so the connect()
promise rejects immediately when prior auth failed instead of waiting 30s;
reference symbols: connect(), this.ad4mClient, this.authState,
this.embeddedReject, timeout.
…lure In embedded mode, the connect() promise now checks if authentication has already failed before connect() was called. Previously, if the client existed but authState was 'locked' or 'unauthenticated', the promise would wait for the full 30-second timeout. Now it rejects immediately with a descriptive error, improving error feedback and avoiding unnecessary delays when auth has already failed.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@connect/src/core.ts`:
- Around line 311-327: The four auth methods (verifyLocalAd4mCode,
verifyEmailCode, loginWithPassword, createAccount) obtain a token then call
this.connect() but never validate the token, which causes connect() to hang in
embedded mode; update each method to call await this.checkAuth() immediately
after setting this.token (and persisting it) and before calling await
this.connect() so checkAuth() validates the token and updates authState to
'authenticated' prior to connect().
♻️ Duplicate comments (1)
connect/src/core.ts (1)
72-78: Missingreturnafter early rejection causes fallthrough to standalone mode.When auth already failed in embedded mode (lines 72-76), the promise is rejected but execution continues past the
ifblock to line 78, where it would fall through to the standalone connection logic. Add areturnstatement after the early rejection.🐛 Proposed fix
} else if (this.ad4mClient && this.authState !== 'authenticated') { // Auth already failed before connect() was called clearTimeout(timeout); reject(new Error(`Embedded auth state: ${this.authState}`)); + return; } }); }
🧹 Nitpick comments (2)
connect/src/core.ts (2)
101-102: Minor type inconsistency: assigningnullto optional properties.Properties
wsClientandapolloClientare declared as optional (wsClient?: WSClient), meaning their type isWSClient | undefined. Assigningnulltechnically introduces a type mismatch. While this works at runtime, consider usingundefinedfor consistency.♻️ Suggested fix
this.wsClient.dispose(); this.apolloClient.stop(); - this.wsClient = null; - this.apolloClient = null; + this.wsClient = undefined; + this.apolloClient = undefined; }
186-197: Add type guard before accessingerror.message.In strict TypeScript, the
catchparameter is typed asunknown. Accessingerror.messagedirectly (line 189) could fail if the caught value isn't anErrorobject. Consider adding a type guard.♻️ Suggested fix
} catch (error) { console.error('[Ad4m Connect] Authentication check failed:', error); const lockedMessage = "Cannot extractByTags from a ciphered wallet. You must unlock first."; - if (error.message === lockedMessage) { + const errorMessage = error instanceof Error ? error.message : String(error); + if (errorMessage === lockedMessage) { // TODO: isLocked throws an error, should just return a boolean. Temp fix this.notifyAuthChange("locked"); return true;
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@connect/package.json`:
- Around line 5-6: The package.json's "main" field points to dist/web.js (a Lit
web component) while "types" points to dist/index.d.ts (which exports functions
such as getAd4mConnect and getAd4mClient), causing a runtime/typings mismatch;
change the "main" entry to point to the CommonJS/ES module entry that matches
the type definitions (e.g., dist/index.js) so that runtime exports align with
the type exports (update "main": "dist/index.js" and verify dist/index.{js,d.ts}
export getAd4mConnect and getAd4mClient).
🧹 Nitpick comments (2)
connect/package.json (2)
40-41: Redundanttscinvocations in build pipeline.The build script runs
tscfive times: once in each ofbuild:index,build:core,build:web,build:utils, and then again at the end. Since they all use the sametsconfig.json, this is wasteful. Consider runningtsconce before the esbuild steps.♻️ Suggested optimization
- "build": "pnpm run build:index && pnpm run build:core && pnpm run build:web && pnpm run build:utils && tsc", - "build:index": "tsc --project tsconfig.json && node scripts/esbuild_index.js", - "build:core": "tsc --project tsconfig.json && node scripts/esbuild.js", - "build:web": "tsc --project tsconfig.json && node scripts/esbuild_web.js", - "build:utils": "tsc --project tsconfig.json && node scripts/esbuild_utils.js" + "build": "tsc --project tsconfig.json && pnpm run build:index && pnpm run build:core && pnpm run build:web && pnpm run build:utils", + "build:index": "node scripts/esbuild_index.js", + "build:core": "node scripts/esbuild.js", + "build:web": "node scripts/esbuild_web.js", + "build:utils": "node scripts/esbuild_utils.js"
62-68: Build-time dependencies independenciesinstead ofdevDependencies.
esbuild-plugin-copyandesbuild-plugin-replaceare build-time tools used only by the esbuild scripts. They should be indevDependenciesto avoid bloating the install for consumers.♻️ Suggested fix
Move to
devDependencies:"devDependencies": { "@coasys/ad4m": "workspace:*", "@apollo/client": "3.7.10", "@types/node": "^16.11.11", "esbuild": "^0.15.5", + "esbuild-plugin-copy": "^2.1.1", "esbuild-plugin-lit": "^0.0.10", + "esbuild-plugin-replace": "^1.4.0", "graphql-ws": "5.12.0", ... }, "dependencies": { "@undecaf/barcode-detector-polyfill": "^0.9.15", "@undecaf/zbar-wasm": "^0.9.12", "auto-bind": "^5.0.1", - "esbuild-plugin-copy": "^2.1.1", - "esbuild-plugin-replace": "^1.4.0", "lit": "^2.3.1" },
Change main entry point from dist/web.js to dist/index.js to align with the types field (dist/index.d.ts). The web.js build is a Lit web component while index.js exports the core API functions (getAd4mConnect, getAd4mClient). This ensures backward compatibility with older tools that only read the main/types fields. Modern bundlers will continue to use the exports field which was already correctly configured.
- Add loading and validation checks to all Enter key handlers in RemoteAuthentication to prevent duplicate submissions (email login, password login, account creation) - Add validation checks to remote node connection Enter handler in ConnectionOptions - Disable remote connection button when URL is empty - Minor formatting cleanup in core.ts These changes ensure Enter key behavior matches button disabled states across all authentication and connection flows, preventing race conditions and improving user experience.
- Add CurrentState view to display connection details (local/remote, port/URL, auth status) - Implement disconnect() method in core to clean up connections and tokens - Add page reload on disconnect to reset app state - Fix settings button visibility after page refresh by calling requestUpdate() - Refactor shared-styles to consolidate duplicate CSS rules and improve organization - Replace option-header with box-header across ConnectionOptions for consistency - Replace button-row with center utility class in LocalAuthentication - Add danger button styles for disconnect action - Organize CSS by grouping buttons, inputs, layout utilities, and state indicators The disconnect flow now properly clears the AD4M client, disposes websockets, removes authentication tokens, and reloads the page to ensure apps start fresh when switching between different nodes or users.
…lay remote node option - Add showMultiUserOption boolean property to ConnectionOptions component - Conditionally render remote node connection UI based on multiUser option - Pass multiUser option from core to ConnectionOptions in web.ts - Enables hiding remote connection for single-user deployments
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@connect/src/components/views/ConnectionOptions.ts`:
- Around line 133-135: The external-download button in ConnectionOptions.ts uses
window.open without protection; update the `@click` handler on the Download AD4M
button (the element rendering DownloadIcon()) to open the URL with a new tab and
include noopener,noreferrer (e.g., call
window.open("https://github.com/coasys/ad4m/releases", "_blank",
"noopener,noreferrer") or replace the button with an <a> tag that has
target="_blank" and rel="noopener noreferrer") so the opened page cannot access
window.opener.
AD4M Connect: Architecture Refactor & Code Consolidation
Overview
This PR represents a comprehensive architectural refactor of
@coasys/ad4m-connect, consolidating 19 components into 4 focused view components + 11 SVG icons, and reducing the overall codebase by 50%. The refactor builds upon the multi-user authentication features added in the previousmulti-userbranch, providing a cleaner, more maintainable foundation with clear separation of concerns and a simplified developer experience.Note: Multi-user authentication features (email-based auth, passwordless verification, account creation) were implemented in the previous
multi-userbranch. This refactor focuses on consolidating that implementation into a better architecture.🎯 Key Improvements
Architecture Consolidation
Single Unified API
await getAd4mClient(options)that works everywhereEvent-Driven Architecture
authstatechange,connectionstatechange,configstatechangeverify-code,email-login,connect-remote-node, etc.bubbles: true, composed: trueEmbedded Mode Support
window.self !== window.top📊 Impact Summary
Code Quality
Architecture
core.ts- All business logic, connection management, authenticationweb.ts- UI orchestration, view routing, state coordinationshared-styles.ts- Consistent styling systemwithTempClient()- Temporary client creation with automatic cleanupcreateApolloClient()- Centralized Apollo configurationbuildAppInfo()- App metadata constructionthiscontextDeveloper Experience
🔄 Breaking Changes
Main API Change
Before:
After - Two Approaches:
Simple API (Recommended)
Returns just the authenticated client:
Advanced API
Returns core and client separately, allowing event listener attachment before authentication:
When to use each:
Package Exports
./web.jsto./index.jsuseClienthooks from React and Vue packages (redundant with explicit client passing)Configuration Options
backendUrlrenamed toremoteUrlfor clarityhosting,mobile,multiUseroptions (auto-detected)🏗️ Architecture
Before: Fragmented Architecture
After: Separation of Concerns
📝 Detailed Changes
Core Logic (
src/core.ts)Key responsibilities:
Embedded Mode:
Helper Patterns:
Web Orchestrator (
src/web.ts)Key responsibilities:
View Routing:
Event Delegation:
View Components
ConnectionOptions (
src/components/views/ConnectionOptions.ts)connect-local-node,connect-remote-node,change-portLocalAuthentication (
src/components/views/LocalAuthentication.ts)request-capability,verify-codeRemoteAuthentication (
src/components/views/RemoteAuthentication.ts)remoteAuthStateemail-login,verify-email-code,password-login,create-accountShared Styles (
src/styles/shared-styles.ts)Consistent design system:
🧪 Testing Scenarios
Standalone Mode
First-time user (local)
First-time user (remote)
Returning user
Embedded Mode
WE Launcher integration
Host app integration
Error Scenarios
🔄 Migration Guide
Update Initialization
Remove useClient Hooks
Remove Embedded Mode Logic
📦 Files Changed
Core Package
getAd4mClient()andgetAd4mConnect()APIsView Components
Icon Components (11 SVG components)
Configuration
🎯 Future Enhancements
✅ Checklist
🤝 Related PRs
Review Focus:
Testing Priority:
Summary by CodeRabbit
New Features
Refactor
Chores
✏️ Tip: You can customize this high-level summary in your review settings.