From 376dc88f77b179519429c4042c7d5ac738532fef Mon Sep 17 00:00:00 2001 From: David Plugge Date: Mon, 26 Jun 2023 21:15:50 +0200 Subject: [PATCH 01/15] Add expand and nested filter support --- .changeset/fresh-pugs-repair.md | 5 + .changeset/pre.json | 10 ++ .prettierrc | 2 +- CHANGELOG.md | 6 + docker-compose.yml | 21 +++ example/Database.ts | 61 ++++++++ example/index.ts | 52 ++++++- package.json | 18 ++- pnpm-lock.yaml | 32 +++- src/codegen/ambient.d.ts | 2 + src/codegen/index.ts | 264 ++++++++++++++++++++++++++++++++ src/expand.ts | 63 ++++++++ src/fields.ts | 2 +- src/file-service.ts | 39 +++++ src/filter.ts | 24 ++- src/index.ts | 24 ++- src/pocketbase.ts | 11 -- src/record-service.ts | 187 +++++++++++++++------- src/sort.ts | 6 +- src/types.ts | 115 +++++++++++++- tsconfig.json | 5 +- tsup.config.ts | 31 +++- 22 files changed, 863 insertions(+), 117 deletions(-) create mode 100644 .changeset/fresh-pugs-repair.md create mode 100644 .changeset/pre.json create mode 100644 docker-compose.yml create mode 100644 example/Database.ts create mode 100644 src/codegen/ambient.d.ts create mode 100644 src/codegen/index.ts create mode 100644 src/expand.ts create mode 100644 src/file-service.ts delete mode 100644 src/pocketbase.ts diff --git a/.changeset/fresh-pugs-repair.md b/.changeset/fresh-pugs-repair.md new file mode 100644 index 0000000..5bbd28b --- /dev/null +++ b/.changeset/fresh-pugs-repair.md @@ -0,0 +1,5 @@ +--- +'typed-pocketbase': patch +--- + +Add expand and nested filter support diff --git a/.changeset/pre.json b/.changeset/pre.json new file mode 100644 index 0000000..0bd9bf5 --- /dev/null +++ b/.changeset/pre.json @@ -0,0 +1,10 @@ +{ + "mode": "pre", + "tag": "next", + "initialVersions": { + "typed-pocketbase": "0.0.2" + }, + "changesets": [ + "fresh-pugs-repair" + ] +} diff --git a/.prettierrc b/.prettierrc index decd7ec..62fdbfe 100644 --- a/.prettierrc +++ b/.prettierrc @@ -2,6 +2,6 @@ "useTabs": true, "singleQuote": true, "trailingComma": "none", - "printWidth": 100, + "printWidth": 80, "tabWidth": 4 } diff --git a/CHANGELOG.md b/CHANGELOG.md index bce4c65..bb4e426 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # typed-pocketbase +## 0.0.3-next.0 + +### Patch Changes + +- Add expand and nested filter support + ## 0.0.2 ### Patch Changes diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..13d806d --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,21 @@ +version: "3.7" + +services: + pocketbase: + image: ghcr.io/muchobien/pocketbase:latest + container_name: pocketbase + restart: unless-stopped + environment: + ENCRYPTION: example #optional + ports: + - "8090:8090" + volumes: + - pb_data:/pb_data + healthcheck: #optional (recommended) since v0.10.0 + test: wget --no-verbose --tries=1 --spider http://localhost:8090/api/health || exit 1 + interval: 5s + timeout: 5s + retries: 5 + +volumes: + pb_data: \ No newline at end of file diff --git a/example/Database.ts b/example/Database.ts new file mode 100644 index 0000000..6aefc4a --- /dev/null +++ b/example/Database.ts @@ -0,0 +1,61 @@ +export type BaseSystemFields = { + id: string; + created: string; + updated: string; + collectionId: string; + collectionName: T; +}; + +export type AuthSystemFields = { + email: string; + emailVisibility: boolean; + username: string; + verified: boolean; +} & BaseSystemFields; + +export type UsersRecord = { + name?: string; + avatar?: string; +}; + +export type UsersResponse = UsersRecord & AuthSystemFields<'users'>; + +export type UsersCollection = { + collectionId: '_pb_users_auth_'; + collectionName: 'users'; + record: UsersRecord; + response: UsersResponse; + files: { + avatar: { thumbs: never }; + }; + relations: { + 'posts(owner)': PostsCollection[]; + }; +}; + +export type PostsRecord = { + title: string; + slug: string; + date?: string; + content?: string; + published?: boolean; + owner?: string; +}; + +export type PostsResponse = PostsRecord & BaseSystemFields<'posts'>; + +export type PostsCollection = { + collectionId: 'sbrth2mzfnqba9e'; + collectionName: 'posts'; + record: PostsRecord; + response: PostsResponse; + files: {}; + relations: { + owner: UsersCollection; + }; +}; + +export type Schema = { + users: UsersCollection; + posts: PostsCollection; +}; diff --git a/example/index.ts b/example/index.ts index 0747480..2856248 100644 --- a/example/index.ts +++ b/example/index.ts @@ -1,11 +1,47 @@ import PocketBase from 'pocketbase'; -import { CollectionRecords } from './pocketbase-types'; -import { TypedPocketBase, neq, sort, fields } from '../src'; +import { Schema } from './Database.js'; +import { + TypedPocketBase, + fields, + expand, + eq, + asc, + sort +} from '../src/index.js'; -const db: TypedPocketBase = new PocketBase('http://localhost:8090'); +const db = new PocketBase('http://localhost:8090') as TypedPocketBase; +await db.admins.authWithPassword('test@test.com', 'secretpassword'); -const { items } = await db.collection('posts').getList(1, 10, { - fields: fields('content', 'created', 'id'), - sort: sort('-date'), - filter: neq('content', '') -}); +{ + const posts = await db.collection('posts').getFullList({ + fields: fields('id', 'title', 'slug', 'content'), + sort: asc('date'), + filter: eq('published', true) + }); + + console.log(posts); +} + +{ + const posts = await db.collection('posts').getFullList({ + sort: asc('date'), + filter: eq('owner.email', 'user@test.com'), + expand: expand({ + owner: true + }) + }); + + console.log(posts); +} + +{ + const post = await db + .collection('posts') + .getFirstListItem(eq('owner.email', 'user@test.com'), { + expand: expand({ + owner: true + }) + }); + + console.log(post); +} diff --git a/package.json b/package.json index 70b4928..52c8d48 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "typed-pocketbase", - "version": "0.0.2", + "version": "0.0.3-next.0", "description": "Add types to the PocketBase JavaScript SDK", "author": "David Plugge", "repository": { @@ -16,9 +16,13 @@ "dist", "src" ], - "main": "./dist/index.js", - "module": "./dist/index.mjs", - "types": "./dist/index.d.ts", + "type": "module", + "main": "./dist/client/index.js", + "module": "./dist/client/index.mjs", + "types": "./dist/client/index.d.ts", + "bin": { + "typed-pocketbase": "dist/codegen/index.js" + }, "scripts": { "build": "tsup", "lint": "tsc", @@ -29,11 +33,15 @@ "peerDependencies": { "pocketbase": "^0.15.2" }, + "dependencies": { + "sade": "^1.8.1" + }, "devDependencies": { + "@types/node": "^18.13.0", "@changesets/cli": "^2.26.1", "pocketbase": "^0.15.2", "typescript": "^5.1.3", - "tsup": "^7.0.0", + "tsup": "^7.1.0", "prettier": "^2.8.8" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 194e901..24de6e6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4,10 +4,18 @@ settings: autoInstallPeers: true excludeLinksFromLockfile: false +dependencies: + sade: + specifier: ^1.8.1 + version: 1.8.1 + devDependencies: '@changesets/cli': specifier: ^2.26.1 version: 2.26.1 + '@types/node': + specifier: ^18.13.0 + version: 18.13.0 pocketbase: specifier: ^0.15.2 version: 0.15.2 @@ -15,8 +23,8 @@ devDependencies: specifier: ^2.8.8 version: 2.8.8 tsup: - specifier: ^7.0.0 - version: 7.0.0(typescript@5.1.3) + specifier: ^7.1.0 + version: 7.1.0(typescript@5.1.3) typescript: specifier: ^5.1.3 version: 5.1.3 @@ -522,6 +530,10 @@ packages: resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} dev: true + /@types/node@18.13.0: + resolution: {integrity: sha512-gC3TazRzGoOnoKAhUx+Q0t8S9Tzs74z7m0ipwGpSqQrleP14hKxP4/JUeEQcD3W1/aIpnWl8pHowI7WokuZpXg==} + dev: true + /@types/normalize-package-data@2.4.1: resolution: {integrity: sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==} dev: true @@ -1619,6 +1631,11 @@ packages: engines: {node: '>= 8.0.0'} dev: true + /mri@1.2.0: + resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} + engines: {node: '>=4'} + dev: false + /ms@2.1.2: resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} dev: true @@ -1952,6 +1969,13 @@ packages: queue-microtask: 1.2.3 dev: true + /sade@1.8.1: + resolution: {integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==} + engines: {node: '>=6'} + dependencies: + mri: 1.2.0 + dev: false + /safe-regex-test@1.0.0: resolution: {integrity: sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==} dependencies: @@ -2216,8 +2240,8 @@ packages: resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} dev: true - /tsup@7.0.0(typescript@5.1.3): - resolution: {integrity: sha512-yYARDRkPq07mO3YUXTvF12ISwWQG57Odve8aFEgLdHyeGungxuKxb19yf9G0W8y59SZFkLnRj1gkoVk1gd5fbQ==} + /tsup@7.1.0(typescript@5.1.3): + resolution: {integrity: sha512-mazl/GRAk70j8S43/AbSYXGgvRP54oQeX8Un4iZxzATHt0roW0t6HYDVZIXMw0ZQIpvr1nFMniIVnN5186lW7w==} engines: {node: '>=16.14'} hasBin: true peerDependencies: diff --git a/src/codegen/ambient.d.ts b/src/codegen/ambient.d.ts new file mode 100644 index 0000000..6f7460c --- /dev/null +++ b/src/codegen/ambient.d.ts @@ -0,0 +1,2 @@ +declare const PKG_NAME: string; +declare const PKG_VERSION: string; diff --git a/src/codegen/index.ts b/src/codegen/index.ts new file mode 100644 index 0000000..4f9a92e --- /dev/null +++ b/src/codegen/index.ts @@ -0,0 +1,264 @@ +import { mkdir, writeFile } from 'node:fs/promises'; +import { dirname, resolve } from 'node:path'; +import PocketBase, { SchemaField } from 'pocketbase'; +import sade from 'sade'; + +type Column = { + name: string; + type: string; + required: boolean; +}; +type Relation = { + name: string; + target: unknown | unknown[]; +}; +type File = { + name: string; + thumbs: []; +}; +type CollectionType = 'auth' | 'view' | 'base'; + +interface Collection { + id: string; + name: string; + type: CollectionType; + + typeName: string; + + columns: Column[]; + relations: Relation[]; + files: File[]; +} + +interface CliOptions { + url: string; + email: string; + password: string; + out?: string; +} + +sade(PKG_NAME, true) + .version(PKG_VERSION) + .describe('Generate types for the PocketBase JavaScript SDK') + .option( + '-u, --url', + 'URL to your hosted pocketbase instance.', + 'http://localhost:8090' + ) + .option('-e, --email', 'email for an admin pocketbase user.') + .option('-p, --password', 'email for an admin pocketbase user.') + .option( + '-o, --out', + 'path to save the typescript output file (prints to console by default)' + ) + .action( + ({ + url, + email = process.env.POCKETBASE_EMAIL, + password = process.env.POCKETBASE_PASSWORD, + out + }: Partial) => { + if (!url) error(`required option '-u, --url' not specified`); + + if (!email) + error( + `required option '-e, --email' not specified and 'POCKETBASE_EMAIL' env not set` + ); + + if (!password) + error( + `required option '-p, --password' not specified and 'POCKETBASE_PASSWORD' env not set` + ); + + generateTypes({ + url, + email, + password, + out + }); + } + ) + .parse(process.argv); + +function error(msg: string): never { + console.error(msg); + process.exit(); +} + +async function generateTypes({ url, email, password, out }: CliOptions) { + const pb = new PocketBase(url); + await pb.admins.authWithPassword(email, password); + + const collections = await pb.collections.getFullList(); + + const deferred: Array<() => void> = []; + + const tables = collections.map((c) => { + const columns: Column[] = []; + const files: File[] = []; + const relations: Relation[] = []; + const typeName = pascalCase(c.name); + + c.schema.forEach((field) => { + columns.push({ + name: field.name, + required: field.required, + type: getFieldType(field) + }); + + if (field.type === 'file') { + files.push({ + name: field.name, + thumbs: + field.options.thumbs + ?.map((t: string) => `'${t}'`) + .join(' | ') ?? 'never' + }); + } + + if (field.type === 'relation') { + deferred.push(() => { + const target = tableMap.get(field.options.collectionId); + const targetCollection = collectionMap.get( + field.options.collectionId + ); + + if (!target || !targetCollection) + throw new Error( + `Collection ${field.options.collectionId} not found for relation ${c.name}.${field.name}` + ); + + relations.push({ + name: field.name, + target: `${target.typeName}Collection${ + field.options.maxSelect > 1 ? '[]' : '' + }` + }); + + /** + * indirect expand + * @see https://pocketbase.io/docs/expanding-relations/#indirect-expand + */ + + const reg = new RegExp( + `CREATE UNIQUE INDEX \`idx_rdMGJaq\` ON \`${c.name}\` \\(\`${field.name}\`\\)` + // `CREATE UNIQUE INDEX \`(\\w+)\` ON \`${c.name}\` (\`${field.name}\`)` + ); + + const isUnique = targetCollection.indexes.some((index) => + reg.test(index) + ); + + target.relations.push({ + name: `'${c.name}(${field.name})'`, + target: `${typeName}Collection${isUnique ? '' : '[]'}` + }); + }); + } + }); + + return { + id: c.id, + name: c.name, + type: c.type as CollectionType, + typeName, + columns, + files, + relations + }; + }); + + const tableMap = new Map(tables.map((t) => [t.id, t])); + const collectionMap = new Map(collections.map((c) => [c.id, c])); + + deferred.forEach((c) => c()); + + const definition = + ` +export type BaseSystemFields = { + id: string; + created: string; + updated: string; + collectionId: string; + collectionName: T; +}; + +export type AuthSystemFields = { + email: string; + emailVisibility: boolean; + username: string; + verified: boolean; +} & BaseSystemFields; + +${tables + .map(({ id, name, type, typeName, columns, files, relations }) => + ` +export type ${typeName}Record = { + ${columns + .map((col) => + `${col.name}${col.required ? '' : '?'}: ${col.type};`.trim() + ) + .join('\n' + ' '.repeat(4))} +} + +export type ${typeName}Response = ${typeName}Record & ${ + type === 'auth' ? 'AuthSystemFields' : 'BaseSystemFields' + }<'${name}'> + +export type ${typeName}Collection = { + collectionId: '${id}'; + collectionName: '${name}'; + record: ${typeName}Record; + response: ${typeName}Response; + files: { + ${files + .map((col) => `${col.name}: { thumbs: ${col.thumbs} };`.trim()) + .join('\n' + ' '.repeat(8))} + }; + relations: { + ${relations + .map((col) => `${col.name}: ${col.target};`.trim()) + .join('\n' + ' '.repeat(8))} + }; +}; +`.trim() + ) + .join('\n\n')} + +export type Schema = { + ${tables + .map(({ name, typeName }) => `${name}: ${typeName}Collection;`) + .join('\n ')} +}; + + + `.trim() + '\n'; + + if (out) { + const file = resolve(out); + await mkdir(dirname(file), { recursive: true }); + await writeFile(file, definition, 'utf-8'); + } else { + console.log(definition); + } +} + +function getFieldType(schema: SchemaField) { + switch (schema.type) { + case 'text': + case 'date': + case 'editor': // rich text + case 'relation': + case 'file': + return 'string'; + case 'bool': + return 'boolean'; + } + throw new Error(`Unknown type ${schema.type}`); +} + +function pascalCase(str: string) { + return str + .replace(/[-_]([a-z])/g, (m) => m[1].toUpperCase()) + .replace(/^\w/, (s) => s.toUpperCase()); +} diff --git a/src/expand.ts b/src/expand.ts new file mode 100644 index 0000000..a026b1a --- /dev/null +++ b/src/expand.ts @@ -0,0 +1,63 @@ +import { + TypedRecord, + GenericCollection, + GenericExpand, + ArrayInnerType, + Simplify +} from './types.js'; + +export type ExpandParam< + T extends GenericCollection, + E extends GenericExpand +> = { + __record__?: T; + __expand__?: E; +} & string; + +export type Expand = { + [K in keyof T['relations']]?: + | true + | Expand>; +}; + +type RelationToTypedRecord< + T extends GenericCollection | GenericCollection[], + E extends GenericExpand = {} +> = T extends GenericCollection[] + ? TypedRecord[] + : T extends GenericCollection + ? TypedRecord + : never; + +export type UnpackExpand< + T extends GenericCollection, + E extends Expand +> = Simplify<{ + [K in keyof E & keyof T['relations']]: RelationToTypedRecord< + T['relations'][K], + E[K] extends Expand + ? UnpackExpand, E[K]> + : {} + >; +}>; + +export function expand>( + expand: E +): ExpandParam> { + const expands: string[] = []; + + process(expand); + + function process(obj: any, prefix: string[] = []) { + for (const key in obj) { + const value = obj[key]; + if (value === true) { + expands.push([...prefix, key].join('.')); + } else if (typeof value === 'object') { + process(value, [...prefix, key]); + } + } + } + + return [...new Set(expands)].join(','); +} diff --git a/src/fields.ts b/src/fields.ts index 8f5d9e2..2dcc2da 100644 --- a/src/fields.ts +++ b/src/fields.ts @@ -1,4 +1,4 @@ -import { BaseRecord } from './types'; +import { BaseRecord } from './types.js'; export type FieldsParam = { __record__?: T; diff --git a/src/file-service.ts b/src/file-service.ts new file mode 100644 index 0000000..ed025cc --- /dev/null +++ b/src/file-service.ts @@ -0,0 +1,39 @@ +import Client, { BaseQueryParams, Record as PBRecord } from 'pocketbase'; +import { GenericSchema } from './types.js'; + +export interface TypedFileService { + readonly client: Client; + + /** + * Builds and returns an absolute record file url for the provided filename. + */ + getUrl< + Collection extends keyof Schema, + Field extends keyof Schema[Collection]['files'] + >( + record: { + id: string; + collectionName: Collection; + field: Field; + }, + filename: string, + queryParams?: TypedFileQueryParams< + Schema[Collection]['files'][Field]['thumbs'] + > + ): string; + getUrl( + record: Pick, + filename: string, + queryParams?: TypedFileQueryParams + ): string; + + /** + * Requests a new private file access token for the current auth model (admin or record). + */ + getToken(queryParams?: BaseQueryParams): Promise; +} + +export interface TypedFileQueryParams + extends BaseQueryParams { + thumb?: Thumb; +} diff --git a/src/filter.ts b/src/filter.ts index c962cee..b4e1cd3 100644 --- a/src/filter.ts +++ b/src/filter.ts @@ -1,6 +1,10 @@ -import { BaseRecord } from './types'; +import { BaseRecord } from './types.js'; -type ActualFilter = [K, FilterOperand, T[K]]; +type ActualFilter = [ + K, + FilterOperand, + T[K] +]; export type FilterOperand = | '=' @@ -36,19 +40,27 @@ function serializeFilter([key, op, val]: ActualFilter) { function serializeFilters(filters: Filter[]) { return filters .filter(Boolean) - .map((filter) => (Array.isArray(filter) ? serializeFilter(filter) : filter)); + .map((filter) => + Array.isArray(filter) ? serializeFilter(filter) : filter + ); } -export function and(...filters: Filter[]): FilterParam { +export function and( + ...filters: Filter[] +): FilterParam { const str = serializeFilters(filters).join(' && '); return `(${str})`; } -export function or(...filters: Filter[]): FilterParam { +export function or( + ...filters: Filter[] +): FilterParam { const str = filters .filter(Boolean) - .map((filter) => (Array.isArray(filter) ? serializeFilter(filter) : filter)) + .map((filter) => + Array.isArray(filter) ? serializeFilter(filter) : filter + ) .join(' || '); return `(${str})`; } diff --git a/src/index.ts b/src/index.ts index 3a5bad3..e1cbd78 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,6 +1,20 @@ -export { TypedPocketBase } from './pocketbase'; -export { TypedRecordService } from './record-service'; +import PocketBase from 'pocketbase'; +import { GenericSchema } from './types.js'; +import { TypedRecordService } from './record-service.js'; +import { TypedFileService } from './file-service.js'; -export { fields } from './fields'; -export { and, eq, gt, gte, like, lt, lte, neq, nlike, or } from './filter'; -export { asc, desc, sort } from './sort'; +export { fields } from './fields.js'; +export { and, or, eq, gt, gte, like, lt, lte, neq, nlike } from './filter.js'; +export { expand } from './expand.js'; +export { asc, desc, sort } from './sort.js'; +export { GenericSchema, GenericCollection } from './types.js'; + +// @ts-expect-error typescript... +export interface TypedPocketBase + extends PocketBase { + collection( + idOrName: C + ): TypedRecordService; + + files: TypedFileService; +} diff --git a/src/pocketbase.ts b/src/pocketbase.ts deleted file mode 100644 index c2f3f0a..0000000 --- a/src/pocketbase.ts +++ /dev/null @@ -1,11 +0,0 @@ -import type PocketBase from 'pocketbase'; -import { BaseCollectionRecords, SystemFields } from './types'; -import { TypedRecordService } from './record-service'; - -// @ts-expect-error -export interface TypedPocketBase - extends PocketBase { - collection( - name: C - ): TypedRecordService; -} diff --git a/src/record-service.ts b/src/record-service.ts index 7d0517e..fe976da 100644 --- a/src/record-service.ts +++ b/src/record-service.ts @@ -1,86 +1,159 @@ import { - BaseQueryParams, - FullListQueryParams, - ListQueryParams, ListResult, - RecordListQueryParams, - RecordQueryParams, RecordService, RecordSubscription, UnsubscribeFunc } from 'pocketbase'; -import { Filter, FilterParam } from './filter'; -import { BaseRecord, SystemFields, TypedRecord } from './types'; -import { FieldsParam } from './fields'; -import { SortParam } from './sort'; +import { + Simplify, + GenericCollection, + TypedRecord, + Fields, + Columns, + GenericExpand, + LooseAutocomplete, + RecordWithExpandToDotPath, + BaseRecord +} from './types.js'; +import { FieldsParam } from './fields.js'; +import { Filter, FilterParam } from './filter.js'; +import { SortParam } from './sort.js'; +import { ExpandParam } from './expand.js'; // @ts-expect-error -export interface TypedRecordService extends RecordService { - getFullList( + getFullList< + Select extends Fields = Fields, + Expand extends GenericExpand = {} + >( batch?: number, - queryParams?: TypedRecordListQueryParams | undefined - ): Promise[]>; + queryParams?: TypedRecordListQueryParams + ): Promise< + TypedRecord, Select>>, Expand>[] + >; - getList( - filter: Filter, - queryParams?: TypedRecordListQueryParams | undefined - ): Promise>; + getFirstListItem< + Select extends Fields = Fields, + Expand extends GenericExpand = {} + >( + filter: Filter>, + queryParams?: TypedRecordListQueryParams + ): Promise< + TypedRecord, Select>>, Expand> + >; - getOne( - bodyParams: Omit, - queryParams?: TypedRecordQueryParams - ): Promise>; + create< + Select extends Fields = Fields, + Expand extends GenericExpand = {} + >( + bodyParams: Collection['record'], + queryParams?: TypedRecordQueryParams + ): Promise< + TypedRecord, Select>>, Expand> + >; - update