@blaxel/core 0.2.90-dev.181 → 0.2.90-preview.182
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 +2 -37
- package/dist/cjs/.tsbuildinfo +1 -1
- package/dist/cjs/client/sdk.gen.js +12 -63
- package/dist/cjs/common/autoload.js +3 -0
- package/dist/cjs/common/controlPlaneFetch.js +57 -0
- package/dist/cjs/common/h2fetch.js +1 -1
- package/dist/cjs/common/lazyInit.js +5 -4
- package/dist/cjs/common/settings.js +28 -3
- package/dist/cjs/common/settings.test.js +3 -3
- package/dist/cjs/drive/index.js +3 -39
- package/dist/cjs/index.js +0 -1
- package/dist/cjs/jobs/jobs.js +9 -42
- package/dist/cjs/sandbox/client/sdk.gen.js +1 -1
- package/dist/cjs/sandbox/preview.js +3 -8
- package/dist/cjs/sandbox/sandbox.js +4 -41
- package/dist/cjs/types/client/sdk.gen.d.ts +19 -34
- package/dist/cjs/types/client/types.gen.d.ts +22 -704
- package/dist/cjs/types/common/controlPlaneFetch.d.ts +4 -0
- package/dist/cjs/types/common/settings.d.ts +18 -0
- package/dist/cjs/types/drive/index.d.ts +4 -35
- package/dist/cjs/types/index.d.ts +0 -1
- package/dist/cjs/types/jobs/jobs.d.ts +3 -33
- package/dist/cjs/types/sandbox/client/sdk.gen.d.ts +1 -1
- package/dist/cjs/types/sandbox/client/types.gen.d.ts +0 -24
- package/dist/cjs/types/sandbox/preview.d.ts +2 -2
- package/dist/cjs/types/sandbox/sandbox.d.ts +2 -36
- package/dist/cjs/types/volume/index.d.ts +4 -37
- package/dist/cjs/volume/index.js +3 -41
- package/dist/cjs-browser/.tsbuildinfo +1 -1
- package/dist/cjs-browser/client/sdk.gen.js +12 -63
- package/dist/cjs-browser/common/autoload.js +3 -0
- package/dist/cjs-browser/common/controlPlaneFetch.js +57 -0
- package/dist/cjs-browser/common/lazyInit.js +5 -4
- package/dist/cjs-browser/common/settings.js +28 -3
- package/dist/cjs-browser/common/settings.test.js +3 -3
- package/dist/cjs-browser/drive/index.js +3 -39
- package/dist/cjs-browser/index.js +0 -1
- package/dist/cjs-browser/jobs/jobs.js +9 -42
- package/dist/cjs-browser/sandbox/client/sdk.gen.js +1 -1
- package/dist/cjs-browser/sandbox/preview.js +3 -8
- package/dist/cjs-browser/sandbox/sandbox.js +4 -41
- package/dist/cjs-browser/types/client/sdk.gen.d.ts +19 -34
- package/dist/cjs-browser/types/client/types.gen.d.ts +22 -704
- package/dist/cjs-browser/types/common/controlPlaneFetch.d.ts +4 -0
- package/dist/cjs-browser/types/common/settings.d.ts +18 -0
- package/dist/cjs-browser/types/drive/index.d.ts +4 -35
- package/dist/cjs-browser/types/index.d.ts +0 -1
- package/dist/cjs-browser/types/jobs/jobs.d.ts +3 -33
- package/dist/cjs-browser/types/sandbox/client/sdk.gen.d.ts +1 -1
- package/dist/cjs-browser/types/sandbox/client/types.gen.d.ts +0 -24
- package/dist/cjs-browser/types/sandbox/preview.d.ts +2 -2
- package/dist/cjs-browser/types/sandbox/sandbox.d.ts +2 -36
- package/dist/cjs-browser/types/volume/index.d.ts +4 -37
- package/dist/cjs-browser/volume/index.js +3 -41
- package/dist/esm/.tsbuildinfo +1 -1
- package/dist/esm/client/sdk.gen.js +9 -57
- package/dist/esm/common/autoload.js +3 -0
- package/dist/esm/common/controlPlaneFetch.js +51 -0
- package/dist/esm/common/h2fetch.js +1 -1
- package/dist/esm/common/lazyInit.js +5 -4
- package/dist/esm/common/settings.js +28 -3
- package/dist/esm/common/settings.test.js +3 -3
- package/dist/esm/drive/index.js +3 -39
- package/dist/esm/index.js +0 -1
- package/dist/esm/jobs/jobs.js +9 -42
- package/dist/esm/sandbox/client/sdk.gen.js +1 -1
- package/dist/esm/sandbox/preview.js +3 -8
- package/dist/esm/sandbox/sandbox.js +4 -41
- package/dist/esm/volume/index.js +3 -41
- package/dist/esm-browser/.tsbuildinfo +1 -1
- package/dist/esm-browser/client/sdk.gen.js +9 -57
- package/dist/esm-browser/common/autoload.js +3 -0
- package/dist/esm-browser/common/controlPlaneFetch.js +51 -0
- package/dist/esm-browser/common/lazyInit.js +5 -4
- package/dist/esm-browser/common/settings.js +28 -3
- package/dist/esm-browser/common/settings.test.js +3 -3
- package/dist/esm-browser/drive/index.js +3 -39
- package/dist/esm-browser/index.js +0 -1
- package/dist/esm-browser/jobs/jobs.js +9 -42
- package/dist/esm-browser/sandbox/client/sdk.gen.js +1 -1
- package/dist/esm-browser/sandbox/preview.js +3 -8
- package/dist/esm-browser/sandbox/sandbox.js +4 -41
- package/dist/esm-browser/volume/index.js +3 -41
- package/package.json +1 -1
- package/dist/cjs/common/pagination.js +0 -87
- package/dist/cjs/common/pagination.test.js +0 -62
- package/dist/cjs/types/common/pagination.d.ts +0 -35
- package/dist/cjs/types/common/pagination.test.d.ts +0 -1
- package/dist/cjs-browser/common/pagination.js +0 -87
- package/dist/cjs-browser/common/pagination.test.js +0 -62
- package/dist/cjs-browser/types/common/pagination.d.ts +0 -35
- package/dist/cjs-browser/types/common/pagination.test.d.ts +0 -1
- package/dist/esm/common/pagination.js +0 -83
- package/dist/esm/common/pagination.test.js +0 -60
- package/dist/esm-browser/common/pagination.js +0 -83
- package/dist/esm-browser/common/pagination.test.js +0 -60
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.unwrapListData = unwrapListData;
|
|
4
|
-
exports.createPaginatedList = createPaginatedList;
|
|
5
|
-
function unwrapListData(response) {
|
|
6
|
-
if (!response) {
|
|
7
|
-
return [];
|
|
8
|
-
}
|
|
9
|
-
if (Array.isArray(response)) {
|
|
10
|
-
return response;
|
|
11
|
-
}
|
|
12
|
-
return response.data ?? [];
|
|
13
|
-
}
|
|
14
|
-
function unwrapListMeta(response) {
|
|
15
|
-
if (!response || Array.isArray(response)) {
|
|
16
|
-
return { hasMore: false };
|
|
17
|
-
}
|
|
18
|
-
return response.meta ?? { hasMore: false };
|
|
19
|
-
}
|
|
20
|
-
async function createPaginatedList({ response, fetchPage, mapItem, query, seenCursors, }) {
|
|
21
|
-
const meta = unwrapListMeta(response);
|
|
22
|
-
const data = await Promise.all(unwrapListData(response).map(mapItem));
|
|
23
|
-
const cursors = new Set(seenCursors);
|
|
24
|
-
if (query?.cursor) {
|
|
25
|
-
cursors.add(query.cursor);
|
|
26
|
-
}
|
|
27
|
-
const list = {
|
|
28
|
-
data,
|
|
29
|
-
meta,
|
|
30
|
-
get hasMore() {
|
|
31
|
-
return Boolean(meta.hasMore);
|
|
32
|
-
},
|
|
33
|
-
get nextCursor() {
|
|
34
|
-
return meta.nextCursor || undefined;
|
|
35
|
-
},
|
|
36
|
-
async nextPage() {
|
|
37
|
-
const cursor = list.nextCursor;
|
|
38
|
-
if (!cursor) {
|
|
39
|
-
return null;
|
|
40
|
-
}
|
|
41
|
-
if (cursors.has(cursor)) {
|
|
42
|
-
throw new Error("Pagination returned a repeated cursor");
|
|
43
|
-
}
|
|
44
|
-
const nextQuery = { ...(query ?? {}), cursor };
|
|
45
|
-
const nextSeenCursors = new Set(cursors);
|
|
46
|
-
nextSeenCursors.add(cursor);
|
|
47
|
-
return createPaginatedList({
|
|
48
|
-
response: await fetchPage(nextQuery),
|
|
49
|
-
fetchPage,
|
|
50
|
-
mapItem,
|
|
51
|
-
query: nextQuery,
|
|
52
|
-
seenCursors: nextSeenCursors,
|
|
53
|
-
});
|
|
54
|
-
},
|
|
55
|
-
async autoPagingEach(onItem) {
|
|
56
|
-
for await (const item of list) {
|
|
57
|
-
const shouldContinue = await onItem(item);
|
|
58
|
-
if (shouldContinue === false) {
|
|
59
|
-
return;
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
},
|
|
63
|
-
async autoPagingToArray(options) {
|
|
64
|
-
if (!options || !Number.isFinite(options.limit) || options.limit <= 0) {
|
|
65
|
-
throw new Error("autoPagingToArray requires a positive limit");
|
|
66
|
-
}
|
|
67
|
-
const items = [];
|
|
68
|
-
for await (const item of list) {
|
|
69
|
-
items.push(item);
|
|
70
|
-
if (items.length >= options.limit) {
|
|
71
|
-
return items;
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
return items;
|
|
75
|
-
},
|
|
76
|
-
async *[Symbol.asyncIterator]() {
|
|
77
|
-
let page = list;
|
|
78
|
-
while (page) {
|
|
79
|
-
for (const item of page.data) {
|
|
80
|
-
yield item;
|
|
81
|
-
}
|
|
82
|
-
page = await page.nextPage();
|
|
83
|
-
}
|
|
84
|
-
},
|
|
85
|
-
};
|
|
86
|
-
return list;
|
|
87
|
-
}
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const vitest_1 = require("vitest");
|
|
4
|
-
const pagination_js_1 = require("./pagination.js");
|
|
5
|
-
(0, vitest_1.describe)("pagination helpers", () => {
|
|
6
|
-
(0, vitest_1.it)("unwraps legacy array and paginated list responses", () => {
|
|
7
|
-
(0, vitest_1.expect)((0, pagination_js_1.unwrapListData)([{ name: "legacy" }])).toEqual([{ name: "legacy" }]);
|
|
8
|
-
(0, vitest_1.expect)((0, pagination_js_1.unwrapListData)({
|
|
9
|
-
data: [{ name: "paginated" }],
|
|
10
|
-
meta: { hasMore: false },
|
|
11
|
-
})).toEqual([{ name: "paginated" }]);
|
|
12
|
-
});
|
|
13
|
-
(0, vitest_1.it)("returns page data and lets callers request the next page", async () => {
|
|
14
|
-
const cursors = [];
|
|
15
|
-
const fetchPage = async (query) => {
|
|
16
|
-
await Promise.resolve();
|
|
17
|
-
cursors.push(query?.cursor);
|
|
18
|
-
if (!query?.cursor) {
|
|
19
|
-
return {
|
|
20
|
-
data: ["first"],
|
|
21
|
-
meta: { hasMore: true, nextCursor: "next-page" },
|
|
22
|
-
};
|
|
23
|
-
}
|
|
24
|
-
return {
|
|
25
|
-
data: ["second"],
|
|
26
|
-
meta: { hasMore: false },
|
|
27
|
-
};
|
|
28
|
-
};
|
|
29
|
-
const page = await (0, pagination_js_1.createPaginatedList)({
|
|
30
|
-
response: await fetchPage(),
|
|
31
|
-
fetchPage,
|
|
32
|
-
mapItem: (item) => item.toUpperCase(),
|
|
33
|
-
});
|
|
34
|
-
(0, vitest_1.expect)(page.data).toEqual(["FIRST"]);
|
|
35
|
-
(0, vitest_1.expect)(page.hasMore).toBe(true);
|
|
36
|
-
(0, vitest_1.expect)(page.nextCursor).toBe("next-page");
|
|
37
|
-
const nextPage = await page.nextPage();
|
|
38
|
-
(0, vitest_1.expect)(nextPage?.data).toEqual(["SECOND"]);
|
|
39
|
-
(0, vitest_1.expect)(cursors).toEqual([undefined, "next-page"]);
|
|
40
|
-
});
|
|
41
|
-
(0, vitest_1.it)("supports auto paging with an explicit limit", async () => {
|
|
42
|
-
const fetchPage = async (query) => {
|
|
43
|
-
await Promise.resolve();
|
|
44
|
-
if (!query?.cursor) {
|
|
45
|
-
return {
|
|
46
|
-
data: ["first"],
|
|
47
|
-
meta: { hasMore: true, nextCursor: "next-page" },
|
|
48
|
-
};
|
|
49
|
-
}
|
|
50
|
-
return {
|
|
51
|
-
data: ["second"],
|
|
52
|
-
meta: { hasMore: false },
|
|
53
|
-
};
|
|
54
|
-
};
|
|
55
|
-
const page = await (0, pagination_js_1.createPaginatedList)({
|
|
56
|
-
response: await fetchPage(),
|
|
57
|
-
fetchPage,
|
|
58
|
-
mapItem: (item) => item,
|
|
59
|
-
});
|
|
60
|
-
await (0, vitest_1.expect)(page.autoPagingToArray({ limit: 2 })).resolves.toEqual(["first", "second"]);
|
|
61
|
-
});
|
|
62
|
-
});
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
export type PaginatedListMeta = {
|
|
2
|
-
hasMore?: boolean;
|
|
3
|
-
nextCursor?: string;
|
|
4
|
-
total?: number;
|
|
5
|
-
};
|
|
6
|
-
export type CursorPaginationQuery = {
|
|
7
|
-
cursor?: string;
|
|
8
|
-
};
|
|
9
|
-
export type ListResponse<T> = T[] | {
|
|
10
|
-
data?: T[] | null;
|
|
11
|
-
meta?: PaginatedListMeta | null;
|
|
12
|
-
} | null | undefined;
|
|
13
|
-
export type AutoPagingEachCallback<T> = (item: T) => boolean | void | Promise<boolean | void>;
|
|
14
|
-
export type AutoPagingToArrayOptions = {
|
|
15
|
-
limit: number;
|
|
16
|
-
};
|
|
17
|
-
export type PaginatedList<T, TQuery extends CursorPaginationQuery = CursorPaginationQuery> = AsyncIterable<T> & {
|
|
18
|
-
data: T[];
|
|
19
|
-
meta: PaginatedListMeta;
|
|
20
|
-
hasMore: boolean;
|
|
21
|
-
nextCursor?: string;
|
|
22
|
-
nextPage(): Promise<PaginatedList<T, TQuery> | null>;
|
|
23
|
-
autoPagingEach(onItem: AutoPagingEachCallback<T>): Promise<void>;
|
|
24
|
-
autoPagingToArray(options: AutoPagingToArrayOptions): Promise<T[]>;
|
|
25
|
-
};
|
|
26
|
-
type CreatePaginatedListOptions<TRaw, TItem, TQuery extends CursorPaginationQuery> = {
|
|
27
|
-
response: ListResponse<TRaw>;
|
|
28
|
-
fetchPage: (query?: TQuery) => Promise<ListResponse<TRaw>>;
|
|
29
|
-
mapItem: (item: TRaw) => TItem | Promise<TItem>;
|
|
30
|
-
query?: TQuery;
|
|
31
|
-
seenCursors?: Set<string>;
|
|
32
|
-
};
|
|
33
|
-
export declare function unwrapListData<T>(response: ListResponse<T>): T[];
|
|
34
|
-
export declare function createPaginatedList<TRaw, TItem, TQuery extends CursorPaginationQuery = CursorPaginationQuery>({ response, fetchPage, mapItem, query, seenCursors, }: CreatePaginatedListOptions<TRaw, TItem, TQuery>): Promise<PaginatedList<TItem, TQuery>>;
|
|
35
|
-
export {};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.unwrapListData = unwrapListData;
|
|
4
|
-
exports.createPaginatedList = createPaginatedList;
|
|
5
|
-
function unwrapListData(response) {
|
|
6
|
-
if (!response) {
|
|
7
|
-
return [];
|
|
8
|
-
}
|
|
9
|
-
if (Array.isArray(response)) {
|
|
10
|
-
return response;
|
|
11
|
-
}
|
|
12
|
-
return response.data ?? [];
|
|
13
|
-
}
|
|
14
|
-
function unwrapListMeta(response) {
|
|
15
|
-
if (!response || Array.isArray(response)) {
|
|
16
|
-
return { hasMore: false };
|
|
17
|
-
}
|
|
18
|
-
return response.meta ?? { hasMore: false };
|
|
19
|
-
}
|
|
20
|
-
async function createPaginatedList({ response, fetchPage, mapItem, query, seenCursors, }) {
|
|
21
|
-
const meta = unwrapListMeta(response);
|
|
22
|
-
const data = await Promise.all(unwrapListData(response).map(mapItem));
|
|
23
|
-
const cursors = new Set(seenCursors);
|
|
24
|
-
if (query?.cursor) {
|
|
25
|
-
cursors.add(query.cursor);
|
|
26
|
-
}
|
|
27
|
-
const list = {
|
|
28
|
-
data,
|
|
29
|
-
meta,
|
|
30
|
-
get hasMore() {
|
|
31
|
-
return Boolean(meta.hasMore);
|
|
32
|
-
},
|
|
33
|
-
get nextCursor() {
|
|
34
|
-
return meta.nextCursor || undefined;
|
|
35
|
-
},
|
|
36
|
-
async nextPage() {
|
|
37
|
-
const cursor = list.nextCursor;
|
|
38
|
-
if (!cursor) {
|
|
39
|
-
return null;
|
|
40
|
-
}
|
|
41
|
-
if (cursors.has(cursor)) {
|
|
42
|
-
throw new Error("Pagination returned a repeated cursor");
|
|
43
|
-
}
|
|
44
|
-
const nextQuery = { ...(query ?? {}), cursor };
|
|
45
|
-
const nextSeenCursors = new Set(cursors);
|
|
46
|
-
nextSeenCursors.add(cursor);
|
|
47
|
-
return createPaginatedList({
|
|
48
|
-
response: await fetchPage(nextQuery),
|
|
49
|
-
fetchPage,
|
|
50
|
-
mapItem,
|
|
51
|
-
query: nextQuery,
|
|
52
|
-
seenCursors: nextSeenCursors,
|
|
53
|
-
});
|
|
54
|
-
},
|
|
55
|
-
async autoPagingEach(onItem) {
|
|
56
|
-
for await (const item of list) {
|
|
57
|
-
const shouldContinue = await onItem(item);
|
|
58
|
-
if (shouldContinue === false) {
|
|
59
|
-
return;
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
},
|
|
63
|
-
async autoPagingToArray(options) {
|
|
64
|
-
if (!options || !Number.isFinite(options.limit) || options.limit <= 0) {
|
|
65
|
-
throw new Error("autoPagingToArray requires a positive limit");
|
|
66
|
-
}
|
|
67
|
-
const items = [];
|
|
68
|
-
for await (const item of list) {
|
|
69
|
-
items.push(item);
|
|
70
|
-
if (items.length >= options.limit) {
|
|
71
|
-
return items;
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
return items;
|
|
75
|
-
},
|
|
76
|
-
async *[Symbol.asyncIterator]() {
|
|
77
|
-
let page = list;
|
|
78
|
-
while (page) {
|
|
79
|
-
for (const item of page.data) {
|
|
80
|
-
yield item;
|
|
81
|
-
}
|
|
82
|
-
page = await page.nextPage();
|
|
83
|
-
}
|
|
84
|
-
},
|
|
85
|
-
};
|
|
86
|
-
return list;
|
|
87
|
-
}
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const vitest_1 = require("vitest");
|
|
4
|
-
const pagination_js_1 = require("./pagination.js");
|
|
5
|
-
(0, vitest_1.describe)("pagination helpers", () => {
|
|
6
|
-
(0, vitest_1.it)("unwraps legacy array and paginated list responses", () => {
|
|
7
|
-
(0, vitest_1.expect)((0, pagination_js_1.unwrapListData)([{ name: "legacy" }])).toEqual([{ name: "legacy" }]);
|
|
8
|
-
(0, vitest_1.expect)((0, pagination_js_1.unwrapListData)({
|
|
9
|
-
data: [{ name: "paginated" }],
|
|
10
|
-
meta: { hasMore: false },
|
|
11
|
-
})).toEqual([{ name: "paginated" }]);
|
|
12
|
-
});
|
|
13
|
-
(0, vitest_1.it)("returns page data and lets callers request the next page", async () => {
|
|
14
|
-
const cursors = [];
|
|
15
|
-
const fetchPage = async (query) => {
|
|
16
|
-
await Promise.resolve();
|
|
17
|
-
cursors.push(query?.cursor);
|
|
18
|
-
if (!query?.cursor) {
|
|
19
|
-
return {
|
|
20
|
-
data: ["first"],
|
|
21
|
-
meta: { hasMore: true, nextCursor: "next-page" },
|
|
22
|
-
};
|
|
23
|
-
}
|
|
24
|
-
return {
|
|
25
|
-
data: ["second"],
|
|
26
|
-
meta: { hasMore: false },
|
|
27
|
-
};
|
|
28
|
-
};
|
|
29
|
-
const page = await (0, pagination_js_1.createPaginatedList)({
|
|
30
|
-
response: await fetchPage(),
|
|
31
|
-
fetchPage,
|
|
32
|
-
mapItem: (item) => item.toUpperCase(),
|
|
33
|
-
});
|
|
34
|
-
(0, vitest_1.expect)(page.data).toEqual(["FIRST"]);
|
|
35
|
-
(0, vitest_1.expect)(page.hasMore).toBe(true);
|
|
36
|
-
(0, vitest_1.expect)(page.nextCursor).toBe("next-page");
|
|
37
|
-
const nextPage = await page.nextPage();
|
|
38
|
-
(0, vitest_1.expect)(nextPage?.data).toEqual(["SECOND"]);
|
|
39
|
-
(0, vitest_1.expect)(cursors).toEqual([undefined, "next-page"]);
|
|
40
|
-
});
|
|
41
|
-
(0, vitest_1.it)("supports auto paging with an explicit limit", async () => {
|
|
42
|
-
const fetchPage = async (query) => {
|
|
43
|
-
await Promise.resolve();
|
|
44
|
-
if (!query?.cursor) {
|
|
45
|
-
return {
|
|
46
|
-
data: ["first"],
|
|
47
|
-
meta: { hasMore: true, nextCursor: "next-page" },
|
|
48
|
-
};
|
|
49
|
-
}
|
|
50
|
-
return {
|
|
51
|
-
data: ["second"],
|
|
52
|
-
meta: { hasMore: false },
|
|
53
|
-
};
|
|
54
|
-
};
|
|
55
|
-
const page = await (0, pagination_js_1.createPaginatedList)({
|
|
56
|
-
response: await fetchPage(),
|
|
57
|
-
fetchPage,
|
|
58
|
-
mapItem: (item) => item,
|
|
59
|
-
});
|
|
60
|
-
await (0, vitest_1.expect)(page.autoPagingToArray({ limit: 2 })).resolves.toEqual(["first", "second"]);
|
|
61
|
-
});
|
|
62
|
-
});
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
export type PaginatedListMeta = {
|
|
2
|
-
hasMore?: boolean;
|
|
3
|
-
nextCursor?: string;
|
|
4
|
-
total?: number;
|
|
5
|
-
};
|
|
6
|
-
export type CursorPaginationQuery = {
|
|
7
|
-
cursor?: string;
|
|
8
|
-
};
|
|
9
|
-
export type ListResponse<T> = T[] | {
|
|
10
|
-
data?: T[] | null;
|
|
11
|
-
meta?: PaginatedListMeta | null;
|
|
12
|
-
} | null | undefined;
|
|
13
|
-
export type AutoPagingEachCallback<T> = (item: T) => boolean | void | Promise<boolean | void>;
|
|
14
|
-
export type AutoPagingToArrayOptions = {
|
|
15
|
-
limit: number;
|
|
16
|
-
};
|
|
17
|
-
export type PaginatedList<T, TQuery extends CursorPaginationQuery = CursorPaginationQuery> = AsyncIterable<T> & {
|
|
18
|
-
data: T[];
|
|
19
|
-
meta: PaginatedListMeta;
|
|
20
|
-
hasMore: boolean;
|
|
21
|
-
nextCursor?: string;
|
|
22
|
-
nextPage(): Promise<PaginatedList<T, TQuery> | null>;
|
|
23
|
-
autoPagingEach(onItem: AutoPagingEachCallback<T>): Promise<void>;
|
|
24
|
-
autoPagingToArray(options: AutoPagingToArrayOptions): Promise<T[]>;
|
|
25
|
-
};
|
|
26
|
-
type CreatePaginatedListOptions<TRaw, TItem, TQuery extends CursorPaginationQuery> = {
|
|
27
|
-
response: ListResponse<TRaw>;
|
|
28
|
-
fetchPage: (query?: TQuery) => Promise<ListResponse<TRaw>>;
|
|
29
|
-
mapItem: (item: TRaw) => TItem | Promise<TItem>;
|
|
30
|
-
query?: TQuery;
|
|
31
|
-
seenCursors?: Set<string>;
|
|
32
|
-
};
|
|
33
|
-
export declare function unwrapListData<T>(response: ListResponse<T>): T[];
|
|
34
|
-
export declare function createPaginatedList<TRaw, TItem, TQuery extends CursorPaginationQuery = CursorPaginationQuery>({ response, fetchPage, mapItem, query, seenCursors, }: CreatePaginatedListOptions<TRaw, TItem, TQuery>): Promise<PaginatedList<TItem, TQuery>>;
|
|
35
|
-
export {};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
export function unwrapListData(response) {
|
|
2
|
-
if (!response) {
|
|
3
|
-
return [];
|
|
4
|
-
}
|
|
5
|
-
if (Array.isArray(response)) {
|
|
6
|
-
return response;
|
|
7
|
-
}
|
|
8
|
-
return response.data ?? [];
|
|
9
|
-
}
|
|
10
|
-
function unwrapListMeta(response) {
|
|
11
|
-
if (!response || Array.isArray(response)) {
|
|
12
|
-
return { hasMore: false };
|
|
13
|
-
}
|
|
14
|
-
return response.meta ?? { hasMore: false };
|
|
15
|
-
}
|
|
16
|
-
export async function createPaginatedList({ response, fetchPage, mapItem, query, seenCursors, }) {
|
|
17
|
-
const meta = unwrapListMeta(response);
|
|
18
|
-
const data = await Promise.all(unwrapListData(response).map(mapItem));
|
|
19
|
-
const cursors = new Set(seenCursors);
|
|
20
|
-
if (query?.cursor) {
|
|
21
|
-
cursors.add(query.cursor);
|
|
22
|
-
}
|
|
23
|
-
const list = {
|
|
24
|
-
data,
|
|
25
|
-
meta,
|
|
26
|
-
get hasMore() {
|
|
27
|
-
return Boolean(meta.hasMore);
|
|
28
|
-
},
|
|
29
|
-
get nextCursor() {
|
|
30
|
-
return meta.nextCursor || undefined;
|
|
31
|
-
},
|
|
32
|
-
async nextPage() {
|
|
33
|
-
const cursor = list.nextCursor;
|
|
34
|
-
if (!cursor) {
|
|
35
|
-
return null;
|
|
36
|
-
}
|
|
37
|
-
if (cursors.has(cursor)) {
|
|
38
|
-
throw new Error("Pagination returned a repeated cursor");
|
|
39
|
-
}
|
|
40
|
-
const nextQuery = { ...(query ?? {}), cursor };
|
|
41
|
-
const nextSeenCursors = new Set(cursors);
|
|
42
|
-
nextSeenCursors.add(cursor);
|
|
43
|
-
return createPaginatedList({
|
|
44
|
-
response: await fetchPage(nextQuery),
|
|
45
|
-
fetchPage,
|
|
46
|
-
mapItem,
|
|
47
|
-
query: nextQuery,
|
|
48
|
-
seenCursors: nextSeenCursors,
|
|
49
|
-
});
|
|
50
|
-
},
|
|
51
|
-
async autoPagingEach(onItem) {
|
|
52
|
-
for await (const item of list) {
|
|
53
|
-
const shouldContinue = await onItem(item);
|
|
54
|
-
if (shouldContinue === false) {
|
|
55
|
-
return;
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
},
|
|
59
|
-
async autoPagingToArray(options) {
|
|
60
|
-
if (!options || !Number.isFinite(options.limit) || options.limit <= 0) {
|
|
61
|
-
throw new Error("autoPagingToArray requires a positive limit");
|
|
62
|
-
}
|
|
63
|
-
const items = [];
|
|
64
|
-
for await (const item of list) {
|
|
65
|
-
items.push(item);
|
|
66
|
-
if (items.length >= options.limit) {
|
|
67
|
-
return items;
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
return items;
|
|
71
|
-
},
|
|
72
|
-
async *[Symbol.asyncIterator]() {
|
|
73
|
-
let page = list;
|
|
74
|
-
while (page) {
|
|
75
|
-
for (const item of page.data) {
|
|
76
|
-
yield item;
|
|
77
|
-
}
|
|
78
|
-
page = await page.nextPage();
|
|
79
|
-
}
|
|
80
|
-
},
|
|
81
|
-
};
|
|
82
|
-
return list;
|
|
83
|
-
}
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
import { createPaginatedList, unwrapListData } from "./pagination.js";
|
|
3
|
-
describe("pagination helpers", () => {
|
|
4
|
-
it("unwraps legacy array and paginated list responses", () => {
|
|
5
|
-
expect(unwrapListData([{ name: "legacy" }])).toEqual([{ name: "legacy" }]);
|
|
6
|
-
expect(unwrapListData({
|
|
7
|
-
data: [{ name: "paginated" }],
|
|
8
|
-
meta: { hasMore: false },
|
|
9
|
-
})).toEqual([{ name: "paginated" }]);
|
|
10
|
-
});
|
|
11
|
-
it("returns page data and lets callers request the next page", async () => {
|
|
12
|
-
const cursors = [];
|
|
13
|
-
const fetchPage = async (query) => {
|
|
14
|
-
await Promise.resolve();
|
|
15
|
-
cursors.push(query?.cursor);
|
|
16
|
-
if (!query?.cursor) {
|
|
17
|
-
return {
|
|
18
|
-
data: ["first"],
|
|
19
|
-
meta: { hasMore: true, nextCursor: "next-page" },
|
|
20
|
-
};
|
|
21
|
-
}
|
|
22
|
-
return {
|
|
23
|
-
data: ["second"],
|
|
24
|
-
meta: { hasMore: false },
|
|
25
|
-
};
|
|
26
|
-
};
|
|
27
|
-
const page = await createPaginatedList({
|
|
28
|
-
response: await fetchPage(),
|
|
29
|
-
fetchPage,
|
|
30
|
-
mapItem: (item) => item.toUpperCase(),
|
|
31
|
-
});
|
|
32
|
-
expect(page.data).toEqual(["FIRST"]);
|
|
33
|
-
expect(page.hasMore).toBe(true);
|
|
34
|
-
expect(page.nextCursor).toBe("next-page");
|
|
35
|
-
const nextPage = await page.nextPage();
|
|
36
|
-
expect(nextPage?.data).toEqual(["SECOND"]);
|
|
37
|
-
expect(cursors).toEqual([undefined, "next-page"]);
|
|
38
|
-
});
|
|
39
|
-
it("supports auto paging with an explicit limit", async () => {
|
|
40
|
-
const fetchPage = async (query) => {
|
|
41
|
-
await Promise.resolve();
|
|
42
|
-
if (!query?.cursor) {
|
|
43
|
-
return {
|
|
44
|
-
data: ["first"],
|
|
45
|
-
meta: { hasMore: true, nextCursor: "next-page" },
|
|
46
|
-
};
|
|
47
|
-
}
|
|
48
|
-
return {
|
|
49
|
-
data: ["second"],
|
|
50
|
-
meta: { hasMore: false },
|
|
51
|
-
};
|
|
52
|
-
};
|
|
53
|
-
const page = await createPaginatedList({
|
|
54
|
-
response: await fetchPage(),
|
|
55
|
-
fetchPage,
|
|
56
|
-
mapItem: (item) => item,
|
|
57
|
-
});
|
|
58
|
-
await expect(page.autoPagingToArray({ limit: 2 })).resolves.toEqual(["first", "second"]);
|
|
59
|
-
});
|
|
60
|
-
});
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
export function unwrapListData(response) {
|
|
2
|
-
if (!response) {
|
|
3
|
-
return [];
|
|
4
|
-
}
|
|
5
|
-
if (Array.isArray(response)) {
|
|
6
|
-
return response;
|
|
7
|
-
}
|
|
8
|
-
return response.data ?? [];
|
|
9
|
-
}
|
|
10
|
-
function unwrapListMeta(response) {
|
|
11
|
-
if (!response || Array.isArray(response)) {
|
|
12
|
-
return { hasMore: false };
|
|
13
|
-
}
|
|
14
|
-
return response.meta ?? { hasMore: false };
|
|
15
|
-
}
|
|
16
|
-
export async function createPaginatedList({ response, fetchPage, mapItem, query, seenCursors, }) {
|
|
17
|
-
const meta = unwrapListMeta(response);
|
|
18
|
-
const data = await Promise.all(unwrapListData(response).map(mapItem));
|
|
19
|
-
const cursors = new Set(seenCursors);
|
|
20
|
-
if (query?.cursor) {
|
|
21
|
-
cursors.add(query.cursor);
|
|
22
|
-
}
|
|
23
|
-
const list = {
|
|
24
|
-
data,
|
|
25
|
-
meta,
|
|
26
|
-
get hasMore() {
|
|
27
|
-
return Boolean(meta.hasMore);
|
|
28
|
-
},
|
|
29
|
-
get nextCursor() {
|
|
30
|
-
return meta.nextCursor || undefined;
|
|
31
|
-
},
|
|
32
|
-
async nextPage() {
|
|
33
|
-
const cursor = list.nextCursor;
|
|
34
|
-
if (!cursor) {
|
|
35
|
-
return null;
|
|
36
|
-
}
|
|
37
|
-
if (cursors.has(cursor)) {
|
|
38
|
-
throw new Error("Pagination returned a repeated cursor");
|
|
39
|
-
}
|
|
40
|
-
const nextQuery = { ...(query ?? {}), cursor };
|
|
41
|
-
const nextSeenCursors = new Set(cursors);
|
|
42
|
-
nextSeenCursors.add(cursor);
|
|
43
|
-
return createPaginatedList({
|
|
44
|
-
response: await fetchPage(nextQuery),
|
|
45
|
-
fetchPage,
|
|
46
|
-
mapItem,
|
|
47
|
-
query: nextQuery,
|
|
48
|
-
seenCursors: nextSeenCursors,
|
|
49
|
-
});
|
|
50
|
-
},
|
|
51
|
-
async autoPagingEach(onItem) {
|
|
52
|
-
for await (const item of list) {
|
|
53
|
-
const shouldContinue = await onItem(item);
|
|
54
|
-
if (shouldContinue === false) {
|
|
55
|
-
return;
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
},
|
|
59
|
-
async autoPagingToArray(options) {
|
|
60
|
-
if (!options || !Number.isFinite(options.limit) || options.limit <= 0) {
|
|
61
|
-
throw new Error("autoPagingToArray requires a positive limit");
|
|
62
|
-
}
|
|
63
|
-
const items = [];
|
|
64
|
-
for await (const item of list) {
|
|
65
|
-
items.push(item);
|
|
66
|
-
if (items.length >= options.limit) {
|
|
67
|
-
return items;
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
return items;
|
|
71
|
-
},
|
|
72
|
-
async *[Symbol.asyncIterator]() {
|
|
73
|
-
let page = list;
|
|
74
|
-
while (page) {
|
|
75
|
-
for (const item of page.data) {
|
|
76
|
-
yield item;
|
|
77
|
-
}
|
|
78
|
-
page = await page.nextPage();
|
|
79
|
-
}
|
|
80
|
-
},
|
|
81
|
-
};
|
|
82
|
-
return list;
|
|
83
|
-
}
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
import { createPaginatedList, unwrapListData } from "./pagination.js";
|
|
3
|
-
describe("pagination helpers", () => {
|
|
4
|
-
it("unwraps legacy array and paginated list responses", () => {
|
|
5
|
-
expect(unwrapListData([{ name: "legacy" }])).toEqual([{ name: "legacy" }]);
|
|
6
|
-
expect(unwrapListData({
|
|
7
|
-
data: [{ name: "paginated" }],
|
|
8
|
-
meta: { hasMore: false },
|
|
9
|
-
})).toEqual([{ name: "paginated" }]);
|
|
10
|
-
});
|
|
11
|
-
it("returns page data and lets callers request the next page", async () => {
|
|
12
|
-
const cursors = [];
|
|
13
|
-
const fetchPage = async (query) => {
|
|
14
|
-
await Promise.resolve();
|
|
15
|
-
cursors.push(query?.cursor);
|
|
16
|
-
if (!query?.cursor) {
|
|
17
|
-
return {
|
|
18
|
-
data: ["first"],
|
|
19
|
-
meta: { hasMore: true, nextCursor: "next-page" },
|
|
20
|
-
};
|
|
21
|
-
}
|
|
22
|
-
return {
|
|
23
|
-
data: ["second"],
|
|
24
|
-
meta: { hasMore: false },
|
|
25
|
-
};
|
|
26
|
-
};
|
|
27
|
-
const page = await createPaginatedList({
|
|
28
|
-
response: await fetchPage(),
|
|
29
|
-
fetchPage,
|
|
30
|
-
mapItem: (item) => item.toUpperCase(),
|
|
31
|
-
});
|
|
32
|
-
expect(page.data).toEqual(["FIRST"]);
|
|
33
|
-
expect(page.hasMore).toBe(true);
|
|
34
|
-
expect(page.nextCursor).toBe("next-page");
|
|
35
|
-
const nextPage = await page.nextPage();
|
|
36
|
-
expect(nextPage?.data).toEqual(["SECOND"]);
|
|
37
|
-
expect(cursors).toEqual([undefined, "next-page"]);
|
|
38
|
-
});
|
|
39
|
-
it("supports auto paging with an explicit limit", async () => {
|
|
40
|
-
const fetchPage = async (query) => {
|
|
41
|
-
await Promise.resolve();
|
|
42
|
-
if (!query?.cursor) {
|
|
43
|
-
return {
|
|
44
|
-
data: ["first"],
|
|
45
|
-
meta: { hasMore: true, nextCursor: "next-page" },
|
|
46
|
-
};
|
|
47
|
-
}
|
|
48
|
-
return {
|
|
49
|
-
data: ["second"],
|
|
50
|
-
meta: { hasMore: false },
|
|
51
|
-
};
|
|
52
|
-
};
|
|
53
|
-
const page = await createPaginatedList({
|
|
54
|
-
response: await fetchPage(),
|
|
55
|
-
fetchPage,
|
|
56
|
-
mapItem: (item) => item,
|
|
57
|
-
});
|
|
58
|
-
await expect(page.autoPagingToArray({ limit: 2 })).resolves.toEqual(["first", "second"]);
|
|
59
|
-
});
|
|
60
|
-
});
|