WIP: bootstrap and partial real Solana watcher implementation
This commit is contained in:
@@ -0,0 +1,495 @@
|
||||
# ws: a Node.js WebSocket library
|
||||
|
||||
[](https://www.npmjs.com/package/ws)
|
||||
[](https://github.com/websockets/ws/actions?query=workflow%3ACI+branch%3Amaster)
|
||||
[](https://coveralls.io/github/websockets/ws)
|
||||
|
||||
ws is a simple to use, blazing fast, and thoroughly tested WebSocket client and
|
||||
server implementation.
|
||||
|
||||
Passes the quite extensive Autobahn test suite: [server][server-report],
|
||||
[client][client-report].
|
||||
|
||||
**Note**: This module does not work in the browser. The client in the docs is a
|
||||
reference to a back end with the role of a client in the WebSocket
|
||||
communication. Browser clients must use the native
|
||||
[`WebSocket`](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket)
|
||||
object. To make the same code work seamlessly on Node.js and the browser, you
|
||||
can use one of the many wrappers available on npm, like
|
||||
[isomorphic-ws](https://github.com/heineiuo/isomorphic-ws).
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Protocol support](#protocol-support)
|
||||
- [Installing](#installing)
|
||||
- [Opt-in for performance](#opt-in-for-performance)
|
||||
- [API docs](#api-docs)
|
||||
- [WebSocket compression](#websocket-compression)
|
||||
- [Usage examples](#usage-examples)
|
||||
- [Sending and receiving text data](#sending-and-receiving-text-data)
|
||||
- [Sending binary data](#sending-binary-data)
|
||||
- [Simple server](#simple-server)
|
||||
- [External HTTP/S server](#external-https-server)
|
||||
- [Multiple servers sharing a single HTTP/S server](#multiple-servers-sharing-a-single-https-server)
|
||||
- [Client authentication](#client-authentication)
|
||||
- [Server broadcast](#server-broadcast)
|
||||
- [echo.websocket.org demo](#echowebsocketorg-demo)
|
||||
- [Use the Node.js streams API](#use-the-nodejs-streams-api)
|
||||
- [Other examples](#other-examples)
|
||||
- [FAQ](#faq)
|
||||
- [How to get the IP address of the client?](#how-to-get-the-ip-address-of-the-client)
|
||||
- [How to detect and close broken connections?](#how-to-detect-and-close-broken-connections)
|
||||
- [How to connect via a proxy?](#how-to-connect-via-a-proxy)
|
||||
- [Changelog](#changelog)
|
||||
- [License](#license)
|
||||
|
||||
## Protocol support
|
||||
|
||||
- **HyBi drafts 07-12** (Use the option `protocolVersion: 8`)
|
||||
- **HyBi drafts 13-17** (Current default, alternatively option
|
||||
`protocolVersion: 13`)
|
||||
|
||||
## Installing
|
||||
|
||||
```
|
||||
npm install ws
|
||||
```
|
||||
|
||||
### Opt-in for performance
|
||||
|
||||
There are 2 optional modules that can be installed along side with the ws
|
||||
module. These modules are binary addons which improve certain operations.
|
||||
Prebuilt binaries are available for the most popular platforms so you don't
|
||||
necessarily need to have a C++ compiler installed on your machine.
|
||||
|
||||
- `npm install --save-optional bufferutil`: Allows to efficiently perform
|
||||
operations such as masking and unmasking the data payload of the WebSocket
|
||||
frames.
|
||||
- `npm install --save-optional utf-8-validate`: Allows to efficiently check if a
|
||||
message contains valid UTF-8.
|
||||
|
||||
## API docs
|
||||
|
||||
See [`/doc/ws.md`](./doc/ws.md) for Node.js-like documentation of ws classes and
|
||||
utility functions.
|
||||
|
||||
## WebSocket compression
|
||||
|
||||
ws supports the [permessage-deflate extension][permessage-deflate] which enables
|
||||
the client and server to negotiate a compression algorithm and its parameters,
|
||||
and then selectively apply it to the data payloads of each WebSocket message.
|
||||
|
||||
The extension is disabled by default on the server and enabled by default on the
|
||||
client. It adds a significant overhead in terms of performance and memory
|
||||
consumption so we suggest to enable it only if it is really needed.
|
||||
|
||||
Note that Node.js has a variety of issues with high-performance compression,
|
||||
where increased concurrency, especially on Linux, can lead to [catastrophic
|
||||
memory fragmentation][node-zlib-bug] and slow performance. If you intend to use
|
||||
permessage-deflate in production, it is worthwhile to set up a test
|
||||
representative of your workload and ensure Node.js/zlib will handle it with
|
||||
acceptable performance and memory usage.
|
||||
|
||||
Tuning of permessage-deflate can be done via the options defined below. You can
|
||||
also use `zlibDeflateOptions` and `zlibInflateOptions`, which is passed directly
|
||||
into the creation of [raw deflate/inflate streams][node-zlib-deflaterawdocs].
|
||||
|
||||
See [the docs][ws-server-options] for more options.
|
||||
|
||||
```js
|
||||
const WebSocket = require('ws');
|
||||
|
||||
const wss = new WebSocket.Server({
|
||||
port: 8080,
|
||||
perMessageDeflate: {
|
||||
zlibDeflateOptions: {
|
||||
// See zlib defaults.
|
||||
chunkSize: 1024,
|
||||
memLevel: 7,
|
||||
level: 3
|
||||
},
|
||||
zlibInflateOptions: {
|
||||
chunkSize: 10 * 1024
|
||||
},
|
||||
// Other options settable:
|
||||
clientNoContextTakeover: true, // Defaults to negotiated value.
|
||||
serverNoContextTakeover: true, // Defaults to negotiated value.
|
||||
serverMaxWindowBits: 10, // Defaults to negotiated value.
|
||||
// Below options specified as default values.
|
||||
concurrencyLimit: 10, // Limits zlib concurrency for perf.
|
||||
threshold: 1024 // Size (in bytes) below which messages
|
||||
// should not be compressed.
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
The client will only use the extension if it is supported and enabled on the
|
||||
server. To always disable the extension on the client set the
|
||||
`perMessageDeflate` option to `false`.
|
||||
|
||||
```js
|
||||
const WebSocket = require('ws');
|
||||
|
||||
const ws = new WebSocket('ws://www.host.com/path', {
|
||||
perMessageDeflate: false
|
||||
});
|
||||
```
|
||||
|
||||
## Usage examples
|
||||
|
||||
### Sending and receiving text data
|
||||
|
||||
```js
|
||||
const WebSocket = require('ws');
|
||||
|
||||
const ws = new WebSocket('ws://www.host.com/path');
|
||||
|
||||
ws.on('open', function open() {
|
||||
ws.send('something');
|
||||
});
|
||||
|
||||
ws.on('message', function incoming(data) {
|
||||
console.log(data);
|
||||
});
|
||||
```
|
||||
|
||||
### Sending binary data
|
||||
|
||||
```js
|
||||
const WebSocket = require('ws');
|
||||
|
||||
const ws = new WebSocket('ws://www.host.com/path');
|
||||
|
||||
ws.on('open', function open() {
|
||||
const array = new Float32Array(5);
|
||||
|
||||
for (var i = 0; i < array.length; ++i) {
|
||||
array[i] = i / 2;
|
||||
}
|
||||
|
||||
ws.send(array);
|
||||
});
|
||||
```
|
||||
|
||||
### Simple server
|
||||
|
||||
```js
|
||||
const WebSocket = require('ws');
|
||||
|
||||
const wss = new WebSocket.Server({ port: 8080 });
|
||||
|
||||
wss.on('connection', function connection(ws) {
|
||||
ws.on('message', function incoming(message) {
|
||||
console.log('received: %s', message);
|
||||
});
|
||||
|
||||
ws.send('something');
|
||||
});
|
||||
```
|
||||
|
||||
### External HTTP/S server
|
||||
|
||||
```js
|
||||
const fs = require('fs');
|
||||
const https = require('https');
|
||||
const WebSocket = require('ws');
|
||||
|
||||
const server = https.createServer({
|
||||
cert: fs.readFileSync('/path/to/cert.pem'),
|
||||
key: fs.readFileSync('/path/to/key.pem')
|
||||
});
|
||||
const wss = new WebSocket.Server({ server });
|
||||
|
||||
wss.on('connection', function connection(ws) {
|
||||
ws.on('message', function incoming(message) {
|
||||
console.log('received: %s', message);
|
||||
});
|
||||
|
||||
ws.send('something');
|
||||
});
|
||||
|
||||
server.listen(8080);
|
||||
```
|
||||
|
||||
### Multiple servers sharing a single HTTP/S server
|
||||
|
||||
```js
|
||||
const http = require('http');
|
||||
const WebSocket = require('ws');
|
||||
const url = require('url');
|
||||
|
||||
const server = http.createServer();
|
||||
const wss1 = new WebSocket.Server({ noServer: true });
|
||||
const wss2 = new WebSocket.Server({ noServer: true });
|
||||
|
||||
wss1.on('connection', function connection(ws) {
|
||||
// ...
|
||||
});
|
||||
|
||||
wss2.on('connection', function connection(ws) {
|
||||
// ...
|
||||
});
|
||||
|
||||
server.on('upgrade', function upgrade(request, socket, head) {
|
||||
const pathname = url.parse(request.url).pathname;
|
||||
|
||||
if (pathname === '/foo') {
|
||||
wss1.handleUpgrade(request, socket, head, function done(ws) {
|
||||
wss1.emit('connection', ws, request);
|
||||
});
|
||||
} else if (pathname === '/bar') {
|
||||
wss2.handleUpgrade(request, socket, head, function done(ws) {
|
||||
wss2.emit('connection', ws, request);
|
||||
});
|
||||
} else {
|
||||
socket.destroy();
|
||||
}
|
||||
});
|
||||
|
||||
server.listen(8080);
|
||||
```
|
||||
|
||||
### Client authentication
|
||||
|
||||
```js
|
||||
const http = require('http');
|
||||
const WebSocket = require('ws');
|
||||
|
||||
const server = http.createServer();
|
||||
const wss = new WebSocket.Server({ noServer: true });
|
||||
|
||||
wss.on('connection', function connection(ws, request, client) {
|
||||
ws.on('message', function message(msg) {
|
||||
console.log(`Received message ${msg} from user ${client}`);
|
||||
});
|
||||
});
|
||||
|
||||
server.on('upgrade', function upgrade(request, socket, head) {
|
||||
// This function is not defined on purpose. Implement it with your own logic.
|
||||
authenticate(request, (err, client) => {
|
||||
if (err || !client) {
|
||||
socket.write('HTTP/1.1 401 Unauthorized\r\n\r\n');
|
||||
socket.destroy();
|
||||
return;
|
||||
}
|
||||
|
||||
wss.handleUpgrade(request, socket, head, function done(ws) {
|
||||
wss.emit('connection', ws, request, client);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
server.listen(8080);
|
||||
```
|
||||
|
||||
Also see the provided [example][session-parse-example] using `express-session`.
|
||||
|
||||
### Server broadcast
|
||||
|
||||
A client WebSocket broadcasting to all connected WebSocket clients, including
|
||||
itself.
|
||||
|
||||
```js
|
||||
const WebSocket = require('ws');
|
||||
|
||||
const wss = new WebSocket.Server({ port: 8080 });
|
||||
|
||||
wss.on('connection', function connection(ws) {
|
||||
ws.on('message', function incoming(data) {
|
||||
wss.clients.forEach(function each(client) {
|
||||
if (client.readyState === WebSocket.OPEN) {
|
||||
client.send(data);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
A client WebSocket broadcasting to every other connected WebSocket clients,
|
||||
excluding itself.
|
||||
|
||||
```js
|
||||
const WebSocket = require('ws');
|
||||
|
||||
const wss = new WebSocket.Server({ port: 8080 });
|
||||
|
||||
wss.on('connection', function connection(ws) {
|
||||
ws.on('message', function incoming(data) {
|
||||
wss.clients.forEach(function each(client) {
|
||||
if (client !== ws && client.readyState === WebSocket.OPEN) {
|
||||
client.send(data);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
### echo.websocket.org demo
|
||||
|
||||
```js
|
||||
const WebSocket = require('ws');
|
||||
|
||||
const ws = new WebSocket('wss://echo.websocket.org/', {
|
||||
origin: 'https://websocket.org'
|
||||
});
|
||||
|
||||
ws.on('open', function open() {
|
||||
console.log('connected');
|
||||
ws.send(Date.now());
|
||||
});
|
||||
|
||||
ws.on('close', function close() {
|
||||
console.log('disconnected');
|
||||
});
|
||||
|
||||
ws.on('message', function incoming(data) {
|
||||
console.log(`Roundtrip time: ${Date.now() - data} ms`);
|
||||
|
||||
setTimeout(function timeout() {
|
||||
ws.send(Date.now());
|
||||
}, 500);
|
||||
});
|
||||
```
|
||||
|
||||
### Use the Node.js streams API
|
||||
|
||||
```js
|
||||
const WebSocket = require('ws');
|
||||
|
||||
const ws = new WebSocket('wss://echo.websocket.org/', {
|
||||
origin: 'https://websocket.org'
|
||||
});
|
||||
|
||||
const duplex = WebSocket.createWebSocketStream(ws, { encoding: 'utf8' });
|
||||
|
||||
duplex.pipe(process.stdout);
|
||||
process.stdin.pipe(duplex);
|
||||
```
|
||||
|
||||
### Other examples
|
||||
|
||||
For a full example with a browser client communicating with a ws server, see the
|
||||
examples folder.
|
||||
|
||||
Otherwise, see the test cases.
|
||||
|
||||
## FAQ
|
||||
|
||||
### How to get the IP address of the client?
|
||||
|
||||
The remote IP address can be obtained from the raw socket.
|
||||
|
||||
```js
|
||||
const WebSocket = require('ws');
|
||||
|
||||
const wss = new WebSocket.Server({ port: 8080 });
|
||||
|
||||
wss.on('connection', function connection(ws, req) {
|
||||
const ip = req.socket.remoteAddress;
|
||||
});
|
||||
```
|
||||
|
||||
When the server runs behind a proxy like NGINX, the de-facto standard is to use
|
||||
the `X-Forwarded-For` header.
|
||||
|
||||
```js
|
||||
wss.on('connection', function connection(ws, req) {
|
||||
const ip = req.headers['x-forwarded-for'].split(',')[0].trim();
|
||||
});
|
||||
```
|
||||
|
||||
### How to detect and close broken connections?
|
||||
|
||||
Sometimes the link between the server and the client can be interrupted in a way
|
||||
that keeps both the server and the client unaware of the broken state of the
|
||||
connection (e.g. when pulling the cord).
|
||||
|
||||
In these cases ping messages can be used as a means to verify that the remote
|
||||
endpoint is still responsive.
|
||||
|
||||
```js
|
||||
const WebSocket = require('ws');
|
||||
|
||||
function noop() {}
|
||||
|
||||
function heartbeat() {
|
||||
this.isAlive = true;
|
||||
}
|
||||
|
||||
const wss = new WebSocket.Server({ port: 8080 });
|
||||
|
||||
wss.on('connection', function connection(ws) {
|
||||
ws.isAlive = true;
|
||||
ws.on('pong', heartbeat);
|
||||
});
|
||||
|
||||
const interval = setInterval(function ping() {
|
||||
wss.clients.forEach(function each(ws) {
|
||||
if (ws.isAlive === false) return ws.terminate();
|
||||
|
||||
ws.isAlive = false;
|
||||
ws.ping(noop);
|
||||
});
|
||||
}, 30000);
|
||||
|
||||
wss.on('close', function close() {
|
||||
clearInterval(interval);
|
||||
});
|
||||
```
|
||||
|
||||
Pong messages are automatically sent in response to ping messages as required by
|
||||
the spec.
|
||||
|
||||
Just like the server example above your clients might as well lose connection
|
||||
without knowing it. You might want to add a ping listener on your clients to
|
||||
prevent that. A simple implementation would be:
|
||||
|
||||
```js
|
||||
const WebSocket = require('ws');
|
||||
|
||||
function heartbeat() {
|
||||
clearTimeout(this.pingTimeout);
|
||||
|
||||
// Use `WebSocket#terminate()`, which immediately destroys the connection,
|
||||
// instead of `WebSocket#close()`, which waits for the close timer.
|
||||
// Delay should be equal to the interval at which your server
|
||||
// sends out pings plus a conservative assumption of the latency.
|
||||
this.pingTimeout = setTimeout(() => {
|
||||
this.terminate();
|
||||
}, 30000 + 1000);
|
||||
}
|
||||
|
||||
const client = new WebSocket('wss://echo.websocket.org/');
|
||||
|
||||
client.on('open', heartbeat);
|
||||
client.on('ping', heartbeat);
|
||||
client.on('close', function clear() {
|
||||
clearTimeout(this.pingTimeout);
|
||||
});
|
||||
```
|
||||
|
||||
### How to connect via a proxy?
|
||||
|
||||
Use a custom `http.Agent` implementation like [https-proxy-agent][] or
|
||||
[socks-proxy-agent][].
|
||||
|
||||
## Changelog
|
||||
|
||||
We're using the GitHub [releases][changelog] for changelog entries.
|
||||
|
||||
## License
|
||||
|
||||
[MIT](LICENSE)
|
||||
|
||||
[changelog]: https://github.com/websockets/ws/releases
|
||||
[client-report]: http://websockets.github.io/ws/autobahn/clients/
|
||||
[https-proxy-agent]: https://github.com/TooTallNate/node-https-proxy-agent
|
||||
[node-zlib-bug]: https://github.com/nodejs/node/issues/8871
|
||||
[node-zlib-deflaterawdocs]:
|
||||
https://nodejs.org/api/zlib.html#zlib_zlib_createdeflateraw_options
|
||||
[permessage-deflate]: https://tools.ietf.org/html/rfc7692
|
||||
[server-report]: http://websockets.github.io/ws/autobahn/servers/
|
||||
[session-parse-example]: ./examples/express-session-parse
|
||||
[socks-proxy-agent]: https://github.com/TooTallNate/node-socks-proxy-agent
|
||||
[ws-server-options]:
|
||||
https://github.com/websockets/ws/blob/master/doc/ws.md#new-websocketserveroptions-callback
|
||||
Binary file not shown.
@@ -0,0 +1,455 @@
|
||||
import type { $ZodCheck, $ZodStringFormats } from "./checks.js";
|
||||
import { $constructor } from "./core.js";
|
||||
import type { $ZodType } from "./schemas.js";
|
||||
import type { StandardSchemaV1 } from "./standard-schema.js";
|
||||
import * as util from "./util.js";
|
||||
|
||||
///////////////////////////
|
||||
//// base type ////
|
||||
///////////////////////////
|
||||
export interface $ZodIssueBase {
|
||||
readonly code?: string;
|
||||
readonly input?: unknown;
|
||||
readonly path: PropertyKey[];
|
||||
readonly message: string;
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//// issue subtypes ////
|
||||
////////////////////////////////
|
||||
export type $ZodInvalidTypeExpected =
|
||||
| "string"
|
||||
| "number"
|
||||
| "int"
|
||||
| "boolean"
|
||||
| "bigint"
|
||||
| "symbol"
|
||||
| "undefined"
|
||||
| "null"
|
||||
| "never"
|
||||
| "void"
|
||||
| "date"
|
||||
| "array"
|
||||
| "object"
|
||||
| "tuple"
|
||||
| "record"
|
||||
| "map"
|
||||
| "set"
|
||||
| "file"
|
||||
| "nonoptional"
|
||||
| "nan"
|
||||
| "function"
|
||||
| (string & {}); // class names for instanceof
|
||||
|
||||
export interface $ZodIssueInvalidType<Input = unknown> extends $ZodIssueBase {
|
||||
readonly code: "invalid_type";
|
||||
readonly expected: $ZodInvalidTypeExpected;
|
||||
readonly input?: Input;
|
||||
}
|
||||
|
||||
export interface $ZodIssueTooBig<Input = unknown> extends $ZodIssueBase {
|
||||
readonly code: "too_big";
|
||||
readonly origin: "number" | "int" | "bigint" | "date" | "string" | "array" | "set" | "file" | (string & {});
|
||||
readonly maximum: number | bigint;
|
||||
readonly inclusive?: boolean;
|
||||
readonly exact?: boolean;
|
||||
readonly input?: Input;
|
||||
}
|
||||
|
||||
export interface $ZodIssueTooSmall<Input = unknown> extends $ZodIssueBase {
|
||||
readonly code: "too_small";
|
||||
readonly origin: "number" | "int" | "bigint" | "date" | "string" | "array" | "set" | "file" | (string & {});
|
||||
readonly minimum: number | bigint;
|
||||
/** True if the allowable range includes the minimum */
|
||||
readonly inclusive?: boolean;
|
||||
/** True if the allowed value is fixed (e.g.` z.length(5)`), not a range (`z.minLength(5)`) */
|
||||
readonly exact?: boolean;
|
||||
readonly input?: Input;
|
||||
}
|
||||
|
||||
export interface $ZodIssueInvalidStringFormat extends $ZodIssueBase {
|
||||
readonly code: "invalid_format";
|
||||
readonly format: $ZodStringFormats | (string & {});
|
||||
readonly pattern?: string;
|
||||
readonly input?: string;
|
||||
}
|
||||
|
||||
export interface $ZodIssueNotMultipleOf<Input extends number | bigint = number | bigint> extends $ZodIssueBase {
|
||||
readonly code: "not_multiple_of";
|
||||
readonly divisor: number;
|
||||
readonly input?: Input;
|
||||
}
|
||||
|
||||
export interface $ZodIssueUnrecognizedKeys extends $ZodIssueBase {
|
||||
readonly code: "unrecognized_keys";
|
||||
readonly keys: string[];
|
||||
readonly input?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
interface $ZodIssueInvalidUnionNoMatch extends $ZodIssueBase {
|
||||
readonly code: "invalid_union";
|
||||
readonly errors: $ZodIssue[][];
|
||||
readonly input?: unknown;
|
||||
readonly discriminator?: string | undefined;
|
||||
readonly options?: util.Primitive[];
|
||||
readonly inclusive?: true;
|
||||
}
|
||||
|
||||
interface $ZodIssueInvalidUnionMultipleMatch extends $ZodIssueBase {
|
||||
readonly code: "invalid_union";
|
||||
readonly errors: [];
|
||||
readonly input?: unknown;
|
||||
readonly discriminator?: string | undefined;
|
||||
readonly inclusive: false;
|
||||
}
|
||||
|
||||
export type $ZodIssueInvalidUnion = $ZodIssueInvalidUnionNoMatch | $ZodIssueInvalidUnionMultipleMatch;
|
||||
|
||||
export interface $ZodIssueInvalidKey<Input = unknown> extends $ZodIssueBase {
|
||||
readonly code: "invalid_key";
|
||||
readonly origin: "map" | "record";
|
||||
readonly issues: $ZodIssue[];
|
||||
readonly input?: Input;
|
||||
}
|
||||
|
||||
export interface $ZodIssueInvalidElement<Input = unknown> extends $ZodIssueBase {
|
||||
readonly code: "invalid_element";
|
||||
readonly origin: "map" | "set";
|
||||
readonly key: unknown;
|
||||
readonly issues: $ZodIssue[];
|
||||
readonly input?: Input;
|
||||
}
|
||||
|
||||
export interface $ZodIssueInvalidValue<Input = unknown> extends $ZodIssueBase {
|
||||
readonly code: "invalid_value";
|
||||
readonly values: util.Primitive[];
|
||||
readonly input?: Input;
|
||||
}
|
||||
|
||||
export interface $ZodIssueCustom extends $ZodIssueBase {
|
||||
readonly code: "custom";
|
||||
readonly params?: Record<string, any> | undefined;
|
||||
readonly input?: unknown;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////
|
||||
//// first-party string formats ////
|
||||
////////////////////////////////////////////
|
||||
|
||||
export interface $ZodIssueStringCommonFormats extends $ZodIssueInvalidStringFormat {
|
||||
format: Exclude<$ZodStringFormats, "regex" | "jwt" | "starts_with" | "ends_with" | "includes">;
|
||||
}
|
||||
|
||||
export interface $ZodIssueStringInvalidRegex extends $ZodIssueInvalidStringFormat {
|
||||
format: "regex";
|
||||
pattern: string;
|
||||
}
|
||||
|
||||
export interface $ZodIssueStringInvalidJWT extends $ZodIssueInvalidStringFormat {
|
||||
format: "jwt";
|
||||
algorithm?: string;
|
||||
}
|
||||
|
||||
export interface $ZodIssueStringStartsWith extends $ZodIssueInvalidStringFormat {
|
||||
format: "starts_with";
|
||||
prefix: string;
|
||||
}
|
||||
|
||||
export interface $ZodIssueStringEndsWith extends $ZodIssueInvalidStringFormat {
|
||||
format: "ends_with";
|
||||
suffix: string;
|
||||
}
|
||||
|
||||
export interface $ZodIssueStringIncludes extends $ZodIssueInvalidStringFormat {
|
||||
format: "includes";
|
||||
includes: string;
|
||||
}
|
||||
|
||||
export type $ZodStringFormatIssues =
|
||||
| $ZodIssueStringCommonFormats
|
||||
| $ZodIssueStringInvalidRegex
|
||||
| $ZodIssueStringInvalidJWT
|
||||
| $ZodIssueStringStartsWith
|
||||
| $ZodIssueStringEndsWith
|
||||
| $ZodIssueStringIncludes;
|
||||
|
||||
////////////////////////
|
||||
//// utils /////
|
||||
////////////////////////
|
||||
|
||||
export type $ZodIssue =
|
||||
| $ZodIssueInvalidType
|
||||
| $ZodIssueTooBig
|
||||
| $ZodIssueTooSmall
|
||||
| $ZodIssueInvalidStringFormat
|
||||
| $ZodIssueNotMultipleOf
|
||||
| $ZodIssueUnrecognizedKeys
|
||||
| $ZodIssueInvalidUnion
|
||||
| $ZodIssueInvalidKey
|
||||
| $ZodIssueInvalidElement
|
||||
| $ZodIssueInvalidValue
|
||||
| $ZodIssueCustom;
|
||||
|
||||
export type $ZodIssueCode = $ZodIssue["code"];
|
||||
|
||||
export type $ZodInternalIssue<T extends $ZodIssueBase = $ZodIssue> = T extends any ? RawIssue<T> : never;
|
||||
type RawIssue<T extends $ZodIssueBase> = T extends any
|
||||
? util.Flatten<
|
||||
util.MakePartial<T, "message" | "path"> & {
|
||||
/** The input data */
|
||||
readonly input: unknown;
|
||||
/** The schema or check that originated this issue. */
|
||||
readonly inst?: $ZodType | $ZodCheck;
|
||||
/** If `true`, Zod will continue executing checks/refinements after this issue. */
|
||||
readonly continue?: boolean | undefined;
|
||||
} & Record<string, unknown>
|
||||
>
|
||||
: never;
|
||||
|
||||
export type $ZodRawIssue<T extends $ZodIssueBase = $ZodIssue> = $ZodInternalIssue<T>;
|
||||
|
||||
export interface $ZodErrorMap<T extends $ZodIssueBase = $ZodIssue> {
|
||||
// biome-ignore lint:
|
||||
(issue: $ZodRawIssue<T>): { message: string } | string | undefined | null;
|
||||
}
|
||||
|
||||
//////////////////////// ERROR CLASS ////////////////////////
|
||||
|
||||
// const ZOD_ERROR: symbol = Symbol.for("{{zod.error}}");
|
||||
export interface $ZodError<T = unknown> extends Error {
|
||||
type: T;
|
||||
issues: $ZodIssue[];
|
||||
_zod: {
|
||||
output: T;
|
||||
def: $ZodIssue[];
|
||||
};
|
||||
stack?: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
const initializer = (inst: $ZodError, def: $ZodIssue[]): void => {
|
||||
inst.name = "$ZodError";
|
||||
Object.defineProperty(inst, "_zod", {
|
||||
value: inst._zod,
|
||||
enumerable: false,
|
||||
});
|
||||
Object.defineProperty(inst, "issues", {
|
||||
value: def,
|
||||
enumerable: false,
|
||||
});
|
||||
inst.message = JSON.stringify(def, util.jsonStringifyReplacer, 2);
|
||||
|
||||
Object.defineProperty(inst, "toString", {
|
||||
value: () => inst.message,
|
||||
enumerable: false,
|
||||
});
|
||||
};
|
||||
|
||||
export const $ZodError: $constructor<$ZodError> = $constructor("$ZodError", initializer);
|
||||
interface $ZodRealError<T = any> extends $ZodError<T> {}
|
||||
export const $ZodRealError: $constructor<$ZodRealError> = $constructor("$ZodError", initializer, { Parent: Error });
|
||||
|
||||
/////////////////// ERROR UTILITIES ////////////////////////
|
||||
|
||||
// flatten
|
||||
export type $ZodFlattenedError<T, U = string> = _FlattenedError<T, U>;
|
||||
type _FlattenedError<T, U = string> = {
|
||||
formErrors: U[];
|
||||
fieldErrors: {
|
||||
[P in keyof T]?: U[];
|
||||
};
|
||||
};
|
||||
|
||||
export function flattenError<T>(error: $ZodError<T>): _FlattenedError<T>;
|
||||
export function flattenError<T, U>(error: $ZodError<T>, mapper?: (issue: $ZodIssue) => U): _FlattenedError<T, U>;
|
||||
export function flattenError<T, U>(error: $ZodError<T>, mapper = (issue: $ZodIssue) => issue.message as U) {
|
||||
const fieldErrors: Record<PropertyKey, any> = {};
|
||||
const formErrors: U[] = [];
|
||||
for (const sub of error.issues) {
|
||||
if (sub.path.length > 0) {
|
||||
fieldErrors[sub.path[0]!] = fieldErrors[sub.path[0]!] || [];
|
||||
fieldErrors[sub.path[0]!].push(mapper(sub));
|
||||
} else {
|
||||
formErrors.push(mapper(sub));
|
||||
}
|
||||
}
|
||||
return { formErrors, fieldErrors };
|
||||
}
|
||||
|
||||
type _ZodFormattedError<T, U = string> = T extends [any, ...any[]]
|
||||
? { [K in keyof T]?: $ZodFormattedError<T[K], U> }
|
||||
: T extends any[]
|
||||
? { [k: number]: $ZodFormattedError<T[number], U> }
|
||||
: T extends object
|
||||
? util.Flatten<{ [K in keyof T]?: $ZodFormattedError<T[K], U> }>
|
||||
: any;
|
||||
|
||||
export type $ZodFormattedError<T, U = string> = {
|
||||
_errors: U[];
|
||||
} & util.Flatten<_ZodFormattedError<T, U>>;
|
||||
|
||||
export function formatError<T>(error: $ZodError<T>): $ZodFormattedError<T>;
|
||||
export function formatError<T, U>(error: $ZodError<T>, mapper?: (issue: $ZodIssue) => U): $ZodFormattedError<T, U>;
|
||||
export function formatError<T, U>(error: $ZodError<T>, mapper = (issue: $ZodIssue) => issue.message as U) {
|
||||
const fieldErrors: $ZodFormattedError<T> = { _errors: [] } as any;
|
||||
const processError = (error: { issues: $ZodIssue[] }, path: PropertyKey[] = []) => {
|
||||
for (const issue of error.issues) {
|
||||
if (issue.code === "invalid_union" && issue.errors.length) {
|
||||
issue.errors.map((issues) => processError({ issues }, [...path, ...issue.path]));
|
||||
} else if (issue.code === "invalid_key") {
|
||||
processError({ issues: issue.issues }, [...path, ...issue.path]);
|
||||
} else if (issue.code === "invalid_element") {
|
||||
processError({ issues: issue.issues }, [...path, ...issue.path]);
|
||||
} else {
|
||||
const fullpath = [...path, ...issue.path];
|
||||
if (fullpath.length === 0) {
|
||||
(fieldErrors as any)._errors.push(mapper(issue));
|
||||
} else {
|
||||
let curr: any = fieldErrors;
|
||||
let i = 0;
|
||||
while (i < fullpath.length) {
|
||||
const el = fullpath[i]!;
|
||||
const terminal = i === fullpath.length - 1;
|
||||
|
||||
if (!terminal) {
|
||||
curr[el] = curr[el] || { _errors: [] };
|
||||
} else {
|
||||
curr[el] = curr[el] || { _errors: [] };
|
||||
curr[el]._errors.push(mapper(issue));
|
||||
}
|
||||
|
||||
curr = curr[el];
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
processError(error);
|
||||
return fieldErrors;
|
||||
}
|
||||
|
||||
export type $ZodErrorTree<T, U = string> = T extends util.Primitive
|
||||
? { errors: U[] }
|
||||
: T extends [any, ...any[]]
|
||||
? { errors: U[]; items?: { [K in keyof T]?: $ZodErrorTree<T[K], U> } }
|
||||
: T extends any[]
|
||||
? { errors: U[]; items?: Array<$ZodErrorTree<T[number], U>> }
|
||||
: T extends object
|
||||
? {
|
||||
errors: U[];
|
||||
properties?: { [K in keyof T]?: $ZodErrorTree<T[K], U> };
|
||||
}
|
||||
: { errors: U[] };
|
||||
|
||||
export function treeifyError<T>(error: $ZodError<T>): $ZodErrorTree<T>;
|
||||
export function treeifyError<T, U>(error: $ZodError<T>, mapper?: (issue: $ZodIssue) => U): $ZodErrorTree<T, U>;
|
||||
export function treeifyError<T, U>(error: $ZodError<T>, mapper = (issue: $ZodIssue) => issue.message as U) {
|
||||
const result: $ZodErrorTree<T, U> = { errors: [] } as any;
|
||||
const processError = (error: { issues: $ZodIssue[] }, path: PropertyKey[] = []) => {
|
||||
for (const issue of error.issues) {
|
||||
if (issue.code === "invalid_union" && issue.errors.length) {
|
||||
// regular union error
|
||||
issue.errors.map((issues) => processError({ issues }, [...path, ...issue.path]));
|
||||
} else if (issue.code === "invalid_key") {
|
||||
processError({ issues: issue.issues }, [...path, ...issue.path]);
|
||||
} else if (issue.code === "invalid_element") {
|
||||
processError({ issues: issue.issues }, [...path, ...issue.path]);
|
||||
} else {
|
||||
const fullpath = [...path, ...issue.path];
|
||||
if (fullpath.length === 0) {
|
||||
result.errors.push(mapper(issue));
|
||||
continue;
|
||||
}
|
||||
|
||||
let curr: any = result;
|
||||
let i = 0;
|
||||
while (i < fullpath.length) {
|
||||
const el = fullpath[i]!;
|
||||
|
||||
const terminal = i === fullpath.length - 1;
|
||||
if (typeof el === "string") {
|
||||
curr.properties ??= {};
|
||||
curr.properties[el] ??= { errors: [] };
|
||||
curr = curr.properties[el];
|
||||
} else {
|
||||
curr.items ??= [];
|
||||
curr.items[el] ??= { errors: [] };
|
||||
curr = curr.items[el];
|
||||
}
|
||||
|
||||
if (terminal) {
|
||||
curr.errors.push(mapper(issue));
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
processError(error);
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Format a ZodError as a human-readable string in the following form.
|
||||
*
|
||||
* From
|
||||
*
|
||||
* ```ts
|
||||
* ZodError {
|
||||
* issues: [
|
||||
* {
|
||||
* expected: 'string',
|
||||
* code: 'invalid_type',
|
||||
* path: [ 'username' ],
|
||||
* message: 'Invalid input: expected string'
|
||||
* },
|
||||
* {
|
||||
* expected: 'number',
|
||||
* code: 'invalid_type',
|
||||
* path: [ 'favoriteNumbers', 1 ],
|
||||
* message: 'Invalid input: expected number'
|
||||
* }
|
||||
* ];
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* to
|
||||
*
|
||||
* ```
|
||||
* username
|
||||
* ✖ Expected number, received string at "username
|
||||
* favoriteNumbers[0]
|
||||
* ✖ Invalid input: expected number
|
||||
* ```
|
||||
*/
|
||||
export function toDotPath(_path: readonly (string | number | symbol | StandardSchemaV1.PathSegment)[]): string {
|
||||
const segs: string[] = [];
|
||||
const path: PropertyKey[] = _path.map((seg: any) => (typeof seg === "object" ? seg.key : seg));
|
||||
for (const seg of path) {
|
||||
if (typeof seg === "number") segs.push(`[${seg}]`);
|
||||
else if (typeof seg === "symbol") segs.push(`[${JSON.stringify(String(seg))}]`);
|
||||
else if (/[^\w$]/.test(seg)) segs.push(`[${JSON.stringify(seg)}]`);
|
||||
else {
|
||||
if (segs.length) segs.push(".");
|
||||
segs.push(seg);
|
||||
}
|
||||
}
|
||||
|
||||
return segs.join("");
|
||||
}
|
||||
|
||||
export function prettifyError(error: StandardSchemaV1.FailureResult): string {
|
||||
const lines: string[] = [];
|
||||
// sort by path length
|
||||
const issues = [...error.issues].sort((a, b) => (a.path ?? []).length - (b.path ?? []).length);
|
||||
|
||||
// Process each issue
|
||||
for (const issue of issues) {
|
||||
lines.push(`✖ ${issue.message}`);
|
||||
if (issue.path?.length) lines.push(` → at ${toDotPath(issue.path)}`);
|
||||
}
|
||||
|
||||
// Convert Map to formatted string
|
||||
return lines.join("\n");
|
||||
}
|
||||
@@ -0,0 +1,196 @@
|
||||
/**
|
||||
* @fileoverview The schema to validate language options
|
||||
* @author Nicholas C. Zakas
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Data
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
const globalVariablesValues = new Set([
|
||||
true,
|
||||
"true",
|
||||
"writable",
|
||||
"writeable",
|
||||
false,
|
||||
"false",
|
||||
"readonly",
|
||||
"readable",
|
||||
null,
|
||||
"off",
|
||||
]);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Helpers
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Check if a value is a non-null object.
|
||||
* @param {any} value The value to check.
|
||||
* @returns {boolean} `true` if the value is a non-null object.
|
||||
*/
|
||||
function isNonNullObject(value) {
|
||||
return typeof value === "object" && value !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a value is a non-null non-array object.
|
||||
* @param {any} value The value to check.
|
||||
* @returns {boolean} `true` if the value is a non-null non-array object.
|
||||
*/
|
||||
function isNonArrayObject(value) {
|
||||
return isNonNullObject(value) && !Array.isArray(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a value is undefined.
|
||||
* @param {any} value The value to check.
|
||||
* @returns {boolean} `true` if the value is undefined.
|
||||
*/
|
||||
function isUndefined(value) {
|
||||
return typeof value === "undefined";
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Schemas
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Validates the ecmaVersion property.
|
||||
* @param {string|number} ecmaVersion The value to check.
|
||||
* @returns {void}
|
||||
* @throws {TypeError} If the value is invalid.
|
||||
*/
|
||||
function validateEcmaVersion(ecmaVersion) {
|
||||
if (isUndefined(ecmaVersion)) {
|
||||
throw new TypeError(
|
||||
'Key "ecmaVersion": Expected an "ecmaVersion" property.',
|
||||
);
|
||||
}
|
||||
|
||||
if (typeof ecmaVersion !== "number" && ecmaVersion !== "latest") {
|
||||
throw new TypeError(
|
||||
'Key "ecmaVersion": Expected a number or "latest".',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the sourceType property.
|
||||
* @param {string} sourceType The value to check.
|
||||
* @returns {void}
|
||||
* @throws {TypeError} If the value is invalid.
|
||||
*/
|
||||
function validateSourceType(sourceType) {
|
||||
if (
|
||||
typeof sourceType !== "string" ||
|
||||
!/^(?:script|module|commonjs)$/u.test(sourceType)
|
||||
) {
|
||||
throw new TypeError(
|
||||
'Key "sourceType": Expected "script", "module", or "commonjs".',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the globals property.
|
||||
* @param {Object} globals The value to check.
|
||||
* @returns {void}
|
||||
* @throws {TypeError} If the value is invalid.
|
||||
*/
|
||||
function validateGlobals(globals) {
|
||||
if (!isNonArrayObject(globals)) {
|
||||
throw new TypeError('Key "globals": Expected an object.');
|
||||
}
|
||||
|
||||
for (const key of Object.keys(globals)) {
|
||||
// avoid hairy edge case
|
||||
if (key === "__proto__") {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (key !== key.trim()) {
|
||||
throw new TypeError(
|
||||
`Key "globals": Global "${key}" has leading or trailing whitespace.`,
|
||||
);
|
||||
}
|
||||
|
||||
if (!globalVariablesValues.has(globals[key])) {
|
||||
throw new TypeError(
|
||||
`Key "globals": Key "${key}": Expected "readonly", "writable", or "off".`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the parser property.
|
||||
* @param {Object} parser The value to check.
|
||||
* @returns {void}
|
||||
* @throws {TypeError} If the value is invalid.
|
||||
*/
|
||||
function validateParser(parser) {
|
||||
if (
|
||||
!parser ||
|
||||
typeof parser !== "object" ||
|
||||
(typeof parser.parse !== "function" &&
|
||||
typeof parser.parseForESLint !== "function")
|
||||
) {
|
||||
throw new TypeError(
|
||||
'Key "parser": Expected object with parse() or parseForESLint() method.',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the language options.
|
||||
* @param {Object} languageOptions The language options to validate.
|
||||
* @returns {void}
|
||||
* @throws {TypeError} If the language options are invalid.
|
||||
*/
|
||||
function validateLanguageOptions(languageOptions) {
|
||||
if (!isNonArrayObject(languageOptions)) {
|
||||
throw new TypeError("Expected an object.");
|
||||
}
|
||||
|
||||
const {
|
||||
ecmaVersion,
|
||||
sourceType,
|
||||
globals,
|
||||
parser,
|
||||
parserOptions,
|
||||
...otherOptions
|
||||
} = languageOptions;
|
||||
|
||||
if ("ecmaVersion" in languageOptions) {
|
||||
validateEcmaVersion(ecmaVersion);
|
||||
}
|
||||
|
||||
if ("sourceType" in languageOptions) {
|
||||
validateSourceType(sourceType);
|
||||
}
|
||||
|
||||
if ("globals" in languageOptions) {
|
||||
validateGlobals(globals);
|
||||
}
|
||||
|
||||
if ("parser" in languageOptions) {
|
||||
validateParser(parser);
|
||||
}
|
||||
|
||||
if ("parserOptions" in languageOptions) {
|
||||
if (!isNonArrayObject(parserOptions)) {
|
||||
throw new TypeError('Key "parserOptions": Expected an object.');
|
||||
}
|
||||
}
|
||||
|
||||
const otherOptionKeys = Object.keys(otherOptions);
|
||||
|
||||
if (otherOptionKeys.length > 0) {
|
||||
throw new TypeError(`Unexpected key "${otherOptionKeys[0]}" found.`);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { validateLanguageOptions };
|
||||
@@ -0,0 +1,101 @@
|
||||
import { expect, test } from "vitest";
|
||||
|
||||
import * as z from "zod/v4";
|
||||
|
||||
test("string to number pipe", () => {
|
||||
const schema = z.string().transform(Number).pipe(z.number());
|
||||
expect(schema.parse("1234")).toEqual(1234);
|
||||
});
|
||||
|
||||
test("string to number pipe async", async () => {
|
||||
const schema = z
|
||||
.string()
|
||||
.transform(async (val) => Number(val))
|
||||
.pipe(z.number());
|
||||
expect(await schema.parseAsync("1234")).toEqual(1234);
|
||||
});
|
||||
|
||||
test("string with default fallback", () => {
|
||||
const stringWithDefault = z
|
||||
.pipe(
|
||||
z.transform((v) => (v === "none" ? undefined : v)),
|
||||
z.string()
|
||||
)
|
||||
.catch("default");
|
||||
|
||||
expect(stringWithDefault.parse("ok")).toBe("ok");
|
||||
expect(stringWithDefault.parse(undefined)).toBe("default");
|
||||
expect(stringWithDefault.parse("none")).toBe("default");
|
||||
expect(stringWithDefault.parse(15)).toBe("default");
|
||||
});
|
||||
|
||||
test("continue on non-fatal errors", () => {
|
||||
const schema = z
|
||||
.string()
|
||||
.refine((c) => c === "1234", "A")
|
||||
.transform((val) => Number(val))
|
||||
.refine((c) => c === 1234, "B");
|
||||
|
||||
schema.parse("1234");
|
||||
|
||||
expect(schema.safeParse("4321")).toMatchInlineSnapshot(`
|
||||
{
|
||||
"error": [ZodError: [
|
||||
{
|
||||
"code": "custom",
|
||||
"path": [],
|
||||
"message": "A"
|
||||
}
|
||||
]],
|
||||
"success": false,
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
test("break on fatal errors", () => {
|
||||
const schema = z
|
||||
.string()
|
||||
.refine((c) => c === "1234", { message: "A", abort: true })
|
||||
.transform((val) => Number(val))
|
||||
.refine((c) => c === 1234, "B");
|
||||
|
||||
schema.parse("1234");
|
||||
|
||||
expect(schema.safeParse("4321")).toMatchInlineSnapshot(`
|
||||
{
|
||||
"error": [ZodError: [
|
||||
{
|
||||
"code": "custom",
|
||||
"path": [],
|
||||
"message": "A"
|
||||
}
|
||||
]],
|
||||
"success": false,
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
test("reverse parsing with pipe", () => {
|
||||
const schema = z.string().pipe(z.string());
|
||||
|
||||
// Reverse direction: default should NOT be applied
|
||||
expect(z.safeDecode(schema, "asdf")).toMatchInlineSnapshot(`
|
||||
{
|
||||
"data": "asdf",
|
||||
"success": true,
|
||||
}
|
||||
`);
|
||||
expect(z.safeEncode(schema, "asdf")).toMatchInlineSnapshot(`
|
||||
{
|
||||
"data": "asdf",
|
||||
"success": true,
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
test("reverse parsing with pipe", () => {
|
||||
const schema = z.string().transform((val) => val.length);
|
||||
|
||||
// should throw
|
||||
expect(() => z.encode(schema, 1234)).toThrow();
|
||||
});
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"nodeBuilderFlags.d.ts","sourceRoot":"","sources":["../../src/enums/nodeBuilderFlags.ts"],"names":[],"mappings":"AACA,eAAO,IAAI,gBAAgB,EAAE,GAAG,CAAC"}
|
||||
@@ -0,0 +1,4 @@
|
||||
import * as z from "./v4/classic/external.js";
|
||||
export * from "./v4/classic/external.js";
|
||||
export { z };
|
||||
export default z;
|
||||
@@ -0,0 +1 @@
|
||||
export {default} from './index.js';
|
||||
@@ -0,0 +1,40 @@
|
||||
{
|
||||
"name": "@rolldown/binding-linux-x64-gnu",
|
||||
"version": "1.2.4",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"main": "rolldown-binding.linux-x64-gnu.node",
|
||||
"files": [
|
||||
"rolldown-binding.linux-x64-gnu.node"
|
||||
],
|
||||
"description": "Fast JavaScript/TypeScript bundler in Rust with Rollup-compatible API.",
|
||||
"keywords": [
|
||||
"bundler",
|
||||
"esbuild",
|
||||
"parcel",
|
||||
"rolldown",
|
||||
"rollup",
|
||||
"webpack"
|
||||
],
|
||||
"homepage": "https://rolldown.rs/",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "^20.19.0 || >=22.12.0"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/rolldown/rolldown.git",
|
||||
"directory": "packages/rolldown"
|
||||
},
|
||||
"publishConfig": {
|
||||
"registry": "https://registry.npmjs.org/",
|
||||
"access": "public"
|
||||
},
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
!function(e,n){"object"==typeof exports&&"undefined"!=typeof module?module.exports=n():"function"==typeof define&&define.amd?define(n):(e="undefined"!=typeof globalThis?globalThis:e||self).uuidParse=n()}(this,(function(){"use strict";var e=/^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|00000000-0000-0000-0000-000000000000)$/i;return function(n){if(!function(n){return"string"==typeof n&&e.test(n)}(n))throw TypeError("Invalid UUID");var t,i=new Uint8Array(16);return i[0]=(t=parseInt(n.slice(0,8),16))>>>24,i[1]=t>>>16&255,i[2]=t>>>8&255,i[3]=255&t,i[4]=(t=parseInt(n.slice(9,13),16))>>>8,i[5]=255&t,i[6]=(t=parseInt(n.slice(14,18),16))>>>8,i[7]=255&t,i[8]=(t=parseInt(n.slice(19,23),16))>>>8,i[9]=255&t,i[10]=(t=parseInt(n.slice(24,36),16))/1099511627776&255,i[11]=t/4294967296&255,i[12]=t>>>24&255,i[13]=t>>>16&255,i[14]=t>>>8&255,i[15]=255&t,i}}));
|
||||
@@ -0,0 +1,23 @@
|
||||
/*! *****************************************************************************
|
||||
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.
|
||||
***************************************************************************** */
|
||||
|
||||
|
||||
/// <reference lib="es2021" />
|
||||
/// <reference lib="es2022.array" />
|
||||
/// <reference lib="es2022.error" />
|
||||
/// <reference lib="es2022.intl" />
|
||||
/// <reference lib="es2022.object" />
|
||||
/// <reference lib="es2022.regexp" />
|
||||
/// <reference lib="es2022.string" />
|
||||
@@ -0,0 +1,112 @@
|
||||
import {EventEmitter} from 'events';
|
||||
|
||||
type WithRequiredProperties<T, K extends keyof T> = T & Required<Pick<T, K>>;
|
||||
|
||||
declare class Keyv<Value = any, Options extends Record<string, any> = Record<string, unknown>> extends EventEmitter {
|
||||
/**
|
||||
* `this.opts` is an object containing at least the properties listed
|
||||
* below. However, `Keyv.Options` allows arbitrary properties as well.
|
||||
* These properties can be specified as the second type parameter to `Keyv`.
|
||||
*/
|
||||
opts: WithRequiredProperties<
|
||||
Keyv.Options<Value>,
|
||||
'deserialize' | 'namespace' | 'serialize' | 'store' | 'uri'
|
||||
> &
|
||||
Options;
|
||||
|
||||
/**
|
||||
* @param opts The options object is also passed through to the storage adapter. Check your storage adapter docs for any extra options.
|
||||
*/
|
||||
constructor(options?: Keyv.Options<Value> & Options);
|
||||
/**
|
||||
* @param uri The connection string URI.
|
||||
*
|
||||
* Merged into the options object as options.uri.
|
||||
* @param opts The options object is also passed through to the storage adapter. Check your storage adapter docs for any extra options.
|
||||
*/
|
||||
constructor(uri?: string, options?: Keyv.Options<Value> & Options);
|
||||
|
||||
/** Returns the value. */
|
||||
get(key: string, options?: {raw?: false}): Promise<Value | undefined>;
|
||||
/** Returns the raw value. */
|
||||
get(key: string, options: {raw: true}): Promise<Keyv.DeserializedData<Value> | undefined>;
|
||||
|
||||
/** Returns an array of values. Uses `store.getMany` if it exists, otherwise uses parallel calls to `store.get`. */
|
||||
get(key: string[], options?: {raw?: false}): Promise<Array<Value | undefined>>;
|
||||
/** Returns an array of raw values. Uses `store.getMany` if it exists, otherwise uses parallel calls to `store.get`. */
|
||||
get(key: string[], options: {raw: true}): Promise<Array<Keyv.DeserializedData<Value> | undefined>>;
|
||||
|
||||
/**
|
||||
* Set a value.
|
||||
*
|
||||
* By default keys are persistent. You can set an expiry TTL in milliseconds.
|
||||
*/
|
||||
set(key: string, value: Value, ttl?: number): Promise<true>;
|
||||
/**
|
||||
* Deletes an entry.
|
||||
*
|
||||
* Returns `true` if the key existed, `false` if not.
|
||||
*/
|
||||
delete(key: string | string[]): Promise<boolean>;
|
||||
/** Delete all entries in the current namespace. */
|
||||
clear(): Promise<void>;
|
||||
/** Check if key exists in current namespace. */
|
||||
has(key: string): Promise<boolean>;
|
||||
/** Iterator */
|
||||
iterator(namespace?: string): AsyncGenerator<any, void, any>;
|
||||
/**
|
||||
* Closes the connection.
|
||||
*
|
||||
* Returns `undefined` when the connection closes.
|
||||
*/
|
||||
disconnect(): Promise<void>;
|
||||
}
|
||||
|
||||
declare namespace Keyv {
|
||||
interface Options<Value> {
|
||||
[key: string]: any;
|
||||
|
||||
/** Namespace for the current instance. */
|
||||
namespace?: string | undefined;
|
||||
/** A custom serialization function. */
|
||||
serialize?: ((data: DeserializedData<Value>) => string) | undefined;
|
||||
/** A custom deserialization function. */
|
||||
deserialize?: ((data: string) => DeserializedData<Value> | undefined) | undefined;
|
||||
/** The connection string URI. */
|
||||
uri?: string | undefined;
|
||||
/** The storage adapter instance to be used by Keyv. */
|
||||
store?: Store<string | undefined> | undefined;
|
||||
/** Default TTL. Can be overridden by specififying a TTL on `.set()`. */
|
||||
ttl?: number | undefined;
|
||||
/** Specify an adapter to use. e.g `'redis'` or `'mongodb'`. */
|
||||
adapter?: 'redis' | 'mongodb' | 'mongo' | 'sqlite' | 'postgresql' | 'postgres' | 'mysql' | undefined;
|
||||
/** Enable compression option **/
|
||||
compression?: CompressionAdapter | undefined;
|
||||
}
|
||||
|
||||
interface CompressionAdapter {
|
||||
compress(value: any, options?: any): Promise<any>;
|
||||
decompress(value: any, options?: any): Promise<any>;
|
||||
serialize(value: any): Promise<any>;
|
||||
deserialize(value: any): Promise<any>;
|
||||
}
|
||||
|
||||
interface DeserializedData<Value> {
|
||||
value: Value; expires: number | undefined;
|
||||
}
|
||||
|
||||
type StoredData<Value> = DeserializedData<Value> | string | undefined;
|
||||
|
||||
interface Store<Value> {
|
||||
get(key: string): Value | Promise<Value | undefined> | undefined;
|
||||
set(key: string, value: Value, ttl?: number): any;
|
||||
delete(key: string): boolean | Promise<boolean>;
|
||||
clear(): void | Promise<void>;
|
||||
has?(key: string): boolean | Promise<boolean>;
|
||||
getMany?(
|
||||
keys: string[]
|
||||
): Array<StoredData<Value>> | Promise<Array<StoredData<Value>>> | undefined;
|
||||
}
|
||||
}
|
||||
|
||||
export = Keyv;
|
||||
@@ -0,0 +1,378 @@
|
||||
import { expect, expectTypeOf, test } from "vitest";
|
||||
import * as z from "zod/v4";
|
||||
|
||||
test("transform ctx.addIssue with parse", () => {
|
||||
const strs = ["foo", "bar"];
|
||||
const schema = z.string().transform((data, ctx) => {
|
||||
const i = strs.indexOf(data);
|
||||
if (i === -1) {
|
||||
ctx.addIssue({
|
||||
input: data,
|
||||
code: "custom",
|
||||
message: `${data} is not one of our allowed strings`,
|
||||
});
|
||||
}
|
||||
return data.length;
|
||||
});
|
||||
const result = schema.safeParse("asdf");
|
||||
expect(result.success).toEqual(false);
|
||||
expect(result.error!).toMatchInlineSnapshot(`
|
||||
[ZodError: [
|
||||
{
|
||||
"code": "custom",
|
||||
"message": "asdf is not one of our allowed strings",
|
||||
"path": []
|
||||
}
|
||||
]]
|
||||
`);
|
||||
});
|
||||
|
||||
test("transform ctx.addIssue with parseAsync", async () => {
|
||||
const strs = ["foo", "bar"];
|
||||
|
||||
const result = await z
|
||||
.string()
|
||||
.transform(async (data, ctx) => {
|
||||
const i = strs.indexOf(data);
|
||||
if (i === -1) {
|
||||
ctx.addIssue({
|
||||
input: data,
|
||||
code: "custom",
|
||||
message: `${data} is not one of our allowed strings`,
|
||||
});
|
||||
}
|
||||
return data.length;
|
||||
})
|
||||
.safeParseAsync("asdf");
|
||||
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
{
|
||||
"error": [ZodError: [
|
||||
{
|
||||
"code": "custom",
|
||||
"message": "asdf is not one of our allowed strings",
|
||||
"path": []
|
||||
}
|
||||
]],
|
||||
"success": false,
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
test("z.NEVER in transform", () => {
|
||||
const foo = z
|
||||
.number()
|
||||
.optional()
|
||||
.transform((val, ctx) => {
|
||||
if (!val) {
|
||||
ctx.addIssue({
|
||||
input: val,
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: "bad",
|
||||
});
|
||||
return z.NEVER;
|
||||
}
|
||||
return val;
|
||||
});
|
||||
type foo = z.infer<typeof foo>;
|
||||
expectTypeOf<foo>().toEqualTypeOf<number>();
|
||||
const arg = foo.safeParse(undefined);
|
||||
if (!arg.success) {
|
||||
expect(arg.error.issues[0].message).toEqual("bad");
|
||||
}
|
||||
});
|
||||
|
||||
test("basic transformations", () => {
|
||||
const r1 = z
|
||||
.string()
|
||||
.transform((data) => data.length)
|
||||
.parse("asdf");
|
||||
expect(r1).toEqual(4);
|
||||
});
|
||||
|
||||
test("coercion", () => {
|
||||
const numToString = z.number().transform((n) => String(n));
|
||||
const data = z
|
||||
.object({
|
||||
id: numToString,
|
||||
})
|
||||
.parse({ id: 5 });
|
||||
|
||||
expect(data).toEqual({ id: "5" });
|
||||
});
|
||||
|
||||
test("async coercion", async () => {
|
||||
const numToString = z.number().transform(async (n) => String(n));
|
||||
const data = await z
|
||||
.object({
|
||||
id: numToString,
|
||||
})
|
||||
.parseAsync({ id: 5 });
|
||||
|
||||
expect(data).toEqual({ id: "5" });
|
||||
});
|
||||
|
||||
test("sync coercion async error", async () => {
|
||||
const asyncNumberToString = z.number().transform(async (n) => String(n));
|
||||
expect(() =>
|
||||
z
|
||||
.object({
|
||||
id: asyncNumberToString,
|
||||
})
|
||||
.parse({ id: 5 })
|
||||
).toThrow();
|
||||
// expect(data).toEqual({ id: '5' });
|
||||
});
|
||||
|
||||
test("default", () => {
|
||||
const data = z.string().default("asdf").parse(undefined); // => "asdf"
|
||||
expect(data).toEqual("asdf");
|
||||
});
|
||||
|
||||
test("dynamic default", () => {
|
||||
const data = z
|
||||
.string()
|
||||
.default(() => "string")
|
||||
.parse(undefined); // => "asdf"
|
||||
expect(data).toEqual("string");
|
||||
});
|
||||
|
||||
test("default when property is null or undefined", () => {
|
||||
const data = z
|
||||
.object({
|
||||
foo: z.boolean().nullable().default(true),
|
||||
bar: z.boolean().default(true),
|
||||
})
|
||||
.parse({ foo: null });
|
||||
|
||||
expect(data).toEqual({ foo: null, bar: true });
|
||||
});
|
||||
|
||||
test("default with falsy values", () => {
|
||||
const schema = z.object({
|
||||
emptyStr: z.string().default("def"),
|
||||
zero: z.number().default(5),
|
||||
falseBoolean: z.boolean().default(true),
|
||||
});
|
||||
const input = { emptyStr: "", zero: 0, falseBoolean: true };
|
||||
const output = schema.parse(input);
|
||||
// defaults are not supposed to be used
|
||||
expect(output).toEqual(input);
|
||||
});
|
||||
|
||||
test("object typing", () => {
|
||||
const stringToNumber = z.string().transform((arg) => Number.parseFloat(arg));
|
||||
|
||||
const t1 = z.object({
|
||||
stringToNumber,
|
||||
});
|
||||
|
||||
type t1 = z.input<typeof t1>;
|
||||
type t2 = z.output<typeof t1>;
|
||||
|
||||
expectTypeOf<t1>().toEqualTypeOf<{ stringToNumber: string }>();
|
||||
expectTypeOf<t2>().toEqualTypeOf<{ stringToNumber: number }>();
|
||||
});
|
||||
|
||||
test("transform method overloads", () => {
|
||||
const t1 = z.string().transform((val) => val.toUpperCase());
|
||||
expect(t1.parse("asdf")).toEqual("ASDF");
|
||||
|
||||
const t2 = z.string().transform((val) => val.length);
|
||||
expect(t2.parse("asdf")).toEqual(4);
|
||||
});
|
||||
|
||||
test("multiple transformers", () => {
|
||||
const stringToNumber = z.string().transform((arg) => Number.parseFloat(arg));
|
||||
|
||||
const doubler = stringToNumber.transform((val) => {
|
||||
return val * 2;
|
||||
});
|
||||
expect(doubler.parse("5")).toEqual(10);
|
||||
});
|
||||
|
||||
test("short circuit on dirty", () => {
|
||||
const schema = z
|
||||
.string()
|
||||
.refine(() => false)
|
||||
.transform((val) => val.toUpperCase());
|
||||
const result = schema.safeParse("asdf");
|
||||
expect(result.success).toEqual(false);
|
||||
|
||||
expect(result.error).toMatchInlineSnapshot(`
|
||||
[ZodError: [
|
||||
{
|
||||
"code": "custom",
|
||||
"path": [],
|
||||
"message": "Invalid input"
|
||||
}
|
||||
]]
|
||||
`);
|
||||
|
||||
const result2 = schema.safeParse(1234);
|
||||
expect(result2.success).toEqual(false);
|
||||
if (!result2.success) {
|
||||
expect(result2.error.issues[0].code).toEqual(z.ZodIssueCode.invalid_type);
|
||||
}
|
||||
});
|
||||
|
||||
test("async short circuit on dirty", async () => {
|
||||
const schema = z
|
||||
.string()
|
||||
.refine(() => false)
|
||||
.transform((val) => val.toUpperCase());
|
||||
const result = await schema.spa("asdf");
|
||||
expect(result.success).toEqual(false);
|
||||
|
||||
expect(result.error).toMatchInlineSnapshot(`
|
||||
[ZodError: [
|
||||
{
|
||||
"code": "custom",
|
||||
"path": [],
|
||||
"message": "Invalid input"
|
||||
}
|
||||
]]
|
||||
`);
|
||||
|
||||
const result2 = await schema.spa(1234);
|
||||
expect(result2.success).toEqual(false);
|
||||
|
||||
expect(result2.error).toMatchInlineSnapshot(`
|
||||
[ZodError: [
|
||||
{
|
||||
"expected": "string",
|
||||
"code": "invalid_type",
|
||||
"path": [],
|
||||
"message": "Invalid input: expected string, received number"
|
||||
}
|
||||
]]
|
||||
`);
|
||||
});
|
||||
|
||||
test("do not continue by default", () => {
|
||||
const A = z
|
||||
.string()
|
||||
.transform((val, ctx) => {
|
||||
ctx.addIssue({
|
||||
code: "custom",
|
||||
message: `custom error`,
|
||||
});
|
||||
ctx.addIssue({
|
||||
code: "custom",
|
||||
message: `custom error`,
|
||||
});
|
||||
return val;
|
||||
})
|
||||
.pipe(z.number() as any);
|
||||
expect(A.safeParse("asdf")).toMatchInlineSnapshot(`
|
||||
{
|
||||
"error": [ZodError: [
|
||||
{
|
||||
"code": "custom",
|
||||
"message": "custom error",
|
||||
"path": []
|
||||
},
|
||||
{
|
||||
"code": "custom",
|
||||
"message": "custom error",
|
||||
"path": []
|
||||
}
|
||||
]],
|
||||
"success": false,
|
||||
}
|
||||
`);
|
||||
|
||||
const B = z
|
||||
.string()
|
||||
.transform((val, ctx) => {
|
||||
ctx.issues.push({
|
||||
code: "custom",
|
||||
message: `custom error`,
|
||||
input: val,
|
||||
});
|
||||
ctx.issues.push({
|
||||
code: "custom",
|
||||
message: `custom error`,
|
||||
input: val,
|
||||
});
|
||||
return val;
|
||||
})
|
||||
.pipe(z.number() as any);
|
||||
expect(B.safeParse("asdf")).toMatchInlineSnapshot(`
|
||||
{
|
||||
"error": [ZodError: [
|
||||
{
|
||||
"code": "custom",
|
||||
"message": "custom error",
|
||||
"path": []
|
||||
},
|
||||
{
|
||||
"code": "custom",
|
||||
"message": "custom error",
|
||||
"path": []
|
||||
}
|
||||
]],
|
||||
"success": false,
|
||||
}
|
||||
`);
|
||||
|
||||
const C = z
|
||||
.string()
|
||||
.transform((val, ctx) => {
|
||||
ctx.issues.push({
|
||||
code: "custom",
|
||||
message: `custom error`,
|
||||
input: val,
|
||||
continue: true,
|
||||
});
|
||||
ctx.issues.push({
|
||||
code: "custom",
|
||||
message: `custom error`,
|
||||
input: val,
|
||||
continue: true,
|
||||
});
|
||||
return val;
|
||||
})
|
||||
.pipe(z.number() as any);
|
||||
expect(C.safeParse("asdf")).toMatchInlineSnapshot(`
|
||||
{
|
||||
"error": [ZodError: [
|
||||
{
|
||||
"code": "custom",
|
||||
"message": "custom error",
|
||||
"path": []
|
||||
},
|
||||
{
|
||||
"code": "custom",
|
||||
"message": "custom error",
|
||||
"path": []
|
||||
}
|
||||
]],
|
||||
"success": false,
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
test("encode error", () => {
|
||||
const schema = z.string().transform((val) => val.length);
|
||||
expect(() => z.encode(schema, 1234)).toThrowErrorMatchingInlineSnapshot(
|
||||
`[ZodEncodeError: Encountered unidirectional transform during encode: ZodTransform]`
|
||||
);
|
||||
});
|
||||
|
||||
test("transform context should have addIssue", () => {
|
||||
const schema = z.transform((val, ctx) => {
|
||||
ctx.addIssue({
|
||||
code: "custom",
|
||||
message: "Not valid",
|
||||
});
|
||||
return val;
|
||||
});
|
||||
|
||||
const result = schema.safeParse("test");
|
||||
|
||||
expect(result.success).toBe(false);
|
||||
if (!result.success) {
|
||||
expect(result.error.issues[0].message).toBe("Not valid");
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,4 @@
|
||||
function _identity(t) {
|
||||
return t;
|
||||
}
|
||||
export { _identity as default };
|
||||
@@ -0,0 +1 @@
|
||||
export { _ as default } from "../esm/_class_name_tdz_error.js";
|
||||
@@ -0,0 +1,120 @@
|
||||
'use strict'
|
||||
|
||||
const test = require('node:test')
|
||||
const os = require('node:os')
|
||||
const tspl = require('@matteo.collina/tspl')
|
||||
|
||||
const pino = require('../')
|
||||
|
||||
const { pid } = process
|
||||
const hostname = os.hostname()
|
||||
|
||||
test('metadata works', async (t) => {
|
||||
const plan = tspl(t, { plan: 7 })
|
||||
const now = Date.now()
|
||||
const instance = pino({}, {
|
||||
[Symbol.for('pino.metadata')]: true,
|
||||
write (chunk) {
|
||||
plan.equal(instance, this.lastLogger)
|
||||
plan.equal(30, this.lastLevel)
|
||||
plan.equal('a msg', this.lastMsg)
|
||||
plan.ok(Number(this.lastTime) >= now)
|
||||
plan.deepEqual(this.lastObj, { hello: 'world' })
|
||||
const result = JSON.parse(chunk)
|
||||
plan.ok(new Date(result.time) <= new Date(), 'time is greater than Date.now()')
|
||||
delete result.time
|
||||
plan.deepEqual(result, {
|
||||
pid,
|
||||
hostname,
|
||||
level: 30,
|
||||
hello: 'world',
|
||||
msg: 'a msg'
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
instance.info({ hello: 'world' }, 'a msg')
|
||||
|
||||
await plan
|
||||
})
|
||||
|
||||
test('child loggers works', async (t) => {
|
||||
const plan = tspl(t, { plan: 6 })
|
||||
const instance = pino({}, {
|
||||
[Symbol.for('pino.metadata')]: true,
|
||||
write (chunk) {
|
||||
plan.equal(child, this.lastLogger)
|
||||
plan.equal(30, this.lastLevel)
|
||||
plan.equal('a msg', this.lastMsg)
|
||||
plan.deepEqual(this.lastObj, { from: 'child' })
|
||||
const result = JSON.parse(chunk)
|
||||
plan.ok(new Date(result.time) <= new Date(), 'time is greater than Date.now()')
|
||||
delete result.time
|
||||
plan.deepEqual(result, {
|
||||
pid,
|
||||
hostname,
|
||||
level: 30,
|
||||
hello: 'world',
|
||||
from: 'child',
|
||||
msg: 'a msg'
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
const child = instance.child({ hello: 'world' })
|
||||
child.info({ from: 'child' }, 'a msg')
|
||||
|
||||
await plan
|
||||
})
|
||||
|
||||
test('without object', async (t) => {
|
||||
const plan = tspl(t, { plan: 6 })
|
||||
const instance = pino({}, {
|
||||
[Symbol.for('pino.metadata')]: true,
|
||||
write (chunk) {
|
||||
plan.equal(instance, this.lastLogger)
|
||||
plan.equal(30, this.lastLevel)
|
||||
plan.equal('a msg', this.lastMsg)
|
||||
plan.deepEqual({ }, this.lastObj)
|
||||
const result = JSON.parse(chunk)
|
||||
plan.ok(new Date(result.time) <= new Date(), 'time is greater than Date.now()')
|
||||
delete result.time
|
||||
plan.deepEqual(result, {
|
||||
pid,
|
||||
hostname,
|
||||
level: 30,
|
||||
msg: 'a msg'
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
instance.info('a msg')
|
||||
|
||||
await plan
|
||||
})
|
||||
|
||||
test('without msg', async (t) => {
|
||||
const plan = tspl(t, { plan: 6 })
|
||||
const instance = pino({}, {
|
||||
[Symbol.for('pino.metadata')]: true,
|
||||
write (chunk) {
|
||||
plan.equal(instance, this.lastLogger)
|
||||
plan.equal(30, this.lastLevel)
|
||||
plan.equal(undefined, this.lastMsg)
|
||||
plan.deepEqual({ hello: 'world' }, this.lastObj)
|
||||
const result = JSON.parse(chunk)
|
||||
plan.ok(new Date(result.time) <= new Date(), 'time is greater than Date.now()')
|
||||
delete result.time
|
||||
plan.deepEqual(result, {
|
||||
pid,
|
||||
hostname,
|
||||
level: 30,
|
||||
hello: 'world'
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
instance.info({ hello: 'world' })
|
||||
|
||||
await plan
|
||||
})
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,156 @@
|
||||
import * as util from "../core/util.js";
|
||||
function getRussianPlural(count, one, few, many) {
|
||||
const absCount = Math.abs(count);
|
||||
const lastDigit = absCount % 10;
|
||||
const lastTwoDigits = absCount % 100;
|
||||
if (lastTwoDigits >= 11 && lastTwoDigits <= 19) {
|
||||
return many;
|
||||
}
|
||||
if (lastDigit === 1) {
|
||||
return one;
|
||||
}
|
||||
if (lastDigit >= 2 && lastDigit <= 4) {
|
||||
return few;
|
||||
}
|
||||
return many;
|
||||
}
|
||||
const error = () => {
|
||||
const Sizable = {
|
||||
string: {
|
||||
unit: {
|
||||
one: "символ",
|
||||
few: "символа",
|
||||
many: "символов",
|
||||
},
|
||||
verb: "иметь",
|
||||
},
|
||||
file: {
|
||||
unit: {
|
||||
one: "байт",
|
||||
few: "байта",
|
||||
many: "байт",
|
||||
},
|
||||
verb: "иметь",
|
||||
},
|
||||
array: {
|
||||
unit: {
|
||||
one: "элемент",
|
||||
few: "элемента",
|
||||
many: "элементов",
|
||||
},
|
||||
verb: "иметь",
|
||||
},
|
||||
set: {
|
||||
unit: {
|
||||
one: "элемент",
|
||||
few: "элемента",
|
||||
many: "элементов",
|
||||
},
|
||||
verb: "иметь",
|
||||
},
|
||||
};
|
||||
function getSizing(origin) {
|
||||
return Sizable[origin] ?? null;
|
||||
}
|
||||
const FormatDictionary = {
|
||||
regex: "ввод",
|
||||
email: "email адрес",
|
||||
url: "URL",
|
||||
emoji: "эмодзи",
|
||||
uuid: "UUID",
|
||||
uuidv4: "UUIDv4",
|
||||
uuidv6: "UUIDv6",
|
||||
nanoid: "nanoid",
|
||||
guid: "GUID",
|
||||
cuid: "cuid",
|
||||
cuid2: "cuid2",
|
||||
ulid: "ULID",
|
||||
xid: "XID",
|
||||
ksuid: "KSUID",
|
||||
datetime: "ISO дата и время",
|
||||
date: "ISO дата",
|
||||
time: "ISO время",
|
||||
duration: "ISO длительность",
|
||||
ipv4: "IPv4 адрес",
|
||||
ipv6: "IPv6 адрес",
|
||||
cidrv4: "IPv4 диапазон",
|
||||
cidrv6: "IPv6 диапазон",
|
||||
base64: "строка в формате base64",
|
||||
base64url: "строка в формате base64url",
|
||||
json_string: "JSON строка",
|
||||
e164: "номер E.164",
|
||||
jwt: "JWT",
|
||||
template_literal: "ввод",
|
||||
};
|
||||
const TypeDictionary = {
|
||||
nan: "NaN",
|
||||
number: "число",
|
||||
array: "массив",
|
||||
};
|
||||
return (issue) => {
|
||||
switch (issue.code) {
|
||||
case "invalid_type": {
|
||||
const expected = TypeDictionary[issue.expected] ?? issue.expected;
|
||||
const receivedType = util.parsedType(issue.input);
|
||||
const received = TypeDictionary[receivedType] ?? receivedType;
|
||||
if (/^[A-Z]/.test(issue.expected)) {
|
||||
return `Неверный ввод: ожидалось instanceof ${issue.expected}, получено ${received}`;
|
||||
}
|
||||
return `Неверный ввод: ожидалось ${expected}, получено ${received}`;
|
||||
}
|
||||
case "invalid_value":
|
||||
if (issue.values.length === 1)
|
||||
return `Неверный ввод: ожидалось ${util.stringifyPrimitive(issue.values[0])}`;
|
||||
return `Неверный вариант: ожидалось одно из ${util.joinValues(issue.values, "|")}`;
|
||||
case "too_big": {
|
||||
const adj = issue.inclusive ? "<=" : "<";
|
||||
const sizing = getSizing(issue.origin);
|
||||
if (sizing) {
|
||||
const maxValue = Number(issue.maximum);
|
||||
const unit = getRussianPlural(maxValue, sizing.unit.one, sizing.unit.few, sizing.unit.many);
|
||||
return `Слишком большое значение: ожидалось, что ${issue.origin ?? "значение"} будет иметь ${adj}${issue.maximum.toString()} ${unit}`;
|
||||
}
|
||||
return `Слишком большое значение: ожидалось, что ${issue.origin ?? "значение"} будет ${adj}${issue.maximum.toString()}`;
|
||||
}
|
||||
case "too_small": {
|
||||
const adj = issue.inclusive ? ">=" : ">";
|
||||
const sizing = getSizing(issue.origin);
|
||||
if (sizing) {
|
||||
const minValue = Number(issue.minimum);
|
||||
const unit = getRussianPlural(minValue, sizing.unit.one, sizing.unit.few, sizing.unit.many);
|
||||
return `Слишком маленькое значение: ожидалось, что ${issue.origin} будет иметь ${adj}${issue.minimum.toString()} ${unit}`;
|
||||
}
|
||||
return `Слишком маленькое значение: ожидалось, что ${issue.origin} будет ${adj}${issue.minimum.toString()}`;
|
||||
}
|
||||
case "invalid_format": {
|
||||
const _issue = issue;
|
||||
if (_issue.format === "starts_with")
|
||||
return `Неверная строка: должна начинаться с "${_issue.prefix}"`;
|
||||
if (_issue.format === "ends_with")
|
||||
return `Неверная строка: должна заканчиваться на "${_issue.suffix}"`;
|
||||
if (_issue.format === "includes")
|
||||
return `Неверная строка: должна содержать "${_issue.includes}"`;
|
||||
if (_issue.format === "regex")
|
||||
return `Неверная строка: должна соответствовать шаблону ${_issue.pattern}`;
|
||||
return `Неверный ${FormatDictionary[_issue.format] ?? issue.format}`;
|
||||
}
|
||||
case "not_multiple_of":
|
||||
return `Неверное число: должно быть кратным ${issue.divisor}`;
|
||||
case "unrecognized_keys":
|
||||
return `Нераспознанн${issue.keys.length > 1 ? "ые" : "ый"} ключ${issue.keys.length > 1 ? "и" : ""}: ${util.joinValues(issue.keys, ", ")}`;
|
||||
case "invalid_key":
|
||||
return `Неверный ключ в ${issue.origin}`;
|
||||
case "invalid_union":
|
||||
return "Неверные входные данные";
|
||||
case "invalid_element":
|
||||
return `Неверное значение в ${issue.origin}`;
|
||||
default:
|
||||
return `Неверные входные данные`;
|
||||
}
|
||||
};
|
||||
};
|
||||
export default function () {
|
||||
return {
|
||||
localeError: error(),
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import type { TSESTree } from '@typescript-eslint/types';
|
||||
import type { ScopeManager } from '../ScopeManager';
|
||||
import type { Scope } from './Scope';
|
||||
import { ScopeBase } from './ScopeBase';
|
||||
import { ScopeType } from './ScopeType';
|
||||
export declare class ModuleScope extends ScopeBase<ScopeType.module, TSESTree.Program, Scope> {
|
||||
constructor(scopeManager: ScopeManager, upperScope: ModuleScope['upper'], block: ModuleScope['block']);
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"name": "@types/deep-eql",
|
||||
"version": "4.0.2",
|
||||
"description": "TypeScript definitions for deep-eql",
|
||||
"homepage": "https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/deep-eql",
|
||||
"license": "MIT",
|
||||
"contributors": [
|
||||
{
|
||||
"name": "Rodrigo Pietnechuk",
|
||||
"githubUsername": "ghnoob",
|
||||
"url": "https://github.com/ghnoob"
|
||||
}
|
||||
],
|
||||
"main": "",
|
||||
"types": "index.d.ts",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/DefinitelyTyped/DefinitelyTyped.git",
|
||||
"directory": "types/deep-eql"
|
||||
},
|
||||
"scripts": {},
|
||||
"dependencies": {},
|
||||
"typesPublisherContentHash": "3b8981ce557947fc00ca08cbd93b4206bfc0943360956867381a0a3f6b1eabf5",
|
||||
"typeScriptVersion": "4.5"
|
||||
}
|
||||
@@ -0,0 +1,297 @@
|
||||
/**
|
||||
* @fileoverview Validates spacing before and after semicolon
|
||||
* @author Mathias Schreck
|
||||
* @deprecated in ESLint v8.53.0
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
const astUtils = require("./utils/ast-utils");
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Rule Definition
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** @type {import('../types').Rule.RuleModule} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
deprecated: {
|
||||
message: "Formatting rules are being moved out of ESLint core.",
|
||||
url: "https://eslint.org/blog/2023/10/deprecating-formatting-rules/",
|
||||
deprecatedSince: "8.53.0",
|
||||
availableUntil: "11.0.0",
|
||||
replacedBy: [
|
||||
{
|
||||
message:
|
||||
"ESLint Stylistic now maintains deprecated stylistic core rules.",
|
||||
url: "https://eslint.style/guide/migration",
|
||||
plugin: {
|
||||
name: "@stylistic/eslint-plugin",
|
||||
url: "https://eslint.style",
|
||||
},
|
||||
rule: {
|
||||
name: "semi-spacing",
|
||||
url: "https://eslint.style/rules/semi-spacing",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
type: "layout",
|
||||
|
||||
docs: {
|
||||
description:
|
||||
"Enforce consistent spacing before and after semicolons",
|
||||
recommended: false,
|
||||
url: "https://eslint.org/docs/latest/rules/semi-spacing",
|
||||
},
|
||||
|
||||
fixable: "whitespace",
|
||||
|
||||
schema: [
|
||||
{
|
||||
type: "object",
|
||||
properties: {
|
||||
before: {
|
||||
type: "boolean",
|
||||
default: false,
|
||||
},
|
||||
after: {
|
||||
type: "boolean",
|
||||
default: true,
|
||||
},
|
||||
},
|
||||
additionalProperties: false,
|
||||
},
|
||||
],
|
||||
|
||||
messages: {
|
||||
unexpectedWhitespaceBefore:
|
||||
"Unexpected whitespace before semicolon.",
|
||||
unexpectedWhitespaceAfter: "Unexpected whitespace after semicolon.",
|
||||
missingWhitespaceBefore: "Missing whitespace before semicolon.",
|
||||
missingWhitespaceAfter: "Missing whitespace after semicolon.",
|
||||
},
|
||||
},
|
||||
|
||||
create(context) {
|
||||
const config = context.options[0],
|
||||
sourceCode = context.sourceCode;
|
||||
let requireSpaceBefore = false,
|
||||
requireSpaceAfter = true;
|
||||
|
||||
if (typeof config === "object") {
|
||||
requireSpaceBefore = config.before;
|
||||
requireSpaceAfter = config.after;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a given token has leading whitespace.
|
||||
* @param {Object} token The token to check.
|
||||
* @returns {boolean} True if the given token has leading space, false if not.
|
||||
*/
|
||||
function hasLeadingSpace(token) {
|
||||
const tokenBefore = sourceCode.getTokenBefore(token);
|
||||
|
||||
return (
|
||||
tokenBefore &&
|
||||
astUtils.isTokenOnSameLine(tokenBefore, token) &&
|
||||
sourceCode.isSpaceBetween(tokenBefore, token)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a given token has trailing whitespace.
|
||||
* @param {Object} token The token to check.
|
||||
* @returns {boolean} True if the given token has trailing space, false if not.
|
||||
*/
|
||||
function hasTrailingSpace(token) {
|
||||
const tokenAfter = sourceCode.getTokenAfter(token);
|
||||
|
||||
return (
|
||||
tokenAfter &&
|
||||
astUtils.isTokenOnSameLine(token, tokenAfter) &&
|
||||
sourceCode.isSpaceBetween(token, tokenAfter)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given token is the last token in its line.
|
||||
* @param {Token} token The token to check.
|
||||
* @returns {boolean} Whether or not the token is the last in its line.
|
||||
*/
|
||||
function isLastTokenInCurrentLine(token) {
|
||||
const tokenAfter = sourceCode.getTokenAfter(token);
|
||||
|
||||
return !(
|
||||
tokenAfter && astUtils.isTokenOnSameLine(token, tokenAfter)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given token is the first token in its line
|
||||
* @param {Token} token The token to check.
|
||||
* @returns {boolean} Whether or not the token is the first in its line.
|
||||
*/
|
||||
function isFirstTokenInCurrentLine(token) {
|
||||
const tokenBefore = sourceCode.getTokenBefore(token);
|
||||
|
||||
return !(
|
||||
tokenBefore && astUtils.isTokenOnSameLine(token, tokenBefore)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the next token of a given token is a closing parenthesis.
|
||||
* @param {Token} token The token to check.
|
||||
* @returns {boolean} Whether or not the next token of a given token is a closing parenthesis.
|
||||
*/
|
||||
function isBeforeClosingParen(token) {
|
||||
const nextToken = sourceCode.getTokenAfter(token);
|
||||
|
||||
return (
|
||||
(nextToken && astUtils.isClosingBraceToken(nextToken)) ||
|
||||
astUtils.isClosingParenToken(nextToken)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Report location example :
|
||||
*
|
||||
* for unexpected space `before`
|
||||
*
|
||||
* var a = 'b' ;
|
||||
* ^^^
|
||||
*
|
||||
* for unexpected space `after`
|
||||
*
|
||||
* var a = 'b'; c = 10;
|
||||
* ^^
|
||||
*
|
||||
* Reports if the given token has invalid spacing.
|
||||
* @param {Token} token The semicolon token to check.
|
||||
* @param {ASTNode} node The corresponding node of the token.
|
||||
* @returns {void}
|
||||
*/
|
||||
function checkSemicolonSpacing(token, node) {
|
||||
if (astUtils.isSemicolonToken(token)) {
|
||||
if (hasLeadingSpace(token)) {
|
||||
if (!requireSpaceBefore) {
|
||||
const tokenBefore = sourceCode.getTokenBefore(token);
|
||||
const loc = {
|
||||
start: tokenBefore.loc.end,
|
||||
end: token.loc.start,
|
||||
};
|
||||
|
||||
context.report({
|
||||
node,
|
||||
loc,
|
||||
messageId: "unexpectedWhitespaceBefore",
|
||||
fix(fixer) {
|
||||
return fixer.removeRange([
|
||||
tokenBefore.range[1],
|
||||
token.range[0],
|
||||
]);
|
||||
},
|
||||
});
|
||||
}
|
||||
} else {
|
||||
if (requireSpaceBefore) {
|
||||
const loc = token.loc;
|
||||
|
||||
context.report({
|
||||
node,
|
||||
loc,
|
||||
messageId: "missingWhitespaceBefore",
|
||||
fix(fixer) {
|
||||
return fixer.insertTextBefore(token, " ");
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
!isFirstTokenInCurrentLine(token) &&
|
||||
!isLastTokenInCurrentLine(token) &&
|
||||
!isBeforeClosingParen(token)
|
||||
) {
|
||||
if (hasTrailingSpace(token)) {
|
||||
if (!requireSpaceAfter) {
|
||||
const tokenAfter = sourceCode.getTokenAfter(token);
|
||||
const loc = {
|
||||
start: token.loc.end,
|
||||
end: tokenAfter.loc.start,
|
||||
};
|
||||
|
||||
context.report({
|
||||
node,
|
||||
loc,
|
||||
messageId: "unexpectedWhitespaceAfter",
|
||||
fix(fixer) {
|
||||
return fixer.removeRange([
|
||||
token.range[1],
|
||||
tokenAfter.range[0],
|
||||
]);
|
||||
},
|
||||
});
|
||||
}
|
||||
} else {
|
||||
if (requireSpaceAfter) {
|
||||
const loc = token.loc;
|
||||
|
||||
context.report({
|
||||
node,
|
||||
loc,
|
||||
messageId: "missingWhitespaceAfter",
|
||||
fix(fixer) {
|
||||
return fixer.insertTextAfter(token, " ");
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the spacing of the semicolon with the assumption that the last token is the semicolon.
|
||||
* @param {ASTNode} node The node to check.
|
||||
* @returns {void}
|
||||
*/
|
||||
function checkNode(node) {
|
||||
const token = sourceCode.getLastToken(node);
|
||||
|
||||
checkSemicolonSpacing(token, node);
|
||||
}
|
||||
|
||||
return {
|
||||
VariableDeclaration: checkNode,
|
||||
ExpressionStatement: checkNode,
|
||||
BreakStatement: checkNode,
|
||||
ContinueStatement: checkNode,
|
||||
DebuggerStatement: checkNode,
|
||||
DoWhileStatement: checkNode,
|
||||
ReturnStatement: checkNode,
|
||||
ThrowStatement: checkNode,
|
||||
ImportDeclaration: checkNode,
|
||||
ExportNamedDeclaration: checkNode,
|
||||
ExportAllDeclaration: checkNode,
|
||||
ExportDefaultDeclaration: checkNode,
|
||||
ForStatement(node) {
|
||||
if (node.init) {
|
||||
checkSemicolonSpacing(
|
||||
sourceCode.getTokenAfter(node.init),
|
||||
node,
|
||||
);
|
||||
}
|
||||
|
||||
if (node.test) {
|
||||
checkSemicolonSpacing(
|
||||
sourceCode.getTokenAfter(node.test),
|
||||
node,
|
||||
);
|
||||
}
|
||||
},
|
||||
PropertyDefinition: checkNode,
|
||||
};
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,105 @@
|
||||
import type * as core from "./core.js";
|
||||
import type { $ZodType } from "./schemas.js";
|
||||
|
||||
export const $output: unique symbol = Symbol("ZodOutput");
|
||||
export type $output = typeof $output;
|
||||
export const $input: unique symbol = Symbol("ZodInput");
|
||||
export type $input = typeof $input;
|
||||
|
||||
export type $replace<Meta, S extends $ZodType> = Meta extends $output
|
||||
? core.output<S>
|
||||
: Meta extends $input
|
||||
? core.input<S>
|
||||
: Meta extends (infer M)[]
|
||||
? $replace<M, S>[]
|
||||
: Meta extends (...args: infer P) => infer R
|
||||
? (
|
||||
...args: {
|
||||
[K in keyof P]: $replace<P[K], S>; // tuple
|
||||
}
|
||||
) => $replace<R, S>
|
||||
: // handle objects
|
||||
Meta extends object
|
||||
? { [K in keyof Meta]: $replace<Meta[K], S> }
|
||||
: Meta;
|
||||
|
||||
type MetadataType = object | undefined;
|
||||
export class $ZodRegistry<Meta extends MetadataType = MetadataType, Schema extends $ZodType = $ZodType> {
|
||||
_meta!: Meta;
|
||||
_schema!: Schema;
|
||||
_map: WeakMap<Schema, $replace<Meta, Schema>> = new WeakMap();
|
||||
_idmap: Map<string, Schema> = new Map();
|
||||
|
||||
add<S extends Schema>(
|
||||
schema: S,
|
||||
..._meta: undefined extends Meta ? [$replace<Meta, S>?] : [$replace<Meta, S>]
|
||||
): this {
|
||||
const meta: any = _meta[0];
|
||||
this._map.set(schema, meta!);
|
||||
if (meta && typeof meta === "object" && "id" in meta) {
|
||||
this._idmap.set(meta.id!, schema);
|
||||
}
|
||||
return this as any;
|
||||
}
|
||||
|
||||
clear(): this {
|
||||
this._map = new WeakMap();
|
||||
this._idmap = new Map();
|
||||
return this;
|
||||
}
|
||||
|
||||
remove(schema: Schema): this {
|
||||
const meta: any = this._map.get(schema);
|
||||
if (meta && typeof meta === "object" && "id" in meta) {
|
||||
this._idmap.delete(meta.id!);
|
||||
}
|
||||
this._map.delete(schema);
|
||||
return this;
|
||||
}
|
||||
|
||||
get<S extends Schema>(schema: S): $replace<Meta, S> | undefined {
|
||||
// return this._map.get(schema) as any;
|
||||
|
||||
// inherit metadata
|
||||
const p = schema._zod.parent as Schema;
|
||||
if (p) {
|
||||
const pm: any = { ...(this.get(p) ?? {}) };
|
||||
delete pm.id; // do not inherit id
|
||||
const f = { ...pm, ...this._map.get(schema) } as any;
|
||||
return Object.keys(f).length ? f : undefined;
|
||||
}
|
||||
return this._map.get(schema) as any;
|
||||
}
|
||||
|
||||
has(schema: Schema): boolean {
|
||||
return this._map.has(schema);
|
||||
}
|
||||
}
|
||||
|
||||
export interface JSONSchemaMeta {
|
||||
id?: string | undefined;
|
||||
title?: string | undefined;
|
||||
description?: string | undefined;
|
||||
deprecated?: boolean | undefined;
|
||||
[k: string]: unknown;
|
||||
}
|
||||
|
||||
export interface GlobalMeta extends JSONSchemaMeta {}
|
||||
|
||||
// registries
|
||||
export function registry<T extends MetadataType = MetadataType, S extends $ZodType = $ZodType>(): $ZodRegistry<T, S> {
|
||||
return new $ZodRegistry<T, S>();
|
||||
}
|
||||
|
||||
interface GlobalThisWithRegistry {
|
||||
/**
|
||||
* The globalRegistry instance shared across both CommonJS and ESM builds.
|
||||
* By attaching the registry to `globalThis`, this property ensures a single, deduplicated instance
|
||||
* is used regardless of whether the package is loaded via `require` (CJS) or `import` (ESM).
|
||||
* This prevents dual package hazards and keeps registry state consistent.
|
||||
*/
|
||||
__zod_globalRegistry?: $ZodRegistry<GlobalMeta>;
|
||||
}
|
||||
|
||||
(globalThis as GlobalThisWithRegistry).__zod_globalRegistry ??= registry<GlobalMeta>();
|
||||
export const globalRegistry: $ZodRegistry<GlobalMeta> = (globalThis as GlobalThisWithRegistry).__zod_globalRegistry!;
|
||||
@@ -0,0 +1,73 @@
|
||||
{
|
||||
"name": "@typescript-eslint/tsconfig-utils",
|
||||
"version": "8.67.0",
|
||||
"description": "Utilities for collecting TSConfigs for linting scenarios.",
|
||||
"files": [
|
||||
"dist",
|
||||
"!**/*.tsbuildinfo"
|
||||
],
|
||||
"type": "commonjs",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/index.d.ts",
|
||||
"default": "./dist/index.js"
|
||||
},
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/typescript-eslint/typescript-eslint.git",
|
||||
"directory": "packages/tsconfig-utils"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/typescript-eslint/typescript-eslint/issues"
|
||||
},
|
||||
"homepage": "https://typescript-eslint.io",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
"eslint",
|
||||
"typescript",
|
||||
"estree"
|
||||
],
|
||||
"peerDependencies": {
|
||||
"typescript": ">=4.8.4 <6.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@typescript/native": "npm:typescript@^7.0.2",
|
||||
"@vitest/coverage-v8": "^4.0.18",
|
||||
"rimraf": "^5.0.10",
|
||||
"typescript": ">=4.8.4 <6.1.0",
|
||||
"vitest": "^4.0.18"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"nx": {
|
||||
"name": "tsconfig-utils",
|
||||
"includedScripts": [
|
||||
"clean"
|
||||
],
|
||||
"targets": {
|
||||
"typecheck:tsgo": {},
|
||||
"attw-check": {}
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"//": "These package scripts are mostly here for convenience. Task running is handled by Nx at the root level.",
|
||||
"build": "pnpm exec nx build",
|
||||
"clean": "rimraf dist/ coverage/",
|
||||
"format": "pnpm -w exec format",
|
||||
"lint": "pnpm -w exec nx lint",
|
||||
"test": "pnpm -w exec nx test",
|
||||
"typecheck": "pnpm -w exec nx typecheck",
|
||||
"typecheck:tsgo": "pnpm -w exec nx typecheck:tsgo",
|
||||
"attw-check": "pnpm -w exec nx attw-check"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
/**
|
||||
* @fileoverview Ensure handling of errors when we know they exist.
|
||||
* @author Jamund Ferguson
|
||||
* @deprecated in ESLint v7.0.0
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Rule Definition
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/** @type {import('../types').Rule.RuleModule} */
|
||||
module.exports = {
|
||||
meta: {
|
||||
deprecated: {
|
||||
message: "Node.js rules were moved out of ESLint core.",
|
||||
url: "https://eslint.org/docs/latest/use/migrating-to-7.0.0#deprecate-node-rules",
|
||||
deprecatedSince: "7.0.0",
|
||||
availableUntil: "11.0.0",
|
||||
replacedBy: [
|
||||
{
|
||||
message:
|
||||
"eslint-plugin-n now maintains deprecated Node.js-related rules.",
|
||||
plugin: {
|
||||
name: "eslint-plugin-n",
|
||||
url: "https://github.com/eslint-community/eslint-plugin-n",
|
||||
},
|
||||
rule: {
|
||||
name: "handle-callback-err",
|
||||
url: "https://github.com/eslint-community/eslint-plugin-n/tree/master/docs/rules/handle-callback-err.md",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
type: "suggestion",
|
||||
|
||||
docs: {
|
||||
description: "Require error handling in callbacks",
|
||||
recommended: false,
|
||||
url: "https://eslint.org/docs/latest/rules/handle-callback-err",
|
||||
},
|
||||
|
||||
schema: [
|
||||
{
|
||||
type: "string",
|
||||
},
|
||||
],
|
||||
messages: {
|
||||
expected: "Expected error to be handled.",
|
||||
},
|
||||
},
|
||||
|
||||
create(context) {
|
||||
const errorArgument = context.options[0] || "err";
|
||||
const sourceCode = context.sourceCode;
|
||||
|
||||
/**
|
||||
* Checks if the given argument should be interpreted as a regexp pattern.
|
||||
* @param {string} stringToCheck The string which should be checked.
|
||||
* @returns {boolean} Whether or not the string should be interpreted as a pattern.
|
||||
*/
|
||||
function isPattern(stringToCheck) {
|
||||
const firstChar = stringToCheck[0];
|
||||
|
||||
return firstChar === "^";
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given name matches the configured error argument.
|
||||
* @param {string} name The name which should be compared.
|
||||
* @returns {boolean} Whether or not the given name matches the configured error variable name.
|
||||
*/
|
||||
function matchesConfiguredErrorName(name) {
|
||||
if (isPattern(errorArgument)) {
|
||||
const regexp = new RegExp(errorArgument, "u");
|
||||
|
||||
return regexp.test(name);
|
||||
}
|
||||
return name === errorArgument;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the parameters of a given function scope.
|
||||
* @param {Object} scope The function scope.
|
||||
* @returns {Array} All parameters of the given scope.
|
||||
*/
|
||||
function getParameters(scope) {
|
||||
return scope.variables.filter(
|
||||
variable =>
|
||||
variable.defs[0] && variable.defs[0].type === "Parameter",
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to see if we're handling the error object properly.
|
||||
* @param {ASTNode} node The AST node to check.
|
||||
* @returns {void}
|
||||
*/
|
||||
function checkForError(node) {
|
||||
const scope = sourceCode.getScope(node),
|
||||
parameters = getParameters(scope),
|
||||
firstParameter = parameters[0];
|
||||
|
||||
if (
|
||||
firstParameter &&
|
||||
matchesConfiguredErrorName(firstParameter.name)
|
||||
) {
|
||||
if (firstParameter.references.length === 0) {
|
||||
context.report({ node, messageId: "expected" });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
FunctionDeclaration: checkForError,
|
||||
FunctionExpression: checkForError,
|
||||
ArrowFunctionExpression: checkForError,
|
||||
};
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,23 @@
|
||||
var defineProperty = require("./defineProperty.js");
|
||||
function ownKeys(e, r) {
|
||||
var t = Object.keys(e);
|
||||
if (Object.getOwnPropertySymbols) {
|
||||
var o = Object.getOwnPropertySymbols(e);
|
||||
r && (o = o.filter(function (r) {
|
||||
return Object.getOwnPropertyDescriptor(e, r).enumerable;
|
||||
})), t.push.apply(t, o);
|
||||
}
|
||||
return t;
|
||||
}
|
||||
function _objectSpread2(e) {
|
||||
for (var r = 1; r < arguments.length; r++) {
|
||||
var t = null != arguments[r] ? arguments[r] : {};
|
||||
r % 2 ? ownKeys(Object(t), !0).forEach(function (r) {
|
||||
defineProperty(e, r, t[r]);
|
||||
}) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) {
|
||||
Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r));
|
||||
});
|
||||
}
|
||||
return e;
|
||||
}
|
||||
module.exports = _objectSpread2, module.exports.__esModule = true, module.exports["default"] = module.exports;
|
||||
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* CommanderError class
|
||||
*/
|
||||
class CommanderError extends Error {
|
||||
/**
|
||||
* Constructs the CommanderError class
|
||||
* @param {number} exitCode suggested exit code which could be used with process.exit
|
||||
* @param {string} code an id string representing the error
|
||||
* @param {string} message human-readable description of the error
|
||||
*/
|
||||
constructor(exitCode, code, message) {
|
||||
super(message);
|
||||
// properly capture stack trace in Node.js
|
||||
Error.captureStackTrace(this, this.constructor);
|
||||
this.name = this.constructor.name;
|
||||
this.code = code;
|
||||
this.exitCode = exitCode;
|
||||
this.nestedError = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* InvalidArgumentError class
|
||||
*/
|
||||
class InvalidArgumentError extends CommanderError {
|
||||
/**
|
||||
* Constructs the InvalidArgumentError class
|
||||
* @param {string} [message] explanation of why argument is invalid
|
||||
*/
|
||||
constructor(message) {
|
||||
super(1, 'commander.invalidArgument', message);
|
||||
// properly capture stack trace in Node.js
|
||||
Error.captureStackTrace(this, this.constructor);
|
||||
this.name = this.constructor.name;
|
||||
}
|
||||
}
|
||||
|
||||
exports.CommanderError = CommanderError;
|
||||
exports.InvalidArgumentError = InvalidArgumentError;
|
||||
@@ -0,0 +1,335 @@
|
||||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||
}) : function(o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __importStar = (this && this.__importStar) || (function () {
|
||||
var ownKeys = function(o) {
|
||||
ownKeys = Object.getOwnPropertyNames || function (o) {
|
||||
var ar = [];
|
||||
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
||||
return ar;
|
||||
};
|
||||
return ownKeys(o);
|
||||
};
|
||||
return function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
})();
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const utils_1 = require("@typescript-eslint/utils");
|
||||
const tsutils = __importStar(require("ts-api-utils"));
|
||||
const ts = __importStar(require("typescript"));
|
||||
const util_1 = require("../util");
|
||||
exports.default = (0, util_1.createRule)({
|
||||
name: 'consistent-type-exports',
|
||||
meta: {
|
||||
type: 'suggestion',
|
||||
docs: {
|
||||
description: 'Enforce consistent usage of type exports',
|
||||
requiresTypeChecking: true,
|
||||
},
|
||||
fixable: 'code',
|
||||
messages: {
|
||||
multipleExportsAreTypes: 'Type exports {{exportNames}} are not values and should be exported using `export type`.',
|
||||
singleExportIsType: 'Type export {{exportNames}} is not a value and should be exported using `export type`.',
|
||||
typeOverValue: 'All exports in the declaration are only used as types. Use `export type`.',
|
||||
},
|
||||
schema: [
|
||||
{
|
||||
type: 'object',
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
fixMixedExportsWithInlineTypeSpecifier: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the rule will autofix "mixed" export cases using TS inline type specifiers.',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
defaultOptions: [
|
||||
{
|
||||
fixMixedExportsWithInlineTypeSpecifier: false,
|
||||
},
|
||||
],
|
||||
create(context, [{ fixMixedExportsWithInlineTypeSpecifier }]) {
|
||||
const sourceExportsMap = {};
|
||||
const services = (0, util_1.getParserServices)(context);
|
||||
const checker = services.program.getTypeChecker();
|
||||
/**
|
||||
* Helper for identifying if a symbol resolves to a
|
||||
* JavaScript value or a TypeScript type.
|
||||
*
|
||||
* @returns True/false if is a type or not, or undefined if the specifier
|
||||
* can't be resolved.
|
||||
*/
|
||||
function isSymbolTypeBased(symbol) {
|
||||
if (!symbol || checker.isUnknownSymbol(symbol)) {
|
||||
return undefined;
|
||||
}
|
||||
if (symbol.getDeclarations()?.some(ts.isTypeOnlyImportOrExportDeclaration)) {
|
||||
return true;
|
||||
}
|
||||
if (tsutils.isSymbolFlagSet(symbol, ts.SymbolFlags.Value)) {
|
||||
return false;
|
||||
}
|
||||
return tsutils.isSymbolFlagSet(symbol, ts.SymbolFlags.Alias)
|
||||
? isSymbolTypeBased(checker.getImmediateAliasedSymbol(symbol))
|
||||
: true;
|
||||
}
|
||||
return {
|
||||
ExportAllDeclaration(node) {
|
||||
if (node.exportKind === 'type') {
|
||||
return;
|
||||
}
|
||||
const sourceModule = ts.resolveModuleName(node.source.value, context.filename, services.program.getCompilerOptions(), ts.sys);
|
||||
if (sourceModule.resolvedModule == null) {
|
||||
return;
|
||||
}
|
||||
const sourceFile = services.program.getSourceFile(sourceModule.resolvedModule.resolvedFileName);
|
||||
if (sourceFile == null) {
|
||||
return;
|
||||
}
|
||||
const sourceFileSymbol = checker.getSymbolAtLocation(sourceFile);
|
||||
if (sourceFileSymbol == null) {
|
||||
return;
|
||||
}
|
||||
const sourceFileType = checker.getTypeOfSymbol(sourceFileSymbol);
|
||||
// Module can explicitly export types or values, and it's not difficult
|
||||
// to distinguish one from the other, since we can get the flags of
|
||||
// the exported symbols or check if symbol export declaration has
|
||||
// the "type" keyword in it.
|
||||
//
|
||||
// Things get a lot more complicated when we're dealing with
|
||||
// export * from './module-with-type-only-exports'
|
||||
// export type * from './module-with-type-and-value-exports'
|
||||
//
|
||||
// TS checker has an internal function getExportsOfModuleWorker that
|
||||
// recursively visits all module exports, including "export *". It then
|
||||
// puts type-only-star-exported symbols into the typeOnlyExportStarMap
|
||||
// property of sourceFile's SymbolLinks. Since symbol links aren't
|
||||
// exposed outside the checker, we cannot access it directly.
|
||||
//
|
||||
// Therefore, to filter out value properties, we use the following hack:
|
||||
// checker.getPropertiesOfType returns all exports that were originally
|
||||
// values, but checker.getPropertyOfType returns undefined for
|
||||
// properties that are mentioned in the typeOnlyExportStarMap.
|
||||
const isThereAnyExportedValue = checker
|
||||
.getPropertiesOfType(sourceFileType)
|
||||
.some(propertyTypeSymbol => checker.getPropertyOfType(sourceFileType, propertyTypeSymbol.escapedName.toString()) != null);
|
||||
if (isThereAnyExportedValue) {
|
||||
return;
|
||||
}
|
||||
context.report({
|
||||
node,
|
||||
messageId: 'typeOverValue',
|
||||
fix(fixer) {
|
||||
const asteriskToken = (0, util_1.nullThrows)(context.sourceCode.getFirstToken(node, token => token.type === utils_1.AST_TOKEN_TYPES.Punctuator &&
|
||||
token.value === '*'), util_1.NullThrowsReasons.MissingToken('asterisk', 'export all declaration'));
|
||||
return fixer.insertTextBefore(asteriskToken, 'type ');
|
||||
},
|
||||
});
|
||||
},
|
||||
ExportNamedDeclaration(node) {
|
||||
// Coerce the source into a string for use as a lookup entry.
|
||||
const source = getSourceFromExport(node) ?? 'undefined';
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
const sourceExports = (sourceExportsMap[source] ||= {
|
||||
reportValueExports: [],
|
||||
source,
|
||||
typeOnlyNamedExport: null,
|
||||
valueOnlyNamedExport: null,
|
||||
});
|
||||
// Cache the first encountered exports for the package. We will need to come
|
||||
// back to these later when fixing the problems.
|
||||
if (node.exportKind === 'type') {
|
||||
// The export is a type export
|
||||
sourceExports.typeOnlyNamedExport ??= node;
|
||||
}
|
||||
else {
|
||||
// The export is a value export
|
||||
sourceExports.valueOnlyNamedExport ??= node;
|
||||
}
|
||||
// Next for the current export, we will separate type/value specifiers.
|
||||
const typeBasedSpecifiers = [];
|
||||
const inlineTypeSpecifiers = [];
|
||||
const valueSpecifiers = [];
|
||||
// Note: it is valid to export values as types. We will avoid reporting errors
|
||||
// when this is encountered.
|
||||
if (node.exportKind !== 'type') {
|
||||
for (const specifier of node.specifiers) {
|
||||
if (specifier.exportKind === 'type') {
|
||||
inlineTypeSpecifiers.push(specifier);
|
||||
continue;
|
||||
}
|
||||
const isTypeBased = isSymbolTypeBased(services.getSymbolAtLocation(specifier.exported));
|
||||
if (isTypeBased === true) {
|
||||
typeBasedSpecifiers.push(specifier);
|
||||
}
|
||||
else if (isTypeBased === false) {
|
||||
// When isTypeBased is undefined, we should avoid reporting them.
|
||||
valueSpecifiers.push(specifier);
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((node.exportKind === 'value' && typeBasedSpecifiers.length) ||
|
||||
(node.exportKind === 'type' && valueSpecifiers.length)) {
|
||||
sourceExports.reportValueExports.push({
|
||||
node,
|
||||
inlineTypeSpecifiers,
|
||||
typeBasedSpecifiers,
|
||||
valueSpecifiers,
|
||||
});
|
||||
}
|
||||
},
|
||||
'Program:exit'() {
|
||||
for (const sourceExports of Object.values(sourceExportsMap)) {
|
||||
// If this export has no issues, move on.
|
||||
if (sourceExports.reportValueExports.length === 0) {
|
||||
continue;
|
||||
}
|
||||
for (const report of sourceExports.reportValueExports) {
|
||||
if (report.valueSpecifiers.length === 0) {
|
||||
// Export is all type-only with no type specifiers; convert the entire export to `export type`.
|
||||
context.report({
|
||||
node: report.node,
|
||||
messageId: 'typeOverValue',
|
||||
*fix(fixer) {
|
||||
yield* fixExportInsertType(fixer, context.sourceCode, report.node);
|
||||
},
|
||||
});
|
||||
continue;
|
||||
}
|
||||
// We have both type and value violations.
|
||||
const allExportNames = report.typeBasedSpecifiers.map(specifier => specifier.local.type === utils_1.AST_NODE_TYPES.Identifier
|
||||
? specifier.local.name
|
||||
: specifier.local.value);
|
||||
if (allExportNames.length === 1) {
|
||||
const exportNames = allExportNames[0];
|
||||
context.report({
|
||||
node: report.node,
|
||||
messageId: 'singleExportIsType',
|
||||
data: { exportNames },
|
||||
*fix(fixer) {
|
||||
if (fixMixedExportsWithInlineTypeSpecifier) {
|
||||
yield* fixAddTypeSpecifierToNamedExports(fixer, report);
|
||||
}
|
||||
else {
|
||||
yield* fixSeparateNamedExports(fixer, context.sourceCode, report);
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
else {
|
||||
const exportNames = (0, util_1.formatWordList)(allExportNames);
|
||||
context.report({
|
||||
node: report.node,
|
||||
messageId: 'multipleExportsAreTypes',
|
||||
data: { exportNames },
|
||||
*fix(fixer) {
|
||||
if (fixMixedExportsWithInlineTypeSpecifier) {
|
||||
yield* fixAddTypeSpecifierToNamedExports(fixer, report);
|
||||
}
|
||||
else {
|
||||
yield* fixSeparateNamedExports(fixer, context.sourceCode, report);
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
||||
/**
|
||||
* Inserts "type" into an export.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* export type { Foo } from 'foo';
|
||||
* ^^^^
|
||||
*/
|
||||
function* fixExportInsertType(fixer, sourceCode, node) {
|
||||
const exportToken = (0, util_1.nullThrows)(sourceCode.getFirstToken(node), util_1.NullThrowsReasons.MissingToken('export', node.type));
|
||||
yield fixer.insertTextAfter(exportToken, ' type');
|
||||
for (const specifier of node.specifiers) {
|
||||
if (specifier.exportKind === 'type') {
|
||||
const kindToken = (0, util_1.nullThrows)(sourceCode.getFirstToken(specifier), util_1.NullThrowsReasons.MissingToken('export', specifier.type));
|
||||
const firstTokenAfter = (0, util_1.nullThrows)(sourceCode.getTokenAfter(kindToken, {
|
||||
includeComments: true,
|
||||
}), 'Missing token following the export kind.');
|
||||
yield fixer.removeRange([kindToken.range[0], firstTokenAfter.range[0]]);
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Separates the exports which mismatch the kind of export the given
|
||||
* node represents. For example, a type export's named specifiers which
|
||||
* represent values will be inserted in a separate `export` statement.
|
||||
*/
|
||||
function* fixSeparateNamedExports(fixer, sourceCode, report) {
|
||||
const { node, inlineTypeSpecifiers, typeBasedSpecifiers, valueSpecifiers } = report;
|
||||
const typeSpecifiers = [...typeBasedSpecifiers, ...inlineTypeSpecifiers];
|
||||
const source = getSourceFromExport(node);
|
||||
const specifierNames = typeSpecifiers.map(getSpecifierText).join(', ');
|
||||
const exportToken = (0, util_1.nullThrows)(sourceCode.getFirstToken(node), util_1.NullThrowsReasons.MissingToken('export', node.type));
|
||||
// Filter the bad exports from the current line.
|
||||
const filteredSpecifierNames = valueSpecifiers
|
||||
.map(getSpecifierText)
|
||||
.join(', ');
|
||||
const openToken = (0, util_1.nullThrows)(sourceCode.getFirstToken(node, util_1.isOpeningBraceToken), util_1.NullThrowsReasons.MissingToken('{', node.type));
|
||||
const closeToken = (0, util_1.nullThrows)(sourceCode.getLastToken(node, util_1.isClosingBraceToken), util_1.NullThrowsReasons.MissingToken('}', node.type));
|
||||
// Remove exports from the current line which we're going to re-insert.
|
||||
yield fixer.replaceTextRange([openToken.range[1], closeToken.range[0]], ` ${filteredSpecifierNames} `);
|
||||
// Insert the bad exports into a new export line above.
|
||||
yield fixer.insertTextBefore(exportToken, `export type { ${specifierNames} }${source ? ` from '${source}'` : ''};\n`);
|
||||
}
|
||||
function* fixAddTypeSpecifierToNamedExports(fixer, report) {
|
||||
if (report.node.exportKind === 'type') {
|
||||
return;
|
||||
}
|
||||
for (const specifier of report.typeBasedSpecifiers) {
|
||||
yield fixer.insertTextBefore(specifier, 'type ');
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Returns the source of the export, or undefined if the named export has no source.
|
||||
*/
|
||||
function getSourceFromExport(node) {
|
||||
if (node.source?.type === utils_1.AST_NODE_TYPES.Literal &&
|
||||
typeof node.source.value === 'string') {
|
||||
return node.source.value;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
/**
|
||||
* Returns the specifier text for the export. If it is aliased, we take care to return
|
||||
* the proper formatting.
|
||||
*/
|
||||
function getSpecifierText(specifier) {
|
||||
const exportedName = specifier.exported.type === utils_1.AST_NODE_TYPES.Literal
|
||||
? specifier.exported.raw
|
||||
: specifier.exported.name;
|
||||
const localName = specifier.local.type === utils_1.AST_NODE_TYPES.Literal
|
||||
? specifier.local.raw
|
||||
: specifier.local.name;
|
||||
return `${localName}${exportedName !== localName ? ` as ${exportedName}` : ''}`;
|
||||
}
|
||||
Reference in New Issue
Block a user