Tools for the Atmosphere tools.slices.network
quickslice atproto html
at main 265 lines 6.9 kB view raw view rendered
1# External Dependency Validation Implementation Plan 2 3> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. 4 5**Goal:** Warn users when their lexicons reference external dependencies that aren't published. 6 7**Architecture:** After honk validation passes, extract all `ref` properties pointing to external NSIDs, batch-query the lexicons quickslice to check which exist, show yellow warnings for missing ones. 8 9**Tech Stack:** JavaScript, GraphQL (lexicons quickslice) 10 11--- 12 13### Task 1: Add missingDependencies to state 14 15**Files:** 16- Modify: `lexicon-publisher.html:642-644` 17 18**Step 1: Add the new state property** 19 20Find this block: 21```javascript 22 // New: DNS instructions 23 dnsInstructions: [], // [{ domain, record, lexicons }] 24 }; 25``` 26 27Change to: 28```javascript 29 // New: DNS instructions 30 dnsInstructions: [], // [{ domain, record, lexicons }] 31 32 // New: External dependency check 33 missingDependencies: [], // [nsid, ...] 34 }; 35``` 36 37**Step 2: Commit** 38 39```bash 40git add lexicon-publisher.html 41git commit -m "feat(lexicon-publisher): add missingDependencies state" 42``` 43 44--- 45 46### Task 2: Add extractExternalRefs function 47 48**Files:** 49- Modify: `lexicon-publisher.html` (after `getPublishableLexicons` function, around line 1097) 50 51**Step 1: Add the function** 52 53After the `getPublishableLexicons` function closing brace, add: 54 55```javascript 56 function extractExternalRefs(lexicons, userDomain) { 57 const refs = new Set(); 58 59 function walk(obj) { 60 if (!obj || typeof obj !== 'object') return; 61 if (obj.ref && typeof obj.ref === 'string') { 62 const nsid = obj.ref.split('#')[0]; 63 const domain = getDomainAuthority(nsid); 64 if (domain !== userDomain && !domain.endsWith('.' + userDomain)) { 65 refs.add(nsid); 66 } 67 } 68 for (const val of Object.values(obj)) walk(val); 69 } 70 71 for (const lex of lexicons) walk(lex); 72 return [...refs]; 73 } 74``` 75 76**Step 2: Commit** 77 78```bash 79git add lexicon-publisher.html 80git commit -m "feat(lexicon-publisher): add extractExternalRefs function" 81``` 82 83--- 84 85### Task 3: Add checkExternalDependencies function 86 87**Files:** 88- Modify: `lexicon-publisher.html` (after `extractExternalRefs`) 89 90**Step 1: Add the function** 91 92After `extractExternalRefs`, add: 93 94```javascript 95 async function checkExternalDependencies(externalNsids) { 96 if (externalNsids.length === 0) return { found: [], missing: [] }; 97 98 try { 99 const result = await state.client.query(` 100 query CheckDeps($nsids: [String!]!) { 101 comAtprotoLexiconSchema( 102 first: 100, 103 where: { id: { in: $nsids } } 104 ) { 105 edges { 106 node { id } 107 } 108 } 109 } 110 `, { nsids: externalNsids }); 111 112 const foundIds = (result?.comAtprotoLexiconSchema?.edges || []) 113 .map(e => e.node.id); 114 115 return { 116 found: externalNsids.filter(n => foundIds.includes(n)), 117 missing: externalNsids.filter(n => !foundIds.includes(n)), 118 }; 119 } catch (err) { 120 console.error("External dependency check failed:", err); 121 return { found: [], missing: [] }; 122 } 123 } 124``` 125 126**Step 2: Commit** 127 128```bash 129git add lexicon-publisher.html 130git commit -m "feat(lexicon-publisher): add checkExternalDependencies function" 131``` 132 133--- 134 135### Task 4: Integrate into validateLexicons 136 137**Files:** 138- Modify: `lexicon-publisher.html:1396-1440` (validateLexicons function) 139 140**Step 1: Reset missingDependencies at start** 141 142Find: 143```javascript 144 state.result = null; 145 state.validationPassed = false; 146 state.conflicts = []; 147``` 148 149Change to: 150```javascript 151 state.result = null; 152 state.validationPassed = false; 153 state.conflicts = []; 154 state.missingDependencies = []; 155``` 156 157**Step 2: Add external dependency check after honk passes** 158 159Find: 160```javascript 161 // Check for conflicts if authenticated 162 if (state.viewer) { 163 await checkConflicts(); 164 } 165``` 166 167Change to: 168```javascript 169 // Check for external dependencies and conflicts if authenticated 170 if (state.viewer) { 171 const userDomain = getUserDomain(state.viewer.handle); 172 const externalRefs = extractExternalRefs(parseResult.lexicons, userDomain); 173 const { missing } = await checkExternalDependencies(externalRefs); 174 state.missingDependencies = missing; 175 176 await checkConflicts(); 177 } 178``` 179 180**Step 3: Commit** 181 182```bash 183git add lexicon-publisher.html 184git commit -m "feat(lexicon-publisher): check external deps during validation" 185``` 186 187--- 188 189### Task 5: Update renderResult to show warnings 190 191**Files:** 192- Modify: `lexicon-publisher.html:1251-1266` (renderResult function) 193 194**Step 1: Add warning display after success message** 195 196Find: 197```javascript 198 section.innerHTML = ` 199 <div class="result ${cls}" style="margin-top: 0.75rem;">${icon} ${esc(state.result.message)}</div> 200 `; 201 } 202``` 203 204Change to: 205```javascript 206 let html = `<div class="result ${cls}" style="margin-top: 0.75rem;">${icon} ${esc(state.result.message)}</div>`; 207 208 if (state.missingDependencies.length > 0) { 209 html += ` 210 <div class="warning-box" style="margin-top: 0.75rem;"> 211 <strong>Missing external dependencies:</strong> 212 <ul style="margin: 0.5rem 0 0 1.25rem;"> 213 ${state.missingDependencies.map(d => `<li>${esc(d)}</li>`).join('')} 214 </ul> 215 </div> 216 `; 217 } 218 219 section.innerHTML = html; 220 } 221``` 222 223**Step 2: Commit** 224 225```bash 226git add lexicon-publisher.html 227git commit -m "feat(lexicon-publisher): display missing dependency warnings" 228``` 229 230--- 231 232### Task 6: Manual test 233 234**Step 1: Test with external refs** 235 2361. Open lexicon-publisher in browser 2372. Sign in 2383. Upload or paste a lexicon that references an external type, e.g.: 239 ```json 240 { 241 "lexicon": 1, 242 "id": "slices.network.test", 243 "defs": { 244 "main": { 245 "type": "record", 246 "record": { 247 "type": "object", 248 "properties": { 249 "actor": { "type": "ref", "ref": "app.bsky.actor.defs#profileViewBasic" }, 250 "fake": { "type": "ref", "ref": "com.fake.nonexistent#thing" } 251 } 252 } 253 } 254 } 255 } 256 ``` 2574. Click Validate 2585. Verify: Success message appears, plus yellow warning box showing `com.fake.nonexistent` as missing (but not `app.bsky.actor.defs` if it's published) 259 260**Step 2: Final commit if needed** 261 262```bash 263git add lexicon-publisher.html 264git commit -m "feat(lexicon-publisher): complete external dependency validation" 265```