diff --git a/README.md b/README.md index bd7e43d..6ec5694 100644 --- a/README.md +++ b/README.md @@ -20,20 +20,18 @@ npm install replicate ## Usage -Create the client: +Set your `REPLICATE_API_TOKEN` in your environment: -```js -import Replicate from "replicate"; - -const replicate = new Replicate({ - // get your token from https://replicate.com/account - auth: "my api token", // defaults to process.env.REPLICATE_API_TOKEN -}); +```sh +# get your token from https://replicate.com/account +export REPLICATE_API_TOKEN="r8_123..." ``` Run a model and await the result: ```js +import replicate from "replicate"; + const model = "stability-ai/stable-diffusion:27b93a2413e7f36cd83da926f3656280b2931564ff050bf9575f1fdf9bcd7478"; const input = { prompt: "a 19th century portrait of a raccoon gentleman wearing a suit", @@ -94,8 +92,12 @@ const output = await replicate.run(model, { input }); ### Constructor +You can create a custom instance of the Replicate client using the `replicate.Replicate` constructor: + ```js -const replicate = new Replicate(options); +import replicate from "replicate"; + +const replicate = new replicate.Replicate(options); ``` | name | type | description | @@ -121,10 +123,10 @@ you can install a fetch function from an external package like and pass it to the `fetch` option in the constructor. ```js -import Replicate from "replicate"; +import replicate from "replicate"; import fetch from "cross-fetch"; -const replicate = new Replicate({ fetch }); +const replicate = new replicate.Replicate({ fetch }); ``` You can also use the `fetch` option to add custom behavior to client requests, @@ -778,4 +780,50 @@ You can call this method directly to make other requests to the API. ## TypeScript -The `Replicate` constructor and all `replicate.*` methods are fully typed. +The `Replicate` constructor and all `replicate.*` methods are fully typed. Types are accessible +via the named exports: + +```ts +import type { Model, Prediction } from "replicate"; +``` + +## Deprecated Constructor + +Earlier versions of the Replicate library exported the `Replicate` constructor as the default +export. This will be removed in a future version, to migrate please update your code to use +the following pattern: + +If you don't need to customize your Replicate client you can just remove the constructor code +entirely: + +```js +// Deprecated +import Replicate from "replicate"; + +const replicate = new Replicate(); + +replicate.run(...); + +// Fixed +import replicate from "replicate"; + +replicate.run(...); +``` + +If you need the Replicate construtor it's available on the `replicate` object. + +```js +// Deprecated +import Replicate from "replicate"; + +const replicate = new Replicate({auth: "my-token"}); + +replicate.run(...); + +// Fixed +import replicate from "replicate"; + +replicate = new replicate.Replicate({auth: "my-token"}); + +replicate.run(...); +``` diff --git a/index.cjs b/index.cjs new file mode 100644 index 0000000..ee42af4 --- /dev/null +++ b/index.cjs @@ -0,0 +1 @@ +module.exports = require('./dist/commonjs').default; diff --git a/index.d.ts b/index.d.ts deleted file mode 100644 index 5620f3b..0000000 --- a/index.d.ts +++ /dev/null @@ -1,226 +0,0 @@ -declare module "replicate" { - type Status = "starting" | "processing" | "succeeded" | "failed" | "canceled"; - type Visibility = "public" | "private"; - type WebhookEventType = "start" | "output" | "logs" | "completed"; - - export interface ApiError extends Error { - request: Request; - response: Response; - } - - export interface Collection { - name: string; - slug: string; - description: string; - models?: Model[]; - } - - export interface Hardware { - sku: string; - name: string; - } - - export interface Model { - url: string; - owner: string; - name: string; - description?: string; - visibility: "public" | "private"; - github_url?: string; - paper_url?: string; - license_url?: string; - run_count: number; - cover_image_url?: string; - default_example?: Prediction; - latest_version?: ModelVersion; - } - - export interface ModelVersion { - id: string; - created_at: string; - cog_version: string; - openapi_schema: object; - } - - export interface Prediction { - id: string; - status: Status; - model: string; - version: string; - input: object; - output?: any; - source: "api" | "web"; - error?: any; - logs?: string; - metrics?: { - predict_time?: number; - }; - webhook?: string; - webhook_events_filter?: WebhookEventType[]; - created_at: string; - started_at?: string; - completed_at?: string; - urls: { - get: string; - cancel: string; - stream?: string; - }; - } - - export type Training = Prediction; - - export interface Page { - previous?: string; - next?: string; - results: T[]; - } - - export interface ServerSentEvent { - event: string; - data: string; - id?: string; - retry?: number; - } - - export default class Replicate { - constructor(options?: { - auth?: string; - userAgent?: string; - baseUrl?: string; - fetch?: ( - input: Request | string, - init?: RequestInit - ) => Promise; - }); - - auth: string; - userAgent?: string; - baseUrl?: string; - fetch: (input: Request | string, init?: RequestInit) => Promise; - - run( - identifier: `${string}/${string}` | `${string}/${string}:${string}`, - options: { - input: object; - wait?: { interval?: number }; - webhook?: string; - webhook_events_filter?: WebhookEventType[]; - signal?: AbortSignal; - }, - progress?: (prediction: Prediction) => void - ): Promise; - - stream( - identifier: `${string}/${string}` | `${string}/${string}:${string}`, - options: { - input: object; - webhook?: string; - webhook_events_filter?: WebhookEventType[]; - signal?: AbortSignal; - } - ): AsyncGenerator; - - request( - route: string | URL, - options: { - method?: string; - headers?: object | Headers; - params?: object; - data?: object; - } - ): Promise; - - paginate(endpoint: () => Promise>): AsyncGenerator<[T]>; - - wait( - prediction: Prediction, - options?: { - interval?: number; - }, - stop?: (prediction: Prediction) => Promise - ): Promise; - - collections: { - list(): Promise>; - get(collection_slug: string): Promise; - }; - - deployments: { - predictions: { - create( - deployment_owner: string, - deployment_name: string, - options: { - input: object; - stream?: boolean; - webhook?: string; - webhook_events_filter?: WebhookEventType[]; - } - ): Promise; - }; - }; - - hardware: { - list(): Promise; - }; - - models: { - get(model_owner: string, model_name: string): Promise; - list(): Promise>; - create( - model_owner: string, - model_name: string, - options: { - visibility: Visibility; - hardware: string; - description?: string; - github_url?: string; - paper_url?: string; - license_url?: string; - cover_image_url?: string; - } - ): Promise; - versions: { - list(model_owner: string, model_name: string): Promise; - get( - model_owner: string, - model_name: string, - version_id: string - ): Promise; - }; - }; - - predictions: { - create( - options: { - model?: string; - version?: string; - input: object; - stream?: boolean; - webhook?: string; - webhook_events_filter?: WebhookEventType[]; - } & ({ version: string } | { model: string }) - ): Promise; - get(prediction_id: string): Promise; - cancel(prediction_id: string): Promise; - list(): Promise>; - }; - - trainings: { - create( - model_owner: string, - model_name: string, - version_id: string, - options: { - destination: `${string}/${string}`; - input: object; - webhook?: string; - webhook_events_filter?: WebhookEventType[]; - } - ): Promise; - get(training_id: string): Promise; - cancel(training_id: string): Promise; - list(): Promise>; - }; - } -} diff --git a/index.js b/index.js index ce407f9..45b3c6a 100644 --- a/index.js +++ b/index.js @@ -1,361 +1,99 @@ -const ApiError = require("./lib/error"); -const ModelVersionIdentifier = require("./lib/identifier"); -const { Stream } = require("./lib/stream"); -const { withAutomaticRetries } = require("./lib/util"); +import ReplicateClass from "./lib/replicate.js"; +import ApiError from "./lib/error.js"; -const collections = require("./lib/collections"); -const deployments = require("./lib/deployments"); -const hardware = require("./lib/hardware"); -const models = require("./lib/models"); -const predictions = require("./lib/predictions"); -const trainings = require("./lib/trainings"); +export { default as Replicate } from "./lib/replicate.js"; +export { default as ApiError } from "./lib/error.js"; -const packageJSON = require("./package.json"); +/** + * Placeholder class used to warn of deprecated constructor. + * @deprecated use exported Replicate class instead + */ +class DeprecatedReplicate extends ReplicateClass { + /** + * @deprecated Use `import { Replicate } from "replicate";` instead + * @param {ConstructorParameters[0]=} options + */ + // biome-ignore lint/complexity/noUselessConstructor: exists for the tsdoc comment + constructor(options) { + super(options); + } +} + +const named = { ApiError, Replicate: ReplicateClass }; +const singleton = new ReplicateClass(); /** - * Replicate API client library + * Default instance of the Replicate class that gets the access token + * from the REPLICATE_API_TOKEN environment variable. + * + * Create a new Replicate API client instance. * - * @see https://replicate.com/docs/reference/http * @example - * // Create a new Replicate API client instance - * const Replicate = require("replicate"); - * const replicate = new Replicate({ - * // get your token from https://replicate.com/account - * auth: process.env.REPLICATE_API_TOKEN, - * userAgent: "my-app/1.2.3" - * }); + * + * import replicate from "replicate"; * * // Run a model and await the result: * const model = 'owner/model:version-id' * const input = {text: 'Hello, world!'} * const output = await replicate.run(model, { input }); + * + * @remarks + * + * NOTE: Use of this object as a constructor is deprecated and will + * be removed in a future version. Import the Replicate constructor + * instead: + * + * ``` + * const Replicate = require("replicate").Replicate; + * ``` + * + * Or in commonjs: + * + * ``` + * import { Replicate } from "replicate"; + * const client = new Replicate({...}); + * ``` + * + * @type { Replicate & typeof DeprecatedReplicate & {Replicate: typeof ReplicateClass} } */ -class Replicate { - /** - * Create a new Replicate API client instance. - * - * @param {object} options - Configuration options for the client - * @param {string} options.auth - API access token. Defaults to the `REPLICATE_API_TOKEN` environment variable. - * @param {string} options.userAgent - Identifier of your app - * @param {string} [options.baseUrl] - Defaults to https://api.replicate.com/v1 - * @param {Function} [options.fetch] - Fetch function to use. Defaults to `globalThis.fetch` - */ - constructor(options = {}) { - this.auth = options.auth || process.env.REPLICATE_API_TOKEN; - this.userAgent = - options.userAgent || `replicate-javascript/${packageJSON.version}`; - this.baseUrl = options.baseUrl || "https://api.replicate.com/v1"; - this.fetch = options.fetch || globalThis.fetch; - - this.collections = { - list: collections.list.bind(this), - get: collections.get.bind(this), - }; - - this.deployments = { - predictions: { - create: deployments.predictions.create.bind(this), - }, - }; - - this.hardware = { - list: hardware.list.bind(this), - }; - - this.models = { - get: models.get.bind(this), - list: models.list.bind(this), - create: models.create.bind(this), - versions: { - list: models.versions.list.bind(this), - get: models.versions.get.bind(this), - }, - }; - - this.predictions = { - create: predictions.create.bind(this), - get: predictions.get.bind(this), - cancel: predictions.cancel.bind(this), - list: predictions.list.bind(this), - }; - - this.trainings = { - create: trainings.create.bind(this), - get: trainings.get.bind(this), - cancel: trainings.cancel.bind(this), - list: trainings.list.bind(this), - }; - } - - /** - * Run a model and wait for its output. - * - * @param {string} ref - Required. The model version identifier in the format "owner/name" or "owner/name:version" - * @param {object} options - * @param {object} options.input - Required. An object with the model inputs - * @param {object} [options.wait] - Options for waiting for the prediction to finish - * @param {number} [options.wait.interval] - Polling interval in milliseconds. Defaults to 500 - * @param {string} [options.webhook] - An HTTPS URL for receiving a webhook when the prediction has new output - * @param {string[]} [options.webhook_events_filter] - You can change which events trigger webhook requests by specifying webhook events (`start`|`output`|`logs`|`completed`) - * @param {AbortSignal} [options.signal] - AbortSignal to cancel the prediction - * @param {Function} [progress] - Callback function that receives the prediction object as it's updated. The function is called when the prediction is created, each time its updated while polling for completion, and when it's completed. - * @throws {Error} If the reference is invalid - * @throws {Error} If the prediction failed - * @returns {Promise} - Resolves with the output of running the model - */ - async run(ref, options, progress) { - const { wait, ...data } = options; - - const identifier = ModelVersionIdentifier.parse(ref); - - let prediction; - if (identifier.version) { - prediction = await this.predictions.create({ - ...data, - version: identifier.version, - }); - } else if (identifier.owner && identifier.name) { - prediction = await this.predictions.create({ - ...data, - model: `${identifier.owner}/${identifier.name}`, - }); - } else { - throw new Error("Invalid model version identifier"); - } - - // Call progress callback with the initial prediction object - if (progress) { - progress(prediction); - } - - const { signal } = options; - - prediction = await this.wait( - prediction, - wait || {}, - async (updatedPrediction) => { - // Call progress callback with the updated prediction object - if (progress) { - progress(updatedPrediction); - } - - if (signal && signal.aborted) { - await this.predictions.cancel(updatedPrediction.id); - return true; // stop polling - } - - return false; // continue polling - } - ); - - // Call progress callback with the completed prediction object - if (progress) { - progress(prediction); - } - - if (prediction.status === "failed") { - throw new Error(`Prediction failed: ${prediction.error}`); - } - - return prediction.output; - } - - /** - * Make a request to the Replicate API. - * - * @param {string} route - REST API endpoint path - * @param {object} options - Request parameters - * @param {string} [options.method] - HTTP method. Defaults to GET - * @param {object} [options.params] - Query parameters - * @param {object|Headers} [options.headers] - HTTP headers - * @param {object} [options.data] - Body parameters - * @returns {Promise} - Resolves with the response object - * @throws {ApiError} If the request failed - */ - async request(route, options) { - const { auth, baseUrl, userAgent } = this; - - let url; - if (route instanceof URL) { - url = route; - } else { - url = new URL( - route.startsWith("/") ? route.slice(1) : route, - baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/` - ); - } - - const { method = "GET", params = {}, data } = options; - - for (const [key, value] of Object.entries(params)) { - url.searchParams.append(key, value); - } - - const headers = {}; - if (auth) { - headers["Authorization"] = `Token ${auth}`; - } - headers["Content-Type"] = "application/json"; - headers["User-Agent"] = userAgent; - if (options.headers) { - for (const [key, value] of Object.entries(options.headers)) { - headers[key] = value; - } - } - - const init = { - method, - headers, - body: data ? JSON.stringify(data) : undefined, - }; - - const shouldRetry = - method === "GET" - ? (response) => response.status === 429 || response.status >= 500 - : (response) => response.status === 429; - - // Workaround to fix `TypeError: Illegal invocation` error in Cloudflare Workers - // https://github.com/replicate/replicate-javascript/issues/134 - const _fetch = this.fetch; // eslint-disable-line no-underscore-dangle - const response = await withAutomaticRetries(async () => _fetch(url, init), { - shouldRetry, - }); - - if (!response.ok) { - const request = new Request(url, init); - const responseText = await response.text(); - throw new ApiError( - `Request to ${url} failed with status ${response.status} ${response.statusText}: ${responseText}.`, - request, - response - ); - } - - return response; - } - - /** - * Stream a model and wait for its output. - * - * @param {string} identifier - Required. The model version identifier in the format "{owner}/{name}:{version}" - * @param {object} options - * @param {object} options.input - Required. An object with the model inputs - * @param {string} [options.webhook] - An HTTPS URL for receiving a webhook when the prediction has new output - * @param {string[]} [options.webhook_events_filter] - You can change which events trigger webhook requests by specifying webhook events (`start`|`output`|`logs`|`completed`) - * @param {AbortSignal} [options.signal] - AbortSignal to cancel the prediction - * @throws {Error} If the prediction failed - * @yields {ServerSentEvent} Each streamed event from the prediction - */ - async *stream(ref, options) { - const { wait, ...data } = options; - - const identifier = ModelVersionIdentifier.parse(ref); - - let prediction; - if (identifier.version) { - prediction = await this.predictions.create({ - ...data, - version: identifier.version, - stream: true, - }); - } else if (identifier.owner && identifier.name) { - prediction = await this.predictions.create({ - ...data, - model: `${identifier.owner}/${identifier.name}`, - stream: true, - }); - } else { - throw new Error("Invalid model version identifier"); - } - - if (prediction.urls && prediction.urls.stream) { - const { signal } = options; - const stream = new Stream(prediction.urls.stream, { signal }); - yield* stream; - } else { - throw new Error("Prediction does not support streaming"); - } +const replicate = new Proxy(DeprecatedReplicate, { + get(target, prop, receiver) { + // Should mostly behave like the singleton. + if (named[prop]) { + return named[prop]; + } + // Provide Replicate & ApiError constructors. + if (singleton[prop]) { + return singleton[prop]; + } + // Fallback to Replicate constructor properties. + return Reflect.get(target, prop, receiver); + }, + set(_target, prop, newValue, _receiver) { + singleton[prop] = newValue; + return true; } +}); + +export default replicate; + +// - Type Definitions + +/** + * @typedef {import("./lib/types").Collection} Collection + * @typedef {import("./lib/types").ModelVersion} ModelVersion + * @typedef {import("./lib/types").Hardware} Hardware + * @typedef {import("./lib/types").Model} Model + * @typedef {import("./lib/types").Prediction} Prediction + * @typedef {import("./lib/types").Training} Training + * @typedef {import("./lib/types").ServerSentEvent} ServerSentEvent + * @typedef {import("./lib/types").Status} Status + * @typedef {import("./lib/types").Visibility} Visibility + * @typedef {import("./lib/types").WebhookEventType} WebhookEventType + */ - /** - * Paginate through a list of results. - * - * @generator - * @example - * for await (const page of replicate.paginate(replicate.predictions.list) { - * console.log(page); - * } - * @param {Function} endpoint - Function that returns a promise for the next page of results - * @yields {object[]} Each page of results - */ - async *paginate(endpoint) { - const response = await endpoint(); - yield response.results; - if (response.next) { - const nextPage = () => - this.request(response.next, { method: "GET" }).then((r) => r.json()); - yield* this.paginate(nextPage); - } - } - - /** - * Wait for a prediction to finish. - * - * If the prediction has already finished, - * this function returns immediately. - * Otherwise, it polls the API until the prediction finishes. - * - * @async - * @param {object} prediction - Prediction object - * @param {object} options - Options - * @param {number} [options.interval] - Polling interval in milliseconds. Defaults to 500 - * @param {Function} [stop] - Async callback function that is called after each polling attempt. Receives the prediction object as an argument. Return false to cancel polling. - * @throws {Error} If the prediction doesn't complete within the maximum number of attempts - * @throws {Error} If the prediction failed - * @returns {Promise} Resolves with the completed prediction object - */ - async wait(prediction, options, stop) { - const { id } = prediction; - if (!id) { - throw new Error("Invalid prediction"); - } - - if ( - prediction.status === "succeeded" || - prediction.status === "failed" || - prediction.status === "canceled" - ) { - return prediction; - } - - // eslint-disable-next-line no-promise-executor-return - const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); - - const interval = (options && options.interval) || 500; - - let updatedPrediction = await this.predictions.get(id); - - while ( - updatedPrediction.status !== "succeeded" && - updatedPrediction.status !== "failed" && - updatedPrediction.status !== "canceled" - ) { - /* eslint-disable no-await-in-loop */ - if (stop && (await stop(updatedPrediction)) === true) { - break; - } - - await sleep(interval); - updatedPrediction = await this.predictions.get(prediction.id); - /* eslint-enable no-await-in-loop */ - } - - if (updatedPrediction.status === "failed") { - throw new Error(`Prediction failed: ${updatedPrediction.error}`); - } - - return updatedPrediction; - } -} +/** + * @template T + * @typedef {import("./lib/types").Page} Page + */ -module.exports = Replicate; diff --git a/index.test.ts b/index.test.ts index 5b5a1dd..3eadf87 100644 --- a/index.test.ts +++ b/index.test.ts @@ -1,41 +1,42 @@ import { expect, jest, test } from "@jest/globals"; -import Replicate, { ApiError, Model, Prediction } from "replicate"; +import replicate, { ApiError, Model, Prediction, Replicate } from "./index.js"; import nock from "nock"; import fetch from "cross-fetch"; +import assert from "node:assert"; + +assert(process.env.REPLICATE_API_TOKEN === "test-token", `set REPLICATE_API_TOKEN to "test-token"`) -let client: Replicate; const BASE_URL = "https://api.replicate.com/v1"; nock.disableNetConnect(); -describe("Replicate client", () => { - let unmatched: any[] = []; - const handleNoMatch = (req: unknown, options: any, body: string) => - unmatched.push({ req, options, body }); - - beforeEach(() => { - client = new Replicate({ auth: "test-token" }); - client.fetch = fetch; - - unmatched = []; - nock.emitter.on("no match", handleNoMatch); - }); +describe(`const replicate = require("replicate");`, () => { + testInstance(() => { + replicate.fetch = fetch; + return replicate; + }) +}); - afterEach(() => { - nock.emitter.off("no match", handleNoMatch); - expect(unmatched).toStrictEqual([]); +describe(`const Replicate = require("replicate"); (deprecated)`, () => { + testConstructor((opts) => new replicate({ auth: "test-token", fetch, ...opts })) + testInstance((opts) => new replicate({ auth: "test-token", fetch, ...opts })) +}); - nock.abortPendingRequests(); - nock.cleanAll(); - }); +describe(`const Replicate = require("replicate").Replicate;`, () => { + testConstructor((opts) => new replicate.Replicate({ auth: "test-token", fetch, ...opts })) + testInstance((opts) => new replicate.Replicate({ auth: "test-token", fetch, ...opts })) +}); +/** Test suite to exercise the Replicate constructor */ +function testConstructor(createClient: (opts?: object) => Replicate) { describe("constructor", () => { test("Sets default baseUrl", () => { + const client = createClient(); expect(client.baseUrl).toBe("https://api.replicate.com/v1"); }); test("Sets custom baseUrl", () => { - const clientWithCustomBaseUrl = new Replicate({ + const clientWithCustomBaseUrl = createClient({ baseUrl: "https://example.com/", auth: "test-token", }); @@ -43,7 +44,7 @@ describe("Replicate client", () => { }); test("Sets custom userAgent", () => { - const clientWithCustomUserAgent = new Replicate({ + const clientWithCustomUserAgent = createClient({ userAgent: "my-app/1.2.3", auth: "test-token", }); @@ -54,7 +55,7 @@ describe("Replicate client", () => { process.env.REPLICATE_API_TOKEN = "test-token"; expect(() => { - const clientWithImplicitAuth = new Replicate(); + const clientWithImplicitAuth = createClient(); expect(clientWithImplicitAuth.auth).toBe("test-token"); }).not.toThrow(); @@ -62,11 +63,32 @@ describe("Replicate client", () => { test("Does not throw error if blank auth token is provided", () => { expect(() => { - new Replicate({ auth: "" }); + createClient({ auth: "" }); }).not.toThrow(); }); }); + } + +/** Test suite to exercise the Replicate instance */ +function testInstance(createClient: (opts?: object) => Replicate) { + let unmatched: any[] = []; + const handleNoMatch = (req: unknown, options: any, body: string) => + unmatched.push({ req, options, body }); + + beforeEach(() => { + unmatched = []; + nock.emitter.on("no match", handleNoMatch); + }); + + afterEach(() => { + nock.emitter.off("no match", handleNoMatch); + expect(unmatched).toStrictEqual([]); + + nock.abortPendingRequests(); + nock.cleanAll(); + }); + describe("collections.list", () => { test("Calls the correct API route", async () => { nock(BASE_URL) @@ -89,6 +111,7 @@ describe("Replicate client", () => { previous: null, }); + const client = createClient(); const collections = await client.collections.list(); expect(collections.results.length).toBe(2); }); @@ -105,6 +128,7 @@ describe("Replicate client", () => { models: [], }); + const client = createClient(); const collection = await client.collections.get("super-resolution"); expect(collection.name).toBe("Super resolution"); }); @@ -128,6 +152,7 @@ describe("Replicate client", () => { latest_version: {}, }); + const client = createClient(); await client.models.get("replicate", "hello-world"); }); // Add more tests for error handling, edge cases, etc. @@ -150,6 +175,7 @@ describe("Replicate client", () => { }); const results: Model[] = []; + const client = createClient(); for await (const batch of client.paginate(client.models.list)) { results.push(...batch); } @@ -188,7 +214,9 @@ describe("Replicate client", () => { logs: null, metrics: {}, }); + const client = createClient(); const prediction = await client.predictions.create({ + model: "foo", version: "5c7d5dc6dd8bf75c1acaa8565735e7986bc5b66206b55cca93cb72c9bf15ccaa", input: { @@ -208,7 +236,9 @@ describe("Replicate client", () => { return body; }); + const client = createClient(); await client.predictions.create({ + model: "foo", version: "5c7d5dc6dd8bf75c1acaa8565735e7986bc5b66206b55cca93cb72c9bf15ccaa", input: { @@ -220,7 +250,9 @@ describe("Replicate client", () => { test("Throws an error if webhook URL is invalid", async () => { await expect(async () => { + const client = createClient(); await client.predictions.create({ + model: "foo", version: "5c7d5dc6dd8bf75c1acaa8565735e7986bc5b66206b55cca93cb72c9bf15ccaa", input: { @@ -244,7 +276,9 @@ describe("Replicate client", () => { try { expect.hasAssertions(); + const client = createClient(); await client.predictions.create({ + model: "foo", version: "5c7d5dc6dd8bf75c1acaa8565735e7986bc5b66206b55cca93cb72c9bf15ccaa", input: { @@ -271,7 +305,9 @@ describe("Replicate client", () => { .reply(201, { id: "ufawqhfynnddngldkgtslldrkq", }); + const client = createClient(); const prediction = await client.predictions.create({ + model: "foo", version: "5c7d5dc6dd8bf75c1acaa8565735e7986bc5b66206b55cca93cb72c9bf15ccaa", input: { @@ -290,8 +326,10 @@ describe("Replicate client", () => { { "Content-Type": "application/json" } ); + const client = createClient(); await expect( client.predictions.create({ + model: "foo", version: "5c7d5dc6dd8bf75c1acaa8565735e7986bc5b66206b55cca93cb72c9bf15ccaa", input: { @@ -335,6 +373,7 @@ describe("Replicate client", () => { predict_time: 4.484541, }, }); + const client = createClient(); const prediction = await client.predictions.get( "rrr4z55ocneqzikepnug6xezpe" ); @@ -356,6 +395,7 @@ describe("Replicate client", () => { id: "rrr4z55ocneqzikepnug6xezpe", }); + const client = createClient(); const prediction = await client.predictions.get( "rrr4z55ocneqzikepnug6xezpe" ); @@ -377,6 +417,7 @@ describe("Replicate client", () => { id: "rrr4z55ocneqzikepnug6xezpe", }); + const client = createClient(); const prediction = await client.predictions.get( "rrr4z55ocneqzikepnug6xezpe" ); @@ -411,6 +452,7 @@ describe("Replicate client", () => { metrics: {}, }); + const client = createClient(); const prediction = await client.predictions.cancel( "ufawqhfynnddngldkgtslldrkq" ); @@ -447,6 +489,7 @@ describe("Replicate client", () => { ], }); + const client = createClient(); const predictions = await client.predictions.list(); expect(predictions.results.length).toBe(1); expect(predictions.results[0].id).toBe("jpzd7hm5gfcapbfyt4mqytarku"); @@ -468,6 +511,7 @@ describe("Replicate client", () => { }); const results: Prediction[] = []; + const client = createClient(); for await (const batch of client.paginate(client.predictions.list)) { results.push(...batch); } @@ -502,6 +546,7 @@ describe("Replicate client", () => { completed_at: null, }); + const client = createClient(); const training = await client.trainings.create( "owner", "model", @@ -517,6 +562,7 @@ describe("Replicate client", () => { }); test("Throws an error if webhook is not a valid URL", async () => { + const client = createClient(); await expect( client.trainings.create( "owner", @@ -559,6 +605,7 @@ describe("Replicate client", () => { completed_at: null, }); + const client = createClient(); const training = await client.trainings.get("zz4ibbonubfz7carwiefibzgga"); expect(training.status).toBe("succeeded"); }); @@ -589,6 +636,7 @@ describe("Replicate client", () => { completed_at: null, }); + const client = createClient(); const training = await client.trainings.cancel( "zz4ibbonubfz7carwiefibzgga" ); @@ -625,6 +673,7 @@ describe("Replicate client", () => { ], }); + const client = createClient(); const trainings = await client.trainings.list(); expect(trainings.results.length).toBe(1); expect(trainings.results[0].id).toBe("jpzd7hm5gfcapbfyt4mqytarku"); @@ -646,6 +695,7 @@ describe("Replicate client", () => { }); const results: Prediction[] = []; + const client = createClient(); for await (const batch of client.paginate(client.trainings.list)) { results.push(...batch); } @@ -684,6 +734,7 @@ describe("Replicate client", () => { logs: null, metrics: {}, }); + const client = createClient(); const prediction = await client.deployments.predictions.create( "replicate", "greeter", @@ -721,6 +772,7 @@ describe("Replicate client", () => { get: "https://api.replicate.com/v1/predictions/heat2o3bzn3ahtr6bjfftvbaci", }, }); + const client = createClient(); const prediction = await client.predictions.create({ model: "meta/llama-2-70b-chat", input: { @@ -745,6 +797,7 @@ describe("Replicate client", () => { { name: "Nvidia A40 (Large) GPU", sku: "gpu-a40-large" }, ]); + const client = createClient(); const hardware = await client.hardware.list(); expect(hardware.length).toBe(4); expect(hardware[0].name).toBe("CPU"); @@ -763,6 +816,7 @@ describe("Replicate client", () => { description: "A test model", }); + const client = createClient(); const model = await client.models.create("test-owner", "test-model", { visibility: "public", hardware: "cpu", @@ -779,8 +833,6 @@ describe("Replicate client", () => { describe("run", () => { test("Calls the correct API routes for a version", async () => { - const firstPollingRequest = true; - nock(BASE_URL) .post("/predictions") .reply(201, { @@ -802,6 +854,7 @@ describe("Replicate client", () => { const progress = jest.fn(); + const client = createClient(); const output = await client.run( "owner/model:5c7d5dc6dd8bf75c1acaa8565735e7986bc5b66206b55cca93cb72c9bf15ccaa", { @@ -838,7 +891,6 @@ describe("Replicate client", () => { }); test("Calls the correct API routes for a model", async () => { - const firstPollingRequest = true; nock(BASE_URL) .post("/models/replicate/hello-world/predictions") @@ -861,6 +913,7 @@ describe("Replicate client", () => { const progress = jest.fn(); + const client = createClient(); const output = await client.run( "replicate/hello-world", { @@ -910,24 +963,25 @@ describe("Replicate client", () => { output: "foobar", }); + const client = createClient(); await expect( client.run("a/b-1.0:abc123", { input: { text: "Hello, world!" } }) ).resolves.not.toThrow(); }); test("Throws an error for invalid identifiers", async () => { + const client = createClient(); const options = { input: { text: "Hello, world!" } }; - // @ts-expect-error await expect(client.run("owner:abc123", options)).rejects.toThrow(); await expect(client.run("/model:abc123", options)).rejects.toThrow(); - // @ts-expect-error await expect(client.run(":abc123", options)).rejects.toThrow(); }); test("Throws an error if webhook URL is invalid", async () => { + const client = createClient(); await expect(async () => { await client.run( "owner/model:5c7d5dc6dd8bf75c1acaa8565735e7986bc5b66206b55cca93cb72c9bf15ccaa", @@ -966,6 +1020,7 @@ describe("Replicate client", () => { status: "canceled", }); + const client = createClient(); await client.run( "owner/model:5c7d5dc6dd8bf75c1acaa8565735e7986bc5b66206b55cca93cb72c9bf15ccaa", { @@ -981,4 +1036,5 @@ describe("Replicate client", () => { }); // Continue with tests for other methods -}); +} + diff --git a/integration/commonjs/constructor.test.js b/integration/commonjs/constructor.test.js new file mode 100644 index 0000000..79048b0 --- /dev/null +++ b/integration/commonjs/constructor.test.js @@ -0,0 +1,21 @@ +const { test } = require('node:test'); +const assert = require('node:assert'); +const Replicate = require('replicate').Replicate; + +const replicate = new Replicate(); + +async function main() { + return await replicate.run( + "replicate/hello-world:5c7d5dc6dd8bf75c1acaa8565735e7986bc5b66206b55cca93cb72c9bf15ccaa", + { + input: { + text: "Claire CommonJS" + } + } + ); +}; + +test('main', async () => { + const output = await main(); + assert.equal(output, "hello Claire CommonJS"); +}); diff --git a/integration/commonjs/deprecated.test.js b/integration/commonjs/deprecated.test.js new file mode 100644 index 0000000..49b5579 --- /dev/null +++ b/integration/commonjs/deprecated.test.js @@ -0,0 +1,21 @@ +const { test } = require('node:test'); +const assert = require('node:assert'); +const Replicate = require('replicate'); + +const replicate = new Replicate(); + +async function main() { + return await replicate.run( + "replicate/hello-world:5c7d5dc6dd8bf75c1acaa8565735e7986bc5b66206b55cca93cb72c9bf15ccaa", + { + input: { + text: "Claire CommonJS" + } + } + ); +}; + +test('main', async () => { + const output = await main(); + assert.equal(output, "hello Claire CommonJS"); +}); diff --git a/integration/commonjs/index.test.js b/integration/commonjs/index.test.js deleted file mode 100644 index 5ef7b63..0000000 --- a/integration/commonjs/index.test.js +++ /dev/null @@ -1,8 +0,0 @@ -const { test } = require('node:test'); -const assert = require('node:assert'); -const main = require('./index'); - -test('main', async () => { - const output = await main(); - assert.equal(output, "hello Claire CommonJS"); -}); diff --git a/integration/commonjs/package.json b/integration/commonjs/package.json index 7fb6fc8..d6e6e47 100644 --- a/integration/commonjs/package.json +++ b/integration/commonjs/package.json @@ -5,7 +5,7 @@ "description": "CommonJS integration tests", "main": "index.js", "scripts": { - "test": "node --test ./index.test.js" + "test": "node --test ./*.test.js" }, "dependencies": { "replicate": "file:../../" diff --git a/integration/commonjs/singleton.test.js b/integration/commonjs/singleton.test.js new file mode 100644 index 0000000..1211080 --- /dev/null +++ b/integration/commonjs/singleton.test.js @@ -0,0 +1,19 @@ +const { test } = require('node:test'); +const assert = require('node:assert'); +const replicate = require('replicate'); + +async function main() { + return await replicate.run( + "replicate/hello-world:5c7d5dc6dd8bf75c1acaa8565735e7986bc5b66206b55cca93cb72c9bf15ccaa", + { + input: { + text: "Claire CommonJS" + } + } + ); +}; + +test('main', async () => { + const output = await main(); + assert.equal(output, "hello Claire CommonJS"); +}); diff --git a/integration/esm/constructor.test.js b/integration/esm/constructor.test.js new file mode 100644 index 0000000..3865fca --- /dev/null +++ b/integration/esm/constructor.test.js @@ -0,0 +1,21 @@ +import { test } from 'node:test'; +import assert from 'node:assert'; +import { Replicate } from "replicate"; + +const replicate = new Replicate(); + +async function main() { + return await replicate.run( + "replicate/hello-world:5c7d5dc6dd8bf75c1acaa8565735e7986bc5b66206b55cca93cb72c9bf15ccaa", + { + input: { + text: "Evelyn ESM" + } + } + ); +}; + +test('main', async () => { + const output = await main(); + assert.equal(output, "hello Evelyn ESM"); +}); diff --git a/integration/esm/index.js b/integration/esm/deprecated.test.js similarity index 50% rename from integration/esm/index.js rename to integration/esm/deprecated.test.js index 547b726..0a5c7ee 100644 --- a/integration/esm/index.js +++ b/integration/esm/deprecated.test.js @@ -1,10 +1,10 @@ +import { test } from 'node:test'; +import assert from 'node:assert'; import Replicate from "replicate"; -const replicate = new Replicate({ - auth: process.env.REPLICATE_API_TOKEN, -}); +const replicate = new Replicate(); -export default async function main() { +async function main() { return await replicate.run( "replicate/hello-world:5c7d5dc6dd8bf75c1acaa8565735e7986bc5b66206b55cca93cb72c9bf15ccaa", { @@ -14,3 +14,8 @@ export default async function main() { } ); }; + +test('main', async () => { + const output = await main(); + assert.equal(output, "hello Evelyn ESM"); +}); diff --git a/integration/esm/index.test.js b/integration/esm/index.test.js deleted file mode 100644 index 2bd276f..0000000 --- a/integration/esm/index.test.js +++ /dev/null @@ -1,8 +0,0 @@ -import { test } from 'node:test'; -import assert from 'node:assert'; -import main from './index.js'; - -test('main', async () => { - const output = await main(); - assert.equal(output, "hello Evelyn ESM"); -}); diff --git a/integration/esm/package.json b/integration/esm/package.json index 51076d7..ca093ac 100644 --- a/integration/esm/package.json +++ b/integration/esm/package.json @@ -6,7 +6,7 @@ "main": "index.js", "type": "module", "scripts": { - "test": "node --test ./index.test.js" + "test": "node --test ./*.test.js" }, "dependencies": { "replicate": "file:../../" diff --git a/integration/esm/singleton.test.js b/integration/esm/singleton.test.js new file mode 100644 index 0000000..59860ad --- /dev/null +++ b/integration/esm/singleton.test.js @@ -0,0 +1,19 @@ +import { test } from 'node:test'; +import assert from 'node:assert'; +import replicate from "replicate"; + +async function main() { + return await replicate.run( + "replicate/hello-world:5c7d5dc6dd8bf75c1acaa8565735e7986bc5b66206b55cca93cb72c9bf15ccaa", + { + input: { + text: "Evelyn ESM" + } + } + ); +}; + +test('main', async () => { + const output = await main(); + assert.equal(output, "hello Evelyn ESM"); +}); diff --git a/integration/typescript/constructor.test.ts b/integration/typescript/constructor.test.ts new file mode 100644 index 0000000..35c68b7 --- /dev/null +++ b/integration/typescript/constructor.test.ts @@ -0,0 +1,21 @@ +import { test } from 'node:test'; +import * as assert from 'node:assert'; +import { Replicate } from "replicate"; + +const replicate = new Replicate(); + +async function main() { + return await replicate.run( + "replicate/hello-world:5c7d5dc6dd8bf75c1acaa8565735e7986bc5b66206b55cca93cb72c9bf15ccaa", + { + input: { + text: "Tracy TypeScript" + } + } + ); +}; + +test('main', async () => { + const output = await main(); + assert.equal(output, "hello Tracy TypeScript"); +}); diff --git a/integration/typescript/index.ts b/integration/typescript/deprecated.test.ts similarity index 50% rename from integration/typescript/index.ts rename to integration/typescript/deprecated.test.ts index 8e27a3b..a3c4059 100644 --- a/integration/typescript/index.ts +++ b/integration/typescript/deprecated.test.ts @@ -1,10 +1,10 @@ +import { test } from 'node:test'; +import * as assert from 'node:assert'; import Replicate from "replicate"; -const replicate = new Replicate({ - auth: process.env.REPLICATE_API_TOKEN, -}); +const replicate = new Replicate(); -export default async function main() { +async function main() { return await replicate.run( "replicate/hello-world:5c7d5dc6dd8bf75c1acaa8565735e7986bc5b66206b55cca93cb72c9bf15ccaa", { @@ -14,3 +14,8 @@ export default async function main() { } ); }; + +test('main', async () => { + const output = await main(); + assert.equal(output, "hello Tracy TypeScript"); +}); diff --git a/integration/typescript/index.test.ts b/integration/typescript/index.test.ts deleted file mode 100644 index be4ab90..0000000 --- a/integration/typescript/index.test.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { test } from 'node:test'; -import assert from 'node:assert'; -import main from './index.js'; - -// Verify exported types. -import type { - Status, - Visibility, - WebhookEventType, - ApiError, - Collection, - Hardware, - Model, - ModelVersion, - Prediction, - Training, - Page, - ServerSentEvent, -} from "replicate"; - -test('main', async () => { - const output = await main(); - assert.equal(output, "hello Tracy TypeScript"); -}); diff --git a/integration/typescript/package.json b/integration/typescript/package.json index 4adae99..8dc9a5c 100644 --- a/integration/typescript/package.json +++ b/integration/typescript/package.json @@ -6,7 +6,7 @@ "main": "index.js", "type": "module", "scripts": { - "test": "tsc && node --test ./dist/index.test.js" + "test": "tsc && node --test ./dist/*.test.js" }, "dependencies": { "@types/node": "^20.11.0", diff --git a/integration/typescript/singleton.test.ts b/integration/typescript/singleton.test.ts new file mode 100644 index 0000000..f27a94b --- /dev/null +++ b/integration/typescript/singleton.test.ts @@ -0,0 +1,19 @@ +import { test } from 'node:test'; +import * as assert from 'node:assert'; +import replicate from "replicate"; + +async function main() { + return await replicate.run( + "replicate/hello-world:5c7d5dc6dd8bf75c1acaa8565735e7986bc5b66206b55cca93cb72c9bf15ccaa", + { + input: { + text: "Tracy TypeScript" + } + } + ); +}; + +test('main', async () => { + const output = await main(); + assert.equal(output, "hello Tracy TypeScript"); +}); diff --git a/integration/typescript/tsconfig.json b/integration/typescript/tsconfig.json index 4b733f2..70d29a2 100644 --- a/integration/typescript/tsconfig.json +++ b/integration/typescript/tsconfig.json @@ -77,7 +77,7 @@ // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ // "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */ // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ - "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ + "esModuleInterop": false, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ diff --git a/integration/typescript/types.test.ts b/integration/typescript/types.test.ts new file mode 100644 index 0000000..71210e4 --- /dev/null +++ b/integration/typescript/types.test.ts @@ -0,0 +1,61 @@ +import { ApiError, Collection, Hardware, Model, ModelVersion, Page, Prediction, Status, Training, Visibility, WebhookEventType } from "replicate"; + +// NOTE: We export the constants to avoid unused varaible issues. + +export const collection: Collection = { name: "", slug: "", description: "", models: [] }; +export const status: Status = "starting"; +export const visibility: Visibility = "public"; +export const webhookType: WebhookEventType = "start"; +export const err: ApiError = Object.assign(new Error(), {request: new Request("file://"), response: new Response()}); +export const hardware: Hardware = { sku: "", name: "" }; +export const model: Model = { + url: "", + owner: "", + name: "", + description: "", + visibility: "public", + github_url: "", + paper_url: "", + license_url: "", + run_count: 10, + cover_image_url: "", + default_example: undefined, + latest_version: undefined, +}; +export const version: ModelVersion = { + id: "", + created_at: "", + cog_version: "", + openapi_schema: "", +}; +export const prediction: Prediction = { + id: "", + status: "starting", + model: "", + version: "", + input: {}, + output: {}, + source: "api", + error: undefined, + logs: "", + metrics: { + predict_time: 100, + }, + webhook: "", + webhook_events_filter: [], + created_at: "", + started_at: "", + completed_at: "", + urls: { + get: "", + cancel: "", + stream: "", + }, +}; +export const training: Training = prediction; + +export const page: Page = { + previous: "", + next: "", + results: [version], +}; diff --git a/jest.config.js b/jest.config.js index 058816a..6691a51 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,15 +1,23 @@ // eslint-disable-next-line jsdoc/valid-types /** @type {import('ts-jest').JestConfigWithTsJest} */ -module.exports = { - preset: "ts-jest", - testEnvironment: "node", +export default { + // [...] + preset: 'ts-jest/presets/js-with-ts-esm', // or other ESM presets + moduleNameMapper: { + '^(\\.{1,2}/.*)\\.js$': '$1', + }, testPathIgnorePatterns: ["integration"], + testEnvironment: "node", + extensionsToTreatAsEsm: ['.ts'], + transform: { - "^.+\\.ts?$": [ - "ts-jest", + // '^.+\\.[tj]sx?$' to process js/ts with `ts-jest` + // '^.+\\.m?[tj]sx?$' to process js/ts/mjs/mts with `ts-jest` + '^.+\\.tsx?$': [ + 'ts-jest', { - tsconfig: "tsconfig.json", + useESM: true, }, ], }, -}; +} diff --git a/lib/collections.js b/lib/collections.js index 9332aaa..f908fbb 100644 --- a/lib/collections.js +++ b/lib/collections.js @@ -1,8 +1,14 @@ +/** @typedef {import("./types").Collection} Collection */ +/** + * @template T + * @typedef {import("./types").Page} Page + */ + /** * Fetch a model collection * * @param {string} collection_slug - Required. The slug of the collection. See http://replicate.com/collections - * @returns {Promise} - Resolves with the collection data + * @returns {Promise} - Resolves with the collection data */ async function getCollection(collection_slug) { const response = await this.request(`/collections/${collection_slug}`, { @@ -15,7 +21,7 @@ async function getCollection(collection_slug) { /** * Fetch a list of model collections * - * @returns {Promise} - Resolves with the collections data + * @returns {Promise>} - Resolves with the collections data */ async function listCollections() { const response = await this.request("/collections", { @@ -25,4 +31,5 @@ async function listCollections() { return response.json(); } -module.exports = { get: getCollection, list: listCollections }; +export const get = getCollection; +export const list = listCollections; diff --git a/lib/deployments.js b/lib/deployments.js index 6f32cdb..bcf811a 100644 --- a/lib/deployments.js +++ b/lib/deployments.js @@ -1,14 +1,16 @@ +/** @typedef {import("./types").Prediction} Prediction */ + /** * Create a new prediction with a deployment * * @param {string} deployment_owner - Required. The username of the user or organization who owns the deployment * @param {string} deployment_name - Required. The name of the deployment * @param {object} options - * @param {object} options.input - Required. An object with the model inputs + * @param {unknown} options.input - Required. An object with the model inputs * @param {boolean} [options.stream] - Whether to stream the prediction output. Defaults to false * @param {string} [options.webhook] - An HTTPS URL for receiving a webhook when the prediction has new output - * @param {string[]} [options.webhook_events_filter] - You can change which events trigger webhook requests by specifying webhook events (`start`|`output`|`logs`|`completed`) - * @returns {Promise} Resolves with the created prediction data + * @param {WebhookEventType[]} [options.webhook_events_filter] - You can change which events trigger webhook requests by specifying webhook events (`start`|`output`|`logs`|`completed`) + * @returns {Promise} Resolves with the created prediction data */ async function createPrediction(deployment_owner, deployment_name, options) { const { stream, ...data } = options; @@ -33,8 +35,6 @@ async function createPrediction(deployment_owner, deployment_name, options) { return response.json(); } -module.exports = { - predictions: { +export const predictions = { create: createPrediction, - }, }; diff --git a/lib/error.js b/lib/error.js index cf05cd1..e863ec4 100644 --- a/lib/error.js +++ b/lib/error.js @@ -18,4 +18,4 @@ class ApiError extends Error { } } -module.exports = ApiError; +export default ApiError; diff --git a/lib/hardware.js b/lib/hardware.js index d717548..8644000 100644 --- a/lib/hardware.js +++ b/lib/hardware.js @@ -1,7 +1,8 @@ +/** @typedef {import("./types").Hardware} Hardware */ /** * List hardware * - * @returns {Promise} Resolves with the array of hardware + * @returns {Promise} Resolves with the array of hardware */ async function listHardware() { const response = await this.request("/hardware", { @@ -11,6 +12,4 @@ async function listHardware() { return response.json(); } -module.exports = { - list: listHardware, -}; +export const list = listHardware; diff --git a/lib/identifier.js b/lib/identifier.js index 86e23ee..a054081 100644 --- a/lib/identifier.js +++ b/lib/identifier.js @@ -2,10 +2,10 @@ * A reference to a model version in the format `owner/name` or `owner/name:version`. */ class ModelVersionIdentifier { - /* - * @param {string} Required. The model owner. - * @param {string} Required. The model name. - * @param {string} The model version. + /** + * @param {string} owner Required. The model owner. + * @param {string} name Required. The model name. + * @param {string | null=} version The model version. */ constructor(owner, name, version = null) { this.owner = owner; @@ -13,10 +13,10 @@ class ModelVersionIdentifier { this.version = version; } - /* + /** * Parse a reference to a model version * - * @param {string} + * @param {string} ref * @returns {ModelVersionIdentifier} * @throws {Error} If the reference is invalid. */ @@ -36,4 +36,4 @@ class ModelVersionIdentifier { } } -module.exports = ModelVersionIdentifier; +export default ModelVersionIdentifier; diff --git a/lib/models.js b/lib/models.js index c6a02fc..0279892 100644 --- a/lib/models.js +++ b/lib/models.js @@ -1,9 +1,18 @@ +/** @typedef {import("./types").Model} Model */ +/** @typedef {import("./types").ModelVersion} ModelVersion */ +/** @typedef {import("./types").Prediction} Prediction */ +/** @typedef {import("./types").Visibility} Visibility */ +/** + * @template T + * @typedef {import("./types").Page} Page + */ + /** * Get information about a model * * @param {string} model_owner - Required. The name of the user or organization that owns the model * @param {string} model_name - Required. The name of the model - * @returns {Promise} Resolves with the model data + * @returns {Promise} Resolves with the model data */ async function getModel(model_owner, model_name) { const response = await this.request(`/models/${model_owner}/${model_name}`, { @@ -18,7 +27,7 @@ async function getModel(model_owner, model_name) { * * @param {string} model_owner - Required. The name of the user or organization that owns the model * @param {string} model_name - Required. The name of the model - * @returns {Promise} Resolves with the list of model versions + * @returns {Promise>} Resolves with the list of model versions */ async function listModelVersions(model_owner, model_name) { const response = await this.request( @@ -37,7 +46,7 @@ async function listModelVersions(model_owner, model_name) { * @param {string} model_owner - Required. The name of the user or organization that owns the model * @param {string} model_name - Required. The name of the model * @param {string} version_id - Required. The model version - * @returns {Promise} Resolves with the model version data + * @returns {Promise} Resolves with the model version data */ async function getModelVersion(model_owner, model_name, version_id) { const response = await this.request( @@ -53,7 +62,7 @@ async function getModelVersion(model_owner, model_name, version_id) { /** * List all public models * - * @returns {Promise} Resolves with the model version data + * @returns {Promise>} Resolves with the model version data */ async function listModels() { const response = await this.request("/models", { @@ -69,14 +78,14 @@ async function listModels() { * @param {string} model_owner - Required. The name of the user or organization that will own the model. This must be the same as the user or organization that is making the API request. In other words, the API token used in the request must belong to this user or organization. * @param {string} model_name - Required. The name of the model. This must be unique among all models owned by the user or organization. * @param {object} options - * @param {("public"|"private")} options.visibility - Required. Whether the model should be public or private. A public model can be viewed and run by anyone, whereas a private model can be viewed and run only by the user or organization members that own the model. + * @param {Visibility} options.visibility - Required. Whether the model should be public or private. A public model can be viewed and run by anyone, whereas a private model can be viewed and run only by the user or organization members that own the model. * @param {string} options.hardware - Required. The SKU for the hardware used to run the model. Possible values can be found by calling `Replicate.hardware.list()`. * @param {string} options.description - A description of the model. - * @param {string} options.github_url - A URL for the model's source code on GitHub. - * @param {string} options.paper_url - A URL for the model's paper. - * @param {string} options.license_url - A URL for the model's license. - * @param {string} options.cover_image_url - A URL for the model's cover image. This should be an image file. - * @returns {Promise} Resolves with the model version data + * @param {string=} options.github_url - A URL for the model's source code on GitHub. + * @param {string=} options.paper_url - A URL for the model's paper. + * @param {string=} options.license_url - A URL for the model's license. + * @param {string=} options.cover_image_url - A URL for the model's cover image. This should be an image file. + * @returns {Promise} Resolves with the model version data */ async function createModel(model_owner, model_name, options) { const data = { owner: model_owner, name: model_name, ...options }; @@ -89,9 +98,7 @@ async function createModel(model_owner, model_name, options) { return response.json(); } -module.exports = { - get: getModel, - list: listModels, - create: createModel, - versions: { list: listModelVersions, get: getModelVersion }, -}; +export const get = getModel; +export const list = listModels; +export const create = createModel; +export const versions = { list: listModelVersions, get: getModelVersion }; diff --git a/lib/predictions.js b/lib/predictions.js index 294e8d9..9bf3731 100644 --- a/lib/predictions.js +++ b/lib/predictions.js @@ -1,14 +1,20 @@ +/** + * @template T + * @typedef {import("./types").Page} Page + */ +/** @typedef {import("./types").Prediction} Prediction */ + /** * Create a new prediction * * @param {object} options - * @param {string} options.model - The model. - * @param {string} options.version - The model version. - * @param {object} options.input - Required. An object with the model inputs + * @param {string=} options.model - The model (for official models) + * @param {string=} options.version - The model version. + * @param {unknown} options.input - Required. An object with the model inputs * @param {string} [options.webhook] - An HTTPS URL for receiving a webhook when the prediction has new output * @param {string[]} [options.webhook_events_filter] - You can change which events trigger webhook requests by specifying webhook events (`start`|`output`|`logs`|`completed`) * @param {boolean} [options.stream] - Whether to stream the prediction output. Defaults to false - * @returns {Promise} Resolves with the created prediction + * @returns {Promise} Resolves with the created prediction */ async function createPrediction(options) { const { model, version, stream, ...data } = options; @@ -43,8 +49,8 @@ async function createPrediction(options) { /** * Fetch a prediction by ID * - * @param {number} prediction_id - Required. The prediction ID - * @returns {Promise} Resolves with the prediction data + * @param {string} prediction_id - Required. The prediction ID + * @returns {Promise} Resolves with the prediction data */ async function getPrediction(prediction_id) { const response = await this.request(`/predictions/${prediction_id}`, { @@ -58,7 +64,7 @@ async function getPrediction(prediction_id) { * Cancel a prediction by ID * * @param {string} prediction_id - Required. The training ID - * @returns {Promise} Resolves with the data for the training + * @returns {Promise} Resolves with the data for the training */ async function cancelPrediction(prediction_id) { const response = await this.request(`/predictions/${prediction_id}/cancel`, { @@ -71,7 +77,7 @@ async function cancelPrediction(prediction_id) { /** * List all predictions * - * @returns {Promise} - Resolves with a page of predictions + * @returns {Promise>} - Resolves with a page of predictions */ async function listPredictions() { const response = await this.request("/predictions", { @@ -81,9 +87,7 @@ async function listPredictions() { return response.json(); } -module.exports = { - create: createPrediction, - get: getPrediction, - cancel: cancelPrediction, - list: listPredictions, -}; +export const create = createPrediction; +export const get = getPrediction; +export const cancel = cancelPrediction; +export const list = listPredictions; diff --git a/lib/replicate.js b/lib/replicate.js new file mode 100644 index 0000000..d5fb6cd --- /dev/null +++ b/lib/replicate.js @@ -0,0 +1,397 @@ +/** + * @template T + * @typedef {import("./types").Page} Page + */ + +/** @typedef {import("./types").Prediction} Prediction */ +/** @typedef {import("./types").WebhookEventType} WebhookEventType */ + +import ApiError from "./error.js"; +import ModelVersionIdentifier from "./identifier.js"; +import { Stream } from "./stream.js"; +import { withAutomaticRetries } from "./util.js"; + +import * as collections from "./collections.js" +import * as deployments from "./deployments.js" +import * as hardware from "./hardware.js" +import * as models from "./models.js" +import * as predictions from "./predictions.js" +import * as trainings from "./trainings.js" +import { version } from "../version.js"; + + +/** + * Replicate API client library + * + * @see https://replicate.com/docs/reference/http + * @example + * + * // Create a new Replicate API client instance + * const Replicate = require("replicate").Replicate; + * const replicate = new Replicate({ + * // get your token from https://replicate.com/account + * auth: process.env.REPLICATE_API_TOKEN, + * userAgent: "my-app/1.2.3" + * }); + * + * // Run a model and await the result: + * const model = 'owner/model:version-id' + * const input = {text: 'Hello, world!'} + * const output = await replicate.run(model, { input }); + */ +export default class Replicate { + /** + * Create a new Replicate API client instance. + * + * @example + * // Create a new Replicate API client instance + * const Replicate = require("replicate"); + * const replicate = new Replicate({ + * // get your token from https://replicate.com/account + * auth: process.env.REPLICATE_API_TOKEN, + * userAgent: "my-app/1.2.3" + * }); + * + * // Run a model and await the result: + * const model = 'owner/model:version-id' + * const input = {text: 'Hello, world!'} + * const output = await replicate.run(model, { input }); + * + * @param {Object} [options] - Configuration options for the client + * @param {string} [options.auth] - API access token. Defaults to the `REPLICATE_API_TOKEN` environment variable. + * @param {string} [options.userAgent] - Identifier of your app + * @param {string} [options.baseUrl] - Defaults to https://api.replicate.com/v1 + * @param {Function} [options.fetch] - Fetch function to use. Defaults to `globalThis.fetch` + */ + constructor(options = {}) { + /** @type {string} */ + this.auth = options.auth || process.env.REPLICATE_API_TOKEN; + + /** @type {string} */ + this.userAgent = + options.userAgent || `replicate-javascript/${version}`; + + /** @type {string} */ + this.baseUrl = options.baseUrl || "https://api.replicate.com/v1"; + + /** @type {fetch} */ + this.fetch = options.fetch || globalThis.fetch; + + /** @type {collections} */ + this.collections = { + list: collections.list.bind(this), + get: collections.get.bind(this), + }; + + /** @type {deployments} */ + this.deployments = { + predictions: { + create: deployments.predictions.create.bind(this), + }, + }; + + /** @type {hardware} */ + this.hardware = { + list: hardware.list.bind(this), + }; + + /** @type {models} */ + this.models = { + get: models.get.bind(this), + list: models.list.bind(this), + create: models.create.bind(this), + versions: { + list: models.versions.list.bind(this), + get: models.versions.get.bind(this), + }, + }; + + /** @type {predictions} */ + this.predictions = { + create: predictions.create.bind(this), + get: predictions.get.bind(this), + cancel: predictions.cancel.bind(this), + list: predictions.list.bind(this), + }; + + /** @type {trainings} */ + this.trainings = { + create: trainings.create.bind(this), + get: trainings.get.bind(this), + cancel: trainings.cancel.bind(this), + list: trainings.list.bind(this), + }; + } + + /** + * Run a model and wait for its output. + * + * @param {string} ref - Required. The model version identifier in the format "owner/name" or "owner/name:version" + * @param {object} options + * @param {object} options.input - Required. An object with the model inputs + * @param {object} [options.wait] - Options for waiting for the prediction to finish + * @param {number} [options.wait.interval] - Polling interval in milliseconds. Defaults to 500 + * @param {string} [options.webhook] - An HTTPS URL for receiving a webhook when the prediction has new output + * @param {WebhookEventType[]} [options.webhook_events_filter] - You can change which events trigger webhook requests by specifying webhook events (`start`|`output`|`logs`|`completed`) + * @param {AbortSignal} [options.signal] - AbortSignal to cancel the prediction + * @param {Function} [progress] - Callback function that receives the prediction object as it's updated. The function is called when the prediction is created, each time its updated while polling for completion, and when it's completed. + * @throws {Error} If the reference is invalid + * @throws {Error} If the prediction failed + * @returns {Promise} - Resolves with the output of running the model + */ + async run(ref, options, progress) { + const { wait, ...data } = options; + + const identifier = ModelVersionIdentifier.parse(ref); + + let prediction; + if (identifier.version) { + prediction = await this.predictions.create({ + ...data, + version: identifier.version, + }); + } else if (identifier.owner && identifier.name) { + prediction = await this.predictions.create({ + ...data, + model: `${identifier.owner}/${identifier.name}`, + }); + } else { + throw new Error("Invalid model version identifier"); + } + + // Call progress callback with the initial prediction object + if (progress) { + progress(prediction); + } + + const { signal } = options; + + prediction = await this.wait( + prediction, + wait || {}, + async (updatedPrediction) => { + // Call progress callback with the updated prediction object + if (progress) { + progress(updatedPrediction); + } + + if (signal && signal.aborted) { + await this.predictions.cancel(updatedPrediction.id); + return true; // stop polling + } + + return false; // continue polling + } + ); + + // Call progress callback with the completed prediction object + if (progress) { + progress(prediction); + } + + if (prediction.status === "failed") { + throw new Error(`Prediction failed: ${prediction.error}`); + } + + return prediction.output; + } + + /** + * Make a request to the Replicate API. + * + * @param {string} route - REST API endpoint path + * @param {object} options - Request parameters + * @param {string} [options.method] - HTTP method. Defaults to GET + * @param {object} [options.params] - Query parameters + * @param {object|Headers} [options.headers] - HTTP headers + * @param {object} [options.data] - Body parameters + * @returns {Promise} - Resolves with the response object + * @throws {ApiError} If the request failed + */ + async request(route, options) { + const { auth, baseUrl, userAgent } = this; + + let url; + if (route instanceof URL) { + url = route; + } else { + url = new URL( + route.startsWith("/") ? route.slice(1) : route, + baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/` + ); + } + + const { method = "GET", params = {}, data } = options; + + for (const [key, value] of Object.entries(params)) { + url.searchParams.append(key, value); + } + + const headers = {}; + if (auth) { + headers["Authorization"] = `Token ${auth}`; + } + headers["Content-Type"] = "application/json"; + headers["User-Agent"] = userAgent; + if (options.headers) { + for (const [key, value] of Object.entries(options.headers)) { + headers[key] = value; + } + } + + const init = { + method, + headers, + body: data ? JSON.stringify(data) : undefined, + }; + + const shouldRetry = + method === "GET" + ? (response) => response.status === 429 || response.status >= 500 + : (response) => response.status === 429; + + // Workaround to fix `TypeError: Illegal invocation` error in Cloudflare Workers + // https://github.com/replicate/replicate-javascript/issues/134 + const _fetch = this.fetch; // eslint-disable-line no-underscore-dangle + const response = await withAutomaticRetries(async () => _fetch(url, init), { + shouldRetry, + }); + + if (!response.ok) { + const request = new Request(url, init); + const responseText = await response.text(); + throw new ApiError( + `Request to ${url} failed with status ${response.status} ${response.statusText}: ${responseText}.`, + request, + response + ); + } + + return response; + } + + /** + * Stream a model and wait for its output. + * + * @param {string} ref - Required. The model version identifier in the format "{owner}/{name}:{version}" + * @param {object} options + * @param {object} options.input - Required. An object with the model inputs + * @param {string} [options.webhook] - An HTTPS URL for receiving a webhook when the prediction has new output + * @param {string[]} [options.webhook_events_filter] - You can change which events trigger webhook requests by specifying webhook events (`start`|`output`|`logs`|`completed`) + * @param {AbortSignal} [options.signal] - AbortSignal to cancel the prediction + * @throws {Error} If the prediction failed + * @yields {ServerSentEvent} Each streamed event from the prediction + */ + async *stream(ref, options) { + const { wait, ...data } = options; + + const identifier = ModelVersionIdentifier.parse(ref); + + let prediction; + if (identifier.version) { + prediction = await this.predictions.create({ + ...data, + version: identifier.version, + stream: true, + }); + } else if (identifier.owner && identifier.name) { + prediction = await this.predictions.create({ + ...data, + model: `${identifier.owner}/${identifier.name}`, + stream: true, + }); + } else { + throw new Error("Invalid model version identifier"); + } + + if (prediction.urls && prediction.urls.stream) { + const { signal } = options; + const stream = new Stream(prediction.urls.stream, { signal }); + yield* stream; + } else { + throw new Error("Prediction does not support streaming"); + } + } + + /** + * Paginate through a list of results. + * + * @generator + * @example + * for await (const page of replicate.paginate(replicate.predictions.list) { + * console.log(page); + * } + * @template T + * @param {() => Promise>} endpoint - Function that returns a promise for the next page of results + * @yields {T[]} Each page of results + * @returns {AsyncGenerator} + */ + async *paginate(endpoint) { + const response = await endpoint(); + yield response.results; + if (response.next) { + const nextPage = () => + this.request(response.next, { method: "GET" }).then((r) => r.json()); + yield* this.paginate(nextPage); + } + } + + /** + * Wait for a prediction to finish. + * + * If the prediction has already finished, + * this function returns immediately. + * Otherwise, it polls the API until the prediction finishes. + * + * @async + * @param {object} prediction - Prediction object + * @param {object} options - Options + * @param {number} [options.interval] - Polling interval in milliseconds. Defaults to 500 + * @param {Function} [stop] - Async callback function that is called after each polling attempt. Receives the prediction object as an argument. Return false to cancel polling. + * @throws {Error} If the prediction doesn't complete within the maximum number of attempts + * @throws {Error} If the prediction failed + * @returns {Promise} Resolves with the completed prediction object + */ + async wait(prediction, options, stop) { + const { id } = prediction; + if (!id) { + throw new Error("Invalid prediction"); + } + + if ( + prediction.status === "succeeded" || + prediction.status === "failed" || + prediction.status === "canceled" + ) { + return prediction; + } + + // eslint-disable-next-line no-promise-executor-return + const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); + + const interval = (options && options.interval) || 500; + + let updatedPrediction = await this.predictions.get(id); + + while ( + updatedPrediction.status !== "succeeded" && + updatedPrediction.status !== "failed" && + updatedPrediction.status !== "canceled" + ) { + /* eslint-disable no-await-in-loop */ + if (stop && (await stop(updatedPrediction)) === true) { + break; + } + + await sleep(interval); + updatedPrediction = await this.predictions.get(prediction.id); + /* eslint-enable no-await-in-loop */ + } + + if (updatedPrediction.status === "failed") { + throw new Error(`Prediction failed: ${updatedPrediction.error}`); + } + + return updatedPrediction; + } +}; diff --git a/lib/stream.js b/lib/stream.js index 012d6d0..8237a72 100644 --- a/lib/stream.js +++ b/lib/stream.js @@ -1,4 +1,5 @@ // Attempt to use readable-stream if available, attempt to use the built-in stream module. +/** @type {import("stream").Readable} */ let Readable; try { Readable = require("readable-stream").Readable; @@ -13,7 +14,7 @@ try { /** * A server-sent event. */ -class ServerSentEvent { +export class ServerSentEvent { /** * Create a new server-sent event. * @@ -44,12 +45,12 @@ class ServerSentEvent { /** * A stream of server-sent events. */ -class Stream extends Readable { +export class Stream extends Readable { /** * Create a new stream of server-sent events. * * @param {string} url The URL to connect to. - * @param {object} options The fetch options. + * @param {RequestInit=} options The fetch options. */ constructor(url, options) { if (!Readable) { @@ -63,11 +64,18 @@ class Stream extends Readable { this.options = options; this.event = null; + + /** @type {unknown[]} */ this.data = []; + + /** @type {string | null} */ this.lastEventId = null; + + /** @type {number | null} */ this.retry = null; } + /** @param {string=} line */ decode(line) { if (!line) { if (!this.event && !this.data.length && !this.lastEventId) { @@ -132,8 +140,3 @@ class Stream extends Readable { } } } - -module.exports = { - Stream, - ServerSentEvent, -}; diff --git a/lib/trainings.js b/lib/trainings.js index 6b13dca..1de27ec 100644 --- a/lib/trainings.js +++ b/lib/trainings.js @@ -1,3 +1,9 @@ +/** + * @template T + * @typedef {import("./types").Page} Page + */ +/** @typedef {import("./types").Training} Training */ + /** * Create a new training * @@ -6,10 +12,10 @@ * @param {string} version_id - Required. The version ID * @param {object} options * @param {string} options.destination - Required. The destination for the trained version in the form "{username}/{model_name}" - * @param {object} options.input - Required. An object with the model inputs + * @param {unknown} options.input - Required. An object with the model inputs * @param {string} [options.webhook] - An HTTPS URL for receiving a webhook when the training updates * @param {string[]} [options.webhook_events_filter] - You can change which events trigger webhook requests by specifying webhook events (`start`|`output`|`logs`|`completed`) - * @returns {Promise} Resolves with the data for the created training + * @returns {Promise} Resolves with the data for the created training */ async function createTraining(model_owner, model_name, version_id, options) { const { ...data } = options; @@ -38,7 +44,7 @@ async function createTraining(model_owner, model_name, version_id, options) { * Fetch a training by ID * * @param {string} training_id - Required. The training ID - * @returns {Promise} Resolves with the data for the training + * @returns {Promise} Resolves with the data for the training */ async function getTraining(training_id) { const response = await this.request(`/trainings/${training_id}`, { @@ -52,7 +58,7 @@ async function getTraining(training_id) { * Cancel a training by ID * * @param {string} training_id - Required. The training ID - * @returns {Promise} Resolves with the data for the training + * @returns {Promise} Resolves with the data for the training */ async function cancelTraining(training_id) { const response = await this.request(`/trainings/${training_id}/cancel`, { @@ -65,7 +71,7 @@ async function cancelTraining(training_id) { /** * List all trainings * - * @returns {Promise} - Resolves with a page of trainings + * @returns {Promise>} - Resolves with a page of trainings */ async function listTrainings() { const response = await this.request("/trainings", { @@ -75,9 +81,7 @@ async function listTrainings() { return response.json(); } -module.exports = { - create: createTraining, - get: getTraining, - cancel: cancelTraining, - list: listTrainings, -}; +export const create = createTraining; +export const get = getTraining; +export const cancel = cancelTraining; +export const list = listTrainings; diff --git a/lib/types.js b/lib/types.js new file mode 100644 index 0000000..6c91545 --- /dev/null +++ b/lib/types.js @@ -0,0 +1,70 @@ +/** + * @typedef {"starting" | "processing" | "succeeded" | "failed" | "canceled"} Status + * @typedef {"public" | "private"} Visibility + * @typedef {"start" | "output" | "logs" | "completed"} WebhookEventType + * + * @typedef {Object} Collection + * @property {string} name + * @property {string} slug + * @property {string} description + * @property {Model[]=} models + * + * @typedef {Object} Hardware + * @property {string} sku + * @property {string} name + * + * @typedef {Object} Model + * @property {string} url + * @property {string} owner + * @property {string} name + * @property {string=} description + * @property {Visibility} visibility + * @property {string=} github_url + * @property {string=} paper_url + * @property {string=} license_url + * @property {number} run_count + * @property {string=} cover_image_url + * @property {Prediction=} default_example + * @property {ModelVersion=} latest_version + * + * @typedef {Object} ModelVersion + * @property {string} id + * @property {string} created_at + * @property {string} cog_version + * @property {string} openapi_schema + * + * @typedef {Object} Prediction + * @property {string} id + * @property {Status} status + * @property {string=} model + * @property {string} version + * @property {object} input + * @property {unknown=} output + * @property {"api" | "web"} source + * @property {unknown=} error + * @property {string=} logs + * @property {{predict_time?: number}=} metrics + * @property {string=} webhook + * @property {WebhookEventType[]=} webhook_events_filter + * @property {string} created_at + * @property {string=} started_at + * @property {string=} completed_at + * @property {{get: string; cancel: string; stream?: string}} urls + * + * @typedef {Prediction} Training + * + * @typedef {Object} ServerSentEvent + * @property {string} event + * @property {string} data + * @property {string=} id + * @property {number=} retry + */ + +/** + * @template T + * @typedef {Object} Page + * @property {string=} previous + * @property {string=} next + * @property {T[]} results + */ +export default {}; diff --git a/lib/util.js b/lib/util.js index 7b12633..c11249d 100644 --- a/lib/util.js +++ b/lib/util.js @@ -1,4 +1,4 @@ -const ApiError = require("./error"); +import ApiError from "./error.js"; /** * Automatically retry a request if it fails with an appropriate status code. @@ -19,7 +19,7 @@ const ApiError = require("./error"); * @returns {Promise} - Resolves with the response object * @throws {ApiError} If the request failed */ -async function withAutomaticRetries(request, options = {}) { +export async function withAutomaticRetries(request, options = {}) { const shouldRetry = options.shouldRetry || (() => false); const maxRetries = options.maxRetries || 5; const interval = options.interval || 500; @@ -67,5 +67,3 @@ async function withAutomaticRetries(request, options = {}) { return request(); } - -module.exports = { withAutomaticRetries }; diff --git a/package-lock.json b/package-lock.json index 19133da..e13d03c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,12 +10,12 @@ "license": "Apache-2.0", "devDependencies": { "@biomejs/biome": "^1.4.1", - "@types/jest": "^29.5.3", + "@types/jest": "^29.5.11", "@typescript-eslint/eslint-plugin": "^5.56.0", "cross-fetch": "^3.1.5", - "jest": "^29.6.2", + "jest": "^29.7.0", "nock": "^13.3.0", - "ts-jest": "^29.1.0", + "ts-jest": "^29.1.1", "typescript": "^5.0.2" }, "engines": { @@ -533,9 +533,9 @@ } }, "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.22.5.tgz", - "integrity": "sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.23.3.tgz", + "integrity": "sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" @@ -635,9 +635,9 @@ } }, "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.22.5.tgz", - "integrity": "sha512-1mS2o03i7t1c6VzH6fdQ3OA8tcEIxwG18zIPRp+UY1Ihv6W+XZzBCVxExF9upussPXJ0xE9XRHwMoNs1ep/nRQ==", + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.23.3.tgz", + "integrity": "sha512-9EiNjVJOMwCO+43TqoTrgQ8jMwcAd0sWyXi9RPfIsLTj4R2MADDDQXELhffaUx/uJv2AYcxBgPwH6j4TIA4ytQ==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" @@ -1037,16 +1037,16 @@ } }, "node_modules/@jest/console": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.6.2.tgz", - "integrity": "sha512-0N0yZof5hi44HAR2pPS+ikJ3nzKNoZdVu8FffRf3wy47I7Dm7etk/3KetMdRUqzVd16V4O2m2ISpNTbnIuqy1w==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", + "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", "dev": true, "dependencies": { - "@jest/types": "^29.6.1", + "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", - "jest-message-util": "^29.6.2", - "jest-util": "^29.6.2", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", "slash": "^3.0.0" }, "engines": { @@ -1054,37 +1054,37 @@ } }, "node_modules/@jest/core": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.6.2.tgz", - "integrity": "sha512-Oj+5B+sDMiMWLhPFF+4/DvHOf+U10rgvCLGPHP8Xlsy/7QxS51aU/eBngudHlJXnaWD5EohAgJ4js+T6pa+zOg==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", + "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", "dev": true, "dependencies": { - "@jest/console": "^29.6.2", - "@jest/reporters": "^29.6.2", - "@jest/test-result": "^29.6.2", - "@jest/transform": "^29.6.2", - "@jest/types": "^29.6.1", + "@jest/console": "^29.7.0", + "@jest/reporters": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", "@types/node": "*", "ansi-escapes": "^4.2.1", "chalk": "^4.0.0", "ci-info": "^3.2.0", "exit": "^0.1.2", "graceful-fs": "^4.2.9", - "jest-changed-files": "^29.5.0", - "jest-config": "^29.6.2", - "jest-haste-map": "^29.6.2", - "jest-message-util": "^29.6.2", - "jest-regex-util": "^29.4.3", - "jest-resolve": "^29.6.2", - "jest-resolve-dependencies": "^29.6.2", - "jest-runner": "^29.6.2", - "jest-runtime": "^29.6.2", - "jest-snapshot": "^29.6.2", - "jest-util": "^29.6.2", - "jest-validate": "^29.6.2", - "jest-watcher": "^29.6.2", + "jest-changed-files": "^29.7.0", + "jest-config": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-resolve-dependencies": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "jest-watcher": "^29.7.0", "micromatch": "^4.0.4", - "pretty-format": "^29.6.2", + "pretty-format": "^29.7.0", "slash": "^3.0.0", "strip-ansi": "^6.0.0" }, @@ -1101,88 +1101,88 @@ } }, "node_modules/@jest/environment": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.6.2.tgz", - "integrity": "sha512-AEcW43C7huGd/vogTddNNTDRpO6vQ2zaQNrttvWV18ArBx9Z56h7BIsXkNFJVOO4/kblWEQz30ckw0+L3izc+Q==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", + "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", "dev": true, "dependencies": { - "@jest/fake-timers": "^29.6.2", - "@jest/types": "^29.6.1", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", "@types/node": "*", - "jest-mock": "^29.6.2" + "jest-mock": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jest/expect": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.6.2.tgz", - "integrity": "sha512-m6DrEJxVKjkELTVAztTLyS/7C92Y2b0VYqmDROYKLLALHn8T/04yPs70NADUYPrV3ruI+H3J0iUIuhkjp7vkfg==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", "dev": true, "dependencies": { - "expect": "^29.6.2", - "jest-snapshot": "^29.6.2" + "expect": "^29.7.0", + "jest-snapshot": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jest/expect-utils": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.6.2.tgz", - "integrity": "sha512-6zIhM8go3RV2IG4aIZaZbxwpOzz3ZiM23oxAlkquOIole+G6TrbeXnykxWYlqF7kz2HlBjdKtca20x9atkEQYg==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", + "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", "dev": true, "dependencies": { - "jest-get-type": "^29.4.3" + "jest-get-type": "^29.6.3" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jest/fake-timers": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.6.2.tgz", - "integrity": "sha512-euZDmIlWjm1Z0lJ1D0f7a0/y5Kh/koLFMUBE5SUYWrmy8oNhJpbTBDAP6CxKnadcMLDoDf4waRYCe35cH6G6PA==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", + "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", "dev": true, "dependencies": { - "@jest/types": "^29.6.1", + "@jest/types": "^29.6.3", "@sinonjs/fake-timers": "^10.0.2", "@types/node": "*", - "jest-message-util": "^29.6.2", - "jest-mock": "^29.6.2", - "jest-util": "^29.6.2" + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jest/globals": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.6.2.tgz", - "integrity": "sha512-cjuJmNDjs6aMijCmSa1g2TNG4Lby/AeU7/02VtpW+SLcZXzOLK2GpN2nLqcFjmhy3B3AoPeQVx7BnyOf681bAw==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", + "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", "dev": true, "dependencies": { - "@jest/environment": "^29.6.2", - "@jest/expect": "^29.6.2", - "@jest/types": "^29.6.1", - "jest-mock": "^29.6.2" + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/types": "^29.6.3", + "jest-mock": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jest/reporters": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.6.2.tgz", - "integrity": "sha512-sWtijrvIav8LgfJZlrGCdN0nP2EWbakglJY49J1Y5QihcQLfy7ovyxxjJBRXMNltgt4uPtEcFmIMbVshEDfFWw==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", + "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", "dev": true, "dependencies": { "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^29.6.2", - "@jest/test-result": "^29.6.2", - "@jest/transform": "^29.6.2", - "@jest/types": "^29.6.1", + "@jest/console": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", "@jridgewell/trace-mapping": "^0.3.18", "@types/node": "*", "chalk": "^4.0.0", @@ -1191,13 +1191,13 @@ "glob": "^7.1.3", "graceful-fs": "^4.2.9", "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^5.1.0", + "istanbul-lib-instrument": "^6.0.0", "istanbul-lib-report": "^3.0.0", "istanbul-lib-source-maps": "^4.0.0", "istanbul-reports": "^3.1.3", - "jest-message-util": "^29.6.2", - "jest-util": "^29.6.2", - "jest-worker": "^29.6.2", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", "slash": "^3.0.0", "string-length": "^4.0.1", "strip-ansi": "^6.0.0", @@ -1215,10 +1215,26 @@ } } }, + "node_modules/@jest/reporters/node_modules/istanbul-lib-instrument": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.1.tgz", + "integrity": "sha512-EAMEJBsYuyyztxMxW3g7ugGPkrZsV57v0Hmv3mm1uQsmB+QnZuepg731CRaIgeUVSdmsTngOkSnauNF8p7FIhA==", + "dev": true, + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/@jest/schemas": { - "version": "29.6.0", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.0.tgz", - "integrity": "sha512-rxLjXyJBTL4LQeJW3aKo0M/+GkCOXsO+8i9Iu7eDb6KwtP65ayoDsitrdPBtujxQ88k4wI2FNYfa6TOGwSn6cQ==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", "dev": true, "dependencies": { "@sinclair/typebox": "^0.27.8" @@ -1228,9 +1244,9 @@ } }, "node_modules/@jest/source-map": { - "version": "29.6.0", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.0.tgz", - "integrity": "sha512-oA+I2SHHQGxDCZpbrsCQSoMLb3Bz547JnM+jUr9qEbuw0vQlWZfpPS7CO9J7XiwKicEz9OFn/IYoLkkiUD7bzA==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", + "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", "dev": true, "dependencies": { "@jridgewell/trace-mapping": "^0.3.18", @@ -1242,13 +1258,13 @@ } }, "node_modules/@jest/test-result": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.6.2.tgz", - "integrity": "sha512-3VKFXzcV42EYhMCsJQURptSqnyjqCGbtLuX5Xxb6Pm6gUf1wIRIl+mandIRGJyWKgNKYF9cnstti6Ls5ekduqw==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", + "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", "dev": true, "dependencies": { - "@jest/console": "^29.6.2", - "@jest/types": "^29.6.1", + "@jest/console": "^29.7.0", + "@jest/types": "^29.6.3", "@types/istanbul-lib-coverage": "^2.0.0", "collect-v8-coverage": "^1.0.0" }, @@ -1257,14 +1273,14 @@ } }, "node_modules/@jest/test-sequencer": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.6.2.tgz", - "integrity": "sha512-GVYi6PfPwVejO7slw6IDO0qKVum5jtrJ3KoLGbgBWyr2qr4GaxFV6su+ZAjdTX75Sr1DkMFRk09r2ZVa+wtCGw==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", + "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", "dev": true, "dependencies": { - "@jest/test-result": "^29.6.2", + "@jest/test-result": "^29.7.0", "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.6.2", + "jest-haste-map": "^29.7.0", "slash": "^3.0.0" }, "engines": { @@ -1272,22 +1288,22 @@ } }, "node_modules/@jest/transform": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.6.2.tgz", - "integrity": "sha512-ZqCqEISr58Ce3U+buNFJYUktLJZOggfyvR+bZMaiV1e8B1SIvJbwZMrYz3gx/KAPn9EXmOmN+uB08yLCjWkQQg==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", + "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", "dev": true, "dependencies": { "@babel/core": "^7.11.6", - "@jest/types": "^29.6.1", + "@jest/types": "^29.6.3", "@jridgewell/trace-mapping": "^0.3.18", "babel-plugin-istanbul": "^6.1.1", "chalk": "^4.0.0", "convert-source-map": "^2.0.0", "fast-json-stable-stringify": "^2.1.0", "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.6.2", - "jest-regex-util": "^29.4.3", - "jest-util": "^29.6.2", + "jest-haste-map": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", "micromatch": "^4.0.4", "pirates": "^4.0.4", "slash": "^3.0.0", @@ -1298,12 +1314,12 @@ } }, "node_modules/@jest/types": { - "version": "29.6.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.1.tgz", - "integrity": "sha512-tPKQNMPuXgvdOn2/Lg9HNfUvjYVGolt04Hp03f5hAk878uwOLikN+JzeLY0HcVgKgFl9Hs3EIqpu3WX27XNhnw==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", "dev": true, "dependencies": { - "@jest/schemas": "^29.6.0", + "@jest/schemas": "^29.6.3", "@types/istanbul-lib-coverage": "^2.0.0", "@types/istanbul-reports": "^3.0.0", "@types/node": "*", @@ -1421,9 +1437,9 @@ } }, "node_modules/@types/babel__core": { - "version": "7.20.0", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.0.tgz", - "integrity": "sha512-+n8dL/9GWblDO0iU6eZAwEIJVr5DWigtle+Q6HLOrh/pdbXOhOtqzq8VPPE2zvNJzSKY4vH/z3iT3tn0A3ypiQ==", + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", "dev": true, "dependencies": { "@babel/parser": "^7.20.7", @@ -1434,18 +1450,18 @@ } }, "node_modules/@types/babel__generator": { - "version": "7.6.4", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz", - "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==", + "version": "7.6.8", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", + "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", "dev": true, "dependencies": { "@babel/types": "^7.0.0" } }, "node_modules/@types/babel__template": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", - "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", "dev": true, "dependencies": { "@babel/parser": "^7.1.0", @@ -1453,18 +1469,18 @@ } }, "node_modules/@types/babel__traverse": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.18.3.tgz", - "integrity": "sha512-1kbcJ40lLB7MHsj39U4Sh1uTd2E7rLEa79kmDpI6cy+XiXsteB3POdQomoq4FxszMrO3ZYchkhYJw7A2862b3w==", + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.5.tgz", + "integrity": "sha512-WXCyOcRtH37HAUkpXhUduaxdm82b4GSlyTqajXviN4EfiuPgNYR109xMCKvpl6zPIpua0DGlMEDCq+g8EdoheQ==", "dev": true, "dependencies": { - "@babel/types": "^7.3.0" + "@babel/types": "^7.20.7" } }, "node_modules/@types/graceful-fs": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.6.tgz", - "integrity": "sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw==", + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", + "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", "dev": true, "dependencies": { "@types/node": "*" @@ -1495,9 +1511,9 @@ } }, "node_modules/@types/jest": { - "version": "29.5.3", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.3.tgz", - "integrity": "sha512-1Nq7YrO/vJE/FYnqYyw0FS8LdrjExSgIiHyKg7xPpn+yi8Q4huZryKnkJatN1ZRH89Kw2v33/8ZMB7DuZeSLlA==", + "version": "29.5.11", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.11.tgz", + "integrity": "sha512-S2mHmYIVe13vrm6q4kN6fLYYAka15ALQki/vgDC3mIukEOx8WJlv0kQPM+d4w8Gp6u0uSdKND04IlTXBv0rwnQ==", "dev": true, "dependencies": { "expect": "^29.0.0", @@ -1865,15 +1881,15 @@ } }, "node_modules/babel-jest": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.6.2.tgz", - "integrity": "sha512-BYCzImLos6J3BH/+HvUCHG1dTf2MzmAB4jaVxHV+29RZLjR29XuYTmsf2sdDwkrb+FczkGo3kOhE7ga6sI0P4A==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", + "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", "dev": true, "dependencies": { - "@jest/transform": "^29.6.2", + "@jest/transform": "^29.7.0", "@types/babel__core": "^7.1.14", "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^29.5.0", + "babel-preset-jest": "^29.6.3", "chalk": "^4.0.0", "graceful-fs": "^4.2.9", "slash": "^3.0.0" @@ -1902,9 +1918,9 @@ } }, "node_modules/babel-plugin-jest-hoist": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.5.0.tgz", - "integrity": "sha512-zSuuuAlTMT4mzLj2nPnUm6fsE6270vdOfnpbJ+RmruU75UhLFvL0N2NgI7xpeS7NaB6hGqmd5pVpGTDYvi4Q3w==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", + "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", "dev": true, "dependencies": { "@babel/template": "^7.3.3", @@ -1940,12 +1956,12 @@ } }, "node_modules/babel-preset-jest": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.5.0.tgz", - "integrity": "sha512-JOMloxOqdiBSxMAzjRaH023/vvcaSaec49zvg+2LmNsktC7ei39LTJGw02J+9uUtTZUq6xbLyJ4dxe9sSmIuAg==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", + "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", "dev": true, "dependencies": { - "babel-plugin-jest-hoist": "^29.5.0", + "babel-plugin-jest-hoist": "^29.6.3", "babel-preset-current-node-syntax": "^1.0.0" }, "engines": { @@ -2222,6 +2238,27 @@ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", "dev": true }, + "node_modules/create-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", + "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "prompts": "^2.0.1" + }, + "bin": { + "create-jest": "bin/create-jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, "node_modules/cross-fetch": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.5.tgz", @@ -2302,9 +2339,9 @@ } }, "node_modules/diff-sequences": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.4.3.tgz", - "integrity": "sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", "dev": true, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -2639,17 +2676,16 @@ } }, "node_modules/expect": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/expect/-/expect-29.6.2.tgz", - "integrity": "sha512-iAErsLxJ8C+S02QbLAwgSGSezLQK+XXRDt8IuFXFpwCNw2ECmzZSmjKcCaFVp5VRMk+WAvz6h6jokzEzBFZEuA==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", "dev": true, "dependencies": { - "@jest/expect-utils": "^29.6.2", - "@types/node": "*", - "jest-get-type": "^29.4.3", - "jest-matcher-utils": "^29.6.2", - "jest-message-util": "^29.6.2", - "jest-util": "^29.6.2" + "@jest/expect-utils": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -2791,9 +2827,9 @@ "dev": true }, "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "dev": true, "hasInstallScript": true, "optional": true, @@ -2805,10 +2841,13 @@ } }, "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/gensync": { "version": "1.0.0-beta.2", @@ -2930,18 +2969,6 @@ "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", "dev": true }, - "node_modules/has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.1" - }, - "engines": { - "node": ">= 0.4.0" - } - }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -2951,6 +2978,18 @@ "node": ">=8" } }, + "node_modules/hasown": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", + "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/html-escaper": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", @@ -3063,12 +3102,12 @@ "dev": true }, "node_modules/is-core-module": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", - "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", "dev": true, "dependencies": { - "has": "^1.0.3" + "hasown": "^2.0.0" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -3226,15 +3265,15 @@ } }, "node_modules/jest": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest/-/jest-29.6.2.tgz", - "integrity": "sha512-8eQg2mqFbaP7CwfsTpCxQ+sHzw1WuNWL5UUvjnWP4hx2riGz9fPSzYOaU5q8/GqWn1TfgZIVTqYJygbGbWAANg==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", + "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", "dev": true, "dependencies": { - "@jest/core": "^29.6.2", - "@jest/types": "^29.6.1", + "@jest/core": "^29.7.0", + "@jest/types": "^29.6.3", "import-local": "^3.0.2", - "jest-cli": "^29.6.2" + "jest-cli": "^29.7.0" }, "bin": { "jest": "bin/jest.js" @@ -3252,12 +3291,13 @@ } }, "node_modules/jest-changed-files": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.5.0.tgz", - "integrity": "sha512-IFG34IUMUaNBIxjQXF/iu7g6EcdMrGRRxaUSw92I/2g2YC6vCdTltl4nHvt7Ci5nSJwXIkCu8Ka1DKF+X7Z1Ag==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", + "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", "dev": true, "dependencies": { "execa": "^5.0.0", + "jest-util": "^29.7.0", "p-limit": "^3.1.0" }, "engines": { @@ -3265,28 +3305,28 @@ } }, "node_modules/jest-circus": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.6.2.tgz", - "integrity": "sha512-G9mN+KOYIUe2sB9kpJkO9Bk18J4dTDArNFPwoZ7WKHKel55eKIS/u2bLthxgojwlf9NLCVQfgzM/WsOVvoC6Fw==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", + "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", "dev": true, "dependencies": { - "@jest/environment": "^29.6.2", - "@jest/expect": "^29.6.2", - "@jest/test-result": "^29.6.2", - "@jest/types": "^29.6.1", + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "co": "^4.6.0", "dedent": "^1.0.0", "is-generator-fn": "^2.0.0", - "jest-each": "^29.6.2", - "jest-matcher-utils": "^29.6.2", - "jest-message-util": "^29.6.2", - "jest-runtime": "^29.6.2", - "jest-snapshot": "^29.6.2", - "jest-util": "^29.6.2", + "jest-each": "^29.7.0", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", "p-limit": "^3.1.0", - "pretty-format": "^29.6.2", + "pretty-format": "^29.7.0", "pure-rand": "^6.0.0", "slash": "^3.0.0", "stack-utils": "^2.0.3" @@ -3296,22 +3336,21 @@ } }, "node_modules/jest-cli": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.6.2.tgz", - "integrity": "sha512-TT6O247v6dCEX2UGHGyflMpxhnrL0DNqP2fRTKYm3nJJpCTfXX3GCMQPGFjXDoj0i5/Blp3jriKXFgdfmbYB6Q==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", + "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", "dev": true, "dependencies": { - "@jest/core": "^29.6.2", - "@jest/test-result": "^29.6.2", - "@jest/types": "^29.6.1", + "@jest/core": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", "chalk": "^4.0.0", + "create-jest": "^29.7.0", "exit": "^0.1.2", - "graceful-fs": "^4.2.9", "import-local": "^3.0.2", - "jest-config": "^29.6.2", - "jest-util": "^29.6.2", - "jest-validate": "^29.6.2", - "prompts": "^2.0.1", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", "yargs": "^17.3.1" }, "bin": { @@ -3330,31 +3369,31 @@ } }, "node_modules/jest-config": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.6.2.tgz", - "integrity": "sha512-VxwFOC8gkiJbuodG9CPtMRjBUNZEHxwfQXmIudSTzFWxaci3Qub1ddTRbFNQlD/zUeaifLndh/eDccFX4wCMQw==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", + "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", "dev": true, "dependencies": { "@babel/core": "^7.11.6", - "@jest/test-sequencer": "^29.6.2", - "@jest/types": "^29.6.1", - "babel-jest": "^29.6.2", + "@jest/test-sequencer": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-jest": "^29.7.0", "chalk": "^4.0.0", "ci-info": "^3.2.0", "deepmerge": "^4.2.2", "glob": "^7.1.3", "graceful-fs": "^4.2.9", - "jest-circus": "^29.6.2", - "jest-environment-node": "^29.6.2", - "jest-get-type": "^29.4.3", - "jest-regex-util": "^29.4.3", - "jest-resolve": "^29.6.2", - "jest-runner": "^29.6.2", - "jest-util": "^29.6.2", - "jest-validate": "^29.6.2", + "jest-circus": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", "micromatch": "^4.0.4", "parse-json": "^5.2.0", - "pretty-format": "^29.6.2", + "pretty-format": "^29.7.0", "slash": "^3.0.0", "strip-json-comments": "^3.1.1" }, @@ -3375,24 +3414,24 @@ } }, "node_modules/jest-diff": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.6.2.tgz", - "integrity": "sha512-t+ST7CB9GX5F2xKwhwCf0TAR17uNDiaPTZnVymP9lw0lssa9vG+AFyDZoeIHStU3WowFFwT+ky+er0WVl2yGhA==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", + "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", "dev": true, "dependencies": { "chalk": "^4.0.0", - "diff-sequences": "^29.4.3", - "jest-get-type": "^29.4.3", - "pretty-format": "^29.6.2" + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-docblock": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.4.3.tgz", - "integrity": "sha512-fzdTftThczeSD9nZ3fzA/4KkHtnmllawWrXO69vtI+L9WjEIuXWs4AmyME7lN5hU7dB0sHhuPfcKofRsUb/2Fg==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", + "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", "dev": true, "dependencies": { "detect-newline": "^3.0.0" @@ -3402,62 +3441,62 @@ } }, "node_modules/jest-each": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.6.2.tgz", - "integrity": "sha512-MsrsqA0Ia99cIpABBc3izS1ZYoYfhIy0NNWqPSE0YXbQjwchyt6B1HD2khzyPe1WiJA7hbxXy77ZoUQxn8UlSw==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", + "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", "dev": true, "dependencies": { - "@jest/types": "^29.6.1", + "@jest/types": "^29.6.3", "chalk": "^4.0.0", - "jest-get-type": "^29.4.3", - "jest-util": "^29.6.2", - "pretty-format": "^29.6.2" + "jest-get-type": "^29.6.3", + "jest-util": "^29.7.0", + "pretty-format": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-environment-node": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.6.2.tgz", - "integrity": "sha512-YGdFeZ3T9a+/612c5mTQIllvWkddPbYcN2v95ZH24oWMbGA4GGS2XdIF92QMhUhvrjjuQWYgUGW2zawOyH63MQ==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", + "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", "dev": true, "dependencies": { - "@jest/environment": "^29.6.2", - "@jest/fake-timers": "^29.6.2", - "@jest/types": "^29.6.1", + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", "@types/node": "*", - "jest-mock": "^29.6.2", - "jest-util": "^29.6.2" + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-get-type": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.4.3.tgz", - "integrity": "sha512-J5Xez4nRRMjk8emnTpWrlkyb9pfRQQanDrvWHhsR1+VUfbwxi30eVcZFlcdGInRibU4G5LwHXpI7IRHU0CY+gg==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", "dev": true, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-haste-map": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.6.2.tgz", - "integrity": "sha512-+51XleTDAAysvU8rT6AnS1ZJ+WHVNqhj1k6nTvN2PYP+HjU3kqlaKQ1Lnw3NYW3bm2r8vq82X0Z1nDDHZMzHVA==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", + "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", "dev": true, "dependencies": { - "@jest/types": "^29.6.1", + "@jest/types": "^29.6.3", "@types/graceful-fs": "^4.1.3", "@types/node": "*", "anymatch": "^3.0.3", "fb-watchman": "^2.0.0", "graceful-fs": "^4.2.9", - "jest-regex-util": "^29.4.3", - "jest-util": "^29.6.2", - "jest-worker": "^29.6.2", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", "micromatch": "^4.0.4", "walker": "^1.0.8" }, @@ -3469,46 +3508,46 @@ } }, "node_modules/jest-leak-detector": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.6.2.tgz", - "integrity": "sha512-aNqYhfp5uYEO3tdWMb2bfWv6f0b4I0LOxVRpnRLAeque2uqOVVMLh6khnTcE2qJ5wAKop0HcreM1btoysD6bPQ==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", + "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", "dev": true, "dependencies": { - "jest-get-type": "^29.4.3", - "pretty-format": "^29.6.2" + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-matcher-utils": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.6.2.tgz", - "integrity": "sha512-4LiAk3hSSobtomeIAzFTe+N8kL6z0JtF3n6I4fg29iIW7tt99R7ZcIFW34QkX+DuVrf+CUe6wuVOpm7ZKFJzZQ==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", + "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", "dev": true, "dependencies": { "chalk": "^4.0.0", - "jest-diff": "^29.6.2", - "jest-get-type": "^29.4.3", - "pretty-format": "^29.6.2" + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-message-util": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.6.2.tgz", - "integrity": "sha512-vnIGYEjoPSuRqV8W9t+Wow95SDp6KPX2Uf7EoeG9G99J2OVh7OSwpS4B6J0NfpEIpfkBNHlBZpA2rblEuEFhZQ==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", + "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", "dev": true, "dependencies": { "@babel/code-frame": "^7.12.13", - "@jest/types": "^29.6.1", + "@jest/types": "^29.6.3", "@types/stack-utils": "^2.0.0", "chalk": "^4.0.0", "graceful-fs": "^4.2.9", "micromatch": "^4.0.4", - "pretty-format": "^29.6.2", + "pretty-format": "^29.7.0", "slash": "^3.0.0", "stack-utils": "^2.0.3" }, @@ -3517,14 +3556,14 @@ } }, "node_modules/jest-mock": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.6.2.tgz", - "integrity": "sha512-hoSv3lb3byzdKfwqCuT6uTscan471GUECqgNYykg6ob0yiAw3zYc7OrPnI9Qv8Wwoa4lC7AZ9hyS4AiIx5U2zg==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", + "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", "dev": true, "dependencies": { - "@jest/types": "^29.6.1", + "@jest/types": "^29.6.3", "@types/node": "*", - "jest-util": "^29.6.2" + "jest-util": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -3548,26 +3587,26 @@ } }, "node_modules/jest-regex-util": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.4.3.tgz", - "integrity": "sha512-O4FglZaMmWXbGHSQInfXewIsd1LMn9p3ZXB/6r4FOkyhX2/iP/soMG98jGvk/A3HAN78+5VWcBGO0BJAPRh4kg==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", "dev": true, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-resolve": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.6.2.tgz", - "integrity": "sha512-G/iQUvZWI5e3SMFssc4ug4dH0aZiZpsDq9o1PtXTV1210Ztyb2+w+ZgQkB3iOiC5SmAEzJBOHWz6Hvrd+QnNPw==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", + "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", "dev": true, "dependencies": { "chalk": "^4.0.0", "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.6.2", + "jest-haste-map": "^29.7.0", "jest-pnp-resolver": "^1.2.2", - "jest-util": "^29.6.2", - "jest-validate": "^29.6.2", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", "resolve": "^1.20.0", "resolve.exports": "^2.0.0", "slash": "^3.0.0" @@ -3577,43 +3616,43 @@ } }, "node_modules/jest-resolve-dependencies": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.6.2.tgz", - "integrity": "sha512-LGqjDWxg2fuQQm7ypDxduLu/m4+4Lb4gczc13v51VMZbVP5tSBILqVx8qfWcsdP8f0G7aIqByIALDB0R93yL+w==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", + "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", "dev": true, "dependencies": { - "jest-regex-util": "^29.4.3", - "jest-snapshot": "^29.6.2" + "jest-regex-util": "^29.6.3", + "jest-snapshot": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-runner": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.6.2.tgz", - "integrity": "sha512-wXOT/a0EspYgfMiYHxwGLPCZfC0c38MivAlb2lMEAlwHINKemrttu1uSbcGbfDV31sFaPWnWJPmb2qXM8pqZ4w==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", + "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", "dev": true, "dependencies": { - "@jest/console": "^29.6.2", - "@jest/environment": "^29.6.2", - "@jest/test-result": "^29.6.2", - "@jest/transform": "^29.6.2", - "@jest/types": "^29.6.1", + "@jest/console": "^29.7.0", + "@jest/environment": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "emittery": "^0.13.1", "graceful-fs": "^4.2.9", - "jest-docblock": "^29.4.3", - "jest-environment-node": "^29.6.2", - "jest-haste-map": "^29.6.2", - "jest-leak-detector": "^29.6.2", - "jest-message-util": "^29.6.2", - "jest-resolve": "^29.6.2", - "jest-runtime": "^29.6.2", - "jest-util": "^29.6.2", - "jest-watcher": "^29.6.2", - "jest-worker": "^29.6.2", + "jest-docblock": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-leak-detector": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-resolve": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-util": "^29.7.0", + "jest-watcher": "^29.7.0", + "jest-worker": "^29.7.0", "p-limit": "^3.1.0", "source-map-support": "0.5.13" }, @@ -3622,31 +3661,31 @@ } }, "node_modules/jest-runtime": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.6.2.tgz", - "integrity": "sha512-2X9dqK768KufGJyIeLmIzToDmsN0m7Iek8QNxRSI/2+iPFYHF0jTwlO3ftn7gdKd98G/VQw9XJCk77rbTGZnJg==", - "dev": true, - "dependencies": { - "@jest/environment": "^29.6.2", - "@jest/fake-timers": "^29.6.2", - "@jest/globals": "^29.6.2", - "@jest/source-map": "^29.6.0", - "@jest/test-result": "^29.6.2", - "@jest/transform": "^29.6.2", - "@jest/types": "^29.6.1", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", + "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/globals": "^29.7.0", + "@jest/source-map": "^29.6.3", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "cjs-module-lexer": "^1.0.0", "collect-v8-coverage": "^1.0.0", "glob": "^7.1.3", "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.6.2", - "jest-message-util": "^29.6.2", - "jest-mock": "^29.6.2", - "jest-regex-util": "^29.4.3", - "jest-resolve": "^29.6.2", - "jest-snapshot": "^29.6.2", - "jest-util": "^29.6.2", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", "slash": "^3.0.0", "strip-bom": "^4.0.0" }, @@ -3655,9 +3694,9 @@ } }, "node_modules/jest-snapshot": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.6.2.tgz", - "integrity": "sha512-1OdjqvqmRdGNvWXr/YZHuyhh5DeaLp1p/F8Tht/MrMw4Kr1Uu/j4lRG+iKl1DAqUJDWxtQBMk41Lnf/JETYBRA==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", + "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", "dev": true, "dependencies": { "@babel/core": "^7.11.6", @@ -3665,20 +3704,20 @@ "@babel/plugin-syntax-jsx": "^7.7.2", "@babel/plugin-syntax-typescript": "^7.7.2", "@babel/types": "^7.3.3", - "@jest/expect-utils": "^29.6.2", - "@jest/transform": "^29.6.2", - "@jest/types": "^29.6.1", + "@jest/expect-utils": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", "babel-preset-current-node-syntax": "^1.0.0", "chalk": "^4.0.0", - "expect": "^29.6.2", + "expect": "^29.7.0", "graceful-fs": "^4.2.9", - "jest-diff": "^29.6.2", - "jest-get-type": "^29.4.3", - "jest-matcher-utils": "^29.6.2", - "jest-message-util": "^29.6.2", - "jest-util": "^29.6.2", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", "natural-compare": "^1.4.0", - "pretty-format": "^29.6.2", + "pretty-format": "^29.7.0", "semver": "^7.5.3" }, "engines": { @@ -3686,12 +3725,12 @@ } }, "node_modules/jest-util": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.6.2.tgz", - "integrity": "sha512-3eX1qb6L88lJNCFlEADKOkjpXJQyZRiavX1INZ4tRnrBVr2COd3RgcTLyUiEXMNBlDU/cgYq6taUS0fExrWW4w==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", "dev": true, "dependencies": { - "@jest/types": "^29.6.1", + "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "ci-info": "^3.2.0", @@ -3703,17 +3742,17 @@ } }, "node_modules/jest-validate": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.6.2.tgz", - "integrity": "sha512-vGz0yMN5fUFRRbpJDPwxMpgSXW1LDKROHfBopAvDcmD6s+B/s8WJrwi+4bfH4SdInBA5C3P3BI19dBtKzx1Arg==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", + "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", "dev": true, "dependencies": { - "@jest/types": "^29.6.1", + "@jest/types": "^29.6.3", "camelcase": "^6.2.0", "chalk": "^4.0.0", - "jest-get-type": "^29.4.3", + "jest-get-type": "^29.6.3", "leven": "^3.1.0", - "pretty-format": "^29.6.2" + "pretty-format": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -3732,18 +3771,18 @@ } }, "node_modules/jest-watcher": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.6.2.tgz", - "integrity": "sha512-GZitlqkMkhkefjfN/p3SJjrDaxPflqxEAv3/ik10OirZqJGYH5rPiIsgVcfof0Tdqg3shQGdEIxDBx+B4tuLzA==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", + "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", "dev": true, "dependencies": { - "@jest/test-result": "^29.6.2", - "@jest/types": "^29.6.1", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", "@types/node": "*", "ansi-escapes": "^4.2.1", "chalk": "^4.0.0", "emittery": "^0.13.1", - "jest-util": "^29.6.2", + "jest-util": "^29.7.0", "string-length": "^4.0.1" }, "engines": { @@ -3751,13 +3790,13 @@ } }, "node_modules/jest-worker": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.6.2.tgz", - "integrity": "sha512-l3ccBOabTdkng8I/ORCkADz4eSMKejTYv1vB/Z83UiubqhC1oQ5Li6dWCyqOIvSifGjUBxuvxvlm6KGK2DtuAQ==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", "dev": true, "dependencies": { "@types/node": "*", - "jest-util": "^29.6.2", + "jest-util": "^29.7.0", "merge-stream": "^2.0.0", "supports-color": "^8.0.0" }, @@ -4364,12 +4403,12 @@ } }, "node_modules/pretty-format": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.6.2.tgz", - "integrity": "sha512-1q0oC8eRveTg5nnBEWMXAU2qpv65Gnuf2eCQzSjxpWFkPaPARwqZZDGuNE0zPAZfTCHzIk3A8dIjwlQKKLphyg==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", "dev": true, "dependencies": { - "@jest/schemas": "^29.6.0", + "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", "react-is": "^18.0.0" }, @@ -4431,9 +4470,9 @@ } }, "node_modules/pure-rand": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.2.tgz", - "integrity": "sha512-6Yg0ekpKICSjPswYOuC5sku/TSWaRYlA0qsXqJgM/d/4pLPHPuTxK7Nbf7jFKzAeedUhR8C7K9Uv63FBsSo8xQ==", + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.4.tgz", + "integrity": "sha512-LA0Y9kxMYv47GIPJy6MI84fqTd2HmYZI83W/kM/SkKfDlajnZYfmXFTxkbY+xSBPkLJxltMa9hIkmdc29eguMA==", "dev": true, "funding": [ { @@ -4498,12 +4537,12 @@ } }, "node_modules/resolve": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", - "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", "dev": true, "dependencies": { - "is-core-module": "^2.9.0", + "is-core-module": "^2.13.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, @@ -4901,9 +4940,9 @@ "dev": true }, "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==", + "version": "29.1.1", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.1.1.tgz", + "integrity": "sha512-D6xjnnbP17cC85nliwGiL+tpoKN0StpgE0TeOjXQTU6MVCfsB4v7aW05CgQ/1OywGb0x/oy9hHFnN+sczTiRaA==", "dev": true, "dependencies": { "bs-logger": "0.x", @@ -4912,7 +4951,7 @@ "json5": "^2.2.3", "lodash.memoize": "4.x", "make-error": "1.x", - "semver": "7.x", + "semver": "^7.5.3", "yargs-parser": "^21.0.1" }, "bin": { @@ -5049,25 +5088,19 @@ } }, "node_modules/v8-to-istanbul": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.1.0.tgz", - "integrity": "sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA==", + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.2.0.tgz", + "integrity": "sha512-/EH/sDgxU2eGxajKdwLCDmQ4FWq+kpi3uCmBGpw1xJtnAxEjlD8j8PEiGWpCIMIs3ciNAgH0d3TTJiUkYzyZjA==", "dev": true, "dependencies": { "@jridgewell/trace-mapping": "^0.3.12", "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^1.6.0" + "convert-source-map": "^2.0.0" }, "engines": { "node": ">=10.12.0" } }, - "node_modules/v8-to-istanbul/node_modules/convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", - "dev": true - }, "node_modules/walker": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", diff --git a/package.json b/package.json index 24a088b..ae63527 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,14 @@ "homepage": "https://github.com/replicate/replicate-javascript#readme", "bugs": "https://github.com/replicate/replicate-javascript/issues", "license": "Apache-2.0", - "main": "index.js", + "type": "module", + "exports": { + ".": { + "types": "./dist/types/index.d.ts", + "import": "./index.js", + "require": "./index.cjs" + } + }, "engines": { "node": ">=18.0.0", "npm": ">=7.19.0", @@ -17,7 +24,13 @@ "check": "tsc", "format": "biome format . --write", "lint": "biome lint .", - "test": "jest" + "test": "REPLICATE_API_TOKEN=test-token jest", + "build": "npm run build:version && npm run build:types && npm run build:commonjs", + "build:version": "echo \"export const version = '\"$(node -pe 'require(\"./package.json\").version')\"';\" > version.js", + "build:types": "tsc --target ES2022 --declaration --emitDeclarationOnly --allowJs --types node --outDir ./dist/types index.js", + "build:commonjs": "tsc --target ES2022 --allowJs --module commonjs --sourceMap --types node --outDir ./dist/commonjs index.js && echo '{\"type\": \"commonjs\"}' > ./dist/commonjs/package.json", + "test:integration": "npm --prefix integration/commonjs test;npm --prefix integration/esm test;npm --prefix integration/typescript test", + "test:all": "npm run test && npm run test:integration" }, "optionalDependencies": { "readable-stream": ">=4.0.0" diff --git a/tsconfig.json b/tsconfig.json index 7a564ee..c7961ea 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,9 +2,13 @@ "compilerOptions": { "esModuleInterop": true, "noEmit": true, - "strict": true + "strict": true, + "allowJs": true, }, + "types": ["node"], "exclude": [ + "dist", + "integration", "**/node_modules" ] } diff --git a/version.js b/version.js new file mode 100644 index 0000000..fdb8731 --- /dev/null +++ b/version.js @@ -0,0 +1,2 @@ +// Replaced using `npm run build:version` at pack time. +export const version = '0.0.0-dev';