diff --git a/apps/docs/docs/modules/model-providers.md b/apps/docs/docs/modules/model-providers.md index f2aa726..2d9b5a8 100644 --- a/apps/docs/docs/modules/model-providers.md +++ b/apps/docs/docs/modules/model-providers.md @@ -55,3 +55,14 @@ await openai.stream( } ); ``` + +### Rate limiting + +Some model providers (eg. `OpenAI`) have rate limits on the number of requests and tokens per minute. +By default, Promptable will handle rate limiting for you assuming you are using `text-davinci` as your model. + +If you are using a different model or you want to set your own rate limits, you can do so: +```ts +// Codex model rate limits +new OpenAI(apiKey, { rateLimitConfig: { requestsPerMinute: 20, tokensPerMinute: 40000 } }); +``` diff --git a/examples/src/index.ts b/examples/src/index.ts index 268a40f..77875d2 100644 --- a/examples/src/index.ts +++ b/examples/src/index.ts @@ -40,6 +40,8 @@ import tracingWebPrompt from "./tracing-web-prompt" import chainSimple from "./chain-simple"; import chainMemory from "./chain-memory"; +import rateLimit from "./rate-limit-example"; + // Add examples here! const examples = { @@ -83,6 +85,8 @@ const examples = { "chain-simple": chainSimple, "chain-memory": chainMemory, + + "rate-limit": rateLimit, }; const isExample = (arg: string): arg is keyof typeof examples => diff --git a/examples/src/rate-limit-example.ts b/examples/src/rate-limit-example.ts new file mode 100644 index 0000000..3395feb --- /dev/null +++ b/examples/src/rate-limit-example.ts @@ -0,0 +1,45 @@ +/** +This example shows the built-in rate limiting functionality of Promptable. + +We'll generate 25 requests and try to send them in parallel against the OpenAI API. +Since we're using the codex model, we have a rate limit of 20 requests per minute. + +Without rate limiting, we'd expect some of the requests to fail. +With rate limiting, we expect all requests to succeed. +**/ +import dotenv from "dotenv"; +dotenv.config(); +import { OpenAI } from "promptable"; + +const apiKey = process.env.OPENAI_API_KEY || ""; + +const attemptRequests = async (openai: OpenAI) => { + const text = "this is a test"; + let responsesPromises = []; + for (let i = 0; i < 25; i++) { + responsesPromises.push(openai.generate(text, { model: "code-davinci-002" })); + } + const startTime = performance.now(); + let responses = await Promise.all(responsesPromises); + const endTime = performance.now(); + let numFailed = responses.filter(r => r === "failed").length; + return [numFailed, responses.length, endTime - startTime]; +} + +const run = async (_args: string[]) => { + // Setting rateLimitConfig to null disables rate limiting + const openaiNoLimit = new OpenAI(apiKey, { rateLimitConfig: null }); + let [numFailed, total, time] = await attemptRequests(openaiNoLimit); + console.log(`Without rate limiting, ${numFailed}/${total} requests failed. Total time: ${time.toFixed(0)} ms`); + console.log("Waiting 180 seconds for rate limit to reset..."); + // Sleep for 180 seconds to allow the rate limit to reset + await new Promise(r => setTimeout(r, 180000)); + /* Since the default rateLimitConfig is set for text-davinci-003, we explicitly set the + rateLimitConfig to the codex model's rate limit with a lot of wiggle room (6 instead of 20) + openai's rate limiter for codex is a little wonky so we use low rate limit */ + const openaiLimit = new OpenAI(apiKey, { rateLimitConfig: { requestsPerMinute: 6, tokensPerMinute: 20000 } }); + [numFailed, total, time] = await attemptRequests(openaiLimit); + console.log(`With rate limiting, ${numFailed}/${total} requests failed. Total time: ${time.toFixed(0)} ms`); +}; + +export default run; diff --git a/packages/promptable/package.json b/packages/promptable/package.json index 324f4a4..90214e2 100644 --- a/packages/promptable/package.json +++ b/packages/promptable/package.json @@ -26,6 +26,7 @@ "chalk": "^4.1.2", "csv-parse": "^5.3.4", "gpt3-tokenizer": "^1.1.4", + "limiter": "^2.1.0", "openai": "^3.1.0", "typescript": "latest", "uuid": "^9.0.0", diff --git a/packages/promptable/src/chains/LLMChain.ts b/packages/promptable/src/chains/LLMChain.ts index 0254d5a..2549431 100644 --- a/packages/promptable/src/chains/LLMChain.ts +++ b/packages/promptable/src/chains/LLMChain.ts @@ -10,7 +10,7 @@ export class LLMChain< constructor( public prompt: Prompt, public provider: CompletionsModelProvider - ) {} + ) { } protected async _run(variables: Record) { // TODO: fix trace so that the anonymous function isn't needed diff --git a/packages/promptable/src/providers/OpenAI.ts b/packages/promptable/src/providers/OpenAI.ts index d35506d..31271f0 100644 --- a/packages/promptable/src/providers/OpenAI.ts +++ b/packages/promptable/src/providers/OpenAI.ts @@ -1,3 +1,4 @@ +import { RateLimiter } from "limiter"; import { CompletionsModelProvider, EmbeddingsModelProvider, @@ -9,7 +10,7 @@ import { unescapeStopTokens } from "@utils/unescape-stop-tokens"; import { Document } from "src"; import GPT3Tokenizer from "gpt3-tokenizer"; -class OpenAIConfiguration extends Configuration {} +class OpenAIConfiguration extends Configuration { } type GenerateCompletionOptions = { /** @@ -104,34 +105,67 @@ type GenerateCompletionOptions = { user?: string; }; + +type RateLimitConfig = { requestsPerMinute: number, tokensPerMinute: number } | null; + +export type OpenAIProviderConfig = { + rateLimitConfig?: RateLimitConfig +}; + +function normalizeRequestLimits(perMinute: number) { + let perMillisecond = perMinute / 60000; + let interval = 1; // ms + if (perMillisecond < 1) { + interval = 1 / perMillisecond; + perMillisecond = 1; + } + return [perMillisecond, interval]; +} + export class OpenAI extends ModelProvider - implements CompletionsModelProvider, EmbeddingsModelProvider -{ + implements CompletionsModelProvider, EmbeddingsModelProvider { apiKey: string; - config: OpenAIConfiguration; + openAIConfig: OpenAIConfiguration; api: OpenAIApi; completionsConfig = DEFAULT_COMPLETION_OPTIONS; embeddingsConfig: OpenAIEmbeddingsConfig = DEFAULT_OPENAI_EMBEDDINGS_CONFIG; tokenizer: OpenAITokenizer = new OpenAITokenizer(); + tokenRateLimiter: RateLimiter | null = null; + requestRateLimiter: RateLimiter | null = null; + - constructor(apiKey: string) { + constructor(apiKey: string, { rateLimitConfig }: OpenAIProviderConfig = {}) { super(ModelProviderType.OpenAI); this.apiKey = apiKey; + rateLimitConfig = rateLimitConfig === undefined ? OPENAI_DEFAULT_RATE_LIMITS : rateLimitConfig; + if (rateLimitConfig) { + const [requestsPerInterval, rpmInterval] = normalizeRequestLimits(rateLimitConfig.requestsPerMinute); + // NOTE: the token rate limiter is hard to test properly + this.tokenRateLimiter = new RateLimiter({ tokensPerInterval: rateLimitConfig.tokensPerMinute, interval: 'minute' }); + this.requestRateLimiter = new RateLimiter({ tokensPerInterval: requestsPerInterval, interval: rpmInterval }); + } - const config = new OpenAIConfiguration({ + this.openAIConfig = new OpenAIConfiguration({ apiKey, }); - this.config = config; - - this.api = new OpenAIApi(config); + this.api = new OpenAIApi(this.openAIConfig); } countTokens(text: string) { return this.tokenizer.countTokens(text); } + protected async enforceRateLimit(promptText: string, options: { max_tokens?: number | null }) { + if (this.tokenRateLimiter) { + await this.tokenRateLimiter.removeTokens(this.countTokens(promptText) + (options.max_tokens || 0)); + } + if (this.requestRateLimiter) { + await this.requestRateLimiter.removeTokens(1); + } + } + async generate( promptText: string, options: GenerateCompletionOptions = DEFAULT_COMPLETION_OPTIONS @@ -141,15 +175,24 @@ export class OpenAI options.stop = unescapeStopTokens(options.stop); } + await this.enforceRateLimit(promptText, options); + const res = await this.api.createCompletion({ prompt: promptText, ...options, model: options.model || DEFAULT_COMPLETION_OPTIONS.model, }); - return res.data.choices[0]?.text || ""; } catch (e) { - console.log(e); + // @ts-expect-error + if (e.response) { + // @ts-expect-error + console.error(`Status code: ${e.response.status}. Data: ${JSON.stringify(e.response.data, null, 2)}`); + + } else { + // @ts-expect-error + console.error(e.message); + } } return "failed"; } @@ -216,6 +259,7 @@ export class OpenAI text: string, options: Omit ) => { + await this.enforceRateLimit(text, {}); const result = await this.api.createEmbedding({ ...options, input: text.replace(/\n/g, " "), @@ -229,11 +273,13 @@ export class OpenAI options: Omit ) => { const batchResults = await Promise.all( - texts.map((text) => - this.api.createEmbedding({ + texts.map(async (text) => { + await this.enforceRateLimit(text, {}); + return await this.api.createEmbedding({ ...options, input: text.replace(/\n/g, " "), }) + } ) ); @@ -273,6 +319,11 @@ export const OPENAI_MODEL_SETTINGS = { }, }; +/* Taken from: https://platform.openai.com/docs/guides/rate-limits/overview +By default we'll use the rate limits for pay-as-you-go (after 48 hours) users for text-davinci-003. +*/ +export const OPENAI_DEFAULT_RATE_LIMITS = { requestsPerMinute: 3000, tokensPerMinute: 250000 }; + interface OpenAIEmbeddingsConfig { model: string; } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3dcf949..87e5eca 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -96,7 +96,7 @@ importers: eslint-config-next: 13.1.6 prettier: ^2.8.1 prisma: ^4.9.0 - promptable: ^0.0.7 + promptable: workspace:* ts-node: ^10.9.1 typescript: ^4.9.4 zod: ^3.20.2 @@ -104,7 +104,7 @@ importers: '@mrleebo/prisma-ast': 0.5.1 '@prisma/client': 4.9.0_prisma@4.9.0 dotenv: 16.0.3 - promptable: 0.0.7_ts-node@10.9.1 + promptable: link:../../packages/promptable ts-node: 10.9.1_bdgp3l2zgaopogaavxusmetvge zod: 3.20.2 devDependencies: @@ -327,6 +327,7 @@ importers: csv-parse: ^5.3.4 gpt3-tokenizer: ^1.1.4 jest: ^29.4.1 + limiter: ^2.1.0 openai: ^3.1.0 ts-jest: ^29.0.5 tsup: ^6.5.0 @@ -339,6 +340,7 @@ importers: chalk: 4.1.2 csv-parse: 5.3.4 gpt3-tokenizer: 1.1.4 + limiter: 2.1.0 openai: 3.1.0 typescript: 4.9.5 uuid: 9.0.0 @@ -2957,6 +2959,7 @@ packages: cpu: [arm] os: [android] requiresBuild: true + dev: true optional: true /@esbuild/linux-loong64/0.15.18: @@ -2965,6 +2968,7 @@ packages: cpu: [loong64] os: [linux] requiresBuild: true + dev: true optional: true /@eslint/eslintrc/0.4.3: @@ -5562,6 +5566,7 @@ packages: /any-promise/1.3.0: resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} + dev: true /anymatch/3.1.3: resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} @@ -6092,6 +6097,7 @@ packages: dependencies: esbuild: 0.15.18 load-tsconfig: 0.2.3 + dev: true /bytes/3.0.0: resolution: {integrity: sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==} @@ -6106,6 +6112,7 @@ packages: /cac/6.7.14: resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} engines: {node: '>=8'} + dev: true /cacheable-request/6.1.0: resolution: {integrity: sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==} @@ -6471,6 +6478,7 @@ packages: /commander/4.1.1: resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} engines: {node: '>= 6'} + dev: true /commander/5.1.0: resolution: {integrity: sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==} @@ -7513,6 +7521,7 @@ packages: cpu: [x64] os: [android] requiresBuild: true + dev: true optional: true /esbuild-android-arm64/0.15.18: @@ -7521,6 +7530,7 @@ packages: cpu: [arm64] os: [android] requiresBuild: true + dev: true optional: true /esbuild-darwin-64/0.15.18: @@ -7529,6 +7539,7 @@ packages: cpu: [x64] os: [darwin] requiresBuild: true + dev: true optional: true /esbuild-darwin-arm64/0.15.18: @@ -7537,6 +7548,7 @@ packages: cpu: [arm64] os: [darwin] requiresBuild: true + dev: true optional: true /esbuild-freebsd-64/0.15.18: @@ -7545,6 +7557,7 @@ packages: cpu: [x64] os: [freebsd] requiresBuild: true + dev: true optional: true /esbuild-freebsd-arm64/0.15.18: @@ -7553,6 +7566,7 @@ packages: cpu: [arm64] os: [freebsd] requiresBuild: true + dev: true optional: true /esbuild-linux-32/0.15.18: @@ -7561,6 +7575,7 @@ packages: cpu: [ia32] os: [linux] requiresBuild: true + dev: true optional: true /esbuild-linux-64/0.15.18: @@ -7569,6 +7584,7 @@ packages: cpu: [x64] os: [linux] requiresBuild: true + dev: true optional: true /esbuild-linux-arm/0.15.18: @@ -7577,6 +7593,7 @@ packages: cpu: [arm] os: [linux] requiresBuild: true + dev: true optional: true /esbuild-linux-arm64/0.15.18: @@ -7585,6 +7602,7 @@ packages: cpu: [arm64] os: [linux] requiresBuild: true + dev: true optional: true /esbuild-linux-mips64le/0.15.18: @@ -7593,6 +7611,7 @@ packages: cpu: [mips64el] os: [linux] requiresBuild: true + dev: true optional: true /esbuild-linux-ppc64le/0.15.18: @@ -7601,6 +7620,7 @@ packages: cpu: [ppc64] os: [linux] requiresBuild: true + dev: true optional: true /esbuild-linux-riscv64/0.15.18: @@ -7609,6 +7629,7 @@ packages: cpu: [riscv64] os: [linux] requiresBuild: true + dev: true optional: true /esbuild-linux-s390x/0.15.18: @@ -7617,6 +7638,7 @@ packages: cpu: [s390x] os: [linux] requiresBuild: true + dev: true optional: true /esbuild-netbsd-64/0.15.18: @@ -7625,6 +7647,7 @@ packages: cpu: [x64] os: [netbsd] requiresBuild: true + dev: true optional: true /esbuild-openbsd-64/0.15.18: @@ -7633,6 +7656,7 @@ packages: cpu: [x64] os: [openbsd] requiresBuild: true + dev: true optional: true /esbuild-sunos-64/0.15.18: @@ -7641,6 +7665,7 @@ packages: cpu: [x64] os: [sunos] requiresBuild: true + dev: true optional: true /esbuild-windows-32/0.15.18: @@ -7649,6 +7674,7 @@ packages: cpu: [ia32] os: [win32] requiresBuild: true + dev: true optional: true /esbuild-windows-64/0.15.18: @@ -7657,6 +7683,7 @@ packages: cpu: [x64] os: [win32] requiresBuild: true + dev: true optional: true /esbuild-windows-arm64/0.15.18: @@ -7665,6 +7692,7 @@ packages: cpu: [arm64] os: [win32] requiresBuild: true + dev: true optional: true /esbuild/0.15.18: @@ -7695,6 +7723,7 @@ packages: esbuild-windows-32: 0.15.18 esbuild-windows-64: 0.15.18 esbuild-windows-arm64: 0.15.18 + dev: true /escalade/3.1.1: resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} @@ -9169,6 +9198,7 @@ packages: minimatch: 3.1.2 once: 1.4.0 path-is-absolute: 1.0.1 + dev: true /glob/7.1.7: resolution: {integrity: sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==} @@ -10700,6 +10730,7 @@ packages: /joycon/3.1.1: resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==} engines: {node: '>=10'} + dev: true /js-sdsl/4.3.0: resolution: {integrity: sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ==} @@ -10850,6 +10881,10 @@ packages: array-includes: 3.1.6 object.assign: 4.1.4 + /just-performance/4.3.0: + resolution: {integrity: sha512-L7RjvtJsL0QO8xFs5wEoDDzzJwoiowRw6Rn/GnvldlchS2JQr9wFYPiwZcDfrbbujEKqKN0tvENdbjXdYhDp5Q==} + dev: false + /keyv/3.1.0: resolution: {integrity: sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==} dependencies: @@ -10912,12 +10947,19 @@ packages: resolution: {integrity: sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg==} engines: {node: '>=10'} + /limiter/2.1.0: + resolution: {integrity: sha512-361TYz6iay6n+9KvUUImqdLuFigK+K79qrUtBsXhJTLdH4rIt/r1y8r1iozwh8KbZNpujbFTSh74mJ7bwbAMOw==} + dependencies: + just-performance: 4.3.0 + dev: false + /lines-and-columns/1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} /load-tsconfig/0.2.3: resolution: {integrity: sha512-iyT2MXws+dc2Wi6o3grCFtGXpeMvHmJqS27sMPGtV2eUu4PeFnG+33I8BlFK1t1NWMjOpcx9bridn5yxLDX2gQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dev: true /load-yaml-file/0.2.0: resolution: {integrity: sha512-OfCBkGEw4nN6JLtgRidPX6QxjBQGQf72q3si2uvqyFEMbycSFFHwAZeXx6cJgFM9wmLrf9zBwCP3Ivqa+LLZPw==} @@ -10993,6 +11035,7 @@ packages: /lodash.sortby/4.7.0: resolution: {integrity: sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==} + dev: true /lodash.startcase/4.4.0: resolution: {integrity: sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==} @@ -11366,6 +11409,7 @@ packages: any-promise: 1.3.0 object-assign: 4.1.1 thenify-all: 1.6.0 + dev: true /nano-time/1.0.0: resolution: {integrity: sha512-flnngywOoQ0lLQOTRNexn2gGSNuM9bKj9RZAWSzhQ+UJYaAFG9bac4DW9VHjUAzrOaIcajHybCTHe/bkvozQqA==} @@ -11953,6 +11997,7 @@ packages: /pirates/4.0.5: resolution: {integrity: sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==} engines: {node: '>= 6'} + dev: true /pkg-dir/4.2.0: resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} @@ -12116,23 +12161,6 @@ packages: postcss: 8.4.21 yaml: 1.10.2 - /postcss-load-config/3.1.4_ts-node@10.9.1: - resolution: {integrity: sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==} - engines: {node: '>= 10'} - peerDependencies: - postcss: '>=8.0.9' - ts-node: '>=9.0.0' - peerDependenciesMeta: - postcss: - optional: true - ts-node: - optional: true - dependencies: - lilconfig: 2.0.6 - ts-node: 10.9.1_bdgp3l2zgaopogaavxusmetvge - yaml: 1.10.2 - dev: false - /postcss-loader/7.0.2_6jdsrmfenkuhhw3gx4zvjlznce: resolution: {integrity: sha512-fUJzV/QH7NXUAqV8dWJ9Lg4aTkDCezpTS5HgJ2DvqznexTbSTxgi/dTECvTZ15BwKTtk8G/bqI/QTu2HPd3ZCg==} engines: {node: '>= 14.15.0'} @@ -12682,18 +12710,6 @@ packages: asap: 2.0.6 dev: false - /promptable/0.0.7_ts-node@10.9.1: - resolution: {integrity: sha512-CSc0Gv1yXk2ZAra3ihSK62D2R5HFHCePk9nuFPNxtzoNxZlJcUBlfgz53PK082FjnB8OCWNvfE/FnJNCxM2pFQ==} - dependencies: - tsup: 6.5.0_6qtx7vkbdhwvdm4crzlegk4mvi - typescript: 4.9.5 - transitivePeerDependencies: - - '@swc/core' - - postcss - - supports-color - - ts-node - dev: false - /prompts/2.4.2: resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} engines: {node: '>= 6'} @@ -13525,6 +13541,7 @@ packages: hasBin: true optionalDependencies: fsevents: 2.3.2 + dev: true /rtl-detect/1.0.4: resolution: {integrity: sha512-EBR4I2VDSSYr7PkBmFy04uhycIpDKp+21p/jARYXlCSjQksTBQcJ0HFUPOO79EPPH5JS6VAhiIQbycf0O3JAxQ==} @@ -13943,6 +13960,7 @@ packages: engines: {node: '>= 8'} dependencies: whatwg-url: 7.1.0 + dev: true /space-separated-tokens/1.1.5: resolution: {integrity: sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA==} @@ -14245,6 +14263,7 @@ packages: mz: 2.7.0 pirates: 4.0.5 ts-interface-checker: 0.1.13 + dev: true /superjson/1.9.1: resolution: {integrity: sha512-oT3HA2nPKlU1+5taFgz/HDy+GEaY+CWEbLzaRJVD4gZ7zMVVC4GDNFdgvAZt6/VuIk6D2R7RtPAiCHwmdzlMmg==} @@ -14557,11 +14576,13 @@ packages: engines: {node: '>=0.8'} dependencies: thenify: 3.3.1 + dev: true /thenify/3.3.1: resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} dependencies: any-promise: 1.3.0 + dev: true /thunky/1.1.0: resolution: {integrity: sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==} @@ -14651,6 +14672,7 @@ packages: resolution: {integrity: sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==} dependencies: punycode: 2.3.0 + dev: true /tr46/2.1.0: resolution: {integrity: sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==} @@ -14662,6 +14684,7 @@ packages: /tree-kill/1.2.2: resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} hasBin: true + dev: true /trim-newlines/3.0.1: resolution: {integrity: sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==} @@ -14682,6 +14705,7 @@ packages: /ts-interface-checker/0.1.13: resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} + dev: true /ts-jest/29.0.5_xvkg46limlf5bbm7x5z7xzp47q: resolution: {integrity: sha512-PL3UciSgIpQ7f6XjVOmbi96vmDHUqAyqDr8YxzopDqX3kfgYtX1cuNeBjP+L9sFXi6nzsGGA6R3fP3DDDJyrxA==} @@ -14805,42 +14829,6 @@ packages: /tslib/2.5.0: resolution: {integrity: sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==} - /tsup/6.5.0_6qtx7vkbdhwvdm4crzlegk4mvi: - resolution: {integrity: sha512-36u82r7rYqRHFkD15R20Cd4ercPkbYmuvRkz3Q1LCm5BsiFNUgpo36zbjVhCOgvjyxNBWNKHsaD5Rl8SykfzNA==} - engines: {node: '>=14'} - hasBin: true - peerDependencies: - '@swc/core': ^1 - postcss: ^8.4.12 - typescript: ^4.1.0 - peerDependenciesMeta: - '@swc/core': - optional: true - postcss: - optional: true - typescript: - optional: true - dependencies: - bundle-require: 3.1.2_esbuild@0.15.18 - cac: 6.7.14 - chokidar: 3.5.3 - debug: 4.3.4 - esbuild: 0.15.18 - execa: 5.1.1 - globby: 11.1.0 - joycon: 3.1.1 - postcss-load-config: 3.1.4_ts-node@10.9.1 - resolve-from: 5.0.0 - rollup: 3.10.1 - source-map: 0.8.0-beta.0 - sucrase: 3.29.0 - tree-kill: 1.2.2 - typescript: 4.9.5 - transitivePeerDependencies: - - supports-color - - ts-node - dev: false - /tsup/6.5.0_typescript@4.9.5: resolution: {integrity: sha512-36u82r7rYqRHFkD15R20Cd4ercPkbYmuvRkz3Q1LCm5BsiFNUgpo36zbjVhCOgvjyxNBWNKHsaD5Rl8SykfzNA==} engines: {node: '>=14'} @@ -15563,6 +15551,7 @@ packages: /webidl-conversions/4.0.2: resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==} + dev: true /webidl-conversions/5.0.0: resolution: {integrity: sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==} @@ -15755,6 +15744,7 @@ packages: lodash.sortby: 4.7.0 tr46: 1.0.1 webidl-conversions: 4.0.2 + dev: true /whatwg-url/8.7.0: resolution: {integrity: sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==}