diff --git a/.changeset/silly-bikes-run.md b/.changeset/silly-bikes-run.md new file mode 100644 index 00000000..aa65250b --- /dev/null +++ b/.changeset/silly-bikes-run.md @@ -0,0 +1,6 @@ +--- +'@edge-runtime/node-utils': patch +'@edge-runtime/primitives': patch +--- + +Introducing @edge-runtime/node-utils to run edge-compliant code in Node.js environment diff --git a/docs/pages/packages/_meta.json b/docs/pages/packages/_meta.json index f4192a22..575d79b3 100644 --- a/docs/pages/packages/_meta.json +++ b/docs/pages/packages/_meta.json @@ -3,6 +3,7 @@ "format": "@edge-runtime/format", "jest-environment": "@edge-runtime/jest-environment", "jest-expect": "@edge-runtime/jest-expect", + "node-utils": "@edge-runtime/node-utils", "ponyfill": "@edge-runtime/ponyfill", "primitives": "@edge-runtime/primitives", "runtime": "edge-runtime", diff --git a/docs/pages/packages/node-utils.mdx b/docs/pages/packages/node-utils.mdx new file mode 100644 index 00000000..cebb965c --- /dev/null +++ b/docs/pages/packages/node-utils.mdx @@ -0,0 +1,140 @@ +import { Callout } from 'nextra-theme-docs' +import { Tabs, Tab } from '../../components/tabs' + +# Edge Runtime Node Utils + +The **@edge-runtime/node-utils** package contains utilities to run web compliant code into a Node.js environment. + + + Please note this is an alpha version. + + +## Installation + + + + ```sh + npm install @edge-runtime/node-utils --save + ``` + + + ```sh + yarn add @edge-runtime/node-utils + ``` + + + ```sh + pnpm install @edge-runtime/node-utils --save + ``` + + + +This package includes built-in TypeScript support. + +## Usage + +```ts +import { once } = from 'node:events' +import { createServer } from 'node:http' +import { buildToNodeHandler } from '@edge-runtime/node-utils' + +// 1. builds a transformer, using Node.js@18 globals, and a base url for URL constructor. +const transformToNode = buildToNodeHandler(global, { + origin: 'http://example.com', +}) + +const server = await createServer( + // 2. takes an web compliant request handler, that uses Web globals like Request and Response, + // and turn it into a Node.js compliant request handler. + transformToNode(async (req: Request) => new Response(req.body)) +) + +// 3. start the node.js server +server.listen() +await once(server, 'listening') + +// 4. invoke the request handler +const response = await fetch( + `http://localhost:${(server.address() as AddressInfo).port}`, + { method: 'POST', body: 'hello world' } +) + +console.log(await response.text()) // is 'hello world' +await server.close() +``` + +## API + +### buildToNodeHandler(dependencies, options): toNodeHandler(handler: WebHandler): NodeHandler + +Builds a transformer function to turn an web compliant request handler (`(req: Request) => Promise | Response | null | undefined`) into +a Node.js compliant [request handler](https://nodejs.org/api/http.html#httpcreateserveroptions-requestlistener) (`(req: IncomingMessage, res: ServerResponse) => Promise | void`). + +**Limitation:** it does support the web handler second parameter, so `waitUntil` is not implemented yet. + +#### dependencies: object + +List of Web globals used by the transformer function: + +- [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) +- [Headers](https://developer.mozilla.org/en-US/docs/Web/API/Headers) +- [ReadableStream](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream) +- [Uint8Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) + +When running in Node.js 18+ (or Node.js 16 with [--experimental-fetch](https://nodejs.org/docs/latest-v16.x/api/cli.html#node_optionsoptions)), you may pass the ones from `global` scope. + +```js +import { buildToNodeHandler } from '@edge-runtime/node-utils' + +buildToNodeHandler(globals, { + /* ... options */ +}) +``` + +Otherwise, you can reuse primitives from [@edge-runtime/primitives](/packages/primitives) + +```js +import { buildToNodeHandler } from '@edge-runtime/node-utils' +import * as primitives from '@edge-runtime/primitives' + +buildToNodeHandler(primitives, { + /* ... options */ +}) +``` + +#### options: object + +Options used to build the transformer function, including: + +##### origin: string + +Origin used to turn the incoming request relative url into a full [Request.url](https://developer.mozilla.org/en-US/docs/Web/API/Request/url) string. + +### buildToRequest(dependencies, options): toRequest(request: IncomingMessage, options: object): Request + +Builds a transformer function to turn a Node.js [IncomingMessage](https://nodejs.org/api/http.html#class-httpincomingmessage) into a Web [Request]. +It needs globals Web contstructor a [dependencies](#dependencies-object), as well as [options](#options-object) to handle incoming url. + +### toOutgoingHeaders(headers: Headers): OutgoingHttpHeaders + +Turns Web [Request.headers](https://developer.mozilla.org/en-US/docs/Web/API/Request/headers) into +Node.js `ServerResponse` [OutgoingHttpHeaders](https://nodejs.org/api/http.html#responsegetheaders). + +Includes `set-cookie` special handling, spliting multiple values when relevant. + +### buildToHeaders(dependencies): toHeaders(nodeHeaders: IncomingHttpHeaders): Headers + +Builds a transformer to turn Node.js [IncomingHttpHeaders](https://nodejs.org/api/http.html#messageheaders) into +Web [Request.headers](https://developer.mozilla.org/en-US/docs/Web/API/Request/headers). + +### toToReadable(webStream: ReadableStream, options: object): Readable + +Turns Web [ReadableStream](https://nodejs.org/api/stream.html#readable-streams) +(typically, the [Response.body](https://developer.mozilla.org/en-US/docs/Web/API/Response/body)) into +Node.js [Readable](https://nodejs.org/api/stream.html#readable-streams) stream. + +### buildToReadableStream(dependencies): toReadableStream(stream: Readable): ReadableStream + +Builds a transformer to turn Node.js [Readable](https://nodejs.org/api/stream.html#readable-streams) +(typically, the [IncomingMessage](https://nodejs.org/api/http.html#class-httpincomingmessage)'s payload) into +Web [ReadableStream](https://nodejs.org/api/stream.html#readable-streams). diff --git a/jest.config.ts b/jest.config.ts index 963e654f..b862dc2a 100644 --- a/jest.config.ts +++ b/jest.config.ts @@ -20,5 +20,6 @@ export default (rootDir: string): Config.InitialOptions => { 'jest-watch-typeahead/filename', 'jest-watch-typeahead/testname', ], + collectCoverageFrom: ['src/**/*.{js,jsx,ts,tsx}'], } } diff --git a/packages/node-utils/README.md b/packages/node-utils/README.md new file mode 100644 index 00000000..509764c6 --- /dev/null +++ b/packages/node-utils/README.md @@ -0,0 +1,38 @@ +
+
+ edge-runtime logo +
+
+

@edge-runtime/node-utils: Utilities to run web compliant code into a Node.js environment.

+

See @edge-runtime/node-utils section in our website for more information.

+
+
+ +## Install + +**Note: this is an alpha version.** + +Using npm: + +```sh +npm install @edge-runtime/node-utils --save +``` + +or using yarn: + +```sh +yarn add @edge-runtime/node-utils --dev +``` + +or using pnpm: + +```sh +pnpm install @edge-runtime/node-utils --save +``` + +## License + +**@edge-runtime/node-utils** © [Vercel](https://vercel.com), released under the [MPLv2](https://github.com/vercel/edge-runtime/blob/main/LICENSE.md) License.
+Authored and maintained by [Vercel](https://vercel.com) with help from [contributors](https://github.com/vercel/edge-runtime/contributors). + +> [vercel.com](https://vercel.com) · GitHub [Vercel](https://github.com/vercel) · Twitter [@vercel](https://twitter.com/vercel) diff --git a/packages/node-utils/jest.config.ts b/packages/node-utils/jest.config.ts new file mode 100644 index 00000000..77304c26 --- /dev/null +++ b/packages/node-utils/jest.config.ts @@ -0,0 +1,5 @@ +import buildConfig from '../../jest.config' +import type { Config } from '@jest/types' + +const config: Config.InitialOptions = buildConfig(__dirname) +export default config diff --git a/packages/node-utils/package.json b/packages/node-utils/package.json new file mode 100644 index 00000000..fc063ef9 --- /dev/null +++ b/packages/node-utils/package.json @@ -0,0 +1,38 @@ +{ + "name": "@edge-runtime/node-utils", + "description": "Helpers for running edge-compliant code in Node.js environment", + "homepage": "https://edge-runtime.vercel.app/packages/node-utils", + "version": "1.0.0-alpha.1", + "main": "dist/index.js", + "module": "dist/index.mjs", + "repository": { + "directory": "packages/node-utils", + "type": "git", + "url": "git+https://github.com/vercel/edge-runtime.git" + }, + "bugs": { + "url": "https://github.com/vercel/edge-runtime/issues" + }, + "keywords": [], + "devDependencies": { + "@edge-runtime/primitives": "workspace:2.0.2", + "@types/test-listen": "1.1.0", + "test-listen": "1.1.0", + "tsup": "6" + }, + "files": [ + "dist" + ], + "scripts": { + "build": "tsup", + "clean:build": "rm -rf dist", + "clean:node": "rm -rf node_modules", + "prebuild": "pnpm run clean:build", + "test": "jest" + }, + "license": "MPLv2", + "publishConfig": { + "access": "public" + }, + "types": "dist/index.d.ts" +} diff --git a/packages/node-utils/src/edge-to-node/handler.ts b/packages/node-utils/src/edge-to-node/handler.ts new file mode 100644 index 00000000..a5774ff7 --- /dev/null +++ b/packages/node-utils/src/edge-to-node/handler.ts @@ -0,0 +1,52 @@ +import type { IncomingMessage, ServerResponse } from 'node:http' +import type { + WebHandler, + NodeHandler, + BuildDependencies, + RequestOptions, +} from '../types' +import { buildToRequest } from '../node-to-edge/request' +import { mergeIntoServerResponse, toOutgoingHeaders } from './headers' +import { toToReadable } from './stream' + +export function buildToNodeHandler( + dependencies: BuildDependencies, + options: RequestOptions +) { + const toRequest = buildToRequest(dependencies) + return function toNodeHandler(webHandler: WebHandler): NodeHandler { + return (request: IncomingMessage, response: ServerResponse) => { + const maybePromise = webHandler(toRequest(request, options)) + if (maybePromise instanceof Promise) { + maybePromise.then((webResponse) => + toServerResponse(webResponse, response) + ) + } else { + toServerResponse(maybePromise, response) + } + } + } +} + +function toServerResponse( + webResponse: Response | null | undefined, + serverResponse: ServerResponse +) { + if (!webResponse) { + serverResponse.end() + return + } + mergeIntoServerResponse( + // @ts-ignore getAll() is not standard https://fetch.spec.whatwg.org/#headers-class + toOutgoingHeaders(webResponse.headers), + serverResponse + ) + + serverResponse.statusCode = webResponse.status + serverResponse.statusMessage = webResponse.statusText + if (!webResponse.body) { + serverResponse.end() + return + } + toToReadable(webResponse.body).pipe(serverResponse) +} diff --git a/packages/node-utils/src/edge-to-node/headers.ts b/packages/node-utils/src/edge-to-node/headers.ts new file mode 100644 index 00000000..7a31e22a --- /dev/null +++ b/packages/node-utils/src/edge-to-node/headers.ts @@ -0,0 +1,109 @@ +import type { Headers } from '@edge-runtime/primitives' +import type { OutgoingHttpHeaders, ServerResponse } from 'node:http' + +export function toOutgoingHeaders( + headers?: Headers & { raw?: () => Record } +): OutgoingHttpHeaders { + const outputHeaders: OutgoingHttpHeaders = {} + if (headers) { + for (const [name, value] of typeof headers.raw !== 'undefined' + ? Object.entries(headers.raw()) + : headers.entries()) { + outputHeaders[name] = value + if (name.toLowerCase() === 'set-cookie') { + outputHeaders[name] = + 'getAll' in headers + ? headers.getAll('set-cookie') + : splitCookiesString(value) + } + } + } + return outputHeaders +} + +export function mergeIntoServerResponse( + headers: OutgoingHttpHeaders, + serverResponse: ServerResponse +) { + for (const [name, value] of Object.entries(headers)) { + if (value !== undefined) { + serverResponse.setHeader(name, value) + } + } +} + +/* + Set-Cookie header field-values are sometimes comma joined in one string. This splits them without choking on commas + that are within a single set-cookie field-value, such as in the Expires portion. + This is uncommon, but explicitly allowed - see https://tools.ietf.org/html/rfc2616#section-4.2 + Node.js does this for every header *except* set-cookie - see https://github.com/nodejs/node/blob/d5e363b77ebaf1caf67cd7528224b651c86815c1/lib/_http_incoming.js#L128 + React Native's fetch does this for *every* header, including set-cookie. + + Based on: https://github.com/google/j2objc/commit/16820fdbc8f76ca0c33472810ce0cb03d20efe25 + Credits to: https://github.com/tomball for original and https://github.com/chrusart for JavaScript implementation +*/ +function splitCookiesString(cookiesString: string) { + var cookiesStrings = [] + var pos = 0 + var start + var ch + var lastComma + var nextStart + var cookiesSeparatorFound + + function skipWhitespace() { + while (pos < cookiesString.length && /\s/.test(cookiesString.charAt(pos))) { + pos += 1 + } + return pos < cookiesString.length + } + + function notSpecialChar() { + ch = cookiesString.charAt(pos) + + return ch !== '=' && ch !== ';' && ch !== ',' + } + + while (pos < cookiesString.length) { + start = pos + cookiesSeparatorFound = false + + while (skipWhitespace()) { + ch = cookiesString.charAt(pos) + if (ch === ',') { + // ',' is a cookie separator if we have later first '=', not ';' or ',' + lastComma = pos + pos += 1 + + skipWhitespace() + nextStart = pos + + while (pos < cookiesString.length && notSpecialChar()) { + pos += 1 + } + + // currently special character + if (pos < cookiesString.length && cookiesString.charAt(pos) === '=') { + // we found cookies separator + cookiesSeparatorFound = true + // pos is inside the next cookie, so back up and return it. + pos = nextStart + cookiesStrings.push(cookiesString.substring(start, lastComma)) + start = pos + } else { + // in param ',' or param separator ';', + // we continue from that comma + pos = lastComma + 1 + } + } else { + pos += 1 + } + } + + if (!cookiesSeparatorFound || pos >= cookiesString.length) { + cookiesStrings.push(cookiesString.substring(start, cookiesString.length)) + } + } + + return cookiesStrings +} diff --git a/packages/node-utils/src/edge-to-node/index.ts b/packages/node-utils/src/edge-to-node/index.ts new file mode 100644 index 00000000..206e3c59 --- /dev/null +++ b/packages/node-utils/src/edge-to-node/index.ts @@ -0,0 +1,3 @@ +export * from './handler' +export * from './headers' +export * from './stream' diff --git a/packages/node-utils/src/edge-to-node/stream.ts b/packages/node-utils/src/edge-to-node/stream.ts new file mode 100644 index 00000000..b456ac6e --- /dev/null +++ b/packages/node-utils/src/edge-to-node/stream.ts @@ -0,0 +1,76 @@ +import { Readable } from 'node:stream' + +interface FromWebOptions { + objectMode?: boolean + highWaterMark?: number + encoding?: BufferEncoding + signal?: AbortSignal +} + +/** + * Code adapted from Node's stream.Readable.fromWeb(), because it has to run on Node@14 + * @see https://github.com/nodejs/node/blob/bd462ad81bc30e547e52e699ee3b6fa3d7c882c9/lib/internal/webstreams/adapters.js#L458 + */ +export function toToReadable( + webStream: ReadableStream, + options: FromWebOptions = {} +) { + const reader = webStream.getReader() + let closed = false + const { highWaterMark, encoding, objectMode = false, signal } = options + + const readable = new Readable({ + objectMode, + highWaterMark, + encoding, + // @ts-ignore signal exist only since Node@17 + signal, + read() { + reader.read().then( + (chunk: any) => { + if (chunk.done) { + readable.push(null) + } else { + readable.push(chunk.value) + } + }, + (error: any) => readable.destroy(error) + ) + }, + + destroy(error: any, callback: (arg0: any) => void) { + function done() { + try { + callback(error) + } catch (error) { + // In a next tick because this is happening within + // a promise context, and if there are any errors + // thrown we don't want those to cause an unhandled + // rejection. Let's just escape the promise and + // handle it separately. + process.nextTick(() => { + throw error + }) + } + } + + if (!closed) { + reader.cancel(error).then(done, done) + return + } + done() + }, + }) + + reader.closed.then( + () => { + closed = true + }, + (error: any) => { + closed = true + readable.destroy(error) + } + ) + + return readable +} diff --git a/packages/node-utils/src/index.ts b/packages/node-utils/src/index.ts new file mode 100644 index 00000000..ab2eecc8 --- /dev/null +++ b/packages/node-utils/src/index.ts @@ -0,0 +1,3 @@ +export * from './edge-to-node/index' +export * from './node-to-edge/index' +export * from './types' diff --git a/packages/node-utils/src/node-to-edge/headers.ts b/packages/node-utils/src/node-to-edge/headers.ts new file mode 100644 index 00000000..4134e97e --- /dev/null +++ b/packages/node-utils/src/node-to-edge/headers.ts @@ -0,0 +1,21 @@ +import type { Headers } from '@edge-runtime/primitives' +import type { IncomingHttpHeaders } from 'http' + +interface Dependencies { + Headers: typeof Headers +} + +export function buildToHeaders({ Headers }: Dependencies) { + return function toHeaders(nodeHeaders: IncomingHttpHeaders): Headers { + const headers = new Headers() + for (let [key, value] of Object.entries(nodeHeaders)) { + const values = Array.isArray(value) ? value : [value] + for (let v of values) { + if (v !== undefined) { + headers.append(key, v) + } + } + } + return headers + } +} diff --git a/packages/node-utils/src/node-to-edge/index.ts b/packages/node-utils/src/node-to-edge/index.ts new file mode 100644 index 00000000..5da2c321 --- /dev/null +++ b/packages/node-utils/src/node-to-edge/index.ts @@ -0,0 +1,3 @@ +export * from './headers' +export * from './request' +export * from './stream' diff --git a/packages/node-utils/src/node-to-edge/request.ts b/packages/node-utils/src/node-to-edge/request.ts new file mode 100644 index 00000000..e5418c5f --- /dev/null +++ b/packages/node-utils/src/node-to-edge/request.ts @@ -0,0 +1,23 @@ +import type { IncomingMessage } from 'node:http' +import type { Request } from '@edge-runtime/primitives' +import { buildToHeaders } from './headers' +import { buildToReadableStream } from './stream' +import { BuildDependencies, RequestOptions } from '../types' + +export function buildToRequest(dependencies: BuildDependencies) { + const toHeaders = buildToHeaders(dependencies) + const toReadableStream = buildToReadableStream(dependencies) + const { Request } = dependencies + return function toRequest( + request: IncomingMessage, + options: RequestOptions + ): Request { + return new Request(String(new URL(request.url || '/', options.origin)), { + method: request.method, + headers: toHeaders(request.headers), + body: !['HEAD', 'GET'].includes(request.method ?? '') + ? toReadableStream(request) + : null, + }) + } +} diff --git a/packages/node-utils/src/node-to-edge/stream.ts b/packages/node-utils/src/node-to-edge/stream.ts new file mode 100644 index 00000000..43c82c3b --- /dev/null +++ b/packages/node-utils/src/node-to-edge/stream.ts @@ -0,0 +1,25 @@ +import type { Readable } from 'node:stream' + +interface Dependencies { + ReadableStream: typeof ReadableStream + Uint8Array: typeof Uint8Array +} + +export function buildToReadableStream(dependencies: Dependencies) { + const { ReadableStream, Uint8Array } = dependencies + return function toReadableStream(stream: Readable): ReadableStream { + return new ReadableStream({ + start(controller) { + stream.on('data', (chunk) => { + controller.enqueue(new Uint8Array([...new Uint8Array(chunk)])) + }) + stream.on('end', () => { + controller.close() + }) + stream.on('error', (err) => { + controller.error(err) + }) + }, + }) + } +} diff --git a/packages/node-utils/src/types.ts b/packages/node-utils/src/types.ts new file mode 100644 index 00000000..67b1411a --- /dev/null +++ b/packages/node-utils/src/types.ts @@ -0,0 +1,26 @@ +import type { IncomingMessage, ServerResponse } from 'http' +import type { + Request, + Response, + Headers, + ReadableStream, +} from '@edge-runtime/primitives' +export interface BuildDependencies { + Headers: typeof Headers + ReadableStream: typeof ReadableStream + Request: typeof Request + Uint8Array: typeof Uint8Array +} + +export interface RequestOptions { + origin: string +} + +export type NodeHandler = ( + req: IncomingMessage, + res: ServerResponse +) => Promise | void + +export type WebHandler = ( + req: Request +) => Promise | Response | null | undefined diff --git a/packages/node-utils/test/edge-to-node/handler.test.ts b/packages/node-utils/test/edge-to-node/handler.test.ts new file mode 100644 index 00000000..75fe821c --- /dev/null +++ b/packages/node-utils/test/edge-to-node/handler.test.ts @@ -0,0 +1,231 @@ +import type { TestServer } from '../test-utils/run-test-server' +import { buildToNodeHandler } from '../../src/edge-to-node/handler' +import { runTestServer } from '../test-utils/run-test-server' +import { serializeResponse } from '../test-utils/serialize-response' +import * as Edge from '@edge-runtime/primitives' + +const transformToNode = buildToNodeHandler( + { + Headers: Edge.Headers, + ReadableStream: Edge.ReadableStream, + Request: Edge.Request, + Uint8Array: Uint8Array, + }, + { origin: 'http://example.com' } +) + +let server: TestServer + +afterEach(() => { + return server.close() +}) + +it('turns null response into an empty request', async () => { + server = await runTestServer({ + handler: transformToNode(() => null), + }) + + const response = await server.fetch('/') + expect(await serializeResponse(response)).toMatchObject({ + headers: { 'content-length': '0' }, + status: 200, + statusText: 'OK', + text: '', + }) +}) + +it('returns an empty response', async () => { + server = await runTestServer({ + handler: transformToNode(() => new Edge.Response(null)), + }) + + const response = await server.fetch('/') + expect(await serializeResponse(response)).toMatchObject({ + headers: { 'content-length': '0' }, + status: 200, + statusText: 'OK', + text: '', + }) +}) + +it('can change response text and status', async () => { + server = await runTestServer({ + handler: transformToNode( + () => new Edge.Response(null, { status: 204, statusText: 'MY STATUS' }) + ), + }) + + const response = await server.fetch('/') + expect(await serializeResponse(response)).toMatchObject({ + status: 204, + statusText: 'MY STATUS', + }) +}) + +it('returns a text response', async () => { + server = await runTestServer({ + handler: transformToNode(() => new Edge.Response('OK')), + }) + + const response = await server.fetch('/') + expect(await serializeResponse(response)).toMatchObject({ + headers: { 'content-type': 'text/plain;charset=UTF-8' }, + status: 200, + statusText: 'OK', + text: 'OK', + }) +}) + +it('returns a json response', async () => { + const json = { works: 'just right' } + server = await runTestServer({ + handler: transformToNode(() => Edge.Response.json(json)), + }) + + const response = await server.fetch('/') + expect(await serializeResponse(response)).toMatchObject({ + headers: { 'content-type': 'application/json' }, + json, + status: 200, + statusText: 'OK', + }) +}) + +it('returns an async json response', async () => { + const json = { works: 'just right' } + server = await runTestServer({ + handler: transformToNode(() => Promise.resolve(Edge.Response.json(json))), + }) + + const response = await server.fetch('/') + expect(await serializeResponse(response)).toMatchObject({ + headers: { 'content-type': 'application/json' }, + json, + status: 200, + statusText: 'OK', + }) +}) + +it('can configure response headers', async () => { + server = await runTestServer({ + handler: transformToNode(() => { + const response = new Edge.Response() + response.headers.set('x-vercel-custom', '1') + return response + }), + }) + + const response = await server.fetch('/') + expect(await serializeResponse(response)).toMatchObject({ + headers: { 'x-vercel-custom': '1' }, + status: 200, + }) +}) + +it('returns a streams of data', async () => { + const data = ['lorem', 'ipsum', 'nec', 'mergitur'] + const encoder = new Edge.TextEncoder() + server = await runTestServer({ + handler: transformToNode( + () => + new Edge.Response( + new Edge.ReadableStream({ + start(controller) { + let rank = 0 + function write() { + controller.enqueue(encoder.encode(data[rank++])) + if (rank < data.length) { + setTimeout(write, 500) + } else { + controller.close() + } + } + write() + }, + }) + ) + ), + }) + + const response = await server.fetch('/') + expect(await serializeResponse(response)).toMatchObject({ + status: 200, + text: data.join(''), + }) +}) + +it('returns a stream body', async () => { + const encoder = new Edge.TextEncoder() + const stream = new Edge.ReadableStream({ + start(controller) { + controller.enqueue(encoder.encode('hello')) + setTimeout(() => { + controller.enqueue(encoder.encode(' world')) + controller.close() + }) + }, + }) + + server = await runTestServer({ + handler: transformToNode(() => new Edge.Response(stream, { status: 200 })), + }) + + const response = await server.fetch('/') + expect(await serializeResponse(response)).toMatchObject({ + status: 200, + statusText: 'OK', + text: 'hello world', + }) +}) + +it('returns a buffer body', async () => { + const text = 'blah' + const encoder = new Edge.TextEncoder() + server = await runTestServer({ + handler: transformToNode(() => { + return new Edge.Response(encoder.encode(text), { + status: 200, + }) + }), + }) + + const response = await server.fetch('/') + expect(await serializeResponse(response)).toMatchObject({ + status: 200, + statusText: 'OK', + text, + }) +}) + +it('consumes incoming request body', async () => { + const body = 'hello world' + server = await runTestServer({ + handler: transformToNode((req) => new Edge.Response(req.body)), + }) + + const response = await server.fetch('/', { method: 'POST', body }) + expect(await serializeResponse(response)).toMatchObject({ + status: 200, + statusText: 'OK', + text: body, + }) +}) + +it('consumes incoming headers', async () => { + const headers = { + 'x-custom-header': 'foo', + 'x-another-header': 'bar', + } + server = await runTestServer({ + handler: transformToNode( + (req) => new Edge.Response(null, { headers: req.headers }) + ), + }) + + const response = await server.fetch('/', { headers }) + expect(await serializeResponse(response)).toMatchObject({ + status: 200, + statusText: 'OK', + headers, + }) +}) diff --git a/packages/node-utils/test/edge-to-node/headers.test.ts b/packages/node-utils/test/edge-to-node/headers.test.ts new file mode 100644 index 00000000..04fc04cd --- /dev/null +++ b/packages/node-utils/test/edge-to-node/headers.test.ts @@ -0,0 +1,50 @@ +import { Headers } from '@edge-runtime/primitives' +import { toOutgoingHeaders } from '../../src' + +it('handles simple header values', () => { + expect( + toOutgoingHeaders( + new Headers({ + 'Content-Type': 'image/jpeg', + 'X-My-Custom-Header': 'Zeke are cool', + }) + ) + ).toEqual({ + 'content-type': 'image/jpeg', + 'x-my-custom-header': 'Zeke are cool', + }) +}) + +it('splits set-cookie with getAll()', () => { + const headers = new Headers({ 'set-cookie': 'value1' }) + headers.append('set-cookie', 'value2') + headers.append('set-cookie', 'value3') + expect(toOutgoingHeaders(headers)).toEqual({ + 'set-cookie': ['value1', 'value2', 'value3'], + }) +}) + +it('slits set-cookie without getAll()', () => { + const rawHeaders = { + raw: () => ({ + 'set-cookie': + 'cookie1=value1, cookie2=value2; Max-Age=1000, cookie3=value3; Domain=; Secure', + }), + } + expect(toOutgoingHeaders(rawHeaders as unknown as Headers)).toEqual({ + 'set-cookie': [ + 'cookie1=value1', + 'cookie2=value2; Max-Age=1000', + 'cookie3=value3; Domain=; Secure', + ], + }) +}) + +it('handles multiple values as single string', () => { + const headers = new Headers({ 'x-multiple': 'value1' }) + headers.append('x-multiple', 'value2') + headers.append('x-multiple', 'value3') + expect(toOutgoingHeaders(headers)).toEqual({ + 'x-multiple': 'value1, value2, value3', + }) +}) diff --git a/packages/node-utils/test/edge-to-node/stream.test.ts b/packages/node-utils/test/edge-to-node/stream.test.ts new file mode 100644 index 00000000..f664efde --- /dev/null +++ b/packages/node-utils/test/edge-to-node/stream.test.ts @@ -0,0 +1,26 @@ +import { ReadableStream } from '@edge-runtime/primitives' +import { Readable } from 'node:stream' +import { toToReadable } from '../../src' + +it('handles a web ReadableStream', async () => { + const readableStream = new ReadableStream({ + async start(controller) { + const encoder = new TextEncoder() + controller.enqueue(encoder.encode('hello')) + await new Promise((resolve) => setTimeout(resolve, 200)) + controller.enqueue(encoder.encode(' world')) + controller.close() + }, + }) + + const readable = toToReadable(readableStream) + expect((await transformToBuffer(readable)).toString()).toEqual('hello world') +}) + +async function transformToBuffer(stream: Readable) { + const buffers = [] + for await (const data of stream) { + buffers.push(data) + } + return Buffer.concat(buffers) +} diff --git a/packages/node-utils/test/node-to-edge/request.test.ts b/packages/node-utils/test/node-to-edge/request.test.ts new file mode 100644 index 00000000..57f2c000 --- /dev/null +++ b/packages/node-utils/test/node-to-edge/request.test.ts @@ -0,0 +1,128 @@ +import type { TestServer } from '../test-utils/run-test-server' +import { buildToRequest } from '../../src/node-to-edge/request' +import { runTestServer } from '../test-utils/run-test-server' +import * as EdgeRuntime from '@edge-runtime/primitives' + +const nodeRequestToRequest = buildToRequest({ + Headers: EdgeRuntime.Headers, + ReadableStream: EdgeRuntime.ReadableStream, + Request: EdgeRuntime.Request, + Uint8Array: Uint8Array, +}) + +let requestMap = new Map() +let server: TestServer + +beforeAll(async () => { + server = await runTestServer({ + handler: async (incoming, response) => { + const requestId = incoming.headers['x-request-id'] + if (typeof requestId !== 'string') { + response.writeHead(400) + response.end(`Invalid Request Id.`) + return + } + + const request = nodeRequestToRequest(incoming, { origin: server.url }) + const [body, readable] = request.body?.tee() ?? [] + requestMap.set(requestId, new EdgeRuntime.Request(request, { body })) + response.writeHead(200, { 'Content-Type': 'text/plain' }) + + /** + * We respond right away so we can start consuming the body stream in + * the test before it has been completely sent, but we still need to + * consume the body before completing the server response otherwise + * the socket gets closed and we don't get the full body. + */ + const reader = readable?.getReader() + if (reader) { + while (true) { + const { done } = await reader?.read() + if (done) break + } + } + + response.end() + }, + }) +}) + +afterAll(() => { + return server.close() +}) + +it('maps the request input', async () => { + const input = `${server.url}/hi/there?=foo&bar=baz` + const request = await mapRequest(input) + expect(request.url).toEqual(input) +}) + +it('maps the request headers`', async () => { + const headers = new EdgeRuntime.Headers({ 'x-hi': 'there' }) + headers.append('vercel-is-awesome', 'true') + headers.append('vercel-is-awesome', 'you damn right') + const request = await mapRequest(server.url, { headers }) + expect(request?.headers.get('connection')).toEqual('keep-alive') + expect(request?.headers.get('user-agent')).toEqual('undici') + expect(request?.headers.get('x-hi')).toEqual(headers.get('x-hi')) + expect(request?.headers.get('vercel-is-awesome')).toEqual( + headers.get('vercel-is-awesome') + ) +}) + +describe('maps the request body', () => { + it('allows to read the body as text', async () => { + const request = await mapRequest(server.url, { + body: 'Hello World', + method: 'POST', + }) + + expect(request.method).toEqual('POST') + expect(await request.text()).toEqual('Hello World') + }) + + it('allows to read the body as chunks', async () => { + const encoder = new EdgeRuntime.TextEncoder() + const body = new EdgeRuntime.ReadableStream({ + start(controller) { + controller.enqueue(encoder.encode('Hello ')) + setTimeout(() => { + controller.enqueue(encoder.encode('World')) + controller.close() + }, 500) + }, + }) + + const request = await mapRequest(server.url, { + method: 'POST', + body, + }) + + expect(request.method).toEqual('POST') + expect(await request.text()).toEqual('Hello World') + }) + + it('does not allow to read the body twice', async () => { + const request = await mapRequest(server.url, { + body: 'Hello World', + method: 'POST', + }) + + expect(request.method).toEqual('POST') + expect(await request.text()).toEqual('Hello World') + await expect(request.text()).rejects.toThrowError( + 'The body has already been consumed.' + ) + }) +}) + +async function mapRequest(input: string, init: RequestInit = {}) { + const requestId = EdgeRuntime.crypto.randomUUID() + const headers = new EdgeRuntime.Headers(init.headers) + headers.set('x-request-id', requestId) + const response = await EdgeRuntime.fetch(input, { ...init, headers }) + const request = requestMap.get(requestId) + expect(response.status).toEqual(200) + expect(request).not.toBeUndefined() + return request! +} diff --git a/packages/node-utils/test/test-utils/get-kill-server.ts b/packages/node-utils/test/test-utils/get-kill-server.ts new file mode 100644 index 00000000..e5b3db33 --- /dev/null +++ b/packages/node-utils/test/test-utils/get-kill-server.ts @@ -0,0 +1,36 @@ +import type { Server } from 'http' +import type { Socket } from 'net' + +/** + * It takes some time to close the server when it has been invoked with a + * TransformStream readable side so this function will help closing the + * server immediately. + */ +export function getKillServer(server: Server) { + let sockets: Socket[] = [] + + server.on('connection', (socket) => { + sockets.push(socket) + socket.once('close', () => { + sockets.splice(sockets.indexOf(socket), 1) + }) + }) + + return () => { + return new Promise((resolve, reject) => { + server.close((err) => { + if (err) { + return reject(err) + } + resolve() + }) + + sockets.forEach(function (socket) { + socket.destroy() + }) + + // Reset so the server can be restarted + sockets = [] + }) + } +} diff --git a/packages/node-utils/test/test-utils/run-test-server.ts b/packages/node-utils/test/test-utils/run-test-server.ts new file mode 100644 index 00000000..b85e75d3 --- /dev/null +++ b/packages/node-utils/test/test-utils/run-test-server.ts @@ -0,0 +1,31 @@ +import type { IncomingMessage, ServerResponse } from 'http' +import type * as ET from '@edge-runtime/primitives' +import { getKillServer } from './get-kill-server' +import { createServer } from 'http' +import { fetch, URL } from '@edge-runtime/primitives' +import listen from 'test-listen' +interface ServerOptions { + handler: ( + request: IncomingMessage, + response: ServerResponse + ) => Promise | void + port?: number +} + +export interface TestServer { + close: () => Promise + fetch: (info: ET.RequestInfo, init?: ET.RequestInit) => Promise + url: string +} + +export async function runTestServer( + options: ServerOptions +): Promise { + const server = createServer(options.handler) + const url = await listen(server) + return { + close: getKillServer(server), + fetch: (info, init) => fetch(String(new URL(String(info), url)), init), + url, + } +} diff --git a/packages/node-utils/test/test-utils/serialize-response.ts b/packages/node-utils/test/test-utils/serialize-response.ts new file mode 100644 index 00000000..bb353ade --- /dev/null +++ b/packages/node-utils/test/test-utils/serialize-response.ts @@ -0,0 +1,18 @@ +export async function serializeResponse(response: Response) { + const text = await response.text() + return { + status: response.status, + statusText: response.statusText, + headers: Object.fromEntries(response.headers), + json: toJSON(text), + text, + } +} + +function toJSON(value: string) { + try { + return JSON.parse(value) + } catch (error) { + return {} + } +} diff --git a/packages/node-utils/tsconfig.prod.json b/packages/node-utils/tsconfig.prod.json new file mode 100644 index 00000000..a5cb75c5 --- /dev/null +++ b/packages/node-utils/tsconfig.prod.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.json", + "include": ["src/**/*.ts"] +} diff --git a/packages/node-utils/tsup.config.ts b/packages/node-utils/tsup.config.ts new file mode 100644 index 00000000..08e98f88 --- /dev/null +++ b/packages/node-utils/tsup.config.ts @@ -0,0 +1,8 @@ +import { defineConfig } from 'tsup' + +export default defineConfig({ + dts: true, + entry: ['./src/index.ts'], + format: ['cjs', 'esm'], + tsconfig: './tsconfig.prod.json', +}) diff --git a/packages/ponyfill/test/compliance-with-primitives.node.test.ts b/packages/ponyfill/test/compliance-with-primitives.node.test.ts index 57679fc1..eb0e150f 100644 --- a/packages/ponyfill/test/compliance-with-primitives.node.test.ts +++ b/packages/ponyfill/test/compliance-with-primitives.node.test.ts @@ -70,4 +70,6 @@ export const LIMBO_STATE = [ 'DOMException', 'setGlobalDispatcher', 'getGlobalDispatcher', + 'RequestInfo', + 'RequestInit', ] diff --git a/packages/primitives/type-definitions/fetch.d.ts b/packages/primitives/type-definitions/fetch.d.ts index d9b5cb6f..941ebeaf 100644 --- a/packages/primitives/type-definitions/fetch.d.ts +++ b/packages/primitives/type-definitions/fetch.d.ts @@ -8,9 +8,16 @@ export class Request extends globalThis.Request { export class Response extends globalThis.Response { readonly headers: Headers + static json(data: any, init?: ResponseInit): Response } -declare const fetchImplementation: typeof fetch +export type RequestInfo = Parameters[0] +export type RequestInit = Parameters[1] +declare const fetchImplementation: ( + info: RequestInfo, + init?: RequestInit +) => Promise + declare const FileConstructor: typeof File declare const FormDataConstructor: typeof FormData diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f374c43b..4d3d2064 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -22,23 +22,23 @@ importers: turbo: latest typescript: latest devDependencies: - '@changesets/cli': 2.25.2 + '@changesets/cli': 2.24.3 '@jest/types': 29.1.2 - '@svitejs/changesets-changelog-github-compact': 1.0.0 + '@svitejs/changesets-changelog-github-compact': 0.1.1 '@types/jest': 29.0.3 '@types/node': 14.18.32 - c8: 7.11.3 + c8: 7.12.0 finepack: 2.10.15 - git-authors-cli: 1.0.41 + git-authors-cli: 1.0.40 jest: 29.3.1_4f2ldd7um3b3u4eyvetyqsphze jest-watch-typeahead: 2.2.1_jest@29.3.1 nano-staged: 0.8.0 - prettier: 2.8.0 + prettier: 2.7.1 simple-git-hooks: 2.8.0 - ts-jest: 29.0.3_qomy65tzxgczkhgezbcqrt7ofi - ts-node: 10.9.1_jcmx33t3olsvcxopqdljsohpme - turbo: 1.2.16 - typescript: 4.8.4 + ts-jest: 29.0.3_izjwqmc6fmlrsgurdzvro3bxme + ts-node: 10.9.1_6yr256pmli274zrjafonbeyefq + turbo: 1.6.3 + typescript: 4.9.4 docs: specifiers: @@ -118,6 +118,18 @@ importers: devDependencies: '@edge-runtime/jest-environment': link:../jest-environment + packages/node-utils: + specifiers: + '@edge-runtime/primitives': workspace:2.0.2 + '@types/test-listen': 1.1.0 + test-listen: 1.1.0 + tsup: '6' + devDependencies: + '@edge-runtime/primitives': link:../primitives + '@types/test-listen': 1.1.0 + test-listen: 1.1.0 + tsup: 6.1.2 + packages/ponyfill: specifiers: '@edge-runtime/jest-environment': workspace:2.0.2 @@ -528,59 +540,59 @@ packages: resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} dev: true - /@changesets/apply-release-plan/6.1.2: - resolution: {integrity: sha512-H8TV9E/WtJsDfoDVbrDGPXmkZFSv7W2KLqp4xX4MKZXshb0hsQZUNowUa8pnus9qb/5OZrFFRVsUsDCVHNW/AQ==} + /@changesets/apply-release-plan/6.0.4: + resolution: {integrity: sha512-PutV/ymf8cZMqvaLe/Lh5cP3kBQ9FZl6oGQ3qRDxWD1ML+/uH3jrCE7S7Zw7IVSXkD0lnMD+1dAX7fsOJ6ZvgA==} dependencies: '@babel/runtime': 7.18.9 - '@changesets/config': 2.2.0 + '@changesets/config': 2.1.1 '@changesets/get-version-range-type': 0.3.2 - '@changesets/git': 1.5.0 - '@changesets/types': 5.2.0 + '@changesets/git': 1.4.1 + '@changesets/types': 5.1.0 '@manypkg/get-packages': 1.1.3 detect-indent: 6.1.0 fs-extra: 7.0.1 lodash.startcase: 4.4.0 outdent: 0.5.0 - prettier: 2.8.0 + prettier: 1.19.1 resolve-from: 5.0.0 semver: 5.7.1 dev: true - /@changesets/assemble-release-plan/5.2.2: - resolution: {integrity: sha512-B1qxErQd85AeZgZFZw2bDKyOfdXHhG+X5S+W3Da2yCem8l/pRy4G/S7iOpEcMwg6lH8q2ZhgbZZwZ817D+aLuQ==} + /@changesets/assemble-release-plan/5.2.1: + resolution: {integrity: sha512-d6ckasOWlKF9Mzs82jhl6TKSCgVvfLoUK1ERySrTg2TQJdrVUteZue6uEIYUTA7SgMu67UOSwol6R9yj1nTdjw==} dependencies: '@babel/runtime': 7.18.9 '@changesets/errors': 0.1.4 - '@changesets/get-dependents-graph': 1.3.4 - '@changesets/types': 5.2.0 + '@changesets/get-dependents-graph': 1.3.3 + '@changesets/types': 5.1.0 '@manypkg/get-packages': 1.1.3 semver: 5.7.1 dev: true - /@changesets/changelog-git/0.1.13: - resolution: {integrity: sha512-zvJ50Q+EUALzeawAxax6nF2WIcSsC5PwbuLeWkckS8ulWnuPYx8Fn/Sjd3rF46OzeKA8t30loYYV6TIzp4DIdg==} + /@changesets/changelog-git/0.1.12: + resolution: {integrity: sha512-Xv2CPjTBmwjl8l4ZyQ3xrsXZMq8WafPUpEonDpTmcb24XY8keVzt7ZSCJuDz035EiqrjmDKDhODoQ6XiHudlig==} dependencies: - '@changesets/types': 5.2.0 + '@changesets/types': 5.1.0 dev: true - /@changesets/cli/2.25.2: - resolution: {integrity: sha512-ACScBJXI3kRyMd2R8n8SzfttDHi4tmKSwVwXBazJOylQItSRSF4cGmej2E4FVf/eNfGy6THkL9GzAahU9ErZrA==} + /@changesets/cli/2.24.3: + resolution: {integrity: sha512-okhRV+0WCQJa2Kmil/WvN5TK1o3+1JYSjrsGHqhjv+PYcDgDDgQ6I9J9OMBO9lfmNIpN7xSO80/BzxgvReO4Wg==} hasBin: true dependencies: '@babel/runtime': 7.18.9 - '@changesets/apply-release-plan': 6.1.2 - '@changesets/assemble-release-plan': 5.2.2 - '@changesets/changelog-git': 0.1.13 - '@changesets/config': 2.2.0 + '@changesets/apply-release-plan': 6.0.4 + '@changesets/assemble-release-plan': 5.2.1 + '@changesets/changelog-git': 0.1.12 + '@changesets/config': 2.1.1 '@changesets/errors': 0.1.4 - '@changesets/get-dependents-graph': 1.3.4 - '@changesets/get-release-plan': 3.0.15 - '@changesets/git': 1.5.0 + '@changesets/get-dependents-graph': 1.3.3 + '@changesets/get-release-plan': 3.0.14 + '@changesets/git': 1.4.1 '@changesets/logger': 0.0.5 - '@changesets/pre': 1.0.13 - '@changesets/read': 0.5.8 - '@changesets/types': 5.2.0 - '@changesets/write': 0.2.2 + '@changesets/pre': 1.0.12 + '@changesets/read': 0.5.7 + '@changesets/types': 5.1.0 + '@changesets/write': 0.1.9 '@manypkg/get-packages': 1.1.3 '@types/is-ci': 3.0.0 '@types/semver': 6.2.3 @@ -602,13 +614,13 @@ packages: tty-table: 4.1.6 dev: true - /@changesets/config/2.2.0: - resolution: {integrity: sha512-GGaokp3nm5FEDk/Fv2PCRcQCOxGKKPRZ7prcMqxEr7VSsG75MnChQE8plaW1k6V8L2bJE+jZWiRm19LbnproOw==} + /@changesets/config/2.1.1: + resolution: {integrity: sha512-nSRINMqHpdtBpNVT9Eh9HtmLhOwOTAeSbaqKM5pRmGfsvyaROTBXV84ujF9UsWNlV71YxFbxTbeZnwXSGQlyTw==} dependencies: '@changesets/errors': 0.1.4 - '@changesets/get-dependents-graph': 1.3.4 + '@changesets/get-dependents-graph': 1.3.3 '@changesets/logger': 0.0.5 - '@changesets/types': 5.2.0 + '@changesets/types': 5.1.0 '@manypkg/get-packages': 1.1.3 fs-extra: 7.0.1 micromatch: 4.0.5 @@ -620,10 +632,10 @@ packages: extendable-error: 0.1.7 dev: true - /@changesets/get-dependents-graph/1.3.4: - resolution: {integrity: sha512-+C4AOrrFY146ydrgKOo5vTZfj7vetNu1tWshOID+UjPUU9afYGDXI8yLnAeib1ffeBXV3TuGVcyphKpJ3cKe+A==} + /@changesets/get-dependents-graph/1.3.3: + resolution: {integrity: sha512-h4fHEIt6X+zbxdcznt1e8QD7xgsXRAXd2qzLlyxoRDFSa6SxJrDAUyh7ZUNdhjBU4Byvp4+6acVWVgzmTy4UNQ==} dependencies: - '@changesets/types': 5.2.0 + '@changesets/types': 5.1.0 '@manypkg/get-packages': 1.1.3 chalk: 2.4.2 fs-extra: 7.0.1 @@ -639,15 +651,15 @@ packages: - encoding dev: true - /@changesets/get-release-plan/3.0.15: - resolution: {integrity: sha512-W1tFwxE178/en+zSj/Nqbc3mvz88mcdqUMJhRzN1jDYqN3QI4ifVaRF9mcWUU+KI0gyYEtYR65tour690PqTcA==} + /@changesets/get-release-plan/3.0.14: + resolution: {integrity: sha512-xzSfeyIOvUnbqMuQXVKTYUizreWQfICwoQpvEHoePVbERLocc1tPo5lzR7dmVCFcaA/DcnbP6mxyioeq+JuzSg==} dependencies: '@babel/runtime': 7.18.9 - '@changesets/assemble-release-plan': 5.2.2 - '@changesets/config': 2.2.0 - '@changesets/pre': 1.0.13 - '@changesets/read': 0.5.8 - '@changesets/types': 5.2.0 + '@changesets/assemble-release-plan': 5.2.1 + '@changesets/config': 2.1.1 + '@changesets/pre': 1.0.12 + '@changesets/read': 0.5.7 + '@changesets/types': 5.1.0 '@manypkg/get-packages': 1.1.3 dev: true @@ -655,12 +667,12 @@ packages: resolution: {integrity: sha512-SVqwYs5pULYjYT4op21F2pVbcrca4qA/bAA3FmFXKMN7Y+HcO8sbZUTx3TAy2VXulP2FACd1aC7f2nTuqSPbqg==} dev: true - /@changesets/git/1.5.0: - resolution: {integrity: sha512-Xo8AT2G7rQJSwV87c8PwMm6BAc98BnufRMsML7m7Iw8Or18WFvFmxqG5aOL5PBvhgq9KrKvaeIBNIymracSuHg==} + /@changesets/git/1.4.1: + resolution: {integrity: sha512-GWwRXEqBsQ3nEYcyvY/u2xUK86EKAevSoKV/IhELoZ13caZ1A1TSak/71vyKILtzuLnFPk5mepP5HjBxr7lZ9Q==} dependencies: '@babel/runtime': 7.18.9 '@changesets/errors': 0.1.4 - '@changesets/types': 5.2.0 + '@changesets/types': 5.1.0 '@manypkg/get-packages': 1.1.3 is-subdir: 1.2.0 spawndamnit: 2.0.0 @@ -672,31 +684,31 @@ packages: chalk: 2.4.2 dev: true - /@changesets/parse/0.3.15: - resolution: {integrity: sha512-3eDVqVuBtp63i+BxEWHPFj2P1s3syk0PTrk2d94W9JD30iG+OER0Y6n65TeLlY8T2yB9Fvj6Ev5Gg0+cKe/ZUA==} + /@changesets/parse/0.3.14: + resolution: {integrity: sha512-SWnNVyC9vz61ueTbuxvA6b4HXcSx2iaWr2VEa37lPg1Vw+cEyQp7lOB219P7uow1xFfdtIEEsxbzXnqLAAaY8w==} dependencies: - '@changesets/types': 5.2.0 + '@changesets/types': 5.1.0 js-yaml: 3.14.1 dev: true - /@changesets/pre/1.0.13: - resolution: {integrity: sha512-jrZc766+kGZHDukjKhpBXhBJjVQMied4Fu076y9guY1D3H622NOw8AQaLV3oQsDtKBTrT2AUFjt9Z2Y9Qx+GfA==} + /@changesets/pre/1.0.12: + resolution: {integrity: sha512-RFzWYBZx56MtgMesXjxx7ymyI829/rcIw/41hvz3VJPnY8mDscN7RJyYu7Xm7vts2Fcd+SRcO0T/Ws3I1/6J7g==} dependencies: '@babel/runtime': 7.18.9 '@changesets/errors': 0.1.4 - '@changesets/types': 5.2.0 + '@changesets/types': 5.1.0 '@manypkg/get-packages': 1.1.3 fs-extra: 7.0.1 dev: true - /@changesets/read/0.5.8: - resolution: {integrity: sha512-eYaNfxemgX7f7ELC58e7yqQICW5FB7V+bd1lKt7g57mxUrTveYME+JPaBPpYx02nP53XI6CQp6YxnR9NfmFPKw==} + /@changesets/read/0.5.7: + resolution: {integrity: sha512-Iteg0ccTPpkJ+qFzY97k7qqdVE5Kz30TqPo9GibpBk2g8tcLFUqf+Qd0iXPLcyhUZpPL1U6Hia1gINHNKIKx4g==} dependencies: '@babel/runtime': 7.18.9 - '@changesets/git': 1.5.0 + '@changesets/git': 1.4.1 '@changesets/logger': 0.0.5 - '@changesets/parse': 0.3.15 - '@changesets/types': 5.2.0 + '@changesets/parse': 0.3.14 + '@changesets/types': 5.1.0 chalk: 2.4.2 fs-extra: 7.0.1 p-filter: 2.1.0 @@ -706,18 +718,18 @@ packages: resolution: {integrity: sha512-LDQvVDv5Kb50ny2s25Fhm3d9QSZimsoUGBsUioj6MC3qbMUCuC8GPIvk/M6IvXx3lYhAs0lwWUQLb+VIEUCECw==} dev: true - /@changesets/types/5.2.0: - resolution: {integrity: sha512-km/66KOqJC+eicZXsm2oq8A8bVTSpkZJ60iPV/Nl5Z5c7p9kk8xxh6XGRTlnludHldxOOfudhnDN2qPxtHmXzA==} + /@changesets/types/5.1.0: + resolution: {integrity: sha512-uUByGATZCdaPkaO9JkBsgGDjEvHyY2Sb0e/J23+cwxBi5h0fxpLF/HObggO/Fw8T2nxK6zDfJbPsdQt5RwYFJA==} dev: true - /@changesets/write/0.2.2: - resolution: {integrity: sha512-kCYNHyF3xaId1Q/QE+DF3UTrHTyg3Cj/f++T8S8/EkC+jh1uK2LFnM9h+EzV+fsmnZDrs7r0J4LLpeI/VWC5Hg==} + /@changesets/write/0.1.9: + resolution: {integrity: sha512-E90ZrsrfJVOOQaP3Mm5Xd7uDwBAqq3z5paVEavTHKA8wxi7NAL8CmjgbGxSFuiP7ubnJA2BuHlrdE4z86voGOg==} dependencies: '@babel/runtime': 7.18.9 - '@changesets/types': 5.2.0 + '@changesets/types': 5.1.0 fs-extra: 7.0.1 human-id: 1.0.2 - prettier: 2.8.0 + prettier: 1.19.1 dev: true /@cspotcode/source-map-support/0.8.1: @@ -727,6 +739,15 @@ packages: '@jridgewell/trace-mapping': 0.3.9 dev: true + /@esbuild/linux-loong64/0.14.54: + resolution: {integrity: sha512-bZBrLAIX1kpWelV0XemxBZllyRmM6vgFQQG2GdNb+r3Fkp0FOh1NJSvekXDs7jq70k4euu1cryLMfU+mTXlEpw==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@esbuild/linux-loong64/0.15.5: resolution: {integrity: sha512-UHkDFCfSGTuXq08oQltXxSZmH1TXyWsL+4QhZDWvvLl6mEJQqk3u7/wq1LjhrrAXYIllaTtRSzUXl4Olkf2J8A==} engines: {node: '>=12'} @@ -1081,18 +1102,6 @@ packages: chalk: 4.1.2 dev: true - /@jest/types/29.2.1: - resolution: {integrity: sha512-O/QNDQODLnINEPAI0cl9U6zUIDXEWXt6IC1o2N2QENuos7hlGUIthlKyV4p6ki3TvXFX071blj8HUhgLGquPjw==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/schemas': 29.0.0 - '@types/istanbul-lib-coverage': 2.0.4 - '@types/istanbul-reports': 3.0.1 - '@types/node': 18.7.14 - '@types/yargs': 17.0.11 - chalk: 4.1.2 - dev: true - /@jest/types/29.3.1: resolution: {integrity: sha512-d0S0jmmTpjnhCmNpApgX3jrUZgZ22ivKJRvL2lli5hpCRoNnp1f85r2/wpKfXuYu8E7Jjh1hGfhPyup1NM5AmA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -1528,9 +1537,9 @@ packages: dependencies: '@sinonjs/commons': 1.8.3 - /@svitejs/changesets-changelog-github-compact/1.0.0: - resolution: {integrity: sha512-h9pvZ3Bkk1K9bVr707OlWMtjreBPmXnh/GsxbiIp3ukNu9RjmkxlLmyD+h/DCkr8e0l3UpOlDDAUvbUHYNfGnA==} - engines: {node: ^14.13.1 || ^16.0.0 || >=18} + /@svitejs/changesets-changelog-github-compact/0.1.1: + resolution: {integrity: sha512-eBi211CfmKtkxB6tINicaDPBMbolswPbaAy7kCx+uUFL/LxztLm9cB+7jP54TgCrv+mMz8vSJWIs/baH63PjsA==} + engines: {node: ^12.20 || ^14.13.1 || >= 16} dependencies: '@changesets/get-github-info': 0.5.1 dotenv: 16.0.3 @@ -1740,6 +1749,12 @@ packages: /@types/stack-utils/2.0.1: resolution: {integrity: sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==} + /@types/test-listen/1.1.0: + resolution: {integrity: sha512-y6ZfbSzYHniCeY6ZAzsQjSAdJInNVoEz4Uhsb81W+RCoNYA59yoG/+XbqPqCPj2KCU3Wa6RFWSozutkGIHIsNQ==} + dependencies: + '@types/node': 18.7.14 + dev: true + /@types/ua-parser-js/0.7.36: resolution: {integrity: sha512-N1rW+njavs70y2cApeIw1vLMYXRwfBy+7trgavGuuTfOd7j1Yh7QTRc/yqsPl6ncokt72ZXuxEU0PiCp9bSwNQ==} dev: true @@ -2150,6 +2165,16 @@ packages: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} dev: true + /bundle-require/3.0.4_esbuild@0.14.54: + resolution: {integrity: sha512-VXG6epB1yrLAvWVQpl92qF347/UXmncQj7J3U8kZEbdVZ1ZkQyr4hYeL/9RvcE8vVVdp53dY78Fd/3pqfRqI1A==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + peerDependencies: + esbuild: '>=0.13' + dependencies: + esbuild: 0.14.54 + load-tsconfig: 0.2.3 + dev: true + /bundle-require/3.0.4_esbuild@0.15.5: resolution: {integrity: sha512-VXG6epB1yrLAvWVQpl92qF347/UXmncQj7J3U8kZEbdVZ1ZkQyr4hYeL/9RvcE8vVVdp53dY78Fd/3pqfRqI1A==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -2167,8 +2192,8 @@ packages: streamsearch: 1.1.0 dev: true - /c8/7.11.3: - resolution: {integrity: sha512-6YBmsaNmqRm9OS3ZbIiL2EZgi1+Xc4O24jL3vMYGE6idixYuGdy76rIfIdltSKDj9DpLNrcXSonUTR1miBD0wA==} + /c8/7.12.0: + resolution: {integrity: sha512-CtgQrHOkyxr5koX1wEUmN/5cfDa2ckbHRA4Gy5LAL0zaCFtVWJS5++n+w4/sr2GWGerBxgTjpKeDclk/Qk6W/A==} engines: {node: '>=10.12.0'} hasBin: true dependencies: @@ -2477,7 +2502,7 @@ packages: dev: false /concat-map/0.0.1: - resolution: {integrity: sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=} + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} /configstore/5.0.1: resolution: {integrity: sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==} @@ -2812,6 +2837,15 @@ packages: is-symbol: 1.0.4 dev: true + /esbuild-android-64/0.14.54: + resolution: {integrity: sha512-Tz2++Aqqz0rJ7kYBfz+iqyE3QMycD4vk7LBRyWaAVFgFtQ/O8EJOnVmTOiDWYZ/uYzB4kvP+bqejYdVKzE5lAQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + requiresBuild: true + dev: true + optional: true + /esbuild-android-64/0.15.5: resolution: {integrity: sha512-dYPPkiGNskvZqmIK29OPxolyY3tp+c47+Fsc2WYSOVjEPWNCHNyqhtFqQadcXMJDQt8eN0NMDukbyQgFcHquXg==} engines: {node: '>=12'} @@ -2821,6 +2855,15 @@ packages: dev: true optional: true + /esbuild-android-arm64/0.14.54: + resolution: {integrity: sha512-F9E+/QDi9sSkLaClO8SOV6etqPd+5DgJje1F9lOWoNncDdOBL2YF59IhsWATSt0TLZbYCf3pNlTHvVV5VfHdvg==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + requiresBuild: true + dev: true + optional: true + /esbuild-android-arm64/0.15.5: resolution: {integrity: sha512-YyEkaQl08ze3cBzI/4Cm1S+rVh8HMOpCdq8B78JLbNFHhzi4NixVN93xDrHZLztlocEYqi45rHHCgA8kZFidFg==} engines: {node: '>=12'} @@ -2830,6 +2873,15 @@ packages: dev: true optional: true + /esbuild-darwin-64/0.14.54: + resolution: {integrity: sha512-jtdKWV3nBviOd5v4hOpkVmpxsBy90CGzebpbO9beiqUYVMBtSc0AL9zGftFuBon7PNDcdvNCEuQqw2x0wP9yug==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + /esbuild-darwin-64/0.15.5: resolution: {integrity: sha512-Cr0iIqnWKx3ZTvDUAzG0H/u9dWjLE4c2gTtRLz4pqOBGjfjqdcZSfAObFzKTInLLSmD0ZV1I/mshhPoYSBMMCQ==} engines: {node: '>=12'} @@ -2839,6 +2891,15 @@ packages: dev: true optional: true + /esbuild-darwin-arm64/0.14.54: + resolution: {integrity: sha512-OPafJHD2oUPyvJMrsCvDGkRrVCar5aVyHfWGQzY1dWnzErjrDuSETxwA2HSsyg2jORLY8yBfzc1MIpUkXlctmw==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + /esbuild-darwin-arm64/0.15.5: resolution: {integrity: sha512-WIfQkocGtFrz7vCu44ypY5YmiFXpsxvz2xqwe688jFfSVCnUsCn2qkEVDo7gT8EpsLOz1J/OmqjExePL1dr1Kg==} engines: {node: '>=12'} @@ -2848,6 +2909,15 @@ packages: dev: true optional: true + /esbuild-freebsd-64/0.14.54: + resolution: {integrity: sha512-OKwd4gmwHqOTp4mOGZKe/XUlbDJ4Q9TjX0hMPIDBUWWu/kwhBAudJdBoxnjNf9ocIB6GN6CPowYpR/hRCbSYAg==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + /esbuild-freebsd-64/0.15.5: resolution: {integrity: sha512-M5/EfzV2RsMd/wqwR18CELcenZ8+fFxQAAEO7TJKDmP3knhWSbD72ILzrXFMMwshlPAS1ShCZ90jsxkm+8FlaA==} engines: {node: '>=12'} @@ -2857,6 +2927,15 @@ packages: dev: true optional: true + /esbuild-freebsd-arm64/0.14.54: + resolution: {integrity: sha512-sFwueGr7OvIFiQT6WeG0jRLjkjdqWWSrfbVwZp8iMP+8UHEHRBvlaxL6IuKNDwAozNUmbb8nIMXa7oAOARGs1Q==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + /esbuild-freebsd-arm64/0.15.5: resolution: {integrity: sha512-2JQQ5Qs9J0440F/n/aUBNvY6lTo4XP/4lt1TwDfHuo0DY3w5++anw+jTjfouLzbJmFFiwmX7SmUhMnysocx96w==} engines: {node: '>=12'} @@ -2866,6 +2945,15 @@ packages: dev: true optional: true + /esbuild-linux-32/0.14.54: + resolution: {integrity: sha512-1ZuY+JDI//WmklKlBgJnglpUL1owm2OX+8E1syCD6UAxcMM/XoWd76OHSjl/0MR0LisSAXDqgjT3uJqT67O3qw==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + requiresBuild: true + dev: true + optional: true + /esbuild-linux-32/0.15.5: resolution: {integrity: sha512-gO9vNnIN0FTUGjvTFucIXtBSr1Woymmx/aHQtuU+2OllGU6YFLs99960UD4Dib1kFovVgs59MTXwpFdVoSMZoQ==} engines: {node: '>=12'} @@ -2875,6 +2963,15 @@ packages: dev: true optional: true + /esbuild-linux-64/0.14.54: + resolution: {integrity: sha512-EgjAgH5HwTbtNsTqQOXWApBaPVdDn7XcK+/PtJwZLT1UmpLoznPd8c5CxqsH2dQK3j05YsB3L17T8vE7cp4cCg==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /esbuild-linux-64/0.15.5: resolution: {integrity: sha512-ne0GFdNLsm4veXbTnYAWjbx3shpNKZJUd6XpNbKNUZaNllDZfYQt0/zRqOg0sc7O8GQ+PjSMv9IpIEULXVTVmg==} engines: {node: '>=12'} @@ -2884,6 +2981,15 @@ packages: dev: true optional: true + /esbuild-linux-arm/0.14.54: + resolution: {integrity: sha512-qqz/SjemQhVMTnvcLGoLOdFpCYbz4v4fUo+TfsWG+1aOu70/80RV6bgNpR2JCrppV2moUQkww+6bWxXRL9YMGw==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + /esbuild-linux-arm/0.15.5: resolution: {integrity: sha512-wvAoHEN+gJ/22gnvhZnS/+2H14HyAxM07m59RSLn3iXrQsdS518jnEWRBnJz3fR6BJa+VUTo0NxYjGaNt7RA7Q==} engines: {node: '>=12'} @@ -2893,6 +2999,15 @@ packages: dev: true optional: true + /esbuild-linux-arm64/0.14.54: + resolution: {integrity: sha512-WL71L+0Rwv+Gv/HTmxTEmpv0UgmxYa5ftZILVi2QmZBgX3q7+tDeOQNqGtdXSdsL8TQi1vIaVFHUPDe0O0kdig==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /esbuild-linux-arm64/0.15.5: resolution: {integrity: sha512-7EgFyP2zjO065XTfdCxiXVEk+f83RQ1JsryN1X/VSX2li9rnHAt2swRbpoz5Vlrl6qjHrCmq5b6yxD13z6RheA==} engines: {node: '>=12'} @@ -2902,6 +3017,15 @@ packages: dev: true optional: true + /esbuild-linux-mips64le/0.14.54: + resolution: {integrity: sha512-qTHGQB8D1etd0u1+sB6p0ikLKRVuCWhYQhAHRPkO+OF3I/iSlTKNNS0Lh2Oc0g0UFGguaFZZiPJdJey3AGpAlw==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + requiresBuild: true + dev: true + optional: true + /esbuild-linux-mips64le/0.15.5: resolution: {integrity: sha512-KdnSkHxWrJ6Y40ABu+ipTZeRhFtc8dowGyFsZY5prsmMSr1ZTG9zQawguN4/tunJ0wy3+kD54GaGwdcpwWAvZQ==} engines: {node: '>=12'} @@ -2911,6 +3035,15 @@ packages: dev: true optional: true + /esbuild-linux-ppc64le/0.14.54: + resolution: {integrity: sha512-j3OMlzHiqwZBDPRCDFKcx595XVfOfOnv68Ax3U4UKZ3MTYQB5Yz3X1mn5GnodEVYzhtZgxEBidLWeIs8FDSfrQ==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /esbuild-linux-ppc64le/0.15.5: resolution: {integrity: sha512-QdRHGeZ2ykl5P0KRmfGBZIHmqcwIsUKWmmpZTOq573jRWwmpfRmS7xOhmDHBj9pxv+6qRMH8tLr2fe+ZKQvCYw==} engines: {node: '>=12'} @@ -2920,6 +3053,15 @@ packages: dev: true optional: true + /esbuild-linux-riscv64/0.14.54: + resolution: {integrity: sha512-y7Vt7Wl9dkOGZjxQZnDAqqn+XOqFD7IMWiewY5SPlNlzMX39ocPQlOaoxvT4FllA5viyV26/QzHtvTjVNOxHZg==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /esbuild-linux-riscv64/0.15.5: resolution: {integrity: sha512-p+WE6RX+jNILsf+exR29DwgV6B73khEQV0qWUbzxaycxawZ8NE0wA6HnnTxbiw5f4Gx9sJDUBemh9v49lKOORA==} engines: {node: '>=12'} @@ -2929,6 +3071,15 @@ packages: dev: true optional: true + /esbuild-linux-s390x/0.14.54: + resolution: {integrity: sha512-zaHpW9dziAsi7lRcyV4r8dhfG1qBidQWUXweUjnw+lliChJqQr+6XD71K41oEIC3Mx1KStovEmlzm+MkGZHnHA==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + requiresBuild: true + dev: true + optional: true + /esbuild-linux-s390x/0.15.5: resolution: {integrity: sha512-J2ngOB4cNzmqLHh6TYMM/ips8aoZIuzxJnDdWutBw5482jGXiOzsPoEF4j2WJ2mGnm7FBCO4StGcwzOgic70JQ==} engines: {node: '>=12'} @@ -2938,6 +3089,15 @@ packages: dev: true optional: true + /esbuild-netbsd-64/0.14.54: + resolution: {integrity: sha512-PR01lmIMnfJTgeU9VJTDY9ZerDWVFIUzAtJuDHwwceppW7cQWjBBqP48NdeRtoP04/AtO9a7w3viI+PIDr6d+w==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + requiresBuild: true + dev: true + optional: true + /esbuild-netbsd-64/0.15.5: resolution: {integrity: sha512-MmKUYGDizYjFia0Rwt8oOgmiFH7zaYlsoQ3tIOfPxOqLssAsEgG0MUdRDm5lliqjiuoog8LyDu9srQk5YwWF3w==} engines: {node: '>=12'} @@ -2947,6 +3107,15 @@ packages: dev: true optional: true + /esbuild-openbsd-64/0.14.54: + resolution: {integrity: sha512-Qyk7ikT2o7Wu76UsvvDS5q0amJvmRzDyVlL0qf5VLsLchjCa1+IAvd8kTBgUxD7VBUUVgItLkk609ZHUc1oCaw==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + requiresBuild: true + dev: true + optional: true + /esbuild-openbsd-64/0.15.5: resolution: {integrity: sha512-2mMFfkLk3oPWfopA9Plj4hyhqHNuGyp5KQyTT9Rc8hFd8wAn5ZrbJg+gNcLMo2yzf8Uiu0RT6G9B15YN9WQyMA==} engines: {node: '>=12'} @@ -2960,6 +3129,15 @@ packages: resolution: {integrity: sha512-jyfL/pwPqaFXyKnj8lP8iLk6Z0m099uXR45aSN8Av1XD4vhvQutxxPzgA2bTcAwQpa1zCXDcWOlhFgyP3GKqhQ==} dev: true + /esbuild-sunos-64/0.14.54: + resolution: {integrity: sha512-28GZ24KmMSeKi5ueWzMcco6EBHStL3B6ubM7M51RmPwXQGLe0teBGJocmWhgwccA1GeFXqxzILIxXpHbl9Q/Kw==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + requiresBuild: true + dev: true + optional: true + /esbuild-sunos-64/0.15.5: resolution: {integrity: sha512-2sIzhMUfLNoD+rdmV6AacilCHSxZIoGAU2oT7XmJ0lXcZWnCvCtObvO6D4puxX9YRE97GodciRGDLBaiC6x1SA==} engines: {node: '>=12'} @@ -2969,6 +3147,15 @@ packages: dev: true optional: true + /esbuild-windows-32/0.14.54: + resolution: {integrity: sha512-T+rdZW19ql9MjS7pixmZYVObd9G7kcaZo+sETqNH4RCkuuYSuv9AGHUVnPoP9hhuE1WM1ZimHz1CIBHBboLU7w==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true + optional: true + /esbuild-windows-32/0.15.5: resolution: {integrity: sha512-e+duNED9UBop7Vnlap6XKedA/53lIi12xv2ebeNS4gFmu7aKyTrok7DPIZyU5w/ftHD4MUDs5PJUkQPP9xJRzg==} engines: {node: '>=12'} @@ -2978,6 +3165,15 @@ packages: dev: true optional: true + /esbuild-windows-64/0.14.54: + resolution: {integrity: sha512-AoHTRBUuYwXtZhjXZbA1pGfTo8cJo3vZIcWGLiUcTNgHpJJMC1rVA44ZereBHMJtotyN71S8Qw0npiCIkW96cQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + /esbuild-windows-64/0.15.5: resolution: {integrity: sha512-v+PjvNtSASHOjPDMIai9Yi+aP+Vwox+3WVdg2JB8N9aivJ7lyhp4NVU+J0MV2OkWFPnVO8AE/7xH+72ibUUEnw==} engines: {node: '>=12'} @@ -2987,6 +3183,15 @@ packages: dev: true optional: true + /esbuild-windows-arm64/0.14.54: + resolution: {integrity: sha512-M0kuUvXhot1zOISQGXwWn6YtS+Y/1RT9WrVIOywZnJHo3jCDyewAc79aKNQWFCQm+xNHVTq9h8dZKvygoXQQRg==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + /esbuild-windows-arm64/0.15.5: resolution: {integrity: sha512-Yz8w/D8CUPYstvVQujByu6mlf48lKmXkq6bkeSZZxTA626efQOJb26aDGLzmFWx6eg/FwrXgt6SZs9V8Pwy/aA==} engines: {node: '>=12'} @@ -2996,6 +3201,35 @@ packages: dev: true optional: true + /esbuild/0.14.54: + resolution: {integrity: sha512-Cy9llcy8DvET5uznocPyqL3BFRrFXSVqbgpMJ9Wz8oVjZlh/zUSNbPRbov0VX7VxN2JH1Oa0uNxZ7eLRb62pJA==} + engines: {node: '>=12'} + hasBin: true + requiresBuild: true + optionalDependencies: + '@esbuild/linux-loong64': 0.14.54 + esbuild-android-64: 0.14.54 + esbuild-android-arm64: 0.14.54 + esbuild-darwin-64: 0.14.54 + esbuild-darwin-arm64: 0.14.54 + esbuild-freebsd-64: 0.14.54 + esbuild-freebsd-arm64: 0.14.54 + esbuild-linux-32: 0.14.54 + esbuild-linux-64: 0.14.54 + esbuild-linux-arm: 0.14.54 + esbuild-linux-arm64: 0.14.54 + esbuild-linux-mips64le: 0.14.54 + esbuild-linux-ppc64le: 0.14.54 + esbuild-linux-riscv64: 0.14.54 + esbuild-linux-s390x: 0.14.54 + esbuild-netbsd-64: 0.14.54 + esbuild-openbsd-64: 0.14.54 + esbuild-sunos-64: 0.14.54 + esbuild-windows-32: 0.14.54 + esbuild-windows-64: 0.14.54 + esbuild-windows-arm64: 0.14.54 + dev: true + /esbuild/0.15.5: resolution: {integrity: sha512-VSf6S1QVqvxfIsSKb3UKr3VhUCis7wgDbtF4Vd9z84UJr05/Sp2fRKmzC+CSPG/dNAPPJZ0BTBLTT1Fhd6N9Gg==} engines: {node: '>=12'} @@ -3183,7 +3417,7 @@ packages: jest-get-type: 29.2.0 jest-matcher-utils: 29.2.2 jest-message-util: 29.2.1 - jest-util: 29.2.1 + jest-util: 29.3.1 dev: true /expect/29.3.1: @@ -3455,8 +3689,8 @@ packages: get-intrinsic: 1.1.2 dev: true - /git-authors-cli/1.0.41: - resolution: {integrity: sha512-Tl3a3ZDFSmeYhu411zYqSyz2kjwSaDDXOd/Yg5yuCZ2cn3zWmBN69iG308iTTFzmqg+JeOs0XoWZSQC6Tl+tWQ==} + /git-authors-cli/1.0.40: + resolution: {integrity: sha512-ZbCVPVJXTF3P5ath0AwuLlj2tNi2rHUcaY0Pr2ck46yjqz+7L36PgiVIfVgWyt9+EodSVaaU0bSukJLxyrybmA==} engines: {node: '>= 8'} hasBin: true dependencies: @@ -3465,7 +3699,7 @@ packages: exists-file: 3.0.2 json-future: 2.2.18 meow: 9.0.0 - picocolors: 1.0.0 + picocolors: 0.2.1 update-notifier: 5.1.0 dev: true @@ -3586,7 +3820,7 @@ packages: dev: true /has-ansi/2.0.0: - resolution: {integrity: sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=} + resolution: {integrity: sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==} engines: {node: '>=0.10.0'} dependencies: ansi-regex: 2.1.1 @@ -4197,7 +4431,7 @@ packages: pretty-format: 29.3.1 slash: 3.0.0 strip-json-comments: 3.1.1 - ts-node: 10.9.1_jcmx33t3olsvcxopqdljsohpme + ts-node: 10.9.1_6yr256pmli274zrjafonbeyefq transitivePeerDependencies: - supports-color dev: true @@ -4237,7 +4471,7 @@ packages: pretty-format: 29.3.1 slash: 3.0.0 strip-json-comments: 3.1.1 - ts-node: 10.9.1_jcmx33t3olsvcxopqdljsohpme + ts-node: 10.9.1_6yr256pmli274zrjafonbeyefq transitivePeerDependencies: - supports-color dev: true @@ -4408,7 +4642,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@babel/code-frame': 7.18.6 - '@jest/types': 29.2.1 + '@jest/types': 29.3.1 '@types/stack-utils': 2.0.1 chalk: 4.1.2 graceful-fs: 4.2.10 @@ -4631,18 +4865,6 @@ packages: picomatch: 2.3.1 dev: false - /jest-util/29.2.1: - resolution: {integrity: sha512-P5VWDj25r7kj7kl4pN2rG/RN2c1TLfYYYZYULnS/35nFDjBai+hBeo3MDrYZS7p6IoY3YHZnt2vq4L6mKnLk0g==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - dependencies: - '@jest/types': 29.3.1 - '@types/node': 18.7.14 - chalk: 4.1.2 - ci-info: 3.3.2 - graceful-fs: 4.2.10 - picomatch: 2.3.1 - dev: true - /jest-util/29.3.1: resolution: {integrity: sha512-7YOVZaiX7RJLv76ZfHt4nbNEzzTRiMW/IiOG7ZOKmTXmoGBxUDefgMAxQubu6WPVqP5zSzAdZG0FfLcC7HOIFQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -4757,7 +4979,7 @@ packages: hasBin: true /json-buffer/3.0.0: - resolution: {integrity: sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=} + resolution: {integrity: sha512-CuUqjv0FUZIdXkHPI8MezCnFCdaTAacej1TZYulLoAg1h/PhwkdXFN4V/gzY4g+fMBCOV2xF+rp7t2XD2ns/NQ==} dev: true /json-future/2.2.18: @@ -6120,6 +6342,10 @@ packages: is-reference: 3.0.0 dev: false + /picocolors/0.2.1: + resolution: {integrity: sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==} + dev: true + /picocolors/1.0.0: resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} @@ -6270,8 +6496,14 @@ packages: engines: {node: '>=4'} dev: true - /prettier/2.8.0: - resolution: {integrity: sha512-9Lmg8hTFZKG0Asr/kW9Bp8tJjRVluO8EJQVfY2T7FMw9T5jy4I/Uvx0Rca/XWf50QQ1/SS48+6IJWnrb+2yemA==} + /prettier/1.19.1: + resolution: {integrity: sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==} + engines: {node: '>=4'} + hasBin: true + dev: true + + /prettier/2.7.1: + resolution: {integrity: sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==} engines: {node: '>=10.13.0'} hasBin: true dev: true @@ -7069,6 +7301,19 @@ packages: react: 18.2.0 dev: false + /sucrase/3.21.0: + resolution: {integrity: sha512-FjAhMJjDcifARI7bZej0Bi1yekjWQHoEvWIXhLPwDhC6O4iZ5PtGb86WV56riW87hzpgB13wwBKO9vKAiWu5VQ==} + engines: {node: '>=8'} + hasBin: true + dependencies: + commander: 4.1.1 + glob: 7.1.6 + lines-and-columns: 1.2.4 + mz: 2.7.0 + pirates: 4.0.5 + ts-interface-checker: 0.1.13 + dev: true + /sucrase/3.25.0: resolution: {integrity: sha512-WxTtwEYXSmZArPGStGBicyRsg5TBEFhT5b7N+tF+zauImP0Acy+CoUK0/byJ8JNPK/5lbpWIVuFagI4+0l85QQ==} engines: {node: '>=8'} @@ -7171,6 +7416,10 @@ packages: glob: 7.2.3 minimatch: 3.1.2 + /test-listen/1.1.0: + resolution: {integrity: sha512-OyEVi981C1sb9NX1xayfgZls3p8QTDRwp06EcgxSgd1kktaENBW8dO15i8v/7Fi15j0IYQctJzk5J+hyEBId2w==} + dev: true + /text-encoding/0.7.0: resolution: {integrity: sha512-oJQ3f1hrOnbRLOcwKz0Liq2IcrvDeZRHXhd9RgLrsT+DjWY/nty1Hi7v3dtkaEYbPYe0mUoOfzRrMwfXXwgPUA==} deprecated: no longer maintained @@ -7284,7 +7533,7 @@ packages: resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} dev: true - /ts-jest/29.0.3_qomy65tzxgczkhgezbcqrt7ofi: + /ts-jest/29.0.3_izjwqmc6fmlrsgurdzvro3bxme: resolution: {integrity: sha512-Ibygvmuyq1qp/z3yTh9QTwVVAbFdDy/+4BtIQR2sp6baF2SJU/8CKK/hhnGIDY2L90Az2jIqTwZPnN2p+BweiQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} hasBin: true @@ -7309,16 +7558,16 @@ packages: bs-logger: 0.2.6 fast-json-stable-stringify: 2.1.0 jest: 29.3.1_4f2ldd7um3b3u4eyvetyqsphze - jest-util: 29.2.1 + jest-util: 29.3.1 json5: 2.2.1 lodash.memoize: 4.1.2 make-error: 1.3.6 semver: 7.3.7 - typescript: 4.8.4 + typescript: 4.9.4 yargs-parser: 21.1.1 dev: true - /ts-node/10.9.1_jcmx33t3olsvcxopqdljsohpme: + /ts-node/10.9.1_6yr256pmli274zrjafonbeyefq: resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==} hasBin: true peerDependencies: @@ -7344,7 +7593,7 @@ packages: create-require: 1.1.1 diff: 4.0.2 make-error: 1.3.6 - typescript: 4.8.4 + typescript: 4.9.4 v8-compile-cache-lib: 3.0.1 yn: 3.1.1 dev: true @@ -7357,6 +7606,41 @@ packages: deprecated: no longer maintained dev: true + /tsup/6.1.2: + resolution: {integrity: sha512-Hw4hKDHaAQkm2eVavlArEOrAPA93bziRDamdfwaNs0vXQdUUFfItvUWY0L/F6oQQMVh6GvjQq1+HpDXw8UKtPA==} + 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.0.4_esbuild@0.14.54 + cac: 6.7.12 + chokidar: 3.5.3 + debug: 4.3.4 + esbuild: 0.14.54 + execa: 5.1.1 + globby: 11.1.0 + joycon: 3.1.1 + postcss-load-config: 3.1.4 + resolve-from: 5.0.0 + rollup: 2.78.1 + source-map: 0.8.0-beta.0 + sucrase: 3.21.0 + tree-kill: 1.2.2 + transitivePeerDependencies: + - supports-color + - ts-node + dev: true + /tsup/6.2.2: resolution: {integrity: sha512-vJ9IAdif4GKAz2XMZzjX1hNqhBezJWXjm0qeQEoI7y//a64cxgCF8178eTMV4jBu7YNKnfAPpPSuyXW4mN+9rA==} engines: {node: '>=14'} @@ -7406,128 +7690,65 @@ packages: yargs: 17.5.1 dev: true - /turbo-darwin-64/1.2.16: - resolution: {integrity: sha512-dyitLQJdH3uLVdlH9jAkP4LqEO/K+wOXjUqOzjTciRLjQPzmsNY60/bmFHODADK4eBBl1nxbtn7tmmoT4vS1qA==} + /turbo-darwin-64/1.6.3: + resolution: {integrity: sha512-QmDIX0Yh1wYQl0bUS0gGWwNxpJwrzZU2GIAYt3aOKoirWA2ecnyb3R6ludcS1znfNV2MfunP+l8E3ncxUHwtjA==} cpu: [x64] os: [darwin] requiresBuild: true dev: true optional: true - /turbo-darwin-arm64/1.2.16: - resolution: {integrity: sha512-Ex6uM4HU7rGXdhvJMpzNpp6qxglJ98nWeIi5qR/lBXHLjK3UCvSW8BEALArUJYJTXS9FZBq1a5LowFqXYsfDcA==} + /turbo-darwin-arm64/1.6.3: + resolution: {integrity: sha512-75DXhFpwE7CinBbtxTxH08EcWrxYSPFow3NaeFwsG8aymkWXF+U2aukYHJA6I12n9/dGqf7yRXzkF0S/9UtdyQ==} cpu: [arm64] os: [darwin] requiresBuild: true dev: true optional: true - /turbo-freebsd-64/1.2.16: - resolution: {integrity: sha512-onRGKMvog8B3XDssSBIAg+FrEq9pcBoAybP7bpi/uYIH1L/WQ7YMmLn88X9JX19ehYuVOVZrjap4jWH2GIkU8A==} - cpu: [x64] - os: [freebsd] - requiresBuild: true - dev: true - optional: true - - /turbo-freebsd-arm64/1.2.16: - resolution: {integrity: sha512-S0EqPqxwnJuVNNXRgcHB0r8ai8LSrpHdihVJKRM7WYmIR7isccBEf/G9agrt73sCXwjvenxFs4HDR7cSvGt14Q==} - cpu: [arm64] - os: [freebsd] - requiresBuild: true - dev: true - optional: true - - /turbo-linux-32/1.2.16: - resolution: {integrity: sha512-ecbqmGOxgTWePGrowtwyvZGfvwaLxFWmPK21cU0PS+fzoZBaVmzYmniTdd/2EkGCw7TOPhtiT22v96fWcnRycA==} - cpu: [ia32] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /turbo-linux-64/1.2.16: - resolution: {integrity: sha512-q6gtdMWCzM0Sktkd73zcaQjNoeM1MjtrbwQBctWN/Sgj0eiPBPnzpIvokvx98x7RLf4qyI99/mlme0Dn5fx21A==} + /turbo-linux-64/1.6.3: + resolution: {integrity: sha512-O9uc6J0yoRPWdPg9THRQi69K6E2iZ98cRHNvus05lZbcPzZTxJYkYGb5iagCmCW/pq6fL4T4oLWAd6evg2LGQA==} cpu: [x64] os: [linux] requiresBuild: true dev: true optional: true - /turbo-linux-arm/1.2.16: - resolution: {integrity: sha512-du7uvExELNb89V3g7iM0XP21fR1Yl3EoHRcOfQz32oUqnS7idCKvbEowM9LtiluQl1dKcOIJjn1nlvvsqzkhOg==} - cpu: [arm] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /turbo-linux-arm64/1.2.16: - resolution: {integrity: sha512-gUf67tYJ/N09WAZTTmtUWYrqm381tZxiulnRGAIM+iRsaTrweyUKZaYXwJvlPpI/cQOw25wCG9/IyvxLeagL8A==} + /turbo-linux-arm64/1.6.3: + resolution: {integrity: sha512-dCy667qqEtZIhulsRTe8hhWQNCJO0i20uHXv7KjLHuFZGCeMbWxB8rsneRoY+blf8+QNqGuXQJxak7ayjHLxiA==} cpu: [arm64] os: [linux] requiresBuild: true dev: true optional: true - /turbo-linux-mips64le/1.2.16: - resolution: {integrity: sha512-U5BM+Ql3z13uRtwMmKH/8eL+9DdTgyijC2gaX4xP0RTlcN7WfAstg8Fg/Tn2Vw9vtpVDdxwpw7dvX4kw2ghhpA==} - cpu: [mips64el] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /turbo-linux-ppc64le/1.2.16: - resolution: {integrity: sha512-HQWSCmVZyc5chw7Ie2ZcfZPfmM06mbEEu0Wl11Y5QWh1ZzhPNQHs/TsF4I9r146wHi62XgcrKFjkw4ARZiWsLA==} - cpu: [ppc64] - os: [linux] - requiresBuild: true - dev: true - optional: true - - /turbo-windows-32/1.2.16: - resolution: {integrity: sha512-0ZtPz5FK2qZjznMG4vvRyaabrhO8BgbN+tBx1wjXSuoICTAjYi5TwRVVRh59c3x7qQmR21Cv33CrhLBPRfeAlg==} - cpu: [ia32] - os: [win32] - requiresBuild: true - dev: true - optional: true - - /turbo-windows-64/1.2.16: - resolution: {integrity: sha512-j8iAIixq/rGfBpHNbYOosxMasZrGuMzLILEuQGDxZgKNpYgobJ15QFHQlGR9sit1b8qPU5zZX4CtByRtkgH1Bw==} + /turbo-windows-64/1.6.3: + resolution: {integrity: sha512-lKRqwL3mrVF09b9KySSaOwetehmGknV9EcQTF7d2dxngGYYX1WXoQLjFP9YYH8ZV07oPm+RUOAKSCQuDuMNhiA==} cpu: [x64] os: [win32] requiresBuild: true dev: true optional: true - /turbo-windows-arm64/1.2.16: - resolution: {integrity: sha512-4GpcJG3B8R9WDhwfT8fu6ZmOOfseCg6Q1cy/G8/zpJQk769yYcSnD8MgQhYgHB58aVFxZcMxBvLL6UA0UrpgWA==} + /turbo-windows-arm64/1.6.3: + resolution: {integrity: sha512-BXY1sDPEA1DgPwuENvDCD8B7Hb0toscjus941WpL8CVd10hg9pk/MWn9CNgwDO5Q9ks0mw+liDv2EMnleEjeNA==} cpu: [arm64] os: [win32] requiresBuild: true dev: true optional: true - /turbo/1.2.16: - resolution: {integrity: sha512-PPUa2COKgFkyb6N3uF9AnIY3l9FZkF15QQ3U1K2wpI01D3gyGKQO0Q3DUQ4ipmciP0teBfL7H+l/QTrUA9IVvQ==} + /turbo/1.6.3: + resolution: {integrity: sha512-FtfhJLmEEtHveGxW4Ye/QuY85AnZ2ZNVgkTBswoap7UMHB1+oI4diHPNyqrQLG4K1UFtCkjOlVoLsllUh/9QRw==} hasBin: true requiresBuild: true optionalDependencies: - turbo-darwin-64: 1.2.16 - turbo-darwin-arm64: 1.2.16 - turbo-freebsd-64: 1.2.16 - turbo-freebsd-arm64: 1.2.16 - turbo-linux-32: 1.2.16 - turbo-linux-64: 1.2.16 - turbo-linux-arm: 1.2.16 - turbo-linux-arm64: 1.2.16 - turbo-linux-mips64le: 1.2.16 - turbo-linux-ppc64le: 1.2.16 - turbo-windows-32: 1.2.16 - turbo-windows-64: 1.2.16 - turbo-windows-arm64: 1.2.16 + turbo-darwin-64: 1.6.3 + turbo-darwin-arm64: 1.6.3 + turbo-linux-64: 1.6.3 + turbo-linux-arm64: 1.6.3 + turbo-windows-64: 1.6.3 + turbo-windows-arm64: 1.6.3 dev: true /type-detect/4.0.8: @@ -7575,8 +7796,8 @@ packages: is-typedarray: 1.0.0 dev: true - /typescript/4.8.4: - resolution: {integrity: sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==} + /typescript/4.9.4: + resolution: {integrity: sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==} engines: {node: '>=4.2.0'} hasBin: true dev: true @@ -7595,7 +7816,7 @@ packages: dev: true /underscore/1.6.0: - resolution: {integrity: sha1-izixDKze9jM3uLJOT/htRa6lKag=} + resolution: {integrity: sha512-z4o1fvKUojIWh9XuaVLUDdf86RQiq13AC1dmHbTpoyuu+bquHms76v16CjycCbec87J7z0k//SiQVk0sMdFmpQ==} dev: true /undici/5.11.0: @@ -7897,7 +8118,7 @@ packages: dev: true /which-module/2.0.0: - resolution: {integrity: sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=} + resolution: {integrity: sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==} dev: true /which-pm/2.0.0: