initial commit

This commit is contained in:
dude
2026-09-01 18:33:56 +00:00
commit c24449ee0a
2761 changed files with 302737 additions and 0 deletions
+12
View File
@@ -0,0 +1,12 @@
.idea
data
.travis.yml
screenshots
tmp
temp
node_modules
app/node_modules
npm-debug.log
scripts
docs
_releases
+36
View File
@@ -0,0 +1,36 @@
FROM node:22-alpine
ENV PSITRANSFER_UPLOAD_DIR=/data \
NODE_ENV=production
MAINTAINER Christoph Wiechert <wio@psitrax.de>
RUN apk add --no-cache tzdata
WORKDIR /app
ADD *.js package.json package-lock.json README.md /app/
ADD lib /app/lib
ADD app /app/app
ADD lang /app/lang
ADD plugins /app/plugins
ADD public /app/public
# Rebuild the frontend apps
RUN cd app && \
NODE_ENV=dev npm ci && \
npm run build && \
cd .. && \
mkdir /data && \
chown node /data && \
npm ci && \
rm -rf app
EXPOSE 3000
VOLUME ["/data"]
USER node
# HEALTHCHECK CMD wget -O /dev/null -q http://localhost:3000
CMD ["node", "app.js"]
+9
View File
@@ -0,0 +1,9 @@
Copyright 2017 Christoph Wiechert
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+116
View File
@@ -0,0 +1,116 @@
# PsiTransfer
[![Current Release](https://img.shields.io/github/release/psi-4ward/psitransfer.svg)](https://github.com/psi-4ward/psitransfer/releases)
[![Dependencies](https://david-dm.org/psi-4ward/psitransfer.svg)](https://david-dm.org/psi-4ward/psitransfer)
[![Known Vulnerabilities](https://snyk.io/test/github/psi-4ward/psitransfer/badge.svg)](https://snyk.io/test/github/psi-4ward/psitransfer)
[![Github Stars](https://img.shields.io/github/stars/psi-4ward/psitransfer.svg?style=social&label=Star)](https://github.com/psi-4ward/psitransfer)
[![Docker Stars](https://img.shields.io/docker/stars/psitrax/psitransfer.svg)](https://hub.docker.com/r/psitrax/psitransfer/)
[![Image Size](https://images.microbadger.com/badges/image/psitrax/psitransfer.svg)](https://microbadger.com/images/psitrax/psitransfer)
[![Docker Pulls](https://img.shields.io/docker/pulls/psitrax/psitransfer.svg)](https://hub.docker.com/r/psitrax/psitransfer/)
[![Docker Automated buil](https://img.shields.io/docker/automated/psitrax/psitransfer.svg)](https://hub.docker.com/r/psitrax/psitransfer/)
[![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=RTWDCH74TJN54&item_name=psitransfer)
Simple open source self-hosted file sharing solution.
It's an alternative to paid services like Dropbox, WeTransfer.
* No accounts, no logins
* Mobile friendly responsive interface
* Supports many and very big files (Streams ftw)
* Resumable up- and downloads ([tus.io](https://tus.io))
* Set an expire-time for your upload bucket
* One-time downloads
* Download all files as zip/tar.gz archive
* Modal-style file preview
* Requires Node >=7.4 or use `--harmony-async-await` flag
* Password protected download list ([AES](https://en.wikipedia.org/wiki/Advanced_Encryption_Standard))
* `/admin` Page lists bucket information, [Screenshot](https://raw.githubusercontent.com/psi-4ward/psitransfer/master/docs/PsiTransfer-Admin.png) (_disabled until you set `adminPass` config value_)
* Lightweight [Vue](https://vuejs.org) based frontend apps. Gzipped (on by default) less than 100k
* Explicit named bucket IDs with query param `sid=<myBucketID>`
**See the blog posts about PsiTransfer: https://psi.cx/tags/PsiTransfer/ and checkout the
[Documentation](https://github.com/psi-4ward/psitransfer/tree/master/docs)**
![Screenshot](https://raw.githubusercontent.com/psi-4ward/psitransfer/master/docs/psitransfer.gif)
## Quickstart
### Docker
```bash
$ docker run -p 0.0.0.0:3000:3000 -e PSITRANSFER_ADMIN_PASS=secret -v $PWD/data:/data psitrax/psitransfer
# data volume needs UID 1000
$ sudo chown -R 1000 $PWD/data
```
Specify the version by using [image tags](https://hub.docker.com/r/psitrax/psitransfer/tags/) e.g.:
* `latest`: corresponds to master branch
* `2`: latest stable `2.x.x`
* `1.1`: latest stable `1.1.x`
* `1.0.0`: exact version
### Manual, precompiled
```bash
# Be sure to have NodeJS >= 12
$ node -v
v12.4.0
# Download and extract latest release package from
# https://github.com/psi-4ward/psitransfer/releases
# Install dependencies and start the app
$ NODE_ENV=production npm install
$ npm start
```
### Manual, from source
```bash
# Compile the frontend apps
$ cd app
$ npm install
$ npm run build
# Install dependencies
$ cd ..
$ npm install
$ npm start
```
### Configuration
There are some configs in `config.js` like port and data-dir.
You can:
* Edit the `config.js` **(not recommend)**
* Add a `config.production.js` where `production` is the value from `NODE_ENV`
See `config.dev.js`
* Define environment Variables like `PSITRANSFER_UPLOAD_DIR` to set the upload directory
* To secure your PsiTransfer if exposed to the internet from unwanted, non authorized uploads use the `PSITRANSFER_UPLOAD_PASS` environment variable
### Customization
`public/pug/upload.pug` and `download.pug` are kept simple.
You can alter these files and add your logo and styles.
Please keep a footnote like *Powered by PsiTransfer* :)
### Debug
Psitransfer uses [debug](https://github.com/visionmedia/debug):
```bash
DEBUG=psitransfer:* npm start
```
## Side notes
* **There is no (end-to-end) payload encryption (yet)**.
* `Download all as ZIP` does not support resuming the download.
:star2: Contribution is highly welcome :metal:
Want to say thanks and buy me a beer? [![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=RTWDCH74TJN54&item_name=psitransfer)
## License
[BSD](LICENSE)
+60
View File
@@ -0,0 +1,60 @@
const https = require('https');
const fs = require('fs');
const config = require('./config');
const app = require('./lib/endpoints');
const eventBus = require('./lib/eventBus');
/**
* Naming:
* sid: Group of files
* key: File
* fid: {sid}++{key}
*/
let server;
if(config.port) {
// HTTP Server
server = app.listen(config.port, config.iface, () => {
console.log(`PsiTransfer listening on http://${config.iface}:${config.port}`);
eventBus.emit('listen', server);
});
}
let httpsServer;
if(config.sslPort && config.sslKeyFile && config.sslCertFile) {
// HTTPS Server
const sslOpts = {
key: fs.readFileSync(config.sslKeyFile),
cert: fs.readFileSync(config.sslCertFile)
};
httpsServer = https.createServer(sslOpts, app)
.listen(config.sslPort, config.iface, () => {
console.log(`PsiTransfer listening on https://${config.iface}:${config.sslPort}`);
eventBus.emit('listen', httpsServer);
});
}
// graceful shutdown
function shutdown() {
console.log('PsiTransfer shutting down...');
eventBus.emit('shutdown', server || httpsServer);
if(server) {
server.close(() => {
server = false;
if(!server && !httpsServer) process.exit(0);
});
}
if(httpsServer) {
httpsServer.close(() => {
httpsServer = false;
if(!server && !httpsServer) process.exit(0);
});
}
setTimeout(function() {
console.log('Could not close connections in time, forcefully shutting down');
process.exit(1);
}, 15 * 1000);
}
process.on('SIGTERM', shutdown);
process.on('SIGINT', shutdown);
+2
View File
@@ -0,0 +1,2 @@
#!/usr/bin/env node
require('./app');
+113
View File
@@ -0,0 +1,113 @@
const path = require('path');
const fs = require('fs');
const fsp = require('fs-promise');
// Default Config
// Do not edit this, generate a config.<ENV>.js for your NODE_ENV
// or use ENV-VARS like PSITRANSFER_PORT=8000
const config = {
"uploadDir": path.resolve(__dirname + '/everythingdata/data'),
// set to serve PsiTransfer from a sub-path
"baseUrl": '/',
// use to set custom upload url (subfolder to baseUrl)
"uploadAppPath": '/',
"iface": '0.0.0.0',
// set to false to disable HTTP
"port": 3000,
// HTTPS, set all 3 values to enable
"sslPort": 8443,
"sslKeyFile": false,
"sslCertFile": false,
// Force redirect to https
// can be true or a specific url like https://example.com:8443
// keep empty to disable
"forceHttps": '',
// Use trust proxy to show real IP on logs when reverse proxy are used
// see https://expressjs.com/en/guide/behind-proxies.html for options
"trustProxy": '',
// retention options in seconds:label
"retentions": {
"one-time": "one time download",
"3600": "1 Hour",
"21600": "6 Hours",
"86400": "1 Day",
"259200": "3 Days",
"604800": "1 Week",
"1209600": "2 Weeks",
"2419200": "4 Weeks",
"4838400": "8 Weeks"
},
// admin password, set to false to disable /admin page
// to enable /admin page set your password like this:
// "adminPass": "YourSecurePassword",
"adminPass": "l3tmefiles",
// upload password, set to false to disable
"uploadPass": false,
// make the bucket-password field mandatory
"requireBucketPassword": false,
"defaultRetention": "604800",
// expire every file after maxAge (eg never downloaded one-time files)
"maxAge": 3600 * 24 * 75, // 75 days
// maximum file-size for previews in byte
"maxPreviewSize": Math.pow(2, 20) * 2, // 2MB
"mailTemplate": 'mailto:?subject=File Transfer&body=You can download the files here: %%URL%%',
// see https://github.com/expressjs/morgan
// set to false to disable logging
"accessLog": ':date[iso] :method :url :status :response-time :remote-addr',
// event webhooks
// invokes an HTTP POST to a url whenever a file is downloaded
// for more info, see the webhooks section of docs/configuration.md
"fileDownloadedWebhook": null,
"fileUploadedWebhook": null,
// Fallback language
"defaultLanguage": "en",
// Limit upload size
"maxFileSize": null, // Math.pow(2, 30) * 2, // 2GB
"maxBucketSize": null, // Math.pow(2, 30) * 2, // 10GB
"plugins": ['file-downloaded-webhook', 'file-uploaded-webhook'],
// Disable the QR code button for download url sharing, set to true to disable
"disableQrCode": false,
};
// Load NODE_ENV specific config
const envConfFile = path.resolve(__dirname, `config.${ process.env.NODE_ENV }.js`);
if (process.env.NODE_ENV && fsp.existsSync(envConfFile)) {
Object.assign(config, require(envConfFile));
}
// Load config from ENV VARS
let envName;
for (let k in config) {
envName = 'PSITRANSFER_' + k.replace(/([A-Z])/g, $1 => "_" + $1).toUpperCase();
if (process.env[envName]) {
if (typeof config[k] === 'number') {
config[k] = parseInt(process.env[envName], 10);
} else if (Array.isArray(config[k])) {
config[k] = process.env[envName].split(',');
} else if (typeof config[k] === 'object') {
config[k] = JSON.parse(process.env[envName]);
} else {
config[k] = process.env[envName];
}
}
}
if (!config.baseUrl.endsWith('/')) config.baseUrl = config.baseUrl + '/';
if (!config.uploadAppPath.endsWith('/')) config.uploadAppPath = config.uploadAppPath + '/';
config.uploadAppPath = config.baseUrl.substr(0, config.baseUrl.length - 1) + config.uploadAppPath;
// Load language files
config.languages = {
[config.defaultLanguage]: require(`./lang/${ config.defaultLanguage }`) // default language
};
fs.readdirSync(path.resolve(__dirname, 'lang')).forEach(lang => {
lang = lang.replace('.js', '');
if (lang === config.defaultLanguage) return;
config.languages[lang] = {
...config.languages[config.defaultLanguage],
...require(`./lang/${ lang }`)
};
});
module.exports = config;
Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

+90
View File
@@ -0,0 +1,90 @@
# Configuration
First of all, I'll give an overview about the configuration options. See the
[config.js](https://github.com/psi-4ward/psitransfer/blob/master/config.js#L5) for
possible values. I do **not** recommend changing this file directly, better use one
of the following options.
## Config file: NODE_ENV related
PsiTransfer searches for an config file with the name `config.<NODE_ENV>.js` in the
root folder where `<NODE_ENV>` stands for the value of the environment parameter `NODE_ENV`.
If you start PsiTransfer using `npm start` it's `production` so you can create a
`config.production.js` with your settings. For example take a look at
[config.dev.js](https://github.com/psi-4ward/psitransfer/blob/master/config.dev.js).
This file is used when starting the application with `npm run dev`.
You are completely free to introduce own configs like `config.custom.js` and start
the app with `NODE_ENV=custom node app.js`.
## Environment variables
Some Linux distributions have `/etc/default/<daemon>` or `/etc/sysconfig/<daemon>`
files with environment configurations. Moreover, it's common to
configure the behaviour of Docker containers using environment parameters.
PsiTransfer supports overwriting every config value by environment parameters prefixed
with `PSITRANSFER_`.
```bash
export NODE_ENV=dev
export PSITRANSFER_RETENTIONS='{"one-time":"one time","3600":"1 Hour"}'
export PSITRANSFER_PORT=8080
node app.js
```
* The above example sets the `NODE_ENV` to `dev`.
If `config.dev.js` exists, it is loaded and overwrites the corresponding values from `config.js`.
* Then it will overwrite `retentions` and `port` with the values of the environment parameters.
> Environment parameters always have the highest priority.
## SSL
It's recommended to use Nginx for SSL termination, see [nginx-ssl-example.conf](https://github.com/psi-4ward/psitransfer/blob/master/docs/nginx-ssl-example.conf).
For native SSL support provide `sslPort`, `sslKeyFile`, `sslCertFile` options. To generate
a _snake oil_ certificate use `openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout cert.key -out cert.pem`.
To disable HTTP set the `port` config value to `false`.
## WebHooks
For the sake of integrating PsiTransfer with other systems, PsiTransfer can notify a webhook with a POST request on the following events:
### fileUploaded
On completion of a file upload, if `fileUploadedWebhook` is set in `config.<NODE_ENV>.js`, PsiTransfer will make a POST request to that url.
At the time of writing, the POST body will contain a data structure resembling this (serialized as json):
```json
{
"metadata": {
"sid": "6055ab792b6c",
"retention": 3600,
"password": "file password is in plaintext here",
"name": "test.png",
"comment": "User individual file comment goes here",
"type": "image/png",
"key": "135a3814-df46-4e23-b061-03bdda13425c",
"createdAt": 1589276618052
},
"date": 1589276619385
}
```
* Note: this event will fire many times if a user uploads multiple files in a single session (`sid`), as each individual file is uploaded separately. You'll notice that the `sid` will remain the same, but the `key` will change for each file.
* For file sync purposes (e.g. syncing client uploads to another service or long-term storage), you can reassemble a file fetch url with `https://<PSITRANSFER_HOST>/${sid}++${key}`
### fileDownloaded
When a user attempts to download a file, if `fileDownloadedWebhook` is set in `config.<NODE_ENV>.js`, PsiTransfer will make a POST request to that url.
At the time of writing, the POST body will contain a data structure resembling this (serialized as json):
```json
{
"sid": "6055ab792b6c",
"name": "test.png",
"date": 1589276619415
}
```
@@ -0,0 +1,29 @@
# Deployment using Docker
Using Docker is the most easy and recommended way to run PsiTransfer. There is an
[official Container](https://hub.docker.com/r/psitrax/psitransfer/) which is
updated whenever a GitHub push occurs.
```bash
docker run -d -v $PWD/data:/data -p 3000:3000 psitrax/psitransfer
```
The above command starts the PsiTransfer Docker container and
* `-d` puts the process into background (daemon mode)
* `-v` mounts the data volume into the container
* `-p` forwards the traffic from port 3000 into the container
**Protipp**: There are several [container tags](https://hub.docker.com/r/psitrax/psitransfer/tags/)
if you want to use a specific version. E.g. `1` is always the latest stable `1.x.x` and `1.1`
correlates with `1.1.x`.
If you want to customize some PsiTransfer configurations use environment parameters
by adding `-e` flags to the `docker run` command.
```bash
docker run -v $PWD/data:/data -p 3000:8080 \
-e PSITRANSFER_PORT=8080 \
-e PSITRANSFER_DEFAULT_RETENTION=3600 \
psitrax/psitransfer
```
**Protipp**: By adding `--restart always` Docker will autostart the container after reboots.
@@ -0,0 +1,43 @@
# Deployment as Systemd service
You can also install PsiTransfer as (Linux) system service. Most distributions
use Systemd as main init system. You should **not** run PsiTransfer with root privileges!
**Preparation**
```bash
# Create a target folder for PsiTransfer
mkdir -p /opt/psitransfer
cd /opt/psitransfer
# Download and extract a prebuild
curl -sL https://github.com/psi-4ward/psitransfer/releases/download/1.1.0-beta/psitransfer-1.1.0-beta.tar.gz | tar xz --strip 1
# Install dependencies
npm install --production
# Add a user psitransfer
sudo useradd --system psitransfer
# Make psitransfer owner of /opt/psitransfer
sudo chown -R psitransfer:psitransfer /opt/psitransfer
```
**Systemd unit file**
Grab the [psitransfer.service](https://github.com/psi-4ward/psitransfer/blob/master/docs/psitransfer.service)
sample file, put it in `/etc/systemd/system/` and adjust to your needs.
```bash
cd /etc/systemd/system
sudo wget https://raw.githubusercontent.com/psi-4ward/psitransfer/master/docs/psitransfer.service
# Start the service
sudo systemctl start psitransfer
# Show the status
sudo systemctl status psitransfer
# Enable autostart on boot
sudo systemctl enable psitransfer
```
@@ -0,0 +1,14 @@
# Layout customization
It is easy to customize the look of PsiTransfer. Almost all functional components are encapsulated
in the upload- and download-app and they only need a root element where they can be mounted.
This elements must have the attribute `id="upload"` and `id="download"`, respectively.
PsiTransfer uses [Bootstrap 3](http://getbootstrap.com/) as CSS-Framework and custom styles can
be found in [public/assets/styles.css](https://github.com/psi-4ward/psitransfer/blob/master/public/assets/styles.css).
If you want to give PsiTransfer your own custom look and feel just edit the files in `public/html` and/or
the `styles.css`. If you need to deliver assets like a custom logo put it into the `assets` folder.
But consider: You have to adopt further changes on PsiTransfer updates. This sould not happen very often.
@@ -0,0 +1,50 @@
# don't send the nginx version number in error pages and Server header
server_tokens off;
# redirect all http traffic to https
server {
listen 80;
server_name transfer.example.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name transfer.example.com;
error_log /var/log/nginx/transfer-error.log warn;
access_log /var/log/nginx/transfer-access.log main;
### SSL ###
ssl_certificate /etc/nginx/certs/transfer.example.com/fullchain.pem;
ssl_certificate_key /etc/nginx/certs/transfer.example.com/key.pem;
# enable session resumption to improve https performance
# http://vincent.bernat.im/en/blog/2011-ssl-session-reuse-rfc5077.html
ssl_session_cache shared:SSL:50m;
ssl_session_timeout 5m;
# openssl dhparam -out /etc/nginx/ssl/dhparam.pem 2048
ssl_dhparam /etc/nginx/ssl/dhparams.pem;
# enables server-side protection from BEAST attacks
# http://blog.ivanristic.com/2013/09/is-beast-still-a-threat.html
ssl_prefer_server_ciphers on;
# disable SSLv3(enabled by default since nginx 0.8.19) since it's less secure then TLS http://en.wikipedia.org/wiki/Secure_Sockets_Layer#SSL_3.0
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
# ciphers chosen for forward secrecy and compatibility
# http://blog.ivanristic.com/2013/08/configuring-apache-nginx-and-openssl-for-forward-secrecy.html
ssl_ciphers "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4";
location / {
# Bind PsiTransfer to localhost interface
# ie with PSITRANSFER_IFACE=127.0.0.1
proxy_pass "http://127.0.0.1:3000";
proxy_http_version 1.1;
proxy_buffering off;
proxy_request_buffering off; # needs nginx version >= 1.7.11
proxy_set_header Host $http_host;
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 788 KiB

@@ -0,0 +1,18 @@
[Unit]
Description=PsiTransfer
Documentation=https://github.com/psi-4ward/psitransfer
After=network.target
[Service]
Type=simple
User=psitransfer
Restart=on-failure
Environment=NODE_ENV=production
Environment=PSITRANSFER_PORT=8080
WorkingDirectory=/opt/psitransfer
ExecStart=/usr/bin/node app.js
[Install]
WantedBy=multi-user.target
+58
View File
@@ -0,0 +1,58 @@
module.exports = {
langCode: "de",
oldBrowserError: "Ihr zu alter Browser unterstützt diese Funktion nicht.",
dropFilesHere: "Dateien hier ablegen.",
noPreviewAvailable: "Keine Vorschau verfügbar",
preview: "Vorschau",
comment: "Kommentar",
settings: "Einstellungen",
retention: "Aufbewahrungsdauer",
password: "Passwort",
required: "erforderlich",
optional: "optional",
generateRandomPassword: "Zufälliges Passwort erstellen",
newUpload: "Neuer Upload",
accessDenied: "Zugriff verweigert!",
login: "Login",
email: "E-Mail",
sendViaMail: "Per E-Mail verschicken",
showQrCode: "QR-Code anzeigen",
clipboard: "Zwischenablage",
copyToClipboard: "In die Zwischenablage kopieren",
uploadCompleted: "Upload vollständig",
downloadLink: "Download Link",
upload: "hochladen",
retry: "erneut versuchen",
createNewUploadSession: "Neue Upload-Session erstellen?",
decrypt: "entschlüsseln",
files: "Dateien",
zipDownload: "Alle Dateien als ZIP herunterladen (kann nicht fortgesetzt werden)",
tarGzDownload: "Alle Dateien als tar.gz herunterladen (kann nicht fortgesetzt werden)",
oneTimeDownloadExpired: "Einmal-Download: Datei ist nicht mehr verfügbar.",
fileSizeExceed: "Dateigröße %% überschreitet das Maximum von %%",
bucketSizeExceed: "Uploadgröße %% überschreitet das Maximum von %%",
mailInvalid: "Das Format der E-Mail Adresse ist ungültig",
mailTo: "Senden an",
mailToPlaceholder: "E-Mail Adressen",
mailFrom: "Meine E-Mail Adresse",
mailFromPlaceholder: "E-Mail Adresse",
mailMessage: "Meine Nachricht",
mailDownloadNotification: "Benachrichtige mich, wenn eine Datei heruntergeladen wurde.",
mailSendBtn: "E-Mail verschicken",
mailsSent: "Die E-Mails wurden verschickt.",
mailSubjectUploader: "PsiTransfer Datei-Upload",
mailSubjectDownloader: "PsiTransfer Datei-Download",
mailSubjectFileDownloaded: "Datei wurde heruntergeladen",
retentions: {
"one-time": "Einmal-Download",
"3600": "1 Stunde",
"21600": "6 Stunden",
"86400": "1 Tag",
"259200": "3 Tage",
"604800": "1 Woche",
"1209600": "2 Wochen",
"2419200": "4 Wochen",
"4838400": "8 Wochen"
},
uploadPassword: "Passwort für Upload",
}
+58
View File
@@ -0,0 +1,58 @@
module.exports = {
langCode: "en",
oldBrowserError: "Your browser is too old and does not support this function.",
dropFilesHere: "Drop your files here",
noPreviewAvailable: "No preview available",
preview: "Preview",
comment: "Comment",
settings: "Settings",
retention: "Retention",
password: "Password",
required: "required",
optional: "optional",
generateRandomPassword: "Generate a random password",
newUpload: "New upload",
accessDenied: "Access denied!",
login: "Login",
email: "E-Mail",
clipboard: "Clipboard",
copyToClipboard: "Copy to clipboard",
sendViaMail: "Send via E-Mail",
showQrCode: "Show QR-Code",
uploadCompleted: "Upload completed",
downloadLink: "Download Link",
upload: "upload",
retry: "retry",
createNewUploadSession: "Create a new upload session?",
decrypt: "decrypt",
files: "Files",
zipDownload: "Download all files as ZIP (not resumeable)",
tarGzDownload: "Download all files as tar.gz (not resumeable)",
oneTimeDownloadExpired: "One-Time Download: File is not available anymore.",
fileSizeExceed: "File size %% exceeds maximum of %%",
bucketSizeExceed: "Upload size %% exceeds maximum of %%",
mailInvalid: "The e-mail address looks faulty",
mailTo: "Send to",
mailToPlaceholder: "E-Mail addresses",
mailFrom: "My E-Mail address",
mailFromPlaceholder: "E-Mail address",
mailMessage: "My message",
mailDownloadNotification: "Notify me when a file has been downloaded.",
mailSendBtn: "Send E-Mail",
mailsSent: "The e-mails have been sent.",
mailSubjectUploader: "PsiTransfer File-Upload",
mailSubjectDownloader: "PsiTransfer File-Download",
mailSubjectFileDownloaded: "File has been downloaded",
retentions: {
"one-time": "one time download",
"3600": "1 Hour",
"21600": "6 Hours",
"86400": "1 Day",
"259200": "3 Days",
"604800": "1 Week",
"1209600": "2 Weeks",
"2419200": "4 Weeks",
"4838400": "8 Weeks"
},
uploadPassword: "Password for uploading",
}
+57
View File
@@ -0,0 +1,57 @@
module.exports = {
langCode: "es",
oldBrowserError: "Tu navegador es demasiado antiguo y no soporta esta función.",
dropFilesHere: "Suelta tus archivos aquí",
noPreviewAvailable: "Vista previa no disponible",
preview: "Vista previa",
comment: "Comentario",
settings: "Ajustes",
retention: "Retención",
password: "Contraseña",
required: "requerido",
optional: "opcional",
generateRandomPassword: "Generar una contraseña aleatoria",
newUpload: "Nueva subida",
accessDenied: "¡Acceso denegado!",
login: "Iniciar sesión",
email: "Correo electrónico",
clipboard: "Portapapeles",
copyToClipboard: "Copiar al portapapeles",
sendViaMail: "Enviar por correo electrónico",
showQrCode: "Mostrar código QR",
uploadCompleted: "Subida completada",
downloadLink: "Enlace de descarga",
upload: "subir",
retry: "reintentar",
createNewUploadSession: "¿Crear una nueva sesión de subida?",
decrypt: "descifrar",
files: "Archivos",
zipDownload: "Descargar como archivo zip (no reanudable)",
tarGzDownload: "Descargar como archivo tar.gz (no reanudable)",
oneTimeDownloadExpired: "Descarga única: El archivo ya no está disponible.",
fileSizeExceed: "El tamaño del archivo %% supera el máximo de %%",
bucketSizeExceed: "El tamaño de la subida %% supera el máximo de %%",
mailInvalid: "La dirección de correo electrónico parece inválida",
mailTo: "Enviar a",
mailToPlaceholder: "Direcciones de correo electrónico",
mailFrom: "Mi dirección de correo electrónico",
mailFromPlaceholder: "Dirección de correo electrónico",
mailMessage: "Mi mensaje",
mailDownloadNotification: "Notifícame cuando se haya descargado un archivo.",
mailSendBtn: "Enviar",
mailsSent: "Los mensajes han sido enviados.",
mailSubjectUploader: "PsiTransfer - Subida de archivos",
mailSubjectDownloader: "PsiTransfer - Descarga de archivos",
mailSubjectFileDownloaded: "El archivo ha sido descargado",
retentions: {
"one-time": "Descarga única",
"3600": "1 hora",
"21600": "6 horas",
"86400": "1 día",
"259200": "3 días",
"604800": "1 semana",
"1209600": "2 semanas",
"2419200": "4 semanas",
"4838400": "8 semanas"
},
}
+58
View File
@@ -0,0 +1,58 @@
module.exports = {
langCode: "fr",
oldBrowserError: "Votre navigateur est obsolète et ne supporte pas cette fonction.",
dropFilesHere: "Déposez vos fichiers ici",
noPreviewAvailable: "Prévisualisation non disponible",
preview: "Prévisualisation",
comment: "Commentaire",
settings: "Réglages",
retention: "Rétention",
password: "Mot de passe",
required: "requis",
optional: "optionnel",
generateRandomPassword: "Générer un mot de passe aléatoire",
newUpload: "Nouveau téléversement",
accessDenied: "Accès refusé !",
login: "Login",
email: "E-Mail",
clipboard: "Presse-papier",
copyToClipboard: "Copier le lien",
sendViaMail: "Envoyer via E-Mail",
showQrCode: "Afficher le QR-Code",
uploadCompleted: "Téléversement terminé",
downloadLink: "Lien de téléchargement",
upload: "téléverser",
retry: "réessayer",
createNewUploadSession: "Créer un nouveau transfert ?",
decrypt: "déchiffrer",
files: "Fichiers",
zipDownload: "Télécharger comme archive Zip (ne peut être repris)",
tarGzDownload: "Télécharger comme archive tar.gz (ne peut être repris)",
oneTimeDownloadExpired: "Téléchargement unique: le fichier n'est plus disponible.",
fileSizeExceed: "La taille du fichier %% dépasse le maximum de %%",
bucketSizeExceed: "La taille du transfert %% dépasse le maximum de %%",
mailInvalid: "L'adresse e-mail semble invalide",
mailTo: "Envoyer à",
mailToPlaceholder: "Adresses e-mail",
mailFrom: "Mon adresse e-mail",
mailFromPlaceholder: "Adresse e-mail",
mailMessage: "Mon message",
mailDownloadNotification: "Notifiez moi quand un fichier a été téléchargé.",
mailSendBtn: "Envoyer",
mailsSent: "Les messages ont été envoyés.",
mailSubjectUploader: "PsiTransfer - Téléversement de fichiers",
mailSubjectDownloader: "PsiTransfer - Téléchargement de fichiers",
mailSubjectFileDownloaded: "Le fichier a été téléchargé",
retentions: {
"one-time": "téléchargement unique",
"3600": "1 heure",
"21600": "6 heures",
"86400": "1 jour",
"259200": "3 jours",
"604800": "1 semaine",
"1209600": "2 semaines",
"2419200": "4 semaines",
"4838400": "8 semaines"
},
uploadPassword: "Mot de passe d'accès au téléversement",
}
+58
View File
@@ -0,0 +1,58 @@
module.exports = {
langCode: "it",
oldBrowserError: "Il tuo browser è troppo vecchio e non supporta questa funzione.",
dropFilesHere: "Metti il tuo file qui",
noPreviewAvailable: "Nessuna anteprima disponibile",
preview: "Anteprima",
comment: "Commento",
settings: "Impostazioni",
retention: "Ritenzione",
password: "Password",
required: "richiesto",
optional: "opzionale",
generateRandomPassword: "Genera una password randomica",
newUpload: "Nuovo caricamento",
accessDenied: "Accesso rifiutato!",
login: "Login",
email: "E-Mail",
clipboard: "Appunti",
copyToClipboard: "Copia negli appunti",
sendViaMail: "Invia tramite E-Mail",
showQrCode: "Mostra il QR-Code",
uploadCompleted: "Caricamento completato",
downloadLink: "Link per lo scaricamento",
upload: "caricamento",
retry: "ritenta",
createNewUploadSession: "Creare una nuova sessione di caricamento?",
decrypt: "decripta",
files: "Files",
zipDownload: "Scarica tutti i file come ZIP (non ripristinabile)",
tarGzDownload: "Scarica tutti i file come tar.gz (non ripristinabile)",
oneTimeDownloadExpired: "Scaricamento singolo: Il File non sarà più disponibile",
fileSizeExceed: "La dimensione del File %% eccede il massiomo di %%",
bucketSizeExceed: "Grandezza di caricamento %% eccede il massimo di %%",
mailInvalid: "L'indirizzo e-mail sembra errato",
mailTo: "Invia a",
mailToPlaceholder: "Indirizzo E-Mail",
mailFrom: "Il mio indirizzo E-Mail",
mailFromPlaceholder: "Indirizzo E-Mail",
mailMessage: "Il mio messaggio",
mailDownloadNotification: "Notificami quando il file viene scaricato.",
mailSendBtn: "Invia E-Mail",
mailsSent: "L'e-mails é stata inviata.",
mailSubjectUploader: "PsiTransfer File-Upload",
mailSubjectDownloader: "PsiTransfer File-Download",
mailSubjectFileDownloaded: "Il file é stato scaricato",
retentions: {
"one-time": "Scaricamento singolo",
"3600": "1 Ora",
"21600": "6 Ore",
"86400": "1 Giorno",
"259200": "3 Giorni",
"604800": "1 Settimana",
"1209600": "2 Settimane",
"2419200": "4 Settimane",
"4838400": "8 Settimane"
},
uploadPassword: "Password per il caricamento",
}
+58
View File
@@ -0,0 +1,58 @@
module.exports = {
langCode: "ja",
oldBrowserError: "お使いの Web ブラウザが古すぎるため、この機能をサポートしていません。",
dropFilesHere: "ここにファイルをドロップ",
noPreviewAvailable: "プレビューを表示できません",
preview: "プレビュー",
comment: "コメント",
settings: "設定",
retention: "有効期限",
password: "パスワード",
required: "必須",
optional: "任意",
generateRandomPassword: "適当なパスワードを生成",
newUpload: "新規アップロード",
accessDenied: "パスワードが違います。アクセスが拒否されました!",
login: "ログイン",
email: "メールアドレス",
clipboard: "クリップボード",
copyToClipboard: "クリップボードにコピーする",
sendViaMail: "メールで送る",
showQrCode: "QR コードを表示する",
uploadCompleted: "アップロードが完了しました",
downloadLink: "ダウンロード リンク",
upload: "アップロード",
retry: "再試行",
createNewUploadSession: "新たにアップロードしますか?",
decrypt: "認証する",
files: "ファイル",
zipDownload: "すべてのファイルを Zip ファイルとしてダウンロードする (中途からのダウンロード再開不可)",
tarGzDownload: "すべてのファイルを tar.gz ファイルとしてダウンロードする (中途からのダウンロード再開不可)",
oneTimeDownloadExpired: "1度だけのダウンロード: 該当ファイルは今後ダウンロードできなくなります",
fileSizeExceed: "ファイルサイズが %% あり、最大値の %% を超えています",
bucketSizeExceed: "アップロードサイズが %% あり、最大値の %% を超えています",
mailInvalid: "メールアドレスが無効です",
mailTo: "送信先メールアドレス",
mailToPlaceholder: "送信先メールアドレス",
mailFrom: "発信元メールアドレス",
mailFromPlaceholder: "発信元メールアドレス",
mailMessage: "メッセージ",
mailDownloadNotification: "先方がダウンロードしたら通知する",
mailSendBtn: "メール送信",
mailsSent: "メールを送信しました。",
mailSubjectUploader: "PsiTransfer ファイル送信",
mailSubjectDownloader: "PsiTransfer ファイル受信",
mailSubjectFileDownloaded: "ファイルがダウンロードされました",
retentions: {
"one-time": "一度だけ",
"3600": "1 時間",
"21600": "6 時間",
"86400": "1 日",
"259200": "3 日",
"604800": "1 週間",
"1209600": "2 週間",
"2419200": "4 週間",
"4838400": "8 週間"
},
uploadPassword: "送信パスワード",
}
+57
View File
@@ -0,0 +1,57 @@
module.exports = {
langCode: "ko",
oldBrowserError: "브라우저가 너무 오래되어서 이 기능을 지원하지 않습니다.",
dropFilesHere: "여기에 파일을 놓으세요",
noPreviewAvailable: "미리보기 사용 불가",
preview: "미리보기",
comment: "코멘트",
settings: "설정",
retention: "보유",
password: "비밀번호",
required: "필수 사항",
optional: "선택 사항",
generateRandomPassword: "렌덤 비밀번호를 생성하기",
newUpload: "새 업로드",
accessDenied: "접근 거부!",
login: "로드인",
email: "이메일",
clipboard: "클립보드",
copyToClipboard: "클립보드에 복사하기",
sendViaMail: "이메일로 보내기",
showQrCode: "QR 코드 보기",
uploadCompleted: "업로드 완료됨",
downloadLink: "링크 다운로드하기",
upload: "업로드",
retry: "재시도",
createNewUploadSession: "새로운 업로드 세션 창조하기?",
decrypt: "해독",
files: "파일",
zipDownload: ".zip로 모든 파일을 다운로드하기 (재시작 불가능)",
tarGzDownload: ".tar.gz로 모든 파일을 다운로드하기 (재시작 불가능)",
oneTimeDownloadExpired: "1회만의 다운로드: 파일은 이미 사용 불가입니다.",
fileSizeExceed: "파일 크기 %% 은/는 최대의 %% 초과합니다",
bucketSizeExceed: "업로드 크기 %% 은/는 최대의 %% 초과합니다",
mailInvalid: "이메일 주소를 불완전합니다",
mailTo: "...로 보내기",
mailToPlaceholder: "이메일 주소",
mailFrom: "내 이메일 주소",
mailFromPlaceholder: "이메일 주소",
mailMessage: "내 메시지",
mailDownloadNotification: "파일 다운로드할 때 내게 알리기.",
mailSendBtn: "이메일 보내기",
mailsSent: "이메일(들)은 서공적으로 보낼.",
mailSubjectUploader: "PsiTransfer 파일 업로드",
mailSubjectDownloader: "PsiTransfer 파일 다운로드",
mailSubjectFileDownloaded: "파일은 다운로드됨",
retentions: {
"one-time": "1회만 다운로드",
"3600": "1 시간",
"21600": "6 시간",
"86400": "1 일",
"259200": "3 일",
"604800": "1 주일",
"1209600": "2 주일",
"2419200": "4 주일",
"4838400": "8 주일"
},
}
+57
View File
@@ -0,0 +1,57 @@
module.exports = {
langCode: "nl",
oldBrowserError: "Uw browser is te oud en ondersteunt deze functie niet.",
dropFilesHere: "Sleep uw bestanden in dit vak",
noPreviewAvailable: "Geen voorbeeld beschikbaar",
preview: "Voorbeeld",
comment: "Opmerking",
settings: "Instellingen",
retention: "Vervaltermijn",
password: "Wachtwoord",
required: "verplicht",
optional: "optioneel",
generateRandomPassword: "Genereer een willekeurig wachtwoord",
newUpload: "Nieuwe upload",
accessDenied: "Geen toegang!",
login: "Login",
email: "E-Mail",
clipboard: "Clipboard",
copyToClipboard: "Kopieer naar clipboard",
sendViaMail: "Verstuur via e-mail",
showQrCode: "Toon QR-Code",
uploadCompleted: "Upload voltooid",
downloadLink: "Download link",
upload: "upload",
retry: "probeer opnieuw",
createNewUploadSession: "Nieuwe uploadsessie aanmaken?",
decrypt: "decoderen",
files: "Bestanden",
zipDownload: "Download alle bestanden als ZIP (niet hervatbaar)",
tarGzDownload: "Download alle bestanden als tar.gz (niet hervatbaar)",
oneTimeDownloadExpired: "Eenmalige download: Bestand is niet meer beschikbaar.",
fileSizeExceed: "Bestandsgrootte %% overschrijdt maximum van %%",
bucketSizeExceed: "Uploadgrootte %% overschrijdt maximum van %%",
mailInvalid: "Het e-mailadres ziet er niet correct uit",
mailTo: "Verstuur naar",
mailToPlaceholder: "E-mailaddressen",
mailFrom: "Mijn e-mailadres",
mailFromPlaceholder: "E-mailadres",
mailMessage: "Mijn bericht",
mailDownloadNotification: "Laat het me weten als er een bestand is gedownload.",
mailSendBtn: "Vestuur e-mail",
mailsSent: "De e-mails zijn verzonden.",
mailSubjectUploader: "PsiTransfer bestand upload",
mailSubjectDownloader: "PsiTransfer bestand download",
mailSubjectFileDownloaded: "Bestand is gedownload",
retentions: {
"one-time": "eenmalige download",
"3600": "1 uur",
"21600": "6 uur",
"86400": "1 dag",
"259200": "3 dagen",
"604800": "1 week",
"1209600": "2 weken",
"2419200": "4 weken",
"4838400": "8 weken"
},
}
+57
View File
@@ -0,0 +1,57 @@
module.exports = {
langCode: "pl",
oldBrowserError: "Twoja przeglądarka jest za stara i nie wspiera tej funkcji.",
dropFilesHere: "Upuść pliki tutaj",
noPreviewAvailable: "Podgląd jest niedostępny",
preview: "Podgląd",
comment: "Komentarz",
settings: "Ustawienia",
retention: "Czas retencji plików",
password: "Hasło",
required: "wymagane",
optional: "opcjonalne",
generateRandomPassword: "Wygeneruj losowe hasło",
newUpload: "Nowe przesyłanie",
accessDenied: "Dostęp zabroniony!",
login: "Zaloguj",
email: "E-Mail",
clipboard: "Schowek",
copyToClipboard: "Skopiuj do schowka",
sendViaMail: "Wyślij poprzez E-Mail",
showQrCode: "Pokaż kod QR",
uploadCompleted: "Przesyłanie ukończone",
downloadLink: "Odnośnik do pobierania",
upload: "prześlij",
retry: "powtórz",
createNewUploadSession: "Utworzyć nową sesję przesyłania?",
decrypt: "odszyfruj",
files: "Pliki",
zipDownload: "Pobierz wszystkie pliki jako archiwum ZIP (ponawianie niemożliwe)",
tarGzDownload: "Pobierz wszystkie pliki jako tar.gz (ponawianie niemożliwe)",
oneTimeDownloadExpired: "Jednorazowe pobieranie: Plik nie jest już dostępny.",
fileSizeExceed: "Rozmiar pliku %% przekracza maksymalny rozmiar %%",
bucketSizeExceed: "Wysyłane dane %% przekraczają maksymalny rozmiar %%",
mailInvalid: "Adres e-mail wygląda na nieprawidłowy",
mailTo: "Wyślij do",
mailToPlaceholder: "Adres E-Mail",
mailFrom: "Twój adres E-Mail",
mailFromPlaceholder: "Adres E-Mail",
mailMessage: "Twoja wiadomość",
mailDownloadNotification: "Powiadom mnie kiedy plik zostanie pobrany.",
mailSendBtn: "Wyślij E-Mail",
mailsSent: "Wiadomości E-Mail zostały wysłane.",
mailSubjectUploader: "PsiTransfer przesyłanie plików",
mailSubjectDownloader: "PsiTransfer pobieranie plików",
mailSubjectFileDownloaded: "Plik został pobrany",
retentions: {
"one-time": "pobieranie jednorazowe",
"3600": "1 godzina",
"21600": "6 godzin ",
"86400": "1 dzień",
"259200": "3 dni",
"604800": "1 tydzień",
"1209600": "2 tygodnie",
"2419200": "4 tygodnie",
"4838400": "8 tygodni"
},
}
+57
View File
@@ -0,0 +1,57 @@
module.exports = {
langCode: "pt",
oldBrowserError: "Seu navegador é obsoleto e não suporta esta funcionalidade.",
dropFilesHere: "Arraste seus arquivos aqui",
noPreviewAvailable: "Pré-visualização indisponível",
preview: "Visualizar",
comment: "Comentário",
settings: "Configurações",
retention: "Retenção",
password: "Senha",
required: "obrigatório",
optional: "opcional",
generateRandomPassword: "Gerar senha aleatória",
newUpload: "Novo envio",
accessDenied: "Acesso negado!",
login: "Login",
email: "E-Mail",
clipboard: "Área de transferência",
copyToClipboard: "Copiar para área de transferência",
sendViaMail: "Enviar por e-mail",
showQrCode: "Exibir QR-Code",
uploadCompleted: "Envio completo",
downloadLink: "Link para download",
upload: "envio",
retry: "tentar novamente",
createNewUploadSession: "Criar uma nova sessão de envio?",
decrypt: "decriptografar",
files: "Arquivos",
zipDownload: "Baixar todos os arquivos como ZIP (não resumível)",
tarGzDownload: "Baixar todos os arquivos como tar.gz (não resumível)",
oneTimeDownloadExpired: "Download Único: O arquivo não está mais disponível.",
fileSizeExceed: "O tamanho do arquivo %% excede o máximo permitido de %%",
bucketSizeExceed: "O tamanho do envio %% excede o máximo permitido de %%",
mailInvalid: "O endereço de e-mail parece inválido",
mailTo: "Enviar para",
mailToPlaceholder: "Endereço de e-mail",
mailFrom: "Meu endereço de e-mail",
mailFromPlaceholder: "Endereço de e-mail",
mailMessage: "Minha mensagem",
mailDownloadNotification: "Notificar-me quando um arquivo for baixado.",
mailSendBtn: "Enviar e-mail",
mailsSent: "Os e-mails foram enviados.",
mailSubjectUploader: "PsiTransfer File-Upload",
mailSubjectDownloader: "PsiTransfer File-Download",
mailSubjectFileDownloaded: "O arquivo foi baixado",
retentions: {
"one-time": "baixar uma única vez",
"3600": "1 Hora",
"21600": "6 Horas",
"86400": "1 Dia",
"259200": "3 Dias",
"604800": "1 Semana",
"1209600": "2 Semanas",
"2419200": "4 Semanas",
"4838400": "8 Semanas"
},
}
+57
View File
@@ -0,0 +1,57 @@
module.exports = {
langCode: "ru",
oldBrowserError: "Ваш браузер устарел и больше не поддерживается",
dropFilesHere: "Перетащите сюда файлы для загрузки",
noPreviewAvailable: "Предпросмотр недоступен",
preview: "Предпросмотр",
comment: "Комментарий",
settings: "Настройки",
retention: "Срок хранения",
password: "Пароль",
required: "Обязательно",
optional: "Не обязательно",
generateRandomPassword: "Сгенерировать случайный пароль",
newUpload: "Новая загрузка",
accessDenied: "Доступ запрещён!",
login: "Логин",
email: "Эл. почта",
clipboard: "Ссылка",
copyToClipboard: "Скопировать ссылку",
sendViaMail: "Отправить по эл. почте",
showQrCode: "Показать QR-код",
uploadCompleted: "Отправка завершена",
downloadLink: "Ссылка для скачивания",
upload: "отправка",
retry: "повтор",
createNewUploadSession: "Отправить другой файл?",
decrypt: "Расшифровать",
files: "Файлы",
zipDownload: "Скачать все файлы одним ZIP-архивом (без дозагрузки)",
tarGzDownload: "Скачать все файлы одним TAR-архивом (без дозагрузки)",
oneTimeDownloadExpired: "Одноразовая загрузка: Файл больше недоступен",
fileSizeExceed: "Размер файла (%%) больше допустимого (%%)",
bucketSizeExceed: "Размер файла (%%) больше допустимого (%%)",
mailInvalid: "Неверный адрес эл. почты",
mailTo: "Отправить",
mailToPlaceholder: "Эл. адрес",
mailFrom: "Мой эл. адрес",
mailFromPlaceholder: "Эл. адрес",
mailMessage: "Сообщение",
mailDownloadNotification: "Уведомить меня, когда файл будет скачан",
mailSendBtn: "Отправить по эл. почте",
mailsSent: "Эл. письмо было отправлено",
mailSubjectUploader: "Файл загружен",
mailSubjectDownloader: "Файл скачан",
mailSubjectFileDownloaded: "Файл был скачан",
retentions: {
"one-time": "Одноразовая загрузка",
"3600": "1 час",
"21600": "6 часов",
"86400": "1 день",
"259200": "3 дня",
"604800": "1 неделя",
"1209600": "2 недели",
"2419200": "4 недели",
"4838400": "8 недель"
},
}
+57
View File
@@ -0,0 +1,57 @@
module.exports = {
langCode: "sv",
oldBrowserError: "Din webbläsare är för gammal och stöder inte den här funktionen.",
dropFilesHere: "Släpp dina filer här",
noPreviewAvailable: "Ingen förhandsvisning tillgänglig",
preview: "Förhandsgranskning",
comment: "Kommentar",
settings: "Inställningar",
retention: "Lagringstid",
password: "Lösenord",
required: "obligatoriskt",
optional: "valfritt",
generateRandomPassword: "Generera ett slumpmässigt lösenord",
newUpload: "Ny uppladdning",
accessDenied: "Åtkomst nekad!",
login: "Logga in",
email: "E-post",
clipboard: "Urklipp",
copyToClipboard: "Kopiera till urklipp",
sendViaMail: "Skicka via e-post",
showQrCode: "Visa QR-kod",
uploadCompleted: "Uppladdning klar",
downloadLink: "Nedladdningslänk",
upload: "uppladding",
retry: "försök igen",
createNewUploadSession: "Skapa ny uppladdningssession?",
decrypt: "dekryptera",
files: "Filer",
zipDownload: "Ladda ner alla filer som ZIP (går inte att återuppta)",
tarGzDownload: "Ladda ner alla filer som tar.gz (går inte att återuppta)",
oneTimeDownloadExpired: "Engångsnedladdning: Filen är inte tillgänglig längre.",
fileSizeExceed: "Filens storlek %% överstiger maximum %%",
bucketSizeExceed: "Överföringsstorlek %% överstiger maximalt %%",
mailInvalid: "E-postadressen ser inte korrekt ut",
mailTo: "Skicka till",
mailToPlaceholder: "E-postadresser",
mailFrom: "Min e-postadress",
mailFromPlaceholder: "E-postadress",
mailMessage: "Mitt meddelande",
mailDownloadNotification: "Meddela mig när en fil har hämtats.",
mailSendBtn: "Skicka e-post",
mailsSent: "E-postmeddelandena har skickats.",
mailSubjectUploader: "PsiTransfer File-Upload",
mailSubjectDownloader: "PsiTransfer File-Download",
mailSubjectFileDownloaded: "Filen har laddats ner",
retentions: {
"one-time": "engångs nedladdning",
"3600": "1 timme",
"21600": "6 timmar",
"86400": "1 dag",
"259200": "3 dagar",
"604800": "1 vecka",
"1209600": "2 veckor",
"2419200": "4 veckor",
"4838400": "8 veckor"
},
}
+58
View File
@@ -0,0 +1,58 @@
module.exports = {
langCode: "tr",
oldBrowserError: "Tarayıcınız bu fonksiyonu kullanmak için çok eski",
dropFilesHere: "Dosyalarınızı buraya sürükleyin",
noPreviewAvailable: "Önizleme yok",
preview: "Önizleme",
comment: "Yorum",
settings: "Ayarlar",
retention: "Geçerlilik Süresi",
password: "Parola",
required: "Zorunlu",
optional: "Opsiyonel",
generateRandomPassword: "Rastgele praola oluştur",
newUpload: "Yeni Yükleme",
accessDenied: "Erişim Engellendi",
login: "Giriş",
email: "E-Posta",
clipboard: "Pano",
copyToClipboard: "Panoya Kopyala",
sendViaMail: "E-Posta ile Gönder",
showQrCode: "QR Kod Göster",
uploadCompleted: "Yükleme Tamamlandı",
downloadLink: "İndirme Linki",
upload: "Yükle",
retry: "Tekrar",
createNewUploadSession: "Yeni Yükleme?",
decrypt: "decrypt",
files: "Dosyalar",
zipDownload: "Tüm Dosyaları ZIP olarak indir",
tarGzDownload: "Tüm Dosyaları tar.gz olarak indir",
oneTimeDownloadExpired: "Tek seferlik indirme. Süre dolmuş",
fileSizeExceed: "Dosya Boyutu Büyük %% Maksimum: %%",
bucketSizeExceed: "Yükleme Boyutu Büyük %% Maksimum: %%",
mailInvalid: "E-Posta adresi hatalı gözüküyor",
mailTo: "Gönder:",
mailToPlaceholder: "E-Posta Adresleri",
mailFrom: "E-Posta Adresim",
mailFromPlaceholder: "E-Posta Adresi",
mailMessage: "Mesajım",
mailDownloadNotification: "Dosyam indirilince bilgi ver",
mailSendBtn: "E-Posta gönder",
mailsSent: "E-Postalar gönderildi",
mailSubjectUploader: "PsiTransfer File-Upload",
mailSubjectDownloader: "PsiTransfer Dosya Yüklemesi",
mailSubjectFileDownloaded: "Dosya indirildi",
retentions: {
"one-time": "Tek Seferlik",
"3600": "1 Saat",
"21600": "6 Saat",
"86400": "1 Gün",
"259200": "3 Gün",
"604800": "1 Hafta",
"1209600": "2 Hafta",
"2419200": "4 Hafta",
"4838400": "8 Hafta"
},
uploadPassword: "Yükleme için parola",
}
+57
View File
@@ -0,0 +1,57 @@
module.exports = {
langCode: "zh-tw",
oldBrowserError: "您的瀏覽器版本不支援此功能。",
dropFilesHere: "在此拖放檔案",
noPreviewAvailable: "沒有預覽",
preview: "預覽",
comment: "備註",
settings: "設定",
retention: "有效期限",
password: "密碼",
required: "必填",
optional: "選填",
generateRandomPassword: "產生隨機密碼",
newUpload: "新增上傳",
accessDenied: "密碼錯誤!",
login: "登入",
email: "電子郵件",
clipboard: "複製連結",
copyToClipboard: "複製到剪貼板",
sendViaMail: "透過電郵傳送",
showQrCode: "顯示 QR 碼",
uploadCompleted: "上傳成功",
downloadLink: "下載連結",
upload: "上傳",
retry: "重試",
createNewUploadSession: "要建立新的上傳?",
decrypt: "解鎖",
files: "檔案列表",
zipDownload: "將所有檔案下載為 zip 壓縮檔 (無法續傳)",
tarGzDownload: " 將所有檔案下載為 tar.gz 壓縮檔 (無法續傳)",
oneTimeDownloadExpired: "此檔案僅限單次下載,已無法使用。",
fileSizeExceed: "檔案大小 %% 超過最大容量 %%",
bucketSizeExceed: "總上傳大小 %% 超過最大容量 of %%",
mailInvalid: "電郵地址不正確",
mailTo: "傳送到",
mailToPlaceholder: "電郵地址",
mailFrom: "我的電郵地址",
mailFromPlaceholder: "電郵地址",
mailMessage: "我的訊息",
mailDownloadNotification: "檔案被下載時通知我。",
mailSendBtn: "傳送電子郵件",
mailsSent: "電子郵件已傳送",
mailSubjectUploader: "PsiTransfer 檔案上傳",
mailSubjectDownloader: "PsiTransfer 檔案下載",
mailSubjectFileDownloaded: "檔案已被下載",
retentions: {
"one-time": "單次下載",
"3600": "1 小時",
"21600": "6 小時",
"86400": "1 天",
"259200": "3 天",
"604800": "1 週",
"1209600": "2 週",
"2419200": "4 週",
"4838400": "8 週"
},
}
+57
View File
@@ -0,0 +1,57 @@
module.exports = {
langCode: "zh",
oldBrowserError: "你的浏览器版本不支持使用.",
dropFilesHere: "点击上传或将文件拖动到这里",
noPreviewAvailable: "无可用预览",
preview: "预览",
comment: "备注",
settings: "设置",
retention: "有效期",
password: "提取码",
required: "必选",
optional: "可选",
generateRandomPassword: "生成随机提取码",
newUpload: "新的上传",
accessDenied: "验证失败!",
login: "登录",
email: "邮件",
clipboard: "复制链接",
copyToClipboard: "复制到剪贴板",
sendViaMail: "发送邮件",
showQrCode: "显示二维码",
uploadCompleted: "上传完成",
downloadLink: "下载链接",
upload: "上传",
retry: "重试",
createNewUploadSession: "创建新的上传会话?",
decrypt: "解锁",
files: "文件上传列表",
zipDownload: " 压缩打包下载(zip格式)",
tarGzDownload: "压缩打包下载(tar.gz格式)",
oneTimeDownloadExpired: "该链接被设置为阅后即焚,文件已销毁.",
fileSizeExceed: "文件大小 %% 超过最大体积 of %%",
bucketSizeExceed: "上传大小 %% 超过最大体积 %%",
mailInvalid: "邮件地址不正确",
mailTo: "发送到",
mailToPlaceholder: "邮件地址",
mailFrom: "我的邮件地址",
mailFromPlaceholder: "邮件地址",
mailMessage: "我的消息",
mailDownloadNotification: "下载文件时通知我.",
mailSendBtn: "发送电子邮件",
mailsSent: "电子邮件已经发送了.",
mailSubjectUploader: "文件快传-上传",
mailSubjectDownloader: "文件快传-下载",
mailSubjectFileDownloaded: "文件已下载",
retentions: {
"one-time": "仅限下载一次(阅后即焚)",
"3600": "1 小时",
"21600": "6 小时",
"86400": "1 天",
"259200": "3 天",
"604800": "1 周",
"1209600": "2 周",
"2419200": "1 月",
"4838400": "8 周"
},
}
+159
View File
@@ -0,0 +1,159 @@
'use strict';
const fsp = require('fs-promise');
const path = require('path');
const debug = require('debug')('psitransfer:db');
const config = require('../config');
module.exports = class DB {
constructor(uploadDir, store) {
this.initialized = false;
this.db = {};
this.uploadDir = uploadDir;
this.store = store;
// delete expired files
const gc = () => {
let sid,f,expires;
for (sid of Object.keys(this.db)) {
for (f of this.db[sid]) {
// expire on maxAge
expires = (+f.metadata.createdAt) + (config.maxAge * 1000) - Date.now();
// respect one-time downloads
if(expires > 0 && Number.isInteger(+f.metadata.retention)) {
expires = (+f.metadata.createdAt) + (+f.metadata.retention * 1000) - Date.now();
}
if(expires <= 0) {
debug(`Expired ${sid}++${f.key}`);
this.remove(sid, f.key).catch(e => console.error(e));
}
}
}
};
setInterval(gc, 60*1000);
}
init() {
if(this.initialized) return;
this.initialized = true;
try {
this._sync();
} catch(e) {
this.initialized = false;
e.message = `db initialization failed with error ${e.message}`;
throw e;
}
}
/**
* @private
*/
_sync() {
fsp.ensureDirSync(this.uploadDir);
fsp.readdirSync(this.uploadDir).forEach((sid) => {
this._import(sid);
});
}
/**
* @private
*/
_import(sid) {
const p = path.resolve(this.uploadDir, sid);
const stat = fsp.statSync(p);
if(!stat.isDirectory()) return;
fsp.readdirSync(p).forEach(async (key) => {
if(path.extname(key) !== '') {
return Promise.resolve();
}
try {
let info = await this.store.info(`${sid}++${key}`);
this.add(sid, key, info);
} catch(e) {
console.error(e);
}
});
}
add(sid, key, data) {
if(!this.initialized) throw new Error('DB not initialized_');
if(!this.db[sid]) this.db[sid] = [];
data.key = key;
const old = this.db[sid].findIndex(i => i.key === key);
if(old !== -1) {
this.db[sid].splice(old, 1, data);
debug(`Updated ${sid}++${key}`);
} else {
this.db[sid].push(data);
debug(`Added ${sid}++${key}`);
}
}
async remove(sid, key) {
if(!this.initialized) throw new Error('DB not initialized');
debug(`Remove ${sid}++${key}`);
await this.store.del(sid + '++' + key);
const i = this.db[sid].findIndex(item => item.key === key);
this.get(sid).splice(i, 1);
if(this.get(sid).length === 0) {
delete this.db[sid];
await fsp.rmdir(path.resolve(this.uploadDir, sid));
}
}
async updateLastDownload(sid, key) {
debug(`Update last download ${sid}++${key}`);
const data = this.get(sid).find(item => item.key === key);
if(!data) return;
data.metadata.lastDownload = Date.now();
await this.store.update(`${sid}++${key}`, data);
}
async updateMetadata(sid, key, data) {
debug(`Update metadata ${ sid }++${ key }`);
const file = this.get(sid).find(item => item.key === key);
if (!file) return;
file.metadata = { ...file.metadata, ...data };
await this.store.update(`${ sid }++${ key }`, file);
}
async lock(sid) {
const files = this.get(sid);
if(!files) return;
await Promise.all(files.map(async file => {
await this.updateMetadata(sid, file.key, { buckedLocked: true });
}));
}
isLocked(sid) {
const files = this.get(sid);
if(!files) return false;
return files.some(file => file.metadata.buckedLocked);
}
get(sid) {
return this.db[sid];
}
bucketSize(sid) {
const bucket = this.get(sid);
if(!bucket) return 0;
return bucket.reduce((v, file) => v + +file.metadata.uploadLength, 0);
}
};
+448
View File
@@ -0,0 +1,448 @@
const pug = require('pug');
const express = require('express');
const morgan = require('morgan');
const compression = require('compression');
const uuid = require('uuid').v4;
const path = require('path');
const fs = require("fs");
const assert = require('assert');
const AES = require("crypto-js/aes");
const MD5 = require("crypto-js/md5");
const archiver = require('archiver');
const tar = require('tar-stream');
const config = require('../config');
const eventBus = require('./eventBus');
const tusboy = require('./tusboy');
const Store = require('./store');
const tusMeta = require('./tusboy/tus-metadata');
const debug = require('debug')('psitransfer:main');
const pugVars = {
baseUrl: config.baseUrl
};
const errorPage = pug.compileFile(path.join(__dirname, '../public/pug/error.pug'), { pretty: true });
const adminPage = pug.compileFile(path.join(__dirname, '../public/pug/admin.pug'), { pretty: true });
const uploadPage = pug.compileFile(path.join(__dirname, '../public/pug/upload.pug'), { pretty: true });
const downloadPage = pug.compileFile(path.join(__dirname, '../public/pug/download.pug'), { pretty: true });
const store = new Store(config.uploadDir);
const Db = require('./db');
const { createGzip } = require("zlib");
const httpErrors = require("http-errors");
const db = new Db(config.uploadDir, store);
db.init();
const app = express();
app.disable('x-powered-by');
app.use(compression());
app.use(express.json());
if (config.accessLog) {
app.use(morgan(config.accessLog));
}
if (config.trustProxy) {
app.set('trust proxy', config.trustProxy);
}
if (config.forceHttps) {
app.enable('trust proxy');
app.use(function(req, res, next) {
if (req.secure) return next();
const target = config.forceHttps === 'true' ? 'https://' + req.headers.host : config.forceHttps;
res.redirect(target + req.url);
});
}
// Static files
app.use(`${ config.baseUrl }app`, express.static(path.join(__dirname, '../public/app')));
app.use(`${ config.baseUrl }assets`, express.static(path.join(__dirname, '../public/assets')));
// Resolve language
app.use((req, res, next) => {
const lang = req.acceptsLanguages(...Object.keys(config.languages)) || config.defaultLanguage;
req.translations = config.languages[lang];
next();
});
// robots.txt
app.get(`${ config.baseUrl }robots.txt`, (req, res) => {
res.sendFile(path.join(__dirname, '../public/robots.txt'));
});
// Init plugins
config.plugins.forEach(pluginName => {
require('../plugins/' + pluginName)(eventBus, app, config, db);
});
// Upload App
app.get(config.uploadAppPath, (req, res) => {
res.send(uploadPage({
...pugVars,
baseUrl: config.baseUrl,
uploadAppPath: config.uploadAppPath,
lang: req.translations
}));
});
// Return translations
app.get(`${ config.baseUrl }lang.json`, (req, res) => {
eventBus.emit('getLang', req.translations);
res.json(req.translations);
});
// Config
app.get(`${ config.baseUrl }config.json`, (req, res) => {
// Upload password protection
if (config.uploadPass) {
const bfTimeout = 200;
if (!req.get('x-passwd')) {
setTimeout(() => res.status(401).send('Unauthorized'), bfTimeout);
return;
}
if (req.get('x-passwd') !== config.uploadPass) {
setTimeout(() => res.status(403).send('Forbidden'), bfTimeout);
return;
}
}
const frontendConfig = {
retentions: config.retentions,
defaultRetention: config.defaultRetention,
mailTemplate: config.mailTemplate,
requireBucketPassword: config.requireBucketPassword,
maxFileSize: config.maxFileSize,
maxBucketSize: config.maxBucketSize,
disableQrCode: config.disableQrCode,
};
eventBus.emit('getFrontendConfig', frontendConfig);
res.json(frontendConfig);
});
app.get(`${ config.baseUrl }admin`, (req, res, next) => {
if (!config.adminPass) return next();
res.send(adminPage({ ...pugVars, lang: req.translations }));
});
app.get(`${ config.baseUrl }admin/data.json`, (req, res, next) => {
if (!config.adminPass) return next();
const bfTimeout = 500;
if (!req.get('x-passwd')) {
// delay answer to make brute force attacks more difficult
setTimeout(() => res.status(401).send('Unauthorized'), bfTimeout);
return;
}
if (req.get('x-passwd') !== config.adminPass) {
setTimeout(() => res.status(403).send('Forbidden'), bfTimeout);
return;
}
let result = JSON.parse(JSON.stringify(db.db));
Object.values(result).forEach(bucket => {
bucket.forEach(file => {
if (file.metadata.password) {
file.metadata._password = true;
delete file.metadata.password;
delete file.metadata.key;
delete file.key;
delete file.url;
}
});
});
setTimeout(() => res.json(result), bfTimeout);
});
// List files / Download App
app.get(`${ config.baseUrl }:sid`, (req, res, next) => {
if (req.url.endsWith('.json')) {
const sid = req.params.sid.substr(0, req.params.sid.length - 5);
if (!db.get(sid)) return res.status(404).end();
const downloadPassword = req.get('x-download-pass');
const items = db.get(sid).map(item => ({
...item,
url: `${ config.baseUrl }files/${ sid }++${ item.key }`
}));
res.header('Cache-control', 'private, max-age=0, no-cache, no-store, must-revalidate');
// Currently, every item in a bucket must have the same password
if(items.some(item => item.metadata.password && item.metadata.password !== downloadPassword)) {
setTimeout(() => res.status(401).send('Unauthorized'), 500);
return;
}
res.json({
items,
config: {
maxPreviewSize: config.maxPreviewSize
}
});
} else {
if (!db.get(req.params.sid)) return next();
res.send(downloadPage({ ...pugVars, lang: req.translations }));
}
});
// Download files
app.get(`${ config.baseUrl }files/:fid`, async (req, res, next) => {
// let tusboy handle HEAD requests with Tus Header
if (req.method === 'HEAD' && req.get('Tus-Resumable')) return next();
const sid = req.params.fid.split('++')[0];
// Download all files
if (req.params.fid.match(/^[a-z0-9+]+\.(tar\.gz|zip)$/)) {
const format = req.params.fid.endsWith('.zip') ? 'zip' : 'tar.gz';
const bucket = db.get(sid);
if (!bucket) return res.status(404).send(errorPage({
...pugVars,
error: 'Download bucket not found.',
lang: req.translations,
uploadAppPath: config.uploadAppPath || config.baseUrl,
}));
if (req.params.fid !== sid + '++' + MD5(bucket.map(f => f.key).join()).toString() + '.' + format) {
res.status(404).send(errorPage({
...pugVars,
error: 'Invalid link',
uploadAppPath: config.uploadAppPath || config.baseUrl,
lang: req.translations,
}));
return;
}
debug(`Download Bucket ${ sid }`);
const filename = `${ sid }.${ format }`;
res.header('Content-Disposition', `attachment; filename="${ filename }"`);
try {
res.on('finish', async () => {
bucket.forEach(async info => {
if (info.metadata.retention === 'one-time') {
await db.remove(info.metadata.sid, info.metadata.key);
} else {
await db.updateLastDownload(info.metadata.sid, info.metadata.key);
}
});
eventBus.emit('archiveDownloaded', {
sid,
file: filename,
metadata: bucket[0].metadata,
bucket,
url: req.protocol + '://' + req.get('host') + req.originalUrl,
});
});
}
catch (e) {
console.error(e);
}
if(format === 'zip') {
res.header('ContentType', 'application/zip');
const archive = archiver('zip');
archive.on('error', function(err) {
console.error(err);
});
archive.pipe(res);
for (const info of bucket) {
await new Promise((resolve, reject) => {
const stream = fs.createReadStream(store.getFilename(info.metadata.sid + '++' + info.key));
stream.on('end', resolve);
archive.append(stream, { name: info.metadata.name });
});
}
await archive.finalize();
} else {
res.header('ContentType', 'application/x-gtar');
const pack = tar.pack();
pack.pipe(createGzip()).pipe(res);
for (const info of bucket) {
await new Promise((resolve, reject) => {
const readStream = fs.createReadStream(store.getFilename(info.metadata.sid + '++' + info.key));
const entry = pack.entry({ name: info.metadata.name, size: info.size });
readStream.on('error', reject);
entry.on('error', reject);
entry.on('finish',resolve);
readStream.pipe(entry);
});
}
pack.finalize();
}
return;
}
// Download single file
debug(`Download ${ req.params.fid }`);
try {
const info = await store.info(req.params.fid); // throws on 404
res.download(store.getFilename(req.params.fid), info.metadata.name);
// remove one-time files after download
res.on('finish', async () => {
if (info.metadata.retention === 'one-time') {
await db.remove(info.metadata.sid, info.metadata.key);
} else {
await db.updateLastDownload(info.metadata.sid, info.metadata.key);
}
eventBus.emit('fileDownloaded', {
sid,
file: info.metadata.name,
metadata: info.metadata,
url: req.protocol + '://' + req.get('host') + req.originalUrl,
});
});
}
catch (e) {
res.status(404).send(errorPage({
...pugVars,
error: e.message,
lang: req.translations,
uploadAppPath: config.uploadAppPath || config.baseUrl,
}));
}
});
// Upload file
app.use(`${ config.uploadAppPath }files`,
async function(req, res, next) {
// Upload password protection
if (config.uploadPass) {
const bfTimeout = 500;
if (!req.get('x-passwd')) {
setTimeout(() => res.status(401).send('Unauthorized'), bfTimeout);
return;
}
if (req.get('x-passwd') !== config.uploadPass) {
setTimeout(() => res.status(403).send('Forbidden'), bfTimeout);
return;
}
}
if (req.method === 'GET') return res.status(405).end();
// Lock bucket by PATCH /files/:sid?lock=yes
const fid = req.path.substring(1);
if(!fid.includes('++') && req.method === 'PATCH' && req.query.lock) {
await db.lock(fid);
return res.status(204).end('Bucket locked');
}
if(['POST', 'PATCH'].includes(req.method)) {
// Restrict upload to the bucket if it is locked
if(!fid.includes('++') && db.isLocked(fid)) {
return res.status(400).end('Bucket locked');
}
try {
const info = await store.info(fid);
// Restrict upload to the bucket if it is locked
if(info.metadata.locked) {
return res.status(400).end('Bucket locked');
}
// Restrict upload to a file which upload completed already
if(!info.isPartial) {
return res.status(400).end('Upload already completed');
}
} catch(e) {
if(! e instanceof httpErrors.NotFound) {
console.error(e);
return;
}
}
}
if (req.method === 'POST') {
// validate meta-data
// !! tusMeta.encode supports only strings !!
const meta = tusMeta.decode(req.get('Upload-Metadata'));
try {
assert(meta.name, 'tus meta prop missing: name');
assert(meta.sid, 'tus meta prop missing: sid');
assert(meta.retention, 'tus meta prop missing: retention');
assert(Object.keys(config.retentions).indexOf(meta.retention) >= 0,
`invalid tus meta prop retention. Value ${ meta.retention } not in [${ Object.keys(config.retentions).join(',') }]`);
const uploadLength = req.get('Upload-Length');
assert(uploadLength, 'missing Upload-Length header');
// Restrict creating new files for locked buckets
if(db.isLocked(meta.sid)) {
return res.status(400).end('Bucket locked');
}
meta.uploadLength = uploadLength;
meta.key = uuid();
meta.createdAt = Date.now().toString();
// limit file and bucket size
if (config.maxFileSize && config.maxFileSize < +uploadLength) {
return res
.status(413)
.json({ message: `File exceeds maximum upload size ${ config.maxFileSize }.` });
} else if (config.maxBucketSize && db.bucketSize(meta.sid) + +uploadLength > config.maxBucketSize) {
return res
.status(413)
.json({ message: `Bucket exceeds maximum upload size ${ config.maxBucketSize }.` });
}
// store changed metadata for tusboy
req.headers['upload-metadata'] = tusMeta.encode(meta);
// for tusboy getKey()
req.FID = meta.sid + '++' + meta.key;
db.add(meta.sid, meta.key, {
"isPartial": true,
metadata: meta
});
}
catch (e) {
console.error(e);
return res.status(400).end(e.message);
}
}
next();
},
// let tusboy handle the upload
tusboy(store, {
getKey: req => req.FID,
maxUploadLength: config.maxFileSize || Infinity,
afterComplete: (req, upload, fid) => {
db.add(upload.metadata.sid, upload.metadata.key, upload);
debug(`Completed upload ${ fid }, size=${ upload.size } name=${ upload.metadata.name }`);
eventBus.emit('fileUploaded', upload);
},
})
);
app.use((req, res, next) => {
if (req.url === config.baseUrl) {
return res.redirect(config.uploadAppPath);
}
res.status(404).send(errorPage({
...pugVars,
error: 'Download bucket not found.',
uploadAppPath: config.uploadAppPath || config.baseUrl,
lang: req.translations
}));
});
module.exports = app;
+8
View File
@@ -0,0 +1,8 @@
const EventEmitter = require('events');
const emitter = new EventEmitter();
emitter.on('uncaughtException', function(err) {
console.error(err);
});
module.exports = emitter;
+123
View File
@@ -0,0 +1,123 @@
'use strict';
const fsp = require('fs-promise');
const path = require('path');
const Transform = require('stream').Transform;
const debug = require('debug')('psitransfer:store');
const httpErrors = require('http-errors');
class StreamLen extends Transform {
constructor(options) {
super(options);
this.bytes = 0;
}
_transform(chunk, encoding, cb) {
this.bytes += chunk.length;
this.push(chunk);
cb();
}
}
// TODO ???: make tus-store complaint: https://github.com/blockai/abstract-tus-store
class Store {
constructor(targetDir) {
this.dir = path.normalize(targetDir);
}
getFilename(fid) {
let p = path.resolve(this.dir, fid.replace('++', '/'));
if(!p.startsWith(this.dir)) {
throw new Error('file name not in jail path. aborting');
}
return p;
}
async create(fid, opts = {}) {
debug(`New File ${this.getFilename(fid)}`);
await fsp.ensureDir(path.dirname(this.getFilename(fid)));
await fsp.writeJson(this.getFilename(fid) + '.json', Object.assign(opts, {
isPartial: true
}));
return {uploadId: fid};
}
async update(fid, data) {
debug(`Update File ${this.getFilename(fid)}`);
await fsp.writeJson(this.getFilename(fid) + '.json', data);
return data;
}
async info(fid) {
try {
const info = await fsp.readJson(this.getFilename(fid) + '.json');
const stat = await fsp.stat(this.getFilename(fid));
info.size = stat.size;
info.offset = stat.size;
debug(`Fetched Fileinfo ${this.getFilename(fid)}`);
return info;
} catch(e) {
if(e.code === 'ENOENT') {
throw httpErrors.NotFound();
}
throw e;
}
}
async append(fid, readStream, offset) {
debug(`Append Data to ${this.getFilename(fid)}`);
const uploadSize = new StreamLen();
const ws = fsp.createWriteStream(this.getFilename(fid), {flags: 'a', start: offset});
const ret = new Promise((resolve, reject) => {
ws.on('finish', async() => {
const info = await this.info(fid);
if(info.size >= info.uploadLength) delete info.isPartial;
await fsp.writeJson(this.getFilename(fid) + '.json', info);
debug(`Finished appending Data to ${this.getFilename(fid)}`);
return resolve({ offset: info.offset, upload: info });
});
ws.on('error', reject);
});
readStream.pipe(uploadSize).pipe(ws);
return ret;
}
createReadStream(fid, start, end, cb) {
debug(`Create ReadStream for ${this.getFilename(fid)}`);
this.info(fid).then(info => {
let contentLength = info.size;
if(start > 0) {
if(!end) end = info.size - 1;
contentLength = end - start + 1
}
if(cb) cb({ contentLength, metadata: info.metadata, info });
});
return fsp.createReadStream(this.getFilename(fid), {start, end});
}
async del(fid) {
debug(`Delete ${this.getFilename(fid)}`);
try {
await fsp.unlink(this.getFilename(fid) + '.json');
} catch (e) {
if(e.code !== 'ENOENT') throw e;
}
try {
await fsp.unlink(this.getFilename(fid));
}
catch (e) {
if (e.code !== 'ENOENT') throw e;
}
}
}
module.exports = Store;
@@ -0,0 +1,35 @@
const HEADERS = [
'Content-Type',
'Location',
'Tus-Extension',
'Tus-Max-Size',
'Tus-Resumable',
'Tus-Version',
'Upload-Defer-Length',
'Upload-Length',
'Upload-Metadata',
'Upload-Offset',
'X-HTTP-Method-Override',
'X-Requested-With',
];
const MAX_AGE = 86400;
const ALLOWED_HEADERS = HEADERS;
const EXPOSED_HEADERS = HEADERS;
const ALLOWED_METHODS = [
'POST',
'HEAD',
'PATCH',
'OPTIONS',
];
const TUS_VERSION = '1.0.0';
module.exports = {
MAX_AGE,
ALLOWED_HEADERS,
EXPOSED_HEADERS,
ALLOWED_METHODS,
TUS_VERSION,
}
+51
View File
@@ -0,0 +1,51 @@
const createError = require('http-errors');
const tusResumableHeaderMissing = () => createError(412, 'Tus-Resumable Required')
const invalidHeaders = (headers) => (
createError(412, 'Precondition Failed', {
details: { headers },
})
)
const invalidHeader = (header, val) => (
createError(412, 'Precondition Failed', {
details: { headers: [[header, val]] },
})
)
const missingHeader = (header) => invalidHeader(header)
const entityTooLarge = (msg, props) => createError(413, msg, props)
const preconditionError = (msg, props) => createError(412, msg, props)
const offsetMismatch = (actual, expected) => (
createError(409, `Offset mismatch, got ${actual} but expected ${expected}`)
)
// For store implementations:
const unknownResource = (key) => (
createError(404, `Unknown resource: ${key}`)
)
const concurrentWrite = () => (
createError(409, 'Concurrent write detected')
)
const uploadLengthAlreadySet = () => (
createError(409, 'Upload length is already set')
)
module.exports = {
tusResumableHeaderMissing,
invalidHeaders,
invalidHeader,
missingHeader,
entityTooLarge,
preconditionError,
offsetMismatch,
unknownResource,
concurrentWrite,
uploadLengthAlreadySet,
}
@@ -0,0 +1,16 @@
module.exports = (err, req, res, next) => {
if (res.headersSent) return next(err)
if (!err.status) {
// console.error(err)
return next(err)
}
if (!err.expose) return next(err)
if (!(err instanceof Error)) return next(err)
res.statusCode = err.status
res.json({
name: err.name,
message: err.message,
details: err.details,
})
}
@@ -0,0 +1,46 @@
// HEAD
//
// The Server MUST always include the Upload-Offset header in the
// response for a HEAD request, even if the offset is 0, or the upload
// is already considered completed. If the size of the upload is known,
// the Server MUST include the Upload-Length header in the response. If
// the resource is not found, the Server SHOULD return either the 404
// Not Found, 410 Gone or 403 Forbidden status without the Upload-Offset
// header.
//
// The Server MUST prevent the client and/or proxies from caching the
// response by adding the Cache-Control: no-store header to the
// response.
//
const encodeMetadata = require('../tus-metadata').encode;
module.exports = (store) => (
async (req, res) => {
res.set('Cache-Control', 'no-store')
const { uploadId } = req.params
const upload = await store.info(uploadId)
// The Server MUST always include the Upload-Offset header in the
// response for a HEAD request, even if the offset is 0, or the upload
// is already considered completed.
res.set('Upload-Offset', upload.offset)
if (!('uploadLength' in upload)) {
// As long as the length of the upload is not known, the Server
// MUST set Upload-Defer-Length: 1 in all responses to HEAD requests.
res.set('Upload-Defer-Length', '1')
} else {
// If the size of the upload is known, the Server MUST include
// the Upload-Length header in the response
res.set('Upload-Length', upload.uploadLength)
}
// If an upload contains additional metadata, responses to HEAD
// requests MUST include the Upload-Metadata header and its value as
// specified by the Client during the creation.
const encodedMetadata = encodeMetadata(upload.metadata)
if (encodedMetadata !== '') {
res.set('Upload-Metadata', encodedMetadata)
}
res.end()
}
)
@@ -0,0 +1,23 @@
const cors = require('cors')
const { ALLOWED_HEADERS, ALLOWED_METHODS, MAX_AGE } = require ('../constants');
const tusExtension = (extensions = []) => {
if (!extensions.length) {
return (req, res, next) => next()
}
return (req, res, next) => {
res.set('Tus-Extension', extensions.join(','))
next()
}
}
const corsPreflight = (extraMethods) => cors({
methods: [...ALLOWED_METHODS, ...extraMethods],
allowedHeaders: ALLOWED_HEADERS,
maxAge: MAX_AGE,
})
module.exports = (extensions, extraMethods = []) => ([
tusExtension(extensions),
corsPreflight(extraMethods),
])
@@ -0,0 +1,82 @@
// PATCH
//
// The Server SHOULD accept PATCH requests against any upload URL and
// apply the bytes contained in the message at the given offset
// specified by the Upload-Offset header. All PATCH requests MUST use
// Content-Type: application/offset+octet-stream.
//
// The Upload-Offset header’s value MUST be equal to the current offset
// of the resource. In order to achieve parallel upload the
// Concatenation extension MAY be used. If the offsets do not match, the
// Server MUST respond with the 409 Conflict status without modifying
// the upload resource.
//
// The Client SHOULD send all the remaining bytes of an upload in a
// single PATCH request, but MAY also use multiple small requests
// successively for scenarios where this is desirable, for example, if
// the Checksum extension is used.
//
// The Server MUST acknowledge successful PATCH requests with the 204 No
// Content status. It MUST include the Upload-Offset header containing
// the new offset. The new offset MUST be the sum of the offset before
// the PATCH request and the number of bytes received and processed or
// stored during the current PATCH request.
//
// Both, Client and Server, SHOULD attempt to detect and handle network
// errors predictably. They MAY do so by checking for read/write socket
// errors, as well as setting read/write timeouts. A timeout SHOULD be
// handled by closing the underlying connection.
//
// The Server SHOULD always attempt to store as much of the received
// data as possible.
const storeErrors = require('../store/errors');
const errors = require('../errors');
module.exports = (store, {
onComplete,
beforeComplete = async () => {},
afterComplete = async () => {},
}) => (async (req, res) => {
const after = onComplete || afterComplete
// The request MUST include a Upload-Offset header
if (!('uploadOffset' in req.tus)) {
throw errors.missingHeader('upload-offset')
}
// The request MUST include a Content-Type header
if (typeof req.get('content-type') === 'undefined') {
throw errors.missingHeader('content-type')
}
// All PATCH requests MUST use Content-Type: application/offset+octet-stream
if (req.get('content-type') !== 'application/offset+octet-stream') {
throw errors.invalidHeader('content-type', req.get('content-type'))
}
const uploadId = req.params.uploadId
try {
const {
offset,
upload,
} = await store.append(uploadId, req, req.tus.uploadOffset, {
beforeComplete: (...args) => beforeComplete(req, ...args),
uploadLength: req.tus.uploadLength,
})
if (upload && upload.uploadLength === offset) {
await after(req, upload, uploadId)
}
// It MUST include the Upload-Offset header containing the new offset.
res.set('Upload-Offset', offset)
// The Server MUST acknowledge successful PATCH requests
// with the 204 No Content status.
res.status(204)
res.end()
} catch (err) {
if (err instanceof storeErrors.OffsetMismatch) {
throw errors.offsetMismatch()
}
throw err
}
})
@@ -0,0 +1,105 @@
// Creation
//
// The Client and the Server SHOULD implement the upload creation
// extension. If the Server supports this extension, it MUST add creation
// to the Tus-Extension header.
//
// Headers
//
// Upload-Defer-Length
//
// The Upload-Defer-Length request and response header indicates that the
// size of the upload is not known currently and will be transferred later.
// Its value MUST be 1. If the length of an upload is not deferred, this
// header MUST be omitted.
//
// Upload-Metadata
//
// The Upload-Metadata request and response header MUST consist of one or
// more comma-separated key-value pairs. The key and value MUST be
// separated by a space. The key MUST NOT contain spaces and commas and
// MUST NOT be empty. The key SHOULD be ASCII encoded and the value MUST be
// Base64 encoded. All keys MUST be unique.
//
// Requests
//
// POST
//
// The Client MUST send a POST request against a known upload creation URL
// to request a new upload resource. The request MUST include one of the
// following headers:
//
// a) Upload-Length to indicate the size of an entire upload in bytes.
//
// b) Upload-Defer-Length: 1 if upload size is not known at the time. Once
// it is known the Client MUST set the Upload-Length header in the next
// PATCH request. Once set the length MUST NOT be changed. As long as the
// length of the upload is not known, the Server MUST set
// Upload-Defer-Length: 1 in all responses to HEAD requests.
//
// If the Server supports deferring length, it MUST add
// creation-defer-length to the Tus-Extension header.
//
// The Client MAY supply the Upload-Metadata header to add additional
// metadata to the upload creation request. The Server MAY decide to ignore
// or use this information to further process the request or to reject it.
// If an upload contains additional metadata, responses to HEAD requests
// MUST include the Upload-Metadata header and its value as specified by
// the Client during the creation.
//
// If the length of the upload exceeds the maximum, which MAY be specified
// using the Tus-Max-Size header, the Server MUST respond with the 413
// Request Entity Too Large status.
//
// The Server MUST acknowledge a successful upload creation with the 201
// Created status. The Server MUST set the Location header to the URL of
// the created resource. This URL MAY be absolute or relative.
//
// The Client MUST perform the actual upload using the core protocol.
const errors = require('../errors');
module.exports = (store, {
getKey = () => {},
maxUploadLength = Infinity,
}) => async (req, res, next) => {
const tus = req.tus
const { uploadLength } = tus
const defer = !!tus.uploadDeferLength
if (!defer && !('uploadLength' in tus)) {
return next(errors.preconditionError(
'Missing Upload-Length header'
))
}
// TODO: make sure store supports defer length
if (defer && !store.supportsDeferredLength) {
return next(errors.preconditionError(
'Store does not support creation-defer-length extension'
))
}
if (defer && ('uploadLength' in tus)) {
return next(errors.preconditionError(
'Choose one of Upload-Length OR Upload-Defer-Length'
))
}
if ('uploadLength' in tus && uploadLength > maxUploadLength) {
res.set('Tus-Max-Size', maxUploadLength)
return next(errors.entityTooLarge(
`Upload-length (${uploadLength}) exceeds max upload size (${maxUploadLength})`
), { maxUploadLength, uploadLength })
}
const key = await getKey(req)
const metadata = req.tus.uploadMetadata
const { uploadId } = await store.create(key, {
uploadLength,
metadata,
})
res.status(201)
let basePath = req.baseUrl
if (basePath[basePath.length - 1] !== '/') basePath += '/'
res.set('Location', `${basePath}${uploadId}`)
res.end()
}
+90
View File
@@ -0,0 +1,90 @@
const { Router } = require('express');
const tusHeaderParser = require('./tus-header-parser');
const methodOverride = require('method-override');
const cors = require('cors');
const constants = require('./constants');
const errors = require('./errors');
const errorHandler = require ('./handlers/error');
const options = require ('./handlers/options');
const head = require ('./handlers/head');
const patch = require ('./handlers/patch');
const post = require ('./handlers/post');
/**
* Returns a route handler for Express that calls the passed in function
* @param {Function} fn The asynchronous the route needs to call
* @return {Promise}
*/
function w(fn) {
if (fn.length <= 3) {
return function(req, res, next) {
return fn(req, res, next).catch(next);
};
} else {
return function(err, req, res, next) {
return fn(err, req, res, next).catch(next);
};
}
}
const detectExtensions = (store) => {
return [
'create',
() => { if (store.del) return 'delete' },
].filter(ele => typeof ele === 'string')
}
const versionSupported = (/* versionStr */) => true
const setTusResumableHeader = (req, res, next) => {
res.set('Tus-Resumable', constants.TUS_VERSION)
next()
}
// The Tus-Resumable header MUST be included in every request and
// response except for OPTIONS requests. The value MUST be the version
// of the protocol used by the Client or the Server.
const assertTusResumableHeader = (req, res, next) => {
if (!('tusResumable' in req.tus)) {
res.set('Tus-Version', constants.TUS_VERSION)
return next(errors.preconditionError('Tus-Resumable header missing'))
} else if (!versionSupported(req.tus.tusResumable)) {
res.set('Tus-Version', constants.TUS_VERSION)
return next(errors.preconditionError('Tus-Resumable version not supported'))
}
next()
}
const setCorsHeaders = cors({
origin: true,
exposedHeaders: constants.EXPOSED_HEADERS,
})
module.exports = (store, opts = {}) => {
const { handleErrors = true } = opts
const extensions = detectExtensions(store)
const nextRouter = new Router({ mergeParams: true })
.use(assertTusResumableHeader)
.post('/', w(post(store, opts)))
.head('/:uploadId', w(head(store, opts)))
.patch('/:uploadId', w(patch(store, opts)))
if (handleErrors) nextRouter.use(errorHandler)
const router = new Router({ mergeParams: true })
router
.use(methodOverride('X-HTTP-Method-Override'))
.use(tusHeaderParser())
.options('*', options(extensions, opts.extraCorsMethods))
.use(setCorsHeaders)
.use(setTusResumableHeader)
.use((req, res, next) => {
if (req.method === 'GET') return next()
return nextRouter(req, res, next)
})
return router
}
@@ -0,0 +1,20 @@
class StoreError extends Error {
}
class UploadNotFound extends StoreError {
}
class OffsetMismatch extends StoreError {
}
class KeyNotFound extends StoreError {
}
class UploadLocked extends StoreError {
}
module.exports = StoreError;
module.exports.UploadNotFound = UploadNotFound;
module.exports.OffsetMismatch = OffsetMismatch;
module.exports.KeyNotFound = KeyNotFound;
module.exports.UploadLocked = UploadLocked;
@@ -0,0 +1,111 @@
const toObject = require('to-object-reducer');
const decodeMetadata = require('./tus-metadata').decode;
// TODO: Buffer.from silently ignores invalid bytes :(
// https://github.com/nodejs/node/issues/8569
const base64decode = str => Buffer.from(str, 'base64')
const nonNegativeInteger = (v) => {
// need this because parseInt parses floats too
if (v !== '0' && !v.match(/^[1-9][0-9]*$/)) return
const num = parseInt(v, 10)
if (isNaN(num)) return
if (num >= 0) return num
}
/*
const enum = (possibleVals = []) => (v) => {
if (!possibleVals.includes(v)) return
return v
}
*/
const id = v => v
const parsers = {
'tus-resumable': id,
// The Upload-Offset request and response header indicates a byte offset
// within a resource. The value MUST be a non-negative integer.
'upload-offset': nonNegativeInteger,
// The Upload-Defer-Length request and response header indicates that the
// size of the upload is not known currently and will be transferred later.
// Its value MUST be 1. If the length of an upload is not deferred, this
// header MUST be omitted.
'upload-defer-length': v => {
const num = nonNegativeInteger(v)
if (num === 1) return 1
},
// The Upload-Length request and response header indicates the size of the
// entire upload in bytes. The value MUST be a non-negative integer.
'upload-length': nonNegativeInteger,
'upload-metadata': decodeMetadata,
// The Upload-Checksum request header contains information about the checksum
// of the current body payload. The header MUST consist of the name of the
// used checksum algorithm and the Base64 encoded checksum separated by a
// space.
'upload-checksum': v => {
const parts = v.split(' ')
if (parts.length !== 2) return
const algorithm = parts[0]
const checksum = base64decode(parts[1])
if (!checksum.length) return
return { algorithm, checksum }
},
// The Upload-Concat request and response header MUST be set in both partial
// and final upload creation requests. It indicates whether the upload is
// either a partial or final upload. If the upload is a partial one, the
// header value MUST be partial. In the case of a final upload, its value
// MUST be final followed by a semicolon and a space-separated list of
// partial upload URLs that will be concatenated. The partial uploads URLs
// MAY be absolute or relative and MUST NOT contain spaces as defined in RFC
// 3986.
'upload-concat': v => {
if (v === 'partial') return { partial: true }
const parts = v.split(';')
if (!parts.length) return
const firstPart = parts.shift()
if (firstPart !== 'final') return
const urls = parts.join(';').split(' ')
if (urls.some(url => url === '')) return
// validate urls? nah...
return { final: true, urls }
},
}
const knownHeaders = Object.keys(parsers)
const parseHeader = (tmpHeaderName, str) => {
const headerName = tmpHeaderName.toLowerCase()
if (!knownHeaders.includes(headerName)) return
return parsers[headerName](str)
}
// Pre-computed for speed
const camelCase = {
'tus-resumable': 'tusResumable',
'upload-offset': 'uploadOffset',
'upload-defer-length': 'uploadDeferLength',
'upload-length': 'uploadLength',
'upload-metadata': 'uploadMetadata',
'upload-checksum': 'uploadChecksum',
'upload-concat': 'uploadConcat',
}
const parseHeaders = (headers) => (
Object
.keys(headers)
.filter(headerName => knownHeaders.includes(headerName))
.map(headerName => ([
camelCase[headerName],
parsers[headerName](headers[headerName]),
]))
.reduce(toObject, {})
)
module.exports = () => (req, res, next) => {
req.tus = parseHeaders(req.headers)
next()
};
module.exports.knownHeaders = knownHeaders;
module.exports.parseHeader = parseHeader;
module.exports.parseHeaders = parseHeaders;
@@ -0,0 +1,24 @@
const toObject = require('to-object-reducer');
// return object
const base64decode = str => Buffer.from(str, 'base64').toString('utf8')
const base64encode = str => Buffer.from(str, 'utf8').toString('base64')
module.exports.decode = (str = '') => {
const keypairs = str
.split(',')
// .map(s => s.trim())
const keyvals = keypairs
.map(s => s.split(' '))
.filter(arr => arr.length === 2)
.filter(([key]) => key !== '')
.map(([key, val]) => [key, base64decode(val)])
return keyvals.reduce(toObject, {})
}
module.exports.encode = (obj = {}) => Object
.keys(obj)
.map(key => [key, obj[key]])
.map(([key, val]) => [key, base64encode(val)])
.map(kvArr => kvArr.join(' '))
.join(',')
+1
View File
@@ -0,0 +1 @@
../acorn/bin/acorn
+1
View File
@@ -0,0 +1 @@
../crc-32/bin/crc32.njs
+1
View File
@@ -0,0 +1 @@
../mime/cli.js
+1
View File
@@ -0,0 +1 @@
../@babel/parser/bin/babel-parser.js
+1
View File
@@ -0,0 +1 @@
../uuid/dist/bin/uuid
File diff suppressed because it is too large Load Diff
+22
View File
@@ -0,0 +1,22 @@
MIT License
Copyright (c) 2014-present Sebastian McKenzie and other contributors
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+19
View File
@@ -0,0 +1,19 @@
# @babel/helper-string-parser
> A utility package to parse strings
See our website [@babel/helper-string-parser](https://babeljs.io/docs/babel-helper-string-parser) for more information.
## Install
Using npm:
```sh
npm install --save @babel/helper-string-parser
```
or using yarn:
```sh
yarn add @babel/helper-string-parser
```
@@ -0,0 +1,295 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.readCodePoint = readCodePoint;
exports.readInt = readInt;
exports.readStringContents = readStringContents;
var _isDigit = function isDigit(code) {
return code >= 48 && code <= 57;
};
const forbiddenNumericSeparatorSiblings = {
decBinOct: new Set([46, 66, 69, 79, 95, 98, 101, 111]),
hex: new Set([46, 88, 95, 120])
};
const isAllowedNumericSeparatorSibling = {
bin: ch => ch === 48 || ch === 49,
oct: ch => ch >= 48 && ch <= 55,
dec: ch => ch >= 48 && ch <= 57,
hex: ch => ch >= 48 && ch <= 57 || ch >= 65 && ch <= 70 || ch >= 97 && ch <= 102
};
function readStringContents(type, input, pos, lineStart, curLine, errors) {
const initialPos = pos;
const initialLineStart = lineStart;
const initialCurLine = curLine;
let out = "";
let firstInvalidLoc = null;
let chunkStart = pos;
const {
length
} = input;
for (;;) {
if (pos >= length) {
errors.unterminated(initialPos, initialLineStart, initialCurLine);
out += input.slice(chunkStart, pos);
break;
}
const ch = input.charCodeAt(pos);
if (isStringEnd(type, ch, input, pos)) {
out += input.slice(chunkStart, pos);
break;
}
if (ch === 92) {
out += input.slice(chunkStart, pos);
const res = readEscapedChar(input, pos, lineStart, curLine, type === "template", errors);
if (res.ch === null && !firstInvalidLoc) {
firstInvalidLoc = {
pos,
lineStart,
curLine
};
} else {
out += res.ch;
}
({
pos,
lineStart,
curLine
} = res);
chunkStart = pos;
} else if (ch === 8232 || ch === 8233) {
++pos;
++curLine;
lineStart = pos;
} else if (ch === 10 || ch === 13) {
if (type === "template") {
out += input.slice(chunkStart, pos) + "\n";
++pos;
if (ch === 13 && input.charCodeAt(pos) === 10) {
++pos;
}
++curLine;
chunkStart = lineStart = pos;
} else {
errors.unterminated(initialPos, initialLineStart, initialCurLine);
}
} else {
++pos;
}
}
return {
pos,
str: out,
firstInvalidLoc,
lineStart,
curLine,
containsInvalid: !!firstInvalidLoc
};
}
function isStringEnd(type, ch, input, pos) {
if (type === "template") {
return ch === 96 || ch === 36 && input.charCodeAt(pos + 1) === 123;
}
return ch === (type === "double" ? 34 : 39);
}
function readEscapedChar(input, pos, lineStart, curLine, inTemplate, errors) {
const throwOnInvalid = !inTemplate;
pos++;
const res = ch => ({
pos,
ch,
lineStart,
curLine
});
const ch = input.charCodeAt(pos++);
switch (ch) {
case 110:
return res("\n");
case 114:
return res("\r");
case 120:
{
let code;
({
code,
pos
} = readHexChar(input, pos, lineStart, curLine, 2, false, throwOnInvalid, errors));
return res(code === null ? null : String.fromCharCode(code));
}
case 117:
{
let code;
({
code,
pos
} = readCodePoint(input, pos, lineStart, curLine, throwOnInvalid, errors));
return res(code === null ? null : String.fromCodePoint(code));
}
case 116:
return res("\t");
case 98:
return res("\b");
case 118:
return res("\u000b");
case 102:
return res("\f");
case 13:
if (input.charCodeAt(pos) === 10) {
++pos;
}
case 10:
lineStart = pos;
++curLine;
case 8232:
case 8233:
return res("");
case 56:
case 57:
if (inTemplate) {
return res(null);
} else {
errors.strictNumericEscape(pos - 1, lineStart, curLine);
}
default:
if (ch >= 48 && ch <= 55) {
const startPos = pos - 1;
const match = input.slice(startPos, pos + 2).match(/^[0-7]+/);
let octalStr = match[0];
let octal = parseInt(octalStr, 8);
if (octal > 255) {
octalStr = octalStr.slice(0, -1);
octal = parseInt(octalStr, 8);
}
pos += octalStr.length - 1;
const next = input.charCodeAt(pos);
if (octalStr !== "0" || next === 56 || next === 57) {
if (inTemplate) {
return res(null);
} else {
errors.strictNumericEscape(startPos, lineStart, curLine);
}
}
return res(String.fromCharCode(octal));
}
return res(String.fromCharCode(ch));
}
}
function readHexChar(input, pos, lineStart, curLine, len, forceLen, throwOnInvalid, errors) {
const initialPos = pos;
let n;
({
n,
pos
} = readInt(input, pos, lineStart, curLine, 16, len, forceLen, false, errors, !throwOnInvalid));
if (n === null) {
if (throwOnInvalid) {
errors.invalidEscapeSequence(initialPos, lineStart, curLine);
} else {
pos = initialPos - 1;
}
}
return {
code: n,
pos
};
}
function readInt(input, pos, lineStart, curLine, radix, len, forceLen, allowNumSeparator, errors, bailOnError) {
const start = pos;
const forbiddenSiblings = radix === 16 ? forbiddenNumericSeparatorSiblings.hex : forbiddenNumericSeparatorSiblings.decBinOct;
const isAllowedSibling = radix === 16 ? isAllowedNumericSeparatorSibling.hex : radix === 10 ? isAllowedNumericSeparatorSibling.dec : radix === 8 ? isAllowedNumericSeparatorSibling.oct : isAllowedNumericSeparatorSibling.bin;
let invalid = false;
let total = 0;
for (let i = 0, e = len == null ? Infinity : len; i < e; ++i) {
const code = input.charCodeAt(pos);
let val;
if (code === 95 && allowNumSeparator !== "bail") {
const prev = input.charCodeAt(pos - 1);
const next = input.charCodeAt(pos + 1);
if (!allowNumSeparator) {
if (bailOnError) return {
n: null,
pos
};
errors.numericSeparatorInEscapeSequence(pos, lineStart, curLine);
} else if (Number.isNaN(next) || !isAllowedSibling(next) || forbiddenSiblings.has(prev) || forbiddenSiblings.has(next)) {
if (bailOnError) return {
n: null,
pos
};
errors.unexpectedNumericSeparator(pos, lineStart, curLine);
}
++pos;
continue;
}
if (code >= 97) {
val = code - 97 + 10;
} else if (code >= 65) {
val = code - 65 + 10;
} else if (_isDigit(code)) {
val = code - 48;
} else {
val = Infinity;
}
if (val >= radix) {
if (val <= 9 && bailOnError) {
return {
n: null,
pos
};
} else if (val <= 9 && errors.invalidDigit(pos, lineStart, curLine, radix)) {
val = 0;
} else if (forceLen) {
val = 0;
invalid = true;
} else {
break;
}
}
++pos;
total = total * radix + val;
}
if (pos === start || len != null && pos - start !== len || invalid) {
return {
n: null,
pos
};
}
return {
n: total,
pos
};
}
function readCodePoint(input, pos, lineStart, curLine, throwOnInvalid, errors) {
const ch = input.charCodeAt(pos);
let code;
if (ch === 123) {
++pos;
({
code,
pos
} = readHexChar(input, pos, lineStart, curLine, input.indexOf("}", pos) - pos, true, throwOnInvalid, errors));
++pos;
if (code !== null && code > 0x10ffff) {
if (throwOnInvalid) {
errors.invalidCodePoint(pos, lineStart, curLine);
} else {
return {
code: null,
pos
};
}
}
} else {
({
code,
pos
} = readHexChar(input, pos, lineStart, curLine, 4, false, throwOnInvalid, errors));
}
return {
code,
pos
};
}
//# sourceMappingURL=index.js.map
File diff suppressed because one or more lines are too long
@@ -0,0 +1,28 @@
{
"name": "@babel/helper-string-parser",
"version": "7.23.4",
"description": "A utility package to parse strings",
"repository": {
"type": "git",
"url": "https://github.com/babel/babel.git",
"directory": "packages/babel-helper-string-parser"
},
"homepage": "https://babel.dev/docs/en/next/babel-helper-string-parser",
"license": "MIT",
"publishConfig": {
"access": "public"
},
"main": "./lib/index.js",
"devDependencies": {
"charcodes": "^0.2.0"
},
"engines": {
"node": ">=6.9.0"
},
"author": "The Babel Team (https://babel.dev/team)",
"exports": {
".": "./lib/index.js",
"./package.json": "./package.json"
},
"type": "commonjs"
}
@@ -0,0 +1,22 @@
MIT License
Copyright (c) 2014-present Sebastian McKenzie and other contributors
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,19 @@
# @babel/helper-validator-identifier
> Validate identifier/keywords name
See our website [@babel/helper-validator-identifier](https://babeljs.io/docs/babel-helper-validator-identifier) for more information.
## Install
Using npm:
```sh
npm install --save @babel/helper-validator-identifier
```
or using yarn:
```sh
yarn add @babel/helper-validator-identifier
```
@@ -0,0 +1,70 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.isIdentifierChar = isIdentifierChar;
exports.isIdentifierName = isIdentifierName;
exports.isIdentifierStart = isIdentifierStart;
let nonASCIIidentifierStartChars = "\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0370-\u0374\u0376\u0377\u037a-\u037d\u037f\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u048a-\u052f\u0531-\u0556\u0559\u0560-\u0588\u05d0-\u05ea\u05ef-\u05f2\u0620-\u064a\u066e\u066f\u0671-\u06d3\u06d5\u06e5\u06e6\u06ee\u06ef\u06fa-\u06fc\u06ff\u0710\u0712-\u072f\u074d-\u07a5\u07b1\u07ca-\u07ea\u07f4\u07f5\u07fa\u0800-\u0815\u081a\u0824\u0828\u0840-\u0858\u0860-\u086a\u0870-\u0887\u0889-\u088e\u08a0-\u08c9\u0904-\u0939\u093d\u0950\u0958-\u0961\u0971-\u0980\u0985-\u098c\u098f\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bd\u09ce\u09dc\u09dd\u09df-\u09e1\u09f0\u09f1\u09fc\u0a05-\u0a0a\u0a0f\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a59-\u0a5c\u0a5e\u0a72-\u0a74\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2\u0ab3\u0ab5-\u0ab9\u0abd\u0ad0\u0ae0\u0ae1\u0af9\u0b05-\u0b0c\u0b0f\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32\u0b33\u0b35-\u0b39\u0b3d\u0b5c\u0b5d\u0b5f-\u0b61\u0b71\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bd0\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c39\u0c3d\u0c58-\u0c5a\u0c5d\u0c60\u0c61\u0c80\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbd\u0cdd\u0cde\u0ce0\u0ce1\u0cf1\u0cf2\u0d04-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d\u0d4e\u0d54-\u0d56\u0d5f-\u0d61\u0d7a-\u0d7f\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0e01-\u0e30\u0e32\u0e33\u0e40-\u0e46\u0e81\u0e82\u0e84\u0e86-\u0e8a\u0e8c-\u0ea3\u0ea5\u0ea7-\u0eb0\u0eb2\u0eb3\u0ebd\u0ec0-\u0ec4\u0ec6\u0edc-\u0edf\u0f00\u0f40-\u0f47\u0f49-\u0f6c\u0f88-\u0f8c\u1000-\u102a\u103f\u1050-\u1055\u105a-\u105d\u1061\u1065\u1066\u106e-\u1070\u1075-\u1081\u108e\u10a0-\u10c5\u10c7\u10cd\u10d0-\u10fa\u10fc-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u1380-\u138f\u13a0-\u13f5\u13f8-\u13fd\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16ee-\u16f8\u1700-\u1711\u171f-\u1731\u1740-\u1751\u1760-\u176c\u176e-\u1770\u1780-\u17b3\u17d7\u17dc\u1820-\u1878\u1880-\u18a8\u18aa\u18b0-\u18f5\u1900-\u191e\u1950-\u196d\u1970-\u1974\u1980-\u19ab\u19b0-\u19c9\u1a00-\u1a16\u1a20-\u1a54\u1aa7\u1b05-\u1b33\u1b45-\u1b4c\u1b83-\u1ba0\u1bae\u1baf\u1bba-\u1be5\u1c00-\u1c23\u1c4d-\u1c4f\u1c5a-\u1c7d\u1c80-\u1c88\u1c90-\u1cba\u1cbd-\u1cbf\u1ce9-\u1cec\u1cee-\u1cf3\u1cf5\u1cf6\u1cfa\u1d00-\u1dbf\u1e00-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u2071\u207f\u2090-\u209c\u2102\u2107\u210a-\u2113\u2115\u2118-\u211d\u2124\u2126\u2128\u212a-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2160-\u2188\u2c00-\u2ce4\u2ceb-\u2cee\u2cf2\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d80-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303c\u3041-\u3096\u309b-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312f\u3131-\u318e\u31a0-\u31bf\u31f0-\u31ff\u3400-\u4dbf\u4e00-\ua48c\ua4d0-\ua4fd\ua500-\ua60c\ua610-\ua61f\ua62a\ua62b\ua640-\ua66e\ua67f-\ua69d\ua6a0-\ua6ef\ua717-\ua71f\ua722-\ua788\ua78b-\ua7ca\ua7d0\ua7d1\ua7d3\ua7d5-\ua7d9\ua7f2-\ua801\ua803-\ua805\ua807-\ua80a\ua80c-\ua822\ua840-\ua873\ua882-\ua8b3\ua8f2-\ua8f7\ua8fb\ua8fd\ua8fe\ua90a-\ua925\ua930-\ua946\ua960-\ua97c\ua984-\ua9b2\ua9cf\ua9e0-\ua9e4\ua9e6-\ua9ef\ua9fa-\ua9fe\uaa00-\uaa28\uaa40-\uaa42\uaa44-\uaa4b\uaa60-\uaa76\uaa7a\uaa7e-\uaaaf\uaab1\uaab5\uaab6\uaab9-\uaabd\uaac0\uaac2\uaadb-\uaadd\uaae0-\uaaea\uaaf2-\uaaf4\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uab30-\uab5a\uab5c-\uab69\uab70-\uabe2\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\uf900-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\ufb1d\ufb1f-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40\ufb41\ufb43\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe70-\ufe74\ufe76-\ufefc\uff21-\uff3a\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc";
let nonASCIIidentifierChars = "\u200c\u200d\xb7\u0300-\u036f\u0387\u0483-\u0487\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u0669\u0670\u06d6-\u06dc\u06df-\u06e4\u06e7\u06e8\u06ea-\u06ed\u06f0-\u06f9\u0711\u0730-\u074a\u07a6-\u07b0\u07c0-\u07c9\u07eb-\u07f3\u07fd\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0859-\u085b\u0898-\u089f\u08ca-\u08e1\u08e3-\u0903\u093a-\u093c\u093e-\u094f\u0951-\u0957\u0962\u0963\u0966-\u096f\u0981-\u0983\u09bc\u09be-\u09c4\u09c7\u09c8\u09cb-\u09cd\u09d7\u09e2\u09e3\u09e6-\u09ef\u09fe\u0a01-\u0a03\u0a3c\u0a3e-\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a66-\u0a71\u0a75\u0a81-\u0a83\u0abc\u0abe-\u0ac5\u0ac7-\u0ac9\u0acb-\u0acd\u0ae2\u0ae3\u0ae6-\u0aef\u0afa-\u0aff\u0b01-\u0b03\u0b3c\u0b3e-\u0b44\u0b47\u0b48\u0b4b-\u0b4d\u0b55-\u0b57\u0b62\u0b63\u0b66-\u0b6f\u0b82\u0bbe-\u0bc2\u0bc6-\u0bc8\u0bca-\u0bcd\u0bd7\u0be6-\u0bef\u0c00-\u0c04\u0c3c\u0c3e-\u0c44\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62\u0c63\u0c66-\u0c6f\u0c81-\u0c83\u0cbc\u0cbe-\u0cc4\u0cc6-\u0cc8\u0cca-\u0ccd\u0cd5\u0cd6\u0ce2\u0ce3\u0ce6-\u0cef\u0cf3\u0d00-\u0d03\u0d3b\u0d3c\u0d3e-\u0d44\u0d46-\u0d48\u0d4a-\u0d4d\u0d57\u0d62\u0d63\u0d66-\u0d6f\u0d81-\u0d83\u0dca\u0dcf-\u0dd4\u0dd6\u0dd8-\u0ddf\u0de6-\u0def\u0df2\u0df3\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0e50-\u0e59\u0eb1\u0eb4-\u0ebc\u0ec8-\u0ece\u0ed0-\u0ed9\u0f18\u0f19\u0f20-\u0f29\u0f35\u0f37\u0f39\u0f3e\u0f3f\u0f71-\u0f84\u0f86\u0f87\u0f8d-\u0f97\u0f99-\u0fbc\u0fc6\u102b-\u103e\u1040-\u1049\u1056-\u1059\u105e-\u1060\u1062-\u1064\u1067-\u106d\u1071-\u1074\u1082-\u108d\u108f-\u109d\u135d-\u135f\u1369-\u1371\u1712-\u1715\u1732-\u1734\u1752\u1753\u1772\u1773\u17b4-\u17d3\u17dd\u17e0-\u17e9\u180b-\u180d\u180f-\u1819\u18a9\u1920-\u192b\u1930-\u193b\u1946-\u194f\u19d0-\u19da\u1a17-\u1a1b\u1a55-\u1a5e\u1a60-\u1a7c\u1a7f-\u1a89\u1a90-\u1a99\u1ab0-\u1abd\u1abf-\u1ace\u1b00-\u1b04\u1b34-\u1b44\u1b50-\u1b59\u1b6b-\u1b73\u1b80-\u1b82\u1ba1-\u1bad\u1bb0-\u1bb9\u1be6-\u1bf3\u1c24-\u1c37\u1c40-\u1c49\u1c50-\u1c59\u1cd0-\u1cd2\u1cd4-\u1ce8\u1ced\u1cf4\u1cf7-\u1cf9\u1dc0-\u1dff\u200c\u200d\u203f\u2040\u2054\u20d0-\u20dc\u20e1\u20e5-\u20f0\u2cef-\u2cf1\u2d7f\u2de0-\u2dff\u302a-\u302f\u3099\u309a\u30fb\ua620-\ua629\ua66f\ua674-\ua67d\ua69e\ua69f\ua6f0\ua6f1\ua802\ua806\ua80b\ua823-\ua827\ua82c\ua880\ua881\ua8b4-\ua8c5\ua8d0-\ua8d9\ua8e0-\ua8f1\ua8ff-\ua909\ua926-\ua92d\ua947-\ua953\ua980-\ua983\ua9b3-\ua9c0\ua9d0-\ua9d9\ua9e5\ua9f0-\ua9f9\uaa29-\uaa36\uaa43\uaa4c\uaa4d\uaa50-\uaa59\uaa7b-\uaa7d\uaab0\uaab2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1\uaaeb-\uaaef\uaaf5\uaaf6\uabe3-\uabea\uabec\uabed\uabf0-\uabf9\ufb1e\ufe00-\ufe0f\ufe20-\ufe2f\ufe33\ufe34\ufe4d-\ufe4f\uff10-\uff19\uff3f\uff65";
const nonASCIIidentifierStart = new RegExp("[" + nonASCIIidentifierStartChars + "]");
const nonASCIIidentifier = new RegExp("[" + nonASCIIidentifierStartChars + nonASCIIidentifierChars + "]");
nonASCIIidentifierStartChars = nonASCIIidentifierChars = null;
const astralIdentifierStartCodes = [0, 11, 2, 25, 2, 18, 2, 1, 2, 14, 3, 13, 35, 122, 70, 52, 268, 28, 4, 48, 48, 31, 14, 29, 6, 37, 11, 29, 3, 35, 5, 7, 2, 4, 43, 157, 19, 35, 5, 35, 5, 39, 9, 51, 13, 10, 2, 14, 2, 6, 2, 1, 2, 10, 2, 14, 2, 6, 2, 1, 68, 310, 10, 21, 11, 7, 25, 5, 2, 41, 2, 8, 70, 5, 3, 0, 2, 43, 2, 1, 4, 0, 3, 22, 11, 22, 10, 30, 66, 18, 2, 1, 11, 21, 11, 25, 71, 55, 7, 1, 65, 0, 16, 3, 2, 2, 2, 28, 43, 28, 4, 28, 36, 7, 2, 27, 28, 53, 11, 21, 11, 18, 14, 17, 111, 72, 56, 50, 14, 50, 14, 35, 349, 41, 7, 1, 79, 28, 11, 0, 9, 21, 43, 17, 47, 20, 28, 22, 13, 52, 58, 1, 3, 0, 14, 44, 33, 24, 27, 35, 30, 0, 3, 0, 9, 34, 4, 0, 13, 47, 15, 3, 22, 0, 2, 0, 36, 17, 2, 24, 20, 1, 64, 6, 2, 0, 2, 3, 2, 14, 2, 9, 8, 46, 39, 7, 3, 1, 3, 21, 2, 6, 2, 1, 2, 4, 4, 0, 19, 0, 13, 4, 159, 52, 19, 3, 21, 2, 31, 47, 21, 1, 2, 0, 185, 46, 42, 3, 37, 47, 21, 0, 60, 42, 14, 0, 72, 26, 38, 6, 186, 43, 117, 63, 32, 7, 3, 0, 3, 7, 2, 1, 2, 23, 16, 0, 2, 0, 95, 7, 3, 38, 17, 0, 2, 0, 29, 0, 11, 39, 8, 0, 22, 0, 12, 45, 20, 0, 19, 72, 264, 8, 2, 36, 18, 0, 50, 29, 113, 6, 2, 1, 2, 37, 22, 0, 26, 5, 2, 1, 2, 31, 15, 0, 328, 18, 16, 0, 2, 12, 2, 33, 125, 0, 80, 921, 103, 110, 18, 195, 2637, 96, 16, 1071, 18, 5, 4026, 582, 8634, 568, 8, 30, 18, 78, 18, 29, 19, 47, 17, 3, 32, 20, 6, 18, 689, 63, 129, 74, 6, 0, 67, 12, 65, 1, 2, 0, 29, 6135, 9, 1237, 43, 8, 8936, 3, 2, 6, 2, 1, 2, 290, 16, 0, 30, 2, 3, 0, 15, 3, 9, 395, 2309, 106, 6, 12, 4, 8, 8, 9, 5991, 84, 2, 70, 2, 1, 3, 0, 3, 1, 3, 3, 2, 11, 2, 0, 2, 6, 2, 64, 2, 3, 3, 7, 2, 6, 2, 27, 2, 3, 2, 4, 2, 0, 4, 6, 2, 339, 3, 24, 2, 24, 2, 30, 2, 24, 2, 30, 2, 24, 2, 30, 2, 24, 2, 30, 2, 24, 2, 7, 1845, 30, 7, 5, 262, 61, 147, 44, 11, 6, 17, 0, 322, 29, 19, 43, 485, 27, 757, 6, 2, 3, 2, 1, 2, 14, 2, 196, 60, 67, 8, 0, 1205, 3, 2, 26, 2, 1, 2, 0, 3, 0, 2, 9, 2, 3, 2, 0, 2, 0, 7, 0, 5, 0, 2, 0, 2, 0, 2, 2, 2, 1, 2, 0, 3, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 1, 2, 0, 3, 3, 2, 6, 2, 3, 2, 3, 2, 0, 2, 9, 2, 16, 6, 2, 2, 4, 2, 16, 4421, 42719, 33, 4153, 7, 221, 3, 5761, 15, 7472, 16, 621, 2467, 541, 1507, 4938, 6, 4191];
const astralIdentifierCodes = [509, 0, 227, 0, 150, 4, 294, 9, 1368, 2, 2, 1, 6, 3, 41, 2, 5, 0, 166, 1, 574, 3, 9, 9, 370, 1, 81, 2, 71, 10, 50, 3, 123, 2, 54, 14, 32, 10, 3, 1, 11, 3, 46, 10, 8, 0, 46, 9, 7, 2, 37, 13, 2, 9, 6, 1, 45, 0, 13, 2, 49, 13, 9, 3, 2, 11, 83, 11, 7, 0, 3, 0, 158, 11, 6, 9, 7, 3, 56, 1, 2, 6, 3, 1, 3, 2, 10, 0, 11, 1, 3, 6, 4, 4, 193, 17, 10, 9, 5, 0, 82, 19, 13, 9, 214, 6, 3, 8, 28, 1, 83, 16, 16, 9, 82, 12, 9, 9, 84, 14, 5, 9, 243, 14, 166, 9, 71, 5, 2, 1, 3, 3, 2, 0, 2, 1, 13, 9, 120, 6, 3, 6, 4, 0, 29, 9, 41, 6, 2, 3, 9, 0, 10, 10, 47, 15, 406, 7, 2, 7, 17, 9, 57, 21, 2, 13, 123, 5, 4, 0, 2, 1, 2, 6, 2, 0, 9, 9, 49, 4, 2, 1, 2, 4, 9, 9, 330, 3, 10, 1, 2, 0, 49, 6, 4, 4, 14, 9, 5351, 0, 7, 14, 13835, 9, 87, 9, 39, 4, 60, 6, 26, 9, 1014, 0, 2, 54, 8, 3, 82, 0, 12, 1, 19628, 1, 4706, 45, 3, 22, 543, 4, 4, 5, 9, 7, 3, 6, 31, 3, 149, 2, 1418, 49, 513, 54, 5, 49, 9, 0, 15, 0, 23, 4, 2, 14, 1361, 6, 2, 16, 3, 6, 2, 1, 2, 4, 101, 0, 161, 6, 10, 9, 357, 0, 62, 13, 499, 13, 983, 6, 110, 6, 6, 9, 4759, 9, 787719, 239];
function isInAstralSet(code, set) {
let pos = 0x10000;
for (let i = 0, length = set.length; i < length; i += 2) {
pos += set[i];
if (pos > code) return false;
pos += set[i + 1];
if (pos >= code) return true;
}
return false;
}
function isIdentifierStart(code) {
if (code < 65) return code === 36;
if (code <= 90) return true;
if (code < 97) return code === 95;
if (code <= 122) return true;
if (code <= 0xffff) {
return code >= 0xaa && nonASCIIidentifierStart.test(String.fromCharCode(code));
}
return isInAstralSet(code, astralIdentifierStartCodes);
}
function isIdentifierChar(code) {
if (code < 48) return code === 36;
if (code < 58) return true;
if (code < 65) return false;
if (code <= 90) return true;
if (code < 97) return code === 95;
if (code <= 122) return true;
if (code <= 0xffff) {
return code >= 0xaa && nonASCIIidentifier.test(String.fromCharCode(code));
}
return isInAstralSet(code, astralIdentifierStartCodes) || isInAstralSet(code, astralIdentifierCodes);
}
function isIdentifierName(name) {
let isFirst = true;
for (let i = 0; i < name.length; i++) {
let cp = name.charCodeAt(i);
if ((cp & 0xfc00) === 0xd800 && i + 1 < name.length) {
const trail = name.charCodeAt(++i);
if ((trail & 0xfc00) === 0xdc00) {
cp = 0x10000 + ((cp & 0x3ff) << 10) + (trail & 0x3ff);
}
}
if (isFirst) {
isFirst = false;
if (!isIdentifierStart(cp)) {
return false;
}
} else if (!isIdentifierChar(cp)) {
return false;
}
}
return !isFirst;
}
//# sourceMappingURL=identifier.js.map
File diff suppressed because one or more lines are too long
@@ -0,0 +1,57 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
Object.defineProperty(exports, "isIdentifierChar", {
enumerable: true,
get: function () {
return _identifier.isIdentifierChar;
}
});
Object.defineProperty(exports, "isIdentifierName", {
enumerable: true,
get: function () {
return _identifier.isIdentifierName;
}
});
Object.defineProperty(exports, "isIdentifierStart", {
enumerable: true,
get: function () {
return _identifier.isIdentifierStart;
}
});
Object.defineProperty(exports, "isKeyword", {
enumerable: true,
get: function () {
return _keyword.isKeyword;
}
});
Object.defineProperty(exports, "isReservedWord", {
enumerable: true,
get: function () {
return _keyword.isReservedWord;
}
});
Object.defineProperty(exports, "isStrictBindOnlyReservedWord", {
enumerable: true,
get: function () {
return _keyword.isStrictBindOnlyReservedWord;
}
});
Object.defineProperty(exports, "isStrictBindReservedWord", {
enumerable: true,
get: function () {
return _keyword.isStrictBindReservedWord;
}
});
Object.defineProperty(exports, "isStrictReservedWord", {
enumerable: true,
get: function () {
return _keyword.isStrictReservedWord;
}
});
var _identifier = require("./identifier.js");
var _keyword = require("./keyword.js");
//# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
{"version":3,"names":["_identifier","require","_keyword"],"sources":["../src/index.ts"],"sourcesContent":["export {\n isIdentifierName,\n isIdentifierChar,\n isIdentifierStart,\n} from \"./identifier.ts\";\nexport {\n isReservedWord,\n isStrictBindOnlyReservedWord,\n isStrictBindReservedWord,\n isStrictReservedWord,\n isKeyword,\n} from \"./keyword.ts\";\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,IAAAA,WAAA,GAAAC,OAAA;AAKA,IAAAC,QAAA,GAAAD,OAAA"}
@@ -0,0 +1,35 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.isKeyword = isKeyword;
exports.isReservedWord = isReservedWord;
exports.isStrictBindOnlyReservedWord = isStrictBindOnlyReservedWord;
exports.isStrictBindReservedWord = isStrictBindReservedWord;
exports.isStrictReservedWord = isStrictReservedWord;
const reservedWords = {
keyword: ["break", "case", "catch", "continue", "debugger", "default", "do", "else", "finally", "for", "function", "if", "return", "switch", "throw", "try", "var", "const", "while", "with", "new", "this", "super", "class", "extends", "export", "import", "null", "true", "false", "in", "instanceof", "typeof", "void", "delete"],
strict: ["implements", "interface", "let", "package", "private", "protected", "public", "static", "yield"],
strictBind: ["eval", "arguments"]
};
const keywords = new Set(reservedWords.keyword);
const reservedWordsStrictSet = new Set(reservedWords.strict);
const reservedWordsStrictBindSet = new Set(reservedWords.strictBind);
function isReservedWord(word, inModule) {
return inModule && word === "await" || word === "enum";
}
function isStrictReservedWord(word, inModule) {
return isReservedWord(word, inModule) || reservedWordsStrictSet.has(word);
}
function isStrictBindOnlyReservedWord(word) {
return reservedWordsStrictBindSet.has(word);
}
function isStrictBindReservedWord(word, inModule) {
return isStrictReservedWord(word, inModule) || isStrictBindOnlyReservedWord(word);
}
function isKeyword(word) {
return keywords.has(word);
}
//# sourceMappingURL=keyword.js.map
@@ -0,0 +1 @@
{"version":3,"names":["reservedWords","keyword","strict","strictBind","keywords","Set","reservedWordsStrictSet","reservedWordsStrictBindSet","isReservedWord","word","inModule","isStrictReservedWord","has","isStrictBindOnlyReservedWord","isStrictBindReservedWord","isKeyword"],"sources":["../src/keyword.ts"],"sourcesContent":["const reservedWords = {\n keyword: [\n \"break\",\n \"case\",\n \"catch\",\n \"continue\",\n \"debugger\",\n \"default\",\n \"do\",\n \"else\",\n \"finally\",\n \"for\",\n \"function\",\n \"if\",\n \"return\",\n \"switch\",\n \"throw\",\n \"try\",\n \"var\",\n \"const\",\n \"while\",\n \"with\",\n \"new\",\n \"this\",\n \"super\",\n \"class\",\n \"extends\",\n \"export\",\n \"import\",\n \"null\",\n \"true\",\n \"false\",\n \"in\",\n \"instanceof\",\n \"typeof\",\n \"void\",\n \"delete\",\n ],\n strict: [\n \"implements\",\n \"interface\",\n \"let\",\n \"package\",\n \"private\",\n \"protected\",\n \"public\",\n \"static\",\n \"yield\",\n ],\n strictBind: [\"eval\", \"arguments\"],\n};\nconst keywords = new Set(reservedWords.keyword);\nconst reservedWordsStrictSet = new Set(reservedWords.strict);\nconst reservedWordsStrictBindSet = new Set(reservedWords.strictBind);\n\n/**\n * Checks if word is a reserved word in non-strict mode\n */\nexport function isReservedWord(word: string, inModule: boolean): boolean {\n return (inModule && word === \"await\") || word === \"enum\";\n}\n\n/**\n * Checks if word is a reserved word in non-binding strict mode\n *\n * Includes non-strict reserved words\n */\nexport function isStrictReservedWord(word: string, inModule: boolean): boolean {\n return isReservedWord(word, inModule) || reservedWordsStrictSet.has(word);\n}\n\n/**\n * Checks if word is a reserved word in binding strict mode, but it is allowed as\n * a normal identifier.\n */\nexport function isStrictBindOnlyReservedWord(word: string): boolean {\n return reservedWordsStrictBindSet.has(word);\n}\n\n/**\n * Checks if word is a reserved word in binding strict mode\n *\n * Includes non-strict reserved words and non-binding strict reserved words\n */\nexport function isStrictBindReservedWord(\n word: string,\n inModule: boolean,\n): boolean {\n return (\n isStrictReservedWord(word, inModule) || isStrictBindOnlyReservedWord(word)\n );\n}\n\nexport function isKeyword(word: string): boolean {\n return keywords.has(word);\n}\n"],"mappings":";;;;;;;;;;AAAA,MAAMA,aAAa,GAAG;EACpBC,OAAO,EAAE,CACP,OAAO,EACP,MAAM,EACN,OAAO,EACP,UAAU,EACV,UAAU,EACV,SAAS,EACT,IAAI,EACJ,MAAM,EACN,SAAS,EACT,KAAK,EACL,UAAU,EACV,IAAI,EACJ,QAAQ,EACR,QAAQ,EACR,OAAO,EACP,KAAK,EACL,KAAK,EACL,OAAO,EACP,OAAO,EACP,MAAM,EACN,KAAK,EACL,MAAM,EACN,OAAO,EACP,OAAO,EACP,SAAS,EACT,QAAQ,EACR,QAAQ,EACR,MAAM,EACN,MAAM,EACN,OAAO,EACP,IAAI,EACJ,YAAY,EACZ,QAAQ,EACR,MAAM,EACN,QAAQ,CACT;EACDC,MAAM,EAAE,CACN,YAAY,EACZ,WAAW,EACX,KAAK,EACL,SAAS,EACT,SAAS,EACT,WAAW,EACX,QAAQ,EACR,QAAQ,EACR,OAAO,CACR;EACDC,UAAU,EAAE,CAAC,MAAM,EAAE,WAAW;AAClC,CAAC;AACD,MAAMC,QAAQ,GAAG,IAAIC,GAAG,CAACL,aAAa,CAACC,OAAO,CAAC;AAC/C,MAAMK,sBAAsB,GAAG,IAAID,GAAG,CAACL,aAAa,CAACE,MAAM,CAAC;AAC5D,MAAMK,0BAA0B,GAAG,IAAIF,GAAG,CAACL,aAAa,CAACG,UAAU,CAAC;AAK7D,SAASK,cAAcA,CAACC,IAAY,EAAEC,QAAiB,EAAW;EACvE,OAAQA,QAAQ,IAAID,IAAI,KAAK,OAAO,IAAKA,IAAI,KAAK,MAAM;AAC1D;AAOO,SAASE,oBAAoBA,CAACF,IAAY,EAAEC,QAAiB,EAAW;EAC7E,OAAOF,cAAc,CAACC,IAAI,EAAEC,QAAQ,CAAC,IAAIJ,sBAAsB,CAACM,GAAG,CAACH,IAAI,CAAC;AAC3E;AAMO,SAASI,4BAA4BA,CAACJ,IAAY,EAAW;EAClE,OAAOF,0BAA0B,CAACK,GAAG,CAACH,IAAI,CAAC;AAC7C;AAOO,SAASK,wBAAwBA,CACtCL,IAAY,EACZC,QAAiB,EACR;EACT,OACEC,oBAAoB,CAACF,IAAI,EAAEC,QAAQ,CAAC,IAAIG,4BAA4B,CAACJ,IAAI,CAAC;AAE9E;AAEO,SAASM,SAASA,CAACN,IAAY,EAAW;EAC/C,OAAOL,QAAQ,CAACQ,GAAG,CAACH,IAAI,CAAC;AAC3B"}
@@ -0,0 +1,28 @@
{
"name": "@babel/helper-validator-identifier",
"version": "7.22.20",
"description": "Validate identifier/keywords name",
"repository": {
"type": "git",
"url": "https://github.com/babel/babel.git",
"directory": "packages/babel-helper-validator-identifier"
},
"license": "MIT",
"publishConfig": {
"access": "public"
},
"main": "./lib/index.js",
"exports": {
".": "./lib/index.js",
"./package.json": "./package.json"
},
"devDependencies": {
"@unicode/unicode-15.1.0": "^1.5.2",
"charcodes": "^0.2.0"
},
"engines": {
"node": ">=6.9.0"
},
"author": "The Babel Team (https://babel.dev/team)",
"type": "commonjs"
}
@@ -0,0 +1,73 @@
"use strict";
// Always use the latest available version of Unicode!
// https://tc39.github.io/ecma262/#sec-conformance
const version = "15.1.0";
const start = require(
"@unicode/unicode-" + version + "/Binary_Property/ID_Start/code-points.js"
).filter(function (ch) {
return ch > 0x7f;
});
let last = -1;
const cont = require(
"@unicode/unicode-" + version + "/Binary_Property/ID_Continue/code-points.js"
).filter(function (ch) {
return ch > 0x7f && search(start, ch, last + 1) == -1;
});
function search(arr, ch, starting) {
for (let i = starting; arr[i] <= ch && i < arr.length; last = i++) {
if (arr[i] === ch) return i;
}
return -1;
}
function pad(str, width) {
while (str.length < width) str = "0" + str;
return str;
}
function esc(code) {
const hex = code.toString(16);
if (hex.length <= 2) return "\\x" + pad(hex, 2);
else return "\\u" + pad(hex, 4);
}
function generate(chars) {
const astral = [];
let re = "";
for (let i = 0, at = 0x10000; i < chars.length; i++) {
const from = chars[i];
let to = from;
while (i < chars.length - 1 && chars[i + 1] == to + 1) {
i++;
to++;
}
if (to <= 0xffff) {
if (from == to) re += esc(from);
else if (from + 1 == to) re += esc(from) + esc(to);
else re += esc(from) + "-" + esc(to);
} else {
astral.push(from - at, to - from);
at = to;
}
}
return { nonASCII: re, astral: astral };
}
const startData = generate(start);
const contData = generate(cont);
console.log("/* prettier-ignore */");
console.log('let nonASCIIidentifierStartChars = "' + startData.nonASCII + '";');
console.log("/* prettier-ignore */");
console.log('let nonASCIIidentifierChars = "' + contData.nonASCII + '";');
console.log("/* prettier-ignore */");
console.log(
"const astralIdentifierStartCodes = " + JSON.stringify(startData.astral) + ";"
);
console.log("/* prettier-ignore */");
console.log(
"const astralIdentifierCodes = " + JSON.stringify(contData.astral) + ";"
);
File diff suppressed because it is too large Load Diff
+19
View File
@@ -0,0 +1,19 @@
Copyright (C) 2012-2014 by various contributors (see AUTHORS)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
+19
View File
@@ -0,0 +1,19 @@
# @babel/parser
> A JavaScript parser
See our website [@babel/parser](https://babeljs.io/docs/babel-parser) for more information or the [issues](https://github.com/babel/babel/issues?utf8=%E2%9C%93&q=is%3Aissue+label%3A%22pkg%3A%20parser%22+is%3Aopen) associated with this package.
## Install
Using npm:
```sh
npm install --save-dev @babel/parser
```
or using yarn:
```sh
yarn add @babel/parser --dev
```
+15
View File
@@ -0,0 +1,15 @@
#!/usr/bin/env node
/* eslint no-var: 0 */
var parser = require("..");
var fs = require("fs");
var filename = process.argv[2];
if (!filename) {
console.error("no filename specified");
} else {
var file = fs.readFileSync(filename, "utf8");
var ast = parser.parse(file);
console.log(JSON.stringify(ast, null, " "));
}
+5
View File
@@ -0,0 +1,5 @@
try {
module.exports = require("./lib/index.cjs");
} catch {
module.exports = require("./lib/index.js");
}
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
+46
View File
@@ -0,0 +1,46 @@
{
"name": "@babel/parser",
"version": "7.24.0",
"description": "A JavaScript parser",
"author": "The Babel Team (https://babel.dev/team)",
"homepage": "https://babel.dev/docs/en/next/babel-parser",
"bugs": "https://github.com/babel/babel/issues?utf8=%E2%9C%93&q=is%3Aissue+label%3A%22pkg%3A+parser+%28babylon%29%22+is%3Aopen",
"license": "MIT",
"publishConfig": {
"access": "public"
},
"keywords": [
"babel",
"javascript",
"parser",
"tc39",
"ecmascript",
"@babel/parser"
],
"repository": {
"type": "git",
"url": "https://github.com/babel/babel.git",
"directory": "packages/babel-parser"
},
"main": "./lib/index.js",
"types": "./typings/babel-parser.d.ts",
"files": [
"bin",
"lib",
"typings/babel-parser.d.ts",
"index.cjs"
],
"engines": {
"node": ">=6.0.0"
},
"devDependencies": {
"@babel/code-frame": "^7.23.5",
"@babel/helper-check-duplicate-nodes": "^7.22.5",
"@babel/helper-fixtures": "^7.24.0",
"@babel/helper-string-parser": "^7.23.4",
"@babel/helper-validator-identifier": "^7.22.20",
"charcodes": "^0.2.0"
},
"bin": "./bin/babel-parser.js",
"type": "commonjs"
}
+251
View File
@@ -0,0 +1,251 @@
// This file is auto-generated! Do not modify it directly.
/* eslint-disable import/no-extraneous-dependencies, @typescript-eslint/consistent-type-imports, prettier/prettier */
import * as _babel_types from '@babel/types';
type Plugin =
| "asyncDoExpressions"
| "asyncGenerators"
| "bigInt"
| "classPrivateMethods"
| "classPrivateProperties"
| "classProperties"
| "classStaticBlock" // Enabled by default
| "decimal"
| "decorators-legacy"
| "deferredImportEvaluation"
| "decoratorAutoAccessors"
| "destructuringPrivate"
| "doExpressions"
| "dynamicImport"
| "explicitResourceManagement"
| "exportDefaultFrom"
| "exportNamespaceFrom" // deprecated
| "flow"
| "flowComments"
| "functionBind"
| "functionSent"
| "importMeta"
| "jsx"
| "logicalAssignment"
| "importAssertions" // deprecated
| "importAttributes"
| "importReflection"
| "moduleBlocks"
| "moduleStringNames"
| "nullishCoalescingOperator"
| "numericSeparator"
| "objectRestSpread"
| "optionalCatchBinding"
| "optionalChaining"
| "partialApplication"
| "placeholders"
| "privateIn" // Enabled by default
| "regexpUnicodeSets" // Enabled by default
| "sourcePhaseImports"
| "throwExpressions"
| "topLevelAwait"
| "v8intrinsic"
| ParserPluginWithOptions[0];
type ParserPluginWithOptions =
| ["decorators", DecoratorsPluginOptions]
| ["estree", { classFeatures?: boolean }]
| ["importAttributes", { deprecatedAssertSyntax: boolean }]
// @deprecated
| ["moduleAttributes", { version: "may-2020" }]
| ["optionalChainingAssign", { version: "2023-07" }]
| ["pipelineOperator", PipelineOperatorPluginOptions]
| ["recordAndTuple", RecordAndTuplePluginOptions]
| ["flow", FlowPluginOptions]
| ["typescript", TypeScriptPluginOptions];
type PluginConfig = Plugin | ParserPluginWithOptions;
interface DecoratorsPluginOptions {
decoratorsBeforeExport?: boolean;
allowCallParenthesized?: boolean;
}
interface PipelineOperatorPluginOptions {
proposal: "minimal" | "fsharp" | "hack" | "smart";
topicToken?: "%" | "#" | "@@" | "^^" | "^";
}
interface RecordAndTuplePluginOptions {
syntaxType: "bar" | "hash";
}
interface FlowPluginOptions {
all?: boolean;
enums?: boolean;
}
interface TypeScriptPluginOptions {
dts?: boolean;
disallowAmbiguousJSXLike?: boolean;
}
// Type definitions for @babel/parser
// Project: https://github.com/babel/babel/tree/main/packages/babel-parser
// Definitions by: Troy Gerwien <https://github.com/yortus>
// Marvin Hagemeister <https://github.com/marvinhagemeister>
// Avi Vahl <https://github.com/AviVahl>
// TypeScript Version: 2.9
/**
* Parse the provided code as an entire ECMAScript program.
*/
declare function parse(
input: string,
options?: ParserOptions
): ParseResult<_babel_types.File>;
/**
* Parse the provided code as a single expression.
*/
declare function parseExpression(
input: string,
options?: ParserOptions
): ParseResult<_babel_types.Expression>;
interface ParserOptions {
/**
* By default, import and export declarations can only appear at a program's top level.
* Setting this option to true allows them anywhere where a statement is allowed.
*/
allowImportExportEverywhere?: boolean;
/**
* By default, await use is not allowed outside of an async function.
* Set this to true to accept such code.
*/
allowAwaitOutsideFunction?: boolean;
/**
* By default, a return statement at the top level raises an error.
* Set this to true to accept such code.
*/
allowReturnOutsideFunction?: boolean;
/**
* By default, new.target use is not allowed outside of a function or class.
* Set this to true to accept such code.
*/
allowNewTargetOutsideFunction?: boolean;
allowSuperOutsideMethod?: boolean;
/**
* By default, exported identifiers must refer to a declared variable.
* Set this to true to allow export statements to reference undeclared variables.
*/
allowUndeclaredExports?: boolean;
/**
* By default, Babel parser JavaScript code according to Annex B syntax.
* Set this to `false` to disable such behavior.
*/
annexB?: boolean;
/**
* By default, Babel attaches comments to adjacent AST nodes.
* When this option is set to false, comments are not attached.
* It can provide up to 30% performance improvement when the input code has many comments.
* @babel/eslint-parser will set it for you.
* It is not recommended to use attachComment: false with Babel transform,
* as doing so removes all the comments in output code, and renders annotations such as
* /* istanbul ignore next *\/ nonfunctional.
*/
attachComment?: boolean;
/**
* By default, Babel always throws an error when it finds some invalid code.
* When this option is set to true, it will store the parsing error and
* try to continue parsing the invalid input file.
*/
errorRecovery?: boolean;
/**
* Indicate the mode the code should be parsed in.
* Can be one of "script", "module", or "unambiguous". Defaults to "script".
* "unambiguous" will make @babel/parser attempt to guess, based on the presence
* of ES6 import or export statements.
* Files with ES6 imports and exports are considered "module" and are otherwise "script".
*/
sourceType?: "script" | "module" | "unambiguous";
/**
* Correlate output AST nodes with their source filename.
* Useful when generating code and source maps from the ASTs of multiple input files.
*/
sourceFilename?: string;
/**
* By default, the first line of code parsed is treated as line 1.
* You can provide a line number to alternatively start with.
* Useful for integration with other source tools.
*/
startLine?: number;
/**
* By default, the parsed code is treated as if it starts from line 1, column 0.
* You can provide a column number to alternatively start with.
* Useful for integration with other source tools.
*/
startColumn?: number;
/**
* Array containing the plugins that you want to enable.
*/
plugins?: ParserPlugin[];
/**
* Should the parser work in strict mode.
* Defaults to true if sourceType === 'module'. Otherwise, false.
*/
strictMode?: boolean;
/**
* Adds a ranges property to each node: [node.start, node.end]
*/
ranges?: boolean;
/**
* Adds all parsed tokens to a tokens property on the File node.
*/
tokens?: boolean;
/**
* By default, the parser adds information about parentheses by setting
* `extra.parenthesized` to `true` as needed.
* When this option is `true` the parser creates `ParenthesizedExpression`
* AST nodes instead of using the `extra` property.
*/
createParenthesizedExpressions?: boolean;
/**
* The default is false in Babel 7 and true in Babel 8
* Set this to true to parse it as an `ImportExpression` node.
* Otherwise `import(foo)` is parsed as `CallExpression(Import, [Identifier(foo)])`.
*/
createImportExpressions?: boolean;
}
type ParserPlugin = PluginConfig;
declare const tokTypes: {
// todo(flow->ts) real token type
[name: string]: any;
};
interface ParseError {
code: string;
reasonCode: string;
}
type ParseResult<Result> = Result & {
errors: ParseError[];
};
export { DecoratorsPluginOptions, FlowPluginOptions, ParseError, ParseResult, ParserOptions, ParserPlugin, ParserPluginWithOptions, PipelineOperatorPluginOptions, RecordAndTuplePluginOptions, TypeScriptPluginOptions, parse, parseExpression, tokTypes };
+22
View File
@@ -0,0 +1,22 @@
MIT License
Copyright (c) 2014-present Sebastian McKenzie and other contributors
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+19
View File
@@ -0,0 +1,19 @@
# @babel/types
> Babel Types is a Lodash-esque utility library for AST nodes
See our website [@babel/types](https://babeljs.io/docs/babel-types) for more information or the [issues](https://github.com/babel/babel/issues?utf8=%E2%9C%93&q=is%3Aissue+label%3A%22pkg%3A%20types%22+is%3Aopen) associated with this package.
## Install
Using npm:
```sh
npm install --save-dev @babel/types
```
or using yarn:
```sh
yarn add @babel/types --dev
```
+16
View File
@@ -0,0 +1,16 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = assertNode;
var _isNode = require("../validators/isNode.js");
function assertNode(node) {
if (!(0, _isNode.default)(node)) {
var _node$type;
const type = (_node$type = node == null ? void 0 : node.type) != null ? _node$type : JSON.stringify(node);
throw new TypeError(`Not a valid node of type "${type}"`);
}
}
//# sourceMappingURL=assertNode.js.map
@@ -0,0 +1 @@
{"version":3,"names":["_isNode","require","assertNode","node","isNode","_node$type","type","JSON","stringify","TypeError"],"sources":["../../src/asserts/assertNode.ts"],"sourcesContent":["import isNode from \"../validators/isNode.ts\";\nimport type * as t from \"../index.ts\";\n\nexport default function assertNode(node?: any): asserts node is t.Node {\n if (!isNode(node)) {\n const type = node?.type ?? JSON.stringify(node);\n throw new TypeError(`Not a valid node of type \"${type}\"`);\n }\n}\n"],"mappings":";;;;;;AAAA,IAAAA,OAAA,GAAAC,OAAA;AAGe,SAASC,UAAUA,CAACC,IAAU,EAA0B;EACrE,IAAI,CAAC,IAAAC,eAAM,EAACD,IAAI,CAAC,EAAE;IAAA,IAAAE,UAAA;IACjB,MAAMC,IAAI,IAAAD,UAAA,GAAGF,IAAI,oBAAJA,IAAI,CAAEG,IAAI,YAAAD,UAAA,GAAIE,IAAI,CAACC,SAAS,CAACL,IAAI,CAAC;IAC/C,MAAM,IAAIM,SAAS,CAAE,6BAA4BH,IAAK,GAAE,CAAC;EAC3D;AACF"}
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
@@ -0,0 +1,3 @@
//# sourceMappingURL=index.js.map
File diff suppressed because one or more lines are too long
@@ -0,0 +1,18 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = createFlowUnionType;
var _index = require("../generated/index.js");
var _removeTypeDuplicates = require("../../modifications/flow/removeTypeDuplicates.js");
function createFlowUnionType(types) {
const flattened = (0, _removeTypeDuplicates.default)(types);
if (flattened.length === 1) {
return flattened[0];
} else {
return (0, _index.unionTypeAnnotation)(flattened);
}
}
//# sourceMappingURL=createFlowUnionType.js.map
@@ -0,0 +1 @@
{"version":3,"names":["_index","require","_removeTypeDuplicates","createFlowUnionType","types","flattened","removeTypeDuplicates","length","unionTypeAnnotation"],"sources":["../../../src/builders/flow/createFlowUnionType.ts"],"sourcesContent":["import { unionTypeAnnotation } from \"../generated/index.ts\";\nimport removeTypeDuplicates from \"../../modifications/flow/removeTypeDuplicates.ts\";\nimport type * as t from \"../../index.ts\";\n\n/**\n * Takes an array of `types` and flattens them, removing duplicates and\n * returns a `UnionTypeAnnotation` node containing them.\n */\nexport default function createFlowUnionType<T extends t.FlowType>(\n types: [T] | Array<T>,\n): T | t.UnionTypeAnnotation {\n const flattened = removeTypeDuplicates(types);\n\n if (flattened.length === 1) {\n return flattened[0] as T;\n } else {\n return unionTypeAnnotation(flattened);\n }\n}\n"],"mappings":";;;;;;AAAA,IAAAA,MAAA,GAAAC,OAAA;AACA,IAAAC,qBAAA,GAAAD,OAAA;AAOe,SAASE,mBAAmBA,CACzCC,KAAqB,EACM;EAC3B,MAAMC,SAAS,GAAG,IAAAC,6BAAoB,EAACF,KAAK,CAAC;EAE7C,IAAIC,SAAS,CAACE,MAAM,KAAK,CAAC,EAAE;IAC1B,OAAOF,SAAS,CAAC,CAAC,CAAC;EACrB,CAAC,MAAM;IACL,OAAO,IAAAG,0BAAmB,EAACH,SAAS,CAAC;EACvC;AACF"}
@@ -0,0 +1,31 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _index = require("../generated/index.js");
var _default = exports.default = createTypeAnnotationBasedOnTypeof;
function createTypeAnnotationBasedOnTypeof(type) {
switch (type) {
case "string":
return (0, _index.stringTypeAnnotation)();
case "number":
return (0, _index.numberTypeAnnotation)();
case "undefined":
return (0, _index.voidTypeAnnotation)();
case "boolean":
return (0, _index.booleanTypeAnnotation)();
case "function":
return (0, _index.genericTypeAnnotation)((0, _index.identifier)("Function"));
case "object":
return (0, _index.genericTypeAnnotation)((0, _index.identifier)("Object"));
case "symbol":
return (0, _index.genericTypeAnnotation)((0, _index.identifier)("Symbol"));
case "bigint":
return (0, _index.anyTypeAnnotation)();
}
throw new Error("Invalid typeof value: " + type);
}
//# sourceMappingURL=createTypeAnnotationBasedOnTypeof.js.map
@@ -0,0 +1 @@
{"version":3,"names":["_index","require","_default","exports","default","createTypeAnnotationBasedOnTypeof","type","stringTypeAnnotation","numberTypeAnnotation","voidTypeAnnotation","booleanTypeAnnotation","genericTypeAnnotation","identifier","anyTypeAnnotation","Error"],"sources":["../../../src/builders/flow/createTypeAnnotationBasedOnTypeof.ts"],"sourcesContent":["import {\n anyTypeAnnotation,\n stringTypeAnnotation,\n numberTypeAnnotation,\n voidTypeAnnotation,\n booleanTypeAnnotation,\n genericTypeAnnotation,\n identifier,\n} from \"../generated/index.ts\";\nimport type * as t from \"../../index.ts\";\n\nexport default createTypeAnnotationBasedOnTypeof as {\n (type: \"string\"): t.StringTypeAnnotation;\n (type: \"number\"): t.NumberTypeAnnotation;\n (type: \"undefined\"): t.VoidTypeAnnotation;\n (type: \"boolean\"): t.BooleanTypeAnnotation;\n (type: \"function\"): t.GenericTypeAnnotation;\n (type: \"object\"): t.GenericTypeAnnotation;\n (type: \"symbol\"): t.GenericTypeAnnotation;\n (type: \"bigint\"): t.AnyTypeAnnotation;\n};\n\n/**\n * Create a type annotation based on typeof expression.\n */\nfunction createTypeAnnotationBasedOnTypeof(type: string): t.FlowType {\n switch (type) {\n case \"string\":\n return stringTypeAnnotation();\n case \"number\":\n return numberTypeAnnotation();\n case \"undefined\":\n return voidTypeAnnotation();\n case \"boolean\":\n return booleanTypeAnnotation();\n case \"function\":\n return genericTypeAnnotation(identifier(\"Function\"));\n case \"object\":\n return genericTypeAnnotation(identifier(\"Object\"));\n case \"symbol\":\n return genericTypeAnnotation(identifier(\"Symbol\"));\n case \"bigint\":\n // todo: use BigInt annotation when Flow supports BigInt\n // https://github.com/facebook/flow/issues/6639\n return anyTypeAnnotation();\n }\n throw new Error(\"Invalid typeof value: \" + type);\n}\n"],"mappings":";;;;;;AAAA,IAAAA,MAAA,GAAAC,OAAA;AAQ+B,IAAAC,QAAA,GAAAC,OAAA,CAAAC,OAAA,GAGhBC,iCAAiC;AAchD,SAASA,iCAAiCA,CAACC,IAAY,EAAc;EACnE,QAAQA,IAAI;IACV,KAAK,QAAQ;MACX,OAAO,IAAAC,2BAAoB,EAAC,CAAC;IAC/B,KAAK,QAAQ;MACX,OAAO,IAAAC,2BAAoB,EAAC,CAAC;IAC/B,KAAK,WAAW;MACd,OAAO,IAAAC,yBAAkB,EAAC,CAAC;IAC7B,KAAK,SAAS;MACZ,OAAO,IAAAC,4BAAqB,EAAC,CAAC;IAChC,KAAK,UAAU;MACb,OAAO,IAAAC,4BAAqB,EAAC,IAAAC,iBAAU,EAAC,UAAU,CAAC,CAAC;IACtD,KAAK,QAAQ;MACX,OAAO,IAAAD,4BAAqB,EAAC,IAAAC,iBAAU,EAAC,QAAQ,CAAC,CAAC;IACpD,KAAK,QAAQ;MACX,OAAO,IAAAD,4BAAqB,EAAC,IAAAC,iBAAU,EAAC,QAAQ,CAAC,CAAC;IACpD,KAAK,QAAQ;MAGX,OAAO,IAAAC,wBAAiB,EAAC,CAAC;EAC9B;EACA,MAAM,IAAIC,KAAK,CAAC,wBAAwB,GAAGR,IAAI,CAAC;AAClD"}
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
@@ -0,0 +1,12 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.buildUndefinedNode = buildUndefinedNode;
var _index = require("./generated/index.js");
function buildUndefinedNode() {
return (0, _index.unaryExpression)("void", (0, _index.numericLiteral)(0), true);
}
//# sourceMappingURL=productions.js.map
@@ -0,0 +1 @@
{"version":3,"names":["_index","require","buildUndefinedNode","unaryExpression","numericLiteral"],"sources":["../../src/builders/productions.ts"],"sourcesContent":["import { numericLiteral, unaryExpression } from \"./generated/index.ts\";\n\nexport function buildUndefinedNode() {\n return unaryExpression(\"void\", numericLiteral(0), true);\n}\n"],"mappings":";;;;;;AAAA,IAAAA,MAAA,GAAAC,OAAA;AAEO,SAASC,kBAAkBA,CAAA,EAAG;EACnC,OAAO,IAAAC,sBAAe,EAAC,MAAM,EAAE,IAAAC,qBAAc,EAAC,CAAC,CAAC,EAAE,IAAI,CAAC;AACzD"}
@@ -0,0 +1,24 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = buildChildren;
var _index = require("../../validators/generated/index.js");
var _cleanJSXElementLiteralChild = require("../../utils/react/cleanJSXElementLiteralChild.js");
function buildChildren(node) {
const elements = [];
for (let i = 0; i < node.children.length; i++) {
let child = node.children[i];
if ((0, _index.isJSXText)(child)) {
(0, _cleanJSXElementLiteralChild.default)(child, elements);
continue;
}
if ((0, _index.isJSXExpressionContainer)(child)) child = child.expression;
if ((0, _index.isJSXEmptyExpression)(child)) continue;
elements.push(child);
}
return elements;
}
//# sourceMappingURL=buildChildren.js.map
@@ -0,0 +1 @@
{"version":3,"names":["_index","require","_cleanJSXElementLiteralChild","buildChildren","node","elements","i","children","length","child","isJSXText","cleanJSXElementLiteralChild","isJSXExpressionContainer","expression","isJSXEmptyExpression","push"],"sources":["../../../src/builders/react/buildChildren.ts"],"sourcesContent":["import {\n isJSXText,\n isJSXExpressionContainer,\n isJSXEmptyExpression,\n} from \"../../validators/generated/index.ts\";\nimport cleanJSXElementLiteralChild from \"../../utils/react/cleanJSXElementLiteralChild.ts\";\nimport type * as t from \"../../index.ts\";\n\ntype ReturnedChild =\n | t.JSXSpreadChild\n | t.JSXElement\n | t.JSXFragment\n | t.Expression;\n\nexport default function buildChildren(\n node: t.JSXElement | t.JSXFragment,\n): ReturnedChild[] {\n const elements = [];\n\n for (let i = 0; i < node.children.length; i++) {\n let child: any = node.children[i];\n\n if (isJSXText(child)) {\n cleanJSXElementLiteralChild(child, elements);\n continue;\n }\n\n if (isJSXExpressionContainer(child)) child = child.expression;\n if (isJSXEmptyExpression(child)) continue;\n\n elements.push(child);\n }\n\n return elements;\n}\n"],"mappings":";;;;;;AAAA,IAAAA,MAAA,GAAAC,OAAA;AAKA,IAAAC,4BAAA,GAAAD,OAAA;AASe,SAASE,aAAaA,CACnCC,IAAkC,EACjB;EACjB,MAAMC,QAAQ,GAAG,EAAE;EAEnB,KAAK,IAAIC,CAAC,GAAG,CAAC,EAAEA,CAAC,GAAGF,IAAI,CAACG,QAAQ,CAACC,MAAM,EAAEF,CAAC,EAAE,EAAE;IAC7C,IAAIG,KAAU,GAAGL,IAAI,CAACG,QAAQ,CAACD,CAAC,CAAC;IAEjC,IAAI,IAAAI,gBAAS,EAACD,KAAK,CAAC,EAAE;MACpB,IAAAE,oCAA2B,EAACF,KAAK,EAAEJ,QAAQ,CAAC;MAC5C;IACF;IAEA,IAAI,IAAAO,+BAAwB,EAACH,KAAK,CAAC,EAAEA,KAAK,GAAGA,KAAK,CAACI,UAAU;IAC7D,IAAI,IAAAC,2BAAoB,EAACL,KAAK,CAAC,EAAE;IAEjCJ,QAAQ,CAACU,IAAI,CAACN,KAAK,CAAC;EACtB;EAEA,OAAOJ,QAAQ;AACjB"}
@@ -0,0 +1,22 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = createTSUnionType;
var _index = require("../generated/index.js");
var _removeTypeDuplicates = require("../../modifications/typescript/removeTypeDuplicates.js");
var _index2 = require("../../validators/generated/index.js");
function createTSUnionType(typeAnnotations) {
const types = typeAnnotations.map(type => {
return (0, _index2.isTSTypeAnnotation)(type) ? type.typeAnnotation : type;
});
const flattened = (0, _removeTypeDuplicates.default)(types);
if (flattened.length === 1) {
return flattened[0];
} else {
return (0, _index.tsUnionType)(flattened);
}
}
//# sourceMappingURL=createTSUnionType.js.map
@@ -0,0 +1 @@
{"version":3,"names":["_index","require","_removeTypeDuplicates","_index2","createTSUnionType","typeAnnotations","types","map","type","isTSTypeAnnotation","typeAnnotation","flattened","removeTypeDuplicates","length","tsUnionType"],"sources":["../../../src/builders/typescript/createTSUnionType.ts"],"sourcesContent":["import { tsUnionType } from \"../generated/index.ts\";\nimport removeTypeDuplicates from \"../../modifications/typescript/removeTypeDuplicates.ts\";\nimport { isTSTypeAnnotation } from \"../../validators/generated/index.ts\";\nimport type * as t from \"../../index.ts\";\n\n/**\n * Takes an array of `types` and flattens them, removing duplicates and\n * returns a `UnionTypeAnnotation` node containing them.\n */\nexport default function createTSUnionType(\n typeAnnotations: Array<t.TSTypeAnnotation | t.TSType>,\n): t.TSType {\n const types = typeAnnotations.map(type => {\n return isTSTypeAnnotation(type) ? type.typeAnnotation : type;\n });\n const flattened = removeTypeDuplicates(types);\n\n if (flattened.length === 1) {\n return flattened[0];\n } else {\n return tsUnionType(flattened);\n }\n}\n"],"mappings":";;;;;;AAAA,IAAAA,MAAA,GAAAC,OAAA;AACA,IAAAC,qBAAA,GAAAD,OAAA;AACA,IAAAE,OAAA,GAAAF,OAAA;AAOe,SAASG,iBAAiBA,CACvCC,eAAqD,EAC3C;EACV,MAAMC,KAAK,GAAGD,eAAe,CAACE,GAAG,CAACC,IAAI,IAAI;IACxC,OAAO,IAAAC,0BAAkB,EAACD,IAAI,CAAC,GAAGA,IAAI,CAACE,cAAc,GAAGF,IAAI;EAC9D,CAAC,CAAC;EACF,MAAMG,SAAS,GAAG,IAAAC,6BAAoB,EAACN,KAAK,CAAC;EAE7C,IAAIK,SAAS,CAACE,MAAM,KAAK,CAAC,EAAE;IAC1B,OAAOF,SAAS,CAAC,CAAC,CAAC;EACrB,CAAC,MAAM;IACL,OAAO,IAAAG,kBAAW,EAACH,SAAS,CAAC;EAC/B;AACF"}
@@ -0,0 +1,17 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = validateNode;
var _validate = require("../validators/validate.js");
var _index = require("../index.js");
function validateNode(node) {
const keys = _index.BUILDER_KEYS[node.type];
for (const key of keys) {
(0, _validate.default)(node, key, node[key]);
}
return node;
}
//# sourceMappingURL=validateNode.js.map
@@ -0,0 +1 @@
{"version":3,"names":["_validate","require","_index","validateNode","node","keys","BUILDER_KEYS","type","key","validate"],"sources":["../../src/builders/validateNode.ts"],"sourcesContent":["import validate from \"../validators/validate.ts\";\nimport type * as t from \"../index.ts\";\nimport { BUILDER_KEYS } from \"../index.ts\";\n\nexport default function validateNode<N extends t.Node>(node: N) {\n // todo: because keys not in BUILDER_KEYS are not validated - this actually allows invalid nodes in some cases\n const keys = BUILDER_KEYS[node.type] as (keyof N & string)[];\n for (const key of keys) {\n validate(node, key, node[key]);\n }\n return node;\n}\n"],"mappings":";;;;;;AAAA,IAAAA,SAAA,GAAAC,OAAA;AAEA,IAAAC,MAAA,GAAAD,OAAA;AAEe,SAASE,YAAYA,CAAmBC,IAAO,EAAE;EAE9D,MAAMC,IAAI,GAAGC,mBAAY,CAACF,IAAI,CAACG,IAAI,CAAyB;EAC5D,KAAK,MAAMC,GAAG,IAAIH,IAAI,EAAE;IACtB,IAAAI,iBAAQ,EAACL,IAAI,EAAEI,GAAG,EAAEJ,IAAI,CAACI,GAAG,CAAC,CAAC;EAChC;EACA,OAAOJ,IAAI;AACb"}
+12
View File
@@ -0,0 +1,12 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = clone;
var _cloneNode = require("./cloneNode.js");
function clone(node) {
return (0, _cloneNode.default)(node, false);
}
//# sourceMappingURL=clone.js.map

Some files were not shown because too many files have changed in this diff Show More