@adobe/data 0.9.59 → 0.9.61

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 (173) hide show
  1. package/dist/ecs/database/combine-plugins.d.ts +5 -1
  2. package/dist/ecs/database/combine-plugins.js +3 -3
  3. package/dist/ecs/database/combine-plugins.js.map +1 -1
  4. package/dist/ecs/database/create-plugin.d.ts +17 -14
  5. package/dist/ecs/database/create-plugin.js +2 -1
  6. package/dist/ecs/database/create-plugin.js.map +1 -1
  7. package/dist/ecs/database/database.d.ts +32 -4
  8. package/dist/ecs/database/database.index.performance.test.d.ts +1 -0
  9. package/dist/ecs/database/database.index.performance.test.js +417 -0
  10. package/dist/ecs/database/database.index.performance.test.js.map +1 -0
  11. package/dist/ecs/database/database.index.test.d.ts +1 -0
  12. package/dist/ecs/database/database.index.test.js +762 -0
  13. package/dist/ecs/database/database.index.test.js.map +1 -0
  14. package/dist/ecs/database/database.index.type-test.d.ts +1 -0
  15. package/dist/ecs/database/database.index.type-test.js +275 -0
  16. package/dist/ecs/database/database.index.type-test.js.map +1 -0
  17. package/dist/ecs/database/database.js.map +1 -1
  18. package/dist/ecs/database/deep-extends-chain.type-test.d.ts +256 -256
  19. package/dist/ecs/database/index-api-proof.type-test.d.ts +1 -0
  20. package/dist/ecs/database/index-api-proof.type-test.js +88 -0
  21. package/dist/ecs/database/index-api-proof.type-test.js.map +1 -0
  22. package/dist/ecs/database/index-registry/create-index-registry.d.ts +53 -0
  23. package/dist/ecs/database/index-registry/create-index-registry.js +174 -0
  24. package/dist/ecs/database/index-registry/create-index-registry.js.map +1 -0
  25. package/dist/ecs/database/index-registry/create-index.d.ts +60 -0
  26. package/dist/ecs/database/index-registry/create-index.js +539 -0
  27. package/dist/ecs/database/index-registry/create-index.js.map +1 -0
  28. package/dist/ecs/database/index-registry/index.d.ts +4 -0
  29. package/dist/ecs/database/index-registry/index.js +4 -0
  30. package/dist/ecs/database/index-registry/index.js.map +1 -0
  31. package/dist/ecs/database/public/create-database.js +146 -0
  32. package/dist/ecs/database/public/create-database.js.map +1 -1
  33. package/dist/ecs/database/transactional-store/transactional-store.d.ts +3 -2
  34. package/dist/ecs/plugins/scheduler/scheduler.d.ts +1 -0
  35. package/dist/ecs/store/index-types.d.ts +132 -0
  36. package/dist/ecs/store/index-types.js +3 -0
  37. package/dist/ecs/store/index-types.js.map +1 -0
  38. package/dist/ecs/store/public/create-store.js +189 -3
  39. package/dist/ecs/store/public/create-store.js.map +1 -1
  40. package/dist/ecs/store/store.d.ts +30 -13
  41. package/dist/ecs/store/store.js +5 -4
  42. package/dist/ecs/store/store.js.map +1 -1
  43. package/dist/ecs/store/transaction-functions.d.ts +4 -3
  44. package/dist/equals.js +24 -6
  45. package/dist/equals.js.map +1 -1
  46. package/dist/math/aabb/face/from-position.d.ts +1 -1
  47. package/dist/math/aabb/face/from-position.js +4 -3
  48. package/dist/math/aabb/face/from-position.js.map +1 -1
  49. package/dist/math/aabb/schema.js +1 -1
  50. package/dist/math/aabb/schema.js.map +1 -1
  51. package/dist/schema/fractional-index/between-n.d.ts +2 -0
  52. package/dist/schema/fractional-index/between-n.js +34 -0
  53. package/dist/schema/fractional-index/between-n.js.map +1 -0
  54. package/dist/schema/fractional-index/between-n.test.d.ts +1 -0
  55. package/dist/schema/fractional-index/between-n.test.js +36 -0
  56. package/dist/schema/fractional-index/between-n.test.js.map +1 -0
  57. package/dist/schema/fractional-index/between.d.ts +2 -0
  58. package/dist/schema/fractional-index/between.js +3 -0
  59. package/dist/schema/fractional-index/between.js.map +1 -0
  60. package/dist/schema/fractional-index/between.test.d.ts +1 -0
  61. package/dist/schema/fractional-index/between.test.js +68 -0
  62. package/dist/schema/fractional-index/between.test.js.map +1 -0
  63. package/dist/schema/fractional-index/decrement-integer.d.ts +1 -0
  64. package/dist/schema/fractional-index/decrement-integer.js +32 -0
  65. package/dist/schema/fractional-index/decrement-integer.js.map +1 -0
  66. package/dist/schema/fractional-index/decrement-integer.test.d.ts +1 -0
  67. package/dist/schema/fractional-index/decrement-integer.test.js +31 -0
  68. package/dist/schema/fractional-index/decrement-integer.test.js.map +1 -0
  69. package/dist/schema/fractional-index/digits.d.ts +1 -0
  70. package/dist/schema/fractional-index/digits.js +3 -0
  71. package/dist/schema/fractional-index/digits.js.map +1 -0
  72. package/dist/schema/fractional-index/fractional-index.d.ts +4 -0
  73. package/dist/schema/fractional-index/fractional-index.js +2 -0
  74. package/dist/schema/fractional-index/fractional-index.js.map +1 -0
  75. package/dist/schema/fractional-index/increment-integer.d.ts +1 -0
  76. package/dist/schema/fractional-index/increment-integer.js +32 -0
  77. package/dist/schema/fractional-index/increment-integer.js.map +1 -0
  78. package/dist/schema/fractional-index/increment-integer.test.d.ts +1 -0
  79. package/dist/schema/fractional-index/increment-integer.test.js +34 -0
  80. package/dist/schema/fractional-index/increment-integer.test.js.map +1 -0
  81. package/dist/schema/fractional-index/initial.d.ts +2 -0
  82. package/dist/schema/fractional-index/initial.js +2 -0
  83. package/dist/schema/fractional-index/initial.js.map +1 -0
  84. package/dist/schema/fractional-index/integer-length.d.ts +1 -0
  85. package/dist/schema/fractional-index/integer-length.js +9 -0
  86. package/dist/schema/fractional-index/integer-length.js.map +1 -0
  87. package/dist/schema/fractional-index/integer-part.d.ts +1 -0
  88. package/dist/schema/fractional-index/integer-part.js +9 -0
  89. package/dist/schema/fractional-index/integer-part.js.map +1 -0
  90. package/dist/schema/fractional-index/key-between.d.ts +2 -0
  91. package/dist/schema/fractional-index/key-between.js +39 -0
  92. package/dist/schema/fractional-index/key-between.js.map +1 -0
  93. package/dist/schema/fractional-index/midpoint.d.ts +1 -0
  94. package/dist/schema/fractional-index/midpoint.js +23 -0
  95. package/dist/schema/fractional-index/midpoint.js.map +1 -0
  96. package/dist/schema/fractional-index/midpoint.test.d.ts +1 -0
  97. package/dist/schema/fractional-index/midpoint.test.js +83 -0
  98. package/dist/schema/fractional-index/midpoint.test.js.map +1 -0
  99. package/dist/schema/fractional-index/public.d.ts +4 -0
  100. package/dist/schema/fractional-index/public.js +6 -0
  101. package/dist/schema/fractional-index/public.js.map +1 -0
  102. package/dist/schema/fractional-index/schema.d.ts +4 -0
  103. package/dist/schema/fractional-index/schema.js +5 -0
  104. package/dist/schema/fractional-index/schema.js.map +1 -0
  105. package/dist/schema/fractional-index/smallest-integer.d.ts +1 -0
  106. package/dist/schema/fractional-index/smallest-integer.js +3 -0
  107. package/dist/schema/fractional-index/smallest-integer.js.map +1 -0
  108. package/dist/schema/fractional-index/validate-integer.d.ts +1 -0
  109. package/dist/schema/fractional-index/validate-integer.js +7 -0
  110. package/dist/schema/fractional-index/validate-integer.js.map +1 -0
  111. package/dist/schema/fractional-index/validate-order-key.d.ts +1 -0
  112. package/dist/schema/fractional-index/validate-order-key.js +11 -0
  113. package/dist/schema/fractional-index/validate-order-key.js.map +1 -0
  114. package/dist/schema/guid/create.d.ts +2 -0
  115. package/dist/schema/guid/create.js +10 -0
  116. package/dist/schema/guid/create.js.map +1 -0
  117. package/dist/schema/guid/create.test.d.ts +1 -0
  118. package/dist/schema/guid/create.test.js +39 -0
  119. package/dist/schema/guid/create.test.js.map +1 -0
  120. package/dist/schema/guid/equals.d.ts +2 -0
  121. package/dist/schema/guid/equals.js +3 -0
  122. package/dist/schema/guid/equals.js.map +1 -0
  123. package/dist/schema/guid/equals.test.d.ts +1 -0
  124. package/dist/schema/guid/equals.test.js +30 -0
  125. package/dist/schema/guid/equals.test.js.map +1 -0
  126. package/dist/schema/guid/from-uuid.d.ts +2 -0
  127. package/dist/schema/guid/from-uuid.js +15 -0
  128. package/dist/schema/guid/from-uuid.js.map +1 -0
  129. package/dist/schema/guid/from-uuid.test.d.ts +1 -0
  130. package/dist/schema/guid/from-uuid.test.js +39 -0
  131. package/dist/schema/guid/from-uuid.test.js.map +1 -0
  132. package/dist/schema/guid/guid.performance.test.d.ts +1 -0
  133. package/dist/schema/guid/guid.performance.test.js +297 -0
  134. package/dist/schema/guid/guid.performance.test.js.map +1 -0
  135. package/dist/schema/guid/index.d.ts +4 -0
  136. package/dist/schema/guid/index.js +3 -0
  137. package/dist/schema/guid/index.js.map +1 -0
  138. package/dist/schema/guid/layout.d.ts +1 -0
  139. package/dist/schema/guid/layout.js +5 -0
  140. package/dist/schema/guid/layout.js.map +1 -0
  141. package/dist/schema/guid/nil.d.ts +2 -0
  142. package/dist/schema/guid/nil.js +3 -0
  143. package/dist/schema/guid/nil.js.map +1 -0
  144. package/dist/schema/guid/public.d.ts +8 -0
  145. package/dist/schema/guid/public.js +10 -0
  146. package/dist/schema/guid/public.js.map +1 -0
  147. package/dist/schema/guid/schema.d.ts +12 -0
  148. package/dist/schema/guid/schema.js +10 -0
  149. package/dist/schema/guid/schema.js.map +1 -0
  150. package/dist/schema/guid/schema.test.d.ts +1 -0
  151. package/dist/schema/guid/schema.test.js +35 -0
  152. package/dist/schema/guid/schema.test.js.map +1 -0
  153. package/dist/schema/guid/to-unserializable-key.d.ts +2 -0
  154. package/dist/schema/guid/to-unserializable-key.js +10 -0
  155. package/dist/schema/guid/to-unserializable-key.js.map +1 -0
  156. package/dist/schema/guid/to-uuid.d.ts +2 -0
  157. package/dist/schema/guid/to-uuid.js +15 -0
  158. package/dist/schema/guid/to-uuid.js.map +1 -0
  159. package/dist/schema/guid/to-uuid.test.d.ts +1 -0
  160. package/dist/schema/guid/to-uuid.test.js +31 -0
  161. package/dist/schema/guid/to-uuid.test.js.map +1 -0
  162. package/dist/schema/index.d.ts +2 -0
  163. package/dist/schema/index.js +2 -0
  164. package/dist/schema/index.js.map +1 -1
  165. package/dist/table/select-rows.d.ts +7 -4
  166. package/dist/table/select-rows.js.map +1 -1
  167. package/dist/tsconfig.tsbuildinfo +1 -1
  168. package/package.json +1 -1
  169. package/references/data-lit/package.json +1 -1
  170. package/references/data-lit-tictactoe/package.json +1 -1
  171. package/references/data-react/package.json +1 -1
  172. package/references/data-react-hello/package.json +1 -1
  173. package/references/data-react-pixie/package.json +1 -1
@@ -3,6 +3,7 @@ import { Store } from "../../store/index.js";
3
3
  import { createReconcilingDatabase } from "../reconciling/create-reconciling-database.js";
4
4
  import { calculateSystemOrder } from "../calculate-system-order.js";
5
5
  import { createTransactionDispatcher } from "./create-transaction-dispatcher.js";
6
+ import { observeSelectEntities } from "../observe-select-entities.js";
6
7
  /**
7
8
  * For each system in newDeclarations that is not yet in systemFunctions: call create(db),
8
9
  * store the returned value in systemFunctions, and assign by name. Uses natural declaration order.
@@ -63,6 +64,12 @@ function createEmptyDatabase(sync) {
63
64
  const services = {};
64
65
  const computed = {};
65
66
  const extendedPlugins = new Set();
67
+ // The Store layer owns the IndexRegistry: it instantiates the registry,
68
+ // maintains it eagerly on every insert/update/delete, and exposes the
69
+ // typed handle map as `store.indexes`. The Database layer just surfaces
70
+ // the same reference (via spread / explicit field below) so users see
71
+ // `db.indexes.<name>` and `t.indexes.<name>` pointing at one source of
72
+ // truth.
66
73
  const partialDatabase = {
67
74
  serviceName: "ecs-database-service",
68
75
  ...reconcilingDatabase,
@@ -71,10 +78,39 @@ function createEmptyDatabase(sync) {
71
78
  actions,
72
79
  services,
73
80
  computed,
81
+ indexes: store.indexes,
74
82
  store,
75
83
  system: { functions: systemFunctions, order: systemOrder },
76
84
  extend: undefined,
77
85
  };
86
+ // Auto-route `db.select(include, { where })` and `db.observe.select(...)`
87
+ // through a declared index when the where clause is exactly an equality
88
+ // match on the index's full key tuple and there is no order clause. Other
89
+ // shapes fall back to the archetype scan in the underlying store.
90
+ //
91
+ // The router dispatches through the user-visible handles in `store.indexes`
92
+ // so any override applied to the public handle — tests, instrumentation —
93
+ // takes effect for the routed path too.
94
+ const baseSelect = partialDatabase.select.bind(partialDatabase);
95
+ const indexAwareSelect = (include, options) => {
96
+ const routed = trySelectViaIndex(store, include, options);
97
+ if (routed !== null)
98
+ return routed;
99
+ return baseSelect(include, options);
100
+ };
101
+ partialDatabase.select = indexAwareSelect;
102
+ // `observeSelectEntities` reads from its `store` parameter via only
103
+ // `store.select` and `store.locate`. A minimal façade backed by the
104
+ // index-aware select + the real `store.locate` satisfies that contract,
105
+ // and replacing `observe.select` here mutates the shared `observe` object
106
+ // (same reference as `observedDatabase.observe`) before any user code
107
+ // can subscribe — so the default instance created inside
108
+ // `createObservedDatabase` is cleanly dropped, with no stale closures.
109
+ const indexAwareStoreFacade = {
110
+ select: indexAwareSelect,
111
+ locate: store.locate.bind(store),
112
+ };
113
+ partialDatabase.observe.select = observeSelectEntities(indexAwareStoreFacade, partialDatabase.observe.transactions);
78
114
  const extend = (plugin) => {
79
115
  if (!extendedPlugins.has(plugin)) {
80
116
  extendedPlugins.add(plugin);
@@ -93,6 +129,12 @@ function createEmptyDatabase(sync) {
93
129
  if (!(name in computed))
94
130
  computed[name] = pluginComputed[name](partialDatabase);
95
131
  }
132
+ // `reconcilingDatabase.extend(plugin)` above propagates down to
133
+ // `store.extend({ components, resources, archetypes, indexes })`,
134
+ // so the Store has already absorbed `plugin.indexes`. We refresh
135
+ // our local indexes reference in case the underlying map got a
136
+ // new identity (it doesn't today, but stay defensive).
137
+ partialDatabase.indexes = store.indexes;
96
138
  if (plugin.systems && Object.keys(plugin.systems).length > 0) {
97
139
  Object.assign(allSystemDeclarations, plugin.systems);
98
140
  systemOrder = calculateSystemOrder(allSystemDeclarations);
@@ -106,4 +148,108 @@ function createEmptyDatabase(sync) {
106
148
  partialDatabase.extend = extend;
107
149
  return partialDatabase;
108
150
  }
151
+ /**
152
+ * Returns the equality value implied by `where[key]` if and only if the
153
+ * condition is a pure equality — either a direct primitive value or a
154
+ * comparison object with exactly `{ "==": v }`. Returns the sentinel
155
+ * `NOT_EQUALITY` when the condition uses any other operator.
156
+ */
157
+ const NOT_EQUALITY = Symbol("not-equality");
158
+ const equalityValue = (cond) => {
159
+ if (cond === null || typeof cond !== "object")
160
+ return cond;
161
+ const keys = Object.keys(cond);
162
+ if (keys.length === 1 && keys[0] === "==")
163
+ return cond["=="];
164
+ return NOT_EQUALITY;
165
+ };
166
+ /**
167
+ * Attempts to serve `select(include, options)` from a declared index.
168
+ *
169
+ * Returns `null` when no index applies; the caller must fall back to the
170
+ * archetype scan. Returns an `Entity[]` when an index can answer the query.
171
+ *
172
+ * Match conditions (intentionally conservative for V2):
173
+ * - `options.where` is non-empty.
174
+ * - `options.order` is absent (sort orders are routed in a follow-up).
175
+ * - Every `where` key is a pure equality (`v` or `{ "==": v }`).
176
+ * - The `where` keys, as a set, equal some index's `components`.
177
+ *
178
+ * The query planner accesses `store.indexes` (the user-visible handle map),
179
+ * so test spies and any future user-installed instrumentation see the call.
180
+ * Components for each index are recovered from the registry-internal handle
181
+ * (the handle exposes `find`/`findRange`/`get`; the column set is held by
182
+ * the underlying `RuntimeIndex`, which is the registry's source of truth).
183
+ * For the planner we treat the handle map structurally as
184
+ * `{ [name]: { findByValues, routableColumns } }` because Store internally
185
+ * augments each handle with these fields — see `createStore`.
186
+ *
187
+ * After the index lookup returns candidate entities, each candidate is
188
+ * checked for archetype membership of every `include` component so the
189
+ * returned set respects the same archetype filter as the scan path.
190
+ */
191
+ function trySelectViaIndex(store, include, options) {
192
+ const where = options?.where;
193
+ if (!where || options?.order)
194
+ return null;
195
+ const whereKeys = Object.keys(where);
196
+ if (whereKeys.length === 0)
197
+ return null;
198
+ // Collapse where to an { component -> equality-value } record, bailing if
199
+ // any condition is not a pure equality.
200
+ const values = {};
201
+ for (const k of whereKeys) {
202
+ const eq = equalityValue(where[k]);
203
+ if (eq === NOT_EQUALITY)
204
+ return null;
205
+ values[k] = eq;
206
+ }
207
+ // Iterate the public handle map (store.indexes). Each handle carries a
208
+ // non-public `routableColumns` field placed by `createStore` for exactly
209
+ // this purpose. `routableColumns: null` opts an index out of raw-where
210
+ // auto-routing (function and slot-map keys live in a value space the
211
+ // planner cannot infer from a where clause).
212
+ //
213
+ // For a matched index the planner builds the appropriate `find`
214
+ // argument: a scalar for a single-column key, the full values object
215
+ // for a multi-column key. The call goes through `handle.find` so any
216
+ // user-installed spy or instrumentation on the handle sees the
217
+ // dispatch.
218
+ const handles = store.indexes;
219
+ let matchedHandle;
220
+ let matchedCols;
221
+ for (const name of Object.keys(handles)) {
222
+ const handle = handles[name];
223
+ const cols = handle.routableColumns;
224
+ if (cols === null)
225
+ continue;
226
+ if (cols.length !== whereKeys.length)
227
+ continue;
228
+ if (cols.every(c => c in values)) {
229
+ matchedHandle = handle;
230
+ matchedCols = cols;
231
+ break;
232
+ }
233
+ }
234
+ if (!matchedHandle || !matchedCols)
235
+ return null;
236
+ const findArg = matchedCols.length === 1 ? values[matchedCols[0]] : values;
237
+ const candidates = matchedHandle.find(findArg);
238
+ if (candidates.length === 0)
239
+ return [];
240
+ const includeArr = Array.from(include);
241
+ if (includeArr.length === 0)
242
+ return candidates.slice();
243
+ const result = [];
244
+ for (const entity of candidates) {
245
+ const location = store.locate(entity);
246
+ if (!location)
247
+ continue;
248
+ const cols = location.archetype.columns;
249
+ if (includeArr.every(c => cols[c] !== undefined)) {
250
+ result.push(entity);
251
+ }
252
+ }
253
+ return result;
254
+ }
109
255
  //# sourceMappingURL=create-database.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"create-database.js","sourceRoot":"","sources":["../../../../src/ecs/database/public/create-database.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"create-database.js","sourceRoot":"","sources":["../../../../src/ecs/database/public/create-database.ts"],"names":[],"mappings":"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"}
@@ -8,6 +8,7 @@ import { StringKeyof } from "../../../types/types.js";
8
8
  import { ArchetypeComponents } from "../../store/archetype-components.js";
9
9
  import { FromSchemas } from "../../../schema/from-schemas.js";
10
10
  import { Undoable } from "../undoable.js";
11
+ import { IndexDeclarations } from "../../store/index-types.js";
11
12
  /**
12
13
  * The first argument passed to every transaction function. Extends the full
13
14
  * store read/write surface with `userId` — the identifier of the peer or user
@@ -17,10 +18,10 @@ import { Undoable } from "../undoable.js";
17
18
  *
18
19
  * `userId` is `undefined` in local-only (no-sync) databases.
19
20
  */
20
- export type TransactionContext<C extends Components, R extends ResourceComponents, A extends ArchetypeComponents<StringKeyof<C>>> = Store<C, R, A> & {
21
+ export type TransactionContext<C extends Components, R extends ResourceComponents, A extends ArchetypeComponents<StringKeyof<C>>, IX extends IndexDeclarations<C> = {}> = Store<C, R, A, IX> & {
21
22
  readonly userId: number | string | undefined;
22
23
  };
23
- export interface TransactionalStore<C extends Components = never, R extends ResourceComponents = never, A extends ArchetypeComponents<StringKeyof<C>> = never> extends ReadonlyStore<C, R, A> {
24
+ export interface TransactionalStore<C extends Components = never, R extends ResourceComponents = never, A extends ArchetypeComponents<StringKeyof<C>> = never, IX extends IndexDeclarations<C> = {}> extends ReadonlyStore<C, R, A, IX> {
24
25
  /**
25
26
  * Execute a transaction on the store.
26
27
  * The transactionFunction must NOT directly mutate archetype rows as those changes would not be captured.
@@ -12,4 +12,5 @@ export declare const scheduler: {
12
12
  readonly actions: {};
13
13
  readonly services: {};
14
14
  readonly computed: {};
15
+ readonly indexes: {};
15
16
  };
@@ -0,0 +1,132 @@
1
+ import { StringKeyof } from "../../types/types.js";
2
+ import type { Entity } from "../entity/entity.js";
3
+ import { Components } from "./components.js";
4
+ /**
5
+ * If `T` is an array type, the element type; otherwise `T` unchanged.
6
+ *
7
+ * Multi-value indexes auto-fan-out array values into per-element bucket
8
+ * entries at insert time. The lookup methods therefore take the element
9
+ * type, not the array — a query like `find("joe")` against a
10
+ * `Task { assigned: string[] }` is exactly what users want, and an array
11
+ * as a single opaque key is virtually never useful (order-dependent, not
12
+ * a natural query shape).
13
+ */
14
+ type ElementOf<T> = T extends readonly (infer E)[] ? E : T;
15
+ /**
16
+ * Per-slot extractor in a compound `key` declaration. Either:
17
+ * - a `StringKeyof<C>` — read the value of that column directly, or
18
+ * - a function — derive the slot's value from the index's read components.
19
+ */
20
+ export type IndexKeySlot<C extends Components> = StringKeyof<C> | ((...args: any[]) => unknown);
21
+ /**
22
+ * The four shapes a `key` declaration can take. Drives the `find` / `get`
23
+ * argument type via {@link FindArg}.
24
+ *
25
+ * - `string` — read this one column. `find` takes a scalar.
26
+ * - `readonly string[]` — read each of these columns. `find` takes
27
+ * `{ col1: ..., col2: ... }` keyed by the column names themselves.
28
+ * - `(...args) => Value` — derive the bucket key from the components.
29
+ * `find` takes whatever the function returns (or its element type
30
+ * when the return is an array — multi-value fan-out).
31
+ * - slot map — name each part of a compound key; values are either
32
+ * column-name strings (identity) or extractor functions. `find`
33
+ * takes an object keyed by the slot names.
34
+ */
35
+ export type IndexKey<C extends Components> = StringKeyof<C> | readonly StringKeyof<C>[] | ((...args: any[]) => unknown) | {
36
+ readonly [slot: string]: IndexKeySlot<C>;
37
+ };
38
+ /**
39
+ * Within-bucket ordering. `by` declares the columns to read into the
40
+ * per-entity sort cache; `compare` (optional) is the comparator over
41
+ * `Pick<C, by[number]>`. When `compare` is omitted, the default is
42
+ * ascending across `by` from left to right with positional tie-break
43
+ * (`JS <` / `>` per column).
44
+ *
45
+ * `compare` is method shorthand for the same `IX & XIX` variance reason
46
+ * that `compute` was previously: `Store.extend`'s result intersects index
47
+ * declarations, which would otherwise trip `strictFunctionTypes` over
48
+ * contravariant parameter positions.
49
+ */
50
+ export type IndexOrder<C extends Components, By extends readonly StringKeyof<C>[] = readonly StringKeyof<C>[]> = {
51
+ readonly by: By;
52
+ compare?(a: Pick<C, By[number]>, b: Pick<C, By[number]>): number;
53
+ };
54
+ /**
55
+ * Type-level declaration of an index.
56
+ *
57
+ * - `key` is the only required field. Its shape drives the bucket layout
58
+ * and the lookup argument type of `find` / `get` / `findRange`.
59
+ * - `order` (optional) maintains sorted iteration within each bucket.
60
+ * - `unique` (optional) — when true, at most one entity may hold each
61
+ * bucket key; `get(arg)` is exposed and returns `Entity | null`.
62
+ * - `components` (optional) — names the columns extractor functions
63
+ * read. Only needed when at least one function reads a column not
64
+ * implied by a string-identity key entry.
65
+ *
66
+ * See `packages/data/src/ecs/README.md` for the full pattern catalogue.
67
+ */
68
+ export type Index<C extends Components = any, K extends IndexKey<C> = IndexKey<C>, O extends IndexOrder<C, any> | undefined = IndexOrder<C, any> | undefined, U extends boolean = boolean> = {
69
+ readonly key: K;
70
+ readonly order?: O;
71
+ readonly unique?: U;
72
+ readonly components?: readonly StringKeyof<C>[];
73
+ };
74
+ /**
75
+ * Resolve a slot's contributed `find` field type. Strings are identity
76
+ * reads on the named column; functions yield their return type (element
77
+ * type if the return is an array).
78
+ */
79
+ type SlotFindType<C extends Components, V> = V extends StringKeyof<C> ? ElementOf<C[V]> : V extends (...args: any[]) => infer R ? ElementOf<R> : never;
80
+ /** Argument type of `find` / `get` derived from the `key` shape. */
81
+ type FindArg<C extends Components, K> = K extends StringKeyof<C> ? ElementOf<C[K]> : K extends readonly StringKeyof<C>[] ? {
82
+ readonly [P in K[number]]: ElementOf<C[P]>;
83
+ } : K extends (...args: any[]) => infer R ? ElementOf<R> : K extends Record<string, IndexKeySlot<C>> ? {
84
+ readonly [Slot in keyof K]: SlotFindType<C, K[Slot]>;
85
+ } : never;
86
+ /** Comparison-operator filter on a scalar bucket-key value. */
87
+ type OperatorFilter<T> = {
88
+ readonly "=="?: T;
89
+ readonly "!="?: T;
90
+ readonly "<"?: T;
91
+ readonly "<="?: T;
92
+ readonly ">"?: T;
93
+ readonly ">="?: T;
94
+ };
95
+ /**
96
+ * `findRange` argument: each field of the find argument may be either an
97
+ * equality value or a `<,<=,>,>=,==,!=` operator filter. For scalar keys
98
+ * the entire argument may itself be an operator filter.
99
+ */
100
+ type RangeArg<T> = T extends object ? {
101
+ readonly [P in keyof T]?: T[P] | OperatorFilter<T[P]>;
102
+ } : T | OperatorFilter<T>;
103
+ export declare namespace Index {
104
+ /** Public lookup handle exposed on `db.indexes.<name>` and `t.indexes.<name>`. */
105
+ type Handle<C extends Components, I extends Index<C, any, any, any>> = I extends Index<C, infer K, any, infer U> ? {
106
+ find(arg: FindArg<C, K>): readonly Entity[];
107
+ findRange(arg: RangeArg<FindArg<C, K>>): readonly Entity[];
108
+ } & (U extends true ? {
109
+ get(arg: FindArg<C, K>): Entity | null;
110
+ } : {}) : never;
111
+ }
112
+ /**
113
+ * Plugin-level / store-level map of indexes. Keys are user-chosen index
114
+ * names; values are `Index` declarations whose `key` references real
115
+ * columns of `C`.
116
+ *
117
+ * The structural shape is inlined (rather than `Index<C, any, any, any>`) so
118
+ * that the per-entry `key`/`order` fields are actually constrained at the
119
+ * declaration site. With `Index<C, any, any, any>` the wildcard `any`
120
+ * generics widen `key` past `IndexKey<C>`, so a typo like `{ key: "bogus" }`
121
+ * silently passes the constraint. Inlining `key: IndexKey<C>` here makes
122
+ * the typo a type error at the plugin descriptor.
123
+ */
124
+ export type IndexDeclarations<C extends Components = any> = {
125
+ readonly [name: string]: {
126
+ readonly key: IndexKey<C>;
127
+ readonly order?: IndexOrder<C>;
128
+ readonly unique?: boolean;
129
+ readonly components?: readonly StringKeyof<C>[];
130
+ };
131
+ };
132
+ export {};
@@ -0,0 +1,3 @@
1
+ // © 2026 Adobe. MIT License. See /LICENSE for details.
2
+ export {};
3
+ //# sourceMappingURL=index-types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index-types.js","sourceRoot":"","sources":["../../../src/ecs/store/index-types.ts"],"names":[],"mappings":"AAAA,uDAAuD"}
@@ -1,6 +1,7 @@
1
1
  // © 2026 Adobe. MIT License. See /LICENSE for details.
2
2
  import { createCore } from "../core/create-core.js";
3
3
  import { selectEntities } from "../core/select-entities.js";
4
+ import { createIndexRegistry, } from "../../database/index-registry/index.js";
4
5
  export function createStore(schema) {
5
6
  const schemaArg = schema;
6
7
  const hasSchemaShape = schemaArg &&
@@ -21,6 +22,96 @@ export function createStore(schema) {
21
22
  const archetypeComponentNames = {};
22
23
  const componentAndResourceSchemas = {};
23
24
  const core = createCore(componentAndResourceSchemas);
25
+ // Index registry. Owned at the Store layer because index state is
26
+ // *derived* from store data, and every mutation that needs to keep
27
+ // indexes in sync flows through Store methods (insert / update /
28
+ // delete). Higher layers (`db.indexes`, `t.indexes`) expose this same
29
+ // map by reference.
30
+ //
31
+ // The reader is only used for `applyUpdate` — the patch passed to
32
+ // `store.update` may omit components an index covers, so the registry
33
+ // re-reads the full record to recompute the key. Seeding goes through
34
+ // `seedIndexFromArchetypes` instead (see below).
35
+ const indexRegistry = createIndexRegistry((entity) => {
36
+ const values = core.read(entity);
37
+ if (!values)
38
+ return null;
39
+ // Strip `id` — it is never a useful index key.
40
+ const { id: _id, ...rest } = values;
41
+ return rest;
42
+ });
43
+ /**
44
+ * Populate an index from the archetypes whose component set is a
45
+ * superset of the index's `components`. Only those archetypes can
46
+ * possibly contribute — entities in any other archetype lack at
47
+ * least one of the indexed components, so they would never match
48
+ * `find` / `findRange` / `get` anyway.
49
+ *
50
+ * For each candidate archetype, the values record is built directly
51
+ * from the dense column buffers (no `store.read` / `locate`
52
+ * indirection) and handed to `idx.add` once per row.
53
+ */
54
+ const seedIndexFromArchetypes = (idx) => {
55
+ // `idx.readColumns` already includes both bucket-key columns and
56
+ // sort columns (the RuntimeIndex unifies them at creation time).
57
+ // `queryArchetypes` filters to only those archetypes carrying the
58
+ // full set — entities in any other archetype lack at least one
59
+ // component the index touches and would never produce a key.
60
+ const required = idx.readColumns;
61
+ if (required.length === 0)
62
+ return;
63
+ const archetypes = core.queryArchetypes(required);
64
+ for (const archetype of archetypes) {
65
+ const idCol = archetype.columns.id;
66
+ for (let row = 0; row < archetype.rowCount; row++) {
67
+ const values = {};
68
+ for (const c of required) {
69
+ values[c] = archetype.columns[c].get(row);
70
+ }
71
+ idx.add(idCol.get(row), values);
72
+ }
73
+ }
74
+ };
75
+ // Public handle map keyed by user-chosen name. Each entry exposes
76
+ // find/findRange (always) and get (when the index is unique). Same
77
+ // object reference flows up via spread to the Database layer.
78
+ const indexHandles = {};
79
+ const exposeIndexHandle = (name, idx) => {
80
+ if (name in indexHandles)
81
+ return;
82
+ // Decide whether the auto-router can dispatch a raw-equality `where`
83
+ // clause to this index. Routable iff the key declaration is a pure
84
+ // column reference (bare string) or column tuple (string array) —
85
+ // function keys derive a value from inputs (live in a different value
86
+ // space than the source columns) and slot maps name parts arbitrarily
87
+ // (`{ team: "team", role: ... }`), so the planner cannot infer the
88
+ // column set from a where clause alone for either.
89
+ const key = idx.key;
90
+ let routableColumns;
91
+ if (typeof key === "string") {
92
+ routableColumns = [key];
93
+ }
94
+ else if (Array.isArray(key)) {
95
+ routableColumns = key;
96
+ }
97
+ else {
98
+ routableColumns = null;
99
+ }
100
+ const handle = {
101
+ find: idx.find,
102
+ findRange: idx.findRange,
103
+ // Internal planner-only field. Not part of the public
104
+ // `Database.Index.Handle` type. `routableColumns: null` opts the
105
+ // index out of raw-where auto-routing (function/slot-map keys).
106
+ // The planner reads this to pick a matching index and then
107
+ // calls `handle.find` (so user-installed spies on the handle
108
+ // intercept the routed call).
109
+ routableColumns,
110
+ };
111
+ if (idx.unique)
112
+ handle.get = idx.get;
113
+ indexHandles[name] = handle;
114
+ };
24
115
  // Each resource will be stored as the only entity in an archetype of [id, <resourceName>]
25
116
  // The resource component we added above will contain the resource value
26
117
  const ensureResourceInitialized = (name, resourceSchema) => {
@@ -34,6 +125,9 @@ export function createStore(schema) {
34
125
  const insertValues = isEphemeral
35
126
  ? { [resourceId]: resourceSchema.default, ephemeral: true }
36
127
  : { [resourceId]: resourceSchema.default };
128
+ // Resource singleton inserts bypass index pre-check because
129
+ // resources are not typically indexed by their schema name and
130
+ // the singleton row is created exactly once.
37
131
  archetype.insert(insertValues);
38
132
  }
39
133
  if (!Object.prototype.hasOwnProperty.call(resources, name)) {
@@ -52,8 +146,66 @@ export function createStore(schema) {
52
146
  return selectEntities(core, include, options);
53
147
  };
54
148
  const archetypes = {};
149
+ /**
150
+ * Wraps a raw Core archetype so `insert` maintains index state. Pre-checks
151
+ * unique constraints *before* the column mutation so a collision throws
152
+ * without partially mutating the store. Other archetype methods are
153
+ * passed through unchanged via the Proxy.
154
+ *
155
+ * Cached by raw archetype identity so repeated lookups
156
+ * (`store.archetypes.X`, `store.ensureArchetype([...])`) all return the
157
+ * same wrapper reference — code that uses `===` to compare archetypes
158
+ * keeps working.
159
+ */
160
+ const archetypeWrapCache = new WeakMap();
161
+ const wrapArchetypeForIndexes = (archetype) => {
162
+ const cached = archetypeWrapCache.get(archetype);
163
+ if (cached)
164
+ return cached;
165
+ const rawInsert = archetype.insert.bind(archetype);
166
+ const wrappedInsert = (values) => {
167
+ indexRegistry.checkUniqueAvailableForInsert(values);
168
+ const entity = rawInsert(values);
169
+ indexRegistry.applyInsert(entity, values);
170
+ return entity;
171
+ };
172
+ const wrapped = new Proxy(archetype, {
173
+ get(target, prop, receiver) {
174
+ if (prop === "insert")
175
+ return wrappedInsert;
176
+ return Reflect.get(target, prop, receiver);
177
+ },
178
+ });
179
+ archetypeWrapCache.set(archetype, wrapped);
180
+ return wrapped;
181
+ };
182
+ const ensureArchetype = ((componentNames) => {
183
+ return wrapArchetypeForIndexes(core.ensureArchetype(componentNames));
184
+ });
185
+ // `queryArchetypes` and `locate` also surface archetypes — wrap them
186
+ // so `===` comparisons against `store.archetypes.X` continue to hold.
187
+ // Cached wrappers guarantee identity stability per raw archetype.
188
+ const queryArchetypes = ((include, options) => {
189
+ const raw = core.queryArchetypes(include, options);
190
+ return raw.map((a) => wrapArchetypeForIndexes(a));
191
+ });
192
+ const locate = ((entity) => {
193
+ const loc = core.locate(entity);
194
+ if (loc === null)
195
+ return null;
196
+ return { archetype: wrapArchetypeForIndexes(loc.archetype), row: loc.row };
197
+ });
198
+ const updateEntity = (entity, values) => {
199
+ indexRegistry.checkUniqueAvailableForUpdate(entity, values);
200
+ core.update(entity, values);
201
+ indexRegistry.applyUpdate(entity);
202
+ };
203
+ const deleteEntity = (entity) => {
204
+ core.delete(entity);
205
+ indexRegistry.applyDelete(entity);
206
+ };
55
207
  const extend = (schema) => {
56
- const { components: schemaComponents = {}, resources: schemaResources = {}, archetypes: schemaArchetypes = {} } = schema;
208
+ const { components: schemaComponents = {}, resources: schemaResources = {}, archetypes: schemaArchetypes = {}, indexes: schemaIndexes = {}, } = schema;
57
209
  // components: existing must be identical if present
58
210
  for (const [name, newComponentSchema] of Object.entries(schemaComponents)) {
59
211
  if (name in componentAndResourceSchemas) {
@@ -81,7 +233,8 @@ export function createStore(schema) {
81
233
  newResourceNames.push(name);
82
234
  ensureResourceInitialized(name, newResourceSchema);
83
235
  }
84
- // archetypes: existing must be identical if present
236
+ // archetypes: existing must be identical if present.
237
+ // Wrap each archetype with index maintenance hooks at exposure time.
85
238
  for (const [name, newComponents] of Object.entries(schemaArchetypes)) {
86
239
  if (name in archetypeComponentNames) {
87
240
  if (archetypeComponentNames[name] !== newComponents) {
@@ -91,21 +244,48 @@ export function createStore(schema) {
91
244
  }
92
245
  archetypeComponentNames[name] = newComponents;
93
246
  const archetype = core.ensureArchetype(["id", ...newComponents]);
94
- archetypes[name] = archetype;
247
+ archetypes[name] = wrapArchetypeForIndexes(archetype);
248
+ }
249
+ // indexes: registry enforces (===)-or-throw on same name and
250
+ // structural duplicate detection across names. A new RuntimeIndex
251
+ // is returned when registration actually happened (vs. a benign
252
+ // identity-match re-register), in which case we seed it from the
253
+ // archetypes that contain its component set — and only those.
254
+ const declaredIndexes = schemaIndexes;
255
+ for (const name in declaredIndexes) {
256
+ const newIdx = indexRegistry.register(name, declaredIndexes[name]);
257
+ if (newIdx) {
258
+ exposeIndexHandle(name, newIdx);
259
+ seedIndexFromArchetypes(newIdx);
260
+ }
95
261
  }
96
262
  return store;
97
263
  };
98
264
  const store = {
99
265
  ...core,
266
+ ensureArchetype,
267
+ queryArchetypes,
268
+ locate,
269
+ update: updateEntity,
270
+ delete: deleteEntity,
100
271
  resources,
101
272
  select,
102
273
  archetypes,
274
+ indexes: indexHandles,
103
275
  extend,
104
276
  reset: () => {
105
277
  core.reset();
106
278
  for (const [name, resourceSchema] of Object.entries(resourceSchemas)) {
107
279
  ensureResourceInitialized(name, resourceSchema);
108
280
  }
281
+ // After reset, every index bucket is wiped. Resources are the
282
+ // only entities that exist post-reset; seed each index from
283
+ // archetypes so resource singletons whose components an index
284
+ // happens to cover still appear.
285
+ indexRegistry.clear();
286
+ for (const idx of indexRegistry.indexes.values()) {
287
+ seedIndexFromArchetypes(idx);
288
+ }
109
289
  },
110
290
  toData: () => core.toData(),
111
291
  fromData: (data) => {
@@ -113,6 +293,12 @@ export function createStore(schema) {
113
293
  for (const [name, resourceSchema] of Object.entries(resourceSchemas)) {
114
294
  ensureResourceInitialized(name, resourceSchema);
115
295
  }
296
+ // The loaded core has all entities; re-derive each index by
297
+ // walking only the archetypes that contain its components.
298
+ indexRegistry.clear();
299
+ for (const idx of indexRegistry.indexes.values()) {
300
+ seedIndexFromArchetypes(idx);
301
+ }
116
302
  },
117
303
  };
118
304
  return store.extend(normalizedSchema);