From ee12164835d2e1ed0a1489a93faffd4ad1c55581 Mon Sep 17 00:00:00 2001 From: Vineeth Voruganti <13438633+VVoruganti@users.noreply.github.com> Date: Tue, 13 Jan 2026 11:48:18 -0500 Subject: [PATCH] fix: Maintain this binding for pagination (#289) --- sdks/typescript/__tests__/integration.test.ts | 11 +- sdks/typescript/__tests__/pagination.test.ts | 422 +++++++++--------- sdks/typescript/src/pagination.ts | 9 +- 3 files changed, 221 insertions(+), 221 deletions(-) diff --git a/sdks/typescript/__tests__/integration.test.ts b/sdks/typescript/__tests__/integration.test.ts index 96fded3a..d08914f7 100644 --- a/sdks/typescript/__tests__/integration.test.ts +++ b/sdks/typescript/__tests__/integration.test.ts @@ -183,7 +183,7 @@ describe('Honcho SDK Integration Tests', () => { total: 2, size: 2, hasNextPage: false, - [Symbol.asyncIterator]: async function* () { + [Symbol.asyncIterator]: async function*() { for (const item of this.items) { yield item } @@ -309,9 +309,9 @@ describe('Honcho SDK Integration Tests', () => { size: 2, page: 1, pages: 2, - hasNextPage: true, + hasNextPage: () => true, getNextPage: jest.fn(), - [Symbol.asyncIterator]: async function* () { + [Symbol.asyncIterator]: async function*() { for (const item of this.items) { yield item } @@ -327,9 +327,9 @@ describe('Honcho SDK Integration Tests', () => { size: 2, page: 2, pages: 2, - hasNextPage: false, + hasNextPage: () => false, getNextPage: jest.fn().mockResolvedValue(null), - [Symbol.asyncIterator]: async function* () { + [Symbol.asyncIterator]: async function*() { for (const item of this.items) { yield item } @@ -381,6 +381,7 @@ describe('Honcho SDK Integration Tests', () => { const bob = await honcho.peer('bob') // Test working representation without target + const globalRep = await session.getRepresentation('alice') expect(globalRep).toBe(mockRepresentation) expect( diff --git a/sdks/typescript/__tests__/pagination.test.ts b/sdks/typescript/__tests__/pagination.test.ts index a9385206..2252b3d6 100644 --- a/sdks/typescript/__tests__/pagination.test.ts +++ b/sdks/typescript/__tests__/pagination.test.ts @@ -1,15 +1,15 @@ -import { Page } from '../src/pagination' +import { Page } from "../src/pagination"; -describe('Page', () => { - let mockOriginalPage: any - let mockItems: any[] +describe("Page", () => { + let mockOriginalPage: any; + let mockItems: any[]; beforeEach(() => { mockItems = [ - { id: 'item1', name: 'Item 1' }, - { id: 'item2', name: 'Item 2' }, - { id: 'item3', name: 'Item 3' }, - ] + { id: "item1", name: "Item 1" }, + { id: "item2", name: "Item 2" }, + { id: "item3", name: "Item 3" }, + ]; mockOriginalPage = { items: mockItems, @@ -17,231 +17,231 @@ describe('Page', () => { total: 10, page: 1, pages: 4, - hasNextPage: true, + hasNextPage: () => true, [Symbol.asyncIterator]: async function*() { for (const item of mockItems) { - yield item + yield item; } }, - } as any - }) + } as any; + }); - describe('constructor', () => { - it('should initialize with original page', () => { - const page = new Page(mockOriginalPage) + describe("constructor", () => { + it("should initialize with original page", () => { + const page = new Page(mockOriginalPage); - expect(page['_originalPage']).toBe(mockOriginalPage) - expect(page['_transformFunc']).toBeUndefined() - }) + expect(page["_originalPage"]).toBe(mockOriginalPage); + expect(page["_transformFunc"]).toBeUndefined(); + }); - it('should initialize with transform function', () => { - const transformFunc = (item: any) => ({ ...item, transformed: true }) - const page = new Page(mockOriginalPage, transformFunc) + it("should initialize with transform function", () => { + const transformFunc = (item: any) => ({ ...item, transformed: true }); + const page = new Page(mockOriginalPage, transformFunc); - expect(page['_originalPage']).toBe(mockOriginalPage) - expect(page['_transformFunc']).toBe(transformFunc) - }) - }) + expect(page["_originalPage"]).toBe(mockOriginalPage); + expect(page["_transformFunc"]).toBe(transformFunc); + }); + }); - describe('Symbol.asyncIterator', () => { - it('should iterate through items without transform', async () => { - const page = new Page(mockOriginalPage) - const items: any[] = [] + describe("Symbol.asyncIterator", () => { + it("should iterate through items without transform", async () => { + const page = new Page(mockOriginalPage); + const items: any[] = []; for await (const item of page) { - items.push(item) + items.push(item); } - expect(items).toEqual(mockItems) - }) + expect(items).toEqual(mockItems); + }); - it('should iterate through items with transform', async () => { - const transformFunc = (item: any) => ({ ...item, transformed: true }) - const page = new Page(mockOriginalPage, transformFunc) - const items: any[] = [] + it("should iterate through items with transform", async () => { + const transformFunc = (item: any) => ({ ...item, transformed: true }); + const page = new Page(mockOriginalPage, transformFunc); + const items: any[] = []; for await (const item of page) { - items.push(item) + items.push(item); } expect(items).toEqual([ - { id: 'item1', name: 'Item 1', transformed: true }, - { id: 'item2', name: 'Item 2', transformed: true }, - { id: 'item3', name: 'Item 3', transformed: true }, - ]) - }) + { id: "item1", name: "Item 1", transformed: true }, + { id: "item2", name: "Item 2", transformed: true }, + { id: "item3", name: "Item 3", transformed: true }, + ]); + }); - it('should handle transform function that throws error', async () => { + it("should handle transform function that throws error", async () => { const errorTransform = () => { - throw new Error('Transform error') - } - const page = new Page(mockOriginalPage, errorTransform) + throw new Error("Transform error"); + }; + const page = new Page(mockOriginalPage, errorTransform); const iterate = async () => { for await (const item of page) { // This should throw } - } + }; - await expect(iterate()).rejects.toThrow('Transform error') - }) - }) + await expect(iterate()).rejects.toThrow("Transform error"); + }); + }); - describe('get', () => { - it('should get item by index without transform', () => { - const page = new Page(mockOriginalPage) + describe("get", () => { + it("should get item by index without transform", () => { + const page = new Page(mockOriginalPage); - const item = page.get(1) + const item = page.get(1); - expect(item).toEqual(mockItems[1]) - }) + expect(item).toEqual(mockItems[1]); + }); - it('should get item by index with transform', () => { - const transformFunc = (item: any) => ({ ...item, transformed: true }) - const page = new Page(mockOriginalPage, transformFunc) + it("should get item by index with transform", () => { + const transformFunc = (item: any) => ({ ...item, transformed: true }); + const page = new Page(mockOriginalPage, transformFunc); - const item = page.get(1) + const item = page.get(1); - expect(item).toEqual({ ...mockItems[1], transformed: true }) - }) + expect(item).toEqual({ ...mockItems[1], transformed: true }); + }); - it('should handle out of bounds index', () => { - const page = new Page(mockOriginalPage) + it("should handle out of bounds index", () => { + const page = new Page(mockOriginalPage); expect(() => page.get(999)).toThrow( - 'Index 999 is out of bounds for page with 3 items' - ) - }) - }) + "Index 999 is out of bounds for page with 3 items", + ); + }); + }); - describe('length getter', () => { - it('should return length of items array', () => { - const page = new Page(mockOriginalPage) + describe("length getter", () => { + it("should return length of items array", () => { + const page = new Page(mockOriginalPage); - expect(page.length).toBe(3) - }) + expect(page.length).toBe(3); + }); - it('should handle empty items array', () => { - const emptyPage = { ...mockOriginalPage, items: [] } - const page = new Page(emptyPage) + it("should handle empty items array", () => { + const emptyPage = { ...mockOriginalPage, items: [] }; + const page = new Page(emptyPage); - expect(page.length).toBe(0) - }) + expect(page.length).toBe(0); + }); - it('should handle undefined items', () => { - const noItemsPage = { ...mockOriginalPage, items: undefined } - const page = new Page(noItemsPage) + it("should handle undefined items", () => { + const noItemsPage = { ...mockOriginalPage, items: undefined }; + const page = new Page(noItemsPage); - expect(page.length).toBe(0) - }) - }) + expect(page.length).toBe(0); + }); + }); - describe('items getter', () => { - it('should return items array without transform', () => { - const page = new Page(mockOriginalPage) + describe("items getter", () => { + it("should return items array without transform", () => { + const page = new Page(mockOriginalPage); - const data = page.items + const data = page.items; - expect(data).toEqual(mockItems) - }) + expect(data).toEqual(mockItems); + }); - it('should return items array with transform', () => { - const transformFunc = (item: any) => ({ ...item, transformed: true }) - const page = new Page(mockOriginalPage, transformFunc) + it("should return items array with transform", () => { + const transformFunc = (item: any) => ({ ...item, transformed: true }); + const page = new Page(mockOriginalPage, transformFunc); - const data = page.items + const data = page.items; expect(data).toEqual([ - { id: 'item1', name: 'Item 1', transformed: true }, - { id: 'item2', name: 'Item 2', transformed: true }, - { id: 'item3', name: 'Item 3', transformed: true }, - ]) - }) + { id: "item1", name: "Item 1", transformed: true }, + { id: "item2", name: "Item 2", transformed: true }, + { id: "item3", name: "Item 3", transformed: true }, + ]); + }); - it('should handle transform function returning null', () => { - const nullTransform = () => null - const page = new Page(mockOriginalPage, nullTransform) + it("should handle transform function returning null", () => { + const nullTransform = () => null; + const page = new Page(mockOriginalPage, nullTransform); - const data = page.items + const data = page.items; - expect(data).toEqual([null, null, null]) - }) - }) + expect(data).toEqual([null, null, null]); + }); + }); - describe('pagination metadata getters', () => { - it('should return total from original page', () => { - const page = new Page(mockOriginalPage) + describe("pagination metadata getters", () => { + it("should return total from original page", () => { + const page = new Page(mockOriginalPage); - expect(page.total).toBe(10) - }) + expect(page.total).toBe(10); + }); - it('should return page number from original page', () => { - const page = new Page(mockOriginalPage) + it("should return page number from original page", () => { + const page = new Page(mockOriginalPage); - expect(page.page).toBe(1) - }) + expect(page.page).toBe(1); + }); - it('should return size from original page', () => { - const page = new Page(mockOriginalPage) + it("should return size from original page", () => { + const page = new Page(mockOriginalPage); - expect(page.size).toBe(3) - }) + expect(page.size).toBe(3); + }); - it('should return pages from original page', () => { - const page = new Page(mockOriginalPage) + it("should return pages from original page", () => { + const page = new Page(mockOriginalPage); - expect(page.pages).toBe(4) - }) + expect(page.pages).toBe(4); + }); - it('should handle undefined metadata', () => { - const minimalPage = { items: mockItems } - const page = new Page(minimalPage as any) + it("should handle undefined metadata", () => { + const minimalPage = { items: mockItems }; + const page = new Page(minimalPage as any); - expect(page.total).toBeUndefined() - expect(page.page).toBeUndefined() - expect(page.size).toBeUndefined() - expect(page.pages).toBeUndefined() - }) - }) + expect(page.total).toBeUndefined(); + expect(page.page).toBeUndefined(); + expect(page.size).toBeUndefined(); + expect(page.pages).toBeUndefined(); + }); + }); - describe('hasNextPage getter', () => { - it('should return true when hasNextPage is true', () => { - const page = new Page(mockOriginalPage) + describe("hasNextPage getter", () => { + it("should return true when hasNextPage is true", () => { + const page = new Page(mockOriginalPage); - expect(page.hasNextPage).toBe(true) - }) + expect(page.hasNextPage).toBe(true); + }); - it('should return false when hasNextPage is false', () => { - const lastPage = { ...mockOriginalPage, hasNextPage: false } - const page = new Page(lastPage) + it("should return false when hasNextPage is false", () => { + const lastPage = { ...mockOriginalPage, hasNextPage: false }; + const page = new Page(lastPage); - expect(page.hasNextPage).toBe(false) - }) + expect(page.hasNextPage).toBe(false); + }); - it('should call hasNextPage function if it is a function', () => { - const hasNextPageFn = jest.fn(() => true) - const pageWithFn = { ...mockOriginalPage, hasNextPage: hasNextPageFn } - const page = new Page(pageWithFn) + it("should call hasNextPage function if it is a function", () => { + const hasNextPageFn = jest.fn(() => true); + const pageWithFn = { ...mockOriginalPage, hasNextPage: hasNextPageFn }; + const page = new Page(pageWithFn); - const result = page.hasNextPage + const result = page.hasNextPage; - expect(result).toBe(true) - expect(hasNextPageFn).toHaveBeenCalled() - }) + expect(result).toBe(true); + expect(hasNextPageFn).toHaveBeenCalled(); + }); - it('should return false when hasNextPage is undefined', () => { - const noNextPage = { ...mockOriginalPage } - delete noNextPage.hasNextPage - const page = new Page(noNextPage) + it("should return false when hasNextPage is undefined", () => { + const noNextPage = { ...mockOriginalPage }; + delete noNextPage.hasNextPage; + const page = new Page(noNextPage); - expect(page.hasNextPage).toBe(false) - }) - }) + expect(page.hasNextPage).toBe(false); + }); + }); - describe('getNextPage', () => { - it('should return next page with same transform function', async () => { + describe("getNextPage", () => { + it("should return next page with same transform function", async () => { const nextPageData = { - items: [{ id: 'item4', name: 'Item 4' }], + items: [{ id: "item4", name: "Item 4" }], size: 1, total: 10, page: 2, @@ -249,82 +249,82 @@ describe('Page', () => { hasNextPage: false, [Symbol.asyncIterator]: async function*() { for (const item of this.items) { - yield item + yield item; } }, - } - const transformFunc = (item: any) => ({ ...item, transformed: true }) - mockOriginalPage.getNextPage = jest.fn().mockResolvedValue(nextPageData) - const page = new Page(mockOriginalPage, transformFunc) + }; + const transformFunc = (item: any) => ({ ...item, transformed: true }); + mockOriginalPage.getNextPage = jest.fn().mockResolvedValue(nextPageData); + const page = new Page(mockOriginalPage, transformFunc); - const nextPage = await page.getNextPage() + const nextPage = await page.getNextPage(); - expect(nextPage).toBeInstanceOf(Page) - expect(nextPage!['_transformFunc']).toBe(transformFunc) - expect(mockOriginalPage.getNextPage).toHaveBeenCalled() - }) + expect(nextPage).toBeInstanceOf(Page); + expect(nextPage!["_transformFunc"]).toBe(transformFunc); + expect(mockOriginalPage.getNextPage).toHaveBeenCalled(); + }); - it('should return null when no next page', async () => { - mockOriginalPage.getNextPage = jest.fn().mockResolvedValue(null) - const page = new Page(mockOriginalPage) + it("should return null when no next page", async () => { + mockOriginalPage.getNextPage = jest.fn().mockResolvedValue(null); + const page = new Page(mockOriginalPage); - const nextPage = await page.getNextPage() + const nextPage = await page.getNextPage(); - expect(nextPage).toBeNull() - }) + expect(nextPage).toBeNull(); + }); - it('should return null when getNextPage returns undefined', async () => { - mockOriginalPage.getNextPage = jest.fn().mockResolvedValue(undefined) - const page = new Page(mockOriginalPage) + it("should return null when getNextPage returns undefined", async () => { + mockOriginalPage.getNextPage = jest.fn().mockResolvedValue(undefined); + const page = new Page(mockOriginalPage); - const nextPage = await page.getNextPage() + const nextPage = await page.getNextPage(); - expect(nextPage).toBeNull() - }) + expect(nextPage).toBeNull(); + }); - it('should return null when getNextPage method does not exist', async () => { - const pageWithoutNext = { ...mockOriginalPage } - delete pageWithoutNext.getNextPage - const page = new Page(pageWithoutNext) + it("should return null when getNextPage method does not exist", async () => { + const pageWithoutNext = { ...mockOriginalPage }; + delete pageWithoutNext.getNextPage; + const page = new Page(pageWithoutNext); - const nextPage = await page.getNextPage() + const nextPage = await page.getNextPage(); - expect(nextPage).toBeNull() - }) + expect(nextPage).toBeNull(); + }); - it('should propagate transform function to next page', async () => { + it("should propagate transform function to next page", async () => { const nextPageData = { - items: [{ id: 'item4', name: 'Item 4' }], + items: [{ id: "item4", name: "Item 4" }], [Symbol.asyncIterator]: async function*() { for (const item of this.items) { - yield item + yield item; } }, - } - const transformFunc = (item: any) => ({ ...item, count: 999 }) - mockOriginalPage.getNextPage = jest.fn().mockResolvedValue(nextPageData) - const page = new Page(mockOriginalPage, transformFunc) + }; + const transformFunc = (item: any) => ({ ...item, count: 999 }); + mockOriginalPage.getNextPage = jest.fn().mockResolvedValue(nextPageData); + const page = new Page(mockOriginalPage, transformFunc); - const nextPage = await page.getNextPage() + const nextPage = await page.getNextPage(); - expect(nextPage).not.toBeNull() - const transformedItem = nextPage!.get(0) + expect(nextPage).not.toBeNull(); + const transformedItem = nextPage!.get(0); expect(transformedItem).toEqual({ - id: 'item4', - name: 'Item 4', + id: "item4", + name: "Item 4", count: 999, - }) - }) + }); + }); - it('should handle error from getNextPage', async () => { + it("should handle error from getNextPage", async () => { mockOriginalPage.getNextPage = jest .fn() - .mockRejectedValue(new Error('Failed to get next page')) - const page = new Page(mockOriginalPage) + .mockRejectedValue(new Error("Failed to get next page")); + const page = new Page(mockOriginalPage); await expect(page.getNextPage()).rejects.toThrow( - 'Failed to get next page' - ) - }) - }) -}) + "Failed to get next page", + ); + }); + }); +}); diff --git a/sdks/typescript/src/pagination.ts b/sdks/typescript/src/pagination.ts index c219f885..03acb971 100644 --- a/sdks/typescript/src/pagination.ts +++ b/sdks/typescript/src/pagination.ts @@ -102,11 +102,10 @@ export class Page implements AsyncIterable { * Check if there's a next page. */ get hasNextPage(): boolean { - const hasNext = this._originalPage.hasNextPage - if (typeof hasNext === 'function') { - return hasNext() + if (typeof this._originalPage.hasNextPage === 'function') { + return this._originalPage.hasNextPage() } - return hasNext || false + return false } /** @@ -114,7 +113,7 @@ export class Page implements AsyncIterable { * Returns null if there are no more pages. */ async getNextPage(): Promise | null> { - if (!this._originalPage.getNextPage) { + if (typeof this._originalPage.getNextPage !== 'function') { return null }