@sygnal/sse-core
v2.1.2
Published
Core functionality for Sygnal Site Engine (SSE)
Readme
Sygnal Site Engine (SSE) - Core Library
@sygnal/sse-core is the core utility library for building dynamic, enhanced Webflow projects using the Sygnal Site Engine (SSE) framework. It provides client-side JavaScript utilities for script loading, route dispatching, debugging, and DOM manipulation.
🚀 Quick Start
Installation
npm install @sygnal/sse-coreBasic Usage
import { initSSE, Page, Debug } from '@sygnal/sse-core';
// Initialize SSE with your base URL
initSSE('https://cdn.jsdelivr.net/npm/@sygnal/sse-core@latest');
// Enable debug mode
Debug.enabled = true;
Debug.debug('SSE initialized!');
// Load a script dynamically
Page.loadScript('https://example.com/script.js');📦 Template Repository
To quickly start a new Webflow project with SSE, use our template repository:
sygnaltech/sse-template - A ready-to-use template for building Webflow projects with SSE framework integration.
🎯 Key Features
- Decorator-Based Auto-Registration - Use
@pageand@componentdecorators for automatic discovery - Component Management - Track and retrieve component instances across your application
- Dynamic Script & CSS Loading - Load external resources programmatically
- Route-Based Module Execution - Organize code by page/route with wildcard support
- Extensible Component Initialization - Flexible component initialization with customizable options
- Development/Production Modes - Seamlessly switch between dev and prod environments
- Debug Utilities - Conditional logging with localStorage persistence
- DOM Utilities - Helper functions for element manipulation and traversal
- Request Utilities - Query parameter extraction and response header access
- TypeScript Support - Fully typed with comprehensive type definitions
📚 Core Modules
Base Classes (Recommended)
NEW: SSE provides base classes with automatic Webflow context detection and intuitive lifecycle methods:
import { PageBase, ComponentBase, page, component } from '@sygnal/sse-core';
// Page with automatic Webflow context
@page('/')
export class HomePage extends PageBase {
protected onPrepare(): void {
// Called during <head> load (synchronous)
console.log('Page ID:', this.pageInfo.pageId);
console.log('Collection:', this.pageInfo.collectionId);
}
protected async onLoad(): Promise<void> {
// Called after DOM ready (asynchronous)
console.log('Page path:', this.pageInfo.path);
console.log('Query params:', this.pageInfo.queryParams);
}
}
// Component with element and context
@component('navigation')
export class Navigation extends ComponentBase {
protected onPrepare(): void {
// Called during <head> load
console.log('Component:', this.context.name);
console.log('Element:', this.element);
}
protected async onLoad(): Promise<void> {
// Called after DOM ready
this.element.addEventListener('click', () => {
console.log('Data attrs:', this.context.dataAttributes);
});
}
}Available Context:
Pages get pageInfo:
path,url,hash,queryParams- URL informationdomain- Webflow domain (data-wf-domain)pageId,siteId- Webflow IDslang- Page language (lang attribute)collectionId,itemId,itemSlug- CMS collection data
Components get context:
element- The component's HTMLElementname,id- From data attributesdataAttributes- All data-* attributes
Accessing Page Info from Components:
Components can access the current page via PageBase.getCurrentPage() and read its Webflow context through getPageInfo():
import { ComponentBase, PageBase, component } from '@sygnal/sse-core';
@component('my-component')
export class MyComponent extends ComponentBase {
protected async onLoad(): Promise<void> {
// Get current page reference
const page = PageBase.getCurrentPage();
if (page) {
// Access page info via public accessor
const info = page.getPageInfo();
console.log('Collection ID:', info.collectionId);
console.log('Item Slug:', info.itemSlug);
console.log('Page ID:', info.pageId);
}
// Component works across all pages without knowing page type!
}
}Functional Interactions (FIX)
SSE re-exports the FIX trigger/action system so you can wire declarative interactions without pulling an extra dependency.
import {
initializeFIX,
registerTriggerType,
registerActionType,
registerProgrammaticAction,
EventRegistry,
EventSequential
} from '@sygnal/sse-core';
// Make custom trigger/action types available to FIX attributes
registerTriggerType('submit', TriggerSubmit);
registerActionType('api-call', ActionApiCall);
// Define events and start scanning the DOM
EventRegistry.registerEvent('hero-enter', new EventSequential('hero-enter'));
initializeFIX();
// Optional: actions that are not tied to a DOM element
registerProgrammaticAction('log', 'hero-enter', ActionLog);Use HTML attributes to bind triggers and actions to events:
<button trigger:click="hero-enter" trigger:click:data:section="hero"></button>
<div action:api-call="hero-enter"></div>When to use which:
registerTriggerType(name, TriggerCtor)— expose a trigger class fortrigger:name="event"attributes.registerActionType(name, ActionCtor)— expose an action class foraction:name="event"attributes.registerProgrammaticAction(name, event, ActionCtor)— register an action instance immediately (no element) for code-driven behaviors.
Decorator System
Automatically register pages and components using TypeScript decorators:
import { IModule, page, component } from '@sygnal/sse-core';
// Traditional approach: Implement IModule directly
@page('/')
export class HomePage implements IModule {
setup() {
console.log('Home page setup');
}
async exec() {
console.log('Home page executing');
}
}
// Register a page with wildcard route
@page('/blog/*')
export class BlogPage implements IModule {
async exec() {
const slug = window.location.pathname.replace('/blog/', '');
console.log('Blog post:', slug);
}
}
// Register multiple routes on one page
@page('/about')
@page('/about-us')
@page('/team')
export class AboutPage implements IModule {
async exec() {
console.log('About page loaded');
}
}
// Register a component
@component('my-component')
export class MyComponent implements IModule {
constructor(private elem: HTMLElement) {}
setup() {
console.log('Component setup on:', this.elem);
}
async exec() {
console.log('Component executing');
}
}Note: You can use either approach - extend base classes for automatic context, or implement IModule directly for full control.
Registry Utilities:
import { getAllPages, getRegistryStats, getComponent } from '@sygnal/sse-core';
// Get all registered pages as a routes object
const routes = getAllPages(); // { '/': HomePage, '/blog/*': BlogPage, ... }
// Get registry statistics
const stats = getRegistryStats();
console.log(`${stats.pages} pages, ${stats.components} components`);
// Get a specific component constructor
const ComponentClass = getComponent('my-component');Component Management
Track and retrieve component instances across your application:
import { ComponentManager } from '@sygnal/sse-core';
const manager = new ComponentManager();
// Register component instances
manager.registerComponent('navigation', navInstance);
manager.registerComponent('navigation', mobileNavInstance);
// Retrieve all instances of a type
const navComponents = manager.getComponentsByType<Navigation>('navigation');
// Get all registered component types
const types = manager.getComponentTypes(); // ['navigation', 'slider', 'modal']
// Get total count
const count = manager.getTotalCount();
// Clear all instances
manager.clear();Component Initialization
Automatically discover and initialize components in the DOM:
import { initializeComponents } from '@sygnal/sse-core';
// Basic initialization (uses defaults)
initializeComponents();
// Advanced initialization with options
initializeComponents({
selector: '[data-component]', // Custom selector
attributeName: 'data-component', // Custom attribute name
componentManager: customManager, // Custom ComponentManager instance
logSummary: true, // Log initialization summary
// Custom callbacks
onComponentInit: (name, instance, element) => {
console.log(`Initialized ${name} on`, element);
},
onError: (error, componentName, element) => {
console.error(`Error in ${componentName}:`, error);
},
onUnknownComponent: (componentName, element) => {
console.warn(`Unknown component: ${componentName}`);
}
});HTML Usage:
<div data-component="my-component">
<!-- Component content -->
</div>
<nav data-component="navigation">
<!-- Navigation content -->
</nav>Page Utilities
Static utility methods for page-level operations:
import { Page } from '@sygnal/sse-core';
// Load scripts
Page.Head.loadScript('https://example.com/script.js');
Page.Body.loadScript('https://example.com/body-script.js');
// Load CSS
Page.loadCSS('https://example.com/styles.css');
Page.loadEngineCSS('custom-styles');
// Modify page title
Page.prependToTitle('New Prefix - ');
// DOM traversal
const ancestor = Page.findAncestorWithAttribute(element, 'data-component');
const value = Page.getAncestorAttributeValue(element, 'data-id');
// Unit conversion
const pixels = Page.convertToPixels('2rem'); // Converts rem/em/vh/vw/% to pixels
// Script detection
const scriptUrl = Page.getCurrentScriptUrl();
const baseUrl = Page.getCurrentScriptBaseUrl();
// Fetch utilities
const headers = await Page.getResponseHeaders('https://api.example.com');Route Dispatcher
Organize your code by page or route with automatic execution. Works seamlessly with the decorator system:
import { RouteDispatcher, getAllPages } from '@sygnal/sse-core';
import { Site } from './site';
// Use with decorator-registered pages
const dispatcher = new RouteDispatcher(Site);
dispatcher.routes = getAllPages(); // Auto-populated from @page decorators
// Setup and execute the matching route
dispatcher.setupRoute();
dispatcher.execRoute();Manual Route Registration (alternative):
import { RouteDispatcher } from '@sygnal/sse-core';
// Define a route module
class ProductsPage {
constructor() {
console.log('Products page module created');
}
setup() {
// Setup code runs once when route is matched
console.log('Setting up products page');
}
exec() {
// Exec runs after setup
console.log('Executing products page logic');
}
}
// Create dispatcher and register routes manually
const dispatcher = new RouteDispatcher();
dispatcher.routes = {
'/': HomePage,
'/products': ProductsPage,
'/products/*': ProductDetailsPage, // Wildcard support
'/blog/*': BlogPostPage
};
// Execute the matching route
dispatcher.exec();Script Element
Advanced script loading with configuration options:
import { ScriptElement, ScriptConfig } from '@sygnal/sse-core';
const config = new ScriptConfig({
type: 'module',
async: true,
id: 'my-script',
customAttributes: { 'data-version': '1.0' }
});
const script = new ScriptElement('https://example.com/module.js', config);
script.appendTo(document.head);Debug Utilities
Conditional logging with persistence:
import { Debug } from '@sygnal/sse-core';
// Enable debug mode
Debug.enabled = true;
// Enable persistent debugging (survives page reloads)
Debug.persistentDebug = true;
// Log messages (only when debug is enabled)
Debug.debug('This is a debug message', { data: 'example' });
// Group related logs
Debug.group('API Calls');
Debug.debug('Fetching user data...');
Debug.debug('Response received');
Debug.groupEnd();Request Utilities
Extract query parameters from URLs:
import { Request } from '@sygnal/sse-core';
// Get query parameter from current URL
const userId = Request.getQueryParam('user_id');
// Get parameter from specific URL
const category = Request.getQueryParam('category', 'https://example.com?category=books');Initialization
Initialize the SSE engine with base URL configuration:
import { initSSE } from '@sygnal/sse-core';
// Initialize with CDN base URL
initSSE('https://cdn.jsdelivr.net/npm/@sygnal/[email protected]');
// Access globally
console.log(window.SSE.baseUrl);🔧 Engine Modes
SSE supports two execution modes that can be toggled for development:
Production Mode (Default)
- Loads optimized, production-ready scripts
- Standard execution flow
Development Mode
- Activated via
?engine.mode=devquery parameter - Loads scripts from
dev-srcattributes instead ofsrc - Loads development-specific CSS from
dev-hrefattributes - Persists mode selection in cookies
Usage:
<!-- Script loads from src in prod, dev-src in dev mode -->
<script src="https://cdn.example.com/prod.js"
dev-src="http://localhost:3000/dev.js"></script>
<!-- CSS loads from href in prod, dev-href in dev mode -->
<link rel="stylesheet"
href="https://cdn.example.com/styles.css"
dev-href="http://localhost:3000/styles.css">Toggle modes via query parameter:
- Production:
?engine.mode=prod - Development:
?engine.mode=dev
🏗️ Project Structure
sse-core/
├── src/
│ ├── index.ts # Main entry point and exports
│ ├── init.ts # SSE initialization
│ ├── page.ts # Page utility class
│ ├── page-base.ts # Base class for pages with context
│ ├── component-base.ts # Base class for components with context
│ ├── routeDispatcher.ts # Route-based module execution
│ ├── registry.ts # Decorator system (@page, @component)
│ ├── component-manager.ts # Component instance tracking
│ ├── component-init.ts # Component initialization logic
│ ├── types.ts # Framework type definitions
│ ├── script.ts # ScriptElement and configuration
│ ├── debug.ts # Debug utilities
│ └── request.ts # Request utilities
├── dist/ # Compiled output (JS + .d.ts)
├── package.json
├── tsconfig.json
├── LICENSE # MIT License
├── README.md
└── AGENTS.md # Architecture documentation for AI agents🛠️ Development
Build
npm run buildWatch Mode
npm run watchTypeScript Configuration
The project compiles to ES6 modules with the following settings:
- Target: ES6
- Module: ES6
- Source Maps: Enabled
- Strict Mode: Enabled
- Experimental Decorators: Enabled (required for
@pageand@component) - Output:
dist/
Note: Projects using SSE must enable experimental decorators in their tsconfig.json:
{
"compilerOptions": {
"experimentalDecorators": true,
"emitDecoratorMetadata": true
}
}📖 Documentation
For complete documentation, examples, and guides, visit:
🤝 Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
📄 License
This project is licensed under the MIT License - see the LICENSE file for details.
👤 Author
Michael Wells
- Email: [email protected]
- Website: https://www.sygnal.com
- GitHub: @sygnaltech
🔗 Links
Built with ❤️ by Sygnal Technology Group
