@adobe/ecs 0.2.0 → 0.2.1

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 (66) hide show
  1. package/dist/implementation/world/create.js.map +1 -1
  2. package/dist/implementation/world/finalizers.js.map +1 -1
  3. package/dist/index.d.ts +20 -0
  4. package/dist/index.js.map +1 -1
  5. package/dist/interface/component/component.d.ts +7 -1
  6. package/dist/interface/component/component.js +0 -1
  7. package/dist/interface/component/component.js.map +1 -1
  8. package/dist/interface/component/index.d.ts +1 -1
  9. package/dist/interface/component/value.d.ts +5 -5
  10. package/dist/interface/component/values.d.ts +2 -2
  11. package/dist/interface/finalizer/body.d.ts +5 -5
  12. package/dist/interface/resource/condition.d.ts +3 -3
  13. package/dist/interface/resource/index.d.ts +0 -1
  14. package/dist/interface/resource/resource.d.ts +4 -1
  15. package/dist/interface/resource/resource.js +0 -1
  16. package/dist/interface/resource/resource.js.map +1 -1
  17. package/dist/interface/resource/value.d.ts +5 -5
  18. package/dist/interface/resources/cadence.d.ts +1 -1
  19. package/dist/interface/system/access.d.ts +1 -1
  20. package/dist/interface/system/body.d.ts +2 -2
  21. package/dist/interface/system/commands.d.ts +4 -4
  22. package/dist/interface/system/struct.d.ts +2 -2
  23. package/dist/interface/system/values-of.d.ts +4 -4
  24. package/dist/interface/system/values.d.ts +5 -5
  25. package/dist/interface/trait/types.d.ts +4 -4
  26. package/dist/interface/world/create.d.ts +3 -7
  27. package/dist/interface/world/declarations.d.ts +2 -2
  28. package/dist/interface/world/join.d.ts +2 -10
  29. package/package.json +1 -1
  30. package/src/exports.test.ts +2 -2
  31. package/src/guide/03-systems.out +1 -1
  32. package/src/guide/11-traits.type-test.ts +1 -1
  33. package/src/guide/18-identity.out +1 -1
  34. package/src/implementation/world/create.ts +3 -3
  35. package/src/implementation/world/finalizers.ts +3 -3
  36. package/src/implementation/world/pool-size.cases.ts +3 -1
  37. package/src/implementation/world/reads.test-helper.ts +6 -6
  38. package/src/index.ts +24 -1
  39. package/src/interface/component/component.ts +7 -1
  40. package/src/interface/component/index.ts +1 -1
  41. package/src/interface/component/value.ts +5 -5
  42. package/src/interface/component/values.ts +2 -2
  43. package/src/interface/finalizer/body.ts +5 -5
  44. package/src/interface/index.type-test.ts +1 -1
  45. package/src/interface/resource/condition.ts +3 -3
  46. package/src/interface/resource/index.ts +0 -1
  47. package/src/interface/resource/resource.ts +4 -1
  48. package/src/interface/resource/value.ts +5 -5
  49. package/src/interface/resources/cadence.ts +1 -1
  50. package/src/interface/system/access.ts +1 -1
  51. package/src/interface/system/body.ts +2 -2
  52. package/src/interface/system/commands.ts +4 -4
  53. package/src/interface/system/struct.ts +2 -2
  54. package/src/interface/system/values-of.ts +4 -4
  55. package/src/interface/system/values.ts +5 -5
  56. package/src/interface/thread/thread.type-test.ts +1 -1
  57. package/src/interface/trait/trait.type-test.ts +4 -4
  58. package/src/interface/trait/types.ts +4 -4
  59. package/src/interface/world/create.ts +3 -7
  60. package/src/interface/world/declarations.ts +2 -2
  61. package/src/interface/world/join.ts +2 -10
  62. package/src/interface/world/loop.type-test.ts +6 -6
  63. package/src/observe/members.ts +3 -3
  64. package/src/observe/query.ts +1 -1
  65. package/src/observe/select.ts +1 -1
  66. package/src/observe/values.ts +2 -2
@@ -5,10 +5,10 @@ import type { Resource } from '../resource/resource.js';
5
5
  import type { Comparison } from '../world/filter.js';
6
6
  import type { Listed, Property } from './property.js';
7
7
  export type ComponentValues = {
8
- readonly [K in Component.Key]: Component.Value<K>;
8
+ readonly [K in Component.Key]: Component<K>;
9
9
  };
10
10
  export type ResourceValues = {
11
- readonly [K in Resource.Key]: Resource.Value<K>;
11
+ readonly [K in Resource.Key]: Resource<K>;
12
12
  };
13
13
  /** `Read` values are read-only, `Write` values writable. */
14
14
  export type Values<Read, Write, Keys extends PropertyKey, Types> = {
@@ -24,7 +24,7 @@ export type KeyOf<D> = D extends {
24
24
  export type HeldBy<D> = KeyOf<D>[number] | Listed<Property<D, "components">, "with">;
25
25
  /** The key's values, in key order. */
26
26
  type KeyValues<K extends readonly Component.Key[]> = {
27
- readonly [I in keyof K]: Component.Value<K[I] & Component.Key>;
27
+ readonly [I in keyof K]: Component<K[I] & Component.Key>;
28
28
  };
29
29
  /** A prefix of the key, possibly empty, whose last part may be a comparison. */
30
30
  type Prefix<V extends readonly unknown[]> = V extends readonly [infer First, ...infer Rest] ? readonly [] | readonly [First | Comparison<First>] | readonly [First, ...Prefix<Rest>] : readonly [];
@@ -32,7 +32,7 @@ type Prefix<V extends readonly unknown[]> = V extends readonly [infer First, ...
32
32
  type IndexRow<D> = {
33
33
  readonly entity: Entity;
34
34
  } & {
35
- readonly [K in HeldBy<D> & Component.Key]: Component.Value<K>;
35
+ readonly [K in HeldBy<D> & Component.Key]: Component<K>;
36
36
  };
37
37
  /**
38
38
  * The rows a lookup found, in the index's order: by entity for a hash index, by key and then entity for a sorted one.
@@ -65,7 +65,7 @@ type IndexLookup<D> = D extends {
65
65
  type EntityRow<L> = {
66
66
  readonly [K in (Listed<L, "with"> & Component.Key) | (Exclude<Listed<L, "optional">, Listed<L, "with">> & Component.Key) | (L extends {
67
67
  readonly changed: readonly Component.Key[];
68
- } ? "changed" : never)]: K extends Component.Key ? K extends Listed<L, "with"> ? Component.Value<K> : Component.Value<K> | undefined : boolean;
68
+ } ? "changed" : never)]: K extends Component.Key ? K extends Listed<L, "with"> ? Component<K> : Component<K> | undefined : boolean;
69
69
  };
70
70
  export type LookupFunction<L> = L extends {
71
71
  readonly index: Index<infer D>;
@@ -7,7 +7,7 @@ import type { Reduction } from '../resource/reduction.js';
7
7
  import type { IsComparable, IsString, IsStruct } from '../schema/is-struct.js';
8
8
  import type { Thread } from '../thread/thread.js';
9
9
  /** The value type of the component or resource `N`. */
10
- type ValueOf<N> = N extends Component.Key ? Component.Value<N> : N extends Resource.Key ? Resource.Value<N> : never;
10
+ type ValueOf<N> = N extends Component.Key ? Component<N> : N extends Resource.Key ? Resource<N> : never;
11
11
  /**
12
12
  * Whether each component's and resource's value is a struct, `"string"` for a string, computed once so a check is a
13
13
  * lookup. Its keys are remapped (`as K`) so a generic key's lookup stays one: resolved as `IsStruct` of a generic value,
@@ -44,12 +44,12 @@ export interface Traits<N extends Component.Key | Resource.Key> {
44
44
  readonly derived: true;
45
45
  } | {
46
46
  readonly shared: true;
47
- } ? never : [Component.Value<N>] extends [Entity] ? "owner" | "weak" : never : never;
47
+ } ? never : [Component<N>] extends [Entity] ? "owner" | "weak" : never : never;
48
48
  /**
49
49
  * On a resource, the operator systems reduce into it with: each run that a contributor runs in starts it at the
50
50
  * operator's identity and leaves it holding the fold of every contribution. Booleans fold bitwise.
51
51
  */
52
- readonly reduce: N extends Resource.Key ? [Resource.Value<N>] extends [boolean] ? "&" | "|" | "^" : Structs[N] extends true ? Reduction : never : never;
52
+ readonly reduce: N extends Resource.Key ? [Resource<N>] extends [boolean] ? "&" | "|" | "^" : Structs[N] extends true ? Reduction : never : never;
53
53
  /**
54
54
  * The logical thread an object lives on, `"world"` without it. Not on a struct or a string, which live in shared
55
55
  * memory. A DOM `Node` lives on `"main"`, and says so.
@@ -69,6 +69,6 @@ export interface Traits<N extends Component.Key | Resource.Key> {
69
69
  readonly derived: true;
70
70
  } | {
71
71
  readonly reference: unknown;
72
- } ? never : IsComparable<Component.Value<N>> extends true ? true : never : never;
72
+ } ? never : IsComparable<Component<N>> extends true ? true : never : never;
73
73
  }
74
74
  export {};
@@ -4,15 +4,11 @@ import type { InvalidDeclarations } from './declarations.js';
4
4
  import type { World } from './world.js';
5
5
  /** Values resources start at in place of their defaults: any but those only the world writes. */
6
6
  export type InitialResources = {
7
- readonly [K in Exclude<Resource.Key, WorldWritten>]?: Resource.Value<K>;
7
+ readonly [K in Exclude<Resource.Key, WorldWritten>]?: Resource<K>;
8
8
  };
9
9
  /**
10
- * Creates a world on this thread, synchronously, with resources starting at the given values in place of their
11
- * defaults. This thread is its `"world"` thread for the world's life: it runs the sync points, the ticks and the GPU
12
- * device, and fills every logical thread no joined thread claims. Keys are the resources' symbols, built-in ones
13
- * included (`{ [workers]: 0 }`); string keys are never a resource's, so they're ignored. Throws for a symbol that isn't
14
- * a resource, or is one only the world writes. While a declaration's trait or default doesn't hold, each call fails to
15
- * compile, once, naming each such key, its members and what's wrong with them.
10
+ * The type of `World.create`.
11
+ * @inline
16
12
  */
17
13
  export type Create = (this: [InvalidDeclarations] extends [never] ? unknown : {
18
14
  readonly "declarations don't hold": {
@@ -30,8 +30,8 @@ type Reported<K, Members extends PropertyKey> = [Members] extends [never] ? neve
30
30
  * `World.create` reports these.
31
31
  */
32
32
  export type InvalidDeclarations = {
33
- readonly [K in Component.Key]: Reported<K, Invalid<K, Components[K], Component.Value<K>>>;
33
+ readonly [K in Component.Key]: Reported<K, Invalid<K, Components[K], Component<K>>>;
34
34
  }[Component.Key] | {
35
- readonly [K in Resource.Key]: Reported<K, Invalid<K, Resources[K], Resource.Value<K>>>;
35
+ readonly [K in Resource.Key]: Reported<K, Invalid<K, Resources[K], Resource<K>>>;
36
36
  }[Resource.Key];
37
37
  export {};
@@ -1,16 +1,8 @@
1
1
  import type { Thread } from '../thread/thread.js';
2
2
  import type { World } from './world.js';
3
3
  /**
4
- * Joins, from another thread, a world that `share` let join through the other end of `port`, claiming the logical
5
- * `threads` this thread fills: their objects live here, and the systems and finalizers reading them run here. Resolves,
6
- * once the world accepted the claim, to the same world: its `run` runs systems there, and its `add` and `remove` take
7
- * observer systems, which run here after the world's sync points. Add them before awaiting anything else, so the
8
- * world's `share` resolves with them in place. Its `dispose` disconnects this thread. Rejects with an `Error` saying
9
- * why it didn't join: the world refused the claim, was disposed, or its thread ended first, as the platform reports it:
10
- * the world's `Worker` failing to start or throwing first, a port closed, as in node, or, in a browser, the world's
11
- * thread ending once it was shared through a port this thread listened on. A browser world's worker that the page ended
12
- * before this thread heard from it isn't reported, so the join stays pending. Rejects too where memory can't be shared,
13
- * as without cross-origin isolation.
4
+ * The type of `World.join`.
5
+ * @inline
14
6
  */
15
7
  export type Join = (port: Parameters<World["share"]>[0], options?: {
16
8
  readonly threads?: readonly Exclude<Thread, "world">[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adobe/ecs",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Adobe ECS",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -152,7 +152,7 @@ const customTrait = creating(`
152
152
  export const instanced = Symbol();
153
153
  export const foo = Symbol();
154
154
  declare module "@adobe/ecs" {
155
- interface Traits<N> { readonly [instanced]: N extends Component.Key ? ([Component.Value<N>] extends [readonly number[]] ? true : never) : never }
155
+ interface Traits<N> { readonly [instanced]: N extends Component.Key ? ([Component<N>] extends [readonly number[]] ? true : never) : never }
156
156
  interface Components { [foo]: { type: string; [instanced]: true } }
157
157
  }`);
158
158
 
@@ -182,7 +182,7 @@ const severalTraits = creating(`
182
182
  export const instanced = Symbol();
183
183
  export const name = Symbol();
184
184
  declare module "@adobe/ecs" {
185
- interface Traits<N> { readonly [instanced]: N extends Component.Key ? ([Component.Value<N>] extends [readonly number[]] ? true : never) : never }
185
+ interface Traits<N> { readonly [instanced]: N extends Component.Key ? ([Component<N>] extends [readonly number[]] ? true : never) : never }
186
186
  interface Components { [name]: { type: string; [instanced]: true; thread: "main" } }
187
187
  }`);
188
188
 
@@ -1,5 +1,5 @@
1
1
  at [ 0, 1, 5 ]
2
2
  moving [ 'at 2', 'at 2', 'still' ]
3
3
  frames 1
4
- Error: System "fail" (@adobe/ecs@0.2.0/src/guide/03-systems.ts#fail) failed: broken
4
+ Error: System "fail" (@adobe/ecs@0.2.1/src/guide/03-systems.ts#fail) failed: broken
5
5
  frames 2
@@ -5,7 +5,7 @@ import { Component } from "../index.js";
5
5
  export const instanced = Symbol();
6
6
  declare module "@adobe/ecs" {
7
7
  interface Traits<N> {
8
- readonly [instanced]: N extends Component.Key ? ([Component.Value<N>] extends [readonly number[]] ? true : never) : never;
8
+ readonly [instanced]: N extends Component.Key ? ([Component<N>] extends [readonly number[]] ? true : never) : never;
9
9
  }
10
10
  }
11
11
 
@@ -1 +1 @@
1
- Error: System "explode" (@adobe/ecs@0.2.0/src/guide/18-identity.ts#explode) failed: boom
1
+ Error: System "explode" (@adobe/ecs@0.2.1/src/guide/18-identity.ts#explode) failed: boom
@@ -46,7 +46,7 @@ import { createState, defaultOf, idOf, reside, startTicking, type Key } from './
46
46
  import { indexOf } from './entities.js';
47
47
  import { touchedHeld } from './up.js';
48
48
  import { isHeld } from './transfers.js';
49
- import type { ReadonlyValue as ReadonlyResourceValue } from '../../interface/resource/value.js';
49
+ import type { ReadonlyType as ReadonlyResourceType } from '../../interface/resource/value.js';
50
50
  import { labelOf, whyUnregistered } from '../../interface/registry/index.js';
51
51
 
52
52
  type Queue = <T>(task: () => T | Promise<T>) => Promise<T>;
@@ -425,12 +425,12 @@ export const createWorld = (resources: InitialResources = {}, options: WorldOpti
425
425
  const deviceId = idOf(state, gpuDevice);
426
426
  const deviceDefault = defaultOf(gpuDevice);
427
427
  /** The `gpuDevice` resource's value, read each run, so without allocating. */
428
- const readDevice = (): ReadonlyResourceValue<typeof gpuDevice> => {
428
+ const readDevice = (): ReadonlyResourceType<typeof gpuDevice> => {
429
429
  const index = state.resourceEntity < 0 ? -1 : indexOf(state.resourceEntity);
430
430
  const storage = index < 0 ? undefined : state.archetypes[state.entities.archetype[index]]?.storageById[deviceId];
431
431
  const value = storage !== undefined ? readCell(storage, state.entities.row[index]) : state.initial.has(gpuDevice) ? state.initial.get(gpuDevice) : deviceDefault;
432
432
  // The resource's value matches its registered schema, `GPUDevice | null`.
433
- return value as ReadonlyResourceValue<typeof gpuDevice>;
433
+ return value as ReadonlyResourceType<typeof gpuDevice>;
434
434
  };
435
435
 
436
436
  /**
@@ -12,7 +12,7 @@ import { asEntity, indexOf, locate } from './entities.js';
12
12
  import { createContext, rowBinder, type Context } from './rows.js';
13
13
  import { idOf, type Key, type State } from './state.js';
14
14
  import type { Queued } from './synchronize.js';
15
- import type { Essential, ReadonlyValue } from '../../interface/component/value.js';
15
+ import type { Essential, ReadonlyType } from '../../interface/component/value.js';
16
16
  import type { Leaf } from '../../interface/finalizer/created.js';
17
17
  import { newestKeyOf, threadOf } from '../../interface/registry/index.js';
18
18
  import type { Layout } from '../../interface/registry/layout.js';
@@ -369,8 +369,8 @@ const changesOf = (state: State, runner: FinalizerRunner, queued: readonly Queue
369
369
  return {
370
370
  groups: [...groups.values()],
371
371
  // Stored and commanded values match each component's registered schema, so they have its type.
372
- previous: <K extends Essential>(entity: Entity, key: K) => (bornHere(entity) ? null : live(entity, idOf(state, key))) as ReadonlyValue<K> | null,
373
- next: <K extends Essential>(entity: Entity, key: K) => next(entity, idOf(state, key)) as ReadonlyValue<K> | null,
372
+ previous: <K extends Essential>(entity: Entity, key: K) => (bornHere(entity) ? null : live(entity, idOf(state, key))) as ReadonlyType<K> | null,
373
+ next: <K extends Essential>(entity: Entity, key: K) => next(entity, idOf(state, key)) as ReadonlyType<K> | null,
374
374
  };
375
375
  };
376
376
 
@@ -12,7 +12,9 @@ export const poolSizeCases = (load: () => Promise<typeof Fixture>) => describe("
12
12
  });
13
13
  it("honors a number, and null again gives the platform's choice", async () => {
14
14
  const fixture = await load();
15
- expect((await fixture.resized(1, [null])).started).toEqual([1, 1 + fixture.platformCount()]);
15
+ // The platform's choice starts a pool of its own unless it's the size the pool already is.
16
+ const platform = fixture.platformCount();
17
+ expect((await fixture.resized(1, [null])).started).toEqual([1, platform === 1 ? 1 : 1 + platform]);
16
18
  });
17
19
  it("starts the pool only once a kernel has rows enough for workers", async () => {
18
20
  const fixture = await load();
@@ -1,17 +1,17 @@
1
1
  // © 2026 Adobe. MIT License. See /LICENSE for details.
2
2
  import type { Component } from '../../interface/component/component.js';
3
- import type { ReadonlyValue } from '../../interface/component/value.js';
3
+ import type { ReadonlyType } from '../../interface/component/value.js';
4
4
  import type { Entity } from '../../interface/entity/entity.js';
5
5
  import type { Resource } from '../../interface/resource/resource.js';
6
- import type { ReadonlyValue as ReadonlyResourceValue } from '../../interface/resource/value.js';
6
+ import type { ReadonlyType as ReadonlyResourceType } from '../../interface/resource/value.js';
7
7
  import type { World } from '../../interface/world/world.js';
8
8
  import { Observe } from '../../observe/index.js';
9
9
 
10
10
  // Tests' reads: each is the first value of an observer factory of `src/observe`.
11
11
 
12
12
  /** One component of an entity, or `null` if it's dead or lacks it. */
13
- export const read = async <C extends Component.Key>(world: World, entity: Entity, component: C): Promise<ReadonlyValue<C> | null> => {
14
- const values: { readonly [K in C]: ReadonlyValue<K> } | null = await Observe.toPromise(Observe.entity(world, entity, { required: [component] }));
13
+ export const read = async <C extends Component.Key>(world: World, entity: Entity, component: C): Promise<ReadonlyType<C> | null> => {
14
+ const values: { readonly [K in C]: ReadonlyType<K> } | null = await Observe.toPromise(Observe.entity(world, entity, { required: [component] }));
15
15
  return values === null ? null : values[component];
16
16
  };
17
17
 
@@ -19,12 +19,12 @@ export const read = async <C extends Component.Key>(world: World, entity: Entity
19
19
  export const readEntity = <const C extends readonly Component.Key[]>(world: World, entity: Entity, components: C) =>
20
20
  Observe.toPromise(Observe.entity(world, entity, { required: components }));
21
21
 
22
- export const readResource = <R extends Resource.Key>(world: World, name: R): Promise<ReadonlyResourceValue<R>> => Observe.toPromise(Observe.resource(world, name));
22
+ export const readResource = <R extends Resource.Key>(world: World, name: R): Promise<ReadonlyResourceType<R>> => Observe.toPromise(Observe.resource(world, name));
23
23
 
24
24
  export const select = <const R extends readonly Component.Key[] = []>(world: World, query: Observe.Query<R>): Promise<readonly Entity[]> => Observe.toPromise(Observe.select(world, query));
25
25
 
26
26
  export const count = <const R extends readonly Component.Key[] = []>(world: World, query: Omit<Observe.Query<R>, "order">): Promise<number> => Observe.toPromise(Observe.count(world, query));
27
27
 
28
28
  /** One component of each of these entities, `null` for one that's dead or lacks it. */
29
- export const readEach = <C extends Component.Key>(world: World, entities: readonly Entity[], component: C): Promise<(ReadonlyValue<C> | null)[]> =>
29
+ export const readEach = <C extends Component.Key>(world: World, entities: readonly Entity[], component: C): Promise<(ReadonlyType<C> | null)[]> =>
30
30
  Promise.all(entities.map(entity => read(world, entity, component)));
package/src/index.ts CHANGED
@@ -18,4 +18,27 @@ export type World = Created;
18
18
  * values in place of their defaults; and how another thread joins one that shared itself with it,
19
19
  * `World.join(port, { threads })`.
20
20
  */
21
- export const World: { readonly create: Create; readonly join: Join } = { create, join };
21
+ export const World: {
22
+ /**
23
+ * Creates a world on this thread, synchronously, with resources starting at the given values in place of their
24
+ * defaults. This thread is its `"world"` thread for the world's life: it runs the sync points, the ticks and the GPU
25
+ * device, and fills every logical thread no joined thread claims. Keys are the resources' symbols, built-in ones
26
+ * included (`{ [workers]: 0 }`); string keys are never a resource's, so they're ignored. Throws for a symbol that isn't
27
+ * a resource, or is one only the world writes. While a declaration's trait or default doesn't hold, each call fails to
28
+ * compile, once, naming each such key, its members and what's wrong with them.
29
+ */
30
+ readonly create: Create;
31
+ /**
32
+ * Joins, from another thread, a world that `share` let join through the other end of `port`, claiming the logical
33
+ * `threads` this thread fills: their objects live here, and the systems and finalizers reading them run here. Resolves,
34
+ * once the world accepted the claim, to the same world: its `run` runs systems there, and its `add` and `remove` take
35
+ * observer systems, which run here after the world's sync points. Add them before awaiting anything else, so the
36
+ * world's `share` resolves with them in place. Its `dispose` disconnects this thread. Rejects with an `Error` saying
37
+ * why it didn't join: the world refused the claim, was disposed, or its thread ended first, as the platform reports it:
38
+ * the world's `Worker` failing to start or throwing first, a port closed, as in node, or, in a browser, the world's
39
+ * thread ending once it was shared through a port this thread listened on. A browser world's worker that the page ended
40
+ * before this thread heard from it isn't reported, so the join stays pending. Rejects too where memory can't be shared,
41
+ * as without cross-origin isolation.
42
+ */
43
+ readonly join: Join;
44
+ } = { create, join };
@@ -1,5 +1,11 @@
1
1
  // © 2026 Adobe. MIT License. See /LICENSE for details.
2
+ import type { Key } from './key.js';
3
+ import type { Type } from './value.js';
2
4
 
3
- export type Component = never;
5
+ /**
6
+ * The type of component `K`'s value, as its record in `Components` declares it: `Component<typeof position>`. The
7
+ * namespace's `Key` and `With` name components' keys.
8
+ */
9
+ export type Component<K extends Key> = Type<K>;
4
10
 
5
11
  export * as Component from './index.js';
@@ -1,3 +1,3 @@
1
1
  // © 2026 Adobe. MIT License. See /LICENSE for details.
2
2
  export type { Key } from './key.js';
3
- export type { Value, With } from './value.js';
3
+ export type { With } from './value.js';
@@ -4,16 +4,16 @@ import type { Key as TraitKey } from "../trait/key.js";
4
4
  import type { Key } from "./key.js";
5
5
  import type { Components } from "./types.js";
6
6
 
7
- /** Every component's value type, computed once so `Value` is a lookup. */
8
- type ValueTypes = { readonly [K in Key]: Components[K] extends { readonly type: infer T } ? T : never };
7
+ /** Every component's value type, computed once so `Type` is a lookup. */
8
+ type Types = { readonly [K in Key]: Components[K] extends { readonly type: infer T } ? T : never };
9
9
 
10
10
  /** The value type of component `K`. */
11
- export type Value<K extends Key> = ValueTypes[K];
11
+ export type Type<K extends Key> = Types[K];
12
12
 
13
13
  type Primitive = string | number | boolean | bigint | symbol | null | undefined;
14
14
 
15
15
  /** Component `K`'s value as reads return it: read-only, with branded primitives such as `Entity` as they are. */
16
- export type ReadonlyValue<K extends Key> = Value<K> extends Primitive ? Value<K> : Readonly<Value<K>>;
16
+ export type ReadonlyType<K extends Key> = Type<K> extends Primitive ? Type<K> : Readonly<Type<K>>;
17
17
 
18
18
  /** Components that declare trait `T`, with a value assignable to `V`. */
19
19
  export type With<T extends TraitKey, V = unknown> = { [K in Key]: Components[K] extends { readonly [P in T]: V } ? K : never }[Key];
@@ -35,7 +35,7 @@ export type Shared = With<"shared">;
35
35
  export type Writable = Exclude<Key, With<"reference"> | Shared>;
36
36
 
37
37
  /** Every component's thread, computed once so `ThreadOf` is a lookup. */
38
- type Threads = { readonly [K in Key]: ThreadOf<Components[K], Value<K>> };
38
+ type Threads = { readonly [K in Key]: ThreadOf<Components[K], Type<K>> };
39
39
 
40
40
  /** The thread component `K` lives on, or `never` for a struct, which lives in shared memory. */
41
41
  export type ThreadOfComponent<K extends Key> = Threads[K];
@@ -1,6 +1,6 @@
1
1
  // © 2026 Adobe. MIT License. See /LICENSE for details.
2
2
  import type { Key } from "./key.js";
3
- import type { Value } from './value.js';
3
+ import type { Type } from './value.js';
4
4
 
5
5
  /** Values for any subset of components, keyed by component. */
6
- export type Values = { readonly [K in Key]?: Value<K> };
6
+ export type Values = { readonly [K in Key]?: Type<K> };
@@ -6,7 +6,7 @@ import type { Listed, Property } from '../system/property.js';
6
6
  import type { LookupFunction } from '../system/values.js';
7
7
  import type { WriteResources } from '../system/body.js';
8
8
  import type { ChangesDeclaration, Declaration } from './declaration.js';
9
- import type { Essential, ReadonlyValue } from '../component/value.js';
9
+ import type { Essential, ReadonlyType } from '../component/value.js';
10
10
  import type { Elsewhere, ThreadsOf } from '../system/threads.js';
11
11
 
12
12
  /**
@@ -14,8 +14,8 @@ import type { Elsewhere, ThreadsOf } from '../system/threads.js';
14
14
  * its `optional` where it had them, `undefined` where it didn't.
15
15
  */
16
16
  export type Row<D extends Declaration> =
17
- { readonly entity: Entity } & { readonly [K in Listed<Property<D, "components">, "with"> & Component.Key]: Component.Value<K> }
18
- & { readonly [K in Exclude<Listed<Property<D, "components">, "optional">, Listed<Property<D, "components">, "with">> & Component.Key]: Component.Value<K> | undefined };
17
+ { readonly entity: Entity } & { readonly [K in Listed<Property<D, "components">, "with"> & Component.Key]: Component<K> }
18
+ & { readonly [K in Exclude<Listed<Property<D, "components">, "optional">, Listed<Property<D, "components">, "with">> & Component.Key]: Component<K> | undefined };
19
19
 
20
20
  /**
21
21
  * Every resource, its lookups when declared, and the commands it issues for the next sync point: none on another
@@ -46,7 +46,7 @@ export type Group<C extends Component.Key> = {
46
46
  export type ChangeSet<C extends Component.Key> = {
47
47
  readonly groups: readonly Group<C>[];
48
48
  /** The value before the sync point, or null if the entity didn't have it. */
49
- previous<K extends C>(entity: Entity, component: K): ReadonlyValue<K> | null;
49
+ previous<K extends C>(entity: Entity, component: K): ReadonlyType<K> | null;
50
50
  /** The value after it, or null if the entity won't have it. */
51
- next<K extends C>(entity: Entity, component: K): ReadonlyValue<K> | null;
51
+ next<K extends C>(entity: Entity, component: K): ReadonlyType<K> | null;
52
52
  };
@@ -36,7 +36,7 @@ export const added = world.add(read);
36
36
  // Only types a consumer declares with are named; the rest are inferred where wanted.
37
37
  // @ts-expect-error a thread's name is keyof Threads
38
38
  export type NoThread = Ecs.Thread;
39
- // @ts-expect-error the cadence's value is Resource.Value<typeof cadence>
39
+ // @ts-expect-error the cadence's value is Resource<typeof cadence>
40
40
  export type NoCadence = Ecs.Cadence;
41
41
  // @ts-expect-error a rule is the value Rule.create returns: ReturnType<typeof Rule.create>
42
42
  export const noRule: Rule = Rule.create({ components: { with: [position] }, derive: [slot] });
@@ -1,9 +1,9 @@
1
1
  // © 2026 Adobe. MIT License. See /LICENSE for details.
2
2
  import type { Key } from "./key.js";
3
- import type { Value } from './value.js';
3
+ import type { Type } from './value.js';
4
4
 
5
5
  /** The resources a system can run on the condition of: their type allows `null` or `false`. */
6
- export type Condition = { [K in Key]: null extends Value<K> ? K : false extends Value<K> ? K : never }[Key];
6
+ export type Condition = { [K in Key]: null extends Type<K> ? K : false extends Type<K> ? K : never }[Key];
7
7
 
8
8
  /** A condition's value while `is` holds: neither `null` nor `false`. */
9
- export type Holding<K extends Key> = Exclude<Value<K>, null | false>;
9
+ export type Holding<K extends Key> = Exclude<Type<K>, null | false>;
@@ -1,3 +1,2 @@
1
1
  // © 2026 Adobe. MIT License. See /LICENSE for details.
2
2
  export type { Key } from './key.js';
3
- export type { Value } from './value.js';
@@ -1,5 +1,8 @@
1
1
  // © 2026 Adobe. MIT License. See /LICENSE for details.
2
+ import type { Key } from './key.js';
3
+ import type { Type } from './value.js';
2
4
 
3
- export type Resource = never;
5
+ /** The type of resource `K`'s value, as its record in `Resources` declares it: `Resource<typeof clock>`. */
6
+ export type Resource<K extends Key> = Type<K>;
4
7
 
5
8
  export * as Resource from './index.js';
@@ -3,19 +3,19 @@ import type { ThreadOf } from '../thread/of.js';
3
3
  import type { Key } from "./key.js";
4
4
  import type { Resources } from "./types.js";
5
5
 
6
- /** Every resource's value type, computed once so `Value` is a lookup. */
7
- type ValueTypes = { readonly [K in Key]: Resources[K] extends { readonly type: infer T } ? T : never };
6
+ /** Every resource's value type, computed once so `Type` is a lookup. */
7
+ type Types = { readonly [K in Key]: Resources[K] extends { readonly type: infer T } ? T : never };
8
8
 
9
9
  /** The value type of resource `K`. */
10
- export type Value<K extends Key> = ValueTypes[K];
10
+ export type Type<K extends Key> = Types[K];
11
11
 
12
12
  type Primitive = string | number | boolean | bigint | symbol | null | undefined;
13
13
 
14
14
  /** Resource `K`'s value as reads return it: read-only, with branded primitives such as `Entity` as they are. */
15
- export type ReadonlyValue<K extends Key> = Value<K> extends Primitive ? Value<K> : Readonly<Value<K>>;
15
+ export type ReadonlyType<K extends Key> = Type<K> extends Primitive ? Type<K> : Readonly<Type<K>>;
16
16
 
17
17
  /** Every resource's thread, computed once so `ThreadOf` is a lookup. */
18
- type Threads = { readonly [K in Key]: ThreadOf<Resources[K], Value<K>> };
18
+ type Threads = { readonly [K in Key]: ThreadOf<Resources[K], Type<K>> };
19
19
 
20
20
  /** The thread resource `K` lives on, or `never` for a struct, which lives in shared memory. */
21
21
  export type ThreadOfResource<K extends Key> = Threads[K];
@@ -1,7 +1,7 @@
1
1
  // © 2026 Adobe. MIT License. See /LICENSE for details.
2
2
  import { registerResource } from '../registry/declarations.js';
3
3
 
4
- /** The cadence's value, for the implementation: `Resource.Value<typeof cadence>`. */
4
+ /** The cadence's value, for the implementation: `Resource<typeof cadence>`. */
5
5
  export type Cadence = { readonly kind: "frame" } | { readonly kind: "fixed"; readonly step: number; readonly maxSteps?: number };
6
6
 
7
7
  /**
@@ -26,7 +26,7 @@ export type ComponentAccess = {
26
26
  * that runs the system, once per archetype as it appears and again whenever a resource it reads changed. With
27
27
  * change filters, an archetype it skipped keeps its rows unseen until it passes again.
28
28
  */
29
- readonly where?: { readonly [K in Shared]?: Component.Value<K> | ((value: Component.Value<K>, context: { readonly resources: ReadResources }) => boolean) };
29
+ readonly where?: { readonly [K in Shared]?: Component<K> | ((value: Component<K>, context: { readonly resources: ReadResources }) => boolean) };
30
30
  };
31
31
 
32
32
  /**
@@ -23,7 +23,7 @@ type Reading<D> = D extends { readonly observer: true } | { readonly shader: obj
23
23
  export type Optional<D> = Exclude<Listed<Property<D, "components">, "optional">, Members<D>>;
24
24
 
25
25
  /** Each optional component's value or `undefined`, as `ComponentValues` gives members'. */
26
- type OptionalValues = { readonly [K in Component.Key]: Component.Value<K> | undefined };
26
+ type OptionalValues = { readonly [K in Component.Key]: Component<K> | undefined };
27
27
 
28
28
  /**
29
29
  * Its members' values, writable but for references and shared components, and in a system that only reads; its
@@ -45,7 +45,7 @@ export type ReadResources = Values<Resource.Key, never, Resource.Key, ResourceVa
45
45
  export type WriteResources = Values<Resource.Key, Writes, Resource.Key, ResourceValues>;
46
46
 
47
47
  /** Each reduced resource, as a function contributing a value to the run's fold. */
48
- type Reductions = { readonly [K in Reduced]: (value: Resource.Value<K>) => void };
48
+ type Reductions = { readonly [K in Reduced]: (value: Resource<K>) => void };
49
49
 
50
50
  type Is<D> = Listed<Property<D, "resources">, "is">;
51
51
 
@@ -5,13 +5,13 @@ import type { Essential, Shared } from '../component/value.js';
5
5
  import type { ValuesOf } from './values-of.js';
6
6
 
7
7
  /** The value an `add` of one component takes: none for a tag. */
8
- type ValueArgument<K extends Essential> = Component.Value<K> extends true ? [value?: true] : [value: Component.Value<K>];
8
+ type ValueArgument<K extends Essential> = Component<K> extends true ? [value?: true] : [value: Component<K>];
9
9
 
10
10
  /** Values of components of `C` a spawner's first call may give, constant for every entity it spawns. */
11
- type Preset<C extends readonly Essential[]> = { readonly [K in C[number]]?: Component.Value<K> };
11
+ type Preset<C extends readonly Essential[]> = { readonly [K in C[number]]?: Component<K> };
12
12
 
13
13
  /** The value of each shared component of `C`, which a spawner's first call must give, since they choose the archetype. */
14
- type SharedPreset<C extends readonly Essential[]> = { readonly [K in C[number] & Shared]: Component.Value<K> };
14
+ type SharedPreset<C extends readonly Essential[]> = { readonly [K in C[number] & Shared]: Component<K> };
15
15
 
16
16
  /**
17
17
  * Whether `C` names a shared component, so a spawner of it takes its constants: a list that isn't a tuple, whose
@@ -27,7 +27,7 @@ type Named<C extends readonly Essential[], P> = { readonly [K in Exclude<keyof P
27
27
  * the spawn takes their values first.
28
28
  */
29
29
  type Spawned<C extends readonly Essential[], P> = RequiresPreset<C> extends true
30
- ? [keyof P] extends [never] ? C & { readonly "error: a spawn of a shared component takes its value first, in its constants": { readonly [K in C[number] & Shared]: Component.Value<K> } } : C
30
+ ? [keyof P] extends [never] ? C & { readonly "error: a spawn of a shared component takes its value first, in its constants": { readonly [K in C[number] & Shared]: Component<K> } } : C
31
31
  : C;
32
32
 
33
33
  /** Each component of `C` the constants `P` gave, whose value a spawn's values give again as a message naming it. */
@@ -4,7 +4,7 @@ import type { Resource } from '../resource/resource.js';
4
4
  import type { IsStruct } from '../schema/is-struct.js';
5
5
 
6
6
  /** Components whose values are structs, which kernels read and write. */
7
- export type StructComponent = { [K in Component.Key]: IsStruct<Component.Value<K>> extends true ? K : never }[Component.Key];
7
+ export type StructComponent = { [K in Component.Key]: IsStruct<Component<K>> extends true ? K : never }[Component.Key];
8
8
 
9
9
  /** Resources whose values are structs, which kernels read. */
10
- export type StructResource = { [K in Resource.Key]: IsStruct<Resource.Value<K>> extends true ? K : never }[Resource.Key];
10
+ export type StructResource = { [K in Resource.Key]: IsStruct<Resource<K>> extends true ? K : never }[Resource.Key];
@@ -2,7 +2,7 @@
2
2
  import type { Component } from '../component/component.js';
3
3
 
4
4
  /** Components a spawn or add may leave out: a tag, whose value is `true`, and one whose type allows `null`, its start. */
5
- type Omissible<K extends Component.Key> = Component.Value<K> extends true ? K : null extends Component.Value<K> ? K : never;
5
+ type Omissible<K extends Component.Key> = Component<K> extends true ? K : null extends Component<K> ? K : never;
6
6
 
7
7
  /**
8
8
  * The values a spawn or add of the components `C` takes, keyed by component and exact against `C`, but those `Given`
@@ -12,6 +12,6 @@ type Omissible<K extends Component.Key> = Component.Value<K> extends true ? K :
12
12
  */
13
13
  export type ValuesOf<C extends readonly Component.Key[], Given extends PropertyKey = never> = number extends C["length"]
14
14
  // A list whose components aren't known until it runs, so any of them may be given.
15
- ? { readonly [K in Exclude<C[number], Given>]?: Component.Value<K> }
16
- : { readonly [K in Exclude<C[number], Given> as K extends Omissible<K> ? never : K]: Component.Value<K> }
17
- & { readonly [K in Exclude<C[number], Given> as K extends Omissible<K> ? K : never]?: Component.Value<K> };
15
+ ? { readonly [K in Exclude<C[number], Given>]?: Component<K> }
16
+ : { readonly [K in Exclude<C[number], Given> as K extends Omissible<K> ? never : K]: Component<K> }
17
+ & { readonly [K in Exclude<C[number], Given> as K extends Omissible<K> ? K : never]?: Component<K> };
@@ -6,8 +6,8 @@ import type { Resource } from '../resource/resource.js';
6
6
  import type { Comparison } from '../world/filter.js';
7
7
  import type { Listed, Property } from './property.js';
8
8
 
9
- export type ComponentValues = { readonly [K in Component.Key]: Component.Value<K> };
10
- export type ResourceValues = { readonly [K in Resource.Key]: Resource.Value<K> };
9
+ export type ComponentValues = { readonly [K in Component.Key]: Component<K> };
10
+ export type ResourceValues = { readonly [K in Resource.Key]: Resource<K> };
11
11
 
12
12
  /** `Read` values are read-only, `Write` values writable. */
13
13
  export type Values<Read, Write, Keys extends PropertyKey, Types> =
@@ -21,7 +21,7 @@ export type KeyOf<D> = D extends { readonly key: infer K extends readonly Compon
21
21
  export type HeldBy<D> = KeyOf<D>[number] | Listed<Property<D, "components">, "with">;
22
22
 
23
23
  /** The key's values, in key order. */
24
- type KeyValues<K extends readonly Component.Key[]> = { readonly [I in keyof K]: Component.Value<K[I] & Component.Key> };
24
+ type KeyValues<K extends readonly Component.Key[]> = { readonly [I in keyof K]: Component<K[I] & Component.Key> };
25
25
 
26
26
  /** A prefix of the key, possibly empty, whose last part may be a comparison. */
27
27
  type Prefix<V extends readonly unknown[]> = V extends readonly [infer First, ...infer Rest]
@@ -29,7 +29,7 @@ type Prefix<V extends readonly unknown[]> = V extends readonly [infer First, ...
29
29
  : readonly [];
30
30
 
31
31
  /** A row an index lookup gives: its entity and the values of what every entity the index holds has, read-only. */
32
- type IndexRow<D> = { readonly entity: Entity } & { readonly [K in HeldBy<D> & Component.Key]: Component.Value<K> };
32
+ type IndexRow<D> = { readonly entity: Entity } & { readonly [K in HeldBy<D> & Component.Key]: Component<K> };
33
33
 
34
34
  /**
35
35
  * The rows a lookup found, in the index's order: by entity for a hash index, by key and then entity for a sorted one.
@@ -65,7 +65,7 @@ type IndexLookup<D> =
65
65
  type EntityRow<L> = {
66
66
  readonly [K in (Listed<L, "with"> & Component.Key) | (Exclude<Listed<L, "optional">, Listed<L, "with">> & Component.Key) | (L extends { readonly changed: readonly Component.Key[] } ? "changed" : never)]:
67
67
  // Its `optional` components are their values where the entity has them, `undefined` where it doesn't.
68
- K extends Component.Key ? K extends Listed<L, "with"> ? Component.Value<K> : Component.Value<K> | undefined : boolean
68
+ K extends Component.Key ? K extends Listed<L, "with"> ? Component<K> : Component<K> | undefined : boolean
69
69
  };
70
70
 
71
71
  export type LookupFunction<L> = L extends { readonly index: Index<infer D> }
@@ -55,7 +55,7 @@ declare module '@adobe/ecs' {
55
55
  }
56
56
  }
57
57
 
58
- check<Equal<Component.Value<typeof path>, readonly number[]>>();
58
+ check<Equal<Component<typeof path>, readonly number[]>>();
59
59
  check<Equal<ThreadOf<{ readonly thread: "main" }, string>, never>>();
60
60
  check<Equal<ThreadOf<{ readonly thread: "main" }, "edit" | "view">, never>>();
61
61
  check<Equal<ThreadOf<{ readonly thread: "main" }, readonly string[]>, "main">>();