# Architecture Documentation

## System Architecture

### High-Level Overview

```
┌─────────────────────────────────────────────────────────────┐
│                        Browser (Client)                      │
│  ┌──────────────────────────────────────────────────────┐  │
│  │              sharepointpermissions.html               │  │
│  │                                                        │  │
│  │  ┌──────────────┐  ┌──────────────┐  ┌────────────┐ │  │
│  │  │     HTML     │  │     CSS      │  │ JavaScript │ │  │
│  │  │  Structure   │  │   Styling    │  │   Logic    │ │  │
│  │  └──────────────┘  └──────────────┘  └────────────┘ │  │
│  │                                                        │  │
│  │  ┌────────────────────────────────────────────────┐  │  │
│  │  │          Application State (app.state)         │  │  │
│  │  │  • principals[]  • nodes[]  • selectedId       │  │  │
│  │  └────────────────────────────────────────────────┘  │  │
│  │                                                        │  │
│  │  ┌──────────┐  ┌──────────┐  ┌──────────────────┐   │  │
│  │  │  Build   │  │Structure │  │    Effective      │   │  │
│  │  │  Panel   │  │  Panel   │  │  Permissions      │   │  │
│  │  │ (Editor) │  │  (Tree)  │  │  Panel (Analysis) │   │  │
│  │  └──────────┘  └──────────┘  └──────────────────┘   │  │
│  └──────────────────────────────────────────────────────┘  │
│                                                             │
│  ┌──────────────────────────────────────────────────────┐  │
│  │              localStorage (Optional)                  │  │
│  │         sp_permissions_scenario: JSON state           │  │
│  └──────────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────────┘
```

## Design Patterns

### 1. Single Page Application (SPA)
- No page reloads
- All interactions handled via JavaScript
- Dynamic DOM updates

### 2. Immediately Invoked Function Expression (IIFE)
```javascript
(function(){
  // All code enclosed to avoid global namespace pollution
  const app = { state: {...} }
  // ...
})();
```

**Benefits:**
- Prevents global variable conflicts
- Encapsulation of all logic
- Clear separation from browser APIs

### 3. State Management Pattern
```
User Action → State Mutation → Re-render → DOM Update
```

**Flow:**
1. User clicks button or changes input
2. Event handler modifies `app.state`
3. `render()` called
4. All three panels regenerate HTML from state

**Key principle:** State is source of truth. Never manipulate DOM directly without state change.

### 4. Unidirectional Data Flow

```
┌─────────────┐
│ User Action │
└──────┬──────┘
       │
       v
┌─────────────┐
│   Handler   │ (actHandler, button clicks)
└──────┬──────┘
       │
       v
┌─────────────┐
│ State Mutate│ (app.state modified)
└──────┬──────┘
       │
       v
┌─────────────┐
│  render()   │ (rebuild all panels)
└──────┬──────┘
       │
       v
┌─────────────┐
│ DOM Update  │ (innerHTML replaced)
└──────┬──────┘
       │
       v
┌─────────────┐
│ Bind Events │ (querySelectorAll + addEventListener)
└─────────────┘
```

### 5. Declarative Rendering
Instead of:
```javascript
// Imperative (bad)
const div = document.createElement('div');
div.className = 'chip';
div.textContent = name;
parent.appendChild(div);
```

We use:
```javascript
// Declarative (good)
el.innerHTML = `<div class="chip">${escapeHtml(name)}</div>`;
```

**Trade-off:** Faster development, easier debugging, but full DOM replacement on each render.

## Component Architecture

### Three-Panel Layout

```
┌────────────────────────────────────────────────────────────────┐
│                          Header                                 │
│  • Title & Help    • Toolbar    • Quick Start Guide            │
└────────────────────────────────────────────────────────────────┘
┌──────────────┬─────────────────┬───────────────────────────────┐
│   Build      │   Structure     │   Effective Permissions       │
│   Panel      │   Panel         │   Panel                       │
│              │                 │                               │
│ renderBuilder│ renderTree()    │ renderEffects()               │
│  ()          │                 │                               │
│              │                 │                               │
│ • Edit item  │ • Visual tree   │ • Risk assessment             │
│ • Toggle     │ • Click to      │ • Inheritance chain           │
│   inherit    │   select        │ • Permission table            │
│ • Grant      │ • Shows badges  │ • Explanations                │
│   access     │ • Link icons    │                               │
│ • Manage     │                 │                               │
│   people     │                 │                               │
└──────────────┴─────────────────┴───────────────────────────────┘
```

### Component Responsibilities

#### Build Panel (Left)
**Purpose:** Edit the selected node and manage the global principals directory

**Renders:**
- Selected item name and type
- Inheritance checkbox
- Add child/sibling buttons
- Delete button
- Grant access forms (direct assignment + sharing links)
- Direct permissions list
- Principals directory

**State Dependencies:**
- `app.state.selectedId`
- `app.state.principals`
- Selected node's `assignments` and `sharingLinks`

**Updates state:**
- Node properties (name, inherits)
- Node assignments and sharingLinks
- Principals array

#### Structure Panel (Middle)
**Purpose:** Display the hierarchical tree and allow selection

**Renders:**
- Recursive tree structure
- Badges (inherits/unique)
- Icons (site/library/folder/file)
- Sharing link indicators

**State Dependencies:**
- `app.state.nodes`
- `app.state.selectedId`

**Updates state:**
- `app.state.selectedId` (on click)

#### Effective Permissions Panel (Right)
**Purpose:** Calculate and display who can access the selected item

**Renders:**
- Risk level badge
- Risk message
- Inheritance chain
- Permission table (person → role → sources)
- Explanation text

**State Dependencies:**
- `app.state.selectedId`
- `app.state.nodes` (for inheritance walkup)
- `app.state.principals` (for display names)

**Updates state:** None (read-only)

## Core Algorithms

### 1. Permission Calculation (`computeEffectiveDetailed`)

**Input:** Node object
**Output:** Map of `principalId → {role, sources}`

**Algorithm:**
```
Phase 1: Direct Assignments (Respects Inheritance)
├─ Start at node
├─ Collect assignments on current node
├─ If inherits = true, walk to parent and repeat
└─ If inherits = false, STOP (don't look at parents)

Phase 2: Sharing Links (BYPASSES Inheritance!)
├─ Start at node
├─ Collect sharing links on current node
├─ Create virtual principal for each link type
├─ ALWAYS walk to parent (even if inherits = false)
├─ Mark links as "bypassing" if inheritance was broken
└─ Continue to root

Combine Results:
├─ For each principal, take highest role
└─ Track all sources for explanation
```

**Key Insight:** Two-phase approach models real SharePoint where sharing links ignore broken inheritance.

**Time Complexity:** O(h × p) where h = height of tree, p = average principals per node

**Example:**
```
Site (Alice: Edit)
└── Library (inherits)
    ├── 🔗 Anyone: Read
    └── Folder (UNIQUE, Bob: Contribute)
        └── File (inherits from Folder)

Effective permissions on File:
- Bob: Contribute (via Folder assignment, inherited)
- Anyone: Read (via Library sharing link, BYPASSES Folder's unique perms)
```

### 2. Risk Assessment (`riskOf`)

**Input:** Node object
**Output:** `{level, color, msg}`

**Decision Tree:**
```
Has "Anyone" link at any level?
│
├─ YES → HIGH (anonymous access)
│
└─ NO → Has sharing link bypassing inheritance?
         │
         ├─ YES → HIGH (dangerous bypass)
         │
         └─ NO → Has external user with Edit+?
                  │
                  ├─ YES → HIGH (external with power)
                  │
                  └─ NO → Broken inheritance + 5+ editors?
                           │
                           ├─ YES → MEDIUM (broad access)
                           │
                           └─ NO → LOW (safe)
```

**Time Complexity:** O(h + e) where h = height, e = effective principals

### 3. Inheritance Chain (`renderEffects` helper)

**Purpose:** Show which ancestors contribute to permissions

**Algorithm:**
```
chain = [node]
cur = node
while cur has parent:
  if cur.inherits = false:
    STOP (unique permissions, don't show ancestors)
  cur = cur.parent
  chain.prepend(cur)
return chain
```

**Visual Output:**
```
Site (inherits) ➜ Library (inherits) ➜ Folder (unique)
```

### 4. Tree Rendering (`renderNode` recursion)

**Purpose:** Generate nested HTML for tree visualization

**Algorithm:**
```javascript
function renderNode(node) {
  const children = childrenOf(node.id)
  return `
    <li>
      ${nodeLine(node)}
      ${children.length ? `<ul>${children.map(renderNode).join('')}</ul>` : ''}
    </li>
  `
}
```

**Pattern:** Recursive rendering, depth-first traversal

## Data Flow Examples

### Example 1: Toggling Inheritance

```
1. User unchecks "Inherit permissions" checkbox
   └─ checkbox onChange event fires

2. actHandler() called with act='toggle-inherit'
   └─ Sets sel.inherits = false

3. render() called
   └─ Triggers all three panels to re-render

4. renderBuilder() regenerates left panel
   └─ Checkbox now unchecked, shows "unique perms" badge

5. renderTree() regenerates middle panel
   └─ Node now shows yellow "unique perms" badge

6. renderEffects() regenerates right panel
   └─ Inheritance chain stops at this node
   └─ Risk assessment recalculated
   └─ Permission table updated (ancestors no longer contribute)

7. Event listeners re-bound to new DOM elements
```

### Example 2: Adding a Sharing Link

```
1. User selects link type "Anyone" and role "Read", clicks "Create Link"
   └─ button click event fires

2. actHandler() called with act='add-link'
   └─ Calls addSharingLink()

3. addSharingLink() modifies state
   └─ sel.sharingLinks.push({id, type:'anyone', role:'Read', created})

4. render() called
   └─ All panels regenerate

5. renderBuilder() shows new link in "Sharing links" section
   └─ Link appears with 🌐 icon and "Anyone with the link" label
   └─ "Revoke" button bound to new link

6. renderTree() adds 📤 indicator to node

7. renderEffects() recalculates permissions
   └─ New virtual principal "link-anyone-{nodeId}" added to table
   └─ Risk level changes to HIGH
   └─ Warning message: "Anyone sharing link grants anonymous access"

8. All descendants now show Anyone access in their effective permissions
   (even if they have unique permissions!)
```

## State Management

### State Structure

```javascript
app.state = {
  principals: [
    {
      id: "abc123",           // unique identifier
      name: "John Doe",       // display name
      kind: "user"            // user|group|external
    }
  ],
  nodes: [
    {
      id: "xyz789",           // unique identifier
      type: "folder",         // site|library|folder|file
      name: "My Folder",      // display name
      parentId: "parent123",  // null for root site
      inherits: true,         // inheritance flag
      assignments: [          // direct permission grants
        {
          principalId: "abc123",
          role: "Edit"
        }
      ],
      sharingLinks: [         // sharing links created on this item
        {
          id: "link456",
          type: "org",        // anyone|org|specific|existing
          role: "Read",
          created: 1234567890 // timestamp
        }
      ]
    }
  ],
  selectedId: "xyz789",       // currently selected node
  schemaVersion: 2            // for migrations
}
```

### State Mutations

**Allowed Operations:**
- Add/remove principals
- Add/remove nodes
- Modify node properties (name, inherits)
- Add/remove assignments
- Add/remove sharing links
- Change selection

**Forbidden Operations:**
- Direct DOM manipulation without state change
- Storing UI state outside of `app.state`
- Caching derived values (always compute from state)

### State Persistence

**localStorage:**
```javascript
// Save
localStorage.setItem('sp_permissions_scenario', JSON.stringify(app.state))

// Load
app.state = JSON.parse(localStorage.getItem('sp_permissions_scenario'))
```

**JSON Export:**
```javascript
// Export (triggers download)
downloadBlob(JSON.stringify(app.state), 'scenario.json', 'application/json')

// Import (FileReader API)
const data = JSON.parse(fileContent)
app.state = data
```

## Security Architecture

### Threat Model

**Threats NOT applicable (client-side only):**
- SQL injection
- Server-side code execution
- Authentication bypass
- Session hijacking
- CSRF

**Threats that ARE applicable:**
- XSS (Cross-Site Scripting)
- Malicious JSON import
- Prototype pollution

### XSS Prevention

**Two-layer defense:**

1. **HTML Escaping**
```javascript
function escapeHtml(s) {
  return String(s).replace(/[&<>]/g, c => ({
    '&': '&amp;',
    '<': '&lt;',
    '>': '&gt;'
  }[c]))
}
```

Applied to all user-controlled strings before inserting into HTML.

2. **Attribute Escaping**
```javascript
function escapeAttr(s) {
  return String(s).replace(/[\"&<>]/g, c => ({
    '\"': '&quot;',
    '&': '&amp;',
    '<': '&lt;',
    '>': '&gt;'
  }[c]))
}
```

Applied to values inserted into HTML attributes.

**Safe pattern:**
```javascript
el.innerHTML = `<div class="chip">${escapeHtml(name)}</div>`
el.innerHTML = `<input value="${escapeAttr(value)}"/>`
```

**Unsafe pattern (NOT used):**
```javascript
el.innerHTML = `<div class="chip">${name}</div>` // XSS vulnerability!
```

### JSON Import Validation

```javascript
function importJSONFile(file) {
  try {
    const data = JSON.parse(reader.result)

    // Validation
    if (!data || !Array.isArray(data.nodes) || !Array.isArray(data.principals)) {
      throw new Error('Invalid file')
    }

    // Schema migration (defensive)
    for (const node of data.nodes) {
      if (!node.sharingLinks) {
        node.sharingLinks = []
      }
    }

    app.state = data
    render()
  } catch(e) {
    alert('Invalid JSON file')
  }
}
```

## Performance Characteristics

### Rendering Performance

**Full re-render on every state change:**
- Typical render time: < 5ms for 50 nodes
- No virtual DOM diffing
- Simple enough that full replacement is fast

**Optimization: Debouncing text input**
```javascript
if (act === 'rename') {
  sel.name = ev.currentTarget.value
  if (ev.type !== 'change') return // Don't re-render on keystroke
}
```

Only re-render when input loses focus, not on every keystroke.

### Computation Performance

**Permission calculation:**
- O(h) tree walk where h = height
- Typical scenario: h ≤ 5 (Site → Library → Folder → Subfolder → File)
- Executes in < 1ms

**Risk assessment:**
- O(h + e) where e = effective principals
- Typical scenario: e ≤ 20
- Executes in < 1ms

### Memory Profile

**State size:**
- 100 nodes: ~20 KB JSON
- 50 principals: ~5 KB JSON
- Total state: typically < 50 KB

**DOM size:**
- Three panels: ~300 DOM nodes
- No memory leaks (full replacement on render)

## Browser Compatibility Matrix

| Feature | Chrome | Firefox | Safari | Edge |
|---------|--------|---------|--------|------|
| CSS Grid | 57+ | 52+ | 10.1+ | 16+ |
| CSS Custom Properties | 49+ | 31+ | 9.1+ | 15+ |
| Arrow Functions | 45+ | 22+ | 10+ | 12+ |
| Template Literals | 41+ | 34+ | 9+ | 12+ |
| FileReader API | 6+ | 3.6+ | 6+ | 10+ |
| localStorage | 4+ | 3.5+ | 4+ | 8+ |

**Minimum versions:**
- Chrome 57+ (Mar 2017)
- Firefox 52+ (Mar 2017)
- Safari 10.1+ (Mar 2017)
- Edge 16+ (Oct 2017)

## Error Handling

### User Input Validation

```javascript
// Structural validation
if (parent.type === 'file') {
  alert('Cannot add under a File')
  return
}

// Required field validation
if (!name) {
  alert('Enter a name or email')
  return
}

// Logical validation
if (type === 'library' && parent.type !== 'site') {
  alert('Libraries can only be added under a Site')
  return
}
```

### Defensive Programming

**Check for null/undefined:**
```javascript
const selected = byId(app.state.selectedId) || app.state.nodes[0]
if (!selected) {
  el.innerHTML = '<p class="muted">No scenario loaded.</p>'
  return
}
```

**Optional chaining pattern:**
```javascript
const linkType = (document.getElementById('link-type') || {}).value || 'org'
```

**Array defaults:**
```javascript
sel.sharingLinks = sel.sharingLinks || []
sel.assignments = sel.assignments || []
```

## Testing Strategy

### Self-Test Suite

**Located:** Lines 846-877

**Test scenarios:**
1. Add principal
2. Grant permission at library
3. Verify effective permissions (should have Edit)
4. Add folder with broken inheritance
5. Verify inheritance break (should have no access)
6. Grant permission at folder
7. Add file under folder
8. Verify inheritance from folder (should have Contribute)

**Pattern:**
```javascript
const snapshot = clone(app.state)
try {
  // Test operations
  assert(condition, 'description', results)
} finally {
  // Restore state
  app.state = snapshot
  render()
}
```

### Manual Testing Checklist

- [ ] Load sample scenario
- [ ] Select each item type (site, library, folder, file)
- [ ] Toggle inheritance on/off
- [ ] Add principal of each kind (user, group, external)
- [ ] Grant permissions with each role
- [ ] Create each sharing link type
- [ ] Delete nodes
- [ ] Add siblings and children
- [ ] Export JSON and re-import
- [ ] Export CSV and verify
- [ ] Save to localStorage and reload
- [ ] Test in different browsers

## Deployment

### No Build Process

**To deploy:**
1. Copy `sharepointpermissions.html` to destination
2. Open in browser
3. Done.

**To distribute:**
- Email attachment
- USB drive
- Internal web server (static file)
- Git repository
- SharePoint document library (ironically!)

### Version Control

**Current approach:**
- Manual backup files (`sharepointpermissions.html1stbak`)
- Timestamp in filename for versioning

**Recommended approach:**
- Git repository
- Semantic versioning tags
- CHANGELOG.md

## Maintenance & Evolution

### Schema Migrations

**Current migration (v1 → v2):**
```javascript
for (const node of data.nodes) {
  if (!node.sharingLinks) {
    node.sharingLinks = []
  }
}
if (!data.schemaVersion) {
  data.schemaVersion = 2
}
```

**Pattern for future migrations:**
```javascript
function migrate(data) {
  const version = data.schemaVersion || 1

  if (version < 2) {
    // v1 → v2 migration
  }

  if (version < 3) {
    // v2 → v3 migration
  }

  data.schemaVersion = CURRENT_VERSION
  return data
}
```

### Adding New Features

**Guidelines:**
1. Update state schema (add schemaVersion if breaking)
2. Update sample() to include new feature
3. Update renderBuilder() / renderTree() / renderEffects()
4. Add action handler in actHandler()
5. Update export/import logic
6. Update self-test
7. Update documentation

**Example: Adding time-based link expiration**
```javascript
// 1. State schema
sharingLinks: [{
  id, type, role, created,
  expires: null  // NEW: timestamp or null
}]

// 2. UI in renderBuilder()
<input type="datetime-local" data-act="set-link-expires"/>

// 3. Logic in computeEffectiveDetailed()
if (link.expires && link.expires < Date.now()) {
  continue // Skip expired link
}

// 4. Migration
if (version < 3) {
  for (const node of data.nodes) {
    for (const link of node.sharingLinks) {
      if (!link.expires) link.expires = null
    }
  }
}
```

## Code Quality

### Naming Conventions

- **Functions:** camelCase (`computeEffectiveDetailed`, `riskOf`)
- **Variables:** camelCase (`selectedId`, `principalsList`)
- **Constants:** UPPER_SNAKE_CASE (`ROLE_ORDER`, `LINK_TYPES`)
- **Element IDs:** kebab-case (`btn-new`, `grant-existing`)
- **CSS classes:** kebab-case (`.flow-left`, `.step-badge`)

### Code Organization

**Top to bottom:**
1. Data model and constants
2. Helper functions
3. Core logic (permissions, risk)
4. Sample data
5. Rendering functions
6. Import/export
7. Utilities
8. Initialization

**Principle:** Read from top to bottom, general to specific.

### Comments

**What to comment:**
- Complex algorithms (permission calculation)
- Non-obvious behavior (sharing links bypass inheritance)
- Business rules (structural validation)

**What NOT to comment:**
- Self-explanatory code
- Redundant descriptions

**Example good comment:**
```javascript
// PART 2: Process sharing links (CRITICAL: bypasses broken inheritance - walks to root!)
```

**Example bad comment:**
```javascript
// Set the name to the input value
sel.name = ev.currentTarget.value
```

## Glossary

**Principal:** User, group, or external identity that can be granted permissions

**Node:** Any item in the hierarchy (site, library, folder, or file)

**Assignment:** Direct permission grant (principal + role + node)

**Sharing Link:** URL-based access grant (type + role + node)

**Inheritance:** Automatic flow of permissions from parent to children

**Breaking Inheritance:** Stopping permission flow to create unique permissions

**Effective Permissions:** Actual access someone has (combined from all sources)

**Role:** Permission level (None, Read, Contribute, Edit, Full Control)

**Virtual Principal:** Synthetic principal representing a sharing link in effective permissions

**Inheritance Chain:** Sequence of ancestors that contribute permissions

**Bypass:** When sharing links grant access despite broken inheritance
