Middleware System - TheEditorX/wme-sdk-plus GitHub Wiki
Middleware System
Introduction
The Middleware System is a core component of WME SDK+, providing a powerful mechanism for userscripts to predictably intercept, inspect, modify, and potentially prevent various actions and processes within the Waze Map Editor (WME) environment before they are finalized.
Core Concept:
Middleware allows you to register custom functions (handlers) that run at specific points during WME operations. These points are identified by unique string names called Action Points (e.g., closures.save
). When an action corresponding to an action point occurs, all registered middleware handlers for that point are executed in sequence.
How it Works:
- Registration: You register your handler function using
sdk.Events.registerMiddleware(actionPoint, handler)
. This returns anUnregisterFunction
you can call later to remove your specific handler. - Execution: When the
actionPoint
is triggered, yourhandler
function is called with two arguments:context
andnext
. - Context (
context
): This object provides details about the action being processed:context.actionPoint
: The name of the triggered action point.context.originalData
: An immutable snapshot of the relevant data before any middleware started. Useful for comparison.context.data
: A mutable copy of the data. Your handler can modify this object to influence the final outcome of the action.
- Continuation (
next
): This function passes control to the next handler in the chain, or to the final WME/SDK+ logic. You must callnext()
(orawait next()
for async operations) if you want the action to proceed, or throw an error to prevent further execution. - Modification & Prevention: By modifying
context.data
before callingnext()
, you can alter the action's outcome. By throwing an error (especiallyWmeSDK.Errors.MiddlewarePreventedError
), you can stop the action entirely and prevent subsequent handlers from running.
Availability: The Middleware System is generally available starting with WME SDK+ version 1.1.0. Some action points might have different version requirements.
Benefits:
- Extensibility: Allows adding custom validation, logic, and automation into core WME workflows without modifying the underlying WME code directly.
- Predictability: Provides defined points (
actionPoint
s) to hook into, making script interactions more reliable across WME updates (compared to patching internal functions). - Collaboration: Enables multiple scripts to interact with the same action point in a defined order.
Available Action Points
This section details the specific action points currently available in WME SDK+ that your scripts can hook into using sdk.Events.registerMiddleware
.
closures.save
Action Point: closures.save
Trigger: Triggered just before WME attempts to save pending road (segment) and turn closure changes initiated by the user or potentially by scripts.
Purpose: Allows scripts to:
- Validate closure data before it's committed.
- Modify closure properties (reason, dates, times, etc.) programmatically.
- Prevent the save operation based on custom logic (e.g., policy compliance).
- Log closure save attempts.
- Potentially add or remove closures from the save batch (use with caution).
Context Data Structure (context.data
/ context.originalData
)
The data
object follows the SaveClosuresData
structure:
interface SaveClosuresData {
/**
* An array of closures to be added or updated.
* These objects contain the full closure details.
*/
closures: Array<WmeSDK.Closure | WmeSDK.SegmentClosure | WmeSDK.TurnClosure>;
/**
* An array of closures to be deleted.
* These objects typically only need the ID and type of the closure.
*/
deleteClosures: Array<{
id: string | number; // Closure ID
type: 'SEGMENT' | 'TURN'; // Type identifier
}>;
}
Mutability:
context.data
(including the objects within theclosures
anddeleteClosures
arrays) is mutable. Changes made here will affect the final save operation if differences are detected (see Internal Handling).context.originalData
is an immutable snapshot for comparison.
Example Handler: Ensure all saved closures have a reason prefixed
sdk.Events.registerMiddleware('closures.save', (context, next) => {
const prefix = "[Validated] ";
// Modify closures being added/updated
if (context.data.closures) {
context.data.closures.forEach(closure => {
// Check if reason exists and doesn't already have the prefix
if (closure.reason && !closure.reason.startsWith(prefix)) {
closure.reason = prefix + closure.reason;
} else if (!closure.reason) {
closure.reason = prefix + "Reason added by script"; // Add reason if missing
}
});
}
next();
});
updateRequests.addComment
Action Point: updateRequests.addComment
Trigger: Triggered just before a comment written by the user (or a script) is submitted to be added to a specific Update Request (UR).
Purpose: Allows scripts to:
- Validate comment content before sending (e.g., check length, look for required keywords, block certain phrases).
- Automatically modify comment content (e.g., add a standard signature, template responses, fix common typos).
- Prevent the comment from being added based on custom logic
- Log comment submissions to URs.
Context Data Structure (context.data
/ context.originalData
)
The data
object follows the AddCommentData
structure:
interface AddCommentData {
/** The unique identifier of the Update Request being commented on. */
readonly updateRequestId: number;
/** The content of the comment's message */
message: string;
}
Mutability:
context.data.message
is mutable, allowing handlers to change the comment text before it's sent.context.data.updateRequestId
is readonly within this context, as defined by the interface. You cannot change the target UR for the comment via middleware.context.originalData
is an immutable snapshot of theAddCommentData
before middleware processing began.
Example Handler: Adding a signature to all sent comments in any update request
const unregister = wmeSdk.Events.registerMiddleware(
'updateRequests.addComment',
(context, next) => {
const username = wmeSdk.state.getUserInfo()?.userName ?? 'Waze Map Editor';
context.data.message += '\n\nKind regards,\n' + username;
next();
},
);