Files
strata-compile/runtime/core/strata.sts
Carlos Gutierrez 9e451469f5 git commit -m "feat: initial release of Strata framework v0.1.0
- Static compiler with STRC pattern (Static Template Resolution with
   Compartmentalized Layers)
   - Template syntax: { } interpolation, { s-for }, { s-if/s-elif/s-else
    }
   - File types: .strata, .compiler.sts, .service.sts, .api.sts, .sts,
   .scss
   - CLI tools: strata dev, strata build, strata g (generators)
   - create-strata scaffolding CLI with Pokemon API example
   - Dev server with WebSocket HMR (Hot Module Replacement)
   - Documentation: README, ARCHITECTURE, CHANGELOG, CONTRIBUTING,
   LICENSE
2026-01-16 09:01:29 -05:00

232 lines
5.8 KiB
Plaintext

/**
* Strata Core Runtime
* Main entry point for browser runtime
*/
import { StrataStore, createStore, useStore } from '../store/store.sts';
import { StrataFetch } from '../fetch/fetch.sts';
interface StrataConfig {
encryptionKey?: number[];
apiBaseUrl?: string;
devMode?: boolean;
}
interface BroadcastHandler {
(data: any): void;
}
class Strata {
private worker: SharedWorker | null = null;
private _tabId: string = '';
private messageHandlers: Map<string, Function[]> = new Map();
private broadcastHandlers: Map<string, BroadcastHandler[]> = new Map();
private pendingRequests: Map<string, { resolve: Function; reject: Function }> = new Map();
private config: StrataConfig = {};
private _fetch: StrataFetch | null = null;
get tabId(): string {
return this._tabId;
}
get fetch(): StrataFetch {
if (!this._fetch) {
throw new Error('Strata not initialized. Call strata.init() first.');
}
return this._fetch;
}
async init(config: StrataConfig = {}): Promise<void> {
this.config = config;
// Initialize SharedWorker
if (typeof SharedWorker !== 'undefined') {
this.worker = new SharedWorker(
new URL('../worker/shared-worker.sts', import.meta.url),
{ type: 'module', name: 'strata-worker' }
);
this.worker.port.onmessage = (event) => this.handleWorkerMessage(event.data);
this.worker.port.start();
// Send encryption key if provided
if (config.encryptionKey) {
this.worker.port.postMessage({
type: 'setEncryptionKey',
key: config.encryptionKey,
});
}
// Wait for connection
await this.waitForConnection();
} else {
// Fallback for browsers without SharedWorker support
this._tabId = 'tab_' + Math.random().toString(36).slice(2, 10);
console.warn('SharedWorker not supported, falling back to single-tab mode');
}
// Initialize fetch
this._fetch = new StrataFetch(this);
// Handle page unload
window.addEventListener('beforeunload', () => {
this.disconnect();
});
if (config.devMode) {
(window as any).__STRATA__ = this;
console.log(`Strata initialized (tabId: ${this._tabId})`);
}
}
private waitForConnection(): Promise<void> {
return new Promise((resolve) => {
const handler = (message: any) => {
if (message.type === 'connected') {
this._tabId = message.tabId;
resolve();
}
};
this.addMessageHandler('connected', handler);
});
}
private handleWorkerMessage(message: any) {
// Handle specific message types
switch (message.type) {
case 'connected':
this._tabId = message.tabId;
break;
case 'fetch:response':
case 'fetch:error':
this.handleFetchResponse(message);
break;
case 'store:value':
this.handleStoreValue(message);
break;
case 'broadcast':
this.handleBroadcast(message);
break;
}
// Call registered handlers
const handlers = this.messageHandlers.get(message.type) || [];
for (const handler of handlers) {
handler(message);
}
}
private handleFetchResponse(message: any) {
const pending = this.pendingRequests.get(message.requestId);
if (pending) {
if (message.type === 'fetch:error') {
pending.reject(new Error(message.error));
} else {
pending.resolve(message.data);
}
this.pendingRequests.delete(message.requestId);
}
}
private handleStoreValue(message: any) {
// Handled by store subscriptions
}
private handleBroadcast(message: any) {
const handlers = this.broadcastHandlers.get(message.event) || [];
for (const handler of handlers) {
handler(message.data);
}
// Also call wildcard handlers
const wildcardHandlers = this.broadcastHandlers.get('*') || [];
for (const handler of wildcardHandlers) {
handler({ event: message.event, data: message.data });
}
}
private addMessageHandler(type: string, handler: Function) {
if (!this.messageHandlers.has(type)) {
this.messageHandlers.set(type, []);
}
this.messageHandlers.get(type)!.push(handler);
}
// Public API
sendToWorker(message: any): void {
if (this.worker) {
this.worker.port.postMessage(message);
}
}
async fetchViaWorker(url: string, options?: any): Promise<any> {
const requestId = crypto.randomUUID();
return new Promise((resolve, reject) => {
this.pendingRequests.set(requestId, { resolve, reject });
this.sendToWorker({
type: 'fetch',
url,
options,
requestId,
});
// Timeout after 30 seconds
setTimeout(() => {
if (this.pendingRequests.has(requestId)) {
this.pendingRequests.delete(requestId);
reject(new Error('Request timeout'));
}
}, 30000);
});
}
broadcast(event: string, data?: any, targetTabs?: string[]): void {
this.sendToWorker({
type: 'broadcast',
event,
data,
targetTabs,
});
}
onBroadcast(event: string, handler: BroadcastHandler): () => void {
if (!this.broadcastHandlers.has(event)) {
this.broadcastHandlers.set(event, []);
}
this.broadcastHandlers.get(event)!.push(handler);
// Return unsubscribe function
return () => {
const handlers = this.broadcastHandlers.get(event);
if (handlers) {
const index = handlers.indexOf(handler);
if (index > -1) {
handlers.splice(index, 1);
}
}
};
}
invalidateCache(pattern: string = '*'): void {
this.sendToWorker({
type: 'cache:invalidate',
pattern,
});
}
private disconnect(): void {
this.sendToWorker({ type: 'disconnect' });
}
}
// Singleton instance
export const strata = new Strata();
// Re-exports
export { createStore, useStore };
export type { StrataStore };