@stackframe/stack-shared 2.8.56 → 2.8.59
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/apps/apps-config.d.mts +6 -0
- package/dist/apps/apps-config.d.ts +6 -0
- package/dist/apps/apps-config.js +6 -0
- package/dist/apps/apps-config.js.map +1 -1
- package/dist/config/migrate-catalogs-to-product-lines.d.mts +12 -0
- package/dist/config/migrate-catalogs-to-product-lines.d.ts +12 -0
- package/dist/config/migrate-catalogs-to-product-lines.js +211 -0
- package/dist/config/migrate-catalogs-to-product-lines.js.map +1 -0
- package/dist/config/schema-fuzzer.test.js +20 -6
- package/dist/config/schema-fuzzer.test.js.map +1 -1
- package/dist/config/schema.d.mts +296 -224
- package/dist/config/schema.d.ts +296 -224
- package/dist/config/schema.js +46 -8
- package/dist/config/schema.js.map +1 -1
- package/dist/esm/apps/apps-config.js +6 -0
- package/dist/esm/apps/apps-config.js.map +1 -1
- package/dist/esm/config/migrate-catalogs-to-product-lines.js +186 -0
- package/dist/esm/config/migrate-catalogs-to-product-lines.js.map +1 -0
- package/dist/esm/config/schema-fuzzer.test.js +20 -6
- package/dist/esm/config/schema-fuzzer.test.js.map +1 -1
- package/dist/esm/config/schema.js +47 -9
- package/dist/esm/config/schema.js.map +1 -1
- package/dist/esm/interface/admin-interface.js +49 -1
- package/dist/esm/interface/admin-interface.js.map +1 -1
- package/dist/esm/interface/client-interface.js +124 -25
- package/dist/esm/interface/client-interface.js.map +1 -1
- package/dist/esm/interface/crud/current-user.js +5 -2
- package/dist/esm/interface/crud/current-user.js.map +1 -1
- package/dist/esm/interface/crud/email-outbox.js +204 -0
- package/dist/esm/interface/crud/email-outbox.js.map +1 -0
- package/dist/esm/interface/crud/emails.js +0 -2
- package/dist/esm/interface/crud/emails.js.map +1 -1
- package/dist/esm/interface/crud/products.js +12 -1
- package/dist/esm/interface/crud/products.js.map +1 -1
- package/dist/esm/interface/crud/projects.js +3 -1
- package/dist/esm/interface/crud/projects.js.map +1 -1
- package/dist/esm/interface/crud/users.js +9 -2
- package/dist/esm/interface/crud/users.js.map +1 -1
- package/dist/esm/interface/server-interface.js +54 -0
- package/dist/esm/interface/server-interface.js.map +1 -1
- package/dist/esm/known-errors.js +69 -1
- package/dist/esm/known-errors.js.map +1 -1
- package/dist/esm/schema-fields.js +27 -3
- package/dist/esm/schema-fields.js.map +1 -1
- package/dist/esm/sessions.js +72 -8
- package/dist/esm/sessions.js.map +1 -1
- package/dist/esm/utils/env.js +13 -2
- package/dist/esm/utils/env.js.map +1 -1
- package/dist/esm/utils/esbuild.js +50 -21
- package/dist/esm/utils/esbuild.js.map +1 -1
- package/dist/esm/utils/globals.js +12 -0
- package/dist/esm/utils/globals.js.map +1 -1
- package/dist/esm/utils/paginated-lists.js +153 -23
- package/dist/esm/utils/paginated-lists.js.map +1 -1
- package/dist/esm/utils/paginated-lists.test.js +842 -0
- package/dist/esm/utils/paginated-lists.test.js.map +1 -0
- package/dist/esm/utils/proxies.js +28 -1
- package/dist/esm/utils/proxies.js.map +1 -1
- package/dist/esm/utils/react.js +7 -3
- package/dist/esm/utils/react.js.map +1 -1
- package/dist/esm/utils/results.js.map +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/interface/admin-interface.d.mts +26 -3
- package/dist/interface/admin-interface.d.ts +26 -3
- package/dist/interface/admin-interface.js +49 -1
- package/dist/interface/admin-interface.js.map +1 -1
- package/dist/interface/client-interface.d.mts +36 -0
- package/dist/interface/client-interface.d.ts +36 -0
- package/dist/interface/client-interface.js +124 -25
- package/dist/interface/client-interface.js.map +1 -1
- package/dist/interface/crud/current-user.d.mts +23 -6
- package/dist/interface/crud/current-user.d.ts +23 -6
- package/dist/interface/crud/current-user.js +5 -2
- package/dist/interface/crud/current-user.js.map +1 -1
- package/dist/interface/crud/email-outbox.d.mts +1075 -0
- package/dist/interface/crud/email-outbox.d.ts +1075 -0
- package/dist/interface/crud/email-outbox.js +241 -0
- package/dist/interface/crud/email-outbox.js.map +1 -0
- package/dist/interface/crud/emails.d.mts +0 -34
- package/dist/interface/crud/emails.d.ts +0 -34
- package/dist/interface/crud/emails.js +0 -2
- package/dist/interface/crud/emails.js.map +1 -1
- package/dist/interface/crud/products.d.mts +77 -0
- package/dist/interface/crud/products.d.ts +77 -0
- package/dist/interface/crud/products.js +12 -1
- package/dist/interface/crud/products.js.map +1 -1
- package/dist/interface/crud/project-api-keys.d.mts +1 -1
- package/dist/interface/crud/project-api-keys.d.ts +1 -1
- package/dist/interface/crud/projects.d.mts +70 -66
- package/dist/interface/crud/projects.d.ts +70 -66
- package/dist/interface/crud/projects.js +3 -1
- package/dist/interface/crud/projects.js.map +1 -1
- package/dist/interface/crud/team-member-profiles.d.mts +28 -12
- package/dist/interface/crud/team-member-profiles.d.ts +28 -12
- package/dist/interface/crud/users.d.mts +38 -6
- package/dist/interface/crud/users.d.ts +38 -6
- package/dist/interface/crud/users.js +9 -2
- package/dist/interface/crud/users.js.map +1 -1
- package/dist/interface/server-interface.d.mts +52 -0
- package/dist/interface/server-interface.d.ts +52 -0
- package/dist/interface/server-interface.js +54 -0
- package/dist/interface/server-interface.js.map +1 -1
- package/dist/interface/webhooks.d.mts +18 -2
- package/dist/interface/webhooks.d.ts +18 -2
- package/dist/known-errors.d.mts +20 -1
- package/dist/known-errors.d.ts +20 -1
- package/dist/known-errors.js +69 -1
- package/dist/known-errors.js.map +1 -1
- package/dist/schema-fields.d.mts +38 -5
- package/dist/schema-fields.d.ts +38 -5
- package/dist/schema-fields.js +33 -3
- package/dist/schema-fields.js.map +1 -1
- package/dist/sessions.d.mts +35 -4
- package/dist/sessions.d.ts +35 -4
- package/dist/sessions.js +72 -8
- package/dist/sessions.js.map +1 -1
- package/dist/utils/env.d.mts +2 -1
- package/dist/utils/env.d.ts +2 -1
- package/dist/utils/env.js +13 -1
- package/dist/utils/env.js.map +1 -1
- package/dist/utils/esbuild.js +49 -20
- package/dist/utils/esbuild.js.map +1 -1
- package/dist/utils/globals.d.mts +6 -1
- package/dist/utils/globals.d.ts +6 -1
- package/dist/utils/globals.js +13 -0
- package/dist/utils/globals.js.map +1 -1
- package/dist/utils/paginated-lists.d.mts +269 -12
- package/dist/utils/paginated-lists.d.ts +269 -12
- package/dist/utils/paginated-lists.js +153 -23
- package/dist/utils/paginated-lists.js.map +1 -1
- package/dist/utils/paginated-lists.test.d.mts +2 -0
- package/dist/utils/paginated-lists.test.d.ts +2 -0
- package/dist/utils/paginated-lists.test.js +844 -0
- package/dist/utils/paginated-lists.test.js.map +1 -0
- package/dist/utils/proxies.d.mts +8 -1
- package/dist/utils/proxies.d.ts +8 -1
- package/dist/utils/proxies.js +30 -2
- package/dist/utils/proxies.js.map +1 -1
- package/dist/utils/react.d.mts +1 -1
- package/dist/utils/react.d.ts +1 -1
- package/dist/utils/react.js +7 -3
- package/dist/utils/react.js.map +1 -1
- package/dist/utils/results.d.mts +5 -5
- package/dist/utils/results.d.ts +5 -5
- package/dist/utils/results.js.map +1 -1
- package/package.json +5 -4
- package/CHANGELOG.md +0 -1354
- package/dist/esm/interface/crud/config.js +0 -40
- package/dist/esm/interface/crud/config.js.map +0 -1
- package/dist/interface/crud/config.d.mts +0 -49
- package/dist/interface/crud/config.d.ts +0 -49
- package/dist/interface/crud/config.js +0 -79
- package/dist/interface/crud/config.js.map +0 -1
|
@@ -27,7 +27,8 @@ type ImplQueryOptions<Type extends 'next' | 'prev', Cursor, Filter, OrderBy> = Q
|
|
|
27
27
|
type QueryResult<Item, Cursor> = {
|
|
28
28
|
items: {
|
|
29
29
|
item: Item;
|
|
30
|
-
|
|
30
|
+
prevCursor: Cursor;
|
|
31
|
+
nextCursor: Cursor;
|
|
31
32
|
}[];
|
|
32
33
|
isFirst: boolean;
|
|
33
34
|
isLast: boolean;
|
|
@@ -36,30 +37,99 @@ type QueryResult<Item, Cursor> = {
|
|
|
36
37
|
type ImplQueryResult<Item, Cursor> = {
|
|
37
38
|
items: {
|
|
38
39
|
item: Item;
|
|
39
|
-
|
|
40
|
+
prevCursor: Cursor;
|
|
41
|
+
nextCursor: Cursor;
|
|
40
42
|
}[];
|
|
41
43
|
isFirst: boolean;
|
|
42
44
|
isLast: boolean;
|
|
43
45
|
cursor: Cursor;
|
|
44
46
|
};
|
|
47
|
+
/**
|
|
48
|
+
* Abstract base class for cursor-based pagination over any ordered data source.
|
|
49
|
+
*
|
|
50
|
+
* Subclasses implement `_nextOrPrev` to fetch items in one direction. This class handles
|
|
51
|
+
* limit enforcement, sorting validation, and provides `map`, `filter`, `flatMap`, and `merge` utilities.
|
|
52
|
+
*
|
|
53
|
+
* @template Item - The type of items in the list
|
|
54
|
+
* @template Cursor - A string-based cursor type for position tracking. Cursors are always between two items in the list. Note that cursors may not be stable if the filter or orderBy changes.
|
|
55
|
+
* @template Filter - Query filter type
|
|
56
|
+
* @template OrderBy - Sort order specification type
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* ```ts
|
|
60
|
+
* // Basic usage: paginate through users
|
|
61
|
+
* const users = new MyUserList();
|
|
62
|
+
* const first10 = await users.next({ after: users.getFirstCursor(), limit: 10, filter: {}, orderBy: 'name', limitPrecision: 'exact' });
|
|
63
|
+
* // first10 = { items: [...], isFirst: true, isLast: false, cursor: "cursor-after-item-10" }
|
|
64
|
+
*
|
|
65
|
+
* const next10 = await users.next({ after: first10.cursor, limit: 10, filter: {}, orderBy: 'name', limitPrecision: 'exact' });
|
|
66
|
+
* // Continues from where we left off
|
|
67
|
+
* ```
|
|
68
|
+
*/
|
|
45
69
|
declare abstract class PaginatedList<Item, Cursor extends string, Filter extends unknown, OrderBy extends unknown> {
|
|
46
70
|
protected abstract _getFirstCursor(): Cursor;
|
|
47
71
|
protected abstract _getLastCursor(): Cursor;
|
|
48
72
|
protected abstract _compare(orderBy: OrderBy, a: Item, b: Item): number;
|
|
49
73
|
protected abstract _nextOrPrev(type: 'next' | 'prev', options: ImplQueryOptions<'next' | 'prev', Cursor, Filter, OrderBy>): Promise<ImplQueryResult<Item, Cursor>>;
|
|
74
|
+
/** Returns the cursor pointing to the start of the list (before any items). */
|
|
50
75
|
getFirstCursor(): Cursor;
|
|
76
|
+
/** Returns the cursor pointing to the end of the list (after all items). */
|
|
51
77
|
getLastCursor(): Cursor;
|
|
78
|
+
/** Compares two items according to the given orderBy. Returns negative if a < b, 0 if equal, positive if a > b. */
|
|
52
79
|
compare(orderBy: OrderBy, a: Item, b: Item): number;
|
|
80
|
+
/**
|
|
81
|
+
* Fetches items moving forward ('next') or backward ('prev') from the given cursor.
|
|
82
|
+
*
|
|
83
|
+
* Respects `limitPrecision`: 'exact' guarantees the exact limit, 'at-least'/'at-most'/'approximate'
|
|
84
|
+
* allow flexibility for performance. Returns items, boundary flags, and a new cursor.
|
|
85
|
+
*
|
|
86
|
+
* @example
|
|
87
|
+
* ```ts
|
|
88
|
+
* // Get 5 items after the start
|
|
89
|
+
* const result = await list.nextOrPrev('next', { cursor: list.getFirstCursor(), limit: 5, filter: {}, orderBy: 'asc', limitPrecision: 'exact' });
|
|
90
|
+
* // result.items.length === 5 (or less if list has fewer items)
|
|
91
|
+
* // result.isFirst === true (started at first cursor)
|
|
92
|
+
* // result.isLast === true if we got all remaining items
|
|
93
|
+
*
|
|
94
|
+
* // Continue from where we left off
|
|
95
|
+
* const more = await list.nextOrPrev('next', { cursor: result.cursor, limit: 5, ... });
|
|
96
|
+
* ```
|
|
97
|
+
*/
|
|
53
98
|
nextOrPrev(type: 'next' | 'prev', options: QueryOptions<'next' | 'prev', Cursor, Filter, OrderBy>): Promise<QueryResult<Item, Cursor>>;
|
|
99
|
+
/** Fetches items after the given cursor (forward pagination). */
|
|
54
100
|
next({ after, ...rest }: QueryOptions<'next', Cursor, Filter, OrderBy>): Promise<QueryResult<Item, Cursor>>;
|
|
101
|
+
/** Fetches items before the given cursor (backward pagination). */
|
|
55
102
|
prev({ before, ...rest }: QueryOptions<'prev', Cursor, Filter, OrderBy>): Promise<QueryResult<Item, Cursor>>;
|
|
103
|
+
/**
|
|
104
|
+
* Transforms this list by mapping each item to zero or more new items.
|
|
105
|
+
*
|
|
106
|
+
* Note that the sort order must be preserved after the operation; the flat-mapped list will not be sorted automatically.
|
|
107
|
+
*
|
|
108
|
+
* @param itemMapper - Maps each item (with its cursor) to an array of new items
|
|
109
|
+
* @param compare - Comparison function for the new item type
|
|
110
|
+
* @param newCursorFromOldCursor/oldCursorFromNewCursor - Cursor conversion functions
|
|
111
|
+
* @param estimateItemsToFetch - Estimates how many source items to fetch for a given limit
|
|
112
|
+
*
|
|
113
|
+
* @example
|
|
114
|
+
* ```ts
|
|
115
|
+
* // Expand orders into line items (1 order -> N line items)
|
|
116
|
+
* const lineItems = ordersList.flatMap({
|
|
117
|
+
* itemMapper: ({ item: order }) => order.lineItems.map((li, i) => ({ item: li, prevCursor: `${order.id}-${i}`, nextCursor: `${order.id}-${i + 1}` })),
|
|
118
|
+
* compare: (_, a, b) => a.createdAt - b.createdAt,
|
|
119
|
+
* estimateItemsToFetch: ({ limit }) => Math.ceil(limit / 3), // avg 3 items per order
|
|
120
|
+
* // ... cursor converters
|
|
121
|
+
* });
|
|
122
|
+
* ```
|
|
123
|
+
*/
|
|
56
124
|
flatMap<Item2, Cursor2 extends string, Filter2 extends unknown, OrderBy2 extends unknown>(options: {
|
|
57
125
|
itemMapper: (itemEntry: {
|
|
58
126
|
item: Item;
|
|
59
|
-
|
|
127
|
+
prevCursor: Cursor;
|
|
128
|
+
nextCursor: Cursor;
|
|
60
129
|
}, filter: Filter2, orderBy: OrderBy2) => {
|
|
61
130
|
item: Item2;
|
|
62
|
-
|
|
131
|
+
prevCursor: Cursor2;
|
|
132
|
+
nextCursor: Cursor2;
|
|
63
133
|
}[];
|
|
64
134
|
compare: (orderBy: OrderBy2, a: Item2, b: Item2) => number;
|
|
65
135
|
newCursorFromOldCursor: (cursor: Cursor) => Cursor2;
|
|
@@ -72,12 +142,45 @@ declare abstract class PaginatedList<Item, Cursor extends string, Filter extends
|
|
|
72
142
|
limit: number;
|
|
73
143
|
}) => number;
|
|
74
144
|
}): PaginatedList<Item2, Cursor2, Filter2, OrderBy2>;
|
|
145
|
+
/**
|
|
146
|
+
* Transforms each item in the list. Requires a reverse mapper for comparison delegation.
|
|
147
|
+
*
|
|
148
|
+
* @param itemMapper - Transforms each item
|
|
149
|
+
* @param oldItemFromNewItem - Reverse-maps new items back to old items (for comparison)
|
|
150
|
+
*
|
|
151
|
+
* @example
|
|
152
|
+
* ```ts
|
|
153
|
+
* // Convert User objects to UserDTO
|
|
154
|
+
* const userDtos = usersList.map({
|
|
155
|
+
* itemMapper: (user) => ({ id: user.id, displayName: user.name }),
|
|
156
|
+
* oldItemFromNewItem: (dto) => fullUsers.get(dto.id)!, // for comparison
|
|
157
|
+
* oldFilterFromNewFilter: (f) => f,
|
|
158
|
+
* oldOrderByFromNewOrderBy: (o) => o,
|
|
159
|
+
* });
|
|
160
|
+
* ```
|
|
161
|
+
*/
|
|
75
162
|
map<Item2, Filter2 extends unknown, OrderBy2 extends unknown>(options: {
|
|
76
163
|
itemMapper: (item: Item) => Item2;
|
|
77
164
|
oldItemFromNewItem: (item: Item2) => Item;
|
|
78
165
|
oldFilterFromNewFilter: (filter: Filter2) => Filter;
|
|
79
166
|
oldOrderByFromNewOrderBy: (orderBy: OrderBy2) => OrderBy;
|
|
80
167
|
}): PaginatedList<Item2, Cursor, Filter2, OrderBy2>;
|
|
168
|
+
/**
|
|
169
|
+
* Filters items in the list. Requires an estimate function since filtering may reduce output.
|
|
170
|
+
*
|
|
171
|
+
* @param filter - Predicate to include/exclude items
|
|
172
|
+
* @param estimateItemsToFetch - Estimates how many source items to fetch (accounts for filter selectivity)
|
|
173
|
+
*
|
|
174
|
+
* @example
|
|
175
|
+
* ```ts
|
|
176
|
+
* // Filter to only active users
|
|
177
|
+
* const activeUsers = usersList.filter({
|
|
178
|
+
* filter: (user, filterOpts) => user.isActive && user.role === filterOpts.role,
|
|
179
|
+
* oldFilterFromNewFilter: (f) => ({}), // original list has no filter
|
|
180
|
+
* estimateItemsToFetch: ({ limit }) => limit * 2, // expect ~50% active
|
|
181
|
+
* });
|
|
182
|
+
* ```
|
|
183
|
+
*/
|
|
81
184
|
filter<Filter2 extends unknown>(options: {
|
|
82
185
|
filter: (item: Item, filter: Filter2) => boolean;
|
|
83
186
|
oldFilterFromNewFilter: (filter: Filter2) => Filter;
|
|
@@ -87,6 +190,20 @@ declare abstract class PaginatedList<Item, Cursor extends string, Filter extends
|
|
|
87
190
|
limit: number;
|
|
88
191
|
}) => number;
|
|
89
192
|
}): PaginatedList<Item, Cursor, Filter2, OrderBy>;
|
|
193
|
+
/**
|
|
194
|
+
* Adds an additional filter constraint while preserving the original filter type.
|
|
195
|
+
* Shorthand for `filter()` that intersects Filter with AddedFilter.
|
|
196
|
+
*
|
|
197
|
+
* @example
|
|
198
|
+
* ```ts
|
|
199
|
+
* // Add a "verified" filter on top of existing filters
|
|
200
|
+
* const verifiedUsers = usersList.addFilter({
|
|
201
|
+
* filter: (user, f) => user.emailVerified,
|
|
202
|
+
* estimateItemsToFetch: ({ limit }) => limit * 2, // ~50% are verified
|
|
203
|
+
* });
|
|
204
|
+
* // verifiedUsers filter type is Filter
|
|
205
|
+
* ```
|
|
206
|
+
*/
|
|
90
207
|
addFilter<AddedFilter extends unknown>(options: {
|
|
91
208
|
filter: (item: Item, filter: Filter & AddedFilter) => boolean;
|
|
92
209
|
estimateItemsToFetch: (options: {
|
|
@@ -95,7 +212,31 @@ declare abstract class PaginatedList<Item, Cursor extends string, Filter extends
|
|
|
95
212
|
limit: number;
|
|
96
213
|
}) => number;
|
|
97
214
|
}): PaginatedList<Item, Cursor, Filter & AddedFilter, OrderBy>;
|
|
215
|
+
/**
|
|
216
|
+
* Merges multiple paginated lists into one, interleaving items by sort order.
|
|
217
|
+
* All lists must use the same compare function.
|
|
218
|
+
*
|
|
219
|
+
* The merged cursor is a JSON-encoded array of individual list cursors.
|
|
220
|
+
*
|
|
221
|
+
* @example
|
|
222
|
+
* ```ts
|
|
223
|
+
* // Merge users from multiple sources into a unified feed
|
|
224
|
+
* const allUsers = PaginatedList.merge(internalUsers, externalUsers, partnerUsers);
|
|
225
|
+
* const page = await allUsers.next({ after: allUsers.getFirstCursor(), limit: 20, ... });
|
|
226
|
+
* // page.items contains interleaved items from all sources, sorted by orderBy
|
|
227
|
+
* ```
|
|
228
|
+
*/
|
|
98
229
|
static merge<Item, Filter extends unknown, OrderBy extends unknown>(...lists: PaginatedList<Item, any, Filter, OrderBy>[]): PaginatedList<Item, string, Filter, OrderBy>;
|
|
230
|
+
/**
|
|
231
|
+
* Returns an empty paginated list that always returns no items.
|
|
232
|
+
*
|
|
233
|
+
* @example
|
|
234
|
+
* ```ts
|
|
235
|
+
* const empty = PaginatedList.empty();
|
|
236
|
+
* const result = await empty.next({ after: empty.getFirstCursor(), limit: 10, ... });
|
|
237
|
+
* // result = { items: [], isFirst: true, isLast: true, cursor: "first" }
|
|
238
|
+
* ```
|
|
239
|
+
*/
|
|
99
240
|
static empty(): {
|
|
100
241
|
_getFirstCursor(): "first";
|
|
101
242
|
_getLastCursor(): "last";
|
|
@@ -106,19 +247,65 @@ declare abstract class PaginatedList<Item, Cursor extends string, Filter extends
|
|
|
106
247
|
isLast: boolean;
|
|
107
248
|
cursor: "first";
|
|
108
249
|
}>;
|
|
250
|
+
/** Returns the cursor pointing to the start of the list (before any items). */
|
|
109
251
|
getFirstCursor(): "first" | "last";
|
|
252
|
+
/** Returns the cursor pointing to the end of the list (after all items). */
|
|
110
253
|
getLastCursor(): "first" | "last";
|
|
254
|
+
/** Compares two items according to the given orderBy. Returns negative if a < b, 0 if equal, positive if a > b. */
|
|
111
255
|
compare(orderBy: any, a: never, b: never): number;
|
|
256
|
+
/**
|
|
257
|
+
* Fetches items moving forward ('next') or backward ('prev') from the given cursor.
|
|
258
|
+
*
|
|
259
|
+
* Respects `limitPrecision`: 'exact' guarantees the exact limit, 'at-least'/'at-most'/'approximate'
|
|
260
|
+
* allow flexibility for performance. Returns items, boundary flags, and a new cursor.
|
|
261
|
+
*
|
|
262
|
+
* @example
|
|
263
|
+
* ```ts
|
|
264
|
+
* // Get 5 items after the start
|
|
265
|
+
* const result = await list.nextOrPrev('next', { cursor: list.getFirstCursor(), limit: 5, filter: {}, orderBy: 'asc', limitPrecision: 'exact' });
|
|
266
|
+
* // result.items.length === 5 (or less if list has fewer items)
|
|
267
|
+
* // result.isFirst === true (started at first cursor)
|
|
268
|
+
* // result.isLast === true if we got all remaining items
|
|
269
|
+
*
|
|
270
|
+
* // Continue from where we left off
|
|
271
|
+
* const more = await list.nextOrPrev('next', { cursor: result.cursor, limit: 5, ... });
|
|
272
|
+
* ```
|
|
273
|
+
*/
|
|
112
274
|
nextOrPrev(type: "next" | "prev", options: QueryOptions<"next" | "prev", "first" | "last", any, any>): Promise<QueryResult<never, "first" | "last">>;
|
|
275
|
+
/** Fetches items after the given cursor (forward pagination). */
|
|
113
276
|
next({ after, ...rest }: QueryOptions<"next", "first" | "last", any, any>): Promise<QueryResult<never, "first" | "last">>;
|
|
277
|
+
/** Fetches items before the given cursor (backward pagination). */
|
|
114
278
|
prev({ before, ...rest }: QueryOptions<"prev", "first" | "last", any, any>): Promise<QueryResult<never, "first" | "last">>;
|
|
279
|
+
/**
|
|
280
|
+
* Transforms this list by mapping each item to zero or more new items.
|
|
281
|
+
*
|
|
282
|
+
* Note that the sort order must be preserved after the operation; the flat-mapped list will not be sorted automatically.
|
|
283
|
+
*
|
|
284
|
+
* @param itemMapper - Maps each item (with its cursor) to an array of new items
|
|
285
|
+
* @param compare - Comparison function for the new item type
|
|
286
|
+
* @param newCursorFromOldCursor/oldCursorFromNewCursor - Cursor conversion functions
|
|
287
|
+
* @param estimateItemsToFetch - Estimates how many source items to fetch for a given limit
|
|
288
|
+
*
|
|
289
|
+
* @example
|
|
290
|
+
* ```ts
|
|
291
|
+
* // Expand orders into line items (1 order -> N line items)
|
|
292
|
+
* const lineItems = ordersList.flatMap({
|
|
293
|
+
* itemMapper: ({ item: order }) => order.lineItems.map((li, i) => ({ item: li, prevCursor: `${order.id}-${i}`, nextCursor: `${order.id}-${i + 1}` })),
|
|
294
|
+
* compare: (_, a, b) => a.createdAt - b.createdAt,
|
|
295
|
+
* estimateItemsToFetch: ({ limit }) => Math.ceil(limit / 3), // avg 3 items per order
|
|
296
|
+
* // ... cursor converters
|
|
297
|
+
* });
|
|
298
|
+
* ```
|
|
299
|
+
*/
|
|
115
300
|
flatMap<Item2, Cursor2 extends string, Filter2 extends unknown, OrderBy2 extends unknown>(options: {
|
|
116
301
|
itemMapper: (itemEntry: {
|
|
117
302
|
item: never;
|
|
118
|
-
|
|
303
|
+
prevCursor: "first" | "last";
|
|
304
|
+
nextCursor: "first" | "last";
|
|
119
305
|
}, filter: Filter2, orderBy: OrderBy2) => {
|
|
120
306
|
item: Item2;
|
|
121
|
-
|
|
307
|
+
prevCursor: Cursor2;
|
|
308
|
+
nextCursor: Cursor2;
|
|
122
309
|
}[];
|
|
123
310
|
compare: (orderBy: OrderBy2, a: Item2, b: Item2) => number;
|
|
124
311
|
newCursorFromOldCursor: (cursor: "first" | "last") => Cursor2;
|
|
@@ -131,12 +318,45 @@ declare abstract class PaginatedList<Item, Cursor extends string, Filter extends
|
|
|
131
318
|
limit: number;
|
|
132
319
|
}) => number;
|
|
133
320
|
}): PaginatedList<Item2, Cursor2, Filter2, OrderBy2>;
|
|
321
|
+
/**
|
|
322
|
+
* Transforms each item in the list. Requires a reverse mapper for comparison delegation.
|
|
323
|
+
*
|
|
324
|
+
* @param itemMapper - Transforms each item
|
|
325
|
+
* @param oldItemFromNewItem - Reverse-maps new items back to old items (for comparison)
|
|
326
|
+
*
|
|
327
|
+
* @example
|
|
328
|
+
* ```ts
|
|
329
|
+
* // Convert User objects to UserDTO
|
|
330
|
+
* const userDtos = usersList.map({
|
|
331
|
+
* itemMapper: (user) => ({ id: user.id, displayName: user.name }),
|
|
332
|
+
* oldItemFromNewItem: (dto) => fullUsers.get(dto.id)!, // for comparison
|
|
333
|
+
* oldFilterFromNewFilter: (f) => f,
|
|
334
|
+
* oldOrderByFromNewOrderBy: (o) => o,
|
|
335
|
+
* });
|
|
336
|
+
* ```
|
|
337
|
+
*/
|
|
134
338
|
map<Item2_1, Filter2_1 extends unknown, OrderBy2_1 extends unknown>(options: {
|
|
135
339
|
itemMapper: (item: never) => Item2_1;
|
|
136
340
|
oldItemFromNewItem: (item: Item2_1) => never;
|
|
137
341
|
oldFilterFromNewFilter: (filter: Filter2_1) => any;
|
|
138
342
|
oldOrderByFromNewOrderBy: (orderBy: OrderBy2_1) => any;
|
|
139
343
|
}): PaginatedList<Item2_1, "first" | "last", Filter2_1, OrderBy2_1>;
|
|
344
|
+
/**
|
|
345
|
+
* Filters items in the list. Requires an estimate function since filtering may reduce output.
|
|
346
|
+
*
|
|
347
|
+
* @param filter - Predicate to include/exclude items
|
|
348
|
+
* @param estimateItemsToFetch - Estimates how many source items to fetch (accounts for filter selectivity)
|
|
349
|
+
*
|
|
350
|
+
* @example
|
|
351
|
+
* ```ts
|
|
352
|
+
* // Filter to only active users
|
|
353
|
+
* const activeUsers = usersList.filter({
|
|
354
|
+
* filter: (user, filterOpts) => user.isActive && user.role === filterOpts.role,
|
|
355
|
+
* oldFilterFromNewFilter: (f) => ({}), // original list has no filter
|
|
356
|
+
* estimateItemsToFetch: ({ limit }) => limit * 2, // expect ~50% active
|
|
357
|
+
* });
|
|
358
|
+
* ```
|
|
359
|
+
*/
|
|
140
360
|
filter<Filter2_2 extends unknown>(options: {
|
|
141
361
|
filter: (item: never, filter: Filter2_2) => boolean;
|
|
142
362
|
oldFilterFromNewFilter: (filter: Filter2_2) => any;
|
|
@@ -146,6 +366,20 @@ declare abstract class PaginatedList<Item, Cursor extends string, Filter extends
|
|
|
146
366
|
limit: number;
|
|
147
367
|
}) => number;
|
|
148
368
|
}): PaginatedList<never, "first" | "last", Filter2_2, any>;
|
|
369
|
+
/**
|
|
370
|
+
* Adds an additional filter constraint while preserving the original filter type.
|
|
371
|
+
* Shorthand for `filter()` that intersects Filter with AddedFilter.
|
|
372
|
+
*
|
|
373
|
+
* @example
|
|
374
|
+
* ```ts
|
|
375
|
+
* // Add a "verified" filter on top of existing filters
|
|
376
|
+
* const verifiedUsers = usersList.addFilter({
|
|
377
|
+
* filter: (user, f) => user.emailVerified,
|
|
378
|
+
* estimateItemsToFetch: ({ limit }) => limit * 2, // ~50% are verified
|
|
379
|
+
* });
|
|
380
|
+
* // verifiedUsers filter type is Filter
|
|
381
|
+
* ```
|
|
382
|
+
*/
|
|
149
383
|
addFilter<AddedFilter extends unknown>(options: {
|
|
150
384
|
filter: (item: never, filter: any) => boolean;
|
|
151
385
|
estimateItemsToFetch: (options: {
|
|
@@ -156,20 +390,43 @@ declare abstract class PaginatedList<Item, Cursor extends string, Filter extends
|
|
|
156
390
|
}): PaginatedList<never, "first" | "last", any, any>;
|
|
157
391
|
};
|
|
158
392
|
}
|
|
159
|
-
|
|
393
|
+
/**
|
|
394
|
+
* A simple in-memory paginated list backed by an array.
|
|
395
|
+
*
|
|
396
|
+
* Filter is a predicate function, OrderBy is a comparator function.
|
|
397
|
+
* Cursors are in the format "before-{index}" representing the position before that index.
|
|
398
|
+
*
|
|
399
|
+
* Note: This implementation re-filters and re-sorts the entire array on each query,
|
|
400
|
+
* so it's only suitable for small datasets.
|
|
401
|
+
*
|
|
402
|
+
* @example
|
|
403
|
+
* ```ts
|
|
404
|
+
* const numbers = new ArrayPaginatedList([5, 2, 8, 1, 9, 3]);
|
|
405
|
+
* const page = await numbers.next({
|
|
406
|
+
* after: "before-0",
|
|
407
|
+
* limit: 3,
|
|
408
|
+
* filter: (n) => n > 2,
|
|
409
|
+
* orderBy: (a, b) => a - b,
|
|
410
|
+
* limitPrecision: 'exact',
|
|
411
|
+
* });
|
|
412
|
+
* // page.items = [{ item: 3, prevCursor: "before-0", nextCursor: "before-1" }, { item: 5, prevCursor: "before-1", nextCursor: "before-2" }, ...]
|
|
413
|
+
* ```
|
|
414
|
+
*/
|
|
415
|
+
declare class ArrayPaginatedList<Item> extends PaginatedList<Item, `before-${number}`, (item: Item) => boolean, (a: Item, b: Item) => number> {
|
|
160
416
|
private readonly array;
|
|
161
417
|
constructor(array: Item[]);
|
|
162
|
-
_getFirstCursor(): "0";
|
|
163
|
-
_getLastCursor():
|
|
418
|
+
_getFirstCursor(): "before-0";
|
|
419
|
+
_getLastCursor(): `before-${number}`;
|
|
164
420
|
_compare(orderBy: (a: Item, b: Item) => number, a: Item, b: Item): number;
|
|
165
|
-
_nextOrPrev(type: 'next' | 'prev', options: ImplQueryOptions<'next' | 'prev',
|
|
421
|
+
_nextOrPrev(type: 'next' | 'prev', options: ImplQueryOptions<'next' | 'prev', `before-${number}`, (item: Item) => boolean, (a: Item, b: Item) => number>): Promise<{
|
|
166
422
|
items: {
|
|
167
423
|
item: Item;
|
|
168
|
-
|
|
424
|
+
prevCursor: `before-${number}`;
|
|
425
|
+
nextCursor: `before-${number}`;
|
|
169
426
|
}[];
|
|
170
427
|
isFirst: boolean;
|
|
171
428
|
isLast: boolean;
|
|
172
|
-
cursor:
|
|
429
|
+
cursor: `before-${number}`;
|
|
173
430
|
}>;
|
|
174
431
|
}
|
|
175
432
|
|