     1	/**
     2	 * URL Helper Functions
     3	 *
     4	 * Centralized functions for URL generation to avoid code duplication
     5	 * and support flexible URL prefix override capabilities.
     6	 */
     7	
     8	/**
     9	 * Get the complete URL prefix, handling all calculation and override logic
    10	 *
    11	 * Priority order:
    12	 * 1. context.urlPrefixOverride - Explicit override (from hooks or middleware)
    13	 * 2. context.urlPrefix - Pre-calculated URL prefix
    14	 * 3. Calculate from request if provided
    15	 * 4. scope.vars.transport?.mountPath - Fallback to mount path
    16	 * 5. '' - Empty string as final fallback
    17	 *
    18	 * @param {Object} context - Request context (may have urlPrefixOverride or urlPrefix)
    19	 * @param {Object} scope - Scope/resource object with transport vars
    20	 * @param {Object} req - Optional Express request object for auto-calculation
    21	 * @returns {string} The final URL prefix to use
    22	 *
    23	 * @example
    24	 * // With full context
    25	 * const urlPrefix = getUrlPrefix(context, scope);
    26	 *
    27	 * @example
    28	 * // With request object for auto-calculation
    29	 * const urlPrefix = getUrlPrefix({}, api, req);
    30	 */
    31	export function getUrlPrefix (context, scope, req = null) {
    32	  // Priority 1: Explicit override
    33	  if (context?.urlPrefixOverride) {
    34	    return context.urlPrefixOverride
    35	  }
    36	
    37	  // Priority 2: Pre-calculated urlPrefix
    38	  if (context?.urlPrefix) {
    39	    return context.urlPrefix
    40	  }
    41	
    42	  // Priority 3: Calculate from request if provided
    43	  if (req) {
    44	    const protocol = req.get?.('x-forwarded-proto') || req.protocol || 'http'
    45	    const host = req.get?.('x-forwarded-host') || req.get?.('host')
    46	    const mountPath = scope?.vars?.transport?.mountPath || ''
    47	
    48	    if (host) {
    49	      return `${protocol}://${host}${mountPath}`
    50	    }
    51	  }
    52	
    53	  // Priority 4: Fallback to mount path
    54	  return scope?.vars?.transport?.mountPath || ''
    55	}
    56	
    57	/**
    58	 * Build a complete resource URL
    59	 *
    60	 * @param {Object} context - Request context
    61	 * @param {Object} scope - Scope containing transport vars
    62	 * @param {string} scopeName - Resource type name
    63	 * @param {string} id - Resource ID
    64	 * @returns {string} Complete resource URL
    65	 *
    66	 * @example
    67	 * const url = buildResourceUrl(context, scope, 'books', '123');
    68	 * // Returns: "https://api.example.com/api/books/123"
    69	 */
    70	export function buildResourceUrl (context, scope, scopeName, id) {
    71	  const urlPrefix = getUrlPrefix(context, scope)
    72	  return `${urlPrefix}/${scopeName}/${id}`
    73	}
    74	
    75	/**
    76	 * Build a relationship URL
    77	 *
    78	 * @param {Object} context - Request context
    79	 * @param {Object} scope - Scope containing transport vars
    80	 * @param {string} scopeName - Resource type name
    81	 * @param {string} id - Resource ID
    82	 * @param {string} relationshipName - Name of the relationship
    83	 * @param {boolean} isRelationshipEndpoint - If true, builds /relationships/ URL
    84	 * @returns {string} Complete relationship URL
    85	 *
    86	 * @example
    87	 * // For relationship linkage endpoint
    88	 * buildRelationshipUrl(context, scope, 'books', '1', 'author', true);
    89	 * // Returns: "https://api.example.com/api/books/1/relationships/author"
    90	 *
    91	 * // For related resource endpoint
    92	 * buildRelationshipUrl(context, scope, 'books', '1', 'author', false);
    93	 * // Returns: "https://api.example.com/api/books/1/author"
    94	 */
    95	export function buildRelationshipUrl (context, scope, scopeName, id, relationshipName, isRelationshipEndpoint = false) {
    96	  const urlPrefix = getUrlPrefix(context, scope)
    97	  if (isRelationshipEndpoint) {
    98	    return `${urlPrefix}/${scopeName}/${id}/relationships/${relationshipName}`
    99	  }
   100	  return `${urlPrefix}/${scopeName}/${id}/${relationshipName}`
   101	}
   102	
   103	/**
   104	 * Build a collection URL with optional query string
   105	 *
   106	 * @param {Object} context - Request context
   107	 * @param {Object} scope - Scope containing transport vars
   108	 * @param {string} scopeName - Resource type name
   109	 * @param {string} queryString - Optional query string (with leading ?)
   110	 * @returns {string} Complete collection URL
   111	 *
   112	 * @example
   113	 * buildCollectionUrl(context, scope, 'books', '?page[size]=10');
   114	 * // Returns: "https://api.example.com/api/books?page[size]=10"
   115	 */
   116	export function buildCollectionUrl (context, scope, scopeName, queryString = '') {
   117	  const urlPrefix = getUrlPrefix(context, scope)
   118	  return `${urlPrefix}/${scopeName}${queryString}`
   119	}
