Overview
Optional: Consider internalizing RWGPSLib into the codebase following Core/Adapter pattern.
Decision Criteria
Should only proceed if:
- ✅ You need features RWGPSLib doesn't provide
- ✅ You're comfortable maintaining OAuth/API code
- ✅ RWGPSLib has caused production issues
- ✅ You want extensive testing around RWGPS operations
Skip if:
- ❌ External library works fine (less maintenance burden)
- ❌ No specific pain points with current implementation
Proposed Architecture
// RWGPSCore.js - Pure JavaScript
class RWGPSCore {
static prepareEventPayload(event) {
// Pure JSON building logic
// 100% testable
}
static parseEventResponse(json) {
// Pure response parsing
// 100% testable
}
static validateEventData(event) {
// Pure validation
// 100% testable
}
}
// RWGPSAdapter.js - GAS API calls
class RWGPSAdapter {
constructor(apiKey) {
this.apiKey = apiKey;
}
getEvent(url) {
const response = UrlFetchApp.fetch(url + '.json');
return RWGPSCore.parseEventResponse(
JSON.parse(response.getContentText())
);
}
}
Success Criteria (If Proceeding)
Effort Estimate
4-7 days
Dependencies
CRITICAL: Must complete Phase 1 (Issues #171, #172, #173) BEFORE considering this
Recommendation
Defer this decision until Phase 1 complete. External library is likely fine.
References
- Architecture Plan: docs/Architecture-Refactoring-Plan.md
- Copilot Instructions: .github/copilot-instructions.md
Note on This Issue
This is an optional/deferred issue. Recommendation is to skip unless there's a clear need.
The external RWGPSLib library is working well, and internalizing it adds maintenance burden. Only proceed if:
- RWGPSLib is causing production issues
- You need features it doesn't provide
- You want extensive testing around RWGPS operations
Copilot Execution Checklist
Before Starting
GAS-Compatible Module Pattern
MANDATORY: All modules must work in BOTH Jest (with require/module.exports) AND GAS (no modules, global scope):
// At top of file - Jest compatibility
if (typeof require !== 'undefined') {
var SomeDependency = require('./SomeDependency'); // Use 'var', not 'const'!
}
// Module definition using IIFE
var ModuleName = (function() {
class ModuleName {
// Implementation
}
return ModuleName;
})();
// At bottom - Jest export
if (typeof module !== 'undefined') {
module.exports = ModuleName; // Export the class/object
}
Why this works:
if (typeof require !== 'undefined') - Only runs in Jest (Node.js environment)
var declarations - Hoisted in GAS, can be redeclared without error
- IIFE pattern - Works in both environments
if (typeof module !== 'undefined') - Only runs in Jest for exports
- In GAS: All files concatenated to global scope,
ModuleName becomes global variable
Implementation Steps
-
Create Core module - src/ModuleNameCore.js
- Write pure JavaScript business logic (NO GAS APIs)
- Use dependency injection pattern (pass dependencies as parameters)
- Follow conditional module pattern above
- Variable name MUST match class/module name
-
Create tests FIRST - test/__tests__/ModuleNameCore.test.js
- Achieve 100% coverage BEFORE writing adapter
- Run:
npm test -- --coverage --collectCoverageFrom='src/ModuleNameCore.js'
- Must show 100% statements/branches/functions/lines
- Only acceptable uncovered:
if (typeof require !== 'undefined') compatibility checks
-
Create type definitions - src/ModuleNameCore.d.ts
- Add JSDoc comments to all functions in
.js file
- Use
declare namespace for namespace objects (e.g., var X = { method1: ..., method2: ... })
- Use
declare class for class constructors (e.g., class X { ... })
- Export default:
export default ModuleNameCore;
-
Update Exports.js - Add module to src/Exports.js
get ModuleNameCore() {
return ModuleNameCore; // Property name MUST match variable name
}
- Run:
npm run validate-exports (must pass - verifies module is defined)
-
MANDATORY: Check VS Code errors
- Run:
get_errors(['src/ModuleNameCore.js', 'src/ModuleNameCore.d.ts'])
- Fix ALL errors before proceeding (zero tolerance)
- CRITICAL: VS Code TypeScript server is MORE STRICT than
tsc --noEmit
- Common fixes:
- Add
/** @type {any[]} */ for array variables
- Add
/** @param {T} paramName */ to all function parameters
- Add error type guards:
const err = error instanceof Error ? error : new Error(String(error));
-
Refactor adapter (if updating existing file) - src/ModuleName.js
- Keep ONLY GAS API calls (SpreadsheetApp, PropertiesService, etc.)
- Delegate all logic to Core module
- Update corresponding
.d.ts file
- Run
get_errors again
-
Final validation
npm run typecheck && npm run validate-exports && npm test
- ALL must pass with ZERO errors
-
Deploy and test
- Manual testing in GAS environment
- Check spreadsheet menu: "Ride Schedulers" > "Show Version" to verify deployment
- Check User Activity Log for any errors
- Test the specific functionality you modified
Completion Criteria
Overview
Optional: Consider internalizing RWGPSLib into the codebase following Core/Adapter pattern.
Decision Criteria
Should only proceed if:
Skip if:
Proposed Architecture
Success Criteria (If Proceeding)
Effort Estimate
4-7 days
Dependencies
CRITICAL: Must complete Phase 1 (Issues #171, #172, #173) BEFORE considering this
Recommendation
Defer this decision until Phase 1 complete. External library is likely fine.
References
Note on This Issue
This is an optional/deferred issue. Recommendation is to skip unless there's a clear need.
The external RWGPSLib library is working well, and internalizing it adds maintenance burden. Only proceed if:
Copilot Execution Checklist
Before Starting
.github/copilot-instructions.mdsections: Architecture Overview, Module Addition Checklist, TypeScript Type CoverageGAS-Compatible Module Pattern
MANDATORY: All modules must work in BOTH Jest (with require/module.exports) AND GAS (no modules, global scope):
Why this works:
if (typeof require !== 'undefined')- Only runs in Jest (Node.js environment)vardeclarations - Hoisted in GAS, can be redeclared without errorif (typeof module !== 'undefined')- Only runs in Jest for exportsModuleNamebecomes global variableImplementation Steps
Create Core module -
src/ModuleNameCore.jsCreate tests FIRST -
test/__tests__/ModuleNameCore.test.jsnpm test -- --coverage --collectCoverageFrom='src/ModuleNameCore.js'if (typeof require !== 'undefined')compatibility checksCreate type definitions -
src/ModuleNameCore.d.ts.jsfiledeclare namespacefor namespace objects (e.g.,var X = { method1: ..., method2: ... })declare classfor class constructors (e.g.,class X { ... })export default ModuleNameCore;Update Exports.js - Add module to
src/Exports.jsnpm run validate-exports(must pass - verifies module is defined)MANDATORY: Check VS Code errors
get_errors(['src/ModuleNameCore.js', 'src/ModuleNameCore.d.ts'])tsc --noEmit/** @type {any[]} */for array variables/** @param {T} paramName */to all function parametersconst err = error instanceof Error ? error : new Error(String(error));Refactor adapter (if updating existing file) -
src/ModuleName.js.d.tsfileget_errorsagainFinal validation
Deploy and test
Completion Criteria
get_errors(['src/'])shows nonenpm run typecheckpasses (ZERO errors)npm run validate-exportspassesnpm testpasses (all tests green)