@zenithcore/core
Version:
Core functionality for ZenithKernel framework
1,546 lines (1,532 loc) • 1.58 MB
JavaScript
var __create = Object.create;
var __getProtoOf = Object.getPrototypeOf;
var __defProp = Object.defineProperty;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __toESM = (mod, isNodeMode, target) => {
target = mod != null ? __create(__getProtoOf(mod)) : {};
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
for (let key of __getOwnPropNames(mod))
if (!__hasOwnProp.call(to, key))
__defProp(to, key, {
get: () => mod[key],
enumerable: true
});
return to;
};
var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, {
get: all[name],
enumerable: true,
configurable: true,
set: (newValue) => all[name] = () => newValue
});
};
var __legacyDecorateClassTS = function(decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function")
r = Reflect.decorate(decorators, target, key, desc);
else
for (var i = decorators.length - 1;i >= 0; i--)
if (d = decorators[i])
r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
// src/core/Messenger.ts
class Messenger {
queues = new Map;
register(id) {
this.queues.set(id, []);
}
unregister(id) {
this.queues.delete(id);
}
send(targetId, message) {
const queue = this.queues.get(targetId);
if (queue)
queue.push(message);
}
receive(id) {
const queue = this.queues.get(id);
return queue ? queue.splice(0, queue.length) : [];
}
}
// src/core/BaseSystem.ts
class BaseSystem {
ecs;
kernel;
constructor(kernel) {
this.kernel = kernel;
this.ecs = kernel.getECS();
}
query(component) {
return this.ecs.getEntitiesWith(component);
}
}
// src/core/Scheduler.ts
var Scheduler;
var init_Scheduler = __esm(() => {
Scheduler = class Scheduler extends BaseSystem {
static id = "Scheduler";
static instance = null;
static getInstance(ecsManager) {
if (!Scheduler.instance) {
Scheduler.instance = new Scheduler(ecsManager);
}
return Scheduler.instance;
}
onLoad() {}
onUnload() {}
update() {
this.tick();
}
updateQueue = new Map;
schedule(id, generatorFactory) {
this.updateQueue.set(id, generatorFactory);
}
unschedule(id) {
this.updateQueue.delete(id);
}
tick() {
for (const [_, factory] of this.updateQueue) {
const gen = factory();
gen.next();
}
}
};
});
// src/core/utils/EventEmitter.ts
class EventEmitter {
events = new Map;
on(event, listener) {
if (!this.events.has(event)) {
this.events.set(event, []);
}
this.events.get(event).push(listener);
return this;
}
once(event, listener) {
const onceWrapper = (...args) => {
listener(...args);
this.off(event, onceWrapper);
};
return this.on(event, onceWrapper);
}
off(event, listener) {
if (!this.events.has(event)) {
return this;
}
const listeners = this.events.get(event);
const index = listeners.indexOf(listener);
if (index !== -1) {
listeners.splice(index, 1);
}
return this;
}
removeAllListeners(event) {
if (event) {
this.events.delete(event);
} else {
this.events.clear();
}
return this;
}
emit(event, ...args) {
if (!this.events.has(event)) {
return false;
}
const listeners = this.events.get(event);
for (const listener of listeners) {
listener(...args);
}
return true;
}
listenerCount(event) {
return this.events.get(event)?.length || 0;
}
listeners(event) {
return this.events.get(event) || [];
}
}
// src/core/signals.ts
class SignalECSComponent {
data;
constructor(data) {
this.data = data;
}
}
class Signal {
_value;
_id;
_name;
_equals;
_subscribers = new Set;
_ecsEntity;
_ecsManager;
_debug;
_scheduler;
_errorHandler;
_disposed = false;
_accessCount = 0;
_updateCount = 0;
_lastAccess;
_lastUpdate;
_createdAt;
constructor(initialValue, options = {}) {
this._value = initialValue;
this._id = ++signalIdCounter;
this._name = options.name;
this._equals = options.equals || Object.is;
this._ecsEntity = options.ecsEntity;
this._ecsManager = options.ecsManager;
this._debug = options.debug || debugMode;
this._scheduler = options.scheduler || "sync";
this._errorHandler = options.errorHandler;
this._createdAt = Date.now();
debugLog(`Created signal ${this._id}`, { name: this._name, value: initialValue, scheduler: this._scheduler });
if (typeof this._ecsEntity === "number" && this._ecsManager && typeof this._ecsManager.addComponent === "function") {
try {
const componentData = {
signalId: this._id,
value: initialValue,
type: "signal",
name: this._name || `signal_${this._id}`,
created: this._createdAt
};
const signalComponentInstance = new SignalECSComponent(componentData);
this._ecsManager.addComponent(this._ecsEntity, SignalECSComponent, signalComponentInstance);
debugLog(`Signal ${this.id} registered with ECS entity ${this._ecsEntity}`);
} catch (error) {
this._handleError(new SignalError(`Failed to register with ECS: ${error.message}`, this._id, this._name));
}
}
}
get value() {
if (this._disposed) {
throw new SignalError(`Cannot access disposed signal ${this._name || this._id}`, this._id, this._name);
}
this._accessCount++;
this._lastAccess = Date.now();
if (currentComputation && !currentComputation.isDisposed) {
this._subscribers.add(currentComputation);
currentComputation.dependencies.add(this);
debugLog(`Tracked dependency: signal ${this._id} (${this._name}) -> computation ${currentComputation.name || "anonymous"}`);
}
return this._value;
}
set value(newValue) {
if (this._disposed) {
throw new SignalError(`Cannot update disposed signal ${this._name || this._id}`, this._id, this._name);
}
if (!this._equals(this._value, newValue)) {
const oldValue = this._value;
this._value = newValue;
this._updateCount++;
this._lastUpdate = Date.now();
debugLog(`Signal ${this._id} (${this._name}) updated`, { oldValue, newValue, subscribers: this._subscribers.size });
this._notifySubscribers();
if (typeof this._ecsEntity === "number" && this._ecsManager && typeof this._ecsManager.addComponent === "function") {
try {
const componentData = {
signalId: this._id,
value: newValue,
lastUpdated: this._lastUpdate,
updateCount: this._updateCount,
type: "signal",
name: this._name || `signal_${this._id}`,
created: this._createdAt
};
const signalComponentInstance = new SignalECSComponent(componentData);
this._ecsManager.addComponent(this._ecsEntity, SignalECSComponent, signalComponentInstance);
debugLog(`Signal ${this.id} updated ECS entity ${this._ecsEntity}`);
} catch (error) {
this._handleError(new SignalError(`Failed to update ECS: ${error.message}`, this._id, this._name));
}
}
}
}
_notifySubscribers() {
if (this._scheduler === "sync" && batchDepth === 0) {
this._flushNotifications();
return;
}
if (!currentBatch) {
currentBatch = new Set;
}
currentBatch.add(this);
if (batchDepth === 0) {
if (this._scheduler === "raf") {
scheduleRafUpdate();
} else {
scheduleMicrotaskUpdate();
}
}
}
_flushNotifications() {
if (this._subscribers.size === 0)
return;
debugLog(`Flushing notifications for signal ${this.id} (${this.name}) to ${this._subscribers.size} subscribers`);
const subscribers = Array.from(this._subscribers);
for (const computation of subscribers) {
if (!computation.isDisposed) {
try {
computation.execute();
} catch (error) {
this._handleError(new SignalError(`Computation error during flush for signal ${this.id}: ${error.message}`, this._id, this._name));
}
}
}
}
peek() {
return this._value;
}
get id() {
return this._id;
}
get name() {
return this._name;
}
get subscriberCount() {
return this._subscribers.size;
}
get isDisposed() {
return this._disposed;
}
get accessCount() {
return this._accessCount;
}
get updateCount() {
return this._updateCount;
}
get lastAccess() {
return this._lastAccess;
}
get lastUpdate() {
return this._lastUpdate;
}
dispose() {
if (this._disposed)
return;
debugLog(`Disposing signal ${this._id}`, { name: this._name, subscribers: this._subscribers.size });
this._disposed = true;
const subscribersToNotify = Array.from(this._subscribers);
this._subscribers.clear();
for (const comp of subscribersToNotify) {
comp.dependencies.delete(this);
if (!comp.isDisposed) {
comp.execute();
}
}
if (typeof this._ecsEntity === "number" && this._ecsManager && typeof this._ecsManager.removeComponent === "function") {
try {
this._ecsManager.removeComponent(this._ecsEntity, SignalECSComponent);
debugLog(`Signal ${this.id} removed from ECS entity ${this._ecsEntity}`);
} catch (error) {
this._handleError(new SignalError(`Failed to cleanup ECS: ${error.message}`, this._id, this._name));
}
}
}
_handleError(error) {
if (this._errorHandler) {
this._errorHandler(error, this);
} else {
console.error(error.message, error.signalId ? `(Signal ID: ${error.signalId})` : "", error.signalName ? `(Name: ${error.signalName})` : "");
}
}
map(mapper, options) {
return new ComputedSignal(() => mapper(this.value), {
name: this._name ? `${this._name}.map` : undefined,
debug: this._debug,
scheduler: this._scheduler,
errorHandler: this._errorHandler ? (err, sig) => this._errorHandler(err, this) : undefined,
...options
});
}
filter(predicate, options) {
return new ComputedSignal(() => predicate(this.value) ? this.value : undefined, {
name: this._name ? `${this._name}.filter` : undefined,
debug: this._debug,
scheduler: this._scheduler,
errorHandler: this._errorHandler ? (err, sig) => this._errorHandler(err, this) : undefined,
...options
});
}
}
class Computation {
dependencies = new Set;
_fn;
_cleanup;
_name;
_disposed = false;
_executing = false;
_executionCount = 0;
_lastExecution;
_errorHandler;
_isEffect = true;
_recentExecutions = [];
_pendingExecution = false;
constructor(fn, options = {}, isEffect = true) {
this._fn = fn;
this._name = options.name;
this._errorHandler = options.errorHandler;
this._isEffect = isEffect;
debugLog(`Created computation ${this._name || "anonymous"}`, { isEffect });
if (!options.defer) {
this.execute();
}
}
execute() {
if (this._disposed) {
debugLog(`Attempted to execute disposed computation ${this._name || "anonymous"}`);
return;
}
if (this._executing && this._isEffect) {
this._pendingExecution = true;
debugLog(`Computation ${this._name || "anonymous"} (effect) already executing, marking for re-execution.`);
return;
}
if (this._isEffect) {
const now = Date.now();
this._recentExecutions = this._recentExecutions.filter((time) => now - time < EXECUTION_TIME_WINDOW);
if (this._recentExecutions.length >= MAX_EFFECT_EXECUTIONS_PER_COMPUTATION) {
debugLog(`Effect execution limit reached (${MAX_EFFECT_EXECUTIONS_PER_COMPUTATION} in ${EXECUTION_TIME_WINDOW}ms), preventing infinite loop for ${this._name || "anonymous"}`);
return;
}
this._recentExecutions.push(now);
}
this._executing = true;
this._executionCount++;
this._lastExecution = Date.now();
debugLog(`Executing computation ${this._name || "anonymous"}`, { count: this._executionCount, recentExecutions: this._recentExecutions.length });
for (const signal of this.dependencies) {
signal._subscribers.delete(this);
}
this.dependencies.clear();
if (this._cleanup) {
try {
this._cleanup();
} catch (error) {
this._handleError(error);
}
this._cleanup = undefined;
}
const prevComputation = currentComputation;
currentComputation = this;
try {
const result = this._fn();
if (typeof result === "function") {
this._cleanup = result;
}
} catch (error) {
this._handleError(error);
} finally {
currentComputation = prevComputation;
this._executing = false;
if (this._pendingExecution && !this._disposed) {
this._pendingExecution = false;
debugLog(`Computation ${this._name || "anonymous"} has pending execution, running again.`);
this.execute();
}
}
}
dispose() {
if (this._disposed)
return;
this._disposed = true;
debugLog(`Disposing computation ${this._name || "anonymous"}`);
for (const signal of this.dependencies) {
signal._subscribers.delete(this);
}
this.dependencies.clear();
if (this._cleanup) {
try {
this._cleanup();
} catch (e) {
this._handleError(e);
}
this._cleanup = undefined;
}
}
get name() {
return this._name;
}
get isDisposed() {
return this._disposed;
}
get isExecuting() {
return this._executing;
}
get executionCount() {
return this._executionCount;
}
get lastExecution() {
return this._lastExecution;
}
get dependencyCount() {
return this.dependencies.size;
}
_handleError(error) {
if (this._errorHandler) {
this._errorHandler(error);
} else {
console.error(`Computation error in ${this._name || "anonymous"}:`, error);
}
}
}
function signal(initialValue, options) {
return new Signal(initialValue, options);
}
function computed(fn, options) {
return new ComputedSignal(fn, options);
}
function asyncSignal(loadFn, options) {
return new AsyncSignal(undefined, loadFn, { initialState: "idle", ...options });
}
function asyncSignalWithInitial(initialValue, loadFn, options) {
return new AsyncSignal(initialValue, loadFn, { initialState: "idle", ...options });
}
function effect(fn, options) {
return new Computation(fn, options, true);
}
function resource(loadFn, options) {
const asyncSig = new AsyncSignal(undefined, loadFn, { ...options, initialState: "loading" });
const valueSignal = computed(() => asyncSig.value, { name: options?.name ? `${options.name}.$value` : undefined, scheduler: options?.scheduler || "sync" });
const loadingSignal = computed(() => asyncSig.loading, { name: options?.name ? `${options.name}.$loadingState` : undefined, scheduler: "sync" });
const errorSignal = computed(() => asyncSig.error, { name: options?.name ? `${options.name}.$errorState` : undefined, scheduler: "sync" });
return [
valueSignal,
{
loading: loadingSignal,
error: errorSignal,
reload: () => asyncSig.reload()
}
];
}
function batch(fn) {
if (batchDepth > 0) {
debugLog("Nested batch call");
return fn();
}
batchDepth++;
debugLog(`Batch started (depth: ${batchDepth})`);
const isOuterMostBatch = currentBatch === null;
if (isOuterMostBatch) {
currentBatch = new Set;
}
let result;
try {
result = fn();
if (batchDepth === 1) {
debugLog(`Top-level batch finished, flushing updates...`);
flushUpdates();
}
} finally {
batchDepth--;
debugLog(`Batch ended (depth: ${batchDepth})`);
if (batchDepth === 0) {
if (isOuterMostBatch && currentBatch !== null) {
currentBatch = null;
debugLog("Global batch context explicitly cleared post-outermost batch.");
}
}
}
return result;
}
function untrack(fn) {
const prevComputation = currentComputation;
currentComputation = null;
try {
return fn();
} finally {
currentComputation = prevComputation;
}
}
function ecsSignal(initialValue, ecsEntity, ecsManager, options) {
return new Signal(initialValue, {
...options,
ecsEntity,
ecsManager
});
}
function signalObject(obj, options) {
const result = {};
for (const [key, value] of Object.entries(obj)) {
result[key] = signal(value, {
...options,
name: options?.name ? `${options.name}.${key}` : key
});
}
return result;
}
function createStore(initialState, options) {
const signals = signalObject(initialState, options);
const store = {};
for (const [key, signalInstance] of Object.entries(signals)) {
Object.defineProperty(store, key, {
get: () => signalInstance.value,
set: (value) => {
signalInstance.value = value;
},
enumerable: true,
configurable: true
});
store[`$${key}`] = signalInstance;
}
return store;
}
function getCurrentComputation() {
return currentComputation;
}
function isInBatch() {
return batchDepth > 0;
}
function setDebugMode(enabled) {
debugMode = enabled;
debugLog(`Debug mode ${enabled ? "enabled" : "disabled"}`);
}
function getSignalRegistry() {
console.warn("getSignalRegistry: Global signal registry not implemented in this version.");
return new Map;
}
function resolve(value) {
return value instanceof Signal ? value.value : value;
}
function isSignal(value) {
return value instanceof Signal;
}
function derived(source, mapper, options) {
return computed(() => mapper(source.value), {
...options,
name: options?.name || (source.name ? `${source.name}.derived` : undefined)
});
}
function derivedWithSetter(source, getter, setter, options) {
const derivedSignal = signal(getter(source.peek()), options);
let internalUpdate = false;
effect(() => {
if (internalUpdate)
return;
const newDerivedValue = getter(source.value);
internalUpdate = true;
derivedSignal.value = newDerivedValue;
internalUpdate = false;
});
effect(() => {
if (internalUpdate)
return;
const newSourceValue = setter(derivedSignal.value, source.peek());
internalUpdate = true;
source.value = newSourceValue;
internalUpdate = false;
});
return derivedSignal;
}
function combine(signals, options) {
return computed(() => signals.map((s) => s.value), options);
}
function fromPromise(promise, options) {
const asyncOptions = {
initialState: "loading",
scheduler: "sync",
...options
};
return new AsyncSignal(undefined, () => promise, asyncOptions);
}
function fromEvent(emitter, event, initialValue, mapper, options) {
const sig = signal(initialValue, options);
const handler = (data) => {
sig.value = mapper ? mapper(data) : data;
};
emitter.addEventListener(event, handler);
return sig;
}
var currentComputation = null, currentBatch = null, batchDepth = 0, scheduledUpdate = null, MAX_EFFECT_EXECUTIONS_PER_COMPUTATION = 1000, EXECUTION_TIME_WINDOW = 100, signalIdCounter = 0, debugMode = false, debugLog = (message, ...args) => {
if (debugMode)
console.log(`[Signals] ${message}`, ...args);
}, SignalError, scheduleRafUpdate = () => {
if (scheduledUpdate === null) {
scheduledUpdate = requestAnimationFrame(() => {
scheduledUpdate = null;
flushUpdates();
});
}
}, scheduleMicrotaskUpdate = () => {
if (scheduledUpdate === null) {
scheduledUpdate = 1;
Promise.resolve().then(() => {
if (scheduledUpdate === 1) {
scheduledUpdate = null;
flushUpdates();
}
});
}
}, flushUpdates = () => {
if (!currentBatch)
return;
const uniqueComputationsToRun = new Set;
const batchToProcess = Array.from(currentBatch);
currentBatch = null;
for (const signal of batchToProcess) {
for (const comp of signal._subscribers) {
if (!comp.isDisposed) {
uniqueComputationsToRun.add(comp);
}
}
}
debugLog(`Flushing updates for ${uniqueComputationsToRun.size} computations from ${batchToProcess.length} signals.`);
for (const comp of uniqueComputationsToRun) {
if (!comp.isDisposed) {
comp.execute();
}
}
}, AsyncSignal, ComputedSignal;
var init_signals = __esm(() => {
SignalError = class SignalError extends Error {
signalId;
signalName;
constructor(message, signalId, signalName) {
super(message);
this.signalId = signalId;
this.signalName = signalName;
this.name = "SignalError";
}
};
AsyncSignal = class AsyncSignal extends Signal {
_loadingSignal;
_errorSignal;
_timeout;
_retryCount;
_retryDelay;
_currentRetry = 0;
_loadFn;
_currentLoadPromise = null;
constructor(initialValue, loadFn, options = {}) {
const baseOptions = {
name: options.name,
ecsEntity: options.ecsEntity,
ecsManager: options.ecsManager,
debug: options.debug,
scheduler: options.scheduler || "sync",
errorHandler: options.errorHandler,
equals: options.equals
};
super(initialValue, baseOptions);
this._loadFn = loadFn;
this._timeout = options.timeout;
this._retryCount = options.retryCount || 0;
this._retryDelay = options.retryDelay || 1000;
const isLoadingInitially = options.initialState === "loading";
this._loadingSignal = signal(isLoadingInitially, { name: options.name ? `${options.name}.$loading` : undefined, scheduler: "sync" });
this._errorSignal = signal(null, { name: options.name ? `${options.name}.$error` : undefined, scheduler: "sync" });
if (isLoadingInitially) {
this.reload().catch((err) => {
debugLog(`AsyncSignal ${this.id} (${this.name}) initial reload() call resulted in error: `, err);
if (this._errorSignal.peek() === null) {
this._errorSignal.value = err instanceof Error ? err : new SignalError(String(err), this.id, this.name);
}
if (this._loadingSignal.peek()) {
this._loadingSignal.value = false;
}
});
}
}
get loading() {
return this._loadingSignal.value;
}
get error() {
return this._errorSignal.value;
}
get isSuccess() {
return !this._loadingSignal.peek() && !this._errorSignal.peek() && this.peek() !== undefined;
}
async reload() {
if (this._loadingSignal.peek() && this._currentLoadPromise) {
debugLog(`AsyncSignal ${this.id} (${this.name}) reload called while already loading.`);
return this._currentLoadPromise;
}
this._loadingSignal.value = true;
this._errorSignal.value = null;
this._currentRetry = 0;
this._currentLoadPromise = this._attemptLoad();
return this._currentLoadPromise;
}
async _attemptLoad() {
let result = undefined;
let caughtError = null;
debugLog(`AsyncSignal ${this.id} (${this.name}) attempting load (attempt ${this._currentRetry + 1}).`);
try {
let loadPromise = this._loadFn();
if (this._timeout) {
const timeoutPromise = new Promise((_, reject) => {
setTimeout(() => reject(new SignalError(`Timeout after ${this._timeout}ms`, this.id, this.name)), this._timeout);
});
loadPromise = Promise.race([loadPromise, timeoutPromise]);
}
result = await loadPromise;
} catch (error) {
caughtError = error instanceof Error ? error : new SignalError(String(error), this.id, this.name);
}
const updateSignalStates = () => {
if (this.isDisposed) {
debugLog(`AsyncSignal ${this.id} (${this.name}) disposed during load/retry. Aborting state update.`);
return;
}
if (caughtError) {
if (this._currentRetry < this._retryCount) {
this._currentRetry++;
debugLog(`AsyncSignal ${this.id} (${this.name}) retrying load... (${this._currentRetry}/${this._retryCount})`, { error: caughtError });
this._loadingSignal.value = true;
this._errorSignal.value = null;
setTimeout(() => {
if (!this.isDisposed)
this._attemptLoad();
}, this._retryDelay * Math.pow(2, this._currentRetry - 1));
} else {
debugLog(`AsyncSignal ${this.id} (${this.name}) load failed after all retries.`, { error: caughtError });
this._errorSignal.value = caughtError;
this._loadingSignal.value = false;
}
} else {
debugLog(`AsyncSignal ${this.id} (${this.name}) loaded successfully.`, { value: result });
this.value = result;
this._errorSignal.value = null;
this._loadingSignal.value = false;
}
};
if (this._scheduler === "sync" && batchDepth === 0) {
updateSignalStates();
} else {
batch(updateSignalStates);
}
if (!caughtError || this._currentRetry >= this._retryCount) {
this._currentLoadPromise = null;
}
}
dispose() {
this._loadingSignal.dispose();
this._errorSignal.dispose();
super.dispose();
}
};
ComputedSignal = class ComputedSignal extends Signal {
_computation;
_fn;
constructor(fn, options = {}) {
const { defer, errorHandler, ...baseSignalOptions } = options;
super(undefined, baseSignalOptions);
this._fn = fn;
let adaptedComputationErrorHandler = undefined;
if (errorHandler) {
adaptedComputationErrorHandler = (err) => errorHandler(err, this);
} else if (this._errorHandler) {
adaptedComputationErrorHandler = (err) => this._errorHandler(err, this);
}
this._computation = new Computation(() => {
const newValue = this._fn();
if (this._computation.executionCount === 1 && this._value === undefined && newValue !== undefined || !this._equals(this._value, newValue)) {
this._value = newValue;
this._notifySubscribers();
}
}, {
defer: true,
name: options.name || (this._fn && this._fn.name ? `computed(${this._fn.name})` : "computed"),
errorHandler: adaptedComputationErrorHandler
}, false);
if (!defer) {
this._computation.execute();
}
}
get value() {
if (this._disposed) {
throw new SignalError(`Cannot access disposed computed signal ${this.name || this.id}`);
}
if (this._computation.executionCount === 0 && !this._computation.isDisposed) {
debugLog(`Computed signal ${this.id} (${this.name}) accessed, running initial/deferred computation.`);
this._computation.execute();
}
return super.value;
}
set value(_) {
throw new SignalError("Cannot set value of computed signal", this.id, this.name);
}
dispose() {
this._computation.dispose();
super.dispose();
}
};
});
// src/core/ECSManager.ts
var exports_ECSManager = {};
__export(exports_ECSManager, {
Query: () => Query,
ECSManager: () => ECSManager
});
class SparseSet {
sparse;
dense;
_count = 0;
constructor(capacity = DEFAULT_ENTITY_POOL_SIZE) {
this.sparse = new Uint32Array(capacity);
this.dense = new Uint32Array(capacity);
}
add(id) {
if (id >= this.sparse.length) {
this.resize(Math.max(id + 1, this.sparse.length * 2));
}
if (this.has(id))
return false;
this.dense[this._count] = id;
this.sparse[id] = this._count;
this._count++;
return true;
}
remove(id) {
if (!this.has(id))
return false;
const denseIndex = this.sparse[id];
const lastId = this.dense[this._count - 1];
this.dense[denseIndex] = lastId;
this.sparse[lastId] = denseIndex;
this.dense[this._count - 1] = 0;
this._count--;
return true;
}
has(id) {
return id < this.sparse.length && this.sparse[id] < this._count && this.dense[this.sparse[id]] === id;
}
get count() {
return this._count;
}
get entities() {
return this.dense.subarray(0, this._count);
}
resize(newCapacity) {
logDebug(`SparseSet resizing sparse from ${this.sparse.length} to ${newCapacity}`);
const newSparse = new Uint32Array(newCapacity);
newSparse.set(this.sparse);
this.sparse = newSparse;
if (newCapacity > this.dense.length) {
logDebug(`SparseSet resizing dense from ${this.dense.length} to ${newCapacity}`);
const newDense = new Uint32Array(newCapacity);
newDense.set(this.dense);
this.dense = newDense;
}
}
}
class Query {
_id;
_required = [];
_excluded = [];
_entities;
_dirty = true;
_bitflags = new Map;
_requiredMask = 0;
_excludedMask = 0;
constructor(id, required = [], excluded = []) {
this._id = id;
this._required = [...required];
this._excluded = [...excluded];
this._entities = new SparseSet;
}
get id() {
return this._id;
}
get required() {
return [...this._required];
}
get excluded() {
return [...this._excluded];
}
get entities() {
return this._entities.entities;
}
get count() {
return this._entities.count;
}
setBitflags(componentName, flag) {
this._bitflags.set(componentName, flag);
this._recalculateMasks();
this._dirty = true;
}
_recalculateMasks() {
this._requiredMask = 0;
for (const compName of this._required) {
this._requiredMask |= this._bitflags.get(compName) || 0;
}
this._excludedMask = 0;
for (const compName of this._excluded) {
this._excludedMask |= this._bitflags.get(compName) || 0;
}
}
matches(entityMask) {
const matchesRequired = (entityMask & this._requiredMask) === this._requiredMask;
const matchesExcluded = (entityMask & this._excludedMask) === 0;
return matchesRequired && matchesExcluded;
}
addEntity(entityId) {
this._entities.add(entityId);
}
removeEntity(entityId) {
this._entities.remove(entityId);
}
hasEntity(entityId) {
return this._entities.has(entityId);
}
markDirty() {
this._dirty = true;
}
isDirty() {
return this._dirty;
}
markClean() {
this._dirty = false;
}
}
var DEFAULT_ENTITY_POOL_SIZE = 1e4, DEBUG_ECS = false, logDebug = (message, ...args) => {
if (DEBUG_ECS)
console.log(`[ECSManager DEBUG] ${message}`, ...args);
}, ECSManager;
var init_ECSManager = __esm(() => {
init_signals();
ECSManager = class ECSManager extends EventEmitter {
componentTypes = new Map;
componentSerializers = new Map;
componentDeserializers = new Map;
components = new Map;
nextEntityId = 0;
removedEntities = [];
entities = new SparseSet(DEFAULT_ENTITY_POOL_SIZE);
entityMasks = new Map;
entityComponentNames = new Map;
componentNameBitflags = new Map;
nextComponentBitflag = 1;
queries = new Map;
kernelRef;
systems = [];
entityNames = new Map;
constructor() {
super();
}
setKernel(kernel) {
this.kernelRef = kernel;
}
getSystems() {
return [...this.systems];
}
get kernel() {
if (!this.kernelRef)
throw new Error("ECSManager: Kernel reference not set.");
return this.kernelRef;
}
addSystem(system) {
if (!system) {
console.warn("[ECSManager] Attempted to add undefined system");
return;
}
this.systems.push(system);
if (typeof system.init === "function") {
system.init();
} else {
logDebug(`System ${system.constructor.name} has no init method`);
}
}
registerSystem(system) {
if (!system) {
console.warn("[ECSManager] Attempted to register undefined system");
return;
}
if (this.systems.includes(system)) {
console.warn("[ECSManager] System already registered", system);
return;
}
this.addSystem(system);
logDebug(`Registered system: ${system.constructor.name}`);
}
updateSystems() {
for (const system of this.systems) {
system.update();
}
}
createEntity() {
const entityId = this.removedEntities.length > 0 ? this.removedEntities.pop() : this.nextEntityId++;
this.entities.add(entityId);
this.entityMasks.set(entityId, 0);
this.entityComponentNames.set(entityId, new Set);
logDebug(`Entity created: ${entityId}`);
this.emit("entityCreated", entityId);
return entityId;
}
destroyEntity(entity) {
if (!this.entities.has(entity)) {
logDebug(`Attempted to destroy non-existent entity: ${entity}`);
return;
}
const componentNames = this.entityComponentNames.get(entity);
if (componentNames) {
[...componentNames].forEach((compName) => this.removeComponentByTypeName(entity, compName, true));
}
this.entities.remove(entity);
this.entityMasks.delete(entity);
this.entityComponentNames.delete(entity);
this.removedEntities.push(entity);
logDebug(`Entity destroyed: ${entity}`);
this.emit("entityRemoved", entity);
}
_getComponentTypeName(type) {
return type.typeName || type.name;
}
addComponent(entity, type, instance) {
const typeName = this._getComponentTypeName(type);
if (!this.entities.has(entity)) {
logDebug(`Entity ${entity} not tracked, adding implicitly with component ${typeName}.`);
this.entities.add(entity);
this.entityMasks.set(entity, 0);
this.entityComponentNames.set(entity, new Set);
if (entity >= this.nextEntityId) {
this.nextEntityId = entity + 1;
}
}
let componentMap = this.components.get(typeName);
if (!componentMap) {
componentMap = new Map;
this.components.set(typeName, componentMap);
}
const existingInstance = componentMap.get(entity);
componentMap.set(entity, instance);
if (!this.componentNameBitflags.has(typeName)) {
const bitflag2 = this.nextComponentBitflag;
this.nextComponentBitflag <<= 1;
if (this.nextComponentBitflag === 0) {
console.error("ECSManager: Component bitflag overflow! Too many component types.");
}
this.componentNameBitflags.set(typeName, bitflag2);
logDebug(`Assigned bitflag ${bitflag2} to component type ${typeName}`);
this.queries.forEach((query) => query.markDirty());
}
const entityComponentsSet = this.entityComponentNames.get(entity);
const hadComponentBefore = entityComponentsSet.has(typeName);
entityComponentsSet.add(typeName);
const bitflag = this.componentNameBitflags.get(typeName);
const currentMask = this.entityMasks.get(entity);
const newMask = currentMask | bitflag;
if (currentMask !== newMask) {
this.entityMasks.set(entity, newMask);
this._updateEntityQueries(entity);
}
logDebug(`Component ${typeName} ${existingInstance ? "updated on" : "added to"} entity ${entity}. Mask: ${newMask.toString(2)}`);
this.emit("componentAdded", { entity, componentType: typeName, instance });
if (typeName === "EntityNameComponent" && instance && typeof instance.name === "string") {
const entityName = instance.name;
if (this.entityNames.has(entityName) && this.entityNames.get(entityName) !== entity) {
logDebug(`Warning: Entity name "${entityName}" was already registered to entity ${this.entityNames.get(entityName)}. Re-registering to ${entity}.`);
}
this.entityNames.set(entityName, entity);
logDebug(`Entity ${entity} named "${entityName}"`);
}
return instance;
}
updateComponent(entity, type, updatedData) {
const typeName = this._getComponentTypeName(type);
const componentMap = this.components.get(typeName);
if (!componentMap || !componentMap.has(entity)) {
logDebug(`Update failed: Component ${typeName} not found on entity ${entity}.`);
return false;
}
const currentInstance = componentMap.get(entity);
if (currentInstance instanceof SignalECSComponent && typeof currentInstance.data === "object" && currentInstance.data !== null) {
Object.assign(currentInstance.data, updatedData);
logDebug(`Component ${typeName} (SignalECSComponent data) updated on entity ${entity}.`);
} else if (typeof currentInstance === "object" && currentInstance !== null) {
Object.assign(currentInstance, updatedData);
logDebug(`Component ${typeName} (generic object) updated on entity ${entity}.`);
} else {
console.warn(`ECSManager: Component ${typeName} on entity ${entity} is not an updatable object.`);
return false;
}
this.emit("componentUpdated", { entity, componentType: typeName, instance: currentInstance });
return true;
}
getComponent(entity, type) {
const typeName = this._getComponentTypeName(type);
const instance = this.components.get(typeName)?.get(entity);
logDebug(`getComponent ${typeName} for entity ${entity}: ${instance ? "found" : "not found"}`);
return instance;
}
removeComponent(entity, type) {
const typeName = this._getComponentTypeName(type);
return this.removeComponentByTypeName(entity, typeName);
}
removeComponentByTypeName(entity, typeName, isDestroyingEntity = false) {
if (!this.entities.has(entity)) {
logDebug(`Remove failed: Entity ${entity} not found.`);
return false;
}
const componentMap = this.components.get(typeName);
const existedInMap = componentMap?.delete(entity) || false;
if (componentMap && componentMap.size === 0) {
this.components.delete(typeName);
}
const entityComponents = this.entityComponentNames.get(entity);
let existedInEntitySet = false;
if (entityComponents?.delete(typeName)) {
existedInEntitySet = true;
const bitflag = this.componentNameBitflags.get(typeName);
if (bitflag !== undefined) {
const currentMask = this.entityMasks.get(entity) || 0;
this.entityMasks.set(entity, currentMask & ~bitflag);
if (!isDestroyingEntity) {
this._updateEntityQueries(entity);
}
}
logDebug(`Component ${typeName} removed from entity ${entity}.`);
this.emit("componentRemoved", { entity, componentType: typeName });
}
return existedInMap || existedInEntitySet;
}
_updateEntityQueries(entity) {
const mask = this.entityMasks.get(entity) || 0;
logDebug(`Updating queries for entity ${entity} with mask ${mask.toString(2)}`);
this.queries.forEach((query) => {
const wasInQuery = query.hasEntity(entity);
const shouldBeInQuery = query.matches(mask);
if (shouldBeInQuery && !wasInQuery) {
query.addEntity(entity);
logDebug(`Entity ${entity} added to query ${query.id}`);
} else if (!shouldBeInQuery && wasInQuery) {
query.removeEntity(entity);
logDebug(`Entity ${entity} removed from query ${query.id}`);
}
});
}
removeSystem(system) {
const index = this.systems.indexOf(system);
if (index !== -1)
this.systems.splice(index, 1);
logDebug(`System removed: ${system.constructor.name}`);
}
getEntitiesWith(type) {
const typeName = this._getComponentTypeName(type);
const map = this.components.get(typeName);
return map ? Array.from(map.entries()) : [];
}
defineQuery(id, requiredNames = [], excludedNames = []) {
if (this.queries.has(id))
throw new Error(`Query ${id} already exists`);
const query = new Query(id, requiredNames, excludedNames);
logDebug(`Defining query ${id}: Required [${requiredNames.join(", ")}], Excluded [${excludedNames.join(", ")}]`);
const allQueryComponentNames = new Set([...requiredNames, ...excludedNames]);
allQueryComponentNames.forEach((compName) => {
if (!this.componentNameBitflags.has(compName)) {
const bitflag = this.nextComponentBitflag;
this.nextComponentBitflag <<= 1;
if (this.nextComponentBitflag === 0)
console.error("ECSManager: Component bitflag overflow!");
this.componentNameBitflags.set(compName, bitflag);
logDebug(`Assigned new bitflag ${bitflag} to component type ${compName} for query ${id}`);
}
query.setBitflags(compName, this.componentNameBitflags.get(compName));
});
for (let i = 0;i < this.entities.count; i++) {
const entityId = this.entities.entities[i];
const mask = this.entityMasks.get(entityId) || 0;
if (query.matches(mask))
query.addEntity(entityId);
}
this.queries.set(id, query);
logDebug(`Query ${id} defined with ${query.count} initial entities.`);
return query;
}
getQuery(id) {
return this.queries.get(id);
}
removeQuery(id) {
this.queries.delete(id);
logDebug(`Query ${id} removed.`);
}
dumpComponentMap() {
return this.components;
}
getEntitiesWithQuery(queryId) {
const query = this.queries.get(queryId);
if (!query)
throw new Error(`Query ${queryId} does not exist`);
if (query.isDirty()) {
logDebug(`Query ${queryId} was dirty, re-evaluating.`);
this.entities.entities.forEach((entityId) => {
const mask = this.entityMasks.get(entityId) || 0;
const matches = query.matches(mask);
const hasEntityInQuery = query.hasEntity(entityId);
if (matches && !hasEntityInQuery)
query.addEntity(entityId);
else if (!matches && hasEntityInQuery)
query.removeEntity(entityId);
});
query.markClean();
}
return Array.from(query.entities);
}
hasComponent(entity, type) {
const typeName = this._getComponentTypeName(type);
return this.entityComponentNames.get(entity)?.has(typeName) || false;
}
getEntityComponents(entity) {
return this.entities.has(entity) ? Array.from(this.entityComponentNames.get(entity)) : [];
}
registerComponentType(typeName, componentType, serializer, deserializer) {
if (componentType && typeof componentType === "function") {
if (!componentType.typeName) {
componentType.typeName = typeName;
}
this.componentTypes.set(typeName, componentType);
if (DEBUG_ECS) {
console.log(`[ECSManager] Registered component type: ${typeName}`);
}
if (serializer) {
this.componentSerializers.set(typeName, serializer);
}
if (deserializer) {
this.componentDeserializers.set(typeName, deserializer);
}
} else {
throw new Error(`Invalid component type provided for ${typeName}`);
}
}
getComponentType(typeName) {
return this.componentTypes.get(typeName);
}
serializeComponent(typeName, component) {
const serializer = this.componentSerializers.get(typeName);
if (serializer) {
return serializer(component);
}
return component;
}
deserializeComponent(typeName, data) {
const deserializer = this.componentDeserializers.get(typeName);
if (deserializer) {
return deserializer(data);
}
const componentType = this.componentTypes.get(typeName);
if (componentType) {
try {
return new componentType(data);
} catch (e) {
console.error(`Error deserializing component ${typeName}:`, e);
}
}
return data;
}
getEntitiesWithComponent(componentTypeName) {
const queryId = `__internal_query_for_${componentTypeName}`;
if (!this.queries.has(queryId)) {
this.defineQuery(queryId, [componentTypeName]);
}
return this.getEntitiesWithQuery(queryId);
}
getEntityCount() {
return this.entities.count;
}
getComponentTypeCount() {
return this.componentNameBitflags.size;
}
getAllEntities() {
return Array.from(this.entities.entities);
}
getPerformanceStats() {
return {
entities: this.entities.count,
components: this.componentNameBitflags.size,
queries: this.queries.size,
recycledEntities: this.removedEntities.length
};
}
static get SparseSet() {
return SparseSet;
}
};
});
// src/decorators/RegisterSystem.ts
function RegisterSystem(id, dependsOn = []) {
return function(target) {
if (registered.has(target))
return;
registered.add(target);
systemRegistry.push({ id, dependsOn, cls: target });
};
}
function getRegisteredSystems() {
return systemRegistry;
}
var systemRegistry, registered;
var init_RegisterSystem = __esm(() => {
systemRegistry = [];
registered = new Set;
});
// src/core/SystemManager.ts
var SystemManager;
var init_SystemManager = __esm(() => {
init_RegisterSystem();
SystemManager = class SystemManager extends BaseSystem {
onLoad() {
throw new Error("Method not implemented.");
}
onUnload() {
throw new Error("Method not implemented.");
}
systems = new Map;
loadOrder = [];
constructor(ecs) {
super(ecs);
}
init() {
const registry = getRegisteredSystems();
const visited = new Set;
const stack = new Set;
const resolve2 = (id) => {
if (stack.has(id))
throw new Error(`Cyclic dependency: ${id}`);
if (visited.has(id))
return;
stack.add(id);
const entry = registry.find((r) => r.id === id);
if (!entry)
throw new Error(`Unknown system id: ${id}`);
for (const dep of entry.dependsOn)
resolve2(dep);
visited.add(id);
stack.delete(id);
this.loadOrder.push(id);
};
for (const { id } of registry)
resolve2(id);
for (const id of this.loadOrder) {
const entry = registry.find((r) => r.id === id);
const instance = new entry.cls(this.ecs);
instance.init?.();
this.systems.set(id, instance);
}
}
update() {
for (const id of this.loadOrder) {
console.log("Updating", id);
this.systems.get(id).update();
}
}
dispose() {
for (const id of [...this.loadOrder].reverse()) {
this.systems.get(id)?.dispose?.();
}
}
};
});
// src/core/ZenithKernel.ts
var exports_ZenithKernel = {};
__export(exports_ZenithKernel, {
ZenithKernel: () => ZenithKernel
});
class ZenithKernel {
enableDiagnostics() {
console.log("Diagnostics enabled");
}
enableHotReload() {
console.log("Hot reload enabled");
}
setLogLevel(logLevel) {
console.log(`Log level set to: ${logLevel}`);
}
initialize() {
console.log("ZenithKernel initialized");
return this;
}
getSystem(systemId) {
return this.dynamicSystems.get(systemId);
}
startLoop() {
console.log("Kernel loop started");
return this;
}
setOnlineStatus(isOnline) {
console.log(`Online status set to: ${isOnline}`);
return this;
}
stop() {
console.log("Kernel stopped");
}
modules = new Map;
messenger = new Messenger;
scheduler;
ecs = new ECSManager;
systemManager = new SystemManager(this.ecs);
dynamicSystems = new Map;
router;
islands = new Map;
hydratedIslands = new WeakMap;
messageHandlers = new Map;
debug = true;
constructor() {
this.messenger.register("kernel");
}
getECS() {
return this.ecs;
}
getRouter() {
return this.router;
}
setRouter(router) {
this.router = router;
}
unregisterSystem(systemId) {
const system = this.dynamicSystems.get(systemId);
if (!system) {
console.warn(`⚠️ System "${systemId}" not found.`);
return;
}
this.dynamicSystems.delete(systemId);
this.ecs.removeSystem(system);
if (this.debug) {
console.info(`[ZenithKernel] Unregistered system: ${systemId}`);
}
}
hotSwapSystem(systemId, NewCtor) {
this.unregisterSystem(systemId);
const instance = new NewCtor(this);
this.registerSystem(systemId, instance);
if (this.debug) {
console.info(`[ZenithKernel] Hot-swapped system: ${systemId}`);
}
}
registerMessageHandler(messageType, handler) {
if (!this.messageHandlers.has(messageType)) {
this.messageHandlers.set(messageType, []);
}
const handlers = this.messageHandlers.get(messageType);
if (handlers) {
handlers.push(handler);
}
if (this.debug) {
console.info(`[ZenithKernel] Registered message handler for type: ${messageType}`);
}
}
send(targetId, message) {
this.messenger.send(targetId, message);
if (this.debug) {
console.info(`[ZenithKernel] Sent message to ${targetId}:`, message);
}
}
sendMessage(messageType, payload = {}) {
const handlers = this.messageHandlers.get(messageType);
if (handlers && handlers.length > 0) {
const message = { type: messageType, payload, timestamp: Date.now() };
handlers.forEach((handler) => {
try {
handler(message);
} catch (error) {
console.error(`[ZenithKernel] Error in message handler for ${messageType}:`, error);
}
});
}
}
g