@lowerdeck/pagination 1.0.0 → 1.0.1
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/CHANGELOG.md +7 -0
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.module.js +1 -1
- package/dist/index.module.js.map +1 -1
- package/dist/index.umd.js +1 -1
- package/dist/index.umd.js.map +1 -1
- package/dist/paginatedProvider.d.ts +11 -0
- package/dist/paginatedProvider.d.ts.map +1 -1
- package/dist/paginator.d.ts +2 -1
- package/dist/paginator.d.ts.map +1 -1
- package/package.json +3 -2
- package/src/paginatedProvider.ts +69 -0
- package/src/paginator.ts +4 -1
package/CHANGELOG.md
ADDED
package/dist/index.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var r=require("@lowerdeck/error"),e=require("@lowerdeck/validation"),t=require("@lowerdeck/base62"),
|
|
1
|
+
var r=require("@lowerdeck/error"),e=require("@lowerdeck/validation"),t=require("@lowerdeck/base62"),o=function(e){return function(t){try{var o=t.limit,i=t.after,n=t.before,a=t.order;if(i&&n)throw new r.ServiceError(r.badRequestError({message:"Cannot use both after and before cursors"}));var s={orderBy:[{id:a}],take:o+2,skip:0},u=null!=i?i:n,l="none";return i?(s.cursor={id:i},l="after"):n&&(s.cursor={id:n},s.take=-s.take,s.skip=0,l="before"),Promise.resolve(e(s)).then(function(r){var e=r,t=u?null==e?void 0:e.find(function(r){return r.id==u}):void 0,i=t?null==e?void 0:e.indexOf(t):void 0,n="number"==typeof i?[].concat(e.slice(0,i),e.slice(i+1)):e,a=null==n?void 0:n.slice(0,o);"before"==l&&n.length>o&&(a=null==n?void 0:n.slice(1,o+1));var s=!1,c=!1;return"after"==l||"none"==l?(n.length>a.length&&(c=!0),t&&(s=!0)):"before"==l&&(n.length>a.length&&(s=!0),t&&(c=!0)),{items:a,pagination:{hasNextPage:c,hasPreviousPage:s}}})}catch(r){return Promise.reject(r)}}},i=function(e){return function(t){try{var o=t.limit,i=t.after,n=t.before,a=t.order;if(i&&n)throw new r.ServiceError(r.badRequestError({message:"Cannot use both after and before cursors"}));var s={},u={_id:"asc"===a?1:-1},l="none";return i?(s._id="asc"===a?{$gt:i}:{$lt:i},l="after"):n&&(s._id="asc"===a?{$lt:n}:{$gt:n},u._id="asc"===a?-1:1,l="before"),Promise.resolve(e({filter:s,sort:u,limit:o+1})).then(function(r){"before"===l&&(r=r.reverse());var e=r.length>o,t=e?r.slice(0,o):r,a=!1,s=!1;return"after"===l||"none"===l?(s=e,a=!!i):"before"===l&&(a=e,s=!!n),{items:t,pagination:{hasNextPage:s,hasPreviousPage:a}}})}catch(r){return Promise.reject(r)}}},n="cur_",a=/*#__PURE__*/function(){function e(r,e){this.id=void 0,this.type=void 0,this.id=r,this.type=e}return e.fromString=function(o){if(!o.startsWith(n))throw new r.ServiceError(r.badRequestError({message:"Invalid cursor format",hint:'Cursor must start with "cur_"'}));try{var i=t.base62.decode(o.slice(4)),a=JSON.parse(i.toString()),s=a[0],u=a[1];if("after"!==s&&"before"!==s)throw new Error("Invalid cursor type");if("string"!=typeof u)throw new Error("Invalid cursor id");return new e(u,s)}catch(e){throw new r.ServiceError(r.badRequestError({message:"Invalid cursor format",hint:"Please provide a valid cursor from another page."}))}},e.fromId=function(r,t){return new e(r,t)},e.prototype.toString=function(){return n+t.base62.encode(JSON.stringify([this.type,this.id]))},e}();exports.Paginator=/*#__PURE__*/function(){function r(r,e){void 0===e&&(e={}),this.provider=void 0,this.opts=void 0,this.provider=r,this.opts=e}return r.create=function(e,t){return void 0===t&&(t={}),new r(e,t)},r.validate=function(r){return e.v.intersection([e.v.object({limit:e.v.optional(e.v.number({modifiers:[e.v.minValue(1),e.v.maxValue(100)]})),after:e.v.optional(e.v.string()),before:e.v.optional(e.v.string()),cursor:e.v.optional(e.v.string()),order:e.v.optional(e.v.enumOf(["asc","desc"]))}),null!=r?r:e.v.object({})])},r.present=function(r,e){return function(t){return{run:function(){try{return Promise.resolve(Promise.all(r.items.map(function(r){var o;return null==(o=e(r))||null==(o=o(t))?void 0:o.run({})}))).then(function(e){return{__typename:"list",items:e.filter(Boolean),pagination:{has_more_after:r.pagination.hasNextPage,has_more_before:r.pagination.hasPreviousPage}}})}catch(r){return Promise.reject(r)}}}}},r.presentLight=function(r,e){try{return Promise.resolve(Promise.all(r.items.map(function(r){return e(r)}))).then(function(e){return{__typename:"list",items:e.filter(Boolean),pagination:{has_more_after:r.pagination.hasNextPage,has_more_before:r.pagination.hasPreviousPage}}})}catch(r){return Promise.reject(r)}},r.prototype.run=function(r){try{var e,t,n,s,u=this,l=Number(r.limit);isNaN(l)&&(l=20);var c={limit:Math.max(Math.min(null!=(e=null!=l?l:u.opts.defaultLimit)?e:20,null!=(t=u.opts.defaultLimit)?t:100),1),order:null!=(n=null!=(s=r.order)?s:u.opts.defaultOrder)?n:"asc"};if(r.after)c.after=r.after;else if(r.before)c.before=r.before;else if(r.cursor){var f=a.fromString(r.cursor);c[f.type]=f.id}var v=u.provider({prisma:o,mongoose:i});return Promise.resolve(v(c))}catch(r){return Promise.reject(r)}},r}(),exports.paginatedProviderMongoose=i,exports.paginatedProviderPrisma=o;
|
|
2
2
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","sources":["../src/paginatedProvider.ts","../src/cursor.ts","../src/paginator.ts"],"sourcesContent":["import { ServiceError, badRequestError } from '@lowerdeck/error';\nimport { PaginatedList } from './types';\n\nexport interface PaginatedProviderInput {\n limit: number;\n after?: string;\n before?: string;\n order: 'asc' | 'desc';\n}\n\nexport interface PrismaPaginationOpts {\n orderBy: [{ id: 'asc' | 'desc' }];\n cursor?: { id: string };\n take: number;\n skip: number;\n}\n\nexport type PaginatedProvider<T> = (\n input: PaginatedProviderInput\n) => Promise<PaginatedList<T>>;\n\nexport let paginatedProviderPrisma =\n <T extends { id: string }>(\n provider: (opts: PrismaPaginationOpts) => Promise<T[] | null | undefined>\n ): PaginatedProvider<T> =>\n async (input: PaginatedProviderInput) => {\n let { limit, after, before, order } = input;\n\n if (after && before) {\n throw new ServiceError(\n badRequestError({\n message: 'Cannot use both after and before cursors'\n })\n );\n }\n\n let opts: PrismaPaginationOpts = {\n orderBy: [{ id: order }],\n take: limit + 2,\n skip: 0\n };\n\n let cursorId = after ?? before;\n let cursorType: 'after' | 'before' | 'none' = 'none';\n\n if (after) {\n opts.cursor = { id: after };\n cursorType = 'after';\n } else if (before) {\n opts.cursor = { id: before };\n opts.take = -opts.take!;\n opts.skip = 0;\n cursorType = 'before';\n }\n\n let items = (await provider(opts)) ?? [];\n\n let orderedItems = items; /* items?.sort((a, b) => {\n if (order == 'asc') {\n return a.id.localeCompare(b.id);\n } else {\n return b.id.localeCompare(a.id);\n }\n });*/\n\n let cursorItem = cursorId ? orderedItems?.find(item => item.id == cursorId) : undefined;\n let cursorItemIndex = cursorItem ? orderedItems?.indexOf(cursorItem) : undefined;\n let orderedItemsWithoutCursor =\n typeof cursorItemIndex == 'number'\n ? [\n ...orderedItems.slice(0, cursorItemIndex),\n ...orderedItems.slice(cursorItemIndex + 1)\n ]\n : orderedItems;\n\n let selectedItems = orderedItemsWithoutCursor?.slice(0, limit);\n\n if (cursorType == 'before' && orderedItemsWithoutCursor.length > limit) {\n selectedItems = orderedItemsWithoutCursor?.slice(1, limit + 1);\n }\n\n let hasItemsBefore = false;\n let hasItemsAfter = false;\n\n if (cursorType == 'after' || cursorType == 'none') {\n if (orderedItemsWithoutCursor.length > selectedItems.length) hasItemsAfter = true;\n if (cursorItem) hasItemsBefore = true;\n } else if (cursorType == 'before') {\n if (orderedItemsWithoutCursor.length > selectedItems.length) hasItemsBefore = true;\n if (cursorItem) hasItemsAfter = true;\n }\n\n return {\n items: selectedItems,\n pagination: {\n hasNextPage: hasItemsAfter,\n hasPreviousPage: hasItemsBefore\n }\n };\n };\n","import { base62 } from '@lowerdeck/base62';\nimport { badRequestError, ServiceError } from '@lowerdeck/error';\n\nlet PREFIX = 'cur_';\n\nexport class Cursor {\n private constructor(\n public readonly id: string,\n public readonly type: 'after' | 'before'\n ) {}\n\n static fromString(str: string) {\n if (!str.startsWith(PREFIX)) {\n throw new ServiceError(\n badRequestError({\n message: 'Invalid cursor format',\n hint: 'Cursor must start with \"cur_\"'\n })\n );\n }\n\n try {\n let decoded = base62.decode(str.slice(PREFIX.length));\n let [type, id] = JSON.parse(decoded.toString());\n\n if (type !== 'after' && type !== 'before') {\n throw new Error('Invalid cursor type');\n }\n\n if (typeof id !== 'string') {\n throw new Error('Invalid cursor id');\n }\n\n return new Cursor(id, type);\n } catch (e) {\n throw new ServiceError(\n badRequestError({\n message: 'Invalid cursor format',\n hint: 'Please provide a valid cursor from another page.'\n })\n );\n }\n }\n\n static fromId(id: string, type: 'after' | 'before') {\n return new Cursor(id, type);\n }\n\n toString() {\n return PREFIX + base62.encode(JSON.stringify([this.type, this.id]));\n }\n}\n","import { v, ValidationType } from '@lowerdeck/validation';\nimport { Cursor } from './cursor';\nimport {\n PaginatedProvider,\n PaginatedProviderInput,\n paginatedProviderPrisma\n} from './paginatedProvider';\nimport { PaginatedList } from './types';\n\nexport interface PaginatorInput {\n limit?: number | string;\n after?: string;\n before?: string;\n cursor?: string;\n order?: 'asc' | 'desc';\n}\n\nexport interface PaginatorOpts {\n defaultLimit?: number;\n defaultOrder?: 'asc' | 'desc';\n}\n\nexport type Provider<T> = (providers: {\n prisma: typeof paginatedProviderPrisma;\n}) => PaginatedProvider<T>;\n\nexport class Paginator<T> {\n private constructor(\n private provider: Provider<T>,\n private opts: PaginatorOpts = {}\n ) {}\n\n static create<T>(provider: Provider<T>, opts: PaginatorOpts = {}) {\n return new Paginator(provider, opts);\n }\n\n static validate<Inner extends object>(inner?: ValidationType<Inner>) {\n return v.intersection([\n v.object({\n limit: v.optional(\n v.number({\n modifiers: [v.minValue(1), v.maxValue(100)]\n })\n ),\n after: v.optional(v.string()),\n before: v.optional(v.string()),\n cursor: v.optional(v.string()),\n order: v.optional(v.enumOf(['asc', 'desc']))\n }),\n inner ?? v.object({})\n ]) as ValidationType<\n Inner & {\n limit?: number;\n after?: string;\n before?: string;\n cursor?: string;\n order?: 'asc' | 'desc';\n }\n >;\n }\n\n static present<T, R>(\n list: PaginatedList<T>,\n presenter: (item: T) => (context: any) => { run: (d: any) => R } | undefined\n ) {\n return (context: any) => ({\n run: async () => ({\n __typename: `list`,\n items: (\n await Promise.all(list.items.map(item => presenter(item)?.(context)?.run({})))\n ).filter(Boolean),\n pagination: {\n has_more_after: list.pagination.hasNextPage,\n has_more_before: list.pagination.hasPreviousPage\n }\n })\n });\n }\n\n static async presentLight<T, R>(\n list: PaginatedList<T>,\n presenter: (item: T) => R | Promise<R>\n ) {\n return {\n __typename: `list`,\n items: (await Promise.all(list.items.map(item => presenter(item)))).filter(Boolean),\n pagination: {\n has_more_after: list.pagination.hasNextPage,\n has_more_before: list.pagination.hasPreviousPage\n }\n };\n }\n\n async run(input: PaginatorInput): Promise<PaginatedList<T>> {\n let numberLimit = Number(input.limit);\n if (isNaN(numberLimit)) numberLimit = 20;\n\n let providerInput: PaginatedProviderInput = {\n limit: Math.max(\n Math.min(numberLimit ?? this.opts.defaultLimit ?? 20, this.opts.defaultLimit ?? 100),\n 1\n ),\n order: input.order ?? this.opts.defaultOrder ?? 'asc'\n };\n\n if (input.after) {\n providerInput.after = input.after;\n } else if (input.before) {\n providerInput.before = input.before;\n } else if (input.cursor) {\n let cursor = Cursor.fromString(input.cursor);\n providerInput[cursor.type] = cursor.id;\n }\n\n let provider = this.provider({\n prisma: paginatedProviderPrisma\n });\n\n return await provider(providerInput);\n }\n}\n"],"names":["paginatedProviderPrisma","provider","input","limit","after","before","order","ServiceError","badRequestError","message","opts","orderBy","id","take","skip","cursorId","cursorType","cursor","Promise","resolve","then","items","orderedItems","cursorItem","find","item","undefined","cursorItemIndex","indexOf","orderedItemsWithoutCursor","concat","slice","selectedItems","length","hasItemsBefore","hasItemsAfter","pagination","hasNextPage","hasPreviousPage","e","reject","PREFIX","Cursor","type","this","fromString","str","startsWith","hint","decoded","base62","decode","_JSON$parse","JSON","parse","toString","Error","fromId","prototype","encode","stringify","Paginator","create","validate","inner","v","intersection","object","optional","number","modifiers","minValue","maxValue","string","enumOf","present","list","presenter","context","run","all","map","_presenter","_Promise$all","__typename","filter","Boolean","has_more_after","has_more_before","presentLight","_Promise$all2","_ref","_this$opts$defaultLim","_ref2","_input$order","_this","numberLimit","Number","isNaN","providerInput","Math","max","min","defaultLimit","defaultOrder","prisma"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":["../src/paginatedProvider.ts","../src/cursor.ts","../src/paginator.ts"],"sourcesContent":["import { ServiceError, badRequestError } from '@lowerdeck/error';\nimport type { QueryFilter, SortOrder } from 'mongoose';\nimport { PaginatedList } from './types';\n\nexport interface PaginatedProviderInput {\n limit: number;\n after?: string;\n before?: string;\n order: 'asc' | 'desc';\n}\n\nexport interface PrismaPaginationOpts {\n orderBy: [{ id: 'asc' | 'desc' }];\n cursor?: { id: string };\n take: number;\n skip: number;\n}\n\nexport interface MongoosePaginationOpts<T> {\n filter: QueryFilter<T>;\n sort: { _id: SortOrder };\n limit: number;\n}\n\nexport type PaginatedProvider<T> = (\n input: PaginatedProviderInput\n) => Promise<PaginatedList<T>>;\n\nexport let paginatedProviderPrisma =\n <T extends { id: string }>(\n provider: (opts: PrismaPaginationOpts) => Promise<T[] | null | undefined>\n ): PaginatedProvider<T> =>\n async (input: PaginatedProviderInput) => {\n let { limit, after, before, order } = input;\n\n if (after && before) {\n throw new ServiceError(\n badRequestError({\n message: 'Cannot use both after and before cursors'\n })\n );\n }\n\n let opts: PrismaPaginationOpts = {\n orderBy: [{ id: order }],\n take: limit + 2,\n skip: 0\n };\n\n let cursorId = after ?? before;\n let cursorType: 'after' | 'before' | 'none' = 'none';\n\n if (after) {\n opts.cursor = { id: after };\n cursorType = 'after';\n } else if (before) {\n opts.cursor = { id: before };\n opts.take = -opts.take!;\n opts.skip = 0;\n cursorType = 'before';\n }\n\n let items = (await provider(opts)) ?? [];\n\n let orderedItems = items; /* items?.sort((a, b) => {\n if (order == 'asc') {\n return a.id.localeCompare(b.id);\n } else {\n return b.id.localeCompare(a.id);\n }\n });*/\n\n let cursorItem = cursorId ? orderedItems?.find(item => item.id == cursorId) : undefined;\n let cursorItemIndex = cursorItem ? orderedItems?.indexOf(cursorItem) : undefined;\n let orderedItemsWithoutCursor =\n typeof cursorItemIndex == 'number'\n ? [\n ...orderedItems.slice(0, cursorItemIndex),\n ...orderedItems.slice(cursorItemIndex + 1)\n ]\n : orderedItems;\n\n let selectedItems = orderedItemsWithoutCursor?.slice(0, limit);\n\n if (cursorType == 'before' && orderedItemsWithoutCursor.length > limit) {\n selectedItems = orderedItemsWithoutCursor?.slice(1, limit + 1);\n }\n\n let hasItemsBefore = false;\n let hasItemsAfter = false;\n\n if (cursorType == 'after' || cursorType == 'none') {\n if (orderedItemsWithoutCursor.length > selectedItems.length) hasItemsAfter = true;\n if (cursorItem) hasItemsBefore = true;\n } else if (cursorType == 'before') {\n if (orderedItemsWithoutCursor.length > selectedItems.length) hasItemsBefore = true;\n if (cursorItem) hasItemsAfter = true;\n }\n\n return {\n items: selectedItems,\n pagination: {\n hasNextPage: hasItemsAfter,\n hasPreviousPage: hasItemsBefore\n }\n };\n };\n\nexport let paginatedProviderMongoose =\n <T extends { _id: string }>(\n provider: (opts: MongoosePaginationOpts<T>) => Promise<T[] | null | undefined>\n ): PaginatedProvider<T> =>\n async (input: PaginatedProviderInput) => {\n let { limit, after, before, order } = input;\n\n if (after && before) {\n throw new ServiceError(\n badRequestError({\n message: 'Cannot use both after and before cursors'\n })\n );\n }\n\n let filter: QueryFilter<T> = {};\n let sort: { _id: SortOrder } = { _id: order === 'asc' ? 1 : -1 };\n let cursorId = after ?? before;\n let cursorType: 'after' | 'before' | 'none' = 'none';\n\n if (after) {\n filter._id = order === 'asc' ? { $gt: after } : { $lt: after };\n cursorType = 'after';\n } else if (before) {\n filter._id = order === 'asc' ? { $lt: before } : { $gt: before };\n sort._id = order === 'asc' ? -1 : 1;\n cursorType = 'before';\n }\n\n let opts: MongoosePaginationOpts<T> = {\n filter,\n sort,\n limit: limit + 1\n };\n\n let items = (await provider(opts)) ?? [];\n\n if (cursorType === 'before') items = items.reverse();\n\n let hasMore = items.length > limit;\n let selectedItems = hasMore ? items.slice(0, limit) : items;\n\n let hasItemsBefore = false;\n let hasItemsAfter = false;\n\n if (cursorType === 'after' || cursorType === 'none') {\n hasItemsAfter = hasMore;\n hasItemsBefore = !!after;\n } else if (cursorType === 'before') {\n hasItemsBefore = hasMore;\n hasItemsAfter = !!before;\n }\n\n return {\n items: selectedItems,\n pagination: {\n hasNextPage: hasItemsAfter,\n hasPreviousPage: hasItemsBefore\n }\n };\n };\n","import { base62 } from '@lowerdeck/base62';\nimport { badRequestError, ServiceError } from '@lowerdeck/error';\n\nlet PREFIX = 'cur_';\n\nexport class Cursor {\n private constructor(\n public readonly id: string,\n public readonly type: 'after' | 'before'\n ) {}\n\n static fromString(str: string) {\n if (!str.startsWith(PREFIX)) {\n throw new ServiceError(\n badRequestError({\n message: 'Invalid cursor format',\n hint: 'Cursor must start with \"cur_\"'\n })\n );\n }\n\n try {\n let decoded = base62.decode(str.slice(PREFIX.length));\n let [type, id] = JSON.parse(decoded.toString());\n\n if (type !== 'after' && type !== 'before') {\n throw new Error('Invalid cursor type');\n }\n\n if (typeof id !== 'string') {\n throw new Error('Invalid cursor id');\n }\n\n return new Cursor(id, type);\n } catch (e) {\n throw new ServiceError(\n badRequestError({\n message: 'Invalid cursor format',\n hint: 'Please provide a valid cursor from another page.'\n })\n );\n }\n }\n\n static fromId(id: string, type: 'after' | 'before') {\n return new Cursor(id, type);\n }\n\n toString() {\n return PREFIX + base62.encode(JSON.stringify([this.type, this.id]));\n }\n}\n","import { v, ValidationType } from '@lowerdeck/validation';\nimport { Cursor } from './cursor';\nimport {\n PaginatedProvider,\n PaginatedProviderInput,\n paginatedProviderMongoose,\n paginatedProviderPrisma\n} from './paginatedProvider';\nimport { PaginatedList } from './types';\n\nexport interface PaginatorInput {\n limit?: number | string;\n after?: string;\n before?: string;\n cursor?: string;\n order?: 'asc' | 'desc';\n}\n\nexport interface PaginatorOpts {\n defaultLimit?: number;\n defaultOrder?: 'asc' | 'desc';\n}\n\nexport type Provider<T> = (providers: {\n prisma: typeof paginatedProviderPrisma;\n mongoose: typeof paginatedProviderMongoose;\n}) => PaginatedProvider<T>;\n\nexport class Paginator<T> {\n private constructor(\n private provider: Provider<T>,\n private opts: PaginatorOpts = {}\n ) {}\n\n static create<T>(provider: Provider<T>, opts: PaginatorOpts = {}) {\n return new Paginator(provider, opts);\n }\n\n static validate<Inner extends object>(inner?: ValidationType<Inner>) {\n return v.intersection([\n v.object({\n limit: v.optional(\n v.number({\n modifiers: [v.minValue(1), v.maxValue(100)]\n })\n ),\n after: v.optional(v.string()),\n before: v.optional(v.string()),\n cursor: v.optional(v.string()),\n order: v.optional(v.enumOf(['asc', 'desc']))\n }),\n inner ?? v.object({})\n ]) as ValidationType<\n Inner & {\n limit?: number;\n after?: string;\n before?: string;\n cursor?: string;\n order?: 'asc' | 'desc';\n }\n >;\n }\n\n static present<T, R>(\n list: PaginatedList<T>,\n presenter: (item: T) => (context: any) => { run: (d: any) => R } | undefined\n ) {\n return (context: any) => ({\n run: async () => ({\n __typename: `list`,\n items: (\n await Promise.all(list.items.map(item => presenter(item)?.(context)?.run({})))\n ).filter(Boolean),\n pagination: {\n has_more_after: list.pagination.hasNextPage,\n has_more_before: list.pagination.hasPreviousPage\n }\n })\n });\n }\n\n static async presentLight<T, R>(\n list: PaginatedList<T>,\n presenter: (item: T) => R | Promise<R>\n ) {\n return {\n __typename: `list`,\n items: (await Promise.all(list.items.map(item => presenter(item)))).filter(Boolean),\n pagination: {\n has_more_after: list.pagination.hasNextPage,\n has_more_before: list.pagination.hasPreviousPage\n }\n };\n }\n\n async run(input: PaginatorInput): Promise<PaginatedList<T>> {\n let numberLimit = Number(input.limit);\n if (isNaN(numberLimit)) numberLimit = 20;\n\n let providerInput: PaginatedProviderInput = {\n limit: Math.max(\n Math.min(numberLimit ?? this.opts.defaultLimit ?? 20, this.opts.defaultLimit ?? 100),\n 1\n ),\n order: input.order ?? this.opts.defaultOrder ?? 'asc'\n };\n\n if (input.after) {\n providerInput.after = input.after;\n } else if (input.before) {\n providerInput.before = input.before;\n } else if (input.cursor) {\n let cursor = Cursor.fromString(input.cursor);\n providerInput[cursor.type] = cursor.id;\n }\n\n let provider = this.provider({\n prisma: paginatedProviderPrisma,\n mongoose: paginatedProviderMongoose\n });\n\n return await provider(providerInput);\n }\n}\n"],"names":["paginatedProviderPrisma","provider","input","limit","after","before","order","ServiceError","badRequestError","message","opts","orderBy","id","take","skip","cursorId","cursorType","cursor","Promise","resolve","then","items","orderedItems","cursorItem","find","item","undefined","cursorItemIndex","indexOf","orderedItemsWithoutCursor","concat","slice","selectedItems","length","hasItemsBefore","hasItemsAfter","pagination","hasNextPage","hasPreviousPage","e","reject","paginatedProviderMongoose","filter","sort","_id","$gt","$lt","reverse","hasMore","PREFIX","Cursor","type","this","fromString","str","startsWith","hint","decoded","base62","decode","_JSON$parse","JSON","parse","toString","Error","fromId","prototype","encode","stringify","Paginator","create","validate","inner","v","intersection","object","optional","number","modifiers","minValue","maxValue","string","enumOf","present","list","presenter","context","run","all","map","_presenter","_Promise$all","__typename","Boolean","has_more_after","has_more_before","presentLight","_Promise$all2","_ref","_this$opts$defaultLim","_ref2","_input$order","_this","numberLimit","Number","isNaN","providerInput","Math","max","min","defaultLimit","defaultOrder","prisma","mongoose"],"mappings":"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"}
|
package/dist/index.module.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{ServiceError as
|
|
1
|
+
import{ServiceError as e,badRequestError as r}from"@lowerdeck/error";import{v as t}from"@lowerdeck/validation";import{base62 as o}from"@lowerdeck/base62";var n=function(t){return function(o){try{var n=o.limit,i=o.after,a=o.before,s=o.order;if(i&&a)throw new e(r({message:"Cannot use both after and before cursors"}));var u={orderBy:[{id:s}],take:n+2,skip:0},f=null!=i?i:a,l="none";return i?(u.cursor={id:i},l="after"):a&&(u.cursor={id:a},u.take=-u.take,u.skip=0,l="before"),Promise.resolve(t(u)).then(function(e){var r=e,t=f?null==r?void 0:r.find(function(e){return e.id==f}):void 0,o=t?null==r?void 0:r.indexOf(t):void 0,i="number"==typeof o?[].concat(r.slice(0,o),r.slice(o+1)):r,a=null==i?void 0:i.slice(0,n);"before"==l&&i.length>n&&(a=null==i?void 0:i.slice(1,n+1));var s=!1,u=!1;return"after"==l||"none"==l?(i.length>a.length&&(u=!0),t&&(s=!0)):"before"==l&&(i.length>a.length&&(s=!0),t&&(u=!0)),{items:a,pagination:{hasNextPage:u,hasPreviousPage:s}}})}catch(e){return Promise.reject(e)}}},i=function(t){return function(o){try{var n=o.limit,i=o.after,a=o.before,s=o.order;if(i&&a)throw new e(r({message:"Cannot use both after and before cursors"}));var u={},f={_id:"asc"===s?1:-1},l="none";return i?(u._id="asc"===s?{$gt:i}:{$lt:i},l="after"):a&&(u._id="asc"===s?{$lt:a}:{$gt:a},f._id="asc"===s?-1:1,l="before"),Promise.resolve(t({filter:u,sort:f,limit:n+1})).then(function(e){"before"===l&&(e=e.reverse());var r=e.length>n,t=r?e.slice(0,n):e,o=!1,s=!1;return"after"===l||"none"===l?(s=r,o=!!i):"before"===l&&(o=r,s=!!a),{items:t,pagination:{hasNextPage:s,hasPreviousPage:o}}})}catch(e){return Promise.reject(e)}}},a="cur_",s=/*#__PURE__*/function(){function t(e,r){this.id=void 0,this.type=void 0,this.id=e,this.type=r}return t.fromString=function(n){if(!n.startsWith(a))throw new e(r({message:"Invalid cursor format",hint:'Cursor must start with "cur_"'}));try{var i=o.decode(n.slice(4)),s=JSON.parse(i.toString()),u=s[0],f=s[1];if("after"!==u&&"before"!==u)throw new Error("Invalid cursor type");if("string"!=typeof f)throw new Error("Invalid cursor id");return new t(f,u)}catch(t){throw new e(r({message:"Invalid cursor format",hint:"Please provide a valid cursor from another page."}))}},t.fromId=function(e,r){return new t(e,r)},t.prototype.toString=function(){return a+o.encode(JSON.stringify([this.type,this.id]))},t}(),u=/*#__PURE__*/function(){function e(e,r){void 0===r&&(r={}),this.provider=void 0,this.opts=void 0,this.provider=e,this.opts=r}return e.create=function(r,t){return void 0===t&&(t={}),new e(r,t)},e.validate=function(e){return t.intersection([t.object({limit:t.optional(t.number({modifiers:[t.minValue(1),t.maxValue(100)]})),after:t.optional(t.string()),before:t.optional(t.string()),cursor:t.optional(t.string()),order:t.optional(t.enumOf(["asc","desc"]))}),null!=e?e:t.object({})])},e.present=function(e,r){return function(t){return{run:function(){try{return Promise.resolve(Promise.all(e.items.map(function(e){var o;return null==(o=r(e))||null==(o=o(t))?void 0:o.run({})}))).then(function(r){return{__typename:"list",items:r.filter(Boolean),pagination:{has_more_after:e.pagination.hasNextPage,has_more_before:e.pagination.hasPreviousPage}}})}catch(e){return Promise.reject(e)}}}}},e.presentLight=function(e,r){try{return Promise.resolve(Promise.all(e.items.map(function(e){return r(e)}))).then(function(r){return{__typename:"list",items:r.filter(Boolean),pagination:{has_more_after:e.pagination.hasNextPage,has_more_before:e.pagination.hasPreviousPage}}})}catch(e){return Promise.reject(e)}},e.prototype.run=function(e){try{var r,t,o,a,u=this,f=Number(e.limit);isNaN(f)&&(f=20);var l={limit:Math.max(Math.min(null!=(r=null!=f?f:u.opts.defaultLimit)?r:20,null!=(t=u.opts.defaultLimit)?t:100),1),order:null!=(o=null!=(a=e.order)?a:u.opts.defaultOrder)?o:"asc"};if(e.after)l.after=e.after;else if(e.before)l.before=e.before;else if(e.cursor){var c=s.fromString(e.cursor);l[c.type]=c.id}var m=u.provider({prisma:n,mongoose:i});return Promise.resolve(m(l))}catch(e){return Promise.reject(e)}},e}();export{u as Paginator,i as paginatedProviderMongoose,n as paginatedProviderPrisma};
|
|
2
2
|
//# sourceMappingURL=index.module.js.map
|
package/dist/index.module.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.module.js","sources":["../src/paginatedProvider.ts","../src/cursor.ts","../src/paginator.ts"],"sourcesContent":["import { ServiceError, badRequestError } from '@lowerdeck/error';\nimport { PaginatedList } from './types';\n\nexport interface PaginatedProviderInput {\n limit: number;\n after?: string;\n before?: string;\n order: 'asc' | 'desc';\n}\n\nexport interface PrismaPaginationOpts {\n orderBy: [{ id: 'asc' | 'desc' }];\n cursor?: { id: string };\n take: number;\n skip: number;\n}\n\nexport type PaginatedProvider<T> = (\n input: PaginatedProviderInput\n) => Promise<PaginatedList<T>>;\n\nexport let paginatedProviderPrisma =\n <T extends { id: string }>(\n provider: (opts: PrismaPaginationOpts) => Promise<T[] | null | undefined>\n ): PaginatedProvider<T> =>\n async (input: PaginatedProviderInput) => {\n let { limit, after, before, order } = input;\n\n if (after && before) {\n throw new ServiceError(\n badRequestError({\n message: 'Cannot use both after and before cursors'\n })\n );\n }\n\n let opts: PrismaPaginationOpts = {\n orderBy: [{ id: order }],\n take: limit + 2,\n skip: 0\n };\n\n let cursorId = after ?? before;\n let cursorType: 'after' | 'before' | 'none' = 'none';\n\n if (after) {\n opts.cursor = { id: after };\n cursorType = 'after';\n } else if (before) {\n opts.cursor = { id: before };\n opts.take = -opts.take!;\n opts.skip = 0;\n cursorType = 'before';\n }\n\n let items = (await provider(opts)) ?? [];\n\n let orderedItems = items; /* items?.sort((a, b) => {\n if (order == 'asc') {\n return a.id.localeCompare(b.id);\n } else {\n return b.id.localeCompare(a.id);\n }\n });*/\n\n let cursorItem = cursorId ? orderedItems?.find(item => item.id == cursorId) : undefined;\n let cursorItemIndex = cursorItem ? orderedItems?.indexOf(cursorItem) : undefined;\n let orderedItemsWithoutCursor =\n typeof cursorItemIndex == 'number'\n ? [\n ...orderedItems.slice(0, cursorItemIndex),\n ...orderedItems.slice(cursorItemIndex + 1)\n ]\n : orderedItems;\n\n let selectedItems = orderedItemsWithoutCursor?.slice(0, limit);\n\n if (cursorType == 'before' && orderedItemsWithoutCursor.length > limit) {\n selectedItems = orderedItemsWithoutCursor?.slice(1, limit + 1);\n }\n\n let hasItemsBefore = false;\n let hasItemsAfter = false;\n\n if (cursorType == 'after' || cursorType == 'none') {\n if (orderedItemsWithoutCursor.length > selectedItems.length) hasItemsAfter = true;\n if (cursorItem) hasItemsBefore = true;\n } else if (cursorType == 'before') {\n if (orderedItemsWithoutCursor.length > selectedItems.length) hasItemsBefore = true;\n if (cursorItem) hasItemsAfter = true;\n }\n\n return {\n items: selectedItems,\n pagination: {\n hasNextPage: hasItemsAfter,\n hasPreviousPage: hasItemsBefore\n }\n };\n };\n","import { base62 } from '@lowerdeck/base62';\nimport { badRequestError, ServiceError } from '@lowerdeck/error';\n\nlet PREFIX = 'cur_';\n\nexport class Cursor {\n private constructor(\n public readonly id: string,\n public readonly type: 'after' | 'before'\n ) {}\n\n static fromString(str: string) {\n if (!str.startsWith(PREFIX)) {\n throw new ServiceError(\n badRequestError({\n message: 'Invalid cursor format',\n hint: 'Cursor must start with \"cur_\"'\n })\n );\n }\n\n try {\n let decoded = base62.decode(str.slice(PREFIX.length));\n let [type, id] = JSON.parse(decoded.toString());\n\n if (type !== 'after' && type !== 'before') {\n throw new Error('Invalid cursor type');\n }\n\n if (typeof id !== 'string') {\n throw new Error('Invalid cursor id');\n }\n\n return new Cursor(id, type);\n } catch (e) {\n throw new ServiceError(\n badRequestError({\n message: 'Invalid cursor format',\n hint: 'Please provide a valid cursor from another page.'\n })\n );\n }\n }\n\n static fromId(id: string, type: 'after' | 'before') {\n return new Cursor(id, type);\n }\n\n toString() {\n return PREFIX + base62.encode(JSON.stringify([this.type, this.id]));\n }\n}\n","import { v, ValidationType } from '@lowerdeck/validation';\nimport { Cursor } from './cursor';\nimport {\n PaginatedProvider,\n PaginatedProviderInput,\n paginatedProviderPrisma\n} from './paginatedProvider';\nimport { PaginatedList } from './types';\n\nexport interface PaginatorInput {\n limit?: number | string;\n after?: string;\n before?: string;\n cursor?: string;\n order?: 'asc' | 'desc';\n}\n\nexport interface PaginatorOpts {\n defaultLimit?: number;\n defaultOrder?: 'asc' | 'desc';\n}\n\nexport type Provider<T> = (providers: {\n prisma: typeof paginatedProviderPrisma;\n}) => PaginatedProvider<T>;\n\nexport class Paginator<T> {\n private constructor(\n private provider: Provider<T>,\n private opts: PaginatorOpts = {}\n ) {}\n\n static create<T>(provider: Provider<T>, opts: PaginatorOpts = {}) {\n return new Paginator(provider, opts);\n }\n\n static validate<Inner extends object>(inner?: ValidationType<Inner>) {\n return v.intersection([\n v.object({\n limit: v.optional(\n v.number({\n modifiers: [v.minValue(1), v.maxValue(100)]\n })\n ),\n after: v.optional(v.string()),\n before: v.optional(v.string()),\n cursor: v.optional(v.string()),\n order: v.optional(v.enumOf(['asc', 'desc']))\n }),\n inner ?? v.object({})\n ]) as ValidationType<\n Inner & {\n limit?: number;\n after?: string;\n before?: string;\n cursor?: string;\n order?: 'asc' | 'desc';\n }\n >;\n }\n\n static present<T, R>(\n list: PaginatedList<T>,\n presenter: (item: T) => (context: any) => { run: (d: any) => R } | undefined\n ) {\n return (context: any) => ({\n run: async () => ({\n __typename: `list`,\n items: (\n await Promise.all(list.items.map(item => presenter(item)?.(context)?.run({})))\n ).filter(Boolean),\n pagination: {\n has_more_after: list.pagination.hasNextPage,\n has_more_before: list.pagination.hasPreviousPage\n }\n })\n });\n }\n\n static async presentLight<T, R>(\n list: PaginatedList<T>,\n presenter: (item: T) => R | Promise<R>\n ) {\n return {\n __typename: `list`,\n items: (await Promise.all(list.items.map(item => presenter(item)))).filter(Boolean),\n pagination: {\n has_more_after: list.pagination.hasNextPage,\n has_more_before: list.pagination.hasPreviousPage\n }\n };\n }\n\n async run(input: PaginatorInput): Promise<PaginatedList<T>> {\n let numberLimit = Number(input.limit);\n if (isNaN(numberLimit)) numberLimit = 20;\n\n let providerInput: PaginatedProviderInput = {\n limit: Math.max(\n Math.min(numberLimit ?? this.opts.defaultLimit ?? 20, this.opts.defaultLimit ?? 100),\n 1\n ),\n order: input.order ?? this.opts.defaultOrder ?? 'asc'\n };\n\n if (input.after) {\n providerInput.after = input.after;\n } else if (input.before) {\n providerInput.before = input.before;\n } else if (input.cursor) {\n let cursor = Cursor.fromString(input.cursor);\n providerInput[cursor.type] = cursor.id;\n }\n\n let provider = this.provider({\n prisma: paginatedProviderPrisma\n });\n\n return await provider(providerInput);\n }\n}\n"],"names":["paginatedProviderPrisma","provider","input","limit","after","before","order","ServiceError","badRequestError","message","opts","orderBy","id","take","skip","cursorId","cursorType","cursor","Promise","resolve","then","items","orderedItems","cursorItem","find","item","undefined","cursorItemIndex","indexOf","orderedItemsWithoutCursor","concat","slice","selectedItems","length","hasItemsBefore","hasItemsAfter","pagination","hasNextPage","hasPreviousPage","e","reject","PREFIX","Cursor","type","this","fromString","str","startsWith","hint","decoded","base62","decode","_JSON$parse","JSON","parse","toString","Error","fromId","prototype","encode","stringify","Paginator","create","validate","inner","v","intersection","object","optional","number","modifiers","minValue","maxValue","string","enumOf","present","list","presenter","context","run","all","map","_presenter","_Promise$all","__typename","filter","Boolean","has_more_after","has_more_before","presentLight","_Promise$all2","_ref","_this$opts$defaultLim","_ref2","_input$order","_this","numberLimit","Number","isNaN","providerInput","Math","max","min","defaultLimit","defaultOrder","prisma"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.module.js","sources":["../src/paginatedProvider.ts","../src/cursor.ts","../src/paginator.ts"],"sourcesContent":["import { ServiceError, badRequestError } from '@lowerdeck/error';\nimport type { QueryFilter, SortOrder } from 'mongoose';\nimport { PaginatedList } from './types';\n\nexport interface PaginatedProviderInput {\n limit: number;\n after?: string;\n before?: string;\n order: 'asc' | 'desc';\n}\n\nexport interface PrismaPaginationOpts {\n orderBy: [{ id: 'asc' | 'desc' }];\n cursor?: { id: string };\n take: number;\n skip: number;\n}\n\nexport interface MongoosePaginationOpts<T> {\n filter: QueryFilter<T>;\n sort: { _id: SortOrder };\n limit: number;\n}\n\nexport type PaginatedProvider<T> = (\n input: PaginatedProviderInput\n) => Promise<PaginatedList<T>>;\n\nexport let paginatedProviderPrisma =\n <T extends { id: string }>(\n provider: (opts: PrismaPaginationOpts) => Promise<T[] | null | undefined>\n ): PaginatedProvider<T> =>\n async (input: PaginatedProviderInput) => {\n let { limit, after, before, order } = input;\n\n if (after && before) {\n throw new ServiceError(\n badRequestError({\n message: 'Cannot use both after and before cursors'\n })\n );\n }\n\n let opts: PrismaPaginationOpts = {\n orderBy: [{ id: order }],\n take: limit + 2,\n skip: 0\n };\n\n let cursorId = after ?? before;\n let cursorType: 'after' | 'before' | 'none' = 'none';\n\n if (after) {\n opts.cursor = { id: after };\n cursorType = 'after';\n } else if (before) {\n opts.cursor = { id: before };\n opts.take = -opts.take!;\n opts.skip = 0;\n cursorType = 'before';\n }\n\n let items = (await provider(opts)) ?? [];\n\n let orderedItems = items; /* items?.sort((a, b) => {\n if (order == 'asc') {\n return a.id.localeCompare(b.id);\n } else {\n return b.id.localeCompare(a.id);\n }\n });*/\n\n let cursorItem = cursorId ? orderedItems?.find(item => item.id == cursorId) : undefined;\n let cursorItemIndex = cursorItem ? orderedItems?.indexOf(cursorItem) : undefined;\n let orderedItemsWithoutCursor =\n typeof cursorItemIndex == 'number'\n ? [\n ...orderedItems.slice(0, cursorItemIndex),\n ...orderedItems.slice(cursorItemIndex + 1)\n ]\n : orderedItems;\n\n let selectedItems = orderedItemsWithoutCursor?.slice(0, limit);\n\n if (cursorType == 'before' && orderedItemsWithoutCursor.length > limit) {\n selectedItems = orderedItemsWithoutCursor?.slice(1, limit + 1);\n }\n\n let hasItemsBefore = false;\n let hasItemsAfter = false;\n\n if (cursorType == 'after' || cursorType == 'none') {\n if (orderedItemsWithoutCursor.length > selectedItems.length) hasItemsAfter = true;\n if (cursorItem) hasItemsBefore = true;\n } else if (cursorType == 'before') {\n if (orderedItemsWithoutCursor.length > selectedItems.length) hasItemsBefore = true;\n if (cursorItem) hasItemsAfter = true;\n }\n\n return {\n items: selectedItems,\n pagination: {\n hasNextPage: hasItemsAfter,\n hasPreviousPage: hasItemsBefore\n }\n };\n };\n\nexport let paginatedProviderMongoose =\n <T extends { _id: string }>(\n provider: (opts: MongoosePaginationOpts<T>) => Promise<T[] | null | undefined>\n ): PaginatedProvider<T> =>\n async (input: PaginatedProviderInput) => {\n let { limit, after, before, order } = input;\n\n if (after && before) {\n throw new ServiceError(\n badRequestError({\n message: 'Cannot use both after and before cursors'\n })\n );\n }\n\n let filter: QueryFilter<T> = {};\n let sort: { _id: SortOrder } = { _id: order === 'asc' ? 1 : -1 };\n let cursorId = after ?? before;\n let cursorType: 'after' | 'before' | 'none' = 'none';\n\n if (after) {\n filter._id = order === 'asc' ? { $gt: after } : { $lt: after };\n cursorType = 'after';\n } else if (before) {\n filter._id = order === 'asc' ? { $lt: before } : { $gt: before };\n sort._id = order === 'asc' ? -1 : 1;\n cursorType = 'before';\n }\n\n let opts: MongoosePaginationOpts<T> = {\n filter,\n sort,\n limit: limit + 1\n };\n\n let items = (await provider(opts)) ?? [];\n\n if (cursorType === 'before') items = items.reverse();\n\n let hasMore = items.length > limit;\n let selectedItems = hasMore ? items.slice(0, limit) : items;\n\n let hasItemsBefore = false;\n let hasItemsAfter = false;\n\n if (cursorType === 'after' || cursorType === 'none') {\n hasItemsAfter = hasMore;\n hasItemsBefore = !!after;\n } else if (cursorType === 'before') {\n hasItemsBefore = hasMore;\n hasItemsAfter = !!before;\n }\n\n return {\n items: selectedItems,\n pagination: {\n hasNextPage: hasItemsAfter,\n hasPreviousPage: hasItemsBefore\n }\n };\n };\n","import { base62 } from '@lowerdeck/base62';\nimport { badRequestError, ServiceError } from '@lowerdeck/error';\n\nlet PREFIX = 'cur_';\n\nexport class Cursor {\n private constructor(\n public readonly id: string,\n public readonly type: 'after' | 'before'\n ) {}\n\n static fromString(str: string) {\n if (!str.startsWith(PREFIX)) {\n throw new ServiceError(\n badRequestError({\n message: 'Invalid cursor format',\n hint: 'Cursor must start with \"cur_\"'\n })\n );\n }\n\n try {\n let decoded = base62.decode(str.slice(PREFIX.length));\n let [type, id] = JSON.parse(decoded.toString());\n\n if (type !== 'after' && type !== 'before') {\n throw new Error('Invalid cursor type');\n }\n\n if (typeof id !== 'string') {\n throw new Error('Invalid cursor id');\n }\n\n return new Cursor(id, type);\n } catch (e) {\n throw new ServiceError(\n badRequestError({\n message: 'Invalid cursor format',\n hint: 'Please provide a valid cursor from another page.'\n })\n );\n }\n }\n\n static fromId(id: string, type: 'after' | 'before') {\n return new Cursor(id, type);\n }\n\n toString() {\n return PREFIX + base62.encode(JSON.stringify([this.type, this.id]));\n }\n}\n","import { v, ValidationType } from '@lowerdeck/validation';\nimport { Cursor } from './cursor';\nimport {\n PaginatedProvider,\n PaginatedProviderInput,\n paginatedProviderMongoose,\n paginatedProviderPrisma\n} from './paginatedProvider';\nimport { PaginatedList } from './types';\n\nexport interface PaginatorInput {\n limit?: number | string;\n after?: string;\n before?: string;\n cursor?: string;\n order?: 'asc' | 'desc';\n}\n\nexport interface PaginatorOpts {\n defaultLimit?: number;\n defaultOrder?: 'asc' | 'desc';\n}\n\nexport type Provider<T> = (providers: {\n prisma: typeof paginatedProviderPrisma;\n mongoose: typeof paginatedProviderMongoose;\n}) => PaginatedProvider<T>;\n\nexport class Paginator<T> {\n private constructor(\n private provider: Provider<T>,\n private opts: PaginatorOpts = {}\n ) {}\n\n static create<T>(provider: Provider<T>, opts: PaginatorOpts = {}) {\n return new Paginator(provider, opts);\n }\n\n static validate<Inner extends object>(inner?: ValidationType<Inner>) {\n return v.intersection([\n v.object({\n limit: v.optional(\n v.number({\n modifiers: [v.minValue(1), v.maxValue(100)]\n })\n ),\n after: v.optional(v.string()),\n before: v.optional(v.string()),\n cursor: v.optional(v.string()),\n order: v.optional(v.enumOf(['asc', 'desc']))\n }),\n inner ?? v.object({})\n ]) as ValidationType<\n Inner & {\n limit?: number;\n after?: string;\n before?: string;\n cursor?: string;\n order?: 'asc' | 'desc';\n }\n >;\n }\n\n static present<T, R>(\n list: PaginatedList<T>,\n presenter: (item: T) => (context: any) => { run: (d: any) => R } | undefined\n ) {\n return (context: any) => ({\n run: async () => ({\n __typename: `list`,\n items: (\n await Promise.all(list.items.map(item => presenter(item)?.(context)?.run({})))\n ).filter(Boolean),\n pagination: {\n has_more_after: list.pagination.hasNextPage,\n has_more_before: list.pagination.hasPreviousPage\n }\n })\n });\n }\n\n static async presentLight<T, R>(\n list: PaginatedList<T>,\n presenter: (item: T) => R | Promise<R>\n ) {\n return {\n __typename: `list`,\n items: (await Promise.all(list.items.map(item => presenter(item)))).filter(Boolean),\n pagination: {\n has_more_after: list.pagination.hasNextPage,\n has_more_before: list.pagination.hasPreviousPage\n }\n };\n }\n\n async run(input: PaginatorInput): Promise<PaginatedList<T>> {\n let numberLimit = Number(input.limit);\n if (isNaN(numberLimit)) numberLimit = 20;\n\n let providerInput: PaginatedProviderInput = {\n limit: Math.max(\n Math.min(numberLimit ?? this.opts.defaultLimit ?? 20, this.opts.defaultLimit ?? 100),\n 1\n ),\n order: input.order ?? this.opts.defaultOrder ?? 'asc'\n };\n\n if (input.after) {\n providerInput.after = input.after;\n } else if (input.before) {\n providerInput.before = input.before;\n } else if (input.cursor) {\n let cursor = Cursor.fromString(input.cursor);\n providerInput[cursor.type] = cursor.id;\n }\n\n let provider = this.provider({\n prisma: paginatedProviderPrisma,\n mongoose: paginatedProviderMongoose\n });\n\n return await provider(providerInput);\n }\n}\n"],"names":["paginatedProviderPrisma","provider","input","limit","after","before","order","ServiceError","badRequestError","message","opts","orderBy","id","take","skip","cursorId","cursorType","cursor","Promise","resolve","then","items","orderedItems","cursorItem","find","item","undefined","cursorItemIndex","indexOf","orderedItemsWithoutCursor","concat","slice","selectedItems","length","hasItemsBefore","hasItemsAfter","pagination","hasNextPage","hasPreviousPage","e","reject","paginatedProviderMongoose","filter","sort","_id","$gt","$lt","reverse","hasMore","PREFIX","Cursor","type","this","fromString","str","startsWith","hint","decoded","base62","decode","_JSON$parse","JSON","parse","toString","Error","fromId","prototype","encode","stringify","Paginator","create","validate","inner","v","intersection","object","optional","number","modifiers","minValue","maxValue","string","enumOf","present","list","presenter","context","run","all","map","_presenter","_Promise$all","__typename","Boolean","has_more_after","has_more_before","presentLight","_Promise$all2","_ref","_this$opts$defaultLim","_ref2","_input$order","_this","numberLimit","Number","isNaN","providerInput","Math","max","min","defaultLimit","defaultOrder","prisma","mongoose"],"mappings":"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"}
|
package/dist/index.umd.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
!function(e,r){"object"==typeof exports&&"undefined"!=typeof module?r(exports,require("@lowerdeck/error"),require("@lowerdeck/validation"),require("@lowerdeck/base62")):"function"==typeof define&&define.amd?define(["exports","@lowerdeck/error","@lowerdeck/validation","@lowerdeck/base62"],r):r((e||self).pagination={},e.error,e.validation,e.base62)}(this,function(e,r,t,
|
|
1
|
+
!function(e,r){"object"==typeof exports&&"undefined"!=typeof module?r(exports,require("@lowerdeck/error"),require("@lowerdeck/validation"),require("@lowerdeck/base62")):"function"==typeof define&&define.amd?define(["exports","@lowerdeck/error","@lowerdeck/validation","@lowerdeck/base62"],r):r((e||self).pagination={},e.error,e.validation,e.base62)}(this,function(e,r,t,o){var i=function(e){return function(t){try{var o=t.limit,i=t.after,n=t.before,a=t.order;if(i&&n)throw new r.ServiceError(r.badRequestError({message:"Cannot use both after and before cursors"}));var s={orderBy:[{id:a}],take:o+2,skip:0},u=null!=i?i:n,f="none";return i?(s.cursor={id:i},f="after"):n&&(s.cursor={id:n},s.take=-s.take,s.skip=0,f="before"),Promise.resolve(e(s)).then(function(e){var r=e,t=u?null==r?void 0:r.find(function(e){return e.id==u}):void 0,i=t?null==r?void 0:r.indexOf(t):void 0,n="number"==typeof i?[].concat(r.slice(0,i),r.slice(i+1)):r,a=null==n?void 0:n.slice(0,o);"before"==f&&n.length>o&&(a=null==n?void 0:n.slice(1,o+1));var s=!1,l=!1;return"after"==f||"none"==f?(n.length>a.length&&(l=!0),t&&(s=!0)):"before"==f&&(n.length>a.length&&(s=!0),t&&(l=!0)),{items:a,pagination:{hasNextPage:l,hasPreviousPage:s}}})}catch(e){return Promise.reject(e)}}},n=function(e){return function(t){try{var o=t.limit,i=t.after,n=t.before,a=t.order;if(i&&n)throw new r.ServiceError(r.badRequestError({message:"Cannot use both after and before cursors"}));var s={},u={_id:"asc"===a?1:-1},f="none";return i?(s._id="asc"===a?{$gt:i}:{$lt:i},f="after"):n&&(s._id="asc"===a?{$lt:n}:{$gt:n},u._id="asc"===a?-1:1,f="before"),Promise.resolve(e({filter:s,sort:u,limit:o+1})).then(function(e){"before"===f&&(e=e.reverse());var r=e.length>o,t=r?e.slice(0,o):e,a=!1,s=!1;return"after"===f||"none"===f?(s=r,a=!!i):"before"===f&&(a=r,s=!!n),{items:t,pagination:{hasNextPage:s,hasPreviousPage:a}}})}catch(e){return Promise.reject(e)}}},a="cur_",s=/*#__PURE__*/function(){function e(e,r){this.id=void 0,this.type=void 0,this.id=e,this.type=r}return e.fromString=function(t){if(!t.startsWith(a))throw new r.ServiceError(r.badRequestError({message:"Invalid cursor format",hint:'Cursor must start with "cur_"'}));try{var i=o.base62.decode(t.slice(4)),n=JSON.parse(i.toString()),s=n[0],u=n[1];if("after"!==s&&"before"!==s)throw new Error("Invalid cursor type");if("string"!=typeof u)throw new Error("Invalid cursor id");return new e(u,s)}catch(e){throw new r.ServiceError(r.badRequestError({message:"Invalid cursor format",hint:"Please provide a valid cursor from another page."}))}},e.fromId=function(r,t){return new e(r,t)},e.prototype.toString=function(){return a+o.base62.encode(JSON.stringify([this.type,this.id]))},e}();e.Paginator=/*#__PURE__*/function(){function e(e,r){void 0===r&&(r={}),this.provider=void 0,this.opts=void 0,this.provider=e,this.opts=r}return e.create=function(r,t){return void 0===t&&(t={}),new e(r,t)},e.validate=function(e){return t.v.intersection([t.v.object({limit:t.v.optional(t.v.number({modifiers:[t.v.minValue(1),t.v.maxValue(100)]})),after:t.v.optional(t.v.string()),before:t.v.optional(t.v.string()),cursor:t.v.optional(t.v.string()),order:t.v.optional(t.v.enumOf(["asc","desc"]))}),null!=e?e:t.v.object({})])},e.present=function(e,r){return function(t){return{run:function(){try{return Promise.resolve(Promise.all(e.items.map(function(e){var o;return null==(o=r(e))||null==(o=o(t))?void 0:o.run({})}))).then(function(r){return{__typename:"list",items:r.filter(Boolean),pagination:{has_more_after:e.pagination.hasNextPage,has_more_before:e.pagination.hasPreviousPage}}})}catch(e){return Promise.reject(e)}}}}},e.presentLight=function(e,r){try{return Promise.resolve(Promise.all(e.items.map(function(e){return r(e)}))).then(function(r){return{__typename:"list",items:r.filter(Boolean),pagination:{has_more_after:e.pagination.hasNextPage,has_more_before:e.pagination.hasPreviousPage}}})}catch(e){return Promise.reject(e)}},e.prototype.run=function(e){try{var r,t,o,a,u=this,f=Number(e.limit);isNaN(f)&&(f=20);var l={limit:Math.max(Math.min(null!=(r=null!=f?f:u.opts.defaultLimit)?r:20,null!=(t=u.opts.defaultLimit)?t:100),1),order:null!=(o=null!=(a=e.order)?a:u.opts.defaultOrder)?o:"asc"};if(e.after)l.after=e.after;else if(e.before)l.before=e.before;else if(e.cursor){var c=s.fromString(e.cursor);l[c.type]=c.id}var d=u.provider({prisma:i,mongoose:n});return Promise.resolve(d(l))}catch(e){return Promise.reject(e)}},e}(),e.paginatedProviderMongoose=n,e.paginatedProviderPrisma=i});
|
|
2
2
|
//# sourceMappingURL=index.umd.js.map
|
package/dist/index.umd.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.umd.js","sources":["../src/paginatedProvider.ts","../src/cursor.ts","../src/paginator.ts"],"sourcesContent":["import { ServiceError, badRequestError } from '@lowerdeck/error';\nimport { PaginatedList } from './types';\n\nexport interface PaginatedProviderInput {\n limit: number;\n after?: string;\n before?: string;\n order: 'asc' | 'desc';\n}\n\nexport interface PrismaPaginationOpts {\n orderBy: [{ id: 'asc' | 'desc' }];\n cursor?: { id: string };\n take: number;\n skip: number;\n}\n\nexport type PaginatedProvider<T> = (\n input: PaginatedProviderInput\n) => Promise<PaginatedList<T>>;\n\nexport let paginatedProviderPrisma =\n <T extends { id: string }>(\n provider: (opts: PrismaPaginationOpts) => Promise<T[] | null | undefined>\n ): PaginatedProvider<T> =>\n async (input: PaginatedProviderInput) => {\n let { limit, after, before, order } = input;\n\n if (after && before) {\n throw new ServiceError(\n badRequestError({\n message: 'Cannot use both after and before cursors'\n })\n );\n }\n\n let opts: PrismaPaginationOpts = {\n orderBy: [{ id: order }],\n take: limit + 2,\n skip: 0\n };\n\n let cursorId = after ?? before;\n let cursorType: 'after' | 'before' | 'none' = 'none';\n\n if (after) {\n opts.cursor = { id: after };\n cursorType = 'after';\n } else if (before) {\n opts.cursor = { id: before };\n opts.take = -opts.take!;\n opts.skip = 0;\n cursorType = 'before';\n }\n\n let items = (await provider(opts)) ?? [];\n\n let orderedItems = items; /* items?.sort((a, b) => {\n if (order == 'asc') {\n return a.id.localeCompare(b.id);\n } else {\n return b.id.localeCompare(a.id);\n }\n });*/\n\n let cursorItem = cursorId ? orderedItems?.find(item => item.id == cursorId) : undefined;\n let cursorItemIndex = cursorItem ? orderedItems?.indexOf(cursorItem) : undefined;\n let orderedItemsWithoutCursor =\n typeof cursorItemIndex == 'number'\n ? [\n ...orderedItems.slice(0, cursorItemIndex),\n ...orderedItems.slice(cursorItemIndex + 1)\n ]\n : orderedItems;\n\n let selectedItems = orderedItemsWithoutCursor?.slice(0, limit);\n\n if (cursorType == 'before' && orderedItemsWithoutCursor.length > limit) {\n selectedItems = orderedItemsWithoutCursor?.slice(1, limit + 1);\n }\n\n let hasItemsBefore = false;\n let hasItemsAfter = false;\n\n if (cursorType == 'after' || cursorType == 'none') {\n if (orderedItemsWithoutCursor.length > selectedItems.length) hasItemsAfter = true;\n if (cursorItem) hasItemsBefore = true;\n } else if (cursorType == 'before') {\n if (orderedItemsWithoutCursor.length > selectedItems.length) hasItemsBefore = true;\n if (cursorItem) hasItemsAfter = true;\n }\n\n return {\n items: selectedItems,\n pagination: {\n hasNextPage: hasItemsAfter,\n hasPreviousPage: hasItemsBefore\n }\n };\n };\n","import { base62 } from '@lowerdeck/base62';\nimport { badRequestError, ServiceError } from '@lowerdeck/error';\n\nlet PREFIX = 'cur_';\n\nexport class Cursor {\n private constructor(\n public readonly id: string,\n public readonly type: 'after' | 'before'\n ) {}\n\n static fromString(str: string) {\n if (!str.startsWith(PREFIX)) {\n throw new ServiceError(\n badRequestError({\n message: 'Invalid cursor format',\n hint: 'Cursor must start with \"cur_\"'\n })\n );\n }\n\n try {\n let decoded = base62.decode(str.slice(PREFIX.length));\n let [type, id] = JSON.parse(decoded.toString());\n\n if (type !== 'after' && type !== 'before') {\n throw new Error('Invalid cursor type');\n }\n\n if (typeof id !== 'string') {\n throw new Error('Invalid cursor id');\n }\n\n return new Cursor(id, type);\n } catch (e) {\n throw new ServiceError(\n badRequestError({\n message: 'Invalid cursor format',\n hint: 'Please provide a valid cursor from another page.'\n })\n );\n }\n }\n\n static fromId(id: string, type: 'after' | 'before') {\n return new Cursor(id, type);\n }\n\n toString() {\n return PREFIX + base62.encode(JSON.stringify([this.type, this.id]));\n }\n}\n","import { v, ValidationType } from '@lowerdeck/validation';\nimport { Cursor } from './cursor';\nimport {\n PaginatedProvider,\n PaginatedProviderInput,\n paginatedProviderPrisma\n} from './paginatedProvider';\nimport { PaginatedList } from './types';\n\nexport interface PaginatorInput {\n limit?: number | string;\n after?: string;\n before?: string;\n cursor?: string;\n order?: 'asc' | 'desc';\n}\n\nexport interface PaginatorOpts {\n defaultLimit?: number;\n defaultOrder?: 'asc' | 'desc';\n}\n\nexport type Provider<T> = (providers: {\n prisma: typeof paginatedProviderPrisma;\n}) => PaginatedProvider<T>;\n\nexport class Paginator<T> {\n private constructor(\n private provider: Provider<T>,\n private opts: PaginatorOpts = {}\n ) {}\n\n static create<T>(provider: Provider<T>, opts: PaginatorOpts = {}) {\n return new Paginator(provider, opts);\n }\n\n static validate<Inner extends object>(inner?: ValidationType<Inner>) {\n return v.intersection([\n v.object({\n limit: v.optional(\n v.number({\n modifiers: [v.minValue(1), v.maxValue(100)]\n })\n ),\n after: v.optional(v.string()),\n before: v.optional(v.string()),\n cursor: v.optional(v.string()),\n order: v.optional(v.enumOf(['asc', 'desc']))\n }),\n inner ?? v.object({})\n ]) as ValidationType<\n Inner & {\n limit?: number;\n after?: string;\n before?: string;\n cursor?: string;\n order?: 'asc' | 'desc';\n }\n >;\n }\n\n static present<T, R>(\n list: PaginatedList<T>,\n presenter: (item: T) => (context: any) => { run: (d: any) => R } | undefined\n ) {\n return (context: any) => ({\n run: async () => ({\n __typename: `list`,\n items: (\n await Promise.all(list.items.map(item => presenter(item)?.(context)?.run({})))\n ).filter(Boolean),\n pagination: {\n has_more_after: list.pagination.hasNextPage,\n has_more_before: list.pagination.hasPreviousPage\n }\n })\n });\n }\n\n static async presentLight<T, R>(\n list: PaginatedList<T>,\n presenter: (item: T) => R | Promise<R>\n ) {\n return {\n __typename: `list`,\n items: (await Promise.all(list.items.map(item => presenter(item)))).filter(Boolean),\n pagination: {\n has_more_after: list.pagination.hasNextPage,\n has_more_before: list.pagination.hasPreviousPage\n }\n };\n }\n\n async run(input: PaginatorInput): Promise<PaginatedList<T>> {\n let numberLimit = Number(input.limit);\n if (isNaN(numberLimit)) numberLimit = 20;\n\n let providerInput: PaginatedProviderInput = {\n limit: Math.max(\n Math.min(numberLimit ?? this.opts.defaultLimit ?? 20, this.opts.defaultLimit ?? 100),\n 1\n ),\n order: input.order ?? this.opts.defaultOrder ?? 'asc'\n };\n\n if (input.after) {\n providerInput.after = input.after;\n } else if (input.before) {\n providerInput.before = input.before;\n } else if (input.cursor) {\n let cursor = Cursor.fromString(input.cursor);\n providerInput[cursor.type] = cursor.id;\n }\n\n let provider = this.provider({\n prisma: paginatedProviderPrisma\n });\n\n return await provider(providerInput);\n }\n}\n"],"names":["paginatedProviderPrisma","provider","input","limit","after","before","order","ServiceError","badRequestError","message","opts","orderBy","id","take","skip","cursorId","cursorType","cursor","Promise","resolve","then","items","orderedItems","cursorItem","find","item","undefined","cursorItemIndex","indexOf","orderedItemsWithoutCursor","concat","slice","selectedItems","length","hasItemsBefore","hasItemsAfter","pagination","hasNextPage","hasPreviousPage","e","reject","PREFIX","Cursor","type","this","fromString","str","startsWith","hint","decoded","base62","decode","_JSON$parse","JSON","parse","toString","Error","fromId","prototype","encode","stringify","Paginator","create","validate","inner","v","intersection","object","optional","number","modifiers","minValue","maxValue","string","enumOf","present","list","presenter","context","run","all","map","_presenter","_Promise$all","__typename","filter","Boolean","has_more_after","has_more_before","presentLight","_Promise$all2","_ref","_this$opts$defaultLim","_ref2","_input$order","_this","numberLimit","Number","isNaN","providerInput","Math","max","min","defaultLimit","defaultOrder","prisma"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.umd.js","sources":["../src/paginatedProvider.ts","../src/cursor.ts","../src/paginator.ts"],"sourcesContent":["import { ServiceError, badRequestError } from '@lowerdeck/error';\nimport type { QueryFilter, SortOrder } from 'mongoose';\nimport { PaginatedList } from './types';\n\nexport interface PaginatedProviderInput {\n limit: number;\n after?: string;\n before?: string;\n order: 'asc' | 'desc';\n}\n\nexport interface PrismaPaginationOpts {\n orderBy: [{ id: 'asc' | 'desc' }];\n cursor?: { id: string };\n take: number;\n skip: number;\n}\n\nexport interface MongoosePaginationOpts<T> {\n filter: QueryFilter<T>;\n sort: { _id: SortOrder };\n limit: number;\n}\n\nexport type PaginatedProvider<T> = (\n input: PaginatedProviderInput\n) => Promise<PaginatedList<T>>;\n\nexport let paginatedProviderPrisma =\n <T extends { id: string }>(\n provider: (opts: PrismaPaginationOpts) => Promise<T[] | null | undefined>\n ): PaginatedProvider<T> =>\n async (input: PaginatedProviderInput) => {\n let { limit, after, before, order } = input;\n\n if (after && before) {\n throw new ServiceError(\n badRequestError({\n message: 'Cannot use both after and before cursors'\n })\n );\n }\n\n let opts: PrismaPaginationOpts = {\n orderBy: [{ id: order }],\n take: limit + 2,\n skip: 0\n };\n\n let cursorId = after ?? before;\n let cursorType: 'after' | 'before' | 'none' = 'none';\n\n if (after) {\n opts.cursor = { id: after };\n cursorType = 'after';\n } else if (before) {\n opts.cursor = { id: before };\n opts.take = -opts.take!;\n opts.skip = 0;\n cursorType = 'before';\n }\n\n let items = (await provider(opts)) ?? [];\n\n let orderedItems = items; /* items?.sort((a, b) => {\n if (order == 'asc') {\n return a.id.localeCompare(b.id);\n } else {\n return b.id.localeCompare(a.id);\n }\n });*/\n\n let cursorItem = cursorId ? orderedItems?.find(item => item.id == cursorId) : undefined;\n let cursorItemIndex = cursorItem ? orderedItems?.indexOf(cursorItem) : undefined;\n let orderedItemsWithoutCursor =\n typeof cursorItemIndex == 'number'\n ? [\n ...orderedItems.slice(0, cursorItemIndex),\n ...orderedItems.slice(cursorItemIndex + 1)\n ]\n : orderedItems;\n\n let selectedItems = orderedItemsWithoutCursor?.slice(0, limit);\n\n if (cursorType == 'before' && orderedItemsWithoutCursor.length > limit) {\n selectedItems = orderedItemsWithoutCursor?.slice(1, limit + 1);\n }\n\n let hasItemsBefore = false;\n let hasItemsAfter = false;\n\n if (cursorType == 'after' || cursorType == 'none') {\n if (orderedItemsWithoutCursor.length > selectedItems.length) hasItemsAfter = true;\n if (cursorItem) hasItemsBefore = true;\n } else if (cursorType == 'before') {\n if (orderedItemsWithoutCursor.length > selectedItems.length) hasItemsBefore = true;\n if (cursorItem) hasItemsAfter = true;\n }\n\n return {\n items: selectedItems,\n pagination: {\n hasNextPage: hasItemsAfter,\n hasPreviousPage: hasItemsBefore\n }\n };\n };\n\nexport let paginatedProviderMongoose =\n <T extends { _id: string }>(\n provider: (opts: MongoosePaginationOpts<T>) => Promise<T[] | null | undefined>\n ): PaginatedProvider<T> =>\n async (input: PaginatedProviderInput) => {\n let { limit, after, before, order } = input;\n\n if (after && before) {\n throw new ServiceError(\n badRequestError({\n message: 'Cannot use both after and before cursors'\n })\n );\n }\n\n let filter: QueryFilter<T> = {};\n let sort: { _id: SortOrder } = { _id: order === 'asc' ? 1 : -1 };\n let cursorId = after ?? before;\n let cursorType: 'after' | 'before' | 'none' = 'none';\n\n if (after) {\n filter._id = order === 'asc' ? { $gt: after } : { $lt: after };\n cursorType = 'after';\n } else if (before) {\n filter._id = order === 'asc' ? { $lt: before } : { $gt: before };\n sort._id = order === 'asc' ? -1 : 1;\n cursorType = 'before';\n }\n\n let opts: MongoosePaginationOpts<T> = {\n filter,\n sort,\n limit: limit + 1\n };\n\n let items = (await provider(opts)) ?? [];\n\n if (cursorType === 'before') items = items.reverse();\n\n let hasMore = items.length > limit;\n let selectedItems = hasMore ? items.slice(0, limit) : items;\n\n let hasItemsBefore = false;\n let hasItemsAfter = false;\n\n if (cursorType === 'after' || cursorType === 'none') {\n hasItemsAfter = hasMore;\n hasItemsBefore = !!after;\n } else if (cursorType === 'before') {\n hasItemsBefore = hasMore;\n hasItemsAfter = !!before;\n }\n\n return {\n items: selectedItems,\n pagination: {\n hasNextPage: hasItemsAfter,\n hasPreviousPage: hasItemsBefore\n }\n };\n };\n","import { base62 } from '@lowerdeck/base62';\nimport { badRequestError, ServiceError } from '@lowerdeck/error';\n\nlet PREFIX = 'cur_';\n\nexport class Cursor {\n private constructor(\n public readonly id: string,\n public readonly type: 'after' | 'before'\n ) {}\n\n static fromString(str: string) {\n if (!str.startsWith(PREFIX)) {\n throw new ServiceError(\n badRequestError({\n message: 'Invalid cursor format',\n hint: 'Cursor must start with \"cur_\"'\n })\n );\n }\n\n try {\n let decoded = base62.decode(str.slice(PREFIX.length));\n let [type, id] = JSON.parse(decoded.toString());\n\n if (type !== 'after' && type !== 'before') {\n throw new Error('Invalid cursor type');\n }\n\n if (typeof id !== 'string') {\n throw new Error('Invalid cursor id');\n }\n\n return new Cursor(id, type);\n } catch (e) {\n throw new ServiceError(\n badRequestError({\n message: 'Invalid cursor format',\n hint: 'Please provide a valid cursor from another page.'\n })\n );\n }\n }\n\n static fromId(id: string, type: 'after' | 'before') {\n return new Cursor(id, type);\n }\n\n toString() {\n return PREFIX + base62.encode(JSON.stringify([this.type, this.id]));\n }\n}\n","import { v, ValidationType } from '@lowerdeck/validation';\nimport { Cursor } from './cursor';\nimport {\n PaginatedProvider,\n PaginatedProviderInput,\n paginatedProviderMongoose,\n paginatedProviderPrisma\n} from './paginatedProvider';\nimport { PaginatedList } from './types';\n\nexport interface PaginatorInput {\n limit?: number | string;\n after?: string;\n before?: string;\n cursor?: string;\n order?: 'asc' | 'desc';\n}\n\nexport interface PaginatorOpts {\n defaultLimit?: number;\n defaultOrder?: 'asc' | 'desc';\n}\n\nexport type Provider<T> = (providers: {\n prisma: typeof paginatedProviderPrisma;\n mongoose: typeof paginatedProviderMongoose;\n}) => PaginatedProvider<T>;\n\nexport class Paginator<T> {\n private constructor(\n private provider: Provider<T>,\n private opts: PaginatorOpts = {}\n ) {}\n\n static create<T>(provider: Provider<T>, opts: PaginatorOpts = {}) {\n return new Paginator(provider, opts);\n }\n\n static validate<Inner extends object>(inner?: ValidationType<Inner>) {\n return v.intersection([\n v.object({\n limit: v.optional(\n v.number({\n modifiers: [v.minValue(1), v.maxValue(100)]\n })\n ),\n after: v.optional(v.string()),\n before: v.optional(v.string()),\n cursor: v.optional(v.string()),\n order: v.optional(v.enumOf(['asc', 'desc']))\n }),\n inner ?? v.object({})\n ]) as ValidationType<\n Inner & {\n limit?: number;\n after?: string;\n before?: string;\n cursor?: string;\n order?: 'asc' | 'desc';\n }\n >;\n }\n\n static present<T, R>(\n list: PaginatedList<T>,\n presenter: (item: T) => (context: any) => { run: (d: any) => R } | undefined\n ) {\n return (context: any) => ({\n run: async () => ({\n __typename: `list`,\n items: (\n await Promise.all(list.items.map(item => presenter(item)?.(context)?.run({})))\n ).filter(Boolean),\n pagination: {\n has_more_after: list.pagination.hasNextPage,\n has_more_before: list.pagination.hasPreviousPage\n }\n })\n });\n }\n\n static async presentLight<T, R>(\n list: PaginatedList<T>,\n presenter: (item: T) => R | Promise<R>\n ) {\n return {\n __typename: `list`,\n items: (await Promise.all(list.items.map(item => presenter(item)))).filter(Boolean),\n pagination: {\n has_more_after: list.pagination.hasNextPage,\n has_more_before: list.pagination.hasPreviousPage\n }\n };\n }\n\n async run(input: PaginatorInput): Promise<PaginatedList<T>> {\n let numberLimit = Number(input.limit);\n if (isNaN(numberLimit)) numberLimit = 20;\n\n let providerInput: PaginatedProviderInput = {\n limit: Math.max(\n Math.min(numberLimit ?? this.opts.defaultLimit ?? 20, this.opts.defaultLimit ?? 100),\n 1\n ),\n order: input.order ?? this.opts.defaultOrder ?? 'asc'\n };\n\n if (input.after) {\n providerInput.after = input.after;\n } else if (input.before) {\n providerInput.before = input.before;\n } else if (input.cursor) {\n let cursor = Cursor.fromString(input.cursor);\n providerInput[cursor.type] = cursor.id;\n }\n\n let provider = this.provider({\n prisma: paginatedProviderPrisma,\n mongoose: paginatedProviderMongoose\n });\n\n return await provider(providerInput);\n }\n}\n"],"names":["paginatedProviderPrisma","provider","input","limit","after","before","order","ServiceError","badRequestError","message","opts","orderBy","id","take","skip","cursorId","cursorType","cursor","Promise","resolve","then","items","orderedItems","cursorItem","find","item","undefined","cursorItemIndex","indexOf","orderedItemsWithoutCursor","concat","slice","selectedItems","length","hasItemsBefore","hasItemsAfter","pagination","hasNextPage","hasPreviousPage","e","reject","paginatedProviderMongoose","filter","sort","_id","$gt","$lt","reverse","hasMore","PREFIX","Cursor","type","this","fromString","str","startsWith","hint","decoded","base62","decode","_JSON$parse","JSON","parse","toString","Error","fromId","prototype","encode","stringify","Paginator","create","validate","inner","v","intersection","object","optional","number","modifiers","minValue","maxValue","string","enumOf","present","list","presenter","context","run","all","map","_presenter","_Promise$all","__typename","Boolean","has_more_after","has_more_before","presentLight","_Promise$all2","_ref","_this$opts$defaultLim","_ref2","_input$order","_this","numberLimit","Number","isNaN","providerInput","Math","max","min","defaultLimit","defaultOrder","prisma","mongoose"],"mappings":"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"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { QueryFilter, SortOrder } from 'mongoose';
|
|
1
2
|
import { PaginatedList } from './types';
|
|
2
3
|
export interface PaginatedProviderInput {
|
|
3
4
|
limit: number;
|
|
@@ -15,8 +16,18 @@ export interface PrismaPaginationOpts {
|
|
|
15
16
|
take: number;
|
|
16
17
|
skip: number;
|
|
17
18
|
}
|
|
19
|
+
export interface MongoosePaginationOpts<T> {
|
|
20
|
+
filter: QueryFilter<T>;
|
|
21
|
+
sort: {
|
|
22
|
+
_id: SortOrder;
|
|
23
|
+
};
|
|
24
|
+
limit: number;
|
|
25
|
+
}
|
|
18
26
|
export type PaginatedProvider<T> = (input: PaginatedProviderInput) => Promise<PaginatedList<T>>;
|
|
19
27
|
export declare let paginatedProviderPrisma: <T extends {
|
|
20
28
|
id: string;
|
|
21
29
|
}>(provider: (opts: PrismaPaginationOpts) => Promise<T[] | null | undefined>) => PaginatedProvider<T>;
|
|
30
|
+
export declare let paginatedProviderMongoose: <T extends {
|
|
31
|
+
_id: string;
|
|
32
|
+
}>(provider: (opts: MongoosePaginationOpts<T>) => Promise<T[] | null | undefined>) => PaginatedProvider<T>;
|
|
22
33
|
//# sourceMappingURL=paginatedProvider.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"paginatedProvider.d.ts","sourceRoot":"","sources":["../src/paginatedProvider.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAExC,MAAM,WAAW,sBAAsB;IACrC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,KAAK,GAAG,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,CAAC;QAAE,EAAE,EAAE,KAAK,GAAG,MAAM,CAAA;KAAE,CAAC,CAAC;IAClC,MAAM,CAAC,EAAE;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,MAAM,iBAAiB,CAAC,CAAC,IAAI,CACjC,KAAK,EAAE,sBAAsB,KAC1B,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC;AAE/B,eAAO,IAAI,uBAAuB,GAC/B,CAAC,SAAS;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,EACvB,UAAU,CAAC,IAAI,EAAE,oBAAoB,KAAK,OAAO,CAAC,CAAC,EAAE,GAAG,IAAI,GAAG,SAAS,CAAC,KACxE,iBAAiB,CAAC,CAAC,CA2ErB,CAAC"}
|
|
1
|
+
{"version":3,"file":"paginatedProvider.d.ts","sourceRoot":"","sources":["../src/paginatedProvider.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAExC,MAAM,WAAW,sBAAsB;IACrC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,KAAK,GAAG,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,CAAC;QAAE,EAAE,EAAE,KAAK,GAAG,MAAM,CAAA;KAAE,CAAC,CAAC;IAClC,MAAM,CAAC,EAAE;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,sBAAsB,CAAC,CAAC;IACvC,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC;IACvB,IAAI,EAAE;QAAE,GAAG,EAAE,SAAS,CAAA;KAAE,CAAC;IACzB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,MAAM,iBAAiB,CAAC,CAAC,IAAI,CACjC,KAAK,EAAE,sBAAsB,KAC1B,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC;AAE/B,eAAO,IAAI,uBAAuB,GAC/B,CAAC,SAAS;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,EACvB,UAAU,CAAC,IAAI,EAAE,oBAAoB,KAAK,OAAO,CAAC,CAAC,EAAE,GAAG,IAAI,GAAG,SAAS,CAAC,KACxE,iBAAiB,CAAC,CAAC,CA2ErB,CAAC;AAEJ,eAAO,IAAI,yBAAyB,GACjC,CAAC,SAAS;IAAE,GAAG,EAAE,MAAM,CAAA;CAAE,EACxB,UAAU,CAAC,IAAI,EAAE,sBAAsB,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,EAAE,GAAG,IAAI,GAAG,SAAS,CAAC,KAC7E,iBAAiB,CAAC,CAAC,CAyDrB,CAAC"}
|
package/dist/paginator.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ValidationType } from '@lowerdeck/validation';
|
|
2
|
-
import { PaginatedProvider, paginatedProviderPrisma } from './paginatedProvider';
|
|
2
|
+
import { PaginatedProvider, paginatedProviderMongoose, paginatedProviderPrisma } from './paginatedProvider';
|
|
3
3
|
import { PaginatedList } from './types';
|
|
4
4
|
export interface PaginatorInput {
|
|
5
5
|
limit?: number | string;
|
|
@@ -14,6 +14,7 @@ export interface PaginatorOpts {
|
|
|
14
14
|
}
|
|
15
15
|
export type Provider<T> = (providers: {
|
|
16
16
|
prisma: typeof paginatedProviderPrisma;
|
|
17
|
+
mongoose: typeof paginatedProviderMongoose;
|
|
17
18
|
}) => PaginatedProvider<T>;
|
|
18
19
|
export declare class Paginator<T> {
|
|
19
20
|
private provider;
|
package/dist/paginator.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"paginator.d.ts","sourceRoot":"","sources":["../src/paginator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAK,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAE1D,OAAO,EACL,iBAAiB,EAEjB,uBAAuB,EACxB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAExC,MAAM,WAAW,cAAc;IAC7B,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,aAAa;IAC5B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;CAC/B;AAED,MAAM,MAAM,QAAQ,CAAC,CAAC,IAAI,CAAC,SAAS,EAAE;IACpC,MAAM,EAAE,OAAO,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"paginator.d.ts","sourceRoot":"","sources":["../src/paginator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAK,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAE1D,OAAO,EACL,iBAAiB,EAEjB,yBAAyB,EACzB,uBAAuB,EACxB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAExC,MAAM,WAAW,cAAc;IAC7B,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,aAAa;IAC5B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;CAC/B;AAED,MAAM,MAAM,QAAQ,CAAC,CAAC,IAAI,CAAC,SAAS,EAAE;IACpC,MAAM,EAAE,OAAO,uBAAuB,CAAC;IACvC,QAAQ,EAAE,OAAO,yBAAyB,CAAC;CAC5C,KAAK,iBAAiB,CAAC,CAAC,CAAC,CAAC;AAE3B,qBAAa,SAAS,CAAC,CAAC;IAEpB,OAAO,CAAC,QAAQ;IAChB,OAAO,CAAC,IAAI;IAFd,OAAO;IAKP,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,IAAI,GAAE,aAAkB;IAIhE,MAAM,CAAC,QAAQ,CAAC,KAAK,SAAS,MAAM,EAAE,KAAK,CAAC,EAAE,cAAc,CAAC,KAAK,CAAC,GAc3D,cAAc,CAClB,KAAK,GAAG;QACN,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,KAAK,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;KACxB,CACF;IAGH,MAAM,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,EACjB,IAAI,EAAE,aAAa,CAAC,CAAC,CAAC,EACtB,SAAS,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,GAAG,KAAK;QAAE,GAAG,EAAE,CAAC,CAAC,EAAE,GAAG,KAAK,CAAC,CAAA;KAAE,GAAG,SAAS,IAEpE,SAAS,GAAG;;;;;;;;;;WAcT,YAAY,CAAC,CAAC,EAAE,CAAC,EAC5B,IAAI,EAAE,aAAa,CAAC,CAAC,CAAC,EACtB,SAAS,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;;;;;;;;IAYlC,GAAG,CAAC,KAAK,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;CA4B5D"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lowerdeck/pagination",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -24,8 +24,9 @@
|
|
|
24
24
|
"build": "microbundle"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
|
-
"microbundle": "^0.15.1",
|
|
28
27
|
"@lowerdeck/tsconfig": "^1.0.0",
|
|
28
|
+
"microbundle": "^0.15.1",
|
|
29
|
+
"mongoose": "^9.0.2",
|
|
29
30
|
"typescript": "^5.9.3",
|
|
30
31
|
"vitest": "^3.2.4"
|
|
31
32
|
},
|
package/src/paginatedProvider.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ServiceError, badRequestError } from '@lowerdeck/error';
|
|
2
|
+
import type { QueryFilter, SortOrder } from 'mongoose';
|
|
2
3
|
import { PaginatedList } from './types';
|
|
3
4
|
|
|
4
5
|
export interface PaginatedProviderInput {
|
|
@@ -15,6 +16,12 @@ export interface PrismaPaginationOpts {
|
|
|
15
16
|
skip: number;
|
|
16
17
|
}
|
|
17
18
|
|
|
19
|
+
export interface MongoosePaginationOpts<T> {
|
|
20
|
+
filter: QueryFilter<T>;
|
|
21
|
+
sort: { _id: SortOrder };
|
|
22
|
+
limit: number;
|
|
23
|
+
}
|
|
24
|
+
|
|
18
25
|
export type PaginatedProvider<T> = (
|
|
19
26
|
input: PaginatedProviderInput
|
|
20
27
|
) => Promise<PaginatedList<T>>;
|
|
@@ -98,3 +105,65 @@ export let paginatedProviderPrisma =
|
|
|
98
105
|
}
|
|
99
106
|
};
|
|
100
107
|
};
|
|
108
|
+
|
|
109
|
+
export let paginatedProviderMongoose =
|
|
110
|
+
<T extends { _id: string }>(
|
|
111
|
+
provider: (opts: MongoosePaginationOpts<T>) => Promise<T[] | null | undefined>
|
|
112
|
+
): PaginatedProvider<T> =>
|
|
113
|
+
async (input: PaginatedProviderInput) => {
|
|
114
|
+
let { limit, after, before, order } = input;
|
|
115
|
+
|
|
116
|
+
if (after && before) {
|
|
117
|
+
throw new ServiceError(
|
|
118
|
+
badRequestError({
|
|
119
|
+
message: 'Cannot use both after and before cursors'
|
|
120
|
+
})
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
let filter: QueryFilter<T> = {};
|
|
125
|
+
let sort: { _id: SortOrder } = { _id: order === 'asc' ? 1 : -1 };
|
|
126
|
+
let cursorId = after ?? before;
|
|
127
|
+
let cursorType: 'after' | 'before' | 'none' = 'none';
|
|
128
|
+
|
|
129
|
+
if (after) {
|
|
130
|
+
filter._id = order === 'asc' ? { $gt: after } : { $lt: after };
|
|
131
|
+
cursorType = 'after';
|
|
132
|
+
} else if (before) {
|
|
133
|
+
filter._id = order === 'asc' ? { $lt: before } : { $gt: before };
|
|
134
|
+
sort._id = order === 'asc' ? -1 : 1;
|
|
135
|
+
cursorType = 'before';
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
let opts: MongoosePaginationOpts<T> = {
|
|
139
|
+
filter,
|
|
140
|
+
sort,
|
|
141
|
+
limit: limit + 1
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
let items = (await provider(opts)) ?? [];
|
|
145
|
+
|
|
146
|
+
if (cursorType === 'before') items = items.reverse();
|
|
147
|
+
|
|
148
|
+
let hasMore = items.length > limit;
|
|
149
|
+
let selectedItems = hasMore ? items.slice(0, limit) : items;
|
|
150
|
+
|
|
151
|
+
let hasItemsBefore = false;
|
|
152
|
+
let hasItemsAfter = false;
|
|
153
|
+
|
|
154
|
+
if (cursorType === 'after' || cursorType === 'none') {
|
|
155
|
+
hasItemsAfter = hasMore;
|
|
156
|
+
hasItemsBefore = !!after;
|
|
157
|
+
} else if (cursorType === 'before') {
|
|
158
|
+
hasItemsBefore = hasMore;
|
|
159
|
+
hasItemsAfter = !!before;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return {
|
|
163
|
+
items: selectedItems,
|
|
164
|
+
pagination: {
|
|
165
|
+
hasNextPage: hasItemsAfter,
|
|
166
|
+
hasPreviousPage: hasItemsBefore
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
};
|
package/src/paginator.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { Cursor } from './cursor';
|
|
|
3
3
|
import {
|
|
4
4
|
PaginatedProvider,
|
|
5
5
|
PaginatedProviderInput,
|
|
6
|
+
paginatedProviderMongoose,
|
|
6
7
|
paginatedProviderPrisma
|
|
7
8
|
} from './paginatedProvider';
|
|
8
9
|
import { PaginatedList } from './types';
|
|
@@ -22,6 +23,7 @@ export interface PaginatorOpts {
|
|
|
22
23
|
|
|
23
24
|
export type Provider<T> = (providers: {
|
|
24
25
|
prisma: typeof paginatedProviderPrisma;
|
|
26
|
+
mongoose: typeof paginatedProviderMongoose;
|
|
25
27
|
}) => PaginatedProvider<T>;
|
|
26
28
|
|
|
27
29
|
export class Paginator<T> {
|
|
@@ -113,7 +115,8 @@ export class Paginator<T> {
|
|
|
113
115
|
}
|
|
114
116
|
|
|
115
117
|
let provider = this.provider({
|
|
116
|
-
prisma: paginatedProviderPrisma
|
|
118
|
+
prisma: paginatedProviderPrisma,
|
|
119
|
+
mongoose: paginatedProviderMongoose
|
|
117
120
|
});
|
|
118
121
|
|
|
119
122
|
return await provider(providerInput);
|