{"version":3,"file":"index.mjs","names":[],"sources":["../../src/systems/middlewares/initializers/worker-thread-health-schedule.ts","../../src/systems/middlewares/initializers/worker-thread-message-subscription.ts","../../src/systems/middlewares/updaters/worker-thread-data-complete.ts","../../src/systems/middlewares/updaters/worker-thread-health-request.ts","../../src/systems/middlewares/updaters/worker-thread-health-response.ts"],"sourcesContent":["import { ISystemContext, ISystemMiddleware } from '@awesome-ecs/abstract/systems';\nimport { WorkerThreadEntity } from '../../../entities/worker-thread';\n\n// @injectable\nexport class WorkerThreadHealthScheduleSystem implements ISystemMiddleware<WorkerThreadEntity> {\n  action(context: ISystemContext<WorkerThreadEntity>): void {\n    const { identity } = context.entity;\n\n    context.scheduler.scheduleUpdate(\n      {\n        entityType: identity.entityType,\n        entityUid: identity.model.uid\n      },\n      identity.model.healthCheckInterval || 5000\n    );\n  }\n\n  cleanup(context: ISystemContext<WorkerThreadEntity>): void {\n    context.scheduler.removeSchedule();\n    context.entity.instance.worker?.terminate();\n  }\n}\n","import { ISystemContext, ISystemMiddleware } from '@awesome-ecs/abstract/systems';\r\nimport { WorkerThreadEventType } from '../../../abstract/events/event-type';\r\nimport { WorkerThreadEventMessageResponseData } from '../../../abstract/events/message';\r\nimport { WorkerMessageType, WorkerResponseMessage } from '../../../abstract/worker-message';\r\nimport { WorkerStatus } from '../../../components/worker-instance';\r\nimport { WorkerThreadEntity } from '../../../entities/worker-thread';\r\n\r\n// @injectable\r\nexport class WorkerThreadMessageSubscriptionSystem\r\n  implements ISystemMiddleware<WorkerThreadEntity>\r\n{\r\n  shouldRun(context: ISystemContext<WorkerThreadEntity>): boolean {\r\n    return !!context.entity.instance.worker;\r\n  }\r\n\r\n  action(context: ISystemContext<WorkerThreadEntity>): void {\r\n    const { identity, instance } = context.entity;\r\n    const proxy = identity.proxy;\r\n\r\n    instance.worker.onmessage = (event: MessageEvent<WorkerResponseMessage<any>>) => {\r\n      const data: WorkerThreadEventMessageResponseData<any> = {\r\n        uid:\r\n          event.data.messageType === WorkerMessageType.health\r\n            ? WorkerThreadEventType.messageReceivedHealth\r\n            : WorkerThreadEventType.messageReceivedData,\r\n        message: event.data\r\n      };\r\n\r\n      // dispatch event to SELF so it can be processed by other middleware on next tick\r\n      context.events.dispatchEvent(data, proxy);\r\n\r\n      context.logger.debug(\r\n        '[WORKER-RECEIVED] Message from worker',\r\n        proxy,\r\n        data.message.messageType,\r\n        data.message.messageUid,\r\n        data.message.workerUid\r\n      );\r\n    };\r\n\r\n    instance.worker.onerror = (error: ErrorEvent) => {\r\n      const data: WorkerThreadEventMessageResponseData<any> = {\r\n        uid: WorkerThreadEventType.messageReceivedHealth,\r\n        message: null,\r\n        error\r\n      };\r\n\r\n      context.events.broadcastEvent(data);\r\n      instance.status = WorkerStatus.error;\r\n    };\r\n\r\n    instance.worker.onmessageerror = (error: MessageEvent) => {\r\n      const data: WorkerThreadEventMessageResponseData<any> = {\r\n        uid: WorkerThreadEventType.messageReceivedHealth,\r\n        message: null,\r\n        messageError: error\r\n      };\r\n\r\n      context.events.broadcastEvent(data);\r\n      instance.status = WorkerStatus.error;\r\n    };\r\n  }\r\n}\r\n","import type { IEntityEvent } from '@awesome-ecs/abstract/entities';\r\nimport { ISystemContext, ISystemEventMiddleware } from '@awesome-ecs/abstract/systems';\r\nimport { WorkerThreadEventMessageResponseData } from '../../../abstract/events/message';\r\nimport { WorkerMessageType } from '../../../abstract/worker-message';\r\nimport { WorkerStatus } from '../../../components/worker-instance';\r\nimport { WorkerThreadEntity } from '../../../entities/worker-thread';\r\n\r\n// @injectable\r\nexport class WorkerThreadDataCompleteSystem\r\n  implements ISystemEventMiddleware<WorkerThreadEntity, WorkerThreadEventMessageResponseData<unknown>>\r\n{\r\n  shouldRun(\r\n    context: ISystemContext<WorkerThreadEntity>,\r\n    event: IEntityEvent<WorkerThreadEventMessageResponseData<unknown>>\r\n  ): boolean {\r\n    const workerUid = context.entity.identity.model.uid.toString();\r\n    return event.origin.entityUid.toString() === workerUid || event.data.message?.workerUid === workerUid;\r\n  }\r\n\r\n  action(\r\n    context: ISystemContext<WorkerThreadEntity>,\r\n    event: IEntityEvent<WorkerThreadEventMessageResponseData<unknown>>\r\n  ): void {\r\n    if (event.data.message.messageType !== WorkerMessageType.data) {\r\n      return;\r\n    }\r\n\r\n    context.entity.instance.status = WorkerStatus.available;\r\n  }\r\n}\r\n","import { ISystemContext, ISystemMiddleware } from '@awesome-ecs/abstract/systems';\r\nimport { assertComponent } from '@awesome-ecs/core/utils';\r\nimport { WorkerEntityType } from '../../../abstract/types/entity-type';\r\nimport { WorkerThreadHealthRequest } from '../../../abstract/worker-health';\r\nimport { WorkerMessageType, WorkerRequestMessage } from '../../../abstract/worker-message';\r\nimport { WorkerStatus } from '../../../components/worker-instance';\r\nimport { WorkerThreadEntity, WorkerThreadModel } from '../../../entities/worker-thread';\r\n\r\n// @injectable\r\nexport class WorkerThreadHealthRequestSystem implements ISystemMiddleware<WorkerThreadEntity> {\r\n  private readonly maxHeartbeatsPending = 3;\r\n\r\n  shouldRun(context: ISystemContext<WorkerThreadEntity>): boolean {\r\n    if (!context.entity.instance.worker) {\r\n      return false;\r\n    }\r\n\r\n    const { lastHealthQuery } = context.entity.instance;\r\n\r\n    if (!lastHealthQuery) {\r\n      return true;\r\n    }\r\n\r\n    const passedMs = new Date().getTime() - lastHealthQuery.getTime();\r\n\r\n    if (passedMs > context.entity.identity.model.healthCheckInterval) {\r\n      return true;\r\n    }\r\n\r\n    return false;\r\n  }\r\n\r\n  action(context: ISystemContext<WorkerThreadEntity>): void {\r\n    assertComponent(context.entity.instance, context.entity, 'instance');\r\n\r\n    const { entity } = context;\r\n    const { instance } = entity;\r\n\r\n    if (instance.status === WorkerStatus.busy) {\r\n      context.logger.debug(\r\n        \"Worker is still busy, won't send heartbeat.\",\r\n        entity.identity.model.uid\r\n      );\r\n      return;\r\n    }\r\n\r\n    const randomUid = Math.random().toFixed(3);\r\n\r\n    if (instance.heartbeats.size > this.maxHeartbeatsPending) {\r\n      context.logger.warn(\r\n        `Worker ${entity.identity.model.uid} has ${instance.heartbeats.size} pending heartbeat. Will deactivate!`\r\n      );\r\n\r\n      // remove current unresponsive worker\r\n      context.repository.removeEntity(context.entity.identity.proxy);\r\n\r\n      const existingModel = context.entity.identity.model;\r\n\r\n      // register new worker\r\n      const model: WorkerThreadModel = {\r\n        type: existingModel.type,\r\n        uid: `worker-${existingModel.type}-${randomUid}`,\r\n        healthCheckInterval: existingModel.healthCheckInterval\r\n      };\r\n\r\n      context.repository.addEntity(WorkerEntityType.workerThread, model);\r\n    } else {\r\n      this.sendHeartbeatMessage(randomUid, entity);\r\n      context.entity.instance.lastHealthQuery = new Date();\r\n    }\r\n  }\r\n\r\n  private sendHeartbeatMessage(\r\n    uid: string,\r\n    entity: ISystemContext<WorkerThreadEntity>['entity']\r\n  ) {\r\n    const message: WorkerRequestMessage<WorkerThreadHealthRequest> = {\r\n      messageUid: `worker-heartbeat-${uid}`,\r\n      request: {\r\n        heartbeatValue: uid\r\n      },\r\n      workerType: entity.identity.model.type,\r\n      messageType: WorkerMessageType.health\r\n    };\r\n\r\n    entity.instance.worker.postMessage(message);\r\n    entity.instance.heartbeats.set(uid, true);\r\n  }\r\n}\r\n","import type { IEntityEvent } from '@awesome-ecs/abstract/entities';\r\nimport { ISystemContext, ISystemEventMiddleware } from '@awesome-ecs/abstract/systems';\r\nimport { assertComponent } from '@awesome-ecs/core/utils';\r\nimport { WorkerThreadEventMessageResponseData } from '../../../abstract/events/message';\r\nimport { WorkerThreadHealthResponse } from '../../../abstract/worker-health';\r\nimport { WorkerStatus } from '../../../components/worker-instance';\r\nimport { WorkerThreadEntity } from '../../../entities/worker-thread';\r\n\r\n// @injectable\r\nexport class WorkerThreadHealthResponseSystem\r\n  implements\r\n    ISystemEventMiddleware<\r\n      WorkerThreadEntity,\r\n      WorkerThreadEventMessageResponseData<WorkerThreadHealthResponse>\r\n    >\r\n{\r\n  shouldRun(\r\n    context: ISystemContext<WorkerThreadEntity>,\r\n    event: IEntityEvent<WorkerThreadEventMessageResponseData<WorkerThreadHealthResponse>>\r\n  ): boolean {\r\n    return !!context.entity.instance.worker && this.isEventForWorker(context, event);\r\n  }\r\n\r\n  action(\r\n    context: ISystemContext<WorkerThreadEntity>,\r\n    event: IEntityEvent<WorkerThreadEventMessageResponseData<WorkerThreadHealthResponse>>\r\n  ): void {\r\n    assertComponent(context.entity.instance, context.entity, 'instance');\r\n\r\n    const { instance } = context.entity;\r\n\r\n    if (event.data.error) {\r\n      throw new Error(`[WORKER-REPLY] ${event.data.error.message}`);\r\n    }\r\n\r\n    const { message } = event.data;\r\n\r\n    context.logger.debug('[WORKER-HEALTH-REPLY]', context.entity.identity.model.uid, message);\r\n\r\n    if (instance.status === WorkerStatus.starting) {\r\n      instance.status = WorkerStatus.available;\r\n    }\r\n\r\n    instance.heartbeats.delete(message.data.heartbeatValue);\r\n  }\r\n\r\n  private isEventForWorker(\r\n    context: ISystemContext<WorkerThreadEntity>,\r\n    event: IEntityEvent<WorkerThreadEventMessageResponseData<WorkerThreadHealthResponse>>\r\n  ): boolean {\r\n    const workerUid = context.entity.identity.model.uid.toString();\r\n    return (\r\n      event.origin.entityUid.toString() === workerUid || event.data.message?.workerUid === workerUid\r\n    );\r\n  }\r\n}\r\n"],"mappings":"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"}