From a45eae92e61cdf74fa0d903c256e386973f6b011 Mon Sep 17 00:00:00 2001 From: Derrick Hammer Date: Thu, 30 Jun 2022 16:13:15 -0400 Subject: [PATCH] *Add dist --- dist/index.d.ts | 15 + dist/index.js | 16421 +++++++++++++++++++++++++++++++++++++++++++++ dist/tester.d.ts | 1 + public/tester.js | 5199 ++++++++++++++ 4 files changed, 21636 insertions(+) create mode 100644 dist/index.d.ts create mode 100644 dist/index.js create mode 100644 dist/tester.d.ts create mode 100644 public/tester.js diff --git a/dist/index.d.ts b/dist/index.d.ts new file mode 100644 index 0000000..ddcb479 --- /dev/null +++ b/dist/index.d.ts @@ -0,0 +1,15 @@ +import { Page } from "puppeteer"; +import { errTuple } from "libskynet/dist"; +export declare const KERNEL_TEST_SUITE = "AQCPJ9WRzMpKQHIsPo8no3XJpUydcDCjw7VJy8lG1MCZ3g"; +export declare const KERNEL_HELPER_MODULE = "AQCoaLP6JexdZshDDZRQaIwN3B7DqFjlY7byMikR7u1IEA"; +export declare const TEST_KERNEL_SKLINK = "AQCw2_9rg0Fxuy8ky3pvLiDhcJTmAqthy1Buc7Frl2v2fA"; +export declare function generateSeedPhrase(): string; +export declare function login(page: Page, seed?: string): Promise; +export declare function loadTester(page: Page, port?: number): Promise; +declare class Tester { + private page; + constructor(page: Page); + callModule(id: string, method: string, data?: {}): Promise; +} +export declare const tester: (page: Page) => Tester; +export {}; diff --git a/dist/index.js b/dist/index.js new file mode 100644 index 0000000..705ad2b --- /dev/null +++ b/dist/index.js @@ -0,0 +1,16421 @@ +'use strict'; + +var require$$0$2 = require('path'); +var http$1 = require('node:http'); +var https = require('node:https'); +var zlib = require('node:zlib'); +var Stream = require('node:stream'); +var node_buffer = require('node:buffer'); +var node_util = require('node:util'); +var node_url = require('node:url'); +var node_net = require('node:net'); +require('node:fs'); +require('node:path'); +var require$$6 = require('crypto'); +var require$$0$3 = require('events'); +var require$$1$3 = require('util'); +var require$$2$2 = require('http'); +var require$$3 = require('url'); +var require$$1$1 = require('fs'); +var require$$1$2 = require('child_process'); +var require$$0$1 = require('os'); + +function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } + +var require$$0__default$1 = /*#__PURE__*/_interopDefaultLegacy(require$$0$2); +var http__default = /*#__PURE__*/_interopDefaultLegacy(http$1); +var https__default = /*#__PURE__*/_interopDefaultLegacy(https); +var zlib__default = /*#__PURE__*/_interopDefaultLegacy(zlib); +var Stream__default = /*#__PURE__*/_interopDefaultLegacy(Stream); +var require$$6__default = /*#__PURE__*/_interopDefaultLegacy(require$$6); +var require$$0__default$2 = /*#__PURE__*/_interopDefaultLegacy(require$$0$3); +var require$$1__default$2 = /*#__PURE__*/_interopDefaultLegacy(require$$1$3); +var require$$2__default = /*#__PURE__*/_interopDefaultLegacy(require$$2$2); +var require$$3__default = /*#__PURE__*/_interopDefaultLegacy(require$$3); +var require$$1__default = /*#__PURE__*/_interopDefaultLegacy(require$$1$1); +var require$$1__default$1 = /*#__PURE__*/_interopDefaultLegacy(require$$1$2); +var require$$0__default = /*#__PURE__*/_interopDefaultLegacy(require$$0$1); + +var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}; + +function getDefaultExportFromCjs (x) { + return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x; +} + +function getAugmentedNamespace(n) { + var f = n.default; + if (typeof f == "function") { + var a = function () { + return f.apply(this, arguments); + }; + a.prototype = f.prototype; + } else a = {}; + Object.defineProperty(a, '__esModule', {value: true}); + Object.keys(n).forEach(function (k) { + var d = Object.getOwnPropertyDescriptor(n, k); + Object.defineProperty(a, k, d.get ? d : { + enumerable: true, + get: function () { + return n[k]; + } + }); + }); + return a; +} + +var build = {}; + +// Blake2B, adapted from the reference implementation in RFC7693 +// Ported to Javascript by DC - https://github.com/dcposch +// Then ported to typescript by https://github.com/DavidVorick +// 64-bit unsigned addition +// Sets v[a,a+1] += v[b,b+1] +// v should be a Uint32Array +function ADD64AA(v, a, b) { + const o0 = v[a] + v[b]; + let o1 = v[a + 1] + v[b + 1]; + if (o0 >= 0x100000000) { + o1++; + } + v[a] = o0; + v[a + 1] = o1; +} +// 64-bit unsigned addition +// Sets v[a,a+1] += b +// b0 is the low 32 bits of b, b1 represents the high 32 bits +function ADD64AC(v, a, b0, b1) { + let o0 = v[a] + b0; + if (b0 < 0) { + o0 += 0x100000000; + } + let o1 = v[a + 1] + b1; + if (o0 >= 0x100000000) { + o1++; + } + v[a] = o0; + v[a + 1] = o1; +} +// Little-endian byte access +function B2B_GET32(arr, i) { + return arr[i] ^ (arr[i + 1] << 8) ^ (arr[i + 2] << 16) ^ (arr[i + 3] << 24); +} +// G Mixing function +// The ROTRs are inlined for speed +function B2B_G(a, b, c, d, ix, iy, m, v) { + const x0 = m[ix]; + const x1 = m[ix + 1]; + const y0 = m[iy]; + const y1 = m[iy + 1]; + ADD64AA(v, a, b); // v[a,a+1] += v[b,b+1] ... in JS we must store a uint64 as two uint32s + ADD64AC(v, a, x0, x1); // v[a, a+1] += x ... x0 is the low 32 bits of x, x1 is the high 32 bits + // v[d,d+1] = (v[d,d+1] xor v[a,a+1]) rotated to the right by 32 bits + let xor0 = v[d] ^ v[a]; + let xor1 = v[d + 1] ^ v[a + 1]; + v[d] = xor1; + v[d + 1] = xor0; + ADD64AA(v, c, d); + // v[b,b+1] = (v[b,b+1] xor v[c,c+1]) rotated right by 24 bits + xor0 = v[b] ^ v[c]; + xor1 = v[b + 1] ^ v[c + 1]; + v[b] = (xor0 >>> 24) ^ (xor1 << 8); + v[b + 1] = (xor1 >>> 24) ^ (xor0 << 8); + ADD64AA(v, a, b); + ADD64AC(v, a, y0, y1); + // v[d,d+1] = (v[d,d+1] xor v[a,a+1]) rotated right by 16 bits + xor0 = v[d] ^ v[a]; + xor1 = v[d + 1] ^ v[a + 1]; + v[d] = (xor0 >>> 16) ^ (xor1 << 16); + v[d + 1] = (xor1 >>> 16) ^ (xor0 << 16); + ADD64AA(v, c, d); + // v[b,b+1] = (v[b,b+1] xor v[c,c+1]) rotated right by 63 bits + xor0 = v[b] ^ v[c]; + xor1 = v[b + 1] ^ v[c + 1]; + v[b] = (xor1 >>> 31) ^ (xor0 << 1); + v[b + 1] = (xor0 >>> 31) ^ (xor1 << 1); +} +// Initialization Vector +const BLAKE2B_IV32 = new Uint32Array([ + 0xf3bcc908, 0x6a09e667, 0x84caa73b, 0xbb67ae85, 0xfe94f82b, 0x3c6ef372, 0x5f1d36f1, 0xa54ff53a, 0xade682d1, + 0x510e527f, 0x2b3e6c1f, 0x9b05688c, 0xfb41bd6b, 0x1f83d9ab, 0x137e2179, 0x5be0cd19, +]); +const SIGMA8 = [ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3, 11, 8, 12, + 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4, 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8, 9, 0, 5, 7, 2, 4, 10, + 15, 14, 1, 11, 12, 6, 8, 3, 13, 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9, 12, 5, 1, 15, 14, 13, 4, 10, 0, + 7, 6, 3, 9, 2, 8, 11, 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10, 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, + 1, 4, 10, 5, 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3, +]; +// These are offsets into a uint64 buffer. +// Multiply them all by 2 to make them offsets into a uint32 buffer, +// because this is Javascript and we don't have uint64s +const SIGMA82 = new Uint8Array(SIGMA8.map(function (x) { + return x * 2; +})); +// Compression function. 'last' flag indicates last block. +// Note we're representing 16 uint64s as 32 uint32s +function blake2bCompress(ctx, last) { + const v = new Uint32Array(32); + const m = new Uint32Array(32); + let i = 0; + // init work variables + for (i = 0; i < 16; i++) { + v[i] = ctx.h[i]; + v[i + 16] = BLAKE2B_IV32[i]; + } + // low 64 bits of offset + v[24] = v[24] ^ ctx.t; + v[25] = v[25] ^ (ctx.t / 0x100000000); + // high 64 bits not supported, offset may not be higher than 2**53-1 + // last block flag set ? + if (last) { + v[28] = ~v[28]; + v[29] = ~v[29]; + } + // get little-endian words + for (i = 0; i < 32; i++) { + m[i] = B2B_GET32(ctx.b, 4 * i); + } + // twelve rounds of mixing + for (i = 0; i < 12; i++) { + B2B_G(0, 8, 16, 24, SIGMA82[i * 16 + 0], SIGMA82[i * 16 + 1], m, v); + B2B_G(2, 10, 18, 26, SIGMA82[i * 16 + 2], SIGMA82[i * 16 + 3], m, v); + B2B_G(4, 12, 20, 28, SIGMA82[i * 16 + 4], SIGMA82[i * 16 + 5], m, v); + B2B_G(6, 14, 22, 30, SIGMA82[i * 16 + 6], SIGMA82[i * 16 + 7], m, v); + B2B_G(0, 10, 20, 30, SIGMA82[i * 16 + 8], SIGMA82[i * 16 + 9], m, v); + B2B_G(2, 12, 22, 24, SIGMA82[i * 16 + 10], SIGMA82[i * 16 + 11], m, v); + B2B_G(4, 14, 16, 26, SIGMA82[i * 16 + 12], SIGMA82[i * 16 + 13], m, v); + B2B_G(6, 8, 18, 28, SIGMA82[i * 16 + 14], SIGMA82[i * 16 + 15], m, v); + } + for (i = 0; i < 16; i++) { + ctx.h[i] = ctx.h[i] ^ v[i] ^ v[i + 16]; + } +} +// Creates a BLAKE2b hashing context +// Requires an output length between 1 and 64 bytes +function blake2bInit() { + // state, 'param block' + const ctx = { + b: new Uint8Array(128), + h: new Uint32Array(16), + t: 0, + c: 0, + outlen: 32, // output length in bytes + }; + // initialize hash state + for (let i = 0; i < 16; i++) { + ctx.h[i] = BLAKE2B_IV32[i]; + } + ctx.h[0] ^= 0x01010000 ^ 32; + return ctx; +} +// Updates a BLAKE2b streaming hash +// Requires hash context and Uint8Array (byte array) +function blake2bUpdate(ctx, input) { + for (let i = 0; i < input.length; i++) { + if (ctx.c === 128) { + // buffer full ? + ctx.t += ctx.c; // add counters + blake2bCompress(ctx, false); // compress (not last) + ctx.c = 0; // counter to zero + } + ctx.b[ctx.c++] = input[i]; + } +} +// Completes a BLAKE2b streaming hash +// Returns a Uint8Array containing the message digest +function blake2bFinal(ctx) { + ctx.t += ctx.c; // mark last block offset + while (ctx.c < 128) { + // fill up with zeros + ctx.b[ctx.c++] = 0; + } + blake2bCompress(ctx, true); // final block flag = 1 + // little endian convert and store + const out = new Uint8Array(ctx.outlen); + for (let i = 0; i < ctx.outlen; i++) { + out[i] = ctx.h[i >> 2] >> (8 * (i & 3)); + } + return out; +} +// Computes the blake2b hash of the input. Returns 32 bytes. +let blake2b = function (input) { + const ctx = blake2bInit(); + blake2bUpdate(ctx, input); + return blake2bFinal(ctx); +}; + +const defaultPortalList = ["https://siasky.net", "https://web3portal.com"]; + +// DICTIONARY_UNIQUE_PREFIX defines the number of characters that are +// guaranteed to be unique for each word in the dictionary. The seed code only +// looks at these three characters when parsing a word, allowing users to make +// substitutions for words if they prefer or find it easier to memorize. +const DICTIONARY_UNIQUE_PREFIX = 3; +// dictionary contains the word list for the mysky seed. +const dictionary = [ + "abbey", + "ablaze", + "abort", + "absorb", + "abyss", + "aces", + "aching", + "acidic", + "across", + "acumen", + "adapt", + "adept", + "adjust", + "adopt", + "adult", + "aerial", + "afar", + "affair", + "afield", + "afloat", + "afoot", + "afraid", + "after", + "agenda", + "agile", + "aglow", + "agony", + "agreed", + "ahead", + "aided", + "aisle", + "ajar", + "akin", + "alarms", + "album", + "alerts", + "alley", + "almost", + "aloof", + "alpine", + "also", + "alumni", + "always", + "amaze", + "ambush", + "amidst", + "ammo", + "among", + "amply", + "amused", + "anchor", + "angled", + "ankle", + "antics", + "anvil", + "apart", + "apex", + "aphid", + "aplomb", + "apply", + "archer", + "ardent", + "arena", + "argue", + "arises", + "army", + "around", + "arrow", + "ascend", + "aside", + "asked", + "asleep", + "aspire", + "asylum", + "atlas", + "atom", + "atrium", + "attire", + "auburn", + "audio", + "august", + "aunt", + "autumn", + "avatar", + "avidly", + "avoid", + "awful", + "awning", + "awoken", + "axes", + "axis", + "axle", + "aztec", + "azure", + "baby", + "bacon", + "badge", + "bailed", + "bakery", + "bamboo", + "banjo", + "basin", + "batch", + "bawled", + "bays", + "beer", + "befit", + "begun", + "behind", + "being", + "below", + "bested", + "bevel", + "beware", + "beyond", + "bias", + "bids", + "bikini", + "birth", + "bite", + "blip", + "boat", + "bodies", + "bogeys", + "boil", + "boldly", + "bomb", + "border", + "boss", + "both", + "bovine", + "boxes", + "broken", + "brunt", + "bubble", + "budget", + "buffet", + "bugs", + "bulb", + "bumper", + "bunch", + "butter", + "buying", + "buzzer", + "byline", + "bypass", + "cabin", + "cactus", + "cadets", + "cafe", + "cage", + "cajun", + "cake", + "camp", + "candy", + "casket", + "catch", + "cause", + "cease", + "cedar", + "cell", + "cement", + "cent", + "chrome", + "cider", + "cigar", + "cinema", + "circle", + "claim", + "click", + "clue", + "coal", + "cobra", + "cocoa", + "code", + "coffee", + "cogs", + "coils", + "colony", + "comb", + "cool", + "copy", + "cousin", + "cowl", + "cube", + "cuffs", + "custom", + "dads", + "daft", + "dagger", + "daily", + "damp", + "dapper", + "darted", + "dash", + "dating", + "dawn", + "dazed", + "debut", + "decay", + "deftly", + "deity", + "dented", + "depth", + "desk", + "devoid", + "dice", + "diet", + "digit", + "dilute", + "dime", + "dinner", + "diode", + "ditch", + "divers", + "dizzy", + "doctor", + "dodge", + "does", + "dogs", + "doing", + "donuts", + "dosage", + "dotted", + "double", + "dove", + "down", + "dozen", + "dreams", + "drinks", + "drunk", + "drying", + "dual", + "dubbed", + "dude", + "duets", + "duke", + "dummy", + "dunes", + "duplex", + "dusted", + "duties", + "dwarf", + "dwelt", + "dying", + "each", + "eagle", + "earth", + "easy", + "eating", + "echo", + "eden", + "edgy", + "edited", + "eels", + "eggs", + "eight", + "either", + "eject", + "elapse", + "elbow", + "eldest", + "eleven", + "elite", + "elope", + "else", + "eluded", + "emails", + "ember", + "emerge", + "emit", + "empty", + "energy", + "enigma", + "enjoy", + "enlist", + "enmity", + "enough", + "ensign", + "envy", + "epoxy", + "equip", + "erase", + "error", + "estate", + "etched", + "ethics", + "excess", + "exhale", + "exit", + "exotic", + "extra", + "exult", + "fading", + "faked", + "fall", + "family", + "fancy", + "fatal", + "faulty", + "fawns", + "faxed", + "fazed", + "feast", + "feel", + "feline", + "fences", + "ferry", + "fever", + "fewest", + "fiat", + "fibula", + "fidget", + "fierce", + "fight", + "films", + "firm", + "five", + "fixate", + "fizzle", + "fleet", + "flying", + "foamy", + "focus", + "foes", + "foggy", + "foiled", + "fonts", + "fossil", + "fowls", + "foxes", + "foyer", + "framed", + "frown", + "fruit", + "frying", + "fudge", + "fuel", + "fully", + "fuming", + "fungal", + "future", + "fuzzy", + "gables", + "gadget", + "gags", + "gained", + "galaxy", + "gambit", + "gang", + "gasp", + "gather", + "gauze", + "gave", + "gawk", + "gaze", + "gecko", + "geek", + "gels", + "germs", + "geyser", + "ghetto", + "ghost", + "giant", + "giddy", + "gifts", + "gills", + "ginger", + "girth", + "giving", + "glass", + "glide", + "gnaw", + "gnome", + "goat", + "goblet", + "goes", + "going", + "gone", + "gopher", + "gossip", + "gotten", + "gown", + "grunt", + "guest", + "guide", + "gulp", + "guru", + "gusts", + "gutter", + "guys", + "gypsy", + "gyrate", + "hairy", + "having", + "hawk", + "hazard", + "heels", + "hefty", + "height", + "hence", + "heron", + "hiding", + "hijack", + "hiker", + "hills", + "hinder", + "hippo", + "hire", + "hive", + "hoax", + "hobby", + "hockey", + "hold", + "honked", + "hookup", + "hope", + "hornet", + "hotel", + "hover", + "howls", + "huddle", + "huge", + "hull", + "humid", + "hunter", + "huts", + "hybrid", + "hyper", + "icing", + "icon", + "idiom", + "idled", + "idols", + "igloo", + "ignore", + "iguana", + "impel", + "incur", + "injury", + "inline", + "inmate", + "input", + "insult", + "invoke", + "ionic", + "irate", + "iris", + "irony", + "island", + "issued", + "itches", + "items", + "itself", + "ivory", + "jabbed", + "jaded", + "jagged", + "jailed", + "jargon", + "jaunt", + "jaws", + "jazz", + "jeans", + "jeers", + "jester", + "jewels", + "jigsaw", + "jingle", + "jive", + "jobs", + "jockey", + "jogger", + "joking", + "jolted", + "jostle", + "joyous", + "judge", + "juicy", + "july", + "jump", + "junk", + "jury", + "karate", + "keep", + "kennel", + "kept", + "kettle", + "king", + "kiosk", + "kisses", + "kiwi", + "knee", + "knife", + "koala", + "ladder", + "lagoon", + "lair", + "lakes", + "lamb", + "laptop", + "large", + "last", + "later", + "lava", + "layout", + "lazy", + "ledge", + "leech", + "left", + "legion", + "lemon", + "lesson", + "liar", + "licks", + "lids", + "lied", + "light", + "lilac", + "limits", + "linen", + "lion", + "liquid", + "listen", + "lively", + "loaded", + "locker", + "lodge", + "lofty", + "logic", + "long", + "lopped", + "losing", + "loudly", + "love", + "lower", + "loyal", + "lucky", + "lumber", + "lunar", + "lurk", + "lush", + "luxury", + "lymph", + "lynx", + "lyrics", + "macro", + "mailed", + "major", + "makeup", + "malady", + "mammal", + "maps", + "match", + "maul", + "mayor", + "maze", + "meant", + "memoir", + "menu", + "merger", + "mesh", + "metro", + "mews", + "mice", + "midst", + "mighty", + "mime", + "mirror", + "misery", + "moat", + "mobile", + "mocked", + "mohawk", + "molten", + "moment", + "money", + "moon", + "mops", + "morsel", + "mostly", + "mouth", + "mowing", + "much", + "muddy", + "muffin", + "mugged", + "mullet", + "mumble", + "muppet", + "mural", + "muzzle", + "myriad", + "myth", + "nagged", + "nail", + "names", + "nanny", + "napkin", + "nasty", + "navy", + "nearby", + "needed", + "neon", + "nephew", + "nerves", + "nestle", + "never", + "newt", + "nexus", + "nibs", + "niche", + "niece", + "nifty", + "nimbly", + "nobody", + "nodes", + "noises", + "nomad", + "noted", + "nouns", + "nozzle", + "nuance", + "nudged", + "nugget", + "null", + "number", + "nuns", + "nurse", + "nylon", + "oaks", + "oars", + "oasis", + "object", + "occur", + "ocean", + "odds", + "offend", + "often", + "okay", + "older", + "olive", + "omega", + "onion", + "online", + "onto", + "onward", + "oozed", + "opened", + "opus", + "orange", + "orbit", + "orchid", + "orders", + "organs", + "origin", + "oscar", + "otter", + "ouch", + "ought", + "ounce", + "oust", + "oval", + "oven", + "owed", + "owls", + "owner", + "oxygen", + "oyster", + "ozone", + "pact", + "pager", + "palace", + "paper", + "pastry", + "patio", + "pause", + "peeled", + "pegs", + "pencil", + "people", + "pepper", + "pests", + "petals", + "phase", + "phone", + "piano", + "picked", + "pierce", + "pimple", + "pirate", + "pivot", + "pixels", + "pizza", + "pledge", + "pliers", + "plus", + "poetry", + "point", + "poker", + "polar", + "ponies", + "pool", + "potato", + "pouch", + "powder", + "pram", + "pride", + "pruned", + "prying", + "public", + "puck", + "puddle", + "puffin", + "pulp", + "punch", + "puppy", + "purged", + "push", + "putty", + "pylons", + "python", + "queen", + "quick", + "quote", + "radar", + "rafts", + "rage", + "raking", + "rally", + "ramped", + "rapid", + "rarest", + "rash", + "rated", + "ravine", + "rays", + "razor", + "react", + "rebel", + "recipe", + "reduce", + "reef", + "refer", + "reheat", + "relic", + "remedy", + "repent", + "reruns", + "rest", + "return", + "revamp", + "rewind", + "rhino", + "rhythm", + "ribbon", + "richly", + "ridges", + "rift", + "rigid", + "rims", + "riots", + "ripped", + "rising", + "ritual", + "river", + "roared", + "robot", + "rodent", + "rogue", + "roles", + "roomy", + "roped", + "roster", + "rotate", + "rover", + "royal", + "ruby", + "rudely", + "rugged", + "ruined", + "ruling", + "rumble", + "runway", + "rural", + "sack", + "safety", + "saga", + "sailor", + "sake", + "salads", + "sample", + "sanity", + "sash", + "satin", + "saved", + "scenic", + "school", + "scoop", + "scrub", + "scuba", + "second", + "sedan", + "seeded", + "setup", + "sewage", + "sieve", + "silk", + "sipped", + "siren", + "sizes", + "skater", + "skew", + "skulls", + "slid", + "slower", + "slug", + "smash", + "smog", + "snake", + "sneeze", + "sniff", + "snout", + "snug", + "soapy", + "sober", + "soccer", + "soda", + "soggy", + "soil", + "solved", + "sonic", + "soothe", + "sorry", + "sowed", + "soya", + "space", + "speedy", + "sphere", + "spout", + "sprig", + "spud", + "spying", + "square", + "stick", + "subtly", + "suede", + "sugar", + "summon", + "sunken", + "surfer", + "sushi", + "suture", + "swept", + "sword", + "swung", + "system", + "taboo", + "tacit", + "tagged", + "tail", + "taken", + "talent", + "tamper", + "tanks", + "tasked", + "tattoo", + "taunts", + "tavern", + "tawny", + "taxi", + "tell", + "tender", + "tepid", + "tether", + "thaw", + "thorn", + "thumbs", + "thwart", + "ticket", + "tidy", + "tiers", + "tiger", + "tilt", + "timber", + "tinted", + "tipsy", + "tirade", + "tissue", + "titans", + "today", + "toffee", + "toilet", + "token", + "tonic", + "topic", + "torch", + "tossed", + "total", + "touchy", + "towel", + "toxic", + "toyed", + "trash", + "trendy", + "tribal", + "truth", + "trying", + "tubes", + "tucks", + "tudor", + "tufts", + "tugs", + "tulips", + "tunnel", + "turnip", + "tusks", + "tutor", + "tuxedo", + "twang", + "twice", + "tycoon", + "typist", + "tyrant", + "ugly", + "ulcers", + "umpire", + "uncle", + "under", + "uneven", + "unfit", + "union", + "unmask", + "unrest", + "unsafe", + "until", + "unveil", + "unwind", + "unzip", + "upbeat", + "update", + "uphill", + "upkeep", + "upload", + "upon", + "upper", + "urban", + "urgent", + "usage", + "useful", + "usher", + "using", + "usual", + "utmost", + "utopia", + "vague", + "vain", + "value", + "vane", + "vary", + "vats", + "vaults", + "vector", + "veered", + "vegan", + "vein", + "velvet", + "vessel", + "vexed", + "vials", + "victim", + "video", + "viking", + "violin", + "vipers", + "vitals", + "vivid", + "vixen", + "vocal", + "vogue", + "voice", + "vortex", + "voted", + "vowels", + "voyage", + "wade", + "waffle", + "waist", + "waking", + "wanted", + "warped", + "water", + "waxing", + "wedge", + "weird", + "went", + "wept", + "were", + "whale", + "when", + "whole", + "width", + "wield", + "wife", + "wiggle", + "wildly", + "winter", + "wiring", + "wise", + "wives", + "wizard", + "wobbly", + "woes", + "woken", + "wolf", + "woozy", + "worry", + "woven", + "wrap", + "wrist", + "wrong", + "yacht", + "yahoo", + "yanks", +]; + +var dictionary$1 = /*#__PURE__*/Object.freeze({ + __proto__: null, + DICTIONARY_UNIQUE_PREFIX: DICTIONARY_UNIQUE_PREFIX, + dictionary: dictionary +}); + +// tryStringify will try to turn the provided input into a string. If the input +// object is already a string, the input object will be returned. If the input +// object has a toString method, the toString method will be called. If that +// fails, we try to call JSON.stringify on the object. And if that fails, we +// set the return value to "[stringify failed]". +function tryStringify$1(obj) { + // Check for undefined input. + if (obj === undefined) { + return "[cannot stringify undefined input]"; + } + if (obj === null) { + return "[null]"; + } + // Parse the error into a string. + if (typeof obj === "string") { + return obj; + } + // Check if the object has a 'toString' method defined on it. To ensure + // that we don't crash or throw, check that the toString is a function, and + // also that the return value of toString is a string. + if (Object.prototype.hasOwnProperty.call(obj, "toString")) { + if (typeof obj.toString === "function") { + let str = obj.toString(); + if (typeof str === "string") { + return str; + } + } + } + // If the object does not have a custom toString, attempt to perform a + // JSON.stringify. + try { + return JSON.stringify(obj); + } + catch { + return "[stringify failed]"; + } +} + +// addContextToErr is a helper function that standardizes the formatting of +// adding context to an error. Within the world of go we discovered that being +// persistent about layering context onto errors is helpful when debugging, +// even though it often creates rather verbose error messages. +// +// addContextToErr will return null if the input err is null. +// +// NOTE: To protect against accidental situations where an Error type or some +// other type is provided instead of a string, we wrap both of the inputs with +// tryStringify before returning them. This prevents runtime failures. +function addContextToErr$1(err, context) { + if (err === null) { + err = "[no error provided]"; + } + err = tryStringify$1(err); + return tryStringify$1(context) + ": " + tryStringify$1(err); +} +// composeErr takes a series of inputs and composes them into a single string. +// Each element will be separated by a newline. If the input is not a string, +// it will be transformed into a string with JSON.stringify. +// +// Any object that cannot be stringified will be skipped, though an error will +// be logged. +function composeErr$1(...inputs) { + let result = ""; + let resultEmpty = true; + for (let i = 0; i < inputs.length; i++) { + if (inputs[i] === null) { + continue; + } + if (resultEmpty) { + resultEmpty = false; + } + else { + result += "\n"; + } + result += tryStringify$1(inputs[i]); + } + if (resultEmpty) { + return null; + } + return result; +} + +// Helper consts to make it easy to return empty values alongside errors. +const nu8$7 = new Uint8Array(0); +// b64ToBuf will take an untrusted base64 string and convert it into a +// Uin8Array, returning an error if the input is not valid base64. +function b64ToBuf(b64) { + // Check that the final string is valid base64. + let b64regex = /^[0-9a-zA-Z-_/+=]*$/; + if (!b64regex.test(b64)) { + return [nu8$7, "provided string is not valid base64"]; + } + // Swap any '-' characters for '+', and swap any '_' characters for '/' + // for use in the atob function. + b64 = b64.replace(/-/g, "+").replace(/_/g, "/"); + // Perform the conversion. + let binStr = atob(b64); + let len = binStr.length; + let buf = new Uint8Array(len); + for (let i = 0; i < len; i++) { + buf[i] = binStr.charCodeAt(i); + } + return [buf, null]; +} +// bufToHex takes a Uint8Array as input and returns the hex encoding of those +// bytes as a string. +function bufToHex(buf) { + return [...buf].map((x) => x.toString(16).padStart(2, "0")).join(""); +} +// bufToB64 will convert a Uint8Array to a base64 string with URL encoding and +// no padding characters. +function bufToB64$1(buf) { + let b64Str = btoa(String.fromCharCode.apply(null, buf)); + return b64Str.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, ""); +} +// bufToStr takes an ArrayBuffer as input and returns a text string. bufToStr +// will check for invalid characters. +function bufToStr(buf) { + try { + let text = new TextDecoder("utf-8", { fatal: true }).decode(buf); + return [text, null]; + } + catch (err) { + return ["", addContextToErr$1(err.toString(), "unable to decode ArrayBuffer to string")]; + } +} +// decodeBigint will take an 8 byte Uint8Array and decode it as a bigint. +function decodeBigint(buf) { + if (buf.length !== 8) { + return [0n, "a number is expected to be 8 bytes"]; + } + let num = 0n; + for (let i = 7; i >= 0; i--) { + num *= 256n; + num += BigInt(buf[i]); + } + return [num, null]; +} +// encodePrefixedBytes takes a Uint8Array as input and returns a Uint8Array +// that has the length prefixed as an 8 byte prefix. The input can be at most 4 +// GiB. +function encodePrefixedBytes(bytes) { + let len = bytes.length; + if (len > 4294968295) { + return [nu8$7, "input is too large to be encoded"]; + } + let buf = new ArrayBuffer(8 + len); + let view = new DataView(buf); + view.setUint32(0, len, true); + let uint8Bytes = new Uint8Array(buf); + uint8Bytes.set(bytes, 8); + return [uint8Bytes, null]; +} +// encodeU64 will encode a bigint in the range of a uint64 to an 8 byte +// Uint8Array. +function encodeU64$1(num) { + // Check the bounds on the bigint. + if (num < 0) { + return [nu8$7, "expected a positive integer"]; + } + if (num > 18446744073709551615n) { + return [nu8$7, "expected a number no larger than a uint64"]; + } + // Encode the bigint into a Uint8Array. + let encoded = new Uint8Array(8); + for (let i = 0; i < encoded.length; i++) { + let byte = Number(num & 0xffn); + encoded[i] = byte; + num = num >> 8n; + } + return [encoded, null]; +} +// hexToBuf takes an untrusted string as input, verifies that the string is +// valid hex, and then converts the string to a Uint8Array. +function hexToBuf(hex) { + // Check that the length makes sense. + if (hex.length % 2 != 0) { + return [nu8$7, "input has incorrect length"]; + } + // Check that all of the characters are legal. + let match = /[0-9A-Fa-f]*/g; + if (!match.test(hex)) { + return [nu8$7, "input has invalid character"]; + } + // Create the buffer and fill it. + let matches = hex.match(/.{1,2}/g); + if (matches === null) { + return [nu8$7, "input is incomplete"]; + } + let u8 = new Uint8Array(matches.map((byte) => parseInt(byte, 16))); + return [u8, null]; +} + +// Helper values for cleanly returning errors. +const nu8$6 = new Uint8Array(0); +// blake2bAddSubtreeToProofStack will add a subtree to a proof stack. +function blake2bAddSubtreeToProofStack(ps, subtreeRoot, subtreeHeight) { + // Input checking. + if (subtreeRoot.length !== 32) { + return "cannot add subtree because root is wrong length"; + } + // If the proofStack has no elements in it yet, add the subtree + // with no further checks. + if (ps.subtreeRoots.length === 0) { + ps.subtreeRoots.push(subtreeRoot); + ps.subtreeHeights.push(subtreeHeight); + return null; + } + // Check the height of the new subtree against the height of the smallest + // subtree in the proofStack. If the new subtree is larger, the subtree + // cannot be added. + let maxHeight = ps.subtreeHeights[ps.subtreeHeights.length - 1]; + if (subtreeHeight > maxHeight) { + return `cannot add a subtree that is taller ${subtreeHeight} than the smallest ${maxHeight} subtree in the stack`; + } + // If the new subtreeHeight is smaller than the max height, we can just + // append the subtree height without doing anything more. + if (subtreeHeight < maxHeight) { + ps.subtreeRoots.push(subtreeRoot); + ps.subtreeHeights.push(subtreeHeight); + return null; + } + // If the new subtree is the same height as the smallest subtree, we + // have to pull the smallest subtree out, combine it with the new + // subtree, and push the result. + let oldSTR = ps.subtreeRoots.pop(); + ps.subtreeHeights.pop(); // We already have the height. + let combinedRoot = new Uint8Array(65); + combinedRoot[0] = 1; + combinedRoot.set(oldSTR, 1); + combinedRoot.set(subtreeRoot, 33); + let newSubtreeRoot = blake2b(combinedRoot); + return blake2bAddSubtreeToProofStack(ps, newSubtreeRoot, subtreeHeight + 1n); +} +// blake2bAddLeafBytesToProofStack will add a leaf to a proof stack. +function blake2bAddLeafBytesToProofStack(ps, leafBytes) { + if (leafBytes.length !== 64) { + return "proofStack expects leafByte objects to be exactly 64 bytes"; + } + let taggedBytes = new Uint8Array(65); + taggedBytes.set(leafBytes, 1); + let subtreeRoot = blake2b(taggedBytes); + return blake2bAddSubtreeToProofStack(ps, subtreeRoot, 1n); +} +// blake2bProofStackRoot returns the final Merkle root of the data in the +// current proof stack. +function blake2bProofStackRoot(ps) { + // Input checking. + if (ps.subtreeRoots.length === 0) { + return [nu8$6, "cannot compute the Merkle root of an empty data set"]; + } + // Algorithm is pretty basic, start with the final tree, and then add + // it to the previous tree. Repeat until there are no more trees. + let baseSubtreeRoot = ps.subtreeRoots.pop(); + while (ps.subtreeRoots.length !== 0) { + let nextSubtreeRoot = ps.subtreeRoots.pop(); + let combinedRoot = new Uint8Array(65); + combinedRoot[0] = 1; + combinedRoot.set(baseSubtreeRoot, 1); + combinedRoot.set(nextSubtreeRoot, 33); + baseSubtreeRoot = blake2b(combinedRoot); + } + return [baseSubtreeRoot, null]; +} +// nextSubtreeHeight returns the height of the largest subtree that contains +// 'start', contains no elements prior to 'start', and also does not contain +// 'end'. +function nextSubtreeHeight(start, end) { + // Input checking. + if (end <= start) { + return [0n, 0n, `end (${end}) must be strictly larger than start (${start})`]; + } + // Merkle trees have a nice mathematical property that the largest tree + // which contains a particular node and no nodes prior to it will have + // a height that is equal to the number of trailing zeroes in the base + // 2 representation of the index of that node. + // + // We are exploiting that property to compute the 'idealTreeHeight'. If + // 'start' is zero, the ideal tree height will just keep counting up + // forever, so we cut it off at 53. + let idealTreeHeight = 1n; + let idealTreeSize = 1n; + // The conditional inside the loop tests if the next ideal tree size is + // acceptable. If it is, we increment the height and double the size. + while (start % (idealTreeSize * 2n) === 0n) { + idealTreeHeight++; + idealTreeSize = idealTreeSize * 2n; + } + // To compute the max tree height, we essentially just find the largest + // power of 2 that is smaller than or equal to the gap between start + // and end. + let maxTreeHeight = 1n; + let maxTreeSize = 1n; + let range = end - start + 1n; + while (maxTreeSize * 2n < range) { + maxTreeHeight++; + maxTreeSize = maxTreeSize * 2n; + } + // Return the smaller of the ideal height and the max height, as each + // of them is an upper bound on how large things are allowed to be. + if (idealTreeHeight < maxTreeHeight) { + return [idealTreeHeight, idealTreeSize, null]; + } + return [maxTreeHeight, maxTreeSize, null]; +} +// blake2bMerkleRoot computes the merkle root of the provided data using a leaf +// size of 64 bytes and blake2b as the hashing function. +function blake2bMerkleRoot(data) { + // Check that the input is an acceptable length. + if (data.length % 64 !== 0) { + return [nu8$6, "cannot take the merkle root of data that is not a multiple of 64 bytes"]; + } + // Compute the Merkle root. + let ps = { + subtreeRoots: [], + subtreeHeights: [], + }; + for (let i = 0; i < data.length; i += 64) { + blake2bAddLeafBytesToProofStack(ps, data.slice(i, i + 64)); + } + return blake2bProofStackRoot(ps); +} +// blake2bVerifySectorRangeProof will verify a merkle proof that the provided +// data exists within the provided sector at the provided range. +// +// NOTE: This implementation only handles a single range, but the transition to +// doing mulit-range proofs is not very large. The main reason I didn't extend +// this function was because it made the inputs a lot messier. The Sia merkle +// tree repo uses the same techniques and has the full implementation, use that +// as a reference if you need to extend this function to support multi-range +// proofs. +function blake2bVerifySectorRangeProof(root, data, rangeStart, rangeEnd, proof) { + // Verify the inputs. + if (root.length !== 32) { + return "provided root is not a blake2b sector root"; + } + if (rangeEnd <= rangeStart) { + return "provided has no data"; + } + if (rangeStart < 0n) { + return "cannot use negative ranges"; + } + if (rangeEnd > 4194304n) { + return "range is out of bounds"; + } + if (proof.length % 32 !== 0) { + return "merkle proof has invalid length"; + } + if (data.length !== Number(rangeEnd - rangeStart)) { + return "data length does not match provided range"; + } + if (data.length % 64 !== 0) { + return "data must have a multiple of 64 bytes"; + } + // We will consume proof elements until we get to the rangeStart of the + // data. + let ps = { + subtreeRoots: [], + subtreeHeights: [], + }; + let currentOffset = 0n; + let proofOffset = 0; + while (currentOffset < rangeStart) { + if (proof.length < proofOffset + 32) { + return "merkle proof has insufficient data"; + } + let [height, size, errNST] = nextSubtreeHeight(currentOffset / 64n, rangeStart / 64n); + if (errNST !== null) { + return addContextToErr$1(errNST, "error computing subtree height of initial proof stack"); + } + let newSubtreeRoot = new Uint8Array(32); + newSubtreeRoot.set(proof.slice(proofOffset, proofOffset + 32), 0); + proofOffset += 32; + let errSPS = blake2bAddSubtreeToProofStack(ps, newSubtreeRoot, height); + if (errSPS !== null) { + return addContextToErr$1(errSPS, "error adding subtree to initial proof stack"); + } + currentOffset += size * 64n; + } + // We will consume data elements until we get to the end of the data. + let dataOffset = 0; + while (data.length > dataOffset) { + let errLBPS = blake2bAddLeafBytesToProofStack(ps, data.slice(dataOffset, dataOffset + 64)); + if (errLBPS !== null) { + return addContextToErr$1(errLBPS, "error adding leaves to proof stack"); + } + dataOffset += 64; + currentOffset += 64n; + } + // Consume proof elements until the entire sector is proven. + let sectorEnd = 4194304n; + while (currentOffset < sectorEnd) { + if (proof.length < proofOffset + 32) { + return "merkle proof has insufficient data"; + } + let [height, size, errNST] = nextSubtreeHeight(currentOffset / 64n, sectorEnd / 64n); + if (errNST !== null) { + return addContextToErr$1(errNST, "error computing subtree height of trailing proof stack"); + } + let newSubtreeRoot = new Uint8Array(32); + newSubtreeRoot.set(proof.slice(proofOffset, proofOffset + 32), 0); + proofOffset += 32; + let errSPS = blake2bAddSubtreeToProofStack(ps, newSubtreeRoot, height); + if (errSPS !== null) { + return addContextToErr$1(errSPS, "error adding subtree to trailing proof stack"); + } + currentOffset += size * 64n; + } + return null; +} + +// Helper consts to make it easier to return empty values in the event of an +// error. +const nu8$5 = new Uint8Array(0); +// verifyDownload will verify a download response from a portal. The input is +// essentially components of a skylink - the offset, length, and merkle root. +// The output is the raw file data. +// +// The 'buf' input should match the standard response body of a verified +// download request to a portal, which is the skylink raw data followed by a +// merkle proof. The offset and length provided as input indicate the offset +// and length of the skylink raw data - not the offset and length of the +// request within the file (that would be a different set of params). +// +// The skylink raw data itself breaks down into a metadata component and a file +// component. The metadata component will contain information like the length +// of the real file, and any fanout structure for large files. The first step +// we need to take is verifying the Merkel proof, which will appear at the end +// of the buffer. We'll have to hash the data we received and then compare it +// against the Merkle proof and ensure it matches the data we are expecting. +// Then we'll have to look at the layout to figure out which pieces of the data +// are the full file, while also checking for corruption as the file can be +// malicious independent of the portal operator. +// +// As long as the Merkle proof matches the root, offset, and length that we +// have as input, the portal is considered non-malicious. Any additional errors +// we find after that can be considered malice or incompetence on the part of +// the person who uploaded the file. +function verifyDownload(root, offset, fetchSize, buf) { + let u8 = new Uint8Array(buf); + // Input checking. If any of this is incorrect, its safe to blame the + // server because the skylink format fundamentally should enable these + // to be correct. + if (u8.length < fetchSize) { + return [nu8$5, true, "provided data is not large enough to cover fetchSize"]; + } + if (u8.length < 99) { + return [nu8$5, true, "provided data is not large enough to contain a skyfile"]; + } + // Grab the skylinkData and Merkle proof from the array, and then + // verify the Merkle proof. + let skylinkData = u8.slice(0, Number(fetchSize)); + let merkleProof = u8.slice(Number(fetchSize), u8.length); + let errVBSRP = blake2bVerifySectorRangeProof(root, skylinkData, offset, fetchSize, merkleProof); + if (errVBSRP !== null) { + return [nu8$5, true, addContextToErr$1(errVBSRP, "provided Merkle proof is not valid")]; + } + // Up until this point, an error indicated that the portal was at fault for + // either returning the wrong data or otherwise providing a malformed + // repsonse. The remaining checks relate to the consistency of the file + // itself, if the file is corrupt but the hash matches, there will be an + // error and the portal will not be at fault. + // The organization of the skylinkData is always: + // layoutBytes || fanoutBytes || metadataBytes || fileBytes + // + // The layout is always exactly 99 bytes. Bytes [1,8] of the layout + // contain the exact size of the fileBytes. Bytes [9, 16] of the layout + // contain the exact size of the metadata. And bytes [17,24] of the + // layout contain the exact size of the fanout. To get the offset of + // the fileData, we need to extract the sizes of the metadata and + // fanout, and then add those values to 99 to get the fileData offset. + let fileSizeBytes = skylinkData.slice(1, 9); + let mdSizeBytes = skylinkData.slice(9, 17); + let fanoutSizeBytes = skylinkData.slice(17, 25); + let [fileSize, errFSDN] = decodeBigint(fileSizeBytes); + if (errFSDN !== null) { + return [nu8$5, false, addContextToErr$1(errFSDN, "unable to decode filesize")]; + } + let [mdSize, errMDDN] = decodeBigint(mdSizeBytes); + if (errMDDN !== null) { + return [nu8$5, false, addContextToErr$1(errMDDN, "unable to decode metadata size")]; + } + let [fanoutSize, errFODN] = decodeBigint(fanoutSizeBytes); + if (errFODN !== null) { + return [nu8$5, false, addContextToErr$1(errFODN, "unable to decode fanout size")]; + } + if (BigInt(skylinkData.length) < 99n + fileSize + mdSize + fanoutSize) { + return [nu8$5, false, "provided data is too short to contain the full skyfile"]; + } + let fileData = skylinkData.slice(Number(99n + mdSize + fanoutSize), Number(99n + mdSize + fanoutSize + fileSize)); + return [fileData, false, null]; +} + +// @ts-nocheck +// json_parse extracted from the json-bigint npm library +// regexpxs extracted from +// (c) BSD-3-Clause +// https://github.com/fastify/secure-json-parse/graphs/contributors and https://github.com/hapijs/bourne/graphs/contributors +const suspectProtoRx = /(?:_|\\u005[Ff])(?:_|\\u005[Ff])(?:p|\\u0070)(?:r|\\u0072)(?:o|\\u006[Ff])(?:t|\\u0074)(?:o|\\u006[Ff])(?:_|\\u005[Ff])(?:_|\\u005[Ff])/; +const suspectConstructorRx = /(?:c|\\u0063)(?:o|\\u006[Ff])(?:n|\\u006[Ee])(?:s|\\u0073)(?:t|\\u0074)(?:r|\\u0072)(?:u|\\u0075)(?:c|\\u0063)(?:t|\\u0074)(?:o|\\u006[Ff])(?:r|\\u0072)/; +let json_parse = function (options) { + // This is a function that can parse a JSON text, producing a JavaScript + // data structure. It is a simple, recursive descent parser. It does not use + // eval or regular expressions, so it can be used as a model for implementing + // a JSON parser in other languages. + // We are defining the function inside of another function to avoid creating + // global variables. + // Default options one can override by passing options to the parse() + let _options = { + strict: false, + storeAsString: false, + alwaysParseAsBig: false, + protoAction: "error", + constructorAction: "error", + }; + // If there are options, then use them to override the default _options + if (options !== undefined && options !== null) { + if (options.strict === true) { + _options.strict = true; + } + if (options.storeAsString === true) { + _options.storeAsString = true; + } + _options.alwaysParseAsBig = options.alwaysParseAsBig === true ? options.alwaysParseAsBig : false; + if (typeof options.constructorAction !== "undefined") { + if (options.constructorAction === "error" || + options.constructorAction === "ignore" || + options.constructorAction === "preserve") { + _options.constructorAction = options.constructorAction; + } + else { + throw new Error(`Incorrect value for constructorAction option, must be "error", "ignore" or undefined but passed ${options.constructorAction}`); + } + } + if (typeof options.protoAction !== "undefined") { + if (options.protoAction === "error" || options.protoAction === "ignore" || options.protoAction === "preserve") { + _options.protoAction = options.protoAction; + } + else { + throw new Error(`Incorrect value for protoAction option, must be "error", "ignore" or undefined but passed ${options.protoAction}`); + } + } + } + let at, // The index of the current character + ch, // The current character + escapee = { + '"': '"', + "\\": "\\", + "/": "/", + b: "\b", + f: "\f", + n: "\n", + r: "\r", + t: "\t", + }, text, error = function (m) { + // Call error when something is wrong. + throw { + name: "SyntaxError", + message: m, + at: at, + text: text, + }; + }, next = function (c) { + // If a c parameter is provided, verify that it matches the current character. + if (c && c !== ch) { + error("Expected '" + c + "' instead of '" + ch + "'"); + } + // Get the next character. When there are no more characters, + // return the empty string. + ch = text.charAt(at); + at += 1; + return ch; + }, number = function () { + // Parse a number value. + let number, string = ""; + if (ch === "-") { + string = "-"; + next("-"); + } + while (ch >= "0" && ch <= "9") { + string += ch; + next(); + } + if (ch === ".") { + string += "."; + while (next() && ch >= "0" && ch <= "9") { + string += ch; + } + } + if (ch === "e" || ch === "E") { + string += ch; + next(); + if (ch === "-" || ch === "+") { + string += ch; + next(); + } + while (ch >= "0" && ch <= "9") { + string += ch; + next(); + } + } + number = +string; + if (!isFinite(number)) { + error("Bad number"); + } + else { + if (Number.isSafeInteger(number)) + return !_options.alwaysParseAsBig ? number : BigInt(number); + // Number with fractional part should be treated as number(double) including big integers in scientific notation, i.e 1.79e+308 + else + return _options.storeAsString ? string : /[.eE]/.test(string) ? number : BigInt(string); + } + }, string = function () { + // Parse a string value. + let hex, i, string = "", uffff; + // When parsing for string values, we must look for " and \ characters. + if (ch === '"') { + let startAt = at; + while (next()) { + if (ch === '"') { + if (at - 1 > startAt) + string += text.substring(startAt, at - 1); + next(); + return string; + } + if (ch === "\\") { + if (at - 1 > startAt) + string += text.substring(startAt, at - 1); + next(); + if (ch === "u") { + uffff = 0; + for (i = 0; i < 4; i += 1) { + hex = parseInt(next(), 16); + if (!isFinite(hex)) { + break; + } + uffff = uffff * 16 + hex; + } + string += String.fromCharCode(uffff); + } + else if (typeof escapee[ch] === "string") { + string += escapee[ch]; + } + else { + break; + } + startAt = at; + } + } + } + error("Bad string"); + }, white = function () { + // Skip whitespace. + while (ch && ch <= " ") { + next(); + } + }, word = function () { + // true, false, or null. + switch (ch) { + case "t": + next("t"); + next("r"); + next("u"); + next("e"); + return true; + case "f": + next("f"); + next("a"); + next("l"); + next("s"); + next("e"); + return false; + case "n": + next("n"); + next("u"); + next("l"); + next("l"); + return null; + } + error("Unexpected '" + ch + "'"); + }, value, // Place holder for the value function. + array = function () { + // Parse an array value. + let array = []; + if (ch === "[") { + next("["); + white(); + if (ch === "]") { + next("]"); + return array; // empty array + } + while (ch) { + array.push(value()); + white(); + if (ch === "]") { + next("]"); + return array; + } + next(","); + white(); + } + } + error("Bad array"); + }, object = function () { + // Parse an object value. + let key, object = Object.create(null); + if (ch === "{") { + next("{"); + white(); + if (ch === "}") { + next("}"); + return object; // empty object + } + while (ch) { + key = string(); + white(); + next(":"); + if (_options.strict === true && Object.hasOwnProperty.call(object, key)) { + error('Duplicate key "' + key + '"'); + } + if (suspectProtoRx.test(key) === true) { + if (_options.protoAction === "error") { + error("Object contains forbidden prototype property"); + } + else if (_options.protoAction === "ignore") { + value(); + } + else { + object[key] = value(); + } + } + else if (suspectConstructorRx.test(key) === true) { + if (_options.constructorAction === "error") { + error("Object contains forbidden constructor property"); + } + else if (_options.constructorAction === "ignore") { + value(); + } + else { + object[key] = value(); + } + } + else { + object[key] = value(); + } + white(); + if (ch === "}") { + next("}"); + return object; + } + next(","); + white(); + } + } + error("Bad object"); + }; + value = function () { + // Parse a JSON value. It could be an object, an array, a string, a number, + // or a word. + white(); + switch (ch) { + case "{": + return object(); + case "[": + return array(); + case '"': + return string(); + case "-": + return number(); + default: + return ch >= "0" && ch <= "9" ? number() : word(); + } + }; + // Return the json_parse function. It will have access to all of the above + // functions and variables. + return function (source, reviver) { + let result; + text = source + ""; + at = 0; + ch = " "; + result = value(); + white(); + if (ch) { + error("Syntax error"); + } + // If there is a reviver function, we recursively walk the new structure, + // passing each name/value pair to the reviver function for possible + // transformation, starting with a temporary root object that holds the result + // in an empty key. If there is not a reviver function, we simply return the + // result. + return typeof reviver === "function" + ? (function walk(holder, key) { + let v, value = holder[key]; + if (value && typeof value === "object") { + Object.keys(value).forEach(function (k) { + v = walk(value, k); + if (v !== undefined) { + value[k] = v; + } + else { + delete value[k]; + } + }); + } + return reviver.call(holder, key, value); + })({ "": result }, "") + : result; + }; +}; +// parseJSON is a wrapper for JSONbig.parse that returns an error rather than +// throwing an error. JSONbig is an alternative to JSON.parse that decodes +// every number as a bigint. This is required when working with the skyd API +// because the skyd API uses 64 bit precision for all of its numbers, and +// therefore cannot be parsed losslessly by javascript. The skyd API is +// cryptographic, therefore full precision is required. +function parseJSON(json) { + try { + let obj = json_parse({ alwaysParseAsBig: true })(json); + return [obj, null]; + } + catch (err) { + return [{}, tryStringify$1(err)]; + } +} + +// Helper consts that make it easier to return empty values when returning an +// error inside of a function. +const nu8$4 = new Uint8Array(0); +// parseSkylinkBitfield parses a skylink bitfield and returns the corresponding +// version, offset, and fetchSize. +function parseSkylinkBitfield(skylink) { + // Validate the input. + if (skylink.length !== 34) { + return [0n, 0n, 0n, "provided skylink has incorrect length"]; + } + // Extract the bitfield. + let bitfield = new DataView(skylink.buffer).getUint16(0, true); + // Extract the version. + let version = (bitfield & 3) + 1; + // Only versions 1 and 2 are recognized. + if (version !== 1 && version !== 2) { + return [0n, 0n, 0n, "provided skylink has unrecognized version"]; + } + // If the skylink is set to version 2, we only recognize the link if + // the rest of the bits in the bitfield are empty. + if (version === 2) { + if ((bitfield & 3) !== bitfield) { + return [0n, 0n, 0n, "provided skylink has unrecognized version"]; + } + return [BigInt(version), 0n, 0n, null]; + } + // Verify that the mode is valid, then fetch the mode. + bitfield = bitfield >> 2; + if ((bitfield & 255) === 255) { + return [0n, 0n, 0n, "provided skylink has an unrecognized version"]; + } + let mode = 0; + for (let i = 0; i < 8; i++) { + if ((bitfield & 1) === 0) { + bitfield = bitfield >> 1; + break; + } + bitfield = bitfield >> 1; + mode++; + } + // If the mode is greater than 7, this is not a valid v1 skylink. + if (mode > 7) { + return [0n, 0n, 0n, "provided skylink has an invalid v1 bitfield"]; + } + // Determine the offset and fetchSize increment. + let offsetIncrement = 4096 << mode; + let fetchSizeIncrement = 4096; + let fetchSizeStart = 0; + if (mode > 0) { + fetchSizeIncrement = fetchSizeIncrement << (mode - 1); + fetchSizeStart = (1 << 15) << (mode - 1); + } + // The next three bits decide the fetchSize. + let fetchSizeBits = bitfield & 7; + fetchSizeBits++; // semantic upstep, range should be [1,8] not [0,8). + let fetchSize = fetchSizeBits * fetchSizeIncrement + fetchSizeStart; + bitfield = bitfield >> 3; + // The remaining bits determine the offset. + let offset = bitfield * offsetIncrement; + if (offset + fetchSize > 1 << 22) { + return [0n, 0n, 0n, "provided skylink has an invalid v1 bitfield"]; + } + // Return what we learned. + return [BigInt(version), BigInt(offset), BigInt(fetchSize), null]; +} +// skylinkV1Bitfield sets the bitfield of a V1 skylink. It assumes the version +// is 1 and the offset is 0. It will determine the appropriate fetchSize from +// the provided dataSize. +function skylinkV1Bitfield(dataSizeBI) { + // Check that the dataSize is not too large. + if (dataSizeBI > 1 << 22) { + return [nu8$4, "dataSize must be less than the sector size"]; + } + let dataSize = Number(dataSizeBI); + // Determine the mode for the file. The mode is determined by the + // dataSize. + let mode = 0; + for (let i = 1 << 15; i < dataSize; i *= 2) { + mode += 1; + } + // Determine the download number. + let downloadNumber = 0; + if (mode === 0) { + if (dataSize !== 0) { + downloadNumber = Math.floor((dataSize - 1) / (1 << 12)); + } + } + else { + let step = 1 << (11 + mode); + let target = dataSize - (1 << (14 + mode)); + if (target !== 0) { + downloadNumber = Math.floor((target - 1) / step); + } + } + // Create the Uint8Array and fill it out. The main reason I switch over + // the 7 modes like this is because I wasn't sure how to make a uint16 + // in javascript. If we could treat the uint8array as a uint16 and then + // later convert it over, we could use basic bitshifiting and really + // simplify the code here. + let bitfield = new Uint8Array(2); + if (mode === 7) { + // 0 0 0 X X X 0 1|1 1 1 1 1 1 0 0 + bitfield[0] = downloadNumber; + bitfield[0] *= 4; + bitfield[0] += 1; + bitfield[1] = 4 + 8 + 16 + 32 + 64 + 128; + } + if (mode === 6) { + // 0 0 0 0 X X X 0|1 1 1 1 1 1 0 0 + bitfield[0] = downloadNumber; + bitfield[0] *= 2; + bitfield[1] = 4 + 8 + 16 + 32 + 64 + 128; + } + if (mode === 5) { + // 0 0 0 0 0 X X X|0 1 1 1 1 1 0 0 + bitfield[0] = downloadNumber; + bitfield[1] = 4 + 8 + 16 + 32 + 64; + } + if (mode === 4) { + // 0 0 0 0 0 0 X X|X 0 1 1 1 1 0 0 + bitfield[0] = downloadNumber; + bitfield[0] /= 2; + bitfield[1] = (downloadNumber & 1) * 128; + bitfield[1] += 4 + 8 + 16 + 32; + } + if (mode === 3) { + // 0 0 0 0 0 0 0 X|X X 0 1 1 1 0 0 + bitfield[0] = downloadNumber; + bitfield[0] /= 4; + bitfield[1] = (downloadNumber & 3) * 64; + bitfield[1] += 4 + 8 + 16; + } + if (mode === 2) { + // 0 0 0 0 0 0 0 0|X X X 0 1 1 0 0 + bitfield[0] = 0; + bitfield[1] = downloadNumber * 32; + bitfield[1] += 4 + 8; + } + if (mode === 1) { + // 0 0 0 0 0 0 0 0|0 X X X 0 1 0 0 + bitfield[0] = 0; + bitfield[1] = downloadNumber * 16; + bitfield[1] += 4; + } + if (mode === 0) { + // 0 0 0 0 0 0 0 0|0 0 X X X 0 0 0 + bitfield[0] = 0; + bitfield[1] = downloadNumber * 8; + } + // Swap the byte order. + let zero = bitfield[0]; + bitfield[0] = bitfield[1]; + bitfield[1] = zero; + return [bitfield, null]; +} + +const HASH_SIZE = 64; +const K = [ + 0x428a2f98, 0xd728ae22, 0x71374491, 0x23ef65cd, 0xb5c0fbcf, 0xec4d3b2f, 0xe9b5dba5, 0x8189dbbc, 0x3956c25b, + 0xf348b538, 0x59f111f1, 0xb605d019, 0x923f82a4, 0xaf194f9b, 0xab1c5ed5, 0xda6d8118, 0xd807aa98, 0xa3030242, + 0x12835b01, 0x45706fbe, 0x243185be, 0x4ee4b28c, 0x550c7dc3, 0xd5ffb4e2, 0x72be5d74, 0xf27b896f, 0x80deb1fe, + 0x3b1696b1, 0x9bdc06a7, 0x25c71235, 0xc19bf174, 0xcf692694, 0xe49b69c1, 0x9ef14ad2, 0xefbe4786, 0x384f25e3, + 0x0fc19dc6, 0x8b8cd5b5, 0x240ca1cc, 0x77ac9c65, 0x2de92c6f, 0x592b0275, 0x4a7484aa, 0x6ea6e483, 0x5cb0a9dc, + 0xbd41fbd4, 0x76f988da, 0x831153b5, 0x983e5152, 0xee66dfab, 0xa831c66d, 0x2db43210, 0xb00327c8, 0x98fb213f, + 0xbf597fc7, 0xbeef0ee4, 0xc6e00bf3, 0x3da88fc2, 0xd5a79147, 0x930aa725, 0x06ca6351, 0xe003826f, 0x14292967, + 0x0a0e6e70, 0x27b70a85, 0x46d22ffc, 0x2e1b2138, 0x5c26c926, 0x4d2c6dfc, 0x5ac42aed, 0x53380d13, 0x9d95b3df, + 0x650a7354, 0x8baf63de, 0x766a0abb, 0x3c77b2a8, 0x81c2c92e, 0x47edaee6, 0x92722c85, 0x1482353b, 0xa2bfe8a1, + 0x4cf10364, 0xa81a664b, 0xbc423001, 0xc24b8b70, 0xd0f89791, 0xc76c51a3, 0x0654be30, 0xd192e819, 0xd6ef5218, + 0xd6990624, 0x5565a910, 0xf40e3585, 0x5771202a, 0x106aa070, 0x32bbd1b8, 0x19a4c116, 0xb8d2d0c8, 0x1e376c08, + 0x5141ab53, 0x2748774c, 0xdf8eeb99, 0x34b0bcb5, 0xe19b48a8, 0x391c0cb3, 0xc5c95a63, 0x4ed8aa4a, 0xe3418acb, + 0x5b9cca4f, 0x7763e373, 0x682e6ff3, 0xd6b2b8a3, 0x748f82ee, 0x5defb2fc, 0x78a5636f, 0x43172f60, 0x84c87814, + 0xa1f0ab72, 0x8cc70208, 0x1a6439ec, 0x90befffa, 0x23631e28, 0xa4506ceb, 0xde82bde9, 0xbef9a3f7, 0xb2c67915, + 0xc67178f2, 0xe372532b, 0xca273ece, 0xea26619c, 0xd186b8c7, 0x21c0c207, 0xeada7dd6, 0xcde0eb1e, 0xf57d4f7f, + 0xee6ed178, 0x06f067aa, 0x72176fba, 0x0a637dc5, 0xa2c898a6, 0x113f9804, 0xbef90dae, 0x1b710b35, 0x131c471b, + 0x28db77f5, 0x23047d84, 0x32caab7b, 0x40c72493, 0x3c9ebe0a, 0x15c9bebc, 0x431d67c4, 0x9c100d4c, 0x4cc5d4be, + 0xcb3e42b6, 0x597f299c, 0xfc657e2a, 0x5fcb6fab, 0x3ad6faec, 0x6c44198c, 0x4a475817, +]; +function ts64(x, i, h, l) { + x[i] = (h >> 24) & 0xff; + x[i + 1] = (h >> 16) & 0xff; + x[i + 2] = (h >> 8) & 0xff; + x[i + 3] = h & 0xff; + x[i + 4] = (l >> 24) & 0xff; + x[i + 5] = (l >> 16) & 0xff; + x[i + 6] = (l >> 8) & 0xff; + x[i + 7] = l & 0xff; +} +function crypto_hashblocks_hl(hh, hl, m, n) { + let wh = new Int32Array(16), wl = new Int32Array(16), bh0, bh1, bh2, bh3, bh4, bh5, bh6, bh7, bl0, bl1, bl2, bl3, bl4, bl5, bl6, bl7, th, tl, i, j, h, l, a, b, c, d; + let ah0 = hh[0], ah1 = hh[1], ah2 = hh[2], ah3 = hh[3], ah4 = hh[4], ah5 = hh[5], ah6 = hh[6], ah7 = hh[7], al0 = hl[0], al1 = hl[1], al2 = hl[2], al3 = hl[3], al4 = hl[4], al5 = hl[5], al6 = hl[6], al7 = hl[7]; + let pos = 0; + while (n >= 128) { + for (i = 0; i < 16; i++) { + j = 8 * i + pos; + wh[i] = (m[j + 0] << 24) | (m[j + 1] << 16) | (m[j + 2] << 8) | m[j + 3]; + wl[i] = (m[j + 4] << 24) | (m[j + 5] << 16) | (m[j + 6] << 8) | m[j + 7]; + } + for (i = 0; i < 80; i++) { + bh0 = ah0; + bh1 = ah1; + bh2 = ah2; + bh3 = ah3; + bh4 = ah4; + bh5 = ah5; + bh6 = ah6; + bh7 = ah7; + bl0 = al0; + bl1 = al1; + bl2 = al2; + bl3 = al3; + bl4 = al4; + bl5 = al5; + bl6 = al6; + bl7 = al7; + // add + h = ah7; + l = al7; + a = l & 0xffff; + b = l >>> 16; + c = h & 0xffff; + d = h >>> 16; + // Sigma1 + h = + ((ah4 >>> 14) | (al4 << (32 - 14))) ^ + ((ah4 >>> 18) | (al4 << (32 - 18))) ^ + ((al4 >>> (41 - 32)) | (ah4 << (32 - (41 - 32)))); + l = + ((al4 >>> 14) | (ah4 << (32 - 14))) ^ + ((al4 >>> 18) | (ah4 << (32 - 18))) ^ + ((ah4 >>> (41 - 32)) | (al4 << (32 - (41 - 32)))); + a += l & 0xffff; + b += l >>> 16; + c += h & 0xffff; + d += h >>> 16; + // Ch + h = (ah4 & ah5) ^ (~ah4 & ah6); + l = (al4 & al5) ^ (~al4 & al6); + a += l & 0xffff; + b += l >>> 16; + c += h & 0xffff; + d += h >>> 16; + // K + h = K[i * 2]; + l = K[i * 2 + 1]; + a += l & 0xffff; + b += l >>> 16; + c += h & 0xffff; + d += h >>> 16; + // w + h = wh[i % 16]; + l = wl[i % 16]; + a += l & 0xffff; + b += l >>> 16; + c += h & 0xffff; + d += h >>> 16; + b += a >>> 16; + c += b >>> 16; + d += c >>> 16; + th = (c & 0xffff) | (d << 16); + tl = (a & 0xffff) | (b << 16); + // add + h = th; + l = tl; + a = l & 0xffff; + b = l >>> 16; + c = h & 0xffff; + d = h >>> 16; + // Sigma0 + h = + ((ah0 >>> 28) | (al0 << (32 - 28))) ^ + ((al0 >>> (34 - 32)) | (ah0 << (32 - (34 - 32)))) ^ + ((al0 >>> (39 - 32)) | (ah0 << (32 - (39 - 32)))); + l = + ((al0 >>> 28) | (ah0 << (32 - 28))) ^ + ((ah0 >>> (34 - 32)) | (al0 << (32 - (34 - 32)))) ^ + ((ah0 >>> (39 - 32)) | (al0 << (32 - (39 - 32)))); + a += l & 0xffff; + b += l >>> 16; + c += h & 0xffff; + d += h >>> 16; + // Maj + h = (ah0 & ah1) ^ (ah0 & ah2) ^ (ah1 & ah2); + l = (al0 & al1) ^ (al0 & al2) ^ (al1 & al2); + a += l & 0xffff; + b += l >>> 16; + c += h & 0xffff; + d += h >>> 16; + b += a >>> 16; + c += b >>> 16; + d += c >>> 16; + bh7 = (c & 0xffff) | (d << 16); + bl7 = (a & 0xffff) | (b << 16); + // add + h = bh3; + l = bl3; + a = l & 0xffff; + b = l >>> 16; + c = h & 0xffff; + d = h >>> 16; + h = th; + l = tl; + a += l & 0xffff; + b += l >>> 16; + c += h & 0xffff; + d += h >>> 16; + b += a >>> 16; + c += b >>> 16; + d += c >>> 16; + bh3 = (c & 0xffff) | (d << 16); + bl3 = (a & 0xffff) | (b << 16); + ah1 = bh0; + ah2 = bh1; + ah3 = bh2; + ah4 = bh3; + ah5 = bh4; + ah6 = bh5; + ah7 = bh6; + ah0 = bh7; + al1 = bl0; + al2 = bl1; + al3 = bl2; + al4 = bl3; + al5 = bl4; + al6 = bl5; + al7 = bl6; + al0 = bl7; + if (i % 16 === 15) { + for (j = 0; j < 16; j++) { + // add + h = wh[j]; + l = wl[j]; + a = l & 0xffff; + b = l >>> 16; + c = h & 0xffff; + d = h >>> 16; + h = wh[(j + 9) % 16]; + l = wl[(j + 9) % 16]; + a += l & 0xffff; + b += l >>> 16; + c += h & 0xffff; + d += h >>> 16; + // sigma0 + th = wh[(j + 1) % 16]; + tl = wl[(j + 1) % 16]; + h = ((th >>> 1) | (tl << (32 - 1))) ^ ((th >>> 8) | (tl << (32 - 8))) ^ (th >>> 7); + l = ((tl >>> 1) | (th << (32 - 1))) ^ ((tl >>> 8) | (th << (32 - 8))) ^ ((tl >>> 7) | (th << (32 - 7))); + a += l & 0xffff; + b += l >>> 16; + c += h & 0xffff; + d += h >>> 16; + // sigma1 + th = wh[(j + 14) % 16]; + tl = wl[(j + 14) % 16]; + h = ((th >>> 19) | (tl << (32 - 19))) ^ ((tl >>> (61 - 32)) | (th << (32 - (61 - 32)))) ^ (th >>> 6); + l = + ((tl >>> 19) | (th << (32 - 19))) ^ + ((th >>> (61 - 32)) | (tl << (32 - (61 - 32)))) ^ + ((tl >>> 6) | (th << (32 - 6))); + a += l & 0xffff; + b += l >>> 16; + c += h & 0xffff; + d += h >>> 16; + b += a >>> 16; + c += b >>> 16; + d += c >>> 16; + wh[j] = (c & 0xffff) | (d << 16); + wl[j] = (a & 0xffff) | (b << 16); + } + } + } + // add + h = ah0; + l = al0; + a = l & 0xffff; + b = l >>> 16; + c = h & 0xffff; + d = h >>> 16; + h = hh[0]; + l = hl[0]; + a += l & 0xffff; + b += l >>> 16; + c += h & 0xffff; + d += h >>> 16; + b += a >>> 16; + c += b >>> 16; + d += c >>> 16; + hh[0] = ah0 = (c & 0xffff) | (d << 16); + hl[0] = al0 = (a & 0xffff) | (b << 16); + h = ah1; + l = al1; + a = l & 0xffff; + b = l >>> 16; + c = h & 0xffff; + d = h >>> 16; + h = hh[1]; + l = hl[1]; + a += l & 0xffff; + b += l >>> 16; + c += h & 0xffff; + d += h >>> 16; + b += a >>> 16; + c += b >>> 16; + d += c >>> 16; + hh[1] = ah1 = (c & 0xffff) | (d << 16); + hl[1] = al1 = (a & 0xffff) | (b << 16); + h = ah2; + l = al2; + a = l & 0xffff; + b = l >>> 16; + c = h & 0xffff; + d = h >>> 16; + h = hh[2]; + l = hl[2]; + a += l & 0xffff; + b += l >>> 16; + c += h & 0xffff; + d += h >>> 16; + b += a >>> 16; + c += b >>> 16; + d += c >>> 16; + hh[2] = ah2 = (c & 0xffff) | (d << 16); + hl[2] = al2 = (a & 0xffff) | (b << 16); + h = ah3; + l = al3; + a = l & 0xffff; + b = l >>> 16; + c = h & 0xffff; + d = h >>> 16; + h = hh[3]; + l = hl[3]; + a += l & 0xffff; + b += l >>> 16; + c += h & 0xffff; + d += h >>> 16; + b += a >>> 16; + c += b >>> 16; + d += c >>> 16; + hh[3] = ah3 = (c & 0xffff) | (d << 16); + hl[3] = al3 = (a & 0xffff) | (b << 16); + h = ah4; + l = al4; + a = l & 0xffff; + b = l >>> 16; + c = h & 0xffff; + d = h >>> 16; + h = hh[4]; + l = hl[4]; + a += l & 0xffff; + b += l >>> 16; + c += h & 0xffff; + d += h >>> 16; + b += a >>> 16; + c += b >>> 16; + d += c >>> 16; + hh[4] = ah4 = (c & 0xffff) | (d << 16); + hl[4] = al4 = (a & 0xffff) | (b << 16); + h = ah5; + l = al5; + a = l & 0xffff; + b = l >>> 16; + c = h & 0xffff; + d = h >>> 16; + h = hh[5]; + l = hl[5]; + a += l & 0xffff; + b += l >>> 16; + c += h & 0xffff; + d += h >>> 16; + b += a >>> 16; + c += b >>> 16; + d += c >>> 16; + hh[5] = ah5 = (c & 0xffff) | (d << 16); + hl[5] = al5 = (a & 0xffff) | (b << 16); + h = ah6; + l = al6; + a = l & 0xffff; + b = l >>> 16; + c = h & 0xffff; + d = h >>> 16; + h = hh[6]; + l = hl[6]; + a += l & 0xffff; + b += l >>> 16; + c += h & 0xffff; + d += h >>> 16; + b += a >>> 16; + c += b >>> 16; + d += c >>> 16; + hh[6] = ah6 = (c & 0xffff) | (d << 16); + hl[6] = al6 = (a & 0xffff) | (b << 16); + h = ah7; + l = al7; + a = l & 0xffff; + b = l >>> 16; + c = h & 0xffff; + d = h >>> 16; + h = hh[7]; + l = hl[7]; + a += l & 0xffff; + b += l >>> 16; + c += h & 0xffff; + d += h >>> 16; + b += a >>> 16; + c += b >>> 16; + d += c >>> 16; + hh[7] = ah7 = (c & 0xffff) | (d << 16); + hl[7] = al7 = (a & 0xffff) | (b << 16); + pos += 128; + n -= 128; + } + return n; +} +const sha512internal = function (out, m, n) { + let hh = new Int32Array(8), hl = new Int32Array(8), x = new Uint8Array(256), i, b = n; + hh[0] = 0x6a09e667; + hh[1] = 0xbb67ae85; + hh[2] = 0x3c6ef372; + hh[3] = 0xa54ff53a; + hh[4] = 0x510e527f; + hh[5] = 0x9b05688c; + hh[6] = 0x1f83d9ab; + hh[7] = 0x5be0cd19; + hl[0] = 0xf3bcc908; + hl[1] = 0x84caa73b; + hl[2] = 0xfe94f82b; + hl[3] = 0x5f1d36f1; + hl[4] = 0xade682d1; + hl[5] = 0x2b3e6c1f; + hl[6] = 0xfb41bd6b; + hl[7] = 0x137e2179; + crypto_hashblocks_hl(hh, hl, m, n); + n %= 128; + for (i = 0; i < n; i++) + x[i] = m[b - n + i]; + x[n] = 128; + n = 256 - 128 * (n < 112 ? 1 : 0); + x[n - 9] = 0; + ts64(x, n - 8, (b / 0x20000000) | 0, b << 3); + crypto_hashblocks_hl(hh, hl, x, n); + for (i = 0; i < 8; i++) + ts64(out, 8 * i, hh[i], hl[i]); + return 0; +}; +// sha512 is the standard sha512 cryptographic hash function. This is the +// default choice for Skynet operations, though many of the Sia protocol +// standards use blake2b instead, so you will see both. +function sha512(m) { + const out = new Uint8Array(HASH_SIZE); + sha512internal(out, m, m.length); + return out; +} + +let crypto_sign_BYTES = 64, crypto_sign_PUBLICKEYBYTES = 32, crypto_sign_SECRETKEYBYTES = 64, crypto_sign_SEEDBYTES = 32; +let gf = function () { + let r = new Float64Array(16); + return r; +}; +let gfi$1 = function (init) { + let i, r = new Float64Array(16); + if (init) + for (i = 0; i < init.length; i++) + r[i] = init[i]; + return r; +}; +let gf0 = gf(), gf1 = gfi$1([1]), D = gfi$1([ + 0x78a3, 0x1359, 0x4dca, 0x75eb, 0xd8ab, 0x4141, 0x0a4d, 0x0070, 0xe898, 0x7779, 0x4079, 0x8cc7, 0xfe73, 0x2b6f, + 0x6cee, 0x5203, +]), D2 = gfi$1([ + 0xf159, 0x26b2, 0x9b94, 0xebd6, 0xb156, 0x8283, 0x149a, 0x00e0, 0xd130, 0xeef3, 0x80f2, 0x198e, 0xfce7, 0x56df, + 0xd9dc, 0x2406, +]), X = gfi$1([ + 0xd51a, 0x8f25, 0x2d60, 0xc956, 0xa7b2, 0x9525, 0xc760, 0x692c, 0xdc5c, 0xfdd6, 0xe231, 0xc0a4, 0x53fe, 0xcd6e, + 0x36d3, 0x2169, +]), Y = gfi$1([ + 0x6658, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, + 0x6666, 0x6666, +]), I = gfi$1([ + 0xa0b0, 0x4a0e, 0x1b27, 0xc4ee, 0xe478, 0xad2f, 0x1806, 0x2f43, 0xd7a7, 0x3dfb, 0x0099, 0x2b4d, 0xdf0b, 0x4fc1, + 0x2480, 0x2b83, +]); +function vn(x, xi, y, yi, n) { + let i, d = 0; + for (i = 0; i < n; i++) + d |= x[xi + i] ^ y[yi + i]; + return (1 & ((d - 1) >>> 8)) - 1; +} +function crypto_verify_32(x, xi, y, yi) { + return vn(x, xi, y, yi, 32); +} +function set25519(r, a) { + let i; + for (i = 0; i < 16; i++) + r[i] = a[i] | 0; +} +function car25519(o) { + let i, v, c = 1; + for (i = 0; i < 16; i++) { + v = o[i] + c + 65535; + c = Math.floor(v / 65536); + o[i] = v - c * 65536; + } + o[0] += c - 1 + 37 * (c - 1); +} +function sel25519(p, q, b) { + let t, c = ~(b - 1); + for (let i = 0; i < 16; i++) { + t = c & (p[i] ^ q[i]); + p[i] ^= t; + q[i] ^= t; + } +} +function pack25519(o, n) { + let i, j, b; + let m = gf(), t = gf(); + for (i = 0; i < 16; i++) + t[i] = n[i]; + car25519(t); + car25519(t); + car25519(t); + for (j = 0; j < 2; j++) { + m[0] = t[0] - 0xffed; + for (i = 1; i < 15; i++) { + m[i] = t[i] - 0xffff - ((m[i - 1] >> 16) & 1); + m[i - 1] &= 0xffff; + } + m[15] = t[15] - 0x7fff - ((m[14] >> 16) & 1); + b = (m[15] >> 16) & 1; + m[14] &= 0xffff; + sel25519(t, m, 1 - b); + } + for (i = 0; i < 16; i++) { + o[2 * i] = t[i] & 0xff; + o[2 * i + 1] = t[i] >> 8; + } +} +function neq25519(a, b) { + let c = new Uint8Array(32), d = new Uint8Array(32); + pack25519(c, a); + pack25519(d, b); + return crypto_verify_32(c, 0, d, 0); +} +function par25519(a) { + let d = new Uint8Array(32); + pack25519(d, a); + return d[0] & 1; +} +function unpack25519(o, n) { + let i; + for (i = 0; i < 16; i++) + o[i] = n[2 * i] + (n[2 * i + 1] << 8); + o[15] &= 0x7fff; +} +function A$1(o, a, b) { + for (let i = 0; i < 16; i++) + o[i] = a[i] + b[i]; +} +function Z$1(o, a, b) { + for (let i = 0; i < 16; i++) + o[i] = a[i] - b[i]; +} +function M(o, a, b) { + let v, c, t0 = 0, t1 = 0, t2 = 0, t3 = 0, t4 = 0, t5 = 0, t6 = 0, t7 = 0, t8 = 0, t9 = 0, t10 = 0, t11 = 0, t12 = 0, t13 = 0, t14 = 0, t15 = 0, t16 = 0, t17 = 0, t18 = 0, t19 = 0, t20 = 0, t21 = 0, t22 = 0, t23 = 0, t24 = 0, t25 = 0, t26 = 0, t27 = 0, t28 = 0, t29 = 0, t30 = 0, b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3], b4 = b[4], b5 = b[5], b6 = b[6], b7 = b[7], b8 = b[8], b9 = b[9], b10 = b[10], b11 = b[11], b12 = b[12], b13 = b[13], b14 = b[14], b15 = b[15]; + v = a[0]; + t0 += v * b0; + t1 += v * b1; + t2 += v * b2; + t3 += v * b3; + t4 += v * b4; + t5 += v * b5; + t6 += v * b6; + t7 += v * b7; + t8 += v * b8; + t9 += v * b9; + t10 += v * b10; + t11 += v * b11; + t12 += v * b12; + t13 += v * b13; + t14 += v * b14; + t15 += v * b15; + v = a[1]; + t1 += v * b0; + t2 += v * b1; + t3 += v * b2; + t4 += v * b3; + t5 += v * b4; + t6 += v * b5; + t7 += v * b6; + t8 += v * b7; + t9 += v * b8; + t10 += v * b9; + t11 += v * b10; + t12 += v * b11; + t13 += v * b12; + t14 += v * b13; + t15 += v * b14; + t16 += v * b15; + v = a[2]; + t2 += v * b0; + t3 += v * b1; + t4 += v * b2; + t5 += v * b3; + t6 += v * b4; + t7 += v * b5; + t8 += v * b6; + t9 += v * b7; + t10 += v * b8; + t11 += v * b9; + t12 += v * b10; + t13 += v * b11; + t14 += v * b12; + t15 += v * b13; + t16 += v * b14; + t17 += v * b15; + v = a[3]; + t3 += v * b0; + t4 += v * b1; + t5 += v * b2; + t6 += v * b3; + t7 += v * b4; + t8 += v * b5; + t9 += v * b6; + t10 += v * b7; + t11 += v * b8; + t12 += v * b9; + t13 += v * b10; + t14 += v * b11; + t15 += v * b12; + t16 += v * b13; + t17 += v * b14; + t18 += v * b15; + v = a[4]; + t4 += v * b0; + t5 += v * b1; + t6 += v * b2; + t7 += v * b3; + t8 += v * b4; + t9 += v * b5; + t10 += v * b6; + t11 += v * b7; + t12 += v * b8; + t13 += v * b9; + t14 += v * b10; + t15 += v * b11; + t16 += v * b12; + t17 += v * b13; + t18 += v * b14; + t19 += v * b15; + v = a[5]; + t5 += v * b0; + t6 += v * b1; + t7 += v * b2; + t8 += v * b3; + t9 += v * b4; + t10 += v * b5; + t11 += v * b6; + t12 += v * b7; + t13 += v * b8; + t14 += v * b9; + t15 += v * b10; + t16 += v * b11; + t17 += v * b12; + t18 += v * b13; + t19 += v * b14; + t20 += v * b15; + v = a[6]; + t6 += v * b0; + t7 += v * b1; + t8 += v * b2; + t9 += v * b3; + t10 += v * b4; + t11 += v * b5; + t12 += v * b6; + t13 += v * b7; + t14 += v * b8; + t15 += v * b9; + t16 += v * b10; + t17 += v * b11; + t18 += v * b12; + t19 += v * b13; + t20 += v * b14; + t21 += v * b15; + v = a[7]; + t7 += v * b0; + t8 += v * b1; + t9 += v * b2; + t10 += v * b3; + t11 += v * b4; + t12 += v * b5; + t13 += v * b6; + t14 += v * b7; + t15 += v * b8; + t16 += v * b9; + t17 += v * b10; + t18 += v * b11; + t19 += v * b12; + t20 += v * b13; + t21 += v * b14; + t22 += v * b15; + v = a[8]; + t8 += v * b0; + t9 += v * b1; + t10 += v * b2; + t11 += v * b3; + t12 += v * b4; + t13 += v * b5; + t14 += v * b6; + t15 += v * b7; + t16 += v * b8; + t17 += v * b9; + t18 += v * b10; + t19 += v * b11; + t20 += v * b12; + t21 += v * b13; + t22 += v * b14; + t23 += v * b15; + v = a[9]; + t9 += v * b0; + t10 += v * b1; + t11 += v * b2; + t12 += v * b3; + t13 += v * b4; + t14 += v * b5; + t15 += v * b6; + t16 += v * b7; + t17 += v * b8; + t18 += v * b9; + t19 += v * b10; + t20 += v * b11; + t21 += v * b12; + t22 += v * b13; + t23 += v * b14; + t24 += v * b15; + v = a[10]; + t10 += v * b0; + t11 += v * b1; + t12 += v * b2; + t13 += v * b3; + t14 += v * b4; + t15 += v * b5; + t16 += v * b6; + t17 += v * b7; + t18 += v * b8; + t19 += v * b9; + t20 += v * b10; + t21 += v * b11; + t22 += v * b12; + t23 += v * b13; + t24 += v * b14; + t25 += v * b15; + v = a[11]; + t11 += v * b0; + t12 += v * b1; + t13 += v * b2; + t14 += v * b3; + t15 += v * b4; + t16 += v * b5; + t17 += v * b6; + t18 += v * b7; + t19 += v * b8; + t20 += v * b9; + t21 += v * b10; + t22 += v * b11; + t23 += v * b12; + t24 += v * b13; + t25 += v * b14; + t26 += v * b15; + v = a[12]; + t12 += v * b0; + t13 += v * b1; + t14 += v * b2; + t15 += v * b3; + t16 += v * b4; + t17 += v * b5; + t18 += v * b6; + t19 += v * b7; + t20 += v * b8; + t21 += v * b9; + t22 += v * b10; + t23 += v * b11; + t24 += v * b12; + t25 += v * b13; + t26 += v * b14; + t27 += v * b15; + v = a[13]; + t13 += v * b0; + t14 += v * b1; + t15 += v * b2; + t16 += v * b3; + t17 += v * b4; + t18 += v * b5; + t19 += v * b6; + t20 += v * b7; + t21 += v * b8; + t22 += v * b9; + t23 += v * b10; + t24 += v * b11; + t25 += v * b12; + t26 += v * b13; + t27 += v * b14; + t28 += v * b15; + v = a[14]; + t14 += v * b0; + t15 += v * b1; + t16 += v * b2; + t17 += v * b3; + t18 += v * b4; + t19 += v * b5; + t20 += v * b6; + t21 += v * b7; + t22 += v * b8; + t23 += v * b9; + t24 += v * b10; + t25 += v * b11; + t26 += v * b12; + t27 += v * b13; + t28 += v * b14; + t29 += v * b15; + v = a[15]; + t15 += v * b0; + t16 += v * b1; + t17 += v * b2; + t18 += v * b3; + t19 += v * b4; + t20 += v * b5; + t21 += v * b6; + t22 += v * b7; + t23 += v * b8; + t24 += v * b9; + t25 += v * b10; + t26 += v * b11; + t27 += v * b12; + t28 += v * b13; + t29 += v * b14; + t30 += v * b15; + t0 += 38 * t16; + t1 += 38 * t17; + t2 += 38 * t18; + t3 += 38 * t19; + t4 += 38 * t20; + t5 += 38 * t21; + t6 += 38 * t22; + t7 += 38 * t23; + t8 += 38 * t24; + t9 += 38 * t25; + t10 += 38 * t26; + t11 += 38 * t27; + t12 += 38 * t28; + t13 += 38 * t29; + t14 += 38 * t30; + // t15 left as is + // first car + c = 1; + v = t0 + c + 65535; + c = Math.floor(v / 65536); + t0 = v - c * 65536; + v = t1 + c + 65535; + c = Math.floor(v / 65536); + t1 = v - c * 65536; + v = t2 + c + 65535; + c = Math.floor(v / 65536); + t2 = v - c * 65536; + v = t3 + c + 65535; + c = Math.floor(v / 65536); + t3 = v - c * 65536; + v = t4 + c + 65535; + c = Math.floor(v / 65536); + t4 = v - c * 65536; + v = t5 + c + 65535; + c = Math.floor(v / 65536); + t5 = v - c * 65536; + v = t6 + c + 65535; + c = Math.floor(v / 65536); + t6 = v - c * 65536; + v = t7 + c + 65535; + c = Math.floor(v / 65536); + t7 = v - c * 65536; + v = t8 + c + 65535; + c = Math.floor(v / 65536); + t8 = v - c * 65536; + v = t9 + c + 65535; + c = Math.floor(v / 65536); + t9 = v - c * 65536; + v = t10 + c + 65535; + c = Math.floor(v / 65536); + t10 = v - c * 65536; + v = t11 + c + 65535; + c = Math.floor(v / 65536); + t11 = v - c * 65536; + v = t12 + c + 65535; + c = Math.floor(v / 65536); + t12 = v - c * 65536; + v = t13 + c + 65535; + c = Math.floor(v / 65536); + t13 = v - c * 65536; + v = t14 + c + 65535; + c = Math.floor(v / 65536); + t14 = v - c * 65536; + v = t15 + c + 65535; + c = Math.floor(v / 65536); + t15 = v - c * 65536; + t0 += c - 1 + 37 * (c - 1); + // second car + c = 1; + v = t0 + c + 65535; + c = Math.floor(v / 65536); + t0 = v - c * 65536; + v = t1 + c + 65535; + c = Math.floor(v / 65536); + t1 = v - c * 65536; + v = t2 + c + 65535; + c = Math.floor(v / 65536); + t2 = v - c * 65536; + v = t3 + c + 65535; + c = Math.floor(v / 65536); + t3 = v - c * 65536; + v = t4 + c + 65535; + c = Math.floor(v / 65536); + t4 = v - c * 65536; + v = t5 + c + 65535; + c = Math.floor(v / 65536); + t5 = v - c * 65536; + v = t6 + c + 65535; + c = Math.floor(v / 65536); + t6 = v - c * 65536; + v = t7 + c + 65535; + c = Math.floor(v / 65536); + t7 = v - c * 65536; + v = t8 + c + 65535; + c = Math.floor(v / 65536); + t8 = v - c * 65536; + v = t9 + c + 65535; + c = Math.floor(v / 65536); + t9 = v - c * 65536; + v = t10 + c + 65535; + c = Math.floor(v / 65536); + t10 = v - c * 65536; + v = t11 + c + 65535; + c = Math.floor(v / 65536); + t11 = v - c * 65536; + v = t12 + c + 65535; + c = Math.floor(v / 65536); + t12 = v - c * 65536; + v = t13 + c + 65535; + c = Math.floor(v / 65536); + t13 = v - c * 65536; + v = t14 + c + 65535; + c = Math.floor(v / 65536); + t14 = v - c * 65536; + v = t15 + c + 65535; + c = Math.floor(v / 65536); + t15 = v - c * 65536; + t0 += c - 1 + 37 * (c - 1); + o[0] = t0; + o[1] = t1; + o[2] = t2; + o[3] = t3; + o[4] = t4; + o[5] = t5; + o[6] = t6; + o[7] = t7; + o[8] = t8; + o[9] = t9; + o[10] = t10; + o[11] = t11; + o[12] = t12; + o[13] = t13; + o[14] = t14; + o[15] = t15; +} +function S$1(o, a) { + M(o, a, a); +} +function inv25519(o, i) { + let c = gf(); + let a; + for (a = 0; a < 16; a++) + c[a] = i[a]; + for (a = 253; a >= 0; a--) { + S$1(c, c); + if (a !== 2 && a !== 4) + M(c, c, i); + } + for (a = 0; a < 16; a++) + o[a] = c[a]; +} +function pow2523(o, i) { + let c = gf(); + let a; + for (a = 0; a < 16; a++) + c[a] = i[a]; + for (a = 250; a >= 0; a--) { + S$1(c, c); + if (a !== 1) + M(c, c, i); + } + for (a = 0; a < 16; a++) + o[a] = c[a]; +} +function add(p, q) { + let a = gf(), b = gf(), c = gf(), d = gf(), e = gf(), f = gf(), g = gf(), h = gf(), t = gf(); + Z$1(a, p[1], p[0]); + Z$1(t, q[1], q[0]); + M(a, a, t); + A$1(b, p[0], p[1]); + A$1(t, q[0], q[1]); + M(b, b, t); + M(c, p[3], q[3]); + M(c, c, D2); + M(d, p[2], q[2]); + A$1(d, d, d); + Z$1(e, b, a); + Z$1(f, d, c); + A$1(g, d, c); + A$1(h, b, a); + M(p[0], e, f); + M(p[1], h, g); + M(p[2], g, f); + M(p[3], e, h); +} +function cswap(p, q, b) { + let i; + for (i = 0; i < 4; i++) { + sel25519(p[i], q[i], b); + } +} +function pack(r, p) { + let tx = gf(), ty = gf(), zi = gf(); + inv25519(zi, p[2]); + M(tx, p[0], zi); + M(ty, p[1], zi); + pack25519(r, ty); + r[31] ^= par25519(tx) << 7; +} +function scalarmult(p, q, s) { + let b, i; + set25519(p[0], gf0); + set25519(p[1], gf1); + set25519(p[2], gf1); + set25519(p[3], gf0); + for (i = 255; i >= 0; --i) { + b = (s[(i / 8) | 0] >> (i & 7)) & 1; + cswap(p, q, b); + add(q, p); + add(p, p); + cswap(p, q, b); + } +} +function scalarbase(p, s) { + let q = [gf(), gf(), gf(), gf()]; + set25519(q[0], X); + set25519(q[1], Y); + set25519(q[2], gf1); + M(q[3], X, Y); + scalarmult(p, q, s); +} +let L = new Float64Array([ + 0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0x10, +]); +function modL(r, x) { + let carry, i, j, k; + for (i = 63; i >= 32; --i) { + carry = 0; + for (j = i - 32, k = i - 12; j < k; ++j) { + x[j] += carry - 16 * x[i] * L[j - (i - 32)]; + carry = Math.floor((x[j] + 128) / 256); + x[j] -= carry * 256; + } + x[j] += carry; + x[i] = 0; + } + carry = 0; + for (j = 0; j < 32; j++) { + x[j] += carry - (x[31] >> 4) * L[j]; + carry = x[j] >> 8; + x[j] &= 255; + } + for (j = 0; j < 32; j++) + x[j] -= carry * L[j]; + for (i = 0; i < 32; i++) { + x[i + 1] += x[i] >> 8; + r[i] = x[i] & 255; + } +} +function unpackneg(r, p) { + let t = gf(), chk = gf(), num = gf(), den = gf(), den2 = gf(), den4 = gf(), den6 = gf(); + set25519(r[2], gf1); + unpack25519(r[1], p); + S$1(num, r[1]); + M(den, num, D); + Z$1(num, num, r[2]); + A$1(den, r[2], den); + S$1(den2, den); + S$1(den4, den2); + M(den6, den4, den2); + M(t, den6, num); + M(t, t, den); + pow2523(t, t); + M(t, t, num); + M(t, t, den); + M(t, t, den); + M(r[0], t, den); + S$1(chk, r[0]); + M(chk, chk, den); + if (neq25519(chk, num)) + M(r[0], r[0], I); + S$1(chk, r[0]); + M(chk, chk, den); + if (neq25519(chk, num)) + return -1; + if (par25519(r[0]) === p[31] >> 7) + Z$1(r[0], gf0, r[0]); + M(r[3], r[0], r[1]); + return 0; +} +function reduce(r) { + let x = new Float64Array(64), i; + for (i = 0; i < 64; i++) + x[i] = r[i]; + for (i = 0; i < 64; i++) + r[i] = 0; + modL(r, x); +} +function crypto_sign_keypair(pk, sk) { + let d = new Uint8Array(64); + let p = [gf(), gf(), gf(), gf()]; + let i; + sha512internal(d, sk, 32); + d[0] &= 248; + d[31] &= 127; + d[31] |= 64; + scalarbase(p, d); + pack(pk, p); + for (i = 0; i < 32; i++) + sk[i + 32] = pk[i]; + return 0; +} +function crypto_sign_open(m, sm, n, pk) { + let i; + let t = new Uint8Array(32), h = new Uint8Array(64); + let p = [gf(), gf(), gf(), gf()], q = [gf(), gf(), gf(), gf()]; + if (n < 64) + return -1; + if (unpackneg(q, pk)) + return -1; + for (i = 0; i < n; i++) + m[i] = sm[i]; + for (i = 0; i < 32; i++) + m[i + 32] = pk[i]; + sha512internal(h, m, n); + reduce(h); + scalarmult(p, q, h); + scalarbase(q, sm.subarray(32)); + add(p, q); + pack(t, p); + n -= 64; + if (crypto_verify_32(sm, 0, t, 0)) { + for (i = 0; i < n; i++) + m[i] = 0; + return -1; + } + for (i = 0; i < n; i++) + m[i] = sm[i + 64]; + return n; +} +// Note: difference from C - smlen returned, not passed as argument. +function crypto_sign(sm, m, n, sk) { + let d = new Uint8Array(64), h = new Uint8Array(64), r = new Uint8Array(64); + let i, j, x = new Float64Array(64); + let p = [gf(), gf(), gf(), gf()]; + sha512internal(d, sk, 32); + d[0] &= 248; + d[31] &= 127; + d[31] |= 64; + let smlen = n + 64; + for (i = 0; i < n; i++) + sm[64 + i] = m[i]; + for (i = 0; i < 32; i++) + sm[32 + i] = d[32 + i]; + sha512internal(r, sm.subarray(32), n + 32); + reduce(r); + scalarbase(p, r); + pack(sm, p); + for (i = 32; i < 64; i++) + sm[i] = sk[i]; + sha512internal(h, sm, n + 64); + reduce(h); + for (i = 0; i < 64; i++) + x[i] = 0; + for (i = 0; i < 32; i++) + x[i] = r[i]; + for (i = 0; i < 32; i++) { + for (j = 0; j < 32; j++) { + x[i + j] += h[i] * d[j]; + } + } + modL(sm.subarray(32), x); + return smlen; +} +// Zero types to make error returns more convenient. +const nu8$3 = new Uint8Array(0); +const nkp$1 = { publicKey: nu8$3, secretKey: nu8$3 }; +// checkAllUint8Array is a helper function to perform input checking on the +// crypto API functions. Because the kernel is often hot-loading untrusted +// code, we cannot depend on typescript to provide type safety. +function checkAllUint8Array(...args) { + for (let i = 0; i < args.length; i++) { + if (!(args[i] instanceof Uint8Array)) { + return "unexpected type, use Uint8Array"; + } + } + return null; +} +// ed25519KeypairFromEntropy is a function that generates an ed25519 keypair +// from the provided entropy. +function ed25519KeypairFromEntropy(seed) { + // Input checking. + let errU8 = checkAllUint8Array(seed); + if (errU8 !== null) { + return [nkp$1, addContextToErr$1(errU8, "seed is invalid")]; + } + if (seed.length !== crypto_sign_SEEDBYTES) { + return [nkp$1, "bad seed size"]; + } + // Build the keypair. + let pk = new Uint8Array(crypto_sign_PUBLICKEYBYTES); + let sk = new Uint8Array(crypto_sign_SECRETKEYBYTES); + for (let i = 0; i < 32; i++) { + sk[i] = seed[i]; + } + crypto_sign_keypair(pk, sk); + return [ + { + publicKey: pk, + secretKey: sk, + }, + null, + ]; +} +// ed25519Sign will produce an ed25519 signature of a given input. +function ed25519Sign(msg, secretKey) { + // Input checking. + let errU8 = checkAllUint8Array(msg, secretKey); + if (errU8 !== null) { + return [nu8$3, addContextToErr$1(errU8, "inputs are invalid")]; + } + if (secretKey.length !== crypto_sign_SECRETKEYBYTES) { + return [nu8$3, "bad secret key size"]; + } + // Build the signature. + let signedMsg = new Uint8Array(crypto_sign_BYTES + msg.length); + crypto_sign(signedMsg, msg, msg.length, secretKey); + let sig = new Uint8Array(crypto_sign_BYTES); + for (let i = 0; i < sig.length; i++) { + sig[i] = signedMsg[i]; + } + return [sig, null]; +} +// ed25519Verify will check whether a signature is valid against the given +// publicKey and message. +function ed25519Verify(msg, sig, publicKey) { + let errU8 = checkAllUint8Array(msg, sig, publicKey); + if (errU8 !== null) { + return false; + } + if (sig.length !== crypto_sign_BYTES) { + return false; + } + if (publicKey.length !== crypto_sign_PUBLICKEYBYTES) { + return false; + } + let sm = new Uint8Array(crypto_sign_BYTES + msg.length); + let m = new Uint8Array(crypto_sign_BYTES + msg.length); + let i; + for (i = 0; i < crypto_sign_BYTES; i++) { + sm[i] = sig[i]; + } + for (i = 0; i < msg.length; i++) { + sm[i + crypto_sign_BYTES] = msg[i]; + } + return crypto_sign_open(m, sm, sm.length, publicKey) >= 0; +} + +// Define the number of entropy words used when generating the seed. +const SEED_ENTROPY_WORDS = 13; +const SEED_BYTES = 16; +// deriveChildSeed is a helper function to derive a child seed from a parent +// seed using a string as the path. +function deriveChildSeed(parentSeed, derivationTag) { + let tagU8 = new TextEncoder().encode(" - " + derivationTag); + let preimage = new Uint8Array(parentSeed.length + tagU8.length); + preimage.set(parentSeed, 0); + preimage.set(tagU8, parentSeed.length); + let hash = sha512(preimage); + return hash.slice(0, SEED_BYTES); +} +// deriveMyskyRoot is a helper function to derive the root mysky seed of the +// provided user seed. +// +// NOTE: This is code is to provide legacy compatibility with the MySky +// ecosystem. Compatibility cannot be broken here. +function deriveMyskyRootKeypair(userSeed) { + let saltBytes = new TextEncoder().encode("root discoverable key"); + let saltHash = sha512(saltBytes); + let userSeedHash = sha512(userSeed); + let mergedHash = sha512(new Uint8Array([...saltHash, ...userSeedHash])); + let keyEntropy = mergedHash.slice(0, 32); + // Error is ignored because it should not be possible with the provided + // inputs. + let [keypair] = ed25519KeypairFromEntropy(keyEntropy); + return keypair; +} +// generateSeedPhraseDeterministic will generate and verify a seed phrase for +// the user. +function generateSeedPhraseDeterministic(password) { + let u8 = new TextEncoder().encode(password); + let buf = sha512(u8); + let randNums = Uint16Array.from(buf); + // Generate the seed phrase from the randNums. + let seedWords = []; + for (let i = 0; i < SEED_ENTROPY_WORDS; i++) { + let wordIndex = randNums[i] % dictionary.length; + if (i == SEED_ENTROPY_WORDS - 1) { + wordIndex = randNums[i] % (dictionary.length / 4); + } + seedWords.push(dictionary[wordIndex]); + } + // Convert the seedWords to a seed. + let [seed, err1] = seedWordsToSeed(seedWords); + if (err1 !== null) { + return ["", err1]; + } + // Compute the checksum. + let [checksumOne, checksumTwo, err2] = seedToChecksumWords(seed); + if (err2 !== null) { + return ["", err2]; + } + // Assemble the final seed phrase and set the text field. + let allWords = [...seedWords, checksumOne, checksumTwo]; + let seedPhrase = allWords.join(" "); + return [seedPhrase, null]; +} +// seedToChecksumWords will compute the two checksum words for the provided +// seed. The two return values are the two checksum words. +function seedToChecksumWords(seed) { + // Input validation. + if (seed.length !== SEED_BYTES) { + return ["", "", `seed has the wrong length: ${seed.length}`]; + } + // Get the hash. + let h = sha512(seed); + // Turn the hash into two words. + let word1 = h[0] << 8; + word1 += h[1]; + word1 >>= 6; + let word2 = h[1] << 10; + word2 &= 0xffff; + word2 += h[2] << 2; + word2 >>= 6; + return [dictionary[word1], dictionary[word2], null]; +} +// validSeedPhrase checks whether the provided seed phrase is valid, returning +// an error if not. If the seed phrase is valid, the full seed will be returned +// as a Uint8Array. +function validSeedPhrase(seedPhrase) { + // Create a helper function to make the below code more readable. + let prefix = function (s) { + return s.slice(0, DICTIONARY_UNIQUE_PREFIX); + }; + // Pull the seed into its respective parts. + let seedWordsAndChecksum = seedPhrase.split(" "); + let seedWords = seedWordsAndChecksum.slice(0, SEED_ENTROPY_WORDS); + let checksumOne = seedWordsAndChecksum[SEED_ENTROPY_WORDS]; + let checksumTwo = seedWordsAndChecksum[SEED_ENTROPY_WORDS + 1]; + // Convert the seedWords to a seed. + let [seed, err1] = seedWordsToSeed(seedWords); + if (err1 !== null) { + return [new Uint8Array(0), addContextToErr$1(err1, "unable to parse seed phrase")]; + } + let [checksumOneVerify, checksumTwoVerify, err2] = seedToChecksumWords(seed); + if (err2 !== null) { + return [new Uint8Array(0), addContextToErr$1(err2, "could not compute checksum words")]; + } + if (prefix(checksumOne) !== prefix(checksumOneVerify)) { + return [new Uint8Array(0), "first checksum word is invalid"]; + } + if (prefix(checksumTwo) !== prefix(checksumTwoVerify)) { + return [new Uint8Array(0), "second checksum word is invalid"]; + } + return [seed, null]; +} +// seedWordsToSeed will convert a provided seed phrase to to a Uint8Array that +// represents the cryptographic seed in bytes. +function seedWordsToSeed(seedWords) { + // Input checking. + if (seedWords.length !== SEED_ENTROPY_WORDS) { + return [new Uint8Array(0), `Seed words should have length ${SEED_ENTROPY_WORDS} but has length ${seedWords.length}`]; + } + // We are getting 16 bytes of entropy. + let bytes = new Uint8Array(SEED_BYTES); + let curByte = 0; + let curBit = 0; + for (let i = 0; i < SEED_ENTROPY_WORDS; i++) { + // Determine which number corresponds to the next word. + let word = -1; + for (let j = 0; j < dictionary.length; j++) { + if (seedWords[i].slice(0, DICTIONARY_UNIQUE_PREFIX) === dictionary[j].slice(0, DICTIONARY_UNIQUE_PREFIX)) { + word = j; + break; + } + } + if (word === -1) { + return [new Uint8Array(0), `word '${seedWords[i]}' at index ${i} not found in dictionary`]; + } + let wordBits = 10; + if (i === SEED_ENTROPY_WORDS - 1) { + wordBits = 8; + } + // Iterate over the bits of the 10- or 8-bit word. + for (let j = 0; j < wordBits; j++) { + let bitSet = (word & (1 << (wordBits - j - 1))) > 0; + if (bitSet) { + bytes[curByte] |= 1 << (8 - curBit - 1); + } + curBit += 1; + if (curBit >= 8) { + // Current byte has 8 bits, go to the next byte. + curByte += 1; + curBit = 0; + } + } + } + return [bytes, null]; +} +// seedPhraseToSeed will take a seed phrase and return the corresponding seed, +// providing an error if the seed phrase is invalid. This is an alias of +// validSeedPhrase. +function seedPhraseToSeed(seedPhrase) { + return validSeedPhrase(seedPhrase); +} + +var seed = /*#__PURE__*/Object.freeze({ + __proto__: null, + deriveChildSeed: deriveChildSeed, + deriveMyskyRootKeypair: deriveMyskyRootKeypair, + generateSeedPhraseDeterministic: generateSeedPhraseDeterministic, + seedToChecksumWords: seedToChecksumWords, + seedPhraseToSeed: seedPhraseToSeed, + validSeedPhrase: validSeedPhrase, + SEED_BYTES: SEED_BYTES +}); + +// Define some empty values to make our return statements more concise. +const nu8$2 = new Uint8Array(0); +const nkp = { publicKey: nu8$2, secretKey: nu8$2 }; +// computeRegistrySignature will take a secret key and the required fields of a +// registry entry and use them to compute a registry signature, returning both +// the signature and the encoded data for the registry entry. +function computeRegistrySignature(secretKey, dataKey, data, revision) { + // Check that the data is the right size. + if (data.length > 86) { + return [nu8$2, "registry data must be at most 86 bytes"]; + } + // Build the encoded data. + let [encodedData, errEPB] = encodePrefixedBytes(data); + if (errEPB !== null) { + return [nu8$2, addContextToErr$1(errEPB, "unable to encode provided registry data")]; + } + let [encodedRevision, errEU64] = encodeU64$1(revision); + if (errEU64 !== null) { + return [nu8$2, addContextToErr$1(errEU64, "unable to encode the revision number")]; + } + // Build the signing data. + let dataToSign = new Uint8Array(32 + 8 + data.length + 8); + dataToSign.set(dataKey, 0); + dataToSign.set(encodedData, 32); + dataToSign.set(encodedRevision, 32 + 8 + data.length); + let sigHash = blake2b(dataToSign); + // Sign the data. + let [sig, errS] = ed25519Sign(sigHash, secretKey); + if (errS !== null) { + return [nu8$2, addContextToErr$1(errS, "unable to sign registry entry")]; + } + return [sig, null]; +} +// deriveRegistryEntryID derives a registry entry ID from a provided pubkey and +// datakey. +function deriveRegistryEntryID(pubkey, datakey) { + // Check the lengths of the inputs. + if (pubkey.length !== 32) { + return [nu8$2, "pubkey is invalid, length is wrong"]; + } + if (datakey.length !== 32) { + return [nu8$2, "datakey is not a valid hash, length is wrong"]; + } + // Establish the encoding. First 16 bytes is a specifier, second 8 + // bytes declares the length of the pubkey, the next 32 bytes is the + // pubkey and the final 32 bytes is the datakey. This encoding is + // determined by the Sia protocol. + let encoding = new Uint8Array(16 + 8 + 32 + 32); + // Set the specifier. + encoding[0] = "e".charCodeAt(0); + encoding[1] = "d".charCodeAt(0); + encoding[2] = "2".charCodeAt(0); + encoding[3] = "5".charCodeAt(0); + encoding[4] = "5".charCodeAt(0); + encoding[5] = "1".charCodeAt(0); + encoding[6] = "9".charCodeAt(0); + // Set the pubkey. + let [encodedLen, errU64] = encodeU64$1(32n); + if (errU64 !== null) { + return [nu8$2, addContextToErr$1(errU64, "unable to encode pubkey length")]; + } + encoding.set(encodedLen, 16); + encoding.set(pubkey, 16 + 8); + encoding.set(datakey, 16 + 8 + 32); + // Get the final ID by hashing the encoded data. + let id = blake2b(encoding); + return [id, null]; +} +// entryIDToSkylink converts a registry entry id to a resolver skylink. +function entryIDToSkylink(entryID) { + let v2Skylink = new Uint8Array(34); + v2Skylink.set(entryID, 2); + v2Skylink[0] = 1; + return bufToB64$1(v2Skylink); +} +// resolverLink will take a registryEntryID and return the corresponding +// resolver link. +function resolverLink(entryID) { + if (entryID.length !== 32) { + return ["", "provided entry ID has the wrong length"]; + } + let v2Skylink = new Uint8Array(34); + v2Skylink.set(entryID, 2); + v2Skylink[0] = 1; + let skylink = bufToB64$1(v2Skylink); + return [skylink, null]; +} +// registryEntryKeys will use the user's seed to derive a keypair and a datakey +// using the provided seed and tags. The keypairTag is a tag which salts the +// keypair. If you change the input keypairTag, the resulting public key and +// secret key will be different. The dataKey tag is the salt for the datakey, +// if you provide a different datakey tag, the resulting datakey will be +// different. +// +// Note that changing the keypair tag will also change the resulting datakey. +// The purpose of the keypair tag is to obfuscate the fact that two registry +// entries are owned by the same identity. This obfuscation would break if two +// different public keys were using the same datakey. Changing the datakey does +// not change the public key. +function taggedRegistryEntryKeys(seed, keypairTagStr, datakeyTagStr) { + if (seed.length !== SEED_BYTES) { + return [nkp, nu8$2, "seed has the wrong length"]; + } + if (keypairTagStr.length > 255) { + return [nkp, nu8$2, "keypairTag must be less than 256 characters"]; + } + // If no datakey tag was provided, use the empty string. + if (datakeyTagStr === undefined) { + datakeyTagStr = ""; + } + // Generate a unique set of entropy using the seed and keypairTag. + let keypairTag = new TextEncoder().encode(keypairTagStr); + let entropyInput = new Uint8Array(keypairTag.length + seed.length); + entropyInput.set(seed, 0); + entropyInput.set(keypairTag, seed.length); + let keypairEntropy = sha512(entropyInput); + // Use the seed to dervie the datakey for the registry entry. We use + // a different tag to ensure that the datakey is independently random, such + // that the registry entry looks like it could be any other registry entry. + // + // We don't want it to be possible for two different combinations of + // tags to end up with the same datakey. If you don't use a length + // prefix, for example the tags ["123", "456"] and ["12", "3456"] would + // have the same datakey. You have to add the length prefix to the + // first tag otherwise you can get pairs like ["6", "4321"] and ["65", + // "321"] which could end up with the same datakey. + let datakeyTag = new TextEncoder().encode(datakeyTagStr); + let datakeyInput = new Uint8Array(seed.length + 1 + keypairTag.length + datakeyTag.length); + let keypairLen = new Uint8Array(1); + keypairLen[0] = keypairTag.length; + datakeyInput.set(seed); + datakeyInput.set(keypairLen, seed.length); + datakeyInput.set(keypairTag, seed.length + 1); + datakeyInput.set(datakeyTag, seed.length + 1 + keypairTag.length); + let datakeyEntropy = sha512(datakeyInput); + // Create the private key for the registry entry. + let [keypair, errKPFE] = ed25519KeypairFromEntropy(keypairEntropy.slice(0, 32)); + if (errKPFE !== null) { + return [nkp, nu8$2, addContextToErr$1(errKPFE, "unable to derive keypair")]; + } + let datakey = datakeyEntropy.slice(0, 32); + return [keypair, datakey, null]; +} +// verifyRegistrySignature will verify the signature of a registry entry. +function verifyRegistrySignature(pubkey, datakey, data, revision, sig) { + let [encodedData, errEPB] = encodePrefixedBytes(data); + if (errEPB !== null) { + return false; + } + let [encodedRevision, errU64] = encodeU64$1(revision); + if (errU64 !== null) { + return false; + } + let dataToVerify = new Uint8Array(32 + 8 + data.length + 8); + dataToVerify.set(datakey, 0); + dataToVerify.set(encodedData, 32); + dataToVerify.set(encodedRevision, 32 + 8 + data.length); + let sigHash = blake2b(dataToVerify); + return ed25519Verify(sigHash, sig, pubkey); +} + +// validateSkyfilePath checks whether the provided path is a valid path for a +// file in a skylink. +function validateSkyfilePath(path) { + if (path === "") { + return "path cannot be blank"; + } + if (path === "..") { + return "path cannot be .."; + } + if (path === ".") { + return "path cannot be ."; + } + if (path.startsWith("/")) { + return "metdata.Filename cannot start with /"; + } + if (path.startsWith("../")) { + return "metdata.Filename cannot start with ../"; + } + if (path.startsWith("./")) { + return "metdata.Filename cannot start with ./"; + } + let pathElems = path.split("/"); + for (let i = 0; i < pathElems.length; i++) { + if (pathElems[i] === ".") { + return "path cannot have a . element"; + } + if (pathElems[i] === "..") { + return "path cannot have a .. element"; + } + if (pathElems[i] === "") { + return "path cannot have an empty element, cannot contain //"; + } + } + return null; +} +// validateSkyfileMetadata checks whether the provided metadata is valid +// metadata for a skyfile. +function validateSkyfileMetadata(metadata) { + // Check that the filename is valid. + if (!("Filename" in metadata)) { + return "metadata.Filename does not exist"; + } + if (typeof metadata.Filename !== "string") { + return "metadata.Filename is not a string"; + } + let errVSP = validateSkyfilePath(metadata.Filename); + if (errVSP !== null) { + return addContextToErr$1(errVSP, "metadata.Filename does not have a valid path"); + } + // Check that there are no subfiles. + if ("Subfiles" in metadata) { + // TODO: Fill this out using code from + // skymodules.ValidateSkyfileMetadata to support subfiles. + return "cannot upload files that have subfiles"; + } + // Check that the default path rules are being respected. + if ("DisableDefaultPath" in metadata && "DefaultPath" in metadata) { + return "cannot set both a DefaultPath and also DisableDefaultPath"; + } + if ("DefaultPath" in metadata) { + // TODO: Fill this out with code from + // skymodules.validateDefaultPath to support subfiles and + // default paths. + return "cannot set a default path if there are no subfiles"; + } + if ("TryFiles" in metadata) { + if (!metadata.TryFiles.IsArray()) { + return "metadata.TryFiles must be an array"; + } + if (metadata.TryFiles.length === 0) { + return "metadata.TryFiles should not be empty"; + } + if ("DefaultPath" in metadata) { + return "metadata.TryFiles cannot be used alongside DefaultPath"; + } + if ("DisableDefaultPath" in metadata) { + return "metadata.TryFiles cannot be used alongside DisableDefaultPath"; + } + // TODO: finish the TryFiles checking using skymodules.ValidateTryFiles + return "TryFiles is not supported at this time"; + } + if ("ErrorPages" in metadata) { + // TODO: finish using skymodules.ValidateErrorPages + return "ErrorPages is not supported at this time"; + } + return null; +} +// validSkylink returns true if the provided Uint8Array is a valid skylink. +// This is an alias for 'parseSkylinkBitfield', as both perform the same +// validation. +function validSkylink(skylink) { + if (skylink.length !== 34) { + return false; + } + let [, , , errPSB] = parseSkylinkBitfield(skylink); + if (errPSB !== null) { + return false; + } + return true; +} + +// Helper consts to make returning empty values alongside errors more +// convenient. +const nu8$1 = new Uint8Array(0); +// verifyResolverLinkProof will check that the given resolver proof matches the +// provided skylink. If the proof is correct and the signature matches, the +// data will be returned. The returned link will be a verified skylink. +function verifyResolverLinkProof(skylink, proof) { + // Verify the presented skylink is formatted correctly. + if (skylink.length !== 34) { + return [nu8$1, "skylink is malformed, expecting 34 bytes"]; + } + // Verify that all of the required fields are present in the proof. + if (!("data" in proof) || + !("datakey" in proof) || + !("publickey" in proof) || + !("signature" in proof) || + !("type" in proof) || + !("revision" in proof)) { + return [nu8$1, "proof is malformed, fields are missing"]; + } + if (!("algorithm" in proof.publickey) || !("key" in proof.publickey)) { + return [nu8$1, "pubkey is malformed"]; + } + // Verify the typing of the fields. + if (typeof proof.data !== "string") { + return [nu8$1, "data is malformed"]; + } + let dataStr = proof.data; + if (typeof proof.datakey !== "string") { + return [nu8$1, "datakey is malformed"]; + } + let datakeyStr = proof.datakey; + if (proof.publickey.algorithm !== "ed25519") { + return [nu8$1, "pubkey has unrecognized algorithm"]; + } + if (typeof proof.publickey.key !== "string") { + return [nu8$1, "pubkey key is malformed"]; + } + let pubkeyStr = proof.publickey.key; + if (typeof proof.signature !== "string") { + return [nu8$1, "signature is malformed"]; + } + if (proof.type !== 1n) { + return [nu8$1, "registry entry has unrecognized type: " + tryStringify$1(proof.type)]; + } + let sigStr = proof.signature; + if (typeof proof.revision !== "bigint") { + return [nu8$1, "revision is malformed"]; + } + let revision = proof.revision; + // Decode all of the fields. They are presented in varied types and + // encodings. + let [data, errD] = hexToBuf(dataStr); + if (errD !== null) { + return [nu8$1, addContextToErr$1(errD, "data is invalid hex")]; + } + let [datakey, errDK] = hexToBuf(datakeyStr); + if (errDK !== null) { + return [nu8$1, addContextToErr$1(errDK, "datakey is invalid hex")]; + } + let [pubkey, errPK] = b64ToBuf(pubkeyStr); + if (errPK !== null) { + return [nu8$1, addContextToErr$1(errPK, "pubkey key is invalid base64")]; + } + let [sig, errS] = hexToBuf(sigStr); + if (errS !== null) { + return [nu8$1, addContextToErr$1(errS, "signature is invalid hex")]; + } + // Verify that the data is a skylink - this is a proof for a resolver, + // which means the proof is pointing to a specific skylink. + if (!validSkylink(data)) { + return [nu8$1, "this skylink does not resolve to another skylink"]; + } + // Verify that the combination of the datakey and the public key match + // the skylink. + let [entryID, errREID] = deriveRegistryEntryID(pubkey, datakey); + if (errREID !== null) { + return [nu8$1, addContextToErr$1(errREID, "proof pubkey is malformed")]; + } + let linkID = skylink.slice(2, 34); + for (let i = 0; i < entryID.length; i++) { + if (entryID[i] !== linkID[i]) { + return [nu8$1, "proof pubkey and datakey do not match the skylink root"]; + } + } + // Verify the signature. + if (!verifyRegistrySignature(pubkey, datakey, data, revision, sig)) { + return [nu8$1, "signature does not match"]; + } + return [data, null]; +} +// verifyResolverLinkProofs will verify a set of resolver link proofs provided +// by a portal after performing a resolver link lookup. Each proof corresponds +// to one level of resolution. The final value returned will be the V1 skylink +// at the end of the chain. +// +// This function treats the proof as untrusted data and will verify all of the +// fields that are provided. +function verifyResolverLinkProofs(skylink, proof) { + // Check that the proof is an array. + if (!Array.isArray(proof)) { + return [nu8$1, "provided proof is not an array: " + tryStringify$1(proof)]; + } + if (proof.length === 0) { + return [nu8$1, "proof array is empty"]; + } + // Check each proof in the chain, returning the final skylink. + for (let i = 0; i < proof.length; i++) { + let errVRLP; + [skylink, errVRLP] = verifyResolverLinkProof(skylink, proof[i]); + if (errVRLP !== null) { + return [nu8$1, addContextToErr$1(errVRLP, "one of the resolution proofs is invalid")]; + } + } + // Though it says 'skylink', the verifier is actually just returning + // whatever the registry data is. We need to check that the final value + // is a V1 skylink. + if (skylink.length !== 34) { + return [nu8$1, "final value returned by the resolver link is not a skylink"]; + } + let [version, , , errPSB] = parseSkylinkBitfield(skylink); + if (errPSB !== null) { + return [nu8$1, addContextToErr$1(errPSB, "final value returned by resolver link is not a valid skylink")]; + } + if (version !== 1n) { + return [nu8$1, "final value returned by resolver link is not a v1 skylink"]; + } + return [skylink, null]; +} + +// Establish the function that verifies the result is correct. +// +// The fileDataPtr input is an empty object that verifyDownloadResponse will +// fill with the fileData. It basically allows the verify function to +// communicate back to the caller. Note that the verify function might be +// called multiple times in a row if early portals fail to retrieve the data, +// but the verify function doesn't write to the fileDataPtr until it knows that +// the download is final. +function verifyDownloadResponse(response, u8Link, fileDataPtr) { + return new Promise((resolve) => { + // Currently the only valid successful response for a download is a + // 200. Anything else is unexpected and counts as an error. + if (response.status !== 200) { + resolve("unrecognized response status " + tryStringify$1(response.status) + ", expecting 200"); + return; + } + // Break the input link into its components. + let [version, offset, fetchSize, errBF] = parseSkylinkBitfield(u8Link); + if (errBF !== null) { + resolve(addContextToErr$1(errBF, "skylink bitfield could not be parsed")); + return; + } + // If this is a resolver skylink, we need to verify the resolver + // proofs. This conditional will update the value of 'u8Link' to be the + // value of the fully resolved link. + if (version === 2n) { + // Verify the resolver proofs and update the link to the correct + // link. + let proofJSON = response.headers.get("skynet-proof"); + if (proofJSON === null || proofJSON === undefined) { + resolve("response did not include resolver proofs"); + return; + } + let [proof, errPJ] = parseJSON(proofJSON); + if (errPJ !== null) { + resolve(addContextToErr$1(errPJ, "unable to parse resolver link proofs")); + return; + } + // We need to update the u8Link in-place so that the rest of the + // function doesn't need special handling. + let errVRLP; + [u8Link, errVRLP] = verifyResolverLinkProofs(u8Link, proof); + if (errVRLP !== null) { + resolve(addContextToErr$1(errVRLP, "unable to verify resolver link proofs")); + return; + } + // We also need to update the parsed bitfield, because the link has + // changed. + [version, offset, fetchSize, errBF] = parseSkylinkBitfield(u8Link); + if (errBF !== null) { + resolve(addContextToErr$1(errBF, "fully resolved link has invalid bitfield")); + return; + } + if (version !== 1n) { + resolve("fully resolved link does not have version 1"); + return; + } + } + response + .arrayBuffer() + .then((buf) => { + let [fileData, portalAtFault, errVD] = verifyDownload(u8Link.slice(2, 34), offset, fetchSize, buf); + if (errVD !== null && portalAtFault) { + resolve("received invalid download from portal"); + return; + } + if (errVD !== null) { + fileDataPtr.fileData = new Uint8Array(0); + fileDataPtr.err = addContextToErr$1(errVD, "file is corrupt"); + } + else { + fileDataPtr.fileData = fileData; + fileDataPtr.err = null; + } + // If the portal is not at fault, we tell progressiveFetch that + // the download was a success. The caller will have to check + // the fileDataPtr + resolve(null); + }) + .catch((err) => { + resolve(addContextToErr$1(err, "unable to read response body")); + }); + }); +} + +// progressiveFetchHelper is the full progressiveFetch function, split out into +// a helper because the inptus/api is more complicated but only necessary for +// internal use. +function progressiveFetchHelper$1(pfm, resolve, verifyFunction) { + // If we run out of portals, return an error. + if (pfm.remainingPortals.length === 0) { + let newLog = "query failed because all portals have been tried"; + pfm.logs.push(newLog); + resolve({ + success: false, + portal: null, + response: null, + portalsFailed: pfm.portalsFailed, + responsesFailed: pfm.responsesFailed, + messagesFailed: pfm.messagesFailed, + remainingPortals: null, + logs: pfm.logs, + }); + return; + } + // Grab the portal and query. + let portal = pfm.remainingPortals.shift(); + let query = portal + pfm.endpoint; + // Create a helper function for trying the next portal. + let nextPortal = function (response, log) { + if (response !== null) { + response + .clone() + .text() + .then((t) => { + pfm.logs.push(log); + pfm.portalsFailed.push(portal); + pfm.responsesFailed.push(response); + pfm.messagesFailed.push(t); + progressiveFetchHelper$1(pfm, resolve, verifyFunction); + }); + } + else { + pfm.logs.push(log); + pfm.portalsFailed.push(portal); + pfm.responsesFailed.push(response); + pfm.messagesFailed.push(""); + progressiveFetchHelper$1(pfm, resolve, verifyFunction); + } + }; + // Try sending the query to the portal. + fetch(query, pfm.fetchOpts) + .then((response) => { + // Check for a 5XX error. + if (!("status" in response) || typeof response.status !== "number") { + nextPortal(response, "portal has returned invalid response\n" + tryStringify$1({ portal, query })); + return; + } + if (response.status < 200 || response.status >= 300) { + nextPortal(response, "portal has returned error status\n" + tryStringify$1({ portal, query })); + return; + } + // Check the result against the verify function. + verifyFunction(response.clone()).then((errVF) => { + if (errVF !== null) { + nextPortal(response, "verify function has returned an error from portal " + portal + " - " + errVF); + return; + } + // Success! Return the response. + resolve({ + success: true, + portal, + response, + portalsFailed: pfm.portalsFailed, + responsesFailed: pfm.responsesFailed, + remainingPortals: pfm.remainingPortals, + messagesFailed: pfm.messagesFailed, + logs: pfm.logs, + }); + }); + }) + .catch((err) => { + // This portal failed, try again with the next portal. + nextPortal(null, "fetch returned an error\n" + tryStringify$1(err) + tryStringify$1(pfm.fetchOpts)); + return; + }); +} +// progressiveFetch will query multiple portals until one returns with a +// non-error response. In the event of a 4XX response, progressiveFetch will +// keep querying additional portals to try and find a working 2XX response. In +// the event that no working 2XX response is found, the first 4XX response will +// be returned. +// +// If progressiveFetch returns a 2XX response, it merely means that the portal +// returned a 2XX response. progressiveFetch cannot be confident that the +// portal has returned a correct/honest message, the verification has to be +// handled by the caller. The response (progressiveFetchResult) contains the +// list of portals that progressiveFetch hasn't tried yet. In the event that +// the 2XX response is not correct, the progressiveFetchResult contains the +// list of failover portals that have not been used yet, allowing +// progressiveFetch to be called again. +// +// This progressive method of querying portals helps prevent queries from +// failing, but if the first portal is not a good portal it introduces +// substantial latency. progressiveFetch does not do anything to make sure the +// portals are the best portals, it just queries them in order. The caller +// should make a best attempt to always have the best, most reliable and +// fastest portal as the first portal in the list. +// +// The reason that we don't blindly accept a 4XX response from a portal is that +// we have no way of verifying that the 4XX is legitimate. We don't trust the +// portal, and we can't give a rogue portal the opportunity to interrupt our +// user experience simply by returning a dishonest 404. So we need to keep +// querying more portals and gain confidence that the 404 a truthful response. +function progressiveFetch$1(endpoint, fetchOpts, portals, verifyFunction) { + let portalsCopy = [...portals]; + return new Promise((resolve) => { + let pfm = { + endpoint, + fetchOpts, + remainingPortals: portalsCopy, + portalsFailed: [], + responsesFailed: [], + messagesFailed: [], + logs: [], + }; + progressiveFetchHelper$1(pfm, resolve, verifyFunction); + }); +} + +// downloadSkylink will download the provided skylink. +function downloadSkylink(skylink) { + return new Promise((resolve) => { + // Get the Uint8Array of the input skylink. + let [u8Link, errBTB] = b64ToBuf(skylink); + if (errBTB !== null) { + resolve([new Uint8Array(0), addContextToErr$1(errBTB, "unable to decode skylink")]); + return; + } + if (!validSkylink(u8Link)) { + resolve([new Uint8Array(0), "skylink appears to be invalid"]); + return; + } + // Prepare the download call. + let endpoint = "/skynet/trustless/basesector/" + skylink; + let fileDataPtr = { fileData: new Uint8Array(0), err: null }; + let verifyFunction = function (response) { + return verifyDownloadResponse(response, u8Link, fileDataPtr); + }; + // Perform the download call. + progressiveFetch$1(endpoint, null, defaultPortalList, verifyFunction).then((result) => { + // Return an error if the call failed. + if (result.success !== true) { + // Check for a 404. + for (let i = 0; i < result.responsesFailed.length; i++) { + if (result.responsesFailed[i].status === 404) { + resolve([new Uint8Array(0), "404"]); + return; + } + } + // Error is not a 404, return the logs as the error. + let err = tryStringify$1(result.logs); + resolve([new Uint8Array(0), addContextToErr$1(err, "unable to complete download")]); + return; + } + // Check if the portal is honest but the download is corrupt. + if (fileDataPtr.err !== null) { + resolve([new Uint8Array(0), addContextToErr$1(fileDataPtr.err, "download is corrupt")]); + return; + } + resolve([fileDataPtr.fileData, null]); + }); + }); +} + +// verifyDecodedResp will verify the decoded response from a portal for a +// regRead call. +function verifyDecodedResp(resp, data, pubkey, datakey) { + // Status is expected to be 200. + if (resp.status !== 200) { + return "expected 200 response status, got: " + tryStringify$1(resp.status); + } + // Verify that all required fields were provided. + if (!("data" in data)) { + return "expected data field in response"; + } + if (typeof data.data !== "string") { + return "expected data field to be a string"; + } + if (!("revision" in data)) { + return "expected revision in response"; + } + if (typeof data.revision !== "bigint") { + return "expected revision to be a number"; + } + if (!("signature" in data)) { + return "expected signature in response"; + } + if (typeof data.signature !== "string") { + return "expected signature to be a string"; + } + // Parse out the fields we need. + let [entryData, errHTB] = hexToBuf(data.data); + if (errHTB !== null) { + return "could not decode registry data from response"; + } + let [sig, errHTB2] = hexToBuf(data.signature); + if (errHTB2 !== null) { + return "could not decode signature from response"; + } + // Verify the signature. + if (!verifyRegistrySignature(pubkey, datakey, entryData, data.revision, sig)) { + return "signature mismatch"; + } + // TODO: Need to be handling type 2 registry entries here otherwise we will + // be flagging non malicious portals as malicious. + return null; +} +// verifyRegistryReadResponse will verify that the registry read response from +// the portal was correct. +function verifyRegistryReadResponse(resp, pubkey, datakey) { + return new Promise((resolve) => { + resp + .text() + .then((str) => { + let [obj, errPJ] = parseJSON(str); + if (errPJ !== null) { + resolve(addContextToErr$1(errPJ, "unable to parse registry response")); + return; + } + let errVDR = verifyDecodedResp(resp, obj, pubkey, datakey); + if (errVDR !== null) { + resolve(addContextToErr$1(errVDR, "regRead response failed verification")); + return; + } + resolve(null); + }) + .catch((err) => { + resolve(addContextToErr$1(tryStringify$1(err), "unable to decode response")); + }); + }); +} +// verifyRegistryWriteResponse will verify that the response from a +// registryWrite call is valid. There's not much to verify beyond looking for +// the right response code, as the portal is not providing us with data, just +// confirming that a write succeeded. +function verifyRegistryWriteResponse(resp) { + return new Promise((resolve) => { + if (resp.status === 204) { + resolve(null); + } + resolve("expecting 200 status code for registry write, got:" + resp.status.toString()); + }); +} + +// stringifyjson.ts is split into a separate file to avoid a circular +// dependency. If you merge it with stringifytry.ts you have a circular import +// where err.js is importing stringify.js and stringify.js is importing err.js. +// Splitting the functions out resolves this issue. +// jsonStringify is a replacement for JSON.stringify that returns an error +// rather than throwing. +function jsonStringify(obj) { + try { + let str = JSON.stringify(obj); + return [str, null]; + } + catch (err) { + return ["", addContextToErr$1(tryStringify$1(err), "unable to stringify object")]; + } +} + +var dist$2 = /*#__PURE__*/Object.freeze({ + __proto__: null, + blake2b: blake2b, + defaultPortalList: defaultPortalList, + dictionary: dictionary, + downloadSkylink: downloadSkylink, + verifyDownload: verifyDownload, + verifyDownloadResponse: verifyDownloadResponse, + ed25519Sign: ed25519Sign, + ed25519Verify: ed25519Verify, + b64ToBuf: b64ToBuf, + bufToB64: bufToB64$1, + bufToHex: bufToHex, + bufToStr: bufToStr, + encodePrefixedBytes: encodePrefixedBytes, + encodeU64: encodeU64$1, + hexToBuf: hexToBuf, + addContextToErr: addContextToErr$1, + composeErr: composeErr$1, + blake2bAddLeafBytesToProofStack: blake2bAddLeafBytesToProofStack, + blake2bMerkleRoot: blake2bMerkleRoot, + blake2bProofStackRoot: blake2bProofStackRoot, + parseJSON: parseJSON, + progressiveFetch: progressiveFetch$1, + computeRegistrySignature: computeRegistrySignature, + deriveRegistryEntryID: deriveRegistryEntryID, + entryIDToSkylink: entryIDToSkylink, + resolverLink: resolverLink, + taggedRegistryEntryKeys: taggedRegistryEntryKeys, + verifyRegistrySignature: verifyRegistrySignature, + verifyRegistryReadResponse: verifyRegistryReadResponse, + verifyRegistryWriteResponse: verifyRegistryWriteResponse, + deriveChildSeed: deriveChildSeed, + deriveMyskyRootKeypair: deriveMyskyRootKeypair, + generateSeedPhraseDeterministic: generateSeedPhraseDeterministic, + seedPhraseToSeed: seedPhraseToSeed, + validSeedPhrase: validSeedPhrase, + sha512: sha512, + parseSkylinkBitfield: parseSkylinkBitfield, + skylinkV1Bitfield: skylinkV1Bitfield, + validateSkyfileMetadata: validateSkyfileMetadata, + validateSkyfilePath: validateSkyfilePath, + validSkylink: validSkylink, + verifyResolverLinkProofs: verifyResolverLinkProofs, + jsonStringify: jsonStringify, + tryStringify: tryStringify$1 +}); + +var require$$0 = /*@__PURE__*/getAugmentedNamespace(dist$2); + +var require$$1 = /*@__PURE__*/getAugmentedNamespace(seed); + +var require$$2$1 = /*@__PURE__*/getAugmentedNamespace(dictionary$1); + +/** + * Returns a `Buffer` instance from the given data URI `uri`. + * + * @param {String} uri Data URI to turn into a Buffer instance + * @returns {Buffer} Buffer instance from Data URI + * @api public + */ +function dataUriToBuffer(uri) { + if (!/^data:/i.test(uri)) { + throw new TypeError('`uri` does not appear to be a Data URI (must begin with "data:")'); + } + // strip newlines + uri = uri.replace(/\r?\n/g, ''); + // split the URI up into the "metadata" and the "data" portions + const firstComma = uri.indexOf(','); + if (firstComma === -1 || firstComma <= 4) { + throw new TypeError('malformed data: URI'); + } + // remove the "data:" scheme and parse the metadata + const meta = uri.substring(5, firstComma).split(';'); + let charset = ''; + let base64 = false; + const type = meta[0] || 'text/plain'; + let typeFull = type; + for (let i = 1; i < meta.length; i++) { + if (meta[i] === 'base64') { + base64 = true; + } + else { + typeFull += `;${meta[i]}`; + if (meta[i].indexOf('charset=') === 0) { + charset = meta[i].substring(8); + } + } + } + // defaults to US-ASCII only if type is not provided + if (!meta[0] && !charset.length) { + typeFull += ';charset=US-ASCII'; + charset = 'US-ASCII'; + } + // get the encoded data portion and decode URI-encoded chars + const encoding = base64 ? 'base64' : 'ascii'; + const data = unescape(uri.substring(firstComma + 1)); + const buffer = Buffer.from(data, encoding); + // set `.type` and `.typeFull` properties to MIME type + buffer.type = type; + buffer.typeFull = typeFull; + // set the `.charset` property + buffer.charset = charset; + return buffer; +} + +var ponyfill_es2018 = {exports: {}}; + +/** + * web-streams-polyfill v3.2.1 + */ + +var hasRequiredPonyfill_es2018; + +function requirePonyfill_es2018 () { + if (hasRequiredPonyfill_es2018) return ponyfill_es2018.exports; + hasRequiredPonyfill_es2018 = 1; + (function (module, exports) { + (function (global, factory) { + factory(exports) ; + }(commonjsGlobal, (function (exports) { + /// + const SymbolPolyfill = typeof Symbol === 'function' && typeof Symbol.iterator === 'symbol' ? + Symbol : + description => `Symbol(${description})`; + + /// + function noop() { + return undefined; + } + function getGlobals() { + if (typeof self !== 'undefined') { + return self; + } + else if (typeof window !== 'undefined') { + return window; + } + else if (typeof commonjsGlobal !== 'undefined') { + return commonjsGlobal; + } + return undefined; + } + const globals = getGlobals(); + + function typeIsObject(x) { + return (typeof x === 'object' && x !== null) || typeof x === 'function'; + } + const rethrowAssertionErrorRejection = noop; + + const originalPromise = Promise; + const originalPromiseThen = Promise.prototype.then; + const originalPromiseResolve = Promise.resolve.bind(originalPromise); + const originalPromiseReject = Promise.reject.bind(originalPromise); + function newPromise(executor) { + return new originalPromise(executor); + } + function promiseResolvedWith(value) { + return originalPromiseResolve(value); + } + function promiseRejectedWith(reason) { + return originalPromiseReject(reason); + } + function PerformPromiseThen(promise, onFulfilled, onRejected) { + // There doesn't appear to be any way to correctly emulate the behaviour from JavaScript, so this is just an + // approximation. + return originalPromiseThen.call(promise, onFulfilled, onRejected); + } + function uponPromise(promise, onFulfilled, onRejected) { + PerformPromiseThen(PerformPromiseThen(promise, onFulfilled, onRejected), undefined, rethrowAssertionErrorRejection); + } + function uponFulfillment(promise, onFulfilled) { + uponPromise(promise, onFulfilled); + } + function uponRejection(promise, onRejected) { + uponPromise(promise, undefined, onRejected); + } + function transformPromiseWith(promise, fulfillmentHandler, rejectionHandler) { + return PerformPromiseThen(promise, fulfillmentHandler, rejectionHandler); + } + function setPromiseIsHandledToTrue(promise) { + PerformPromiseThen(promise, undefined, rethrowAssertionErrorRejection); + } + const queueMicrotask = (() => { + const globalQueueMicrotask = globals && globals.queueMicrotask; + if (typeof globalQueueMicrotask === 'function') { + return globalQueueMicrotask; + } + const resolvedPromise = promiseResolvedWith(undefined); + return (fn) => PerformPromiseThen(resolvedPromise, fn); + })(); + function reflectCall(F, V, args) { + if (typeof F !== 'function') { + throw new TypeError('Argument is not a function'); + } + return Function.prototype.apply.call(F, V, args); + } + function promiseCall(F, V, args) { + try { + return promiseResolvedWith(reflectCall(F, V, args)); + } + catch (value) { + return promiseRejectedWith(value); + } + } + + // Original from Chromium + // https://chromium.googlesource.com/chromium/src/+/0aee4434a4dba42a42abaea9bfbc0cd196a63bc1/third_party/blink/renderer/core/streams/SimpleQueue.js + const QUEUE_MAX_ARRAY_SIZE = 16384; + /** + * Simple queue structure. + * + * Avoids scalability issues with using a packed array directly by using + * multiple arrays in a linked list and keeping the array size bounded. + */ + class SimpleQueue { + constructor() { + this._cursor = 0; + this._size = 0; + // _front and _back are always defined. + this._front = { + _elements: [], + _next: undefined + }; + this._back = this._front; + // The cursor is used to avoid calling Array.shift(). + // It contains the index of the front element of the array inside the + // front-most node. It is always in the range [0, QUEUE_MAX_ARRAY_SIZE). + this._cursor = 0; + // When there is only one node, size === elements.length - cursor. + this._size = 0; + } + get length() { + return this._size; + } + // For exception safety, this method is structured in order: + // 1. Read state + // 2. Calculate required state mutations + // 3. Perform state mutations + push(element) { + const oldBack = this._back; + let newBack = oldBack; + if (oldBack._elements.length === QUEUE_MAX_ARRAY_SIZE - 1) { + newBack = { + _elements: [], + _next: undefined + }; + } + // push() is the mutation most likely to throw an exception, so it + // goes first. + oldBack._elements.push(element); + if (newBack !== oldBack) { + this._back = newBack; + oldBack._next = newBack; + } + ++this._size; + } + // Like push(), shift() follows the read -> calculate -> mutate pattern for + // exception safety. + shift() { // must not be called on an empty queue + const oldFront = this._front; + let newFront = oldFront; + const oldCursor = this._cursor; + let newCursor = oldCursor + 1; + const elements = oldFront._elements; + const element = elements[oldCursor]; + if (newCursor === QUEUE_MAX_ARRAY_SIZE) { + newFront = oldFront._next; + newCursor = 0; + } + // No mutations before this point. + --this._size; + this._cursor = newCursor; + if (oldFront !== newFront) { + this._front = newFront; + } + // Permit shifted element to be garbage collected. + elements[oldCursor] = undefined; + return element; + } + // The tricky thing about forEach() is that it can be called + // re-entrantly. The queue may be mutated inside the callback. It is easy to + // see that push() within the callback has no negative effects since the end + // of the queue is checked for on every iteration. If shift() is called + // repeatedly within the callback then the next iteration may return an + // element that has been removed. In this case the callback will be called + // with undefined values until we either "catch up" with elements that still + // exist or reach the back of the queue. + forEach(callback) { + let i = this._cursor; + let node = this._front; + let elements = node._elements; + while (i !== elements.length || node._next !== undefined) { + if (i === elements.length) { + node = node._next; + elements = node._elements; + i = 0; + if (elements.length === 0) { + break; + } + } + callback(elements[i]); + ++i; + } + } + // Return the element that would be returned if shift() was called now, + // without modifying the queue. + peek() { // must not be called on an empty queue + const front = this._front; + const cursor = this._cursor; + return front._elements[cursor]; + } + } + + function ReadableStreamReaderGenericInitialize(reader, stream) { + reader._ownerReadableStream = stream; + stream._reader = reader; + if (stream._state === 'readable') { + defaultReaderClosedPromiseInitialize(reader); + } + else if (stream._state === 'closed') { + defaultReaderClosedPromiseInitializeAsResolved(reader); + } + else { + defaultReaderClosedPromiseInitializeAsRejected(reader, stream._storedError); + } + } + // A client of ReadableStreamDefaultReader and ReadableStreamBYOBReader may use these functions directly to bypass state + // check. + function ReadableStreamReaderGenericCancel(reader, reason) { + const stream = reader._ownerReadableStream; + return ReadableStreamCancel(stream, reason); + } + function ReadableStreamReaderGenericRelease(reader) { + if (reader._ownerReadableStream._state === 'readable') { + defaultReaderClosedPromiseReject(reader, new TypeError(`Reader was released and can no longer be used to monitor the stream's closedness`)); + } + else { + defaultReaderClosedPromiseResetToRejected(reader, new TypeError(`Reader was released and can no longer be used to monitor the stream's closedness`)); + } + reader._ownerReadableStream._reader = undefined; + reader._ownerReadableStream = undefined; + } + // Helper functions for the readers. + function readerLockException(name) { + return new TypeError('Cannot ' + name + ' a stream using a released reader'); + } + // Helper functions for the ReadableStreamDefaultReader. + function defaultReaderClosedPromiseInitialize(reader) { + reader._closedPromise = newPromise((resolve, reject) => { + reader._closedPromise_resolve = resolve; + reader._closedPromise_reject = reject; + }); + } + function defaultReaderClosedPromiseInitializeAsRejected(reader, reason) { + defaultReaderClosedPromiseInitialize(reader); + defaultReaderClosedPromiseReject(reader, reason); + } + function defaultReaderClosedPromiseInitializeAsResolved(reader) { + defaultReaderClosedPromiseInitialize(reader); + defaultReaderClosedPromiseResolve(reader); + } + function defaultReaderClosedPromiseReject(reader, reason) { + if (reader._closedPromise_reject === undefined) { + return; + } + setPromiseIsHandledToTrue(reader._closedPromise); + reader._closedPromise_reject(reason); + reader._closedPromise_resolve = undefined; + reader._closedPromise_reject = undefined; + } + function defaultReaderClosedPromiseResetToRejected(reader, reason) { + defaultReaderClosedPromiseInitializeAsRejected(reader, reason); + } + function defaultReaderClosedPromiseResolve(reader) { + if (reader._closedPromise_resolve === undefined) { + return; + } + reader._closedPromise_resolve(undefined); + reader._closedPromise_resolve = undefined; + reader._closedPromise_reject = undefined; + } + + const AbortSteps = SymbolPolyfill('[[AbortSteps]]'); + const ErrorSteps = SymbolPolyfill('[[ErrorSteps]]'); + const CancelSteps = SymbolPolyfill('[[CancelSteps]]'); + const PullSteps = SymbolPolyfill('[[PullSteps]]'); + + /// + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isFinite#Polyfill + const NumberIsFinite = Number.isFinite || function (x) { + return typeof x === 'number' && isFinite(x); + }; + + /// + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/trunc#Polyfill + const MathTrunc = Math.trunc || function (v) { + return v < 0 ? Math.ceil(v) : Math.floor(v); + }; + + // https://heycam.github.io/webidl/#idl-dictionaries + function isDictionary(x) { + return typeof x === 'object' || typeof x === 'function'; + } + function assertDictionary(obj, context) { + if (obj !== undefined && !isDictionary(obj)) { + throw new TypeError(`${context} is not an object.`); + } + } + // https://heycam.github.io/webidl/#idl-callback-functions + function assertFunction(x, context) { + if (typeof x !== 'function') { + throw new TypeError(`${context} is not a function.`); + } + } + // https://heycam.github.io/webidl/#idl-object + function isObject(x) { + return (typeof x === 'object' && x !== null) || typeof x === 'function'; + } + function assertObject(x, context) { + if (!isObject(x)) { + throw new TypeError(`${context} is not an object.`); + } + } + function assertRequiredArgument(x, position, context) { + if (x === undefined) { + throw new TypeError(`Parameter ${position} is required in '${context}'.`); + } + } + function assertRequiredField(x, field, context) { + if (x === undefined) { + throw new TypeError(`${field} is required in '${context}'.`); + } + } + // https://heycam.github.io/webidl/#idl-unrestricted-double + function convertUnrestrictedDouble(value) { + return Number(value); + } + function censorNegativeZero(x) { + return x === 0 ? 0 : x; + } + function integerPart(x) { + return censorNegativeZero(MathTrunc(x)); + } + // https://heycam.github.io/webidl/#idl-unsigned-long-long + function convertUnsignedLongLongWithEnforceRange(value, context) { + const lowerBound = 0; + const upperBound = Number.MAX_SAFE_INTEGER; + let x = Number(value); + x = censorNegativeZero(x); + if (!NumberIsFinite(x)) { + throw new TypeError(`${context} is not a finite number`); + } + x = integerPart(x); + if (x < lowerBound || x > upperBound) { + throw new TypeError(`${context} is outside the accepted range of ${lowerBound} to ${upperBound}, inclusive`); + } + if (!NumberIsFinite(x) || x === 0) { + return 0; + } + // TODO Use BigInt if supported? + // let xBigInt = BigInt(integerPart(x)); + // xBigInt = BigInt.asUintN(64, xBigInt); + // return Number(xBigInt); + return x; + } + + function assertReadableStream(x, context) { + if (!IsReadableStream(x)) { + throw new TypeError(`${context} is not a ReadableStream.`); + } + } + + // Abstract operations for the ReadableStream. + function AcquireReadableStreamDefaultReader(stream) { + return new ReadableStreamDefaultReader(stream); + } + // ReadableStream API exposed for controllers. + function ReadableStreamAddReadRequest(stream, readRequest) { + stream._reader._readRequests.push(readRequest); + } + function ReadableStreamFulfillReadRequest(stream, chunk, done) { + const reader = stream._reader; + const readRequest = reader._readRequests.shift(); + if (done) { + readRequest._closeSteps(); + } + else { + readRequest._chunkSteps(chunk); + } + } + function ReadableStreamGetNumReadRequests(stream) { + return stream._reader._readRequests.length; + } + function ReadableStreamHasDefaultReader(stream) { + const reader = stream._reader; + if (reader === undefined) { + return false; + } + if (!IsReadableStreamDefaultReader(reader)) { + return false; + } + return true; + } + /** + * A default reader vended by a {@link ReadableStream}. + * + * @public + */ + class ReadableStreamDefaultReader { + constructor(stream) { + assertRequiredArgument(stream, 1, 'ReadableStreamDefaultReader'); + assertReadableStream(stream, 'First parameter'); + if (IsReadableStreamLocked(stream)) { + throw new TypeError('This stream has already been locked for exclusive reading by another reader'); + } + ReadableStreamReaderGenericInitialize(this, stream); + this._readRequests = new SimpleQueue(); + } + /** + * Returns a promise that will be fulfilled when the stream becomes closed, + * or rejected if the stream ever errors or the reader's lock is released before the stream finishes closing. + */ + get closed() { + if (!IsReadableStreamDefaultReader(this)) { + return promiseRejectedWith(defaultReaderBrandCheckException('closed')); + } + return this._closedPromise; + } + /** + * If the reader is active, behaves the same as {@link ReadableStream.cancel | stream.cancel(reason)}. + */ + cancel(reason = undefined) { + if (!IsReadableStreamDefaultReader(this)) { + return promiseRejectedWith(defaultReaderBrandCheckException('cancel')); + } + if (this._ownerReadableStream === undefined) { + return promiseRejectedWith(readerLockException('cancel')); + } + return ReadableStreamReaderGenericCancel(this, reason); + } + /** + * Returns a promise that allows access to the next chunk from the stream's internal queue, if available. + * + * If reading a chunk causes the queue to become empty, more data will be pulled from the underlying source. + */ + read() { + if (!IsReadableStreamDefaultReader(this)) { + return promiseRejectedWith(defaultReaderBrandCheckException('read')); + } + if (this._ownerReadableStream === undefined) { + return promiseRejectedWith(readerLockException('read from')); + } + let resolvePromise; + let rejectPromise; + const promise = newPromise((resolve, reject) => { + resolvePromise = resolve; + rejectPromise = reject; + }); + const readRequest = { + _chunkSteps: chunk => resolvePromise({ value: chunk, done: false }), + _closeSteps: () => resolvePromise({ value: undefined, done: true }), + _errorSteps: e => rejectPromise(e) + }; + ReadableStreamDefaultReaderRead(this, readRequest); + return promise; + } + /** + * Releases the reader's lock on the corresponding stream. After the lock is released, the reader is no longer active. + * If the associated stream is errored when the lock is released, the reader will appear errored in the same way + * from now on; otherwise, the reader will appear closed. + * + * A reader's lock cannot be released while it still has a pending read request, i.e., if a promise returned by + * the reader's {@link ReadableStreamDefaultReader.read | read()} method has not yet been settled. Attempting to + * do so will throw a `TypeError` and leave the reader locked to the stream. + */ + releaseLock() { + if (!IsReadableStreamDefaultReader(this)) { + throw defaultReaderBrandCheckException('releaseLock'); + } + if (this._ownerReadableStream === undefined) { + return; + } + if (this._readRequests.length > 0) { + throw new TypeError('Tried to release a reader lock when that reader has pending read() calls un-settled'); + } + ReadableStreamReaderGenericRelease(this); + } + } + Object.defineProperties(ReadableStreamDefaultReader.prototype, { + cancel: { enumerable: true }, + read: { enumerable: true }, + releaseLock: { enumerable: true }, + closed: { enumerable: true } + }); + if (typeof SymbolPolyfill.toStringTag === 'symbol') { + Object.defineProperty(ReadableStreamDefaultReader.prototype, SymbolPolyfill.toStringTag, { + value: 'ReadableStreamDefaultReader', + configurable: true + }); + } + // Abstract operations for the readers. + function IsReadableStreamDefaultReader(x) { + if (!typeIsObject(x)) { + return false; + } + if (!Object.prototype.hasOwnProperty.call(x, '_readRequests')) { + return false; + } + return x instanceof ReadableStreamDefaultReader; + } + function ReadableStreamDefaultReaderRead(reader, readRequest) { + const stream = reader._ownerReadableStream; + stream._disturbed = true; + if (stream._state === 'closed') { + readRequest._closeSteps(); + } + else if (stream._state === 'errored') { + readRequest._errorSteps(stream._storedError); + } + else { + stream._readableStreamController[PullSteps](readRequest); + } + } + // Helper functions for the ReadableStreamDefaultReader. + function defaultReaderBrandCheckException(name) { + return new TypeError(`ReadableStreamDefaultReader.prototype.${name} can only be used on a ReadableStreamDefaultReader`); + } + + /// + /* eslint-disable @typescript-eslint/no-empty-function */ + const AsyncIteratorPrototype = Object.getPrototypeOf(Object.getPrototypeOf(async function* () { }).prototype); + + /// + class ReadableStreamAsyncIteratorImpl { + constructor(reader, preventCancel) { + this._ongoingPromise = undefined; + this._isFinished = false; + this._reader = reader; + this._preventCancel = preventCancel; + } + next() { + const nextSteps = () => this._nextSteps(); + this._ongoingPromise = this._ongoingPromise ? + transformPromiseWith(this._ongoingPromise, nextSteps, nextSteps) : + nextSteps(); + return this._ongoingPromise; + } + return(value) { + const returnSteps = () => this._returnSteps(value); + return this._ongoingPromise ? + transformPromiseWith(this._ongoingPromise, returnSteps, returnSteps) : + returnSteps(); + } + _nextSteps() { + if (this._isFinished) { + return Promise.resolve({ value: undefined, done: true }); + } + const reader = this._reader; + if (reader._ownerReadableStream === undefined) { + return promiseRejectedWith(readerLockException('iterate')); + } + let resolvePromise; + let rejectPromise; + const promise = newPromise((resolve, reject) => { + resolvePromise = resolve; + rejectPromise = reject; + }); + const readRequest = { + _chunkSteps: chunk => { + this._ongoingPromise = undefined; + // This needs to be delayed by one microtask, otherwise we stop pulling too early which breaks a test. + // FIXME Is this a bug in the specification, or in the test? + queueMicrotask(() => resolvePromise({ value: chunk, done: false })); + }, + _closeSteps: () => { + this._ongoingPromise = undefined; + this._isFinished = true; + ReadableStreamReaderGenericRelease(reader); + resolvePromise({ value: undefined, done: true }); + }, + _errorSteps: reason => { + this._ongoingPromise = undefined; + this._isFinished = true; + ReadableStreamReaderGenericRelease(reader); + rejectPromise(reason); + } + }; + ReadableStreamDefaultReaderRead(reader, readRequest); + return promise; + } + _returnSteps(value) { + if (this._isFinished) { + return Promise.resolve({ value, done: true }); + } + this._isFinished = true; + const reader = this._reader; + if (reader._ownerReadableStream === undefined) { + return promiseRejectedWith(readerLockException('finish iterating')); + } + if (!this._preventCancel) { + const result = ReadableStreamReaderGenericCancel(reader, value); + ReadableStreamReaderGenericRelease(reader); + return transformPromiseWith(result, () => ({ value, done: true })); + } + ReadableStreamReaderGenericRelease(reader); + return promiseResolvedWith({ value, done: true }); + } + } + const ReadableStreamAsyncIteratorPrototype = { + next() { + if (!IsReadableStreamAsyncIterator(this)) { + return promiseRejectedWith(streamAsyncIteratorBrandCheckException('next')); + } + return this._asyncIteratorImpl.next(); + }, + return(value) { + if (!IsReadableStreamAsyncIterator(this)) { + return promiseRejectedWith(streamAsyncIteratorBrandCheckException('return')); + } + return this._asyncIteratorImpl.return(value); + } + }; + if (AsyncIteratorPrototype !== undefined) { + Object.setPrototypeOf(ReadableStreamAsyncIteratorPrototype, AsyncIteratorPrototype); + } + // Abstract operations for the ReadableStream. + function AcquireReadableStreamAsyncIterator(stream, preventCancel) { + const reader = AcquireReadableStreamDefaultReader(stream); + const impl = new ReadableStreamAsyncIteratorImpl(reader, preventCancel); + const iterator = Object.create(ReadableStreamAsyncIteratorPrototype); + iterator._asyncIteratorImpl = impl; + return iterator; + } + function IsReadableStreamAsyncIterator(x) { + if (!typeIsObject(x)) { + return false; + } + if (!Object.prototype.hasOwnProperty.call(x, '_asyncIteratorImpl')) { + return false; + } + try { + // noinspection SuspiciousTypeOfGuard + return x._asyncIteratorImpl instanceof + ReadableStreamAsyncIteratorImpl; + } + catch (_a) { + return false; + } + } + // Helper functions for the ReadableStream. + function streamAsyncIteratorBrandCheckException(name) { + return new TypeError(`ReadableStreamAsyncIterator.${name} can only be used on a ReadableSteamAsyncIterator`); + } + + /// + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isNaN#Polyfill + const NumberIsNaN = Number.isNaN || function (x) { + // eslint-disable-next-line no-self-compare + return x !== x; + }; + + function CreateArrayFromList(elements) { + // We use arrays to represent lists, so this is basically a no-op. + // Do a slice though just in case we happen to depend on the unique-ness. + return elements.slice(); + } + function CopyDataBlockBytes(dest, destOffset, src, srcOffset, n) { + new Uint8Array(dest).set(new Uint8Array(src, srcOffset, n), destOffset); + } + // Not implemented correctly + function TransferArrayBuffer(O) { + return O; + } + // Not implemented correctly + // eslint-disable-next-line @typescript-eslint/no-unused-vars + function IsDetachedBuffer(O) { + return false; + } + function ArrayBufferSlice(buffer, begin, end) { + // ArrayBuffer.prototype.slice is not available on IE10 + // https://www.caniuse.com/mdn-javascript_builtins_arraybuffer_slice + if (buffer.slice) { + return buffer.slice(begin, end); + } + const length = end - begin; + const slice = new ArrayBuffer(length); + CopyDataBlockBytes(slice, 0, buffer, begin, length); + return slice; + } + + function IsNonNegativeNumber(v) { + if (typeof v !== 'number') { + return false; + } + if (NumberIsNaN(v)) { + return false; + } + if (v < 0) { + return false; + } + return true; + } + function CloneAsUint8Array(O) { + const buffer = ArrayBufferSlice(O.buffer, O.byteOffset, O.byteOffset + O.byteLength); + return new Uint8Array(buffer); + } + + function DequeueValue(container) { + const pair = container._queue.shift(); + container._queueTotalSize -= pair.size; + if (container._queueTotalSize < 0) { + container._queueTotalSize = 0; + } + return pair.value; + } + function EnqueueValueWithSize(container, value, size) { + if (!IsNonNegativeNumber(size) || size === Infinity) { + throw new RangeError('Size must be a finite, non-NaN, non-negative number.'); + } + container._queue.push({ value, size }); + container._queueTotalSize += size; + } + function PeekQueueValue(container) { + const pair = container._queue.peek(); + return pair.value; + } + function ResetQueue(container) { + container._queue = new SimpleQueue(); + container._queueTotalSize = 0; + } + + /** + * A pull-into request in a {@link ReadableByteStreamController}. + * + * @public + */ + class ReadableStreamBYOBRequest { + constructor() { + throw new TypeError('Illegal constructor'); + } + /** + * Returns the view for writing in to, or `null` if the BYOB request has already been responded to. + */ + get view() { + if (!IsReadableStreamBYOBRequest(this)) { + throw byobRequestBrandCheckException('view'); + } + return this._view; + } + respond(bytesWritten) { + if (!IsReadableStreamBYOBRequest(this)) { + throw byobRequestBrandCheckException('respond'); + } + assertRequiredArgument(bytesWritten, 1, 'respond'); + bytesWritten = convertUnsignedLongLongWithEnforceRange(bytesWritten, 'First parameter'); + if (this._associatedReadableByteStreamController === undefined) { + throw new TypeError('This BYOB request has been invalidated'); + } + if (IsDetachedBuffer(this._view.buffer)) ; + ReadableByteStreamControllerRespond(this._associatedReadableByteStreamController, bytesWritten); + } + respondWithNewView(view) { + if (!IsReadableStreamBYOBRequest(this)) { + throw byobRequestBrandCheckException('respondWithNewView'); + } + assertRequiredArgument(view, 1, 'respondWithNewView'); + if (!ArrayBuffer.isView(view)) { + throw new TypeError('You can only respond with array buffer views'); + } + if (this._associatedReadableByteStreamController === undefined) { + throw new TypeError('This BYOB request has been invalidated'); + } + if (IsDetachedBuffer(view.buffer)) ; + ReadableByteStreamControllerRespondWithNewView(this._associatedReadableByteStreamController, view); + } + } + Object.defineProperties(ReadableStreamBYOBRequest.prototype, { + respond: { enumerable: true }, + respondWithNewView: { enumerable: true }, + view: { enumerable: true } + }); + if (typeof SymbolPolyfill.toStringTag === 'symbol') { + Object.defineProperty(ReadableStreamBYOBRequest.prototype, SymbolPolyfill.toStringTag, { + value: 'ReadableStreamBYOBRequest', + configurable: true + }); + } + /** + * Allows control of a {@link ReadableStream | readable byte stream}'s state and internal queue. + * + * @public + */ + class ReadableByteStreamController { + constructor() { + throw new TypeError('Illegal constructor'); + } + /** + * Returns the current BYOB pull request, or `null` if there isn't one. + */ + get byobRequest() { + if (!IsReadableByteStreamController(this)) { + throw byteStreamControllerBrandCheckException('byobRequest'); + } + return ReadableByteStreamControllerGetBYOBRequest(this); + } + /** + * Returns the desired size to fill the controlled stream's internal queue. It can be negative, if the queue is + * over-full. An underlying byte source ought to use this information to determine when and how to apply backpressure. + */ + get desiredSize() { + if (!IsReadableByteStreamController(this)) { + throw byteStreamControllerBrandCheckException('desiredSize'); + } + return ReadableByteStreamControllerGetDesiredSize(this); + } + /** + * Closes the controlled readable stream. Consumers will still be able to read any previously-enqueued chunks from + * the stream, but once those are read, the stream will become closed. + */ + close() { + if (!IsReadableByteStreamController(this)) { + throw byteStreamControllerBrandCheckException('close'); + } + if (this._closeRequested) { + throw new TypeError('The stream has already been closed; do not close it again!'); + } + const state = this._controlledReadableByteStream._state; + if (state !== 'readable') { + throw new TypeError(`The stream (in ${state} state) is not in the readable state and cannot be closed`); + } + ReadableByteStreamControllerClose(this); + } + enqueue(chunk) { + if (!IsReadableByteStreamController(this)) { + throw byteStreamControllerBrandCheckException('enqueue'); + } + assertRequiredArgument(chunk, 1, 'enqueue'); + if (!ArrayBuffer.isView(chunk)) { + throw new TypeError('chunk must be an array buffer view'); + } + if (chunk.byteLength === 0) { + throw new TypeError('chunk must have non-zero byteLength'); + } + if (chunk.buffer.byteLength === 0) { + throw new TypeError(`chunk's buffer must have non-zero byteLength`); + } + if (this._closeRequested) { + throw new TypeError('stream is closed or draining'); + } + const state = this._controlledReadableByteStream._state; + if (state !== 'readable') { + throw new TypeError(`The stream (in ${state} state) is not in the readable state and cannot be enqueued to`); + } + ReadableByteStreamControllerEnqueue(this, chunk); + } + /** + * Errors the controlled readable stream, making all future interactions with it fail with the given error `e`. + */ + error(e = undefined) { + if (!IsReadableByteStreamController(this)) { + throw byteStreamControllerBrandCheckException('error'); + } + ReadableByteStreamControllerError(this, e); + } + /** @internal */ + [CancelSteps](reason) { + ReadableByteStreamControllerClearPendingPullIntos(this); + ResetQueue(this); + const result = this._cancelAlgorithm(reason); + ReadableByteStreamControllerClearAlgorithms(this); + return result; + } + /** @internal */ + [PullSteps](readRequest) { + const stream = this._controlledReadableByteStream; + if (this._queueTotalSize > 0) { + const entry = this._queue.shift(); + this._queueTotalSize -= entry.byteLength; + ReadableByteStreamControllerHandleQueueDrain(this); + const view = new Uint8Array(entry.buffer, entry.byteOffset, entry.byteLength); + readRequest._chunkSteps(view); + return; + } + const autoAllocateChunkSize = this._autoAllocateChunkSize; + if (autoAllocateChunkSize !== undefined) { + let buffer; + try { + buffer = new ArrayBuffer(autoAllocateChunkSize); + } + catch (bufferE) { + readRequest._errorSteps(bufferE); + return; + } + const pullIntoDescriptor = { + buffer, + bufferByteLength: autoAllocateChunkSize, + byteOffset: 0, + byteLength: autoAllocateChunkSize, + bytesFilled: 0, + elementSize: 1, + viewConstructor: Uint8Array, + readerType: 'default' + }; + this._pendingPullIntos.push(pullIntoDescriptor); + } + ReadableStreamAddReadRequest(stream, readRequest); + ReadableByteStreamControllerCallPullIfNeeded(this); + } + } + Object.defineProperties(ReadableByteStreamController.prototype, { + close: { enumerable: true }, + enqueue: { enumerable: true }, + error: { enumerable: true }, + byobRequest: { enumerable: true }, + desiredSize: { enumerable: true } + }); + if (typeof SymbolPolyfill.toStringTag === 'symbol') { + Object.defineProperty(ReadableByteStreamController.prototype, SymbolPolyfill.toStringTag, { + value: 'ReadableByteStreamController', + configurable: true + }); + } + // Abstract operations for the ReadableByteStreamController. + function IsReadableByteStreamController(x) { + if (!typeIsObject(x)) { + return false; + } + if (!Object.prototype.hasOwnProperty.call(x, '_controlledReadableByteStream')) { + return false; + } + return x instanceof ReadableByteStreamController; + } + function IsReadableStreamBYOBRequest(x) { + if (!typeIsObject(x)) { + return false; + } + if (!Object.prototype.hasOwnProperty.call(x, '_associatedReadableByteStreamController')) { + return false; + } + return x instanceof ReadableStreamBYOBRequest; + } + function ReadableByteStreamControllerCallPullIfNeeded(controller) { + const shouldPull = ReadableByteStreamControllerShouldCallPull(controller); + if (!shouldPull) { + return; + } + if (controller._pulling) { + controller._pullAgain = true; + return; + } + controller._pulling = true; + // TODO: Test controller argument + const pullPromise = controller._pullAlgorithm(); + uponPromise(pullPromise, () => { + controller._pulling = false; + if (controller._pullAgain) { + controller._pullAgain = false; + ReadableByteStreamControllerCallPullIfNeeded(controller); + } + }, e => { + ReadableByteStreamControllerError(controller, e); + }); + } + function ReadableByteStreamControllerClearPendingPullIntos(controller) { + ReadableByteStreamControllerInvalidateBYOBRequest(controller); + controller._pendingPullIntos = new SimpleQueue(); + } + function ReadableByteStreamControllerCommitPullIntoDescriptor(stream, pullIntoDescriptor) { + let done = false; + if (stream._state === 'closed') { + done = true; + } + const filledView = ReadableByteStreamControllerConvertPullIntoDescriptor(pullIntoDescriptor); + if (pullIntoDescriptor.readerType === 'default') { + ReadableStreamFulfillReadRequest(stream, filledView, done); + } + else { + ReadableStreamFulfillReadIntoRequest(stream, filledView, done); + } + } + function ReadableByteStreamControllerConvertPullIntoDescriptor(pullIntoDescriptor) { + const bytesFilled = pullIntoDescriptor.bytesFilled; + const elementSize = pullIntoDescriptor.elementSize; + return new pullIntoDescriptor.viewConstructor(pullIntoDescriptor.buffer, pullIntoDescriptor.byteOffset, bytesFilled / elementSize); + } + function ReadableByteStreamControllerEnqueueChunkToQueue(controller, buffer, byteOffset, byteLength) { + controller._queue.push({ buffer, byteOffset, byteLength }); + controller._queueTotalSize += byteLength; + } + function ReadableByteStreamControllerFillPullIntoDescriptorFromQueue(controller, pullIntoDescriptor) { + const elementSize = pullIntoDescriptor.elementSize; + const currentAlignedBytes = pullIntoDescriptor.bytesFilled - pullIntoDescriptor.bytesFilled % elementSize; + const maxBytesToCopy = Math.min(controller._queueTotalSize, pullIntoDescriptor.byteLength - pullIntoDescriptor.bytesFilled); + const maxBytesFilled = pullIntoDescriptor.bytesFilled + maxBytesToCopy; + const maxAlignedBytes = maxBytesFilled - maxBytesFilled % elementSize; + let totalBytesToCopyRemaining = maxBytesToCopy; + let ready = false; + if (maxAlignedBytes > currentAlignedBytes) { + totalBytesToCopyRemaining = maxAlignedBytes - pullIntoDescriptor.bytesFilled; + ready = true; + } + const queue = controller._queue; + while (totalBytesToCopyRemaining > 0) { + const headOfQueue = queue.peek(); + const bytesToCopy = Math.min(totalBytesToCopyRemaining, headOfQueue.byteLength); + const destStart = pullIntoDescriptor.byteOffset + pullIntoDescriptor.bytesFilled; + CopyDataBlockBytes(pullIntoDescriptor.buffer, destStart, headOfQueue.buffer, headOfQueue.byteOffset, bytesToCopy); + if (headOfQueue.byteLength === bytesToCopy) { + queue.shift(); + } + else { + headOfQueue.byteOffset += bytesToCopy; + headOfQueue.byteLength -= bytesToCopy; + } + controller._queueTotalSize -= bytesToCopy; + ReadableByteStreamControllerFillHeadPullIntoDescriptor(controller, bytesToCopy, pullIntoDescriptor); + totalBytesToCopyRemaining -= bytesToCopy; + } + return ready; + } + function ReadableByteStreamControllerFillHeadPullIntoDescriptor(controller, size, pullIntoDescriptor) { + pullIntoDescriptor.bytesFilled += size; + } + function ReadableByteStreamControllerHandleQueueDrain(controller) { + if (controller._queueTotalSize === 0 && controller._closeRequested) { + ReadableByteStreamControllerClearAlgorithms(controller); + ReadableStreamClose(controller._controlledReadableByteStream); + } + else { + ReadableByteStreamControllerCallPullIfNeeded(controller); + } + } + function ReadableByteStreamControllerInvalidateBYOBRequest(controller) { + if (controller._byobRequest === null) { + return; + } + controller._byobRequest._associatedReadableByteStreamController = undefined; + controller._byobRequest._view = null; + controller._byobRequest = null; + } + function ReadableByteStreamControllerProcessPullIntoDescriptorsUsingQueue(controller) { + while (controller._pendingPullIntos.length > 0) { + if (controller._queueTotalSize === 0) { + return; + } + const pullIntoDescriptor = controller._pendingPullIntos.peek(); + if (ReadableByteStreamControllerFillPullIntoDescriptorFromQueue(controller, pullIntoDescriptor)) { + ReadableByteStreamControllerShiftPendingPullInto(controller); + ReadableByteStreamControllerCommitPullIntoDescriptor(controller._controlledReadableByteStream, pullIntoDescriptor); + } + } + } + function ReadableByteStreamControllerPullInto(controller, view, readIntoRequest) { + const stream = controller._controlledReadableByteStream; + let elementSize = 1; + if (view.constructor !== DataView) { + elementSize = view.constructor.BYTES_PER_ELEMENT; + } + const ctor = view.constructor; + // try { + const buffer = TransferArrayBuffer(view.buffer); + // } catch (e) { + // readIntoRequest._errorSteps(e); + // return; + // } + const pullIntoDescriptor = { + buffer, + bufferByteLength: buffer.byteLength, + byteOffset: view.byteOffset, + byteLength: view.byteLength, + bytesFilled: 0, + elementSize, + viewConstructor: ctor, + readerType: 'byob' + }; + if (controller._pendingPullIntos.length > 0) { + controller._pendingPullIntos.push(pullIntoDescriptor); + // No ReadableByteStreamControllerCallPullIfNeeded() call since: + // - No change happens on desiredSize + // - The source has already been notified of that there's at least 1 pending read(view) + ReadableStreamAddReadIntoRequest(stream, readIntoRequest); + return; + } + if (stream._state === 'closed') { + const emptyView = new ctor(pullIntoDescriptor.buffer, pullIntoDescriptor.byteOffset, 0); + readIntoRequest._closeSteps(emptyView); + return; + } + if (controller._queueTotalSize > 0) { + if (ReadableByteStreamControllerFillPullIntoDescriptorFromQueue(controller, pullIntoDescriptor)) { + const filledView = ReadableByteStreamControllerConvertPullIntoDescriptor(pullIntoDescriptor); + ReadableByteStreamControllerHandleQueueDrain(controller); + readIntoRequest._chunkSteps(filledView); + return; + } + if (controller._closeRequested) { + const e = new TypeError('Insufficient bytes to fill elements in the given buffer'); + ReadableByteStreamControllerError(controller, e); + readIntoRequest._errorSteps(e); + return; + } + } + controller._pendingPullIntos.push(pullIntoDescriptor); + ReadableStreamAddReadIntoRequest(stream, readIntoRequest); + ReadableByteStreamControllerCallPullIfNeeded(controller); + } + function ReadableByteStreamControllerRespondInClosedState(controller, firstDescriptor) { + const stream = controller._controlledReadableByteStream; + if (ReadableStreamHasBYOBReader(stream)) { + while (ReadableStreamGetNumReadIntoRequests(stream) > 0) { + const pullIntoDescriptor = ReadableByteStreamControllerShiftPendingPullInto(controller); + ReadableByteStreamControllerCommitPullIntoDescriptor(stream, pullIntoDescriptor); + } + } + } + function ReadableByteStreamControllerRespondInReadableState(controller, bytesWritten, pullIntoDescriptor) { + ReadableByteStreamControllerFillHeadPullIntoDescriptor(controller, bytesWritten, pullIntoDescriptor); + if (pullIntoDescriptor.bytesFilled < pullIntoDescriptor.elementSize) { + return; + } + ReadableByteStreamControllerShiftPendingPullInto(controller); + const remainderSize = pullIntoDescriptor.bytesFilled % pullIntoDescriptor.elementSize; + if (remainderSize > 0) { + const end = pullIntoDescriptor.byteOffset + pullIntoDescriptor.bytesFilled; + const remainder = ArrayBufferSlice(pullIntoDescriptor.buffer, end - remainderSize, end); + ReadableByteStreamControllerEnqueueChunkToQueue(controller, remainder, 0, remainder.byteLength); + } + pullIntoDescriptor.bytesFilled -= remainderSize; + ReadableByteStreamControllerCommitPullIntoDescriptor(controller._controlledReadableByteStream, pullIntoDescriptor); + ReadableByteStreamControllerProcessPullIntoDescriptorsUsingQueue(controller); + } + function ReadableByteStreamControllerRespondInternal(controller, bytesWritten) { + const firstDescriptor = controller._pendingPullIntos.peek(); + ReadableByteStreamControllerInvalidateBYOBRequest(controller); + const state = controller._controlledReadableByteStream._state; + if (state === 'closed') { + ReadableByteStreamControllerRespondInClosedState(controller); + } + else { + ReadableByteStreamControllerRespondInReadableState(controller, bytesWritten, firstDescriptor); + } + ReadableByteStreamControllerCallPullIfNeeded(controller); + } + function ReadableByteStreamControllerShiftPendingPullInto(controller) { + const descriptor = controller._pendingPullIntos.shift(); + return descriptor; + } + function ReadableByteStreamControllerShouldCallPull(controller) { + const stream = controller._controlledReadableByteStream; + if (stream._state !== 'readable') { + return false; + } + if (controller._closeRequested) { + return false; + } + if (!controller._started) { + return false; + } + if (ReadableStreamHasDefaultReader(stream) && ReadableStreamGetNumReadRequests(stream) > 0) { + return true; + } + if (ReadableStreamHasBYOBReader(stream) && ReadableStreamGetNumReadIntoRequests(stream) > 0) { + return true; + } + const desiredSize = ReadableByteStreamControllerGetDesiredSize(controller); + if (desiredSize > 0) { + return true; + } + return false; + } + function ReadableByteStreamControllerClearAlgorithms(controller) { + controller._pullAlgorithm = undefined; + controller._cancelAlgorithm = undefined; + } + // A client of ReadableByteStreamController may use these functions directly to bypass state check. + function ReadableByteStreamControllerClose(controller) { + const stream = controller._controlledReadableByteStream; + if (controller._closeRequested || stream._state !== 'readable') { + return; + } + if (controller._queueTotalSize > 0) { + controller._closeRequested = true; + return; + } + if (controller._pendingPullIntos.length > 0) { + const firstPendingPullInto = controller._pendingPullIntos.peek(); + if (firstPendingPullInto.bytesFilled > 0) { + const e = new TypeError('Insufficient bytes to fill elements in the given buffer'); + ReadableByteStreamControllerError(controller, e); + throw e; + } + } + ReadableByteStreamControllerClearAlgorithms(controller); + ReadableStreamClose(stream); + } + function ReadableByteStreamControllerEnqueue(controller, chunk) { + const stream = controller._controlledReadableByteStream; + if (controller._closeRequested || stream._state !== 'readable') { + return; + } + const buffer = chunk.buffer; + const byteOffset = chunk.byteOffset; + const byteLength = chunk.byteLength; + const transferredBuffer = TransferArrayBuffer(buffer); + if (controller._pendingPullIntos.length > 0) { + const firstPendingPullInto = controller._pendingPullIntos.peek(); + if (IsDetachedBuffer(firstPendingPullInto.buffer)) ; + firstPendingPullInto.buffer = TransferArrayBuffer(firstPendingPullInto.buffer); + } + ReadableByteStreamControllerInvalidateBYOBRequest(controller); + if (ReadableStreamHasDefaultReader(stream)) { + if (ReadableStreamGetNumReadRequests(stream) === 0) { + ReadableByteStreamControllerEnqueueChunkToQueue(controller, transferredBuffer, byteOffset, byteLength); + } + else { + if (controller._pendingPullIntos.length > 0) { + ReadableByteStreamControllerShiftPendingPullInto(controller); + } + const transferredView = new Uint8Array(transferredBuffer, byteOffset, byteLength); + ReadableStreamFulfillReadRequest(stream, transferredView, false); + } + } + else if (ReadableStreamHasBYOBReader(stream)) { + // TODO: Ideally in this branch detaching should happen only if the buffer is not consumed fully. + ReadableByteStreamControllerEnqueueChunkToQueue(controller, transferredBuffer, byteOffset, byteLength); + ReadableByteStreamControllerProcessPullIntoDescriptorsUsingQueue(controller); + } + else { + ReadableByteStreamControllerEnqueueChunkToQueue(controller, transferredBuffer, byteOffset, byteLength); + } + ReadableByteStreamControllerCallPullIfNeeded(controller); + } + function ReadableByteStreamControllerError(controller, e) { + const stream = controller._controlledReadableByteStream; + if (stream._state !== 'readable') { + return; + } + ReadableByteStreamControllerClearPendingPullIntos(controller); + ResetQueue(controller); + ReadableByteStreamControllerClearAlgorithms(controller); + ReadableStreamError(stream, e); + } + function ReadableByteStreamControllerGetBYOBRequest(controller) { + if (controller._byobRequest === null && controller._pendingPullIntos.length > 0) { + const firstDescriptor = controller._pendingPullIntos.peek(); + const view = new Uint8Array(firstDescriptor.buffer, firstDescriptor.byteOffset + firstDescriptor.bytesFilled, firstDescriptor.byteLength - firstDescriptor.bytesFilled); + const byobRequest = Object.create(ReadableStreamBYOBRequest.prototype); + SetUpReadableStreamBYOBRequest(byobRequest, controller, view); + controller._byobRequest = byobRequest; + } + return controller._byobRequest; + } + function ReadableByteStreamControllerGetDesiredSize(controller) { + const state = controller._controlledReadableByteStream._state; + if (state === 'errored') { + return null; + } + if (state === 'closed') { + return 0; + } + return controller._strategyHWM - controller._queueTotalSize; + } + function ReadableByteStreamControllerRespond(controller, bytesWritten) { + const firstDescriptor = controller._pendingPullIntos.peek(); + const state = controller._controlledReadableByteStream._state; + if (state === 'closed') { + if (bytesWritten !== 0) { + throw new TypeError('bytesWritten must be 0 when calling respond() on a closed stream'); + } + } + else { + if (bytesWritten === 0) { + throw new TypeError('bytesWritten must be greater than 0 when calling respond() on a readable stream'); + } + if (firstDescriptor.bytesFilled + bytesWritten > firstDescriptor.byteLength) { + throw new RangeError('bytesWritten out of range'); + } + } + firstDescriptor.buffer = TransferArrayBuffer(firstDescriptor.buffer); + ReadableByteStreamControllerRespondInternal(controller, bytesWritten); + } + function ReadableByteStreamControllerRespondWithNewView(controller, view) { + const firstDescriptor = controller._pendingPullIntos.peek(); + const state = controller._controlledReadableByteStream._state; + if (state === 'closed') { + if (view.byteLength !== 0) { + throw new TypeError('The view\'s length must be 0 when calling respondWithNewView() on a closed stream'); + } + } + else { + if (view.byteLength === 0) { + throw new TypeError('The view\'s length must be greater than 0 when calling respondWithNewView() on a readable stream'); + } + } + if (firstDescriptor.byteOffset + firstDescriptor.bytesFilled !== view.byteOffset) { + throw new RangeError('The region specified by view does not match byobRequest'); + } + if (firstDescriptor.bufferByteLength !== view.buffer.byteLength) { + throw new RangeError('The buffer of view has different capacity than byobRequest'); + } + if (firstDescriptor.bytesFilled + view.byteLength > firstDescriptor.byteLength) { + throw new RangeError('The region specified by view is larger than byobRequest'); + } + const viewByteLength = view.byteLength; + firstDescriptor.buffer = TransferArrayBuffer(view.buffer); + ReadableByteStreamControllerRespondInternal(controller, viewByteLength); + } + function SetUpReadableByteStreamController(stream, controller, startAlgorithm, pullAlgorithm, cancelAlgorithm, highWaterMark, autoAllocateChunkSize) { + controller._controlledReadableByteStream = stream; + controller._pullAgain = false; + controller._pulling = false; + controller._byobRequest = null; + // Need to set the slots so that the assert doesn't fire. In the spec the slots already exist implicitly. + controller._queue = controller._queueTotalSize = undefined; + ResetQueue(controller); + controller._closeRequested = false; + controller._started = false; + controller._strategyHWM = highWaterMark; + controller._pullAlgorithm = pullAlgorithm; + controller._cancelAlgorithm = cancelAlgorithm; + controller._autoAllocateChunkSize = autoAllocateChunkSize; + controller._pendingPullIntos = new SimpleQueue(); + stream._readableStreamController = controller; + const startResult = startAlgorithm(); + uponPromise(promiseResolvedWith(startResult), () => { + controller._started = true; + ReadableByteStreamControllerCallPullIfNeeded(controller); + }, r => { + ReadableByteStreamControllerError(controller, r); + }); + } + function SetUpReadableByteStreamControllerFromUnderlyingSource(stream, underlyingByteSource, highWaterMark) { + const controller = Object.create(ReadableByteStreamController.prototype); + let startAlgorithm = () => undefined; + let pullAlgorithm = () => promiseResolvedWith(undefined); + let cancelAlgorithm = () => promiseResolvedWith(undefined); + if (underlyingByteSource.start !== undefined) { + startAlgorithm = () => underlyingByteSource.start(controller); + } + if (underlyingByteSource.pull !== undefined) { + pullAlgorithm = () => underlyingByteSource.pull(controller); + } + if (underlyingByteSource.cancel !== undefined) { + cancelAlgorithm = reason => underlyingByteSource.cancel(reason); + } + const autoAllocateChunkSize = underlyingByteSource.autoAllocateChunkSize; + if (autoAllocateChunkSize === 0) { + throw new TypeError('autoAllocateChunkSize must be greater than 0'); + } + SetUpReadableByteStreamController(stream, controller, startAlgorithm, pullAlgorithm, cancelAlgorithm, highWaterMark, autoAllocateChunkSize); + } + function SetUpReadableStreamBYOBRequest(request, controller, view) { + request._associatedReadableByteStreamController = controller; + request._view = view; + } + // Helper functions for the ReadableStreamBYOBRequest. + function byobRequestBrandCheckException(name) { + return new TypeError(`ReadableStreamBYOBRequest.prototype.${name} can only be used on a ReadableStreamBYOBRequest`); + } + // Helper functions for the ReadableByteStreamController. + function byteStreamControllerBrandCheckException(name) { + return new TypeError(`ReadableByteStreamController.prototype.${name} can only be used on a ReadableByteStreamController`); + } + + // Abstract operations for the ReadableStream. + function AcquireReadableStreamBYOBReader(stream) { + return new ReadableStreamBYOBReader(stream); + } + // ReadableStream API exposed for controllers. + function ReadableStreamAddReadIntoRequest(stream, readIntoRequest) { + stream._reader._readIntoRequests.push(readIntoRequest); + } + function ReadableStreamFulfillReadIntoRequest(stream, chunk, done) { + const reader = stream._reader; + const readIntoRequest = reader._readIntoRequests.shift(); + if (done) { + readIntoRequest._closeSteps(chunk); + } + else { + readIntoRequest._chunkSteps(chunk); + } + } + function ReadableStreamGetNumReadIntoRequests(stream) { + return stream._reader._readIntoRequests.length; + } + function ReadableStreamHasBYOBReader(stream) { + const reader = stream._reader; + if (reader === undefined) { + return false; + } + if (!IsReadableStreamBYOBReader(reader)) { + return false; + } + return true; + } + /** + * A BYOB reader vended by a {@link ReadableStream}. + * + * @public + */ + class ReadableStreamBYOBReader { + constructor(stream) { + assertRequiredArgument(stream, 1, 'ReadableStreamBYOBReader'); + assertReadableStream(stream, 'First parameter'); + if (IsReadableStreamLocked(stream)) { + throw new TypeError('This stream has already been locked for exclusive reading by another reader'); + } + if (!IsReadableByteStreamController(stream._readableStreamController)) { + throw new TypeError('Cannot construct a ReadableStreamBYOBReader for a stream not constructed with a byte ' + + 'source'); + } + ReadableStreamReaderGenericInitialize(this, stream); + this._readIntoRequests = new SimpleQueue(); + } + /** + * Returns a promise that will be fulfilled when the stream becomes closed, or rejected if the stream ever errors or + * the reader's lock is released before the stream finishes closing. + */ + get closed() { + if (!IsReadableStreamBYOBReader(this)) { + return promiseRejectedWith(byobReaderBrandCheckException('closed')); + } + return this._closedPromise; + } + /** + * If the reader is active, behaves the same as {@link ReadableStream.cancel | stream.cancel(reason)}. + */ + cancel(reason = undefined) { + if (!IsReadableStreamBYOBReader(this)) { + return promiseRejectedWith(byobReaderBrandCheckException('cancel')); + } + if (this._ownerReadableStream === undefined) { + return promiseRejectedWith(readerLockException('cancel')); + } + return ReadableStreamReaderGenericCancel(this, reason); + } + /** + * Attempts to reads bytes into view, and returns a promise resolved with the result. + * + * If reading a chunk causes the queue to become empty, more data will be pulled from the underlying source. + */ + read(view) { + if (!IsReadableStreamBYOBReader(this)) { + return promiseRejectedWith(byobReaderBrandCheckException('read')); + } + if (!ArrayBuffer.isView(view)) { + return promiseRejectedWith(new TypeError('view must be an array buffer view')); + } + if (view.byteLength === 0) { + return promiseRejectedWith(new TypeError('view must have non-zero byteLength')); + } + if (view.buffer.byteLength === 0) { + return promiseRejectedWith(new TypeError(`view's buffer must have non-zero byteLength`)); + } + if (IsDetachedBuffer(view.buffer)) ; + if (this._ownerReadableStream === undefined) { + return promiseRejectedWith(readerLockException('read from')); + } + let resolvePromise; + let rejectPromise; + const promise = newPromise((resolve, reject) => { + resolvePromise = resolve; + rejectPromise = reject; + }); + const readIntoRequest = { + _chunkSteps: chunk => resolvePromise({ value: chunk, done: false }), + _closeSteps: chunk => resolvePromise({ value: chunk, done: true }), + _errorSteps: e => rejectPromise(e) + }; + ReadableStreamBYOBReaderRead(this, view, readIntoRequest); + return promise; + } + /** + * Releases the reader's lock on the corresponding stream. After the lock is released, the reader is no longer active. + * If the associated stream is errored when the lock is released, the reader will appear errored in the same way + * from now on; otherwise, the reader will appear closed. + * + * A reader's lock cannot be released while it still has a pending read request, i.e., if a promise returned by + * the reader's {@link ReadableStreamBYOBReader.read | read()} method has not yet been settled. Attempting to + * do so will throw a `TypeError` and leave the reader locked to the stream. + */ + releaseLock() { + if (!IsReadableStreamBYOBReader(this)) { + throw byobReaderBrandCheckException('releaseLock'); + } + if (this._ownerReadableStream === undefined) { + return; + } + if (this._readIntoRequests.length > 0) { + throw new TypeError('Tried to release a reader lock when that reader has pending read() calls un-settled'); + } + ReadableStreamReaderGenericRelease(this); + } + } + Object.defineProperties(ReadableStreamBYOBReader.prototype, { + cancel: { enumerable: true }, + read: { enumerable: true }, + releaseLock: { enumerable: true }, + closed: { enumerable: true } + }); + if (typeof SymbolPolyfill.toStringTag === 'symbol') { + Object.defineProperty(ReadableStreamBYOBReader.prototype, SymbolPolyfill.toStringTag, { + value: 'ReadableStreamBYOBReader', + configurable: true + }); + } + // Abstract operations for the readers. + function IsReadableStreamBYOBReader(x) { + if (!typeIsObject(x)) { + return false; + } + if (!Object.prototype.hasOwnProperty.call(x, '_readIntoRequests')) { + return false; + } + return x instanceof ReadableStreamBYOBReader; + } + function ReadableStreamBYOBReaderRead(reader, view, readIntoRequest) { + const stream = reader._ownerReadableStream; + stream._disturbed = true; + if (stream._state === 'errored') { + readIntoRequest._errorSteps(stream._storedError); + } + else { + ReadableByteStreamControllerPullInto(stream._readableStreamController, view, readIntoRequest); + } + } + // Helper functions for the ReadableStreamBYOBReader. + function byobReaderBrandCheckException(name) { + return new TypeError(`ReadableStreamBYOBReader.prototype.${name} can only be used on a ReadableStreamBYOBReader`); + } + + function ExtractHighWaterMark(strategy, defaultHWM) { + const { highWaterMark } = strategy; + if (highWaterMark === undefined) { + return defaultHWM; + } + if (NumberIsNaN(highWaterMark) || highWaterMark < 0) { + throw new RangeError('Invalid highWaterMark'); + } + return highWaterMark; + } + function ExtractSizeAlgorithm(strategy) { + const { size } = strategy; + if (!size) { + return () => 1; + } + return size; + } + + function convertQueuingStrategy(init, context) { + assertDictionary(init, context); + const highWaterMark = init === null || init === void 0 ? void 0 : init.highWaterMark; + const size = init === null || init === void 0 ? void 0 : init.size; + return { + highWaterMark: highWaterMark === undefined ? undefined : convertUnrestrictedDouble(highWaterMark), + size: size === undefined ? undefined : convertQueuingStrategySize(size, `${context} has member 'size' that`) + }; + } + function convertQueuingStrategySize(fn, context) { + assertFunction(fn, context); + return chunk => convertUnrestrictedDouble(fn(chunk)); + } + + function convertUnderlyingSink(original, context) { + assertDictionary(original, context); + const abort = original === null || original === void 0 ? void 0 : original.abort; + const close = original === null || original === void 0 ? void 0 : original.close; + const start = original === null || original === void 0 ? void 0 : original.start; + const type = original === null || original === void 0 ? void 0 : original.type; + const write = original === null || original === void 0 ? void 0 : original.write; + return { + abort: abort === undefined ? + undefined : + convertUnderlyingSinkAbortCallback(abort, original, `${context} has member 'abort' that`), + close: close === undefined ? + undefined : + convertUnderlyingSinkCloseCallback(close, original, `${context} has member 'close' that`), + start: start === undefined ? + undefined : + convertUnderlyingSinkStartCallback(start, original, `${context} has member 'start' that`), + write: write === undefined ? + undefined : + convertUnderlyingSinkWriteCallback(write, original, `${context} has member 'write' that`), + type + }; + } + function convertUnderlyingSinkAbortCallback(fn, original, context) { + assertFunction(fn, context); + return (reason) => promiseCall(fn, original, [reason]); + } + function convertUnderlyingSinkCloseCallback(fn, original, context) { + assertFunction(fn, context); + return () => promiseCall(fn, original, []); + } + function convertUnderlyingSinkStartCallback(fn, original, context) { + assertFunction(fn, context); + return (controller) => reflectCall(fn, original, [controller]); + } + function convertUnderlyingSinkWriteCallback(fn, original, context) { + assertFunction(fn, context); + return (chunk, controller) => promiseCall(fn, original, [chunk, controller]); + } + + function assertWritableStream(x, context) { + if (!IsWritableStream(x)) { + throw new TypeError(`${context} is not a WritableStream.`); + } + } + + function isAbortSignal(value) { + if (typeof value !== 'object' || value === null) { + return false; + } + try { + return typeof value.aborted === 'boolean'; + } + catch (_a) { + // AbortSignal.prototype.aborted throws if its brand check fails + return false; + } + } + const supportsAbortController = typeof AbortController === 'function'; + /** + * Construct a new AbortController, if supported by the platform. + * + * @internal + */ + function createAbortController() { + if (supportsAbortController) { + return new AbortController(); + } + return undefined; + } + + /** + * A writable stream represents a destination for data, into which you can write. + * + * @public + */ + class WritableStream { + constructor(rawUnderlyingSink = {}, rawStrategy = {}) { + if (rawUnderlyingSink === undefined) { + rawUnderlyingSink = null; + } + else { + assertObject(rawUnderlyingSink, 'First parameter'); + } + const strategy = convertQueuingStrategy(rawStrategy, 'Second parameter'); + const underlyingSink = convertUnderlyingSink(rawUnderlyingSink, 'First parameter'); + InitializeWritableStream(this); + const type = underlyingSink.type; + if (type !== undefined) { + throw new RangeError('Invalid type is specified'); + } + const sizeAlgorithm = ExtractSizeAlgorithm(strategy); + const highWaterMark = ExtractHighWaterMark(strategy, 1); + SetUpWritableStreamDefaultControllerFromUnderlyingSink(this, underlyingSink, highWaterMark, sizeAlgorithm); + } + /** + * Returns whether or not the writable stream is locked to a writer. + */ + get locked() { + if (!IsWritableStream(this)) { + throw streamBrandCheckException$2('locked'); + } + return IsWritableStreamLocked(this); + } + /** + * Aborts the stream, signaling that the producer can no longer successfully write to the stream and it is to be + * immediately moved to an errored state, with any queued-up writes discarded. This will also execute any abort + * mechanism of the underlying sink. + * + * The returned promise will fulfill if the stream shuts down successfully, or reject if the underlying sink signaled + * that there was an error doing so. Additionally, it will reject with a `TypeError` (without attempting to cancel + * the stream) if the stream is currently locked. + */ + abort(reason = undefined) { + if (!IsWritableStream(this)) { + return promiseRejectedWith(streamBrandCheckException$2('abort')); + } + if (IsWritableStreamLocked(this)) { + return promiseRejectedWith(new TypeError('Cannot abort a stream that already has a writer')); + } + return WritableStreamAbort(this, reason); + } + /** + * Closes the stream. The underlying sink will finish processing any previously-written chunks, before invoking its + * close behavior. During this time any further attempts to write will fail (without erroring the stream). + * + * The method returns a promise that will fulfill if all remaining chunks are successfully written and the stream + * successfully closes, or rejects if an error is encountered during this process. Additionally, it will reject with + * a `TypeError` (without attempting to cancel the stream) if the stream is currently locked. + */ + close() { + if (!IsWritableStream(this)) { + return promiseRejectedWith(streamBrandCheckException$2('close')); + } + if (IsWritableStreamLocked(this)) { + return promiseRejectedWith(new TypeError('Cannot close a stream that already has a writer')); + } + if (WritableStreamCloseQueuedOrInFlight(this)) { + return promiseRejectedWith(new TypeError('Cannot close an already-closing stream')); + } + return WritableStreamClose(this); + } + /** + * Creates a {@link WritableStreamDefaultWriter | writer} and locks the stream to the new writer. While the stream + * is locked, no other writer can be acquired until this one is released. + * + * This functionality is especially useful for creating abstractions that desire the ability to write to a stream + * without interruption or interleaving. By getting a writer for the stream, you can ensure nobody else can write at + * the same time, which would cause the resulting written data to be unpredictable and probably useless. + */ + getWriter() { + if (!IsWritableStream(this)) { + throw streamBrandCheckException$2('getWriter'); + } + return AcquireWritableStreamDefaultWriter(this); + } + } + Object.defineProperties(WritableStream.prototype, { + abort: { enumerable: true }, + close: { enumerable: true }, + getWriter: { enumerable: true }, + locked: { enumerable: true } + }); + if (typeof SymbolPolyfill.toStringTag === 'symbol') { + Object.defineProperty(WritableStream.prototype, SymbolPolyfill.toStringTag, { + value: 'WritableStream', + configurable: true + }); + } + // Abstract operations for the WritableStream. + function AcquireWritableStreamDefaultWriter(stream) { + return new WritableStreamDefaultWriter(stream); + } + // Throws if and only if startAlgorithm throws. + function CreateWritableStream(startAlgorithm, writeAlgorithm, closeAlgorithm, abortAlgorithm, highWaterMark = 1, sizeAlgorithm = () => 1) { + const stream = Object.create(WritableStream.prototype); + InitializeWritableStream(stream); + const controller = Object.create(WritableStreamDefaultController.prototype); + SetUpWritableStreamDefaultController(stream, controller, startAlgorithm, writeAlgorithm, closeAlgorithm, abortAlgorithm, highWaterMark, sizeAlgorithm); + return stream; + } + function InitializeWritableStream(stream) { + stream._state = 'writable'; + // The error that will be reported by new method calls once the state becomes errored. Only set when [[state]] is + // 'erroring' or 'errored'. May be set to an undefined value. + stream._storedError = undefined; + stream._writer = undefined; + // Initialize to undefined first because the constructor of the controller checks this + // variable to validate the caller. + stream._writableStreamController = undefined; + // This queue is placed here instead of the writer class in order to allow for passing a writer to the next data + // producer without waiting for the queued writes to finish. + stream._writeRequests = new SimpleQueue(); + // Write requests are removed from _writeRequests when write() is called on the underlying sink. This prevents + // them from being erroneously rejected on error. If a write() call is in-flight, the request is stored here. + stream._inFlightWriteRequest = undefined; + // The promise that was returned from writer.close(). Stored here because it may be fulfilled after the writer + // has been detached. + stream._closeRequest = undefined; + // Close request is removed from _closeRequest when close() is called on the underlying sink. This prevents it + // from being erroneously rejected on error. If a close() call is in-flight, the request is stored here. + stream._inFlightCloseRequest = undefined; + // The promise that was returned from writer.abort(). This may also be fulfilled after the writer has detached. + stream._pendingAbortRequest = undefined; + // The backpressure signal set by the controller. + stream._backpressure = false; + } + function IsWritableStream(x) { + if (!typeIsObject(x)) { + return false; + } + if (!Object.prototype.hasOwnProperty.call(x, '_writableStreamController')) { + return false; + } + return x instanceof WritableStream; + } + function IsWritableStreamLocked(stream) { + if (stream._writer === undefined) { + return false; + } + return true; + } + function WritableStreamAbort(stream, reason) { + var _a; + if (stream._state === 'closed' || stream._state === 'errored') { + return promiseResolvedWith(undefined); + } + stream._writableStreamController._abortReason = reason; + (_a = stream._writableStreamController._abortController) === null || _a === void 0 ? void 0 : _a.abort(); + // TypeScript narrows the type of `stream._state` down to 'writable' | 'erroring', + // but it doesn't know that signaling abort runs author code that might have changed the state. + // Widen the type again by casting to WritableStreamState. + const state = stream._state; + if (state === 'closed' || state === 'errored') { + return promiseResolvedWith(undefined); + } + if (stream._pendingAbortRequest !== undefined) { + return stream._pendingAbortRequest._promise; + } + let wasAlreadyErroring = false; + if (state === 'erroring') { + wasAlreadyErroring = true; + // reason will not be used, so don't keep a reference to it. + reason = undefined; + } + const promise = newPromise((resolve, reject) => { + stream._pendingAbortRequest = { + _promise: undefined, + _resolve: resolve, + _reject: reject, + _reason: reason, + _wasAlreadyErroring: wasAlreadyErroring + }; + }); + stream._pendingAbortRequest._promise = promise; + if (!wasAlreadyErroring) { + WritableStreamStartErroring(stream, reason); + } + return promise; + } + function WritableStreamClose(stream) { + const state = stream._state; + if (state === 'closed' || state === 'errored') { + return promiseRejectedWith(new TypeError(`The stream (in ${state} state) is not in the writable state and cannot be closed`)); + } + const promise = newPromise((resolve, reject) => { + const closeRequest = { + _resolve: resolve, + _reject: reject + }; + stream._closeRequest = closeRequest; + }); + const writer = stream._writer; + if (writer !== undefined && stream._backpressure && state === 'writable') { + defaultWriterReadyPromiseResolve(writer); + } + WritableStreamDefaultControllerClose(stream._writableStreamController); + return promise; + } + // WritableStream API exposed for controllers. + function WritableStreamAddWriteRequest(stream) { + const promise = newPromise((resolve, reject) => { + const writeRequest = { + _resolve: resolve, + _reject: reject + }; + stream._writeRequests.push(writeRequest); + }); + return promise; + } + function WritableStreamDealWithRejection(stream, error) { + const state = stream._state; + if (state === 'writable') { + WritableStreamStartErroring(stream, error); + return; + } + WritableStreamFinishErroring(stream); + } + function WritableStreamStartErroring(stream, reason) { + const controller = stream._writableStreamController; + stream._state = 'erroring'; + stream._storedError = reason; + const writer = stream._writer; + if (writer !== undefined) { + WritableStreamDefaultWriterEnsureReadyPromiseRejected(writer, reason); + } + if (!WritableStreamHasOperationMarkedInFlight(stream) && controller._started) { + WritableStreamFinishErroring(stream); + } + } + function WritableStreamFinishErroring(stream) { + stream._state = 'errored'; + stream._writableStreamController[ErrorSteps](); + const storedError = stream._storedError; + stream._writeRequests.forEach(writeRequest => { + writeRequest._reject(storedError); + }); + stream._writeRequests = new SimpleQueue(); + if (stream._pendingAbortRequest === undefined) { + WritableStreamRejectCloseAndClosedPromiseIfNeeded(stream); + return; + } + const abortRequest = stream._pendingAbortRequest; + stream._pendingAbortRequest = undefined; + if (abortRequest._wasAlreadyErroring) { + abortRequest._reject(storedError); + WritableStreamRejectCloseAndClosedPromiseIfNeeded(stream); + return; + } + const promise = stream._writableStreamController[AbortSteps](abortRequest._reason); + uponPromise(promise, () => { + abortRequest._resolve(); + WritableStreamRejectCloseAndClosedPromiseIfNeeded(stream); + }, (reason) => { + abortRequest._reject(reason); + WritableStreamRejectCloseAndClosedPromiseIfNeeded(stream); + }); + } + function WritableStreamFinishInFlightWrite(stream) { + stream._inFlightWriteRequest._resolve(undefined); + stream._inFlightWriteRequest = undefined; + } + function WritableStreamFinishInFlightWriteWithError(stream, error) { + stream._inFlightWriteRequest._reject(error); + stream._inFlightWriteRequest = undefined; + WritableStreamDealWithRejection(stream, error); + } + function WritableStreamFinishInFlightClose(stream) { + stream._inFlightCloseRequest._resolve(undefined); + stream._inFlightCloseRequest = undefined; + const state = stream._state; + if (state === 'erroring') { + // The error was too late to do anything, so it is ignored. + stream._storedError = undefined; + if (stream._pendingAbortRequest !== undefined) { + stream._pendingAbortRequest._resolve(); + stream._pendingAbortRequest = undefined; + } + } + stream._state = 'closed'; + const writer = stream._writer; + if (writer !== undefined) { + defaultWriterClosedPromiseResolve(writer); + } + } + function WritableStreamFinishInFlightCloseWithError(stream, error) { + stream._inFlightCloseRequest._reject(error); + stream._inFlightCloseRequest = undefined; + // Never execute sink abort() after sink close(). + if (stream._pendingAbortRequest !== undefined) { + stream._pendingAbortRequest._reject(error); + stream._pendingAbortRequest = undefined; + } + WritableStreamDealWithRejection(stream, error); + } + // TODO(ricea): Fix alphabetical order. + function WritableStreamCloseQueuedOrInFlight(stream) { + if (stream._closeRequest === undefined && stream._inFlightCloseRequest === undefined) { + return false; + } + return true; + } + function WritableStreamHasOperationMarkedInFlight(stream) { + if (stream._inFlightWriteRequest === undefined && stream._inFlightCloseRequest === undefined) { + return false; + } + return true; + } + function WritableStreamMarkCloseRequestInFlight(stream) { + stream._inFlightCloseRequest = stream._closeRequest; + stream._closeRequest = undefined; + } + function WritableStreamMarkFirstWriteRequestInFlight(stream) { + stream._inFlightWriteRequest = stream._writeRequests.shift(); + } + function WritableStreamRejectCloseAndClosedPromiseIfNeeded(stream) { + if (stream._closeRequest !== undefined) { + stream._closeRequest._reject(stream._storedError); + stream._closeRequest = undefined; + } + const writer = stream._writer; + if (writer !== undefined) { + defaultWriterClosedPromiseReject(writer, stream._storedError); + } + } + function WritableStreamUpdateBackpressure(stream, backpressure) { + const writer = stream._writer; + if (writer !== undefined && backpressure !== stream._backpressure) { + if (backpressure) { + defaultWriterReadyPromiseReset(writer); + } + else { + defaultWriterReadyPromiseResolve(writer); + } + } + stream._backpressure = backpressure; + } + /** + * A default writer vended by a {@link WritableStream}. + * + * @public + */ + class WritableStreamDefaultWriter { + constructor(stream) { + assertRequiredArgument(stream, 1, 'WritableStreamDefaultWriter'); + assertWritableStream(stream, 'First parameter'); + if (IsWritableStreamLocked(stream)) { + throw new TypeError('This stream has already been locked for exclusive writing by another writer'); + } + this._ownerWritableStream = stream; + stream._writer = this; + const state = stream._state; + if (state === 'writable') { + if (!WritableStreamCloseQueuedOrInFlight(stream) && stream._backpressure) { + defaultWriterReadyPromiseInitialize(this); + } + else { + defaultWriterReadyPromiseInitializeAsResolved(this); + } + defaultWriterClosedPromiseInitialize(this); + } + else if (state === 'erroring') { + defaultWriterReadyPromiseInitializeAsRejected(this, stream._storedError); + defaultWriterClosedPromiseInitialize(this); + } + else if (state === 'closed') { + defaultWriterReadyPromiseInitializeAsResolved(this); + defaultWriterClosedPromiseInitializeAsResolved(this); + } + else { + const storedError = stream._storedError; + defaultWriterReadyPromiseInitializeAsRejected(this, storedError); + defaultWriterClosedPromiseInitializeAsRejected(this, storedError); + } + } + /** + * Returns a promise that will be fulfilled when the stream becomes closed, or rejected if the stream ever errors or + * the writer’s lock is released before the stream finishes closing. + */ + get closed() { + if (!IsWritableStreamDefaultWriter(this)) { + return promiseRejectedWith(defaultWriterBrandCheckException('closed')); + } + return this._closedPromise; + } + /** + * Returns the desired size to fill the stream’s internal queue. It can be negative, if the queue is over-full. + * A producer can use this information to determine the right amount of data to write. + * + * It will be `null` if the stream cannot be successfully written to (due to either being errored, or having an abort + * queued up). It will return zero if the stream is closed. And the getter will throw an exception if invoked when + * the writer’s lock is released. + */ + get desiredSize() { + if (!IsWritableStreamDefaultWriter(this)) { + throw defaultWriterBrandCheckException('desiredSize'); + } + if (this._ownerWritableStream === undefined) { + throw defaultWriterLockException('desiredSize'); + } + return WritableStreamDefaultWriterGetDesiredSize(this); + } + /** + * Returns a promise that will be fulfilled when the desired size to fill the stream’s internal queue transitions + * from non-positive to positive, signaling that it is no longer applying backpressure. Once the desired size dips + * back to zero or below, the getter will return a new promise that stays pending until the next transition. + * + * If the stream becomes errored or aborted, or the writer’s lock is released, the returned promise will become + * rejected. + */ + get ready() { + if (!IsWritableStreamDefaultWriter(this)) { + return promiseRejectedWith(defaultWriterBrandCheckException('ready')); + } + return this._readyPromise; + } + /** + * If the reader is active, behaves the same as {@link WritableStream.abort | stream.abort(reason)}. + */ + abort(reason = undefined) { + if (!IsWritableStreamDefaultWriter(this)) { + return promiseRejectedWith(defaultWriterBrandCheckException('abort')); + } + if (this._ownerWritableStream === undefined) { + return promiseRejectedWith(defaultWriterLockException('abort')); + } + return WritableStreamDefaultWriterAbort(this, reason); + } + /** + * If the reader is active, behaves the same as {@link WritableStream.close | stream.close()}. + */ + close() { + if (!IsWritableStreamDefaultWriter(this)) { + return promiseRejectedWith(defaultWriterBrandCheckException('close')); + } + const stream = this._ownerWritableStream; + if (stream === undefined) { + return promiseRejectedWith(defaultWriterLockException('close')); + } + if (WritableStreamCloseQueuedOrInFlight(stream)) { + return promiseRejectedWith(new TypeError('Cannot close an already-closing stream')); + } + return WritableStreamDefaultWriterClose(this); + } + /** + * Releases the writer’s lock on the corresponding stream. After the lock is released, the writer is no longer active. + * If the associated stream is errored when the lock is released, the writer will appear errored in the same way from + * now on; otherwise, the writer will appear closed. + * + * Note that the lock can still be released even if some ongoing writes have not yet finished (i.e. even if the + * promises returned from previous calls to {@link WritableStreamDefaultWriter.write | write()} have not yet settled). + * It’s not necessary to hold the lock on the writer for the duration of the write; the lock instead simply prevents + * other producers from writing in an interleaved manner. + */ + releaseLock() { + if (!IsWritableStreamDefaultWriter(this)) { + throw defaultWriterBrandCheckException('releaseLock'); + } + const stream = this._ownerWritableStream; + if (stream === undefined) { + return; + } + WritableStreamDefaultWriterRelease(this); + } + write(chunk = undefined) { + if (!IsWritableStreamDefaultWriter(this)) { + return promiseRejectedWith(defaultWriterBrandCheckException('write')); + } + if (this._ownerWritableStream === undefined) { + return promiseRejectedWith(defaultWriterLockException('write to')); + } + return WritableStreamDefaultWriterWrite(this, chunk); + } + } + Object.defineProperties(WritableStreamDefaultWriter.prototype, { + abort: { enumerable: true }, + close: { enumerable: true }, + releaseLock: { enumerable: true }, + write: { enumerable: true }, + closed: { enumerable: true }, + desiredSize: { enumerable: true }, + ready: { enumerable: true } + }); + if (typeof SymbolPolyfill.toStringTag === 'symbol') { + Object.defineProperty(WritableStreamDefaultWriter.prototype, SymbolPolyfill.toStringTag, { + value: 'WritableStreamDefaultWriter', + configurable: true + }); + } + // Abstract operations for the WritableStreamDefaultWriter. + function IsWritableStreamDefaultWriter(x) { + if (!typeIsObject(x)) { + return false; + } + if (!Object.prototype.hasOwnProperty.call(x, '_ownerWritableStream')) { + return false; + } + return x instanceof WritableStreamDefaultWriter; + } + // A client of WritableStreamDefaultWriter may use these functions directly to bypass state check. + function WritableStreamDefaultWriterAbort(writer, reason) { + const stream = writer._ownerWritableStream; + return WritableStreamAbort(stream, reason); + } + function WritableStreamDefaultWriterClose(writer) { + const stream = writer._ownerWritableStream; + return WritableStreamClose(stream); + } + function WritableStreamDefaultWriterCloseWithErrorPropagation(writer) { + const stream = writer._ownerWritableStream; + const state = stream._state; + if (WritableStreamCloseQueuedOrInFlight(stream) || state === 'closed') { + return promiseResolvedWith(undefined); + } + if (state === 'errored') { + return promiseRejectedWith(stream._storedError); + } + return WritableStreamDefaultWriterClose(writer); + } + function WritableStreamDefaultWriterEnsureClosedPromiseRejected(writer, error) { + if (writer._closedPromiseState === 'pending') { + defaultWriterClosedPromiseReject(writer, error); + } + else { + defaultWriterClosedPromiseResetToRejected(writer, error); + } + } + function WritableStreamDefaultWriterEnsureReadyPromiseRejected(writer, error) { + if (writer._readyPromiseState === 'pending') { + defaultWriterReadyPromiseReject(writer, error); + } + else { + defaultWriterReadyPromiseResetToRejected(writer, error); + } + } + function WritableStreamDefaultWriterGetDesiredSize(writer) { + const stream = writer._ownerWritableStream; + const state = stream._state; + if (state === 'errored' || state === 'erroring') { + return null; + } + if (state === 'closed') { + return 0; + } + return WritableStreamDefaultControllerGetDesiredSize(stream._writableStreamController); + } + function WritableStreamDefaultWriterRelease(writer) { + const stream = writer._ownerWritableStream; + const releasedError = new TypeError(`Writer was released and can no longer be used to monitor the stream's closedness`); + WritableStreamDefaultWriterEnsureReadyPromiseRejected(writer, releasedError); + // The state transitions to "errored" before the sink abort() method runs, but the writer.closed promise is not + // rejected until afterwards. This means that simply testing state will not work. + WritableStreamDefaultWriterEnsureClosedPromiseRejected(writer, releasedError); + stream._writer = undefined; + writer._ownerWritableStream = undefined; + } + function WritableStreamDefaultWriterWrite(writer, chunk) { + const stream = writer._ownerWritableStream; + const controller = stream._writableStreamController; + const chunkSize = WritableStreamDefaultControllerGetChunkSize(controller, chunk); + if (stream !== writer._ownerWritableStream) { + return promiseRejectedWith(defaultWriterLockException('write to')); + } + const state = stream._state; + if (state === 'errored') { + return promiseRejectedWith(stream._storedError); + } + if (WritableStreamCloseQueuedOrInFlight(stream) || state === 'closed') { + return promiseRejectedWith(new TypeError('The stream is closing or closed and cannot be written to')); + } + if (state === 'erroring') { + return promiseRejectedWith(stream._storedError); + } + const promise = WritableStreamAddWriteRequest(stream); + WritableStreamDefaultControllerWrite(controller, chunk, chunkSize); + return promise; + } + const closeSentinel = {}; + /** + * Allows control of a {@link WritableStream | writable stream}'s state and internal queue. + * + * @public + */ + class WritableStreamDefaultController { + constructor() { + throw new TypeError('Illegal constructor'); + } + /** + * The reason which was passed to `WritableStream.abort(reason)` when the stream was aborted. + * + * @deprecated + * This property has been removed from the specification, see https://github.com/whatwg/streams/pull/1177. + * Use {@link WritableStreamDefaultController.signal}'s `reason` instead. + */ + get abortReason() { + if (!IsWritableStreamDefaultController(this)) { + throw defaultControllerBrandCheckException$2('abortReason'); + } + return this._abortReason; + } + /** + * An `AbortSignal` that can be used to abort the pending write or close operation when the stream is aborted. + */ + get signal() { + if (!IsWritableStreamDefaultController(this)) { + throw defaultControllerBrandCheckException$2('signal'); + } + if (this._abortController === undefined) { + // Older browsers or older Node versions may not support `AbortController` or `AbortSignal`. + // We don't want to bundle and ship an `AbortController` polyfill together with our polyfill, + // so instead we only implement support for `signal` if we find a global `AbortController` constructor. + throw new TypeError('WritableStreamDefaultController.prototype.signal is not supported'); + } + return this._abortController.signal; + } + /** + * Closes the controlled writable stream, making all future interactions with it fail with the given error `e`. + * + * This method is rarely used, since usually it suffices to return a rejected promise from one of the underlying + * sink's methods. However, it can be useful for suddenly shutting down a stream in response to an event outside the + * normal lifecycle of interactions with the underlying sink. + */ + error(e = undefined) { + if (!IsWritableStreamDefaultController(this)) { + throw defaultControllerBrandCheckException$2('error'); + } + const state = this._controlledWritableStream._state; + if (state !== 'writable') { + // The stream is closed, errored or will be soon. The sink can't do anything useful if it gets an error here, so + // just treat it as a no-op. + return; + } + WritableStreamDefaultControllerError(this, e); + } + /** @internal */ + [AbortSteps](reason) { + const result = this._abortAlgorithm(reason); + WritableStreamDefaultControllerClearAlgorithms(this); + return result; + } + /** @internal */ + [ErrorSteps]() { + ResetQueue(this); + } + } + Object.defineProperties(WritableStreamDefaultController.prototype, { + abortReason: { enumerable: true }, + signal: { enumerable: true }, + error: { enumerable: true } + }); + if (typeof SymbolPolyfill.toStringTag === 'symbol') { + Object.defineProperty(WritableStreamDefaultController.prototype, SymbolPolyfill.toStringTag, { + value: 'WritableStreamDefaultController', + configurable: true + }); + } + // Abstract operations implementing interface required by the WritableStream. + function IsWritableStreamDefaultController(x) { + if (!typeIsObject(x)) { + return false; + } + if (!Object.prototype.hasOwnProperty.call(x, '_controlledWritableStream')) { + return false; + } + return x instanceof WritableStreamDefaultController; + } + function SetUpWritableStreamDefaultController(stream, controller, startAlgorithm, writeAlgorithm, closeAlgorithm, abortAlgorithm, highWaterMark, sizeAlgorithm) { + controller._controlledWritableStream = stream; + stream._writableStreamController = controller; + // Need to set the slots so that the assert doesn't fire. In the spec the slots already exist implicitly. + controller._queue = undefined; + controller._queueTotalSize = undefined; + ResetQueue(controller); + controller._abortReason = undefined; + controller._abortController = createAbortController(); + controller._started = false; + controller._strategySizeAlgorithm = sizeAlgorithm; + controller._strategyHWM = highWaterMark; + controller._writeAlgorithm = writeAlgorithm; + controller._closeAlgorithm = closeAlgorithm; + controller._abortAlgorithm = abortAlgorithm; + const backpressure = WritableStreamDefaultControllerGetBackpressure(controller); + WritableStreamUpdateBackpressure(stream, backpressure); + const startResult = startAlgorithm(); + const startPromise = promiseResolvedWith(startResult); + uponPromise(startPromise, () => { + controller._started = true; + WritableStreamDefaultControllerAdvanceQueueIfNeeded(controller); + }, r => { + controller._started = true; + WritableStreamDealWithRejection(stream, r); + }); + } + function SetUpWritableStreamDefaultControllerFromUnderlyingSink(stream, underlyingSink, highWaterMark, sizeAlgorithm) { + const controller = Object.create(WritableStreamDefaultController.prototype); + let startAlgorithm = () => undefined; + let writeAlgorithm = () => promiseResolvedWith(undefined); + let closeAlgorithm = () => promiseResolvedWith(undefined); + let abortAlgorithm = () => promiseResolvedWith(undefined); + if (underlyingSink.start !== undefined) { + startAlgorithm = () => underlyingSink.start(controller); + } + if (underlyingSink.write !== undefined) { + writeAlgorithm = chunk => underlyingSink.write(chunk, controller); + } + if (underlyingSink.close !== undefined) { + closeAlgorithm = () => underlyingSink.close(); + } + if (underlyingSink.abort !== undefined) { + abortAlgorithm = reason => underlyingSink.abort(reason); + } + SetUpWritableStreamDefaultController(stream, controller, startAlgorithm, writeAlgorithm, closeAlgorithm, abortAlgorithm, highWaterMark, sizeAlgorithm); + } + // ClearAlgorithms may be called twice. Erroring the same stream in multiple ways will often result in redundant calls. + function WritableStreamDefaultControllerClearAlgorithms(controller) { + controller._writeAlgorithm = undefined; + controller._closeAlgorithm = undefined; + controller._abortAlgorithm = undefined; + controller._strategySizeAlgorithm = undefined; + } + function WritableStreamDefaultControllerClose(controller) { + EnqueueValueWithSize(controller, closeSentinel, 0); + WritableStreamDefaultControllerAdvanceQueueIfNeeded(controller); + } + function WritableStreamDefaultControllerGetChunkSize(controller, chunk) { + try { + return controller._strategySizeAlgorithm(chunk); + } + catch (chunkSizeE) { + WritableStreamDefaultControllerErrorIfNeeded(controller, chunkSizeE); + return 1; + } + } + function WritableStreamDefaultControllerGetDesiredSize(controller) { + return controller._strategyHWM - controller._queueTotalSize; + } + function WritableStreamDefaultControllerWrite(controller, chunk, chunkSize) { + try { + EnqueueValueWithSize(controller, chunk, chunkSize); + } + catch (enqueueE) { + WritableStreamDefaultControllerErrorIfNeeded(controller, enqueueE); + return; + } + const stream = controller._controlledWritableStream; + if (!WritableStreamCloseQueuedOrInFlight(stream) && stream._state === 'writable') { + const backpressure = WritableStreamDefaultControllerGetBackpressure(controller); + WritableStreamUpdateBackpressure(stream, backpressure); + } + WritableStreamDefaultControllerAdvanceQueueIfNeeded(controller); + } + // Abstract operations for the WritableStreamDefaultController. + function WritableStreamDefaultControllerAdvanceQueueIfNeeded(controller) { + const stream = controller._controlledWritableStream; + if (!controller._started) { + return; + } + if (stream._inFlightWriteRequest !== undefined) { + return; + } + const state = stream._state; + if (state === 'erroring') { + WritableStreamFinishErroring(stream); + return; + } + if (controller._queue.length === 0) { + return; + } + const value = PeekQueueValue(controller); + if (value === closeSentinel) { + WritableStreamDefaultControllerProcessClose(controller); + } + else { + WritableStreamDefaultControllerProcessWrite(controller, value); + } + } + function WritableStreamDefaultControllerErrorIfNeeded(controller, error) { + if (controller._controlledWritableStream._state === 'writable') { + WritableStreamDefaultControllerError(controller, error); + } + } + function WritableStreamDefaultControllerProcessClose(controller) { + const stream = controller._controlledWritableStream; + WritableStreamMarkCloseRequestInFlight(stream); + DequeueValue(controller); + const sinkClosePromise = controller._closeAlgorithm(); + WritableStreamDefaultControllerClearAlgorithms(controller); + uponPromise(sinkClosePromise, () => { + WritableStreamFinishInFlightClose(stream); + }, reason => { + WritableStreamFinishInFlightCloseWithError(stream, reason); + }); + } + function WritableStreamDefaultControllerProcessWrite(controller, chunk) { + const stream = controller._controlledWritableStream; + WritableStreamMarkFirstWriteRequestInFlight(stream); + const sinkWritePromise = controller._writeAlgorithm(chunk); + uponPromise(sinkWritePromise, () => { + WritableStreamFinishInFlightWrite(stream); + const state = stream._state; + DequeueValue(controller); + if (!WritableStreamCloseQueuedOrInFlight(stream) && state === 'writable') { + const backpressure = WritableStreamDefaultControllerGetBackpressure(controller); + WritableStreamUpdateBackpressure(stream, backpressure); + } + WritableStreamDefaultControllerAdvanceQueueIfNeeded(controller); + }, reason => { + if (stream._state === 'writable') { + WritableStreamDefaultControllerClearAlgorithms(controller); + } + WritableStreamFinishInFlightWriteWithError(stream, reason); + }); + } + function WritableStreamDefaultControllerGetBackpressure(controller) { + const desiredSize = WritableStreamDefaultControllerGetDesiredSize(controller); + return desiredSize <= 0; + } + // A client of WritableStreamDefaultController may use these functions directly to bypass state check. + function WritableStreamDefaultControllerError(controller, error) { + const stream = controller._controlledWritableStream; + WritableStreamDefaultControllerClearAlgorithms(controller); + WritableStreamStartErroring(stream, error); + } + // Helper functions for the WritableStream. + function streamBrandCheckException$2(name) { + return new TypeError(`WritableStream.prototype.${name} can only be used on a WritableStream`); + } + // Helper functions for the WritableStreamDefaultController. + function defaultControllerBrandCheckException$2(name) { + return new TypeError(`WritableStreamDefaultController.prototype.${name} can only be used on a WritableStreamDefaultController`); + } + // Helper functions for the WritableStreamDefaultWriter. + function defaultWriterBrandCheckException(name) { + return new TypeError(`WritableStreamDefaultWriter.prototype.${name} can only be used on a WritableStreamDefaultWriter`); + } + function defaultWriterLockException(name) { + return new TypeError('Cannot ' + name + ' a stream using a released writer'); + } + function defaultWriterClosedPromiseInitialize(writer) { + writer._closedPromise = newPromise((resolve, reject) => { + writer._closedPromise_resolve = resolve; + writer._closedPromise_reject = reject; + writer._closedPromiseState = 'pending'; + }); + } + function defaultWriterClosedPromiseInitializeAsRejected(writer, reason) { + defaultWriterClosedPromiseInitialize(writer); + defaultWriterClosedPromiseReject(writer, reason); + } + function defaultWriterClosedPromiseInitializeAsResolved(writer) { + defaultWriterClosedPromiseInitialize(writer); + defaultWriterClosedPromiseResolve(writer); + } + function defaultWriterClosedPromiseReject(writer, reason) { + if (writer._closedPromise_reject === undefined) { + return; + } + setPromiseIsHandledToTrue(writer._closedPromise); + writer._closedPromise_reject(reason); + writer._closedPromise_resolve = undefined; + writer._closedPromise_reject = undefined; + writer._closedPromiseState = 'rejected'; + } + function defaultWriterClosedPromiseResetToRejected(writer, reason) { + defaultWriterClosedPromiseInitializeAsRejected(writer, reason); + } + function defaultWriterClosedPromiseResolve(writer) { + if (writer._closedPromise_resolve === undefined) { + return; + } + writer._closedPromise_resolve(undefined); + writer._closedPromise_resolve = undefined; + writer._closedPromise_reject = undefined; + writer._closedPromiseState = 'resolved'; + } + function defaultWriterReadyPromiseInitialize(writer) { + writer._readyPromise = newPromise((resolve, reject) => { + writer._readyPromise_resolve = resolve; + writer._readyPromise_reject = reject; + }); + writer._readyPromiseState = 'pending'; + } + function defaultWriterReadyPromiseInitializeAsRejected(writer, reason) { + defaultWriterReadyPromiseInitialize(writer); + defaultWriterReadyPromiseReject(writer, reason); + } + function defaultWriterReadyPromiseInitializeAsResolved(writer) { + defaultWriterReadyPromiseInitialize(writer); + defaultWriterReadyPromiseResolve(writer); + } + function defaultWriterReadyPromiseReject(writer, reason) { + if (writer._readyPromise_reject === undefined) { + return; + } + setPromiseIsHandledToTrue(writer._readyPromise); + writer._readyPromise_reject(reason); + writer._readyPromise_resolve = undefined; + writer._readyPromise_reject = undefined; + writer._readyPromiseState = 'rejected'; + } + function defaultWriterReadyPromiseReset(writer) { + defaultWriterReadyPromiseInitialize(writer); + } + function defaultWriterReadyPromiseResetToRejected(writer, reason) { + defaultWriterReadyPromiseInitializeAsRejected(writer, reason); + } + function defaultWriterReadyPromiseResolve(writer) { + if (writer._readyPromise_resolve === undefined) { + return; + } + writer._readyPromise_resolve(undefined); + writer._readyPromise_resolve = undefined; + writer._readyPromise_reject = undefined; + writer._readyPromiseState = 'fulfilled'; + } + + /// + const NativeDOMException = typeof DOMException !== 'undefined' ? DOMException : undefined; + + /// + function isDOMExceptionConstructor(ctor) { + if (!(typeof ctor === 'function' || typeof ctor === 'object')) { + return false; + } + try { + new ctor(); + return true; + } + catch (_a) { + return false; + } + } + function createDOMExceptionPolyfill() { + // eslint-disable-next-line no-shadow + const ctor = function DOMException(message, name) { + this.message = message || ''; + this.name = name || 'Error'; + if (Error.captureStackTrace) { + Error.captureStackTrace(this, this.constructor); + } + }; + ctor.prototype = Object.create(Error.prototype); + Object.defineProperty(ctor.prototype, 'constructor', { value: ctor, writable: true, configurable: true }); + return ctor; + } + // eslint-disable-next-line no-redeclare + const DOMException$1 = isDOMExceptionConstructor(NativeDOMException) ? NativeDOMException : createDOMExceptionPolyfill(); + + function ReadableStreamPipeTo(source, dest, preventClose, preventAbort, preventCancel, signal) { + const reader = AcquireReadableStreamDefaultReader(source); + const writer = AcquireWritableStreamDefaultWriter(dest); + source._disturbed = true; + let shuttingDown = false; + // This is used to keep track of the spec's requirement that we wait for ongoing writes during shutdown. + let currentWrite = promiseResolvedWith(undefined); + return newPromise((resolve, reject) => { + let abortAlgorithm; + if (signal !== undefined) { + abortAlgorithm = () => { + const error = new DOMException$1('Aborted', 'AbortError'); + const actions = []; + if (!preventAbort) { + actions.push(() => { + if (dest._state === 'writable') { + return WritableStreamAbort(dest, error); + } + return promiseResolvedWith(undefined); + }); + } + if (!preventCancel) { + actions.push(() => { + if (source._state === 'readable') { + return ReadableStreamCancel(source, error); + } + return promiseResolvedWith(undefined); + }); + } + shutdownWithAction(() => Promise.all(actions.map(action => action())), true, error); + }; + if (signal.aborted) { + abortAlgorithm(); + return; + } + signal.addEventListener('abort', abortAlgorithm); + } + // Using reader and writer, read all chunks from this and write them to dest + // - Backpressure must be enforced + // - Shutdown must stop all activity + function pipeLoop() { + return newPromise((resolveLoop, rejectLoop) => { + function next(done) { + if (done) { + resolveLoop(); + } + else { + // Use `PerformPromiseThen` instead of `uponPromise` to avoid + // adding unnecessary `.catch(rethrowAssertionErrorRejection)` handlers + PerformPromiseThen(pipeStep(), next, rejectLoop); + } + } + next(false); + }); + } + function pipeStep() { + if (shuttingDown) { + return promiseResolvedWith(true); + } + return PerformPromiseThen(writer._readyPromise, () => { + return newPromise((resolveRead, rejectRead) => { + ReadableStreamDefaultReaderRead(reader, { + _chunkSteps: chunk => { + currentWrite = PerformPromiseThen(WritableStreamDefaultWriterWrite(writer, chunk), undefined, noop); + resolveRead(false); + }, + _closeSteps: () => resolveRead(true), + _errorSteps: rejectRead + }); + }); + }); + } + // Errors must be propagated forward + isOrBecomesErrored(source, reader._closedPromise, storedError => { + if (!preventAbort) { + shutdownWithAction(() => WritableStreamAbort(dest, storedError), true, storedError); + } + else { + shutdown(true, storedError); + } + }); + // Errors must be propagated backward + isOrBecomesErrored(dest, writer._closedPromise, storedError => { + if (!preventCancel) { + shutdownWithAction(() => ReadableStreamCancel(source, storedError), true, storedError); + } + else { + shutdown(true, storedError); + } + }); + // Closing must be propagated forward + isOrBecomesClosed(source, reader._closedPromise, () => { + if (!preventClose) { + shutdownWithAction(() => WritableStreamDefaultWriterCloseWithErrorPropagation(writer)); + } + else { + shutdown(); + } + }); + // Closing must be propagated backward + if (WritableStreamCloseQueuedOrInFlight(dest) || dest._state === 'closed') { + const destClosed = new TypeError('the destination writable stream closed before all data could be piped to it'); + if (!preventCancel) { + shutdownWithAction(() => ReadableStreamCancel(source, destClosed), true, destClosed); + } + else { + shutdown(true, destClosed); + } + } + setPromiseIsHandledToTrue(pipeLoop()); + function waitForWritesToFinish() { + // Another write may have started while we were waiting on this currentWrite, so we have to be sure to wait + // for that too. + const oldCurrentWrite = currentWrite; + return PerformPromiseThen(currentWrite, () => oldCurrentWrite !== currentWrite ? waitForWritesToFinish() : undefined); + } + function isOrBecomesErrored(stream, promise, action) { + if (stream._state === 'errored') { + action(stream._storedError); + } + else { + uponRejection(promise, action); + } + } + function isOrBecomesClosed(stream, promise, action) { + if (stream._state === 'closed') { + action(); + } + else { + uponFulfillment(promise, action); + } + } + function shutdownWithAction(action, originalIsError, originalError) { + if (shuttingDown) { + return; + } + shuttingDown = true; + if (dest._state === 'writable' && !WritableStreamCloseQueuedOrInFlight(dest)) { + uponFulfillment(waitForWritesToFinish(), doTheRest); + } + else { + doTheRest(); + } + function doTheRest() { + uponPromise(action(), () => finalize(originalIsError, originalError), newError => finalize(true, newError)); + } + } + function shutdown(isError, error) { + if (shuttingDown) { + return; + } + shuttingDown = true; + if (dest._state === 'writable' && !WritableStreamCloseQueuedOrInFlight(dest)) { + uponFulfillment(waitForWritesToFinish(), () => finalize(isError, error)); + } + else { + finalize(isError, error); + } + } + function finalize(isError, error) { + WritableStreamDefaultWriterRelease(writer); + ReadableStreamReaderGenericRelease(reader); + if (signal !== undefined) { + signal.removeEventListener('abort', abortAlgorithm); + } + if (isError) { + reject(error); + } + else { + resolve(undefined); + } + } + }); + } + + /** + * Allows control of a {@link ReadableStream | readable stream}'s state and internal queue. + * + * @public + */ + class ReadableStreamDefaultController { + constructor() { + throw new TypeError('Illegal constructor'); + } + /** + * Returns the desired size to fill the controlled stream's internal queue. It can be negative, if the queue is + * over-full. An underlying source ought to use this information to determine when and how to apply backpressure. + */ + get desiredSize() { + if (!IsReadableStreamDefaultController(this)) { + throw defaultControllerBrandCheckException$1('desiredSize'); + } + return ReadableStreamDefaultControllerGetDesiredSize(this); + } + /** + * Closes the controlled readable stream. Consumers will still be able to read any previously-enqueued chunks from + * the stream, but once those are read, the stream will become closed. + */ + close() { + if (!IsReadableStreamDefaultController(this)) { + throw defaultControllerBrandCheckException$1('close'); + } + if (!ReadableStreamDefaultControllerCanCloseOrEnqueue(this)) { + throw new TypeError('The stream is not in a state that permits close'); + } + ReadableStreamDefaultControllerClose(this); + } + enqueue(chunk = undefined) { + if (!IsReadableStreamDefaultController(this)) { + throw defaultControllerBrandCheckException$1('enqueue'); + } + if (!ReadableStreamDefaultControllerCanCloseOrEnqueue(this)) { + throw new TypeError('The stream is not in a state that permits enqueue'); + } + return ReadableStreamDefaultControllerEnqueue(this, chunk); + } + /** + * Errors the controlled readable stream, making all future interactions with it fail with the given error `e`. + */ + error(e = undefined) { + if (!IsReadableStreamDefaultController(this)) { + throw defaultControllerBrandCheckException$1('error'); + } + ReadableStreamDefaultControllerError(this, e); + } + /** @internal */ + [CancelSteps](reason) { + ResetQueue(this); + const result = this._cancelAlgorithm(reason); + ReadableStreamDefaultControllerClearAlgorithms(this); + return result; + } + /** @internal */ + [PullSteps](readRequest) { + const stream = this._controlledReadableStream; + if (this._queue.length > 0) { + const chunk = DequeueValue(this); + if (this._closeRequested && this._queue.length === 0) { + ReadableStreamDefaultControllerClearAlgorithms(this); + ReadableStreamClose(stream); + } + else { + ReadableStreamDefaultControllerCallPullIfNeeded(this); + } + readRequest._chunkSteps(chunk); + } + else { + ReadableStreamAddReadRequest(stream, readRequest); + ReadableStreamDefaultControllerCallPullIfNeeded(this); + } + } + } + Object.defineProperties(ReadableStreamDefaultController.prototype, { + close: { enumerable: true }, + enqueue: { enumerable: true }, + error: { enumerable: true }, + desiredSize: { enumerable: true } + }); + if (typeof SymbolPolyfill.toStringTag === 'symbol') { + Object.defineProperty(ReadableStreamDefaultController.prototype, SymbolPolyfill.toStringTag, { + value: 'ReadableStreamDefaultController', + configurable: true + }); + } + // Abstract operations for the ReadableStreamDefaultController. + function IsReadableStreamDefaultController(x) { + if (!typeIsObject(x)) { + return false; + } + if (!Object.prototype.hasOwnProperty.call(x, '_controlledReadableStream')) { + return false; + } + return x instanceof ReadableStreamDefaultController; + } + function ReadableStreamDefaultControllerCallPullIfNeeded(controller) { + const shouldPull = ReadableStreamDefaultControllerShouldCallPull(controller); + if (!shouldPull) { + return; + } + if (controller._pulling) { + controller._pullAgain = true; + return; + } + controller._pulling = true; + const pullPromise = controller._pullAlgorithm(); + uponPromise(pullPromise, () => { + controller._pulling = false; + if (controller._pullAgain) { + controller._pullAgain = false; + ReadableStreamDefaultControllerCallPullIfNeeded(controller); + } + }, e => { + ReadableStreamDefaultControllerError(controller, e); + }); + } + function ReadableStreamDefaultControllerShouldCallPull(controller) { + const stream = controller._controlledReadableStream; + if (!ReadableStreamDefaultControllerCanCloseOrEnqueue(controller)) { + return false; + } + if (!controller._started) { + return false; + } + if (IsReadableStreamLocked(stream) && ReadableStreamGetNumReadRequests(stream) > 0) { + return true; + } + const desiredSize = ReadableStreamDefaultControllerGetDesiredSize(controller); + if (desiredSize > 0) { + return true; + } + return false; + } + function ReadableStreamDefaultControllerClearAlgorithms(controller) { + controller._pullAlgorithm = undefined; + controller._cancelAlgorithm = undefined; + controller._strategySizeAlgorithm = undefined; + } + // A client of ReadableStreamDefaultController may use these functions directly to bypass state check. + function ReadableStreamDefaultControllerClose(controller) { + if (!ReadableStreamDefaultControllerCanCloseOrEnqueue(controller)) { + return; + } + const stream = controller._controlledReadableStream; + controller._closeRequested = true; + if (controller._queue.length === 0) { + ReadableStreamDefaultControllerClearAlgorithms(controller); + ReadableStreamClose(stream); + } + } + function ReadableStreamDefaultControllerEnqueue(controller, chunk) { + if (!ReadableStreamDefaultControllerCanCloseOrEnqueue(controller)) { + return; + } + const stream = controller._controlledReadableStream; + if (IsReadableStreamLocked(stream) && ReadableStreamGetNumReadRequests(stream) > 0) { + ReadableStreamFulfillReadRequest(stream, chunk, false); + } + else { + let chunkSize; + try { + chunkSize = controller._strategySizeAlgorithm(chunk); + } + catch (chunkSizeE) { + ReadableStreamDefaultControllerError(controller, chunkSizeE); + throw chunkSizeE; + } + try { + EnqueueValueWithSize(controller, chunk, chunkSize); + } + catch (enqueueE) { + ReadableStreamDefaultControllerError(controller, enqueueE); + throw enqueueE; + } + } + ReadableStreamDefaultControllerCallPullIfNeeded(controller); + } + function ReadableStreamDefaultControllerError(controller, e) { + const stream = controller._controlledReadableStream; + if (stream._state !== 'readable') { + return; + } + ResetQueue(controller); + ReadableStreamDefaultControllerClearAlgorithms(controller); + ReadableStreamError(stream, e); + } + function ReadableStreamDefaultControllerGetDesiredSize(controller) { + const state = controller._controlledReadableStream._state; + if (state === 'errored') { + return null; + } + if (state === 'closed') { + return 0; + } + return controller._strategyHWM - controller._queueTotalSize; + } + // This is used in the implementation of TransformStream. + function ReadableStreamDefaultControllerHasBackpressure(controller) { + if (ReadableStreamDefaultControllerShouldCallPull(controller)) { + return false; + } + return true; + } + function ReadableStreamDefaultControllerCanCloseOrEnqueue(controller) { + const state = controller._controlledReadableStream._state; + if (!controller._closeRequested && state === 'readable') { + return true; + } + return false; + } + function SetUpReadableStreamDefaultController(stream, controller, startAlgorithm, pullAlgorithm, cancelAlgorithm, highWaterMark, sizeAlgorithm) { + controller._controlledReadableStream = stream; + controller._queue = undefined; + controller._queueTotalSize = undefined; + ResetQueue(controller); + controller._started = false; + controller._closeRequested = false; + controller._pullAgain = false; + controller._pulling = false; + controller._strategySizeAlgorithm = sizeAlgorithm; + controller._strategyHWM = highWaterMark; + controller._pullAlgorithm = pullAlgorithm; + controller._cancelAlgorithm = cancelAlgorithm; + stream._readableStreamController = controller; + const startResult = startAlgorithm(); + uponPromise(promiseResolvedWith(startResult), () => { + controller._started = true; + ReadableStreamDefaultControllerCallPullIfNeeded(controller); + }, r => { + ReadableStreamDefaultControllerError(controller, r); + }); + } + function SetUpReadableStreamDefaultControllerFromUnderlyingSource(stream, underlyingSource, highWaterMark, sizeAlgorithm) { + const controller = Object.create(ReadableStreamDefaultController.prototype); + let startAlgorithm = () => undefined; + let pullAlgorithm = () => promiseResolvedWith(undefined); + let cancelAlgorithm = () => promiseResolvedWith(undefined); + if (underlyingSource.start !== undefined) { + startAlgorithm = () => underlyingSource.start(controller); + } + if (underlyingSource.pull !== undefined) { + pullAlgorithm = () => underlyingSource.pull(controller); + } + if (underlyingSource.cancel !== undefined) { + cancelAlgorithm = reason => underlyingSource.cancel(reason); + } + SetUpReadableStreamDefaultController(stream, controller, startAlgorithm, pullAlgorithm, cancelAlgorithm, highWaterMark, sizeAlgorithm); + } + // Helper functions for the ReadableStreamDefaultController. + function defaultControllerBrandCheckException$1(name) { + return new TypeError(`ReadableStreamDefaultController.prototype.${name} can only be used on a ReadableStreamDefaultController`); + } + + function ReadableStreamTee(stream, cloneForBranch2) { + if (IsReadableByteStreamController(stream._readableStreamController)) { + return ReadableByteStreamTee(stream); + } + return ReadableStreamDefaultTee(stream); + } + function ReadableStreamDefaultTee(stream, cloneForBranch2) { + const reader = AcquireReadableStreamDefaultReader(stream); + let reading = false; + let readAgain = false; + let canceled1 = false; + let canceled2 = false; + let reason1; + let reason2; + let branch1; + let branch2; + let resolveCancelPromise; + const cancelPromise = newPromise(resolve => { + resolveCancelPromise = resolve; + }); + function pullAlgorithm() { + if (reading) { + readAgain = true; + return promiseResolvedWith(undefined); + } + reading = true; + const readRequest = { + _chunkSteps: chunk => { + // This needs to be delayed a microtask because it takes at least a microtask to detect errors (using + // reader._closedPromise below), and we want errors in stream to error both branches immediately. We cannot let + // successful synchronously-available reads get ahead of asynchronously-available errors. + queueMicrotask(() => { + readAgain = false; + const chunk1 = chunk; + const chunk2 = chunk; + // There is no way to access the cloning code right now in the reference implementation. + // If we add one then we'll need an implementation for serializable objects. + // if (!canceled2 && cloneForBranch2) { + // chunk2 = StructuredDeserialize(StructuredSerialize(chunk2)); + // } + if (!canceled1) { + ReadableStreamDefaultControllerEnqueue(branch1._readableStreamController, chunk1); + } + if (!canceled2) { + ReadableStreamDefaultControllerEnqueue(branch2._readableStreamController, chunk2); + } + reading = false; + if (readAgain) { + pullAlgorithm(); + } + }); + }, + _closeSteps: () => { + reading = false; + if (!canceled1) { + ReadableStreamDefaultControllerClose(branch1._readableStreamController); + } + if (!canceled2) { + ReadableStreamDefaultControllerClose(branch2._readableStreamController); + } + if (!canceled1 || !canceled2) { + resolveCancelPromise(undefined); + } + }, + _errorSteps: () => { + reading = false; + } + }; + ReadableStreamDefaultReaderRead(reader, readRequest); + return promiseResolvedWith(undefined); + } + function cancel1Algorithm(reason) { + canceled1 = true; + reason1 = reason; + if (canceled2) { + const compositeReason = CreateArrayFromList([reason1, reason2]); + const cancelResult = ReadableStreamCancel(stream, compositeReason); + resolveCancelPromise(cancelResult); + } + return cancelPromise; + } + function cancel2Algorithm(reason) { + canceled2 = true; + reason2 = reason; + if (canceled1) { + const compositeReason = CreateArrayFromList([reason1, reason2]); + const cancelResult = ReadableStreamCancel(stream, compositeReason); + resolveCancelPromise(cancelResult); + } + return cancelPromise; + } + function startAlgorithm() { + // do nothing + } + branch1 = CreateReadableStream(startAlgorithm, pullAlgorithm, cancel1Algorithm); + branch2 = CreateReadableStream(startAlgorithm, pullAlgorithm, cancel2Algorithm); + uponRejection(reader._closedPromise, (r) => { + ReadableStreamDefaultControllerError(branch1._readableStreamController, r); + ReadableStreamDefaultControllerError(branch2._readableStreamController, r); + if (!canceled1 || !canceled2) { + resolveCancelPromise(undefined); + } + }); + return [branch1, branch2]; + } + function ReadableByteStreamTee(stream) { + let reader = AcquireReadableStreamDefaultReader(stream); + let reading = false; + let readAgainForBranch1 = false; + let readAgainForBranch2 = false; + let canceled1 = false; + let canceled2 = false; + let reason1; + let reason2; + let branch1; + let branch2; + let resolveCancelPromise; + const cancelPromise = newPromise(resolve => { + resolveCancelPromise = resolve; + }); + function forwardReaderError(thisReader) { + uponRejection(thisReader._closedPromise, r => { + if (thisReader !== reader) { + return; + } + ReadableByteStreamControllerError(branch1._readableStreamController, r); + ReadableByteStreamControllerError(branch2._readableStreamController, r); + if (!canceled1 || !canceled2) { + resolveCancelPromise(undefined); + } + }); + } + function pullWithDefaultReader() { + if (IsReadableStreamBYOBReader(reader)) { + ReadableStreamReaderGenericRelease(reader); + reader = AcquireReadableStreamDefaultReader(stream); + forwardReaderError(reader); + } + const readRequest = { + _chunkSteps: chunk => { + // This needs to be delayed a microtask because it takes at least a microtask to detect errors (using + // reader._closedPromise below), and we want errors in stream to error both branches immediately. We cannot let + // successful synchronously-available reads get ahead of asynchronously-available errors. + queueMicrotask(() => { + readAgainForBranch1 = false; + readAgainForBranch2 = false; + const chunk1 = chunk; + let chunk2 = chunk; + if (!canceled1 && !canceled2) { + try { + chunk2 = CloneAsUint8Array(chunk); + } + catch (cloneE) { + ReadableByteStreamControllerError(branch1._readableStreamController, cloneE); + ReadableByteStreamControllerError(branch2._readableStreamController, cloneE); + resolveCancelPromise(ReadableStreamCancel(stream, cloneE)); + return; + } + } + if (!canceled1) { + ReadableByteStreamControllerEnqueue(branch1._readableStreamController, chunk1); + } + if (!canceled2) { + ReadableByteStreamControllerEnqueue(branch2._readableStreamController, chunk2); + } + reading = false; + if (readAgainForBranch1) { + pull1Algorithm(); + } + else if (readAgainForBranch2) { + pull2Algorithm(); + } + }); + }, + _closeSteps: () => { + reading = false; + if (!canceled1) { + ReadableByteStreamControllerClose(branch1._readableStreamController); + } + if (!canceled2) { + ReadableByteStreamControllerClose(branch2._readableStreamController); + } + if (branch1._readableStreamController._pendingPullIntos.length > 0) { + ReadableByteStreamControllerRespond(branch1._readableStreamController, 0); + } + if (branch2._readableStreamController._pendingPullIntos.length > 0) { + ReadableByteStreamControllerRespond(branch2._readableStreamController, 0); + } + if (!canceled1 || !canceled2) { + resolveCancelPromise(undefined); + } + }, + _errorSteps: () => { + reading = false; + } + }; + ReadableStreamDefaultReaderRead(reader, readRequest); + } + function pullWithBYOBReader(view, forBranch2) { + if (IsReadableStreamDefaultReader(reader)) { + ReadableStreamReaderGenericRelease(reader); + reader = AcquireReadableStreamBYOBReader(stream); + forwardReaderError(reader); + } + const byobBranch = forBranch2 ? branch2 : branch1; + const otherBranch = forBranch2 ? branch1 : branch2; + const readIntoRequest = { + _chunkSteps: chunk => { + // This needs to be delayed a microtask because it takes at least a microtask to detect errors (using + // reader._closedPromise below), and we want errors in stream to error both branches immediately. We cannot let + // successful synchronously-available reads get ahead of asynchronously-available errors. + queueMicrotask(() => { + readAgainForBranch1 = false; + readAgainForBranch2 = false; + const byobCanceled = forBranch2 ? canceled2 : canceled1; + const otherCanceled = forBranch2 ? canceled1 : canceled2; + if (!otherCanceled) { + let clonedChunk; + try { + clonedChunk = CloneAsUint8Array(chunk); + } + catch (cloneE) { + ReadableByteStreamControllerError(byobBranch._readableStreamController, cloneE); + ReadableByteStreamControllerError(otherBranch._readableStreamController, cloneE); + resolveCancelPromise(ReadableStreamCancel(stream, cloneE)); + return; + } + if (!byobCanceled) { + ReadableByteStreamControllerRespondWithNewView(byobBranch._readableStreamController, chunk); + } + ReadableByteStreamControllerEnqueue(otherBranch._readableStreamController, clonedChunk); + } + else if (!byobCanceled) { + ReadableByteStreamControllerRespondWithNewView(byobBranch._readableStreamController, chunk); + } + reading = false; + if (readAgainForBranch1) { + pull1Algorithm(); + } + else if (readAgainForBranch2) { + pull2Algorithm(); + } + }); + }, + _closeSteps: chunk => { + reading = false; + const byobCanceled = forBranch2 ? canceled2 : canceled1; + const otherCanceled = forBranch2 ? canceled1 : canceled2; + if (!byobCanceled) { + ReadableByteStreamControllerClose(byobBranch._readableStreamController); + } + if (!otherCanceled) { + ReadableByteStreamControllerClose(otherBranch._readableStreamController); + } + if (chunk !== undefined) { + if (!byobCanceled) { + ReadableByteStreamControllerRespondWithNewView(byobBranch._readableStreamController, chunk); + } + if (!otherCanceled && otherBranch._readableStreamController._pendingPullIntos.length > 0) { + ReadableByteStreamControllerRespond(otherBranch._readableStreamController, 0); + } + } + if (!byobCanceled || !otherCanceled) { + resolveCancelPromise(undefined); + } + }, + _errorSteps: () => { + reading = false; + } + }; + ReadableStreamBYOBReaderRead(reader, view, readIntoRequest); + } + function pull1Algorithm() { + if (reading) { + readAgainForBranch1 = true; + return promiseResolvedWith(undefined); + } + reading = true; + const byobRequest = ReadableByteStreamControllerGetBYOBRequest(branch1._readableStreamController); + if (byobRequest === null) { + pullWithDefaultReader(); + } + else { + pullWithBYOBReader(byobRequest._view, false); + } + return promiseResolvedWith(undefined); + } + function pull2Algorithm() { + if (reading) { + readAgainForBranch2 = true; + return promiseResolvedWith(undefined); + } + reading = true; + const byobRequest = ReadableByteStreamControllerGetBYOBRequest(branch2._readableStreamController); + if (byobRequest === null) { + pullWithDefaultReader(); + } + else { + pullWithBYOBReader(byobRequest._view, true); + } + return promiseResolvedWith(undefined); + } + function cancel1Algorithm(reason) { + canceled1 = true; + reason1 = reason; + if (canceled2) { + const compositeReason = CreateArrayFromList([reason1, reason2]); + const cancelResult = ReadableStreamCancel(stream, compositeReason); + resolveCancelPromise(cancelResult); + } + return cancelPromise; + } + function cancel2Algorithm(reason) { + canceled2 = true; + reason2 = reason; + if (canceled1) { + const compositeReason = CreateArrayFromList([reason1, reason2]); + const cancelResult = ReadableStreamCancel(stream, compositeReason); + resolveCancelPromise(cancelResult); + } + return cancelPromise; + } + function startAlgorithm() { + return; + } + branch1 = CreateReadableByteStream(startAlgorithm, pull1Algorithm, cancel1Algorithm); + branch2 = CreateReadableByteStream(startAlgorithm, pull2Algorithm, cancel2Algorithm); + forwardReaderError(reader); + return [branch1, branch2]; + } + + function convertUnderlyingDefaultOrByteSource(source, context) { + assertDictionary(source, context); + const original = source; + const autoAllocateChunkSize = original === null || original === void 0 ? void 0 : original.autoAllocateChunkSize; + const cancel = original === null || original === void 0 ? void 0 : original.cancel; + const pull = original === null || original === void 0 ? void 0 : original.pull; + const start = original === null || original === void 0 ? void 0 : original.start; + const type = original === null || original === void 0 ? void 0 : original.type; + return { + autoAllocateChunkSize: autoAllocateChunkSize === undefined ? + undefined : + convertUnsignedLongLongWithEnforceRange(autoAllocateChunkSize, `${context} has member 'autoAllocateChunkSize' that`), + cancel: cancel === undefined ? + undefined : + convertUnderlyingSourceCancelCallback(cancel, original, `${context} has member 'cancel' that`), + pull: pull === undefined ? + undefined : + convertUnderlyingSourcePullCallback(pull, original, `${context} has member 'pull' that`), + start: start === undefined ? + undefined : + convertUnderlyingSourceStartCallback(start, original, `${context} has member 'start' that`), + type: type === undefined ? undefined : convertReadableStreamType(type, `${context} has member 'type' that`) + }; + } + function convertUnderlyingSourceCancelCallback(fn, original, context) { + assertFunction(fn, context); + return (reason) => promiseCall(fn, original, [reason]); + } + function convertUnderlyingSourcePullCallback(fn, original, context) { + assertFunction(fn, context); + return (controller) => promiseCall(fn, original, [controller]); + } + function convertUnderlyingSourceStartCallback(fn, original, context) { + assertFunction(fn, context); + return (controller) => reflectCall(fn, original, [controller]); + } + function convertReadableStreamType(type, context) { + type = `${type}`; + if (type !== 'bytes') { + throw new TypeError(`${context} '${type}' is not a valid enumeration value for ReadableStreamType`); + } + return type; + } + + function convertReaderOptions(options, context) { + assertDictionary(options, context); + const mode = options === null || options === void 0 ? void 0 : options.mode; + return { + mode: mode === undefined ? undefined : convertReadableStreamReaderMode(mode, `${context} has member 'mode' that`) + }; + } + function convertReadableStreamReaderMode(mode, context) { + mode = `${mode}`; + if (mode !== 'byob') { + throw new TypeError(`${context} '${mode}' is not a valid enumeration value for ReadableStreamReaderMode`); + } + return mode; + } + + function convertIteratorOptions(options, context) { + assertDictionary(options, context); + const preventCancel = options === null || options === void 0 ? void 0 : options.preventCancel; + return { preventCancel: Boolean(preventCancel) }; + } + + function convertPipeOptions(options, context) { + assertDictionary(options, context); + const preventAbort = options === null || options === void 0 ? void 0 : options.preventAbort; + const preventCancel = options === null || options === void 0 ? void 0 : options.preventCancel; + const preventClose = options === null || options === void 0 ? void 0 : options.preventClose; + const signal = options === null || options === void 0 ? void 0 : options.signal; + if (signal !== undefined) { + assertAbortSignal(signal, `${context} has member 'signal' that`); + } + return { + preventAbort: Boolean(preventAbort), + preventCancel: Boolean(preventCancel), + preventClose: Boolean(preventClose), + signal + }; + } + function assertAbortSignal(signal, context) { + if (!isAbortSignal(signal)) { + throw new TypeError(`${context} is not an AbortSignal.`); + } + } + + function convertReadableWritablePair(pair, context) { + assertDictionary(pair, context); + const readable = pair === null || pair === void 0 ? void 0 : pair.readable; + assertRequiredField(readable, 'readable', 'ReadableWritablePair'); + assertReadableStream(readable, `${context} has member 'readable' that`); + const writable = pair === null || pair === void 0 ? void 0 : pair.writable; + assertRequiredField(writable, 'writable', 'ReadableWritablePair'); + assertWritableStream(writable, `${context} has member 'writable' that`); + return { readable, writable }; + } + + /** + * A readable stream represents a source of data, from which you can read. + * + * @public + */ + class ReadableStream { + constructor(rawUnderlyingSource = {}, rawStrategy = {}) { + if (rawUnderlyingSource === undefined) { + rawUnderlyingSource = null; + } + else { + assertObject(rawUnderlyingSource, 'First parameter'); + } + const strategy = convertQueuingStrategy(rawStrategy, 'Second parameter'); + const underlyingSource = convertUnderlyingDefaultOrByteSource(rawUnderlyingSource, 'First parameter'); + InitializeReadableStream(this); + if (underlyingSource.type === 'bytes') { + if (strategy.size !== undefined) { + throw new RangeError('The strategy for a byte stream cannot have a size function'); + } + const highWaterMark = ExtractHighWaterMark(strategy, 0); + SetUpReadableByteStreamControllerFromUnderlyingSource(this, underlyingSource, highWaterMark); + } + else { + const sizeAlgorithm = ExtractSizeAlgorithm(strategy); + const highWaterMark = ExtractHighWaterMark(strategy, 1); + SetUpReadableStreamDefaultControllerFromUnderlyingSource(this, underlyingSource, highWaterMark, sizeAlgorithm); + } + } + /** + * Whether or not the readable stream is locked to a {@link ReadableStreamDefaultReader | reader}. + */ + get locked() { + if (!IsReadableStream(this)) { + throw streamBrandCheckException$1('locked'); + } + return IsReadableStreamLocked(this); + } + /** + * Cancels the stream, signaling a loss of interest in the stream by a consumer. + * + * The supplied `reason` argument will be given to the underlying source's {@link UnderlyingSource.cancel | cancel()} + * method, which might or might not use it. + */ + cancel(reason = undefined) { + if (!IsReadableStream(this)) { + return promiseRejectedWith(streamBrandCheckException$1('cancel')); + } + if (IsReadableStreamLocked(this)) { + return promiseRejectedWith(new TypeError('Cannot cancel a stream that already has a reader')); + } + return ReadableStreamCancel(this, reason); + } + getReader(rawOptions = undefined) { + if (!IsReadableStream(this)) { + throw streamBrandCheckException$1('getReader'); + } + const options = convertReaderOptions(rawOptions, 'First parameter'); + if (options.mode === undefined) { + return AcquireReadableStreamDefaultReader(this); + } + return AcquireReadableStreamBYOBReader(this); + } + pipeThrough(rawTransform, rawOptions = {}) { + if (!IsReadableStream(this)) { + throw streamBrandCheckException$1('pipeThrough'); + } + assertRequiredArgument(rawTransform, 1, 'pipeThrough'); + const transform = convertReadableWritablePair(rawTransform, 'First parameter'); + const options = convertPipeOptions(rawOptions, 'Second parameter'); + if (IsReadableStreamLocked(this)) { + throw new TypeError('ReadableStream.prototype.pipeThrough cannot be used on a locked ReadableStream'); + } + if (IsWritableStreamLocked(transform.writable)) { + throw new TypeError('ReadableStream.prototype.pipeThrough cannot be used on a locked WritableStream'); + } + const promise = ReadableStreamPipeTo(this, transform.writable, options.preventClose, options.preventAbort, options.preventCancel, options.signal); + setPromiseIsHandledToTrue(promise); + return transform.readable; + } + pipeTo(destination, rawOptions = {}) { + if (!IsReadableStream(this)) { + return promiseRejectedWith(streamBrandCheckException$1('pipeTo')); + } + if (destination === undefined) { + return promiseRejectedWith(`Parameter 1 is required in 'pipeTo'.`); + } + if (!IsWritableStream(destination)) { + return promiseRejectedWith(new TypeError(`ReadableStream.prototype.pipeTo's first argument must be a WritableStream`)); + } + let options; + try { + options = convertPipeOptions(rawOptions, 'Second parameter'); + } + catch (e) { + return promiseRejectedWith(e); + } + if (IsReadableStreamLocked(this)) { + return promiseRejectedWith(new TypeError('ReadableStream.prototype.pipeTo cannot be used on a locked ReadableStream')); + } + if (IsWritableStreamLocked(destination)) { + return promiseRejectedWith(new TypeError('ReadableStream.prototype.pipeTo cannot be used on a locked WritableStream')); + } + return ReadableStreamPipeTo(this, destination, options.preventClose, options.preventAbort, options.preventCancel, options.signal); + } + /** + * Tees this readable stream, returning a two-element array containing the two resulting branches as + * new {@link ReadableStream} instances. + * + * Teeing a stream will lock it, preventing any other consumer from acquiring a reader. + * To cancel the stream, cancel both of the resulting branches; a composite cancellation reason will then be + * propagated to the stream's underlying source. + * + * Note that the chunks seen in each branch will be the same object. If the chunks are not immutable, + * this could allow interference between the two branches. + */ + tee() { + if (!IsReadableStream(this)) { + throw streamBrandCheckException$1('tee'); + } + const branches = ReadableStreamTee(this); + return CreateArrayFromList(branches); + } + values(rawOptions = undefined) { + if (!IsReadableStream(this)) { + throw streamBrandCheckException$1('values'); + } + const options = convertIteratorOptions(rawOptions, 'First parameter'); + return AcquireReadableStreamAsyncIterator(this, options.preventCancel); + } + } + Object.defineProperties(ReadableStream.prototype, { + cancel: { enumerable: true }, + getReader: { enumerable: true }, + pipeThrough: { enumerable: true }, + pipeTo: { enumerable: true }, + tee: { enumerable: true }, + values: { enumerable: true }, + locked: { enumerable: true } + }); + if (typeof SymbolPolyfill.toStringTag === 'symbol') { + Object.defineProperty(ReadableStream.prototype, SymbolPolyfill.toStringTag, { + value: 'ReadableStream', + configurable: true + }); + } + if (typeof SymbolPolyfill.asyncIterator === 'symbol') { + Object.defineProperty(ReadableStream.prototype, SymbolPolyfill.asyncIterator, { + value: ReadableStream.prototype.values, + writable: true, + configurable: true + }); + } + // Abstract operations for the ReadableStream. + // Throws if and only if startAlgorithm throws. + function CreateReadableStream(startAlgorithm, pullAlgorithm, cancelAlgorithm, highWaterMark = 1, sizeAlgorithm = () => 1) { + const stream = Object.create(ReadableStream.prototype); + InitializeReadableStream(stream); + const controller = Object.create(ReadableStreamDefaultController.prototype); + SetUpReadableStreamDefaultController(stream, controller, startAlgorithm, pullAlgorithm, cancelAlgorithm, highWaterMark, sizeAlgorithm); + return stream; + } + // Throws if and only if startAlgorithm throws. + function CreateReadableByteStream(startAlgorithm, pullAlgorithm, cancelAlgorithm) { + const stream = Object.create(ReadableStream.prototype); + InitializeReadableStream(stream); + const controller = Object.create(ReadableByteStreamController.prototype); + SetUpReadableByteStreamController(stream, controller, startAlgorithm, pullAlgorithm, cancelAlgorithm, 0, undefined); + return stream; + } + function InitializeReadableStream(stream) { + stream._state = 'readable'; + stream._reader = undefined; + stream._storedError = undefined; + stream._disturbed = false; + } + function IsReadableStream(x) { + if (!typeIsObject(x)) { + return false; + } + if (!Object.prototype.hasOwnProperty.call(x, '_readableStreamController')) { + return false; + } + return x instanceof ReadableStream; + } + function IsReadableStreamLocked(stream) { + if (stream._reader === undefined) { + return false; + } + return true; + } + // ReadableStream API exposed for controllers. + function ReadableStreamCancel(stream, reason) { + stream._disturbed = true; + if (stream._state === 'closed') { + return promiseResolvedWith(undefined); + } + if (stream._state === 'errored') { + return promiseRejectedWith(stream._storedError); + } + ReadableStreamClose(stream); + const reader = stream._reader; + if (reader !== undefined && IsReadableStreamBYOBReader(reader)) { + reader._readIntoRequests.forEach(readIntoRequest => { + readIntoRequest._closeSteps(undefined); + }); + reader._readIntoRequests = new SimpleQueue(); + } + const sourceCancelPromise = stream._readableStreamController[CancelSteps](reason); + return transformPromiseWith(sourceCancelPromise, noop); + } + function ReadableStreamClose(stream) { + stream._state = 'closed'; + const reader = stream._reader; + if (reader === undefined) { + return; + } + defaultReaderClosedPromiseResolve(reader); + if (IsReadableStreamDefaultReader(reader)) { + reader._readRequests.forEach(readRequest => { + readRequest._closeSteps(); + }); + reader._readRequests = new SimpleQueue(); + } + } + function ReadableStreamError(stream, e) { + stream._state = 'errored'; + stream._storedError = e; + const reader = stream._reader; + if (reader === undefined) { + return; + } + defaultReaderClosedPromiseReject(reader, e); + if (IsReadableStreamDefaultReader(reader)) { + reader._readRequests.forEach(readRequest => { + readRequest._errorSteps(e); + }); + reader._readRequests = new SimpleQueue(); + } + else { + reader._readIntoRequests.forEach(readIntoRequest => { + readIntoRequest._errorSteps(e); + }); + reader._readIntoRequests = new SimpleQueue(); + } + } + // Helper functions for the ReadableStream. + function streamBrandCheckException$1(name) { + return new TypeError(`ReadableStream.prototype.${name} can only be used on a ReadableStream`); + } + + function convertQueuingStrategyInit(init, context) { + assertDictionary(init, context); + const highWaterMark = init === null || init === void 0 ? void 0 : init.highWaterMark; + assertRequiredField(highWaterMark, 'highWaterMark', 'QueuingStrategyInit'); + return { + highWaterMark: convertUnrestrictedDouble(highWaterMark) + }; + } + + // The size function must not have a prototype property nor be a constructor + const byteLengthSizeFunction = (chunk) => { + return chunk.byteLength; + }; + try { + Object.defineProperty(byteLengthSizeFunction, 'name', { + value: 'size', + configurable: true + }); + } + catch (_a) { + // This property is non-configurable in older browsers, so ignore if this throws. + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/name#browser_compatibility + } + /** + * A queuing strategy that counts the number of bytes in each chunk. + * + * @public + */ + class ByteLengthQueuingStrategy { + constructor(options) { + assertRequiredArgument(options, 1, 'ByteLengthQueuingStrategy'); + options = convertQueuingStrategyInit(options, 'First parameter'); + this._byteLengthQueuingStrategyHighWaterMark = options.highWaterMark; + } + /** + * Returns the high water mark provided to the constructor. + */ + get highWaterMark() { + if (!IsByteLengthQueuingStrategy(this)) { + throw byteLengthBrandCheckException('highWaterMark'); + } + return this._byteLengthQueuingStrategyHighWaterMark; + } + /** + * Measures the size of `chunk` by returning the value of its `byteLength` property. + */ + get size() { + if (!IsByteLengthQueuingStrategy(this)) { + throw byteLengthBrandCheckException('size'); + } + return byteLengthSizeFunction; + } + } + Object.defineProperties(ByteLengthQueuingStrategy.prototype, { + highWaterMark: { enumerable: true }, + size: { enumerable: true } + }); + if (typeof SymbolPolyfill.toStringTag === 'symbol') { + Object.defineProperty(ByteLengthQueuingStrategy.prototype, SymbolPolyfill.toStringTag, { + value: 'ByteLengthQueuingStrategy', + configurable: true + }); + } + // Helper functions for the ByteLengthQueuingStrategy. + function byteLengthBrandCheckException(name) { + return new TypeError(`ByteLengthQueuingStrategy.prototype.${name} can only be used on a ByteLengthQueuingStrategy`); + } + function IsByteLengthQueuingStrategy(x) { + if (!typeIsObject(x)) { + return false; + } + if (!Object.prototype.hasOwnProperty.call(x, '_byteLengthQueuingStrategyHighWaterMark')) { + return false; + } + return x instanceof ByteLengthQueuingStrategy; + } + + // The size function must not have a prototype property nor be a constructor + const countSizeFunction = () => { + return 1; + }; + try { + Object.defineProperty(countSizeFunction, 'name', { + value: 'size', + configurable: true + }); + } + catch (_a) { + // This property is non-configurable in older browsers, so ignore if this throws. + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/name#browser_compatibility + } + /** + * A queuing strategy that counts the number of chunks. + * + * @public + */ + class CountQueuingStrategy { + constructor(options) { + assertRequiredArgument(options, 1, 'CountQueuingStrategy'); + options = convertQueuingStrategyInit(options, 'First parameter'); + this._countQueuingStrategyHighWaterMark = options.highWaterMark; + } + /** + * Returns the high water mark provided to the constructor. + */ + get highWaterMark() { + if (!IsCountQueuingStrategy(this)) { + throw countBrandCheckException('highWaterMark'); + } + return this._countQueuingStrategyHighWaterMark; + } + /** + * Measures the size of `chunk` by always returning 1. + * This ensures that the total queue size is a count of the number of chunks in the queue. + */ + get size() { + if (!IsCountQueuingStrategy(this)) { + throw countBrandCheckException('size'); + } + return countSizeFunction; + } + } + Object.defineProperties(CountQueuingStrategy.prototype, { + highWaterMark: { enumerable: true }, + size: { enumerable: true } + }); + if (typeof SymbolPolyfill.toStringTag === 'symbol') { + Object.defineProperty(CountQueuingStrategy.prototype, SymbolPolyfill.toStringTag, { + value: 'CountQueuingStrategy', + configurable: true + }); + } + // Helper functions for the CountQueuingStrategy. + function countBrandCheckException(name) { + return new TypeError(`CountQueuingStrategy.prototype.${name} can only be used on a CountQueuingStrategy`); + } + function IsCountQueuingStrategy(x) { + if (!typeIsObject(x)) { + return false; + } + if (!Object.prototype.hasOwnProperty.call(x, '_countQueuingStrategyHighWaterMark')) { + return false; + } + return x instanceof CountQueuingStrategy; + } + + function convertTransformer(original, context) { + assertDictionary(original, context); + const flush = original === null || original === void 0 ? void 0 : original.flush; + const readableType = original === null || original === void 0 ? void 0 : original.readableType; + const start = original === null || original === void 0 ? void 0 : original.start; + const transform = original === null || original === void 0 ? void 0 : original.transform; + const writableType = original === null || original === void 0 ? void 0 : original.writableType; + return { + flush: flush === undefined ? + undefined : + convertTransformerFlushCallback(flush, original, `${context} has member 'flush' that`), + readableType, + start: start === undefined ? + undefined : + convertTransformerStartCallback(start, original, `${context} has member 'start' that`), + transform: transform === undefined ? + undefined : + convertTransformerTransformCallback(transform, original, `${context} has member 'transform' that`), + writableType + }; + } + function convertTransformerFlushCallback(fn, original, context) { + assertFunction(fn, context); + return (controller) => promiseCall(fn, original, [controller]); + } + function convertTransformerStartCallback(fn, original, context) { + assertFunction(fn, context); + return (controller) => reflectCall(fn, original, [controller]); + } + function convertTransformerTransformCallback(fn, original, context) { + assertFunction(fn, context); + return (chunk, controller) => promiseCall(fn, original, [chunk, controller]); + } + + // Class TransformStream + /** + * A transform stream consists of a pair of streams: a {@link WritableStream | writable stream}, + * known as its writable side, and a {@link ReadableStream | readable stream}, known as its readable side. + * In a manner specific to the transform stream in question, writes to the writable side result in new data being + * made available for reading from the readable side. + * + * @public + */ + class TransformStream { + constructor(rawTransformer = {}, rawWritableStrategy = {}, rawReadableStrategy = {}) { + if (rawTransformer === undefined) { + rawTransformer = null; + } + const writableStrategy = convertQueuingStrategy(rawWritableStrategy, 'Second parameter'); + const readableStrategy = convertQueuingStrategy(rawReadableStrategy, 'Third parameter'); + const transformer = convertTransformer(rawTransformer, 'First parameter'); + if (transformer.readableType !== undefined) { + throw new RangeError('Invalid readableType specified'); + } + if (transformer.writableType !== undefined) { + throw new RangeError('Invalid writableType specified'); + } + const readableHighWaterMark = ExtractHighWaterMark(readableStrategy, 0); + const readableSizeAlgorithm = ExtractSizeAlgorithm(readableStrategy); + const writableHighWaterMark = ExtractHighWaterMark(writableStrategy, 1); + const writableSizeAlgorithm = ExtractSizeAlgorithm(writableStrategy); + let startPromise_resolve; + const startPromise = newPromise(resolve => { + startPromise_resolve = resolve; + }); + InitializeTransformStream(this, startPromise, writableHighWaterMark, writableSizeAlgorithm, readableHighWaterMark, readableSizeAlgorithm); + SetUpTransformStreamDefaultControllerFromTransformer(this, transformer); + if (transformer.start !== undefined) { + startPromise_resolve(transformer.start(this._transformStreamController)); + } + else { + startPromise_resolve(undefined); + } + } + /** + * The readable side of the transform stream. + */ + get readable() { + if (!IsTransformStream(this)) { + throw streamBrandCheckException('readable'); + } + return this._readable; + } + /** + * The writable side of the transform stream. + */ + get writable() { + if (!IsTransformStream(this)) { + throw streamBrandCheckException('writable'); + } + return this._writable; + } + } + Object.defineProperties(TransformStream.prototype, { + readable: { enumerable: true }, + writable: { enumerable: true } + }); + if (typeof SymbolPolyfill.toStringTag === 'symbol') { + Object.defineProperty(TransformStream.prototype, SymbolPolyfill.toStringTag, { + value: 'TransformStream', + configurable: true + }); + } + function InitializeTransformStream(stream, startPromise, writableHighWaterMark, writableSizeAlgorithm, readableHighWaterMark, readableSizeAlgorithm) { + function startAlgorithm() { + return startPromise; + } + function writeAlgorithm(chunk) { + return TransformStreamDefaultSinkWriteAlgorithm(stream, chunk); + } + function abortAlgorithm(reason) { + return TransformStreamDefaultSinkAbortAlgorithm(stream, reason); + } + function closeAlgorithm() { + return TransformStreamDefaultSinkCloseAlgorithm(stream); + } + stream._writable = CreateWritableStream(startAlgorithm, writeAlgorithm, closeAlgorithm, abortAlgorithm, writableHighWaterMark, writableSizeAlgorithm); + function pullAlgorithm() { + return TransformStreamDefaultSourcePullAlgorithm(stream); + } + function cancelAlgorithm(reason) { + TransformStreamErrorWritableAndUnblockWrite(stream, reason); + return promiseResolvedWith(undefined); + } + stream._readable = CreateReadableStream(startAlgorithm, pullAlgorithm, cancelAlgorithm, readableHighWaterMark, readableSizeAlgorithm); + // The [[backpressure]] slot is set to undefined so that it can be initialised by TransformStreamSetBackpressure. + stream._backpressure = undefined; + stream._backpressureChangePromise = undefined; + stream._backpressureChangePromise_resolve = undefined; + TransformStreamSetBackpressure(stream, true); + stream._transformStreamController = undefined; + } + function IsTransformStream(x) { + if (!typeIsObject(x)) { + return false; + } + if (!Object.prototype.hasOwnProperty.call(x, '_transformStreamController')) { + return false; + } + return x instanceof TransformStream; + } + // This is a no-op if both sides are already errored. + function TransformStreamError(stream, e) { + ReadableStreamDefaultControllerError(stream._readable._readableStreamController, e); + TransformStreamErrorWritableAndUnblockWrite(stream, e); + } + function TransformStreamErrorWritableAndUnblockWrite(stream, e) { + TransformStreamDefaultControllerClearAlgorithms(stream._transformStreamController); + WritableStreamDefaultControllerErrorIfNeeded(stream._writable._writableStreamController, e); + if (stream._backpressure) { + // Pretend that pull() was called to permit any pending write() calls to complete. TransformStreamSetBackpressure() + // cannot be called from enqueue() or pull() once the ReadableStream is errored, so this will will be the final time + // _backpressure is set. + TransformStreamSetBackpressure(stream, false); + } + } + function TransformStreamSetBackpressure(stream, backpressure) { + // Passes also when called during construction. + if (stream._backpressureChangePromise !== undefined) { + stream._backpressureChangePromise_resolve(); + } + stream._backpressureChangePromise = newPromise(resolve => { + stream._backpressureChangePromise_resolve = resolve; + }); + stream._backpressure = backpressure; + } + // Class TransformStreamDefaultController + /** + * Allows control of the {@link ReadableStream} and {@link WritableStream} of the associated {@link TransformStream}. + * + * @public + */ + class TransformStreamDefaultController { + constructor() { + throw new TypeError('Illegal constructor'); + } + /** + * Returns the desired size to fill the readable side’s internal queue. It can be negative, if the queue is over-full. + */ + get desiredSize() { + if (!IsTransformStreamDefaultController(this)) { + throw defaultControllerBrandCheckException('desiredSize'); + } + const readableController = this._controlledTransformStream._readable._readableStreamController; + return ReadableStreamDefaultControllerGetDesiredSize(readableController); + } + enqueue(chunk = undefined) { + if (!IsTransformStreamDefaultController(this)) { + throw defaultControllerBrandCheckException('enqueue'); + } + TransformStreamDefaultControllerEnqueue(this, chunk); + } + /** + * Errors both the readable side and the writable side of the controlled transform stream, making all future + * interactions with it fail with the given error `e`. Any chunks queued for transformation will be discarded. + */ + error(reason = undefined) { + if (!IsTransformStreamDefaultController(this)) { + throw defaultControllerBrandCheckException('error'); + } + TransformStreamDefaultControllerError(this, reason); + } + /** + * Closes the readable side and errors the writable side of the controlled transform stream. This is useful when the + * transformer only needs to consume a portion of the chunks written to the writable side. + */ + terminate() { + if (!IsTransformStreamDefaultController(this)) { + throw defaultControllerBrandCheckException('terminate'); + } + TransformStreamDefaultControllerTerminate(this); + } + } + Object.defineProperties(TransformStreamDefaultController.prototype, { + enqueue: { enumerable: true }, + error: { enumerable: true }, + terminate: { enumerable: true }, + desiredSize: { enumerable: true } + }); + if (typeof SymbolPolyfill.toStringTag === 'symbol') { + Object.defineProperty(TransformStreamDefaultController.prototype, SymbolPolyfill.toStringTag, { + value: 'TransformStreamDefaultController', + configurable: true + }); + } + // Transform Stream Default Controller Abstract Operations + function IsTransformStreamDefaultController(x) { + if (!typeIsObject(x)) { + return false; + } + if (!Object.prototype.hasOwnProperty.call(x, '_controlledTransformStream')) { + return false; + } + return x instanceof TransformStreamDefaultController; + } + function SetUpTransformStreamDefaultController(stream, controller, transformAlgorithm, flushAlgorithm) { + controller._controlledTransformStream = stream; + stream._transformStreamController = controller; + controller._transformAlgorithm = transformAlgorithm; + controller._flushAlgorithm = flushAlgorithm; + } + function SetUpTransformStreamDefaultControllerFromTransformer(stream, transformer) { + const controller = Object.create(TransformStreamDefaultController.prototype); + let transformAlgorithm = (chunk) => { + try { + TransformStreamDefaultControllerEnqueue(controller, chunk); + return promiseResolvedWith(undefined); + } + catch (transformResultE) { + return promiseRejectedWith(transformResultE); + } + }; + let flushAlgorithm = () => promiseResolvedWith(undefined); + if (transformer.transform !== undefined) { + transformAlgorithm = chunk => transformer.transform(chunk, controller); + } + if (transformer.flush !== undefined) { + flushAlgorithm = () => transformer.flush(controller); + } + SetUpTransformStreamDefaultController(stream, controller, transformAlgorithm, flushAlgorithm); + } + function TransformStreamDefaultControllerClearAlgorithms(controller) { + controller._transformAlgorithm = undefined; + controller._flushAlgorithm = undefined; + } + function TransformStreamDefaultControllerEnqueue(controller, chunk) { + const stream = controller._controlledTransformStream; + const readableController = stream._readable._readableStreamController; + if (!ReadableStreamDefaultControllerCanCloseOrEnqueue(readableController)) { + throw new TypeError('Readable side is not in a state that permits enqueue'); + } + // We throttle transform invocations based on the backpressure of the ReadableStream, but we still + // accept TransformStreamDefaultControllerEnqueue() calls. + try { + ReadableStreamDefaultControllerEnqueue(readableController, chunk); + } + catch (e) { + // This happens when readableStrategy.size() throws. + TransformStreamErrorWritableAndUnblockWrite(stream, e); + throw stream._readable._storedError; + } + const backpressure = ReadableStreamDefaultControllerHasBackpressure(readableController); + if (backpressure !== stream._backpressure) { + TransformStreamSetBackpressure(stream, true); + } + } + function TransformStreamDefaultControllerError(controller, e) { + TransformStreamError(controller._controlledTransformStream, e); + } + function TransformStreamDefaultControllerPerformTransform(controller, chunk) { + const transformPromise = controller._transformAlgorithm(chunk); + return transformPromiseWith(transformPromise, undefined, r => { + TransformStreamError(controller._controlledTransformStream, r); + throw r; + }); + } + function TransformStreamDefaultControllerTerminate(controller) { + const stream = controller._controlledTransformStream; + const readableController = stream._readable._readableStreamController; + ReadableStreamDefaultControllerClose(readableController); + const error = new TypeError('TransformStream terminated'); + TransformStreamErrorWritableAndUnblockWrite(stream, error); + } + // TransformStreamDefaultSink Algorithms + function TransformStreamDefaultSinkWriteAlgorithm(stream, chunk) { + const controller = stream._transformStreamController; + if (stream._backpressure) { + const backpressureChangePromise = stream._backpressureChangePromise; + return transformPromiseWith(backpressureChangePromise, () => { + const writable = stream._writable; + const state = writable._state; + if (state === 'erroring') { + throw writable._storedError; + } + return TransformStreamDefaultControllerPerformTransform(controller, chunk); + }); + } + return TransformStreamDefaultControllerPerformTransform(controller, chunk); + } + function TransformStreamDefaultSinkAbortAlgorithm(stream, reason) { + // abort() is not called synchronously, so it is possible for abort() to be called when the stream is already + // errored. + TransformStreamError(stream, reason); + return promiseResolvedWith(undefined); + } + function TransformStreamDefaultSinkCloseAlgorithm(stream) { + // stream._readable cannot change after construction, so caching it across a call to user code is safe. + const readable = stream._readable; + const controller = stream._transformStreamController; + const flushPromise = controller._flushAlgorithm(); + TransformStreamDefaultControllerClearAlgorithms(controller); + // Return a promise that is fulfilled with undefined on success. + return transformPromiseWith(flushPromise, () => { + if (readable._state === 'errored') { + throw readable._storedError; + } + ReadableStreamDefaultControllerClose(readable._readableStreamController); + }, r => { + TransformStreamError(stream, r); + throw readable._storedError; + }); + } + // TransformStreamDefaultSource Algorithms + function TransformStreamDefaultSourcePullAlgorithm(stream) { + // Invariant. Enforced by the promises returned by start() and pull(). + TransformStreamSetBackpressure(stream, false); + // Prevent the next pull() call until there is backpressure. + return stream._backpressureChangePromise; + } + // Helper functions for the TransformStreamDefaultController. + function defaultControllerBrandCheckException(name) { + return new TypeError(`TransformStreamDefaultController.prototype.${name} can only be used on a TransformStreamDefaultController`); + } + // Helper functions for the TransformStream. + function streamBrandCheckException(name) { + return new TypeError(`TransformStream.prototype.${name} can only be used on a TransformStream`); + } + + exports.ByteLengthQueuingStrategy = ByteLengthQueuingStrategy; + exports.CountQueuingStrategy = CountQueuingStrategy; + exports.ReadableByteStreamController = ReadableByteStreamController; + exports.ReadableStream = ReadableStream; + exports.ReadableStreamBYOBReader = ReadableStreamBYOBReader; + exports.ReadableStreamBYOBRequest = ReadableStreamBYOBRequest; + exports.ReadableStreamDefaultController = ReadableStreamDefaultController; + exports.ReadableStreamDefaultReader = ReadableStreamDefaultReader; + exports.TransformStream = TransformStream; + exports.TransformStreamDefaultController = TransformStreamDefaultController; + exports.WritableStream = WritableStream; + exports.WritableStreamDefaultController = WritableStreamDefaultController; + exports.WritableStreamDefaultWriter = WritableStreamDefaultWriter; + + Object.defineProperty(exports, '__esModule', { value: true }); + + }))); + +} (ponyfill_es2018, ponyfill_es2018.exports)); + return ponyfill_es2018.exports; +} + +/* c8 ignore start */ + +// 64 KiB (same size chrome slice theirs blob into Uint8array's) +const POOL_SIZE$1 = 65536; + +if (!globalThis.ReadableStream) { + // `node:stream/web` got introduced in v16.5.0 as experimental + // and it's preferred over the polyfilled version. So we also + // suppress the warning that gets emitted by NodeJS for using it. + try { + const process = require('node:process'); + const { emitWarning } = process; + try { + process.emitWarning = () => {}; + Object.assign(globalThis, require('node:stream/web')); + process.emitWarning = emitWarning; + } catch (error) { + process.emitWarning = emitWarning; + throw error + } + } catch (error) { + // fallback to polyfill implementation + Object.assign(globalThis, requirePonyfill_es2018()); + } +} + +try { + // Don't use node: prefix for this, require+node: is not supported until node v14.14 + // Only `import()` can use prefix in 12.20 and later + const { Blob } = require('buffer'); + if (Blob && !Blob.prototype.stream) { + Blob.prototype.stream = function name (params) { + let position = 0; + const blob = this; + + return new ReadableStream({ + type: 'bytes', + async pull (ctrl) { + const chunk = blob.slice(position, Math.min(blob.size, position + POOL_SIZE$1)); + const buffer = await chunk.arrayBuffer(); + position += buffer.byteLength; + ctrl.enqueue(new Uint8Array(buffer)); + + if (position === blob.size) { + ctrl.close(); + } + } + }) + }; + } +} catch (error) {} + +/*! fetch-blob. MIT License. Jimmy Wärting */ + +// 64 KiB (same size chrome slice theirs blob into Uint8array's) +const POOL_SIZE = 65536; + +/** @param {(Blob | Uint8Array)[]} parts */ +async function * toIterator (parts, clone = true) { + for (const part of parts) { + if ('stream' in part) { + yield * (/** @type {AsyncIterableIterator} */ (part.stream())); + } else if (ArrayBuffer.isView(part)) { + if (clone) { + let position = part.byteOffset; + const end = part.byteOffset + part.byteLength; + while (position !== end) { + const size = Math.min(end - position, POOL_SIZE); + const chunk = part.buffer.slice(position, position + size); + position += chunk.byteLength; + yield new Uint8Array(chunk); + } + } else { + yield part; + } + /* c8 ignore next 10 */ + } else { + // For blobs that have arrayBuffer but no stream method (nodes buffer.Blob) + let position = 0, b = (/** @type {Blob} */ (part)); + while (position !== b.size) { + const chunk = b.slice(position, Math.min(b.size, position + POOL_SIZE)); + const buffer = await chunk.arrayBuffer(); + position += buffer.byteLength; + yield new Uint8Array(buffer); + } + } + } +} + +const _Blob = class Blob { + /** @type {Array.<(Blob|Uint8Array)>} */ + #parts = [] + #type = '' + #size = 0 + #endings = 'transparent' + + /** + * The Blob() constructor returns a new Blob object. The content + * of the blob consists of the concatenation of the values given + * in the parameter array. + * + * @param {*} blobParts + * @param {{ type?: string, endings?: string }} [options] + */ + constructor (blobParts = [], options = {}) { + if (typeof blobParts !== 'object' || blobParts === null) { + throw new TypeError('Failed to construct \'Blob\': The provided value cannot be converted to a sequence.') + } + + if (typeof blobParts[Symbol.iterator] !== 'function') { + throw new TypeError('Failed to construct \'Blob\': The object must have a callable @@iterator property.') + } + + if (typeof options !== 'object' && typeof options !== 'function') { + throw new TypeError('Failed to construct \'Blob\': parameter 2 cannot convert to dictionary.') + } + + if (options === null) options = {}; + + const encoder = new TextEncoder(); + for (const element of blobParts) { + let part; + if (ArrayBuffer.isView(element)) { + part = new Uint8Array(element.buffer.slice(element.byteOffset, element.byteOffset + element.byteLength)); + } else if (element instanceof ArrayBuffer) { + part = new Uint8Array(element.slice(0)); + } else if (element instanceof Blob) { + part = element; + } else { + part = encoder.encode(`${element}`); + } + + const size = ArrayBuffer.isView(part) ? part.byteLength : part.size; + // Avoid pushing empty parts into the array to better GC them + if (size) { + this.#size += size; + this.#parts.push(part); + } + } + + this.#endings = `${options.endings === undefined ? 'transparent' : options.endings}`; + const type = options.type === undefined ? '' : String(options.type); + this.#type = /^[\x20-\x7E]*$/.test(type) ? type : ''; + } + + /** + * The Blob interface's size property returns the + * size of the Blob in bytes. + */ + get size () { + return this.#size + } + + /** + * The type property of a Blob object returns the MIME type of the file. + */ + get type () { + return this.#type + } + + /** + * The text() method in the Blob interface returns a Promise + * that resolves with a string containing the contents of + * the blob, interpreted as UTF-8. + * + * @return {Promise} + */ + async text () { + // More optimized than using this.arrayBuffer() + // that requires twice as much ram + const decoder = new TextDecoder(); + let str = ''; + for await (const part of toIterator(this.#parts, false)) { + str += decoder.decode(part, { stream: true }); + } + // Remaining + str += decoder.decode(); + return str + } + + /** + * The arrayBuffer() method in the Blob interface returns a + * Promise that resolves with the contents of the blob as + * binary data contained in an ArrayBuffer. + * + * @return {Promise} + */ + async arrayBuffer () { + // Easier way... Just a unnecessary overhead + // const view = new Uint8Array(this.size); + // await this.stream().getReader({mode: 'byob'}).read(view); + // return view.buffer; + + const data = new Uint8Array(this.size); + let offset = 0; + for await (const chunk of toIterator(this.#parts, false)) { + data.set(chunk, offset); + offset += chunk.length; + } + + return data.buffer + } + + stream () { + const it = toIterator(this.#parts, true); + + return new globalThis.ReadableStream({ + // @ts-ignore + type: 'bytes', + async pull (ctrl) { + const chunk = await it.next(); + chunk.done ? ctrl.close() : ctrl.enqueue(chunk.value); + }, + + async cancel () { + await it.return(); + } + }) + } + + /** + * The Blob interface's slice() method creates and returns a + * new Blob object which contains data from a subset of the + * blob on which it's called. + * + * @param {number} [start] + * @param {number} [end] + * @param {string} [type] + */ + slice (start = 0, end = this.size, type = '') { + const { size } = this; + + let relativeStart = start < 0 ? Math.max(size + start, 0) : Math.min(start, size); + let relativeEnd = end < 0 ? Math.max(size + end, 0) : Math.min(end, size); + + const span = Math.max(relativeEnd - relativeStart, 0); + const parts = this.#parts; + const blobParts = []; + let added = 0; + + for (const part of parts) { + // don't add the overflow to new blobParts + if (added >= span) { + break + } + + const size = ArrayBuffer.isView(part) ? part.byteLength : part.size; + if (relativeStart && size <= relativeStart) { + // Skip the beginning and change the relative + // start & end position as we skip the unwanted parts + relativeStart -= size; + relativeEnd -= size; + } else { + let chunk; + if (ArrayBuffer.isView(part)) { + chunk = part.subarray(relativeStart, Math.min(size, relativeEnd)); + added += chunk.byteLength; + } else { + chunk = part.slice(relativeStart, Math.min(size, relativeEnd)); + added += chunk.size; + } + relativeEnd -= size; + blobParts.push(chunk); + relativeStart = 0; // All next sequential parts should start at 0 + } + } + + const blob = new Blob([], { type: String(type).toLowerCase() }); + blob.#size = span; + blob.#parts = blobParts; + + return blob + } + + get [Symbol.toStringTag] () { + return 'Blob' + } + + static [Symbol.hasInstance] (object) { + return ( + object && + typeof object === 'object' && + typeof object.constructor === 'function' && + ( + typeof object.stream === 'function' || + typeof object.arrayBuffer === 'function' + ) && + /^(Blob|File)$/.test(object[Symbol.toStringTag]) + ) + } +}; + +Object.defineProperties(_Blob.prototype, { + size: { enumerable: true }, + type: { enumerable: true }, + slice: { enumerable: true } +}); + +/** @type {typeof globalThis.Blob} */ +const Blob = _Blob; + +const _File = class File extends Blob { + #lastModified = 0 + #name = '' + + /** + * @param {*[]} fileBits + * @param {string} fileName + * @param {{lastModified?: number, type?: string}} options + */// @ts-ignore + constructor (fileBits, fileName, options = {}) { + if (arguments.length < 2) { + throw new TypeError(`Failed to construct 'File': 2 arguments required, but only ${arguments.length} present.`) + } + super(fileBits, options); + + if (options === null) options = {}; + + // Simulate WebIDL type casting for NaN value in lastModified option. + const lastModified = options.lastModified === undefined ? Date.now() : Number(options.lastModified); + if (!Number.isNaN(lastModified)) { + this.#lastModified = lastModified; + } + + this.#name = String(fileName); + } + + get name () { + return this.#name + } + + get lastModified () { + return this.#lastModified + } + + get [Symbol.toStringTag] () { + return 'File' + } + + static [Symbol.hasInstance] (object) { + return !!object && object instanceof Blob && + /^(File)$/.test(object[Symbol.toStringTag]) + } +}; + +/** @type {typeof globalThis.File} */// @ts-ignore +const File = _File; + +/*! formdata-polyfill. MIT License. Jimmy Wärting */ + +var {toStringTag:t,iterator:i,hasInstance:h}=Symbol, +r=Math.random, +m='append,set,get,getAll,delete,keys,values,entries,forEach,constructor'.split(','), +f$1=(a,b,c)=>(a+='',/^(Blob|File)$/.test(b && b[t])?[(c=c!==void 0?c+'':b[t]=='File'?b.name:'blob',a),b.name!==c||b[t]=='blob'?new File([b],c,b):b]:[a,b+'']), +e=(c,f)=>(f?c:c.replace(/\r?\n|\r/g,'\r\n')).replace(/\n/g,'%0A').replace(/\r/g,'%0D').replace(/"/g,'%22'), +x=(n, a, e)=>{if(a.lengthtypeof o[m]!='function')} +append(...a){x('append',arguments,2);this.#d.push(f$1(...a));} +delete(a){x('delete',arguments,1);a+='';this.#d=this.#d.filter(([b])=>b!==a);} +get(a){x('get',arguments,1);a+='';for(var b=this.#d,l=b.length,c=0;cc[0]===a&&b.push(c[1]));return b} +has(a){x('has',arguments,1);a+='';return this.#d.some(b=>b[0]===a)} +forEach(a,b){x('forEach',arguments,1);for(var [c,d]of this)a.call(b,d,c,this);} +set(...a){x('set',arguments,2);var b=[],c=!0;a=f$1(...a);this.#d.forEach(d=>{d[0]===a[0]?c&&(c=!b.push(a)):b.push(d);});c&&b.push(a);this.#d=b;} +*entries(){yield*this.#d;} +*keys(){for(var[a]of this)yield a;} +*values(){for(var[,a]of this)yield a;}}; + +/** @param {FormData} F */ +function formDataToBlob (F,B=Blob){ +var b=`${r()}${r()}`.replace(/\./g, '').slice(-28).padStart(32, '-'),c=[],p=`--${b}\r\nContent-Disposition: form-data; name="`; +F.forEach((v,n)=>typeof v=='string' +?c.push(p+e(n)+`"\r\n\r\n${v.replace(/\r(?!\n)|(? { + return ( + typeof object === 'object' && + typeof object.append === 'function' && + typeof object.delete === 'function' && + typeof object.get === 'function' && + typeof object.getAll === 'function' && + typeof object.has === 'function' && + typeof object.set === 'function' && + typeof object.sort === 'function' && + object[NAME] === 'URLSearchParams' + ); +}; + +/** + * Check if `object` is a W3C `Blob` object (which `File` inherits from) + * @param {*} object - Object to check for + * @return {boolean} + */ +const isBlob = object => { + return ( + object && + typeof object === 'object' && + typeof object.arrayBuffer === 'function' && + typeof object.type === 'string' && + typeof object.stream === 'function' && + typeof object.constructor === 'function' && + /^(Blob|File)$/.test(object[NAME]) + ); +}; + +/** + * Check if `obj` is an instance of AbortSignal. + * @param {*} object - Object to check for + * @return {boolean} + */ +const isAbortSignal = object => { + return ( + typeof object === 'object' && ( + object[NAME] === 'AbortSignal' || + object[NAME] === 'EventTarget' + ) + ); +}; + +/** + * isDomainOrSubdomain reports whether sub is a subdomain (or exact match) of + * the parent domain. + * + * Both domains must already be in canonical form. + * @param {string|URL} original + * @param {string|URL} destination + */ +const isDomainOrSubdomain = (destination, original) => { + const orig = new URL(original).hostname; + const dest = new URL(destination).hostname; + + return orig === dest || orig.endsWith(`.${dest}`); +}; + +const pipeline = node_util.promisify(Stream__default["default"].pipeline); +const INTERNALS$2 = Symbol('Body internals'); + +/** + * Body mixin + * + * Ref: https://fetch.spec.whatwg.org/#body + * + * @param Stream body Readable stream + * @param Object opts Response options + * @return Void + */ +class Body { + constructor(body, { + size = 0 + } = {}) { + let boundary = null; + + if (body === null) { + // Body is undefined or null + body = null; + } else if (isURLSearchParameters(body)) { + // Body is a URLSearchParams + body = node_buffer.Buffer.from(body.toString()); + } else if (isBlob(body)) ; else if (node_buffer.Buffer.isBuffer(body)) ; else if (node_util.types.isAnyArrayBuffer(body)) { + // Body is ArrayBuffer + body = node_buffer.Buffer.from(body); + } else if (ArrayBuffer.isView(body)) { + // Body is ArrayBufferView + body = node_buffer.Buffer.from(body.buffer, body.byteOffset, body.byteLength); + } else if (body instanceof Stream__default["default"]) ; else if (body instanceof FormData) { + // Body is FormData + body = formDataToBlob(body); + boundary = body.type.split('=')[1]; + } else { + // None of the above + // coerce to string then buffer + body = node_buffer.Buffer.from(String(body)); + } + + let stream = body; + + if (node_buffer.Buffer.isBuffer(body)) { + stream = Stream__default["default"].Readable.from(body); + } else if (isBlob(body)) { + stream = Stream__default["default"].Readable.from(body.stream()); + } + + this[INTERNALS$2] = { + body, + stream, + boundary, + disturbed: false, + error: null + }; + this.size = size; + + if (body instanceof Stream__default["default"]) { + body.on('error', error_ => { + const error = error_ instanceof FetchBaseError ? + error_ : + new FetchError(`Invalid response body while trying to fetch ${this.url}: ${error_.message}`, 'system', error_); + this[INTERNALS$2].error = error; + }); + } + } + + get body() { + return this[INTERNALS$2].stream; + } + + get bodyUsed() { + return this[INTERNALS$2].disturbed; + } + + /** + * Decode response as ArrayBuffer + * + * @return Promise + */ + async arrayBuffer() { + const {buffer, byteOffset, byteLength} = await consumeBody(this); + return buffer.slice(byteOffset, byteOffset + byteLength); + } + + async formData() { + const ct = this.headers.get('content-type'); + + if (ct.startsWith('application/x-www-form-urlencoded')) { + const formData = new FormData(); + const parameters = new URLSearchParams(await this.text()); + + for (const [name, value] of parameters) { + formData.append(name, value); + } + + return formData; + } + + const {toFormData} = await Promise.resolve().then(function () { return multipartParser; }); + return toFormData(this.body, ct); + } + + /** + * Return raw response as Blob + * + * @return Promise + */ + async blob() { + const ct = (this.headers && this.headers.get('content-type')) || (this[INTERNALS$2].body && this[INTERNALS$2].body.type) || ''; + const buf = await this.arrayBuffer(); + + return new Blob([buf], { + type: ct + }); + } + + /** + * Decode response as json + * + * @return Promise + */ + async json() { + const text = await this.text(); + return JSON.parse(text); + } + + /** + * Decode response as text + * + * @return Promise + */ + async text() { + const buffer = await consumeBody(this); + return new TextDecoder().decode(buffer); + } + + /** + * Decode response as buffer (non-spec api) + * + * @return Promise + */ + buffer() { + return consumeBody(this); + } +} + +Body.prototype.buffer = node_util.deprecate(Body.prototype.buffer, 'Please use \'response.arrayBuffer()\' instead of \'response.buffer()\'', 'node-fetch#buffer'); + +// In browsers, all properties are enumerable. +Object.defineProperties(Body.prototype, { + body: {enumerable: true}, + bodyUsed: {enumerable: true}, + arrayBuffer: {enumerable: true}, + blob: {enumerable: true}, + json: {enumerable: true}, + text: {enumerable: true}, + data: {get: node_util.deprecate(() => {}, + 'data doesn\'t exist, use json(), text(), arrayBuffer(), or body instead', + 'https://github.com/node-fetch/node-fetch/issues/1000 (response)')} +}); + +/** + * Consume and convert an entire Body to a Buffer. + * + * Ref: https://fetch.spec.whatwg.org/#concept-body-consume-body + * + * @return Promise + */ +async function consumeBody(data) { + if (data[INTERNALS$2].disturbed) { + throw new TypeError(`body used already for: ${data.url}`); + } + + data[INTERNALS$2].disturbed = true; + + if (data[INTERNALS$2].error) { + throw data[INTERNALS$2].error; + } + + const {body} = data; + + // Body is null + if (body === null) { + return node_buffer.Buffer.alloc(0); + } + + /* c8 ignore next 3 */ + if (!(body instanceof Stream__default["default"])) { + return node_buffer.Buffer.alloc(0); + } + + // Body is stream + // get ready to actually consume the body + const accum = []; + let accumBytes = 0; + + try { + for await (const chunk of body) { + if (data.size > 0 && accumBytes + chunk.length > data.size) { + const error = new FetchError(`content size at ${data.url} over limit: ${data.size}`, 'max-size'); + body.destroy(error); + throw error; + } + + accumBytes += chunk.length; + accum.push(chunk); + } + } catch (error) { + const error_ = error instanceof FetchBaseError ? error : new FetchError(`Invalid response body while trying to fetch ${data.url}: ${error.message}`, 'system', error); + throw error_; + } + + if (body.readableEnded === true || body._readableState.ended === true) { + try { + if (accum.every(c => typeof c === 'string')) { + return node_buffer.Buffer.from(accum.join('')); + } + + return node_buffer.Buffer.concat(accum, accumBytes); + } catch (error) { + throw new FetchError(`Could not create Buffer from response body for ${data.url}: ${error.message}`, 'system', error); + } + } else { + throw new FetchError(`Premature close of server response while trying to fetch ${data.url}`); + } +} + +/** + * Clone body given Res/Req instance + * + * @param Mixed instance Response or Request instance + * @param String highWaterMark highWaterMark for both PassThrough body streams + * @return Mixed + */ +const clone = (instance, highWaterMark) => { + let p1; + let p2; + let {body} = instance[INTERNALS$2]; + + // Don't allow cloning a used body + if (instance.bodyUsed) { + throw new Error('cannot clone body after it is used'); + } + + // Check that body is a stream and not form-data object + // note: we can't clone the form-data object without having it as a dependency + if ((body instanceof Stream__default["default"]) && (typeof body.getBoundary !== 'function')) { + // Tee instance body + p1 = new Stream.PassThrough({highWaterMark}); + p2 = new Stream.PassThrough({highWaterMark}); + body.pipe(p1); + body.pipe(p2); + // Set instance body to teed body and return the other teed body + instance[INTERNALS$2].stream = p1; + body = p2; + } + + return body; +}; + +const getNonSpecFormDataBoundary = node_util.deprecate( + body => body.getBoundary(), + 'form-data doesn\'t follow the spec and requires special treatment. Use alternative package', + 'https://github.com/node-fetch/node-fetch/issues/1167' +); + +/** + * Performs the operation "extract a `Content-Type` value from |object|" as + * specified in the specification: + * https://fetch.spec.whatwg.org/#concept-bodyinit-extract + * + * This function assumes that instance.body is present. + * + * @param {any} body Any options.body input + * @returns {string | null} + */ +const extractContentType = (body, request) => { + // Body is null or undefined + if (body === null) { + return null; + } + + // Body is string + if (typeof body === 'string') { + return 'text/plain;charset=UTF-8'; + } + + // Body is a URLSearchParams + if (isURLSearchParameters(body)) { + return 'application/x-www-form-urlencoded;charset=UTF-8'; + } + + // Body is blob + if (isBlob(body)) { + return body.type || null; + } + + // Body is a Buffer (Buffer, ArrayBuffer or ArrayBufferView) + if (node_buffer.Buffer.isBuffer(body) || node_util.types.isAnyArrayBuffer(body) || ArrayBuffer.isView(body)) { + return null; + } + + if (body instanceof FormData) { + return `multipart/form-data; boundary=${request[INTERNALS$2].boundary}`; + } + + // Detect form data input from form-data module + if (body && typeof body.getBoundary === 'function') { + return `multipart/form-data;boundary=${getNonSpecFormDataBoundary(body)}`; + } + + // Body is stream - can't really do much about this + if (body instanceof Stream__default["default"]) { + return null; + } + + // Body constructor defaults other things to string + return 'text/plain;charset=UTF-8'; +}; + +/** + * The Fetch Standard treats this as if "total bytes" is a property on the body. + * For us, we have to explicitly get it with a function. + * + * ref: https://fetch.spec.whatwg.org/#concept-body-total-bytes + * + * @param {any} obj.body Body object from the Body instance. + * @returns {number | null} + */ +const getTotalBytes = request => { + const {body} = request[INTERNALS$2]; + + // Body is null or undefined + if (body === null) { + return 0; + } + + // Body is Blob + if (isBlob(body)) { + return body.size; + } + + // Body is Buffer + if (node_buffer.Buffer.isBuffer(body)) { + return body.length; + } + + // Detect form data input from form-data module + if (body && typeof body.getLengthSync === 'function') { + return body.hasKnownLength && body.hasKnownLength() ? body.getLengthSync() : null; + } + + // Body is stream + return null; +}; + +/** + * Write a Body to a Node.js WritableStream (e.g. http.Request) object. + * + * @param {Stream.Writable} dest The stream to write to. + * @param obj.body Body object from the Body instance. + * @returns {Promise} + */ +const writeToStream = async (dest, {body}) => { + if (body === null) { + // Body is null + dest.end(); + } else { + // Body is stream + await pipeline(body, dest); + } +}; + +/** + * Headers.js + * + * Headers class offers convenient helpers + */ + +/* c8 ignore next 9 */ +const validateHeaderName = typeof http__default["default"].validateHeaderName === 'function' ? + http__default["default"].validateHeaderName : + name => { + if (!/^[\^`\-\w!#$%&'*+.|~]+$/.test(name)) { + const error = new TypeError(`Header name must be a valid HTTP token [${name}]`); + Object.defineProperty(error, 'code', {value: 'ERR_INVALID_HTTP_TOKEN'}); + throw error; + } + }; + +/* c8 ignore next 9 */ +const validateHeaderValue = typeof http__default["default"].validateHeaderValue === 'function' ? + http__default["default"].validateHeaderValue : + (name, value) => { + if (/[^\t\u0020-\u007E\u0080-\u00FF]/.test(value)) { + const error = new TypeError(`Invalid character in header content ["${name}"]`); + Object.defineProperty(error, 'code', {value: 'ERR_INVALID_CHAR'}); + throw error; + } + }; + +/** + * @typedef {Headers | Record | Iterable | Iterable>} HeadersInit + */ + +/** + * This Fetch API interface allows you to perform various actions on HTTP request and response headers. + * These actions include retrieving, setting, adding to, and removing. + * A Headers object has an associated header list, which is initially empty and consists of zero or more name and value pairs. + * You can add to this using methods like append() (see Examples.) + * In all methods of this interface, header names are matched by case-insensitive byte sequence. + * + */ +class Headers extends URLSearchParams { + /** + * Headers class + * + * @constructor + * @param {HeadersInit} [init] - Response headers + */ + constructor(init) { + // Validate and normalize init object in [name, value(s)][] + /** @type {string[][]} */ + let result = []; + if (init instanceof Headers) { + const raw = init.raw(); + for (const [name, values] of Object.entries(raw)) { + result.push(...values.map(value => [name, value])); + } + } else if (init == null) ; else if (typeof init === 'object' && !node_util.types.isBoxedPrimitive(init)) { + const method = init[Symbol.iterator]; + // eslint-disable-next-line no-eq-null, eqeqeq + if (method == null) { + // Record + result.push(...Object.entries(init)); + } else { + if (typeof method !== 'function') { + throw new TypeError('Header pairs must be iterable'); + } + + // Sequence> + // Note: per spec we have to first exhaust the lists then process them + result = [...init] + .map(pair => { + if ( + typeof pair !== 'object' || node_util.types.isBoxedPrimitive(pair) + ) { + throw new TypeError('Each header pair must be an iterable object'); + } + + return [...pair]; + }).map(pair => { + if (pair.length !== 2) { + throw new TypeError('Each header pair must be a name/value tuple'); + } + + return [...pair]; + }); + } + } else { + throw new TypeError('Failed to construct \'Headers\': The provided value is not of type \'(sequence> or record)'); + } + + // Validate and lowercase + result = + result.length > 0 ? + result.map(([name, value]) => { + validateHeaderName(name); + validateHeaderValue(name, String(value)); + return [String(name).toLowerCase(), String(value)]; + }) : + undefined; + + super(result); + + // Returning a Proxy that will lowercase key names, validate parameters and sort keys + // eslint-disable-next-line no-constructor-return + return new Proxy(this, { + get(target, p, receiver) { + switch (p) { + case 'append': + case 'set': + return (name, value) => { + validateHeaderName(name); + validateHeaderValue(name, String(value)); + return URLSearchParams.prototype[p].call( + target, + String(name).toLowerCase(), + String(value) + ); + }; + + case 'delete': + case 'has': + case 'getAll': + return name => { + validateHeaderName(name); + return URLSearchParams.prototype[p].call( + target, + String(name).toLowerCase() + ); + }; + + case 'keys': + return () => { + target.sort(); + return new Set(URLSearchParams.prototype.keys.call(target)).keys(); + }; + + default: + return Reflect.get(target, p, receiver); + } + } + }); + /* c8 ignore next */ + } + + get [Symbol.toStringTag]() { + return this.constructor.name; + } + + toString() { + return Object.prototype.toString.call(this); + } + + get(name) { + const values = this.getAll(name); + if (values.length === 0) { + return null; + } + + let value = values.join(', '); + if (/^content-encoding$/i.test(name)) { + value = value.toLowerCase(); + } + + return value; + } + + forEach(callback, thisArg = undefined) { + for (const name of this.keys()) { + Reflect.apply(callback, thisArg, [this.get(name), name, this]); + } + } + + * values() { + for (const name of this.keys()) { + yield this.get(name); + } + } + + /** + * @type {() => IterableIterator<[string, string]>} + */ + * entries() { + for (const name of this.keys()) { + yield [name, this.get(name)]; + } + } + + [Symbol.iterator]() { + return this.entries(); + } + + /** + * Node-fetch non-spec method + * returning all headers and their values as array + * @returns {Record} + */ + raw() { + return [...this.keys()].reduce((result, key) => { + result[key] = this.getAll(key); + return result; + }, {}); + } + + /** + * For better console.log(headers) and also to convert Headers into Node.js Request compatible format + */ + [Symbol.for('nodejs.util.inspect.custom')]() { + return [...this.keys()].reduce((result, key) => { + const values = this.getAll(key); + // Http.request() only supports string as Host header. + // This hack makes specifying custom Host header possible. + if (key === 'host') { + result[key] = values[0]; + } else { + result[key] = values.length > 1 ? values : values[0]; + } + + return result; + }, {}); + } +} + +/** + * Re-shaping object for Web IDL tests + * Only need to do it for overridden methods + */ +Object.defineProperties( + Headers.prototype, + ['get', 'entries', 'forEach', 'values'].reduce((result, property) => { + result[property] = {enumerable: true}; + return result; + }, {}) +); + +/** + * Create a Headers object from an http.IncomingMessage.rawHeaders, ignoring those that do + * not conform to HTTP grammar productions. + * @param {import('http').IncomingMessage['rawHeaders']} headers + */ +function fromRawHeaders(headers = []) { + return new Headers( + headers + // Split into pairs + .reduce((result, value, index, array) => { + if (index % 2 === 0) { + result.push(array.slice(index, index + 2)); + } + + return result; + }, []) + .filter(([name, value]) => { + try { + validateHeaderName(name); + validateHeaderValue(name, String(value)); + return true; + } catch { + return false; + } + }) + + ); +} + +const redirectStatus = new Set([301, 302, 303, 307, 308]); + +/** + * Redirect code matching + * + * @param {number} code - Status code + * @return {boolean} + */ +const isRedirect = code => { + return redirectStatus.has(code); +}; + +/** + * Response.js + * + * Response class provides content decoding + */ + +const INTERNALS$1 = Symbol('Response internals'); + +/** + * Response class + * + * Ref: https://fetch.spec.whatwg.org/#response-class + * + * @param Stream body Readable stream + * @param Object opts Response options + * @return Void + */ +class Response extends Body { + constructor(body = null, options = {}) { + super(body, options); + + // eslint-disable-next-line no-eq-null, eqeqeq, no-negated-condition + const status = options.status != null ? options.status : 200; + + const headers = new Headers(options.headers); + + if (body !== null && !headers.has('Content-Type')) { + const contentType = extractContentType(body, this); + if (contentType) { + headers.append('Content-Type', contentType); + } + } + + this[INTERNALS$1] = { + type: 'default', + url: options.url, + status, + statusText: options.statusText || '', + headers, + counter: options.counter, + highWaterMark: options.highWaterMark + }; + } + + get type() { + return this[INTERNALS$1].type; + } + + get url() { + return this[INTERNALS$1].url || ''; + } + + get status() { + return this[INTERNALS$1].status; + } + + /** + * Convenience property representing if the request ended normally + */ + get ok() { + return this[INTERNALS$1].status >= 200 && this[INTERNALS$1].status < 300; + } + + get redirected() { + return this[INTERNALS$1].counter > 0; + } + + get statusText() { + return this[INTERNALS$1].statusText; + } + + get headers() { + return this[INTERNALS$1].headers; + } + + get highWaterMark() { + return this[INTERNALS$1].highWaterMark; + } + + /** + * Clone this response + * + * @return Response + */ + clone() { + return new Response(clone(this, this.highWaterMark), { + type: this.type, + url: this.url, + status: this.status, + statusText: this.statusText, + headers: this.headers, + ok: this.ok, + redirected: this.redirected, + size: this.size, + highWaterMark: this.highWaterMark + }); + } + + /** + * @param {string} url The URL that the new response is to originate from. + * @param {number} status An optional status code for the response (e.g., 302.) + * @returns {Response} A Response object. + */ + static redirect(url, status = 302) { + if (!isRedirect(status)) { + throw new RangeError('Failed to execute "redirect" on "response": Invalid status code'); + } + + return new Response(null, { + headers: { + location: new URL(url).toString() + }, + status + }); + } + + static error() { + const response = new Response(null, {status: 0, statusText: ''}); + response[INTERNALS$1].type = 'error'; + return response; + } + + get [Symbol.toStringTag]() { + return 'Response'; + } +} + +Object.defineProperties(Response.prototype, { + type: {enumerable: true}, + url: {enumerable: true}, + status: {enumerable: true}, + ok: {enumerable: true}, + redirected: {enumerable: true}, + statusText: {enumerable: true}, + headers: {enumerable: true}, + clone: {enumerable: true} +}); + +const getSearch = parsedURL => { + if (parsedURL.search) { + return parsedURL.search; + } + + const lastOffset = parsedURL.href.length - 1; + const hash = parsedURL.hash || (parsedURL.href[lastOffset] === '#' ? '#' : ''); + return parsedURL.href[lastOffset - hash.length] === '?' ? '?' : ''; +}; + +/** + * @external URL + * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/URL|URL} + */ + +/** + * @module utils/referrer + * @private + */ + +/** + * @see {@link https://w3c.github.io/webappsec-referrer-policy/#strip-url|Referrer Policy §8.4. Strip url for use as a referrer} + * @param {string} URL + * @param {boolean} [originOnly=false] + */ +function stripURLForUseAsAReferrer(url, originOnly = false) { + // 1. If url is null, return no referrer. + if (url == null) { // eslint-disable-line no-eq-null, eqeqeq + return 'no-referrer'; + } + + url = new URL(url); + + // 2. If url's scheme is a local scheme, then return no referrer. + if (/^(about|blob|data):$/.test(url.protocol)) { + return 'no-referrer'; + } + + // 3. Set url's username to the empty string. + url.username = ''; + + // 4. Set url's password to null. + // Note: `null` appears to be a mistake as this actually results in the password being `"null"`. + url.password = ''; + + // 5. Set url's fragment to null. + // Note: `null` appears to be a mistake as this actually results in the fragment being `"#null"`. + url.hash = ''; + + // 6. If the origin-only flag is true, then: + if (originOnly) { + // 6.1. Set url's path to null. + // Note: `null` appears to be a mistake as this actually results in the path being `"/null"`. + url.pathname = ''; + + // 6.2. Set url's query to null. + // Note: `null` appears to be a mistake as this actually results in the query being `"?null"`. + url.search = ''; + } + + // 7. Return url. + return url; +} + +/** + * @see {@link https://w3c.github.io/webappsec-referrer-policy/#enumdef-referrerpolicy|enum ReferrerPolicy} + */ +const ReferrerPolicy = new Set([ + '', + 'no-referrer', + 'no-referrer-when-downgrade', + 'same-origin', + 'origin', + 'strict-origin', + 'origin-when-cross-origin', + 'strict-origin-when-cross-origin', + 'unsafe-url' +]); + +/** + * @see {@link https://w3c.github.io/webappsec-referrer-policy/#default-referrer-policy|default referrer policy} + */ +const DEFAULT_REFERRER_POLICY = 'strict-origin-when-cross-origin'; + +/** + * @see {@link https://w3c.github.io/webappsec-referrer-policy/#referrer-policies|Referrer Policy §3. Referrer Policies} + * @param {string} referrerPolicy + * @returns {string} referrerPolicy + */ +function validateReferrerPolicy(referrerPolicy) { + if (!ReferrerPolicy.has(referrerPolicy)) { + throw new TypeError(`Invalid referrerPolicy: ${referrerPolicy}`); + } + + return referrerPolicy; +} + +/** + * @see {@link https://w3c.github.io/webappsec-secure-contexts/#is-origin-trustworthy|Referrer Policy §3.2. Is origin potentially trustworthy?} + * @param {external:URL} url + * @returns `true`: "Potentially Trustworthy", `false`: "Not Trustworthy" + */ +function isOriginPotentiallyTrustworthy(url) { + // 1. If origin is an opaque origin, return "Not Trustworthy". + // Not applicable + + // 2. Assert: origin is a tuple origin. + // Not for implementations + + // 3. If origin's scheme is either "https" or "wss", return "Potentially Trustworthy". + if (/^(http|ws)s:$/.test(url.protocol)) { + return true; + } + + // 4. If origin's host component matches one of the CIDR notations 127.0.0.0/8 or ::1/128 [RFC4632], return "Potentially Trustworthy". + const hostIp = url.host.replace(/(^\[)|(]$)/g, ''); + const hostIPVersion = node_net.isIP(hostIp); + + if (hostIPVersion === 4 && /^127\./.test(hostIp)) { + return true; + } + + if (hostIPVersion === 6 && /^(((0+:){7})|(::(0+:){0,6}))0*1$/.test(hostIp)) { + return true; + } + + // 5. If origin's host component is "localhost" or falls within ".localhost", and the user agent conforms to the name resolution rules in [let-localhost-be-localhost], return "Potentially Trustworthy". + // We are returning FALSE here because we cannot ensure conformance to + // let-localhost-be-loalhost (https://tools.ietf.org/html/draft-west-let-localhost-be-localhost) + if (/^(.+\.)*localhost$/.test(url.host)) { + return false; + } + + // 6. If origin's scheme component is file, return "Potentially Trustworthy". + if (url.protocol === 'file:') { + return true; + } + + // 7. If origin's scheme component is one which the user agent considers to be authenticated, return "Potentially Trustworthy". + // Not supported + + // 8. If origin has been configured as a trustworthy origin, return "Potentially Trustworthy". + // Not supported + + // 9. Return "Not Trustworthy". + return false; +} + +/** + * @see {@link https://w3c.github.io/webappsec-secure-contexts/#is-url-trustworthy|Referrer Policy §3.3. Is url potentially trustworthy?} + * @param {external:URL} url + * @returns `true`: "Potentially Trustworthy", `false`: "Not Trustworthy" + */ +function isUrlPotentiallyTrustworthy(url) { + // 1. If url is "about:blank" or "about:srcdoc", return "Potentially Trustworthy". + if (/^about:(blank|srcdoc)$/.test(url)) { + return true; + } + + // 2. If url's scheme is "data", return "Potentially Trustworthy". + if (url.protocol === 'data:') { + return true; + } + + // Note: The origin of blob: and filesystem: URLs is the origin of the context in which they were + // created. Therefore, blobs created in a trustworthy origin will themselves be potentially + // trustworthy. + if (/^(blob|filesystem):$/.test(url.protocol)) { + return true; + } + + // 3. Return the result of executing §3.2 Is origin potentially trustworthy? on url's origin. + return isOriginPotentiallyTrustworthy(url); +} + +/** + * Modifies the referrerURL to enforce any extra security policy considerations. + * @see {@link https://w3c.github.io/webappsec-referrer-policy/#determine-requests-referrer|Referrer Policy §8.3. Determine request's Referrer}, step 7 + * @callback module:utils/referrer~referrerURLCallback + * @param {external:URL} referrerURL + * @returns {external:URL} modified referrerURL + */ + +/** + * Modifies the referrerOrigin to enforce any extra security policy considerations. + * @see {@link https://w3c.github.io/webappsec-referrer-policy/#determine-requests-referrer|Referrer Policy §8.3. Determine request's Referrer}, step 7 + * @callback module:utils/referrer~referrerOriginCallback + * @param {external:URL} referrerOrigin + * @returns {external:URL} modified referrerOrigin + */ + +/** + * @see {@link https://w3c.github.io/webappsec-referrer-policy/#determine-requests-referrer|Referrer Policy §8.3. Determine request's Referrer} + * @param {Request} request + * @param {object} o + * @param {module:utils/referrer~referrerURLCallback} o.referrerURLCallback + * @param {module:utils/referrer~referrerOriginCallback} o.referrerOriginCallback + * @returns {external:URL} Request's referrer + */ +function determineRequestsReferrer(request, {referrerURLCallback, referrerOriginCallback} = {}) { + // There are 2 notes in the specification about invalid pre-conditions. We return null, here, for + // these cases: + // > Note: If request's referrer is "no-referrer", Fetch will not call into this algorithm. + // > Note: If request's referrer policy is the empty string, Fetch will not call into this + // > algorithm. + if (request.referrer === 'no-referrer' || request.referrerPolicy === '') { + return null; + } + + // 1. Let policy be request's associated referrer policy. + const policy = request.referrerPolicy; + + // 2. Let environment be request's client. + // not applicable to node.js + + // 3. Switch on request's referrer: + if (request.referrer === 'about:client') { + return 'no-referrer'; + } + + // "a URL": Let referrerSource be request's referrer. + const referrerSource = request.referrer; + + // 4. Let request's referrerURL be the result of stripping referrerSource for use as a referrer. + let referrerURL = stripURLForUseAsAReferrer(referrerSource); + + // 5. Let referrerOrigin be the result of stripping referrerSource for use as a referrer, with the + // origin-only flag set to true. + let referrerOrigin = stripURLForUseAsAReferrer(referrerSource, true); + + // 6. If the result of serializing referrerURL is a string whose length is greater than 4096, set + // referrerURL to referrerOrigin. + if (referrerURL.toString().length > 4096) { + referrerURL = referrerOrigin; + } + + // 7. The user agent MAY alter referrerURL or referrerOrigin at this point to enforce arbitrary + // policy considerations in the interests of minimizing data leakage. For example, the user + // agent could strip the URL down to an origin, modify its host, replace it with an empty + // string, etc. + if (referrerURLCallback) { + referrerURL = referrerURLCallback(referrerURL); + } + + if (referrerOriginCallback) { + referrerOrigin = referrerOriginCallback(referrerOrigin); + } + + // 8.Execute the statements corresponding to the value of policy: + const currentURL = new URL(request.url); + + switch (policy) { + case 'no-referrer': + return 'no-referrer'; + + case 'origin': + return referrerOrigin; + + case 'unsafe-url': + return referrerURL; + + case 'strict-origin': + // 1. If referrerURL is a potentially trustworthy URL and request's current URL is not a + // potentially trustworthy URL, then return no referrer. + if (isUrlPotentiallyTrustworthy(referrerURL) && !isUrlPotentiallyTrustworthy(currentURL)) { + return 'no-referrer'; + } + + // 2. Return referrerOrigin. + return referrerOrigin.toString(); + + case 'strict-origin-when-cross-origin': + // 1. If the origin of referrerURL and the origin of request's current URL are the same, then + // return referrerURL. + if (referrerURL.origin === currentURL.origin) { + return referrerURL; + } + + // 2. If referrerURL is a potentially trustworthy URL and request's current URL is not a + // potentially trustworthy URL, then return no referrer. + if (isUrlPotentiallyTrustworthy(referrerURL) && !isUrlPotentiallyTrustworthy(currentURL)) { + return 'no-referrer'; + } + + // 3. Return referrerOrigin. + return referrerOrigin; + + case 'same-origin': + // 1. If the origin of referrerURL and the origin of request's current URL are the same, then + // return referrerURL. + if (referrerURL.origin === currentURL.origin) { + return referrerURL; + } + + // 2. Return no referrer. + return 'no-referrer'; + + case 'origin-when-cross-origin': + // 1. If the origin of referrerURL and the origin of request's current URL are the same, then + // return referrerURL. + if (referrerURL.origin === currentURL.origin) { + return referrerURL; + } + + // Return referrerOrigin. + return referrerOrigin; + + case 'no-referrer-when-downgrade': + // 1. If referrerURL is a potentially trustworthy URL and request's current URL is not a + // potentially trustworthy URL, then return no referrer. + if (isUrlPotentiallyTrustworthy(referrerURL) && !isUrlPotentiallyTrustworthy(currentURL)) { + return 'no-referrer'; + } + + // 2. Return referrerURL. + return referrerURL; + + default: + throw new TypeError(`Invalid referrerPolicy: ${policy}`); + } +} + +/** + * @see {@link https://w3c.github.io/webappsec-referrer-policy/#parse-referrer-policy-from-header|Referrer Policy §8.1. Parse a referrer policy from a Referrer-Policy header} + * @param {Headers} headers Response headers + * @returns {string} policy + */ +function parseReferrerPolicyFromHeader(headers) { + // 1. Let policy-tokens be the result of extracting header list values given `Referrer-Policy` + // and response’s header list. + const policyTokens = (headers.get('referrer-policy') || '').split(/[,\s]+/); + + // 2. Let policy be the empty string. + let policy = ''; + + // 3. For each token in policy-tokens, if token is a referrer policy and token is not the empty + // string, then set policy to token. + // Note: This algorithm loops over multiple policy values to allow deployment of new policy + // values with fallbacks for older user agents, as described in § 11.1 Unknown Policy Values. + for (const token of policyTokens) { + if (token && ReferrerPolicy.has(token)) { + policy = token; + } + } + + // 4. Return policy. + return policy; +} + +/** + * Request.js + * + * Request class contains server only options + * + * All spec algorithm step numbers are based on https://fetch.spec.whatwg.org/commit-snapshots/ae716822cb3a61843226cd090eefc6589446c1d2/. + */ + +const INTERNALS = Symbol('Request internals'); + +/** + * Check if `obj` is an instance of Request. + * + * @param {*} object + * @return {boolean} + */ +const isRequest = object => { + return ( + typeof object === 'object' && + typeof object[INTERNALS] === 'object' + ); +}; + +const doBadDataWarn = node_util.deprecate(() => {}, + '.data is not a valid RequestInit property, use .body instead', + 'https://github.com/node-fetch/node-fetch/issues/1000 (request)'); + +/** + * Request class + * + * Ref: https://fetch.spec.whatwg.org/#request-class + * + * @param Mixed input Url or Request instance + * @param Object init Custom options + * @return Void + */ +class Request extends Body { + constructor(input, init = {}) { + let parsedURL; + + // Normalize input and force URL to be encoded as UTF-8 (https://github.com/node-fetch/node-fetch/issues/245) + if (isRequest(input)) { + parsedURL = new URL(input.url); + } else { + parsedURL = new URL(input); + input = {}; + } + + if (parsedURL.username !== '' || parsedURL.password !== '') { + throw new TypeError(`${parsedURL} is an url with embedded credentials.`); + } + + let method = init.method || input.method || 'GET'; + if (/^(delete|get|head|options|post|put)$/i.test(method)) { + method = method.toUpperCase(); + } + + if ('data' in init) { + doBadDataWarn(); + } + + // eslint-disable-next-line no-eq-null, eqeqeq + if ((init.body != null || (isRequest(input) && input.body !== null)) && + (method === 'GET' || method === 'HEAD')) { + throw new TypeError('Request with GET/HEAD method cannot have body'); + } + + const inputBody = init.body ? + init.body : + (isRequest(input) && input.body !== null ? + clone(input) : + null); + + super(inputBody, { + size: init.size || input.size || 0 + }); + + const headers = new Headers(init.headers || input.headers || {}); + + if (inputBody !== null && !headers.has('Content-Type')) { + const contentType = extractContentType(inputBody, this); + if (contentType) { + headers.set('Content-Type', contentType); + } + } + + let signal = isRequest(input) ? + input.signal : + null; + if ('signal' in init) { + signal = init.signal; + } + + // eslint-disable-next-line no-eq-null, eqeqeq + if (signal != null && !isAbortSignal(signal)) { + throw new TypeError('Expected signal to be an instanceof AbortSignal or EventTarget'); + } + + // §5.4, Request constructor steps, step 15.1 + // eslint-disable-next-line no-eq-null, eqeqeq + let referrer = init.referrer == null ? input.referrer : init.referrer; + if (referrer === '') { + // §5.4, Request constructor steps, step 15.2 + referrer = 'no-referrer'; + } else if (referrer) { + // §5.4, Request constructor steps, step 15.3.1, 15.3.2 + const parsedReferrer = new URL(referrer); + // §5.4, Request constructor steps, step 15.3.3, 15.3.4 + referrer = /^about:(\/\/)?client$/.test(parsedReferrer) ? 'client' : parsedReferrer; + } else { + referrer = undefined; + } + + this[INTERNALS] = { + method, + redirect: init.redirect || input.redirect || 'follow', + headers, + parsedURL, + signal, + referrer + }; + + // Node-fetch-only options + this.follow = init.follow === undefined ? (input.follow === undefined ? 20 : input.follow) : init.follow; + this.compress = init.compress === undefined ? (input.compress === undefined ? true : input.compress) : init.compress; + this.counter = init.counter || input.counter || 0; + this.agent = init.agent || input.agent; + this.highWaterMark = init.highWaterMark || input.highWaterMark || 16384; + this.insecureHTTPParser = init.insecureHTTPParser || input.insecureHTTPParser || false; + + // §5.4, Request constructor steps, step 16. + // Default is empty string per https://fetch.spec.whatwg.org/#concept-request-referrer-policy + this.referrerPolicy = init.referrerPolicy || input.referrerPolicy || ''; + } + + /** @returns {string} */ + get method() { + return this[INTERNALS].method; + } + + /** @returns {string} */ + get url() { + return node_url.format(this[INTERNALS].parsedURL); + } + + /** @returns {Headers} */ + get headers() { + return this[INTERNALS].headers; + } + + get redirect() { + return this[INTERNALS].redirect; + } + + /** @returns {AbortSignal} */ + get signal() { + return this[INTERNALS].signal; + } + + // https://fetch.spec.whatwg.org/#dom-request-referrer + get referrer() { + if (this[INTERNALS].referrer === 'no-referrer') { + return ''; + } + + if (this[INTERNALS].referrer === 'client') { + return 'about:client'; + } + + if (this[INTERNALS].referrer) { + return this[INTERNALS].referrer.toString(); + } + + return undefined; + } + + get referrerPolicy() { + return this[INTERNALS].referrerPolicy; + } + + set referrerPolicy(referrerPolicy) { + this[INTERNALS].referrerPolicy = validateReferrerPolicy(referrerPolicy); + } + + /** + * Clone this request + * + * @return Request + */ + clone() { + return new Request(this); + } + + get [Symbol.toStringTag]() { + return 'Request'; + } +} + +Object.defineProperties(Request.prototype, { + method: {enumerable: true}, + url: {enumerable: true}, + headers: {enumerable: true}, + redirect: {enumerable: true}, + clone: {enumerable: true}, + signal: {enumerable: true}, + referrer: {enumerable: true}, + referrerPolicy: {enumerable: true} +}); + +/** + * Convert a Request to Node.js http request options. + * + * @param {Request} request - A Request instance + * @return The options object to be passed to http.request + */ +const getNodeRequestOptions = request => { + const {parsedURL} = request[INTERNALS]; + const headers = new Headers(request[INTERNALS].headers); + + // Fetch step 1.3 + if (!headers.has('Accept')) { + headers.set('Accept', '*/*'); + } + + // HTTP-network-or-cache fetch steps 2.4-2.7 + let contentLengthValue = null; + if (request.body === null && /^(post|put)$/i.test(request.method)) { + contentLengthValue = '0'; + } + + if (request.body !== null) { + const totalBytes = getTotalBytes(request); + // Set Content-Length if totalBytes is a number (that is not NaN) + if (typeof totalBytes === 'number' && !Number.isNaN(totalBytes)) { + contentLengthValue = String(totalBytes); + } + } + + if (contentLengthValue) { + headers.set('Content-Length', contentLengthValue); + } + + // 4.1. Main fetch, step 2.6 + // > If request's referrer policy is the empty string, then set request's referrer policy to the + // > default referrer policy. + if (request.referrerPolicy === '') { + request.referrerPolicy = DEFAULT_REFERRER_POLICY; + } + + // 4.1. Main fetch, step 2.7 + // > If request's referrer is not "no-referrer", set request's referrer to the result of invoking + // > determine request's referrer. + if (request.referrer && request.referrer !== 'no-referrer') { + request[INTERNALS].referrer = determineRequestsReferrer(request); + } else { + request[INTERNALS].referrer = 'no-referrer'; + } + + // 4.5. HTTP-network-or-cache fetch, step 6.9 + // > If httpRequest's referrer is a URL, then append `Referer`/httpRequest's referrer, serialized + // > and isomorphic encoded, to httpRequest's header list. + if (request[INTERNALS].referrer instanceof URL) { + headers.set('Referer', request.referrer); + } + + // HTTP-network-or-cache fetch step 2.11 + if (!headers.has('User-Agent')) { + headers.set('User-Agent', 'node-fetch'); + } + + // HTTP-network-or-cache fetch step 2.15 + if (request.compress && !headers.has('Accept-Encoding')) { + headers.set('Accept-Encoding', 'gzip, deflate, br'); + } + + let {agent} = request; + if (typeof agent === 'function') { + agent = agent(parsedURL); + } + + if (!headers.has('Connection') && !agent) { + headers.set('Connection', 'close'); + } + + // HTTP-network fetch step 4.2 + // chunked encoding is handled by Node.js + + const search = getSearch(parsedURL); + + // Pass the full URL directly to request(), but overwrite the following + // options: + const options = { + // Overwrite search to retain trailing ? (issue #776) + path: parsedURL.pathname + search, + // The following options are not expressed in the URL + method: request.method, + headers: headers[Symbol.for('nodejs.util.inspect.custom')](), + insecureHTTPParser: request.insecureHTTPParser, + agent + }; + + return { + /** @type {URL} */ + parsedURL, + options + }; +}; + +/** + * AbortError interface for cancelled requests + */ +class AbortError extends FetchBaseError { + constructor(message, type = 'aborted') { + super(message, type); + } +} + +/*! node-domexception. MIT License. Jimmy Wärting */ + +if (!globalThis.DOMException) { + try { + const { MessageChannel } = require('worker_threads'), + port = new MessageChannel().port1, + ab = new ArrayBuffer(); + port.postMessage(ab, [ab, ab]); + } catch (err) { + err.constructor.name === 'DOMException' && ( + globalThis.DOMException = err.constructor + ); + } +} + +/** + * Index.js + * + * a request API compatible with window.fetch + * + * All spec algorithm step numbers are based on https://fetch.spec.whatwg.org/commit-snapshots/ae716822cb3a61843226cd090eefc6589446c1d2/. + */ + +const supportedSchemas = new Set(['data:', 'http:', 'https:']); + +/** + * Fetch function + * + * @param {string | URL | import('./request').default} url - Absolute url or Request instance + * @param {*} [options_] - Fetch options + * @return {Promise} + */ +async function fetch$1(url, options_) { + return new Promise((resolve, reject) => { + // Build request object + const request = new Request(url, options_); + const {parsedURL, options} = getNodeRequestOptions(request); + if (!supportedSchemas.has(parsedURL.protocol)) { + throw new TypeError(`node-fetch cannot load ${url}. URL scheme "${parsedURL.protocol.replace(/:$/, '')}" is not supported.`); + } + + if (parsedURL.protocol === 'data:') { + const data = dataUriToBuffer(request.url); + const response = new Response(data, {headers: {'Content-Type': data.typeFull}}); + resolve(response); + return; + } + + // Wrap http.request into fetch + const send = (parsedURL.protocol === 'https:' ? https__default["default"] : http__default["default"]).request; + const {signal} = request; + let response = null; + + const abort = () => { + const error = new AbortError('The operation was aborted.'); + reject(error); + if (request.body && request.body instanceof Stream__default["default"].Readable) { + request.body.destroy(error); + } + + if (!response || !response.body) { + return; + } + + response.body.emit('error', error); + }; + + if (signal && signal.aborted) { + abort(); + return; + } + + const abortAndFinalize = () => { + abort(); + finalize(); + }; + + // Send request + const request_ = send(parsedURL.toString(), options); + + if (signal) { + signal.addEventListener('abort', abortAndFinalize); + } + + const finalize = () => { + request_.abort(); + if (signal) { + signal.removeEventListener('abort', abortAndFinalize); + } + }; + + request_.on('error', error => { + reject(new FetchError(`request to ${request.url} failed, reason: ${error.message}`, 'system', error)); + finalize(); + }); + + fixResponseChunkedTransferBadEnding(request_, error => { + if (response && response.body) { + response.body.destroy(error); + } + }); + + /* c8 ignore next 18 */ + if (process.version < 'v14') { + // Before Node.js 14, pipeline() does not fully support async iterators and does not always + // properly handle when the socket close/end events are out of order. + request_.on('socket', s => { + let endedWithEventsCount; + s.prependListener('end', () => { + endedWithEventsCount = s._eventsCount; + }); + s.prependListener('close', hadError => { + // if end happened before close but the socket didn't emit an error, do it now + if (response && endedWithEventsCount < s._eventsCount && !hadError) { + const error = new Error('Premature close'); + error.code = 'ERR_STREAM_PREMATURE_CLOSE'; + response.body.emit('error', error); + } + }); + }); + } + + request_.on('response', response_ => { + request_.setTimeout(0); + const headers = fromRawHeaders(response_.rawHeaders); + + // HTTP fetch step 5 + if (isRedirect(response_.statusCode)) { + // HTTP fetch step 5.2 + const location = headers.get('Location'); + + // HTTP fetch step 5.3 + let locationURL = null; + try { + locationURL = location === null ? null : new URL(location, request.url); + } catch { + // error here can only be invalid URL in Location: header + // do not throw when options.redirect == manual + // let the user extract the errorneous redirect URL + if (request.redirect !== 'manual') { + reject(new FetchError(`uri requested responds with an invalid redirect URL: ${location}`, 'invalid-redirect')); + finalize(); + return; + } + } + + // HTTP fetch step 5.5 + switch (request.redirect) { + case 'error': + reject(new FetchError(`uri requested responds with a redirect, redirect mode is set to error: ${request.url}`, 'no-redirect')); + finalize(); + return; + case 'manual': + // Nothing to do + break; + case 'follow': { + // HTTP-redirect fetch step 2 + if (locationURL === null) { + break; + } + + // HTTP-redirect fetch step 5 + if (request.counter >= request.follow) { + reject(new FetchError(`maximum redirect reached at: ${request.url}`, 'max-redirect')); + finalize(); + return; + } + + // HTTP-redirect fetch step 6 (counter increment) + // Create a new Request object. + const requestOptions = { + headers: new Headers(request.headers), + follow: request.follow, + counter: request.counter + 1, + agent: request.agent, + compress: request.compress, + method: request.method, + body: clone(request), + signal: request.signal, + size: request.size, + referrer: request.referrer, + referrerPolicy: request.referrerPolicy + }; + + // when forwarding sensitive headers like "Authorization", + // "WWW-Authenticate", and "Cookie" to untrusted targets, + // headers will be ignored when following a redirect to a domain + // that is not a subdomain match or exact match of the initial domain. + // For example, a redirect from "foo.com" to either "foo.com" or "sub.foo.com" + // will forward the sensitive headers, but a redirect to "bar.com" will not. + if (!isDomainOrSubdomain(request.url, locationURL)) { + for (const name of ['authorization', 'www-authenticate', 'cookie', 'cookie2']) { + requestOptions.headers.delete(name); + } + } + + // HTTP-redirect fetch step 9 + if (response_.statusCode !== 303 && request.body && options_.body instanceof Stream__default["default"].Readable) { + reject(new FetchError('Cannot follow redirect with body being a readable stream', 'unsupported-redirect')); + finalize(); + return; + } + + // HTTP-redirect fetch step 11 + if (response_.statusCode === 303 || ((response_.statusCode === 301 || response_.statusCode === 302) && request.method === 'POST')) { + requestOptions.method = 'GET'; + requestOptions.body = undefined; + requestOptions.headers.delete('content-length'); + } + + // HTTP-redirect fetch step 14 + const responseReferrerPolicy = parseReferrerPolicyFromHeader(headers); + if (responseReferrerPolicy) { + requestOptions.referrerPolicy = responseReferrerPolicy; + } + + // HTTP-redirect fetch step 15 + resolve(fetch$1(new Request(locationURL, requestOptions))); + finalize(); + return; + } + + default: + return reject(new TypeError(`Redirect option '${request.redirect}' is not a valid value of RequestRedirect`)); + } + } + + // Prepare response + if (signal) { + response_.once('end', () => { + signal.removeEventListener('abort', abortAndFinalize); + }); + } + + let body = Stream.pipeline(response_, new Stream.PassThrough(), error => { + if (error) { + reject(error); + } + }); + // see https://github.com/nodejs/node/pull/29376 + /* c8 ignore next 3 */ + if (process.version < 'v12.10') { + response_.on('aborted', abortAndFinalize); + } + + const responseOptions = { + url: request.url, + status: response_.statusCode, + statusText: response_.statusMessage, + headers, + size: request.size, + counter: request.counter, + highWaterMark: request.highWaterMark + }; + + // HTTP-network fetch step 12.1.1.3 + const codings = headers.get('Content-Encoding'); + + // HTTP-network fetch step 12.1.1.4: handle content codings + + // in following scenarios we ignore compression support + // 1. compression support is disabled + // 2. HEAD request + // 3. no Content-Encoding header + // 4. no content response (204) + // 5. content not modified response (304) + if (!request.compress || request.method === 'HEAD' || codings === null || response_.statusCode === 204 || response_.statusCode === 304) { + response = new Response(body, responseOptions); + resolve(response); + return; + } + + // For Node v6+ + // Be less strict when decoding compressed responses, since sometimes + // servers send slightly invalid responses that are still accepted + // by common browsers. + // Always using Z_SYNC_FLUSH is what cURL does. + const zlibOptions = { + flush: zlib__default["default"].Z_SYNC_FLUSH, + finishFlush: zlib__default["default"].Z_SYNC_FLUSH + }; + + // For gzip + if (codings === 'gzip' || codings === 'x-gzip') { + body = Stream.pipeline(body, zlib__default["default"].createGunzip(zlibOptions), error => { + if (error) { + reject(error); + } + }); + response = new Response(body, responseOptions); + resolve(response); + return; + } + + // For deflate + if (codings === 'deflate' || codings === 'x-deflate') { + // Handle the infamous raw deflate response from old servers + // a hack for old IIS and Apache servers + const raw = Stream.pipeline(response_, new Stream.PassThrough(), error => { + if (error) { + reject(error); + } + }); + raw.once('data', chunk => { + // See http://stackoverflow.com/questions/37519828 + if ((chunk[0] & 0x0F) === 0x08) { + body = Stream.pipeline(body, zlib__default["default"].createInflate(), error => { + if (error) { + reject(error); + } + }); + } else { + body = Stream.pipeline(body, zlib__default["default"].createInflateRaw(), error => { + if (error) { + reject(error); + } + }); + } + + response = new Response(body, responseOptions); + resolve(response); + }); + raw.once('end', () => { + // Some old IIS servers return zero-length OK deflate responses, so + // 'data' is never emitted. See https://github.com/node-fetch/node-fetch/pull/903 + if (!response) { + response = new Response(body, responseOptions); + resolve(response); + } + }); + return; + } + + // For br + if (codings === 'br') { + body = Stream.pipeline(body, zlib__default["default"].createBrotliDecompress(), error => { + if (error) { + reject(error); + } + }); + response = new Response(body, responseOptions); + resolve(response); + return; + } + + // Otherwise, use response as-is + response = new Response(body, responseOptions); + resolve(response); + }); + + // eslint-disable-next-line promise/prefer-await-to-then + writeToStream(request_, request).catch(reject); + }); +} + +function fixResponseChunkedTransferBadEnding(request, errorCallback) { + const LAST_CHUNK = node_buffer.Buffer.from('0\r\n\r\n'); + + let isChunkedTransfer = false; + let properLastChunkReceived = false; + let previousChunk; + + request.on('response', response => { + const {headers} = response; + isChunkedTransfer = headers['transfer-encoding'] === 'chunked' && !headers['content-length']; + }); + + request.on('socket', socket => { + const onSocketClose = () => { + if (isChunkedTransfer && !properLastChunkReceived) { + const error = new Error('Premature close'); + error.code = 'ERR_STREAM_PREMATURE_CLOSE'; + errorCallback(error); + } + }; + + const onData = buf => { + properLastChunkReceived = node_buffer.Buffer.compare(buf.slice(-5), LAST_CHUNK) === 0; + + // Sometimes final 0-length chunk and end of message code are in separate packets + if (!properLastChunkReceived && previousChunk) { + properLastChunkReceived = ( + node_buffer.Buffer.compare(previousChunk.slice(-3), LAST_CHUNK.slice(0, 3)) === 0 && + node_buffer.Buffer.compare(buf.slice(-2), LAST_CHUNK.slice(3)) === 0 + ); + } + + previousChunk = buf; + }; + + socket.prependListener('close', onSocketClose); + socket.on('data', onData); + + request.on('close', () => { + socket.removeListener('close', onSocketClose); + socket.removeListener('data', onData); + }); + }); +} + +// progressiveFetchHelper is the full progressiveFetch function, split out into +// a helper because the inptus/api is more complicated but only necessary for +// internal use. +function progressiveFetchHelper(pfm, resolve, verifyFunction) { + // If we run out of portals, return an error. + if (pfm.remainingPortals.length === 0) { + let newLog = "query failed because all portals have been tried\n" + JSON.stringify(pfm); + pfm.logs.push(newLog); + resolve({ + success: false, + portal: null, + response: null, + portalsFailed: pfm.portalsFailed, + responsesFailed: pfm.responsesFailed, + remainingPortals: null, + logs: pfm.logs, + }); + return; + } + // Grab the portal and query. + let portal = pfm.remainingPortals.shift(); + let query = portal + pfm.endpoint; + // Create a helper function for trying the next portal. + let nextPortal = function (response, log) { + if (response !== null) { + response + .clone() + .text() + .then((t) => { + pfm.logs.push(log); + pfm.portalsFailed.push(portal); + pfm.responsesFailed.push(response); + pfm.messagesFailed.push(t); + progressiveFetchHelper(pfm, resolve, verifyFunction); + }); + } + else { + pfm.logs.push(log); + pfm.portalsFailed.push(portal); + pfm.responsesFailed.push(response); + pfm.messagesFailed.push(""); + progressiveFetchHelper(pfm, resolve, verifyFunction); + } + }; + // Try sending the query to the portal. + fetch$1(query, pfm.fetchOpts) + .then((response) => { + // Check for a 5XX error. + if (!("status" in response) || typeof response.status !== "number") { + nextPortal(response, "portal has returned invalid response\n" + JSON.stringify({ portal, query })); + return; + } + if (response.status < 200 || response.status >= 300) { + nextPortal(response, "portal has returned error status\n" + JSON.stringify({ portal, query })); + return; + } + // Check the result against the verify function. + verifyFunction(response.clone()).then((errVF) => { + if (errVF !== null) { + nextPortal(response, "verify function has returned an error from portal " + portal + " - " + errVF); + return; + } + // Success! Return the response. + resolve({ + success: true, + portal, + response, + portalsFailed: pfm.portalsFailed, + responsesFailed: pfm.responsesFailed, + remainingPortals: pfm.remainingPortals, + messagesFailed: pfm.messagesFailed, + logs: pfm.logs, + }); + }); + }) + .catch((err) => { + // This portal failed, try again with the next portal. + nextPortal(null, "fetch returned an error\n" + JSON.stringify(err) + JSON.stringify(pfm.fetchOpts)); + return; + }); +} +// progressiveFetch will query multiple portals until one returns with a +// non-error response. In the event of a 4XX response, progressiveFetch will +// keep querying additional portals to try and find a working 2XX response. In +// the event that no working 2XX response is found, the first 4XX response will +// be returned. +// +// If progressiveFetch returns a 2XX response, it merely means that the portal +// returned a 2XX response. progressiveFetch cannot be confident that the +// portal has returned a correct/honest message, the verification has to be +// handled by the caller. The response (progressiveFetchResult) contains the +// list of portals that progressiveFetch hasn't tried yet. In the event that +// the 2XX response is not correct, the progressiveFetchResult contains the +// list of failover portals that have not been used yet, allowing +// progressiveFetch to be called again. +// +// This progressive method of querying portals helps prevent queries from +// failing, but if the first portal is not a good portal it introduces +// substantial latency. progressiveFetch does not do anything to make sure the +// portals are the best portals, it just queries them in order. The caller +// should make a best attempt to always have the best, most reliable and +// fastest portal as the first portal in the list. +// +// The reason that we don't blindly accept a 4XX response from a portal is that +// we have no way of verifying that the 4XX is legitimate. We don't trust the +// portal, and we can't give a rogue portal the opportunity to interrupt our +// user experience simply by returning a dishonest 404. So we need to keep +// querying more portals and gain confidence that the 404 a truthful response. +// +// TODO: Would be great if 'verifyFunction' could check the function signature +// of the function being passed in, I don't know how to do this. +function progressiveFetch(endpoint, fetchOpts, portals, verifyFunction) { + let portalsCopy = [...portals]; + return new Promise((resolve) => { + let pfm = { + endpoint, + fetchOpts, + remainingPortals: portalsCopy, + portalsFailed: [], + responsesFailed: [], + messagesFailed: [], + logs: [], + }; + progressiveFetchHelper(pfm, resolve, verifyFunction); + }); +} + +// readRegistryEntry will read and verify a registry entry. The tag strings +// will be hashed with the user's seed to produce the correct entropy. +function readRegistryEntry(pubkey, datakey) { + return new Promise((resolve, reject) => { + let pubkeyHex = bufToHex(pubkey); + let datakeyHex = bufToHex(datakey); + let endpoint = "/skynet/registry?publickey=ed25519%3A" + pubkeyHex + "&datakey=" + datakeyHex; + let verifyFunc = function (response) { + return verifyRegistryReadResponse(response, pubkey, datakey); + }; + progressiveFetch(endpoint, {}, defaultPortalList, verifyFunc).then((result) => { + // Check for a success. + if (result.success === true) { + result.response + .json() + .then((j) => { + resolve({ + exists: true, + data: j.data, + revision: BigInt(j.revision), + }); + }) + .catch((err) => { + reject(addContextToErr$1(err, "unable to parse response despite passing verification")); + }); + return; + } + // Check for 404. + for (let i = 0; i < result.responsesFailed.length; i++) { + if (result.responsesFailed[i].status === 404) { + resolve({ + exists: false, + data: new Uint8Array(0), + revision: 0n, + }); + return; + } + } + reject("unable to read registry entry\n" + JSON.stringify(result)); + }); + }); +} + +// verifyRegistryWrite checks that a response from the portal matches the write +// we attempted to perform. +function verifyRegistryWrite(response) { + return new Promise((resolve) => { + if (!("status" in response)) { + resolve("response did not contain a status"); + return; + } + if (response.status === 204) { + resolve(null); + return; + } + resolve("unrecognized status"); + }); +} +// overwriteRegistryEntry will obliterate an existing registry entry with a new +// value. This function does not have any data safety, and is only recommended +// for uses where the caller is not concerned about wiping existing data. +// Improper use of this function has caused a large number of developers to +// accidentally wipe critical user data, please avoid using this function for +// any sort of incremental data. +function overwriteRegistryEntry(keypair, datakey, data) { + return new Promise((resolve, reject) => { + // Check that the data is small enough to fit in a registry + // entry. The actual limit for a type 2 entry is 90 bytes, but + // we are leaving 4 bytes of room for potential extensions + // later. + if (data.length > 86) { + reject("provided data is too large to fit in a registry entry"); + return; + } + // Fetch the current registry entry so that we know the + // revision number. + // + // TODO: Need special error handling for max revision number, + // which probably also means we need to use bignums as the + // return type. + readRegistryEntry(keypair.publicKey, datakey) + .then((result) => { + let revisionNumber; + if (!result.exists) { + revisionNumber = 0n; + } + else { + revisionNumber = result.revision + 1n; + } + let [encodedRevision, errU64] = encodeU64$1(revisionNumber); + if (errU64 !== null) { + reject(addContextToErr$1(errU64, "unable to encode revision number")); + return; + } + // Compute the signature of the new registry entry. + let datakeyHex = bufToHex(datakey); + let [encodedData, errEPB] = encodePrefixedBytes(data); + if (errEPB !== null) { + reject(addContextToErr$1(errEPB, "unable to encode the registry data")); + return; + } + let dataToSign = new Uint8Array(32 + 8 + data.length + 8); + dataToSign.set(datakey, 0); + dataToSign.set(encodedData, 32); + dataToSign.set(encodedRevision, 32 + 8 + data.length); + let sigHash = blake2b(dataToSign); + let [sig, errS] = ed25519Sign(sigHash, keypair.secretKey); + if (errS !== null) { + reject(addContextToErr$1(errS, "unable to produce signature")); + return; + } + // Compose the registry entry query. + let postBody = { + publickey: { + algorithm: "ed25519", + key: Array.from(keypair.publicKey), + }, + datakey: datakeyHex, + revision: Number(revisionNumber), + data: Array.from(data), + signature: Array.from(sig), + }; + let fetchOpts = { + method: "post", + body: JSON.stringify(postBody), + }; + let endpoint = "/skynet/registry"; + // Perform the fetch call. + progressiveFetch(endpoint, fetchOpts, defaultPortalList, verifyRegistryWrite).then((result) => { + if (result.success === true) { + resolve(null); + return; + } + reject("unable to write registry entry\n" + JSON.stringify(result)); + }); + }) + .catch((err) => { + reject(addContextToErr$1(err, "unable to write registry entry")); + }); + }); +} + +// generateSeedPhraseRandom will randomly generate and verify a seed phrase for the user. +function generateSeedPhraseRandom() { + let buf = Uint8Array.from(require$$6.randomBytes(32)); + let str = bufToB64$1(buf); + let [sp, errGSPD] = generateSeedPhraseDeterministic(str); + if (errGSPD !== null) { + return ["", addContextToErr$1(errGSPD, "unable to generate seed from string")]; + } + return [sp, null]; +} + +// upload will upload the provided fileData to Skynet using the provided +// metadata and then return the resulting skylink. Upload is a secure function +// that computes the skylink of the upload locally, ensuring that the server +// cannot return a malicious skylink and convince a user to run modified code. +function upload$1(fileData, metadata) { + return new Promise((resolve, reject) => { + // Check that this is a small file. + if (fileData.length > 4 * 1000 * 1000) { + reject("currently only small uploads are supported, please use less than 4 MB"); + return; + } + // Encode the metadata after checking that it is valid. + let errVSM = validateSkyfileMetadata(metadata); + if (errVSM !== null) { + reject(addContextToErr$1(errVSM, "upload is using invalid metadata")); + return; + } + let metadataBytes = new TextEncoder().encode(JSON.stringify(metadata)); + // Build the layout of the skyfile. + let layoutBytes = new Uint8Array(99); + let offset = 0; + layoutBytes[offset] = 1; // Set the Version + offset += 1; + let [filesizeBytes, errU641] = encodeU64$1(BigInt(fileData.length)); + if (errU641 !== null) { + reject(addContextToErr$1(errU641, "unable to encode fileData length")); + return; + } + layoutBytes.set(filesizeBytes, offset); + offset += 8; + let [mdSizeBytes, errU642] = encodeU64$1(BigInt(metadataBytes.length)); + if (errU642 !== null) { + reject(addContextToErr$1(errU642, "unable to encode metadata bytes length")); + return; + } + layoutBytes.set(mdSizeBytes, offset); + offset += 8; + let [fanoutSizeBytes, errU643] = encodeU64$1(0n); + if (errU643 !== null) { + reject(addContextToErr$1(errU643, "unable to encode fanout bytes length")); + return; + } + layoutBytes.set(fanoutSizeBytes, offset); + offset += 8; + layoutBytes[offset] = 0; // Set the fanout data pieces + offset += 1; + layoutBytes[offset] = 0; // Set the fanout parity pieces + offset += 1; + layoutBytes[offset + 7] = 1; // Set the cipher type + offset += 8; + if (offset + 64 !== 99) { + reject("error when building the layout bytes, got wrong final offset"); + return; + } + // Build the base sector. + let totalSize = layoutBytes.length + metadataBytes.length + fileData.length; + if (totalSize > 1 << 22) { + reject("error when building the base sector: total sector is too large"); + return; + } + let baseSector = new Uint8Array(1 << 22); + offset = 0; + baseSector.set(layoutBytes, offset); + offset += layoutBytes.length; + baseSector.set(metadataBytes, offset); + offset += metadataBytes.length; + baseSector.set(fileData, offset); + // Compute the Skylink of this file. + let [sectorRoot, errBMR] = blake2bMerkleRoot(baseSector); + if (errBMR !== null) { + reject(addContextToErr$1(errBMR, "unable to create bitfield for skylink")); + return; + } + let skylinkBytes = new Uint8Array(34); + let [bitfield, errSV1B] = skylinkV1Bitfield(BigInt(totalSize)); + if (errSV1B !== null) { + reject(addContextToErr$1(errSV1B, "unable to create bitfield for skylink")); + return; + } + skylinkBytes.set(bitfield, 0); + skylinkBytes.set(sectorRoot, 2); + // Build the header for the upload call. + let header = new Uint8Array(92); + let [headerMetadataPrefix, errU644] = encodeU64$1(15n); + if (errU644 !== null) { + reject(addContextToErr$1(errU644, "unable to encode header metadata length")); + return; + } + let headerMetadata = new TextEncoder().encode("Skyfile Backup\n"); + let [versionPrefix, errU645] = encodeU64$1(7n); + if (errU645 !== null) { + reject(addContextToErr$1(errU645, "unable to encode version prefix length")); + return; + } + let version = new TextEncoder().encode("v1.5.5\n"); + let [skylinkPrefix, errU646] = encodeU64$1(46n); + if (errU646 !== null) { + reject(addContextToErr$1(errU646, "unable to encode skylink length")); + return; + } + let skylink = bufToB64$1(skylinkBytes); + offset = 0; + header.set(headerMetadataPrefix, offset); + offset += 8; + header.set(headerMetadata, offset); + offset += 15; + header.set(versionPrefix, offset); + offset += 8; + header.set(version, offset); + offset += 7; + header.set(skylinkPrefix, offset); + offset += 8; + header.set(new TextEncoder().encode(skylink), offset); + // Build the full request body. + let reqBody = new Uint8Array((1 << 22) + 92); + reqBody.set(header, 0); + reqBody.set(baseSector, 92); + // Call progressiveFetch to perform the upload. + let endpoint = "/skynet/restore"; + let fetchOpts = { + method: "post", + body: reqBody, + }; + // Establish the function that verifies the result is correct. + let verifyFunction = function (response) { + return new Promise((resolve) => { + response + .json() + .then((j) => { + if (!("skylink" in j)) { + resolve("response is missing the skylink field\n" + JSON.stringify(j)); + return; + } + if (j.skylink !== skylink) { + resolve("wrong skylink was returned, expecting " + skylink + " but got " + j.skylink); + return; + } + resolve(null); + }) + .catch((err) => { + resolve(addContextToErr$1(err, "unable to read response body")); + }); + }); + }; + progressiveFetch(endpoint, fetchOpts, defaultPortalList, verifyFunction).then((result) => { + result.response + .json() + .then((j) => { + resolve(j.skylink); + }) + .catch((err) => { + reject(addContextToErr$1(err, "unable to read response body, despite verification of response succeeding")); + }); + }); + }); +} + +var dist$1 = /*#__PURE__*/Object.freeze({ + __proto__: null, + overwriteRegistryEntry: overwriteRegistryEntry, + generateSeedPhraseRandom: generateSeedPhraseRandom, + upload: upload$1 +}); + +var require$$4 = /*@__PURE__*/getAugmentedNamespace(dist$1); + +// log provides a wrapper for console.log that prefixes '[libkernel]' to the +// output. +function log(...inputs) { + console.log("[libkernel]", ...inputs); +} +// logErr provides a wrapper for console.error that prefixes '[libkernel]' to +// the output. +function logErr(...inputs) { + console.error("[libkernel]", ...inputs); +} + +// tryStringify will try to turn the provided input into a string. If the input +// object is already a string, the input object will be returned. If the input +// object has a toString method, the toString method will be called. If that +// fails, we try to call JSON.stringify on the object. And if that fails, we +// set the return value to "[stringify failed]". +function tryStringify(obj) { + // Check for undefined input. + if (obj === undefined || obj === null) { + return "[cannot stringify undefined input]"; + } + // Parse the error into a string. + if (typeof obj === "string") { + return obj; + } + // Check if the object has a custom toString and use that if so. + let hasToString = typeof obj.toString === "function"; + if (hasToString && obj.toString !== Object.prototype.toString) { + return obj.toString(); + } + // If the object does not have a custom toString, attempt to perform a + // JSON.stringify. + try { + return JSON.stringify(obj); + } + catch { + return "[stringify failed]"; + } +} + +// addContextToErr is a helper function that standardizes the formatting of +// adding context to an error. Within the world of go we discovered that being +// persistent about layering context onto errors is helpful when debugging, +// even though it often creates rather verbose error messages. +// +// addContextToErr will return null if the input err is null. +// +// NOTE: To protect against accidental situations where an Error type or some +// other type is provided instead of a string, we wrap both of the inputs with +// tryStringify before returning them. This prevents runtime failures. +function addContextToErr(err, context) { + if (err === null) { + err = "[no error provided]"; + } + return tryStringify(context) + ": " + tryStringify(err); +} +// composeErr takes a series of inputs and composes them into a single string. +// Each element will be separated by a newline. If the input is not a string, +// it will be transformed into a string with JSON.stringify. +// +// Any object that cannot be stringified will be skipped, though an error will +// be logged. +function composeErr(...inputs) { + let result = ""; + let resultEmpty = true; + for (let i = 0; i < inputs.length; i++) { + if (inputs[i] === null) { + continue; + } + if (resultEmpty) { + resultEmpty = false; + } + else { + result += "\n"; + } + result += tryStringify(inputs[i]); + } + if (resultEmpty) { + return null; + } + return result; +} + +// Helper consts to make it easy to return empty values alongside errors. +const nu8 = new Uint8Array(0); +// bufToB64 will convert a Uint8Array to a base64 string with URL encoding and +// no padding characters. +function bufToB64(buf) { + let b64Str = btoa(String.fromCharCode.apply(null, buf)); + return b64Str.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, ""); +} +// encodeU64 will encode a bigint in the range of a uint64 to an 8 byte +// Uint8Array. +function encodeU64(num) { + // Check the bounds on the bigint. + if (num < 0) { + return [nu8, "expected a positive integer"]; + } + if (num > 18446744073709551615n) { + return [nu8, "expected a number no larger than a uint64"]; + } + // Encode the bigint into a Uint8Array. + let encoded = new Uint8Array(8); + for (let i = 0; i < encoded.length; i++) { + let byte = Number(num & 0xffn); + encoded[i] = byte; + num = num >> 8n; + } + return [encoded, null]; +} + +let gfi = function (init) { + let i, r = new Float64Array(16); + if (init) + for (i = 0; i < init.length; i++) + r[i] = init[i]; + return r; +}; +gfi([1]); gfi([ + 0x78a3, 0x1359, 0x4dca, 0x75eb, 0xd8ab, 0x4141, 0x0a4d, 0x0070, 0xe898, 0x7779, 0x4079, 0x8cc7, 0xfe73, 0x2b6f, + 0x6cee, 0x5203, +]); gfi([ + 0xf159, 0x26b2, 0x9b94, 0xebd6, 0xb156, 0x8283, 0x149a, 0x00e0, 0xd130, 0xeef3, 0x80f2, 0x198e, 0xfce7, 0x56df, + 0xd9dc, 0x2406, +]); gfi([ + 0xd51a, 0x8f25, 0x2d60, 0xc956, 0xa7b2, 0x9525, 0xc760, 0x692c, 0xdc5c, 0xfdd6, 0xe231, 0xc0a4, 0x53fe, 0xcd6e, + 0x36d3, 0x2169, +]); gfi([ + 0x6658, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, + 0x6666, 0x6666, +]); gfi([ + 0xa0b0, 0x4a0e, 0x1b27, 0xc4ee, 0xe478, 0xad2f, 0x1806, 0x2f43, 0xd7a7, 0x3dfb, 0x0099, 0x2b4d, 0xdf0b, 0x4fc1, + 0x2480, 0x2b83, +]); + +// Create the queryMap. +let queries = {}; +// Define the nonce handling. nonceSeed is 16 random bytes that get generated +// at init and serve as the baseline for creating random nonces. nonceCounter +// tracks which messages have been sent. We hash together the nonceSeed and the +// current nonceCounter to get a secure nonce. +// +// We need a secure nonce so that we know which messages from the kernel are +// intended for us. There could be multiple pieces of independent code talking +// to the kernel and using nonces, by having secure random nonces we can +// guarantee that the applications will not use conflicting nonces. +let nonceSeed; +let nonceCounter; +function initNonce() { + nonceSeed = new Uint8Array(16); + nonceCounter = 0; + crypto.getRandomValues(nonceSeed); +} +// nextNonce will combine the nonceCounter with the nonceSeed to produce a +// unique string that can be used as the nonce with the kernel. +// +// Note: the nonce is only ever going to be visible to the kernel and to other +// code running in the same webpage, so we don't need to hash our nonceSeed. We +// just need it to be unique, not undetectable. +function nextNonce() { + let nonceNum = nonceCounter; + nonceCounter += 1; + let [nonceNumBytes, err] = encodeU64(BigInt(nonceNum)); + if (err !== null) { + // encodeU64 only fails if nonceNum is outside the bounds of a + // uint64, which shouldn't happen ever. + logErr("encodeU64 somehow failed", err); + } + let noncePreimage = new Uint8Array(nonceNumBytes.length + nonceSeed.length); + noncePreimage.set(nonceNumBytes, 0); + noncePreimage.set(nonceSeed, nonceNumBytes.length); + return bufToB64(noncePreimage); +} +// Establish the handler for incoming messages. +function handleMessage(event) { + // Ignore all messages that aren't from approved kernel sources. The two + // approved sources are skt.us and the browser extension bridge (which has + // an event.source equal to 'window') + if (event.source !== window && event.origin !== "https://skt.us") { + return; + } + // Ignore any messages that don't have a method and data field. + if (!("method" in event.data) || !("data" in event.data)) { + return; + } + // Handle logging messages. + if (event.data.method === "log") { + // We display the logging message if the kernel is a browser + // extension, so that the kernel's logs appear in the app + // console as well as the extension console. If the kernel is + // in an iframe, its logging messages will already be in the + // app console and therefore don't need to be displayed. + if (kernelOrigin === window.origin) { + if (event.data.data.isErr) { + console.error(event.data.data.message); + } + else { + console.log(event.data.data.message); + } + } + return; + } + // init is complete when the kernel sends us the auth status. If the + // user is logged in, report success, otherwise return an error + // indicating that the user is not logged in. + if (event.data.method === "kernelAuthStatus") { + // If we have received an auth status message, it means the bootloader + // at a minimum is working. + if (initResolved === false) { + initResolved = true; + initResolve(); + } + // If the auth status message says that login is complete, it means + // that the user is logged in. + if (loginResolved === false && event.data.data.loginComplete === true) { + loginResolved = true; + loginResolve(); + } + // If the auth status message says that the kernel loaded, it means + // that the kernel is ready to receive messages. + if (kernelLoadedResolved === false && event.data.data.kernelLoaded !== "not yet") { + kernelLoadedResolved = true; + if (event.data.data.kernelLoaded === "success") { + kernelLoadedResolve(null); + } + else { + kernelLoadedResolve(event.data.data.kernelLoaded); + } + } + // If we have received a message indicating that the user has logged + // out, we need to reload the page and reset the auth process. + if (event.data.data.logoutComplete === true) { + { + logoutResolve(); + } + window.location.reload(); + } + return; + } + // Check that the message sent has a nonce. We don't log + // on failure because the message may have come from 'window', which + // will happen if the app has other messages being sent to the window. + if (!("nonce" in event.data)) { + return; + } + // If we can't locate the nonce in the queries map, there is nothing to do. + // This can happen especially for responseUpdate messages. + if (!(event.data.nonce in queries)) { + return; + } + let query = queries[event.data.nonce]; + // Handle a response. Once the response has been received, it is safe to + // delete the query from the queries map. + if (event.data.method === "response") { + queries[event.data.nonce].resolve([event.data.data, event.data.err]); + delete queries[event.data.nonce]; + return; + } + // Handle a response update. + if (event.data.method === "responseUpdate") { + // If no update handler was provided, there is nothing to do. + if (typeof query.receiveUpdate === "function") { + query.receiveUpdate(event.data.data); + } + return; + } + // Handle a responseNonce. + if (event.data.method === "responseNonce") { + if (typeof query.kernelNonceReceived === "function") { + query.kernelNonceReceived(event.data.data.nonce); + } + return; + } + // Ignore any other messages as they might be from other applications. +} +// launchKernelFrame will launch the skt.us iframe that is used to connect to the +// Skynet kernel if the kernel cannot be reached through the browser extension. +function launchKernelFrame() { + let iframe = document.createElement("iframe"); + iframe.src = "https://skt.us"; + iframe.width = "0"; + iframe.height = "0"; + iframe.style.border = "0"; + iframe.style.position = "absolute"; + document.body.appendChild(iframe); + kernelSource = iframe.contentWindow; + kernelOrigin = "https://skt.us"; + kernelAuthLocation = "https://skt.us/auth.html"; + // Set a timer to fail the login process if the kernel doesn't load in + // time. + setTimeout(() => { + if (initResolved === true) { + return; + } + initResolved = true; + initResolve("tried to open kernel in iframe, but hit a timeout"); + }, 24000); +} +// messageBridge will send a message to the bridge of the skynet extension to +// see if it exists. If it does not respond or if it responds with an error, +// messageBridge will open an iframe to skt.us and use that as the kernel. +let kernelSource; +let kernelOrigin; +let kernelAuthLocation; +function messageBridge() { + // Establish the function that will handle the bridge's response. + let bridgeInitComplete = false; + let bridgeResolve = () => { }; // Need to set bridgeResolve here to make tsc happy + let p = new Promise((resolve) => { + bridgeResolve = resolve; + }); + p.then(([, err]) => { + // Check if the timeout already elapsed. + if (bridgeInitComplete === true) { + logErr("received response from bridge, but init already finished"); + return; + } + bridgeInitComplete = true; + // Deconstruct the input and return if there's an error. + if (err !== null) { + logErr("bridge exists but returned an error", err); + launchKernelFrame(); + return; + } + // Bridge has responded successfully, and there's no error. + kernelSource = window; + kernelOrigin = window.origin; + kernelAuthLocation = "http://kernel.skynet/auth.html"; + console.log("established connection to bridge, using browser extension for kernel"); + }); + // Add the handler to the queries map. + let nonce = nextNonce(); + queries[nonce] = { + resolve: bridgeResolve, + }; + // Send a message to the bridge of the browser extension to determine + // whether the bridge exists. + window.postMessage({ + nonce, + method: "kernelBridgeVersion", + }, window.origin); + // Set a timeout, if we do not hear back from the bridge in 500 + // milliseconds we assume that the bridge is not available. + setTimeout(() => { + // If we've already received and processed a message from the + // bridge, there is nothing to do. + if (bridgeInitComplete === true) { + return; + } + bridgeInitComplete = true; + log("browser extension not found, falling back to skt.us"); + launchKernelFrame(); + }, 500); + return initPromise; +} +// init is a function that returns a promise which will resolve when +// initialization is complete. +// +// The init / auth process has 5 stages. The first stage is that something +// somewhere needs to call init(). It is safe to call init() multiple times, +// thanks to the 'initialized' variable. +let initialized = false; // set to true once 'init()' has been called +let initResolved = false; // set to true once we know the bootloader is working +let initResolve; +let initPromise; +let loginResolved = false; // set to true once we know the user is logged in +let loginResolve; +let loginPromise; +let kernelLoadedResolved = false; // set to true once the user kernel is loaded +let kernelLoadedResolve; +let kernelLoadedPromise; +let logoutResolve; +let logoutPromise; +function init() { + // If init has already been called, just return the init promise. + if (initialized === true) { + return initPromise; + } + initialized = true; + // Run all of the init functions. + initNonce(); + window.addEventListener("message", handleMessage); + messageBridge(); + // Create the promises that resolve at various stages of the auth flow. + initPromise = new Promise((resolve) => { + initResolve = resolve; + }); + loginPromise = new Promise((resolve) => { + loginResolve = resolve; + }); + kernelLoadedPromise = new Promise((resolve) => { + kernelLoadedResolve = resolve; + }); + logoutPromise = new Promise((resolve) => { + logoutResolve = resolve; + }); + // Return the initPromise, which will resolve when bootloader init is + // complete. + return initPromise; +} +// callModule is a generic function to call a module. The first input is the +// module identifier (typically a skylink), the second input is the method +// being called on the module, and the final input is optional and contains +// input data to be passed to the module. The input data will depend on the +// module and the method that is being called. The return value is an errTuple +// that contains the module's response. The format of the response is an +// arbitrary object whose fields depend on the module and method being called. +// +// callModule can only be used for query-response communication, there is no +// support for sending or receiving updates. +function callModule(module, method, data) { + let moduleCallData = { + module, + method, + data, + }; + let [, query] = newKernelQuery("moduleCall", moduleCallData, false); + return query; +} +// connectModule is the standard function to send a query to a module that can +// optionally send and optionally receive updates. The first three inputs match +// the inputs of 'callModule', and the fourth input is a function that will be +// called any time that the module sends a responseUpdate. The receiveUpdate +// function should have the following signature: +// +// `function receiveUpdate(data: any)` +// +// The structure of the data will depend on the module and method that was +// queried. +// +// The first return value is a 'sendUpdate' function that can be called to send +// a queryUpdate to the module. The sendUpdate function has the same signature +// as the receiveUpdate function, it's an arbitrary object whose fields depend +// on the module and method being queried. +// +// The second return value is a promise that returns an errTuple. It will +// resolve when the module sends a response message, and works the same as the +// return value of callModule. +function connectModule(module, method, data, receiveUpdate) { + let moduleCallData = { + module, + method, + data, + }; + return newKernelQuery("moduleCall", moduleCallData, true, receiveUpdate); +} +// newKernelQuery opens a query to the kernel. Details like postMessage +// communication and nonce handling are all abstracted away by newKernelQuery. +// +// The first arg is the method that is being called on the kernel, and the +// second arg is the data that will be sent to the kernel as input to the +// method. +// +// The thrid arg is an optional function that can be passed in to receive +// responseUpdates to the query. Not every query will send responseUpdates, and +// most responseUpdates can be ignored, but sometimes contain useful +// information like download progress. +// +// The first output is a 'sendUpdate' function that can be called to send a +// queryUpdate. The second output is a promise that will resolve when the query +// receives a response message. Once the response message has been received, no +// more updates can be sent or received. +function newKernelQuery(method, data, sendUpdates, receiveUpdate) { + // NOTE: The implementation here is gnarly, because I didn't want to use + // async/await (that decision should be left to the caller) and I also + // wanted this function to work correctly even if init() had not been + // called yet. + // + // This function returns a sendUpdate function along with a promise, so we + // can't simply wrap everything in a basic promise. The sendUpdate function + // has to block internally until all of the setup is complete, and then we + // can't send a query until all of the setup is complete, and the setup + // cylce has multiple dependencies and therefore we get a few promises that + // all depend on each other. + // + // Using async/await here actually breaks certain usage patterns (or at + // least makes them much more difficult to use correctly). The standard way + // to establish duplex communication using connectModule is to define a + // variable 'sendUpdate' before defining the function 'receiveUpdate', and + // then setting 'sendUpdate' equal to the first return value of + // 'connectModue'. It looks like this: + // + // let sendUpdate; + // let receiveUpdate = function(data: any) { + // if (data.needsUpdate) { + // sendUpdate(someUpdate) + // } + // } + // let [sendUpdateFn, response] = connectModule(x, y, z, receiveUpdate) + // sendUpdate = sendUpdateFn + // + // If we use async/await, it's not safe to set sendUpdate after + // connectModule returns because 'receiveUpdate' may be called before + // 'sendUpdate' is set. You can fix that by using a promise, but it's a + // complicated fix and we want this library to be usable by less + // experienced developers. + // + // Therefore, we make an implementation tradeoff here and avoid async/await + // at the cost of having a bunch of complicated promise chaining. + // Create a promise that will resolve once the nonce is available. We + // cannot get the nonce until init() is complete. getNonce therefore + // implies that init is complete. + let getNonce = new Promise((resolve) => { + init().then(() => { + kernelLoadedPromise.then(() => { + resolve(nextNonce()); + }); + }); + }); + // Two promises are being created at once here. Once is 'p', which will be + // returned to the caller of newKernelQuery and will be resolved when the + // kernel provides a 'response' message. The other is for internal use and + // will resolve once the query has been created. + let p; + let queryCreated; + let haveQueryCreated = new Promise((resolve) => { + queryCreated = resolve; + p = new Promise((resolve) => { + getNonce.then((nonce) => { + queries[nonce] = { resolve }; + if (receiveUpdate !== null && receiveUpdate !== undefined) { + queries[nonce]["receiveUpdate"] = receiveUpdate; + } + queryCreated(nonce); + }); + }); + }); + // Create a promise that will be resolved once we are ready to receive the + // kernelNonce. We won't be ready to receive the kernel nonce until after + // the queries[nonce] object has been created. + let readyForKernelNonce; + let getReadyForKernelNonce = new Promise((resolve) => { + readyForKernelNonce = resolve; + }); + // Create the sendUpdate function. It defaults to doing nothing. After the + // sendUpdate function is ready to receive the kernelNonce, resolve the + // promise that blocks until the sendUpdate function is ready to receive + // the kernel nonce. + let sendUpdate; + if (sendUpdates !== true) { + sendUpdate = () => { }; + readyForKernelNonce(); // We won't get a kernel nonce, no reason to block. + } + else { + // sendUpdate will send an update to the kernel. The update can't be + // sent until the kernel nonce is known. Create a promise that will + // resolve when the kernel nonce is known. + // + // This promise cannot itself be created until the queries[nonce] + // object has been created, so block for the query to be created. + let blockForKernelNonce = new Promise((resolve) => { + haveQueryCreated.then((nonce) => { + queries[nonce]["kernelNonceReceived"] = resolve; + readyForKernelNonce(); + }); + }); + // The sendUpdate function needs both the local nonce and also the + // kernel nonce. Block for both. Having the kernel nonce implies that + // the local nonce is ready, therefore start by blocking for the kernel + // nonce. + sendUpdate = function (updateData) { + blockForKernelNonce.then((nonce) => { + kernelSource.postMessage({ + method: "queryUpdate", + nonce, + data: updateData, + }, kernelOrigin); + }); + }; + } + // Prepare to send the query to the kernel. The query cannot be sent until + // the queries object is created and also we are ready to receive the + // kernel nonce. + haveQueryCreated.then((nonce) => { + getReadyForKernelNonce.then(() => { + // There are two types of messages we can send depending on whether + // we are talking to skt.us or the background script. + let kernelMessage = { + method, + nonce, + data, + sendKernelNonce: sendUpdates, + }; + let backgroundMessage = { + method: "newKernelQuery", + nonce, + data: kernelMessage, + }; + // The message structure needs to adjust based on whether we are + // talking directly to the kernel or whether we are talking to the + // background page. + if (kernelOrigin === "https://skt.us") { + kernelSource.postMessage(kernelMessage, kernelOrigin); + } + else { + kernelSource.postMessage(backgroundMessage, kernelOrigin); + } + }); + }); + // Return sendUpdate and the promise. sendUpdate is already set to block + // until all the necessary prereqs are complete. + return [sendUpdate, p]; +} + +// There are 5 stages of auth. +// +// Stage 0: Bootloader is not loaded. +// Stage 1: Bootloader is loaded, user is not logged in. +// Stage 2: Bootloader is loaded, user is logged in. +// Stage 3: Kernel is loaded, user is logged in. +// Stage 4: Kernel is loaded, user is logged out. +// +// init() will block until auth has reached stage 1. If the user is already +// logged in from a previous session, auth will immediately progress to stage +// 2. +// +// loginComplete() will block until auth has reached stage 2. The kernel is not +// ready to receive messages yet, but apps do not need to present users with a +// login dialog. +// +// kernelLoaded() will block until auth has reached stage 3. kernelLoaded() +// returns a promise that can resolve with an error. If there was an error, it +// means the kernel could not be loaded and cannot be used. +// +// logoutComplete() will block until auth has reached stage 4. libkernel does +// not support resetting the auth stages, once stage 4 has been reached the app +// needs to refresh. +// kernelLoaded will resolve when the user has successfully loaded the kernel. +// If there was an error in loading the kernel, the error will be returned. +// +// NOTE: kernelLoaded will not resolve until after loginComplete has resolved. +function kernelLoaded() { + return kernelLoadedPromise; +} +// loginComplete will resolve when the user has successfully logged in. +function loginComplete() { + return loginPromise; +} +// logoutComplete will resolve when the user has logged out. Note that +// logoutComplete will only resolve if the user logged in first - if the user +// was not logged in to begin with, this promise will not resolve. +function logoutComplete() { + return logoutPromise; +} +// openAuthWindow is intended to be used as an onclick target when the user +// clicks the 'login' button on a skynet application. It will block until the +// auth location is known, and then it will pop open the correct auth window +// for the user. +// +// NOTE: openAuthWindow will only open a window if the user is not already +// logged in. If the user is already logged in, this function is a no-op. +// +// NOTE: When using this function, you probably want to have your login button +// faded out or presenting the user with a spinner until init() resolves. In +// the worst case (user has no browser extension, and is on a slow internet +// connection) this could take multiple seconds. +function openAuthWindow() { + // openAuthWindow doesn't care what the auth status is, it's just trying to + // open the right window. + init().then(() => { + window.open(kernelAuthLocation, "_blank"); + }); +} + +// download will take a skylink and return the file data for that skylink. The +// download occurs using a kernel module that verifies the data's integrity and +// prevents the portal from lying about the download. +function download(skylink) { + return new Promise((resolve) => { + let downloadModule = "AQCIaQ0P-r6FwPEDq3auCZiuH_jqrHfqRcY7TjZ136Z_Yw"; + let data = { + skylink, + }; + callModule(downloadModule, "secureDownload", data).then(([result, err]) => { + // Pull the fileData out of the result. + if (err !== null) { + resolve([new Uint8Array(0), addContextToErr(err, "unable to complete download")]); + return; + } + resolve([result.fileData, null]); + }); + }); +} + +// registryRead will perform a registry read on a portal. readEntry does not +// guarantee that the latest revision has been provided, however it does +// guarantee that the provided data has a matching signature. +// +// registryRead returns the full registry entry object provided by the module +// because the object is relatively complex and all of the fields are more or +// less required. +function registryRead(publicKey, dataKey) { + let registryModule = "AQCovesg1AXUzKXLeRzQFILbjYMKr_rvNLsNhdq5GbYb2Q"; + let data = { + publicKey, + dataKey, + }; + return callModule(registryModule, "readEntry", data); +} +// registryWrite will perform a registry write on a portal. +// +// registryWrite is not considered a safe function, there are easy ways to +// misuse registryWrite such that user data will be lost. We recommend using a +// safe set of functions for writing to the registry such as getsetjson. +function registryWrite(keypair, dataKey, entryData, revision) { + return new Promise((resolve) => { + let registryModule = "AQCovesg1AXUzKXLeRzQFILbjYMKr_rvNLsNhdq5GbYb2Q"; + let callData = { + publicKey: keypair.publicKey, + secretKey: keypair.secretKey, + dataKey, + entryData, + revision, + }; + callModule(registryModule, "writeEntry", callData).then(([result, err]) => { + if (err !== null) { + resolve(["", err]); + return; + } + resolve([result.entryID, null]); + }); + }); +} + +// upload will take a filename and some file data and perform a secure upload +// to Skynet. All data is verified and the correct Skylink is returned. This +// function cannot fully guarantee that the data was pinned, but it can fully +// guarantee that the final skylink matches the data that was presented for the +// upload. +function upload(filename, fileData) { + return new Promise((resolve) => { + // Prepare the module call. + let uploadModule = "AQAT_a0MzOInZoJzt1CwBM2U8oQ3GIfP5yKKJu8Un-SfNg"; + let data = { + filename, + fileData, + }; + callModule(uploadModule, "secureUpload", data).then(([result, err]) => { + // Pull the skylink out of the result. + if (err !== null) { + resolve(["", addContextToErr(err, "uable to complete upload")]); + return; + } + resolve([result.skylink, null]); + }); + }); +} + +// kernelVersion will fetch the version number of the kernel. If successful, +// the returned value will be an object containing a field 'version' with a +// version string, and a 'distribtion' field with a string that states the +// distribution of the kernel". +function kernelVersion() { + return new Promise((resolve) => { + let [, query] = newKernelQuery("version", {}, false); + query.then(([result, err]) => { + if (err !== null) { + resolve(["", "", err]); + return; + } + resolve([result.version, result.distribution, err]); + }); + }); +} + +var dist = /*#__PURE__*/Object.freeze({ + __proto__: null, + kernelLoaded: kernelLoaded, + loginComplete: loginComplete, + logoutComplete: logoutComplete, + openAuthWindow: openAuthWindow, + download: download, + registryRead: registryRead, + registryWrite: registryWrite, + upload: upload, + kernelVersion: kernelVersion, + callModule: callModule, + connectModule: connectModule, + init: init, + newKernelQuery: newKernelQuery, + addContextToErr: addContextToErr, + composeErr: composeErr +}); + +var require$$5 = /*@__PURE__*/getAugmentedNamespace(dist); + +var require$$2 = { + "application/andrew-inset": [ + "ez" +], + "application/applixware": [ + "aw" +], + "application/atom+xml": [ + "atom" +], + "application/atomcat+xml": [ + "atomcat" +], + "application/atomsvc+xml": [ + "atomsvc" +], + "application/bdoc": [ + "bdoc" +], + "application/ccxml+xml": [ + "ccxml" +], + "application/cdmi-capability": [ + "cdmia" +], + "application/cdmi-container": [ + "cdmic" +], + "application/cdmi-domain": [ + "cdmid" +], + "application/cdmi-object": [ + "cdmio" +], + "application/cdmi-queue": [ + "cdmiq" +], + "application/cu-seeme": [ + "cu" +], + "application/dash+xml": [ + "mpd" +], + "application/davmount+xml": [ + "davmount" +], + "application/docbook+xml": [ + "dbk" +], + "application/dssc+der": [ + "dssc" +], + "application/dssc+xml": [ + "xdssc" +], + "application/ecmascript": [ + "ecma" +], + "application/emma+xml": [ + "emma" +], + "application/epub+zip": [ + "epub" +], + "application/exi": [ + "exi" +], + "application/font-tdpfr": [ + "pfr" +], + "application/font-woff": [ +], + "application/font-woff2": [ +], + "application/geo+json": [ + "geojson" +], + "application/gml+xml": [ + "gml" +], + "application/gpx+xml": [ + "gpx" +], + "application/gxf": [ + "gxf" +], + "application/gzip": [ + "gz" +], + "application/hyperstudio": [ + "stk" +], + "application/inkml+xml": [ + "ink", + "inkml" +], + "application/ipfix": [ + "ipfix" +], + "application/java-archive": [ + "jar", + "war", + "ear" +], + "application/java-serialized-object": [ + "ser" +], + "application/java-vm": [ + "class" +], + "application/javascript": [ + "js", + "mjs" +], + "application/json": [ + "json", + "map" +], + "application/json5": [ + "json5" +], + "application/jsonml+json": [ + "jsonml" +], + "application/ld+json": [ + "jsonld" +], + "application/lost+xml": [ + "lostxml" +], + "application/mac-binhex40": [ + "hqx" +], + "application/mac-compactpro": [ + "cpt" +], + "application/mads+xml": [ + "mads" +], + "application/manifest+json": [ + "webmanifest" +], + "application/marc": [ + "mrc" +], + "application/marcxml+xml": [ + "mrcx" +], + "application/mathematica": [ + "ma", + "nb", + "mb" +], + "application/mathml+xml": [ + "mathml" +], + "application/mbox": [ + "mbox" +], + "application/mediaservercontrol+xml": [ + "mscml" +], + "application/metalink+xml": [ + "metalink" +], + "application/metalink4+xml": [ + "meta4" +], + "application/mets+xml": [ + "mets" +], + "application/mods+xml": [ + "mods" +], + "application/mp21": [ + "m21", + "mp21" +], + "application/mp4": [ + "mp4s", + "m4p" +], + "application/msword": [ + "doc", + "dot" +], + "application/mxf": [ + "mxf" +], + "application/octet-stream": [ + "bin", + "dms", + "lrf", + "mar", + "so", + "dist", + "distz", + "pkg", + "bpk", + "dump", + "elc", + "deploy", + "exe", + "dll", + "deb", + "dmg", + "iso", + "img", + "msi", + "msp", + "msm", + "buffer" +], + "application/oda": [ + "oda" +], + "application/oebps-package+xml": [ + "opf" +], + "application/ogg": [ + "ogx" +], + "application/omdoc+xml": [ + "omdoc" +], + "application/onenote": [ + "onetoc", + "onetoc2", + "onetmp", + "onepkg" +], + "application/oxps": [ + "oxps" +], + "application/patch-ops-error+xml": [ + "xer" +], + "application/pdf": [ + "pdf" +], + "application/pgp-encrypted": [ + "pgp" +], + "application/pgp-signature": [ + "asc", + "sig" +], + "application/pics-rules": [ + "prf" +], + "application/pkcs10": [ + "p10" +], + "application/pkcs7-mime": [ + "p7m", + "p7c" +], + "application/pkcs7-signature": [ + "p7s" +], + "application/pkcs8": [ + "p8" +], + "application/pkix-attr-cert": [ + "ac" +], + "application/pkix-cert": [ + "cer" +], + "application/pkix-crl": [ + "crl" +], + "application/pkix-pkipath": [ + "pkipath" +], + "application/pkixcmp": [ + "pki" +], + "application/pls+xml": [ + "pls" +], + "application/postscript": [ + "ai", + "eps", + "ps" +], + "application/prs.cww": [ + "cww" +], + "application/pskc+xml": [ + "pskcxml" +], + "application/raml+yaml": [ + "raml" +], + "application/rdf+xml": [ + "rdf" +], + "application/reginfo+xml": [ + "rif" +], + "application/relax-ng-compact-syntax": [ + "rnc" +], + "application/resource-lists+xml": [ + "rl" +], + "application/resource-lists-diff+xml": [ + "rld" +], + "application/rls-services+xml": [ + "rs" +], + "application/rpki-ghostbusters": [ + "gbr" +], + "application/rpki-manifest": [ + "mft" +], + "application/rpki-roa": [ + "roa" +], + "application/rsd+xml": [ + "rsd" +], + "application/rss+xml": [ + "rss" +], + "application/rtf": [ + "rtf" +], + "application/sbml+xml": [ + "sbml" +], + "application/scvp-cv-request": [ + "scq" +], + "application/scvp-cv-response": [ + "scs" +], + "application/scvp-vp-request": [ + "spq" +], + "application/scvp-vp-response": [ + "spp" +], + "application/sdp": [ + "sdp" +], + "application/set-payment-initiation": [ + "setpay" +], + "application/set-registration-initiation": [ + "setreg" +], + "application/shf+xml": [ + "shf" +], + "application/smil+xml": [ + "smi", + "smil" +], + "application/sparql-query": [ + "rq" +], + "application/sparql-results+xml": [ + "srx" +], + "application/srgs": [ + "gram" +], + "application/srgs+xml": [ + "grxml" +], + "application/sru+xml": [ + "sru" +], + "application/ssdl+xml": [ + "ssdl" +], + "application/ssml+xml": [ + "ssml" +], + "application/tei+xml": [ + "tei", + "teicorpus" +], + "application/thraud+xml": [ + "tfi" +], + "application/timestamped-data": [ + "tsd" +], + "application/vnd.3gpp.pic-bw-large": [ + "plb" +], + "application/vnd.3gpp.pic-bw-small": [ + "psb" +], + "application/vnd.3gpp.pic-bw-var": [ + "pvb" +], + "application/vnd.3gpp2.tcap": [ + "tcap" +], + "application/vnd.3m.post-it-notes": [ + "pwn" +], + "application/vnd.accpac.simply.aso": [ + "aso" +], + "application/vnd.accpac.simply.imp": [ + "imp" +], + "application/vnd.acucobol": [ + "acu" +], + "application/vnd.acucorp": [ + "atc", + "acutc" +], + "application/vnd.adobe.air-application-installer-package+zip": [ + "air" +], + "application/vnd.adobe.formscentral.fcdt": [ + "fcdt" +], + "application/vnd.adobe.fxp": [ + "fxp", + "fxpl" +], + "application/vnd.adobe.xdp+xml": [ + "xdp" +], + "application/vnd.adobe.xfdf": [ + "xfdf" +], + "application/vnd.ahead.space": [ + "ahead" +], + "application/vnd.airzip.filesecure.azf": [ + "azf" +], + "application/vnd.airzip.filesecure.azs": [ + "azs" +], + "application/vnd.amazon.ebook": [ + "azw" +], + "application/vnd.americandynamics.acc": [ + "acc" +], + "application/vnd.amiga.ami": [ + "ami" +], + "application/vnd.android.package-archive": [ + "apk" +], + "application/vnd.anser-web-certificate-issue-initiation": [ + "cii" +], + "application/vnd.anser-web-funds-transfer-initiation": [ + "fti" +], + "application/vnd.antix.game-component": [ + "atx" +], + "application/vnd.apple.installer+xml": [ + "mpkg" +], + "application/vnd.apple.mpegurl": [ + "m3u8" +], + "application/vnd.apple.pkpass": [ + "pkpass" +], + "application/vnd.aristanetworks.swi": [ + "swi" +], + "application/vnd.astraea-software.iota": [ + "iota" +], + "application/vnd.audiograph": [ + "aep" +], + "application/vnd.blueice.multipass": [ + "mpm" +], + "application/vnd.bmi": [ + "bmi" +], + "application/vnd.businessobjects": [ + "rep" +], + "application/vnd.chemdraw+xml": [ + "cdxml" +], + "application/vnd.chipnuts.karaoke-mmd": [ + "mmd" +], + "application/vnd.cinderella": [ + "cdy" +], + "application/vnd.claymore": [ + "cla" +], + "application/vnd.cloanto.rp9": [ + "rp9" +], + "application/vnd.clonk.c4group": [ + "c4g", + "c4d", + "c4f", + "c4p", + "c4u" +], + "application/vnd.cluetrust.cartomobile-config": [ + "c11amc" +], + "application/vnd.cluetrust.cartomobile-config-pkg": [ + "c11amz" +], + "application/vnd.commonspace": [ + "csp" +], + "application/vnd.contact.cmsg": [ + "cdbcmsg" +], + "application/vnd.cosmocaller": [ + "cmc" +], + "application/vnd.crick.clicker": [ + "clkx" +], + "application/vnd.crick.clicker.keyboard": [ + "clkk" +], + "application/vnd.crick.clicker.palette": [ + "clkp" +], + "application/vnd.crick.clicker.template": [ + "clkt" +], + "application/vnd.crick.clicker.wordbank": [ + "clkw" +], + "application/vnd.criticaltools.wbs+xml": [ + "wbs" +], + "application/vnd.ctc-posml": [ + "pml" +], + "application/vnd.cups-ppd": [ + "ppd" +], + "application/vnd.curl.car": [ + "car" +], + "application/vnd.curl.pcurl": [ + "pcurl" +], + "application/vnd.dart": [ + "dart" +], + "application/vnd.data-vision.rdz": [ + "rdz" +], + "application/vnd.dece.data": [ + "uvf", + "uvvf", + "uvd", + "uvvd" +], + "application/vnd.dece.ttml+xml": [ + "uvt", + "uvvt" +], + "application/vnd.dece.unspecified": [ + "uvx", + "uvvx" +], + "application/vnd.dece.zip": [ + "uvz", + "uvvz" +], + "application/vnd.denovo.fcselayout-link": [ + "fe_launch" +], + "application/vnd.dna": [ + "dna" +], + "application/vnd.dolby.mlp": [ + "mlp" +], + "application/vnd.dpgraph": [ + "dpg" +], + "application/vnd.dreamfactory": [ + "dfac" +], + "application/vnd.ds-keypoint": [ + "kpxx" +], + "application/vnd.dvb.ait": [ + "ait" +], + "application/vnd.dvb.service": [ + "svc" +], + "application/vnd.dynageo": [ + "geo" +], + "application/vnd.ecowin.chart": [ + "mag" +], + "application/vnd.enliven": [ + "nml" +], + "application/vnd.epson.esf": [ + "esf" +], + "application/vnd.epson.msf": [ + "msf" +], + "application/vnd.epson.quickanime": [ + "qam" +], + "application/vnd.epson.salt": [ + "slt" +], + "application/vnd.epson.ssf": [ + "ssf" +], + "application/vnd.eszigno3+xml": [ + "es3", + "et3" +], + "application/vnd.ezpix-album": [ + "ez2" +], + "application/vnd.ezpix-package": [ + "ez3" +], + "application/vnd.fdf": [ + "fdf" +], + "application/vnd.fdsn.mseed": [ + "mseed" +], + "application/vnd.fdsn.seed": [ + "seed", + "dataless" +], + "application/vnd.flographit": [ + "gph" +], + "application/vnd.fluxtime.clip": [ + "ftc" +], + "application/vnd.framemaker": [ + "fm", + "frame", + "maker", + "book" +], + "application/vnd.frogans.fnc": [ + "fnc" +], + "application/vnd.frogans.ltf": [ + "ltf" +], + "application/vnd.fsc.weblaunch": [ + "fsc" +], + "application/vnd.fujitsu.oasys": [ + "oas" +], + "application/vnd.fujitsu.oasys2": [ + "oa2" +], + "application/vnd.fujitsu.oasys3": [ + "oa3" +], + "application/vnd.fujitsu.oasysgp": [ + "fg5" +], + "application/vnd.fujitsu.oasysprs": [ + "bh2" +], + "application/vnd.fujixerox.ddd": [ + "ddd" +], + "application/vnd.fujixerox.docuworks": [ + "xdw" +], + "application/vnd.fujixerox.docuworks.binder": [ + "xbd" +], + "application/vnd.fuzzysheet": [ + "fzs" +], + "application/vnd.genomatix.tuxedo": [ + "txd" +], + "application/vnd.geogebra.file": [ + "ggb" +], + "application/vnd.geogebra.tool": [ + "ggt" +], + "application/vnd.geometry-explorer": [ + "gex", + "gre" +], + "application/vnd.geonext": [ + "gxt" +], + "application/vnd.geoplan": [ + "g2w" +], + "application/vnd.geospace": [ + "g3w" +], + "application/vnd.gmx": [ + "gmx" +], + "application/vnd.google-apps.document": [ + "gdoc" +], + "application/vnd.google-apps.presentation": [ + "gslides" +], + "application/vnd.google-apps.spreadsheet": [ + "gsheet" +], + "application/vnd.google-earth.kml+xml": [ + "kml" +], + "application/vnd.google-earth.kmz": [ + "kmz" +], + "application/vnd.grafeq": [ + "gqf", + "gqs" +], + "application/vnd.groove-account": [ + "gac" +], + "application/vnd.groove-help": [ + "ghf" +], + "application/vnd.groove-identity-message": [ + "gim" +], + "application/vnd.groove-injector": [ + "grv" +], + "application/vnd.groove-tool-message": [ + "gtm" +], + "application/vnd.groove-tool-template": [ + "tpl" +], + "application/vnd.groove-vcard": [ + "vcg" +], + "application/vnd.hal+xml": [ + "hal" +], + "application/vnd.handheld-entertainment+xml": [ + "zmm" +], + "application/vnd.hbci": [ + "hbci" +], + "application/vnd.hhe.lesson-player": [ + "les" +], + "application/vnd.hp-hpgl": [ + "hpgl" +], + "application/vnd.hp-hpid": [ + "hpid" +], + "application/vnd.hp-hps": [ + "hps" +], + "application/vnd.hp-jlyt": [ + "jlt" +], + "application/vnd.hp-pcl": [ + "pcl" +], + "application/vnd.hp-pclxl": [ + "pclxl" +], + "application/vnd.hydrostatix.sof-data": [ + "sfd-hdstx" +], + "application/vnd.ibm.minipay": [ + "mpy" +], + "application/vnd.ibm.modcap": [ + "afp", + "listafp", + "list3820" +], + "application/vnd.ibm.rights-management": [ + "irm" +], + "application/vnd.ibm.secure-container": [ + "sc" +], + "application/vnd.iccprofile": [ + "icc", + "icm" +], + "application/vnd.igloader": [ + "igl" +], + "application/vnd.immervision-ivp": [ + "ivp" +], + "application/vnd.immervision-ivu": [ + "ivu" +], + "application/vnd.insors.igm": [ + "igm" +], + "application/vnd.intercon.formnet": [ + "xpw", + "xpx" +], + "application/vnd.intergeo": [ + "i2g" +], + "application/vnd.intu.qbo": [ + "qbo" +], + "application/vnd.intu.qfx": [ + "qfx" +], + "application/vnd.ipunplugged.rcprofile": [ + "rcprofile" +], + "application/vnd.irepository.package+xml": [ + "irp" +], + "application/vnd.is-xpr": [ + "xpr" +], + "application/vnd.isac.fcs": [ + "fcs" +], + "application/vnd.jam": [ + "jam" +], + "application/vnd.jcp.javame.midlet-rms": [ + "rms" +], + "application/vnd.jisp": [ + "jisp" +], + "application/vnd.joost.joda-archive": [ + "joda" +], + "application/vnd.kahootz": [ + "ktz", + "ktr" +], + "application/vnd.kde.karbon": [ + "karbon" +], + "application/vnd.kde.kchart": [ + "chrt" +], + "application/vnd.kde.kformula": [ + "kfo" +], + "application/vnd.kde.kivio": [ + "flw" +], + "application/vnd.kde.kontour": [ + "kon" +], + "application/vnd.kde.kpresenter": [ + "kpr", + "kpt" +], + "application/vnd.kde.kspread": [ + "ksp" +], + "application/vnd.kde.kword": [ + "kwd", + "kwt" +], + "application/vnd.kenameaapp": [ + "htke" +], + "application/vnd.kidspiration": [ + "kia" +], + "application/vnd.kinar": [ + "kne", + "knp" +], + "application/vnd.koan": [ + "skp", + "skd", + "skt", + "skm" +], + "application/vnd.kodak-descriptor": [ + "sse" +], + "application/vnd.las.las+xml": [ + "lasxml" +], + "application/vnd.llamagraphics.life-balance.desktop": [ + "lbd" +], + "application/vnd.llamagraphics.life-balance.exchange+xml": [ + "lbe" +], + "application/vnd.lotus-1-2-3": [ + "123" +], + "application/vnd.lotus-approach": [ + "apr" +], + "application/vnd.lotus-freelance": [ + "pre" +], + "application/vnd.lotus-notes": [ + "nsf" +], + "application/vnd.lotus-organizer": [ + "org" +], + "application/vnd.lotus-screencam": [ + "scm" +], + "application/vnd.lotus-wordpro": [ + "lwp" +], + "application/vnd.macports.portpkg": [ + "portpkg" +], + "application/vnd.mcd": [ + "mcd" +], + "application/vnd.medcalcdata": [ + "mc1" +], + "application/vnd.mediastation.cdkey": [ + "cdkey" +], + "application/vnd.mfer": [ + "mwf" +], + "application/vnd.mfmp": [ + "mfm" +], + "application/vnd.micrografx.flo": [ + "flo" +], + "application/vnd.micrografx.igx": [ + "igx" +], + "application/vnd.mif": [ + "mif" +], + "application/vnd.mobius.daf": [ + "daf" +], + "application/vnd.mobius.dis": [ + "dis" +], + "application/vnd.mobius.mbk": [ + "mbk" +], + "application/vnd.mobius.mqy": [ + "mqy" +], + "application/vnd.mobius.msl": [ + "msl" +], + "application/vnd.mobius.plc": [ + "plc" +], + "application/vnd.mobius.txf": [ + "txf" +], + "application/vnd.mophun.application": [ + "mpn" +], + "application/vnd.mophun.certificate": [ + "mpc" +], + "application/vnd.mozilla.xul+xml": [ + "xul" +], + "application/vnd.ms-artgalry": [ + "cil" +], + "application/vnd.ms-cab-compressed": [ + "cab" +], + "application/vnd.ms-excel": [ + "xls", + "xlm", + "xla", + "xlc", + "xlt", + "xlw" +], + "application/vnd.ms-excel.addin.macroenabled.12": [ + "xlam" +], + "application/vnd.ms-excel.sheet.binary.macroenabled.12": [ + "xlsb" +], + "application/vnd.ms-excel.sheet.macroenabled.12": [ + "xlsm" +], + "application/vnd.ms-excel.template.macroenabled.12": [ + "xltm" +], + "application/vnd.ms-fontobject": [ + "eot" +], + "application/vnd.ms-htmlhelp": [ + "chm" +], + "application/vnd.ms-ims": [ + "ims" +], + "application/vnd.ms-lrm": [ + "lrm" +], + "application/vnd.ms-officetheme": [ + "thmx" +], + "application/vnd.ms-outlook": [ + "msg" +], + "application/vnd.ms-pki.seccat": [ + "cat" +], + "application/vnd.ms-pki.stl": [ + "stl" +], + "application/vnd.ms-powerpoint": [ + "ppt", + "pps", + "pot" +], + "application/vnd.ms-powerpoint.addin.macroenabled.12": [ + "ppam" +], + "application/vnd.ms-powerpoint.presentation.macroenabled.12": [ + "pptm" +], + "application/vnd.ms-powerpoint.slide.macroenabled.12": [ + "sldm" +], + "application/vnd.ms-powerpoint.slideshow.macroenabled.12": [ + "ppsm" +], + "application/vnd.ms-powerpoint.template.macroenabled.12": [ + "potm" +], + "application/vnd.ms-project": [ + "mpp", + "mpt" +], + "application/vnd.ms-word.document.macroenabled.12": [ + "docm" +], + "application/vnd.ms-word.template.macroenabled.12": [ + "dotm" +], + "application/vnd.ms-works": [ + "wps", + "wks", + "wcm", + "wdb" +], + "application/vnd.ms-wpl": [ + "wpl" +], + "application/vnd.ms-xpsdocument": [ + "xps" +], + "application/vnd.mseq": [ + "mseq" +], + "application/vnd.musician": [ + "mus" +], + "application/vnd.muvee.style": [ + "msty" +], + "application/vnd.mynfc": [ + "taglet" +], + "application/vnd.neurolanguage.nlu": [ + "nlu" +], + "application/vnd.nitf": [ + "ntf", + "nitf" +], + "application/vnd.noblenet-directory": [ + "nnd" +], + "application/vnd.noblenet-sealer": [ + "nns" +], + "application/vnd.noblenet-web": [ + "nnw" +], + "application/vnd.nokia.n-gage.data": [ + "ngdat" +], + "application/vnd.nokia.n-gage.symbian.install": [ + "n-gage" +], + "application/vnd.nokia.radio-preset": [ + "rpst" +], + "application/vnd.nokia.radio-presets": [ + "rpss" +], + "application/vnd.novadigm.edm": [ + "edm" +], + "application/vnd.novadigm.edx": [ + "edx" +], + "application/vnd.novadigm.ext": [ + "ext" +], + "application/vnd.oasis.opendocument.chart": [ + "odc" +], + "application/vnd.oasis.opendocument.chart-template": [ + "otc" +], + "application/vnd.oasis.opendocument.database": [ + "odb" +], + "application/vnd.oasis.opendocument.formula": [ + "odf" +], + "application/vnd.oasis.opendocument.formula-template": [ + "odft" +], + "application/vnd.oasis.opendocument.graphics": [ + "odg" +], + "application/vnd.oasis.opendocument.graphics-template": [ + "otg" +], + "application/vnd.oasis.opendocument.image": [ + "odi" +], + "application/vnd.oasis.opendocument.image-template": [ + "oti" +], + "application/vnd.oasis.opendocument.presentation": [ + "odp" +], + "application/vnd.oasis.opendocument.presentation-template": [ + "otp" +], + "application/vnd.oasis.opendocument.spreadsheet": [ + "ods" +], + "application/vnd.oasis.opendocument.spreadsheet-template": [ + "ots" +], + "application/vnd.oasis.opendocument.text": [ + "odt" +], + "application/vnd.oasis.opendocument.text-master": [ + "odm" +], + "application/vnd.oasis.opendocument.text-template": [ + "ott" +], + "application/vnd.oasis.opendocument.text-web": [ + "oth" +], + "application/vnd.olpc-sugar": [ + "xo" +], + "application/vnd.oma.dd2+xml": [ + "dd2" +], + "application/vnd.openofficeorg.extension": [ + "oxt" +], + "application/vnd.openxmlformats-officedocument.presentationml.presentation": [ + "pptx" +], + "application/vnd.openxmlformats-officedocument.presentationml.slide": [ + "sldx" +], + "application/vnd.openxmlformats-officedocument.presentationml.slideshow": [ + "ppsx" +], + "application/vnd.openxmlformats-officedocument.presentationml.template": [ + "potx" +], + "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": [ + "xlsx" +], + "application/vnd.openxmlformats-officedocument.spreadsheetml.template": [ + "xltx" +], + "application/vnd.openxmlformats-officedocument.wordprocessingml.document": [ + "docx" +], + "application/vnd.openxmlformats-officedocument.wordprocessingml.template": [ + "dotx" +], + "application/vnd.osgeo.mapguide.package": [ + "mgp" +], + "application/vnd.osgi.dp": [ + "dp" +], + "application/vnd.osgi.subsystem": [ + "esa" +], + "application/vnd.palm": [ + "pdb", + "pqa", + "oprc" +], + "application/vnd.pawaafile": [ + "paw" +], + "application/vnd.pg.format": [ + "str" +], + "application/vnd.pg.osasli": [ + "ei6" +], + "application/vnd.picsel": [ + "efif" +], + "application/vnd.pmi.widget": [ + "wg" +], + "application/vnd.pocketlearn": [ + "plf" +], + "application/vnd.powerbuilder6": [ + "pbd" +], + "application/vnd.previewsystems.box": [ + "box" +], + "application/vnd.proteus.magazine": [ + "mgz" +], + "application/vnd.publishare-delta-tree": [ + "qps" +], + "application/vnd.pvi.ptid1": [ + "ptid" +], + "application/vnd.quark.quarkxpress": [ + "qxd", + "qxt", + "qwd", + "qwt", + "qxl", + "qxb" +], + "application/vnd.realvnc.bed": [ + "bed" +], + "application/vnd.recordare.musicxml": [ + "mxl" +], + "application/vnd.recordare.musicxml+xml": [ + "musicxml" +], + "application/vnd.rig.cryptonote": [ + "cryptonote" +], + "application/vnd.rim.cod": [ + "cod" +], + "application/vnd.rn-realmedia": [ + "rm" +], + "application/vnd.rn-realmedia-vbr": [ + "rmvb" +], + "application/vnd.route66.link66+xml": [ + "link66" +], + "application/vnd.sailingtracker.track": [ + "st" +], + "application/vnd.seemail": [ + "see" +], + "application/vnd.sema": [ + "sema" +], + "application/vnd.semd": [ + "semd" +], + "application/vnd.semf": [ + "semf" +], + "application/vnd.shana.informed.formdata": [ + "ifm" +], + "application/vnd.shana.informed.formtemplate": [ + "itp" +], + "application/vnd.shana.informed.interchange": [ + "iif" +], + "application/vnd.shana.informed.package": [ + "ipk" +], + "application/vnd.simtech-mindmapper": [ + "twd", + "twds" +], + "application/vnd.smaf": [ + "mmf" +], + "application/vnd.smart.teacher": [ + "teacher" +], + "application/vnd.solent.sdkm+xml": [ + "sdkm", + "sdkd" +], + "application/vnd.spotfire.dxp": [ + "dxp" +], + "application/vnd.spotfire.sfs": [ + "sfs" +], + "application/vnd.stardivision.calc": [ + "sdc" +], + "application/vnd.stardivision.draw": [ + "sda" +], + "application/vnd.stardivision.impress": [ + "sdd" +], + "application/vnd.stardivision.math": [ + "smf" +], + "application/vnd.stardivision.writer": [ + "sdw", + "vor" +], + "application/vnd.stardivision.writer-global": [ + "sgl" +], + "application/vnd.stepmania.package": [ + "smzip" +], + "application/vnd.stepmania.stepchart": [ + "sm" +], + "application/vnd.sun.wadl+xml": [ + "wadl" +], + "application/vnd.sun.xml.calc": [ + "sxc" +], + "application/vnd.sun.xml.calc.template": [ + "stc" +], + "application/vnd.sun.xml.draw": [ + "sxd" +], + "application/vnd.sun.xml.draw.template": [ + "std" +], + "application/vnd.sun.xml.impress": [ + "sxi" +], + "application/vnd.sun.xml.impress.template": [ + "sti" +], + "application/vnd.sun.xml.math": [ + "sxm" +], + "application/vnd.sun.xml.writer": [ + "sxw" +], + "application/vnd.sun.xml.writer.global": [ + "sxg" +], + "application/vnd.sun.xml.writer.template": [ + "stw" +], + "application/vnd.sus-calendar": [ + "sus", + "susp" +], + "application/vnd.svd": [ + "svd" +], + "application/vnd.symbian.install": [ + "sis", + "sisx" +], + "application/vnd.syncml+xml": [ + "xsm" +], + "application/vnd.syncml.dm+wbxml": [ + "bdm" +], + "application/vnd.syncml.dm+xml": [ + "xdm" +], + "application/vnd.tao.intent-module-archive": [ + "tao" +], + "application/vnd.tcpdump.pcap": [ + "pcap", + "cap", + "dmp" +], + "application/vnd.tmobile-livetv": [ + "tmo" +], + "application/vnd.trid.tpt": [ + "tpt" +], + "application/vnd.triscape.mxs": [ + "mxs" +], + "application/vnd.trueapp": [ + "tra" +], + "application/vnd.ufdl": [ + "ufd", + "ufdl" +], + "application/vnd.uiq.theme": [ + "utz" +], + "application/vnd.umajin": [ + "umj" +], + "application/vnd.unity": [ + "unityweb" +], + "application/vnd.uoml+xml": [ + "uoml" +], + "application/vnd.vcx": [ + "vcx" +], + "application/vnd.visio": [ + "vsd", + "vst", + "vss", + "vsw" +], + "application/vnd.visionary": [ + "vis" +], + "application/vnd.vsf": [ + "vsf" +], + "application/vnd.wap.wbxml": [ + "wbxml" +], + "application/vnd.wap.wmlc": [ + "wmlc" +], + "application/vnd.wap.wmlscriptc": [ + "wmlsc" +], + "application/vnd.webturbo": [ + "wtb" +], + "application/vnd.wolfram.player": [ + "nbp" +], + "application/vnd.wordperfect": [ + "wpd" +], + "application/vnd.wqd": [ + "wqd" +], + "application/vnd.wt.stf": [ + "stf" +], + "application/vnd.xara": [ + "xar" +], + "application/vnd.xfdl": [ + "xfdl" +], + "application/vnd.yamaha.hv-dic": [ + "hvd" +], + "application/vnd.yamaha.hv-script": [ + "hvs" +], + "application/vnd.yamaha.hv-voice": [ + "hvp" +], + "application/vnd.yamaha.openscoreformat": [ + "osf" +], + "application/vnd.yamaha.openscoreformat.osfpvg+xml": [ + "osfpvg" +], + "application/vnd.yamaha.smaf-audio": [ + "saf" +], + "application/vnd.yamaha.smaf-phrase": [ + "spf" +], + "application/vnd.yellowriver-custom-menu": [ + "cmp" +], + "application/vnd.zul": [ + "zir", + "zirz" +], + "application/vnd.zzazz.deck+xml": [ + "zaz" +], + "application/voicexml+xml": [ + "vxml" +], + "application/wasm": [ + "wasm" +], + "application/widget": [ + "wgt" +], + "application/winhlp": [ + "hlp" +], + "application/wsdl+xml": [ + "wsdl" +], + "application/wspolicy+xml": [ + "wspolicy" +], + "application/x-7z-compressed": [ + "7z" +], + "application/x-abiword": [ + "abw" +], + "application/x-ace-compressed": [ + "ace" +], + "application/x-apple-diskimage": [ +], + "application/x-arj": [ + "arj" +], + "application/x-authorware-bin": [ + "aab", + "x32", + "u32", + "vox" +], + "application/x-authorware-map": [ + "aam" +], + "application/x-authorware-seg": [ + "aas" +], + "application/x-bcpio": [ + "bcpio" +], + "application/x-bdoc": [ +], + "application/x-bittorrent": [ + "torrent" +], + "application/x-blorb": [ + "blb", + "blorb" +], + "application/x-bzip": [ + "bz" +], + "application/x-bzip2": [ + "bz2", + "boz" +], + "application/x-cbr": [ + "cbr", + "cba", + "cbt", + "cbz", + "cb7" +], + "application/x-cdlink": [ + "vcd" +], + "application/x-cfs-compressed": [ + "cfs" +], + "application/x-chat": [ + "chat" +], + "application/x-chess-pgn": [ + "pgn" +], + "application/x-chrome-extension": [ + "crx" +], + "application/x-cocoa": [ + "cco" +], + "application/x-conference": [ + "nsc" +], + "application/x-cpio": [ + "cpio" +], + "application/x-csh": [ + "csh" +], + "application/x-debian-package": [ + "udeb" +], + "application/x-dgc-compressed": [ + "dgc" +], + "application/x-director": [ + "dir", + "dcr", + "dxr", + "cst", + "cct", + "cxt", + "w3d", + "fgd", + "swa" +], + "application/x-doom": [ + "wad" +], + "application/x-dtbncx+xml": [ + "ncx" +], + "application/x-dtbook+xml": [ + "dtb" +], + "application/x-dtbresource+xml": [ + "res" +], + "application/x-dvi": [ + "dvi" +], + "application/x-envoy": [ + "evy" +], + "application/x-eva": [ + "eva" +], + "application/x-font-bdf": [ + "bdf" +], + "application/x-font-ghostscript": [ + "gsf" +], + "application/x-font-linux-psf": [ + "psf" +], + "application/x-font-pcf": [ + "pcf" +], + "application/x-font-snf": [ + "snf" +], + "application/x-font-type1": [ + "pfa", + "pfb", + "pfm", + "afm" +], + "application/x-freearc": [ + "arc" +], + "application/x-futuresplash": [ + "spl" +], + "application/x-gca-compressed": [ + "gca" +], + "application/x-glulx": [ + "ulx" +], + "application/x-gnumeric": [ + "gnumeric" +], + "application/x-gramps-xml": [ + "gramps" +], + "application/x-gtar": [ + "gtar" +], + "application/x-hdf": [ + "hdf" +], + "application/x-httpd-php": [ + "php" +], + "application/x-install-instructions": [ + "install" +], + "application/x-iso9660-image": [ +], + "application/x-java-archive-diff": [ + "jardiff" +], + "application/x-java-jnlp-file": [ + "jnlp" +], + "application/x-latex": [ + "latex" +], + "application/x-lua-bytecode": [ + "luac" +], + "application/x-lzh-compressed": [ + "lzh", + "lha" +], + "application/x-makeself": [ + "run" +], + "application/x-mie": [ + "mie" +], + "application/x-mobipocket-ebook": [ + "prc", + "mobi" +], + "application/x-ms-application": [ + "application" +], + "application/x-ms-shortcut": [ + "lnk" +], + "application/x-ms-wmd": [ + "wmd" +], + "application/x-ms-wmz": [ + "wmz" +], + "application/x-ms-xbap": [ + "xbap" +], + "application/x-msaccess": [ + "mdb" +], + "application/x-msbinder": [ + "obd" +], + "application/x-mscardfile": [ + "crd" +], + "application/x-msclip": [ + "clp" +], + "application/x-msdos-program": [ +], + "application/x-msdownload": [ + "com", + "bat" +], + "application/x-msmediaview": [ + "mvb", + "m13", + "m14" +], + "application/x-msmetafile": [ + "wmf", + "emf", + "emz" +], + "application/x-msmoney": [ + "mny" +], + "application/x-mspublisher": [ + "pub" +], + "application/x-msschedule": [ + "scd" +], + "application/x-msterminal": [ + "trm" +], + "application/x-mswrite": [ + "wri" +], + "application/x-netcdf": [ + "nc", + "cdf" +], + "application/x-ns-proxy-autoconfig": [ + "pac" +], + "application/x-nzb": [ + "nzb" +], + "application/x-perl": [ + "pl", + "pm" +], + "application/x-pilot": [ +], + "application/x-pkcs12": [ + "p12", + "pfx" +], + "application/x-pkcs7-certificates": [ + "p7b", + "spc" +], + "application/x-pkcs7-certreqresp": [ + "p7r" +], + "application/x-rar-compressed": [ + "rar" +], + "application/x-redhat-package-manager": [ + "rpm" +], + "application/x-research-info-systems": [ + "ris" +], + "application/x-sea": [ + "sea" +], + "application/x-sh": [ + "sh" +], + "application/x-shar": [ + "shar" +], + "application/x-shockwave-flash": [ + "swf" +], + "application/x-silverlight-app": [ + "xap" +], + "application/x-sql": [ + "sql" +], + "application/x-stuffit": [ + "sit" +], + "application/x-stuffitx": [ + "sitx" +], + "application/x-subrip": [ + "srt" +], + "application/x-sv4cpio": [ + "sv4cpio" +], + "application/x-sv4crc": [ + "sv4crc" +], + "application/x-t3vm-image": [ + "t3" +], + "application/x-tads": [ + "gam" +], + "application/x-tar": [ + "tar" +], + "application/x-tcl": [ + "tcl", + "tk" +], + "application/x-tex": [ + "tex" +], + "application/x-tex-tfm": [ + "tfm" +], + "application/x-texinfo": [ + "texinfo", + "texi" +], + "application/x-tgif": [ + "obj" +], + "application/x-ustar": [ + "ustar" +], + "application/x-virtualbox-hdd": [ + "hdd" +], + "application/x-virtualbox-ova": [ + "ova" +], + "application/x-virtualbox-ovf": [ + "ovf" +], + "application/x-virtualbox-vbox": [ + "vbox" +], + "application/x-virtualbox-vbox-extpack": [ + "vbox-extpack" +], + "application/x-virtualbox-vdi": [ + "vdi" +], + "application/x-virtualbox-vhd": [ + "vhd" +], + "application/x-virtualbox-vmdk": [ + "vmdk" +], + "application/x-wais-source": [ + "src" +], + "application/x-web-app-manifest+json": [ + "webapp" +], + "application/x-x509-ca-cert": [ + "der", + "crt", + "pem" +], + "application/x-xfig": [ + "fig" +], + "application/x-xliff+xml": [ + "xlf" +], + "application/x-xpinstall": [ + "xpi" +], + "application/x-xz": [ + "xz" +], + "application/x-zmachine": [ + "z1", + "z2", + "z3", + "z4", + "z5", + "z6", + "z7", + "z8" +], + "application/xaml+xml": [ + "xaml" +], + "application/xcap-diff+xml": [ + "xdf" +], + "application/xenc+xml": [ + "xenc" +], + "application/xhtml+xml": [ + "xhtml", + "xht" +], + "application/xml": [ + "xml", + "xsl", + "xsd", + "rng" +], + "application/xml-dtd": [ + "dtd" +], + "application/xop+xml": [ + "xop" +], + "application/xproc+xml": [ + "xpl" +], + "application/xslt+xml": [ + "xslt" +], + "application/xspf+xml": [ + "xspf" +], + "application/xv+xml": [ + "mxml", + "xhvml", + "xvml", + "xvm" +], + "application/yang": [ + "yang" +], + "application/yin+xml": [ + "yin" +], + "application/zip": [ + "zip" +], + "audio/3gpp": [ +], + "audio/adpcm": [ + "adp" +], + "audio/basic": [ + "au", + "snd" +], + "audio/midi": [ + "mid", + "midi", + "kar", + "rmi" +], + "audio/mp3": [ +], + "audio/mp4": [ + "m4a", + "mp4a" +], + "audio/mpeg": [ + "mpga", + "mp2", + "mp2a", + "mp3", + "m2a", + "m3a" +], + "audio/ogg": [ + "oga", + "ogg", + "spx" +], + "audio/s3m": [ + "s3m" +], + "audio/silk": [ + "sil" +], + "audio/vnd.dece.audio": [ + "uva", + "uvva" +], + "audio/vnd.digital-winds": [ + "eol" +], + "audio/vnd.dra": [ + "dra" +], + "audio/vnd.dts": [ + "dts" +], + "audio/vnd.dts.hd": [ + "dtshd" +], + "audio/vnd.lucent.voice": [ + "lvp" +], + "audio/vnd.ms-playready.media.pya": [ + "pya" +], + "audio/vnd.nuera.ecelp4800": [ + "ecelp4800" +], + "audio/vnd.nuera.ecelp7470": [ + "ecelp7470" +], + "audio/vnd.nuera.ecelp9600": [ + "ecelp9600" +], + "audio/vnd.rip": [ + "rip" +], + "audio/wav": [ + "wav" +], + "audio/wave": [ +], + "audio/webm": [ + "weba" +], + "audio/x-aac": [ + "aac" +], + "audio/x-aiff": [ + "aif", + "aiff", + "aifc" +], + "audio/x-caf": [ + "caf" +], + "audio/x-flac": [ + "flac" +], + "audio/x-m4a": [ +], + "audio/x-matroska": [ + "mka" +], + "audio/x-mpegurl": [ + "m3u" +], + "audio/x-ms-wax": [ + "wax" +], + "audio/x-ms-wma": [ + "wma" +], + "audio/x-pn-realaudio": [ + "ram", + "ra" +], + "audio/x-pn-realaudio-plugin": [ + "rmp" +], + "audio/x-realaudio": [ +], + "audio/x-wav": [ +], + "audio/xm": [ + "xm" +], + "chemical/x-cdx": [ + "cdx" +], + "chemical/x-cif": [ + "cif" +], + "chemical/x-cmdf": [ + "cmdf" +], + "chemical/x-cml": [ + "cml" +], + "chemical/x-csml": [ + "csml" +], + "chemical/x-xyz": [ + "xyz" +], + "font/collection": [ + "ttc" +], + "font/otf": [ + "otf" +], + "font/ttf": [ + "ttf" +], + "font/woff": [ + "woff" +], + "font/woff2": [ + "woff2" +], + "image/apng": [ + "apng" +], + "image/bmp": [ + "bmp" +], + "image/cgm": [ + "cgm" +], + "image/g3fax": [ + "g3" +], + "image/gif": [ + "gif" +], + "image/ief": [ + "ief" +], + "image/jp2": [ + "jp2", + "jpg2" +], + "image/jpeg": [ + "jpeg", + "jpg", + "jpe" +], + "image/jpm": [ + "jpm" +], + "image/jpx": [ + "jpx", + "jpf" +], + "image/ktx": [ + "ktx" +], + "image/png": [ + "png" +], + "image/prs.btif": [ + "btif" +], + "image/sgi": [ + "sgi" +], + "image/svg+xml": [ + "svg", + "svgz" +], + "image/tiff": [ + "tiff", + "tif" +], + "image/vnd.adobe.photoshop": [ + "psd" +], + "image/vnd.dece.graphic": [ + "uvi", + "uvvi", + "uvg", + "uvvg" +], + "image/vnd.djvu": [ + "djvu", + "djv" +], + "image/vnd.dvb.subtitle": [ +], + "image/vnd.dwg": [ + "dwg" +], + "image/vnd.dxf": [ + "dxf" +], + "image/vnd.fastbidsheet": [ + "fbs" +], + "image/vnd.fpx": [ + "fpx" +], + "image/vnd.fst": [ + "fst" +], + "image/vnd.fujixerox.edmics-mmr": [ + "mmr" +], + "image/vnd.fujixerox.edmics-rlc": [ + "rlc" +], + "image/vnd.ms-modi": [ + "mdi" +], + "image/vnd.ms-photo": [ + "wdp" +], + "image/vnd.net-fpx": [ + "npx" +], + "image/vnd.wap.wbmp": [ + "wbmp" +], + "image/vnd.xiff": [ + "xif" +], + "image/webp": [ + "webp" +], + "image/x-3ds": [ + "3ds" +], + "image/x-cmu-raster": [ + "ras" +], + "image/x-cmx": [ + "cmx" +], + "image/x-freehand": [ + "fh", + "fhc", + "fh4", + "fh5", + "fh7" +], + "image/x-icon": [ + "ico" +], + "image/x-jng": [ + "jng" +], + "image/x-mrsid-image": [ + "sid" +], + "image/x-ms-bmp": [ +], + "image/x-pcx": [ + "pcx" +], + "image/x-pict": [ + "pic", + "pct" +], + "image/x-portable-anymap": [ + "pnm" +], + "image/x-portable-bitmap": [ + "pbm" +], + "image/x-portable-graymap": [ + "pgm" +], + "image/x-portable-pixmap": [ + "ppm" +], + "image/x-rgb": [ + "rgb" +], + "image/x-tga": [ + "tga" +], + "image/x-xbitmap": [ + "xbm" +], + "image/x-xpixmap": [ + "xpm" +], + "image/x-xwindowdump": [ + "xwd" +], + "message/rfc822": [ + "eml", + "mime" +], + "model/gltf+json": [ + "gltf" +], + "model/gltf-binary": [ + "glb" +], + "model/iges": [ + "igs", + "iges" +], + "model/mesh": [ + "msh", + "mesh", + "silo" +], + "model/vnd.collada+xml": [ + "dae" +], + "model/vnd.dwf": [ + "dwf" +], + "model/vnd.gdl": [ + "gdl" +], + "model/vnd.gtw": [ + "gtw" +], + "model/vnd.mts": [ + "mts" +], + "model/vnd.vtu": [ + "vtu" +], + "model/vrml": [ + "wrl", + "vrml" +], + "model/x3d+binary": [ + "x3db", + "x3dbz" +], + "model/x3d+vrml": [ + "x3dv", + "x3dvz" +], + "model/x3d+xml": [ + "x3d", + "x3dz" +], + "text/cache-manifest": [ + "appcache", + "manifest" +], + "text/calendar": [ + "ics", + "ifb" +], + "text/coffeescript": [ + "coffee", + "litcoffee" +], + "text/css": [ + "css" +], + "text/csv": [ + "csv" +], + "text/hjson": [ + "hjson" +], + "text/html": [ + "html", + "htm", + "shtml" +], + "text/jade": [ + "jade" +], + "text/jsx": [ + "jsx" +], + "text/less": [ + "less" +], + "text/markdown": [ + "markdown", + "md" +], + "text/mathml": [ + "mml" +], + "text/n3": [ + "n3" +], + "text/plain": [ + "txt", + "text", + "conf", + "def", + "list", + "log", + "in", + "ini" +], + "text/prs.lines.tag": [ + "dsc" +], + "text/richtext": [ + "rtx" +], + "text/rtf": [ +], + "text/sgml": [ + "sgml", + "sgm" +], + "text/slim": [ + "slim", + "slm" +], + "text/stylus": [ + "stylus", + "styl" +], + "text/tab-separated-values": [ + "tsv" +], + "text/troff": [ + "t", + "tr", + "roff", + "man", + "me", + "ms" +], + "text/turtle": [ + "ttl" +], + "text/uri-list": [ + "uri", + "uris", + "urls" +], + "text/vcard": [ + "vcard" +], + "text/vnd.curl": [ + "curl" +], + "text/vnd.curl.dcurl": [ + "dcurl" +], + "text/vnd.curl.mcurl": [ + "mcurl" +], + "text/vnd.curl.scurl": [ + "scurl" +], + "text/vnd.dvb.subtitle": [ + "sub" +], + "text/vnd.fly": [ + "fly" +], + "text/vnd.fmi.flexstor": [ + "flx" +], + "text/vnd.graphviz": [ + "gv" +], + "text/vnd.in3d.3dml": [ + "3dml" +], + "text/vnd.in3d.spot": [ + "spot" +], + "text/vnd.sun.j2me.app-descriptor": [ + "jad" +], + "text/vnd.wap.wml": [ + "wml" +], + "text/vnd.wap.wmlscript": [ + "wmls" +], + "text/vtt": [ + "vtt" +], + "text/x-asm": [ + "s", + "asm" +], + "text/x-c": [ + "c", + "cc", + "cxx", + "cpp", + "h", + "hh", + "dic" +], + "text/x-component": [ + "htc" +], + "text/x-fortran": [ + "f", + "for", + "f77", + "f90" +], + "text/x-handlebars-template": [ + "hbs" +], + "text/x-java-source": [ + "java" +], + "text/x-lua": [ + "lua" +], + "text/x-markdown": [ + "mkd" +], + "text/x-nfo": [ + "nfo" +], + "text/x-opml": [ + "opml" +], + "text/x-org": [ +], + "text/x-pascal": [ + "p", + "pas" +], + "text/x-processing": [ + "pde" +], + "text/x-sass": [ + "sass" +], + "text/x-scss": [ + "scss" +], + "text/x-setext": [ + "etx" +], + "text/x-sfv": [ + "sfv" +], + "text/x-suse-ymp": [ + "ymp" +], + "text/x-uuencode": [ + "uu" +], + "text/x-vcalendar": [ + "vcs" +], + "text/x-vcard": [ + "vcf" +], + "text/xml": [ +], + "text/yaml": [ + "yaml", + "yml" +], + "video/3gpp": [ + "3gp", + "3gpp" +], + "video/3gpp2": [ + "3g2" +], + "video/h261": [ + "h261" +], + "video/h263": [ + "h263" +], + "video/h264": [ + "h264" +], + "video/jpeg": [ + "jpgv" +], + "video/jpm": [ + "jpgm" +], + "video/mj2": [ + "mj2", + "mjp2" +], + "video/mp2t": [ + "ts" +], + "video/mp4": [ + "mp4", + "mp4v", + "mpg4" +], + "video/mpeg": [ + "mpeg", + "mpg", + "mpe", + "m1v", + "m2v" +], + "video/ogg": [ + "ogv" +], + "video/quicktime": [ + "qt", + "mov" +], + "video/vnd.dece.hd": [ + "uvh", + "uvvh" +], + "video/vnd.dece.mobile": [ + "uvm", + "uvvm" +], + "video/vnd.dece.pd": [ + "uvp", + "uvvp" +], + "video/vnd.dece.sd": [ + "uvs", + "uvvs" +], + "video/vnd.dece.video": [ + "uvv", + "uvvv" +], + "video/vnd.dvb.file": [ + "dvb" +], + "video/vnd.fvt": [ + "fvt" +], + "video/vnd.mpegurl": [ + "mxu", + "m4u" +], + "video/vnd.ms-playready.media.pyv": [ + "pyv" +], + "video/vnd.uvvu.mp4": [ + "uvu", + "uvvu" +], + "video/vnd.vivo": [ + "viv" +], + "video/webm": [ + "webm" +], + "video/x-f4v": [ + "f4v" +], + "video/x-fli": [ + "fli" +], + "video/x-flv": [ + "flv" +], + "video/x-m4v": [ + "m4v" +], + "video/x-matroska": [ + "mkv", + "mk3d", + "mks" +], + "video/x-mng": [ + "mng" +], + "video/x-ms-asf": [ + "asf", + "asx" +], + "video/x-ms-vob": [ + "vob" +], + "video/x-ms-wm": [ + "wm" +], + "video/x-ms-wmv": [ + "wmv" +], + "video/x-ms-wmx": [ + "wmx" +], + "video/x-ms-wvx": [ + "wvx" +], + "video/x-msvideo": [ + "avi" +], + "video/x-sgi-movie": [ + "movie" +], + "video/x-smv": [ + "smv" +], + "x-conference/x-cooltalk": [ + "ice" +] +}; + +var fs$2 = require$$1__default["default"]; + +function Mime() { + // Map of extension -> mime type + this.types = Object.create(null); + + // Map of mime type -> extension + this.extensions = Object.create(null); +} + +/** + * Define mimetype -> extension mappings. Each key is a mime-type that maps + * to an array of extensions associated with the type. The first extension is + * used as the default extension for the type. + * + * e.g. mime.define({'audio/ogg', ['oga', 'ogg', 'spx']}); + * + * @param map (Object) type definitions + */ +Mime.prototype.define = function (map) { + for (var type in map) { + var exts = map[type]; + for (var i = 0; i < exts.length; i++) { + if (process.env.DEBUG_MIME && this.types[exts[i]]) { + console.warn((this._loading || "define()").replace(/.*\//, ''), 'changes "' + exts[i] + '" extension type from ' + + this.types[exts[i]] + ' to ' + type); + } + + this.types[exts[i]] = type; + } + + // Default extension is the first one we encounter + if (!this.extensions[type]) { + this.extensions[type] = exts[0]; + } + } +}; + +/** + * Load an Apache2-style ".types" file + * + * This may be called multiple times (it's expected). Where files declare + * overlapping types/extensions, the last file wins. + * + * @param file (String) path of file to load. + */ +Mime.prototype.load = function(file) { + this._loading = file; + // Read file and split into lines + var map = {}, + content = fs$2.readFileSync(file, 'ascii'), + lines = content.split(/[\r\n]+/); + + lines.forEach(function(line) { + // Clean up whitespace/comments, and split into fields + var fields = line.replace(/\s*#.*|^\s*|\s*$/g, '').split(/\s+/); + map[fields.shift()] = fields; + }); + + this.define(map); + + this._loading = null; +}; + +/** + * Lookup a mime type based on extension + */ +Mime.prototype.lookup = function(path, fallback) { + var ext = path.replace(/^.*[\.\/\\]/, '').toLowerCase(); + + return this.types[ext] || fallback || this.default_type; +}; + +/** + * Return file extension associated with a mime type + */ +Mime.prototype.extension = function(mimeType) { + var type = mimeType.match(/^\s*([^;\s]*)(?:;|\s|$)/)[1].toLowerCase(); + return this.extensions[type]; +}; + +// Default instance +var mime$1 = new Mime(); + +// Define built-in types +mime$1.define(require$$2); + +// Default type +mime$1.default_type = mime$1.lookup('bin'); + +// +// Additional API specific to the default instance +// + +mime$1.Mime = Mime; + +/** + * Lookup a charset based on mime type. + */ +mime$1.charsets = { + lookup: function(mimeType, fallback) { + // Assume text types are utf8 + return (/^text\/|^application\/(javascript|json)/).test(mimeType) ? 'UTF-8' : fallback; + } +}; + +var mime_1 = mime$1; + +var isWsl$2 = {exports: {}}; + +const os = require$$0__default["default"]; +const fs$1 = require$$1__default["default"]; + +const isWsl$1 = () => { + if (process.platform !== 'linux') { + return false; + } + + if (os.release().includes('Microsoft')) { + return true; + } + + try { + return fs$1.readFileSync('/proc/version', 'utf8').includes('Microsoft'); + } catch (err) { + return false; + } +}; + +if (process.env.__IS_WSL_TEST__) { + isWsl$2.exports = isWsl$1; +} else { + isWsl$2.exports = isWsl$1(); +} + +const path$1 = require$$0__default$1["default"]; +const childProcess = require$$1__default$1["default"]; +const isWsl = isWsl$2.exports; + +var opn$1 = (target, opts) => { + if (typeof target !== 'string') { + return Promise.reject(new Error('Expected a `target`')); + } + + opts = Object.assign({wait: true}, opts); + + let cmd; + let appArgs = []; + let args = []; + const cpOpts = {}; + + if (Array.isArray(opts.app)) { + appArgs = opts.app.slice(1); + opts.app = opts.app[0]; + } + + if (process.platform === 'darwin') { + cmd = 'open'; + + if (opts.wait) { + args.push('-W'); + } + + if (opts.app) { + args.push('-a', opts.app); + } + } else if (process.platform === 'win32' || isWsl) { + cmd = 'cmd' + (isWsl ? '.exe' : ''); + args.push('/c', 'start', '""', '/b'); + target = target.replace(/&/g, '^&'); + + if (opts.wait) { + args.push('/wait'); + } + + if (opts.app) { + args.push(opts.app); + } + + if (appArgs.length > 0) { + args = args.concat(appArgs); + } + } else { + if (opts.app) { + cmd = opts.app; + } else { + const useSystemXdgOpen = process.versions.electron || process.platform === 'android'; + cmd = useSystemXdgOpen ? 'xdg-open' : path$1.join(__dirname, 'xdg-open'); + } + + if (appArgs.length > 0) { + args = args.concat(appArgs); + } + + if (!opts.wait) { + // `xdg-open` will block the process unless + // stdio is ignored and it's detached from the parent + // even if it's unref'd + cpOpts.stdio = 'ignore'; + cpOpts.detached = true; + } + } + + args.push(target); + + if (process.platform === 'darwin' && appArgs.length > 0) { + args.push('--args'); + args = args.concat(appArgs); + } + + const cp = childProcess.spawn(cmd, args, cpOpts); + + if (opts.wait) { + return new Promise((resolve, reject) => { + cp.once('error', reject); + + cp.once('close', code => { + if (code > 0) { + reject(new Error('Exited with code ' + code)); + return; + } + + resolve(cp); + }); + }); + } + + cp.unref(); + + return Promise.resolve(cp); +}; + +const DEFAULT_INDEX = 'index.html'; + +const HTTP_STATUS_OK = 200; +const HTTP_STATUS_PARTIAL_CONTENT = 206; +const HTTP_STATUS_NOT_MODIFIED = 304; +const HTTP_STATUS_ERR = 500; +const HTTP_STATUS_BAD_REQUEST = 400; +const HTTP_STATUS_FORBIDDEN = 403; +const HTTP_STATUS_NOT_FOUND = 404; +const HTTP_STATUS_INVALID_METHOD = 405; + +const VALID_HTTP_METHODS = ['GET', 'HEAD']; + +const RANGE_REQUEST_HEADER_TEST = /^bytes=/; +const RANGE_REQUEST_HEADER_PATTERN = /\d*-\d*/g; + +const TIME_MS_PRECISION = 3; + +const MULTIPART_SEPARATOR = '--MULTIPARTSEPERATORaufielqbghgzwr'; + +const NEWLINE = '\n'; + + +var EventEmitter = require$$0__default$2["default"].EventEmitter; +var util = require$$1__default$2["default"]; +var http = require$$2__default["default"]; +var url = require$$3__default["default"]; +var mime = mime_1; +var path = require$$0__default$1["default"]; +var fs = require$$1__default["default"]; +var opn = opn$1; + + +/** +Exposes the StaticServer class +*/ +var server_1 = StaticServer; + + +/** +Create a new instance of StaticServer class + +Options are : + - name the server name, what will be sent as "X-Powered-by" + - host the host interface where the server will listen to. If not specified, + the server will listen on any networking interfaces + - cors a cors header, will be sent as "Access-Control-Allow-Origin", + - port the listening port number + - rootPath the serving root path. Any file above that path will be denied + - followSymlink true to follow any symbolic link, false to forbid + - templates + - index the default index file to server for a directory (default 'index.html') + - notFound the 404 error template + - noCache disables 304 responses + - open open server in the local browser + +@param options {Object} +*/ +function StaticServer(options) { + options = options || {}; + + if (!options.rootPath) { + throw new Error('Root path not specified'); + } + + if(!options.templates){ + options.templates = {}; + } + + this.name = options.name; + this.host = options.host; + this.port = options.port; + this.cors = options.cors; + this.rootPath = path.resolve(options.rootPath); + this.followSymlink = !!options.followSymlink; + this.templates = { + 'index': (options.templates.index || DEFAULT_INDEX), + 'notFound': options.templates.notFound + }; + // the arguments parser converts `--no-XXXX` to `XXXX` with a value of false; + this.noCache = !options.cache; + this.open = options.open; + + if (options.index) { + console.log("options.index is now deprecated please use options.templates.index instead."); + this.templates.index = options.index; + } + + Object.defineProperty(this, '_socket', { + configurable: true, + enumerable: false, + writable: true, + value: null + }); + +} +util.inherits(StaticServer, EventEmitter); + +/** +Expose the http.STATUS_CODES object +*/ +StaticServer.STATUS_CODES = http.STATUS_CODES; + + +/** +Start listening on the given host:port + +@param callback {Function} the function to call once the server is ready +*/ +StaticServer.prototype.start = function start(callback) { + this._socket = http.createServer(requestHandler(this)).listen(this.port, this.host, callback); + if(this.open && this.port){ + opn('http://localhost:' + this.port); + } +}; + + +/** +Stop listening +*/ +StaticServer.prototype.stop = function stop() { + if (this._socket) { + this._socket.close(); + this._socket = null; + } +}; + + +/** +Return the server's request handler function + +@param server {StaticServer} server instance +@return {Function} +*/ +function requestHandler(server) { + return function handler(req, res) { + var uri = req.path = decodeURIComponent(url.parse(req.url).pathname); + var filename = path.join(server.rootPath, uri); + var timestamp = process.hrtime(); + + // add a property to get the elapsed time since the request was issued + Object.defineProperty(req, 'elapsedTime', { + get: function getElapsedTime() { + var elapsed = process.hrtime(timestamp); + return (elapsed[0] ? elapsed[0] + 's ' : '') + (elapsed[1] / 1000000).toFixed(TIME_MS_PRECISION) + 'ms'; + } + }); + + res.headers = {}; + if (server.name) { + res.headers['X-Powered-By'] = server.name; + } + + if (server.cors) { + res.headers['Access-Control-Allow-Origin'] = server.cors; + } + + server.emit('request', req, res); + + if (VALID_HTTP_METHODS.indexOf(req.method) === -1) { + return sendError(server, req, res, null, HTTP_STATUS_INVALID_METHOD); + } else if (!validPath(server.rootPath, filename)) { + return sendError(server, req, res, null, HTTP_STATUS_FORBIDDEN); + } + + getFileStats(server, [filename, path.join(filename, server.templates.index)], function (err, stat, file, index) { + if (err) { + handleError(server, req, res); + } else if (stat.isDirectory()) { + // + // TODO : handle directory listing here + // + sendError(server, req, res, null, HTTP_STATUS_FORBIDDEN); + } else { + sendFile(server, req, res, stat, file); + } + }); + }; +} + + +/** +Handle an error + +Currently assumes that the only error would be a 404 error. + +@param server {StaticServer} server instance +@param req {Object} request Object +@param res {Object} response Object +@param err {Object} the error to handle +*/ +function handleError(server, req, res, err){ + if(server.templates.notFound){ + getFileStats(server, [server.templates.notFound], function(err, stat, file, index){ + if (err) { + sendError(server, req, res, null, HTTP_STATUS_NOT_FOUND); + } else { + res.status = HTTP_STATUS_NOT_FOUND; + sendFile(server, req, res, stat, file); + } + }); + }else { + sendError(server, req, res, null, HTTP_STATUS_NOT_FOUND); + } +} + + + +/** +Check that path is valid so we don't access invalid resources + +@param rootPath {String} the server root path +@param file {String} the path to validate +*/ +function validPath(rootPath, file) { + var resolvedPath = path.resolve(rootPath, file); + + // only if we are still in the rootPath of the static site + return resolvedPath.indexOf(rootPath) === 0; +} + + +/** +Get stats for the given file(s). The function will return the stats for the +first valid (i.e. found) file or directory. + + getFile(server, ['file1', 'file2'], callback); + +The callback function receives four arguments; an error if any, a stats object, +the file name matching the stats found, and the actual index of the file from +the provided list of files. + +@param server {StaticServer} the StaticServer instance +@param files {Array} list of files +@param callback {Function} a callback function +*/ +function getFileStats(server, files, callback) { + var dirFound; + var dirStat; + var dirIndex; + + function checkNext(err, index) { + if (files.length) { + next(files.shift(), index + 1); + } else if (dirFound) { + // if a directory was found at some point, return it and ignore the error + callback(null, dirStat, dirFound, dirIndex); + } else { + callback(err || new Error('File not found')); + } + } + + function next(file, index) { + fs.lstat(file, function (err, stat) { + if (err) { + checkNext(err, index); + } else if (stat.isSymbolicLink()) { + if (server.followSymlink) { + fs.readlink(file, function (err, fileRef) { + if (err) { + checkNext(err, index); + } else { + if (!path.isAbsolute(fileRef)) { + fileRef = path.join( path.dirname(file), fileRef ); + } + server.emit('symbolicLink', fileRef); + next(fileRef, index); + } + }); + } else { + callback(new Error('Symbolic link not allowed')); + } + } else if (stat.isDirectory()) { + if (!dirFound) { + dirFound = file; + dirStat = stat; + dirIndex = index; + } + checkNext(null, index); + } else { + callback(null, stat, file, index); + } + }); + } + + checkNext(null, 0); +} + + +/** +Validate that this file is not client cached + +@param req {Object} the request object +@param res {Object} the response object +@return {boolean} true if the file is client cached +*/ +function validateClientCache(server, req, res, stat) { + var mtime = stat.mtime.getTime(); + var clientETag = req.headers['if-none-match']; + var clientMTime = Date.parse(req.headers['if-modified-since']); + + if (server.noCache) return false; + + if ((clientMTime || clientETag) && + (!clientETag || clientETag === res.headers['Etag']) && + (!clientMTime || clientMTime >= mtime)) { + + // NOT MODIFIED responses should not contain entity headers + [ + 'Content-Encoding', + 'Content-Language', + 'Content-Length', + 'Content-Location', + 'Content-MD5', + 'Content-Range', + 'Content-Type', + 'Expires', + 'Last-Modified' + ].forEach(function(entityHeader) { + delete res.headers[entityHeader]; + }); + + res.status = HTTP_STATUS_NOT_MODIFIED; + + res.writeHead(res.status, res.headers); + res.end(); + + server.emit('response', req, res); + + return true; + } else { + return false; + } +} + +function parseRanges(req, res, size) { + var ranges; + var start; + var end; + var i; + var originalSize = size; + + // support range headers + if (req.headers.range) { + // 'bytes=100-200,300-400' --> ['100-200','300-400'] + if (!RANGE_REQUEST_HEADER_TEST.test(req.headers.range)) { + return sendError(req, res, null, HTTP_STATUS_BAD_REQUEST, 'Invalid Range Headers: ' + req.headers.range); + } + + ranges = req.headers.range.match(RANGE_REQUEST_HEADER_PATTERN); + size = 0; + + if (!ranges) { + return sendError(server, req, res, null, HTTP_STATUS_BAD_REQUEST, 'Invalid Range Headers: ' + req.headers.range); + } + + i = ranges.length; + + while (--i >= 0) { + // 100-200 --> [100, 200] = bytes 100 to 200 + // -200 --> [null, 200] = last 100 bytes + // 100- --> [100, null] = bytes 100 to end + range = ranges[i].split('-'); + start = range[0] ? Number(range[0]) : null; + end = range[1] ? Number(range[1]) : null; + + // check if requested range is valid: + // - check it is within file range + // - check that start is smaller than end, if both are set + + if ((start > originalSize) || (end > originalSize) || ((start && end) && start > end)) { + res.headers['Content-Range'] = 'bytes=0-' + originalSize; + return sendError(server, req, res, null, DEFAULT_STATUS_REQUEST_RANGE_NOT_SATISFIABLE); + } + + // update size + if (start !== null && end !== null) { + size += (end - start); + ranges[i] = { start: start, end: end + 1 }; + } else if (start !== null) { + size += (originalSize - start); + ranges[i] = { start: start, end: originalSize + 1 }; + } else if (end !== null) { + size += end; + ranges[i] = { start: originalSize - end, end: originalSize }; + } + } + } + + return { + ranges: ranges, + size: size + }; +} + + +/** +Send error back to the client. If `status` is not specified, a value +of 500 is used. If `message` is not specified, the default message for +the given status is returned. + +@param server {StaticServer} the server instance +@param req {Object} the request object +@param res {Object} the response object +@param err {Object} an Error object, if any +@param status {Number} the status (default 500) +@param message {String} the status message (optional) +*/ +function sendError(server, req, res, err, status, message) { + status = status || res.status || HTTP_STATUS_ERR; + message = message || http.STATUS_CODES[status]; + + if (status >= 400) { + // ERR responses should not contain entity headers + [ + 'Content-Encoding', + 'Content-Language', + 'Content-Length', + 'Content-Location', + 'Content-MD5', + // 'Content-Range', // Error 416 SHOULD contain this header + 'Etag', + 'Expires', + 'Last-Modified' + ].forEach(function(entityHeader) { + delete res.headers[entityHeader]; + }); + + res.status = status; + res.headers['Content-Type'] = mime.lookup('text'); + + res.writeHead(status, res.headers); + res.write(message); + res.end(); + } + + server.emit('response', req, res, err); +} + + +/** +Send a file back at the client. If the file is not found, an error 404 +will be returned. If the file cannot be read, for any reason, an error 500 +will be read and the error will be sent to stderr + +@param server {StaticServer} the server instance +@param req {Object} the request object +@param res {Object} the response object +@param stat {Object} the actual file stat +@param file {String} the absolute file path +*/ +function sendFile(server, req, res, stat, file) { + var headersSent = false; + var contentParts = parseRanges(req, res, stat.size); + var streamOptions = { flags: 'r' }; + var contentType = mime.lookup(file); + var rangeIndex = 0; + + if (!contentParts) { + return; // ranges failed, abort + } + + if (!server.noCache) { + res.headers['Etag'] = JSON.stringify([stat.ino, stat.size, stat.mtime.getTime()].join('-')); + res.headers['Last-Modified'] = new Date(stat.mtime).toUTCString(); + } + + res.headers['Date'] = new Date().toUTCString(); + + if (contentParts.ranges && contentParts.ranges.length > 1) { + res.headers['Content-Type'] = 'multipart/byteranges; boundary=' + MULTIPART_SEPARATOR; + } else { + res.headers['Content-Type'] = contentType; + res.headers['Content-Length'] = contentParts.size; + + if (contentParts.ranges) { + res.headers['Content-Range'] = req.headers.range; + } + } + + // return only headers if request method is HEAD + if (req.method === 'HEAD') { + res.status = HTTP_STATUS_OK; + res.writeHead(HTTP_STATUS_OK, res.headers); + res.end(); + server.emit('response', req, res, null, file, stat); + } else if (!validateClientCache(server, req, res, stat)) { + + (function sendNext() { + var range; + + if (contentParts.ranges) { + range = contentParts.ranges[rangeIndex++]; + + streamOptions.start = range.start; + streamOptions.end = range.end; + } + + fs.createReadStream(file, streamOptions) + .on('close', function () { + // close response when there are no ranges defined + // or when the last range has been read + if (!range || (rangeIndex >= contentParts.ranges.length)) { + res.end(); + server.emit('response', req, res, null, file, stat); + } else { + setImmediate(sendNext); + } + }).on('open', function (fd) { + if (!headersSent) { + if (!res.status){ + if (range) { + res.status = HTTP_STATUS_PARTIAL_CONTENT; + } else { + res.status = HTTP_STATUS_OK; + } + } + res.writeHead(res.status, res.headers); + headersSent = true; + } + + if (range && contentParts.ranges.length > 1) { + res.write(MULTIPART_SEPARATOR + NEWLINE + + 'Content-Type: ' + contentType + NEWLINE + + 'Content-Range: ' + (range.start || '') + '-' + (range.end || '') + NEWLINE + NEWLINE); + } + }).on('error', function (err) { + sendError(server, req, res, err); + }).on('data', function (chunk) { + res.write(chunk); + }); + })(); + } + +} + +(function (exports) { + Object.defineProperty(exports, "__esModule", { value: true }); + exports.tester = exports.loadTester = exports.login = exports.generateSeedPhrase = exports.TEST_KERNEL_SKLINK = exports.KERNEL_HELPER_MODULE = exports.KERNEL_TEST_SUITE = void 0; + const libskynet_1 = require$$0; + const seed_1 = require$$1; + const dictionary_1 = require$$2$1; + const path = require$$0__default$1["default"]; + const libskynetnode_1 = require$$4; + const kernel = require$$5; + const crypto_1 = require$$6__default["default"]; + // @ts-ignore + const StaticServer = server_1; + exports.KERNEL_TEST_SUITE = "AQCPJ9WRzMpKQHIsPo8no3XJpUydcDCjw7VJy8lG1MCZ3g"; + exports.KERNEL_HELPER_MODULE = "AQCoaLP6JexdZshDDZRQaIwN3B7DqFjlY7byMikR7u1IEA"; + exports.TEST_KERNEL_SKLINK = "AQCw2_9rg0Fxuy8ky3pvLiDhcJTmAqthy1Buc7Frl2v2fA"; + const SEED_ENTROPY_WORDS = 13; + const crypto = crypto_1.webcrypto; + function generateSeedPhrase() { + // Get the random numbers for the seed phrase. Typically, you need to + // have code that avoids bias by checking the random results and + // re-rolling the random numbers if the result is outside of the range + // of numbers that would produce no bias. Because the search space + // (1024) evenly divides the random number space (2^16), we can skip + // this step and just use a modulus instead. The result will have no + // bias, but only because the search space is a power of 2. + let randNums = new Uint16Array(SEED_ENTROPY_WORDS); + crypto.getRandomValues(randNums); + // Generate the seed phrase from the randNums. + let seedWords = []; + for (let i = 0; i < SEED_ENTROPY_WORDS; i++) { + let wordIndex = randNums[i] % libskynet_1.dictionary.length; + seedWords.push(libskynet_1.dictionary[wordIndex]); + } + // Convert the seedWords to a seed. + let [seed] = seedWordsToSeed(seedWords); + // Compute the checksum. + let [checksumOne, checksumTwo, err2] = (0, seed_1.seedToChecksumWords)(seed); + // Assemble the final seed phrase and set the text field. + return [...seedWords, checksumOne, checksumTwo].join(" "); + } + exports.generateSeedPhrase = generateSeedPhrase; + function seedWordsToSeed(seedWords) { + // Input checking. + if (seedWords.length !== SEED_ENTROPY_WORDS) { + return [ + new Uint8Array(0), + `Seed words should have length ${SEED_ENTROPY_WORDS} but has length ${seedWords.length}`, + ]; + } + // We are getting 16 bytes of entropy. + let bytes = new Uint8Array(seed_1.SEED_BYTES); + let curByte = 0; + let curBit = 0; + for (let i = 0; i < SEED_ENTROPY_WORDS; i++) { + // Determine which number corresponds to the next word. + let word = -1; + for (let j = 0; j < libskynet_1.dictionary.length; j++) { + if (seedWords[i].slice(0, dictionary_1.DICTIONARY_UNIQUE_PREFIX) === + libskynet_1.dictionary[j].slice(0, dictionary_1.DICTIONARY_UNIQUE_PREFIX)) { + word = j; + break; + } + } + if (word === -1) { + return [ + new Uint8Array(0), + `word '${seedWords[i]}' at index ${i} not found in dictionary`, + ]; + } + let wordBits = 10; + if (i === SEED_ENTROPY_WORDS - 1) { + wordBits = 8; + } + // Iterate over the bits of the 10- or 8-bit word. + for (let j = 0; j < wordBits; j++) { + let bitSet = (word & (1 << (wordBits - j - 1))) > 0; + if (bitSet) { + bytes[curByte] |= 1 << (8 - curBit - 1); + } + curBit += 1; + if (curBit >= 8) { + // Current byte has 8 bits, go to the next byte. + curByte += 1; + curBit = 0; + } + } + } + return [bytes, null]; + } + async function login(page, seed = generateSeedPhrase()) { + await page.goto("http://skt.us"); + let userSeed; + [userSeed] = (0, libskynet_1.seedPhraseToSeed)(seed); + let seedHex = (0, libskynet_1.bufToHex)(userSeed); + await page.evaluate((seed) => { + window.localStorage.setItem("v1-seed", seed); + }, seedHex); + let kernelEntrySeed = (0, libskynet_1.deriveChildSeed)(userSeed, "userPreferredKernel2"); + // Get the registry keys. + let [keypair, dataKey] = (0, libskynet_1.taggedRegistryEntryKeys)(kernelEntrySeed, "user kernel"); + await (0, libskynetnode_1.overwriteRegistryEntry)(keypair, dataKey, (0, libskynet_1.b64ToBuf)(exports.TEST_KERNEL_SKLINK)[0]); + } + exports.login = login; + async function loadTester(page, port = 8080) { + const server = new StaticServer({ + rootPath: path.resolve(__dirname, "..", "public"), + port, + host: "localhost", + }); + await new Promise((resolve) => { + server.start(resolve); + }); + await page.goto(`http://localhost:${port}/`); + await page.evaluate(() => { + return kernel.init(); + }); + } + exports.loadTester = loadTester; + class Tester { + page; + constructor(page) { + this.page = page; + } + async callModule(id, method, data = {}) { + return this.page.evaluate(async (id, method, data) => { + return kernel.callModule(id, method, data); + }, id, method, data); + } + } + const tester = (page) => new Tester(page); + exports.tester = tester; +} (build)); + +var index = /*@__PURE__*/getDefaultExportFromCjs(build); + +let s = 0; +const S = { + START_BOUNDARY: s++, + HEADER_FIELD_START: s++, + HEADER_FIELD: s++, + HEADER_VALUE_START: s++, + HEADER_VALUE: s++, + HEADER_VALUE_ALMOST_DONE: s++, + HEADERS_ALMOST_DONE: s++, + PART_DATA_START: s++, + PART_DATA: s++, + END: s++ +}; + +let f = 1; +const F = { + PART_BOUNDARY: f, + LAST_BOUNDARY: f *= 2 +}; + +const LF = 10; +const CR = 13; +const SPACE = 32; +const HYPHEN = 45; +const COLON = 58; +const A = 97; +const Z = 122; + +const lower = c => c | 0x20; + +const noop = () => {}; + +class MultipartParser { + /** + * @param {string} boundary + */ + constructor(boundary) { + this.index = 0; + this.flags = 0; + + this.onHeaderEnd = noop; + this.onHeaderField = noop; + this.onHeadersEnd = noop; + this.onHeaderValue = noop; + this.onPartBegin = noop; + this.onPartData = noop; + this.onPartEnd = noop; + + this.boundaryChars = {}; + + boundary = '\r\n--' + boundary; + const ui8a = new Uint8Array(boundary.length); + for (let i = 0; i < boundary.length; i++) { + ui8a[i] = boundary.charCodeAt(i); + this.boundaryChars[ui8a[i]] = true; + } + + this.boundary = ui8a; + this.lookbehind = new Uint8Array(this.boundary.length + 8); + this.state = S.START_BOUNDARY; + } + + /** + * @param {Uint8Array} data + */ + write(data) { + let i = 0; + const length_ = data.length; + let previousIndex = this.index; + let {lookbehind, boundary, boundaryChars, index, state, flags} = this; + const boundaryLength = this.boundary.length; + const boundaryEnd = boundaryLength - 1; + const bufferLength = data.length; + let c; + let cl; + + const mark = name => { + this[name + 'Mark'] = i; + }; + + const clear = name => { + delete this[name + 'Mark']; + }; + + const callback = (callbackSymbol, start, end, ui8a) => { + if (start === undefined || start !== end) { + this[callbackSymbol](ui8a && ui8a.subarray(start, end)); + } + }; + + const dataCallback = (name, clear) => { + const markSymbol = name + 'Mark'; + if (!(markSymbol in this)) { + return; + } + + if (clear) { + callback(name, this[markSymbol], i, data); + delete this[markSymbol]; + } else { + callback(name, this[markSymbol], data.length, data); + this[markSymbol] = 0; + } + }; + + for (i = 0; i < length_; i++) { + c = data[i]; + + switch (state) { + case S.START_BOUNDARY: + if (index === boundary.length - 2) { + if (c === HYPHEN) { + flags |= F.LAST_BOUNDARY; + } else if (c !== CR) { + return; + } + + index++; + break; + } else if (index - 1 === boundary.length - 2) { + if (flags & F.LAST_BOUNDARY && c === HYPHEN) { + state = S.END; + flags = 0; + } else if (!(flags & F.LAST_BOUNDARY) && c === LF) { + index = 0; + callback('onPartBegin'); + state = S.HEADER_FIELD_START; + } else { + return; + } + + break; + } + + if (c !== boundary[index + 2]) { + index = -2; + } + + if (c === boundary[index + 2]) { + index++; + } + + break; + case S.HEADER_FIELD_START: + state = S.HEADER_FIELD; + mark('onHeaderField'); + index = 0; + // falls through + case S.HEADER_FIELD: + if (c === CR) { + clear('onHeaderField'); + state = S.HEADERS_ALMOST_DONE; + break; + } + + index++; + if (c === HYPHEN) { + break; + } + + if (c === COLON) { + if (index === 1) { + // empty header field + return; + } + + dataCallback('onHeaderField', true); + state = S.HEADER_VALUE_START; + break; + } + + cl = lower(c); + if (cl < A || cl > Z) { + return; + } + + break; + case S.HEADER_VALUE_START: + if (c === SPACE) { + break; + } + + mark('onHeaderValue'); + state = S.HEADER_VALUE; + // falls through + case S.HEADER_VALUE: + if (c === CR) { + dataCallback('onHeaderValue', true); + callback('onHeaderEnd'); + state = S.HEADER_VALUE_ALMOST_DONE; + } + + break; + case S.HEADER_VALUE_ALMOST_DONE: + if (c !== LF) { + return; + } + + state = S.HEADER_FIELD_START; + break; + case S.HEADERS_ALMOST_DONE: + if (c !== LF) { + return; + } + + callback('onHeadersEnd'); + state = S.PART_DATA_START; + break; + case S.PART_DATA_START: + state = S.PART_DATA; + mark('onPartData'); + // falls through + case S.PART_DATA: + previousIndex = index; + + if (index === 0) { + // boyer-moore derrived algorithm to safely skip non-boundary data + i += boundaryEnd; + while (i < bufferLength && !(data[i] in boundaryChars)) { + i += boundaryLength; + } + + i -= boundaryEnd; + c = data[i]; + } + + if (index < boundary.length) { + if (boundary[index] === c) { + if (index === 0) { + dataCallback('onPartData', true); + } + + index++; + } else { + index = 0; + } + } else if (index === boundary.length) { + index++; + if (c === CR) { + // CR = part boundary + flags |= F.PART_BOUNDARY; + } else if (c === HYPHEN) { + // HYPHEN = end boundary + flags |= F.LAST_BOUNDARY; + } else { + index = 0; + } + } else if (index - 1 === boundary.length) { + if (flags & F.PART_BOUNDARY) { + index = 0; + if (c === LF) { + // unset the PART_BOUNDARY flag + flags &= ~F.PART_BOUNDARY; + callback('onPartEnd'); + callback('onPartBegin'); + state = S.HEADER_FIELD_START; + break; + } + } else if (flags & F.LAST_BOUNDARY) { + if (c === HYPHEN) { + callback('onPartEnd'); + state = S.END; + flags = 0; + } else { + index = 0; + } + } else { + index = 0; + } + } + + if (index > 0) { + // when matching a possible boundary, keep a lookbehind reference + // in case it turns out to be a false lead + lookbehind[index - 1] = c; + } else if (previousIndex > 0) { + // if our boundary turned out to be rubbish, the captured lookbehind + // belongs to partData + const _lookbehind = new Uint8Array(lookbehind.buffer, lookbehind.byteOffset, lookbehind.byteLength); + callback('onPartData', 0, previousIndex, _lookbehind); + previousIndex = 0; + mark('onPartData'); + + // reconsider the current character even so it interrupted the sequence + // it could be the beginning of a new sequence + i--; + } + + break; + case S.END: + break; + default: + throw new Error(`Unexpected state entered: ${state}`); + } + } + + dataCallback('onHeaderField'); + dataCallback('onHeaderValue'); + dataCallback('onPartData'); + + // Update properties for the next call + this.index = index; + this.state = state; + this.flags = flags; + } + + end() { + if ((this.state === S.HEADER_FIELD_START && this.index === 0) || + (this.state === S.PART_DATA && this.index === this.boundary.length)) { + this.onPartEnd(); + } else if (this.state !== S.END) { + throw new Error('MultipartParser.end(): stream ended unexpectedly'); + } + } +} + +function _fileName(headerValue) { + // matches either a quoted-string or a token (RFC 2616 section 19.5.1) + const m = headerValue.match(/\bfilename=("(.*?)"|([^()<>@,;:\\"/[\]?={}\s\t]+))($|;\s)/i); + if (!m) { + return; + } + + const match = m[2] || m[3] || ''; + let filename = match.slice(match.lastIndexOf('\\') + 1); + filename = filename.replace(/%22/g, '"'); + filename = filename.replace(/&#(\d{4});/g, (m, code) => { + return String.fromCharCode(code); + }); + return filename; +} + +async function toFormData(Body, ct) { + if (!/multipart/i.test(ct)) { + throw new TypeError('Failed to fetch'); + } + + const m = ct.match(/boundary=(?:"([^"]+)"|([^;]+))/i); + + if (!m) { + throw new TypeError('no or bad content-type header, no multipart boundary'); + } + + const parser = new MultipartParser(m[1] || m[2]); + + let headerField; + let headerValue; + let entryValue; + let entryName; + let contentType; + let filename; + const entryChunks = []; + const formData = new FormData(); + + const onPartData = ui8a => { + entryValue += decoder.decode(ui8a, {stream: true}); + }; + + const appendToFile = ui8a => { + entryChunks.push(ui8a); + }; + + const appendFileToFormData = () => { + const file = new File(entryChunks, filename, {type: contentType}); + formData.append(entryName, file); + }; + + const appendEntryToFormData = () => { + formData.append(entryName, entryValue); + }; + + const decoder = new TextDecoder('utf-8'); + decoder.decode(); + + parser.onPartBegin = function () { + parser.onPartData = onPartData; + parser.onPartEnd = appendEntryToFormData; + + headerField = ''; + headerValue = ''; + entryValue = ''; + entryName = ''; + contentType = ''; + filename = null; + entryChunks.length = 0; + }; + + parser.onHeaderField = function (ui8a) { + headerField += decoder.decode(ui8a, {stream: true}); + }; + + parser.onHeaderValue = function (ui8a) { + headerValue += decoder.decode(ui8a, {stream: true}); + }; + + parser.onHeaderEnd = function () { + headerValue += decoder.decode(); + headerField = headerField.toLowerCase(); + + if (headerField === 'content-disposition') { + // matches either a quoted-string or a token (RFC 2616 section 19.5.1) + const m = headerValue.match(/\bname=("([^"]*)"|([^()<>@,;:\\"/[\]?={}\s\t]+))/i); + + if (m) { + entryName = m[2] || m[3] || ''; + } + + filename = _fileName(headerValue); + + if (filename) { + parser.onPartData = appendToFile; + parser.onPartEnd = appendFileToFormData; + } + } else if (headerField === 'content-type') { + contentType = headerValue; + } + + headerValue = ''; + headerField = ''; + }; + + for await (const chunk of Body) { + parser.write(chunk); + } + + parser.end(); + + return formData; +} + +var multipartParser = /*#__PURE__*/Object.freeze({ + __proto__: null, + toFormData: toFormData +}); + +module.exports = index; diff --git a/dist/tester.d.ts b/dist/tester.d.ts new file mode 100644 index 0000000..cb0ff5c --- /dev/null +++ b/dist/tester.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/public/tester.js b/public/tester.js new file mode 100644 index 0000000..c6650fa --- /dev/null +++ b/public/tester.js @@ -0,0 +1,5199 @@ +(function () { + 'use strict'; + + var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}; + + function getAugmentedNamespace(n) { + var f = n.default; + if (typeof f == "function") { + var a = function () { + return f.apply(this, arguments); + }; + a.prototype = f.prototype; + } else a = {}; + Object.defineProperty(a, '__esModule', {value: true}); + Object.keys(n).forEach(function (k) { + var d = Object.getOwnPropertyDescriptor(n, k); + Object.defineProperty(a, k, d.get ? d : { + enumerable: true, + get: function () { + return n[k]; + } + }); + }); + return a; + } + + var tester = {}; + + // log provides a wrapper for console.log that prefixes '[libkernel]' to the + // output. + function log(...inputs) { + console.log("[libkernel]", ...inputs); + } + // logErr provides a wrapper for console.error that prefixes '[libkernel]' to + // the output. + function logErr(...inputs) { + console.error("[libkernel]", ...inputs); + } + + // tryStringify will try to turn the provided input into a string. If the input + // object is already a string, the input object will be returned. If the input + // object has a toString method, the toString method will be called. If that + // fails, we try to call JSON.stringify on the object. And if that fails, we + // set the return value to "[stringify failed]". + function tryStringify$1(obj) { + // Check for undefined input. + if (obj === undefined || obj === null) { + return "[cannot stringify undefined input]"; + } + // Parse the error into a string. + if (typeof obj === "string") { + return obj; + } + // Check if the object has a custom toString and use that if so. + let hasToString = typeof obj.toString === "function"; + if (hasToString && obj.toString !== Object.prototype.toString) { + return obj.toString(); + } + // If the object does not have a custom toString, attempt to perform a + // JSON.stringify. + try { + return JSON.stringify(obj); + } + catch { + return "[stringify failed]"; + } + } + + // addContextToErr is a helper function that standardizes the formatting of + // adding context to an error. Within the world of go we discovered that being + // persistent about layering context onto errors is helpful when debugging, + // even though it often creates rather verbose error messages. + // + // addContextToErr will return null if the input err is null. + // + // NOTE: To protect against accidental situations where an Error type or some + // other type is provided instead of a string, we wrap both of the inputs with + // tryStringify before returning them. This prevents runtime failures. + function addContextToErr$1(err, context) { + if (err === null) { + err = "[no error provided]"; + } + return tryStringify$1(context) + ": " + tryStringify$1(err); + } + // composeErr takes a series of inputs and composes them into a single string. + // Each element will be separated by a newline. If the input is not a string, + // it will be transformed into a string with JSON.stringify. + // + // Any object that cannot be stringified will be skipped, though an error will + // be logged. + function composeErr$1(...inputs) { + let result = ""; + let resultEmpty = true; + for (let i = 0; i < inputs.length; i++) { + if (inputs[i] === null) { + continue; + } + if (resultEmpty) { + resultEmpty = false; + } + else { + result += "\n"; + } + result += tryStringify$1(inputs[i]); + } + if (resultEmpty) { + return null; + } + return result; + } + + // Helper consts to make it easy to return empty values alongside errors. + const nu8$7 = new Uint8Array(0); + // bufToB64 will convert a Uint8Array to a base64 string with URL encoding and + // no padding characters. + function bufToB64$1(buf) { + let b64Str = btoa(String.fromCharCode.apply(null, buf)); + return b64Str.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, ""); + } + // encodeU64 will encode a bigint in the range of a uint64 to an 8 byte + // Uint8Array. + function encodeU64$1(num) { + // Check the bounds on the bigint. + if (num < 0) { + return [nu8$7, "expected a positive integer"]; + } + if (num > 18446744073709551615n) { + return [nu8$7, "expected a number no larger than a uint64"]; + } + // Encode the bigint into a Uint8Array. + let encoded = new Uint8Array(8); + for (let i = 0; i < encoded.length; i++) { + let byte = Number(num & 0xffn); + encoded[i] = byte; + num = num >> 8n; + } + return [encoded, null]; + } + + let gfi$1 = function (init) { + let i, r = new Float64Array(16); + if (init) + for (i = 0; i < init.length; i++) + r[i] = init[i]; + return r; + }; + gfi$1([1]); gfi$1([ + 0x78a3, 0x1359, 0x4dca, 0x75eb, 0xd8ab, 0x4141, 0x0a4d, 0x0070, 0xe898, 0x7779, 0x4079, 0x8cc7, 0xfe73, 0x2b6f, + 0x6cee, 0x5203, + ]); gfi$1([ + 0xf159, 0x26b2, 0x9b94, 0xebd6, 0xb156, 0x8283, 0x149a, 0x00e0, 0xd130, 0xeef3, 0x80f2, 0x198e, 0xfce7, 0x56df, + 0xd9dc, 0x2406, + ]); gfi$1([ + 0xd51a, 0x8f25, 0x2d60, 0xc956, 0xa7b2, 0x9525, 0xc760, 0x692c, 0xdc5c, 0xfdd6, 0xe231, 0xc0a4, 0x53fe, 0xcd6e, + 0x36d3, 0x2169, + ]); gfi$1([ + 0x6658, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, + 0x6666, 0x6666, + ]); gfi$1([ + 0xa0b0, 0x4a0e, 0x1b27, 0xc4ee, 0xe478, 0xad2f, 0x1806, 0x2f43, 0xd7a7, 0x3dfb, 0x0099, 0x2b4d, 0xdf0b, 0x4fc1, + 0x2480, 0x2b83, + ]); + + // Create the queryMap. + let queries = {}; + // Define the nonce handling. nonceSeed is 16 random bytes that get generated + // at init and serve as the baseline for creating random nonces. nonceCounter + // tracks which messages have been sent. We hash together the nonceSeed and the + // current nonceCounter to get a secure nonce. + // + // We need a secure nonce so that we know which messages from the kernel are + // intended for us. There could be multiple pieces of independent code talking + // to the kernel and using nonces, by having secure random nonces we can + // guarantee that the applications will not use conflicting nonces. + let nonceSeed; + let nonceCounter; + function initNonce() { + nonceSeed = new Uint8Array(16); + nonceCounter = 0; + crypto.getRandomValues(nonceSeed); + } + // nextNonce will combine the nonceCounter with the nonceSeed to produce a + // unique string that can be used as the nonce with the kernel. + // + // Note: the nonce is only ever going to be visible to the kernel and to other + // code running in the same webpage, so we don't need to hash our nonceSeed. We + // just need it to be unique, not undetectable. + function nextNonce() { + let nonceNum = nonceCounter; + nonceCounter += 1; + let [nonceNumBytes, err] = encodeU64$1(BigInt(nonceNum)); + if (err !== null) { + // encodeU64 only fails if nonceNum is outside the bounds of a + // uint64, which shouldn't happen ever. + logErr("encodeU64 somehow failed", err); + } + let noncePreimage = new Uint8Array(nonceNumBytes.length + nonceSeed.length); + noncePreimage.set(nonceNumBytes, 0); + noncePreimage.set(nonceSeed, nonceNumBytes.length); + return bufToB64$1(noncePreimage); + } + // Establish the handler for incoming messages. + function handleMessage(event) { + // Ignore all messages that aren't from approved kernel sources. The two + // approved sources are skt.us and the browser extension bridge (which has + // an event.source equal to 'window') + if (event.source !== window && event.origin !== "https://skt.us") { + return; + } + // Ignore any messages that don't have a method and data field. + if (!("method" in event.data) || !("data" in event.data)) { + return; + } + // Handle logging messages. + if (event.data.method === "log") { + // We display the logging message if the kernel is a browser + // extension, so that the kernel's logs appear in the app + // console as well as the extension console. If the kernel is + // in an iframe, its logging messages will already be in the + // app console and therefore don't need to be displayed. + if (kernelOrigin === window.origin) { + if (event.data.data.isErr) { + console.error(event.data.data.message); + } + else { + console.log(event.data.data.message); + } + } + return; + } + // init is complete when the kernel sends us the auth status. If the + // user is logged in, report success, otherwise return an error + // indicating that the user is not logged in. + if (event.data.method === "kernelAuthStatus") { + // If we have received an auth status message, it means the bootloader + // at a minimum is working. + if (initResolved === false) { + initResolved = true; + initResolve(); + } + // If the auth status message says that login is complete, it means + // that the user is logged in. + if (loginResolved === false && event.data.data.loginComplete === true) { + loginResolved = true; + loginResolve(); + } + // If the auth status message says that the kernel loaded, it means + // that the kernel is ready to receive messages. + if (kernelLoadedResolved === false && event.data.data.kernelLoaded !== "not yet") { + kernelLoadedResolved = true; + if (event.data.data.kernelLoaded === "success") { + kernelLoadedResolve(null); + } + else { + kernelLoadedResolve(event.data.data.kernelLoaded); + } + } + // If we have received a message indicating that the user has logged + // out, we need to reload the page and reset the auth process. + if (event.data.data.logoutComplete === true) { + { + logoutResolve(); + } + window.location.reload(); + } + return; + } + // Check that the message sent has a nonce. We don't log + // on failure because the message may have come from 'window', which + // will happen if the app has other messages being sent to the window. + if (!("nonce" in event.data)) { + return; + } + // If we can't locate the nonce in the queries map, there is nothing to do. + // This can happen especially for responseUpdate messages. + if (!(event.data.nonce in queries)) { + return; + } + let query = queries[event.data.nonce]; + // Handle a response. Once the response has been received, it is safe to + // delete the query from the queries map. + if (event.data.method === "response") { + queries[event.data.nonce].resolve([event.data.data, event.data.err]); + delete queries[event.data.nonce]; + return; + } + // Handle a response update. + if (event.data.method === "responseUpdate") { + // If no update handler was provided, there is nothing to do. + if (typeof query.receiveUpdate === "function") { + query.receiveUpdate(event.data.data); + } + return; + } + // Handle a responseNonce. + if (event.data.method === "responseNonce") { + if (typeof query.kernelNonceReceived === "function") { + query.kernelNonceReceived(event.data.data.nonce); + } + return; + } + // Ignore any other messages as they might be from other applications. + } + // launchKernelFrame will launch the skt.us iframe that is used to connect to the + // Skynet kernel if the kernel cannot be reached through the browser extension. + function launchKernelFrame() { + let iframe = document.createElement("iframe"); + iframe.src = "https://skt.us"; + iframe.width = "0"; + iframe.height = "0"; + iframe.style.border = "0"; + iframe.style.position = "absolute"; + document.body.appendChild(iframe); + kernelSource = iframe.contentWindow; + kernelOrigin = "https://skt.us"; + kernelAuthLocation = "https://skt.us/auth.html"; + // Set a timer to fail the login process if the kernel doesn't load in + // time. + setTimeout(() => { + if (initResolved === true) { + return; + } + initResolved = true; + initResolve("tried to open kernel in iframe, but hit a timeout"); + }, 24000); + } + // messageBridge will send a message to the bridge of the skynet extension to + // see if it exists. If it does not respond or if it responds with an error, + // messageBridge will open an iframe to skt.us and use that as the kernel. + let kernelSource; + let kernelOrigin; + let kernelAuthLocation; + function messageBridge() { + // Establish the function that will handle the bridge's response. + let bridgeInitComplete = false; + let bridgeResolve = () => { }; // Need to set bridgeResolve here to make tsc happy + let p = new Promise((resolve) => { + bridgeResolve = resolve; + }); + p.then(([, err]) => { + // Check if the timeout already elapsed. + if (bridgeInitComplete === true) { + logErr("received response from bridge, but init already finished"); + return; + } + bridgeInitComplete = true; + // Deconstruct the input and return if there's an error. + if (err !== null) { + logErr("bridge exists but returned an error", err); + launchKernelFrame(); + return; + } + // Bridge has responded successfully, and there's no error. + kernelSource = window; + kernelOrigin = window.origin; + kernelAuthLocation = "http://kernel.skynet/auth.html"; + console.log("established connection to bridge, using browser extension for kernel"); + }); + // Add the handler to the queries map. + let nonce = nextNonce(); + queries[nonce] = { + resolve: bridgeResolve, + }; + // Send a message to the bridge of the browser extension to determine + // whether the bridge exists. + window.postMessage({ + nonce, + method: "kernelBridgeVersion", + }, window.origin); + // Set a timeout, if we do not hear back from the bridge in 500 + // milliseconds we assume that the bridge is not available. + setTimeout(() => { + // If we've already received and processed a message from the + // bridge, there is nothing to do. + if (bridgeInitComplete === true) { + return; + } + bridgeInitComplete = true; + log("browser extension not found, falling back to skt.us"); + launchKernelFrame(); + }, 500); + return initPromise; + } + // init is a function that returns a promise which will resolve when + // initialization is complete. + // + // The init / auth process has 5 stages. The first stage is that something + // somewhere needs to call init(). It is safe to call init() multiple times, + // thanks to the 'initialized' variable. + let initialized = false; // set to true once 'init()' has been called + let initResolved = false; // set to true once we know the bootloader is working + let initResolve; + let initPromise; + let loginResolved = false; // set to true once we know the user is logged in + let loginResolve; + let loginPromise; + let kernelLoadedResolved = false; // set to true once the user kernel is loaded + let kernelLoadedResolve; + let kernelLoadedPromise; + let logoutResolve; + let logoutPromise; + function init() { + // If init has already been called, just return the init promise. + if (initialized === true) { + return initPromise; + } + initialized = true; + // Run all of the init functions. + initNonce(); + window.addEventListener("message", handleMessage); + messageBridge(); + // Create the promises that resolve at various stages of the auth flow. + initPromise = new Promise((resolve) => { + initResolve = resolve; + }); + loginPromise = new Promise((resolve) => { + loginResolve = resolve; + }); + kernelLoadedPromise = new Promise((resolve) => { + kernelLoadedResolve = resolve; + }); + logoutPromise = new Promise((resolve) => { + logoutResolve = resolve; + }); + // Return the initPromise, which will resolve when bootloader init is + // complete. + return initPromise; + } + // callModule is a generic function to call a module. The first input is the + // module identifier (typically a skylink), the second input is the method + // being called on the module, and the final input is optional and contains + // input data to be passed to the module. The input data will depend on the + // module and the method that is being called. The return value is an errTuple + // that contains the module's response. The format of the response is an + // arbitrary object whose fields depend on the module and method being called. + // + // callModule can only be used for query-response communication, there is no + // support for sending or receiving updates. + function callModule(module, method, data) { + let moduleCallData = { + module, + method, + data, + }; + let [, query] = newKernelQuery("moduleCall", moduleCallData, false); + return query; + } + // connectModule is the standard function to send a query to a module that can + // optionally send and optionally receive updates. The first three inputs match + // the inputs of 'callModule', and the fourth input is a function that will be + // called any time that the module sends a responseUpdate. The receiveUpdate + // function should have the following signature: + // + // `function receiveUpdate(data: any)` + // + // The structure of the data will depend on the module and method that was + // queried. + // + // The first return value is a 'sendUpdate' function that can be called to send + // a queryUpdate to the module. The sendUpdate function has the same signature + // as the receiveUpdate function, it's an arbitrary object whose fields depend + // on the module and method being queried. + // + // The second return value is a promise that returns an errTuple. It will + // resolve when the module sends a response message, and works the same as the + // return value of callModule. + function connectModule(module, method, data, receiveUpdate) { + let moduleCallData = { + module, + method, + data, + }; + return newKernelQuery("moduleCall", moduleCallData, true, receiveUpdate); + } + // newKernelQuery opens a query to the kernel. Details like postMessage + // communication and nonce handling are all abstracted away by newKernelQuery. + // + // The first arg is the method that is being called on the kernel, and the + // second arg is the data that will be sent to the kernel as input to the + // method. + // + // The thrid arg is an optional function that can be passed in to receive + // responseUpdates to the query. Not every query will send responseUpdates, and + // most responseUpdates can be ignored, but sometimes contain useful + // information like download progress. + // + // The first output is a 'sendUpdate' function that can be called to send a + // queryUpdate. The second output is a promise that will resolve when the query + // receives a response message. Once the response message has been received, no + // more updates can be sent or received. + function newKernelQuery(method, data, sendUpdates, receiveUpdate) { + // NOTE: The implementation here is gnarly, because I didn't want to use + // async/await (that decision should be left to the caller) and I also + // wanted this function to work correctly even if init() had not been + // called yet. + // + // This function returns a sendUpdate function along with a promise, so we + // can't simply wrap everything in a basic promise. The sendUpdate function + // has to block internally until all of the setup is complete, and then we + // can't send a query until all of the setup is complete, and the setup + // cylce has multiple dependencies and therefore we get a few promises that + // all depend on each other. + // + // Using async/await here actually breaks certain usage patterns (or at + // least makes them much more difficult to use correctly). The standard way + // to establish duplex communication using connectModule is to define a + // variable 'sendUpdate' before defining the function 'receiveUpdate', and + // then setting 'sendUpdate' equal to the first return value of + // 'connectModue'. It looks like this: + // + // let sendUpdate; + // let receiveUpdate = function(data: any) { + // if (data.needsUpdate) { + // sendUpdate(someUpdate) + // } + // } + // let [sendUpdateFn, response] = connectModule(x, y, z, receiveUpdate) + // sendUpdate = sendUpdateFn + // + // If we use async/await, it's not safe to set sendUpdate after + // connectModule returns because 'receiveUpdate' may be called before + // 'sendUpdate' is set. You can fix that by using a promise, but it's a + // complicated fix and we want this library to be usable by less + // experienced developers. + // + // Therefore, we make an implementation tradeoff here and avoid async/await + // at the cost of having a bunch of complicated promise chaining. + // Create a promise that will resolve once the nonce is available. We + // cannot get the nonce until init() is complete. getNonce therefore + // implies that init is complete. + let getNonce = new Promise((resolve) => { + init().then(() => { + kernelLoadedPromise.then(() => { + resolve(nextNonce()); + }); + }); + }); + // Two promises are being created at once here. Once is 'p', which will be + // returned to the caller of newKernelQuery and will be resolved when the + // kernel provides a 'response' message. The other is for internal use and + // will resolve once the query has been created. + let p; + let queryCreated; + let haveQueryCreated = new Promise((resolve) => { + queryCreated = resolve; + p = new Promise((resolve) => { + getNonce.then((nonce) => { + queries[nonce] = { resolve }; + if (receiveUpdate !== null && receiveUpdate !== undefined) { + queries[nonce]["receiveUpdate"] = receiveUpdate; + } + queryCreated(nonce); + }); + }); + }); + // Create a promise that will be resolved once we are ready to receive the + // kernelNonce. We won't be ready to receive the kernel nonce until after + // the queries[nonce] object has been created. + let readyForKernelNonce; + let getReadyForKernelNonce = new Promise((resolve) => { + readyForKernelNonce = resolve; + }); + // Create the sendUpdate function. It defaults to doing nothing. After the + // sendUpdate function is ready to receive the kernelNonce, resolve the + // promise that blocks until the sendUpdate function is ready to receive + // the kernel nonce. + let sendUpdate; + if (sendUpdates !== true) { + sendUpdate = () => { }; + readyForKernelNonce(); // We won't get a kernel nonce, no reason to block. + } + else { + // sendUpdate will send an update to the kernel. The update can't be + // sent until the kernel nonce is known. Create a promise that will + // resolve when the kernel nonce is known. + // + // This promise cannot itself be created until the queries[nonce] + // object has been created, so block for the query to be created. + let blockForKernelNonce = new Promise((resolve) => { + haveQueryCreated.then((nonce) => { + queries[nonce]["kernelNonceReceived"] = resolve; + readyForKernelNonce(); + }); + }); + // The sendUpdate function needs both the local nonce and also the + // kernel nonce. Block for both. Having the kernel nonce implies that + // the local nonce is ready, therefore start by blocking for the kernel + // nonce. + sendUpdate = function (updateData) { + blockForKernelNonce.then((nonce) => { + kernelSource.postMessage({ + method: "queryUpdate", + nonce, + data: updateData, + }, kernelOrigin); + }); + }; + } + // Prepare to send the query to the kernel. The query cannot be sent until + // the queries object is created and also we are ready to receive the + // kernel nonce. + haveQueryCreated.then((nonce) => { + getReadyForKernelNonce.then(() => { + // There are two types of messages we can send depending on whether + // we are talking to skt.us or the background script. + let kernelMessage = { + method, + nonce, + data, + sendKernelNonce: sendUpdates, + }; + let backgroundMessage = { + method: "newKernelQuery", + nonce, + data: kernelMessage, + }; + // The message structure needs to adjust based on whether we are + // talking directly to the kernel or whether we are talking to the + // background page. + if (kernelOrigin === "https://skt.us") { + kernelSource.postMessage(kernelMessage, kernelOrigin); + } + else { + kernelSource.postMessage(backgroundMessage, kernelOrigin); + } + }); + }); + // Return sendUpdate and the promise. sendUpdate is already set to block + // until all the necessary prereqs are complete. + return [sendUpdate, p]; + } + + // There are 5 stages of auth. + // + // Stage 0: Bootloader is not loaded. + // Stage 1: Bootloader is loaded, user is not logged in. + // Stage 2: Bootloader is loaded, user is logged in. + // Stage 3: Kernel is loaded, user is logged in. + // Stage 4: Kernel is loaded, user is logged out. + // + // init() will block until auth has reached stage 1. If the user is already + // logged in from a previous session, auth will immediately progress to stage + // 2. + // + // loginComplete() will block until auth has reached stage 2. The kernel is not + // ready to receive messages yet, but apps do not need to present users with a + // login dialog. + // + // kernelLoaded() will block until auth has reached stage 3. kernelLoaded() + // returns a promise that can resolve with an error. If there was an error, it + // means the kernel could not be loaded and cannot be used. + // + // logoutComplete() will block until auth has reached stage 4. libkernel does + // not support resetting the auth stages, once stage 4 has been reached the app + // needs to refresh. + // kernelLoaded will resolve when the user has successfully loaded the kernel. + // If there was an error in loading the kernel, the error will be returned. + // + // NOTE: kernelLoaded will not resolve until after loginComplete has resolved. + function kernelLoaded() { + return kernelLoadedPromise; + } + // loginComplete will resolve when the user has successfully logged in. + function loginComplete() { + return loginPromise; + } + // logoutComplete will resolve when the user has logged out. Note that + // logoutComplete will only resolve if the user logged in first - if the user + // was not logged in to begin with, this promise will not resolve. + function logoutComplete() { + return logoutPromise; + } + // openAuthWindow is intended to be used as an onclick target when the user + // clicks the 'login' button on a skynet application. It will block until the + // auth location is known, and then it will pop open the correct auth window + // for the user. + // + // NOTE: openAuthWindow will only open a window if the user is not already + // logged in. If the user is already logged in, this function is a no-op. + // + // NOTE: When using this function, you probably want to have your login button + // faded out or presenting the user with a spinner until init() resolves. In + // the worst case (user has no browser extension, and is on a slow internet + // connection) this could take multiple seconds. + function openAuthWindow() { + // openAuthWindow doesn't care what the auth status is, it's just trying to + // open the right window. + init().then(() => { + window.open(kernelAuthLocation, "_blank"); + }); + } + + // download will take a skylink and return the file data for that skylink. The + // download occurs using a kernel module that verifies the data's integrity and + // prevents the portal from lying about the download. + function download(skylink) { + return new Promise((resolve) => { + let downloadModule = "AQCIaQ0P-r6FwPEDq3auCZiuH_jqrHfqRcY7TjZ136Z_Yw"; + let data = { + skylink, + }; + callModule(downloadModule, "secureDownload", data).then(([result, err]) => { + // Pull the fileData out of the result. + if (err !== null) { + resolve([new Uint8Array(0), addContextToErr$1(err, "unable to complete download")]); + return; + } + resolve([result.fileData, null]); + }); + }); + } + + // registryRead will perform a registry read on a portal. readEntry does not + // guarantee that the latest revision has been provided, however it does + // guarantee that the provided data has a matching signature. + // + // registryRead returns the full registry entry object provided by the module + // because the object is relatively complex and all of the fields are more or + // less required. + function registryRead(publicKey, dataKey) { + let registryModule = "AQCovesg1AXUzKXLeRzQFILbjYMKr_rvNLsNhdq5GbYb2Q"; + let data = { + publicKey, + dataKey, + }; + return callModule(registryModule, "readEntry", data); + } + // registryWrite will perform a registry write on a portal. + // + // registryWrite is not considered a safe function, there are easy ways to + // misuse registryWrite such that user data will be lost. We recommend using a + // safe set of functions for writing to the registry such as getsetjson. + function registryWrite(keypair, dataKey, entryData, revision) { + return new Promise((resolve) => { + let registryModule = "AQCovesg1AXUzKXLeRzQFILbjYMKr_rvNLsNhdq5GbYb2Q"; + let callData = { + publicKey: keypair.publicKey, + secretKey: keypair.secretKey, + dataKey, + entryData, + revision, + }; + callModule(registryModule, "writeEntry", callData).then(([result, err]) => { + if (err !== null) { + resolve(["", err]); + return; + } + resolve([result.entryID, null]); + }); + }); + } + + // upload will take a filename and some file data and perform a secure upload + // to Skynet. All data is verified and the correct Skylink is returned. This + // function cannot fully guarantee that the data was pinned, but it can fully + // guarantee that the final skylink matches the data that was presented for the + // upload. + function upload(filename, fileData) { + return new Promise((resolve) => { + // Prepare the module call. + let uploadModule = "AQAT_a0MzOInZoJzt1CwBM2U8oQ3GIfP5yKKJu8Un-SfNg"; + let data = { + filename, + fileData, + }; + callModule(uploadModule, "secureUpload", data).then(([result, err]) => { + // Pull the skylink out of the result. + if (err !== null) { + resolve(["", addContextToErr$1(err, "uable to complete upload")]); + return; + } + resolve([result.skylink, null]); + }); + }); + } + + // kernelVersion will fetch the version number of the kernel. If successful, + // the returned value will be an object containing a field 'version' with a + // version string, and a 'distribtion' field with a string that states the + // distribution of the kernel". + function kernelVersion() { + return new Promise((resolve) => { + let [, query] = newKernelQuery("version", {}, false); + query.then(([result, err]) => { + if (err !== null) { + resolve(["", "", err]); + return; + } + resolve([result.version, result.distribution, err]); + }); + }); + } + + var dist$1 = /*#__PURE__*/Object.freeze({ + __proto__: null, + kernelLoaded: kernelLoaded, + loginComplete: loginComplete, + logoutComplete: logoutComplete, + openAuthWindow: openAuthWindow, + download: download, + registryRead: registryRead, + registryWrite: registryWrite, + upload: upload, + kernelVersion: kernelVersion, + callModule: callModule, + connectModule: connectModule, + init: init, + newKernelQuery: newKernelQuery, + addContextToErr: addContextToErr$1, + composeErr: composeErr$1 + }); + + var require$$0 = /*@__PURE__*/getAugmentedNamespace(dist$1); + + // Blake2B, adapted from the reference implementation in RFC7693 + // Ported to Javascript by DC - https://github.com/dcposch + // Then ported to typescript by https://github.com/DavidVorick + // 64-bit unsigned addition + // Sets v[a,a+1] += v[b,b+1] + // v should be a Uint32Array + function ADD64AA(v, a, b) { + const o0 = v[a] + v[b]; + let o1 = v[a + 1] + v[b + 1]; + if (o0 >= 0x100000000) { + o1++; + } + v[a] = o0; + v[a + 1] = o1; + } + // 64-bit unsigned addition + // Sets v[a,a+1] += b + // b0 is the low 32 bits of b, b1 represents the high 32 bits + function ADD64AC(v, a, b0, b1) { + let o0 = v[a] + b0; + if (b0 < 0) { + o0 += 0x100000000; + } + let o1 = v[a + 1] + b1; + if (o0 >= 0x100000000) { + o1++; + } + v[a] = o0; + v[a + 1] = o1; + } + // Little-endian byte access + function B2B_GET32(arr, i) { + return arr[i] ^ (arr[i + 1] << 8) ^ (arr[i + 2] << 16) ^ (arr[i + 3] << 24); + } + // G Mixing function + // The ROTRs are inlined for speed + function B2B_G(a, b, c, d, ix, iy, m, v) { + const x0 = m[ix]; + const x1 = m[ix + 1]; + const y0 = m[iy]; + const y1 = m[iy + 1]; + ADD64AA(v, a, b); // v[a,a+1] += v[b,b+1] ... in JS we must store a uint64 as two uint32s + ADD64AC(v, a, x0, x1); // v[a, a+1] += x ... x0 is the low 32 bits of x, x1 is the high 32 bits + // v[d,d+1] = (v[d,d+1] xor v[a,a+1]) rotated to the right by 32 bits + let xor0 = v[d] ^ v[a]; + let xor1 = v[d + 1] ^ v[a + 1]; + v[d] = xor1; + v[d + 1] = xor0; + ADD64AA(v, c, d); + // v[b,b+1] = (v[b,b+1] xor v[c,c+1]) rotated right by 24 bits + xor0 = v[b] ^ v[c]; + xor1 = v[b + 1] ^ v[c + 1]; + v[b] = (xor0 >>> 24) ^ (xor1 << 8); + v[b + 1] = (xor1 >>> 24) ^ (xor0 << 8); + ADD64AA(v, a, b); + ADD64AC(v, a, y0, y1); + // v[d,d+1] = (v[d,d+1] xor v[a,a+1]) rotated right by 16 bits + xor0 = v[d] ^ v[a]; + xor1 = v[d + 1] ^ v[a + 1]; + v[d] = (xor0 >>> 16) ^ (xor1 << 16); + v[d + 1] = (xor1 >>> 16) ^ (xor0 << 16); + ADD64AA(v, c, d); + // v[b,b+1] = (v[b,b+1] xor v[c,c+1]) rotated right by 63 bits + xor0 = v[b] ^ v[c]; + xor1 = v[b + 1] ^ v[c + 1]; + v[b] = (xor1 >>> 31) ^ (xor0 << 1); + v[b + 1] = (xor0 >>> 31) ^ (xor1 << 1); + } + // Initialization Vector + const BLAKE2B_IV32 = new Uint32Array([ + 0xf3bcc908, 0x6a09e667, 0x84caa73b, 0xbb67ae85, 0xfe94f82b, 0x3c6ef372, 0x5f1d36f1, 0xa54ff53a, 0xade682d1, + 0x510e527f, 0x2b3e6c1f, 0x9b05688c, 0xfb41bd6b, 0x1f83d9ab, 0x137e2179, 0x5be0cd19, + ]); + const SIGMA8 = [ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3, 11, 8, 12, + 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4, 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8, 9, 0, 5, 7, 2, 4, 10, + 15, 14, 1, 11, 12, 6, 8, 3, 13, 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9, 12, 5, 1, 15, 14, 13, 4, 10, 0, + 7, 6, 3, 9, 2, 8, 11, 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10, 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, + 1, 4, 10, 5, 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3, + ]; + // These are offsets into a uint64 buffer. + // Multiply them all by 2 to make them offsets into a uint32 buffer, + // because this is Javascript and we don't have uint64s + const SIGMA82 = new Uint8Array(SIGMA8.map(function (x) { + return x * 2; + })); + // Compression function. 'last' flag indicates last block. + // Note we're representing 16 uint64s as 32 uint32s + function blake2bCompress(ctx, last) { + const v = new Uint32Array(32); + const m = new Uint32Array(32); + let i = 0; + // init work variables + for (i = 0; i < 16; i++) { + v[i] = ctx.h[i]; + v[i + 16] = BLAKE2B_IV32[i]; + } + // low 64 bits of offset + v[24] = v[24] ^ ctx.t; + v[25] = v[25] ^ (ctx.t / 0x100000000); + // high 64 bits not supported, offset may not be higher than 2**53-1 + // last block flag set ? + if (last) { + v[28] = ~v[28]; + v[29] = ~v[29]; + } + // get little-endian words + for (i = 0; i < 32; i++) { + m[i] = B2B_GET32(ctx.b, 4 * i); + } + // twelve rounds of mixing + for (i = 0; i < 12; i++) { + B2B_G(0, 8, 16, 24, SIGMA82[i * 16 + 0], SIGMA82[i * 16 + 1], m, v); + B2B_G(2, 10, 18, 26, SIGMA82[i * 16 + 2], SIGMA82[i * 16 + 3], m, v); + B2B_G(4, 12, 20, 28, SIGMA82[i * 16 + 4], SIGMA82[i * 16 + 5], m, v); + B2B_G(6, 14, 22, 30, SIGMA82[i * 16 + 6], SIGMA82[i * 16 + 7], m, v); + B2B_G(0, 10, 20, 30, SIGMA82[i * 16 + 8], SIGMA82[i * 16 + 9], m, v); + B2B_G(2, 12, 22, 24, SIGMA82[i * 16 + 10], SIGMA82[i * 16 + 11], m, v); + B2B_G(4, 14, 16, 26, SIGMA82[i * 16 + 12], SIGMA82[i * 16 + 13], m, v); + B2B_G(6, 8, 18, 28, SIGMA82[i * 16 + 14], SIGMA82[i * 16 + 15], m, v); + } + for (i = 0; i < 16; i++) { + ctx.h[i] = ctx.h[i] ^ v[i] ^ v[i + 16]; + } + } + // Creates a BLAKE2b hashing context + // Requires an output length between 1 and 64 bytes + function blake2bInit() { + // state, 'param block' + const ctx = { + b: new Uint8Array(128), + h: new Uint32Array(16), + t: 0, + c: 0, + outlen: 32, // output length in bytes + }; + // initialize hash state + for (let i = 0; i < 16; i++) { + ctx.h[i] = BLAKE2B_IV32[i]; + } + ctx.h[0] ^= 0x01010000 ^ 32; + return ctx; + } + // Updates a BLAKE2b streaming hash + // Requires hash context and Uint8Array (byte array) + function blake2bUpdate(ctx, input) { + for (let i = 0; i < input.length; i++) { + if (ctx.c === 128) { + // buffer full ? + ctx.t += ctx.c; // add counters + blake2bCompress(ctx, false); // compress (not last) + ctx.c = 0; // counter to zero + } + ctx.b[ctx.c++] = input[i]; + } + } + // Completes a BLAKE2b streaming hash + // Returns a Uint8Array containing the message digest + function blake2bFinal(ctx) { + ctx.t += ctx.c; // mark last block offset + while (ctx.c < 128) { + // fill up with zeros + ctx.b[ctx.c++] = 0; + } + blake2bCompress(ctx, true); // final block flag = 1 + // little endian convert and store + const out = new Uint8Array(ctx.outlen); + for (let i = 0; i < ctx.outlen; i++) { + out[i] = ctx.h[i >> 2] >> (8 * (i & 3)); + } + return out; + } + // Computes the blake2b hash of the input. Returns 32 bytes. + let blake2b = function (input) { + const ctx = blake2bInit(); + blake2bUpdate(ctx, input); + return blake2bFinal(ctx); + }; + + const defaultPortalList = ["https://siasky.net", "https://web3portal.com"]; + + // DICTIONARY_UNIQUE_PREFIX defines the number of characters that are + // guaranteed to be unique for each word in the dictionary. The seed code only + // looks at these three characters when parsing a word, allowing users to make + // substitutions for words if they prefer or find it easier to memorize. + const DICTIONARY_UNIQUE_PREFIX = 3; + // dictionary contains the word list for the mysky seed. + const dictionary = [ + "abbey", + "ablaze", + "abort", + "absorb", + "abyss", + "aces", + "aching", + "acidic", + "across", + "acumen", + "adapt", + "adept", + "adjust", + "adopt", + "adult", + "aerial", + "afar", + "affair", + "afield", + "afloat", + "afoot", + "afraid", + "after", + "agenda", + "agile", + "aglow", + "agony", + "agreed", + "ahead", + "aided", + "aisle", + "ajar", + "akin", + "alarms", + "album", + "alerts", + "alley", + "almost", + "aloof", + "alpine", + "also", + "alumni", + "always", + "amaze", + "ambush", + "amidst", + "ammo", + "among", + "amply", + "amused", + "anchor", + "angled", + "ankle", + "antics", + "anvil", + "apart", + "apex", + "aphid", + "aplomb", + "apply", + "archer", + "ardent", + "arena", + "argue", + "arises", + "army", + "around", + "arrow", + "ascend", + "aside", + "asked", + "asleep", + "aspire", + "asylum", + "atlas", + "atom", + "atrium", + "attire", + "auburn", + "audio", + "august", + "aunt", + "autumn", + "avatar", + "avidly", + "avoid", + "awful", + "awning", + "awoken", + "axes", + "axis", + "axle", + "aztec", + "azure", + "baby", + "bacon", + "badge", + "bailed", + "bakery", + "bamboo", + "banjo", + "basin", + "batch", + "bawled", + "bays", + "beer", + "befit", + "begun", + "behind", + "being", + "below", + "bested", + "bevel", + "beware", + "beyond", + "bias", + "bids", + "bikini", + "birth", + "bite", + "blip", + "boat", + "bodies", + "bogeys", + "boil", + "boldly", + "bomb", + "border", + "boss", + "both", + "bovine", + "boxes", + "broken", + "brunt", + "bubble", + "budget", + "buffet", + "bugs", + "bulb", + "bumper", + "bunch", + "butter", + "buying", + "buzzer", + "byline", + "bypass", + "cabin", + "cactus", + "cadets", + "cafe", + "cage", + "cajun", + "cake", + "camp", + "candy", + "casket", + "catch", + "cause", + "cease", + "cedar", + "cell", + "cement", + "cent", + "chrome", + "cider", + "cigar", + "cinema", + "circle", + "claim", + "click", + "clue", + "coal", + "cobra", + "cocoa", + "code", + "coffee", + "cogs", + "coils", + "colony", + "comb", + "cool", + "copy", + "cousin", + "cowl", + "cube", + "cuffs", + "custom", + "dads", + "daft", + "dagger", + "daily", + "damp", + "dapper", + "darted", + "dash", + "dating", + "dawn", + "dazed", + "debut", + "decay", + "deftly", + "deity", + "dented", + "depth", + "desk", + "devoid", + "dice", + "diet", + "digit", + "dilute", + "dime", + "dinner", + "diode", + "ditch", + "divers", + "dizzy", + "doctor", + "dodge", + "does", + "dogs", + "doing", + "donuts", + "dosage", + "dotted", + "double", + "dove", + "down", + "dozen", + "dreams", + "drinks", + "drunk", + "drying", + "dual", + "dubbed", + "dude", + "duets", + "duke", + "dummy", + "dunes", + "duplex", + "dusted", + "duties", + "dwarf", + "dwelt", + "dying", + "each", + "eagle", + "earth", + "easy", + "eating", + "echo", + "eden", + "edgy", + "edited", + "eels", + "eggs", + "eight", + "either", + "eject", + "elapse", + "elbow", + "eldest", + "eleven", + "elite", + "elope", + "else", + "eluded", + "emails", + "ember", + "emerge", + "emit", + "empty", + "energy", + "enigma", + "enjoy", + "enlist", + "enmity", + "enough", + "ensign", + "envy", + "epoxy", + "equip", + "erase", + "error", + "estate", + "etched", + "ethics", + "excess", + "exhale", + "exit", + "exotic", + "extra", + "exult", + "fading", + "faked", + "fall", + "family", + "fancy", + "fatal", + "faulty", + "fawns", + "faxed", + "fazed", + "feast", + "feel", + "feline", + "fences", + "ferry", + "fever", + "fewest", + "fiat", + "fibula", + "fidget", + "fierce", + "fight", + "films", + "firm", + "five", + "fixate", + "fizzle", + "fleet", + "flying", + "foamy", + "focus", + "foes", + "foggy", + "foiled", + "fonts", + "fossil", + "fowls", + "foxes", + "foyer", + "framed", + "frown", + "fruit", + "frying", + "fudge", + "fuel", + "fully", + "fuming", + "fungal", + "future", + "fuzzy", + "gables", + "gadget", + "gags", + "gained", + "galaxy", + "gambit", + "gang", + "gasp", + "gather", + "gauze", + "gave", + "gawk", + "gaze", + "gecko", + "geek", + "gels", + "germs", + "geyser", + "ghetto", + "ghost", + "giant", + "giddy", + "gifts", + "gills", + "ginger", + "girth", + "giving", + "glass", + "glide", + "gnaw", + "gnome", + "goat", + "goblet", + "goes", + "going", + "gone", + "gopher", + "gossip", + "gotten", + "gown", + "grunt", + "guest", + "guide", + "gulp", + "guru", + "gusts", + "gutter", + "guys", + "gypsy", + "gyrate", + "hairy", + "having", + "hawk", + "hazard", + "heels", + "hefty", + "height", + "hence", + "heron", + "hiding", + "hijack", + "hiker", + "hills", + "hinder", + "hippo", + "hire", + "hive", + "hoax", + "hobby", + "hockey", + "hold", + "honked", + "hookup", + "hope", + "hornet", + "hotel", + "hover", + "howls", + "huddle", + "huge", + "hull", + "humid", + "hunter", + "huts", + "hybrid", + "hyper", + "icing", + "icon", + "idiom", + "idled", + "idols", + "igloo", + "ignore", + "iguana", + "impel", + "incur", + "injury", + "inline", + "inmate", + "input", + "insult", + "invoke", + "ionic", + "irate", + "iris", + "irony", + "island", + "issued", + "itches", + "items", + "itself", + "ivory", + "jabbed", + "jaded", + "jagged", + "jailed", + "jargon", + "jaunt", + "jaws", + "jazz", + "jeans", + "jeers", + "jester", + "jewels", + "jigsaw", + "jingle", + "jive", + "jobs", + "jockey", + "jogger", + "joking", + "jolted", + "jostle", + "joyous", + "judge", + "juicy", + "july", + "jump", + "junk", + "jury", + "karate", + "keep", + "kennel", + "kept", + "kettle", + "king", + "kiosk", + "kisses", + "kiwi", + "knee", + "knife", + "koala", + "ladder", + "lagoon", + "lair", + "lakes", + "lamb", + "laptop", + "large", + "last", + "later", + "lava", + "layout", + "lazy", + "ledge", + "leech", + "left", + "legion", + "lemon", + "lesson", + "liar", + "licks", + "lids", + "lied", + "light", + "lilac", + "limits", + "linen", + "lion", + "liquid", + "listen", + "lively", + "loaded", + "locker", + "lodge", + "lofty", + "logic", + "long", + "lopped", + "losing", + "loudly", + "love", + "lower", + "loyal", + "lucky", + "lumber", + "lunar", + "lurk", + "lush", + "luxury", + "lymph", + "lynx", + "lyrics", + "macro", + "mailed", + "major", + "makeup", + "malady", + "mammal", + "maps", + "match", + "maul", + "mayor", + "maze", + "meant", + "memoir", + "menu", + "merger", + "mesh", + "metro", + "mews", + "mice", + "midst", + "mighty", + "mime", + "mirror", + "misery", + "moat", + "mobile", + "mocked", + "mohawk", + "molten", + "moment", + "money", + "moon", + "mops", + "morsel", + "mostly", + "mouth", + "mowing", + "much", + "muddy", + "muffin", + "mugged", + "mullet", + "mumble", + "muppet", + "mural", + "muzzle", + "myriad", + "myth", + "nagged", + "nail", + "names", + "nanny", + "napkin", + "nasty", + "navy", + "nearby", + "needed", + "neon", + "nephew", + "nerves", + "nestle", + "never", + "newt", + "nexus", + "nibs", + "niche", + "niece", + "nifty", + "nimbly", + "nobody", + "nodes", + "noises", + "nomad", + "noted", + "nouns", + "nozzle", + "nuance", + "nudged", + "nugget", + "null", + "number", + "nuns", + "nurse", + "nylon", + "oaks", + "oars", + "oasis", + "object", + "occur", + "ocean", + "odds", + "offend", + "often", + "okay", + "older", + "olive", + "omega", + "onion", + "online", + "onto", + "onward", + "oozed", + "opened", + "opus", + "orange", + "orbit", + "orchid", + "orders", + "organs", + "origin", + "oscar", + "otter", + "ouch", + "ought", + "ounce", + "oust", + "oval", + "oven", + "owed", + "owls", + "owner", + "oxygen", + "oyster", + "ozone", + "pact", + "pager", + "palace", + "paper", + "pastry", + "patio", + "pause", + "peeled", + "pegs", + "pencil", + "people", + "pepper", + "pests", + "petals", + "phase", + "phone", + "piano", + "picked", + "pierce", + "pimple", + "pirate", + "pivot", + "pixels", + "pizza", + "pledge", + "pliers", + "plus", + "poetry", + "point", + "poker", + "polar", + "ponies", + "pool", + "potato", + "pouch", + "powder", + "pram", + "pride", + "pruned", + "prying", + "public", + "puck", + "puddle", + "puffin", + "pulp", + "punch", + "puppy", + "purged", + "push", + "putty", + "pylons", + "python", + "queen", + "quick", + "quote", + "radar", + "rafts", + "rage", + "raking", + "rally", + "ramped", + "rapid", + "rarest", + "rash", + "rated", + "ravine", + "rays", + "razor", + "react", + "rebel", + "recipe", + "reduce", + "reef", + "refer", + "reheat", + "relic", + "remedy", + "repent", + "reruns", + "rest", + "return", + "revamp", + "rewind", + "rhino", + "rhythm", + "ribbon", + "richly", + "ridges", + "rift", + "rigid", + "rims", + "riots", + "ripped", + "rising", + "ritual", + "river", + "roared", + "robot", + "rodent", + "rogue", + "roles", + "roomy", + "roped", + "roster", + "rotate", + "rover", + "royal", + "ruby", + "rudely", + "rugged", + "ruined", + "ruling", + "rumble", + "runway", + "rural", + "sack", + "safety", + "saga", + "sailor", + "sake", + "salads", + "sample", + "sanity", + "sash", + "satin", + "saved", + "scenic", + "school", + "scoop", + "scrub", + "scuba", + "second", + "sedan", + "seeded", + "setup", + "sewage", + "sieve", + "silk", + "sipped", + "siren", + "sizes", + "skater", + "skew", + "skulls", + "slid", + "slower", + "slug", + "smash", + "smog", + "snake", + "sneeze", + "sniff", + "snout", + "snug", + "soapy", + "sober", + "soccer", + "soda", + "soggy", + "soil", + "solved", + "sonic", + "soothe", + "sorry", + "sowed", + "soya", + "space", + "speedy", + "sphere", + "spout", + "sprig", + "spud", + "spying", + "square", + "stick", + "subtly", + "suede", + "sugar", + "summon", + "sunken", + "surfer", + "sushi", + "suture", + "swept", + "sword", + "swung", + "system", + "taboo", + "tacit", + "tagged", + "tail", + "taken", + "talent", + "tamper", + "tanks", + "tasked", + "tattoo", + "taunts", + "tavern", + "tawny", + "taxi", + "tell", + "tender", + "tepid", + "tether", + "thaw", + "thorn", + "thumbs", + "thwart", + "ticket", + "tidy", + "tiers", + "tiger", + "tilt", + "timber", + "tinted", + "tipsy", + "tirade", + "tissue", + "titans", + "today", + "toffee", + "toilet", + "token", + "tonic", + "topic", + "torch", + "tossed", + "total", + "touchy", + "towel", + "toxic", + "toyed", + "trash", + "trendy", + "tribal", + "truth", + "trying", + "tubes", + "tucks", + "tudor", + "tufts", + "tugs", + "tulips", + "tunnel", + "turnip", + "tusks", + "tutor", + "tuxedo", + "twang", + "twice", + "tycoon", + "typist", + "tyrant", + "ugly", + "ulcers", + "umpire", + "uncle", + "under", + "uneven", + "unfit", + "union", + "unmask", + "unrest", + "unsafe", + "until", + "unveil", + "unwind", + "unzip", + "upbeat", + "update", + "uphill", + "upkeep", + "upload", + "upon", + "upper", + "urban", + "urgent", + "usage", + "useful", + "usher", + "using", + "usual", + "utmost", + "utopia", + "vague", + "vain", + "value", + "vane", + "vary", + "vats", + "vaults", + "vector", + "veered", + "vegan", + "vein", + "velvet", + "vessel", + "vexed", + "vials", + "victim", + "video", + "viking", + "violin", + "vipers", + "vitals", + "vivid", + "vixen", + "vocal", + "vogue", + "voice", + "vortex", + "voted", + "vowels", + "voyage", + "wade", + "waffle", + "waist", + "waking", + "wanted", + "warped", + "water", + "waxing", + "wedge", + "weird", + "went", + "wept", + "were", + "whale", + "when", + "whole", + "width", + "wield", + "wife", + "wiggle", + "wildly", + "winter", + "wiring", + "wise", + "wives", + "wizard", + "wobbly", + "woes", + "woken", + "wolf", + "woozy", + "worry", + "woven", + "wrap", + "wrist", + "wrong", + "yacht", + "yahoo", + "yanks", + ]; + + // tryStringify will try to turn the provided input into a string. If the input + // object is already a string, the input object will be returned. If the input + // object has a toString method, the toString method will be called. If that + // fails, we try to call JSON.stringify on the object. And if that fails, we + // set the return value to "[stringify failed]". + function tryStringify(obj) { + // Check for undefined input. + if (obj === undefined) { + return "[cannot stringify undefined input]"; + } + if (obj === null) { + return "[null]"; + } + // Parse the error into a string. + if (typeof obj === "string") { + return obj; + } + // Check if the object has a 'toString' method defined on it. To ensure + // that we don't crash or throw, check that the toString is a function, and + // also that the return value of toString is a string. + if (Object.prototype.hasOwnProperty.call(obj, "toString")) { + if (typeof obj.toString === "function") { + let str = obj.toString(); + if (typeof str === "string") { + return str; + } + } + } + // If the object does not have a custom toString, attempt to perform a + // JSON.stringify. + try { + return JSON.stringify(obj); + } + catch { + return "[stringify failed]"; + } + } + + // addContextToErr is a helper function that standardizes the formatting of + // adding context to an error. Within the world of go we discovered that being + // persistent about layering context onto errors is helpful when debugging, + // even though it often creates rather verbose error messages. + // + // addContextToErr will return null if the input err is null. + // + // NOTE: To protect against accidental situations where an Error type or some + // other type is provided instead of a string, we wrap both of the inputs with + // tryStringify before returning them. This prevents runtime failures. + function addContextToErr(err, context) { + if (err === null) { + err = "[no error provided]"; + } + err = tryStringify(err); + return tryStringify(context) + ": " + tryStringify(err); + } + // composeErr takes a series of inputs and composes them into a single string. + // Each element will be separated by a newline. If the input is not a string, + // it will be transformed into a string with JSON.stringify. + // + // Any object that cannot be stringified will be skipped, though an error will + // be logged. + function composeErr(...inputs) { + let result = ""; + let resultEmpty = true; + for (let i = 0; i < inputs.length; i++) { + if (inputs[i] === null) { + continue; + } + if (resultEmpty) { + resultEmpty = false; + } + else { + result += "\n"; + } + result += tryStringify(inputs[i]); + } + if (resultEmpty) { + return null; + } + return result; + } + + // Helper consts to make it easy to return empty values alongside errors. + const nu8$6 = new Uint8Array(0); + // b64ToBuf will take an untrusted base64 string and convert it into a + // Uin8Array, returning an error if the input is not valid base64. + function b64ToBuf(b64) { + // Check that the final string is valid base64. + let b64regex = /^[0-9a-zA-Z-_/+=]*$/; + if (!b64regex.test(b64)) { + return [nu8$6, "provided string is not valid base64"]; + } + // Swap any '-' characters for '+', and swap any '_' characters for '/' + // for use in the atob function. + b64 = b64.replace(/-/g, "+").replace(/_/g, "/"); + // Perform the conversion. + let binStr = atob(b64); + let len = binStr.length; + let buf = new Uint8Array(len); + for (let i = 0; i < len; i++) { + buf[i] = binStr.charCodeAt(i); + } + return [buf, null]; + } + // bufToHex takes a Uint8Array as input and returns the hex encoding of those + // bytes as a string. + function bufToHex(buf) { + return [...buf].map((x) => x.toString(16).padStart(2, "0")).join(""); + } + // bufToB64 will convert a Uint8Array to a base64 string with URL encoding and + // no padding characters. + function bufToB64(buf) { + let b64Str = btoa(String.fromCharCode.apply(null, buf)); + return b64Str.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, ""); + } + // bufToStr takes an ArrayBuffer as input and returns a text string. bufToStr + // will check for invalid characters. + function bufToStr(buf) { + try { + let text = new TextDecoder("utf-8", { fatal: true }).decode(buf); + return [text, null]; + } + catch (err) { + return ["", addContextToErr(err.toString(), "unable to decode ArrayBuffer to string")]; + } + } + // decodeBigint will take an 8 byte Uint8Array and decode it as a bigint. + function decodeBigint(buf) { + if (buf.length !== 8) { + return [0n, "a number is expected to be 8 bytes"]; + } + let num = 0n; + for (let i = 7; i >= 0; i--) { + num *= 256n; + num += BigInt(buf[i]); + } + return [num, null]; + } + // encodePrefixedBytes takes a Uint8Array as input and returns a Uint8Array + // that has the length prefixed as an 8 byte prefix. The input can be at most 4 + // GiB. + function encodePrefixedBytes(bytes) { + let len = bytes.length; + if (len > 4294968295) { + return [nu8$6, "input is too large to be encoded"]; + } + let buf = new ArrayBuffer(8 + len); + let view = new DataView(buf); + view.setUint32(0, len, true); + let uint8Bytes = new Uint8Array(buf); + uint8Bytes.set(bytes, 8); + return [uint8Bytes, null]; + } + // encodeU64 will encode a bigint in the range of a uint64 to an 8 byte + // Uint8Array. + function encodeU64(num) { + // Check the bounds on the bigint. + if (num < 0) { + return [nu8$6, "expected a positive integer"]; + } + if (num > 18446744073709551615n) { + return [nu8$6, "expected a number no larger than a uint64"]; + } + // Encode the bigint into a Uint8Array. + let encoded = new Uint8Array(8); + for (let i = 0; i < encoded.length; i++) { + let byte = Number(num & 0xffn); + encoded[i] = byte; + num = num >> 8n; + } + return [encoded, null]; + } + // hexToBuf takes an untrusted string as input, verifies that the string is + // valid hex, and then converts the string to a Uint8Array. + function hexToBuf(hex) { + // Check that the length makes sense. + if (hex.length % 2 != 0) { + return [nu8$6, "input has incorrect length"]; + } + // Check that all of the characters are legal. + let match = /[0-9A-Fa-f]*/g; + if (!match.test(hex)) { + return [nu8$6, "input has invalid character"]; + } + // Create the buffer and fill it. + let matches = hex.match(/.{1,2}/g); + if (matches === null) { + return [nu8$6, "input is incomplete"]; + } + let u8 = new Uint8Array(matches.map((byte) => parseInt(byte, 16))); + return [u8, null]; + } + + // Helper values for cleanly returning errors. + const nu8$5 = new Uint8Array(0); + // blake2bAddSubtreeToProofStack will add a subtree to a proof stack. + function blake2bAddSubtreeToProofStack(ps, subtreeRoot, subtreeHeight) { + // Input checking. + if (subtreeRoot.length !== 32) { + return "cannot add subtree because root is wrong length"; + } + // If the proofStack has no elements in it yet, add the subtree + // with no further checks. + if (ps.subtreeRoots.length === 0) { + ps.subtreeRoots.push(subtreeRoot); + ps.subtreeHeights.push(subtreeHeight); + return null; + } + // Check the height of the new subtree against the height of the smallest + // subtree in the proofStack. If the new subtree is larger, the subtree + // cannot be added. + let maxHeight = ps.subtreeHeights[ps.subtreeHeights.length - 1]; + if (subtreeHeight > maxHeight) { + return `cannot add a subtree that is taller ${subtreeHeight} than the smallest ${maxHeight} subtree in the stack`; + } + // If the new subtreeHeight is smaller than the max height, we can just + // append the subtree height without doing anything more. + if (subtreeHeight < maxHeight) { + ps.subtreeRoots.push(subtreeRoot); + ps.subtreeHeights.push(subtreeHeight); + return null; + } + // If the new subtree is the same height as the smallest subtree, we + // have to pull the smallest subtree out, combine it with the new + // subtree, and push the result. + let oldSTR = ps.subtreeRoots.pop(); + ps.subtreeHeights.pop(); // We already have the height. + let combinedRoot = new Uint8Array(65); + combinedRoot[0] = 1; + combinedRoot.set(oldSTR, 1); + combinedRoot.set(subtreeRoot, 33); + let newSubtreeRoot = blake2b(combinedRoot); + return blake2bAddSubtreeToProofStack(ps, newSubtreeRoot, subtreeHeight + 1n); + } + // blake2bAddLeafBytesToProofStack will add a leaf to a proof stack. + function blake2bAddLeafBytesToProofStack(ps, leafBytes) { + if (leafBytes.length !== 64) { + return "proofStack expects leafByte objects to be exactly 64 bytes"; + } + let taggedBytes = new Uint8Array(65); + taggedBytes.set(leafBytes, 1); + let subtreeRoot = blake2b(taggedBytes); + return blake2bAddSubtreeToProofStack(ps, subtreeRoot, 1n); + } + // blake2bProofStackRoot returns the final Merkle root of the data in the + // current proof stack. + function blake2bProofStackRoot(ps) { + // Input checking. + if (ps.subtreeRoots.length === 0) { + return [nu8$5, "cannot compute the Merkle root of an empty data set"]; + } + // Algorithm is pretty basic, start with the final tree, and then add + // it to the previous tree. Repeat until there are no more trees. + let baseSubtreeRoot = ps.subtreeRoots.pop(); + while (ps.subtreeRoots.length !== 0) { + let nextSubtreeRoot = ps.subtreeRoots.pop(); + let combinedRoot = new Uint8Array(65); + combinedRoot[0] = 1; + combinedRoot.set(baseSubtreeRoot, 1); + combinedRoot.set(nextSubtreeRoot, 33); + baseSubtreeRoot = blake2b(combinedRoot); + } + return [baseSubtreeRoot, null]; + } + // nextSubtreeHeight returns the height of the largest subtree that contains + // 'start', contains no elements prior to 'start', and also does not contain + // 'end'. + function nextSubtreeHeight(start, end) { + // Input checking. + if (end <= start) { + return [0n, 0n, `end (${end}) must be strictly larger than start (${start})`]; + } + // Merkle trees have a nice mathematical property that the largest tree + // which contains a particular node and no nodes prior to it will have + // a height that is equal to the number of trailing zeroes in the base + // 2 representation of the index of that node. + // + // We are exploiting that property to compute the 'idealTreeHeight'. If + // 'start' is zero, the ideal tree height will just keep counting up + // forever, so we cut it off at 53. + let idealTreeHeight = 1n; + let idealTreeSize = 1n; + // The conditional inside the loop tests if the next ideal tree size is + // acceptable. If it is, we increment the height and double the size. + while (start % (idealTreeSize * 2n) === 0n) { + idealTreeHeight++; + idealTreeSize = idealTreeSize * 2n; + } + // To compute the max tree height, we essentially just find the largest + // power of 2 that is smaller than or equal to the gap between start + // and end. + let maxTreeHeight = 1n; + let maxTreeSize = 1n; + let range = end - start + 1n; + while (maxTreeSize * 2n < range) { + maxTreeHeight++; + maxTreeSize = maxTreeSize * 2n; + } + // Return the smaller of the ideal height and the max height, as each + // of them is an upper bound on how large things are allowed to be. + if (idealTreeHeight < maxTreeHeight) { + return [idealTreeHeight, idealTreeSize, null]; + } + return [maxTreeHeight, maxTreeSize, null]; + } + // blake2bMerkleRoot computes the merkle root of the provided data using a leaf + // size of 64 bytes and blake2b as the hashing function. + function blake2bMerkleRoot(data) { + // Check that the input is an acceptable length. + if (data.length % 64 !== 0) { + return [nu8$5, "cannot take the merkle root of data that is not a multiple of 64 bytes"]; + } + // Compute the Merkle root. + let ps = { + subtreeRoots: [], + subtreeHeights: [], + }; + for (let i = 0; i < data.length; i += 64) { + blake2bAddLeafBytesToProofStack(ps, data.slice(i, i + 64)); + } + return blake2bProofStackRoot(ps); + } + // blake2bVerifySectorRangeProof will verify a merkle proof that the provided + // data exists within the provided sector at the provided range. + // + // NOTE: This implementation only handles a single range, but the transition to + // doing mulit-range proofs is not very large. The main reason I didn't extend + // this function was because it made the inputs a lot messier. The Sia merkle + // tree repo uses the same techniques and has the full implementation, use that + // as a reference if you need to extend this function to support multi-range + // proofs. + function blake2bVerifySectorRangeProof(root, data, rangeStart, rangeEnd, proof) { + // Verify the inputs. + if (root.length !== 32) { + return "provided root is not a blake2b sector root"; + } + if (rangeEnd <= rangeStart) { + return "provided has no data"; + } + if (rangeStart < 0n) { + return "cannot use negative ranges"; + } + if (rangeEnd > 4194304n) { + return "range is out of bounds"; + } + if (proof.length % 32 !== 0) { + return "merkle proof has invalid length"; + } + if (data.length !== Number(rangeEnd - rangeStart)) { + return "data length does not match provided range"; + } + if (data.length % 64 !== 0) { + return "data must have a multiple of 64 bytes"; + } + // We will consume proof elements until we get to the rangeStart of the + // data. + let ps = { + subtreeRoots: [], + subtreeHeights: [], + }; + let currentOffset = 0n; + let proofOffset = 0; + while (currentOffset < rangeStart) { + if (proof.length < proofOffset + 32) { + return "merkle proof has insufficient data"; + } + let [height, size, errNST] = nextSubtreeHeight(currentOffset / 64n, rangeStart / 64n); + if (errNST !== null) { + return addContextToErr(errNST, "error computing subtree height of initial proof stack"); + } + let newSubtreeRoot = new Uint8Array(32); + newSubtreeRoot.set(proof.slice(proofOffset, proofOffset + 32), 0); + proofOffset += 32; + let errSPS = blake2bAddSubtreeToProofStack(ps, newSubtreeRoot, height); + if (errSPS !== null) { + return addContextToErr(errSPS, "error adding subtree to initial proof stack"); + } + currentOffset += size * 64n; + } + // We will consume data elements until we get to the end of the data. + let dataOffset = 0; + while (data.length > dataOffset) { + let errLBPS = blake2bAddLeafBytesToProofStack(ps, data.slice(dataOffset, dataOffset + 64)); + if (errLBPS !== null) { + return addContextToErr(errLBPS, "error adding leaves to proof stack"); + } + dataOffset += 64; + currentOffset += 64n; + } + // Consume proof elements until the entire sector is proven. + let sectorEnd = 4194304n; + while (currentOffset < sectorEnd) { + if (proof.length < proofOffset + 32) { + return "merkle proof has insufficient data"; + } + let [height, size, errNST] = nextSubtreeHeight(currentOffset / 64n, sectorEnd / 64n); + if (errNST !== null) { + return addContextToErr(errNST, "error computing subtree height of trailing proof stack"); + } + let newSubtreeRoot = new Uint8Array(32); + newSubtreeRoot.set(proof.slice(proofOffset, proofOffset + 32), 0); + proofOffset += 32; + let errSPS = blake2bAddSubtreeToProofStack(ps, newSubtreeRoot, height); + if (errSPS !== null) { + return addContextToErr(errSPS, "error adding subtree to trailing proof stack"); + } + currentOffset += size * 64n; + } + return null; + } + + // Helper consts to make it easier to return empty values in the event of an + // error. + const nu8$4 = new Uint8Array(0); + // verifyDownload will verify a download response from a portal. The input is + // essentially components of a skylink - the offset, length, and merkle root. + // The output is the raw file data. + // + // The 'buf' input should match the standard response body of a verified + // download request to a portal, which is the skylink raw data followed by a + // merkle proof. The offset and length provided as input indicate the offset + // and length of the skylink raw data - not the offset and length of the + // request within the file (that would be a different set of params). + // + // The skylink raw data itself breaks down into a metadata component and a file + // component. The metadata component will contain information like the length + // of the real file, and any fanout structure for large files. The first step + // we need to take is verifying the Merkel proof, which will appear at the end + // of the buffer. We'll have to hash the data we received and then compare it + // against the Merkle proof and ensure it matches the data we are expecting. + // Then we'll have to look at the layout to figure out which pieces of the data + // are the full file, while also checking for corruption as the file can be + // malicious independent of the portal operator. + // + // As long as the Merkle proof matches the root, offset, and length that we + // have as input, the portal is considered non-malicious. Any additional errors + // we find after that can be considered malice or incompetence on the part of + // the person who uploaded the file. + function verifyDownload(root, offset, fetchSize, buf) { + let u8 = new Uint8Array(buf); + // Input checking. If any of this is incorrect, its safe to blame the + // server because the skylink format fundamentally should enable these + // to be correct. + if (u8.length < fetchSize) { + return [nu8$4, true, "provided data is not large enough to cover fetchSize"]; + } + if (u8.length < 99) { + return [nu8$4, true, "provided data is not large enough to contain a skyfile"]; + } + // Grab the skylinkData and Merkle proof from the array, and then + // verify the Merkle proof. + let skylinkData = u8.slice(0, Number(fetchSize)); + let merkleProof = u8.slice(Number(fetchSize), u8.length); + let errVBSRP = blake2bVerifySectorRangeProof(root, skylinkData, offset, fetchSize, merkleProof); + if (errVBSRP !== null) { + return [nu8$4, true, addContextToErr(errVBSRP, "provided Merkle proof is not valid")]; + } + // Up until this point, an error indicated that the portal was at fault for + // either returning the wrong data or otherwise providing a malformed + // repsonse. The remaining checks relate to the consistency of the file + // itself, if the file is corrupt but the hash matches, there will be an + // error and the portal will not be at fault. + // The organization of the skylinkData is always: + // layoutBytes || fanoutBytes || metadataBytes || fileBytes + // + // The layout is always exactly 99 bytes. Bytes [1,8] of the layout + // contain the exact size of the fileBytes. Bytes [9, 16] of the layout + // contain the exact size of the metadata. And bytes [17,24] of the + // layout contain the exact size of the fanout. To get the offset of + // the fileData, we need to extract the sizes of the metadata and + // fanout, and then add those values to 99 to get the fileData offset. + let fileSizeBytes = skylinkData.slice(1, 9); + let mdSizeBytes = skylinkData.slice(9, 17); + let fanoutSizeBytes = skylinkData.slice(17, 25); + let [fileSize, errFSDN] = decodeBigint(fileSizeBytes); + if (errFSDN !== null) { + return [nu8$4, false, addContextToErr(errFSDN, "unable to decode filesize")]; + } + let [mdSize, errMDDN] = decodeBigint(mdSizeBytes); + if (errMDDN !== null) { + return [nu8$4, false, addContextToErr(errMDDN, "unable to decode metadata size")]; + } + let [fanoutSize, errFODN] = decodeBigint(fanoutSizeBytes); + if (errFODN !== null) { + return [nu8$4, false, addContextToErr(errFODN, "unable to decode fanout size")]; + } + if (BigInt(skylinkData.length) < 99n + fileSize + mdSize + fanoutSize) { + return [nu8$4, false, "provided data is too short to contain the full skyfile"]; + } + let fileData = skylinkData.slice(Number(99n + mdSize + fanoutSize), Number(99n + mdSize + fanoutSize + fileSize)); + return [fileData, false, null]; + } + + // @ts-nocheck + // json_parse extracted from the json-bigint npm library + // regexpxs extracted from + // (c) BSD-3-Clause + // https://github.com/fastify/secure-json-parse/graphs/contributors and https://github.com/hapijs/bourne/graphs/contributors + const suspectProtoRx = /(?:_|\\u005[Ff])(?:_|\\u005[Ff])(?:p|\\u0070)(?:r|\\u0072)(?:o|\\u006[Ff])(?:t|\\u0074)(?:o|\\u006[Ff])(?:_|\\u005[Ff])(?:_|\\u005[Ff])/; + const suspectConstructorRx = /(?:c|\\u0063)(?:o|\\u006[Ff])(?:n|\\u006[Ee])(?:s|\\u0073)(?:t|\\u0074)(?:r|\\u0072)(?:u|\\u0075)(?:c|\\u0063)(?:t|\\u0074)(?:o|\\u006[Ff])(?:r|\\u0072)/; + let json_parse = function (options) { + // This is a function that can parse a JSON text, producing a JavaScript + // data structure. It is a simple, recursive descent parser. It does not use + // eval or regular expressions, so it can be used as a model for implementing + // a JSON parser in other languages. + // We are defining the function inside of another function to avoid creating + // global variables. + // Default options one can override by passing options to the parse() + let _options = { + strict: false, + storeAsString: false, + alwaysParseAsBig: false, + protoAction: "error", + constructorAction: "error", + }; + // If there are options, then use them to override the default _options + if (options !== undefined && options !== null) { + if (options.strict === true) { + _options.strict = true; + } + if (options.storeAsString === true) { + _options.storeAsString = true; + } + _options.alwaysParseAsBig = options.alwaysParseAsBig === true ? options.alwaysParseAsBig : false; + if (typeof options.constructorAction !== "undefined") { + if (options.constructorAction === "error" || + options.constructorAction === "ignore" || + options.constructorAction === "preserve") { + _options.constructorAction = options.constructorAction; + } + else { + throw new Error(`Incorrect value for constructorAction option, must be "error", "ignore" or undefined but passed ${options.constructorAction}`); + } + } + if (typeof options.protoAction !== "undefined") { + if (options.protoAction === "error" || options.protoAction === "ignore" || options.protoAction === "preserve") { + _options.protoAction = options.protoAction; + } + else { + throw new Error(`Incorrect value for protoAction option, must be "error", "ignore" or undefined but passed ${options.protoAction}`); + } + } + } + let at, // The index of the current character + ch, // The current character + escapee = { + '"': '"', + "\\": "\\", + "/": "/", + b: "\b", + f: "\f", + n: "\n", + r: "\r", + t: "\t", + }, text, error = function (m) { + // Call error when something is wrong. + throw { + name: "SyntaxError", + message: m, + at: at, + text: text, + }; + }, next = function (c) { + // If a c parameter is provided, verify that it matches the current character. + if (c && c !== ch) { + error("Expected '" + c + "' instead of '" + ch + "'"); + } + // Get the next character. When there are no more characters, + // return the empty string. + ch = text.charAt(at); + at += 1; + return ch; + }, number = function () { + // Parse a number value. + let number, string = ""; + if (ch === "-") { + string = "-"; + next("-"); + } + while (ch >= "0" && ch <= "9") { + string += ch; + next(); + } + if (ch === ".") { + string += "."; + while (next() && ch >= "0" && ch <= "9") { + string += ch; + } + } + if (ch === "e" || ch === "E") { + string += ch; + next(); + if (ch === "-" || ch === "+") { + string += ch; + next(); + } + while (ch >= "0" && ch <= "9") { + string += ch; + next(); + } + } + number = +string; + if (!isFinite(number)) { + error("Bad number"); + } + else { + if (Number.isSafeInteger(number)) + return !_options.alwaysParseAsBig ? number : BigInt(number); + // Number with fractional part should be treated as number(double) including big integers in scientific notation, i.e 1.79e+308 + else + return _options.storeAsString ? string : /[.eE]/.test(string) ? number : BigInt(string); + } + }, string = function () { + // Parse a string value. + let hex, i, string = "", uffff; + // When parsing for string values, we must look for " and \ characters. + if (ch === '"') { + let startAt = at; + while (next()) { + if (ch === '"') { + if (at - 1 > startAt) + string += text.substring(startAt, at - 1); + next(); + return string; + } + if (ch === "\\") { + if (at - 1 > startAt) + string += text.substring(startAt, at - 1); + next(); + if (ch === "u") { + uffff = 0; + for (i = 0; i < 4; i += 1) { + hex = parseInt(next(), 16); + if (!isFinite(hex)) { + break; + } + uffff = uffff * 16 + hex; + } + string += String.fromCharCode(uffff); + } + else if (typeof escapee[ch] === "string") { + string += escapee[ch]; + } + else { + break; + } + startAt = at; + } + } + } + error("Bad string"); + }, white = function () { + // Skip whitespace. + while (ch && ch <= " ") { + next(); + } + }, word = function () { + // true, false, or null. + switch (ch) { + case "t": + next("t"); + next("r"); + next("u"); + next("e"); + return true; + case "f": + next("f"); + next("a"); + next("l"); + next("s"); + next("e"); + return false; + case "n": + next("n"); + next("u"); + next("l"); + next("l"); + return null; + } + error("Unexpected '" + ch + "'"); + }, value, // Place holder for the value function. + array = function () { + // Parse an array value. + let array = []; + if (ch === "[") { + next("["); + white(); + if (ch === "]") { + next("]"); + return array; // empty array + } + while (ch) { + array.push(value()); + white(); + if (ch === "]") { + next("]"); + return array; + } + next(","); + white(); + } + } + error("Bad array"); + }, object = function () { + // Parse an object value. + let key, object = Object.create(null); + if (ch === "{") { + next("{"); + white(); + if (ch === "}") { + next("}"); + return object; // empty object + } + while (ch) { + key = string(); + white(); + next(":"); + if (_options.strict === true && Object.hasOwnProperty.call(object, key)) { + error('Duplicate key "' + key + '"'); + } + if (suspectProtoRx.test(key) === true) { + if (_options.protoAction === "error") { + error("Object contains forbidden prototype property"); + } + else if (_options.protoAction === "ignore") { + value(); + } + else { + object[key] = value(); + } + } + else if (suspectConstructorRx.test(key) === true) { + if (_options.constructorAction === "error") { + error("Object contains forbidden constructor property"); + } + else if (_options.constructorAction === "ignore") { + value(); + } + else { + object[key] = value(); + } + } + else { + object[key] = value(); + } + white(); + if (ch === "}") { + next("}"); + return object; + } + next(","); + white(); + } + } + error("Bad object"); + }; + value = function () { + // Parse a JSON value. It could be an object, an array, a string, a number, + // or a word. + white(); + switch (ch) { + case "{": + return object(); + case "[": + return array(); + case '"': + return string(); + case "-": + return number(); + default: + return ch >= "0" && ch <= "9" ? number() : word(); + } + }; + // Return the json_parse function. It will have access to all of the above + // functions and variables. + return function (source, reviver) { + let result; + text = source + ""; + at = 0; + ch = " "; + result = value(); + white(); + if (ch) { + error("Syntax error"); + } + // If there is a reviver function, we recursively walk the new structure, + // passing each name/value pair to the reviver function for possible + // transformation, starting with a temporary root object that holds the result + // in an empty key. If there is not a reviver function, we simply return the + // result. + return typeof reviver === "function" + ? (function walk(holder, key) { + let v, value = holder[key]; + if (value && typeof value === "object") { + Object.keys(value).forEach(function (k) { + v = walk(value, k); + if (v !== undefined) { + value[k] = v; + } + else { + delete value[k]; + } + }); + } + return reviver.call(holder, key, value); + })({ "": result }, "") + : result; + }; + }; + // parseJSON is a wrapper for JSONbig.parse that returns an error rather than + // throwing an error. JSONbig is an alternative to JSON.parse that decodes + // every number as a bigint. This is required when working with the skyd API + // because the skyd API uses 64 bit precision for all of its numbers, and + // therefore cannot be parsed losslessly by javascript. The skyd API is + // cryptographic, therefore full precision is required. + function parseJSON(json) { + try { + let obj = json_parse({ alwaysParseAsBig: true })(json); + return [obj, null]; + } + catch (err) { + return [{}, tryStringify(err)]; + } + } + + // Helper consts that make it easier to return empty values when returning an + // error inside of a function. + const nu8$3 = new Uint8Array(0); + // parseSkylinkBitfield parses a skylink bitfield and returns the corresponding + // version, offset, and fetchSize. + function parseSkylinkBitfield(skylink) { + // Validate the input. + if (skylink.length !== 34) { + return [0n, 0n, 0n, "provided skylink has incorrect length"]; + } + // Extract the bitfield. + let bitfield = new DataView(skylink.buffer).getUint16(0, true); + // Extract the version. + let version = (bitfield & 3) + 1; + // Only versions 1 and 2 are recognized. + if (version !== 1 && version !== 2) { + return [0n, 0n, 0n, "provided skylink has unrecognized version"]; + } + // If the skylink is set to version 2, we only recognize the link if + // the rest of the bits in the bitfield are empty. + if (version === 2) { + if ((bitfield & 3) !== bitfield) { + return [0n, 0n, 0n, "provided skylink has unrecognized version"]; + } + return [BigInt(version), 0n, 0n, null]; + } + // Verify that the mode is valid, then fetch the mode. + bitfield = bitfield >> 2; + if ((bitfield & 255) === 255) { + return [0n, 0n, 0n, "provided skylink has an unrecognized version"]; + } + let mode = 0; + for (let i = 0; i < 8; i++) { + if ((bitfield & 1) === 0) { + bitfield = bitfield >> 1; + break; + } + bitfield = bitfield >> 1; + mode++; + } + // If the mode is greater than 7, this is not a valid v1 skylink. + if (mode > 7) { + return [0n, 0n, 0n, "provided skylink has an invalid v1 bitfield"]; + } + // Determine the offset and fetchSize increment. + let offsetIncrement = 4096 << mode; + let fetchSizeIncrement = 4096; + let fetchSizeStart = 0; + if (mode > 0) { + fetchSizeIncrement = fetchSizeIncrement << (mode - 1); + fetchSizeStart = (1 << 15) << (mode - 1); + } + // The next three bits decide the fetchSize. + let fetchSizeBits = bitfield & 7; + fetchSizeBits++; // semantic upstep, range should be [1,8] not [0,8). + let fetchSize = fetchSizeBits * fetchSizeIncrement + fetchSizeStart; + bitfield = bitfield >> 3; + // The remaining bits determine the offset. + let offset = bitfield * offsetIncrement; + if (offset + fetchSize > 1 << 22) { + return [0n, 0n, 0n, "provided skylink has an invalid v1 bitfield"]; + } + // Return what we learned. + return [BigInt(version), BigInt(offset), BigInt(fetchSize), null]; + } + // skylinkV1Bitfield sets the bitfield of a V1 skylink. It assumes the version + // is 1 and the offset is 0. It will determine the appropriate fetchSize from + // the provided dataSize. + function skylinkV1Bitfield(dataSizeBI) { + // Check that the dataSize is not too large. + if (dataSizeBI > 1 << 22) { + return [nu8$3, "dataSize must be less than the sector size"]; + } + let dataSize = Number(dataSizeBI); + // Determine the mode for the file. The mode is determined by the + // dataSize. + let mode = 0; + for (let i = 1 << 15; i < dataSize; i *= 2) { + mode += 1; + } + // Determine the download number. + let downloadNumber = 0; + if (mode === 0) { + if (dataSize !== 0) { + downloadNumber = Math.floor((dataSize - 1) / (1 << 12)); + } + } + else { + let step = 1 << (11 + mode); + let target = dataSize - (1 << (14 + mode)); + if (target !== 0) { + downloadNumber = Math.floor((target - 1) / step); + } + } + // Create the Uint8Array and fill it out. The main reason I switch over + // the 7 modes like this is because I wasn't sure how to make a uint16 + // in javascript. If we could treat the uint8array as a uint16 and then + // later convert it over, we could use basic bitshifiting and really + // simplify the code here. + let bitfield = new Uint8Array(2); + if (mode === 7) { + // 0 0 0 X X X 0 1|1 1 1 1 1 1 0 0 + bitfield[0] = downloadNumber; + bitfield[0] *= 4; + bitfield[0] += 1; + bitfield[1] = 4 + 8 + 16 + 32 + 64 + 128; + } + if (mode === 6) { + // 0 0 0 0 X X X 0|1 1 1 1 1 1 0 0 + bitfield[0] = downloadNumber; + bitfield[0] *= 2; + bitfield[1] = 4 + 8 + 16 + 32 + 64 + 128; + } + if (mode === 5) { + // 0 0 0 0 0 X X X|0 1 1 1 1 1 0 0 + bitfield[0] = downloadNumber; + bitfield[1] = 4 + 8 + 16 + 32 + 64; + } + if (mode === 4) { + // 0 0 0 0 0 0 X X|X 0 1 1 1 1 0 0 + bitfield[0] = downloadNumber; + bitfield[0] /= 2; + bitfield[1] = (downloadNumber & 1) * 128; + bitfield[1] += 4 + 8 + 16 + 32; + } + if (mode === 3) { + // 0 0 0 0 0 0 0 X|X X 0 1 1 1 0 0 + bitfield[0] = downloadNumber; + bitfield[0] /= 4; + bitfield[1] = (downloadNumber & 3) * 64; + bitfield[1] += 4 + 8 + 16; + } + if (mode === 2) { + // 0 0 0 0 0 0 0 0|X X X 0 1 1 0 0 + bitfield[0] = 0; + bitfield[1] = downloadNumber * 32; + bitfield[1] += 4 + 8; + } + if (mode === 1) { + // 0 0 0 0 0 0 0 0|0 X X X 0 1 0 0 + bitfield[0] = 0; + bitfield[1] = downloadNumber * 16; + bitfield[1] += 4; + } + if (mode === 0) { + // 0 0 0 0 0 0 0 0|0 0 X X X 0 0 0 + bitfield[0] = 0; + bitfield[1] = downloadNumber * 8; + } + // Swap the byte order. + let zero = bitfield[0]; + bitfield[0] = bitfield[1]; + bitfield[1] = zero; + return [bitfield, null]; + } + + const HASH_SIZE = 64; + const K = [ + 0x428a2f98, 0xd728ae22, 0x71374491, 0x23ef65cd, 0xb5c0fbcf, 0xec4d3b2f, 0xe9b5dba5, 0x8189dbbc, 0x3956c25b, + 0xf348b538, 0x59f111f1, 0xb605d019, 0x923f82a4, 0xaf194f9b, 0xab1c5ed5, 0xda6d8118, 0xd807aa98, 0xa3030242, + 0x12835b01, 0x45706fbe, 0x243185be, 0x4ee4b28c, 0x550c7dc3, 0xd5ffb4e2, 0x72be5d74, 0xf27b896f, 0x80deb1fe, + 0x3b1696b1, 0x9bdc06a7, 0x25c71235, 0xc19bf174, 0xcf692694, 0xe49b69c1, 0x9ef14ad2, 0xefbe4786, 0x384f25e3, + 0x0fc19dc6, 0x8b8cd5b5, 0x240ca1cc, 0x77ac9c65, 0x2de92c6f, 0x592b0275, 0x4a7484aa, 0x6ea6e483, 0x5cb0a9dc, + 0xbd41fbd4, 0x76f988da, 0x831153b5, 0x983e5152, 0xee66dfab, 0xa831c66d, 0x2db43210, 0xb00327c8, 0x98fb213f, + 0xbf597fc7, 0xbeef0ee4, 0xc6e00bf3, 0x3da88fc2, 0xd5a79147, 0x930aa725, 0x06ca6351, 0xe003826f, 0x14292967, + 0x0a0e6e70, 0x27b70a85, 0x46d22ffc, 0x2e1b2138, 0x5c26c926, 0x4d2c6dfc, 0x5ac42aed, 0x53380d13, 0x9d95b3df, + 0x650a7354, 0x8baf63de, 0x766a0abb, 0x3c77b2a8, 0x81c2c92e, 0x47edaee6, 0x92722c85, 0x1482353b, 0xa2bfe8a1, + 0x4cf10364, 0xa81a664b, 0xbc423001, 0xc24b8b70, 0xd0f89791, 0xc76c51a3, 0x0654be30, 0xd192e819, 0xd6ef5218, + 0xd6990624, 0x5565a910, 0xf40e3585, 0x5771202a, 0x106aa070, 0x32bbd1b8, 0x19a4c116, 0xb8d2d0c8, 0x1e376c08, + 0x5141ab53, 0x2748774c, 0xdf8eeb99, 0x34b0bcb5, 0xe19b48a8, 0x391c0cb3, 0xc5c95a63, 0x4ed8aa4a, 0xe3418acb, + 0x5b9cca4f, 0x7763e373, 0x682e6ff3, 0xd6b2b8a3, 0x748f82ee, 0x5defb2fc, 0x78a5636f, 0x43172f60, 0x84c87814, + 0xa1f0ab72, 0x8cc70208, 0x1a6439ec, 0x90befffa, 0x23631e28, 0xa4506ceb, 0xde82bde9, 0xbef9a3f7, 0xb2c67915, + 0xc67178f2, 0xe372532b, 0xca273ece, 0xea26619c, 0xd186b8c7, 0x21c0c207, 0xeada7dd6, 0xcde0eb1e, 0xf57d4f7f, + 0xee6ed178, 0x06f067aa, 0x72176fba, 0x0a637dc5, 0xa2c898a6, 0x113f9804, 0xbef90dae, 0x1b710b35, 0x131c471b, + 0x28db77f5, 0x23047d84, 0x32caab7b, 0x40c72493, 0x3c9ebe0a, 0x15c9bebc, 0x431d67c4, 0x9c100d4c, 0x4cc5d4be, + 0xcb3e42b6, 0x597f299c, 0xfc657e2a, 0x5fcb6fab, 0x3ad6faec, 0x6c44198c, 0x4a475817, + ]; + function ts64(x, i, h, l) { + x[i] = (h >> 24) & 0xff; + x[i + 1] = (h >> 16) & 0xff; + x[i + 2] = (h >> 8) & 0xff; + x[i + 3] = h & 0xff; + x[i + 4] = (l >> 24) & 0xff; + x[i + 5] = (l >> 16) & 0xff; + x[i + 6] = (l >> 8) & 0xff; + x[i + 7] = l & 0xff; + } + function crypto_hashblocks_hl(hh, hl, m, n) { + let wh = new Int32Array(16), wl = new Int32Array(16), bh0, bh1, bh2, bh3, bh4, bh5, bh6, bh7, bl0, bl1, bl2, bl3, bl4, bl5, bl6, bl7, th, tl, i, j, h, l, a, b, c, d; + let ah0 = hh[0], ah1 = hh[1], ah2 = hh[2], ah3 = hh[3], ah4 = hh[4], ah5 = hh[5], ah6 = hh[6], ah7 = hh[7], al0 = hl[0], al1 = hl[1], al2 = hl[2], al3 = hl[3], al4 = hl[4], al5 = hl[5], al6 = hl[6], al7 = hl[7]; + let pos = 0; + while (n >= 128) { + for (i = 0; i < 16; i++) { + j = 8 * i + pos; + wh[i] = (m[j + 0] << 24) | (m[j + 1] << 16) | (m[j + 2] << 8) | m[j + 3]; + wl[i] = (m[j + 4] << 24) | (m[j + 5] << 16) | (m[j + 6] << 8) | m[j + 7]; + } + for (i = 0; i < 80; i++) { + bh0 = ah0; + bh1 = ah1; + bh2 = ah2; + bh3 = ah3; + bh4 = ah4; + bh5 = ah5; + bh6 = ah6; + bh7 = ah7; + bl0 = al0; + bl1 = al1; + bl2 = al2; + bl3 = al3; + bl4 = al4; + bl5 = al5; + bl6 = al6; + bl7 = al7; + // add + h = ah7; + l = al7; + a = l & 0xffff; + b = l >>> 16; + c = h & 0xffff; + d = h >>> 16; + // Sigma1 + h = + ((ah4 >>> 14) | (al4 << (32 - 14))) ^ + ((ah4 >>> 18) | (al4 << (32 - 18))) ^ + ((al4 >>> (41 - 32)) | (ah4 << (32 - (41 - 32)))); + l = + ((al4 >>> 14) | (ah4 << (32 - 14))) ^ + ((al4 >>> 18) | (ah4 << (32 - 18))) ^ + ((ah4 >>> (41 - 32)) | (al4 << (32 - (41 - 32)))); + a += l & 0xffff; + b += l >>> 16; + c += h & 0xffff; + d += h >>> 16; + // Ch + h = (ah4 & ah5) ^ (~ah4 & ah6); + l = (al4 & al5) ^ (~al4 & al6); + a += l & 0xffff; + b += l >>> 16; + c += h & 0xffff; + d += h >>> 16; + // K + h = K[i * 2]; + l = K[i * 2 + 1]; + a += l & 0xffff; + b += l >>> 16; + c += h & 0xffff; + d += h >>> 16; + // w + h = wh[i % 16]; + l = wl[i % 16]; + a += l & 0xffff; + b += l >>> 16; + c += h & 0xffff; + d += h >>> 16; + b += a >>> 16; + c += b >>> 16; + d += c >>> 16; + th = (c & 0xffff) | (d << 16); + tl = (a & 0xffff) | (b << 16); + // add + h = th; + l = tl; + a = l & 0xffff; + b = l >>> 16; + c = h & 0xffff; + d = h >>> 16; + // Sigma0 + h = + ((ah0 >>> 28) | (al0 << (32 - 28))) ^ + ((al0 >>> (34 - 32)) | (ah0 << (32 - (34 - 32)))) ^ + ((al0 >>> (39 - 32)) | (ah0 << (32 - (39 - 32)))); + l = + ((al0 >>> 28) | (ah0 << (32 - 28))) ^ + ((ah0 >>> (34 - 32)) | (al0 << (32 - (34 - 32)))) ^ + ((ah0 >>> (39 - 32)) | (al0 << (32 - (39 - 32)))); + a += l & 0xffff; + b += l >>> 16; + c += h & 0xffff; + d += h >>> 16; + // Maj + h = (ah0 & ah1) ^ (ah0 & ah2) ^ (ah1 & ah2); + l = (al0 & al1) ^ (al0 & al2) ^ (al1 & al2); + a += l & 0xffff; + b += l >>> 16; + c += h & 0xffff; + d += h >>> 16; + b += a >>> 16; + c += b >>> 16; + d += c >>> 16; + bh7 = (c & 0xffff) | (d << 16); + bl7 = (a & 0xffff) | (b << 16); + // add + h = bh3; + l = bl3; + a = l & 0xffff; + b = l >>> 16; + c = h & 0xffff; + d = h >>> 16; + h = th; + l = tl; + a += l & 0xffff; + b += l >>> 16; + c += h & 0xffff; + d += h >>> 16; + b += a >>> 16; + c += b >>> 16; + d += c >>> 16; + bh3 = (c & 0xffff) | (d << 16); + bl3 = (a & 0xffff) | (b << 16); + ah1 = bh0; + ah2 = bh1; + ah3 = bh2; + ah4 = bh3; + ah5 = bh4; + ah6 = bh5; + ah7 = bh6; + ah0 = bh7; + al1 = bl0; + al2 = bl1; + al3 = bl2; + al4 = bl3; + al5 = bl4; + al6 = bl5; + al7 = bl6; + al0 = bl7; + if (i % 16 === 15) { + for (j = 0; j < 16; j++) { + // add + h = wh[j]; + l = wl[j]; + a = l & 0xffff; + b = l >>> 16; + c = h & 0xffff; + d = h >>> 16; + h = wh[(j + 9) % 16]; + l = wl[(j + 9) % 16]; + a += l & 0xffff; + b += l >>> 16; + c += h & 0xffff; + d += h >>> 16; + // sigma0 + th = wh[(j + 1) % 16]; + tl = wl[(j + 1) % 16]; + h = ((th >>> 1) | (tl << (32 - 1))) ^ ((th >>> 8) | (tl << (32 - 8))) ^ (th >>> 7); + l = ((tl >>> 1) | (th << (32 - 1))) ^ ((tl >>> 8) | (th << (32 - 8))) ^ ((tl >>> 7) | (th << (32 - 7))); + a += l & 0xffff; + b += l >>> 16; + c += h & 0xffff; + d += h >>> 16; + // sigma1 + th = wh[(j + 14) % 16]; + tl = wl[(j + 14) % 16]; + h = ((th >>> 19) | (tl << (32 - 19))) ^ ((tl >>> (61 - 32)) | (th << (32 - (61 - 32)))) ^ (th >>> 6); + l = + ((tl >>> 19) | (th << (32 - 19))) ^ + ((th >>> (61 - 32)) | (tl << (32 - (61 - 32)))) ^ + ((tl >>> 6) | (th << (32 - 6))); + a += l & 0xffff; + b += l >>> 16; + c += h & 0xffff; + d += h >>> 16; + b += a >>> 16; + c += b >>> 16; + d += c >>> 16; + wh[j] = (c & 0xffff) | (d << 16); + wl[j] = (a & 0xffff) | (b << 16); + } + } + } + // add + h = ah0; + l = al0; + a = l & 0xffff; + b = l >>> 16; + c = h & 0xffff; + d = h >>> 16; + h = hh[0]; + l = hl[0]; + a += l & 0xffff; + b += l >>> 16; + c += h & 0xffff; + d += h >>> 16; + b += a >>> 16; + c += b >>> 16; + d += c >>> 16; + hh[0] = ah0 = (c & 0xffff) | (d << 16); + hl[0] = al0 = (a & 0xffff) | (b << 16); + h = ah1; + l = al1; + a = l & 0xffff; + b = l >>> 16; + c = h & 0xffff; + d = h >>> 16; + h = hh[1]; + l = hl[1]; + a += l & 0xffff; + b += l >>> 16; + c += h & 0xffff; + d += h >>> 16; + b += a >>> 16; + c += b >>> 16; + d += c >>> 16; + hh[1] = ah1 = (c & 0xffff) | (d << 16); + hl[1] = al1 = (a & 0xffff) | (b << 16); + h = ah2; + l = al2; + a = l & 0xffff; + b = l >>> 16; + c = h & 0xffff; + d = h >>> 16; + h = hh[2]; + l = hl[2]; + a += l & 0xffff; + b += l >>> 16; + c += h & 0xffff; + d += h >>> 16; + b += a >>> 16; + c += b >>> 16; + d += c >>> 16; + hh[2] = ah2 = (c & 0xffff) | (d << 16); + hl[2] = al2 = (a & 0xffff) | (b << 16); + h = ah3; + l = al3; + a = l & 0xffff; + b = l >>> 16; + c = h & 0xffff; + d = h >>> 16; + h = hh[3]; + l = hl[3]; + a += l & 0xffff; + b += l >>> 16; + c += h & 0xffff; + d += h >>> 16; + b += a >>> 16; + c += b >>> 16; + d += c >>> 16; + hh[3] = ah3 = (c & 0xffff) | (d << 16); + hl[3] = al3 = (a & 0xffff) | (b << 16); + h = ah4; + l = al4; + a = l & 0xffff; + b = l >>> 16; + c = h & 0xffff; + d = h >>> 16; + h = hh[4]; + l = hl[4]; + a += l & 0xffff; + b += l >>> 16; + c += h & 0xffff; + d += h >>> 16; + b += a >>> 16; + c += b >>> 16; + d += c >>> 16; + hh[4] = ah4 = (c & 0xffff) | (d << 16); + hl[4] = al4 = (a & 0xffff) | (b << 16); + h = ah5; + l = al5; + a = l & 0xffff; + b = l >>> 16; + c = h & 0xffff; + d = h >>> 16; + h = hh[5]; + l = hl[5]; + a += l & 0xffff; + b += l >>> 16; + c += h & 0xffff; + d += h >>> 16; + b += a >>> 16; + c += b >>> 16; + d += c >>> 16; + hh[5] = ah5 = (c & 0xffff) | (d << 16); + hl[5] = al5 = (a & 0xffff) | (b << 16); + h = ah6; + l = al6; + a = l & 0xffff; + b = l >>> 16; + c = h & 0xffff; + d = h >>> 16; + h = hh[6]; + l = hl[6]; + a += l & 0xffff; + b += l >>> 16; + c += h & 0xffff; + d += h >>> 16; + b += a >>> 16; + c += b >>> 16; + d += c >>> 16; + hh[6] = ah6 = (c & 0xffff) | (d << 16); + hl[6] = al6 = (a & 0xffff) | (b << 16); + h = ah7; + l = al7; + a = l & 0xffff; + b = l >>> 16; + c = h & 0xffff; + d = h >>> 16; + h = hh[7]; + l = hl[7]; + a += l & 0xffff; + b += l >>> 16; + c += h & 0xffff; + d += h >>> 16; + b += a >>> 16; + c += b >>> 16; + d += c >>> 16; + hh[7] = ah7 = (c & 0xffff) | (d << 16); + hl[7] = al7 = (a & 0xffff) | (b << 16); + pos += 128; + n -= 128; + } + return n; + } + const sha512internal = function (out, m, n) { + let hh = new Int32Array(8), hl = new Int32Array(8), x = new Uint8Array(256), i, b = n; + hh[0] = 0x6a09e667; + hh[1] = 0xbb67ae85; + hh[2] = 0x3c6ef372; + hh[3] = 0xa54ff53a; + hh[4] = 0x510e527f; + hh[5] = 0x9b05688c; + hh[6] = 0x1f83d9ab; + hh[7] = 0x5be0cd19; + hl[0] = 0xf3bcc908; + hl[1] = 0x84caa73b; + hl[2] = 0xfe94f82b; + hl[3] = 0x5f1d36f1; + hl[4] = 0xade682d1; + hl[5] = 0x2b3e6c1f; + hl[6] = 0xfb41bd6b; + hl[7] = 0x137e2179; + crypto_hashblocks_hl(hh, hl, m, n); + n %= 128; + for (i = 0; i < n; i++) + x[i] = m[b - n + i]; + x[n] = 128; + n = 256 - 128 * (n < 112 ? 1 : 0); + x[n - 9] = 0; + ts64(x, n - 8, (b / 0x20000000) | 0, b << 3); + crypto_hashblocks_hl(hh, hl, x, n); + for (i = 0; i < 8; i++) + ts64(out, 8 * i, hh[i], hl[i]); + return 0; + }; + // sha512 is the standard sha512 cryptographic hash function. This is the + // default choice for Skynet operations, though many of the Sia protocol + // standards use blake2b instead, so you will see both. + function sha512(m) { + const out = new Uint8Array(HASH_SIZE); + sha512internal(out, m, m.length); + return out; + } + + let crypto_sign_BYTES = 64, crypto_sign_PUBLICKEYBYTES = 32, crypto_sign_SECRETKEYBYTES = 64, crypto_sign_SEEDBYTES = 32; + let gf = function () { + let r = new Float64Array(16); + return r; + }; + let gfi = function (init) { + let i, r = new Float64Array(16); + if (init) + for (i = 0; i < init.length; i++) + r[i] = init[i]; + return r; + }; + let gf0 = gf(), gf1 = gfi([1]), D = gfi([ + 0x78a3, 0x1359, 0x4dca, 0x75eb, 0xd8ab, 0x4141, 0x0a4d, 0x0070, 0xe898, 0x7779, 0x4079, 0x8cc7, 0xfe73, 0x2b6f, + 0x6cee, 0x5203, + ]), D2 = gfi([ + 0xf159, 0x26b2, 0x9b94, 0xebd6, 0xb156, 0x8283, 0x149a, 0x00e0, 0xd130, 0xeef3, 0x80f2, 0x198e, 0xfce7, 0x56df, + 0xd9dc, 0x2406, + ]), X = gfi([ + 0xd51a, 0x8f25, 0x2d60, 0xc956, 0xa7b2, 0x9525, 0xc760, 0x692c, 0xdc5c, 0xfdd6, 0xe231, 0xc0a4, 0x53fe, 0xcd6e, + 0x36d3, 0x2169, + ]), Y = gfi([ + 0x6658, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, + 0x6666, 0x6666, + ]), I = gfi([ + 0xa0b0, 0x4a0e, 0x1b27, 0xc4ee, 0xe478, 0xad2f, 0x1806, 0x2f43, 0xd7a7, 0x3dfb, 0x0099, 0x2b4d, 0xdf0b, 0x4fc1, + 0x2480, 0x2b83, + ]); + function vn(x, xi, y, yi, n) { + let i, d = 0; + for (i = 0; i < n; i++) + d |= x[xi + i] ^ y[yi + i]; + return (1 & ((d - 1) >>> 8)) - 1; + } + function crypto_verify_32(x, xi, y, yi) { + return vn(x, xi, y, yi, 32); + } + function set25519(r, a) { + let i; + for (i = 0; i < 16; i++) + r[i] = a[i] | 0; + } + function car25519(o) { + let i, v, c = 1; + for (i = 0; i < 16; i++) { + v = o[i] + c + 65535; + c = Math.floor(v / 65536); + o[i] = v - c * 65536; + } + o[0] += c - 1 + 37 * (c - 1); + } + function sel25519(p, q, b) { + let t, c = ~(b - 1); + for (let i = 0; i < 16; i++) { + t = c & (p[i] ^ q[i]); + p[i] ^= t; + q[i] ^= t; + } + } + function pack25519(o, n) { + let i, j, b; + let m = gf(), t = gf(); + for (i = 0; i < 16; i++) + t[i] = n[i]; + car25519(t); + car25519(t); + car25519(t); + for (j = 0; j < 2; j++) { + m[0] = t[0] - 0xffed; + for (i = 1; i < 15; i++) { + m[i] = t[i] - 0xffff - ((m[i - 1] >> 16) & 1); + m[i - 1] &= 0xffff; + } + m[15] = t[15] - 0x7fff - ((m[14] >> 16) & 1); + b = (m[15] >> 16) & 1; + m[14] &= 0xffff; + sel25519(t, m, 1 - b); + } + for (i = 0; i < 16; i++) { + o[2 * i] = t[i] & 0xff; + o[2 * i + 1] = t[i] >> 8; + } + } + function neq25519(a, b) { + let c = new Uint8Array(32), d = new Uint8Array(32); + pack25519(c, a); + pack25519(d, b); + return crypto_verify_32(c, 0, d, 0); + } + function par25519(a) { + let d = new Uint8Array(32); + pack25519(d, a); + return d[0] & 1; + } + function unpack25519(o, n) { + let i; + for (i = 0; i < 16; i++) + o[i] = n[2 * i] + (n[2 * i + 1] << 8); + o[15] &= 0x7fff; + } + function A(o, a, b) { + for (let i = 0; i < 16; i++) + o[i] = a[i] + b[i]; + } + function Z(o, a, b) { + for (let i = 0; i < 16; i++) + o[i] = a[i] - b[i]; + } + function M(o, a, b) { + let v, c, t0 = 0, t1 = 0, t2 = 0, t3 = 0, t4 = 0, t5 = 0, t6 = 0, t7 = 0, t8 = 0, t9 = 0, t10 = 0, t11 = 0, t12 = 0, t13 = 0, t14 = 0, t15 = 0, t16 = 0, t17 = 0, t18 = 0, t19 = 0, t20 = 0, t21 = 0, t22 = 0, t23 = 0, t24 = 0, t25 = 0, t26 = 0, t27 = 0, t28 = 0, t29 = 0, t30 = 0, b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3], b4 = b[4], b5 = b[5], b6 = b[6], b7 = b[7], b8 = b[8], b9 = b[9], b10 = b[10], b11 = b[11], b12 = b[12], b13 = b[13], b14 = b[14], b15 = b[15]; + v = a[0]; + t0 += v * b0; + t1 += v * b1; + t2 += v * b2; + t3 += v * b3; + t4 += v * b4; + t5 += v * b5; + t6 += v * b6; + t7 += v * b7; + t8 += v * b8; + t9 += v * b9; + t10 += v * b10; + t11 += v * b11; + t12 += v * b12; + t13 += v * b13; + t14 += v * b14; + t15 += v * b15; + v = a[1]; + t1 += v * b0; + t2 += v * b1; + t3 += v * b2; + t4 += v * b3; + t5 += v * b4; + t6 += v * b5; + t7 += v * b6; + t8 += v * b7; + t9 += v * b8; + t10 += v * b9; + t11 += v * b10; + t12 += v * b11; + t13 += v * b12; + t14 += v * b13; + t15 += v * b14; + t16 += v * b15; + v = a[2]; + t2 += v * b0; + t3 += v * b1; + t4 += v * b2; + t5 += v * b3; + t6 += v * b4; + t7 += v * b5; + t8 += v * b6; + t9 += v * b7; + t10 += v * b8; + t11 += v * b9; + t12 += v * b10; + t13 += v * b11; + t14 += v * b12; + t15 += v * b13; + t16 += v * b14; + t17 += v * b15; + v = a[3]; + t3 += v * b0; + t4 += v * b1; + t5 += v * b2; + t6 += v * b3; + t7 += v * b4; + t8 += v * b5; + t9 += v * b6; + t10 += v * b7; + t11 += v * b8; + t12 += v * b9; + t13 += v * b10; + t14 += v * b11; + t15 += v * b12; + t16 += v * b13; + t17 += v * b14; + t18 += v * b15; + v = a[4]; + t4 += v * b0; + t5 += v * b1; + t6 += v * b2; + t7 += v * b3; + t8 += v * b4; + t9 += v * b5; + t10 += v * b6; + t11 += v * b7; + t12 += v * b8; + t13 += v * b9; + t14 += v * b10; + t15 += v * b11; + t16 += v * b12; + t17 += v * b13; + t18 += v * b14; + t19 += v * b15; + v = a[5]; + t5 += v * b0; + t6 += v * b1; + t7 += v * b2; + t8 += v * b3; + t9 += v * b4; + t10 += v * b5; + t11 += v * b6; + t12 += v * b7; + t13 += v * b8; + t14 += v * b9; + t15 += v * b10; + t16 += v * b11; + t17 += v * b12; + t18 += v * b13; + t19 += v * b14; + t20 += v * b15; + v = a[6]; + t6 += v * b0; + t7 += v * b1; + t8 += v * b2; + t9 += v * b3; + t10 += v * b4; + t11 += v * b5; + t12 += v * b6; + t13 += v * b7; + t14 += v * b8; + t15 += v * b9; + t16 += v * b10; + t17 += v * b11; + t18 += v * b12; + t19 += v * b13; + t20 += v * b14; + t21 += v * b15; + v = a[7]; + t7 += v * b0; + t8 += v * b1; + t9 += v * b2; + t10 += v * b3; + t11 += v * b4; + t12 += v * b5; + t13 += v * b6; + t14 += v * b7; + t15 += v * b8; + t16 += v * b9; + t17 += v * b10; + t18 += v * b11; + t19 += v * b12; + t20 += v * b13; + t21 += v * b14; + t22 += v * b15; + v = a[8]; + t8 += v * b0; + t9 += v * b1; + t10 += v * b2; + t11 += v * b3; + t12 += v * b4; + t13 += v * b5; + t14 += v * b6; + t15 += v * b7; + t16 += v * b8; + t17 += v * b9; + t18 += v * b10; + t19 += v * b11; + t20 += v * b12; + t21 += v * b13; + t22 += v * b14; + t23 += v * b15; + v = a[9]; + t9 += v * b0; + t10 += v * b1; + t11 += v * b2; + t12 += v * b3; + t13 += v * b4; + t14 += v * b5; + t15 += v * b6; + t16 += v * b7; + t17 += v * b8; + t18 += v * b9; + t19 += v * b10; + t20 += v * b11; + t21 += v * b12; + t22 += v * b13; + t23 += v * b14; + t24 += v * b15; + v = a[10]; + t10 += v * b0; + t11 += v * b1; + t12 += v * b2; + t13 += v * b3; + t14 += v * b4; + t15 += v * b5; + t16 += v * b6; + t17 += v * b7; + t18 += v * b8; + t19 += v * b9; + t20 += v * b10; + t21 += v * b11; + t22 += v * b12; + t23 += v * b13; + t24 += v * b14; + t25 += v * b15; + v = a[11]; + t11 += v * b0; + t12 += v * b1; + t13 += v * b2; + t14 += v * b3; + t15 += v * b4; + t16 += v * b5; + t17 += v * b6; + t18 += v * b7; + t19 += v * b8; + t20 += v * b9; + t21 += v * b10; + t22 += v * b11; + t23 += v * b12; + t24 += v * b13; + t25 += v * b14; + t26 += v * b15; + v = a[12]; + t12 += v * b0; + t13 += v * b1; + t14 += v * b2; + t15 += v * b3; + t16 += v * b4; + t17 += v * b5; + t18 += v * b6; + t19 += v * b7; + t20 += v * b8; + t21 += v * b9; + t22 += v * b10; + t23 += v * b11; + t24 += v * b12; + t25 += v * b13; + t26 += v * b14; + t27 += v * b15; + v = a[13]; + t13 += v * b0; + t14 += v * b1; + t15 += v * b2; + t16 += v * b3; + t17 += v * b4; + t18 += v * b5; + t19 += v * b6; + t20 += v * b7; + t21 += v * b8; + t22 += v * b9; + t23 += v * b10; + t24 += v * b11; + t25 += v * b12; + t26 += v * b13; + t27 += v * b14; + t28 += v * b15; + v = a[14]; + t14 += v * b0; + t15 += v * b1; + t16 += v * b2; + t17 += v * b3; + t18 += v * b4; + t19 += v * b5; + t20 += v * b6; + t21 += v * b7; + t22 += v * b8; + t23 += v * b9; + t24 += v * b10; + t25 += v * b11; + t26 += v * b12; + t27 += v * b13; + t28 += v * b14; + t29 += v * b15; + v = a[15]; + t15 += v * b0; + t16 += v * b1; + t17 += v * b2; + t18 += v * b3; + t19 += v * b4; + t20 += v * b5; + t21 += v * b6; + t22 += v * b7; + t23 += v * b8; + t24 += v * b9; + t25 += v * b10; + t26 += v * b11; + t27 += v * b12; + t28 += v * b13; + t29 += v * b14; + t30 += v * b15; + t0 += 38 * t16; + t1 += 38 * t17; + t2 += 38 * t18; + t3 += 38 * t19; + t4 += 38 * t20; + t5 += 38 * t21; + t6 += 38 * t22; + t7 += 38 * t23; + t8 += 38 * t24; + t9 += 38 * t25; + t10 += 38 * t26; + t11 += 38 * t27; + t12 += 38 * t28; + t13 += 38 * t29; + t14 += 38 * t30; + // t15 left as is + // first car + c = 1; + v = t0 + c + 65535; + c = Math.floor(v / 65536); + t0 = v - c * 65536; + v = t1 + c + 65535; + c = Math.floor(v / 65536); + t1 = v - c * 65536; + v = t2 + c + 65535; + c = Math.floor(v / 65536); + t2 = v - c * 65536; + v = t3 + c + 65535; + c = Math.floor(v / 65536); + t3 = v - c * 65536; + v = t4 + c + 65535; + c = Math.floor(v / 65536); + t4 = v - c * 65536; + v = t5 + c + 65535; + c = Math.floor(v / 65536); + t5 = v - c * 65536; + v = t6 + c + 65535; + c = Math.floor(v / 65536); + t6 = v - c * 65536; + v = t7 + c + 65535; + c = Math.floor(v / 65536); + t7 = v - c * 65536; + v = t8 + c + 65535; + c = Math.floor(v / 65536); + t8 = v - c * 65536; + v = t9 + c + 65535; + c = Math.floor(v / 65536); + t9 = v - c * 65536; + v = t10 + c + 65535; + c = Math.floor(v / 65536); + t10 = v - c * 65536; + v = t11 + c + 65535; + c = Math.floor(v / 65536); + t11 = v - c * 65536; + v = t12 + c + 65535; + c = Math.floor(v / 65536); + t12 = v - c * 65536; + v = t13 + c + 65535; + c = Math.floor(v / 65536); + t13 = v - c * 65536; + v = t14 + c + 65535; + c = Math.floor(v / 65536); + t14 = v - c * 65536; + v = t15 + c + 65535; + c = Math.floor(v / 65536); + t15 = v - c * 65536; + t0 += c - 1 + 37 * (c - 1); + // second car + c = 1; + v = t0 + c + 65535; + c = Math.floor(v / 65536); + t0 = v - c * 65536; + v = t1 + c + 65535; + c = Math.floor(v / 65536); + t1 = v - c * 65536; + v = t2 + c + 65535; + c = Math.floor(v / 65536); + t2 = v - c * 65536; + v = t3 + c + 65535; + c = Math.floor(v / 65536); + t3 = v - c * 65536; + v = t4 + c + 65535; + c = Math.floor(v / 65536); + t4 = v - c * 65536; + v = t5 + c + 65535; + c = Math.floor(v / 65536); + t5 = v - c * 65536; + v = t6 + c + 65535; + c = Math.floor(v / 65536); + t6 = v - c * 65536; + v = t7 + c + 65535; + c = Math.floor(v / 65536); + t7 = v - c * 65536; + v = t8 + c + 65535; + c = Math.floor(v / 65536); + t8 = v - c * 65536; + v = t9 + c + 65535; + c = Math.floor(v / 65536); + t9 = v - c * 65536; + v = t10 + c + 65535; + c = Math.floor(v / 65536); + t10 = v - c * 65536; + v = t11 + c + 65535; + c = Math.floor(v / 65536); + t11 = v - c * 65536; + v = t12 + c + 65535; + c = Math.floor(v / 65536); + t12 = v - c * 65536; + v = t13 + c + 65535; + c = Math.floor(v / 65536); + t13 = v - c * 65536; + v = t14 + c + 65535; + c = Math.floor(v / 65536); + t14 = v - c * 65536; + v = t15 + c + 65535; + c = Math.floor(v / 65536); + t15 = v - c * 65536; + t0 += c - 1 + 37 * (c - 1); + o[0] = t0; + o[1] = t1; + o[2] = t2; + o[3] = t3; + o[4] = t4; + o[5] = t5; + o[6] = t6; + o[7] = t7; + o[8] = t8; + o[9] = t9; + o[10] = t10; + o[11] = t11; + o[12] = t12; + o[13] = t13; + o[14] = t14; + o[15] = t15; + } + function S(o, a) { + M(o, a, a); + } + function inv25519(o, i) { + let c = gf(); + let a; + for (a = 0; a < 16; a++) + c[a] = i[a]; + for (a = 253; a >= 0; a--) { + S(c, c); + if (a !== 2 && a !== 4) + M(c, c, i); + } + for (a = 0; a < 16; a++) + o[a] = c[a]; + } + function pow2523(o, i) { + let c = gf(); + let a; + for (a = 0; a < 16; a++) + c[a] = i[a]; + for (a = 250; a >= 0; a--) { + S(c, c); + if (a !== 1) + M(c, c, i); + } + for (a = 0; a < 16; a++) + o[a] = c[a]; + } + function add(p, q) { + let a = gf(), b = gf(), c = gf(), d = gf(), e = gf(), f = gf(), g = gf(), h = gf(), t = gf(); + Z(a, p[1], p[0]); + Z(t, q[1], q[0]); + M(a, a, t); + A(b, p[0], p[1]); + A(t, q[0], q[1]); + M(b, b, t); + M(c, p[3], q[3]); + M(c, c, D2); + M(d, p[2], q[2]); + A(d, d, d); + Z(e, b, a); + Z(f, d, c); + A(g, d, c); + A(h, b, a); + M(p[0], e, f); + M(p[1], h, g); + M(p[2], g, f); + M(p[3], e, h); + } + function cswap(p, q, b) { + let i; + for (i = 0; i < 4; i++) { + sel25519(p[i], q[i], b); + } + } + function pack(r, p) { + let tx = gf(), ty = gf(), zi = gf(); + inv25519(zi, p[2]); + M(tx, p[0], zi); + M(ty, p[1], zi); + pack25519(r, ty); + r[31] ^= par25519(tx) << 7; + } + function scalarmult(p, q, s) { + let b, i; + set25519(p[0], gf0); + set25519(p[1], gf1); + set25519(p[2], gf1); + set25519(p[3], gf0); + for (i = 255; i >= 0; --i) { + b = (s[(i / 8) | 0] >> (i & 7)) & 1; + cswap(p, q, b); + add(q, p); + add(p, p); + cswap(p, q, b); + } + } + function scalarbase(p, s) { + let q = [gf(), gf(), gf(), gf()]; + set25519(q[0], X); + set25519(q[1], Y); + set25519(q[2], gf1); + M(q[3], X, Y); + scalarmult(p, q, s); + } + let L = new Float64Array([ + 0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0x10, + ]); + function modL(r, x) { + let carry, i, j, k; + for (i = 63; i >= 32; --i) { + carry = 0; + for (j = i - 32, k = i - 12; j < k; ++j) { + x[j] += carry - 16 * x[i] * L[j - (i - 32)]; + carry = Math.floor((x[j] + 128) / 256); + x[j] -= carry * 256; + } + x[j] += carry; + x[i] = 0; + } + carry = 0; + for (j = 0; j < 32; j++) { + x[j] += carry - (x[31] >> 4) * L[j]; + carry = x[j] >> 8; + x[j] &= 255; + } + for (j = 0; j < 32; j++) + x[j] -= carry * L[j]; + for (i = 0; i < 32; i++) { + x[i + 1] += x[i] >> 8; + r[i] = x[i] & 255; + } + } + function unpackneg(r, p) { + let t = gf(), chk = gf(), num = gf(), den = gf(), den2 = gf(), den4 = gf(), den6 = gf(); + set25519(r[2], gf1); + unpack25519(r[1], p); + S(num, r[1]); + M(den, num, D); + Z(num, num, r[2]); + A(den, r[2], den); + S(den2, den); + S(den4, den2); + M(den6, den4, den2); + M(t, den6, num); + M(t, t, den); + pow2523(t, t); + M(t, t, num); + M(t, t, den); + M(t, t, den); + M(r[0], t, den); + S(chk, r[0]); + M(chk, chk, den); + if (neq25519(chk, num)) + M(r[0], r[0], I); + S(chk, r[0]); + M(chk, chk, den); + if (neq25519(chk, num)) + return -1; + if (par25519(r[0]) === p[31] >> 7) + Z(r[0], gf0, r[0]); + M(r[3], r[0], r[1]); + return 0; + } + function reduce(r) { + let x = new Float64Array(64), i; + for (i = 0; i < 64; i++) + x[i] = r[i]; + for (i = 0; i < 64; i++) + r[i] = 0; + modL(r, x); + } + function crypto_sign_keypair(pk, sk) { + let d = new Uint8Array(64); + let p = [gf(), gf(), gf(), gf()]; + let i; + sha512internal(d, sk, 32); + d[0] &= 248; + d[31] &= 127; + d[31] |= 64; + scalarbase(p, d); + pack(pk, p); + for (i = 0; i < 32; i++) + sk[i + 32] = pk[i]; + return 0; + } + function crypto_sign_open(m, sm, n, pk) { + let i; + let t = new Uint8Array(32), h = new Uint8Array(64); + let p = [gf(), gf(), gf(), gf()], q = [gf(), gf(), gf(), gf()]; + if (n < 64) + return -1; + if (unpackneg(q, pk)) + return -1; + for (i = 0; i < n; i++) + m[i] = sm[i]; + for (i = 0; i < 32; i++) + m[i + 32] = pk[i]; + sha512internal(h, m, n); + reduce(h); + scalarmult(p, q, h); + scalarbase(q, sm.subarray(32)); + add(p, q); + pack(t, p); + n -= 64; + if (crypto_verify_32(sm, 0, t, 0)) { + for (i = 0; i < n; i++) + m[i] = 0; + return -1; + } + for (i = 0; i < n; i++) + m[i] = sm[i + 64]; + return n; + } + // Note: difference from C - smlen returned, not passed as argument. + function crypto_sign(sm, m, n, sk) { + let d = new Uint8Array(64), h = new Uint8Array(64), r = new Uint8Array(64); + let i, j, x = new Float64Array(64); + let p = [gf(), gf(), gf(), gf()]; + sha512internal(d, sk, 32); + d[0] &= 248; + d[31] &= 127; + d[31] |= 64; + let smlen = n + 64; + for (i = 0; i < n; i++) + sm[64 + i] = m[i]; + for (i = 0; i < 32; i++) + sm[32 + i] = d[32 + i]; + sha512internal(r, sm.subarray(32), n + 32); + reduce(r); + scalarbase(p, r); + pack(sm, p); + for (i = 32; i < 64; i++) + sm[i] = sk[i]; + sha512internal(h, sm, n + 64); + reduce(h); + for (i = 0; i < 64; i++) + x[i] = 0; + for (i = 0; i < 32; i++) + x[i] = r[i]; + for (i = 0; i < 32; i++) { + for (j = 0; j < 32; j++) { + x[i + j] += h[i] * d[j]; + } + } + modL(sm.subarray(32), x); + return smlen; + } + // Zero types to make error returns more convenient. + const nu8$2 = new Uint8Array(0); + const nkp$1 = { publicKey: nu8$2, secretKey: nu8$2 }; + // checkAllUint8Array is a helper function to perform input checking on the + // crypto API functions. Because the kernel is often hot-loading untrusted + // code, we cannot depend on typescript to provide type safety. + function checkAllUint8Array(...args) { + for (let i = 0; i < args.length; i++) { + if (!(args[i] instanceof Uint8Array)) { + return "unexpected type, use Uint8Array"; + } + } + return null; + } + // ed25519KeypairFromEntropy is a function that generates an ed25519 keypair + // from the provided entropy. + function ed25519KeypairFromEntropy(seed) { + // Input checking. + let errU8 = checkAllUint8Array(seed); + if (errU8 !== null) { + return [nkp$1, addContextToErr(errU8, "seed is invalid")]; + } + if (seed.length !== crypto_sign_SEEDBYTES) { + return [nkp$1, "bad seed size"]; + } + // Build the keypair. + let pk = new Uint8Array(crypto_sign_PUBLICKEYBYTES); + let sk = new Uint8Array(crypto_sign_SECRETKEYBYTES); + for (let i = 0; i < 32; i++) { + sk[i] = seed[i]; + } + crypto_sign_keypair(pk, sk); + return [ + { + publicKey: pk, + secretKey: sk, + }, + null, + ]; + } + // ed25519Sign will produce an ed25519 signature of a given input. + function ed25519Sign(msg, secretKey) { + // Input checking. + let errU8 = checkAllUint8Array(msg, secretKey); + if (errU8 !== null) { + return [nu8$2, addContextToErr(errU8, "inputs are invalid")]; + } + if (secretKey.length !== crypto_sign_SECRETKEYBYTES) { + return [nu8$2, "bad secret key size"]; + } + // Build the signature. + let signedMsg = new Uint8Array(crypto_sign_BYTES + msg.length); + crypto_sign(signedMsg, msg, msg.length, secretKey); + let sig = new Uint8Array(crypto_sign_BYTES); + for (let i = 0; i < sig.length; i++) { + sig[i] = signedMsg[i]; + } + return [sig, null]; + } + // ed25519Verify will check whether a signature is valid against the given + // publicKey and message. + function ed25519Verify(msg, sig, publicKey) { + let errU8 = checkAllUint8Array(msg, sig, publicKey); + if (errU8 !== null) { + return false; + } + if (sig.length !== crypto_sign_BYTES) { + return false; + } + if (publicKey.length !== crypto_sign_PUBLICKEYBYTES) { + return false; + } + let sm = new Uint8Array(crypto_sign_BYTES + msg.length); + let m = new Uint8Array(crypto_sign_BYTES + msg.length); + let i; + for (i = 0; i < crypto_sign_BYTES; i++) { + sm[i] = sig[i]; + } + for (i = 0; i < msg.length; i++) { + sm[i + crypto_sign_BYTES] = msg[i]; + } + return crypto_sign_open(m, sm, sm.length, publicKey) >= 0; + } + + // Define the number of entropy words used when generating the seed. + const SEED_ENTROPY_WORDS = 13; + const SEED_BYTES = 16; + // deriveChildSeed is a helper function to derive a child seed from a parent + // seed using a string as the path. + function deriveChildSeed(parentSeed, derivationTag) { + let tagU8 = new TextEncoder().encode(" - " + derivationTag); + let preimage = new Uint8Array(parentSeed.length + tagU8.length); + preimage.set(parentSeed, 0); + preimage.set(tagU8, parentSeed.length); + let hash = sha512(preimage); + return hash.slice(0, SEED_BYTES); + } + // deriveMyskyRoot is a helper function to derive the root mysky seed of the + // provided user seed. + // + // NOTE: This is code is to provide legacy compatibility with the MySky + // ecosystem. Compatibility cannot be broken here. + function deriveMyskyRootKeypair(userSeed) { + let saltBytes = new TextEncoder().encode("root discoverable key"); + let saltHash = sha512(saltBytes); + let userSeedHash = sha512(userSeed); + let mergedHash = sha512(new Uint8Array([...saltHash, ...userSeedHash])); + let keyEntropy = mergedHash.slice(0, 32); + // Error is ignored because it should not be possible with the provided + // inputs. + let [keypair] = ed25519KeypairFromEntropy(keyEntropy); + return keypair; + } + // generateSeedPhraseDeterministic will generate and verify a seed phrase for + // the user. + function generateSeedPhraseDeterministic(password) { + let u8 = new TextEncoder().encode(password); + let buf = sha512(u8); + let randNums = Uint16Array.from(buf); + // Generate the seed phrase from the randNums. + let seedWords = []; + for (let i = 0; i < SEED_ENTROPY_WORDS; i++) { + let wordIndex = randNums[i] % dictionary.length; + if (i == SEED_ENTROPY_WORDS - 1) { + wordIndex = randNums[i] % (dictionary.length / 4); + } + seedWords.push(dictionary[wordIndex]); + } + // Convert the seedWords to a seed. + let [seed, err1] = seedWordsToSeed(seedWords); + if (err1 !== null) { + return ["", err1]; + } + // Compute the checksum. + let [checksumOne, checksumTwo, err2] = seedToChecksumWords(seed); + if (err2 !== null) { + return ["", err2]; + } + // Assemble the final seed phrase and set the text field. + let allWords = [...seedWords, checksumOne, checksumTwo]; + let seedPhrase = allWords.join(" "); + return [seedPhrase, null]; + } + // seedToChecksumWords will compute the two checksum words for the provided + // seed. The two return values are the two checksum words. + function seedToChecksumWords(seed) { + // Input validation. + if (seed.length !== SEED_BYTES) { + return ["", "", `seed has the wrong length: ${seed.length}`]; + } + // Get the hash. + let h = sha512(seed); + // Turn the hash into two words. + let word1 = h[0] << 8; + word1 += h[1]; + word1 >>= 6; + let word2 = h[1] << 10; + word2 &= 0xffff; + word2 += h[2] << 2; + word2 >>= 6; + return [dictionary[word1], dictionary[word2], null]; + } + // validSeedPhrase checks whether the provided seed phrase is valid, returning + // an error if not. If the seed phrase is valid, the full seed will be returned + // as a Uint8Array. + function validSeedPhrase(seedPhrase) { + // Create a helper function to make the below code more readable. + let prefix = function (s) { + return s.slice(0, DICTIONARY_UNIQUE_PREFIX); + }; + // Pull the seed into its respective parts. + let seedWordsAndChecksum = seedPhrase.split(" "); + let seedWords = seedWordsAndChecksum.slice(0, SEED_ENTROPY_WORDS); + let checksumOne = seedWordsAndChecksum[SEED_ENTROPY_WORDS]; + let checksumTwo = seedWordsAndChecksum[SEED_ENTROPY_WORDS + 1]; + // Convert the seedWords to a seed. + let [seed, err1] = seedWordsToSeed(seedWords); + if (err1 !== null) { + return [new Uint8Array(0), addContextToErr(err1, "unable to parse seed phrase")]; + } + let [checksumOneVerify, checksumTwoVerify, err2] = seedToChecksumWords(seed); + if (err2 !== null) { + return [new Uint8Array(0), addContextToErr(err2, "could not compute checksum words")]; + } + if (prefix(checksumOne) !== prefix(checksumOneVerify)) { + return [new Uint8Array(0), "first checksum word is invalid"]; + } + if (prefix(checksumTwo) !== prefix(checksumTwoVerify)) { + return [new Uint8Array(0), "second checksum word is invalid"]; + } + return [seed, null]; + } + // seedWordsToSeed will convert a provided seed phrase to to a Uint8Array that + // represents the cryptographic seed in bytes. + function seedWordsToSeed(seedWords) { + // Input checking. + if (seedWords.length !== SEED_ENTROPY_WORDS) { + return [new Uint8Array(0), `Seed words should have length ${SEED_ENTROPY_WORDS} but has length ${seedWords.length}`]; + } + // We are getting 16 bytes of entropy. + let bytes = new Uint8Array(SEED_BYTES); + let curByte = 0; + let curBit = 0; + for (let i = 0; i < SEED_ENTROPY_WORDS; i++) { + // Determine which number corresponds to the next word. + let word = -1; + for (let j = 0; j < dictionary.length; j++) { + if (seedWords[i].slice(0, DICTIONARY_UNIQUE_PREFIX) === dictionary[j].slice(0, DICTIONARY_UNIQUE_PREFIX)) { + word = j; + break; + } + } + if (word === -1) { + return [new Uint8Array(0), `word '${seedWords[i]}' at index ${i} not found in dictionary`]; + } + let wordBits = 10; + if (i === SEED_ENTROPY_WORDS - 1) { + wordBits = 8; + } + // Iterate over the bits of the 10- or 8-bit word. + for (let j = 0; j < wordBits; j++) { + let bitSet = (word & (1 << (wordBits - j - 1))) > 0; + if (bitSet) { + bytes[curByte] |= 1 << (8 - curBit - 1); + } + curBit += 1; + if (curBit >= 8) { + // Current byte has 8 bits, go to the next byte. + curByte += 1; + curBit = 0; + } + } + } + return [bytes, null]; + } + // seedPhraseToSeed will take a seed phrase and return the corresponding seed, + // providing an error if the seed phrase is invalid. This is an alias of + // validSeedPhrase. + function seedPhraseToSeed(seedPhrase) { + return validSeedPhrase(seedPhrase); + } + + // Define some empty values to make our return statements more concise. + const nu8$1 = new Uint8Array(0); + const nkp = { publicKey: nu8$1, secretKey: nu8$1 }; + // computeRegistrySignature will take a secret key and the required fields of a + // registry entry and use them to compute a registry signature, returning both + // the signature and the encoded data for the registry entry. + function computeRegistrySignature(secretKey, dataKey, data, revision) { + // Check that the data is the right size. + if (data.length > 86) { + return [nu8$1, "registry data must be at most 86 bytes"]; + } + // Build the encoded data. + let [encodedData, errEPB] = encodePrefixedBytes(data); + if (errEPB !== null) { + return [nu8$1, addContextToErr(errEPB, "unable to encode provided registry data")]; + } + let [encodedRevision, errEU64] = encodeU64(revision); + if (errEU64 !== null) { + return [nu8$1, addContextToErr(errEU64, "unable to encode the revision number")]; + } + // Build the signing data. + let dataToSign = new Uint8Array(32 + 8 + data.length + 8); + dataToSign.set(dataKey, 0); + dataToSign.set(encodedData, 32); + dataToSign.set(encodedRevision, 32 + 8 + data.length); + let sigHash = blake2b(dataToSign); + // Sign the data. + let [sig, errS] = ed25519Sign(sigHash, secretKey); + if (errS !== null) { + return [nu8$1, addContextToErr(errS, "unable to sign registry entry")]; + } + return [sig, null]; + } + // deriveRegistryEntryID derives a registry entry ID from a provided pubkey and + // datakey. + function deriveRegistryEntryID(pubkey, datakey) { + // Check the lengths of the inputs. + if (pubkey.length !== 32) { + return [nu8$1, "pubkey is invalid, length is wrong"]; + } + if (datakey.length !== 32) { + return [nu8$1, "datakey is not a valid hash, length is wrong"]; + } + // Establish the encoding. First 16 bytes is a specifier, second 8 + // bytes declares the length of the pubkey, the next 32 bytes is the + // pubkey and the final 32 bytes is the datakey. This encoding is + // determined by the Sia protocol. + let encoding = new Uint8Array(16 + 8 + 32 + 32); + // Set the specifier. + encoding[0] = "e".charCodeAt(0); + encoding[1] = "d".charCodeAt(0); + encoding[2] = "2".charCodeAt(0); + encoding[3] = "5".charCodeAt(0); + encoding[4] = "5".charCodeAt(0); + encoding[5] = "1".charCodeAt(0); + encoding[6] = "9".charCodeAt(0); + // Set the pubkey. + let [encodedLen, errU64] = encodeU64(32n); + if (errU64 !== null) { + return [nu8$1, addContextToErr(errU64, "unable to encode pubkey length")]; + } + encoding.set(encodedLen, 16); + encoding.set(pubkey, 16 + 8); + encoding.set(datakey, 16 + 8 + 32); + // Get the final ID by hashing the encoded data. + let id = blake2b(encoding); + return [id, null]; + } + // entryIDToSkylink converts a registry entry id to a resolver skylink. + function entryIDToSkylink(entryID) { + let v2Skylink = new Uint8Array(34); + v2Skylink.set(entryID, 2); + v2Skylink[0] = 1; + return bufToB64(v2Skylink); + } + // resolverLink will take a registryEntryID and return the corresponding + // resolver link. + function resolverLink(entryID) { + if (entryID.length !== 32) { + return ["", "provided entry ID has the wrong length"]; + } + let v2Skylink = new Uint8Array(34); + v2Skylink.set(entryID, 2); + v2Skylink[0] = 1; + let skylink = bufToB64(v2Skylink); + return [skylink, null]; + } + // registryEntryKeys will use the user's seed to derive a keypair and a datakey + // using the provided seed and tags. The keypairTag is a tag which salts the + // keypair. If you change the input keypairTag, the resulting public key and + // secret key will be different. The dataKey tag is the salt for the datakey, + // if you provide a different datakey tag, the resulting datakey will be + // different. + // + // Note that changing the keypair tag will also change the resulting datakey. + // The purpose of the keypair tag is to obfuscate the fact that two registry + // entries are owned by the same identity. This obfuscation would break if two + // different public keys were using the same datakey. Changing the datakey does + // not change the public key. + function taggedRegistryEntryKeys(seed, keypairTagStr, datakeyTagStr) { + if (seed.length !== SEED_BYTES) { + return [nkp, nu8$1, "seed has the wrong length"]; + } + if (keypairTagStr.length > 255) { + return [nkp, nu8$1, "keypairTag must be less than 256 characters"]; + } + // If no datakey tag was provided, use the empty string. + if (datakeyTagStr === undefined) { + datakeyTagStr = ""; + } + // Generate a unique set of entropy using the seed and keypairTag. + let keypairTag = new TextEncoder().encode(keypairTagStr); + let entropyInput = new Uint8Array(keypairTag.length + seed.length); + entropyInput.set(seed, 0); + entropyInput.set(keypairTag, seed.length); + let keypairEntropy = sha512(entropyInput); + // Use the seed to dervie the datakey for the registry entry. We use + // a different tag to ensure that the datakey is independently random, such + // that the registry entry looks like it could be any other registry entry. + // + // We don't want it to be possible for two different combinations of + // tags to end up with the same datakey. If you don't use a length + // prefix, for example the tags ["123", "456"] and ["12", "3456"] would + // have the same datakey. You have to add the length prefix to the + // first tag otherwise you can get pairs like ["6", "4321"] and ["65", + // "321"] which could end up with the same datakey. + let datakeyTag = new TextEncoder().encode(datakeyTagStr); + let datakeyInput = new Uint8Array(seed.length + 1 + keypairTag.length + datakeyTag.length); + let keypairLen = new Uint8Array(1); + keypairLen[0] = keypairTag.length; + datakeyInput.set(seed); + datakeyInput.set(keypairLen, seed.length); + datakeyInput.set(keypairTag, seed.length + 1); + datakeyInput.set(datakeyTag, seed.length + 1 + keypairTag.length); + let datakeyEntropy = sha512(datakeyInput); + // Create the private key for the registry entry. + let [keypair, errKPFE] = ed25519KeypairFromEntropy(keypairEntropy.slice(0, 32)); + if (errKPFE !== null) { + return [nkp, nu8$1, addContextToErr(errKPFE, "unable to derive keypair")]; + } + let datakey = datakeyEntropy.slice(0, 32); + return [keypair, datakey, null]; + } + // verifyRegistrySignature will verify the signature of a registry entry. + function verifyRegistrySignature(pubkey, datakey, data, revision, sig) { + let [encodedData, errEPB] = encodePrefixedBytes(data); + if (errEPB !== null) { + return false; + } + let [encodedRevision, errU64] = encodeU64(revision); + if (errU64 !== null) { + return false; + } + let dataToVerify = new Uint8Array(32 + 8 + data.length + 8); + dataToVerify.set(datakey, 0); + dataToVerify.set(encodedData, 32); + dataToVerify.set(encodedRevision, 32 + 8 + data.length); + let sigHash = blake2b(dataToVerify); + return ed25519Verify(sigHash, sig, pubkey); + } + + // validateSkyfilePath checks whether the provided path is a valid path for a + // file in a skylink. + function validateSkyfilePath(path) { + if (path === "") { + return "path cannot be blank"; + } + if (path === "..") { + return "path cannot be .."; + } + if (path === ".") { + return "path cannot be ."; + } + if (path.startsWith("/")) { + return "metdata.Filename cannot start with /"; + } + if (path.startsWith("../")) { + return "metdata.Filename cannot start with ../"; + } + if (path.startsWith("./")) { + return "metdata.Filename cannot start with ./"; + } + let pathElems = path.split("/"); + for (let i = 0; i < pathElems.length; i++) { + if (pathElems[i] === ".") { + return "path cannot have a . element"; + } + if (pathElems[i] === "..") { + return "path cannot have a .. element"; + } + if (pathElems[i] === "") { + return "path cannot have an empty element, cannot contain //"; + } + } + return null; + } + // validateSkyfileMetadata checks whether the provided metadata is valid + // metadata for a skyfile. + function validateSkyfileMetadata(metadata) { + // Check that the filename is valid. + if (!("Filename" in metadata)) { + return "metadata.Filename does not exist"; + } + if (typeof metadata.Filename !== "string") { + return "metadata.Filename is not a string"; + } + let errVSP = validateSkyfilePath(metadata.Filename); + if (errVSP !== null) { + return addContextToErr(errVSP, "metadata.Filename does not have a valid path"); + } + // Check that there are no subfiles. + if ("Subfiles" in metadata) { + // TODO: Fill this out using code from + // skymodules.ValidateSkyfileMetadata to support subfiles. + return "cannot upload files that have subfiles"; + } + // Check that the default path rules are being respected. + if ("DisableDefaultPath" in metadata && "DefaultPath" in metadata) { + return "cannot set both a DefaultPath and also DisableDefaultPath"; + } + if ("DefaultPath" in metadata) { + // TODO: Fill this out with code from + // skymodules.validateDefaultPath to support subfiles and + // default paths. + return "cannot set a default path if there are no subfiles"; + } + if ("TryFiles" in metadata) { + if (!metadata.TryFiles.IsArray()) { + return "metadata.TryFiles must be an array"; + } + if (metadata.TryFiles.length === 0) { + return "metadata.TryFiles should not be empty"; + } + if ("DefaultPath" in metadata) { + return "metadata.TryFiles cannot be used alongside DefaultPath"; + } + if ("DisableDefaultPath" in metadata) { + return "metadata.TryFiles cannot be used alongside DisableDefaultPath"; + } + // TODO: finish the TryFiles checking using skymodules.ValidateTryFiles + return "TryFiles is not supported at this time"; + } + if ("ErrorPages" in metadata) { + // TODO: finish using skymodules.ValidateErrorPages + return "ErrorPages is not supported at this time"; + } + return null; + } + // validSkylink returns true if the provided Uint8Array is a valid skylink. + // This is an alias for 'parseSkylinkBitfield', as both perform the same + // validation. + function validSkylink(skylink) { + if (skylink.length !== 34) { + return false; + } + let [, , , errPSB] = parseSkylinkBitfield(skylink); + if (errPSB !== null) { + return false; + } + return true; + } + + // Helper consts to make returning empty values alongside errors more + // convenient. + const nu8 = new Uint8Array(0); + // verifyResolverLinkProof will check that the given resolver proof matches the + // provided skylink. If the proof is correct and the signature matches, the + // data will be returned. The returned link will be a verified skylink. + function verifyResolverLinkProof(skylink, proof) { + // Verify the presented skylink is formatted correctly. + if (skylink.length !== 34) { + return [nu8, "skylink is malformed, expecting 34 bytes"]; + } + // Verify that all of the required fields are present in the proof. + if (!("data" in proof) || + !("datakey" in proof) || + !("publickey" in proof) || + !("signature" in proof) || + !("type" in proof) || + !("revision" in proof)) { + return [nu8, "proof is malformed, fields are missing"]; + } + if (!("algorithm" in proof.publickey) || !("key" in proof.publickey)) { + return [nu8, "pubkey is malformed"]; + } + // Verify the typing of the fields. + if (typeof proof.data !== "string") { + return [nu8, "data is malformed"]; + } + let dataStr = proof.data; + if (typeof proof.datakey !== "string") { + return [nu8, "datakey is malformed"]; + } + let datakeyStr = proof.datakey; + if (proof.publickey.algorithm !== "ed25519") { + return [nu8, "pubkey has unrecognized algorithm"]; + } + if (typeof proof.publickey.key !== "string") { + return [nu8, "pubkey key is malformed"]; + } + let pubkeyStr = proof.publickey.key; + if (typeof proof.signature !== "string") { + return [nu8, "signature is malformed"]; + } + if (proof.type !== 1n) { + return [nu8, "registry entry has unrecognized type: " + tryStringify(proof.type)]; + } + let sigStr = proof.signature; + if (typeof proof.revision !== "bigint") { + return [nu8, "revision is malformed"]; + } + let revision = proof.revision; + // Decode all of the fields. They are presented in varied types and + // encodings. + let [data, errD] = hexToBuf(dataStr); + if (errD !== null) { + return [nu8, addContextToErr(errD, "data is invalid hex")]; + } + let [datakey, errDK] = hexToBuf(datakeyStr); + if (errDK !== null) { + return [nu8, addContextToErr(errDK, "datakey is invalid hex")]; + } + let [pubkey, errPK] = b64ToBuf(pubkeyStr); + if (errPK !== null) { + return [nu8, addContextToErr(errPK, "pubkey key is invalid base64")]; + } + let [sig, errS] = hexToBuf(sigStr); + if (errS !== null) { + return [nu8, addContextToErr(errS, "signature is invalid hex")]; + } + // Verify that the data is a skylink - this is a proof for a resolver, + // which means the proof is pointing to a specific skylink. + if (!validSkylink(data)) { + return [nu8, "this skylink does not resolve to another skylink"]; + } + // Verify that the combination of the datakey and the public key match + // the skylink. + let [entryID, errREID] = deriveRegistryEntryID(pubkey, datakey); + if (errREID !== null) { + return [nu8, addContextToErr(errREID, "proof pubkey is malformed")]; + } + let linkID = skylink.slice(2, 34); + for (let i = 0; i < entryID.length; i++) { + if (entryID[i] !== linkID[i]) { + return [nu8, "proof pubkey and datakey do not match the skylink root"]; + } + } + // Verify the signature. + if (!verifyRegistrySignature(pubkey, datakey, data, revision, sig)) { + return [nu8, "signature does not match"]; + } + return [data, null]; + } + // verifyResolverLinkProofs will verify a set of resolver link proofs provided + // by a portal after performing a resolver link lookup. Each proof corresponds + // to one level of resolution. The final value returned will be the V1 skylink + // at the end of the chain. + // + // This function treats the proof as untrusted data and will verify all of the + // fields that are provided. + function verifyResolverLinkProofs(skylink, proof) { + // Check that the proof is an array. + if (!Array.isArray(proof)) { + return [nu8, "provided proof is not an array: " + tryStringify(proof)]; + } + if (proof.length === 0) { + return [nu8, "proof array is empty"]; + } + // Check each proof in the chain, returning the final skylink. + for (let i = 0; i < proof.length; i++) { + let errVRLP; + [skylink, errVRLP] = verifyResolverLinkProof(skylink, proof[i]); + if (errVRLP !== null) { + return [nu8, addContextToErr(errVRLP, "one of the resolution proofs is invalid")]; + } + } + // Though it says 'skylink', the verifier is actually just returning + // whatever the registry data is. We need to check that the final value + // is a V1 skylink. + if (skylink.length !== 34) { + return [nu8, "final value returned by the resolver link is not a skylink"]; + } + let [version, , , errPSB] = parseSkylinkBitfield(skylink); + if (errPSB !== null) { + return [nu8, addContextToErr(errPSB, "final value returned by resolver link is not a valid skylink")]; + } + if (version !== 1n) { + return [nu8, "final value returned by resolver link is not a v1 skylink"]; + } + return [skylink, null]; + } + + // Establish the function that verifies the result is correct. + // + // The fileDataPtr input is an empty object that verifyDownloadResponse will + // fill with the fileData. It basically allows the verify function to + // communicate back to the caller. Note that the verify function might be + // called multiple times in a row if early portals fail to retrieve the data, + // but the verify function doesn't write to the fileDataPtr until it knows that + // the download is final. + function verifyDownloadResponse(response, u8Link, fileDataPtr) { + return new Promise((resolve) => { + // Currently the only valid successful response for a download is a + // 200. Anything else is unexpected and counts as an error. + if (response.status !== 200) { + resolve("unrecognized response status " + tryStringify(response.status) + ", expecting 200"); + return; + } + // Break the input link into its components. + let [version, offset, fetchSize, errBF] = parseSkylinkBitfield(u8Link); + if (errBF !== null) { + resolve(addContextToErr(errBF, "skylink bitfield could not be parsed")); + return; + } + // If this is a resolver skylink, we need to verify the resolver + // proofs. This conditional will update the value of 'u8Link' to be the + // value of the fully resolved link. + if (version === 2n) { + // Verify the resolver proofs and update the link to the correct + // link. + let proofJSON = response.headers.get("skynet-proof"); + if (proofJSON === null || proofJSON === undefined) { + resolve("response did not include resolver proofs"); + return; + } + let [proof, errPJ] = parseJSON(proofJSON); + if (errPJ !== null) { + resolve(addContextToErr(errPJ, "unable to parse resolver link proofs")); + return; + } + // We need to update the u8Link in-place so that the rest of the + // function doesn't need special handling. + let errVRLP; + [u8Link, errVRLP] = verifyResolverLinkProofs(u8Link, proof); + if (errVRLP !== null) { + resolve(addContextToErr(errVRLP, "unable to verify resolver link proofs")); + return; + } + // We also need to update the parsed bitfield, because the link has + // changed. + [version, offset, fetchSize, errBF] = parseSkylinkBitfield(u8Link); + if (errBF !== null) { + resolve(addContextToErr(errBF, "fully resolved link has invalid bitfield")); + return; + } + if (version !== 1n) { + resolve("fully resolved link does not have version 1"); + return; + } + } + response + .arrayBuffer() + .then((buf) => { + let [fileData, portalAtFault, errVD] = verifyDownload(u8Link.slice(2, 34), offset, fetchSize, buf); + if (errVD !== null && portalAtFault) { + resolve("received invalid download from portal"); + return; + } + if (errVD !== null) { + fileDataPtr.fileData = new Uint8Array(0); + fileDataPtr.err = addContextToErr(errVD, "file is corrupt"); + } + else { + fileDataPtr.fileData = fileData; + fileDataPtr.err = null; + } + // If the portal is not at fault, we tell progressiveFetch that + // the download was a success. The caller will have to check + // the fileDataPtr + resolve(null); + }) + .catch((err) => { + resolve(addContextToErr(err, "unable to read response body")); + }); + }); + } + + // progressiveFetchHelper is the full progressiveFetch function, split out into + // a helper because the inptus/api is more complicated but only necessary for + // internal use. + function progressiveFetchHelper(pfm, resolve, verifyFunction) { + // If we run out of portals, return an error. + if (pfm.remainingPortals.length === 0) { + let newLog = "query failed because all portals have been tried"; + pfm.logs.push(newLog); + resolve({ + success: false, + portal: null, + response: null, + portalsFailed: pfm.portalsFailed, + responsesFailed: pfm.responsesFailed, + messagesFailed: pfm.messagesFailed, + remainingPortals: null, + logs: pfm.logs, + }); + return; + } + // Grab the portal and query. + let portal = pfm.remainingPortals.shift(); + let query = portal + pfm.endpoint; + // Create a helper function for trying the next portal. + let nextPortal = function (response, log) { + if (response !== null) { + response + .clone() + .text() + .then((t) => { + pfm.logs.push(log); + pfm.portalsFailed.push(portal); + pfm.responsesFailed.push(response); + pfm.messagesFailed.push(t); + progressiveFetchHelper(pfm, resolve, verifyFunction); + }); + } + else { + pfm.logs.push(log); + pfm.portalsFailed.push(portal); + pfm.responsesFailed.push(response); + pfm.messagesFailed.push(""); + progressiveFetchHelper(pfm, resolve, verifyFunction); + } + }; + // Try sending the query to the portal. + fetch(query, pfm.fetchOpts) + .then((response) => { + // Check for a 5XX error. + if (!("status" in response) || typeof response.status !== "number") { + nextPortal(response, "portal has returned invalid response\n" + tryStringify({ portal, query })); + return; + } + if (response.status < 200 || response.status >= 300) { + nextPortal(response, "portal has returned error status\n" + tryStringify({ portal, query })); + return; + } + // Check the result against the verify function. + verifyFunction(response.clone()).then((errVF) => { + if (errVF !== null) { + nextPortal(response, "verify function has returned an error from portal " + portal + " - " + errVF); + return; + } + // Success! Return the response. + resolve({ + success: true, + portal, + response, + portalsFailed: pfm.portalsFailed, + responsesFailed: pfm.responsesFailed, + remainingPortals: pfm.remainingPortals, + messagesFailed: pfm.messagesFailed, + logs: pfm.logs, + }); + }); + }) + .catch((err) => { + // This portal failed, try again with the next portal. + nextPortal(null, "fetch returned an error\n" + tryStringify(err) + tryStringify(pfm.fetchOpts)); + return; + }); + } + // progressiveFetch will query multiple portals until one returns with a + // non-error response. In the event of a 4XX response, progressiveFetch will + // keep querying additional portals to try and find a working 2XX response. In + // the event that no working 2XX response is found, the first 4XX response will + // be returned. + // + // If progressiveFetch returns a 2XX response, it merely means that the portal + // returned a 2XX response. progressiveFetch cannot be confident that the + // portal has returned a correct/honest message, the verification has to be + // handled by the caller. The response (progressiveFetchResult) contains the + // list of portals that progressiveFetch hasn't tried yet. In the event that + // the 2XX response is not correct, the progressiveFetchResult contains the + // list of failover portals that have not been used yet, allowing + // progressiveFetch to be called again. + // + // This progressive method of querying portals helps prevent queries from + // failing, but if the first portal is not a good portal it introduces + // substantial latency. progressiveFetch does not do anything to make sure the + // portals are the best portals, it just queries them in order. The caller + // should make a best attempt to always have the best, most reliable and + // fastest portal as the first portal in the list. + // + // The reason that we don't blindly accept a 4XX response from a portal is that + // we have no way of verifying that the 4XX is legitimate. We don't trust the + // portal, and we can't give a rogue portal the opportunity to interrupt our + // user experience simply by returning a dishonest 404. So we need to keep + // querying more portals and gain confidence that the 404 a truthful response. + function progressiveFetch(endpoint, fetchOpts, portals, verifyFunction) { + let portalsCopy = [...portals]; + return new Promise((resolve) => { + let pfm = { + endpoint, + fetchOpts, + remainingPortals: portalsCopy, + portalsFailed: [], + responsesFailed: [], + messagesFailed: [], + logs: [], + }; + progressiveFetchHelper(pfm, resolve, verifyFunction); + }); + } + + // downloadSkylink will download the provided skylink. + function downloadSkylink(skylink) { + return new Promise((resolve) => { + // Get the Uint8Array of the input skylink. + let [u8Link, errBTB] = b64ToBuf(skylink); + if (errBTB !== null) { + resolve([new Uint8Array(0), addContextToErr(errBTB, "unable to decode skylink")]); + return; + } + if (!validSkylink(u8Link)) { + resolve([new Uint8Array(0), "skylink appears to be invalid"]); + return; + } + // Prepare the download call. + let endpoint = "/skynet/trustless/basesector/" + skylink; + let fileDataPtr = { fileData: new Uint8Array(0), err: null }; + let verifyFunction = function (response) { + return verifyDownloadResponse(response, u8Link, fileDataPtr); + }; + // Perform the download call. + progressiveFetch(endpoint, null, defaultPortalList, verifyFunction).then((result) => { + // Return an error if the call failed. + if (result.success !== true) { + // Check for a 404. + for (let i = 0; i < result.responsesFailed.length; i++) { + if (result.responsesFailed[i].status === 404) { + resolve([new Uint8Array(0), "404"]); + return; + } + } + // Error is not a 404, return the logs as the error. + let err = tryStringify(result.logs); + resolve([new Uint8Array(0), addContextToErr(err, "unable to complete download")]); + return; + } + // Check if the portal is honest but the download is corrupt. + if (fileDataPtr.err !== null) { + resolve([new Uint8Array(0), addContextToErr(fileDataPtr.err, "download is corrupt")]); + return; + } + resolve([fileDataPtr.fileData, null]); + }); + }); + } + + // verifyDecodedResp will verify the decoded response from a portal for a + // regRead call. + function verifyDecodedResp(resp, data, pubkey, datakey) { + // Status is expected to be 200. + if (resp.status !== 200) { + return "expected 200 response status, got: " + tryStringify(resp.status); + } + // Verify that all required fields were provided. + if (!("data" in data)) { + return "expected data field in response"; + } + if (typeof data.data !== "string") { + return "expected data field to be a string"; + } + if (!("revision" in data)) { + return "expected revision in response"; + } + if (typeof data.revision !== "bigint") { + return "expected revision to be a number"; + } + if (!("signature" in data)) { + return "expected signature in response"; + } + if (typeof data.signature !== "string") { + return "expected signature to be a string"; + } + // Parse out the fields we need. + let [entryData, errHTB] = hexToBuf(data.data); + if (errHTB !== null) { + return "could not decode registry data from response"; + } + let [sig, errHTB2] = hexToBuf(data.signature); + if (errHTB2 !== null) { + return "could not decode signature from response"; + } + // Verify the signature. + if (!verifyRegistrySignature(pubkey, datakey, entryData, data.revision, sig)) { + return "signature mismatch"; + } + // TODO: Need to be handling type 2 registry entries here otherwise we will + // be flagging non malicious portals as malicious. + return null; + } + // verifyRegistryReadResponse will verify that the registry read response from + // the portal was correct. + function verifyRegistryReadResponse(resp, pubkey, datakey) { + return new Promise((resolve) => { + resp + .text() + .then((str) => { + let [obj, errPJ] = parseJSON(str); + if (errPJ !== null) { + resolve(addContextToErr(errPJ, "unable to parse registry response")); + return; + } + let errVDR = verifyDecodedResp(resp, obj, pubkey, datakey); + if (errVDR !== null) { + resolve(addContextToErr(errVDR, "regRead response failed verification")); + return; + } + resolve(null); + }) + .catch((err) => { + resolve(addContextToErr(tryStringify(err), "unable to decode response")); + }); + }); + } + // verifyRegistryWriteResponse will verify that the response from a + // registryWrite call is valid. There's not much to verify beyond looking for + // the right response code, as the portal is not providing us with data, just + // confirming that a write succeeded. + function verifyRegistryWriteResponse(resp) { + return new Promise((resolve) => { + if (resp.status === 204) { + resolve(null); + } + resolve("expecting 200 status code for registry write, got:" + resp.status.toString()); + }); + } + + // stringifyjson.ts is split into a separate file to avoid a circular + // dependency. If you merge it with stringifytry.ts you have a circular import + // where err.js is importing stringify.js and stringify.js is importing err.js. + // Splitting the functions out resolves this issue. + // jsonStringify is a replacement for JSON.stringify that returns an error + // rather than throwing. + function jsonStringify(obj) { + try { + let str = JSON.stringify(obj); + return [str, null]; + } + catch (err) { + return ["", addContextToErr(tryStringify(err), "unable to stringify object")]; + } + } + + var dist = /*#__PURE__*/Object.freeze({ + __proto__: null, + blake2b: blake2b, + defaultPortalList: defaultPortalList, + dictionary: dictionary, + downloadSkylink: downloadSkylink, + verifyDownload: verifyDownload, + verifyDownloadResponse: verifyDownloadResponse, + ed25519Sign: ed25519Sign, + ed25519Verify: ed25519Verify, + b64ToBuf: b64ToBuf, + bufToB64: bufToB64, + bufToHex: bufToHex, + bufToStr: bufToStr, + encodePrefixedBytes: encodePrefixedBytes, + encodeU64: encodeU64, + hexToBuf: hexToBuf, + addContextToErr: addContextToErr, + composeErr: composeErr, + blake2bAddLeafBytesToProofStack: blake2bAddLeafBytesToProofStack, + blake2bMerkleRoot: blake2bMerkleRoot, + blake2bProofStackRoot: blake2bProofStackRoot, + parseJSON: parseJSON, + progressiveFetch: progressiveFetch, + computeRegistrySignature: computeRegistrySignature, + deriveRegistryEntryID: deriveRegistryEntryID, + entryIDToSkylink: entryIDToSkylink, + resolverLink: resolverLink, + taggedRegistryEntryKeys: taggedRegistryEntryKeys, + verifyRegistrySignature: verifyRegistrySignature, + verifyRegistryReadResponse: verifyRegistryReadResponse, + verifyRegistryWriteResponse: verifyRegistryWriteResponse, + deriveChildSeed: deriveChildSeed, + deriveMyskyRootKeypair: deriveMyskyRootKeypair, + generateSeedPhraseDeterministic: generateSeedPhraseDeterministic, + seedPhraseToSeed: seedPhraseToSeed, + validSeedPhrase: validSeedPhrase, + sha512: sha512, + parseSkylinkBitfield: parseSkylinkBitfield, + skylinkV1Bitfield: skylinkV1Bitfield, + validateSkyfileMetadata: validateSkyfileMetadata, + validateSkyfilePath: validateSkyfilePath, + validSkylink: validSkylink, + verifyResolverLinkProofs: verifyResolverLinkProofs, + jsonStringify: jsonStringify, + tryStringify: tryStringify + }); + + var require$$1 = /*@__PURE__*/getAugmentedNamespace(dist); + + Object.defineProperty(tester, "__esModule", { value: true }); + const kernel = require$$0; + const skynet = require$$1; + // @ts-ignore + commonjsGlobal.kernel = kernel; + // @ts-ignore + commonjsGlobal.skynet = skynet; + + return tester; + +})();