├── .envrc ├── CODEOWNERS ├── .github ├── FUNDING.yml └── workflows │ ├── test-nix.yml │ ├── no-master.yml │ ├── build.yml │ └── test-upload.yml ├── tests ├── dir │ └── hello │ │ └── file.txt └── action.test.ts ├── dist ├── package.json └── sourcemap-register.cjs ├── .gitignore ├── jest.config.ts ├── renovate.json ├── SECURITY.md ├── shell.nix ├── CONTRIBUTING.md ├── tsconfig.json ├── src ├── types.ts ├── utils.ts └── index.ts ├── LICENSE ├── package.json ├── action.yml └── README.md /.envrc: -------------------------------------------------------------------------------- 1 | use nix 2 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @ryand56 -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: ryand56 -------------------------------------------------------------------------------- /tests/dir/hello/file.txt: -------------------------------------------------------------------------------- 1 | Hello world! -------------------------------------------------------------------------------- /dist/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "module" 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **/node_modules 2 | **/.env 3 | 4 | # NixOS 5 | **/.direnv 6 | **/.envrc 7 | 8 | .github/workflows/test-upload-local.yml -------------------------------------------------------------------------------- /jest.config.ts: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: 'ts-jest', 3 | testEnvironment: 'node', 4 | //moduleFileExtensions: ['ts', 'd.ts', 'js', 'node'] 5 | }; -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "config:recommended" 5 | ], 6 | "baseBranches": ["develop"] 7 | } 8 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Reporting a Vulnerability 4 | 5 | If you have believe to have found a vulnerability in my action or related packages, please contact me immediately by emailing `security@ryand.ca` or messaging my [Matrix](https://matrix.to/#/@ryand56:matrix.ryand.ca) to disclose a potential security vulnerability. If it is a package that is related, that package will be updated immediately (if there is an update) and a new release will be pushed. -------------------------------------------------------------------------------- /.github/workflows/test-nix.yml: -------------------------------------------------------------------------------- 1 | name: Action CI / Nix 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - '*' 7 | 8 | jobs: 9 | nix-shell-build: 10 | name: Build Nix Shell 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - name: Checkout 15 | uses: actions/checkout@v4 16 | - name: Install Nix 17 | uses: DeterminateSystems/nix-installer-action@v15 18 | - name: Magic Nix Cache 19 | uses: DeterminateSystems/magic-nix-cache-action@v8 20 | - name: Build 21 | run: nix-build shell.nix 22 | -------------------------------------------------------------------------------- /shell.nix: -------------------------------------------------------------------------------- 1 | { 2 | system ? builtins.currentSystem, 3 | }: 4 | let 5 | nixpkgs = fetchTarball { 6 | url = "https://github.com/NixOS/nixpkgs/archive/b5b2fecd0cadd82ef107c9583018f381ae70f222.tar.gz"; # Pinned from nixpkgs 7 | sha256 = "0bj1mjz2m4m5ns7c0cxxvraw0rc84cd172pv6vyqrgiw7ld339lk"; 8 | }; 9 | 10 | pkgs = import nixpkgs { 11 | inherit system; 12 | config = { }; 13 | overlays = [ ]; 14 | }; 15 | in 16 | pkgs.mkShellNoCC { 17 | packages = with pkgs; [ 18 | # Format using nixfmt 19 | nixfmt-rfc-style 20 | 21 | node2nix 22 | nodejs 23 | nodePackages.pnpm 24 | yarn 25 | ]; 26 | } 27 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | Any contributions you make are **greatly appreciated**. 3 | 4 | If you have an enhancement to add and/or bugfixes to patch, please fork the repository and create a pull request. You can also simply open an issue with the tag "enhancement". If you have found a bug, you can simply open an issue with the tag "bug". 5 | Don't forget to give this project a star. Thanks! 6 | 7 | 1. Fork the project 8 | 2. Create your feature branch (`git checkout -b patch-1`) 9 | 3. Commit your changes to the feature branch (`git commit -m 'Add some code'`) 10 | 4. Push to the feature branch (`git push origin patch-1`) 11 | 5. Open a PR -------------------------------------------------------------------------------- /.github/workflows/no-master.yml: -------------------------------------------------------------------------------- 1 | name: Action CI / Branch 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - master 7 | 8 | permissions: 9 | contents: read 10 | 11 | jobs: 12 | fail: 13 | if: github.repository_owner != 'ryand56' || github.head_ref != 'develop' 14 | permissions: 15 | contents: none 16 | name: "Targeting master" 17 | runs-on: ubuntu-latest 18 | steps: 19 | - run: | 20 | cat < { 23 | output: T 24 | url: string 25 | } 26 | 27 | export type UploadHandler = (file: string, fileName: string, config: R2Config, retries?: number, retryTimeout?: number) => Promise> 28 | -------------------------------------------------------------------------------- /tests/action.test.ts: -------------------------------------------------------------------------------- 1 | import { RunOptions, RunTarget } from "github-action-ts-run-api"; 2 | import * as dotenv from "dotenv"; 3 | 4 | dotenv.config(); 5 | 6 | describe("r2-upload-action", () => { 7 | const target = process.env.CI 8 | ? RunTarget.mainJs("action.yml") 9 | : RunTarget.jsFile("dist/index.js", "action.yml"); 10 | 11 | it("Upload test directory to root of R2", async () => { 12 | const options = RunOptions.create({ 13 | inputs: { 14 | "r2-account-id": process.env.TEST_R2_ACCOUNT_ID, 15 | "r2-access-key-id": process.env.TEST_R2_AKID, 16 | "r2-secret-access-key": process.env.TEST_R2_SECRET_AK, 17 | "r2-bucket": process.env.TEST_R2_BUCKET, 18 | "source-dir": "tests/dir/", 19 | "destination-dir": "./" 20 | } 21 | }); 22 | const res = await target.run(options); 23 | 24 | expect(res.isSuccess).toEqual(true); 25 | }); 26 | }); -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Action CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | 8 | jobs: 9 | build: 10 | name: Build and Update Mappings 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Checkout 14 | uses: actions/checkout@v4 15 | with: 16 | token: ${{ secrets.PAT }} 17 | - name: Setup pnpm 18 | uses: pnpm/action-setup@v4 19 | with: 20 | version: latest 21 | - name: Install Dependencies 22 | run: pnpm i --frozen-lockfile 23 | - name: Build 24 | run: pnpm run build 25 | - name: Update Mappings 26 | uses: stefanzweifel/git-auto-commit-action@v5 27 | with: 28 | commit_message: '[skip ci] Update mappings' 29 | file_pattern: 'dist/*.js dist/*.js.map dist/*.cjs' 30 | commit_user_name: r2-action-bot[bot] 31 | commit_user_email: 118486773+r2-action-bot[bot]@users.noreply.github.com 32 | commit_author: r2-action-bot[bot] <118486773+r2-action-bot[bot]@users.noreply.github.com> 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022-2024 Ryan Omasta 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "r2-upload-action", 3 | "version": "1.4", 4 | "description": "GitHub Action to upload files to a Cloudflare R2 bucket", 5 | "main": "dist/index.js", 6 | "type": "module", 7 | "scripts": { 8 | "build": "ncc build src/index.ts -s -o dist", 9 | "test": "jest", 10 | "action": "node dist/index.js" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "git+https://github.com/ryand56/r2-upload-action.git" 15 | }, 16 | "author": "ryand56", 17 | "license": "(MIT or 0BSD)", 18 | "bugs": { 19 | "url": "https://github.com/ryand56/r2-upload-action/issues" 20 | }, 21 | "homepage": "https://github.com/ryand56/r2-upload-action#readme", 22 | "devDependencies": { 23 | "@types/dedent": "^0.7.0", 24 | "@types/jest": "^29.5.3", 25 | "@types/md5": "^2.3.2", 26 | "@types/node": "^22.0.0", 27 | "@vercel/ncc": "^0.38.1", 28 | "dotenv": "^16.3.1", 29 | "github-action-ts-run-api": "^3.0.4", 30 | "jest": "^29.6.3", 31 | "ts-jest": "^29.1.1", 32 | "ts-node": "^10.9.1", 33 | "typescript": "^5.2.2" 34 | }, 35 | "dependencies": { 36 | "@actions/core": "^1.10.1", 37 | "@aws-sdk/client-s3": "^3.395.0", 38 | "@aws-sdk/s3-request-presigner": "^3.395.0", 39 | "md5": "^2.3.0", 40 | "mime": "^4.0.0" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /action.yml: -------------------------------------------------------------------------------- 1 | name: 'R2 Upload Action' 2 | description: 'GitHub Action to upload files to a Cloudflare R2 bucket' 3 | author: 'ryand56' 4 | branding: 5 | icon: 'upload-cloud' 6 | color: 'white' 7 | inputs: 8 | r2-account-id: 9 | description: '' 10 | required: true 11 | r2-access-key-id: 12 | description: '' 13 | required: true 14 | r2-secret-access-key: 15 | description: '' 16 | required: true 17 | r2-bucket: 18 | description: '' 19 | required: true 20 | r2-jurisdiction: 21 | description: '' 22 | required: false 23 | source-dir: 24 | description: '' 25 | required: true 26 | destination-dir: 27 | description: '' 28 | required: false 29 | output-file-url: 30 | description: '' 31 | default: 'true' 32 | required: false 33 | multipart-size: 34 | description: 'use multipart upload when file size is greater than this value (MB)' 35 | default: '100' 36 | required: false 37 | max-retries: 38 | description: 'maximum number of retries' 39 | default: '5' 40 | required: false 41 | retry-timeout: 42 | description: 'time to wait between retries' 43 | default: '2000' 44 | required: false 45 | multipart-concurrent: 46 | description: 'whether to use concurrent while multipart upload' 47 | default: 'true' 48 | required: false 49 | keep-file-fresh: 50 | description: '' 51 | default: 'false' 52 | required: false 53 | custom-charset: 54 | description: '' 55 | required: false 56 | outputs: 57 | result: 58 | description: '`success` or `failure`' 59 | file-urls: 60 | description: '' 61 | runs: 62 | using: 'node20' 63 | main: 'dist/index.js' -------------------------------------------------------------------------------- /src/utils.ts: -------------------------------------------------------------------------------- 1 | import { FileMap } from "./types.js"; 2 | import fs from 'fs'; 3 | import path from 'path'; 4 | 5 | export const formatBytes = (bytes: number) => { 6 | const sizes = ["Bytes", "KB", "MB", "GB", "TB"] 7 | 8 | if (bytes == 0) { 9 | return "0 Bytes" 10 | } 11 | 12 | const i = Math.floor(Math.log(bytes) / Math.log(1024)) 13 | 14 | if (i == 0) { 15 | return bytes + " " + sizes[i] 16 | } 17 | 18 | return (bytes / Math.pow(1024, i)).toFixed(2) + " " + sizes[i] 19 | }; 20 | 21 | export const getFileList = (dir: string, oldDir?: string) => { 22 | let files: FileMap = {}; 23 | let dirSplit = dir.trim().split("\n"); 24 | 25 | for (const singleDir of dirSplit) { 26 | const trimmedDir = singleDir.trim(); 27 | 28 | if (fs.statSync(trimmedDir).isFile()) 29 | files[singleDir] = path.basename(trimmedDir); 30 | else 31 | { 32 | const items = fs.readdirSync(trimmedDir, { 33 | withFileTypes: true, 34 | }); 35 | 36 | for (const item of items) { 37 | const isDir = item.isDirectory(); 38 | const absolutePath = path.join(trimmedDir, item.name); 39 | if (isDir) { 40 | files = {...files, ...getFileList(absolutePath, oldDir || trimmedDir)}; 41 | } else { 42 | files[absolutePath] = path.relative(oldDir || trimmedDir, absolutePath); 43 | } 44 | } 45 | } 46 | } 47 | 48 | return files; 49 | }; 50 | 51 | export const getFileSizeMB = (file: string) => { 52 | return fs.statSync(file).size / (1024 * 1024); 53 | } 54 | 55 | export const formatFileSize = (file: string) => { 56 | return formatBytes(fs.statSync(file).size); 57 | } 58 | 59 | export const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms)); 60 | 61 | // In R2, fixed size for each chunk is required. 62 | // FYR: https://community.cloudflare.com/t/all-non-trailing-parts-must-have-the-same-length/552190 63 | export async function* readFixedChunkSize(file: string, chunkSize: number): AsyncIterable { 64 | const stream = fs.createReadStream(file); 65 | let buffer = Buffer.alloc(0); 66 | 67 | for await (const chunk of stream) { 68 | buffer = Buffer.concat([buffer, chunk]); 69 | 70 | while (buffer.length >= chunkSize) { 71 | yield buffer.subarray(0, chunkSize); 72 | buffer = buffer.subarray(chunkSize); 73 | } 74 | } 75 | 76 | if (buffer.length > 0) { 77 | yield buffer; 78 | } 79 | }; 80 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # R2 Upload Action 2 | GitHub Action to upload files to a Cloudflare R2 bucket, built on top of @aws-sdk/client-s3 in TypeScript. 3 |
4 | Combination of these two repos: [S3 Upload Action](https://github.com/hkusu/s3-upload-action) and [Cloudflare R2 Upload](https://github.com/Karbust/Cloudflare_R2_Upload). 5 | 6 | > [!IMPORTANT] 7 | > Node.js 20 is now required to run this action. 8 | 9 | 10 | ## Usage 11 | 12 | 13 | ### Basic Usage 14 | ```yaml 15 | - uses: ryand56/r2-upload-action@latest 16 | with: 17 | r2-account-id: ${{ secrets.R2_ACCOUNT_ID }} 18 | r2-access-key-id: ${{ secrets.R2_ACCESS_KEY_ID }} 19 | r2-secret-access-key: ${{ secrets.R2_SECRET_ACCESS_KEY }} 20 | r2-bucket: ${{ secrets.R2_BUCKET }} 21 | source-dir: src 22 | destination-dir: ./ 23 | ``` 24 | 25 | In this example, `source-dir` is stored in the root of the bucket. 26 | Change `destination-dir` input to specify the location of where the directory will be uploaded. 27 | 28 | 29 | 30 | ### Custom Usage 31 | ```yaml 32 | - uses: ryand56/r2-upload-action@latest # Can be any release 33 | with: 34 | r2-account-id: ${{ secrets.R2_ACCOUNT_ID }} 35 | r2-access-key-id: ${{ secrets.R2_ACCESS_KEY_ID }} 36 | r2-secret-access-key: ${{ secrets.R2_SECRET_ACCESS_KEY }} 37 | r2-bucket: ${{ secrets.R2_BUCKET }} 38 | source-dir: src 39 | destination-dir: artifacts # Can be anything as long as it is an actual path 40 | output-file-url: true # defaults to true 41 | multipart-size: 100 # If the file size is greater than the value provided here, then use multipart upload 42 | max-retries: 5 # The maximum number of retries it takes to upload a multipart chunk until it moves on to the next part 43 | multipart-concurrent: true # Whether to concurrently upload a multipart chunk 44 | keep-file-fresh: false # defaults to false 45 | ``` 46 | See the latest [action.yml](https://github.com/ryand56/r2-upload-action/blob/master/action.yml) for every input and output or take a look below. 47 | 48 | 49 | 50 | ## Inputs 51 | 52 | | Name | Description | Default | 53 | | --- | --- | --- | 54 | | `r2-account-id` | **(Required)** Your Cloudflare account ID. | *N/A* | 55 | | `r2-access-key-id` | **(Required)** Your Cloudflare R2 bucket access key ID. | *N/A* | 56 | | `r2-secret-access-key` | **(Required)** Your Cloudflare R2 bucket secret access key. | *N/A* | 57 | | `r2-bucket` | **(Required)** Your Cloudflare R2 bucket name. | *N/A* | 58 | | `source-dir` | **(Required)** The directory to upload to the Cloudflare R2 bucket. | *N/A* | 59 | | `destination-dir` | (Optional) The destination to upload the directory to in the Cloudflare R2 bucket. | Empty string | 60 | | `output-file-url` | (Optional) Output the results of the action uploaded files to the `file-urls` output | true | 61 | | `multipart-size` | (Optional) The minimum file size to use multipart file upload | 100 (in MB) | 62 | | `max-retries` | (Optional) The maximum number of retries before failing | 5 | 63 | | `multipart-concurrent` | (Optional) Use multipart concurrent file uploading | true | 64 | | `keep-file-fresh` | (Optional) Keep the destination up-to-date, **which will permanently delete the previous contents** | false | 65 | 66 | 67 | ## Outputs 68 | 69 | | Name | Description | 70 | | --- | --- | 71 | | `result` | Result of this action. Either `success` or `failure` is set. | 72 | | `file-urls` | The URLs of the uploaded files in the directory. | 73 | 74 | 75 | ## Contributing 76 | 77 | See the [contributing guide](https://github.com/ryand56/r2-upload-action/blob/master/CONTRIBUTING.md) for more detail on how to implement something into the project. -------------------------------------------------------------------------------- /.github/workflows/test-upload.yml: -------------------------------------------------------------------------------- 1 | name: Action CI 2 | 3 | on: 4 | pull_request_target: 5 | branches: 6 | - '*' 7 | 8 | jobs: 9 | build: 10 | name: Build 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - name: Checkout 15 | uses: actions/checkout@v4 16 | - name: Get current date 17 | id: date 18 | run: echo "::set-output name=date::$(date +'%Y-%m-%dT%H:%M:%S')" 19 | - name: Setup pnpm 20 | uses: pnpm/action-setup@v4 21 | with: 22 | version: latest 23 | run_install: true 24 | - name: Setup Node.js ${{ matrix.node-version }} 25 | uses: actions/setup-node@v4 26 | with: 27 | node-version: ${{ matrix.node-version }} 28 | cache: "pnpm" 29 | - name: Install Dependencies 30 | run: pnpm i --frozen-lockfile 31 | - name: Build 32 | run: pnpm run build 33 | - name: Upload Artifact 34 | uses: actions/upload-artifact@v4 35 | with: 36 | name: r2-upload-action 37 | path: dist/ 38 | 39 | upload: 40 | name: R2 Test / Upload 41 | needs: build 42 | 43 | strategy: 44 | matrix: 45 | os: ["ubuntu-latest", "macos-latest"] 46 | node-version: ["lts/*"] 47 | 48 | runs-on: ${{ matrix.os }} 49 | 50 | steps: 51 | - name: Checkout 52 | uses: actions/checkout@v4 53 | - name: Download Artifact 54 | uses: actions/download-artifact@v4 55 | with: 56 | name: r2-upload-action 57 | path: dist/ 58 | - name: Get current date 59 | id: date 60 | run: echo "::set-output name=date::$(date +'%Y-%m-%dT%H:%M:%S')" 61 | - name: Create test file 62 | run: | 63 | mkdir tests/log 64 | echo Upload test - ${{ steps.date.outputs.date }} > "tests/log/upload-${{ steps.date.outputs.date }}" 65 | - name: Upload to R2 66 | uses: ./ 67 | with: 68 | r2-account-id: ${{ secrets.TEST_R2_ACCOUNT_ID }} 69 | r2-access-key-id: ${{ secrets.TEST_R2_AKID }} 70 | r2-secret-access-key: ${{ secrets.TEST_R2_SECRET_AK }} 71 | r2-bucket: ${{ secrets.TEST_R2_BUCKET }} 72 | source-dir: tests/log 73 | destination-dir: r2-upload-action/test-logs 74 | keep-file-fresh: true 75 | 76 | upload-multiple: 77 | name: R2 Test / Upload Multiple 78 | needs: upload 79 | 80 | strategy: 81 | matrix: 82 | os: ["ubuntu-latest", "macos-latest"] 83 | node-version: ["lts/*"] 84 | 85 | runs-on: ${{ matrix.os }} 86 | 87 | steps: 88 | - name: Checkout 89 | uses: actions/checkout@v4 90 | - name: Download Artifact 91 | uses: actions/download-artifact@v4 92 | with: 93 | name: r2-upload-action 94 | path: dist/ 95 | - name: Get current date 96 | id: date 97 | run: echo "::set-output name=date::$(date +'%Y-%m-%dT%H:%M:%S')" 98 | - name: Create test file 99 | run: | 100 | mkdir tests/log 101 | echo Upload test - ${{ steps.date.outputs.date }} - File 1 > "tests/log/upload-${{ steps.date.outputs.date }}-file1" 102 | echo Upload test - ${{ steps.date.outputs.date }} - File 2 > "tests/log/upload-${{ steps.date.outputs.date }}-file2" 103 | mkdir tests/log2 104 | echo Upload test - ${{ steps.date.outputs.date }} - File 3 > "tests/log2/upload-${{ steps.date.outputs.date }}-file3" 105 | echo Upload test - ${{ steps.date.outputs.date }} - File 4 > "tests/log2/upload-${{ steps.date.outputs.date }}-file4" 106 | - name: Upload to R2 107 | uses: ./ 108 | with: 109 | r2-account-id: ${{ secrets.TEST_R2_ACCOUNT_ID }} 110 | r2-access-key-id: ${{ secrets.TEST_R2_AKID }} 111 | r2-secret-access-key: ${{ secrets.TEST_R2_SECRET_AK }} 112 | r2-bucket: ${{ secrets.TEST_R2_BUCKET }} 113 | source-dir: | 114 | tests/log/upload-${{ steps.date.outputs.date }}-file1 115 | tests/log/upload-${{ steps.date.outputs.date }}-file2 116 | tests/log2 117 | destination-dir: r2-upload-action/test-logs 118 | keep-file-fresh: true -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import { R2Config, FileMap, UploadHandler } from "./types.js"; 2 | import { getInput, setOutput, setFailed } from "@actions/core"; 3 | import { 4 | S3Client, 5 | PutObjectCommandInput, 6 | PutObjectCommand, 7 | PutObjectCommandOutput, 8 | S3ServiceException, 9 | CompleteMultipartUploadCommand, 10 | CompleteMultipartUploadCommandInput, 11 | UploadPartCommandInput, 12 | AbortMultipartUploadCommand, 13 | AbortMultipartUploadCommandInput, 14 | CreateMultipartUploadCommand, 15 | CreateMultipartUploadCommandInput, 16 | UploadPartCommand, 17 | CompleteMultipartUploadCommandOutput, 18 | ListObjectsV2Command, 19 | DeleteObjectsCommand, 20 | DeleteObjectsCommandInput 21 | } from "@aws-sdk/client-s3"; 22 | import { getSignedUrl } from "@aws-sdk/s3-request-presigner"; 23 | import * as fs from "fs"; 24 | import mime from "mime"; 25 | import md5 from "md5"; 26 | import path from "path"; 27 | import { formatBytes, formatFileSize, getFileList, getFileSizeMB, readFixedChunkSize, sleep } from "./utils.js"; 28 | 29 | let config: R2Config = { 30 | accountId: getInput("r2-account-id", { required: true }), 31 | accessKeyId: getInput("r2-access-key-id", { required: true }), 32 | secretAccessKey: getInput("r2-secret-access-key", { required: true }), 33 | bucket: getInput("r2-bucket", { required: true }), 34 | jurisdiction: getInput("r2-jurisdiction"), 35 | sourceDir: getInput("source-dir", { required: true }), 36 | destinationDir: getInput("destination-dir"), 37 | outputFileUrl: getInput("output-file-url") === 'true', 38 | multiPartSize: parseInt(getInput("multipart-size")) || 100, 39 | maxTries: parseInt(getInput("max-retries")) || 5, 40 | retryTimeout: parseInt(getInput("retry-timeout")) || 2000, 41 | multiPartConcurrent: getInput("multipart-concurrent") === 'true', 42 | keepFileFresh: getInput("keep-file-fresh") === 'true', 43 | customCharset: getInput("custom-charset") 44 | }; 45 | 46 | const S3 = new S3Client({ 47 | region: "auto", 48 | endpoint: `https://${config.accountId}.${config.jurisdiction != "" ? config.jurisdiction + "." : ""}r2.cloudflarestorage.com`, 49 | credentials: { 50 | accessKeyId: config.accessKeyId, 51 | secretAccessKey: config.secretAccessKey, 52 | }, 53 | }); 54 | 55 | const deleteRemoteFiles = async (bucket: string, prefix: string) => { 56 | try { 57 | const listParams = { 58 | Bucket: bucket, 59 | Prefix: prefix 60 | }; 61 | 62 | const listedObjects = await S3.send(new ListObjectsV2Command(listParams)); 63 | 64 | if (listedObjects.Contents && listedObjects.Contents.length > 0) { 65 | const deleteParams: DeleteObjectsCommandInput = { 66 | Bucket: bucket, 67 | Delete: { 68 | Objects: listedObjects.Contents.map(({ Key }) => ({ Key })), 69 | Quiet: true 70 | } 71 | }; 72 | 73 | await S3.send(new DeleteObjectsCommand(deleteParams)); 74 | console.log(`Deleted all objects under ${prefix}`); 75 | } 76 | } catch (err) { 77 | console.error("Error deleting remote files: ", err); 78 | throw err; 79 | } 80 | }; 81 | 82 | const run = async (config: R2Config) => { 83 | const map = new Map(); 84 | const urls: FileMap = {}; 85 | 86 | if (config.keepFileFresh) { 87 | const remotePrefix = config.destinationDir !== "" ? config.destinationDir : config.sourceDir; 88 | await deleteRemoteFiles(config.bucket, remotePrefix); 89 | } 90 | 91 | const files: FileMap = getFileList(config.sourceDir); 92 | 93 | for (const file in files) { 94 | console.log(config.sourceDir); 95 | console.log(config.destinationDir); 96 | //const fileName = file.replace(config.sourceDir, ""); 97 | const fileName = files[file]; 98 | // const fileKey = path.join(config.destinationDir !== "" ? config.destinationDir : config.sourceDir, fileName); 99 | 100 | const destinationDir = config.destinationDir.split(path.sep).join('/'); 101 | const fileKey = path.posix.join(destinationDir !== "" ? destinationDir : config.sourceDir.split(path.sep).join('/'), fileName.split(path.sep).join('/')); 102 | 103 | if (fileName.includes('.gitkeep')) 104 | continue; 105 | 106 | console.log(fileKey); 107 | 108 | try { 109 | const fileMB = getFileSizeMB(file); 110 | console.info(`R2 Info - Uploading ${file} (${formatFileSize(file)}) to ${fileKey}`); 111 | const upload = fileMB > config.multiPartSize ? uploadMultiPart : putObject; 112 | const result = await upload(file, fileKey, config, config.maxTries, config.retryTimeout); 113 | map.set(file, result.output); 114 | urls[file] = result.url; 115 | } catch (err: unknown) { 116 | const error = err as S3ServiceException; 117 | if (error.hasOwnProperty("$metadata")) { 118 | if (error.$metadata.httpStatusCode !== 412) // If-None-Match 119 | throw error; 120 | } else { 121 | console.error(`Error while uploading ${file} to ${fileKey}: `, err); 122 | throw error; 123 | } 124 | } 125 | } 126 | 127 | if (config.outputFileUrl) setOutput('file-urls', urls); 128 | return map; 129 | }; 130 | 131 | const uploadMultiPart: UploadHandler = async (file: string, fileName: string, config: R2Config, retries: number = 5, retryTimeout: number = 2000) => { 132 | const mimeType = mime.getType(file); 133 | 134 | const createMultiPartParams: CreateMultipartUploadCommandInput = { 135 | Bucket: config.bucket, 136 | Key: fileName, 137 | ContentType: mimeType ?? 'application/octet-stream' 138 | } 139 | 140 | const cmd = new CreateMultipartUploadCommand(createMultiPartParams); 141 | 142 | const created = await S3.send(cmd); 143 | 144 | const chunkSize = 10 * 1024 * 1024; // 10MB 145 | 146 | const multiPartMap = { 147 | Parts: [] 148 | } 149 | 150 | const totalSize = formatFileSize(file); 151 | let bytesRead = 0; 152 | let partNumber = 0; 153 | let interrupted = false; 154 | const uploads = []; 155 | for await (const chunk of readFixedChunkSize(file, chunkSize)) { 156 | 157 | const uploadPartParams: UploadPartCommandInput = { 158 | Bucket: config.bucket, 159 | Key: fileName, 160 | PartNumber: ++partNumber, 161 | UploadId: created.UploadId, 162 | Body: chunk, 163 | } 164 | 165 | const uploadPart = async (partNumber: number) => { 166 | const cmd = new UploadPartCommand(uploadPartParams); 167 | let attempts = 0 168 | while (attempts < retries) { 169 | if (interrupted) { 170 | console.info(`R2 Info - Aborting upload part ${partNumber} of ${file} due to previous error`) 171 | return; 172 | } 173 | try { 174 | const result = await S3.send(cmd); 175 | multiPartMap.Parts.push({ PartNumber: partNumber, ETag: result.ETag }); 176 | break; 177 | } catch (err: any) { 178 | retries++; 179 | console.error(`R2 Error - ${err.message}, retrying: ${retries}/${config.maxTries}`, err); 180 | await sleep(300); 181 | } 182 | } 183 | if (attempts >= retries) { 184 | console.info(`Retries exhausted, aborting upload`) 185 | interrupted = true; 186 | const abortParams: AbortMultipartUploadCommandInput = { 187 | Bucket: config.bucket, 188 | Key: fileName, 189 | UploadId: created.UploadId 190 | } 191 | const cmd = new AbortMultipartUploadCommand(abortParams); 192 | await S3.send(cmd); 193 | throw new Error(`R2 Error - Failed to upload part ${partNumber} of ${file}`); 194 | } 195 | bytesRead += chunk.byteLength; 196 | console.info(`R2 Success - Uploaded part ${formatBytes(bytesRead)}/${totalSize} of ${file} (${partNumber})`) 197 | } 198 | 199 | if (config.multiPartConcurrent) { 200 | uploads.push(uploadPart(partNumber)); 201 | } else { 202 | await uploadPart(partNumber); 203 | } 204 | } 205 | 206 | if (config.multiPartConcurrent) { 207 | await Promise.all(uploads); 208 | } 209 | 210 | console.info(`R2 Info - Completing upload of ${file} to ${fileName}`) 211 | 212 | const completeMultiPartUploadParams: CompleteMultipartUploadCommandInput = { 213 | Bucket: config.bucket, 214 | Key: fileName, 215 | UploadId: created.UploadId, 216 | MultipartUpload: multiPartMap 217 | } 218 | 219 | const completeCmd = new CompleteMultipartUploadCommand(completeMultiPartUploadParams); 220 | 221 | let attempts = 0; 222 | while (attempts < retries) 223 | { 224 | try 225 | { 226 | console.log(`Attempting multipart upload (${attempts + 1}/${retries}) of ${file}`); 227 | 228 | const data = await S3.send(completeCmd); 229 | console.log(`R2 Success - ${file}`); 230 | const url = await getSignedUrl(S3, completeCmd); 231 | return { 232 | output: data, 233 | url 234 | }; 235 | } catch (err) { 236 | attempts++; 237 | console.error(`Attempt ${attempts} of ${retries} failed for ${file}, retrying...`); 238 | 239 | if (attempts >= retries) 240 | throw new Error(`Failed to upload multipart of ${file} after ${attempts} attempts for ${retries} retries`); 241 | 242 | await new Promise(resolve => setTimeout(resolve, retryTimeout)); 243 | } 244 | } 245 | }; 246 | 247 | const putObject: UploadHandler = async (file: string, fileName: string, config: R2Config, retries: number = 5, retryTimeout: number = 2000) => { 248 | const mimeType = mime.getType(file); 249 | 250 | console.info(`using put object upload for ${fileName}`); 251 | 252 | const fileStream = fs.readFileSync(file); 253 | 254 | let contentType = mimeType ?? 'application/octet-stream'; 255 | if (config.customCharset) 256 | contentType = `${contentType}; charset=${config.customCharset}`; 257 | 258 | const uploadParams: PutObjectCommandInput = { 259 | Bucket: config.bucket, 260 | Key: fileName, 261 | Body: fileStream, 262 | ContentLength: fs.statSync(file).size, 263 | ContentType: contentType 264 | }; 265 | const cmd = new PutObjectCommand(uploadParams); 266 | const digest = md5(fileStream); 267 | // cmd.middlewareStack.add((next: any) => async (args: any) => { 268 | // args.request.headers['if-none-match'] = `"${digest}"`; 269 | // return await next(args); 270 | // }, { 271 | // step: 'build', 272 | // name: 'addETag' 273 | // }); 274 | 275 | let attempts = 0; 276 | while (attempts < retries) 277 | { 278 | try 279 | { 280 | console.log(`Attempting upload (${attempts + 1}/${retries}) of ${file}`); 281 | 282 | const data = await S3.send(cmd); 283 | console.log(`R2 Success - ${file}`); 284 | const url = await getSignedUrl(S3, cmd); 285 | return { 286 | output: data, 287 | url 288 | }; 289 | } catch (err) { 290 | attempts++; 291 | console.error(`Attempt ${attempts} of ${retries} failed for ${file}, retrying...`); 292 | 293 | if (attempts >= retries) 294 | throw new Error(`Failed to upload ${file} after ${attempts} attempts for ${retries} retries`); 295 | 296 | await new Promise(resolve => setTimeout(resolve, retryTimeout)); 297 | } 298 | } 299 | }; 300 | 301 | run(config) 302 | .then(result => setOutput('result', 'success')) 303 | .catch(err => { 304 | if (err.hasOwnProperty('$metadata')) { 305 | console.error(`R2 Error - ${err.message}`); 306 | } else { 307 | console.error('Error', err); 308 | } 309 | 310 | setOutput('result', 'failure'); 311 | setFailed(err.message); 312 | }); 313 | -------------------------------------------------------------------------------- /dist/sourcemap-register.cjs: -------------------------------------------------------------------------------- 1 | (()=>{var e={296:e=>{var r=Object.prototype.toString;var n=typeof Buffer!=="undefined"&&typeof Buffer.alloc==="function"&&typeof Buffer.allocUnsafe==="function"&&typeof Buffer.from==="function";function isArrayBuffer(e){return r.call(e).slice(8,-1)==="ArrayBuffer"}function fromArrayBuffer(e,r,t){r>>>=0;var o=e.byteLength-r;if(o<0){throw new RangeError("'offset' is out of bounds")}if(t===undefined){t=o}else{t>>>=0;if(t>o){throw new RangeError("'length' is out of bounds")}}return n?Buffer.from(e.slice(r,r+t)):new Buffer(new Uint8Array(e.slice(r,r+t)))}function fromString(e,r){if(typeof r!=="string"||r===""){r="utf8"}if(!Buffer.isEncoding(r)){throw new TypeError('"encoding" must be a valid string encoding')}return n?Buffer.from(e,r):new Buffer(e,r)}function bufferFrom(e,r,t){if(typeof e==="number"){throw new TypeError('"value" argument must not be a number')}if(isArrayBuffer(e)){return fromArrayBuffer(e,r,t)}if(typeof e==="string"){return fromString(e,r)}return n?Buffer.from(e):new Buffer(e)}e.exports=bufferFrom},599:(e,r,n)=>{e=n.nmd(e);var t=n(927).SourceMapConsumer;var o=n(928);var i;try{i=n(896);if(!i.existsSync||!i.readFileSync){i=null}}catch(e){}var a=n(296);function dynamicRequire(e,r){return e.require(r)}var u=false;var s=false;var l=false;var c="auto";var p={};var f={};var g=/^data:application\/json[^,]+base64,/;var d=[];var h=[];function isInBrowser(){if(c==="browser")return true;if(c==="node")return false;return typeof window!=="undefined"&&typeof XMLHttpRequest==="function"&&!(window.require&&window.module&&window.process&&window.process.type==="renderer")}function hasGlobalProcessEventEmitter(){return typeof process==="object"&&process!==null&&typeof process.on==="function"}function globalProcessVersion(){if(typeof process==="object"&&process!==null){return process.version}else{return""}}function globalProcessStderr(){if(typeof process==="object"&&process!==null){return process.stderr}}function globalProcessExit(e){if(typeof process==="object"&&process!==null&&typeof process.exit==="function"){return process.exit(e)}}function handlerExec(e){return function(r){for(var n=0;n"}var n=this.getLineNumber();if(n!=null){r+=":"+n;var t=this.getColumnNumber();if(t){r+=":"+t}}}var o="";var i=this.getFunctionName();var a=true;var u=this.isConstructor();var s=!(this.isToplevel()||u);if(s){var l=this.getTypeName();if(l==="[object Object]"){l="null"}var c=this.getMethodName();if(i){if(l&&i.indexOf(l)!=0){o+=l+"."}o+=i;if(c&&i.indexOf("."+c)!=i.length-c.length-1){o+=" [as "+c+"]"}}else{o+=l+"."+(c||"")}}else if(u){o+="new "+(i||"")}else if(i){o+=i}else{o+=r;a=false}if(a){o+=" ("+r+")"}return o}function cloneCallSite(e){var r={};Object.getOwnPropertyNames(Object.getPrototypeOf(e)).forEach((function(n){r[n]=/^(?:is|get)/.test(n)?function(){return e[n].call(e)}:e[n]}));r.toString=CallSiteToString;return r}function wrapCallSite(e,r){if(r===undefined){r={nextPosition:null,curPosition:null}}if(e.isNative()){r.curPosition=null;return e}var n=e.getFileName()||e.getScriptNameOrSourceURL();if(n){var t=e.getLineNumber();var o=e.getColumnNumber()-1;var i=/^v(10\.1[6-9]|10\.[2-9][0-9]|10\.[0-9]{3,}|1[2-9]\d*|[2-9]\d|\d{3,}|11\.11)/;var a=i.test(globalProcessVersion())?0:62;if(t===1&&o>a&&!isInBrowser()&&!e.isEval()){o-=a}var u=mapSourcePosition({source:n,line:t,column:o});r.curPosition=u;e=cloneCallSite(e);var s=e.getFunctionName;e.getFunctionName=function(){if(r.nextPosition==null){return s()}return r.nextPosition.name||s()};e.getFileName=function(){return u.source};e.getLineNumber=function(){return u.line};e.getColumnNumber=function(){return u.column+1};e.getScriptNameOrSourceURL=function(){return u.source};return e}var l=e.isEval()&&e.getEvalOrigin();if(l){l=mapEvalOrigin(l);e=cloneCallSite(e);e.getEvalOrigin=function(){return l};return e}return e}function prepareStackTrace(e,r){if(l){p={};f={}}var n=e.name||"Error";var t=e.message||"";var o=n+": "+t;var i={nextPosition:null,curPosition:null};var a=[];for(var u=r.length-1;u>=0;u--){a.push("\n at "+wrapCallSite(r[u],i));i.nextPosition=i.curPosition}i.curPosition=i.nextPosition=null;return o+a.reverse().join("")}function getErrorSource(e){var r=/\n at [^(]+ \((.*):(\d+):(\d+)\)/.exec(e.stack);if(r){var n=r[1];var t=+r[2];var o=+r[3];var a=p[n];if(!a&&i&&i.existsSync(n)){try{a=i.readFileSync(n,"utf8")}catch(e){a=""}}if(a){var u=a.split(/(?:\r\n|\r|\n)/)[t-1];if(u){return n+":"+t+"\n"+u+"\n"+new Array(o).join(" ")+"^"}}}return null}function printErrorAndExit(e){var r=getErrorSource(e);var n=globalProcessStderr();if(n&&n._handle&&n._handle.setBlocking){n._handle.setBlocking(true)}if(r){console.error();console.error(r)}console.error(e.stack);globalProcessExit(1)}function shimEmitUncaughtException(){var e=process.emit;process.emit=function(r){if(r==="uncaughtException"){var n=arguments[1]&&arguments[1].stack;var t=this.listeners(r).length>0;if(n&&!t){return printErrorAndExit(arguments[1])}}return e.apply(this,arguments)}}var S=d.slice(0);var _=h.slice(0);r.wrapCallSite=wrapCallSite;r.getErrorSource=getErrorSource;r.mapSourcePosition=mapSourcePosition;r.retrieveSourceMap=v;r.install=function(r){r=r||{};if(r.environment){c=r.environment;if(["node","browser","auto"].indexOf(c)===-1){throw new Error("environment "+c+" was unknown. Available options are {auto, browser, node}")}}if(r.retrieveFile){if(r.overrideRetrieveFile){d.length=0}d.unshift(r.retrieveFile)}if(r.retrieveSourceMap){if(r.overrideRetrieveSourceMap){h.length=0}h.unshift(r.retrieveSourceMap)}if(r.hookRequire&&!isInBrowser()){var n=dynamicRequire(e,"module");var t=n.prototype._compile;if(!t.__sourceMapSupport){n.prototype._compile=function(e,r){p[r]=e;f[r]=undefined;return t.call(this,e,r)};n.prototype._compile.__sourceMapSupport=true}}if(!l){l="emptyCacheBetweenOperations"in r?r.emptyCacheBetweenOperations:false}if(!u){u=true;Error.prepareStackTrace=prepareStackTrace}if(!s){var o="handleUncaughtExceptions"in r?r.handleUncaughtExceptions:true;try{var i=dynamicRequire(e,"worker_threads");if(i.isMainThread===false){o=false}}catch(e){}if(o&&hasGlobalProcessEventEmitter()){s=true;shimEmitUncaughtException()}}};r.resetRetrieveHandlers=function(){d.length=0;h.length=0;d=S.slice(0);h=_.slice(0);v=handlerExec(h);m=handlerExec(d)}},517:(e,r,n)=>{var t=n(297);var o=Object.prototype.hasOwnProperty;var i=typeof Map!=="undefined";function ArraySet(){this._array=[];this._set=i?new Map:Object.create(null)}ArraySet.fromArray=function ArraySet_fromArray(e,r){var n=new ArraySet;for(var t=0,o=e.length;t=0){return r}}else{var n=t.toSetString(e);if(o.call(this._set,n)){return this._set[n]}}throw new Error('"'+e+'" is not in the set.')};ArraySet.prototype.at=function ArraySet_at(e){if(e>=0&&e{var t=n(158);var o=5;var i=1<>1;return r?-n:n}r.encode=function base64VLQ_encode(e){var r="";var n;var i=toVLQSigned(e);do{n=i&a;i>>>=o;if(i>0){n|=u}r+=t.encode(n)}while(i>0);return r};r.decode=function base64VLQ_decode(e,r,n){var i=e.length;var s=0;var l=0;var c,p;do{if(r>=i){throw new Error("Expected more digits in base 64 VLQ value.")}p=t.decode(e.charCodeAt(r++));if(p===-1){throw new Error("Invalid base64 digit: "+e.charAt(r-1))}c=!!(p&u);p&=a;s=s+(p<{var n="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".split("");r.encode=function(e){if(0<=e&&e{r.GREATEST_LOWER_BOUND=1;r.LEAST_UPPER_BOUND=2;function recursiveSearch(e,n,t,o,i,a){var u=Math.floor((n-e)/2)+e;var s=i(t,o[u],true);if(s===0){return u}else if(s>0){if(n-u>1){return recursiveSearch(u,n,t,o,i,a)}if(a==r.LEAST_UPPER_BOUND){return n1){return recursiveSearch(e,u,t,o,i,a)}if(a==r.LEAST_UPPER_BOUND){return u}else{return e<0?-1:e}}}r.search=function search(e,n,t,o){if(n.length===0){return-1}var i=recursiveSearch(-1,n.length,e,n,t,o||r.GREATEST_LOWER_BOUND);if(i<0){return-1}while(i-1>=0){if(t(n[i],n[i-1],true)!==0){break}--i}return i}},24:(e,r,n)=>{var t=n(297);function generatedPositionAfter(e,r){var n=e.generatedLine;var o=r.generatedLine;var i=e.generatedColumn;var a=r.generatedColumn;return o>n||o==n&&a>=i||t.compareByGeneratedPositionsInflated(e,r)<=0}function MappingList(){this._array=[];this._sorted=true;this._last={generatedLine:-1,generatedColumn:0}}MappingList.prototype.unsortedForEach=function MappingList_forEach(e,r){this._array.forEach(e,r)};MappingList.prototype.add=function MappingList_add(e){if(generatedPositionAfter(this._last,e)){this._last=e;this._array.push(e)}else{this._sorted=false;this._array.push(e)}};MappingList.prototype.toArray=function MappingList_toArray(){if(!this._sorted){this._array.sort(t.compareByGeneratedPositionsInflated);this._sorted=true}return this._array};r.P=MappingList},299:(e,r)=>{function swap(e,r,n){var t=e[r];e[r]=e[n];e[n]=t}function randomIntInRange(e,r){return Math.round(e+Math.random()*(r-e))}function doQuickSort(e,r,n,t){if(n{var t;var o=n(297);var i=n(197);var a=n(517).C;var u=n(818);var s=n(299).g;function SourceMapConsumer(e,r){var n=e;if(typeof e==="string"){n=o.parseSourceMapInput(e)}return n.sections!=null?new IndexedSourceMapConsumer(n,r):new BasicSourceMapConsumer(n,r)}SourceMapConsumer.fromSourceMap=function(e,r){return BasicSourceMapConsumer.fromSourceMap(e,r)};SourceMapConsumer.prototype._version=3;SourceMapConsumer.prototype.__generatedMappings=null;Object.defineProperty(SourceMapConsumer.prototype,"_generatedMappings",{configurable:true,enumerable:true,get:function(){if(!this.__generatedMappings){this._parseMappings(this._mappings,this.sourceRoot)}return this.__generatedMappings}});SourceMapConsumer.prototype.__originalMappings=null;Object.defineProperty(SourceMapConsumer.prototype,"_originalMappings",{configurable:true,enumerable:true,get:function(){if(!this.__originalMappings){this._parseMappings(this._mappings,this.sourceRoot)}return this.__originalMappings}});SourceMapConsumer.prototype._charIsMappingSeparator=function SourceMapConsumer_charIsMappingSeparator(e,r){var n=e.charAt(r);return n===";"||n===","};SourceMapConsumer.prototype._parseMappings=function SourceMapConsumer_parseMappings(e,r){throw new Error("Subclasses must implement _parseMappings")};SourceMapConsumer.GENERATED_ORDER=1;SourceMapConsumer.ORIGINAL_ORDER=2;SourceMapConsumer.GREATEST_LOWER_BOUND=1;SourceMapConsumer.LEAST_UPPER_BOUND=2;SourceMapConsumer.prototype.eachMapping=function SourceMapConsumer_eachMapping(e,r,n){var t=r||null;var i=n||SourceMapConsumer.GENERATED_ORDER;var a;switch(i){case SourceMapConsumer.GENERATED_ORDER:a=this._generatedMappings;break;case SourceMapConsumer.ORIGINAL_ORDER:a=this._originalMappings;break;default:throw new Error("Unknown order of iteration.")}var u=this.sourceRoot;a.map((function(e){var r=e.source===null?null:this._sources.at(e.source);r=o.computeSourceURL(u,r,this._sourceMapURL);return{source:r,generatedLine:e.generatedLine,generatedColumn:e.generatedColumn,originalLine:e.originalLine,originalColumn:e.originalColumn,name:e.name===null?null:this._names.at(e.name)}}),this).forEach(e,t)};SourceMapConsumer.prototype.allGeneratedPositionsFor=function SourceMapConsumer_allGeneratedPositionsFor(e){var r=o.getArg(e,"line");var n={source:o.getArg(e,"source"),originalLine:r,originalColumn:o.getArg(e,"column",0)};n.source=this._findSourceIndex(n.source);if(n.source<0){return[]}var t=[];var a=this._findMapping(n,this._originalMappings,"originalLine","originalColumn",o.compareByOriginalPositions,i.LEAST_UPPER_BOUND);if(a>=0){var u=this._originalMappings[a];if(e.column===undefined){var s=u.originalLine;while(u&&u.originalLine===s){t.push({line:o.getArg(u,"generatedLine",null),column:o.getArg(u,"generatedColumn",null),lastColumn:o.getArg(u,"lastGeneratedColumn",null)});u=this._originalMappings[++a]}}else{var l=u.originalColumn;while(u&&u.originalLine===r&&u.originalColumn==l){t.push({line:o.getArg(u,"generatedLine",null),column:o.getArg(u,"generatedColumn",null),lastColumn:o.getArg(u,"lastGeneratedColumn",null)});u=this._originalMappings[++a]}}}return t};r.SourceMapConsumer=SourceMapConsumer;function BasicSourceMapConsumer(e,r){var n=e;if(typeof e==="string"){n=o.parseSourceMapInput(e)}var t=o.getArg(n,"version");var i=o.getArg(n,"sources");var u=o.getArg(n,"names",[]);var s=o.getArg(n,"sourceRoot",null);var l=o.getArg(n,"sourcesContent",null);var c=o.getArg(n,"mappings");var p=o.getArg(n,"file",null);if(t!=this._version){throw new Error("Unsupported version: "+t)}if(s){s=o.normalize(s)}i=i.map(String).map(o.normalize).map((function(e){return s&&o.isAbsolute(s)&&o.isAbsolute(e)?o.relative(s,e):e}));this._names=a.fromArray(u.map(String),true);this._sources=a.fromArray(i,true);this._absoluteSources=this._sources.toArray().map((function(e){return o.computeSourceURL(s,e,r)}));this.sourceRoot=s;this.sourcesContent=l;this._mappings=c;this._sourceMapURL=r;this.file=p}BasicSourceMapConsumer.prototype=Object.create(SourceMapConsumer.prototype);BasicSourceMapConsumer.prototype.consumer=SourceMapConsumer;BasicSourceMapConsumer.prototype._findSourceIndex=function(e){var r=e;if(this.sourceRoot!=null){r=o.relative(this.sourceRoot,r)}if(this._sources.has(r)){return this._sources.indexOf(r)}var n;for(n=0;n1){v.source=l+_[1];l+=_[1];v.originalLine=i+_[2];i=v.originalLine;v.originalLine+=1;v.originalColumn=a+_[3];a=v.originalColumn;if(_.length>4){v.name=c+_[4];c+=_[4]}}m.push(v);if(typeof v.originalLine==="number"){h.push(v)}}}s(m,o.compareByGeneratedPositionsDeflated);this.__generatedMappings=m;s(h,o.compareByOriginalPositions);this.__originalMappings=h};BasicSourceMapConsumer.prototype._findMapping=function SourceMapConsumer_findMapping(e,r,n,t,o,a){if(e[n]<=0){throw new TypeError("Line must be greater than or equal to 1, got "+e[n])}if(e[t]<0){throw new TypeError("Column must be greater than or equal to 0, got "+e[t])}return i.search(e,r,o,a)};BasicSourceMapConsumer.prototype.computeColumnSpans=function SourceMapConsumer_computeColumnSpans(){for(var e=0;e=0){var t=this._generatedMappings[n];if(t.generatedLine===r.generatedLine){var i=o.getArg(t,"source",null);if(i!==null){i=this._sources.at(i);i=o.computeSourceURL(this.sourceRoot,i,this._sourceMapURL)}var a=o.getArg(t,"name",null);if(a!==null){a=this._names.at(a)}return{source:i,line:o.getArg(t,"originalLine",null),column:o.getArg(t,"originalColumn",null),name:a}}}return{source:null,line:null,column:null,name:null}};BasicSourceMapConsumer.prototype.hasContentsOfAllSources=function BasicSourceMapConsumer_hasContentsOfAllSources(){if(!this.sourcesContent){return false}return this.sourcesContent.length>=this._sources.size()&&!this.sourcesContent.some((function(e){return e==null}))};BasicSourceMapConsumer.prototype.sourceContentFor=function SourceMapConsumer_sourceContentFor(e,r){if(!this.sourcesContent){return null}var n=this._findSourceIndex(e);if(n>=0){return this.sourcesContent[n]}var t=e;if(this.sourceRoot!=null){t=o.relative(this.sourceRoot,t)}var i;if(this.sourceRoot!=null&&(i=o.urlParse(this.sourceRoot))){var a=t.replace(/^file:\/\//,"");if(i.scheme=="file"&&this._sources.has(a)){return this.sourcesContent[this._sources.indexOf(a)]}if((!i.path||i.path=="/")&&this._sources.has("/"+t)){return this.sourcesContent[this._sources.indexOf("/"+t)]}}if(r){return null}else{throw new Error('"'+t+'" is not in the SourceMap.')}};BasicSourceMapConsumer.prototype.generatedPositionFor=function SourceMapConsumer_generatedPositionFor(e){var r=o.getArg(e,"source");r=this._findSourceIndex(r);if(r<0){return{line:null,column:null,lastColumn:null}}var n={source:r,originalLine:o.getArg(e,"line"),originalColumn:o.getArg(e,"column")};var t=this._findMapping(n,this._originalMappings,"originalLine","originalColumn",o.compareByOriginalPositions,o.getArg(e,"bias",SourceMapConsumer.GREATEST_LOWER_BOUND));if(t>=0){var i=this._originalMappings[t];if(i.source===n.source){return{line:o.getArg(i,"generatedLine",null),column:o.getArg(i,"generatedColumn",null),lastColumn:o.getArg(i,"lastGeneratedColumn",null)}}}return{line:null,column:null,lastColumn:null}};t=BasicSourceMapConsumer;function IndexedSourceMapConsumer(e,r){var n=e;if(typeof e==="string"){n=o.parseSourceMapInput(e)}var t=o.getArg(n,"version");var i=o.getArg(n,"sections");if(t!=this._version){throw new Error("Unsupported version: "+t)}this._sources=new a;this._names=new a;var u={line:-1,column:0};this._sections=i.map((function(e){if(e.url){throw new Error("Support for url field in sections not implemented.")}var n=o.getArg(e,"offset");var t=o.getArg(n,"line");var i=o.getArg(n,"column");if(t{var t=n(818);var o=n(297);var i=n(517).C;var a=n(24).P;function SourceMapGenerator(e){if(!e){e={}}this._file=o.getArg(e,"file",null);this._sourceRoot=o.getArg(e,"sourceRoot",null);this._skipValidation=o.getArg(e,"skipValidation",false);this._sources=new i;this._names=new i;this._mappings=new a;this._sourcesContents=null}SourceMapGenerator.prototype._version=3;SourceMapGenerator.fromSourceMap=function SourceMapGenerator_fromSourceMap(e){var r=e.sourceRoot;var n=new SourceMapGenerator({file:e.file,sourceRoot:r});e.eachMapping((function(e){var t={generated:{line:e.generatedLine,column:e.generatedColumn}};if(e.source!=null){t.source=e.source;if(r!=null){t.source=o.relative(r,t.source)}t.original={line:e.originalLine,column:e.originalColumn};if(e.name!=null){t.name=e.name}}n.addMapping(t)}));e.sources.forEach((function(t){var i=t;if(r!==null){i=o.relative(r,t)}if(!n._sources.has(i)){n._sources.add(i)}var a=e.sourceContentFor(t);if(a!=null){n.setSourceContent(t,a)}}));return n};SourceMapGenerator.prototype.addMapping=function SourceMapGenerator_addMapping(e){var r=o.getArg(e,"generated");var n=o.getArg(e,"original",null);var t=o.getArg(e,"source",null);var i=o.getArg(e,"name",null);if(!this._skipValidation){this._validateMapping(r,n,t,i)}if(t!=null){t=String(t);if(!this._sources.has(t)){this._sources.add(t)}}if(i!=null){i=String(i);if(!this._names.has(i)){this._names.add(i)}}this._mappings.add({generatedLine:r.line,generatedColumn:r.column,originalLine:n!=null&&n.line,originalColumn:n!=null&&n.column,source:t,name:i})};SourceMapGenerator.prototype.setSourceContent=function SourceMapGenerator_setSourceContent(e,r){var n=e;if(this._sourceRoot!=null){n=o.relative(this._sourceRoot,n)}if(r!=null){if(!this._sourcesContents){this._sourcesContents=Object.create(null)}this._sourcesContents[o.toSetString(n)]=r}else if(this._sourcesContents){delete this._sourcesContents[o.toSetString(n)];if(Object.keys(this._sourcesContents).length===0){this._sourcesContents=null}}};SourceMapGenerator.prototype.applySourceMap=function SourceMapGenerator_applySourceMap(e,r,n){var t=r;if(r==null){if(e.file==null){throw new Error("SourceMapGenerator.prototype.applySourceMap requires either an explicit source file, "+'or the source map\'s "file" property. Both were omitted.')}t=e.file}var a=this._sourceRoot;if(a!=null){t=o.relative(a,t)}var u=new i;var s=new i;this._mappings.unsortedForEach((function(r){if(r.source===t&&r.originalLine!=null){var i=e.originalPositionFor({line:r.originalLine,column:r.originalColumn});if(i.source!=null){r.source=i.source;if(n!=null){r.source=o.join(n,r.source)}if(a!=null){r.source=o.relative(a,r.source)}r.originalLine=i.line;r.originalColumn=i.column;if(i.name!=null){r.name=i.name}}}var l=r.source;if(l!=null&&!u.has(l)){u.add(l)}var c=r.name;if(c!=null&&!s.has(c)){s.add(c)}}),this);this._sources=u;this._names=s;e.sources.forEach((function(r){var t=e.sourceContentFor(r);if(t!=null){if(n!=null){r=o.join(n,r)}if(a!=null){r=o.relative(a,r)}this.setSourceContent(r,t)}}),this)};SourceMapGenerator.prototype._validateMapping=function SourceMapGenerator_validateMapping(e,r,n,t){if(r&&typeof r.line!=="number"&&typeof r.column!=="number"){throw new Error("original.line and original.column are not numbers -- you probably meant to omit "+"the original mapping entirely and only map the generated position. If so, pass "+"null for the original mapping instead of an object with empty or null values.")}if(e&&"line"in e&&"column"in e&&e.line>0&&e.column>=0&&!r&&!n&&!t){return}else if(e&&"line"in e&&"column"in e&&r&&"line"in r&&"column"in r&&e.line>0&&e.column>=0&&r.line>0&&r.column>=0&&n){return}else{throw new Error("Invalid mapping: "+JSON.stringify({generated:e,source:n,original:r,name:t}))}};SourceMapGenerator.prototype._serializeMappings=function SourceMapGenerator_serializeMappings(){var e=0;var r=1;var n=0;var i=0;var a=0;var u=0;var s="";var l;var c;var p;var f;var g=this._mappings.toArray();for(var d=0,h=g.length;d0){if(!o.compareByGeneratedPositionsInflated(c,g[d-1])){continue}l+=","}}l+=t.encode(c.generatedColumn-e);e=c.generatedColumn;if(c.source!=null){f=this._sources.indexOf(c.source);l+=t.encode(f-u);u=f;l+=t.encode(c.originalLine-1-i);i=c.originalLine-1;l+=t.encode(c.originalColumn-n);n=c.originalColumn;if(c.name!=null){p=this._names.indexOf(c.name);l+=t.encode(p-a);a=p}}s+=l}return s};SourceMapGenerator.prototype._generateSourcesContent=function SourceMapGenerator_generateSourcesContent(e,r){return e.map((function(e){if(!this._sourcesContents){return null}if(r!=null){e=o.relative(r,e)}var n=o.toSetString(e);return Object.prototype.hasOwnProperty.call(this._sourcesContents,n)?this._sourcesContents[n]:null}),this)};SourceMapGenerator.prototype.toJSON=function SourceMapGenerator_toJSON(){var e={version:this._version,sources:this._sources.toArray(),names:this._names.toArray(),mappings:this._serializeMappings()};if(this._file!=null){e.file=this._file}if(this._sourceRoot!=null){e.sourceRoot=this._sourceRoot}if(this._sourcesContents){e.sourcesContent=this._generateSourcesContent(e.sources,e.sourceRoot)}return e};SourceMapGenerator.prototype.toString=function SourceMapGenerator_toString(){return JSON.stringify(this.toJSON())};r.x=SourceMapGenerator},565:(e,r,n)=>{var t;var o=n(163).x;var i=n(297);var a=/(\r?\n)/;var u=10;var s="$$$isSourceNode$$$";function SourceNode(e,r,n,t,o){this.children=[];this.sourceContents={};this.line=e==null?null:e;this.column=r==null?null:r;this.source=n==null?null:n;this.name=o==null?null:o;this[s]=true;if(t!=null)this.add(t)}SourceNode.fromStringWithSourceMap=function SourceNode_fromStringWithSourceMap(e,r,n){var t=new SourceNode;var o=e.split(a);var u=0;var shiftNextLine=function(){var e=getNextLine();var r=getNextLine()||"";return e+r;function getNextLine(){return u=0;r--){this.prepend(e[r])}}else if(e[s]||typeof e==="string"){this.children.unshift(e)}else{throw new TypeError("Expected a SourceNode, string, or an array of SourceNodes and strings. Got "+e)}return this};SourceNode.prototype.walk=function SourceNode_walk(e){var r;for(var n=0,t=this.children.length;n0){r=[];for(n=0;n{function getArg(e,r,n){if(r in e){return e[r]}else if(arguments.length===3){return n}else{throw new Error('"'+r+'" is a required argument.')}}r.getArg=getArg;var n=/^(?:([\w+\-.]+):)?\/\/(?:(\w+:\w+)@)?([\w.-]*)(?::(\d+))?(.*)$/;var t=/^data:.+\,.+$/;function urlParse(e){var r=e.match(n);if(!r){return null}return{scheme:r[1],auth:r[2],host:r[3],port:r[4],path:r[5]}}r.urlParse=urlParse;function urlGenerate(e){var r="";if(e.scheme){r+=e.scheme+":"}r+="//";if(e.auth){r+=e.auth+"@"}if(e.host){r+=e.host}if(e.port){r+=":"+e.port}if(e.path){r+=e.path}return r}r.urlGenerate=urlGenerate;function normalize(e){var n=e;var t=urlParse(e);if(t){if(!t.path){return e}n=t.path}var o=r.isAbsolute(n);var i=n.split(/\/+/);for(var a,u=0,s=i.length-1;s>=0;s--){a=i[s];if(a==="."){i.splice(s,1)}else if(a===".."){u++}else if(u>0){if(a===""){i.splice(s+1,u);u=0}else{i.splice(s,2);u--}}}n=i.join("/");if(n===""){n=o?"/":"."}if(t){t.path=n;return urlGenerate(t)}return n}r.normalize=normalize;function join(e,r){if(e===""){e="."}if(r===""){r="."}var n=urlParse(r);var o=urlParse(e);if(o){e=o.path||"/"}if(n&&!n.scheme){if(o){n.scheme=o.scheme}return urlGenerate(n)}if(n||r.match(t)){return r}if(o&&!o.host&&!o.path){o.host=r;return urlGenerate(o)}var i=r.charAt(0)==="/"?r:normalize(e.replace(/\/+$/,"")+"/"+r);if(o){o.path=i;return urlGenerate(o)}return i}r.join=join;r.isAbsolute=function(e){return e.charAt(0)==="/"||n.test(e)};function relative(e,r){if(e===""){e="."}e=e.replace(/\/$/,"");var n=0;while(r.indexOf(e+"/")!==0){var t=e.lastIndexOf("/");if(t<0){return r}e=e.slice(0,t);if(e.match(/^([^\/]+:\/)?\/*$/)){return r}++n}return Array(n+1).join("../")+r.substr(e.length+1)}r.relative=relative;var o=function(){var e=Object.create(null);return!("__proto__"in e)}();function identity(e){return e}function toSetString(e){if(isProtoString(e)){return"$"+e}return e}r.toSetString=o?identity:toSetString;function fromSetString(e){if(isProtoString(e)){return e.slice(1)}return e}r.fromSetString=o?identity:fromSetString;function isProtoString(e){if(!e){return false}var r=e.length;if(r<9){return false}if(e.charCodeAt(r-1)!==95||e.charCodeAt(r-2)!==95||e.charCodeAt(r-3)!==111||e.charCodeAt(r-4)!==116||e.charCodeAt(r-5)!==111||e.charCodeAt(r-6)!==114||e.charCodeAt(r-7)!==112||e.charCodeAt(r-8)!==95||e.charCodeAt(r-9)!==95){return false}for(var n=r-10;n>=0;n--){if(e.charCodeAt(n)!==36){return false}}return true}function compareByOriginalPositions(e,r,n){var t=strcmp(e.source,r.source);if(t!==0){return t}t=e.originalLine-r.originalLine;if(t!==0){return t}t=e.originalColumn-r.originalColumn;if(t!==0||n){return t}t=e.generatedColumn-r.generatedColumn;if(t!==0){return t}t=e.generatedLine-r.generatedLine;if(t!==0){return t}return strcmp(e.name,r.name)}r.compareByOriginalPositions=compareByOriginalPositions;function compareByGeneratedPositionsDeflated(e,r,n){var t=e.generatedLine-r.generatedLine;if(t!==0){return t}t=e.generatedColumn-r.generatedColumn;if(t!==0||n){return t}t=strcmp(e.source,r.source);if(t!==0){return t}t=e.originalLine-r.originalLine;if(t!==0){return t}t=e.originalColumn-r.originalColumn;if(t!==0){return t}return strcmp(e.name,r.name)}r.compareByGeneratedPositionsDeflated=compareByGeneratedPositionsDeflated;function strcmp(e,r){if(e===r){return 0}if(e===null){return 1}if(r===null){return-1}if(e>r){return 1}return-1}function compareByGeneratedPositionsInflated(e,r){var n=e.generatedLine-r.generatedLine;if(n!==0){return n}n=e.generatedColumn-r.generatedColumn;if(n!==0){return n}n=strcmp(e.source,r.source);if(n!==0){return n}n=e.originalLine-r.originalLine;if(n!==0){return n}n=e.originalColumn-r.originalColumn;if(n!==0){return n}return strcmp(e.name,r.name)}r.compareByGeneratedPositionsInflated=compareByGeneratedPositionsInflated;function parseSourceMapInput(e){return JSON.parse(e.replace(/^\)]}'[^\n]*\n/,""))}r.parseSourceMapInput=parseSourceMapInput;function computeSourceURL(e,r,n){r=r||"";if(e){if(e[e.length-1]!=="/"&&r[0]!=="/"){e+="/"}r=e+r}if(n){var t=urlParse(n);if(!t){throw new Error("sourceMapURL could not be parsed")}if(t.path){var o=t.path.lastIndexOf("/");if(o>=0){t.path=t.path.substring(0,o+1)}}r=join(urlGenerate(t),r)}return normalize(r)}r.computeSourceURL=computeSourceURL},927:(e,r,n)=>{n(163).x;r.SourceMapConsumer=n(684).SourceMapConsumer;n(565)},896:e=>{"use strict";e.exports=require("fs")},928:e=>{"use strict";e.exports=require("path")}};var r={};function __webpack_require__(n){var t=r[n];if(t!==undefined){return t.exports}var o=r[n]={id:n,loaded:false,exports:{}};var i=true;try{e[n](o,o.exports,__webpack_require__);i=false}finally{if(i)delete r[n]}o.loaded=true;return o.exports}(()=>{__webpack_require__.nmd=e=>{e.paths=[];if(!e.children)e.children=[];return e}})();if(typeof __webpack_require__!=="undefined")__webpack_require__.ab=__dirname+"/";var n={};__webpack_require__(599).install();module.exports=n})(); --------------------------------------------------------------------------------