@adobe/ecs 0.2.0 → 0.2.3

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 (262) hide show
  1. package/README.md +2 -2
  2. package/dist/implementation/compiler/describe.d.ts +1 -1
  3. package/dist/implementation/compiler/describe.js +3 -3
  4. package/dist/implementation/compiler/describe.js.map +1 -1
  5. package/dist/implementation/compiler/signature.js +1 -1
  6. package/dist/implementation/compiler/transform.js +3 -3
  7. package/dist/implementation/compiler/transform.js.map +1 -1
  8. package/dist/implementation/world/commands.js +50 -1
  9. package/dist/implementation/world/commands.js.map +1 -1
  10. package/dist/implementation/world/create.js.map +1 -1
  11. package/dist/implementation/world/entities.d.ts +2 -0
  12. package/dist/implementation/world/entities.js +6 -0
  13. package/dist/implementation/world/entities.js.map +1 -1
  14. package/dist/implementation/world/finalizers.js.map +1 -1
  15. package/dist/implementation/world/join.js +10 -2
  16. package/dist/implementation/world/join.js.map +1 -1
  17. package/dist/implementation/world/register.js +2 -2
  18. package/dist/implementation/world/rows.d.ts +1 -1
  19. package/dist/implementation/world/rows.js +5 -0
  20. package/dist/implementation/world/rows.js.map +1 -1
  21. package/dist/implementation/world/runner.js +3 -3
  22. package/dist/implementation/world/runner.js.map +1 -1
  23. package/dist/implementation/world/sharing.js +1 -1
  24. package/dist/implementation/world/staging.d.ts +4 -0
  25. package/dist/implementation/world/staging.js +15 -3
  26. package/dist/implementation/world/staging.js.map +1 -1
  27. package/dist/implementation/world/tables-read.d.ts +7 -0
  28. package/dist/implementation/world/tables-read.js +60 -0
  29. package/dist/implementation/world/tables-read.js.map +1 -0
  30. package/dist/implementation/world/tables.d.ts +9 -0
  31. package/dist/implementation/world/tables.js +355 -0
  32. package/dist/implementation/world/tables.js.map +1 -0
  33. package/dist/implementation/world/thread-observer.js +1 -1
  34. package/dist/implementation/world/thread-observer.js.map +1 -1
  35. package/dist/implementation/world/thread-systems.js +1 -0
  36. package/dist/implementation/world/thread-systems.js.map +1 -1
  37. package/dist/index.d.ts +8 -8
  38. package/dist/index.js +4 -9
  39. package/dist/index.js.map +1 -1
  40. package/dist/interface/component/component.d.ts +7 -1
  41. package/dist/interface/component/component.js +0 -1
  42. package/dist/interface/component/component.js.map +1 -1
  43. package/dist/interface/component/declarations.d.ts +4 -0
  44. package/dist/interface/component/declarations.js +8 -0
  45. package/dist/interface/component/declarations.js.map +1 -0
  46. package/dist/interface/component/declared.d.ts +12 -0
  47. package/dist/interface/component/declared.js +2 -0
  48. package/dist/interface/component/declared.js.map +1 -0
  49. package/dist/interface/component/index.d.ts +3 -1
  50. package/dist/interface/component/index.js +1 -1
  51. package/dist/interface/component/index.js.map +1 -1
  52. package/dist/interface/component/value.d.ts +5 -5
  53. package/dist/interface/component/values.d.ts +2 -2
  54. package/dist/interface/finalizer/body.d.ts +5 -5
  55. package/dist/interface/registry/names.d.ts +2 -0
  56. package/dist/interface/registry/names.js +9 -0
  57. package/dist/interface/registry/names.js.map +1 -1
  58. package/dist/interface/registry/schedule.d.ts +3 -3
  59. package/dist/interface/registry/schedule.js +3 -3
  60. package/dist/interface/registry/schedule.js.map +1 -1
  61. package/dist/interface/resource/condition.d.ts +3 -3
  62. package/dist/interface/resource/declarations.d.ts +4 -0
  63. package/dist/interface/resource/declarations.js +8 -0
  64. package/dist/interface/resource/declarations.js.map +1 -0
  65. package/dist/interface/resource/index.d.ts +2 -1
  66. package/dist/interface/resource/index.js +1 -1
  67. package/dist/interface/resource/index.js.map +1 -1
  68. package/dist/interface/resource/resource.d.ts +4 -1
  69. package/dist/interface/resource/resource.js +0 -1
  70. package/dist/interface/resource/resource.js.map +1 -1
  71. package/dist/interface/resource/value.d.ts +5 -5
  72. package/dist/interface/resources/cadence.d.ts +1 -1
  73. package/dist/interface/system/access.d.ts +1 -1
  74. package/dist/interface/system/body.d.ts +13 -8
  75. package/dist/interface/system/check.d.ts +1 -1
  76. package/dist/interface/system/check.js +25 -9
  77. package/dist/interface/system/check.js.map +1 -1
  78. package/dist/interface/system/commands.d.ts +20 -4
  79. package/dist/interface/system/create.d.ts +21 -9
  80. package/dist/interface/system/create.js +3 -3
  81. package/dist/interface/system/create.js.map +1 -1
  82. package/dist/interface/system/declaration.d.ts +9 -2
  83. package/dist/interface/system/struct.d.ts +2 -2
  84. package/dist/interface/system/tables.d.ts +20 -0
  85. package/dist/interface/system/tables.js +2 -0
  86. package/dist/interface/system/tables.js.map +1 -0
  87. package/dist/interface/system/values-of.d.ts +4 -4
  88. package/dist/interface/system/values.d.ts +5 -5
  89. package/dist/interface/trait/types.d.ts +4 -4
  90. package/dist/interface/world/create.d.ts +3 -7
  91. package/dist/interface/world/declarations.d.ts +2 -2
  92. package/dist/interface/world/index.d.ts +1 -1
  93. package/dist/interface/world/join.d.ts +2 -10
  94. package/dist/interface/world/table/index.d.ts +26 -0
  95. package/dist/interface/world/table/index.js +3 -0
  96. package/dist/interface/world/table/index.js.map +1 -0
  97. package/dist/interface/world/table/table.d.ts +11 -0
  98. package/dist/interface/world/table/table.js +2 -0
  99. package/dist/interface/world/table/table.js.map +1 -0
  100. package/dist/interface/world/world.d.ts +10 -10
  101. package/dist/world.d.ts +27 -0
  102. package/dist/world.js +27 -0
  103. package/dist/world.js.map +1 -0
  104. package/package.json +1 -1
  105. package/src/exports.test.ts +3 -3
  106. package/src/guide/00-keys.ts +1 -1
  107. package/src/guide/01-resources.md +1 -1
  108. package/src/guide/01-resources.ts +1 -1
  109. package/src/guide/02-entities.ts +2 -2
  110. package/src/guide/03-systems.md +2 -2
  111. package/src/guide/03-systems.out +1 -1
  112. package/src/guide/03-systems.ts +3 -3
  113. package/src/guide/04-ticks.md +1 -1
  114. package/src/guide/04-ticks.ts +3 -3
  115. package/src/guide/05-scheduling.ts +1 -1
  116. package/src/guide/06-sync-points.md +9 -2
  117. package/src/guide/06-sync-points.ts +2 -2
  118. package/src/guide/07-change-filters.ts +2 -2
  119. package/src/guide/08-input.ts +1 -1
  120. package/src/guide/09-rules.ts +1 -1
  121. package/src/guide/11-traits.ts +3 -3
  122. package/src/guide/11-traits.type-test.ts +1 -1
  123. package/src/guide/12-lookups.ts +2 -2
  124. package/src/guide/13-indexes.ts +4 -4
  125. package/src/guide/14-kernels.ts +1 -1
  126. package/src/guide/15-reductions.ts +1 -1
  127. package/src/guide/16-gpu.ts +1 -1
  128. package/src/guide/17-hierarchies.ts +1 -1
  129. package/src/guide/18-identity.out +1 -1
  130. package/src/guide/19-threads.md +4 -2
  131. package/src/guide/19-threads.ts +2 -2
  132. package/src/guide/20-shared.ts +3 -3
  133. package/src/guide/21-dom.md +2 -2
  134. package/src/guide/21-dom.ts +1 -1
  135. package/src/guide/22-canvas.md +1 -1
  136. package/src/guide/22-canvas.ts +1 -1
  137. package/src/guide/CLAUDE.md +1 -1
  138. package/src/guide/guide.test.ts +7 -5
  139. package/src/implementation/compiler/describe.test-helper.ts +1 -1
  140. package/src/implementation/compiler/describe.test.ts +1 -1
  141. package/src/implementation/compiler/describe.ts +3 -3
  142. package/src/implementation/compiler/hygiene.test.ts +1 -1
  143. package/src/implementation/compiler/plugin.test.ts +2 -2
  144. package/src/implementation/compiler/signature.ts +1 -1
  145. package/src/implementation/compiler/transform.test.ts +3 -3
  146. package/src/implementation/compiler/transform.ts +3 -3
  147. package/src/implementation/world/change-blocks.test.ts +1 -1
  148. package/src/implementation/world/commands.ts +44 -1
  149. package/src/implementation/world/create.ts +3 -3
  150. package/src/implementation/world/defaults.test.ts +1 -1
  151. package/src/implementation/world/entities.ts +6 -0
  152. package/src/implementation/world/finalizers.test.ts +1 -1
  153. package/src/implementation/world/finalizers.ts +3 -3
  154. package/src/implementation/world/index-changed.test.ts +2 -2
  155. package/src/implementation/world/index-joined.test.ts +5 -5
  156. package/src/implementation/world/index-order.test.ts +1 -1
  157. package/src/implementation/world/join.test.ts +4 -4
  158. package/src/implementation/world/join.ts +10 -2
  159. package/src/implementation/world/joined-observer.test.ts +1 -1
  160. package/src/implementation/world/joining-worker.fixture.ts +1 -1
  161. package/src/implementation/world/joining.fixture.ts +1 -1
  162. package/src/implementation/world/joining.test-helper.ts +1 -1
  163. package/src/implementation/world/kernel-spawns.test.ts +1 -1
  164. package/src/implementation/world/observe-factories.test.ts +2 -2
  165. package/src/implementation/world/observe-ticks-cases.test.ts +1 -1
  166. package/src/implementation/world/observe.test-helper.ts +1 -1
  167. package/src/implementation/world/observer-contained-worker.fixture.ts +1 -1
  168. package/src/implementation/world/observer-contained.test-helper.ts +1 -1
  169. package/src/implementation/world/observer-contained.test.ts +2 -2
  170. package/src/implementation/world/observer-joined.test-helper.ts +4 -4
  171. package/src/implementation/world/observer-joined.test.ts +23 -15
  172. package/src/implementation/world/observer.browser.test.ts +2 -2
  173. package/src/implementation/world/observer.test.ts +8 -8
  174. package/src/implementation/world/optional-workers.fixture.ts +1 -1
  175. package/src/implementation/world/optional.test.ts +7 -7
  176. package/src/implementation/world/pool-size.cases.ts +3 -1
  177. package/src/implementation/world/reads.test-helper.ts +6 -6
  178. package/src/implementation/world/reclaim.test.ts +3 -3
  179. package/src/implementation/world/register.ts +2 -2
  180. package/src/implementation/world/remote-objects.test-helper.ts +1 -1
  181. package/src/implementation/world/rows.ts +5 -1
  182. package/src/implementation/world/run-results.test.ts +10 -10
  183. package/src/implementation/world/runner.ts +3 -3
  184. package/src/implementation/world/shared-gpu.browser.test.ts +2 -2
  185. package/src/implementation/world/shared-kernels.test.ts +2 -2
  186. package/src/implementation/world/shared-workers.fixture.ts +2 -2
  187. package/src/implementation/world/shared.test.ts +8 -8
  188. package/src/implementation/world/sharing.ts +1 -1
  189. package/src/implementation/world/staging.ts +16 -3
  190. package/src/implementation/world/tables-read.ts +59 -0
  191. package/src/implementation/world/tables.test.ts +267 -0
  192. package/src/implementation/world/tables.ts +330 -0
  193. package/src/implementation/world/tails.test.ts +1 -1
  194. package/src/implementation/world/target.test.ts +2 -2
  195. package/src/implementation/world/thread-observer.ts +1 -1
  196. package/src/implementation/world/thread-systems.test-helper.ts +3 -3
  197. package/src/implementation/world/thread-systems.ts +1 -0
  198. package/src/implementation/world/where.test.ts +4 -4
  199. package/src/implementation/world/workers.fixture.ts +2 -2
  200. package/src/index.ts +4 -6
  201. package/src/interface/component/component.ts +7 -1
  202. package/src/interface/component/declarations.ts +11 -0
  203. package/src/interface/component/declared.ts +9 -0
  204. package/src/interface/component/index.ts +3 -1
  205. package/src/interface/component/value.ts +5 -5
  206. package/src/interface/component/values.ts +2 -2
  207. package/src/interface/finalizer/body.ts +5 -5
  208. package/src/interface/index.type-test.ts +3 -3
  209. package/src/interface/indexes/index.type-test.ts +1 -1
  210. package/src/interface/registry/names.ts +8 -0
  211. package/src/interface/registry/schedule.ts +4 -4
  212. package/src/interface/resource/condition.ts +3 -3
  213. package/src/interface/resource/declarations.ts +11 -0
  214. package/src/interface/resource/index.ts +2 -1
  215. package/src/interface/resource/resource.ts +4 -1
  216. package/src/interface/resource/value.ts +5 -5
  217. package/src/interface/resources/cadence.ts +1 -1
  218. package/src/interface/system/access.ts +1 -1
  219. package/src/interface/system/body.ts +9 -7
  220. package/src/interface/system/check.ts +22 -8
  221. package/src/interface/system/commands.ts +18 -4
  222. package/src/interface/system/create.test.ts +13 -13
  223. package/src/interface/system/create.ts +15 -10
  224. package/src/interface/system/declaration.ts +9 -2
  225. package/src/interface/system/lists.type-test.ts +1 -1
  226. package/src/interface/system/optional.type-test.ts +1 -1
  227. package/src/interface/system/shared.type-test.ts +1 -1
  228. package/src/interface/system/struct.ts +2 -2
  229. package/src/interface/system/system.type-test.ts +26 -20
  230. package/src/interface/system/tables.ts +21 -0
  231. package/src/interface/system/up.type-test.ts +3 -3
  232. package/src/interface/system/values-of.ts +4 -4
  233. package/src/interface/system/values.ts +5 -5
  234. package/src/interface/thread/thread.type-test.ts +1 -1
  235. package/src/interface/trait/trait.type-test.ts +4 -4
  236. package/src/interface/trait/types.ts +4 -4
  237. package/src/interface/world/create.ts +3 -7
  238. package/src/interface/world/declarations.ts +2 -2
  239. package/src/interface/world/index.ts +2 -2
  240. package/src/interface/world/join.ts +2 -10
  241. package/src/interface/world/loop.type-test.ts +6 -6
  242. package/src/interface/world/run.type-test.ts +3 -3
  243. package/src/interface/world/table/index.ts +19 -0
  244. package/src/interface/world/table/table.ts +12 -0
  245. package/src/interface/world/world.ts +10 -10
  246. package/src/observe/entity.ts +2 -2
  247. package/src/observe/members.ts +3 -3
  248. package/src/observe/query.ts +1 -1
  249. package/src/observe/resource.ts +2 -2
  250. package/src/observe/select.ts +5 -5
  251. package/src/observe/values.ts +2 -2
  252. package/src/observe/world-state.ts +3 -3
  253. package/src/perftest/benchmarks.ts +1 -1
  254. package/src/perftest/indexes.ts +3 -3
  255. package/src/perftest/loop.ts +1 -1
  256. package/src/perftest/observer.ts +4 -4
  257. package/src/perftest/reads.ts +2 -2
  258. package/src/perftest/remote.ts +5 -5
  259. package/src/perftest/strings.ts +1 -1
  260. package/src/perftest/suite.ts +1 -1
  261. package/src/world.test.ts +2 -2
  262. package/src/world.ts +32 -0
@@ -0,0 +1,20 @@
1
+ import type { Component } from '../component/component.js';
2
+ import type { Resource } from '../resource/resource.js';
3
+ import type { Table } from '../world/table/table.js';
4
+ /**
5
+ * What a readonly system's `tables` reads: of every archetype matching `with` and `not` that holds any of
6
+ * `components`, a table of those it holds; and, with `resources`, each resource's value.
7
+ */
8
+ export type TablesDeclaration = {
9
+ readonly components: readonly Component.Key[];
10
+ readonly with?: readonly Component.Key[];
11
+ readonly not?: readonly Component.Key[];
12
+ readonly resources?: readonly Resource.Key[];
13
+ };
14
+ /** A readonly system's `tables`: a table of each archetype its declaration reads, and the values of the resources it reads. */
15
+ export type Tables = {
16
+ readonly archetypes: readonly Table[];
17
+ readonly resources: {
18
+ readonly [K in Resource.Key]?: Resource<K>;
19
+ };
20
+ };
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=tables.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tables.js","sourceRoot":"","sources":["../../../src/interface/system/tables.ts"],"names":[],"mappings":""}
@@ -1,6 +1,6 @@
1
1
  import type { Component } from '../component/component.js';
2
2
  /** Components a spawn or add may leave out: a tag, whose value is `true`, and one whose type allows `null`, its start. */
3
- type Omissible<K extends Component.Key> = Component.Value<K> extends true ? K : null extends Component.Value<K> ? K : never;
3
+ type Omissible<K extends Component.Key> = Component<K> extends true ? K : null extends Component<K> ? K : never;
4
4
  /**
5
5
  * The values a spawn or add of the components `C` takes, keyed by component and exact against `C`, but those `Given`
6
6
  * already: a tag needs no value, a component whose type allows `null` may be left out to start at `null`, and every
@@ -8,10 +8,10 @@ type Omissible<K extends Component.Key> = Component.Value<K> extends true ? K :
8
8
  * tuple takes any of its components' values.
9
9
  */
10
10
  export type ValuesOf<C extends readonly Component.Key[], Given extends PropertyKey = never> = number extends C["length"] ? {
11
- readonly [K in Exclude<C[number], Given>]?: Component.Value<K>;
11
+ readonly [K in Exclude<C[number], Given>]?: Component<K>;
12
12
  } : {
13
- readonly [K in Exclude<C[number], Given> as K extends Omissible<K> ? never : K]: Component.Value<K>;
13
+ readonly [K in Exclude<C[number], Given> as K extends Omissible<K> ? never : K]: Component<K>;
14
14
  } & {
15
- 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> as K extends Omissible<K> ? K : never]?: Component<K>;
16
16
  };
17
17
  export {};
@@ -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 +1 @@
1
- export {};
1
+ export type { Table } from './table/table.js';
@@ -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">[];
@@ -0,0 +1,26 @@
1
+ /**
2
+ * One component's values in a table, copies taken as a readonly system reads `tables`:
3
+ * - `bytes`: a struct's or number's, `stride` bytes per row in the world's layout for it, which is a function of the
4
+ * component's schema alone;
5
+ * - `values`: a string's or an object's, one per row;
6
+ * - `value`: one value every row holds, for a shared component or one whose schema is a constant.
7
+ */
8
+ export type Column = {
9
+ readonly kind: "bytes";
10
+ readonly bytes: Uint8Array;
11
+ readonly stride: number;
12
+ } | {
13
+ readonly kind: "values";
14
+ readonly values: readonly unknown[];
15
+ } | {
16
+ readonly kind: "value";
17
+ readonly value: unknown;
18
+ };
19
+ /**
20
+ * What `commands.spawnTables` spawned: each table's new entities in its row order, `Entity.none` for a row it didn't
21
+ * spawn, and how many rows it didn't, those owned by an entity outside the tables.
22
+ */
23
+ export type Spawned = {
24
+ readonly entities: readonly Float64Array[];
25
+ readonly dropped: number;
26
+ };
@@ -0,0 +1,3 @@
1
+ // © 2026 Adobe. MIT License. See /LICENSE for details.
2
+ export {};
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/interface/world/table/index.ts"],"names":[],"mappings":"AAAA,uDAAuD"}
@@ -0,0 +1,11 @@
1
+ import type { Component } from '../../component/component.js';
2
+ import type { Column } from './index.js';
3
+ /** The rows of one archetype: their entities in row order, and a column for each component asked for that it holds. */
4
+ export type Table = {
5
+ readonly count: number;
6
+ readonly entities: Float64Array;
7
+ readonly columns: {
8
+ readonly [K in Component.Key]?: Column;
9
+ };
10
+ };
11
+ export type * as Table from './index.js';
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=table.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"table.js","sourceRoot":"","sources":["../../../../src/interface/world/table/table.ts"],"names":[],"mappings":""}
@@ -1,23 +1,23 @@
1
1
  import type { Input } from '../system/input.js';
2
2
  import type { System } from '../system/system.js';
3
3
  /**
4
- * A world. Systems read and change it; observer systems read it after each run's and tick's sync point. Every member's
4
+ * A world. Systems read and change it; readonly systems read it after each run's and tick's sync point. Every member's
5
5
  * type is named from the package root, so declarations a consumer emits for its results name no internal module.
6
6
  */
7
7
  export type World = {
8
8
  /**
9
9
  * The one way to change state. Runs are sequential in call order; physically, any that don't conflict overlap. Each
10
- * runs a system or composite, ending in a sync point, then observer systems, the run's and those added, and
10
+ * runs a system or composite, ending in a sync point, then readonly systems, the run's and those added, and
11
11
  * resolves to what a per-run body returns: a system without components resolves to its body's value, or
12
12
  * `undefined` when its body was skipped, and any other system or composite to nothing. So a one-off read is a run
13
- * of an observer system returning what it read; one that runs on a joined thread, as it reads objects there, runs
14
- * there before the run resolves, to its value as structured clone. A run of observer systems alone runs them once
13
+ * of a readonly system returning what it read; one that runs on a joined thread, as it reads objects there, runs
14
+ * there before the run resolves, to its value as structured clone. A run of readonly systems alone runs them once
15
15
  * after an empty sync point, and doesn't wake the loop.
16
16
  *
17
17
  * When a system or finalizer of the run fails, later tiers are skipped, the sync point still applies what was done,
18
- * and once its observer systems ran the run rejects with an `Error` naming each failure, its system's label and id,
18
+ * and once its readonly systems ran the run rejects with an `Error` naming each failure, its system's label and id,
19
19
  * message and cause. It rejects too when the run can't happen or finish: registration fails, the world is disposed,
20
- * its GPU device is lost, or its sync point can't apply a command's value. What fails after it, an observer system
20
+ * its GPU device is lost, or its sync point can't apply a command's value. What fails after it, a readonly system
21
21
  * added or GPU work, is reported out of band, as ticks' failures are (see `add`).
22
22
  */
23
23
  run<I, R>(system: System<I, R>, ...input: Input<NoInfer<I>>): Promise<R>;
@@ -25,9 +25,9 @@ export type World = {
25
25
  * Adds a system to every tick, from the next one on, ordered with those already added by its constraints, as a
26
26
  * composite's members are. A tick runs on a beat of the `cadence` resource where `always` is set, `wake` is due or
27
27
  * a run arrived since the last tick, and only while some system is added. Throws for a system with no ordering path
28
- * to one already added that conflicts with it. An observer system runs after every run's and tick's sync point
28
+ * to one already added that conflicts with it. A readonly system runs after every run's and tick's sync point
29
29
  * instead. It never conflicts and doesn't count as an added system for ticking. On a joined thread `add` takes only
30
- * observer systems, whose keys need ids: each runs on that thread after the world's sync points, once for the
30
+ * readonly systems, whose keys need ids: each runs on that thread after the world's sync points, once for the
31
31
  * newest when the thread fell behind, reading structs and the objects of the threads it fills as of one sync point.
32
32
  *
33
33
  * What added systems throw, and what fails in ticks, is reported out of band on the world's thread, as an `Error`
@@ -41,14 +41,14 @@ export type World = {
41
41
  * released. A system added both alone and within an added composite stays until both are removed; constraints
42
42
  * naming a removed system are ignored. Removing a value that isn't added does nothing. Throws, removing nothing,
43
43
  * when the systems left would conflict with no ordering path between them, as `add` does. On a joined thread it
44
- * takes only observer systems.
44
+ * takes only readonly systems.
45
45
  */
46
46
  remove(system: System<void>): void;
47
47
  /**
48
48
  * Lets another thread join this world through `port`, one end of a channel to it, of the browser or node: a
49
49
  * `MessagePort`, a `Worker`, or a worker's own scope, `self` or node's `parentPort`. That thread joins through the
50
50
  * other end, claiming the logical threads it fills. Resolves once it has joined: the threads it claims are its own,
51
- * and the observer systems it added as its join resolved, before awaiting anything else, are added, so a run
51
+ * and the readonly systems it added as its join resolved, before awaiting anything else, are added, so a run
52
52
  * started afterwards runs on it and reaches them; one it adds later reaches the runs after its add arrived.
53
53
  * Rejects with an `Error` saying why it didn't join: the world refused its claim, as of a thread claimed twice, the
54
54
  * world was disposed, the port is shared already, or that thread ended first, as the platform reports it: a
@@ -0,0 +1,27 @@
1
+ import type { Create } from './interface/world/create.js';
2
+ import type { Join } from './interface/world/join.js';
3
+ export type { Table } from './interface/world/table/table.js';
4
+ /**
5
+ * Creates a world on this thread, synchronously, with resources starting at the given values in place of their
6
+ * defaults. This thread is its `"world"` thread for the world's life: it runs the sync points, the ticks and the GPU
7
+ * device, and fills every logical thread no joined thread claims. Keys are the resources' symbols, built-in ones
8
+ * included (`{ [workers]: 0 }`); string keys are never a resource's, so they're ignored. Throws for a symbol that isn't
9
+ * a resource, or is one only the world writes. While a declaration's trait or default doesn't hold, each call fails to
10
+ * compile, once, naming each such key, its members and what's wrong with them.
11
+ * @function
12
+ */
13
+ export declare const create: Create;
14
+ /**
15
+ * Joins, from another thread, a world that `share` let join through the other end of `port`, claiming the logical
16
+ * `threads` this thread fills: their objects live here, and the systems and finalizers reading them run here. Resolves,
17
+ * once the world accepted the claim, to the same world: its `run` runs systems there, and its `add` and `remove` take
18
+ * readonly systems, which run here after the world's sync points. Add them before awaiting anything else, so the
19
+ * world's `share` resolves with them in place. Its `dispose` disconnects this thread. Rejects with an `Error` saying
20
+ * why it didn't join: the world refused the claim, was disposed, or its thread ended first, as the platform reports it:
21
+ * the world's `Worker` failing to start or throwing first, a port closed, as in node, or, in a browser, the world's
22
+ * thread ending once it was shared through a port this thread listened on. A browser world's worker that the page ended
23
+ * before this thread heard from it isn't reported, so the join stays pending. Rejects too where memory can't be shared,
24
+ * as without cross-origin isolation.
25
+ * @function
26
+ */
27
+ export declare const join: Join;
package/dist/world.js ADDED
@@ -0,0 +1,27 @@
1
+ // © 2026 Adobe. MIT License. See /LICENSE for details.
2
+ import { create as createWorld, join as joinWorld } from './implementation/index.js';
3
+ /**
4
+ * Creates a world on this thread, synchronously, with resources starting at the given values in place of their
5
+ * defaults. This thread is its `"world"` thread for the world's life: it runs the sync points, the ticks and the GPU
6
+ * device, and fills every logical thread no joined thread claims. Keys are the resources' symbols, built-in ones
7
+ * included (`{ [workers]: 0 }`); string keys are never a resource's, so they're ignored. Throws for a symbol that isn't
8
+ * a resource, or is one only the world writes. While a declaration's trait or default doesn't hold, each call fails to
9
+ * compile, once, naming each such key, its members and what's wrong with them.
10
+ * @function
11
+ */
12
+ export const create = createWorld;
13
+ /**
14
+ * Joins, from another thread, a world that `share` let join through the other end of `port`, claiming the logical
15
+ * `threads` this thread fills: their objects live here, and the systems and finalizers reading them run here. Resolves,
16
+ * once the world accepted the claim, to the same world: its `run` runs systems there, and its `add` and `remove` take
17
+ * readonly systems, which run here after the world's sync points. Add them before awaiting anything else, so the
18
+ * world's `share` resolves with them in place. Its `dispose` disconnects this thread. Rejects with an `Error` saying
19
+ * why it didn't join: the world refused the claim, was disposed, or its thread ended first, as the platform reports it:
20
+ * the world's `Worker` failing to start or throwing first, a port closed, as in node, or, in a browser, the world's
21
+ * thread ending once it was shared through a port this thread listened on. A browser world's worker that the page ended
22
+ * before this thread heard from it isn't reported, so the join stays pending. Rejects too where memory can't be shared,
23
+ * as without cross-origin isolation.
24
+ * @function
25
+ */
26
+ export const join = joinWorld;
27
+ //# sourceMappingURL=world.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"world.js","sourceRoot":"","sources":["../src/world.ts"],"names":[],"mappings":"AAAA,uDAAuD;AACvD,OAAO,EAAE,MAAM,IAAI,WAAW,EAAE,IAAI,IAAI,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAMrF;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,MAAM,GAAW,WAAW,CAAC;AAE1C;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,IAAI,GAAS,SAAS,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adobe/ecs",
3
- "version": "0.2.0",
3
+ "version": "0.2.3",
4
4
  "description": "Adobe ECS",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -50,7 +50,7 @@ export const world: World = World.create({ [workers]: 0 });
50
50
  // Another thread joins it through the other end of a channel the world shares.
51
51
  declare const port: MessagePort;
52
52
  export const joined: Promise<World> = World.join(port, { threads: ["main"] });
53
- export const read = System.create({ observer: true, components: { with: [position] }, lookup: { at: { with: [position] } } }, () => {});
53
+ export const read = System.create({ readonly: true, components: { with: [position] }, lookup: { at: { with: [position] } } }, () => {});
54
54
  export const added = world.add(read);
55
55
  `;
56
56
 
@@ -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
 
@@ -23,7 +23,7 @@ const award = System.create({ components: { with: [score] } }, (row, { resources
23
23
  row[score] += resources[bonus];
24
24
  });
25
25
 
26
- const read = System.create({ observer: true, components: { with: [score] } }, row => {
26
+ const read = System.create({ readonly: true, components: { with: [score] } }, row => {
27
27
  console.log("score", row[score]);
28
28
  });
29
29
 
@@ -17,6 +17,6 @@ that isn't data, such as an `AudioContext`, is stored as it is; a change inside
17
17
  written back. An `Entity` resource with no entity holds `Entity.none`, which is `0` rather than `null`, so it stays data
18
18
  every thread can read.
19
19
 
20
- A system reads resources by key. With `observer: true` it only reads, and runs after the world's changes apply.
20
+ A system reads resources by key. With `readonly: true` it only reads, and runs after the world's changes apply.
21
21
  [`run`](01-resources.ts#L31-L32) runs a system once, and resolves to what the body returns, for a system without
22
22
  components.
@@ -22,7 +22,7 @@ declare module "@adobe/ecs" {
22
22
  }
23
23
  }
24
24
 
25
- const read = System.create({ observer: true }, ({ resources }) =>
25
+ const read = System.create({ readonly: true }, ({ resources }) =>
26
26
  [resources[gravity], resources[floor], resources[names], resources[audio], resources[focused]]);
27
27
 
28
28
  export const main = async (): Promise<void> => {
@@ -25,10 +25,10 @@ export const defeat = System.create({ components: { with: [enemy] } }, (row, { c
25
25
  commands.remove(row.entity, enemy);
26
26
  });
27
27
 
28
- const readFoes = System.create({ observer: true, components: { with: [position, enemy] } }, row => {
28
+ const readFoes = System.create({ readonly: true, components: { with: [position, enemy] } }, row => {
29
29
  console.log("a foe at", row[position]);
30
30
  });
31
- const readParticles = System.create({ observer: true, components: { with: [...Particle] } }, row => {
31
+ const readParticles = System.create({ readonly: true, components: { with: [...Particle] } }, row => {
32
32
  console.log("a particle moving at", row[velocity]);
33
33
  });
34
34
 
@@ -8,8 +8,8 @@ it in parallel.
8
8
  component in `with` and none in `not`, so the `frozen` one stays put. Values are replaced whole, never edited in
9
9
  place.
10
10
  - **Systems without components**, such as [`count`](03-systems.ts#L35-L37), run once per run.
11
- - **Observer systems**, `observer: true`, only read. They see what the runs before them left. Run one to read the
12
- world, or add one to react to it.
11
+ - **Readonly systems**, `readonly: true`, only read. Run one to read the world, as [`read`](03-systems.ts#L40-L42)
12
+ does, or add one to react to it. [Sync points](06-sync-points.md) says when and where they run.
13
13
  - **Optional components**, `optional: [velocity]`, are in the row where the entity has them and `undefined` where it
14
14
  doesn't. They never narrow which entities a system runs on; [`readSpeeds`](03-systems.ts#L44-L47) still reads the
15
15
  entity without a velocity. Only commands add and remove them.
@@ -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.3/src/guide/03-systems.ts#fail) failed: broken
5
5
  frames 2
@@ -37,16 +37,16 @@ const count = System.create({}, ({ resources }) => {
37
37
  });
38
38
 
39
39
  const places: number[] = [];
40
- const read = System.create({ observer: true, components: { with: [position] } }, row => {
40
+ const read = System.create({ readonly: true, components: { with: [position] } }, row => {
41
41
  places.push(row[position][0]);
42
42
  });
43
43
 
44
44
  const speeds: string[] = [];
45
- const readSpeeds = System.create({ observer: true, components: { with: [position], optional: [velocity] } }, row => {
45
+ const readSpeeds = System.create({ readonly: true, components: { with: [position], optional: [velocity] } }, row => {
46
46
  speeds.push(row[velocity] === undefined ? "still" : `at ${row[velocity][0]}`);
47
47
  });
48
48
 
49
- const frameCount = System.create({ observer: true }, ({ resources }) => resources[frames]);
49
+ const frameCount = System.create({ readonly: true }, ({ resources }) => resources[frames]);
50
50
 
51
51
  const fail = System.create(() => {
52
52
  throw new Error("broken");
@@ -9,7 +9,7 @@ every tick from then on, and [`world.remove`](04-ticks.ts#L41) takes it off.
9
9
  `step` milliseconds, each with that `delta`.
10
10
  - A world ticks only after a run arrives, unless `always` is set, as for [the first world](04-ticks.ts#L36).
11
11
 
12
- An observer with `resources: { changed: [elapsed] }` runs after each tick that changed `elapsed`:
12
+ A readonly system with `resources: { changed: [elapsed] }` runs after each tick that changed `elapsed`:
13
13
  [`watch`](04-ticks.ts#L18-L21) resolves once 30 ms have passed. Once `advance` is removed, `elapsed` stays put.
14
14
 
15
15
  **Asking for a tick.** A system that needs another tick contributes a time to the `wake` reduction:
@@ -16,11 +16,11 @@ const advance = System.create({}, ({ resources }) => {
16
16
  });
17
17
 
18
18
  let reached: (elapsed: number) => void = () => {};
19
- const watch = System.create({ observer: true, resources: { changed: [elapsed] } }, ({ resources }) => {
19
+ const watch = System.create({ readonly: true, resources: { changed: [elapsed] } }, ({ resources }) => {
20
20
  if (resources[elapsed] >= 30) reached(resources[elapsed]);
21
21
  });
22
22
 
23
- const readTime = System.create({ observer: true }, ({ resources }) => ({ elapsed: resources[elapsed], delta: resources[clock].delta }));
23
+ const readTime = System.create({ readonly: true }, ({ resources }) => ({ elapsed: resources[elapsed], delta: resources[clock].delta }));
24
24
 
25
25
  const step = System.create({}, ({ resources, reduce }) => {
26
26
  resources[steps] += 1;
@@ -28,7 +28,7 @@ const step = System.create({}, ({ resources, reduce }) => {
28
28
  });
29
29
 
30
30
  let finished: (steps: number) => void = () => {};
31
- const watchSteps = System.create({ observer: true, resources: { changed: [steps] } }, ({ resources }) => {
31
+ const watchSteps = System.create({ readonly: true, resources: { changed: [steps] } }, ({ resources }) => {
32
32
  if (resources[steps] === 3) finished(resources[steps]);
33
33
  });
34
34
 
@@ -20,7 +20,7 @@ const heal = System.create({ components: { with: [health] }, after: [hit] }, row
20
20
  });
21
21
  const frame = System.create([heal, hit]);
22
22
 
23
- const read = System.create({ observer: true, components: { with: [health] } }, row => {
23
+ const read = System.create({ readonly: true, components: { with: [health] } }, row => {
24
24
  console.log("health", row[health]);
25
25
  });
26
26
 
@@ -10,5 +10,12 @@ A run ends at a *sync point*, where its commands apply.
10
10
  A composite is one run with one sync point. In [`frame`](06-sync-points.ts#L25), `count` runs `after: [spawn]` but
11
11
  before the spawned entities exist, so it counts none; run alone, after the sync point, it counts the hero.
12
12
 
13
- An observer added to a world runs after the sync point of every run and tick, reading what it left:
14
- [`watch`](06-sync-points.ts#L39-L41) logs the hero after the run that spawned it.
13
+ **Readonly systems.** `readonly: true` places a system after the sync point, so it reads what the commands did. Added
14
+ to a world, [`watch`](06-sync-points.ts#L27-L29) runs after the sync point of every run and tick, and logs the hero
15
+ after the run that spawned it; `count`, which isn't readonly, runs before the sync point, in its tier.
16
+
17
+ - **Where:** a readonly system runs on the thread that added or ran it, so a [joined thread](19-threads.md) adds only
18
+ readonly systems.
19
+ - **Order:** none. It can't declare `before`, `after` or `during`.
20
+ - **Limits:** it can't write, use `commands` or `reduce`, or take input. Its row and resources are read-only, so each
21
+ is a type error.
@@ -24,10 +24,10 @@ const count = System.create({ components: { with: [health] }, after: [spawn] },
24
24
  });
25
25
  const frame = System.create([spawn, count]);
26
26
 
27
- const watch = System.create({ observer: true, components: { with: [health] } }, row => {
27
+ const watch = System.create({ readonly: true, components: { with: [health] } }, row => {
28
28
  console.log("health", row[health]);
29
29
  });
30
- const readSeen = System.create({ observer: true }, ({ resources }) => resources[seen]);
30
+ const readSeen = System.create({ readonly: true }, ({ resources }) => resources[seen]);
31
31
 
32
32
  export const main = async (): Promise<void> => {
33
33
  await using world = World.create();
@@ -36,7 +36,7 @@ const bump = System.create({ components: { with: [score, boss] } }, row => {
36
36
  });
37
37
 
38
38
  let loaded = 0;
39
- const countLoaded = System.create({ observer: true, components: { with: [shown] } }, () => {
39
+ const countLoaded = System.create({ readonly: true, components: { with: [shown] } }, () => {
40
40
  loaded += 1;
41
41
  });
42
42
 
@@ -45,7 +45,7 @@ const step = System.create({ resources: { is: [map], not: [paused] } }, ({ resou
45
45
  });
46
46
  const pause = System.create({}, ({ resources }) => { resources[paused] = true; });
47
47
  const open = System.create({}, ({ resources }) => { resources[map] = { width: 4 }; });
48
- const readSteps = System.create({ observer: true }, ({ resources }) => resources[steps]);
48
+ const readSteps = System.create({ readonly: true }, ({ resources }) => resources[steps]);
49
49
 
50
50
  export const main = async (): Promise<void> => {
51
51
  await using world = World.create();
@@ -30,7 +30,7 @@ const heal = System.create({
30
30
  });
31
31
 
32
32
  const healths: number[] = [];
33
- const read = System.create({ observer: true, components: { with: [health] } }, row => {
33
+ const read = System.create({ readonly: true, components: { with: [health] } }, row => {
34
34
  healths.push(row[health]);
35
35
  });
36
36
 
@@ -31,7 +31,7 @@ const hide = System.create({ components: { with: [sprite] } }, (row, { commands
31
31
  });
32
32
 
33
33
  const drawing: string[] = [];
34
- const read = System.create({ observer: true, components: { with: [layer, drawn], optional: [mesh, sprite] } }, row => {
34
+ const read = System.create({ readonly: true, components: { with: [layer, drawn], optional: [mesh, sprite] } }, row => {
35
35
  drawing.push(`${row[mesh] === undefined ? `sprite ${row[sprite]}` : `mesh ${row[mesh]}`} on layer ${row[layer]}`);
36
36
  });
37
37
  /** Logs what's drawn, in order. */
@@ -33,9 +33,9 @@ const destroy = System.create({ components: { with: [ship] } }, (row, { commands
33
33
  });
34
34
 
35
35
  const counts = { outlined: 0, targeting: 0, entities: 0 };
36
- const countOutlined = System.create({ observer: true, components: { with: [outline] } }, () => { counts.outlined += 1; });
37
- const countTargeting = System.create({ observer: true, components: { with: [target] } }, () => { counts.targeting += 1; });
38
- const countAll = System.create({ observer: true, components: {} }, () => { counts.entities += 1; });
36
+ const countOutlined = System.create({ readonly: true, components: { with: [outline] } }, () => { counts.outlined += 1; });
37
+ const countTargeting = System.create({ readonly: true, components: { with: [target] } }, () => { counts.targeting += 1; });
38
+ const countAll = System.create({ readonly: true, components: {} }, () => { counts.entities += 1; });
39
39
  const count = System.create([countOutlined, countTargeting, countAll]);
40
40
 
41
41
  export const main = async (): Promise<void> => {