Locker Service Compatibility
How forceCalendar maintains compatibility with Salesforce Lightning Locker Service.
What is Locker Service?
Lightning Locker Service is Salesforce's security architecture that isolates Lightning components in sandboxed environments. It restricts access to global objects, DOM APIs, and certain JavaScript features to prevent cross-component interference and XSS attacks.
Restrictions and Solutions
No eval() or Function()
Restriction: eval(), new Function(), and setTimeout(string) are blocked.
Solution: forceCalendar uses no dynamic code evaluation. All logic is in static modules. Template rendering uses programmatic DOM creation, not template strings.
No innerHTML (security-critical paths)
Restriction: innerHTML assignments can be blocked or sanitized by Locker Service.
Solution: View renderers use document.createElement(), element.textContent, and element.appendChild() for all DOM construction. No HTML strings are parsed.
// What we do (safe)
const div = document.createElement('div');
div.className = 'fc-event';
div.textContent = event.title;
container.appendChild(div);
// What we avoid (blocked by Locker)
container.innerHTML = `<div class="fc-event">${event.title}</div>`;Restricted Global Access
Restriction: Direct access to window, document, and navigator may be proxied or restricted.
Solution: The core package has zero DOM dependencies. The interface package accesses DOM APIs only through component-scoped references (this.shadowRoot, this.template). The AdaptiveMemoryManager accesses process.memoryUsage through an indirect reference to avoid triggering Locker Service restrictions:
// Indirect access pattern for Locker Service
const getMemory = () => {
try {
const p = globalThis.process || {};
return typeof p.memoryUsage === 'function' ? p.memoryUsage() : null;
} catch {
return null;
}
};No Dynamic import()
Restriction: Dynamic import() is not available in Locker Service contexts.
Solution: All imports are static ESM import statements resolved at bundle time. The LWC component uses dynamic document.createElement() for the custom element instead of dynamic imports.
Web Worker Limitations
Restriction: Web Workers may not be available or may have restricted APIs.
Solution: SearchWorkerManager detects Worker availability and falls back to InvertedIndex (synchronous, main-thread) when Workers are unavailable:
// SearchWorkerManager automatically falls back
if (typeof Worker === 'undefined') {
// Use InvertedIndex on the main thread
this.fallbackIndex = new InvertedIndex();
}Shadow DOM
Restriction: Locker Service enforces Shadow DOM boundaries more strictly than standard browsers.
Solution: All events use composed: true to cross shadow boundaries:
this.dispatchEvent(new CustomEvent('calendar-navigate', {
detail: { date, view },
bubbles: true,
composed: true, // Crosses Shadow DOM boundaries
}));No External Network Requests
Restriction: CSP headers in Salesforce block requests to external origins unless explicitly allowlisted.
Solution: The core package makes no network requests. ICS URL subscription (ICSHandler.subscribe()) and importFromURL() should only be used in non-Salesforce contexts. In Salesforce, use the Apex controller for all data access.
Compatibility Checklist
| Feature | Locker Service Safe | Notes |
|---|---|---|
| Core Calendar API | Yes | Zero DOM dependency |
| Event CRUD | Yes | Pure JS objects |
| Recurrence expansion | Yes | Pure computation |
| State management | Yes | Pure JS |
| Date utilities | Yes | Uses Intl.DateTimeFormat |
| ICS parsing | Yes | String processing only |
| ICS URL import | No | Blocked by CSP; use Apex instead |
| Full-text search | Partial | InvertedIndex works; Web Workers may not |
| Web Components | Yes | Shadow DOM with composed events |
| CSS theming | Yes | CSS custom properties inherit through Shadow DOM |
| Adaptive memory | Yes | Indirect process access with fallback |
| Drag and drop | Partial | Standard DOM events work; pointer events may be proxied |
Best Practices for Salesforce
-
Use the Apex controller for all data access. Do not use
fetch(),XMLHttpRequest, or ICS URL imports. -
Deploy as static resources. Bundle
@forcecalendar/coreand@forcecalendar/interfaceinto single files and deploy as static resources. -
Use
lwc:dom="manual"for the custom element. This is required because LWC templates cannot directly reference custom elements from static resources. -
Test in Lightning Experience. Locker Service behavior differs between Lightning Experience, the Salesforce mobile app, and development environments. Always test in the target environment.
-
Handle Worker fallback. Expect
SearchWorkerManagerto use theInvertedIndexfallback. Design your search UX to work synchronously. -
Avoid large event sets. Without Web Workers, search and recurrence expansion run on the main thread. Keep event counts manageable (under 5000) for responsive UI.