@stolbivi/pirojok
Version:
Some minimalistic library used to build chrome extensions, covers some popular Chrome Extension API
225 lines (224 loc) • 9.04 kB
JavaScript
;
/**
* Type definitions and utilities for Chrome extension messaging system.
* Provides a type-safe way to handle request-response style communication between different parts of a Chrome extension.
*/
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Messages = exports.createFromRequest = exports.createAction = exports.createRequest = void 0;
/**
* Creates a request creator function for a specific message type
* @template Payload - Type of the request payload
* @template Response - Type of the expected response
* @param type - Unique identifier for the request type
* @returns A function that creates requests of the specified type
*/
function createRequest(type) {
function create(payload) {
var request = {
type: type,
payload: payload,
};
return __assign(__assign({}, request), { toAction: function () { return request; } });
}
create.toString = function () { return "".concat(type); };
create.type = type;
return create;
}
exports.createRequest = createRequest;
/**
* Creates an action creator function for handling specific message types
* @template Payload - Type of the action payload
* @template Response - Type of the response to be sent back
* @param type - Unique identifier for the action type
* @param handler - Function that will handle the incoming message
* @returns A function that creates actions of the specified type
*/
function createAction(type, handler) {
function create(payload) {
return {
type: type,
payload: payload !== null && payload !== void 0 ? payload : undefined,
handler: handler,
};
}
create.toString = function () { return "".concat(type); };
create.type = type;
return create;
}
exports.createAction = createAction;
/**
* Creates an action creator from an existing request creator
* @template Payload - Type of the payload
* @template Response - Type of the response
* @param requestCreator - The request creator to base the action on
* @param handler - Function that will handle the incoming message
* @returns A function that creates actions matching the request type
*/
function createFromRequest(requestCreator, handler) {
function create(payload) {
return {
type: requestCreator.type,
payload: payload,
handler: handler,
};
}
create.toString = function () { return "".concat(requestCreator.type); };
create.type = requestCreator.type;
return create;
}
exports.createFromRequest = createFromRequest;
/**
* Enhanced messaging API wrapper for Chrome extensions.
* Provides a type-safe way to handle request-response communication between different parts of the extension.
* Automatically cleans up listeners after receiving a response or on failure.
*/
var Messages = /** @class */ (function () {
/**
* Creates a new Messages instance
* @param verbose - Whether to enable verbose logging
*/
function Messages(verbose) {
if (verbose === void 0) { verbose = false; }
this._verbose = verbose;
}
/**
* Sends a request to the extension runtime
* @template Payload - Type of the request payload
* @template Response - Type of the expected response
* @param request - The request to send
* @returns Promise that resolves with the response or rejects with an error
*/
Messages.prototype.request = function (request) {
return this.handleRequest(request, chrome.runtime.connect({ name: request.type }));
};
/**
* Sends a request to a specific tab
* @template Payload - Type of the request payload
* @template Response - Type of the expected response
* @param tabId - ID of the target tab
* @param request - The request to send
* @returns Promise that resolves with the response or rejects with an error
*/
Messages.prototype.requestTab = function (tabId, request) {
return this.handleRequest(request, chrome.tabs.connect(tabId, { name: request.type }));
};
/**
* Handles the request-response cycle for a given port
* @template Payload - Type of the request payload
* @template Response - Type of the expected response
* @param request - The request to send
* @param port - The port to communicate through
* @returns Promise that resolves with the response or rejects with an error
*/
Messages.prototype.handleRequest = function (request, port) {
var _this = this;
return new Promise(function (resolve, reject) {
var onDisconnect = function () {
try {
if (chrome.runtime.lastError) {
reject(chrome.runtime.lastError.message);
}
}
catch (e) {
reject('Error handling runtime error: ' + JSON.stringify(e));
}
if (_this._verbose) {
console.debug('Removing onDisconnect listener for:', request.type);
}
port.onDisconnect.removeListener(onDisconnect);
};
port.onDisconnect.addListener(onDisconnect);
var onMessage = function (response) {
resolve(response);
if (_this._verbose) {
console.debug('Removing onMessage listener for:', request.type);
}
port.onMessage.removeListener(onMessage);
};
port.onMessage.addListener(onMessage);
try {
port.postMessage(request.payload);
}
catch (e) {
reject('Error posting message: ' + JSON.stringify(e));
}
});
};
/**
* Sets up a listener for incoming connections of a specific type
* @template Payload - Type of the incoming message payload
* @template Response - Type of the response to be sent back
* @param actionCreator - Function that creates actions for handling messages
* @returns The connection listener function that was added
*/
Messages.prototype.listen = function (actionCreator) {
var _this = this;
var onConnect = function (port) {
var action = actionCreator();
if (port.name === action.type) {
var onMessage_1 = function (payload) {
action
.handler(payload, port.sender)
.then(function (response) {
try {
port.postMessage(response);
}
catch (e) {
console.error('Error posting response:', JSON.stringify(e));
}
})
.then(function () {
if (_this._verbose) {
console.debug('Listener completed for:', port.name);
}
})
.catch(function (e) { return console.error(e); })
.finally(function () {
if (_this._verbose) {
console.debug('Disconnecting port and removing listener');
}
port.disconnect();
port.onMessage.removeListener(onMessage_1);
});
};
if (_this._verbose) {
console.debug('Adding listener for port:', port.name);
}
port.onMessage.addListener(onMessage_1);
var onDisconnect_1 = function (p) {
if (_this._verbose) {
console.debug('Disconnect detected for:', p.name, 'removing listeners');
}
p.onMessage.removeListener(onMessage_1);
p.onDisconnect.removeListener(onDisconnect_1);
};
port.onDisconnect.addListener(onDisconnect_1);
}
};
chrome.runtime.onConnect.addListener(onConnect);
return onConnect;
};
/**
* Removes a previously added connection listener
* @param onConnect - The listener function to remove
*/
Messages.prototype.removeListener = function (onConnect) {
if (this._verbose) {
console.debug('Removing listener');
}
chrome.runtime.onConnect.removeListener(onConnect);
};
return Messages;
}());
exports.Messages = Messages;