diff --git a/.gitignore b/.gitignore index f79499e..992f45f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,3 @@ .env node_modules -package.json -package-lock.json .DS_Store diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..41583e3 --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +@jsr:registry=https://npm.jsr.io diff --git a/backend/alias/main.js b/backend/alias/main.js index 7f36082..9aff95b 100644 --- a/backend/alias/main.js +++ b/backend/alias/main.js @@ -20,7 +20,7 @@ */ import { join } from "../../deps.js"; -import { fetch } from "../../main.js"; +import { fetch } from "../../mod.js"; /** * @param {Request} request diff --git a/backend/chunker/main.js b/backend/chunker/main.js index 29b5a8a..aafc33e 100644 --- a/backend/chunker/main.js +++ b/backend/chunker/main.js @@ -103,7 +103,7 @@ */ import { crypto, encodeHex } from "../../deps.js"; import { Chunker } from "../../lib/streams/chunker.js"; -import { fetch } from "../../main.js"; +import { fetch } from "../../mod.js"; const METADATA_VERSION = 1; diff --git a/backend/crypt/PathCipher.js b/backend/crypt/PathCipher.js index 3528639..6ca95af 100644 --- a/backend/crypt/PathCipher.js +++ b/backend/crypt/PathCipher.js @@ -1,12 +1,6 @@ -import { AES } from "https://esm.sh/aes-js@3.1.2"; -import { - pad, - Padding, - unpad, -} from "https://deno.land/x/crypto/src/utils/padding.ts"; -import { default as decodeBase32 } from "https://esm.sh/base32-decode@1.0.0"; -import { default as encodeBase32 } from "https://esm.sh/base32-encode@2.0.0"; +import { AES, decodeBase32, encodeBase32 } from "../../deps.js"; import { Decrypt, Encrypt } from "./eme.js"; +import { pad, Padding, unpad } from "./padding.js"; const encoder = new TextEncoder(); const decoder = new TextDecoder(); diff --git a/backend/crypt/main.js b/backend/crypt/main.js index f46cc17..87aca16 100644 --- a/backend/crypt/main.js +++ b/backend/crypt/main.js @@ -95,9 +95,8 @@ * - 1049120 bytes total (a 0.05% overhead). This is the overhead for big files. */ -import { scrypt } from "https://deno.land/x/scrypto@v1.0.0/scrypt.ts"; -import { contentType, extname } from "../../deps.js"; -import { fetch } from "../../main.js"; +import { contentType, extname, scrypt } from "../../deps.js"; +import { fetch } from "../../mod.js"; import { reveal } from "../../cmd/obscure/main.js"; import PathCipher from "./PathCipher.js"; import { DecryptionStream, EncryptionStream } from "./secretbox.js"; @@ -124,10 +123,10 @@ const DEFAULT_SALT = new Uint8Array([ ]); // Params for scrypt -const N = 16384; +const N = 2 ** 14; // 16384 const r = 8; const p = 1; -const keySize = 32 + 32 + 16; +const dkLen = 32 + 32 + 16; const SECRETBOX_OPTIONS = { blockSize: 64 * 1024, // 64 KiB @@ -330,16 +329,15 @@ export async function decode(options, ...args) { * @returns {Promise} */ async function deriveKey(encPass, encSalt) { - const password = await reveal(encPass).then((r) => r.arrayBuffer()).then( - (buf) => new Uint8Array(buf), - ); - const decryptedSalt = await reveal(encSalt).then((r) => r.arrayBuffer()).then( - (buf) => new Uint8Array(buf), - ); + const password = await reveal(encPass) + .then((response) => response.arrayBuffer()) + .then((buf) => new Uint8Array(buf)); + const decryptedSalt = await reveal(encSalt) + .then((response) => response.arrayBuffer()) + .then((buf) => new Uint8Array(buf)); const salt = decryptedSalt.length ? decryptedSalt : DEFAULT_SALT; - const derivedKey = await scrypt(password, salt, N, r, p, keySize); - return new Uint8Array(derivedKey); + return scrypt(password, salt, { N, r, p, dkLen }); } export default { diff --git a/backend/crypt/main_test.js b/backend/crypt/main_test.js index c8def80..382c7e1 100644 --- a/backend/crypt/main_test.js +++ b/backend/crypt/main_test.js @@ -1,7 +1,7 @@ import { contentType, extname, join } from "../../deps.js"; import { assert, assertEquals, equalBytes, fc } from "../../dev_deps.js"; -import { decode, encode, default as crypt } from "./main.js"; +import { decode, default as crypt, encode } from "./main.js"; const PASSWORD = "UmyLSdRHfew6aual28-ggx78qHqSfQ"; const SALT = "Cj3gLa5PVwc2aot0QpKiOZ3YEzs3Sw"; diff --git a/backend/crypt/padding.js b/backend/crypt/padding.js new file mode 100644 index 0000000..8c4a5c5 --- /dev/null +++ b/backend/crypt/padding.js @@ -0,0 +1,119 @@ +/** + * @enum { number } + */ +export const Padding = { + NONE: 1, + PKCS7: 2, + ONE_AND_ZEROS: 3, + LAST_BYTE: 4, + NULL: 5, + SPACES: 6, +}; + +/** + * Pads the input bytes to the specified block size. + * @param {Uint8Array} bytes + * @param {Padding} padding + * @param {number} blockSize + * @returns {Uint8Array} + */ +export function pad(bytes, padding, blockSize) { + if (padding === Padding.NONE) { + if (bytes.length % blockSize === 0) return bytes; + else { + throw new Error( + `Invalid data size (must be multiple of ${blockSize} bytes)`, + ); + } + } + + const count = blockSize - bytes.length % blockSize; + + if (count === blockSize && bytes.length > 0 && padding !== Padding.PKCS7) { + return bytes; + } + + const writer = new Uint8Array(bytes.length + count); + const newBytes = []; + let remaining = count; + let padChar = 0; + + switch (padding) { + case Padding.PKCS7: { + padChar = count; + break; + } + case Padding.ONE_AND_ZEROS: { + newBytes.push(0x80); + remaining--; + break; + } + case Padding.SPACES: { + padChar = 0x20; + break; + } + } + + while (remaining > 0) { + if (padding === Padding.LAST_BYTE && remaining === 1) { + newBytes.push(count); + break; + } + newBytes.push(padChar); + remaining--; + } + + writer.set(bytes); + writer.set(newBytes, bytes.length); + return writer; +} + +/** + * Unpads the input bytes to the specified block size. + * @param {Uint8Array} bytes + * @param {Padding} padding + * @param {number} blockSize + * @returns {Uint8Array} + */ +export function unpad(bytes, padding, blockSize) { + let cutLength = 0; + switch (padding) { + case Padding.NONE: { + return bytes; + } + case Padding.LAST_BYTE: + case Padding.PKCS7: { + const lastChar = bytes[bytes.length - 1]; + if (lastChar <= blockSize) { + cutLength = lastChar; + } + break; + } + case Padding.ONE_AND_ZEROS: { + for (let i = 1; i <= blockSize; i++) { + const char = bytes[bytes.length - i]; + if (char === 0x80) { + cutLength = i; + break; + } + if (char !== 0) { + break; + } + } + break; + } + case Padding.NULL: + case Padding.SPACES: { + const padChar = (padding === Padding.SPACES) ? 0x20 : 0; + for (let i = 1; i <= blockSize; i++) { + const char = bytes[bytes.length - i]; + if (char !== padChar) { + cutLength = i - 1; + break; + } + } + break; + } + } + return bytes.subarray(0, bytes.length - cutLength); +} diff --git a/backend/crypt/secretbox.js b/backend/crypt/secretbox.js index 428f1ab..5fdde0d 100644 --- a/backend/crypt/secretbox.js +++ b/backend/crypt/secretbox.js @@ -1,7 +1,7 @@ /** * @module crypt/secretbox */ -import { xsalsa20poly1305 } from "https://esm.sh/@noble/ciphers@0.3.0/salsa"; +import { xsalsa20poly1305 } from "../../deps.js"; /** * Options for secretbox diff --git a/backend/drive/auth.js b/backend/drive/auth.js index 0ce3e8f..77cdb7d 100644 --- a/backend/drive/auth.js +++ b/backend/drive/auth.js @@ -1,4 +1,4 @@ -import { base64url } from "../../deps.js"; +import { encodeBase64Url } from "../../deps.js"; import { reveal } from "../../cmd/obscure/main.js"; @@ -168,7 +168,7 @@ async function createJWT(serviceAccount, scopes) { key, encoder.encode(jwt), ); - const signatureEncoded = base64url.encode(new Uint8Array(signature)); + const signatureEncoded = encodeBase64Url(new Uint8Array(signature)); return `${jwt}.${signatureEncoded}`; } diff --git a/backend/drive/main.js b/backend/drive/main.js index ea6f453..0b24a78 100644 --- a/backend/drive/main.js +++ b/backend/drive/main.js @@ -64,7 +64,14 @@ async function router(request) { /** * @type {import("./file.js").File[]} */ - const files = await list(request).then((r) => r.json()); + let files = []; + try { + files = await list(request).then((r) => r.json()); + } catch (_error) { + return new Response(null, { + status: 404, + }); + } if (method === "HEAD" || method === "GET") { // For request to folder, displays the folder content in HTML @@ -137,13 +144,20 @@ async function router(request) { headers.set("Content-Type", "text/html;charset=utf-8"); } else { + const file = files[0]; + if (!file) { + return new Response(null, { + status: 404, + }); + } + const { id, name, mimeType, size, modifiedTime, - } = files[0]; + } = file; // Sets initial headers based on file metadata. headers.set("Content-Length", String(size)); diff --git a/backend/http/main.js b/backend/http/main.js index 359ba4b..57e42a2 100644 --- a/backend/http/main.js +++ b/backend/http/main.js @@ -105,7 +105,7 @@ async function router(request) { `); /** - * @type {import("../../main.js").File} + * @type {import("../../mod.js").File} */ let item; diff --git a/backend/local/main.js b/backend/local/main.js index b0601f6..c30cc5a 100644 --- a/backend/local/main.js +++ b/backend/local/main.js @@ -1,9 +1,9 @@ #!/usr/bin/env -S deno serve --allow-all +import { cwd } from "node:process"; +import { mkdir, open, readdir, rm, stat } from "node:fs/promises"; import { contentType, extname, formatBytes, join } from "../../deps.js"; -const cwd = Deno.cwd(); - /** * Serves a local remote * @param {Request} request @@ -12,7 +12,7 @@ const cwd = Deno.cwd(); async function router(request) { const { method, url } = request; const { pathname, searchParams } = new URL(url); - const absolutePath = join(cwd, pathname); + const absolutePath = join(cwd(), pathname); let stats; @@ -20,25 +20,14 @@ async function router(request) { let status = 200, body = null; if (method === "HEAD" || method === "GET") { - const file = await Deno.open(absolutePath); - stats = await file.stat(); - file.close(); + stats = await stat(absolutePath); - if (stats.isDirectory) { + if (stats.isDirectory()) { // TODO: refactor to use `node:fs` /** - * @type {Deno.DirEntry[]} + * @type {import("node:fs").Dirent[]} */ - const files = []; - for await (const file of Deno.readDir(absolutePath)) { - const { name, isDirectory } = file; - let filePath = name; - if (isDirectory) { - filePath += "/"; - } - files.push(file); - } - + const files = await readdir(absolutePath, { withFileTypes: true }); files.sort((a, b) => a.name.localeCompare(b.name)); if (method === "GET") { @@ -73,14 +62,16 @@ async function router(request) { `); - for await (const { name, isDirectory } of files) { - let filePath = name; + for await (const file of files) { + const name = file.name; + let filePath = file.name; + const isDirectory = file.isDirectory(); if (isDirectory) { filePath += "/"; } // TODO: refactor to use `node:fs` - const { size, mtime } = await Deno.stat( + const { size, mtime } = await stat( join(absolutePath, filePath), ); @@ -124,8 +115,22 @@ async function router(request) { headers.append("Content-Length", stats.size.toString()); if (method === "GET") { - const file = await Deno.open(absolutePath); - body = file.readable; + const file = await open(absolutePath); + + body = new ReadableStream({ + async pull(controller) { + const chunk = new Uint8Array(64 * 1024); + const { bytesRead } = await file.read(chunk, 0); + controller.enqueue(chunk.slice(0, bytesRead)); + if (bytesRead === 0) { + controller.close(); + file.close(); + } + }, + cancel() { + file.close(); + }, + }); } } @@ -137,10 +142,17 @@ async function router(request) { if (method === "PUT") { // Folder ending with trailing slash. if (pathname.endsWith("/")) { - await Deno.mkdir(absolutePath, { recursive: true }); + await mkdir(absolutePath, { recursive: true }); } else { - const file = await Deno.open(absolutePath, { write: true, create: true }); - await request.body.pipeTo(file.writable); + const file = await open(absolutePath, "w"); + await request.body.pipeTo(new WritableStream({ + async write(chunk) { + await file.write(chunk); + }, + async close() { + await file.close(); + }, + })); } status = 201; @@ -148,7 +160,7 @@ async function router(request) { } if (method === "DELETE") { - await Deno.remove(absolutePath, { recursive: true }); + await rm(absolutePath, { recursive: true }); status = 204; } diff --git a/cmd/cat/main.js b/cmd/cat/main.js index 2cab3a7..a49f756 100644 --- a/cmd/cat/main.js +++ b/cmd/cat/main.js @@ -1,4 +1,4 @@ -import { fetch } from "../../main.js"; +import { fetch } from "../../mod.js"; /** * @param {string} location diff --git a/cmd/config/main.js b/cmd/config/main.js index f4ea788..d1e59af 100644 --- a/cmd/config/main.js +++ b/cmd/config/main.js @@ -1,3 +1,6 @@ +import { env } from "node:process"; +import { readFile } from "node:fs/promises"; + import { config_dir, INI, join } from "../../deps.js"; const NAME_REGEX = /^[\w.][\w.\s-]*$/; @@ -41,11 +44,11 @@ export async function config(subcommand, name, options, init) { const PATHS = [ "rclone.conf", join(config_dir(), "rclone", "rclone.conf"), - join(Deno.env.get("HOME"), ".rclone.conf"), + join(env["HOME"], ".rclone.conf"), ]; for await (const path of PATHS) { try { - ini = await Deno.readTextFile(path); + ini = await readFile(path, { encoding: "utf8" }); file = path; break; } catch (_error) { diff --git a/cmd/copy/main.js b/cmd/copy/main.js index 72780b7..755d4b6 100644 --- a/cmd/copy/main.js +++ b/cmd/copy/main.js @@ -1,4 +1,4 @@ -import { fetch } from "../../main.js"; +import { fetch } from "../../mod.js"; /** * Copy files from source to dest, skipping identical files. * diff --git a/cmd/copyurl/main.js b/cmd/copyurl/main.js index f1f9616..5dc594b 100644 --- a/cmd/copyurl/main.js +++ b/cmd/copyurl/main.js @@ -1,6 +1,6 @@ import { basename, resolve } from "../../deps.js"; -import { fetch } from "../../main.js"; +import { fetch } from "../../mod.js"; /** * @typedef {Object} Options diff --git a/cmd/ls/main.js b/cmd/ls/main.js index 905d3ff..62ff879 100644 --- a/cmd/ls/main.js +++ b/cmd/ls/main.js @@ -1,4 +1,4 @@ -import { lsf } from "../../main.js"; +import { lsf } from "../../mod.js"; /** * List the objects in the path with size and path. * @@ -8,7 +8,7 @@ import { lsf } from "../../main.js"; * Example: * * ```js - * import { ls } from "./mod.js"; + * import { ls } from "./main.js"; * const response = await ls("remote:path"); * console.log(await response.text()); * // 60295 bevajer5jef diff --git a/cmd/lsd/main.js b/cmd/lsd/main.js index 9f20619..10ddde6 100644 --- a/cmd/lsd/main.js +++ b/cmd/lsd/main.js @@ -1,4 +1,4 @@ -import { lsf } from "../../main.js"; +import { lsf } from "../../mod.js"; /** * List all directories/containers/buckets in the path. * diff --git a/cmd/lsf/main.js b/cmd/lsf/main.js index 17fd606..0d363af 100644 --- a/cmd/lsf/main.js +++ b/cmd/lsf/main.js @@ -1,4 +1,4 @@ -import { lsjson } from "../../main.js"; +import { lsjson } from "../../mod.js"; const FORMATS = { "p": "Path", @@ -24,7 +24,7 @@ const FORMATS = { * Example * * ```js - * import { lsf } from "./mod.js"; + * import { lsf } from "./main.js"; * const response = await lsf("remote:path"); * console.log(await response.text()); * // bevajer5jef @@ -56,7 +56,7 @@ const FORMATS = { * Example: * * ```js - * import { Rclone } from "./mod.js"; + * import { lsf } from "./main.js"; * const response = await lsf("remote:path", { format: "tsp" }); * console.log(await response.text()); * // 2016-06-25 18:55:41;60295;bevajer5jef @@ -73,7 +73,7 @@ const FORMATS = { * Example: * * ```js - * import { Rclone } from "./mod.js"; + * import { lsf } from "./main.js"; * const response = await lsf("remote:path", { * separator: ",", * format: "tshp", @@ -91,7 +91,7 @@ const FORMATS = { * Example: * * ```js - * import { Rclone } from "./mod.js"; + * import { lsf } from "./main.js"; * const response = await lsf("remote:path", { * csv: true, * files_only: true, @@ -143,7 +143,7 @@ export async function lsf(location, flags = {}) { if (chunk.startsWith("{") && chunk.at(-1) === "}") { /** - * @type {import("../../main.js").File} + * @type {import("../../mod.js").File} */ const item = JSON.parse(chunk); if (dirsOnly && !item.IsDir) return; diff --git a/cmd/lsjson/main.js b/cmd/lsjson/main.js index 6d41dd4..a1ef21d 100644 --- a/cmd/lsjson/main.js +++ b/cmd/lsjson/main.js @@ -1,6 +1,6 @@ import { basename, HTMLRewriter, toLocaleISOString } from "../../deps.js"; -import { fetch } from "../../main.js"; +import { fetch } from "../../mod.js"; const encoder = new TextEncoder(); const decoder = new TextDecoder(); @@ -105,7 +105,7 @@ export async function lsjson(location, flags = {}) { let body = new ReadableStream({ start(controller) { /** - * @type {import("../../main.js").File} + * @type {import("../../mod.js").File} */ let item; let transformed = Promise.resolve(); diff --git a/cmd/lsl/main.js b/cmd/lsl/main.js index 68124b7..d1d7368 100644 --- a/cmd/lsl/main.js +++ b/cmd/lsl/main.js @@ -1,4 +1,4 @@ -import { lsf } from "../../main.js"; +import { lsf } from "../../mod.js"; /** * List the objects in path with modification time, size and path. @@ -9,7 +9,7 @@ import { lsf } from "../../main.js"; * Example: * * ```js - * import { lsl } from "./mod.js"; + * import { lsl } from "./main.js"; * const response = await lsl("remote:path"); * console.log(await response.text()); * // 60295 2016-06-25 18:55:41.062626927 bevajer5jef diff --git a/cmd/rcat/main.js b/cmd/rcat/main.js index f73ffde..6136c52 100644 --- a/cmd/rcat/main.js +++ b/cmd/rcat/main.js @@ -1,4 +1,4 @@ -import { fetch } from "../../main.js"; +import { fetch } from "../../mod.js"; /** * Copies standard input to file on remote. diff --git a/cmd/serve/http/main.js b/cmd/serve/http/main.js index ca3a7ea..380c5d8 100644 --- a/cmd/serve/http/main.js +++ b/cmd/serve/http/main.js @@ -1,3 +1,26 @@ +import { createServer } from "node:http"; + +/** + * Converts Node's `IncomingMessage` to web `Request`. + * @param {import("node:http").IncomingMessage} incoming + * @returns {Request} + */ +function toWeb(incoming) { + let { url, headers, method, body } = incoming; + const abortController = new AbortController(); + headers = new Headers(headers); + url = new URL(url, `http://${headers.get("Host")}`); + + incoming.once("aborted", () => abortController.abort()); + + return new Request(url, { + method, + headers, + body, + signal: abortController.signal, + }); +} + /** * Serve the remote over HTTP. * @param {(Request) => Response | Promise} fetch @@ -9,18 +32,16 @@ export function serve(fetch, flags = {}) { ...backendFlags } = flags; - const [hostname, port] = addr.split(":"); + let [hostname, port] = addr.split(":"); + if (!hostname) { + hostname = "0.0.0.0"; + } let server; const body = new ReadableStream({ start(controller) { - server = Deno.serve({ - port: Number(port), - hostname, - onListen({ port, hostname }) { - controller.enqueue(`Listening on http://${hostname}:${port}`); - }, - }, (request) => { + server = createServer(async (incoming, outgoing) => { + let request = toWeb(incoming); const url = new URL(request.url); Object.entries(backendFlags).forEach(([key, value]) => { if (!url.searchParams.has(key)) { @@ -29,12 +50,27 @@ export function serve(fetch, flags = {}) { }); request = new Request(url, request); + const response = await fetch(request); + + const { status, statusText, headers, body } = response; + headers.forEach((value, key) => outgoing.setHeader(key, value)); + outgoing.writeHead(status, statusText); + + if (body) { + for await (const chunk of body) { + outgoing.write(chunk); + } + } + + outgoing.end(); + }); - return fetch(request); + server.listen(Number(port), hostname, () => { + controller.enqueue(`Listening on http://${hostname}:${port}`); }); }, async cancel() { - await server.shutdown(); + await server.close(); }, }).pipeThrough(new TextEncoderStream()); diff --git a/cmd/serve/main.js b/cmd/serve/main.js index b904a4f..a2f798a 100644 --- a/cmd/serve/main.js +++ b/cmd/serve/main.js @@ -1,4 +1,4 @@ -import { fetch } from "../../main.js"; +import { fetch } from "../../mod.js"; import * as backends from "../../backend/main.js"; /** diff --git a/deno.json b/deno.json deleted file mode 100644 index f5e35a3..0000000 --- a/deno.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "tasks": { - "start": "deno run -A main.js", - "install": "deno install -A -n dclone main.js", - "bundle": "deno bundle -A main.js", - "test": "deno test -A --no-check --fail-fast", - "bench": "deno bench -A" - } -} diff --git a/deno.lock b/deno.lock index 6e537ae..88b7ada 100644 --- a/deno.lock +++ b/deno.lock @@ -2,12 +2,139 @@ "version": "3", "packages": { "specifiers": { - "npm:fast-check@3.13.1": "npm:fast-check@3.13.1", - "npm:html-rewriter-wasm@0.4.1": "npm:html-rewriter-wasm@0.4.1" + "npm:@jsr/sntran__html-rewriter@^0.1.1": "npm:@jsr/sntran__html-rewriter@0.1.1", + "npm:@jsr/std__assert@^1.0.0-rc.2": "npm:@jsr/std__assert@1.0.0-rc.2", + "npm:@jsr/std__cli@^1.0.0-rc.1": "npm:@jsr/std__cli@1.0.0-rc.1", + "npm:@jsr/std__crypto@^1.0.0-rc.1": "npm:@jsr/std__crypto@1.0.0-rc.1", + "npm:@jsr/std__encoding@^1.0.0-rc.2": "npm:@jsr/std__encoding@1.0.0-rc.2", + "npm:@jsr/std__fmt@^0.225.4": "npm:@jsr/std__fmt@0.225.4", + "npm:@jsr/std__http@^0.224.5": "npm:@jsr/std__http@0.224.5", + "npm:@jsr/std__ini@^0.225.1": "npm:@jsr/std__ini@0.225.1", + "npm:@jsr/std__media-types@^1.0.0-rc.1": "npm:@jsr/std__media-types@1.0.0-rc.1", + "npm:@jsr/std__path@^1.0.0-rc.2": "npm:@jsr/std__path@1.0.0-rc.2", + "npm:@noble/ciphers@^0.5.3": "npm:@noble/ciphers@0.5.3", + "npm:@noble/hashes@^1.4.0": "npm:@noble/hashes@1.4.0", + "npm:@types/node": "npm:@types/node@18.16.19", + "npm:aes-js@^3.1.2": "npm:aes-js@3.1.2", + "npm:base32-decode@^1.0.0": "npm:base32-decode@1.0.0", + "npm:base32-encode@^2.0.0": "npm:base32-encode@2.0.0", + "npm:fast-check@^3.19.0": "npm:fast-check@3.19.0" }, "npm": { - "fast-check@3.13.1": { - "integrity": "sha512-Xp00tFuWd83i8rbG/4wU54qU+yINjQha7bXH2N4ARNTkyOimzHtUBJ5+htpdXk7RMaCOD/j2jxSjEt9u9ZPNeQ==", + "@jsr/sntran__html-rewriter@0.1.1": { + "integrity": "sha512-0uGu8RAt3D2hEjTsprB+aLwLirn1kS8QIFslU7lwAFldClWArBPGy4LJSGB8IcQsLxdUegQofT7iBvzR3bNrSA==", + "dependencies": { + "html-rewriter-wasm": "html-rewriter-wasm@0.4.1" + } + }, + "@jsr/std__assert@1.0.0-rc.2": { + "integrity": "sha512-5ABr022bTpmYxOnbe9sIuxspk6HEmHOvifncPVigND27j+OHjvPwPRijsLnG/H9wZXS9CPeNRiHvrxw2WLGQiQ==", + "dependencies": { + "@jsr/std__internal": "@jsr/std__internal@1.0.0" + } + }, + "@jsr/std__async@1.0.0-rc.2": { + "integrity": "sha512-mW3WDco4WkcsVhuwWRRRMSOW2odyXb1O80TMnfFjJZbxmlVXZKZmR9RkJvSQv8BcxopdZ3JCigkHNEmtRFsTXQ==", + "dependencies": {} + }, + "@jsr/std__bytes@1.0.0-rc.3": { + "integrity": "sha512-4cHQrAO+8e6YcJVNHiPvkX9LVPhy4ugiatYUZnZ5kO1JG2dZia6wu9dYEsL6pInUQuBx9tFcPIowc0YmeNP+CQ==", + "dependencies": {} + }, + "@jsr/std__cli@0.224.7": { + "integrity": "sha512-SSE7AMT/nqTUKeQXB+PZcmD6L5IjsOFDQdm88Ik2o6P4gS+gXewLMPZJu/X/gaOH5ux2C4xZ26Dgp3vCAIuB4g==", + "dependencies": {} + }, + "@jsr/std__cli@1.0.0-rc.1": { + "integrity": "sha512-4MdEB0lsJgK99k+ykHVzc0szoe4DPGETpv+fqgFhpj2ipkjOV8oV2QiSknXcbHps0CkvC70Mx/+W2AJrdtNORg==", + "dependencies": {} + }, + "@jsr/std__crypto@1.0.0-rc.1": { + "integrity": "sha512-HdqxgBx468H2ePsUUEYh4dyABELe1qxt4r2qK4UAR4gBrLxPRMBmAkZD0E7n/kUYA7+LTtLZ7OOhjZoQ9PPgrQ==", + "dependencies": {} + }, + "@jsr/std__encoding@1.0.0-rc.2": { + "integrity": "sha512-ISvxD+exWVg/aESN3G1Tr0oszHRgUEEK1JjiRcuyCtd1uqSGXXMAoswYlYvPwA6mxZ7FQbFSZEbP3tN2J+fIUQ==", + "dependencies": {} + }, + "@jsr/std__fmt@0.225.4": { + "integrity": "sha512-lb8QcV3YnAu3GsrG7wd51Fb56RxOBp4szMRS/TCLnQptmF0Rd0v8ksQeTHmGkuD6MY/V8gEy/Sj0qulRvR9lqQ==", + "dependencies": {} + }, + "@jsr/std__http@0.224.5": { + "integrity": "sha512-PomDgsxwBoCkqTCG7e1Sz4QCAgNFhEGf71GWNercppQlgKtzp6zxsDgcJ4+vrqHIq5W8L1eqJWiceNkUUkMZjQ==", + "dependencies": { + "@jsr/std__async": "@jsr/std__async@1.0.0-rc.2", + "@jsr/std__cli": "@jsr/std__cli@0.224.7", + "@jsr/std__encoding": "@jsr/std__encoding@1.0.0-rc.2", + "@jsr/std__fmt": "@jsr/std__fmt@0.225.4", + "@jsr/std__media-types": "@jsr/std__media-types@1.0.0-rc.1", + "@jsr/std__net": "@jsr/std__net@0.224.3", + "@jsr/std__path": "@jsr/std__path@1.0.0-rc.2", + "@jsr/std__streams": "@jsr/std__streams@0.224.5" + } + }, + "@jsr/std__ini@0.225.1": { + "integrity": "sha512-K23bS0WRbZ2riDg9jQMnaJW4uW/KaQpFJZRmv8cGDL+7JhmMkI+yC3wm8zGxctggPR1+pxIXH0WmK44CbGkORQ==", + "dependencies": {} + }, + "@jsr/std__internal@1.0.0": { + "integrity": "sha512-6kp0FsXcHW4QvGYpOPAtglYSh/813OYKBP7RlH8VCaMyKwnWcRvcH+fhVc92RQ1a39Gh2XDwD6WGRYaFXZJxGw==", + "dependencies": {} + }, + "@jsr/std__io@0.224.2": { + "integrity": "sha512-7IbtJWWM13TaXkZR3jL0LLkAbEaublLR6WSMZuzg58IACDGnzcmxKlI57KjEBTuawX4u6x7g8f6zRleuRpSXVA==", + "dependencies": { + "@jsr/std__bytes": "@jsr/std__bytes@1.0.0-rc.3" + } + }, + "@jsr/std__media-types@1.0.0-rc.1": { + "integrity": "sha512-0UdhqMLempJi8rHXJ4xSsRs79gMIm+X8/jAcBxUa9sdbsRALM148t/c2JIRWN4S58LQVBRHNK3WYd/YgR4fLNA==", + "dependencies": {} + }, + "@jsr/std__net@0.224.3": { + "integrity": "sha512-67B6bpkMjNSWtEPLy/k4OpuCb65w+L0mf9Amp1E2uUs4nt31vZZazW8IUykE9+1sv0tDVxa8nFZ2p9VwdiLXaA==", + "dependencies": {} + }, + "@jsr/std__path@1.0.0-rc.2": { + "integrity": "sha512-qUHfVMqAtMGPFmdkOfmkCONqgpe4UFVAaD+wL6ZdlQxtvQiNxHQWsje+djri+0095s/kkoBOPH4CpYxBq+zh3A==", + "dependencies": {} + }, + "@jsr/std__streams@0.224.5": { + "integrity": "sha512-LWs0LfBcyj4VzE3ff1cwoEyJ3AkoIT3CQubxeA0YQr3KEC/c6xB9Rq9nVit8gZ8RgtbMDlyfPtN1MkwTgPFj+g==", + "dependencies": { + "@jsr/std__bytes": "@jsr/std__bytes@1.0.0-rc.3", + "@jsr/std__io": "@jsr/std__io@0.224.2" + } + }, + "@noble/ciphers@0.5.3": { + "integrity": "sha512-B0+6IIHiqEs3BPMT0hcRmHvEj2QHOLu+uwt+tqDDeVd0oyVzh7BPrDcPjRnV1PV/5LaknXJJQvOuRGR0zQJz+w==", + "dependencies": {} + }, + "@noble/hashes@1.4.0": { + "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", + "dependencies": {} + }, + "@types/node@18.16.19": { + "integrity": "sha512-IXl7o+R9iti9eBW4Wg2hx1xQDig183jj7YLn8F7udNceyfkbn1ZxmzZXuak20gR40D7pIkIY1kYGx5VIGbaHKA==", + "dependencies": {} + }, + "aes-js@3.1.2": { + "integrity": "sha512-e5pEa2kBnBOgR4Y/p20pskXI74UEz7de8ZGVo58asOtvSVG5YAbJeELPZxOmt+Bnz3rX753YKhfIn4X4l1PPRQ==", + "dependencies": {} + }, + "base32-decode@1.0.0": { + "integrity": "sha512-KNWUX/R7wKenwE/G/qFMzGScOgVntOmbE27vvc6GrniDGYb6a5+qWcuoXl8WIOQL7q0TpK7nZDm1Y04Yi3Yn5g==", + "dependencies": {} + }, + "base32-encode@2.0.0": { + "integrity": "sha512-mlmkfc2WqdDtMl/id4qm3A7RjW6jxcbAoMjdRmsPiwQP0ufD4oXItYMnPgVHe80lnAIy+1xwzhHE1s4FoIceSw==", + "dependencies": { + "to-data-view": "to-data-view@2.0.0" + } + }, + "fast-check@3.19.0": { + "integrity": "sha512-CO2JX/8/PT9bDGO1iXa5h5ey1skaKI1dvecERyhH4pp3PGjwd3KIjMAXEg79Ps9nclsdt4oPbfqiAnLU0EwrAQ==", "dependencies": { "pure-rand": "pure-rand@6.1.0" } @@ -19,179 +146,34 @@ "pure-rand@6.1.0": { "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", "dependencies": {} + }, + "to-data-view@2.0.0": { + "integrity": "sha512-RGEM5KqlPHr+WVTPmGNAXNeFEmsBnlkxXaIfEpUYV0AST2Z5W1EGq9L/MENFrMMmL2WQr1wjkmZy/M92eKhjYA==", + "dependencies": {} } } }, - "redirects": { - "https://deno.land/x/crypto/src/utils/padding.ts": "https://deno.land/x/crypto@v0.10.1/src/utils/padding.ts", - "https://esm.sh/v135/@types/aes-js@~3.1/index.d.ts": "https://esm.sh/v135/@types/aes-js@3.1.4/index.d.ts" - }, - "remote": { - "https://deno.land/std@0.203.0/assert/_constants.ts": "8a9da298c26750b28b326b297316cdde860bc237533b07e1337c021379e6b2a9", - "https://deno.land/std@0.203.0/assert/_diff.ts": "1a3c044aedf77647d6cac86b798c6417603361b66b54c53331b312caeb447aea", - "https://deno.land/std@0.203.0/assert/_format.ts": "a69126e8a469009adf4cf2a50af889aca364c349797e63174884a52ff75cf4c7", - "https://deno.land/std@0.203.0/assert/assert.ts": "9a97dad6d98c238938e7540736b826440ad8c1c1e54430ca4c4e623e585607ee", - "https://deno.land/std@0.203.0/assert/assert_almost_equals.ts": "e15ca1f34d0d5e0afae63b3f5d975cbd18335a132e42b0c747d282f62ad2cd6c", - "https://deno.land/std@0.203.0/assert/assert_array_includes.ts": "6856d7f2c3544bc6e62fb4646dfefa3d1df5ff14744d1bca19f0cbaf3b0d66c9", - "https://deno.land/std@0.203.0/assert/assert_equals.ts": "d8ec8a22447fbaf2fc9d7c3ed2e66790fdb74beae3e482855d75782218d68227", - "https://deno.land/std@0.203.0/assert/assert_exists.ts": "407cb6b9fb23a835cd8d5ad804e2e2edbbbf3870e322d53f79e1c7a512e2efd7", - "https://deno.land/std@0.203.0/assert/assert_false.ts": "0ccbcaae910f52c857192ff16ea08bda40fdc79de80846c206bfc061e8c851c6", - "https://deno.land/std@0.203.0/assert/assert_greater.ts": "ae2158a2d19313bf675bf7251d31c6dc52973edb12ac64ac8fc7064152af3e63", - "https://deno.land/std@0.203.0/assert/assert_greater_or_equal.ts": "1439da5ebbe20855446cac50097ac78b9742abe8e9a43e7de1ce1426d556e89c", - "https://deno.land/std@0.203.0/assert/assert_instance_of.ts": "3aedb3d8186e120812d2b3a5dea66a6e42bf8c57a8bd927645770bd21eea554c", - "https://deno.land/std@0.203.0/assert/assert_is_error.ts": "c21113094a51a296ffaf036767d616a78a2ae5f9f7bbd464cd0197476498b94b", - "https://deno.land/std@0.203.0/assert/assert_less.ts": "aec695db57db42ec3e2b62e97e1e93db0063f5a6ec133326cc290ff4b71b47e4", - "https://deno.land/std@0.203.0/assert/assert_less_or_equal.ts": "5fa8b6a3ffa20fd0a05032fe7257bf985d207b85685fdbcd23651b70f928c848", - "https://deno.land/std@0.203.0/assert/assert_match.ts": "c4083f80600bc190309903c95e397a7c9257ff8b5ae5c7ef91e834704e672e9b", - "https://deno.land/std@0.203.0/assert/assert_not_equals.ts": "9f1acab95bd1f5fc9a1b17b8027d894509a745d91bac1718fdab51dc76831754", - "https://deno.land/std@0.203.0/assert/assert_not_instance_of.ts": "0c14d3dfd9ab7a5276ed8ed0b18c703d79a3d106102077ec437bfe7ed912bd22", - "https://deno.land/std@0.203.0/assert/assert_not_match.ts": "3796a5b0c57a1ce6c1c57883dd4286be13a26f715ea662318ab43a8491a13ab0", - "https://deno.land/std@0.203.0/assert/assert_not_strict_equals.ts": "ca6c6d645e95fbc873d25320efeb8c4c6089a9a5e09f92d7c1c4b6e935c2a6ad", - "https://deno.land/std@0.203.0/assert/assert_object_match.ts": "d8fc2867cfd92eeacf9cea621e10336b666de1874a6767b5ec48988838370b54", - "https://deno.land/std@0.203.0/assert/assert_rejects.ts": "45c59724de2701e3b1f67c391d6c71c392363635aad3f68a1b3408f9efca0057", - "https://deno.land/std@0.203.0/assert/assert_strict_equals.ts": "b1f538a7ea5f8348aeca261d4f9ca603127c665e0f2bbfeb91fa272787c87265", - "https://deno.land/std@0.203.0/assert/assert_string_includes.ts": "b821d39ebf5cb0200a348863c86d8c4c4b398e02012ce74ad15666fc4b631b0c", - "https://deno.land/std@0.203.0/assert/assert_throws.ts": "63784e951475cb7bdfd59878cd25a0931e18f6dc32a6077c454b2cd94f4f4bcd", - "https://deno.land/std@0.203.0/assert/assertion_error.ts": "4d0bde9b374dfbcbe8ac23f54f567b77024fb67dbb1906a852d67fe050d42f56", - "https://deno.land/std@0.203.0/assert/equal.ts": "9f1a46d5993966d2596c44e5858eec821859b45f783a5ee2f7a695dfc12d8ece", - "https://deno.land/std@0.203.0/assert/fail.ts": "c36353d7ae6e1f7933d45f8ea51e358c8c4b67d7e7502028598fe1fea062e278", - "https://deno.land/std@0.203.0/assert/mod.ts": "37c49a26aae2b254bbe25723434dc28cd7532e444cf0b481a97c045d110ec085", - "https://deno.land/std@0.203.0/assert/unimplemented.ts": "d56fbeecb1f108331a380f72e3e010a1f161baa6956fd0f7cf3e095ae1a4c75a", - "https://deno.land/std@0.203.0/assert/unreachable.ts": "4600dc0baf7d9c15a7f7d234f00c23bca8f3eba8b140286aaca7aa998cf9a536", - "https://deno.land/std@0.203.0/fmt/colors.ts": "c51c4642678eb690dcf5ffee5918b675bf01a33fba82acf303701ae1a4f8c8d9", - "https://deno.land/std@0.209.0/assert/assert.ts": "e265ad50a9341f3b40e51dd4cb41ab253d976943ba78a977106950e52e0302ab", - "https://deno.land/std@0.209.0/assert/assertion_error.ts": "26ed1863d905005f00785c89750c001c3522c5417e4f58f95044b8143cfc1593", - "https://deno.land/std@0.209.0/cli/parse_args.ts": "9bea02050b3f302e706871ff87ecfa3ad82cc34249adbe0dcddfaac75bdb48ff", - "https://deno.land/std@0.209.0/crypto/_fnv/fnv32.ts": "e4649dfdefc5c987ed53c3c25db62db771a06d9d1b9c36d2b5cf0853b8e82153", - "https://deno.land/std@0.209.0/crypto/_fnv/fnv64.ts": "bfa0e4702061fdb490a14e6bf5f9168a22fb022b307c5723499469bfefca555e", - "https://deno.land/std@0.209.0/crypto/_fnv/mod.ts": "f956a95f58910f223e420340b7404702ecd429603acd4491fa77af84f746040c", - "https://deno.land/std@0.209.0/crypto/_fnv/util.ts": "accba12bfd80a352e32a872f87df2a195e75561f1b1304a4cb4f5a4648d288f9", - "https://deno.land/std@0.209.0/crypto/_wasm/lib/deno_std_wasm_crypto.generated.mjs": "524b64057e4c1f0a4f65e9ec8cf7257a618e0f1fe326fa1e0bbff35444a7effb", - "https://deno.land/std@0.209.0/crypto/_wasm/mod.ts": "d7b7dc54bbd6b02c16cd08e8e3d30fa9aa9692efb112a7ab5d8595827b9a0234", - "https://deno.land/std@0.209.0/crypto/crypto.ts": "91c67764abb640b3e2a0b46867704d02077c0b1f978f5c711e4408e5d856717d", - "https://deno.land/std@0.209.0/crypto/mod.ts": "e49caf22e980db882b917c48ac119e44c46ab57399cb565c2cd3138f8c33835e", - "https://deno.land/std@0.209.0/crypto/timing_safe_equal.ts": "f6edc08d702f660b1ab3505b74d53a9d499e34a1351f6ab70f5ce8653fee8fb7", - "https://deno.land/std@0.209.0/crypto/to_hash_string.ts": "5a687ffee6b54c935a84394c8f2394c2e1bf62bbff0f855b8ab6f5669e47cc8a", - "https://deno.land/std@0.209.0/crypto/unstable_keystack.ts": "8514e9952440f0768466b9287a176ca898dd6ebdbfd56e430edff4ce447f2c0a", - "https://deno.land/std@0.209.0/encoding/_util.ts": "f368920189c4fe6592ab2e93bd7ded8f3065b84f95cd3e036a4a10a75649dcba", - "https://deno.land/std@0.209.0/encoding/base64.ts": "d31eb3c0b6003daa188a86074603d721d572f927c72ad6cd008145c0d42063ab", - "https://deno.land/std@0.209.0/encoding/base64url.ts": "698bbc4dcc51dac757008f8d29c52b49856a779ce66af929be0d7aa10ecb603e", - "https://deno.land/std@0.209.0/encoding/hex.ts": "f0a88c260af36f19c7f36b127a4e0c960bb3e65e16cb04493912e4c8b478e877", - "https://deno.land/std@0.209.0/fmt/bytes.ts": "f29cf69e0791d375f9f5d94ae1f0641e5a03b975f32ddf86d70f70fdf37e7b6a", - "https://deno.land/std@0.209.0/fmt/duration.ts": "1dc8d51e85fc566f8fb1bb89b3b252298bd649bec5dd4fdb0f03ac52179ca7ad", - "https://deno.land/std@0.209.0/http/cookie.ts": "c6079019fc15c781c302574f40fa2ac71c26b251e8f74eb236ea43e0424edcd7", - "https://deno.land/std@0.209.0/ini/ini_map.ts": "d2467e5690a9c02a80eb0823e4e618553f6c3bb363e9dac170163a1fb25d5f7b", - "https://deno.land/std@0.209.0/ini/mod.ts": "ae5eb7ad2d0615a42b0ac73422a2c39fde6351d20c256b4f774125be412a6c8c", - "https://deno.land/std@0.209.0/ini/parse.ts": "0a95cdea7b5fc076e44bf5a09630187d68b3dfdcaf30c97132bf18186a9fce3b", - "https://deno.land/std@0.209.0/ini/stringify.ts": "9fb770d84d3e057ff603800528edfb9cb3386ecbc5b318cd870731c678f225a2", - "https://deno.land/std@0.209.0/media_types/_db.ts": "7606d83e31f23ce1a7968cbaee852810c2cf477903a095696cdc62eaab7ce570", - "https://deno.land/std@0.209.0/media_types/_util.ts": "0879b04cc810ff18d3dcd97d361e03c9dfb29f67d7fc4a9c6c9d387282ef5fe8", - "https://deno.land/std@0.209.0/media_types/content_type.ts": "86a618b6db1c722859968f416d38028722a4be333e3e69e738f017a74149316b", - "https://deno.land/std@0.209.0/media_types/extension.ts": "a7cd28c9417143387cdfed27d4e8607ebcf5b1ec27eb8473d5b000144689fe65", - "https://deno.land/std@0.209.0/media_types/extensions_by_type.ts": "43806d6a52a0d6d965ada9d20e60a982feb40bc7a82268178d94edb764694fed", - "https://deno.land/std@0.209.0/media_types/format_media_type.ts": "f5e1073c05526a6f5a516ac5c5587a1abd043bf1039c71cde1166aa4328c8baf", - "https://deno.land/std@0.209.0/media_types/get_charset.ts": "18b88274796fda5d353806bf409eb1d2ddb3f004eb4bd311662c4cdd8ac173db", - "https://deno.land/std@0.209.0/media_types/mod.ts": "d3f0b99f85053bc0b98ecc24eaa3546dfa09b856dc0bbaf60d8956d2cdd710c8", - "https://deno.land/std@0.209.0/media_types/parse_media_type.ts": "31ccf2388ffab31b49500bb89fa0f5de189c8897e2ee6c9954f207637d488211", - "https://deno.land/std@0.209.0/media_types/type_by_extension.ts": "daa801eb0f11cdf199445d0f1b656cf116d47dcf9e5b85cc1e6b4469f5ee0432", - "https://deno.land/std@0.209.0/media_types/vendor/mime-db.v1.52.0.ts": "6925bbcae81ca37241e3f55908d0505724358cda3384eaea707773b2c7e99586", - "https://deno.land/std@0.209.0/path/_common/assert_path.ts": "061e4d093d4ba5aebceb2c4da3318bfe3289e868570e9d3a8e327d91c2958946", - "https://deno.land/std@0.209.0/path/_common/basename.ts": "0d978ff818f339cd3b1d09dc914881f4d15617432ae519c1b8fdc09ff8d3789a", - "https://deno.land/std@0.209.0/path/_common/common.ts": "9e4233b2eeb50f8b2ae10ecc2108f58583aea6fd3e8907827020282dc2b76143", - "https://deno.land/std@0.209.0/path/_common/constants.ts": "e49961f6f4f48039c0dfed3c3f93e963ca3d92791c9d478ac5b43183413136e0", - "https://deno.land/std@0.209.0/path/_common/dirname.ts": "2ba7fb4cc9fafb0f38028f434179579ce61d4d9e51296fad22b701c3d3cd7397", - "https://deno.land/std@0.209.0/path/_common/format.ts": "11aa62e316dfbf22c126917f5e03ea5fe2ee707386555a8f513d27ad5756cf96", - "https://deno.land/std@0.209.0/path/_common/from_file_url.ts": "ef1bf3197d2efbf0297a2bdbf3a61d804b18f2bcce45548ae112313ec5be3c22", - "https://deno.land/std@0.209.0/path/_common/glob_to_reg_exp.ts": "50386887d6041f15741d0013a703ee63ef673983d465d3a0c9c190e95f8da4fe", - "https://deno.land/std@0.209.0/path/_common/normalize.ts": "2ba7fb4cc9fafb0f38028f434179579ce61d4d9e51296fad22b701c3d3cd7397", - "https://deno.land/std@0.209.0/path/_common/normalize_string.ts": "88c472f28ae49525f9fe82de8c8816d93442d46a30d6bb5063b07ff8a89ff589", - "https://deno.land/std@0.209.0/path/_common/relative.ts": "1af19d787a2a84b8c534cc487424fe101f614982ae4851382c978ab2216186b4", - "https://deno.land/std@0.209.0/path/_common/strip_trailing_separators.ts": "7ffc7c287e97bdeeee31b155828686967f222cd73f9e5780bfe7dfb1b58c6c65", - "https://deno.land/std@0.209.0/path/_common/to_file_url.ts": "a8cdd1633bc9175b7eebd3613266d7c0b6ae0fb0cff24120b6092ac31662f9ae", - "https://deno.land/std@0.209.0/path/_interface.ts": "6471159dfbbc357e03882c2266d21ef9afdb1e4aa771b0545e90db58a0ba314b", - "https://deno.land/std@0.209.0/path/_os.ts": "30b0c2875f360c9296dbe6b7f2d528f0f9c741cecad2e97f803f5219e91b40a2", - "https://deno.land/std@0.209.0/path/basename.ts": "04bb5ef3e86bba8a35603b8f3b69537112cdd19ce64b77f2522006da2977a5f3", - "https://deno.land/std@0.209.0/path/common.ts": "f4d061c7d0b95a65c2a1a52439edec393e906b40f1caf4604c389fae7caa80f5", - "https://deno.land/std@0.209.0/path/dirname.ts": "88a0a71c21debafc4da7a4cd44fd32e899462df458fbca152390887d41c40361", - "https://deno.land/std@0.209.0/path/extname.ts": "2da4e2490f3b48b7121d19fb4c91681a5e11bd6bd99df4f6f47d7a71bb6ecdf2", - "https://deno.land/std@0.209.0/path/format.ts": "3457530cc85d1b4bab175f9ae73998b34fd456c830d01883169af0681b8894fb", - "https://deno.land/std@0.209.0/path/from_file_url.ts": "e7fa233ea1dff9641e8d566153a24d95010110185a6f418dd2e32320926043f8", - "https://deno.land/std@0.209.0/path/glob_to_regexp.ts": "74d7448c471e293d03f05ccb968df4365fed6aaa508506b6325a8efdc01d8271", - "https://deno.land/std@0.209.0/path/is_absolute.ts": "67232b41b860571c5b7537f4954c88d86ae2ba45e883ee37d3dec27b74909d13", - "https://deno.land/std@0.209.0/path/is_glob.ts": "567dce5c6656bdedfc6b3ee6c0833e1e4db2b8dff6e62148e94a917f289c06ad", - "https://deno.land/std@0.209.0/path/join.ts": "98d3d76c819af4a11a81d5ba2dbb319f1ce9d63fc2b615597d4bcfddd4a89a09", - "https://deno.land/std@0.209.0/path/join_globs.ts": "9b84d5103b63d3dbed4b2cf8b12477b2ad415c7d343f1488505162dc0e5f4db8", - "https://deno.land/std@0.209.0/path/mod.ts": "3defabebc98279e62b392fee7a6937adc932a8f4dcd2471441e36c15b97b00e0", - "https://deno.land/std@0.209.0/path/normalize.ts": "aa95be9a92c7bd4f9dc0ba51e942a1973e2b93d266cd74f5ca751c136d520b66", - "https://deno.land/std@0.209.0/path/normalize_glob.ts": "674baa82e1c00b6cb153bbca36e06f8e0337cb8062db6d905ab5de16076ca46b", - "https://deno.land/std@0.209.0/path/parse.ts": "d87ff0deef3fb495bc0d862278ff96da5a06acf0625ca27769fc52ac0d3d6ece", - "https://deno.land/std@0.209.0/path/posix/_util.ts": "ecf49560fedd7dd376c6156cc5565cad97c1abe9824f4417adebc7acc36c93e5", - "https://deno.land/std@0.209.0/path/posix/basename.ts": "a630aeb8fd8e27356b1823b9dedd505e30085015407caa3396332752f6b8406a", - "https://deno.land/std@0.209.0/path/posix/common.ts": "e781d395dc76f6282e3f7dd8de13194abb8b04a82d109593141abc6e95755c8b", - "https://deno.land/std@0.209.0/path/posix/dirname.ts": "f48c9c42cc670803b505478b7ef162c7cfa9d8e751b59d278b2ec59470531472", - "https://deno.land/std@0.209.0/path/posix/extname.ts": "ee7f6571a9c0a37f9218fbf510c440d1685a7c13082c348d701396cc795e0be0", - "https://deno.land/std@0.209.0/path/posix/format.ts": "b94876f77e61bfe1f147d5ccb46a920636cd3cef8be43df330f0052b03875968", - "https://deno.land/std@0.209.0/path/posix/from_file_url.ts": "b97287a83e6407ac27bdf3ab621db3fccbf1c27df0a1b1f20e1e1b5acf38a379", - "https://deno.land/std@0.209.0/path/posix/glob_to_regexp.ts": "6ed00c71fbfe0ccc35977c35444f94e82200b721905a60bd1278b1b768d68b1a", - "https://deno.land/std@0.209.0/path/posix/is_absolute.ts": "159900a3422d11069d48395568217eb7fc105ceda2683d03d9b7c0f0769e01b8", - "https://deno.land/std@0.209.0/path/posix/is_glob.ts": "ec4fbc604b9db8487f7b56ab0e759b24a971ab6a45f7b0b698bc39b8b9f9680f", - "https://deno.land/std@0.209.0/path/posix/join.ts": "0c0d84bdc344876930126640011ec1b888e6facf74153ffad9ef26813aa2a076", - "https://deno.land/std@0.209.0/path/posix/join_globs.ts": "f4838d54b1f60a34a40625a3293f6e583135348be1b2974341ac04743cb26121", - "https://deno.land/std@0.209.0/path/posix/mod.ts": "f1b08a7f64294b7de87fc37190d63b6ce5b02889af9290c9703afe01951360ae", - "https://deno.land/std@0.209.0/path/posix/normalize.ts": "11de90a94ab7148cc46e5a288f7d732aade1d616bc8c862f5560fa18ff987b4b", - "https://deno.land/std@0.209.0/path/posix/normalize_glob.ts": "10a1840c628ebbab679254d5fa1c20e59106102354fb648a1765aed72eb9f3f9", - "https://deno.land/std@0.209.0/path/posix/parse.ts": "199208f373dd93a792e9c585352bfc73a6293411bed6da6d3bc4f4ef90b04c8e", - "https://deno.land/std@0.209.0/path/posix/relative.ts": "e2f230608b0f083e6deaa06e063943e5accb3320c28aef8d87528fbb7fe6504c", - "https://deno.land/std@0.209.0/path/posix/resolve.ts": "51579d83159d5c719518c9ae50812a63959bbcb7561d79acbdb2c3682236e285", - "https://deno.land/std@0.209.0/path/posix/separator.ts": "0b6573b5f3269a3164d8edc9cefc33a02dd51003731c561008c8bb60220ebac1", - "https://deno.land/std@0.209.0/path/posix/to_file_url.ts": "08d43ea839ee75e9b8b1538376cfe95911070a655cd312bc9a00f88ef14967b6", - "https://deno.land/std@0.209.0/path/posix/to_namespaced_path.ts": "c9228a0e74fd37e76622cd7b142b8416663a9b87db643302fa0926b5a5c83bdc", - "https://deno.land/std@0.209.0/path/relative.ts": "23d45ede8b7ac464a8299663a43488aad6b561414e7cbbe4790775590db6349c", - "https://deno.land/std@0.209.0/path/resolve.ts": "5b184efc87155a0af9fa305ff68a109e28de9aee81fc3e77cd01380f19daf867", - "https://deno.land/std@0.209.0/path/separator.ts": "40a3e9a4ad10bef23bc2cd6c610291b6c502a06237c2c4cd034a15ca78dedc1f", - "https://deno.land/std@0.209.0/path/to_file_url.ts": "edaafa089e0bce386e1b2d47afe7c72e379ff93b28a5829a5885e4b6c626d864", - "https://deno.land/std@0.209.0/path/to_namespaced_path.ts": "cf8734848aac3c7527d1689d2adf82132b1618eff3cc523a775068847416b22a", - "https://deno.land/std@0.209.0/path/windows/_util.ts": "f32b9444554c8863b9b4814025c700492a2b57ff2369d015360970a1b1099d54", - "https://deno.land/std@0.209.0/path/windows/basename.ts": "8a9dbf7353d50afbc5b221af36c02a72c2d1b2b5b9f7c65bf6a5a2a0baf88ad3", - "https://deno.land/std@0.209.0/path/windows/common.ts": "e781d395dc76f6282e3f7dd8de13194abb8b04a82d109593141abc6e95755c8b", - "https://deno.land/std@0.209.0/path/windows/dirname.ts": "5c2aa541384bf0bd9aca821275d2a8690e8238fa846198ef5c7515ce31a01a94", - "https://deno.land/std@0.209.0/path/windows/extname.ts": "07f4fa1b40d06a827446b3e3bcc8d619c5546b079b8ed0c77040bbef716c7614", - "https://deno.land/std@0.209.0/path/windows/format.ts": "343019130d78f172a5c49fdc7e64686a7faf41553268961e7b6c92a6d6548edf", - "https://deno.land/std@0.209.0/path/windows/from_file_url.ts": "d53335c12b0725893d768be3ac6bf0112cc5b639d2deb0171b35988493b46199", - "https://deno.land/std@0.209.0/path/windows/glob_to_regexp.ts": "290755e18ec6c1a4f4d711c3390537358e8e3179581e66261a0cf348b1a13395", - "https://deno.land/std@0.209.0/path/windows/is_absolute.ts": "245b56b5f355ede8664bd7f080c910a97e2169972d23075554ae14d73722c53c", - "https://deno.land/std@0.209.0/path/windows/is_glob.ts": "ec4fbc604b9db8487f7b56ab0e759b24a971ab6a45f7b0b698bc39b8b9f9680f", - "https://deno.land/std@0.209.0/path/windows/join.ts": "e6600bf88edeeef4e2276e155b8de1d5dec0435fd526ba2dc4d37986b2882f16", - "https://deno.land/std@0.209.0/path/windows/join_globs.ts": "f4838d54b1f60a34a40625a3293f6e583135348be1b2974341ac04743cb26121", - "https://deno.land/std@0.209.0/path/windows/mod.ts": "d7040f461465c2c21c1c68fc988ef0bdddd499912138cde3abf6ad60c7fb3814", - "https://deno.land/std@0.209.0/path/windows/normalize.ts": "9deebbf40c81ef540b7b945d4ccd7a6a2c5a5992f791e6d3377043031e164e69", - "https://deno.land/std@0.209.0/path/windows/normalize_glob.ts": "344ff5ed45430495b9a3d695567291e50e00b1b3b04ea56712a2acf07ab5c128", - "https://deno.land/std@0.209.0/path/windows/parse.ts": "120faf778fe1f22056f33ded069b68e12447668fcfa19540c0129561428d3ae5", - "https://deno.land/std@0.209.0/path/windows/relative.ts": "026855cd2c36c8f28f1df3c6fbd8f2449a2aa21f48797a74700c5d872b86d649", - "https://deno.land/std@0.209.0/path/windows/resolve.ts": "5ff441ab18a2346abadf778121128ee71bda4d0898513d4639a6ca04edca366b", - "https://deno.land/std@0.209.0/path/windows/separator.ts": "ae21f27015f10510ed1ac4a0ba9c4c9c967cbdd9d9e776a3e4967553c397bd5d", - "https://deno.land/std@0.209.0/path/windows/to_file_url.ts": "8e9ea9e1ff364aa06fa72999204229952d0a279dbb876b7b838b2b2fea55cce3", - "https://deno.land/std@0.209.0/path/windows/to_namespaced_path.ts": "e0f4d4a5e77f28a5708c1a33ff24360f35637ba6d8f103d19661255ef7bfd50d", - "https://deno.land/x/crypto@v0.10.1/src/utils/padding.ts": "544c51a471a413b15940bf08b285bc6a5db27796ff3cf240564f42701aba01dc", - "https://deno.land/x/scrypto@v1.0.0/scrypt.ts": "8db05b7f839f9442fbd2df1e7e93b7245a409abbf2a6a7ba28e71f8ff0ffbf33", - "https://esm.sh/@noble/ciphers@0.3.0/salsa": "ff5047605cf6dd373c798a3e25bf80b9f8af357f8c86b37d48deadb5a2386358", - "https://esm.sh/@worker-tools/html-rewriter@0.1.0-pre.17/base64": "22bfb6213b9a33cae123d2e2e12408b8b444f5bed393d5ff4f58f2788496e75d", - "https://esm.sh/@worker-tools/html-rewriter@0.1.0-pre.19/base64": "570a199915865df6052f59f0607aba88977dc9def8ecbf552e7cec8d87ffee13", - "https://esm.sh/aes-js@3.1.2": "cf70f934a0f95d7367aba5106e96c2b534e021984018f182afc447b8686ea7dd", - "https://esm.sh/base32-decode@1.0.0": "b7f14a77cf96fa7a2cfac43bc021ac4d3c6a0b36c3a0c766b9925d263efe4260", - "https://esm.sh/base32-encode@2.0.0": "62c8f3b227369f02c5bd947e8ab8ac26afab9ecdbfaf1698d0460644c9540367", - "https://esm.sh/v135/@noble/ciphers@0.3.0/denonext/_poly1305.js": "39ed82f0f0da32d5e9ba2e8711ad56c06eca0e507aada796c672e996977e339a", - "https://esm.sh/v135/@noble/ciphers@0.3.0/denonext/salsa.js": "664cc1a73eef150761fca727fb3eceee977f31ad99273907aae6eec8c04d0f47", - "https://esm.sh/v135/@noble/ciphers@0.3.0/denonext/utils.js": "d358fb8bea4cc6eb63664fd0c48873487736517293f08ea42553df2112b43f74", - "https://esm.sh/v135/@stardazed/streams-compression@1.0.0/denonext/streams-compression.mjs": "dcbe4a6123222a1757f1fdb5930aeabc3c34353d403166a31178202bc820a9c7", - "https://esm.sh/v135/@stardazed/zlib@1.0.1/denonext/zlib.mjs": "adfa562e41b5eee1391ade5516720ffdf3be3815b70d9d0b9bba14c1ba8e6ad8", - "https://esm.sh/v135/@worker-tools/html-rewriter@0.1.0-pre.17/denonext/base64.js": "aa3a6b6c5cf5e2e53392fca48805a801796538ee5418e1e69352f57edc30938f", - "https://esm.sh/v135/@worker-tools/html-rewriter@0.1.0-pre.19/denonext/base64.js": "ed3432de5fea3e1fbd63cf371223560dab39f7f453f033c0a9bb6a2eba24bf43", - "https://esm.sh/v135/@worker-tools/resolvable-promise@0.2.0-pre.6/denonext/resolvable-promise.mjs": "fc5d118bf6f6bb8ed37d27dcb36efc713692ff41ed31d86f399aee632d7e6dfc", - "https://esm.sh/v135/aes-js@3.1.2/denonext/aes-js.mjs": "5c667dd96d94b8ec6790ad3256bd7c5dd92d6567289efb30197b9b5d8e3d6ed1", - "https://esm.sh/v135/base32-decode@1.0.0/denonext/base32-decode.mjs": "fc60335f713d9840046ad9cf6add6dc858b0ff16da838213763693e2ed324fc5", - "https://esm.sh/v135/base32-encode@2.0.0/denonext/base32-encode.mjs": "02bfcbb76fd80c79df47f54a953bc8bd779d347a48386aa36722f7a8efbf2403", - "https://esm.sh/v135/to-data-view@2.0.0/denonext/to-data-view.mjs": "35093e5fc0fe65237adf05d4e20f7244c10c84d94f2c2607d19fbef721c4bcc0", - "https://raw.githubusercontent.com/rclone/rclone/master/fs/rc/js/wasm_exec.js": "e6f7c7d25fceebf63cf20a139941877117f30262a0035c4727d1f70acbb1cfe7" + "remote": {}, + "workspace": { + "packageJson": { + "dependencies": [ + "npm:@jsr/sntran__html-rewriter@^0.1.1", + "npm:@jsr/std__assert@^1.0.0-rc.2", + "npm:@jsr/std__cli@^1.0.0-rc.1", + "npm:@jsr/std__crypto@^1.0.0-rc.1", + "npm:@jsr/std__encoding@^1.0.0-rc.2", + "npm:@jsr/std__fmt@^0.225.4", + "npm:@jsr/std__http@^0.224.5", + "npm:@jsr/std__ini@^0.225.1", + "npm:@jsr/std__media-types@^1.0.0-rc.1", + "npm:@jsr/std__path@^1.0.0-rc.2", + "npm:@noble/ciphers@^0.5.3", + "npm:@noble/hashes@^1.4.0", + "npm:aes-js@^3.1.2", + "npm:base32-decode@^1.0.0", + "npm:base32-encode@^2.0.0", + "npm:fast-check@^3.19.0" + ] + } } } diff --git a/deps.js b/deps.js index 3baa6f6..49e45b0 100644 --- a/deps.js +++ b/deps.js @@ -1,26 +1,32 @@ -export { parseArgs } from "https://deno.land/std@0.209.0/cli/parse_args.ts"; -export * as INI from "https://deno.land/std@0.209.0/ini/mod.ts"; -export { - basename, - extname, - join, - resolve, -} from "https://deno.land/std@0.209.0/path/mod.ts"; -export { getCookies } from "https://deno.land/std@0.209.0/http/cookie.ts"; -export { contentType } from "https://deno.land/std@0.209.0/media_types/mod.ts"; -export { format as formatBytes } from "https://deno.land/std@0.209.0/fmt/bytes.ts"; -export { format as formatDuration } from "https://deno.land/std@0.209.0/fmt/duration.ts"; - -export * as base64url from "https://deno.land/std@0.209.0/encoding/base64url.ts"; -export { encodeHex } from "https://deno.land/std@0.209.0/encoding/hex.ts"; -import { crypto } from "https://deno.land/std@0.209.0/crypto/mod.ts"; +import { platform } from "node:os"; +import { env } from "node:process"; + +export { parseArgs } from "@std/cli/parse-args"; +export * as INI from "@std/ini"; +export { basename, extname, join, resolve } from "@std/path"; +export { getCookies } from "@std/http/cookie"; +export { contentType } from "@std/media-types"; +export { format as formatBytes } from "@std/fmt/bytes"; +export { format as formatDuration } from "@std/fmt/duration"; + +export { encodeBase64Url, encodeHex } from "@std/encoding"; +import { crypto } from "@std/crypto"; + +export { default as decodeBase32 } from "base32-decode"; +export { default as encodeBase32 } from "base32-encode"; + +import aesjs from "aes-js"; +// `aes-js` is a CommonJS module, so we need to use `default` to import it. +const { AES } = aesjs; +export { AES }; + +export { xsalsa20poly1305 } from "@noble/ciphers/salsa"; +export { scryptAsync as scrypt } from "@noble/hashes/scrypt"; // Polyfills `HTMLRewriter`, which is available on Cloudflare Workers. // @ts-ignore: `HTMLRewriter` is not part of globalThis. if (!globalThis.HTMLRewriter) { - const { HTMLRewriter } = await import( - "https://esm.sh/@worker-tools/html-rewriter@0.1.0-pre.19/base64" - ); + const { HTMLRewriter } = await import("@sntran/html-rewriter"); // @ts-ignore: `HTMLRewriter` is not part of globalThis. globalThis.HTMLRewriter = HTMLRewriter; } @@ -32,7 +38,7 @@ export { HTMLRewriter }; /** * Calculates the hash of a file. * @param {string} path - * @param {import("https://deno.land/std@0.209.0/crypto/_wasm/mod.ts").DigestAlgorithm} [algorithm="MD5"] + * @param {import("@std/crypto").DigestAlgorithm} [algorithm=""] * @returns {string} */ async function digest(path, algorithm = "MD5") { @@ -49,19 +55,19 @@ export { crypto, digest }; * @returns {string | null} */ export function config_dir() { - switch (Deno.build.os) { + switch (platform()) { case "linux": case "darwin": { - const xdg = Deno.env.get("XDG_CONFIG_HOME"); + const xdg = env["XDG_CONFIG_HOME"]; if (xdg) return xdg; - const home = Deno.env.get("HOME"); + const home = env["HOME"]; if (home) return `${home}/.config`; break; } case "windows": - return Deno.env.get("APPDATA") ?? null; + return env["APPDATA"] ?? null; } return null; diff --git a/dev_deps.js b/dev_deps.js index e55bba3..6f68b7a 100644 --- a/dev_deps.js +++ b/dev_deps.js @@ -3,7 +3,7 @@ import { assertEquals, assertNotEquals, assertRejects, -} from "https://deno.land/std@0.203.0/assert/mod.ts"; +} from "@std/assert"; export { assert, assertEquals, assertNotEquals, assertRejects }; /** @@ -38,4 +38,4 @@ export function equalBytes(a, b) { return isSame; } -export * as fc from "npm:fast-check@3.13.1"; +export * as fc from "fast-check"; diff --git a/main.js b/main.js old mode 100644 new mode 100755 index 47abcd2..3883014 --- a/main.js +++ b/main.js @@ -10,309 +10,70 @@ * ./main.js copy source:/path/to/file destination:/path/to/file * ``` */ +import { argv, env, stdout } from "node:process"; +import { parseArgs } from "./deps.js"; -import * as backends from "./backend/main.js"; -import * as commands from "./cmd/main.js"; - -export * from "./cmd/main.js"; - -export { Progress } from "./lib/streams/progress.js"; - -/** - * @typedef {Object} Options - */ - -/** - * @typedef {(args: string[], options?: Options) => Promise} Command - */ - -/** - * @typedef {Options & { type: string }} Remote - */ +import * as commands from "./mod.js"; +/** All optional params for the subcommand as an object. */ /** - * File represents a file or directory. - * @typedef {Object} File - * @property {string} ID - * @property {string} OrigID - * @property {boolean} IsDir - * @property {string} MimeType - * @property {string} ModTime - * @property {string} Name - * @property {string} Encrypted - * @property {string} EncryptedPath - * @property {string} Path - * @property {number} Size - * @property {string} Tier - * @property {Object} Metadata + * @type {Options} */ - -const REMOTE_REGEX = - /^(:)?(?:([\w.][\w.\s-]*(?:,[\w=,"'*#.:@%-_\/]+)?):)?([^:]*)$/; - -const globalFetch = globalThis.fetch; - -/** - * Extended global `fetch` with support for remote URL. - * - * The syntax of the paths passed to the rclone command are as follows. - * - * `/path/to/dir`` - * - * This refers to the local file system. - * - * On Windows `\` may be used instead of `/` in local paths only, non local - * paths must use `/`. - * - * These paths needn't start with a leading `/` - if they don't then they - * will be relative to the current directory. - * - * `remote:path/to/dir` - * - * This refers to a directory `path/to/dir` on `remote:` as defined in the - * config. - * - * `remote:/path/to/dir` - * - * On most backends this refers to the same directory as `remote:path/to/dir` - * and that format should be preferred. On a very small number of remotes - * (FTP, SFTP, ..) this will refer to a different directory. On these, paths - * without a leading `/` will refer to your "home" directory and paths with a - * leading `/` will refer to the root. - * - * `:backend:path/to/dir` - * - * This is an advanced form for creating remotes on the fly. `backend` should - * be the name or prefix of a backend (the `type` in the config file) and all - * the configuration for the backend should be provided on the command line - * (or in environment variables). - * - * ## Precedence - * - * The various different methods of backend configuration are read in this - * order and the first one with a value is used. - * - * - Parameters in connection strings, e.g. `myRemote,skip_links:` - * - Flag values as supplied on the command line, e.g. `--skip-links` - * - Remote specific environment vars, e.g. `RCLONE_CONFIG_MYREMOTE_SKIP_LINKS` - * - Backend-specific environment vars, e.g. `RCLONE_LOCAL_SKIP_LINKS` - * - Backend generic environment vars, e.g. `RCLONE_SKIP_LINKS` - * - Config file, e.g. `skip_links = true`. - * - Default values, e.g. `false` - these can't be changed. - * - * So if both `--skip-links` is supplied on the command line and an environment - * variable `RCLONE_LOCAL_SKIP_LINKS` is set, the command line flag will take - * preference. - * - * For non backend configuration the order is as follows: - * - * - Flag values as supplied on the command line, e.g. `--stats 5s`. - * - Environment vars, e.g. `RCLONE_STATS=5s`. - * - Default values, e.g. `1m` - these can't be changed. - * - * ## Other environment variables - * - * @param {string | Request | URL} input - * @param { RequestInit} [init={}] - * @returns {Promise} - */ -export async function fetch(input, init = {}) { - if ( - typeof input !== "string" || input.startsWith("https://") || - input.startsWith("http://") || input.startsWith("file://") - ) { - return globalFetch(input, init); - } - - const [, colon, remote = "local", path] = input.match(REMOTE_REGEX) || []; - if (!remote && !path) { - return globalFetch(input, init); - } - - // The final params. - const params = new URLSearchParams(); - +const options = {}; + +const { + _: [subcommand = "help", ...args], + // @ts-ignore - Deno.args is not typed. +} = parseArgs(argv.slice(2), { + alias: { + "progress": "P", // Show progress during transfer + }, + boolean: [ + "dry-run", // Do a trial run with no permanent changes + "human-readable", // Print numbers in a human-readable format, sizes with suffix Ki|Mi|Gi|Ti|Pi + "progress", + ], + negatable: [ + "progress", + ], + string: [ + "_", + "header", + "header-download", + "header-upload", + "transfers", + ], + collect: [ + "header", + "header-download", + "header-upload", + ], + default: { + "dry-run": env["RCLONE_DRY_RUN"] === "true", + progress: env["RCLONE_PROGRESS"] === "true", + transfers: env["RCLONE_TRANSFERS"] || 4, + }, /** - * Sets backend generic environment vars first. + * Collects other optional params + * @param {string} _arg + * @param {string} [key] + * @param {string} [value] + * @returns {boolean | void} */ - const env = Deno?.env?.toObject() || {}; - for (const [key, value] of Object.entries(env)) { - if (key.startsWith("RCLONE_")) { - const shortKey = key.slice(7).toLowerCase(); - params.set(shortKey, value); + unknown: (_arg, key, value) => { + if (key) { // key is the flag name + options[key.replace(/-/g, "_")] = `${value}`; + return false; } - } - - // Remote can be connection string with arguments separated by commas. - let [name, ...args] = remote.split(","); - - /** - * @type {Remote} - */ - let config; - - if (colon || remote === "local") { // the location has format `:type:path/to/file` - config = { type: name }; - name = ""; - } else { - const response = await commands.config("show", name, undefined, { - headers: { "Accept": "application/json" }, - }); - if (!response.ok) { - throw new Error(`Remote ${name} not found in config.`); - } - config = await response.json(); - } - - const type = config.type; - delete config.type; - - // Stores config into params if not already set, with lowest precedence. - Object.entries(config).forEach(([key, value]) => { - if (!params.has(key)) { - params.set(key, value); - } - }); - - // Overrides with backend-specific environment vars. - let envPrefix = `RCLONE_${type.toUpperCase()}_`; - for (const [key, value] of Object.entries(env)) { - if (key.startsWith(envPrefix)) { - const shortKey = key.slice(envPrefix.length).toLowerCase(); - params.set(shortKey, value); - - // Deletes any params that are already set by environment vars. - params.delete(`${type}_${shortKey}`); - } - } - - // Overrides with remote specific environment vars - envPrefix = `RCLONE_CONFIG_${name.toUpperCase()}_`; - for (const [key, value] of Object.entries(env)) { - if (key.startsWith(envPrefix)) { - const shortKey = key.slice(envPrefix.length).toLowerCase(); - params.set(shortKey, value); - - // Deletes any params that are already set by environment vars. - params.delete(`config_${name}_${shortKey}`); - } - } - - // `searchParams` may contain other flags not for the current backend. - const { pathname, searchParams } = new URL(path, "file:"); - - // Overrides with flags in request's search params. - searchParams.forEach((value, key) => { - if (key.startsWith(`${type}_`)) { - params.set(key.replace(`${type}_`, ""), value); - } - }); - - // Overrides with parameters in connection string. - args.forEach((arg) => { - const [key, value = "true"] = arg.split("="); - // `value` can be encoded through URLSearchParams. - params.set(key, decodeURIComponent(value)); - }); - - const headers = new Headers(init.headers); - - if (init.method === "TRACE") { - let body = `TRACE ${pathname}?${params} HTTP/1.1\r`; - // Reflects the request as response body. - for (const [name, value] of headers) { - body += `${name}: ${value}\r`; - } - return new Response(body, { - status: 200, - headers: { - "Content-Type": "message/http", - "Via": `${type}/1.1 ${name}`.trim(), - }, - }); - } - - if (init.body instanceof ReadableStream) { - // @ts-ignore: Must have `duplex` for streaming body - init.duplex = "half"; // Must set this for stream body. - } - - /** - * @type {import("./backend/main.js").Backend} - */ - const backend = backends[type]; - - const url = new URL(`${pathname}?${params}`, import.meta.url); - // Creates a new request with the initial init. - const request = new Request(url, init); - // Clones that request and updates the headers. - return backend.fetch( - new Request(request, { - headers, - }), - ); -} - -globalThis.fetch = fetch; - -if (import.meta.main) { - const { parseArgs } = await import("./deps.js"); - - /** All optional params for the subcommand as an object. */ - /** - * @type {Options} - */ - const options = {}; - - const { - _: [subcommand = "help", ...args], - // @ts-ignore - Deno.args is not typed. - } = parseArgs(Deno.args, { - alias: { - "progress": "P", // Show progress during transfer - }, - boolean: [ - "dry-run", // Do a trial run with no permanent changes - "human-readable", // Print numbers in a human-readable format, sizes with suffix Ki|Mi|Gi|Ti|Pi - "progress", - ], - negatable: [ - "progress", - ], - string: [ - "_", - "header", - "header-download", - "header-upload", - "transfers", - ], - collect: [ - "header", - "header-download", - "header-upload", - ], - default: { - "dry-run": Deno.env.get("RCLONE_DRY_RUN ") === "true", - progress: Deno.env.get("RCLONE_PROGRESS") === "true", - transfers: Deno.env.get("RCLONE_TRANSFERS") || 4, - }, - /** - * Collects other optional params - * @param {string} _arg - * @param {string} [key] - * @param {string} [value] - * @returns {boolean | void} - */ - unknown: (_arg, key, value) => { - if (key) { // key is the flag name - options[key.replace(/-/g, "_")] = `${value}`; - return false; - } - }, - }); + }, +}); +(async () => { /** TODO merge global flags into config */ - // @ts-ignore valid arguments. const response = await commands[subcommand](...args, options); - response.body?.pipeTo(Deno.stdout.writable); -} + if (response.body) { + for await (const chunk of response.body) { + stdout.write(chunk); + } + } +})(); diff --git a/mod.js b/mod.js index b59bf04..1e27073 100644 --- a/mod.js +++ b/mod.js @@ -1 +1,243 @@ -export * from "./main.js"; +import { env } from "node:process"; +import * as backends from "./backend/main.js"; +import * as commands from "./cmd/main.js"; + +export * from "./cmd/main.js"; + +export { Progress } from "./lib/streams/progress.js"; + +/** + * @typedef {Object} Options + */ + +/** + * @typedef {(args: string[], options?: Options) => Promise} Command + */ + +/** + * @typedef {Options & { type: string }} Remote + */ + +/** + * File represents a file or directory. + * @typedef {Object} File + * @property {string} ID + * @property {string} OrigID + * @property {boolean} IsDir + * @property {string} MimeType + * @property {string} ModTime + * @property {string} Name + * @property {string} Encrypted + * @property {string} EncryptedPath + * @property {string} Path + * @property {number} Size + * @property {string} Tier + * @property {Object} Metadata + */ + +const REMOTE_REGEX = + /^(:)?(?:([\w.][\w.\s-]*(?:,[\w=,"'*#.:@%-_\/]+)?):)?([^:]*)$/; + +const globalFetch = globalThis.fetch; + +/** + * Extended global `fetch` with support for remote URL. + * + * The syntax of the paths passed to the rclone command are as follows. + * + * `/path/to/dir`` + * + * This refers to the local file system. + * + * On Windows `\` may be used instead of `/` in local paths only, non local + * paths must use `/`. + * + * These paths needn't start with a leading `/` - if they don't then they + * will be relative to the current directory. + * + * `remote:path/to/dir` + * + * This refers to a directory `path/to/dir` on `remote:` as defined in the + * config. + * + * `remote:/path/to/dir` + * + * On most backends this refers to the same directory as `remote:path/to/dir` + * and that format should be preferred. On a very small number of remotes + * (FTP, SFTP, ..) this will refer to a different directory. On these, paths + * without a leading `/` will refer to your "home" directory and paths with a + * leading `/` will refer to the root. + * + * `:backend:path/to/dir` + * + * This is an advanced form for creating remotes on the fly. `backend` should + * be the name or prefix of a backend (the `type` in the config file) and all + * the configuration for the backend should be provided on the command line + * (or in environment variables). + * + * ## Precedence + * + * The various different methods of backend configuration are read in this + * order and the first one with a value is used. + * + * - Parameters in connection strings, e.g. `myRemote,skip_links:` + * - Flag values as supplied on the command line, e.g. `--skip-links` + * - Remote specific environment vars, e.g. `RCLONE_CONFIG_MYREMOTE_SKIP_LINKS` + * - Backend-specific environment vars, e.g. `RCLONE_LOCAL_SKIP_LINKS` + * - Backend generic environment vars, e.g. `RCLONE_SKIP_LINKS` + * - Config file, e.g. `skip_links = true`. + * - Default values, e.g. `false` - these can't be changed. + * + * So if both `--skip-links` is supplied on the command line and an environment + * variable `RCLONE_LOCAL_SKIP_LINKS` is set, the command line flag will take + * preference. + * + * For non backend configuration the order is as follows: + * + * - Flag values as supplied on the command line, e.g. `--stats 5s`. + * - Environment vars, e.g. `RCLONE_STATS=5s`. + * - Default values, e.g. `1m` - these can't be changed. + * + * ## Other environment variables + * + * @param {string | Request | URL} input + * @param { RequestInit} [init={}] + * @returns {Promise} + */ +export async function fetch(input, init = {}) { + if ( + typeof input !== "string" || input.startsWith("https://") || + input.startsWith("http://") || input.startsWith("file://") + ) { + return globalFetch(input, init); + } + + const [, colon, remote = "local", path] = input.match(REMOTE_REGEX) || []; + if (!remote && !path) { + return globalFetch(input, init); + } + + // The final params. + const params = new URLSearchParams(); + + /** + * Sets backend generic environment vars first. + */ + for (const [key, value] of Object.entries(env)) { + if (key.startsWith("RCLONE_")) { + const shortKey = key.slice(7).toLowerCase(); + params.set(shortKey, value); + } + } + + // Remote can be connection string with arguments separated by commas. + let [name, ...args] = remote.split(","); + + /** + * @type {Remote} + */ + let config; + + if (colon || remote === "local") { // the location has format `:type:path/to/file` + config = { type: name }; + name = ""; + } else { + const response = await commands.config("show", name, undefined, { + headers: { "Accept": "application/json" }, + }); + if (!response.ok) { + throw new Error(`Remote ${name} not found in config.`); + } + config = await response.json(); + } + + const type = config.type; + delete config.type; + + // Stores config into params if not already set, with lowest precedence. + Object.entries(config).forEach(([key, value]) => { + if (!params.has(key)) { + params.set(key, value); + } + }); + + // Overrides with backend-specific environment vars. + let envPrefix = `RCLONE_${type.toUpperCase()}_`; + for (const [key, value] of Object.entries(env)) { + if (key.startsWith(envPrefix)) { + const shortKey = key.slice(envPrefix.length).toLowerCase(); + params.set(shortKey, value); + + // Deletes any params that are already set by environment vars. + params.delete(`${type}_${shortKey}`); + } + } + + // Overrides with remote specific environment vars + envPrefix = `RCLONE_CONFIG_${name.toUpperCase()}_`; + for (const [key, value] of Object.entries(env)) { + if (key.startsWith(envPrefix)) { + const shortKey = key.slice(envPrefix.length).toLowerCase(); + params.set(shortKey, value); + + // Deletes any params that are already set by environment vars. + params.delete(`config_${name}_${shortKey}`); + } + } + + // `searchParams` may contain other flags not for the current backend. + const { pathname, searchParams } = new URL(path, "file:"); + + // Overrides with flags in request's search params. + searchParams.forEach((value, key) => { + if (key.startsWith(`${type}_`)) { + params.set(key.replace(`${type}_`, ""), value); + } + }); + + // Overrides with parameters in connection string. + args.forEach((arg) => { + const [key, value = "true"] = arg.split("="); + // `value` can be encoded through URLSearchParams. + params.set(key, decodeURIComponent(value)); + }); + + const headers = new Headers(init.headers); + + if (init.method === "TRACE") { + let body = `TRACE ${pathname}?${params} HTTP/1.1\r`; + // Reflects the request as response body. + for (const [name, value] of headers) { + body += `${name}: ${value}\r`; + } + return new Response(body, { + status: 200, + headers: { + "Content-Type": "message/http", + "Via": `${type}/1.1 ${name}`.trim(), + }, + }); + } + + if (init.body instanceof ReadableStream) { + // @ts-ignore: Must have `duplex` for streaming body + init.duplex = "half"; // Must set this for stream body. + } + + /** + * @type {import("./backend/main.js").Backend} + */ + const backend = backends[type]; + + const url = new URL(`${pathname}?${params}`, import.meta.url); + // Creates a new request with the initial init. + const request = new Request(url, init); + // Clones that request and updates the headers. + return backend.fetch( + new Request(request, { + headers, + }), + ); +} + +globalThis.fetch = fetch; diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..716b5d0 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,271 @@ +{ + "name": "denolcr", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "denolcr", + "version": "0.1.0", + "license": "ISC", + "dependencies": { + "@noble/ciphers": "^0.5.3", + "@noble/hashes": "^1.4.0", + "@sntran/html-rewriter": "npm:@jsr/sntran__html-rewriter@^0.1.1", + "@std/cli": "npm:@jsr/std__cli@^1.0.0-rc.1", + "@std/crypto": "npm:@jsr/std__crypto@^1.0.0-rc.1", + "@std/encoding": "npm:@jsr/std__encoding@^1.0.0-rc.2", + "@std/fmt": "npm:@jsr/std__fmt@^0.225.4", + "@std/http": "npm:@jsr/std__http@^0.224.5", + "@std/ini": "npm:@jsr/std__ini@^0.225.1", + "@std/media-types": "npm:@jsr/std__media-types@^1.0.0-rc.1", + "@std/path": "npm:@jsr/std__path@^1.0.0-rc.2", + "aes-js": "^3.1.2", + "base32-decode": "^1.0.0", + "base32-encode": "^2.0.0" + }, + "bin": { + "denolcr": "main.js" + }, + "devDependencies": { + "@std/assert": "npm:@jsr/std__assert@^1.0.0-rc.2", + "fast-check": "^3.19.0" + }, + "engines": { + "deno": ">=1.44.0", + "node": ">=22.2.0" + } + }, + "node_modules/@jsr/std__async": { + "version": "1.0.0-rc.2", + "resolved": "https://npm.jsr.io/~/11/@jsr/std__async/1.0.0-rc.2.tgz", + "integrity": "sha512-mW3WDco4WkcsVhuwWRRRMSOW2odyXb1O80TMnfFjJZbxmlVXZKZmR9RkJvSQv8BcxopdZ3JCigkHNEmtRFsTXQ==" + }, + "node_modules/@jsr/std__bytes": { + "version": "1.0.0-rc.3", + "resolved": "https://npm.jsr.io/~/11/@jsr/std__bytes/1.0.0-rc.3.tgz", + "integrity": "sha512-4cHQrAO+8e6YcJVNHiPvkX9LVPhy4ugiatYUZnZ5kO1JG2dZia6wu9dYEsL6pInUQuBx9tFcPIowc0YmeNP+CQ==" + }, + "node_modules/@jsr/std__cli": { + "version": "0.224.7", + "resolved": "https://npm.jsr.io/~/11/@jsr/std__cli/0.224.7.tgz", + "integrity": "sha512-SSE7AMT/nqTUKeQXB+PZcmD6L5IjsOFDQdm88Ik2o6P4gS+gXewLMPZJu/X/gaOH5ux2C4xZ26Dgp3vCAIuB4g==" + }, + "node_modules/@jsr/std__encoding": { + "version": "1.0.0-rc.2", + "resolved": "https://npm.jsr.io/~/11/@jsr/std__encoding/1.0.0-rc.2.tgz", + "integrity": "sha512-ISvxD+exWVg/aESN3G1Tr0oszHRgUEEK1JjiRcuyCtd1uqSGXXMAoswYlYvPwA6mxZ7FQbFSZEbP3tN2J+fIUQ==" + }, + "node_modules/@jsr/std__fmt": { + "version": "0.225.4", + "resolved": "https://npm.jsr.io/~/11/@jsr/std__fmt/0.225.4.tgz", + "integrity": "sha512-lb8QcV3YnAu3GsrG7wd51Fb56RxOBp4szMRS/TCLnQptmF0Rd0v8ksQeTHmGkuD6MY/V8gEy/Sj0qulRvR9lqQ==" + }, + "node_modules/@jsr/std__internal": { + "version": "1.0.0", + "resolved": "https://npm.jsr.io/~/11/@jsr/std__internal/1.0.0.tgz", + "integrity": "sha512-6kp0FsXcHW4QvGYpOPAtglYSh/813OYKBP7RlH8VCaMyKwnWcRvcH+fhVc92RQ1a39Gh2XDwD6WGRYaFXZJxGw==", + "dev": true + }, + "node_modules/@jsr/std__io": { + "version": "0.224.2", + "resolved": "https://npm.jsr.io/~/11/@jsr/std__io/0.224.2.tgz", + "integrity": "sha512-7IbtJWWM13TaXkZR3jL0LLkAbEaublLR6WSMZuzg58IACDGnzcmxKlI57KjEBTuawX4u6x7g8f6zRleuRpSXVA==", + "dependencies": { + "@jsr/std__bytes": "^1.0.0-rc.3" + } + }, + "node_modules/@jsr/std__media-types": { + "version": "1.0.0-rc.1", + "resolved": "https://npm.jsr.io/~/11/@jsr/std__media-types/1.0.0-rc.1.tgz", + "integrity": "sha512-0UdhqMLempJi8rHXJ4xSsRs79gMIm+X8/jAcBxUa9sdbsRALM148t/c2JIRWN4S58LQVBRHNK3WYd/YgR4fLNA==" + }, + "node_modules/@jsr/std__net": { + "version": "0.224.3", + "resolved": "https://npm.jsr.io/~/11/@jsr/std__net/0.224.3.tgz", + "integrity": "sha512-67B6bpkMjNSWtEPLy/k4OpuCb65w+L0mf9Amp1E2uUs4nt31vZZazW8IUykE9+1sv0tDVxa8nFZ2p9VwdiLXaA==" + }, + "node_modules/@jsr/std__path": { + "version": "1.0.0-rc.2", + "resolved": "https://npm.jsr.io/~/11/@jsr/std__path/1.0.0-rc.2.tgz", + "integrity": "sha512-qUHfVMqAtMGPFmdkOfmkCONqgpe4UFVAaD+wL6ZdlQxtvQiNxHQWsje+djri+0095s/kkoBOPH4CpYxBq+zh3A==" + }, + "node_modules/@jsr/std__streams": { + "version": "0.224.5", + "resolved": "https://npm.jsr.io/~/11/@jsr/std__streams/0.224.5.tgz", + "integrity": "sha512-LWs0LfBcyj4VzE3ff1cwoEyJ3AkoIT3CQubxeA0YQr3KEC/c6xB9Rq9nVit8gZ8RgtbMDlyfPtN1MkwTgPFj+g==", + "dependencies": { + "@jsr/std__bytes": "^1.0.0-rc.3", + "@jsr/std__io": "^0.224.1" + } + }, + "node_modules/@noble/ciphers": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-0.5.3.tgz", + "integrity": "sha512-B0+6IIHiqEs3BPMT0hcRmHvEj2QHOLu+uwt+tqDDeVd0oyVzh7BPrDcPjRnV1PV/5LaknXJJQvOuRGR0zQJz+w==", + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/hashes": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", + "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@sntran/html-rewriter": { + "name": "@jsr/sntran__html-rewriter", + "version": "0.1.1", + "resolved": "https://npm.jsr.io/~/11/@jsr/sntran__html-rewriter/0.1.1.tgz", + "integrity": "sha512-0uGu8RAt3D2hEjTsprB+aLwLirn1kS8QIFslU7lwAFldClWArBPGy4LJSGB8IcQsLxdUegQofT7iBvzR3bNrSA==", + "dependencies": { + "html-rewriter-wasm": "0.4.1" + } + }, + "node_modules/@std/assert": { + "name": "@jsr/std__assert", + "version": "1.0.0-rc.2", + "resolved": "https://npm.jsr.io/~/11/@jsr/std__assert/1.0.0-rc.2.tgz", + "integrity": "sha512-5ABr022bTpmYxOnbe9sIuxspk6HEmHOvifncPVigND27j+OHjvPwPRijsLnG/H9wZXS9CPeNRiHvrxw2WLGQiQ==", + "dev": true, + "dependencies": { + "@jsr/std__internal": "^1.0.0" + } + }, + "node_modules/@std/cli": { + "name": "@jsr/std__cli", + "version": "1.0.0-rc.1", + "resolved": "https://npm.jsr.io/~/11/@jsr/std__cli/1.0.0-rc.1.tgz", + "integrity": "sha512-4MdEB0lsJgK99k+ykHVzc0szoe4DPGETpv+fqgFhpj2ipkjOV8oV2QiSknXcbHps0CkvC70Mx/+W2AJrdtNORg==" + }, + "node_modules/@std/crypto": { + "name": "@jsr/std__crypto", + "version": "1.0.0-rc.1", + "resolved": "https://npm.jsr.io/~/11/@jsr/std__crypto/1.0.0-rc.1.tgz", + "integrity": "sha512-HdqxgBx468H2ePsUUEYh4dyABELe1qxt4r2qK4UAR4gBrLxPRMBmAkZD0E7n/kUYA7+LTtLZ7OOhjZoQ9PPgrQ==" + }, + "node_modules/@std/encoding": { + "name": "@jsr/std__encoding", + "version": "1.0.0-rc.2", + "resolved": "https://npm.jsr.io/~/11/@jsr/std__encoding/1.0.0-rc.2.tgz", + "integrity": "sha512-ISvxD+exWVg/aESN3G1Tr0oszHRgUEEK1JjiRcuyCtd1uqSGXXMAoswYlYvPwA6mxZ7FQbFSZEbP3tN2J+fIUQ==" + }, + "node_modules/@std/fmt": { + "name": "@jsr/std__fmt", + "version": "0.225.4", + "resolved": "https://npm.jsr.io/~/11/@jsr/std__fmt/0.225.4.tgz", + "integrity": "sha512-lb8QcV3YnAu3GsrG7wd51Fb56RxOBp4szMRS/TCLnQptmF0Rd0v8ksQeTHmGkuD6MY/V8gEy/Sj0qulRvR9lqQ==" + }, + "node_modules/@std/http": { + "name": "@jsr/std__http", + "version": "0.224.5", + "resolved": "https://npm.jsr.io/~/11/@jsr/std__http/0.224.5.tgz", + "integrity": "sha512-PomDgsxwBoCkqTCG7e1Sz4QCAgNFhEGf71GWNercppQlgKtzp6zxsDgcJ4+vrqHIq5W8L1eqJWiceNkUUkMZjQ==", + "dependencies": { + "@jsr/std__async": "^1.0.0-rc.1", + "@jsr/std__cli": "^0.224.7", + "@jsr/std__encoding": "1.0.0-rc.2", + "@jsr/std__fmt": "^0.225.4", + "@jsr/std__media-types": "^1.0.0-rc.1", + "@jsr/std__net": "^0.224.3", + "@jsr/std__path": "1.0.0-rc.2", + "@jsr/std__streams": "^0.224.5" + } + }, + "node_modules/@std/ini": { + "name": "@jsr/std__ini", + "version": "0.225.1", + "resolved": "https://npm.jsr.io/~/11/@jsr/std__ini/0.225.1.tgz", + "integrity": "sha512-K23bS0WRbZ2riDg9jQMnaJW4uW/KaQpFJZRmv8cGDL+7JhmMkI+yC3wm8zGxctggPR1+pxIXH0WmK44CbGkORQ==" + }, + "node_modules/@std/media-types": { + "name": "@jsr/std__media-types", + "version": "1.0.0-rc.1", + "resolved": "https://npm.jsr.io/~/11/@jsr/std__media-types/1.0.0-rc.1.tgz", + "integrity": "sha512-0UdhqMLempJi8rHXJ4xSsRs79gMIm+X8/jAcBxUa9sdbsRALM148t/c2JIRWN4S58LQVBRHNK3WYd/YgR4fLNA==" + }, + "node_modules/@std/path": { + "name": "@jsr/std__path", + "version": "1.0.0-rc.2", + "resolved": "https://npm.jsr.io/~/11/@jsr/std__path/1.0.0-rc.2.tgz", + "integrity": "sha512-qUHfVMqAtMGPFmdkOfmkCONqgpe4UFVAaD+wL6ZdlQxtvQiNxHQWsje+djri+0095s/kkoBOPH4CpYxBq+zh3A==" + }, + "node_modules/aes-js": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-3.1.2.tgz", + "integrity": "sha512-e5pEa2kBnBOgR4Y/p20pskXI74UEz7de8ZGVo58asOtvSVG5YAbJeELPZxOmt+Bnz3rX753YKhfIn4X4l1PPRQ==" + }, + "node_modules/base32-decode": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/base32-decode/-/base32-decode-1.0.0.tgz", + "integrity": "sha512-KNWUX/R7wKenwE/G/qFMzGScOgVntOmbE27vvc6GrniDGYb6a5+qWcuoXl8WIOQL7q0TpK7nZDm1Y04Yi3Yn5g==" + }, + "node_modules/base32-encode": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base32-encode/-/base32-encode-2.0.0.tgz", + "integrity": "sha512-mlmkfc2WqdDtMl/id4qm3A7RjW6jxcbAoMjdRmsPiwQP0ufD4oXItYMnPgVHe80lnAIy+1xwzhHE1s4FoIceSw==", + "dependencies": { + "to-data-view": "^2.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/fast-check": { + "version": "3.19.0", + "resolved": "https://registry.npmjs.org/fast-check/-/fast-check-3.19.0.tgz", + "integrity": "sha512-CO2JX/8/PT9bDGO1iXa5h5ey1skaKI1dvecERyhH4pp3PGjwd3KIjMAXEg79Ps9nclsdt4oPbfqiAnLU0EwrAQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "dependencies": { + "pure-rand": "^6.1.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/html-rewriter-wasm": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/html-rewriter-wasm/-/html-rewriter-wasm-0.4.1.tgz", + "integrity": "sha512-lNovG8CMCCmcVB1Q7xggMSf7tqPCijZXaH4gL6iE8BFghdQCbaY5Met9i1x2Ex8m/cZHDUtXK9H6/znKamRP8Q==" + }, + "node_modules/pure-rand": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", + "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ] + }, + "node_modules/to-data-view": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-data-view/-/to-data-view-2.0.0.tgz", + "integrity": "sha512-RGEM5KqlPHr+WVTPmGNAXNeFEmsBnlkxXaIfEpUYV0AST2Z5W1EGq9L/MENFrMMmL2WQr1wjkmZy/M92eKhjYA==", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..1e813fb --- /dev/null +++ b/package.json @@ -0,0 +1,48 @@ +{ + "name": "denolcr", + "version": "0.1.0", + "description": "Deno port of Rclone, rewriting functionality using Web API.", + "engines": { + "node": ">=22.2.0", + "deno": ">=1.44.4" + }, + "bin": { + "denolcr": "main.js" + }, + "main": "mod.js", + "type": "module", + "repository": { + "type": "git", + "url": "git+https://github.com/sntran/denolcr.git" + }, + "scripts": { + "start": "node main.js", + "test": "deno test -A --no-check --fail-fast" + }, + "author": "Trần Nguyễn Sơn", + "license": "ISC", + "bugs": { + "url": "https://github.com/sntran/denolcr/issues" + }, + "homepage": "https://github.com/sntran/denolcr#readme", + "dependencies": { + "@noble/ciphers": "^0.5.3", + "@noble/hashes": "^1.4.0", + "@sntran/html-rewriter": "npm:@jsr/sntran__html-rewriter@^0.1.1", + "@std/cli": "npm:@jsr/std__cli@^1.0.0-rc.1", + "@std/crypto": "npm:@jsr/std__crypto@^1.0.0-rc.1", + "@std/encoding": "npm:@jsr/std__encoding@^1.0.0-rc.2", + "@std/fmt": "npm:@jsr/std__fmt@^0.225.4", + "@std/http": "npm:@jsr/std__http@^0.224.5", + "@std/ini": "npm:@jsr/std__ini@^0.225.1", + "@std/media-types": "npm:@jsr/std__media-types@^1.0.0-rc.1", + "@std/path": "npm:@jsr/std__path@^1.0.0-rc.2", + "aes-js": "^3.1.2", + "base32-decode": "^1.0.0", + "base32-encode": "^2.0.0" + }, + "devDependencies": { + "@std/assert": "npm:@jsr/std__assert@^1.0.0-rc.2", + "fast-check": "^3.19.0" + } +}