WIP: bootstrap and partial real Solana watcher implementation
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -0,0 +1,2 @@
|
||||
import type { LibDefinition } from '../variable';
|
||||
export declare const es7: LibDefinition;
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"ed25519.d.ts","sourceRoot":"","sources":["src/ed25519.ts"],"names":[],"mappings":"AAUA,OAAO,EAAa,KAAK,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClE,OAAO,EACL,iBAAiB,EAEjB,KAAK,OAAO,EAEZ,KAAK,YAAY,EAClB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAIL,KAAK,SAAS,EACd,KAAK,aAAa,EAClB,KAAK,SAAS,EACd,KAAK,YAAY,EAClB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAOL,KAAK,MAAM,EACZ,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAc,KAAK,cAAc,IAAI,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AACvF,OAAO,EAA4C,KAAK,GAAG,EAAE,MAAM,YAAY,CAAC;AA+FhF;;;;;;;;;GASG;AACH,eAAO,MAAM,OAAO,EAAE,OAAmE,CAAC;AAY1F,8DAA8D;AAC9D,eAAO,MAAM,UAAU,EAAE,OAIlB,CAAC;AAER,4FAA4F;AAC5F,eAAO,MAAM,SAAS,EAAE,OAMlB,CAAC;AAEP;;;;;;;;;GASG;AACH,eAAO,MAAM,MAAM,EAAE,QAYjB,CAAC;AA0EL,2DAA2D;AAC3D,eAAO,MAAM,cAAc,EAAE,SAAS,CAAC,MAAM,CAavC,CAAC;AA6BP,KAAK,aAAa,GAAG,YAAY,CAAC;AAsClC;;;;;;;;GAQG;AACH,cAAM,eAAgB,SAAQ,iBAAiB,CAAC,eAAe,CAAC;IAI9D,MAAM,CAAC,IAAI,EAAE,eAAe,CACwC;IAEpE,MAAM,CAAC,IAAI,EAAE,eAAe,CACwC;IAEpE,MAAM,CAAC,EAAE,EAAE,MAAM,CAAC,MAAM,CAAC,CACM;IAE/B,MAAM,CAAC,EAAE,EAAE,MAAM,CAAC,MAAM,CAAC,CACM;gBAEnB,EAAE,EAAE,aAAa;IAI7B,MAAM,CAAC,UAAU,CAAC,EAAE,EAAE,WAAW,CAAC,MAAM,CAAC,GAAG,eAAe;IAI3D,SAAS,CAAC,UAAU,CAAC,KAAK,EAAE,eAAe,GAAG,IAAI;IAIlD,SAAS,CAAC,IAAI,CAAC,EAAE,EAAE,YAAY,GAAG,eAAe;IAIjD,wFAAwF;IACxF,MAAM,CAAC,WAAW,CAAC,GAAG,EAAE,GAAG,GAAG,eAAe;IAI7C,MAAM,CAAC,SAAS,CAAC,KAAK,EAAE,UAAU,GAAG,eAAe;IA4BpD;;;;OAIG;IACH,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,GAAG,eAAe;IAIzC,MAAM,CAAC,GAAG,CAAC,MAAM,EAAE,eAAe,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,eAAe;IAIzE;;;OAGG;IACH,OAAO,IAAI,UAAU;IA4BrB;;;OAGG;IACH,MAAM,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO;IAWvC,GAAG,IAAI,OAAO;CAGf;AAED,eAAO,MAAM,YAAY,EAAE;IACzB,KAAK,EAAE,OAAO,eAAe,CAAC;CACF,CAAC;AAE/B,gEAAgE;AAChE,eAAO,MAAM,mBAAmB,EAAE,aAAa,CAAC,MAAM,CAUrD,CAAC;AAUF;;;;;GAKG;AACH,eAAO,MAAM,wBAAwB,EAAE,MAAM,EAS5C,CAAC;AAEF,mDAAmD;AACnD,wBAAgB,sBAAsB,CAAC,UAAU,EAAE,GAAG,GAAG,UAAU,CAElE;AACD,mDAAmD;AACnD,eAAO,MAAM,mBAAmB,EAAE,OAAO,sBAA+C,CAAC;AAEzF,yDAAyD;AACzD,wBAAgB,uBAAuB,CAAC,WAAW,EAAE,UAAU,GAAG,UAAU,CAE3E;AAED,2CAA2C;AAC3C,eAAO,MAAM,cAAc,EAAE,OAAO,eAAiC,CAAC;AACtE,mFAAmF;AACnF,eAAO,MAAM,WAAW,EAAE,SAAS,CAAC,MAAM,CAAwD,CAAC;AACnG,mFAAmF;AACnF,eAAO,MAAM,aAAa,EAAE,SAAS,CAAC,MAAM,CACX,CAAC;AAClC,KAAK,UAAU,GAAG,CAAC,GAAG,EAAE,UAAU,EAAE,OAAO,EAAE,YAAY,KAAK,eAAe,CAAC;AAC9E,wFAAwF;AACxF,eAAO,MAAM,kBAAkB,EAAE,UACiB,CAAC;AACnD,wFAAwF;AACxF,eAAO,MAAM,oBAAoB,EAAE,UACe,CAAC"}
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"jsxEmit.js","sourceRoot":"","sources":["../../src/enums/jsxEmit.ts"],"names":[],"mappings":"AAAA,sGAAsG;AACtG,MAAM,CAAC,IAAI,OAAY,CAAC;AACxB,CAAC,UAAU,OAAO;IACd,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,MAAM,CAAC;IACtC,OAAO,CAAC,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,GAAG,UAAU,CAAC;IAC9C,OAAO,CAAC,OAAO,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,GAAG,aAAa,CAAC;IACpD,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,OAAO,CAAC;IACxC,OAAO,CAAC,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,GAAG,UAAU,CAAC;IAC9C,OAAO,CAAC,OAAO,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,GAAG,aAAa,CAAC;AACxD,CAAC,CAAC,CAAC,OAAO,IAAI,CAAC,OAAO,GAAG,EAAE,CAAC,CAAC,CAAC"}
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
export { _ as default } from "../esm/_iterable_to_array_limit_loose.js";
|
||||
@@ -0,0 +1,2 @@
|
||||
import type { LibDefinition } from '../variable';
|
||||
export declare const es5: LibDefinition;
|
||||
@@ -0,0 +1,64 @@
|
||||
/**
|
||||
* @fileoverview ESLint Parser
|
||||
* @author Nicholas C. Zakas
|
||||
*/
|
||||
/* eslint class-methods-use-this: off -- Anticipate future constructor arguments. */
|
||||
|
||||
"use strict";
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Types
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/** @typedef {import("../linter/vfile.js").VFile} VFile */
|
||||
/** @typedef {import("@eslint/core").Language} Language */
|
||||
/** @typedef {import("@eslint/core").LanguageOptions} LanguageOptions */
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Exports
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* The parser for ESLint.
|
||||
*/
|
||||
class ParserService {
|
||||
/**
|
||||
* Parses the given file synchronously.
|
||||
* @param {VFile} file The file to parse.
|
||||
* @param {{language:Language,languageOptions:LanguageOptions}} config The configuration to use.
|
||||
* @returns {Object} An object with the parsed source code or errors.
|
||||
* @throws {Error} If the parser returns a promise.
|
||||
*/
|
||||
parseSync(file, config) {
|
||||
const { language, languageOptions } = config;
|
||||
const result = language.parse(file, { languageOptions });
|
||||
|
||||
if (typeof result.then === "function") {
|
||||
throw new Error("Unsupported: Language parser returned a promise.");
|
||||
}
|
||||
|
||||
if (result.ok) {
|
||||
return {
|
||||
ok: true,
|
||||
sourceCode: language.createSourceCode(file, result, {
|
||||
languageOptions,
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
// if we made it to here there was an error
|
||||
return {
|
||||
ok: false,
|
||||
errors: result.errors.map(error => ({
|
||||
ruleId: null,
|
||||
fatal: true,
|
||||
severity: 2,
|
||||
message: `Parsing error: ${error.message}`,
|
||||
line: error.line,
|
||||
column: error.column,
|
||||
})),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { ParserService };
|
||||
@@ -0,0 +1,634 @@
|
||||
node-fetch
|
||||
==========
|
||||
|
||||
[![npm version][npm-image]][npm-url]
|
||||
[![build status][travis-image]][travis-url]
|
||||
[![coverage status][codecov-image]][codecov-url]
|
||||
[![install size][install-size-image]][install-size-url]
|
||||
[![Discord][discord-image]][discord-url]
|
||||
|
||||
A light-weight module that brings `window.fetch` to Node.js
|
||||
|
||||
(We are looking for [v2 maintainers and collaborators](https://github.com/bitinn/node-fetch/issues/567))
|
||||
|
||||
[![Backers][opencollective-image]][opencollective-url]
|
||||
|
||||
<!-- TOC -->
|
||||
|
||||
- [Motivation](#motivation)
|
||||
- [Features](#features)
|
||||
- [Difference from client-side fetch](#difference-from-client-side-fetch)
|
||||
- [Installation](#installation)
|
||||
- [Loading and configuring the module](#loading-and-configuring-the-module)
|
||||
- [Common Usage](#common-usage)
|
||||
- [Plain text or HTML](#plain-text-or-html)
|
||||
- [JSON](#json)
|
||||
- [Simple Post](#simple-post)
|
||||
- [Post with JSON](#post-with-json)
|
||||
- [Post with form parameters](#post-with-form-parameters)
|
||||
- [Handling exceptions](#handling-exceptions)
|
||||
- [Handling client and server errors](#handling-client-and-server-errors)
|
||||
- [Advanced Usage](#advanced-usage)
|
||||
- [Streams](#streams)
|
||||
- [Buffer](#buffer)
|
||||
- [Accessing Headers and other Meta data](#accessing-headers-and-other-meta-data)
|
||||
- [Extract Set-Cookie Header](#extract-set-cookie-header)
|
||||
- [Post data using a file stream](#post-data-using-a-file-stream)
|
||||
- [Post with form-data (detect multipart)](#post-with-form-data-detect-multipart)
|
||||
- [Request cancellation with AbortSignal](#request-cancellation-with-abortsignal)
|
||||
- [API](#api)
|
||||
- [fetch(url[, options])](#fetchurl-options)
|
||||
- [Options](#options)
|
||||
- [Class: Request](#class-request)
|
||||
- [Class: Response](#class-response)
|
||||
- [Class: Headers](#class-headers)
|
||||
- [Interface: Body](#interface-body)
|
||||
- [Class: FetchError](#class-fetcherror)
|
||||
- [License](#license)
|
||||
- [Acknowledgement](#acknowledgement)
|
||||
|
||||
<!-- /TOC -->
|
||||
|
||||
## Motivation
|
||||
|
||||
Instead of implementing `XMLHttpRequest` in Node.js to run browser-specific [Fetch polyfill](https://github.com/github/fetch), why not go from native `http` to `fetch` API directly? Hence, `node-fetch`, minimal code for a `window.fetch` compatible API on Node.js runtime.
|
||||
|
||||
See Matt Andrews' [isomorphic-fetch](https://github.com/matthew-andrews/isomorphic-fetch) or Leonardo Quixada's [cross-fetch](https://github.com/lquixada/cross-fetch) for isomorphic usage (exports `node-fetch` for server-side, `whatwg-fetch` for client-side).
|
||||
|
||||
## Features
|
||||
|
||||
- Stay consistent with `window.fetch` API.
|
||||
- Make conscious trade-off when following [WHATWG fetch spec][whatwg-fetch] and [stream spec](https://streams.spec.whatwg.org/) implementation details, document known differences.
|
||||
- Use native promise but allow substituting it with [insert your favorite promise library].
|
||||
- Use native Node streams for body on both request and response.
|
||||
- Decode content encoding (gzip/deflate) properly and convert string output (such as `res.text()` and `res.json()`) to UTF-8 automatically.
|
||||
- Useful extensions such as timeout, redirect limit, response size limit, [explicit errors](ERROR-HANDLING.md) for troubleshooting.
|
||||
|
||||
## Difference from client-side fetch
|
||||
|
||||
- See [Known Differences](LIMITS.md) for details.
|
||||
- If you happen to use a missing feature that `window.fetch` offers, feel free to open an issue.
|
||||
- Pull requests are welcomed too!
|
||||
|
||||
## Installation
|
||||
|
||||
Current stable release (`2.x`)
|
||||
|
||||
```sh
|
||||
$ npm install node-fetch
|
||||
```
|
||||
|
||||
## Loading and configuring the module
|
||||
We suggest you load the module via `require` until the stabilization of ES modules in node:
|
||||
```js
|
||||
const fetch = require('node-fetch');
|
||||
```
|
||||
|
||||
If you are using a Promise library other than native, set it through `fetch.Promise`:
|
||||
```js
|
||||
const Bluebird = require('bluebird');
|
||||
|
||||
fetch.Promise = Bluebird;
|
||||
```
|
||||
|
||||
## Common Usage
|
||||
|
||||
NOTE: The documentation below is up-to-date with `2.x` releases; see the [`1.x` readme](https://github.com/bitinn/node-fetch/blob/1.x/README.md), [changelog](https://github.com/bitinn/node-fetch/blob/1.x/CHANGELOG.md) and [2.x upgrade guide](UPGRADE-GUIDE.md) for the differences.
|
||||
|
||||
#### Plain text or HTML
|
||||
```js
|
||||
fetch('https://github.com/')
|
||||
.then(res => res.text())
|
||||
.then(body => console.log(body));
|
||||
```
|
||||
|
||||
#### JSON
|
||||
|
||||
```js
|
||||
|
||||
fetch('https://api.github.com/users/github')
|
||||
.then(res => res.json())
|
||||
.then(json => console.log(json));
|
||||
```
|
||||
|
||||
#### Simple Post
|
||||
```js
|
||||
fetch('https://httpbin.org/post', { method: 'POST', body: 'a=1' })
|
||||
.then(res => res.json()) // expecting a json response
|
||||
.then(json => console.log(json));
|
||||
```
|
||||
|
||||
#### Post with JSON
|
||||
|
||||
```js
|
||||
const body = { a: 1 };
|
||||
|
||||
fetch('https://httpbin.org/post', {
|
||||
method: 'post',
|
||||
body: JSON.stringify(body),
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
})
|
||||
.then(res => res.json())
|
||||
.then(json => console.log(json));
|
||||
```
|
||||
|
||||
#### Post with form parameters
|
||||
`URLSearchParams` is available in Node.js as of v7.5.0. See [official documentation](https://nodejs.org/api/url.html#url_class_urlsearchparams) for more usage methods.
|
||||
|
||||
NOTE: The `Content-Type` header is only set automatically to `x-www-form-urlencoded` when an instance of `URLSearchParams` is given as such:
|
||||
|
||||
```js
|
||||
const { URLSearchParams } = require('url');
|
||||
|
||||
const params = new URLSearchParams();
|
||||
params.append('a', 1);
|
||||
|
||||
fetch('https://httpbin.org/post', { method: 'POST', body: params })
|
||||
.then(res => res.json())
|
||||
.then(json => console.log(json));
|
||||
```
|
||||
|
||||
#### Handling exceptions
|
||||
NOTE: 3xx-5xx responses are *NOT* exceptions and should be handled in `then()`; see the next section for more information.
|
||||
|
||||
Adding a catch to the fetch promise chain will catch *all* exceptions, such as errors originating from node core libraries, network errors and operational errors, which are instances of FetchError. See the [error handling document](ERROR-HANDLING.md) for more details.
|
||||
|
||||
```js
|
||||
fetch('https://domain.invalid/')
|
||||
.catch(err => console.error(err));
|
||||
```
|
||||
|
||||
#### Handling client and server errors
|
||||
It is common to create a helper function to check that the response contains no client (4xx) or server (5xx) error responses:
|
||||
|
||||
```js
|
||||
function checkStatus(res) {
|
||||
if (res.ok) { // res.status >= 200 && res.status < 300
|
||||
return res;
|
||||
} else {
|
||||
throw MyCustomError(res.statusText);
|
||||
}
|
||||
}
|
||||
|
||||
fetch('https://httpbin.org/status/400')
|
||||
.then(checkStatus)
|
||||
.then(res => console.log('will not get here...'))
|
||||
```
|
||||
|
||||
## Advanced Usage
|
||||
|
||||
#### Streams
|
||||
The "Node.js way" is to use streams when possible:
|
||||
|
||||
```js
|
||||
fetch('https://assets-cdn.github.com/images/modules/logos_page/Octocat.png')
|
||||
.then(res => {
|
||||
const dest = fs.createWriteStream('./octocat.png');
|
||||
res.body.pipe(dest);
|
||||
});
|
||||
```
|
||||
|
||||
In Node.js 14 you can also use async iterators to read `body`; however, be careful to catch
|
||||
errors -- the longer a response runs, the more likely it is to encounter an error.
|
||||
|
||||
```js
|
||||
const fetch = require('node-fetch');
|
||||
const response = await fetch('https://httpbin.org/stream/3');
|
||||
try {
|
||||
for await (const chunk of response.body) {
|
||||
console.dir(JSON.parse(chunk.toString()));
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err.stack);
|
||||
}
|
||||
```
|
||||
|
||||
In Node.js 12 you can also use async iterators to read `body`; however, async iterators with streams
|
||||
did not mature until Node.js 14, so you need to do some extra work to ensure you handle errors
|
||||
directly from the stream and wait on it response to fully close.
|
||||
|
||||
```js
|
||||
const fetch = require('node-fetch');
|
||||
const read = async body => {
|
||||
let error;
|
||||
body.on('error', err => {
|
||||
error = err;
|
||||
});
|
||||
for await (const chunk of body) {
|
||||
console.dir(JSON.parse(chunk.toString()));
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
body.on('close', () => {
|
||||
error ? reject(error) : resolve();
|
||||
});
|
||||
});
|
||||
};
|
||||
try {
|
||||
const response = await fetch('https://httpbin.org/stream/3');
|
||||
await read(response.body);
|
||||
} catch (err) {
|
||||
console.error(err.stack);
|
||||
}
|
||||
```
|
||||
|
||||
#### Buffer
|
||||
If you prefer to cache binary data in full, use buffer(). (NOTE: `buffer()` is a `node-fetch`-only API)
|
||||
|
||||
```js
|
||||
const fileType = require('file-type');
|
||||
|
||||
fetch('https://assets-cdn.github.com/images/modules/logos_page/Octocat.png')
|
||||
.then(res => res.buffer())
|
||||
.then(buffer => fileType(buffer))
|
||||
.then(type => { /* ... */ });
|
||||
```
|
||||
|
||||
#### Accessing Headers and other Meta data
|
||||
```js
|
||||
fetch('https://github.com/')
|
||||
.then(res => {
|
||||
console.log(res.ok);
|
||||
console.log(res.status);
|
||||
console.log(res.statusText);
|
||||
console.log(res.headers.raw());
|
||||
console.log(res.headers.get('content-type'));
|
||||
});
|
||||
```
|
||||
|
||||
#### Extract Set-Cookie Header
|
||||
|
||||
Unlike browsers, you can access raw `Set-Cookie` headers manually using `Headers.raw()`. This is a `node-fetch` only API.
|
||||
|
||||
```js
|
||||
fetch(url).then(res => {
|
||||
// returns an array of values, instead of a string of comma-separated values
|
||||
console.log(res.headers.raw()['set-cookie']);
|
||||
});
|
||||
```
|
||||
|
||||
#### Post data using a file stream
|
||||
|
||||
```js
|
||||
const { createReadStream } = require('fs');
|
||||
|
||||
const stream = createReadStream('input.txt');
|
||||
|
||||
fetch('https://httpbin.org/post', { method: 'POST', body: stream })
|
||||
.then(res => res.json())
|
||||
.then(json => console.log(json));
|
||||
```
|
||||
|
||||
#### Post with form-data (detect multipart)
|
||||
|
||||
```js
|
||||
const FormData = require('form-data');
|
||||
|
||||
const form = new FormData();
|
||||
form.append('a', 1);
|
||||
|
||||
fetch('https://httpbin.org/post', { method: 'POST', body: form })
|
||||
.then(res => res.json())
|
||||
.then(json => console.log(json));
|
||||
|
||||
// OR, using custom headers
|
||||
// NOTE: getHeaders() is non-standard API
|
||||
|
||||
const form = new FormData();
|
||||
form.append('a', 1);
|
||||
|
||||
const options = {
|
||||
method: 'POST',
|
||||
body: form,
|
||||
headers: form.getHeaders()
|
||||
}
|
||||
|
||||
fetch('https://httpbin.org/post', options)
|
||||
.then(res => res.json())
|
||||
.then(json => console.log(json));
|
||||
```
|
||||
|
||||
#### Request cancellation with AbortSignal
|
||||
|
||||
> NOTE: You may cancel streamed requests only on Node >= v8.0.0
|
||||
|
||||
You may cancel requests with `AbortController`. A suggested implementation is [`abort-controller`](https://www.npmjs.com/package/abort-controller).
|
||||
|
||||
An example of timing out a request after 150ms could be achieved as the following:
|
||||
|
||||
```js
|
||||
import AbortController from 'abort-controller';
|
||||
|
||||
const controller = new AbortController();
|
||||
const timeout = setTimeout(
|
||||
() => { controller.abort(); },
|
||||
150,
|
||||
);
|
||||
|
||||
fetch(url, { signal: controller.signal })
|
||||
.then(res => res.json())
|
||||
.then(
|
||||
data => {
|
||||
useData(data)
|
||||
},
|
||||
err => {
|
||||
if (err.name === 'AbortError') {
|
||||
// request was aborted
|
||||
}
|
||||
},
|
||||
)
|
||||
.finally(() => {
|
||||
clearTimeout(timeout);
|
||||
});
|
||||
```
|
||||
|
||||
See [test cases](https://github.com/bitinn/node-fetch/blob/master/test/test.js) for more examples.
|
||||
|
||||
|
||||
## API
|
||||
|
||||
### fetch(url[, options])
|
||||
|
||||
- `url` A string representing the URL for fetching
|
||||
- `options` [Options](#fetch-options) for the HTTP(S) request
|
||||
- Returns: <code>Promise<[Response](#class-response)></code>
|
||||
|
||||
Perform an HTTP(S) fetch.
|
||||
|
||||
`url` should be an absolute url, such as `https://example.com/`. A path-relative URL (`/file/under/root`) or protocol-relative URL (`//can-be-http-or-https.com/`) will result in a rejected `Promise`.
|
||||
|
||||
<a id="fetch-options"></a>
|
||||
### Options
|
||||
|
||||
The default values are shown after each option key.
|
||||
|
||||
```js
|
||||
{
|
||||
// These properties are part of the Fetch Standard
|
||||
method: 'GET',
|
||||
headers: {}, // request headers. format is the identical to that accepted by the Headers constructor (see below)
|
||||
body: null, // request body. can be null, a string, a Buffer, a Blob, or a Node.js Readable stream
|
||||
redirect: 'follow', // set to `manual` to extract redirect headers, `error` to reject redirect
|
||||
signal: null, // pass an instance of AbortSignal to optionally abort requests
|
||||
|
||||
// The following properties are node-fetch extensions
|
||||
follow: 20, // maximum redirect count. 0 to not follow redirect
|
||||
timeout: 0, // req/res timeout in ms, it resets on redirect. 0 to disable (OS limit applies). Signal is recommended instead.
|
||||
compress: true, // support gzip/deflate content encoding. false to disable
|
||||
size: 0, // maximum response body size in bytes. 0 to disable
|
||||
agent: null // http(s).Agent instance or function that returns an instance (see below)
|
||||
}
|
||||
```
|
||||
|
||||
##### Default Headers
|
||||
|
||||
If no values are set, the following request headers will be sent automatically:
|
||||
|
||||
Header | Value
|
||||
------------------- | --------------------------------------------------------
|
||||
`Accept-Encoding` | `gzip,deflate` _(when `options.compress === true`)_
|
||||
`Accept` | `*/*`
|
||||
`Content-Length` | _(automatically calculated, if possible)_
|
||||
`Transfer-Encoding` | `chunked` _(when `req.body` is a stream)_
|
||||
`User-Agent` | `node-fetch/1.0 (+https://github.com/bitinn/node-fetch)`
|
||||
|
||||
Note: when `body` is a `Stream`, `Content-Length` is not set automatically.
|
||||
|
||||
##### Custom Agent
|
||||
|
||||
The `agent` option allows you to specify networking related options which are out of the scope of Fetch, including and not limited to the following:
|
||||
|
||||
- Support self-signed certificate
|
||||
- Use only IPv4 or IPv6
|
||||
- Custom DNS Lookup
|
||||
|
||||
See [`http.Agent`](https://nodejs.org/api/http.html#http_new_agent_options) for more information.
|
||||
|
||||
If no agent is specified, the default agent provided by Node.js is used. Note that [this changed in Node.js 19](https://github.com/nodejs/node/blob/4267b92604ad78584244488e7f7508a690cb80d0/lib/_http_agent.js#L564) to have `keepalive` true by default. If you wish to enable `keepalive` in an earlier version of Node.js, you can override the agent as per the following code sample.
|
||||
|
||||
In addition, the `agent` option accepts a function that returns `http`(s)`.Agent` instance given current [URL](https://nodejs.org/api/url.html), this is useful during a redirection chain across HTTP and HTTPS protocol.
|
||||
|
||||
```js
|
||||
const httpAgent = new http.Agent({
|
||||
keepAlive: true
|
||||
});
|
||||
const httpsAgent = new https.Agent({
|
||||
keepAlive: true
|
||||
});
|
||||
|
||||
const options = {
|
||||
agent: function (_parsedURL) {
|
||||
if (_parsedURL.protocol == 'http:') {
|
||||
return httpAgent;
|
||||
} else {
|
||||
return httpsAgent;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<a id="class-request"></a>
|
||||
### Class: Request
|
||||
|
||||
An HTTP(S) request containing information about URL, method, headers, and the body. This class implements the [Body](#iface-body) interface.
|
||||
|
||||
Due to the nature of Node.js, the following properties are not implemented at this moment:
|
||||
|
||||
- `type`
|
||||
- `destination`
|
||||
- `referrer`
|
||||
- `referrerPolicy`
|
||||
- `mode`
|
||||
- `credentials`
|
||||
- `cache`
|
||||
- `integrity`
|
||||
- `keepalive`
|
||||
|
||||
The following node-fetch extension properties are provided:
|
||||
|
||||
- `follow`
|
||||
- `compress`
|
||||
- `counter`
|
||||
- `agent`
|
||||
|
||||
See [options](#fetch-options) for exact meaning of these extensions.
|
||||
|
||||
#### new Request(input[, options])
|
||||
|
||||
<small>*(spec-compliant)*</small>
|
||||
|
||||
- `input` A string representing a URL, or another `Request` (which will be cloned)
|
||||
- `options` [Options][#fetch-options] for the HTTP(S) request
|
||||
|
||||
Constructs a new `Request` object. The constructor is identical to that in the [browser](https://developer.mozilla.org/en-US/docs/Web/API/Request/Request).
|
||||
|
||||
In most cases, directly `fetch(url, options)` is simpler than creating a `Request` object.
|
||||
|
||||
<a id="class-response"></a>
|
||||
### Class: Response
|
||||
|
||||
An HTTP(S) response. This class implements the [Body](#iface-body) interface.
|
||||
|
||||
The following properties are not implemented in node-fetch at this moment:
|
||||
|
||||
- `Response.error()`
|
||||
- `Response.redirect()`
|
||||
- `type`
|
||||
- `trailer`
|
||||
|
||||
#### new Response([body[, options]])
|
||||
|
||||
<small>*(spec-compliant)*</small>
|
||||
|
||||
- `body` A `String` or [`Readable` stream][node-readable]
|
||||
- `options` A [`ResponseInit`][response-init] options dictionary
|
||||
|
||||
Constructs a new `Response` object. The constructor is identical to that in the [browser](https://developer.mozilla.org/en-US/docs/Web/API/Response/Response).
|
||||
|
||||
Because Node.js does not implement service workers (for which this class was designed), one rarely has to construct a `Response` directly.
|
||||
|
||||
#### response.ok
|
||||
|
||||
<small>*(spec-compliant)*</small>
|
||||
|
||||
Convenience property representing if the request ended normally. Will evaluate to true if the response status was greater than or equal to 200 but smaller than 300.
|
||||
|
||||
#### response.redirected
|
||||
|
||||
<small>*(spec-compliant)*</small>
|
||||
|
||||
Convenience property representing if the request has been redirected at least once. Will evaluate to true if the internal redirect counter is greater than 0.
|
||||
|
||||
<a id="class-headers"></a>
|
||||
### Class: Headers
|
||||
|
||||
This class allows manipulating and iterating over a set of HTTP headers. All methods specified in the [Fetch Standard][whatwg-fetch] are implemented.
|
||||
|
||||
#### new Headers([init])
|
||||
|
||||
<small>*(spec-compliant)*</small>
|
||||
|
||||
- `init` Optional argument to pre-fill the `Headers` object
|
||||
|
||||
Construct a new `Headers` object. `init` can be either `null`, a `Headers` object, an key-value map object or any iterable object.
|
||||
|
||||
```js
|
||||
// Example adapted from https://fetch.spec.whatwg.org/#example-headers-class
|
||||
|
||||
const meta = {
|
||||
'Content-Type': 'text/xml',
|
||||
'Breaking-Bad': '<3'
|
||||
};
|
||||
const headers = new Headers(meta);
|
||||
|
||||
// The above is equivalent to
|
||||
const meta = [
|
||||
[ 'Content-Type', 'text/xml' ],
|
||||
[ 'Breaking-Bad', '<3' ]
|
||||
];
|
||||
const headers = new Headers(meta);
|
||||
|
||||
// You can in fact use any iterable objects, like a Map or even another Headers
|
||||
const meta = new Map();
|
||||
meta.set('Content-Type', 'text/xml');
|
||||
meta.set('Breaking-Bad', '<3');
|
||||
const headers = new Headers(meta);
|
||||
const copyOfHeaders = new Headers(headers);
|
||||
```
|
||||
|
||||
<a id="iface-body"></a>
|
||||
### Interface: Body
|
||||
|
||||
`Body` is an abstract interface with methods that are applicable to both `Request` and `Response` classes.
|
||||
|
||||
The following methods are not yet implemented in node-fetch at this moment:
|
||||
|
||||
- `formData()`
|
||||
|
||||
#### body.body
|
||||
|
||||
<small>*(deviation from spec)*</small>
|
||||
|
||||
* Node.js [`Readable` stream][node-readable]
|
||||
|
||||
Data are encapsulated in the `Body` object. Note that while the [Fetch Standard][whatwg-fetch] requires the property to always be a WHATWG `ReadableStream`, in node-fetch it is a Node.js [`Readable` stream][node-readable].
|
||||
|
||||
#### body.bodyUsed
|
||||
|
||||
<small>*(spec-compliant)*</small>
|
||||
|
||||
* `Boolean`
|
||||
|
||||
A boolean property for if this body has been consumed. Per the specs, a consumed body cannot be used again.
|
||||
|
||||
#### body.arrayBuffer()
|
||||
#### body.blob()
|
||||
#### body.json()
|
||||
#### body.text()
|
||||
|
||||
<small>*(spec-compliant)*</small>
|
||||
|
||||
* Returns: <code>Promise</code>
|
||||
|
||||
Consume the body and return a promise that will resolve to one of these formats.
|
||||
|
||||
#### body.buffer()
|
||||
|
||||
<small>*(node-fetch extension)*</small>
|
||||
|
||||
* Returns: <code>Promise<Buffer></code>
|
||||
|
||||
Consume the body and return a promise that will resolve to a Buffer.
|
||||
|
||||
#### body.textConverted()
|
||||
|
||||
<small>*(node-fetch extension)*</small>
|
||||
|
||||
* Returns: <code>Promise<String></code>
|
||||
|
||||
Identical to `body.text()`, except instead of always converting to UTF-8, encoding sniffing will be performed and text converted to UTF-8 if possible.
|
||||
|
||||
(This API requires an optional dependency of the npm package [encoding](https://www.npmjs.com/package/encoding), which you need to install manually. `webpack` users may see [a warning message](https://github.com/bitinn/node-fetch/issues/412#issuecomment-379007792) due to this optional dependency.)
|
||||
|
||||
<a id="class-fetcherror"></a>
|
||||
### Class: FetchError
|
||||
|
||||
<small>*(node-fetch extension)*</small>
|
||||
|
||||
An operational error in the fetching process. See [ERROR-HANDLING.md][] for more info.
|
||||
|
||||
<a id="class-aborterror"></a>
|
||||
### Class: AbortError
|
||||
|
||||
<small>*(node-fetch extension)*</small>
|
||||
|
||||
An Error thrown when the request is aborted in response to an `AbortSignal`'s `abort` event. It has a `name` property of `AbortError`. See [ERROR-HANDLING.MD][] for more info.
|
||||
|
||||
## Acknowledgement
|
||||
|
||||
Thanks to [github/fetch](https://github.com/github/fetch) for providing a solid implementation reference.
|
||||
|
||||
`node-fetch` v1 was maintained by [@bitinn](https://github.com/bitinn); v2 was maintained by [@TimothyGu](https://github.com/timothygu), [@bitinn](https://github.com/bitinn) and [@jimmywarting](https://github.com/jimmywarting); v2 readme is written by [@jkantr](https://github.com/jkantr).
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
|
||||
[npm-image]: https://flat.badgen.net/npm/v/node-fetch
|
||||
[npm-url]: https://www.npmjs.com/package/node-fetch
|
||||
[travis-image]: https://flat.badgen.net/travis/bitinn/node-fetch
|
||||
[travis-url]: https://travis-ci.org/bitinn/node-fetch
|
||||
[codecov-image]: https://flat.badgen.net/codecov/c/github/bitinn/node-fetch/master
|
||||
[codecov-url]: https://codecov.io/gh/bitinn/node-fetch
|
||||
[install-size-image]: https://flat.badgen.net/packagephobia/install/node-fetch
|
||||
[install-size-url]: https://packagephobia.now.sh/result?p=node-fetch
|
||||
[discord-image]: https://img.shields.io/discord/619915844268326952?color=%237289DA&label=Discord&style=flat-square
|
||||
[discord-url]: https://discord.gg/Zxbndcm
|
||||
[opencollective-image]: https://opencollective.com/node-fetch/backers.svg
|
||||
[opencollective-url]: https://opencollective.com/node-fetch
|
||||
[whatwg-fetch]: https://fetch.spec.whatwg.org/
|
||||
[response-init]: https://fetch.spec.whatwg.org/#responseinit
|
||||
[node-readable]: https://nodejs.org/api/stream.html#stream_readable_streams
|
||||
[mdn-headers]: https://developer.mozilla.org/en-US/docs/Web/API/Headers
|
||||
[LIMITS.md]: https://github.com/bitinn/node-fetch/blob/master/LIMITS.md
|
||||
[ERROR-HANDLING.md]: https://github.com/bitinn/node-fetch/blob/master/ERROR-HANDLING.md
|
||||
[UPGRADE-GUIDE.md]: https://github.com/bitinn/node-fetch/blob/master/UPGRADE-GUIDE.md
|
||||
@@ -0,0 +1,60 @@
|
||||
import { expect, expectTypeOf, test } from "vitest";
|
||||
import * as z from "zod/v4";
|
||||
|
||||
test("instanceof", async () => {
|
||||
class Test {}
|
||||
class Subtest extends Test {}
|
||||
abstract class AbstractBar {
|
||||
constructor(public val: string) {}
|
||||
}
|
||||
class Bar extends AbstractBar {}
|
||||
|
||||
const TestSchema = z.instanceof(Test);
|
||||
const SubtestSchema = z.instanceof(Subtest);
|
||||
const AbstractSchema = z.instanceof(AbstractBar);
|
||||
const BarSchema = z.instanceof(Bar);
|
||||
|
||||
TestSchema.parse(new Test());
|
||||
TestSchema.parse(new Subtest());
|
||||
SubtestSchema.parse(new Subtest());
|
||||
AbstractSchema.parse(new Bar("asdf"));
|
||||
const bar = BarSchema.parse(new Bar("asdf"));
|
||||
expect(bar.val).toEqual("asdf");
|
||||
|
||||
await expect(() => SubtestSchema.parse(new Test())).toThrow();
|
||||
await expect(() => TestSchema.parse(12)).toThrow();
|
||||
|
||||
expectTypeOf<Test>().toEqualTypeOf<z.infer<typeof TestSchema>>();
|
||||
});
|
||||
|
||||
test("instanceof fatal", () => {
|
||||
const schema = z.instanceof(Date).refine((d) => d.toString());
|
||||
const res = schema.safeParse(null);
|
||||
expect(res.success).toBe(false);
|
||||
});
|
||||
|
||||
test("instanceof respects customError", () => {
|
||||
class Test {
|
||||
name!: string;
|
||||
}
|
||||
|
||||
z.config({
|
||||
customError: () => {
|
||||
return "This is invalid!";
|
||||
},
|
||||
});
|
||||
|
||||
const TestSchema = z.instanceof(Test);
|
||||
const result = TestSchema.safeParse("whatever");
|
||||
expect(result.success).toBe(false);
|
||||
if (!result.success) {
|
||||
const issue = result.error.issues[0];
|
||||
expect(issue.code).toBe("invalid_type");
|
||||
if (issue.code === "invalid_type") {
|
||||
expect(issue.expected).toBe("Test");
|
||||
}
|
||||
expect(issue.message).toBe("This is invalid!");
|
||||
}
|
||||
|
||||
z.config({ customError: undefined });
|
||||
});
|
||||
@@ -0,0 +1,18 @@
|
||||
/*! *****************************************************************************
|
||||
Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||
this file except in compliance with the License. You may obtain a copy of the
|
||||
License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
|
||||
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
|
||||
MERCHANTABILITY OR NON-INFRINGEMENT.
|
||||
|
||||
See the Apache Version 2.0 License for specific language governing permissions
|
||||
and limitations under the License.
|
||||
***************************************************************************** */
|
||||
|
||||
|
||||
// This file's contents are now included in the main types file.
|
||||
// The file has been left for backward compatibility.
|
||||
@@ -0,0 +1,35 @@
|
||||
'use strict'
|
||||
|
||||
const { test } = require('tap')
|
||||
const fs = require('fs')
|
||||
const SonicBoom = require('../')
|
||||
const { file } = require('./helper')
|
||||
|
||||
const MAX_WRITE = 16 * 1024
|
||||
|
||||
test('drain deadlock', (t) => {
|
||||
t.plan(4)
|
||||
|
||||
const dest = file()
|
||||
const stream = new SonicBoom({ dest, sync: false, minLength: 9999 })
|
||||
|
||||
t.ok(stream.write(Buffer.alloc(1500).fill('x').toString()))
|
||||
t.ok(stream.write(Buffer.alloc(1500).fill('x').toString()))
|
||||
t.ok(!stream.write(Buffer.alloc(MAX_WRITE).fill('x').toString()))
|
||||
stream.on('drain', () => {
|
||||
t.pass()
|
||||
})
|
||||
})
|
||||
|
||||
test('should throw if minLength >= maxWrite', (t) => {
|
||||
t.plan(1)
|
||||
t.throws(() => {
|
||||
const dest = file()
|
||||
const fd = fs.openSync(dest, 'w')
|
||||
|
||||
SonicBoom({
|
||||
fd,
|
||||
minLength: MAX_WRITE
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,10 @@
|
||||
function _typeof(o) {
|
||||
"@babel/helpers - typeof";
|
||||
|
||||
return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) {
|
||||
return typeof o;
|
||||
} : function (o) {
|
||||
return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;
|
||||
}, _typeof(o);
|
||||
}
|
||||
export { _typeof as default };
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2026-present, rolldown/plugins repository contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,2 @@
|
||||
import type { LibDefinition } from '../variable';
|
||||
export declare const esnext_error: LibDefinition;
|
||||
@@ -0,0 +1,98 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.parseThenCall = parseThenCall;
|
||||
exports.parseCatchCall = parseCatchCall;
|
||||
exports.parseFinallyCall = parseFinallyCall;
|
||||
const utils_1 = require("@typescript-eslint/utils");
|
||||
const misc_1 = require("./misc");
|
||||
/**
|
||||
* Parses a syntactically possible `Promise.then()` call. Does not check the
|
||||
* type of the callee.
|
||||
*/
|
||||
function parseThenCall(node, context) {
|
||||
if (node.callee.type === utils_1.AST_NODE_TYPES.MemberExpression) {
|
||||
const methodName = (0, misc_1.getStaticMemberAccessValue)(node.callee, context);
|
||||
if (methodName === 'then') {
|
||||
if (node.arguments.length >= 1) {
|
||||
if (node.arguments[0].type === utils_1.AST_NODE_TYPES.SpreadElement) {
|
||||
return {
|
||||
object: node.callee.object,
|
||||
};
|
||||
}
|
||||
if (node.arguments.length >= 2) {
|
||||
if (node.arguments[1].type === utils_1.AST_NODE_TYPES.SpreadElement) {
|
||||
return {
|
||||
object: node.callee.object,
|
||||
onFulfilled: node.arguments[0],
|
||||
};
|
||||
}
|
||||
return {
|
||||
object: node.callee.object,
|
||||
onFulfilled: node.arguments[0],
|
||||
onRejected: node.arguments[1],
|
||||
};
|
||||
}
|
||||
return {
|
||||
object: node.callee.object,
|
||||
onFulfilled: node.arguments[0],
|
||||
};
|
||||
}
|
||||
return {
|
||||
object: node.callee.object,
|
||||
};
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
/**
|
||||
* Parses a syntactically possible `Promise.catch()` call. Does not check the
|
||||
* type of the callee.
|
||||
*/
|
||||
function parseCatchCall(node, context) {
|
||||
if (node.callee.type === utils_1.AST_NODE_TYPES.MemberExpression) {
|
||||
const methodName = (0, misc_1.getStaticMemberAccessValue)(node.callee, context);
|
||||
if (methodName === 'catch') {
|
||||
if (node.arguments.length >= 1) {
|
||||
if (node.arguments[0].type === utils_1.AST_NODE_TYPES.SpreadElement) {
|
||||
return {
|
||||
object: node.callee.object,
|
||||
};
|
||||
}
|
||||
return {
|
||||
object: node.callee.object,
|
||||
onRejected: node.arguments[0],
|
||||
};
|
||||
}
|
||||
return {
|
||||
object: node.callee.object,
|
||||
};
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
/**
|
||||
* Parses a syntactically possible `Promise.finally()` call. Does not check the
|
||||
* type of the callee.
|
||||
*/
|
||||
function parseFinallyCall(node, context) {
|
||||
if (node.callee.type === utils_1.AST_NODE_TYPES.MemberExpression) {
|
||||
const methodName = (0, misc_1.getStaticMemberAccessValue)(node.callee, context);
|
||||
if (methodName === 'finally') {
|
||||
if (node.arguments.length >= 1) {
|
||||
if (node.arguments[0].type === utils_1.AST_NODE_TYPES.SpreadElement) {
|
||||
return {
|
||||
object: node.callee.object,
|
||||
};
|
||||
}
|
||||
return {
|
||||
object: node.callee.object,
|
||||
onFinally: node.arguments[0],
|
||||
};
|
||||
}
|
||||
return {
|
||||
object: node.callee.object,
|
||||
};
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
@@ -0,0 +1,644 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.allProcessors = exports.lazyProcessor = exports.optionalProcessor = exports.promiseProcessor = exports.readonlyProcessor = exports.pipeProcessor = exports.catchProcessor = exports.prefaultProcessor = exports.defaultProcessor = exports.nonoptionalProcessor = exports.nullableProcessor = exports.recordProcessor = exports.tupleProcessor = exports.intersectionProcessor = exports.unionProcessor = exports.objectProcessor = exports.arrayProcessor = exports.setProcessor = exports.mapProcessor = exports.transformProcessor = exports.functionProcessor = exports.customProcessor = exports.successProcessor = exports.fileProcessor = exports.templateLiteralProcessor = exports.nanProcessor = exports.literalProcessor = exports.enumProcessor = exports.dateProcessor = exports.unknownProcessor = exports.anyProcessor = exports.neverProcessor = exports.voidProcessor = exports.undefinedProcessor = exports.nullProcessor = exports.symbolProcessor = exports.bigintProcessor = exports.booleanProcessor = exports.numberProcessor = exports.stringProcessor = void 0;
|
||||
exports.toJSONSchema = toJSONSchema;
|
||||
const to_json_schema_js_1 = require("./to-json-schema.cjs");
|
||||
const util_js_1 = require("./util.cjs");
|
||||
const formatMap = {
|
||||
guid: "uuid",
|
||||
url: "uri",
|
||||
datetime: "date-time",
|
||||
json_string: "json-string",
|
||||
regex: "", // do not set
|
||||
};
|
||||
// ==================== SIMPLE TYPE PROCESSORS ====================
|
||||
const stringProcessor = (schema, ctx, _json, _params) => {
|
||||
const json = _json;
|
||||
json.type = "string";
|
||||
const { minimum, maximum, format, patterns, contentEncoding } = schema._zod
|
||||
.bag;
|
||||
if (typeof minimum === "number")
|
||||
json.minLength = minimum;
|
||||
if (typeof maximum === "number")
|
||||
json.maxLength = maximum;
|
||||
// custom pattern overrides format
|
||||
if (format) {
|
||||
json.format = formatMap[format] ?? format;
|
||||
if (json.format === "")
|
||||
delete json.format; // empty format is not valid
|
||||
// JSON Schema format: "time" requires a full time with offset or Z
|
||||
// z.iso.time() does not include timezone information, so format: "time" should never be used
|
||||
if (format === "time") {
|
||||
delete json.format;
|
||||
}
|
||||
}
|
||||
if (contentEncoding)
|
||||
json.contentEncoding = contentEncoding;
|
||||
if (patterns && patterns.size > 0) {
|
||||
const regexes = [...patterns];
|
||||
if (regexes.length === 1)
|
||||
json.pattern = regexes[0].source;
|
||||
else if (regexes.length > 1) {
|
||||
json.allOf = [
|
||||
...regexes.map((regex) => ({
|
||||
...(ctx.target === "draft-07" || ctx.target === "draft-04" || ctx.target === "openapi-3.0"
|
||||
? { type: "string" }
|
||||
: {}),
|
||||
pattern: regex.source,
|
||||
})),
|
||||
];
|
||||
}
|
||||
}
|
||||
};
|
||||
exports.stringProcessor = stringProcessor;
|
||||
const numberProcessor = (schema, ctx, _json, _params) => {
|
||||
const json = _json;
|
||||
const { minimum, maximum, format, multipleOf, exclusiveMaximum, exclusiveMinimum } = schema._zod.bag;
|
||||
if (typeof format === "string" && format.includes("int"))
|
||||
json.type = "integer";
|
||||
else
|
||||
json.type = "number";
|
||||
// when both minimum and exclusiveMinimum exist, pick the more restrictive one
|
||||
const exMin = typeof exclusiveMinimum === "number" && exclusiveMinimum >= (minimum ?? Number.NEGATIVE_INFINITY);
|
||||
const exMax = typeof exclusiveMaximum === "number" && exclusiveMaximum <= (maximum ?? Number.POSITIVE_INFINITY);
|
||||
const legacy = ctx.target === "draft-04" || ctx.target === "openapi-3.0";
|
||||
if (exMin) {
|
||||
if (legacy) {
|
||||
json.minimum = exclusiveMinimum;
|
||||
json.exclusiveMinimum = true;
|
||||
}
|
||||
else {
|
||||
json.exclusiveMinimum = exclusiveMinimum;
|
||||
}
|
||||
}
|
||||
else if (typeof minimum === "number") {
|
||||
json.minimum = minimum;
|
||||
}
|
||||
if (exMax) {
|
||||
if (legacy) {
|
||||
json.maximum = exclusiveMaximum;
|
||||
json.exclusiveMaximum = true;
|
||||
}
|
||||
else {
|
||||
json.exclusiveMaximum = exclusiveMaximum;
|
||||
}
|
||||
}
|
||||
else if (typeof maximum === "number") {
|
||||
json.maximum = maximum;
|
||||
}
|
||||
if (typeof multipleOf === "number")
|
||||
json.multipleOf = multipleOf;
|
||||
};
|
||||
exports.numberProcessor = numberProcessor;
|
||||
const booleanProcessor = (_schema, _ctx, json, _params) => {
|
||||
json.type = "boolean";
|
||||
};
|
||||
exports.booleanProcessor = booleanProcessor;
|
||||
const bigintProcessor = (_schema, ctx, _json, _params) => {
|
||||
if (ctx.unrepresentable === "throw") {
|
||||
throw new Error("BigInt cannot be represented in JSON Schema");
|
||||
}
|
||||
};
|
||||
exports.bigintProcessor = bigintProcessor;
|
||||
const symbolProcessor = (_schema, ctx, _json, _params) => {
|
||||
if (ctx.unrepresentable === "throw") {
|
||||
throw new Error("Symbols cannot be represented in JSON Schema");
|
||||
}
|
||||
};
|
||||
exports.symbolProcessor = symbolProcessor;
|
||||
const nullProcessor = (_schema, ctx, json, _params) => {
|
||||
if (ctx.target === "openapi-3.0") {
|
||||
json.type = "string";
|
||||
json.nullable = true;
|
||||
json.enum = [null];
|
||||
}
|
||||
else {
|
||||
json.type = "null";
|
||||
}
|
||||
};
|
||||
exports.nullProcessor = nullProcessor;
|
||||
const undefinedProcessor = (_schema, ctx, _json, _params) => {
|
||||
if (ctx.unrepresentable === "throw") {
|
||||
throw new Error("Undefined cannot be represented in JSON Schema");
|
||||
}
|
||||
};
|
||||
exports.undefinedProcessor = undefinedProcessor;
|
||||
const voidProcessor = (_schema, ctx, _json, _params) => {
|
||||
if (ctx.unrepresentable === "throw") {
|
||||
throw new Error("Void cannot be represented in JSON Schema");
|
||||
}
|
||||
};
|
||||
exports.voidProcessor = voidProcessor;
|
||||
const neverProcessor = (_schema, _ctx, json, _params) => {
|
||||
json.not = {};
|
||||
};
|
||||
exports.neverProcessor = neverProcessor;
|
||||
const anyProcessor = (_schema, _ctx, _json, _params) => {
|
||||
// empty schema accepts anything
|
||||
};
|
||||
exports.anyProcessor = anyProcessor;
|
||||
const unknownProcessor = (_schema, _ctx, _json, _params) => {
|
||||
// empty schema accepts anything
|
||||
};
|
||||
exports.unknownProcessor = unknownProcessor;
|
||||
const dateProcessor = (_schema, ctx, _json, _params) => {
|
||||
if (ctx.unrepresentable === "throw") {
|
||||
throw new Error("Date cannot be represented in JSON Schema");
|
||||
}
|
||||
};
|
||||
exports.dateProcessor = dateProcessor;
|
||||
const enumProcessor = (schema, _ctx, json, _params) => {
|
||||
const def = schema._zod.def;
|
||||
const values = (0, util_js_1.getEnumValues)(def.entries);
|
||||
// Number enums can have both string and number values
|
||||
if (values.every((v) => typeof v === "number"))
|
||||
json.type = "number";
|
||||
if (values.every((v) => typeof v === "string"))
|
||||
json.type = "string";
|
||||
json.enum = values;
|
||||
};
|
||||
exports.enumProcessor = enumProcessor;
|
||||
const literalProcessor = (schema, ctx, json, _params) => {
|
||||
const def = schema._zod.def;
|
||||
const vals = [];
|
||||
for (const val of def.values) {
|
||||
if (val === undefined) {
|
||||
if (ctx.unrepresentable === "throw") {
|
||||
throw new Error("Literal `undefined` cannot be represented in JSON Schema");
|
||||
}
|
||||
else {
|
||||
// do not add to vals
|
||||
}
|
||||
}
|
||||
else if (typeof val === "bigint") {
|
||||
if (ctx.unrepresentable === "throw") {
|
||||
throw new Error("BigInt literals cannot be represented in JSON Schema");
|
||||
}
|
||||
else {
|
||||
vals.push(Number(val));
|
||||
}
|
||||
}
|
||||
else {
|
||||
vals.push(val);
|
||||
}
|
||||
}
|
||||
if (vals.length === 0) {
|
||||
// do nothing (an undefined literal was stripped)
|
||||
}
|
||||
else if (vals.length === 1) {
|
||||
const val = vals[0];
|
||||
json.type = val === null ? "null" : typeof val;
|
||||
if (ctx.target === "draft-04" || ctx.target === "openapi-3.0") {
|
||||
json.enum = [val];
|
||||
}
|
||||
else {
|
||||
json.const = val;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (vals.every((v) => typeof v === "number"))
|
||||
json.type = "number";
|
||||
if (vals.every((v) => typeof v === "string"))
|
||||
json.type = "string";
|
||||
if (vals.every((v) => typeof v === "boolean"))
|
||||
json.type = "boolean";
|
||||
if (vals.every((v) => v === null))
|
||||
json.type = "null";
|
||||
json.enum = vals;
|
||||
}
|
||||
};
|
||||
exports.literalProcessor = literalProcessor;
|
||||
const nanProcessor = (_schema, ctx, _json, _params) => {
|
||||
if (ctx.unrepresentable === "throw") {
|
||||
throw new Error("NaN cannot be represented in JSON Schema");
|
||||
}
|
||||
};
|
||||
exports.nanProcessor = nanProcessor;
|
||||
const templateLiteralProcessor = (schema, _ctx, json, _params) => {
|
||||
const _json = json;
|
||||
const pattern = schema._zod.pattern;
|
||||
if (!pattern)
|
||||
throw new Error("Pattern not found in template literal");
|
||||
_json.type = "string";
|
||||
_json.pattern = pattern.source;
|
||||
};
|
||||
exports.templateLiteralProcessor = templateLiteralProcessor;
|
||||
const fileProcessor = (schema, _ctx, json, _params) => {
|
||||
const _json = json;
|
||||
const file = {
|
||||
type: "string",
|
||||
format: "binary",
|
||||
contentEncoding: "binary",
|
||||
};
|
||||
const { minimum, maximum, mime } = schema._zod.bag;
|
||||
if (minimum !== undefined)
|
||||
file.minLength = minimum;
|
||||
if (maximum !== undefined)
|
||||
file.maxLength = maximum;
|
||||
if (mime) {
|
||||
if (mime.length === 1) {
|
||||
file.contentMediaType = mime[0];
|
||||
Object.assign(_json, file);
|
||||
}
|
||||
else {
|
||||
Object.assign(_json, file); // shared props at root
|
||||
_json.anyOf = mime.map((m) => ({ contentMediaType: m })); // only contentMediaType differs
|
||||
}
|
||||
}
|
||||
else {
|
||||
Object.assign(_json, file);
|
||||
}
|
||||
};
|
||||
exports.fileProcessor = fileProcessor;
|
||||
const successProcessor = (_schema, _ctx, json, _params) => {
|
||||
json.type = "boolean";
|
||||
};
|
||||
exports.successProcessor = successProcessor;
|
||||
const customProcessor = (_schema, ctx, _json, _params) => {
|
||||
if (ctx.unrepresentable === "throw") {
|
||||
throw new Error("Custom types cannot be represented in JSON Schema");
|
||||
}
|
||||
};
|
||||
exports.customProcessor = customProcessor;
|
||||
const functionProcessor = (_schema, ctx, _json, _params) => {
|
||||
if (ctx.unrepresentable === "throw") {
|
||||
throw new Error("Function types cannot be represented in JSON Schema");
|
||||
}
|
||||
};
|
||||
exports.functionProcessor = functionProcessor;
|
||||
const transformProcessor = (_schema, ctx, _json, _params) => {
|
||||
if (ctx.unrepresentable === "throw") {
|
||||
throw new Error("Transforms cannot be represented in JSON Schema");
|
||||
}
|
||||
};
|
||||
exports.transformProcessor = transformProcessor;
|
||||
const mapProcessor = (_schema, ctx, _json, _params) => {
|
||||
if (ctx.unrepresentable === "throw") {
|
||||
throw new Error("Map cannot be represented in JSON Schema");
|
||||
}
|
||||
};
|
||||
exports.mapProcessor = mapProcessor;
|
||||
const setProcessor = (_schema, ctx, _json, _params) => {
|
||||
if (ctx.unrepresentable === "throw") {
|
||||
throw new Error("Set cannot be represented in JSON Schema");
|
||||
}
|
||||
};
|
||||
exports.setProcessor = setProcessor;
|
||||
// ==================== COMPOSITE TYPE PROCESSORS ====================
|
||||
const arrayProcessor = (schema, ctx, _json, params) => {
|
||||
const json = _json;
|
||||
const def = schema._zod.def;
|
||||
const { minimum, maximum } = schema._zod.bag;
|
||||
if (typeof minimum === "number")
|
||||
json.minItems = minimum;
|
||||
if (typeof maximum === "number")
|
||||
json.maxItems = maximum;
|
||||
json.type = "array";
|
||||
json.items = (0, to_json_schema_js_1.process)(def.element, ctx, {
|
||||
...params,
|
||||
path: [...params.path, "items"],
|
||||
});
|
||||
};
|
||||
exports.arrayProcessor = arrayProcessor;
|
||||
const objectProcessor = (schema, ctx, _json, params) => {
|
||||
const json = _json;
|
||||
const def = schema._zod.def;
|
||||
json.type = "object";
|
||||
json.properties = {};
|
||||
const shape = def.shape;
|
||||
for (const key in shape) {
|
||||
json.properties[key] = (0, to_json_schema_js_1.process)(shape[key], ctx, {
|
||||
...params,
|
||||
path: [...params.path, "properties", key],
|
||||
});
|
||||
}
|
||||
// required keys
|
||||
const allKeys = new Set(Object.keys(shape));
|
||||
const requiredKeys = new Set([...allKeys].filter((key) => {
|
||||
const v = def.shape[key]._zod;
|
||||
if (ctx.io === "input") {
|
||||
return v.optin === undefined;
|
||||
}
|
||||
else {
|
||||
return v.optout === undefined;
|
||||
}
|
||||
}));
|
||||
if (requiredKeys.size > 0) {
|
||||
json.required = Array.from(requiredKeys);
|
||||
}
|
||||
// catchall
|
||||
if (def.catchall?._zod.def.type === "never") {
|
||||
// strict
|
||||
json.additionalProperties = false;
|
||||
}
|
||||
else if (!def.catchall) {
|
||||
// regular
|
||||
if (ctx.io === "output")
|
||||
json.additionalProperties = false;
|
||||
}
|
||||
else if (def.catchall) {
|
||||
json.additionalProperties = (0, to_json_schema_js_1.process)(def.catchall, ctx, {
|
||||
...params,
|
||||
path: [...params.path, "additionalProperties"],
|
||||
});
|
||||
}
|
||||
};
|
||||
exports.objectProcessor = objectProcessor;
|
||||
const unionProcessor = (schema, ctx, json, params) => {
|
||||
const def = schema._zod.def;
|
||||
// Exclusive unions (inclusive === false) use oneOf (exactly one match) instead of anyOf (one or more matches)
|
||||
// This includes both z.xor() and discriminated unions
|
||||
const isExclusive = def.inclusive === false;
|
||||
const options = def.options.map((x, i) => (0, to_json_schema_js_1.process)(x, ctx, {
|
||||
...params,
|
||||
path: [...params.path, isExclusive ? "oneOf" : "anyOf", i],
|
||||
}));
|
||||
if (isExclusive) {
|
||||
json.oneOf = options;
|
||||
}
|
||||
else {
|
||||
json.anyOf = options;
|
||||
}
|
||||
};
|
||||
exports.unionProcessor = unionProcessor;
|
||||
const intersectionProcessor = (schema, ctx, json, params) => {
|
||||
const def = schema._zod.def;
|
||||
const a = (0, to_json_schema_js_1.process)(def.left, ctx, {
|
||||
...params,
|
||||
path: [...params.path, "allOf", 0],
|
||||
});
|
||||
const b = (0, to_json_schema_js_1.process)(def.right, ctx, {
|
||||
...params,
|
||||
path: [...params.path, "allOf", 1],
|
||||
});
|
||||
const isSimpleIntersection = (val) => "allOf" in val && Object.keys(val).length === 1;
|
||||
const allOf = [
|
||||
...(isSimpleIntersection(a) ? a.allOf : [a]),
|
||||
...(isSimpleIntersection(b) ? b.allOf : [b]),
|
||||
];
|
||||
json.allOf = allOf;
|
||||
};
|
||||
exports.intersectionProcessor = intersectionProcessor;
|
||||
const tupleProcessor = (schema, ctx, _json, params) => {
|
||||
const json = _json;
|
||||
const def = schema._zod.def;
|
||||
json.type = "array";
|
||||
const prefixPath = ctx.target === "draft-2020-12" ? "prefixItems" : "items";
|
||||
const restPath = ctx.target === "draft-2020-12" ? "items" : ctx.target === "openapi-3.0" ? "items" : "additionalItems";
|
||||
const prefixItems = def.items.map((x, i) => (0, to_json_schema_js_1.process)(x, ctx, {
|
||||
...params,
|
||||
path: [...params.path, prefixPath, i],
|
||||
}));
|
||||
const rest = def.rest
|
||||
? (0, to_json_schema_js_1.process)(def.rest, ctx, {
|
||||
...params,
|
||||
path: [...params.path, restPath, ...(ctx.target === "openapi-3.0" ? [def.items.length] : [])],
|
||||
})
|
||||
: null;
|
||||
if (ctx.target === "draft-2020-12") {
|
||||
json.prefixItems = prefixItems;
|
||||
if (rest) {
|
||||
json.items = rest;
|
||||
}
|
||||
}
|
||||
else if (ctx.target === "openapi-3.0") {
|
||||
json.items = {
|
||||
anyOf: prefixItems,
|
||||
};
|
||||
if (rest) {
|
||||
json.items.anyOf.push(rest);
|
||||
}
|
||||
json.minItems = prefixItems.length;
|
||||
if (!rest) {
|
||||
json.maxItems = prefixItems.length;
|
||||
}
|
||||
}
|
||||
else {
|
||||
json.items = prefixItems;
|
||||
if (rest) {
|
||||
json.additionalItems = rest;
|
||||
}
|
||||
}
|
||||
// length
|
||||
const { minimum, maximum } = schema._zod.bag;
|
||||
if (typeof minimum === "number")
|
||||
json.minItems = minimum;
|
||||
if (typeof maximum === "number")
|
||||
json.maxItems = maximum;
|
||||
};
|
||||
exports.tupleProcessor = tupleProcessor;
|
||||
const recordProcessor = (schema, ctx, _json, params) => {
|
||||
const json = _json;
|
||||
const def = schema._zod.def;
|
||||
json.type = "object";
|
||||
// For looseRecord with regex patterns, use patternProperties
|
||||
// This correctly represents "only validate keys matching the pattern" semantics
|
||||
// and composes well with allOf (intersections)
|
||||
const keyType = def.keyType;
|
||||
const keyBag = keyType._zod.bag;
|
||||
const patterns = keyBag?.patterns;
|
||||
if (def.mode === "loose" && patterns && patterns.size > 0) {
|
||||
// Use patternProperties for looseRecord with regex patterns
|
||||
const valueSchema = (0, to_json_schema_js_1.process)(def.valueType, ctx, {
|
||||
...params,
|
||||
path: [...params.path, "patternProperties", "*"],
|
||||
});
|
||||
json.patternProperties = {};
|
||||
for (const pattern of patterns) {
|
||||
json.patternProperties[pattern.source] = valueSchema;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Default behavior: use propertyNames + additionalProperties
|
||||
if (ctx.target === "draft-07" || ctx.target === "draft-2020-12") {
|
||||
json.propertyNames = (0, to_json_schema_js_1.process)(def.keyType, ctx, {
|
||||
...params,
|
||||
path: [...params.path, "propertyNames"],
|
||||
});
|
||||
}
|
||||
json.additionalProperties = (0, to_json_schema_js_1.process)(def.valueType, ctx, {
|
||||
...params,
|
||||
path: [...params.path, "additionalProperties"],
|
||||
});
|
||||
}
|
||||
// Add required for keys with discrete values (enum, literal, etc.)
|
||||
const keyValues = keyType._zod.values;
|
||||
if (keyValues) {
|
||||
const validKeyValues = [...keyValues].filter((v) => typeof v === "string" || typeof v === "number");
|
||||
if (validKeyValues.length > 0) {
|
||||
json.required = validKeyValues;
|
||||
}
|
||||
}
|
||||
};
|
||||
exports.recordProcessor = recordProcessor;
|
||||
const nullableProcessor = (schema, ctx, json, params) => {
|
||||
const def = schema._zod.def;
|
||||
const inner = (0, to_json_schema_js_1.process)(def.innerType, ctx, params);
|
||||
const seen = ctx.seen.get(schema);
|
||||
if (ctx.target === "openapi-3.0") {
|
||||
seen.ref = def.innerType;
|
||||
json.nullable = true;
|
||||
}
|
||||
else {
|
||||
json.anyOf = [inner, { type: "null" }];
|
||||
}
|
||||
};
|
||||
exports.nullableProcessor = nullableProcessor;
|
||||
const nonoptionalProcessor = (schema, ctx, _json, params) => {
|
||||
const def = schema._zod.def;
|
||||
(0, to_json_schema_js_1.process)(def.innerType, ctx, params);
|
||||
const seen = ctx.seen.get(schema);
|
||||
seen.ref = def.innerType;
|
||||
};
|
||||
exports.nonoptionalProcessor = nonoptionalProcessor;
|
||||
const defaultProcessor = (schema, ctx, json, params) => {
|
||||
const def = schema._zod.def;
|
||||
(0, to_json_schema_js_1.process)(def.innerType, ctx, params);
|
||||
const seen = ctx.seen.get(schema);
|
||||
seen.ref = def.innerType;
|
||||
json.default = JSON.parse(JSON.stringify(def.defaultValue));
|
||||
};
|
||||
exports.defaultProcessor = defaultProcessor;
|
||||
const prefaultProcessor = (schema, ctx, json, params) => {
|
||||
const def = schema._zod.def;
|
||||
(0, to_json_schema_js_1.process)(def.innerType, ctx, params);
|
||||
const seen = ctx.seen.get(schema);
|
||||
seen.ref = def.innerType;
|
||||
if (ctx.io === "input")
|
||||
json._prefault = JSON.parse(JSON.stringify(def.defaultValue));
|
||||
};
|
||||
exports.prefaultProcessor = prefaultProcessor;
|
||||
const catchProcessor = (schema, ctx, json, params) => {
|
||||
const def = schema._zod.def;
|
||||
(0, to_json_schema_js_1.process)(def.innerType, ctx, params);
|
||||
const seen = ctx.seen.get(schema);
|
||||
seen.ref = def.innerType;
|
||||
let catchValue;
|
||||
try {
|
||||
catchValue = def.catchValue(undefined);
|
||||
}
|
||||
catch {
|
||||
throw new Error("Dynamic catch values are not supported in JSON Schema");
|
||||
}
|
||||
json.default = catchValue;
|
||||
};
|
||||
exports.catchProcessor = catchProcessor;
|
||||
const pipeProcessor = (schema, ctx, _json, params) => {
|
||||
const def = schema._zod.def;
|
||||
const inIsTransform = def.in._zod.traits.has("$ZodTransform");
|
||||
const innerType = ctx.io === "input" ? (inIsTransform ? def.out : def.in) : def.out;
|
||||
(0, to_json_schema_js_1.process)(innerType, ctx, params);
|
||||
const seen = ctx.seen.get(schema);
|
||||
seen.ref = innerType;
|
||||
};
|
||||
exports.pipeProcessor = pipeProcessor;
|
||||
const readonlyProcessor = (schema, ctx, json, params) => {
|
||||
const def = schema._zod.def;
|
||||
(0, to_json_schema_js_1.process)(def.innerType, ctx, params);
|
||||
const seen = ctx.seen.get(schema);
|
||||
seen.ref = def.innerType;
|
||||
json.readOnly = true;
|
||||
};
|
||||
exports.readonlyProcessor = readonlyProcessor;
|
||||
const promiseProcessor = (schema, ctx, _json, params) => {
|
||||
const def = schema._zod.def;
|
||||
(0, to_json_schema_js_1.process)(def.innerType, ctx, params);
|
||||
const seen = ctx.seen.get(schema);
|
||||
seen.ref = def.innerType;
|
||||
};
|
||||
exports.promiseProcessor = promiseProcessor;
|
||||
const optionalProcessor = (schema, ctx, _json, params) => {
|
||||
const def = schema._zod.def;
|
||||
(0, to_json_schema_js_1.process)(def.innerType, ctx, params);
|
||||
const seen = ctx.seen.get(schema);
|
||||
seen.ref = def.innerType;
|
||||
};
|
||||
exports.optionalProcessor = optionalProcessor;
|
||||
const lazyProcessor = (schema, ctx, _json, params) => {
|
||||
const innerType = schema._zod.innerType;
|
||||
(0, to_json_schema_js_1.process)(innerType, ctx, params);
|
||||
const seen = ctx.seen.get(schema);
|
||||
seen.ref = innerType;
|
||||
};
|
||||
exports.lazyProcessor = lazyProcessor;
|
||||
// ==================== ALL PROCESSORS ====================
|
||||
exports.allProcessors = {
|
||||
string: exports.stringProcessor,
|
||||
number: exports.numberProcessor,
|
||||
boolean: exports.booleanProcessor,
|
||||
bigint: exports.bigintProcessor,
|
||||
symbol: exports.symbolProcessor,
|
||||
null: exports.nullProcessor,
|
||||
undefined: exports.undefinedProcessor,
|
||||
void: exports.voidProcessor,
|
||||
never: exports.neverProcessor,
|
||||
any: exports.anyProcessor,
|
||||
unknown: exports.unknownProcessor,
|
||||
date: exports.dateProcessor,
|
||||
enum: exports.enumProcessor,
|
||||
literal: exports.literalProcessor,
|
||||
nan: exports.nanProcessor,
|
||||
template_literal: exports.templateLiteralProcessor,
|
||||
file: exports.fileProcessor,
|
||||
success: exports.successProcessor,
|
||||
custom: exports.customProcessor,
|
||||
function: exports.functionProcessor,
|
||||
transform: exports.transformProcessor,
|
||||
map: exports.mapProcessor,
|
||||
set: exports.setProcessor,
|
||||
array: exports.arrayProcessor,
|
||||
object: exports.objectProcessor,
|
||||
union: exports.unionProcessor,
|
||||
intersection: exports.intersectionProcessor,
|
||||
tuple: exports.tupleProcessor,
|
||||
record: exports.recordProcessor,
|
||||
nullable: exports.nullableProcessor,
|
||||
nonoptional: exports.nonoptionalProcessor,
|
||||
default: exports.defaultProcessor,
|
||||
prefault: exports.prefaultProcessor,
|
||||
catch: exports.catchProcessor,
|
||||
pipe: exports.pipeProcessor,
|
||||
readonly: exports.readonlyProcessor,
|
||||
promise: exports.promiseProcessor,
|
||||
optional: exports.optionalProcessor,
|
||||
lazy: exports.lazyProcessor,
|
||||
};
|
||||
function toJSONSchema(input, params) {
|
||||
if ("_idmap" in input) {
|
||||
// Registry case
|
||||
const registry = input;
|
||||
const ctx = (0, to_json_schema_js_1.initializeContext)({ ...params, processors: exports.allProcessors });
|
||||
const defs = {};
|
||||
// First pass: process all schemas to build the seen map
|
||||
for (const entry of registry._idmap.entries()) {
|
||||
const [_, schema] = entry;
|
||||
(0, to_json_schema_js_1.process)(schema, ctx);
|
||||
}
|
||||
const schemas = {};
|
||||
const external = {
|
||||
registry,
|
||||
uri: params?.uri,
|
||||
defs,
|
||||
};
|
||||
// Update the context with external configuration
|
||||
ctx.external = external;
|
||||
// Second pass: emit each schema
|
||||
for (const entry of registry._idmap.entries()) {
|
||||
const [key, schema] = entry;
|
||||
(0, to_json_schema_js_1.extractDefs)(ctx, schema);
|
||||
schemas[key] = (0, to_json_schema_js_1.finalize)(ctx, schema);
|
||||
}
|
||||
if (Object.keys(defs).length > 0) {
|
||||
const defsSegment = ctx.target === "draft-2020-12" ? "$defs" : "definitions";
|
||||
schemas.__shared = {
|
||||
[defsSegment]: defs,
|
||||
};
|
||||
}
|
||||
return { schemas };
|
||||
}
|
||||
// Single schema case
|
||||
const ctx = (0, to_json_schema_js_1.initializeContext)({ ...params, processors: exports.allProcessors });
|
||||
(0, to_json_schema_js_1.process)(input, ctx);
|
||||
(0, to_json_schema_js_1.extractDefs)(ctx, input);
|
||||
return (0, to_json_schema_js_1.finalize)(ctx, input);
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
var id = 0;
|
||||
function _classPrivateFieldKey(e) {
|
||||
return "__private_" + id++ + "_" + e;
|
||||
}
|
||||
module.exports = _classPrivateFieldKey, module.exports.__esModule = true, module.exports["default"] = module.exports;
|
||||
@@ -0,0 +1,14 @@
|
||||
/* eslint-disable no-new-func */
|
||||
/* globals __non_webpack__require__ */
|
||||
|
||||
const realImport = new Function('modulePath', 'return import(modulePath)')
|
||||
|
||||
function realRequire (modulePath) {
|
||||
if (typeof __non_webpack__require__ === 'function') {
|
||||
return __non_webpack__require__(modulePath)
|
||||
}
|
||||
|
||||
return require(modulePath)
|
||||
}
|
||||
|
||||
module.exports = { realImport, realRequire }
|
||||
@@ -0,0 +1,22 @@
|
||||
// borrowed from tsx implementation:
|
||||
// https://github.com/esbuild-kit/tsx
|
||||
|
||||
const ignoreWarnings = new Set([
|
||||
'--experimental-loader is an experimental feature. This feature could change at any time',
|
||||
'Custom ESM Loaders is an experimental feature. This feature could change at any time',
|
||||
'Custom ESM Loaders is an experimental feature and might change at any time',
|
||||
'VM Modules is an experimental feature and might change at any time',
|
||||
'VM Modules is an experimental feature. This feature could change at any time',
|
||||
'stripTypeScriptTypes is an experimental feature and might change at any time',
|
||||
])
|
||||
|
||||
const { emit } = process
|
||||
|
||||
process.emit = function (event, warning) {
|
||||
if (event === 'warning' && ignoreWarnings.has(warning.message)) {
|
||||
return
|
||||
}
|
||||
|
||||
// eslint-disable-next-line prefer-rest-params
|
||||
return Reflect.apply(emit, this, arguments)
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import { _ as _overload_yield } from "./_overload_yield.js";
|
||||
|
||||
function _await_async_generator(value) {
|
||||
return new _overload_yield(value, /* kind: await */ 0);
|
||||
}
|
||||
export { _await_async_generator as _ };
|
||||
@@ -0,0 +1,378 @@
|
||||
/**
|
||||
* @fileoverview Rule to disallow use of unmodified expressions in loop conditions
|
||||
* @author Toru Nagashima
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Requirements
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const Traverser = require("../shared/traverser"),
|
||||
astUtils = require("./utils/ast-utils");
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Types
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** @typedef {import("eslint-scope").Variable} Variable */
|
||||
/** @typedef {import("eslint-scope").Reference} Reference */
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Helpers
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const SENTINEL_PATTERN =
|
||||
/(?:(?:Call|Class|Function|Member|New|Yield)Expression|Statement|Declaration)$/u;
|
||||
const LOOP_PATTERN = /^(?:DoWhile|For|While)Statement$/u; // for-in/of statements don't have `test` property.
|
||||
const GROUP_PATTERN = /^(?:BinaryExpression|ConditionalExpression)$/u;
|
||||
const SKIP_PATTERN = /^(?:ArrowFunction|Class|Function)Expression$/u;
|
||||
const DYNAMIC_PATTERN = /^(?:Call|Member|New|TaggedTemplate|Yield)Expression$/u;
|
||||
|
||||
/**
|
||||
* @typedef {Object} LoopConditionInfo
|
||||
* @property {Reference} reference - The reference.
|
||||
* @property {ASTNode} group - BinaryExpression or ConditionalExpression nodes
|
||||
* that the reference is belonging to.
|
||||
* @property {Function} isInLoop - The predicate which checks a given reference
|
||||
* is in this loop.
|
||||
* @property {boolean} modified - The flag that the reference is modified in
|
||||
* this loop.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Checks whether or not a given reference is a write reference.
|
||||
* @param {Reference} reference A reference to check.
|
||||
* @returns {boolean} `true` if the reference is a write reference.
|
||||
*/
|
||||
function isWriteReference(reference) {
|
||||
if (reference.init) {
|
||||
const def = reference.resolved && reference.resolved.defs[0];
|
||||
|
||||
if (!def || def.type !== "Variable" || def.parent.kind !== "var") {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return reference.isWrite();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether or not a given loop condition info does not have the modified
|
||||
* flag.
|
||||
* @param {LoopConditionInfo} condition A loop condition info to check.
|
||||
* @returns {boolean} `true` if the loop condition info is "unmodified".
|
||||
*/
|
||||
function isUnmodified(condition) {
|
||||
return !condition.modified;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether or not a given loop condition info does not have the modified
|
||||
* flag and does not have the group this condition belongs to.
|
||||
* @param {LoopConditionInfo} condition A loop condition info to check.
|
||||
* @returns {boolean} `true` if the loop condition info is "unmodified".
|
||||
*/
|
||||
function isUnmodifiedAndNotBelongToGroup(condition) {
|
||||
return !(condition.modified || condition.group);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether or not a given reference is inside of a given node.
|
||||
* @param {ASTNode} node A node to check.
|
||||
* @param {Reference} reference A reference to check.
|
||||
* @returns {boolean} `true` if the reference is inside of the node.
|
||||
*/
|
||||
function isInRange(node, reference) {
|
||||
const or = node.range;
|
||||
const ir = reference.identifier.range;
|
||||
|
||||
return or[0] <= ir[0] && ir[1] <= or[1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether or not a given reference is inside of a loop node's condition.
|
||||
* @param {ASTNode} node A node to check.
|
||||
* @param {Reference} reference A reference to check.
|
||||
* @returns {boolean} `true` if the reference is inside of the loop node's
|
||||
* condition.
|
||||
*/
|
||||
const isInLoop = {
|
||||
WhileStatement: isInRange,
|
||||
DoWhileStatement: isInRange,
|
||||
ForStatement(node, reference) {
|
||||
return (
|
||||
isInRange(node, reference) &&
|
||||
!(node.init && isInRange(node.init, reference))
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the function which encloses a given reference.
|
||||
* This supports only FunctionDeclaration.
|
||||
* @param {Reference} reference A reference to get.
|
||||
* @returns {ASTNode|null} The function node or null.
|
||||
*/
|
||||
function getEncloseFunctionDeclaration(reference) {
|
||||
let node = reference.identifier;
|
||||
|
||||
while (node) {
|
||||
if (node.type === "FunctionDeclaration") {
|
||||
return node.id ? node : null;
|
||||
}
|
||||
|
||||
node = node.parent;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a given modifier is in a loop.
|
||||
*
|
||||
* Besides checking for the condition being in the loop, this also checks
|
||||
* whether the function that this modifier is belonging to is called
|
||||
* in the loop.
|
||||
* @param {LoopConditionInfo} condition The condition to check.
|
||||
* @param {Reference} modifier The modifier to check.
|
||||
* @returns {boolean} `true` if the modifier is in a loop.
|
||||
*/
|
||||
function hasModifierInLoop(condition, modifier) {
|
||||
if (condition.isInLoop(modifier)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const funcNode = getEncloseFunctionDeclaration(modifier);
|
||||
|
||||
if (!funcNode) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const funcVar = astUtils.getVariableByName(
|
||||
modifier.from.upper,
|
||||
funcNode.id.name,
|
||||
);
|
||||
|
||||
return Boolean(funcVar && funcVar.references.some(condition.isInLoop));
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the "modified" flags of given loop conditions with given modifiers.
|
||||
* @param {LoopConditionInfo[]} conditions The loop conditions to be updated.
|
||||
* @param {Reference[]} modifiers The references to update.
|
||||
* @returns {void}
|
||||
*/
|
||||
function updateModifiedFlag(conditions, modifiers) {
|
||||
for (let i = 0; i < conditions.length; ++i) {
|
||||
const condition = conditions[i];
|
||||
|
||||
for (let j = 0; !condition.modified && j < modifiers.length; ++j) {
|
||||
const modifier = modifiers[j];
|
||||
|
||||
condition.modified = hasModifierInLoop(condition, modifier);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Rule Definition
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** @type {import('../types').Rule.RuleModule} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
type: "problem",
|
||||
|
||||
docs: {
|
||||
description: "Disallow unmodified loop conditions",
|
||||
recommended: false,
|
||||
url: "https://eslint.org/docs/latest/rules/no-unmodified-loop-condition",
|
||||
},
|
||||
|
||||
schema: [],
|
||||
|
||||
messages: {
|
||||
loopConditionNotModified:
|
||||
"'{{name}}' is not modified in this loop.",
|
||||
},
|
||||
},
|
||||
|
||||
create(context) {
|
||||
const sourceCode = context.sourceCode;
|
||||
let groupMap = null;
|
||||
|
||||
/**
|
||||
* Reports a given condition info.
|
||||
* @param {LoopConditionInfo} condition A loop condition info to report.
|
||||
* @returns {void}
|
||||
*/
|
||||
function report(condition) {
|
||||
const node = condition.reference.identifier;
|
||||
|
||||
context.report({
|
||||
node,
|
||||
messageId: "loopConditionNotModified",
|
||||
data: node,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers given conditions to the group the condition belongs to.
|
||||
* @param {LoopConditionInfo[]} conditions A loop condition info to
|
||||
* register.
|
||||
* @returns {void}
|
||||
*/
|
||||
function registerConditionsToGroup(conditions) {
|
||||
for (let i = 0; i < conditions.length; ++i) {
|
||||
const condition = conditions[i];
|
||||
|
||||
if (condition.group) {
|
||||
let group = groupMap.get(condition.group);
|
||||
|
||||
if (!group) {
|
||||
group = [];
|
||||
groupMap.set(condition.group, group);
|
||||
}
|
||||
group.push(condition);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reports references which are inside of unmodified groups.
|
||||
* @param {LoopConditionInfo[]} conditions A loop condition info to report.
|
||||
* @returns {void}
|
||||
*/
|
||||
function checkConditionsInGroup(conditions) {
|
||||
if (conditions.every(isUnmodified)) {
|
||||
conditions.forEach(report);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether or not a given group node has any dynamic elements.
|
||||
* @param {ASTNode} root A node to check.
|
||||
* This node is one of BinaryExpression or ConditionalExpression.
|
||||
* @returns {boolean} `true` if the node is dynamic.
|
||||
*/
|
||||
function hasDynamicExpressions(root) {
|
||||
let retv = false;
|
||||
|
||||
Traverser.traverse(root, {
|
||||
visitorKeys: sourceCode.visitorKeys,
|
||||
enter(node) {
|
||||
if (DYNAMIC_PATTERN.test(node.type)) {
|
||||
retv = true;
|
||||
this.break();
|
||||
} else if (SKIP_PATTERN.test(node.type)) {
|
||||
this.skip();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
return retv;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the loop condition information from a given reference.
|
||||
* @param {Reference} reference A reference to create.
|
||||
* @returns {LoopConditionInfo|null} Created loop condition info, or null.
|
||||
*/
|
||||
function toLoopCondition(reference) {
|
||||
if (reference.init) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let group = null;
|
||||
let child = reference.identifier;
|
||||
let node = child.parent;
|
||||
|
||||
while (node) {
|
||||
if (SENTINEL_PATTERN.test(node.type)) {
|
||||
if (LOOP_PATTERN.test(node.type) && node.test === child) {
|
||||
// This reference is inside of a loop condition.
|
||||
return {
|
||||
reference,
|
||||
group,
|
||||
isInLoop: isInLoop[node.type].bind(null, node),
|
||||
modified: false,
|
||||
};
|
||||
}
|
||||
|
||||
// This reference is outside of a loop condition.
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* If it's inside of a group, OK if either operand is modified.
|
||||
* So stores the group this reference belongs to.
|
||||
*/
|
||||
if (GROUP_PATTERN.test(node.type)) {
|
||||
// If this expression is dynamic, no need to check.
|
||||
if (hasDynamicExpressions(node)) {
|
||||
break;
|
||||
} else {
|
||||
group = node;
|
||||
}
|
||||
}
|
||||
|
||||
child = node;
|
||||
node = node.parent;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds unmodified references which are inside of a loop condition.
|
||||
* Then reports the references which are outside of groups.
|
||||
* @param {Variable} variable A variable to report.
|
||||
* @returns {void}
|
||||
*/
|
||||
function checkReferences(variable) {
|
||||
// Gets references that exist in loop conditions.
|
||||
const conditions = variable.references
|
||||
.map(toLoopCondition)
|
||||
.filter(Boolean);
|
||||
|
||||
if (conditions.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Registers the conditions to belonging groups.
|
||||
registerConditionsToGroup(conditions);
|
||||
|
||||
// Check the conditions are modified.
|
||||
const modifiers = variable.references.filter(isWriteReference);
|
||||
|
||||
if (modifiers.length > 0) {
|
||||
updateModifiedFlag(conditions, modifiers);
|
||||
}
|
||||
|
||||
/*
|
||||
* Reports the conditions which are not belonging to groups.
|
||||
* Others will be reported after all variables are done.
|
||||
*/
|
||||
conditions.filter(isUnmodifiedAndNotBelongToGroup).forEach(report);
|
||||
}
|
||||
|
||||
return {
|
||||
"Program:exit"(node) {
|
||||
const queue = [sourceCode.getScope(node)];
|
||||
|
||||
groupMap = new Map();
|
||||
|
||||
let scope;
|
||||
|
||||
while ((scope = queue.pop())) {
|
||||
queue.push(...scope.childScopes);
|
||||
scope.variables.forEach(checkReferences);
|
||||
}
|
||||
|
||||
groupMap.forEach(checkConditionsInGroup);
|
||||
groupMap = null;
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,2 @@
|
||||
import type { LibDefinition } from '../variable';
|
||||
export declare const es2023_full: LibDefinition;
|
||||
@@ -0,0 +1,562 @@
|
||||
/* eslint no-unused-vars: ["error", { "varsIgnorePattern": "^Duplex$", "caughtErrors": "none" }] */
|
||||
|
||||
'use strict';
|
||||
|
||||
const EventEmitter = require('events');
|
||||
const http = require('http');
|
||||
const { Duplex } = require('stream');
|
||||
const { createHash } = require('crypto');
|
||||
|
||||
const extension = require('./extension');
|
||||
const PerMessageDeflate = require('./permessage-deflate');
|
||||
const subprotocol = require('./subprotocol');
|
||||
const WebSocket = require('./websocket');
|
||||
const { CLOSE_TIMEOUT, GUID, kWebSocket } = require('./constants');
|
||||
|
||||
const keyRegex = /^[+/0-9A-Za-z]{22}==$/;
|
||||
|
||||
const RUNNING = 0;
|
||||
const CLOSING = 1;
|
||||
const CLOSED = 2;
|
||||
|
||||
/**
|
||||
* Class representing a WebSocket server.
|
||||
*
|
||||
* @extends EventEmitter
|
||||
*/
|
||||
class WebSocketServer extends EventEmitter {
|
||||
/**
|
||||
* Create a `WebSocketServer` instance.
|
||||
*
|
||||
* @param {Object} options Configuration options
|
||||
* @param {Boolean} [options.allowSynchronousEvents=true] Specifies whether
|
||||
* any of the `'message'`, `'ping'`, and `'pong'` events can be emitted
|
||||
* multiple times in the same tick
|
||||
* @param {Boolean} [options.autoPong=true] Specifies whether or not to
|
||||
* automatically send a pong in response to a ping
|
||||
* @param {Number} [options.backlog=511] The maximum length of the queue of
|
||||
* pending connections
|
||||
* @param {Boolean} [options.clientTracking=true] Specifies whether or not to
|
||||
* track clients
|
||||
* @param {Number} [options.closeTimeout=30000] Duration in milliseconds to
|
||||
* wait for the closing handshake to finish after `websocket.close()` is
|
||||
* called
|
||||
* @param {Function} [options.handleProtocols] A hook to handle protocols
|
||||
* @param {String} [options.host] The hostname where to bind the server
|
||||
* @param {Number} [options.maxBufferedChunks=262144] The maximum number of
|
||||
* buffered data chunks
|
||||
* @param {Number} [options.maxFragments=16384] The maximum number of message
|
||||
* fragments
|
||||
* @param {Number} [options.maxPayload=104857600] The maximum allowed message
|
||||
* size
|
||||
* @param {Boolean} [options.noServer=false] Enable no server mode
|
||||
* @param {String} [options.path] Accept only connections matching this path
|
||||
* @param {(Boolean|Object)} [options.perMessageDeflate=false] Enable/disable
|
||||
* permessage-deflate
|
||||
* @param {Number} [options.port] The port where to bind the server
|
||||
* @param {(http.Server|https.Server)} [options.server] A pre-created HTTP/S
|
||||
* server to use
|
||||
* @param {Boolean} [options.skipUTF8Validation=false] Specifies whether or
|
||||
* not to skip UTF-8 validation for text and close messages
|
||||
* @param {Function} [options.verifyClient] A hook to reject connections
|
||||
* @param {Function} [options.WebSocket=WebSocket] Specifies the `WebSocket`
|
||||
* class to use. It must be the `WebSocket` class or class that extends it
|
||||
* @param {Function} [callback] A listener for the `listening` event
|
||||
*/
|
||||
constructor(options, callback) {
|
||||
super();
|
||||
|
||||
options = {
|
||||
allowSynchronousEvents: true,
|
||||
autoPong: true,
|
||||
maxBufferedChunks: 256 * 1024,
|
||||
maxFragments: 16 * 1024,
|
||||
maxPayload: 100 * 1024 * 1024,
|
||||
skipUTF8Validation: false,
|
||||
perMessageDeflate: false,
|
||||
handleProtocols: null,
|
||||
clientTracking: true,
|
||||
closeTimeout: CLOSE_TIMEOUT,
|
||||
verifyClient: null,
|
||||
noServer: false,
|
||||
backlog: null, // use default (511 as implemented in net.js)
|
||||
server: null,
|
||||
host: null,
|
||||
path: null,
|
||||
port: null,
|
||||
WebSocket,
|
||||
...options
|
||||
};
|
||||
|
||||
if (
|
||||
(options.port == null && !options.server && !options.noServer) ||
|
||||
(options.port != null && (options.server || options.noServer)) ||
|
||||
(options.server && options.noServer)
|
||||
) {
|
||||
throw new TypeError(
|
||||
'One and only one of the "port", "server", or "noServer" options ' +
|
||||
'must be specified'
|
||||
);
|
||||
}
|
||||
|
||||
if (options.port != null) {
|
||||
this._server = http.createServer((req, res) => {
|
||||
const body = http.STATUS_CODES[426];
|
||||
|
||||
res.writeHead(426, {
|
||||
'Content-Length': body.length,
|
||||
'Content-Type': 'text/plain'
|
||||
});
|
||||
res.end(body);
|
||||
});
|
||||
this._server.listen(
|
||||
options.port,
|
||||
options.host,
|
||||
options.backlog,
|
||||
callback
|
||||
);
|
||||
} else if (options.server) {
|
||||
this._server = options.server;
|
||||
}
|
||||
|
||||
if (this._server) {
|
||||
const emitConnection = this.emit.bind(this, 'connection');
|
||||
|
||||
this._removeListeners = addListeners(this._server, {
|
||||
listening: this.emit.bind(this, 'listening'),
|
||||
error: this.emit.bind(this, 'error'),
|
||||
upgrade: (req, socket, head) => {
|
||||
this.handleUpgrade(req, socket, head, emitConnection);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (options.perMessageDeflate === true) options.perMessageDeflate = {};
|
||||
if (options.clientTracking) {
|
||||
this.clients = new Set();
|
||||
this._shouldEmitClose = false;
|
||||
}
|
||||
|
||||
this.options = options;
|
||||
this._state = RUNNING;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the bound address, the address family name, and port of the server
|
||||
* as reported by the operating system if listening on an IP socket.
|
||||
* If the server is listening on a pipe or UNIX domain socket, the name is
|
||||
* returned as a string.
|
||||
*
|
||||
* @return {(Object|String|null)} The address of the server
|
||||
* @public
|
||||
*/
|
||||
address() {
|
||||
if (this.options.noServer) {
|
||||
throw new Error('The server is operating in "noServer" mode');
|
||||
}
|
||||
|
||||
if (!this._server) return null;
|
||||
return this._server.address();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the server from accepting new connections and emit the `'close'` event
|
||||
* when all existing connections are closed.
|
||||
*
|
||||
* @param {Function} [cb] A one-time listener for the `'close'` event
|
||||
* @public
|
||||
*/
|
||||
close(cb) {
|
||||
if (this._state === CLOSED) {
|
||||
if (cb) {
|
||||
this.once('close', () => {
|
||||
cb(new Error('The server is not running'));
|
||||
});
|
||||
}
|
||||
|
||||
process.nextTick(emitClose, this);
|
||||
return;
|
||||
}
|
||||
|
||||
if (cb) this.once('close', cb);
|
||||
|
||||
if (this._state === CLOSING) return;
|
||||
this._state = CLOSING;
|
||||
|
||||
if (this.options.noServer || this.options.server) {
|
||||
if (this._server) {
|
||||
this._removeListeners();
|
||||
this._removeListeners = this._server = null;
|
||||
}
|
||||
|
||||
if (this.clients) {
|
||||
if (!this.clients.size) {
|
||||
process.nextTick(emitClose, this);
|
||||
} else {
|
||||
this._shouldEmitClose = true;
|
||||
}
|
||||
} else {
|
||||
process.nextTick(emitClose, this);
|
||||
}
|
||||
} else {
|
||||
const server = this._server;
|
||||
|
||||
this._removeListeners();
|
||||
this._removeListeners = this._server = null;
|
||||
|
||||
//
|
||||
// The HTTP/S server was created internally. Close it, and rely on its
|
||||
// `'close'` event.
|
||||
//
|
||||
server.close(() => {
|
||||
emitClose(this);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* See if a given request should be handled by this server instance.
|
||||
*
|
||||
* @param {http.IncomingMessage} req Request object to inspect
|
||||
* @return {Boolean} `true` if the request is valid, else `false`
|
||||
* @public
|
||||
*/
|
||||
shouldHandle(req) {
|
||||
if (this.options.path) {
|
||||
const index = req.url.indexOf('?');
|
||||
const pathname = index !== -1 ? req.url.slice(0, index) : req.url;
|
||||
|
||||
if (pathname !== this.options.path) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a HTTP Upgrade request.
|
||||
*
|
||||
* @param {http.IncomingMessage} req The request object
|
||||
* @param {Duplex} socket The network socket between the server and client
|
||||
* @param {Buffer} head The first packet of the upgraded stream
|
||||
* @param {Function} cb Callback
|
||||
* @public
|
||||
*/
|
||||
handleUpgrade(req, socket, head, cb) {
|
||||
socket.on('error', socketOnError);
|
||||
|
||||
const key = req.headers['sec-websocket-key'];
|
||||
const upgrade = req.headers.upgrade;
|
||||
const version = +req.headers['sec-websocket-version'];
|
||||
|
||||
if (req.method !== 'GET') {
|
||||
const message = 'Invalid HTTP method';
|
||||
abortHandshakeOrEmitwsClientError(this, req, socket, 405, message);
|
||||
return;
|
||||
}
|
||||
|
||||
if (upgrade === undefined || upgrade.toLowerCase() !== 'websocket') {
|
||||
const message = 'Invalid Upgrade header';
|
||||
abortHandshakeOrEmitwsClientError(this, req, socket, 400, message);
|
||||
return;
|
||||
}
|
||||
|
||||
if (key === undefined || !keyRegex.test(key)) {
|
||||
const message = 'Missing or invalid Sec-WebSocket-Key header';
|
||||
abortHandshakeOrEmitwsClientError(this, req, socket, 400, message);
|
||||
return;
|
||||
}
|
||||
|
||||
if (version !== 13 && version !== 8) {
|
||||
const message = 'Missing or invalid Sec-WebSocket-Version header';
|
||||
abortHandshakeOrEmitwsClientError(this, req, socket, 400, message, {
|
||||
'Sec-WebSocket-Version': '13, 8'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.shouldHandle(req)) {
|
||||
abortHandshake(socket, 400);
|
||||
return;
|
||||
}
|
||||
|
||||
const secWebSocketProtocol = req.headers['sec-websocket-protocol'];
|
||||
let protocols = new Set();
|
||||
|
||||
if (secWebSocketProtocol !== undefined) {
|
||||
try {
|
||||
protocols = subprotocol.parse(secWebSocketProtocol);
|
||||
} catch (err) {
|
||||
const message = 'Invalid Sec-WebSocket-Protocol header';
|
||||
abortHandshakeOrEmitwsClientError(this, req, socket, 400, message);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const secWebSocketExtensions = req.headers['sec-websocket-extensions'];
|
||||
const extensions = {};
|
||||
|
||||
if (
|
||||
this.options.perMessageDeflate &&
|
||||
secWebSocketExtensions !== undefined
|
||||
) {
|
||||
const perMessageDeflate = new PerMessageDeflate({
|
||||
...this.options.perMessageDeflate,
|
||||
isServer: true,
|
||||
maxPayload: this.options.maxPayload
|
||||
});
|
||||
|
||||
try {
|
||||
const offers = extension.parse(secWebSocketExtensions);
|
||||
|
||||
if (offers[PerMessageDeflate.extensionName]) {
|
||||
perMessageDeflate.accept(offers[PerMessageDeflate.extensionName]);
|
||||
extensions[PerMessageDeflate.extensionName] = perMessageDeflate;
|
||||
}
|
||||
} catch (err) {
|
||||
const message =
|
||||
'Invalid or unacceptable Sec-WebSocket-Extensions header';
|
||||
abortHandshakeOrEmitwsClientError(this, req, socket, 400, message);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Optionally call external client verification handler.
|
||||
//
|
||||
if (this.options.verifyClient) {
|
||||
const info = {
|
||||
origin:
|
||||
req.headers[`${version === 8 ? 'sec-websocket-origin' : 'origin'}`],
|
||||
secure: !!(req.socket.authorized || req.socket.encrypted),
|
||||
req
|
||||
};
|
||||
|
||||
if (this.options.verifyClient.length === 2) {
|
||||
this.options.verifyClient(info, (verified, code, message, headers) => {
|
||||
if (!verified) {
|
||||
return abortHandshake(socket, code || 401, message, headers);
|
||||
}
|
||||
|
||||
this.completeUpgrade(
|
||||
extensions,
|
||||
key,
|
||||
protocols,
|
||||
req,
|
||||
socket,
|
||||
head,
|
||||
cb
|
||||
);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.options.verifyClient(info)) return abortHandshake(socket, 401);
|
||||
}
|
||||
|
||||
this.completeUpgrade(extensions, key, protocols, req, socket, head, cb);
|
||||
}
|
||||
|
||||
/**
|
||||
* Upgrade the connection to WebSocket.
|
||||
*
|
||||
* @param {Object} extensions The accepted extensions
|
||||
* @param {String} key The value of the `Sec-WebSocket-Key` header
|
||||
* @param {Set} protocols The subprotocols
|
||||
* @param {http.IncomingMessage} req The request object
|
||||
* @param {Duplex} socket The network socket between the server and client
|
||||
* @param {Buffer} head The first packet of the upgraded stream
|
||||
* @param {Function} cb Callback
|
||||
* @throws {Error} If called more than once with the same socket
|
||||
* @private
|
||||
*/
|
||||
completeUpgrade(extensions, key, protocols, req, socket, head, cb) {
|
||||
//
|
||||
// Destroy the socket if the client has already sent a FIN packet.
|
||||
//
|
||||
if (!socket.readable || !socket.writable) return socket.destroy();
|
||||
|
||||
if (socket[kWebSocket]) {
|
||||
throw new Error(
|
||||
'server.handleUpgrade() was called more than once with the same ' +
|
||||
'socket, possibly due to a misconfiguration'
|
||||
);
|
||||
}
|
||||
|
||||
if (this._state > RUNNING) return abortHandshake(socket, 503);
|
||||
|
||||
const digest = createHash('sha1')
|
||||
.update(key + GUID)
|
||||
.digest('base64');
|
||||
|
||||
const headers = [
|
||||
'HTTP/1.1 101 Switching Protocols',
|
||||
'Upgrade: websocket',
|
||||
'Connection: Upgrade',
|
||||
`Sec-WebSocket-Accept: ${digest}`
|
||||
];
|
||||
|
||||
const ws = new this.options.WebSocket(null, undefined, this.options);
|
||||
|
||||
if (protocols.size) {
|
||||
//
|
||||
// Optionally call external protocol selection handler.
|
||||
//
|
||||
const protocol = this.options.handleProtocols
|
||||
? this.options.handleProtocols(protocols, req)
|
||||
: protocols.values().next().value;
|
||||
|
||||
if (protocol) {
|
||||
headers.push(`Sec-WebSocket-Protocol: ${protocol}`);
|
||||
ws._protocol = protocol;
|
||||
}
|
||||
}
|
||||
|
||||
if (extensions[PerMessageDeflate.extensionName]) {
|
||||
const params = extensions[PerMessageDeflate.extensionName].params;
|
||||
const value = extension.format({
|
||||
[PerMessageDeflate.extensionName]: [params]
|
||||
});
|
||||
headers.push(`Sec-WebSocket-Extensions: ${value}`);
|
||||
ws._extensions = extensions;
|
||||
}
|
||||
|
||||
//
|
||||
// Allow external modification/inspection of handshake headers.
|
||||
//
|
||||
this.emit('headers', headers, req);
|
||||
|
||||
socket.write(headers.concat('\r\n').join('\r\n'));
|
||||
socket.removeListener('error', socketOnError);
|
||||
|
||||
ws.setSocket(socket, head, {
|
||||
allowSynchronousEvents: this.options.allowSynchronousEvents,
|
||||
maxBufferedChunks: this.options.maxBufferedChunks,
|
||||
maxFragments: this.options.maxFragments,
|
||||
maxPayload: this.options.maxPayload,
|
||||
skipUTF8Validation: this.options.skipUTF8Validation
|
||||
});
|
||||
|
||||
if (this.clients) {
|
||||
this.clients.add(ws);
|
||||
ws.on('close', () => {
|
||||
this.clients.delete(ws);
|
||||
|
||||
if (this._shouldEmitClose && !this.clients.size) {
|
||||
process.nextTick(emitClose, this);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
cb(ws, req);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = WebSocketServer;
|
||||
|
||||
/**
|
||||
* Add event listeners on an `EventEmitter` using a map of <event, listener>
|
||||
* pairs.
|
||||
*
|
||||
* @param {EventEmitter} server The event emitter
|
||||
* @param {Object.<String, Function>} map The listeners to add
|
||||
* @return {Function} A function that will remove the added listeners when
|
||||
* called
|
||||
* @private
|
||||
*/
|
||||
function addListeners(server, map) {
|
||||
for (const event of Object.keys(map)) server.on(event, map[event]);
|
||||
|
||||
return function removeListeners() {
|
||||
for (const event of Object.keys(map)) {
|
||||
server.removeListener(event, map[event]);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit a `'close'` event on an `EventEmitter`.
|
||||
*
|
||||
* @param {EventEmitter} server The event emitter
|
||||
* @private
|
||||
*/
|
||||
function emitClose(server) {
|
||||
server._state = CLOSED;
|
||||
server.emit('close');
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle socket errors.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
function socketOnError() {
|
||||
this.destroy();
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the connection when preconditions are not fulfilled.
|
||||
*
|
||||
* @param {Duplex} socket The socket of the upgrade request
|
||||
* @param {Number} code The HTTP response status code
|
||||
* @param {String} [message] The HTTP response body
|
||||
* @param {Object} [headers] Additional HTTP response headers
|
||||
* @private
|
||||
*/
|
||||
function abortHandshake(socket, code, message, headers) {
|
||||
//
|
||||
// The socket is writable unless the user destroyed or ended it before calling
|
||||
// `server.handleUpgrade()` or in the `verifyClient` function, which is a user
|
||||
// error. Handling this does not make much sense as the worst that can happen
|
||||
// is that some of the data written by the user might be discarded due to the
|
||||
// call to `socket.end()` below, which triggers an `'error'` event that in
|
||||
// turn causes the socket to be destroyed.
|
||||
//
|
||||
message = message || http.STATUS_CODES[code];
|
||||
headers = {
|
||||
Connection: 'close',
|
||||
'Content-Type': 'text/html',
|
||||
'Content-Length': Buffer.byteLength(message),
|
||||
...headers
|
||||
};
|
||||
|
||||
socket.once('finish', socket.destroy);
|
||||
|
||||
socket.end(
|
||||
`HTTP/1.1 ${code} ${http.STATUS_CODES[code]}\r\n` +
|
||||
Object.keys(headers)
|
||||
.map((h) => `${h}: ${headers[h]}`)
|
||||
.join('\r\n') +
|
||||
'\r\n\r\n' +
|
||||
message
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit a `'wsClientError'` event on a `WebSocketServer` if there is at least
|
||||
* one listener for it, otherwise call `abortHandshake()`.
|
||||
*
|
||||
* @param {WebSocketServer} server The WebSocket server
|
||||
* @param {http.IncomingMessage} req The request object
|
||||
* @param {Duplex} socket The socket of the upgrade request
|
||||
* @param {Number} code The HTTP response status code
|
||||
* @param {String} message The HTTP response body
|
||||
* @param {Object} [headers] The HTTP response headers
|
||||
* @private
|
||||
*/
|
||||
function abortHandshakeOrEmitwsClientError(
|
||||
server,
|
||||
req,
|
||||
socket,
|
||||
code,
|
||||
message,
|
||||
headers
|
||||
) {
|
||||
if (server.listenerCount('wsClientError')) {
|
||||
const err = new Error(message);
|
||||
Error.captureStackTrace(err, abortHandshakeOrEmitwsClientError);
|
||||
|
||||
server.emit('wsClientError', err, socket, req);
|
||||
} else {
|
||||
abortHandshake(socket, code, message, headers);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
'use strict'
|
||||
|
||||
module.exports = deleteLogProperty
|
||||
|
||||
const getPropertyValue = require('./get-property-value')
|
||||
const splitPropertyKey = require('./split-property-key')
|
||||
|
||||
/**
|
||||
* Deletes a specified property from a log object if it exists.
|
||||
* This function mutates the passed in `log` object.
|
||||
*
|
||||
* @param {object} log The log object to be modified.
|
||||
* @param {string} property A string identifying the property to be deleted from
|
||||
* the log object. Accepts nested properties delimited by a `.`
|
||||
* Delimiter can be escaped to preserve property names that contain the delimiter.
|
||||
* e.g. `'prop1.prop2'` or `'prop2\.domain\.corp.prop2'`
|
||||
*/
|
||||
function deleteLogProperty (log, property) {
|
||||
const props = splitPropertyKey(property)
|
||||
const propToDelete = props.pop()
|
||||
|
||||
log = getPropertyValue(log, props)
|
||||
|
||||
/* istanbul ignore else */
|
||||
if (log !== null && typeof log === 'object' && Object.prototype.hasOwnProperty.call(log, propToDelete)) {
|
||||
delete log[propToDelete]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
import validate from './validate.js';
|
||||
/**
|
||||
* Convert array of 16 byte values to UUID string format of the form:
|
||||
* XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
|
||||
*/
|
||||
|
||||
const byteToHex = [];
|
||||
|
||||
for (let i = 0; i < 256; ++i) {
|
||||
byteToHex.push((i + 0x100).toString(16).substr(1));
|
||||
}
|
||||
|
||||
function stringify(arr, offset = 0) {
|
||||
// Note: Be careful editing this code! It's been tuned for performance
|
||||
// and works in ways you may not expect. See https://github.com/uuidjs/uuid/pull/434
|
||||
const uuid = (byteToHex[arr[offset + 0]] + byteToHex[arr[offset + 1]] + byteToHex[arr[offset + 2]] + byteToHex[arr[offset + 3]] + '-' + byteToHex[arr[offset + 4]] + byteToHex[arr[offset + 5]] + '-' + byteToHex[arr[offset + 6]] + byteToHex[arr[offset + 7]] + '-' + byteToHex[arr[offset + 8]] + byteToHex[arr[offset + 9]] + '-' + byteToHex[arr[offset + 10]] + byteToHex[arr[offset + 11]] + byteToHex[arr[offset + 12]] + byteToHex[arr[offset + 13]] + byteToHex[arr[offset + 14]] + byteToHex[arr[offset + 15]]).toLowerCase(); // Consistency check for valid UUID. If this throws, it's likely due to one
|
||||
// of the following:
|
||||
// - One or more input array values don't map to a hex octet (leading to
|
||||
// "undefined" in the uuid)
|
||||
// - Invalid input values for the RFC `version` or `variant` fields
|
||||
|
||||
if (!validate(uuid)) {
|
||||
throw TypeError('Stringified UUID is invalid');
|
||||
}
|
||||
|
||||
return uuid;
|
||||
}
|
||||
|
||||
export default stringify;
|
||||
@@ -0,0 +1,9 @@
|
||||
function _initializer_warning_helper(descriptor, context) {
|
||||
throw new Error(
|
||||
"Decorating class property failed. Please ensure that "
|
||||
+ "proposal-class-properties is enabled and set to use loose mode. "
|
||||
+ "To use proposal-class-properties in spec mode with decorators, wait for "
|
||||
+ "the next major version of decorators in stage 2."
|
||||
);
|
||||
}
|
||||
export { _initializer_warning_helper as _ };
|
||||
@@ -0,0 +1,4 @@
|
||||
declare module 'fast-deep-equal' {
|
||||
const equal: (a: any, b: any) => boolean;
|
||||
export = equal;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
"use strict";
|
||||
|
||||
function _tagged_template_literal_loose(strings, raw) {
|
||||
if (!raw) raw = strings.slice(0);
|
||||
|
||||
strings.raw = raw;
|
||||
|
||||
return strings;
|
||||
}
|
||||
exports._ = _tagged_template_literal_loose;
|
||||
Reference in New Issue
Block a user