diff --git a/.editorconfig b/.editorconfig index a5420933..542ba55a 100644 --- a/.editorconfig +++ b/.editorconfig @@ -10,7 +10,7 @@ end_of_line = lf # editorconfig-tools is unable to ignore longs strings or urls max_line_length = off -[*.js] +[*.{js,ts}] quote_type = single [CHANGELOG.md] diff --git a/index.d.ts b/index.d.ts index e8ddb700..9b3dbee0 100644 --- a/index.d.ts +++ b/index.d.ts @@ -1,13 +1,13 @@ type Identifier = `${string}/${string}:${string}`; -type WebhookEventType = "start" | "output" | "logs" | "completed"; +type WebhookEventType = 'start' | 'output' | 'logs' | 'completed'; -declare module "replicate" { - export interface ReplicateOptions { - auth: string; - userAgent?: string; - baseUrl?: string; - } +interface Page { + previous?: string; + next?: string; + results: T[]; +} +declare module 'replicate' { export interface Collection { id: string; name: string; @@ -43,39 +43,17 @@ declare module "replicate" { updated: string; } - export interface CollectionsGetOptions { - collection_slug: string; - } - - export interface ModelsGetOptions { - model_owner: string; - model_name: string; - } - - export interface ModelsVersionsListOptions { - model_owner: string; - model_name: string; - } + export default class Replicate { + constructor(options: { + auth: string; + userAgent?: string; + baseUrl?: string; + }); - export interface ModelsVersionsGetOptions { - model_owner: string; - model_name: string; - id: string; - } - - export interface PredictionsCreateOptions { - version: string; - input: any; - webhook?: string; - webhook_events_filter?: WebhookEventType[]; - } - - export interface PredictionsGetOptions { - predictionId: string; - } - - export class Replicate { - constructor(options: ReplicateOptions); + auth: string; + userAgent?: string; + baseUrl?: string; + private instance: any; run( identifier: Identifier, @@ -87,7 +65,7 @@ declare module "replicate" { } ): Promise; request(route: string, parameters: any): Promise; - paginate(endpoint: () => Promise): AsyncGenerator; + paginate(endpoint: () => Promise>): AsyncGenerator<[T]>; wait( prediction: Prediction, options: { @@ -97,23 +75,30 @@ declare module "replicate" { ): Promise; collections: { - get(options: CollectionsGetOptions): Promise; + get(collection_slug: string): Promise; }; models: { - get(options: ModelsGetOptions): Promise; + get(model_owner: string, model_name: string): Promise; versions: { - list(options: ModelsVersionsListOptions): Promise; - get(options: ModelsVersionsGetOptions): Promise; + list(model_owner: string, model_name: string): Promise; + get( + model_owner: string, + model_name: string, + version_id: string + ): Promise; }; }; predictions: { - create(options: PredictionsCreateOptions): Promise; - get(options: PredictionsGetOptions): Promise; - list(): Promise; + create(options: { + version: string; + input: any; + webhook?: string; + webhook_events_filter?: WebhookEventType[]; + }): Promise; + get(prediction_id: string): Promise; + list(): Promise>; }; } - - export default Replicate; } diff --git a/index.test.js b/index.test.js deleted file mode 100644 index b2699199..00000000 --- a/index.test.js +++ /dev/null @@ -1,153 +0,0 @@ -const Replicate = require('./index'); -require('jest'); - -describe('Replicate client', () => { - let client; - beforeEach(() => { - client = new Replicate({ baseUrl: 'https://api.replicate.com/v1' }); - }); - - test('Constructor sets default baseUrl', () => { - const clientWithoutBaseUrl = new Replicate({}); - expect(clientWithoutBaseUrl.baseUrl).toBe('https://api.replicate.com/v1'); - }); - - describe('collections.get', () => { - test('Calls the correct API route', async () => { - client.request = jest.fn(); - await client.collections.get('text-to-image'); - expect(client.request).toHaveBeenCalledWith( - '/collections/text-to-image', - { - method: 'GET', - } - ); - }); - - // Add more tests for error handling, edge cases, etc. - }); - - describe('models.get', () => { - test('Calls the correct API route', async () => { - client.request = jest.fn(); - await client.models.get('owner', 'name'); - expect(client.request).toHaveBeenCalledWith('/models/owner/name', { - method: 'GET', - }); - }); - - // Add more tests for error handling, edge cases, etc. - }); - - describe('predictions.create', () => { - test('Calls the correct API route with the correct payload', async () => { - client.request = jest.fn(); - const input = { text: 'Hello, world!' }; - await client.predictions.create({ - version: - '632231d0d49d34d5c4633bd838aee3d81d936e59a886fbf28524702003b4c532', - input, - }); - expect(client.request).toHaveBeenCalledWith('/predictions', { - method: 'POST', - data: { - version: - '632231d0d49d34d5c4633bd838aee3d81d936e59a886fbf28524702003b4c532', - input: { - text: 'Hello, world!', - }, - }, - }); - }); - - test('Calls the correct API route with the correct payload, webhook URL and webhook filters', async () => { - client.request = jest.fn(); - const input = { text: 'Hello, world!' }; - await client.predictions.create({ - version: - '632231d0d49d34d5c4633bd838aee3d81d936e59a886fbf28524702003b4c532', - input, - webhook: 'http://test.host/webhook', - webhook_events_filter: ['output', 'completed'], - }); - expect(client.request).toHaveBeenCalledWith('/predictions', { - method: 'POST', - data: { - version: - '632231d0d49d34d5c4633bd838aee3d81d936e59a886fbf28524702003b4c532', - input: { - text: 'Hello, world!', - }, - webhook: 'http://test.host/webhook', - webhook_events_filter: ['output', 'completed'], - }, - }); - }); - - // Add more tests for error handling, edge cases, etc. - }); - - describe('predictions.get', () => { - test('Calls the correct API route with the correct payload', async () => { - client.request = jest.fn(); - await client.predictions.get(123); - expect(client.request).toHaveBeenCalledWith('/predictions/123', { - method: 'GET', - }); - }); - - // Add more tests for error handling, edge cases, etc. - }); - - describe('predictions.list', () => { - test('Calls the correct API route with the correct payload', async () => { - client.request = jest.fn(); - await client.predictions.list(); - expect(client.request).toHaveBeenCalledWith('/predictions', { - method: 'GET', - }); - }); - - test('Paginates results', async () => { - client.request = jest.fn(); - client.request.mockResolvedValueOnce({ - results: [{ id: 1 }, { id: 2 }], - next: 'https://api.replicate.com/v1/predictions?cursor=cD0yMDIyLTAxLTIxKzIzJTNBMTglM0EyNC41MzAzNTclMkIwMCUzQTAw', - }); - client.request.mockResolvedValueOnce({ - results: [{ id: 3 }], - next: null, - }); - - const results = []; - // eslint-disable-next-line no-restricted-syntax - for await (const page of client.paginate(client.predictions.list)) { - results.push(...page); - } - expect(client.request).toHaveBeenCalledTimes(2); - expect(results).toEqual([{ id: 1 }, { id: 2 }, { id: 3 }]); - }); - - // Add more tests for error handling, edge cases, etc. - }); - - describe('run', () => { - test('Calls the correct API routes', async () => { - client.request = jest.fn(); - client.request.mockResolvedValueOnce({ - status: 'processing', - id: 'prediction-id', - }); - client.request.mockResolvedValueOnce({ - status: 'succeeded', - output: 'foobar', - }); - const output = await client.run('owner/model:abc123', { - input: { text: 'Hello, world!' }, - }); - expect(output).toBe('foobar'); - }); - }); - - // Continue with tests for other methods -}); diff --git a/index.test.ts b/index.test.ts new file mode 100644 index 00000000..70e644f8 --- /dev/null +++ b/index.test.ts @@ -0,0 +1,307 @@ +import { expect, jest, test } from '@jest/globals'; + +import Replicate, { Prediction } from 'replicate'; + +import axios from 'axios'; + +describe('Replicate client', () => { + let client: Replicate; + + beforeEach(() => { + client = new Replicate({ auth: 'test-token' }); + client['instance'] = jest.fn(); + }); + + describe('constructor', () => { + test('Sets default baseUrl', () => { + expect(client.baseUrl).toBe('https://api.replicate.com/v1'); + }); + + test('Sets custom baseUrl', () => { + const clientWithCustomBaseUrl = new Replicate({ + baseUrl: 'https://example.com/', + auth: 'test-token', + }); + expect(clientWithCustomBaseUrl.baseUrl).toBe('https://example.com/'); + }); + + test('Sets custom userAgent', () => { + const clientWithCustomUserAgent = new Replicate({ + userAgent: 'my-app/1.2.3', + auth: 'test-token', + }); + expect(clientWithCustomUserAgent.userAgent).toBe('my-app/1.2.3'); + }); + }); + + describe('collections.get', () => { + test('Calls the correct API route', async () => { + client['instance'].mockResolvedValueOnce({ + data: { + name: 'Super resolution', + slug: 'super-resolution', + description: + 'Upscaling models that create high-quality images from low-quality images.', + models: [], + }, + }); + const collection = await client.collections.get('super-resolution'); + expect(client['instance']).toHaveBeenCalledWith( + '/collections/super-resolution', + { + method: 'GET', + } + ); + expect(collection.name).toBe('Super resolution'); + }); + + // Add more tests for error handling, edge cases, etc. + }); + + describe('models.get', () => { + test('Calls the correct API route', async () => { + client['instance'].mockResolvedValueOnce({ + data: { + url: 'https://replicate.com/replicate/hello-world', + owner: 'replicate', + name: 'hello-world', + description: 'A tiny model that says hello', + visibility: 'public', + github_url: 'https://github.com/replicate/cog-examples', + paper_url: null, + license_url: null, + run_count: 12345, + cover_image_url: '', + default_example: {}, + latest_version: {}, + }, + }); + await client.models.get('replicate', 'hello-world'); + expect(client['instance']).toHaveBeenCalledWith( + '/models/replicate/hello-world', + { + method: 'GET', + } + ); + }); + + // Add more tests for error handling, edge cases, etc. + }); + + describe('predictions.create', () => { + test('Calls the correct API route with the correct payload', async () => { + client['instance'].mockResolvedValueOnce({ + data: { + id: 'ufawqhfynnddngldkgtslldrkq', + version: + '5c7d5dc6dd8bf75c1acaa8565735e7986bc5b66206b55cca93cb72c9bf15ccaa', + urls: { + get: 'https://api.replicate.com/v1/predictions/ufawqhfynnddngldkgtslldrkq', + cancel: + 'https://api.replicate.com/v1/predictions/ufawqhfynnddngldkgtslldrkq/cancel', + }, + created_at: '2022-04-26T22:13:06.224088Z', + started_at: null, + completed_at: null, + status: 'starting', + input: { + text: 'Alice', + }, + output: null, + error: null, + logs: null, + metrics: {}, + }, + }); + + const prediction = await client.predictions.create({ + version: + '5c7d5dc6dd8bf75c1acaa8565735e7986bc5b66206b55cca93cb72c9bf15ccaa', + input: { + text: 'Alice', + }, + webhook: 'http://test.host/webhook', + webhook_events_filter: ['output', 'completed'], + }); + expect(prediction.id).toBe('ufawqhfynnddngldkgtslldrkq'); + + expect(client['instance']).toHaveBeenCalledWith('/predictions', { + method: 'POST', + data: { + version: + '5c7d5dc6dd8bf75c1acaa8565735e7986bc5b66206b55cca93cb72c9bf15ccaa', + input: { + text: 'Alice', + }, + webhook: 'http://test.host/webhook', + webhook_events_filter: ['output', 'completed'], + }, + }); + }); + + // Add more tests for error handling, edge cases, etc. + }); + + describe('predictions.get', () => { + test('Calls the correct API route with the correct payload', async () => { + client['instance'].mockResolvedValueOnce({ + data: { + id: 'rrr4z55ocneqzikepnug6xezpe', + version: + 'be04660a5b93ef2aff61e3668dedb4cbeb14941e62a3fd5998364a32d613e35e', + urls: { + get: 'https://api.replicate.com/v1/predictions/rrr4z55ocneqzikepnug6xezpe', + cancel: + 'https://api.replicate.com/v1/predictions/rrr4z55ocneqzikepnug6xezpe/cancel', + }, + created_at: '2022-09-13T22:54:18.578761Z', + started_at: '2022-09-13T22:54:19.438525Z', + completed_at: '2022-09-13T22:54:23.236610Z', + source: 'api', + status: 'succeeded', + input: { + prompt: 'oak tree with boletus growing on its branches', + }, + output: [ + 'https://replicate.com/api/models/stability-ai/stable-diffusion/files/9c3b6fe4-2d37-4571-a17a-83951b1cb120/out-0.png', + ], + error: null, + logs: 'Using seed: 36941...', + metrics: { + predict_time: 4.484541, + }, + }, + }); + + const prediction = await client.predictions.get( + 'rrr4z55ocneqzikepnug6xezpe' + ); + expect(prediction.id).toBe('rrr4z55ocneqzikepnug6xezpe'); + + expect(client['instance']).toHaveBeenCalledWith( + '/predictions/rrr4z55ocneqzikepnug6xezpe', + { + method: 'GET', + } + ); + }); + + // Add more tests for error handling, edge cases, etc. + }); + + describe('predictions.list', () => { + test('Calls the correct API route with the correct payload', async () => { + client['instance'].mockResolvedValueOnce({ + data: { + next: 'https://api.replicate.com/v1/predictions?cursor=cD0yMDIyLTAxLTIxKzIzJTNBMTglM0EyNC41MzAzNTclMkIwMCUzQTAw', + previous: null, + results: [ + { + id: 'jpzd7hm5gfcapbfyt4mqytarku', + version: + 'b21cbe271e65c1718f2999b038c18b45e21e4fba961181fbfae9342fc53b9e05', + urls: { + get: 'https://api.replicate.com/v1/predictions/jpzd7hm5gfcapbfyt4mqytarku', + cancel: + 'https://api.replicate.com/v1/predictions/jpzd7hm5gfcapbfyt4mqytarku/cancel', + }, + created_at: '2022-04-26T20:00:40.658234Z', + started_at: '2022-04-26T20:00:84.583803Z', + completed_at: '2022-04-26T20:02:27.648305Z', + source: 'web', + status: 'succeeded', + }, + ], + }, + }); + + const predictions = await client.predictions.list(); + expect(predictions.results.length).toBe(1); + expect(predictions.results[0].id).toBe('jpzd7hm5gfcapbfyt4mqytarku'); + + expect(client['instance']).toHaveBeenCalledWith('/predictions', { + method: 'GET', + }); + }); + + test('Paginates results', async () => { + client['instance'].mockResolvedValueOnce({ + data: { + results: [{ id: 'ufawqhfynnddngldkgtslldrkq' }], + next: 'https://api.replicate.com/v1/predictions?cursor=cD0yMDIyLTAxLTIxKzIzJTNBMTglM0EyNC41MzAzNTclMkIwMCUzQTAw', + }, + }); + client['instance'].mockResolvedValueOnce({ + data: { + results: [{ id: 'rrr4z55ocneqzikepnug6xezpe' }], + next: null, + }, + }); + + const results: Prediction[] = []; + // eslint-disable-next-line no-restricted-syntax + for await (const batch of client.paginate(client.predictions.list)) { + results.push(...batch); + } + expect(results).toEqual([ + { id: 'ufawqhfynnddngldkgtslldrkq' }, + { id: 'rrr4z55ocneqzikepnug6xezpe' }, + ]); + + expect(client['instance']).toHaveBeenCalledWith('/predictions', { + method: 'GET', + }); + expect(client['instance']).toHaveBeenCalledWith( + 'https://api.replicate.com/v1/predictions?cursor=cD0yMDIyLTAxLTIxKzIzJTNBMTglM0EyNC41MzAzNTclMkIwMCUzQTAw', + { + method: 'GET', + } + ); + }); + + // Add more tests for error handling, edge cases, etc. + }); + + describe('run', () => { + test('Calls the correct API routes', async () => { + client['instance'].mockResolvedValueOnce({ + data: { + id: 'ufawqhfynnddngldkgtslldrkq', + status: 'processing', + }, + }); + client['instance'].mockResolvedValueOnce({ + data: { + id: 'ufawqhfynnddngldkgtslldrkq', + status: 'succeeded', + output: 'foobar', + }, + }); + const output = await client.run( + 'owner/model:5c7d5dc6dd8bf75c1acaa8565735e7986bc5b66206b55cca93cb72c9bf15ccaa', + { + input: { text: 'Hello, world!' }, + } + ); + expect(client['instance']).toHaveBeenCalledWith('/predictions', { + method: 'POST', + data: { + version: + '5c7d5dc6dd8bf75c1acaa8565735e7986bc5b66206b55cca93cb72c9bf15ccaa', + input: { + text: 'Hello, world!', + }, + }, + }); + expect(client['instance']).toHaveBeenCalledWith( + '/predictions/ufawqhfynnddngldkgtslldrkq', + { + method: 'GET', + } + ); + expect(output).toBe('foobar'); + }); + }); + + // Continue with tests for other methods +}); diff --git a/jest.config.js b/jest.config.js new file mode 100644 index 00000000..9ea9e367 --- /dev/null +++ b/jest.config.js @@ -0,0 +1,6 @@ +// eslint-disable-next-line jsdoc/valid-types +/** @type {import('ts-jest').JestConfigWithTsJest} */ +module.exports = { + preset: 'ts-jest', + testEnvironment: 'node', +}; diff --git a/lib/predictions.js b/lib/predictions.js index 5d531f52..b688feeb 100644 --- a/lib/predictions.js +++ b/lib/predictions.js @@ -42,7 +42,7 @@ async function getPrediction(prediction_id) { /** * List all predictions * - * @returns {Promise} - Resolves with the list of predictions + * @returns {Promise} - Resolves with a page of predictions */ async function listPredictions() { return this.request('/predictions', { diff --git a/package-lock.json b/package-lock.json index 78cc347b..3d448519 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,8 @@ "axios": "^1.3.4" }, "devDependencies": { + "@tsconfig/recommended": "^1.0.2", + "@types/jest": "^29.5.0", "@typescript-eslint/eslint-plugin": "^5.56.0", "eslint": "^8.36.0", "eslint-config-airbnb-base": "^15.0.0", @@ -21,6 +23,7 @@ "eslint-plugin-n": "^15.6.1", "eslint-plugin-promise": "^6.1.1", "jest": "^29.5.0", + "ts-jest": "^29.1.0", "typescript": "^5.0.2" } }, @@ -1232,6 +1235,12 @@ "@sinonjs/commons": "^2.0.0" } }, + "node_modules/@tsconfig/recommended": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@tsconfig/recommended/-/recommended-1.0.2.tgz", + "integrity": "sha512-dbHBtbWBOjq0/otpopAE02NT2Cm05Qe2JsEKeCf/wjSYbI2hz8nCqnpnOJWHATgjDz4fd3dchs3Wy1gQGjfN6w==", + "dev": true + }, "node_modules/@types/babel__core": { "version": "7.20.0", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.0.tgz", @@ -1306,6 +1315,16 @@ "@types/istanbul-lib-report": "*" } }, + "node_modules/@types/jest": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.0.tgz", + "integrity": "sha512-3Emr5VOl/aoBwnWcH/EFQvlSAmjV+XtV9GGu5mwdYew5vhQh0IUZx/60x0TzHDu09Bi7HMx10t/namdJw5QIcg==", + "dev": true, + "dependencies": { + "expect": "^29.0.0", + "pretty-format": "^29.0.0" + } + }, "node_modules/@types/json-schema": { "version": "7.0.11", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", @@ -1904,6 +1923,18 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/bs-logger": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", + "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", + "dev": true, + "dependencies": { + "fast-json-stable-stringify": "2.x" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/bser": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", @@ -4504,6 +4535,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", + "dev": true + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -4543,6 +4580,12 @@ "semver": "bin/semver.js" } }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, "node_modules/makeerror": { "version": "1.0.12", "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", @@ -5592,6 +5635,49 @@ "node": ">=8.0" } }, + "node_modules/ts-jest": { + "version": "29.1.0", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.1.0.tgz", + "integrity": "sha512-ZhNr7Z4PcYa+JjMl62ir+zPiNJfXJN6E8hSLnaUKhOgqcn8vb3e537cpkd0FuAfRK3sR1LSqM1MOhliXNgOFPA==", + "dev": true, + "dependencies": { + "bs-logger": "0.x", + "fast-json-stable-stringify": "2.x", + "jest-util": "^29.0.0", + "json5": "^2.2.3", + "lodash.memoize": "4.x", + "make-error": "1.x", + "semver": "7.x", + "yargs-parser": "^21.0.1" + }, + "bin": { + "ts-jest": "cli.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.0 <8", + "@jest/types": "^29.0.0", + "babel-jest": "^29.0.0", + "jest": "^29.0.0", + "typescript": ">=4.3 <6" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "@jest/types": { + "optional": true + }, + "babel-jest": { + "optional": true + }, + "esbuild": { + "optional": true + } + } + }, "node_modules/tsconfig-paths": { "version": "3.14.2", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz", @@ -6866,6 +6952,12 @@ "@sinonjs/commons": "^2.0.0" } }, + "@tsconfig/recommended": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@tsconfig/recommended/-/recommended-1.0.2.tgz", + "integrity": "sha512-dbHBtbWBOjq0/otpopAE02NT2Cm05Qe2JsEKeCf/wjSYbI2hz8nCqnpnOJWHATgjDz4fd3dchs3Wy1gQGjfN6w==", + "dev": true + }, "@types/babel__core": { "version": "7.20.0", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.0.tgz", @@ -6940,6 +7032,16 @@ "@types/istanbul-lib-report": "*" } }, + "@types/jest": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.0.tgz", + "integrity": "sha512-3Emr5VOl/aoBwnWcH/EFQvlSAmjV+XtV9GGu5mwdYew5vhQh0IUZx/60x0TzHDu09Bi7HMx10t/namdJw5QIcg==", + "dev": true, + "requires": { + "expect": "^29.0.0", + "pretty-format": "^29.0.0" + } + }, "@types/json-schema": { "version": "7.0.11", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", @@ -7345,6 +7447,15 @@ "update-browserslist-db": "^1.0.10" } }, + "bs-logger": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", + "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", + "dev": true, + "requires": { + "fast-json-stable-stringify": "2.x" + } + }, "bser": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", @@ -9248,6 +9359,12 @@ "p-locate": "^5.0.0" } }, + "lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", + "dev": true + }, "lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -9280,6 +9397,12 @@ } } }, + "make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, "makeerror": { "version": "1.0.12", "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", @@ -10036,6 +10159,22 @@ "is-number": "^7.0.0" } }, + "ts-jest": { + "version": "29.1.0", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.1.0.tgz", + "integrity": "sha512-ZhNr7Z4PcYa+JjMl62ir+zPiNJfXJN6E8hSLnaUKhOgqcn8vb3e537cpkd0FuAfRK3sR1LSqM1MOhliXNgOFPA==", + "dev": true, + "requires": { + "bs-logger": "0.x", + "fast-json-stable-stringify": "2.x", + "jest-util": "^29.0.0", + "json5": "^2.2.3", + "lodash.memoize": "4.x", + "make-error": "1.x", + "semver": "7.x", + "yargs-parser": "^21.0.1" + } + }, "tsconfig-paths": { "version": "3.14.2", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz", diff --git a/package.json b/package.json index 057e4022..991b49a2 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,8 @@ "axios": "^1.3.4" }, "devDependencies": { + "@tsconfig/recommended": "^1.0.2", + "@types/jest": "^29.5.0", "@typescript-eslint/eslint-plugin": "^5.56.0", "eslint": "^8.36.0", "eslint-config-airbnb-base": "^15.0.0", @@ -24,6 +26,7 @@ "eslint-plugin-n": "^15.6.1", "eslint-plugin-promise": "^6.1.1", "jest": "^29.5.0", + "ts-jest": "^29.1.0", "typescript": "^5.0.2" } } diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 00000000..97cb33b3 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "@tsconfig/recommended/tsconfig.json" +}