WakaPAC Integration

WakaSync integrates with WakaPAC as a plugin, delivering HTTP responses as messages through msgProc. This gives components a unified event-driven architecture for UI events, network events, and timers — all handled in the same dispatch table.

Plugin Registration

Register WakaSync with WakaPAC once, before creating any components. This gives every component automatic access to a scoped HTTP handle via this.http and automatic request cancellation when the component is destroyed.

<script src="wakapac.js"></script>
<script src="wakasync.js"></script>

<script>
    wakaPAC.use(wakaSync);
</script>

The Basics

Once registered, every component has a this.http handle. Initiate requests with it; responses arrive in msgProc as messages. No promises, no try/catch — everything flows through the same dispatch table as clicks, keypresses, and timers.

wakaPAC('#app', {
    user: null,
    loading: false,
    error: null,

    init() {
        this.loading = true;
        this.http.get('/api/user');
    },

    msgProc(event) {
        switch (event.message) {
            case wakaPAC.MSG_HTTP_SUCCESS:
                this.user = event.detail.data;
                this.loading = false;
                break;

            case wakaPAC.MSG_HTTP_ERROR:
                this.error = event.detail.error.message;
                this.loading = false;
                break;

            case wakaPAC.MSG_HTTP_ABORT:
                this.loading = false;
                break;
        }
    }
});

Message Contract

The plugin delivers three message types to msgProc. Use event.wParam to correlate responses to requests when a component has multiple requests in flight.

Message wParam lParam detail
MSG_HTTP_SUCCESS Request ID 0 { data, url, method, timing }
MSG_HTTP_ERROR Request ID HTTP status (or 0) { error, url, method, status, code }
MSG_HTTP_ABORT Request ID 0 { error, url, method }

For HEAD requests and responses with status 204, 205, or 304, event.detail.data is undefined.

Error Handling via lParam

On MSG_HTTP_ERROR, event.lParam carries the HTTP status code (or 0 for network errors). This allows quick filtering without inspecting the detail object:

case wakaPAC.MSG_HTTP_ERROR:
    switch (event.lParam) {
        case 401:
            window.location = '/login';
            break;

        case 404:
            this.error = 'Not found';
            break;

        case 0:
            this.error = 'Network error — check your connection';
            break;

        default:
            this.error = event.detail.error.message;
            break;
    }
    break;

Error Codes

The event.detail.code property on MSG_HTTP_ERROR provides more granular error classification:

  • HTTP_404, HTTP_500, etc. — HTTP status code errors
  • PARSE_ERROR — Failed to parse response body
  • INTERCEPTOR_ERROR — Request or response interceptor failed
  • INVALID_URL — Malformed URL provided

Cancellations — including timeouts, groupKey/latestOnly superseding, explicit this.http.cancel(), and component destruction — are all delivered as MSG_HTTP_ABORT. The cancellation reason is available via event.detail.error.code:

  • CANCEL_TIMEOUT — Request timed out
  • CANCEL_CANCELLED — Manually cancelled or component destroyed
  • No code (raw AbortError) — Superseded by a newer request in the same group

Request Correlation

Every call to this.http.get(), this.http.post(), etc. returns a request ID. When the response arrives, event.wParam carries the same ID. This lets components distinguish which response belongs to which request:

init() {
    this._ordersReq = this.http.get('/api/orders');
    this._usersReq = this.http.get('/api/users');
},

msgProc(event) {
    if (event.message === wakaPAC.MSG_HTTP_SUCCESS) {
        if (event.wParam === this._ordersReq) {
            this.orders = event.detail.data;
        } else if (event.wParam === this._usersReq) {
            this.users = event.detail.data;
        }
    }
}

HTTP Methods

The this.http handle provides convenience methods for all standard HTTP verbs. Each returns a request ID for correlation.

this.http.get('/api/users');
this.http.post('/api/users', { name: 'Alice' });
this.http.put('/api/users/123', { name: 'Alice Smith' });
this.http.patch('/api/users/123', { lastLogin: new Date().toISOString() });
this.http.delete('/api/users/123');
this.http.head('/api/users/123');

WakaSync sets Content-Type automatically based on the body type: objects are serialized as application/json, strings as text/plain, and Blob/ArrayBuffer as application/octet-stream. For FormData, Content-Type is left to the browser so it can set the correct multipart boundary.

Request Cancellation

Each component's requests are automatically grouped by its pacId. Call this.http.cancel() to cancel all in-flight requests for the component. Requests are also cancelled automatically when the component is destroyed.

For more granular control, pass an explicit groupKey to tag specific requests. When a new request is made with the same groupKey, all previous requests in that group are automatically cancelled:

watch: {
    searchQuery(query) {
        if (!query) {
            this.results = [];
            return;
        }

        // Previous requests with the same groupKey are
        // cancelled automatically when a new one starts.
        this.http.get('/api/search?q=' + encodeURIComponent(query), {
            groupKey: 'search'
        });
        this.searching = true;
    }
}

groupKey vs latestOnly

Both groupKey and latestOnly auto-cancel previous requests, but they work differently. Use groupKey when requests to different URLs should cancel each other — such as search queries where the URL changes with each keystroke. Use latestOnly when repeated requests hit the same URL and only the most recent response matters, such as polling or refreshing. If both are set, groupKey takes precedence.

// groupKey: cancels any previous request tagged 'search', regardless of URL
this.http.get('/api/search?q=' + query, { groupKey: 'search' });

// latestOnly: cancels previous requests to this exact URL
this.http.get('/api/dashboard/stats', { latestOnly: true });

Configuration

HTTP options are resolved in three layers, each overriding the previous:

  1. Global defaults — WakaSync's built-in configuration
  2. Component defaults — per-component options via the http key in wakaPAC's third argument
  3. Per-request options — passed directly to this.http.get(), this.http.post(), etc.

Global Defaults

These apply to all requests unless overridden at the component or request level:

Option Default Description
timeout 30000 Request timeout in milliseconds (0 = no timeout)
retries 0 Number of retry attempts on failure
retryDelay 1000 Base delay between retries in milliseconds (scaled by backoff strategy)
retryBackoff 'exponential' Backoff strategy: 'exponential' (with jitter), 'linear', or 'fixed'
retryBackoffMax 30000 Maximum backoff delay in milliseconds (caps exponential/linear growth)
headers {} Default headers applied to all requests
responseType 'auto' Response parsing: 'json', 'text', 'blob', 'response', 'auto'
validateStatus response.ok Function(response) that determines whether a response is treated as success or error. Default accepts 200–299.

WakaSync retries requests that fail due to network errors, 5xx server errors, or 429 (rate limiting). It does not retry 4xx client errors (like 404, 403) since these indicate the request itself is wrong. Retries use exponential backoff with jitter by default, and respect Retry-After headers on 429 responses. Retries are aborted if the request is cancelled during the delay.

To change global defaults, configure the wakaSync instance before registering:

wakaSync.config.timeout = 10000;
wakaSync.config.retries = 2;

wakaPAC.use(wakaSync);

Component Defaults

Set per-component defaults via the http key in wakaPAC's options argument. These override global defaults for all requests made by that component:

wakaPAC('#quick-lookup', {
    init() { this.http.get('/api/autocomplete?q=test'); },
    msgProc(event) { /* ... */ }
}, { http: { timeout: 5000 } });

wakaPAC('#resilient-dashboard', {
    init() { this.http.get('/api/metrics'); },
    msgProc(event) { /* ... */ }
}, { http: { retries: 3, retryDelay: 2000 } });

Per-Request Options

Override any option on individual requests. These take highest precedence:

Option Description
groupKey Group identifier for batch cancellation (defaults to component's pacId)
latestOnly Auto-cancel previous requests to the same URL (ignored if groupKey is set)
headers Request-specific headers (merged with defaults)
timeout Override timeout for this request
retries Override retry count for this request
retryDelay Override base retry delay for this request
retryBackoff Override backoff strategy for this request
retryBackoffMax Override maximum backoff delay for this request
shouldRetry Custom function(error, attempt, maxAttempts) to control retry logic
responseType Override response parsing for this request
validateStatus Override status validation for this request
credentials, mode, cache, redirect, referrer, referrerPolicy, integrity, keepalive, priority Passed through directly to the underlying fetch() call
// Long timeout for a large export
this.http.get('/api/export/full', { timeout: 60000 });

// Aggressive retries for an unreliable endpoint
this.http.get('/api/unstable', { retries: 3, retryDelay: 1000 });

// Include cookies for cross-origin request
this.http.get('/api/external', { credentials: 'include' });

Advanced Features

Response Type Control

By default, WakaSync auto-detects response type from the Content-Type header. Override this when you need specific handling:

this.http.get('/api/download', { responseType: 'blob' });
this.http.get('/api/page', { responseType: 'text' });

The parsed result arrives in event.detail.data, regardless of response type.

Timing Information

Successful responses include timing metadata in event.detail.timing:

case wakaPAC.MSG_HTTP_SUCCESS:
    console.log('Request took', event.detail.timing.duration, 'ms');
    // timing.startTime, timing.endTime also available
    break;

Request and Response Interceptors

Interceptors transform requests before they're sent and responses after they're received. They apply globally to all requests made through the wakaSync instance. Each returns an unsubscribe function.

// Add auth token to every request
wakaSync.addRequestInterceptor(function(config) {
    config.headers.set('Authorization', 'Bearer ' + getToken());
    return config;
});

// Log all response timing
wakaSync.addResponseInterceptor(function(data, config, timing) {
    console.log(config.method + ' ' + config.url + ' took ' + timing.duration + 'ms');
    return data;
});

// Unsubscribe when no longer needed
const unsub = wakaSync.addRequestInterceptor(fn);
unsub();

Request interceptors may be async — useful for refreshing expired tokens before a request is sent. Interceptors should return the modified config or data. If nothing is returned (undefined), the original value is preserved. Returning any other value — including falsy values like 0, "", false, or null — will be used as the new value.

Request Headers

WakaSync adds X-WakaSync-Request: true and X-WakaSync-Version headers to every request, along with a default Accept: application/json, text/plain, */* header. User-provided headers override these defaults.

Best Practices

  • Register once: Call wakaPAC.use(wakaSync) once before creating components.
  • Use component defaults for shared behavior: Set { http: { retries: 3 } } on components that talk to unreliable endpoints rather than repeating options on every request.
  • Use groupKey for searches: Prevents race conditions — previous requests with the same groupKey are cancelled when a new one starts.
  • Handle all three message types: Always account for MSG_HTTP_SUCCESS, MSG_HTTP_ERROR, and MSG_HTTP_ABORT to keep loading states consistent.
  • Ignore aborts gracefully: Cancelled requests are expected during rapid typing or navigation — don't show error messages for MSG_HTTP_ABORT.
  • Use lParam for HTTP status: Quick status checks via event.lParam are cleaner than digging into event.detail for common cases like 401 or 404.
  • No manual cleanup needed: The plugin automatically cancels in-flight requests when a component is destroyed.
  • Request IDs are optional: Store the return value of this.http.get() only when you need to distinguish between multiple concurrent requests.