@yagejs/core 0.5.0 → 0.6.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.
package/dist/index.d.ts CHANGED
@@ -195,6 +195,13 @@ declare class Entity {
195
195
  readonly name: string;
196
196
  /** Tags for group queries. */
197
197
  readonly tags: Set<string>;
198
+ /**
199
+ * Stable identity key, scene-scoped. Set at spawn-time when
200
+ * `options.key` is passed to `scene.spawn` / `entity.spawnChild`;
201
+ * `undefined` otherwise. Used with `scene.findByKey` and as a stable
202
+ * id in persistent stores (e.g. `defineSet<string>("world.opened")`).
203
+ */
204
+ readonly key?: string;
198
205
  private components;
199
206
  private _destroyed;
200
207
  private _scene;
@@ -240,13 +247,13 @@ declare class Entity {
240
247
  * this.spawnChild("hp", EnemyHealthBar);
241
248
  * ```
242
249
  */
243
- spawnChild(name: string): Entity;
244
- spawnChild<E extends Entity>(name: string, Class: new () => E): E;
250
+ spawnChild(name: string, options?: SpawnOptions): Entity;
251
+ spawnChild<E extends Entity>(name: string, Class: new () => E, options?: SpawnOptions): E;
245
252
  spawnChild<E extends Entity, P>(name: string, Class: new () => E & {
246
253
  setup(params: P): void;
247
- }, params: P): E;
248
- spawnChild<P>(name: string, blueprint: Blueprint<P>, params: P): Entity;
249
- spawnChild(name: string, blueprint: Blueprint<void>): Entity;
254
+ }, params: P, options?: SpawnOptions): E;
255
+ spawnChild<P>(name: string, blueprint: Blueprint<P>, params: P, options?: SpawnOptions): Entity;
256
+ spawnChild(name: string, blueprint: Blueprint<void>, options?: SpawnOptions): Entity;
250
257
  /** Remove a named child. Returns the detached entity. */
251
258
  removeChild(name: string): Entity;
252
259
  /** Get a child by name. Throws if not found. */
@@ -290,11 +297,24 @@ declare class Entity {
290
297
  afterRestore?(data: unknown, resolve: SnapshotResolver): void;
291
298
  /** Check if this entity's class implements a given trait. Acts as a type guard. */
292
299
  hasTrait<T>(token: TraitToken<T>): this is this & T;
300
+ /**
301
+ * Return the stable key, or throw if this entity was spawned without one.
302
+ * Use inside component `setup()` when the component depends on identity
303
+ * (e.g. reading from a `defineSet` keyed by entity id).
304
+ */
305
+ requireKey(): string;
293
306
  /**
294
307
  * Internal: set the scene and callbacks. Called by Scene.spawn().
295
308
  * @internal
296
309
  */
297
310
  _setScene(scene: Scene | null, callbacks: EntityCallbacks | null): void;
311
+ /**
312
+ * Internal: assign the stable identity key. Called by `Scene._registerKey`
313
+ * during spawn. Throws if the entity already has a key — keys are
314
+ * immutable for an entity's lifetime.
315
+ * @internal
316
+ */
317
+ _setKey(key: string): void;
298
318
  }
299
319
 
300
320
  /** Filter criteria for entity queries. All fields are AND'd together. */
@@ -311,6 +331,28 @@ interface EntityFilter {
311
331
  /** Apply a filter to an iterable of entities. Skips destroyed entities. */
312
332
  declare function filterEntities(entities: Iterable<Entity>, filter: EntityFilter): Entity[];
313
333
 
334
+ /**
335
+ * Options accepted by the trailing argument of `Scene.spawn` and
336
+ * `Entity.spawnChild`.
337
+ */
338
+ interface SpawnOptions {
339
+ /**
340
+ * Stable per-scene identity key. Looked up via `Scene.findByKey` and used
341
+ * as a stable id in persistent stores (e.g. `defineSet<string>("world.opened")`).
342
+ *
343
+ * Identity is opt-in — most entities (bullets, particles, transient enemies)
344
+ * never need a key. Pass one only for entities whose state should persist
345
+ * across save/load or cross-scene navigation, or that game code looks up
346
+ * by name (chests, doors, named NPCs).
347
+ *
348
+ * Heads-up: don't name a top-level setup-params field `key`. The 2-arg
349
+ * `spawn(Class, X)` form looks at `X`'s shape to disambiguate params from
350
+ * options — an `X` whose only own keys are SpawnOptions fields routes
351
+ * to options. If your params shape clashes (e.g. `setup(p: { key: number })`),
352
+ * use the explicit 3-arg form `spawn(Class, params, options)`.
353
+ */
354
+ key?: string;
355
+ }
314
356
  /**
315
357
  * Scenes own entities and define lifecycle hooks.
316
358
  * Each scene is a self-contained world with its own entity pool.
@@ -339,6 +381,7 @@ declare abstract class Scene {
339
381
  private _entityEventHandlers?;
340
382
  private _entityEventObserver?;
341
383
  private _scopedServices?;
384
+ private _identityIndex?;
342
385
  /** Access the EngineContext. */
343
386
  get context(): EngineContext;
344
387
  /** Whether this scene is effectively paused (manual pause or paused by stack). */
@@ -355,16 +398,51 @@ declare abstract class Scene {
355
398
  * The actual resolution is deferred until first property access.
356
399
  */
357
400
  protected service<T extends object>(key: ServiceKey<T>): T;
358
- /** Spawn a new entity in this scene. */
359
- spawn(name?: string): Entity;
360
- spawn<P>(blueprint: Blueprint<P>, params: P): Entity;
361
- spawn(blueprint: Blueprint<void>): Entity;
401
+ /**
402
+ * Spawn a new entity in this scene.
403
+ *
404
+ * Pass `{ key }` in the trailing options to register a stable per-scene
405
+ * identity key, looked up later via `scene.findByKey`. The key is assigned
406
+ * before `setup()` runs, so `entity.requireKey()` is safe inside it.
407
+ *
408
+ * Runtime routing for the 2-arg class form (`spawn(Class, X)`):
409
+ * - If the class doesn't declare `setup` → `X` is options.
410
+ * - Else if `X`'s own keys are exactly SpawnOptions fields (`{ key }`) →
411
+ * `X` is options. Covers both `setup(params = {})` keyed without
412
+ * params and `setup()` (no real params) keyed.
413
+ * - Else → `X` is params (forwarded to `setup`).
414
+ * The 3-arg form is always unambiguous: `spawn(Class, params, options)`.
415
+ *
416
+ * Don't name a top-level setup-params field `key` — the shape check would
417
+ * misroute it. If you must, use the 3-arg form.
418
+ */
419
+ spawn(name?: string, options?: SpawnOptions): Entity;
420
+ /**
421
+ * Spawn from a blueprint. **Note**: blueprint params must not include a
422
+ * top-level `key: string` field — the runtime can't disambiguate it from
423
+ * `SpawnOptions`. If your params do, use the explicit 3-arg form
424
+ * (`spawn(bp, params, { key })`) so options arrives in the trailing slot.
425
+ */
426
+ spawn<P>(blueprint: Blueprint<P>, params: P, options?: SpawnOptions): Entity;
427
+ spawn(blueprint: Blueprint<void>, options?: SpawnOptions): Entity;
362
428
  /** Spawn an entity subclass with setup params. */
363
429
  spawn<E extends Entity, P>(Class: new () => E & {
364
430
  setup(params: P): void;
365
- }, params: P): E;
431
+ }, params: P, options?: SpawnOptions): E;
366
432
  /** Spawn an entity subclass without setup params. */
367
- spawn<E extends Entity>(Class: new () => E): E;
433
+ spawn<E extends Entity>(Class: new () => E, options?: SpawnOptions): E;
434
+ /**
435
+ * Look up an entity by its stable identity key, scoped to this scene.
436
+ * Returns `undefined` for unknown or already-destroyed entities.
437
+ */
438
+ findByKey<E extends Entity = Entity>(key: string): E | undefined;
439
+ /**
440
+ * Internal: register a key on a freshly spawned entity. Throws on
441
+ * duplicate so callers (Scene.spawn) can abort before adding to
442
+ * `this.entities` or emitting `entity:created`.
443
+ * @internal
444
+ */
445
+ _registerKey(entity: Entity, key: string): void;
368
446
  /**
369
447
  * Add an existing entity to this scene (used by Entity.addChild for auto-scene-membership).
370
448
  * @internal
@@ -450,7 +528,9 @@ declare abstract class Scene {
450
528
  */
451
529
  _flushDestroyQueue(): void;
452
530
  /**
453
- * Destroy all entities — used during scene exit.
531
+ * Destroy all entities — used during scene exit. Clears the identity
532
+ * index in bulk; per-entity key removal in `_flushDestroyQueue` is the
533
+ * in-game path.
454
534
  * @internal
455
535
  */
456
536
  _destroyAllEntities(): void;
@@ -2364,6 +2444,178 @@ declare function createMockEntity(name?: string): {
2364
2444
  /** Advance the game loop by N frames (manual tick). */
2365
2445
  declare function advanceFrames(engine: Engine, n: number, dtMs?: number): void;
2366
2446
 
2447
+ /**
2448
+ * Atom — minimal reactive cell.
2449
+ *
2450
+ * Signal-shaped: `get`, `set`, `subscribe`. Identity-based change detection
2451
+ * (`Object.is`); subscribers are notified only when the value actually changes.
2452
+ */
2453
+ interface Atom<T> {
2454
+ get(): T;
2455
+ set(next: T): void;
2456
+ subscribe(listener: (value: T) => void): () => void;
2457
+ }
2458
+ declare function createAtom<T>(initial: T): Atom<T>;
2459
+
2460
+ /**
2461
+ * Store — object-shaped reactive store with shallow merge.
2462
+ *
2463
+ * `get()` returns a `Readonly<T>` whose reference is stable between sets.
2464
+ * `set(partial)` merges keys via spread and notifies only when at least one key
2465
+ * changed (Object.is per key). Mutation through the snapshot bypasses
2466
+ * subscribers — TypeScript's `Readonly<T>` enforces the contract; do not reach
2467
+ * into nested objects to mutate them, use `set` instead.
2468
+ */
2469
+ interface Store<T extends object> {
2470
+ get(): Readonly<T>;
2471
+ set(partial: Partial<T>): void;
2472
+ subscribe(listener: () => void): () => void;
2473
+ }
2474
+ declare function createStore<T extends object>(initial: T): Store<T>;
2475
+
2476
+ /**
2477
+ * A codec converts a value between its in-memory representation and a JSON-safe
2478
+ * representation that adapters can stringify. Codecs are pure functions; they
2479
+ * don't read or write storage.
2480
+ */
2481
+ interface Codec<T> {
2482
+ encode(value: T): unknown;
2483
+ decode(raw: unknown): T;
2484
+ }
2485
+ /** Identity codec — pass-through for plain JSON-serializable values. */
2486
+ declare function jsonCodec<T>(): Codec<T>;
2487
+ /** Set ↔ array. */
2488
+ declare function setCodec<K>(): Codec<Set<K>>;
2489
+ /** Map ↔ entries array. */
2490
+ declare function mapCodec<K, V>(): Codec<Map<K, V>>;
2491
+ /** Date ↔ ISO string. */
2492
+ declare function dateCodec(): Codec<Date>;
2493
+
2494
+ /**
2495
+ * Common persistence shape implemented by every defineX output. The save layer
2496
+ * accepts anything matching this structural type — stores don't depend on the
2497
+ * save layer.
2498
+ */
2499
+ interface PersistentLike {
2500
+ readonly id: string;
2501
+ readonly version: number;
2502
+ serialize(): {
2503
+ version: number;
2504
+ data: unknown;
2505
+ };
2506
+ hydrate(payload: {
2507
+ version: number;
2508
+ data: unknown;
2509
+ }): void;
2510
+ subscribe(listener: () => void): () => void;
2511
+ }
2512
+ /**
2513
+ * Reset every persistent store created by defineStore / defineSet / defineMap /
2514
+ * defineCounter back to its defaults. Test-only.
2515
+ *
2516
+ * @internal
2517
+ */
2518
+ declare function _resetAllStoresForTesting(): void;
2519
+ /**
2520
+ * Drop every persistent store from the internal registry. Use only when you
2521
+ * intend to redefine stores with the same ids (e.g. between test files that
2522
+ * share a module namespace via Vitest's module cache).
2523
+ *
2524
+ * @internal
2525
+ */
2526
+ declare function _clearStoreRegistryForTesting(): void;
2527
+ interface DefineStoreOptions<T> {
2528
+ /** Schema version. Defaults to 1. Bump when shape changes; provide `migrate`. */
2529
+ version?: number;
2530
+ /** Factory producing the default value. Called on creation and on `reset()`. */
2531
+ defaults: () => T;
2532
+ /** Codec for `T`. Defaults to identity (jsonCodec). */
2533
+ codec?: Codec<T>;
2534
+ /**
2535
+ * Migrate previously-stored data to the current version. Receives the raw
2536
+ * decoded payload and the version it was written at. Called when
2537
+ * `payload.version < version` during `hydrate`.
2538
+ */
2539
+ migrate?: (old: unknown, fromVersion: number) => T;
2540
+ }
2541
+ interface PersistentStore<T extends object> extends Store<T>, PersistentLike {
2542
+ reset(): void;
2543
+ }
2544
+ /** Thrown by `hydrate` when stored data is from a newer version than this build. */
2545
+ declare class StoreVersionTooNewError extends Error {
2546
+ readonly storeId: string;
2547
+ readonly storedVersion: number;
2548
+ readonly currentVersion: number;
2549
+ constructor(storeId: string, storedVersion: number, currentVersion: number);
2550
+ }
2551
+ /** Thrown by `hydrate` when stored data is older than the current version and no `migrate` is configured. */
2552
+ declare class StoreMigrationMissingError extends Error {
2553
+ readonly storeId: string;
2554
+ readonly storedVersion: number;
2555
+ readonly currentVersion: number;
2556
+ constructor(storeId: string, storedVersion: number, currentVersion: number);
2557
+ }
2558
+ declare function defineStore<T extends object>(id: string, opts: DefineStoreOptions<T>): PersistentStore<T>;
2559
+ interface PersistentSet<K> extends PersistentLike {
2560
+ has(key: K): boolean;
2561
+ add(key: K): void;
2562
+ remove(key: K): void;
2563
+ clear(): void;
2564
+ size(): number;
2565
+ values(): IterableIterator<K>;
2566
+ reset(): void;
2567
+ }
2568
+ interface DefineSetOptions<K> {
2569
+ version?: number;
2570
+ defaults?: () => Iterable<K>;
2571
+ /**
2572
+ * Migrate older stored data to the current version. Receives the raw decoded
2573
+ * payload (a `K[]` from setCodec) and the version it was written at. Required
2574
+ * when bumping `version`; otherwise older payloads throw at hydrate.
2575
+ */
2576
+ migrate?: (old: unknown, fromVersion: number) => Set<K>;
2577
+ }
2578
+ declare function defineSet<K>(id: string, opts?: DefineSetOptions<K>): PersistentSet<K>;
2579
+ interface PersistentMap<K, V> extends PersistentLike {
2580
+ has(key: K): boolean;
2581
+ get(key: K): V | undefined;
2582
+ set(key: K, value: V): void;
2583
+ remove(key: K): void;
2584
+ clear(): void;
2585
+ size(): number;
2586
+ entries(): IterableIterator<[K, V]>;
2587
+ reset(): void;
2588
+ }
2589
+ interface DefineMapOptions<K, V> {
2590
+ version?: number;
2591
+ defaults?: () => Iterable<[K, V]>;
2592
+ /**
2593
+ * Migrate older stored data to the current version. Receives the raw decoded
2594
+ * payload (a `[K, V][]` from mapCodec) and the version it was written at.
2595
+ * Required when bumping `version`; otherwise older payloads throw at hydrate.
2596
+ */
2597
+ migrate?: (old: unknown, fromVersion: number) => Map<K, V>;
2598
+ }
2599
+ declare function defineMap<K, V>(id: string, opts?: DefineMapOptions<K, V>): PersistentMap<K, V>;
2600
+ interface PersistentCounter extends PersistentLike {
2601
+ value(): number;
2602
+ set(n: number): void;
2603
+ increment(by?: number): void;
2604
+ decrement(by?: number): void;
2605
+ reset(): void;
2606
+ }
2607
+ interface DefineCounterOptions {
2608
+ version?: number;
2609
+ defaults?: () => number;
2610
+ /**
2611
+ * Migrate older stored data to the current version. Receives the raw decoded
2612
+ * payload (a number) and the version it was written at. Required when
2613
+ * bumping `version`; otherwise older payloads throw at hydrate.
2614
+ */
2615
+ migrate?: (old: unknown, fromVersion: number) => number;
2616
+ }
2617
+ declare function defineCounter(id: string, opts?: DefineCounterOptions): PersistentCounter;
2618
+
2367
2619
  declare const VERSION = "0.0.0";
2368
2620
 
2369
- export { AssetHandle, type AssetLoader, AssetManager, AssetManagerKey, type Blueprint, type CameraSnapshot, Component, type ComponentClass, ComponentFixedUpdateSystem, type ComponentStateSnapshot, ComponentUpdateSystem, type EasingFunction, Engine, type EngineConfig, EngineContext, type EngineEvents, EngineKey, type EngineSnapshot, Entity, type EntityCallbacks, type EntityFilter, type EntitySnapshot, ErrorBoundary, ErrorBoundaryKey, type ErrorSnapshot, EventBus, EventBusKey, type EventLogEntry, type EventMap, EventToken, GameLoop, type GameLoopCallbacks, type GameLoopConfig, GameLoopKey, type InputStateSnapshot, Inspector, InspectorKey, type InspectorTimeController, type Interpolatable, type Keyframe, type KeyframeAnimationDef, KeyframeAnimator, type KeyframeTrackOptions, LoadingScene, type LogEntry, LogLevel, Logger, type LoggerConfig, LoggerKey, MathUtils, Phase, type PhysicsSnapshot, type Plugin, type PointerSnapshot, Process, ProcessComponent, type ProcessOptions, ProcessSlot, type ProcessSlotConfig, ProcessSystem, ProcessSystemKey, QueryCache, QueryCacheKey, QueryResult, RandomKey, type RandomService, type RendererAdapter, RendererAdapterKey, SERIALIZABLE_KEY, Scene, SceneHookRegistry, SceneHookRegistryKey, type SceneHooks, SceneManager, SceneManagerKey, type SceneSnapshot, type SceneTransition, type SceneTransitionContext, type SceneTransitionKind, type SceneTransitionOptions, type ScopedProcessQueue, Sequence, SerializableRegistry, ServiceKey, type ServiceKeyOptions, type ServiceScope, type SmoothDampResult, type SnapshotResolver, System, SystemScheduler, SystemSchedulerKey, type SystemSnapshot, TimerEntity, TraitToken, Transform, type TransformData, Tween, type UINodeSnapshot, type UITreeSnapshot, VERSION, Vec2, type Vec2Like, type WorldEntitySnapshot, type WorldSceneSnapshot, _resetEntityIdCounter, advanceFrames, createDefaultRandomSeed, createKeyframeTrack, createMockEntity, createMockScene, createRandomService, createTestEngine, defineBlueprint, defineEvent, defineTrait, easeInOutQuad, easeInQuad, easeLinear, easeOutBounce, easeOutQuad, filterEntities, getSerializableType, globalRandom, interpolate, isPointerConsumeContainer, isSerializable, makeEntityScopedQueue, makeGlobalScopedQueue, makeSceneScopedQueue, markPointerConsumeContainer, normalizeSeed, resolveTransition, serializable, trait, unmarkPointerConsumeContainer };
2621
+ export { AssetHandle, type AssetLoader, AssetManager, AssetManagerKey, type Atom, type Blueprint, type CameraSnapshot, type Codec, Component, type ComponentClass, ComponentFixedUpdateSystem, type ComponentStateSnapshot, ComponentUpdateSystem, type DefineCounterOptions, type DefineMapOptions, type DefineSetOptions, type DefineStoreOptions, type EasingFunction, Engine, type EngineConfig, EngineContext, type EngineEvents, EngineKey, type EngineSnapshot, Entity, type EntityCallbacks, type EntityFilter, type EntitySnapshot, ErrorBoundary, ErrorBoundaryKey, type ErrorSnapshot, EventBus, EventBusKey, type EventLogEntry, type EventMap, EventToken, GameLoop, type GameLoopCallbacks, type GameLoopConfig, GameLoopKey, type InputStateSnapshot, Inspector, InspectorKey, type InspectorTimeController, type Interpolatable, type Keyframe, type KeyframeAnimationDef, KeyframeAnimator, type KeyframeTrackOptions, LoadingScene, type LogEntry, LogLevel, Logger, type LoggerConfig, LoggerKey, MathUtils, type PersistentCounter, type PersistentLike, type PersistentMap, type PersistentSet, type PersistentStore, Phase, type PhysicsSnapshot, type Plugin, type PointerSnapshot, Process, ProcessComponent, type ProcessOptions, ProcessSlot, type ProcessSlotConfig, ProcessSystem, ProcessSystemKey, QueryCache, QueryCacheKey, QueryResult, RandomKey, type RandomService, type RendererAdapter, RendererAdapterKey, SERIALIZABLE_KEY, Scene, SceneHookRegistry, SceneHookRegistryKey, type SceneHooks, SceneManager, SceneManagerKey, type SceneSnapshot, type SceneTransition, type SceneTransitionContext, type SceneTransitionKind, type SceneTransitionOptions, type ScopedProcessQueue, Sequence, SerializableRegistry, ServiceKey, type ServiceKeyOptions, type ServiceScope, type SmoothDampResult, type SnapshotResolver, type SpawnOptions, type Store, StoreMigrationMissingError, StoreVersionTooNewError, System, SystemScheduler, SystemSchedulerKey, type SystemSnapshot, TimerEntity, TraitToken, Transform, type TransformData, Tween, type UINodeSnapshot, type UITreeSnapshot, VERSION, Vec2, type Vec2Like, type WorldEntitySnapshot, type WorldSceneSnapshot, _clearStoreRegistryForTesting, _resetAllStoresForTesting, _resetEntityIdCounter, advanceFrames, createAtom, createDefaultRandomSeed, createKeyframeTrack, createMockEntity, createMockScene, createRandomService, createStore, createTestEngine, dateCodec, defineBlueprint, defineCounter, defineEvent, defineMap, defineSet, defineStore, defineTrait, easeInOutQuad, easeInQuad, easeLinear, easeOutBounce, easeOutQuad, filterEntities, getSerializableType, globalRandom, interpolate, isPointerConsumeContainer, isSerializable, jsonCodec, makeEntityScopedQueue, makeGlobalScopedQueue, makeSceneScopedQueue, mapCodec, markPointerConsumeContainer, normalizeSeed, resolveTransition, serializable, setCodec, trait, unmarkPointerConsumeContainer };