@@ -211,34 +211,6 @@ const propagateScopesToNode = (fromNodeInfo: NodeInfo, toNodeInfo: NodeInfo): bo
211211 return changed ;
212212} ;
213213
214- /**
215- * Seeds each node in the graph with its local access scope(s) based on its own properties.
216- * - 'read' if readOnly: true
217- * - 'write' if writeOnly: true
218- * - 'normal' if node is an object property
219- *
220- * Only non-array objects are considered for scope seeding.
221- *
222- * @param nodes - Map of JSON Pointer to NodeInfo.
223- */
224- export const seedLocalScopes = ( nodes : Graph [ 'nodes' ] ) : void => {
225- for ( const [ pointer , nodeInfo ] of nodes ) {
226- const { node } = nodeInfo ;
227-
228- if ( typeof node !== 'object' || node === null || node instanceof Array ) {
229- continue ;
230- }
231-
232- if ( 'readOnly' in node && node . readOnly === true ) {
233- nodeInfo . scopes = new Set ( [ 'read' ] ) ;
234- } else if ( 'writeOnly' in node && node . writeOnly === true ) {
235- nodeInfo . scopes = new Set ( [ 'write' ] ) ;
236- } else if ( pointer . match ( / \/ p r o p e r t i e s \/ [ ^ / ] + $ / ) ) {
237- nodeInfo . scopes = new Set ( [ 'normal' ] ) ;
238- }
239- }
240- } ;
241-
242214/**
243215 * Builds a graph of all nodes in an OpenAPI spec, indexed by normalized JSON Pointer,
244216 * and tracks all $ref dependencies and reverse dependencies between nodes.
@@ -299,14 +271,14 @@ export function buildGraph(
299271 graph . nodeDependencies . get ( pointer ) ! . add ( refPointer ) ;
300272 }
301273 // Check for tags property (should be an array of strings)
302- if ( 'tags' in node && node . tags instanceof Array ) {
274+ if ( 'tags' in node && Array . isArray ( node . tags ) ) {
303275 tags = new Set ( node . tags . filter ( ( tag ) => typeof tag === 'string' ) ) ;
304276 }
305277 }
306278
307279 graph . nodes . set ( pointer , { deprecated, key, node, parentPointer, tags } ) ;
308280
309- if ( node instanceof Array ) {
281+ if ( Array . isArray ( node ) ) {
310282 node . forEach ( ( item , index ) =>
311283 walk ( {
312284 key : index ,
@@ -340,16 +312,44 @@ export function buildGraph(
340312 transitiveDependencies : new Map ( ) ,
341313 } ;
342314
315+ // Merge parentToChildren cache build + scope seeding into one pass over graph.nodes.
343316 for ( const [ pointer , nodeInfo ] of graph . nodes ) {
344317 const parent = nodeInfo . parentPointer ;
345- if ( ! parent ) continue ;
346- if ( ! cache . parentToChildren . has ( parent ) ) {
347- cache . parentToChildren . set ( parent , [ ] ) ;
318+ if ( parent ) {
319+ let arr = cache . parentToChildren . get ( parent ) ;
320+ if ( ! arr ) {
321+ arr = [ ] ;
322+ cache . parentToChildren . set ( parent , arr ) ;
323+ }
324+ arr . push ( pointer ) ;
325+ }
326+
327+ // Seeds each node in the graph with its local access scope(s) based on its own properties.
328+ // - 'read' if readOnly: true
329+ // - 'write' if writeOnly: true
330+ // - 'normal' if node is an object property
331+ //
332+ // Only non-array objects are considered for scope seeding.
333+ const { node } = nodeInfo ;
334+ if ( typeof node === 'object' && node !== null && ! Array . isArray ( node ) ) {
335+ if ( 'readOnly' in node && node . readOnly === true ) {
336+ nodeInfo . scopes = new Set ( [ 'read' ] ) ;
337+ } else if ( 'writeOnly' in node && node . writeOnly === true ) {
338+ nodeInfo . scopes = new Set ( [ 'write' ] ) ;
339+ } else {
340+ // Check /properties/{key} without a regex: compare the segment before the
341+ // last slash to the string 'properties'.
342+ const lastSlash = pointer . lastIndexOf ( '/' ) ;
343+ if ( lastSlash > 0 ) {
344+ const prevSlash = pointer . lastIndexOf ( '/' , lastSlash - 1 ) ;
345+ if ( prevSlash >= 0 && pointer . slice ( prevSlash + 1 , lastSlash ) === 'properties' ) {
346+ nodeInfo . scopes = new Set ( [ 'normal' ] ) ;
347+ }
348+ }
349+ }
348350 }
349- cache . parentToChildren . get ( parent ) ! . push ( pointer ) ;
350351 }
351352
352- seedLocalScopes ( graph . nodes ) ;
353353 propagateScopes ( graph ) ;
354354 annotateChildScopes ( graph . nodes ) ;
355355
0 commit comments