Remove IIFE wrapper from googleDriveAdapterHelper.js
Changed from IIFE pattern to direct object literal evaluation
Rationale:
- Global variable functions should evaluate to an object directly
- IIFE wrapper was unnecessary complexity
- Simpler pattern: code evaluates to object, not function that returns object
- Matches intended architecture for vm.Script module loading
Changes:
1. Removed IIFE wrapper:
- Before: (function createHelpers() { ... return {...} })()
- After: ({ ... })
2. Removed function indentation (2 spaces throughout file)
3. Updated constitution.md:
- Pattern description: 'IIFE returning object' → 'evaluates to a single object'
- Clarified: 'not wrapped in IIFE'
- Updated all pattern references
Structure:
- File defines classes and functions at top level
- Final expression is object literal referencing all functions
- When executed via vm.Script, evaluates to the object
- Object is assigned to globalVariableContext.googleDriveAdapterHelper
Benefits:
✅ Simpler pattern (no function wrapper)
✅ Clearer intent (direct object evaluation)
✅ Matches architecture description
✅ Easier to understand and maintain
✅ Same functionality, cleaner implementation
Before (IIFE):
After (Object Literal):
Testing:
✓ Syntax validated
✓ Server starts successfully
✓ Module loads: 'Loaded global functions: googleDriveAdapterHelper'
✓ All function calls work correctly
✓ Request handling works as expected
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -204,9 +204,9 @@ Follow-up TODOs:
|
|||||||
|
|
||||||
**Helper Functions Pattern**: Pure utility functions (XML escaping, validation, formatting, routing) MAY be extracted to `src/globalVariables/googleDriveAdapterHelper.js` to improve readability and maintainability. The helpers module:
|
**Helper Functions Pattern**: Pure utility functions (XML escaping, validation, formatting, routing) MAY be extracted to `src/globalVariables/googleDriveAdapterHelper.js` to improve readability and maintainability. The helpers module:
|
||||||
- MUST be loaded via `vm.Script` (same isolation as proxy.js)
|
- MUST be loaded via `vm.Script` (same isolation as proxy.js)
|
||||||
- MUST return a single object with all helper functions
|
- MUST evaluate to a single JavaScript object with all helper functions
|
||||||
- MUST have ZERO imports/exports
|
- MUST have ZERO imports/exports
|
||||||
- Is injected as `helpers` global object into VM context
|
- Is injected as `googleDriveAdapterHelper` global object into VM context
|
||||||
- Contains ONLY pure utilities, NO business logic or state
|
- Contains ONLY pure utilities, NO business logic or state
|
||||||
|
|
||||||
### I. Zero External Imports or Exports from `src/proxyScripts/proxy.js` (NON-NEGOTIABLE)
|
### I. Zero External Imports or Exports from `src/proxyScripts/proxy.js` (NON-NEGOTIABLE)
|
||||||
@@ -291,11 +291,11 @@ config/
|
|||||||
|
|
||||||
**googleDriveAdapterHelper.js Pattern**:
|
**googleDriveAdapterHelper.js Pattern**:
|
||||||
- MUST be loaded using `vm.Script` (same isolation as proxy.js)
|
- MUST be loaded using `vm.Script` (same isolation as proxy.js)
|
||||||
- MUST return single object with all helper functions via IIFE
|
- MUST evaluate to a single JavaScript object (not wrapped in IIFE)
|
||||||
- MUST have ZERO imports/exports (pure vm.Script execution)
|
- MUST have ZERO imports/exports (pure vm.Script execution)
|
||||||
- Loaded by `loadGlobalVariables()` which scans for both JSON and JS files
|
- Loaded by `loadGlobalVariables()` which scans for both JSON and JS files
|
||||||
- Filename determines global key: `googleDriveAdapterHelper.js` → `globalVariableContext.helpers`
|
- Filename determines global key: `googleDriveAdapterHelper.js` → `globalVariableContext.googleDriveAdapterHelper`
|
||||||
- Injected as `helpers` global object into VM context
|
- Injected as `googleDriveAdapterHelper` global object into VM context
|
||||||
- Contains ONLY pure utilities: validators, formatters, XML, error mappers
|
- Contains ONLY pure utilities: validators, formatters, XML, error mappers
|
||||||
- MUST NOT contain: authentication, API calls, state, business decisions
|
- MUST NOT contain: authentication, API calls, state, business decisions
|
||||||
- Executed in context with full access to globalVMContext and globalVariableContext
|
- Executed in context with full access to globalVMContext and globalVariableContext
|
||||||
@@ -441,7 +441,6 @@ script.runInContext(context);
|
|||||||
10. **googleDriveAdapterHelper** - Pure utility functions object (OPTIONAL)
|
10. **googleDriveAdapterHelper** - Pure utility functions object (OPTIONAL)
|
||||||
- Purpose: Extracted helper functions for code organization
|
- Purpose: Extracted helper functions for code organization
|
||||||
- Source: `src/globalVariables/googleDriveAdapterHelper.js` loaded via `vm.Script`
|
- Source: `src/globalVariables/googleDriveAdapterHelper.js` loaded via `vm.Script`
|
||||||
- Pattern: IIFE returning object with all helper functions
|
|
||||||
- Loading: server.js loads via `loadGlobalVariables()` at startup
|
- Loading: server.js loads via `loadGlobalVariables()` at startup
|
||||||
- Generic Loading Pattern:
|
- Generic Loading Pattern:
|
||||||
- All .js files in globalVariables/ are loaded automatically
|
- All .js files in globalVariables/ are loaded automatically
|
||||||
|
|||||||
@@ -17,299 +17,296 @@
|
|||||||
* @returns {Object} Helpers object with all utility functions
|
* @returns {Object} Helpers object with all utility functions
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Wrap in IIFE that returns helpers object
|
/**
|
||||||
(function createHelpers() {
|
* Custom error for document count exceeding limit
|
||||||
/**
|
*/
|
||||||
* Custom error for document count exceeding limit
|
class DocumentCountExceededError extends Error {
|
||||||
*/
|
constructor(count, limit) {
|
||||||
class DocumentCountExceededError extends Error {
|
super(`Document count ${count} exceeds limit of ${limit}`);
|
||||||
constructor(count, limit) {
|
this.name = "DocumentCountExceededError";
|
||||||
super(`Document count ${count} exceeds limit of ${limit}`);
|
this.count = count;
|
||||||
this.name = "DocumentCountExceededError";
|
this.limit = limit;
|
||||||
this.count = count;
|
this.statusCode = 413;
|
||||||
this.limit = limit;
|
}
|
||||||
this.statusCode = 413;
|
}
|
||||||
}
|
|
||||||
|
// =============================================================================
|
||||||
|
// Utility Functions
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a unique request ID for tracing
|
||||||
|
* Uses UUID v4 for uniqueness
|
||||||
|
*
|
||||||
|
* @returns {string} Request ID in format: req_<uuid>
|
||||||
|
*/
|
||||||
|
function generateRequestId() {
|
||||||
|
return `req_${crypto.randomUUID()}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate document ID format
|
||||||
|
* Google Drive IDs are alphanumeric with hyphens and underscores
|
||||||
|
*
|
||||||
|
* @param {string} id - Document ID to validate
|
||||||
|
* @returns {boolean} True if valid
|
||||||
|
*/
|
||||||
|
function validateDocumentId(id) {
|
||||||
|
if (!id || typeof id !== "string") {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// =============================================================================
|
// Google Drive IDs are typically 8-128 characters
|
||||||
// Utility Functions
|
// Characters: a-z, A-Z, 0-9, -, _
|
||||||
// =============================================================================
|
const pattern = /^[a-zA-Z0-9_-]{8,128}$/;
|
||||||
|
return pattern.test(id);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate a unique request ID for tracing
|
* Validate document count against limit
|
||||||
* Uses UUID v4 for uniqueness
|
*
|
||||||
*
|
* @param {number} count - Document count
|
||||||
* @returns {string} Request ID in format: req_<uuid>
|
* @param {number} limit - Maximum allowed (default: 50000)
|
||||||
*/
|
* @throws {DocumentCountExceededError} If count > limit
|
||||||
function generateRequestId() {
|
*/
|
||||||
return `req_${crypto.randomUUID()}`;
|
function validateDocumentCount(count, limit = 50000) {
|
||||||
|
if (count > limit) {
|
||||||
|
throw new DocumentCountExceededError(count, limit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// XML Utilities
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Escape special XML characters
|
||||||
|
* Prevents XML injection and ensures valid XML output
|
||||||
|
*
|
||||||
|
* @param {string} str - String to escape
|
||||||
|
* @returns {string} Escaped string safe for XML
|
||||||
|
*/
|
||||||
|
function escapeXml(str) {
|
||||||
|
if (!str) return "";
|
||||||
|
|
||||||
|
return str
|
||||||
|
.replace(/&/g, "&")
|
||||||
|
.replace(/</g, "<")
|
||||||
|
.replace(/>/g, ">")
|
||||||
|
.replace(/"/g, """)
|
||||||
|
.replace(/'/g, "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// Error Mapping
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map Drive API error to HTTP status code and retry info
|
||||||
|
*
|
||||||
|
* Per specification:
|
||||||
|
* - 429: Rate limit - include Retry-After header
|
||||||
|
* - 503: Service unavailable - NO RETRY (fail immediately)
|
||||||
|
* - 401: Authentication failed
|
||||||
|
* - 500: Other errors
|
||||||
|
*
|
||||||
|
* @param {Error} error - Drive API error
|
||||||
|
* @returns {Object} { statusCode, retryAfter? }
|
||||||
|
*/
|
||||||
|
function mapDriveErrorToHttp(error) {
|
||||||
|
// Handle DocumentCountExceededError
|
||||||
|
if (error instanceof DocumentCountExceededError) {
|
||||||
|
return { statusCode: 413 };
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// Extract status code from Drive API error
|
||||||
* Validate document ID format
|
const statusCode = error.response?.status || error.code || 500;
|
||||||
* Google Drive IDs are alphanumeric with hyphens and underscores
|
|
||||||
*
|
|
||||||
* @param {string} id - Document ID to validate
|
|
||||||
* @returns {boolean} True if valid
|
|
||||||
*/
|
|
||||||
function validateDocumentId(id) {
|
|
||||||
if (!id || typeof id !== "string") {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Google Drive IDs are typically 8-128 characters
|
// Handle rate limiting (429)
|
||||||
// Characters: a-z, A-Z, 0-9, -, _
|
if (statusCode === 429) {
|
||||||
const pattern = /^[a-zA-Z0-9_-]{8,128}$/;
|
// Extract Retry-After from response headers if present
|
||||||
return pattern.test(id);
|
const retryAfter = error.response?.headers?.["retry-after"];
|
||||||
|
const retryAfterSeconds = retryAfter ? parseInt(retryAfter, 10) : 60;
|
||||||
|
|
||||||
|
return {
|
||||||
|
statusCode: 429,
|
||||||
|
retryAfter: retryAfterSeconds,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// Handle service unavailable (503) - NO RETRY per spec
|
||||||
* Validate document count against limit
|
if (statusCode === 503) {
|
||||||
*
|
return { statusCode: 503 };
|
||||||
* @param {number} count - Document count
|
|
||||||
* @param {number} limit - Maximum allowed (default: 50000)
|
|
||||||
* @throws {DocumentCountExceededError} If count > limit
|
|
||||||
*/
|
|
||||||
function validateDocumentCount(count, limit = 50000) {
|
|
||||||
if (count > limit) {
|
|
||||||
throw new DocumentCountExceededError(count, limit);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// =============================================================================
|
// Handle authentication errors
|
||||||
// XML Utilities
|
if (statusCode === 401 || statusCode === 403) {
|
||||||
// =============================================================================
|
return { statusCode: statusCode };
|
||||||
|
|
||||||
/**
|
|
||||||
* Escape special XML characters
|
|
||||||
* Prevents XML injection and ensures valid XML output
|
|
||||||
*
|
|
||||||
* @param {string} str - String to escape
|
|
||||||
* @returns {string} Escaped string safe for XML
|
|
||||||
*/
|
|
||||||
function escapeXml(str) {
|
|
||||||
if (!str) return "";
|
|
||||||
|
|
||||||
return str
|
|
||||||
.replace(/&/g, "&")
|
|
||||||
.replace(/</g, "<")
|
|
||||||
.replace(/>/g, ">")
|
|
||||||
.replace(/"/g, """)
|
|
||||||
.replace(/'/g, "'");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// =============================================================================
|
// All other errors map to 500
|
||||||
// Error Mapping
|
return { statusCode: 500 };
|
||||||
// =============================================================================
|
}
|
||||||
|
|
||||||
/**
|
// =============================================================================
|
||||||
* Map Drive API error to HTTP status code and retry info
|
// Sitemap Functions
|
||||||
*
|
// =============================================================================
|
||||||
* Per specification:
|
|
||||||
* - 429: Rate limit - include Retry-After header
|
|
||||||
* - 503: Service unavailable - NO RETRY (fail immediately)
|
|
||||||
* - 401: Authentication failed
|
|
||||||
* - 500: Other errors
|
|
||||||
*
|
|
||||||
* @param {Error} error - Drive API error
|
|
||||||
* @returns {Object} { statusCode, retryAfter? }
|
|
||||||
*/
|
|
||||||
function mapDriveErrorToHttp(error) {
|
|
||||||
// Handle DocumentCountExceededError
|
|
||||||
if (error instanceof DocumentCountExceededError) {
|
|
||||||
return { statusCode: 413 };
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract status code from Drive API error
|
/**
|
||||||
const statusCode = error.response?.status || error.code || 500;
|
* Transform Drive document to sitemap entry
|
||||||
|
*
|
||||||
// Handle rate limiting (429)
|
* Creates RESTful URL in format: {baseUrl}/documents/{documentId}
|
||||||
if (statusCode === 429) {
|
* Per specification clarification #2.
|
||||||
// Extract Retry-After from response headers if present
|
*
|
||||||
const retryAfter = error.response?.headers?.["retry-after"];
|
* @param {Object} document - Drive API document
|
||||||
const retryAfterSeconds = retryAfter ? parseInt(retryAfter, 10) : 60;
|
* @param {string} document.id - Document ID
|
||||||
|
* @param {string} document.modifiedTime - ISO 8601 timestamp
|
||||||
return {
|
* @param {string} baseUrl - Base URL for the adapter
|
||||||
statusCode: 429,
|
* @returns {Object} Sitemap entry { loc, lastmod }
|
||||||
retryAfter: retryAfterSeconds,
|
*/
|
||||||
};
|
function toSitemapEntry(document, baseUrl) {
|
||||||
}
|
if (!document || !document.id) {
|
||||||
|
console.error("Invalid document for sitemap entry", { document });
|
||||||
// Handle service unavailable (503) - NO RETRY per spec
|
return null;
|
||||||
if (statusCode === 503) {
|
|
||||||
return { statusCode: 503 };
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle authentication errors
|
|
||||||
if (statusCode === 401 || statusCode === 403) {
|
|
||||||
return { statusCode: statusCode };
|
|
||||||
}
|
|
||||||
|
|
||||||
// All other errors map to 500
|
|
||||||
return { statusCode: 500 };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// =============================================================================
|
// RESTful URL format: /documents/{documentId}
|
||||||
// Sitemap Functions
|
const loc = `${baseUrl}/documents/${encodeURIComponent(document.id)}`;
|
||||||
// =============================================================================
|
|
||||||
|
|
||||||
/**
|
// Format lastmod as ISO 8601 date (YYYY-MM-DD)
|
||||||
* Transform Drive document to sitemap entry
|
let lastmod;
|
||||||
*
|
if (document.modifiedTime) {
|
||||||
* Creates RESTful URL in format: {baseUrl}/documents/{documentId}
|
try {
|
||||||
* Per specification clarification #2.
|
const date = new Date(document.modifiedTime);
|
||||||
*
|
lastmod = date.toISOString().split("T")[0]; // Extract YYYY-MM-DD
|
||||||
* @param {Object} document - Drive API document
|
} catch (error) {
|
||||||
* @param {string} document.id - Document ID
|
console.error("Invalid modifiedTime for document", {
|
||||||
* @param {string} document.modifiedTime - ISO 8601 timestamp
|
documentId: document.id,
|
||||||
* @param {string} baseUrl - Base URL for the adapter
|
modifiedTime: document.modifiedTime,
|
||||||
* @returns {Object} Sitemap entry { loc, lastmod }
|
});
|
||||||
*/
|
|
||||||
function toSitemapEntry(document, baseUrl) {
|
|
||||||
if (!document || !document.id) {
|
|
||||||
console.error("Invalid document for sitemap entry", { document });
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// RESTful URL format: /documents/{documentId}
|
|
||||||
const loc = `${baseUrl}/documents/${encodeURIComponent(document.id)}`;
|
|
||||||
|
|
||||||
// Format lastmod as ISO 8601 date (YYYY-MM-DD)
|
|
||||||
let lastmod;
|
|
||||||
if (document.modifiedTime) {
|
|
||||||
try {
|
|
||||||
const date = new Date(document.modifiedTime);
|
|
||||||
lastmod = date.toISOString().split("T")[0]; // Extract YYYY-MM-DD
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Invalid modifiedTime for document", {
|
|
||||||
documentId: document.id,
|
|
||||||
modifiedTime: document.modifiedTime,
|
|
||||||
});
|
|
||||||
lastmod = new Date().toISOString().split("T")[0]; // Fallback to today
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
lastmod = new Date().toISOString().split("T")[0]; // Fallback to today
|
lastmod = new Date().toISOString().split("T")[0]; // Fallback to today
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
return { loc, lastmod };
|
lastmod = new Date().toISOString().split("T")[0]; // Fallback to today
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
return { loc, lastmod };
|
||||||
* Transform array of Drive documents to sitemap entries
|
}
|
||||||
*
|
|
||||||
* @param {Array<Object>} documents - Array of Drive API documents
|
|
||||||
* @param {string} baseUrl - Base URL for the adapter
|
|
||||||
* @returns {Array<Object>} Array of sitemap entries
|
|
||||||
*/
|
|
||||||
function transformDocumentsToSitemapEntries(documents, baseUrl) {
|
|
||||||
if (!Array.isArray(documents)) {
|
|
||||||
console.error("Documents must be an array", { documents });
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
return documents
|
/**
|
||||||
.map((doc) => toSitemapEntry(doc, baseUrl))
|
* Transform array of Drive documents to sitemap entries
|
||||||
.filter((entry) => entry !== null);
|
*
|
||||||
|
* @param {Array<Object>} documents - Array of Drive API documents
|
||||||
|
* @param {string} baseUrl - Base URL for the adapter
|
||||||
|
* @returns {Array<Object>} Array of sitemap entries
|
||||||
|
*/
|
||||||
|
function transformDocumentsToSitemapEntries(documents, baseUrl) {
|
||||||
|
if (!Array.isArray(documents)) {
|
||||||
|
console.error("Documents must be an array", { documents });
|
||||||
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
return documents
|
||||||
* Generate XML sitemap from sitemap entries
|
.map((doc) => toSitemapEntry(doc, baseUrl))
|
||||||
*
|
.filter((entry) => entry !== null);
|
||||||
* Handles empty sitemap (0 documents) case - returns valid XML with empty urlset.
|
}
|
||||||
*
|
|
||||||
* @param {Array<Object>} sitemapEntries - Array of { loc, lastmod } objects
|
|
||||||
* @returns {string} Complete XML sitemap string
|
|
||||||
*/
|
|
||||||
function generateSitemapXML(sitemapEntries) {
|
|
||||||
let xml = '<?xml version="1.0" encoding="UTF-8"?>\n';
|
|
||||||
xml += '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">\n';
|
|
||||||
|
|
||||||
// Handle empty sitemap - valid XML with no <url> elements
|
/**
|
||||||
if (!sitemapEntries || sitemapEntries.length === 0) {
|
* Generate XML sitemap from sitemap entries
|
||||||
xml += "</urlset>";
|
*
|
||||||
return xml;
|
* Handles empty sitemap (0 documents) case - returns valid XML with empty urlset.
|
||||||
}
|
*
|
||||||
|
* @param {Array<Object>} sitemapEntries - Array of { loc, lastmod } objects
|
||||||
for (const entry of sitemapEntries) {
|
* @returns {string} Complete XML sitemap string
|
||||||
xml += " <url>\n";
|
*/
|
||||||
xml += ` <loc>${escapeXml(entry.loc)}</loc>\n`;
|
function generateSitemapXML(sitemapEntries) {
|
||||||
xml += ` <lastmod>${escapeXml(entry.lastmod)}</lastmod>\n`;
|
let xml = '<?xml version="1.0" encoding="UTF-8"?>\n';
|
||||||
xml += " </url>\n";
|
xml += '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">\n';
|
||||||
}
|
|
||||||
|
|
||||||
|
// Handle empty sitemap - valid XML with no <url> elements
|
||||||
|
if (!sitemapEntries || sitemapEntries.length === 0) {
|
||||||
xml += "</urlset>";
|
xml += "</urlset>";
|
||||||
|
|
||||||
return xml;
|
return xml;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
for (const entry of sitemapEntries) {
|
||||||
* Main sitemap generation function
|
xml += " <url>\n";
|
||||||
*
|
xml += ` <loc>${escapeXml(entry.loc)}</loc>\n`;
|
||||||
* Combines document transformation and XML generation.
|
xml += ` <lastmod>${escapeXml(entry.lastmod)}</lastmod>\n`;
|
||||||
*
|
xml += " </url>\n";
|
||||||
* @param {Array<Object>} documents - Array of Drive API documents
|
|
||||||
* @param {string} baseUrl - Base URL for the adapter
|
|
||||||
* @returns {string} Complete XML sitemap
|
|
||||||
*/
|
|
||||||
function generateSitemap(documents, baseUrl) {
|
|
||||||
const entries = transformDocumentsToSitemapEntries(documents, baseUrl);
|
|
||||||
return generateSitemapXML(entries);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// =============================================================================
|
xml += "</urlset>";
|
||||||
// Route Parsing
|
|
||||||
// =============================================================================
|
|
||||||
|
|
||||||
/**
|
return xml;
|
||||||
* Parse route from request
|
}
|
||||||
* @param {string} method - HTTP method
|
|
||||||
* @param {string} url - Request URL
|
|
||||||
* @returns {Object} Route info or error
|
|
||||||
*/
|
|
||||||
function parseRoute(method, url) {
|
|
||||||
if (method !== "GET") {
|
|
||||||
return { route: null, error: "Method not allowed", statusCode: 405 };
|
|
||||||
}
|
|
||||||
|
|
||||||
const urlObj = new URL(url, "http://localhost");
|
/**
|
||||||
const path = urlObj.pathname;
|
* Main sitemap generation function
|
||||||
|
*
|
||||||
|
* Combines document transformation and XML generation.
|
||||||
|
*
|
||||||
|
* @param {Array<Object>} documents - Array of Drive API documents
|
||||||
|
* @param {string} baseUrl - Base URL for the adapter
|
||||||
|
* @returns {string} Complete XML sitemap
|
||||||
|
*/
|
||||||
|
function generateSitemap(documents, baseUrl) {
|
||||||
|
const entries = transformDocumentsToSitemapEntries(documents, baseUrl);
|
||||||
|
return generateSitemapXML(entries);
|
||||||
|
}
|
||||||
|
|
||||||
// Match any path containing 'sitemap.xml'
|
// =============================================================================
|
||||||
if (path.includes("sitemap.xml")) {
|
// Route Parsing
|
||||||
return { route: "sitemap" };
|
// =============================================================================
|
||||||
}
|
|
||||||
|
|
||||||
// All other paths return 404
|
/**
|
||||||
return { route: null, error: "Not found", statusCode: 404 };
|
* Parse route from request
|
||||||
|
* @param {string} method - HTTP method
|
||||||
|
* @param {string} url - Request URL
|
||||||
|
* @returns {Object} Route info or error
|
||||||
|
*/
|
||||||
|
function parseRoute(method, url) {
|
||||||
|
if (method !== "GET") {
|
||||||
|
return { route: null, error: "Method not allowed", statusCode: 405 };
|
||||||
}
|
}
|
||||||
|
|
||||||
// =============================================================================
|
const urlObj = new URL(url, "http://localhost");
|
||||||
// Return helpers object with all functions
|
const path = urlObj.pathname;
|
||||||
// =============================================================================
|
|
||||||
|
|
||||||
return {
|
// Match any path containing 'sitemap.xml'
|
||||||
// Error classes
|
if (path.includes("sitemap.xml")) {
|
||||||
DocumentCountExceededError,
|
return { route: "sitemap" };
|
||||||
|
}
|
||||||
|
|
||||||
// Utilities
|
// All other paths return 404
|
||||||
generateRequestId,
|
return { route: null, error: "Not found", statusCode: 404 };
|
||||||
validateDocumentId,
|
}
|
||||||
validateDocumentCount,
|
|
||||||
|
|
||||||
// XML
|
// =============================================================================
|
||||||
escapeXml,
|
// Return helpers object with all functions
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
// Error mapping
|
({
|
||||||
mapDriveErrorToHttp,
|
// Error classes
|
||||||
|
DocumentCountExceededError,
|
||||||
|
|
||||||
// Sitemap
|
// Utilities
|
||||||
toSitemapEntry,
|
generateRequestId,
|
||||||
transformDocumentsToSitemapEntries,
|
validateDocumentId,
|
||||||
generateSitemapXML,
|
validateDocumentCount,
|
||||||
generateSitemap,
|
|
||||||
|
|
||||||
// Routing
|
// XML
|
||||||
parseRoute,
|
escapeXml,
|
||||||
};
|
|
||||||
})();
|
// Error mapping
|
||||||
|
mapDriveErrorToHttp,
|
||||||
|
|
||||||
|
// Sitemap
|
||||||
|
toSitemapEntry,
|
||||||
|
transformDocumentsToSitemapEntries,
|
||||||
|
generateSitemapXML,
|
||||||
|
generateSitemap,
|
||||||
|
|
||||||
|
// Routing
|
||||||
|
parseRoute,
|
||||||
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user