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
This commit is contained in:
231
runtime/core/strata.sts
Normal file
231
runtime/core/strata.sts
Normal file
@@ -0,0 +1,231 @@
|
||||
/**
|
||||
* 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 };
|
||||
Reference in New Issue
Block a user