abx-spec-behaviors icon indicating copy to clipboard operation
abx-spec-behaviors copied to clipboard

Alternative approach: implement it via browser extension exclusively

Open pirate opened this issue 1 year ago • 0 comments

// manifest.json
{
    "manifest_version": 3,
    "name": "Behavior Extension",
    "version": "1.0",
    "permissions": [
        "tabs",
        "activeTab",
        "storage",
        "debugger",
        "webNavigation",
        "webRequest",
        "scripting"
    ],
    "host_permissions": [
        "<all_urls>"
    ],
    "background": {
        "service_worker": "background.js",
        "type": "module"
    },
    "content_scripts": [{
        "matches": ["<all_urls>"],
        "js": ["behavior_bus.js", "content_script.js"],
        "run_at": "document_start"
    }]
}

// background.js (Service Worker Context)
import { ServiceWorkerBehaviorBus, BehaviorEvent } from './behavior_bus.js';

// Create a BehaviorBus instance for the service worker context
const backgroundBus = new ServiceWorkerBehaviorBus();

// Load behaviors from extension storage or a remote source
let BEHAVIORS = [];
chrome.storage.local.get('behaviors', ({behaviors}) => {
    BEHAVIORS = behaviors || [];
    backgroundBus.attachBehaviors(BEHAVIORS);
});

// Listen for tab lifecycle events and forward them to the BehaviorBus
chrome.webNavigation.onCommitted.addListener((details) => {
    if (details.frameId === 0) {  // main frame only
        backgroundBus.emit({
            type: 'PAGE_SETUP',
            url: details.url,
            tabId: details.tabId
        });
    }
});

chrome.webNavigation.onCompleted.addListener((details) => {
    if (details.frameId === 0) {
        backgroundBus.emit({
            type: 'PAGE_LOAD',
            url: details.url,
            tabId: details.tabId
        });
    }
});

// Handle messages from content scripts
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
    if (message.type === 'BEHAVIOR_EVENT') {
        // Forward events from content script to background BehaviorBus
        backgroundBus.emit(message.event);
    }
    else if (message.type === 'FS_WRITE_FILE') {
        // Handle file system operations using chrome.downloads API
        chrome.downloads.download({
            url: URL.createObjectURL(new Blob([message.content])),
            filename: message.path,
            saveAs: false
        });
    }
});

// content_script.js (Page Context)
import { WindowBehaviorBus, BehaviorEvent } from './behavior_bus.js';

// Create a BehaviorBus instance for the page context
const pageBus = new WindowBehaviorBus();

// Load behaviors from extension storage
chrome.storage.local.get('behaviors', ({behaviors}) => {
    pageBus.attachBehaviors(behaviors || []);
    pageBus.attachContext(window);
});

// Forward all events from page BehaviorBus to background service worker
pageBus.on('*', (event) => {
    chrome.runtime.sendMessage({
        type: 'BEHAVIOR_EVENT',
        event: event.detail
    });
});

// Listen for messages from background script
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
    if (message.type === 'BEHAVIOR_EVENT') {
        // Forward events from background to page BehaviorBus
        pageBus.emit(message.event);
    }
});

// Example implementation of a behavior
const ExtractArticleTextBehavior = {
    name: 'ExtractArticleTextBehavior',
    schema: '[email protected]',
    hooks: {
        window: {
            PAGE_CAPTURE: async (event, BehaviorBus, window) => {
                const text = window.document.body.innerText;
                BehaviorBus.emit({
                    type: 'DISCOVERED_TEXT',
                    selector: 'body',
                    text
                });
                
                // This will be handled by the background script's download API
                BehaviorBus.emit({
                    type: 'FS_WRITE_FILE',
                    path: 'article.txt',
                    content: text
                });
            }
        },
        serviceworker: {
            // Hooks that run in extension background context
            PAGE_SETUP: async (event, BehaviorBus, serviceWorkerGlobal) => {
                // Setup debugger APIs or other extension-specific functionality
                if (event.tabId) {
                    await chrome.debugger.attach({tabId: event.tabId}, '1.3');
                    // ... setup other debugger listeners
                }
            }
        }
    }
};

// behavior_bus.js - Core implementation
export class BehaviorEvent extends CustomEvent {
    constructor(type, detail = {}) {
        super(type, {
            detail: {
                type,
                metadata: {
                    id: crypto.randomUUID(),
                    timestamp: Date.now(),
                    path: []
                },
                ...detail
            }
        });
    }
}

export class BaseBehaviorBus extends EventTarget {
    constructor(behaviors = [], context = null) {
        super();
        this.behaviors = [];
        this.context = null;
        if (behaviors) this.attachBehaviors(behaviors);
        if (context) this.attachContext(context);
    }

    attachContext(context) {
        this.context = context;
    }

    attachBehaviors(behaviors) {
        for (const behavior of behaviors) {
            this.behaviors.push(behavior);
            // Attach event listeners based on behavior hooks
            // ... implementation details
        }
    }

    emit(event) {
        if (!(event instanceof BehaviorEvent)) {
            event = new BehaviorEvent(event.type, event);
        }
        return this.dispatchEvent(event);
    }
}

export class WindowBehaviorBus extends BaseBehaviorBus {
    // Implementation for page context
}

export class ServiceWorkerBehaviorBus extends BaseBehaviorBus {
    // Implementation for service worker context
}

pirate avatar Nov 16 '24 06:11 pirate