@travetto/model-indexed 8.0.0-alpha.10 → 8.0.0-alpha.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -136,7 +136,7 @@ export const specificOrders = keyedIndex(Order, {
136
136
  ```
137
137
 
138
138
  ## Using Indexes
139
- Model services that implement [ModelIndexedSupport](https://github.com/travetto/travetto/tree/main/module/model-indexed/src/types/service.ts#L23) allow you to query using the indexes you've defined.
139
+ Model services that implement [ModelIndexedSupport](https://github.com/travetto/travetto/tree/main/module/model-indexed/src/types/service.ts#L15) allow you to query using the indexes you've defined.
140
140
 
141
141
  ### Service Interface
142
142
 
@@ -204,17 +204,29 @@ export interface ModelIndexedSupport extends ModelBasicSupport {
204
204
  >(cls: Class<T>, idx: SingleItemIndex<T, K, S>, body: FullKeyedIndexWithPartialBody<T, K, S>): Promise<T>;
205
205
 
206
206
  /**
207
- * List entity by ranged index as defined by fields of idx
207
+ * Page through entities by ranged index as defined by fields of idx
208
208
  * @param cls The type to search by
209
209
  * @param idx The index to search against
210
210
  * @param body The payload of fields needed to search
211
- * @param options The configuration for listing
211
+ * @param options The configuration for pagination
212
212
  */
213
- listByIndex<
213
+ pageByIndex<
214
214
  T extends ModelType,
215
215
  S extends SortedIndexSelection<T>,
216
216
  K extends KeyedIndexSelection<T>
217
217
  >(cls: Class<T>, idx: SortedIndex<T, K, S>, body: KeyedIndexBody<T, K>, options?: ListPageOptions): Promise<ListPageResult<T>>;
218
+
219
+ /**
220
+ * List all entities by ranged index as defined by fields of idx
221
+ * @param cls The type to search by
222
+ * @param idx The index to search against
223
+ * @param body The payload of fields needed to search
224
+ */
225
+ listByIndex<
226
+ T extends ModelType,
227
+ S extends SortedIndexSelection<T>,
228
+ K extends KeyedIndexSelection<T>
229
+ >(cls: Class<T>, idx: SortedIndex<T, K, S>, body: KeyedIndexBody<T, K>,): AsyncIterable<T>;
218
230
  }
219
231
  ```
220
232
 
@@ -224,41 +236,63 @@ The service provides these operations:
224
236
  * `upsertByIndex` — Insert or update by index
225
237
  * `updateByIndex` — Update an existing item by index
226
238
  * `updatePartialByIndex` — Partially update an item by index
227
- * `listByIndex` — List items with pagination and sorting
239
+ * `pageByIndex` — Fetch a page of items with pagination metadata
240
+ * `listByIndex` — Stream all matching items from a sorted index
228
241
 
229
242
  ### Getting Items
230
243
  Use `getByIndex` to fetch a single item by providing all required key fields.
231
244
 
232
245
  **Code: Getting by Keyed Index**
233
246
  ```typescript
234
- export async function getExample(modelService: any) {
247
+ export async function getExample(modelService: ModelIndexedSupport) {
235
248
  const user = await modelService.getByIndex(User, userByName, {
236
249
  name: 'John Doe'
237
250
  });
238
251
  return user;
239
252
  }
253
+
254
+ export async function getScopedExample(modelService: ModelIndexedSupport) {
255
+ const user = await modelService.getByIndex(User, userByName, {
256
+ name: 'John Doe',
257
+ id: 'user-123'
258
+ });
259
+ return user;
260
+ }
240
261
  ```
241
262
 
242
- For sorted indexes with key fields, you must provide all key values plus the sort value if using it to identify a specific item.
263
+ For sorted indexes with key fields, you must provide all key values plus the sort value if using it to identify a specific item. All single-item index operations also accept an optional `id` in the request body. This is useful when the index is not unique and you need to ensure the supplied index values resolve to the same record as the provided `id`, such as enforcing a pattern like "userId matches".
264
+
265
+ **Code: Disambiguating with id**
266
+ ```typescript
267
+ export async function getScopedExample(modelService: ModelIndexedSupport) {
268
+ const user = await modelService.getByIndex(User, userByName, {
269
+ name: 'John Doe',
270
+ id: 'user-123'
271
+ });
272
+ return user;
273
+ }
274
+ ```
243
275
 
244
276
  ### Deleting Items
245
277
  Use `deleteByIndex` to remove an item by index.
246
278
 
247
279
  **Code: Deleting by Index**
248
280
  ```typescript
249
- export async function deleteExample(modelService: any) {
281
+ export async function deleteExample(modelService: ModelIndexedSupport) {
250
282
  await modelService.deleteByIndex(User, userByName, {
251
283
  name: 'John Doe'
252
284
  });
253
285
  }
254
286
  ```
255
287
 
288
+ As with `getByIndex`, you can pass an optional `id` to ensure the computed index values resolve to the expected record before deleting it.
289
+
256
290
  ### Upserting Items
257
291
  Use `upsertByIndex` to insert a new item or update an existing one. The index acts as a primary key.
258
292
 
259
293
  **Code: Upserting by Index**
260
294
  ```typescript
261
- export async function upsertExample(modelService: any) {
295
+ export async function upsertExample(modelService: ModelIndexedSupport) {
262
296
  const user = await modelService.upsertByIndex(User, userByName, {
263
297
  id: 'user-1',
264
298
  name: 'John Doe',
@@ -273,7 +307,7 @@ Use `updateByIndex` to update an existing item, or `updatePartialByIndex` for pa
273
307
 
274
308
  **Code: Updating by Index**
275
309
  ```typescript
276
- export async function updateExample(modelService: any) {
310
+ export async function updateExample(modelService: ModelIndexedSupport) {
277
311
  // Full update — all fields required
278
312
  const user = await modelService.updateByIndex(User, userByName, {
279
313
  id: 'user-1',
@@ -284,7 +318,7 @@ export async function updateExample(modelService: any) {
284
318
  return user;
285
319
  }
286
320
 
287
- export async function updatePartialExample(modelService: any) {
321
+ export async function updatePartialExample(modelService: ModelIndexedSupport) {
288
322
  // Partial update — only updated fields required
289
323
  const user = await modelService.updatePartialByIndex(User, userByName, {
290
324
  name: 'John Doe',
@@ -295,12 +329,12 @@ export async function updatePartialExample(modelService: any) {
295
329
  ```
296
330
 
297
331
  ### Listing Items
298
- Use `listByIndex` to fetch multiple items from a sorted index with pagination.
332
+ Use `pageByIndex` when you want paginated access to a sorted index.
299
333
 
300
- **Code: Listing by Sorted Index**
334
+ **Code: Paging by Sorted Index**
301
335
  ```typescript
302
- export async function listExample(modelService: any) {
303
- const result = await modelService.listByIndex(User, recentUsers, {}, {
336
+ export async function listExample(modelService: ModelIndexedSupport) {
337
+ const result = await modelService.pageByIndex(User, recentUsers, {}, {
304
338
  limit: 20,
305
339
  offset: '0'
306
340
  });
@@ -311,13 +345,28 @@ export async function listExample(modelService: any) {
311
345
  }
312
346
  ```
313
347
 
314
- You can also provide key values to filter within a sorted index:
348
+ Use `listByIndex` when you want to iterate through every matching item as an async stream.
349
+
350
+ **Code: Streaming by Sorted Index**
351
+ ```typescript
352
+ export async function listStreamExample(modelService: ModelIndexedSupport) {
353
+ const items: User[] = [];
354
+
355
+ for await (const user of modelService.listByIndex(User, recentUsers, {})) {
356
+ items.push(user);
357
+ }
358
+
359
+ return items;
360
+ }
361
+ ```
362
+
363
+ You can also provide key values to filter within a sorted index with `pageByIndex`:
315
364
 
316
365
  **Code: Listing with Key Filter**
317
366
  ```typescript
318
- export async function listWithFilterExample(modelService: any) {
367
+ export async function listWithFilterExample(modelService: ModelIndexedSupport) {
319
368
  // Get all users named 'John' sorted by age
320
- const result = await modelService.listByIndex(User, usersByNameAge, {
369
+ const result = await modelService.pageByIndex(User, usersByNameAge, {
321
370
  name: 'John'
322
371
  }, {
323
372
  limit: 10
@@ -327,7 +376,7 @@ export async function listWithFilterExample(modelService: any) {
327
376
  ```
328
377
 
329
378
  ## Integration
330
- Index registration happens automatically when models are decorated with [@Model](https://github.com/travetto/travetto/tree/main/module/model/src/registry/decorator.ts#L14). Model services like [Memory Model Support](https://github.com/travetto/travetto/tree/main/module/model-memory#readme "Memory backing for the travetto model module."), [MongoDB Model Support](https://github.com/travetto/travetto/tree/main/module/model-mongo#readme "Mongo backing for the travetto model module."), and [SQL Model Service](https://github.com/travetto/travetto/tree/main/module/model-sql#readme "SQL backing for the travetto model module, with real-time modeling support for SQL schemas.") implement the [ModelIndexedSupport](https://github.com/travetto/travetto/tree/main/module/model-indexed/src/types/service.ts#L23) interface to provide indexed access.
379
+ Index registration happens automatically when models are decorated with [@Model](https://github.com/travetto/travetto/tree/main/module/model/src/registry/decorator.ts#L14). Model services like [Memory Model Support](https://github.com/travetto/travetto/tree/main/module/model-memory#readme "Memory backing for the travetto model module."), [MongoDB Model Support](https://github.com/travetto/travetto/tree/main/module/model-mongo#readme "Mongo backing for the travetto model module."), and [SQL Model Service](https://github.com/travetto/travetto/tree/main/module/model-sql#readme "SQL backing for the travetto model module, with real-time modeling support for SQL schemas.") implement the [ModelIndexedSupport](https://github.com/travetto/travetto/tree/main/module/model-indexed/src/types/service.ts#L15) interface to provide indexed access.
331
380
 
332
381
  ### Reading Registry Information
333
382
  You can access registered indexes via [ModelRegistryIndex](https://github.com/travetto/travetto/tree/main/module/model/src/registry/registry-index.ts#L12) at runtime:
@@ -335,11 +384,11 @@ You can access registered indexes via [ModelRegistryIndex](https://github.com/tr
335
384
  **Code: Accessing Model Indexes**
336
385
  ```typescript
337
386
  export function registryAccessExample() {
338
- const registry = ModelRegistryIndex.get(User);
387
+ const registry = ModelRegistryIndex.getConfig(User);
339
388
  const indexes = registry.indices; // Map of all indexes for the model
340
389
 
341
390
  // Access a specific index
342
- const userByName = indexes['userByName'];
391
+ const userByName = indexes?.['userByName'];
343
392
  return userByName;
344
393
  }
345
394
  ```
@@ -350,4 +399,4 @@ export function registryAccessExample() {
350
399
  * **Use composite keys** — When filtering by multiple fields, include all of them in a single index
351
400
  * **Leverage sorting** — Use sorted indexes for paginated lists and range queries
352
401
  * **Enforce uniqueness** — Use [uniqueIndex](https://github.com/travetto/travetto/tree/main/module/model-indexed/src/indexes.ts#L47) for fields that must be globally unique
353
- * **Handle errors gracefully** — Catch [IndexedFieldError](https://github.com/travetto/travetto/tree/main/module/model-indexed/src/types/indexes.ts#L88) when working with user input
402
+ * **Handle errors gracefully** — Catch [IndexedFieldError](https://github.com/travetto/travetto/tree/main/module/model-indexed/src/types/error.ts#L7) when working with user input
package/__index__.ts CHANGED
@@ -1,4 +1,6 @@
1
+ export * from './src/types/error.ts';
1
2
  export * from './src/types/indexes.ts';
3
+ export * from './src/types/list.ts';
2
4
  export * from './src/types/service.ts';
3
5
  export * from './src/computed.ts';
4
6
  export * from './src/indexes.ts';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@travetto/model-indexed",
3
- "version": "8.0.0-alpha.10",
3
+ "version": "8.0.0-alpha.11",
4
4
  "type": "module",
5
5
  "description": "Basic indexing support for model sources that support it.",
6
6
  "keywords": [
package/src/computed.ts CHANGED
@@ -2,15 +2,16 @@ import type { ModelType } from '@travetto/model';
2
2
  import { castTo, type Any } from '@travetto/runtime';
3
3
 
4
4
  import {
5
- type KeyedIndexSelection, type SortedIndexSelection, type AllIndexes, type KeyedIndexBody, IndexedFieldError,
5
+ type KeyedIndexSelection, type SortedIndexSelection, type AllIndexes, type KeyedIndexBody,
6
6
  type FullKeyedIndexBody, type TemplateValue, type TemplatePart
7
7
  } from './types/indexes.ts';
8
+ import { IndexedFieldError } from './types/error.ts';
8
9
 
9
10
  const DEFAULT_SEP = '\u8203';
10
11
 
11
- type IndexPart<T extends TemplateValue = TemplateValue> = {
12
+ type IndexPart<T extends TemplateValue = TemplateValue, V = unknown> = {
12
13
  state: 'missing' | 'empty' | 'mismatch' | 'found';
13
- value: unknown;
14
+ value: V;
14
15
  path: string[];
15
16
  templateValue: T;
16
17
  };
@@ -29,11 +30,11 @@ function buildIndexParts<T extends TemplateValue = TemplateValue>(
29
30
  if (value && pathItem in value) {
30
31
  value = castTo<Record<string, unknown>>(value)[pathItem];
31
32
  } else {
32
- bodyPart = { value: undefined!, state: 'missing' };
33
+ bodyPart = { value: null, state: 'missing' };
33
34
  break;
34
35
  }
35
36
  } else {
36
- bodyPart = { value: undefined!, state: 'mismatch' };
37
+ bodyPart = { value: castTo(value), state: 'mismatch' };
37
38
  break;
38
39
  }
39
40
  }
@@ -75,6 +76,7 @@ export class ModelIndexedComputedIndex<T extends ModelType> {
75
76
 
76
77
  keyedParts: IndexPart<true>[];
77
78
  sortParts: IndexPart<-1 | 1>[];
79
+ idPart: IndexPart<true, string> | undefined;
78
80
  idx: AllIndexes<T>;
79
81
 
80
82
  constructor(
@@ -84,6 +86,9 @@ export class ModelIndexedComputedIndex<T extends ModelType> {
84
86
  this.idx = idx;
85
87
  this.keyedParts = buildIndexParts(idx.keyTemplate, castTo(body));
86
88
  this.sortParts = buildIndexParts(idx.sortTemplate, castTo(body), value => typeof value === 'number' || value instanceof Date);
89
+ if ('id' in body && typeof body.id === 'string') {
90
+ this.idPart = { path: ['id'], value: body.id, state: body.id === null || body.id === undefined ? 'empty' : 'found', templateValue: true };
91
+ }
87
92
  }
88
93
 
89
94
  get allParts(): IndexPart[] {
@@ -119,8 +124,8 @@ export class ModelIndexedComputedIndex<T extends ModelType> {
119
124
  }
120
125
  }
121
126
 
122
- project(config: IndexProcessConfig<{ emptyValue?: unknown }> = {}): Record<string, unknown> {
123
- const { keyed = true, sort = false, emptyValue = null } = config;
127
+ project(config: IndexProcessConfig<{ emptyValue?: unknown, includeId?: boolean }> = {}): Record<string, unknown> {
128
+ const { keyed = true, sort = false, emptyValue = null, includeId } = config;
124
129
  const response: Record<string, unknown> = {};
125
130
  if (keyed) {
126
131
  for (const { path, value, state } of this.keyedParts) {
@@ -144,6 +149,9 @@ export class ModelIndexedComputedIndex<T extends ModelType> {
144
149
  sub[last] = state === 'empty' ? emptyValue : value;
145
150
  }
146
151
  }
152
+ if (includeId && this.idPart) {
153
+ response.id = this.idPart.state === 'empty' ? emptyValue : this.idPart.value;
154
+ }
147
155
  return response;
148
156
  }
149
157
  }
@@ -0,0 +1,13 @@
1
+ import type { ModelType } from '@travetto/model';
2
+ import { RuntimeError, type Class } from '@travetto/runtime';
3
+
4
+ import type { AllIndexes } from './indexes.ts';
5
+
6
+
7
+ export class IndexedFieldError<T extends ModelType> extends RuntimeError {
8
+ constructor(cls: Class<T>, idx: AllIndexes<T>, fieldPath: string, message: string) {
9
+ super(`${message}: ${idx.name} on ${cls.name} at path ${fieldPath}`, {
10
+ details: { cls: cls.name, index: idx.name, fieldPath }
11
+ });
12
+ }
13
+ }
@@ -1,5 +1,5 @@
1
1
  import type { ModelType, IndexConfig } from '@travetto/model';
2
- import { type IntrinsicType, type Any, type DeepPartial, RuntimeError, type Class } from '@travetto/runtime';
2
+ import { type IntrinsicType, type Any, type DeepPartial } from '@travetto/runtime';
3
3
 
4
4
  type TypeProjection<T, V> = {
5
5
  [P in keyof T]?:
@@ -10,8 +10,8 @@ type TypeProjection<T, V> = {
10
10
  );
11
11
  };
12
12
 
13
- export type KeyedIndexSelection<T> = TypeProjection<T, true>;
14
- export type SortedIndexSelection<T> = TypeProjection<T, 1 | -1>;
13
+ export type KeyedIndexSelection<T extends ModelType> = TypeProjection<T, true>;
14
+ export type SortedIndexSelection<T extends ModelType> = TypeProjection<T, 1 | -1>;
15
15
 
16
16
  export type KeyedIndexBody<T, K> = {
17
17
  [P in keyof K]: (P extends keyof T ?
@@ -44,8 +44,8 @@ export type KeyedIndexWithPartialBody<T, K> = {
44
44
  DeepPartial<Omit<T, keyof K>>;
45
45
 
46
46
 
47
- export type FullKeyedIndexBody<T, K, S> = KeyedIndexBody<T, Merge<K, S>>;
48
- export type FullKeyedIndexWithPartialBody<T, K, S> = KeyedIndexWithPartialBody<T, Merge<K, S>>;
47
+ export type FullKeyedIndexBody<T, K, S> = KeyedIndexBody<Omit<T, 'id'>, Merge<K, S>> & { id?: string };
48
+ export type FullKeyedIndexWithPartialBody<T, K, S> = KeyedIndexWithPartialBody<Omit<T, 'id'>, Merge<K, S>> & { id?: string };
49
49
 
50
50
  export type TemplateValue = 1 | -1 | true;
51
51
  export type TemplatePart<T extends TemplateValue = TemplateValue> = { path: string[], value: T, part: 'key' | 'sort' };
@@ -85,10 +85,3 @@ export type AllIndexes<
85
85
  S extends SortedIndexSelection<T> = Any
86
86
  > = KeyedIndex<T, K, S> | SortedIndex<T, K, S>;
87
87
 
88
- export class IndexedFieldError<T extends ModelType> extends RuntimeError {
89
- constructor(cls: Class<T>, idx: AllIndexes<T>, fieldPath: string, message: string) {
90
- super(`${message}: ${idx.name} on ${cls.name} at path ${fieldPath}`, {
91
- details: { cls: cls.name, index: idx.name, fieldPath }
92
- });
93
- }
94
- }
@@ -0,0 +1,11 @@
1
+ import type { ModelType } from '@travetto/model';
2
+
3
+ export type ListPageOptions<O = string> = {
4
+ limit?: number;
5
+ offset?: O;
6
+ };
7
+
8
+ export type ListPageResult<T extends ModelType> = {
9
+ items: T[];
10
+ nextOffset?: string;
11
+ };
@@ -1,19 +1,11 @@
1
1
  import type { ModelType, ModelBasicSupport, OptionalId } from '@travetto/model';
2
2
  import type { Class } from '@travetto/runtime';
3
+
3
4
  import type {
4
5
  KeyedIndexSelection, KeyedIndexBody, SortedIndexSelection, SortedIndex,
5
6
  SingleItemIndex, FullKeyedIndexBody, FullKeyedIndexWithPartialBody
6
7
  } from './indexes.ts';
7
-
8
- export type ListPageOptions<O = string> = {
9
- limit?: number;
10
- offset?: O;
11
- };
12
-
13
- export type ListPageResult<T extends ModelType> = {
14
- items: T[];
15
- nextOffset?: string;
16
- };
8
+ import type { ListPageOptions, ListPageResult } from './list.ts';
17
9
 
18
10
  /**
19
11
  * Support for simple indexed activity
@@ -82,15 +74,27 @@ export interface ModelIndexedSupport extends ModelBasicSupport {
82
74
  >(cls: Class<T>, idx: SingleItemIndex<T, K, S>, body: FullKeyedIndexWithPartialBody<T, K, S>): Promise<T>;
83
75
 
84
76
  /**
85
- * List entity by ranged index as defined by fields of idx
77
+ * Page through entities by ranged index as defined by fields of idx
86
78
  * @param cls The type to search by
87
79
  * @param idx The index to search against
88
80
  * @param body The payload of fields needed to search
89
- * @param options The configuration for listing
81
+ * @param options The configuration for pagination
90
82
  */
91
- listByIndex<
83
+ pageByIndex<
92
84
  T extends ModelType,
93
85
  S extends SortedIndexSelection<T>,
94
86
  K extends KeyedIndexSelection<T>
95
87
  >(cls: Class<T>, idx: SortedIndex<T, K, S>, body: KeyedIndexBody<T, K>, options?: ListPageOptions): Promise<ListPageResult<T>>;
88
+
89
+ /**
90
+ * List all entities by ranged index as defined by fields of idx
91
+ * @param cls The type to search by
92
+ * @param idx The index to search against
93
+ * @param body The payload of fields needed to search
94
+ */
95
+ listByIndex<
96
+ T extends ModelType,
97
+ S extends SortedIndexSelection<T>,
98
+ K extends KeyedIndexSelection<T>
99
+ >(cls: Class<T>, idx: SortedIndex<T, K, S>, body: KeyedIndexBody<T, K>,): AsyncIterable<T>;
96
100
  }
@@ -102,6 +102,33 @@ export abstract class ModelIndexedSuite extends BaseModelSuite<ModelIndexedSuppo
102
102
  assert(found2.name === 'bob2');
103
103
  }
104
104
 
105
+ @Test()
106
+ async readByKeyedIndexUsingId() {
107
+ const service = await this.service;
108
+
109
+ const first = await service.create(User, User.from({ name: 'sam' }));
110
+ const second = await service.create(User, User.from({ name: 'bob' }));
111
+
112
+ const found = await service.getByIndex(User, userNameIndex, {
113
+ name: 'bob',
114
+ id: second.id
115
+ });
116
+
117
+ assert(found.id === second.id);
118
+
119
+ await assert.rejects(
120
+ () => service.getByIndex(User, userNameIndex, { name: 'bob', id: first.id }),
121
+ NotFoundError
122
+ );
123
+
124
+ await service.deleteByIndex(User, userNameIndex, { name: 'sam', id: first.id });
125
+
126
+ await assert.rejects(() => service.get(User, first.id), NotFoundError);
127
+
128
+ const remaining = await service.getByIndex(User, userNameIndex, { name: 'bob', id: second.id });
129
+ assert(remaining.id === second.id);
130
+ }
131
+
105
132
  @Test()
106
133
  async readMissingValue() {
107
134
  const service = await this.service;
@@ -133,6 +160,30 @@ export abstract class ModelIndexedSuite extends BaseModelSuite<ModelIndexedSuppo
133
160
  await assert.rejects(() => service.getByIndex(User3, userAgeIndex, { name: 'bob' }), IndexedFieldError);
134
161
  }
135
162
 
163
+ @Test()
164
+ async readBySortedIndexUsingId() {
165
+ const service = await this.service;
166
+
167
+ const first = await service.create(User3, User3.from({ name: 'bob', age: 40, color: 'blue' }));
168
+ const second = await service.create(User3, User3.from({ name: 'bob', age: 40, color: 'green' }));
169
+
170
+ const found = await service.getByIndex(User3, userAgeIndex, {
171
+ name: 'bob',
172
+ age: 40,
173
+ id: second.id
174
+ });
175
+
176
+ assert(found.id === second.id);
177
+ assert(found.color === 'green');
178
+
179
+ await service.deleteByIndex(User3, userAgeIndex, { name: 'bob', age: 40, id: first.id });
180
+
181
+ await assert.rejects(() => service.get(User3, first.id), NotFoundError);
182
+
183
+ const remaining = await service.getByIndex(User3, userAgeIndex, { name: 'bob', age: 40, id: second.id });
184
+ assert(remaining.id === second.id);
185
+ }
186
+
136
187
  @Test()
137
188
  async queryList() {
138
189
  const service = await this.service;
@@ -141,7 +192,7 @@ export abstract class ModelIndexedSuite extends BaseModelSuite<ModelIndexedSuppo
141
192
  await service.create(User3, User3.from({ name: 'bob', age: 30, color: 'red' }));
142
193
  await service.create(User3, User3.from({ name: 'bob', age: 50, color: 'green' }));
143
194
 
144
- const { items: arr } = await service.listByIndex(User3, userAgeIndex, { name: 'bob' });
195
+ const { items: arr } = await service.pageByIndex(User3, userAgeIndex, { name: 'bob' });
145
196
 
146
197
  console.error(arr);
147
198
 
@@ -153,7 +204,7 @@ export abstract class ModelIndexedSuite extends BaseModelSuite<ModelIndexedSuppo
153
204
  assert(arr[2].name === 'bob');
154
205
 
155
206
  // @ts-expect-error
156
- await assert.rejects(() => service.listByIndex(User3, userAgeIndex, {}), IndexedFieldError);
207
+ await assert.rejects(() => service.pageByIndex(User3, userAgeIndex, {}), IndexedFieldError);
157
208
  }
158
209
 
159
210
  @Test()
@@ -164,7 +215,7 @@ export abstract class ModelIndexedSuite extends BaseModelSuite<ModelIndexedSuppo
164
215
  await service.create(User3, User3.from({ name: 'alice', age: 30, color: 'red' }));
165
216
  await service.create(User3, User3.from({ name: 'bob', age: 50, color: 'green' }));
166
217
 
167
- const { items: arr } = await service.listByIndex(User3, userAgeNoKeyIndex, {});
218
+ const { items: arr } = await service.pageByIndex(User3, userAgeNoKeyIndex, {});
168
219
 
169
220
  assert(arr[0].name === 'alice' && arr[0].age === 30);
170
221
  assert(arr[1].name === 'charlie' && arr[1].age === 40);
@@ -185,13 +236,13 @@ export abstract class ModelIndexedSuite extends BaseModelSuite<ModelIndexedSuppo
185
236
  await service.create(User4, User4.from({ child: { name: 'bob', age: 30 }, color: 'red' }));
186
237
  await service.create(User4, User4.from({ child: { name: 'bob', age: 50 }, color: 'green' }));
187
238
 
188
- const { items: arr } = await service.listByIndex(User4, childAgeIndex, { child: { name: 'bob' } });
239
+ const { items: arr } = await service.pageByIndex(User4, childAgeIndex, { child: { name: 'bob' } });
189
240
  assert(arr[0].color === 'red' && arr[0].child.name === 'bob' && arr[0].child.age === 30);
190
241
  assert(arr[1].color === 'blue' && arr[1].child.name === 'bob' && arr[1].child.age === 40);
191
242
  assert(arr[2].color === 'green' && arr[2].child.name === 'bob' && arr[2].child.age === 50);
192
243
 
193
244
  // @ts-expect-error
194
- await assert.rejects(() => service.listByIndex(User4, childAgeIndex, {}), IndexedFieldError);
245
+ await assert.rejects(() => service.pageByIndex(User4, childAgeIndex, {}), IndexedFieldError);
195
246
  }
196
247
 
197
248
  @Test({ skip: (self) => !castTo<ModelIndexedSuite>(self).supportsDeepIndexes })
@@ -202,14 +253,14 @@ export abstract class ModelIndexedSuite extends BaseModelSuite<ModelIndexedSuppo
202
253
  await service.create(User4, User4.from({ child: { name: 'bob', age: 30 }, createdDate: TimeUtil.fromNow('2d'), color: 'red' }));
203
254
  await service.create(User4, User4.from({ child: { name: 'bob', age: 50 }, createdDate: TimeUtil.fromNow('-1d'), color: 'green' }));
204
255
 
205
- const { items: arr } = await service.listByIndex(User4, nameCreatedIndex, { child: { name: 'bob' } });
256
+ const { items: arr } = await service.pageByIndex(User4, nameCreatedIndex, { child: { name: 'bob' } });
206
257
 
207
258
  assert(arr[0].color === 'green' && arr[0].child.name === 'bob' && arr[0].child.age === 50);
208
259
  assert(arr[1].color === 'red' && arr[1].child.name === 'bob' && arr[1].child.age === 30);
209
260
  assert(arr[2].color === 'blue' && arr[2].child.name === 'bob' && arr[2].child.age === 40);
210
261
 
211
262
  // @ts-expect-error
212
- await assert.rejects(() => service.listByIndex(User4, nameCreatedIndex, {}), IndexedFieldError);
263
+ await assert.rejects(() => service.pageByIndex(User4, nameCreatedIndex, {}), IndexedFieldError);
213
264
  }
214
265
 
215
266
  @Test()
@@ -220,7 +271,7 @@ export abstract class ModelIndexedSuite extends BaseModelSuite<ModelIndexedSuppo
220
271
  const user2 = await service.upsertByIndex(User3, userAgeIndex, { name: 'bob', age: 40, color: 'green' });
221
272
  const user3 = await service.upsertByIndex(User3, userAgeIndex, { name: 'bob', age: 40, color: 'red' });
222
273
 
223
- const { items: arr } = await service.listByIndex(User3, userAgeIndex, { name: 'bob' });
274
+ const { items: arr } = await service.pageByIndex(User3, userAgeIndex, { name: 'bob' });
224
275
  assert(arr.length === 1);
225
276
 
226
277
  assert(user1.id === user2.id);
@@ -229,12 +280,12 @@ export abstract class ModelIndexedSuite extends BaseModelSuite<ModelIndexedSuppo
229
280
  assert(user3.color === 'red');
230
281
 
231
282
  const user4 = await service.upsertByIndex(User3, userAgeIndex, { name: 'bob', age: 30, color: 'red' });
232
- const { items: arr2 } = await service.listByIndex(User3, userAgeIndex, { name: 'bob' });
283
+ const { items: arr2 } = await service.pageByIndex(User3, userAgeIndex, { name: 'bob' });
233
284
  assert(arr2.length === 2);
234
285
 
235
286
  await service.deleteByIndex(User3, userAgeIndex, user1);
236
287
 
237
- const { items: arr3 } = await service.listByIndex(User3, userAgeIndex, { name: 'bob' });
288
+ const { items: arr3 } = await service.pageByIndex(User3, userAgeIndex, { name: 'bob' });
238
289
  assert(arr3.length === 1);
239
290
  assert(arr3[0].id === user4.id);
240
291
  }
@@ -288,7 +339,7 @@ export abstract class ModelIndexedSuite extends BaseModelSuite<ModelIndexedSuppo
288
339
  let offset: string | undefined;
289
340
 
290
341
  do {
291
- const page = await service.listByIndex(User3, userAgeIndex, { name: 'page' }, { limit, offset });
342
+ const page = await service.pageByIndex(User3, userAgeIndex, { name: 'page' }, { limit, offset });
292
343
  items.push(...page.items.map(u => u.color!));
293
344
  offset = page.nextOffset;
294
345
  } while (offset);
@@ -312,7 +363,7 @@ export abstract class ModelIndexedSuite extends BaseModelSuite<ModelIndexedSuppo
312
363
  let offset: string | undefined;
313
364
 
314
365
  do {
315
- const page = await service.listByIndex(User3, userAgeReversedIndex, { name: 'page' }, { limit, offset });
366
+ const page = await service.pageByIndex(User3, userAgeReversedIndex, { name: 'page' }, { limit, offset });
316
367
  items.push(...page.items.map(u => u.color!));
317
368
  offset = page.nextOffset;
318
369
  } while (offset);