a collection of lightweight TypeScript packages for AT Protocol, the protocol powering Bluesky
atproto bluesky typescript npm
101
fork

Configure Feed

Select the types of activity you want to include in your feed.

refactor(lexicon-doc): don't make findExternalReferences recurse

Mary 33f811ad 1111462c

+63 -105
+5
.changeset/fresh-deserts-lay.md
··· 1 + --- 2 + '@atcute/lexicon-doc': patch 3 + --- 4 + 5 + don't make findExternalReferences recurse
+58 -105
packages/lexicons/lexicon-doc/lib/utils/refs.ts
··· 71 71 export const findExternalReferences = (doc: LexiconDoc, defId?: string): Set<string> => { 72 72 const refs = new Set<string>(); 73 73 const visited = new Set<string>(); 74 + let stack: { value: SchemaValue; next: typeof stack } | undefined; 74 75 75 - const extract = (def: SchemaValue): void => { 76 + if (defId !== undefined) { 77 + const def = doc.defs[defId]; 78 + if (def !== undefined) { 79 + stack = { value: def, next: stack }; 80 + } 81 + } else { 82 + for (const defId in doc.defs) { 83 + stack = { value: doc.defs[defId], next: stack }; 84 + } 85 + } 86 + 87 + while (stack !== undefined) { 88 + const def = stack.value; 89 + stack = stack.next; 90 + 76 91 switch (def.type) { 77 92 case 'ref': { 78 - const ref = def.ref; 79 - if (ref.startsWith('#')) { 80 - const id = extractDefId(ref)!; 81 - 82 - if (visited.has(id)) { 83 - break; 84 - } 85 - 86 - visited.add(id); 87 - 88 - const child = doc.defs[id]; 89 - if (child !== undefined) { 90 - extract(child); 91 - } 92 - 93 + const id = getInternalDefId(def.ref, doc.id); 94 + if (id === undefined) { 95 + refs.add(def.ref); 93 96 break; 94 97 } 95 98 96 - const nsid = stripHash(ref); 97 - if (nsid === doc.id) { 98 - const id = extractDefId(ref)!; 99 - 100 - if (visited.has(id)) { 101 - break; 102 - } 99 + if (visited.has(id)) { 100 + break; 101 + } 103 102 104 - visited.add(id); 103 + visited.add(id); 105 104 106 - const child = doc.defs[id]; 107 - if (child !== undefined) { 108 - extract(child); 109 - } 110 - 111 - break; 105 + const child = doc.defs[id]; 106 + if (child !== undefined) { 107 + stack = { value: child, next: stack }; 112 108 } 113 109 114 - refs.add(ref); 115 110 break; 116 111 } 117 112 case 'union': { 118 - for (const ref of def.refs) { 119 - if (ref.startsWith('#')) { 120 - const id = extractDefId(ref)!; 121 - 122 - if (visited.has(id)) { 123 - continue; 124 - } 125 - 126 - visited.add(id); 127 - 128 - const child = doc.defs[id]; 129 - if (child !== undefined) { 130 - extract(child); 131 - } 113 + for (let idx = 0, len = def.refs.length; idx < len; idx++) { 114 + const ref = def.refs[idx]; 115 + const id = getInternalDefId(ref, doc.id); 116 + if (id === undefined) { 117 + refs.add(ref); 118 + continue; 119 + } 132 120 121 + if (visited.has(id)) { 133 122 continue; 134 123 } 135 124 136 - const nsid = stripHash(ref); 137 - if (nsid === doc.id) { 138 - const id = extractDefId(ref)!; 125 + visited.add(id); 139 126 140 - if (visited.has(id)) { 141 - continue; 142 - } 143 - 144 - visited.add(id); 145 - 146 - const child = doc.defs[id]; 147 - if (child !== undefined) { 148 - extract(child); 149 - } 150 - 151 - continue; 127 + const child = doc.defs[id]; 128 + if (child !== undefined) { 129 + stack = { value: child, next: stack }; 152 130 } 153 - 154 - refs.add(ref); 155 131 } 156 132 157 133 break; 158 134 } 159 - 160 135 case 'record': { 161 - extract(def.record); 136 + stack = { value: def.record, next: stack }; 162 137 break; 163 138 } 164 - 165 139 case 'array': { 166 - extract(def.items); 140 + stack = { value: def.items, next: stack }; 167 141 break; 168 142 } 169 - 170 143 case 'object': { 171 144 const properties = def.properties; 172 145 if (properties === undefined) { 173 146 break; 174 147 } 175 148 176 - for (const item of Object.values(properties)) { 177 - extract(item); 149 + for (const key in properties) { 150 + stack = { value: properties[key], next: stack }; 178 151 } 179 152 180 153 break; 181 154 } 182 - 183 155 case 'procedure': { 184 - const { input, output } = def; 185 - 186 - if (input?.schema !== undefined) { 187 - extract(input.schema); 156 + if (def.input?.schema !== undefined) { 157 + stack = { value: def.input.schema, next: stack }; 188 158 } 189 159 190 - if (output?.schema !== undefined) { 191 - extract(output.schema); 160 + if (def.output?.schema !== undefined) { 161 + stack = { value: def.output.schema, next: stack }; 192 162 } 193 163 194 164 break; 195 165 } 196 166 case 'subscription': { 197 - const { message } = def; 198 - 199 - if (message?.schema !== undefined) { 200 - extract(message.schema); 167 + if (def.message?.schema !== undefined) { 168 + stack = { value: def.message.schema, next: stack }; 201 169 } 202 170 203 171 break; 204 172 } 205 173 case 'query': { 206 - const { output } = def; 207 - 208 - if (output?.schema !== undefined) { 209 - extract(output.schema); 174 + if (def.output?.schema !== undefined) { 175 + stack = { value: def.output.schema, next: stack }; 210 176 } 211 177 212 178 break; 213 179 } 214 180 } 215 - }; 216 - 217 - if (defId !== undefined) { 218 - const def = doc.defs[defId]; 219 - if (def !== undefined) { 220 - extract(def); 221 - } 222 - } else { 223 - for (const defId in doc.defs) { 224 - const def = doc.defs[defId]; 225 - extract(def); 226 - } 227 181 } 228 182 229 183 return refs; 230 184 }; 231 185 232 - const stripHash = (defUri: string): string => { 233 - const index = defUri.indexOf('#'); 234 - if (index === -1) { 235 - return defUri; 186 + const getInternalDefId = (ref: string, docId: string): string | undefined => { 187 + const hashIndex = ref.indexOf('#'); 188 + if (hashIndex === 0) { 189 + return ref.slice(1); 236 190 } 237 191 238 - return defUri.slice(0, index); 239 - }; 192 + if (hashIndex !== docId.length) { 193 + return undefined; 194 + } 240 195 241 - const extractDefId = (ref: string): string | undefined => { 242 - const hashIndex = ref.indexOf('#'); 243 - if (hashIndex === -1) { 196 + if (!ref.startsWith(docId)) { 244 197 return undefined; 245 198 } 246 199