ecspresso 0.11.0 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. package/README.md +200 -148
  2. package/dist/asset-manager.d.ts +1 -1
  3. package/dist/asset-types.d.ts +2 -2
  4. package/dist/command-buffer.d.ts +34 -24
  5. package/dist/ecspresso-builder.d.ts +100 -72
  6. package/dist/ecspresso.d.ts +257 -122
  7. package/dist/entity-manager.d.ts +57 -47
  8. package/dist/index.d.ts +5 -4
  9. package/dist/plugin.d.ts +61 -0
  10. package/dist/{bundles → plugins}/audio.d.ts +27 -47
  11. package/dist/{bundles → plugins}/bounds.d.ts +17 -25
  12. package/dist/{bundles → plugins}/camera.d.ts +8 -9
  13. package/dist/{bundles → plugins}/collision.d.ts +22 -26
  14. package/dist/plugins/coroutine.d.ts +126 -0
  15. package/dist/{bundles → plugins}/diagnostics.d.ts +5 -4
  16. package/dist/{bundles → plugins}/input.d.ts +9 -15
  17. package/dist/plugins/particles.d.ts +225 -0
  18. package/dist/{bundles → plugins}/physics2D.d.ts +27 -23
  19. package/dist/{bundles → plugins}/renderers/renderer2D.d.ts +40 -39
  20. package/dist/{bundles → plugins}/spatial-index.d.ts +11 -10
  21. package/dist/plugins/sprite-animation.d.ts +150 -0
  22. package/dist/{bundles → plugins}/state-machine.d.ts +50 -104
  23. package/dist/plugins/timers.d.ts +151 -0
  24. package/dist/{bundles → plugins}/transform.d.ts +18 -19
  25. package/dist/{bundles → plugins}/tween.d.ts +36 -71
  26. package/dist/resource-manager.d.ts +32 -7
  27. package/dist/screen-manager.d.ts +17 -11
  28. package/dist/screen-types.d.ts +5 -2
  29. package/dist/src/index.js +2 -2
  30. package/dist/src/index.js.map +17 -17
  31. package/dist/src/plugins/audio.js +4 -0
  32. package/dist/src/plugins/audio.js.map +10 -0
  33. package/dist/src/plugins/bounds.js +4 -0
  34. package/dist/src/plugins/bounds.js.map +10 -0
  35. package/dist/src/plugins/camera.js +4 -0
  36. package/dist/src/plugins/camera.js.map +10 -0
  37. package/dist/src/plugins/collision.js +4 -0
  38. package/dist/src/plugins/collision.js.map +11 -0
  39. package/dist/src/plugins/coroutine.js +4 -0
  40. package/dist/src/plugins/coroutine.js.map +10 -0
  41. package/dist/src/plugins/diagnostics.js +5 -0
  42. package/dist/src/plugins/diagnostics.js.map +10 -0
  43. package/dist/src/plugins/input.js +4 -0
  44. package/dist/src/plugins/input.js.map +10 -0
  45. package/dist/src/plugins/particles.js +4 -0
  46. package/dist/src/plugins/particles.js.map +10 -0
  47. package/dist/src/plugins/physics2D.js +4 -0
  48. package/dist/src/plugins/physics2D.js.map +11 -0
  49. package/dist/src/plugins/renderers/renderer2D.js +4 -0
  50. package/dist/src/plugins/renderers/renderer2D.js.map +10 -0
  51. package/dist/src/plugins/spatial-index.js +4 -0
  52. package/dist/src/plugins/spatial-index.js.map +11 -0
  53. package/dist/src/plugins/sprite-animation.js +4 -0
  54. package/dist/src/plugins/sprite-animation.js.map +10 -0
  55. package/dist/src/plugins/state-machine.js +4 -0
  56. package/dist/src/plugins/state-machine.js.map +10 -0
  57. package/dist/src/plugins/timers.js +4 -0
  58. package/dist/src/plugins/timers.js.map +10 -0
  59. package/dist/src/plugins/transform.js +4 -0
  60. package/dist/src/plugins/transform.js.map +10 -0
  61. package/dist/src/plugins/tween.js +4 -0
  62. package/dist/src/plugins/tween.js.map +11 -0
  63. package/dist/system-builder.d.ts +66 -97
  64. package/dist/type-utils.d.ts +218 -27
  65. package/dist/types.d.ts +52 -24
  66. package/dist/utils/check-required-cycle.d.ts +1 -1
  67. package/dist/utils/narrowphase.d.ts +7 -7
  68. package/package.json +53 -45
  69. package/dist/bundle.d.ts +0 -173
  70. package/dist/bundles/timers.d.ts +0 -173
  71. package/dist/src/bundles/audio.js +0 -4
  72. package/dist/src/bundles/audio.js.map +0 -10
  73. package/dist/src/bundles/bounds.js +0 -4
  74. package/dist/src/bundles/bounds.js.map +0 -10
  75. package/dist/src/bundles/camera.js +0 -4
  76. package/dist/src/bundles/camera.js.map +0 -10
  77. package/dist/src/bundles/collision.js +0 -4
  78. package/dist/src/bundles/collision.js.map +0 -11
  79. package/dist/src/bundles/diagnostics.js +0 -5
  80. package/dist/src/bundles/diagnostics.js.map +0 -10
  81. package/dist/src/bundles/input.js +0 -4
  82. package/dist/src/bundles/input.js.map +0 -10
  83. package/dist/src/bundles/physics2D.js +0 -4
  84. package/dist/src/bundles/physics2D.js.map +0 -11
  85. package/dist/src/bundles/renderers/renderer2D.js +0 -4
  86. package/dist/src/bundles/renderers/renderer2D.js.map +0 -10
  87. package/dist/src/bundles/spatial-index.js +0 -4
  88. package/dist/src/bundles/spatial-index.js.map +0 -11
  89. package/dist/src/bundles/state-machine.js +0 -4
  90. package/dist/src/bundles/state-machine.js.map +0 -10
  91. package/dist/src/bundles/timers.js +0 -4
  92. package/dist/src/bundles/timers.js.map +0 -10
  93. package/dist/src/bundles/transform.js +0 -4
  94. package/dist/src/bundles/transform.js.map +0 -10
  95. package/dist/src/bundles/tween.js +0 -4
  96. package/dist/src/bundles/tween.js.map +0 -11
@@ -1,10 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../src/bundles/state-machine.ts"],
4
- "sourcesContent": [
5
- "/**\n * State Machine Bundle for ECSpresso\n *\n * Provides ECS-native finite state machines with guard-based transitions,\n * event-driven transitions, and lifecycle hooks (onEnter, onExit, onUpdate).\n *\n * Each entity gets a `stateMachine` component referencing a shared definition.\n * One system processes all state machine entities each tick.\n */\n\nimport { Bundle } from 'ecspresso';\nimport type { SystemPhase } from 'ecspresso';\n\n// ==================== World Interface ====================\n\n/**\n * Structural interface for ECS methods available inside state machine hooks.\n * Uses method syntax for bivariant parameter checking under strictFunctionTypes,\n * allowing users to annotate hooks with their concrete ECSpresso type.\n */\nexport interface StateMachineWorld {\n\tentityManager: {\n\t\tgetComponent(entityId: number, componentName: string): unknown | undefined;\n\t};\n\teventBus: {\n\t\tpublish(eventType: string, data: unknown): void;\n\t};\n\tspawn(components: Record<string, unknown>): { id: number };\n\tremoveEntity(entityOrId: number): boolean;\n\thasComponent(entityId: number, componentName: string): boolean;\n\tgetResource(key: string): unknown;\n\thasResource(key: string): boolean;\n\tmarkChanged(entityId: number, componentName: string): void;\n\tcommands: {\n\t\tspawn(components: Record<string, unknown>): void;\n\t\tremoveEntity(entityId: number): void;\n\t};\n}\n\n// ==================== State Config ====================\n\n/**\n * Configuration for a single state in a state machine definition.\n *\n * @template S - Union of state name strings\n * @template W - World interface type for hooks/guards (default: StateMachineWorld)\n */\nexport interface StateConfig<S extends string, W extends StateMachineWorld = StateMachineWorld> {\n\t/** Called when entering this state */\n\tonEnter?(ecs: W, entityId: number): void;\n\t/** Called when exiting this state */\n\tonExit?(ecs: W, entityId: number): void;\n\t/** Called each tick while in this state */\n\tonUpdate?(ecs: W, entityId: number, deltaTime: number): void;\n\t/** Guard-based transitions evaluated each tick. First passing guard wins. */\n\ttransitions?: ReadonlyArray<{\n\t\ttarget: S;\n\t\tguard(ecs: W, entityId: number): boolean;\n\t}>;\n\t/** Event-based transition map: eventName → target state or guarded transition */\n\ton?: Record<string, S | { target: S; guard(ecs: W, entityId: number): boolean }>;\n}\n\n// ==================== State Machine Definition ====================\n\n/**\n * Immutable definition of a state machine. Shared across entities.\n *\n * @template S - Union of state name strings\n */\nexport interface StateMachineDefinition<S extends string> {\n\treadonly id: string;\n\treadonly initial: S;\n\treadonly states: { readonly [K in S]: StateConfig<S> };\n}\n\n// ==================== Component ====================\n\n/**\n * Runtime state machine component stored on each entity.\n *\n * @template S - Union of state name strings (default: string)\n */\nexport interface StateMachine<S extends string = string> {\n\treadonly definition: StateMachineDefinition<string>;\n\tcurrent: S;\n\tprevious: S | null;\n\tstateTime: number;\n}\n\n/**\n * Component types provided by the state machine bundle.\n *\n * @template S - Union of state name strings (default: string)\n */\nexport interface StateMachineComponentTypes<S extends string = string> {\n\tstateMachine: StateMachine<S>;\n}\n\n// ==================== Event Types ====================\n\n/**\n * Event published on every state transition.\n *\n * @template S - Union of state name strings (default: string)\n */\nexport interface StateTransitionEvent<S extends string = string> {\n\tentityId: number;\n\tfrom: S;\n\tto: S;\n\tdefinitionId: string;\n}\n\n/**\n * Event types provided by the state machine bundle.\n *\n * @template S - Union of state name strings (default: string)\n */\nexport interface StateMachineEventTypes<S extends string = string> {\n\tstateTransition: StateTransitionEvent<S>;\n}\n\n/**\n * Extract the state name union from a StateMachineDefinition.\n *\n * @example\n * ```typescript\n * const enemyFSM = defineStateMachine('enemy', { initial: 'idle', states: { idle: {}, chase: {} } });\n * type EnemyStates = StatesOf<typeof enemyFSM>; // 'idle' | 'chase'\n * type AllStates = StatesOf<typeof enemyFSM> | StatesOf<typeof playerFSM>;\n * ```\n */\nexport type StatesOf<D> = D extends StateMachineDefinition<infer S> ? S : never;\n\n// ==================== Bundle Options ====================\n\n/**\n * Configuration options for the state machine bundle.\n */\nexport interface StateMachineBundleOptions<G extends string = 'stateMachine'> {\n\t/** System group name (default: 'stateMachine') */\n\tsystemGroup?: G;\n\t/** Priority for state machine system (default: 0) */\n\tpriority?: number;\n\t/** Execution phase (default: 'update') */\n\tphase?: SystemPhase;\n}\n\n// ==================== Helper Functions ====================\n\n/**\n * Define a state machine with type-safe state names.\n *\n * @template S - Union of state name strings, inferred from `states` keys\n * @param id - Unique identifier for this definition\n * @param config - Initial state and state configurations\n * @returns A frozen StateMachineDefinition\n *\n * @example\n * ```typescript\n * const enemyFSM = defineStateMachine('enemy', {\n * initial: 'idle',\n * states: {\n * idle: {\n * onEnter: (ecs, id) => { ... },\n * transitions: [{ target: 'chase', guard: (ecs, id) => playerNearby(ecs, id) }],\n * },\n * chase: {\n * onUpdate: (ecs, id, dt) => { ... },\n * on: { playerLost: 'idle' },\n * },\n * },\n * });\n * ```\n */\nexport function defineStateMachine<S extends string>(\n\tid: string,\n\tconfig: { initial: NoInfer<S>; states: Record<S, StateConfig<NoInfer<S>>> },\n): StateMachineDefinition<S> {\n\treturn Object.freeze({\n\t\tid,\n\t\tinitial: config.initial,\n\t\tstates: Object.freeze(config.states),\n\t}) as StateMachineDefinition<S>;\n}\n\n/**\n * Create a stateMachine component from a definition.\n *\n * @param definition - The state machine definition to use\n * @param options - Optional overrides (e.g., initial state)\n * @returns Component object suitable for spreading into spawn()\n *\n * @example\n * ```typescript\n * ecs.spawn({\n * ...createStateMachine(enemyFSM),\n * position: { x: 100, y: 200 },\n * });\n * ```\n */\nexport function createStateMachine<S extends string>(\n\tdefinition: StateMachineDefinition<S>,\n\toptions?: { initial?: S },\n): Pick<StateMachineComponentTypes<S>, 'stateMachine'> {\n\tconst initial = options?.initial ?? definition.initial;\n\treturn {\n\t\tstateMachine: {\n\t\t\tdefinition,\n\t\t\tcurrent: initial,\n\t\t\tprevious: null,\n\t\t\tstateTime: 0,\n\t\t},\n\t};\n}\n\n// ==================== Internal: Shared Transition Logic ====================\n\n/**\n * Perform a state transition: onExit → update fields → onEnter → markChanged → publish event.\n * Returns true if the target state exists, false otherwise.\n */\nfunction performTransition(\n\tecs: StateMachineWorld,\n\tentityId: number,\n\tsm: StateMachine,\n\ttargetState: string,\n): boolean {\n\tconst states = sm.definition.states as Record<string, StateConfig<string>>;\n\tconst currentConfig = states[sm.current];\n\tconst targetConfig = states[targetState];\n\n\tif (!targetConfig) return false;\n\n\tcurrentConfig?.onExit?.(ecs, entityId);\n\n\tsm.previous = sm.current;\n\tsm.current = targetState;\n\tsm.stateTime = 0;\n\n\ttargetConfig.onEnter?.(ecs, entityId);\n\n\tecs.markChanged(entityId, 'stateMachine');\n\tecs.eventBus.publish('stateTransition', {\n\t\tentityId,\n\t\tfrom: sm.previous,\n\t\tto: sm.current,\n\t\tdefinitionId: sm.definition.id,\n\t} satisfies StateTransitionEvent);\n\n\treturn true;\n}\n\n// ==================== Utility Functions ====================\n\n/**\n * Directly transition an entity's state machine to a target state.\n * Fires onExit, onEnter hooks and publishes stateTransition event.\n *\n * @param ecs - ECS instance (structural typing)\n * @param entityId - Entity to transition\n * @param targetState - State to transition to\n * @returns true if transition succeeded, false if entity has no stateMachine or target state doesn't exist\n */\nexport function transitionTo(\n\tecs: StateMachineWorld,\n\tentityId: number,\n\ttargetState: string,\n): boolean {\n\tconst sm = ecs.entityManager.getComponent(entityId, 'stateMachine') as StateMachine | undefined;\n\tif (!sm) return false;\n\treturn performTransition(ecs, entityId, sm, targetState);\n}\n\n/**\n * Send a named event to an entity's state machine.\n * Checks the current state's `on` handlers for a matching event.\n *\n * @param ecs - ECS instance (structural typing)\n * @param entityId - Entity to send event to\n * @param eventName - Event name to match against `on` handlers\n * @returns true if a transition occurred, false otherwise\n */\nexport function sendEvent(\n\tecs: StateMachineWorld,\n\tentityId: number,\n\teventName: string,\n): boolean {\n\tconst sm = ecs.entityManager.getComponent(entityId, 'stateMachine') as StateMachine | undefined;\n\tif (!sm) return false;\n\n\tconst states = sm.definition.states as Record<string, StateConfig<string>>;\n\tconst currentConfig = states[sm.current];\n\tif (!currentConfig?.on) return false;\n\n\tconst handler = currentConfig.on[eventName];\n\tif (handler === undefined) return false;\n\n\tif (typeof handler === 'string') {\n\t\treturn performTransition(ecs, entityId, sm, handler);\n\t}\n\n\tif (!handler.guard(ecs, entityId)) return false;\n\treturn performTransition(ecs, entityId, sm, handler.target);\n}\n\n/**\n * Get the current state of an entity's state machine.\n *\n * @param ecs - ECS instance (structural typing)\n * @param entityId - Entity to query\n * @returns The current state string, or undefined if entity has no stateMachine\n */\nexport function getStateMachineState(\n\tecs: StateMachineWorld,\n\tentityId: number,\n): string | undefined {\n\tconst sm = ecs.entityManager.getComponent(entityId, 'stateMachine') as StateMachine | undefined;\n\treturn sm?.current;\n}\n\n// ==================== State Machine Kit ====================\n\n/**\n * A typed kit that captures the world type W once, providing helpers\n * where hooks/guards contextually receive W instead of StateMachineWorld.\n *\n * @template W - Concrete ECS world type\n */\nexport interface StateMachineKit<W extends StateMachineWorld, S extends string = string> {\n\tbundle: Bundle<StateMachineComponentTypes<S>, StateMachineEventTypes<S>, {}, {}, {}, 'state-machine-update', 'stateMachine'>;\n\tdefineStateMachine: <DS extends S>(\n\t\tid: string,\n\t\tconfig: { initial: NoInfer<DS>; states: Record<DS, StateConfig<NoInfer<DS>, W>> },\n\t) => StateMachineDefinition<DS>;\n\tcreateStateMachine: <DS extends S>(\n\t\tdefinition: StateMachineDefinition<DS>,\n\t\toptions?: { initial?: DS },\n\t) => Pick<StateMachineComponentTypes<S>, 'stateMachine'>;\n}\n\n/**\n * Create a typed state machine kit that captures the world type W.\n *\n * Hooks and guards in definitions created via the kit's `defineStateMachine`\n * contextually receive W as their `ecs` parameter — no manual annotations needed.\n *\n * @template W - Concrete ECS world type\n * @param options - Optional bundle configuration (same as createStateMachineBundle)\n * @returns A kit object with bundle, defineStateMachine, createStateMachine, transitionTo, sendEvent, getStateMachineState\n *\n * @example\n * ```typescript\n * const ecs = ECSpresso.create()\n * .withBundle(createStateMachineBundle())\n * .withComponentTypes<{ enemy: true }>()\n * .build();\n *\n * type ECS = typeof ecs;\n * const { bundle, defineStateMachine, createStateMachine } =\n * createStateMachineKit<ECS>();\n *\n * const enemyFSM = defineStateMachine('enemy', {\n * initial: 'patrol',\n * states: {\n * patrol: {\n * onEnter(ecs, entityId) {\n * ecs.getResource('bounds'); // fully typed\n * },\n * transitions: [{\n * target: 'chase',\n * guard: (ecs, entityId) => distanceToPlayer(ecs, entityId) < 180,\n * }],\n * },\n * chase: {},\n * },\n * });\n * ```\n */\nexport function createStateMachineKit<W extends StateMachineWorld = StateMachineWorld, S extends string = string>(\n\toptions?: StateMachineBundleOptions,\n): StateMachineKit<W, S> {\n\treturn {\n\t\tbundle: createStateMachineBundle<S>(options),\n\t\tdefineStateMachine: defineStateMachine as StateMachineKit<W, S>['defineStateMachine'],\n\t\tcreateStateMachine: createStateMachine as StateMachineKit<W, S>['createStateMachine'],\n\t};\n}\n\n// ==================== Bundle Factory ====================\n\n/**\n * Create a state machine bundle for ECSpresso.\n *\n * Provides:\n * - Lifecycle hooks (onEnter, onExit, onUpdate) per state\n * - Guard-based automatic transitions evaluated each tick\n * - Event-based transitions via `sendEvent()`\n * - Direct transitions via `transitionTo()`\n * - stateTransition events published on every transition\n *\n * @example\n * ```typescript\n * const ecs = ECSpresso.create()\n * .withBundle(createStateMachineBundle())\n * .build();\n *\n * const fsm = defineStateMachine('enemy', {\n * initial: 'idle',\n * states: {\n * idle: {\n * transitions: [{ target: 'chase', guard: (ecs, id) => playerNearby(ecs, id) }],\n * },\n * chase: {\n * onUpdate: (ecs, id, dt) => moveTowardPlayer(ecs, id, dt),\n * on: { playerLost: 'idle' },\n * },\n * },\n * });\n *\n * ecs.spawn({\n * ...createStateMachine(fsm),\n * position: { x: 0, y: 0 },\n * });\n * ```\n */\nexport function createStateMachineBundle<S extends string = string, G extends string = 'stateMachine'>(\n\toptions?: StateMachineBundleOptions<G>,\n): Bundle<StateMachineComponentTypes<S>, StateMachineEventTypes<S>, {}, {}, {}, 'state-machine-update', G> {\n\tconst {\n\t\tsystemGroup = 'stateMachine',\n\t\tpriority = 0,\n\t\tphase = 'update',\n\t} = options ?? {};\n\n\tconst bundle = new Bundle<StateMachineComponentTypes<S>, StateMachineEventTypes<S>, {}>('stateMachine');\n\n\tconst initialized = new Set<number>();\n\n\tbundle\n\t\t.addSystem('state-machine-update')\n\t\t.setPriority(priority)\n\t\t.inPhase(phase)\n\t\t.inGroup(systemGroup)\n\t\t.addQuery('machines', {\n\t\t\twith: ['stateMachine'],\n\t\t})\n\t\t.setOnInitialize((ecs) => {\n\t\t\tecs.onComponentRemoved('stateMachine', (_value, entity) => {\n\t\t\t\tinitialized.delete(entity.id);\n\t\t\t});\n\t\t})\n\t\t.setProcess((queries, deltaTime, ecs) => {\n\t\t\tfor (const entity of queries.machines) {\n\t\t\t\tconst sm = entity.components.stateMachine;\n\t\t\t\tconst states = sm.definition.states as Record<string, StateConfig<string>>;\n\t\t\t\tconst ecsWorld = ecs as unknown as StateMachineWorld;\n\n\t\t\t\t// Initialize: fire onEnter for initial state on first tick\n\t\t\t\tif (!initialized.has(entity.id)) {\n\t\t\t\t\tinitialized.add(entity.id);\n\t\t\t\t\tstates[sm.current]?.onEnter?.(ecsWorld, entity.id);\n\t\t\t\t}\n\n\t\t\t\t// Accumulate state time\n\t\t\t\tsm.stateTime += deltaTime;\n\n\t\t\t\t// onUpdate hook\n\t\t\t\tstates[sm.current]?.onUpdate?.(ecsWorld, entity.id, deltaTime);\n\n\t\t\t\t// Evaluate guard transitions (first passing guard wins)\n\t\t\t\tconst currentConfig = states[sm.current];\n\t\t\t\tif (currentConfig?.transitions) {\n\t\t\t\t\tfor (const transition of currentConfig.transitions) {\n\t\t\t\t\t\tif (transition.guard(ecsWorld, entity.id)) {\n\t\t\t\t\t\t\tperformTransition(ecsWorld, entity.id, sm, transition.target);\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t\t.and();\n\n\treturn bundle as Bundle<StateMachineComponentTypes<S>, StateMachineEventTypes<S>, {}, {}, {}, 'state-machine-update', G>;\n}\n"
6
- ],
7
- "mappings": "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",
8
- "debugId": "3E99D19B151FCC8064756E2164756E21",
9
- "names": []
10
- }
@@ -1,4 +0,0 @@
1
- var{defineProperty:L,getOwnPropertyNames:R,getOwnPropertyDescriptor:U}=Object,V=Object.prototype.hasOwnProperty;var Q=new WeakMap,X=(j)=>{var k=Q.get(j),z;if(k)return k;if(k=L({},"__esModule",{value:!0}),j&&typeof j==="object"||typeof j==="function")R(j).map((A)=>!V.call(k,A)&&L(k,A,{get:()=>j[A],enumerable:!(z=U(j,A))||z.enumerable}));return Q.set(j,k),k};var Y=(j,k)=>{for(var z in k)L(j,z,{get:k[z],enumerable:!0,configurable:!0,set:(A)=>k[z]=()=>A})};var Z=(j,k)=>()=>(j&&(k=j(j=0)),k);var _=((j)=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(j,{get:(k,z)=>(typeof require<"u"?require:k)[z]}):j)(function(j){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+j+'" is not supported')});import{Bundle as W}from"ecspresso";function B(j,k){return{timer:{elapsed:0,duration:j,repeat:!1,active:!0,justFinished:!1,onComplete:k?.onComplete}}}function C(j,k){return{timer:{elapsed:0,duration:j,repeat:!0,active:!0,justFinished:!1,onComplete:k?.onComplete}}}function G(j){let{systemGroup:k="timers",priority:z=0,phase:A="preUpdate"}=j??{},M=new W("timers");M.addSystem("timer-update").setPriority(z).inPhase(A).inGroup(k).addQuery("timers",{with:["timer"]}).setProcess((J,K,F)=>{for(let H of J.timers){let{timer:x}=H.components;if(x.justFinished=!1,!x.active)continue;if(x.elapsed+=K,x.elapsed<x.duration)continue;if(x.repeat)while(x.elapsed>=x.duration)x.justFinished=!0,O(F,H.id,x),x.elapsed-=x.duration;else x.justFinished=!0,O(F,H.id,x),x.active=!1,F.commands.removeEntity(H.id)}}).and();function O(J,K,F){if(!F.onComplete)return;let H={entityId:K,duration:F.duration,elapsed:F.elapsed};J.eventBus.publish(F.onComplete,H)}return M}export{G as createTimerBundle,B as createTimer,C as createRepeatingTimer};
2
-
3
- //# debugId=24F664FC7BF6417364756E2164756E21
4
- //# sourceMappingURL=timers.js.map
@@ -1,10 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../src/bundles/timers.ts"],
4
- "sourcesContent": [
5
- "/**\n * Timer Bundle for ECSpresso\n *\n * Provides ECS-native timers following the \"data, not callbacks\" philosophy.\n * Timers are components processed each frame, automatically cleaned up when entities are removed.\n */\n\nimport { Bundle } from 'ecspresso';\nimport type { SystemPhase } from 'ecspresso';\n\n// ==================== Event Types ====================\n\n/**\n * Data structure published when a timer completes.\n * Use this type when defining timer completion events in your EventTypes interface.\n *\n * @example\n * ```typescript\n * interface Events {\n * hideMessage: TimerEventData;\n * spawnWave: TimerEventData;\n * }\n * ```\n */\nexport interface TimerEventData {\n\t/** The entity ID that the timer belongs to */\n\tentityId: number;\n\t/** The timer's configured duration in seconds */\n\tduration: number;\n\t/** The actual elapsed time (may exceed duration slightly) */\n\telapsed: number;\n}\n\n// ==================== Component Types ====================\n\n/**\n * Extracts event names from EventTypes that have TimerEventData as their payload.\n * This ensures only compatible events can be used with timer.onComplete.\n */\nexport type TimerEventName<EventTypes extends Record<string, any>> = {\n\t[K in keyof EventTypes]: EventTypes[K] extends TimerEventData ? K : never\n}[keyof EventTypes];\n\n/**\n * Timer component data structure.\n * Use `justFinished` to detect timer completion in your systems.\n *\n * @template EventTypes The event types from your ECS\n */\nexport interface Timer<EventTypes extends Record<string, any>> {\n\t/** Time accumulated so far (seconds) */\n\telapsed: number;\n\t/** Target duration (seconds) */\n\tduration: number;\n\t/** Whether timer repeats after completion */\n\trepeat: boolean;\n\t/** Whether timer is currently running */\n\tactive: boolean;\n\t/** True for one frame after timer completes */\n\tjustFinished: boolean;\n\t/** Optional event name to publish when timer completes. Must be an event with TimerEventData payload. */\n\tonComplete?: TimerEventName<EventTypes>;\n}\n\n/**\n * Component types provided by the timer bundle.\n * Included automatically via `.withBundle(createTimerBundle<Events>())`.\n * The EventTypes generic constrains which events can be used with `onComplete`.\n *\n * @template EventTypes The event types from your ECS\n *\n * @example\n * ```typescript\n * const ecs = ECSpresso.create()\n * .withBundle(createTimerBundle<{ respawn: TimerEventData }>())\n * .withComponentTypes<{ velocity: { x: number; y: number }; player: true }>()\n * .build();\n * ```\n */\nexport interface TimerComponentTypes<EventTypes extends Record<string, any>> {\n\ttimer: Timer<EventTypes>;\n}\n\n// ==================== Bundle Options ====================\n\n/**\n * Configuration options for the timer bundle.\n */\nexport interface TimerBundleOptions<G extends string = 'timers'> {\n\t/** System group name (default: 'timers') */\n\tsystemGroup?: G;\n\t/** Priority for timer update system (default: 0) */\n\tpriority?: number;\n\t/** Execution phase (default: 'preUpdate') */\n\tphase?: SystemPhase;\n}\n\n// ==================== Helper Functions ====================\n\n/**\n * Options for timer creation\n *\n * @template EventTypes The event types from your ECS\n */\nexport interface TimerOptions<EventTypes extends Record<string, any>> {\n\t/** Event name to publish when timer completes. Must be an event with TimerEventData payload. */\n\tonComplete?: TimerEventName<EventTypes>;\n}\n\n/**\n * Create a one-shot timer that fires once after the specified duration.\n *\n * @template EventTypes The event types from your ECS (must be explicitly provided)\n * @param duration Duration in seconds until the timer completes\n * @param options Optional configuration including event name\n * @returns Component object suitable for spreading into spawn()\n *\n * @example\n * ```typescript\n * // Timer without event\n * ecs.spawn({\n * ...createTimer<GameEvents>(2),\n * explosion: true,\n * });\n *\n * // Timer that publishes an event on completion\n * ecs.spawn({\n * ...createTimer<GameEvents>(1.5, { onComplete: 'hideMessage' }),\n * });\n * ```\n */\nexport function createTimer<EventTypes extends Record<string, any>>(\n\tduration: number,\n\toptions?: TimerOptions<EventTypes>\n): Pick<TimerComponentTypes<EventTypes>, 'timer'> {\n\treturn {\n\t\ttimer: {\n\t\t\telapsed: 0,\n\t\t\tduration,\n\t\t\trepeat: false,\n\t\t\tactive: true,\n\t\t\tjustFinished: false,\n\t\t\tonComplete: options?.onComplete,\n\t\t},\n\t};\n}\n\n/**\n * Create a repeating timer that fires every `duration` seconds.\n *\n * @template EventTypes The event types from your ECS (must be explicitly provided)\n * @param duration Duration in seconds between each timer completion\n * @param options Optional configuration including event name\n * @returns Component object suitable for spreading into spawn()\n *\n * @example\n * ```typescript\n * // Timer without event\n * ecs.spawn({\n * ...createRepeatingTimer<GameEvents>(5),\n * spawner: true,\n * });\n *\n * // Repeating timer that publishes an event each cycle\n * ecs.spawn({\n * ...createRepeatingTimer<GameEvents>(3, { onComplete: 'spawnWave' }),\n * });\n * ```\n */\nexport function createRepeatingTimer<EventTypes extends Record<string, any>>(\n\tduration: number,\n\toptions?: TimerOptions<EventTypes>\n): Pick<TimerComponentTypes<EventTypes>, 'timer'> {\n\treturn {\n\t\ttimer: {\n\t\t\telapsed: 0,\n\t\t\tduration,\n\t\t\trepeat: true,\n\t\t\tactive: true,\n\t\t\tjustFinished: false,\n\t\t\tonComplete: options?.onComplete,\n\t\t},\n\t};\n}\n\n// ==================== Bundle Factory ====================\n\n/**\n * Create a timer bundle for ECSpresso.\n *\n * This bundle provides:\n * - Timer update system that processes all timer components each frame\n * - `justFinished` flag pattern for one-frame completion detection\n * - Automatic cleanup when entities are removed\n *\n * @example\n * ```typescript\n * const ecs = ECSpresso\n * .create<Components, Events, Resources>()\n * .withBundle(createTimerBundle())\n * .build();\n *\n * // Spawn entity with timer\n * ecs.spawn({\n * ...createRepeatingTimer(5),\n * spawner: true,\n * });\n *\n * // React to timer completion in a system\n * ecs.addSystem('spawn-on-timer')\n * .addQuery('spawners', { with: ['timer', 'spawner'] })\n * .setProcess((queries, _dt, ecs) => {\n * for (const { components } of queries.spawners) {\n * if (components.timer.justFinished) {\n * ecs.spawn({ enemy: true });\n * }\n * }\n * });\n * ```\n */\nexport function createTimerBundle<EventTypes extends Record<string, any>, G extends string = 'timers'>(\n\toptions?: TimerBundleOptions<G>\n): Bundle<TimerComponentTypes<EventTypes>, EventTypes, {}, {}, {}, 'timer-update', G> {\n\tconst {\n\t\tsystemGroup = 'timers',\n\t\tpriority = 0,\n\t\tphase = 'preUpdate',\n\t} = options ?? {};\n\n\tconst bundle = new Bundle<TimerComponentTypes<EventTypes>, EventTypes, {}>('timers');\n\n\tbundle\n\t\t.addSystem('timer-update')\n\t\t.setPriority(priority)\n\t\t.inPhase(phase)\n\t\t.inGroup(systemGroup)\n\t\t.addQuery('timers', {\n\t\t\twith: ['timer'],\n\t\t})\n\t\t.setProcess((queries, deltaTime, ecs) => {\n\t\t\tfor (const entity of queries.timers) {\n\t\t\t\tconst { timer } = entity.components;\n\n\t\t\t\t// Reset justFinished flag from previous frame\n\t\t\t\ttimer.justFinished = false;\n\n\t\t\t\t// Skip inactive timers\n\t\t\t\tif (!timer.active) continue;\n\n\t\t\t\t// Accumulate time\n\t\t\t\ttimer.elapsed += deltaTime;\n\n\t\t\t\t// Check if timer completed\n\t\t\t\tif (timer.elapsed < timer.duration) continue;\n\n\t\t\t\t// Timer completed - handle based on repeat mode\n\t\t\t\tif (timer.repeat) {\n\t\t\t\t\t// Handle multiple cycles in one frame\n\t\t\t\t\twhile (timer.elapsed >= timer.duration) {\n\t\t\t\t\t\ttimer.justFinished = true;\n\t\t\t\t\t\tpublishTimerEvent(ecs, entity.id, timer);\n\t\t\t\t\t\ttimer.elapsed -= timer.duration;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t// One-shot timer\n\t\t\t\t\ttimer.justFinished = true;\n\t\t\t\t\tpublishTimerEvent(ecs, entity.id, timer);\n\t\t\t\t\ttimer.active = false;\n\t\t\t\t\t// Auto-remove one-shot timer entities after completion.\n\t\t\t\t\t// If configurability is needed in the future, add an autoRemove option to TimerOptions.\n\t\t\t\t\tecs.commands.removeEntity(entity.id);\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t\t.and();\n\n\t/**\n\t * Publishes timer completion event if onComplete is specified.\n\t * Type assertion needed: TypeScript can't infer that TimerEventName<EventTypes>\n\t * maps to events with TimerEventData payloads, even though that's what the type enforces.\n\t */\n\tfunction publishTimerEvent(\n\t\tecs: { eventBus: { publish: (event: any, data: any) => void } },\n\t\tentityId: number,\n\t\ttimer: Timer<EventTypes>\n\t): void {\n\t\tif (!timer.onComplete) return;\n\t\tconst eventData: TimerEventData = {\n\t\t\tentityId,\n\t\t\tduration: timer.duration,\n\t\t\telapsed: timer.elapsed,\n\t\t};\n\t\tecs.eventBus.publish(timer.onComplete, eventData);\n\t}\n\n\treturn bundle as Bundle<TimerComponentTypes<EventTypes>, EventTypes, {}, {}, {}, 'timer-update', G>;\n}\n"
6
- ],
7
- "mappings": "uuBAOA,iBAAS,kBA4HF,SAAS,CAAmD,CAClE,EACA,EACiD,CACjD,MAAO,CACN,MAAO,CACN,QAAS,EACT,WACA,OAAQ,GACR,OAAQ,GACR,aAAc,GACd,WAAY,GAAS,UACtB,CACD,EAyBM,SAAS,CAA4D,CAC3E,EACA,EACiD,CACjD,MAAO,CACN,MAAO,CACN,QAAS,EACT,WACA,OAAQ,GACR,OAAQ,GACR,aAAc,GACd,WAAY,GAAS,UACtB,CACD,EAsCM,SAAS,CAAsF,CACrG,EACqF,CACrF,IACC,cAAc,SACd,WAAW,EACX,QAAQ,aACL,GAAW,CAAC,EAEV,EAAS,IAAI,EAAwD,QAAQ,EAEnF,EACE,UAAU,cAAc,EACxB,YAAY,CAAQ,EACpB,QAAQ,CAAK,EACb,QAAQ,CAAW,EACnB,SAAS,SAAU,CACnB,KAAM,CAAC,OAAO,CACf,CAAC,EACA,WAAW,CAAC,EAAS,EAAW,IAAQ,CACxC,QAAW,KAAU,EAAQ,OAAQ,CACpC,IAAQ,SAAU,EAAO,WAMzB,GAHA,EAAM,aAAe,GAGjB,CAAC,EAAM,OAAQ,SAMnB,GAHA,EAAM,SAAW,EAGb,EAAM,QAAU,EAAM,SAAU,SAGpC,GAAI,EAAM,OAET,MAAO,EAAM,SAAW,EAAM,SAC7B,EAAM,aAAe,GACrB,EAAkB,EAAK,EAAO,GAAI,CAAK,EACvC,EAAM,SAAW,EAAM,SAIxB,OAAM,aAAe,GACrB,EAAkB,EAAK,EAAO,GAAI,CAAK,EACvC,EAAM,OAAS,GAGf,EAAI,SAAS,aAAa,EAAO,EAAE,GAGrC,EACA,IAAI,EAON,SAAS,CAAiB,CACzB,EACA,EACA,EACO,CACP,GAAI,CAAC,EAAM,WAAY,OACvB,IAAM,EAA4B,CACjC,WACA,SAAU,EAAM,SAChB,QAAS,EAAM,OAChB,EACA,EAAI,SAAS,QAAQ,EAAM,WAAY,CAAS,EAGjD,OAAO",
8
- "debugId": "24F664FC7BF6417364756E2164756E21",
9
- "names": []
10
- }
@@ -1,4 +0,0 @@
1
- var{defineProperty:H,getOwnPropertyNames:N,getOwnPropertyDescriptor:O}=Object,P=Object.prototype.hasOwnProperty;var K=new WeakMap,V=(j)=>{var k=K.get(j),q;if(k)return k;if(k=H({},"__esModule",{value:!0}),j&&typeof j==="object"||typeof j==="function")N(j).map((v)=>!P.call(k,v)&&H(k,v,{get:()=>j[v],enumerable:!(q=O(j,v))||q.enumerable}));return K.set(j,k),k};var Z=(j,k)=>{for(var q in k)H(j,q,{get:k[q],enumerable:!0,configurable:!0,set:(v)=>k[q]=()=>v})};var _=(j,k)=>()=>(j&&(k=j(j=0)),k);var $=((j)=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(j,{get:(k,q)=>(typeof require<"u"?require:k)[q]}):j)(function(j){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+j+'" is not supported')});import{Bundle as Q}from"ecspresso";var B={x:0,y:0,rotation:0,scaleX:1,scaleY:1},E={x:0,y:0,rotation:0,scaleX:1,scaleY:1};function G(j,k){return{localTransform:{x:j,y:k,rotation:0,scaleX:1,scaleY:1}}}function R(j,k){return{worldTransform:{x:j,y:k,rotation:0,scaleX:1,scaleY:1}}}function h(j,k,q){let v=q?.scale??q?.scaleX??1,D=q?.scale??q?.scaleY??1,z=q?.rotation??0,C={x:j,y:k,rotation:z,scaleX:v,scaleY:D};return{localTransform:{...C},worldTransform:{...C}}}function W(j){let{systemGroup:k="transform",priority:q=500,phase:v="postUpdate"}=j??{},D=new Q("transform");return D.registerRequired("localTransform","worldTransform",(z)=>({x:z.x,y:z.y,rotation:z.rotation,scaleX:z.scaleX,scaleY:z.scaleY})),D.addSystem("transform-propagation").setPriority(q).inPhase(v).inGroup(k).setProcess((z,C,F)=>{S(F)}).and(),D}function S(j){let k=j.entityManager;j.forEachInHierarchy((v,D)=>{let z=k.getComponent(v,"localTransform"),C=k.getComponent(v,"worldTransform");if(!z||!C)return;if(D===null)J(z,C);else{let F=k.getComponent(D,"worldTransform");if(F)U(F,z,C);else J(z,C)}j.markChanged(v,"worldTransform")});let q=j.getEntitiesWithQuery(["localTransform","worldTransform"]);for(let v of q)if(j.getParent(v.id)===null&&j.getChildren(v.id).length===0){let{localTransform:z,worldTransform:C}=v.components;J(z,C),j.markChanged(v.id,"worldTransform")}}function J(j,k){k.x=j.x,k.y=j.y,k.rotation=j.rotation,k.scaleX=j.scaleX,k.scaleY=j.scaleY}function U(j,k,q){let v=k.x*j.scaleX,D=k.y*j.scaleY,z=Math.cos(j.rotation),C=Math.sin(j.rotation),F=v*z-D*C,M=v*C+D*z;q.x=j.x+F,q.y=j.y+M,q.rotation=j.rotation+k.rotation,q.scaleX=j.scaleX*k.scaleX,q.scaleY=j.scaleY*k.scaleY}export{R as createWorldTransform,W as createTransformBundle,h as createTransform,G as createLocalTransform,E as DEFAULT_WORLD_TRANSFORM,B as DEFAULT_LOCAL_TRANSFORM};
2
-
3
- //# debugId=B32D83741C8A3A2964756E2164756E21
4
- //# sourceMappingURL=transform.js.map
@@ -1,10 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../src/bundles/transform.ts"],
4
- "sourcesContent": [
5
- "/**\n * Transform Bundle for ECSpresso\n *\n * Provides hierarchical transform propagation following Bevy's Transform/GlobalTransform pattern.\n * LocalTransform is modified by user code; WorldTransform is computed automatically.\n *\n * @see https://docs.rs/bevy/latest/bevy/transform/components/struct.GlobalTransform.html\n */\n\nimport { Bundle } from 'ecspresso';\nimport type { SystemPhase } from 'ecspresso';\nimport type ECSpresso from 'ecspresso';\n\n// ==================== Component Types ====================\n\n/**\n * Local transform relative to parent (or world if no parent).\n * This is the transform you modify directly.\n */\nexport interface LocalTransform {\n\tx: number;\n\ty: number;\n\trotation: number;\n\tscaleX: number;\n\tscaleY: number;\n}\n\n/**\n * Computed world transform (accumulated from parent chain).\n * Read-only - managed by the transform propagation system.\n */\nexport interface WorldTransform {\n\tx: number;\n\ty: number;\n\trotation: number;\n\tscaleX: number;\n\tscaleY: number;\n}\n\n/**\n * Component types provided by the transform bundle.\n * Included automatically via `.withBundle(createTransformBundle())`.\n *\n * @example\n * ```typescript\n * const ecs = ECSpresso.create()\n * .withBundle(createTransformBundle())\n * .withComponentTypes<{ sprite: Sprite; velocity: { x: number; y: number } }>()\n * .build();\n * ```\n */\nexport interface TransformComponentTypes {\n\tlocalTransform: LocalTransform;\n\tworldTransform: WorldTransform;\n}\n\n// ==================== Bundle Options ====================\n\n/**\n * Configuration options for the transform bundle.\n */\nexport interface TransformBundleOptions<G extends string = 'transform'> {\n\t/** System group name (default: 'transform') */\n\tsystemGroup?: G;\n\t/** Priority for transform propagation (default: 500, runs after physics) */\n\tpriority?: number;\n\t/** Execution phase (default: 'postUpdate') */\n\tphase?: SystemPhase;\n}\n\n// ==================== Default Values ====================\n\n/**\n * Default local transform values.\n */\nexport const DEFAULT_LOCAL_TRANSFORM: Readonly<LocalTransform> = {\n\tx: 0,\n\ty: 0,\n\trotation: 0,\n\tscaleX: 1,\n\tscaleY: 1,\n};\n\n/**\n * Default world transform values.\n */\nexport const DEFAULT_WORLD_TRANSFORM: Readonly<WorldTransform> = {\n\tx: 0,\n\ty: 0,\n\trotation: 0,\n\tscaleX: 1,\n\tscaleY: 1,\n};\n\n// ==================== Helper Functions ====================\n\n/**\n * Create a local transform component with position only.\n * Uses default rotation (0) and scale (1, 1).\n *\n * @param x The x coordinate\n * @param y The y coordinate\n * @returns Component object suitable for spreading into spawn()\n *\n * @example\n * ```typescript\n * ecs.spawn({\n * ...createLocalTransform(100, 200),\n * sprite,\n * });\n * ```\n */\nexport function createLocalTransform(x: number, y: number): Pick<TransformComponentTypes, 'localTransform'> {\n\treturn {\n\t\tlocalTransform: {\n\t\t\tx,\n\t\t\ty,\n\t\t\trotation: 0,\n\t\t\tscaleX: 1,\n\t\t\tscaleY: 1,\n\t\t},\n\t};\n}\n\n/**\n * Create a world transform component with position only.\n * Typically used alongside createLocalTransform for initial state.\n *\n * @param x The x coordinate\n * @param y The y coordinate\n * @returns Component object suitable for spreading into spawn()\n */\nexport function createWorldTransform(x: number, y: number): Pick<TransformComponentTypes, 'worldTransform'> {\n\treturn {\n\t\tworldTransform: {\n\t\t\tx,\n\t\t\ty,\n\t\t\trotation: 0,\n\t\t\tscaleX: 1,\n\t\t\tscaleY: 1,\n\t\t},\n\t};\n}\n\n/**\n * Options for creating a full transform.\n */\nexport interface TransformOptions {\n\trotation?: number;\n\tscaleX?: number;\n\tscaleY?: number;\n\t/** Uniform scale (overrides scaleX/scaleY if provided) */\n\tscale?: number;\n}\n\n/**\n * Create both local and world transform components.\n * World transform is initialized to match local transform.\n *\n * @param x The x coordinate\n * @param y The y coordinate\n * @param options Optional rotation and scale\n * @returns Component object suitable for spreading into spawn()\n *\n * @example\n * ```typescript\n * ecs.spawn({\n * ...createTransform(100, 200),\n * sprite,\n * });\n *\n * // With rotation and scale\n * ecs.spawn({\n * ...createTransform(100, 200, { rotation: Math.PI / 4, scale: 2 }),\n * sprite,\n * });\n * ```\n */\nexport function createTransform(\n\tx: number,\n\ty: number,\n\toptions?: TransformOptions\n): TransformComponentTypes {\n\tconst scaleX = options?.scale ?? options?.scaleX ?? 1;\n\tconst scaleY = options?.scale ?? options?.scaleY ?? 1;\n\tconst rotation = options?.rotation ?? 0;\n\n\tconst transform = {\n\t\tx,\n\t\ty,\n\t\trotation,\n\t\tscaleX,\n\t\tscaleY,\n\t};\n\n\treturn {\n\t\tlocalTransform: { ...transform },\n\t\tworldTransform: { ...transform },\n\t};\n}\n\n// ==================== Bundle Factory ====================\n\n/**\n * Create a transform bundle for ECSpresso.\n *\n * This bundle provides:\n * - Transform propagation system that computes world transforms from local transforms\n * - Parent-first traversal ensures parents are processed before children\n * - Supports full transform hierarchy (position, rotation, scale)\n *\n * @example\n * ```typescript\n * const ecs = ECSpresso\n * .create<Components, Events, Resources>()\n * .withBundle(createTransformBundle())\n * .withBundle(createPhysics2DBundle())\n * .build();\n *\n * // Spawn entity with transform\n * ecs.spawn({\n * ...createTransform(100, 200),\n * velocity: { x: 50, y: 0 },\n * });\n * ```\n */\nexport function createTransformBundle<G extends string = 'transform'>(\n\toptions?: TransformBundleOptions<G>\n): Bundle<TransformComponentTypes, {}, {}, {}, {}, 'transform-propagation', G> {\n\tconst {\n\t\tsystemGroup = 'transform',\n\t\tpriority = 500,\n\t\tphase = 'postUpdate',\n\t} = options ?? {};\n\n\tconst bundle = new Bundle<TransformComponentTypes, {}, {}>('transform');\n\n\t// localTransform requires worldTransform — initialize from localTransform values\n\tbundle.registerRequired('localTransform', 'worldTransform', (lt) => ({\n\t\tx: lt.x, y: lt.y, rotation: lt.rotation, scaleX: lt.scaleX, scaleY: lt.scaleY,\n\t}));\n\n\tbundle\n\t\t.addSystem('transform-propagation')\n\t\t.setPriority(priority)\n\t\t.inPhase(phase)\n\t\t.inGroup(systemGroup)\n\t\t.setProcess((_queries, _deltaTime, ecs) => {\n\t\t\tpropagateTransforms(ecs);\n\t\t})\n\t\t.and();\n\n\treturn bundle as Bundle<TransformComponentTypes, {}, {}, {}, {}, 'transform-propagation', G>;\n}\n\n/**\n * Propagate transforms through the hierarchy.\n * Parent-first traversal ensures parents are computed before children.\n *\n * Runs unconditionally for all entities with transforms — user code can\n * freely mutate localTransform without needing to call markChanged.\n * Marks worldTransform as changed so downstream systems (e.g. renderer\n * sync) pick up the updated values.\n */\nfunction propagateTransforms(ecs: ECSpresso<TransformComponentTypes>): void {\n\tconst em = ecs.entityManager;\n\n\t// Use parent-first traversal for entities in hierarchy\n\tecs.forEachInHierarchy((entityId, parentId) => {\n\t\tconst localTransform = em.getComponent(entityId, 'localTransform');\n\t\tconst worldTransform = em.getComponent(entityId, 'worldTransform');\n\n\t\tif (!localTransform || !worldTransform) return;\n\n\t\tif (parentId === null) {\n\t\t\t// Root entity: world transform equals local transform\n\t\t\tcopyTransform(localTransform, worldTransform);\n\t\t} else {\n\t\t\t// Child entity: combine with parent's world transform\n\t\t\tconst parentWorld = em.getComponent(parentId, 'worldTransform');\n\t\t\tif (parentWorld) {\n\t\t\t\tcombineTransforms(parentWorld, localTransform, worldTransform);\n\t\t\t} else {\n\t\t\t\t// Parent has no world transform, treat as root\n\t\t\t\tcopyTransform(localTransform, worldTransform);\n\t\t\t}\n\t\t}\n\n\t\tecs.markChanged(entityId, 'worldTransform');\n\t});\n\n\t// Process orphaned entities (not in hierarchy but have transforms)\n\tconst orphanedEntities = ecs.getEntitiesWithQuery(['localTransform', 'worldTransform']);\n\tfor (const entity of orphanedEntities) {\n\t\tconst parentId = ecs.getParent(entity.id);\n\t\t// Only process if truly orphaned (no parent and not a root with children)\n\t\tif (parentId === null && ecs.getChildren(entity.id).length === 0) {\n\t\t\tconst { localTransform, worldTransform } = entity.components;\n\t\t\tcopyTransform(localTransform, worldTransform);\n\t\t\tecs.markChanged(entity.id, 'worldTransform');\n\t\t}\n\t}\n}\n\n/**\n * Copy transform values from source to destination.\n */\nfunction copyTransform(src: LocalTransform, dest: WorldTransform): void {\n\tdest.x = src.x;\n\tdest.y = src.y;\n\tdest.rotation = src.rotation;\n\tdest.scaleX = src.scaleX;\n\tdest.scaleY = src.scaleY;\n}\n\n/**\n * Combine parent world transform with child local transform into child world transform.\n */\nfunction combineTransforms(\n\tparent: WorldTransform,\n\tlocal: LocalTransform,\n\tworld: WorldTransform\n): void {\n\t// Apply parent's scale to local position\n\tconst scaledLocalX = local.x * parent.scaleX;\n\tconst scaledLocalY = local.y * parent.scaleY;\n\n\t// Rotate local position by parent's rotation\n\tconst cos = Math.cos(parent.rotation);\n\tconst sin = Math.sin(parent.rotation);\n\tconst rotatedX = scaledLocalX * cos - scaledLocalY * sin;\n\tconst rotatedY = scaledLocalX * sin + scaledLocalY * cos;\n\n\t// Add to parent's position\n\tworld.x = parent.x + rotatedX;\n\tworld.y = parent.y + rotatedY;\n\tworld.rotation = parent.rotation + local.rotation;\n\tworld.scaleX = parent.scaleX * local.scaleX;\n\tworld.scaleY = parent.scaleY * local.scaleY;\n}\n"
6
- ],
7
- "mappings": "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",
8
- "debugId": "B32D83741C8A3A2964756E2164756E21",
9
- "names": []
10
- }
@@ -1,4 +0,0 @@
1
- var{defineProperty:R,getOwnPropertyNames:_,getOwnPropertyDescriptor:T}=Object,B=Object.prototype.hasOwnProperty;var Q=new WeakMap,m=(z)=>{var H=Q.get(z),M;if(H)return H;if(H=R({},"__esModule",{value:!0}),z&&typeof z==="object"||typeof z==="function")_(z).map((N)=>!B.call(H,N)&&R(H,N,{get:()=>z[N],enumerable:!(M=T(z,N))||M.enumerable}));return Q.set(z,H),H};var d=(z,H)=>{for(var M in H)R(z,M,{get:H[M],enumerable:!0,configurable:!0,set:(N)=>H[M]=()=>N})};var l=(z,H)=>()=>(z&&(H=z(z=0)),H);var u=((z)=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(z,{get:(H,M)=>(typeof require<"u"?require:H)[M]}):z)(function(z){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+z+'" is not supported')});import{Bundle as O}from"ecspresso";function j(z){return z}var b=1.70158,w=b*1.525,r=b+1;var i=2*Math.PI/3,p=2*Math.PI/4.5;function f(z,H,M,N,W){let{from:X,easing:Y=j,loop:G="once",loops:P=1,onComplete:k}=W??{};return{tween:{steps:[{targets:[{component:z,path:H.split("."),from:X??null,to:M}],duration:N,easing:Y}],currentStep:0,elapsed:0,loop:G,totalLoops:P,completedLoops:0,direction:1,state:"pending",onComplete:k,justFinished:!1}}}function v(z,H){let{loop:M="once",loops:N=1,onComplete:W}=H??{};return{tween:{steps:z.map((X)=>({targets:X.targets.map((Y)=>({component:Y.component,path:Y.field.split("."),from:Y.from??null,to:Y.to})),duration:X.duration,easing:X.easing??j})),currentStep:0,elapsed:0,loop:M,totalLoops:N,completedLoops:0,direction:1,state:"pending",onComplete:W,justFinished:!1}}}function n(z){return{bundle:y(z),createTween:f,createTweenSequence:v}}var F={parent:{},key:""};function K(z,H){let M=H.length-1,N=z;for(let X=0;X<M;X++){let Y=H[X];if(Y===void 0)return null;let G=N[Y];if(G===null||G===void 0||typeof G!=="object")return null;N=G}let W=H[M];if(W===void 0)return null;if(!(W in N))return null;return F.parent=N,F.key=W,F}function E(z,H){let M=K(z,H);if(!M)return null;let N=M.parent[M.key];return typeof N==="number"?N:null}function x(z,H,M){let N=K(z,H);if(!N)return!1;return N.parent[N.key]=M,!0}function S(z,H,M){return z<H?H:z>M?M:z}function g(z,H){for(let M of z.steps)for(let N of M.targets){if(N.from!==null)continue;let W=H[N.component];if(!W||typeof W!=="object")continue;let X=E(W,N.path);if(X!==null)N.from=X;else N.from=0}}function I(z,H,M,N,W){let X=z.easing(H);for(let Y of z.targets){let G=M[Y.component];if(!G||typeof G!=="object")continue;let P=Y.from??0,k=P+(Y.to-P)*X;if(x(G,Y.path,k))W.markChanged(N,Y.component)}}function h(z,H,M,N){for(let W of z.targets){let X=H[W.component];if(!X||typeof X!=="object")continue;if(x(X,W.path,W.to))N.markChanged(M,W.component)}}function C(z){for(let H of z.steps)for(let M of H.targets){let N=M.from??0;M.from=M.to,M.to=N}}function y(z){let{systemGroup:H="tweens",priority:M=0,phase:N="update"}=z??{},W=new O("tweens");W.addSystem("tween-update").setPriority(M).inPhase(N).inGroup(H).addQuery("tweens",{with:["tween"]}).setProcess((J,$,Z)=>{for(let D of J.tweens){let U=D.components.tween,V=D.components;if(U.justFinished){U.justFinished=!1;continue}if(U.state==="complete")continue;if(U.state==="pending")g(U,V),U.state="active";if(!U.steps[U.currentStep])continue;U.elapsed+=$,X(U,V,D.id,Z)}}).and();function X(J,$,Z,D){while(!0){let U=J.steps[J.currentStep];if(!U)return;if(U.duration<=0){if(h(U,$,Z,D),J.elapsed=0,!Y(J,$,Z,D))return;continue}if(J.elapsed>=U.duration){h(U,$,Z,D);let q=J.elapsed-U.duration;if(J.elapsed=q,!Y(J,$,Z,D))return;continue}let V=S(J.elapsed/U.duration,0,1);I(U,V,$,Z,D);return}}function Y(J,$,Z,D){let U=J.currentStep+1;if(U<J.steps.length){J.currentStep=U;let V=J.steps[U];if(V)for(let q of V.targets){if(q.from!==null)continue;let L=$[q.component];if(!L||typeof L!=="object")continue;let A=E(L,q.path);q.from=A??0}return!0}return G(J,Z,D)}function G(J,$,Z){if(J.completedLoops++,J.loop==="once")return P(J,$,Z),!1;if(J.totalLoops>0&&J.completedLoops>=J.totalLoops)return P(J,$,Z),!1;if(J.loop==="yoyo")J.direction=J.direction===1?-1:1,C(J);return J.currentStep=0,J.elapsed>0}function P(J,$,Z){J.state="complete",J.justFinished=!0,k(Z,$,J),Z.commands.removeComponent($,"tween")}function k(J,$,Z){if(!Z.onComplete)return;let D={entityId:$,stepCount:Z.steps.length};J.eventBus.publish(Z.onComplete,D)}return W}export{v as createTweenSequence,n as createTweenKit,y as createTweenBundle,f as createTween};
2
-
3
- //# debugId=2CF1E4A2F7E8D74B64756E2164756E21
4
- //# sourceMappingURL=tween.js.map
@@ -1,11 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../src/bundles/tween.ts", "../src/utils/easing.ts"],
4
- "sourcesContent": [
5
- "/**\n * Tween Bundle for ECSpresso\n *\n * Declarative property animation within the ECS. Tween any numeric component\n * field over time with standard easing functions, sequences, and completion events.\n */\n\nimport { Bundle } from 'ecspresso';\nimport type { SystemPhase, ComponentsOfWorld, EventsOfWorld } from 'ecspresso';\nimport { linear, type EasingFn } from '../utils/easing';\n\n// ==================== Event Types ====================\n\n/**\n * Data structure published when a tween completes.\n * Use this type when defining tween completion events in your EventTypes interface.\n */\nexport interface TweenEventData {\n\t/** The entity ID the tween belongs to */\n\tentityId: number;\n\t/** Number of steps in the tween */\n\tstepCount: number;\n}\n\n/**\n * Extracts event names from EventTypes that have TweenEventData as their payload.\n * This ensures only compatible events can be used with tween.onComplete.\n * Uses `keyof EventTypes & string` to exclude number/symbol keys, ensuring the\n * result is always a string subtype and Tween<E> is assignable to Tween<Record<string, any>>.\n */\nexport type TweenEventName<EventTypes extends Record<string, any>> = {\n\t[K in keyof EventTypes & string]: EventTypes[K] extends TweenEventData ? K : never\n}[keyof EventTypes & string];\n\n// ==================== Component Types ====================\n\nexport interface TweenTarget {\n\t/** Component name on the entity */\n\tcomponent: string;\n\t/** Pre-split field path (e.g., ['position', 'x']) */\n\tpath: readonly string[];\n\t/** Starting value. null = resolve from current value on first tick */\n\tfrom: number | null;\n\t/** Target value */\n\tto: number;\n}\n\nexport interface TweenStep {\n\ttargets: TweenTarget[];\n\tduration: number;\n\teasing: EasingFn;\n}\n\nexport interface Tween<EventTypes extends Record<string, any> = Record<string, any>> {\n\tsteps: TweenStep[];\n\tcurrentStep: number;\n\telapsed: number;\n\tloop: LoopMode;\n\ttotalLoops: number;\n\tcompletedLoops: number;\n\tdirection: 1 | -1;\n\tstate: 'pending' | 'active' | 'complete';\n\tonComplete?: TweenEventName<EventTypes>;\n\tjustFinished: boolean;\n}\n\nexport type LoopMode = 'once' | 'loop' | 'yoyo';\n\n/**\n * Component types provided by the tween bundle.\n */\nexport interface TweenComponentTypes<EventTypes extends Record<string, any> = Record<string, any>> {\n\ttween: Tween<EventTypes>;\n}\n\n// ==================== Bundle Options ====================\n\nexport interface TweenBundleOptions<G extends string = 'tweens'> {\n\t/** System group name (default: 'tweens') */\n\tsystemGroup?: G;\n\t/** Priority for tween update system (default: 0) */\n\tpriority?: number;\n\t/** Execution phase (default: 'update') */\n\tphase?: SystemPhase;\n}\n\n// ==================== Helper Functions ====================\n\nexport interface TweenOptions<EventTypes extends Record<string, any> = Record<string, any>> {\n\t/** Explicit starting value (default: captures current value on first tick) */\n\tfrom?: number;\n\t/** Easing function (default: linear) */\n\teasing?: EasingFn;\n\t/** Loop mode (default: 'once') */\n\tloop?: LoopMode;\n\t/** Number of loops. -1 = infinite (default: 1) */\n\tloops?: number;\n\t/** Event name to publish when tween completes */\n\tonComplete?: TweenEventName<EventTypes>;\n}\n\n/**\n * Create a single-target tween component.\n *\n * @param component Component name on the entity\n * @param field Field path (dot-separated for nested, e.g. 'position.x')\n * @param to Target value\n * @param duration Duration in seconds\n * @param options Optional configuration\n * @returns Component object suitable for spreading into spawn()\n */\nexport function createTween<EventTypes extends Record<string, any> = Record<string, any>>(\n\tcomponent: string,\n\tfield: string,\n\tto: number,\n\tduration: number,\n\toptions?: TweenOptions<EventTypes>,\n): Pick<TweenComponentTypes<EventTypes>, 'tween'> {\n\tconst {\n\t\tfrom,\n\t\teasing = linear,\n\t\tloop = 'once',\n\t\tloops = 1,\n\t\tonComplete,\n\t} = options ?? {};\n\n\treturn {\n\t\ttween: {\n\t\t\tsteps: [{\n\t\t\t\ttargets: [{\n\t\t\t\t\tcomponent,\n\t\t\t\t\tpath: field.split('.'),\n\t\t\t\t\tfrom: from ?? null,\n\t\t\t\t\tto,\n\t\t\t\t}],\n\t\t\t\tduration,\n\t\t\t\teasing,\n\t\t\t}],\n\t\t\tcurrentStep: 0,\n\t\t\telapsed: 0,\n\t\t\tloop,\n\t\t\ttotalLoops: loops,\n\t\t\tcompletedLoops: 0,\n\t\t\tdirection: 1,\n\t\t\tstate: 'pending',\n\t\t\tonComplete,\n\t\t\tjustFinished: false,\n\t\t},\n\t};\n}\n\nexport interface TweenSequenceStepInput {\n\ttargets: ReadonlyArray<{\n\t\tcomponent: string;\n\t\tfield: string;\n\t\tto: number;\n\t\tfrom?: number;\n\t}>;\n\tduration: number;\n\teasing?: EasingFn;\n}\n\nexport interface TweenSequenceOptions<EventTypes extends Record<string, any> = Record<string, any>> {\n\t/** Loop mode (default: 'once') */\n\tloop?: LoopMode;\n\t/** Number of loops. -1 = infinite (default: 1) */\n\tloops?: number;\n\t/** Event name to publish when tween completes */\n\tonComplete?: TweenEventName<EventTypes>;\n}\n\n/**\n * Create a multi-step tween sequence. Each step can have parallel targets.\n *\n * @param steps Array of step definitions\n * @param options Optional configuration\n * @returns Component object suitable for spreading into spawn()\n */\nexport function createTweenSequence<EventTypes extends Record<string, any> = Record<string, any>>(\n\tsteps: ReadonlyArray<TweenSequenceStepInput>,\n\toptions?: TweenSequenceOptions<EventTypes>,\n): Pick<TweenComponentTypes<EventTypes>, 'tween'> {\n\tconst {\n\t\tloop = 'once',\n\t\tloops = 1,\n\t\tonComplete,\n\t} = options ?? {};\n\n\treturn {\n\t\ttween: {\n\t\t\tsteps: steps.map((step) => ({\n\t\t\t\ttargets: step.targets.map((target) => ({\n\t\t\t\t\tcomponent: target.component,\n\t\t\t\t\tpath: target.field.split('.'),\n\t\t\t\t\tfrom: target.from ?? null,\n\t\t\t\t\tto: target.to,\n\t\t\t\t})),\n\t\t\t\tduration: step.duration,\n\t\t\t\teasing: step.easing ?? linear,\n\t\t\t})),\n\t\t\tcurrentStep: 0,\n\t\t\telapsed: 0,\n\t\t\tloop,\n\t\t\ttotalLoops: loops,\n\t\t\tcompletedLoops: 0,\n\t\t\tdirection: 1,\n\t\t\tstate: 'pending',\n\t\t\tonComplete,\n\t\t\tjustFinished: false,\n\t\t},\n\t};\n}\n\n// ==================== Kit Types ====================\n\ntype AnyECSpresso = import('ecspresso').default<any, any, any, any, any, any, any>;\n\n/**\n * Recursively produce a union of dot-separated paths that resolve to `number`\n * within type T. Depth-limited to 4 levels to prevent TS recursion errors.\n *\n * @example\n * NumericPaths<{ x: number; y: number }> // 'x' | 'y'\n * NumericPaths<{ position: { x: number }; rotation: number }> // 'position.x' | 'rotation'\n */\nexport type NumericPaths<T, Depth extends readonly unknown[] = []> =\n\tDepth['length'] extends 4 ? never :\n\tT extends readonly unknown[] ? never :\n\tT extends Record<string, unknown>\n\t\t? { [K in keyof T & string]:\n\t\t\tNonNullable<T[K]> extends number\n\t\t\t\t? K\n\t\t\t\t: NonNullable<T[K]> extends readonly unknown[]\n\t\t\t\t\t? never\n\t\t\t\t\t: NonNullable<T[K]> extends Record<string, unknown>\n\t\t\t\t\t\t? `${K}.${NumericPaths<NonNullable<T[K]>, [...Depth, unknown]>}`\n\t\t\t\t\t\t: never\n\t\t}[keyof T & string]\n\t\t: never;\n\n/**\n * Discriminated union over component names: each variant constrains `field`\n * to the numeric paths of that component. TS narrows inline object literals\n * by `component` discriminant — zero runtime overhead.\n */\nexport type TypedTweenTargetInput<C extends Record<string, any>> = {\n\t[K in keyof C & string]: {\n\t\tcomponent: K;\n\t\tfield: NumericPaths<C[K]>;\n\t\tto: number;\n\t\tfrom?: number;\n\t}\n}[keyof C & string];\n\nexport interface TypedTweenSequenceStepInput<C extends Record<string, any>> {\n\ttargets: ReadonlyArray<TypedTweenTargetInput<C>>;\n\tduration: number;\n\teasing?: EasingFn;\n}\n\nexport interface TweenKit<W extends AnyECSpresso, G extends string = 'tweens'> {\n\tbundle: Bundle<TweenComponentTypes<EventsOfWorld<W>>, EventsOfWorld<W>, {}, {}, {}, 'tween-update', G>;\n\tcreateTween: <K extends keyof ComponentsOfWorld<W> & string>(\n\t\tcomponent: K,\n\t\tfield: NumericPaths<ComponentsOfWorld<W>[K]>,\n\t\tto: number,\n\t\tduration: number,\n\t\toptions?: TweenOptions<EventsOfWorld<W>>,\n\t) => Pick<TweenComponentTypes<EventsOfWorld<W>>, 'tween'>;\n\tcreateTweenSequence: (\n\t\tsteps: ReadonlyArray<TypedTweenSequenceStepInput<ComponentsOfWorld<W>>>,\n\t\toptions?: TweenSequenceOptions<EventsOfWorld<W>>,\n\t) => Pick<TweenComponentTypes<EventsOfWorld<W>>, 'tween'>;\n}\n\n/**\n * Create a typed tween kit that captures the world type W.\n *\n * The returned `createTween` and `createTweenSequence` validate component names\n * and field paths at compile time. Runtime behavior is identical to the standalone\n * functions — all validation is type-level only.\n *\n * @template W - Concrete ECS world type (e.g. `typeof ecs`)\n * @template G - System group name (default: 'tweens')\n * @param options - Optional bundle configuration (same as createTweenBundle)\n * @returns A kit object with bundle, createTween, createTweenSequence\n *\n * @example\n * ```typescript\n * const kit = createTweenKit<typeof ecs>();\n * // or: const kit = createTweenKit<ECS>();\n *\n * const ecs = ECSpresso.create()\n * .withBundle(kit.bundle)\n * .build();\n *\n * // Type-safe: 'position' must be a component, 'x' must be a numeric field\n * kit.createTween('position', 'x', 100, 1);\n *\n * // Type error: 'z' is not a field of position\n * kit.createTween('position', 'z', 100, 1);\n * ```\n */\nexport function createTweenKit<W extends AnyECSpresso, G extends string = 'tweens'>(\n\toptions?: TweenBundleOptions<G>,\n): TweenKit<W, G> {\n\treturn {\n\t\tbundle: createTweenBundle<EventsOfWorld<W>, G>(options),\n\t\tcreateTween: createTween as TweenKit<W, G>['createTween'],\n\t\tcreateTweenSequence: createTweenSequence as TweenKit<W, G>['createTweenSequence'],\n\t};\n}\n\n// ==================== Field Path Resolution ====================\n\n/**\n * Module-scoped mutable result to avoid per-call allocation in hot path.\n */\nconst _fieldRef: { parent: Record<string, unknown>; key: string } = { parent: {} as Record<string, unknown>, key: '' };\n\n/**\n * Traverse an object by path segments. Returns the parent object and final key\n * for read/write, or null if any segment is missing.\n */\nfunction resolveField(obj: Record<string, unknown>, path: readonly string[]): typeof _fieldRef | null {\n\tconst lastIdx = path.length - 1;\n\tlet current: Record<string, unknown> = obj;\n\n\tfor (let i = 0; i < lastIdx; i++) {\n\t\tconst segment = path[i];\n\t\tif (segment === undefined) return null;\n\t\tconst next = current[segment];\n\t\tif (next === null || next === undefined || typeof next !== 'object') return null;\n\t\tcurrent = next as Record<string, unknown>;\n\t}\n\n\tconst finalKey = path[lastIdx];\n\tif (finalKey === undefined) return null;\n\tif (!(finalKey in current)) return null;\n\n\t_fieldRef.parent = current;\n\t_fieldRef.key = finalKey;\n\treturn _fieldRef;\n}\n\nfunction readField(obj: Record<string, unknown>, path: readonly string[]): number | null {\n\tconst ref = resolveField(obj, path);\n\tif (!ref) return null;\n\tconst val = ref.parent[ref.key];\n\treturn typeof val === 'number' ? val : null;\n}\n\nfunction writeField(obj: Record<string, unknown>, path: readonly string[], value: number): boolean {\n\tconst ref = resolveField(obj, path);\n\tif (!ref) return false;\n\tref.parent[ref.key] = value;\n\treturn true;\n}\n\n// ==================== System Logic ====================\n\nfunction clamp(value: number, min: number, max: number): number {\n\treturn value < min ? min : value > max ? max : value;\n}\n\n/**\n * Resolve all null `from` values by reading current component field values.\n */\nfunction resolveFromValues<E extends Record<string, any>>(\n\ttween: Tween<E>,\n\tentityComponents: Record<string, unknown>,\n): void {\n\tfor (const step of tween.steps) {\n\t\tfor (const target of step.targets) {\n\t\t\tif (target.from !== null) continue;\n\t\t\tconst comp = entityComponents[target.component];\n\t\t\tif (!comp || typeof comp !== 'object') continue;\n\t\t\tconst val = readField(comp as Record<string, unknown>, target.path);\n\t\t\tif (val !== null) {\n\t\t\t\ttarget.from = val;\n\t\t\t} else {\n\t\t\t\ttarget.from = 0;\n\t\t\t}\n\t\t}\n\t}\n}\n\n/**\n * Apply interpolation for a step's targets at a given progress.\n */\nfunction applyStep(\n\tstep: TweenStep,\n\tprogress: number,\n\tentityComponents: Record<string, unknown>,\n\tentityId: number,\n\tecs: { markChanged: (entityId: number, componentName: any) => void },\n): void {\n\tconst easedT = step.easing(progress);\n\n\tfor (const target of step.targets) {\n\t\tconst comp = entityComponents[target.component];\n\t\tif (!comp || typeof comp !== 'object') continue;\n\t\tconst from = target.from ?? 0;\n\t\tconst value = from + (target.to - from) * easedT;\n\t\tconst written = writeField(comp as Record<string, unknown>, target.path, value);\n\t\tif (written) {\n\t\t\tecs.markChanged(entityId, target.component);\n\t\t}\n\t}\n}\n\n/**\n * Snap all targets in a step to their final values (from or to depending on direction).\n */\nfunction snapStepToEnd(\n\tstep: TweenStep,\n\tentityComponents: Record<string, unknown>,\n\tentityId: number,\n\tecs: { markChanged: (entityId: number, componentName: any) => void },\n): void {\n\tfor (const target of step.targets) {\n\t\tconst comp = entityComponents[target.component];\n\t\tif (!comp || typeof comp !== 'object') continue;\n\t\tconst written = writeField(comp as Record<string, unknown>, target.path, target.to);\n\t\tif (written) {\n\t\t\tecs.markChanged(entityId, target.component);\n\t\t}\n\t}\n}\n\n/**\n * Reverse all from/to values in every step (for yoyo).\n */\nfunction reverseAllTargets<E extends Record<string, any>>(tween: Tween<E>): void {\n\tfor (const step of tween.steps) {\n\t\tfor (const target of step.targets) {\n\t\t\tconst tmp = target.from ?? 0;\n\t\t\ttarget.from = target.to;\n\t\t\ttarget.to = tmp;\n\t\t}\n\t}\n}\n\n// ==================== Bundle Factory ====================\n\n/**\n * Create a tween bundle for ECSpresso.\n *\n * This bundle provides:\n * - Tween system that processes all tween components each frame\n * - Support for single-field, multi-target, and multi-step sequences\n * - 31 standard easing functions\n * - Loop modes: once, loop, yoyo\n * - `justFinished` flag for one-frame completion detection\n * - `onComplete` event publishing\n * - Change detection via markChanged\n */\nexport function createTweenBundle<EventTypes extends Record<string, any> = Record<string, any>, G extends string = 'tweens'>(\n\toptions?: TweenBundleOptions<G>\n): Bundle<TweenComponentTypes<EventTypes>, EventTypes, {}, {}, {}, 'tween-update', G> {\n\tconst {\n\t\tsystemGroup = 'tweens',\n\t\tpriority = 0,\n\t\tphase = 'update',\n\t} = options ?? {};\n\n\tconst bundle = new Bundle<TweenComponentTypes<EventTypes>, EventTypes, {}>('tweens');\n\n\tbundle\n\t\t.addSystem('tween-update')\n\t\t.setPriority(priority)\n\t\t.inPhase(phase)\n\t\t.inGroup(systemGroup)\n\t\t.addQuery('tweens', {\n\t\t\twith: ['tween'],\n\t\t})\n\t\t.setProcess((queries, deltaTime, ecs) => {\n\t\t\tfor (const entity of queries.tweens) {\n\t\t\t\tconst tween = entity.components.tween as Tween<EventTypes>;\n\t\t\t\tconst entityComponents = entity.components as Record<string, unknown>;\n\n\t\t\t\t// Reset justFinished flag from previous frame\n\t\t\t\tif (tween.justFinished) {\n\t\t\t\t\ttween.justFinished = false;\n\t\t\t\t\t// Component removal was queued, skip processing\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t// Skip completed tweens\n\t\t\t\tif (tween.state === 'complete') continue;\n\n\t\t\t\t// Resolve pending state: capture null from values\n\t\t\t\tif (tween.state === 'pending') {\n\t\t\t\t\tresolveFromValues(tween, entityComponents);\n\t\t\t\t\ttween.state = 'active';\n\t\t\t\t}\n\n\t\t\t\t// Process active tween\n\t\t\t\tconst currentStep = tween.steps[tween.currentStep];\n\t\t\t\tif (!currentStep) continue;\n\n\t\t\t\ttween.elapsed += deltaTime;\n\n\t\t\t\t// Process steps, handling overflow across multiple steps\n\t\t\t\tprocessTweenProgress(tween, entityComponents, entity.id, ecs);\n\t\t\t}\n\t\t})\n\t\t.and();\n\n\tfunction processTweenProgress(\n\t\ttween: Tween<EventTypes>,\n\t\tentityComponents: Record<string, unknown>,\n\t\tentityId: number,\n\t\tecs: { markChanged: (entityId: number, componentName: any) => void; eventBus: { publish: (event: any, data: any) => void }; commands: { removeComponent: (entityId: number, componentName: any) => void } },\n\t): void {\n\t\t// eslint-disable-next-line no-constant-condition\n\t\twhile (true) {\n\t\t\tconst currentStep = tween.steps[tween.currentStep];\n\t\t\tif (!currentStep) return;\n\n\t\t\t// Zero-duration steps complete immediately\n\t\t\tif (currentStep.duration <= 0) {\n\t\t\t\tsnapStepToEnd(currentStep, entityComponents, entityId, ecs);\n\t\t\t\ttween.elapsed = 0;\n\n\t\t\t\tif (!advanceStep(tween, entityComponents, entityId, ecs)) return;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (tween.elapsed >= currentStep.duration) {\n\t\t\t\t// Step complete — snap to end and carry overflow\n\t\t\t\tsnapStepToEnd(currentStep, entityComponents, entityId, ecs);\n\t\t\t\tconst overflow = tween.elapsed - currentStep.duration;\n\t\t\t\ttween.elapsed = overflow;\n\n\t\t\t\tif (!advanceStep(tween, entityComponents, entityId, ecs)) return;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// Step in progress — interpolate\n\t\t\tconst progress = clamp(tween.elapsed / currentStep.duration, 0, 1);\n\t\t\tapplyStep(currentStep, progress, entityComponents, entityId, ecs);\n\t\t\treturn;\n\t\t}\n\t}\n\n\t/**\n\t * Advance to next step. Returns true if there's more work to process,\n\t * false if the tween has completed or looped.\n\t */\n\tfunction advanceStep(\n\t\ttween: Tween<EventTypes>,\n\t\tentityComponents: Record<string, unknown>,\n\t\tentityId: number,\n\t\tecs: { markChanged: (entityId: number, componentName: any) => void; eventBus: { publish: (event: any, data: any) => void }; commands: { removeComponent: (entityId: number, componentName: any) => void } },\n\t): boolean {\n\t\tconst nextStep = tween.currentStep + 1;\n\n\t\tif (nextStep < tween.steps.length) {\n\t\t\t// More steps — resolve from values for next step and continue\n\t\t\ttween.currentStep = nextStep;\n\t\t\tconst step = tween.steps[nextStep];\n\t\t\tif (step) {\n\t\t\t\tfor (const target of step.targets) {\n\t\t\t\t\tif (target.from !== null) continue;\n\t\t\t\t\tconst comp = entityComponents[target.component];\n\t\t\t\t\tif (!comp || typeof comp !== 'object') continue;\n\t\t\t\t\tconst val = readField(comp as Record<string, unknown>, target.path);\n\t\t\t\t\ttarget.from = val ?? 0;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\n\t\t// All steps done — handle loop/complete\n\t\treturn handleTweenEnd(tween, entityId, ecs);\n\t}\n\n\tfunction handleTweenEnd(\n\t\ttween: Tween<EventTypes>,\n\t\tentityId: number,\n\t\tecs: { markChanged: (entityId: number, componentName: any) => void; eventBus: { publish: (event: any, data: any) => void }; commands: { removeComponent: (entityId: number, componentName: any) => void } },\n\t): boolean {\n\t\ttween.completedLoops++;\n\n\t\tif (tween.loop === 'once') {\n\t\t\tcompleteTween(tween, entityId, ecs);\n\t\t\treturn false;\n\t\t}\n\n\t\t// Check if finite loops exhausted\n\t\tif (tween.totalLoops > 0 && tween.completedLoops >= tween.totalLoops) {\n\t\t\tcompleteTween(tween, entityId, ecs);\n\t\t\treturn false;\n\t\t}\n\n\t\t// Loop continues\n\t\tif (tween.loop === 'yoyo') {\n\t\t\ttween.direction = tween.direction === 1 ? -1 : 1;\n\t\t\treverseAllTargets(tween);\n\t\t}\n\n\t\ttween.currentStep = 0;\n\n\t\t// For 'loop' mode, from values stay as-is so the animation replays identically.\n\t\t// For 'yoyo' mode, reverseAllTargets already swapped from/to.\n\n\t\treturn tween.elapsed > 0;\n\t}\n\n\tfunction completeTween(\n\t\ttween: Tween<EventTypes>,\n\t\tentityId: number,\n\t\tecs: { eventBus: { publish: (event: any, data: any) => void }; commands: { removeComponent: (entityId: number, componentName: any) => void } },\n\t): void {\n\t\ttween.state = 'complete';\n\t\ttween.justFinished = true;\n\n\t\tpublishTweenEvent(ecs, entityId, tween);\n\t\tecs.commands.removeComponent(entityId, 'tween');\n\t}\n\n\tfunction publishTweenEvent(\n\t\tecs: { eventBus: { publish: (event: any, data: any) => void } },\n\t\tentityId: number,\n\t\ttween: Tween<EventTypes>,\n\t): void {\n\t\tif (!tween.onComplete) return;\n\t\tconst eventData: TweenEventData = {\n\t\t\tentityId,\n\t\t\tstepCount: tween.steps.length,\n\t\t};\n\t\tecs.eventBus.publish(tween.onComplete, eventData);\n\t}\n\n\treturn bundle as Bundle<TweenComponentTypes<EventTypes>, EventTypes, {}, {}, {}, 'tween-update', G>;\n}\n",
6
- "/**\n * Easing Functions\n *\n * 31 standard easing functions for animation. Pure math, no dependencies.\n */\n\nexport type EasingFn = (t: number) => number;\n\nexport function linear(t: number): number {\n\treturn t;\n}\n\n// Quad\nexport function easeInQuad(t: number): number {\n\treturn t * t;\n}\n\nexport function easeOutQuad(t: number): number {\n\treturn t * (2 - t);\n}\n\nexport function easeInOutQuad(t: number): number {\n\treturn t < 0.5\n\t\t? 2 * t * t\n\t\t: -1 + (4 - 2 * t) * t;\n}\n\n// Cubic\nexport function easeInCubic(t: number): number {\n\treturn t * t * t;\n}\n\nexport function easeOutCubic(t: number): number {\n\tconst t1 = t - 1;\n\treturn t1 * t1 * t1 + 1;\n}\n\nexport function easeInOutCubic(t: number): number {\n\treturn t < 0.5\n\t\t? 4 * t * t * t\n\t\t: 1 + (t - 1) * (2 * t - 2) * (2 * t - 2);\n}\n\n// Quart\nexport function easeInQuart(t: number): number {\n\treturn t * t * t * t;\n}\n\nexport function easeOutQuart(t: number): number {\n\tconst t1 = t - 1;\n\treturn 1 - t1 * t1 * t1 * t1;\n}\n\nexport function easeInOutQuart(t: number): number {\n\treturn t < 0.5\n\t\t? 8 * t * t * t * t\n\t\t: 1 - 8 * (t - 1) * (t - 1) * (t - 1) * (t - 1);\n}\n\n// Quint\nexport function easeInQuint(t: number): number {\n\treturn t * t * t * t * t;\n}\n\nexport function easeOutQuint(t: number): number {\n\tconst t1 = t - 1;\n\treturn 1 + t1 * t1 * t1 * t1 * t1;\n}\n\nexport function easeInOutQuint(t: number): number {\n\treturn t < 0.5\n\t\t? 16 * t * t * t * t * t\n\t\t: 1 + 16 * (t - 1) * (t - 1) * (t - 1) * (t - 1) * (t - 1);\n}\n\n// Sine\nexport function easeInSine(t: number): number {\n\treturn 1 - Math.cos((t * Math.PI) / 2);\n}\n\nexport function easeOutSine(t: number): number {\n\treturn Math.sin((t * Math.PI) / 2);\n}\n\nexport function easeInOutSine(t: number): number {\n\treturn -(Math.cos(Math.PI * t) - 1) / 2;\n}\n\n// Expo\nexport function easeInExpo(t: number): number {\n\treturn t === 0 ? 0 : Math.pow(2, 10 * (t - 1));\n}\n\nexport function easeOutExpo(t: number): number {\n\treturn t === 1 ? 1 : 1 - Math.pow(2, -10 * t);\n}\n\nexport function easeInOutExpo(t: number): number {\n\tif (t === 0) return 0;\n\tif (t === 1) return 1;\n\treturn t < 0.5\n\t\t? Math.pow(2, 20 * t - 10) / 2\n\t\t: (2 - Math.pow(2, -20 * t + 10)) / 2;\n}\n\n// Circ\nexport function easeInCirc(t: number): number {\n\treturn 1 - Math.sqrt(1 - t * t);\n}\n\nexport function easeOutCirc(t: number): number {\n\tconst t1 = t - 1;\n\treturn Math.sqrt(1 - t1 * t1);\n}\n\nexport function easeInOutCirc(t: number): number {\n\treturn t < 0.5\n\t\t? (1 - Math.sqrt(1 - 4 * t * t)) / 2\n\t\t: (Math.sqrt(1 - (-2 * t + 2) * (-2 * t + 2)) + 1) / 2;\n}\n\n// Back\nconst BACK_C1 = 1.70158;\nconst BACK_C2 = BACK_C1 * 1.525;\nconst BACK_C3 = BACK_C1 + 1;\n\nexport function easeInBack(t: number): number {\n\treturn BACK_C3 * t * t * t - BACK_C1 * t * t;\n}\n\nexport function easeOutBack(t: number): number {\n\tconst t1 = t - 1;\n\treturn 1 + BACK_C3 * t1 * t1 * t1 + BACK_C1 * t1 * t1;\n}\n\nexport function easeInOutBack(t: number): number {\n\treturn t < 0.5\n\t\t? ((2 * t) * (2 * t) * ((BACK_C2 + 1) * 2 * t - BACK_C2)) / 2\n\t\t: ((2 * t - 2) * (2 * t - 2) * ((BACK_C2 + 1) * (t * 2 - 2) + BACK_C2) + 2) / 2;\n}\n\n// Elastic\nconst ELASTIC_C4 = (2 * Math.PI) / 3;\nconst ELASTIC_C5 = (2 * Math.PI) / 4.5;\n\nexport function easeInElastic(t: number): number {\n\tif (t === 0) return 0;\n\tif (t === 1) return 1;\n\treturn -Math.pow(2, 10 * t - 10) * Math.sin((10 * t - 10.75) * ELASTIC_C4);\n}\n\nexport function easeOutElastic(t: number): number {\n\tif (t === 0) return 0;\n\tif (t === 1) return 1;\n\treturn Math.pow(2, -10 * t) * Math.sin((10 * t - 0.75) * ELASTIC_C4) + 1;\n}\n\nexport function easeInOutElastic(t: number): number {\n\tif (t === 0) return 0;\n\tif (t === 1) return 1;\n\treturn t < 0.5\n\t\t? -(Math.pow(2, 20 * t - 10) * Math.sin((20 * t - 11.125) * ELASTIC_C5)) / 2\n\t\t: (Math.pow(2, -20 * t + 10) * Math.sin((20 * t - 11.125) * ELASTIC_C5)) / 2 + 1;\n}\n\n// Bounce\nexport function easeOutBounce(t: number): number {\n\tconst n1 = 7.5625;\n\tconst d1 = 2.75;\n\n\tif (t < 1 / d1) {\n\t\treturn n1 * t * t;\n\t} else if (t < 2 / d1) {\n\t\tconst t1 = t - 1.5 / d1;\n\t\treturn n1 * t1 * t1 + 0.75;\n\t} else if (t < 2.5 / d1) {\n\t\tconst t1 = t - 2.25 / d1;\n\t\treturn n1 * t1 * t1 + 0.9375;\n\t} else {\n\t\tconst t1 = t - 2.625 / d1;\n\t\treturn n1 * t1 * t1 + 0.984375;\n\t}\n}\n\nexport function easeInBounce(t: number): number {\n\treturn 1 - easeOutBounce(1 - t);\n}\n\nexport function easeInOutBounce(t: number): number {\n\treturn t < 0.5\n\t\t? (1 - easeOutBounce(1 - 2 * t)) / 2\n\t\t: (1 + easeOutBounce(2 * t - 1)) / 2;\n}\n\n/** Runtime lookup of all easing functions by name */\nexport const easings = {\n\tlinear,\n\teaseInQuad,\n\teaseOutQuad,\n\teaseInOutQuad,\n\teaseInCubic,\n\teaseOutCubic,\n\teaseInOutCubic,\n\teaseInQuart,\n\teaseOutQuart,\n\teaseInOutQuart,\n\teaseInQuint,\n\teaseOutQuint,\n\teaseInOutQuint,\n\teaseInSine,\n\teaseOutSine,\n\teaseInOutSine,\n\teaseInExpo,\n\teaseOutExpo,\n\teaseInOutExpo,\n\teaseInCirc,\n\teaseOutCirc,\n\teaseInOutCirc,\n\teaseInBack,\n\teaseOutBack,\n\teaseInOutBack,\n\teaseInElastic,\n\teaseOutElastic,\n\teaseInOutElastic,\n\teaseInBounce,\n\teaseOutBounce,\n\teaseInOutBounce,\n} as const;\n"
7
- ],
8
- "mappings": "uuBAOA,iBAAS,kBCCF,SAAS,CAAM,CAAC,EAAmB,CACzC,OAAO,EAiHR,IAAM,EAAU,QACV,EAAU,EAAU,MACpB,EAAU,EAAU,EAkB1B,IAAM,EAAc,EAAI,KAAK,GAAM,EAC7B,EAAc,EAAI,KAAK,GAAM,IDhC5B,SAAS,CAAyE,CACxF,EACA,EACA,EACA,EACA,EACiD,CACjD,IACC,OACA,SAAS,EACT,OAAO,OACP,QAAQ,EACR,cACG,GAAW,CAAC,EAEhB,MAAO,CACN,MAAO,CACN,MAAO,CAAC,CACP,QAAS,CAAC,CACT,YACA,KAAM,EAAM,MAAM,GAAG,EACrB,KAAM,GAAQ,KACd,IACD,CAAC,EACD,WACA,QACD,CAAC,EACD,YAAa,EACb,QAAS,EACT,OACA,WAAY,EACZ,eAAgB,EAChB,UAAW,EACX,MAAO,UACP,aACA,aAAc,EACf,CACD,EA8BM,SAAS,CAAiF,CAChG,EACA,EACiD,CACjD,IACC,OAAO,OACP,QAAQ,EACR,cACG,GAAW,CAAC,EAEhB,MAAO,CACN,MAAO,CACN,MAAO,EAAM,IAAI,CAAC,KAAU,CAC3B,QAAS,EAAK,QAAQ,IAAI,CAAC,KAAY,CACtC,UAAW,EAAO,UAClB,KAAM,EAAO,MAAM,MAAM,GAAG,EAC5B,KAAM,EAAO,MAAQ,KACrB,GAAI,EAAO,EACZ,EAAE,EACF,SAAU,EAAK,SACf,OAAQ,EAAK,QAAU,CACxB,EAAE,EACF,YAAa,EACb,QAAS,EACT,OACA,WAAY,EACZ,eAAgB,EAChB,UAAW,EACX,MAAO,UACP,aACA,aAAc,EACf,CACD,EA6FM,SAAS,CAAmE,CAClF,EACiB,CACjB,MAAO,CACN,OAAQ,EAAuC,CAAO,EACtD,YAAa,EACb,oBAAqB,CACtB,EAQD,IAAM,EAA8D,CAAE,OAAQ,CAAC,EAA8B,IAAK,EAAG,EAMrH,SAAS,CAAY,CAAC,EAA8B,EAAkD,CACrG,IAAM,EAAU,EAAK,OAAS,EAC1B,EAAmC,EAEvC,QAAS,EAAI,EAAG,EAAI,EAAS,IAAK,CACjC,IAAM,EAAU,EAAK,GACrB,GAAI,IAAY,OAAW,OAAO,KAClC,IAAM,EAAO,EAAQ,GACrB,GAAI,IAAS,MAAQ,IAAS,QAAa,OAAO,IAAS,SAAU,OAAO,KAC5E,EAAU,EAGX,IAAM,EAAW,EAAK,GACtB,GAAI,IAAa,OAAW,OAAO,KACnC,GAAI,EAAE,KAAY,GAAU,OAAO,KAInC,OAFA,EAAU,OAAS,EACnB,EAAU,IAAM,EACT,EAGR,SAAS,CAAS,CAAC,EAA8B,EAAwC,CACxF,IAAM,EAAM,EAAa,EAAK,CAAI,EAClC,GAAI,CAAC,EAAK,OAAO,KACjB,IAAM,EAAM,EAAI,OAAO,EAAI,KAC3B,OAAO,OAAO,IAAQ,SAAW,EAAM,KAGxC,SAAS,CAAU,CAAC,EAA8B,EAAyB,EAAwB,CAClG,IAAM,EAAM,EAAa,EAAK,CAAI,EAClC,GAAI,CAAC,EAAK,MAAO,GAEjB,OADA,EAAI,OAAO,EAAI,KAAO,EACf,GAKR,SAAS,CAAK,CAAC,EAAe,EAAa,EAAqB,CAC/D,OAAO,EAAQ,EAAM,EAAM,EAAQ,EAAM,EAAM,EAMhD,SAAS,CAAgD,CACxD,EACA,EACO,CACP,QAAW,KAAQ,EAAM,MACxB,QAAW,KAAU,EAAK,QAAS,CAClC,GAAI,EAAO,OAAS,KAAM,SAC1B,IAAM,EAAO,EAAiB,EAAO,WACrC,GAAI,CAAC,GAAQ,OAAO,IAAS,SAAU,SACvC,IAAM,EAAM,EAAU,EAAiC,EAAO,IAAI,EAClE,GAAI,IAAQ,KACX,EAAO,KAAO,EAEd,OAAO,KAAO,GASlB,SAAS,CAAS,CACjB,EACA,EACA,EACA,EACA,EACO,CACP,IAAM,EAAS,EAAK,OAAO,CAAQ,EAEnC,QAAW,KAAU,EAAK,QAAS,CAClC,IAAM,EAAO,EAAiB,EAAO,WACrC,GAAI,CAAC,GAAQ,OAAO,IAAS,SAAU,SACvC,IAAM,EAAO,EAAO,MAAQ,EACtB,EAAQ,GAAQ,EAAO,GAAK,GAAQ,EAE1C,GADgB,EAAW,EAAiC,EAAO,KAAM,CAAK,EAE7E,EAAI,YAAY,EAAU,EAAO,SAAS,GAQ7C,SAAS,CAAa,CACrB,EACA,EACA,EACA,EACO,CACP,QAAW,KAAU,EAAK,QAAS,CAClC,IAAM,EAAO,EAAiB,EAAO,WACrC,GAAI,CAAC,GAAQ,OAAO,IAAS,SAAU,SAEvC,GADgB,EAAW,EAAiC,EAAO,KAAM,EAAO,EAAE,EAEjF,EAAI,YAAY,EAAU,EAAO,SAAS,GAQ7C,SAAS,CAAgD,CAAC,EAAuB,CAChF,QAAW,KAAQ,EAAM,MACxB,QAAW,KAAU,EAAK,QAAS,CAClC,IAAM,EAAM,EAAO,MAAQ,EAC3B,EAAO,KAAO,EAAO,GACrB,EAAO,GAAK,GAmBR,SAAS,CAA4G,CAC3H,EACqF,CACrF,IACC,cAAc,SACd,WAAW,EACX,QAAQ,UACL,GAAW,CAAC,EAEV,EAAS,IAAI,EAAwD,QAAQ,EAEnF,EACE,UAAU,cAAc,EACxB,YAAY,CAAQ,EACpB,QAAQ,CAAK,EACb,QAAQ,CAAW,EACnB,SAAS,SAAU,CACnB,KAAM,CAAC,OAAO,CACf,CAAC,EACA,WAAW,CAAC,EAAS,EAAW,IAAQ,CACxC,QAAW,KAAU,EAAQ,OAAQ,CACpC,IAAM,EAAQ,EAAO,WAAW,MAC1B,EAAmB,EAAO,WAGhC,GAAI,EAAM,aAAc,CACvB,EAAM,aAAe,GAErB,SAID,GAAI,EAAM,QAAU,WAAY,SAGhC,GAAI,EAAM,QAAU,UACnB,EAAkB,EAAO,CAAgB,EACzC,EAAM,MAAQ,SAKf,GAAI,CADgB,EAAM,MAAM,EAAM,aACpB,SAElB,EAAM,SAAW,EAGjB,EAAqB,EAAO,EAAkB,EAAO,GAAI,CAAG,GAE7D,EACA,IAAI,EAEN,SAAS,CAAoB,CAC5B,EACA,EACA,EACA,EACO,CAEP,MAAO,GAAM,CACZ,IAAM,EAAc,EAAM,MAAM,EAAM,aACtC,GAAI,CAAC,EAAa,OAGlB,GAAI,EAAY,UAAY,EAAG,CAI9B,GAHA,EAAc,EAAa,EAAkB,EAAU,CAAG,EAC1D,EAAM,QAAU,EAEZ,CAAC,EAAY,EAAO,EAAkB,EAAU,CAAG,EAAG,OAC1D,SAGD,GAAI,EAAM,SAAW,EAAY,SAAU,CAE1C,EAAc,EAAa,EAAkB,EAAU,CAAG,EAC1D,IAAM,EAAW,EAAM,QAAU,EAAY,SAG7C,GAFA,EAAM,QAAU,EAEZ,CAAC,EAAY,EAAO,EAAkB,EAAU,CAAG,EAAG,OAC1D,SAID,IAAM,EAAW,EAAM,EAAM,QAAU,EAAY,SAAU,EAAG,CAAC,EACjE,EAAU,EAAa,EAAU,EAAkB,EAAU,CAAG,EAChE,QAQF,SAAS,CAAW,CACnB,EACA,EACA,EACA,EACU,CACV,IAAM,EAAW,EAAM,YAAc,EAErC,GAAI,EAAW,EAAM,MAAM,OAAQ,CAElC,EAAM,YAAc,EACpB,IAAM,EAAO,EAAM,MAAM,GACzB,GAAI,EACH,QAAW,KAAU,EAAK,QAAS,CAClC,GAAI,EAAO,OAAS,KAAM,SAC1B,IAAM,EAAO,EAAiB,EAAO,WACrC,GAAI,CAAC,GAAQ,OAAO,IAAS,SAAU,SACvC,IAAM,EAAM,EAAU,EAAiC,EAAO,IAAI,EAClE,EAAO,KAAO,GAAO,EAGvB,MAAO,GAIR,OAAO,EAAe,EAAO,EAAU,CAAG,EAG3C,SAAS,CAAc,CACtB,EACA,EACA,EACU,CAGV,GAFA,EAAM,iBAEF,EAAM,OAAS,OAElB,OADA,EAAc,EAAO,EAAU,CAAG,EAC3B,GAIR,GAAI,EAAM,WAAa,GAAK,EAAM,gBAAkB,EAAM,WAEzD,OADA,EAAc,EAAO,EAAU,CAAG,EAC3B,GAIR,GAAI,EAAM,OAAS,OAClB,EAAM,UAAY,EAAM,YAAc,EAAI,GAAK,EAC/C,EAAkB,CAAK,EAQxB,OALA,EAAM,YAAc,EAKb,EAAM,QAAU,EAGxB,SAAS,CAAa,CACrB,EACA,EACA,EACO,CACP,EAAM,MAAQ,WACd,EAAM,aAAe,GAErB,EAAkB,EAAK,EAAU,CAAK,EACtC,EAAI,SAAS,gBAAgB,EAAU,OAAO,EAG/C,SAAS,CAAiB,CACzB,EACA,EACA,EACO,CACP,GAAI,CAAC,EAAM,WAAY,OACvB,IAAM,EAA4B,CACjC,WACA,UAAW,EAAM,MAAM,MACxB,EACA,EAAI,SAAS,QAAQ,EAAM,WAAY,CAAS,EAGjD,OAAO",
9
- "debugId": "2CF1E4A2F7E8D74B64756E2164756E21",
10
- "names": []
11
- }