guardz-event
Version:
Type-safe event handling with runtime validation using guardz for unsafe data from 3rd parties
320 lines • 14 kB
JavaScript
;
// ===============================
// Modern, Simplified API
// ===============================
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Status = exports.safeStorageEventListener = exports.safeCustomEventListener = exports.safeEventSourceListener = exports.safeDOMEventListener = exports.safeWebSocketListener = exports.safePostMessageListener = exports.createTypedSafeStorageEvent = exports.createTypedSafeCustomEvent = exports.createTypedSafeEventSource = exports.createTypedSafeWebSocket = exports.createTypedSafePostMessage = exports.createTypedSafeDOMEvent = exports.createSafeEventContext = exports.safe = exports.SafeEventBuilder = exports.safeEvent = exports.safeEventSource = exports.safeWebSocket = exports.safeDOMEvent = exports.EventGuard = void 0;
exports.onEvent = onEvent;
exports.onMessage = onMessage;
exports.onClick = onClick;
exports.onCustom = onCustom;
exports.onStorage = onStorage;
exports.eventGuard = eventGuard;
exports.useEvent = useEvent;
exports.safeHandler = safeHandler;
exports.safeTolerant = safeTolerant;
exports.createTypedHandler = createTypedHandler;
const Status_1 = require("./domain/event/Status");
// ===============================
// 1. Simple onEvent API (Primary)
// ===============================
/**
* Create a safe event handler for any event type
* Usage: window.addEventListener('message', onEvent('message', isChatMessage, { onSuccess: handleData }))
*/
function onEvent(eventType, guard, options) {
const config = {
guard,
tolerance: options.tolerance,
identifier: options.identifier || eventType,
allowedOrigins: options.allowedOrigins,
allowedSources: options.allowedSources,
onError: (error, context) => {
if (context.type === 'security' && options.onSecurityViolation) {
options.onSecurityViolation(context.origin || 'null', error);
}
else if (context.type === 'validation' && options.onTypeMismatch) {
options.onTypeMismatch(error);
}
else if (options.onError) {
options.onError(error);
}
},
};
return (event) => {
let result;
if (event instanceof MessageEvent) {
result = (0, safe_event_never_throws_2.safePostMessage)(config)(event);
}
else if (event instanceof CustomEvent) {
result = (0, safe_event_never_throws_2.safeCustomEvent)(config)(event);
}
else if (event instanceof StorageEvent) {
result = (0, safe_event_never_throws_2.safeStorageEvent)(config)(event);
}
else {
result = (0, safe_event_never_throws_2.safeDOMEvent)(config)(event);
}
if (result.status === Status_1.Status.SUCCESS) {
options.onSuccess(result.data);
}
else if (result.code === 500 && options.onTypeMismatch) {
options.onTypeMismatch(result.message);
}
else if (options.onError) {
options.onError(result.message);
}
};
}
// ===============================
// 2. Auto-inferred Event Handlers
// ===============================
/**
* Create a safe message event handler
* Usage: window.addEventListener('message', onMessage(isChatMessage, { onSuccess: handleData }))
*/
function onMessage(guard, options) {
return onEvent('message', guard, options);
}
/**
* Create a safe DOM event handler
* Usage: element.addEventListener('click', onClick(isClickData, { onSuccess: handleClick }))
*/
function onClick(guard, options) {
return onEvent('click', guard, options);
}
/**
* Create a safe custom event handler
* Usage: element.addEventListener('custom-event', onCustom('custom-event', isCustomData, { onSuccess: handleData }))
*/
function onCustom(eventType, guard, options) {
return onEvent(eventType, guard, options);
}
/**
* Create a safe storage event handler
* Usage: window.addEventListener('storage', onStorage(isStorageData, { onSuccess: handleStorage }))
*/
function onStorage(guard, options) {
return onEvent('storage', guard, options);
}
// ===============================
// 3. Fluent Builder API (Advanced)
// ===============================
class EventGuard {
constructor() {
this.eventType = '';
this.options = {};
}
type(eventType) {
this.eventType = eventType;
return this;
}
validate(guard) {
const newGuard = new EventGuard();
newGuard.eventType = this.eventType;
newGuard.guard = guard;
// Reset options for the new type
newGuard.options = {};
return newGuard;
}
onSuccess(callback) {
this.options.onSuccess = callback;
return this;
}
onError(callback) {
this.options.onError = callback;
return this;
}
onTypeMismatch(callback) {
this.options.onTypeMismatch = callback;
return this;
}
onSecurityViolation(callback) {
this.options.onSecurityViolation = callback;
return this;
}
tolerance(enabled = true) {
this.options.tolerance = enabled;
return this;
}
allowedOrigins(origins) {
this.options.allowedOrigins = origins;
return this;
}
allowedSources(sources) {
this.options.allowedSources = sources;
return this;
}
identifier(id) {
this.options.identifier = id;
return this;
}
build() {
if (!this.guard) {
throw new Error('Guard function is required. Call .validate(guard) first.');
}
if (!this.options.onSuccess) {
throw new Error('Success callback is required. Call .onSuccess(callback) first.');
}
return onEvent(this.eventType, this.guard, this.options);
}
}
exports.EventGuard = EventGuard;
/**
* Create a fluent event guard builder
* Usage: eventGuard().type('message').validate(isChatMessage).onSuccess(handleData).build()
*/
function eventGuard() {
return new EventGuard();
}
/**
* React hook for safe event handling
* Usage: const { data, error } = useEvent('message', isChatMessage, { tolerance: true })
*
* Note: This is a framework-agnostic implementation. For React integration,
* you would need to wrap this with React's useEffect and useState hooks.
*
* Example React implementation:
* ```tsx
* function useSafeEvent<T>(eventType: string, guard: TypeGuardFn<T>, options: UseEventOptions<T> = {}) {
* const [data, setData] = useState<T | null>(null);
* const [error, setError] = useState<string | null>(null);
* const [isLoading, setIsLoading] = useState(false);
*
* useEffect(() => {
* const handler = onEvent(eventType, guard, {
* onSuccess: (data) => {
* setData(data);
* setError(null);
* setIsLoading(false);
* options.onSuccess?.(data);
* },
* onError: (message) => {
* setError(message);
* setIsLoading(false);
* options.onError?.(message);
* },
* onTypeMismatch: options.onTypeMismatch,
* onSecurityViolation: options.onSecurityViolation,
* tolerance: options.tolerance,
* allowedOrigins: options.allowedOrigins,
* allowedSources: options.allowedSources,
* identifier: options.identifier,
* });
*
* // Add event listener
* window.addEventListener(eventType, handler);
*
* // Cleanup
* return () => window.removeEventListener(eventType, handler);
* }, [eventType, guard, options]);
*
* return { data, error, isLoading, refetch: () => setIsLoading(true) };
* }
* ```
*/
function useEvent(eventType, guard, options = {}) {
// Framework-agnostic implementation
// In a real React implementation, you would use:
// const [data, setData] = useState<T | null>(null);
// const [error, setError] = useState<string | null>(null);
// const [isLoading, setIsLoading] = useState(false);
const handler = onEvent(eventType, guard, {
onSuccess: (data) => {
if (options.onSuccess) {
options.onSuccess(data);
}
},
onError: (message) => {
if (options.onError) {
options.onError(message);
}
},
onTypeMismatch: options.onTypeMismatch,
onSecurityViolation: options.onSecurityViolation,
tolerance: options.tolerance,
allowedOrigins: options.allowedOrigins,
allowedSources: options.allowedSources,
identifier: options.identifier,
});
return {
data: null,
error: null,
isLoading: false,
refetch: () => {
// In a real implementation, this would trigger a re-fetch
// For now, it's a no-op
},
};
}
// ===============================
// 5. Utility Functions
// ===============================
/**
* Create a safe event handler with minimal configuration
* Usage: window.addEventListener('message', safeHandler(isChatMessage, handleData))
*/
function safeHandler(guard, onSuccess, onError) {
return onEvent('message', guard, { onSuccess, onError });
}
/**
* Create a safe event handler with tolerance mode
* Usage: window.addEventListener('message', safeTolerant(isChatMessage, handleData, handleError))
*/
function safeTolerant(guard, onSuccess, onError) {
return onEvent('message', guard, { onSuccess, onError, tolerance: true });
}
/**
* Create a typed event handler factory
*/
function createTypedHandler(guard) {
return (options) => onEvent('message', guard, options);
}
// ===============================
// 7. Backward Compatibility
// ===============================
// Re-export existing APIs for backward compatibility (excluding EventResult to avoid conflicts)
var safe_event_never_throws_1 = require("./utils/safe-event-never-throws");
// Legacy API functions
Object.defineProperty(exports, "safeDOMEvent", { enumerable: true, get: function () { return safe_event_never_throws_1.safeDOMEvent; } });
Object.defineProperty(exports, "safeWebSocket", { enumerable: true, get: function () { return safe_event_never_throws_1.safeWebSocket; } });
Object.defineProperty(exports, "safeEventSource", { enumerable: true, get: function () { return safe_event_never_throws_1.safeEventSource; } });
Object.defineProperty(exports, "safeEvent", { enumerable: true, get: function () { return safe_event_never_throws_1.safeEvent; } });
// Builder API
Object.defineProperty(exports, "SafeEventBuilder", { enumerable: true, get: function () { return safe_event_never_throws_1.SafeEventBuilder; } });
Object.defineProperty(exports, "safe", { enumerable: true, get: function () { return safe_event_never_throws_1.safe; } });
// Context API
Object.defineProperty(exports, "createSafeEventContext", { enumerable: true, get: function () { return safe_event_never_throws_1.createSafeEventContext; } });
Object.defineProperty(exports, "createTypedSafeDOMEvent", { enumerable: true, get: function () { return safe_event_never_throws_1.createTypedSafeDOMEvent; } });
Object.defineProperty(exports, "createTypedSafePostMessage", { enumerable: true, get: function () { return safe_event_never_throws_1.createTypedSafePostMessage; } });
Object.defineProperty(exports, "createTypedSafeWebSocket", { enumerable: true, get: function () { return safe_event_never_throws_1.createTypedSafeWebSocket; } });
Object.defineProperty(exports, "createTypedSafeEventSource", { enumerable: true, get: function () { return safe_event_never_throws_1.createTypedSafeEventSource; } });
Object.defineProperty(exports, "createTypedSafeCustomEvent", { enumerable: true, get: function () { return safe_event_never_throws_1.createTypedSafeCustomEvent; } });
Object.defineProperty(exports, "createTypedSafeStorageEvent", { enumerable: true, get: function () { return safe_event_never_throws_1.createTypedSafeStorageEvent; } });
// Ergonomic API functions (legacy)
Object.defineProperty(exports, "safePostMessageListener", { enumerable: true, get: function () { return safe_event_never_throws_1.safePostMessageListener; } });
Object.defineProperty(exports, "safeWebSocketListener", { enumerable: true, get: function () { return safe_event_never_throws_1.safeWebSocketListener; } });
Object.defineProperty(exports, "safeDOMEventListener", { enumerable: true, get: function () { return safe_event_never_throws_1.safeDOMEventListener; } });
Object.defineProperty(exports, "safeEventSourceListener", { enumerable: true, get: function () { return safe_event_never_throws_1.safeEventSourceListener; } });
Object.defineProperty(exports, "safeCustomEventListener", { enumerable: true, get: function () { return safe_event_never_throws_1.safeCustomEventListener; } });
Object.defineProperty(exports, "safeStorageEventListener", { enumerable: true, get: function () { return safe_event_never_throws_1.safeStorageEventListener; } });
__exportStar(require("./utils/additional-sources"), exports);
var Status_2 = require("./domain/event/Status");
Object.defineProperty(exports, "Status", { enumerable: true, get: function () { return Status_2.Status; } });
// Import the functions we need
const safe_event_never_throws_2 = require("./utils/safe-event-never-throws");
//# sourceMappingURL=index.js.map